home *** CD-ROM | disk | FTP | other *** search
- /*
- * ptmidinp.c: MIDI input module for for ptmid. Reads a MIDI file and
- * creates a structure representing it.
- *
- * Author: Andrew Scott (c)opyright 1994
- *
- * Date: 17/11/1993 ver 0.0
- * 8/1/1994 ver 0.1
- * 11/2/1994 ver 0.2
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <io.h>
- #include <fcntl.h>
- #include <string.h>
- #include "ptmid.h"
-
- #define BUFSIZ 512
-
- #define ODD(x) (x & 1)
-
- typedef unsigned long VLQ; /** VLQ is a variable length quantity **/
-
- typedef struct { /** RF is a record of a file **/
- int fd, ib;
- unsigned char rgb[BUFSIZ];
- } RF;
-
- int rgbPatch[16], wDivision, wBendsen = 2, wModwheel = 0;
- unsigned wQuant;
- NRL *rgpnrl[16][128];
- Tune *ptuneMain, *ptuneCurr;
- RF *prf;
-
- /*
- * Init: Yes.. you guessed it. Initializes variables and stuff.
- *
- * date: 30/6/1994 - added 16 NRL array init
- */
- static void Init(void)
- {
- int i, j;
-
- for (i = 16; i--; ) { /** Clear current instrument array **/
- rgbPatch[i] = 0;
- for (j = 128; j--; ) /** Clear hanging-note arrays **/
- rgpnrl[i][j] = NULL;
- }
- }
-
- /*
- * ChGetFd: Given a file, returns next character from it.
- */
- int ChGetFd(int fd)
- {
- unsigned char b;
-
- if (read(fd, &b, 1) < 1)
- return EOF;
- return b;
- }
-
- /*
- * ChGetIrf: Given an index, returns next character from corresponding
- * file record.
- */
- unsigned char ChGetIrf(unsigned irf)
- {
- if (prf[irf].ib == BUFSIZ) {
- if (read(prf[irf].fd, prf[irf].rgb, BUFSIZ) == -1) {
- ERROR;
- exit(1);
- }
- prf[irf].ib = 1;
- return prf[irf].rgb[0];
- }
- return prf[irf].rgb[prf[irf].ib++];
- }
-
- /*
- * SkipIrf: Given an index, skips a given number of bytes forward in
- * that file record.
- *
- * date: 4/7/1994 - fixed longstanding bug: wouldn't read in new buffer
- */
- void SkipIrf(unsigned irf, long cb)
- {
- if (BUFSIZ - prf[irf].ib >= cb)
- prf[irf].ib += (int) cb;
- else {
- cb -= BUFSIZ - prf[irf].ib;
- if (lseek(prf[irf].fd, cb, SEEK_CUR) == -1) {
- ERROR;
- exit(1);
- }
- prf[irf].ib = BUFSIZ;
- }
- }
-
- /*
- * ValidquantSz: Takes a lower-case string and checks to see if it is a
- * legal quantize fraction. If not, zero is returned, else value of
- * string is returned (+1 if a triplet case).
- */
- int ValidquantSz(Sz szQuant)
- {
- int bFrac;
- Sz szEnd;
-
- if ((bFrac = (int) strtol(szQuant, &szEnd, 10))) /** If a number **/
- if ('t' == *szEnd || 'T' == *szEnd) /** possibly followed by a 't' **/
- bFrac++;
- return bFrac; /** return valid **/
- }
-
- /*
- * VlqFromFd: Reads bytes from given file until a variable length
- * quantity is decoded. Returns that vlq.
- */
- VLQ VlqFromFd(int fd)
- {
- VLQ vlqRead = 0;
- unsigned char b = 0;
-
- while (read(fd, &b, 1) == 1 && (b & 0x80))
- vlqRead = (vlqRead << 7) | (b & 0x7F);
- return (vlqRead << 7) | b;
- }
-
- /*
- * VlqFromIrf: Reads bytes from file corresponding to given index until
- * a variable length quantity is decoded. Returns that vlq.
- */
- VLQ VlqFromIrf(unsigned irf)
- {
- VLQ vlqRead = 0;
- unsigned char b = 0;
-
- while ((b = ChGetIrf(irf)) & 0x80)
- vlqRead = (vlqRead << 7) | (b & 0x7F);
- return (vlqRead << 7) | b;
- }
-
- /*
- * LongFromFd: Reads number of bytes from Fd until number is in.
- */
- unsigned long LongFromFd(int fd, unsigned cb)
- {
- unsigned char rgb[4];
- unsigned long longT = 0;
- unsigned ib = 0;
-
- read(fd, rgb, cb);
- for (; ib < cb; ib++)
- longT = (longT << 8) + rgb[ib];
- return longT;
- }
-
- /*
- * Addnote: Given channel, pitch, instrument, and volume will add that
- * note to the array of playing notes.
- *
- * date: 30/6/1994 - added chan as well as pfx init
- */
- void Addnote(int chan, int pitch, int inst, int vol)
- {
- NRL *pnrlT;
-
- if (0 > pitch || 127 < pitch || 0 > chan || 15 < chan)
- return;
- pnrlT = (NRL *) malloc(sizeof(NRL)); /** Allocate space **/
- pnrlT->pnrl = rgpnrl[chan][pitch];
- rgpnrl[chan][pitch] = pnrlT; /** Attach to front of list in array **/
- pnrlT->inst = inst;
- pnrlT->vol = vol;
- pnrlT->pfxTail = NULL;
- pnrlT->ptuneNow = ptuneCurr;
- }
-
- /*
- * PeiRequestPtune: Returns a pointer to a new event structure at the
- * position in the tune specified.
- */
- EI *PeiRequestPtune(Tune *ptune)
- {
- EI *pei;
-
- pei = (EI *) malloc(sizeof(EI)); /** Allocate space for event **/
- pei->pei = ptune->pei;
- ptune->pei = pei; /** Attach to front of event list at tune position **/
- return pei;
- }
-
- /*
- * Endnote: Given a pitch and instrument, will remove a note from array
- * of playing notes and store it as an event in the tune.
- *
- * date: 30/6/1994 - added support for effects, 16 chans, min. perc. durat.
- */
- void Endnote(int chan, int pitch, int inst)
- {
- NRL *pnrlT, *pnrlOld = NULL;
- unsigned long durat;
- EI *peiT;
-
- if (0 > pitch || 127 < pitch || 0 > chan || 15 < chan)
- return;
- for (pnrlT = rgpnrl[chan][pitch]; NULL != pnrlT && pnrlT->inst != inst; ) {
- pnrlOld = pnrlT;
- pnrlT = pnrlT->pnrl;
- } /** Find instrument in hanging-note array **/
- if (NULL == pnrlT)
- return;
-
- durat = ptuneCurr->count - pnrlT->ptuneNow->count; /** Calc. duration **/
- if (0 > inst && wDivision / 2 > durat)
- durat = wDivision / 2; /** Percussion sounds have a min. duration **/
- if (durat < wQuant)
- durat = wQuant; /** All sounds have a min. duration **/
-
- peiT = PeiRequestPtune(pnrlT->ptuneNow); /** Get an event **/
- peiT->effect = durat;
- peiT->inst = inst; /** Store the note in it **/
- peiT->pitch = pitch;
- peiT->vol = pnrlT->vol;
- peiT->pfxTail = pnrlT->pfxTail;
-
- if (NULL == pnrlOld) /** Remove note from hanging-note array **/
- rgpnrl[chan][pitch] = pnrlT->pnrl;
- else
- pnrlOld->pnrl = pnrlT->pnrl;
- free(pnrlT);
- }
-
- /*
- * VlqInterpIrf: Reads a file pointer and gathers all notes at this
- * instant, storing them on the note stack. Returns ticks until next
- * collection of notes. A running status of current channel is maintained.
- *
- * date: 30/6/1994 - added effect support
- */
- VLQ VlqInterpIrf(unsigned irf, unsigned *pbStat)
- {
- unsigned bEvent;
- VLQ vlqT;
- FX *pfx;
-
- do { /** Loop.. **/
- bEvent = ChGetIrf(irf); /** Get first data byte **/
- if (0x80 <= bEvent && 0xEF >= bEvent) { /** If a command **/
- *pbStat = bEvent; /** update running-status byte **/
- bEvent = ChGetIrf(irf); /** and get next data byte **/
- }
- if (0xF0 == bEvent || 0xF7 == bEvent) /** If a sys-exclusive message **/
- SkipIrf(irf, VlqFromIrf(irf)); /** skip it **/
- else if (0xFF == bEvent) { /** Else if a meta event **/
- bEvent = ChGetIrf(irf); /** get type of event **/
- vlqT = VlqFromIrf(irf); /** and length **/
- if (0x2F == bEvent) { /*** If termination event ***/
- close(prf[irf].fd); /*** close handle ***/
- prf[irf].fd = -1;
- vlqT = 0;
- break; /*** and terminate ***/
- }
- if (0x51 == bEvent) { /*** Else if tempo event ***/
- unsigned long t;
- EI *pei;
-
- t = (unsigned long) ChGetIrf(irf) << 16; /*** get value ***/
- t += (unsigned long) ChGetIrf(irf) << 8;
- t += ChGetIrf(irf);
- pei = PeiRequestPtune(ptuneCurr);
- pei->effect = 60000000L / wQuant * wDivision / t; /*** and convert ***/
- pei->pitch = -1;
- } else
- SkipIrf(irf, vlqT); /*** Else skip event ***/
- } else
- switch (*pbStat & 0xF0) { /** Else must be a midi event.. **/
- case 0x80:
- case 0x90: { /** Note on/off **/
- unsigned bVol, bChan;
-
- bVol = ChGetIrf(irf);
- bChan = *pbStat & 0x0F;
- if (0 < bVol && 0x90 <= *pbStat)
- if (bChan != bDrumch)
- Addnote(bChan, bEvent, rgbPatch[bChan], bVol);
- else
- Addnote(bChan, 0, -1 - bEvent, bVol);
- else
- if (bChan != bDrumch)
- Endnote(bChan, bEvent, rgbPatch[bChan]);
- else
- Endnote(bChan, 0, -1 - bEvent);
- break;
- }
- case 0xA0: { /** Polyphonic Key Pressure **/
- NRL *pnrlT;
- unsigned bChan;
-
- bChan = *pbStat & 0x0F;
- pnrlT = rgpnrl[bChan][bEvent];
- while (NULL != pnrlT && pnrlT->inst != rgbPatch[bChan])
- pnrlT = pnrlT->pnrl;
- if (NULL != pnrlT) {
- pfx = (FX *) malloc(sizeof(FX));
- pfx->delay = ptuneCurr->count - pnrlT->ptuneNow->count;
- pfx->eff = 1;
- pfx->param = ChGetIrf(irf);
- if (NULL == pnrlT->pfxTail)
- pfx->pfx = pfx;
- else {
- pfx->pfx = pnrlT->pfxTail->pfx;
- pnrlT->pfxTail->pfx = pfx;
- }
- pnrlT->pfxTail = pfx;
- } else
- ChGetIrf(irf);
- break;
- }
- case 0xB0: { /** Controller change **/
- int bValue;
-
- bValue = ChGetIrf(irf);
- switch (bValue) {
- case 1: /*** Modulation wheel (ie. vibrato) ***/
- wModwheel = bValue;
- break;
- case 6: /*** Pitch bend sensativity ***/
- wBendsen = bValue;
- break;
- }
- break;
- }
- case 0xE0: /** Pitch Wheel change **/
- ChGetIrf(irf);
- break;
- case 0xC0: /** Program change **/
- rgbPatch[*pbStat - 0xC0] = bEvent;
- break;
- case 0xD0: /** Channel Pressure **/
- break;
- }
- vlqT = VlqFromIrf(irf);
- } while (0 == vlqT); /** Continue loop for simultaneous notes **/
- return vlqT;
- }
-
- /*
- * Freearray: Performs a free on all structures left in rgpnrl array.
- * Assume these notes are rebels.
- *
- * date: 30/6/1994 - now frees remaining fx too
- */
- void Freearray(void)
- {
- int i, j = 16;
- NRL *pnrlT, *pnrlT2;
- FX *pfxT, *pfxT2;
-
- while (j--) /** Go through hanging-note array **/
- for (i = 128; i--; )
- for (pnrlT = rgpnrl[j][i]; NULL != pnrlT; ) { /** freeing each list **/
- pnrlT2 = pnrlT->pnrl;
- pfxT = pnrlT->pfxTail->pfx;
- if (NULL != pfxT) {
- while (pfxT != pnrlT->pfxTail) { /** and each fx list **/
- pfxT2 = pfxT;
- pfxT = pfxT->pfx;
- free(pfxT2);
- }
- free(pfxT);
- }
- free(pnrlT);
- pnrlT = pnrlT2;
- }
- }
-
- /*
- * PtuneLoadPfile: Given the filename of a MIDI file, parse it and return
- * a pointer to a collection of chords (a Tune structure). If MIDI file
- * cannot be processed, NULL is returned.
- */
- Tune *PtuneLoadFn(Sz FnMIDI)
- {
- int fd, crf;
- unsigned long cb, *pvlqWait, vlqMin = -1, vlqT, wCount, wNcount, cDev = 0;
- unsigned irf, irfMax, *pbStatus, wQuant2, wDev, wMaxdev = 0, cMaxdev = 0;
- char rgbHeader[9] = {'M', 'T', 'h', 'd', 0, 0, 0, 6, 0}, rgbTest[9];
-
- Init();
- fd = open(FnMIDI, O_RDONLY | O_BINARY);
- read(fd, rgbTest, 9);
- if (memcmp(rgbHeader, rgbTest, 9) || ChGetFd(fd) > 1) {
- close(fd);
- return NULL; /** Only process type 0 or type 1 general MIDI files **/
- }
-
- irfMax = (unsigned) LongFromFd(fd, 2); /** Get # tracks **/
- wDivision = (int) LongFromFd(fd, 2); /** Get ticks for a beat **/
-
- if (-1 == lseek(fd, 23, SEEK_SET)) {
- close(fd);
- return NULL; /** Error if MIDI file is smaller than 23 bytes **/
- }
- if (fNocopy && ChGetFd(fd) == 0xFF && ChGetFd(fd) == 0x02) {
- close(fd);
- if (!fQuiet)
- printf("** NOCOPY option set and copyright notice found in file **\n");
- return NULL; /** Error if Nocopy and copyright notice exists **/
- }
- if (32767 < wDivision) {
- if (!fQuiet)
- printf("** Slack programmer error -- SMPTE frames not supported **\n");
- close(fd);
- return NULL;
- }
-
- if (ODD(wQuantval)) /** Calculate quantize ticks from quantize fraction **/
- wQuant = wDivision * 8 / (3 * (wQuantval - 1));
- else
- wQuant = wDivision * 4 / wQuantval;
- wQuant2 = wQuant/2;
- if (fStats) {
- printf(" Ticks to quantize: %u\n", wQuant);
- printf(" Number of tracks: %d\n", irfMax);
- }
-
- prf = (RF *) malloc(sizeof(RF) * irfMax);
- pvlqWait = (VLQ *) malloc(sizeof(VLQ) * irfMax);
- pbStatus = (unsigned *) malloc(sizeof(unsigned) * irfMax);
-
- lseek(fd, 18, SEEK_SET);
- /** Put file descriptors at the start of each track in file **/
- for (irf = 0; !eof(fd) && irf < irfMax; irf++) {
- cb = LongFromFd(fd, 4);
- if ((prf[irf].fd = open(FnMIDI, O_RDONLY | O_BINARY)) == -1) {
- ERROR;
- exit(1);
- }
- lseek(prf[irf].fd, tell(fd), SEEK_SET);
- read(prf[irf].fd, prf[irf].rgb, BUFSIZ);
- prf[irf].ib = 0;
- if ((pvlqWait[irf] = VlqFromIrf(irf)) < vlqMin)
- vlqMin = pvlqWait[irf]; /** Find minimum time to first event **/
- pbStatus[irf] = 0x90;
- lseek(fd, cb + 4, SEEK_CUR);
- }
- close(fd);
- if (irf != irfMax) { /** If things look a bit suspicious **/
- printf("** MIDI file ends prematurely **\n");
- free(prf); /** Free prf - NB. not closed **/
- free(pvlqWait);
- free(pbStatus);
- return NULL;
- }
-
- ptuneMain = ptuneCurr = (Tune *) malloc(sizeof(Tune));
- wCount = vlqMin; /** Start from first event **/
- wNcount = (wCount + wQuant2) % wQuant;
- if (fStats) {
- wDev = abs(wQuant2 - (int) wNcount);
- if (wDev > 2)
- cDev++;
- if (wDev > wMaxdev) {
- wMaxdev = wDev;
- cMaxdev = 1;
- } else if (wDev == wMaxdev)
- cMaxdev++;
- }
- ptuneMain->count = wCount + wQuant2 - wNcount;
- wNcount = wQuant - wNcount;
- ptuneMain->ptune = NULL;
- ptuneMain->pei = NULL;
- crf = 1;
- while (0 < crf) { /** While still tracks in file to process **/
- crf = 0;
- vlqT = -1;
- for (irf = 0; irf < irfMax; irf++) /** With each track.. **/
- if (-1 != prf[irf].fd) { /** If not finished **/
- crf++;
- /**
- ** Must keep all tracks in sync, if events occurring on this track
- ** at this instant, then interpret them. Also note when next event
- ** will occur (ie. minimum ticks to next event)
- **/
- if ((pvlqWait[irf] -= vlqMin) == 0)
- pvlqWait[irf] = VlqInterpIrf(irf, pbStatus + irf);
- if (pvlqWait[irf] < vlqT)
- vlqT = pvlqWait[irf];
- }
-
- vlqMin = vlqT;
- wCount += vlqMin;
- if (wNcount <= vlqMin) { /** If need to advance to new quanta **/
- if ((ptuneCurr->ptune = (Tune *) malloc(sizeof(Tune))) == NULL) {
- ERROR;
- exit(1);
- } /** allocate **/
- ptuneCurr = ptuneCurr->ptune; /** and initialize **/
- wNcount = (wCount + wQuant2) % wQuant;
- if (fStats) {
- wDev = abs(wQuant2 - (int) wNcount);
- if (wDev > 2)
- cDev++;
- if (wDev > wMaxdev) {
- wMaxdev = wDev;
- cMaxdev = 1;
- } else if (wDev == wMaxdev)
- cMaxdev++;
- }
- ptuneCurr->count = wCount + wQuant2 - wNcount;
- wNcount = wQuant - wNcount;
- ptuneCurr->ptune = NULL;
- ptuneCurr->pei = NULL;
- } else
- wNcount -= vlqMin; /** Else decrememnt "new quanta" count **/
- }
-
- Freearray();
- free(pvlqWait);
- free(pbStatus);
- free(prf);
-
- if (fStats) {
- printf(" Maximum quantize error: %u\n", wMaxdev);
- printf(" Number of times it occurred: %u\n", cMaxdev);
- printf(" Number of times quantize error > 2 occurred: %lu\n", cDev);
- }
-
- return ptuneMain;
- }
-