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