1/*
2 * Copyright 2005-2006, Haiku.
3 *
4 * Copyright (c) 2002-2004 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 <MidiProducer.h>
16#include <MidiRoster.h>
17#include "protocol.h"
18
19
20BMidiLocalProducer::BMidiLocalProducer(const char* name)
21	: BMidiProducer(name)
22{
23	TRACE(("BMidiLocalProducer::BMidiLocalProducer"))
24
25	fIsLocal = true;
26	fRefCount = 1;
27
28	BMidiRoster::MidiRoster()->CreateLocal(this);
29}
30
31
32BMidiLocalProducer::~BMidiLocalProducer()
33{
34	TRACE(("BMidiLocalProducer::~BMidiLocalProducer"))
35
36	BMidiRoster::MidiRoster()->DeleteLocal(this);
37}
38
39
40void
41BMidiLocalProducer::Connected(BMidiConsumer* cons)
42{
43	ASSERT(cons != NULL)
44	TRACE(("Connected() %" B_PRId32 " to %" B_PRId32 "", ID(), cons->ID()))
45
46	// Do nothing.
47}
48
49
50void
51BMidiLocalProducer::Disconnected(BMidiConsumer* cons)
52{
53	ASSERT(cons != NULL)
54	TRACE(("Disconnected() %" B_PRId32 " from %" B_PRId32 "", ID(), cons->ID()))
55
56	// Do nothing.
57}
58
59
60void
61BMidiLocalProducer::SprayData(void* data, size_t length,
62	bool atomic, bigtime_t time) const
63{
64	SprayEvent(data, length, atomic, time);
65}
66
67
68void
69BMidiLocalProducer::SprayNoteOff(uchar channel, uchar note,
70	uchar velocity, bigtime_t time) const
71{
72	if (channel < 16) {
73		uchar data[3];
74		data[0] = B_NOTE_OFF + channel;
75		data[1] = note;
76		data[2] = velocity;
77
78		SprayEvent(&data, 3, true, time);
79	} else {
80		debugger("invalid MIDI channel");
81	}
82}
83
84
85void
86BMidiLocalProducer::SprayNoteOn(uchar channel, uchar note,
87	uchar velocity, bigtime_t time) const
88{
89	if (channel < 16) {
90		uchar data[3];
91		data[0] = B_NOTE_ON + channel;
92		data[1] = note;
93		data[2] = velocity;
94
95		SprayEvent(&data, 3, true, time);
96	} else {
97		debugger("invalid MIDI channel");
98	}
99}
100
101
102void
103BMidiLocalProducer::SprayKeyPressure(uchar channel, uchar note,
104	uchar pressure, bigtime_t time) const
105{
106	if (channel < 16) {
107		uchar data[3];
108		data[0] = B_KEY_PRESSURE + channel;
109		data[1] = note;
110		data[2] = pressure;
111
112		SprayEvent(&data, 3, true, time);
113	} else {
114		debugger("invalid MIDI channel");
115	}
116}
117
118
119void
120BMidiLocalProducer::SprayControlChange(uchar channel,
121	uchar controlNumber, uchar controlValue, bigtime_t time) const
122{
123	if (channel < 16) {
124		uchar data[3];
125		data[0] = B_CONTROL_CHANGE + channel;
126		data[1] = controlNumber;
127		data[2] = controlValue;
128
129		SprayEvent(&data, 3, true, time);
130	} else {
131		debugger("invalid MIDI channel");
132	}
133}
134
135
136void
137BMidiLocalProducer::SprayProgramChange(uchar channel,
138	uchar programNumber, bigtime_t time) const
139{
140	if (channel < 16) {
141		uchar data[2];
142		data[0] = B_PROGRAM_CHANGE + channel;
143		data[1] = programNumber;
144
145		SprayEvent(&data, 2, true, time);
146	} else {
147		debugger("invalid MIDI channel");
148	}
149}
150
151
152void
153BMidiLocalProducer::SprayChannelPressure(uchar channel,
154	uchar pressure, bigtime_t time) const
155{
156	if (channel < 16) {
157		uchar data[2];
158		data[0] = B_CHANNEL_PRESSURE + channel;
159		data[1] = pressure;
160
161		SprayEvent(&data, 2, true, time);
162	} else {
163		debugger("invalid MIDI channel");
164	}
165}
166
167
168void
169BMidiLocalProducer::SprayPitchBend(uchar channel,
170	uchar lsb, uchar msb, bigtime_t time) const
171{
172	if (channel < 16) {
173		uchar data[3];
174		data[0] = B_PITCH_BEND + channel;
175		data[1] = lsb;
176		data[2] = msb;
177
178		SprayEvent(&data, 3, true, time);
179	} else {
180		debugger("invalid MIDI channel");
181	}
182}
183
184
185void
186BMidiLocalProducer::SpraySystemExclusive(void* data,
187	size_t length, bigtime_t time) const
188{
189	SprayEvent(data, length, true, time, true);
190}
191
192
193void
194BMidiLocalProducer::SpraySystemCommon(uchar status, uchar data1,
195	uchar data2, bigtime_t time) const
196{
197	size_t len;
198	uchar data[3];
199	data[0] = status;
200	data[1] = data1;
201	data[2] = data2;
202
203	switch (status) {
204		case B_TUNE_REQUEST:
205		case B_SYS_EX_END:
206			len = 1;
207			break;
208
209		case B_CABLE_MESSAGE:
210		case B_MIDI_TIME_CODE:
211		case B_SONG_SELECT:
212			len = 2;
213			break;
214
215		case B_SONG_POSITION:
216			len = 3;
217			break;
218
219		default:
220			debugger("invalid system common status");
221			len = 0;
222	}
223
224	SprayEvent(&data, len, true, time);
225}
226
227
228void
229BMidiLocalProducer::SpraySystemRealTime(uchar status,
230	bigtime_t time) const
231{
232	if (status >= B_TIMING_CLOCK)
233		SprayEvent(&status, 1, true, time);
234	else
235		debugger("invalid real time status");
236}
237
238
239void
240BMidiLocalProducer::SprayTempoChange(int32 beatsPerMinute,
241	bigtime_t time) const
242{
243	int32 tempo = 60000000 / beatsPerMinute;
244
245	uchar data[6];
246	data[0] = 0xFF;
247	data[1] = 0x51;
248	data[2] = 0x03;
249	data[3] = tempo >> 16;
250	data[4] = tempo >> 8;
251	data[5] = tempo;
252
253	SprayEvent(&data, 6, true, time);
254}
255
256//------------------------------------------------------------------------------
257
258void BMidiLocalProducer::_Reserved1() { }
259void BMidiLocalProducer::_Reserved2() { }
260void BMidiLocalProducer::_Reserved3() { }
261void BMidiLocalProducer::_Reserved4() { }
262void BMidiLocalProducer::_Reserved5() { }
263void BMidiLocalProducer::_Reserved6() { }
264void BMidiLocalProducer::_Reserved7() { }
265void BMidiLocalProducer::_Reserved8() { }
266
267//------------------------------------------------------------------------------
268
269void
270BMidiLocalProducer::SprayEvent(const void* data, size_t length,
271	bool atomic, bigtime_t time, bool sysex) const
272{
273	if (LockProducer()) {
274		if (CountConsumers() > 0) {
275			// We don't just send the MIDI event data to all connected
276			// consumers, we also send a header. The header contains our
277			// ID (4 bytes), the consumer's ID (4 bytes), the performance
278			// time (8 bytes), whether the data is atomic (1 byte), and
279			// padding (3 bytes). The MIDI event data follows the header.
280
281			size_t buf_size = 20 + length;
282			if (sysex) {
283				// add 0xF0 and 0xF7 markers
284				buf_size += 2;
285			}
286
287			uint8* buffer = (uint8*)malloc(buf_size);
288			if (buffer != NULL) {
289				*((uint32*)    (buffer +  0)) = fId;
290				*((bigtime_t*) (buffer +  8)) = time;
291				*((uint32*)    (buffer + 16)) = 0;
292				*((bool*)      (buffer + 16)) = atomic;
293
294				if (sysex) {
295					*((uint8*) (buffer + 20)) = B_SYS_EX_START;
296					if (data != NULL)
297						memcpy(buffer + 21, data, length);
298
299					*((uint8*) (buffer + buf_size - 1)) = B_SYS_EX_END;
300				} else if (data != NULL) {
301					memcpy(buffer + 20, data, length);
302				}
303
304				for (int32 t = 0; t < CountConsumers(); ++t) {
305					BMidiConsumer* cons = ConsumerAt(t);
306					*((uint32*) (buffer + 4)) = cons->fId;
307
308					#ifdef DEBUG
309					printf("*** spraying: ");
310					for (uint32 t = 0; t < buf_size; ++t)
311					{
312						printf("%02X, ", buffer[t]);
313					}
314					printf("\n");
315					#endif
316
317					write_port(cons->fPort, 0, buffer, buf_size);
318				}
319
320				free(buffer);
321			}
322		}
323
324		UnlockProducer();
325	}
326}
327
328