From b834f3168a07710f62e89cb8216b179d2aface31 Mon Sep 17 00:00:00 2001 From: Christian Colglazier Date: Sat, 6 Nov 2021 16:48:17 -0400 Subject: [PATCH] Added basic LFO --- src/droplets/lfo_droplet.cpp | 108 +++++++++++++++++++++++++++++++++++ src/droplets/lfo_droplet.h | 82 ++++++++++++++++++++++++++ src/droplets/vco_droplet.cpp | 22 ------- src/droplets/vco_droplet.h | 8 --- src/main.cpp | 4 ++ src/main.h | 1 + src/menu.cpp | 1 + src/menu_item.h | 2 +- src/util.cpp | 22 +++++++ src/util.h | 10 ++++ 10 files changed, 229 insertions(+), 31 deletions(-) create mode 100644 src/droplets/lfo_droplet.cpp create mode 100644 src/droplets/lfo_droplet.h diff --git a/src/droplets/lfo_droplet.cpp b/src/droplets/lfo_droplet.cpp new file mode 100644 index 0000000..c3c99f3 --- /dev/null +++ b/src/droplets/lfo_droplet.cpp @@ -0,0 +1,108 @@ +#include "lfo_droplet.h" + +void LFO::Init(DaisyPatch* m_patch, + float samplerate, + AnalogControl freqKnob, + AnalogControl ampKnob) { + patch = m_patch; + osc.Init(samplerate); + osc.SetAmp(1); + wave = 0; + freqCtrl.Init(freqKnob, 0.1f, 35.0f, Parameter::LOGARITHMIC); + ampCtrl.Init(ampKnob, 0.0f, 1.0f, Parameter::LINEAR); + } + +void LFO::Process(DacHandle::Channel chn) { + osc.SetFreq(freqCtrl.Process()); + osc.SetWaveform(wave); + + patch->seed.dac.WriteValue( + chn, + uint16_t((osc.Process() + 1.f) * + .5f * ampCtrl.Process() * 4095.f)); +} + +void LFO::UpdateWave(int change) { + wave = (MAX_WAVE + wave + change) % MAX_WAVE; +} + +uint8_t LFO::GetWave() { + return wave; +} + +LFODroplet::LFODroplet(DaisyPatch* m_patch, + DropletState m_state, + float sample_rate) : + Droplet(m_patch, + m_state) { + switch (GetState()) { + default: + case DropletState::kFull: + lfo[0].Init(Patch(), + sample_rate, + Patch()->controls[Patch()->CTRL_1], + Patch()->controls[Patch()->CTRL_2]); + lfo[1].Init(Patch(), + sample_rate, + Patch()->controls[Patch()->CTRL_3], + Patch()->controls[Patch()->CTRL_4]); + break; + case DropletState::kLeft: + lfo[0].Init(Patch(), + sample_rate, + Patch()->controls[Patch()->CTRL_1], + Patch()->controls[Patch()->CTRL_2]); + break; + case DropletState::kRight: + lfo[0].Init(Patch(), + sample_rate, + Patch()->controls[Patch()->CTRL_3], + Patch()->controls[Patch()->CTRL_4]); + break; + } +} + +LFODroplet::~LFODroplet() {} + +void LFODroplet::Control() { + Patch()->ProcessAnalogControls(); + Patch()->encoder.Debounce(); + lfo[0].UpdateWave(Patch()->encoder.Increment()); + if (GetState() == DropletState::kFull) { + lfo[1].UpdateWave(Patch()->encoder.Increment()); + } +} + +void LFODroplet::Process(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size) { + Patch()->ProcessAnalogControls(); + + for(size_t i = 0; i < size; i++) { + if (GetState() == DropletState::kRight) { + lfo[0].Process(DacHandle::Channel::TWO); + } else { + lfo[0].Process(DacHandle::Channel::ONE); + } + if (GetState() == DropletState::kFull) { + lfo[1].Process(DacHandle::Channel::TWO); + } + } +} + +void LFODroplet::Draw() { + if (GetState() == DropletState::kFull) { + WriteCenteredString(Patch(), + (GetScreenMax()-GetScreenMin())/2, + 54, + Font_6x8, + WaveToString(lfo[0].GetWave())); + } else { + WriteDoubleCentered(Patch(), + GetScreenMin() + + (GetScreenMax()-GetScreenMin())/2, + 54, + GetScreenMax()-GetScreenMin(), + Font_6x8, + WaveToString(lfo[0].GetWave())); + } + DrawName("LFO"); +} diff --git a/src/droplets/lfo_droplet.h b/src/droplets/lfo_droplet.h new file mode 100644 index 0000000..48eb286 --- /dev/null +++ b/src/droplets/lfo_droplet.h @@ -0,0 +1,82 @@ +#pragma once + +#ifndef CASCADE_DROPLETS_LFO_DROPLET_H_ +#define CASCADE_DROPLETS_LFO_DROPLET_H_ + +#include "daisysp.h" +#include "daisy_patch.h" + +#include "droplet.h" +#include "../util.h" + +using namespace daisy; +using namespace daisysp; + +#define MAX_WAVE Oscillator::WAVE_POLYBLEP_TRI + +class LFO { +private: + Oscillator osc; + Parameter freqCtrl; + Parameter ampCtrl; + float amp; + float freq; + uint8_t wave; + float value; + DaisyPatch* patch; +public: + void Init(DaisyPatch* m_patch, + float samplerate, + AnalogControl freqKnob, + AnalogControl ampKnob); + + void Process(DacHandle::Channel chn); + + void UpdateWave(int change); + + uint8_t GetWave(); +}; + +class LFODroplet: public Droplet { +private: + LFO lfo[2]; + +public: + /* + * Constructor for a LFO droplet. + * + * @param m_patch pointer to patch + * @param m_state droplet position + */ + LFODroplet(DaisyPatch* m_patch, + DropletState m_state, + float sample_rate); + + /* + * Destructor for vco droplet. + */ + ~LFODroplet(); + + /* + * Processes user controls and inputs. + */ + void Control(); + + /* + * Processes audio input and outputs. + * + * @param in the audio inputs for the patch + * @param out the audio outputs for the patch + * @param size the number of inputs and outputs + */ + void Process(AudioHandle::InputBuffer in, + AudioHandle::OutputBuffer out, + size_t size); + + /* + * Processes information to be shown on the display. + */ + void Draw(); +}; + +#endif // CASCADE_DROPLETS_LFO_DROPLET_H_ diff --git a/src/droplets/vco_droplet.cpp b/src/droplets/vco_droplet.cpp index 5418fb3..68489ba 100644 --- a/src/droplets/vco_droplet.cpp +++ b/src/droplets/vco_droplet.cpp @@ -105,28 +105,6 @@ void VCODroplet::Draw() { AnimationInc(); } -std::string VCODroplet::WaveToString(uint8_t wf) { - switch(wf){ - case Oscillator::WAVE_TRI: - return "Triangle"; - case Oscillator::WAVE_SQUARE: - return "Square"; - case Oscillator::WAVE_SIN: - return "Sine"; - case Oscillator::WAVE_SAW: - return "Saw"; - case Oscillator::WAVE_RAMP: - return "Ramp"; - case Oscillator::WAVE_POLYBLEP_TRI: - return "PolyBLEP Triangle"; - case Oscillator::WAVE_POLYBLEP_SQUARE: - return "PolyBLEP Square"; - case Oscillator::WAVE_POLYBLEP_SAW: - return "PolyBLEP Saw"; - } - return ""; -} - void VCODroplet::SetWaveState(uint8_t wf) { switch(wf){ case Oscillator::WAVE_TRI: diff --git a/src/droplets/vco_droplet.h b/src/droplets/vco_droplet.h index 921d4fe..77265d3 100644 --- a/src/droplets/vco_droplet.h +++ b/src/droplets/vco_droplet.h @@ -24,14 +24,6 @@ private: const double pi = std::acos(-1); Wave* wave_graphic = new Wave(WaveShape::kTriangle, 21, GetTitleHeight()); - /* - * Converts oscilator to name of wave shape. - * - * @param wf wave shape - * @return name of wave shap - */ - std::string WaveToString(uint8_t wf); - /* * Sets the vco wave shap to display on screen. * diff --git a/src/main.cpp b/src/main.cpp index 3dba468..4aefe8e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -115,6 +115,10 @@ static void AudioThrough(AudioHandle::InputBuffer in, Droplet* GetDroplet(DropletState state) { switch(selected_menu->GetState()) { default: + case MenuState::kLFO: + return new LFODroplet(&patch, + state, + sample_rate); case MenuState::kMixer: return new MixerDroplet(&patch, state); diff --git a/src/main.h b/src/main.h index e938339..39bbef4 100644 --- a/src/main.h +++ b/src/main.h @@ -12,6 +12,7 @@ #include "menu.h" #include "droplets/droplet.h" #include "droplets/droplet_manager.h" +#include "droplets/lfo_droplet.h" #include "droplets/mixer_droplet.h" #include "droplets/noise_droplet.h" #include "droplets/vca_droplet.h" diff --git a/src/menu.cpp b/src/menu.cpp index d7c3adc..b6728d6 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -9,6 +9,7 @@ Menu::Menu(DaisyPatch* m_patch, head = new MenuItem(MenuState::kSplit, "Split"); head->AddItemEnd(new MenuItem(MenuState::kChange, "")); + head->AddItemEnd(new MenuItem(MenuState::kLFO, "LFO")); head->AddItemEnd(new MenuItem(MenuState::kMixer, "Mixer")); head->AddItemEnd(new MenuItem(MenuState::kNoise, "Noise")); head->AddItemEnd(new MenuItem(MenuState::kVCA, "VCA")); diff --git a/src/menu_item.h b/src/menu_item.h index 72adbc8..2e29c60 100644 --- a/src/menu_item.h +++ b/src/menu_item.h @@ -5,7 +5,7 @@ #include -enum class MenuState {kSplit, kChange, kMixer, kNoise, kVCA, kVCO}; +enum class MenuState {kSplit, kChange, kLFO, kMixer, kNoise, kVCA, kVCO}; class MenuItem { private: diff --git a/src/util.cpp b/src/util.cpp index c08d188..e4f0c8f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -93,3 +93,25 @@ void WriteDoubleCentered(DaisyPatch* patch, std::string text) { WriteDoubleCentered(patch, x, y, width, font, text, true); } + +std::string WaveToString(uint8_t wf) { + switch(wf){ + case Oscillator::WAVE_TRI: + return "Triangle"; + case Oscillator::WAVE_SQUARE: + return "Square"; + case Oscillator::WAVE_SIN: + return "Sine"; + case Oscillator::WAVE_SAW: + return "Saw"; + case Oscillator::WAVE_RAMP: + return "Ramp"; + case Oscillator::WAVE_POLYBLEP_TRI: + return "PolyBLEP Triangle"; + case Oscillator::WAVE_POLYBLEP_SQUARE: + return "PolyBLEP Square"; + case Oscillator::WAVE_POLYBLEP_SAW: + return "PolyBLEP Saw"; + } + return ""; +} diff --git a/src/util.h b/src/util.h index fda2a6d..dafdfd3 100644 --- a/src/util.h +++ b/src/util.h @@ -3,11 +3,13 @@ #ifndef CASCADE_UTIL_H_ #define CASCADE_UTIL_H_ +#include "daisysp.h" #include "daisy_patch.h" #include using namespace daisy; +using namespace daisysp; #define SSD1309_WIDTH 128 @@ -128,4 +130,12 @@ void WriteDoubleCentered(DaisyPatch* patch, FontDef font, std::string text); +/* + * Converts oscilator to name of wave shape. + * + * @param wf wave shape + * @return name of wave shap + */ +std::string WaveToString(uint8_t wf); + #endif // CASCADE_UTIL_H_