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