xref: /haiku/src/kits/midi/MidiStore.cpp (revision f9b2179cb2003db1496efde7ac479f1ee450fc17)
149fa1748Smahlzeit /*
29138338cSmahlzeit  * Copyright (c) 2002-2004 Matthijs Hollemans
349fa1748Smahlzeit  * Copyright (c) 2002 Jerome Leveque
449fa1748Smahlzeit  * Copyright (c) 2002 Paul Stadler
549fa1748Smahlzeit  *
649fa1748Smahlzeit  * Permission is hereby granted, free of charge, to any person obtaining a
749fa1748Smahlzeit  * copy of this software and associated documentation files (the "Software"),
849fa1748Smahlzeit  * to deal in the Software without restriction, including without limitation
949fa1748Smahlzeit  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1049fa1748Smahlzeit  * and/or sell copies of the Software, and to permit persons to whom the
1149fa1748Smahlzeit  * Software is furnished to do so, subject to the following conditions:
1249fa1748Smahlzeit  *
1349fa1748Smahlzeit  * The above copyright notice and this permission notice shall be included in
1449fa1748Smahlzeit  * all copies or substantial portions of the Software.
1549fa1748Smahlzeit  *
1649fa1748Smahlzeit  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749fa1748Smahlzeit  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849fa1748Smahlzeit  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1949fa1748Smahlzeit  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049fa1748Smahlzeit  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2149fa1748Smahlzeit  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2249fa1748Smahlzeit  * DEALINGS IN THE SOFTWARE.
2349fa1748Smahlzeit  */
2452a38012Sejakowatz 
25eac71506Sjerl1 #include <File.h>
26eac71506Sjerl1 #include <List.h>
2749fa1748Smahlzeit #include <stdlib.h>
2849fa1748Smahlzeit #include <string.h>
29eac71506Sjerl1 
3049fa1748Smahlzeit #include "MidiDefs.h"
3149fa1748Smahlzeit #include "MidiStore.h"
3249fa1748Smahlzeit #include "debug.h"
33eac71506Sjerl1 
3449fa1748Smahlzeit //------------------------------------------------------------------------------
35eac71506Sjerl1 
3649fa1748Smahlzeit struct BMidiEvent
37eac71506Sjerl1 {
3849fa1748Smahlzeit 	BMidiEvent()
3949fa1748Smahlzeit 	{
4049fa1748Smahlzeit 		byte1  = 0;
4149fa1748Smahlzeit 		byte2  = 0;
4249fa1748Smahlzeit 		byte3  = 0;
4349fa1748Smahlzeit 		data   = NULL;
4449fa1748Smahlzeit 		length = 0;
45eac71506Sjerl1 	}
46eac71506Sjerl1 
4749fa1748Smahlzeit 	~BMidiEvent()
4849fa1748Smahlzeit 	{
4949fa1748Smahlzeit 		free(data);
5049fa1748Smahlzeit 	}
5149fa1748Smahlzeit 
5249fa1748Smahlzeit 	uint32 time;    // either ticks or milliseconds
5349fa1748Smahlzeit 	bool   ticks;   // event is from MIDI file
5449fa1748Smahlzeit 	uchar  byte1;
5549fa1748Smahlzeit 	uchar  byte2;
5649fa1748Smahlzeit 	uchar  byte3;
5749fa1748Smahlzeit 	void*  data;    // sysex data
5849fa1748Smahlzeit 	size_t length;  // sysex data size
5949fa1748Smahlzeit 	int32  tempo;   // beats per minute
6049fa1748Smahlzeit };
6149fa1748Smahlzeit 
6249fa1748Smahlzeit //------------------------------------------------------------------------------
6349fa1748Smahlzeit 
6449fa1748Smahlzeit static int compare_events(const void* event1, const void* event2)
6549fa1748Smahlzeit {
6649fa1748Smahlzeit 	BMidiEvent* e1 = *((BMidiEvent**) event1);
6749fa1748Smahlzeit 	BMidiEvent* e2 = *((BMidiEvent**) event2);
6849fa1748Smahlzeit 
6949fa1748Smahlzeit 	return (e1->time - e2->time);
7049fa1748Smahlzeit }
7149fa1748Smahlzeit 
7249fa1748Smahlzeit //------------------------------------------------------------------------------
7352a38012Sejakowatz 
746ba60405Smahlzeit BMidiStore::BMidiStore()
756ba60405Smahlzeit {
7649fa1748Smahlzeit 	events = new BList;
7749fa1748Smahlzeit 	currentEvent = 0;
7849fa1748Smahlzeit 	startTime = 0;
7949fa1748Smahlzeit 	needsSorting = false;
8049fa1748Smahlzeit 	beatsPerMinute = 60;
8149fa1748Smahlzeit 	ticksPerBeat = 240;
8249fa1748Smahlzeit 	file = NULL;
83b178e190Smahlzeit 	hookFunc = NULL;
84b178e190Smahlzeit 	looping = false;
85b178e190Smahlzeit 	paused = false;
86b178e190Smahlzeit 	finished = false;
87*f9b2179cSmahlzeit 	instruments = new bool[128];
8852a38012Sejakowatz }
8952a38012Sejakowatz 
9049fa1748Smahlzeit //------------------------------------------------------------------------------
916ba60405Smahlzeit 
926ba60405Smahlzeit BMidiStore::~BMidiStore()
936ba60405Smahlzeit {
9449fa1748Smahlzeit 	for (int32 t = 0; t < events->CountItems(); ++t)
956ba60405Smahlzeit 	{
9649fa1748Smahlzeit 		delete EventAt(t);
9752a38012Sejakowatz 	}
986ba60405Smahlzeit 	delete events;
99*f9b2179cSmahlzeit 	delete[] instruments;
10052a38012Sejakowatz }
10152a38012Sejakowatz 
10249fa1748Smahlzeit //------------------------------------------------------------------------------
10352a38012Sejakowatz 
10449fa1748Smahlzeit void BMidiStore::NoteOff(
10549fa1748Smahlzeit 	uchar channel, uchar note, uchar velocity, uint32 time)
1066ba60405Smahlzeit {
10749fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
10849fa1748Smahlzeit 	event->time  = time;
10949fa1748Smahlzeit 	event->ticks = false;
11049fa1748Smahlzeit 	event->byte1 = B_NOTE_OFF | (channel - 1);
11149fa1748Smahlzeit 	event->byte2 = note;
11249fa1748Smahlzeit 	event->byte3 = velocity;
11349fa1748Smahlzeit 	AddEvent(event);
1146ba60405Smahlzeit }
1156ba60405Smahlzeit 
11649fa1748Smahlzeit //------------------------------------------------------------------------------
1176ba60405Smahlzeit 
11849fa1748Smahlzeit void BMidiStore::NoteOn(
11949fa1748Smahlzeit 	uchar channel, uchar note, uchar velocity, uint32 time)
1206ba60405Smahlzeit {
12149fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
12249fa1748Smahlzeit 	event->time  = time;
12349fa1748Smahlzeit 	event->ticks = false;
12449fa1748Smahlzeit 	event->byte1 = B_NOTE_ON | (channel - 1);
12549fa1748Smahlzeit 	event->byte2 = note;
12649fa1748Smahlzeit 	event->byte3 = velocity;
12749fa1748Smahlzeit 	AddEvent(event);
1286ba60405Smahlzeit }
1296ba60405Smahlzeit 
13049fa1748Smahlzeit //------------------------------------------------------------------------------
1316ba60405Smahlzeit 
13249fa1748Smahlzeit void BMidiStore::KeyPressure(
13349fa1748Smahlzeit 	uchar channel, uchar note, uchar pressure, uint32 time)
1346ba60405Smahlzeit {
13549fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
13649fa1748Smahlzeit 	event->time  = time;
13749fa1748Smahlzeit 	event->ticks = false;
13849fa1748Smahlzeit 	event->byte1 = B_KEY_PRESSURE | (channel - 1);
13949fa1748Smahlzeit 	event->byte2 = note;
14049fa1748Smahlzeit 	event->byte3 = pressure;
14149fa1748Smahlzeit 	AddEvent(event);
1426ba60405Smahlzeit }
1436ba60405Smahlzeit 
14449fa1748Smahlzeit //------------------------------------------------------------------------------
1456ba60405Smahlzeit 
14649fa1748Smahlzeit void BMidiStore::ControlChange(
14749fa1748Smahlzeit 	uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
1486ba60405Smahlzeit {
14949fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
15049fa1748Smahlzeit 	event->time  = time;
15149fa1748Smahlzeit 	event->ticks = false;
15249fa1748Smahlzeit 	event->byte1 = B_CONTROL_CHANGE | (channel - 1);
15349fa1748Smahlzeit 	event->byte2 = controlNumber;
15449fa1748Smahlzeit 	event->byte3 = controlValue;
15549fa1748Smahlzeit 	AddEvent(event);
1566ba60405Smahlzeit }
1576ba60405Smahlzeit 
15849fa1748Smahlzeit //------------------------------------------------------------------------------
1596ba60405Smahlzeit 
16049fa1748Smahlzeit void BMidiStore::ProgramChange(
16149fa1748Smahlzeit 	uchar channel, uchar programNumber, uint32 time)
1626ba60405Smahlzeit {
16349fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
16449fa1748Smahlzeit 	event->time  = time;
16549fa1748Smahlzeit 	event->ticks = false;
16649fa1748Smahlzeit 	event->byte1 = B_PROGRAM_CHANGE | (channel - 1);
16749fa1748Smahlzeit 	event->byte2 = programNumber;
16849fa1748Smahlzeit 	AddEvent(event);
1696ba60405Smahlzeit }
1706ba60405Smahlzeit 
17149fa1748Smahlzeit //------------------------------------------------------------------------------
1726ba60405Smahlzeit 
17349fa1748Smahlzeit void BMidiStore::ChannelPressure(uchar channel, uchar pressure, uint32 time)
1746ba60405Smahlzeit {
17549fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
17649fa1748Smahlzeit 	event->time  = time;
17749fa1748Smahlzeit 	event->ticks = false;
17849fa1748Smahlzeit 	event->byte1 = B_CHANNEL_PRESSURE | (channel - 1);
17949fa1748Smahlzeit 	event->byte2 = pressure;
18049fa1748Smahlzeit 	AddEvent(event);
1816ba60405Smahlzeit }
1826ba60405Smahlzeit 
18349fa1748Smahlzeit //------------------------------------------------------------------------------
1846ba60405Smahlzeit 
18549fa1748Smahlzeit void BMidiStore::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
1866ba60405Smahlzeit {
18749fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
18849fa1748Smahlzeit 	event->time  = time;
18949fa1748Smahlzeit 	event->ticks = false;
19049fa1748Smahlzeit 	event->byte1 = B_PITCH_BEND | (channel - 1);
19149fa1748Smahlzeit 	event->byte2 = lsb;
19249fa1748Smahlzeit 	event->byte3 = msb;
19349fa1748Smahlzeit 	AddEvent(event);
19452a38012Sejakowatz }
19552a38012Sejakowatz 
19649fa1748Smahlzeit //------------------------------------------------------------------------------
1976ba60405Smahlzeit 
19849fa1748Smahlzeit void BMidiStore::SystemExclusive(void* data, size_t length, uint32 time)
1996ba60405Smahlzeit {
20049fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
20149fa1748Smahlzeit 	event->time   = time;
20249fa1748Smahlzeit 	event->ticks  = false;
20349fa1748Smahlzeit 	event->byte1  = B_SYS_EX_START;
20449fa1748Smahlzeit 	event->data   = malloc(length);
20549fa1748Smahlzeit 	event->length = length;
20649fa1748Smahlzeit 	memcpy(event->data, data, length);
20749fa1748Smahlzeit 	AddEvent(event);
20852a38012Sejakowatz }
20952a38012Sejakowatz 
21049fa1748Smahlzeit //------------------------------------------------------------------------------
2116ba60405Smahlzeit 
21249fa1748Smahlzeit void BMidiStore::SystemCommon(
21349fa1748Smahlzeit 	uchar status, uchar data1, uchar data2, uint32 time)
2146ba60405Smahlzeit {
21549fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
21649fa1748Smahlzeit 	event->time  = time;
21749fa1748Smahlzeit 	event->ticks = false;
21849fa1748Smahlzeit 	event->byte1 = status;
21949fa1748Smahlzeit 	event->byte2 = data1;
22049fa1748Smahlzeit 	event->byte3 = data2;
22149fa1748Smahlzeit 	AddEvent(event);
22252a38012Sejakowatz }
22352a38012Sejakowatz 
22449fa1748Smahlzeit //------------------------------------------------------------------------------
22552a38012Sejakowatz 
22649fa1748Smahlzeit void BMidiStore::SystemRealTime(uchar status, uint32 time)
2276ba60405Smahlzeit {
22849fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
22949fa1748Smahlzeit 	event->time  = time;
23049fa1748Smahlzeit 	event->ticks = false;
23149fa1748Smahlzeit 	event->byte1 = status;
23249fa1748Smahlzeit 	AddEvent(event);
23352a38012Sejakowatz }
23452a38012Sejakowatz 
23549fa1748Smahlzeit //------------------------------------------------------------------------------
23649fa1748Smahlzeit 
23749fa1748Smahlzeit void BMidiStore::TempoChange(int32 beatsPerMinute, uint32 time)
23849fa1748Smahlzeit {
23949fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
24049fa1748Smahlzeit 	event->time  = time;
24149fa1748Smahlzeit 	event->ticks = false;
24249fa1748Smahlzeit 	event->byte1 = 0xFF;
24349fa1748Smahlzeit 	event->byte2 = 0x51;
24449fa1748Smahlzeit 	event->byte3 = 0x03;
24549fa1748Smahlzeit 	event->tempo = beatsPerMinute;
24649fa1748Smahlzeit 	AddEvent(event);
24749fa1748Smahlzeit }
24849fa1748Smahlzeit 
24949fa1748Smahlzeit //------------------------------------------------------------------------------
2506ba60405Smahlzeit 
2516ba60405Smahlzeit status_t BMidiStore::Import(const entry_ref* ref)
2526ba60405Smahlzeit {
253*f9b2179cSmahlzeit 	memset(instruments, 0, 128 * sizeof(bool));
254*f9b2179cSmahlzeit 
25549fa1748Smahlzeit 	try
25649fa1748Smahlzeit 	{
25749fa1748Smahlzeit 		file = new BFile(ref, B_READ_ONLY);
25849fa1748Smahlzeit 		if (file->InitCheck() != B_OK)
25949fa1748Smahlzeit 		{
26049fa1748Smahlzeit 			throw file->InitCheck();
26125767509Smahlzeit 		}
26225767509Smahlzeit 
26349fa1748Smahlzeit 		char fourcc[4];
26449fa1748Smahlzeit 		ReadFourCC(fourcc);
26549fa1748Smahlzeit 		if (strncmp(fourcc, "MThd", 4) != 0)
266eac71506Sjerl1 		{
26749fa1748Smahlzeit 			throw (status_t) B_BAD_MIDI_DATA;
268eac71506Sjerl1 		}
26925767509Smahlzeit 
27049fa1748Smahlzeit 		if (Read32Bit() != 6)
271eac71506Sjerl1 		{
27249fa1748Smahlzeit 			throw (status_t) B_BAD_MIDI_DATA;
273eac71506Sjerl1 		}
274eac71506Sjerl1 
27549fa1748Smahlzeit 		format = Read16Bit();
27649fa1748Smahlzeit 		numTracks = Read16Bit();
27749fa1748Smahlzeit 		ticksPerBeat = Read16Bit();
27849fa1748Smahlzeit 
27949fa1748Smahlzeit 		if (ticksPerBeat & 0x8000)  // we don't support SMPTE
28049fa1748Smahlzeit 		{                           // time codes, only ticks
28149fa1748Smahlzeit 			ticksPerBeat = 240;     // per quarter note
282eac71506Sjerl1 		}
283eac71506Sjerl1 
28449fa1748Smahlzeit 		currTrack = 0;
28549fa1748Smahlzeit 		while (currTrack < numTracks)
2866ba60405Smahlzeit 		{
28749fa1748Smahlzeit 			ReadChunk();
28849fa1748Smahlzeit 		}
28949fa1748Smahlzeit 	}
29049fa1748Smahlzeit 	catch (status_t e)
29149fa1748Smahlzeit 	{
29249fa1748Smahlzeit 		delete file;
29349fa1748Smahlzeit 		file = NULL;
29449fa1748Smahlzeit 		return e;
295eac71506Sjerl1 	}
296eac71506Sjerl1 
297eac71506Sjerl1 	SortEvents(true);
298eac71506Sjerl1 
29949fa1748Smahlzeit 	delete file;
30049fa1748Smahlzeit 	file = NULL;
3016ba60405Smahlzeit 	return B_OK;
3026ba60405Smahlzeit }
3036ba60405Smahlzeit 
30449fa1748Smahlzeit //------------------------------------------------------------------------------
30549fa1748Smahlzeit 
30649fa1748Smahlzeit status_t BMidiStore::Export(const entry_ref* ref, int32 format)
30749fa1748Smahlzeit {
30849fa1748Smahlzeit 	try
30949fa1748Smahlzeit 	{
31049fa1748Smahlzeit 		file = new BFile(ref, B_READ_WRITE);
31149fa1748Smahlzeit 		if (file->InitCheck() != B_OK)
31249fa1748Smahlzeit 		{
31349fa1748Smahlzeit 			throw file->InitCheck();
31449fa1748Smahlzeit 		}
31549fa1748Smahlzeit 
31649fa1748Smahlzeit 		SortEvents(true);
31749fa1748Smahlzeit 
31849fa1748Smahlzeit 		WriteFourCC('M', 'T', 'h', 'd');
31949fa1748Smahlzeit 		Write32Bit(6);
32049fa1748Smahlzeit 		Write16Bit(0);  // we do only format 0
32149fa1748Smahlzeit 		Write16Bit(1);
32249fa1748Smahlzeit 		Write16Bit(ticksPerBeat);
32349fa1748Smahlzeit 
32449fa1748Smahlzeit 		WriteTrack();
32549fa1748Smahlzeit 	}
32649fa1748Smahlzeit 	catch (status_t e)
32749fa1748Smahlzeit 	{
32849fa1748Smahlzeit 		delete file;
32949fa1748Smahlzeit 		file = NULL;
33049fa1748Smahlzeit 		return e;
33149fa1748Smahlzeit 	}
33249fa1748Smahlzeit 
33349fa1748Smahlzeit 	delete file;
33449fa1748Smahlzeit 	file = NULL;
33549fa1748Smahlzeit 	return B_OK;
33649fa1748Smahlzeit }
33749fa1748Smahlzeit 
33849fa1748Smahlzeit //------------------------------------------------------------------------------
3396ba60405Smahlzeit 
3406ba60405Smahlzeit void BMidiStore::SortEvents(bool force)
3416ba60405Smahlzeit {
34249fa1748Smahlzeit 	if (force || needsSorting)
34349fa1748Smahlzeit 	{
34449fa1748Smahlzeit 		events->SortItems(compare_events);
34549fa1748Smahlzeit 		needsSorting = false;
34649fa1748Smahlzeit 	}
3476ba60405Smahlzeit }
3486ba60405Smahlzeit 
34949fa1748Smahlzeit //------------------------------------------------------------------------------
3506ba60405Smahlzeit 
3516ba60405Smahlzeit uint32 BMidiStore::CountEvents() const
35249fa1748Smahlzeit {
3536ba60405Smahlzeit 	return events->CountItems();
3546ba60405Smahlzeit }
3556ba60405Smahlzeit 
35649fa1748Smahlzeit //------------------------------------------------------------------------------
3576ba60405Smahlzeit 
3586ba60405Smahlzeit uint32 BMidiStore::CurrentEvent() const
35949fa1748Smahlzeit {
36049fa1748Smahlzeit 	return currentEvent;
3616ba60405Smahlzeit }
3626ba60405Smahlzeit 
36349fa1748Smahlzeit //------------------------------------------------------------------------------
3646ba60405Smahlzeit 
36549fa1748Smahlzeit void BMidiStore::SetCurrentEvent(uint32 eventNumber)
36649fa1748Smahlzeit {
36749fa1748Smahlzeit 	currentEvent = eventNumber;
3686ba60405Smahlzeit }
3696ba60405Smahlzeit 
37049fa1748Smahlzeit //------------------------------------------------------------------------------
3716ba60405Smahlzeit 
37249fa1748Smahlzeit uint32 BMidiStore::DeltaOfEvent(uint32 eventNumber) const
37349fa1748Smahlzeit {
37449fa1748Smahlzeit 	// Even though the BeBook says that the delta is the time span between
37549fa1748Smahlzeit 	// an event and the first event in the list, this doesn't appear to be
37649fa1748Smahlzeit 	// true for events that were captured from other BMidi objects such as
37749fa1748Smahlzeit 	// BMidiPort. For those events, we return the absolute timestamp. The
37849fa1748Smahlzeit 	// BeBook is correct for events from MIDI files, though.
37949fa1748Smahlzeit 
38049fa1748Smahlzeit 	BMidiEvent* event = EventAt(eventNumber);
38149fa1748Smahlzeit 	if (event != NULL)
38249fa1748Smahlzeit 	{
38349fa1748Smahlzeit 		return GetEventTime(event);
38449fa1748Smahlzeit 	}
38549fa1748Smahlzeit 
3866ba60405Smahlzeit 	return 0;
3876ba60405Smahlzeit }
3886ba60405Smahlzeit 
38949fa1748Smahlzeit //------------------------------------------------------------------------------
390eac71506Sjerl1 
391eac71506Sjerl1 uint32 BMidiStore::EventAtDelta(uint32 time) const
39249fa1748Smahlzeit {
39349fa1748Smahlzeit 	for (int32 t = 0; t < events->CountItems(); ++t)
39449fa1748Smahlzeit 	{
39549fa1748Smahlzeit 		if (GetEventTime(EventAt(t)) >= time) { return t; }
39649fa1748Smahlzeit 	}
39749fa1748Smahlzeit 
398eac71506Sjerl1 	return 0;
399eac71506Sjerl1 }
400eac71506Sjerl1 
40149fa1748Smahlzeit //------------------------------------------------------------------------------
4026ba60405Smahlzeit 
4036ba60405Smahlzeit uint32 BMidiStore::BeginTime() const
40449fa1748Smahlzeit {
40549fa1748Smahlzeit 	return startTime;
4066ba60405Smahlzeit }
4076ba60405Smahlzeit 
40849fa1748Smahlzeit //------------------------------------------------------------------------------
4096ba60405Smahlzeit 
41049fa1748Smahlzeit void BMidiStore::SetTempo(int32 beatsPerMinute_)
41149fa1748Smahlzeit {
41249fa1748Smahlzeit 	beatsPerMinute = beatsPerMinute_;
4136ba60405Smahlzeit }
4146ba60405Smahlzeit 
41549fa1748Smahlzeit //------------------------------------------------------------------------------
4166ba60405Smahlzeit 
4176ba60405Smahlzeit int32 BMidiStore::Tempo() const
41849fa1748Smahlzeit {
41949fa1748Smahlzeit 	return beatsPerMinute;
4206ba60405Smahlzeit }
4216ba60405Smahlzeit 
42249fa1748Smahlzeit //------------------------------------------------------------------------------
42349fa1748Smahlzeit 
42449fa1748Smahlzeit void BMidiStore::_ReservedMidiStore1() { }
42549fa1748Smahlzeit void BMidiStore::_ReservedMidiStore2() { }
42649fa1748Smahlzeit void BMidiStore::_ReservedMidiStore3() { }
42749fa1748Smahlzeit 
42849fa1748Smahlzeit //------------------------------------------------------------------------------
4296ba60405Smahlzeit 
4306ba60405Smahlzeit void BMidiStore::Run()
4316ba60405Smahlzeit {
432b178e190Smahlzeit 	// This rather compilicated Run() loop is not only used by BMidiStore
433b178e190Smahlzeit 	// but also by BMidiSynthFile. The "paused", "finished", and "looping"
434b178e190Smahlzeit 	// flags, and the "stop hook" are especially provided for the latter.
435b178e190Smahlzeit 
436b178e190Smahlzeit 	paused = false;
437b178e190Smahlzeit 	finished = false;
438b178e190Smahlzeit 
43949fa1748Smahlzeit 	int32 timeAdjust;
440b178e190Smahlzeit 	uint32 baseTime;
44149fa1748Smahlzeit 	bool firstEvent = true;
442b178e190Smahlzeit 	bool resetTime = false;
44349fa1748Smahlzeit 
4446ba60405Smahlzeit 	while (KeepRunning())
4456ba60405Smahlzeit 	{
446b178e190Smahlzeit 		if (paused)
447b178e190Smahlzeit 		{
448b178e190Smahlzeit 			resetTime = true;
449b178e190Smahlzeit 			snooze(100000);
450b178e190Smahlzeit 			continue;
451b178e190Smahlzeit 		}
452b178e190Smahlzeit 
45349fa1748Smahlzeit 		BMidiEvent* event = EventAt(currentEvent);
454b178e190Smahlzeit 
455b178e190Smahlzeit 		if (event == NULL)  // no more events
456b178e190Smahlzeit 		{
457b178e190Smahlzeit 			if (looping)
458b178e190Smahlzeit 			{
459b178e190Smahlzeit 				resetTime = true;
460b178e190Smahlzeit 				currentEvent = 0;
461b178e190Smahlzeit 			}
462b178e190Smahlzeit 			else
463b178e190Smahlzeit 			{
464b178e190Smahlzeit 				break;
465b178e190Smahlzeit 			}
466b178e190Smahlzeit 		}
4676ba60405Smahlzeit 
46849fa1748Smahlzeit 		if (firstEvent)
4696ba60405Smahlzeit 		{
47049fa1748Smahlzeit 			startTime = B_NOW;
471b178e190Smahlzeit 			baseTime = startTime;
472b178e190Smahlzeit 		}
473b178e190Smahlzeit 		else if (resetTime)
474b178e190Smahlzeit 		{
475b178e190Smahlzeit 			baseTime = B_NOW;
476b178e190Smahlzeit 		}
477b178e190Smahlzeit 
478b178e190Smahlzeit 		if (firstEvent || resetTime)
479b178e190Smahlzeit 		{
480b178e190Smahlzeit 			timeAdjust = baseTime - GetEventTime(event);
481b178e190Smahlzeit 			SprayEvent(event, baseTime);
48249fa1748Smahlzeit 			firstEvent = false;
483b178e190Smahlzeit 			resetTime = false;
484eac71506Sjerl1 		}
48549fa1748Smahlzeit 		else
486eac71506Sjerl1 		{
48749fa1748Smahlzeit 			SprayEvent(event, GetEventTime(event) + timeAdjust);
4886ba60405Smahlzeit 		}
4896ba60405Smahlzeit 
49049fa1748Smahlzeit 		++currentEvent;
49149fa1748Smahlzeit 	}
492b178e190Smahlzeit 
493b178e190Smahlzeit 	finished = true;
494b178e190Smahlzeit 	paused = false;
495b178e190Smahlzeit 
496b178e190Smahlzeit 	if (hookFunc != NULL)
497b178e190Smahlzeit 	{
498b178e190Smahlzeit 		(*hookFunc)(hookArg);
499b178e190Smahlzeit 	}
5006ba60405Smahlzeit }
5016ba60405Smahlzeit 
50249fa1748Smahlzeit //------------------------------------------------------------------------------
5036ba60405Smahlzeit 
50449fa1748Smahlzeit void BMidiStore::AddEvent(BMidiEvent* event)
50525767509Smahlzeit {
50649fa1748Smahlzeit 	events->AddItem(event);
50749fa1748Smahlzeit 	needsSorting = true;
50825767509Smahlzeit }
50925767509Smahlzeit 
51049fa1748Smahlzeit //------------------------------------------------------------------------------
51149fa1748Smahlzeit 
51249fa1748Smahlzeit void BMidiStore::SprayEvent(const BMidiEvent* event, uint32 time)
51325767509Smahlzeit {
51449fa1748Smahlzeit 	uchar byte1 = event->byte1;
51549fa1748Smahlzeit 	uchar byte2 = event->byte2;
51649fa1748Smahlzeit 	uchar byte3 = event->byte3;
51725767509Smahlzeit 
51849fa1748Smahlzeit 	switch (byte1 & 0xF0)
519eac71506Sjerl1 	{
520eac71506Sjerl1 		case B_NOTE_OFF:
52149fa1748Smahlzeit 			SprayNoteOff((byte1 & 0x0F) + 1, byte2, byte3, time);
52249fa1748Smahlzeit 			return;
52349fa1748Smahlzeit 
524eac71506Sjerl1 		case B_NOTE_ON:
52549fa1748Smahlzeit 			SprayNoteOn((byte1 & 0x0F) + 1, byte2, byte3, time);
52649fa1748Smahlzeit 			return;
52749fa1748Smahlzeit 
528eac71506Sjerl1 		case B_KEY_PRESSURE:
52949fa1748Smahlzeit 			SprayKeyPressure((byte1 & 0x0F) + 1, byte2, byte3, time);
53049fa1748Smahlzeit 			return;
53149fa1748Smahlzeit 
532eac71506Sjerl1 		case B_CONTROL_CHANGE:
53349fa1748Smahlzeit 			SprayControlChange((byte1 & 0x0F) + 1, byte2, byte3, time);
53449fa1748Smahlzeit 			return;
53549fa1748Smahlzeit 
536eac71506Sjerl1 		case B_PROGRAM_CHANGE:
53749fa1748Smahlzeit 			SprayProgramChange((byte1 & 0x0F) + 1, byte2, time);
53849fa1748Smahlzeit 			return;
53949fa1748Smahlzeit 
540eac71506Sjerl1 		case B_CHANNEL_PRESSURE:
54149fa1748Smahlzeit 			SprayChannelPressure((byte1 & 0x0F) + 1, byte2, time);
54249fa1748Smahlzeit 			return;
54349fa1748Smahlzeit 
54449fa1748Smahlzeit 		case B_PITCH_BEND:
54549fa1748Smahlzeit 			SprayPitchBend((byte1 & 0x0F) + 1, byte2, byte3, time);
54649fa1748Smahlzeit 			return;
54749fa1748Smahlzeit 
54849fa1748Smahlzeit 		case 0xF0:
54949fa1748Smahlzeit 			switch (byte1)
550eac71506Sjerl1 			{
551eac71506Sjerl1 				case B_SYS_EX_START:
55249fa1748Smahlzeit 					SpraySystemExclusive(event->data, event->length, time);
55349fa1748Smahlzeit 					return;
55449fa1748Smahlzeit 
555eac71506Sjerl1 				case B_MIDI_TIME_CODE:
55649fa1748Smahlzeit 				case B_SONG_POSITION:
557eac71506Sjerl1 				case B_SONG_SELECT:
55849fa1748Smahlzeit 				case B_CABLE_MESSAGE:
559eac71506Sjerl1 				case B_TUNE_REQUEST:
56049fa1748Smahlzeit 				case B_SYS_EX_END:
56149fa1748Smahlzeit 					SpraySystemCommon(byte1, byte2, byte3, time);
56249fa1748Smahlzeit 					return;
56349fa1748Smahlzeit 
564eac71506Sjerl1 				case B_TIMING_CLOCK:
565eac71506Sjerl1 				case B_START:
566eac71506Sjerl1 				case B_CONTINUE:
567eac71506Sjerl1 				case B_STOP:
568eac71506Sjerl1 				case B_ACTIVE_SENSING:
56949fa1748Smahlzeit 					SpraySystemRealTime(byte1, time);
57049fa1748Smahlzeit 					return;
57149fa1748Smahlzeit 
572eac71506Sjerl1 				case B_SYSTEM_RESET:
57349fa1748Smahlzeit 					if ((byte2 == 0x51) && (byte3 == 0x03))
57449fa1748Smahlzeit 					{
57549fa1748Smahlzeit 						SprayTempoChange(event->tempo, time);
57649fa1748Smahlzeit 						beatsPerMinute = event->tempo;
577eac71506Sjerl1 					}
578eac71506Sjerl1 					else
579eac71506Sjerl1 					{
58049fa1748Smahlzeit 						SpraySystemRealTime(byte1, time);
581eac71506Sjerl1 					}
58249fa1748Smahlzeit 					return;
583eac71506Sjerl1 			}
58449fa1748Smahlzeit 			return;
585eac71506Sjerl1 	}
586eac71506Sjerl1 }
587eac71506Sjerl1 
58849fa1748Smahlzeit //------------------------------------------------------------------------------
589eac71506Sjerl1 
59049fa1748Smahlzeit BMidiEvent* BMidiStore::EventAt(int32 index) const
591eac71506Sjerl1 {
59249fa1748Smahlzeit 	return (BMidiEvent*) events->ItemAt(index);
593eac71506Sjerl1 }
594eac71506Sjerl1 
59549fa1748Smahlzeit //------------------------------------------------------------------------------
596eac71506Sjerl1 
59749fa1748Smahlzeit uint32 BMidiStore::GetEventTime(const BMidiEvent* event) const
598eac71506Sjerl1 {
59949fa1748Smahlzeit 	if (event->ticks)
60049fa1748Smahlzeit 	{
60149fa1748Smahlzeit 		return TicksToMilliseconds(event->time);
60249fa1748Smahlzeit 	}
603eac71506Sjerl1 	else
604eac71506Sjerl1 	{
60549fa1748Smahlzeit 		return event->time;
60649fa1748Smahlzeit 	}
60749fa1748Smahlzeit }
60849fa1748Smahlzeit 
60949fa1748Smahlzeit //------------------------------------------------------------------------------
61049fa1748Smahlzeit 
61149fa1748Smahlzeit uint32 BMidiStore::TicksToMilliseconds(uint32 ticks) const
612eac71506Sjerl1 {
61349fa1748Smahlzeit 	return ((uint64) ticks * 60000) / (beatsPerMinute * ticksPerBeat);
61449fa1748Smahlzeit }
61549fa1748Smahlzeit 
61649fa1748Smahlzeit //------------------------------------------------------------------------------
61749fa1748Smahlzeit 
61849fa1748Smahlzeit uint32 BMidiStore::MillisecondsToTicks(uint32 ms) const
619eac71506Sjerl1 {
62049fa1748Smahlzeit 	return ((uint64) ms * beatsPerMinute * ticksPerBeat) / 60000;
621eac71506Sjerl1 }
62249fa1748Smahlzeit 
62349fa1748Smahlzeit //------------------------------------------------------------------------------
62449fa1748Smahlzeit 
62549fa1748Smahlzeit void BMidiStore::ReadFourCC(char* fourcc)
626eac71506Sjerl1 {
62749fa1748Smahlzeit 	if (file->Read(fourcc, 4) != 4)
628eac71506Sjerl1 	{
62949fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
630eac71506Sjerl1 	}
63149fa1748Smahlzeit }
63249fa1748Smahlzeit 
63349fa1748Smahlzeit //------------------------------------------------------------------------------
63449fa1748Smahlzeit 
63549fa1748Smahlzeit void BMidiStore::WriteFourCC(char a, char b, char c, char d)
636eac71506Sjerl1 {
63749fa1748Smahlzeit 	char fourcc[4] = { a, b, c, d };
63849fa1748Smahlzeit 	if (file->Write(fourcc, 4) != 4)
639eac71506Sjerl1 	{
64049fa1748Smahlzeit 		throw (status_t) B_ERROR;
641eac71506Sjerl1 	}
64249fa1748Smahlzeit }
64349fa1748Smahlzeit 
64449fa1748Smahlzeit //------------------------------------------------------------------------------
64549fa1748Smahlzeit 
64649fa1748Smahlzeit uint32 BMidiStore::Read32Bit()
647eac71506Sjerl1 {
64849fa1748Smahlzeit 	uint8 buf[4];
64949fa1748Smahlzeit 	if (file->Read(buf, 4) != 4)
650eac71506Sjerl1 	{
65149fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
652eac71506Sjerl1 	}
65349fa1748Smahlzeit 
65449fa1748Smahlzeit 	return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
65549fa1748Smahlzeit }
65649fa1748Smahlzeit 
65749fa1748Smahlzeit //------------------------------------------------------------------------------
65849fa1748Smahlzeit 
65949fa1748Smahlzeit void BMidiStore::Write32Bit(uint32 val)
660eac71506Sjerl1 {
66149fa1748Smahlzeit 	uint8 buf[4];
66249fa1748Smahlzeit 	buf[0] = (val >> 24) & 0xFF;
66349fa1748Smahlzeit 	buf[1] = (val >> 16) & 0xFF;
66449fa1748Smahlzeit 	buf[2] = (val >>  8) & 0xFF;
66549fa1748Smahlzeit 	buf[3] =  val        & 0xFF;
66649fa1748Smahlzeit 
66749fa1748Smahlzeit 	if (file->Write(buf, 4) != 4)
668eac71506Sjerl1 	{
66949fa1748Smahlzeit 		throw (status_t) B_ERROR;
670eac71506Sjerl1 	}
67149fa1748Smahlzeit }
67249fa1748Smahlzeit 
67349fa1748Smahlzeit //------------------------------------------------------------------------------
67449fa1748Smahlzeit 
67549fa1748Smahlzeit uint16 BMidiStore::Read16Bit()
676eac71506Sjerl1 {
67749fa1748Smahlzeit 	uint8 buf[2];
67849fa1748Smahlzeit 	if (file->Read(buf, 2) != 2)
679eac71506Sjerl1 	{
68049fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
681eac71506Sjerl1 	}
682eac71506Sjerl1 
68349fa1748Smahlzeit 	return (buf[0] << 8) | buf[1];
684eac71506Sjerl1 }
685eac71506Sjerl1 
68649fa1748Smahlzeit //------------------------------------------------------------------------------
687eac71506Sjerl1 
68849fa1748Smahlzeit void BMidiStore::Write16Bit(uint16 val)
689eac71506Sjerl1 {
69049fa1748Smahlzeit 	uint8 buf[2];
69149fa1748Smahlzeit 	buf[0] = (val >>  8) & 0xFF;
69249fa1748Smahlzeit 	buf[1] =  val        & 0xFF;
693eac71506Sjerl1 
69449fa1748Smahlzeit 	if (file->Write(buf, 2) != 2)
695eac71506Sjerl1 	{
69649fa1748Smahlzeit 		throw (status_t) B_ERROR;
697eac71506Sjerl1 	}
698eac71506Sjerl1 }
699eac71506Sjerl1 
70049fa1748Smahlzeit //------------------------------------------------------------------------------
701eac71506Sjerl1 
70249fa1748Smahlzeit uint8 BMidiStore::PeekByte()
703eac71506Sjerl1 {
70449fa1748Smahlzeit 	uint8 buf;
70549fa1748Smahlzeit 	if (file->Read(&buf, 1) != 1)
70649fa1748Smahlzeit 	{
70749fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
708eac71506Sjerl1 	}
709eac71506Sjerl1 
71049fa1748Smahlzeit 	if (file->Seek(-1, SEEK_CUR) < 0)
71149fa1748Smahlzeit 	{
71249fa1748Smahlzeit 		throw (status_t) B_ERROR;
713eac71506Sjerl1 	}
714eac71506Sjerl1 
71549fa1748Smahlzeit 	return buf;
71649fa1748Smahlzeit }
717eac71506Sjerl1 
71849fa1748Smahlzeit //------------------------------------------------------------------------------
71949fa1748Smahlzeit 
72049fa1748Smahlzeit uint8 BMidiStore::NextByte()
72149fa1748Smahlzeit {
72249fa1748Smahlzeit 	uint8 buf;
72349fa1748Smahlzeit 	if (file->Read(&buf, 1) != 1)
72449fa1748Smahlzeit 	{
72549fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
72649fa1748Smahlzeit 	}
72749fa1748Smahlzeit 
72849fa1748Smahlzeit 	--byteCount;
72949fa1748Smahlzeit 	return buf;
73049fa1748Smahlzeit }
73149fa1748Smahlzeit 
73249fa1748Smahlzeit //------------------------------------------------------------------------------
73349fa1748Smahlzeit 
73449fa1748Smahlzeit void BMidiStore::WriteByte(uint8 val)
73549fa1748Smahlzeit {
73649fa1748Smahlzeit 	if (file->Write(&val, 1) != 1)
73749fa1748Smahlzeit 	{
73849fa1748Smahlzeit 		throw (status_t) B_ERROR;
73949fa1748Smahlzeit 	}
74049fa1748Smahlzeit 
74149fa1748Smahlzeit 	++byteCount;
74249fa1748Smahlzeit }
74349fa1748Smahlzeit 
74449fa1748Smahlzeit //------------------------------------------------------------------------------
74549fa1748Smahlzeit 
74649fa1748Smahlzeit void BMidiStore::SkipBytes(uint32 length)
74749fa1748Smahlzeit {
74849fa1748Smahlzeit 	if (file->Seek(length, SEEK_CUR) < 0)
74949fa1748Smahlzeit 	{
75049fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
75149fa1748Smahlzeit 	}
75249fa1748Smahlzeit 
75349fa1748Smahlzeit 	byteCount -= length;
75449fa1748Smahlzeit }
75549fa1748Smahlzeit 
75649fa1748Smahlzeit //------------------------------------------------------------------------------
75749fa1748Smahlzeit 
75849fa1748Smahlzeit uint32 BMidiStore::ReadVarLength()
75949fa1748Smahlzeit {
76049fa1748Smahlzeit 	uint32 val;
76149fa1748Smahlzeit 	uint8 byte;
76249fa1748Smahlzeit 
76349fa1748Smahlzeit 	if ((val = NextByte()) & 0x80)
76449fa1748Smahlzeit 	{
76549fa1748Smahlzeit 		val &= 0x7F;
76649fa1748Smahlzeit 		do
76749fa1748Smahlzeit 		{
76849fa1748Smahlzeit 			val = (val << 7) + ((byte = NextByte()) & 0x7F);
76949fa1748Smahlzeit 		}
77049fa1748Smahlzeit 		while (byte & 0x80);
77149fa1748Smahlzeit 	}
77249fa1748Smahlzeit 
77349fa1748Smahlzeit 	return val;
77449fa1748Smahlzeit }
77549fa1748Smahlzeit 
77649fa1748Smahlzeit //------------------------------------------------------------------------------
77749fa1748Smahlzeit 
77849fa1748Smahlzeit void BMidiStore::WriteVarLength(uint32 val)
77949fa1748Smahlzeit {
78049fa1748Smahlzeit 	uint32 buffer = val & 0x7F;
78149fa1748Smahlzeit 
78249fa1748Smahlzeit 	while ((val >>= 7))
783eac71506Sjerl1 	{
784eac71506Sjerl1 		buffer <<= 8;
78549fa1748Smahlzeit 		buffer |= ((val & 0x7F) | 0x80);
786eac71506Sjerl1 	}
78749fa1748Smahlzeit 
788eac71506Sjerl1 	while (true)
789eac71506Sjerl1 	{
79049fa1748Smahlzeit 		WriteByte(buffer);
791eac71506Sjerl1 		if (buffer & 0x80)
792eac71506Sjerl1 			buffer >>= 8;
793eac71506Sjerl1 		else
794eac71506Sjerl1 			break;
79525767509Smahlzeit 	}
79625767509Smahlzeit }
79725767509Smahlzeit 
79849fa1748Smahlzeit //------------------------------------------------------------------------------
79925767509Smahlzeit 
80049fa1748Smahlzeit void BMidiStore::ReadChunk()
801eac71506Sjerl1 {
80249fa1748Smahlzeit 	char fourcc[4];
80349fa1748Smahlzeit 	ReadFourCC(fourcc);
804eac71506Sjerl1 
80549fa1748Smahlzeit 	byteCount = Read32Bit();
806eac71506Sjerl1 
80749fa1748Smahlzeit 	if (strncmp(fourcc, "MTrk", 4) == 0)
808eac71506Sjerl1 	{
80949fa1748Smahlzeit 		ReadTrack();
810eac71506Sjerl1 	}
81149fa1748Smahlzeit 	else
812eac71506Sjerl1 	{
81349fa1748Smahlzeit 		TRACE(("Skipping '%c%c%c%c' chunk (%lu bytes)",
81449fa1748Smahlzeit 			fourcc[0], fourcc[1], fourcc[2], fourcc[3], byteCount))
81549fa1748Smahlzeit 
81649fa1748Smahlzeit 		SkipBytes(byteCount);
81749fa1748Smahlzeit 	}
818eac71506Sjerl1 }
819eac71506Sjerl1 
82049fa1748Smahlzeit //------------------------------------------------------------------------------
82149fa1748Smahlzeit 
82249fa1748Smahlzeit void BMidiStore::ReadTrack()
82349fa1748Smahlzeit {
82449fa1748Smahlzeit 	uint8 status = 0;
82549fa1748Smahlzeit 	uint8 data1;
82649fa1748Smahlzeit 	uint8 data2;
82749fa1748Smahlzeit 	BMidiEvent* event;
82849fa1748Smahlzeit 
82949fa1748Smahlzeit 	totalTicks = 0;
83049fa1748Smahlzeit 
83149fa1748Smahlzeit 	while (byteCount > 0)
83249fa1748Smahlzeit 	{
83349fa1748Smahlzeit 		uint32 ticks = ReadVarLength();
83449fa1748Smahlzeit 		totalTicks += ticks;
83549fa1748Smahlzeit 
83649fa1748Smahlzeit 		if (PeekByte() & 0x80)
83749fa1748Smahlzeit 		{
83849fa1748Smahlzeit 			status = NextByte();
83949fa1748Smahlzeit 		}
84049fa1748Smahlzeit 
84149fa1748Smahlzeit 		switch (status & 0xF0)
84249fa1748Smahlzeit 		{
84349fa1748Smahlzeit 			case B_NOTE_OFF:
84449fa1748Smahlzeit 			case B_NOTE_ON:
84549fa1748Smahlzeit 			case B_KEY_PRESSURE:
84649fa1748Smahlzeit 			case B_CONTROL_CHANGE:
84749fa1748Smahlzeit 			case B_PITCH_BEND:
84849fa1748Smahlzeit 				data1 = NextByte();
84949fa1748Smahlzeit 				data2 = NextByte();
85049fa1748Smahlzeit 				event = new BMidiEvent;
85149fa1748Smahlzeit 				event->time  = totalTicks;
85249fa1748Smahlzeit 				event->ticks = true;
85349fa1748Smahlzeit 				event->byte1 = status;
85449fa1748Smahlzeit 				event->byte2 = data1;
85549fa1748Smahlzeit 				event->byte3 = data2;
85649fa1748Smahlzeit 				AddEvent(event);
85749fa1748Smahlzeit 				break;
85849fa1748Smahlzeit 
85949fa1748Smahlzeit 			case B_PROGRAM_CHANGE:
86049fa1748Smahlzeit 			case B_CHANNEL_PRESSURE:
86149fa1748Smahlzeit 				data1 = NextByte();
86249fa1748Smahlzeit 				event = new BMidiEvent;
86349fa1748Smahlzeit 				event->time  = totalTicks;
86449fa1748Smahlzeit 				event->ticks = true;
86549fa1748Smahlzeit 				event->byte1 = status;
86649fa1748Smahlzeit 				event->byte2 = data1;
86749fa1748Smahlzeit 				AddEvent(event);
868*f9b2179cSmahlzeit 
869*f9b2179cSmahlzeit 				if ((status & 0xF0) == B_PROGRAM_CHANGE)
870*f9b2179cSmahlzeit 				{
871*f9b2179cSmahlzeit 					instruments[data1] = true;
872*f9b2179cSmahlzeit 				}
87349fa1748Smahlzeit 				break;
87449fa1748Smahlzeit 
87549fa1748Smahlzeit 			case 0xF0:
87649fa1748Smahlzeit 				switch (status)
87749fa1748Smahlzeit 				{
87849fa1748Smahlzeit 					case B_SYS_EX_START:
87949fa1748Smahlzeit 						ReadSystemExclusive();
88049fa1748Smahlzeit 						break;
88149fa1748Smahlzeit 
88249fa1748Smahlzeit 					case B_TUNE_REQUEST:
88349fa1748Smahlzeit 					case B_SYS_EX_END:
88449fa1748Smahlzeit 					case B_TIMING_CLOCK:
88549fa1748Smahlzeit 					case B_START:
88649fa1748Smahlzeit 					case B_CONTINUE:
88749fa1748Smahlzeit 					case B_STOP:
88849fa1748Smahlzeit 					case B_ACTIVE_SENSING:
88949fa1748Smahlzeit 						event = new BMidiEvent;
89049fa1748Smahlzeit 						event->time  = totalTicks;
89149fa1748Smahlzeit 						event->ticks = true;
89249fa1748Smahlzeit 						event->byte1 = status;
89349fa1748Smahlzeit 						AddEvent(event);
89449fa1748Smahlzeit 						break;
89549fa1748Smahlzeit 
89649fa1748Smahlzeit 					case B_MIDI_TIME_CODE:
89749fa1748Smahlzeit 					case B_SONG_SELECT:
89849fa1748Smahlzeit 					case B_CABLE_MESSAGE:
89949fa1748Smahlzeit 						data1 = NextByte();
90049fa1748Smahlzeit 						event = new BMidiEvent;
90149fa1748Smahlzeit 						event->time  = totalTicks;
90249fa1748Smahlzeit 						event->ticks = true;
90349fa1748Smahlzeit 						event->byte1 = status;
90449fa1748Smahlzeit 						event->byte2 = data1;
90549fa1748Smahlzeit 						AddEvent(event);
90649fa1748Smahlzeit 						break;
90749fa1748Smahlzeit 
90849fa1748Smahlzeit 					case B_SONG_POSITION:
90949fa1748Smahlzeit 						data1 = NextByte();
91049fa1748Smahlzeit 						data2 = NextByte();
91149fa1748Smahlzeit 						event = new BMidiEvent;
91249fa1748Smahlzeit 						event->time  = totalTicks;
91349fa1748Smahlzeit 						event->ticks = true;
91449fa1748Smahlzeit 						event->byte1 = status;
91549fa1748Smahlzeit 						event->byte2 = data1;
91649fa1748Smahlzeit 						event->byte3 = data2;
91749fa1748Smahlzeit 						AddEvent(event);
91849fa1748Smahlzeit 						break;
91949fa1748Smahlzeit 
92049fa1748Smahlzeit 					case B_SYSTEM_RESET:
92149fa1748Smahlzeit 						ReadMetaEvent();
92249fa1748Smahlzeit 						break;
92349fa1748Smahlzeit 				}
92449fa1748Smahlzeit 				break;
92549fa1748Smahlzeit 		}
92649fa1748Smahlzeit 
92749fa1748Smahlzeit 		event = NULL;
92849fa1748Smahlzeit 	}
92949fa1748Smahlzeit 
93049fa1748Smahlzeit 	++currTrack;
93149fa1748Smahlzeit }
93249fa1748Smahlzeit 
93349fa1748Smahlzeit //------------------------------------------------------------------------------
93449fa1748Smahlzeit 
93549fa1748Smahlzeit void BMidiStore::ReadSystemExclusive()
93649fa1748Smahlzeit {
93749fa1748Smahlzeit 	// We do not import sysex's from MIDI files.
93849fa1748Smahlzeit 
93949fa1748Smahlzeit 	SkipBytes(ReadVarLength());
94049fa1748Smahlzeit }
94149fa1748Smahlzeit 
94249fa1748Smahlzeit //------------------------------------------------------------------------------
94349fa1748Smahlzeit 
94449fa1748Smahlzeit void BMidiStore::ReadMetaEvent()
94549fa1748Smahlzeit {
94649fa1748Smahlzeit 	// We only import the Tempo Change meta event.
94749fa1748Smahlzeit 
94849fa1748Smahlzeit 	uint8 type = NextByte();
94949fa1748Smahlzeit 	uint32 length = ReadVarLength();
95049fa1748Smahlzeit 
95149fa1748Smahlzeit 	if ((type == 0x51) && (length == 3))
95249fa1748Smahlzeit 	{
95349fa1748Smahlzeit 		uchar data[3];
95449fa1748Smahlzeit 		data[0] = NextByte();
95549fa1748Smahlzeit 		data[1] = NextByte();
95649fa1748Smahlzeit 		data[2] = NextByte();
95749fa1748Smahlzeit 		uint32 val = (data[0] << 16) | (data[1] << 8) | data[2];
95849fa1748Smahlzeit 
95949fa1748Smahlzeit 		BMidiEvent* event = new BMidiEvent;
96049fa1748Smahlzeit 		event->time  = totalTicks;
96149fa1748Smahlzeit 		event->ticks = true;
96249fa1748Smahlzeit 		event->byte1 = 0xFF;
96349fa1748Smahlzeit 		event->byte2 = 0x51;
96449fa1748Smahlzeit 		event->byte3 = 0x03;
96549fa1748Smahlzeit 		event->tempo = 60000000 / val;
96649fa1748Smahlzeit 		AddEvent(event);
96749fa1748Smahlzeit 	}
96849fa1748Smahlzeit 	else
96949fa1748Smahlzeit 	{
97049fa1748Smahlzeit 		SkipBytes(length);
97149fa1748Smahlzeit 	}
97249fa1748Smahlzeit }
97349fa1748Smahlzeit 
97449fa1748Smahlzeit //------------------------------------------------------------------------------
97549fa1748Smahlzeit 
97649fa1748Smahlzeit void BMidiStore::WriteTrack()
97749fa1748Smahlzeit {
97849fa1748Smahlzeit 	WriteFourCC('M', 'T', 'r', 'k');
97949fa1748Smahlzeit 	off_t lengthPos = file->Position();
98049fa1748Smahlzeit 	Write32Bit(0);
98149fa1748Smahlzeit 
98249fa1748Smahlzeit 	byteCount = 0;
98349fa1748Smahlzeit 	uint32 oldTime;
98449fa1748Smahlzeit 	uint32 newTime;
98549fa1748Smahlzeit 
9869138338cSmahlzeit 	for (uint32 t = 0; t < CountEvents(); ++t)
98749fa1748Smahlzeit 	{
98849fa1748Smahlzeit 		BMidiEvent* event = EventAt(t);
98949fa1748Smahlzeit 
99049fa1748Smahlzeit 		if (event->ticks)
99149fa1748Smahlzeit 		{
99249fa1748Smahlzeit 			newTime = event->time;
99349fa1748Smahlzeit 		}
99449fa1748Smahlzeit 		else
99549fa1748Smahlzeit 		{
99649fa1748Smahlzeit 			newTime = MillisecondsToTicks(event->time);
99749fa1748Smahlzeit 		}
99849fa1748Smahlzeit 
99949fa1748Smahlzeit 		if (t == 0)
100049fa1748Smahlzeit 		{
100149fa1748Smahlzeit 			WriteVarLength(0);
100249fa1748Smahlzeit 		}
100349fa1748Smahlzeit 		else
100449fa1748Smahlzeit 		{
100549fa1748Smahlzeit 			WriteVarLength(newTime - oldTime);
100649fa1748Smahlzeit 		}
100749fa1748Smahlzeit 
100849fa1748Smahlzeit 		oldTime = newTime;
100949fa1748Smahlzeit 
101049fa1748Smahlzeit 		switch (event->byte1 & 0xF0)
101149fa1748Smahlzeit 		{
101249fa1748Smahlzeit 			case B_NOTE_OFF:
101349fa1748Smahlzeit 			case B_NOTE_ON:
101449fa1748Smahlzeit 			case B_KEY_PRESSURE:
101549fa1748Smahlzeit 			case B_CONTROL_CHANGE:
101649fa1748Smahlzeit 			case B_PITCH_BEND:
101749fa1748Smahlzeit 				WriteByte(event->byte1);
101849fa1748Smahlzeit 				WriteByte(event->byte2);
101949fa1748Smahlzeit 				WriteByte(event->byte3);
102049fa1748Smahlzeit 				break;
102149fa1748Smahlzeit 
102249fa1748Smahlzeit 			case B_PROGRAM_CHANGE:
102349fa1748Smahlzeit 			case B_CHANNEL_PRESSURE:
102449fa1748Smahlzeit 				WriteByte(event->byte1);
102549fa1748Smahlzeit 				WriteByte(event->byte2);
102649fa1748Smahlzeit 				break;
102749fa1748Smahlzeit 
102849fa1748Smahlzeit 			case 0xF0:
102949fa1748Smahlzeit 				switch (event->byte1)
103049fa1748Smahlzeit 				{
103149fa1748Smahlzeit 					case B_SYS_EX_START:
103249fa1748Smahlzeit 						// We do not export sysex's.
103349fa1748Smahlzeit 						break;
103449fa1748Smahlzeit 
103549fa1748Smahlzeit 					case B_TUNE_REQUEST:
103649fa1748Smahlzeit 					case B_SYS_EX_END:
103749fa1748Smahlzeit 					case B_TIMING_CLOCK:
103849fa1748Smahlzeit 					case B_START:
103949fa1748Smahlzeit 					case B_CONTINUE:
104049fa1748Smahlzeit 					case B_STOP:
104149fa1748Smahlzeit 					case B_ACTIVE_SENSING:
104249fa1748Smahlzeit 						WriteByte(event->byte1);
104349fa1748Smahlzeit 						break;
104449fa1748Smahlzeit 
104549fa1748Smahlzeit 					case B_MIDI_TIME_CODE:
104649fa1748Smahlzeit 					case B_SONG_SELECT:
104749fa1748Smahlzeit 					case B_CABLE_MESSAGE:
104849fa1748Smahlzeit 						WriteByte(event->byte1);
104949fa1748Smahlzeit 						WriteByte(event->byte2);
105049fa1748Smahlzeit 						break;
105149fa1748Smahlzeit 
105249fa1748Smahlzeit 					case B_SONG_POSITION:
105349fa1748Smahlzeit 						WriteByte(event->byte1);
105449fa1748Smahlzeit 						WriteByte(event->byte2);
105549fa1748Smahlzeit 						WriteByte(event->byte3);
105649fa1748Smahlzeit 						break;
105749fa1748Smahlzeit 
105849fa1748Smahlzeit 					case B_SYSTEM_RESET:
105949fa1748Smahlzeit 						WriteMetaEvent(event);
106049fa1748Smahlzeit 						break;
106149fa1748Smahlzeit 				}
106249fa1748Smahlzeit 				break;
106349fa1748Smahlzeit 
106449fa1748Smahlzeit 		}
106549fa1748Smahlzeit 	}
106649fa1748Smahlzeit 
106749fa1748Smahlzeit 	WriteVarLength(0);
106849fa1748Smahlzeit 	WriteByte(0xFF);   // the end-of-track
106949fa1748Smahlzeit 	WriteByte(0x2F);   // marker is required
107049fa1748Smahlzeit 	WriteByte(0x00);
107149fa1748Smahlzeit 
107249fa1748Smahlzeit 	file->Seek(lengthPos, SEEK_SET);
107349fa1748Smahlzeit 	Write32Bit(byteCount);
107449fa1748Smahlzeit 	file->Seek(0, SEEK_END);
107549fa1748Smahlzeit }
107649fa1748Smahlzeit 
107749fa1748Smahlzeit //------------------------------------------------------------------------------
107849fa1748Smahlzeit 
107949fa1748Smahlzeit void BMidiStore::WriteMetaEvent(BMidiEvent* event)
108049fa1748Smahlzeit {
108149fa1748Smahlzeit 	// We only export the Tempo Change meta event.
108249fa1748Smahlzeit 
108349fa1748Smahlzeit 	if ((event->byte2 == 0x51) && (event->byte3 == 0x03))
108449fa1748Smahlzeit 	{
108549fa1748Smahlzeit 		uint32 val = 60000000 / event->tempo;
108649fa1748Smahlzeit 
108749fa1748Smahlzeit 		WriteByte(0xFF);
108849fa1748Smahlzeit 		WriteByte(0x51);
108949fa1748Smahlzeit 		WriteByte(0x03);
109049fa1748Smahlzeit 		WriteByte(val >> 16);
109149fa1748Smahlzeit 		WriteByte(val >> 8);
109249fa1748Smahlzeit 		WriteByte(val);
109349fa1748Smahlzeit 	}
109449fa1748Smahlzeit }
109549fa1748Smahlzeit 
1096b178e190Smahlzeit 
109749fa1748Smahlzeit //------------------------------------------------------------------------------
1098