Index: /home/nedko/svn/oberon.atia.com/ssg/lv2-midifunctions.h =================================================================== --- /home/nedko/svn/oberon.atia.com/ssg/lv2-midifunctions.h (revision 3) +++ /home/nedko/svn/oberon.atia.com/ssg/lv2-midifunctions.h (revision 4) @@ -1,132 +0,0 @@ -/**************************************************************************** - - lv2-midifunctions.h - support file for using MIDI in LV2 plugins - - Copyright (C) 2006-2007 Lars Luthman - - 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 - -#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 inline 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 inline 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 inline 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 - Index: /home/nedko/svn/oberon.atia.com/ssg/lv2-midiport.h =================================================================== --- /home/nedko/svn/oberon.atia.com/ssg/lv2-midiport.h (revision 3) +++ /home/nedko/svn/oberon.atia.com/ssg/lv2-midiport.h (revision 4) @@ -1,177 +0,0 @@ -/**************************************************************************** - - lv2-midiport.h - header file for using MIDI in LV2 plugins - - Copyright (C) 2006 Lars Luthman - - 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 - 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 Index: /home/nedko/svn/oberon.atia.com/ssg/lv2_uri_map.h =================================================================== --- /home/nedko/svn/oberon.atia.com/ssg/lv2_uri_map.h (revision 0) +++ /home/nedko/svn/oberon.atia.com/ssg/lv2_uri_map.h (revision 4) @@ -0,0 +1,88 @@ +/* lv2_uri_map.h - C header file for the LV2 URI Map extension. + * + * Copyright (C) 2008 Dave Robillard + * + * This header 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 header 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 header; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA + */ + +#ifndef LV2_URI_MAP_H +#define LV2_URI_MAP_H + +#define LV2_URI_MAP_URI "http://lv2plug.in/ns/ext/uri-map" + +#include + +/** @file + * This header defines the LV2 URI Map extension with the URI + * (preferred prefix 'lv2urimap'). + * + * This extension defines a simple mechanism for plugins to map URIs to + * integers, usually for performance reasons (e.g. processing events + * typed by URIs in real time). The expected use case is for plugins to + * map URIs to integers for things they 'understand' at instantiation time, + * and store those values for use in the audio thread without doing any string + * comparison. This allows the extensibility of RDF with the performance of + * integers (or centrally defined enumerations). + */ + + +/** Opaque pointer to host data. */ +typedef void* LV2_URI_Map_Callback_Data; + + +/** The data field of the LV2_Feature for this extension. + * + * To support this feature the host must pass an LV2_Feature struct to the + * plugin's instantiate method with URI "http://lv2plug.in/ns/ext/uri-map" + * and data pointed to an instance of this struct. + */ +typedef struct { + + /** Opaque pointer to host data. + * + * The plugin MUST pass this to any call to functions in this struct. + * Otherwise, it must not be interpreted in any way. + */ + LV2_URI_Map_Callback_Data callback_data; + + /** Get the numeric ID of a URI from the host. + * + * @param callback_data Must be the callback_data member of this struct. + * @param map The 'context' of this URI. Certain extensions may define a + * URI that must be passed here with certain restrictions on the + * return value (e.g. limited range). This value may be NULL if + * the plugin needs an ID for a URI in general. + * @param uri The URI to be mapped to an integer ID. + * + * This function is referentially transparent - any number of calls with + * the same arguments is guaranteed to return the same value over the life + * of a plugin instance (though the same URI may return different values + * with a different map parameter). However, this function is not + * necessarily very fast: plugins should cache any IDs they might need in + * performance critical situations. + * The return value 0 is reserved and means an ID for that URI could not + * be created for whatever reason. Extensions may define more precisely + * what this means, but in general plugins should gracefully handle 0 + * and consider whatever they wanted the URI for "unsupported". + */ + uint32_t (*uri_to_id)(LV2_URI_Map_Callback_Data callback_data, + const char* map, + const char* uri); + +} LV2_URI_Map_Feature; + + +#endif // LV2_URI_MAP_H + Property changes on: /home/nedko/svn/oberon.atia.com/ssg/lv2_uri_map.h ___________________________________________________________________ Name: svn:executable + * Index: /home/nedko/svn/oberon.atia.com/ssg/lv2_event.h =================================================================== --- /home/nedko/svn/oberon.atia.com/ssg/lv2_event.h (revision 0) +++ /home/nedko/svn/oberon.atia.com/ssg/lv2_event.h (revision 4) @@ -0,0 +1,273 @@ +/* lv2_event.h - C header file for the LV2 events extension. + * + * Copyright (C) 2006-2007 Lars Luthman + * Copyright (C) 2008 Dave Robillard + * + * This header 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 header 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 header; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA + */ + +#ifndef LV2_EVENT_H +#define LV2_EVENT_H + +#define LV2_EVENT_URI "http://lv2plug.in/ns/ext/event" +#define LV2_EVENT_AUDIO_STAMP 0 + +#include + +/** @file + * This header defines the code portion of the LV2 events extension with + * URI . + * + * Below, the URI prefix 'lv2ev' is assumed to expand to + * . + * + * This extension is a generic transport mechanism for time stamped events + * of any type (e.g. MIDI, OSC, ramps, etc). Each port can transport mixed + * events of any type; the type of events and timestamps are defined by a URI + * which is mapped to an integer by the host for performance reasons. + * + * This extension requires the host to support the LV2 URI Map extension. + * This requirement is implicit - a plugin does not have to list the URI Map + * feature as required or optional in its RDF data for the host to provide + * the URI Map LV2_Feature in the instantiation function. + * + * Any host which supports this extension MUST guarantee that any call to + * the LV2 URI Map uri_to_id function with the URI of this extension as the + * 'map' argument returns a value within the range of uint16_t. + */ + + +/** The best Pulses Per Quarter Note for tempo-based uint32_t timestmaps. + * Equal to 2^12 * 5 * 7 * 9 * 11 * 13 * 17, which is evenly divisble + * by all integers from 1 through 18 inclusive, and powers of 2 up to 2^12. + */ +static const uint32_t LV2_EVENT_PPQN = 3136573440U; + + +/** An LV2 event (header only). + * + * LV2 events are generic time-stamped containers for any type of event. + * The type field defines the format of a given event's contents. + * + * This struct defines the header of an LV2 event. An LV2 event, as specified + * in this extension, is a single chunk of POD (plain old data), usually + * contained in a flat buffer (see LV2_EventBuffer below), that can be safely + * copied using a simple: + * + * memcpy(ev_copy, ev, sizeof(LV2_Event) + ev->size); (or equivalent) + * + * However, events with event type 0 need to be handled specially (see below). + */ +typedef struct { + + /** The frames portion of timestamp. The units used here can optionally + * be set for a port (with lv2ev:supportsTimeStamp and related + * properties), otherwise this is audio frames, corresponding to the + * sample_count parameter of the LV2 run method (e.g. frame 0 is the + * first frame for that call to run). + */ + uint32_t frames; + + /** The sub-frames portion of timestamp. The units used here can + * optionally be set for a port (with lv2ev:supportsTimeStamp and + * related properties), otherwise this is 1/(2^32) of an audio frame. + */ + uint32_t subframes; + + /** The type of this event, as a number which represents some URI + * defining an event type. This value MUST be some value previously + * returned from a call to the uri_to_id function defined in the LV2 + * URI map extension (see lv2_uri_map.h). + * + * There are special rules which must be followed depending on the type + * of an event. If the plugin recognizes an event type, the definition + * of that event type will describe how to interpret the event, and + * any required behaviour. Otherwise, if the type is 0, this event is a + * non-POD event and lv2_event_unref MUST be called if the event is + * 'dropped' (see below). Even if the plugin does not understand an + * event, it may pass the event through to an output by simply copying + * (and NOT calling lv2_event_unref). These rules are designed to + * allow for generic event handling plugins and large non-POD events, + * but with minimal hassle on simple plugins that "don't care" about + * these more advanced features. + * + * Plugins should not interpret type 0 events in any way unless + * specified by another extension. + */ + uint16_t type; + + /** The size of the data portion of this event in bytes, which + * immediately follows. The header size (12 bytes) is not included in + * this value. + */ + uint16_t size; + + /* size bytes of data follow here */ + +} LV2_Event; + + + +/** A buffer of LV2 events (header only). + * + * This struct is used as the port buffer for LV2 plugin ports that have the + * port class lv2ev:EventPort. + * + * The data member points to a buffer that contains an event header (defined + * by struct* LV2_Event), followed by that event's contents (padded to 64 bits), + * followed by another header, etc: + * + * | | | | | | | + * | | | | | | | | | | | | | | | | | | | | | | | | | + * |FRAMES |SUBFRMS|TYP|LEN|DATA..DATA..PAD|FRAMES | ... + */ +typedef struct { + + /** The contents of the event buffer. This may or may not reside in the + * same block of memory as this header, plugins must not assume either. + * The host guarantees this points to at least capacity bytes of + * allocated memory (though only size bytes of that are valid events). + */ + uint8_t* data; + + /** The size of this event header in bytes (including everything). + * + * This is to allow for extending this header in the future without + * breaking binary compatibility. Whenever this header is copied, + * it MUST be done using this field (and NOT the sizeof this struct). + */ + uint16_t header_size; + + /** The type of the time stamps for events in this buffer. + * As a special exception, '0' always means audio frames and subframes + * (1/UINT32_MAX'th of a frame) in the sample rate passed to instantiate. + * INPUTS: The host must set this field to the numeric ID of some URI + * defining the meaning of the frames/subframes fields of contained + * events (obtained by the LV2 URI Map uri_to_id function with the URI + * of this extension as the 'map' argument, see lv2_uri_map.h). + * The host must never pass a plugin a buffer which uses a stamp type + * the plugin does not 'understand'. The value of this field must + * never change, except when connect_port is called on the input + * port, at which time the host MUST have set the stamp_type field to + * the value that will be used for all subsequent run calls. + * OUTPUTS: The plugin may set this to any value that has been returned + * from uri_to_id with the URI of this extension for a 'map' argument. + * When connected to a buffer with connect_port, output ports MUST set + * this field to the type of time stamp they will be writing. On any + * call to connect_port on an event input port, the plugin may change + * this field on any output port, it is the responsibility of the host + * to check if any of these values have changed and act accordingly. + */ + uint16_t stamp_type; + + /** The number of events in this buffer. + * INPUTS: The host must set this field to the number of events + * contained in the data buffer before calling run(). + * The plugin must not change this field. + * OUTPUTS: The plugin must set this field to the number of events it + * has written to the buffer before returning from run(). + * Any initial value should be ignored by the plugin. + */ + uint32_t event_count; + + /** The size of the data buffer in bytes. + * This is set by the host and must not be changed by the plugin. + * The host is allowed to change this between run() calls. + */ + uint32_t capacity; + + /** The size of the initial portion of the data buffer containing data. + * INPUTS: The host must set this field to the number of bytes used + * by all events it has written to the buffer (including headers) + * before calling the plugin's run(). + * The plugin must not change this field. + * OUTPUTS: The plugin must set this field to the number of bytes + * used by all events it has written to the buffer (including headers) + * before returning from run(). + * Any initial value should be ignored by the plugin. + */ + uint32_t size; + +} LV2_Event_Buffer; + + +/** Opaque pointer to host data. */ +typedef void* LV2_Event_Callback_Data; + + +/** The data field of the LV2_Feature for this extension. + * + * To support this extension the host must pass an LV2_Feature struct to the + * plugin's instantiate method with URI "http://lv2plug.in/ns/ext/event" + * and data pointed to an instance of this struct. The plugin does not have + * to list that URI as a required or optional feature in its RDF data - the + * host MUST pass this LV2_Feature if the plugin has an port of class + * lv2ev:EventPortthat the host connects to. + */ +typedef struct { + + /** Opaque pointer to host data. + * + * The plugin MUST pass this to any call to functions in this struct. + * Otherwise, it must not be interpreted in any way. + */ + LV2_Event_Callback_Data callback_data; + + /** Take a reference to a non-POD event. + * + * If a plugin receives an event with type 0, it means the event is a + * pointer to some object in memory and not a flat sequence of bytes + * in the buffer. When receiving a non-POD event, the plugin already + * has an implicit reference to the event. If the event is stored AND + * passed to an output, or passed to two outputs, lv2_event_ref MUST + * be called on that event. + * If the event is only stored OR passed through, this is not necessary + * (as the plugin already has 1 implicit reference). + * + * The host guarantees that this function is realtime safe if the + * plugin is. + * + * @param event An event received at an input that will be copied to + * more than one output or duplicated in some other way. + * + * PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. + */ + uint32_t (*lv2_event_ref)(LV2_Event_Callback_Data callback_data, + LV2_Event* event); + + /** Drop a reference to a non-POD event. + * + * If a plugin receives an event with type 0, it means the event is a + * pointer to some object in memory and not a flat sequence of bytes + * in the buffer. If the plugin does not pass the event through to + * an output or store it internally somehow, it MUST call this function + * on the event (more information on using non-POD events below). + * + * The host guarantees that this function is realtime safe if the + * plugin is. + * + * @param event An event received at an input that will not be copied to + * an output or stored in any way. + * + * PLUGINS THAT VIOLATE THESE RULES MAY CAUSE CRASHES AND MEMORY LEAKS. + */ + uint32_t (*lv2_event_unref)(LV2_Event_Callback_Data callback_data, + LV2_Event* event); + +} LV2_Event_Feature; + + +#endif // LV2_EVENT_H + Property changes on: /home/nedko/svn/oberon.atia.com/ssg/lv2_event.h ___________________________________________________________________ Name: svn:executable + * Index: /home/nedko/svn/oberon.atia.com/ssg/lv2_event_helpers.h =================================================================== --- /home/nedko/svn/oberon.atia.com/ssg/lv2_event_helpers.h (revision 0) +++ /home/nedko/svn/oberon.atia.com/ssg/lv2_event_helpers.h (revision 4) @@ -0,0 +1,262 @@ +/* lv2_event_helpers.h - Helper functions for the LV2 events extension. + * + * Copyright (C) 2008 Dave Robillard + * + * This header 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 header 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 header; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 01222-1307 USA + */ + +#ifndef LV2_EVENT_HELPERS_H +#define LV2_EVENT_HELPERS_H + +#include +#include +#include +#include +#include + +#include + +/** @file + * This header defines some helper functions for the the LV2 events extension + * with URI ('lv2ev'). + * + * These functions are provided for convenience only, use of them is not + * required for supporting lv2ev (i.e. the events extension is defined by the + * raw buffer format described in lv2_event.h and NOT by this API). + * + * Note that these functions are all static inline which basically means: + * do not take the address of these functions. */ + + +/** Pad a size to 64 bits (for event sizes) */ +static inline uint16_t +lv2_event_pad_size(uint16_t size) +{ + return (size + 7) & (~7); +} + + +/** Initialize (empty, reset..) an existing event buffer. + * The contents of buf are ignored entirely and overwritten, except capacity + * which is unmodified. */ +static inline void +lv2_event_buffer_reset(LV2_Event_Buffer* buf, uint16_t stamp_type, uint8_t *data) +{ + buf->data = data; + buf->header_size = sizeof(LV2_Event_Buffer); + buf->stamp_type = stamp_type; + buf->event_count = 0; + buf->size = 0; +} + + +/** Allocate a new, empty event buffer. */ +static inline LV2_Event_Buffer* +lv2_event_buffer_new(uint32_t capacity, uint16_t stamp_type) +{ + LV2_Event_Buffer* buf = (LV2_Event_Buffer*)malloc(sizeof(LV2_Event_Buffer) + capacity); + if (buf != NULL) { + buf->capacity = capacity; + lv2_event_buffer_reset(buf, stamp_type, (uint8_t *)(buf + 1)); + return buf; + } else { + return NULL; + } +} + + +/** An iterator over an LV2_Event_Buffer. + * + * Multiple simultaneous read iterators over a single buffer is fine, + * but changing the buffer invalidates all iterators (e.g. RW Lock). */ +typedef struct { + LV2_Event_Buffer* buf; + uint32_t offset; +} LV2_Event_Iterator; + + +/** Reset an iterator to point to the start of @a buf. + * @return True if @a iter is valid, otherwise false (buffer is empty) */ +static inline bool +lv2_event_begin(LV2_Event_Iterator* iter, + LV2_Event_Buffer* buf) +{ + iter->buf = buf; + iter->offset = 0; + return (buf->size > 0); +} + + +/** Check if @a iter is valid.. + * @return True if @a iter is valid, otherwise false (past end of buffer) */ +static inline bool +lv2_event_is_valid(LV2_Event_Iterator* iter) +{ + return (iter->offset < iter->buf->size); +} + + +/** Advance @a iter forward one event. + * @a iter must be valid. + * @return True if @a iter is valid, otherwise false (reached end of buffer) */ +static inline bool +lv2_event_increment(LV2_Event_Iterator* iter) +{ + assert(lv2_event_is_valid(iter)); + + LV2_Event* const ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + iter->offset += lv2_event_pad_size(sizeof(LV2_Event) + ev->size); + + return true; +} + + +/** Dereference an event iterator (get the event currently pointed at). + * @a iter must be valid. + * @a data if non-NULL, will be set to point to the contents of the event + * returned. + * @return A Pointer to the event @a iter is currently pointing at, or NULL + * if the end of the buffer is reached (in which case @a data is + * also set to NULL). */ +static inline LV2_Event* +lv2_event_get(LV2_Event_Iterator* iter, + uint8_t** data) +{ + assert(lv2_event_is_valid(iter)); + + LV2_Event* const ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + if (data) + *data = (uint8_t*)ev + sizeof(LV2_Event); + + return ev; +} + + +/** Get the type of the non-POD event referenced by an event iterator. + * @a iter must be valid. + * @return The type of the non-POD event, or 0 if the event is not non-POD. */ +static inline uint16_t +lv2_event_get_nonpod_type(LV2_Event_Iterator* iter) +{ + assert(lv2_event_is_valid(iter)); + + LV2_Event* const ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + if (ev->type != 0 || ev->size < 2) + return 0; + + return *(uint16_t*)((uint8_t*)ev + sizeof(LV2_Event)); +} + + +/** Write an event at @a iter. + * The event (if any) pointed to by @iter will be overwritten, and @a iter + * incremented to point to the following event (i.e. several calls to this + * function can be done in sequence without twiddling iter in-between). + * @return True if event was written, otherwise false (buffer is full). */ +static inline bool +lv2_event_write(LV2_Event_Iterator* iter, + uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size, + const uint8_t* data) +{ + if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) + return false; + + LV2_Event* const ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + ev->frames = frames; + ev->subframes = subframes; + ev->type = type; + ev->size = size; + memcpy((uint8_t*)ev + sizeof(LV2_Event), data, size); + ++iter->buf->event_count; + + size = lv2_event_pad_size(sizeof(LV2_Event) + size); + iter->buf->size += size; + iter->offset += size; + + return true; +} + + +/** Reserve space for an event in the buffer and return a pointer to + * the memory where the caller can write the event data, or NULL if there + * is not enough room in the buffer. */ +static inline uint8_t* +lv2_event_reserve(LV2_Event_Iterator* iter, + uint32_t frames, + uint32_t subframes, + uint16_t type, + uint16_t size) +{ + size = lv2_event_pad_size(size); + if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + size) + return NULL; + + LV2_Event* const ev = (LV2_Event*)((uint8_t*)iter->buf->data + + iter->offset); + + ev->frames = frames; + ev->subframes = subframes; + ev->type = type; + ev->size = size; + ++iter->buf->event_count; + + size = lv2_event_pad_size(sizeof(LV2_Event) + size); + iter->buf->size += size; + iter->offset += size; + + return (uint8_t*)ev + sizeof(LV2_Event); +} + + +/** Write an event at @a iter. + * The event (if any) pointed to by @iter will be overwritten, and @a iter + * incremented to point to the following event (i.e. several calls to this + * function can be done in sequence without twiddling iter in-between). + * @return True if event was written, otherwise false (buffer is full). */ +static inline bool +lv2_event_write_event(LV2_Event_Iterator* iter, + const LV2_Event* ev, + const uint8_t* data) +{ + if (iter->buf->capacity - iter->buf->size < sizeof(LV2_Event) + ev->size) + return false; + + LV2_Event* const write_ev = (LV2_Event*)( + (uint8_t*)iter->buf->data + iter->offset); + + *write_ev = *ev; + memcpy((uint8_t*)write_ev + sizeof(LV2_Event), data, ev->size); + ++iter->buf->event_count; + + const uint16_t size = lv2_event_pad_size(sizeof(LV2_Event) + ev->size); + iter->buf->size += size; + iter->offset += size; + + return true; +} + +#endif // LV2_EVENT_HELPERS_H + Property changes on: /home/nedko/svn/oberon.atia.com/ssg/lv2_event_helpers.h ___________________________________________________________________ Name: svn:executable + * Index: /home/nedko/svn/oberon.atia.com/ssg/ssg.c =================================================================== --- /home/nedko/svn/oberon.atia.com/ssg/ssg.c (revision 3) +++ /home/nedko/svn/oberon.atia.com/ssg/ssg.c (revision 4) @@ -19,63 +19,94 @@ *****************************************************************************/ #include +#include #include #include #include -#include "lv2-midiport.h" -#include "lv2-midifunctions.h" +#include "lv2_event.h" +#include "lv2_event_helpers.h" +#include "lv2_uri_map.h" #define MIDI_INPUT_PORT_INDEX 0 #define AUDIO_OUTPUT_PORT_INDEX 1 -struct ssg -{ - LV2_MIDI * input; - float * output; - float ramp; - float note_volume; - unsigned char note; - float note_frqs[128]; + +static LV2_Event_Buffer *EventBuffer = NULL; +static LV2_Event_Feature *event_ref = NULL; + +struct ssg { + float * output; + float ramp; + float note_volume; + unsigned char note; + float note_frqs[128]; + int midi_event_id; }; -static -LV2_Handle -instantiate( +static LV2_Handle instantiate( const LV2_Descriptor * descriptor, double sample_rate, const char * bundle_path, - const LV2_Feature * const * host_features) -{ - struct ssg * ssg_ptr; - int i; + const LV2_Feature * const * host_features) { - ssg_ptr = malloc(sizeof(struct ssg)); + struct ssg * ssg_ptr; + LV2_URI_Map_Feature *map_feature; + int i; - ssg_ptr->ramp = 0.0; - ssg_ptr->note = 0; - ssg_ptr->note_volume = 0.0; + ssg_ptr = malloc(sizeof(struct ssg)); - for (i = 0 ; i < 128 ; i++) - { - ssg_ptr->note_frqs[i] = (2.0 * 440.0 / 32.0) * pow(2, (((float)i - 9.0) / 12.0)) / sample_rate; - } + ssg_ptr->ramp = 0.0; + ssg_ptr->note = 0; + ssg_ptr->note_volume = 0.0; + for (i = 0 ; i < 128 ; i++) { + ssg_ptr->note_frqs[i] = (2.0 * 440.0 / 32.0) * pow(2, (((float)i - 9.0) / 12.0)) / sample_rate; + } + + + + for (i = 0; host_features[i]; ++i) { + if (!strcmp(host_features[i]->URI, "http://lv2plug.in/ns/ext/uri-map")) { + map_feature = host_features[i]->data; + ssg_ptr->midi_event_id = + map_feature->uri_to_id(map_feature->callback_data, "http://lv2plug.in/ns/ext/event", "http://lv2plug.in/ns/ext/midi#MidiEvent"); + } + else if (!strcmp(host_features[i]->URI, "http://lv2plug.in/ns/ext/event")) { + event_ref = host_features[i]->data; + } + } + if (ssg_ptr->midi_event_id == 0 || event_ref == NULL) { + printf("init failed, leaving\n"); + return NULL; + } + return (LV2_Handle)ssg_ptr; } -static -inline -void -generate( + +static inline uint16_t handle_event(LV2_Event* ev, struct ssg * ssg_ptr) { + + uint16_t size = 0; + + if (ev->type == 0) + event_ref->lv2_event_unref(event_ref->callback_data, ev); + else if (ev->type == ssg_ptr->midi_event_id) { + size = ev->size; + } + return size; +} + + +static inline void generate( struct ssg * ssg_ptr, uint32_t start, /* index of first sample to process */ - uint32_t end) /* index of sample to stop processing before */ -{ + uint32_t end) { /* index of sample to stop processing before */ + uint32_t pos; for (pos = start ; pos < end ; pos++) - { + { ssg_ptr->ramp += ssg_ptr->note_frqs[ssg_ptr->note]; ssg_ptr->ramp = (ssg_ptr->ramp > 1.0) ? ssg_ptr->ramp - 2.0 : ssg_ptr->ramp; ssg_ptr->output[pos] = ssg_ptr->note_volume * sin(2 * M_PI * ssg_ptr->ramp); @@ -84,89 +115,89 @@ #define ssg_ptr ((struct ssg *)lv2instance) -static -void -connect_port( - LV2_Handle lv2instance, - uint32_t port, - void * data) -{ - switch (port) - { - case MIDI_INPUT_PORT_INDEX: - ssg_ptr->input = data; - break; - case AUDIO_OUTPUT_PORT_INDEX: - ssg_ptr->output = data; - break; - } +static void connect_port(LV2_Handle lv2instance, uint32_t port, void * data) { + + switch (port) { + case MIDI_INPUT_PORT_INDEX: + EventBuffer = data; + break; + case AUDIO_OUTPUT_PORT_INDEX: + ssg_ptr->output = data; + break; + } } -static -void -cleanup(LV2_Handle lv2instance) -{ - free(ssg_ptr); +static void cleanup(LV2_Handle lv2instance) { + free(ssg_ptr); } -static -void -run( - LV2_Handle lv2instance, - uint32_t sample_count) -{ - uint32_t offset; - LV2_MIDIState midi; - uint32_t event_size; - double event_timestamp; - unsigned char * event_data; - midi.midi = ssg_ptr->input; - midi.frame_count = sample_count; - midi.position = 0; +static void run(LV2_Handle lv2instance, uint32_t sample_count) { - offset = 0; + uint32_t offset = 0; + LV2_Event_Iterator iterator; + uint32_t frames_done = 0; + uint16_t size = 0; + uint8_t *midi_data; - while (lv2midi_get_event(&midi, &event_timestamp, &event_size, &event_data) < sample_count) - { - if (event_size == 3 && (event_data[0] & 0xF0) == 0x90 && event_data[1] < 128) - { - /* note on */ - generate(ssg_ptr, offset, event_timestamp); - ssg_ptr->note = event_data[1]; - ssg_ptr->note_volume = 1.0; /* we could map velocity to volume here */ - offset = event_timestamp; + lv2_event_begin(&iterator, EventBuffer); + + while (frames_done < sample_count) { + + uint32_t to; + + if (lv2_event_is_valid(&iterator)) { + size = handle_event(lv2_event_get(&iterator, NULL), ssg_ptr); + to = lv2_event_get(&iterator, NULL)->frames; + midi_data = (uint8_t *) lv2_event_get(&iterator, NULL) + 12; + } + + /* note on */ + if (size == 3 && ((*midi_data & 0xF0) == 0x90) && (*(midi_data+1) < 128)) { + generate(ssg_ptr, offset, to); + ssg_ptr->note = *(midi_data+1); + ssg_ptr->note_volume = 1.0; // we could map velocity to volume here + offset = to; + frames_done += offset; } - else if (event_size == 3 && (event_data[0] & 0xF0) == 0x80) - { - /* note off */ - generate(ssg_ptr, offset, event_timestamp); + + /* note off */ + else if (size == 3 && ((*midi_data & 0xF0) == 0x80)) { + generate(ssg_ptr, offset, to); ssg_ptr->note_volume = 0.0; - offset = event_timestamp; + offset = to; + frames_done += offset; } + + else { + generate(ssg_ptr, offset, sample_count); + frames_done = sample_count; + } - lv2midi_step(&midi); + if (lv2_event_is_valid(&iterator)) { + lv2_event_increment(&iterator); + size = 0; + } + } - generate(ssg_ptr, offset, sample_count); } + static LV2_Descriptor g_lv2descriptor = { - .URI = "http://nedko.arnaudov.name/ssg/0", - .instantiate = instantiate, - .connect_port = connect_port, - .run = run, - .cleanup = cleanup, + .URI = "http://nedko.arnaudov.name/ssg/0", + .instantiate = instantiate, + .connect_port = connect_port, + .run = run, + .cleanup = cleanup, }; -const LV2_Descriptor * -lv2_descriptor(uint32_t index) -{ - if (index == 0) - { - return &g_lv2descriptor; - } - return NULL; +const LV2_Descriptor *lv2_descriptor(uint32_t index) { + + if (index == 0) { + return &g_lv2descriptor; + } + return NULL; } Index: /home/nedko/svn/oberon.atia.com/ssg/ssg.ttl =================================================================== --- /home/nedko/svn/oberon.atia.com/ssg/ssg.ttl (revision 3) +++ /home/nedko/svn/oberon.atia.com/ssg/ssg.ttl (revision 4) @@ -1,6 +1,7 @@ @prefix lv2: . @prefix foaf: . @prefix doap: . +@prefix lv2ev: . a lv2:Plugin; @@ -13,9 +14,11 @@ doap:license ; lv2:port [ - a lv2:InputPort, ; + a lv2ev:EventPort; + a lv2:InputPort ; + lv2ev:supportsEvent ; lv2:index 0; - lv2:symbol "midi_in"; + lv2:symbol "in"; lv2:name "MIDI Input"; ],