|
|
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:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.