diff options
Diffstat (limited to 'portmidi/pm_haiku')
| -rw-r--r-- | portmidi/pm_haiku/pmhaiku.cpp | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/portmidi/pm_haiku/pmhaiku.cpp b/portmidi/pm_haiku/pmhaiku.cpp new file mode 100644 index 0000000..0c592f1 --- /dev/null +++ b/portmidi/pm_haiku/pmhaiku.cpp | |||
| @@ -0,0 +1,473 @@ | |||
| 1 | /* pmhaiku.cpp -- PortMidi os-dependent code */ | ||
| 2 | |||
| 3 | #include <stdio.h> | ||
| 4 | #include <stdlib.h> | ||
| 5 | #include <vector> | ||
| 6 | #include <MidiConsumer.h> | ||
| 7 | #include <MidiEndpoint.h> | ||
| 8 | #include <MidiProducer.h> | ||
| 9 | #include <MidiRoster.h> | ||
| 10 | #include <MidiSynth.h> | ||
| 11 | #include "portmidi.h" | ||
| 12 | #include "pmutil.h" | ||
| 13 | #include "pminternal.h" | ||
| 14 | |||
| 15 | namespace { | ||
| 16 | struct PmInputConsumer : BMidiLocalConsumer { | ||
| 17 | PmInputConsumer(PmInternal *midi) : | ||
| 18 | BMidiLocalConsumer("PortMidi input consumer"), | ||
| 19 | midi(midi) | ||
| 20 | { | ||
| 21 | } | ||
| 22 | |||
| 23 | |||
| 24 | void Data(uchar *data, size_t length, bool atomic, bigtime_t time) | ||
| 25 | { | ||
| 26 | if (!atomic) | ||
| 27 | return; // should these be also supported? | ||
| 28 | |||
| 29 | if (data[0] == B_SYS_EX_START) { | ||
| 30 | pm_read_bytes(midi, data, length, time / 1000); | ||
| 31 | } else { | ||
| 32 | PmEvent event; | ||
| 33 | switch (length) { | ||
| 34 | case 1: | ||
| 35 | event.message = Pm_Message(data[0], 0, 0); | ||
| 36 | break; | ||
| 37 | case 2: | ||
| 38 | event.message = Pm_Message(data[0], data[1], 0); | ||
| 39 | break; | ||
| 40 | case 3: | ||
| 41 | event.message = Pm_Message(data[0], data[1], data[2]); | ||
| 42 | break; | ||
| 43 | default: | ||
| 44 | printf("Unexpected message length for short message, got %" B_PRIuSIZE "\n", length); | ||
| 45 | break; | ||
| 46 | } | ||
| 47 | event.timestamp = time / 1000; | ||
| 48 | pm_read_short(midi, &event); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | private: | ||
| 53 | PmInternal *midi; | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct PmOutputInfo { | ||
| 57 | BMidiLocalProducer *producer; | ||
| 58 | std::vector<unsigned char> sysexBuffer; | ||
| 59 | }; | ||
| 60 | |||
| 61 | struct PmSynthOutputInfo { | ||
| 62 | BMidiSynth midiSynth; | ||
| 63 | std::vector<unsigned char> sysexBuffer; | ||
| 64 | }; | ||
| 65 | |||
| 66 | |||
| 67 | PmTimestamp synchronize(PmInternal *midi) | ||
| 68 | { | ||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | |||
| 73 | PmError in_open(PmInternal *midi, void *driverInfo) | ||
| 74 | { | ||
| 75 | int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; | ||
| 76 | BMidiProducer *producer = BMidiRoster::FindProducer(endpointID); | ||
| 77 | if (!producer) | ||
| 78 | return pmInvalidDeviceId; | ||
| 79 | PmInputConsumer *consumer = new PmInputConsumer(midi); | ||
| 80 | status_t status = producer->Connect(consumer); | ||
| 81 | if (status != B_OK) { | ||
| 82 | consumer->Release(); | ||
| 83 | producer->Release(); | ||
| 84 | strcpy(pm_hosterror_text, strerror(status)); | ||
| 85 | pm_hosterror = TRUE; | ||
| 86 | return pmHostError; | ||
| 87 | } | ||
| 88 | midi->api_info = consumer; | ||
| 89 | producer->Release(); | ||
| 90 | return pmNoError; | ||
| 91 | } | ||
| 92 | |||
| 93 | |||
| 94 | PmError in_abort(PmInternal *midi) | ||
| 95 | { | ||
| 96 | return pmNoError; | ||
| 97 | } | ||
| 98 | |||
| 99 | |||
| 100 | PmError in_close(PmInternal *midi) | ||
| 101 | { | ||
| 102 | int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; | ||
| 103 | BMidiProducer *producer = BMidiRoster::FindProducer(endpointID); | ||
| 104 | if (!producer) | ||
| 105 | return pmInvalidDeviceId; | ||
| 106 | PmInputConsumer *consumer = (PmInputConsumer*)midi->api_info; | ||
| 107 | status_t status = producer->Disconnect(consumer); | ||
| 108 | if (status != B_OK) { | ||
| 109 | consumer->Release(); | ||
| 110 | producer->Release(); | ||
| 111 | strcpy(pm_hosterror_text, strerror(status)); | ||
| 112 | pm_hosterror = TRUE; | ||
| 113 | return pmHostError; | ||
| 114 | } | ||
| 115 | consumer->Release(); | ||
| 116 | midi->api_info = NULL; | ||
| 117 | producer->Release(); | ||
| 118 | return pmNoError; | ||
| 119 | } | ||
| 120 | |||
| 121 | |||
| 122 | PmError out_open(PmInternal *midi, void *driverInfo) | ||
| 123 | { | ||
| 124 | int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; | ||
| 125 | BMidiConsumer *consumer = BMidiRoster::FindConsumer(endpointID); | ||
| 126 | if (!consumer) | ||
| 127 | return pmInvalidDeviceId; | ||
| 128 | BMidiLocalProducer *producer = new BMidiLocalProducer("PortMidi output producer"); | ||
| 129 | status_t status = producer->Connect(consumer); | ||
| 130 | if (status != B_OK) { | ||
| 131 | consumer->Release(); | ||
| 132 | producer->Release(); | ||
| 133 | strcpy(pm_hosterror_text, strerror(status)); | ||
| 134 | pm_hosterror = TRUE; | ||
| 135 | return pmHostError; | ||
| 136 | } | ||
| 137 | PmOutputInfo *info = new PmOutputInfo; | ||
| 138 | info->producer = producer; | ||
| 139 | midi->api_info = info; | ||
| 140 | consumer->Release(); | ||
| 141 | return pmNoError; | ||
| 142 | } | ||
| 143 | |||
| 144 | |||
| 145 | PmError out_abort(PmInternal *midi) | ||
| 146 | { | ||
| 147 | return pmNoError; | ||
| 148 | } | ||
| 149 | |||
| 150 | |||
| 151 | PmError out_close(PmInternal *midi) | ||
| 152 | { | ||
| 153 | int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; | ||
| 154 | BMidiConsumer *consumer = BMidiRoster::FindConsumer(endpointID); | ||
| 155 | if (!consumer) | ||
| 156 | return pmInvalidDeviceId; | ||
| 157 | PmOutputInfo *info = (PmOutputInfo*)midi->api_info; | ||
| 158 | status_t status = info->producer->Disconnect(consumer); | ||
| 159 | if (status != B_OK) { | ||
| 160 | consumer->Release(); | ||
| 161 | midi->api_info = NULL; | ||
| 162 | info->producer->Release(); | ||
| 163 | delete info; | ||
| 164 | strcpy(pm_hosterror_text, strerror(status)); | ||
| 165 | pm_hosterror = TRUE; | ||
| 166 | return pmHostError; | ||
| 167 | } | ||
| 168 | consumer->Release(); | ||
| 169 | midi->api_info = NULL; | ||
| 170 | info->producer->Release(); | ||
| 171 | delete info; | ||
| 172 | return pmNoError; | ||
| 173 | } | ||
| 174 | |||
| 175 | |||
| 176 | PmError write_short(PmInternal *midi, PmEvent *buffer) | ||
| 177 | { | ||
| 178 | PmOutputInfo *info = (PmOutputInfo*)midi->api_info; | ||
| 179 | uchar data[3]; | ||
| 180 | data[0] = Pm_MessageStatus(buffer->message); | ||
| 181 | data[1] = Pm_MessageData1(buffer->message); | ||
| 182 | data[2] = Pm_MessageData2(buffer->message); | ||
| 183 | size_t length = pm_midi_length(data[0]); | ||
| 184 | |||
| 185 | info->producer->SprayData(data, length, true, buffer->timestamp * 1000); | ||
| 186 | |||
| 187 | // TODO: handle latency != 0 | ||
| 188 | return pmNoError; | ||
| 189 | } | ||
| 190 | |||
| 191 | |||
| 192 | PmError begin_sysex(PmInternal *midi, PmTimestamp timestamp) | ||
| 193 | { | ||
| 194 | PmOutputInfo *info = (PmOutputInfo*)midi->api_info; | ||
| 195 | info->sysexBuffer.clear(); | ||
| 196 | return pmNoError; | ||
| 197 | } | ||
| 198 | |||
| 199 | |||
| 200 | PmError end_sysex(PmInternal *midi, PmTimestamp timestamp) | ||
| 201 | { | ||
| 202 | PmOutputInfo *info = (PmOutputInfo*)midi->api_info; | ||
| 203 | info->producer->SpraySystemExclusive(&info->sysexBuffer[0], info->sysexBuffer.size(), timestamp * 1000); | ||
| 204 | info->sysexBuffer.clear(); | ||
| 205 | return pmNoError; | ||
| 206 | } | ||
| 207 | |||
| 208 | |||
| 209 | PmError write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp) | ||
| 210 | { | ||
| 211 | PmOutputInfo *info = (PmOutputInfo*)midi->api_info; | ||
| 212 | info->sysexBuffer.push_back(byte); | ||
| 213 | return pmNoError; | ||
| 214 | } | ||
| 215 | |||
| 216 | |||
| 217 | PmError write_realtime(PmInternal *midi, PmEvent *buffer) | ||
| 218 | { | ||
| 219 | PmOutputInfo *info = (PmOutputInfo*)midi->api_info; | ||
| 220 | info->producer->SpraySystemRealTime(Pm_MessageStatus(buffer->message), buffer->timestamp * 1000); | ||
| 221 | return pmNoError; | ||
| 222 | } | ||
| 223 | |||
| 224 | |||
| 225 | PmError synth_open(PmInternal *midi, void *driverInfo) | ||
| 226 | { | ||
| 227 | PmSynthOutputInfo *info = new PmSynthOutputInfo; | ||
| 228 | info->midiSynth.EnableInput(true, true); | ||
| 229 | midi->api_info = info; | ||
| 230 | return pmNoError; | ||
| 231 | } | ||
| 232 | |||
| 233 | |||
| 234 | PmError synth_abort(PmInternal *midi) | ||
| 235 | { | ||
| 236 | return pmNoError; | ||
| 237 | } | ||
| 238 | |||
| 239 | |||
| 240 | PmError synth_close(PmInternal *midi) | ||
| 241 | { | ||
| 242 | PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; | ||
| 243 | delete info; | ||
| 244 | midi->api_info = NULL; | ||
| 245 | return pmNoError; | ||
| 246 | } | ||
| 247 | |||
| 248 | |||
| 249 | PmError write_short_synth(PmInternal *midi, PmEvent *buffer) | ||
| 250 | { | ||
| 251 | PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; | ||
| 252 | uchar data[3]; | ||
| 253 | data[0] = Pm_MessageStatus(buffer->message); | ||
| 254 | data[1] = Pm_MessageData1(buffer->message); | ||
| 255 | data[2] = Pm_MessageData2(buffer->message); | ||
| 256 | |||
| 257 | switch(data[0] & 0xf0) { | ||
| 258 | case B_NOTE_OFF: | ||
| 259 | info->midiSynth.NoteOff((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); | ||
| 260 | break; | ||
| 261 | case B_NOTE_ON: | ||
| 262 | info->midiSynth.NoteOn((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); | ||
| 263 | break; | ||
| 264 | case B_KEY_PRESSURE: | ||
| 265 | info->midiSynth.KeyPressure((data[0] & 0x0f + 1), data[1], data[2], buffer->timestamp); | ||
| 266 | break; | ||
| 267 | case B_CONTROL_CHANGE: | ||
| 268 | info->midiSynth.ControlChange((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); | ||
| 269 | break; | ||
| 270 | case B_PROGRAM_CHANGE: | ||
| 271 | info->midiSynth.ProgramChange((data[0] & 0x0f) + 1, data[1], buffer->timestamp); | ||
| 272 | break; | ||
| 273 | case B_CHANNEL_PRESSURE: | ||
| 274 | info->midiSynth.ChannelPressure((data[0] & 0x0f) + 1, data[1], buffer->timestamp); | ||
| 275 | break; | ||
| 276 | case B_PITCH_BEND: | ||
| 277 | info->midiSynth.PitchBend((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); | ||
| 278 | break; | ||
| 279 | } | ||
| 280 | |||
| 281 | // TODO: handle latency != 0 | ||
| 282 | return pmNoError; | ||
| 283 | } | ||
| 284 | |||
| 285 | |||
| 286 | PmError begin_sysex_synth(PmInternal *midi, PmTimestamp timestamp) | ||
| 287 | { | ||
| 288 | PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; | ||
| 289 | info->sysexBuffer.clear(); | ||
| 290 | return pmNoError; | ||
| 291 | } | ||
| 292 | |||
| 293 | |||
| 294 | PmError end_sysex_synth(PmInternal *midi, PmTimestamp timestamp) | ||
| 295 | { | ||
| 296 | PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; | ||
| 297 | info->midiSynth.SystemExclusive(&info->sysexBuffer[0], info->sysexBuffer.size(), timestamp); | ||
| 298 | info->sysexBuffer.clear(); | ||
| 299 | return pmNoError; | ||
| 300 | } | ||
| 301 | |||
| 302 | |||
| 303 | PmError write_byte_synth(PmInternal *midi, unsigned char byte, PmTimestamp timestamp) | ||
| 304 | { | ||
| 305 | PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; | ||
| 306 | info->sysexBuffer.push_back(byte); | ||
| 307 | return pmNoError; | ||
| 308 | } | ||
| 309 | |||
| 310 | |||
| 311 | PmError write_realtime_synth(PmInternal *midi, PmEvent *buffer) | ||
| 312 | { | ||
| 313 | PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; | ||
| 314 | info->midiSynth.SystemRealTime(Pm_MessageStatus(buffer->message), buffer->timestamp); | ||
| 315 | return pmNoError; | ||
| 316 | } | ||
| 317 | |||
| 318 | |||
| 319 | PmError write_flush(PmInternal *midi, PmTimestamp timestamp) | ||
| 320 | { | ||
| 321 | return pmNoError; | ||
| 322 | } | ||
| 323 | |||
| 324 | |||
| 325 | unsigned int check_host_error(PmInternal *midi) | ||
| 326 | { | ||
| 327 | return 0; | ||
| 328 | } | ||
| 329 | |||
| 330 | |||
| 331 | pm_fns_node pm_in_dictionary = { | ||
| 332 | none_write_short, | ||
| 333 | none_sysex, | ||
| 334 | none_sysex, | ||
| 335 | none_write_byte, | ||
| 336 | none_write_short, | ||
| 337 | none_write_flush, | ||
| 338 | synchronize, | ||
| 339 | in_open, | ||
| 340 | in_abort, | ||
| 341 | in_close, | ||
| 342 | success_poll, | ||
| 343 | check_host_error | ||
| 344 | }; | ||
| 345 | |||
| 346 | pm_fns_node pm_out_dictionary = { | ||
| 347 | write_short, | ||
| 348 | begin_sysex, | ||
| 349 | end_sysex, | ||
| 350 | write_byte, | ||
| 351 | write_realtime, | ||
| 352 | write_flush, | ||
| 353 | synchronize, | ||
| 354 | out_open, | ||
| 355 | out_abort, | ||
| 356 | out_close, | ||
| 357 | none_poll, | ||
| 358 | check_host_error | ||
| 359 | }; | ||
| 360 | |||
| 361 | |||
| 362 | pm_fns_node pm_synth_dictionary = { | ||
| 363 | write_short_synth, | ||
| 364 | begin_sysex_synth, | ||
| 365 | end_sysex_synth, | ||
| 366 | write_byte_synth, | ||
| 367 | write_realtime_synth, | ||
| 368 | write_flush, | ||
| 369 | synchronize, | ||
| 370 | synth_open, | ||
| 371 | synth_abort, | ||
| 372 | synth_close, | ||
| 373 | none_poll, | ||
| 374 | check_host_error | ||
| 375 | }; | ||
| 376 | |||
| 377 | |||
| 378 | PmError create_virtual(int is_input, const char *name, void *driverInfo) | ||
| 379 | { | ||
| 380 | BMidiEndpoint *endpoint = is_input ? (BMidiEndpoint*)new BMidiLocalProducer(name) : new BMidiLocalConsumer(name); | ||
| 381 | if (!endpoint->IsValid()) { | ||
| 382 | endpoint->Release(); | ||
| 383 | strcpy(pm_hosterror_text, "Endpoint could not be created"); | ||
| 384 | pm_hosterror = TRUE; | ||
| 385 | return pmHostError; | ||
| 386 | } | ||
| 387 | status_t status = endpoint->Register(); | ||
| 388 | if (status != B_OK) { | ||
| 389 | endpoint->Release(); | ||
| 390 | strcpy(pm_hosterror_text, strerror(status)); | ||
| 391 | pm_hosterror = TRUE; | ||
| 392 | return pmHostError; | ||
| 393 | } | ||
| 394 | return pm_add_device(const_cast<char*>("Haiku MIDI kit"), name, is_input, TRUE, (void*)(intptr_t)endpoint->ID(), is_input ? &pm_in_dictionary : &pm_out_dictionary); | ||
| 395 | } | ||
| 396 | |||
| 397 | PmError delete_virtual(PmDeviceID id) | ||
| 398 | { | ||
| 399 | int32 endpointID = (int32)(intptr_t)pm_descriptors[id].descriptor; | ||
| 400 | BMidiEndpoint *endpoint = BMidiRoster::FindEndpoint(endpointID); | ||
| 401 | //TODO: handle connected producers and consumers | ||
| 402 | status_t status = endpoint->Unregister(); | ||
| 403 | // release twice to actually free the endpoint (FindEndpoint increases the ref-count) | ||
| 404 | endpoint->Release(); | ||
| 405 | endpoint->Release(); | ||
| 406 | if (status != B_OK) { | ||
| 407 | strcpy(pm_hosterror_text, strerror(status)); | ||
| 408 | pm_hosterror = TRUE; | ||
| 409 | return pmHostError; | ||
| 410 | } | ||
| 411 | return pmNoError; | ||
| 412 | } | ||
| 413 | } | ||
| 414 | |||
| 415 | extern "C" { | ||
| 416 | void pm_init() | ||
| 417 | { | ||
| 418 | pm_add_interf(const_cast<char*>("Haiku MIDI kit"), create_virtual, delete_virtual); | ||
| 419 | |||
| 420 | pm_add_device(const_cast<char*>("Haiku MIDI kit"), "Soft Synth", FALSE, FALSE, NULL, &pm_synth_dictionary); | ||
| 421 | |||
| 422 | int32 id = 0; | ||
| 423 | BMidiEndpoint *endpoint; | ||
| 424 | |||
| 425 | while ((endpoint = BMidiRoster::NextEndpoint(&id)) != NULL) { | ||
| 426 | bool isInput = endpoint->IsProducer(); | ||
| 427 | pm_add_device(const_cast<char*>("Haiku MIDI kit"), endpoint->Name(), isInput, FALSE, (void*)(intptr_t)id, isInput ? &pm_in_dictionary : &pm_out_dictionary); | ||
| 428 | endpoint->Release(); | ||
| 429 | } | ||
| 430 | } | ||
| 431 | |||
| 432 | |||
| 433 | void pm_term() | ||
| 434 | { | ||
| 435 | int i; | ||
| 436 | for (i = 0; i < pm_descriptor_len; i++) { | ||
| 437 | PmInternal *midi = pm_descriptors[i].pm_internal; | ||
| 438 | if (midi && midi->api_info) { | ||
| 439 | // device is still open, close it | ||
| 440 | (*midi->dictionary->close)(midi); | ||
| 441 | } | ||
| 442 | if (pm_descriptors[i].pub.is_virtual && !pm_descriptors[i].deleted) { | ||
| 443 | delete_virtual(i); | ||
| 444 | } | ||
| 445 | } | ||
| 446 | } | ||
| 447 | |||
| 448 | |||
| 449 | PmDeviceID Pm_GetDefaultInputDeviceID() | ||
| 450 | { | ||
| 451 | Pm_Initialize(); | ||
| 452 | return pm_default_input_device_id; | ||
| 453 | } | ||
| 454 | |||
| 455 | |||
| 456 | PmDeviceID Pm_GetDefaultOutputDeviceID() | ||
| 457 | { | ||
| 458 | Pm_Initialize(); | ||
| 459 | return pm_default_output_device_id; | ||
| 460 | } | ||
| 461 | |||
| 462 | |||
| 463 | void *pm_alloc(size_t s) | ||
| 464 | { | ||
| 465 | return malloc(s); | ||
| 466 | } | ||
| 467 | |||
| 468 | |||
| 469 | void pm_free(void *ptr) | ||
| 470 | { | ||
| 471 | free(ptr); | ||
| 472 | } | ||
| 473 | } | ||
