149fa1748Smahlzeit /*
277f87987SJérôme Duval * Copyright 2005-2006, Haiku.
377f87987SJérôme Duval *
477f87987SJérôme Duval * Copyright (c) 2002-2004 Matthijs Hollemans
577f87987SJérôme Duval * Copyright (c) 2002 Jerome Leveque
677f87987SJérôme Duval * Copyright (c) 2002 Paul Stadler
74810cdcdSJérôme Duval * Distributed under the terms of the MIT License.
849fa1748Smahlzeit *
94810cdcdSJérôme Duval * Authors:
104810cdcdSJérôme Duval * Matthijs Hollemans
114810cdcdSJérôme Duval * Jérôme Leveque
124810cdcdSJérôme Duval * Paul Stadler
1349fa1748Smahlzeit */
1452a38012Sejakowatz
15eac71506Sjerl1 #include <File.h>
16eac71506Sjerl1 #include <List.h>
174810cdcdSJérôme Duval #include <MidiDefs.h>
184810cdcdSJérôme Duval #include <MidiStore.h>
1949fa1748Smahlzeit #include <stdlib.h>
2049fa1748Smahlzeit #include <string.h>
21eac71506Sjerl1
2249fa1748Smahlzeit #include "debug.h"
23eac71506Sjerl1
24eac71506Sjerl1
257142b1a7SAxel Dörfler struct BMidiEvent {
BMidiEventBMidiEvent2649fa1748Smahlzeit BMidiEvent()
2749fa1748Smahlzeit {
2849fa1748Smahlzeit byte1 = 0;
2949fa1748Smahlzeit byte2 = 0;
3049fa1748Smahlzeit byte3 = 0;
3149fa1748Smahlzeit data = NULL;
3249fa1748Smahlzeit length = 0;
33eac71506Sjerl1 }
34eac71506Sjerl1
~BMidiEventBMidiEvent3549fa1748Smahlzeit ~BMidiEvent()
3649fa1748Smahlzeit {
3749fa1748Smahlzeit free(data);
3849fa1748Smahlzeit }
3949fa1748Smahlzeit
4049fa1748Smahlzeit uint32 time; // either ticks or milliseconds
4149fa1748Smahlzeit bool ticks; // event is from MIDI file
4249fa1748Smahlzeit uchar byte1;
4349fa1748Smahlzeit uchar byte2;
4449fa1748Smahlzeit uchar byte3;
4549fa1748Smahlzeit void* data; // sysex data
4649fa1748Smahlzeit size_t length; // sysex data size
4749fa1748Smahlzeit int32 tempo; // beats per minute
4849fa1748Smahlzeit };
4949fa1748Smahlzeit
5049fa1748Smahlzeit
517142b1a7SAxel Dörfler static int
compare_events(const void * event1,const void * event2)527142b1a7SAxel Dörfler compare_events(const void* event1, const void* event2)
5349fa1748Smahlzeit {
5449fa1748Smahlzeit BMidiEvent* e1 = *((BMidiEvent**) event1);
5549fa1748Smahlzeit BMidiEvent* e2 = *((BMidiEvent**) event2);
5649fa1748Smahlzeit
5749fa1748Smahlzeit return (e1->time - e2->time);
5849fa1748Smahlzeit }
5949fa1748Smahlzeit
607142b1a7SAxel Dörfler
617142b1a7SAxel Dörfler // #pragma mark -
627142b1a7SAxel Dörfler
6352a38012Sejakowatz
BMidiStore()646ba60405Smahlzeit BMidiStore::BMidiStore()
656ba60405Smahlzeit {
664810cdcdSJérôme Duval fEvents = new BList;
674810cdcdSJérôme Duval fCurrentEvent = 0;
684810cdcdSJérôme Duval fStartTime = 0;
694810cdcdSJérôme Duval fNeedsSorting = false;
704810cdcdSJérôme Duval fBeatsPerMinute = 60;
714810cdcdSJérôme Duval fTicksPerBeat = 240;
724810cdcdSJérôme Duval fFile = NULL;
734810cdcdSJérôme Duval fHookFunc = NULL;
744810cdcdSJérôme Duval fLooping = false;
754810cdcdSJérôme Duval fPaused = false;
764810cdcdSJérôme Duval fFinished = false;
774810cdcdSJérôme Duval fInstruments = new bool[128];
7852a38012Sejakowatz }
7952a38012Sejakowatz
806ba60405Smahlzeit
~BMidiStore()816ba60405Smahlzeit BMidiStore::~BMidiStore()
826ba60405Smahlzeit {
834810cdcdSJérôme Duval for (int32 t = 0; t < fEvents->CountItems(); ++t) {
8449fa1748Smahlzeit delete EventAt(t);
8552a38012Sejakowatz }
867142b1a7SAxel Dörfler
874810cdcdSJérôme Duval delete fEvents;
884810cdcdSJérôme Duval delete[] fInstruments;
8952a38012Sejakowatz }
9052a38012Sejakowatz
9152a38012Sejakowatz
927142b1a7SAxel Dörfler void
NoteOff(uchar channel,uchar note,uchar velocity,uint32 time)937142b1a7SAxel Dörfler BMidiStore::NoteOff(uchar channel, uchar note, uchar velocity,
947142b1a7SAxel Dörfler uint32 time)
956ba60405Smahlzeit {
9649fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
9749fa1748Smahlzeit event->time = time;
9849fa1748Smahlzeit event->ticks = false;
9949fa1748Smahlzeit event->byte1 = B_NOTE_OFF | (channel - 1);
10049fa1748Smahlzeit event->byte2 = note;
10149fa1748Smahlzeit event->byte3 = velocity;
10249fa1748Smahlzeit AddEvent(event);
1036ba60405Smahlzeit }
1046ba60405Smahlzeit
1056ba60405Smahlzeit
1067142b1a7SAxel Dörfler void
NoteOn(uchar channel,uchar note,uchar velocity,uint32 time)1077142b1a7SAxel Dörfler BMidiStore::NoteOn(uchar channel, uchar note,
1087142b1a7SAxel Dörfler uchar velocity, uint32 time)
1096ba60405Smahlzeit {
11049fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
11149fa1748Smahlzeit event->time = time;
11249fa1748Smahlzeit event->ticks = false;
11349fa1748Smahlzeit event->byte1 = B_NOTE_ON | (channel - 1);
11449fa1748Smahlzeit event->byte2 = note;
11549fa1748Smahlzeit event->byte3 = velocity;
11649fa1748Smahlzeit AddEvent(event);
1176ba60405Smahlzeit }
1186ba60405Smahlzeit
1196ba60405Smahlzeit
1207142b1a7SAxel Dörfler void
KeyPressure(uchar channel,uchar note,uchar pressure,uint32 time)1217142b1a7SAxel Dörfler BMidiStore::KeyPressure(uchar channel, uchar note,
1227142b1a7SAxel Dörfler uchar pressure, uint32 time)
1236ba60405Smahlzeit {
12449fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
12549fa1748Smahlzeit event->time = time;
12649fa1748Smahlzeit event->ticks = false;
12749fa1748Smahlzeit event->byte1 = B_KEY_PRESSURE | (channel - 1);
12849fa1748Smahlzeit event->byte2 = note;
12949fa1748Smahlzeit event->byte3 = pressure;
13049fa1748Smahlzeit AddEvent(event);
1316ba60405Smahlzeit }
1326ba60405Smahlzeit
1336ba60405Smahlzeit
1347142b1a7SAxel Dörfler void
ControlChange(uchar channel,uchar controlNumber,uchar controlValue,uint32 time)1357142b1a7SAxel Dörfler BMidiStore::ControlChange(uchar channel, uchar controlNumber,
1367142b1a7SAxel Dörfler uchar controlValue, uint32 time)
1376ba60405Smahlzeit {
13849fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
13949fa1748Smahlzeit event->time = time;
14049fa1748Smahlzeit event->ticks = false;
14149fa1748Smahlzeit event->byte1 = B_CONTROL_CHANGE | (channel - 1);
14249fa1748Smahlzeit event->byte2 = controlNumber;
14349fa1748Smahlzeit event->byte3 = controlValue;
14449fa1748Smahlzeit AddEvent(event);
1456ba60405Smahlzeit }
1466ba60405Smahlzeit
1476ba60405Smahlzeit
1487142b1a7SAxel Dörfler void
ProgramChange(uchar channel,uchar programNumber,uint32 time)1497142b1a7SAxel Dörfler BMidiStore::ProgramChange(uchar channel, uchar programNumber,
1507142b1a7SAxel Dörfler uint32 time)
1516ba60405Smahlzeit {
15249fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
15349fa1748Smahlzeit event->time = time;
15449fa1748Smahlzeit event->ticks = false;
15549fa1748Smahlzeit event->byte1 = B_PROGRAM_CHANGE | (channel - 1);
15649fa1748Smahlzeit event->byte2 = programNumber;
15749fa1748Smahlzeit AddEvent(event);
1586ba60405Smahlzeit }
1596ba60405Smahlzeit
1606ba60405Smahlzeit
1617142b1a7SAxel Dörfler void
ChannelPressure(uchar channel,uchar pressure,uint32 time)1627142b1a7SAxel Dörfler BMidiStore::ChannelPressure(uchar channel, uchar pressure, uint32 time)
1636ba60405Smahlzeit {
16449fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
16549fa1748Smahlzeit event->time = time;
16649fa1748Smahlzeit event->ticks = false;
16749fa1748Smahlzeit event->byte1 = B_CHANNEL_PRESSURE | (channel - 1);
16849fa1748Smahlzeit event->byte2 = pressure;
16949fa1748Smahlzeit AddEvent(event);
1706ba60405Smahlzeit }
1716ba60405Smahlzeit
1726ba60405Smahlzeit
1737142b1a7SAxel Dörfler void
PitchBend(uchar channel,uchar lsb,uchar msb,uint32 time)1747142b1a7SAxel Dörfler BMidiStore::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
1756ba60405Smahlzeit {
17649fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
17749fa1748Smahlzeit event->time = time;
17849fa1748Smahlzeit event->ticks = false;
17949fa1748Smahlzeit event->byte1 = B_PITCH_BEND | (channel - 1);
18049fa1748Smahlzeit event->byte2 = lsb;
18149fa1748Smahlzeit event->byte3 = msb;
18249fa1748Smahlzeit AddEvent(event);
18352a38012Sejakowatz }
18452a38012Sejakowatz
1856ba60405Smahlzeit
1867142b1a7SAxel Dörfler void
SystemExclusive(void * data,size_t length,uint32 time)1877142b1a7SAxel Dörfler BMidiStore::SystemExclusive(void* data, size_t length, uint32 time)
1886ba60405Smahlzeit {
18949fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
19049fa1748Smahlzeit event->time = time;
19149fa1748Smahlzeit event->ticks = false;
19249fa1748Smahlzeit event->byte1 = B_SYS_EX_START;
19349fa1748Smahlzeit event->data = malloc(length);
19449fa1748Smahlzeit event->length = length;
19549fa1748Smahlzeit memcpy(event->data, data, length);
19649fa1748Smahlzeit AddEvent(event);
19752a38012Sejakowatz }
19852a38012Sejakowatz
1996ba60405Smahlzeit
2007142b1a7SAxel Dörfler void
SystemCommon(uchar status,uchar data1,uchar data2,uint32 time)2017142b1a7SAxel Dörfler BMidiStore::SystemCommon(uchar status, uchar data1,
2027142b1a7SAxel Dörfler uchar data2, uint32 time)
2036ba60405Smahlzeit {
20449fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
20549fa1748Smahlzeit event->time = time;
20649fa1748Smahlzeit event->ticks = false;
20749fa1748Smahlzeit event->byte1 = status;
20849fa1748Smahlzeit event->byte2 = data1;
20949fa1748Smahlzeit event->byte3 = data2;
21049fa1748Smahlzeit AddEvent(event);
21152a38012Sejakowatz }
21252a38012Sejakowatz
21352a38012Sejakowatz
2147142b1a7SAxel Dörfler void
SystemRealTime(uchar status,uint32 time)2157142b1a7SAxel Dörfler BMidiStore::SystemRealTime(uchar status, uint32 time)
2166ba60405Smahlzeit {
21749fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
21849fa1748Smahlzeit event->time = time;
21949fa1748Smahlzeit event->ticks = false;
22049fa1748Smahlzeit event->byte1 = status;
22149fa1748Smahlzeit AddEvent(event);
22252a38012Sejakowatz }
22352a38012Sejakowatz
22449fa1748Smahlzeit
2257142b1a7SAxel Dörfler void
TempoChange(int32 beatsPerMinute,uint32 time)2267142b1a7SAxel Dörfler BMidiStore::TempoChange(int32 beatsPerMinute, uint32 time)
22749fa1748Smahlzeit {
22849fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
22949fa1748Smahlzeit event->time = time;
23049fa1748Smahlzeit event->ticks = false;
23149fa1748Smahlzeit event->byte1 = 0xFF;
23249fa1748Smahlzeit event->byte2 = 0x51;
23349fa1748Smahlzeit event->byte3 = 0x03;
23449fa1748Smahlzeit event->tempo = beatsPerMinute;
23549fa1748Smahlzeit AddEvent(event);
23649fa1748Smahlzeit }
23749fa1748Smahlzeit
2386ba60405Smahlzeit
2397142b1a7SAxel Dörfler status_t
Import(const entry_ref * ref)2407142b1a7SAxel Dörfler BMidiStore::Import(const entry_ref* ref)
2416ba60405Smahlzeit {
2424810cdcdSJérôme Duval memset(fInstruments, 0, 128 * sizeof(bool));
243f9b2179cSmahlzeit
2447142b1a7SAxel Dörfler try {
2454810cdcdSJérôme Duval fFile = new BFile(ref, B_READ_ONLY);
2464810cdcdSJérôme Duval if (fFile->InitCheck() != B_OK)
2474810cdcdSJérôme Duval throw fFile->InitCheck();
24825767509Smahlzeit
24949fa1748Smahlzeit char fourcc[4];
25049fa1748Smahlzeit ReadFourCC(fourcc);
25149fa1748Smahlzeit if (strncmp(fourcc, "MThd", 4) != 0)
25249fa1748Smahlzeit throw (status_t) B_BAD_MIDI_DATA;
25325767509Smahlzeit
25449fa1748Smahlzeit if (Read32Bit() != 6)
25549fa1748Smahlzeit throw (status_t) B_BAD_MIDI_DATA;
256eac71506Sjerl1
2574810cdcdSJérôme Duval fFormat = Read16Bit();
2584810cdcdSJérôme Duval fNumTracks = Read16Bit();
2594810cdcdSJérôme Duval fTicksPerBeat = Read16Bit();
26049fa1748Smahlzeit
2614810cdcdSJérôme Duval if (fTicksPerBeat & 0x8000) {
2627142b1a7SAxel Dörfler // we don't support SMPTE time codes,
2637142b1a7SAxel Dörfler // only ticks per quarter note
2644810cdcdSJérôme Duval fTicksPerBeat = 240;
265eac71506Sjerl1 }
266eac71506Sjerl1
2674810cdcdSJérôme Duval fCurrTrack = 0;
2684810cdcdSJérôme Duval while (fCurrTrack < fNumTracks) {
26949fa1748Smahlzeit ReadChunk();
27049fa1748Smahlzeit }
2717142b1a7SAxel Dörfler } catch (status_t e) {
2724810cdcdSJérôme Duval delete fFile;
2734810cdcdSJérôme Duval fFile = NULL;
27449fa1748Smahlzeit return e;
275eac71506Sjerl1 }
276eac71506Sjerl1
277eac71506Sjerl1 SortEvents(true);
278eac71506Sjerl1
2794810cdcdSJérôme Duval delete fFile;
2804810cdcdSJérôme Duval fFile = NULL;
2816ba60405Smahlzeit return B_OK;
2826ba60405Smahlzeit }
2836ba60405Smahlzeit
28449fa1748Smahlzeit
2857142b1a7SAxel Dörfler status_t
Export(const entry_ref * ref,int32 format)2867142b1a7SAxel Dörfler BMidiStore::Export(const entry_ref* ref, int32 format)
28749fa1748Smahlzeit {
2887142b1a7SAxel Dörfler try {
2894810cdcdSJérôme Duval fFile = new BFile(ref, B_READ_WRITE);
2904810cdcdSJérôme Duval if (fFile->InitCheck() != B_OK)
2914810cdcdSJérôme Duval throw fFile->InitCheck();
29249fa1748Smahlzeit
29349fa1748Smahlzeit SortEvents(true);
29449fa1748Smahlzeit
29549fa1748Smahlzeit WriteFourCC('M', 'T', 'h', 'd');
29649fa1748Smahlzeit Write32Bit(6);
29749fa1748Smahlzeit Write16Bit(0); // we do only format 0
29849fa1748Smahlzeit Write16Bit(1);
2994810cdcdSJérôme Duval Write16Bit(fTicksPerBeat);
30049fa1748Smahlzeit
30149fa1748Smahlzeit WriteTrack();
3027142b1a7SAxel Dörfler } catch (status_t e) {
3034810cdcdSJérôme Duval delete fFile;
3044810cdcdSJérôme Duval fFile = NULL;
30549fa1748Smahlzeit return e;
30649fa1748Smahlzeit }
30749fa1748Smahlzeit
3084810cdcdSJérôme Duval delete fFile;
3094810cdcdSJérôme Duval fFile = NULL;
31049fa1748Smahlzeit return B_OK;
31149fa1748Smahlzeit }
31249fa1748Smahlzeit
3136ba60405Smahlzeit
3147142b1a7SAxel Dörfler void
SortEvents(bool force)3157142b1a7SAxel Dörfler BMidiStore::SortEvents(bool force)
3166ba60405Smahlzeit {
3174810cdcdSJérôme Duval if (force || fNeedsSorting) {
3184810cdcdSJérôme Duval fEvents->SortItems(compare_events);
3194810cdcdSJérôme Duval fNeedsSorting = false;
32049fa1748Smahlzeit }
3216ba60405Smahlzeit }
3226ba60405Smahlzeit
3236ba60405Smahlzeit
3247142b1a7SAxel Dörfler uint32
CountEvents() const3257142b1a7SAxel Dörfler BMidiStore::CountEvents() const
32649fa1748Smahlzeit {
3274810cdcdSJérôme Duval return fEvents->CountItems();
3286ba60405Smahlzeit }
3296ba60405Smahlzeit
3306ba60405Smahlzeit
3317142b1a7SAxel Dörfler uint32
CurrentEvent() const3327142b1a7SAxel Dörfler BMidiStore::CurrentEvent() const
33349fa1748Smahlzeit {
3344810cdcdSJérôme Duval return fCurrentEvent;
3356ba60405Smahlzeit }
3366ba60405Smahlzeit
3376ba60405Smahlzeit
3387142b1a7SAxel Dörfler void
SetCurrentEvent(uint32 eventNumber)3397142b1a7SAxel Dörfler BMidiStore::SetCurrentEvent(uint32 eventNumber)
34049fa1748Smahlzeit {
3414810cdcdSJérôme Duval fCurrentEvent = eventNumber;
3426ba60405Smahlzeit }
3436ba60405Smahlzeit
3446ba60405Smahlzeit
3457142b1a7SAxel Dörfler uint32
DeltaOfEvent(uint32 eventNumber) const3467142b1a7SAxel Dörfler BMidiStore::DeltaOfEvent(uint32 eventNumber) const
34749fa1748Smahlzeit {
34849fa1748Smahlzeit // Even though the BeBook says that the delta is the time span between
34949fa1748Smahlzeit // an event and the first event in the list, this doesn't appear to be
35049fa1748Smahlzeit // true for events that were captured from other BMidi objects such as
35149fa1748Smahlzeit // BMidiPort. For those events, we return the absolute timestamp. The
35249fa1748Smahlzeit // BeBook is correct for events from MIDI files, though.
35349fa1748Smahlzeit
35449fa1748Smahlzeit BMidiEvent* event = EventAt(eventNumber);
35549fa1748Smahlzeit if (event != NULL)
35649fa1748Smahlzeit return GetEventTime(event);
3577142b1a7SAxel Dörfler
3587142b1a7SAxel Dörfler return 0;
3597142b1a7SAxel Dörfler }
3607142b1a7SAxel Dörfler
3617142b1a7SAxel Dörfler
3627142b1a7SAxel Dörfler uint32
EventAtDelta(uint32 time) const3637142b1a7SAxel Dörfler BMidiStore::EventAtDelta(uint32 time) const
3647142b1a7SAxel Dörfler {
3654810cdcdSJérôme Duval for (int32 t = 0; t < fEvents->CountItems(); ++t) {
3667142b1a7SAxel Dörfler if (GetEventTime(EventAt(t)) >= time)
3677142b1a7SAxel Dörfler return t;
36849fa1748Smahlzeit }
36949fa1748Smahlzeit
3706ba60405Smahlzeit return 0;
3716ba60405Smahlzeit }
3726ba60405Smahlzeit
373eac71506Sjerl1
3747142b1a7SAxel Dörfler uint32
BeginTime() const3757142b1a7SAxel Dörfler BMidiStore::BeginTime() const
37649fa1748Smahlzeit {
3774810cdcdSJérôme Duval return fStartTime;
3786ba60405Smahlzeit }
3796ba60405Smahlzeit
3806ba60405Smahlzeit
3817142b1a7SAxel Dörfler void
SetTempo(int32 beatsPerMinute_)3827142b1a7SAxel Dörfler BMidiStore::SetTempo(int32 beatsPerMinute_)
38349fa1748Smahlzeit {
3844810cdcdSJérôme Duval fBeatsPerMinute = beatsPerMinute_;
3856ba60405Smahlzeit }
3866ba60405Smahlzeit
3876ba60405Smahlzeit
3887142b1a7SAxel Dörfler int32
Tempo() const3897142b1a7SAxel Dörfler BMidiStore::Tempo() const
39049fa1748Smahlzeit {
3914810cdcdSJérôme Duval return fBeatsPerMinute;
3926ba60405Smahlzeit }
3936ba60405Smahlzeit
39449fa1748Smahlzeit
_ReservedMidiStore1()39549fa1748Smahlzeit void BMidiStore::_ReservedMidiStore1() { }
_ReservedMidiStore2()39649fa1748Smahlzeit void BMidiStore::_ReservedMidiStore2() { }
_ReservedMidiStore3()39749fa1748Smahlzeit void BMidiStore::_ReservedMidiStore3() { }
39849fa1748Smahlzeit
3996ba60405Smahlzeit
4007142b1a7SAxel Dörfler void
Run()4017142b1a7SAxel Dörfler BMidiStore::Run()
4026ba60405Smahlzeit {
403b178e190Smahlzeit // This rather compilicated Run() loop is not only used by BMidiStore
404b178e190Smahlzeit // but also by BMidiSynthFile. The "paused", "finished", and "looping"
405b178e190Smahlzeit // flags, and the "stop hook" are especially provided for the latter.
406b178e190Smahlzeit
4074810cdcdSJérôme Duval fPaused = false;
4084810cdcdSJérôme Duval fFinished = false;
409b178e190Smahlzeit
4107142b1a7SAxel Dörfler int32 timeAdjust = 0;
4117142b1a7SAxel Dörfler uint32 baseTime = 0;
41249fa1748Smahlzeit bool firstEvent = true;
413b178e190Smahlzeit bool resetTime = false;
41449fa1748Smahlzeit
4157142b1a7SAxel Dörfler while (KeepRunning()) {
4164810cdcdSJérôme Duval if (fPaused) {
417b178e190Smahlzeit resetTime = true;
418b178e190Smahlzeit snooze(100000);
419b178e190Smahlzeit continue;
420b178e190Smahlzeit }
421b178e190Smahlzeit
4224810cdcdSJérôme Duval BMidiEvent* event = EventAt(fCurrentEvent);
423b178e190Smahlzeit
4247142b1a7SAxel Dörfler if (event == NULL) {
4257142b1a7SAxel Dörfler // no more events
4264810cdcdSJérôme Duval if (fLooping) {
427b178e190Smahlzeit resetTime = true;
4284810cdcdSJérôme Duval fCurrentEvent = 0;
4296c1399c4SJérôme Duval continue;
4306c1399c4SJérôme Duval }
431b178e190Smahlzeit break;
432b178e190Smahlzeit }
4336ba60405Smahlzeit
4347142b1a7SAxel Dörfler if (firstEvent) {
4354810cdcdSJérôme Duval fStartTime = B_NOW;
4364810cdcdSJérôme Duval baseTime = fStartTime;
4377142b1a7SAxel Dörfler } else if (resetTime)
438b178e190Smahlzeit baseTime = B_NOW;
439b178e190Smahlzeit
4407142b1a7SAxel Dörfler if (firstEvent || resetTime) {
441b178e190Smahlzeit timeAdjust = baseTime - GetEventTime(event);
442b178e190Smahlzeit SprayEvent(event, baseTime);
44349fa1748Smahlzeit firstEvent = false;
444b178e190Smahlzeit resetTime = false;
4457142b1a7SAxel Dörfler } else
44649fa1748Smahlzeit SprayEvent(event, GetEventTime(event) + timeAdjust);
4476ba60405Smahlzeit
4484810cdcdSJérôme Duval ++fCurrentEvent;
44949fa1748Smahlzeit }
450b178e190Smahlzeit
4514810cdcdSJérôme Duval fFinished = true;
4524810cdcdSJérôme Duval fPaused = false;
453b178e190Smahlzeit
4544810cdcdSJérôme Duval if (fHookFunc != NULL)
4554810cdcdSJérôme Duval (*fHookFunc)(fHookArg);
456b178e190Smahlzeit }
4576ba60405Smahlzeit
4586ba60405Smahlzeit
4597142b1a7SAxel Dörfler void
AddEvent(BMidiEvent * event)4607142b1a7SAxel Dörfler BMidiStore::AddEvent(BMidiEvent* event)
46125767509Smahlzeit {
4624810cdcdSJérôme Duval fEvents->AddItem(event);
4634810cdcdSJérôme Duval fNeedsSorting = true;
46425767509Smahlzeit }
46525767509Smahlzeit
46649fa1748Smahlzeit
4677142b1a7SAxel Dörfler void
SprayEvent(const BMidiEvent * event,uint32 time)4687142b1a7SAxel Dörfler BMidiStore::SprayEvent(const BMidiEvent* event, uint32 time)
46925767509Smahlzeit {
47049fa1748Smahlzeit uchar byte1 = event->byte1;
47149fa1748Smahlzeit uchar byte2 = event->byte2;
47249fa1748Smahlzeit uchar byte3 = event->byte3;
47325767509Smahlzeit
4747142b1a7SAxel Dörfler switch (byte1 & 0xF0) {
475eac71506Sjerl1 case B_NOTE_OFF:
47649fa1748Smahlzeit SprayNoteOff((byte1 & 0x0F) + 1, byte2, byte3, time);
47749fa1748Smahlzeit return;
47849fa1748Smahlzeit
479eac71506Sjerl1 case B_NOTE_ON:
48049fa1748Smahlzeit SprayNoteOn((byte1 & 0x0F) + 1, byte2, byte3, time);
48149fa1748Smahlzeit return;
48249fa1748Smahlzeit
483eac71506Sjerl1 case B_KEY_PRESSURE:
48449fa1748Smahlzeit SprayKeyPressure((byte1 & 0x0F) + 1, byte2, byte3, time);
48549fa1748Smahlzeit return;
48649fa1748Smahlzeit
487eac71506Sjerl1 case B_CONTROL_CHANGE:
48849fa1748Smahlzeit SprayControlChange((byte1 & 0x0F) + 1, byte2, byte3, time);
48949fa1748Smahlzeit return;
49049fa1748Smahlzeit
491eac71506Sjerl1 case B_PROGRAM_CHANGE:
49249fa1748Smahlzeit SprayProgramChange((byte1 & 0x0F) + 1, byte2, time);
49349fa1748Smahlzeit return;
49449fa1748Smahlzeit
495eac71506Sjerl1 case B_CHANNEL_PRESSURE:
49649fa1748Smahlzeit SprayChannelPressure((byte1 & 0x0F) + 1, byte2, time);
49749fa1748Smahlzeit return;
49849fa1748Smahlzeit
49949fa1748Smahlzeit case B_PITCH_BEND:
50049fa1748Smahlzeit SprayPitchBend((byte1 & 0x0F) + 1, byte2, byte3, time);
50149fa1748Smahlzeit return;
50249fa1748Smahlzeit
50349fa1748Smahlzeit case 0xF0:
5047142b1a7SAxel Dörfler switch (byte1) {
505eac71506Sjerl1 case B_SYS_EX_START:
50649fa1748Smahlzeit SpraySystemExclusive(event->data, event->length, time);
50749fa1748Smahlzeit return;
50849fa1748Smahlzeit
509eac71506Sjerl1 case B_MIDI_TIME_CODE:
51049fa1748Smahlzeit case B_SONG_POSITION:
511eac71506Sjerl1 case B_SONG_SELECT:
51249fa1748Smahlzeit case B_CABLE_MESSAGE:
513eac71506Sjerl1 case B_TUNE_REQUEST:
51449fa1748Smahlzeit case B_SYS_EX_END:
51549fa1748Smahlzeit SpraySystemCommon(byte1, byte2, byte3, time);
51649fa1748Smahlzeit return;
51749fa1748Smahlzeit
518eac71506Sjerl1 case B_TIMING_CLOCK:
519eac71506Sjerl1 case B_START:
520eac71506Sjerl1 case B_CONTINUE:
521eac71506Sjerl1 case B_STOP:
522eac71506Sjerl1 case B_ACTIVE_SENSING:
52349fa1748Smahlzeit SpraySystemRealTime(byte1, time);
52449fa1748Smahlzeit return;
52549fa1748Smahlzeit
526eac71506Sjerl1 case B_SYSTEM_RESET:
5277142b1a7SAxel Dörfler if (byte2 == 0x51 && byte3 == 0x03) {
52849fa1748Smahlzeit SprayTempoChange(event->tempo, time);
5294810cdcdSJérôme Duval fBeatsPerMinute = event->tempo;
5307142b1a7SAxel Dörfler } else
53149fa1748Smahlzeit SpraySystemRealTime(byte1, time);
53249fa1748Smahlzeit return;
533eac71506Sjerl1 }
53449fa1748Smahlzeit return;
535eac71506Sjerl1 }
536eac71506Sjerl1 }
537eac71506Sjerl1
538eac71506Sjerl1
5397142b1a7SAxel Dörfler BMidiEvent*
EventAt(int32 index) const5407142b1a7SAxel Dörfler BMidiStore::EventAt(int32 index) const
541eac71506Sjerl1 {
5424810cdcdSJérôme Duval return (BMidiEvent*)fEvents->ItemAt(index);
543eac71506Sjerl1 }
544eac71506Sjerl1
545eac71506Sjerl1
5467142b1a7SAxel Dörfler uint32
GetEventTime(const BMidiEvent * event) const5477142b1a7SAxel Dörfler BMidiStore::GetEventTime(const BMidiEvent* event) const
548eac71506Sjerl1 {
54949fa1748Smahlzeit if (event->ticks)
55049fa1748Smahlzeit return TicksToMilliseconds(event->time);
5517142b1a7SAxel Dörfler
55249fa1748Smahlzeit return event->time;
55349fa1748Smahlzeit }
55449fa1748Smahlzeit
55549fa1748Smahlzeit
5567142b1a7SAxel Dörfler uint32
TicksToMilliseconds(uint32 ticks) const5577142b1a7SAxel Dörfler BMidiStore::TicksToMilliseconds(uint32 ticks) const
558eac71506Sjerl1 {
5594810cdcdSJérôme Duval return ((uint64)ticks * 60000) / (fBeatsPerMinute * fTicksPerBeat);
56049fa1748Smahlzeit }
56149fa1748Smahlzeit
56249fa1748Smahlzeit
5637142b1a7SAxel Dörfler uint32
MillisecondsToTicks(uint32 ms) const5647142b1a7SAxel Dörfler BMidiStore::MillisecondsToTicks(uint32 ms) const
565eac71506Sjerl1 {
5664810cdcdSJérôme Duval return ((uint64)ms * fBeatsPerMinute * fTicksPerBeat) / 60000;
567eac71506Sjerl1 }
56849fa1748Smahlzeit
56949fa1748Smahlzeit
5707142b1a7SAxel Dörfler void
ReadFourCC(char * fourcc)5717142b1a7SAxel Dörfler BMidiStore::ReadFourCC(char* fourcc)
572eac71506Sjerl1 {
5734810cdcdSJérôme Duval if (fFile->Read(fourcc, 4) != 4)
57449fa1748Smahlzeit throw (status_t) B_BAD_MIDI_DATA;
575eac71506Sjerl1 }
57649fa1748Smahlzeit
57749fa1748Smahlzeit
5787142b1a7SAxel Dörfler void
WriteFourCC(char a,char b,char c,char d)5797142b1a7SAxel Dörfler BMidiStore::WriteFourCC(char a, char b, char c, char d)
580eac71506Sjerl1 {
58149fa1748Smahlzeit char fourcc[4] = { a, b, c, d };
5827142b1a7SAxel Dörfler
5834810cdcdSJérôme Duval if (fFile->Write(fourcc, 4) != 4)
58449fa1748Smahlzeit throw (status_t) B_ERROR;
585eac71506Sjerl1 }
58649fa1748Smahlzeit
58749fa1748Smahlzeit
5887142b1a7SAxel Dörfler uint32
Read32Bit()5897142b1a7SAxel Dörfler BMidiStore::Read32Bit()
590eac71506Sjerl1 {
59149fa1748Smahlzeit uint8 buf[4];
5924810cdcdSJérôme Duval if (fFile->Read(buf, 4) != 4)
59349fa1748Smahlzeit throw (status_t) B_BAD_MIDI_DATA;
5947142b1a7SAxel Dörfler
5957142b1a7SAxel Dörfler return (buf[0] << 24L) | (buf[1] << 16L) | (buf[2] << 8L) | buf[3];
596eac71506Sjerl1 }
59749fa1748Smahlzeit
59849fa1748Smahlzeit
5997142b1a7SAxel Dörfler void
Write32Bit(uint32 val)6007142b1a7SAxel Dörfler BMidiStore::Write32Bit(uint32 val)
601eac71506Sjerl1 {
60249fa1748Smahlzeit uint8 buf[4];
60349fa1748Smahlzeit buf[0] = (val >> 24) & 0xFF;
60449fa1748Smahlzeit buf[1] = (val >> 16) & 0xFF;
60549fa1748Smahlzeit buf[2] = (val >> 8) & 0xFF;
60649fa1748Smahlzeit buf[3] = val & 0xFF;
60749fa1748Smahlzeit
6084810cdcdSJérôme Duval if (fFile->Write(buf, 4) != 4)
60949fa1748Smahlzeit throw (status_t) B_ERROR;
610eac71506Sjerl1 }
61149fa1748Smahlzeit
61249fa1748Smahlzeit
6137142b1a7SAxel Dörfler uint16
Read16Bit()6147142b1a7SAxel Dörfler BMidiStore::Read16Bit()
615eac71506Sjerl1 {
61649fa1748Smahlzeit uint8 buf[2];
6174810cdcdSJérôme Duval if (fFile->Read(buf, 2) != 2)
61849fa1748Smahlzeit throw (status_t) B_BAD_MIDI_DATA;
619eac71506Sjerl1
62049fa1748Smahlzeit return (buf[0] << 8) | buf[1];
621eac71506Sjerl1 }
622eac71506Sjerl1
623eac71506Sjerl1
6247142b1a7SAxel Dörfler void
Write16Bit(uint16 val)6257142b1a7SAxel Dörfler BMidiStore::Write16Bit(uint16 val)
626eac71506Sjerl1 {
62749fa1748Smahlzeit uint8 buf[2];
62849fa1748Smahlzeit buf[0] = (val >> 8) & 0xFF;
62949fa1748Smahlzeit buf[1] = val & 0xFF;
630eac71506Sjerl1
6314810cdcdSJérôme Duval if (fFile->Write(buf, 2) != 2)
63249fa1748Smahlzeit throw (status_t) B_ERROR;
633eac71506Sjerl1 }
634eac71506Sjerl1
635eac71506Sjerl1
6367142b1a7SAxel Dörfler uint8
PeekByte()6377142b1a7SAxel Dörfler BMidiStore::PeekByte()
638eac71506Sjerl1 {
63949fa1748Smahlzeit uint8 buf;
6404810cdcdSJérôme Duval if (fFile->Read(&buf, 1) != 1)
64149fa1748Smahlzeit throw (status_t) B_BAD_MIDI_DATA;
642eac71506Sjerl1
6434810cdcdSJérôme Duval if (fFile->Seek(-1, SEEK_CUR) < 0)
64449fa1748Smahlzeit throw (status_t) B_ERROR;
645eac71506Sjerl1
64649fa1748Smahlzeit return buf;
64749fa1748Smahlzeit }
648eac71506Sjerl1
64949fa1748Smahlzeit
6507142b1a7SAxel Dörfler uint8
NextByte()6517142b1a7SAxel Dörfler BMidiStore::NextByte()
65249fa1748Smahlzeit {
65349fa1748Smahlzeit uint8 buf;
6544810cdcdSJérôme Duval if (fFile->Read(&buf, 1) != 1)
65549fa1748Smahlzeit throw (status_t) B_BAD_MIDI_DATA;
65649fa1748Smahlzeit
6574810cdcdSJérôme Duval --fByteCount;
65849fa1748Smahlzeit return buf;
65949fa1748Smahlzeit }
66049fa1748Smahlzeit
66149fa1748Smahlzeit
6627142b1a7SAxel Dörfler void
WriteByte(uint8 val)6637142b1a7SAxel Dörfler BMidiStore::WriteByte(uint8 val)
66449fa1748Smahlzeit {
6654810cdcdSJérôme Duval if (fFile->Write(&val, 1) != 1)
66649fa1748Smahlzeit throw (status_t) B_ERROR;
66749fa1748Smahlzeit
6684810cdcdSJérôme Duval ++fByteCount;
66949fa1748Smahlzeit }
67049fa1748Smahlzeit
67149fa1748Smahlzeit
6727142b1a7SAxel Dörfler void
SkipBytes(uint32 length)6737142b1a7SAxel Dörfler BMidiStore::SkipBytes(uint32 length)
67449fa1748Smahlzeit {
6754810cdcdSJérôme Duval if (fFile->Seek(length, SEEK_CUR) < 0) {
67649fa1748Smahlzeit throw (status_t) B_BAD_MIDI_DATA;
67749fa1748Smahlzeit }
67849fa1748Smahlzeit
6794810cdcdSJérôme Duval fByteCount -= length;
68049fa1748Smahlzeit }
68149fa1748Smahlzeit
68249fa1748Smahlzeit
6837142b1a7SAxel Dörfler uint32
ReadVarLength()6847142b1a7SAxel Dörfler BMidiStore::ReadVarLength()
68549fa1748Smahlzeit {
68649fa1748Smahlzeit uint32 val;
68749fa1748Smahlzeit uint8 byte;
68849fa1748Smahlzeit
6897142b1a7SAxel Dörfler if ((val = NextByte()) & 0x80) {
69049fa1748Smahlzeit val &= 0x7F;
6917142b1a7SAxel Dörfler do {
69249fa1748Smahlzeit val = (val << 7) + ((byte = NextByte()) & 0x7F);
6937142b1a7SAxel Dörfler } while (byte & 0x80);
69449fa1748Smahlzeit }
69549fa1748Smahlzeit
69649fa1748Smahlzeit return val;
69749fa1748Smahlzeit }
69849fa1748Smahlzeit
69949fa1748Smahlzeit
7007142b1a7SAxel Dörfler void
WriteVarLength(uint32 val)7017142b1a7SAxel Dörfler BMidiStore::WriteVarLength(uint32 val)
70249fa1748Smahlzeit {
70349fa1748Smahlzeit uint32 buffer = val & 0x7F;
70449fa1748Smahlzeit
7057142b1a7SAxel Dörfler while ((val >>= 7) != 0) {
706eac71506Sjerl1 buffer <<= 8;
70749fa1748Smahlzeit buffer |= ((val & 0x7F) | 0x80);
708eac71506Sjerl1 }
70949fa1748Smahlzeit
7107142b1a7SAxel Dörfler while (true) {
71149fa1748Smahlzeit WriteByte(buffer);
712eac71506Sjerl1 if (buffer & 0x80)
713eac71506Sjerl1 buffer >>= 8;
714eac71506Sjerl1 else
715eac71506Sjerl1 break;
71625767509Smahlzeit }
71725767509Smahlzeit }
71825767509Smahlzeit
71925767509Smahlzeit
7207142b1a7SAxel Dörfler void
ReadChunk()7217142b1a7SAxel Dörfler BMidiStore::ReadChunk()
722eac71506Sjerl1 {
72349fa1748Smahlzeit char fourcc[4];
72449fa1748Smahlzeit ReadFourCC(fourcc);
725eac71506Sjerl1
7264810cdcdSJérôme Duval fByteCount = Read32Bit();
727eac71506Sjerl1
72849fa1748Smahlzeit if (strncmp(fourcc, "MTrk", 4) == 0)
72949fa1748Smahlzeit ReadTrack();
7307142b1a7SAxel Dörfler else {
731*1a7bcf69SOliver Tappe TRACE(("Skipping '%c%c%c%c' chunk (%" B_PRIu32 " bytes)",
732e2e2e542SJérôme Duval fourcc[0], fourcc[1], fourcc[2], fourcc[3], fByteCount))
73349fa1748Smahlzeit
7344810cdcdSJérôme Duval SkipBytes(fByteCount);
73549fa1748Smahlzeit }
736eac71506Sjerl1 }
737eac71506Sjerl1
73849fa1748Smahlzeit
7397142b1a7SAxel Dörfler void
ReadTrack()7407142b1a7SAxel Dörfler BMidiStore::ReadTrack()
74149fa1748Smahlzeit {
74249fa1748Smahlzeit uint8 status = 0;
74349fa1748Smahlzeit uint8 data1;
74449fa1748Smahlzeit uint8 data2;
74549fa1748Smahlzeit BMidiEvent* event;
74649fa1748Smahlzeit
7474810cdcdSJérôme Duval fTotalTicks = 0;
74849fa1748Smahlzeit
7494810cdcdSJérôme Duval while (fByteCount > 0) {
75049fa1748Smahlzeit uint32 ticks = ReadVarLength();
7514810cdcdSJérôme Duval fTotalTicks += ticks;
75249fa1748Smahlzeit
75349fa1748Smahlzeit if (PeekByte() & 0x80)
75449fa1748Smahlzeit status = NextByte();
75549fa1748Smahlzeit
7567142b1a7SAxel Dörfler switch (status & 0xF0) {
75749fa1748Smahlzeit case B_NOTE_OFF:
75849fa1748Smahlzeit case B_NOTE_ON:
75949fa1748Smahlzeit case B_KEY_PRESSURE:
76049fa1748Smahlzeit case B_CONTROL_CHANGE:
76149fa1748Smahlzeit case B_PITCH_BEND:
76249fa1748Smahlzeit data1 = NextByte();
76349fa1748Smahlzeit data2 = NextByte();
76449fa1748Smahlzeit event = new BMidiEvent;
7654810cdcdSJérôme Duval event->time = fTotalTicks;
76649fa1748Smahlzeit event->ticks = true;
76749fa1748Smahlzeit event->byte1 = status;
76849fa1748Smahlzeit event->byte2 = data1;
76949fa1748Smahlzeit event->byte3 = data2;
77049fa1748Smahlzeit AddEvent(event);
77149fa1748Smahlzeit break;
77249fa1748Smahlzeit
77349fa1748Smahlzeit case B_PROGRAM_CHANGE:
77449fa1748Smahlzeit case B_CHANNEL_PRESSURE:
77549fa1748Smahlzeit data1 = NextByte();
77649fa1748Smahlzeit event = new BMidiEvent;
7774810cdcdSJérôme Duval event->time = fTotalTicks;
77849fa1748Smahlzeit event->ticks = true;
77949fa1748Smahlzeit event->byte1 = status;
78049fa1748Smahlzeit event->byte2 = data1;
78149fa1748Smahlzeit AddEvent(event);
782f9b2179cSmahlzeit
783f9b2179cSmahlzeit if ((status & 0xF0) == B_PROGRAM_CHANGE)
7844810cdcdSJérôme Duval fInstruments[data1] = true;
78549fa1748Smahlzeit break;
78649fa1748Smahlzeit
78749fa1748Smahlzeit case 0xF0:
7887142b1a7SAxel Dörfler switch (status) {
78949fa1748Smahlzeit case B_SYS_EX_START:
79049fa1748Smahlzeit ReadSystemExclusive();
79149fa1748Smahlzeit break;
79249fa1748Smahlzeit
79349fa1748Smahlzeit case B_TUNE_REQUEST:
79449fa1748Smahlzeit case B_SYS_EX_END:
79549fa1748Smahlzeit case B_TIMING_CLOCK:
79649fa1748Smahlzeit case B_START:
79749fa1748Smahlzeit case B_CONTINUE:
79849fa1748Smahlzeit case B_STOP:
79949fa1748Smahlzeit case B_ACTIVE_SENSING:
80049fa1748Smahlzeit event = new BMidiEvent;
8014810cdcdSJérôme Duval event->time = fTotalTicks;
80249fa1748Smahlzeit event->ticks = true;
80349fa1748Smahlzeit event->byte1 = status;
80449fa1748Smahlzeit AddEvent(event);
80549fa1748Smahlzeit break;
80649fa1748Smahlzeit
80749fa1748Smahlzeit case B_MIDI_TIME_CODE:
80849fa1748Smahlzeit case B_SONG_SELECT:
80949fa1748Smahlzeit case B_CABLE_MESSAGE:
81049fa1748Smahlzeit data1 = NextByte();
81149fa1748Smahlzeit event = new BMidiEvent;
8124810cdcdSJérôme Duval event->time = fTotalTicks;
81349fa1748Smahlzeit event->ticks = true;
81449fa1748Smahlzeit event->byte1 = status;
81549fa1748Smahlzeit event->byte2 = data1;
81649fa1748Smahlzeit AddEvent(event);
81749fa1748Smahlzeit break;
81849fa1748Smahlzeit
81949fa1748Smahlzeit case B_SONG_POSITION:
82049fa1748Smahlzeit data1 = NextByte();
82149fa1748Smahlzeit data2 = NextByte();
82249fa1748Smahlzeit event = new BMidiEvent;
8234810cdcdSJérôme Duval event->time = fTotalTicks;
82449fa1748Smahlzeit event->ticks = true;
82549fa1748Smahlzeit event->byte1 = status;
82649fa1748Smahlzeit event->byte2 = data1;
82749fa1748Smahlzeit event->byte3 = data2;
82849fa1748Smahlzeit AddEvent(event);
82949fa1748Smahlzeit break;
83049fa1748Smahlzeit
83149fa1748Smahlzeit case B_SYSTEM_RESET:
83249fa1748Smahlzeit ReadMetaEvent();
83349fa1748Smahlzeit break;
83449fa1748Smahlzeit }
83549fa1748Smahlzeit break;
83649fa1748Smahlzeit }
83749fa1748Smahlzeit
83849fa1748Smahlzeit event = NULL;
83949fa1748Smahlzeit }
84049fa1748Smahlzeit
8414810cdcdSJérôme Duval ++fCurrTrack;
84249fa1748Smahlzeit }
84349fa1748Smahlzeit
84449fa1748Smahlzeit
8457142b1a7SAxel Dörfler void
ReadSystemExclusive()8467142b1a7SAxel Dörfler BMidiStore::ReadSystemExclusive()
84749fa1748Smahlzeit {
84849fa1748Smahlzeit // We do not import sysex's from MIDI files.
84949fa1748Smahlzeit
85049fa1748Smahlzeit SkipBytes(ReadVarLength());
85149fa1748Smahlzeit }
85249fa1748Smahlzeit
85349fa1748Smahlzeit
8547142b1a7SAxel Dörfler void
ReadMetaEvent()8557142b1a7SAxel Dörfler BMidiStore::ReadMetaEvent()
85649fa1748Smahlzeit {
85749fa1748Smahlzeit // We only import the Tempo Change meta event.
85849fa1748Smahlzeit
85949fa1748Smahlzeit uint8 type = NextByte();
86049fa1748Smahlzeit uint32 length = ReadVarLength();
86149fa1748Smahlzeit
8627142b1a7SAxel Dörfler if (type == 0x51 && length == 3) {
86349fa1748Smahlzeit uchar data[3];
86449fa1748Smahlzeit data[0] = NextByte();
86549fa1748Smahlzeit data[1] = NextByte();
86649fa1748Smahlzeit data[2] = NextByte();
86749fa1748Smahlzeit uint32 val = (data[0] << 16) | (data[1] << 8) | data[2];
86849fa1748Smahlzeit
86949fa1748Smahlzeit BMidiEvent* event = new BMidiEvent;
8704810cdcdSJérôme Duval event->time = fTotalTicks;
87149fa1748Smahlzeit event->ticks = true;
87249fa1748Smahlzeit event->byte1 = 0xFF;
87349fa1748Smahlzeit event->byte2 = 0x51;
87449fa1748Smahlzeit event->byte3 = 0x03;
87549fa1748Smahlzeit event->tempo = 60000000 / val;
87649fa1748Smahlzeit AddEvent(event);
8777142b1a7SAxel Dörfler } else
87849fa1748Smahlzeit SkipBytes(length);
87949fa1748Smahlzeit }
88049fa1748Smahlzeit
88149fa1748Smahlzeit
8827142b1a7SAxel Dörfler void
WriteTrack()8837142b1a7SAxel Dörfler BMidiStore::WriteTrack()
88449fa1748Smahlzeit {
88549fa1748Smahlzeit WriteFourCC('M', 'T', 'r', 'k');
8864810cdcdSJérôme Duval off_t lengthPos = fFile->Position();
88749fa1748Smahlzeit Write32Bit(0);
88849fa1748Smahlzeit
8894810cdcdSJérôme Duval fByteCount = 0;
8907142b1a7SAxel Dörfler uint32 oldTime = 0;
891e4aca0a6Smahlzeit uint32 newTime;
89249fa1748Smahlzeit
8937142b1a7SAxel Dörfler for (uint32 t = 0; t < CountEvents(); ++t) {
89449fa1748Smahlzeit BMidiEvent* event = EventAt(t);
89549fa1748Smahlzeit
89649fa1748Smahlzeit if (event->ticks)
89749fa1748Smahlzeit newTime = event->time;
89849fa1748Smahlzeit else
89949fa1748Smahlzeit newTime = MillisecondsToTicks(event->time);
90049fa1748Smahlzeit
90149fa1748Smahlzeit if (t == 0)
90249fa1748Smahlzeit WriteVarLength(0);
90349fa1748Smahlzeit else
90449fa1748Smahlzeit WriteVarLength(newTime - oldTime);
90549fa1748Smahlzeit
90649fa1748Smahlzeit oldTime = newTime;
90749fa1748Smahlzeit
9087142b1a7SAxel Dörfler switch (event->byte1 & 0xF0) {
90949fa1748Smahlzeit case B_NOTE_OFF:
91049fa1748Smahlzeit case B_NOTE_ON:
91149fa1748Smahlzeit case B_KEY_PRESSURE:
91249fa1748Smahlzeit case B_CONTROL_CHANGE:
91349fa1748Smahlzeit case B_PITCH_BEND:
91449fa1748Smahlzeit WriteByte(event->byte1);
91549fa1748Smahlzeit WriteByte(event->byte2);
91649fa1748Smahlzeit WriteByte(event->byte3);
91749fa1748Smahlzeit break;
91849fa1748Smahlzeit
91949fa1748Smahlzeit case B_PROGRAM_CHANGE:
92049fa1748Smahlzeit case B_CHANNEL_PRESSURE:
92149fa1748Smahlzeit WriteByte(event->byte1);
92249fa1748Smahlzeit WriteByte(event->byte2);
92349fa1748Smahlzeit break;
92449fa1748Smahlzeit
92549fa1748Smahlzeit case 0xF0:
9267142b1a7SAxel Dörfler switch (event->byte1) {
92749fa1748Smahlzeit case B_SYS_EX_START:
92849fa1748Smahlzeit // We do not export sysex's.
92949fa1748Smahlzeit break;
93049fa1748Smahlzeit
93149fa1748Smahlzeit case B_TUNE_REQUEST:
93249fa1748Smahlzeit case B_SYS_EX_END:
93349fa1748Smahlzeit case B_TIMING_CLOCK:
93449fa1748Smahlzeit case B_START:
93549fa1748Smahlzeit case B_CONTINUE:
93649fa1748Smahlzeit case B_STOP:
93749fa1748Smahlzeit case B_ACTIVE_SENSING:
93849fa1748Smahlzeit WriteByte(event->byte1);
93949fa1748Smahlzeit break;
94049fa1748Smahlzeit
94149fa1748Smahlzeit case B_MIDI_TIME_CODE:
94249fa1748Smahlzeit case B_SONG_SELECT:
94349fa1748Smahlzeit case B_CABLE_MESSAGE:
94449fa1748Smahlzeit WriteByte(event->byte1);
94549fa1748Smahlzeit WriteByte(event->byte2);
94649fa1748Smahlzeit break;
94749fa1748Smahlzeit
94849fa1748Smahlzeit case B_SONG_POSITION:
94949fa1748Smahlzeit WriteByte(event->byte1);
95049fa1748Smahlzeit WriteByte(event->byte2);
95149fa1748Smahlzeit WriteByte(event->byte3);
95249fa1748Smahlzeit break;
95349fa1748Smahlzeit
95449fa1748Smahlzeit case B_SYSTEM_RESET:
95549fa1748Smahlzeit WriteMetaEvent(event);
95649fa1748Smahlzeit break;
95749fa1748Smahlzeit }
95849fa1748Smahlzeit break;
95949fa1748Smahlzeit }
96049fa1748Smahlzeit }
96149fa1748Smahlzeit
96249fa1748Smahlzeit WriteVarLength(0);
96349fa1748Smahlzeit WriteByte(0xFF); // the end-of-track
96449fa1748Smahlzeit WriteByte(0x2F); // marker is required
96549fa1748Smahlzeit WriteByte(0x00);
96649fa1748Smahlzeit
9674810cdcdSJérôme Duval fFile->Seek(lengthPos, SEEK_SET);
9684810cdcdSJérôme Duval Write32Bit(fByteCount);
9694810cdcdSJérôme Duval fFile->Seek(0, SEEK_END);
97049fa1748Smahlzeit }
97149fa1748Smahlzeit
97249fa1748Smahlzeit
9737142b1a7SAxel Dörfler void
WriteMetaEvent(BMidiEvent * event)9747142b1a7SAxel Dörfler BMidiStore::WriteMetaEvent(BMidiEvent* event)
97549fa1748Smahlzeit {
97649fa1748Smahlzeit // We only export the Tempo Change meta event.
97749fa1748Smahlzeit
9787142b1a7SAxel Dörfler if (event->byte2 == 0x51 && event->byte3 == 0x03) {
97949fa1748Smahlzeit uint32 val = 60000000 / event->tempo;
98049fa1748Smahlzeit
98149fa1748Smahlzeit WriteByte(0xFF);
98249fa1748Smahlzeit WriteByte(0x51);
98349fa1748Smahlzeit WriteByte(0x03);
98449fa1748Smahlzeit WriteByte(val >> 16);
98549fa1748Smahlzeit WriteByte(val >> 8);
98649fa1748Smahlzeit WriteByte(val);
98749fa1748Smahlzeit }
98849fa1748Smahlzeit }
98949fa1748Smahlzeit
990