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