/*
 * 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;
}