/* * Copyright (C) 2013-2019 Robin Gareus * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define _GNU_SOURCE #include #include #include #include /* LV2 */ #include "lv2/lv2plug.in/ns/lv2core/lv2.h" #include "lv2/lv2plug.in/ns/ext/atom/atom.h" #include "lv2/lv2plug.in/ns/ext/urid/urid.h" #include "lv2/lv2plug.in/ns/ext/midi/midi.h" #define RSY_URI "https://community.ardour.org/node/7596" /* the synth interface */ static void * synth_alloc (void); static void synth_init (void *, double rate); static void synth_free (void *); static void synth_parse_midi (void *, const uint8_t *data, const size_t size); static uint32_t synth_sound (void *, uint32_t written, uint32_t nframes, float **out); #include "rsynth.c" typedef enum { RSY_MIDIIN = 0, RSY_OUTL, RSY_OUTR } PortIndex; typedef struct { const LV2_Atom_Sequence* midiin; float* outL; float* outR; LV2_URID_Map* map; LV2_URID midi_MidiEvent; double SampleRateD; void *synth; bool xmas; } RSynth; /* main LV2 */ static LV2_Handle instantiate (const LV2_Descriptor* descriptor, double rate, const char* bundle_path, const LV2_Feature* const* features) { (void) descriptor; /* unused variable */ (void) bundle_path; /* unused variable */ if (rate < 8000) { fprintf(stderr, "RSynth.lv2 error: unsupported sample-rate (must be > 8k)\n"); return NULL; } RSynth* self = (RSynth*)calloc(1, sizeof(RSynth)); if(!self) { return NULL; } self->SampleRateD = rate; int i; for (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, "RSynth.lv2 error: Host does not support urid:map\n"); free(self); return NULL; } self->midi_MidiEvent = self->map->map(self->map->handle, LV2_MIDI__MidiEvent); self->synth = synth_alloc(); synth_init(self->synth, rate); if (getenv("ITSXMAS")) { printf("reasonable synth.lv2 says: happy holidays!\n"); self->xmas = true; } return (LV2_Handle)self; } static void connect_port (LV2_Handle handle, uint32_t port, void* data) { RSynth* self = (RSynth*)handle; switch ((PortIndex)port) { case RSY_MIDIIN: self->midiin = (const LV2_Atom_Sequence*)data; break; case RSY_OUTL: self->outL = (float*)data; break; case RSY_OUTR: self->outR = (float*)data; break; } } static void run (LV2_Handle handle, uint32_t n_samples) { RSynth* self = (RSynth*)handle; float* audio[2]; audio[0] = self->outL; audio[1] = self->outR; uint32_t written = 0; /* Process incoming MIDI events */ if (self->midiin) { LV2_Atom_Event const* ev = (LV2_Atom_Event const*)((uintptr_t)((&(self->midiin)->body) + 1)); // lv2_atom_sequence_begin while( // !lv2_atom_sequence_is_end (const uint8_t*)ev < ((const uint8_t*) &(self->midiin)->body + (self->midiin)->atom.size) ) { if (ev->body.type == self->midi_MidiEvent) { #ifdef DEBUG_MIDI_EVENT // debug midi messages in synth -- not rt-safe(!) printf ("%5d (%d):", ev->time.frames, ev->body.size); for (uint8_t i = 0; i < ev->body.size; ++i) { printf (" %02x", ((const uint8_t*)(ev+1))[i]); } printf ("\n"); #endif if (written + BUFFER_SIZE_SAMPLES < ev->time.frames && ev->time.frames < n_samples) { /* first synthesize sound up until the message timestamp */ written = synth_sound(self->synth, written, ev->time.frames, audio); } /* send midi message to synth */ if (self->xmas) { synth_parse_xmas(self->synth, (const uint8_t*)(ev+1), ev->body.size); } else { synth_parse_midi(self->synth, (const uint8_t*)(ev+1), ev->body.size); } } ev = (LV2_Atom_Event const*) // lv2_atom_sequence_next() ((uintptr_t)((const uint8_t*)ev + sizeof(LV2_Atom_Event) + ((ev->body.size + 7) & ~7))); } } /* synthesize [remaining] sound */ synth_sound(self->synth, written, n_samples, audio); } static void cleanup(LV2_Handle handle) { RSynth* self = (RSynth*)handle; synth_free(self->synth); free(handle); } static const void* extension_data(const char* uri) { (void) uri; /* unused variable */ return NULL; } static const LV2_Descriptor descriptor = { RSY_URI, instantiate, connect_port, NULL, run, NULL, cleanup, extension_data }; #if defined(COMPILER_MSVC) __declspec(dllexport) #else __attribute__ ((visibility ("default"))) #endif const LV2_Descriptor* lv2_descriptor(uint32_t idx) { switch (idx) { case 0: return &descriptor; default: return NULL; } }