xref: /haiku/src/kits/midi/MidiStore.cpp (revision 1a7bcf6962e1c99906cce0fe602e08c3fcda46f6)
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