diff options
author | David Robillard <d@drobilla.net> | 2014-02-08 02:30:06 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2014-02-08 02:30:06 +0000 |
commit | 60eb52f31976763497cd0355cc0d6b46af6c465f (patch) | |
tree | 13783ae9948977c809d2907ee7b80aa0ebbdb2d9 | |
parent | 6d41effb84c6663704d7f2ed562d7bbf4f5cc441 (diff) | |
download | lv2-60eb52f31976763497cd0355cc0d6b46af6c465f.tar.xz |
Add lv2_atom_sequence_clear() and lv2_atom_sequence_append_event() helper functions.
Add MIDI fifths example plugin for simple non-forge MIDI reading and writing.
-rw-r--r-- | lv2/lv2plug.in/ns/ext/atom/lv2-atom.doap.ttl | 2 | ||||
-rw-r--r-- | lv2/lv2plug.in/ns/ext/atom/util.h | 46 | ||||
-rw-r--r-- | plugins/eg06-fifths.lv2/README.txt | 3 | ||||
-rw-r--r-- | plugins/eg06-fifths.lv2/fifths.c | 191 | ||||
-rw-r--r-- | plugins/eg06-fifths.lv2/fifths.ttl | 36 | ||||
-rw-r--r-- | plugins/eg06-fifths.lv2/manifest.ttl.in | 8 | ||||
-rw-r--r-- | plugins/eg06-fifths.lv2/uris.h | 64 | ||||
l--------- | plugins/eg06-fifths.lv2/waf | 1 | ||||
-rw-r--r-- | plugins/eg06-fifths.lv2/wscript | 64 |
9 files changed, 415 insertions, 0 deletions
diff --git a/lv2/lv2plug.in/ns/ext/atom/lv2-atom.doap.ttl b/lv2/lv2plug.in/ns/ext/atom/lv2-atom.doap.ttl index 251d998..f70a410 100644 --- a/lv2/lv2plug.in/ns/ext/atom/lv2-atom.doap.ttl +++ b/lv2/lv2plug.in/ns/ext/atom/lv2-atom.doap.ttl @@ -22,6 +22,8 @@ rdfs:label "Add lv2_atom_forge_is_object_type() and lv2_atom_forge_is_blank() to ease backwards compatibility." ] , [ rdfs:label "Add lv2_atom_forge_key() for terser object writing." + ] , [ + rdfs:label "Add lv2_atom_sequence_clear() and lv2_atom_sequence_append_event() helper functions." ] ] ] , [ diff --git a/lv2/lv2plug.in/ns/ext/atom/util.h b/lv2/lv2plug.in/ns/ext/atom/util.h index d306e1c..d7c020b 100644 --- a/lv2/lv2plug.in/ns/ext/atom/util.h +++ b/lv2/lv2plug.in/ns/ext/atom/util.h @@ -129,6 +129,52 @@ lv2_atom_sequence_next(const LV2_Atom_Event* i) /** @} + @name Sequence Utilities + @{ +*/ + +/** + Clear all events from @p sequence. + + This simply resets the size field, the other fields are left untouched. +*/ +static inline void +lv2_atom_sequence_clear(LV2_Atom_Sequence* seq) +{ + seq->atom.size = sizeof(LV2_Atom_Sequence_Body); +} + +/** + Append an event at the end of @p sequence. + + @param seq Sequence to append to. + @param capacity Total capacity of the sequence atom + (e.g. as set by the host for sequence output ports). + @param event Event to write. + + @return A pointer to the newly written event in @p seq, + or NULL on failure (insufficient space). +*/ +static inline LV2_Atom_Event* +lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, + uint32_t capacity, + const LV2_Atom_Event* event) +{ + const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size; + if (capacity - seq->atom.size < total_size) { + return NULL; + } + + LV2_Atom_Event* e = lv2_atom_sequence_end(&seq->body, seq->atom.size); + memcpy(e, event, total_size); + + seq->atom.size += lv2_atom_pad_size(total_size); + + return e; +} + +/** + @} @name Tuple Iterator @{ */ diff --git a/plugins/eg06-fifths.lv2/README.txt b/plugins/eg06-fifths.lv2/README.txt new file mode 100644 index 0000000..2154321 --- /dev/null +++ b/plugins/eg06-fifths.lv2/README.txt @@ -0,0 +1,3 @@ +== Fifths == + +This plugin demonstrates simple MIDI event reading and writing. diff --git a/plugins/eg06-fifths.lv2/fifths.c b/plugins/eg06-fifths.lv2/fifths.c new file mode 100644 index 0000000..13d47f4 --- /dev/null +++ b/plugins/eg06-fifths.lv2/fifths.c @@ -0,0 +1,191 @@ +/* + LV2 Fifths Example Plugin + Copyright 2014 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#include <math.h> +#include <stdlib.h> +#include <string.h> +#ifndef __cplusplus +# include <stdbool.h> +#endif + +#include <sndfile.h> + +#include "lv2/lv2plug.in/ns/ext/atom/util.h" +#include "lv2/lv2plug.in/ns/ext/midi/midi.h" +#include "lv2/lv2plug.in/ns/ext/patch/patch.h" +#include "lv2/lv2plug.in/ns/ext/state/state.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" + +#include "./uris.h" + +enum { + FIFTHS_IN = 0, + FIFTHS_OUT = 1 +}; + +typedef struct { + // Features + LV2_URID_Map* map; + + // Ports + const LV2_Atom_Sequence* in_port; + LV2_Atom_Sequence* out_port; + + // URIs + FifthsURIs uris; +} Fifths; + +static void +connect_port(LV2_Handle instance, + uint32_t port, + void* data) +{ + Fifths* self = (Fifths*)instance; + switch (port) { + case FIFTHS_IN: + self->in_port = (const LV2_Atom_Sequence*)data; + break; + case FIFTHS_OUT: + self->out_port = (LV2_Atom_Sequence*)data; + break; + default: + break; + } +} + +static LV2_Handle +instantiate(const LV2_Descriptor* descriptor, + double rate, + const char* path, + const LV2_Feature* const* features) +{ + // Allocate and initialise instance structure. + Fifths* self = (Fifths*)malloc(sizeof(Fifths)); + if (!self) { + return NULL; + } + memset(self, 0, sizeof(Fifths)); + + // Get host features + for (int i = 0; features[i]; ++i) { + if (!strcmp(features[i]->URI, LV2_URID__map)) { + self->map = (LV2_URID_Map*)features[i]->data; + } + } + if (!self->map) { + fprintf(stderr, "Missing feature urid:map\n"); + free(self); + return NULL; + } + + // Map URIs and initialise forge/logger + map_fifths_uris(self->map, &self->uris); + + return (LV2_Handle)self; +} + +static void +cleanup(LV2_Handle instance) +{ + free(instance); +} + +static void +run(LV2_Handle instance, + uint32_t sample_count) +{ + Fifths* self = (Fifths*)instance; + FifthsURIs* uris = &self->uris; + + // Struct for a 3 byte MIDI event, used for writing notes + typedef struct { + LV2_Atom_Event event; + uint8_t msg[3]; + } MIDINoteEvent; + + // Initially self->out_port contains a Chunk with size set to capacity + + // Get the capacity + const uint32_t out_capacity = self->out_port->atom.size; + + // Write an empty Sequence header to the output + lv2_atom_sequence_clear(self->out_port); + self->out_port->atom.type = self->in_port->atom.type; + + // Read incoming events + LV2_ATOM_SEQUENCE_FOREACH(self->in_port, ev) { + if (ev->body.type == uris->midi_Event) { + const uint8_t* const msg = (const uint8_t*)(ev + 1); + switch (lv2_midi_message_type(msg)) { + case LV2_MIDI_MSG_NOTE_ON: + case LV2_MIDI_MSG_NOTE_OFF: + // Forward note to output + lv2_atom_sequence_append_event( + self->out_port, out_capacity, ev); + + const uint8_t note = msg[1]; + if (note < 127 - 7) { + // Make a note one 5th (7 semitones) higher than input + MIDINoteEvent fifth; + fifth.event = *ev; + fifth.msg[0] = msg[0]; // Same status + fifth.msg[1] = msg[1] + 7; // Pitch up 7 semitones + fifth.msg[2] = msg[2]; // Same velocity + + // Write 5th event + lv2_atom_sequence_append_event( + self->out_port, out_capacity, &fifth.event); + } + break; + default: + // Forward all other MIDI events directly + lv2_atom_sequence_append_event( + self->out_port, out_capacity, ev); + break; + } + } + } +} + +static const void* +extension_data(const char* uri) +{ + return NULL; +} + +static const LV2_Descriptor descriptor = { + EG_FIFTHS_URI, + instantiate, + connect_port, + NULL, // activate, + run, + NULL, // deactivate, + cleanup, + extension_data +}; + +LV2_SYMBOL_EXPORT +const LV2_Descriptor* lv2_descriptor(uint32_t index) +{ + switch (index) { + case 0: + return &descriptor; + default: + return NULL; + } +} diff --git a/plugins/eg06-fifths.lv2/fifths.ttl b/plugins/eg06-fifths.lv2/fifths.ttl new file mode 100644 index 0000000..2d439c7 --- /dev/null +++ b/plugins/eg06-fifths.lv2/fifths.ttl @@ -0,0 +1,36 @@ +@prefix atom: <http://lv2plug.in/ns/ext/atom#> . +@prefix doap: <http://usefulinc.com/ns/doap#> . +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix patch: <http://lv2plug.in/ns/ext/patch#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix state: <http://lv2plug.in/ns/ext/state#> . +@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . +@prefix urid: <http://lv2plug.in/ns/ext/urid#> . +@prefix work: <http://lv2plug.in/ns/ext/worker#> . +@prefix midi: <http://lv2plug.in/ns/ext/midi#> . + +<http://lv2plug.in/plugins/eg-fifths> + a lv2:Plugin ; + doap:name "Example Fifths" ; + doap:license <http://opensource.org/licenses/isc> ; + lv2:project <http://lv2plug.in/ns/lv2> ; + lv2:requiredFeature urid:map ; + lv2:optionalFeature lv2:hardRTCapable ; + lv2:extensionData work:interface ; + lv2:port [ + a lv2:InputPort , + atom:AtomPort ; + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 0 ; + lv2:symbol "in" ; + lv2:name "In" + ] , [ + a lv2:OutputPort , + atom:AtomPort ; + atom:bufferType atom:Sequence ; + atom:supports midi:MidiEvent ; + lv2:index 1 ; + lv2:symbol "out" ; + lv2:name "Out" + ] . diff --git a/plugins/eg06-fifths.lv2/manifest.ttl.in b/plugins/eg06-fifths.lv2/manifest.ttl.in new file mode 100644 index 0000000..f87f2c1 --- /dev/null +++ b/plugins/eg06-fifths.lv2/manifest.ttl.in @@ -0,0 +1,8 @@ +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix ui: <http://lv2plug.in/ns/extensions/ui#> . + +<http://lv2plug.in/plugins/eg-fifths> + a lv2:Plugin ; + lv2:binary <fifths@LIB_EXT@> ; + rdfs:seeAlso <fifths.ttl> . diff --git a/plugins/eg06-fifths.lv2/uris.h b/plugins/eg06-fifths.lv2/uris.h new file mode 100644 index 0000000..e174fb4 --- /dev/null +++ b/plugins/eg06-fifths.lv2/uris.h @@ -0,0 +1,64 @@ +/* + LV2 Fifths Example Plugin + Copyright 2014 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifndef FIFTHS_URIS_H +#define FIFTHS_URIS_H + +#include "lv2/lv2plug.in/ns/ext/log/log.h" +#include "lv2/lv2plug.in/ns/ext/midi/midi.h" +#include "lv2/lv2plug.in/ns/ext/state/state.h" + +#define EG_FIFTHS_URI "http://lv2plug.in/plugins/eg-fifths" +#define EG_FIFTHS__sample EG_FIFTHS_URI "#sample" +#define EG_FIFTHS__applySample EG_FIFTHS_URI "#applySample" +#define EG_FIFTHS__freeSample EG_FIFTHS_URI "#freeSample" + +typedef struct { + LV2_URID atom_Blank; + LV2_URID atom_Path; + LV2_URID atom_Resource; + LV2_URID atom_Sequence; + LV2_URID atom_URID; + LV2_URID atom_eventTransfer; + LV2_URID eg_applySample; + LV2_URID eg_sample; + LV2_URID eg_freeSample; + LV2_URID midi_Event; + LV2_URID patch_Set; + LV2_URID patch_property; + LV2_URID patch_value; +} FifthsURIs; + +static inline void +map_fifths_uris(LV2_URID_Map* map, FifthsURIs* uris) +{ + uris->atom_Blank = map->map(map->handle, LV2_ATOM__Blank); + uris->atom_Path = map->map(map->handle, LV2_ATOM__Path); + uris->atom_Resource = map->map(map->handle, LV2_ATOM__Resource); + uris->atom_Sequence = map->map(map->handle, LV2_ATOM__Sequence); + uris->atom_URID = map->map(map->handle, LV2_ATOM__URID); + uris->atom_eventTransfer = map->map(map->handle, LV2_ATOM__eventTransfer); + uris->eg_applySample = map->map(map->handle, EG_FIFTHS__applySample); + uris->eg_freeSample = map->map(map->handle, EG_FIFTHS__freeSample); + uris->eg_sample = map->map(map->handle, EG_FIFTHS__sample); + uris->midi_Event = map->map(map->handle, LV2_MIDI__MidiEvent); + uris->patch_Set = map->map(map->handle, LV2_PATCH__Set); + uris->patch_property = map->map(map->handle, LV2_PATCH__property); + uris->patch_value = map->map(map->handle, LV2_PATCH__value); +} + +#endif /* FIFTHS_URIS_H */ diff --git a/plugins/eg06-fifths.lv2/waf b/plugins/eg06-fifths.lv2/waf new file mode 120000 index 0000000..59a1ac9 --- /dev/null +++ b/plugins/eg06-fifths.lv2/waf @@ -0,0 +1 @@ +../../waf
\ No newline at end of file diff --git a/plugins/eg06-fifths.lv2/wscript b/plugins/eg06-fifths.lv2/wscript new file mode 100644 index 0000000..46e2345 --- /dev/null +++ b/plugins/eg06-fifths.lv2/wscript @@ -0,0 +1,64 @@ +#!/usr/bin/env python +from waflib.extras import autowaf as autowaf +import re + +# Variables for 'waf dist' +APPNAME = 'eg-fifths.lv2' +VERSION = '1.0.0' + +# Mandatory variables +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + autowaf.set_options(opt) + +def configure(conf): + conf.load('compiler_c') + autowaf.configure(conf) + autowaf.set_c99_mode(conf) + autowaf.display_header('Fifths Configuration') + + if not autowaf.is_child(): + autowaf.check_pkg(conf, 'lv2', atleast_version='1.2.1', uselib_store='LV2') + + autowaf.display_msg(conf, 'LV2 bundle directory', conf.env.LV2DIR) + print('') + +def build(bld): + bundle = 'eg-fifths.lv2' + + # Make a pattern for shared objects without the 'lib' prefix + module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN) + module_ext = module_pat[module_pat.rfind('.'):] + + # Build manifest.ttl by substitution (for portable lib extension) + bld(features = 'subst', + source = 'manifest.ttl.in', + target = '%s/%s' % (bundle, 'manifest.ttl'), + install_path = '${LV2DIR}/%s' % bundle, + LIB_EXT = module_ext) + + # Copy other data files to build bundle (build/eg-fifths.lv2) + for i in ['fifths.ttl']: + bld(features = 'subst', + is_copy = True, + source = i, + target = '%s/%s' % (bundle, i), + install_path = '${LV2DIR}/%s' % bundle) + + # Use LV2 headers from parent directory if building as a sub-project + includes = ['.'] + if autowaf.is_child: + includes += ['../..'] + + # Build plugin library + obj = bld(features = 'c cshlib', + source = 'fifths.c', + name = 'fifths', + target = '%s/fifths' % bundle, + install_path = '${LV2DIR}/%s' % bundle, + use = 'SNDFILE LV2', + includes = includes) + obj.env.cshlib_PATTERN = module_pat |