summaryrefslogtreecommitdiff
path: root/tutorials/sndkit/ossmplay/mlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'tutorials/sndkit/ossmplay/mlib.c')
-rw-r--r--tutorials/sndkit/ossmplay/mlib.c903
1 files changed, 903 insertions, 0 deletions
diff --git a/tutorials/sndkit/ossmplay/mlib.c b/tutorials/sndkit/ossmplay/mlib.c
new file mode 100644
index 0000000..517d39f
--- /dev/null
+++ b/tutorials/sndkit/ossmplay/mlib.c
@@ -0,0 +1,903 @@
+#define DEB(stmt) /* stmt */ /* For debug printing */
+#define DEB2(stmt) /* stmt */ /* For debug printing */
+#define DEB3(stmt) /* stmt */ /* For debug printing */
+#define DEB_ALWAYS(stmt) /* stmt */ /* Interesting things */
+
+#define _INCLUDE_POSIX_SOURCE 1
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/errno.h>
+
+extern int errno;
+
+#define USE_SIMPLE_MACROS
+#include "../../../include/soundcard.h"
+
+#include "mlib.h"
+
+#define _seqbuf eventptr
+#define _seqbufptr 4
+#define _SEQ_ADVBUF(len) *optr = *optr + 1;
+
+#define STORE(cmd) \
+ if (track->events) \
+ { \
+ unsigned char *eventptr = &(track->events[*optr*12]); \
+ *(int *)&eventptr[0] = track->current_time; \
+ cmd; \
+ } \
+ else \
+ { \
+ *optr = *optr + 1; \
+ }
+
+#define META_EVENT(type, buf, len) \
+ { \
+ eventptr[_seqbufptr + 0] = EV_PRIVATE_META; \
+ eventptr[_seqbufptr + 1] = (type); \
+ *(short*)&eventptr[_seqbufptr + 2] = (short)len; \
+ *(char**)&eventptr[_seqbufptr + 4] = malloc(len); \
+ memcpy((char**)&eventptr[_seqbufptr + 4], &(buf), len); \
+ *optr = *optr + 1; \
+ }
+
+#define MAX_EVENT_SIZE 4095
+
+static int midi_msglen[8] = {
+ 2, /* 8x = Note off */
+ 2, /* 9x = Note on */
+ 2, /* Ax = Polyphonic key pressure (After-touch) */
+ 2, /* Bx = Control change */
+ 1, /* Cx = Program change */
+ 1, /* Dx = Channel pressure (After-touch) */
+ 2, /* Ex = Pitch wheel change */
+ 0 /* Fx = system messages */
+};
+
+static char mlib_errstring[256] = "No errors so far\n";
+
+static char *
+mlib_seterr (char *msg)
+{
+ strncpy (mlib_errstring, msg, sizeof (mlib_errstring));
+ return NULL;
+}
+
+static char *
+mlib_syserr (char *name)
+{
+ sprintf (mlib_errstring, "%s: %s", name, strerror (errno));
+ return NULL;
+}
+
+int
+mlib_chkdesc (mlib_desc * desc)
+{
+ if (desc == NULL)
+ {
+ mlib_seterr ("NULL mlib descriptor specified");
+ return 0;
+ }
+
+ if (desc->magic != 0x121234)
+ {
+ mlib_seterr ("Invalid MAGIC in the mlib descriptor");
+ return 0;
+ }
+
+
+ if (desc->fd < 0)
+ {
+ mlib_seterr ("Invalid fd in the mlib descriptor");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+mlib_loadbuf (mlib_desc * desc)
+{
+ desc->bufp = 0;
+
+ if ((desc->bufcnt = read (desc->fd, desc->buf, sizeof (desc->buf))) == -1)
+ {
+ mlib_syserr (desc->path);
+ return 0;
+ }
+
+ if (desc->bufcnt == 0)
+ {
+ mlib_seterr ("End of MIDI file");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+mlib_seek (mlib_desc * desc, long pos)
+{
+
+ if (lseek (desc->fd, pos, 0) == -1)
+ {
+ mlib_syserr (desc->path);
+ return 0;
+ }
+
+ desc->bufcnt = 0;
+ desc->bufp = 1;
+
+ return 1;
+}
+
+static int
+mlib_rdstr (mlib_desc * desc, char *target, int nchars)
+{
+ int i;
+
+ if (!mlib_chkdesc (desc))
+ return 0;
+
+ for (i = 0; i < nchars; i++)
+ {
+ if (desc->bufp >= desc->bufcnt)
+ if (!mlib_loadbuf (desc))
+ return 0;
+
+ target[i] = desc->buf[desc->bufp];
+ desc->bufp++;
+ }
+
+ target[nchars] = 0; /* End of string */
+
+ return nchars;
+}
+
+static int
+mlib_rdvarlen (mlib_desc * desc, int *target)
+{
+ int tmp, i;
+
+ *target = 0;
+
+ for (i = 0; i < 6; i++) /* Read at most 6 bytes */
+ {
+ if (desc->bufp >= desc->bufcnt)
+ if (!mlib_loadbuf (desc))
+ return 0;
+
+ tmp = desc->buf[desc->bufp];
+ desc->bufp++;
+
+ *target <<= 7;
+ *target |= tmp & 0x7f; /* Extract 7 bits */
+ if ((tmp & 0x80) == 0)
+ return i + 1; /* Last byte */
+ }
+
+ mlib_seterr ("Unterminated variable length value");
+
+ return 0;
+}
+
+static int
+mlib_rdint16 (mlib_desc * desc, int *target)
+{
+ int tmp, i;
+
+ *target = 0;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (desc->bufp >= desc->bufcnt)
+ if (!mlib_loadbuf (desc))
+ return 0;
+
+ tmp = desc->buf[desc->bufp];
+ desc->bufp++;
+
+ *target <<= 8;
+ *target |= tmp & 0xff;
+ }
+
+ return 2;
+}
+
+static int
+mlib_rdbyte (mlib_desc * desc, unsigned char *target)
+{
+ if (desc->bufp >= desc->bufcnt)
+ if (!mlib_loadbuf (desc))
+ return 0;
+
+ *target = (char) (desc->buf[desc->bufp]);
+ desc->bufp++;
+ return 1;
+}
+
+static int
+mlib_rdint32 (mlib_desc * desc, int *target)
+{
+ int tmp, i;
+
+ *target = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (desc->bufp >= desc->bufcnt)
+ if (!mlib_loadbuf (desc))
+ return 0;
+
+ tmp = desc->buf[desc->bufp];
+ desc->bufp++;
+
+ *target <<= 8;
+ *target += tmp & 0xff;
+ }
+
+ return 4;
+}
+
+static int
+mlib_read_mthd (mlib_desc * desc)
+{
+ char sig[5];
+ unsigned char hi, lo;
+ int len;
+
+ if (!mlib_chkdesc (desc))
+ return 0;
+
+ if (!mlib_rdstr (desc, sig, 4))
+ return 0;
+
+ if (strcmp (sig, "MThd"))
+ {
+ mlib_seterr ("Invalid header signature (!= MThd)");
+ return 0;
+ }
+
+ if (mlib_rdint32 (desc, &len))
+
+ if (mlib_rdint16 (desc, &desc->hdr.MThd_fmt))
+ DEB2 (printf ("Header: Format %d\n", desc->hdr.MThd_fmt));
+
+ if (mlib_rdint16 (desc, &desc->hdr.MThd_ntrk))
+ DEB2 (printf ("Header: ntrks %d\n", desc->hdr.MThd_ntrk));
+
+ if (mlib_rdbyte (desc, &hi))
+ if (mlib_rdbyte (desc, &lo))
+
+ if (hi & 0x80) /* Negative */
+ {
+ DEB2 (printf ("SMPTE timing: format = %d, resolution = %d\n",
+ (char) hi, lo));
+ desc->hdr.time_mode = TIME_SMPTE;
+ desc->hdr.SMPTE_format = (char) hi;
+ desc->hdr.SMPTE_resolution = lo;
+ }
+ else
+ {
+ desc->hdr.time_mode = TIME_MIDI;
+ desc->hdr.division = (hi << 8) + lo;
+ DEB2 (printf ("Midi timing: timebase = %d ppqn\n",
+ desc->hdr.division));
+ }
+
+ desc->curr_trk = -1;
+ desc->trk_offs = 0;
+ desc->next_trk_offs = 4 + 4 + len;;
+
+ return mlib_seek (desc, desc->trk_offs); /* Point to the first track */
+}
+
+static int
+mlib_store_chn_voice_msg (mlib_desc * desc, mlib_track * track, int *optr,
+ unsigned char *evbuf, int len)
+{
+
+ unsigned char chn, pgm, note, vel;
+
+ chn = evbuf[0] & 0x0f; /* Midi channel */
+
+ if (track->init_chn == -1)
+ track->init_chn = chn;
+ else if (chn != track->init_chn)
+ track->flags |= TRK_MULTICHN;
+
+ track->chnmask |= (1 << chn); /* Update channel mask */
+
+ switch (evbuf[0] & 0xf0)
+ {
+ case 0x90: /* Note on */
+ vel = evbuf[2];
+ note = evbuf[1];
+
+ if (vel != 0)
+ { /* Velocity given -> true note on event */
+ track->flags |= TRK_NOTES;
+
+ if (note > track->max_note)
+ track->max_note = note;
+
+ if (note < track->min_note)
+ track->min_note = note;
+
+ if (track->noteon_time == -1)
+ track->noteon_time = track->current_time;
+
+ if (vel != 64)
+ track->flags |= TRK_VEL_NOTEON;
+
+ STORE (SEQ_START_NOTE (0, chn, note, vel));
+ if (chn == 9) /* Drum channel */
+ track->drum_map[note] = note;
+ break; /* NOTE! The break is here, not later */
+ }
+ /* Note on with zero G -> note off with vel=64. */
+ /* Fall to the next case */
+ evbuf[2] = 64; /* Velocity for the note off handler */
+
+ case 0x80: /* Note off */
+ vel = evbuf[2];
+ note = evbuf[1];
+
+ if (vel != 64)
+ track->flags |= TRK_VEL_NOTEOFF;
+ STORE (SEQ_STOP_NOTE (0, chn, note, vel));
+ break;
+
+ case 0xA0: /* Polyphonic key pressure/Aftertouch */
+ track->flags |= TRK_POLY_AFTERTOUCH | TRK_AFTERTOUCH;
+ STORE (SEQ_KEY_PRESSURE (0, chn, note, vel));
+ break;
+
+ case 0xB0: /* Control change */
+ {
+ unsigned short value = evbuf[2]; /* Incorrect */
+ unsigned char ctl = evbuf[1];
+
+ track->flags |= TRK_CONTROLS;
+ STORE (SEQ_CONTROL (0, chn, ctl, value));
+ }
+ break;
+
+ case 0xC0: /* Program change */
+ pgm = evbuf[1];
+ if (track->init_pgm == -1)
+ track->init_pgm = pgm;
+ else if (pgm != track->init_pgm)
+ track->flags |= TRK_MULTIPGM;
+
+ track->pgm_map[pgm] = pgm;
+ STORE (SEQ_SET_PATCH (0, chn, pgm));
+ break;
+
+ case 0xD0: /* Channel pressure/Aftertouch */
+ track->flags |= TRK_AFTERTOUCH;
+ STORE (SEQ_CHN_PRESSURE (0, chn, evbuf[1]));
+ break;
+
+ case 0xE0: /* Pitch bend change */
+ track->flags |= TRK_BENDER;
+ STORE (SEQ_BENDER
+ (0, chn, (evbuf[1] & 0x7f) + ((evbuf[2] & 0x7f) << 7)));
+ break;
+
+ default:
+ mlib_seterr ("Internal error: Unexpected midi event");
+ fprintf (stderr, "Internal error: Unexpected midi event %02x\n",
+ evbuf[0]);
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+mlib_store_meta_event (mlib_desc * desc, mlib_track * track, int *optr,
+ unsigned char *evbuf, int len)
+{
+ int type = evbuf[0];
+ int i;
+
+/*
+ * The event is stored at the end of this function. If there is no
+ * need to store the event, the case entry should return (return 1).
+ */
+
+ switch (type)
+ {
+ case 0x00: /* Extension events ?????????????? */
+ /* Not supported yet */
+ break;
+
+ case 0x01: /* Descriptive text */
+ case 0x02: /* Copyright notice */
+ case 0x03: /* Sequence/track name */
+ case 0x04: /* Instrument name */
+ case 0x05: /* Lyric */
+ case 0x06: /* Marker */
+ case 0x07: /* Cue point */
+#if 0
+ for (i = 0; i < len - 1; i++)
+ printf ("%c", evbuf[1 + i]);
+ printf ("\n");
+#endif
+ break;
+
+ case 0x21: /* What is this */
+ break;
+
+/* Here is a big hole in the known meta event space */
+
+ case 0x2f: /* End of track */
+ break;
+
+/* Here is a big hole in the known meta event space */
+
+ case 0x51: /* Set tempo (usec per MIDI quarter-note) */
+ {
+ int tempo_bpm = 120;
+ unsigned int usecs_per_qn;
+
+ usecs_per_qn = (evbuf[1] << 16) | (evbuf[2] << 8) | evbuf[3];
+ tempo_bpm = (60000000 + (usecs_per_qn / 2)) / usecs_per_qn;
+
+ STORE (SEQ_SET_TEMPO (tempo_bpm));
+ return 1;
+ }
+ break;
+
+ case 0x54: /* SMPTE offset */
+ break;
+
+ case 0x58: /* Time signature */
+ {
+ unsigned sig;
+
+ sig = evbuf[1] << 24;
+ sig |= evbuf[2] << 16;
+ sig |= evbuf[3] << 8;
+ sig |= evbuf[4];
+
+ if (!desc->timesig)
+ desc->timesig = sig;
+ STORE (SEQ_TIME_SIGNATURE (sig));
+ }
+ break;
+
+ case 0x59: /* Key signature */
+ break;
+
+ case 0x7f: /* Vendor specific meta event */
+ if (evbuf[1] == 0 && evbuf[2] == 0 && evbuf[3] == 0x41)
+ { /* Microsoft ?? */
+ if (len == 4)
+ DEB_ALWAYS (printf ("GM file flag \n"));
+
+/* Don't forget to add more code here */
+ }
+ else
+ {
+ DEB_ALWAYS (printf ("Private meta event:\n"));
+ for (i = 0; i < len; i++)
+ DEB_ALWAYS (printf ("%02x ", evbuf[i]));
+ DEB_ALWAYS (printf ("\n"));
+ for (i = 0; i < len; i++)
+ DEB_ALWAYS (printf ("%c", (evbuf[i] >= ' ' && evbuf[i] < 127) ?
+ evbuf[i] : '.'));
+ DEB_ALWAYS (printf ("\n"));
+ }
+ break;
+
+ default:
+ DEB_ALWAYS (printf ("Meta event: 0x%02x???\n", type));
+ for (i = 0; i < len; i++)
+ DEB_ALWAYS (printf ("%02x ", evbuf[i]));
+ DEB_ALWAYS (printf ("\n"));
+ for (i = 0; i < len; i++)
+ DEB_ALWAYS (printf ("%c", (evbuf[i] >= ' ' && evbuf[i] < 127) ?
+ evbuf[i] : '.'));
+ DEB_ALWAYS (printf ("\n"));
+ }
+
+ STORE (META_EVENT (type, evbuf, len));
+
+ return 1;
+}
+
+static int
+mlib_input_event (mlib_desc * desc, int *iptr, int *optr, mlib_track * track)
+{
+ int time, len, event_len, i, p;
+ unsigned char b, status, type;
+
+ unsigned char buf[MAX_EVENT_SIZE + 1];
+
+ if (!(len = mlib_rdvarlen (desc, &time)))
+ return 0;
+ *iptr += len;
+
+ track->current_time += time;
+ if (track->end_time < track->current_time)
+ track->end_time = track->current_time;
+
+ if (!(len = mlib_rdbyte (desc, &type)))
+ return 0;
+ *iptr += len;
+
+ DEB (printf ("EVENT: time = %d type = 0x%02x: ", time, type));
+
+ p = 0;
+
+ switch (type)
+ {
+ case 0xff: /* Meta event */
+ if (!(len = mlib_rdbyte (desc, &type)))
+ return 0;
+ *iptr += len;
+
+ buf[p++] = type;
+
+ if (!(len = mlib_rdvarlen (desc, &event_len)))
+ return 0;
+ *iptr += len;
+
+ DEB (printf ("meta(type=%02x, len=%d): ", type, event_len));
+
+ if ((event_len + 1) > MAX_EVENT_SIZE)
+ {
+ mlib_seterr ("Too long meta event in file");
+ *iptr += event_len;
+ return 0; /* Fatal error */
+ }
+
+ for (i = 0; i < event_len; i++)
+ {
+ if (!(len = mlib_rdbyte (desc, &b)))
+ return 0;
+ *iptr += len;
+
+ buf[p++] = b;
+ DEB (printf ("%02x ", b));
+ }
+
+/*
+ * End of track test
+ */
+
+ if (type == 0x2f)
+ {
+ *iptr = 0x7fffffff;
+ return 1;
+ }
+ if (!mlib_store_meta_event (desc, track, optr, buf, p))
+ return 0;
+ break;
+
+ case 0xf0: /* Sysex, variation 1 */
+ if (!(len = mlib_rdvarlen (desc, &event_len)))
+ return 0;
+ *iptr += len;
+ DEB (printf ("sysex1 (%d): f0 ", event_len));
+ p = 0;
+ buf[p++] = 0xf0;
+ for (i = 0; i < event_len; i++)
+ {
+ if (!(len = mlib_rdbyte (desc, &b)))
+ return 0;
+ *iptr += len;
+ buf[p++] = b;
+
+ DEB (printf ("%02x ", b));
+ }
+ {
+ int l;
+
+ i = 0;
+ while (i < p)
+ {
+ int xx;
+ l = p - i;
+ if (l > 6)
+ l = 6;
+ STORE (SEQ_SYSEX (0, &buf[i], l));
+ i += l;
+ }
+ }
+ break;
+
+ case 0xf7: /* Sysex, variation 2 */
+ if (!(len = mlib_rdvarlen (desc, &event_len)))
+ return 0;
+ *iptr += len;
+ DEB (printf ("sysex2 (%d): ", event_len));
+ for (i = 0; i < event_len; i++)
+ {
+ if (!(len = mlib_rdbyte (desc, &b)))
+ return 0;
+ buf[p++] = b;
+ *iptr += len;
+
+ DEB (printf ("%02x ", b));
+ }
+ {
+ int l;
+
+ i = 0;
+ while (i < p)
+ {
+ l = p - i;
+ if (l > 6)
+ l = 6;
+ STORE (SEQ_SYSEX (0, &buf[i], l));
+ i += l;
+ }
+ }
+ break;
+
+ default:
+
+ if ((type & 0xf0) == 0xf0) /* Sys message */
+ {
+ DEB (printf ("system message "));
+ DEB (printf ("\n"));
+ mlib_seterr ("Unsupported midi file event");
+ return 0;
+ }
+ else if (type < 0x80)
+ {
+ buf[p++] = status = desc->prev_status;
+ DEB (printf ("Midi message (RST): %02x %02x ", status, type));
+ buf[p++] = type;
+
+ event_len = midi_msglen[((status >> 4) & 0xf) - 8] - 1;
+ for (i = 0; i < event_len; i++)
+ {
+ if (!(len = mlib_rdbyte (desc, &b)))
+ return 0;
+ *iptr += len;
+ buf[p++] = b;
+ DEB (printf ("%02x ", b));
+ }
+ if (!mlib_store_chn_voice_msg (desc, track, optr, buf, p))
+ return 0;
+ }
+ else
+ {
+ buf[p++] = status = desc->prev_status = type;
+ DEB (printf ("Midi message: %02x ", status));
+
+ event_len = midi_msglen[((status >> 4) & 0xf) - 8];
+ for (i = 0; i < event_len; i++)
+ {
+ if (!(len = mlib_rdbyte (desc, &b)))
+ return 0;
+ *iptr += len;
+ buf[p++] = b;
+ DEB (printf ("%02x ", b));
+ }
+ if (!mlib_store_chn_voice_msg (desc, track, optr, buf, p))
+ return 0;
+ }
+ }
+
+ DEB (printf ("\n"));
+
+ return 1;
+}
+
+static int
+mlib_load_pass (mlib_desc * desc, mlib_track * track, int *iptr, int *optr,
+ int len)
+{
+ int i;
+
+ for (i = 0; i < 128; i++)
+ {
+ track->pgm_map[i] = -1;
+ track->drum_map[i] = -1;
+ }
+
+ track->len = 0;
+ track->flags = 0;
+ track->init_chn = -1;
+ track->init_pgm = -1;
+ track->port = -1;
+ track->chn = -1;
+ track->chnmask = 0;
+ track->pgm = -1;
+ track->current_time = 0;
+ track->noteon_time = -1;
+ track->end_time = 0;
+ track->min_note = 300;
+ track->max_note = -1;
+
+ *iptr = *optr = 0;
+ desc->prev_status = 0;
+
+ while (*iptr < len)
+ if (!mlib_input_event (desc, iptr, optr, track))
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+mlib_track *
+mlib_loadtrack (mlib_desc * desc, int *end_detected)
+{
+ mlib_track *track;
+ char sig[5];
+ int dummy, len, iptr, optr, pass1events;
+
+ *end_detected = 0;
+
+ if (!mlib_chkdesc (desc))
+ return NULL;
+
+ if ((track = (mlib_track *) malloc (sizeof (*track))) == NULL)
+ return (mlib_track *) mlib_seterr ("Can't malloc track descriptor");
+
+ desc->curr_trk++;
+
+ if (desc->curr_trk >= desc->hdr.MThd_ntrk)
+ {
+ *end_detected = 1;
+ return (mlib_track *) mlib_seterr ("No more tracks in the midi file");
+ }
+
+ desc->trk_offs = desc->next_trk_offs;
+
+ if (!mlib_seek (desc, desc->trk_offs))
+ {
+ free ((char *) track);
+ return NULL;
+ }
+
+ if (!mlib_rdstr (desc, sig, 4))
+ {
+ free ((char *) track);
+ return NULL;
+ }
+
+ if (strcmp (sig, "MTrk"))
+ {
+ free ((char *) track);
+ return (mlib_track *) mlib_seterr ("Invalid track signature (!= MTrk)");
+ }
+
+ if (!mlib_rdint32 (desc, &len))
+ {
+ free ((char *) track);
+ return 0;
+ }
+
+ desc->next_trk_offs = desc->trk_offs + 4 + 4 + len;
+
+ track->events = NULL;
+
+ if (!mlib_load_pass (desc, track, &iptr, &optr, len))
+ {
+ free ((char *) track);
+ return NULL;
+ }
+
+ pass1events = optr;
+
+/*
+ * Pass 2 actually store the messages
+ */
+
+ dummy = pass1events;
+ if (dummy < 10)
+ dummy = 10;
+ track->events = malloc (12 * dummy);
+ if (pass1events == 0)
+ return track; /* Empty track */
+
+ if (!mlib_seek (desc, desc->trk_offs + 4)) /* Past the MTrk */
+ {
+ free (track->events);
+ free ((char *) track);
+ return NULL;
+ }
+
+ if (!mlib_rdint32 (desc, &dummy))
+ {
+ free (track->events);
+ free ((char *) track);
+ return 0;
+ }
+
+ if (!mlib_load_pass (desc, track, &iptr, &optr, len))
+ {
+ free (track->events);
+ free ((char *) track);
+ return NULL;
+ }
+
+ track->len = optr;
+
+ if (optr != pass1events)
+ fprintf (stderr,
+ "Warning: Events counts of pass 1 and 2 differ (%d/%d)\n",
+ pass1events, optr);
+
+ return track;
+}
+
+void
+mlib_deltrack (mlib_track * track)
+{
+ if (track == NULL)
+ return;
+
+ if (track->events != NULL)
+ free (track->events);
+
+ free ((char *) track);
+}
+
+mlib_desc *
+mlib_open (char *path)
+{
+ mlib_desc *desc;
+ int i;
+
+ desc = (mlib_desc *) malloc (sizeof (*desc));
+ if (desc == NULL)
+ return (mlib_desc *) mlib_seterr ("Malloc failed");
+
+ if ((desc->fd = open (path, O_RDONLY, 0)) == -1)
+ {
+ free ((char *) desc);
+ return (mlib_desc *) mlib_syserr (path);
+ }
+
+ strncpy (desc->path, path, sizeof (desc->path));
+
+ desc->bufcnt = 0;
+ desc->bufp = 1;
+ desc->curr_trk = 0;
+ desc->trk_offs = 0;
+ desc->next_trk_offs = 0;
+ desc->magic = 0x121234;
+ desc->control_track = NULL;
+ desc->timesig = 0;
+ for (i = 0; i < MAX_TRACK; i++)
+ desc->tracks[i] = NULL;
+
+ if (!mlib_read_mthd (desc))
+ {
+ close (desc->fd);
+ free ((char *) desc);
+ return NULL;
+ }
+
+ return desc;
+}
+
+void
+mlib_close (mlib_desc * desc)
+{
+ if (mlib_chkdesc (desc))
+ {
+ close (desc->fd);
+ desc->fd = -1;
+ free (desc);
+ }
+}
+
+char *
+mlib_errmsg (void)
+{
+ return &mlib_errstring[0];
+}