Annotation of Examples/SoundAndMusic/MidiDriver/midifile.c, revision 1.1.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.