1 /*
2 * Copyright 2006, Haiku.
3 *
4 * Copyright (c) 2002-2003 Matthijs Hollemans
5 * Distributed under the terms of the MIT License.
6 *
7 * Authors:
8 * Matthijs Hollemans
9 */
10
11 #include <stdlib.h>
12
13 #include "debug.h"
14 #include <MidiConsumer.h>
15 #include <MidiRoster.h>
16 #include "protocol.h"
17
18
19 int32
_midi_event_thread(void * data)20 _midi_event_thread(void* data)
21 {
22 return ((BMidiLocalConsumer*) data)->EventThread();
23 }
24
25
BMidiLocalConsumer(const char * name)26 BMidiLocalConsumer::BMidiLocalConsumer(const char* name)
27 : BMidiConsumer(name)
28 {
29 TRACE(("BMidiLocalConsumer::BMidiLocalConsumer"))
30
31 fIsLocal = true;
32 fRefCount = 1;
33 fTimeout = (bigtime_t) -1;
34 fTimeoutData = NULL;
35
36 fPort = create_port(1, "MidiEventPort");
37 fThread = spawn_thread(
38 _midi_event_thread, "MidiEventThread", B_REAL_TIME_PRIORITY, this);
39 resume_thread(fThread);
40
41 BMidiRoster::MidiRoster()->CreateLocal(this);
42 }
43
44
~BMidiLocalConsumer()45 BMidiLocalConsumer::~BMidiLocalConsumer()
46 {
47 TRACE(("BMidiLocalConsumer::~BMidiLocalConsumer"))
48
49 BMidiRoster::MidiRoster()->DeleteLocal(this);
50
51 delete_port(fPort);
52
53 status_t result;
54 wait_for_thread(fThread, &result);
55 }
56
57
58 void
SetLatency(bigtime_t latency_)59 BMidiLocalConsumer::SetLatency(bigtime_t latency_)
60 {
61 if (latency_ < 0) {
62 WARN("SetLatency() does not accept negative values");
63 return;
64 } else if (!IsValid()) {
65 return;
66 } else if (fLatency != latency_) {
67 BMessage msg;
68 msg.AddInt64("midi:latency", latency_);
69
70 if (SendChangeRequest(&msg) == B_OK) {
71 if (LockLooper()) {
72 fLatency = latency_;
73 UnlockLooper();
74 }
75 }
76 }
77 }
78
79
80 int32
GetProducerID()81 BMidiLocalConsumer::GetProducerID()
82 {
83 return fCurrentProducer;
84 }
85
86
87 void
SetTimeout(bigtime_t when,void * data)88 BMidiLocalConsumer::SetTimeout(bigtime_t when, void* data)
89 {
90 fTimeout = when;
91 fTimeoutData = data;
92 }
93
94
95 void
Timeout(void * data)96 BMidiLocalConsumer::Timeout(void* data)
97 {
98 // Do nothing.
99 }
100
101
102 void
Data(uchar * data,size_t length,bool atomic,bigtime_t time)103 BMidiLocalConsumer::Data(uchar* data, size_t length, bool atomic, bigtime_t time)
104 {
105 if (atomic) {
106 switch (data[0] & 0xF0) {
107 case B_NOTE_OFF:
108 {
109 if (length == 3)
110 NoteOff(data[0] & 0x0F, data[1], data[2], time);
111 break;
112 }
113
114 case B_NOTE_ON:
115 {
116 if (length == 3)
117 NoteOn(data[0] & 0x0F, data[1], data[2], time);
118 break;
119 }
120
121 case B_KEY_PRESSURE:
122 {
123 if (length == 3)
124 KeyPressure(data[0] & 0x0F, data[1], data[2], time);
125 break;
126 }
127
128 case B_CONTROL_CHANGE:
129 {
130 if (length == 3)
131 ControlChange(data[0] & 0x0F, data[1], data[2], time);
132 break;
133 }
134
135 case B_PROGRAM_CHANGE:
136 {
137 if (length == 2)
138 ProgramChange(data[0] & 0x0F, data[1], time);
139 break;
140 }
141
142 case B_CHANNEL_PRESSURE:
143 {
144 if (length == 2)
145 ChannelPressure(data[0] & 0x0F, data[1], time);
146 break;
147 }
148
149 case B_PITCH_BEND:
150 {
151 if (length == 3)
152 PitchBend(data[0] & 0x0F, data[1], data[2], time);
153 break;
154 }
155
156 case 0xF0:
157 {
158 switch (data[0]) {
159 case B_SYS_EX_START:
160 {
161 if (data[length - 1] == B_SYS_EX_END) {
162 SystemExclusive(data + 1, length - 2, time);
163 } else { // sysex-end is not required
164 SystemExclusive(data + 1, length - 1, time);
165 }
166 break;
167 }
168
169 case B_TUNE_REQUEST:
170 case B_SYS_EX_END:
171 {
172 if (length == 1) {
173 SystemCommon(data[0], 0, 0, time);
174 }
175 break;
176 }
177
178 case B_CABLE_MESSAGE:
179 case B_MIDI_TIME_CODE:
180 case B_SONG_SELECT:
181 {
182 if (length == 2) {
183 SystemCommon(data[0], data[1], 0, time);
184 }
185 break;
186 }
187
188 case B_SONG_POSITION:
189 {
190 if (length == 3) {
191 SystemCommon(data[0], data[1], data[2], time);
192 }
193 break;
194 }
195
196 case B_TIMING_CLOCK:
197 case B_START:
198 case B_CONTINUE:
199 case B_STOP:
200 case B_ACTIVE_SENSING:
201 {
202 if (length == 1) {
203 SystemRealTime(data[0], time);
204 }
205 break;
206 }
207
208 case B_SYSTEM_RESET:
209 {
210 if (length == 1) {
211 SystemRealTime(data[0], time);
212 } else if ((length == 6) && (data[1] == 0x51)
213 && (data[2] == 0x03)) {
214 int32 tempo =
215 (data[3] << 16) | (data[4] << 8) | data[5];
216
217 TempoChange(60000000/tempo, time);
218 }
219 }
220 }
221 break;
222 }
223 }
224 }
225 }
226
227
228 void
NoteOff(uchar channel,uchar note,uchar velocity,bigtime_t time)229 BMidiLocalConsumer::NoteOff(uchar channel, uchar note, uchar velocity, bigtime_t time)
230 {
231 // Do nothing.
232 }
233
234
235 void
NoteOn(uchar channel,uchar note,uchar velocity,bigtime_t time)236 BMidiLocalConsumer::NoteOn(uchar channel, uchar note, uchar velocity, bigtime_t time)
237 {
238 // Do nothing.
239 }
240
241
242 void
KeyPressure(uchar channel,uchar note,uchar pressure,bigtime_t time)243 BMidiLocalConsumer::KeyPressure(uchar channel, uchar note, uchar pressure, bigtime_t time)
244 {
245 // Do nothing.
246 }
247
248
249 void
ControlChange(uchar channel,uchar controlNumber,uchar controlValue,bigtime_t time)250 BMidiLocalConsumer::ControlChange(uchar channel, uchar controlNumber, uchar controlValue, bigtime_t time)
251 {
252 // Do nothing.
253 }
254
255
256 void
ProgramChange(uchar channel,uchar programNumber,bigtime_t time)257 BMidiLocalConsumer::ProgramChange(uchar channel, uchar programNumber, bigtime_t time)
258 {
259 // Do nothing.
260 }
261
262
ChannelPressure(uchar channel,uchar pressure,bigtime_t time)263 void BMidiLocalConsumer::ChannelPressure(uchar channel, uchar pressure, bigtime_t time)
264 {
265 // Do nothing.
266 }
267
268
269 void
PitchBend(uchar channel,uchar lsb,uchar msb,bigtime_t time)270 BMidiLocalConsumer::PitchBend(uchar channel, uchar lsb, uchar msb, bigtime_t time)
271 {
272 // Do nothing.
273 }
274
275
276 void
SystemExclusive(void * data,size_t length,bigtime_t time)277 BMidiLocalConsumer::SystemExclusive(
278 void* data, size_t length, bigtime_t time)
279 {
280 // Do nothing.
281 }
282
283
284 void
SystemCommon(uchar statusByte,uchar data1,uchar data2,bigtime_t time)285 BMidiLocalConsumer::SystemCommon(
286 uchar statusByte, uchar data1, uchar data2, bigtime_t time)
287 {
288 // Do nothing.
289 }
290
291
292 void
SystemRealTime(uchar statusByte,bigtime_t time)293 BMidiLocalConsumer::SystemRealTime(uchar statusByte, bigtime_t time)
294 {
295 // Do nothing.
296 }
297
298
299 void
TempoChange(int32 beatsPerMinute,bigtime_t time)300 BMidiLocalConsumer::TempoChange(int32 beatsPerMinute, bigtime_t time)
301 {
302 // Do nothing.
303 }
304
305
306 void
AllNotesOff(bool justChannel,bigtime_t time)307 BMidiLocalConsumer::AllNotesOff(bool justChannel, bigtime_t time)
308 {
309 // Do nothing.
310 }
311
312
_Reserved1()313 void BMidiLocalConsumer::_Reserved1() { }
_Reserved2()314 void BMidiLocalConsumer::_Reserved2() { }
_Reserved3()315 void BMidiLocalConsumer::_Reserved3() { }
_Reserved4()316 void BMidiLocalConsumer::_Reserved4() { }
_Reserved5()317 void BMidiLocalConsumer::_Reserved5() { }
_Reserved6()318 void BMidiLocalConsumer::_Reserved6() { }
_Reserved7()319 void BMidiLocalConsumer::_Reserved7() { }
_Reserved8()320 void BMidiLocalConsumer::_Reserved8() { }
321
322
323 int32
EventThread()324 BMidiLocalConsumer::EventThread()
325 {
326 int32 msg_code;
327 ssize_t msg_size;
328 ssize_t buf_size = 100;
329 uint8* buffer = (uint8*) malloc(buf_size);
330
331 while (true) {
332 if (fTimeout == (bigtime_t) -1) {
333 msg_size = port_buffer_size(fPort);
334 } else { // have timeout
335 msg_size = port_buffer_size_etc(fPort, B_ABSOLUTE_TIMEOUT, fTimeout);
336 if (msg_size == B_TIMED_OUT) {
337 Timeout(fTimeoutData);
338 fTimeout = (bigtime_t) -1;
339 fTimeoutData = NULL;
340 continue;
341 }
342 }
343
344 if (msg_size < 0)
345 break; // error reading port
346
347 if (msg_size > buf_size) {
348 uint8* tmp_buffer = (uint8*) realloc(buffer, msg_size);
349 if (tmp_buffer == NULL)
350 break; // error in realloc()
351 buffer = tmp_buffer;
352 buf_size = msg_size;
353 }
354
355 read_port(fPort, &msg_code, buffer, msg_size);
356
357 if (msg_size > 20) { // minimum valid size
358 #ifdef DEBUG
359 printf("*** received: ");
360 for (int32 t = 0; t < msg_size; ++t) {
361 printf("%02X, ", ((uint8*) buffer)[t]);
362 }
363 printf("\n");
364 #endif
365
366 fCurrentProducer = *((uint32*) (buffer + 0));
367 int32 targetId = *((uint32*) (buffer + 4));
368 bigtime_t time = *((bigtime_t*) (buffer + 8));
369 bool atomic = *((bool*) (buffer + 16));
370
371 if (targetId == fId) { // only if we are the destination
372 Data((uchar*) (buffer + 20), msg_size - 20, atomic, time);
373 }
374 }
375 }
376
377 free(buffer);
378 return 0;
379 }
380
381