From 3c4e80d8c97fd703e13100587706c4c1c1146187 Mon Sep 17 00:00:00 2001 From: heck Date: Sat, 5 Oct 2024 23:30:22 +0200 Subject: [PATCH] main_framework_proto1 - new framework prototype using updated SWTimer (now PeriodicTask) and Observer (now PollingObserver) --- examples/main_framework_proto1.cc | 235 ++++++++++++++++++++++++++++++ examples/main_framework_proto1.hh | 36 +++++ src/utils.hh | 119 ++++++++++++++- 3 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 examples/main_framework_proto1.cc create mode 100644 examples/main_framework_proto1.hh diff --git a/examples/main_framework_proto1.cc b/examples/main_framework_proto1.cc new file mode 100644 index 0000000..3f70f80 --- /dev/null +++ b/examples/main_framework_proto1.cc @@ -0,0 +1,235 @@ +#include "main_framework_proto1.hh" +#include "utils.hh" +#include +namespace Heck { + namespace { // anonymous namespace for internal linkage + // declarations + + void audio_callback( + ld::AudioHandle::InputBuffer in, + ld::AudioHandle::OutputBuffer out, + size_t size); + + // ============================================================================================= + // STATIC INIT + // ============================================================================================= + ld::DaisySeed seed{}; + ld::MidiUartHandler midi{}; + dsp::Oscillator osc{}; + + + // Buttons + std::array buts_value{ false, false, false, false }; + + ld::Switch ld_but_1{}; + PollingObserver but_1{ []() -> bool { + ld_but_1.Debounce(); + return ld_but_1.Pressed(); + }, + [](bool val) { + buts_value[0] = val; + seed.PrintLine("BUT_1: %i", buts_value[0]); + } }; + + ld::Switch ld_but_2{}; + PollingObserver but_2{ []() -> bool { + ld_but_2.Debounce(); + return ld_but_2.Pressed(); + }, + [](bool val) { + buts_value[1] = val; + seed.PrintLine("BUT_2: %i", buts_value[1]); + } }; + + ld::Switch ld_but_3{}; + PollingObserver but_3{ []() -> bool { + ld_but_3.Debounce(); + return ld_but_3.Pressed(); + }, + [](bool val) { + buts_value[2] = val; + seed.PrintLine("BUT_3: %i", buts_value[2]); + } }; + + ld::Switch ld_but_4{}; + PollingObserver but_4{ []() -> bool { + ld_but_4.Debounce(); + return ld_but_4.Pressed(); + }, + [](bool val) { + buts_value[3] = val; + seed.PrintLine("BUT_4: %i", buts_value[3]); + } }; + + + // Pots + std::array pots_value{ -1.F, -1.F, -1.F, -1.F }; + PollingObserver pot1{ []() -> int { return seed.adc.Get(0); }, + [](int val) { + pots_value[0] = float(val) / + (float)std::numeric_limits::max(); + seed.PrintLine("POT_0: %d", (int)(pots_value[0] * 100.)); + } }; + + PollingObserver pot2{ []() -> int { return seed.adc.Get(1); }, + [](int val) { + pots_value[1] = float(val) / + (float)std::numeric_limits::max(); + seed.PrintLine("POT_1: %d", (int)(pots_value[1] * 100.)); + } }; + + PollingObserver pot3{ []() -> int { return seed.adc.Get(2); }, + [](int val) { + pots_value[2] = float(val) / + (float)std::numeric_limits::max(); + seed.PrintLine("POT_2: %d", (int)(pots_value[2] * 100.)); + } }; + + PollingObserver pot4{ []() -> int { return seed.adc.Get(3); }, + [](int val) { + pots_value[3] = float(val) / + (float)std::numeric_limits::max(); + seed.PrintLine("POT_3: %d", (int)(pots_value[3] * 100.)); + } }; + + + // Tasks + PeriodicTask ui_task{ 10, [](u32 time_now) { + pot1.fetch_and_deliver_fuzzy(Constants::Hardware::BUT_FUZZ); + pot2.fetch_and_deliver_fuzzy(Constants::Hardware::BUT_FUZZ); + pot3.fetch_and_deliver_fuzzy(Constants::Hardware::BUT_FUZZ); + pot4.fetch_and_deliver_fuzzy(Constants::Hardware::BUT_FUZZ); + but_1.fetch_and_deliver(); + but_2.fetch_and_deliver(); + but_3.fetch_and_deliver(); + but_4.fetch_and_deliver(); + + while (midi.HasEvents()) { + ld::MidiEvent msg = midi.PopEvent(); + char strbuf[128]; + GetMidiTypeAsString(msg, &strbuf[0]); + seed.PrintLine("%s", strbuf); + } + } }; + + + PeriodicTask heartbeat_task{ 100, [](u32 time) { + static bool heartbeat_led_state{ false }; + heartbeat_led_state = !heartbeat_led_state; + seed.SetLed(heartbeat_led_state); + return; + } }; + + + // ============================================================================================= + // RUNTIME INIT + // ============================================================================================= + void init() + { + seed.Configure(); + seed.Init(Constants::CPU_BOOST480MHZ); + seed.StartLog(Constants::Developer::LOG_BLOCKS_BOOT); + + { + ld_but_1.Init(seed.GetPin(Constants::Hardware::PIN_BUT_1)); + ld_but_2.Init(seed.GetPin(Constants::Hardware::PIN_BUT_2)); + ld_but_3.Init(seed.GetPin(Constants::Hardware::PIN_BUT_3)); + ld_but_4.Init(seed.GetPin(Constants::Hardware::PIN_BUT_4)); + } + + { + ld::AdcChannelConfig adc_cfg[4]; + adc_cfg[0].InitSingle(ld::DaisySeed::GetPin(Constants::Hardware::PIN_POT_1)); + adc_cfg[1].InitSingle(ld::DaisySeed::GetPin(Constants::Hardware::PIN_POT_2)); + adc_cfg[2].InitSingle(ld::DaisySeed::GetPin(Constants::Hardware::PIN_POT_3)); + adc_cfg[3].InitSingle(ld::DaisySeed::GetPin(Constants::Hardware::PIN_POT_4)); + + seed.adc.Init(adc_cfg, 4); + seed.adc.Start(); + } + + seed.PrintLine("Setting Blocksize: %i", Constants::AUDIO_BUFFERSIZE); + seed.SetAudioBlockSize(Constants::AUDIO_BUFFERSIZE); + + seed.PrintLine("Setting Samplerate: %i", Constants::AUDIO_SAMPLERATE); + seed.SetAudioSampleRate(Constants::AUDIO_SAMPLERATE); + + seed.PrintLine("Initializing MIDI"); + ld::MidiUartHandler::Config midi_config{}; + midi.Init(midi_config); + + seed.PrintLine("Starting MIDI Receive"); + midi.StartReceiveRt([](const ld::MidiEvent& msg) { + switch (msg.srt_type) { + case ld::TimingClock: { + } break; + case ld::Start: { + } break; + case ld::Stop: { + } break; + case ld::Reset: { + } break; + case ld::Continue: { + } break; + } + }); + midi.Listen(); + + seed.PrintLine("Starting Audio"); + seed.StartAudio(audio_callback); + + osc.Init(Constants::AUDIO_SAMPLERATE); + osc.SetWaveform(osc.WAVE_SIN); + osc.SetAmp(1.F); + osc.SetFreq(100); + } + + + // ============================================================================================= + // RUN + // ============================================================================================= + + void audio_callback(ld::AudioHandle::InputBuffer in, ld::AudioHandle::OutputBuffer out, size_t size) + { + { + float osc_out{}; + for (size_t i = 0; i < size; i++) { + osc_out = osc.Process(); + osc_out *= 0.05; + out[0][i] = osc_out; + out[1][i] = osc_out; + } + } + + if constexpr (false) { + // Channel 1 + for (size_t i = 0; i < size; i++) { + out[0][i] = in[0][i]; + } + + // Channel 2 + for (size_t i = 0; i < size; i++) { + out[1][i] = in[1][i]; + } + } + } + + void mainloop() + { + seed.PrintLine("Entering MainLoop"); + u32 uptime_ms{}; + while (true) { + uptime_ms = ld::System::GetNow(); + ui_task.run_pending(uptime_ms); + heartbeat_task.run_pending(uptime_ms); + } + } + } // namespace +} // namespace Heck + + +int main() +{ + Heck::init(); + Heck::mainloop(); +} \ No newline at end of file diff --git a/examples/main_framework_proto1.hh b/examples/main_framework_proto1.hh new file mode 100644 index 0000000..61ebba6 --- /dev/null +++ b/examples/main_framework_proto1.hh @@ -0,0 +1,36 @@ +#ifndef HECK_LIBDIZZY_MAIN_FRAMEWORK_PROTO1_HH +#define HECK_LIBDIZZY_MAIN_FRAMEWORK_PROTO1_HH + +#include +#include "daisy_seed.h" +#include "daisysp.h" +#include "types.hh" + +namespace Heck { + namespace Constants { + namespace Hardware { + constexpr int PIN_BUT_1 = 27; + constexpr int PIN_BUT_2 = 28; + constexpr int PIN_BUT_3 = 29; + constexpr int PIN_BUT_4 = 30; + + constexpr int PIN_POT_1 = 15; + constexpr int PIN_POT_2 = 16; + constexpr int PIN_POT_3 = 17; + constexpr int PIN_POT_4 = 18; + + constexpr int BUT_FUZZ = 40; + } // namespace Hardware + + constexpr bool CPU_BOOST480MHZ = false; + constexpr int AUDIO_BUFFERSIZE = 4; + constexpr Samplerate AUDIO_SAMPLERATE = Samplerate::SAI_48KHZ; + + namespace Developer { + constexpr bool LOG_BLOCKS_BOOT = true; + } + } // namespace Constants +} // namespace Heck + + +#endif \ No newline at end of file diff --git a/src/utils.hh b/src/utils.hh index f8d3d53..c57f568 100644 --- a/src/utils.hh +++ b/src/utils.hh @@ -27,6 +27,59 @@ namespace Heck { u32 time_last_exec_{}; u32 time_period_{}; }; + + class PeriodicTask { + public: + using Task = std::function; + + PeriodicTask() = delete; + ~PeriodicTask() = default; + + PeriodicTask(u32 period, Task t) + { + init(period, t); + } + + PeriodicTask(PeriodicTask&) = delete; + PeriodicTask& operator=(PeriodicTask&) = delete; + + PeriodicTask(PeriodicTask&&) = delete; + PeriodicTask& operator=(PeriodicTask&&) = delete; + + void set_period(u32 time_units) + { + time_period_ = time_units; + } + + void set_task(Task t) + { + task_ = t; + } + + bool run_pending(u32 time_now) + { + if (time_now - time_last_exec_ >= time_period_) { + time_last_exec_ = time_now; + if (task_) { + //todo: constexpr if metrics + task_(time_now); + } + return true; + } + return false; + } + + private: + void init(u32 period, Task t) + { + set_period(period); + set_task(t); + } + + Task task_{}; + u32 time_last_exec_{}; + u32 time_period_{}; + }; } // namespace Heck namespace Heck { @@ -56,5 +109,69 @@ namespace Heck { private: T val_current_{}; }; + + template class PollingObserver { + + public: + using FetchCallback = std::function; + using DeliverCallback = std::function; + + PollingObserver() = delete; + ~PollingObserver() = default; + + explicit PollingObserver(FetchCallback fetch_cb, DeliverCallback deliver_cb) + { + init(fetch_cb, deliver_cb); + } + + PollingObserver(PollingObserver&) = delete; + PollingObserver& operator=(PollingObserver) = delete; + + PollingObserver(PollingObserver&&) = delete; + PollingObserver& operator=(PollingObserver&&) = delete; + + void init(FetchCallback fetch_cb, DeliverCallback deliver_cb) + { + set_fetch_callback(fetch_cb); + set_deliver_callback(deliver_cb); + } + + void set_fetch_callback(FetchCallback cb) + { + fetch_cb_ = cb; + } + + void set_deliver_callback(DeliverCallback cb) + { + deliver_cb_ = cb; + } + + bool fetch_and_deliver() + { + T val_fetched{ fetch_cb_() }; + if (val_fetched != val_current_) { + val_current_ = val_fetched; + deliver_cb_(val_current_); + return true; + } + return false; + } + + bool fetch_and_deliver_fuzzy(T min_deviation) + { + T val_fetched{ fetch_cb_() }; + if (std::abs(val_fetched - val_current_) >= min_deviation) { + val_current_ = val_fetched; + deliver_cb_(val_current_); + return true; + } + return false; + } + + private: + T val_current_{}; + FetchCallback fetch_cb_{}; + DeliverCallback deliver_cb_{}; + }; } // namespace Heck -#endif // HECK_DAISY_UTILS_HH \ No newline at end of file +#endif // HECK_DAISY_UTILS_HH` \ No newline at end of file