diff --git a/config/audio.yaml b/config/audio.yaml index 947f59f..ed94762 100644 --- a/config/audio.yaml +++ b/config/audio.yaml @@ -3,7 +3,7 @@ # Configures properties for the RtAudio engine # Number of samples per second -sampleRate: 96000 +sampleRate: 44100 # unconfigurable: sampleFormat; [-1, 1] float # number of audio channels diff --git a/config/profiles/default.yaml b/config/profiles/default.yaml index 7fa4d50..34e4e6f 100644 --- a/config/profiles/default.yaml +++ b/config/profiles/default.yaml @@ -4,7 +4,7 @@ # sequences in the form [x, x, x] denote [setValue, sliderMinimum, sliderMaximum] -version: 0x0002 +version: 0x0003 # deprecated, useless Osc1Freq: [100, 20, 600] @@ -13,16 +13,19 @@ Osc1Freq: [100, 20, 600] OscWaveSelector1: 2 OscWaveSelector2: 1 -# Oscillator frequency parameters +# Frequency parameters +MasterOctaveOffset: [0, -5, 5] +MasterSemitoneOffset: [0, -12, 12] +MasterPitchOffset: [0, -100, 100] Osc1OctaveOffset: [0, -5, 5] Osc1SemitoneOffset: [0, -12, 12] -Osc1PitchOffset: [0, -100, 100] +Osc1PitchOffset: [1.34, -100, 100] Osc2OctaveOffset: [1, -5, 5] Osc2SemitoneOffset: [0, -12, 12] -Osc2PitchOffset: [0, -100, 100] +Osc2PitchOffset: [12.86, -100, 100] Osc3OctaveOffset: [1, -5, 5] Osc3SemitoneOffset: [7, -12, 12] -Osc3PitchOffset: [1.96, -100, 100] +Osc3PitchOffset: [-8.79, -100, 100] # Envelope generator parameters Osc1Volume: diff --git a/src/ConfigInterface.cpp b/src/ConfigInterface.cpp index 058ae0b..956803a 100644 --- a/src/ConfigInterface.cpp +++ b/src/ConfigInterface.cpp @@ -66,7 +66,7 @@ void ConfigInterface::loadProfile(std::string filename) { std::cout << "Parameter profile version " << version << "is outdated below the compatible version " << CONFIG_VERSION << std::endl; return; } else { - std::cout << version << std::endl; + std::cout << "Parameter profile version " << version << std::endl; } // extract values from the config file @@ -74,6 +74,27 @@ void ConfigInterface::loadProfile(std::string filename) { std::array fCutoffProfile = loadEnvProfile(&config, "FilterCutoff"); std::array fResonanceProfile = loadEnvProfile(&config, "FilterResonance"); + std::array masterPitchOffsets = {{ + { config["MasterOctaveOffset"][0].as(), config["MasterOctaveOffset"][1].as(), config["MasterOctaveOffset"][2].as() }, + { config["MasterSemitoneOffset"][0].as(), config["MasterSemitoneOffset"][1].as(), config["MasterSemitoneOffset"][2].as() }, + { config["MasterPitchOffset"][0].as(), config["MasterPitchOffset"][1].as(), config["MasterPitchOffset"][2].as() }, + }}; + std::array osc1PitchOffsets = {{ + { config["Osc1OctaveOffset"][0].as(), config["Osc1OctaveOffset"][1].as(), config["Osc1OctaveOffset"][2].as() }, + { config["Osc1SemitoneOffset"][0].as(), config["Osc1SemitoneOffset"][1].as(), config["Osc1SemitoneOffset"][2].as() }, + { config["Osc1PitchOffset"][0].as(), config["Osc1PitchOffset"][1].as(), config["Osc1PitchOffset"][2].as() }, + }}; + std::array osc2PitchOffsets = {{ + { config["Osc2OctaveOffset"][0].as(), config["Osc2OctaveOffset"][1].as(), config["Osc2OctaveOffset"][2].as() }, + { config["Osc2SemitoneOffset"][0].as(), config["Osc2SemitoneOffset"][1].as(), config["Osc2SemitoneOffset"][2].as() }, + { config["Osc2PitchOffset"][0].as(), config["Osc2PitchOffset"][1].as(), config["Osc2PitchOffset"][2].as() }, + }}; + std::array osc3PitchOffsets = {{ + { config["Osc3OctaveOffset"][0].as(), config["Osc3OctaveOffset"][1].as(), config["Osc3OctaveOffset"][2].as() }, + { config["Osc3SemitoneOffset"][0].as(), config["Osc3SemitoneOffset"][1].as(), config["Osc3SemitoneOffset"][2].as() }, + { config["Osc3PitchOffset"][0].as(), config["Osc3PitchOffset"][1].as(), config["Osc3PitchOffset"][2].as() }, + }}; + // TODO: remove this once all the parameters are set properly params_->resetToDefaults(); @@ -81,8 +102,19 @@ void ConfigInterface::loadProfile(std::string filename) { params_->set(EnvelopeId::Osc1Volume, osc1VolumeProfile[0].def, osc1VolumeProfile[1].def, osc1VolumeProfile[2].def, osc1VolumeProfile[3].def, osc1VolumeProfile[4].def); params_->set(EnvelopeId::FilterCutoff, fCutoffProfile[0].def, fCutoffProfile[1].def, fCutoffProfile[2].def, fCutoffProfile[3].def, fCutoffProfile[4].def); params_->set(EnvelopeId::FilterResonance, fResonanceProfile[0].def, fResonanceProfile[1].def, fResonanceProfile[2].def, fResonanceProfile[3].def, fResonanceProfile[4].def); - // TODO: why do I bother passing in 5 values independently when I can just do an array ? - // VVV look down there its so easy + // TODO: why do I bother passing in 5 values independently when I can just do an array like in loadEnvProfile ? + params_->set(ParamId::MasterOctaveOffset, masterPitchOffsets[0].def); + params_->set(ParamId::MasterSemitoneOffset, masterPitchOffsets[1].def); + params_->set(ParamId::MasterPitchOffset, masterPitchOffsets[2].def); + params_->set(ParamId::Osc1OctaveOffset, osc1PitchOffsets[0].def); + params_->set(ParamId::Osc1SemitoneOffset, osc1PitchOffsets[1].def); + params_->set(ParamId::Osc1PitchOffset, osc1PitchOffsets[2].def); + params_->set(ParamId::Osc2OctaveOffset, osc2PitchOffsets[0].def); + params_->set(ParamId::Osc2SemitoneOffset, osc2PitchOffsets[1].def); + params_->set(ParamId::Osc2PitchOffset, osc2PitchOffsets[2].def); + params_->set(ParamId::Osc3OctaveOffset, osc3PitchOffsets[0].def); + params_->set(ParamId::Osc3SemitoneOffset, osc3PitchOffsets[1].def); + params_->set(ParamId::Osc3PitchOffset, osc3PitchOffsets[2].def); // TODO: // load wavetable settings diff --git a/src/ParameterStore.h b/src/ParameterStore.h index 2bbdff9..25b203a 100644 --- a/src/ParameterStore.h +++ b/src/ParameterStore.h @@ -9,6 +9,9 @@ enum class ParamId : uint16_t { Osc1Frequency, Osc1WaveSelector1, Osc1WaveSelector2, + MasterOctaveOffset, + MasterSemitoneOffset, + MasterPitchOffset, Osc1OctaveOffset, Osc1SemitoneOffset, Osc1PitchOffset, @@ -73,6 +76,9 @@ constexpr std::array(ParamId::Count)> PARAM_DE { 100.0f, 20.0f, 600.0f}, // Osc1Freq { 2.0f, 0.0f, 0.0f}, // OscWaveSelector1 { 1.0f, 0.0f, 0.0f}, // OscWaveSelector2 + { 0.0f, -5.0f, 5.0f}, // MasterOctaveOffset + { 0.0f, -12.0f, 12.0f}, // MasterSemitoneOffset + { 0.0f, -100.0f, 100.0f}, // MasterPitchOffset { 0.0f, -5.0f, 5.0f}, // Osc1OctaveOffset { 0.0f, -12.0f, 12.0f}, // Osc1SemitoneOffset { 0.0f, -100.0f, 100.0f}, // Osc1PitchOffset diff --git a/src/synth/Voice.cpp b/src/synth/Voice.cpp index c7a03a1..0f40d3b 100644 --- a/src/synth/Voice.cpp +++ b/src/synth/Voice.cpp @@ -89,14 +89,14 @@ float Voice::process(float* params, bool& scopeTrigger) { // calculate the note/pitch of the oscillators bool temp = false; - uint8_t osc1NoteOffset = static_cast((SYNTH_NOTES_PER_OCTAVE+1) * getParam(ParamId::Osc1OctaveOffset) + getParam(ParamId::Osc1SemitoneOffset)); - uint8_t osc2NoteOffset = static_cast((SYNTH_NOTES_PER_OCTAVE+1) * getParam(ParamId::Osc2OctaveOffset) + getParam(ParamId::Osc2SemitoneOffset)); - uint8_t osc3NoteOffset = static_cast((SYNTH_NOTES_PER_OCTAVE+1) * getParam(ParamId::Osc3OctaveOffset) + getParam(ParamId::Osc3SemitoneOffset)); + uint8_t osc1NoteOffset = static_cast(std::round((SYNTH_NOTES_PER_OCTAVE) * (getParam(ParamId::Osc1OctaveOffset) + getParam(ParamId::MasterOctaveOffset)) + getParam(ParamId::Osc1SemitoneOffset) + getParam(ParamId::MasterSemitoneOffset))); + uint8_t osc2NoteOffset = static_cast(std::round((SYNTH_NOTES_PER_OCTAVE) * (getParam(ParamId::Osc2OctaveOffset) + getParam(ParamId::MasterOctaveOffset)) + getParam(ParamId::Osc2SemitoneOffset) + getParam(ParamId::MasterSemitoneOffset))); + uint8_t osc3NoteOffset = static_cast(std::round((SYNTH_NOTES_PER_OCTAVE) * (getParam(ParamId::Osc3OctaveOffset) + getParam(ParamId::MasterOctaveOffset)) + getParam(ParamId::Osc3SemitoneOffset) + getParam(ParamId::MasterSemitoneOffset))); // 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); - + float osc1 = oscillators_[0].process(osc1NoteOffset + note_, (getParam(ParamId::Osc1PitchOffset) + getParam(ParamId::MasterPitchOffset))/100.0f, scopeTrigger); + float osc2 = oscillators_[1].process(osc2NoteOffset + note_, (getParam(ParamId::Osc2PitchOffset) + getParam(ParamId::MasterPitchOffset))/100.0f, temp); + float osc3 = oscillators_[2].process(osc3NoteOffset + note_, (getParam(ParamId::Osc3PitchOffset) + getParam(ParamId::MasterPitchOffset))/100.0f, temp); + // mix oscillators float sampleOut = (osc1 + osc2*0.25f + osc3*0.125f) * gain;