| @@ -0,0 +1,132 @@ | |||
| /**************************************************************************** | |||
| lv2-midifunctions.h - support file for using MIDI in LV2 plugins | |||
| Copyright (C) 2006-2007 Lars Luthman <lars.luthman@gmail.com> | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA | |||
| ****************************************************************************/ | |||
| /** @file | |||
| This file contains static helper functions for the LV2 MIDI datatype | |||
| extension. | |||
| */ | |||
| #ifndef LV2_MIDIFUNCTIONS | |||
| #define LV2_MIDIFUNCTIONS | |||
| #include <string.h> | |||
| #include "lv2-midiport.h" | |||
| /** This structure contains information about a MIDI port buffer, the | |||
| current period size, and the position in the MIDI data buffer that | |||
| we are currently reading from or writing to. It needs to be recreated | |||
| or at least reinitialised every process() call. */ | |||
| typedef struct { | |||
| /** The MIDI port structure that we want to read or write. */ | |||
| LV2_MIDI* midi; | |||
| /** The number of frames in this process cycle. */ | |||
| uint32_t frame_count; | |||
| /** The current position in the data buffer. Should be initialised to 0. */ | |||
| uint32_t position; | |||
| } LV2_MIDIState; | |||
| /** This function reads one event from the port associated with the @c state | |||
| parameter and writes its timestamp, size and a pointer to its data bytes | |||
| into the parameters @c timestamp, @c size and @c data, respectively. | |||
| It does not advance the read position in the MIDI data buffer, two | |||
| subsequent calls to lv2midi_get_event() will read the same event. | |||
| The function returns the timestamp for the read event, or the @c frame_count | |||
| member of @c state if there are no more events in the buffer. */ | |||
| static double lv2midi_get_event(LV2_MIDIState* state, | |||
| double* timestamp, | |||
| uint32_t* size, | |||
| unsigned char** data) { | |||
| if (state->position >= state->midi->size) { | |||
| state->position = state->midi->size; | |||
| *timestamp = state->frame_count; | |||
| *size = 0; | |||
| *data = NULL; | |||
| return *timestamp; | |||
| } | |||
| *timestamp = *(double*)(state->midi->data + state->position); | |||
| *size = *(uint32_t*)(state->midi->data + state->position + sizeof(double)); | |||
| *data = state->midi->data + state->position + | |||
| sizeof(double) + sizeof(uint32_t); | |||
| return *timestamp; | |||
| } | |||
| /** This function advances the read/write position in @c state to the next | |||
| event and returns its timestamp, or the @c frame_count member of @c state | |||
| is there are no more events. */ | |||
| static double lv2midi_step(LV2_MIDIState* state) { | |||
| if (state->position + sizeof(double) + sizeof(uint32_t) >= state->midi->size) { | |||
| state->position = state->midi->size; | |||
| return state->frame_count; | |||
| } | |||
| state->position += sizeof(double); | |||
| uint32_t size = *(uint32_t*)(state->midi->data + state->position); | |||
| state->position += sizeof(uint32_t); | |||
| state->position += size; | |||
| if (state->position >= state->midi->size) | |||
| return state->frame_count; | |||
| return *(double*)(state->midi->data + state->position); | |||
| } | |||
| /** This function writes one MIDI event to the port buffer associated with | |||
| @c state. It returns 0 when the event was written successfully to the | |||
| buffer, and -1 when there was not enough room. The read/write position | |||
| is advanced automatically. */ | |||
| static int lv2midi_put_event(LV2_MIDIState* state, | |||
| double timestamp, | |||
| uint32_t size, | |||
| const unsigned char* data) { | |||
| if (state->midi->capacity - state->midi->size < | |||
| sizeof(double) + sizeof(uint32_t) + size) | |||
| return -1; | |||
| *(double*)(state->midi->data + state->midi->size) = timestamp; | |||
| state->midi->size += sizeof(double); | |||
| *(uint32_t*)(state->midi->data + state->midi->size) = size; | |||
| state->midi->size += sizeof(uint32_t); | |||
| memcpy(state->midi->data + state->midi->size, data, size); | |||
| state->midi->size += size; | |||
| ++state->midi->event_count; | |||
| return 0; | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,177 @@ | |||
| /**************************************************************************** | |||
| lv2-midiport.h - header file for using MIDI in LV2 plugins | |||
| Copyright (C) 2006 Lars Luthman <lars.luthman@gmail.com> | |||
| This program is free software; you can redistribute it and/or modify | |||
| it under the terms of the GNU Lesser General Public License as published by | |||
| the Free Software Foundation; either version 2 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public License | |||
| along with this program; if not, write to the Free Software | |||
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA | |||
| ****************************************************************************/ | |||
| /** @file | |||
| This file contains the specification for an LV2 MIDI port extension. | |||
| */ | |||
| #ifndef LV2_MIDIPORT_H | |||
| #define LV2_MIDIPORT_H | |||
| /** This data structure is used to contain the MIDI events for one run() | |||
| cycle. The port buffer for an LV2 port of the type | |||
| <http://ll-plugins.nongnu.org/lv2/ext/MidiPort> should be a pointer | |||
| to an instance of this struct. | |||
| To store two Note On events on MIDI channel 0 in a buffer, with timestamps | |||
| 12 and 35.5, you could use something like this code (assuming that | |||
| midi_data is a variable of type LV2_MIDI): | |||
| @code | |||
| uint32_t buffer_offset = 0; | |||
| *(double*)(midi_data->data + buffer_offset) = 12; | |||
| buffer_offset += sizeof(double); | |||
| *(uint32_t*)(midi_data->data + buffer_offset) = 3; | |||
| buffer_offset += sizeof(uint32_t); | |||
| midi_data->data[buffer_offset++] = 0x90; | |||
| midi_data->data[buffer_offset++] = 0x48; | |||
| midi_data->data[buffer_offset++] = 0x64; | |||
| ++midi_data->event_count; | |||
| *(double*)(midi_data->data + buffer_offset) = 35.5; | |||
| buffer_offset += sizeof(double); | |||
| *(uint32_t*)(midi_data->data + buffer_offset) = 3; | |||
| buffer_offset += sizeof(uint32_t); | |||
| midi_data->data[buffer_offset++] = 0x90; | |||
| midi_data->data[buffer_offset++] = 0x55; | |||
| midi_data->data[buffer_offset++] = 0x64; | |||
| ++midi_data->event_count; | |||
| midi_data->size = buffer_offset; | |||
| @endcode | |||
| This would be done by the host in the case of an input port, and by the | |||
| plugin in the case of an output port. Whoever is writing events to the | |||
| buffer must also take care not to exceed the capacity of the data buffer. | |||
| To read events from a buffer, you could do something like this: | |||
| @code | |||
| uint32_t buffer_offset = 0; | |||
| uint32_t i; | |||
| for (i = 0; i < midi_data->event_count; ++i) { | |||
| double timestamp = *(double*)(midi_data->data + buffer_offset); | |||
| buffer_offset += sizeof(double); | |||
| uint32_t size = *(uint32_t*)(midi_data->data + buffer_offset); | |||
| buffer_offset += sizeof(uint32_t); | |||
| do_something_with_event(timestamp, size, | |||
| midi_data->data + buffer_offset); | |||
| buffer_offset += size; | |||
| } | |||
| @endcode | |||
| If you think this looks like too much code to simply read and write MIDI | |||
| events, take a look at the helper functions in lv2-midifunctions.h. They | |||
| are not an official part of this extension, but they can be convenient. | |||
| */ | |||
| typedef struct { | |||
| /** The number of MIDI events in the data buffer. | |||
| INPUT PORTS: It's the host's responsibility to set this field to the | |||
| number of MIDI events contained in the data buffer before calling the | |||
| plugin's run() function. The plugin may not change this field. | |||
| OUTPUT PORTS: It's the plugin's responsibility to set this field to the | |||
| number of MIDI events it has stored in the data buffer before returning | |||
| from the run() function. Any initial value should be ignored by the | |||
| plugin. | |||
| */ | |||
| uint32_t event_count; | |||
| /** The size of the data buffer in bytes. It is set by the host and may not | |||
| be changed by the plugin. The host is allowed to change this between | |||
| run() calls. | |||
| */ | |||
| uint32_t capacity; | |||
| /** The size of the initial part of the data buffer that actually contains | |||
| data. | |||
| INPUT PORTS: It's the host's responsibility to set this field to the | |||
| number of bytes used by all MIDI events it has written to the buffer | |||
| (including timestamps and size fields) before calling the plugin's | |||
| run() function. The plugin may not change this field. | |||
| OUTPUT PORTS: It's the plugin's responsibility to set this field to | |||
| the number of bytes used by all MIDI events it has written to the | |||
| buffer (including timestamps and size fields) before returning from | |||
| the run() function. Any initial value should be ignored by the plugin. | |||
| */ | |||
| uint32_t size; | |||
| /** The data buffer that is used to store MIDI events. The events are packed | |||
| after each other, and the format of each event is as follows: | |||
| First there is a timestamp, which should have the type "double", | |||
| i.e. have the same bit size as a double and the same bit layout as a | |||
| double (whatever that is on the current platform). This timestamp gives | |||
| the offset from the beginning of the current cycle, in frames, that | |||
| the MIDI event occurs on. It must be strictly smaller than the 'nframes' | |||
| parameter to the current run() call. The MIDI events in the buffer must | |||
| be ordered by their timestamp, e.g. an event with a timestamp of 123.23 | |||
| must be stored after an event with a timestamp of 65.0. | |||
| The second part of the event is a size field, which should have the type | |||
| "uint32_t" (as defined in the standard C header stddef.h). It should | |||
| contain the size of the MIDI data for this event, i.e. the number of | |||
| bytes used to store the actual MIDI event. The bytes used by the | |||
| timestamp and the size field should not be counted. | |||
| The third part of the event is the actual MIDI data. There are some | |||
| requirements that must be followed: | |||
| * Running status is not allowed. Every event must have its own status | |||
| byte. | |||
| * Note On events with velocity 0 are not allowed. These events are | |||
| equivalent to Note Off in standard MIDI streams, but in order to make | |||
| plugins and hosts easier to write, as well as more efficient, only | |||
| proper Note Off events are allowed as Note Off. | |||
| * "Realtime events" (status bytes 0xF8 to 0xFF) are allowed, but may not | |||
| occur inside other events like they are allowed to in hardware MIDI | |||
| streams. | |||
| * All events must be fully contained in a single data buffer, i.e. events | |||
| may not "wrap around" by storing the first few bytes in one buffer and | |||
| then wait for the next run() call to store the rest of the event. If | |||
| there isn't enough space in the current data buffer to store an event, | |||
| the event will either have to wait until next run() call, be ignored, | |||
| or compensated for in some more clever way. | |||
| * All events must be valid MIDI events. This means for example that | |||
| only the first byte in each event (the status byte) may have the eighth | |||
| bit set, that Note On and Note Off events are always 3 bytes long etc. | |||
| The MIDI writer (host or plugin) is responsible for writing valid MIDI | |||
| events to the buffer, and the MIDI reader (plugin or host) can assume | |||
| that all events are valid. | |||
| On a platform where double is 8 bytes the data buffer layout for a | |||
| 3-byte event followed by a 4-byte event may look something like this: | |||
| _______________________________________________________________ | |||
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ... | |||
| |TIMESTAMP 1 |SIZE 1 |DATA |TIMESTAMP 2 |SIZE 2 |DATA | ... | |||
| */ | |||
| unsigned char* data; | |||
| } LV2_MIDI; | |||
| #endif | |||
| @@ -43,9 +43,8 @@ const unsigned int PLUGIN_CAN_BALANCE = 0x40; | |||
| // parameter hints | |||
| const unsigned int PARAMETER_IS_ENABLED = 0x01; | |||
| const unsigned int PARAMETER_IS_AUTOMABLE = 0x02; | |||
| const unsigned int PARAMETER_HAS_STRICT_BOUNDS = 0x04; | |||
| const unsigned int PARAMETER_USES_SCALEPOINTS = 0x08; | |||
| const unsigned int PARAMETER_USES_SAMPLERATE = 0x10; | |||
| const unsigned int PARAMETER_USES_SCALEPOINTS = 0x04; | |||
| const unsigned int PARAMETER_USES_SAMPLERATE = 0x08; | |||
| enum BinaryType { | |||
| BINARY_NONE = 0, | |||
| @@ -151,7 +151,7 @@ public: | |||
| m_name = get_unique_name(info->name); | |||
| m_filename = strdup(filename); | |||
| // TODO - get bridge binary here | |||
| // FIXME - verify if path exists, if not return false | |||
| m_thread->setOscData(binarytype2str(m_binary), label, plugintype2str(m_type)); | |||
| m_thread->start(); | |||
| @@ -357,12 +357,6 @@ public: | |||
| return 0.0; | |||
| } | |||
| // FIXME - remove this? | |||
| // double get_default_parameter_value(uint32_t param_id) | |||
| // { | |||
| // return param.ranges[param_id].def; | |||
| // } | |||
| virtual void get_label(char* buf_str) | |||
| { | |||
| *buf_str = 0; | |||
| @@ -856,7 +850,6 @@ public: | |||
| { | |||
| post_events.lock.lock(); | |||
| // FIXME? | |||
| memcpy(post_events_dst, post_events.data, sizeof(PluginPostEvent)*MAX_POST_EVENTS); | |||
| for (unsigned short i=0; i < MAX_POST_EVENTS; i++) | |||
| @@ -759,7 +759,7 @@ public: | |||
| if (param.port_cin) | |||
| { | |||
| jack_default_audio_sample_t* pin_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(param.port_cin, nframes); | |||
| void* pin_buffer = jack_port_get_buffer(param.port_cin, nframes); | |||
| jack_midi_event_t pin_event; | |||
| uint32_t n_pin_events = jack_midi_get_event_count(pin_buffer); | |||
| @@ -1125,7 +1125,7 @@ public: | |||
| if (param.port_cout) | |||
| { | |||
| jack_default_audio_sample_t* cout_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(param.port_cout, nframes); | |||
| void* cout_buffer = jack_port_get_buffer(param.port_cout, nframes); | |||
| jack_midi_clear_buffer(cout_buffer); | |||
| double value; | |||
| @@ -643,7 +643,7 @@ public: | |||
| if (param.port_cin) | |||
| { | |||
| jack_default_audio_sample_t* pin_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(param.port_cin, nframes); | |||
| void* pin_buffer = jack_port_get_buffer(param.port_cin, nframes); | |||
| jack_midi_event_t pin_event; | |||
| uint32_t n_pin_events = jack_midi_get_event_count(pin_buffer); | |||
| @@ -848,7 +848,7 @@ public: | |||
| if (param.port_cout) | |||
| { | |||
| jack_default_audio_sample_t* cout_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(param.port_cout, nframes); | |||
| void* cout_buffer = jack_port_get_buffer(param.port_cout, nframes); | |||
| jack_midi_clear_buffer(cout_buffer); | |||
| double value; | |||
| @@ -30,30 +30,39 @@ | |||
| #include "lv2/ui.h" | |||
| #include "lv2_rdf.h" | |||
| #include "lv2/lv2-midiport.h" | |||
| #include "lv2/lv2-midifunctions.h" | |||
| // static max values | |||
| const unsigned int MAX_EVENT_BUFFER = 0x7FFF; // 32767 | |||
| const unsigned int MAX_EVENT_BUFFER = 8192; // 0x7FFF; // 32767 | |||
| // extra plugin hints | |||
| const unsigned int PLUGIN_HAS_EXTENSION_STATE = 0x1000; | |||
| const unsigned int PLUGIN_HAS_EXTENSION_DYNPARAM = 0x2000; | |||
| // parameter hints | |||
| const unsigned int PARAMETER_HAS_STRICT_BOUNDS = 0x100; | |||
| // feature ids | |||
| const uint32_t lv2_feature_id_uri_map = 0; | |||
| const uint32_t lv2_feature_id_urid_map = 1; | |||
| const uint32_t lv2_feature_id_urid_unmap = 2; | |||
| const uint32_t lv2_feature_id_event = 3; | |||
| //const uint32_t lv2_feature_id_rtmempool = 4; | |||
| const uint32_t lv2_feature_id_rtmempool = 4; | |||
| //const uint32_t lv2_feature_id_data_access = 5; | |||
| //const uint32_t lv2_feature_id_instance_access = 6; | |||
| //const uint32_t lv2_feature_id_ui_resize = 7; | |||
| //const uint32_t lv2_feature_id_ui_parent = 8; | |||
| //const uint32_t lv2_feature_id_external_ui = 9; | |||
| //const uint32_t lv2_feature_id_external_ui_old = 10; | |||
| const uint32_t lv2_feature_count = 4; | |||
| const uint32_t lv2_feature_count = 5; | |||
| // event types | |||
| const unsigned int CARLA_EVENT_TYPE_MIDI = 0x1; | |||
| const unsigned int CARLA_EVENT_TYPE_TIME = 0x2; | |||
| // event data/types | |||
| const unsigned int CARLA_EVENT_DATA_ATOM = 0x01; | |||
| const unsigned int CARLA_EVENT_DATA_EVENT = 0x02; | |||
| const unsigned int CARLA_EVENT_DATA_MIDI_LL = 0x04; | |||
| const unsigned int CARLA_EVENT_TYPE_MIDI = 0x10; | |||
| const unsigned int CARLA_EVENT_TYPE_TIME = 0x20; | |||
| // pre-set uri[d] map ids | |||
| const uint32_t CARLA_URI_MAP_ID_NULL = 0; | |||
| @@ -70,7 +79,10 @@ enum Lv2ParameterDataType { | |||
| struct EventData { | |||
| unsigned int types; | |||
| jack_port_t* port; | |||
| LV2_Event_Buffer* buffer; | |||
| union { | |||
| LV2_Event_Buffer* e; | |||
| LV2_MIDI* m; | |||
| } buffer; | |||
| }; | |||
| struct PluginEventData { | |||
| @@ -428,7 +440,7 @@ public: | |||
| CarlaPlugin::set_custom_data(dtype, key, value, gui_send); | |||
| } | |||
| virtual void set_gui_data(int, void* ptr) | |||
| virtual void set_gui_data(int, void* /*ptr*/) | |||
| { | |||
| // switch(gui.type) | |||
| // { | |||
| @@ -468,7 +480,7 @@ public: | |||
| // } | |||
| } | |||
| virtual void show_gui(bool yesno) | |||
| virtual void show_gui(bool /*yesno*/) | |||
| { | |||
| // switch(gui.type) | |||
| // { | |||
| @@ -597,6 +609,7 @@ public: | |||
| ains = aouts = cv_ins = cv_outs = ev_ins = ev_outs = params = 0; | |||
| const uint32_t PortCount = rdf_descriptor->PortCount; | |||
| unsigned int event_data_type = 0; | |||
| for (uint32_t i=0; i<PortCount; i++) | |||
| { | |||
| @@ -621,6 +634,15 @@ public: | |||
| ev_ins += 1; | |||
| else if (LV2_IS_PORT_OUTPUT(PortType)) | |||
| ev_outs += 1; | |||
| event_data_type = CARLA_EVENT_DATA_EVENT; | |||
| } | |||
| else if (LV2_IS_PORT_MIDI_LL(PortType)) | |||
| { | |||
| if (LV2_IS_PORT_INPUT(PortType)) | |||
| ev_ins += 1; | |||
| else if (LV2_IS_PORT_OUTPUT(PortType)) | |||
| ev_outs += 1; | |||
| event_data_type = CARLA_EVENT_DATA_MIDI_LL; | |||
| } | |||
| else if (LV2_IS_PORT_CONTROL(PortType)) | |||
| params += 1; | |||
| @@ -651,9 +673,27 @@ public: | |||
| for (j=0; j < ev_ins; j++) | |||
| { | |||
| evin.data[j].types = 0; | |||
| evin.data[j].port = nullptr; | |||
| evin.data[j].buffer = lv2_event_buffer_new(MAX_EVENT_BUFFER, LV2_EVENT_AUDIO_STAMP); | |||
| evin.data[j].port = nullptr; | |||
| if (event_data_type == CARLA_EVENT_DATA_ATOM) | |||
| { | |||
| evin.data[j].types = CARLA_EVENT_DATA_ATOM; | |||
| // TODO | |||
| } | |||
| else if (event_data_type == CARLA_EVENT_DATA_EVENT) | |||
| { | |||
| evin.data[j].types = CARLA_EVENT_DATA_EVENT; | |||
| evin.data[j].buffer.e = lv2_event_buffer_new(MAX_EVENT_BUFFER, LV2_EVENT_AUDIO_STAMP); | |||
| } | |||
| else if (event_data_type == CARLA_EVENT_DATA_MIDI_LL) | |||
| { | |||
| evin.data[j].types = CARLA_EVENT_DATA_MIDI_LL; | |||
| evin.data[j].buffer.m = new LV2_MIDI; | |||
| evin.data[j].buffer.m->capacity = MAX_EVENT_BUFFER; | |||
| evin.data[j].buffer.m->data = new unsigned char [MAX_EVENT_BUFFER]; | |||
| } | |||
| else | |||
| evin.data[j].types = 0; | |||
| } | |||
| } | |||
| @@ -663,9 +703,27 @@ public: | |||
| for (j=0; j < ev_outs; j++) | |||
| { | |||
| evout.data[j].types = 0; | |||
| evout.data[j].port = nullptr; | |||
| evout.data[j].buffer = lv2_event_buffer_new(MAX_EVENT_BUFFER, LV2_EVENT_AUDIO_STAMP); | |||
| evout.data[j].port = nullptr; | |||
| if (event_data_type == CARLA_EVENT_DATA_ATOM) | |||
| { | |||
| evout.data[j].types = CARLA_EVENT_DATA_ATOM; | |||
| // TODO | |||
| } | |||
| else if (event_data_type == CARLA_EVENT_DATA_EVENT) | |||
| { | |||
| evout.data[j].types = CARLA_EVENT_DATA_EVENT; | |||
| evout.data[j].buffer.e = lv2_event_buffer_new(MAX_EVENT_BUFFER, LV2_EVENT_AUDIO_STAMP); | |||
| } | |||
| else if (event_data_type == CARLA_EVENT_DATA_MIDI_LL) | |||
| { | |||
| evout.data[j].types = CARLA_EVENT_DATA_MIDI_LL; | |||
| evout.data[j].buffer.m = new LV2_MIDI; | |||
| evout.data[j].buffer.m->capacity = MAX_EVENT_BUFFER; | |||
| evout.data[j].buffer.m->data = new unsigned char [MAX_EVENT_BUFFER]; | |||
| } | |||
| else | |||
| evout.data[j].types = 0; | |||
| } | |||
| } | |||
| @@ -685,7 +743,7 @@ public: | |||
| { | |||
| const LV2_Property PortType = rdf_descriptor->Ports[i].Type; | |||
| if (LV2_IS_PORT_AUDIO(PortType) || LV2_IS_PORT_CV(PortType) || LV2_IS_PORT_EVENT(PortType)) | |||
| if (LV2_IS_PORT_AUDIO(PortType) || LV2_IS_PORT_CV(PortType) || LV2_IS_PORT_EVENT(PortType) || LV2_IS_PORT_MIDI_LL(PortType)) | |||
| { | |||
| #ifndef BUILD_BRIDGE | |||
| if (carla_options.global_jack_client) | |||
| @@ -737,7 +795,7 @@ public: | |||
| if (LV2_IS_PORT_INPUT(PortType)) | |||
| { | |||
| j = evin.count++; | |||
| descriptor->connect_port(handle, i, evin.data[j].buffer); | |||
| descriptor->connect_port(handle, i, evin.data[j].buffer.e); | |||
| if (LV2_IS_PORT_EVENT_MIDI(PortType)) | |||
| { | |||
| @@ -747,13 +805,12 @@ public: | |||
| if (LV2_IS_PORT_EVENT_TIME(PortType)) | |||
| { | |||
| evin.data[j].types |= CARLA_EVENT_TYPE_TIME; | |||
| //wants_time_pos = true; | |||
| } | |||
| } | |||
| else if (LV2_IS_PORT_OUTPUT(PortType)) | |||
| { | |||
| j = evout.count++; | |||
| descriptor->connect_port(handle, i, evout.data[j].buffer); | |||
| descriptor->connect_port(handle, i, evout.data[j].buffer.e); | |||
| if (LV2_IS_PORT_EVENT_MIDI(PortType)) | |||
| { | |||
| @@ -768,6 +825,27 @@ public: | |||
| else | |||
| qWarning("WARNING - Got a broken Port (Event, but not input or output)"); | |||
| } | |||
| else if (LV2_IS_PORT_MIDI_LL(PortType)) | |||
| { | |||
| if (LV2_IS_PORT_INPUT(PortType)) | |||
| { | |||
| j = evin.count++; | |||
| descriptor->connect_port(handle, i, evin.data[j].buffer.m); | |||
| evin.data[j].types |= CARLA_EVENT_TYPE_MIDI; | |||
| evin.data[j].port = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); | |||
| } | |||
| else if (LV2_IS_PORT_OUTPUT(PortType)) | |||
| { | |||
| j = evout.count++; | |||
| descriptor->connect_port(handle, i, evout.data[j].buffer.m); | |||
| evout.data[j].types |= CARLA_EVENT_TYPE_MIDI; | |||
| evout.data[j].port = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); | |||
| } | |||
| else | |||
| qWarning("WARNING - Got a broken Port (Midi, but not input or output)"); | |||
| } | |||
| else if (LV2_IS_PORT_CONTROL(PortType)) | |||
| { | |||
| const LV2_Property PortProps = rdf_descriptor->Ports[i].Properties; | |||
| @@ -997,11 +1075,13 @@ public: | |||
| jack_default_audio_sample_t* ains_buffer[ain.count]; | |||
| jack_default_audio_sample_t* aouts_buffer[aout.count]; | |||
| LV2_Event_Iterator evin_iters[evin.count]; | |||
| void* evins_buffer[evin.count]; | |||
| void* evouts_buffer[evout.count]; | |||
| // different midi APIs | |||
| LV2_MIDIState evin_states[evin.count]; | |||
| LV2_Event_Iterator evin_iters[evin.count]; | |||
| for (i=0; i < ain.count; i++) | |||
| ains_buffer[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(ain.ports[i], nframes); | |||
| @@ -1010,21 +1090,46 @@ public: | |||
| for (i=0; i < evin.count; i++) | |||
| { | |||
| lv2_event_buffer_reset(evin.data[i].buffer, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(evin.data[i].buffer + 1)); | |||
| lv2_event_begin(&evin_iters[i], evin.data[i].buffer); | |||
| if (evin.data[i].types & CARLA_EVENT_DATA_ATOM) | |||
| { | |||
| } | |||
| else if (evin.data[i].types & CARLA_EVENT_DATA_EVENT) | |||
| { | |||
| lv2_event_buffer_reset(evin.data[i].buffer.e, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(evin.data[i].buffer.e + 1)); | |||
| lv2_event_begin(&evin_iters[i], evin.data[i].buffer.e); | |||
| } | |||
| else if (evin.data[i].types & CARLA_EVENT_DATA_MIDI_LL) | |||
| { | |||
| evin_states[i].midi = evin.data[i].buffer.m; | |||
| evin_states[i].frame_count = nframes; | |||
| evin_states[i].position = 0; | |||
| evin_states[i].midi->event_count = 0; | |||
| evin_states[i].midi->size = 0; | |||
| } | |||
| if (evin.data[i].port) | |||
| evins_buffer[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(evin.data[i].port, nframes); | |||
| evins_buffer[i] = jack_port_get_buffer(evin.data[i].port, nframes); | |||
| else | |||
| evins_buffer[i] = nullptr; | |||
| } | |||
| for (i=0; i < evout.count; i++) | |||
| { | |||
| lv2_event_buffer_reset(evout.data[i].buffer, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(evout.data[i].buffer + 1)); | |||
| if (evin.data[i].types & CARLA_EVENT_DATA_ATOM) | |||
| { | |||
| } | |||
| else if (evin.data[i].types & CARLA_EVENT_DATA_EVENT) | |||
| { | |||
| lv2_event_buffer_reset(evout.data[i].buffer.e, LV2_EVENT_AUDIO_STAMP, (uint8_t*)(evout.data[i].buffer.e + 1)); | |||
| } | |||
| else if (evin.data[i].types & CARLA_EVENT_DATA_MIDI_LL) | |||
| { | |||
| // not needed | |||
| } | |||
| if (evout.data[i].port) | |||
| evouts_buffer[i] = (jack_default_audio_sample_t*)jack_port_get_buffer(evout.data[i].port, nframes); | |||
| evouts_buffer[i] = jack_port_get_buffer(evout.data[i].port, nframes); | |||
| else | |||
| evouts_buffer[i] = nullptr; | |||
| } | |||
| @@ -1052,7 +1157,7 @@ public: | |||
| if (param.port_cin) | |||
| { | |||
| jack_default_audio_sample_t* pin_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(param.port_cin, nframes); | |||
| void* pin_buffer = jack_port_get_buffer(param.port_cin, nframes); | |||
| jack_midi_event_t pin_event; | |||
| uint32_t n_pin_events = jack_midi_get_event_count(pin_buffer); | |||
| @@ -1195,19 +1300,22 @@ public: | |||
| { | |||
| if (ext_midi_notes[i].valid) | |||
| { | |||
| uint8_t midi_event[4] = { 0 }; | |||
| midi_event[0] = ext_midi_notes[i].onoff ? MIDI_STATUS_NOTE_ON : MIDI_STATUS_NOTE_OFF; | |||
| midi_event[1] = ext_midi_notes[i].note; | |||
| midi_event[2] = ext_midi_notes[i].velo; | |||
| // send to all midi inputs | |||
| for (k=0; k < evin.count; k++) | |||
| { | |||
| if (evins_buffer[k] == nullptr || (evin.data[k].types & CARLA_EVENT_TYPE_MIDI) == 0) | |||
| continue; | |||
| uint8_t* midi_event = lv2_event_reserve(&evin_iters[k], 0, 0, CARLA_URI_MAP_ID_EVENT_MIDI, 3); | |||
| if (midi_event) | |||
| if (evin.data[k].types & CARLA_EVENT_TYPE_MIDI) | |||
| { | |||
| midi_event[0] = ext_midi_notes[i].onoff ? MIDI_STATUS_NOTE_ON : MIDI_STATUS_NOTE_OFF; | |||
| midi_event[1] = ext_midi_notes[i].note; | |||
| midi_event[2] = ext_midi_notes[i].velo; | |||
| if (evin.data[k].types & CARLA_EVENT_DATA_ATOM) | |||
| continue; // TODO | |||
| else if (evin.data[k].types & CARLA_EVENT_DATA_EVENT) | |||
| lv2_event_write(&evin_iters[k], 0, 0, CARLA_URI_MAP_ID_EVENT_MIDI, 3, midi_event); | |||
| else if (evin.data[k].types & CARLA_EVENT_DATA_MIDI_LL) | |||
| lv2midi_put_event(&evin_states[k], 0, 3, midi_event); | |||
| } | |||
| } | |||
| @@ -1229,7 +1337,7 @@ public: | |||
| for (i=0; i < evin.count; i++) | |||
| { | |||
| if (evins_buffer[i] == nullptr || (evin.data[i].types & CARLA_EVENT_TYPE_MIDI) == 0) | |||
| if (evins_buffer[i] == nullptr) | |||
| continue; | |||
| jack_midi_event_t min_event; | |||
| @@ -1252,7 +1360,12 @@ public: | |||
| // write supported status types | |||
| if (MIDI_IS_STATUS_NOTE_OFF(status) || MIDI_IS_STATUS_NOTE_ON(status) || MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status) || MIDI_IS_STATUS_AFTERTOUCH(status) || MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status)) | |||
| { | |||
| lv2_event_write(&evin_iters[i], min_event.time, 0, CARLA_URI_MAP_ID_EVENT_MIDI, min_event.size, min_event.buffer); | |||
| if (evin.data[i].types & CARLA_EVENT_DATA_ATOM) | |||
| continue; // TODO | |||
| else if (evin.data[i].types & CARLA_EVENT_DATA_EVENT) | |||
| lv2_event_write(&evin_iters[i], min_event.time, 0, CARLA_URI_MAP_ID_EVENT_MIDI, min_event.size, min_event.buffer); | |||
| else if (evin.data[i].types & CARLA_EVENT_DATA_MIDI_LL) | |||
| lv2midi_put_event(&evin_states[i], min_event.time, min_event.size, min_event.buffer); | |||
| if (MIDI_IS_STATUS_NOTE_OFF(status)) | |||
| postpone_event(PostEventNoteOff, min_event.buffer[1], 0.0); | |||
| @@ -1377,7 +1490,7 @@ public: | |||
| if (param.port_cout) | |||
| { | |||
| jack_default_audio_sample_t* cout_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(param.port_cout, nframes); | |||
| void* cout_buffer = jack_port_get_buffer(param.port_cout, nframes); | |||
| jack_midi_clear_buffer(cout_buffer); | |||
| double value, rvalue; | |||
| @@ -1413,24 +1526,44 @@ public: | |||
| for (i=0; i < evout.count; i++) | |||
| { | |||
| if (evouts_buffer[i] == nullptr || (evout.data[i].types & CARLA_EVENT_TYPE_MIDI) == 0) | |||
| if (evouts_buffer[i] == nullptr) | |||
| continue; | |||
| jack_midi_clear_buffer(evouts_buffer[i]); | |||
| LV2_Event* ev; | |||
| uint8_t* data; | |||
| if (evin.data[i].types & CARLA_EVENT_DATA_ATOM) | |||
| { | |||
| } | |||
| else if (evin.data[i].types & CARLA_EVENT_DATA_EVENT) | |||
| { | |||
| LV2_Event* ev; | |||
| LV2_Event_Iterator iter; | |||
| LV2_Event_Iterator iter; | |||
| lv2_event_begin(&iter, evout.data[i].buffer); | |||
| uint8_t* data; | |||
| lv2_event_begin(&iter, evout.data[i].buffer.e); | |||
| for (k=0; k < iter.buf->event_count; k++) | |||
| { | |||
| ev = lv2_event_get(&iter, &data); | |||
| if (ev && data) | |||
| jack_midi_event_write(evouts_buffer[i], ev->frames, data, ev->size); | |||
| for (k=0; k < iter.buf->event_count; k++) | |||
| lv2_event_increment(&iter); | |||
| } | |||
| } | |||
| else if (evin.data[i].types & CARLA_EVENT_DATA_MIDI_LL) | |||
| { | |||
| ev = lv2_event_get(&iter, &data); | |||
| if (ev && data) | |||
| jack_midi_event_write(evouts_buffer[i], ev->frames, data, ev->size); | |||
| LV2_MIDIState state = { evout.data[i].buffer.m, nframes, 0 }; | |||
| uint32_t event_size; | |||
| double event_timestamp; | |||
| unsigned char* event_data; | |||
| lv2_event_increment(&iter); | |||
| while (lv2midi_get_event(&state, &event_timestamp, &event_size, &event_data) < nframes) | |||
| { | |||
| jack_midi_event_write(evouts_buffer[i], event_timestamp, event_data, event_size); | |||
| lv2midi_step(&state); | |||
| } | |||
| } | |||
| } // End of MIDI Output | |||
| @@ -1463,7 +1596,20 @@ public: | |||
| if (evin.count > 0) | |||
| { | |||
| for (uint32_t i=0; i < evin.count; i++) | |||
| free(evin.data[i].buffer); | |||
| { | |||
| if (evin.data[i].types & CARLA_EVENT_DATA_ATOM) | |||
| { | |||
| } | |||
| else if (evin.data[i].types & CARLA_EVENT_DATA_EVENT) | |||
| { | |||
| free(evin.data[i].buffer.e); | |||
| } | |||
| else if (evin.data[i].types & CARLA_EVENT_DATA_MIDI_LL) | |||
| { | |||
| delete[] evin.data[i].buffer.m->data; | |||
| delete evin.data[i].buffer.m; | |||
| } | |||
| } | |||
| delete[] evin.data; | |||
| } | |||
| @@ -1471,7 +1617,20 @@ public: | |||
| if (evout.count > 0) | |||
| { | |||
| for (uint32_t i=0; i < evout.count; i++) | |||
| free(evout.data[i].buffer); | |||
| { | |||
| if (evout.data[i].types & CARLA_EVENT_DATA_ATOM) | |||
| { | |||
| } | |||
| else if (evout.data[i].types & CARLA_EVENT_DATA_EVENT) | |||
| { | |||
| free(evout.data[i].buffer.e); | |||
| } | |||
| else if (evout.data[i].types & CARLA_EVENT_DATA_MIDI_LL) | |||
| { | |||
| delete[] evout.data[i].buffer.m->data; | |||
| delete evout.data[i].buffer.m; | |||
| } | |||
| } | |||
| delete[] evout.data; | |||
| } | |||
| @@ -1550,7 +1709,6 @@ public: | |||
| uint32_t i = 0; | |||
| while ((descriptor = descfn(i++))) | |||
| { | |||
| qDebug("%s | %s", descriptor->URI, URI); | |||
| if (strcmp(descriptor->URI, URI) == 0) | |||
| break; | |||
| } | |||
| @@ -1563,7 +1721,7 @@ public: | |||
| for (i=0; i < rdf_descriptor->PortCount; i++) | |||
| { | |||
| LV2_Property PortType = rdf_descriptor->Ports[i].Type; | |||
| if (bool(LV2_IS_PORT_AUDIO(PortType) || LV2_IS_PORT_CONTROL(PortType) || LV2_IS_PORT_EVENT(PortType)) == false) | |||
| if (bool(LV2_IS_PORT_AUDIO(PortType) || LV2_IS_PORT_CONTROL(PortType) || LV2_IS_PORT_EVENT(PortType) || LV2_IS_PORT_MIDI_LL(PortType)) == false) | |||
| { | |||
| if (! LV2_IS_PORT_OPTIONAL(rdf_descriptor->Ports[i].Properties)) | |||
| { | |||
| @@ -766,26 +766,26 @@ inline bool is_lv2_feature_supported(const char* uri) | |||
| return false; | |||
| } | |||
| inline bool is_lv2_ui_feature_supported(const char */*uri*/) | |||
| inline bool is_lv2_ui_feature_supported(const char* uri) | |||
| { | |||
| // if (strcmp(uri, "http://lv2plug.in/ns/lv2core#hardRTCapable") == 0) | |||
| // return true; | |||
| // else if (strcmp(uri, "http://lv2plug.in/ns/lv2core#inPlaceBroken") == 0) | |||
| // return true; | |||
| // else if (strcmp(uri, "http://lv2plug.in/ns/lv2core#isLive") == 0) | |||
| // return true; | |||
| // else if (strcmp(uri, "http://lv2plug.in/ns/ext/event") == 0) | |||
| // return true; | |||
| // else if (strcmp(uri, "http://lv2plug.in/ns/ext/state#makePath") == 0) | |||
| // return false; // TODO | |||
| // else if (strcmp(uri, "http://lv2plug.in/ns/ext/state#mapPath") == 0) | |||
| // return false; // TODO | |||
| // else if (strcmp(uri, "http://lv2plug.in/ns/ext/uri-map") == 0) | |||
| // return true; | |||
| // else if (strcmp(uri, "http://lv2plug.in/ns/ext/urid#map") == 0) | |||
| // return true; | |||
| // else if (strcmp(uri, "http://lv2plug.in/ns/ext/urid#unmap") == 0) | |||
| // return true; | |||
| if (strcmp(uri, "http://lv2plug.in/ns/lv2core#hardRTCapable") == 0) | |||
| return true; | |||
| else if (strcmp(uri, "http://lv2plug.in/ns/lv2core#inPlaceBroken") == 0) | |||
| return true; | |||
| else if (strcmp(uri, "http://lv2plug.in/ns/lv2core#isLive") == 0) | |||
| return true; | |||
| else if (strcmp(uri, "http://lv2plug.in/ns/ext/event") == 0) | |||
| return true; | |||
| else if (strcmp(uri, "http://lv2plug.in/ns/ext/state#makePath") == 0) | |||
| return false; // TODO | |||
| else if (strcmp(uri, "http://lv2plug.in/ns/ext/state#mapPath") == 0) | |||
| return false; // TODO | |||
| else if (strcmp(uri, "http://lv2plug.in/ns/ext/uri-map") == 0) | |||
| return true; | |||
| else if (strcmp(uri, "http://lv2plug.in/ns/ext/urid#map") == 0) | |||
| return true; | |||
| else if (strcmp(uri, "http://lv2plug.in/ns/ext/urid#unmap") == 0) | |||
| return true; | |||
| // else if (strcmp(uri, "http://lv2plug.in/ns/ext/data-access") == 0) | |||
| // return true; | |||
| // else if (strcmp(uri, "http://lv2plug.in/ns/ext/instance-access") == 0) | |||
| @@ -806,7 +806,7 @@ inline bool is_lv2_ui_feature_supported(const char */*uri*/) | |||
| // return true; | |||
| // else if (strcmp(uri, "http://nedko.arnaudov.name/lv2/external_ui/") == 0) | |||
| // return true; | |||
| // else | |||
| else | |||
| return false; | |||
| } | |||
| @@ -366,9 +366,6 @@ public: | |||
| param.port_cout = jack_port_register(jack_client, port_name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); | |||
| // TODO - auto-assign midi ccs | |||
| // TODO - verify min/max values | |||
| // ---------------------- | |||
| j = Sf2ReverbOnOff; | |||
| param.data[j].index = j; | |||
| @@ -714,7 +711,7 @@ public: | |||
| // Parameters Input [Automation] | |||
| { | |||
| jack_default_audio_sample_t* pin_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(param.port_cin, nframes); | |||
| void* pin_buffer = jack_port_get_buffer(param.port_cin, nframes); | |||
| jack_midi_event_t pin_event; | |||
| uint32_t n_pin_events = jack_midi_get_event_count(pin_buffer); | |||
| @@ -1047,7 +1044,7 @@ public: | |||
| // Control Output | |||
| { | |||
| jack_default_audio_sample_t* cout_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(param.port_cout, nframes); | |||
| void* cout_buffer = jack_port_get_buffer(param.port_cout, nframes); | |||
| jack_midi_clear_buffer(cout_buffer); | |||
| k = Sf2VoiceCount; | |||
| @@ -637,7 +637,7 @@ public: | |||
| if (param.port_cin) | |||
| { | |||
| jack_default_audio_sample_t* pin_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(param.port_cin, nframes); | |||
| void* pin_buffer = jack_port_get_buffer(param.port_cin, nframes); | |||
| jack_midi_event_t pin_event; | |||
| uint32_t n_pin_events = jack_midi_get_event_count(pin_buffer); | |||
| @@ -1208,7 +1208,7 @@ public: | |||
| { | |||
| VstEvents* events = (VstEvents*)ptr; | |||
| jack_default_audio_sample_t* mout_buffer = (jack_default_audio_sample_t*)jack_port_get_buffer(plugin->mout.ports[0], get_buffer_size()); | |||
| void* mout_buffer = jack_port_get_buffer(plugin->mout.ports[0], get_buffer_size()); | |||
| jack_midi_clear_buffer(mout_buffer); | |||
| for (int32_t i=0; i < events->numEvents; i++) | |||
| @@ -624,9 +624,8 @@ PLUGIN_CAN_BALANCE = 0x40 | |||
| # parameter hints | |||
| PARAMETER_IS_ENABLED = 0x01 | |||
| PARAMETER_IS_AUTOMABLE = 0x02 | |||
| PARAMETER_HAS_STRICT_BOUNDS = 0x04 | |||
| PARAMETER_USES_SCALEPOINTS = 0x08 | |||
| PARAMETER_USES_SAMPLERATE = 0x10 | |||
| PARAMETER_USES_SCALEPOINTS = 0x04 | |||
| PARAMETER_USES_SAMPLERATE = 0x08 | |||
| # enum BinaryType | |||
| BINARY_NONE = 0 | |||