xref: /haiku/src/kits/midi/MidiStore.cpp (revision 1d9d47fc72028bb71b5f232a877231e59cfe2438)
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 
25 struct 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 
51 static int
52 compare_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 
64 BMidiStore::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 
81 BMidiStore::~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 
92 void
93 BMidiStore::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 
106 void
107 BMidiStore::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 
120 void
121 BMidiStore::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 
134 void
135 BMidiStore::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 
148 void
149 BMidiStore::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 
161 void
162 BMidiStore::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 
173 void
174 BMidiStore::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 
186 void
187 BMidiStore::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 
200 void
201 BMidiStore::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 
214 void
215 BMidiStore::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 
225 void
226 BMidiStore::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 
239 status_t
240 BMidiStore::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 
285 status_t
286 BMidiStore::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 
314 void
315 BMidiStore::SortEvents(bool force)
316 {
317 	if (force || fNeedsSorting) {
318 		fEvents->SortItems(compare_events);
319 		fNeedsSorting = false;
320 	}
321 }
322 
323 
324 uint32
325 BMidiStore::CountEvents() const
326 {
327 	return fEvents->CountItems();
328 }
329 
330 
331 uint32
332 BMidiStore::CurrentEvent() const
333 {
334 	return fCurrentEvent;
335 }
336 
337 
338 void
339 BMidiStore::SetCurrentEvent(uint32 eventNumber)
340 {
341 	fCurrentEvent = eventNumber;
342 }
343 
344 
345 uint32
346 BMidiStore::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 
362 uint32
363 BMidiStore::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 
374 uint32
375 BMidiStore::BeginTime() const
376 {
377 	return fStartTime;
378 }
379 
380 
381 void
382 BMidiStore::SetTempo(int32 beatsPerMinute_)
383 {
384 	fBeatsPerMinute = beatsPerMinute_;
385 }
386 
387 
388 int32
389 BMidiStore::Tempo() const
390 {
391 	return fBeatsPerMinute;
392 }
393 
394 
395 void BMidiStore::_ReservedMidiStore1() { }
396 void BMidiStore::_ReservedMidiStore2() { }
397 void BMidiStore::_ReservedMidiStore3() { }
398 
399 
400 void
401 BMidiStore::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 			} else
430 				break;
431 		}
432 
433 		if (firstEvent) {
434 			fStartTime = B_NOW;
435 			baseTime = fStartTime;
436 		} else if (resetTime)
437 			baseTime = B_NOW;
438 
439 		if (firstEvent || resetTime) {
440 			timeAdjust = baseTime - GetEventTime(event);
441 			SprayEvent(event, baseTime);
442 			firstEvent = false;
443 			resetTime = false;
444 		} else
445 			SprayEvent(event, GetEventTime(event) + timeAdjust);
446 
447 		++fCurrentEvent;
448 	}
449 
450 	fFinished = true;
451 	fPaused = false;
452 
453 	if (fHookFunc != NULL)
454 		(*fHookFunc)(fHookArg);
455 }
456 
457 
458 void
459 BMidiStore::AddEvent(BMidiEvent* event)
460 {
461 	fEvents->AddItem(event);
462 	fNeedsSorting = true;
463 }
464 
465 
466 void
467 BMidiStore::SprayEvent(const BMidiEvent* event, uint32 time)
468 {
469 	uchar byte1 = event->byte1;
470 	uchar byte2 = event->byte2;
471 	uchar byte3 = event->byte3;
472 
473 	switch (byte1 & 0xF0) {
474 		case B_NOTE_OFF:
475 			SprayNoteOff((byte1 & 0x0F) + 1, byte2, byte3, time);
476 			return;
477 
478 		case B_NOTE_ON:
479 			SprayNoteOn((byte1 & 0x0F) + 1, byte2, byte3, time);
480 			return;
481 
482 		case B_KEY_PRESSURE:
483 			SprayKeyPressure((byte1 & 0x0F) + 1, byte2, byte3, time);
484 			return;
485 
486 		case B_CONTROL_CHANGE:
487 			SprayControlChange((byte1 & 0x0F) + 1, byte2, byte3, time);
488 			return;
489 
490 		case B_PROGRAM_CHANGE:
491 			SprayProgramChange((byte1 & 0x0F) + 1, byte2, time);
492 			return;
493 
494 		case B_CHANNEL_PRESSURE:
495 			SprayChannelPressure((byte1 & 0x0F) + 1, byte2, time);
496 			return;
497 
498 		case B_PITCH_BEND:
499 			SprayPitchBend((byte1 & 0x0F) + 1, byte2, byte3, time);
500 			return;
501 
502 		case 0xF0:
503 			switch (byte1) {
504 				case B_SYS_EX_START:
505 					SpraySystemExclusive(event->data, event->length, time);
506 					return;
507 
508 				case B_MIDI_TIME_CODE:
509 				case B_SONG_POSITION:
510 				case B_SONG_SELECT:
511 				case B_CABLE_MESSAGE:
512 				case B_TUNE_REQUEST:
513 				case B_SYS_EX_END:
514 					SpraySystemCommon(byte1, byte2, byte3, time);
515 					return;
516 
517 				case B_TIMING_CLOCK:
518 				case B_START:
519 				case B_CONTINUE:
520 				case B_STOP:
521 				case B_ACTIVE_SENSING:
522 					SpraySystemRealTime(byte1, time);
523 					return;
524 
525 				case B_SYSTEM_RESET:
526 					if (byte2 == 0x51 && byte3 == 0x03) {
527 						SprayTempoChange(event->tempo, time);
528 						fBeatsPerMinute = event->tempo;
529 					} else
530 						SpraySystemRealTime(byte1, time);
531 					return;
532 			}
533 			return;
534 	}
535 }
536 
537 
538 BMidiEvent*
539 BMidiStore::EventAt(int32 index) const
540 {
541 	return (BMidiEvent*)fEvents->ItemAt(index);
542 }
543 
544 
545 uint32
546 BMidiStore::GetEventTime(const BMidiEvent* event) const
547 {
548 	if (event->ticks)
549 		return TicksToMilliseconds(event->time);
550 
551 	return event->time;
552 }
553 
554 
555 uint32
556 BMidiStore::TicksToMilliseconds(uint32 ticks) const
557 {
558 	return ((uint64)ticks * 60000) / (fBeatsPerMinute * fTicksPerBeat);
559 }
560 
561 
562 uint32
563 BMidiStore::MillisecondsToTicks(uint32 ms) const
564 {
565 	return ((uint64)ms * fBeatsPerMinute * fTicksPerBeat) / 60000;
566 }
567 
568 
569 void
570 BMidiStore::ReadFourCC(char* fourcc)
571 {
572 	if (fFile->Read(fourcc, 4) != 4)
573 		throw (status_t) B_BAD_MIDI_DATA;
574 }
575 
576 
577 void
578 BMidiStore::WriteFourCC(char a, char b, char c, char d)
579 {
580 	char fourcc[4] = { a, b, c, d };
581 
582 	if (fFile->Write(fourcc, 4) != 4)
583 		throw (status_t) B_ERROR;
584 }
585 
586 
587 uint32
588 BMidiStore::Read32Bit()
589 {
590 	uint8 buf[4];
591 	if (fFile->Read(buf, 4) != 4)
592 		throw (status_t) B_BAD_MIDI_DATA;
593 
594 	return (buf[0] << 24L) | (buf[1] << 16L) | (buf[2] << 8L) | buf[3];
595 }
596 
597 
598 void
599 BMidiStore::Write32Bit(uint32 val)
600 {
601 	uint8 buf[4];
602 	buf[0] = (val >> 24) & 0xFF;
603 	buf[1] = (val >> 16) & 0xFF;
604 	buf[2] = (val >>  8) & 0xFF;
605 	buf[3] =  val        & 0xFF;
606 
607 	if (fFile->Write(buf, 4) != 4)
608 		throw (status_t) B_ERROR;
609 }
610 
611 
612 uint16
613 BMidiStore::Read16Bit()
614 {
615 	uint8 buf[2];
616 	if (fFile->Read(buf, 2) != 2)
617 		throw (status_t) B_BAD_MIDI_DATA;
618 
619 	return (buf[0] << 8) | buf[1];
620 }
621 
622 
623 void
624 BMidiStore::Write16Bit(uint16 val)
625 {
626 	uint8 buf[2];
627 	buf[0] = (val >> 8) & 0xFF;
628 	buf[1] = val & 0xFF;
629 
630 	if (fFile->Write(buf, 2) != 2)
631 		throw (status_t) B_ERROR;
632 }
633 
634 
635 uint8
636 BMidiStore::PeekByte()
637 {
638 	uint8 buf;
639 	if (fFile->Read(&buf, 1) != 1)
640 		throw (status_t) B_BAD_MIDI_DATA;
641 
642 	if (fFile->Seek(-1, SEEK_CUR) < 0)
643 		throw (status_t) B_ERROR;
644 
645 	return buf;
646 }
647 
648 
649 uint8
650 BMidiStore::NextByte()
651 {
652 	uint8 buf;
653 	if (fFile->Read(&buf, 1) != 1)
654 		throw (status_t) B_BAD_MIDI_DATA;
655 
656 	--fByteCount;
657 	return buf;
658 }
659 
660 
661 void
662 BMidiStore::WriteByte(uint8 val)
663 {
664 	if (fFile->Write(&val, 1) != 1)
665 		throw (status_t) B_ERROR;
666 
667 	++fByteCount;
668 }
669 
670 
671 void
672 BMidiStore::SkipBytes(uint32 length)
673 {
674 	if (fFile->Seek(length, SEEK_CUR) < 0) {
675 		throw (status_t) B_BAD_MIDI_DATA;
676 	}
677 
678 	fByteCount -= length;
679 }
680 
681 
682 uint32
683 BMidiStore::ReadVarLength()
684 {
685 	uint32 val;
686 	uint8 byte;
687 
688 	if ((val = NextByte()) & 0x80) {
689 		val &= 0x7F;
690 		do {
691 			val = (val << 7) + ((byte = NextByte()) & 0x7F);
692 		} while (byte & 0x80);
693 	}
694 
695 	return val;
696 }
697 
698 
699 void
700 BMidiStore::WriteVarLength(uint32 val)
701 {
702 	uint32 buffer = val & 0x7F;
703 
704 	while ((val >>= 7) != 0) {
705 		buffer <<= 8;
706 		buffer |= ((val & 0x7F) | 0x80);
707 	}
708 
709 	while (true) {
710 		WriteByte(buffer);
711 		if (buffer & 0x80)
712 			buffer >>= 8;
713 		else
714 			break;
715 	}
716 }
717 
718 
719 void
720 BMidiStore::ReadChunk()
721 {
722 	char fourcc[4];
723 	ReadFourCC(fourcc);
724 
725 	fByteCount = Read32Bit();
726 
727 	if (strncmp(fourcc, "MTrk", 4) == 0)
728 		ReadTrack();
729 	else {
730 		TRACE(("Skipping '%c%c%c%c' chunk (%lu bytes)",
731 			fourcc[0], fourcc[1], fourcc[2], fourcc[3], fByteCount))
732 
733 		SkipBytes(fByteCount);
734 	}
735 }
736 
737 
738 void
739 BMidiStore::ReadTrack()
740 {
741 	uint8 status = 0;
742 	uint8 data1;
743 	uint8 data2;
744 	BMidiEvent* event;
745 
746 	fTotalTicks = 0;
747 
748 	while (fByteCount > 0) {
749 		uint32 ticks = ReadVarLength();
750 		fTotalTicks += ticks;
751 
752 		if (PeekByte() & 0x80)
753 			status = NextByte();
754 
755 		switch (status & 0xF0) {
756 			case B_NOTE_OFF:
757 			case B_NOTE_ON:
758 			case B_KEY_PRESSURE:
759 			case B_CONTROL_CHANGE:
760 			case B_PITCH_BEND:
761 				data1 = NextByte();
762 				data2 = NextByte();
763 				event = new BMidiEvent;
764 				event->time  = fTotalTicks;
765 				event->ticks = true;
766 				event->byte1 = status;
767 				event->byte2 = data1;
768 				event->byte3 = data2;
769 				AddEvent(event);
770 				break;
771 
772 			case B_PROGRAM_CHANGE:
773 			case B_CHANNEL_PRESSURE:
774 				data1 = NextByte();
775 				event = new BMidiEvent;
776 				event->time  = fTotalTicks;
777 				event->ticks = true;
778 				event->byte1 = status;
779 				event->byte2 = data1;
780 				AddEvent(event);
781 
782 				if ((status & 0xF0) == B_PROGRAM_CHANGE)
783 					fInstruments[data1] = true;
784 				break;
785 
786 			case 0xF0:
787 				switch (status) {
788 					case B_SYS_EX_START:
789 						ReadSystemExclusive();
790 						break;
791 
792 					case B_TUNE_REQUEST:
793 					case B_SYS_EX_END:
794 					case B_TIMING_CLOCK:
795 					case B_START:
796 					case B_CONTINUE:
797 					case B_STOP:
798 					case B_ACTIVE_SENSING:
799 						event = new BMidiEvent;
800 						event->time  = fTotalTicks;
801 						event->ticks = true;
802 						event->byte1 = status;
803 						AddEvent(event);
804 						break;
805 
806 					case B_MIDI_TIME_CODE:
807 					case B_SONG_SELECT:
808 					case B_CABLE_MESSAGE:
809 						data1 = NextByte();
810 						event = new BMidiEvent;
811 						event->time  = fTotalTicks;
812 						event->ticks = true;
813 						event->byte1 = status;
814 						event->byte2 = data1;
815 						AddEvent(event);
816 						break;
817 
818 					case B_SONG_POSITION:
819 						data1 = NextByte();
820 						data2 = NextByte();
821 						event = new BMidiEvent;
822 						event->time  = fTotalTicks;
823 						event->ticks = true;
824 						event->byte1 = status;
825 						event->byte2 = data1;
826 						event->byte3 = data2;
827 						AddEvent(event);
828 						break;
829 
830 					case B_SYSTEM_RESET:
831 						ReadMetaEvent();
832 						break;
833 				}
834 				break;
835 		}
836 
837 		event = NULL;
838 	}
839 
840 	++fCurrTrack;
841 }
842 
843 
844 void
845 BMidiStore::ReadSystemExclusive()
846 {
847 	// We do not import sysex's from MIDI files.
848 
849 	SkipBytes(ReadVarLength());
850 }
851 
852 
853 void
854 BMidiStore::ReadMetaEvent()
855 {
856 	// We only import the Tempo Change meta event.
857 
858 	uint8 type = NextByte();
859 	uint32 length = ReadVarLength();
860 
861 	if (type == 0x51 && length == 3) {
862 		uchar data[3];
863 		data[0] = NextByte();
864 		data[1] = NextByte();
865 		data[2] = NextByte();
866 		uint32 val = (data[0] << 16) | (data[1] << 8) | data[2];
867 
868 		BMidiEvent* event = new BMidiEvent;
869 		event->time  = fTotalTicks;
870 		event->ticks = true;
871 		event->byte1 = 0xFF;
872 		event->byte2 = 0x51;
873 		event->byte3 = 0x03;
874 		event->tempo = 60000000 / val;
875 		AddEvent(event);
876 	} else
877 		SkipBytes(length);
878 }
879 
880 
881 void
882 BMidiStore::WriteTrack()
883 {
884 	WriteFourCC('M', 'T', 'r', 'k');
885 	off_t lengthPos = fFile->Position();
886 	Write32Bit(0);
887 
888 	fByteCount = 0;
889 	uint32 oldTime = 0;
890 	uint32 newTime;
891 
892 	for (uint32 t = 0; t < CountEvents(); ++t) {
893 		BMidiEvent* event = EventAt(t);
894 
895 		if (event->ticks)
896 			newTime = event->time;
897 		else
898 			newTime = MillisecondsToTicks(event->time);
899 
900 		if (t == 0)
901 			WriteVarLength(0);
902 		else
903 			WriteVarLength(newTime - oldTime);
904 
905 		oldTime = newTime;
906 
907 		switch (event->byte1 & 0xF0) {
908 			case B_NOTE_OFF:
909 			case B_NOTE_ON:
910 			case B_KEY_PRESSURE:
911 			case B_CONTROL_CHANGE:
912 			case B_PITCH_BEND:
913 				WriteByte(event->byte1);
914 				WriteByte(event->byte2);
915 				WriteByte(event->byte3);
916 				break;
917 
918 			case B_PROGRAM_CHANGE:
919 			case B_CHANNEL_PRESSURE:
920 				WriteByte(event->byte1);
921 				WriteByte(event->byte2);
922 				break;
923 
924 			case 0xF0:
925 				switch (event->byte1) {
926 					case B_SYS_EX_START:
927 						// We do not export sysex's.
928 						break;
929 
930 					case B_TUNE_REQUEST:
931 					case B_SYS_EX_END:
932 					case B_TIMING_CLOCK:
933 					case B_START:
934 					case B_CONTINUE:
935 					case B_STOP:
936 					case B_ACTIVE_SENSING:
937 						WriteByte(event->byte1);
938 						break;
939 
940 					case B_MIDI_TIME_CODE:
941 					case B_SONG_SELECT:
942 					case B_CABLE_MESSAGE:
943 						WriteByte(event->byte1);
944 						WriteByte(event->byte2);
945 						break;
946 
947 					case B_SONG_POSITION:
948 						WriteByte(event->byte1);
949 						WriteByte(event->byte2);
950 						WriteByte(event->byte3);
951 						break;
952 
953 					case B_SYSTEM_RESET:
954 						WriteMetaEvent(event);
955 						break;
956 				}
957 				break;
958 		}
959 	}
960 
961 	WriteVarLength(0);
962 	WriteByte(0xFF);   // the end-of-track
963 	WriteByte(0x2F);   // marker is required
964 	WriteByte(0x00);
965 
966 	fFile->Seek(lengthPos, SEEK_SET);
967 	Write32Bit(fByteCount);
968 	fFile->Seek(0, SEEK_END);
969 }
970 
971 
972 void
973 BMidiStore::WriteMetaEvent(BMidiEvent* event)
974 {
975 	// We only export the Tempo Change meta event.
976 
977 	if (event->byte2 == 0x51 && event->byte3 == 0x03) {
978 		uint32 val = 60000000 / event->tempo;
979 
980 		WriteByte(0xFF);
981 		WriteByte(0x51);
982 		WriteByte(0x03);
983 		WriteByte(val >> 16);
984 		WriteByte(val >> 8);
985 		WriteByte(val);
986 	}
987 }
988 
989