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 {
BMidiEventBMidiEvent26 BMidiEvent()
27 {
28 byte1 = 0;
29 byte2 = 0;
30 byte3 = 0;
31 data = NULL;
32 length = 0;
33 }
34
~BMidiEventBMidiEvent35 ~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
compare_events(const void * event1,const void * event2)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
BMidiStore()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
~BMidiStore()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
NoteOff(uchar channel,uchar note,uchar velocity,uint32 time)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
NoteOn(uchar channel,uchar note,uchar velocity,uint32 time)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
KeyPressure(uchar channel,uchar note,uchar pressure,uint32 time)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
ControlChange(uchar channel,uchar controlNumber,uchar controlValue,uint32 time)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
ProgramChange(uchar channel,uchar programNumber,uint32 time)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
ChannelPressure(uchar channel,uchar pressure,uint32 time)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
PitchBend(uchar channel,uchar lsb,uchar msb,uint32 time)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
SystemExclusive(void * data,size_t length,uint32 time)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
SystemCommon(uchar status,uchar data1,uchar data2,uint32 time)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
SystemRealTime(uchar status,uint32 time)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
TempoChange(int32 beatsPerMinute,uint32 time)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
Import(const entry_ref * ref)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
Export(const entry_ref * ref,int32 format)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
SortEvents(bool force)315 BMidiStore::SortEvents(bool force)
316 {
317 if (force || fNeedsSorting) {
318 fEvents->SortItems(compare_events);
319 fNeedsSorting = false;
320 }
321 }
322
323
324 uint32
CountEvents() const325 BMidiStore::CountEvents() const
326 {
327 return fEvents->CountItems();
328 }
329
330
331 uint32
CurrentEvent() const332 BMidiStore::CurrentEvent() const
333 {
334 return fCurrentEvent;
335 }
336
337
338 void
SetCurrentEvent(uint32 eventNumber)339 BMidiStore::SetCurrentEvent(uint32 eventNumber)
340 {
341 fCurrentEvent = eventNumber;
342 }
343
344
345 uint32
DeltaOfEvent(uint32 eventNumber) const346 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
EventAtDelta(uint32 time) const363 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
BeginTime() const375 BMidiStore::BeginTime() const
376 {
377 return fStartTime;
378 }
379
380
381 void
SetTempo(int32 beatsPerMinute_)382 BMidiStore::SetTempo(int32 beatsPerMinute_)
383 {
384 fBeatsPerMinute = beatsPerMinute_;
385 }
386
387
388 int32
Tempo() const389 BMidiStore::Tempo() const
390 {
391 return fBeatsPerMinute;
392 }
393
394
_ReservedMidiStore1()395 void BMidiStore::_ReservedMidiStore1() { }
_ReservedMidiStore2()396 void BMidiStore::_ReservedMidiStore2() { }
_ReservedMidiStore3()397 void BMidiStore::_ReservedMidiStore3() { }
398
399
400 void
Run()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 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
459 void
AddEvent(BMidiEvent * event)460 BMidiStore::AddEvent(BMidiEvent* event)
461 {
462 fEvents->AddItem(event);
463 fNeedsSorting = true;
464 }
465
466
467 void
SprayEvent(const BMidiEvent * event,uint32 time)468 BMidiStore::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
539 BMidiEvent*
EventAt(int32 index) const540 BMidiStore::EventAt(int32 index) const
541 {
542 return (BMidiEvent*)fEvents->ItemAt(index);
543 }
544
545
546 uint32
GetEventTime(const BMidiEvent * event) const547 BMidiStore::GetEventTime(const BMidiEvent* event) const
548 {
549 if (event->ticks)
550 return TicksToMilliseconds(event->time);
551
552 return event->time;
553 }
554
555
556 uint32
TicksToMilliseconds(uint32 ticks) const557 BMidiStore::TicksToMilliseconds(uint32 ticks) const
558 {
559 return ((uint64)ticks * 60000) / (fBeatsPerMinute * fTicksPerBeat);
560 }
561
562
563 uint32
MillisecondsToTicks(uint32 ms) const564 BMidiStore::MillisecondsToTicks(uint32 ms) const
565 {
566 return ((uint64)ms * fBeatsPerMinute * fTicksPerBeat) / 60000;
567 }
568
569
570 void
ReadFourCC(char * fourcc)571 BMidiStore::ReadFourCC(char* fourcc)
572 {
573 if (fFile->Read(fourcc, 4) != 4)
574 throw (status_t) B_BAD_MIDI_DATA;
575 }
576
577
578 void
WriteFourCC(char a,char b,char c,char d)579 BMidiStore::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
588 uint32
Read32Bit()589 BMidiStore::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
599 void
Write32Bit(uint32 val)600 BMidiStore::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
613 uint16
Read16Bit()614 BMidiStore::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
624 void
Write16Bit(uint16 val)625 BMidiStore::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
636 uint8
PeekByte()637 BMidiStore::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
650 uint8
NextByte()651 BMidiStore::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
662 void
WriteByte(uint8 val)663 BMidiStore::WriteByte(uint8 val)
664 {
665 if (fFile->Write(&val, 1) != 1)
666 throw (status_t) B_ERROR;
667
668 ++fByteCount;
669 }
670
671
672 void
SkipBytes(uint32 length)673 BMidiStore::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
683 uint32
ReadVarLength()684 BMidiStore::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
700 void
WriteVarLength(uint32 val)701 BMidiStore::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
720 void
ReadChunk()721 BMidiStore::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
739 void
ReadTrack()740 BMidiStore::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
845 void
ReadSystemExclusive()846 BMidiStore::ReadSystemExclusive()
847 {
848 // We do not import sysex's from MIDI files.
849
850 SkipBytes(ReadVarLength());
851 }
852
853
854 void
ReadMetaEvent()855 BMidiStore::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
882 void
WriteTrack()883 BMidiStore::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
973 void
WriteMetaEvent(BMidiEvent * event)974 BMidiStore::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