cleanup oscillators
This commit is contained in:
@@ -17,8 +17,8 @@ float Oscillator::frequency() {
|
||||
return frequency_;
|
||||
}
|
||||
|
||||
float Oscillator::process(uint8_t note, bool& scopeTrigger) {
|
||||
frequency_ = noteToFrequency(note);
|
||||
float Oscillator::process(uint8_t note, float detune, bool& scopeTrigger) {
|
||||
frequency_ = noteToFrequency(note, detune);
|
||||
return process(frequency_, scopeTrigger);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ float Oscillator::process(float frequency, bool& scopeTrigger) {
|
||||
return sampleOut;
|
||||
}
|
||||
|
||||
|
||||
inline float Oscillator::noteToFrequency(uint8_t note) {
|
||||
return SYNTH_PITCH_STANDARD * pow(2.0f, static_cast<float>(note - SYNTH_MIDI_HOME) / static_cast<float>(SYNTH_NOTES_PER_OCTAVE));
|
||||
inline float Oscillator::noteToFrequency(uint8_t note, float detune) {
|
||||
return SYNTH_PITCH_STANDARD * pow(2.0f, (static_cast<float>(note - SYNTH_MIDI_HOME) + detune) / static_cast<float>(SYNTH_NOTES_PER_OCTAVE));
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
#define SYNTH_TWELFTH_ROOT_TWO 1.05946309435929526463
|
||||
|
||||
// TODO: you get it, also in a yml config
|
||||
#define SYNTH_PITCH_STANDARD 440.0f // frequency of home pitch
|
||||
#define SYNTH_MIDI_HOME 69 // midi note index of home pitch
|
||||
@@ -27,14 +29,14 @@ public:
|
||||
void setSampleRate(float sampleRate);
|
||||
float frequency();
|
||||
|
||||
float process(uint8_t note, bool& scopeTrigger);
|
||||
float process(uint8_t note, float detune, bool& scopeTrigger); // detune (-1, 1)
|
||||
float process(float frequency, bool& scopeTrigger);
|
||||
|
||||
private:
|
||||
|
||||
float sampleRate_ = 44100.0f;
|
||||
|
||||
inline float noteToFrequency(uint8_t note);
|
||||
inline float noteToFrequency(uint8_t note, float detune);
|
||||
|
||||
// internal state tracking
|
||||
float phase_ = 0.0f;
|
||||
|
||||
@@ -87,12 +87,17 @@ float Voice::process(float* params, bool& scopeTrigger) {
|
||||
o.setWavetable(osc1Wave);
|
||||
}
|
||||
|
||||
// TODO: oscillator pitch offset
|
||||
// calculate the note/pitch of the oscillators
|
||||
bool temp = false;
|
||||
float osc1 = oscillators_[0].process(note_, scopeTrigger);
|
||||
float osc2 = oscillators_[1].process(static_cast<uint8_t>(note_+12), temp);
|
||||
float osc3 = oscillators_[2].process(static_cast<uint8_t>(note_+19), temp);
|
||||
uint8_t osc1NoteOffset = static_cast<uint8_t>((SYNTH_NOTES_PER_OCTAVE+1) * getParam(ParamId::Osc1OctaveOffset) + getParam(ParamId::Osc1SemitoneOffset));
|
||||
uint8_t osc2NoteOffset = static_cast<uint8_t>((SYNTH_NOTES_PER_OCTAVE+1) * getParam(ParamId::Osc2OctaveOffset) + getParam(ParamId::Osc2SemitoneOffset));
|
||||
uint8_t osc3NoteOffset = static_cast<uint8_t>((SYNTH_NOTES_PER_OCTAVE+1) * getParam(ParamId::Osc3OctaveOffset) + getParam(ParamId::Osc3SemitoneOffset));
|
||||
// sample oscillators
|
||||
float osc1 = oscillators_[0].process(osc1NoteOffset + note_, getParam(ParamId::Osc1PitchOffset)/100.0f, scopeTrigger);
|
||||
float osc2 = oscillators_[1].process(osc2NoteOffset + note_, getParam(ParamId::Osc2PitchOffset)/100.0f, temp);
|
||||
float osc3 = oscillators_[2].process(osc3NoteOffset + note_, getParam(ParamId::Osc3PitchOffset)/100.0f, temp);
|
||||
|
||||
// mix oscillators
|
||||
float sampleOut = (osc1 + osc2*0.5f + osc3*0.25f) * gain;
|
||||
|
||||
// filter sample
|
||||
|
||||
70
src/synth/WavetableController.cpp
Normal file
70
src/synth/WavetableController.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
|
||||
#include "WavetableController.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
WavetableController::WavetableController() {
|
||||
// load from files
|
||||
|
||||
init();
|
||||
|
||||
std::cout << "wavetable init" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
void WavetableController::init() {
|
||||
|
||||
wavetables_.resize(4); // resize for however many files we find
|
||||
|
||||
float phase = 0.0f;
|
||||
float phaseInc = 2.0f * M_PI / static_cast<float>(SYNTH_WAVETABLE_SIZE);
|
||||
|
||||
for(int i = 0; i < SYNTH_WAVETABLE_SIZE; i++) {
|
||||
|
||||
wavetables_[0][i] = std::sin(phase) / 0.707f; // sine
|
||||
wavetables_[1][i] = (phase >= M_PI) ? 1.0f : -1.0f; // square
|
||||
wavetables_[2][i] = ((phase / M_PI) - 1.0f) / 0.577f; // saw
|
||||
|
||||
// triangle
|
||||
float tri = 0.0f;
|
||||
if(phase <= M_PI/2.0f) {
|
||||
tri = phase * 2.0f/M_PI;
|
||||
} else if(phase <= 3.0f*M_PI/2.0f) {
|
||||
tri = phase * -2.0f/M_PI + 2.0f;
|
||||
} else {
|
||||
tri = phase * 2.0f/M_PI - 4.0f;
|
||||
}
|
||||
wavetables_[3][i] = tri / 0.577f;
|
||||
|
||||
phase += phaseInc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
float WavetableController::sample(uint8_t wavetableIndex, float phase) {
|
||||
|
||||
float sampleValue = 0.0f;
|
||||
|
||||
if(phase >= 0.0f) {
|
||||
while(phase >= 2.0f*M_PI) {
|
||||
phase -= 2.0f*M_PI;
|
||||
}
|
||||
} else {
|
||||
while(phase <= 0.0f*M_PI) {
|
||||
phase += 2.0f*M_PI;
|
||||
}
|
||||
}
|
||||
|
||||
float scaledPhase = phase * static_cast<float>(SYNTH_WAVETABLE_SIZE) / (2.0f*M_PI);
|
||||
uint32_t index = static_cast<uint32_t>(round(scaledPhase));
|
||||
if(index >= SYNTH_WAVETABLE_SIZE) index = SYNTH_WAVETABLE_SIZE - 1;
|
||||
|
||||
if(wavetableIndex >= 4) {
|
||||
wavetableIndex = 3;
|
||||
} else if(wavetableIndex < 0) {
|
||||
wavetableIndex = 0;
|
||||
}
|
||||
|
||||
return wavetables_[wavetableIndex][index];
|
||||
}
|
||||
34
src/synth/WavetableController.h
Normal file
34
src/synth/WavetableController.h
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#define SYNTH_WAVETABLE_SIZE 2048
|
||||
#ifndef M_PI // I hate my stupid chungus life
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
typedef std::array<float, SYNTH_WAVETABLE_SIZE> Wavetable;
|
||||
|
||||
class WavetableController {
|
||||
|
||||
private:
|
||||
/* data */
|
||||
public:
|
||||
|
||||
WavetableController();
|
||||
~WavetableController() = default;
|
||||
|
||||
void init();
|
||||
|
||||
// phase = [0, 2pi)
|
||||
float sample(uint8_t wavetableIndex, float phase);
|
||||
|
||||
private:
|
||||
|
||||
std::vector<Wavetable> wavetables_;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user