Logo Search packages:      
Sourcecode: nekobee version File versions  Download package

nekobee_voice_render.c

/* nekobee DSSI software synthesizer plugin
 */

#define _BSD_SOURCE    1
#define _SVID_SOURCE   1
#define _ISOC99_SOURCE 1

#include <math.h>

#include <ladspa.h>

#include "nekobee.h"
#include "nekobee_synth.h"
#include "nekobee_voice.h"
 
#define M_2PI_F (2.0f * (float)M_PI)
#define M_PI_F (float)M_PI

#define VCF_FREQ_MAX  (0.825f)    /* original filters only stable to this frequency */

static int   tables_initialized = 0;

float        nekobee_pitch[128];

#define pitch_ref_note 69

#define volume_to_amplitude_scale 128

static float volume_to_amplitude_table[4 + volume_to_amplitude_scale + 2];

static float velocity_to_attenuation[128];

static float qdB_to_amplitude_table[4 + 256 + 0];

void
nekobee_init_tables(void)
{
    int i;
    float pexp;
    float volume, volume_exponent;
    float ol, amp;

    if (tables_initialized)
        return;

    /* MIDI note to pitch */
    for (i = 0; i < 128; ++i) {
        pexp = (float)(i - pitch_ref_note) / 12.0f;
        nekobee_pitch[i] = powf(2.0f, pexp);
    }

    /* volume to amplitude
     *
     * This generates a curve which is:
     *  volume_to_amplitude_table[128 + 4] = 0.25 * 3.16...   ~=  -2dB
     *  volume_to_amplitude_table[64 + 4]  = 0.25 * 1.0       ~= -12dB
     *  volume_to_amplitude_table[32 + 4]  = 0.25 * 0.316...  ~= -22dB
     *  volume_to_amplitude_table[16 + 4]  = 0.25 * 0.1       ~= -32dB
     *   etc.
     */
    volume_exponent = 1.0f / (2.0f * log10f(2.0f));
    for (i = 0; i <= volume_to_amplitude_scale; i++) {
        volume = (float)i / (float)volume_to_amplitude_scale;
        volume_to_amplitude_table[i + 4] = powf(2.0f * volume, volume_exponent) / 4.0f;
    }
    volume_to_amplitude_table[ -1 + 4] = 0.0f;
    volume_to_amplitude_table[129 + 4] = volume_to_amplitude_table[128 + 4];

    /* velocity to attenuation
     *
     * Creates the velocity to attenuation lookup table, for converting
     * velocities [1, 127] to full-velocity-sensitivity attenuation in
     * quarter decibels.  Modeled after my TX-7's velocity response.*/
    velocity_to_attenuation[0] = 253.9999f;
    for (i = 1; i < 127; i++) {
        if (i >= 10) {
            ol = (powf(((float)i / 127.0f), 0.32f) - 1.0f) * 100.0f;
            amp = powf(2.0f, ol / 8.0f);
        } else {
            ol = (powf(((float)10 / 127.0f), 0.32f) - 1.0f) * 100.0f;
            amp = powf(2.0f, ol / 8.0f) * (float)i / 10.0f;
        }
        velocity_to_attenuation[i] = log10f(amp) * -80.0f;
    }
    velocity_to_attenuation[127] = 0.0f;

    /* quarter-decibel attenuation to amplitude */
    qdB_to_amplitude_table[-1 + 4] = 1.0f;
    for (i = 0; i <= 255; i++) {
        qdB_to_amplitude_table[i + 4] = powf(10.0f, (float)i / -80.0f);
    }

    tables_initialized = 1;
}

static inline float
volume(float level)
{
    unsigned char segment;
    float fract;

    level *= (float)volume_to_amplitude_scale;
    segment = lrintf(level - 0.5f);
    fract = level - (float)segment;

    return volume_to_amplitude_table[segment + 4] + fract *
               (volume_to_amplitude_table[segment + 5] -
                volume_to_amplitude_table[segment + 4]);
}

static inline float
qdB_to_amplitude(float qdB)
{
    int i = lrintf(qdB - 0.5f);
    float f = qdB - (float)i;
    return qdB_to_amplitude_table[i + 4] + f *
           (qdB_to_amplitude_table[i + 5] -
            qdB_to_amplitude_table[i + 4]);
}

void blosc_place_step_dd(float *buffer, int index, float phase, float w, float scale){
    float r;
    int i;

    r = MINBLEP_PHASES * phase / w;
    i = lrintf(r - 0.5f);
    r -= (float)i;
    i &= MINBLEP_PHASE_MASK;  /* port changes can cause i to be out-of-range */
    /* This would be better than the above, but more expensive:
     *  while (i < 0) {
     *    i += MINBLEP_PHASES;
     *    index++;
     *  }
     */

    while (i < MINBLEP_PHASES * STEP_DD_PULSE_LENGTH) {
        buffer[index] += scale * (step_dd_table[i].value + r * step_dd_table[i].delta);
        i += MINBLEP_PHASES;
        index++;
    }
}


void vco(unsigned long sample_count, nekobee_voice_t *voice, struct blosc *osc,
                int index, float w)

{
      unsigned long sample;
      float pos = osc->pos;
      float pw, gain, halfgain, out;
      pw=0.46f;
      gain=1.0f;
      halfgain=gain*0.5f;
      int   bp_high = osc->bp_high;
      out=(bp_high ? halfgain : -halfgain);

            switch (osc->waveform)
            {
            default:
            case 0: {

                  for (sample = 0; sample < sample_count; sample++) {
            pos += w;
        if (bp_high) {
            if (pos >= pw) {
                blosc_place_step_dd(voice->osc_audio, index, pos - pw, w, -gain);
                bp_high = 0;
                out = -halfgain;
            }
            if (pos >= 1.0f) {
                pos -= 1.0f;
                blosc_place_step_dd(voice->osc_audio, index, pos, w, gain);
                bp_high = 1;
                out = halfgain;
            }
                  } else {
            if (pos >= 1.0f) {
                pos -= 1.0f;
                blosc_place_step_dd(voice->osc_audio, index, pos, w, gain);
                bp_high = 1;
                out = halfgain;
            }

            if (bp_high && pos >= pw) {
                blosc_place_step_dd(voice->osc_audio, index, pos - pw, w, -gain);
                bp_high = 0;
                out = -halfgain;
            }
        }
            
        voice->osc_audio[index + DD_SAMPLE_DELAY] += out;

        index++;
    }

    osc->pos = pos;
    osc->bp_high = bp_high;
            break;
        }
            case 1:                 // sawtooth wave 
            {
                    for (sample=0; sample < sample_count; sample++) {
              pos += w;
              if (pos >= 1.0f) {
                          pos -= 1.0f;
                         blosc_place_step_dd(voice->osc_audio, index, pos, w, gain);
                    }
              voice->osc_audio[index + DD_SAMPLE_DELAY] += gain * (0.5f - pos);

                    index++;
              }
              
              break;
        }
        
      }
      
        osc->pos=pos;
}

static inline void
vcf_4pole(nekobee_voice_t *voice, unsigned long sample_count,
          float *in, float *out, float *cutoff, float qres, float *amp)
{
    unsigned long sample;
    float freqcut, freqcut2, highpass,
          delay1 = voice->delay1,
          delay2 = voice->delay2,
          delay3 = voice->delay3,
          delay4 = voice->delay4;

    qres = 2.0f - qres * 1.995f;

    for (sample = 0; sample < sample_count; sample++) {

        /* Hal Chamberlin's state variable filter */

        freqcut = cutoff[sample] * 2.0f;
        freqcut2 = cutoff[sample] * 4.0f;


        if (freqcut > VCF_FREQ_MAX) freqcut = VCF_FREQ_MAX;
        if (freqcut2 > VCF_FREQ_MAX) freqcut2 = VCF_FREQ_MAX;

        delay2 = delay2 + freqcut * delay1;             /* delay2/4 = lowpass output */
        highpass = in[sample] - delay2 - qres * delay1;
        delay1 = freqcut * highpass + delay1;           /* delay1/3 = bandpass output */

        delay4 = delay4 + freqcut2 * delay3;
        highpass = delay2 - delay4 - qres * delay3;
        delay3 = freqcut2 * highpass + delay3;

        /* mix filter output into output buffer */
        out[sample] += 0.1*atan(3*delay4 * amp[sample]);
    }

    voice->delay1 = delay1;
    voice->delay2 = delay2;
    voice->delay3 = delay3;
    voice->delay4 = delay4;
    voice->c5 = 0.0f;
}


/*
 * nekobee_voice_render
 *
 * generate the actual sound data for this voice
 */
void
nekobee_voice_render(nekobee_synth_t *synth, nekobee_voice_t *voice,
                    LADSPA_Data *out, unsigned long sample_count,
                    int do_control_update)
{
    unsigned long sample;

    /* state variables saved in voice */

    float         lfo_pos    = voice->lfo_pos,
                  vca_eg        = voice->vca_eg,
                  vcf_eg        = voice->vcf_eg;
    unsigned char vca_eg_phase  = voice->vca_eg_phase,
                  vcf_eg_phase  = voice->vcf_eg_phase;
    int           osc_index  = voice->osc_index;
    /* temporary variables used in calculating voice */

    float fund_pitch;
    float deltat = synth->deltat;
    float freq, cutoff, vcf_amt;
      float vcf_acc_amt;

    /* set up synthesis variables from patch */
    float         omega;
    float         vca_eg_amp = qdB_to_amplitude(velocity_to_attenuation[voice->velocity] * 0);
                                             
    float         vca_eg_rate_level[3], vca_eg_one_rate[3];
    float         vcf_eg_amp = qdB_to_amplitude(velocity_to_attenuation[voice->velocity] * 0);
                                             
    float         vcf_eg_rate_level[3], vcf_eg_one_rate[3];
    float         qres = *(synth->resonance); 
    float         vol_out = volume(*(synth->volume));

      float velocity = (voice->velocity);
      
      float vcf_egdecay = *(synth->decay);
      
    fund_pitch = .1*voice->target_pitch  +0.9 * voice->prev_pitch;    /* glide */

    if (do_control_update) {
        voice->prev_pitch = fund_pitch; /* save pitch for next time */
    }

    fund_pitch *= 440;
    
    omega = *(synth->tuning) * fund_pitch;
    
      // if we have triggered ACCENT
      // we need a shorter decay
      // we should probably have something like this in the note on code
      // that could trigger an ACCENT light
      if (velocity>90) { 
            vcf_egdecay=.0005;
      }
      
      // VCA - In a real 303, it is set for around 2 seconds
    vca_eg_rate_level[0] = 0.1f * vca_eg_amp;   // instant on attack
    vca_eg_one_rate[0] = 0.9f;                              // very fast
    vca_eg_rate_level[1] = 0;                               // sustain is zero
    vca_eg_one_rate[1] = 1.0f - 0.00001f;       // decay time is very slow
    vca_eg_rate_level[2] = 0.0f;                      // decays to zero
    vca_eg_one_rate[2] =  0.975f;                           // very fast release
      
      // VCF - funny things go on with the accent
      
    vcf_eg_rate_level[0] = 0.1f * vcf_eg_amp;
    vcf_eg_one_rate[0] = 1-0.1f; //0.9f;
    vcf_eg_rate_level[1] = 0; // vcf_egdecay * *(synth->vcf_eg_sustain_level) * vcf_eg_amp;
    vcf_eg_one_rate[1] = 1.0f - vcf_egdecay;
    vcf_eg_rate_level[2] = 0.0f;
    vcf_eg_one_rate[2] = 0.9995f; // 1.0f - *(synth->vcf_eg_release_time);

    vca_eg_amp *= 0.99f;
    vcf_eg_amp *= 0.99f;

    freq = M_PI_F * deltat * fund_pitch * synth->mod_wheel;  /* now (0 to 1) * pi */

    cutoff = 0.008 * *(synth->cutoff);

      // 303 always has slight VCF mod
    vcf_amt = 0.05+(*(synth->envmod)*0.75);

   /* copy some things so oscillator functions can see them */
    voice->osc1.waveform = lrintf(*(synth->waveform));

      // work out how much the accent will affect the filter      
      vcf_acc_amt=.333+ (*(synth->resonance)/1.5);

    for (sample = 0; sample < sample_count; sample++) {
        vca_eg = vca_eg_rate_level[vca_eg_phase] + vca_eg_one_rate[vca_eg_phase] * vca_eg;
        vcf_eg = vcf_eg_rate_level[vcf_eg_phase] + vcf_eg_one_rate[vcf_eg_phase] * vcf_eg;

            
        voice->freqcut_buf[sample] = (cutoff + (vcf_amt * vcf_eg/2)  + (synth->vcf_accent * *(synth->accent)*0.5));

        voice->vca_buf[sample] = vca_eg * vol_out*(1 + *(synth->accent)*synth->vca_accent);

        if (!vca_eg_phase && vca_eg > vca_eg_amp) vca_eg_phase = 1;  /* flip from attack to decay */
        if (!vcf_eg_phase && vcf_eg > vcf_eg_amp) vcf_eg_phase = 1;  /* flip from attack to decay */
    }

    // oscillator

       vco(sample_count, voice, &voice->osc1, osc_index, deltat * omega);

    // VCF and VCA
      
    vcf_4pole(voice, sample_count, voice->osc_audio + osc_index, out, voice->freqcut_buf, qres, voice->vca_buf);
                          
    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 (vca_eg_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, " nekobee_voice_render check for dead: killing note id %d\n", voice->note_id);
            nekobee_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->vca_eg        = vca_eg;
    voice->vca_eg_phase  = vca_eg_phase;
    voice->vcf_eg        = vcf_eg;
    voice->vcf_eg_phase  = vcf_eg_phase;
    voice->osc_index  = osc_index;
}

Generated by  Doxygen 1.6.0   Back to index