From 711c807d5d855d72fe1c30b18fcade008520a04b Mon Sep 17 00:00:00 2001
From: TheSlowGrowth <9356320+TheSlowGrowth@users.noreply.github.com>
Date: Fri, 9 Aug 2024 19:37:23 +0200
Subject: [PATCH 1/9] style: remove whitespaces
---
dsp/src/dsp/Recorder.h | 8 ++++----
dsp/tests/Recorder_gtest.cpp | 8 ++++----
firmware/src/LooperController.h | 8 ++++----
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/dsp/src/dsp/Recorder.h b/dsp/src/dsp/Recorder.h
index 47fd6ff..6645163 100644
--- a/dsp/src/dsp/Recorder.h
+++ b/dsp/src/dsp/Recorder.h
@@ -1,16 +1,16 @@
-/**
+/**
* Copyright (C) Johannes Elliesen, 2021
- *
+ *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
diff --git a/dsp/tests/Recorder_gtest.cpp b/dsp/tests/Recorder_gtest.cpp
index efcd11c..068822e 100644
--- a/dsp/tests/Recorder_gtest.cpp
+++ b/dsp/tests/Recorder_gtest.cpp
@@ -1,16 +1,16 @@
-/**
+/**
* Copyright (C) Johannes Elliesen, 2021
- *
+ *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
diff --git a/firmware/src/LooperController.h b/firmware/src/LooperController.h
index 44a6a4e..12cbc0a 100644
--- a/firmware/src/LooperController.h
+++ b/firmware/src/LooperController.h
@@ -1,16 +1,16 @@
-/**
+/**
* Copyright (C) Johannes Elliesen, 2021
- *
+ *
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
From 8ea658ee41a90690a1cb92a884d816d6e3b240e8 Mon Sep 17 00:00:00 2001
From: TheSlowGrowth <9356320+TheSlowGrowth@users.noreply.github.com>
Date: Fri, 16 Aug 2024 08:13:14 +0200
Subject: [PATCH 2/9] feat: apply CV inputs to volume and pitch
---
firmware/src/ui/LooperParameterProvider.h | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/firmware/src/ui/LooperParameterProvider.h b/firmware/src/ui/LooperParameterProvider.h
index 1cce526..a879b16 100644
--- a/firmware/src/ui/LooperParameterProvider.h
+++ b/firmware/src/ui/LooperParameterProvider.h
@@ -42,8 +42,10 @@ class LooperParameterProvider
/** Called by the LooperController */
float getGainParameter(size_t looperChannel) const
{
- return volumeSliderToGainMultiplier(controlInputs_[looperChannel].volumeSliderPosition)
- * volumeCvToGainMultiplier(controlInputs_[looperChannel].volumeCvVolts);
+ return std::clamp(volumeSliderToGainMultiplier(controlInputs_[looperChannel].volumeSliderPosition)
+ * volumeCvToGainMultiplier(controlInputs_[looperChannel].volumeCvVolts),
+ 0.0f,
+ 1.0f);
}
/** Called by the LooperController */
TapeProcessorParameters getProcessorParameters(size_t looperChannel) const
@@ -118,8 +120,7 @@ class LooperParameterProvider
float pitchCvToSemitones(float cvVolts) const
{
- (void) (cvVolts);
- return 0.0f; // TODO
+ return cvVolts * 12.0f;
}
float volumeSliderToGainMultiplier(float sliderValue) const
@@ -130,8 +131,7 @@ class LooperParameterProvider
float volumeCvToGainMultiplier(float cvVolts) const
{
- (void) (cvVolts);
- return 1.0f; // TODO
+ return cvVolts / 5.0f;
}
float getUnclampedSpeedParameter(size_t looperChannel) const
From 0dd02a48ce10f0c617f2999b1d09323fae341301 Mon Sep 17 00:00:00 2001
From: TheSlowGrowth <9356320+TheSlowGrowth@users.noreply.github.com>
Date: Fri, 16 Aug 2024 08:13:31 +0200
Subject: [PATCH 3/9] hardware: ignore some irrelevant DRC errors
---
.../mainboard/TapeLooper_MainBoard.kicad_pro | 25 ++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/hardware/mainboard/TapeLooper_MainBoard.kicad_pro b/hardware/mainboard/TapeLooper_MainBoard.kicad_pro
index 37f9264..ae40d8f 100644
--- a/hardware/mainboard/TapeLooper_MainBoard.kicad_pro
+++ b/hardware/mainboard/TapeLooper_MainBoard.kicad_pro
@@ -52,7 +52,30 @@
}
},
"diff_pair_dimensions": [],
- "drc_exclusions": [],
+ "drc_exclusions": [
+ "clearance|148441180|66660180|d210c00d-e8ca-453f-98c4-8eff554d0be9|446f493e-4139-4f9a-a333-e468b58079db",
+ "clearance|148441180|67160180|002b59ad-d45d-4ad6-b168-b0124bfe813e|446f493e-4139-4f9a-a333-e468b58079db",
+ "clearance|148841180|66660180|c0aec2fe-75c6-4fbc-b287-4598f2405880|9652f17e-4ac6-45a7-9fda-e684b061b4a8",
+ "clearance|148841180|66660180|c5bc26d4-d522-41fc-be0e-1aef511fa28a|9652f17e-4ac6-45a7-9fda-e684b061b4a8",
+ "clearance|148841180|66660180|d210c00d-e8ca-453f-98c4-8eff554d0be9|9652f17e-4ac6-45a7-9fda-e684b061b4a8",
+ "clearance|148841180|67160180|002b59ad-d45d-4ad6-b168-b0124bfe813e|9652f17e-4ac6-45a7-9fda-e684b061b4a8",
+ "copper_edge_clearance|203100000|74589640|3e6b317e-2525-4d23-8ecc-5108681fb852|f60fdbbd-6d66-470c-9136-5f6506bd4020",
+ "courtyards_overlap|109538121|112399639|00000000-0000-0000-0000-000061a1cf37|00000000-0000-0000-0000-000061a1cf5f",
+ "courtyards_overlap|109550821|133250499|00000000-0000-0000-0000-000061a1cf87|00000000-0000-0000-0000-000061a1cfaf",
+ "courtyards_overlap|133490321|112394559|00000000-0000-0000-0000-000061a1cfd7|00000000-0000-0000-0000-000061a1cfff",
+ "courtyards_overlap|133503021|133263096|00000000-0000-0000-0000-000061a1d027|00000000-0000-0000-0000-000061a1d04f",
+ "courtyards_overlap|157457761|133241334|00000000-0000-0000-0000-000061a1cf9b|00000000-0000-0000-0000-000061a1cfc3",
+ "courtyards_overlap|157465381|112387385|00000000-0000-0000-0000-000061a1cf4b|00000000-0000-0000-0000-000061a1cf73",
+ "courtyards_overlap|181420121|112386836|00000000-0000-0000-0000-000061a1cfeb|00000000-0000-0000-0000-000061a1d013",
+ "courtyards_overlap|181425201|133268279|00000000-0000-0000-0000-000061a1d03b|00000000-0000-0000-0000-000061bacfa0",
+ "courtyards_overlap|90226321|76198401|00000000-0000-0000-0000-0000619b7ab8|00000000-0000-0000-0000-0000619b7acc",
+ "courtyards_overlap|94666241|76198401|00000000-0000-0000-0000-0000619b7acc|00000000-0000-0000-0000-0000619b7ae0",
+ "courtyards_overlap|99027421|76198401|00000000-0000-0000-0000-0000619b7ae0|00000000-0000-0000-0000-0000619b7af4",
+ "solder_mask_bridge|148441180|66660180|446f493e-4139-4f9a-a333-e468b58079db|d210c00d-e8ca-453f-98c4-8eff554d0be9",
+ "solder_mask_bridge|148441180|67160180|446f493e-4139-4f9a-a333-e468b58079db|002b59ad-d45d-4ad6-b168-b0124bfe813e",
+ "solder_mask_bridge|148841180|66660180|9652f17e-4ac6-45a7-9fda-e684b061b4a8|d210c00d-e8ca-453f-98c4-8eff554d0be9",
+ "solder_mask_bridge|148841180|67160180|9652f17e-4ac6-45a7-9fda-e684b061b4a8|002b59ad-d45d-4ad6-b168-b0124bfe813e"
+ ],
"meta": {
"filename": "board_design_settings.json",
"version": 2
From 6e0e717bfb90ec8b6f53cad2c19e2dfb6afe6fdc Mon Sep 17 00:00:00 2001
From: TheSlowGrowth <9356320+TheSlowGrowth@users.noreply.github.com>
Date: Thu, 15 Aug 2024 21:37:34 +0200
Subject: [PATCH 4/9] feat: passthrough CV values
---
firmware/src/main.cpp | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp
index 0242ceb..f648dbe 100644
--- a/firmware/src/main.cpp
+++ b/firmware/src/main.cpp
@@ -147,6 +147,16 @@ void audioCallback(const float* const* in, float** out, size_t size)
{
cpuLoadMeter.OnBlockStart();
+ // update CV values
+ looperParameterProvider->controlInputs_[0].pitchCvVolts = uiHardware->getCvVolts(CvInput::chA_speed);
+ looperParameterProvider->controlInputs_[1].pitchCvVolts = uiHardware->getCvVolts(CvInput::chB_speed);
+ looperParameterProvider->controlInputs_[2].pitchCvVolts = uiHardware->getCvVolts(CvInput::chC_speed);
+ looperParameterProvider->controlInputs_[3].pitchCvVolts = uiHardware->getCvVolts(CvInput::chD_speed);
+ looperParameterProvider->controlInputs_[0].volumeCvVolts = uiHardware->getCvVolts(CvInput::chA_volume);
+ looperParameterProvider->controlInputs_[1].volumeCvVolts = uiHardware->getCvVolts(CvInput::chB_volume);
+ looperParameterProvider->controlInputs_[2].volumeCvVolts = uiHardware->getCvVolts(CvInput::chC_volume);
+ looperParameterProvider->controlInputs_[3].volumeCvVolts = uiHardware->getCvVolts(CvInput::chD_volume);
+
// peak meters
peakMeters[0].readPeaks(in[0]);
peakMeters[1].readPeaks(in[1]);
From be61dda0dfa1693472556563babb3e071f29276c Mon Sep 17 00:00:00 2001
From: TheSlowGrowth <9356320+TheSlowGrowth@users.noreply.github.com>
Date: Thu, 15 Aug 2024 23:52:25 +0200
Subject: [PATCH 5/9] feat: add a calibration page
---
firmware/src/ui/TapeLooperUi.h | 7 ++++-
firmware/src/ui/UiCalibrationPage.h | 47 +++++++++++++++++++++++++++++
firmware/src/ui/UiSettingsPage.h | 39 ++++++++++++++++++++----
3 files changed, 86 insertions(+), 7 deletions(-)
create mode 100644 firmware/src/ui/UiCalibrationPage.h
diff --git a/firmware/src/ui/TapeLooperUi.h b/firmware/src/ui/TapeLooperUi.h
index e208ff6..7686ffc 100644
--- a/firmware/src/ui/TapeLooperUi.h
+++ b/firmware/src/ui/TapeLooperUi.h
@@ -24,6 +24,7 @@
#include "LooperParameterProvider.h"
#include "UiBasePage.h"
+#include "UiCalibrationPage.h"
#include "UiSettingsPage.h"
#include "UiSavePage.h"
#include "UiLoadPage.h"
@@ -45,8 +46,10 @@ class TapeLooperUi
ParameterProviderType& looperParameterProvider) :
uiHardware_(uiHardware),
eventQueue_(eventQueue),
+ calibrationPage_(uiHardware),
settingsPage_(looperController,
- looperParameterProvider),
+ looperParameterProvider,
+ calibrationPage_),
savePage_(looperController),
loadPage_(looperController),
recordingPage_(looperController),
@@ -83,6 +86,8 @@ class TapeLooperUi
UiHardwareType& uiHardware_;
daisy::UiEventQueue& eventQueue_;
daisy::UI ui_;
+ UiCalibrationPage
+ calibrationPage_;
UiSettingsPage
diff --git a/firmware/src/ui/UiCalibrationPage.h b/firmware/src/ui/UiCalibrationPage.h
new file mode 100644
index 0000000..4c7e3b0
--- /dev/null
+++ b/firmware/src/ui/UiCalibrationPage.h
@@ -0,0 +1,47 @@
+/**
+ * Copyright (C) Johannes Elliesen, 2024
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include
+#include
+
+#include
+
+#include "../constants.h"
+#include "../hardware/UiHardwareTypes.h"
+#include "LooperParameterProvider.h"
+
+template
+class UiCalibrationPage : public daisy::UiPage
+{
+public:
+ UiCalibrationPage(UiHardwareType& uiHardware) :
+ uiHardware_(uiHardware)
+ {
+ }
+
+ virtual ~UiCalibrationPage() {}
+
+ bool IsOpaque(const daisy::UiCanvasDescriptor&) override { return false; }
+
+private:
+ UiCalibrationPage(const UiCalibrationPage&) = delete;
+ UiCalibrationPage& operator=(const UiCalibrationPage&) = delete;
+
+ UiHardwareType& uiHardware_;
+};
\ No newline at end of file
diff --git a/firmware/src/ui/UiSettingsPage.h b/firmware/src/ui/UiSettingsPage.h
index a880e38..c417913 100644
--- a/firmware/src/ui/UiSettingsPage.h
+++ b/firmware/src/ui/UiSettingsPage.h
@@ -33,9 +33,11 @@ class UiSettingsPage : public daisy::UiPage
{
public:
UiSettingsPage(LooperControllerType& looperController,
- LooperParameterProviderType& looperParameterProvider) :
+ LooperParameterProviderType& looperParameterProvider,
+ daisy::UiPage& calibrationPage) :
looperController_(looperController),
- looperParameterProvider_(looperParameterProvider)
+ looperParameterProvider_(looperParameterProvider),
+ calibrationPage_(calibrationPage)
{
}
@@ -120,17 +122,22 @@ class UiSettingsPage : public daisy::UiPage
void OnFocusGained() override
{
currentSetting_ = Setting::direction;
+ saveButtonState_ = false;
+ loadButtonState_ = false;
}
bool OnButton(uint16_t buttonID, uint8_t numberOfPresses, bool isRetriggering) override
{
(void) (isRetriggering); // ignore this argument
+ const Button button = Button(buttonID);
+
+ trackLoadAndSaveButtonStates(button, numberOfPresses);
+
// swallow but ignore button-up messages
if (numberOfPresses < 1)
return true;
- const Button button = Button(buttonID);
switch (button)
{
// channel up rocker switch
@@ -168,11 +175,14 @@ class UiSettingsPage : public daisy::UiPage
else if (currentSetting_ == Setting::motorLag)
Close(); // close this page
break;
- case Button::record:
+
+ // open calibration menu when load and save pressed at the same time
case Button::load:
case Button::save:
- Close(); // close this page
- return false; // pass event to the page below to open the respective page
+ if (saveButtonState_ && loadButtonState_)
+ {
+ GetParentUI()->OpenPage(calibrationPage_);
+ }
default:
break;
}
@@ -188,6 +198,18 @@ class UiSettingsPage : public daisy::UiPage
UiSettingsPage(const UiSettingsPage&) = delete;
UiSettingsPage& operator=(const UiSettingsPage&) = delete;
+ void trackLoadAndSaveButtonStates(Button button, int numberOfPresses)
+ {
+ if (button == Button::save)
+ {
+ saveButtonState_ = numberOfPresses > 0;
+ }
+ if (button == Button::load)
+ {
+ loadButtonState_ = numberOfPresses > 0;
+ }
+ }
+
void onUpButton(size_t looperChannel)
{
if (currentSetting_ == Setting::direction)
@@ -266,6 +288,11 @@ class UiSettingsPage : public daisy::UiPage
};
Setting currentSetting_ = Setting::direction;
+ bool saveButtonState_ = false;
+ bool loadButtonState_ = false;
+
LooperControllerType& looperController_;
LooperParameterProviderType& looperParameterProvider_;
+
+ daisy::UiPage& calibrationPage_;
};
\ No newline at end of file
From cf0d980f866e0453da2719ca5f3845c9a495ac68 Mon Sep 17 00:00:00 2001
From: TheSlowGrowth <9356320+TheSlowGrowth@users.noreply.github.com>
Date: Thu, 15 Aug 2024 23:54:00 +0200
Subject: [PATCH 6/9] refactor: expose KnobAndCvReader instead of raw CV values
---
firmware/src/hardware/UiHardware.h | 5 +----
firmware/src/main.cpp | 24 ++++++++++++++++--------
2 files changed, 17 insertions(+), 12 deletions(-)
diff --git a/firmware/src/hardware/UiHardware.h b/firmware/src/hardware/UiHardware.h
index 5404970..d063dba 100644
--- a/firmware/src/hardware/UiHardware.h
+++ b/firmware/src/hardware/UiHardware.h
@@ -340,10 +340,7 @@ class UiHardware
ledDriver_.SwapBuffersAndTransmit();
}
- float getCvVolts(CvInput cv) const
- {
- return knobsAndCv_.getCvVolts(cv);
- }
+ KnobAndCvReader& getKnobsAndCv() { return knobsAndCv_; }
// ===================================================================
// implements the button reader interface for the libDaisy UI system
diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp
index f648dbe..e333c2a 100644
--- a/firmware/src/main.cpp
+++ b/firmware/src/main.cpp
@@ -148,14 +148,22 @@ void audioCallback(const float* const* in, float** out, size_t size)
cpuLoadMeter.OnBlockStart();
// update CV values
- looperParameterProvider->controlInputs_[0].pitchCvVolts = uiHardware->getCvVolts(CvInput::chA_speed);
- looperParameterProvider->controlInputs_[1].pitchCvVolts = uiHardware->getCvVolts(CvInput::chB_speed);
- looperParameterProvider->controlInputs_[2].pitchCvVolts = uiHardware->getCvVolts(CvInput::chC_speed);
- looperParameterProvider->controlInputs_[3].pitchCvVolts = uiHardware->getCvVolts(CvInput::chD_speed);
- looperParameterProvider->controlInputs_[0].volumeCvVolts = uiHardware->getCvVolts(CvInput::chA_volume);
- looperParameterProvider->controlInputs_[1].volumeCvVolts = uiHardware->getCvVolts(CvInput::chB_volume);
- looperParameterProvider->controlInputs_[2].volumeCvVolts = uiHardware->getCvVolts(CvInput::chC_volume);
- looperParameterProvider->controlInputs_[3].volumeCvVolts = uiHardware->getCvVolts(CvInput::chD_volume);
+ looperParameterProvider->controlInputs_[0].pitchCvVolts =
+ uiHardware->getKnobsAndCv().getCvVolts(CvInput::chA_speed);
+ looperParameterProvider->controlInputs_[1].pitchCvVolts =
+ uiHardware->getKnobsAndCv().getCvVolts(CvInput::chB_speed);
+ looperParameterProvider->controlInputs_[2].pitchCvVolts =
+ uiHardware->getKnobsAndCv().getCvVolts(CvInput::chC_speed);
+ looperParameterProvider->controlInputs_[3].pitchCvVolts =
+ uiHardware->getKnobsAndCv().getCvVolts(CvInput::chD_speed);
+ looperParameterProvider->controlInputs_[0].volumeCvVolts =
+ uiHardware->getKnobsAndCv().getCvVolts(CvInput::chA_volume);
+ looperParameterProvider->controlInputs_[1].volumeCvVolts =
+ uiHardware->getKnobsAndCv().getCvVolts(CvInput::chB_volume);
+ looperParameterProvider->controlInputs_[2].volumeCvVolts =
+ uiHardware->getKnobsAndCv().getCvVolts(CvInput::chC_volume);
+ looperParameterProvider->controlInputs_[3].volumeCvVolts =
+ uiHardware->getKnobsAndCv().getCvVolts(CvInput::chD_volume);
// peak meters
peakMeters[0].readPeaks(in[0]);
From 413c8690d7f5986bec3ecb87ed5e0cefefe7190c Mon Sep 17 00:00:00 2001
From: TheSlowGrowth <9356320+TheSlowGrowth@users.noreply.github.com>
Date: Thu, 15 Aug 2024 23:56:25 +0200
Subject: [PATCH 7/9] feat: add CV calibration storage to UiHardware
---
firmware/src/hardware/UiHardware.h | 97 ++++++++++++++++++++++--------
firmware/src/main.cpp | 3 +-
2 files changed, 75 insertions(+), 25 deletions(-)
diff --git a/firmware/src/hardware/UiHardware.h b/firmware/src/hardware/UiHardware.h
index d063dba..9a5fdf5 100644
--- a/firmware/src/hardware/UiHardware.h
+++ b/firmware/src/hardware/UiHardware.h
@@ -154,8 +154,15 @@ class ButtonReader
class KnobAndCvReader
{
public:
+ KnobAndCvReader(daisy::QSPIHandle& qspi) :
+ calibrationStorage_(qspi)
+ {
+ }
+
void init()
{
+ calibrationStorage_.Init(getDefaultCalibrationData(), kCalibrationDataOffset);
+
const dsy_gpio_pin mux0 = { DSY_GPIOA, 1 };
const dsy_gpio_pin mux1 = { DSY_GPIOA, 0 };
const dsy_gpio_pin mux2 = { DSY_GPIOD, 11 };
@@ -224,7 +231,55 @@ class KnobAndCvReader
return 0.0f;
}
+ float getCvRaw(CvInput cv) const
+ {
+ switch (cv)
+ {
+ case CvInput::chA_speed:
+ return adc_.GetMuxFloat(0, 5);
+ case CvInput::chA_volume:
+ return adc_.GetMuxFloat(0, 6);
+ case CvInput::chB_speed:
+ return adc_.GetMuxFloat(1, 5);
+ case CvInput::chB_volume:
+ return adc_.GetMuxFloat(1, 6);
+ case CvInput::chC_speed:
+ return adc_.GetMuxFloat(2, 5);
+ case CvInput::chC_volume:
+ return adc_.GetMuxFloat(2, 6);
+ case CvInput::chD_speed:
+ return adc_.GetMuxFloat(3, 5);
+ case CvInput::chD_volume:
+ return adc_.GetMuxFloat(3, 6);
+ case CvInput::NUM_CVS:
+ break;
+ }
+ return 0.0f;
+ }
+
float getCvVolts(CvInput cv) const
+ {
+ const auto& coeffs = calibrationStorage_.GetSettings();
+ return getCvRaw(cv) * coeffs[int(cv)].scale
+ + coeffs[int(cv)].offset;
+ }
+
+ struct CvInCoefficients
+ {
+ bool operator==(const CvInCoefficients& other) const
+ {
+ return scale == other.scale && offset == other.offset;
+ }
+ float scale;
+ float offset;
+ };
+ using CalibrationData = std::array;
+
+ CalibrationData& getCalibrationData() { return calibrationStorage_.GetSettings(); }
+ void saveCalibrationData() { calibrationStorage_.Save(); }
+
+private:
+ static constexpr CalibrationData getDefaultCalibrationData()
{
/**
The general formula for the CV inputs is
@@ -262,31 +317,23 @@ class KnobAndCvReader
constexpr auto kScaleVolume = -4.852941f;
constexpr auto kOffsetVolume = 5.0f;
- switch (cv)
+ constexpr CalibrationData defaults = []() constexpr
{
- case CvInput::chA_speed:
- return adc_.GetMuxFloat(0, 5) * kScaleSpeed + kOffsetSpeed;
- case CvInput::chA_volume:
- return adc_.GetMuxFloat(0, 6) * kScaleVolume + kOffsetVolume;
- case CvInput::chB_speed:
- return adc_.GetMuxFloat(1, 5) * kScaleSpeed + kOffsetSpeed;
- case CvInput::chB_volume:
- return adc_.GetMuxFloat(1, 6) * kScaleVolume + kOffsetVolume;
- case CvInput::chC_speed:
- return adc_.GetMuxFloat(2, 5) * kScaleSpeed + kOffsetSpeed;
- case CvInput::chC_volume:
- return adc_.GetMuxFloat(2, 6) * kScaleVolume + kOffsetVolume;
- case CvInput::chD_speed:
- return adc_.GetMuxFloat(3, 5) * kScaleSpeed + kOffsetSpeed;
- case CvInput::chD_volume:
- return adc_.GetMuxFloat(3, 6) * kScaleVolume + kOffsetVolume;
- case CvInput::NUM_CVS:
- break;
- }
- return 0.0f;
+ CalibrationData result = { { 0.0f, 0.0f } };
+ for (size_t i = 0; i < result.size(); i++)
+ {
+ const auto isSpeedCv = i % 2 == 0;
+ result[i] = isSpeedCv ? CvInCoefficients { kScaleSpeed, kOffsetSpeed } : CvInCoefficients { kScaleVolume, kOffsetVolume };
+ }
+ return result;
+ }();
+
+ return defaults;
}
+ static constexpr auto kCalibrationDataOffset = 0;
+
+ mutable daisy::PersistentStorage calibrationStorage_;
-private:
daisy::AdcHandle adc_;
};
@@ -296,10 +343,12 @@ class UiHardware
using LedDriverType = daisy::LedDriverPca9685<3, false>;
using LedDmaBufferType = LedDriverType::DmaBuffer;
- UiHardware(daisy::UiEventQueue& eventQueue,
+ UiHardware(daisy::QSPIHandle& qspi,
+ daisy::UiEventQueue& eventQueue,
LedDmaBufferType bufferA,
LedDmaBufferType bufferB,
- uint16_t* shiftRegisterDmaBuffer)
+ uint16_t* shiftRegisterDmaBuffer) :
+ knobsAndCv_(qspi)
{
potMonitor_.Init(eventQueue, *this);
buttonMonitor_.Init(eventQueue,
diff --git a/firmware/src/main.cpp b/firmware/src/main.cpp
index e333c2a..4ee83ca 100644
--- a/firmware/src/main.cpp
+++ b/firmware/src/main.cpp
@@ -85,7 +85,8 @@ void flushLedDisplay(const daisy::UiCanvasDescriptor&)
void initUi()
{
// init the UI hardware
- auto& hardware = *uiHardware.create(uiEventQueue,
+ auto& hardware = *uiHardware.create(seed.qspi,
+ uiEventQueue,
ledDmaBufferA,
ledDmaBufferB,
&buttonShiftRegisterDmaBuffer);
From 0b514aa3b898f908c423d5d8ea1d2916fae65316 Mon Sep 17 00:00:00 2001
From: TheSlowGrowth <9356320+TheSlowGrowth@users.noreply.github.com>
Date: Thu, 15 Aug 2024 23:56:47 +0200
Subject: [PATCH 8/9] feat: implement calibration UI page
---
firmware/src/ui/UiCalibrationPage.h | 202 ++++++++++++++++++++++++++++
1 file changed, 202 insertions(+)
diff --git a/firmware/src/ui/UiCalibrationPage.h b/firmware/src/ui/UiCalibrationPage.h
index 4c7e3b0..17206f4 100644
--- a/firmware/src/ui/UiCalibrationPage.h
+++ b/firmware/src/ui/UiCalibrationPage.h
@@ -39,9 +39,211 @@ class UiCalibrationPage : public daisy::UiPage
bool IsOpaque(const daisy::UiCanvasDescriptor&) override { return false; }
+ void OnShow() override
+ {
+ state_ = State::idle;
+ recordedMin_ = 0.0f;
+ selectedChannel_ = -1;
+ }
+
+ void Draw(const daisy::UiCanvasDescriptor& canvas) override
+ {
+ UiHardwareType& hardware = *((UiHardwareType*) canvas.handle_);
+
+ // light up the load & save page leds to indicate calibration
+ hardware.setLed(Led::load, LedColour::pulsingRed);
+ hardware.setLed(Led::save, LedColour::pulsingRed);
+
+ // clear all channel octave leds
+ for (auto led : { Led::chA_m2,
+ Led::chA_m1,
+ Led::chA_p1,
+ Led::chA_p2,
+ Led::chB_m2,
+ Led::chB_m1,
+ Led::chB_p1,
+ Led::chB_p2,
+ Led::chC_m2,
+ Led::chC_m1,
+ Led::chC_p1,
+ Led::chC_p2,
+ Led::chD_m2,
+ Led::chD_m1,
+ Led::chD_p1,
+ Led::chD_p2 })
+ {
+ hardware.setLed(led, LedColour::off);
+ }
+
+ // still selecting an input ?
+ if (selectedChannel_ < 0)
+ {
+ hardware.setLed(Led::chA_m2, LedColour::pulsingYellow);
+ hardware.setLed(Led::chA_p2, LedColour::pulsingYellow);
+ hardware.setLed(Led::chB_m2, LedColour::pulsingYellow);
+ hardware.setLed(Led::chB_p2, LedColour::pulsingYellow);
+ hardware.setLed(Led::chC_m2, LedColour::pulsingYellow);
+ hardware.setLed(Led::chC_p2, LedColour::pulsingYellow);
+ hardware.setLed(Led::chD_m2, LedColour::pulsingYellow);
+ hardware.setLed(Led::chD_p2, LedColour::pulsingYellow);
+ }
+ else
+ {
+ constexpr Led lowestLeds[4] = { Led::chA_m2, Led::chB_m2, Led::chC_m2, Led::chD_m2 };
+ constexpr Led highestLeds[4] = { Led::chA_p2, Led::chB_p2, Led::chC_p2, Led::chD_p2 };
+
+ switch (state_)
+ {
+ case State::readMinSpeed:
+ hardware.setLed(lowestLeds[selectedChannel_], LedColour::pulsingRed);
+ break;
+ case State::readMaxSpeed:
+ hardware.setLed(highestLeds[selectedChannel_], LedColour::pulsingRed);
+ break;
+ case State::readMinVolume:
+ hardware.setLed(lowestLeds[selectedChannel_], LedColour::pulsingGreen);
+ break;
+ case State::readMaxVolume:
+ hardware.setLed(highestLeds[selectedChannel_], LedColour::pulsingGreen);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ bool OnButton(uint16_t buttonID, uint8_t numberOfPresses, bool isRetriggering) override
+ {
+ (void) (isRetriggering); // ignore
+
+ // consume but ignore button-up messages
+ if (numberOfPresses < 1)
+ return true;
+
+ const Button button = Button(buttonID);
+ switch (button)
+ {
+ // channel play buttons
+ case Button::chA_play:
+ channelButtonPressed(0);
+ break;
+ case Button::chB_play:
+ channelButtonPressed(1);
+ break;
+ case Button::chC_play:
+ channelButtonPressed(2);
+ break;
+ case Button::chD_play:
+ channelButtonPressed(3);
+ break;
+
+ case Button::save:
+ uiHardware_.getKnobsAndCv().getCalibrationData() = newCalibrationData_;
+ uiHardware_.getKnobsAndCv().saveCalibrationData();
+ Close();
+ break;
+
+ case Button::record:
+ case Button::load:
+ case Button::settings:
+ Close(); // close without saving
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+ }
+
+ bool OnPotMoved(uint16_t, float) override
+ {
+ return false; // passthrough pot events to the base page
+ }
+
private:
UiCalibrationPage(const UiCalibrationPage&) = delete;
UiCalibrationPage& operator=(const UiCalibrationPage&) = delete;
+ void channelButtonPressed(const int channel)
+ {
+ // a channel button was pressed.
+ // If it's the same channel, advance the calibration procedure.
+ if (channel == selectedChannel_)
+ {
+ constexpr CvInput speedCvForChannel[4] = { CvInput::chA_speed,
+ CvInput::chB_speed,
+ CvInput::chC_speed,
+ CvInput::chD_speed };
+ constexpr CvInput volumeCvForChannel[4] = { CvInput::chA_volume,
+ CvInput::chB_volume,
+ CvInput::chC_volume,
+ CvInput::chD_volume };
+ switch (state_)
+ {
+ case State::readMinSpeed:
+ {
+ recordedMin_ = uiHardware_.getKnobsAndCv().getCvRaw(speedCvForChannel[selectedChannel_]);
+ state_ = State::readMaxSpeed;
+ break;
+ }
+ case State::readMaxSpeed:
+ {
+ const auto recordedMax = uiHardware_.getKnobsAndCv().getCvRaw(speedCvForChannel[selectedChannel_]);
+ const auto coeffs = calculateCoefficients(recordedMin_, 0.0f, recordedMax, 2.0f);
+ newCalibrationData_[int(speedCvForChannel[selectedChannel_])] = coeffs;
+ state_ = State::readMinVolume;
+ break;
+ }
+ case State::readMinVolume:
+ {
+ recordedMin_ = uiHardware_.getKnobsAndCv().getCvRaw(volumeCvForChannel[selectedChannel_]);
+ state_ = State::readMaxVolume;
+ break;
+ }
+ case State::readMaxVolume:
+ {
+ const auto recordedMax = uiHardware_.getKnobsAndCv().getCvRaw(volumeCvForChannel[selectedChannel_]);
+ const auto coeffs = calculateCoefficients(recordedMin_, 0.0f, recordedMax, 5.0f);
+ newCalibrationData_[int(volumeCvForChannel[selectedChannel_])] = coeffs;
+ selectedChannel_ = -1;
+ state_ = State::idle;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ // if it's a different channel, select that channel and start the calibration readings
+ else
+ {
+ selectedChannel_ = channel;
+ state_ = State::readMinSpeed;
+ }
+ }
+
+ typename KnobAndCvReader::CvInCoefficients calculateCoefficients(float rawMin,
+ float realMin,
+ float rawMax,
+ float realMax)
+ {
+ const auto scale = (realMax - realMin) / (rawMax - rawMin);
+ const auto offset = realMax - scale * (rawMax);
+ return { scale, offset };
+ }
+
+ enum class State
+ {
+ readMinSpeed,
+ readMaxSpeed,
+ readMinVolume,
+ readMaxVolume,
+ idle
+ };
+
UiHardwareType& uiHardware_;
+ int selectedChannel_ = -1;
+ State state_ = State::idle;
+ float recordedMin_;
+ typename KnobAndCvReader::CalibrationData newCalibrationData_;
};
\ No newline at end of file
From 9d3aa44838c122680c576c90224122413cf668fa Mon Sep 17 00:00:00 2001
From: TheSlowGrowth <9356320+TheSlowGrowth@users.noreply.github.com>
Date: Fri, 16 Aug 2024 00:17:09 +0200
Subject: [PATCH 9/9] feat: close settings page before entering calibration
page
---
firmware/src/ui/UiSettingsPage.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/firmware/src/ui/UiSettingsPage.h b/firmware/src/ui/UiSettingsPage.h
index c417913..091c0e2 100644
--- a/firmware/src/ui/UiSettingsPage.h
+++ b/firmware/src/ui/UiSettingsPage.h
@@ -181,6 +181,7 @@ class UiSettingsPage : public daisy::UiPage
case Button::save:
if (saveButtonState_ && loadButtonState_)
{
+ Close();
GetParentUI()->OpenPage(calibrationPage_);
}
default: