summaryrefslogtreecommitdiffstats
path: root/hosts
diff options
context:
space:
mode:
Diffstat (limited to 'hosts')
-rw-r--r--hosts/Makefile.am2
-rw-r--r--hosts/lv2-midifunctions.h161
-rw-r--r--hosts/lv2-miditype.h170
l---------hosts/lv2_event.h1
l---------hosts/lv2_event_helpers.h1
-rw-r--r--hosts/lv2_jack_host.c100
-rw-r--r--hosts/lv2_simple_jack_host.c2
l---------hosts/lv2_uri_map.h1
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, &timestamp, &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