xref: /haiku/src/kits/midi2/MidiLocalProducer.cpp (revision 1a7bcf6962e1c99906cce0fe602e08c3fcda46f6)
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 
BMidiLocalProducer(const char * name)20 BMidiLocalProducer::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 
~BMidiLocalProducer()32 BMidiLocalProducer::~BMidiLocalProducer()
33 {
34 	TRACE(("BMidiLocalProducer::~BMidiLocalProducer"))
35 
36 	BMidiRoster::MidiRoster()->DeleteLocal(this);
37 }
38 
39 
40 void
Connected(BMidiConsumer * cons)41 BMidiLocalProducer::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 
50 void
Disconnected(BMidiConsumer * cons)51 BMidiLocalProducer::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 
60 void
SprayData(void * data,size_t length,bool atomic,bigtime_t time) const61 BMidiLocalProducer::SprayData(void* data, size_t length,
62 	bool atomic, bigtime_t time) const
63 {
64 	SprayEvent(data, length, atomic, time);
65 }
66 
67 
68 void
SprayNoteOff(uchar channel,uchar note,uchar velocity,bigtime_t time) const69 BMidiLocalProducer::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 
85 void
SprayNoteOn(uchar channel,uchar note,uchar velocity,bigtime_t time) const86 BMidiLocalProducer::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 
102 void
SprayKeyPressure(uchar channel,uchar note,uchar pressure,bigtime_t time) const103 BMidiLocalProducer::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 
119 void
SprayControlChange(uchar channel,uchar controlNumber,uchar controlValue,bigtime_t time) const120 BMidiLocalProducer::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 
136 void
SprayProgramChange(uchar channel,uchar programNumber,bigtime_t time) const137 BMidiLocalProducer::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 
152 void
SprayChannelPressure(uchar channel,uchar pressure,bigtime_t time) const153 BMidiLocalProducer::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 
168 void
SprayPitchBend(uchar channel,uchar lsb,uchar msb,bigtime_t time) const169 BMidiLocalProducer::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 
185 void
SpraySystemExclusive(void * data,size_t length,bigtime_t time) const186 BMidiLocalProducer::SpraySystemExclusive(void* data,
187 	size_t length, bigtime_t time) const
188 {
189 	SprayEvent(data, length, true, time, true);
190 }
191 
192 
193 void
SpraySystemCommon(uchar status,uchar data1,uchar data2,bigtime_t time) const194 BMidiLocalProducer::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 
228 void
SpraySystemRealTime(uchar status,bigtime_t time) const229 BMidiLocalProducer::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 
239 void
SprayTempoChange(int32 beatsPerMinute,bigtime_t time) const240 BMidiLocalProducer::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 
_Reserved1()258 void BMidiLocalProducer::_Reserved1() { }
_Reserved2()259 void BMidiLocalProducer::_Reserved2() { }
_Reserved3()260 void BMidiLocalProducer::_Reserved3() { }
_Reserved4()261 void BMidiLocalProducer::_Reserved4() { }
_Reserved5()262 void BMidiLocalProducer::_Reserved5() { }
_Reserved6()263 void BMidiLocalProducer::_Reserved6() { }
_Reserved7()264 void BMidiLocalProducer::_Reserved7() { }
_Reserved8()265 void BMidiLocalProducer::_Reserved8() { }
266 
267 //------------------------------------------------------------------------------
268 
269 void
SprayEvent(const void * data,size_t length,bool atomic,bigtime_t time,bool sysex) const270 BMidiLocalProducer::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