From be8965f9bcdbf302acf353408cff1ac1581e0c02 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sat, 27 Apr 2024 12:36:53 -0700 Subject: [PATCH 01/23] gitignore --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 8fc783475..89df1bfaf 100644 --- a/.gitignore +++ b/.gitignore @@ -131,3 +131,9 @@ MyProjects/ .cortex-debug.registers.state.json .cortex-debug.peripherals.state.json + + +**/build +**/.vscode + + From 939a6e7d6ca0564f3ee2984597685cb604605d72 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sat, 27 Apr 2024 12:49:11 -0700 Subject: [PATCH 02/23] Add current hachikit code --- optic/HachiKit/Bd8.cpp | 103 +++++++++++++ optic/HachiKit/Bd8.h | 74 +++++++++ optic/HachiKit/Blank.cpp | 71 +++++++++ optic/HachiKit/Blank.h | 69 +++++++++ optic/HachiKit/Ch.cpp | 87 +++++++++++ optic/HachiKit/Ch.h | 71 +++++++++ optic/HachiKit/FmDrum.cpp | 99 ++++++++++++ optic/HachiKit/FmDrum.h | 73 +++++++++ optic/HachiKit/HachiKit.cpp | 273 ++++++++++++++++++++++++++++++++++ optic/HachiKit/HhSource68.cpp | 60 ++++++++ optic/HachiKit/HhSource68.h | 54 +++++++ optic/HachiKit/IDrum.h | 36 +++++ optic/HachiKit/ISource.h | 24 +++ optic/HachiKit/Makefile | 24 +++ optic/HachiKit/README.md | 20 +++ optic/HachiKit/Screen.cpp | 46 ++++++ optic/HachiKit/Screen.h | 36 +++++ optic/HachiKit/Sd8.cpp | 111 ++++++++++++++ optic/HachiKit/Sd8.h | 75 ++++++++++ optic/HachiKit/SdNoise.cpp | 73 +++++++++ optic/HachiKit/SdNoise.h | 70 +++++++++ optic/HachiKit/Utility.h | 96 ++++++++++++ 22 files changed, 1645 insertions(+) create mode 100644 optic/HachiKit/Bd8.cpp create mode 100644 optic/HachiKit/Bd8.h create mode 100644 optic/HachiKit/Blank.cpp create mode 100644 optic/HachiKit/Blank.h create mode 100644 optic/HachiKit/Ch.cpp create mode 100644 optic/HachiKit/Ch.h create mode 100644 optic/HachiKit/FmDrum.cpp create mode 100644 optic/HachiKit/FmDrum.h create mode 100644 optic/HachiKit/HachiKit.cpp create mode 100644 optic/HachiKit/HhSource68.cpp create mode 100644 optic/HachiKit/HhSource68.h create mode 100644 optic/HachiKit/IDrum.h create mode 100644 optic/HachiKit/ISource.h create mode 100644 optic/HachiKit/Makefile create mode 100644 optic/HachiKit/README.md create mode 100644 optic/HachiKit/Screen.cpp create mode 100644 optic/HachiKit/Screen.h create mode 100644 optic/HachiKit/Sd8.cpp create mode 100644 optic/HachiKit/Sd8.h create mode 100644 optic/HachiKit/SdNoise.cpp create mode 100644 optic/HachiKit/SdNoise.h create mode 100644 optic/HachiKit/Utility.h diff --git a/optic/HachiKit/Bd8.cpp b/optic/HachiKit/Bd8.cpp new file mode 100644 index 000000000..5afc599ef --- /dev/null +++ b/optic/HachiKit/Bd8.cpp @@ -0,0 +1,103 @@ +#include "Bd8.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +void Bd8::Init(float sample_rate) { + Init(sample_rate, 60, 0.001, 0.5, 0.001, 0.2, 0.1); +} + +void Bd8::Init(float sample_rate, float frequency, float ampAttack, float ampDecay, float pitchAttack, float pitchDecay, float modAmount) { + + // oscillator settings + osc.Init(sample_rate); + SetParam(PARAM_FREQUENCY, frequency, false); + osc.SetWaveform(Oscillator::WAVE_SIN); + + // ampEnv settings + ampEnv.Init(sample_rate); + ampEnv.SetMax(1); + ampEnv.SetMin(0); + ampEnv.SetCurve(-100); + SetParam(PARAM_AMP_ATTACK, ampAttack, false); + SetParam(PARAM_AMP_DECAY, ampDecay, false); + + // pitchEnv settings + pitchEnv.Init(sample_rate); + pitchEnv.SetMax(1); + pitchEnv.SetMin(0); + pitchEnv.SetCurve(-100); + SetParam(PARAM_PITCH_ATTACK, pitchAttack, false); + SetParam(PARAM_PITCH_DECAY, pitchDecay, false); + SetParam(PARAM_MOD_AMT, modAmount, false); +} + +float Bd8::Process() { + float psig = pitchEnv.Process(); + osc.SetFreq(params[PARAM_FREQUENCY] + params[PARAM_MOD_AMT] * psig); + return velocity * osc.Process() * ampEnv.Process(); +} + +void Bd8::Trigger(float velocity) { + this->velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + ampEnv.Trigger(); + pitchEnv.Trigger(); + } +} + +float Bd8::GetParam(uint8_t param) { + return param < Bd8::PARAM_COUNT ? params[param] : 0.0f; +} + +std::string Bd8::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_FREQUENCY: + case PARAM_MOD_AMT: + return std::to_string((int)GetParam(param));// + "hz"; + case PARAM_AMP_ATTACK: + case PARAM_AMP_DECAY: + case PARAM_PITCH_ATTACK: + case PARAM_PITCH_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + } + } + return ""; + } + +float Bd8::SetParam(uint8_t param, float value, bool scaled) { + if (param < Bd8::PARAM_COUNT) { + if (scaled) { + switch (param) { + case PARAM_FREQUENCY: + params[param] = Utility::ScaleFloat(value, 20, 5000, Parameter::EXPONENTIAL); + return params[param]; + case PARAM_AMP_ATTACK: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + ampEnv.SetTime(ADENV_SEG_ATTACK, params[param]); + return params[param]; + case PARAM_AMP_DECAY: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + ampEnv.SetTime(ADENV_SEG_DECAY, params[param]); + return params[param]; + case PARAM_PITCH_ATTACK: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + pitchEnv.SetTime(ADENV_SEG_ATTACK, params[param]); + return params[param]; + case PARAM_PITCH_DECAY: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + pitchEnv.SetTime(ADENV_SEG_DECAY, params[param]); + return params[param]; + case PARAM_MOD_AMT: + params[param] = Utility::ScaleFloat(value, 0, 2000, Parameter::EXPONENTIAL); + return params[param]; + } + } else { + params[param] = value; + } + } + + return value; +} diff --git a/optic/HachiKit/Bd8.h b/optic/HachiKit/Bd8.h new file mode 100644 index 000000000..444f0eb32 --- /dev/null +++ b/optic/HachiKit/Bd8.h @@ -0,0 +1,74 @@ +#ifndef BD8_H +#define BD8_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +class Bd8: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 6; + // This is the order params will appear in the UI. + static const uint8_t PARAM_FREQUENCY = 0; + static const uint8_t PARAM_AMP_DECAY = 1; + static const uint8_t PARAM_PITCH_DECAY = 2; + static const uint8_t PARAM_MOD_AMT = 3; + static const uint8_t PARAM_AMP_ATTACK = 4; + static const uint8_t PARAM_PITCH_ATTACK = 5; + + /** Initialize model with default parameters. + * \param sample_rate audio sample rate. + */ + void Init(float sample_rate); + + /** Initialize model with specified parameters. + * \param sample_rate audio sample rate. + * \param frequency oscillator frequency in hertz. + */ + void Init(float sample_rate, float frequency, float ampAttack, float ampDecay, float pitchAttack, float pitchDecay, float modAmount); + + float Process(); + void Trigger(float velocity); + + /** Get the current value of a parameter. + * \param param index of the desired parameter (must be velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + // trigger envelopes + } +} + +float Blank::GetParam(uint8_t param) { + return param < Blank::PARAM_COUNT ? params[param] : 0.0f; +} + +std::string Blank::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_FREQUENCY: + return std::to_string((int)GetParam(param));// + "hz"; + case PARAM_ATTACK: + case PARAM_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + } + } + return ""; + } + +float Blank::SetParam(uint8_t param, float value, bool scaled) { + if (param < Blank::PARAM_COUNT) { + if (scaled) { + switch (param) { + case PARAM_FREQUENCY: + params[param] = Utility::ScaleFloat(value, 20, 5000, Parameter::EXPONENTIAL); + // update accordingly + return params[param]; + case PARAM_ATTACK: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + // update accordingly + return params[param]; + case PARAM_DECAY: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + // update accordingly + return params[param]; + } + } else { + params[param] = value; + } + } + + return value; +} diff --git a/optic/HachiKit/Blank.h b/optic/HachiKit/Blank.h new file mode 100644 index 000000000..ed9606899 --- /dev/null +++ b/optic/HachiKit/Blank.h @@ -0,0 +1,69 @@ +#ifndef BLANK_H +#define BLANK_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +class Blank: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 3; + // This is the order params will appear in the UI. + static const uint8_t PARAM_FREQUENCY = 0; + static const uint8_t PARAM_ATTACK = 1; + static const uint8_t PARAM_DECAY = 2; + + /** Initialize model with default parameters. + * \param sample_rate audio sample rate. + */ + void Init(float sample_rate); + + /** Initialize model with specified parameters. + * \param sample_rate audio sample rate. + * \param frequency oscillator frequency in hertz. + */ + void Init(float sample_rate, float frequency, float attack, float decay); + + float Process(); + void Trigger(float velocity); + + /** Get the current value of a parameter. + * \param param index of the desired parameter (must be source = source; + + // env settings + env.Init(sample_rate); + env.SetMax(1); + env.SetMin(0); + env.SetCurve(-20); + SetParam(PARAM_ATTACK, attack, false); + SetParam(PARAM_DECAY, decay, false); +} + +float Ch::Process() { + if (source == NULL) { + return 0.0f; + } + + float sig = source->Signal() * env.Process(); + return velocity * sig; +} + +void Ch::Trigger(float velocity) { + this->velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + env.Trigger(); + } +} + +float Ch::GetParam(uint8_t param) { + return param < Ch::PARAM_COUNT ? params[param] : 0.0f; +} + +std::string Ch::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + case PARAM_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + } + } + return ""; + } + +float Ch::SetParam(uint8_t param, float value, bool scaled) { + if (param < Ch::PARAM_COUNT) { + if (scaled) { + switch (param) { + case PARAM_ATTACK: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + // update accordingly + return params[param]; + case PARAM_DECAY: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + // update accordingly + return params[param]; + } + } else { + params[param] = value; + } + } + + return value; +} diff --git a/optic/HachiKit/Ch.h b/optic/HachiKit/Ch.h new file mode 100644 index 000000000..61fb0b918 --- /dev/null +++ b/optic/HachiKit/Ch.h @@ -0,0 +1,71 @@ +#ifndef CH_H +#define CH_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "ISource.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +class Ch: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 2; + // This is the order params will appear in the UI. + static const uint8_t PARAM_ATTACK = 0; + static const uint8_t PARAM_DECAY = 1; + + /** Initialize model with default parameters. + * \param sample_rate audio sample rate. + */ + void Init(float sample_rate); + + /** Initialize model with specified parameters. + * \param sample_rate audio sample rate. + * \param frequency oscillator frequency in hertz. + */ + // void Init(float sample_rate, float attack, float decay); + void Init(float sample_rate, float attack, float decay, ISource *source); + + float Process(); + void Trigger(float velocity); + + /** Get the current value of a parameter. + * \param param index of the desired parameter (must be velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + ampEnv.Trigger(); + } +} + +float FmDrum::GetParam(uint8_t param) { + return param < FmDrum::PARAM_COUNT ? params[param] : 0.0f; +} + +std::string FmDrum::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_FREQUENCY: + return std::to_string((int)GetParam(param));// + "hz"; + case PARAM_RATIO: + return Utility::FloatToString2(GetParam(param)); + case PARAM_MOD_AMT: + return Utility::FloatToString2(GetParam(param)); + case PARAM_ATTACK: + case PARAM_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + case PARAM_ENV_CURVE: + return std::to_string((int)GetParam(param)); + } + } + return ""; + } + +float FmDrum::SetParam(uint8_t param, float value, bool scaled) { + if (param < FmDrum::PARAM_COUNT) { + switch (param) { + case PARAM_FREQUENCY: + params[param] = value; + if (scaled) params[param] = Utility::ScaleFloat(value, 20, 5000, Parameter::EXPONENTIAL); + fm.SetFrequency(params[param]); + return params[param]; + case PARAM_RATIO: + params[param] = value; + if (scaled) params[param] = Utility::ScaleFloat(value, 0.125, 8, Parameter::EXPONENTIAL); + fm.SetRatio(params[param]); + return params[param]; + case PARAM_MOD_AMT: + params[param] = value; + if (scaled) params[param] = Utility::ScaleFloat(value, 0, 5, Parameter::EXPONENTIAL); + fm.SetIndex(params[param]); + return params[param]; + case PARAM_ATTACK: + params[param] = value; + if (scaled) params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + ampEnv.SetTime(ADENV_SEG_ATTACK, params[param]); + return params[param]; + case PARAM_DECAY: + params[param] = value; + if (scaled) params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + ampEnv.SetTime(ADENV_SEG_DECAY, params[param]); + return params[param]; + case PARAM_ENV_CURVE: + params[param] = value; + if (scaled) params[param] = Utility::ScaleFloat(value, -100, 100, Parameter::LINEAR); + ampEnv.SetTime(ADENV_SEG_DECAY, params[param]); + return params[param]; + } + return params[param]; + } + + return value; +} diff --git a/optic/HachiKit/FmDrum.h b/optic/HachiKit/FmDrum.h new file mode 100644 index 000000000..9906d6e8b --- /dev/null +++ b/optic/HachiKit/FmDrum.h @@ -0,0 +1,73 @@ +#ifndef FMDRUM_H +#define FMDRUM_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +class FmDrum: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 5; + // This is the order params will appear in the UI. + static const uint8_t PARAM_FREQUENCY = 0; + static const uint8_t PARAM_RATIO = 1; + static const uint8_t PARAM_MOD_AMT = 2; + static const uint8_t PARAM_DECAY = 3; + static const uint8_t PARAM_ATTACK = 4; + static const uint8_t PARAM_ENV_CURVE = 5; + + /** Initialize model with default parameters. + * \param sample_rate audio sample rate. + */ + void Init(float sample_rate); + + /** Initialize model with specified parameters. + * \param sample_rate audio sample rate. + * \param frequency oscillator frequency in hertz. + */ + void Init(float sample_rate, float frequency, float ratio, float modAmount, float attack, float decay, float curve); + + float Process(); + void Trigger(float velocity); + + /** Get the current value of a parameter. + * \param param index of the desired parameter (must be +#include + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "Utility.h" +#include "Screen.h" +#include "IDrum.h" +#include "Bd8.h" +#include "Sd8.h" +#include "SdNoise.h" +#include "FmDrum.h" +#include "HhSource68.h" +#include "Ch.h" + +using namespace daisy; +using namespace daisysp; + +#define MINIMUM_NOTE 36 +#define KNOB_COUNT 4 + +DaisyPatch hw; +Screen screen(&hw.display); +IDrum *drums[16]; +uint8_t drumCount = 4; +Bd8 bd; +SdNoise rs; +Sd8 sd; +FmDrum cp; +Ch ch; + +HhSource68 source68; + +uint8_t currentDrum = 0; +uint8_t currentKnobRow = 0; + +void OledMessage(std::string message, int row) +{ + char* mstr = &message[0]; + hw.display.SetCursor(0, row * 10); + hw.display.WriteString(mstr, Font_6x8, true); + hw.display.Update(); +} + +void OledMessage(std::string message, int row, int column) +{ + char* mstr = &message[0]; + hw.display.SetCursor(column * 8, row * 10); + hw.display.WriteString(mstr, Font_6x8, true); + hw.display.Update(); +} + +// Display the available parameter names. +void DisplayParamMenu() { + + hw.display.DrawRect(0, 0, 127, 30, false, true); + hw.display.DrawLine(0,10,127,10, true); + + uint8_t param; + for (int knob = 0; knob < KNOB_COUNT; knob++) { + for (u8 row = 0; row <= drums[currentDrum]->PARAM_COUNT / 4; row++) { + // for (u8 row = 0; row < 2; row++) { + Rectangle rect2(knob * 32, (row + 1) * 11, 32, 11); + param = row * KNOB_COUNT + knob; + std::string sc = drums[currentDrum]->GetParamName(param); + bool selected = row == currentKnobRow; + // hw.display.WriteStringAligned(sc.c_str(), Font_6x8, rect2, Alignment::centered, true); + screen.DrawButton(rect2, sc, selected, selected, !selected); + // hw.display.SetCursor(rect2.GetX(), rect2.GetY()); + // hw.display.WriteString(sc.c_str(), Font_6x8, true); + hw.display.DrawLine(0, rect2.GetY() + 11, 127, rect2.GetY() + 11, true); + } + } +} + +void ProcessEncoder() { + int inc = hw.encoder.Increment(); + if (inc != 0) { + currentDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); + screen.DrawMenu(currentDrum); + DisplayParamMenu(); + hw.display.Update(); + } +} + +// Process the current knob values and update model params accordingly. +// Knob number == param number, since model params are listed in UI order. +void ProcessKnobs() { + + for (int knob = 0; knob < KNOB_COUNT; knob++) { + float sig = hw.controls[knob].Process(); + // need to add check for whether we should update value + uint8_t param = currentKnobRow * KNOB_COUNT + knob; + drums[currentDrum]->SetParam(param, sig, true); + } +} + +// Display the current values and parameter names of model params for 4 knobs. +// Knob number == param number, since model params are listed in UI order. +void DisplayKnobValues() { + + hw.display.DrawRect(0, 0, 127, 30, false, true); + hw.display.DrawLine(0,10,127,10, true); + + uint8_t param; + for (int knob = 0; knob < KNOB_COUNT; knob++) { + // top row: current param values from knobs + param = currentKnobRow * KNOB_COUNT + knob; + Rectangle rect(knob * 32, 0, 32, 8); + std::string sc = drums[currentDrum]->GetParamString(param); + hw.display.WriteStringAligned(sc.c_str(), Font_6x8, rect, Alignment::centered, true); + // screen.DrawButton(rect, sc, false, true, false); + + for (u8 row = 0; row <= drums[currentDrum]->PARAM_COUNT / 4; row++) { + Rectangle rect2(knob * 32, (row + 1) * 11, 32, 11); + param = row * KNOB_COUNT + knob; + sc = drums[currentDrum]->GetParamName(param); + bool selected = row == currentKnobRow; + // hw.display.WriteStringAligned(sc.c_str(), Font_6x8, rect2, Alignment::centered, true); + screen.DrawButton(rect2, sc, selected, selected, !selected); + // hw.display.SetCursor(rect2.GetX(), rect2.GetY()); + // hw.display.WriteString(sc.c_str(), Font_6x8, true); + hw.display.DrawLine(0, rect2.GetY() + 11, 127, rect2.GetY() + 11, true); + } + } +} + +void ProcessControls() +{ + hw.ProcessAnalogControls(); + hw.ProcessDigitalControls(); + + ProcessEncoder(); + ProcessKnobs(); + // ProcessGates(); +} + + +void AudioCallback(AudioHandle::InputBuffer in, + AudioHandle::OutputBuffer out, + size_t size) +{ + + ProcessControls(); + + for(size_t i = 0; i < size; i++) + { + + float sig = 0.0f; + for (uint8_t i = 0; i < drumCount; i++) { + sig += drums[i]->Process(); + } + float limited = sig / drumCount; + + out[0][i] = out[1][i] = limited; + out[2][i] = out[3][i] = sig; + + } +} + + +// Typical Switch case for Message Type. +void HandleMidiMessage(MidiEvent m) +{ + OledMessage("MIDI, d=" + std::to_string(m.data[0]) + "," + std::to_string(m.data[1]), 4); + + switch(m.type) + { + case NoteOn: + { + // NoteOnEvent p = m.AsNoteOn(); + // This is to avoid Max/MSP Note outs for now.. + // if(m.data[1] != 0) + // { + // p = m.AsNoteOn(); + // osc.SetFreq(mtof(p.note)); + // osc.SetAmp((p.velocity / 127.0f)); + // } else { + // osc.SetAmp(0.0f); + // } + // OledMessage("Midi: " + std::to_string(p.note) + ", " + std::to_string(p.velocity) + " ", 3); + + NoteOnEvent p = m.AsNoteOn(); + float velocity = p.velocity / 127.0f; + if (p.velocity > 0) { + if (p.note >= MINIMUM_NOTE && p.note < MINIMUM_NOTE + drumCount) { + drums[p.note - MINIMUM_NOTE]->Trigger(velocity); + } + OledMessage("Midi: " + std::to_string(p.note) + ", " + std::to_string(p.velocity) + " ", 2); + } + } + break; + case NoteOff: + { + } + break; + case ControlChange: + { + ControlChangeEvent p = m.AsControlChange(); + switch(p.control_number) + { + case 1: + break; + case 2: + break; + default: break; + } + break; + } + default: break; + } +} + + +// Main -- Init, and Midi Handling +int main(void) +{ + // Init + float samplerate; + hw.Init(); + samplerate = hw.AudioSampleRate(); + + drums[0] = &bd; + drums[1] = &rs; + drums[2] = &sd; + drums[3] = &cp; + // drums[4] = &ch; + // drumCount = 5; + currentDrum = 0; + + for (uint8_t i = 0; i < drumCount; i++) { + drums[i]->Init(samplerate); + } + + source68.Init(samplerate, 0.5); + ch.Init(samplerate,0.001, 0.5, &source68); + + // for (u8 i = 0; i < 4; i++) { + // for (u8 j = 1; j < 4; j++) { + // drums[j * 4 + i] = drums[i]; + // } + // } + // drumCount = 16; + + //display + hw.display.Fill(false); + // OledMessage("Hachikit 0.1", 5); + // Utility::DrawDrums(&hw.display, 5); + screen.DrawMenu(currentDrum); + hw.display.Update(); + + + // Start stuff. + hw.SetAudioBlockSize(128); + hw.midi.StartReceive(); + hw.StartAdc(); + hw.StartAudio(AudioCallback); + int c = 0; + for(;;) + { + OledMessage("Count: " + std::to_string(c), 3); + hw.midi.Listen(); + // Handle MIDI Events + while(hw.midi.HasEvents()) + { + HandleMidiMessage(hw.midi.PopEvent()); + } + DisplayKnobValues(); + hw.display.Update(); + c++; + } +} diff --git a/optic/HachiKit/HhSource68.cpp b/optic/HachiKit/HhSource68.cpp new file mode 100644 index 000000000..cbc9eb50c --- /dev/null +++ b/optic/HachiKit/HhSource68.cpp @@ -0,0 +1,60 @@ +#include "HhSource68.h" + +using namespace daisy; +using namespace daisysp; + +const float HhSource68::freqs606[OSC_COUNT] = { 245, 306, 365, 415, 437, 619 }; +const float HhSource68::freqs808[OSC_COUNT] = { 204, 298, 366, 515, 540, 800 }; +const float HhSource68::MIN_FREQ_FACTOR = 0.1; + + +void HhSource68::Init(float sample_rate) { + Init(sample_rate, MORPH_808_VALUE); +} + +void HhSource68::Init(float sample_rate, float morph) { + + for (int osc = 0; osc < OSC_COUNT; osc++) { + oscs[osc].Init(sample_rate); + oscs[osc].SetWaveform(Oscillator::WAVE_SQUARE); + oscs[osc].SetFreq(freqs808[osc]); + } + + SetMorph(morph); + signal = 0.0f; +} + +float HhSource68::Process() { + float sig = 0.0f; + for (int osc = 0; osc < OSC_COUNT; osc++) { + sig += oscs[osc].Process(); + } + sig /= OSC_COUNT; + + signal = sig; + return signal; +} + +float HhSource68::Signal() { + return signal; +} + +float HhSource68::GetMorph() { + return this->morph; +} + +void HhSource68::SetMorph(float morph) { + + this->morph = morph; + + float range68 = MORPH_808_VALUE - MORPH_606_VALUE; // assumes 8>6 and both betw 0 and 1 + float weight8 = (morph - MORPH_606_VALUE) * (1 / range68); + float weight6 = 1 - weight8; + + for (int osc = 0; osc < OSC_COUNT; osc++) { + float freq = weight6 * freqs606[osc] + weight8 * freqs808[osc]; + freq = std::max(freq, freqs606[osc] * MIN_FREQ_FACTOR); // make sure freqs don't go to zero (and mins aren't all the same) + oscs[osc].SetFreq(freq); + } + +} \ No newline at end of file diff --git a/optic/HachiKit/HhSource68.h b/optic/HachiKit/HhSource68.h new file mode 100644 index 000000000..5e2e64437 --- /dev/null +++ b/optic/HachiKit/HhSource68.h @@ -0,0 +1,54 @@ +#ifndef HHSOURCE68_H +#define HHSOURCE68_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include "ISource.h" + +using namespace daisy; +using namespace daisysp; + +class HhSource68: public ISource { + + public: + // how many square waves make up the sound source + static const uint8_t OSC_COUNT = 6; + + // the morph value at which the sound imitates a 606 + static const uint8_t MORPH_606_VALUE = 0.35; + // the morph value at which the sound imitates an 808 + static const uint8_t MORPH_808_VALUE = 0.65; + + + /** Initialize model with default parameters. + * \param sample_rate audio sample rate. + */ + void Init(float sample_rate); + + /** Initialize model with specified parameters. + * \param sample_rate audio sample rate. + * \param frequency oscillator frequency in hertz. + */ + void Init(float sample_rate, float morph); + + float Process(); + float Signal(); + + float GetMorph(); + void SetMorph(float morph); + + private: + static const float freqs606[]; + static const float freqs808[]; + static const float MIN_FREQ_FACTOR; + + Oscillator oscs[OSC_COUNT]; + float morph; + float frequencies[OSC_COUNT]; + float signal; + +}; + + + +#endif diff --git a/optic/HachiKit/IDrum.h b/optic/HachiKit/IDrum.h new file mode 100644 index 000000000..cd6b5b01e --- /dev/null +++ b/optic/HachiKit/IDrum.h @@ -0,0 +1,36 @@ +#ifndef IDRUM_H +#define IDRUM_H + +using namespace daisy; +using namespace daisysp; + +#include "daisy_patch.h" +#include "daisysp.h" +#include + + +class IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 0; + + virtual ~IDrum() { + + } + + virtual void Init(float sample_rate) = 0; + virtual float Process() = 0; + virtual void Trigger(float velocity) = 0; + virtual float GetParam(uint8_t param) = 0; + virtual float SetParam(uint8_t param, float value, bool scaled) = 0; + + virtual std::string Name() = 0; + virtual std::string Slot() = 0; + virtual std::string GetParamName(uint8_t param) = 0; + virtual std::string GetParamString(uint8_t param) = 0; + +}; + + +#endif diff --git a/optic/HachiKit/ISource.h b/optic/HachiKit/ISource.h new file mode 100644 index 000000000..11dd6881f --- /dev/null +++ b/optic/HachiKit/ISource.h @@ -0,0 +1,24 @@ +#ifndef ISOURCE_H +#define ISOURCE_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +class ISource { + + public: + virtual void Init(float sample_rate) = 0; + virtual float Process() = 0; + virtual float Signal() = 0; + + private: + +}; + + + +#endif diff --git a/optic/HachiKit/Makefile b/optic/HachiKit/Makefile new file mode 100644 index 000000000..efb68b55b --- /dev/null +++ b/optic/HachiKit/Makefile @@ -0,0 +1,24 @@ +# Project Name +TARGET = HachiKit + +# Sources +CPP_SOURCES += HachiKit.cpp \ +Screen.cpp \ +Bd8.cpp \ +Sd8.cpp \ +SdNoise.cpp \ +FmDrum.cpp \ +HhSource68.cpp \ +Ch.cpp + + +OPT = -Os + +# Library Locations +LIBDAISY_DIR = ../../libDaisy +DAISYSP_DIR = ../../DaisySP + +# Core location, and generic Makefile. +SYSTEM_FILES_DIR = $(LIBDAISY_DIR)/core +include $(SYSTEM_FILES_DIR)/Makefile + diff --git a/optic/HachiKit/README.md b/optic/HachiKit/README.md new file mode 100644 index 000000000..49e9c8ac6 --- /dev/null +++ b/optic/HachiKit/README.md @@ -0,0 +1,20 @@ +# Midi + +## Description +Control a synth voice over Midi. + +[Source Code](https://github.com/electro-smith/DaisyExamples/tree/master/patch/Midi) + +## Author + +Shensley + +## Controls +| Control | Description | +| --- | --- | +| Midi CC 1 | Filter Cutoff | +| Midi CC 2 | Filter Resonance | +| Audio Outs | Oscillator Output| + + + diff --git a/optic/HachiKit/Screen.cpp b/optic/HachiKit/Screen.cpp new file mode 100644 index 000000000..84628fa23 --- /dev/null +++ b/optic/HachiKit/Screen.cpp @@ -0,0 +1,46 @@ +#include "Screen.h" + +using namespace daisy; +using namespace daisysp; + +const FontDef Screen::FONT = Font_6x8; +const std::string Screen::menuItems[] = { "BD", "RS", "SD", "CP", + "S2", "LT", "CH", "MT", + "MA", "HT", "OH", "LC", + "HC", "CY", "CB", "CL" }; +#define MENU_SIZE 16 + +void Screen::DrawRect(Rectangle rect, bool on, bool fill) { + display->DrawRect(rect.GetX(), rect.GetY(), rect.GetX() + rect.GetWidth(), rect.GetY() + rect.GetHeight(), on, fill); +} + +void Screen::DrawRectFilled(Rectangle rect, bool border, bool fill) { + DrawRect(rect, fill, true); + DrawRect(rect, border, false); +} + +void Screen::DrawButton(Rectangle rect, std::string str, bool border, bool fill, bool text) { + DrawRectFilled(rect, border, fill); + // display->WriteStringAligned(str.c_str(), FONT, rect, Alignment::centered, text); + display->SetCursor(rect.GetX() + 2, rect.GetY() + 2); + display->WriteString(str.c_str(), FONT, text); +} + +void Screen::DrawMenu(uint8_t selected) { + + uint8_t itemWidth = FONT.FontWidth * 2 + 3; + uint8_t itemHeight = FONT.FontWidth + 4; + uint8_t displayCount = std::min(WIDTH / itemWidth, MENU_SIZE); + uint8_t highlightPos = displayCount / 2; + // highlightPos = 4; + uint8_t start = std::min(std::max(0, selected - highlightPos), MENU_SIZE - displayCount); + + for (uint8_t pos = start; pos < start + displayCount; pos++) { + bool sel = pos == selected; + uint8_t x = itemWidth * (pos - start); + uint8_t y = HEIGHT - itemHeight; + Rectangle rect(x, y, itemWidth, itemHeight); + DrawButton(rect, menuItems[pos], true, sel, !sel); + } + +} diff --git a/optic/HachiKit/Screen.h b/optic/HachiKit/Screen.h new file mode 100644 index 000000000..8de2d7da7 --- /dev/null +++ b/optic/HachiKit/Screen.h @@ -0,0 +1,36 @@ +#ifndef SCREEN_H +#define SCREEN_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include + +using namespace daisy; +using namespace daisysp; + +class Screen { + + public: + static const uint8_t HEIGHT = 63; + static const uint8_t WIDTH = 127; + static const FontDef FONT; + static const std::string menuItems[]; + + void DrawRect(Rectangle rect, bool on, bool fill); + void DrawRectFilled(Rectangle rect, bool border, bool fill); + void DrawButton(Rectangle rect, std::string str, bool border, bool text, bool fill); + + Screen(OledDisplay *display) { + this->display = display; + } + + void DrawMenu(uint8_t selected); + + + private: + OledDisplay *display; + +}; + + +#endif diff --git a/optic/HachiKit/Sd8.cpp b/optic/HachiKit/Sd8.cpp new file mode 100644 index 000000000..6a4fbc223 --- /dev/null +++ b/optic/HachiKit/Sd8.cpp @@ -0,0 +1,111 @@ +#include "Sd8.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +const std::string Sd8::paramNames[] = { "Freq", "oDec", "nDec", "Mix", "oAtt", "nAtt" }; + + +void Sd8::Init(float sample_rate) { + Init(sample_rate, 110, 0.001, 0.5, 0.001, 0.5, 0.5); +} + +void Sd8::Init(float sample_rate, float oscFrequency, float oscAttack, float oscDecay, float noiseAttack, float noiseDecay, float mix) { + + // oscillator settings + osc.Init(sample_rate); + SetParam(PARAM_OSC_FREQUENCY, oscFrequency, false); + osc.SetWaveform(Oscillator::WAVE_SIN); + + // oscEnv settings + oscEnv.Init(sample_rate); + oscEnv.SetMax(1); + oscEnv.SetMin(0); + oscEnv.SetCurve(-20); + SetParam(PARAM_OSC_ATTACK, oscAttack, false); + SetParam(PARAM_OSC_DECAY, oscDecay, false); + + // noise + noise.Init(); + + // noiseEnv settings + noiseEnv.Init(sample_rate); + noiseEnv.SetMax(1); + noiseEnv.SetMin(0); + noiseEnv.SetCurve(-20); + SetParam(PARAM_NOISE_ATTACK, noiseAttack, false); + SetParam(PARAM_NOISE_DECAY, noiseDecay, false); + SetParam(PARAM_MIX, mix, false); +} + +float Sd8::Process() { + float sig = (1 - params[PARAM_MIX]) * osc.Process() * oscEnv.Process(); + sig += params[PARAM_MIX] * noise.Process() * noiseEnv.Process(); + return velocity * sig; // / 2; +} + +void Sd8::Trigger(float velocity) { + this->velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + oscEnv.Trigger(); + noiseEnv.Trigger(); + } +} + +float Sd8::GetParam(uint8_t param) { + return param < Sd8::PARAM_COUNT ? params[param] : 0.0f; +} + +std::string Sd8::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_OSC_FREQUENCY: + return std::to_string((int)GetParam(param));// + "hz"; + case PARAM_OSC_ATTACK: + case PARAM_OSC_DECAY: + case PARAM_NOISE_ATTACK: + case PARAM_NOISE_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + case PARAM_MIX: + return std::to_string((int)(GetParam(param) * 100)); + } + } + return ""; + } + +float Sd8::SetParam(uint8_t param, float value, bool scaled) { + if (param < Sd8::PARAM_COUNT) { + if (scaled) { + switch (param) { + case PARAM_OSC_FREQUENCY: + params[param] = Utility::ScaleFloat(value, 20, 5000, Parameter::EXPONENTIAL); + osc.SetFreq(params[param]); + return params[param]; + case PARAM_OSC_ATTACK: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + oscEnv.SetTime(ADENV_SEG_ATTACK, params[param]); + return params[param]; + case PARAM_OSC_DECAY: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + oscEnv.SetTime(ADENV_SEG_DECAY, params[param]); + return params[param]; + case PARAM_NOISE_ATTACK: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + noiseEnv.SetTime(ADENV_SEG_ATTACK, params[param]); + return params[param]; + case PARAM_NOISE_DECAY: + params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); + noiseEnv.SetTime(ADENV_SEG_DECAY, params[param]); + return params[param]; + case PARAM_MIX: + params[param] = Utility::LimitFloat(value, 0, 1); + return params[param]; + } + } else { + params[param] = value; + } + } + + return value; +} diff --git a/optic/HachiKit/Sd8.h b/optic/HachiKit/Sd8.h new file mode 100644 index 000000000..64512e30e --- /dev/null +++ b/optic/HachiKit/Sd8.h @@ -0,0 +1,75 @@ +#ifndef SD8_H +#define SD8_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +class Sd8: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 6; + // This is the order params will appear in the UI. + static const uint8_t PARAM_OSC_FREQUENCY = 0; + static const uint8_t PARAM_OSC_DECAY = 1; + static const uint8_t PARAM_NOISE_DECAY = 2; + static const uint8_t PARAM_MIX = 3; + static const uint8_t PARAM_OSC_ATTACK = 4; + static const uint8_t PARAM_NOISE_ATTACK = 5; + static const std::string paramNames[]; + + /** Initialize model with default parameters. + * \param sample_rate audio sample rate. + */ + void Init(float sample_rate); + + /** Initialize model with specified parameters. + * \param sample_rate audio sample rate. + * \param frequency oscillator frequency in hertz. + */ + void Init(float sample_rate, float oscFrequency, float oscAttack, float oscDecay, float noiseAttack, float noiseDecay, float mix); + + float Process(); + void Trigger(float velocity); + + /** Get the current value of a parameter. + * \param param index of the desired parameter (must be velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + ampEnv.Trigger(); + } +} + +float SdNoise::GetParam(uint8_t param) { + return param < PARAM_COUNT ? params[param] : 0.0f; +} + +std::string SdNoise::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + case PARAM_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + case PARAM_CURVE: + return std::to_string((int)GetParam(param)); + } + } + return ""; + } + +float SdNoise::SetParam(uint8_t param, float value, bool scaled) { + if (param < SdNoise::PARAM_COUNT) { + if (scaled) { + switch (param) { + case PARAM_ATTACK: + params[param] = Utility::ScaleFloat(value, 0.001, 5, Parameter::EXPONENTIAL); + ampEnv.SetTime(ADENV_SEG_ATTACK, params[param]); + return params[param]; + case PARAM_DECAY: + params[param] = Utility::ScaleFloat(value, 0.001, 5, Parameter::EXPONENTIAL); + ampEnv.SetTime(ADENV_SEG_DECAY, params[param]); + return params[param]; + case PARAM_CURVE: + params[param] = Utility::ScaleFloat(value, -100, 100, Parameter::LINEAR); + ampEnv.SetCurve(params[param]); + return params[param]; + } + } else { + params[param] = value; + return value; + } + } + + return 0.0f; +} diff --git a/optic/HachiKit/SdNoise.h b/optic/HachiKit/SdNoise.h new file mode 100644 index 000000000..49b7054d1 --- /dev/null +++ b/optic/HachiKit/SdNoise.h @@ -0,0 +1,70 @@ +#ifndef SDNOISE_H +#define SDNOISE_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" + +using namespace daisy; +using namespace daisysp; + + +class SdNoise: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 3; + // This is the order params will appear in the UI. + static const uint8_t PARAM_ATTACK = 0; + static const uint8_t PARAM_DECAY = 1; + static const uint8_t PARAM_CURVE = 2; + + /** Initialize model with default parameters. + * \param sample_rate audio sample rate. + */ + void Init(float sample_rate); + + /** Initialize model with specified parameters. + * \param sample_rate audio sample rate. + * \param frequency oscillator frequency in hertz. + */ + void Init(float sample_rate, float attack, float decay, float curve); + + float Process(); + void Trigger(float velocity); + + /** Get the current value of a parameter. + * \param param index of the desired parameter (must be *display, uint8_t current) { + display->DrawRect(0, 48, 127, 63, true, false); + display->SetCursor(8, 52); + display->WriteString("BD", Font_6x8, true); + display->SetCursor(104, 52); + display->WriteString("36", Font_6x8, true); + + for (uint8_t i = 0; i < 16; i++) { + uint8_t x = 32 + (i % 8) * 8; + uint8_t y = 48 + (i / 8) * 8; + bool fill = i == current; + display->DrawRect(x, y, x+7, y + 7, true, fill); + if (fill) { + display->DrawRect(x, y, x+7, y + 7, false, false); + } + } + + } +}; + + +#endif From ec1fcd5cf36db2464dde2d5322424630b0a67773 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sat, 4 May 2024 15:16:28 -0700 Subject: [PATCH 03/23] Added jitter and value-jump protection for drum params --- optic/HachiKit/Bd8.cpp | 87 ++++++++++++++----------- optic/HachiKit/Bd8.h | 38 +++-------- optic/HachiKit/BitArray.cpp | 123 ++++++++++++++++++++++++++++++++++++ optic/HachiKit/BitArray.h | 44 +++++++++++++ optic/HachiKit/Blank.cpp | 47 ++++++++------ optic/HachiKit/Blank.h | 30 ++------- optic/HachiKit/Ch.cpp | 43 +++++++------ optic/HachiKit/Ch.h | 30 ++------- optic/HachiKit/FmDrum.cpp | 74 ++++++++++++---------- optic/HachiKit/FmDrum.h | 33 ++-------- optic/HachiKit/HachiKit.cpp | 24 ++++--- optic/HachiKit/IDrum.h | 40 +++++++++++- optic/HachiKit/Param.h | 81 ++++++++++++++++++++++++ optic/HachiKit/Sd8.cpp | 89 ++++++++++++++------------ optic/HachiKit/Sd8.h | 33 ++-------- optic/HachiKit/SdNoise.cpp | 56 +++++++++------- optic/HachiKit/SdNoise.h | 33 ++-------- optic/HachiKit/Utility.h | 8 +++ 18 files changed, 572 insertions(+), 341 deletions(-) create mode 100644 optic/HachiKit/BitArray.cpp create mode 100644 optic/HachiKit/BitArray.h create mode 100644 optic/HachiKit/Param.h diff --git a/optic/HachiKit/Bd8.cpp b/optic/HachiKit/Bd8.cpp index 5afc599ef..d1f5a2504 100644 --- a/optic/HachiKit/Bd8.cpp +++ b/optic/HachiKit/Bd8.cpp @@ -12,7 +12,7 @@ void Bd8::Init(float sample_rate, float frequency, float ampAttack, float ampDec // oscillator settings osc.Init(sample_rate); - SetParam(PARAM_FREQUENCY, frequency, false); + SetParam(PARAM_FREQUENCY, frequency); osc.SetWaveform(Oscillator::WAVE_SIN); // ampEnv settings @@ -20,22 +20,23 @@ void Bd8::Init(float sample_rate, float frequency, float ampAttack, float ampDec ampEnv.SetMax(1); ampEnv.SetMin(0); ampEnv.SetCurve(-100); - SetParam(PARAM_AMP_ATTACK, ampAttack, false); - SetParam(PARAM_AMP_DECAY, ampDecay, false); + SetParam(PARAM_AMP_ATTACK, ampAttack); + SetParam(PARAM_AMP_DECAY, ampDecay); // pitchEnv settings pitchEnv.Init(sample_rate); pitchEnv.SetMax(1); pitchEnv.SetMin(0); pitchEnv.SetCurve(-100); - SetParam(PARAM_PITCH_ATTACK, pitchAttack, false); - SetParam(PARAM_PITCH_DECAY, pitchDecay, false); - SetParam(PARAM_MOD_AMT, modAmount, false); + SetParam(PARAM_PITCH_ATTACK, pitchAttack); + SetParam(PARAM_PITCH_DECAY, pitchDecay); + SetParam(PARAM_MOD_AMT, modAmount); } float Bd8::Process() { float psig = pitchEnv.Process(); - osc.SetFreq(params[PARAM_FREQUENCY] + params[PARAM_MOD_AMT] * psig); + osc.SetFreq(parameters[PARAM_FREQUENCY].GetScaledValue() + parameters[PARAM_MOD_AMT].GetScaledValue() * psig); + // osc.SetFreq(parameters[PARAM_FREQUENCY].GetScaledValue()); return velocity * osc.Process() * ampEnv.Process(); } @@ -48,7 +49,7 @@ void Bd8::Trigger(float velocity) { } float Bd8::GetParam(uint8_t param) { - return param < Bd8::PARAM_COUNT ? params[param] : 0.0f; + return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; } std::string Bd8::GetParamString(uint8_t param) { @@ -67,37 +68,47 @@ std::string Bd8::GetParamString(uint8_t param) { return ""; } -float Bd8::SetParam(uint8_t param, float value, bool scaled) { - if (param < Bd8::PARAM_COUNT) { - if (scaled) { - switch (param) { - case PARAM_FREQUENCY: - params[param] = Utility::ScaleFloat(value, 20, 5000, Parameter::EXPONENTIAL); - return params[param]; - case PARAM_AMP_ATTACK: - params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); - ampEnv.SetTime(ADENV_SEG_ATTACK, params[param]); - return params[param]; - case PARAM_AMP_DECAY: - params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); - ampEnv.SetTime(ADENV_SEG_DECAY, params[param]); - return params[param]; - case PARAM_PITCH_ATTACK: - params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); - pitchEnv.SetTime(ADENV_SEG_ATTACK, params[param]); - return params[param]; - case PARAM_PITCH_DECAY: - params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); - pitchEnv.SetTime(ADENV_SEG_DECAY, params[param]); - return params[param]; - case PARAM_MOD_AMT: - params[param] = Utility::ScaleFloat(value, 0, 2000, Parameter::EXPONENTIAL); - return params[param]; - } - } else { - params[param] = value; +float Bd8::UpdateParam(uint8_t param, float raw) { + float scaled = raw; + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_FREQUENCY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 5000, Parameter::EXPONENTIAL)); + break; + case PARAM_AMP_ATTACK: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + ampEnv.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_AMP_DECAY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + ampEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_PITCH_ATTACK: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + pitchEnv.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_PITCH_DECAY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + pitchEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_MOD_AMT: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0, 2000, Parameter::EXPONENTIAL)); + break; } } - return value; + return scaled; } + +void Bd8::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } +} + +void Bd8::SetParam(uint8_t param, float value) { + if (param < PARAM_COUNT) { + parameters[param].SetScaledValue(value); + } +} + diff --git a/optic/HachiKit/Bd8.h b/optic/HachiKit/Bd8.h index 444f0eb32..fd7451779 100644 --- a/optic/HachiKit/Bd8.h +++ b/optic/HachiKit/Bd8.h @@ -6,6 +6,7 @@ #include #include "IDrum.h" #include "Utility.h" +#include "Param.h" using namespace daisy; using namespace daisysp; @@ -17,55 +18,36 @@ class Bd8: public IDrum { static const uint8_t PARAM_COUNT = 6; // This is the order params will appear in the UI. static const uint8_t PARAM_FREQUENCY = 0; - static const uint8_t PARAM_AMP_DECAY = 1; - static const uint8_t PARAM_PITCH_DECAY = 2; - static const uint8_t PARAM_MOD_AMT = 3; + static const uint8_t PARAM_MOD_AMT = 1; + static const uint8_t PARAM_AMP_DECAY = 2; + static const uint8_t PARAM_PITCH_DECAY = 3; static const uint8_t PARAM_AMP_ATTACK = 4; static const uint8_t PARAM_PITCH_ATTACK = 5; + // TODO: add aCurve and pCurve - /** Initialize model with default parameters. - * \param sample_rate audio sample rate. - */ void Init(float sample_rate); - - /** Initialize model with specified parameters. - * \param sample_rate audio sample rate. - * \param frequency oscillator frequency in hertz. - */ void Init(float sample_rate, float frequency, float ampAttack, float ampDecay, float pitchAttack, float pitchDecay, float modAmount); - float Process(); void Trigger(float velocity); - /** Get the current value of a parameter. - * \param param index of the desired parameter (must be > (index*4); + } + index -= 8; + return (array.hi & (nibMask << (index*4))) >> (index*4); +} + +void NibArray16_Set(nib_array_16& array, u8 index, u8 value) { + if (index < 8) { + array.lo = (array.lo & ~(nibMask << (index*4))) | (value << (index*4)); + } + index -= 8; + array.hi = (array.hi & ~(nibMask << (index*4))) | (value << (index*4)); +} + +bool NibArray16_GetFlag(nib_array_16& array, u8 index) { + return (NibArray16_Get(array, index) & 8) != 0; +} + +u8 NibArray16_GetValue(nib_array_16& array, u8 index) { + return NibArray16_Get(array, index) & 7; +} +void NibArray16_SetFlag(nib_array_16& array, u8 index, bool flag) { + u8 value = NibArray16_GetValue(array, index); + NibArray16_Set(array, index, value | (flag<<3)); +} + +void NibArray16_ToggleFlag(nib_array_16& array, u8 index) { + bool toggleFlag = !NibArray16_GetFlag(array, index); + u8 value = NibArray16_GetValue(array, index); + NibArray16_Set(array, index, value | (toggleFlag<<3)); +} + +void NibArray16_SetValue(nib_array_16& array, u8 index, u8 value) { + bool flag = NibArray16_GetFlag(array, index); + return NibArray16_Set(array, index, value | (flag<<3)); +} + +// /***** nibble array functions ******************************/ + +// u8 NibArray16_Get(nib_array_16& array, u8 index) { +// // SERIALPRINTLN("NA16_Get: idx=" + String(index)); +// return (array & (nibMask << (index*4))) >> (index*4); +// } + +// void NibArray16_Set(nib_array_16& array, u8 index, u8 value) { +// // SERIALPRINTLN("NA16_Set: idx=" + String(index)); +// array = (array & ~(nibMask << (index*4))) | (value << (index*4)); +// } + +// // s8 SignedNib(u8 value) { +// // return value < 8 ? value : value - 16; +// // } + +// // u8 UnsignedNib(s8 value) { +// // return value >= 0 ? value : value + 16; +// // } + +// // s8 NibArray16_GetSigned(nib_array_16& array, u8 index) { +// // u8 value = NibArray16_Get(array, index); +// // s8 signedValue = SignedNib(value); +// // return signedValue; +// // } + +// // void NibArray16_SetSigned(nib_array_16& array, u8 index, s8 value) { +// // u8 unsignedValue = UnsignedNib(value); +// // NibArray16_Set(array, index, unsignedValue); +// // } + +// bool NibArray16_GetFlag(nib_array_16& array, u8 index) { +// // SERIALPRINTLN("NA16_GetFlag: idx=" + String(index) + ", nib=" + String(NibArray16_Get(array, index)) + ", flag=" + String((NibArray16_Get(array, index) & 8) != 0)); +// return (NibArray16_Get(array, index) & 8) != 0; +// } + +// u8 NibArray16_GetValue(nib_array_16& array, u8 index) { +// // SERIALPRINTLN("NA16_GetValue: idx=" + String(index) + ", nib=" + String(NibArray16_Get(array, index)) + ", flag=" + String(NibArray16_Get(array, index) & 7)); +// return NibArray16_Get(array, index) & 7; +// } +// void NibArray16_SetFlag(nib_array_16& array, u8 index, bool flag) { +// u8 value = NibArray16_GetValue(array, index); +// // SERIALPRINTLN("NA16_SetFlag: idx=" + String(index) + ", value=" + String(value) + ", flag=" + String(flag)); +// NibArray16_Set(array, index, value | (flag<<3)); +// } + +// void NibArray16_ToggleFlag(nib_array_16& array, u8 index) { +// bool toggleFlag = !NibArray16_GetFlag(array, index); +// u8 value = NibArray16_GetValue(array, index); +// // SERIALPRINTLN("NA16_ToggleFlag: idx=" + String(index) + ", value=" + String(value) + ", tflag=" + String(toggleFlag)); +// NibArray16_Set(array, index, value | (toggleFlag<<3)); +// } + +// void NibArray16_SetValue(nib_array_16& array, u8 index, u8 value) { +// bool flag = NibArray16_GetFlag(array, index); +// return NibArray16_Set(array, index, value | (flag<<3)); +// } + diff --git a/optic/HachiKit/BitArray.h b/optic/HachiKit/BitArray.h new file mode 100644 index 000000000..ab0948d79 --- /dev/null +++ b/optic/HachiKit/BitArray.h @@ -0,0 +1,44 @@ +#ifndef BITARRAY_H +#define BITARRAY_H + + +// typedef and convenient functions for treating a 16-bit uint as a bit array +// does not change the array in the func, but returns the modified form +typedef uint16_t bit_array_16; + +bool BitArray16_Get(bit_array_16 array, u8 index); +bit_array_16 BitArray16_Set(bit_array_16 array, u8 index, bool value); +bit_array_16 BitArray16_Toggle(bit_array_16 array, u8 index); + + +// typedef and convenient functions for a 4-bit (nibble) array +// nib array is treated as unsigned +// even though value is set/returned as a u8, only lower 4 bits are used +// these functions change the array in place +// +// typedef u64 nib_array_16; // <-- this did not work properly on arduino +struct nib_array_16 { + u32 lo = 0; + u32 hi = 0; +}; + +u8 NibArray16_Get(nib_array_16& array, u8 index); +void NibArray16_Set(nib_array_16& array, u8 index, u8 value); + +// these functions treat the nibble as a 4-bit signed 2s-complement value (i.e. where 1111 = -1) +// s8 SignedNib(u8 value); +// u8 UnsignedNib(s8 value); +// s8 NibArray16_GetSigned(nib_array_16& array, u8 index); +// void NibArray16_SetSigned(nib_array_16& array, u8 index, s8 value); + +// these functions treat the nibble as a 1-bit flag and a 3-bit unsigned value +bool NibArray16_GetFlag(nib_array_16& array, u8 index); +u8 NibArray16_GetValue(nib_array_16& array, u8 index); +void NibArray16_SetFlag(nib_array_16& array, u8 index, bool flag); +void NibArray16_ToggleFlag(nib_array_16& array, u8 index); +void NibArray16_SetValue(nib_array_16& array, u8 index, u8 value); + + + + +#endif diff --git a/optic/HachiKit/Blank.cpp b/optic/HachiKit/Blank.cpp index 2a4b7ed92..efe6b55fa 100644 --- a/optic/HachiKit/Blank.cpp +++ b/optic/HachiKit/Blank.cpp @@ -9,7 +9,6 @@ void Blank::Init(float sample_rate) { } void Blank::Init(float sample_rate, float frequency, float attack, float decay) { - // initialize audio objects SetParam(PARAM_FREQUENCY, frequency, false); SetParam(PARAM_ATTACK, attack, false); @@ -29,7 +28,7 @@ void Blank::Trigger(float velocity) { } float Blank::GetParam(uint8_t param) { - return param < Blank::PARAM_COUNT ? params[param] : 0.0f; + return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; } std::string Blank::GetParamString(uint8_t param) { @@ -45,27 +44,33 @@ std::string Blank::GetParamString(uint8_t param) { return ""; } -float Blank::SetParam(uint8_t param, float value, bool scaled) { +float Blank::UpdateParam(uint8_t param, float raw) { + float scaled = raw; if (param < Blank::PARAM_COUNT) { - if (scaled) { - switch (param) { - case PARAM_FREQUENCY: - params[param] = Utility::ScaleFloat(value, 20, 5000, Parameter::EXPONENTIAL); - // update accordingly - return params[param]; - case PARAM_ATTACK: - params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); - // update accordingly - return params[param]; - case PARAM_DECAY: - params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); - // update accordingly - return params[param]; - } - } else { - params[param] = value; + switch (param) { + case PARAM_FREQUENCY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 5000, Parameter::EXPONENTIAL)); + break; + case PARAM_ATTACK: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + break; + case PARAM_DECAY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + break; } } - return value; + return scaled; +} + +void Blank::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } +} + +void Blank::SetParam(uint8_t param, float value) { + if (param < PARAM_COUNT) { + parameters[param].SetScaledValue(value); + } } diff --git a/optic/HachiKit/Blank.h b/optic/HachiKit/Blank.h index ed9606899..82aa53761 100644 --- a/optic/HachiKit/Blank.h +++ b/optic/HachiKit/Blank.h @@ -6,6 +6,7 @@ #include #include "IDrum.h" #include "Utility.h" +#include "Param.h" using namespace daisy; using namespace daisysp; @@ -20,36 +21,17 @@ class Blank: public IDrum { static const uint8_t PARAM_ATTACK = 1; static const uint8_t PARAM_DECAY = 2; - /** Initialize model with default parameters. - * \param sample_rate audio sample rate. - */ void Init(float sample_rate); - - /** Initialize model with specified parameters. - * \param sample_rate audio sample rate. - * \param frequency oscillator frequency in hertz. - */ void Init(float sample_rate, float frequency, float attack, float decay); - float Process(); void Trigger(float velocity); - /** Get the current value of a parameter. - * \param param index of the desired parameter (must be #include "IDrum.h" #include "Utility.h" +#include "Param.h" using namespace daisy; using namespace daisysp; @@ -14,7 +15,7 @@ class FmDrum: public IDrum { public: // Number of settable parameters for this model. - static const uint8_t PARAM_COUNT = 5; + static const uint8_t PARAM_COUNT = 6; // This is the order params will appear in the UI. static const uint8_t PARAM_FREQUENCY = 0; static const uint8_t PARAM_RATIO = 1; @@ -23,46 +24,26 @@ class FmDrum: public IDrum { static const uint8_t PARAM_ATTACK = 4; static const uint8_t PARAM_ENV_CURVE = 5; - /** Initialize model with default parameters. - * \param sample_rate audio sample rate. - */ void Init(float sample_rate); - - /** Initialize model with specified parameters. - * \param sample_rate audio sample rate. - * \param frequency oscillator frequency in hertz. - */ void Init(float sample_rate, float frequency, float ratio, float modAmount, float attack, float decay, float curve); - float Process(); void Trigger(float velocity); - /** Get the current value of a parameter. - * \param param index of the desired parameter (must be #include "Utility.h" +#include "BitArray.h" #include "Screen.h" #include "IDrum.h" #include "Bd8.h" @@ -78,6 +79,7 @@ void ProcessEncoder() { int inc = hw.encoder.Increment(); if (inc != 0) { currentDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); + drums[currentDrum]->ResetParams(); screen.DrawMenu(currentDrum); DisplayParamMenu(); hw.display.Update(); @@ -89,10 +91,15 @@ void ProcessEncoder() { void ProcessKnobs() { for (int knob = 0; knob < KNOB_COUNT; knob++) { - float sig = hw.controls[knob].Process(); - // need to add check for whether we should update value + float sig = hw.controls[knob].Value(); uint8_t param = currentKnobRow * KNOB_COUNT + knob; - drums[currentDrum]->SetParam(param, sig, true); + // drums[currentDrum]->SetParam(param, sig, true); + drums[currentDrum]->UpdateParam(param, sig); + // float val = drums[currentDrum]->UpdateParam(param, sig); + // if (param == 0) { + // float scaled = Utility::ScaleFloat(sig, 20, 5000, Parameter::EXPONENTIAL); + // OledMessage("Upd: " + std::to_string(param) + ", " + std::to_string((int)(sig*1000)) + ", " + std::to_string((int)scaled) + ", " + std::to_string((int)val) + " ", 4); + // } } } @@ -163,7 +170,6 @@ void AudioCallback(AudioHandle::InputBuffer in, // Typical Switch case for Message Type. void HandleMidiMessage(MidiEvent m) { - OledMessage("MIDI, d=" + std::to_string(m.data[0]) + "," + std::to_string(m.data[1]), 4); switch(m.type) { @@ -187,7 +193,6 @@ void HandleMidiMessage(MidiEvent m) if (p.note >= MINIMUM_NOTE && p.note < MINIMUM_NOTE + drumCount) { drums[p.note - MINIMUM_NOTE]->Trigger(velocity); } - OledMessage("Midi: " + std::to_string(p.note) + ", " + std::to_string(p.velocity) + " ", 2); } } break; @@ -225,16 +230,16 @@ int main(void) drums[1] = &rs; drums[2] = &sd; drums[3] = &cp; - // drums[4] = &ch; - // drumCount = 5; + drums[4] = &ch; + drumCount = 5; currentDrum = 0; for (uint8_t i = 0; i < drumCount; i++) { drums[i]->Init(samplerate); } - source68.Init(samplerate, 0.5); - ch.Init(samplerate,0.001, 0.5, &source68); + // source68.Init(samplerate, 0.5); + // ch.Init(samplerate,0.001, 0.5, &source68); // for (u8 i = 0; i < 4; i++) { // for (u8 j = 1; j < 4; j++) { @@ -259,7 +264,6 @@ int main(void) int c = 0; for(;;) { - OledMessage("Count: " + std::to_string(c), 3); hw.midi.Listen(); // Handle MIDI Events while(hw.midi.HasEvents()) diff --git a/optic/HachiKit/IDrum.h b/optic/HachiKit/IDrum.h index cd6b5b01e..ef4398548 100644 --- a/optic/HachiKit/IDrum.h +++ b/optic/HachiKit/IDrum.h @@ -19,15 +19,53 @@ class IDrum { } + /** Initialize model with default parameters. + * \param sample_rate audio sample rate. + */ virtual void Init(float sample_rate) = 0; + + /** + * Calculate the next sample value. + */ virtual float Process() = 0; + + /** + * Trigger the sound from the beginning. + */ virtual void Trigger(float velocity) = 0; + + /** Get the current value of a parameter. + * \param param index of the desired parameter (must be < PARAM_COUNT). + */ virtual float GetParam(uint8_t param) = 0; - virtual float SetParam(uint8_t param, float value, bool scaled) = 0; + + /** Set the value of a parameter, regardless of the current value. + * Useful for initializing values on creation, or for setting from snapshots. + * Value is whatever is appropriate for this parameter; allows more intuitive creation + * of default values. + * + * \param param index of the desired parameter (must be scaled = scaled; + Reset(); + } + + /** + * Reset sets the Param back to a state where it is waiting for control. + */ + void Reset() { + raw = -1.0f; // any value (0-1) will appear as a change + active = lower = higher = false; + } + + /** + * Update checks the raw value for jitter and prevents jump in value. + * Should be called with both the raw value and the computed scaled value. + * + * Returns the scaled value. + */ + float Update(float raw, float scaled) { + if (!active) { + if (scaled <= this->scaled || raw <= 0.01) lower = true; + if (scaled >= this->scaled || raw >= 0.99) higher = true; + active = (lower && higher); + } + if (active && std::abs(raw - this->raw) > delta) { + this->raw = raw; + this->scaled = scaled; + } + return this->scaled; + } + + private: + float delta = 0.001f; + float raw = 0.0f; + float scaled = 0.0f; + bool active = false; + bool lower = false; + bool higher = false; + +}; + + +#endif diff --git a/optic/HachiKit/Sd8.cpp b/optic/HachiKit/Sd8.cpp index 6a4fbc223..045491e0e 100644 --- a/optic/HachiKit/Sd8.cpp +++ b/optic/HachiKit/Sd8.cpp @@ -4,7 +4,7 @@ using namespace daisy; using namespace daisysp; -const std::string Sd8::paramNames[] = { "Freq", "oDec", "nDec", "Mix", "oAtt", "nAtt" }; +const std::string Sd8::paramNames[] = { "oFrq", "oDcy", "nDcy", "Mix", "oAtk", "nAtk" }; void Sd8::Init(float sample_rate) { @@ -15,7 +15,7 @@ void Sd8::Init(float sample_rate, float oscFrequency, float oscAttack, float osc // oscillator settings osc.Init(sample_rate); - SetParam(PARAM_OSC_FREQUENCY, oscFrequency, false); + SetParam(PARAM_OSC_FREQUENCY, oscFrequency); osc.SetWaveform(Oscillator::WAVE_SIN); // oscEnv settings @@ -23,8 +23,8 @@ void Sd8::Init(float sample_rate, float oscFrequency, float oscAttack, float osc oscEnv.SetMax(1); oscEnv.SetMin(0); oscEnv.SetCurve(-20); - SetParam(PARAM_OSC_ATTACK, oscAttack, false); - SetParam(PARAM_OSC_DECAY, oscDecay, false); + SetParam(PARAM_OSC_ATTACK, oscAttack); + SetParam(PARAM_OSC_DECAY, oscDecay); // noise noise.Init(); @@ -34,14 +34,14 @@ void Sd8::Init(float sample_rate, float oscFrequency, float oscAttack, float osc noiseEnv.SetMax(1); noiseEnv.SetMin(0); noiseEnv.SetCurve(-20); - SetParam(PARAM_NOISE_ATTACK, noiseAttack, false); - SetParam(PARAM_NOISE_DECAY, noiseDecay, false); - SetParam(PARAM_MIX, mix, false); + SetParam(PARAM_NOISE_ATTACK, noiseAttack); + SetParam(PARAM_NOISE_DECAY, noiseDecay); + SetParam(PARAM_MIX, mix); } float Sd8::Process() { - float sig = (1 - params[PARAM_MIX]) * osc.Process() * oscEnv.Process(); - sig += params[PARAM_MIX] * noise.Process() * noiseEnv.Process(); + float sig = (1 - parameters[PARAM_MIX].GetScaledValue()) * osc.Process() * oscEnv.Process(); + sig += parameters[PARAM_MIX].GetScaledValue() * noise.Process() * noiseEnv.Process(); return velocity * sig; // / 2; } @@ -54,7 +54,7 @@ void Sd8::Trigger(float velocity) { } float Sd8::GetParam(uint8_t param) { - return param < Sd8::PARAM_COUNT ? params[param] : 0.0f; + return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; } std::string Sd8::GetParamString(uint8_t param) { @@ -74,38 +74,47 @@ std::string Sd8::GetParamString(uint8_t param) { return ""; } -float Sd8::SetParam(uint8_t param, float value, bool scaled) { +float Sd8::UpdateParam(uint8_t param, float raw) { + float scaled = raw; if (param < Sd8::PARAM_COUNT) { - if (scaled) { - switch (param) { - case PARAM_OSC_FREQUENCY: - params[param] = Utility::ScaleFloat(value, 20, 5000, Parameter::EXPONENTIAL); - osc.SetFreq(params[param]); - return params[param]; - case PARAM_OSC_ATTACK: - params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); - oscEnv.SetTime(ADENV_SEG_ATTACK, params[param]); - return params[param]; - case PARAM_OSC_DECAY: - params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); - oscEnv.SetTime(ADENV_SEG_DECAY, params[param]); - return params[param]; - case PARAM_NOISE_ATTACK: - params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); - noiseEnv.SetTime(ADENV_SEG_ATTACK, params[param]); - return params[param]; - case PARAM_NOISE_DECAY: - params[param] = Utility::ScaleFloat(value, 0.01, 5, Parameter::EXPONENTIAL); - noiseEnv.SetTime(ADENV_SEG_DECAY, params[param]); - return params[param]; - case PARAM_MIX: - params[param] = Utility::LimitFloat(value, 0, 1); - return params[param]; - } - } else { - params[param] = value; + switch (param) { + case PARAM_OSC_FREQUENCY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 5000, Parameter::EXPONENTIAL)); + osc.SetFreq(scaled); + break; + case PARAM_OSC_ATTACK: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + oscEnv.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_OSC_DECAY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + oscEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_NOISE_ATTACK: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + noiseEnv.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_NOISE_DECAY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + noiseEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_MIX: + scaled = parameters[param].Update(raw, Utility::LimitFloat(raw, 0, 1)); + break; } } - return value; + return scaled; +} + +void Sd8::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } +} + +void Sd8::SetParam(uint8_t param, float value) { + if (param < PARAM_COUNT) { + parameters[param].SetScaledValue(value); + } } diff --git a/optic/HachiKit/Sd8.h b/optic/HachiKit/Sd8.h index 64512e30e..34867ad3e 100644 --- a/optic/HachiKit/Sd8.h +++ b/optic/HachiKit/Sd8.h @@ -6,6 +6,7 @@ #include #include "IDrum.h" #include "Utility.h" +#include "Param.h" using namespace daisy; using namespace daisysp; @@ -22,51 +23,31 @@ class Sd8: public IDrum { static const uint8_t PARAM_MIX = 3; static const uint8_t PARAM_OSC_ATTACK = 4; static const uint8_t PARAM_NOISE_ATTACK = 5; - static const std::string paramNames[]; - /** Initialize model with default parameters. - * \param sample_rate audio sample rate. - */ void Init(float sample_rate); - - /** Initialize model with specified parameters. - * \param sample_rate audio sample rate. - * \param frequency oscillator frequency in hertz. - */ void Init(float sample_rate, float oscFrequency, float oscAttack, float oscDecay, float noiseAttack, float noiseDecay, float mix); - float Process(); void Trigger(float velocity); - /** Get the current value of a parameter. - * \param param index of the desired parameter (must be #include "IDrum.h" +#include "Param.h" using namespace daisy; using namespace daisysp; @@ -20,48 +21,28 @@ class SdNoise: public IDrum { static const uint8_t PARAM_DECAY = 1; static const uint8_t PARAM_CURVE = 2; - /** Initialize model with default parameters. - * \param sample_rate audio sample rate. - */ void Init(float sample_rate); - - /** Initialize model with specified parameters. - * \param sample_rate audio sample rate. - * \param frequency oscillator frequency in hertz. - */ void Init(float sample_rate, float attack, float decay, float curve); - float Process(); void Trigger(float velocity); - /** Get the current value of a parameter. - * \param param index of the desired parameter (must be Date: Mon, 6 May 2024 21:29:58 -0700 Subject: [PATCH 04/23] Added a 606/808-style hihat source and CH model --- optic/HachiKit/Bd8.cpp | 1 + optic/HachiKit/Blank.h | 2 +- optic/HachiKit/Ch.cpp | 53 ++++++++++++++++++++--------- optic/HachiKit/Ch.h | 14 +++++--- optic/HachiKit/FmDrum.cpp | 1 + optic/HachiKit/FmDrum.h | 2 +- optic/HachiKit/HachiKit.cpp | 29 ++++++++-------- optic/HachiKit/HhSource68.cpp | 63 ++++++++++++++++++++++++++++++----- optic/HachiKit/HhSource68.h | 20 ++++++++--- optic/HachiKit/Sd8.cpp | 1 + 10 files changed, 137 insertions(+), 49 deletions(-) diff --git a/optic/HachiKit/Bd8.cpp b/optic/HachiKit/Bd8.cpp index d1f5a2504..6832fa9a9 100644 --- a/optic/HachiKit/Bd8.cpp +++ b/optic/HachiKit/Bd8.cpp @@ -43,6 +43,7 @@ float Bd8::Process() { void Bd8::Trigger(float velocity) { this->velocity = Utility::Limit(velocity); if (this->velocity > 0) { + osc.Reset(); ampEnv.Trigger(); pitchEnv.Trigger(); } diff --git a/optic/HachiKit/Blank.h b/optic/HachiKit/Blank.h index 82aa53761..bc237a19d 100644 --- a/optic/HachiKit/Blank.h +++ b/optic/HachiKit/Blank.h @@ -38,7 +38,7 @@ class Blank: public IDrum { std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } private: - std::string paramNames[PARAM_COUNT] = { "Freq", "Att", "Dec" }; + std::string paramNames[PARAM_COUNT] = { "Freq", "Atk", "Dcy" }; std::string slot; Param parameters[PARAM_COUNT]; float velocity; diff --git a/optic/HachiKit/Ch.cpp b/optic/HachiKit/Ch.cpp index 1ff92c782..bdca9c76c 100644 --- a/optic/HachiKit/Ch.cpp +++ b/optic/HachiKit/Ch.cpp @@ -6,24 +6,11 @@ using namespace daisysp; void Ch::Init(float sample_rate) { - // Init(sample_rate, 0.001, 0.5); - Init(sample_rate, 0.001, 0.5, NULL); + Init(sample_rate, 0.001, 0.5, NULL, 0.5, 2000, 5000); } -// void Ch::Init(float sample_rate, float attack, float decay) { - -// // env settings -// env.Init(sample_rate); -// env.SetMax(1); -// env.SetMin(0); -// env.SetCurve(-20); -// SetParam(PARAM_ATTACK, attack, false); -// SetParam(PARAM_DECAY, decay, false); -// } - -void Ch::Init(float sample_rate, float attack, float decay, ISource *source) { - - this->source = source; +// void Ch::Init(float sample_rate, float attack, float decay, ISource *source) { +void Ch::Init(float sample_rate, float attack, float decay, HhSource68 *source, float morph, float hpf, float lpf) { // env settings env.Init(sample_rate); @@ -32,6 +19,13 @@ void Ch::Init(float sample_rate, float attack, float decay, ISource *source) { env.SetCurve(-20); SetParam(PARAM_ATTACK, attack); SetParam(PARAM_DECAY, decay); + + // source settings + this->source = source; + SetParam(PARAM_MORPH, morph); + SetParam(PARAM_HPF, hpf); + SetParam(PARAM_LPF, lpf); + } float Ch::Process() { @@ -51,6 +45,11 @@ void Ch::Trigger(float velocity) { } float Ch::GetParam(uint8_t param) { + // have to refetch source params since they could be changed in other ways + // parameters[PARAM_MORPH].SetScaledValue(source->GetMorph()); + // parameters[PARAM_HPF].SetScaledValue(source->GetHpfFrequency()); + // parameters[PARAM_LPF].SetScaledValue(source->GetLpfFrequency()); + return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; } @@ -60,6 +59,14 @@ std::string Ch::GetParamString(uint8_t param) { case PARAM_ATTACK: case PARAM_DECAY: return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + case PARAM_MORPH: + case PARAM_HPF: + case PARAM_LPF: + // have to refetch source params since they could be changed in other ways + // parameters[PARAM_MORPH].SetScaledValue(source->GetMorph()); + // parameters[PARAM_HPF].SetScaledValue(source->GetHpfFrequency()); + // parameters[PARAM_LPF].SetScaledValue(source->GetLpfFrequency()); + return std::to_string((int)GetParam(param)); } } return ""; @@ -71,9 +78,23 @@ float Ch::UpdateParam(uint8_t param, float raw) { switch (param) { case PARAM_ATTACK: scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + env.SetTime(ADENV_SEG_ATTACK, scaled); break; case PARAM_DECAY: scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + env.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_MORPH: + scaled = parameters[param].Update(raw, Utility::Limit(raw)); + source->SetMorph(scaled); + break; + case PARAM_HPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HhSource68::HH_HPF_MIN, HhSource68::HH_HPF_MAX, Parameter::EXPONENTIAL)); + source->SetHpfFrequency(scaled); + break; + case PARAM_LPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HhSource68::HH_HPF_MIN, HhSource68::HH_HPF_MAX, Parameter::EXPONENTIAL)); + source->SetLpfFrequency(scaled); break; } } diff --git a/optic/HachiKit/Ch.h b/optic/HachiKit/Ch.h index 9119083d1..3be7dfaaf 100644 --- a/optic/HachiKit/Ch.h +++ b/optic/HachiKit/Ch.h @@ -8,6 +8,7 @@ #include "ISource.h" #include "Utility.h" #include "Param.h" +#include "HhSource68.h" using namespace daisy; using namespace daisysp; @@ -16,13 +17,17 @@ class Ch: public IDrum { public: // Number of settable parameters for this model. - static const uint8_t PARAM_COUNT = 2; + static const uint8_t PARAM_COUNT = 5; // This is the order params will appear in the UI. static const uint8_t PARAM_ATTACK = 0; static const uint8_t PARAM_DECAY = 1; + static const uint8_t PARAM_MORPH = 2; + static const uint8_t PARAM_HPF = 3; + static const uint8_t PARAM_LPF = 4; void Init(float sample_rate); - void Init(float sample_rate, float attack, float decay, ISource *source); + // void Init(float sample_rate, float attack, float decay, ISource *source); + void Init(float sample_rate, float attack, float decay, HhSource68 *source, float morph, float hpf, float lpf); float Process(); void Trigger(float velocity); @@ -37,11 +42,12 @@ class Ch: public IDrum { std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } private: - std::string paramNames[PARAM_COUNT] = { "Att", "Dec" }; + std::string paramNames[PARAM_COUNT] = { "Atk", "Dcy", "Mrph", "Hpf" }; std::string slot; Param parameters[PARAM_COUNT]; float velocity; - ISource *source = NULL; + // ISource *source = NULL; + HhSource68 *source = NULL; AdEnv env; }; diff --git a/optic/HachiKit/FmDrum.cpp b/optic/HachiKit/FmDrum.cpp index 59d15e46d..0c7fb9e39 100644 --- a/optic/HachiKit/FmDrum.cpp +++ b/optic/HachiKit/FmDrum.cpp @@ -31,6 +31,7 @@ float FmDrum::Process() { void FmDrum::Trigger(float velocity) { this->velocity = Utility::Limit(velocity); if (this->velocity > 0) { + fm.Reset(); ampEnv.Trigger(); } } diff --git a/optic/HachiKit/FmDrum.h b/optic/HachiKit/FmDrum.h index 89ea7760f..b39e8698a 100644 --- a/optic/HachiKit/FmDrum.h +++ b/optic/HachiKit/FmDrum.h @@ -40,7 +40,7 @@ class FmDrum: public IDrum { std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } private: - std::string paramNames[PARAM_COUNT] = { "Freq", "Ratio", "Mod", "Dec", "Att", "Curve" }; + std::string paramNames[PARAM_COUNT] = { "Freq", "Ratio", "Mod", "Dcy", "Atk", "Curve" }; std::string slot; Param parameters[PARAM_COUNT]; float velocity; diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index 0ebf2cfcf..a765003cb 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -29,13 +29,14 @@ Bd8 bd; SdNoise rs; Sd8 sd; FmDrum cp; -Ch ch; +Ch ch, ch2; HhSource68 source68; uint8_t currentDrum = 0; uint8_t currentKnobRow = 0; + void OledMessage(std::string message, int row) { char* mstr = &message[0]; @@ -78,8 +79,9 @@ void DisplayParamMenu() { void ProcessEncoder() { int inc = hw.encoder.Increment(); if (inc != 0) { - currentDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); - drums[currentDrum]->ResetParams(); + int newDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); + drums[newDrum]->ResetParams(); + currentDrum = newDrum; screen.DrawMenu(currentDrum); DisplayParamMenu(); hw.display.Update(); @@ -154,6 +156,8 @@ void AudioCallback(AudioHandle::InputBuffer in, for(size_t i = 0; i < size; i++) { + float src = source68.Process(); + float sig = 0.0f; for (uint8_t i = 0; i < drumCount; i++) { sig += drums[i]->Process(); @@ -161,7 +165,8 @@ void AudioCallback(AudioHandle::InputBuffer in, float limited = sig / drumCount; out[0][i] = out[1][i] = limited; - out[2][i] = out[3][i] = sig; + // out[2][i] = out[3][i] = sig; + out[2][i] = out[3][i] = src; } } @@ -231,22 +236,18 @@ int main(void) drums[2] = &sd; drums[3] = &cp; drums[4] = &ch; - drumCount = 5; + drums[5] = &ch2; + drumCount = 4; currentDrum = 0; for (uint8_t i = 0; i < drumCount; i++) { drums[i]->Init(samplerate); } - // source68.Init(samplerate, 0.5); - // ch.Init(samplerate,0.001, 0.5, &source68); - - // for (u8 i = 0; i < 4; i++) { - // for (u8 j = 1; j < 4; j++) { - // drums[j * 4 + i] = drums[i]; - // } - // } - // drumCount = 16; + drumCount = 6; + source68.Init(samplerate, 0.65); + ch.Init(samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 8000, 8000); + ch2.Init(samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 8000, 8000); //display hw.display.Fill(false); diff --git a/optic/HachiKit/HhSource68.cpp b/optic/HachiKit/HhSource68.cpp index cbc9eb50c..606f2021f 100644 --- a/optic/HachiKit/HhSource68.cpp +++ b/optic/HachiKit/HhSource68.cpp @@ -6,6 +6,11 @@ using namespace daisysp; const float HhSource68::freqs606[OSC_COUNT] = { 245, 306, 365, 415, 437, 619 }; const float HhSource68::freqs808[OSC_COUNT] = { 204, 298, 366, 515, 540, 800 }; const float HhSource68::MIN_FREQ_FACTOR = 0.1; +const float HhSource68::MORPH_606_VALUE = 0.35; +const float HhSource68::MORPH_808_VALUE = 0.65; +const float HhSource68::HH_HPF_MAX = 12000; +const float HhSource68::HH_HPF_MIN = 100; +const float HhSource68::GAIN_MAX = 100; void HhSource68::Init(float sample_rate) { @@ -14,12 +19,29 @@ void HhSource68::Init(float sample_rate) { void HhSource68::Init(float sample_rate, float morph) { + oscs[0] = &osc0; + oscs[1] = &osc1; + oscs[2] = &osc2; + oscs[3] = &osc3; + oscs[4] = &osc4; + oscs[5] = &osc5; + for (int osc = 0; osc < OSC_COUNT; osc++) { - oscs[osc].Init(sample_rate); - oscs[osc].SetWaveform(Oscillator::WAVE_SQUARE); - oscs[osc].SetFreq(freqs808[osc]); + oscs[osc]->Init(sample_rate); + oscs[osc]->SetWaveform(Oscillator::WAVE_SQUARE); + oscs[osc]->SetFreq(freqs808[osc]); } + hpf.Init(sample_rate); + hpf.SetRes(0); + hpf.SetDrive(.002); + hpf.SetFreq(2700); + + lpf.Init(sample_rate); + lpf.SetRes(0); + lpf.SetDrive(.002); + lpf.SetFreq(5000); + SetMorph(morph); signal = 0.0f; } @@ -27,11 +49,15 @@ void HhSource68::Init(float sample_rate, float morph) { float HhSource68::Process() { float sig = 0.0f; for (int osc = 0; osc < OSC_COUNT; osc++) { - sig += oscs[osc].Process(); + sig += oscs[osc]->Process(); } - sig /= OSC_COUNT; + // sig = sig / OSC_COUNT; + + hpf.Process(sig); + lpf.Process(hpf.High()); + // signal = gain * lpf.Low();; + signal = lpf.Low();; - signal = sig; return signal; } @@ -53,8 +79,27 @@ void HhSource68::SetMorph(float morph) { for (int osc = 0; osc < OSC_COUNT; osc++) { float freq = weight6 * freqs606[osc] + weight8 * freqs808[osc]; - freq = std::max(freq, freqs606[osc] * MIN_FREQ_FACTOR); // make sure freqs don't go to zero (and mins aren't all the same) - oscs[osc].SetFreq(freq); + // freq = std::max(freq, freqs606[osc] * MIN_FREQ_FACTOR); // make sure freqs don't go to zero (and mins aren't all the same) + freq = std::max(freq, 200.0f); + oscs[osc]->SetFreq(freq); } +} + +float HhSource68::GetHpfFrequency() { + return hpfFreq; +} + +void HhSource68::SetHpfFrequency(float freq) { + hpfFreq = freq; + hpf.SetFreq(hpfFreq); + gain = Utility::LimitFloat(1.0f + freq * (HH_HPF_MAX - HH_HPF_MIN) * (GAIN_MAX - 1), 1.0f, GAIN_MAX); +} -} \ No newline at end of file +float HhSource68::GetLpfFrequency() { + return lpfFreq; +} + +void HhSource68::SetLpfFrequency(float freq) { + lpfFreq = freq; + lpf.SetFreq(lpfFreq); +} diff --git a/optic/HachiKit/HhSource68.h b/optic/HachiKit/HhSource68.h index 5e2e64437..d6fba9439 100644 --- a/optic/HachiKit/HhSource68.h +++ b/optic/HachiKit/HhSource68.h @@ -15,10 +15,13 @@ class HhSource68: public ISource { static const uint8_t OSC_COUNT = 6; // the morph value at which the sound imitates a 606 - static const uint8_t MORPH_606_VALUE = 0.35; + static const float MORPH_606_VALUE; // the morph value at which the sound imitates an 808 - static const uint8_t MORPH_808_VALUE = 0.65; + static const float MORPH_808_VALUE; + static const float HH_HPF_MAX; + static const float HH_HPF_MIN; + static const float GAIN_MAX; /** Initialize model with default parameters. * \param sample_rate audio sample rate. @@ -33,19 +36,28 @@ class HhSource68: public ISource { float Process(); float Signal(); + float hpfFreq = 2700; + float lpfFreq = 5000; float GetMorph(); void SetMorph(float morph); + float GetHpfFrequency(); + void SetHpfFrequency(float freq); + float GetLpfFrequency(); + void SetLpfFrequency(float freq); private: static const float freqs606[]; static const float freqs808[]; static const float MIN_FREQ_FACTOR; - Oscillator oscs[OSC_COUNT]; float morph; - float frequencies[OSC_COUNT]; float signal; + float gain = 1.0; + + Oscillator* oscs[OSC_COUNT]; + Oscillator osc0, osc1, osc2, osc3, osc4, osc5; + Svf hpf, lpf; }; diff --git a/optic/HachiKit/Sd8.cpp b/optic/HachiKit/Sd8.cpp index 045491e0e..13c50ebad 100644 --- a/optic/HachiKit/Sd8.cpp +++ b/optic/HachiKit/Sd8.cpp @@ -48,6 +48,7 @@ float Sd8::Process() { void Sd8::Trigger(float velocity) { this->velocity = Utility::Limit(velocity); if (this->velocity > 0) { + osc.Reset(); oscEnv.Trigger(); noiseEnv.Trigger(); } From 85e16ce0b6203fc87173f5b124ec99a3e4982dc8 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Mon, 6 May 2024 23:09:39 -0700 Subject: [PATCH 05/23] Made HhSource an IDrum, standardized params --- optic/HachiKit/Ch.cpp | 33 +++++++++--------- optic/HachiKit/Ch.h | 2 +- optic/HachiKit/HhSource68.cpp | 64 +++++++++++++++++++++++++---------- optic/HachiKit/HhSource68.h | 39 ++++++++++++++------- 4 files changed, 91 insertions(+), 47 deletions(-) diff --git a/optic/HachiKit/Ch.cpp b/optic/HachiKit/Ch.cpp index bdca9c76c..4a3ed3de4 100644 --- a/optic/HachiKit/Ch.cpp +++ b/optic/HachiKit/Ch.cpp @@ -45,12 +45,20 @@ void Ch::Trigger(float velocity) { } float Ch::GetParam(uint8_t param) { - // have to refetch source params since they could be changed in other ways - // parameters[PARAM_MORPH].SetScaledValue(source->GetMorph()); - // parameters[PARAM_HPF].SetScaledValue(source->GetHpfFrequency()); - // parameters[PARAM_LPF].SetScaledValue(source->GetLpfFrequency()); - - return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + case PARAM_DECAY: + return parameters[param].GetScaledValue(); + case PARAM_MORPH: + return source->GetParam(HhSource68::PARAM_MORPH); + case PARAM_HPF: + return source->GetParam(HhSource68::PARAM_HPF); + case PARAM_LPF: + return source->GetParam(HhSource68::PARAM_LPF); + } + } + return 0.0f; } std::string Ch::GetParamString(uint8_t param) { @@ -62,10 +70,6 @@ std::string Ch::GetParamString(uint8_t param) { case PARAM_MORPH: case PARAM_HPF: case PARAM_LPF: - // have to refetch source params since they could be changed in other ways - // parameters[PARAM_MORPH].SetScaledValue(source->GetMorph()); - // parameters[PARAM_HPF].SetScaledValue(source->GetHpfFrequency()); - // parameters[PARAM_LPF].SetScaledValue(source->GetLpfFrequency()); return std::to_string((int)GetParam(param)); } } @@ -85,16 +89,13 @@ float Ch::UpdateParam(uint8_t param, float raw) { env.SetTime(ADENV_SEG_DECAY, scaled); break; case PARAM_MORPH: - scaled = parameters[param].Update(raw, Utility::Limit(raw)); - source->SetMorph(scaled); + source->UpdateParam(HhSource68::PARAM_MORPH, raw); break; case PARAM_HPF: - scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HhSource68::HH_HPF_MIN, HhSource68::HH_HPF_MAX, Parameter::EXPONENTIAL)); - source->SetHpfFrequency(scaled); + source->UpdateParam(HhSource68::PARAM_HPF, raw); break; case PARAM_LPF: - scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HhSource68::HH_HPF_MIN, HhSource68::HH_HPF_MAX, Parameter::EXPONENTIAL)); - source->SetLpfFrequency(scaled); + source->UpdateParam(HhSource68::PARAM_LPF, raw); break; } } diff --git a/optic/HachiKit/Ch.h b/optic/HachiKit/Ch.h index 3be7dfaaf..20f4e35eb 100644 --- a/optic/HachiKit/Ch.h +++ b/optic/HachiKit/Ch.h @@ -21,12 +21,12 @@ class Ch: public IDrum { // This is the order params will appear in the UI. static const uint8_t PARAM_ATTACK = 0; static const uint8_t PARAM_DECAY = 1; + // These are pass-thru params that belong to the sound source and aren't tracked in Ch static const uint8_t PARAM_MORPH = 2; static const uint8_t PARAM_HPF = 3; static const uint8_t PARAM_LPF = 4; void Init(float sample_rate); - // void Init(float sample_rate, float attack, float decay, ISource *source); void Init(float sample_rate, float attack, float decay, HhSource68 *source, float morph, float hpf, float lpf); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/HhSource68.cpp b/optic/HachiKit/HhSource68.cpp index 606f2021f..4117f53d6 100644 --- a/optic/HachiKit/HhSource68.cpp +++ b/optic/HachiKit/HhSource68.cpp @@ -61,18 +61,16 @@ float HhSource68::Process() { return signal; } -float HhSource68::Signal() { - return signal; +void HhSource68::Trigger(float velocity) { + // NOP } -float HhSource68::GetMorph() { - return this->morph; +float HhSource68::Signal() { + return signal; } void HhSource68::SetMorph(float morph) { - this->morph = morph; - float range68 = MORPH_808_VALUE - MORPH_606_VALUE; // assumes 8>6 and both betw 0 and 1 float weight8 = (morph - MORPH_606_VALUE) * (1 / range68); float weight6 = 1 - weight8; @@ -85,21 +83,53 @@ void HhSource68::SetMorph(float morph) { } } -float HhSource68::GetHpfFrequency() { - return hpfFreq; +float HhSource68::GetParam(uint8_t param) { + return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; } -void HhSource68::SetHpfFrequency(float freq) { - hpfFreq = freq; - hpf.SetFreq(hpfFreq); - gain = Utility::LimitFloat(1.0f + freq * (HH_HPF_MAX - HH_HPF_MIN) * (GAIN_MAX - 1), 1.0f, GAIN_MAX); +std::string HhSource68::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_MORPH: + return std::to_string((int)GetParam(param * 100)); + case PARAM_HPF: + case PARAM_LPF: + return std::to_string((int)GetParam(param));// + "hz"; + } + } + return ""; + } + +float HhSource68::UpdateParam(uint8_t param, float raw) { + float scaled = raw; + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_MORPH: + scaled = parameters[param].Update(raw, Utility::Limit(raw)); + SetMorph(scaled); + break; + case PARAM_HPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HH_HPF_MIN, HH_HPF_MAX, Parameter::EXPONENTIAL)); + hpf.SetFreq(scaled); + break; + case PARAM_LPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HH_HPF_MIN, HH_HPF_MAX, Parameter::EXPONENTIAL)); + lpf.SetFreq(scaled); + break; + } + } + + return scaled; } -float HhSource68::GetLpfFrequency() { - return lpfFreq; +void HhSource68::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } } -void HhSource68::SetLpfFrequency(float freq) { - lpfFreq = freq; - lpf.SetFreq(lpfFreq); +void HhSource68::SetParam(uint8_t param, float value) { + if (param < PARAM_COUNT) { + parameters[param].SetScaledValue(value); + } } diff --git a/optic/HachiKit/HhSource68.h b/optic/HachiKit/HhSource68.h index d6fba9439..c036ae437 100644 --- a/optic/HachiKit/HhSource68.h +++ b/optic/HachiKit/HhSource68.h @@ -3,14 +3,21 @@ #include "daisy_patch.h" #include "daisysp.h" -#include "ISource.h" +#include "IDrum.h" +#include "Param.h" using namespace daisy; using namespace daisysp; -class HhSource68: public ISource { +class HhSource68: public IDrum { public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 3; + static const uint8_t PARAM_MORPH = 0; + static const uint8_t PARAM_HPF = 1; + static const uint8_t PARAM_LPF = 2; + // how many square waves make up the sound source static const uint8_t OSC_COUNT = 6; @@ -35,30 +42,36 @@ class HhSource68: public ISource { void Init(float sample_rate, float morph); float Process(); + void Trigger(float velocity); float Signal(); - float hpfFreq = 2700; - float lpfFreq = 5000; - float GetMorph(); - void SetMorph(float morph); - float GetHpfFrequency(); - void SetHpfFrequency(float freq); - float GetLpfFrequency(); - void SetLpfFrequency(float freq); + float GetParam(uint8_t param); + std::string GetParamString(uint8_t param); + float UpdateParam(uint8_t param, float value); + void SetParam(uint8_t param, float value); + void ResetParams(); + + std::string Name() { return "Bd8"; } + std::string Slot() { return slot; } + std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } private: static const float freqs606[]; static const float freqs808[]; static const float MIN_FREQ_FACTOR; - float morph; - float signal; - float gain = 1.0; + std::string paramNames[PARAM_COUNT] = { "Mrph", "HPF", "LPF" }; + std::string slot; + Param parameters[PARAM_COUNT]; + float signal = 0.0f; + float gain = 1.0; Oscillator* oscs[OSC_COUNT]; Oscillator osc0, osc1, osc2, osc3, osc4, osc5; Svf hpf, lpf; + void SetMorph(float morph); + }; From e2ce937c106d0e9b5f9e239a7db8105b196af5d1 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Mon, 6 May 2024 23:10:00 -0700 Subject: [PATCH 06/23] Adjusted default params for drums --- optic/HachiKit/Bd8.cpp | 4 ++-- optic/HachiKit/FmDrum.cpp | 2 +- optic/HachiKit/Sd8.cpp | 2 +- optic/HachiKit/SdNoise.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/optic/HachiKit/Bd8.cpp b/optic/HachiKit/Bd8.cpp index 6832fa9a9..22e30d324 100644 --- a/optic/HachiKit/Bd8.cpp +++ b/optic/HachiKit/Bd8.cpp @@ -5,7 +5,7 @@ using namespace daisy; using namespace daisysp; void Bd8::Init(float sample_rate) { - Init(sample_rate, 60, 0.001, 0.5, 0.001, 0.2, 0.1); + Init(sample_rate, 78, 0.001, 4.857, 0.001, 0.1, 0.95); } void Bd8::Init(float sample_rate, float frequency, float ampAttack, float ampDecay, float pitchAttack, float pitchDecay, float modAmount) { @@ -37,7 +37,7 @@ float Bd8::Process() { float psig = pitchEnv.Process(); osc.SetFreq(parameters[PARAM_FREQUENCY].GetScaledValue() + parameters[PARAM_MOD_AMT].GetScaledValue() * psig); // osc.SetFreq(parameters[PARAM_FREQUENCY].GetScaledValue()); - return velocity * osc.Process() * ampEnv.Process(); + return 2 * velocity * osc.Process() * ampEnv.Process(); } void Bd8::Trigger(float velocity) { diff --git a/optic/HachiKit/FmDrum.cpp b/optic/HachiKit/FmDrum.cpp index 0c7fb9e39..00855a4da 100644 --- a/optic/HachiKit/FmDrum.cpp +++ b/optic/HachiKit/FmDrum.cpp @@ -5,7 +5,7 @@ using namespace daisy; using namespace daisysp; void FmDrum::Init(float sample_rate) { - Init(sample_rate, 220, 3, 2, 0.001, 0.5, -50); + Init(sample_rate, 68, 3.3, 2.2, 0.001, 0.043, -50); } void FmDrum::Init(float sample_rate, float frequency, float ratio, float modAmount, float attack, float decay, float curve) { diff --git a/optic/HachiKit/Sd8.cpp b/optic/HachiKit/Sd8.cpp index 13c50ebad..c47ab3a7c 100644 --- a/optic/HachiKit/Sd8.cpp +++ b/optic/HachiKit/Sd8.cpp @@ -8,7 +8,7 @@ const std::string Sd8::paramNames[] = { "oFrq", "oDcy", "nDcy", "Mix", "oAtk", void Sd8::Init(float sample_rate) { - Init(sample_rate, 110, 0.001, 0.5, 0.001, 0.5, 0.5); + Init(sample_rate, 153, 0.001, 1.212, 0.001, 0.971, 0.5); } void Sd8::Init(float sample_rate, float oscFrequency, float oscAttack, float oscDecay, float noiseAttack, float noiseDecay, float mix) { diff --git a/optic/HachiKit/SdNoise.cpp b/optic/HachiKit/SdNoise.cpp index 08dd1759e..59dc49a1a 100644 --- a/optic/HachiKit/SdNoise.cpp +++ b/optic/HachiKit/SdNoise.cpp @@ -5,7 +5,7 @@ using namespace daisy; using namespace daisysp; void SdNoise::Init(float sample_rate) { - Init(sample_rate, 0.01, 0.1, -100.0f); + Init(sample_rate, 0.01, 0.237 -30.0f); } void SdNoise::Init(float sample_rate, float attack, float decay, float curve) { From 1f9b3057e327516d2b3d43ca39c1c934f83812b8 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Fri, 10 May 2024 22:10:42 -0700 Subject: [PATCH 07/23] Added OH and AHD env --- optic/HachiKit/AhdEnv.cpp | 40 ++++++++++++ optic/HachiKit/AhdEnv.h | 29 +++++++++ optic/HachiKit/HachiKit.cpp | 8 ++- optic/HachiKit/HhSource68.cpp | 4 +- optic/HachiKit/Makefile | 4 +- optic/HachiKit/Oh.cpp | 119 ++++++++++++++++++++++++++++++++++ optic/HachiKit/Oh.h | 58 +++++++++++++++++ optic/HachiKit/SdNoise.cpp | 2 +- 8 files changed, 258 insertions(+), 6 deletions(-) create mode 100644 optic/HachiKit/AhdEnv.cpp create mode 100644 optic/HachiKit/AhdEnv.h create mode 100644 optic/HachiKit/Oh.cpp create mode 100644 optic/HachiKit/Oh.h diff --git a/optic/HachiKit/AhdEnv.cpp b/optic/HachiKit/AhdEnv.cpp new file mode 100644 index 000000000..6cbfaf9bc --- /dev/null +++ b/optic/HachiKit/AhdEnv.cpp @@ -0,0 +1,40 @@ +#include "AhdEnv.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +void AhdEnv::Init(float sample_rate) { + ad.Init(sample_rate); + adsr.Init(sample_rate); + adsr.SetTime(ADSR_SEG_DECAY, 0); + adsr.SetSustainLevel(1.0); +} + +float AhdEnv::Process() { + // The AD envelope is used to count off the gate time of the ADSR, providing the hold time. + ad.Process(); + return adsr.Process(ad.IsRunning()); +} + +void AhdEnv::Trigger() { + ad.Trigger(); + adsr.Retrigger(false); +} + +void AhdEnv::SetAttack(float time) { + ad.SetTime(ADENV_SEG_ATTACK, time); + adsr.SetTime(ADSR_SEG_ATTACK, time); +} + +void AhdEnv::SetHold(float time) { + ad.SetTime(ADENV_SEG_DECAY, time); +} + +void AhdEnv::SetDecay(float time) { + adsr.SetTime(ADSR_SEG_RELEASE, time); +} + +bool AhdEnv::IsRunning() const { + return adsr.IsRunning(); +} diff --git a/optic/HachiKit/AhdEnv.h b/optic/HachiKit/AhdEnv.h new file mode 100644 index 000000000..7895cd60e --- /dev/null +++ b/optic/HachiKit/AhdEnv.h @@ -0,0 +1,29 @@ +#ifndef AHDENV_H +#define AHDENV_H + +#include "daisy_patch.h" +#include "daisysp.h" + +using namespace daisy; +using namespace daisysp; + +class AhdEnv { + + public: + void Init(float sample_rate); + float Process(); + void Trigger(); + void SetAttack(float time); + void SetHold(float time); + void SetDecay(float time); + void SetCurve(float scalar); + bool IsRunning() const; + + private: + AdEnv ad; + Adsr adsr; +}; + + + +#endif diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index a765003cb..d5d579285 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -14,6 +14,7 @@ #include "FmDrum.h" #include "HhSource68.h" #include "Ch.h" +#include "Oh.h" using namespace daisy; using namespace daisysp; @@ -29,7 +30,8 @@ Bd8 bd; SdNoise rs; Sd8 sd; FmDrum cp; -Ch ch, ch2; +Ch ch; +Oh oh; HhSource68 source68; @@ -236,7 +238,7 @@ int main(void) drums[2] = &sd; drums[3] = &cp; drums[4] = &ch; - drums[5] = &ch2; + drums[5] = &oh; drumCount = 4; currentDrum = 0; @@ -247,7 +249,7 @@ int main(void) drumCount = 6; source68.Init(samplerate, 0.65); ch.Init(samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 8000, 8000); - ch2.Init(samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 8000, 8000); + oh.Init(samplerate, 0.001, 0.3, 0.2, &source68, HhSource68::MORPH_808_VALUE, 8000, 8000); //display hw.display.Fill(false); diff --git a/optic/HachiKit/HhSource68.cpp b/optic/HachiKit/HhSource68.cpp index 4117f53d6..ac08967a9 100644 --- a/optic/HachiKit/HhSource68.cpp +++ b/optic/HachiKit/HhSource68.cpp @@ -10,6 +10,8 @@ const float HhSource68::MORPH_606_VALUE = 0.35; const float HhSource68::MORPH_808_VALUE = 0.65; const float HhSource68::HH_HPF_MAX = 12000; const float HhSource68::HH_HPF_MIN = 100; +const float HhSource68::HH_LPF_MAX = 18000; +const float HhSource68::HH_LPF_MIN = 100; const float HhSource68::GAIN_MAX = 100; @@ -113,7 +115,7 @@ float HhSource68::UpdateParam(uint8_t param, float raw) { hpf.SetFreq(scaled); break; case PARAM_LPF: - scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HH_HPF_MIN, HH_HPF_MAX, Parameter::EXPONENTIAL)); + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HH_LPF_MIN, HH_LPF_MAX, Parameter::EXPONENTIAL)); lpf.SetFreq(scaled); break; } diff --git a/optic/HachiKit/Makefile b/optic/HachiKit/Makefile index efb68b55b..ac2d077b6 100644 --- a/optic/HachiKit/Makefile +++ b/optic/HachiKit/Makefile @@ -9,7 +9,9 @@ Sd8.cpp \ SdNoise.cpp \ FmDrum.cpp \ HhSource68.cpp \ -Ch.cpp +Ch.cpp \ +AhdEnv.cpp \ +Oh.cpp OPT = -Os diff --git a/optic/HachiKit/Oh.cpp b/optic/HachiKit/Oh.cpp new file mode 100644 index 000000000..f3951abfc --- /dev/null +++ b/optic/HachiKit/Oh.cpp @@ -0,0 +1,119 @@ +#include "Oh.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + + +void Oh::Init(float sample_rate) { + Init(sample_rate, 0.001, 0.3f, 0.2f, NULL, 0.5, 2000, 5000); +} + +void Oh::Init(float sample_rate, float attack, float hold, float decay, HhSource68 *source, float morph, float hpf, float lpf) { + + // env settings + env.Init(sample_rate); + SetParam(PARAM_ATTACK, attack); + SetParam(PARAM_HOLD, hold); + SetParam(PARAM_DECAY, decay); + + // source settings + this->source = source; + SetParam(PARAM_MORPH, morph); + SetParam(PARAM_HPF, hpf); + SetParam(PARAM_LPF, lpf); + +} + +float Oh::Process() { + if (source == NULL) { + return 0.0f; + } + + float sig = source->Signal() * env.Process(); + return velocity * sig; +} + +void Oh::Trigger(float velocity) { + this->velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + env.Trigger(); + } +} + +float Oh::GetParam(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + case PARAM_HOLD: + case PARAM_DECAY: + return parameters[param].GetScaledValue(); + case PARAM_MORPH: + return source->GetParam(HhSource68::PARAM_MORPH); + case PARAM_HPF: + return source->GetParam(HhSource68::PARAM_HPF); + case PARAM_LPF: + return source->GetParam(HhSource68::PARAM_LPF); + } + } + return 0.0f; +} + +std::string Oh::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + case PARAM_HOLD: + case PARAM_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + case PARAM_MORPH: + case PARAM_HPF: + case PARAM_LPF: + return std::to_string((int)GetParam(param)); + } + } + return ""; + } + +float Oh::UpdateParam(uint8_t param, float raw) { + float scaled = raw; + if (param < Oh::PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + env.SetAttack(scaled); + break; + case PARAM_HOLD: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + env.SetHold(scaled); + break; + case PARAM_DECAY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + env.SetDecay(scaled); + break; + case PARAM_MORPH: + source->UpdateParam(HhSource68::PARAM_MORPH, raw); + break; + case PARAM_HPF: + source->UpdateParam(HhSource68::PARAM_HPF, raw); + break; + case PARAM_LPF: + source->UpdateParam(HhSource68::PARAM_LPF, raw); + break; + } + } + + return scaled; +} + +void Oh::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } +} + +void Oh::SetParam(uint8_t param, float value) { + if (param < PARAM_COUNT) { + parameters[param].SetScaledValue(value); + } +} diff --git a/optic/HachiKit/Oh.h b/optic/HachiKit/Oh.h new file mode 100644 index 000000000..388f23e1c --- /dev/null +++ b/optic/HachiKit/Oh.h @@ -0,0 +1,58 @@ +#ifndef OH_H +#define OH_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "ISource.h" +#include "Utility.h" +#include "Param.h" +#include "HhSource68.h" +#include "AhdEnv.h" + +using namespace daisy; +using namespace daisysp; + +class Oh: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 6; + // This is the order params will appear in the UI. + static const uint8_t PARAM_ATTACK = 0; + static const uint8_t PARAM_HOLD = 1; + static const uint8_t PARAM_DECAY = 2; + // These are pass-thru params that belong to the sound source and aren't tracked in Oh + static const uint8_t PARAM_LPF = 3; + static const uint8_t PARAM_MORPH = 4; + static const uint8_t PARAM_HPF = 5; + + void Init(float sample_rate); + void Init(float sample_rate, float attack, float hold, float decay, HhSource68 *source, float morph, float hpf, float lpf); + float Process(); + void Trigger(float velocity); + + float GetParam(uint8_t param); + float UpdateParam(uint8_t param, float value); + void SetParam(uint8_t param, float value); + void ResetParams(); + std::string GetParamString(uint8_t param); + + std::string Name() { return "Blank"; } + std::string Slot() { return slot; } + std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } + + private: + std::string paramNames[PARAM_COUNT] = { "Atk", "Hold", "Dcy", "Lpf" }; + std::string slot; + Param parameters[PARAM_COUNT]; + float velocity; + HhSource68 *source = NULL; + AhdEnv env; + +}; + + + +#endif diff --git a/optic/HachiKit/SdNoise.cpp b/optic/HachiKit/SdNoise.cpp index 59dc49a1a..78a8fc488 100644 --- a/optic/HachiKit/SdNoise.cpp +++ b/optic/HachiKit/SdNoise.cpp @@ -5,7 +5,7 @@ using namespace daisy; using namespace daisysp; void SdNoise::Init(float sample_rate) { - Init(sample_rate, 0.01, 0.237 -30.0f); + Init(sample_rate, 0.01, 0.237, -30.0f); } void SdNoise::Init(float sample_rate, float attack, float decay, float curve) { From 11ffe6fa542fec7e4b3c0d4ad6e8b0a9c3047c3b Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sun, 12 May 2024 22:47:51 -0700 Subject: [PATCH 08/23] Fixed some issues with params not being set correctly & jumping values --- optic/HachiKit/AhdEnv.cpp | 6 +++--- optic/HachiKit/Bd8.cpp | 28 ++++++++++++++++++++++++++-- optic/HachiKit/Ch.cpp | 27 +++++++++++++++++++++++---- optic/HachiKit/FmDrum.cpp | 34 +++++++++++++++++++++++++++++----- optic/HachiKit/HachiKit.cpp | 4 ++-- optic/HachiKit/HhSource68.h | 2 ++ optic/HachiKit/Oh.cpp | 28 ++++++++++++++++++++++++++-- optic/HachiKit/Oh.h | 2 +- optic/HachiKit/Param.h | 2 +- optic/HachiKit/Sd8.cpp | 33 ++++++++++++++++++++++++++++----- optic/HachiKit/SdNoise.cpp | 21 +++++++++++++++++---- 11 files changed, 158 insertions(+), 29 deletions(-) diff --git a/optic/HachiKit/AhdEnv.cpp b/optic/HachiKit/AhdEnv.cpp index 6cbfaf9bc..10486d215 100644 --- a/optic/HachiKit/AhdEnv.cpp +++ b/optic/HachiKit/AhdEnv.cpp @@ -7,8 +7,8 @@ using namespace daisysp; void AhdEnv::Init(float sample_rate) { ad.Init(sample_rate); adsr.Init(sample_rate); - adsr.SetTime(ADSR_SEG_DECAY, 0); - adsr.SetSustainLevel(1.0); + adsr.SetTime(ADSR_SEG_DECAY, 0.001); + adsr.SetSustainLevel(0.5); } float AhdEnv::Process() { @@ -19,7 +19,7 @@ float AhdEnv::Process() { void AhdEnv::Trigger() { ad.Trigger(); - adsr.Retrigger(false); + adsr.Retrigger(true); } void AhdEnv::SetAttack(float time) { diff --git a/optic/HachiKit/Bd8.cpp b/optic/HachiKit/Bd8.cpp index 22e30d324..73f63f9ee 100644 --- a/optic/HachiKit/Bd8.cpp +++ b/optic/HachiKit/Bd8.cpp @@ -107,9 +107,33 @@ void Bd8::ResetParams() { } } -void Bd8::SetParam(uint8_t param, float value) { +void Bd8::SetParam(uint8_t param, float scaled) { if (param < PARAM_COUNT) { - parameters[param].SetScaledValue(value); + switch (param) { + case PARAM_FREQUENCY: + parameters[param].SetScaledValue(scaled); + break; + case PARAM_AMP_ATTACK: + parameters[param].SetScaledValue(scaled); + ampEnv.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_AMP_DECAY: + parameters[param].SetScaledValue(scaled); + ampEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_PITCH_ATTACK: + parameters[param].SetScaledValue(scaled); + pitchEnv.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_PITCH_DECAY: + parameters[param].SetScaledValue(scaled); + pitchEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_MOD_AMT: + parameters[param].SetScaledValue(scaled); + break; + } } + } diff --git a/optic/HachiKit/Ch.cpp b/optic/HachiKit/Ch.cpp index 4a3ed3de4..7cf72e5bf 100644 --- a/optic/HachiKit/Ch.cpp +++ b/optic/HachiKit/Ch.cpp @@ -68,6 +68,7 @@ std::string Ch::GetParamString(uint8_t param) { case PARAM_DECAY: return std::to_string((int)(GetParam(param) * 1000));// + "ms"; case PARAM_MORPH: + return std::to_string((int)(GetParam(param) * 100)); case PARAM_HPF: case PARAM_LPF: return std::to_string((int)GetParam(param)); @@ -107,10 +108,28 @@ void Ch::ResetParams() { for (u8 param = 0; param < PARAM_COUNT; param++) { parameters[param].Reset(); } + source->ResetParams(); } -void Ch::SetParam(uint8_t param, float value) { +void Ch::SetParam(uint8_t param, float scaled) { if (param < PARAM_COUNT) { - parameters[param].SetScaledValue(value); - } -} + switch (param) { + case PARAM_ATTACK: + parameters[param].SetScaledValue(scaled); + env.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_DECAY: + parameters[param].SetScaledValue(scaled); + env.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_MORPH: + source->SetParam(HhSource68::PARAM_MORPH, scaled); + break; + case PARAM_HPF: + source->SetParam(HhSource68::PARAM_HPF, scaled); + break; + case PARAM_LPF: + source->SetParam(HhSource68::PARAM_LPF, scaled); + break; + } + }} diff --git a/optic/HachiKit/FmDrum.cpp b/optic/HachiKit/FmDrum.cpp index 00855a4da..124dfd8cc 100644 --- a/optic/HachiKit/FmDrum.cpp +++ b/optic/HachiKit/FmDrum.cpp @@ -61,7 +61,7 @@ std::string FmDrum::GetParamString(uint8_t param) { float FmDrum::UpdateParam(uint8_t param, float raw) { float scaled = raw; - if (param < FmDrum::PARAM_COUNT) { + if (param < PARAM_COUNT) { switch (param) { case PARAM_FREQUENCY: scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 5000, Parameter::EXPONENTIAL)); @@ -99,8 +99,32 @@ void FmDrum::ResetParams() { } } -void FmDrum::SetParam(uint8_t param, float value) { +void FmDrum::SetParam(uint8_t param, float scaled) { if (param < PARAM_COUNT) { - parameters[param].SetScaledValue(value); - } -} + switch (param) { + case PARAM_FREQUENCY: + parameters[param].SetScaledValue(scaled); + fm.SetFrequency(scaled); + break; + case PARAM_RATIO: + parameters[param].SetScaledValue(scaled); + fm.SetRatio(scaled); + break; + case PARAM_MOD_AMT: + parameters[param].SetScaledValue(scaled); + fm.SetIndex(scaled); + break; + case PARAM_ATTACK: + parameters[param].SetScaledValue(scaled); + ampEnv.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_DECAY: + parameters[param].SetScaledValue(scaled); + ampEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_ENV_CURVE: + parameters[param].SetScaledValue(scaled); + // TODO: set the curve + break; + } + }} diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index d5d579285..d29131484 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -80,8 +80,8 @@ void DisplayParamMenu() { void ProcessEncoder() { int inc = hw.encoder.Increment(); - if (inc != 0) { - int newDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); + int newDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); + if (newDrum != currentDrum) { drums[newDrum]->ResetParams(); currentDrum = newDrum; screen.DrawMenu(currentDrum); diff --git a/optic/HachiKit/HhSource68.h b/optic/HachiKit/HhSource68.h index c036ae437..5c08b99e0 100644 --- a/optic/HachiKit/HhSource68.h +++ b/optic/HachiKit/HhSource68.h @@ -28,6 +28,8 @@ class HhSource68: public IDrum { static const float HH_HPF_MAX; static const float HH_HPF_MIN; + static const float HH_LPF_MAX; + static const float HH_LPF_MIN; static const float GAIN_MAX; /** Initialize model with default parameters. diff --git a/optic/HachiKit/Oh.cpp b/optic/HachiKit/Oh.cpp index f3951abfc..69754ff50 100644 --- a/optic/HachiKit/Oh.cpp +++ b/optic/HachiKit/Oh.cpp @@ -67,6 +67,7 @@ std::string Oh::GetParamString(uint8_t param) { case PARAM_DECAY: return std::to_string((int)(GetParam(param) * 1000));// + "ms"; case PARAM_MORPH: + return std::to_string((int)(GetParam(param) * 100)); case PARAM_HPF: case PARAM_LPF: return std::to_string((int)GetParam(param)); @@ -110,10 +111,33 @@ void Oh::ResetParams() { for (u8 param = 0; param < PARAM_COUNT; param++) { parameters[param].Reset(); } + source->ResetParams(); } -void Oh::SetParam(uint8_t param, float value) { +void Oh::SetParam(uint8_t param, float scaled) { if (param < PARAM_COUNT) { - parameters[param].SetScaledValue(value); + switch (param) { + case PARAM_ATTACK: + parameters[param].SetScaledValue(scaled); + env.SetAttack(scaled); + break; + case PARAM_HOLD: + parameters[param].SetScaledValue(scaled); + env.SetHold(scaled); + break; + case PARAM_DECAY: + parameters[param].SetScaledValue(scaled); + env.SetDecay(scaled); + break; + case PARAM_MORPH: + source->SetParam(HhSource68::PARAM_MORPH, scaled); + break; + case PARAM_HPF: + source->SetParam(HhSource68::PARAM_HPF, scaled); + break; + case PARAM_LPF: + source->SetParam(HhSource68::PARAM_LPF, scaled); + break; + } } } diff --git a/optic/HachiKit/Oh.h b/optic/HachiKit/Oh.h index 388f23e1c..928cb0f5e 100644 --- a/optic/HachiKit/Oh.h +++ b/optic/HachiKit/Oh.h @@ -44,7 +44,7 @@ class Oh: public IDrum { std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } private: - std::string paramNames[PARAM_COUNT] = { "Atk", "Hold", "Dcy", "Lpf" }; + std::string paramNames[PARAM_COUNT] = { "Atk", "Hold", "Dcy", "Lpf", "Mrph", "Hpf" }; std::string slot; Param parameters[PARAM_COUNT]; float velocity; diff --git a/optic/HachiKit/Param.h b/optic/HachiKit/Param.h index 30ca571a2..e4b472435 100644 --- a/optic/HachiKit/Param.h +++ b/optic/HachiKit/Param.h @@ -56,7 +56,7 @@ class Param { */ float Update(float raw, float scaled) { if (!active) { - if (scaled <= this->scaled || raw <= 0.01) lower = true; + if (scaled <= this->scaled || (raw <= 0.01 && raw >= 0.0f)) lower = true; if (scaled >= this->scaled || raw >= 0.99) higher = true; active = (lower && higher); } diff --git a/optic/HachiKit/Sd8.cpp b/optic/HachiKit/Sd8.cpp index c47ab3a7c..8b47086c3 100644 --- a/optic/HachiKit/Sd8.cpp +++ b/optic/HachiKit/Sd8.cpp @@ -77,7 +77,7 @@ std::string Sd8::GetParamString(uint8_t param) { float Sd8::UpdateParam(uint8_t param, float raw) { float scaled = raw; - if (param < Sd8::PARAM_COUNT) { + if (param < PARAM_COUNT) { switch (param) { case PARAM_OSC_FREQUENCY: scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 5000, Parameter::EXPONENTIAL)); @@ -114,8 +114,31 @@ void Sd8::ResetParams() { } } -void Sd8::SetParam(uint8_t param, float value) { +void Sd8::SetParam(uint8_t param, float scaled) { if (param < PARAM_COUNT) { - parameters[param].SetScaledValue(value); - } -} + switch (param) { + case PARAM_OSC_FREQUENCY: + parameters[param].SetScaledValue(scaled); + osc.SetFreq(scaled); + break; + case PARAM_OSC_ATTACK: + parameters[param].SetScaledValue(scaled); + oscEnv.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_OSC_DECAY: + parameters[param].SetScaledValue(scaled); + oscEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_NOISE_ATTACK: + parameters[param].SetScaledValue(scaled); + noiseEnv.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_NOISE_DECAY: + parameters[param].SetScaledValue(scaled); + noiseEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_MIX: + parameters[param].SetScaledValue(scaled); + break; + } + }} diff --git a/optic/HachiKit/SdNoise.cpp b/optic/HachiKit/SdNoise.cpp index 78a8fc488..653a87522 100644 --- a/optic/HachiKit/SdNoise.cpp +++ b/optic/HachiKit/SdNoise.cpp @@ -48,7 +48,7 @@ std::string SdNoise::GetParamString(uint8_t param) { float SdNoise::UpdateParam(uint8_t param, float raw) { float scaled = raw; - if (param < SdNoise::PARAM_COUNT) { + if (param < PARAM_COUNT) { switch (param) { case PARAM_ATTACK: scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); @@ -74,8 +74,21 @@ void SdNoise::ResetParams() { } } -void SdNoise::SetParam(uint8_t param, float value) { +void SdNoise::SetParam(uint8_t param, float scaled) { if (param < PARAM_COUNT) { - parameters[param].SetScaledValue(value); - } + switch (param) { + case PARAM_ATTACK: + parameters[param].SetScaledValue(scaled); + ampEnv.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_DECAY: + parameters[param].SetScaledValue(scaled); + ampEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_CURVE: + parameters[param].SetScaledValue(scaled); + ampEnv.SetCurve(scaled); + break; + } + } } From 70f28c8ac05411f9b881b1c8840b864488b9b2a2 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sat, 18 May 2024 19:02:40 -0700 Subject: [PATCH 09/23] Added Cy, which uses the hh source but adds HP and LP filters --- optic/HachiKit/Ch.h | 2 +- optic/HachiKit/Cy.cpp | 141 ++++++++++++++++++++++++++++++++++ optic/HachiKit/Cy.h | 62 +++++++++++++++ optic/HachiKit/HachiKit.cpp | 16 ++-- optic/HachiKit/HhSource68.cpp | 21 ++++- optic/HachiKit/Makefile | 3 +- optic/HachiKit/Oh.cpp | 2 +- 7 files changed, 234 insertions(+), 13 deletions(-) create mode 100644 optic/HachiKit/Cy.cpp create mode 100644 optic/HachiKit/Cy.h diff --git a/optic/HachiKit/Ch.h b/optic/HachiKit/Ch.h index 20f4e35eb..c071d5c62 100644 --- a/optic/HachiKit/Ch.h +++ b/optic/HachiKit/Ch.h @@ -42,7 +42,7 @@ class Ch: public IDrum { std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } private: - std::string paramNames[PARAM_COUNT] = { "Atk", "Dcy", "Mrph", "Hpf" }; + std::string paramNames[PARAM_COUNT] = { "Atk", "Dcy", "Mrph", "Hpf", "Lpf" }; std::string slot; Param parameters[PARAM_COUNT]; float velocity; diff --git a/optic/HachiKit/Cy.cpp b/optic/HachiKit/Cy.cpp new file mode 100644 index 000000000..a57adb8f4 --- /dev/null +++ b/optic/HachiKit/Cy.cpp @@ -0,0 +1,141 @@ +#include "Cy.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + + +const float Cy::HPF_MAX = 12000; +const float Cy::HPF_MIN = 100; +const float Cy::LPF_MAX = 18000; +const float Cy::LPF_MIN = 100; + + +void Cy::Init(float sample_rate) { + Init(sample_rate, 0.001, 3.5, NULL, 1700, 2300); +} + +// void Cy::Init(float sample_rate, float attack, float decay, ISource *source) { +void Cy::Init(float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff) { + + // env settings + env.Init(sample_rate); + env.SetMax(1); + env.SetMin(0); + env.SetCurve(-0.04); + SetParam(PARAM_ATTACK, attack); + SetParam(PARAM_DECAY, decay); + + hpf.Init(sample_rate); + hpf.SetRes(0.2); + hpf.SetDrive(.002); + hpf.SetFreq(hpfCutoff); + + lpf.Init(sample_rate); + lpf.SetRes(0.2); + lpf.SetDrive(.002); + lpf.SetFreq(lpfCutoff); + + this->source = source; + +} + +float Cy::Process() { + if (source == NULL) { + return 0.0f; + } + + float sig = source->Signal() * env.Process(); + hpf.Process(sig); + lpf.Process(hpf.High()); + return velocity * lpf.Low();; +} + +void Cy::Trigger(float velocity) { + this->velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + env.Trigger(); + } +} + +float Cy::GetParam(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + case PARAM_DECAY: + case PARAM_HPF: + case PARAM_LPF: + return parameters[param].GetScaledValue(); + } + } + return 0.0f; +} + +std::string Cy::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + case PARAM_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + case PARAM_HPF: + case PARAM_LPF: + return std::to_string((int)GetParam(param)); + } + } + return ""; + } + +float Cy::UpdateParam(uint8_t param, float raw) { + float scaled = raw; + if (param < Cy::PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + env.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_DECAY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 15, Parameter::EXPONENTIAL)); + env.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_HPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HPF_MIN, HPF_MAX, Parameter::EXPONENTIAL)); + hpf.SetFreq(scaled); + break; + case PARAM_LPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, LPF_MIN, LPF_MAX, Parameter::EXPONENTIAL)); + lpf.SetFreq(scaled); + break; + } + } + + return scaled; +} + +void Cy::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } + source->ResetParams(); +} + +void Cy::SetParam(uint8_t param, float scaled) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + parameters[param].SetScaledValue(scaled); + env.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_DECAY: + parameters[param].SetScaledValue(scaled); + env.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_HPF: + parameters[param].SetScaledValue(scaled); + hpf.SetFreq(scaled); + break; + case PARAM_LPF: + parameters[param].SetScaledValue(scaled); + lpf.SetFreq(scaled); + break; + } + }} diff --git a/optic/HachiKit/Cy.h b/optic/HachiKit/Cy.h new file mode 100644 index 000000000..d41a1c3f5 --- /dev/null +++ b/optic/HachiKit/Cy.h @@ -0,0 +1,62 @@ +#ifndef CY_H +#define CY_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "ISource.h" +#include "Utility.h" +#include "Param.h" +#include "HhSource68.h" + +using namespace daisy; +using namespace daisysp; + +class Cy: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 4; + // This is the order params will appear in the UI. + static const uint8_t PARAM_ATTACK = 0; + static const uint8_t PARAM_DECAY = 1; + // These are pass-thru params that belong to the sound source and aren't tracked in Ch + static const uint8_t PARAM_HPF = 2; + static const uint8_t PARAM_LPF = 3; + + static const float HPF_MAX; + static const float HPF_MIN; + static const float LPF_MAX; + static const float LPF_MIN; + + void Init(float sample_rate); + void Init(float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff); + float Process(); + void Trigger(float velocity); + + float GetParam(uint8_t param); + float UpdateParam(uint8_t param, float value); + void SetParam(uint8_t param, float value); + void ResetParams(); + std::string GetParamString(uint8_t param); + + std::string Name() { return "Blank"; } + std::string Slot() { return slot; } + std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } + + private: + std::string paramNames[PARAM_COUNT] = { "Atk", "Dcy", "Hpf", "Lpf" }; + std::string slot; + Param parameters[PARAM_COUNT]; + float velocity; + // ISource *source = NULL; + HhSource68 *source = NULL; + AdEnv env; + Svf hpf, lpf; + +}; + + + +#endif diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index d29131484..b81c9e1fb 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -15,6 +15,7 @@ #include "HhSource68.h" #include "Ch.h" #include "Oh.h" +#include "Cy.h" using namespace daisy; using namespace daisysp; @@ -32,6 +33,7 @@ Sd8 sd; FmDrum cp; Ch ch; Oh oh; +Cy cy; HhSource68 source68; @@ -237,8 +239,6 @@ int main(void) drums[1] = &rs; drums[2] = &sd; drums[3] = &cp; - drums[4] = &ch; - drums[5] = &oh; drumCount = 4; currentDrum = 0; @@ -246,10 +246,14 @@ int main(void) drums[i]->Init(samplerate); } - drumCount = 6; - source68.Init(samplerate, 0.65); - ch.Init(samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 8000, 8000); - oh.Init(samplerate, 0.001, 0.3, 0.2, &source68, HhSource68::MORPH_808_VALUE, 8000, 8000); + drums[4] = &ch; + drums[5] = &oh; + drums[6] = &cy; + drumCount = 7; + source68.Init(samplerate, HhSource68::MORPH_808_VALUE); + ch.Init(samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); + oh.Init(samplerate, 0.001, 0.13, 0.05, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); + cy.Init(samplerate, 0.001, 3.5, &source68, 1700, 2400); //display hw.display.Fill(false); diff --git a/optic/HachiKit/HhSource68.cpp b/optic/HachiKit/HhSource68.cpp index ac08967a9..b0c17a3bc 100644 --- a/optic/HachiKit/HhSource68.cpp +++ b/optic/HachiKit/HhSource68.cpp @@ -35,12 +35,12 @@ void HhSource68::Init(float sample_rate, float morph) { } hpf.Init(sample_rate); - hpf.SetRes(0); + hpf.SetRes(0.05); hpf.SetDrive(.002); hpf.SetFreq(2700); lpf.Init(sample_rate); - lpf.SetRes(0); + lpf.SetRes(0.05); lpf.SetDrive(.002); lpf.SetFreq(5000); @@ -130,8 +130,21 @@ void HhSource68::ResetParams() { } } -void HhSource68::SetParam(uint8_t param, float value) { +void HhSource68::SetParam(uint8_t param, float scaled) { if (param < PARAM_COUNT) { - parameters[param].SetScaledValue(value); + switch (param) { + case PARAM_MORPH: + parameters[param].SetScaledValue(scaled); + SetMorph(scaled); + break; + case PARAM_HPF: + parameters[param].SetScaledValue(scaled); + hpf.SetFreq(scaled); + break; + case PARAM_LPF: + parameters[param].SetScaledValue(scaled); + lpf.SetFreq(scaled); + break; + } } } diff --git a/optic/HachiKit/Makefile b/optic/HachiKit/Makefile index ac2d077b6..deb90fe4a 100644 --- a/optic/HachiKit/Makefile +++ b/optic/HachiKit/Makefile @@ -11,7 +11,8 @@ FmDrum.cpp \ HhSource68.cpp \ Ch.cpp \ AhdEnv.cpp \ -Oh.cpp +Oh.cpp \ +Cy.cpp OPT = -Os diff --git a/optic/HachiKit/Oh.cpp b/optic/HachiKit/Oh.cpp index 69754ff50..5af42fac8 100644 --- a/optic/HachiKit/Oh.cpp +++ b/optic/HachiKit/Oh.cpp @@ -6,7 +6,7 @@ using namespace daisysp; void Oh::Init(float sample_rate) { - Init(sample_rate, 0.001, 0.3f, 0.2f, NULL, 0.5, 2000, 5000); + Init(sample_rate, 0.001, 0.13f, 0.05f, NULL, 0.5, 2000, 5000); } void Oh::Init(float sample_rate, float attack, float hold, float decay, HhSource68 *source, float morph, float hpf, float lpf) { From e14d11c73bbb8017f862ac62d530d296be7642c9 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sat, 18 May 2024 21:33:54 -0700 Subject: [PATCH 10/23] Added cowbell using two oscillators from the HH source --- optic/HachiKit/Cow8.cpp | 141 ++++++++++++++++++++++++++++++++++ optic/HachiKit/Cow8.h | 62 +++++++++++++++ optic/HachiKit/Cy.cpp | 2 +- optic/HachiKit/HachiKit.cpp | 6 +- optic/HachiKit/HhSource68.cpp | 15 +++- optic/HachiKit/HhSource68.h | 3 + optic/HachiKit/Makefile | 3 +- 7 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 optic/HachiKit/Cow8.cpp create mode 100644 optic/HachiKit/Cow8.h diff --git a/optic/HachiKit/Cow8.cpp b/optic/HachiKit/Cow8.cpp new file mode 100644 index 000000000..f65868336 --- /dev/null +++ b/optic/HachiKit/Cow8.cpp @@ -0,0 +1,141 @@ +#include "Cow8.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + + +const float Cow8::HPF_MAX = 12000; +const float Cow8::HPF_MIN = 100; +const float Cow8::LPF_MAX = 18000; +const float Cow8::LPF_MIN = 100; + + +void Cow8::Init(float sample_rate) { + Init(sample_rate, 0.001, 3.5, NULL, 1700, 2300); +} + +// void Cow8::Init(float sample_rate, float attack, float decay, ISource *source) { +void Cow8::Init(float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff) { + + // env settings + env.Init(sample_rate); + env.SetMax(1); + env.SetMin(0); + env.SetCurve(-20); + SetParam(PARAM_ATTACK, attack); + SetParam(PARAM_DECAY, decay); + + hpf.Init(sample_rate); + hpf.SetRes(0.2); + hpf.SetDrive(.002); + hpf.SetFreq(hpfCutoff); + + lpf.Init(sample_rate); + lpf.SetRes(0.2); + lpf.SetDrive(.002); + lpf.SetFreq(lpfCutoff); + + this->source = source; + +} + +float Cow8::Process() { + if (source == NULL) { + return 0.0f; + } + + float sig = source->Cowbell(false) * env.Process(); + hpf.Process(sig); + lpf.Process(hpf.High()); + return velocity * lpf.Low();; +} + +void Cow8::Trigger(float velocity) { + this->velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + env.Trigger(); + } +} + +float Cow8::GetParam(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + case PARAM_DECAY: + case PARAM_HPF: + case PARAM_LPF: + return parameters[param].GetScaledValue(); + } + } + return 0.0f; +} + +std::string Cow8::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + case PARAM_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + case PARAM_HPF: + case PARAM_LPF: + return std::to_string((int)GetParam(param)); + } + } + return ""; + } + +float Cow8::UpdateParam(uint8_t param, float raw) { + float scaled = raw; + if (param < Cow8::PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + env.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_DECAY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 15, Parameter::EXPONENTIAL)); + env.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_HPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HPF_MIN, HPF_MAX, Parameter::EXPONENTIAL)); + hpf.SetFreq(scaled); + break; + case PARAM_LPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, LPF_MIN, LPF_MAX, Parameter::EXPONENTIAL)); + lpf.SetFreq(scaled); + break; + } + } + + return scaled; +} + +void Cow8::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } + source->ResetParams(); +} + +void Cow8::SetParam(uint8_t param, float scaled) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_ATTACK: + parameters[param].SetScaledValue(scaled); + env.SetTime(ADENV_SEG_ATTACK, scaled); + break; + case PARAM_DECAY: + parameters[param].SetScaledValue(scaled); + env.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_HPF: + parameters[param].SetScaledValue(scaled); + hpf.SetFreq(scaled); + break; + case PARAM_LPF: + parameters[param].SetScaledValue(scaled); + lpf.SetFreq(scaled); + break; + } + }} diff --git a/optic/HachiKit/Cow8.h b/optic/HachiKit/Cow8.h new file mode 100644 index 000000000..c10023065 --- /dev/null +++ b/optic/HachiKit/Cow8.h @@ -0,0 +1,62 @@ +#ifndef COW8_H +#define COW8_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "ISource.h" +#include "Utility.h" +#include "Param.h" +#include "HhSource68.h" + +using namespace daisy; +using namespace daisysp; + +class Cow8: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 4; + // This is the order params will appear in the UI. + static const uint8_t PARAM_ATTACK = 0; + static const uint8_t PARAM_DECAY = 1; + // These are pass-thru params that belong to the sound source and aren't tracked in Ch + static const uint8_t PARAM_HPF = 2; + static const uint8_t PARAM_LPF = 3; + + static const float HPF_MAX; + static const float HPF_MIN; + static const float LPF_MAX; + static const float LPF_MIN; + + void Init(float sample_rate); + void Init(float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff); + float Process(); + void Trigger(float velocity); + + float GetParam(uint8_t param); + float UpdateParam(uint8_t param, float value); + void SetParam(uint8_t param, float value); + void ResetParams(); + std::string GetParamString(uint8_t param); + + std::string Name() { return "Blank"; } + std::string Slot() { return slot; } + std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } + + private: + std::string paramNames[PARAM_COUNT] = { "Atk", "Dcy", "Hpf", "Lpf" }; + std::string slot; + Param parameters[PARAM_COUNT]; + float velocity; + // ISource *source = NULL; + HhSource68 *source = NULL; + AdEnv env; + Svf hpf, lpf; + +}; + + + +#endif diff --git a/optic/HachiKit/Cy.cpp b/optic/HachiKit/Cy.cpp index a57adb8f4..547943dda 100644 --- a/optic/HachiKit/Cy.cpp +++ b/optic/HachiKit/Cy.cpp @@ -22,7 +22,7 @@ void Cy::Init(float sample_rate, float attack, float decay, HhSource68 *source, env.Init(sample_rate); env.SetMax(1); env.SetMin(0); - env.SetCurve(-0.04); + env.SetCurve(-4); SetParam(PARAM_ATTACK, attack); SetParam(PARAM_DECAY, decay); diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index b81c9e1fb..b0c799c77 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -16,6 +16,7 @@ #include "Ch.h" #include "Oh.h" #include "Cy.h" +#include "Cow8.h" using namespace daisy; using namespace daisysp; @@ -34,6 +35,7 @@ FmDrum cp; Ch ch; Oh oh; Cy cy; +Cow8 cb; HhSource68 source68; @@ -249,11 +251,13 @@ int main(void) drums[4] = &ch; drums[5] = &oh; drums[6] = &cy; - drumCount = 7; + drums[7] = &cb; + drumCount = 8; source68.Init(samplerate, HhSource68::MORPH_808_VALUE); ch.Init(samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); oh.Init(samplerate, 0.001, 0.13, 0.05, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); cy.Init(samplerate, 0.001, 3.5, &source68, 1700, 2400); + cb.Init(samplerate, 0.001, 0.5, &source68, 1700, 2400); //display hw.display.Fill(false); diff --git a/optic/HachiKit/HhSource68.cpp b/optic/HachiKit/HhSource68.cpp index b0c17a3bc..2bd9d6ea8 100644 --- a/optic/HachiKit/HhSource68.cpp +++ b/optic/HachiKit/HhSource68.cpp @@ -49,9 +49,16 @@ void HhSource68::Init(float sample_rate, float morph) { } float HhSource68::Process() { - float sig = 0.0f; + float sig = cowSignal = lowCowSignal = 0.0f; for (int osc = 0; osc < OSC_COUNT; osc++) { - sig += oscs[osc]->Process(); + float oscSignal = oscs[osc]->Process(); + sig += oscSignal; + if (osc == 4 || osc == 5) { + cowSignal += oscSignal; + } + if (osc == 3 || osc == 4) { + lowCowSignal += oscSignal; + } } // sig = sig / OSC_COUNT; @@ -71,6 +78,10 @@ float HhSource68::Signal() { return signal; } +float HhSource68::Cowbell(bool isLow) { + return isLow ? lowCowSignal : cowSignal; +} + void HhSource68::SetMorph(float morph) { float range68 = MORPH_808_VALUE - MORPH_606_VALUE; // assumes 8>6 and both betw 0 and 1 diff --git a/optic/HachiKit/HhSource68.h b/optic/HachiKit/HhSource68.h index 5c08b99e0..2e1dd9f10 100644 --- a/optic/HachiKit/HhSource68.h +++ b/optic/HachiKit/HhSource68.h @@ -46,6 +46,7 @@ class HhSource68: public IDrum { float Process(); void Trigger(float velocity); float Signal(); + float Cowbell(bool isLow); float GetParam(uint8_t param); std::string GetParamString(uint8_t param); @@ -67,6 +68,8 @@ class HhSource68: public IDrum { Param parameters[PARAM_COUNT]; float signal = 0.0f; + float cowSignal = 0.0f; + float lowCowSignal = 0.0f; float gain = 1.0; Oscillator* oscs[OSC_COUNT]; Oscillator osc0, osc1, osc2, osc3, osc4, osc5; diff --git a/optic/HachiKit/Makefile b/optic/HachiKit/Makefile index deb90fe4a..b357fece6 100644 --- a/optic/HachiKit/Makefile +++ b/optic/HachiKit/Makefile @@ -12,7 +12,8 @@ HhSource68.cpp \ Ch.cpp \ AhdEnv.cpp \ Oh.cpp \ -Cy.cpp +Cy.cpp \ +Cow8.cpp OPT = -Os From dfb881af7f2c0973f6d663f84845b24aa9e1fe0f Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sun, 19 May 2024 22:42:27 -0700 Subject: [PATCH 11/23] Adding Tom sound; hitting processing limit? --- optic/HachiKit/Cy.cpp | 4 +- optic/HachiKit/HachiKit.cpp | 37 ++++--- optic/HachiKit/HhSource68.cpp | 12 +-- optic/HachiKit/HhSource68.h | 8 +- optic/HachiKit/Makefile | 3 +- optic/HachiKit/Tom.cpp | 176 ++++++++++++++++++++++++++++++++++ optic/HachiKit/Tom.h | 57 +++++++++++ 7 files changed, 270 insertions(+), 27 deletions(-) create mode 100644 optic/HachiKit/Tom.cpp create mode 100644 optic/HachiKit/Tom.h diff --git a/optic/HachiKit/Cy.cpp b/optic/HachiKit/Cy.cpp index 547943dda..f9486b688 100644 --- a/optic/HachiKit/Cy.cpp +++ b/optic/HachiKit/Cy.cpp @@ -29,12 +29,12 @@ void Cy::Init(float sample_rate, float attack, float decay, HhSource68 *source, hpf.Init(sample_rate); hpf.SetRes(0.2); hpf.SetDrive(.002); - hpf.SetFreq(hpfCutoff); + SetParam(PARAM_HPF, hpfCutoff); lpf.Init(sample_rate); lpf.SetRes(0.2); lpf.SetDrive(.002); - lpf.SetFreq(lpfCutoff); + SetParam(PARAM_LPF, lpfCutoff); this->source = source; diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index b0c799c77..981a8766b 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -17,6 +17,7 @@ #include "Oh.h" #include "Cy.h" #include "Cow8.h" +#include "Tom.h" using namespace daisy; using namespace daisysp; @@ -36,6 +37,7 @@ Ch ch; Oh oh; Cy cy; Cow8 cb; +Tom lt, mt, ht; HhSource68 source68; @@ -237,28 +239,35 @@ int main(void) hw.Init(); samplerate = hw.AudioSampleRate(); - drums[0] = &bd; - drums[1] = &rs; - drums[2] = &sd; - drums[3] = &cp; - drumCount = 4; - currentDrum = 0; + bd.Init(samplerate); + rs.Init(samplerate); + sd.Init(samplerate); + cp.Init(samplerate); - for (uint8_t i = 0; i < drumCount; i++) { - drums[i]->Init(samplerate); - } + lt.Init(samplerate, 80); + mt.Init(samplerate, 91); + ht.Init(samplerate, 106); - drums[4] = &ch; - drums[5] = &oh; - drums[6] = &cy; - drums[7] = &cb; - drumCount = 8; source68.Init(samplerate, HhSource68::MORPH_808_VALUE); ch.Init(samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); oh.Init(samplerate, 0.001, 0.13, 0.05, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); cy.Init(samplerate, 0.001, 3.5, &source68, 1700, 2400); cb.Init(samplerate, 0.001, 0.5, &source68, 1700, 2400); + drums[0] = &bd; + drums[1] = &rs; + drums[2] = &sd; + drums[3] = &cp; + drums[4] = < + drums[5] = &mt; + drums[6] = &ch; + // drums[7] = &ht; + drums[7] = &oh; + // drums[9] = &cb; + drums[8] = &cy; + drumCount = 9; + currentDrum = 0; + //display hw.display.Fill(false); // OledMessage("Hachikit 0.1", 5); diff --git a/optic/HachiKit/HhSource68.cpp b/optic/HachiKit/HhSource68.cpp index 2bd9d6ea8..648452d31 100644 --- a/optic/HachiKit/HhSource68.cpp +++ b/optic/HachiKit/HhSource68.cpp @@ -8,10 +8,10 @@ const float HhSource68::freqs808[OSC_COUNT] = { 204, 298, 366, 515, 540, 800 }; const float HhSource68::MIN_FREQ_FACTOR = 0.1; const float HhSource68::MORPH_606_VALUE = 0.35; const float HhSource68::MORPH_808_VALUE = 0.65; -const float HhSource68::HH_HPF_MAX = 12000; -const float HhSource68::HH_HPF_MIN = 100; -const float HhSource68::HH_LPF_MAX = 18000; -const float HhSource68::HH_LPF_MIN = 100; +const float HhSource68::HPF_MAX = 12000; +const float HhSource68::HPF_MIN = 100; +const float HhSource68::LPF_MAX = 18000; +const float HhSource68::LPF_MIN = 100; const float HhSource68::GAIN_MAX = 100; @@ -122,11 +122,11 @@ float HhSource68::UpdateParam(uint8_t param, float raw) { SetMorph(scaled); break; case PARAM_HPF: - scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HH_HPF_MIN, HH_HPF_MAX, Parameter::EXPONENTIAL)); + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HPF_MIN, HPF_MAX, Parameter::EXPONENTIAL)); hpf.SetFreq(scaled); break; case PARAM_LPF: - scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, HH_LPF_MIN, HH_LPF_MAX, Parameter::EXPONENTIAL)); + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, LPF_MIN, LPF_MAX, Parameter::EXPONENTIAL)); lpf.SetFreq(scaled); break; } diff --git a/optic/HachiKit/HhSource68.h b/optic/HachiKit/HhSource68.h index 2e1dd9f10..84ba2c786 100644 --- a/optic/HachiKit/HhSource68.h +++ b/optic/HachiKit/HhSource68.h @@ -26,10 +26,10 @@ class HhSource68: public IDrum { // the morph value at which the sound imitates an 808 static const float MORPH_808_VALUE; - static const float HH_HPF_MAX; - static const float HH_HPF_MIN; - static const float HH_LPF_MAX; - static const float HH_LPF_MIN; + static const float HPF_MAX; + static const float HPF_MIN; + static const float LPF_MAX; + static const float LPF_MIN; static const float GAIN_MAX; /** Initialize model with default parameters. diff --git a/optic/HachiKit/Makefile b/optic/HachiKit/Makefile index b357fece6..f332fdd02 100644 --- a/optic/HachiKit/Makefile +++ b/optic/HachiKit/Makefile @@ -13,7 +13,8 @@ Ch.cpp \ AhdEnv.cpp \ Oh.cpp \ Cy.cpp \ -Cow8.cpp +Cow8.cpp \ +Tom.cpp OPT = -Os diff --git a/optic/HachiKit/Tom.cpp b/optic/HachiKit/Tom.cpp new file mode 100644 index 000000000..02ee85c91 --- /dev/null +++ b/optic/HachiKit/Tom.cpp @@ -0,0 +1,176 @@ +#include "Tom.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + + +void Tom::Init(float sample_rate) { + Init(sample_rate, 80); +} + +void Tom::Init(float sample_rate, float frequency) { + + osc.Init(sample_rate); + SetParam(PARAM_FREQUENCY, frequency); + osc.SetWaveform(Oscillator::WAVE_POLYBLEP_TRI); + + vibratoOsc.Init(sample_rate); + vibratoOsc.SetWaveform(Oscillator::WAVE_SIN); + vibratoOsc.SetFreq(20); + + noise.Init(); + + ampEnv.Init(sample_rate); + ampEnv.SetMax(1); + ampEnv.SetMin(0); + ampEnv.SetCurve(-10); + ampEnv.SetTime(ADENV_SEG_ATTACK, 0.001); + SetParam(PARAM_AMP_DECAY, 1.1); + SetParam(PARAM_PITCH_MOD, 60); + + lpfEnv.Init(sample_rate); + lpfEnv.SetMax(1); + lpfEnv.SetMin(0); + lpfEnv.SetCurve(-20); + lpfEnv.SetTime(ADENV_SEG_ATTACK, 0.001); + lpfEnv.SetTime(ADENV_SEG_DECAY, 0.09); + + pitchEnv.Init(sample_rate); + pitchEnv.SetMax(1); + pitchEnv.SetMin(0); + pitchEnv.SetCurve(-20); + pitchEnv.SetTime(ADENV_SEG_ATTACK, 0.001); + pitchEnv.SetTime(ADENV_SEG_DECAY, 0.25); + + hpf.Init(sample_rate); + hpf.SetRes(0.2); + hpf.SetDrive(.002); + SetParam(PARAM_HPF, 1500); + + lpf.Init(sample_rate); + lpf.SetRes(0.2); + lpf.SetDrive(.002); + SetParam(PARAM_LPF, 191); + SetParam(PARAM_LPF_MOD, 116); + + +} + +float Tom::Process() { + + // noise goes through a hpf, then through an lpf modulated by the lpf env. + // lpf env also controls amp of noise + float lpfEnvSignal = lpfEnv.Process(); + lpf.SetFreq(1000 + parameters[PARAM_LPF_MOD].GetScaledValue() * lpfEnvSignal); + hpf.Process(noise.Process()); + lpf.Process(hpf.High()); + float noiseSignal = lpf.Low() * lpfEnvSignal; + + // sine osc freq is modulated by pitch env, amp by amp env + float pitchEnvSignal = pitchEnv.Process(); + float ampEnvSignal = ampEnv.Process(); + // TODO: including vibrato seems to crash the Daisy if there are 3 toms + // float vibratoSignal = vibratoOsc.Process(); + // osc.SetFreq(parameters[PARAM_FREQUENCY].GetScaledValue() + parameters[PARAM_PITCH_MOD].GetScaledValue() * pitchEnvSignal + 3 * vibratoSignal); + osc.SetFreq(parameters[PARAM_FREQUENCY].GetScaledValue() + parameters[PARAM_PITCH_MOD].GetScaledValue() * pitchEnvSignal); + float oscSignal = osc.Process() * ampEnvSignal; + + // TODO: figure out why this wasn't working + // // // apply velocity scaling more strong to noise than osc + // // float signal = noiseSignal * velocity + oscSignal * (0.4 + velocity * 0.6); + // // return signal; + return (noiseSignal + oscSignal) * velocity; +} + +void Tom::Trigger(float velocity) { + this->velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + ampEnv.Trigger(); + lpfEnv.Trigger(); + pitchEnv.Trigger(); + osc.Reset(); + vibratoOsc.Reset(); + } +} + +float Tom::GetParam(uint8_t param) { + return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; +} + +std::string Tom::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_FREQUENCY: + case PARAM_PITCH_MOD: + case PARAM_LPF_MOD: + case PARAM_HPF: + case PARAM_LPF: + return std::to_string((int)GetParam(param)); + case PARAM_AMP_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + } + } + return ""; + } + +float Tom::UpdateParam(uint8_t param, float raw) { + float scaled = raw; + if (param < Tom::PARAM_COUNT) { + switch (param) { + case PARAM_FREQUENCY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 1000, Parameter::EXPONENTIAL)); + break; + case PARAM_AMP_DECAY: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0.01, 5, Parameter::EXPONENTIAL)); + ampEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_PITCH_MOD: + case PARAM_LPF_MOD: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0, 2000, Parameter::EXPONENTIAL)); + break; + case PARAM_HPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 3000, Parameter::EXPONENTIAL)); + hpf.SetFreq(scaled); + break; + case PARAM_LPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 3000, Parameter::EXPONENTIAL)); + lpf.SetFreq(scaled); + break; + } + } + + return scaled; +} + +void Tom::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } +} + +void Tom::SetParam(uint8_t param, float scaled) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_FREQUENCY: + parameters[param].SetScaledValue(scaled); + break; + case PARAM_AMP_DECAY: + parameters[param].SetScaledValue(scaled); + ampEnv.SetTime(ADENV_SEG_DECAY, scaled); + break; + case PARAM_PITCH_MOD: + case PARAM_LPF_MOD: + parameters[param].SetScaledValue(scaled); + break; + case PARAM_HPF: + parameters[param].SetScaledValue(scaled); + hpf.SetFreq(scaled); + break; + case PARAM_LPF: + parameters[param].SetScaledValue(scaled); + lpf.SetFreq(scaled); + break; + } + } +} diff --git a/optic/HachiKit/Tom.h b/optic/HachiKit/Tom.h new file mode 100644 index 000000000..787f8550b --- /dev/null +++ b/optic/HachiKit/Tom.h @@ -0,0 +1,57 @@ +#ifndef TOM_H +#define TOM_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "ISource.h" +#include "Utility.h" +#include "Param.h" + +using namespace daisy; +using namespace daisysp; + +class Tom: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 6; + // This is the order params will appear in the UI. + static const uint8_t PARAM_FREQUENCY = 0; + static const uint8_t PARAM_AMP_DECAY = 1; + static const uint8_t PARAM_PITCH_MOD = 2; + static const uint8_t PARAM_LPF_MOD = 3; + static const uint8_t PARAM_HPF = 4; + static const uint8_t PARAM_LPF = 5; + + void Init(float sample_rate); + void Init(float sample_rate, float frequency); + float Process(); + void Trigger(float velocity); + + float GetParam(uint8_t param); + float UpdateParam(uint8_t param, float value); + void SetParam(uint8_t param, float value); + void ResetParams(); + std::string GetParamString(uint8_t param); + + std::string Name() { return "Tom"; } + std::string Slot() { return slot; } + std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } + + private: + std::string paramNames[PARAM_COUNT] = { "Freq", "aDcy", "pMod", "fMod", "Hpf", "Lpf" }; + std::string slot; + Param parameters[PARAM_COUNT]; + float velocity; + Oscillator osc, vibratoOsc; + WhiteNoise noise; + Svf hpf, lpf; + AdEnv ampEnv, lpfEnv, pitchEnv; + +}; + + + +#endif From 6b86280950ca7c360fff932ff776c4f70f372417 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Tue, 21 May 2024 10:14:30 -0700 Subject: [PATCH 12/23] Update README.md --- optic/HachiKit/README.md | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/optic/HachiKit/README.md b/optic/HachiKit/README.md index 49e9c8ac6..422a4f53a 100644 --- a/optic/HachiKit/README.md +++ b/optic/HachiKit/README.md @@ -1,20 +1,10 @@ -# Midi +# HachiKit ## Description -Control a synth voice over Midi. +A 16-voice virtual analog drumkit. -[Source Code](https://github.com/electro-smith/DaisyExamples/tree/master/patch/Midi) ## Author -Shensley - -## Controls -| Control | Description | -| --- | --- | -| Midi CC 1 | Filter Cutoff | -| Midi CC 2 | Filter Resonance | -| Audio Outs | Oscillator Output| - - +Perkowitz From fe4e601405c4587174d1409c3266d4392e8d9fdb Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Tue, 21 May 2024 23:15:50 -0700 Subject: [PATCH 13/23] Added CpuLoadMeter and incremental loading of drums to track CPU usage --- optic/HachiKit/HachiKit.cpp | 47 +++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index 981a8766b..ce8ad6e6c 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -3,6 +3,7 @@ #include "daisy_patch.h" #include "daisysp.h" +#include "util/CpuLoadMeter.h" #include #include "Utility.h" #include "BitArray.h" @@ -40,9 +41,11 @@ Cow8 cb; Tom lt, mt, ht; HhSource68 source68; +CpuLoadMeter meter; uint8_t currentDrum = 0; uint8_t currentKnobRow = 0; +u8 maxDrum = 1; void OledMessage(std::string message, int row) @@ -86,8 +89,12 @@ void DisplayParamMenu() { void ProcessEncoder() { int inc = hw.encoder.Increment(); - int newDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); + // int newDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); + int newDrum = Utility::LimitInt(currentDrum + inc, 0, maxDrum-1); if (newDrum != currentDrum) { + if (newDrum < maxDrum) { + drumCount = newDrum + 1; + } drums[newDrum]->ResetParams(); currentDrum = newDrum; screen.DrawMenu(currentDrum); @@ -158,6 +165,7 @@ void AudioCallback(AudioHandle::InputBuffer in, AudioHandle::OutputBuffer out, size_t size) { + meter.OnBlockStart(); ProcessControls(); @@ -177,6 +185,8 @@ void AudioCallback(AudioHandle::InputBuffer in, out[2][i] = out[3][i] = src; } + + meter.OnBlockEnd(); } @@ -239,6 +249,8 @@ int main(void) hw.Init(); samplerate = hw.AudioSampleRate(); + meter.Init(samplerate, 128, 1.0f); + bd.Init(samplerate); rs.Init(samplerate); sd.Init(samplerate); @@ -254,20 +266,23 @@ int main(void) cy.Init(samplerate, 0.001, 3.5, &source68, 1700, 2400); cb.Init(samplerate, 0.001, 0.5, &source68, 1700, 2400); - drums[0] = &bd; - drums[1] = &rs; - drums[2] = &sd; - drums[3] = &cp; - drums[4] = < - drums[5] = &mt; - drums[6] = &ch; - // drums[7] = &ht; - drums[7] = &oh; - // drums[9] = &cb; - drums[8] = &cy; - drumCount = 9; + drums[0] = &bd; //15? + drums[1] = &rs; //4 + drums[2] = &sd; //7 + drums[3] = &cp; //7 + drums[4] = < //19 + drums[5] = &mt; //19 + drums[6] = &ch; //3 + drums[7] = &ht; //19 + drums[8] = &oh; //2 + drums[9] = &cb; // + drums[10] = &cy; // + drumCount = 10; currentDrum = 0; + maxDrum = 11; + drumCount = 1; + //display hw.display.Fill(false); // OledMessage("Hachikit 0.1", 5); @@ -281,7 +296,6 @@ int main(void) hw.midi.StartReceive(); hw.StartAdc(); hw.StartAudio(AudioCallback); - int c = 0; for(;;) { hw.midi.Listen(); @@ -291,7 +305,10 @@ int main(void) HandleMidiMessage(hw.midi.PopEvent()); } DisplayKnobValues(); + + float avgCpu = meter.GetAvgCpuLoad(); + OledMessage("cpu:" + std::to_string((int)(avgCpu * 100)) + "%", 4); + hw.display.Update(); - c++; } } From 4757a693d81d504afe70494084c7ce5c1566afb4 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Tue, 21 May 2024 23:59:26 -0700 Subject: [PATCH 14/23] Pulled the click part of the Tom out to a shared ClickSource, reducing CPU usage --- optic/HachiKit/ClickSource.cpp | 112 +++++++++++++++++++++++++++++++++ optic/HachiKit/ClickSource.h | 55 ++++++++++++++++ optic/HachiKit/HachiKit.cpp | 26 +++++--- optic/HachiKit/Makefile | 3 +- optic/HachiKit/Tom.cpp | 85 +++++++++++-------------- optic/HachiKit/Tom.h | 9 +-- 6 files changed, 229 insertions(+), 61 deletions(-) create mode 100644 optic/HachiKit/ClickSource.cpp create mode 100644 optic/HachiKit/ClickSource.h diff --git a/optic/HachiKit/ClickSource.cpp b/optic/HachiKit/ClickSource.cpp new file mode 100644 index 000000000..58abf3a47 --- /dev/null +++ b/optic/HachiKit/ClickSource.cpp @@ -0,0 +1,112 @@ +#include "ClickSource.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + + +void ClickSource::Init(float sample_rate) { + Init(sample_rate, 1500, 191, 116); +} + +void ClickSource::Init(float sample_rate, float hpfFreq, float lpfFreq, float mod) { + + noise.Init(); + + lpfEnv.Init(sample_rate); + lpfEnv.SetMax(1); + lpfEnv.SetMin(0); + lpfEnv.SetCurve(-20); + lpfEnv.SetTime(ADENV_SEG_ATTACK, 0.001); + lpfEnv.SetTime(ADENV_SEG_DECAY, 0.09); + + hpf.Init(sample_rate); + hpf.SetRes(0.2); + hpf.SetDrive(.002); + SetParam(PARAM_HPF, hpfFreq); + + lpf.Init(sample_rate); + lpf.SetRes(0.2); + lpf.SetDrive(.002); + SetParam(PARAM_LPF, lpfFreq); + SetParam(PARAM_LPF_MOD, mod); +} + +float ClickSource::Process() { + // noise goes through a hpf, then through an lpf modulated by the lpf env. + // lpf env also controls amp of noise + float lpfEnvSignal = lpfEnv.Process(); + lpf.SetFreq(1000 + parameters[PARAM_LPF_MOD].GetScaledValue() * lpfEnvSignal); + hpf.Process(noise.Process()); + lpf.Process(hpf.High()); + signal = lpf.Low() * lpfEnvSignal; + return signal; +} + +void ClickSource::Trigger(float velocity) { + this->velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + lpfEnv.Trigger(); + } +} + +float ClickSource::GetParam(uint8_t param) { + return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; +} + +std::string ClickSource::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_LPF_MOD: + case PARAM_HPF: + case PARAM_LPF: + return std::to_string((int)GetParam(param)); + } + } + return ""; + } + +float ClickSource::UpdateParam(uint8_t param, float raw) { + float scaled = raw; + if (param < ClickSource::PARAM_COUNT) { + switch (param) { + case PARAM_LPF_MOD: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0, 2000, Parameter::EXPONENTIAL)); + break; + case PARAM_HPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 3000, Parameter::EXPONENTIAL)); + hpf.SetFreq(scaled); + break; + case PARAM_LPF: + scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 3000, Parameter::EXPONENTIAL)); + lpf.SetFreq(scaled); + break; + } + } + + return scaled; +} + +void ClickSource::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } +} + +void ClickSource::SetParam(uint8_t param, float scaled) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_LPF_MOD: + parameters[param].SetScaledValue(scaled); + break; + case PARAM_HPF: + parameters[param].SetScaledValue(scaled); + hpf.SetFreq(scaled); + break; + case PARAM_LPF: + parameters[param].SetScaledValue(scaled); + lpf.SetFreq(scaled); + break; + } + } +} diff --git a/optic/HachiKit/ClickSource.h b/optic/HachiKit/ClickSource.h new file mode 100644 index 000000000..69c6ab2af --- /dev/null +++ b/optic/HachiKit/ClickSource.h @@ -0,0 +1,55 @@ +#ifndef CLICKSOURCE_H +#define CLICKSOURCE_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "ISource.h" +#include "Utility.h" +#include "Param.h" + +using namespace daisy; +using namespace daisysp; + +class ClickSource: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 3; + // This is the order params will appear in the UI. + static const uint8_t PARAM_HPF = 0; + static const uint8_t PARAM_LPF = 1; + static const uint8_t PARAM_LPF_MOD = 2; + + void Init(float sample_rate); + void Init(float sample_rate, float hpfFreq, float lpfFreq, float mod); + float Process(); + void Trigger(float velocity); + float Signal() { return signal; } + + float GetParam(uint8_t param); + float UpdateParam(uint8_t param, float value); + void SetParam(uint8_t param, float value); + void ResetParams(); + std::string GetParamString(uint8_t param); + + std::string Name() { return "Tom"; } + std::string Slot() { return slot; } + std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } + + private: + std::string paramNames[PARAM_COUNT] = { "Hpf", "Lpf", "fMod" }; + std::string slot; + Param parameters[PARAM_COUNT]; + float velocity; + float signal; + WhiteNoise noise; + Svf hpf, lpf; + AdEnv lpfEnv; + +}; + + + +#endif diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index ce8ad6e6c..6bb11346d 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -14,6 +14,7 @@ #include "SdNoise.h" #include "FmDrum.h" #include "HhSource68.h" +#include "ClickSource.h" #include "Ch.h" #include "Oh.h" #include "Cy.h" @@ -28,6 +29,8 @@ using namespace daisysp; DaisyPatch hw; Screen screen(&hw.display); +CpuLoadMeter meter; + IDrum *drums[16]; uint8_t drumCount = 4; Bd8 bd; @@ -40,8 +43,9 @@ Cy cy; Cow8 cb; Tom lt, mt, ht; +// Shared sound sources HhSource68 source68; -CpuLoadMeter meter; +ClickSource clickSource; uint8_t currentDrum = 0; uint8_t currentKnobRow = 0; @@ -172,7 +176,9 @@ void AudioCallback(AudioHandle::InputBuffer in, for(size_t i = 0; i < size; i++) { - float src = source68.Process(); + // Process shared sound sources + source68.Process(); + clickSource.Process(); float sig = 0.0f; for (uint8_t i = 0; i < drumCount; i++) { @@ -181,8 +187,7 @@ void AudioCallback(AudioHandle::InputBuffer in, float limited = sig / drumCount; out[0][i] = out[1][i] = limited; - // out[2][i] = out[3][i] = sig; - out[2][i] = out[3][i] = src; + out[2][i] = out[3][i] = sig; } @@ -251,16 +256,19 @@ int main(void) meter.Init(samplerate, 128, 1.0f); + // Shared sound sources + source68.Init(samplerate, HhSource68::MORPH_808_VALUE); + clickSource.Init(samplerate, 1500, 191, 116); + bd.Init(samplerate); rs.Init(samplerate); sd.Init(samplerate); cp.Init(samplerate); - lt.Init(samplerate, 80); - mt.Init(samplerate, 91); - ht.Init(samplerate, 106); + lt.Init(samplerate, 80, &clickSource); + mt.Init(samplerate, 91, &clickSource); + ht.Init(samplerate, 106, &clickSource); - source68.Init(samplerate, HhSource68::MORPH_808_VALUE); ch.Init(samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); oh.Init(samplerate, 0.001, 0.13, 0.05, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); cy.Init(samplerate, 0.001, 3.5, &source68, 1700, 2400); @@ -277,7 +285,7 @@ int main(void) drums[8] = &oh; //2 drums[9] = &cb; // drums[10] = &cy; // - drumCount = 10; + drumCount = 11; currentDrum = 0; maxDrum = 11; diff --git a/optic/HachiKit/Makefile b/optic/HachiKit/Makefile index f332fdd02..47a7d5c7d 100644 --- a/optic/HachiKit/Makefile +++ b/optic/HachiKit/Makefile @@ -14,7 +14,8 @@ AhdEnv.cpp \ Oh.cpp \ Cy.cpp \ Cow8.cpp \ -Tom.cpp +Tom.cpp \ +ClickSource.cpp OPT = -Os diff --git a/optic/HachiKit/Tom.cpp b/optic/HachiKit/Tom.cpp index 02ee85c91..4a80474f5 100644 --- a/optic/HachiKit/Tom.cpp +++ b/optic/HachiKit/Tom.cpp @@ -6,10 +6,12 @@ using namespace daisysp; void Tom::Init(float sample_rate) { - Init(sample_rate, 80); + Init(sample_rate, 80, NULL); } -void Tom::Init(float sample_rate, float frequency) { +void Tom::Init(float sample_rate, float frequency, ClickSource *clickSource) { + + this->clickSource = clickSource; osc.Init(sample_rate); SetParam(PARAM_FREQUENCY, frequency); @@ -19,8 +21,6 @@ void Tom::Init(float sample_rate, float frequency) { vibratoOsc.SetWaveform(Oscillator::WAVE_SIN); vibratoOsc.SetFreq(20); - noise.Init(); - ampEnv.Init(sample_rate); ampEnv.SetMax(1); ampEnv.SetMin(0); @@ -29,43 +29,17 @@ void Tom::Init(float sample_rate, float frequency) { SetParam(PARAM_AMP_DECAY, 1.1); SetParam(PARAM_PITCH_MOD, 60); - lpfEnv.Init(sample_rate); - lpfEnv.SetMax(1); - lpfEnv.SetMin(0); - lpfEnv.SetCurve(-20); - lpfEnv.SetTime(ADENV_SEG_ATTACK, 0.001); - lpfEnv.SetTime(ADENV_SEG_DECAY, 0.09); - pitchEnv.Init(sample_rate); pitchEnv.SetMax(1); pitchEnv.SetMin(0); pitchEnv.SetCurve(-20); pitchEnv.SetTime(ADENV_SEG_ATTACK, 0.001); pitchEnv.SetTime(ADENV_SEG_DECAY, 0.25); - - hpf.Init(sample_rate); - hpf.SetRes(0.2); - hpf.SetDrive(.002); - SetParam(PARAM_HPF, 1500); - - lpf.Init(sample_rate); - lpf.SetRes(0.2); - lpf.SetDrive(.002); - SetParam(PARAM_LPF, 191); - SetParam(PARAM_LPF_MOD, 116); - - } float Tom::Process() { - // noise goes through a hpf, then through an lpf modulated by the lpf env. - // lpf env also controls amp of noise - float lpfEnvSignal = lpfEnv.Process(); - lpf.SetFreq(1000 + parameters[PARAM_LPF_MOD].GetScaledValue() * lpfEnvSignal); - hpf.Process(noise.Process()); - lpf.Process(hpf.High()); - float noiseSignal = lpf.Low() * lpfEnvSignal; + float clickSignal = clickSource == NULL ? 0.0f : clickSource->Signal(); // sine osc freq is modulated by pitch env, amp by amp env float pitchEnvSignal = pitchEnv.Process(); @@ -80,14 +54,14 @@ float Tom::Process() { // // // apply velocity scaling more strong to noise than osc // // float signal = noiseSignal * velocity + oscSignal * (0.4 + velocity * 0.6); // // return signal; - return (noiseSignal + oscSignal) * velocity; + return (clickSignal + oscSignal) * velocity; } void Tom::Trigger(float velocity) { this->velocity = Utility::Limit(velocity); if (this->velocity > 0) { + clickSource->Trigger(velocity); ampEnv.Trigger(); - lpfEnv.Trigger(); pitchEnv.Trigger(); osc.Reset(); vibratoOsc.Reset(); @@ -95,7 +69,22 @@ void Tom::Trigger(float velocity) { } float Tom::GetParam(uint8_t param) { - return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_FREQUENCY: + case PARAM_AMP_DECAY: + case PARAM_PITCH_MOD: + return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; + case PARAM_LPF_MOD: + return clickSource->GetParam(ClickSource::PARAM_LPF_MOD); + case PARAM_HPF: + return clickSource->GetParam(ClickSource::PARAM_HPF); + case PARAM_LPF: + return clickSource->GetParam(ClickSource::PARAM_LPF); + } + } + + return 0.0f; } std::string Tom::GetParamString(uint8_t param) { @@ -103,12 +92,14 @@ std::string Tom::GetParamString(uint8_t param) { switch (param) { case PARAM_FREQUENCY: case PARAM_PITCH_MOD: + case PARAM_AMP_DECAY: + return std::to_string((int)GetParam(param)); case PARAM_LPF_MOD: + return clickSource->GetParamString(ClickSource::PARAM_LPF_MOD); case PARAM_HPF: + return clickSource->GetParamString(ClickSource::PARAM_HPF); case PARAM_LPF: - return std::to_string((int)GetParam(param)); - case PARAM_AMP_DECAY: - return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + return clickSource->GetParamString(ClickSource::PARAM_LPF); } } return ""; @@ -126,16 +117,16 @@ float Tom::UpdateParam(uint8_t param, float raw) { ampEnv.SetTime(ADENV_SEG_DECAY, scaled); break; case PARAM_PITCH_MOD: - case PARAM_LPF_MOD: scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 0, 2000, Parameter::EXPONENTIAL)); break; + case PARAM_LPF_MOD: + clickSource->UpdateParam(ClickSource::PARAM_LPF_MOD, raw); + break; case PARAM_HPF: - scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 3000, Parameter::EXPONENTIAL)); - hpf.SetFreq(scaled); + clickSource->UpdateParam(ClickSource::PARAM_HPF, raw); break; case PARAM_LPF: - scaled = parameters[param].Update(raw, Utility::ScaleFloat(raw, 20, 3000, Parameter::EXPONENTIAL)); - lpf.SetFreq(scaled); + clickSource->UpdateParam(ClickSource::PARAM_LPF, raw); break; } } @@ -160,16 +151,16 @@ void Tom::SetParam(uint8_t param, float scaled) { ampEnv.SetTime(ADENV_SEG_DECAY, scaled); break; case PARAM_PITCH_MOD: - case PARAM_LPF_MOD: parameters[param].SetScaledValue(scaled); break; + case PARAM_LPF_MOD: + clickSource->SetParam(ClickSource::PARAM_LPF_MOD, scaled); + break; case PARAM_HPF: - parameters[param].SetScaledValue(scaled); - hpf.SetFreq(scaled); + clickSource->SetParam(ClickSource::PARAM_LPF_MOD, scaled); break; case PARAM_LPF: - parameters[param].SetScaledValue(scaled); - lpf.SetFreq(scaled); + clickSource->SetParam(ClickSource::PARAM_LPF_MOD, scaled); break; } } diff --git a/optic/HachiKit/Tom.h b/optic/HachiKit/Tom.h index 787f8550b..f14d09e7d 100644 --- a/optic/HachiKit/Tom.h +++ b/optic/HachiKit/Tom.h @@ -8,6 +8,7 @@ #include "ISource.h" #include "Utility.h" #include "Param.h" +#include "ClickSource.h" using namespace daisy; using namespace daisysp; @@ -21,12 +22,13 @@ class Tom: public IDrum { static const uint8_t PARAM_FREQUENCY = 0; static const uint8_t PARAM_AMP_DECAY = 1; static const uint8_t PARAM_PITCH_MOD = 2; + // These are pass-thru params that belong to the click source and aren't tracked in Tom static const uint8_t PARAM_LPF_MOD = 3; static const uint8_t PARAM_HPF = 4; static const uint8_t PARAM_LPF = 5; void Init(float sample_rate); - void Init(float sample_rate, float frequency); + void Init(float sample_rate, float frequency, ClickSource *clickSource); float Process(); void Trigger(float velocity); @@ -46,9 +48,8 @@ class Tom: public IDrum { Param parameters[PARAM_COUNT]; float velocity; Oscillator osc, vibratoOsc; - WhiteNoise noise; - Svf hpf, lpf; - AdEnv ampEnv, lpfEnv, pitchEnv; + AdEnv ampEnv, pitchEnv; + ClickSource *clickSource; }; From 514c4c74fdf47a81b9d9478ba4210af141e2c0c8 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sat, 25 May 2024 15:02:34 -0700 Subject: [PATCH 15/23] Fixed small bugs in Blank (drum template) --- optic/HachiKit/Blank.cpp | 9 ++++----- optic/HachiKit/Blank.h | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/optic/HachiKit/Blank.cpp b/optic/HachiKit/Blank.cpp index efe6b55fa..25237a298 100644 --- a/optic/HachiKit/Blank.cpp +++ b/optic/HachiKit/Blank.cpp @@ -10,14 +10,13 @@ void Blank::Init(float sample_rate) { void Blank::Init(float sample_rate, float frequency, float attack, float decay) { // initialize audio objects - SetParam(PARAM_FREQUENCY, frequency, false); - SetParam(PARAM_ATTACK, attack, false); - SetParam(PARAM_DECAY, decay, false); + SetParam(PARAM_FREQUENCY, frequency); + SetParam(PARAM_ATTACK, attack); + SetParam(PARAM_DECAY, decay); } float Blank::Process() { - float sig = 0.0f; - return velocity * sig; + return 0.0f; } void Blank::Trigger(float velocity) { diff --git a/optic/HachiKit/Blank.h b/optic/HachiKit/Blank.h index bc237a19d..0a30f48cf 100644 --- a/optic/HachiKit/Blank.h +++ b/optic/HachiKit/Blank.h @@ -31,7 +31,6 @@ class Blank: public IDrum { float UpdateParam(uint8_t param, float value); void SetParam(uint8_t param, float value); void ResetParams(); - std::string GetParamString(uint8_t param); std::string Name() { return "Blank"; } std::string Slot() { return slot; } From 0ae093f80a9b0a1f4a86a6aaef239762d573d378 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sat, 25 May 2024 15:04:36 -0700 Subject: [PATCH 16/23] Fixed bug in Tom param display, restored default drum order --- optic/HachiKit/HachiKit.cpp | 22 +++++++++++----------- optic/HachiKit/Makefile | 2 +- optic/HachiKit/Tom.cpp | 3 ++- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index 6bb11346d..ebd375754 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -274,17 +274,17 @@ int main(void) cy.Init(samplerate, 0.001, 3.5, &source68, 1700, 2400); cb.Init(samplerate, 0.001, 0.5, &source68, 1700, 2400); - drums[0] = &bd; //15? - drums[1] = &rs; //4 - drums[2] = &sd; //7 - drums[3] = &cp; //7 - drums[4] = < //19 - drums[5] = &mt; //19 - drums[6] = &ch; //3 - drums[7] = &ht; //19 - drums[8] = &oh; //2 - drums[9] = &cb; // - drums[10] = &cy; // + drums[0] = &bd; + drums[1] = &rs; // 24 + drums[2] = &sd; // 32 + drums[3] = &cp; // 38 + drums[4] = < // 45 + drums[5] = &mt; // 52 + drums[6] = &ch; // 55 + drums[7] = &ht; // 62 + drums[8] = &oh; // 65 + drums[9] = &cb; // 70 + drums[10] = &cy; // 75 drumCount = 11; currentDrum = 0; diff --git a/optic/HachiKit/Makefile b/optic/HachiKit/Makefile index 47a7d5c7d..7b5555ab6 100644 --- a/optic/HachiKit/Makefile +++ b/optic/HachiKit/Makefile @@ -15,7 +15,7 @@ Oh.cpp \ Cy.cpp \ Cow8.cpp \ Tom.cpp \ -ClickSource.cpp +ClickSource.cpp OPT = -Os diff --git a/optic/HachiKit/Tom.cpp b/optic/HachiKit/Tom.cpp index 4a80474f5..0622303cb 100644 --- a/optic/HachiKit/Tom.cpp +++ b/optic/HachiKit/Tom.cpp @@ -92,8 +92,9 @@ std::string Tom::GetParamString(uint8_t param) { switch (param) { case PARAM_FREQUENCY: case PARAM_PITCH_MOD: - case PARAM_AMP_DECAY: return std::to_string((int)GetParam(param)); + case PARAM_AMP_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; case PARAM_LPF_MOD: return clickSource->GetParamString(ClickSource::PARAM_LPF_MOD); case PARAM_HPF: From 030ae807de509ad62477905c4be0e6fa02adfb86 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Mon, 27 May 2024 23:23:27 -0700 Subject: [PATCH 17/23] Added slot to all IDrums. Added a CPU usage debugging mode to play a single IDrum and see usage. --- optic/HachiKit/Bd8.cpp | 8 ++++-- optic/HachiKit/Bd8.h | 4 +-- optic/HachiKit/Blank.cpp | 9 ++++-- optic/HachiKit/Blank.h | 4 +-- optic/HachiKit/Ch.cpp | 9 +++--- optic/HachiKit/Ch.h | 4 +-- optic/HachiKit/ClickSource.cpp | 8 ++++-- optic/HachiKit/ClickSource.h | 4 +-- optic/HachiKit/Cow8.cpp | 9 +++--- optic/HachiKit/Cow8.h | 4 +-- optic/HachiKit/Cy.cpp | 9 +++--- optic/HachiKit/Cy.h | 4 +-- optic/HachiKit/FmDrum.cpp | 8 ++++-- optic/HachiKit/FmDrum.h | 4 +-- optic/HachiKit/HachiKit.cpp | 51 +++++++++++++++++----------------- optic/HachiKit/HhSource68.cpp | 8 ++++-- optic/HachiKit/HhSource68.h | 4 +-- optic/HachiKit/IDrum.h | 2 +- optic/HachiKit/Oh.cpp | 8 ++++-- optic/HachiKit/Oh.h | 4 +-- optic/HachiKit/Sd8.cpp | 8 ++++-- optic/HachiKit/Sd8.h | 4 +-- optic/HachiKit/SdNoise.cpp | 7 +++-- optic/HachiKit/SdNoise.h | 4 +-- optic/HachiKit/Tom.cpp | 7 +++-- optic/HachiKit/Tom.h | 4 +-- 26 files changed, 109 insertions(+), 90 deletions(-) diff --git a/optic/HachiKit/Bd8.cpp b/optic/HachiKit/Bd8.cpp index 73f63f9ee..24ae71f82 100644 --- a/optic/HachiKit/Bd8.cpp +++ b/optic/HachiKit/Bd8.cpp @@ -4,11 +4,13 @@ using namespace daisy; using namespace daisysp; -void Bd8::Init(float sample_rate) { - Init(sample_rate, 78, 0.001, 4.857, 0.001, 0.1, 0.95); +void Bd8::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 78, 0.001, 4.857, 0.001, 0.1, 0.95); } -void Bd8::Init(float sample_rate, float frequency, float ampAttack, float ampDecay, float pitchAttack, float pitchDecay, float modAmount) { +void Bd8::Init(std::string slot, float sample_rate, float frequency, float ampAttack, float ampDecay, float pitchAttack, float pitchDecay, float modAmount) { + + this->slot = slot; // oscillator settings osc.Init(sample_rate); diff --git a/optic/HachiKit/Bd8.h b/optic/HachiKit/Bd8.h index fd7451779..a2c60c15b 100644 --- a/optic/HachiKit/Bd8.h +++ b/optic/HachiKit/Bd8.h @@ -25,8 +25,8 @@ class Bd8: public IDrum { static const uint8_t PARAM_PITCH_ATTACK = 5; // TODO: add aCurve and pCurve - void Init(float sample_rate); - void Init(float sample_rate, float frequency, float ampAttack, float ampDecay, float pitchAttack, float pitchDecay, float modAmount); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float frequency, float ampAttack, float ampDecay, float pitchAttack, float pitchDecay, float modAmount); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/Blank.cpp b/optic/HachiKit/Blank.cpp index 25237a298..7dc719ea4 100644 --- a/optic/HachiKit/Blank.cpp +++ b/optic/HachiKit/Blank.cpp @@ -4,11 +4,14 @@ using namespace daisy; using namespace daisysp; -void Blank::Init(float sample_rate) { - Init(sample_rate, 220, 0.001, 0.5); +void Blank::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 220, 0.001, 0.5); } -void Blank::Init(float sample_rate, float frequency, float attack, float decay) { +void Blank::Init(std::string slot, float sample_rate, float frequency, float attack, float decay) { + + this->slot = slot; + // initialize audio objects SetParam(PARAM_FREQUENCY, frequency); SetParam(PARAM_ATTACK, attack); diff --git a/optic/HachiKit/Blank.h b/optic/HachiKit/Blank.h index 0a30f48cf..fc1a7140d 100644 --- a/optic/HachiKit/Blank.h +++ b/optic/HachiKit/Blank.h @@ -21,8 +21,8 @@ class Blank: public IDrum { static const uint8_t PARAM_ATTACK = 1; static const uint8_t PARAM_DECAY = 2; - void Init(float sample_rate); - void Init(float sample_rate, float frequency, float attack, float decay); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float frequency, float attack, float decay); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/Ch.cpp b/optic/HachiKit/Ch.cpp index 7cf72e5bf..4ec4b936f 100644 --- a/optic/HachiKit/Ch.cpp +++ b/optic/HachiKit/Ch.cpp @@ -5,12 +5,13 @@ using namespace daisy; using namespace daisysp; -void Ch::Init(float sample_rate) { - Init(sample_rate, 0.001, 0.5, NULL, 0.5, 2000, 5000); +void Ch::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 0.001, 0.5, NULL, 0.5, 2000, 5000); } -// void Ch::Init(float sample_rate, float attack, float decay, ISource *source) { -void Ch::Init(float sample_rate, float attack, float decay, HhSource68 *source, float morph, float hpf, float lpf) { +void Ch::Init(std::string slot, float sample_rate, float attack, float decay, HhSource68 *source, float morph, float hpf, float lpf) { + + this->slot = slot; // env settings env.Init(sample_rate); diff --git a/optic/HachiKit/Ch.h b/optic/HachiKit/Ch.h index c071d5c62..7001b25e9 100644 --- a/optic/HachiKit/Ch.h +++ b/optic/HachiKit/Ch.h @@ -26,8 +26,8 @@ class Ch: public IDrum { static const uint8_t PARAM_HPF = 3; static const uint8_t PARAM_LPF = 4; - void Init(float sample_rate); - void Init(float sample_rate, float attack, float decay, HhSource68 *source, float morph, float hpf, float lpf); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float attack, float decay, HhSource68 *source, float morph, float hpf, float lpf); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/ClickSource.cpp b/optic/HachiKit/ClickSource.cpp index 58abf3a47..4a3b6c022 100644 --- a/optic/HachiKit/ClickSource.cpp +++ b/optic/HachiKit/ClickSource.cpp @@ -5,11 +5,13 @@ using namespace daisy; using namespace daisysp; -void ClickSource::Init(float sample_rate) { - Init(sample_rate, 1500, 191, 116); +void ClickSource::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 1500, 191, 116); } -void ClickSource::Init(float sample_rate, float hpfFreq, float lpfFreq, float mod) { +void ClickSource::Init(std::string slot, float sample_rate, float hpfFreq, float lpfFreq, float mod) { + + this->slot = slot; noise.Init(); diff --git a/optic/HachiKit/ClickSource.h b/optic/HachiKit/ClickSource.h index 69c6ab2af..5d3dd3686 100644 --- a/optic/HachiKit/ClickSource.h +++ b/optic/HachiKit/ClickSource.h @@ -22,8 +22,8 @@ class ClickSource: public IDrum { static const uint8_t PARAM_LPF = 1; static const uint8_t PARAM_LPF_MOD = 2; - void Init(float sample_rate); - void Init(float sample_rate, float hpfFreq, float lpfFreq, float mod); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float hpfFreq, float lpfFreq, float mod); float Process(); void Trigger(float velocity); float Signal() { return signal; } diff --git a/optic/HachiKit/Cow8.cpp b/optic/HachiKit/Cow8.cpp index f65868336..01b3925a4 100644 --- a/optic/HachiKit/Cow8.cpp +++ b/optic/HachiKit/Cow8.cpp @@ -11,12 +11,13 @@ const float Cow8::LPF_MAX = 18000; const float Cow8::LPF_MIN = 100; -void Cow8::Init(float sample_rate) { - Init(sample_rate, 0.001, 3.5, NULL, 1700, 2300); +void Cow8::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 0.001, 3.5, NULL, 1700, 2300); } -// void Cow8::Init(float sample_rate, float attack, float decay, ISource *source) { -void Cow8::Init(float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff) { +void Cow8::Init(std::string slot, float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff) { + + this->slot = slot; // env settings env.Init(sample_rate); diff --git a/optic/HachiKit/Cow8.h b/optic/HachiKit/Cow8.h index c10023065..cec8235d6 100644 --- a/optic/HachiKit/Cow8.h +++ b/optic/HachiKit/Cow8.h @@ -30,8 +30,8 @@ class Cow8: public IDrum { static const float LPF_MAX; static const float LPF_MIN; - void Init(float sample_rate); - void Init(float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/Cy.cpp b/optic/HachiKit/Cy.cpp index f9486b688..62740d89f 100644 --- a/optic/HachiKit/Cy.cpp +++ b/optic/HachiKit/Cy.cpp @@ -11,12 +11,13 @@ const float Cy::LPF_MAX = 18000; const float Cy::LPF_MIN = 100; -void Cy::Init(float sample_rate) { - Init(sample_rate, 0.001, 3.5, NULL, 1700, 2300); +void Cy::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 0.001, 3.5, NULL, 1700, 2300); } -// void Cy::Init(float sample_rate, float attack, float decay, ISource *source) { -void Cy::Init(float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff) { +void Cy::Init(std::string slot, float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff) { + + this->slot = slot; // env settings env.Init(sample_rate); diff --git a/optic/HachiKit/Cy.h b/optic/HachiKit/Cy.h index d41a1c3f5..429b5a852 100644 --- a/optic/HachiKit/Cy.h +++ b/optic/HachiKit/Cy.h @@ -30,8 +30,8 @@ class Cy: public IDrum { static const float LPF_MAX; static const float LPF_MIN; - void Init(float sample_rate); - void Init(float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float attack, float decay, HhSource68 *source, float hpfCutoff, float lpfCutoff); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/FmDrum.cpp b/optic/HachiKit/FmDrum.cpp index 124dfd8cc..d1e2d6961 100644 --- a/optic/HachiKit/FmDrum.cpp +++ b/optic/HachiKit/FmDrum.cpp @@ -4,12 +4,14 @@ using namespace daisy; using namespace daisysp; -void FmDrum::Init(float sample_rate) { - Init(sample_rate, 68, 3.3, 2.2, 0.001, 0.043, -50); +void FmDrum::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 68, 3.3, 2.2, 0.001, 0.043, -50); } -void FmDrum::Init(float sample_rate, float frequency, float ratio, float modAmount, float attack, float decay, float curve) { +void FmDrum::Init(std::string slot, float sample_rate, float frequency, float ratio, float modAmount, float attack, float decay, float curve) { + this->slot = slot; + // 2-osc fm object fm.Init(sample_rate); SetParam(PARAM_FREQUENCY, frequency); diff --git a/optic/HachiKit/FmDrum.h b/optic/HachiKit/FmDrum.h index b39e8698a..078df14b3 100644 --- a/optic/HachiKit/FmDrum.h +++ b/optic/HachiKit/FmDrum.h @@ -24,8 +24,8 @@ class FmDrum: public IDrum { static const uint8_t PARAM_ATTACK = 4; static const uint8_t PARAM_ENV_CURVE = 5; - void Init(float sample_rate); - void Init(float sample_rate, float frequency, float ratio, float modAmount, float attack, float decay, float curve); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float frequency, float ratio, float modAmount, float attack, float decay, float curve); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index ebd375754..492729703 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -26,6 +26,7 @@ using namespace daisysp; #define MINIMUM_NOTE 36 #define KNOB_COUNT 4 +#define CPU_SINGLE false DaisyPatch hw; Screen screen(&hw.display); @@ -93,12 +94,8 @@ void DisplayParamMenu() { void ProcessEncoder() { int inc = hw.encoder.Increment(); - // int newDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); - int newDrum = Utility::LimitInt(currentDrum + inc, 0, maxDrum-1); + int newDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); if (newDrum != currentDrum) { - if (newDrum < maxDrum) { - drumCount = newDrum + 1; - } drums[newDrum]->ResetParams(); currentDrum = newDrum; screen.DrawMenu(currentDrum); @@ -173,18 +170,23 @@ void AudioCallback(AudioHandle::InputBuffer in, ProcessControls(); - for(size_t i = 0; i < size; i++) - { + for(size_t i = 0; i < size; i++) { // Process shared sound sources source68.Process(); clickSource.Process(); float sig = 0.0f; - for (uint8_t i = 0; i < drumCount; i++) { - sig += drums[i]->Process(); + float limited = 0.0f; + if (CPU_SINGLE) { + sig = drums[currentDrum]->Process(); + limited = sig; + } else { + for (uint8_t i = 0; i < drumCount; i++) { + sig += drums[i]->Process(); + } + limited = sig / drumCount; } - float limited = sig / drumCount; out[0][i] = out[1][i] = limited; out[2][i] = out[3][i] = sig; @@ -257,22 +259,22 @@ int main(void) meter.Init(samplerate, 128, 1.0f); // Shared sound sources - source68.Init(samplerate, HhSource68::MORPH_808_VALUE); - clickSource.Init(samplerate, 1500, 191, 116); + source68.Init("", samplerate, HhSource68::MORPH_808_VALUE); + clickSource.Init("", samplerate, 1500, 191, 116); - bd.Init(samplerate); - rs.Init(samplerate); - sd.Init(samplerate); - cp.Init(samplerate); + bd.Init("BD", samplerate); + rs.Init("RS", samplerate); + sd.Init("SD", samplerate); + cp.Init("CP", samplerate); - lt.Init(samplerate, 80, &clickSource); - mt.Init(samplerate, 91, &clickSource); - ht.Init(samplerate, 106, &clickSource); + lt.Init("LT", samplerate, 80, &clickSource); + mt.Init("MT", samplerate, 91, &clickSource); + ht.Init("HT", samplerate, 106, &clickSource); - ch.Init(samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); - oh.Init(samplerate, 0.001, 0.13, 0.05, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); - cy.Init(samplerate, 0.001, 3.5, &source68, 1700, 2400); - cb.Init(samplerate, 0.001, 0.5, &source68, 1700, 2400); + ch.Init("CH", samplerate, 0.001, 0.5, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); + oh.Init("OH", samplerate, 0.001, 0.13, 0.05, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); + cy.Init("CY", samplerate, 0.001, 3.5, &source68, 1700, 2400); + cb.Init("CB", samplerate, 0.001, 0.5, &source68, 1700, 2400); drums[0] = &bd; drums[1] = &rs; // 24 @@ -288,9 +290,6 @@ int main(void) drumCount = 11; currentDrum = 0; - maxDrum = 11; - drumCount = 1; - //display hw.display.Fill(false); // OledMessage("Hachikit 0.1", 5); diff --git a/optic/HachiKit/HhSource68.cpp b/optic/HachiKit/HhSource68.cpp index 648452d31..571f3bfb4 100644 --- a/optic/HachiKit/HhSource68.cpp +++ b/optic/HachiKit/HhSource68.cpp @@ -15,11 +15,13 @@ const float HhSource68::LPF_MIN = 100; const float HhSource68::GAIN_MAX = 100; -void HhSource68::Init(float sample_rate) { - Init(sample_rate, MORPH_808_VALUE); +void HhSource68::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, MORPH_808_VALUE); } -void HhSource68::Init(float sample_rate, float morph) { +void HhSource68::Init(std::string slot, float sample_rate, float morph) { + + this->slot = slot; oscs[0] = &osc0; oscs[1] = &osc1; diff --git a/optic/HachiKit/HhSource68.h b/optic/HachiKit/HhSource68.h index 84ba2c786..29c15d22a 100644 --- a/optic/HachiKit/HhSource68.h +++ b/optic/HachiKit/HhSource68.h @@ -35,13 +35,13 @@ class HhSource68: public IDrum { /** Initialize model with default parameters. * \param sample_rate audio sample rate. */ - void Init(float sample_rate); + void Init(std::string slot, float sample_rate); /** Initialize model with specified parameters. * \param sample_rate audio sample rate. * \param frequency oscillator frequency in hertz. */ - void Init(float sample_rate, float morph); + void Init(std::string slot, float sample_rate, float morph); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/IDrum.h b/optic/HachiKit/IDrum.h index ef4398548..558d2efc3 100644 --- a/optic/HachiKit/IDrum.h +++ b/optic/HachiKit/IDrum.h @@ -22,7 +22,7 @@ class IDrum { /** Initialize model with default parameters. * \param sample_rate audio sample rate. */ - virtual void Init(float sample_rate) = 0; + virtual void Init(std::string slot, float sample_rate) = 0; /** * Calculate the next sample value. diff --git a/optic/HachiKit/Oh.cpp b/optic/HachiKit/Oh.cpp index 5af42fac8..7b32f171a 100644 --- a/optic/HachiKit/Oh.cpp +++ b/optic/HachiKit/Oh.cpp @@ -5,12 +5,14 @@ using namespace daisy; using namespace daisysp; -void Oh::Init(float sample_rate) { - Init(sample_rate, 0.001, 0.13f, 0.05f, NULL, 0.5, 2000, 5000); +void Oh::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 0.001, 0.13f, 0.05f, NULL, 0.5, 2000, 5000); } -void Oh::Init(float sample_rate, float attack, float hold, float decay, HhSource68 *source, float morph, float hpf, float lpf) { +void Oh::Init(std::string slot, float sample_rate, float attack, float hold, float decay, HhSource68 *source, float morph, float hpf, float lpf) { + this->slot = slot; + // env settings env.Init(sample_rate); SetParam(PARAM_ATTACK, attack); diff --git a/optic/HachiKit/Oh.h b/optic/HachiKit/Oh.h index 928cb0f5e..fc8e51ace 100644 --- a/optic/HachiKit/Oh.h +++ b/optic/HachiKit/Oh.h @@ -28,8 +28,8 @@ class Oh: public IDrum { static const uint8_t PARAM_MORPH = 4; static const uint8_t PARAM_HPF = 5; - void Init(float sample_rate); - void Init(float sample_rate, float attack, float hold, float decay, HhSource68 *source, float morph, float hpf, float lpf); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float attack, float hold, float decay, HhSource68 *source, float morph, float hpf, float lpf); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/Sd8.cpp b/optic/HachiKit/Sd8.cpp index 8b47086c3..06222983b 100644 --- a/optic/HachiKit/Sd8.cpp +++ b/optic/HachiKit/Sd8.cpp @@ -7,12 +7,14 @@ using namespace daisysp; const std::string Sd8::paramNames[] = { "oFrq", "oDcy", "nDcy", "Mix", "oAtk", "nAtk" }; -void Sd8::Init(float sample_rate) { - Init(sample_rate, 153, 0.001, 1.212, 0.001, 0.971, 0.5); +void Sd8::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 153, 0.001, 1.212, 0.001, 0.971, 0.5); } -void Sd8::Init(float sample_rate, float oscFrequency, float oscAttack, float oscDecay, float noiseAttack, float noiseDecay, float mix) { +void Sd8::Init(std::string slot, float sample_rate, float oscFrequency, float oscAttack, float oscDecay, float noiseAttack, float noiseDecay, float mix) { + this->slot = slot; + // oscillator settings osc.Init(sample_rate); SetParam(PARAM_OSC_FREQUENCY, oscFrequency); diff --git a/optic/HachiKit/Sd8.h b/optic/HachiKit/Sd8.h index 34867ad3e..86eed07c9 100644 --- a/optic/HachiKit/Sd8.h +++ b/optic/HachiKit/Sd8.h @@ -24,8 +24,8 @@ class Sd8: public IDrum { static const uint8_t PARAM_OSC_ATTACK = 4; static const uint8_t PARAM_NOISE_ATTACK = 5; - void Init(float sample_rate); - void Init(float sample_rate, float oscFrequency, float oscAttack, float oscDecay, float noiseAttack, float noiseDecay, float mix); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float oscFrequency, float oscAttack, float oscDecay, float noiseAttack, float noiseDecay, float mix); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/SdNoise.cpp b/optic/HachiKit/SdNoise.cpp index 653a87522..737a8cb6d 100644 --- a/optic/HachiKit/SdNoise.cpp +++ b/optic/HachiKit/SdNoise.cpp @@ -4,11 +4,12 @@ using namespace daisy; using namespace daisysp; -void SdNoise::Init(float sample_rate) { - Init(sample_rate, 0.01, 0.237, -30.0f); +void SdNoise::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 0.01, 0.237, -30.0f); } -void SdNoise::Init(float sample_rate, float attack, float decay, float curve) { +void SdNoise::Init(std::string slot, float sample_rate, float attack, float decay, float curve) { + this->slot = slot; noise.Init(); ampEnv.Init(sample_rate); ampEnv.SetMax(1); diff --git a/optic/HachiKit/SdNoise.h b/optic/HachiKit/SdNoise.h index 001ad77c7..31952d669 100644 --- a/optic/HachiKit/SdNoise.h +++ b/optic/HachiKit/SdNoise.h @@ -21,8 +21,8 @@ class SdNoise: public IDrum { static const uint8_t PARAM_DECAY = 1; static const uint8_t PARAM_CURVE = 2; - void Init(float sample_rate); - void Init(float sample_rate, float attack, float decay, float curve); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float attack, float decay, float curve); float Process(); void Trigger(float velocity); diff --git a/optic/HachiKit/Tom.cpp b/optic/HachiKit/Tom.cpp index 0622303cb..9cd8ac6eb 100644 --- a/optic/HachiKit/Tom.cpp +++ b/optic/HachiKit/Tom.cpp @@ -5,12 +5,13 @@ using namespace daisy; using namespace daisysp; -void Tom::Init(float sample_rate) { - Init(sample_rate, 80, NULL); +void Tom::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 80, NULL); } -void Tom::Init(float sample_rate, float frequency, ClickSource *clickSource) { +void Tom::Init(std::string slot, float sample_rate, float frequency, ClickSource *clickSource) { + this->slot = slot; this->clickSource = clickSource; osc.Init(sample_rate); diff --git a/optic/HachiKit/Tom.h b/optic/HachiKit/Tom.h index f14d09e7d..c755a5752 100644 --- a/optic/HachiKit/Tom.h +++ b/optic/HachiKit/Tom.h @@ -27,8 +27,8 @@ class Tom: public IDrum { static const uint8_t PARAM_HPF = 4; static const uint8_t PARAM_LPF = 5; - void Init(float sample_rate); - void Init(float sample_rate, float frequency, ClickSource *clickSource); + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float frequency, ClickSource *clickSource); float Process(); void Trigger(float velocity); From ba146aca1cf1157c3d872f59732b4c391665faf8 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sat, 1 Jun 2024 16:06:28 -0700 Subject: [PATCH 18/23] Display 2nd row of params --- optic/HachiKit/HachiKit.cpp | 97 +++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 42 deletions(-) diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index 492729703..622c2b725 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -72,14 +72,16 @@ void OledMessage(std::string message, int row, int column) // Display the available parameter names. void DisplayParamMenu() { - hw.display.DrawRect(0, 0, 127, 30, false, true); - hw.display.DrawLine(0,10,127,10, true); + hw.display.DrawRect(0, 9, 127, 36, false, true); + // hw.display.DrawLine(0,12,127,12, true); + // hw.display.DrawLine(0,33,127,33, true); + // hw.display.DrawLine(0,44,127,44, true); uint8_t param; for (int knob = 0; knob < KNOB_COUNT; knob++) { - for (u8 row = 0; row <= drums[currentDrum]->PARAM_COUNT / 4; row++) { - // for (u8 row = 0; row < 2; row++) { - Rectangle rect2(knob * 32, (row + 1) * 11, 32, 11); + // for (u8 row = 0; row <= drums[currentDrum]->PARAM_COUNT / 4; row++) { + for (u8 row = 0; row < 2; row++) { + Rectangle rect2(knob * 32, (row + 1) * 12, 32, 12); param = row * KNOB_COUNT + knob; std::string sc = drums[currentDrum]->GetParamName(param); bool selected = row == currentKnobRow; @@ -87,17 +89,63 @@ void DisplayParamMenu() { screen.DrawButton(rect2, sc, selected, selected, !selected); // hw.display.SetCursor(rect2.GetX(), rect2.GetY()); // hw.display.WriteString(sc.c_str(), Font_6x8, true); - hw.display.DrawLine(0, rect2.GetY() + 11, 127, rect2.GetY() + 11, true); + // hw.display.DrawLine(0, rect2.GetY() + 11, 127, rect2.GetY() + 11, true); } } + + hw.display.DrawLine(0,11,127,11, true); + hw.display.DrawLine(0,36,127,36, true); + +} + +// Display the current values and parameter names of model params for 4 knobs. +// Knob number == param number, since model params are listed in UI order. +void DisplayKnobValues() { + + hw.display.DrawRect(0, 0, 127, 11, false, true); + // hw.display.DrawLine(0,10,127,10, true); + + uint8_t param; + for (int knob = 0; knob < KNOB_COUNT; knob++) { + // top row: current param values from knobs + param = currentKnobRow * KNOB_COUNT + knob; + Rectangle rect(knob * 32, 0, 32, 8); + std::string sc = drums[currentDrum]->GetParamString(param); + hw.display.WriteStringAligned(sc.c_str(), Font_6x8, rect, Alignment::centered, true); + // screen.DrawButton(rect, sc, false, true, false); + + // for (u8 row = 0; row <= drums[currentDrum]->PARAM_COUNT / 4; row++) { + // Rectangle rect2(knob * 32, (row + 1) * 11, 32, 11); + // param = row * KNOB_COUNT + knob; + // sc = drums[currentDrum]->GetParamName(param); + // bool selected = row == currentKnobRow; + // // hw.display.WriteStringAligned(sc.c_str(), Font_6x8, rect2, Alignment::centered, true); + // screen.DrawButton(rect2, sc, selected, selected, !selected); + // // hw.display.SetCursor(rect2.GetX(), rect2.GetY()); + // // hw.display.WriteString(sc.c_str(), Font_6x8, true); + // hw.display.DrawLine(0, rect2.GetY() + 11, 127, rect2.GetY() + 11, true); + // } + } } void ProcessEncoder() { + + bool redraw = false; int inc = hw.encoder.Increment(); int newDrum = Utility::LimitInt(currentDrum + inc, 0, drumCount-1); if (newDrum != currentDrum) { drums[newDrum]->ResetParams(); currentDrum = newDrum; + redraw = true; + } + + if (hw.encoder.RisingEdge()) { + currentKnobRow = (currentKnobRow + 1) % 2; + redraw = true; + drums[currentDrum]->ResetParams(); + } + + if (redraw) { screen.DrawMenu(currentDrum); DisplayParamMenu(); hw.display.Update(); @@ -111,43 +159,7 @@ void ProcessKnobs() { for (int knob = 0; knob < KNOB_COUNT; knob++) { float sig = hw.controls[knob].Value(); uint8_t param = currentKnobRow * KNOB_COUNT + knob; - // drums[currentDrum]->SetParam(param, sig, true); drums[currentDrum]->UpdateParam(param, sig); - // float val = drums[currentDrum]->UpdateParam(param, sig); - // if (param == 0) { - // float scaled = Utility::ScaleFloat(sig, 20, 5000, Parameter::EXPONENTIAL); - // OledMessage("Upd: " + std::to_string(param) + ", " + std::to_string((int)(sig*1000)) + ", " + std::to_string((int)scaled) + ", " + std::to_string((int)val) + " ", 4); - // } - } -} - -// Display the current values and parameter names of model params for 4 knobs. -// Knob number == param number, since model params are listed in UI order. -void DisplayKnobValues() { - - hw.display.DrawRect(0, 0, 127, 30, false, true); - hw.display.DrawLine(0,10,127,10, true); - - uint8_t param; - for (int knob = 0; knob < KNOB_COUNT; knob++) { - // top row: current param values from knobs - param = currentKnobRow * KNOB_COUNT + knob; - Rectangle rect(knob * 32, 0, 32, 8); - std::string sc = drums[currentDrum]->GetParamString(param); - hw.display.WriteStringAligned(sc.c_str(), Font_6x8, rect, Alignment::centered, true); - // screen.DrawButton(rect, sc, false, true, false); - - for (u8 row = 0; row <= drums[currentDrum]->PARAM_COUNT / 4; row++) { - Rectangle rect2(knob * 32, (row + 1) * 11, 32, 11); - param = row * KNOB_COUNT + knob; - sc = drums[currentDrum]->GetParamName(param); - bool selected = row == currentKnobRow; - // hw.display.WriteStringAligned(sc.c_str(), Font_6x8, rect2, Alignment::centered, true); - screen.DrawButton(rect2, sc, selected, selected, !selected); - // hw.display.SetCursor(rect2.GetX(), rect2.GetY()); - // hw.display.WriteString(sc.c_str(), Font_6x8, true); - hw.display.DrawLine(0, rect2.GetY() + 11, 127, rect2.GetY() + 11, true); - } } } @@ -295,6 +307,7 @@ int main(void) // OledMessage("Hachikit 0.1", 5); // Utility::DrawDrums(&hw.display, 5); screen.DrawMenu(currentDrum); + DisplayParamMenu(); hw.display.Update(); From e65d70c1bb1e6da2460570ec046bc75b26f48aa7 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Mon, 3 Jun 2024 22:07:36 -0700 Subject: [PATCH 19/23] Small changes. Start tracking CPU usage in README --- optic/HachiKit/Bd8.cpp | 2 +- optic/HachiKit/HachiKit.cpp | 10 +++++++++- optic/HachiKit/README.md | 15 +++++++++++++++ optic/HachiKit/Tom.cpp | 8 -------- optic/HachiKit/Tom.h | 2 +- 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/optic/HachiKit/Bd8.cpp b/optic/HachiKit/Bd8.cpp index 24ae71f82..c24199de5 100644 --- a/optic/HachiKit/Bd8.cpp +++ b/optic/HachiKit/Bd8.cpp @@ -5,7 +5,7 @@ using namespace daisy; using namespace daisysp; void Bd8::Init(std::string slot, float sample_rate) { - Init(slot, sample_rate, 78, 0.001, 4.857, 0.001, 0.1, 0.95); + Init(slot, sample_rate, 78, 0.001, 4.0, 0.001, 0.1, 0.95); } void Bd8::Init(std::string slot, float sample_rate, float frequency, float ampAttack, float ampDecay, float pitchAttack, float pitchDecay, float modAmount) { diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index 622c2b725..5f17bfdfa 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -53,6 +53,11 @@ uint8_t currentKnobRow = 0; u8 maxDrum = 1; +u8 cycle = 0; +u8 cycleLength = 8; +float savedSignal = 0.0f; + + void OledMessage(std::string message, int row) { char* mstr = &message[0]; @@ -190,6 +195,8 @@ void AudioCallback(AudioHandle::InputBuffer in, float sig = 0.0f; float limited = 0.0f; + cycle = (cycle + 1) % 8; + if (cycle < 5) { if (CPU_SINGLE) { sig = drums[currentDrum]->Process(); limited = sig; @@ -199,10 +206,10 @@ void AudioCallback(AudioHandle::InputBuffer in, } limited = sig / drumCount; } + } out[0][i] = out[1][i] = limited; out[2][i] = out[3][i] = sig; - } meter.OnBlockEnd(); @@ -266,6 +273,7 @@ int main(void) // Init float samplerate; hw.Init(); + hw.SetAudioSampleRate(SaiHandle::Config::SampleRate::SAI_48KHZ); samplerate = hw.AudioSampleRate(); meter.Init(samplerate, 128, 1.0f); diff --git a/optic/HachiKit/README.md b/optic/HachiKit/README.md index 422a4f53a..07978387a 100644 --- a/optic/HachiKit/README.md +++ b/optic/HachiKit/README.md @@ -8,3 +8,18 @@ A 16-voice virtual analog drumkit. Perkowitz +## Performance + +Drum sounds require the following percentages of CPU to run (at 48k, 400mhz): +- ClickSource - 12% +- HhSource68 - 8% +- Bd8 - +- Ch - 3% (+ HhSource68) +- Cow8 - +- Cy - +- FmDrum - +- Oh - 3% (+ HhSource68) +- Sd8 - +- SdNoise - +- Tom - 7% (+ ClickSource) + diff --git a/optic/HachiKit/Tom.cpp b/optic/HachiKit/Tom.cpp index 9cd8ac6eb..4bcc7c81f 100644 --- a/optic/HachiKit/Tom.cpp +++ b/optic/HachiKit/Tom.cpp @@ -18,10 +18,6 @@ void Tom::Init(std::string slot, float sample_rate, float frequency, ClickSource SetParam(PARAM_FREQUENCY, frequency); osc.SetWaveform(Oscillator::WAVE_POLYBLEP_TRI); - vibratoOsc.Init(sample_rate); - vibratoOsc.SetWaveform(Oscillator::WAVE_SIN); - vibratoOsc.SetFreq(20); - ampEnv.Init(sample_rate); ampEnv.SetMax(1); ampEnv.SetMin(0); @@ -45,9 +41,6 @@ float Tom::Process() { // sine osc freq is modulated by pitch env, amp by amp env float pitchEnvSignal = pitchEnv.Process(); float ampEnvSignal = ampEnv.Process(); - // TODO: including vibrato seems to crash the Daisy if there are 3 toms - // float vibratoSignal = vibratoOsc.Process(); - // osc.SetFreq(parameters[PARAM_FREQUENCY].GetScaledValue() + parameters[PARAM_PITCH_MOD].GetScaledValue() * pitchEnvSignal + 3 * vibratoSignal); osc.SetFreq(parameters[PARAM_FREQUENCY].GetScaledValue() + parameters[PARAM_PITCH_MOD].GetScaledValue() * pitchEnvSignal); float oscSignal = osc.Process() * ampEnvSignal; @@ -65,7 +58,6 @@ void Tom::Trigger(float velocity) { ampEnv.Trigger(); pitchEnv.Trigger(); osc.Reset(); - vibratoOsc.Reset(); } } diff --git a/optic/HachiKit/Tom.h b/optic/HachiKit/Tom.h index c755a5752..ce6e3bc74 100644 --- a/optic/HachiKit/Tom.h +++ b/optic/HachiKit/Tom.h @@ -47,7 +47,7 @@ class Tom: public IDrum { std::string slot; Param parameters[PARAM_COUNT]; float velocity; - Oscillator osc, vibratoOsc; + Oscillator osc; AdEnv ampEnv, pitchEnv; ClickSource *clickSource; From 91aab7ef410197016886388bfc668986dadf9bb4 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Tue, 4 Jun 2024 23:48:14 -0700 Subject: [PATCH 20/23] Added screensaver that shows midi notes --- optic/HachiKit/HachiKit.cpp | 98 +++++++++++++++++++++-------------- optic/HachiKit/Screen.cpp | 100 ++++++++++++++++++++++++++++++++++-- optic/HachiKit/Screen.h | 35 +++++++++++-- 3 files changed, 186 insertions(+), 47 deletions(-) diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index 5f17bfdfa..f2bdbc5ab 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -51,33 +51,20 @@ ClickSource clickSource; uint8_t currentDrum = 0; uint8_t currentKnobRow = 0; u8 maxDrum = 1; - +float lastKnobValue[KNOB_COUNT]; u8 cycle = 0; u8 cycleLength = 8; float savedSignal = 0.0f; +u32 usageCounter = 0; -void OledMessage(std::string message, int row) -{ - char* mstr = &message[0]; - hw.display.SetCursor(0, row * 10); - hw.display.WriteString(mstr, Font_6x8, true); - hw.display.Update(); -} -void OledMessage(std::string message, int row, int column) -{ - char* mstr = &message[0]; - hw.display.SetCursor(column * 8, row * 10); - hw.display.WriteString(mstr, Font_6x8, true); - hw.display.Update(); -} // Display the available parameter names. void DisplayParamMenu() { - hw.display.DrawRect(0, 9, 127, 36, false, true); + screen.DrawRect(0, 9, 127, 36, false, true); // hw.display.DrawLine(0,12,127,12, true); // hw.display.DrawLine(0,33,127,33, true); // hw.display.DrawLine(0,44,127,44, true); @@ -98,8 +85,8 @@ void DisplayParamMenu() { } } - hw.display.DrawLine(0,11,127,11, true); - hw.display.DrawLine(0,36,127,36, true); + screen.DrawLine(0,11,127,11, true); + screen.DrawLine(0,36,127,36, true); } @@ -107,8 +94,8 @@ void DisplayParamMenu() { // Knob number == param number, since model params are listed in UI order. void DisplayKnobValues() { - hw.display.DrawRect(0, 0, 127, 11, false, true); - // hw.display.DrawLine(0,10,127,10, true); + screen.DrawRect(0, 0, 127, 11, false, true); + // screen.DrawLine(0,10,127,10, true); uint8_t param; for (int knob = 0; knob < KNOB_COUNT; knob++) { @@ -116,7 +103,7 @@ void DisplayKnobValues() { param = currentKnobRow * KNOB_COUNT + knob; Rectangle rect(knob * 32, 0, 32, 8); std::string sc = drums[currentDrum]->GetParamString(param); - hw.display.WriteStringAligned(sc.c_str(), Font_6x8, rect, Alignment::centered, true); + screen.WriteStringAligned(sc.c_str(), Font_6x8, rect, Alignment::centered, true); // screen.DrawButton(rect, sc, false, true, false); // for (u8 row = 0; row <= drums[currentDrum]->PARAM_COUNT / 4; row++) { @@ -133,6 +120,16 @@ void DisplayKnobValues() { } } +void DrawScreen(bool clearFirst) { + if (clearFirst) { + hw.display.Fill(false); + } + DisplayParamMenu(); + DisplayKnobValues(); + screen.DrawMenu(currentDrum); + hw.display.Update(); +} + void ProcessEncoder() { bool redraw = false; @@ -142,17 +139,22 @@ void ProcessEncoder() { drums[newDrum]->ResetParams(); currentDrum = newDrum; redraw = true; + usageCounter = 0; + screen.SetScreenOn(true); + hw.display.Fill(false); } if (hw.encoder.RisingEdge()) { currentKnobRow = (currentKnobRow + 1) % 2; redraw = true; drums[currentDrum]->ResetParams(); + usageCounter = 0; + screen.SetScreenOn(true); + hw.display.Fill(false); } if (redraw) { - screen.DrawMenu(currentDrum); - DisplayParamMenu(); + DrawScreen(false); hw.display.Update(); } } @@ -165,6 +167,13 @@ void ProcessKnobs() { float sig = hw.controls[knob].Value(); uint8_t param = currentKnobRow * KNOB_COUNT + knob; drums[currentDrum]->UpdateParam(param, sig); + if (std::abs(sig - lastKnobValue[knob]) > 0.1f) { // TODO: use delta value from Param? + usageCounter = 0; + screen.SetScreenOn(true); + lastKnobValue[knob] = sig; + DrawScreen(true); + } + } } @@ -196,16 +205,16 @@ void AudioCallback(AudioHandle::InputBuffer in, float sig = 0.0f; float limited = 0.0f; cycle = (cycle + 1) % 8; - if (cycle < 5) { - if (CPU_SINGLE) { - sig = drums[currentDrum]->Process(); - limited = sig; - } else { - for (uint8_t i = 0; i < drumCount; i++) { - sig += drums[i]->Process(); + if (cycle < 9) { + if (CPU_SINGLE) { + sig = drums[currentDrum]->Process(); + limited = sig; + } else { + for (uint8_t i = 0; i < drumCount; i++) { + sig += drums[i]->Process(); + } + limited = sig / drumCount; } - limited = sig / drumCount; - } } out[0][i] = out[1][i] = limited; @@ -234,13 +243,15 @@ void HandleMidiMessage(MidiEvent m) // } else { // osc.SetAmp(0.0f); // } - // OledMessage("Midi: " + std::to_string(p.note) + ", " + std::to_string(p.velocity) + " ", 3); + // screen.OledMessage("Midi: " + std::to_string(p.note) + ", " + std::to_string(p.velocity) + " ", 3); NoteOnEvent p = m.AsNoteOn(); float velocity = p.velocity / 127.0f; if (p.velocity > 0) { if (p.note >= MINIMUM_NOTE && p.note < MINIMUM_NOTE + drumCount) { - drums[p.note - MINIMUM_NOTE]->Trigger(velocity); + u8 drum = p.note - MINIMUM_NOTE; + drums[drum]->Trigger(velocity); + screen.ScreensaveEvent(drum); } } } @@ -272,8 +283,8 @@ int main(void) { // Init float samplerate; - hw.Init(); - hw.SetAudioSampleRate(SaiHandle::Config::SampleRate::SAI_48KHZ); + hw.Init(true); // "true" boosts processor speed from 400mhz to 480mhz + hw.SetAudioSampleRate(SaiHandle::Config::SampleRate::SAI_32KHZ); // 8,16,32,48 samplerate = hw.AudioSampleRate(); meter.Init(samplerate, 128, 1.0f); @@ -310,9 +321,13 @@ int main(void) drumCount = 11; currentDrum = 0; + for (u8 i = 0; i < KNOB_COUNT; i++) { + lastKnobValue[i] = 0.0f; + } + //display hw.display.Fill(false); - // OledMessage("Hachikit 0.1", 5); + // screen.OledMessage("Hachikit 0.1", 5); // Utility::DrawDrums(&hw.display, 5); screen.DrawMenu(currentDrum); DisplayParamMenu(); @@ -335,8 +350,15 @@ int main(void) DisplayKnobValues(); float avgCpu = meter.GetAvgCpuLoad(); - OledMessage("cpu:" + std::to_string((int)(avgCpu * 100)) + "%", 4); + screen.OledMessage("cpu:" + std::to_string((int)(avgCpu * 100)) + "%", 4); + usageCounter++; + if (usageCounter > 10000) { // 10000=about 90 seconds + if (screen.IsScreenOn()) { + screen.SetScreenOn(false); + } + screen.Screensave(); + } hw.display.Update(); } } diff --git a/optic/HachiKit/Screen.cpp b/optic/HachiKit/Screen.cpp index 84628fa23..88f9044e2 100644 --- a/optic/HachiKit/Screen.cpp +++ b/optic/HachiKit/Screen.cpp @@ -10,23 +10,60 @@ const std::string Screen::menuItems[] = { "BD", "RS", "SD", "CP", "HC", "CY", "CB", "CL" }; #define MENU_SIZE 16 +void Screen::DrawRect(uint_fast8_t x1, uint_fast8_t y1, uint_fast8_t x2, uint_fast8_t y2, bool on, bool fill) { + if (!screenOn) { return; } + + display->DrawRect(x1, y1, x2, y2, on, fill); +} + +Rectangle Screen::WriteStringAligned( + const char* str, + const FontDef& font, + Rectangle boundingBox, + Alignment alignment, + bool on) { + + if (!screenOn) { return boundingBox; } + + return display->WriteStringAligned(str, font, boundingBox, alignment, on); +} + +void Screen::DrawLine( + uint_fast8_t x1, + uint_fast8_t y1, + uint_fast8_t x2, + uint_fast8_t y2, + bool on) { + + if (!screenOn) { return; } + + display->DrawLine(x1, y1, x2, y2, on); +} + void Screen::DrawRect(Rectangle rect, bool on, bool fill) { + if (!screenOn) { return; } + display->DrawRect(rect.GetX(), rect.GetY(), rect.GetX() + rect.GetWidth(), rect.GetY() + rect.GetHeight(), on, fill); } void Screen::DrawRectFilled(Rectangle rect, bool border, bool fill) { + if (!screenOn) { return; } + DrawRect(rect, fill, true); DrawRect(rect, border, false); } void Screen::DrawButton(Rectangle rect, std::string str, bool border, bool fill, bool text) { - DrawRectFilled(rect, border, fill); - // display->WriteStringAligned(str.c_str(), FONT, rect, Alignment::centered, text); - display->SetCursor(rect.GetX() + 2, rect.GetY() + 2); - display->WriteString(str.c_str(), FONT, text); + if (!screenOn) { return; } + + DrawRectFilled(rect, border, fill); + // display->WriteStringAligned(str.c_str(), FONT, rect, Alignment::centered, text); + display->SetCursor(rect.GetX() + 2, rect.GetY() + 2); + display->WriteString(str.c_str(), FONT, text); } void Screen::DrawMenu(uint8_t selected) { + if (!screenOn) { return; } uint8_t itemWidth = FONT.FontWidth * 2 + 3; uint8_t itemHeight = FONT.FontWidth + 4; @@ -44,3 +81,58 @@ void Screen::DrawMenu(uint8_t selected) { } } + +void Screen::SetScreenOn(bool screenOn) { + this->screenOn = screenOn; + if (!screenOn) { + screenCounter = 0; + } +} + +void Screen::Screensave() { + if (screenOn) { return; } + + // // blank the screen + // display->Fill(false); + + // // randomly blank one pixel at a time + // u8 x = std::rand() % WIDTH; + // u8 y = std::rand() % HEIGHT; + // display->DrawPixel(x, y, false); + + // horizontal wipe + u8 x = (screenCounter / 8) % (WIDTH + 1); + display->DrawLine(x, 0, x, HEIGHT, false); + if (x < WIDTH - 1) { + display->DrawLine(x + 1, 0, x + 1, HEIGHT, true); + } + screenCounter++; +} + +void Screen::ScreensaveEvent(u8 drum) { + if (screenOn) { return; } + + u8 x = (screenCounter / 8) % (WIDTH + 1); + if (x > 6 && x < WIDTH - 2) { + display->DrawCircle(x - 4, (15-drum) * 4 + 1, 1, true); + } +} + +void Screen::OledMessage(std::string message, int row) { + if (!screenOn) { return; } + + char* mstr = &message[0]; + display->SetCursor(0, row * 10); + display->WriteString(mstr, Font_6x8, true); + display->Update(); +} + +void Screen::OledMessage(std::string message, int row, int column) { + if (!screenOn) { return; } + + char* mstr = &message[0]; + display->SetCursor(column * 8, row * 10); + display->WriteString(mstr, Font_6x8, true); + display->Update(); +} + diff --git a/optic/HachiKit/Screen.h b/optic/HachiKit/Screen.h index 8de2d7da7..bd6821db1 100644 --- a/optic/HachiKit/Screen.h +++ b/optic/HachiKit/Screen.h @@ -4,6 +4,7 @@ #include "daisy_patch.h" #include "daisysp.h" #include +#include "Utility.h" using namespace daisy; using namespace daisysp; @@ -16,19 +17,43 @@ class Screen { static const FontDef FONT; static const std::string menuItems[]; - void DrawRect(Rectangle rect, bool on, bool fill); - void DrawRectFilled(Rectangle rect, bool border, bool fill); - void DrawButton(Rectangle rect, std::string str, bool border, bool text, bool fill); - Screen(OledDisplay *display) { this->display = display; } + // pass-throughs to hw display + void DrawRect(uint_fast8_t x1, uint_fast8_t y1, uint_fast8_t x2, uint_fast8_t y2, bool on, bool fill); + Rectangle WriteStringAligned(const char* str, + const FontDef& font, + Rectangle boundingBox, + Alignment alignment, + bool on); + void DrawLine(uint_fast8_t x1, + uint_fast8_t y1, + uint_fast8_t x2, + uint_fast8_t y2, + bool on); + + + void DrawRect(Rectangle rect, bool on, bool fill); + void DrawRectFilled(Rectangle rect, bool border, bool fill); + + void DrawButton(Rectangle rect, std::string str, bool border, bool text, bool fill); + void DrawMenu(uint8_t selected); - + + void SetScreenOn(bool screenOn); + bool IsScreenOn() { return screenOn; } + void Screensave(); + void ScreensaveEvent(u8 drum); + + void OledMessage(std::string message, int row); + void OledMessage(std::string message, int row, int column); private: OledDisplay *display; + bool screenOn = true; + u16 screenCounter = 0; }; From 188b602f96d722c42346daba364eddef734c14bc Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Thu, 6 Jun 2024 18:28:48 -0700 Subject: [PATCH 21/23] Cleanup in screensaver --- optic/HachiKit/Screen.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/optic/HachiKit/Screen.cpp b/optic/HachiKit/Screen.cpp index 88f9044e2..3f77b6af5 100644 --- a/optic/HachiKit/Screen.cpp +++ b/optic/HachiKit/Screen.cpp @@ -92,14 +92,6 @@ void Screen::SetScreenOn(bool screenOn) { void Screen::Screensave() { if (screenOn) { return; } - // // blank the screen - // display->Fill(false); - - // // randomly blank one pixel at a time - // u8 x = std::rand() % WIDTH; - // u8 y = std::rand() % HEIGHT; - // display->DrawPixel(x, y, false); - // horizontal wipe u8 x = (screenCounter / 8) % (WIDTH + 1); display->DrawLine(x, 0, x, HEIGHT, false); @@ -112,6 +104,7 @@ void Screen::Screensave() { void Screen::ScreensaveEvent(u8 drum) { if (screenOn) { return; } + // show notes with horizontal wipe u8 x = (screenCounter / 8) % (WIDTH + 1); if (x > 6 && x < WIDTH - 2) { display->DrawCircle(x - 4, (15-drum) * 4 + 1, 1, true); From 3745fb6af5e4aa4066a8e6cf2f5693f4c01e4b75 Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Fri, 7 Jun 2024 21:11:26 -0700 Subject: [PATCH 22/23] Added clap --- optic/HachiKit/Clap.cpp | 99 +++++++++++++++++++++++++++++++++++++ optic/HachiKit/Clap.h | 56 +++++++++++++++++++++ optic/HachiKit/HachiKit.cpp | 27 +++++----- optic/HachiKit/Makefile | 3 +- 4 files changed, 172 insertions(+), 13 deletions(-) create mode 100644 optic/HachiKit/Clap.cpp create mode 100644 optic/HachiKit/Clap.h diff --git a/optic/HachiKit/Clap.cpp b/optic/HachiKit/Clap.cpp new file mode 100644 index 000000000..72bc1f5dd --- /dev/null +++ b/optic/HachiKit/Clap.cpp @@ -0,0 +1,99 @@ +#include "Clap.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +void Clap::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 0.012, 1.0); +} + +void Clap::Init(std::string slot, float sample_rate, float spread, float decay) { + + this->slot = slot; + + noise.Init(); + + env.Init(sample_rate); + env.SetMax(1); + env.SetMin(0); + env.SetCurve(-20); + env.SetTime(ADENV_SEG_ATTACK, 0.001); + SetParam(PARAM_DECAY, decay); + SetParam(PARAM_SPREAD, spread); +} + +float Clap::Process() { + if (!env.IsRunning()) { + repeat++; + if (repeat == REPEATS) { + env.SetTime(ADENV_SEG_DECAY, GetParam(PARAM_DECAY)); + } + if (repeat <= REPEATS) { + env.Trigger(); + } + } + return velocity * noise.Process() * env.Process(); +} + +void Clap::Trigger(float velocity) { + this->velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + repeat = 0; + env.SetTime(ADENV_SEG_DECAY, GetParam(PARAM_SPREAD)); + env.Trigger(); + } +} + +float Clap::GetParam(uint8_t param) { + return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; +} + +std::string Clap::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_SPREAD: + case PARAM_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + } + } + return ""; + } + +float Clap::UpdateParam(uint8_t param, float raw) { + return SetParam(param, raw, true); +} + +void Clap::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } +} + +void Clap::SetParam(uint8_t param, float scaled) { + SetParam(param, scaled, false); +} + +float Clap::SetParam(uint8_t param, float value, bool isRaw) { + float scaled = value; + if (param < Clap::PARAM_COUNT) { + switch (param) { + case PARAM_SPREAD: + if (isRaw) { + scaled = parameters[param].Update(value, Utility::ScaleFloat(value, 0.001, 0.050, Parameter::LINEAR)); + } else { + parameters[param].SetScaledValue(value); + } + return scaled; + case PARAM_DECAY: + if (isRaw) { + scaled = parameters[param].Update(value, Utility::ScaleFloat(value, 0.001, 3, Parameter::EXPONENTIAL)); + } else { + parameters[param].SetScaledValue(value); + } + return scaled; + } + } + + return 0.0f; +} \ No newline at end of file diff --git a/optic/HachiKit/Clap.h b/optic/HachiKit/Clap.h new file mode 100644 index 000000000..6c19a3b4e --- /dev/null +++ b/optic/HachiKit/Clap.h @@ -0,0 +1,56 @@ +#ifndef CLAP_H +#define CLAP_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "Utility.h" +#include "Param.h" + +using namespace daisy; +using namespace daisysp; + +class Clap: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 2; + // This is the order params will appear in the UI. + static const uint8_t PARAM_SPREAD = 0; + static const uint8_t PARAM_DECAY = 1; + + static const uint8_t REPEATS = 3; + + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float spread, float decay); + float Process(); + void Trigger(float velocity); + + float GetParam(uint8_t param); + std::string GetParamString(uint8_t param); + float UpdateParam(uint8_t param, float value); + void SetParam(uint8_t param, float value); + void ResetParams(); + + std::string Name() { return "Blank"; } + std::string Slot() { return slot; } + std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } + + private: + std::string paramNames[PARAM_COUNT] = { "Sprd", "Dcy" }; + std::string slot; + Param parameters[PARAM_COUNT]; + float velocity; + float repeat; + // audio objects + WhiteNoise noise; + AdEnv env; + + float SetParam(uint8_t param, float value, bool isRaw); + +}; + + + +#endif diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index f2bdbc5ab..4c5a4bdcf 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -20,6 +20,7 @@ #include "Cy.h" #include "Cow8.h" #include "Tom.h" +#include "Clap.h" using namespace daisy; using namespace daisysp; @@ -37,12 +38,13 @@ uint8_t drumCount = 4; Bd8 bd; SdNoise rs; Sd8 sd; -FmDrum cp; +FmDrum fm; Ch ch; Oh oh; Cy cy; Cow8 cb; Tom lt, mt, ht; +Clap cp; // Shared sound sources HhSource68 source68; @@ -308,17 +310,18 @@ int main(void) cb.Init("CB", samplerate, 0.001, 0.5, &source68, 1700, 2400); drums[0] = &bd; - drums[1] = &rs; // 24 - drums[2] = &sd; // 32 - drums[3] = &cp; // 38 - drums[4] = < // 45 - drums[5] = &mt; // 52 - drums[6] = &ch; // 55 - drums[7] = &ht; // 62 - drums[8] = &oh; // 65 - drums[9] = &cb; // 70 - drums[10] = &cy; // 75 - drumCount = 11; + drums[1] = &rs; + drums[2] = &sd; + drums[3] = &cp; + drums[4] = < + drums[5] = &mt; + drums[6] = &ch; + drums[7] = &ht; + drums[8] = &oh; + drums[9] = &cb; + drums[10] = &cy; + drums[11] = &fm; + drumCount = 12; currentDrum = 0; for (u8 i = 0; i < KNOB_COUNT; i++) { diff --git a/optic/HachiKit/Makefile b/optic/HachiKit/Makefile index 7b5555ab6..3172e142d 100644 --- a/optic/HachiKit/Makefile +++ b/optic/HachiKit/Makefile @@ -15,7 +15,8 @@ Oh.cpp \ Cy.cpp \ Cow8.cpp \ Tom.cpp \ -ClickSource.cpp +ClickSource.cpp \ +Clap.cpp OPT = -Os From 5aa9cb84f9aab1026622e6075148d98759c90d7c Mon Sep 17 00:00:00 2001 From: Mike Perkowitz Date: Sun, 9 Jun 2024 16:58:42 -0700 Subject: [PATCH 23/23] Digital clap, using the clocked noise source --- optic/HachiKit/DigiClap.cpp | 111 ++++++++++++++++++++++++++++++++++++ optic/HachiKit/DigiClap.h | 57 ++++++++++++++++++ optic/HachiKit/HachiKit.cpp | 33 ++++++----- optic/HachiKit/Makefile | 3 +- optic/HachiKit/README.md | 17 ++++++ optic/HachiKit/Sd8.cpp | 2 +- 6 files changed, 208 insertions(+), 15 deletions(-) create mode 100644 optic/HachiKit/DigiClap.cpp create mode 100644 optic/HachiKit/DigiClap.h diff --git a/optic/HachiKit/DigiClap.cpp b/optic/HachiKit/DigiClap.cpp new file mode 100644 index 000000000..55c2c718e --- /dev/null +++ b/optic/HachiKit/DigiClap.cpp @@ -0,0 +1,111 @@ +#include "DigiClap.h" +#include "Utility.h" + +using namespace daisy; +using namespace daisysp; + +void DigiClap::Init(std::string slot, float sample_rate) { + Init(slot, sample_rate, 0.012f, 1.0f, 3000.0f); +} + +void DigiClap::Init(std::string slot, float sample_rate, float spread, float decay, float frequency) { + + this->slot = slot; + + clockedNoise.Init(sample_rate); + SetParam(PARAM_FREQUENCY, frequency); + clockedNoise.SetFreq(frequency); + + env.Init(sample_rate); + env.SetMax(1); + env.SetMin(0); + env.SetCurve(-20); + env.SetTime(ADENV_SEG_ATTACK, 0.001); + SetParam(PARAM_DECAY, decay); + SetParam(PARAM_SPREAD, spread); +} + +float DigiClap::Process() { + if (!env.IsRunning()) { + repeat++; + if (repeat == REPEATS) { + env.SetTime(ADENV_SEG_DECAY, GetParam(PARAM_DECAY)); + } + if (repeat <= REPEATS) { + env.Trigger(); + } + } + return velocity * clockedNoise.Process() * env.Process(); +} + +void DigiClap::Trigger(float velocity) { + this->velocity = Utility::Limit(velocity); + if (this->velocity > 0) { + repeat = 0; + env.SetTime(ADENV_SEG_DECAY, GetParam(PARAM_SPREAD)); + env.Trigger(); + } +} + +float DigiClap::GetParam(uint8_t param) { + return param < PARAM_COUNT ? parameters[param].GetScaledValue() : 0.0f; +} + +std::string DigiClap::GetParamString(uint8_t param) { + if (param < PARAM_COUNT) { + switch (param) { + case PARAM_SPREAD: + case PARAM_DECAY: + return std::to_string((int)(GetParam(param) * 1000));// + "ms"; + case PARAM_FREQUENCY: + return std::to_string((int)(GetParam(param))); + } + } + return ""; + } + +float DigiClap::UpdateParam(uint8_t param, float raw) { + return SetParam(param, raw, true); +} + +void DigiClap::ResetParams() { + for (u8 param = 0; param < PARAM_COUNT; param++) { + parameters[param].Reset(); + } +} + +void DigiClap::SetParam(uint8_t param, float scaled) { + SetParam(param, scaled, false); +} + +float DigiClap::SetParam(uint8_t param, float value, bool isRaw) { + float scaled = value; + if (param < DigiClap::PARAM_COUNT) { + switch (param) { + case PARAM_SPREAD: + if (isRaw) { + scaled = parameters[param].Update(value, Utility::ScaleFloat(value, 0.001, 0.050, Parameter::LINEAR)); + } else { + parameters[param].SetScaledValue(value); + } + return scaled; + case PARAM_DECAY: + if (isRaw) { + scaled = parameters[param].Update(value, Utility::ScaleFloat(value, 0.001, 5, Parameter::EXPONENTIAL)); + } else { + parameters[param].SetScaledValue(value); + } + return scaled; + case PARAM_FREQUENCY: + if (isRaw) { + scaled = parameters[param].Update(value, Utility::ScaleFloat(value, 20, 15000, Parameter::EXPONENTIAL)); + } else { + parameters[param].SetScaledValue(value); + } + clockedNoise.SetFreq(scaled); + return scaled; + } + } + + return 0.0f; +} \ No newline at end of file diff --git a/optic/HachiKit/DigiClap.h b/optic/HachiKit/DigiClap.h new file mode 100644 index 000000000..88f1ddd88 --- /dev/null +++ b/optic/HachiKit/DigiClap.h @@ -0,0 +1,57 @@ +#ifndef DIGICLAP_H +#define DIGICLAP_H + +#include "daisy_patch.h" +#include "daisysp.h" +#include +#include "IDrum.h" +#include "Utility.h" +#include "Param.h" + +using namespace daisy; +using namespace daisysp; + +class DigiClap: public IDrum { + + public: + // Number of settable parameters for this model. + static const uint8_t PARAM_COUNT = 3; + // This is the order params will appear in the UI. + static const uint8_t PARAM_SPREAD = 0; + static const uint8_t PARAM_DECAY = 1; + static const uint8_t PARAM_FREQUENCY = 2; + + static const uint8_t REPEATS = 3; + + void Init(std::string slot, float sample_rate); + void Init(std::string slot, float sample_rate, float spread, float decay, float frequency); + float Process(); + void Trigger(float velocity); + + float GetParam(uint8_t param); + std::string GetParamString(uint8_t param); + float UpdateParam(uint8_t param, float value); + void SetParam(uint8_t param, float value); + void ResetParams(); + + std::string Name() { return "Blank"; } + std::string Slot() { return slot; } + std::string GetParamName(uint8_t param) { return param < PARAM_COUNT ? paramNames[param] : ""; } + + private: + std::string paramNames[PARAM_COUNT] = { "Sprd", "Dcy", "Freq" }; + std::string slot; + Param parameters[PARAM_COUNT]; + float velocity; + float repeat; + // audio objects + ClockedNoise clockedNoise; + AdEnv env; + + float SetParam(uint8_t param, float value, bool isRaw); + +}; + + + +#endif diff --git a/optic/HachiKit/HachiKit.cpp b/optic/HachiKit/HachiKit.cpp index 4c5a4bdcf..bf157e1fa 100644 --- a/optic/HachiKit/HachiKit.cpp +++ b/optic/HachiKit/HachiKit.cpp @@ -21,6 +21,7 @@ #include "Cow8.h" #include "Tom.h" #include "Clap.h" +#include "DigiClap.h" using namespace daisy; using namespace daisysp; @@ -34,17 +35,18 @@ Screen screen(&hw.display); CpuLoadMeter meter; IDrum *drums[16]; -uint8_t drumCount = 4; +uint8_t drumCount = 1; Bd8 bd; SdNoise rs; Sd8 sd; -FmDrum fm; +Clap cp; +DigiClap sd2; +Tom lt, mt, ht; Ch ch; Oh oh; Cy cy; Cow8 cb; -Tom lt, mt, ht; -Clap cp; +FmDrum fm1, fm2; // Shared sound sources HhSource68 source68; @@ -87,8 +89,8 @@ void DisplayParamMenu() { } } - screen.DrawLine(0,11,127,11, true); - screen.DrawLine(0,36,127,36, true); + // screen.DrawLine(0,11,127,11, true); + // screen.DrawLine(0,36,127,36, true); } @@ -298,8 +300,9 @@ int main(void) bd.Init("BD", samplerate); rs.Init("RS", samplerate); sd.Init("SD", samplerate); - cp.Init("CP", samplerate); + cp.Init("CP", samplerate, 0.012, 0.8); + sd2.Init("S2", samplerate, 0.012, 0.8, 3000); lt.Init("LT", samplerate, 80, &clickSource); mt.Init("MT", samplerate, 91, &clickSource); ht.Init("HT", samplerate, 106, &clickSource); @@ -308,20 +311,24 @@ int main(void) oh.Init("OH", samplerate, 0.001, 0.13, 0.05, &source68, HhSource68::MORPH_808_VALUE, 6000, 16000); cy.Init("CY", samplerate, 0.001, 3.5, &source68, 1700, 2400); cb.Init("CB", samplerate, 0.001, 0.5, &source68, 1700, 2400); + fm1.Init("LC", samplerate, 98, 3.3, 2.2, 0.001, 0.101, -50); + fm2.Init("HC", samplerate, 131, 3.3, 2.2, 0.001, 0.101, -50); drums[0] = &bd; drums[1] = &rs; drums[2] = &sd; drums[3] = &cp; - drums[4] = < - drums[5] = &mt; + drums[4] = &sd2; + drums[5] = < drums[6] = &ch; - drums[7] = &ht; + drums[7] = &mt; drums[8] = &oh; - drums[9] = &cb; + drums[9] = &ht; drums[10] = &cy; - drums[11] = &fm; - drumCount = 12; + drums[11] = &fm1; + drums[12] = &fm2; + drums[13] = &cb; + drumCount = 14; currentDrum = 0; for (u8 i = 0; i < KNOB_COUNT; i++) { diff --git a/optic/HachiKit/Makefile b/optic/HachiKit/Makefile index 3172e142d..be2d57586 100644 --- a/optic/HachiKit/Makefile +++ b/optic/HachiKit/Makefile @@ -16,7 +16,8 @@ Cy.cpp \ Cow8.cpp \ Tom.cpp \ ClickSource.cpp \ -Clap.cpp +Clap.cpp \ +DigiClap.cpp OPT = -Os diff --git a/optic/HachiKit/README.md b/optic/HachiKit/README.md index 07978387a..31ae6a26d 100644 --- a/optic/HachiKit/README.md +++ b/optic/HachiKit/README.md @@ -10,6 +10,23 @@ Perkowitz ## Performance +Basic modules require the following CPU percentages (at 48k, 400mhz): +- Oscillator: 2.3% +- SVF: 1.3% +- WhiteNoise: 0.4% +- AdEnv: 1.3% +- ADSR: 0.8% +- OnePole: 0.6% +- AnalogBassDrum: 21.9% +- AnalogSnareDrum: 87.2% +- SquareNoise: 1.8% +- ZOscillator: 6.0% +- ModalVoice: 47.8% +- Wavefolder: 0.9% +- ClockedNoise: 1.4% +- FormantOscillator: 3.1% + + Drum sounds require the following percentages of CPU to run (at 48k, 400mhz): - ClickSource - 12% - HhSource68 - 8% diff --git a/optic/HachiKit/Sd8.cpp b/optic/HachiKit/Sd8.cpp index 06222983b..6cd48cfb0 100644 --- a/optic/HachiKit/Sd8.cpp +++ b/optic/HachiKit/Sd8.cpp @@ -44,7 +44,7 @@ void Sd8::Init(std::string slot, float sample_rate, float oscFrequency, float os float Sd8::Process() { float sig = (1 - parameters[PARAM_MIX].GetScaledValue()) * osc.Process() * oscEnv.Process(); sig += parameters[PARAM_MIX].GetScaledValue() * noise.Process() * noiseEnv.Process(); - return velocity * sig; // / 2; + return velocity * sig * 10; } void Sd8::Trigger(float velocity) {