csdlsynthesis

Struggle to program an LFO


based on some tutorial code I've found I coded a little synthesizer with three oscilators and four different waveform. It works well and I want to add an LFO to module the sounds. Since I didn't coded everything on my own I'm a bit confused of how I could fit the LFO formula on my code. This is more or less what I tried in order to implement the LFO formula on a sinewave.(This formula is something like this: sinewaveFormula + 0.5 * Sinefreq * sin(2pi*1) * time)

double normalize(double phase)
{
  double cycles = phase/(2.0*pi);
  phase -= trunc(cycles) * 2.0 * pi;
  if (phase < 0) phase += 2.0*pi;
  return phase;
}

double sine(double phase)
 { phase = normalize(phase); return (sin(phase));}

static void build_sine_table(int16_t *data, int wave_length) {

    double phase_increment = (2.0f * pi) / (double)wave_length;
    double current_phase = 0;
    for(int i = 0; i < wave_length; i++) {
        int sample = synthOsc(current_phase, oscNum, selectedWave, selectedWave2, selectedWave3, intensity, intensity2, intensity3) + 0.5 * ((current_phase* wave_length) / (2*pi)) * sin(2*pi*(1.0)) * wave_length;
        data[i] = (int16_t)sample;
        current_phase += phase_increment;
    }
}

static void write_samples(int16_t *s_byteStream, long begin, long end, long length) {

    if(note > 0) {
        double d_sample_rate = sample_rate;
        double d_table_length = table_length;
        double d_note = note;

        // get correct phase increment for note depending on sample rate and table length.
        double phase_increment = (get_pitch(d_note) / d_sample_rate) * d_table_length;

        // loop through the buffer and write samples.
        for (int i = 0; i < length; i+=2) {
            phase_double += phase_increment;
            phase_int = (int)phase_double;
            if(phase_double >= table_length) {
                double diff = phase_double - table_length;
                phase_double = diff;
                phase_int = (int)diff;
            }

            if(phase_int < table_length && phase_int > -1) {
                if(s_byteStream != NULL) {
                    int16_t sample = sine_waveform_wave[phase_int];
                    target_amp = update_envelope();
                    if(smoothing_enabled) {
                        // move current amp towards target amp for a smoother transition.
                        if(current_amp < target_amp) {
                            current_amp += smoothing_amp_speed;
                            if(current_amp > target_amp) {
                                current_amp = target_amp;
                            }
                        } else if(current_amp > target_amp) {
                            current_amp -= smoothing_amp_speed;
                            if(current_amp < target_amp) {
                                current_amp = target_amp;
                            }
                        }
                    } else {
                        current_amp = target_amp;
                    }
                    sample *= current_amp; // scale volume.
                    s_byteStream[i+begin] = sample; // left channel
                    s_byteStream[i+begin+1] = sample; // right channel
                }
            }
        }
    }
}

The code compile but there's no LFO on the sine. I don't understand how I could make this formula work with this code.


Solution

  • It may help to get a basic understanding of how a LFO actually works. It is not that difficult - as an LFO is just another oscillator that is mixed to the waveform you want to modulate.

    I would suggest to remove your LFO formular from your call of synthOsc(), then you get a clean oscillator signal again. As a next step, create another oscillator signal for which you can use a very low frequency. Mix both signals together and you are done.

    Expresssed in simple math, it is like this:

    int the_sample_you_want_to_modulate = synthOsc1(...);
    int a_sample_with_very_low_frequency = synthOsc2(...);
    

    Mixing two waveforms is done through addition:

    int mixed_sample = the_sample_you_want_to_modulate + a_sample_with_very_low_frequency;
    

    The resulting sample will sweep now based on the frequency you have used for synthOsc2().

    As you can see, to implement an LFO you actually do not need a separate formular. You already have the formular when you know how to create an oscillator.

    Note that if you add two sine oscillators that have the exact same frequency, the resulting signal will just get louder. But when each has a different frequency, you will get a new waveform. For LFOs (which are in fact just ordinary oscillators - like in your build_sine_table() function) you typically set a very low frequency: 1 - 10 Hz is low enough to get an audible sweep. For higher frequencies you get chords as a result.