1/* 2 * Copyright 2006, Haiku. 3 * 4 * Copyright (c) 2002-2003 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 <MidiRoster.h> 16#include "protocol.h" 17 18 19int32 20_midi_event_thread(void* data) 21{ 22 return ((BMidiLocalConsumer*) data)->EventThread(); 23} 24 25 26BMidiLocalConsumer::BMidiLocalConsumer(const char* name) 27 : BMidiConsumer(name) 28{ 29 TRACE(("BMidiLocalConsumer::BMidiLocalConsumer")) 30 31 fIsLocal = true; 32 fRefCount = 1; 33 fTimeout = (bigtime_t) -1; 34 fTimeoutData = NULL; 35 36 fPort = create_port(1, "MidiEventPort"); 37 fThread = spawn_thread( 38 _midi_event_thread, "MidiEventThread", B_REAL_TIME_PRIORITY, this); 39 resume_thread(fThread); 40 41 BMidiRoster::MidiRoster()->CreateLocal(this); 42} 43 44 45BMidiLocalConsumer::~BMidiLocalConsumer() 46{ 47 TRACE(("BMidiLocalConsumer::~BMidiLocalConsumer")) 48 49 BMidiRoster::MidiRoster()->DeleteLocal(this); 50 51 delete_port(fPort); 52 53 status_t result; 54 wait_for_thread(fThread, &result); 55} 56 57 58void 59BMidiLocalConsumer::SetLatency(bigtime_t latency_) 60{ 61 if (latency_ < 0) { 62 WARN("SetLatency() does not accept negative values"); 63 return; 64 } else if (!IsValid()) { 65 return; 66 } else if (fLatency != latency_) { 67 BMessage msg; 68 msg.AddInt64("midi:latency", latency_); 69 70 if (SendChangeRequest(&msg) == B_OK) { 71 if (LockLooper()) { 72 fLatency = latency_; 73 UnlockLooper(); 74 } 75 } 76 } 77} 78 79 80int32 81BMidiLocalConsumer::GetProducerID() 82{ 83 return fCurrentProducer; 84} 85 86 87void 88BMidiLocalConsumer::SetTimeout(bigtime_t when, void* data) 89{ 90 fTimeout = when; 91 fTimeoutData = data; 92} 93 94 95void 96BMidiLocalConsumer::Timeout(void* data) 97{ 98 // Do nothing. 99} 100 101 102void 103BMidiLocalConsumer::Data(uchar* data, size_t length, bool atomic, bigtime_t time) 104{ 105 if (atomic) { 106 switch (data[0] & 0xF0) { 107 case B_NOTE_OFF: 108 { 109 if (length == 3) 110 NoteOff(data[0] & 0x0F, data[1], data[2], time); 111 break; 112 } 113 114 case B_NOTE_ON: 115 { 116 if (length == 3) 117 NoteOn(data[0] & 0x0F, data[1], data[2], time); 118 break; 119 } 120 121 case B_KEY_PRESSURE: 122 { 123 if (length == 3) 124 KeyPressure(data[0] & 0x0F, data[1], data[2], time); 125 break; 126 } 127 128 case B_CONTROL_CHANGE: 129 { 130 if (length == 3) 131 ControlChange(data[0] & 0x0F, data[1], data[2], time); 132 break; 133 } 134 135 case B_PROGRAM_CHANGE: 136 { 137 if (length == 2) 138 ProgramChange(data[0] & 0x0F, data[1], time); 139 break; 140 } 141 142 case B_CHANNEL_PRESSURE: 143 { 144 if (length == 2) 145 ChannelPressure(data[0] & 0x0F, data[1], time); 146 break; 147 } 148 149 case B_PITCH_BEND: 150 { 151 if (length == 3) 152 PitchBend(data[0] & 0x0F, data[1], data[2], time); 153 break; 154 } 155 156 case 0xF0: 157 { 158 switch (data[0]) { 159 case B_SYS_EX_START: 160 { 161 if (data[length - 1] == B_SYS_EX_END) { 162 SystemExclusive(data + 1, length - 2, time); 163 } else { // sysex-end is not required 164 SystemExclusive(data + 1, length - 1, time); 165 } 166 break; 167 } 168 169 case B_TUNE_REQUEST: 170 case B_SYS_EX_END: 171 { 172 if (length == 1) { 173 SystemCommon(data[0], 0, 0, time); 174 } 175 break; 176 } 177 178 case B_CABLE_MESSAGE: 179 case B_MIDI_TIME_CODE: 180 case B_SONG_SELECT: 181 { 182 if (length == 2) { 183 SystemCommon(data[0], data[1], 0, time); 184 } 185 break; 186 } 187 188 case B_SONG_POSITION: 189 { 190 if (length == 3) { 191 SystemCommon(data[0], data[1], data[2], time); 192 } 193 break; 194 } 195 196 case B_TIMING_CLOCK: 197 case B_START: 198 case B_CONTINUE: 199 case B_STOP: 200 case B_ACTIVE_SENSING: 201 { 202 if (length == 1) { 203 SystemRealTime(data[0], time); 204 } 205 break; 206 } 207 208 case B_SYSTEM_RESET: 209 { 210 if (length == 1) { 211 SystemRealTime(data[0], time); 212 } else if ((length == 6) && (data[1] == 0x51) 213 && (data[2] == 0x03)) { 214 int32 tempo = 215 (data[3] << 16) | (data[4] << 8) | data[5]; 216 217 TempoChange(60000000/tempo, time); 218 } 219 } 220 } 221 break; 222 } 223 } 224 } 225} 226 227 228void 229BMidiLocalConsumer::NoteOff(uchar channel, uchar note, uchar velocity, bigtime_t time) 230{ 231 // Do nothing. 232} 233 234 235void 236BMidiLocalConsumer::NoteOn(uchar channel, uchar note, uchar velocity, bigtime_t time) 237{ 238 // Do nothing. 239} 240 241 242void 243BMidiLocalConsumer::KeyPressure(uchar channel, uchar note, uchar pressure, bigtime_t time) 244{ 245 // Do nothing. 246} 247 248 249void 250BMidiLocalConsumer::ControlChange(uchar channel, uchar controlNumber, uchar controlValue, bigtime_t time) 251{ 252 // Do nothing. 253} 254 255 256void 257BMidiLocalConsumer::ProgramChange(uchar channel, uchar programNumber, bigtime_t time) 258{ 259 // Do nothing. 260} 261 262 263void BMidiLocalConsumer::ChannelPressure(uchar channel, uchar pressure, bigtime_t time) 264{ 265 // Do nothing. 266} 267 268 269void 270BMidiLocalConsumer::PitchBend(uchar channel, uchar lsb, uchar msb, bigtime_t time) 271{ 272 // Do nothing. 273} 274 275 276void 277BMidiLocalConsumer::SystemExclusive( 278 void* data, size_t length, bigtime_t time) 279{ 280 // Do nothing. 281} 282 283 284void 285BMidiLocalConsumer::SystemCommon( 286 uchar statusByte, uchar data1, uchar data2, bigtime_t time) 287{ 288 // Do nothing. 289} 290 291 292void 293BMidiLocalConsumer::SystemRealTime(uchar statusByte, bigtime_t time) 294{ 295 // Do nothing. 296} 297 298 299void 300BMidiLocalConsumer::TempoChange(int32 beatsPerMinute, bigtime_t time) 301{ 302 // Do nothing. 303} 304 305 306void 307BMidiLocalConsumer::AllNotesOff(bool justChannel, bigtime_t time) 308{ 309 // Do nothing. 310} 311 312 313void BMidiLocalConsumer::_Reserved1() { } 314void BMidiLocalConsumer::_Reserved2() { } 315void BMidiLocalConsumer::_Reserved3() { } 316void BMidiLocalConsumer::_Reserved4() { } 317void BMidiLocalConsumer::_Reserved5() { } 318void BMidiLocalConsumer::_Reserved6() { } 319void BMidiLocalConsumer::_Reserved7() { } 320void BMidiLocalConsumer::_Reserved8() { } 321 322 323int32 324BMidiLocalConsumer::EventThread() 325{ 326 int32 msg_code; 327 ssize_t msg_size; 328 ssize_t buf_size = 100; 329 uint8* buffer = (uint8*) malloc(buf_size); 330 331 while (true) { 332 if (fTimeout == (bigtime_t) -1) { 333 msg_size = port_buffer_size(fPort); 334 } else { // have timeout 335 msg_size = port_buffer_size_etc(fPort, B_ABSOLUTE_TIMEOUT, fTimeout); 336 if (msg_size == B_TIMED_OUT) { 337 Timeout(fTimeoutData); 338 fTimeout = (bigtime_t) -1; 339 fTimeoutData = NULL; 340 continue; 341 } 342 } 343 344 if (msg_size < 0) 345 break; // error reading port 346 347 if (msg_size > buf_size) { 348 uint8* tmp_buffer = (uint8*) realloc(buffer, msg_size); 349 if (tmp_buffer == NULL) 350 break; // error in realloc() 351 buffer = tmp_buffer; 352 buf_size = msg_size; 353 } 354 355 read_port(fPort, &msg_code, buffer, msg_size); 356 357 if (msg_size > 20) { // minimum valid size 358 #ifdef DEBUG 359 printf("*** received: "); 360 for (int32 t = 0; t < msg_size; ++t) { 361 printf("%02X, ", ((uint8*) buffer)[t]); 362 } 363 printf("\n"); 364 #endif 365 366 fCurrentProducer = *((uint32*) (buffer + 0)); 367 int32 targetId = *((uint32*) (buffer + 4)); 368 bigtime_t time = *((bigtime_t*) (buffer + 8)); 369 bool atomic = *((bool*) (buffer + 16)); 370 371 if (targetId == fId) { // only if we are the destination 372 Data((uchar*) (buffer + 20), msg_size - 20, atomic, time); 373 } 374 } 375 } 376 377 free(buffer); 378 return 0; 379} 380 381