xref: /haiku/src/kits/midi2/MidiLocalConsumer.cpp (revision f465c7fa619a31eabf63b5e8d72edcdf78982c8e)
1abe999d8Smahlzeit /*
277f87987SJérôme Duval  * Copyright 2006, Haiku.
377f87987SJérôme Duval  *
477f87987SJérôme Duval  * Copyright (c) 2002-2003 Matthijs Hollemans
560d15308SJérôme Duval  * Distributed under the terms of the MIT License.
6263f2c60Smahlzeit  *
760d15308SJérôme Duval  * Authors:
860d15308SJérôme Duval  *		Matthijs Hollemans
9a4e33e10Smahlzeit  */
106ac523eaSmahlzeit 
11e4fa4972Smahlzeit #include <stdlib.h>
12e4fa4972Smahlzeit 
136ac523eaSmahlzeit #include "debug.h"
1460d15308SJérôme Duval #include <MidiConsumer.h>
1560d15308SJérôme Duval #include <MidiRoster.h>
1653284a9fSmahlzeit #include "protocol.h"
176ac523eaSmahlzeit 
186ac523eaSmahlzeit 
1960d15308SJérôme Duval int32
_midi_event_thread(void * data)2060d15308SJérôme Duval _midi_event_thread(void* data)
21e4fa4972Smahlzeit {
22e4fa4972Smahlzeit 	return ((BMidiLocalConsumer*) data)->EventThread();
23e4fa4972Smahlzeit }
24e4fa4972Smahlzeit 
25e4fa4972Smahlzeit 
BMidiLocalConsumer(const char * name)266ac523eaSmahlzeit BMidiLocalConsumer::BMidiLocalConsumer(const char* name)
2796462df1Sjerl1 	: BMidiConsumer(name)
286ac523eaSmahlzeit {
2953284a9fSmahlzeit 	TRACE(("BMidiLocalConsumer::BMidiLocalConsumer"))
3053284a9fSmahlzeit 
3160d15308SJérôme Duval 	fIsLocal = true;
3260d15308SJérôme Duval 	fRefCount = 1;
3360d15308SJérôme Duval 	fTimeout = (bigtime_t) -1;
3460d15308SJérôme Duval 	fTimeoutData = NULL;
35e4fa4972Smahlzeit 
3660d15308SJérôme Duval 	fPort = create_port(1, "MidiEventPort");
3760d15308SJérôme Duval 	fThread = spawn_thread(
38e4fa4972Smahlzeit 		_midi_event_thread, "MidiEventThread", B_REAL_TIME_PRIORITY, this);
3960d15308SJérôme Duval 	resume_thread(fThread);
4053284a9fSmahlzeit 
4153284a9fSmahlzeit 	BMidiRoster::MidiRoster()->CreateLocal(this);
426ac523eaSmahlzeit }
436ac523eaSmahlzeit 
446ac523eaSmahlzeit 
~BMidiLocalConsumer()456ac523eaSmahlzeit BMidiLocalConsumer::~BMidiLocalConsumer()
466ac523eaSmahlzeit {
4753284a9fSmahlzeit 	TRACE(("BMidiLocalConsumer::~BMidiLocalConsumer"))
4853284a9fSmahlzeit 
4953284a9fSmahlzeit 	BMidiRoster::MidiRoster()->DeleteLocal(this);
50e4fa4972Smahlzeit 
5160d15308SJérôme Duval 	delete_port(fPort);
52e4fa4972Smahlzeit 
53e4fa4972Smahlzeit 	status_t result;
5460d15308SJérôme Duval 	wait_for_thread(fThread, &result);
556ac523eaSmahlzeit }
566ac523eaSmahlzeit 
576ac523eaSmahlzeit 
5860d15308SJérôme Duval void
SetLatency(bigtime_t latency_)5960d15308SJérôme Duval BMidiLocalConsumer::SetLatency(bigtime_t latency_)
606ac523eaSmahlzeit {
6160d15308SJérôme Duval 	if (latency_ < 0) {
6253284a9fSmahlzeit 		WARN("SetLatency() does not accept negative values");
6353284a9fSmahlzeit 		return;
6460d15308SJérôme Duval 	} else if (!IsValid()) {
6553284a9fSmahlzeit 		return;
6660d15308SJérôme Duval 	} else if (fLatency != latency_) {
6753284a9fSmahlzeit 		BMessage msg;
6853284a9fSmahlzeit 		msg.AddInt64("midi:latency", latency_);
6953284a9fSmahlzeit 
7060d15308SJérôme Duval 		if (SendChangeRequest(&msg) == B_OK) {
7160d15308SJérôme Duval 			if (LockLooper()) {
7260d15308SJérôme Duval 				fLatency = latency_;
7353284a9fSmahlzeit 				UnlockLooper();
7453284a9fSmahlzeit 			}
7553284a9fSmahlzeit 		}
7653284a9fSmahlzeit 	}
776ac523eaSmahlzeit }
786ac523eaSmahlzeit 
796ac523eaSmahlzeit 
8060d15308SJérôme Duval int32
GetProducerID()81deb109ccSJérôme Duval BMidiLocalConsumer::GetProducerID()
826ac523eaSmahlzeit {
8360d15308SJérôme Duval 	return fCurrentProducer;
846ac523eaSmahlzeit }
856ac523eaSmahlzeit 
866ac523eaSmahlzeit 
8760d15308SJérôme Duval void
SetTimeout(bigtime_t when,void * data)8860d15308SJérôme Duval BMidiLocalConsumer::SetTimeout(bigtime_t when, void* data)
896ac523eaSmahlzeit {
9060d15308SJérôme Duval 	fTimeout = when;
9160d15308SJérôme Duval 	fTimeoutData = data;
926ac523eaSmahlzeit }
936ac523eaSmahlzeit 
946ac523eaSmahlzeit 
9560d15308SJérôme Duval void
Timeout(void * data)9660d15308SJérôme Duval BMidiLocalConsumer::Timeout(void* data)
976ac523eaSmahlzeit {
98e4fa4972Smahlzeit 	// Do nothing.
996ac523eaSmahlzeit }
1006ac523eaSmahlzeit 
1016ac523eaSmahlzeit 
10260d15308SJérôme Duval void
Data(uchar * data,size_t length,bool atomic,bigtime_t time)10360d15308SJérôme Duval BMidiLocalConsumer::Data(uchar* data, size_t length, bool atomic, bigtime_t time)
1046ac523eaSmahlzeit {
10560d15308SJérôme Duval 	if (atomic) {
10660d15308SJérôme Duval 		switch (data[0] & 0xF0) {
107e4fa4972Smahlzeit 			case B_NOTE_OFF:
108e4fa4972Smahlzeit 			{
109e4fa4972Smahlzeit 				if (length == 3)
110e4fa4972Smahlzeit 					NoteOff(data[0] & 0x0F, data[1], data[2], time);
111e4fa4972Smahlzeit 				break;
112e4fa4972Smahlzeit 			}
113e4fa4972Smahlzeit 
114e4fa4972Smahlzeit 			case B_NOTE_ON:
115e4fa4972Smahlzeit 			{
116e4fa4972Smahlzeit 				if (length == 3)
117e4fa4972Smahlzeit 					NoteOn(data[0] & 0x0F, data[1], data[2], time);
118e4fa4972Smahlzeit 				break;
119e4fa4972Smahlzeit 			}
120e4fa4972Smahlzeit 
121e4fa4972Smahlzeit 			case B_KEY_PRESSURE:
122e4fa4972Smahlzeit 			{
123e4fa4972Smahlzeit 				if (length == 3)
124e4fa4972Smahlzeit 					KeyPressure(data[0] & 0x0F, data[1], data[2], time);
125e4fa4972Smahlzeit 				break;
126e4fa4972Smahlzeit 			}
127e4fa4972Smahlzeit 
128e4fa4972Smahlzeit 			case B_CONTROL_CHANGE:
129e4fa4972Smahlzeit 			{
130e4fa4972Smahlzeit 				if (length == 3)
131e4fa4972Smahlzeit 					ControlChange(data[0] & 0x0F, data[1], data[2], time);
132e4fa4972Smahlzeit 				break;
133e4fa4972Smahlzeit 			}
134e4fa4972Smahlzeit 
135e4fa4972Smahlzeit 			case B_PROGRAM_CHANGE:
136e4fa4972Smahlzeit 			{
137e4fa4972Smahlzeit 				if (length == 2)
138e4fa4972Smahlzeit 					ProgramChange(data[0] & 0x0F, data[1], time);
139e4fa4972Smahlzeit 				break;
140e4fa4972Smahlzeit 			}
141e4fa4972Smahlzeit 
142e4fa4972Smahlzeit 			case B_CHANNEL_PRESSURE:
143e4fa4972Smahlzeit 			{
144e4fa4972Smahlzeit 				if (length == 2)
145e4fa4972Smahlzeit 					ChannelPressure(data[0] & 0x0F, data[1], time);
146e4fa4972Smahlzeit 				break;
147e4fa4972Smahlzeit 			}
148e4fa4972Smahlzeit 
149e4fa4972Smahlzeit 			case B_PITCH_BEND:
150e4fa4972Smahlzeit 			{
151e4fa4972Smahlzeit 				if (length == 3)
152e4fa4972Smahlzeit 					PitchBend(data[0] & 0x0F, data[1], data[2], time);
153e4fa4972Smahlzeit 				break;
154e4fa4972Smahlzeit 			}
155e4fa4972Smahlzeit 
156e4fa4972Smahlzeit 			case 0xF0:
157e4fa4972Smahlzeit 			{
15860d15308SJérôme Duval 				switch (data[0]) {
159e4fa4972Smahlzeit 					case B_SYS_EX_START:
160e4fa4972Smahlzeit 					{
16160d15308SJérôme Duval 						if (data[length - 1] == B_SYS_EX_END) {
162e4fa4972Smahlzeit 							SystemExclusive(data + 1, length - 2, time);
16360d15308SJérôme Duval 						} else { // sysex-end is not required
16492185c23Smahlzeit 							SystemExclusive(data + 1, length - 1, time);
16592185c23Smahlzeit 						}
166e4fa4972Smahlzeit 						break;
167e4fa4972Smahlzeit 					}
168e4fa4972Smahlzeit 
169e4fa4972Smahlzeit 					case B_TUNE_REQUEST:
170e4fa4972Smahlzeit 					case B_SYS_EX_END:
171e4fa4972Smahlzeit 					{
17260d15308SJérôme Duval 						if (length == 1) {
173e4fa4972Smahlzeit 							SystemCommon(data[0], 0, 0, time);
174e4fa4972Smahlzeit 						}
175e4fa4972Smahlzeit 						break;
176e4fa4972Smahlzeit 					}
177e4fa4972Smahlzeit 
178e4fa4972Smahlzeit 					case B_CABLE_MESSAGE:
179e4fa4972Smahlzeit 					case B_MIDI_TIME_CODE:
180e4fa4972Smahlzeit 					case B_SONG_SELECT:
181e4fa4972Smahlzeit 					{
18260d15308SJérôme Duval 						if (length == 2) {
183e4fa4972Smahlzeit 							SystemCommon(data[0], data[1], 0, time);
184e4fa4972Smahlzeit 						}
185e4fa4972Smahlzeit 						break;
186e4fa4972Smahlzeit 					}
187e4fa4972Smahlzeit 
188e4fa4972Smahlzeit 					case B_SONG_POSITION:
189e4fa4972Smahlzeit 					{
19060d15308SJérôme Duval 						if (length == 3) {
191e4fa4972Smahlzeit 							SystemCommon(data[0], data[1], data[2], time);
192e4fa4972Smahlzeit 						}
193e4fa4972Smahlzeit 						break;
194e4fa4972Smahlzeit 					}
195e4fa4972Smahlzeit 
196e4fa4972Smahlzeit 					case B_TIMING_CLOCK:
197e4fa4972Smahlzeit 					case B_START:
198e4fa4972Smahlzeit 					case B_CONTINUE:
199e4fa4972Smahlzeit 					case B_STOP:
200e4fa4972Smahlzeit 					case B_ACTIVE_SENSING:
201e4fa4972Smahlzeit 					{
20260d15308SJérôme Duval 						if (length == 1) {
203e4fa4972Smahlzeit 							SystemRealTime(data[0], time);
204e4fa4972Smahlzeit 						}
205e4fa4972Smahlzeit 						break;
206e4fa4972Smahlzeit 					}
207a06c9b68Smahlzeit 
208a06c9b68Smahlzeit 					case B_SYSTEM_RESET:
209a06c9b68Smahlzeit 					{
21060d15308SJérôme Duval 						if (length == 1) {
211a06c9b68Smahlzeit 							SystemRealTime(data[0], time);
21260d15308SJérôme Duval 						} else if ((length == 6) && (data[1] == 0x51)
21360d15308SJérôme Duval 								&& (data[2] == 0x03)) {
214a06c9b68Smahlzeit 							int32 tempo =
215a06c9b68Smahlzeit 								(data[3] << 16) | (data[4] << 8) | data[5];
216a06c9b68Smahlzeit 
217a06c9b68Smahlzeit 							TempoChange(60000000/tempo, time);
218a06c9b68Smahlzeit 						}
219a06c9b68Smahlzeit 					}
220e4fa4972Smahlzeit 				}
221e4fa4972Smahlzeit 				break;
222e4fa4972Smahlzeit 			}
223e4fa4972Smahlzeit 		}
224e4fa4972Smahlzeit 	}
2256ac523eaSmahlzeit }
2266ac523eaSmahlzeit 
2276ac523eaSmahlzeit 
22860d15308SJérôme Duval void
NoteOff(uchar channel,uchar note,uchar velocity,bigtime_t time)22960d15308SJérôme Duval BMidiLocalConsumer::NoteOff(uchar channel, uchar note, uchar velocity, bigtime_t time)
2306ac523eaSmahlzeit {
231e4fa4972Smahlzeit 	// Do nothing.
2326ac523eaSmahlzeit }
2336ac523eaSmahlzeit 
2346ac523eaSmahlzeit 
23560d15308SJérôme Duval void
NoteOn(uchar channel,uchar note,uchar velocity,bigtime_t time)23660d15308SJérôme Duval BMidiLocalConsumer::NoteOn(uchar channel, uchar note, uchar velocity, bigtime_t time)
2376ac523eaSmahlzeit {
238e4fa4972Smahlzeit 	// Do nothing.
2396ac523eaSmahlzeit }
2406ac523eaSmahlzeit 
2416ac523eaSmahlzeit 
24260d15308SJérôme Duval void
KeyPressure(uchar channel,uchar note,uchar pressure,bigtime_t time)24360d15308SJérôme Duval BMidiLocalConsumer::KeyPressure(uchar channel, uchar note, uchar pressure, bigtime_t time)
2446ac523eaSmahlzeit {
245e4fa4972Smahlzeit 	// Do nothing.
2466ac523eaSmahlzeit }
2476ac523eaSmahlzeit 
2486ac523eaSmahlzeit 
24960d15308SJérôme Duval void
ControlChange(uchar channel,uchar controlNumber,uchar controlValue,bigtime_t time)25060d15308SJérôme Duval BMidiLocalConsumer::ControlChange(uchar channel, uchar controlNumber, uchar controlValue, bigtime_t time)
2516ac523eaSmahlzeit {
252e4fa4972Smahlzeit 	// Do nothing.
2536ac523eaSmahlzeit }
2546ac523eaSmahlzeit 
2556ac523eaSmahlzeit 
25660d15308SJérôme Duval void
ProgramChange(uchar channel,uchar programNumber,bigtime_t time)25760d15308SJérôme Duval BMidiLocalConsumer::ProgramChange(uchar channel, uchar programNumber, bigtime_t time)
2586ac523eaSmahlzeit {
259e4fa4972Smahlzeit 	// Do nothing.
2606ac523eaSmahlzeit }
2616ac523eaSmahlzeit 
2626ac523eaSmahlzeit 
ChannelPressure(uchar channel,uchar pressure,bigtime_t time)26360d15308SJérôme Duval void BMidiLocalConsumer::ChannelPressure(uchar channel, uchar pressure, bigtime_t time)
2646ac523eaSmahlzeit {
265e4fa4972Smahlzeit 	// Do nothing.
2666ac523eaSmahlzeit }
2676ac523eaSmahlzeit 
2686ac523eaSmahlzeit 
26960d15308SJérôme Duval void
PitchBend(uchar channel,uchar lsb,uchar msb,bigtime_t time)27060d15308SJérôme Duval BMidiLocalConsumer::PitchBend(uchar channel, uchar lsb, uchar msb, bigtime_t time)
2716ac523eaSmahlzeit {
272e4fa4972Smahlzeit 	// Do nothing.
2736ac523eaSmahlzeit }
2746ac523eaSmahlzeit 
2756ac523eaSmahlzeit 
27660d15308SJérôme Duval void
SystemExclusive(void * data,size_t length,bigtime_t time)27760d15308SJérôme Duval BMidiLocalConsumer::SystemExclusive(
278e4fa4972Smahlzeit 	void* data, size_t length, bigtime_t time)
2796ac523eaSmahlzeit {
280e4fa4972Smahlzeit 	// Do nothing.
2816ac523eaSmahlzeit }
2826ac523eaSmahlzeit 
2836ac523eaSmahlzeit 
28460d15308SJérôme Duval void
SystemCommon(uchar statusByte,uchar data1,uchar data2,bigtime_t time)28560d15308SJérôme Duval BMidiLocalConsumer::SystemCommon(
2866ac523eaSmahlzeit 	uchar statusByte, uchar data1, uchar data2, bigtime_t time)
2876ac523eaSmahlzeit {
288e4fa4972Smahlzeit 	// Do nothing.
2896ac523eaSmahlzeit }
2906ac523eaSmahlzeit 
2916ac523eaSmahlzeit 
29260d15308SJérôme Duval void
SystemRealTime(uchar statusByte,bigtime_t time)29360d15308SJérôme Duval BMidiLocalConsumer::SystemRealTime(uchar statusByte, bigtime_t time)
2946ac523eaSmahlzeit {
295e4fa4972Smahlzeit 	// Do nothing.
2966ac523eaSmahlzeit }
2976ac523eaSmahlzeit 
2986ac523eaSmahlzeit 
29960d15308SJérôme Duval void
TempoChange(int32 beatsPerMinute,bigtime_t time)30060d15308SJérôme Duval BMidiLocalConsumer::TempoChange(int32 beatsPerMinute, bigtime_t time)
3016ac523eaSmahlzeit {
302e4fa4972Smahlzeit 	// Do nothing.
3036ac523eaSmahlzeit }
3046ac523eaSmahlzeit 
3056ac523eaSmahlzeit 
30660d15308SJérôme Duval void
AllNotesOff(bool justChannel,bigtime_t time)30760d15308SJérôme Duval BMidiLocalConsumer::AllNotesOff(bool justChannel, bigtime_t time)
3086ac523eaSmahlzeit {
309e4fa4972Smahlzeit 	// Do nothing.
3106ac523eaSmahlzeit }
3116ac523eaSmahlzeit 
3126ac523eaSmahlzeit 
_Reserved1()3136ac523eaSmahlzeit void BMidiLocalConsumer::_Reserved1() { }
_Reserved2()3146ac523eaSmahlzeit void BMidiLocalConsumer::_Reserved2() { }
_Reserved3()3156ac523eaSmahlzeit void BMidiLocalConsumer::_Reserved3() { }
_Reserved4()3166ac523eaSmahlzeit void BMidiLocalConsumer::_Reserved4() { }
_Reserved5()3176ac523eaSmahlzeit void BMidiLocalConsumer::_Reserved5() { }
_Reserved6()3186ac523eaSmahlzeit void BMidiLocalConsumer::_Reserved6() { }
_Reserved7()3196ac523eaSmahlzeit void BMidiLocalConsumer::_Reserved7() { }
_Reserved8()3206ac523eaSmahlzeit void BMidiLocalConsumer::_Reserved8() { }
3216ac523eaSmahlzeit 
322e4fa4972Smahlzeit 
32360d15308SJérôme Duval int32
EventThread()32460d15308SJérôme Duval BMidiLocalConsumer::EventThread()
325e4fa4972Smahlzeit {
326e4fa4972Smahlzeit 	int32 msg_code;
327e4fa4972Smahlzeit 	ssize_t msg_size;
328e4fa4972Smahlzeit 	ssize_t buf_size = 100;
329e4fa4972Smahlzeit 	uint8* buffer = (uint8*) malloc(buf_size);
330e4fa4972Smahlzeit 
33160d15308SJérôme Duval 	while (true) {
33260d15308SJérôme Duval 		if (fTimeout == (bigtime_t) -1) {
33360d15308SJérôme Duval 			msg_size = port_buffer_size(fPort);
33460d15308SJérôme Duval 		} else { // have timeout
33560d15308SJérôme Duval 			msg_size = port_buffer_size_etc(fPort, B_ABSOLUTE_TIMEOUT, fTimeout);
33660d15308SJérôme Duval 			if (msg_size == B_TIMED_OUT) {
33760d15308SJérôme Duval 				Timeout(fTimeoutData);
33860d15308SJérôme Duval 				fTimeout = (bigtime_t) -1;
33960d15308SJérôme Duval 				fTimeoutData = NULL;
340e4fa4972Smahlzeit 				continue;
341e4fa4972Smahlzeit 			}
342e4fa4972Smahlzeit 		}
343e4fa4972Smahlzeit 
34460d15308SJérôme Duval 		if (msg_size < 0)
34560d15308SJérôme Duval 			break;  // error reading port
346e4fa4972Smahlzeit 
34760d15308SJérôme Duval 		if (msg_size > buf_size) {
348*f465c7faSMurai Takashi 			uint8* tmp_buffer = (uint8*) realloc(buffer, msg_size);
349*f465c7faSMurai Takashi 			if (tmp_buffer == NULL)
350*f465c7faSMurai Takashi 				break; // error in realloc()
351*f465c7faSMurai Takashi 			buffer = tmp_buffer;
352e4fa4972Smahlzeit 			buf_size = msg_size;
353e4fa4972Smahlzeit 		}
354e4fa4972Smahlzeit 
35560d15308SJérôme Duval 		read_port(fPort, &msg_code, buffer, msg_size);
356e4fa4972Smahlzeit 
35760d15308SJérôme Duval 		if (msg_size > 20) { // minimum valid size
358e4fa4972Smahlzeit 			#ifdef DEBUG
359e4fa4972Smahlzeit 			printf("*** received: ");
36060d15308SJérôme Duval 			for (int32 t = 0; t < msg_size; ++t) {
361e4fa4972Smahlzeit 				printf("%02X, ", ((uint8*) buffer)[t]);
362e4fa4972Smahlzeit 			}
363e4fa4972Smahlzeit 			printf("\n");
364e4fa4972Smahlzeit 			#endif
365e4fa4972Smahlzeit 
36660d15308SJérôme Duval 			fCurrentProducer = *((uint32*)    (buffer +  0));
367e4fa4972Smahlzeit 			int32 targetId  = *((uint32*)    (buffer +  4));
368e4fa4972Smahlzeit 			bigtime_t time  = *((bigtime_t*) (buffer +  8));
369e4fa4972Smahlzeit 			bool atomic     = *((bool*)      (buffer + 16));
370e4fa4972Smahlzeit 
37160d15308SJérôme Duval 			if (targetId == fId) { // only if we are the destination
372e4fa4972Smahlzeit 				Data((uchar*) (buffer + 20), msg_size - 20, atomic, time);
373e4fa4972Smahlzeit 			}
374e4fa4972Smahlzeit 		}
375e4fa4972Smahlzeit 	}
376e4fa4972Smahlzeit 
377e4fa4972Smahlzeit 	free(buffer);
378e4fa4972Smahlzeit 	return 0;
379e4fa4972Smahlzeit }
380e4fa4972Smahlzeit 
381