Skip to main content

NTS-1 Harmonik Oscillator

Since I already explored subharmonic sound generation related to the undertone series and in the process programmed the Subharmonikorg, a subharmonic oscillator for the nts-1, it was time to look into the overtone series. Also known as the harmonic series, the overtone series is generated by multiplying the fundamental frequency by a series of integer numbers in order to create harmonic relationships from the root note. Looking into the eurorack synth world, I came across the Verbos Electronics Harmonic Oscillator which has an architecture of one main oscillator and seven harmonic oscillators. Each harmonic oscillator multiplies the main frequency by its ordered number and has faders that allow the user to set the amplitude of the waves that are going to be added to the fundamental. The Verbos has more features but I decided to adopt the basic architecture limited to only four harmonics and include a wavefolder.

I made a post discussing the principles of creating complex wave forms from adding harmonic sine waves to the fundamental frequency wave. It is explained step by step in a python jupyter notebook. Based on those principles, we will be able to compose several harmonic related sine waves and combine them to get interesting timbres.

The Harmonik Oscillator

 

 

As mentioned before, the Harmonik oscillator will have one main and four harmonic sine wave oscillators. All of them with level controls for mixing the desired amplitudes in the final wave output. Lets start with the definition of the Oscillator:

typedef enum {
    harmonic = 0,
    odd = 1,
    even = 2,
    octaves = 3
} Spread;

typedef struct Oscillator {
    float phases[5] = { 0 };
    float gains[5]= { 1.f, 0.f, 0.f, 0.f, 0.f };
    float drive;
    float offset;
    Spread spread;
} Oscillator;

The Oscillator struct contains all the data related to the oscillator as a whole. Phase and gain information for the main and harmonic sine waves get processed with each cycle. The indexes of the arrays correspond to each harmonic order with zero being the fundamental, and the rest being the harmonic relationships.

The harmonic relationships with the main frequency can be set in four different Spread types:

  • Harmonic: each sine wave's frequency gets multiplied by its corresponding harmonic integer.
  • Odd: each sine wave gets spread apart in odd partials (main frequency multiplied by 3, 5, 7, 9).
  • Even: each sine wave gets spread apart in even partials (main frequency multiplied by 2, 4, 6, 8).
  • Octaves: each sine wave gets spread by the geometric progression, producing octaves.

The way we achieve this is with the simple function shown below:

int partial(int index) {
    int s;
    switch (spread) {
        case harmonic:
          s = index + 1;
          break;
        case odd:
          s = 2 * (index + 1) - 1;
          break;
        case even:
          s = (index == 0) ? 1 : 2 * index;
          break;
        case octaves:
          s = 1 << index;
          break;
    }
    return s;
}

In order to complete the additive synthesis combo, I decided to look into implementing a wavefolder. After many attempts without satisfying results, I searched for a solution and found an implementation done in C but for VCV rack, you can check it out in squinkylabs github repo. It doesn't fold the waves the exact way as you would see in VCV rack, but it folds them alright. Even though I am coding for the nts-1, I didn't change a thing from the original implementation:

float fold(float x) {
    float fold;
    const float bias = (x < 0) ? -1.f : 1.f;
    int phase = int((x + bias) / 2.f);
    bool isEven = !(phase & 1);
    if (isEven) {
        fold = x - 2.f * phase;
    } else {
        fold = -x + 2.f * phase;
    }
    return fold;
}

The Harmonik code can be found here and the nts-1 oscillator can be downloaded from here. A summary of features and parameters is described below:

Features

  • Six sine wave oscillators with amplitude control.
  • A wave folder that folds each sine wave before summing the result to the final signal.
  • Spread control for the partials so that we have harmonic, odd or even partial overtones.
  • Offset control for asymmetric wavefolding.
  • The wave folder can be modulated with the Shape LFO.

Parameters

Parameter Range Description
SHAPE 0 to 100 Wave folder control
ALT 0 to 100 Offset of signals for asymmetric wavefolding
P1 0 to 100 The first partial frequency amplitude
P2 0 to 100 The second partial frequency amplitude
P3 0 to 100 The third partial frequency amplitude
P4 0 to 100 The fourth partial frequency amplitude
P5 0 to 100 The fifth partial frequency amplitude
SPRD 0 to 3 The spread controller 0: harmonic, 1: odd, 2: even, 3: octaves