From f5c386bbb40e176028022efa7577369eca79b788 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 20 Oct 2013 04:31:07 +0200 Subject: [PATCH] add reasonablesynth.lv2 A reasonably simple synth to allow new users to 'hear midi'. This is a first step. It still needs proper install and bundling. --- libs/plugins/reasonablesynth.lv2/lv2.c | 190 +++++++ .../reasonablesynth.lv2/manifest.ttl.in | 8 + .../reasonablesynth.ttl.in | 51 ++ libs/plugins/reasonablesynth.lv2/rsynth.c | 490 ++++++++++++++++++ libs/plugins/reasonablesynth.lv2/wscript | 44 ++ wscript | 1 + 6 files changed, 784 insertions(+) create mode 100644 libs/plugins/reasonablesynth.lv2/lv2.c create mode 100644 libs/plugins/reasonablesynth.lv2/manifest.ttl.in create mode 100644 libs/plugins/reasonablesynth.lv2/reasonablesynth.ttl.in create mode 100644 libs/plugins/reasonablesynth.lv2/rsynth.c create mode 100644 libs/plugins/reasonablesynth.lv2/wscript diff --git a/libs/plugins/reasonablesynth.lv2/lv2.c b/libs/plugins/reasonablesynth.lv2/lv2.c new file mode 100644 index 0000000000..85aa968e8f --- /dev/null +++ b/libs/plugins/reasonablesynth.lv2/lv2.c @@ -0,0 +1,190 @@ +/* reasonable simple synth + * + * Copyright (C) 2013 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE + +#include +#include +#include + +/* LV2 */ +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" +#include "lv2/lv2plug.in/ns/ext/atom/util.h" +#include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "lv2/lv2plug.in/ns/ext/midi/midi.h" + +#define RSY_URI "http://gareus.org/oss/lv2/reasonablesynth" + +/* 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 *, uint8_t *data, 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; +} RSynth; + +/* main LV2 */ + +static LV2_Handle +instantiate(const LV2_Descriptor* descriptor, + double rate, + const char* bundle_path, + const LV2_Feature* const* features) +{ + 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); + + 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* ev = lv2_atom_sequence_begin(&(self->midiin)->body); + while(!lv2_atom_sequence_is_end(&(self->midiin)->body, (self->midiin)->atom.size, ev)) { + if (ev->body.type == self->midi_MidiEvent) { + 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 */ + synth_parse_midi(self->synth, (uint8_t*)(ev+1), ev->body.size); + } + ev = lv2_atom_sequence_next(ev); + } + } + + /* 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) +{ + return NULL; +} + +static const LV2_Descriptor descriptor = { + RSY_URI, + instantiate, + connect_port, + NULL, + run, + NULL, + cleanup, + extension_data +}; + +LV2_SYMBOL_EXPORT +const LV2_Descriptor* +lv2_descriptor(uint32_t index) +{ + switch (index) { + case 0: + return &descriptor; + default: + return NULL; + } +} + +/* vi:set ts=8 sts=2 sw=2: */ diff --git a/libs/plugins/reasonablesynth.lv2/manifest.ttl.in b/libs/plugins/reasonablesynth.lv2/manifest.ttl.in new file mode 100644 index 0000000000..ad0cff08fa --- /dev/null +++ b/libs/plugins/reasonablesynth.lv2/manifest.ttl.in @@ -0,0 +1,8 @@ +@prefix lv2: . +@prefix rdfs: . +@prefix ui: . + + + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso . diff --git a/libs/plugins/reasonablesynth.lv2/reasonablesynth.ttl.in b/libs/plugins/reasonablesynth.lv2/reasonablesynth.ttl.in new file mode 100644 index 0000000000..2b8861f3d6 --- /dev/null +++ b/libs/plugins/reasonablesynth.lv2/reasonablesynth.ttl.in @@ -0,0 +1,51 @@ +@prefix atom: . +@prefix doap: . +@prefix foaf: . +@prefix lv2: . +@prefix rdf: . +@prefix rdfs: . +@prefix pg: . +@prefix units: . +@prefix urid: . + + + a foaf:Person ; + foaf:name "Robin Gareus" ; + foaf:mbox ; + foaf:homepage . + + + a lv2:Plugin, lv2:InstrumentPlugin, doap:Project; + doap:license ; + doap:maintainer ; + doap:name "Reasonable Synth"; + lv2:optionalFeature lv2:hardRTCapable ; + lv2:requiredFeature urid:map ; + rdfs:comment """A simple synthesizer with no controls at all but a reasonable sound instead.""" ; + lv2:port + [ + a atom:AtomPort , + lv2:InputPort ; + atom:bufferType atom:Sequence ; + atom:supports ; + lv2:index 0 ; + lv2:symbol "MidiIn" ; + lv2:name "MIDI Input" ; + ], + [ + a lv2:AudioPort , + lv2:OutputPort ; + lv2:index 1 ; + lv2:symbol "outL" ; + lv2:name "Left output" ; + lv2:designation pg:left ; + ], + [ + a lv2:AudioPort , + lv2:OutputPort ; + lv2:index 2 ; + lv2:symbol "outR" ; + lv2:name "Right Output" ; + lv2:designation pg:right ; + ] + . diff --git a/libs/plugins/reasonablesynth.lv2/rsynth.c b/libs/plugins/reasonablesynth.lv2/rsynth.c new file mode 100644 index 0000000000..b89f0fb45f --- /dev/null +++ b/libs/plugins/reasonablesynth.lv2/rsynth.c @@ -0,0 +1,490 @@ +/* reasonable simple synth + * + * Copyright (C) 2013 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, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // needed for M_PI +#endif + +#include +#include +#include +#include +#include + +#ifndef BUFFER_SIZE_SAMPLES +#define BUFFER_SIZE_SAMPLES 64 +#endif + +#ifndef MIN +#define MIN(A, B) ( (A) < (B) ? (A) : (B) ) +#endif + +/* internal MIDI event abstraction */ +enum RMIDI_EV_TYPE { + INVALID=0, + NOTE_ON, + NOTE_OFF, + PROGRAM_CHANGE, + CONTROL_CHANGE, +}; + +struct rmidi_event_t { + enum RMIDI_EV_TYPE type; + uint8_t channel; /**< the MIDI channel number 0-15 */ + union { + struct { + uint8_t note; + uint8_t velocity; + } tone; + struct { + uint8_t param; + uint8_t value; + } control; + } d; +}; + +typedef struct { + uint32_t tme[3]; // attack, decay, release times [settings:ms || internal:samples] + float vol[2]; // attack, sustain volume [0..1] + uint32_t off[3]; // internal use (added attack,decay,release times) +} ADSRcfg; + +typedef struct _RSSynthChannel { + uint32_t keycomp; + uint32_t adsr_cnt[128]; + float adsr_amp[128]; + float phase[128]; // various use, zero'ed on note-on + int8_t miditable[128]; // internal, note-on/off velocity + ADSRcfg adsr; + void (*synthesize) (struct _RSSynthChannel* sc, + const uint8_t note, const float vol, const float pc, + const size_t n_samples, float* left, float* right); +} RSSynthChannel; + +typedef void (*SynthFunction) (RSSynthChannel* sc, + const uint8_t note, const float vol, const float pc, + const size_t n_samples, float* left, float* right); + +typedef struct { + uint32_t boffset; + float buf [2][BUFFER_SIZE_SAMPLES]; + RSSynthChannel sc[16]; + float freqs[128]; + float kcgain; + float kcfilt; + double rate; +} RSSynthesizer; + + +/* initialize ADSR values + * + * @param rate sample-rate + * @param a attack time in seconds + * @param d decay time in seconds + * @param r release time in seconds + * @param avol attack gain [0..1] + * @param svol sustain volume level [0..1] + */ +static void init_adsr(ADSRcfg *adsr, const double rate, + const uint32_t a, const uint32_t d, const uint32_t r, + const float avol, const float svol) { + + adsr->vol[0] = avol; + adsr->vol[1] = svol; + adsr->tme[0] = a * rate / 1000.0; + adsr->tme[1] = d * rate / 1000.0; + adsr->tme[2] = r * rate / 1000.0; + + assert(adsr->tme[0] > 32); + assert(adsr->tme[1] > 32); + assert(adsr->tme[2] > 32); + assert(adsr->vol[0] >=0 && adsr->vol[1] <= 1.0); + assert(adsr->vol[1] >=0 && adsr->vol[1] <= 1.0); + + adsr->off[0] = adsr->tme[0]; + adsr->off[1] = adsr->tme[1] + adsr->off[0]; + adsr->off[2] = adsr->tme[2] + adsr->off[1]; +} + +/* calculate per-sample, per-key envelope */ +static inline float adsr_env(RSSynthChannel *sc, const uint8_t note) { + + if (sc->adsr_cnt[note] < sc->adsr.off[0]) { + // attack + const uint32_t p = ++sc->adsr_cnt[note]; + if (p == sc->adsr.tme[0]) { + sc->adsr_amp[note] = sc->adsr.vol[0]; + return sc->adsr.vol[0]; + } else { + const float d = sc->adsr.vol[0] - sc->adsr_amp[note]; + return sc->adsr_amp[note] + (p / (float) sc->adsr.tme[0]) * d; + } + } + else if (sc->adsr_cnt[note] < sc->adsr.off[1]) { + // decay + const uint32_t p = ++sc->adsr_cnt[note] - sc->adsr.off[0]; + if (p == sc->adsr.tme[1]) { + sc->adsr_amp[note] = sc->adsr.vol[1]; + return sc->adsr.vol[1]; + } else { + const float d = sc->adsr.vol[1] - sc->adsr_amp[note]; + return sc->adsr_amp[note] + (p / (float) sc->adsr.tme[1]) * d; + } + } + else if (sc->adsr_cnt[note] == sc->adsr.off[1]) { + // sustain + return sc->adsr.vol[1]; + } + else if (sc->adsr_cnt[note] < sc->adsr.off[2]) { + // release + const uint32_t p = ++sc->adsr_cnt[note] - sc->adsr.off[1]; + if (p == sc->adsr.tme[2]) { + sc->adsr_amp[note] = 0; + return 0; + } else { + const float d = 0 - sc->adsr_amp[note]; + return sc->adsr_amp[note] + (p / (float) sc->adsr.tme[2]) * d; + } + } + else { + sc->adsr_cnt[note] = 0; + return 0; + } +} + + +/*****************************************************************************/ +/* piano like sound w/slight stereo phase */ +static void synthesize_sineP (RSSynthChannel* sc, + const uint8_t note, const float vol, const float fq, + const size_t n_samples, float* left, float* right) { + + float phase = sc->phase[note]; + + for (size_t i=0; i < n_samples; ++i) { + float env = adsr_env(sc, note); + if (sc->adsr_cnt[note] == 0) break; + const float amp = vol * env; + + left[i] += amp * sinf(2.0 * M_PI * phase); + left[i] += .300 * amp * sinf(2.0 * M_PI * phase * 2.0); + left[i] += .150 * amp * sinf(2.0 * M_PI * phase * 3.0); + left[i] += .080 * amp * sinf(2.0 * M_PI * phase * 4.0); + //left[i] -= .007 * amp * sinf(2.0 * M_PI * phase * 5.0); + //left[i] += .010 * amp * sinf(2.0 * M_PI * phase * 6.0); + //left[i] += .020 * amp * sinf(2.0 * M_PI * phase * 7.0); + phase += fq; + right[i] += amp * sinf(2.0 * M_PI * phase); + right[i] += .300 * amp * sinf(2.0 * M_PI * phase * 2.0); + right[i] += .150 * amp * sinf(2.0 * M_PI * phase * 3.0); + right[i] -= .080 * amp * sinf(2.0 * M_PI * phase * 4.0); + //right[i] += .007 * amp * sinf(2.0 * M_PI * phase * 5.0); + //right[i] += .010 * amp * sinf(2.0 * M_PI * phase * 6.0); + //right[i] -= .020 * amp * sinf(2.0 * M_PI * phase * 7.0); + if (phase > 1.0) phase -= 2.0; + } + sc->phase[note] = phase; +} + +static const ADSRcfg piano_adsr = {{ 5, 1300, 100}, { 1.0, 0.0}, {0,0,0}}; + +/*****************************************************************************/ + + +/* process note - move through ADSR states, count active keys,.. */ +static void process_key (void *synth, + const uint8_t chn, const uint8_t note, + const size_t n_samples, float *left, float *right) +{ + RSSynthesizer* rs = (RSSynthesizer*)synth; + RSSynthChannel* sc = &rs->sc[chn]; + const int8_t vel = sc->miditable[note]; + const float vol = /* master_volume */ 0.25 * fabsf(vel) / 127.0; + const float phase = sc->phase[note]; + + if (phase == -10 && vel > 0) { + // new note on + assert(sc->adsr_cnt[note] == 0); + sc->adsr_amp[note] = 0; + sc->adsr_cnt[note] = 0; + sc->phase[note] = 0; + sc->keycomp++; + //printf("[On] Now %d keys active on chn %d\n", sc->keycomp, chn); + } + else if (phase >= -1.0 && phase <= 1.0 && vel > 0) { + // sustain note or re-start note while adsr in progress: + if (sc->adsr_cnt[note] > sc->adsr.off[1]) { + // x-fade to attack + sc->adsr_amp[note] = adsr_env(sc, note); + sc->adsr_cnt[note] = 0; + } + } + else if (phase >= -1.0 && phase <= 1.0 && vel < 0) { + // note off + if (sc->adsr_cnt[note] <= sc->adsr.off[1]) { + if (sc->adsr_cnt[note] != sc->adsr.off[1]) { + // x-fade to release + sc->adsr_amp[note] = adsr_env(sc, note); + } + sc->adsr_cnt[note] = sc->adsr.off[1] + 1; + } + } + else { + /* note-on + off in same cycle */ + sc->miditable[note] = 0; + sc->adsr_cnt[note] = 0; + sc->phase[note] = -10; + return; + } + + // synthesize actual sound + sc->synthesize(sc, note, vol, rs->freqs[note], n_samples, left, right); + + if (sc->adsr_cnt[note] == 0) { + //printf("Note %d,%d released\n", chn, note); + sc->miditable[note] = 0; + sc->adsr_amp[note] = 0; + sc->phase[note] = -10; + sc->keycomp--; + //printf("[off] Now %d keys active on chn %d\n", sc->keycomp, chn); + } +} + +/* synthesize a BUFFER_SIZE_SAMPLES's of audio-data */ +static void synth_fragment (void *synth, const size_t n_samples, float *left, float *right) { + RSSynthesizer* rs = (RSSynthesizer*)synth; + memset (left, 0, n_samples * sizeof(float)); + memset (right, 0, n_samples * sizeof(float)); + uint8_t keycomp = 0; + + for (int c=0; c < 16; ++c) { + for (int k=0; k < 128; ++k) { + if (rs->sc[c].miditable[k] == 0) continue; + process_key(synth, c, k, n_samples, left, right); + } + keycomp += rs->sc[c].keycomp; + } + +#if 1 // key-compression + float kctgt = 8.0 / (float)(keycomp + 7.0); + if (kctgt < .5) kctgt = .5; + if (kctgt > 1.0) kctgt = 1.0; + const float _w = rs->kcfilt; + for (unsigned int i=0; i < n_samples; ++i) { + rs->kcgain += _w * (kctgt - rs->kcgain); + left[i] *= rs->kcgain; + right[i] *= rs->kcgain; + } + rs->kcgain += 1e-12; +#endif +} + +static void synth_reset_channel(RSSynthChannel* sc) { + for (int k=0; k < 128; ++k) { + sc->adsr_cnt[k] = 0; + sc->adsr_amp[k] = 0; + sc->phase[k] = -10; + sc->miditable[k] = 0; + } + sc->keycomp = 0; +} + +static void synth_reset(void *synth) { + RSSynthesizer* rs = (RSSynthesizer*)synth; + for (int c=0; c < 16; ++c) { + synth_reset_channel(&(rs->sc[c])); + } + rs->kcgain = 0; +} + +static void synth_load(RSSynthChannel *sc, const double rate, + SynthFunction synthesize, + ADSRcfg const * const adsr) { + synth_reset_channel(sc); + init_adsr(&sc->adsr, rate, + adsr->tme[0], adsr->tme[1], adsr->tme[2], + adsr->vol[0], adsr->vol[1]); + sc->synthesize = synthesize; +} + + +/** + * internal abstraction of MIDI data handling + */ +static void synth_process_midi_event(void *synth, struct rmidi_event_t *ev) { + RSSynthesizer* rs = (RSSynthesizer*)synth; + switch(ev->type) { + case NOTE_ON: + if (rs->sc[ev->channel].miditable[ev->d.tone.note] <= 0) + rs->sc[ev->channel].miditable[ev->d.tone.note] = ev->d.tone.velocity; + break; + case NOTE_OFF: + if (rs->sc[ev->channel].miditable[ev->d.tone.note] > 0) + rs->sc[ev->channel].miditable[ev->d.tone.note] *= -1.0; + break; + case PROGRAM_CHANGE: + break; + case CONTROL_CHANGE: + if (ev->d.control.param == 0x00 || ev->d.control.param == 0x20) { + /* 0x00 and 0x20 are used for BANK select */ + break; + } else + if (ev->d.control.param == 121) { + /* reset all controllers */ + break; + } else + if (ev->d.control.param == 120 || ev->d.control.param == 123) { + /* Midi panic: 120: all sound off, 123: all notes off*/ + synth_reset_channel(&(rs->sc[ev->channel])); + break; + } else + if (ev->d.control.param >= 120) { + /* params 122-127 are reserved - skip them. */ + break; + } + break; + default: + break; + } +} + +/****************************************************************************** + * PUBLIC API (used by lv2.c) + */ + +/** + * align LV2 and internal synth buffers + * call synth_fragment as often as needed for the given LV2 buffer size + * + * @param synth synth-handle + * @param written samples written so far (offset in \ref out) + * @param nframes total samples to synthesize and write to the \out buffer + * @param out pointer to stereo output buffers + * @return end of buffer (written + nframes) + */ +static uint32_t synth_sound (void *synth, uint32_t written, const uint32_t nframes, float **out) { + RSSynthesizer* rs = (RSSynthesizer*)synth; + + while (written < nframes) { + uint32_t nremain = nframes - written; + + if (rs->boffset >= BUFFER_SIZE_SAMPLES) { + rs->boffset = 0; + synth_fragment(rs, BUFFER_SIZE_SAMPLES, rs->buf[0], rs->buf[1]); + } + + uint32_t nread = MIN(nremain, (BUFFER_SIZE_SAMPLES - rs->boffset)); + + memcpy(&out[0][written], &rs->buf[0][rs->boffset], nread*sizeof(float)); + memcpy(&out[1][written], &rs->buf[1][rs->boffset], nread*sizeof(float)); + + written += nread; + rs->boffset += nread; + } + return written; +} + +/** + * parse raw midi-data. + * + * @param synth synth-handle + * @param data 8bit midi message + * @param size number of bytes in the midi-message + */ +static void synth_parse_midi(void *synth, uint8_t *data, size_t size) { + if (size < 2 || size > 3) return; + // All messages need to be 3 bytes; except program-changes: 2bytes. + if (size == 2 && (data[0] & 0xf0) != 0xC0) return; + + struct rmidi_event_t ev; + + ev.channel = data[0]&0x0f; + switch (data[0] & 0xf0) { + case 0x80: + ev.type=NOTE_OFF; + ev.d.tone.note=data[1]&0x7f; + ev.d.tone.velocity=data[2]&0x7f; + break; + case 0x90: + ev.type=NOTE_ON; + ev.d.tone.note=data[1]&0x7f; + ev.d.tone.velocity=data[2]&0x7f; + break; + case 0xB0: + ev.type=CONTROL_CHANGE; + ev.d.control.param=data[1]&0x7f; + ev.d.control.value=data[2]&0x7f; + break; + case 0xC0: + ev.type=PROGRAM_CHANGE; + ev.d.control.value=data[1]&0x7f; + break; + default: + return; + } + synth_process_midi_event(synth, &ev); +} + +/** + * initialize the synth + * This should be called after synth_alloc() + * as soon as the sample-rate is known + * + * @param synth synth-handle + * @param rate sample-rate + */ +static void synth_init(void *synth, double rate) { + RSSynthesizer* rs = (RSSynthesizer*)synth; + rs->rate = rate; + rs->boffset = BUFFER_SIZE_SAMPLES; + const float tuning = 440; + for (int k=0; k < 128; k++) { + rs->freqs[k] = (2.0 * tuning / 32.0f) * powf(2, (k - 9.0) / 12.0) / rate; + assert(rs->freqs[k] < M_PI/2); // otherwise spatialization may phase out.. + } + rs->kcfilt = 12.0 / rate; + synth_reset(synth); + + for (int c=0; c < 16; c++) { + synth_load(&rs->sc[c], rate, &synthesize_sineP, &piano_adsr); + } +} + +/** + * Allocate data-structure, create a handle for all other synth_* functions. + * + * This data should be freeded with \ref synth_free when the synth is no + * longer needed. + * + * The synth can only be used after calling \rev synth_init as well. + * + * @return synth-handle + */ +static void * synth_alloc(void) { + return calloc(1, sizeof(RSSynthesizer)); +} + +/** + * release synth data structure + * @param synth synth-handle + */ +static void synth_free(void *synth) { + free(synth); +} +/* vi:set ts=8 sts=2 sw=2: */ diff --git a/libs/plugins/reasonablesynth.lv2/wscript b/libs/plugins/reasonablesynth.lv2/wscript new file mode 100644 index 0000000000..10dd3c21b8 --- /dev/null +++ b/libs/plugins/reasonablesynth.lv2/wscript @@ -0,0 +1,44 @@ +#!/usr/bin/env python +import os +import re +import shutil +import waflib.extras.autowaf as autowaf + +# Mandatory variables +top = '.' +out = 'build' + +def options(opt): + autowaf.set_options(opt) + +def configure(opt): + conf.load('compiler_c') + autowaf.configure(conf) + autowaf.set_c99_mode(conf) + autowaf.check_pkg(conf, 'lv2', atleast_version='1.4.1', + uselib_store='LV2_1_4_1') + +def build(bld): + bundle = 'reasonablesynth.lv2' + module_pat = re.sub('^lib', '', bld.env.cshlib_PATTERN) + module_ext = module_pat[module_pat.rfind('.'):] + + # Build RDF files + for i in ['manifest.ttl', 'reasonablesynth.ttl']: + bld(features = 'subst', + source = i + '.in', + target = '%s/%s' % (bundle, i), + install_path = '${LV2DIR}/%s' % bundle, + LIB_EXT = module_ext) + + # Build plugin library + obj = bld(features = 'c cshlib', + source = 'lv2.c', + dep_files = 'rsynth.c', + name = 'reasonablesynth', + target = '%s/reasonablesynth' % bundle, + install_path = '${LV2DIR}/%s' % bundle, + use = 'LV2_1_4_1') + obj.env.cshlib_PATTERN = module_pat + +# vi:set ts=4 sw=4 et: diff --git a/wscript b/wscript index a29d08eee2..85398b792c 100644 --- a/wscript +++ b/wscript @@ -35,6 +35,7 @@ children = [ 'libs/gtkmm2ext', 'libs/clearlooks-newer', 'libs/audiographer', + 'libs/plugins/reasonablesynth.lv2', 'gtk2_ardour', 'export', 'midi_maps',