summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/portmidi/pm_haiku/pmhaiku.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/portmidi/pm_haiku/pmhaiku.cpp')
-rw-r--r--3rdparty/portmidi/pm_haiku/pmhaiku.cpp514
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);
+ }
+}