From 60eb52f31976763497cd0355cc0d6b46af6c465f Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 8 Feb 2014 02:30:06 +0000 Subject: 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. --- plugins/eg06-fifths.lv2/README.txt | 3 + plugins/eg06-fifths.lv2/fifths.c | 191 ++++++++++++++++++++++++++++++++ plugins/eg06-fifths.lv2/fifths.ttl | 36 ++++++ plugins/eg06-fifths.lv2/manifest.ttl.in | 8 ++ plugins/eg06-fifths.lv2/uris.h | 64 +++++++++++ plugins/eg06-fifths.lv2/waf | 1 + plugins/eg06-fifths.lv2/wscript | 64 +++++++++++ 7 files changed, 367 insertions(+) create mode 100644 plugins/eg06-fifths.lv2/README.txt create mode 100644 plugins/eg06-fifths.lv2/fifths.c create mode 100644 plugins/eg06-fifths.lv2/fifths.ttl create mode 100644 plugins/eg06-fifths.lv2/manifest.ttl.in create mode 100644 plugins/eg06-fifths.lv2/uris.h create mode 120000 plugins/eg06-fifths.lv2/waf create mode 100644 plugins/eg06-fifths.lv2/wscript (limited to 'plugins') 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 + + 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 +#include +#include +#ifndef __cplusplus +# include +#endif + +#include + +#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: . +@prefix doap: . +@prefix lv2: . +@prefix patch: . +@prefix rdfs: . +@prefix state: . +@prefix ui: . +@prefix urid: . +@prefix work: . +@prefix midi: . + + + a lv2:Plugin ; + doap:name "Example Fifths" ; + doap:license ; + lv2:project ; + 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: . +@prefix rdfs: . +@prefix ui: . + + + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso . 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 + + 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 -- cgit v1.2.1