1 /**
2 	This file is a port of some old C code I had.
3 
4 	I'll eventually refactor it into something more D-like
5 */
6 module arsd.midi;
7 
8 
9 import core.stdc.stdio;
10 import core.stdc.stdlib;
11 
12 /* NOTE: MIDI files are BIG ENDIAN! */
13 
14 struct MidiChunk {
15 	int timeStamp; // this is encoded my way. real file is msb == 1, more ubytes
16 	ubyte event; 	// Event << 8 | channel is how it is actually stored
17 
18 	// for channel events
19 	ubyte channel; 	// see above - it is stored with event!
20 			// channel == track btw
21 	ubyte param1; 	// pitch (usually)
22 	ubyte param2; 	// volume  - not necessarily present
23 
24 	ubyte status; // event << 4 | channel
25 
26 	// for meta events (event = f, channel = f
27 	ubyte type;
28 	int length; // stored as variable length
29 	ubyte* data; // only allocated if event == 255
30 
31 	MidiChunk* next; // next in the track
32 
33 	// This stuff is just for playing help and such
34 	// It is only set if you call recalculateMidiAbsolutes, and probably
35 	// not maintained if you do edits
36 	int track;
37 	uint absoluteTime;
38 	uint absoluteTimeInMilliSeconds; // for convenience
39 	int absoluteWait;
40 	MidiChunk* nextAbsolute;
41 }
42 
43 /*
44 	Meta event
45 	timeStamp = 0
46 	event = 255
47 	channel = event
48 	param1 param2 = not in gile
49 	length = variable
50 	data[length]
51 */
52 
53 struct MidiTrack {
54 	// MTrk header
55 	int lengthInBytes;
56 	MidiChunk* chunks; // linked list
57 		// the linked list should NOT hold the track ending chunk
58 		// just hold a null instead
59 }
60 
61 struct Midi {
62 	// headers go here
63 	short type;
64 	short numTracks;
65 	short speed;
66 	MidiTrack* tracks; /* Array of numTracks size */
67 
68 	// only set if you call recalculateMidiAbsolutes
69 	MidiChunk* firstAbsolute;
70 }
71 
72 
73 enum MIDI_EVENT_NOTE_OFF =		0x08;
74 enum MIDI_EVENT_NOTE_ON =		0x09;
75 enum MIDI_EVENT_NOTE_AFTERTOUCH =	0x0a;
76 enum MIDI_EVENT_CONTROLLER =		0x0b;
77 enum MIDI_EVENT_PROGRAM_CHANGE =	0x0c;// only one param
78 enum MIDI_EVENT_CHANNEL_AFTERTOUCH =	0x0d;// only one param
79 enum MIDI_EVENT_PITCH_BEND =		0x0e;
80 
81 
82 
83 /*
84 static char[][] instrumentNames = {
85 "", // 0 is nothing
86 // Piano:
87 "Acoustic Grand Piano",
88 "Bright Acoustic Piano",
89 "Electric Grand Piano",
90 "Honky-tonk Piano",
91 "Electric Piano 1",
92 "Electric Piano 2",
93 "Harpsichord",
94 "Clavinet",
95 
96 // Chromatic Percussion:
97 "Celesta",
98 "Glockenspiel",
99 "Music Box",
100 "Vibraphone",
101 "Marimba",
102 "Xylophone",
103 "Tubular Bells",
104 "Dulcimer",
105 
106 // Organ:
107 "Drawbar Organ",
108 "Percussive Organ",
109 "Rock Organ",
110 "Church Organ",
111 "Reed Organ",
112 "Accordion",
113 "Harmonica",
114 "Tango Accordion",
115 
116 // Guitar:
117 "Acoustic Guitar (nylon)",
118 "Acoustic Guitar (steel)",
119 "Electric Guitar (jazz)",
120 "Electric Guitar (clean)",
121 "Electric Guitar (muted)",
122 "Overdriven Guitar",
123 "Distortion Guitar",
124 "Guitar harmonics",
125 
126 // Bass:
127 "Acoustic Bass",
128 "Electric Bass (finger)",
129 "Electric Bass (pick)",
130 "Fretless Bass",
131 "Slap Bass 1",
132 "Slap Bass 2",
133 "Synth Bass 1",
134 "Synth Bass 2",
135 
136 // Strings:
137 "Violin",
138 "Viola",
139 "Cello",
140 "Contrabass",
141 "Tremolo Strings",
142 "Pizzicato Strings",
143 "Orchestral Harp",
144 "Timpani",
145 
146 // Strings (continued):
147 "String Ensemble 1",
148 "String Ensemble 2",
149 "Synth Strings 1",
150 "Synth Strings 2",
151 "Choir Aahs",
152 "Voice Oohs",
153 "Synth Voice",
154 "Orchestra Hit",
155 
156 // Brass:
157 "Trumpet",
158 "Trombone",
159 "Tuba",
160 "Muted Trumpet",
161 "French Horn",
162 "Brass Section",
163 "Synth Brass 1",
164 "Synth Brass 2",
165 
166 // Reed:
167 "Soprano Sax",
168 "Alto Sax",
169 "Tenor Sax",
170 "Baritone Sax",
171 "Oboe",
172 "English Horn",
173 "Bassoon",
174 "Clarinet",
175 
176 // Pipe:
177 "Piccolo",
178 "Flute",
179 "Recorder",
180 "Pan Flute",
181 "Blown Bottle",
182 "Shakuhachi",
183 "Whistle",
184 "Ocarina",
185 
186 // Synth Lead:
187 "Lead 1 (square)",
188 "Lead 2 (sawtooth)",
189 "Lead 3 (calliope)",
190 "Lead 4 (chiff)",
191 "Lead 5 (charang)",
192 "Lead 6 (voice)",
193 "Lead 7 (fifths)",
194 "Lead 8 (bass + lead)",
195 
196 // Synth Pad:
197 "Pad 1 (new age)",
198 "Pad 2 (warm)",
199 "Pad 3 (polysynth)",
200 "Pad 4 (choir)",
201 "Pad 5 (bowed)",
202 "Pad 6 (metallic)",
203 "Pad 7 (halo)",
204 "Pad 8 (sweep)",
205 
206 // Synth Effects:
207 "FX 1 (rain)",
208 "FX 2 (soundtrack)",
209 "FX 3 (crystal)",
210 "FX 4 (atmosphere)",
211 "FX 5 (brightness)",
212 "FX 6 (goblins)",
213 "FX 7 (echoes)",
214 "FX 8 (sci-fi)",
215 
216 // Ethnic:
217 "Sitar",
218 "Banjo",
219 "Shamisen",
220 "Koto",
221 "Kalimba",
222 "Bag pipe",
223 "Fiddle",
224 "Shanai",
225 
226 // Percussive:
227 "Tinkle Bell",
228 "Agogo",
229 "Steel Drums",
230 "Woodblock",
231 "Taiko Drum",
232 "Melodic Tom",
233 "Synth Drum",
234 
235 // Sound effects:
236 "Reverse Cymbal",
237 "Guitar Fret Noise",
238 "Breath Noise",
239 "Seashore",
240 "Bird Tweet",
241 "Telephone Ring",
242 "Helicopter",
243 "Applause",
244 "Gunshot"
245 };
246 */
247 
248 
249 int addMidiTrack(Midi* mid){
250 	int trackNum;
251 	MidiTrack* tracks;
252 	tracks = cast(MidiTrack*) realloc(mid.tracks, MidiTrack.sizeof * (mid.numTracks + 1));
253 	if(tracks is null)
254 		return -1;
255 	
256 	mid.tracks = tracks;
257 	trackNum = mid.numTracks;
258 	mid.numTracks++;
259 
260 	mid.tracks[trackNum].lengthInBytes = 0;
261 	mid.tracks[trackNum].chunks = null;
262 
263 	return trackNum;
264 }
265 
266 int addMidiEvent(Midi* mid, int track, int deltatime, int event, int channel, int value1, int value2){
267 	int length = 2;
268 	MidiChunk* c;
269 	MidiChunk* current, previous;
270 	if(track >= mid.numTracks)
271 		return -1;
272 
273 	c = cast(MidiChunk*) malloc(MidiChunk.sizeof);
274 	if(c is null)
275 		return -1;
276 
277 	c.timeStamp = deltatime;
278 	c.event = cast(ubyte) event;
279 	c.channel = cast(ubyte) channel;
280 	c.param1 = cast(ubyte) value1;
281 	c.param2 = cast(ubyte) value2;
282 
283 	c.status = cast(ubyte) ((event << 4) | channel);
284 
285 	c.type = 0;
286 	c.length = 0;
287 	c.data = null;
288 
289 	c.next = null;
290 
291 
292 	previous = null;
293 	current = mid.tracks[track].chunks;
294 	while(current != null){
295 		previous = current;
296 		current = current . next;
297 	}
298 
299 	if(previous){
300 		previous.next = c;
301 	} else {
302 		mid.tracks[track].chunks = c;
303 	}
304 
305 	length += getvldLength(deltatime);
306 	if(event != MIDI_EVENT_CHANNEL_AFTERTOUCH &&
307 	   event != MIDI_EVENT_PROGRAM_CHANGE)
308 	   	length++; // param2
309 	mid.tracks[track].lengthInBytes += length;
310 
311 	return 0;
312 }
313 
314 int addMidiMetaEvent(Midi* mid, int track, int dt, int type, int length, ubyte* data){
315 	int len = 2;
316 	int a;
317 	MidiChunk* c;
318 	MidiChunk* current, previous;
319 
320 	if(track >= mid.numTracks)
321 		return -1;
322 
323 	c = cast(MidiChunk*) malloc(MidiChunk.sizeof);
324 	if(c == null)
325 		return -1;
326 
327 	c.timeStamp = dt;
328 	c.event = 0xff;
329 	c.channel = 0;
330 	c.param1 = 0;
331 	c.param2 = 0;
332 
333 	c.type = cast(ubyte) type;
334 	c.length = length;
335 	// copy data in
336 	c.data = cast(typeof(c.data)) malloc(length);
337 	if(c.data == null){
338 		free(c);
339 		return -1;
340 	}
341 	for(a = 0; a < length; a++)
342 		c.data[a] = data[a];
343 
344 
345 	c.next = null;
346 
347 
348 	previous = null;
349 	current = mid.tracks[track].chunks;
350 	while(current != null){
351 		previous = current;
352 		current = current . next;
353 	}
354 
355 	if(previous){
356 		previous.next = c;
357 	} else {
358 		mid.tracks[track].chunks = c;
359 	}
360 
361 	len += getvldLength(dt);
362 	len += length;
363 
364 	mid.tracks[track].lengthInBytes += len;
365 
366 	return 0;
367 }
368 
369 int createMidi(Midi** midWhere){
370 	Midi* mid;
371 
372 	mid = cast(Midi*) malloc(Midi.sizeof);
373 	if(mid == null)
374 		return 1;
375 	
376 	mid.type = 1;
377 	mid.numTracks = 0;
378 	mid.speed = 0x80; // 128 ticks per quarter note - potential FIXME
379 	mid.tracks = null;
380 
381 	*midWhere = mid;
382 	return 0;
383 }
384 
385 void freeChunkList(MidiChunk* c){
386 	if(c == null)
387 		return;
388 	freeChunkList(c.next);
389 	if(c.event == 255)
390 		free(c.data);
391 	free(c);
392 }
393 
394 void freeMidi(Midi** mid){
395 	int a;
396 	Midi* m = *mid;
397 
398 	for(a = 0; a < m.numTracks; a++)
399 		freeChunkList(m.tracks[a].chunks);
400 	free(m.tracks);
401 	free(m);
402 	*mid = null;
403 }
404 
405 // FIXME: these fail on big endian machines
406 void write4(int v, FILE* fp){
407 	fputc(*(cast(ubyte*)&v + 3), fp);
408 	fputc(*(cast(ubyte*)&v + 2), fp);
409 	fputc(*(cast(ubyte*)&v + 1), fp);
410 	fputc(*(cast(ubyte*)&v + 0), fp);
411 }
412 
413 void write2(short v, FILE* fp){
414 	fputc(*(cast(ubyte*)&v + 1), fp);
415 	fputc(*(cast(ubyte*)&v + 0), fp);
416 }
417 
418 void writevld(uint v, FILE* fp){
419 	uint omg = v;
420 	ubyte a;
421 	ubyte[4] ubytes;
422 	int c = 0;
423   more:
424 	a = cast(ubyte) (omg&(~(1 << 7)));
425 	omg >>= 7;
426 	if(omg){
427 		ubytes[c++]  = a;
428 		goto more;
429 	}
430 
431 	ubytes[c] = a;
432 
433 	for(; c >= 0; c--)
434 		fputc(ubytes[c] | (c ? (1<<7):0), fp);
435 }
436 
437 
438 int read4(FILE* fp){
439 	int v;
440 
441 	*(cast(ubyte*)&v + 3) = cast(ubyte) fgetc(fp);
442 	*(cast(ubyte*)&v + 2) = cast(ubyte) fgetc(fp);
443 	*(cast(ubyte*)&v + 1) = cast(ubyte) fgetc(fp);
444 	*(cast(ubyte*)&v + 0) = cast(ubyte) fgetc(fp);
445 
446 	return v;
447 }
448 
449 short read2(FILE* fp){
450 	short v;
451 	*(cast(ubyte*)&v + 1) = cast(ubyte) fgetc(fp);
452 	*(cast(ubyte*)&v + 0) = cast(ubyte) fgetc(fp);
453 
454 	return v;
455 }
456 
457 uint readvld(FILE* fp){
458 	uint omg = 0;
459 	ubyte a;
460   more:
461 	a = cast(ubyte) fgetc(fp);
462 	if(a & (1<<7)){
463 		a &= ~(1<<7);
464 		omg <<= 7;
465 		omg |= a;
466 		goto more;
467 	}
468 
469 	omg <<= 7;
470 	omg |= a;
471 
472 	return omg;
473 }
474 
475 
476 
477 int getvldLength(uint v){
478 	int count = 0;
479 	uint omg = v;
480 	ubyte a;
481   more:
482 	a = omg&((1 << 7)-1); //
483 	omg >>= 7;
484 	if(omg){
485 		a &= 1<<7;
486 		count++;
487 		goto more;
488 	}
489 	count++;
490 	return count;
491 }
492 
493 // END: big endian fixme
494 
495 int loadMidi(Midi** midWhere, const char* filename){
496 	int error = 0;
497 	FILE* fp;
498 	Midi* mid = null;
499 	int runningStatus;
500 	int t, a;
501 	int numtrk;
502 
503 	int timestamp;
504 	int event;
505 	int channel;
506 	int param1;
507 	int type;
508 	int length;
509 	int param2;
510 	ubyte* data;
511 
512 	int done;
513 
514 	fp = fopen(filename, "rb");
515 	if(fp == null){
516 		fprintf(stderr, "Cannot load file %s.\n", filename);
517 		error = 1;
518 		goto cleanup1;
519 	}
520 
521 
522 	if(fgetc(fp) != 'M') goto badfile;
523 	if(fgetc(fp) != 'T') goto badfile;
524 	if(fgetc(fp) != 'h') goto badfile;
525 	if(fgetc(fp) != 'd') goto badfile;
526 	if(read4(fp) !=   6) goto badfile;
527 
528 	if(createMidi(&mid) != 0){
529 		fprintf(stderr, "Could not allocate struct\n");
530 		error = 3;
531 		goto cleanup3;
532 	}
533 
534 	mid.type = read2(fp);
535 	numtrk = read2(fp);
536 	mid.speed = read2(fp);
537 
538 	for(t = 0; t < numtrk; t++){
539 		if(fgetc(fp) != 'M') goto badfile;
540 		if(fgetc(fp) != 'T') goto badfile;
541 		if(fgetc(fp) != 'r') goto badfile;
542 		if(fgetc(fp) != 'k') goto badfile;
543 
544 		if(addMidiTrack(mid) < 0){
545 			fprintf(stderr, "add midi track failed \n");
546 			error = 3;
547 			goto cleanup3;
548 		}
549 
550 //		mid.tracks[t].lengthInBytes = read4(fp) - 4;
551 		read4(fp); // ignoring it for now FIXME?
552 
553 		done = 0;
554 		do{
555 			timestamp = readvld(fp);
556 			event = fgetc(fp);
557 			if(event == 0xff){
558 				type = fgetc(fp);
559 				length = readvld(fp);
560 
561 				// potential optimization for malloc
562 				if(length){
563 					data = cast(typeof(data)) malloc(length);
564 					for(a = 0; a < length; a++)
565 						data[a] = cast(ubyte) fgetc(fp);
566 				} else
567 					data = null;
568 
569 
570 				if(type == 0x2f){
571 					done = 1;
572 				} else {
573 					// add the event to the list here
574 					// FIXME: error check
575 					addMidiMetaEvent(mid, t, timestamp,
576 						type, length, data);
577 				}
578 				if(data)
579 					free(data);
580 			} else {
581 				if(event < 0x80){
582 					param1 = event;
583 					event = runningStatus;
584 				} else {
585 					runningStatus = event;
586 					param1 = fgetc(fp);
587 				}
588 
589 				channel = event&0x0f;
590 				event = event >> 4;
591 				if(event != MIDI_EVENT_PROGRAM_CHANGE
592 			 	&& event != MIDI_EVENT_CHANNEL_AFTERTOUCH)
593 					param2 = fgetc(fp);
594 
595 				// add the event
596 				// FIXME: error check
597 				addMidiEvent(mid, t, timestamp, event, channel, param1, param2);
598 			}
599 		} while(!done);
600 	}
601 
602 	goto success;
603   badfile:
604 	fprintf(stderr, "The file is not in the right format. %c\n", fgetc(fp));
605 	error = 2;
606   cleanup3:
607   	if(mid != null)
608 		freeMidi(&mid);
609   success:
610 	fclose(fp);
611 	*midWhere = mid;
612   cleanup1:
613 	return error;
614 }
615 
616 
617 
618 
619 
620 
621 int saveMidi(Midi* mid, char* filename){
622 	int error = 0;
623 	FILE* fp;
624 	int t, a;
625 	int runningStatus = -1;
626 	int status;
627 	
628 	fp = fopen(filename, "wb");
629 	if(fp == null){
630 		fprintf(stderr, "Unable to open midi file (%s) for writing.\n", filename);
631 		error = 1;
632 		goto cleanup1;
633 	}
634 
635 	fputc('M', fp);
636 	fputc('T', fp);
637 	fputc('h', fp);
638 	fputc('d', fp);
639 
640 	write4(6, fp);
641 	write2(mid.type, fp);
642 	write2(mid.numTracks, fp);
643 	write2(mid.speed, fp);
644 
645 	for(t = 0; t < mid.numTracks; t++){
646 		fputc('M', fp);
647 		fputc('T', fp);
648 		fputc('r', fp);
649 		fputc('k', fp);
650 
651 		runningStatus = -1;
652 
653 		write4(mid.tracks[t].lengthInBytes + 4, fp);
654 		MidiChunk* current;
655 		current = mid.tracks[t].chunks;
656 		while(current != null){
657 			writevld(current.timeStamp, fp);
658 			if(current.event == 0xff){
659 				fputc(current.event, fp);
660 				fputc(current.type, fp);
661 				writevld(current.length, fp);
662 				for(a = 0; a < current.length; a++)
663 					fputc(current.data[a], fp);
664 			} else {
665 				// FIXME: add support for writing running status
666 				status = current.event << 4 | current.channel;
667 
668 			//	if(status != runningStatus){
669 					runningStatus = status;
670 					fputc(status, fp);
671 			//	}
672 
673 				fputc(current.param1, fp);
674 				if(current.event != MIDI_EVENT_PROGRAM_CHANGE
675 			 &&current.event != MIDI_EVENT_CHANNEL_AFTERTOUCH)
676 					fputc(current.param2, fp);
677 			}
678 			current = current.next;
679 		}
680 		/* the end of track chunk */
681 		fputc(0, fp);
682 		fputc(0xff, fp);
683 		fputc(0x2f, fp);
684 		fputc(0x00, fp);
685 	}
686 /*  cleanup2:*/
687 	fclose(fp);
688   cleanup1:
689 	return error;
690 }
691 
692 
693 int removeMidiTrack(Midi* m, int track){
694 	int a;
695 	if(track >= m.numTracks)
696 		return -1;
697 
698 	for(a = track; a < m.numTracks-1; a++){
699 		m.tracks[a] = m.tracks[a+1];
700 	}
701 
702 	m.numTracks--;
703 
704 	return 0;
705 }
706 
707 void printMidiEvent(MidiChunk* c){
708 	int e = c.event;
709 	printf("%d %s %d %d\n", c.timeStamp,
710 		 e == MIDI_EVENT_NOTE_OFF ? "Note off".ptr
711 		:e == MIDI_EVENT_NOTE_ON  ? "Note on".ptr
712 		:e == MIDI_EVENT_PROGRAM_CHANGE ? "Program change".ptr
713 		:e == MIDI_EVENT_NOTE_AFTERTOUCH ? "Aftertouch".ptr
714 		: "I dunno".ptr
715 	, c.param1, c.param2);
716 }
717 
718 MidiChunk* getTrackNameChunk(Midi* m, int track){
719 	MidiChunk* c;
720 
721 	if(track >= m.numTracks)
722 		return null;
723 
724 	c = m.tracks[track].chunks;
725 	while(c){
726 		if(c.event == 0xff && c.type == 3)
727 			return c;
728 
729 		c = c.next;
730 	}
731 
732 	return c;
733 }
734 
735 int getMidiTempo(Midi* m){
736 	int a;
737 	MidiChunk* c;
738 	for(a = 0; a < m.numTracks; a++){
739 		c = m.tracks[a].chunks;
740 		while(c){
741 			if(c.event == 0xff)
742 			if(c.type == 0x51){
743 				int p = 0;
744 				p |= cast(int)(c.data[0]) << 16;
745 				p |= cast(int)(c.data[1]) << 8;
746 				p |= cast(int)(c.data[2]) << 0;
747 				
748 				return 60000000 / p;
749 			}
750 			c = c.next;
751 		}
752 	}
753 
754 	return 120;
755 }
756 
757 int getTempoFromTempoEvent(MidiChunk* c){
758 	int tempo = -1;
759 	if(c.event == 0xff && c.type == 0x51){
760 		int p = 0;
761 		p |= cast(int)(c.data[0]) << 16;
762 		p |= cast(int)(c.data[1]) << 8;
763 		p |= cast(int)(c.data[2]) << 0;
764 		tempo = 60000000 / p;
765 	}
766 	return tempo;
767 }
768 
769 // returns milliseconds to wait given the params
770 int getMidiWaitTime(Midi* mid, int timeStamp, int tempo){
771 	return (timeStamp * 60000) / (tempo * mid.speed);
772 }
773 
774 // sets absolute values and links up, useful for playing or editing
775 // but remember you must recalculate them yourself if you change anything
776 // Returns the final absolute time in seconds
777 int recalculateMidiAbsolutes(Midi* mid){
778 	MidiChunk*[128] c;
779 	int[128] trackWaits;
780 	int playing;
781 	int waited;
782 	int minWait = 100000;
783 	int a;
784 	uint absoluteTime = 0;
785 	int tempo = 120;
786 	uint absoluteTimeInMilliSeconds = 0;
787 	int t;
788 	int timeOfLastEvent = 0;
789 
790 	MidiChunk* absoulteCurrent;
791 
792 	mid.firstAbsolute = null;
793 	absoulteCurrent = null;
794 
795 	playing = mid.numTracks;
796 	for(a = 0; a < mid.numTracks; a++){
797 		c[a] = mid.tracks[a].chunks;
798 		if(c[a]){
799 		trackWaits[a] = c[a].timeStamp;
800 		if(trackWaits[a] < minWait)
801 			minWait = trackWaits[a];
802 		} else
803 			playing--;
804 	}
805 
806 	while(playing){
807 		waited = minWait;
808 		minWait = 1000000;
809 		absoluteTime += waited;
810 		absoluteTimeInMilliSeconds += getMidiWaitTime(mid, waited, tempo);
811 	for(a = 0; a < mid.numTracks; a++){
812 		if(!c[a])
813 			continue;
814 		trackWaits[a] -= waited;
815 		if(trackWaits[a] == 0){
816 
817 		t = getTempoFromTempoEvent(c[a]);
818 		if(t != -1)
819 			tempo = t;
820 
821 		// append it to the list
822 		if(absoulteCurrent == null){
823 			mid.firstAbsolute = c[a];
824 			absoulteCurrent = c[a];
825 		} else {
826 			absoulteCurrent.nextAbsolute = c[a];
827 			absoulteCurrent = absoulteCurrent.nextAbsolute;
828 		}
829 			absoulteCurrent.nextAbsolute = null;
830 			absoulteCurrent.absoluteTime = absoluteTime;
831 			absoulteCurrent.absoluteTimeInMilliSeconds = absoluteTimeInMilliSeconds;
832 			absoulteCurrent.track = a;
833 			absoulteCurrent.absoluteWait = absoluteTime - timeOfLastEvent;
834 
835 		timeOfLastEvent = absoluteTime;
836 		c[a] = c[a].next;
837 		if(c[a] == null){
838 			playing --;
839 			trackWaits[a] = 1000000;
840 		}
841 		else
842 			trackWaits[a] = c[a].timeStamp;
843 		}
844 		if(trackWaits[a] < minWait )
845 			minWait = trackWaits[a];
846 	}
847 	}
848 
849 
850 	return absoluteTimeInMilliSeconds / 1000;
851 }
852 
853 // returns approximate seconds
854 int getMidiLength(Midi* mid){
855 	return recalculateMidiAbsolutes(mid);
856 }
857 
858 
859 
860 
861 
862 
863 import arsd.simpleaudio;
864 
865 struct PlayingMidi {
866 	ushort channelMask; /* The channels that will be played */
867 	int[128] playtracks;
868 
869 	// Callbacks
870 	// onPlayedNote. Args: this, note, midi ticks waited since last message
871 	// This is intended for tablature creation
872 	void function(void*, int, int) onPlayedNote;
873 	// onMidiEvent. Args: this, event being executed
874 	// This can be used to print it or whatever
875 	// If you return 1, it skips the event. Return 0 for normal operation
876 	int function(void*, MidiChunk*) onMidiEvent;
877 
878 	Midi* mid;
879 	MidiOutput* dev;
880 
881 	int transpose;
882 	float tempoMultiplier;
883 
884 	/* This stuff is state for the midi in progress */
885 	int tempo;
886 
887 	MidiChunk* current;
888 
889 	int wait;
890 }
891 
892 
893 // the main loop for the first time
894 int resetPlayingMidi(PlayingMidi* pmid){
895 	pmid.current = pmid.mid.firstAbsolute;
896 	pmid.tempo = 120;
897 	pmid.wait = 0;
898 	if(pmid.current)
899 		return getMidiWaitTime(pmid.mid, pmid.current.absoluteWait, cast(int) (pmid.tempo * pmid.tempoMultiplier));
900 	return 0;
901 }
902 
903 void setPlayingMidiDefaults(PlayingMidi* pmid){
904 	int a;
905 	pmid.channelMask =0xffff;
906 	for(a = 0; a < 128; a++)
907 		pmid.playtracks[a] = 1;
908 
909 	pmid.onPlayedNote = null;
910 	pmid.onMidiEvent = null;
911 
912 	pmid.mid = null;
913 	pmid.dev = null;
914 
915 	pmid.transpose = 0;
916 	pmid.tempoMultiplier = 1.0;
917 
918 }
919 
920 
921 void seekPlayingMidi(PlayingMidi* pmid, int sec){
922 	pmid.dev.silenceAllNotes();
923 	pmid.dev.reset();
924 
925 	pmid.current = pmid.mid.firstAbsolute;
926 	while(pmid.current){
927 		if(pmid.current.absoluteTimeInMilliSeconds >= sec * 1000)
928 			break;
929 		pmid.current = pmid.current.next;
930 	}
931 }
932 
933 
934 // This is the main loop. Returns how many milliseconds to wait before
935 // calling it again. If zero, then the song is over.
936 int advancePlayingMidi(PlayingMidi* pmid){
937 	MidiChunk* c;
938 	if(pmid.current == null)
939 		return 0;
940   more:
941 	c = pmid.current;
942   	pmid.wait += c.absoluteWait;
943 
944 	if(pmid.onMidiEvent){
945 		if(pmid.onMidiEvent(pmid, c))
946 			goto skip;
947 	}
948 
949 	if(c.event != 0xff){
950 		if(pmid.playtracks[c.track]){
951 			if(pmid.channelMask & (1 << c.channel)){
952 				int note = c.param1;
953 				if(c.event == MIDI_EVENT_NOTE_ON
954 				  || c.event == MIDI_EVENT_NOTE_AFTERTOUCH
955 				  || c.event == MIDI_EVENT_NOTE_OFF){
956 					note += pmid.transpose;
957 					//skipCounter = SKIP_MAX;
958 				}
959 
960 				if(pmid.dev)
961 					pmid.dev.writeMidiMessage(c.status, note, c.param2);
962 				if(pmid.onPlayedNote)
963 					if(c.event == MIDI_EVENT_NOTE_ON
964 							&& c.param2 != 0){
965 						pmid.onPlayedNote(pmid,
966 							note,
967 							(pmid.wait * 4) / (pmid.mid.speed));
968 						pmid.wait = 0;
969 					}
970 			}
971 		}
972 	} else {
973 		if(c.type == 0x51)
974 			pmid.tempo = getTempoFromTempoEvent(c);
975 	}
976 
977   skip:
978 	pmid.current = pmid.current.nextAbsolute;
979 	if(pmid.current)
980 		if(pmid.current.absoluteWait == 0)
981 			goto more;
982 		else
983 			return getMidiWaitTime(
984 				pmid.mid,
985 				pmid.current.absoluteWait,
986 				cast(int) (pmid.tempo * pmid.tempoMultiplier));
987 	else return 0;
988 }
989 
990 
991 
992 
993 version(MidiDemo) {
994 
995 
996 
997 
998 
999 MidiOutput* globaldev;
1000 
1001 version(Windows)
1002 	import core.sys.windows.windows;
1003 else {
1004 	import core.sys.posix.unistd;
1005 	void Sleep(int ms){
1006 		usleep(ms*1000);
1007 	}
1008 
1009 	import core.stdc.signal;
1010 	// FIXME: this sucks.
1011 	extern(C)
1012 	alias fuckyou = void function(int) @nogc nothrow @system;
1013 	extern(C)
1014 	void sigint(){
1015 		if(globaldev){
1016 			globaldev.silenceAllNotes();
1017 			globaldev.reset();
1018 			destroy(*globaldev);
1019 		}
1020 		exit(1);
1021 	}
1022 }
1023 
1024 enum SKIP_MAX = 3000; // allow no more than about 3 seconds of silence
1025 			 // if the -k option is set
1026 
1027 // Potential FIXME: it doesn't support more than 128 tracks.
1028 
1029 void awesome(void* midiptr, int note, int wait) {
1030 	printf("%d %d ", wait, note);
1031 	fflush(stdout);
1032 }
1033 
1034 // FIXME: add support for displaying lyrics
1035 extern(C) int main(int argc, char** argv){
1036 	int a, b;
1037 
1038 	PlayingMidi pmid;
1039 
1040 	int tempo = 120;
1041 	Midi* mid;
1042 	MidiOutput midiout = MidiOutput(0);
1043 	MidiChunk*[128] c;
1044 
1045 	int minWait = 10000, waited;
1046 	int playing;
1047 
1048 	int wait = 0;
1049 	int num;
1050 
1051 	char* filename = null;
1052 
1053 	int verbose = 0;
1054 	float tempoMultiplier = 1;
1055 	int transpose = 0;
1056 	int displayinfo = 0;
1057 	int play = 1;
1058 	int tracing = 0;
1059 	int skip = 0;
1060 	int[128] playtracks;
1061 	int skipCounter = SKIP_MAX;
1062 
1063 	ushort channelMask = 0xffff;
1064 
1065 	int sleepTime = 0;
1066 
1067 	version(Posix) {
1068 		signal(SIGINT, cast(fuckyou) &sigint);
1069 	}
1070 
1071 	for(a = 0; a< 128; a++)
1072 		playtracks[a] = 1;
1073 
1074 
1075 	for(a = 1; a < argc; a++){
1076 		if(argv[a][0] == '-')
1077 		switch(argv[a][1]){
1078 			case 't':
1079 				for(b = 0; b< 128; b++)
1080 					playtracks[b] = 0;
1081 				num = 0;
1082 				b = 0;
1083 				a++;
1084 				if(a == argc){
1085 					printf("%s: option %s requires an argument\n", argv[0], argv[a-1]);
1086 					return 1;
1087 				}
1088 				for(b = 0; argv[a][b]; b++){
1089 					if(argv[a][b] == ','){
1090 						playtracks[num] = 1;
1091 						num = 0;
1092 						continue;
1093 					}
1094 					num *= 10;
1095 					num += argv[a][b] - '0';
1096 				}
1097 				playtracks[num] = 1;
1098 			break;
1099 			case 's':
1100 				a++;
1101 				if(a == argc){
1102 					printf("%s: option %s requires an argument\n", argv[0], argv[a-1]);
1103 					return 1;
1104 				}
1105 				tempoMultiplier = atof(argv[a]);
1106 			break;
1107 			case 'i': // FIXME
1108 				displayinfo = 1;
1109 				// tracks, guesstimated length
1110 			break;
1111 			// -o loop to from
1112 			// -b begin at
1113 			// -e end at
1114 			case 'l':
1115 				tracing = 1;
1116 			break;
1117 			case 'n':
1118 				play = 0;
1119 			break;
1120 			case 'k':
1121 				skip = 1;
1122 			break;
1123 			case 'c':
1124 				channelMask = 0;
1125 				// channels
1126 				num = 0;
1127 				b = 0;
1128 				a++;
1129 				if(a == argc){
1130 					printf("%s: option %s requires an argument\n", argv[0], argv[a-1]);
1131 					return 1;
1132 				}
1133 				for(b = 0; argv[a][b]; b++){
1134 					if(argv[a][b] == ','){
1135 						channelMask |= (1 << num);
1136 						num = 0;
1137 						continue;
1138 					}
1139 					num *= 10;
1140 					num += argv[a][b] - '0';
1141 				}
1142 					channelMask |= (1 << num);
1143 			break;
1144 			case 'r':
1145 				a++;
1146 				if(a == argc){
1147 					printf("%s: option %s requires an argument\n", argv[0], argv[a-1]);
1148 					return 1;
1149 				}
1150 				transpose = atoi(argv[a]);
1151 			break;
1152 			case 'v':
1153 				verbose = 1;
1154 			break;
1155 			case 'h':
1156 				printf("Usage: %s [options...] file\n", argv[0]);
1157 				printf("  Options:\n");
1158 				printf("  -t comma separated list of tracks to play (default: all)\n");
1159 				printf("  -s tempo (speed) multiplier (default: 1.0)\n");
1160 				printf("  -i file info (track list)\n");
1161 				printf("  -l list notes as they are played (in the format totablature expects)\n");
1162 				printf("  -n no sound; don't actually play the midi\n");
1163 				printf("  -c comma separated list of channels to play (default: all)\n");
1164 				printf("  -r transpose notes by amount (default: 0)\n");
1165 				printf("  -k skip long sections of silence (good for playing single tracks)\n");
1166 
1167 				printf("  -v verbose; list all events except note on / note off\n");
1168 				printf("  -h shows this help screen\n");
1169 
1170 				return 0;
1171 			break;
1172 			default:
1173 				printf("%s: unknown command line option: %s\n", argv[0], argv[1]);
1174 				return 1;
1175 		}
1176 		else
1177 			filename = argv[a];
1178 	}
1179 
1180 	if(filename == null){
1181 		printf("%s: no file given. Try %s -h for help.\n", argv[0], argv[0]);
1182 		return 1;
1183 	}
1184 
1185 
1186 
1187 
1188 
1189 
1190 
1191 	loadMidi(&mid, filename);
1192 	if(mid == null){
1193 		printf("%s: unable to read file %s\n", argv[0], filename);
1194 		return 1;
1195 	}
1196 
1197 	if(displayinfo){
1198 		int len = getMidiLength(mid);
1199 		printf("File: %s\n", filename);
1200 		printf("Ticks per quarter note: %d\n", mid.speed);
1201 		printf("Initial tempo: %d\n", getMidiTempo(mid));
1202 		printf("Length: %d:%d\n", len / 60, len%60);
1203 		printf("Tracks:\n");
1204 		for(a = 0; a < mid.numTracks; a++){
1205 			c[0] = getTrackNameChunk(mid, a);
1206 			if(c[0] != null){
1207 				printf("%d: ", a);
1208 				for(b = 0; b < c[0].length; b++)
1209 					fputc(c[0].data[b], stdout);
1210 				printf("\n");
1211 			}
1212 		}
1213 
1214 		freeMidi(&mid);
1215 		return 0;
1216 	}
1217 
1218 
1219 
1220 	if(play){
1221 		globaldev = &midiout;
1222 	} else
1223 		globaldev = null;
1224 
1225 
1226 	recalculateMidiAbsolutes(mid);
1227 	setPlayingMidiDefaults(&pmid);
1228 
1229 	if(tracing)
1230 		pmid.onPlayedNote = &awesome;
1231 	pmid.mid = mid;
1232 	pmid.dev = &midiout;
1233 
1234 	for(a = 0; a < 127; a++)
1235 		pmid.playtracks[a] = playtracks[a];
1236 	
1237 	pmid.channelMask = channelMask;
1238 	pmid.transpose = transpose;
1239 	pmid.tempoMultiplier = tempoMultiplier;
1240 
1241 
1242 	sleepTime = resetPlayingMidi(&pmid);
1243 	do {
1244 	//printf("%d\n", sleepTime);
1245 		if(play) {
1246 			if(skip && sleepTime > 1000)
1247 				 sleepTime = 1000;
1248 			Sleep(sleepTime);
1249 		}
1250 		sleepTime = advancePlayingMidi(&pmid);
1251 	} while(sleepTime);
1252 
1253 
1254 /*
1255 	playing = mid.numTracks;
1256 
1257 	// prepare!
1258 	for(a = 0; a < mid.numTracks; a++){
1259 		c[a] = mid.tracks[a].chunks;
1260 		if(c[a]){
1261 		trackWaits[a] = c[a].timeStamp;
1262 		if(trackWaits[a] < minWait)
1263 			minWait = trackWaits[a];
1264 		} else
1265 			playing--;
1266 	}
1267 
1268 	while(playing){
1269 		if(play && (!skip || skipCounter > 100)){
1270 			Sleep(getMidiWaitTime(mid, minWait, (int)(tempo * tempoMultiplier)));
1271 			if(skip)
1272 			skipCounter -= getMidiWaitTime(mid, minWait, (int)(tempo*tempoMultiplier));
1273 		}
1274 		waited = minWait;
1275 		minWait = 1000000;
1276 		wait += waited;
1277 	for(a = 0; a < mid.numTracks; a++){
1278 		if(!c[a])
1279 			continue;
1280 		trackWaits[a] -= waited;
1281 		if(trackWaits[a] == 0){
1282 			if(c[a].event != 0xff){
1283 				if(playtracks[a]){
1284 				if(playchannels[c[a].channel]){
1285 					int note = c[a].param1;
1286 					if(c[a].event == MIDI_EVENT_NOTE_ON
1287 					|| c[a].event == MIDI_EVENT_NOTE_AFTERTOUCH
1288 					|| c[a].event == MIDI_EVENT_NOTE_OFF){
1289 						note += transpose;
1290 						skipCounter = SKIP_MAX;
1291 					}
1292 
1293 				if(play)
1294 					writeMidiMessage(dev, c[a].status, note, c[a].param2);
1295 				if(tracing)
1296 				if(c[a].event == MIDI_EVENT_NOTE_ON
1297 					&& c[a].param2 != 0){
1298 					printf("%d %d ",
1299 						(wait * 4) / (mid.speed),
1300 						note);
1301 					fflush(stdout);
1302 					wait = 0;
1303 				}
1304 				}
1305 				}
1306 				// data output:
1307 				// waittime note
1308 				// waittime is in 1/16 notes
1309 			} else {
1310 				if(c[a].type == 0x51){
1311 					tempo = getTempoFromTempoEvent(c[a]);
1312 					if(verbose)
1313 						printf("Tempo change: %d\n", tempo);
1314 				}
1315 			}
1316 			c[a] = c[a].next;
1317 			if(c[a] == null){
1318 				playing --;
1319 				trackWaits[a] = 1000000;
1320 			}
1321 			else
1322 				trackWaits[a] = c[a].timeStamp;
1323 		}
1324 
1325 		if(trackWaits[a] < minWait )
1326 			minWait = trackWaits[a];
1327 	}
1328 	}
1329 */
1330 
1331 	freeMidi(&mid);
1332 
1333 	return 0;
1334 }
1335 }