|
|
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.