xref: /haiku/src/kits/midi2/MidiLocalConsumer.cpp (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
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
20 _midi_event_thread(void* data)
21 {
22 	return ((BMidiLocalConsumer*) data)->EventThread();
23 }
24 
25 
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 
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
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
81 BMidiLocalConsumer::GetProducerID()
82 {
83 	return fCurrentProducer;
84 }
85 
86 
87 void
88 BMidiLocalConsumer::SetTimeout(bigtime_t when, void* data)
89 {
90 	fTimeout = when;
91 	fTimeoutData = data;
92 }
93 
94 
95 void
96 BMidiLocalConsumer::Timeout(void* data)
97 {
98 	// Do nothing.
99 }
100 
101 
102 void
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
229 BMidiLocalConsumer::NoteOff(uchar channel, uchar note, uchar velocity, bigtime_t time)
230 {
231 	// Do nothing.
232 }
233 
234 
235 void
236 BMidiLocalConsumer::NoteOn(uchar channel, uchar note, uchar velocity, bigtime_t time)
237 {
238 	// Do nothing.
239 }
240 
241 
242 void
243 BMidiLocalConsumer::KeyPressure(uchar channel, uchar note, uchar pressure, bigtime_t time)
244 {
245 	// Do nothing.
246 }
247 
248 
249 void
250 BMidiLocalConsumer::ControlChange(uchar channel, uchar controlNumber, uchar controlValue, bigtime_t time)
251 {
252 	// Do nothing.
253 }
254 
255 
256 void
257 BMidiLocalConsumer::ProgramChange(uchar channel, uchar programNumber, bigtime_t time)
258 {
259 	// Do nothing.
260 }
261 
262 
263 void BMidiLocalConsumer::ChannelPressure(uchar channel, uchar pressure, bigtime_t time)
264 {
265 	// Do nothing.
266 }
267 
268 
269 void
270 BMidiLocalConsumer::PitchBend(uchar channel, uchar lsb, uchar msb, bigtime_t time)
271 {
272 	// Do nothing.
273 }
274 
275 
276 void
277 BMidiLocalConsumer::SystemExclusive(
278 	void* data, size_t length, bigtime_t time)
279 {
280 	// Do nothing.
281 }
282 
283 
284 void
285 BMidiLocalConsumer::SystemCommon(
286 	uchar statusByte, uchar data1, uchar data2, bigtime_t time)
287 {
288 	// Do nothing.
289 }
290 
291 
292 void
293 BMidiLocalConsumer::SystemRealTime(uchar statusByte, bigtime_t time)
294 {
295 	// Do nothing.
296 }
297 
298 
299 void
300 BMidiLocalConsumer::TempoChange(int32 beatsPerMinute, bigtime_t time)
301 {
302 	// Do nothing.
303 }
304 
305 
306 void
307 BMidiLocalConsumer::AllNotesOff(bool justChannel, bigtime_t time)
308 {
309 	// Do nothing.
310 }
311 
312 
313 void BMidiLocalConsumer::_Reserved1() { }
314 void BMidiLocalConsumer::_Reserved2() { }
315 void BMidiLocalConsumer::_Reserved3() { }
316 void BMidiLocalConsumer::_Reserved4() { }
317 void BMidiLocalConsumer::_Reserved5() { }
318 void BMidiLocalConsumer::_Reserved6() { }
319 void BMidiLocalConsumer::_Reserved7() { }
320 void BMidiLocalConsumer::_Reserved8() { }
321 
322 
323 int32
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