xref: /haiku/src/kits/midi2/MidiLocalProducer.cpp (revision 5d9e40fe9252c8f9c5e5e41594545bfa4419fcc7)
1 /*
2  * Copyright (c) 2002-2003 Matthijs Hollemans
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <stdlib.h>
24 
25 #include "debug.h"
26 #include "MidiConsumer.h"
27 #include "MidiProducer.h"
28 #include "MidiRoster.h"
29 #include "protocol.h"
30 
31 //------------------------------------------------------------------------------
32 
33 BMidiLocalProducer::BMidiLocalProducer(const char* name)
34 	: BMidiProducer(name)
35 {
36 	TRACE(("BMidiLocalProducer::BMidiLocalProducer"))
37 
38 	isLocal = true;
39 	refCount = 1;
40 
41 	BMidiRoster::MidiRoster()->CreateLocal(this);
42 }
43 
44 //------------------------------------------------------------------------------
45 
46 BMidiLocalProducer::~BMidiLocalProducer()
47 {
48 	TRACE(("BMidiLocalProducer::~BMidiLocalProducer"))
49 
50 	BMidiRoster::MidiRoster()->DeleteLocal(this);
51 }
52 
53 //------------------------------------------------------------------------------
54 
55 void BMidiLocalProducer::Connected(BMidiConsumer* cons)
56 {
57 	ASSERT(cons != NULL)
58 	TRACE(("Connected() %ld to %ld", ID(), cons->ID()))
59 
60 	// Do nothing.
61 }
62 
63 //------------------------------------------------------------------------------
64 
65 void BMidiLocalProducer::Disconnected(BMidiConsumer* cons)
66 {
67 	ASSERT(cons != NULL)
68 	TRACE(("Disconnected() %ld from %ld", ID(), cons->ID()))
69 
70 	// Do nothing.
71 }
72 
73 //------------------------------------------------------------------------------
74 
75 void BMidiLocalProducer::SprayData(
76 	void* data, size_t length, bool atomic, bigtime_t time) const
77 {
78 	SprayEvent(data, length, atomic, time);
79 }
80 
81 //------------------------------------------------------------------------------
82 
83 void BMidiLocalProducer::SprayNoteOff(
84 	uchar channel, uchar note, uchar velocity, bigtime_t time) const
85 {
86 	if (channel < 16)
87 	{
88 		uchar data[3];
89 		data[0] = B_NOTE_OFF + channel;
90 		data[1] = note;
91 		data[2] = velocity;
92 
93 		SprayEvent(&data, 3, true, time);
94 	}
95 	else
96 	{
97 		debugger("invalid MIDI channel");
98 	}
99 }
100 
101 //------------------------------------------------------------------------------
102 
103 void BMidiLocalProducer::SprayNoteOn(
104 	uchar channel, uchar note, uchar velocity, bigtime_t time) const
105 {
106 	if (channel < 16)
107 	{
108 		uchar data[3];
109 		data[0] = B_NOTE_ON + channel;
110 		data[1] = note;
111 		data[2] = velocity;
112 
113 		SprayEvent(&data, 3, true, time);
114 	}
115 	else
116 	{
117 		debugger("invalid MIDI channel");
118 	}
119 }
120 
121 //------------------------------------------------------------------------------
122 
123 void BMidiLocalProducer::SprayKeyPressure(
124 	uchar channel, uchar note, uchar pressure, bigtime_t time) const
125 {
126 	if (channel < 16)
127 	{
128 		uchar data[3];
129 		data[0] = B_KEY_PRESSURE + channel;
130 		data[1] = note;
131 		data[2] = pressure;
132 
133 		SprayEvent(&data, 3, true, time);
134 	}
135 	else
136 	{
137 		debugger("invalid MIDI channel");
138 	}
139 }
140 
141 //------------------------------------------------------------------------------
142 
143 void BMidiLocalProducer::SprayControlChange(
144 	uchar channel, uchar controlNumber, uchar controlValue,
145 	bigtime_t time) const
146 {
147 	if (channel < 16)
148 	{
149 		uchar data[3];
150 		data[0] = B_CONTROL_CHANGE + channel;
151 		data[1] = controlNumber;
152 		data[2] = controlValue;
153 
154 		SprayEvent(&data, 3, true, time);
155 	}
156 	else
157 	{
158 		debugger("invalid MIDI channel");
159 	}
160 }
161 
162 //------------------------------------------------------------------------------
163 
164 void BMidiLocalProducer::SprayProgramChange(
165 	uchar channel, uchar programNumber, bigtime_t time) const
166 {
167 	if (channel < 16)
168 	{
169 		uchar data[2];
170 		data[0] = B_PROGRAM_CHANGE + channel;
171 		data[1] = programNumber;
172 
173 		SprayEvent(&data, 2, true, time);
174 	}
175 	else
176 	{
177 		debugger("invalid MIDI channel");
178 	}
179 }
180 
181 //------------------------------------------------------------------------------
182 
183 void BMidiLocalProducer::SprayChannelPressure(
184 	uchar channel, uchar pressure, bigtime_t time) const
185 {
186 	if (channel < 16)
187 	{
188 		uchar data[2];
189 		data[0] = B_CHANNEL_PRESSURE + channel;
190 		data[1] = pressure;
191 
192 		SprayEvent(&data, 2, true, time);
193 	}
194 	else
195 	{
196 		debugger("invalid MIDI channel");
197 	}
198 }
199 
200 //------------------------------------------------------------------------------
201 
202 void BMidiLocalProducer::SprayPitchBend(
203 	uchar channel, uchar lsb, uchar msb, bigtime_t time) const
204 {
205 	if (channel < 16)
206 	{
207 		uchar data[3];
208 		data[0] = B_PITCH_BEND + channel;
209 		data[1] = lsb;
210 		data[2] = msb;
211 
212 		SprayEvent(&data, 3, true, time);
213 	}
214 	else
215 	{
216 		debugger("invalid MIDI channel");
217 	}
218 }
219 
220 //------------------------------------------------------------------------------
221 
222 void BMidiLocalProducer::SpraySystemExclusive(
223 	void* data, size_t length, bigtime_t time) const
224 {
225 	SprayEvent(data, length, true, time, true);
226 }
227 
228 //------------------------------------------------------------------------------
229 
230 void BMidiLocalProducer::SpraySystemCommon(
231 	uchar status, uchar data1, uchar data2, bigtime_t time) const
232 {
233 	size_t len;
234 	uchar data[3];
235 	data[0] = status;
236 	data[1] = data1;
237 	data[2] = data2;
238 
239 	switch (status)
240 	{
241 		case B_TUNE_REQUEST:
242 		case B_SYS_EX_END:
243 			len = 1; break;
244 
245 		case B_CABLE_MESSAGE:
246 		case B_MIDI_TIME_CODE:
247 		case B_SONG_SELECT:
248 			len = 2; break;
249 
250 		case B_SONG_POSITION:
251 			len = 3; break;
252 
253 		default:
254 			debugger("invalid system common status");
255 	}
256 
257 	SprayEvent(&data, len, true, time);
258 }
259 
260 //------------------------------------------------------------------------------
261 
262 void BMidiLocalProducer::SpraySystemRealTime(
263 	uchar status, bigtime_t time) const
264 {
265 	if (status >= B_TIMING_CLOCK)
266 	{
267 		SprayEvent(&status, 1, true, time);
268 	}
269 	else
270 	{
271 		debugger("invalid real time status");
272 	}
273 }
274 
275 //------------------------------------------------------------------------------
276 
277 void BMidiLocalProducer::SprayTempoChange(
278 	int32 beatsPerMinute, bigtime_t time) const
279 {
280 	int32 tempo = 60000000 / beatsPerMinute;
281 
282 	uchar data[6];
283 	data[0] = 0xFF;
284 	data[1] = 0x51;
285 	data[2] = 0x03;
286 	data[3] = tempo >> 16;
287 	data[4] = tempo >> 8;
288 	data[5] = tempo;
289 
290 	SprayEvent(&data, 6, true, time);
291 }
292 
293 //------------------------------------------------------------------------------
294 
295 void BMidiLocalProducer::_Reserved1() { }
296 void BMidiLocalProducer::_Reserved2() { }
297 void BMidiLocalProducer::_Reserved3() { }
298 void BMidiLocalProducer::_Reserved4() { }
299 void BMidiLocalProducer::_Reserved5() { }
300 void BMidiLocalProducer::_Reserved6() { }
301 void BMidiLocalProducer::_Reserved7() { }
302 void BMidiLocalProducer::_Reserved8() { }
303 
304 //------------------------------------------------------------------------------
305 
306 void BMidiLocalProducer::SprayEvent(
307 	const void* data, size_t length, bool atomic, bigtime_t time,
308 	bool sysex) const
309 {
310 	if (LockProducer())
311 	{
312 		if (CountConsumers() > 0)
313 		{
314 			// We don't just send the MIDI event data to all connected
315 			// consumers, we also send a header. The header contains our
316 			// ID (4 bytes), the consumer's ID (4 bytes), the performance
317 			// time (8 bytes), whether the data is atomic (1 byte), and
318 			// padding (3 bytes). The MIDI event data follows the header.
319 
320 			size_t buf_size = 20 + length;
321 			if (sysex) { buf_size += 2; }   // add 0xF0 and 0xF7 markers
322 
323 			uint8* buffer = (uint8*) malloc(buf_size);
324 			if (buffer != NULL)
325 			{
326 				*((uint32*)    (buffer +  0)) = id;
327 				*((bigtime_t*) (buffer +  8)) = time;
328 				*((uint32*)    (buffer + 16)) = 0;
329 				*((bool*)      (buffer + 16)) = atomic;
330 
331 				if (sysex)
332 				{
333 					*((uint8*) (buffer + 20)) = B_SYS_EX_START;
334 					if (data != NULL)
335 					{
336 						memcpy(buffer + 21, data, length);
337 					}
338 					*((uint8*) (buffer + buf_size - 1)) = B_SYS_EX_END;
339 				}
340 				else if (data != NULL)
341 				{
342 					memcpy(buffer + 20, data, length);
343 				}
344 
345 				for (int32 t = 0; t < CountConsumers(); ++t)
346 				{
347 					BMidiConsumer* cons = ConsumerAt(t);
348 					*((uint32*) (buffer + 4)) = cons->id;
349 
350 					#ifdef DEBUG
351 					printf("*** spraying: ");
352 					for (int32 t = 0; t < buf_size; ++t)
353 					{
354 						printf("%02X, ", buffer[t]);
355 					}
356 					printf("\n");
357 					#endif
358 
359 					write_port(cons->port, 0, buffer, buf_size);
360 				}
361 
362 				free(buffer);
363 			}
364 		}
365 
366 		UnlockProducer();
367 	}
368 }
369 
370 //------------------------------------------------------------------------------
371