| 
							- /*
 -  * DISTRHO Nekobi Plugin, based on Nekobee by Sean Bolton and others.
 -  * Copyright (C) 2004 Sean Bolton and others
 -  * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com>
 -  *
 -  * 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 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.
 -  *
 -  * For a full copy of the GNU General Public License see the doc/GPL.txt file.
 -  */
 - 
 - #include "DistrhoPluginNekobi.hpp"
 - 
 - #ifdef CARLA_EXPORT
 - # include "CarlaUtils.hpp"
 - #else
 - # define CARLA_SAFE_ASSERT_INT2(...)
 - #endif
 - 
 - extern "C" {
 - #include "nekobee-src/nekobee_synth.c"
 - #include "nekobee-src/nekobee_voice.c"
 - #include "nekobee-src/nekobee_voice_render.c"
 - #include "nekobee-src/minblep_tables.c"
 - 
 - // -----------------------------------------------------------------------
 - // mutual exclusion
 - 
 - bool dssp_voicelist_mutex_trylock(nekobee_synth_t* synth)
 - {
 -     /* Attempt the mutex lock */
 -     if (pthread_mutex_trylock(&synth->voicelist_mutex) != 0)
 -     {
 -         synth->voicelist_mutex_grab_failed = 1;
 -         return false;
 -     }
 - 
 -     /* Clean up if a previous mutex grab failed */
 -     if (synth->voicelist_mutex_grab_failed)
 -     {
 -         nekobee_synth_all_voices_off(synth);
 -         synth->voicelist_mutex_grab_failed = 0;
 -     }
 - 
 -     return true;
 - }
 - 
 - bool dssp_voicelist_mutex_lock(nekobee_synth_t* synth)
 - {
 -     return (pthread_mutex_lock(&synth->voicelist_mutex) == 0);
 - }
 - 
 - bool dssp_voicelist_mutex_unlock(nekobee_synth_t *synth)
 - {
 -     return (pthread_mutex_unlock(&synth->voicelist_mutex) == 0);
 - }
 - 
 - // -----------------------------------------------------------------------
 - // nekobee_handle_raw_event
 - 
 - void nekobee_handle_raw_event(nekobee_synth_t* synth, uint8_t size, const uint8_t* data)
 - {
 -     if (size != 3)
 -         return;
 - 
 -     switch (data[0] & 0xf0)
 -     {
 -     case 0x80:
 -         nekobee_synth_note_off(synth, data[1], data[2]);
 -         break;
 -     case 0x90:
 -         if (data[2] > 0)
 -             nekobee_synth_note_on(synth, data[1], data[2]);
 -         else
 -             nekobee_synth_note_off(synth, data[1], 64); /* shouldn't happen, but... */
 -         break;
 -     case 0xB0:
 -         nekobee_synth_control_change(synth, data[1], data[2]);
 -         break;
 -     default:
 -         break;
 -     }
 - }
 - 
 - } /* extern "C" */
 - 
 - START_NAMESPACE_DISTRHO
 - 
 - // -----------------------------------------------------------------------
 - 
 - DistrhoPluginNekobi::DistrhoPluginNekobi()
 -     : Plugin(paramCount, 0, 0) // 0 programs, 0 states
 - {
 -     nekobee_init_tables();
 - 
 -     // init synth
 -     fSynth = new nekobee_synth_t;
 - 
 -     fSynth->sample_rate = d_getSampleRate();
 -     fSynth->deltat = 1.0f / (float)d_getSampleRate();
 -     fSynth->nugget_remains = 0;
 - 
 -     fSynth->note_id = 0;
 -     fSynth->polyphony = XSYNTH_DEFAULT_POLYPHONY;
 -     fSynth->voices = XSYNTH_DEFAULT_POLYPHONY;
 -     fSynth->monophonic = XSYNTH_MONO_MODE_ONCE;
 -     fSynth->glide = 0;
 -     fSynth->last_noteon_pitch = 0.0f;
 -     fSynth->vcf_accent = 0.0f;
 -     fSynth->vca_accent = 0.0f;
 - 
 -     for (int i=0; i<8; ++i)
 -       fSynth->held_keys[i] = -1;
 - 
 -     fSynth->voice = nekobee_voice_new();
 -     fSynth->voicelist_mutex_grab_failed = 0;
 -     pthread_mutex_init(&fSynth->voicelist_mutex, nullptr);
 - 
 -     fSynth->channel_pressure = 0;
 -     fSynth->pitch_wheel_sensitivity = 0;
 -     fSynth->pitch_wheel = 0;
 - 
 -     for (int i=0; i<128; ++i)
 -     {
 -         fSynth->key_pressure[i] = 0;
 -         fSynth->cc[i] = 0;
 -     }
 -     fSynth->cc[7] = 127; // full volume
 - 
 -     fSynth->mod_wheel  = 1.0f;
 -     fSynth->pitch_bend = 1.0f;
 -     fSynth->cc_volume  = 1.0f;
 - 
 -     // Default values
 -     fParams.waveform = 0.0f;
 -     fParams.tuning = 0.0f;
 -     fParams.cutoff = 25.0f;
 -     fParams.resonance = 25.0f;
 -     fParams.envMod = 50.0f;
 -     fParams.decay  = 75.0f;
 -     fParams.accent = 25.0f;
 -     fParams.volume = 75.0f;
 - 
 -     // Internal stuff
 -     fSynth->waveform  = 0.0f;
 -     fSynth->tuning    = 1.0f;
 -     fSynth->cutoff    = 5.0f;
 -     fSynth->resonance = 0.8f;
 -     fSynth->envmod    = 0.3f;
 -     fSynth->decay     = 0.0002f;
 -     fSynth->accent    = 0.3f;
 -     fSynth->volume    = 0.75f;
 - 
 -     // reset
 -     d_deactivate();
 - }
 - 
 - DistrhoPluginNekobi::~DistrhoPluginNekobi()
 - {
 -     std::free(fSynth->voice);
 -     delete fSynth;
 - }
 - 
 - // -----------------------------------------------------------------------
 - // Init
 - 
 - void DistrhoPluginNekobi::d_initParameter(uint32_t index, Parameter& parameter)
 - {
 -     switch (index)
 -     {
 -     case paramWaveform:
 -         parameter.hints      = PARAMETER_IS_AUTOMABLE|PARAMETER_IS_BOOLEAN;
 -         parameter.name       = "Waveform";
 -         parameter.symbol     = "waveform";
 -         parameter.ranges.def = 0.0f;
 -         parameter.ranges.min = 0.0f;
 -         parameter.ranges.max = 1.0f;
 -         break;
 -     case paramTuning:
 -         parameter.hints      = PARAMETER_IS_AUTOMABLE; // was 0.5 <-> 2.0, log
 -         parameter.name       = "Tuning";
 -         parameter.symbol     = "tuning";
 -         parameter.ranges.def = 0.0f;
 -         parameter.ranges.min = -12.0f;
 -         parameter.ranges.max = 12.0f;
 -         break;
 -     case paramCutoff:
 -         parameter.hints      = PARAMETER_IS_AUTOMABLE; // modified x2.5
 -         parameter.name       = "Cutoff";
 -         parameter.symbol     = "cutoff";
 -         parameter.unit       = "%";
 -         parameter.ranges.def = 25.0f;
 -         parameter.ranges.min = 0.0f;
 -         parameter.ranges.max = 100.0f;
 -         break;
 -     case paramResonance:
 -         parameter.hints      = PARAMETER_IS_AUTOMABLE; // modified x100
 -         parameter.name       = "VCF Resonance";
 -         parameter.symbol     = "resonance";
 -         parameter.unit       = "%";
 -         parameter.ranges.def = 25.0f;
 -         parameter.ranges.min = 0.0f;
 -         parameter.ranges.max = 95.0f;
 -         break;
 -     case paramEnvMod:
 -         parameter.hints      = PARAMETER_IS_AUTOMABLE; // modified x100
 -         parameter.name       = "Env Mod";
 -         parameter.symbol     = "env_mod";
 -         parameter.unit       = "%";
 -         parameter.ranges.def = 50.0f;
 -         parameter.ranges.min = 0.0f;
 -         parameter.ranges.max = 100.0f;
 -         break;
 -     case paramDecay:
 -         parameter.hints      = PARAMETER_IS_AUTOMABLE; // was 0.000009 <-> 0.0005, log
 -         parameter.name       = "Decay";
 -         parameter.symbol     = "decay";
 -         parameter.unit       = "%";
 -         parameter.ranges.def = 75.0f;
 -         parameter.ranges.min = 0.0f;
 -         parameter.ranges.max = 100.0f;
 -         break;
 -     case paramAccent:
 -         parameter.hints      = PARAMETER_IS_AUTOMABLE; // modified x100
 -         parameter.name       = "Accent";
 -         parameter.symbol     = "accent";
 -         parameter.unit       = "%";
 -         parameter.ranges.def = 25.0f;
 -         parameter.ranges.min = 0.0f;
 -         parameter.ranges.max = 100.0f;
 -         break;
 -     case paramVolume:
 -         parameter.hints      = PARAMETER_IS_AUTOMABLE; // modified x100
 -         parameter.name       = "Volume";
 -         parameter.symbol     = "volume";
 -         parameter.unit       = "%";
 -         parameter.ranges.def = 75.0f;
 -         parameter.ranges.min = 0.0f;
 -         parameter.ranges.max = 100.0f;
 -         break;
 -     }
 - }
 - 
 - // -----------------------------------------------------------------------
 - // Internal data
 - 
 - float DistrhoPluginNekobi::d_getParameterValue(uint32_t index) const
 - {
 -     switch (index)
 -     {
 -     case paramWaveform:
 -         return fParams.waveform;
 -     case paramTuning:
 -         return fParams.tuning;
 -     case paramCutoff:
 -         return fParams.cutoff;
 -     case paramResonance:
 -         return fParams.resonance;
 -     case paramEnvMod:
 -         return fParams.envMod;
 -     case paramDecay:
 -         return fParams.decay;
 -     case paramAccent:
 -         return fParams.accent;
 -     case paramVolume:
 -         return fParams.volume;
 -     }
 - 
 -     return 0.0f;
 - }
 - 
 - void DistrhoPluginNekobi::d_setParameterValue(uint32_t index, float value)
 - {
 -     switch (index)
 -     {
 -     case paramWaveform:
 -         fParams.waveform = value;
 -         fSynth->waveform = value;
 -         CARLA_SAFE_ASSERT_INT2(fSynth->waveform == 0.0f || fSynth->waveform == 1.0f, fSynth->waveform, value);
 -         break;
 -     case paramTuning:
 -         fParams.tuning = value;
 -         fSynth->tuning = (value+12.0f)/24.0f * 1.5 + 0.5f; // FIXME: log?
 -         CARLA_SAFE_ASSERT_INT2(fSynth->tuning >= 0.5f && fSynth->tuning <= 2.0f, fSynth->tuning, value);
 -         break;
 -     case paramCutoff:
 -         fParams.cutoff = value;
 -         fSynth->cutoff = value/2.5f;
 -         CARLA_SAFE_ASSERT_INT2(fSynth->cutoff >= 0.0f && fSynth->cutoff <= 40.0f, fSynth->cutoff, value);
 -         break;
 -     case paramResonance:
 -         fParams.resonance = value;
 -         fSynth->resonance = value/100.0f;
 -         CARLA_SAFE_ASSERT_INT2(fSynth->resonance >= 0.0f && fSynth->resonance <= 0.95f, fSynth->resonance, value);
 -         break;
 -     case paramEnvMod:
 -         fParams.envMod = value;
 -         fSynth->envmod = value/100.0f;
 -         CARLA_SAFE_ASSERT_INT2(fSynth->envmod >= 0.0f && fSynth->envmod <= 1.0f, fSynth->envmod, value);
 -         break;
 -     case paramDecay:
 -         fParams.decay = value;
 -         fSynth->decay = value/100.0f * 0.000491f + 0.000009f; // FIXME: log?
 -         CARLA_SAFE_ASSERT_INT2(fSynth->decay >= 0.000009f && fSynth->decay <= 0.0005f, fSynth->decay, value);
 -         break;
 -     case paramAccent:
 -         fParams.accent = value;
 -         fSynth->accent = value/100.0f;
 -         CARLA_SAFE_ASSERT_INT2(fSynth->accent >= 0.0f && fSynth->accent <= 1.0f, fSynth->accent, value);
 -         break;
 -     case paramVolume:
 -         fParams.volume = value;
 -         fSynth->volume = value/100.0f;
 -         CARLA_SAFE_ASSERT_INT2(fSynth->volume >= 0.0f && fSynth->volume <= 1.0f, fSynth->volume, value);
 -         break;
 -     }
 - }
 - 
 - // -----------------------------------------------------------------------
 - // Process
 - 
 - void DistrhoPluginNekobi::d_activate()
 - {
 -     fSynth->nugget_remains = 0;
 -     fSynth->note_id = 0;
 - 
 -     if (fSynth->voice != nullptr)
 -         nekobee_synth_all_voices_off(fSynth);
 - }
 - 
 - void DistrhoPluginNekobi::d_deactivate()
 - {
 -     if (fSynth->voice != nullptr)
 -         nekobee_synth_all_voices_off(fSynth);
 - }
 - 
 - void DistrhoPluginNekobi::d_run(float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount)
 - {
 -     uint32_t framesDone = 0;
 -     uint32_t curEventIndex = 0;
 -     uint32_t burstSize;
 - 
 -     float* out = outputs[0];
 - 
 -     if (fSynth->voice == nullptr || ! dssp_voicelist_mutex_trylock(fSynth))
 -     {
 -         for (uint32_t i=0; i < frames; ++i)
 -             *out++ = 0.0f;
 -         return;
 -     }
 - 
 -     while (framesDone < frames)
 -     {
 -         if (fSynth->nugget_remains == 0)
 -             fSynth->nugget_remains = XSYNTH_NUGGET_SIZE;
 - 
 -         /* process any ready events */
 -         while (curEventIndex < midiEventCount && framesDone == midiEvents[curEventIndex].frame)
 -         {
 -             nekobee_handle_raw_event(fSynth, midiEvents[curEventIndex].size, midiEvents[curEventIndex].buf);
 -             curEventIndex++;
 -         }
 - 
 -         /* calculate the sample count (burstSize) for the next nekobee_voice_render() call to be the smallest of:
 -          * - control calculation quantization size (XSYNTH_NUGGET_SIZE, in samples)
 -          * - the number of samples remaining in an already-begun nugget (synth->nugget_remains)
 -          * - the number of samples until the next event is ready
 -          * - the number of samples left in this run
 -          */
 -         burstSize = XSYNTH_NUGGET_SIZE;
 - 
 -         /* we're still in the middle of a nugget, so reduce the burst size
 -          * to end when the nugget ends */
 -         if (fSynth->nugget_remains < burstSize)
 -             burstSize = fSynth->nugget_remains;
 - 
 -         /* reduce burst size to end when next event is ready */
 -         if (curEventIndex < midiEventCount && midiEvents[curEventIndex].frame - framesDone < burstSize)
 -             burstSize = midiEvents[curEventIndex].frame - framesDone;
 - 
 -         /* reduce burst size to end at end of this run */
 -         if (frames - framesDone < burstSize)
 -             burstSize = frames - framesDone;
 - 
 -         /* render the burst */
 -         nekobee_synth_render_voices(fSynth, out + framesDone, burstSize, (burstSize == fSynth->nugget_remains));
 -         framesDone += burstSize;
 -         fSynth->nugget_remains -= burstSize;
 -     }
 - 
 -     dssp_voicelist_mutex_unlock(fSynth);
 - }
 - 
 - // -----------------------------------------------------------------------
 - 
 - Plugin* createPlugin()
 - {
 -     return new DistrhoPluginNekobi();
 - }
 - 
 - // -----------------------------------------------------------------------
 - 
 - END_NAMESPACE_DISTRHO
 
 
  |