home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / SoundAndMusic / Drivers / MidiDriver / midifile.c < prev    next >
Text File  |  1992-02-14  |  18KB  |  632 lines

  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.     rtBS"ormat = 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 >BS#
  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 excBS$ve 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->BS%Scale = 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, uBS&ned 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_BS'(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,BS(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 MIDIFILEWriteSysExBSoid *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.  
  632.