aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2014-02-08 02:30:06 +0000
committerDavid Robillard <d@drobilla.net>2014-02-08 02:30:06 +0000
commit60eb52f31976763497cd0355cc0d6b46af6c465f (patch)
tree13783ae9948977c809d2907ee7b80aa0ebbdb2d9
parent6d41effb84c6663704d7f2ed562d7bbf4f5cc441 (diff)
downloadlv2-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.ttl2
-rw-r--r--lv2/lv2plug.in/ns/ext/atom/util.h46
-rw-r--r--plugins/eg06-fifths.lv2/README.txt3
-rw-r--r--plugins/eg06-fifths.lv2/fifths.c191
-rw-r--r--plugins/eg06-fifths.lv2/fifths.ttl36
-rw-r--r--plugins/eg06-fifths.lv2/manifest.ttl.in8
-rw-r--r--plugins/eg06-fifths.lv2/uris.h64
l---------plugins/eg06-fifths.lv2/waf1
-rw-r--r--plugins/eg06-fifths.lv2/wscript64
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