/* DSSI Plugin Framework * * Copyright (C) 2005-2007 Sean Bolton and others. * * Portions of this file may have come from Peter Hanappe's * Fluidsynth, copyright (C) 2003 Peter Hanappe and others. * Portions of this file may have come from Chris Cannam and Steve * Harris's public domain DSSI example code. * * 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., 59 Temple Place, Suite 330, Boston, * MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include "whysynth_types.h" #include "whysynth.h" #include "whysynth_ports.h" #include "dssp_event.h" #include "common_data.h" #include "whysynth_voice.h" #include "agran_oscillator.h" #include "wave_tables.h" #include "sampleset.h" #include "effects.h" static pthread_mutex_t global_mutex; y_global_t global; static LADSPA_Descriptor *y_LADSPA_descriptor = NULL; static DSSI_Descriptor *y_DSSI_descriptor = NULL; static void y_cleanup(LADSPA_Handle instance); static void y_run_synth(LADSPA_Handle instance, unsigned long sample_count, snd_seq_event_t *events, unsigned long event_count); /* ---- mutual exclusion ---- */ static inline int dssp_voicelist_mutex_trylock(y_synth_t *synth) { int rc; /* Attempt the mutex lock */ rc = pthread_mutex_trylock(&synth->voicelist_mutex); if (rc) { synth->voicelist_mutex_grab_failed = 1; return rc; } /* Clean up if a previous mutex grab failed */ if (synth->voicelist_mutex_grab_failed) { y_synth_all_voices_off(synth); synth->voicelist_mutex_grab_failed = 0; } return 0; } inline int dssp_voicelist_mutex_lock(y_synth_t *synth) { return pthread_mutex_lock(&synth->voicelist_mutex); } inline int dssp_voicelist_mutex_unlock(y_synth_t *synth) { return pthread_mutex_unlock(&synth->voicelist_mutex); } /* ---- LADSPA interface ---- */ /* * y_instantiate * * implements LADSPA (*instantiate)() */ static LADSPA_Handle y_instantiate(const LADSPA_Descriptor *descriptor, unsigned long sample_rate) { y_synth_t *synth = (y_synth_t *)calloc(1, sizeof(y_synth_t)); int i; static float static_zero = 0.0f; if (!synth) return NULL; pthread_mutex_lock(&global_mutex); if (global.initialized) { if (sample_rate != global.sample_rate) { /* all instances must share same sample rate */ pthread_mutex_unlock(&global_mutex); free(synth); return NULL; } global.instance_count++; } else { global.sample_rate = sample_rate; global.grain_envelope = create_grain_envelopes(sample_rate); if (!global.grain_envelope) { YDB_MESSAGE(-1, " y_instantiate: out of memory!\n"); free(synth); return NULL; } if (!sampleset_init()) { YDB_MESSAGE(-1, " y_instantiate: sampleset_setup() failed!\n"); free(synth); return NULL; } global.instance_count = 1; global.initialized = 1; } pthread_mutex_unlock(&global_mutex); /* do any per-instance one-time initialization here */ for (i = 0; i < Y_MAX_POLYPHONY; i++) { synth->voice[i] = y_voice_new(synth); if (!synth->voice[i]) { // YDB_MESSAGE(-1, " y_instantiate: out of memory!\n"); y_cleanup(synth); return NULL; } } if (!new_grain_array(synth, AG_DEFAULT_GRAIN_COUNT)) { YDB_MESSAGE(-1, " y_instantiate: out of memory!\n"); y_cleanup(synth); return NULL; } if (!sampleset_instantiate(synth)) { YDB_MESSAGE(-1, " y_instantiate: out of memory!\n"); y_cleanup(synth); return NULL; } synth->sample_rate = (float)sample_rate; synth->control_rate = (float)sample_rate / (float)Y_CONTROL_PERIOD; synth->deltat = 1.0f / synth->sample_rate; if (!effects_setup(synth)) { YDB_MESSAGE(-1, " y_instantiate: out of memory!\n"); y_cleanup(synth); return NULL; } synth->polyphony = Y_DEFAULT_POLYPHONY; synth->voices = Y_DEFAULT_POLYPHONY; synth->monophonic = 0; synth->glide = 0; synth->last_noteon_pitch = 0.0f; pthread_mutex_init(&synth->voicelist_mutex, NULL); synth->voicelist_mutex_grab_failed = 0; pthread_mutex_init(&synth->patches_mutex, NULL); synth->patch_count = 0; synth->patches_allocated = 0; synth->patches = NULL; synth->pending_patch_change = -1; synth->program_cancel = 1; synth->project_dir = NULL; synth->osc1.sampleset = NULL; synth->osc2.sampleset = NULL; synth->osc3.sampleset = NULL; synth->osc4.sampleset = NULL; synth->glfo.delay = &static_zero; synth->ego.level[3] = &static_zero; synth->eg1.level[3] = &static_zero; synth->eg2.level[3] = &static_zero; synth->eg3.level[3] = &static_zero; synth->eg4.level[3] = &static_zero; synth->mod[Y_MOD_ONE].value = 1.0f; synth->mod[Y_MOD_ONE].next_value = 1.0f; synth->mod[Y_MOD_ONE].delta = 0.0f; synth->dc_block_r = 1.0f - (2.0f * 3.141593f * 20.0f/* Hz */ / (float)sample_rate); /* DC blocker cutoff */ y_data_friendly_patches(synth); y_synth_init_controls(synth); return (LADSPA_Handle)synth; } /* * y_connect_port * * implements LADSPA (*connect_port)() */ static void y_connect_port(LADSPA_Handle instance, unsigned long port, LADSPA_Data *data) { y_synth_t *synth = (y_synth_t *)instance; switch (port) { /* -PORTS- */ case Y_PORT_OUTPUT_LEFT: synth->output_left = data; break; case Y_PORT_OUTPUT_RIGHT: synth->output_right = data; break; case Y_PORT_OSC1_MODE: synth->osc1.mode = data; break; case Y_PORT_OSC1_WAVEFORM: synth->osc1.waveform = data; break; case Y_PORT_OSC1_PITCH: synth->osc1.pitch = data; break; case Y_PORT_OSC1_DETUNE: synth->osc1.detune = data; break; case Y_PORT_OSC1_PITCH_MOD_SRC: synth->osc1.pitch_mod_src = data; break; case Y_PORT_OSC1_PITCH_MOD_AMT: synth->osc1.pitch_mod_amt = data; break; case Y_PORT_OSC1_MPARAM1: synth->osc1.mparam1 = data; break; case Y_PORT_OSC1_MPARAM2: synth->osc1.mparam2 = data; break; case Y_PORT_OSC1_MMOD_SRC: synth->osc1.mmod_src = data; break; case Y_PORT_OSC1_MMOD_AMT: synth->osc1.mmod_amt = data; break; case Y_PORT_OSC1_AMP_MOD_SRC: synth->osc1.amp_mod_src = data; break; case Y_PORT_OSC1_AMP_MOD_AMT: synth->osc1.amp_mod_amt = data; break; case Y_PORT_OSC1_LEVEL_A: synth->osc1.level_a = data; break; case Y_PORT_OSC1_LEVEL_B: synth->osc1.level_b = data; break; case Y_PORT_OSC2_MODE: synth->osc2.mode = data; break; case Y_PORT_OSC2_WAVEFORM: synth->osc2.waveform = data; break; case Y_PORT_OSC2_PITCH: synth->osc2.pitch = data; break; case Y_PORT_OSC2_DETUNE: synth->osc2.detune = data; break; case Y_PORT_OSC2_PITCH_MOD_SRC: synth->osc2.pitch_mod_src = data; break; case Y_PORT_OSC2_PITCH_MOD_AMT: synth->osc2.pitch_mod_amt = data; break; case Y_PORT_OSC2_MPARAM1: synth->osc2.mparam1 = data; break; case Y_PORT_OSC2_MPARAM2: synth->osc2.mparam2 = data; break; case Y_PORT_OSC2_MMOD_SRC: synth->osc2.mmod_src = data; break; case Y_PORT_OSC2_MMOD_AMT: synth->osc2.mmod_amt = data; break; case Y_PORT_OSC2_AMP_MOD_SRC: synth->osc2.amp_mod_src = data; break; case Y_PORT_OSC2_AMP_MOD_AMT: synth->osc2.amp_mod_amt = data; break; case Y_PORT_OSC2_LEVEL_A: synth->osc2.level_a = data; break; case Y_PORT_OSC2_LEVEL_B: synth->osc2.level_b = data; break; case Y_PORT_OSC3_MODE: synth->osc3.mode = data; break; case Y_PORT_OSC3_WAVEFORM: synth->osc3.waveform = data; break; case Y_PORT_OSC3_PITCH: synth->osc3.pitch = data; break; case Y_PORT_OSC3_DETUNE: synth->osc3.detune = data; break; case Y_PORT_OSC3_PITCH_MOD_SRC: synth->osc3.pitch_mod_src = data; break; case Y_PORT_OSC3_PITCH_MOD_AMT: synth->osc3.pitch_mod_amt = data; break; case Y_PORT_OSC3_MPARAM1: synth->osc3.mparam1 = data; break; case Y_PORT_OSC3_MPARAM2: synth->osc3.mparam2 = data; break; case Y_PORT_OSC3_MMOD_SRC: synth->osc3.mmod_src = data; break; case Y_PORT_OSC3_MMOD_AMT: synth->osc3.mmod_amt = data; break; case Y_PORT_OSC3_AMP_MOD_SRC: synth->osc3.amp_mod_src = data; break; case Y_PORT_OSC3_AMP_MOD_AMT: synth->osc3.amp_mod_amt = data; break; case Y_PORT_OSC3_LEVEL_A: synth->osc3.level_a = data; break; case Y_PORT_OSC3_LEVEL_B: synth->osc3.level_b = data; break; case Y_PORT_OSC4_MODE: synth->osc4.mode = data; break; case Y_PORT_OSC4_WAVEFORM: synth->osc4.waveform = data; break; case Y_PORT_OSC4_PITCH: synth->osc4.pitch = data; break; case Y_PORT_OSC4_DETUNE: synth->osc4.detune = data; break; case Y_PORT_OSC4_PITCH_MOD_SRC: synth->osc4.pitch_mod_src = data; break; case Y_PORT_OSC4_PITCH_MOD_AMT: synth->osc4.pitch_mod_amt = data; break; case Y_PORT_OSC4_MPARAM1: synth->osc4.mparam1 = data; break; case Y_PORT_OSC4_MPARAM2: synth->osc4.mparam2 = data; break; case Y_PORT_OSC4_MMOD_SRC: synth->osc4.mmod_src = data; break; case Y_PORT_OSC4_MMOD_AMT: synth->osc4.mmod_amt = data; break; case Y_PORT_OSC4_AMP_MOD_SRC: synth->osc4.amp_mod_src = data; break; case Y_PORT_OSC4_AMP_MOD_AMT: synth->osc4.amp_mod_amt = data; break; case Y_PORT_OSC4_LEVEL_A: synth->osc4.level_a = data; break; case Y_PORT_OSC4_LEVEL_B: synth->osc4.level_b = data; break; case Y_PORT_VCF1_MODE: synth->vcf1.mode = data; break; case Y_PORT_VCF1_SOURCE: synth->vcf1.source = data; break; case Y_PORT_VCF1_FREQUENCY: synth->vcf1.frequency = data; break; case Y_PORT_VCF1_FREQ_MOD_SRC: synth->vcf1.freq_mod_src = data; break; case Y_PORT_VCF1_FREQ_MOD_AMT: synth->vcf1.freq_mod_amt = data; break; case Y_PORT_VCF1_QRES: synth->vcf1.qres = data; break; case Y_PORT_VCF1_MPARAM: synth->vcf1.mparam = data; break; case Y_PORT_VCF2_MODE: synth->vcf2.mode = data; break; case Y_PORT_VCF2_SOURCE: synth->vcf2.source = data; break; case Y_PORT_VCF2_FREQUENCY: synth->vcf2.frequency = data; break; case Y_PORT_VCF2_FREQ_MOD_SRC: synth->vcf2.freq_mod_src = data; break; case Y_PORT_VCF2_FREQ_MOD_AMT: synth->vcf2.freq_mod_amt = data; break; case Y_PORT_VCF2_QRES: synth->vcf2.qres = data; break; case Y_PORT_VCF2_MPARAM: synth->vcf2.mparam = data; break; case Y_PORT_BUSA_LEVEL: synth->busa_level = data; break; case Y_PORT_BUSA_PAN: synth->busa_pan = data; break; case Y_PORT_BUSB_LEVEL: synth->busb_level = data; break; case Y_PORT_BUSB_PAN: synth->busb_pan = data; break; case Y_PORT_VCF1_LEVEL: synth->vcf1_level = data; break; case Y_PORT_VCF1_PAN: synth->vcf1_pan = data; break; case Y_PORT_VCF2_LEVEL: synth->vcf2_level = data; break; case Y_PORT_VCF2_PAN: synth->vcf2_pan = data; break; case Y_PORT_VOLUME: synth->volume = data; break; case Y_PORT_EFFECT_MODE: synth->effect_mode = data; break; case Y_PORT_EFFECT_PARAM1: synth->effect_param1 = data; break; case Y_PORT_EFFECT_PARAM2: synth->effect_param2 = data; break; case Y_PORT_EFFECT_PARAM3: synth->effect_param3 = data; break; case Y_PORT_EFFECT_PARAM4: synth->effect_param4 = data; break; case Y_PORT_EFFECT_PARAM5: synth->effect_param5 = data; break; case Y_PORT_EFFECT_PARAM6: synth->effect_param6 = data; break; case Y_PORT_EFFECT_MIX: synth->effect_mix = data; break; case Y_PORT_GLIDE_TIME: synth->glide_time = data; break; case Y_PORT_BEND_RANGE: synth->bend_range = data; break; case Y_PORT_GLFO_FREQUENCY: synth->glfo.frequency = data; break; case Y_PORT_GLFO_WAVEFORM: synth->glfo.waveform = data; break; /* synth->glfo.delay always points to a 0.0f */ case Y_PORT_GLFO_AMP_MOD_SRC: synth->glfo.amp_mod_src = data; break; case Y_PORT_GLFO_AMP_MOD_AMT: synth->glfo.amp_mod_amt = data; break; case Y_PORT_VLFO_FREQUENCY: synth->vlfo.frequency = data; break; case Y_PORT_VLFO_WAVEFORM: synth->vlfo.waveform = data; break; case Y_PORT_VLFO_DELAY: synth->vlfo.delay = data; break; case Y_PORT_VLFO_AMP_MOD_SRC: synth->vlfo.amp_mod_src = data; break; case Y_PORT_VLFO_AMP_MOD_AMT: synth->vlfo.amp_mod_amt = data; break; case Y_PORT_MLFO_FREQUENCY: synth->mlfo.frequency = data; break; case Y_PORT_MLFO_WAVEFORM: synth->mlfo.waveform = data; break; case Y_PORT_MLFO_DELAY: synth->mlfo.delay = data; break; case Y_PORT_MLFO_AMP_MOD_SRC: synth->mlfo.amp_mod_src = data; break; case Y_PORT_MLFO_AMP_MOD_AMT: synth->mlfo.amp_mod_amt = data; break; case Y_PORT_MLFO_PHASE_SPREAD: synth->mlfo_phase_spread = data; break; case Y_PORT_MLFO_RANDOM_FREQ: synth->mlfo_random_freq = data; break; case Y_PORT_EGO_MODE: synth->ego.mode = data; break; case Y_PORT_EGO_SHAPE1: synth->ego.shape[0] = data; break; case Y_PORT_EGO_TIME1: synth->ego.time[0] = data; break; case Y_PORT_EGO_LEVEL1: synth->ego.level[0] = data; break; case Y_PORT_EGO_SHAPE2: synth->ego.shape[1] = data; break; case Y_PORT_EGO_TIME2: synth->ego.time[1] = data; break; case Y_PORT_EGO_LEVEL2: synth->ego.level[1] = data; break; case Y_PORT_EGO_SHAPE3: synth->ego.shape[2] = data; break; case Y_PORT_EGO_TIME3: synth->ego.time[2] = data; break; case Y_PORT_EGO_LEVEL3: synth->ego.level[2] = data; break; case Y_PORT_EGO_SHAPE4: synth->ego.shape[3] = data; break; case Y_PORT_EGO_TIME4: synth->ego.time[3] = data; break; /* synth->ego.level[3] always points to a 0.0f */ case Y_PORT_EGO_VEL_LEVEL_SENS: synth->ego.vel_level_sens = data; break; case Y_PORT_EGO_VEL_TIME_SCALE: synth->ego.vel_time_scale = data; break; case Y_PORT_EGO_KBD_TIME_SCALE: synth->ego.kbd_time_scale = data; break; case Y_PORT_EGO_AMP_MOD_SRC: synth->ego.amp_mod_src = data; break; case Y_PORT_EGO_AMP_MOD_AMT: synth->ego.amp_mod_amt = data; break; case Y_PORT_EG1_MODE: synth->eg1.mode = data; break; case Y_PORT_EG1_SHAPE1: synth->eg1.shape[0] = data; break; case Y_PORT_EG1_TIME1: synth->eg1.time[0] = data; break; case Y_PORT_EG1_LEVEL1: synth->eg1.level[0] = data; break; case Y_PORT_EG1_SHAPE2: synth->eg1.shape[1] = data; break; case Y_PORT_EG1_TIME2: synth->eg1.time[1] = data; break; case Y_PORT_EG1_LEVEL2: synth->eg1.level[1] = data; break; case Y_PORT_EG1_SHAPE3: synth->eg1.shape[2] = data; break; case Y_PORT_EG1_TIME3: synth->eg1.time[2] = data; break; case Y_PORT_EG1_LEVEL3: synth->eg1.level[2] = data; break; case Y_PORT_EG1_SHAPE4: synth->eg1.shape[3] = data; break; case Y_PORT_EG1_TIME4: synth->eg1.time[3] = data; break; /* synth->eg1.level[3] always points to a 0.0f */ case Y_PORT_EG1_VEL_LEVEL_SENS: synth->eg1.vel_level_sens = data; break; case Y_PORT_EG1_VEL_TIME_SCALE: synth->eg1.vel_time_scale = data; break; case Y_PORT_EG1_KBD_TIME_SCALE: synth->eg1.kbd_time_scale = data; break; case Y_PORT_EG1_AMP_MOD_SRC: synth->eg1.amp_mod_src = data; break; case Y_PORT_EG1_AMP_MOD_AMT: synth->eg1.amp_mod_amt = data; break; case Y_PORT_EG2_MODE: synth->eg2.mode = data; break; case Y_PORT_EG2_SHAPE1: synth->eg2.shape[0] = data; break; case Y_PORT_EG2_TIME1: synth->eg2.time[0] = data; break; case Y_PORT_EG2_LEVEL1: synth->eg2.level[0] = data; break; case Y_PORT_EG2_SHAPE2: synth->eg2.shape[1] = data; break; case Y_PORT_EG2_TIME2: synth->eg2.time[1] = data; break; case Y_PORT_EG2_LEVEL2: synth->eg2.level[1] = data; break; case Y_PORT_EG2_SHAPE3: synth->eg2.shape[2] = data; break; case Y_PORT_EG2_TIME3: synth->eg2.time[2] = data; break; case Y_PORT_EG2_LEVEL3: synth->eg2.level[2] = data; break; case Y_PORT_EG2_SHAPE4: synth->eg2.shape[3] = data; break; case Y_PORT_EG2_TIME4: synth->eg2.time[3] = data; break; /* synth->eg2.level[3] always points to a 0.0f */ case Y_PORT_EG2_VEL_LEVEL_SENS: synth->eg2.vel_level_sens = data; break; case Y_PORT_EG2_VEL_TIME_SCALE: synth->eg2.vel_time_scale = data; break; case Y_PORT_EG2_KBD_TIME_SCALE: synth->eg2.kbd_time_scale = data; break; case Y_PORT_EG2_AMP_MOD_SRC: synth->eg2.amp_mod_src = data; break; case Y_PORT_EG2_AMP_MOD_AMT: synth->eg2.amp_mod_amt = data; break; case Y_PORT_EG3_MODE: synth->eg3.mode = data; break; case Y_PORT_EG3_SHAPE1: synth->eg3.shape[0] = data; break; case Y_PORT_EG3_TIME1: synth->eg3.time[0] = data; break; case Y_PORT_EG3_LEVEL1: synth->eg3.level[0] = data; break; case Y_PORT_EG3_SHAPE2: synth->eg3.shape[1] = data; break; case Y_PORT_EG3_TIME2: synth->eg3.time[1] = data; break; case Y_PORT_EG3_LEVEL2: synth->eg3.level[1] = data; break; case Y_PORT_EG3_SHAPE3: synth->eg3.shape[2] = data; break; case Y_PORT_EG3_TIME3: synth->eg3.time[2] = data; break; case Y_PORT_EG3_LEVEL3: synth->eg3.level[2] = data; break; case Y_PORT_EG3_SHAPE4: synth->eg3.shape[3] = data; break; case Y_PORT_EG3_TIME4: synth->eg3.time[3] = data; break; /* synth->eg3.level[3] always points to a 0.0f */ case Y_PORT_EG3_VEL_LEVEL_SENS: synth->eg3.vel_level_sens = data; break; case Y_PORT_EG3_VEL_TIME_SCALE: synth->eg3.vel_time_scale = data; break; case Y_PORT_EG3_KBD_TIME_SCALE: synth->eg3.kbd_time_scale = data; break; case Y_PORT_EG3_AMP_MOD_SRC: synth->eg3.amp_mod_src = data; break; case Y_PORT_EG3_AMP_MOD_AMT: synth->eg3.amp_mod_amt = data; break; case Y_PORT_EG4_MODE: synth->eg4.mode = data; break; case Y_PORT_EG4_SHAPE1: synth->eg4.shape[0] = data; break; case Y_PORT_EG4_TIME1: synth->eg4.time[0] = data; break; case Y_PORT_EG4_LEVEL1: synth->eg4.level[0] = data; break; case Y_PORT_EG4_SHAPE2: synth->eg4.shape[1] = data; break; case Y_PORT_EG4_TIME2: synth->eg4.time[1] = data; break; case Y_PORT_EG4_LEVEL2: synth->eg4.level[1] = data; break; case Y_PORT_EG4_SHAPE3: synth->eg4.shape[2] = data; break; case Y_PORT_EG4_TIME3: synth->eg4.time[2] = data; break; case Y_PORT_EG4_LEVEL3: synth->eg4.level[2] = data; break; case Y_PORT_EG4_SHAPE4: synth->eg4.shape[3] = data; break; case Y_PORT_EG4_TIME4: synth->eg4.time[3] = data; break; /* synth->eg4.level[3] always points to a 0.0f */ case Y_PORT_EG4_VEL_LEVEL_SENS: synth->eg4.vel_level_sens = data; break; case Y_PORT_EG4_VEL_TIME_SCALE: synth->eg4.vel_time_scale = data; break; case Y_PORT_EG4_KBD_TIME_SCALE: synth->eg4.kbd_time_scale = data; break; case Y_PORT_EG4_AMP_MOD_SRC: synth->eg4.amp_mod_src = data; break; case Y_PORT_EG4_AMP_MOD_AMT: synth->eg4.amp_mod_amt = data; break; case Y_PORT_MODMIX_BIAS: synth->modmix_bias = data; break; case Y_PORT_MODMIX_MOD1_SRC: synth->modmix_mod1_src = data; break; case Y_PORT_MODMIX_MOD1_AMT: synth->modmix_mod1_amt = data; break; case Y_PORT_MODMIX_MOD2_SRC: synth->modmix_mod2_src = data; break; case Y_PORT_MODMIX_MOD2_AMT: synth->modmix_mod2_amt = data; break; case Y_PORT_TUNING: synth->tuning = data; break; default: break; } } /* * y_activate * * implements LADSPA (*activate)() */ static void y_activate(LADSPA_Handle instance) { y_synth_t *synth = (y_synth_t *)instance; synth->control_remains = 0; synth->note_id = 0; y_voice_setup_lfo(synth, &synth->glfo, &synth->glfo_vlfo, 0.0f, 0.0f, synth->mod, &synth->mod[Y_GLOBAL_MOD_GLFO]); y_synth_all_voices_off(synth); } /* * y_ladspa_run_wrapper * * implements LADSPA (*run)() by calling y_run_synth() with no events */ static void y_ladspa_run_wrapper(LADSPA_Handle instance, unsigned long sample_count) { y_run_synth(instance, sample_count, NULL, 0); } // optional: // void (*run_adding)(LADSPA_Handle Instance, // unsigned long SampleCount); // void (*set_run_adding_gain)(LADSPA_Handle Instance, // LADSPA_Data Gain); /* * y_deactivate * * implements LADSPA (*deactivate)() */ void y_deactivate(LADSPA_Handle instance) { y_synth_t *synth = (y_synth_t *)instance; y_synth_all_voices_off(synth); /* stop all sounds immediately */ } /* * y_cleanup * * implements LADSPA (*cleanup)() */ static void y_cleanup(LADSPA_Handle instance) { y_synth_t *synth = (y_synth_t *)instance; int i; for (i = 0; i < Y_MAX_POLYPHONY; i++) if (synth->voice[i]) free(synth->voice[i]); if (synth->patches) free(synth->patches); if (synth->grains) free(synth->grains); if (synth->project_dir) free(synth->project_dir); sampleset_cleanup(synth); effects_cleanup(synth); free(synth); pthread_mutex_lock(&global_mutex); if (--global.instance_count == 0) { sampleset_fini(); free_grain_envelopes(global.grain_envelope); global.initialized = 0; } pthread_mutex_unlock(&global_mutex); } /* ---- DSSI interface ---- */ /* * dssi_configure_message */ char * dssi_configure_message(const char *fmt, ...) { va_list args; char buffer[256]; va_start(args, fmt); vsnprintf(buffer, 256, fmt, args); va_end(args); return strdup(buffer); } /* * y_configure * * implements DSSI (*configure)() */ char * y_configure(LADSPA_Handle instance, const char *key, const char *value) { YDB_MESSAGE(YDB_DSSI, " y_configure called with '%s' and '%s'\n", key, value); if (!strcmp(key, "load")) { return y_synth_handle_load((y_synth_t *)instance, value); } else if (!strcmp(key, "polyphony")) { return y_synth_handle_polyphony((y_synth_t *)instance, value); } else if (!strcmp(key, "monophonic")) { return y_synth_handle_monophonic((y_synth_t *)instance, value); } else if (!strcmp(key, "glide")) { return y_synth_handle_glide((y_synth_t *)instance, value); } else if (!strcmp(key, "program_cancel")) { return y_synth_handle_program_cancel((y_synth_t *)instance, value); } else if (!strcmp(key, DSSI_PROJECT_DIRECTORY_KEY)) { return y_synth_handle_project_dir((y_synth_t *)instance, value); } return strdup("error: unrecognized configure key"); } /* * y_get_program * * implements DSSI (*get_program)() */ const DSSI_Program_Descriptor * y_get_program(LADSPA_Handle instance, unsigned long index) { y_synth_t *synth = (y_synth_t *)instance; static DSSI_Program_Descriptor pd; YDB_MESSAGE(YDB_DSSI, " y_get_program called with %lu\n", index); if (index < synth->patch_count) { y_synth_set_program_descriptor(synth, &pd, index); pd.Bank = index / 128; pd.Program = index % 128; return &pd; } return NULL; } /* * y_select_program * * implements DSSI (*select_program)() */ void y_select_program(LADSPA_Handle handle, unsigned long bank, unsigned long program) { y_synth_t *synth = (y_synth_t *)handle; YDB_MESSAGE(YDB_DSSI, " y_select_program called with %lu and %lu\n", bank, program); /* ignore invalid program requests */ if (program >= 128) return; program = bank * 128 + program; if (program >= synth->patch_count) return; /* Attempt the patch mutex, return if lock fails. */ if (pthread_mutex_trylock(&synth->patches_mutex)) { synth->pending_patch_change = program; return; } y_synth_select_patch(synth, program); pthread_mutex_unlock(&synth->patches_mutex); } /* * dssp_handle_pending_patch_change */ static inline void dssp_handle_pending_patch_change(y_synth_t *synth) { /* Attempt the patch mutex, return if lock fails. */ if (pthread_mutex_trylock(&synth->patches_mutex)) return; if (synth->pending_patch_change < synth->patch_count) y_synth_select_patch(synth, synth->pending_patch_change); synth->pending_patch_change = -1; pthread_mutex_unlock(&synth->patches_mutex); } /* * y_get_midi_controller * * implements DSSI (*get_midi_controller_for_port)() */ int y_get_midi_controller(LADSPA_Handle instance, unsigned long port) { YDB_MESSAGE(YDB_DSSI, " y_get_midi_controller called for port %lu\n", port); switch (port) { case Y_PORT_GLIDE_TIME: return DSSI_CC(MIDI_CTL_MSB_PORTAMENTO_TIME); #ifdef USE_GARETTS_CONTROLLER_ASSIGNMENTS /* Ideally, MIDI controller assignments will someday be run-time * configurable. Until then, you have to roll your own. Here's * a setup that Garett Shulman uses with a Doepfer Pocket Fader * to sort-of emulate a Juno 106: */ case Y_PORT_OSC1_LEVEL_A: return DSSI_CC(0x30); /* General Purpose Controller #1 */ case Y_PORT_OSC2_LEVEL_A: return DSSI_CC(0x32); /* General Purpose Controller #3 */ case Y_PORT_OSC2_MPARAM2: return DSSI_CC(0x33); /* General Purpose Controller #4 */ case Y_PORT_OSC2_MMOD_AMT: return DSSI_CC(0x34); /* Undefined */ case Y_PORT_OSC3_LEVEL_A: return DSSI_CC(0x31); /* General Purpose Controller #2 */ /* case amp mod source amount for all OSC: * return DSSI_CC(0x35); * case frequency for all OSC * return DSSI_CC(0x36); */ case Y_PORT_VCF1_FREQUENCY: return DSSI_CC(0x37); /* case Y_PORT_VCF1_FREQ_MOD_AMT: * return DSSI_CC(0x37); */ case Y_PORT_VCF1_QRES: return DSSI_CC(0x38); /* case Y_PORT_VCF1_MPARAM: * return DSSI_CC(0x39); */ case Y_PORT_GLFO_FREQUENCY: return DSSI_CC(0x3b); case Y_PORT_EG1_TIME1: return DSSI_CC(0x3c); case Y_PORT_EG1_TIME3: return DSSI_CC(0x3d); case Y_PORT_EG1_LEVEL3: return DSSI_CC(0x3e); case Y_PORT_EG1_TIME4: return DSSI_CC(0x3f); case Y_PORT_MODMIX_MOD1_AMT: return DSSI_CC(0x39); case Y_PORT_MODMIX_MOD2_AMT: return DSSI_CC(0x3a); #endif default: break; } return DSSI_NONE; } /* * y_handle_event */ static inline void y_handle_event(y_synth_t *synth, snd_seq_event_t *event) { YDB_MESSAGE(YDB_DSSI, " y_handle_event called with event type %d\n", event->type); switch (event->type) { case SND_SEQ_EVENT_NOTEOFF: y_synth_note_off(synth, event->data.note.note, event->data.note.velocity); break; case SND_SEQ_EVENT_NOTEON: if (event->data.note.velocity > 0) y_synth_note_on(synth, event->data.note.note, event->data.note.velocity); else y_synth_note_off(synth, event->data.note.note, 64); /* shouldn't happen, but... */ break; case SND_SEQ_EVENT_KEYPRESS: y_synth_key_pressure(synth, event->data.note.note, event->data.note.velocity); break; case SND_SEQ_EVENT_CONTROLLER: y_synth_control_change(synth, event->data.control.param, event->data.control.value); break; case SND_SEQ_EVENT_CHANPRESS: y_synth_channel_pressure(synth, event->data.control.value); break; case SND_SEQ_EVENT_PITCHBEND: y_synth_pitch_bend(synth, event->data.control.value); break; /* SND_SEQ_EVENT_PGMCHANGE - shouldn't happen */ /* SND_SEQ_EVENT_SYSEX - shouldn't happen */ /* SND_SEQ_EVENT_CONTROL14? */ /* SND_SEQ_EVENT_NONREGPARAM? */ /* SND_SEQ_EVENT_REGPARAM? */ default: break; } } /* * y_run_synth * * implements DSSI (*run_synth)() */ static void y_run_synth(LADSPA_Handle instance, unsigned long sample_count, snd_seq_event_t *events, unsigned long event_count) { y_synth_t *synth = (y_synth_t *)instance; unsigned long samples_done = 0; unsigned long event_index = 0; unsigned long burst_size; /* attempt the mutex, return only silence if lock fails. */ if (dssp_voicelist_mutex_trylock(synth)) { memset(synth->output_left, 0, sizeof(LADSPA_Data) * sample_count); memset(synth->output_right, 0, sizeof(LADSPA_Data) * sample_count); return; } if (synth->pending_patch_change > -1) dssp_handle_pending_patch_change(synth); while (samples_done < sample_count) { if (!synth->control_remains) synth->control_remains = Y_CONTROL_PERIOD; /* process any ready events */ while (event_index < event_count && samples_done == events[event_index].time.tick) { y_handle_event(synth, &events[event_index]); event_index++; } /* calculate the sample count (burst_size) for the next * y_voice_render() call to be the smallest of: * - control calculation quantization size (Y_CONTROL_PERIOD, in * samples) * - the number of samples remaining in an already-begun control cycle * (synth->control_remains) * - the number of samples until the next event is ready * - the number of samples left in this run */ burst_size = Y_CONTROL_PERIOD; if (synth->control_remains < burst_size) { /* we're still in the middle of a control cycle, so reduce the * burst size to end when the cycle ends */ burst_size = synth->control_remains; } if (event_index < event_count && events[event_index].time.tick - samples_done < burst_size) { /* reduce burst size to end when next event is ready */ burst_size = events[event_index].time.tick - samples_done; } if (sample_count - samples_done < burst_size) { /* reduce burst size to end at end of this run */ burst_size = sample_count - samples_done; } /* render the burst */ y_synth_render_voices(synth, synth->output_left + samples_done, synth->output_right + samples_done, burst_size, (burst_size == synth->control_remains)); samples_done += burst_size; synth->control_remains -= burst_size; } #if defined(Y_DEBUG) && (Y_DEBUG & YDB_AUDIO) *synth->output_left += 0.10f; /* add a 'buzz' to output so there's something audible even when quiescent */ *synth->output_right += 0.10f; #endif /* defined(Y_DEBUG) && (Y_DEBUG & YDB_AUDIO) */ dssp_voicelist_mutex_unlock(synth); } // optional: // void (*run_synth_adding)(LADSPA_Handle Instance, // unsigned long SampleCount, // snd_seq_event_t *Events, // unsigned long EventCount); /* ---- export ---- */ const LADSPA_Descriptor *ladspa_descriptor(unsigned long index) { switch (index) { case 0: return y_LADSPA_descriptor; default: return NULL; } } const DSSI_Descriptor *dssi_descriptor(unsigned long index) { switch (index) { case 0: return y_DSSI_descriptor; default: return NULL; } } #ifdef __GNUC__ __attribute__((constructor)) void init() #else void _init() #endif { int i; char **port_names; LADSPA_PortDescriptor *port_descriptors; LADSPA_PortRangeHint *port_range_hints; Y_DEBUG_INIT("whysynth.so"); pthread_mutex_init(&global_mutex, NULL); global.initialized = 0; y_init_tables(); wave_tables_set_count(); y_LADSPA_descriptor = (LADSPA_Descriptor *) malloc(sizeof(LADSPA_Descriptor)); if (y_LADSPA_descriptor) { y_LADSPA_descriptor->UniqueID = 2187; y_LADSPA_descriptor->Label = "WhySynth"; y_LADSPA_descriptor->Properties = 0; #ifdef HAVE_CONFIG_H y_LADSPA_descriptor->Name = "WhySynth " VERSION " DSSI plugin"; #else y_LADSPA_descriptor->Name = "WhySynth DSSI plugin"; #endif y_LADSPA_descriptor->Maker = "Sean Bolton "; y_LADSPA_descriptor->Copyright = "GNU General Public License version 2 or later"; y_LADSPA_descriptor->PortCount = Y_PORTS_COUNT; port_descriptors = (LADSPA_PortDescriptor *) calloc(y_LADSPA_descriptor->PortCount, sizeof (LADSPA_PortDescriptor)); y_LADSPA_descriptor->PortDescriptors = (const LADSPA_PortDescriptor *) port_descriptors; port_range_hints = (LADSPA_PortRangeHint *) calloc(y_LADSPA_descriptor->PortCount, sizeof (LADSPA_PortRangeHint)); y_LADSPA_descriptor->PortRangeHints = (const LADSPA_PortRangeHint *) port_range_hints; port_names = (char **) calloc(y_LADSPA_descriptor->PortCount, sizeof(char *)); y_LADSPA_descriptor->PortNames = (const char **) port_names; for (i = 0; i < Y_PORTS_COUNT; i++) { port_descriptors[i] = y_port_description[i].port_descriptor; port_names[i] = y_port_description[i].name; port_range_hints[i].HintDescriptor = y_port_description[i].hint_descriptor; port_range_hints[i].LowerBound = y_port_description[i].lower_bound; if (y_port_description[i].type == Y_PORT_TYPE_POPMENU && (y_port_description[i].subtype == Y_PMBUTTON_TYPE_OSC_WAVEFORM || y_port_description[i].subtype == Y_PMBUTTON_TYPE_WT_WAVEFORM)) { port_range_hints[i].UpperBound = (float)wavetables_count - 1; } else { port_range_hints[i].UpperBound = y_port_description[i].upper_bound; } } y_LADSPA_descriptor->instantiate = y_instantiate; y_LADSPA_descriptor->connect_port = y_connect_port; y_LADSPA_descriptor->activate = y_activate; y_LADSPA_descriptor->run = y_ladspa_run_wrapper; y_LADSPA_descriptor->run_adding = NULL; y_LADSPA_descriptor->set_run_adding_gain = NULL; y_LADSPA_descriptor->deactivate = y_deactivate; y_LADSPA_descriptor->cleanup = y_cleanup; } y_DSSI_descriptor = (DSSI_Descriptor *) malloc(sizeof(DSSI_Descriptor)); if (y_DSSI_descriptor) { y_DSSI_descriptor->DSSI_API_Version = 1; y_DSSI_descriptor->LADSPA_Plugin = y_LADSPA_descriptor; y_DSSI_descriptor->configure = y_configure; y_DSSI_descriptor->get_program = y_get_program; y_DSSI_descriptor->select_program = y_select_program; y_DSSI_descriptor->get_midi_controller_for_port = y_get_midi_controller; y_DSSI_descriptor->run_synth = y_run_synth; y_DSSI_descriptor->run_synth_adding = NULL; y_DSSI_descriptor->run_multiple_synths = NULL; y_DSSI_descriptor->run_multiple_synths_adding = NULL; } } #ifdef __GNUC__ __attribute__((destructor)) void fini() #else void _fini() #endif { if (y_LADSPA_descriptor) { free((LADSPA_PortDescriptor *) y_LADSPA_descriptor->PortDescriptors); free((char **) y_LADSPA_descriptor->PortNames); free((LADSPA_PortRangeHint *) y_LADSPA_descriptor->PortRangeHints); free(y_LADSPA_descriptor); } if (y_DSSI_descriptor) { free(y_DSSI_descriptor); } }