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