diff options
Diffstat (limited to 'hosts')
-rw-r--r-- | hosts/Makefile.am | 2 | ||||
-rw-r--r-- | hosts/lv2-midifunctions.h | 161 | ||||
-rw-r--r-- | hosts/lv2-miditype.h | 170 | ||||
l--------- | hosts/lv2_event.h | 1 | ||||
l--------- | hosts/lv2_event_helpers.h | 1 | ||||
-rw-r--r-- | hosts/lv2_jack_host.c | 100 | ||||
-rw-r--r-- | hosts/lv2_simple_jack_host.c | 2 | ||||
l--------- | hosts/lv2_uri_map.h | 1 |
8 files changed, 62 insertions, 376 deletions
diff --git a/hosts/Makefile.am b/hosts/Makefile.am index 67fde28..b403e1b 100644 --- a/hosts/Makefile.am +++ b/hosts/Makefile.am @@ -2,7 +2,7 @@ AM_CFLAGS = -std=c99 -I$(top_srcdir) @REDLAND_CFLAGS@ @SLV2_CFLAGS@ if WITH_JACK -noinst_HEADERS = lv2-miditype.h lv2-midifunctions.h +noinst_HEADERS = lv2_event.h lv2_event_helpers.h bin_PROGRAMS = lv2_jack_host lv2_simple_jack_host diff --git a/hosts/lv2-midifunctions.h b/hosts/lv2-midifunctions.h deleted file mode 100644 index 3a17395..0000000 --- a/hosts/lv2-midifunctions.h +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** - - lv2-midifunctions.h - support 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 - -****************************************************************************/ - -#ifndef LV2_MIDIFUNCTIONS -#define LV2_MIDIFUNCTIONS - -#include <string.h> - -#include "lv2-miditype.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; - - -static LV2_MIDI* lv2midi_new(uint32_t capacity) -{ - LV2_MIDI* midi = malloc(sizeof(LV2_MIDI)); - - midi->event_count = 0; - midi->capacity = capacity; - midi->size = 0; - midi->data = malloc(sizeof(char) * capacity); - - return midi; -} - - -static void lv2midi_free(LV2_MIDI* midi) -{ - free(midi->data); - free(midi); -} - - -static void lv2midi_reset_buffer(LV2_MIDI* midi) -{ - midi->event_count = 0; - midi->size = 0; -} - -static void lv2midi_reset_state(LV2_MIDIState* state, LV2_MIDI* midi, uint32_t frame_count) -{ - state->midi = midi; - state->frame_count = frame_count; - state->position = 0; -} - - -/** 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_increment(LV2_MIDIState* state) { - - if (state->position + sizeof(double) + sizeof(size_t) >= state->midi->size) { - state->position = state->midi->size; - return state->frame_count; - } - - state->position += sizeof(double); - size_t size = *(size_t*)(state->midi->data + state->position); - state->position += sizeof(size_t); - state->position += size; - - if (state->position >= state->midi->size) - return state->frame_count; - - return *(double*)(state->midi->data + state->position); -} - - -/** 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 = *(size_t*)(state->midi->data + state->position + sizeof(double)); - *data = state->midi->data + state->position + - sizeof(double) + sizeof(size_t); - return *timestamp; -} - - -/** 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(size_t) + size) - return -1; - - *(double*)(state->midi->data + state->midi->size) = timestamp; - state->midi->size += sizeof(double); - *(size_t*)(state->midi->data + state->midi->size) = size; - state->midi->size += sizeof(size_t); - memcpy(state->midi->data + state->midi->size, data, (size_t)size); - state->midi->size += size; - - ++state->midi->event_count; - - return 0; -} - - -#endif - diff --git a/hosts/lv2-miditype.h b/hosts/lv2-miditype.h deleted file mode 100644 index 465d5d5..0000000 --- a/hosts/lv2-miditype.h +++ /dev/null @@ -1,170 +0,0 @@ -/**************************************************************************** - - lv2-miditype.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 - -****************************************************************************/ - -#ifndef LV2_MIDITYPE_H -#define LV2_MIDITYPE_H - - -/** This data structure is used to contain the MIDI events for one run() - cycle. The port buffer for a LV2 port that has the datatype - <http://ll-plugins.nongnu.org/lv2/ext/miditype> 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 - - size_t buffer_offset = 0; - *(double*)(midi_data->data + buffer_offset) = 12; - buffer_offset += sizeof(double); - *(size_t*)(midi_data->data + buffer_offset) = 3; - buffer_offset += sizeof(size_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); - *(size_t*)(midi_data->data + buffer_offset) = 3; - buffer_offset += sizeof(size_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 - - size_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); - size_t size = *(size_t*)(midi_data->data + buffer_offset); - buffer_offset += sizeof(size_t); - do_something_with_event(timestamp, size, - midi_data->data + buffer_offset); - buffer_offset += size; - } - - @endcode -*/ -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 - "size_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 and size_t is 4 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 diff --git a/hosts/lv2_event.h b/hosts/lv2_event.h new file mode 120000 index 0000000..4c8ba8b --- /dev/null +++ b/hosts/lv2_event.h @@ -0,0 +1 @@ +../../lv2/lv2/event/lv2_event.h
\ No newline at end of file diff --git a/hosts/lv2_event_helpers.h b/hosts/lv2_event_helpers.h new file mode 120000 index 0000000..221fa8d --- /dev/null +++ b/hosts/lv2_event_helpers.h @@ -0,0 +1 @@ +../../lv2/lv2/event/lv2_event_helpers.h
\ No newline at end of file diff --git a/hosts/lv2_jack_host.c b/hosts/lv2_jack_host.c index 5caa8b8..6b5c61c 100644 --- a/hosts/lv2_jack_host.c +++ b/hosts/lv2_jack_host.c @@ -25,8 +25,9 @@ #include <slv2/slv2.h> #include <jack/jack.h> #include <jack/midiport.h> -#include "lv2-miditype.h" -#include "lv2-midifunctions.h" +#include "lv2_uri_map.h" +#include "lv2_event.h" +#include "lv2_event_helpers.h" #include "jack_compat.h" #define MIDI_BUFFER_SIZE 1024 @@ -39,16 +40,16 @@ enum PortDirection { enum PortType { CONTROL, AUDIO, - MIDI + EVENT }; struct Port { SLV2Port slv2_port; enum PortDirection direction; enum PortType type; - jack_port_t* jack_port; /**< For audio and MIDI ports, otherwise NULL */ - float control; /**< For control ports, otherwise 0.0f */ - LV2_MIDI* midi_buffer; /**< For midi ports, otherwise NULL */ + jack_port_t* jack_port; /**< For audio and MIDI ports, otherwise NULL */ + float control; /**< For control ports, otherwise 0.0f */ + LV2_Event_Buffer* ev_buffer; /**< For midi ports, otherwise NULL */ }; @@ -63,10 +64,27 @@ struct JackHost { SLV2Value output_class; /**< Output port class (URI) */ SLV2Value control_class; /**< Control port class (URI) */ SLV2Value audio_class; /**< Audio port class (URI) */ - SLV2Value midi_class; /**< MIDI port class (URI) */ + SLV2Value event_class; /**< Event port class (URI) */ + SLV2Value midi_class; /**< MIDI event class (URI) */ SLV2Value optional; /**< lv2:connectionOptional port property */ }; +/** URI map feature, for event types (we use only MIDI) */ +#define MIDI_EVENT_ID 1 +uint32_t +uri_to_id(LV2_URI_Map_Callback_Data callback_data, + const char* map, + const char* uri) +{ + if (!strcmp(map, LV2_EVENT_URI) && !strcmp(uri, SLV2_EVENT_CLASS_MIDI)) + return MIDI_EVENT_ID; + else + return 0; // no id for you! +} + +static LV2_URI_Map_Feature uri_map = { &uri_to_id, NULL }; +static const LV2_Feature uri_map_feature = { "http://lv2plug.in/ns/ext/uri-map", &uri_map }; +const LV2_Feature* features[2] = { &uri_map_feature, NULL }; void die(const char* msg); void create_port(struct JackHost* host, uint32_t port_index); @@ -92,7 +110,8 @@ main(int argc, char** argv) host.output_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_OUTPUT); host.control_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_CONTROL); host.audio_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_AUDIO); - host.midi_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_MIDI); + host.event_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_EVENT); + host.midi_class = slv2_value_new_uri(world, SLV2_EVENT_CLASS_MIDI); host.optional = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "connectionOptional"); /* Find the plugin to run */ @@ -133,7 +152,7 @@ main(int argc, char** argv) } /* Connect to JACK */ - printf("JACK Name:\t%s\n", name_str); + printf("JACK Name:\t%s\n", jack_name); host.jack_client = jack_client_open(jack_name, JackNullOption, NULL); free(jack_name); @@ -146,7 +165,7 @@ main(int argc, char** argv) /* Instantiate the plugin */ host.instance = slv2_plugin_instantiate( - host.plugin, jack_get_sample_rate(host.jack_client), NULL); + host.plugin, jack_get_sample_rate(host.jack_client), features); if (!host.instance) die("Failed to instantiate plugin.\n"); else @@ -179,8 +198,8 @@ main(int argc, char** argv) jack_port_unregister(host.jack_client, host.ports[i].jack_port); host.ports[i].jack_port = NULL; } - if (host.ports[i].midi_buffer != NULL) { - lv2midi_free(host.ports[i].midi_buffer); + if (host.ports[i].ev_buffer != NULL) { + free(host.ports[i].ev_buffer); } } jack_client_close(host.jack_client); @@ -194,6 +213,7 @@ main(int argc, char** argv) slv2_value_free(host.output_class); slv2_value_free(host.control_class); slv2_value_free(host.audio_class); + slv2_value_free(host.event_class); slv2_value_free(host.midi_class); slv2_value_free(host.optional); slv2_plugins_free(world, plugins); @@ -225,10 +245,10 @@ create_port(struct JackHost* host, { struct Port* const port = &host->ports[port_index]; - port->slv2_port = slv2_plugin_get_port_by_index(host->plugin, port_index); - port->jack_port = NULL; - port->control = 0.0f; - port->midi_buffer = NULL; + port->slv2_port = slv2_plugin_get_port_by_index(host->plugin, port_index); + port->jack_port = NULL; + port->control = 0.0f; + port->ev_buffer = NULL; slv2_instance_connect_port(host->instance, port_index, NULL); @@ -254,13 +274,13 @@ create_port(struct JackHost* host, port->type = CONTROL; SLV2Value def; slv2_port_get_range(host->plugin, port->slv2_port, &def, NULL, NULL); - port->control = slv2_value_as_float(def); + port->control = def ? slv2_value_as_float(def) : 0.0f; printf("Set %s to %f\n", symbol_str, host->ports[port_index].control); slv2_value_free(def); } else if (slv2_port_is_a(host->plugin, port->slv2_port, host->audio_class)) { port->type = AUDIO; - } else if (slv2_port_is_a(host->plugin, port->slv2_port, host->midi_class)) { - port->type = MIDI; + } else if (slv2_port_is_a(host->plugin, port->slv2_port, host->event_class)) { + port->type = EVENT; } /* Connect the port based on it's type */ @@ -272,11 +292,11 @@ create_port(struct JackHost* host, port->jack_port = jack_port_register(host->jack_client, symbol_str, JACK_DEFAULT_AUDIO_TYPE, jack_flags, 0); break; - case MIDI: + case EVENT: port->jack_port = jack_port_register(host->jack_client, - symbol_str, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); - port->midi_buffer = lv2midi_new(MIDI_BUFFER_SIZE); - slv2_instance_connect_port(host->instance, port_index, port->midi_buffer); + symbol_str, JACK_DEFAULT_MIDI_TYPE, jack_flags, 0); + port->ev_buffer = lv2_event_buffer_new(MIDI_BUFFER_SIZE, LV2_EVENT_AUDIO_STAMP); + slv2_instance_connect_port(host->instance, port_index, port->ev_buffer); break; default: // FIXME: check if port connection is is optional and die if not @@ -302,17 +322,15 @@ jack_process_cb(jack_nframes_t nframes, void* data) slv2_instance_connect_port(host->instance, p, jack_port_get_buffer(host->ports[p].jack_port, nframes)); - } else if (host->ports[p].type == MIDI) { + } else if (host->ports[p].type == EVENT) { - lv2midi_reset_buffer(host->ports[p].midi_buffer); + lv2_event_buffer_reset(host->ports[p].ev_buffer, LV2_EVENT_AUDIO_STAMP); if (host->ports[p].direction == INPUT) { void* jack_buffer = jack_port_get_buffer(host->ports[p].jack_port, nframes); - lv2midi_reset_buffer(host->ports[p].midi_buffer); - - LV2_MIDIState state; - lv2midi_reset_state(&state, host->ports[p].midi_buffer, nframes); + LV2_Event_Iterator iter; + lv2_event_begin(&iter, host->ports[p].ev_buffer); const jack_nframes_t event_count = jack_midi_get_event_count(jack_buffer); @@ -321,9 +339,8 @@ jack_process_cb(jack_nframes_t nframes, void* data) for (jack_nframes_t e=0; e < event_count; ++e) { jack_midi_event_get(&ev, jack_buffer, e); - lv2midi_put_event(&state, (double)ev.time, ev.size, ev.buffer); + lv2_event_append(&iter, ev.time, 0, MIDI_EVENT_ID, ev.size, ev.buffer); } - } } } @@ -337,33 +354,30 @@ jack_process_cb(jack_nframes_t nframes, void* data) for (uint32_t p=0; p < host->num_ports; ++p) { if (host->ports[p].jack_port && host->ports[p].direction == INPUT - && host->ports[p].type == MIDI) { + && host->ports[p].type == EVENT) { void* jack_buffer = jack_port_get_buffer(host->ports[p].jack_port, nframes); jack_midi_clear_buffer(jack_buffer); - LV2_MIDIState state; - lv2midi_reset_state(&state, host->ports[p].midi_buffer, nframes); - - double timestamp = 0.0f; - uint32_t size = 0; - unsigned char* data = NULL; + LV2_Event_Iterator iter; + lv2_event_begin(&iter, host->ports[p].ev_buffer); - const uint32_t event_count = state.midi->event_count; + const uint32_t event_count = iter.buf->event_count; for (uint32_t i=0; i < event_count; ++i) { - lv2midi_get_event(&state, ×tamp, &size, &data); + uint8_t* data; + LV2_Event* ev = lv2_event_get(&iter, &data); #if defined(JACK_MIDI_NEEDS_NFRAMES) jack_midi_event_write(jack_buffer, - (jack_nframes_t)timestamp, data, size, nframes); + (jack_nframes_t)ev->frames, data, size, nframes); #else jack_midi_event_write(jack_buffer, - (jack_nframes_t)timestamp, data, size); + (jack_nframes_t)ev->frames, data, ev->size); #endif - lv2midi_increment(&state); + lv2_event_increment(&iter); } } diff --git a/hosts/lv2_simple_jack_host.c b/hosts/lv2_simple_jack_host.c index cf50d86..37991da 100644 --- a/hosts/lv2_simple_jack_host.c +++ b/hosts/lv2_simple_jack_host.c @@ -105,7 +105,7 @@ main(int argc, char** argv) } /* Connect to JACK */ - printf("JACK Name:\t%s\n", name_str); + printf("JACK Name:\t%s\n", jack_name); host.jack_client = jack_client_open(jack_name, JackNullOption, NULL); free(jack_name); diff --git a/hosts/lv2_uri_map.h b/hosts/lv2_uri_map.h new file mode 120000 index 0000000..408c2f2 --- /dev/null +++ b/hosts/lv2_uri_map.h @@ -0,0 +1 @@ +../../lv2/lv2/uri_map/lv2_uri_map.h
\ No newline at end of file |