diff options
Diffstat (limited to '3rdparty/portmidi/pm_haiku/pmhaiku.cpp')
-rw-r--r-- | 3rdparty/portmidi/pm_haiku/pmhaiku.cpp | 514 |
1 files changed, 514 insertions, 0 deletions
diff --git a/3rdparty/portmidi/pm_haiku/pmhaiku.cpp b/3rdparty/portmidi/pm_haiku/pmhaiku.cpp new file mode 100644 index 00000000000..6d21d8c5a67 --- /dev/null +++ b/3rdparty/portmidi/pm_haiku/pmhaiku.cpp @@ -0,0 +1,514 @@ +/* pmhaiku.cpp -- PortMidi os-dependent code */ + +#include <stdio.h> +#include <stdlib.h> +#include <vector> +#include <MidiConsumer.h> +#include <MidiEndpoint.h> +#include <MidiProducer.h> +#include <MidiRoster.h> +#include <MidiSynth.h> +#include "portmidi.h" +#include "pmutil.h" +#include "pminternal.h" + +// as long as Java on Haiku uses ~/.java/... to store user preferences, we can directly reuse the Linux version +// (it shouldn't, see https://github.com/haikuports/haikuports/issues/7119) +extern "C" { +#include "../pm_linux/finddefault.h" +} + +namespace { + struct PmInputConsumer : BMidiLocalConsumer { + PmInputConsumer(PmInternal *midi) : + BMidiLocalConsumer("PortMidi input consumer"), + midi(midi) + { + } + + + void Data(uchar *data, size_t length, bool atomic, bigtime_t time) + { + if (!atomic) + return; // should these be also supported? + + if (data[0] == B_SYS_EX_START) { + pm_read_bytes(midi, data, length, time / 1000); + } else { + PmEvent event; + switch (length) { + case 1: + event.message = Pm_Message(data[0], 0, 0); + break; + case 2: + event.message = Pm_Message(data[0], data[1], 0); + break; + case 3: + event.message = Pm_Message(data[0], data[1], data[2]); + break; + default: + printf("Unexpected message length for short message, got %" B_PRIuSIZE "\n", length); + break; + } + event.timestamp = time / 1000; + pm_read_short(midi, &event); + } + } + + private: + PmInternal *midi; + }; + + struct PmOutputInfo { + BMidiLocalProducer *producer; + std::vector<unsigned char> sysexBuffer; + }; + + struct PmSynthOutputInfo { + BMidiSynth midiSynth; + std::vector<unsigned char> sysexBuffer; + }; + + + PmTimestamp synchronize(PmInternal *midi) + { + return 0; + } + + + PmError in_open(PmInternal *midi, void *driverInfo) + { + int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; + BMidiProducer *producer = BMidiRoster::FindProducer(endpointID); + if (!producer) + return pmInvalidDeviceId; + PmInputConsumer *consumer = new PmInputConsumer(midi); + status_t status = producer->Connect(consumer); + if (status != B_OK) { + consumer->Release(); + producer->Release(); + strcpy(pm_hosterror_text, strerror(status)); + pm_hosterror = TRUE; + return pmHostError; + } + midi->api_info = consumer; + producer->Release(); + return pmNoError; + } + + + PmError in_abort(PmInternal *midi) + { + return pmNoError; + } + + + PmError in_close(PmInternal *midi) + { + int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; + BMidiProducer *producer = BMidiRoster::FindProducer(endpointID); + if (!producer) + return pmInvalidDeviceId; + PmInputConsumer *consumer = (PmInputConsumer*)midi->api_info; + status_t status = producer->Disconnect(consumer); + if (status != B_OK) { + consumer->Release(); + producer->Release(); + strcpy(pm_hosterror_text, strerror(status)); + pm_hosterror = TRUE; + return pmHostError; + } + consumer->Release(); + midi->api_info = NULL; + producer->Release(); + return pmNoError; + } + + + PmError out_open(PmInternal *midi, void *driverInfo) + { + int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; + BMidiConsumer *consumer = BMidiRoster::FindConsumer(endpointID); + if (!consumer) + return pmInvalidDeviceId; + BMidiLocalProducer *producer = new BMidiLocalProducer("PortMidi output producer"); + status_t status = producer->Connect(consumer); + if (status != B_OK) { + consumer->Release(); + producer->Release(); + strcpy(pm_hosterror_text, strerror(status)); + pm_hosterror = TRUE; + return pmHostError; + } + PmOutputInfo *info = new PmOutputInfo; + info->producer = producer; + midi->api_info = info; + consumer->Release(); + return pmNoError; + } + + + PmError out_abort(PmInternal *midi) + { + return pmNoError; + } + + + PmError out_close(PmInternal *midi) + { + int32 endpointID = (int32)(intptr_t)pm_descriptors[midi->device_id].descriptor; + BMidiConsumer *consumer = BMidiRoster::FindConsumer(endpointID); + if (!consumer) + return pmInvalidDeviceId; + PmOutputInfo *info = (PmOutputInfo*)midi->api_info; + status_t status = info->producer->Disconnect(consumer); + if (status != B_OK) { + consumer->Release(); + midi->api_info = NULL; + info->producer->Release(); + delete info; + strcpy(pm_hosterror_text, strerror(status)); + pm_hosterror = TRUE; + return pmHostError; + } + consumer->Release(); + midi->api_info = NULL; + info->producer->Release(); + delete info; + return pmNoError; + } + + + // copied from the macOS implementation + size_t midi_length(uchar status) + { + int high, low; + static size_t high_lengths[] = { + 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */ + 3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */ + }; + static size_t low_lengths[] = { + 1, 2, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf8 */ + 1, 1, 1, 1, 1, 1, 1, 1 /* 0xf9 through 0xff */ + }; + + high = status >> 4; + low = status & 15; + + return (high != 0xF) ? high_lengths[high] : low_lengths[low]; + } + + + PmError write_short(PmInternal *midi, PmEvent *buffer) + { + PmOutputInfo *info = (PmOutputInfo*)midi->api_info; + uchar data[3]; + data[0] = Pm_MessageStatus(buffer->message); + data[1] = Pm_MessageData1(buffer->message); + data[2] = Pm_MessageData2(buffer->message); + size_t length = midi_length(data[0]); + + info->producer->SprayData(data, length, true, buffer->timestamp * 1000); + + // TODO: handle latency != 0 + return pmNoError; + } + + + PmError begin_sysex(PmInternal *midi, PmTimestamp timestamp) + { + PmOutputInfo *info = (PmOutputInfo*)midi->api_info; + info->sysexBuffer.clear(); + return pmNoError; + } + + + PmError end_sysex(PmInternal *midi, PmTimestamp timestamp) + { + PmOutputInfo *info = (PmOutputInfo*)midi->api_info; + info->producer->SpraySystemExclusive(&info->sysexBuffer[0], info->sysexBuffer.size(), timestamp * 1000); + info->sysexBuffer.clear(); + return pmNoError; + } + + + PmError write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp) + { + PmOutputInfo *info = (PmOutputInfo*)midi->api_info; + info->sysexBuffer.push_back(byte); + return pmNoError; + } + + + PmError write_realtime(PmInternal *midi, PmEvent *buffer) + { + PmOutputInfo *info = (PmOutputInfo*)midi->api_info; + info->producer->SpraySystemRealTime(Pm_MessageStatus(buffer->message), buffer->timestamp * 1000); + return pmNoError; + } + + + PmError synth_open(PmInternal *midi, void *driverInfo) + { + PmSynthOutputInfo *info = new PmSynthOutputInfo; + info->midiSynth.EnableInput(true, true); + midi->api_info = info; + return pmNoError; + } + + + PmError synth_abort(PmInternal *midi) + { + return pmNoError; + } + + + PmError synth_close(PmInternal *midi) + { + PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; + delete info; + midi->api_info = NULL; + return pmNoError; + } + + + PmError write_short_synth(PmInternal *midi, PmEvent *buffer) + { + PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; + uchar data[3]; + data[0] = Pm_MessageStatus(buffer->message); + data[1] = Pm_MessageData1(buffer->message); + data[2] = Pm_MessageData2(buffer->message); + + switch(data[0] & 0xf0) { + case B_NOTE_OFF: + info->midiSynth.NoteOff((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); + break; + case B_NOTE_ON: + info->midiSynth.NoteOn((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); + break; + case B_KEY_PRESSURE: + info->midiSynth.KeyPressure((data[0] & 0x0f + 1), data[1], data[2], buffer->timestamp); + break; + case B_CONTROL_CHANGE: + info->midiSynth.ControlChange((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); + break; + case B_PROGRAM_CHANGE: + info->midiSynth.ProgramChange((data[0] & 0x0f) + 1, data[1], buffer->timestamp); + break; + case B_CHANNEL_PRESSURE: + info->midiSynth.ChannelPressure((data[0] & 0x0f) + 1, data[1], buffer->timestamp); + break; + case B_PITCH_BEND: + info->midiSynth.PitchBend((data[0] & 0x0f) + 1, data[1], data[2], buffer->timestamp); + break; + } + + // TODO: handle latency != 0 + return pmNoError; + } + + + PmError begin_sysex_synth(PmInternal *midi, PmTimestamp timestamp) + { + PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; + info->sysexBuffer.clear(); + return pmNoError; + } + + + PmError end_sysex_synth(PmInternal *midi, PmTimestamp timestamp) + { + PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; + info->midiSynth.SystemExclusive(&info->sysexBuffer[0], info->sysexBuffer.size(), timestamp); + info->sysexBuffer.clear(); + return pmNoError; + } + + + PmError write_byte_synth(PmInternal *midi, unsigned char byte, PmTimestamp timestamp) + { + PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; + info->sysexBuffer.push_back(byte); + return pmNoError; + } + + + PmError write_realtime_synth(PmInternal *midi, PmEvent *buffer) + { + PmSynthOutputInfo *info = (PmSynthOutputInfo*)midi->api_info; + info->midiSynth.SystemRealTime(Pm_MessageStatus(buffer->message), buffer->timestamp); + return pmNoError; + } + + + PmError write_flush(PmInternal *midi, PmTimestamp timestamp) + { + return pmNoError; + } + + + unsigned int check_host_error(PmInternal *midi) + { + return 0; + } + + + pm_fns_node pm_in_dictionary = { + none_write_short, + none_sysex, + none_sysex, + none_write_byte, + none_write_short, + none_write_flush, + synchronize, + in_open, + in_abort, + in_close, + success_poll, + check_host_error + }; + + pm_fns_node pm_out_dictionary = { + write_short, + begin_sysex, + end_sysex, + write_byte, + write_realtime, + write_flush, + synchronize, + out_open, + out_abort, + out_close, + none_poll, + check_host_error + }; + + + pm_fns_node pm_synth_dictionary = { + write_short_synth, + begin_sysex_synth, + end_sysex_synth, + write_byte_synth, + write_realtime_synth, + write_flush, + synchronize, + synth_open, + synth_abort, + synth_close, + none_poll, + check_host_error + }; + + + PmError create_virtual(int is_input, const char *name, void *driverInfo) + { + BMidiEndpoint *endpoint = is_input ? (BMidiEndpoint*)new BMidiLocalProducer(name) : new BMidiLocalConsumer(name); + if (!endpoint->IsValid()) { + endpoint->Release(); + strcpy(pm_hosterror_text, "Endpoint could not be created"); + pm_hosterror = TRUE; + return pmHostError; + } + status_t status = endpoint->Register(); + if (status != B_OK) { + endpoint->Release(); + strcpy(pm_hosterror_text, strerror(status)); + pm_hosterror = TRUE; + return pmHostError; + } + 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); + } + + PmError delete_virtual(PmDeviceID id) + { + int32 endpointID = (int32)(intptr_t)pm_descriptors[id].descriptor; + BMidiEndpoint *endpoint = BMidiRoster::FindEndpoint(endpointID); + //TODO: handle connected producers and consumers + status_t status = endpoint->Unregister(); + // release twice to actually free the endpoint (FindEndpoint increases the ref-count) + endpoint->Release(); + endpoint->Release(); + if (status != B_OK) { + strcpy(pm_hosterror_text, strerror(status)); + pm_hosterror = TRUE; + return pmHostError; + } + return pmNoError; + } + + PmDeviceID pm_default_input_device_id = -1; + PmDeviceID pm_default_output_device_id = -1; +} + +extern "C" { + void pm_init() + { + pm_add_interf(const_cast<char*>("Haiku MIDI kit"), create_virtual, delete_virtual); + + pm_add_device(const_cast<char*>("Haiku MIDI kit"), "Soft Synth", FALSE, FALSE, NULL, &pm_synth_dictionary); + + int32 id = 0; + BMidiEndpoint *endpoint; + + while ((endpoint = BMidiRoster::NextEndpoint(&id)) != NULL) { + bool isInput = endpoint->IsProducer(); + pm_add_device(const_cast<char*>("Haiku MIDI kit"), endpoint->Name(), isInput, FALSE, (void*)(intptr_t)id, isInput ? &pm_in_dictionary : &pm_out_dictionary); + endpoint->Release(); + } + + // the following (default device handling) is copied from the Linux implementation + + // this is set when we return to Pm_Initialize, but we need it + // now in order to (successfully) call Pm_CountDevices() + pm_initialized = TRUE; + pm_default_input_device_id = find_default_device( + const_cast<char*>("/PortMidi/PM_RECOMMENDED_INPUT_DEVICE"), TRUE, + pm_default_input_device_id); + pm_default_output_device_id = find_default_device( + const_cast<char*>("/PortMidi/PM_RECOMMENDED_OUTPUT_DEVICE"), FALSE, + pm_default_output_device_id); + } + + + void pm_term() + { + int i; + for (i = 0; i < pm_descriptor_len; i++) { + PmInternal *midi = pm_descriptors[i].pm_internal; + if (midi && midi->api_info) { + // device is still open, close it + (*midi->dictionary->close)(midi); + } + if (pm_descriptors[i].pub.is_virtual && !pm_descriptors[i].deleted) { + delete_virtual(i); + } + } + } + + + PmDeviceID Pm_GetDefaultInputDeviceID() + { + Pm_Initialize(); + return pm_default_input_device_id; + } + + + PmDeviceID Pm_GetDefaultOutputDeviceID() + { + Pm_Initialize(); + return pm_default_output_device_id; + } + + + void *pm_alloc(size_t s) + { + return malloc(s); + } + + + void pm_free(void *ptr) + { + free(ptr); + } +} |