Skip to content

File harmonic_osc.h

File List > DaisySP > Source > Synthesis > harmonic_osc.h

Go to the documentation of this file

Source Code

/*
Copyright (c) 2020 Electrosmith, Corp, Emilie Gillet

Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/

#pragma once
#ifndef DSY_HARMONIC_H
#define DSY_HARMONIC_H

#include <stdint.h>
#include "Utility/dsp.h"
#ifdef __cplusplus


namespace daisysp
{
template <int num_harmonics = 16>
class HarmonicOscillator
{
  public:
    HarmonicOscillator() {}
    ~HarmonicOscillator() {}

    void Init(float sample_rate)
    {
        sample_rate_ = sample_rate;
        phase_       = 0.0f;

        for(int i = 0; i < num_harmonics; ++i)
        {
            amplitude_[i]    = 0.0f;
            newamplitude_[i] = 0.f;
        }
        amplitude_[0]    = 1.f;
        newamplitude_[0] = 1.f;

        SetFirstHarmIdx(1);
        SetFreq(440.f);

        recalc_ = false;
    }

    float Process()
    {
        if(recalc_)
        {
            recalc_ = false;
            for(int i = 0; i < num_harmonics; ++i)
            {
                float f = frequency_
                          * static_cast<float>(first_harmonic_index_ + i);
                if(f >= 0.5f)
                {
                    f = 0.5f;
                }
                amplitude_[i] = newamplitude_[i] * (1.0f - f * 2.0f);
            }
        }

        phase_ += frequency_;
        if(phase_ >= 1.0f)
        {
            phase_ -= 1.0f;
        }
        const float two_x = 2.0f * sinf(phase_ * TWOPI_F);
        float       previous, current;
        if(first_harmonic_index_ == 1)
        {
            previous = 1.0f;
            current  = two_x * 0.5f;
        }
        else
        {
            const float k = first_harmonic_index_;
            previous      = sinf((phase_ * (k - 1.0f) + 0.25f) * TWOPI_F);
            current       = sinf((phase_ * k) * TWOPI_F);
        }

        float sum = 0.0f;
        for(int i = 0; i < num_harmonics; ++i)
        {
            sum += amplitude_[i] * current;
            float temp = current;
            current    = two_x * current - previous;
            previous   = temp;
        }

        return sum;
    }

    void SetFreq(float freq)
    {
        //convert from Hz to phase_inc / sample
        freq       = freq / sample_rate_;
        freq       = freq >= .5f ? .5f : freq;
        freq       = freq <= -.5f ? -.5f : freq;
        recalc_    = cmp(freq, frequency_) || recalc_;
        frequency_ = freq;
    }

    void SetFirstHarmIdx(int idx)
    {
        idx                   = idx < 1 ? 1 : idx;
        recalc_               = cmp(idx, first_harmonic_index_) || recalc_;
        first_harmonic_index_ = idx;
    }

    void SetAmplitudes(const float* amplitudes)
    {
        for(int i = 0; i < num_harmonics; i++)
        {
            recalc_          = cmp(newamplitude_[i], amplitudes[i]) || recalc_;
            newamplitude_[i] = amplitudes[i];
        }
    }

    void SetSingleAmp(const float amp, int idx)
    {
        if(idx < 0 || idx >= num_harmonics)
        {
            return;
        }
        recalc_            = cmp(amplitude_[idx], amp) || recalc_;
        newamplitude_[idx] = amp;
    }


  private:
    bool cmp(float a, float b) { return fabsf(a - b) > .000001f; }

    float sample_rate_;
    float phase_;
    float frequency_;
    float amplitude_[num_harmonics];
    float newamplitude_[num_harmonics];
    bool  recalc_;

    int first_harmonic_index_;
};
} // namespace daisysp
#endif
#endif