Annotation of Examples/SoundAndMusic/MidiDriver/midifile.c, revision 1.1

1.1     ! root        1: /* Based on original version written by Lee Boynton, revised by David Jaffe. 
        !             2:  */
        !             3: 
        !             4: #import <appkit/nextstd.h>
        !             5: #import <mididriver/midi_spec.h>
        !             6: #import "midifile.h"
        !             7: 
        !             8: /* Some metaevents */
        !             9: #define SEQUENCENUMBER 0
        !            10: #define TRACKCHANGE 0x2f
        !            11: #define TEMPOCHANGE 0x51
        !            12: #define SMPTEOFFSET 0x54
        !            13: #define TIMESIG 0x58
        !            14: #define KEYSIG 0x59
        !            15: 
        !            16: #define DEFAULTTEMPO (120.0)
        !            17: #define DEFAULTDIVISION 1024
        !            18: 
        !            19: /*
        !            20:  * reading
        !            21:  */
        !            22: 
        !            23: typedef struct _MIDIFILEInStruct {  /* Private structure for this module. */
        !            24:     double tempo;    /* in quarter notes per minute */
        !            25:     double timeScale;/* timeScale * currentTime gives time in seconds */
        !            26:     int currentTrack;/* Current track number */
        !            27:     int currentTime; /* Current time in quanta. */
        !            28:     int division;    /* # of delta-time ticks per quarter. (See spec) */
        !            29:     short format;    /* Level 0, 1 or 2 */
        !            30:     int quantaSize;  /* In micro-seconds. */
        !            31:     unsigned char runningStatus; /* Current MIDI running status */
        !            32:     NXStream *s;     /* midifile stream */
        !            33:     int curBufSize;  /* Size of data buffer */
        !            34:     MIDIFILEReadStruct *m;     /* Info for current event */
        !            35: } MIDIFILEInStruct;
        !            36: 
        !            37: void *MIDIFILEBeginReading(NXStream *s,MIDIFILEReadStruct *m)
        !            38: {
        !            39:     MIDIFILEInStruct *rtn;
        !            40:     NX_MALLOC(rtn,MIDIFILEInStruct,1);
        !            41:     rtn->tempo = DEFAULTTEMPO;             /* in beats per minute */
        !            42:     rtn->currentTrack = -1; /* We call the first or "tempo track" 
        !            43:                               "track 0". Therefore, we start counting at -1
        !            44:                               here. */ 
        !            45:     rtn->currentTime = 0;
        !            46:     rtn->division = 0;
        !            47:     rtn->format = 0;
        !            48:     rtn->quantaSize = MIDIFILE_DEFAULTQUANTASIZE; /* size in microseconds */
        !            49:     rtn->s = s;
        !            50:     /* Malloc enough for SMPTEoffset metaevent. Realloc longer fields later */
        !            51:     rtn->curBufSize = 6;
        !            52:     rtn->m = m;
        !            53:     NX_MALLOC(rtn->m->data,unsigned char,rtn->curBufSize);
        !            54:     /* Values are always returned indirectly in these fields. */
        !            55:     return rtn;
        !            56: }
        !            57: 
        !            58: #define IP ((MIDIFILEInStruct *)p)
        !            59: 
        !            60: void *MIDIFILEEndReading(void *p)
        !            61: {
        !            62:     NX_FREE(IP->m->data);
        !            63:     NX_FREE(IP);
        !            64:     return NULL;
        !            65: }
        !            66: 
        !            67: enum {unrecognized = -1,endOfStream = 0,ok = 1,undefined,
        !            68:        /* Multi-packet sys excl: */
        !            69:        firstISysExcl,middleISysExcl,endISysExcl, 
        !            70:        /* Single-packet sys excl */
        !            71:        sysExcl};                                 
        !            72: 
        !            73: static int readChunkType(NXStream *s,char *buf)
        !            74: {
        !            75:     int count = NXRead(s,buf,4);
        !            76:     buf[4] = '\0';
        !            77:     return (count == 4)? ok : 0;
        !            78: }
        !            79: 
        !            80: static int readLong(NXStream *s, int *n)
        !            81: {
        !            82:     int count = NXRead(s,n,4);
        !            83:     return (count == 4)? ok : 0;
        !            84: }
        !            85: 
        !            86: static int readBytes(NXStream *s, unsigned char *bytes,int n)
        !            87: {
        !            88:     int count = NXRead(s,bytes,n);
        !            89:     return (count == n) ? ok : 0;
        !            90: }
        !            91: 
        !            92: static int readShort(NXStream *s, short *n)
        !            93: {
        !            94:     int count = NXRead(s,n,2);
        !            95:     return (count == 2)? ok : 0;
        !            96: }
        !            97: 
        !            98: static int readVariableQuantity(NXStream *s, int *n)
        !            99: {
        !           100:     int m = 0;
        !           101:     unsigned char temp;
        !           102:     while (NXRead(s,&temp,1) > 0) {
        !           103:        if (128 & temp)
        !           104:          m = (m<<7) + (temp & 127);
        !           105:        else {
        !           106:            *n = (m<<7) + (temp & 127);
        !           107:            return ok;
        !           108:        }
        !           109:     }
        !           110:     return endOfStream;
        !           111: }
        !           112: 
        !           113: static int readTrackHeader(MIDIFILEInStruct *p)
        !           114: {
        !           115:     char typebuf[8];
        !           116:     int size;
        !           117:     if (!readChunkType(p->s,typebuf)) 
        !           118:       return endOfStream;
        !           119:     if (strcmp(typebuf,"MTrk")) 
        !           120:       return endOfStream;
        !           121:     p->currentTrack++;
        !           122:     p->currentTime = 0;
        !           123:     if (!readLong(p->s,&size)) 
        !           124:       return endOfStream;
        !           125:     return ok;
        !           126: }
        !           127: 
        !           128: static void checkRealloc(MIDIFILEInStruct *p,int newSize)
        !           129: {
        !           130:     if (p->curBufSize < newSize)
        !           131:       NX_REALLOC(p->m->data,unsigned char,newSize);
        !           132:     p->curBufSize = newSize;
        !           133: }
        !           134: 
        !           135: static int readMetaevent(MIDIFILEInStruct *p)
        !           136: {
        !           137:     unsigned char theByte;
        !           138:     int temp;
        !           139:     int len;
        !           140:     if (!(NXRead(p->s,&theByte,1) && readVariableQuantity(p->s,&len)))
        !           141:       return endOfStream;
        !           142:     if (theByte == SEQUENCENUMBER) {
        !           143:        short val;
        !           144:        if (!readShort(p->s,&val))
        !           145:          return endOfStream;
        !           146:        p->m->data[0] = MIDIFILE_sequenceNumber;
        !           147:        p->m->data[1] = val >> 8;
        !           148:        p->m->data[2] = val;
        !           149:        len -= 2;
        !           150:        p->m->nData = 3;
        !           151:     }
        !           152:     else if (theByte >= 1 && theByte <= 0x0f) { /* Text meta events */
        !           153:        p->m->data[0] = theByte;
        !           154:        p->m->nData = len + 1; /* nData doesn't include the \0 */
        !           155:        checkRealloc(p,p->m->nData + 1);
        !           156:        if (!readBytes(p->s,&(p->m->data[1]),p->m->nData - 1))
        !           157:          return endOfStream;
        !           158:        p->m->data[p->m->nData] = '\0';
        !           159:        return ok;
        !           160:     }
        !           161:     else if (theByte == TRACKCHANGE) {                 /* end of track */
        !           162:        temp = readTrackHeader(p);
        !           163:        if (temp == endOfStream) 
        !           164:          return endOfStream;
        !           165:        /* trackChange doesn't have any args but we pass up the track number,
        !           166:           so no len -= needed.
        !           167:         */
        !           168:        p->m->nData = 3;
        !           169:        p->m->data[0] = MIDIFILE_trackChange;
        !           170:        p->m->data[1] = (p->currentTrack >> 8);
        !           171:        p->m->data[2] = p->currentTrack;
        !           172:     } 
        !           173:     else if (theByte == TEMPOCHANGE) {                  /* tempo */
        !           174:        double n;
        !           175:        int i;
        !           176:        if (!readBytes(p->s,&(p->m->data[1]),3))    /* 24 bits */
        !           177:          return endOfStream;
        !           178:        i = (p->m->data[1] << 16) | (p->m->data[2] << 8) | p->m->data[3];
        !           179:        n = (double)i;
        !           180:        /* tempo in file is in micro-seconds per quarter note */
        !           181:        p->tempo = 60000000.0 / n + 0.5; 
        !           182:        i = p->tempo;
        !           183:        /* division is the number of delta time "ticks" that make up a 
        !           184:           quarter note. Quanta size is in micro seconds. */
        !           185:        p->timeScale = n / (double)(p->division * p->quantaSize);
        !           186:        p->m->data[0] = MIDIFILE_tempoChange;
        !           187:        /* It's a 3 byte quantity but we store it in 4 bytes.*/
        !           188:        p->m->data[1] = 0;         
        !           189:        p->m->data[2] = (i >> 16);
        !           190:        p->m->data[3] = (i >> 8);
        !           191:        p->m->data[4] = i;
        !           192:        p->m->nData = 5;
        !           193:        len -= 3;
        !           194:     } 
        !           195:     else if (theByte == SMPTEOFFSET) {
        !           196:        p->m->data[0] = MIDIFILE_smpteOffset;
        !           197:        if (!readBytes(p->s,&(p->m->data[1]),5))
        !           198:          return endOfStream;
        !           199:        p->m->nData = 6;
        !           200:        len -= 5;
        !           201:     } 
        !           202:     else if (theByte == TIMESIG) {
        !           203:        if (!readBytes(p->s,&p->m->data[1],4))
        !           204:          return endOfStream;
        !           205:        p->m->data[0] = MIDIFILE_timeSig;
        !           206:        p->m->nData = 5;
        !           207:        len -= 4;
        !           208:     } 
        !           209:     else if (theByte == KEYSIG) {
        !           210:        if (!readBytes(p->s,&p->m->data[1],2))
        !           211:          return endOfStream;
        !           212:        p->m->data[0] = MIDIFILE_keySig;
        !           213:        p->m->nData = 3;
        !           214:        len -= 2;
        !           215:     } 
        !           216:     else { /* Skip unrecognized meta events */
        !           217:        if (!readVariableQuantity(p->s,&temp))
        !           218:          return endOfStream;
        !           219:        NXSeek(p->s,temp,NX_FROMCURRENT);
        !           220:        return unrecognized;
        !           221:     }
        !           222:     NXSeek(p->s,len,NX_FROMCURRENT); /* Skip any extra length in field. */
        !           223:     return ok;
        !           224: }
        !           225: 
        !           226: /* We do not support multi-packet system exclusive messages with different
        !           227:    timings. When such a beast occurs, it is concatenated into a single
        !           228:    event and the time stamp is that of the last piece of the event. */
        !           229: 
        !           230: static int readSysExclEvent(MIDIFILEInStruct *p,int oldState)
        !           231: {
        !           232:     int len;
        !           233:     unsigned char *ptr;
        !           234:     if (!readVariableQuantity(p->s,&len))
        !           235:       return endOfStream;
        !           236:     if (oldState == undefined) {
        !           237:        checkRealloc(p,len + 1); /* len doesn't include data[0] */
        !           238:        p->m->data[0] = MIDI_SYSEXCL;
        !           239:        p->m->nData = len + 1;
        !           240:        ptr = &(p->m->data[1]);
        !           241:     } else {      /* firstISysExcl or middleISysExcl */
        !           242:        checkRealloc(p,len + p->m->nData);
        !           243:        ptr = &(p->m->data[p->m->nData]);
        !           244:        p->m->nData += len; 
        !           245:     }
        !           246:     if (readBytes(p->s,ptr,len) == endOfStream)
        !           247:       return endOfStream;
        !           248:     return ((p->m->data[p->m->nData - 1] == MIDI_EOX) ? 
        !           249:            ((oldState == undefined) ? sysExcl : endISysExcl) : 
        !           250:            ((oldState == undefined) ? firstISysExcl : middleISysExcl));
        !           251: }
        !           252: 
        !           253: static int readEscapeEvent(MIDIFILEInStruct *p)
        !           254: {
        !           255:     if (!readVariableQuantity(p->s,&(p->m->nData)))
        !           256:       return endOfStream;
        !           257:     checkRealloc(p,p->m->nData);
        !           258:     return readBytes(p->s,p->m->data, p->m->nData);
        !           259: }
        !           260: 
        !           261: #define SCALEQUANTA(_p,quanta) \
        !           262:   ((int)(0.5 + (((MIDIFILEInStruct *)_p)->timeScale * (double)quanta)))
        !           263: 
        !           264: /*
        !           265:  * Exported routines
        !           266:  */
        !           267: 
        !           268: int MIDIFILEReadPreamble(void *p,int *level,int *trackCount)
        !           269: {
        !           270:     char typebuf[8];
        !           271:     int size;
        !           272:     short fmt, tracks, div;
        !           273:     
        !           274:     if ((!readChunkType(IP->s,typebuf)) || 
        !           275:        (strcmp(typebuf,"MThd")) ||  /* not a MIDIFILE */ 
        !           276:        (!readLong(IP->s,&size)) ||
        !           277:        (size < 6) ||               /* bad header */ 
        !           278:        (!readShort(IP->s,&fmt)) || 
        !           279:        (fmt < 0 || fmt > 2)  ||     /* must be level 0, 1 or 2 */
        !           280:        (!readShort(IP->s,&tracks)) ||  
        !           281:        (!readShort(IP->s,&div))) 
        !           282:       return endOfStream;
        !           283:     size -= 6;
        !           284:     if (size)
        !           285:       NXSeek(IP->s,size,NX_FROMCURRENT); /* Skip any extra length in field. */
        !           286:     *trackCount = fmt ? tracks-1 : 1;
        !           287:     *level = IP->format = fmt;
        !           288:     if (div < 0) { /* Time code encoding? */
        !           289:        /* For now, we undo the effect of the time code. We may want to
        !           290:           eventually pass the time code up? */ 
        !           291:        short SMPTEformat,ticksPerFrame;
        !           292:        ticksPerFrame = div & 0xff;
        !           293:        SMPTEformat = -(div >> 8); 
        !           294:        /* SMPTEformat is one of 24, 25, 29, or 30. It's stored negative */
        !           295:        div = ticksPerFrame * SMPTEformat;
        !           296:     }
        !           297:     IP->division = div;
        !           298:     IP->currentTrack = -1;
        !           299:     IP->timeScale = 60000000.0 / (double)(IP->division * IP->quantaSize);
        !           300:     return ok;
        !           301: }
        !           302: 
        !           303: int MIDIFILEReadEvent(register void *p)
        !           304:     /* return endOfStream when EOS is reached, return 1 otherwise.
        !           305:        Data should be an array of length 3. */
        !           306: {
        !           307:     int deltaTime,quantaTime,state = undefined;
        !           308:     unsigned char theByte;
        !           309:     if (IP->currentTrack < 0 && !readTrackHeader(p)) 
        !           310:       return endOfStream;
        !           311:     for (;;) {
        !           312:        if (!readVariableQuantity(IP->s,&deltaTime)) 
        !           313:          return endOfStream;
        !           314:        IP->currentTime += deltaTime;
        !           315:        quantaTime = SCALEQUANTA(p,IP->currentTime);
        !           316:        if (!NXRead(IP->s,&theByte,1)) 
        !           317:          return endOfStream;
        !           318:        if (theByte == 0xff) {
        !           319:            state = readMetaevent(p);
        !           320:            IP->m->metaEventFlag = YES;
        !           321:            if (state != unrecognized) {
        !           322:                IP->m->quanta = quantaTime;
        !           323:                return state;
        !           324:            }
        !           325:        } else if ((theByte == MIDI_SYSEXCL) || (state != undefined)) {
        !           326:            /* System exclusive */
        !           327:            state = readSysExclEvent(p,state);
        !           328:            IP->m->metaEventFlag = NO;
        !           329:            switch (state) {
        !           330:              case firstISysExcl:
        !           331:                IP->m->quanta = quantaTime;
        !           332:                break;
        !           333:              case middleISysExcl:
        !           334:                IP->m->quanta += quantaTime;
        !           335:                break;
        !           336:              case endISysExcl:
        !           337:                IP->m->quanta += quantaTime;
        !           338:                return ok;
        !           339:              case endOfStream:
        !           340:              case sysExcl:
        !           341:                IP->m->quanta = quantaTime;
        !           342:                return ok;
        !           343:              default:
        !           344:                break;
        !           345:            }
        !           346:        } else if (theByte == 0xf7) { /* Special "escape" code */
        !           347:            IP->m->quanta = quantaTime;
        !           348:            return readEscapeEvent(p);
        !           349:        } else { /* Normal MIDI */
        !           350:            BOOL newRunningStatus = (theByte & MIDI_STATUSBIT);
        !           351:            if (newRunningStatus)
        !           352:              IP->runningStatus = theByte;
        !           353:            IP->m->metaEventFlag = 0;
        !           354:            IP->m->quanta = quantaTime;
        !           355:            IP->m->nData = MIDI_EVENTSIZE(IP->runningStatus);
        !           356:            IP->m->data[0] = IP->runningStatus;
        !           357:            if (IP->m->nData > 1) {
        !           358:                if (newRunningStatus) {
        !           359:                    if (!NXRead(IP->s,&(IP->m->data[1]),1)) 
        !           360:                      return endOfStream;
        !           361:                }
        !           362:                else IP->m->data[1] = theByte;
        !           363:                if (IP->m->nData > 2)
        !           364:                  if (!NXRead(IP->s,&(IP->m->data[2]),1)) 
        !           365:                    return endOfStream;
        !           366:            }
        !           367:            return ok;
        !           368:        }
        !           369:     }
        !           370: }
        !           371: 
        !           372: 
        !           373: /*
        !           374:  * writing
        !           375:  */
        !           376: 
        !           377: typedef struct _MIDIFILEOutStruct {
        !           378:     double tempo;     
        !           379:     double timeScale; 
        !           380:     int currentTrack;
        !           381:     int division;
        !           382:     int currentCount;
        !           383:     int lastTime;
        !           384:     NXStream *s;
        !           385:     int quantaSize;
        !           386: } MIDIFILEOutStruct;
        !           387: 
        !           388: #define OP ((MIDIFILEOutStruct *)p)
        !           389: 
        !           390: static int writeBytes(MIDIFILEOutStruct *p, unsigned char *bytes,int count)
        !           391: {
        !           392:     int bytesWritten;
        !           393:     bytesWritten = NXWrite(p->s,bytes,count);
        !           394:     OP->currentCount += count;
        !           395:     if (bytesWritten != count)
        !           396:       return endOfStream;
        !           397:     else return ok;
        !           398: }
        !           399: 
        !           400: static int writeByte(MIDIFILEOutStruct *p, unsigned char n)
        !           401: {
        !           402:     int bytesWritten = NXWrite(p->s,&n,1);
        !           403:     p->currentCount += bytesWritten;
        !           404:     return bytesWritten;
        !           405: }
        !           406: 
        !           407: static int writeShort(MIDIFILEOutStruct *p, short n)
        !           408: {
        !           409:     int bytesWritten = NXWrite(p->s,&n,2);
        !           410:     p->currentCount += bytesWritten;
        !           411:     return (bytesWritten == 2) ? ok : endOfStream;
        !           412: }
        !           413: 
        !           414: static int writeLong(MIDIFILEOutStruct *p, int n)
        !           415: {
        !           416:     int bytesWritten = NXWrite(p->s,&n,4);
        !           417:     p->currentCount += bytesWritten;
        !           418:     return (bytesWritten == 4) ? ok : endOfStream;
        !           419: }
        !           420: 
        !           421: static int writeChunkType(MIDIFILEOutStruct *p, char *buf)
        !           422: {
        !           423:     int bytesWritten = NXWrite(p->s,buf,4);
        !           424:     p->currentCount += bytesWritten;
        !           425:     return (bytesWritten == 4) ? ok : endOfStream;
        !           426: }
        !           427: 
        !           428: static int writeVariableQuantity(MIDIFILEOutStruct *p, int n)
        !           429: {
        !           430:     if ((n >= (1 << 28) && !writeByte(p,(((n>>28)&15)|128) )) ||
        !           431:        (n >= (1 << 21) && !writeByte(p,(((n>>21)&127)|128) )) ||
        !           432:        (n >= (1 << 14) && !writeByte(p,(((n>>14)&127)|128) )) ||
        !           433:        (n >= (1 << 7) && !writeByte(p,(((n>>7)&127)|128) ))) 
        !           434:       return endOfStream;
        !           435:     return writeByte(p,(n&127));
        !           436: }
        !           437: 
        !           438: void *MIDIFILEBeginWriting(NXStream *s, int level, char *sequenceName)
        !           439: {
        !           440:     short lev = level, div = DEFAULTDIVISION, ntracks = 1;
        !           441:     MIDIFILEOutStruct *p;
        !           442:     NX_MALLOC(p,MIDIFILEOutStruct,1);
        !           443:     OP->tempo = DEFAULTTEMPO;      /* in beats per minute */
        !           444:     OP->quantaSize = MIDIFILE_DEFAULTQUANTASIZE; /* size in microseconds */
        !           445:     OP->lastTime = 0;
        !           446:     OP->s = s;
        !           447:     if ((!writeChunkType(p,"MThd")) || (!writeLong(p,6)) ||
        !           448:        !writeShort(p,lev) || !writeShort(p,ntracks) || !writeShort(p,div))
        !           449:       return endOfStream;
        !           450:     OP->division = div;
        !           451:     OP->currentTrack = -1;
        !           452:     OP->timeScale = 60000000.0 / (double)(OP->division * OP->quantaSize);
        !           453:     OP->currentCount = 0;
        !           454:     if (MIDIFILEBeginWritingTrack(p,sequenceName))
        !           455:       return p;
        !           456:     else {
        !           457:        NX_FREE(p);
        !           458:        return NULL;
        !           459:     }
        !           460: }
        !           461: 
        !           462: int MIDIFILEEndWriting(void *p)
        !           463: {
        !           464:     short ntracks = OP->currentTrack+1; /* +1 for "tempo track" */
        !           465:     if (OP->currentCount) {  /* Did we forget to finish before? */
        !           466:        int err = MIDIFILEEndWritingTrack(p,0);
        !           467:        if (err == endOfStream) {
        !           468:            NX_FREE(p);
        !           469:            return endOfStream;
        !           470:        }
        !           471:     }
        !           472:     NXSeek(OP->s,10,NX_FROMSTART);
        !           473:     if (NXWrite(OP->s,&ntracks,2) != 2) {
        !           474:        NX_FREE(p);
        !           475:        return endOfStream;
        !           476:     }
        !           477:     NXSeek(OP->s,0,NX_FROMEND);
        !           478:     NX_FREE(p);
        !           479:     return ok;
        !           480: }
        !           481: 
        !           482: int MIDIFILEBeginWritingTrack(void *p, char *trackName)
        !           483: {
        !           484:     if (OP->currentCount) /* Did we forget to finish before? */
        !           485:       MIDIFILEEndWritingTrack(p,0);
        !           486:     if (!writeChunkType(p,"MTrk")) 
        !           487:       return endOfStream;
        !           488:     if (!writeLong(p,0))  /* This will be the length of the track. */
        !           489:       return endOfStream;
        !           490:     OP->lastTime = 0;
        !           491:     OP->currentTrack++;
        !           492:     OP->currentCount = 0; /* Set this after the "MTrk" and dummy length are
        !           493:                             written */
        !           494:     if (trackName) {
        !           495:        int i = strlen(trackName);
        !           496:        if (i) {
        !           497:            if (!writeByte(p,0) || !writeByte(p,0xff) || !writeByte(p,0x03) || 
        !           498:                !writeVariableQuantity(p,i) ||
        !           499:                !writeBytes(p,(unsigned char *)trackName,i))  
        !           500:              return endOfStream;
        !           501:        }
        !           502:     }
        !           503:     return ok;
        !           504: }
        !           505: 
        !           506: static int writeTime(MIDIFILEOutStruct *p, int quanta)
        !           507: {
        !           508:     int thisTime = (int)(0.5 + (OP->timeScale * quanta));
        !           509:     int deltaTime = thisTime - OP->lastTime;
        !           510:     OP->lastTime = thisTime;
        !           511:     if (!writeVariableQuantity(p,deltaTime)) 
        !           512:       return endOfStream;
        !           513:     return ok;
        !           514: }
        !           515: 
        !           516: #define METAEVENT(_x) (0xff00 | _x)
        !           517: 
        !           518: int MIDIFILEEndWritingTrack(void *p,int quanta)
        !           519: {
        !           520:     if (!writeTime(p,quanta) || !writeShort(p,METAEVENT(TRACKCHANGE)) || 
        !           521:        !writeByte(p,0)) 
        !           522:        return endOfStream;
        !           523:     /* Seek back to the track length field for this track. */
        !           524:     NXSeek(OP->s,-(OP->currentCount+4),NX_FROMCURRENT);
        !           525:     /* +4 because we don't include the "MTrk" specification and length
        !           526:        in the count */
        !           527:     if (NXWrite(OP->s,&OP->currentCount,4) != 4) 
        !           528:       return endOfStream;
        !           529:     /* Seek to end again. */
        !           530:     NXSeek(OP->s,OP->currentCount,NX_FROMCURRENT);
        !           531:     OP->currentCount = 0; /* Signals other functions that we've just finished
        !           532:                             a track. */
        !           533:     return ok;
        !           534: }
        !           535: 
        !           536: 
        !           537: int MIDIFILEWriteSig(void *p,int quanta,short metaevent,unsigned data)
        !           538: {
        !           539:     BOOL keySig = (metaevent == MIDIFILE_keySig);
        !           540:     unsigned char byteCount = (keySig) ? 2 : 4;
        !           541:     metaevent = METAEVENT(((keySig) ? KEYSIG : TIMESIG));
        !           542:     if (!writeTime(p,quanta) || !writeShort(p,metaevent) ||
        !           543:        !writeByte(p,byteCount))
        !           544:       return endOfStream;
        !           545:     return (keySig) ? writeShort(p,data) : writeLong(p,data);
        !           546: }
        !           547: 
        !           548: int MIDIFILEWriteText(void *p,int quanta,short metaevent,char *text)
        !           549: {
        !           550:     int i;
        !           551:     metaevent = METAEVENT(metaevent);
        !           552:     if (!text)
        !           553:       return ok;
        !           554:     i = strlen(text);
        !           555:     if (!writeTime(p,quanta) || !writeShort(p,metaevent) ||
        !           556:        !writeVariableQuantity(p,i) || !writeBytes(p,(unsigned char *)text,i))
        !           557:       return endOfStream;
        !           558:     return ok;
        !           559: }
        !           560: 
        !           561: int MIDIFILEWriteSMPTEoffset(void *p,unsigned char hr,unsigned char min,
        !           562:                                unsigned char sec,unsigned char ff,
        !           563:                                unsigned char fr)
        !           564: {
        !           565:     if (!writeByte(p,0) ||  /* Delta-time is always 0 for SMPTE offset */
        !           566:        !writeShort(p,METAEVENT(SMPTEOFFSET)) ||
        !           567:        !writeByte(p,5) || !writeByte(p,hr) || !writeByte(p,min) ||
        !           568:        !writeByte(p,sec) || !writeByte(p,fr) || !writeByte(p,ff))
        !           569:       return endOfStream;
        !           570:     return ok;
        !           571: }
        !           572: 
        !           573: int MIDIFILEWriteSequenceNumber(void *p,int data)
        !           574: {
        !           575:     if (!writeByte(p,0) || /* Delta time is 0 */
        !           576:        !writeShort(p,METAEVENT(SEQUENCENUMBER)) ||
        !           577:        !writeByte(p,2) || !writeShort(p,data))
        !           578:       return endOfStream;
        !           579:     return ok;
        !           580: }
        !           581: 
        !           582: int MIDIFILEWriteTempo(void *p,int quanta, int beatsPerMinute)
        !           583: {
        !           584:     int n;
        !           585:     OP->tempo = beatsPerMinute;
        !           586:     n = (int)(0.5 + (60000000.0 / OP->tempo));
        !           587:     OP->timeScale = (double)(OP->division * OP->quantaSize) / (double)n;
        !           588:     n &= 0x00ffffff;
        !           589:     n |= 0x03000000;
        !           590:     if (!writeTime(p,quanta) || !writeShort(p,METAEVENT(TEMPOCHANGE)) || 
        !           591:        !writeLong(p,n)) 
        !           592:       return endOfStream;
        !           593:     return ok;
        !           594: }
        !           595: 
        !           596: int MIDIFILEWriteEvent(register void *p,int quanta,int nData,
        !           597:                          unsigned char *bytes)
        !           598: {
        !           599:     if (!writeTime(p,quanta))
        !           600:       return endOfStream;
        !           601:     if (nData && MIDI_TYPE_SYSTEM(bytes[0])) 
        !           602:       if (!writeByte(p,MIDI_EOX) ||  /* Escape byte */
        !           603:          !writeVariableQuantity(p,nData)) /* Length of message */
        !           604:        return endOfStream;
        !           605:     if (!writeBytes(p,bytes,nData)) 
        !           606:       return endOfStream;
        !           607:     return ok;
        !           608: }
        !           609: 
        !           610: int MIDIFILEWriteSysExcl(void *p,int quanta,int nData,unsigned char *bytes)
        !           611:     /* Assumes there's a MIDI_SYSEXCL at start and there is a
        !           612:      * MIDI_EOX at end. */
        !           613: {
        !           614:     if (!writeTime(p,quanta) || !writeByte(p,MIDI_SYSEXCL) ||
        !           615:        !writeVariableQuantity(p,nData-1) || !writeBytes(p,bytes+1,nData-1))
        !           616:       return endOfStream;
        !           617:     return ok;
        !           618: }
        !           619: 
        !           620: int MIDIFILESetReadQuantaSize(void *p,int usec)
        !           621: {
        !           622:     IP->quantaSize = usec;
        !           623:     if (IP->division) {
        !           624:        double n = 60000000.0 / IP->tempo;
        !           625:        IP->timeScale = n / (double)(IP->division * IP->quantaSize);
        !           626:     }
        !           627:     return usec;
        !           628: }
        !           629: 
        !           630: 
        !           631: 

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.