1/*
2 * Copyright 2005-2006, Haiku.
3 *
4 * Copyright (c) 2002-2004 Matthijs Hollemans
5 * Copyright (c) 2002 Jerome Leveque
6 * Copyright (c) 2002 Paul Stadler
7 * Distributed under the terms of the MIT License.
8 *
9 * Authors:
10 *		Matthijs Hollemans
11 *		J��r��me Leveque
12 *		Paul Stadler
13 */
14
15#include <File.h>
16#include <List.h>
17#include <MidiDefs.h>
18#include <MidiStore.h>
19#include <stdlib.h>
20#include <string.h>
21
22#include "debug.h"
23
24
25struct BMidiEvent {
26	BMidiEvent()
27	{
28		byte1  = 0;
29		byte2  = 0;
30		byte3  = 0;
31		data   = NULL;
32		length = 0;
33	}
34
35	~BMidiEvent()
36	{
37		free(data);
38	}
39
40	uint32 time;    // either ticks or milliseconds
41	bool   ticks;   // event is from MIDI file
42	uchar  byte1;
43	uchar  byte2;
44	uchar  byte3;
45	void*  data;    // sysex data
46	size_t length;  // sysex data size
47	int32  tempo;   // beats per minute
48};
49
50
51static int
52compare_events(const void* event1, const void* event2)
53{
54	BMidiEvent* e1 = *((BMidiEvent**) event1);
55	BMidiEvent* e2 = *((BMidiEvent**) event2);
56
57	return (e1->time - e2->time);
58}
59
60
61//	#pragma mark -
62
63
64BMidiStore::BMidiStore()
65{
66	fEvents = new BList;
67	fCurrentEvent = 0;
68	fStartTime = 0;
69	fNeedsSorting = false;
70	fBeatsPerMinute = 60;
71	fTicksPerBeat = 240;
72	fFile = NULL;
73	fHookFunc = NULL;
74	fLooping = false;
75	fPaused = false;
76	fFinished = false;
77	fInstruments = new bool[128];
78}
79
80
81BMidiStore::~BMidiStore()
82{
83	for (int32 t = 0; t < fEvents->CountItems(); ++t) {
84		delete EventAt(t);
85	}
86
87	delete fEvents;
88	delete[] fInstruments;
89}
90
91
92void
93BMidiStore::NoteOff(uchar channel, uchar note, uchar velocity,
94	uint32 time)
95{
96	BMidiEvent* event = new BMidiEvent;
97	event->time  = time;
98	event->ticks = false;
99	event->byte1 = B_NOTE_OFF | (channel - 1);
100	event->byte2 = note;
101	event->byte3 = velocity;
102	AddEvent(event);
103}
104
105
106void
107BMidiStore::NoteOn(uchar channel, uchar note,
108	uchar velocity, uint32 time)
109{
110	BMidiEvent* event = new BMidiEvent;
111	event->time  = time;
112	event->ticks = false;
113	event->byte1 = B_NOTE_ON | (channel - 1);
114	event->byte2 = note;
115	event->byte3 = velocity;
116	AddEvent(event);
117}
118
119
120void
121BMidiStore::KeyPressure(uchar channel, uchar note,
122	uchar pressure, uint32 time)
123{
124	BMidiEvent* event = new BMidiEvent;
125	event->time  = time;
126	event->ticks = false;
127	event->byte1 = B_KEY_PRESSURE | (channel - 1);
128	event->byte2 = note;
129	event->byte3 = pressure;
130	AddEvent(event);
131}
132
133
134void
135BMidiStore::ControlChange(uchar channel, uchar controlNumber,
136	uchar controlValue, uint32 time)
137{
138	BMidiEvent* event = new BMidiEvent;
139	event->time  = time;
140	event->ticks = false;
141	event->byte1 = B_CONTROL_CHANGE | (channel - 1);
142	event->byte2 = controlNumber;
143	event->byte3 = controlValue;
144	AddEvent(event);
145}
146
147
148void
149BMidiStore::ProgramChange(uchar channel, uchar programNumber,
150	uint32 time)
151{
152	BMidiEvent* event = new BMidiEvent;
153	event->time  = time;
154	event->ticks = false;
155	event->byte1 = B_PROGRAM_CHANGE | (channel - 1);
156	event->byte2 = programNumber;
157	AddEvent(event);
158}
159
160
161void
162BMidiStore::ChannelPressure(uchar channel, uchar pressure, uint32 time)
163{
164	BMidiEvent* event = new BMidiEvent;
165	event->time  = time;
166	event->ticks = false;
167	event->byte1 = B_CHANNEL_PRESSURE | (channel - 1);
168	event->byte2 = pressure;
169	AddEvent(event);
170}
171
172
173void
174BMidiStore::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
175{
176	BMidiEvent* event = new BMidiEvent;
177	event->time  = time;
178	event->ticks = false;
179	event->byte1 = B_PITCH_BEND | (channel - 1);
180	event->byte2 = lsb;
181	event->byte3 = msb;
182	AddEvent(event);
183}
184
185
186void
187BMidiStore::SystemExclusive(void* data, size_t length, uint32 time)
188{
189	BMidiEvent* event = new BMidiEvent;
190	event->time   = time;
191	event->ticks  = false;
192	event->byte1  = B_SYS_EX_START;
193	event->data   = malloc(length);
194	event->length = length;
195	memcpy(event->data, data, length);
196	AddEvent(event);
197}
198
199
200void
201BMidiStore::SystemCommon(uchar status, uchar data1,
202	uchar data2, uint32 time)
203{
204	BMidiEvent* event = new BMidiEvent;
205	event->time  = time;
206	event->ticks = false;
207	event->byte1 = status;
208	event->byte2 = data1;
209	event->byte3 = data2;
210	AddEvent(event);
211}
212
213
214void
215BMidiStore::SystemRealTime(uchar status, uint32 time)
216{
217	BMidiEvent* event = new BMidiEvent;
218	event->time  = time;
219	event->ticks = false;
220	event->byte1 = status;
221	AddEvent(event);
222}
223
224
225void
226BMidiStore::TempoChange(int32 beatsPerMinute, uint32 time)
227{
228	BMidiEvent* event = new BMidiEvent;
229	event->time  = time;
230	event->ticks = false;
231	event->byte1 = 0xFF;
232	event->byte2 = 0x51;
233	event->byte3 = 0x03;
234	event->tempo = beatsPerMinute;
235	AddEvent(event);
236}
237
238
239status_t
240BMidiStore::Import(const entry_ref* ref)
241{
242	memset(fInstruments, 0, 128 * sizeof(bool));
243
244	try {
245		fFile = new BFile(ref, B_READ_ONLY);
246		if (fFile->InitCheck() != B_OK)
247			throw fFile->InitCheck();
248
249		char fourcc[4];
250		ReadFourCC(fourcc);
251		if (strncmp(fourcc, "MThd", 4) != 0)
252			throw (status_t) B_BAD_MIDI_DATA;
253
254		if (Read32Bit() != 6)
255			throw (status_t) B_BAD_MIDI_DATA;
256
257		fFormat = Read16Bit();
258		fNumTracks = Read16Bit();
259		fTicksPerBeat = Read16Bit();
260
261		if (fTicksPerBeat & 0x8000) {
262			// we don't support SMPTE time codes,
263			// only ticks per quarter note
264			fTicksPerBeat = 240;
265		}
266
267		fCurrTrack = 0;
268		while (fCurrTrack < fNumTracks) {
269			ReadChunk();
270		}
271	} catch (status_t e) {
272		delete fFile;
273		fFile = NULL;
274		return e;
275	}
276
277	SortEvents(true);
278
279	delete fFile;
280	fFile = NULL;
281	return B_OK;
282}
283
284
285status_t
286BMidiStore::Export(const entry_ref* ref, int32 format)
287{
288	try {
289		fFile = new BFile(ref, B_READ_WRITE);
290		if (fFile->InitCheck() != B_OK)
291			throw fFile->InitCheck();
292
293		SortEvents(true);
294
295		WriteFourCC('M', 'T', 'h', 'd');
296		Write32Bit(6);
297		Write16Bit(0);  // we do only format 0
298		Write16Bit(1);
299		Write16Bit(fTicksPerBeat);
300
301		WriteTrack();
302	} catch (status_t e) {
303		delete fFile;
304		fFile = NULL;
305		return e;
306	}
307
308	delete fFile;
309	fFile = NULL;
310	return B_OK;
311}
312
313
314void
315BMidiStore::SortEvents(bool force)
316{
317	if (force || fNeedsSorting) {
318		fEvents->SortItems(compare_events);
319		fNeedsSorting = false;
320	}
321}
322
323
324uint32
325BMidiStore::CountEvents() const
326{
327	return fEvents->CountItems();
328}
329
330
331uint32
332BMidiStore::CurrentEvent() const
333{
334	return fCurrentEvent;
335}
336
337
338void
339BMidiStore::SetCurrentEvent(uint32 eventNumber)
340{
341	fCurrentEvent = eventNumber;
342}
343
344
345uint32
346BMidiStore::DeltaOfEvent(uint32 eventNumber) const
347{
348	// Even though the BeBook says that the delta is the time span between
349	// an event and the first event in the list, this doesn't appear to be
350	// true for events that were captured from other BMidi objects such as
351	// BMidiPort. For those events, we return the absolute timestamp. The
352	// BeBook is correct for events from MIDI files, though.
353
354	BMidiEvent* event = EventAt(eventNumber);
355	if (event != NULL)
356		return GetEventTime(event);
357
358	return 0;
359}
360
361
362uint32
363BMidiStore::EventAtDelta(uint32 time) const
364{
365	for (int32 t = 0; t < fEvents->CountItems(); ++t) {
366		if (GetEventTime(EventAt(t)) >= time)
367			return t;
368	}
369
370	return 0;
371}
372
373
374uint32
375BMidiStore::BeginTime() const
376{
377	return fStartTime;
378}
379
380
381void
382BMidiStore::SetTempo(int32 beatsPerMinute_)
383{
384	fBeatsPerMinute = beatsPerMinute_;
385}
386
387
388int32
389BMidiStore::Tempo() const
390{
391	return fBeatsPerMinute;
392}
393
394
395void BMidiStore::_ReservedMidiStore1() { }
396void BMidiStore::_ReservedMidiStore2() { }
397void BMidiStore::_ReservedMidiStore3() { }
398
399
400void
401BMidiStore::Run()
402{
403	// This rather compilicated Run() loop is not only used by BMidiStore
404	// but also by BMidiSynthFile. The "paused", "finished", and "looping"
405	// flags, and the "stop hook" are especially provided for the latter.
406
407	fPaused = false;
408	fFinished = false;
409
410	int32 timeAdjust = 0;
411	uint32 baseTime = 0;
412	bool firstEvent = true;
413	bool resetTime = false;
414
415	while (KeepRunning()) {
416		if (fPaused) {
417			resetTime = true;
418			snooze(100000);
419			continue;
420		}
421
422		BMidiEvent* event = EventAt(fCurrentEvent);
423
424		if (event == NULL) {
425			// no more events
426			if (fLooping) {
427				resetTime = true;
428				fCurrentEvent = 0;
429				continue;
430			}
431			break;
432		}
433
434		if (firstEvent) {
435			fStartTime = B_NOW;
436			baseTime = fStartTime;
437		} else if (resetTime)
438			baseTime = B_NOW;
439
440		if (firstEvent || resetTime) {
441			timeAdjust = baseTime - GetEventTime(event);
442			SprayEvent(event, baseTime);
443			firstEvent = false;
444			resetTime = false;
445		} else
446			SprayEvent(event, GetEventTime(event) + timeAdjust);
447
448		++fCurrentEvent;
449	}
450
451	fFinished = true;
452	fPaused = false;
453
454	if (fHookFunc != NULL)
455		(*fHookFunc)(fHookArg);
456}
457
458
459void
460BMidiStore::AddEvent(BMidiEvent* event)
461{
462	fEvents->AddItem(event);
463	fNeedsSorting = true;
464}
465
466
467void
468BMidiStore::SprayEvent(const BMidiEvent* event, uint32 time)
469{
470	uchar byte1 = event->byte1;
471	uchar byte2 = event->byte2;
472	uchar byte3 = event->byte3;
473
474	switch (byte1 & 0xF0) {
475		case B_NOTE_OFF:
476			SprayNoteOff((byte1 & 0x0F) + 1, byte2, byte3, time);
477			return;
478
479		case B_NOTE_ON:
480			SprayNoteOn((byte1 & 0x0F) + 1, byte2, byte3, time);
481			return;
482
483		case B_KEY_PRESSURE:
484			SprayKeyPressure((byte1 & 0x0F) + 1, byte2, byte3, time);
485			return;
486
487		case B_CONTROL_CHANGE:
488			SprayControlChange((byte1 & 0x0F) + 1, byte2, byte3, time);
489			return;
490
491		case B_PROGRAM_CHANGE:
492			SprayProgramChange((byte1 & 0x0F) + 1, byte2, time);
493			return;
494
495		case B_CHANNEL_PRESSURE:
496			SprayChannelPressure((byte1 & 0x0F) + 1, byte2, time);
497			return;
498
499		case B_PITCH_BEND:
500			SprayPitchBend((byte1 & 0x0F) + 1, byte2, byte3, time);
501			return;
502
503		case 0xF0:
504			switch (byte1) {
505				case B_SYS_EX_START:
506					SpraySystemExclusive(event->data, event->length, time);
507					return;
508
509				case B_MIDI_TIME_CODE:
510				case B_SONG_POSITION:
511				case B_SONG_SELECT:
512				case B_CABLE_MESSAGE:
513				case B_TUNE_REQUEST:
514				case B_SYS_EX_END:
515					SpraySystemCommon(byte1, byte2, byte3, time);
516					return;
517
518				case B_TIMING_CLOCK:
519				case B_START:
520				case B_CONTINUE:
521				case B_STOP:
522				case B_ACTIVE_SENSING:
523					SpraySystemRealTime(byte1, time);
524					return;
525
526				case B_SYSTEM_RESET:
527					if (byte2 == 0x51 && byte3 == 0x03) {
528						SprayTempoChange(event->tempo, time);
529						fBeatsPerMinute = event->tempo;
530					} else
531						SpraySystemRealTime(byte1, time);
532					return;
533			}
534			return;
535	}
536}
537
538
539BMidiEvent*
540BMidiStore::EventAt(int32 index) const
541{
542	return (BMidiEvent*)fEvents->ItemAt(index);
543}
544
545
546uint32
547BMidiStore::GetEventTime(const BMidiEvent* event) const
548{
549	if (event->ticks)
550		return TicksToMilliseconds(event->time);
551
552	return event->time;
553}
554
555
556uint32
557BMidiStore::TicksToMilliseconds(uint32 ticks) const
558{
559	return ((uint64)ticks * 60000) / (fBeatsPerMinute * fTicksPerBeat);
560}
561
562
563uint32
564BMidiStore::MillisecondsToTicks(uint32 ms) const
565{
566	return ((uint64)ms * fBeatsPerMinute * fTicksPerBeat) / 60000;
567}
568
569
570void
571BMidiStore::ReadFourCC(char* fourcc)
572{
573	if (fFile->Read(fourcc, 4) != 4)
574		throw (status_t) B_BAD_MIDI_DATA;
575}
576
577
578void
579BMidiStore::WriteFourCC(char a, char b, char c, char d)
580{
581	char fourcc[4] = { a, b, c, d };
582
583	if (fFile->Write(fourcc, 4) != 4)
584		throw (status_t) B_ERROR;
585}
586
587
588uint32
589BMidiStore::Read32Bit()
590{
591	uint8 buf[4];
592	if (fFile->Read(buf, 4) != 4)
593		throw (status_t) B_BAD_MIDI_DATA;
594
595	return (buf[0] << 24L) | (buf[1] << 16L) | (buf[2] << 8L) | buf[3];
596}
597
598
599void
600BMidiStore::Write32Bit(uint32 val)
601{
602	uint8 buf[4];
603	buf[0] = (val >> 24) & 0xFF;
604	buf[1] = (val >> 16) & 0xFF;
605	buf[2] = (val >>  8) & 0xFF;
606	buf[3] =  val        & 0xFF;
607
608	if (fFile->Write(buf, 4) != 4)
609		throw (status_t) B_ERROR;
610}
611
612
613uint16
614BMidiStore::Read16Bit()
615{
616	uint8 buf[2];
617	if (fFile->Read(buf, 2) != 2)
618		throw (status_t) B_BAD_MIDI_DATA;
619
620	return (buf[0] << 8) | buf[1];
621}
622
623
624void
625BMidiStore::Write16Bit(uint16 val)
626{
627	uint8 buf[2];
628	buf[0] = (val >> 8) & 0xFF;
629	buf[1] = val & 0xFF;
630
631	if (fFile->Write(buf, 2) != 2)
632		throw (status_t) B_ERROR;
633}
634
635
636uint8
637BMidiStore::PeekByte()
638{
639	uint8 buf;
640	if (fFile->Read(&buf, 1) != 1)
641		throw (status_t) B_BAD_MIDI_DATA;
642
643	if (fFile->Seek(-1, SEEK_CUR) < 0)
644		throw (status_t) B_ERROR;
645
646	return buf;
647}
648
649
650uint8
651BMidiStore::NextByte()
652{
653	uint8 buf;
654	if (fFile->Read(&buf, 1) != 1)
655		throw (status_t) B_BAD_MIDI_DATA;
656
657	--fByteCount;
658	return buf;
659}
660
661
662void
663BMidiStore::WriteByte(uint8 val)
664{
665	if (fFile->Write(&val, 1) != 1)
666		throw (status_t) B_ERROR;
667
668	++fByteCount;
669}
670
671
672void
673BMidiStore::SkipBytes(uint32 length)
674{
675	if (fFile->Seek(length, SEEK_CUR) < 0) {
676		throw (status_t) B_BAD_MIDI_DATA;
677	}
678
679	fByteCount -= length;
680}
681
682
683uint32
684BMidiStore::ReadVarLength()
685{
686	uint32 val;
687	uint8 byte;
688
689	if ((val = NextByte()) & 0x80) {
690		val &= 0x7F;
691		do {
692			val = (val << 7) + ((byte = NextByte()) & 0x7F);
693		} while (byte & 0x80);
694	}
695
696	return val;
697}
698
699
700void
701BMidiStore::WriteVarLength(uint32 val)
702{
703	uint32 buffer = val & 0x7F;
704
705	while ((val >>= 7) != 0) {
706		buffer <<= 8;
707		buffer |= ((val & 0x7F) | 0x80);
708	}
709
710	while (true) {
711		WriteByte(buffer);
712		if (buffer & 0x80)
713			buffer >>= 8;
714		else
715			break;
716	}
717}
718
719
720void
721BMidiStore::ReadChunk()
722{
723	char fourcc[4];
724	ReadFourCC(fourcc);
725
726	fByteCount = Read32Bit();
727
728	if (strncmp(fourcc, "MTrk", 4) == 0)
729		ReadTrack();
730	else {
731		TRACE(("Skipping '%c%c%c%c' chunk (%" B_PRIu32 " bytes)",
732			fourcc[0], fourcc[1], fourcc[2], fourcc[3], fByteCount))
733
734		SkipBytes(fByteCount);
735	}
736}
737
738
739void
740BMidiStore::ReadTrack()
741{
742	uint8 status = 0;
743	uint8 data1;
744	uint8 data2;
745	BMidiEvent* event;
746
747	fTotalTicks = 0;
748
749	while (fByteCount > 0) {
750		uint32 ticks = ReadVarLength();
751		fTotalTicks += ticks;
752
753		if (PeekByte() & 0x80)
754			status = NextByte();
755
756		switch (status & 0xF0) {
757			case B_NOTE_OFF:
758			case B_NOTE_ON:
759			case B_KEY_PRESSURE:
760			case B_CONTROL_CHANGE:
761			case B_PITCH_BEND:
762				data1 = NextByte();
763				data2 = NextByte();
764				event = new BMidiEvent;
765				event->time  = fTotalTicks;
766				event->ticks = true;
767				event->byte1 = status;
768				event->byte2 = data1;
769				event->byte3 = data2;
770				AddEvent(event);
771				break;
772
773			case B_PROGRAM_CHANGE:
774			case B_CHANNEL_PRESSURE:
775				data1 = NextByte();
776				event = new BMidiEvent;
777				event->time  = fTotalTicks;
778				event->ticks = true;
779				event->byte1 = status;
780				event->byte2 = data1;
781				AddEvent(event);
782
783				if ((status & 0xF0) == B_PROGRAM_CHANGE)
784					fInstruments[data1] = true;
785				break;
786
787			case 0xF0:
788				switch (status) {
789					case B_SYS_EX_START:
790						ReadSystemExclusive();
791						break;
792
793					case B_TUNE_REQUEST:
794					case B_SYS_EX_END:
795					case B_TIMING_CLOCK:
796					case B_START:
797					case B_CONTINUE:
798					case B_STOP:
799					case B_ACTIVE_SENSING:
800						event = new BMidiEvent;
801						event->time  = fTotalTicks;
802						event->ticks = true;
803						event->byte1 = status;
804						AddEvent(event);
805						break;
806
807					case B_MIDI_TIME_CODE:
808					case B_SONG_SELECT:
809					case B_CABLE_MESSAGE:
810						data1 = NextByte();
811						event = new BMidiEvent;
812						event->time  = fTotalTicks;
813						event->ticks = true;
814						event->byte1 = status;
815						event->byte2 = data1;
816						AddEvent(event);
817						break;
818
819					case B_SONG_POSITION:
820						data1 = NextByte();
821						data2 = NextByte();
822						event = new BMidiEvent;
823						event->time  = fTotalTicks;
824						event->ticks = true;
825						event->byte1 = status;
826						event->byte2 = data1;
827						event->byte3 = data2;
828						AddEvent(event);
829						break;
830
831					case B_SYSTEM_RESET:
832						ReadMetaEvent();
833						break;
834				}
835				break;
836		}
837
838		event = NULL;
839	}
840
841	++fCurrTrack;
842}
843
844
845void
846BMidiStore::ReadSystemExclusive()
847{
848	// We do not import sysex's from MIDI files.
849
850	SkipBytes(ReadVarLength());
851}
852
853
854void
855BMidiStore::ReadMetaEvent()
856{
857	// We only import the Tempo Change meta event.
858
859	uint8 type = NextByte();
860	uint32 length = ReadVarLength();
861
862	if (type == 0x51 && length == 3) {
863		uchar data[3];
864		data[0] = NextByte();
865		data[1] = NextByte();
866		data[2] = NextByte();
867		uint32 val = (data[0] << 16) | (data[1] << 8) | data[2];
868
869		BMidiEvent* event = new BMidiEvent;
870		event->time  = fTotalTicks;
871		event->ticks = true;
872		event->byte1 = 0xFF;
873		event->byte2 = 0x51;
874		event->byte3 = 0x03;
875		event->tempo = 60000000 / val;
876		AddEvent(event);
877	} else
878		SkipBytes(length);
879}
880
881
882void
883BMidiStore::WriteTrack()
884{
885	WriteFourCC('M', 'T', 'r', 'k');
886	off_t lengthPos = fFile->Position();
887	Write32Bit(0);
888
889	fByteCount = 0;
890	uint32 oldTime = 0;
891	uint32 newTime;
892
893	for (uint32 t = 0; t < CountEvents(); ++t) {
894		BMidiEvent* event = EventAt(t);
895
896		if (event->ticks)
897			newTime = event->time;
898		else
899			newTime = MillisecondsToTicks(event->time);
900
901		if (t == 0)
902			WriteVarLength(0);
903		else
904			WriteVarLength(newTime - oldTime);
905
906		oldTime = newTime;
907
908		switch (event->byte1 & 0xF0) {
909			case B_NOTE_OFF:
910			case B_NOTE_ON:
911			case B_KEY_PRESSURE:
912			case B_CONTROL_CHANGE:
913			case B_PITCH_BEND:
914				WriteByte(event->byte1);
915				WriteByte(event->byte2);
916				WriteByte(event->byte3);
917				break;
918
919			case B_PROGRAM_CHANGE:
920			case B_CHANNEL_PRESSURE:
921				WriteByte(event->byte1);
922				WriteByte(event->byte2);
923				break;
924
925			case 0xF0:
926				switch (event->byte1) {
927					case B_SYS_EX_START:
928						// We do not export sysex's.
929						break;
930
931					case B_TUNE_REQUEST:
932					case B_SYS_EX_END:
933					case B_TIMING_CLOCK:
934					case B_START:
935					case B_CONTINUE:
936					case B_STOP:
937					case B_ACTIVE_SENSING:
938						WriteByte(event->byte1);
939						break;
940
941					case B_MIDI_TIME_CODE:
942					case B_SONG_SELECT:
943					case B_CABLE_MESSAGE:
944						WriteByte(event->byte1);
945						WriteByte(event->byte2);
946						break;
947
948					case B_SONG_POSITION:
949						WriteByte(event->byte1);
950						WriteByte(event->byte2);
951						WriteByte(event->byte3);
952						break;
953
954					case B_SYSTEM_RESET:
955						WriteMetaEvent(event);
956						break;
957				}
958				break;
959		}
960	}
961
962	WriteVarLength(0);
963	WriteByte(0xFF);   // the end-of-track
964	WriteByte(0x2F);   // marker is required
965	WriteByte(0x00);
966
967	fFile->Seek(lengthPos, SEEK_SET);
968	Write32Bit(fByteCount);
969	fFile->Seek(0, SEEK_END);
970}
971
972
973void
974BMidiStore::WriteMetaEvent(BMidiEvent* event)
975{
976	// We only export the Tempo Change meta event.
977
978	if (event->byte2 == 0x51 && event->byte3 == 0x03) {
979		uint32 val = 60000000 / event->tempo;
980
981		WriteByte(0xFF);
982		WriteByte(0x51);
983		WriteByte(0x03);
984		WriteByte(val >> 16);
985		WriteByte(val >> 8);
986		WriteByte(val);
987	}
988}
989
990