/* * nekobee_get_midi_controller * * implements DSSI (*get_midi_controller_for_port)() */ int nekobee_get_midi_controller(LADSPA_Handle instance, unsigned long port) { XDB_MESSAGE(XDB_DSSI, " nekobee_get_midi_controller called for port %lu\n", port); switch (port) { case XSYNTH_PORT_TUNING: return DSSI_CC(MIDI_CTL_TUNING); case XSYNTH_PORT_WAVEFORM: return DSSI_CC(MIDI_CTL_WAVEFORM); case XSYNTH_PORT_CUTOFF: return DSSI_CC(MIDI_CTL_CUTOFF); case XSYNTH_PORT_RESONANCE: return DSSI_CC(MIDI_CTL_RESONANCE); case XSYNTH_PORT_ENVMOD: return DSSI_CC(MIDI_CTL_ENVMOD); case XSYNTH_PORT_DECAY: return DSSI_CC(MIDI_CTL_DECAY); case XSYNTH_PORT_ACCENT: return DSSI_CC(MIDI_CTL_ACCENT); case XSYNTH_PORT_VOLUME: return DSSI_CC(MIDI_CTL_MSB_MAIN_VOLUME); default: break; } return DSSI_NONE; }
/* * nekobee_get_program * * implements DSSI (*get_program)() */ const DSSI_Program_Descriptor * nekobee_get_program(LADSPA_Handle instance, unsigned long index) { nekobee_synth_t *synth = (nekobee_synth_t *)instance; static DSSI_Program_Descriptor pd; XDB_MESSAGE(XDB_DSSI, " nekobee_get_program called with %lu\n", index); if (index < 128) { nekobee_synth_set_program_descriptor(synth, &pd, 0, index); return &pd; } return NULL; }
/* * nekobee_synth_note_on */ void nekobee_synth_note_on(nekobee_synth_t *synth, unsigned char key, unsigned char velocity) { nekobee_voice_t* voice; voice = synth->voice; if (_PLAYING(synth->voice)) { XDB_MESSAGE(XDB_NOTE, " nekobee_synth_note_on: retriggering mono voice on new key %d\n", key); } voice->note_id = synth->note_id++; nekobee_voice_note_on(synth, voice, key, velocity); }
/* * nekobee_synth_note_off * * handle a note off message */ void nekobee_synth_note_off(nekobee_synth_t *synth, unsigned char key, unsigned char rvelocity) { int i, count = 0; nekobee_voice_t *voice; for (i = 0; i < synth->voices; i++) { voice = synth->voice; if (_PLAYING(voice)) { XDB_MESSAGE(XDB_NOTE, " nekobee_synth_note_off: key %d rvel %d voice %d note id %d\n", key, rvelocity, i, voice->note_id); nekobee_voice_note_off(synth, voice, key, 64); count++; } } if (!count) nekobee_voice_remove_held_key(synth, key); }
/* * nekobee_configure * * implements DSSI (*configure)() */ char * nekobee_configure(LADSPA_Handle instance, const char *key, const char *value) { XDB_MESSAGE(XDB_DSSI, " nekobee_configure called with '%s' and '%s'\n", key, value); if (strlen(key) == 8 && !strncmp(key, "patches", 7)) { // return nekobee_synth_handle_patches((nekobee_synth_t *)instance, key, value); } else if (!strcmp(key, DSSI_PROJECT_DIRECTORY_KEY)) { return NULL; /* plugin has no use for project directory key, ignore it */ } else if (!strcmp(key, "load")) { return dssi_configure_message("warning: host sent obsolete 'load' key with filename '%s'", value); } return strdup("error: unrecognized configure key"); }
/* * nekobee_select_program * * implements DSSI (*select_program)() */ void nekobee_select_program(LADSPA_Handle handle, unsigned long bank, unsigned long program) { nekobee_synth_t *synth = (nekobee_synth_t *)handle; XDB_MESSAGE(XDB_DSSI, " nekobee_select_program called with %lu and %lu\n", bank, program); /* ignore invalid program requests */ if (bank || program >= 1) return; /* Attempt the patch mutex, return if lock fails. */ if (pthread_mutex_trylock(&synth->patches_mutex)) { synth->pending_program_change = program; return; } nekobee_synth_select_program(synth, bank, program); pthread_mutex_unlock(&synth->patches_mutex); }
/* * nekobee_instantiate * * implements LADSPA (*instantiate)() */ LADSPA_Handle nekobee_instantiate(const LADSPA_Descriptor *descriptor, unsigned long sample_rate) { nekobee_synth_t *synth = (nekobee_synth_t *)calloc(1, sizeof(nekobee_synth_t)); if (!synth) return NULL; synth->voice = nekobee_voice_new(synth); if (!synth->voice) { // XDB_MESSAGE(-1, " nekobee_instantiate: out of memory!\n"); nekobee_cleanup(synth); return NULL; } if (!(synth->patches = (nekobee_patch_t *)malloc(sizeof(nekobee_patch_t)))) { XDB_MESSAGE(-1, " nekobee_instantiate: out of memory!\n"); nekobee_cleanup(synth); return NULL; } /* do any per-instance one-time initialization here */ synth->sample_rate = sample_rate; synth->deltat = 1.0f / (float)synth->sample_rate; synth->polyphony = XSYNTH_DEFAULT_POLYPHONY; synth->voices = XSYNTH_DEFAULT_POLYPHONY; synth->monophonic = XSYNTH_MONO_MODE_ONCE; 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->pending_program_change = -1; synth->current_program = -1; // FIXME - all we really need to do is init the patch data once nekobee_data_friendly_patches(synth); nekobee_synth_init_controls(synth); return (LADSPA_Handle)synth; }
/* * nekobee_handle_event */ inline void nekobee_handle_event(nekobee_synth_t *synth, snd_seq_event_t *event) { XDB_MESSAGE(XDB_DSSI, " nekobee_handle_event called with event type %d\n", event->type); switch (event->type) { case SND_SEQ_EVENT_NOTEOFF: nekobee_synth_note_off(synth, event->data.note.note, event->data.note.velocity); break; case SND_SEQ_EVENT_NOTEON: if (event->data.note.velocity > 0) nekobee_synth_note_on(synth, event->data.note.note, event->data.note.velocity); else nekobee_synth_note_off(synth, event->data.note.note, 64); /* shouldn't happen, but... */ break; case SND_SEQ_EVENT_CONTROLLER: nekobee_synth_control_change(synth, event->data.control.param, event->data.control.value); break; // somewhere in here we need to respond to NRPN default: break; } }
/* * xsynth_voice_render * * generate the actual sound data for this voice */ void xsynth_voice_render(xsynth_synth_t *synth, xsynth_voice_t *voice, LADSPA_Data *out, unsigned long sample_count, int do_control_update) { unsigned long sample; synth->monophonic = *synth->monomode; synth->glide = *synth->glidemode; /* state variables saved in voice */ float lfo_pos = voice->lfo_pos, eg1 = voice->eg1, eg2 = voice->eg2; unsigned char eg1_phase = voice->eg1_phase, eg2_phase = voice->eg2_phase; int osc_index = voice->osc_index; /* temporary variables used in calculating voice */ float fund_pitch; float deltat = synth->deltat; float freq, freqkey, freqeg1, freqeg2, lfo; /* set up synthesis variables from patch */ float omega1, omega2; unsigned char osc_sync = (*(synth->osc_sync) > 0.0001f); float omega3 = *(synth->lfo_frequency); unsigned char lfo_waveform = lrintf(*(synth->lfo_waveform)); float lfo_amount_o = *(synth->lfo_amount_o); float lfo_amount_f = *(synth->lfo_amount_f); float eg1_amp = qdB_to_amplitude(velocity_to_attenuation[voice->velocity] * *(synth->eg1_vel_sens)); float eg1_rate_level[3], eg1_one_rate[3]; float eg1_amount_o = *(synth->eg1_amount_o); float eg2_amp = qdB_to_amplitude(velocity_to_attenuation[voice->velocity] * *(synth->eg2_vel_sens)); float eg2_rate_level[3], eg2_one_rate[3]; float eg2_amount_o = *(synth->eg2_amount_o); unsigned char vcf_mode = lrintf(*(synth->vcf_mode)); float qres = *(synth->vcf_qres) / 1.995f * voice->pressure; /* now 0 to 1 */ float balance1 = 1.0f - *(synth->osc_balance); float balance2 = *(synth->osc_balance); float vol_out = volume(*(synth->volume) * synth->cc_volume); fund_pitch = *(synth->glide_time) * voice->target_pitch + (1.0f - *(synth->glide_time)) * voice->prev_pitch; /* portamento */ if (do_control_update) { voice->prev_pitch = fund_pitch; /* save pitch for next time */ } fund_pitch *= synth->pitch_bend * *(synth->tuning); omega1 = *(synth->osc1_pitch) * fund_pitch; omega2 = *(synth->osc2_pitch) * fund_pitch; eg1_rate_level[0] = *(synth->eg1_attack_time) * eg1_amp; /* eg1_attack_time * 1.0f * eg1_amp */ eg1_one_rate[0] = 1.0f - *(synth->eg1_attack_time); eg1_rate_level[1] = *(synth->eg1_decay_time) * *(synth->eg1_sustain_level) * eg1_amp; eg1_one_rate[1] = 1.0f - *(synth->eg1_decay_time); eg1_rate_level[2] = 0.0f; /* eg1_release_time * 0.0f * eg1_amp */ eg1_one_rate[2] = 1.0f - *(synth->eg1_release_time); eg2_rate_level[0] = *(synth->eg2_attack_time) * eg2_amp; eg2_one_rate[0] = 1.0f - *(synth->eg2_attack_time); eg2_rate_level[1] = *(synth->eg2_decay_time) * *(synth->eg2_sustain_level) * eg2_amp; eg2_one_rate[1] = 1.0f - *(synth->eg2_decay_time); eg2_rate_level[2] = 0.0f; eg2_one_rate[2] = 1.0f - *(synth->eg2_release_time); eg1_amp *= 0.99f; /* Xsynth's original eg phase 1 to 2 transition check was: */ eg2_amp *= 0.99f; /* if (!eg1_phase && eg1 > 0.99f) eg1_phase = 1; */ freq = M_PI_F * deltat * fund_pitch * synth->mod_wheel; /* now (0 to 1) * pi */ freqkey = freq * *(synth->vcf_cutoff); freqeg1 = freq * *(synth->eg1_amount_f); freqeg2 = freq * *(synth->eg2_amount_f); /* copy some things so oscillator functions can see them */ voice->osc1.waveform = lrintf(*(synth->osc1_waveform)); voice->osc1.pw = *(synth->osc1_pulsewidth); voice->osc2.waveform = lrintf(*(synth->osc2_waveform)); voice->osc2.pw = *(synth->osc2_pulsewidth); /* --- LFO, EG1, and EG2 section */ for (sample = 0; sample < sample_count; sample++) { lfo = oscillator(&lfo_pos, omega3, deltat, lfo_waveform); eg1 = eg1_rate_level[eg1_phase] + eg1_one_rate[eg1_phase] * eg1; eg2 = eg2_rate_level[eg2_phase] + eg2_one_rate[eg2_phase] * eg2; voice->osc2_w_buf[sample] = deltat * omega2 * (1.0f + eg1 * eg1_amount_o) * (1.0f + eg2 * eg2_amount_o) * (1.0f + lfo * lfo_amount_o); voice->freqcut_buf[sample] = (freqkey + freqeg1 * eg1 + freqeg2 * eg2) * (1.0f + lfo * lfo_amount_f); voice->vca_buf[sample] = eg1 * vol_out; if (!eg1_phase && eg1 > eg1_amp) eg1_phase = 1; /* flip from attack to decay */ if (!eg2_phase && eg2 > eg2_amp) eg2_phase = 1; /* flip from attack to decay */ } /* --- VCO 1 section */ if (osc_sync) blosc_master(sample_count, voice, &voice->osc1, osc_index, balance1, deltat * omega1); else blosc_single1(sample_count, voice, &voice->osc1, osc_index, balance1, deltat * omega1); /* --- VCO 2 section */ if (osc_sync) blosc_slave(sample_count, voice, &voice->osc2, osc_index, balance2, voice->osc2_w_buf); else blosc_single2(sample_count, voice, &voice->osc2, osc_index, balance2, voice->osc2_w_buf); /* --- VCF and VCA section */ switch (vcf_mode) { default: case 0: vcf_2pole(voice, sample_count, voice->osc_audio + osc_index, out, voice->freqcut_buf, qres, voice->vca_buf); break; case 1: vcf_4pole(voice, sample_count, voice->osc_audio + osc_index, out, voice->freqcut_buf, qres, voice->vca_buf); break; case 2: vcf_mvclpf(voice, sample_count, voice->osc_audio + osc_index, out, voice->freqcut_buf, qres, voice->vca_buf); break; } osc_index += sample_count; if (do_control_update) { /* do those things should be done only once per control-calculation * interval ("nugget"), such as voice check-for-dead, pitch envelope * calculations, volume envelope phase transition checks, etc. */ /* check if we've decayed to nothing, turn off voice if so */ if (eg1_phase == 2 && voice->vca_buf[sample_count - 1] < 6.26e-6f) { /* sound has completed its release phase (>96dB below volume '5' max) */ XDB_MESSAGE(XDB_NOTE, " xsynth_voice_render check for dead: killing note id %d\n", voice->note_id); xsynth_voice_off(voice); return; /* we're dead now, so return */ } /* already saved prev_pitch above */ /* check oscillator audio buffer index, shift buffer if necessary */ if (osc_index > MINBLEP_BUFFER_LENGTH - (XSYNTH_NUGGET_SIZE + LONGEST_DD_PULSE_LENGTH)) { memcpy(voice->osc_audio, voice->osc_audio + osc_index, LONGEST_DD_PULSE_LENGTH * sizeof (float)); memset(voice->osc_audio + LONGEST_DD_PULSE_LENGTH, 0, (MINBLEP_BUFFER_LENGTH - LONGEST_DD_PULSE_LENGTH) * sizeof (float)); osc_index = 0; } } /* save things for next time around */ voice->lfo_pos = lfo_pos; voice->eg1 = eg1; voice->eg1_phase = eg1_phase; voice->eg2 = eg2; voice->eg2_phase = eg2_phase; voice->osc_index = osc_index; }