xref: /haiku/src/kits/midi/MidiStore.cpp (revision 9138338c722a120ea9ab620ea9ef47a562e14cc4)
149fa1748Smahlzeit /*
2*9138338cSmahlzeit  * 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;
8352a38012Sejakowatz }
8452a38012Sejakowatz 
8549fa1748Smahlzeit //------------------------------------------------------------------------------
866ba60405Smahlzeit 
876ba60405Smahlzeit BMidiStore::~BMidiStore()
886ba60405Smahlzeit {
8949fa1748Smahlzeit 	for (int32 t = 0; t < events->CountItems(); ++t)
906ba60405Smahlzeit 	{
9149fa1748Smahlzeit 		delete EventAt(t);
9252a38012Sejakowatz 	}
936ba60405Smahlzeit 	delete events;
9452a38012Sejakowatz }
9552a38012Sejakowatz 
9649fa1748Smahlzeit //------------------------------------------------------------------------------
9752a38012Sejakowatz 
9849fa1748Smahlzeit void BMidiStore::NoteOff(
9949fa1748Smahlzeit 	uchar channel, uchar note, uchar velocity, uint32 time)
1006ba60405Smahlzeit {
10149fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
10249fa1748Smahlzeit 	event->time  = time;
10349fa1748Smahlzeit 	event->ticks = false;
10449fa1748Smahlzeit 	event->byte1 = B_NOTE_OFF | (channel - 1);
10549fa1748Smahlzeit 	event->byte2 = note;
10649fa1748Smahlzeit 	event->byte3 = velocity;
10749fa1748Smahlzeit 	AddEvent(event);
1086ba60405Smahlzeit }
1096ba60405Smahlzeit 
11049fa1748Smahlzeit //------------------------------------------------------------------------------
1116ba60405Smahlzeit 
11249fa1748Smahlzeit void BMidiStore::NoteOn(
11349fa1748Smahlzeit 	uchar channel, uchar note, uchar velocity, uint32 time)
1146ba60405Smahlzeit {
11549fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
11649fa1748Smahlzeit 	event->time  = time;
11749fa1748Smahlzeit 	event->ticks = false;
11849fa1748Smahlzeit 	event->byte1 = B_NOTE_ON | (channel - 1);
11949fa1748Smahlzeit 	event->byte2 = note;
12049fa1748Smahlzeit 	event->byte3 = velocity;
12149fa1748Smahlzeit 	AddEvent(event);
1226ba60405Smahlzeit }
1236ba60405Smahlzeit 
12449fa1748Smahlzeit //------------------------------------------------------------------------------
1256ba60405Smahlzeit 
12649fa1748Smahlzeit void BMidiStore::KeyPressure(
12749fa1748Smahlzeit 	uchar channel, uchar note, uchar pressure, uint32 time)
1286ba60405Smahlzeit {
12949fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
13049fa1748Smahlzeit 	event->time  = time;
13149fa1748Smahlzeit 	event->ticks = false;
13249fa1748Smahlzeit 	event->byte1 = B_KEY_PRESSURE | (channel - 1);
13349fa1748Smahlzeit 	event->byte2 = note;
13449fa1748Smahlzeit 	event->byte3 = pressure;
13549fa1748Smahlzeit 	AddEvent(event);
1366ba60405Smahlzeit }
1376ba60405Smahlzeit 
13849fa1748Smahlzeit //------------------------------------------------------------------------------
1396ba60405Smahlzeit 
14049fa1748Smahlzeit void BMidiStore::ControlChange(
14149fa1748Smahlzeit 	uchar channel, uchar controlNumber, uchar controlValue, uint32 time)
1426ba60405Smahlzeit {
14349fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
14449fa1748Smahlzeit 	event->time  = time;
14549fa1748Smahlzeit 	event->ticks = false;
14649fa1748Smahlzeit 	event->byte1 = B_CONTROL_CHANGE | (channel - 1);
14749fa1748Smahlzeit 	event->byte2 = controlNumber;
14849fa1748Smahlzeit 	event->byte3 = controlValue;
14949fa1748Smahlzeit 	AddEvent(event);
1506ba60405Smahlzeit }
1516ba60405Smahlzeit 
15249fa1748Smahlzeit //------------------------------------------------------------------------------
1536ba60405Smahlzeit 
15449fa1748Smahlzeit void BMidiStore::ProgramChange(
15549fa1748Smahlzeit 	uchar channel, uchar programNumber, uint32 time)
1566ba60405Smahlzeit {
15749fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
15849fa1748Smahlzeit 	event->time  = time;
15949fa1748Smahlzeit 	event->ticks = false;
16049fa1748Smahlzeit 	event->byte1 = B_PROGRAM_CHANGE | (channel - 1);
16149fa1748Smahlzeit 	event->byte2 = programNumber;
16249fa1748Smahlzeit 	AddEvent(event);
1636ba60405Smahlzeit }
1646ba60405Smahlzeit 
16549fa1748Smahlzeit //------------------------------------------------------------------------------
1666ba60405Smahlzeit 
16749fa1748Smahlzeit void BMidiStore::ChannelPressure(uchar channel, uchar pressure, uint32 time)
1686ba60405Smahlzeit {
16949fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
17049fa1748Smahlzeit 	event->time  = time;
17149fa1748Smahlzeit 	event->ticks = false;
17249fa1748Smahlzeit 	event->byte1 = B_CHANNEL_PRESSURE | (channel - 1);
17349fa1748Smahlzeit 	event->byte2 = pressure;
17449fa1748Smahlzeit 	AddEvent(event);
1756ba60405Smahlzeit }
1766ba60405Smahlzeit 
17749fa1748Smahlzeit //------------------------------------------------------------------------------
1786ba60405Smahlzeit 
17949fa1748Smahlzeit void BMidiStore::PitchBend(uchar channel, uchar lsb, uchar msb, uint32 time)
1806ba60405Smahlzeit {
18149fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
18249fa1748Smahlzeit 	event->time  = time;
18349fa1748Smahlzeit 	event->ticks = false;
18449fa1748Smahlzeit 	event->byte1 = B_PITCH_BEND | (channel - 1);
18549fa1748Smahlzeit 	event->byte2 = lsb;
18649fa1748Smahlzeit 	event->byte3 = msb;
18749fa1748Smahlzeit 	AddEvent(event);
18852a38012Sejakowatz }
18952a38012Sejakowatz 
19049fa1748Smahlzeit //------------------------------------------------------------------------------
1916ba60405Smahlzeit 
19249fa1748Smahlzeit void BMidiStore::SystemExclusive(void* data, size_t length, uint32 time)
1936ba60405Smahlzeit {
19449fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
19549fa1748Smahlzeit 	event->time   = time;
19649fa1748Smahlzeit 	event->ticks  = false;
19749fa1748Smahlzeit 	event->byte1  = B_SYS_EX_START;
19849fa1748Smahlzeit 	event->data   = malloc(length);
19949fa1748Smahlzeit 	event->length = length;
20049fa1748Smahlzeit 	memcpy(event->data, data, length);
20149fa1748Smahlzeit 	AddEvent(event);
20252a38012Sejakowatz }
20352a38012Sejakowatz 
20449fa1748Smahlzeit //------------------------------------------------------------------------------
2056ba60405Smahlzeit 
20649fa1748Smahlzeit void BMidiStore::SystemCommon(
20749fa1748Smahlzeit 	uchar status, uchar data1, uchar data2, uint32 time)
2086ba60405Smahlzeit {
20949fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
21049fa1748Smahlzeit 	event->time  = time;
21149fa1748Smahlzeit 	event->ticks = false;
21249fa1748Smahlzeit 	event->byte1 = status;
21349fa1748Smahlzeit 	event->byte2 = data1;
21449fa1748Smahlzeit 	event->byte3 = data2;
21549fa1748Smahlzeit 	AddEvent(event);
21652a38012Sejakowatz }
21752a38012Sejakowatz 
21849fa1748Smahlzeit //------------------------------------------------------------------------------
21952a38012Sejakowatz 
22049fa1748Smahlzeit void BMidiStore::SystemRealTime(uchar status, uint32 time)
2216ba60405Smahlzeit {
22249fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
22349fa1748Smahlzeit 	event->time  = time;
22449fa1748Smahlzeit 	event->ticks = false;
22549fa1748Smahlzeit 	event->byte1 = status;
22649fa1748Smahlzeit 	AddEvent(event);
22752a38012Sejakowatz }
22852a38012Sejakowatz 
22949fa1748Smahlzeit //------------------------------------------------------------------------------
23049fa1748Smahlzeit 
23149fa1748Smahlzeit void BMidiStore::TempoChange(int32 beatsPerMinute, uint32 time)
23249fa1748Smahlzeit {
23349fa1748Smahlzeit 	BMidiEvent* event = new BMidiEvent;
23449fa1748Smahlzeit 	event->time  = time;
23549fa1748Smahlzeit 	event->ticks = false;
23649fa1748Smahlzeit 	event->byte1 = 0xFF;
23749fa1748Smahlzeit 	event->byte2 = 0x51;
23849fa1748Smahlzeit 	event->byte3 = 0x03;
23949fa1748Smahlzeit 	event->tempo = beatsPerMinute;
24049fa1748Smahlzeit 	AddEvent(event);
24149fa1748Smahlzeit }
24249fa1748Smahlzeit 
24349fa1748Smahlzeit //------------------------------------------------------------------------------
2446ba60405Smahlzeit 
2456ba60405Smahlzeit status_t BMidiStore::Import(const entry_ref* ref)
2466ba60405Smahlzeit {
24749fa1748Smahlzeit 	try
24849fa1748Smahlzeit 	{
24949fa1748Smahlzeit 		file = new BFile(ref, B_READ_ONLY);
25049fa1748Smahlzeit 		if (file->InitCheck() != B_OK)
25149fa1748Smahlzeit 		{
25249fa1748Smahlzeit 			throw file->InitCheck();
25325767509Smahlzeit 		}
25425767509Smahlzeit 
25549fa1748Smahlzeit 		char fourcc[4];
25649fa1748Smahlzeit 		ReadFourCC(fourcc);
25749fa1748Smahlzeit 		if (strncmp(fourcc, "MThd", 4) != 0)
258eac71506Sjerl1 		{
25949fa1748Smahlzeit 			throw (status_t) B_BAD_MIDI_DATA;
260eac71506Sjerl1 		}
26125767509Smahlzeit 
26249fa1748Smahlzeit 		if (Read32Bit() != 6)
263eac71506Sjerl1 		{
26449fa1748Smahlzeit 			throw (status_t) B_BAD_MIDI_DATA;
265eac71506Sjerl1 		}
266eac71506Sjerl1 
26749fa1748Smahlzeit 		format = Read16Bit();
26849fa1748Smahlzeit 		numTracks = Read16Bit();
26949fa1748Smahlzeit 		ticksPerBeat = Read16Bit();
27049fa1748Smahlzeit 
27149fa1748Smahlzeit 		if (ticksPerBeat & 0x8000)  // we don't support SMPTE
27249fa1748Smahlzeit 		{                           // time codes, only ticks
27349fa1748Smahlzeit 			ticksPerBeat = 240;     // per quarter note
274eac71506Sjerl1 		}
275eac71506Sjerl1 
27649fa1748Smahlzeit 		currTrack = 0;
27749fa1748Smahlzeit 		while (currTrack < numTracks)
2786ba60405Smahlzeit 		{
27949fa1748Smahlzeit 			ReadChunk();
28049fa1748Smahlzeit 		}
28149fa1748Smahlzeit 	}
28249fa1748Smahlzeit 	catch (status_t e)
28349fa1748Smahlzeit 	{
28449fa1748Smahlzeit 		delete file;
28549fa1748Smahlzeit 		file = NULL;
28649fa1748Smahlzeit 		return e;
287eac71506Sjerl1 	}
288eac71506Sjerl1 
289eac71506Sjerl1 	SortEvents(true);
290eac71506Sjerl1 
29149fa1748Smahlzeit 	delete file;
29249fa1748Smahlzeit 	file = NULL;
2936ba60405Smahlzeit 	return B_OK;
2946ba60405Smahlzeit }
2956ba60405Smahlzeit 
29649fa1748Smahlzeit //------------------------------------------------------------------------------
29749fa1748Smahlzeit 
29849fa1748Smahlzeit status_t BMidiStore::Export(const entry_ref* ref, int32 format)
29949fa1748Smahlzeit {
30049fa1748Smahlzeit 	try
30149fa1748Smahlzeit 	{
30249fa1748Smahlzeit 		file = new BFile(ref, B_READ_WRITE);
30349fa1748Smahlzeit 		if (file->InitCheck() != B_OK)
30449fa1748Smahlzeit 		{
30549fa1748Smahlzeit 			throw file->InitCheck();
30649fa1748Smahlzeit 		}
30749fa1748Smahlzeit 
30849fa1748Smahlzeit 		SortEvents(true);
30949fa1748Smahlzeit 
31049fa1748Smahlzeit 		WriteFourCC('M', 'T', 'h', 'd');
31149fa1748Smahlzeit 		Write32Bit(6);
31249fa1748Smahlzeit 		Write16Bit(0);  // we do only format 0
31349fa1748Smahlzeit 		Write16Bit(1);
31449fa1748Smahlzeit 		Write16Bit(ticksPerBeat);
31549fa1748Smahlzeit 
31649fa1748Smahlzeit 		WriteTrack();
31749fa1748Smahlzeit 	}
31849fa1748Smahlzeit 	catch (status_t e)
31949fa1748Smahlzeit 	{
32049fa1748Smahlzeit 		delete file;
32149fa1748Smahlzeit 		file = NULL;
32249fa1748Smahlzeit 		return e;
32349fa1748Smahlzeit 	}
32449fa1748Smahlzeit 
32549fa1748Smahlzeit 	delete file;
32649fa1748Smahlzeit 	file = NULL;
32749fa1748Smahlzeit 	return B_OK;
32849fa1748Smahlzeit }
32949fa1748Smahlzeit 
33049fa1748Smahlzeit //------------------------------------------------------------------------------
3316ba60405Smahlzeit 
3326ba60405Smahlzeit void BMidiStore::SortEvents(bool force)
3336ba60405Smahlzeit {
33449fa1748Smahlzeit 	if (force || needsSorting)
33549fa1748Smahlzeit 	{
33649fa1748Smahlzeit 		events->SortItems(compare_events);
33749fa1748Smahlzeit 		needsSorting = false;
33849fa1748Smahlzeit 	}
3396ba60405Smahlzeit }
3406ba60405Smahlzeit 
34149fa1748Smahlzeit //------------------------------------------------------------------------------
3426ba60405Smahlzeit 
3436ba60405Smahlzeit uint32 BMidiStore::CountEvents() const
34449fa1748Smahlzeit {
3456ba60405Smahlzeit 	return events->CountItems();
3466ba60405Smahlzeit }
3476ba60405Smahlzeit 
34849fa1748Smahlzeit //------------------------------------------------------------------------------
3496ba60405Smahlzeit 
3506ba60405Smahlzeit uint32 BMidiStore::CurrentEvent() const
35149fa1748Smahlzeit {
35249fa1748Smahlzeit 	return currentEvent;
3536ba60405Smahlzeit }
3546ba60405Smahlzeit 
35549fa1748Smahlzeit //------------------------------------------------------------------------------
3566ba60405Smahlzeit 
35749fa1748Smahlzeit void BMidiStore::SetCurrentEvent(uint32 eventNumber)
35849fa1748Smahlzeit {
35949fa1748Smahlzeit 	currentEvent = eventNumber;
3606ba60405Smahlzeit }
3616ba60405Smahlzeit 
36249fa1748Smahlzeit //------------------------------------------------------------------------------
3636ba60405Smahlzeit 
36449fa1748Smahlzeit uint32 BMidiStore::DeltaOfEvent(uint32 eventNumber) const
36549fa1748Smahlzeit {
36649fa1748Smahlzeit 	// Even though the BeBook says that the delta is the time span between
36749fa1748Smahlzeit 	// an event and the first event in the list, this doesn't appear to be
36849fa1748Smahlzeit 	// true for events that were captured from other BMidi objects such as
36949fa1748Smahlzeit 	// BMidiPort. For those events, we return the absolute timestamp. The
37049fa1748Smahlzeit 	// BeBook is correct for events from MIDI files, though.
37149fa1748Smahlzeit 
37249fa1748Smahlzeit 	BMidiEvent* event = EventAt(eventNumber);
37349fa1748Smahlzeit 	if (event != NULL)
37449fa1748Smahlzeit 	{
37549fa1748Smahlzeit 		return GetEventTime(event);
37649fa1748Smahlzeit 	}
37749fa1748Smahlzeit 
3786ba60405Smahlzeit 	return 0;
3796ba60405Smahlzeit }
3806ba60405Smahlzeit 
38149fa1748Smahlzeit //------------------------------------------------------------------------------
382eac71506Sjerl1 
383eac71506Sjerl1 uint32 BMidiStore::EventAtDelta(uint32 time) const
38449fa1748Smahlzeit {
38549fa1748Smahlzeit 	for (int32 t = 0; t < events->CountItems(); ++t)
38649fa1748Smahlzeit 	{
38749fa1748Smahlzeit 		if (GetEventTime(EventAt(t)) >= time) { return t; }
38849fa1748Smahlzeit 	}
38949fa1748Smahlzeit 
390eac71506Sjerl1 	return 0;
391eac71506Sjerl1 }
392eac71506Sjerl1 
39349fa1748Smahlzeit //------------------------------------------------------------------------------
3946ba60405Smahlzeit 
3956ba60405Smahlzeit uint32 BMidiStore::BeginTime() const
39649fa1748Smahlzeit {
39749fa1748Smahlzeit 	return startTime;
3986ba60405Smahlzeit }
3996ba60405Smahlzeit 
40049fa1748Smahlzeit //------------------------------------------------------------------------------
4016ba60405Smahlzeit 
40249fa1748Smahlzeit void BMidiStore::SetTempo(int32 beatsPerMinute_)
40349fa1748Smahlzeit {
40449fa1748Smahlzeit 	beatsPerMinute = beatsPerMinute_;
4056ba60405Smahlzeit }
4066ba60405Smahlzeit 
40749fa1748Smahlzeit //------------------------------------------------------------------------------
4086ba60405Smahlzeit 
4096ba60405Smahlzeit int32 BMidiStore::Tempo() const
41049fa1748Smahlzeit {
41149fa1748Smahlzeit 	return beatsPerMinute;
4126ba60405Smahlzeit }
4136ba60405Smahlzeit 
41449fa1748Smahlzeit //------------------------------------------------------------------------------
41549fa1748Smahlzeit 
41649fa1748Smahlzeit void BMidiStore::_ReservedMidiStore1() { }
41749fa1748Smahlzeit void BMidiStore::_ReservedMidiStore2() { }
41849fa1748Smahlzeit void BMidiStore::_ReservedMidiStore3() { }
41949fa1748Smahlzeit 
42049fa1748Smahlzeit //------------------------------------------------------------------------------
4216ba60405Smahlzeit 
4226ba60405Smahlzeit void BMidiStore::Run()
4236ba60405Smahlzeit {
42449fa1748Smahlzeit 	int32 timeAdjust;
42549fa1748Smahlzeit 	bool firstEvent = true;
42649fa1748Smahlzeit 
4276ba60405Smahlzeit 	while (KeepRunning())
4286ba60405Smahlzeit 	{
42949fa1748Smahlzeit 		BMidiEvent* event = EventAt(currentEvent);
43049fa1748Smahlzeit 		if (event == NULL) { return; }
4316ba60405Smahlzeit 
43249fa1748Smahlzeit 		if (firstEvent)
4336ba60405Smahlzeit 		{
43449fa1748Smahlzeit 			startTime = B_NOW;
43549fa1748Smahlzeit 			timeAdjust = startTime - GetEventTime(event);
43649fa1748Smahlzeit 			SprayEvent(event, startTime);
43749fa1748Smahlzeit 			firstEvent = false;
438eac71506Sjerl1 		}
43949fa1748Smahlzeit 		else
440eac71506Sjerl1 		{
44149fa1748Smahlzeit 			SprayEvent(event, GetEventTime(event) + timeAdjust);
4426ba60405Smahlzeit 		}
4436ba60405Smahlzeit 
44449fa1748Smahlzeit 		++currentEvent;
44549fa1748Smahlzeit 	}
4466ba60405Smahlzeit }
4476ba60405Smahlzeit 
44849fa1748Smahlzeit //------------------------------------------------------------------------------
4496ba60405Smahlzeit 
45049fa1748Smahlzeit void BMidiStore::AddEvent(BMidiEvent* event)
45125767509Smahlzeit {
45249fa1748Smahlzeit 	events->AddItem(event);
45349fa1748Smahlzeit 	needsSorting = true;
45425767509Smahlzeit }
45525767509Smahlzeit 
45649fa1748Smahlzeit //------------------------------------------------------------------------------
45749fa1748Smahlzeit 
45849fa1748Smahlzeit void BMidiStore::SprayEvent(const BMidiEvent* event, uint32 time)
45925767509Smahlzeit {
46049fa1748Smahlzeit 	uchar byte1 = event->byte1;
46149fa1748Smahlzeit 	uchar byte2 = event->byte2;
46249fa1748Smahlzeit 	uchar byte3 = event->byte3;
46325767509Smahlzeit 
46449fa1748Smahlzeit 	switch (byte1 & 0xF0)
465eac71506Sjerl1 	{
466eac71506Sjerl1 		case B_NOTE_OFF:
46749fa1748Smahlzeit 			SprayNoteOff((byte1 & 0x0F) + 1, byte2, byte3, time);
46849fa1748Smahlzeit 			return;
46949fa1748Smahlzeit 
470eac71506Sjerl1 		case B_NOTE_ON:
47149fa1748Smahlzeit 			SprayNoteOn((byte1 & 0x0F) + 1, byte2, byte3, time);
47249fa1748Smahlzeit 			return;
47349fa1748Smahlzeit 
474eac71506Sjerl1 		case B_KEY_PRESSURE:
47549fa1748Smahlzeit 			SprayKeyPressure((byte1 & 0x0F) + 1, byte2, byte3, time);
47649fa1748Smahlzeit 			return;
47749fa1748Smahlzeit 
478eac71506Sjerl1 		case B_CONTROL_CHANGE:
47949fa1748Smahlzeit 			SprayControlChange((byte1 & 0x0F) + 1, byte2, byte3, time);
48049fa1748Smahlzeit 			return;
48149fa1748Smahlzeit 
482eac71506Sjerl1 		case B_PROGRAM_CHANGE:
48349fa1748Smahlzeit 			SprayProgramChange((byte1 & 0x0F) + 1, byte2, time);
48449fa1748Smahlzeit 			return;
48549fa1748Smahlzeit 
486eac71506Sjerl1 		case B_CHANNEL_PRESSURE:
48749fa1748Smahlzeit 			SprayChannelPressure((byte1 & 0x0F) + 1, byte2, time);
48849fa1748Smahlzeit 			return;
48949fa1748Smahlzeit 
49049fa1748Smahlzeit 		case B_PITCH_BEND:
49149fa1748Smahlzeit 			SprayPitchBend((byte1 & 0x0F) + 1, byte2, byte3, time);
49249fa1748Smahlzeit 			return;
49349fa1748Smahlzeit 
49449fa1748Smahlzeit 		case 0xF0:
49549fa1748Smahlzeit 			switch (byte1)
496eac71506Sjerl1 			{
497eac71506Sjerl1 				case B_SYS_EX_START:
49849fa1748Smahlzeit 					SpraySystemExclusive(event->data, event->length, time);
49949fa1748Smahlzeit 					return;
50049fa1748Smahlzeit 
501eac71506Sjerl1 				case B_MIDI_TIME_CODE:
50249fa1748Smahlzeit 				case B_SONG_POSITION:
503eac71506Sjerl1 				case B_SONG_SELECT:
50449fa1748Smahlzeit 				case B_CABLE_MESSAGE:
505eac71506Sjerl1 				case B_TUNE_REQUEST:
50649fa1748Smahlzeit 				case B_SYS_EX_END:
50749fa1748Smahlzeit 					SpraySystemCommon(byte1, byte2, byte3, time);
50849fa1748Smahlzeit 					return;
50949fa1748Smahlzeit 
510eac71506Sjerl1 				case B_TIMING_CLOCK:
511eac71506Sjerl1 				case B_START:
512eac71506Sjerl1 				case B_CONTINUE:
513eac71506Sjerl1 				case B_STOP:
514eac71506Sjerl1 				case B_ACTIVE_SENSING:
51549fa1748Smahlzeit 					SpraySystemRealTime(byte1, time);
51649fa1748Smahlzeit 					return;
51749fa1748Smahlzeit 
518eac71506Sjerl1 				case B_SYSTEM_RESET:
51949fa1748Smahlzeit 					if ((byte2 == 0x51) && (byte3 == 0x03))
52049fa1748Smahlzeit 					{
52149fa1748Smahlzeit 						SprayTempoChange(event->tempo, time);
52249fa1748Smahlzeit 						beatsPerMinute = event->tempo;
523eac71506Sjerl1 					}
524eac71506Sjerl1 					else
525eac71506Sjerl1 					{
52649fa1748Smahlzeit 						SpraySystemRealTime(byte1, time);
527eac71506Sjerl1 					}
52849fa1748Smahlzeit 					return;
529eac71506Sjerl1 			}
53049fa1748Smahlzeit 			return;
531eac71506Sjerl1 	}
532eac71506Sjerl1 }
533eac71506Sjerl1 
53449fa1748Smahlzeit //------------------------------------------------------------------------------
535eac71506Sjerl1 
53649fa1748Smahlzeit BMidiEvent* BMidiStore::EventAt(int32 index) const
537eac71506Sjerl1 {
53849fa1748Smahlzeit 	return (BMidiEvent*) events->ItemAt(index);
539eac71506Sjerl1 }
540eac71506Sjerl1 
54149fa1748Smahlzeit //------------------------------------------------------------------------------
542eac71506Sjerl1 
54349fa1748Smahlzeit uint32 BMidiStore::GetEventTime(const BMidiEvent* event) const
544eac71506Sjerl1 {
54549fa1748Smahlzeit 	if (event->ticks)
54649fa1748Smahlzeit 	{
54749fa1748Smahlzeit 		return TicksToMilliseconds(event->time);
54849fa1748Smahlzeit 	}
549eac71506Sjerl1 	else
550eac71506Sjerl1 	{
55149fa1748Smahlzeit 		return event->time;
55249fa1748Smahlzeit 	}
55349fa1748Smahlzeit }
55449fa1748Smahlzeit 
55549fa1748Smahlzeit //------------------------------------------------------------------------------
55649fa1748Smahlzeit 
55749fa1748Smahlzeit uint32 BMidiStore::TicksToMilliseconds(uint32 ticks) const
558eac71506Sjerl1 {
55949fa1748Smahlzeit 	return ((uint64) ticks * 60000) / (beatsPerMinute * ticksPerBeat);
56049fa1748Smahlzeit }
56149fa1748Smahlzeit 
56249fa1748Smahlzeit //------------------------------------------------------------------------------
56349fa1748Smahlzeit 
56449fa1748Smahlzeit uint32 BMidiStore::MillisecondsToTicks(uint32 ms) const
565eac71506Sjerl1 {
56649fa1748Smahlzeit 	return ((uint64) ms * beatsPerMinute * ticksPerBeat) / 60000;
567eac71506Sjerl1 }
56849fa1748Smahlzeit 
56949fa1748Smahlzeit //------------------------------------------------------------------------------
57049fa1748Smahlzeit 
57149fa1748Smahlzeit void BMidiStore::ReadFourCC(char* fourcc)
572eac71506Sjerl1 {
57349fa1748Smahlzeit 	if (file->Read(fourcc, 4) != 4)
574eac71506Sjerl1 	{
57549fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
576eac71506Sjerl1 	}
57749fa1748Smahlzeit }
57849fa1748Smahlzeit 
57949fa1748Smahlzeit //------------------------------------------------------------------------------
58049fa1748Smahlzeit 
58149fa1748Smahlzeit void BMidiStore::WriteFourCC(char a, char b, char c, char d)
582eac71506Sjerl1 {
58349fa1748Smahlzeit 	char fourcc[4] = { a, b, c, d };
58449fa1748Smahlzeit 	if (file->Write(fourcc, 4) != 4)
585eac71506Sjerl1 	{
58649fa1748Smahlzeit 		throw (status_t) B_ERROR;
587eac71506Sjerl1 	}
58849fa1748Smahlzeit }
58949fa1748Smahlzeit 
59049fa1748Smahlzeit //------------------------------------------------------------------------------
59149fa1748Smahlzeit 
59249fa1748Smahlzeit uint32 BMidiStore::Read32Bit()
593eac71506Sjerl1 {
59449fa1748Smahlzeit 	uint8 buf[4];
59549fa1748Smahlzeit 	if (file->Read(buf, 4) != 4)
596eac71506Sjerl1 	{
59749fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
598eac71506Sjerl1 	}
59949fa1748Smahlzeit 
60049fa1748Smahlzeit 	return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
60149fa1748Smahlzeit }
60249fa1748Smahlzeit 
60349fa1748Smahlzeit //------------------------------------------------------------------------------
60449fa1748Smahlzeit 
60549fa1748Smahlzeit void BMidiStore::Write32Bit(uint32 val)
606eac71506Sjerl1 {
60749fa1748Smahlzeit 	uint8 buf[4];
60849fa1748Smahlzeit 	buf[0] = (val >> 24) & 0xFF;
60949fa1748Smahlzeit 	buf[1] = (val >> 16) & 0xFF;
61049fa1748Smahlzeit 	buf[2] = (val >>  8) & 0xFF;
61149fa1748Smahlzeit 	buf[3] =  val        & 0xFF;
61249fa1748Smahlzeit 
61349fa1748Smahlzeit 	if (file->Write(buf, 4) != 4)
614eac71506Sjerl1 	{
61549fa1748Smahlzeit 		throw (status_t) B_ERROR;
616eac71506Sjerl1 	}
61749fa1748Smahlzeit }
61849fa1748Smahlzeit 
61949fa1748Smahlzeit //------------------------------------------------------------------------------
62049fa1748Smahlzeit 
62149fa1748Smahlzeit uint16 BMidiStore::Read16Bit()
622eac71506Sjerl1 {
62349fa1748Smahlzeit 	uint8 buf[2];
62449fa1748Smahlzeit 	if (file->Read(buf, 2) != 2)
625eac71506Sjerl1 	{
62649fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
627eac71506Sjerl1 	}
628eac71506Sjerl1 
62949fa1748Smahlzeit 	return (buf[0] << 8) | buf[1];
630eac71506Sjerl1 }
631eac71506Sjerl1 
63249fa1748Smahlzeit //------------------------------------------------------------------------------
633eac71506Sjerl1 
63449fa1748Smahlzeit void BMidiStore::Write16Bit(uint16 val)
635eac71506Sjerl1 {
63649fa1748Smahlzeit 	uint8 buf[2];
63749fa1748Smahlzeit 	buf[0] = (val >>  8) & 0xFF;
63849fa1748Smahlzeit 	buf[1] =  val        & 0xFF;
639eac71506Sjerl1 
64049fa1748Smahlzeit 	if (file->Write(buf, 2) != 2)
641eac71506Sjerl1 	{
64249fa1748Smahlzeit 		throw (status_t) B_ERROR;
643eac71506Sjerl1 	}
644eac71506Sjerl1 }
645eac71506Sjerl1 
64649fa1748Smahlzeit //------------------------------------------------------------------------------
647eac71506Sjerl1 
64849fa1748Smahlzeit uint8 BMidiStore::PeekByte()
649eac71506Sjerl1 {
65049fa1748Smahlzeit 	uint8 buf;
65149fa1748Smahlzeit 	if (file->Read(&buf, 1) != 1)
65249fa1748Smahlzeit 	{
65349fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
654eac71506Sjerl1 	}
655eac71506Sjerl1 
65649fa1748Smahlzeit 	if (file->Seek(-1, SEEK_CUR) < 0)
65749fa1748Smahlzeit 	{
65849fa1748Smahlzeit 		throw (status_t) B_ERROR;
659eac71506Sjerl1 	}
660eac71506Sjerl1 
66149fa1748Smahlzeit 	return buf;
66249fa1748Smahlzeit }
663eac71506Sjerl1 
66449fa1748Smahlzeit //------------------------------------------------------------------------------
66549fa1748Smahlzeit 
66649fa1748Smahlzeit uint8 BMidiStore::NextByte()
66749fa1748Smahlzeit {
66849fa1748Smahlzeit 	uint8 buf;
66949fa1748Smahlzeit 	if (file->Read(&buf, 1) != 1)
67049fa1748Smahlzeit 	{
67149fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
67249fa1748Smahlzeit 	}
67349fa1748Smahlzeit 
67449fa1748Smahlzeit 	--byteCount;
67549fa1748Smahlzeit 	return buf;
67649fa1748Smahlzeit }
67749fa1748Smahlzeit 
67849fa1748Smahlzeit //------------------------------------------------------------------------------
67949fa1748Smahlzeit 
68049fa1748Smahlzeit void BMidiStore::WriteByte(uint8 val)
68149fa1748Smahlzeit {
68249fa1748Smahlzeit 	if (file->Write(&val, 1) != 1)
68349fa1748Smahlzeit 	{
68449fa1748Smahlzeit 		throw (status_t) B_ERROR;
68549fa1748Smahlzeit 	}
68649fa1748Smahlzeit 
68749fa1748Smahlzeit 	++byteCount;
68849fa1748Smahlzeit }
68949fa1748Smahlzeit 
69049fa1748Smahlzeit //------------------------------------------------------------------------------
69149fa1748Smahlzeit 
69249fa1748Smahlzeit void BMidiStore::SkipBytes(uint32 length)
69349fa1748Smahlzeit {
69449fa1748Smahlzeit 	if (file->Seek(length, SEEK_CUR) < 0)
69549fa1748Smahlzeit 	{
69649fa1748Smahlzeit 		throw (status_t) B_BAD_MIDI_DATA;
69749fa1748Smahlzeit 	}
69849fa1748Smahlzeit 
69949fa1748Smahlzeit 	byteCount -= length;
70049fa1748Smahlzeit }
70149fa1748Smahlzeit 
70249fa1748Smahlzeit //------------------------------------------------------------------------------
70349fa1748Smahlzeit 
70449fa1748Smahlzeit uint32 BMidiStore::ReadVarLength()
70549fa1748Smahlzeit {
70649fa1748Smahlzeit 	uint32 val;
70749fa1748Smahlzeit 	uint8 byte;
70849fa1748Smahlzeit 
70949fa1748Smahlzeit 	if ((val = NextByte()) & 0x80)
71049fa1748Smahlzeit 	{
71149fa1748Smahlzeit 		val &= 0x7F;
71249fa1748Smahlzeit 		do
71349fa1748Smahlzeit 		{
71449fa1748Smahlzeit 			val = (val << 7) + ((byte = NextByte()) & 0x7F);
71549fa1748Smahlzeit 		}
71649fa1748Smahlzeit 		while (byte & 0x80);
71749fa1748Smahlzeit 	}
71849fa1748Smahlzeit 
71949fa1748Smahlzeit 	return val;
72049fa1748Smahlzeit }
72149fa1748Smahlzeit 
72249fa1748Smahlzeit //------------------------------------------------------------------------------
72349fa1748Smahlzeit 
72449fa1748Smahlzeit void BMidiStore::WriteVarLength(uint32 val)
72549fa1748Smahlzeit {
72649fa1748Smahlzeit 	uint32 buffer = val & 0x7F;
72749fa1748Smahlzeit 
72849fa1748Smahlzeit 	while ((val >>= 7))
729eac71506Sjerl1 	{
730eac71506Sjerl1 		buffer <<= 8;
73149fa1748Smahlzeit 		buffer |= ((val & 0x7F) | 0x80);
732eac71506Sjerl1 	}
73349fa1748Smahlzeit 
734eac71506Sjerl1 	while (true)
735eac71506Sjerl1 	{
73649fa1748Smahlzeit 		WriteByte(buffer);
737eac71506Sjerl1 		if (buffer & 0x80)
738eac71506Sjerl1 			buffer >>= 8;
739eac71506Sjerl1 		else
740eac71506Sjerl1 			break;
74125767509Smahlzeit 	}
74225767509Smahlzeit }
74325767509Smahlzeit 
74449fa1748Smahlzeit //------------------------------------------------------------------------------
74525767509Smahlzeit 
74649fa1748Smahlzeit void BMidiStore::ReadChunk()
747eac71506Sjerl1 {
74849fa1748Smahlzeit 	char fourcc[4];
74949fa1748Smahlzeit 	ReadFourCC(fourcc);
750eac71506Sjerl1 
75149fa1748Smahlzeit 	byteCount = Read32Bit();
752eac71506Sjerl1 
75349fa1748Smahlzeit 	if (strncmp(fourcc, "MTrk", 4) == 0)
754eac71506Sjerl1 	{
75549fa1748Smahlzeit 		ReadTrack();
756eac71506Sjerl1 	}
75749fa1748Smahlzeit 	else
758eac71506Sjerl1 	{
75949fa1748Smahlzeit 		TRACE(("Skipping '%c%c%c%c' chunk (%lu bytes)",
76049fa1748Smahlzeit 			fourcc[0], fourcc[1], fourcc[2], fourcc[3], byteCount))
76149fa1748Smahlzeit 
76249fa1748Smahlzeit 		SkipBytes(byteCount);
76349fa1748Smahlzeit 	}
764eac71506Sjerl1 }
765eac71506Sjerl1 
76649fa1748Smahlzeit //------------------------------------------------------------------------------
76749fa1748Smahlzeit 
76849fa1748Smahlzeit void BMidiStore::ReadTrack()
76949fa1748Smahlzeit {
77049fa1748Smahlzeit 	uint8 status = 0;
77149fa1748Smahlzeit 	uint8 data1;
77249fa1748Smahlzeit 	uint8 data2;
77349fa1748Smahlzeit 	BMidiEvent* event;
77449fa1748Smahlzeit 
77549fa1748Smahlzeit 	totalTicks = 0;
77649fa1748Smahlzeit 
77749fa1748Smahlzeit 	while (byteCount > 0)
77849fa1748Smahlzeit 	{
77949fa1748Smahlzeit 		uint32 ticks = ReadVarLength();
78049fa1748Smahlzeit 		totalTicks += ticks;
78149fa1748Smahlzeit 
78249fa1748Smahlzeit 		if (PeekByte() & 0x80)
78349fa1748Smahlzeit 		{
78449fa1748Smahlzeit 			status = NextByte();
78549fa1748Smahlzeit 		}
78649fa1748Smahlzeit 
78749fa1748Smahlzeit 		switch (status & 0xF0)
78849fa1748Smahlzeit 		{
78949fa1748Smahlzeit 			case B_NOTE_OFF:
79049fa1748Smahlzeit 			case B_NOTE_ON:
79149fa1748Smahlzeit 			case B_KEY_PRESSURE:
79249fa1748Smahlzeit 			case B_CONTROL_CHANGE:
79349fa1748Smahlzeit 			case B_PITCH_BEND:
79449fa1748Smahlzeit 				data1 = NextByte();
79549fa1748Smahlzeit 				data2 = NextByte();
79649fa1748Smahlzeit 				event = new BMidiEvent;
79749fa1748Smahlzeit 				event->time  = totalTicks;
79849fa1748Smahlzeit 				event->ticks = true;
79949fa1748Smahlzeit 				event->byte1 = status;
80049fa1748Smahlzeit 				event->byte2 = data1;
80149fa1748Smahlzeit 				event->byte3 = data2;
80249fa1748Smahlzeit 				AddEvent(event);
80349fa1748Smahlzeit 				break;
80449fa1748Smahlzeit 
80549fa1748Smahlzeit 			case B_PROGRAM_CHANGE:
80649fa1748Smahlzeit 			case B_CHANNEL_PRESSURE:
80749fa1748Smahlzeit 				data1 = NextByte();
80849fa1748Smahlzeit 				event = new BMidiEvent;
80949fa1748Smahlzeit 				event->time  = totalTicks;
81049fa1748Smahlzeit 				event->ticks = true;
81149fa1748Smahlzeit 				event->byte1 = status;
81249fa1748Smahlzeit 				event->byte2 = data1;
81349fa1748Smahlzeit 				AddEvent(event);
81449fa1748Smahlzeit 				break;
81549fa1748Smahlzeit 
81649fa1748Smahlzeit 			case 0xF0:
81749fa1748Smahlzeit 				switch (status)
81849fa1748Smahlzeit 				{
81949fa1748Smahlzeit 					case B_SYS_EX_START:
82049fa1748Smahlzeit 						ReadSystemExclusive();
82149fa1748Smahlzeit 						break;
82249fa1748Smahlzeit 
82349fa1748Smahlzeit 					case B_TUNE_REQUEST:
82449fa1748Smahlzeit 					case B_SYS_EX_END:
82549fa1748Smahlzeit 					case B_TIMING_CLOCK:
82649fa1748Smahlzeit 					case B_START:
82749fa1748Smahlzeit 					case B_CONTINUE:
82849fa1748Smahlzeit 					case B_STOP:
82949fa1748Smahlzeit 					case B_ACTIVE_SENSING:
83049fa1748Smahlzeit 						event = new BMidiEvent;
83149fa1748Smahlzeit 						event->time  = totalTicks;
83249fa1748Smahlzeit 						event->ticks = true;
83349fa1748Smahlzeit 						event->byte1 = status;
83449fa1748Smahlzeit 						AddEvent(event);
83549fa1748Smahlzeit 						break;
83649fa1748Smahlzeit 
83749fa1748Smahlzeit 					case B_MIDI_TIME_CODE:
83849fa1748Smahlzeit 					case B_SONG_SELECT:
83949fa1748Smahlzeit 					case B_CABLE_MESSAGE:
84049fa1748Smahlzeit 						data1 = NextByte();
84149fa1748Smahlzeit 						event = new BMidiEvent;
84249fa1748Smahlzeit 						event->time  = totalTicks;
84349fa1748Smahlzeit 						event->ticks = true;
84449fa1748Smahlzeit 						event->byte1 = status;
84549fa1748Smahlzeit 						event->byte2 = data1;
84649fa1748Smahlzeit 						AddEvent(event);
84749fa1748Smahlzeit 						break;
84849fa1748Smahlzeit 
84949fa1748Smahlzeit 					case B_SONG_POSITION:
85049fa1748Smahlzeit 						data1 = NextByte();
85149fa1748Smahlzeit 						data2 = NextByte();
85249fa1748Smahlzeit 						event = new BMidiEvent;
85349fa1748Smahlzeit 						event->time  = totalTicks;
85449fa1748Smahlzeit 						event->ticks = true;
85549fa1748Smahlzeit 						event->byte1 = status;
85649fa1748Smahlzeit 						event->byte2 = data1;
85749fa1748Smahlzeit 						event->byte3 = data2;
85849fa1748Smahlzeit 						AddEvent(event);
85949fa1748Smahlzeit 						break;
86049fa1748Smahlzeit 
86149fa1748Smahlzeit 					case B_SYSTEM_RESET:
86249fa1748Smahlzeit 						ReadMetaEvent();
86349fa1748Smahlzeit 						break;
86449fa1748Smahlzeit 				}
86549fa1748Smahlzeit 				break;
86649fa1748Smahlzeit 		}
86749fa1748Smahlzeit 
86849fa1748Smahlzeit 		event = NULL;
86949fa1748Smahlzeit 	}
87049fa1748Smahlzeit 
87149fa1748Smahlzeit 	++currTrack;
87249fa1748Smahlzeit }
87349fa1748Smahlzeit 
87449fa1748Smahlzeit //------------------------------------------------------------------------------
87549fa1748Smahlzeit 
87649fa1748Smahlzeit void BMidiStore::ReadSystemExclusive()
87749fa1748Smahlzeit {
87849fa1748Smahlzeit 	// We do not import sysex's from MIDI files.
87949fa1748Smahlzeit 
88049fa1748Smahlzeit 	SkipBytes(ReadVarLength());
88149fa1748Smahlzeit }
88249fa1748Smahlzeit 
88349fa1748Smahlzeit //------------------------------------------------------------------------------
88449fa1748Smahlzeit 
88549fa1748Smahlzeit void BMidiStore::ReadMetaEvent()
88649fa1748Smahlzeit {
88749fa1748Smahlzeit 	// We only import the Tempo Change meta event.
88849fa1748Smahlzeit 
88949fa1748Smahlzeit 	uint8 type = NextByte();
89049fa1748Smahlzeit 	uint32 length = ReadVarLength();
89149fa1748Smahlzeit 
89249fa1748Smahlzeit 	if ((type == 0x51) && (length == 3))
89349fa1748Smahlzeit 	{
89449fa1748Smahlzeit 		uchar data[3];
89549fa1748Smahlzeit 		data[0] = NextByte();
89649fa1748Smahlzeit 		data[1] = NextByte();
89749fa1748Smahlzeit 		data[2] = NextByte();
89849fa1748Smahlzeit 		uint32 val = (data[0] << 16) | (data[1] << 8) | data[2];
89949fa1748Smahlzeit 
90049fa1748Smahlzeit 		BMidiEvent* event = new BMidiEvent;
90149fa1748Smahlzeit 		event->time  = totalTicks;
90249fa1748Smahlzeit 		event->ticks = true;
90349fa1748Smahlzeit 		event->byte1 = 0xFF;
90449fa1748Smahlzeit 		event->byte2 = 0x51;
90549fa1748Smahlzeit 		event->byte3 = 0x03;
90649fa1748Smahlzeit 		event->tempo = 60000000 / val;
90749fa1748Smahlzeit 		AddEvent(event);
90849fa1748Smahlzeit 	}
90949fa1748Smahlzeit 	else
91049fa1748Smahlzeit 	{
91149fa1748Smahlzeit 		SkipBytes(length);
91249fa1748Smahlzeit 	}
91349fa1748Smahlzeit }
91449fa1748Smahlzeit 
91549fa1748Smahlzeit //------------------------------------------------------------------------------
91649fa1748Smahlzeit 
91749fa1748Smahlzeit void BMidiStore::WriteTrack()
91849fa1748Smahlzeit {
91949fa1748Smahlzeit 	WriteFourCC('M', 'T', 'r', 'k');
92049fa1748Smahlzeit 	off_t lengthPos = file->Position();
92149fa1748Smahlzeit 	Write32Bit(0);
92249fa1748Smahlzeit 
92349fa1748Smahlzeit 	byteCount = 0;
92449fa1748Smahlzeit 	uint32 oldTime;
92549fa1748Smahlzeit 	uint32 newTime;
92649fa1748Smahlzeit 
927*9138338cSmahlzeit 	for (uint32 t = 0; t < CountEvents(); ++t)
92849fa1748Smahlzeit 	{
92949fa1748Smahlzeit 		BMidiEvent* event = EventAt(t);
93049fa1748Smahlzeit 
93149fa1748Smahlzeit 		if (event->ticks)
93249fa1748Smahlzeit 		{
93349fa1748Smahlzeit 			newTime = event->time;
93449fa1748Smahlzeit 		}
93549fa1748Smahlzeit 		else
93649fa1748Smahlzeit 		{
93749fa1748Smahlzeit 			newTime = MillisecondsToTicks(event->time);
93849fa1748Smahlzeit 		}
93949fa1748Smahlzeit 
94049fa1748Smahlzeit 		if (t == 0)
94149fa1748Smahlzeit 		{
94249fa1748Smahlzeit 			WriteVarLength(0);
94349fa1748Smahlzeit 		}
94449fa1748Smahlzeit 		else
94549fa1748Smahlzeit 		{
94649fa1748Smahlzeit 			WriteVarLength(newTime - oldTime);
94749fa1748Smahlzeit 		}
94849fa1748Smahlzeit 
94949fa1748Smahlzeit 		oldTime = newTime;
95049fa1748Smahlzeit 
95149fa1748Smahlzeit 		switch (event->byte1 & 0xF0)
95249fa1748Smahlzeit 		{
95349fa1748Smahlzeit 			case B_NOTE_OFF:
95449fa1748Smahlzeit 			case B_NOTE_ON:
95549fa1748Smahlzeit 			case B_KEY_PRESSURE:
95649fa1748Smahlzeit 			case B_CONTROL_CHANGE:
95749fa1748Smahlzeit 			case B_PITCH_BEND:
95849fa1748Smahlzeit 				WriteByte(event->byte1);
95949fa1748Smahlzeit 				WriteByte(event->byte2);
96049fa1748Smahlzeit 				WriteByte(event->byte3);
96149fa1748Smahlzeit 				break;
96249fa1748Smahlzeit 
96349fa1748Smahlzeit 			case B_PROGRAM_CHANGE:
96449fa1748Smahlzeit 			case B_CHANNEL_PRESSURE:
96549fa1748Smahlzeit 				WriteByte(event->byte1);
96649fa1748Smahlzeit 				WriteByte(event->byte2);
96749fa1748Smahlzeit 				break;
96849fa1748Smahlzeit 
96949fa1748Smahlzeit 			case 0xF0:
97049fa1748Smahlzeit 				switch (event->byte1)
97149fa1748Smahlzeit 				{
97249fa1748Smahlzeit 					case B_SYS_EX_START:
97349fa1748Smahlzeit 						// We do not export sysex's.
97449fa1748Smahlzeit 						break;
97549fa1748Smahlzeit 
97649fa1748Smahlzeit 					case B_TUNE_REQUEST:
97749fa1748Smahlzeit 					case B_SYS_EX_END:
97849fa1748Smahlzeit 					case B_TIMING_CLOCK:
97949fa1748Smahlzeit 					case B_START:
98049fa1748Smahlzeit 					case B_CONTINUE:
98149fa1748Smahlzeit 					case B_STOP:
98249fa1748Smahlzeit 					case B_ACTIVE_SENSING:
98349fa1748Smahlzeit 						WriteByte(event->byte1);
98449fa1748Smahlzeit 						break;
98549fa1748Smahlzeit 
98649fa1748Smahlzeit 					case B_MIDI_TIME_CODE:
98749fa1748Smahlzeit 					case B_SONG_SELECT:
98849fa1748Smahlzeit 					case B_CABLE_MESSAGE:
98949fa1748Smahlzeit 						WriteByte(event->byte1);
99049fa1748Smahlzeit 						WriteByte(event->byte2);
99149fa1748Smahlzeit 						break;
99249fa1748Smahlzeit 
99349fa1748Smahlzeit 					case B_SONG_POSITION:
99449fa1748Smahlzeit 						WriteByte(event->byte1);
99549fa1748Smahlzeit 						WriteByte(event->byte2);
99649fa1748Smahlzeit 						WriteByte(event->byte3);
99749fa1748Smahlzeit 						break;
99849fa1748Smahlzeit 
99949fa1748Smahlzeit 					case B_SYSTEM_RESET:
100049fa1748Smahlzeit 						WriteMetaEvent(event);
100149fa1748Smahlzeit 						break;
100249fa1748Smahlzeit 				}
100349fa1748Smahlzeit 				break;
100449fa1748Smahlzeit 
100549fa1748Smahlzeit 		}
100649fa1748Smahlzeit 	}
100749fa1748Smahlzeit 
100849fa1748Smahlzeit 	WriteVarLength(0);
100949fa1748Smahlzeit 	WriteByte(0xFF);   // the end-of-track
101049fa1748Smahlzeit 	WriteByte(0x2F);   // marker is required
101149fa1748Smahlzeit 	WriteByte(0x00);
101249fa1748Smahlzeit 
101349fa1748Smahlzeit 	file->Seek(lengthPos, SEEK_SET);
101449fa1748Smahlzeit 	Write32Bit(byteCount);
101549fa1748Smahlzeit 	file->Seek(0, SEEK_END);
101649fa1748Smahlzeit }
101749fa1748Smahlzeit 
101849fa1748Smahlzeit //------------------------------------------------------------------------------
101949fa1748Smahlzeit 
102049fa1748Smahlzeit void BMidiStore::WriteMetaEvent(BMidiEvent* event)
102149fa1748Smahlzeit {
102249fa1748Smahlzeit 	// We only export the Tempo Change meta event.
102349fa1748Smahlzeit 
102449fa1748Smahlzeit 	if ((event->byte2 == 0x51) && (event->byte3 == 0x03))
102549fa1748Smahlzeit 	{
102649fa1748Smahlzeit 		uint32 val = 60000000 / event->tempo;
102749fa1748Smahlzeit 
102849fa1748Smahlzeit 		WriteByte(0xFF);
102949fa1748Smahlzeit 		WriteByte(0x51);
103049fa1748Smahlzeit 		WriteByte(0x03);
103149fa1748Smahlzeit 		WriteByte(val >> 16);
103249fa1748Smahlzeit 		WriteByte(val >> 8);
103349fa1748Smahlzeit 		WriteByte(val);
103449fa1748Smahlzeit 	}
103549fa1748Smahlzeit }
103649fa1748Smahlzeit 
103749fa1748Smahlzeit //------------------------------------------------------------------------------
1038