From 9de6bc46fc3082955332fe925eb7a557ca9d1c75 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 24 Oct 2022 19:42:28 -0400 Subject: [PATCH 01/44] implemented a time signature to clock --- API/Inc/SuperClock.h | 11 ++++++++--- API/Src/SuperClock.cpp | 10 ++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/API/Inc/SuperClock.h b/API/Inc/SuperClock.h index fed5ad7..676ee11 100644 --- a/API/Inc/SuperClock.h +++ b/API/Inc/SuperClock.h @@ -29,15 +29,18 @@ class SuperClock { public: int pulse; // the current PPQN + uint8_t step; // current step. Will never exceed value of timeSignature uint16_t ticksPerStep; // how many TIM2 ticks per one step / quarter note uint16_t ticksPerPulse; // how many TIM2 ticks for one PPQN + uint8_t timeSignature; // value represents the number of quarter notes per bar (ie. 3/4, 4/4, 5/4, 6/4, 7/4) bool externalInputMode; Callback tickCallback; // this callback gets executed at a frequency equal to tim1_freq - Callback input_capture_callback; // this callback gets executed every on the rising edge of external input + Callback barResetCallback; // executes when clock.step exceeds the set time signature (ie. one bar) + Callback input_capture_callback; // this callback gets executed on the rising edge of an external input signal Callback ppqnCallback; // this callback gets executed at a rate equal to input capture / PPQN. It passes the current tick values as arguments Callback resetCallback; - Callback overflowCallback; // callback executes when all when a full step completes + Callback overflowCallback; // callback executes when a full step completes /** * TODO: initial inputCapture value should be the product of TIM1 and TIM2 prescaler values combined with 120 BPM @@ -48,6 +51,7 @@ class SuperClock { instance = this; ticksPerStep = 11129; ticksPerPulse = ticksPerStep / PPQN; + timeSignature = 4; }; void init(); @@ -64,7 +68,8 @@ class SuperClock { void attachInputCaptureCallback(Callback func); void attachPPQNCallback(Callback func); void attachResetCallback(Callback func); - + void attachBarResetCallback(Callback func); + // Low Level HAL interupt handlers void handleInputCaptureCallback(); void handleOverflowCallback(); diff --git a/API/Src/SuperClock.cpp b/API/Src/SuperClock.cpp index cf78951..441459d 100644 --- a/API/Src/SuperClock.cpp +++ b/API/Src/SuperClock.cpp @@ -201,6 +201,12 @@ void SuperClock::handleOverflowCallback() // external input will reset pulse to 0 and resume TIM4 in input capture callback } else { pulse = 0; + if (step < timeSignature - 1) { + step++; + } else { + step = 0; + if (barResetCallback) barResetCallback(); + } } } } @@ -220,6 +226,10 @@ void SuperClock::attachResetCallback(Callback func) resetCallback = func; } +void SuperClock::attachBarResetCallback(Callback func) { + barResetCallback = func; +} + void SuperClock::RouteOverflowCallback(TIM_HandleTypeDef *htim) { if (htim == &htim4) From 9216fffee3a9a0f709fa692bd08c72273fc5d6bd Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 24 Oct 2022 19:43:26 -0400 Subject: [PATCH 02/44] sequence now waits for the start of a bar before enabling recording --- Degree/Inc/GlobalControl.h | 1 + Degree/Inc/SuperSeq.h | 2 ++ Degree/Src/GlobalControl.cpp | 19 +++++++++++---- Degree/Src/SuperSeq.cpp | 7 +++++- Degree/Tasks/Inc/task_sequence_handler.h | 1 + Degree/Tasks/Src/task_sequence_handler.cpp | 27 +++++++++++++++++++++- 6 files changed, 50 insertions(+), 7 deletions(-) diff --git a/Degree/Inc/GlobalControl.h b/Degree/Inc/GlobalControl.h index c2e3846..4b5cb58 100644 --- a/Degree/Inc/GlobalControl.h +++ b/Degree/Inc/GlobalControl.h @@ -107,6 +107,7 @@ namespace DEGREE { void advanceSequencer(uint8_t pulse); void resetSequencer(uint8_t pulse); + void handleBarReset(); void handleFreeze(bool freeze); diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index 9e40b91..5a2478e 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -49,6 +49,7 @@ class SuperSeq { bool adaptiveLength; // flag determining if the sequence length should increase past its current length bool overdub; // flag gets set to true so that the sequence handler clears/overdubs existing events + bool recordArmed; // when true, sequence armed to record once a bar has overflowed bool recordEnabled; // when true, sequence will create and new events to the event list bool playbackEnabled; // when true, sequence will playback event list bool bendEnabled; // flag used for overriding current recorded bend with active bend @@ -82,6 +83,7 @@ class SuperSeq { void advance(); + void armRecording(); void enableRecording(); void disableRecording(); diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index e27508d..52e45cf 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -70,6 +70,7 @@ void GlobalControl::init() { // initialize tempo clock->init(); clock->attachResetCallback(callback(this, &GlobalControl::resetSequencer)); + clock->attachBarResetCallback(callback(this, &GlobalControl::handleBarReset)); clock->attachPPQNCallback(callback(this, &GlobalControl::advanceSequencer)); // always do this last clock->disableInputCaptureISR(); // pollTempoPot() will re-enable should pot be in teh right position currTempoPotValue = tempoPot.read_u16(); @@ -647,16 +648,15 @@ void GlobalControl::setSettingsBufferValue(int position, int channel, int data) */ void GlobalControl::advanceSequencer(uint8_t pulse) { - // if (pulse % 16 == 0) - // { - // display_dispatch_isr(DISPLAY_ACTION::PULSE_DISPLAY, CHAN::ALL, 0); - // } - if (pulse == 0) { tempoLED.write(HIGH); tempoGate.write(HIGH); + if (clock->step == 0) { + freezeLED.write(HIGH); + } } else if (pulse == 4) { tempoLED.write(LOW); + freezeLED.write(LOW); tempoGate.write(LOW); } @@ -679,6 +679,15 @@ void GlobalControl::resetSequencer(uint8_t pulse) dispatch_sequencer_event_ISR(CHAN::ALL, SEQ::CORRECT, 0); } +/** + * @brief function that triggers at the begining of every bar + * + */ +void GlobalControl::handleBarReset() +{ + dispatch_sequencer_event_ISR(CHAN::ALL, SEQ::BAR_RESET, 0); +} + void GlobalControl::handleFreeze(bool freeze) { if (this->gestureFlag) { diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index 5847538..f727063 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -79,17 +79,22 @@ void SuperSeq::advance() } void SuperSeq::enableRecording() { + this->recordArmed = false; this->recordEnabled = true; // if no currently recorded events, enable adaptive length if (!this->containsEvents()) { this->reset(); - this->setLength(2); + this->setLength(2); // set this to the max this->adaptiveLength = true; } else { this->adaptiveLength = false; } } +void SuperSeq::armRecording() { + this->recordArmed = true; +} + void SuperSeq::disableRecording() { this->recordEnabled = false; diff --git a/Degree/Tasks/Inc/task_sequence_handler.h b/Degree/Tasks/Inc/task_sequence_handler.h index 2920ff6..2e93407 100644 --- a/Degree/Tasks/Inc/task_sequence_handler.h +++ b/Degree/Tasks/Inc/task_sequence_handler.h @@ -11,6 +11,7 @@ enum class SEQ ADVANCE, FREEZE, RESET, + BAR_RESET, CLEAR_TOUCH, CLEAR_BEND, RECORD_ENABLE, diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index f918d8a..3b02091 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -14,6 +14,7 @@ void task_sequence_handler(void *params) sequencer_task_handle = xTaskGetCurrentTaskHandle(); sequencer_queue = xQueueCreate(96, sizeof(uint32_t)); uint32_t event = 0x0; + bool recordArmed = false; while (1) { // queue == [advance, advance, advance, clear, advance, freeze, advance, advance ] @@ -25,6 +26,11 @@ void task_sequence_handler(void *params) switch (action) { case SEQ::ADVANCE: + if (recordArmed) { + if (data % 24 == 0) { // data in this case is the current pulse + ctrl->recLED.toggle(); + } + } if (channel == CHAN::ALL) { for (int i = 0; i < CHANNEL_COUNT; i++) @@ -92,12 +98,31 @@ void task_sequence_handler(void *params) } break; + case SEQ::BAR_RESET: + if (recordArmed) { + for (int i = 0; i < CHANNEL_COUNT; i++) + { + if (ctrl->channels[i]->sequence.recordArmed) + { + ctrl->channels[i]->enableSequenceRecording(); + } + } + recordArmed = false; + } + break; + + // you are going to want to "wait" for the next bar to start + // you will probably do this by setting a flag in this block (when record gets enabled) + // then you will listen for this flag in the main ADVANCE block. Once the bar rolls over, then you + // enable recording case SEQ::RECORD_ENABLE: + recordArmed = true; for (int i = 0; i < CHANNEL_COUNT; i++) - ctrl->channels[i]->enableSequenceRecording(); + ctrl->channels[i]->sequence.armRecording(); break; case SEQ::RECORD_DISABLE: + recordArmed = false; for (int i = 0; i < CHANNEL_COUNT; i++) ctrl->channels[i]->disableSequenceRecording(); break; From f1f08bde6836a90cc4fe2b33ccd0b5520585bd40 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sat, 29 Oct 2022 11:23:47 -0400 Subject: [PATCH 03/44] implemented superclock reset --- API/Inc/SuperClock.h | 2 ++ API/Src/SuperClock.cpp | 8 ++++++++ Degree/Tasks/Src/task_sequence_handler.cpp | 6 ++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/API/Inc/SuperClock.h b/API/Inc/SuperClock.h index 676ee11..2f7c17f 100644 --- a/API/Inc/SuperClock.h +++ b/API/Inc/SuperClock.h @@ -57,7 +57,9 @@ class SuperClock { void init(); void initTIM2(uint16_t prescaler, uint32_t period); void initTIM4(uint16_t prescaler, uint16_t period); + void start(); + void reset(); void setPulseFrequency(uint32_t ticks); uint16_t convertADCReadToTicks(uint16_t min, uint16_t max, uint16_t value); diff --git a/API/Src/SuperClock.cpp b/API/Src/SuperClock.cpp index 441459d..e9240be 100644 --- a/API/Src/SuperClock.cpp +++ b/API/Src/SuperClock.cpp @@ -19,6 +19,14 @@ void SuperClock::start() error_handler(status); } +void SuperClock::reset() +{ + __HAL_TIM_SetCounter(&htim2, 0); // not certain this has to happen, just assuming + __HAL_TIM_SetCounter(&htim4, 0); + this->pulse = 0; + this->step = 0; +} + /** * @brief initialize TIM2 as a slave to TIM1 * @param prescaler setting to 1 should be best diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index 3b02091..f08e691 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -63,6 +63,7 @@ void task_sequence_handler(void *params) break; case SEQ::RESET: + ctrl->clock->reset(); if (channel == CHAN::ALL) { for (int i = 0; i < CHANNEL_COUNT; i++) @@ -108,13 +109,10 @@ void task_sequence_handler(void *params) } } recordArmed = false; + ctrl->recLED.write(1); } break; - // you are going to want to "wait" for the next bar to start - // you will probably do this by setting a flag in this block (when record gets enabled) - // then you will listen for this flag in the main ADVANCE block. Once the bar rolls over, then you - // enable recording case SEQ::RECORD_ENABLE: recordArmed = true; for (int i = 0; i < CHANNEL_COUNT; i++) From ca40bdee248c4509f73a0dcb097592f63a24bc7c Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sat, 29 Oct 2022 15:34:59 -0400 Subject: [PATCH 04/44] better seq length handling + record disarm by the bar --- Degree/Inc/SuperSeq.h | 2 -- Degree/Src/GlobalControl.cpp | 7 ++-- Degree/Src/SuperSeq.cpp | 33 ++----------------- Degree/Tasks/Inc/task_sequence_handler.h | 2 ++ Degree/Tasks/Src/task_sequence_handler.cpp | 38 +++++++++++++--------- 5 files changed, 30 insertions(+), 52 deletions(-) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index 5a2478e..9e40b91 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -49,7 +49,6 @@ class SuperSeq { bool adaptiveLength; // flag determining if the sequence length should increase past its current length bool overdub; // flag gets set to true so that the sequence handler clears/overdubs existing events - bool recordArmed; // when true, sequence armed to record once a bar has overflowed bool recordEnabled; // when true, sequence will create and new events to the event list bool playbackEnabled; // when true, sequence will playback event list bool bendEnabled; // flag used for overriding current recorded bend with active bend @@ -83,7 +82,6 @@ class SuperSeq { void advance(); - void armRecording(); void enableRecording(); void disableRecording(); diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index 52e45cf..19a6c07 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -412,13 +412,12 @@ void GlobalControl::handleButtonPress(int pad) if (!recordEnabled) { recLED.write(1); - dispatch_sequencer_event(CHAN::ALL, SEQ::RECORD_ENABLE, 0); + dispatch_sequencer_event(CHAN::ALL, SEQ::RECORD_ARM, 0); recordEnabled = true; } else { - recLED.write(0); - dispatch_sequencer_event(CHAN::ALL, SEQ::RECORD_DISABLE, 0); + dispatch_sequencer_event(CHAN::ALL, SEQ::RECORD_DISARM, 0); recordEnabled = false; } break; @@ -465,7 +464,7 @@ void GlobalControl::handleButtonRelease(int pad) if (touchPads->padIsTouched(i, currTouched)) { dispatch_sequencer_event((CHAN)i, SEQ::CLEAR_TOUCH, 0); - if (!recordEnabled) + if (recordEnabled) dispatch_sequencer_event((CHAN)i, SEQ::RECORD_DISABLE, 0); } } diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index f727063..5539ea0 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -79,50 +79,21 @@ void SuperSeq::advance() } void SuperSeq::enableRecording() { - this->recordArmed = false; this->recordEnabled = true; // if no currently recorded events, enable adaptive length if (!this->containsEvents()) { this->reset(); - this->setLength(2); // set this to the max this->adaptiveLength = true; } else { this->adaptiveLength = false; } } -void SuperSeq::armRecording() { - this->recordArmed = true; -} - void SuperSeq::disableRecording() { this->recordEnabled = false; - - // if events were recorded and adaptive length was enabled, update the seq length - if (adaptiveLength) - { - this->adaptiveLength = false; - if (this->containsEvents()) - { - // is all this zero indexed? - if (this->currStep <= SEQ_LENGTH_BLOCK_1) - { - this->setLength(SEQ_LENGTH_BLOCK_1); // 8 steps - } - else if (this->currStep > SEQ_LENGTH_BLOCK_1 && this->currStep <= SEQ_LENGTH_BLOCK_2) - { - this->setLength(SEQ_LENGTH_BLOCK_2); - } - else if (this->currStep > SEQ_LENGTH_BLOCK_2 && this->currStep <= SEQ_LENGTH_BLOCK_3) - { - this->setLength(SEQ_LENGTH_BLOCK_3); - } - else if (this->currStep > SEQ_LENGTH_BLOCK_3) - { - this->setLength(SEQ_LENGTH_BLOCK_4); - } - } + if (this->containsEvents() && adaptiveLength) { + this->setLength(this->currStep); } } diff --git a/Degree/Tasks/Inc/task_sequence_handler.h b/Degree/Tasks/Inc/task_sequence_handler.h index 2e93407..1db57ae 100644 --- a/Degree/Tasks/Inc/task_sequence_handler.h +++ b/Degree/Tasks/Inc/task_sequence_handler.h @@ -16,6 +16,8 @@ enum class SEQ CLEAR_BEND, RECORD_ENABLE, RECORD_DISABLE, + RECORD_ARM, + RECORD_DISARM, SET_LENGTH, QUANTIZE, CORRECT, diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index f08e691..c446560 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -14,7 +14,9 @@ void task_sequence_handler(void *params) sequencer_task_handle = xTaskGetCurrentTaskHandle(); sequencer_queue = xQueueCreate(96, sizeof(uint32_t)); uint32_t event = 0x0; - bool recordArmed = false; + bool recordArm = false; // flag used to enable recording once X number of steps have passed + bool recordDisarm = false; // flag used to disable recording once X number of steps have passed + while (1) { // queue == [advance, advance, advance, clear, advance, freeze, advance, advance ] @@ -26,7 +28,7 @@ void task_sequence_handler(void *params) switch (action) { case SEQ::ADVANCE: - if (recordArmed) { + if (recordArm || recordDisarm) { if (data % 24 == 0) { // data in this case is the current pulse ctrl->recLED.toggle(); } @@ -100,31 +102,37 @@ void task_sequence_handler(void *params) break; case SEQ::BAR_RESET: - if (recordArmed) { - for (int i = 0; i < CHANNEL_COUNT; i++) - { - if (ctrl->channels[i]->sequence.recordArmed) - { - ctrl->channels[i]->enableSequenceRecording(); - } - } - recordArmed = false; - ctrl->recLED.write(1); + if (recordArm) { + recordArm = false; + dispatch_sequencer_event(CHAN::ALL, SEQ::RECORD_ENABLE, 0); + } else if (recordDisarm) { + recordDisarm = false; + dispatch_sequencer_event(CHAN::ALL, SEQ::RECORD_DISABLE, 0); } break; case SEQ::RECORD_ENABLE: - recordArmed = true; + ctrl->recLED.write(1); for (int i = 0; i < CHANNEL_COUNT; i++) - ctrl->channels[i]->sequence.armRecording(); + ctrl->channels[i]->enableSequenceRecording(); break; case SEQ::RECORD_DISABLE: - recordArmed = false; + ctrl->recLED.write(0); for (int i = 0; i < CHANNEL_COUNT; i++) ctrl->channels[i]->disableSequenceRecording(); break; + case SEQ::RECORD_ARM: + recordArm = true; + recordDisarm = false; + break; + + case SEQ::RECORD_DISARM: + recordArm = false; + recordDisarm = true; + break; + case SEQ::SET_LENGTH: ctrl->channels[channel]->updateSequenceLength(data); break; From deaf9fff0b5e3126a13df8d2fd891cc0c998037a Mon Sep 17 00:00:00 2001 From: scottc11 Date: Wed, 2 Nov 2022 06:36:28 -0400 Subject: [PATCH 05/44] added sequencer progress --- Degree/Inc/SuperSeq.h | 9 +++++---- Degree/Src/GlobalControl.cpp | 2 +- Degree/Src/SuperSeq.cpp | 2 ++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index 9e40b91..eb22e51 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -10,10 +10,8 @@ #define SEQ_EVENT_GATE_BIT 4 #define SEQ_EVENT_INDEX_BIT_MASK 0b00001111 -#define SEQ_LENGTH_BLOCK_1 (MAX_SEQ_LENGTH / 4) // 1 bar -#define SEQ_LENGTH_BLOCK_2 (MAX_SEQ_LENGTH / 2) // 2 bars -#define SEQ_LENGTH_BLOCK_3 ((MAX_SEQ_LENGTH / 2) + SEQ_LENGTH_BLOCK_1) // 3 bars -#define SEQ_LENGTH_BLOCK_4 (MAX_SEQ_LENGTH) // 4 bars +#define SEQ_PROGRESS_MAX 16 // "15" +#define SEQ_PROGRESS_MIN 1 // "0" typedef struct SequenceNode { @@ -47,6 +45,9 @@ class SuperSeq { int prevEventPos; // represents the position of the last event which got triggered (either HIGH or LOW) int newEventPos; // when a new event is created, we store the position in this variable in case we need it for something (ie. sequence overdubing) + int progressDiviser; // lengthPPQN / 16 (num LEDs used) + int progressCounter; // current + bool adaptiveLength; // flag determining if the sequence length should increase past its current length bool overdub; // flag gets set to true so that the sequence handler clears/overdubs existing events bool recordEnabled; // when true, sequence will create and new events to the event list diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index 19a6c07..d6dd2fe 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -464,7 +464,7 @@ void GlobalControl::handleButtonRelease(int pad) if (touchPads->padIsTouched(i, currTouched)) { dispatch_sequencer_event((CHAN)i, SEQ::CLEAR_TOUCH, 0); - if (recordEnabled) + if (!recordEnabled) // this chunk is a shortcut for clearing the display dispatch_sequencer_event((CHAN)i, SEQ::RECORD_DISABLE, 0); } } diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index 5539ea0..46859b7 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -83,6 +83,7 @@ void SuperSeq::enableRecording() { // if no currently recorded events, enable adaptive length if (!this->containsEvents()) { this->reset(); + this->setLength(2); this->adaptiveLength = true; } else { this->adaptiveLength = false; @@ -252,6 +253,7 @@ void SuperSeq::setLength(int steps) { length = steps; lengthPPQN = length * PPQN; + progressDiviser = lengthPPQN / SEQ_PROGRESS_MAX; } }; From 7af013e955c47c1743ff732e7f5e8078654feb8a Mon Sep 17 00:00:00 2001 From: scottc11 Date: Thu, 3 Nov 2022 06:05:32 -0400 Subject: [PATCH 06/44] changed order of seq advance vs. seq handling --- Degree/Tasks/Src/task_sequence_handler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index c446560..43510b1 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -37,12 +37,12 @@ void task_sequence_handler(void *params) { for (int i = 0; i < CHANNEL_COUNT; i++) { - ctrl->channels[i]->sequence.advance(); ctrl->channels[i]->handleClock(); + ctrl->channels[i]->sequence.advance(); } } else { - ctrl->channels[channel]->sequence.advance(); ctrl->channels[channel]->handleClock(); + ctrl->channels[channel]->sequence.advance(); } break; From 319e1eb9735fb84d1af58a2e5dcce10cfa53694c Mon Sep 17 00:00:00 2001 From: scottc11 Date: Fri, 4 Nov 2022 06:47:35 -0400 Subject: [PATCH 07/44] reworked toolchain installation instructions --- README.md | 61 ++++++++++++++++--------------------------------------- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index b0b39ea..e2fde39 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,31 @@ -# TODO: -- How do I print to the console through an ST-Link? - - RX/TX connector from St-Link need to be wired to the TX/RX pins of the MCU - - you probably need StMicroelectronics STLink Virtual COM Port driver installed somewhere (dev/tty folder or file) - -- why is my intellisense not detecting errors prior to building? - google "gcc arm problem matcher vscode" - -## FreeRTOS Configuration -Could all this not be done just using a timer? Or is that what an RTOS essential is? -``` - * Task 1: - * - executes at a frequency of 1 quarter note - * - updates tempo / timer to match external clock - * - * Task 2: - * - executes at a frequency of PPQN - * - advances the sequencer - * - handles any interface changes (ie. touch, bend, UI buttons) - * - updates LEDs - * - updates DACs - * - * Task 3: - * - executes at a frequency of PPQN * 8? 🤷‍♂️ - * - reads and filters ADCs - * - * Task 4: - * - low priority for printf() - * - * State Struct: - * - is shared globally between tasks - * - holds a series of "flags" for which to tell each task which code it should execute - * - * Software Timers: - - this is what MBED must have used to create timed events. - - if the RTOS is clocked by the sequencer clock, this could be very usefull - * -``` - ## Toolchain -#### Install OpenOCD for ST-Link Debugging +### Install OpenOCD for ST-Link Debugging +ref: [GDB and OpenOCD](https://openocd.org/doc-release/html/GDB-and-OpenOCD.html#GDB-and-OpenOCD) ``` brew install openocd ``` +### Install [ARM Embedded Toolchain](https://developer.arm.com/downloads/-/gnu-rm) + +This toolchain contains the C compiler, GDB, etc. + +The commands below tap a repository which holds the latest version of the toolchain and then installs it into `/opt/hombrew/bin`. You need to specify this path in VSCode `c_cpp_properties.json`. -#### Install ARM Embedded Toolchain ``` -# not sure about the brew tap, but it works. -brew tap PX4/homebrew-px4 +brew tap ArmMbed/homebrew-formulae brew update -brew install gcc-arm-none-eabi +brew install arm-none-eabi-gcc arm-none-eabi-gcc --version ``` +### GDB +GDB is apart of the gcc-arm-none-eabi toolchain. It can be accessed via `arm-none-eabi-gdb` + +### Install dfu-util for firmware upload over USB +``` +brew install dfu-util +``` + #### Pull submodules ``` git submodule init From b5da5cd4a575a11462a082e8b11d8bc7746d78b6 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sun, 27 Nov 2022 10:58:37 -0500 Subject: [PATCH 08/44] re-wrote seq progress display, cleaned up associated bugs --- Degree/Inc/SuperSeq.h | 8 +++- Degree/Inc/TouchChannel.h | 2 +- Degree/Src/SuperSeq.cpp | 9 +++- Degree/Src/TouchChannel.cpp | 95 +++++++++++++++---------------------- 4 files changed, 51 insertions(+), 63 deletions(-) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index eb22e51..495ce48 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -10,8 +10,8 @@ #define SEQ_EVENT_GATE_BIT 4 #define SEQ_EVENT_INDEX_BIT_MASK 0b00001111 -#define SEQ_PROGRESS_MAX 16 // "15" -#define SEQ_PROGRESS_MIN 1 // "0" +#define SEQ_PROGRESS_MAX 15 // "15" +#define SEQ_PROGRESS_MIN 0 // "0" typedef struct SequenceNode { @@ -47,9 +47,11 @@ class SuperSeq { int progressDiviser; // lengthPPQN / 16 (num LEDs used) int progressCounter; // current + int progress; bool adaptiveLength; // flag determining if the sequence length should increase past its current length bool overdub; // flag gets set to true so that the sequence handler clears/overdubs existing events + bool overwriteExistingEvents; bool recordEnabled; // when true, sequence will create and new events to the event list bool playbackEnabled; // when true, sequence will playback event list bool bendEnabled; // flag used for overriding current recorded bend with active bend @@ -77,6 +79,8 @@ class SuperSeq { int getLength(); int getLengthPPQN(); + void setProgress(); + int getNextPosition(int position); int getPrevPosition(int position); int getLastPosition(); diff --git a/Degree/Inc/TouchChannel.h b/Degree/Inc/TouchChannel.h index 9b414b1..c2ccdf3 100644 --- a/Degree/Inc/TouchChannel.h +++ b/Degree/Inc/TouchChannel.h @@ -209,7 +209,7 @@ namespace DEGREE { void updateSequenceLength(uint8_t steps); void setSequenceLED(uint8_t step, uint8_t pwm, bool blink); void drawSequenceToDisplay(bool blink); - void stepSequenceLED(int currStep, int prevStep, int length); + void stepSequenceLED(); void enableSequenceRecording(); void disableSequenceRecording(); void handleQuantAmountLEDs(); diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index 46859b7..71cbdbe 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -86,14 +86,15 @@ void SuperSeq::enableRecording() { this->setLength(2); this->adaptiveLength = true; } else { - this->adaptiveLength = false; + this->overwriteExistingEvents = true; } } void SuperSeq::disableRecording() { this->recordEnabled = false; - + this->overwriteExistingEvents = false; if (this->containsEvents() && adaptiveLength) { + this->adaptiveLength = false; this->setLength(this->currStep); } } @@ -257,6 +258,10 @@ void SuperSeq::setLength(int steps) } }; +void SuperSeq::setProgress() { + this->progress = this->currPosition / this->progressDiviser; +} + /** * @brief set the level new events get quantized too */ diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 6196bcf..4b142fe 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -173,8 +173,6 @@ void TouchChannel::setPlaybackMode(PlaybackMode targetMode) break; case MONO_LOOP: sequence.playbackEnabled = true; - drawSequenceToDisplay(false); - setSequenceLED(sequence.currStep, PWM::PWM_HIGH, false); setLED(CHANNEL_REC_LED, ON, false); setOctaveLed(currOctave, LedState::ON, false); triggerNote(currDegree, currOctave, SUSTAIN); @@ -186,8 +184,6 @@ void TouchChannel::setPlaybackMode(PlaybackMode targetMode) break; case QUANTIZER_LOOP: sequence.playbackEnabled = true; - drawSequenceToDisplay(false); - setSequenceLED(sequence.currStep, PWM::PWM_HIGH, false); setLED(CHANNEL_REC_LED, ON, false); setActiveDegrees(activeDegrees); triggerNote(currDegree, currOctave, NOTE_OFF); @@ -996,12 +992,11 @@ void TouchChannel::setActiveOctaves(int octave) */ void TouchChannel::handleSequence(int position) { - // always display sequence progression regardless if there are events or not - if (sequence.currStep != sequence.prevStep) // only set led every step - { + // don't show progress when recording a fresh sequence. Only if there is an existing sequence + if (!sequence.recordEnabled || sequence.overwriteExistingEvents) { if (uiMode == UIMode::UI_PLAYBACK) { - stepSequenceLED(sequence.currStep, sequence.prevStep, sequence.length); + stepSequenceLED(); } } @@ -1084,7 +1079,7 @@ void TouchChannel::resetSequence() sequence.reset(); if (sequence.containsEvents() || sequence.recordEnabled) { - drawSequenceToDisplay(false); + // drawSequenceToDisplay(false); } } @@ -1099,15 +1094,18 @@ void TouchChannel::updateSequenceLength(uint8_t steps) drawSequenceToDisplay(true); } +static const int DISPLAY_PATTERN_LED_MAP[16] = { 1, 11, 12, 7, 8, 15, 5, 14, 4, 3, 9, 2, 10, 0, 6, 13 }; +static const int SEQ_LED_PROGRESS_PWM_MAP[16] = {1, 7, 15, 25, 33, 40, 47, 54, 60, 70, 78, 84, 90, 100, 111, 127}; + /** - * @brief given a sequence step, - * - * @param step + * @brief map a given step in the sequence to an display LED + * + * @param step */ -void TouchChannel::setSequenceLED(uint8_t step, uint8_t pwm, bool blink) +void +TouchChannel::setSequenceLED(uint8_t step, uint8_t pwm, bool blink) { - uint8_t ledIndex = step / 2; // 32 step seq displayed with 16 LEDs - display->setChannelLED(channelIndex, ledIndex, pwm, blink); // it is possible ledIndex needs to be subracted by 1 🤔 + display->setChannelLED(channelIndex, DISPLAY_PATTERN_LED_MAP[step], pwm, blink); } /** @@ -1115,57 +1113,40 @@ void TouchChannel::setSequenceLED(uint8_t step, uint8_t pwm, bool blink) */ void TouchChannel::drawSequenceToDisplay(bool blink) { - for (int i = 0; i < MAX_SEQ_LENGTH; i += 2) + int counter = 0; + while (counter < sequence.progress) { - if (i < sequence.length) - { - if (i == sequence.currStep && sequence.playbackEnabled && uiMode == UIMode::UI_PLAYBACK) - { - setSequenceLED(i, PWM::PWM_HIGH, blink); - } - else - { - setSequenceLED(i, PWM::PWM_LOW_MID, blink); - } - } - else - { - setSequenceLED(i, PWM::PWM_OFF, blink); - } + display->setChannelLED(channelIndex, DISPLAY_SPIRAL_LED_MAP[counter], SEQ_LED_PROGRESS_PWM_MAP[counter], blink); + counter++; } } -void TouchChannel::stepSequenceLED(int currStep, int prevStep, int length) -{ - if (sequence.currStepPosition == 0) - { - if (currStep % 2 == 0) - { - // set currStep PWM High - setSequenceLED(currStep, PWM::PWM_HIGH, false); +// this is going to have to take a ppqn instead of a step +void TouchChannel::stepSequenceLED() +{ + if (sequence.currPosition == 0) { + display->clear(channelIndex); + } + + // TODO: maybe flash for every step? - // handle odd sequence lengths. - if (length % 2 == 1) - { - // The last LED in sequence gets set to a different PWM - if (prevStep == length - 1) - { - setSequenceLED(prevStep, PWM::PWM_LOW, false); - } - } - // regular sequence lengths - else - { - // set prevStep PWM back to Mid - setSequenceLED(prevStep, PWM::PWM_LOW_MID, false); - } - } + if (sequence.currPosition % sequence.progressDiviser == 0) { // check if this ppqn should advance the LEDs + sequence.setProgress(); + display->setChannelLED(channelIndex, DISPLAY_SPIRAL_LED_MAP[sequence.progress], SEQ_LED_PROGRESS_PWM_MAP[sequence.progress], sequence.recordEnabled); } } void TouchChannel::enableSequenceRecording() { sequence.enableRecording(); + display->enableBlink(); + + // if no sequence exists, light all 16 seq leds and flash them + if (!sequence.containsEvents()) { + display->fill(this->channelIndex, 20, true); + } else { + drawSequenceToDisplay(sequence.recordEnabled); + } if (playbackMode == MONO) { @@ -1185,12 +1166,10 @@ void TouchChannel::enableSequenceRecording() void TouchChannel::disableSequenceRecording() { sequence.disableRecording(); - + display->disableBlink(); // if a touch event was recorded, remain in loop mode if (sequence.containsEvents()) { - // make sure to update the display so it shows the new seq length - display->clear(channelIndex); drawSequenceToDisplay(false); return; } From 7d558b39073c8d041a381255c94bab371c3d680e Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sun, 27 Nov 2022 15:46:39 -0500 Subject: [PATCH 09/44] closed #188 sequence ratcheting bug --- Degree/Inc/TouchChannel.h | 4 +++- Degree/Src/TouchChannel.cpp | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Degree/Inc/TouchChannel.h b/Degree/Inc/TouchChannel.h index 9b414b1..3dcc5fd 100644 --- a/Degree/Inc/TouchChannel.h +++ b/Degree/Inc/TouchChannel.h @@ -133,7 +133,9 @@ namespace DEGREE { bool led_state[16]; - uint8_t currRatchetRate; // + uint8_t currRatchetRate; + uint8_t prevRatchetRate; + bool gateState; // state of the gate output UIMode uiMode; diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 6196bcf..60e0c8d 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -628,9 +628,12 @@ uint8_t TouchChannel::calculateRatchet(uint16_t bend) /** * @brief based on the current position of the sequencer clock, toggle gate on / off + * @param position sequence position + * @param value amount of bend */ void TouchChannel::handleRatchet(int position, uint16_t value) { + prevRatchetRate = currRatchetRate; currRatchetRate = calculateRatchet(value); if (currRatchetRate != 0 && position % currRatchetRate == 0) { @@ -639,8 +642,11 @@ void TouchChannel::handleRatchet(int position, uint16_t value) } else { - setGate(LOW); - setLED(CHANNEL_RATCHET_LED, OFF, true); + if (prevRatchetRate != 0) + { + setGate(LOW); + setLED(CHANNEL_RATCHET_LED, OFF, true); + } } } @@ -1063,9 +1069,12 @@ void TouchChannel::handleSequence(int position) } } - // Handle Bend Events + // Handle Bend Events (only if the bender is currently idle / inactive) if (sequence.containsBendEvents) { + // somewhere in here, if bend events exists and ratcheting is enabled, then + // an idle bend value is going to set the gate LOW (each PPQN). What needs to happen, + // is if bend idle && sequence bend is == idle, then don't trigger the gate if (bender->isIdle()) { this->handleBend(sequence.getBend(position)); From 3bcbe2b00e8bcc1b7a54baee39f4577e96e1f390 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Thu, 1 Dec 2022 08:15:07 -0500 Subject: [PATCH 10/44] #182 better handling of low frequency adc signals for quantization --- Degree/Inc/TouchChannel.h | 3 +- Degree/Src/TouchChannel.cpp | 80 ++++++++++++++++++------------------- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/Degree/Inc/TouchChannel.h b/Degree/Inc/TouchChannel.h index 3dcc5fd..893fee3 100644 --- a/Degree/Inc/TouchChannel.h +++ b/Degree/Inc/TouchChannel.h @@ -129,7 +129,7 @@ namespace DEGREE { DigitalOut *globalGateOut; // global gate output DigitalOut gateOut; // gate output AnalogHandle adc; // CV input ADC - VoltPerOctave output; + VoltPerOctave output; bool led_state[16]; @@ -160,7 +160,6 @@ namespace DEGREE { int numActiveDegrees; // number of degrees which are active (to quantize voltage input) int numActiveOctaves; // number of active octaves for mapping CV to int activeDegreeLimit; // the max number of degrees allowed to be enabled at one time. - uint16_t prevCVInputValue; // the previously handled CV input value QuantDegree activeDegreeValues[8]; // array which holds noteIndex values and their associated DAC/1vo values QuantOctave activeOctaveValues[OCTAVE_COUNT]; diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 60e0c8d..b148d24 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -858,64 +858,64 @@ void TouchChannel::setActiveDegreeLimit(int value) void TouchChannel::handleCVInput() { // NOTE: CV voltage input is inverted, so everything needs to be flipped to make more sense - uint16_t currCVInputValue = CV_MAX - adc.read_u16(); + uint16_t cvInput = CV_MAX - adc.read_u16(); // We only want trigger events in quantizer mode, so if the gate gets set HIGH, make sure to set it back to low the very next tick if (gateState == HIGH) setGate(LOW); - if (currCVInputValue >= prevCVInputValue + CV_QUANTIZER_DEBOUNCE || currCVInputValue <= prevCVInputValue - CV_QUANTIZER_DEBOUNCE) - { - int refinedValue = 0; // we want a number between 0 and CV_OCTAVE for mapping to degrees. The octave is added afterwords via CV_OCTAVES - int octave = 0; + int refinedValue = 0; // we want a number between 0 and CV_OCTAVE for mapping to degrees. The octave is added afterwords via CV_OCTAVES + int octave = 0; - // determin which octave the CV value will get mapped to - for (int i = 0; i < numActiveOctaves; i++) + // determin which octave the CV value will get mapped to + for (int i = 0; i < numActiveOctaves; i++) + { + if (cvInput < activeOctaveValues[i].threshold) { - if (currCVInputValue < activeOctaveValues[i].threshold) - { - octave = activeOctaveValues[i].octave; - refinedValue = i == 0 ? currCVInputValue : currCVInputValue - activeOctaveValues[i - 1].threshold; // remap adc value to a number between 0 and octaveThreshold - break; - } + octave = activeOctaveValues[i].octave; + refinedValue = i == 0 ? cvInput : cvInput - activeOctaveValues[i - 1].threshold; // remap adc value to a number between 0 and octaveThreshold + + break; } + } - // latch incoming ADC value to DAC value - for (int i = 0; i < numActiveDegrees; i++) + // BEFORE THIS BLOCK: + // check if the newly mapped note and octave values are different then before + + // latch incoming ADC value to DAC value + for (int i = 0; i < numActiveDegrees; i++) + { + // if the calculated value is less than threshold + if (refinedValue < activeDegreeValues[i].threshold) { - // if the calculated value is less than threshold - if (refinedValue < activeDegreeValues[i].threshold) + // prevent duplicate triggering of that same degree / octave + if (currDegree != activeDegreeValues[i].noteIndex || currOctave != octave) // NOTE: currOctave used to be prevOctave 🤷‍♂️ { - // prevent duplicate triggering of that same degree / octave - if (currDegree != activeDegreeValues[i].noteIndex || currOctave != octave) // NOTE: currOctave used to be prevOctave 🤷‍♂️ - { - triggerNote(currDegree, prevOctave, NOTE_OFF); // set previous triggered degree + triggerNote(currDegree, prevOctave, NOTE_OFF); // set previous triggered degree - // re-DIM previously degree LED - if (bitwise_read_bit(activeDegrees, prevDegree)) - { - setDegreeLed(currDegree, BLINK_OFF, true); - setDegreeLed(currDegree, DIM_LOW, true); - } + // re-DIM previously degree LED + if (bitwise_read_bit(activeDegrees, prevDegree)) + { + setDegreeLed(currDegree, BLINK_OFF, true); + setDegreeLed(currDegree, DIM_LOW, true); + } - // trigger the new degree, and set its LED to blink - triggerNote(activeDegreeValues[i].noteIndex, octave, NOTE_ON); - setDegreeLed(activeDegreeValues[i].noteIndex, LedState::BLINK_ON, true); + // trigger the new degree, and set its LED to blink + triggerNote(activeDegreeValues[i].noteIndex, octave, NOTE_ON); + setDegreeLed(activeDegreeValues[i].noteIndex, LedState::BLINK_ON, true); - // re-DIM previous Octave LED - if (bitwise_read_bit(currActiveOctaves, prevOctave)) - { - setOctaveLed(prevOctave, LedState::BLINK_OFF, true); - setOctaveLed(prevOctave, LedState::DIM_LOW, true); - } - // BLINK active quantized octave - setOctaveLed(octave, LedState::BLINK_ON, true); + // re-DIM previous Octave LED + if (bitwise_read_bit(currActiveOctaves, prevOctave)) + { + setOctaveLed(prevOctave, LedState::BLINK_OFF, true); + setOctaveLed(prevOctave, LedState::DIM_LOW, true); } - break; // break from loop as soon as we can + // BLINK active quantized octave + setOctaveLed(octave, LedState::BLINK_ON, true); } + break; // break from loop as soon as we can } } - prevCVInputValue = currCVInputValue; } /** From b44aa3f943116106224848a3c7ed9867b1edbc20 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Thu, 1 Dec 2022 08:58:25 -0500 Subject: [PATCH 11/44] #187 removed unused adc queue which was causing system crashes --- Degree/Inc/AnalogHandle.h | 1 - Degree/Src/AnalogHandle.cpp | 2 -- Degree/Tasks/Src/task_calibration.cpp | 2 -- 3 files changed, 5 deletions(-) diff --git a/Degree/Inc/AnalogHandle.h b/Degree/Inc/AnalogHandle.h index 7912d76..4cc1afc 100644 --- a/Degree/Inc/AnalogHandle.h +++ b/Degree/Inc/AnalogHandle.h @@ -20,7 +20,6 @@ class AnalogHandle { int index; - okQueue queue; okSemaphore denoisingSemaphore; okSemaphore sampleSemaphore; uint16_t idleNoiseThreshold; // how much noise an idle input signal contains diff --git a/Degree/Src/AnalogHandle.cpp b/Degree/Src/AnalogHandle.cpp index 8554237..f230a9d 100644 --- a/Degree/Src/AnalogHandle.cpp +++ b/Degree/Src/AnalogHandle.cpp @@ -127,8 +127,6 @@ void AnalogHandle::sampleReadyCallback(uint16_t sample) } } - - this->queue.send(currValue, (TickType_t)0); // send sample to queue for other tasks } diff --git a/Degree/Tasks/Src/task_calibration.cpp b/Degree/Tasks/Src/task_calibration.cpp index ac5f907..fffa8bc 100644 --- a/Degree/Tasks/Src/task_calibration.cpp +++ b/Degree/Tasks/Src/task_calibration.cpp @@ -22,7 +22,6 @@ void taskObtainSignalFrequency(void *params) // thStartCalibration = xTaskGetCurrentTaskHandle(); - uint16_t sample; float vcoFrequency = 0; // the calculated frequency sample int frequencySampleCounter = 0; // index for storing new frequency sample into freqSamples array float avgFrequencySum = 0; // the sum of all new frequency samples (for calculating a running average afterwards) @@ -47,7 +46,6 @@ void taskObtainSignalFrequency(void *params) while (1) { xSemaphoreTake(sem_obtain_freq, portMAX_DELAY); - xQueueReceive(channel->adc.queue.handle, &sample, portMAX_DELAY); uint16_t curr_adc_sample = channel->adc.read_u16(); // obtain the sample from DMA buffer From 8147769c1b10ca0d7cc950d53ff2027b4680ccb8 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sat, 3 Dec 2022 16:03:50 -0500 Subject: [PATCH 12/44] #189 sequence octave touch pads (mono only) --- Degree/Inc/SuperSeq.h | 21 +++--- Degree/Inc/TouchChannel.h | 1 - Degree/Src/SuperSeq.cpp | 98 +++++++++++++--------------- Degree/Src/TouchChannel.cpp | 123 ++++++++++++++++++++---------------- Degree/Src/main.cpp | 3 - 5 files changed, 128 insertions(+), 118 deletions(-) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index 9e40b91..e9b5bba 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -9,6 +9,7 @@ #define SEQ_EVENT_STATUS_BIT 5 #define SEQ_EVENT_GATE_BIT 4 #define SEQ_EVENT_INDEX_BIT_MASK 0b00001111 +#define SEQ_EVENT_OCTAVE_BIT_MASK 0b11000000 #define SEQ_LENGTH_BLOCK_1 (MAX_SEQ_LENGTH / 4) // 1 bar #define SEQ_LENGTH_BLOCK_2 (MAX_SEQ_LENGTH / 2) // 2 bars @@ -18,11 +19,12 @@ typedef struct SequenceNode { uint8_t activeDegrees; // byte for holding active/inactive notes for a chord - uint8_t data; // bits 0..3: Degree Index || bit 4: Gate || bit 5: status + uint8_t data; // bits 0..3: Degree Index || bit 4: Gate || bit 5: status || bits 6,7: octave uint16_t bend; // raw ADC value from pitch bend bool getStatus() { return bitwise_read_bit(data, SEQ_EVENT_STATUS_BIT); } uint8_t getDegree() { return data & SEQ_EVENT_INDEX_BIT_MASK; } bool getGate() { return bitwise_read_bit(data, SEQ_EVENT_GATE_BIT); } + uint8_t getOctave() { return (data & SEQ_EVENT_OCTAVE_BIT_MASK) >> 6; } } SequenceNode; class SuperSeq { @@ -68,7 +70,7 @@ class SuperSeq { void copyPaste(int prevPosition, int newPosition); void cutPaste(int prevPosition, int newPosition); - void createTouchEvent(int position, int degree, bool gate); + void createTouchEvent(int position, uint8_t degree, uint8_t octave, bool gate); void createBendEvent(int position, uint16_t bend); void createChordEvent(int position, uint8_t notes); @@ -85,13 +87,19 @@ class SuperSeq { void enableRecording(); void disableRecording(); + void enablePlayback(); + void disablePlayback(); + + void enableOverdub(); + void disableOverdub(); + void quantize(); void setQuantizeAmount(QUANT value); - uint8_t constructEventData(uint8_t degree, bool gate, bool status); - void setEventData(int position, uint8_t degree, bool gate, bool status); + void setEventData(int position, uint8_t degree, uint8_t octave, bool gate, bool status); uint8_t getEventDegree(int position); + uint8_t getEventOctave(int position); uint8_t getActiveDegrees(int position); bool getEventGate(int position); bool getEventStatus(int position); @@ -101,13 +109,10 @@ class SuperSeq { void setEventStatus(int position, bool status); uint8_t setIndexBits(uint8_t degree, uint8_t byte); - uint8_t readDegreeBits(uint8_t byte); uint8_t setGateBits(bool state, uint8_t byte); - uint8_t readGateBits(uint8_t byte); uint8_t setStatusBits(bool status, uint8_t byte); - uint8_t readStatusBits(uint8_t byte); + uint8_t setOctaveBits(uint8_t octave, uint8_t byte); - void quantizationTest(); void logSequenceToConsole(); private: diff --git a/Degree/Inc/TouchChannel.h b/Degree/Inc/TouchChannel.h index 893fee3..aa60178 100644 --- a/Degree/Inc/TouchChannel.h +++ b/Degree/Inc/TouchChannel.h @@ -185,7 +185,6 @@ namespace DEGREE { // UI Handler void updateUI(UIMode mode); - void setOctave(int octave); void updateOctaveLeds(int octave, bool isPlaybackEvent); void setLED(int io_pin, LedState state, bool isPlaybackEvent); // if you use a "scene" here, you could remove the boolean diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index 5847538..1db33b2 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -121,6 +121,26 @@ void SuperSeq::disableRecording() { } } +void SuperSeq::enablePlayback() +{ + playbackEnabled = true; +} + +void SuperSeq::disablePlayback() +{ + playbackEnabled = false; +} + +void SuperSeq::enableOverdub() +{ + overdub = true; +} + +void SuperSeq::disableOverdub() +{ + overdub = false; +} + /** * @brief reset the sequence */ @@ -224,7 +244,7 @@ void SuperSeq::cutPaste(int prevPosition, int newPosition) /** * @brief Create new event at position */ -void SuperSeq::createTouchEvent(int position, int degree, bool gate) +void SuperSeq::createTouchEvent(int position, uint8_t degree, uint8_t octave, bool gate) { if (!containsTouchEvents) containsTouchEvents = true; @@ -236,12 +256,12 @@ void SuperSeq::createTouchEvent(int position, int degree, bool gate) { // move that events associated gate LOW event one pulse before new events position // there is a potential bug if by chance the prev position returns an index associated with an active HIGH event - setEventData(this->getPrevPosition(position), events[prevEventPos].getDegree(), false, true); + setEventData(this->getPrevPosition(position), events[prevEventPos].getDegree(), events[prevEventPos].getOctave(), false, true); } } newEventPos = position; - setEventData(newEventPos, degree, gate, true); + setEventData(newEventPos, degree, octave, gate, true); }; /** @@ -371,27 +391,6 @@ void SuperSeq::quantize() // logger_log("\n"); } -void SuperSeq::quantizationTest() -{ - setLength(8); // set sequence length to two steps, ie. 96 * 2 PPQN - setQuantizeAmount(QUANT::EIGTH); - - createTouchEvent(370, 7, true); - createTouchEvent(387, 7, false); - createTouchEvent(388, 6, true); - createTouchEvent(389, 6, false); - createTouchEvent(400, 6, false); - createTouchEvent(401, 5, true); - createTouchEvent(413, 4, true); - createTouchEvent(426, 3, true); - createTouchEvent(439, 2, true); - createTouchEvent(451, 2, false); - createTouchEvent(452, 1, true); - createTouchEvent(466, 0, true); - - quantize(); -} - void SuperSeq::logSequenceToConsole() { logger_log("\nlengthPPQN: "); logger_log(lengthPPQN); @@ -414,23 +413,14 @@ void SuperSeq::logSequenceToConsole() { } /** - * @brief contruct an 8-bit value which holds the degree index, gate state, and status of a sequence event. + * @brief contruct an 8-bit value which holds the degree index, octave, gate state, and status of a sequence event. * - * @param degree the degree index + * @param degree the degree index (0..7) + * @param octave the octave (0..3) * @param gate the state of the gate output (high or low) * @param status the status of the event - * @return uint8_t */ -uint8_t SuperSeq::constructEventData(uint8_t degree, bool gate, bool status) -{ - uint8_t data = 0x00; - data = setIndexBits(degree, data); - data = setGateBits(gate, data); - data = setStatusBits(status, data); - return data; -} - -void SuperSeq::setEventData(int position, uint8_t degree, bool gate, bool status) +void SuperSeq::setEventData(int position, uint8_t degree, uint8_t octave, bool gate, bool status) { if (gate == LOW) // avoid overwriting any active HIGH event with a active LOW event { @@ -439,12 +429,22 @@ void SuperSeq::setEventData(int position, uint8_t degree, bool gate, bool status return; } } - events[position].data = constructEventData(degree, gate, status); + uint8_t data = 0b00000000; + data = setIndexBits(degree, data); + data = setGateBits(gate, data); + data = setStatusBits(status, data); + data = setOctaveBits(octave, data); + events[position].data = data; } uint8_t SuperSeq::getEventDegree(int position) { - return readDegreeBits(events[position].data); + return events[position].getDegree(); +} + +uint8_t SuperSeq::getEventOctave(int position) +{ + return events[position].getOctave(); } uint8_t SuperSeq::getActiveDegrees(int position) @@ -454,12 +454,12 @@ uint8_t SuperSeq::getActiveDegrees(int position) bool SuperSeq::getEventGate(int position) { - return readGateBits(events[position].data); + return events[position].getGate(); } bool SuperSeq::getEventStatus(int position) { - return readStatusBits(events[position].data); + return events[position].getStatus(); } void SuperSeq::setEventStatus(int position, bool status) @@ -486,27 +486,19 @@ uint8_t SuperSeq::setIndexBits(uint8_t degree, uint8_t byte) return byte | degree; } -uint8_t SuperSeq::readDegreeBits(uint8_t byte) -{ - return byte & SEQ_EVENT_INDEX_BIT_MASK; -} - uint8_t SuperSeq::setGateBits(bool state, uint8_t byte) { return state ? bitwise_set_bit(byte, SEQ_EVENT_GATE_BIT) : bitwise_clear_bit(byte, SEQ_EVENT_GATE_BIT); } -uint8_t SuperSeq::readGateBits(uint8_t byte) -{ - return bitwise_read_bit(byte, SEQ_EVENT_GATE_BIT); -} - uint8_t SuperSeq::setStatusBits(bool status, uint8_t byte) { return status ? bitwise_set_bit(byte, SEQ_EVENT_STATUS_BIT) : bitwise_clear_bit(byte, SEQ_EVENT_STATUS_BIT); } -uint8_t SuperSeq::readStatusBits(uint8_t byte) +uint8_t SuperSeq::setOctaveBits(uint8_t octave, uint8_t byte) { - return bitwise_read_bit(byte, SEQ_EVENT_STATUS_BIT); + octave = octave << 6; // shift octave value from bits 0 and 1 to bits 7 and 8 + byte = byte & 0b00111111; // clear bits 7 and 8 + return byte | octave; // set bits 7 and 8: } \ No newline at end of file diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index b148d24..99b2c42 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -297,12 +297,12 @@ void TouchChannel::handleTouchPlaybackEvent(uint8_t pad) // create a new event if (sequence.recordEnabled) { - sequence.overdub = true; - sequence.createTouchEvent(sequence.currPosition, pad, HIGH); + sequence.enableOverdub(); + sequence.createTouchEvent(sequence.currPosition, pad, currOctave, HIGH); } // when record is disabled, this block will freeze the sequence and output the curr touched degree until touch is released else { - sequence.playbackEnabled = false; + sequence.disablePlayback(); } triggerNote(pad, currOctave, NOTE_ON); break; @@ -323,7 +323,32 @@ void TouchChannel::handleTouchPlaybackEvent(uint8_t pad) else // handle octave pads { pad = CHAN_TOUCH_PADS[pad]; // remap - setOctave(pad); + switch (playbackMode) + { + case MONO: + triggerNote(currDegree, pad, NOTE_ON); + break; + case MONO_LOOP: + if (sequence.recordEnabled) + { + sequence.enableOverdub(); + sequence.createTouchEvent(sequence.currPosition, currDegree, pad, HIGH); + } + else + { + sequence.disablePlayback(); + } + triggerNote(currDegree, pad, NOTE_ON); + break; + case QUANTIZER: + setActiveOctaves(pad); + setActiveDegrees(activeDegrees); // update active degrees thresholds + break; + case QUANTIZER_LOOP: + setActiveOctaves(pad); + setActiveDegrees(activeDegrees); // update active degrees thresholds + break; + } } } @@ -340,12 +365,12 @@ void TouchChannel::handleReleasePlaybackEvent(uint8_t pad) case MONO_LOOP: if (sequence.recordEnabled) { - sequence.createTouchEvent(sequence.currPosition, pad, LOW); - sequence.overdub = false; + sequence.createTouchEvent(sequence.currPosition, pad, currOctave, LOW); + sequence.disableOverdub(); } else { - sequence.playbackEnabled = true; + sequence.enablePlayback(); } triggerNote(pad, currOctave, NOTE_OFF); break; @@ -359,7 +384,32 @@ void TouchChannel::handleReleasePlaybackEvent(uint8_t pad) } else { - setGate(LOW); + pad = CHAN_TOUCH_PADS[pad]; + switch (playbackMode) + { + case MONO: + triggerNote(currDegree, pad, NOTE_OFF); + break; + case MONO_LOOP: + + if (sequence.recordEnabled) + { + sequence.createTouchEvent(sequence.currPosition, currDegree, pad, LOW); + sequence.disableOverdub(); // important this goes second + } + else + { + sequence.enablePlayback(); + } + triggerNote(currDegree, pad, NOTE_OFF); + break; + case QUANTIZER: + break; + case QUANTIZER_LOOP: + break; + default: + break; + } } } @@ -378,13 +428,19 @@ void TouchChannel::triggerNote(int degree, int octave, Action action) switch (action) { case NOTE_ON: - if (playbackMode == MONO || playbackMode == MONO_LOOP) { - setDegreeLed(prevDegree, OFF, true); // set the 'previous' active note led LOW - setDegreeLed(degree, ON, true); // new active note HIGH - } setGate(HIGH); currDegree = degree; currOctave = octave; + if (playbackMode == MONO || playbackMode == MONO_LOOP) + { + setDegreeLed(prevDegree, OFF, true); // set the 'previous' active note led LOW + setDegreeLed(currDegree, ON, true); // new active note HIGH + if (currOctave != prevOctave) + { + setOctaveLed(prevOctave, OFF, true); + setOctaveLed(currOctave, ON, true); + } + } output.updateDAC(dacIndex, 0); break; case NOTE_OFF: @@ -529,37 +585,6 @@ void TouchChannel::updateOctaveLeds(int octave, bool isPlaybackEvent) } } -/** - * take a number between 0 - 3 and apply to currOctave -**/ -void TouchChannel::setOctave(int octave) -{ - prevOctave = currOctave; - currOctave = octave; - - switch (playbackMode) - { - case MONO: - updateOctaveLeds(currOctave, true); - triggerNote(currDegree, currOctave, NOTE_ON); - break; - case MONO_LOOP: - updateOctaveLeds(currOctave, true); - triggerNote(currDegree, currOctave, SUSTAIN); - break; - case QUANTIZER: - setActiveOctaves(octave); - setActiveDegrees(activeDegrees); // update active degrees thresholds - break; - case QUANTIZER_LOOP: - setActiveOctaves(octave); - setActiveDegrees(activeDegrees); // update active degrees thresholds - break; - } - - prevOctave = currOctave; -} - /** * sets the +5v gate output via GPIO */ @@ -1040,16 +1065,8 @@ void TouchChannel::handleSequence(int position) } else // Handle Sequence Events { - if (sequence.getEventGate(position) == HIGH) - { - sequence.prevEventPos = position; // store position into variable - triggerNote(sequence.getEventDegree(position), currOctave, NOTE_ON); // trigger note ON - } - else // set event.gate LOW - { - sequence.prevEventPos = position; // store position into variable - triggerNote(sequence.getEventDegree(position), currOctave, NOTE_OFF); // trigger note OFF - } + sequence.prevEventPos = position; // store position into variable + triggerNote(sequence.getEventDegree(position), sequence.getEventOctave(position), sequence.getEventGate(position) ? NOTE_ON : NOTE_OFF); } } break; diff --git a/Degree/Src/main.cpp b/Degree/Src/main.cpp index 9786b13..7dbbdf5 100644 --- a/Degree/Src/main.cpp +++ b/Degree/Src/main.cpp @@ -115,9 +115,6 @@ int main(void) vTaskStartScheduler(); - // code below this comment will not execute. For testing. - chanA.sequence.quantizationTest(); - while (1) { From 5b71ea17f1d148b1bf3c8368f879c9e30cff3580 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 5 Dec 2022 10:22:32 -0500 Subject: [PATCH 13/44] quantizer mode octave recording --- Degree/Inc/SuperSeq.h | 5 ++++- Degree/Inc/TouchChannel.h | 5 ++--- Degree/Src/SuperSeq.cpp | 12 +++++++++++- Degree/Src/TouchChannel.cpp | 30 ++++++++++++++++++++++-------- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index e9b5bba..1885e30 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -25,6 +25,7 @@ typedef struct SequenceNode uint8_t getDegree() { return data & SEQ_EVENT_INDEX_BIT_MASK; } bool getGate() { return bitwise_read_bit(data, SEQ_EVENT_GATE_BIT); } uint8_t getOctave() { return (data & SEQ_EVENT_OCTAVE_BIT_MASK) >> 6; } + uint8_t getActiveOctaves() { return data; } } SequenceNode; class SuperSeq { @@ -72,7 +73,7 @@ class SuperSeq { void createTouchEvent(int position, uint8_t degree, uint8_t octave, bool gate); void createBendEvent(int position, uint16_t bend); - void createChordEvent(int position, uint8_t notes); + void createChordEvent(int position, uint8_t degrees, uint8_t octaves); void setLength(int steps); int getLength(); @@ -101,6 +102,7 @@ class SuperSeq { uint8_t getEventDegree(int position); uint8_t getEventOctave(int position); uint8_t getActiveDegrees(int position); + uint8_t getActiveOctaves(int position); bool getEventGate(int position); bool getEventStatus(int position); bool eventsAreAssociated(int pos1, int pos2); @@ -112,6 +114,7 @@ class SuperSeq { uint8_t setGateBits(bool state, uint8_t byte); uint8_t setStatusBits(bool status, uint8_t byte); uint8_t setOctaveBits(uint8_t octave, uint8_t byte); + uint8_t setActiveOctaveBits(uint8_t octaves); void logSequenceToConsole(); diff --git a/Degree/Inc/TouchChannel.h b/Degree/Inc/TouchChannel.h index aa60178..a2067e3 100644 --- a/Degree/Inc/TouchChannel.h +++ b/Degree/Inc/TouchChannel.h @@ -115,7 +115,7 @@ namespace DEGREE { currOctave = 0; activeDegrees = 0xFF; - currActiveOctaves = 0xF; + activeOctaves = 0xF; numActiveDegrees = DEGREE_COUNT; numActiveOctaves = OCTAVE_COUNT; }; @@ -155,8 +155,7 @@ namespace DEGREE { // Quantiser members uint8_t activeDegrees; // 8 bits to determine which scale degrees are presently active/inactive (active = 1, inactive= 0) - uint8_t currActiveOctaves; // 4-bits to represent the current octaves external CV will get mapped to (active = 1, inactive= 0) - uint8_t prevActiveOctaves; // 4-bits to represent the previous octaves external CV will get mapped to (active = 1, inactive= 0) + uint8_t activeOctaves; // 4-bits to represent the current octaves external CV will get mapped to (active = 1, inactive= 0) int numActiveDegrees; // number of degrees which are active (to quantize voltage input) int numActiveOctaves; // number of active octaves for mapping CV to int activeDegreeLimit; // the max number of degrees allowed to be enabled at one time. diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index 1db33b2..96b1d10 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -278,12 +278,13 @@ void SuperSeq::createBendEvent(int position, uint16_t bend) events[position].bend = bend; } -void SuperSeq::createChordEvent(int position, uint8_t degrees) +void SuperSeq::createChordEvent(int position, uint8_t degrees, uint8_t octaves) { if (!containsTouchEvents) containsTouchEvents = true; events[position].activeDegrees = degrees; + events[position].data = octaves; setEventStatus(position, true); }; @@ -452,6 +453,11 @@ uint8_t SuperSeq::getActiveDegrees(int position) return events[position].activeDegrees; } +uint8_t SuperSeq::getActiveOctaves(int position) +{ + return events[position].getActiveOctaves(); +} + bool SuperSeq::getEventGate(int position) { return events[position].getGate(); @@ -501,4 +507,8 @@ uint8_t SuperSeq::setOctaveBits(uint8_t octave, uint8_t byte) octave = octave << 6; // shift octave value from bits 0 and 1 to bits 7 and 8 byte = byte & 0b00111111; // clear bits 7 and 8 return byte | octave; // set bits 7 and 8: +} + +uint8_t SuperSeq::setActiveOctaveBits(uint8_t octaves) { + return octaves; } \ No newline at end of file diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 99b2c42..5e055ec 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -313,7 +313,10 @@ void TouchChannel::handleTouchPlaybackEvent(uint8_t pad) // every touch detected, take a snapshot of all active degree values and apply them to the sequence setActiveDegrees(bitwise_write_bit(activeDegrees, pad, !bitwise_read_bit(activeDegrees, pad))); if (sequence.recordEnabled) { - sequence.createChordEvent(sequence.currPosition, activeDegrees); + sequence.enableOverdub(); + sequence.createChordEvent(sequence.currPosition, activeDegrees, activeOctaves); + } else { + sequence.disablePlayback(); } break; default: @@ -347,6 +350,13 @@ void TouchChannel::handleTouchPlaybackEvent(uint8_t pad) case QUANTIZER_LOOP: setActiveOctaves(pad); setActiveDegrees(activeDegrees); // update active degrees thresholds + if (sequence.recordEnabled) + { + sequence.enableOverdub(); + sequence.createChordEvent(sequence.currPosition, activeDegrees, activeOctaves); + } else { + sequence.disablePlayback(); + } break; } } @@ -377,6 +387,8 @@ void TouchChannel::handleReleasePlaybackEvent(uint8_t pad) case QUANTIZER: break; case QUANTIZER_LOOP: + sequence.disableOverdub(); + sequence.enablePlayback(); break; default: break; @@ -406,6 +418,8 @@ void TouchChannel::handleReleasePlaybackEvent(uint8_t pad) case QUANTIZER: break; case QUANTIZER_LOOP: + sequence.disableOverdub(); + sequence.enablePlayback(); break; default: break; @@ -870,7 +884,7 @@ void TouchChannel::benderTriStateCallback(Bender::BendState state) void TouchChannel::initQuantizer() { activeDegrees = 0xFF; - currActiveOctaves = 0xF; + activeOctaves = 0xF; numActiveDegrees = DEGREE_COUNT; numActiveOctaves = OCTAVE_COUNT; } @@ -930,7 +944,7 @@ void TouchChannel::handleCVInput() setDegreeLed(activeDegreeValues[i].noteIndex, LedState::BLINK_ON, true); // re-DIM previous Octave LED - if (bitwise_read_bit(currActiveOctaves, prevOctave)) + if (bitwise_read_bit(activeOctaves, prevOctave)) { setOctaveLed(prevOctave, LedState::BLINK_OFF, true); setOctaveLed(prevOctave, LedState::DIM_LOW, true); @@ -976,7 +990,7 @@ void TouchChannel::setActiveDegrees(uint8_t degrees) numActiveOctaves = 0; for (int i = 0; i < OCTAVE_COUNT; i++) { - if (bitwise_read_bit(currActiveOctaves, i)) + if (bitwise_read_bit(activeOctaves, i)) { activeOctaveValues[numActiveOctaves].octave = i; numActiveOctaves += 1; @@ -988,7 +1002,6 @@ void TouchChannel::setActiveDegrees(uint8_t degrees) setOctaveLed(i, OFF, false); } } - prevActiveOctaves = currActiveOctaves; int octaveThreshold = CV_MAX / numActiveOctaves; // divide max ADC value by num octaves int min_threshold = octaveThreshold / numActiveDegrees; // then numActiveDegrees @@ -1007,13 +1020,13 @@ void TouchChannel::setActiveDegrees(uint8_t degrees) } /** - * @brief take the newly touched octave, and either add it or remove it from the currActiveOctaves list + * @brief take the newly touched octave, and either add it or remove it from the activeOctaves list */ void TouchChannel::setActiveOctaves(int octave) { - if (bitwise_flip_bit(currActiveOctaves, octave) != 0) // one octave must always remain active. + if (bitwise_flip_bit(activeOctaves, octave) != 0) // one octave must always remain active. { - currActiveOctaves = bitwise_flip_bit(currActiveOctaves, octave); + activeOctaves = bitwise_flip_bit(activeOctaves, octave); } } @@ -1079,6 +1092,7 @@ void TouchChannel::handleSequence(int position) } else { + activeOctaves = sequence.getActiveOctaves(position); setActiveDegrees(sequence.getActiveDegrees(position)); } } From 38ed5da8a2252ed54943dc1abc94c71756e9940c Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 5 Dec 2022 13:11:51 -0500 Subject: [PATCH 14/44] readme update --- README.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b0b39ea..c8b9c52 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,3 @@ -# TODO: -- How do I print to the console through an ST-Link? - - RX/TX connector from St-Link need to be wired to the TX/RX pins of the MCU - - you probably need StMicroelectronics STLink Virtual COM Port driver installed somewhere (dev/tty folder or file) - -- why is my intellisense not detecting errors prior to building? - google "gcc arm problem matcher vscode" - ## FreeRTOS Configuration Could all this not be done just using a timer? Or is that what an RTOS essential is? ``` @@ -164,4 +156,7 @@ void __attribute__((optimize("O0"))) myFunc() { // this will not be optimized } -``` \ No newline at end of file +``` + +# DEV TOOLS: +- [Bitwise Operations Playground](https://bitwisecmd.com/) \ No newline at end of file From 951a6aa39f06a7aab52db798017dc8bb7a22e131 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 5 Dec 2022 13:27:16 -0500 Subject: [PATCH 15/44] Clear sequence when toggling modes. Closes #192 --- Degree/Src/GlobalControl.cpp | 2 +- Degree/Src/TouchChannel.cpp | 14 +++++++++++--- Degree/Tasks/Inc/task_sequence_handler.h | 1 + Degree/Tasks/Src/task_sequence_handler.cpp | 4 ++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index e27508d..9784bb7 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -282,7 +282,7 @@ void GlobalControl::handleButtonPress(int pad) { if (touchPads->padIsTouched(i, currTouched)) { - channels[i]->toggleMode(); + dispatch_sequencer_event(CHAN(i), SEQ::TOGGLE_MODE, 0); } } break; diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 5e055ec..644f541 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -164,6 +164,7 @@ void TouchChannel::setPlaybackMode(PlaybackMode targetMode) setLED(CHANNEL_REC_LED, OFF, false); setLED(CHANNEL_QUANT_LED, OFF, false); sequence.playbackEnabled = false; + display->clear(channelIndex); switch (playbackMode) { @@ -197,11 +198,18 @@ void TouchChannel::setPlaybackMode(PlaybackMode targetMode) /** * TOGGLE MODE - * - * still needs to be written to handle 3-stage toggle switch. -**/ + * @note if bend events were recorded, but no touch events, then we actually want to + * remain in sequencer playback, but toggle to the other loop mode + **/ void TouchChannel::toggleMode() { + // when exiting either of these modes, delete touch sequence + if (playbackMode == MONO_LOOP || playbackMode == QUANTIZER_LOOP) { + if (sequence.containsTouchEvents) { + sequence.clearAllTouchEvents(); + } + } + if (playbackMode == MONO || playbackMode == MONO_LOOP) { if (sequence.containsBendEvents) { diff --git a/Degree/Tasks/Inc/task_sequence_handler.h b/Degree/Tasks/Inc/task_sequence_handler.h index 2920ff6..2a8f465 100644 --- a/Degree/Tasks/Inc/task_sequence_handler.h +++ b/Degree/Tasks/Inc/task_sequence_handler.h @@ -15,6 +15,7 @@ enum class SEQ CLEAR_BEND, RECORD_ENABLE, RECORD_DISABLE, + TOGGLE_MODE, SET_LENGTH, QUANTIZE, CORRECT, diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index f918d8a..3d839bd 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -102,6 +102,10 @@ void task_sequence_handler(void *params) ctrl->channels[i]->disableSequenceRecording(); break; + case SEQ::TOGGLE_MODE: + ctrl->channels[channel]->toggleMode(); + break; + case SEQ::SET_LENGTH: ctrl->channels[channel]->updateSequenceLength(data); break; From 9ed3a1ea8ccd094fd6c15a63d340ebfb785281b4 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 5 Dec 2022 15:24:35 -0500 Subject: [PATCH 16/44] closes #193 , changed default dac note index from 0 to 12 to allow for downwards pitch bend --- Degree/Inc/VoltPerOctave.h | 7 ++++--- Degree/Src/TouchChannel.cpp | 7 ++----- Degree/Src/VoltPerOctave.cpp | 22 +++++++++++++++------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/Degree/Inc/VoltPerOctave.h b/Degree/Inc/VoltPerOctave.h index f02e46a..0c7cd2d 100644 --- a/Degree/Inc/VoltPerOctave.h +++ b/Degree/Inc/VoltPerOctave.h @@ -40,6 +40,7 @@ namespace DEGREE { uint16_t maxPitchBend; // must be a float! uint16_t minPitchBend = 0; // should always be 0 uint16_t currPitchBend; // the amount of pitch bend to apply to the 1v/o DAC output. Can be positive/negative centered @ 0 + bool bendDirection; // pitch bend up = true, down = false VoltPerOctave(DAC8554 *_dac, DAC8554::Channel _chan, AnalogHandle *_adc) { @@ -50,12 +51,12 @@ namespace DEGREE { }; void init(); - void updateDAC(int index, uint16_t pitchBend, bool direction = false); + void setPitch(int index); + void setPitchBend(uint16_t value, bool direction = false); + void updateDAC(); void resetDAC(); void setPitchBendRange(int value); int getPitchBendRange(); - void setPitchBend(uint16_t value, bool direction = false); - void bend(uint16_t value); uint16_t calculatePitchBend(uint16_t input, uint16_t min, uint16_t max); void resetVoltageMap(); void logVoltageMap(); diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 644f541..7e88a7c 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -463,7 +463,7 @@ void TouchChannel::triggerNote(int degree, int octave, Action action) setOctaveLed(currOctave, ON, true); } } - output.updateDAC(dacIndex, 0); + output.setPitch(dacIndex); break; case NOTE_OFF: setGate(LOW); @@ -473,7 +473,7 @@ void TouchChannel::triggerNote(int degree, int octave, Action action) { setDegreeLed(degree, ON, true); // new active note HIGH } - output.updateDAC(dacIndex, 0); + output.setPitch(dacIndex); break; case PREV_NOTE: /* code */ @@ -1111,9 +1111,6 @@ void TouchChannel::handleSequence(int position) // Handle Bend Events (only if the bender is currently idle / inactive) if (sequence.containsBendEvents) { - // somewhere in here, if bend events exists and ratcheting is enabled, then - // an idle bend value is going to set the gate LOW (each PPQN). What needs to happen, - // is if bend idle && sequence bend is == idle, then don't trigger the gate if (bender->isIdle()) { this->handleBend(sequence.getBend(position)); diff --git a/Degree/Src/VoltPerOctave.cpp b/Degree/Src/VoltPerOctave.cpp index f961f79..fbfa2fb 100644 --- a/Degree/Src/VoltPerOctave.cpp +++ b/Degree/Src/VoltPerOctave.cpp @@ -29,7 +29,16 @@ int VoltPerOctave::getPitchBendRange() { void VoltPerOctave::setPitchBend(uint16_t value, bool direction) { currPitchBend = value; - this->updateDAC(currNoteIndex, currPitchBend, direction); + bendDirection = direction; + this->updateDAC(); +} + +void VoltPerOctave::setPitch(int index) +{ + if (index < DAC_1VO_ARR_SIZE) { + currNoteIndex = index + 12; + this->updateDAC(); + } } /** @@ -47,20 +56,19 @@ uint16_t VoltPerOctave::calculatePitchBend(uint16_t input, uint16_t min, uint16_ * @param index index to be mapped to voltage map. ranging 0..DAC_1VO_ARR_SIZE * @param pitchBend DAC value corrosponing to the amount of pitch bend to apply to output. positive or negative */ -void VoltPerOctave::updateDAC(int index, uint16_t pitchBend, bool direction) +void VoltPerOctave::updateDAC() { - if (index < DAC_1VO_ARR_SIZE) + if (currNoteIndex < DAC_1VO_ARR_SIZE) { - currNoteIndex = index; - if (!direction) + if (!bendDirection) { - uint16_t nValue = dacVoltageMap[currNoteIndex] - pitchBend; + uint16_t nValue = dacVoltageMap[currNoteIndex] - currPitchBend; if (nValue <= dacVoltageMap[currNoteIndex]) { currOutput = nValue; } } else { - uint16_t pValue = dacVoltageMap[currNoteIndex] + pitchBend; + uint16_t pValue = dacVoltageMap[currNoteIndex] + currPitchBend; if (pValue >= dacVoltageMap[currNoteIndex]) { currOutput = pValue; From 44dc17508ce5b2544d50d9edc74aede6e94ed646 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 5 Dec 2022 19:56:18 -0500 Subject: [PATCH 17/44] Record overflow now draws sequence to display --- Degree/Inc/SuperSeq.h | 6 ++++ Degree/Inc/TouchChannel.h | 1 + Degree/Src/SuperSeq.cpp | 6 +++- Degree/Src/TouchChannel.cpp | 34 +++++++++++++++------- Degree/Tasks/Inc/task_sequence_handler.h | 1 + Degree/Tasks/Src/task_sequence_handler.cpp | 10 +++++++ 6 files changed, 47 insertions(+), 11 deletions(-) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index 35b6f17..b688333 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -2,6 +2,7 @@ #include "main.h" #include "Bender.h" +#include "Callback.h" #include "ArrayMethods.h" #include "Quantization.h" @@ -37,6 +38,7 @@ class SuperSeq { Bender *bender; // you need the instance of a bender for determing its idle value when clearing / initializing bender events QUANT quantizeAmount; + Callback recordOverflowCallback; int length; // how many steps the sequence contains int lengthPPQN; // how many PPQN the sequence contains @@ -123,6 +125,10 @@ class SuperSeq { void logSequenceToConsole(); + void attachRecordOverflowCallback(Callback func) { + recordOverflowCallback = func; + } + private: SequenceNode events[MAX_SEQ_LENGTH_PPQN]; }; \ No newline at end of file diff --git a/Degree/Inc/TouchChannel.h b/Degree/Inc/TouchChannel.h index 7152980..2424f02 100644 --- a/Degree/Inc/TouchChannel.h +++ b/Degree/Inc/TouchChannel.h @@ -204,6 +204,7 @@ namespace DEGREE { // Sequencer methods void handleSequence(int position); + void handleRecordOverflow(); void resetSequence(); void updateSequenceLength(uint8_t steps); void setSequenceLED(uint8_t step, uint8_t pwm, bool blink); diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index d0677cc..44a8ffc 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -60,12 +60,16 @@ void SuperSeq::advance() { if (recordEnabled && adaptiveLength) { - if (currStep >= MAX_SEQ_LENGTH) // disable adaptive length, set seq length to max + // disable adaptive length, set seq length to max, and enable overdub + if (currStep >= MAX_SEQ_LENGTH) { adaptiveLength = false; this->setLength(MAX_SEQ_LENGTH); currPosition = 0; currStep = 0; + overwriteExistingEvents = true; // enable overdub + if (recordOverflowCallback) + recordOverflowCallback(); } else { // do nothing, let things keep counting upwards } diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 4ab5a39..3103497 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -34,6 +34,7 @@ void TouchChannel::init() bender->attachTriStateCallback(callback(this, &TouchChannel::benderTriStateCallback)); sequence.init(); // really important sequencer initializes after the bender gets initialized + sequence.attachRecordOverflowCallback(callback(this, &TouchChannel::handleRecordOverflow)); // initialize channel touch pads touchPads->init(); @@ -1114,6 +1115,11 @@ void TouchChannel::handleSequence(int position) } } +void TouchChannel::handleRecordOverflow() +{ + dispatch_sequencer_event(CHAN(channelIndex), SEQ::RECORD_OVERFLOW, 0); +} + /** * @brief reset the sequence * @todo you should probably get the currently queued event, see if it has been triggered yet, and disable it if it has been triggered @@ -1159,7 +1165,7 @@ TouchChannel::setSequenceLED(uint8_t step, uint8_t pwm, bool blink) void TouchChannel::drawSequenceToDisplay(bool blink) { int counter = 0; - while (counter < sequence.progress) + while (counter <= sequence.progress) { display->setChannelLED(channelIndex, DISPLAY_SPIRAL_LED_MAP[counter], SEQ_LED_PROGRESS_PWM_MAP[counter], blink); counter++; @@ -1177,21 +1183,17 @@ void TouchChannel::stepSequenceLED() if (sequence.currPosition % sequence.progressDiviser == 0) { // check if this ppqn should advance the LEDs sequence.setProgress(); - display->setChannelLED(channelIndex, DISPLAY_SPIRAL_LED_MAP[sequence.progress], SEQ_LED_PROGRESS_PWM_MAP[sequence.progress], sequence.recordEnabled); + display->setChannelLED( + channelIndex, + DISPLAY_SPIRAL_LED_MAP[sequence.progress], + SEQ_LED_PROGRESS_PWM_MAP[sequence.progress], + sequence.recordEnabled); } } void TouchChannel::enableSequenceRecording() { sequence.enableRecording(); - display->enableBlink(); - - // if no sequence exists, light all 16 seq leds and flash them - if (!sequence.containsEvents()) { - display->fill(this->channelIndex, 20, true); - } else { - drawSequenceToDisplay(sequence.recordEnabled); - } if (playbackMode == MONO) { @@ -1201,6 +1203,18 @@ void TouchChannel::enableSequenceRecording() { setPlaybackMode(QUANTIZER_LOOP); } + + display->enableBlink(); + + // if no sequence exists, light all 16 seq leds and flash them + if (!sequence.containsEvents()) + { + display->fill(this->channelIndex, 20, true); + } + else + { + drawSequenceToDisplay(sequence.recordEnabled); + } } /** diff --git a/Degree/Tasks/Inc/task_sequence_handler.h b/Degree/Tasks/Inc/task_sequence_handler.h index e8702c7..9851264 100644 --- a/Degree/Tasks/Inc/task_sequence_handler.h +++ b/Degree/Tasks/Inc/task_sequence_handler.h @@ -18,6 +18,7 @@ enum class SEQ RECORD_DISABLE, RECORD_ARM, RECORD_DISARM, + RECORD_OVERFLOW, TOGGLE_MODE, SET_LENGTH, QUANTIZE, diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index aa0c00e..319eb1a 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -64,6 +64,9 @@ void task_sequence_handler(void *params) } break; + // time this so that it triggers: + // if pressed 12 PPQN before beat 1, wait to reset once beat 1 happens + // if < 12 ppqn after beat 1, reset immediately case SEQ::RESET: ctrl->clock->reset(); if (channel == CHAN::ALL) @@ -81,9 +84,11 @@ void task_sequence_handler(void *params) for (int i = 0; i < CHANNEL_COUNT; i++) { ctrl->channels[i]->sequence.clearAllTouchEvents(); + ctrl->channels[i]->setGate(0); } } else { ctrl->channels[channel]->sequence.clearAllTouchEvents(); + ctrl->channels[channel]->setGate(0); } break; @@ -133,6 +138,11 @@ void task_sequence_handler(void *params) recordDisarm = true; break; + case SEQ::RECORD_OVERFLOW: + if (ctrl->channels[channel]->sequence.containsEvents()) + ctrl->channels[channel]->drawSequenceToDisplay(true); + break; + case SEQ::TOGGLE_MODE: ctrl->channels[channel]->toggleMode(); break; From f2acf18939e9493ee8c577cafaa6dccd8bec8a2f Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 5 Dec 2022 20:32:23 -0500 Subject: [PATCH 18/44] added clock step callback, nothing implemented for it yet --- API/Inc/SuperClock.h | 4 ++++ API/Src/SuperClock.cpp | 8 ++++---- Degree/Inc/GlobalControl.h | 1 + Degree/Src/GlobalControl.cpp | 11 +++++++++++ Degree/Tasks/Inc/task_sequence_handler.h | 2 ++ Degree/Tasks/Src/task_sequence_handler.cpp | 12 ++++++++++++ 6 files changed, 34 insertions(+), 4 deletions(-) diff --git a/API/Inc/SuperClock.h b/API/Inc/SuperClock.h index 2f7c17f..db4fea0 100644 --- a/API/Inc/SuperClock.h +++ b/API/Inc/SuperClock.h @@ -39,6 +39,7 @@ class SuperClock { Callback barResetCallback; // executes when clock.step exceeds the set time signature (ie. one bar) Callback input_capture_callback; // this callback gets executed on the rising edge of an external input signal Callback ppqnCallback; // this callback gets executed at a rate equal to input capture / PPQN. It passes the current tick values as arguments + Callback stepCallback; // executes every step Callback resetCallback; Callback overflowCallback; // callback executes when a full step completes @@ -69,6 +70,9 @@ class SuperClock { // Callback Setters void attachInputCaptureCallback(Callback func); void attachPPQNCallback(Callback func); + void attachStepCallback(Callback func) { + stepCallback = func; + } void attachResetCallback(Callback func); void attachBarResetCallback(Callback func); diff --git a/API/Src/SuperClock.cpp b/API/Src/SuperClock.cpp index e9240be..c8de98c 100644 --- a/API/Src/SuperClock.cpp +++ b/API/Src/SuperClock.cpp @@ -195,10 +195,10 @@ void SuperClock::handleOverflowCallback() ppqnCallback(pulse); // when clock inits, this ensures the 0ith pulse will get handled // by checking this first, you have the chance to reset any sequences prior to executing their 0ith pulse - // if (pulse == 0) { - // if (resetCallback) - // resetCallback(); - // } + if (pulse == 0) { + if (stepCallback) + stepCallback(step); + } if (pulse < PPQN - 1) { pulse++; diff --git a/Degree/Inc/GlobalControl.h b/Degree/Inc/GlobalControl.h index 4b5cb58..26ca691 100644 --- a/Degree/Inc/GlobalControl.h +++ b/Degree/Inc/GlobalControl.h @@ -108,6 +108,7 @@ namespace DEGREE { void advanceSequencer(uint8_t pulse); void resetSequencer(uint8_t pulse); void handleBarReset(); + void handleStepCallback(uint16_t step); void handleFreeze(bool freeze); diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index d62ff82..0b9cfd6 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -69,6 +69,7 @@ void GlobalControl::init() { // initialize tempo clock->init(); + clock->attachStepCallback(callback(this, &GlobalControl::handleStepCallback)); clock->attachResetCallback(callback(this, &GlobalControl::resetSequencer)); clock->attachBarResetCallback(callback(this, &GlobalControl::handleBarReset)); clock->attachPPQNCallback(callback(this, &GlobalControl::advanceSequencer)); // always do this last @@ -678,6 +679,16 @@ void GlobalControl::resetSequencer(uint8_t pulse) dispatch_sequencer_event_ISR(CHAN::ALL, SEQ::CORRECT, 0); } +/** + * @brief triggers in ISR everytime clock progresses by one step + * + * @param step + */ +void GlobalControl::handleStepCallback(uint16_t step) +{ + dispatch_sequencer_event_ISR(CHAN::ALL, SEQ::QUARTER_NOTE_OVERFLOW, 0); +} + /** * @brief function that triggers at the begining of every bar * diff --git a/Degree/Tasks/Inc/task_sequence_handler.h b/Degree/Tasks/Inc/task_sequence_handler.h index 9851264..db5621e 100644 --- a/Degree/Tasks/Inc/task_sequence_handler.h +++ b/Degree/Tasks/Inc/task_sequence_handler.h @@ -10,8 +10,10 @@ enum class SEQ { ADVANCE, FREEZE, + RESET_ARM, RESET, BAR_RESET, + QUARTER_NOTE_OVERFLOW, CLEAR_TOUCH, CLEAR_BEND, RECORD_ENABLE, diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index 319eb1a..e394d75 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -16,6 +16,7 @@ void task_sequence_handler(void *params) uint32_t event = 0x0; bool recordArm = false; // flag used to enable recording once X number of steps have passed bool recordDisarm = false; // flag used to disable recording once X number of steps have passed + bool resetArmed = false; while (1) { @@ -64,10 +65,15 @@ void task_sequence_handler(void *params) } break; + case SEQ::RESET_ARM: + resetArmed = true; + break; + // time this so that it triggers: // if pressed 12 PPQN before beat 1, wait to reset once beat 1 happens // if < 12 ppqn after beat 1, reset immediately case SEQ::RESET: + resetArmed = false; // nyi ctrl->clock->reset(); if (channel == CHAN::ALL) { @@ -115,6 +121,12 @@ void task_sequence_handler(void *params) dispatch_sequencer_event(CHAN::ALL, SEQ::RECORD_DISABLE, 0); } break; + + case SEQ::QUARTER_NOTE_OVERFLOW: + if (resetArmed) { + dispatch_sequencer_event(CHAN::ALL, SEQ::RESET, 0); + } + break; case SEQ::RECORD_ENABLE: ctrl->recLED.write(1); From 3bad2df31e09a2eac7bd5e9e2b994843ea64150f Mon Sep 17 00:00:00 2001 From: scottc11 Date: Thu, 2 Feb 2023 17:30:47 -0500 Subject: [PATCH 19/44] handling of pre-recorded events (auto quantization to first step) --- .vscode/settings.json | 3 +- API/Inc/SuperClock.h | 9 +++--- API/Src/SuperClock.cpp | 2 +- Degree/Inc/SuperSeq.h | 8 +++-- Degree/Inc/TouchChannel.h | 5 +++ Degree/Src/SuperSeq.cpp | 6 +++- Degree/Src/TouchChannel.cpp | 37 ++++++++++++++++++++-- Degree/Src/main.cpp | 8 ++--- Degree/Tasks/Inc/task_sequence_handler.h | 1 + Degree/Tasks/Src/task_sequence_handler.cpp | 20 ++++++------ 10 files changed, 71 insertions(+), 28 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 819fbb4..6954227 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -68,6 +68,7 @@ "forward_list": "cpp", "list": "cpp", "condition_variable": "cpp", - "mutex": "cpp" + "mutex": "cpp", + "ranges": "cpp" } } \ No newline at end of file diff --git a/API/Inc/SuperClock.h b/API/Inc/SuperClock.h index db4fea0..e254254 100644 --- a/API/Inc/SuperClock.h +++ b/API/Inc/SuperClock.h @@ -10,7 +10,8 @@ #define PPQN 96 #endif -#define PPQN_ERROR (PPQN - (PPQN / 6)) +#define PPQN_8th (PPQN / 2) +#define PPQN_16th (PPQN / 4) #ifndef EXT_CLOCK_INPUT #define EXT_CLOCK_INPUT PA_3 @@ -29,10 +30,10 @@ class SuperClock { public: int pulse; // the current PPQN - uint8_t step; // current step. Will never exceed value of timeSignature + uint8_t step; // current step. Will never exceed value of stepsPerBar uint16_t ticksPerStep; // how many TIM2 ticks per one step / quarter note uint16_t ticksPerPulse; // how many TIM2 ticks for one PPQN - uint8_t timeSignature; // value represents the number of quarter notes per bar (ie. 3/4, 4/4, 5/4, 6/4, 7/4) + uint8_t stepsPerBar; // value represents the number of quarter notes per bar (ie. 3/4, 4/4, 5/4, 6/4, 7/4) bool externalInputMode; Callback tickCallback; // this callback gets executed at a frequency equal to tim1_freq @@ -52,7 +53,7 @@ class SuperClock { instance = this; ticksPerStep = 11129; ticksPerPulse = ticksPerStep / PPQN; - timeSignature = 4; + stepsPerBar = 4; }; void init(); diff --git a/API/Src/SuperClock.cpp b/API/Src/SuperClock.cpp index c8de98c..0801fc6 100644 --- a/API/Src/SuperClock.cpp +++ b/API/Src/SuperClock.cpp @@ -209,7 +209,7 @@ void SuperClock::handleOverflowCallback() // external input will reset pulse to 0 and resume TIM4 in input capture callback } else { pulse = 0; - if (step < timeSignature - 1) { + if (step < stepsPerBar - 1) { step++; } else { step = 0; diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index b688333..b2dabdd 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -11,6 +11,7 @@ #define SEQ_EVENT_GATE_BIT 4 #define SEQ_EVENT_INDEX_BIT_MASK 0b00001111 #define SEQ_EVENT_OCTAVE_BIT_MASK 0b11000000 +#define SEQ_EVENT_ACTIVE_OCTAVES_BIT_MASK 0b00001111 #define SEQ_PROGRESS_MAX 15 // "15" #define SEQ_PROGRESS_MIN 0 // "0" @@ -25,6 +26,7 @@ typedef struct SequenceNode bool getGate() { return bitwise_read_bit(data, SEQ_EVENT_GATE_BIT); } uint8_t getOctave() { return (data & SEQ_EVENT_OCTAVE_BIT_MASK) >> 6; } uint8_t getActiveOctaves() { return data; } + // void setActiveOctaves(uint8_t octaves) { data = data} } SequenceNode; class SuperSeq { @@ -63,6 +65,9 @@ class SuperSeq { bool containsTouchEvents;// flag indicating if a sequence has any touch events bool containsBendEvents; // flag indicating if a sequence has any bend events + static bool recordArmed; // when true, recording will be enabled the next time bar overflows + static bool recordDisarmed; // when true, recording will be disabled the next time bar overflows + void init(); void reset(); void resetStep(); @@ -81,9 +86,6 @@ class SuperSeq { void createChordEvent(int position, uint8_t degrees, uint8_t octaves); void setLength(int steps); - int getLength(); - int getLengthPPQN(); - void setProgress(); int getNextPosition(int position); diff --git a/Degree/Inc/TouchChannel.h b/Degree/Inc/TouchChannel.h index 2424f02..cde144f 100644 --- a/Degree/Inc/TouchChannel.h +++ b/Degree/Inc/TouchChannel.h @@ -8,6 +8,7 @@ #include "Bender.h" #include "VoltPerOctave.h" #include "SuperSeq.h" +#include "SuperClock.h" #include "Display.h" #include "okSemaphore.h" #include "task_sequence_handler.h" @@ -90,6 +91,7 @@ namespace DEGREE { TouchChannel( int _index, + SuperClock *clock_ptr, Display *display_ptr, MPR121 *touchPads_ptr, SX1509 *leds, @@ -102,6 +104,7 @@ namespace DEGREE { DigitalOut *global_gate_ptr) : gateOut(gatePin, 0), adc(adc_pin), output(dac, dac_chan, &adc), sequence(_bender) { channelIndex = _index; + clock = clock_ptr; display = display_ptr; touchPads = touchPads_ptr; _leds = leds; @@ -121,6 +124,7 @@ namespace DEGREE { }; int channelIndex; // an index value used for accessing the odd array + SuperClock *clock; Display *display; MPR121 *touchPads; SX1509 *_leds; @@ -213,6 +217,7 @@ namespace DEGREE { void enableSequenceRecording(); void disableSequenceRecording(); void handleQuantAmountLEDs(); + void handlePreRecordEvents(); // Bender methods void handleBend(uint16_t value); diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index 44a8ffc..6ad3893 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -1,5 +1,8 @@ #include "SuperSeq.h" +bool SuperSeq::recordArmed = false; +bool SuperSeq::recordDisarmed = false; + void SuperSeq::init() { this->clearAllEvents(); @@ -265,8 +268,9 @@ void SuperSeq::createChordEvent(int position, uint8_t degrees, uint8_t octaves) if (!containsTouchEvents) containsTouchEvents = true; + newEventPos = position; events[position].activeDegrees = degrees; - events[position].data = octaves; + events[position].data = octaves; // this setEventStatus(position, true); }; diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 3103497..44e8a86 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -297,6 +297,7 @@ void TouchChannel::handleTouchPlaybackEvent(uint8_t pad) switch (playbackMode) { case MONO: triggerNote(pad, currOctave, NOTE_ON); + handlePreRecordEvents(); break; case MONO_LOOP: // create a new event @@ -313,6 +314,7 @@ void TouchChannel::handleTouchPlaybackEvent(uint8_t pad) break; case QUANTIZER: setActiveDegrees(bitwise_write_bit(activeDegrees, pad, !bitwise_read_bit(activeDegrees, pad))); + handlePreRecordEvents(); // bug is here break; case QUANTIZER_LOOP: // every touch detected, take a snapshot of all active degree values and apply them to the sequence @@ -335,6 +337,7 @@ void TouchChannel::handleTouchPlaybackEvent(uint8_t pad) { case MONO: triggerNote(currDegree, pad, NOTE_ON); + handlePreRecordEvents(); break; case MONO_LOOP: if (sequence.recordEnabled) @@ -351,6 +354,7 @@ void TouchChannel::handleTouchPlaybackEvent(uint8_t pad) case QUANTIZER: setActiveOctaves(pad); setActiveDegrees(activeDegrees); // update active degrees thresholds + handlePreRecordEvents(); break; case QUANTIZER_LOOP: setActiveOctaves(pad); @@ -1090,7 +1094,7 @@ void TouchChannel::handleSequence(int position) case QUANTIZER_LOOP: if (sequence.getEventStatus(position)) { - if (sequence.overdub) + if (sequence.overdub && position != sequence.newEventPos) { sequence.clearTouchAtPosition(position); } @@ -1204,12 +1208,12 @@ void TouchChannel::enableSequenceRecording() setPlaybackMode(QUANTIZER_LOOP); } - display->enableBlink(); + // display->enableBlink(); // if no sequence exists, light all 16 seq leds and flash them if (!sequence.containsEvents()) { - display->fill(this->channelIndex, 20, true); + display->fill(this->channelIndex, 20, false); } else { @@ -1246,6 +1250,33 @@ void TouchChannel::disableSequenceRecording() } } +/** + * @brief When armed for recording, sudo record touch events leading up to recordEnable and + * place them at position 0 of a sequence + * ie. the last touch during the last 16th note prior to recording being enabled + * + * @param pad + */ +void TouchChannel::handlePreRecordEvents() +{ + if (SuperSeq::recordArmed && !sequence.containsEvents()) { + if (clock->step == clock->stepsPerBar - 1) { + if (clock->pulse > PPQN - PPQN_16th) { + switch (playbackMode) + { + case MONO: + sequence.createTouchEvent(0, currDegree, currOctave, HIGH); + break; + case QUANTIZER: + sequence.createChordEvent(0, activeDegrees, activeOctaves); + break; + } + } + } + } + sequence.containsTouchEvents = false; +} + void TouchChannel::initializeCalibration() { output.resetVoltageMap(); // You should reset prior to tuning output.resetDAC(); diff --git a/Degree/Src/main.cpp b/Degree/Src/main.cpp index 7dbbdf5..993d270 100644 --- a/Degree/Src/main.cpp +++ b/Degree/Src/main.cpp @@ -63,10 +63,10 @@ Bender benderB(&dac2, DAC8554::CHAN_B, PB_ADC_B); Bender benderC(&dac2, DAC8554::CHAN_C, PB_ADC_C); Bender benderD(&dac2, DAC8554::CHAN_D, PB_ADC_D); -TouchChannel chanA(0, &display, &touchA, &ledsA, °rees, &dac1, DAC8554::CHAN_A, &benderA, ADC_A, GATE_OUT_A, &globalGate); -TouchChannel chanB(1, &display, &touchB, &ledsB, °rees, &dac1, DAC8554::CHAN_B, &benderB, ADC_B, GATE_OUT_B, &globalGate); -TouchChannel chanC(2, &display, &touchC, &ledsC, °rees, &dac1, DAC8554::CHAN_C, &benderC, ADC_C, GATE_OUT_C, &globalGate); -TouchChannel chanD(3, &display, &touchD, &ledsD, °rees, &dac1, DAC8554::CHAN_D, &benderD, ADC_D, GATE_OUT_D, &globalGate); +TouchChannel chanA(0, &superClock, &display, &touchA, &ledsA, °rees, &dac1, DAC8554::CHAN_A, &benderA, ADC_A, GATE_OUT_A, &globalGate); +TouchChannel chanB(1, &superClock, &display, &touchB, &ledsB, °rees, &dac1, DAC8554::CHAN_B, &benderB, ADC_B, GATE_OUT_B, &globalGate); +TouchChannel chanC(2, &superClock, &display, &touchC, &ledsC, °rees, &dac1, DAC8554::CHAN_C, &benderC, ADC_C, GATE_OUT_C, &globalGate); +TouchChannel chanD(3, &superClock, &display, &touchD, &ledsD, °rees, &dac1, DAC8554::CHAN_D, &benderD, ADC_D, GATE_OUT_D, &globalGate); GlobalControl glblCtrl(&superClock, &chanA, &chanB, &chanC, &chanD, &globalTouch, °rees, &buttons, &display); diff --git a/Degree/Tasks/Inc/task_sequence_handler.h b/Degree/Tasks/Inc/task_sequence_handler.h index db5621e..c113e9a 100644 --- a/Degree/Tasks/Inc/task_sequence_handler.h +++ b/Degree/Tasks/Inc/task_sequence_handler.h @@ -3,6 +3,7 @@ #include "main.h" #include "logger.h" #include "GlobalControl.h" +#include "SuperSeq.h" using namespace DEGREE; diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index e394d75..a241c03 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -14,8 +14,6 @@ void task_sequence_handler(void *params) sequencer_task_handle = xTaskGetCurrentTaskHandle(); sequencer_queue = xQueueCreate(96, sizeof(uint32_t)); uint32_t event = 0x0; - bool recordArm = false; // flag used to enable recording once X number of steps have passed - bool recordDisarm = false; // flag used to disable recording once X number of steps have passed bool resetArmed = false; while (1) @@ -29,7 +27,7 @@ void task_sequence_handler(void *params) switch (action) { case SEQ::ADVANCE: - if (recordArm || recordDisarm) { + if (SuperSeq::recordArmed || SuperSeq::recordDisarmed) { if (data % 24 == 0) { // data in this case is the current pulse ctrl->recLED.toggle(); } @@ -113,11 +111,11 @@ void task_sequence_handler(void *params) break; case SEQ::BAR_RESET: - if (recordArm) { - recordArm = false; + if (SuperSeq::recordArmed) { + SuperSeq::recordArmed = false; dispatch_sequencer_event(CHAN::ALL, SEQ::RECORD_ENABLE, 0); - } else if (recordDisarm) { - recordDisarm = false; + } else if (SuperSeq::recordDisarmed) { + SuperSeq::recordDisarmed = false; dispatch_sequencer_event(CHAN::ALL, SEQ::RECORD_DISABLE, 0); } break; @@ -141,13 +139,13 @@ void task_sequence_handler(void *params) break; case SEQ::RECORD_ARM: - recordArm = true; - recordDisarm = false; + SuperSeq::recordDisarmed = false; + SuperSeq::recordArmed = true; break; case SEQ::RECORD_DISARM: - recordArm = false; - recordDisarm = true; + SuperSeq::recordArmed = false; + SuperSeq::recordDisarmed = true; break; case SEQ::RECORD_OVERFLOW: From b34a3a7ab6ccc9ecc825a32a98ae0d0b14bcb70c Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sun, 5 Feb 2023 16:52:34 -0500 Subject: [PATCH 20/44] freeze button led stays on when pressed --- Degree/Inc/GlobalControl.h | 1 + Degree/Src/GlobalControl.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Degree/Inc/GlobalControl.h b/Degree/Inc/GlobalControl.h index 26ca691..19cab9d 100644 --- a/Degree/Inc/GlobalControl.h +++ b/Degree/Inc/GlobalControl.h @@ -94,6 +94,7 @@ namespace DEGREE { uint16_t currTempoPotValue; uint16_t prevTempoPotValue; + bool freezeBtn; // state of the freeze button bool gestureFlag; uint8_t currTouched; uint8_t prevTouched; diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index 0b9cfd6..b7988d8 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -656,8 +656,11 @@ void GlobalControl::advanceSequencer(uint8_t pulse) } } else if (pulse == 4) { tempoLED.write(LOW); - freezeLED.write(LOW); tempoGate.write(LOW); + if (freezeBtn == false) + { + freezeLED.write(LOW); + } } dispatch_sequencer_event_ISR(CHAN::ALL, SEQ::ADVANCE, pulse); @@ -699,6 +702,8 @@ void GlobalControl::handleBarReset() } void GlobalControl::handleFreeze(bool freeze) { + // set freeze variable here, and check in clock callback if freeze is true/false + freezeBtn = freeze; if (this->gestureFlag) { for (int i = 0; i < CHANNEL_COUNT; i++) From ce2deaf5838335e0dd9ccb71376af49b562bc3a8 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 6 Feb 2023 08:54:55 -0500 Subject: [PATCH 21/44] LENGTH button now adjusts clock time signature (steps per bar) --- API/Inc/SuperClock.h | 1 + API/Src/SuperClock.cpp | 10 +++++++++ Degree/Inc/GlobalControl.h | 1 + Degree/Inc/SuperSeq.h | 5 ++++- Degree/Src/GlobalControl.cpp | 24 ++++++++++++++++++++-- Degree/Src/SuperSeq.cpp | 15 +++++++++++--- Degree/Src/TouchChannel.cpp | 6 +++--- Degree/Tasks/Inc/task_sequence_handler.h | 2 ++ Degree/Tasks/Src/task_sequence_handler.cpp | 10 +++++++++ 9 files changed, 65 insertions(+), 9 deletions(-) diff --git a/API/Inc/SuperClock.h b/API/Inc/SuperClock.h index e254254..68bed73 100644 --- a/API/Inc/SuperClock.h +++ b/API/Inc/SuperClock.h @@ -62,6 +62,7 @@ class SuperClock { void start(); void reset(); + void setStepsPerBar(int steps); void setPulseFrequency(uint32_t ticks); uint16_t convertADCReadToTicks(uint16_t min, uint16_t max, uint16_t value); diff --git a/API/Src/SuperClock.cpp b/API/Src/SuperClock.cpp index 0801fc6..4d6adcc 100644 --- a/API/Src/SuperClock.cpp +++ b/API/Src/SuperClock.cpp @@ -27,6 +27,16 @@ void SuperClock::reset() this->step = 0; } +void SuperClock::setStepsPerBar(int steps) { + if (steps < 3) { + return; + } else if (steps > 7) { + return; + } else { + stepsPerBar = steps; + } +} + /** * @brief initialize TIM2 as a slave to TIM1 * @param prescaler setting to 1 should be best diff --git a/Degree/Inc/GlobalControl.h b/Degree/Inc/GlobalControl.h index 19cab9d..49a6500 100644 --- a/Degree/Inc/GlobalControl.h +++ b/Degree/Inc/GlobalControl.h @@ -110,6 +110,7 @@ namespace DEGREE { void resetSequencer(uint8_t pulse); void handleBarReset(); void handleStepCallback(uint16_t step); + void drawTimeSignatureToDisplay(); void handleFreeze(bool freeze); diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index b2dabdd..7e4620a 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -44,6 +44,8 @@ class SuperSeq { int length; // how many steps the sequence contains int lengthPPQN; // how many PPQN the sequence contains + int maxLength; // the maximum allowable steps in a sequence + int currStepPosition; // the number of PPQN that have passed since the last step was advanced int currStep; // current sequence step int prevStep; // the previous step executed in the sequence @@ -86,6 +88,7 @@ class SuperSeq { void createChordEvent(int position, uint8_t degrees, uint8_t octaves); void setLength(int steps); + void setMaxLength(int stepsPerBar); void setProgress(); int getNextPosition(int position); @@ -94,7 +97,7 @@ class SuperSeq { void advance(); - void enableRecording(); + void enableRecording(int stepsPerBar); void disableRecording(); void enablePlayback(); diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index b7988d8..bbf133f 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -402,10 +402,16 @@ void GlobalControl::handleButtonPress(int pad) mode = ControlMode::SETTING_SEQUENCE_LENGTH; for (int chan = 0; chan < CHANNEL_COUNT; chan++) { + // deactivate any downstream channel interactions with the display, channels[chan]->setUIMode(TouchChannel::UIMode::UI_SEQUENCE_LENGTH); + + // take control of all the benders channels[chan]->enableBenderOverride(); - display->enableBlink(); - channels[chan]->drawSequenceToDisplay(true); + + // draw the current clock time signature to the display + drawTimeSignatureToDisplay(); + + // bender tristate callback will send commands to the rtos } break; @@ -692,6 +698,20 @@ void GlobalControl::handleStepCallback(uint16_t step) dispatch_sequencer_event_ISR(CHAN::ALL, SEQ::QUARTER_NOTE_OVERFLOW, 0); } +/** + * @brief draw the current clock time signature to the display + * + */ +void GlobalControl::drawTimeSignatureToDisplay() +{ + display->clear(); + for (int i = 0; i < clock->stepsPerBar; i++) + { + display->setLED(i, 127, false); + } +} + + /** * @brief function that triggers at the begining of every bar * diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index 6ad3893..663f61a 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -64,10 +64,10 @@ void SuperSeq::advance() if (recordEnabled && adaptiveLength) { // disable adaptive length, set seq length to max, and enable overdub - if (currStep >= MAX_SEQ_LENGTH) + if (currStep >= maxLength) { adaptiveLength = false; - this->setLength(MAX_SEQ_LENGTH); + this->setLength(maxLength); currPosition = 0; currStep = 0; overwriteExistingEvents = true; // enable overdub @@ -85,12 +85,13 @@ void SuperSeq::advance() } } -void SuperSeq::enableRecording() { +void SuperSeq::enableRecording(int stepsPerBar /*time signature*/) { this->recordEnabled = true; // if no currently recorded events, enable adaptive length if (!this->containsEvents()) { this->reset(); this->setLength(2); + this->setMaxLength(stepsPerBar); this->adaptiveLength = true; } else { this->overwriteExistingEvents = true; @@ -287,6 +288,14 @@ void SuperSeq::setLength(int steps) } }; +void SuperSeq::setMaxLength(int stepsPerBar) { + maxLength = (MAX_SEQ_LENGTH / stepsPerBar) * stepsPerBar; + if (maxLength > MAX_SEQ_LENGTH) + { + maxLength = MAX_SEQ_LENGTH; + } +} + void SuperSeq::setProgress() { this->progress = this->currPosition / this->progressDiviser; } diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 44e8a86..4103cd7 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -863,11 +863,11 @@ void TouchChannel::benderTriStateCallback(Bender::BendState state) case TouchChannel::UIMode::UI_SEQUENCE_LENGTH: if (state == Bender::BendState::BENDING_UP) { - dispatch_sequencer_event((CHAN)channelIndex, SEQ::SET_LENGTH, sequence.length + 2); + dispatch_sequencer_event((CHAN)channelIndex, SEQ::INCREMENT_TIME_SIG, 0); } else if (state == Bender::BendState::BENDING_DOWN) { - dispatch_sequencer_event((CHAN)channelIndex, SEQ::SET_LENGTH, sequence.length - 2); + dispatch_sequencer_event((CHAN)channelIndex, SEQ::DECREMENT_TIME_SIG, 0); } break; default: @@ -1197,7 +1197,7 @@ void TouchChannel::stepSequenceLED() void TouchChannel::enableSequenceRecording() { - sequence.enableRecording(); + sequence.enableRecording(clock->stepsPerBar); if (playbackMode == MONO) { diff --git a/Degree/Tasks/Inc/task_sequence_handler.h b/Degree/Tasks/Inc/task_sequence_handler.h index c113e9a..4ac494b 100644 --- a/Degree/Tasks/Inc/task_sequence_handler.h +++ b/Degree/Tasks/Inc/task_sequence_handler.h @@ -24,6 +24,8 @@ enum class SEQ RECORD_OVERFLOW, TOGGLE_MODE, SET_LENGTH, + INCREMENT_TIME_SIG, + DECREMENT_TIME_SIG, QUANTIZE, CORRECT, HANDLE_TOUCH, diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index a241c03..055b02b 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -161,6 +161,16 @@ void task_sequence_handler(void *params) ctrl->channels[channel]->updateSequenceLength(data); break; + case SEQ::INCREMENT_TIME_SIG: + ctrl->clock->setStepsPerBar(ctrl->clock->stepsPerBar + 1); + ctrl->drawTimeSignatureToDisplay(); + break; + + case SEQ::DECREMENT_TIME_SIG: + ctrl->clock->setStepsPerBar(ctrl->clock->stepsPerBar - 1); + ctrl->drawTimeSignatureToDisplay(); + break; + case SEQ::QUANTIZE: if (channel == CHAN::ALL) { From 7351ffdb9ffd36d0a393bf805608f032d072187e Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 6 Feb 2023 18:11:34 -0500 Subject: [PATCH 22/44] closes #201, fixed pitch bend when exiting idle bender position --- Degree/Src/TouchChannel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 7e88a7c..0a988ab 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -734,13 +734,13 @@ void TouchChannel::handlePitchBend(uint16_t value) { if (value < bender->getIdleValue()) { pitchbend = output.calculatePitchBend(value, bender->getMinBend(), bender->getIdleValue()); - output.setPitchBend(output.maxPitchBend - pitchbend, true); // NOTE: inverted mapping + output.setPitchBend(output.maxPitchBend - pitchbend - BENDER_NOISE_THRESHOLD, true); // NOTE: inverted mapping } // Pitch Bend DOWN else if (value > bender->getIdleValue()) { pitchbend = output.calculatePitchBend(value, bender->getIdleValue(), bender->getMaxBend()); - output.setPitchBend(pitchbend, false); // value needs to be negative + output.setPitchBend(pitchbend - BENDER_NOISE_THRESHOLD, false); // value needs to be negative } } } From 56d6a4d452e6ccd49e940df911b2fefd0aa3ca9a Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sat, 11 Mar 2023 15:50:04 -0500 Subject: [PATCH 23/44] closes #195 firmware version check --- Degree/Inc/main.h | 10 +++++-- Degree/Src/GlobalControl.cpp | 51 +++++++++++++++++++++++++++++++----- Makefile | 5 ++++ 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/Degree/Inc/main.h b/Degree/Inc/main.h index 2faa3dc..3a74e83 100644 --- a/Degree/Inc/main.h +++ b/Degree/Inc/main.h @@ -9,6 +9,10 @@ // #define LOGGING_ENABLED #define BOARD_VERSION 42 +#ifndef FIRMWARE_VERSION // this will be passed in as a flag by compiler +#define FIRMWARE_VERSION "default" +#endif + #define PPQN 96 #define PPQN_DIV_2 (PPQN / 2) #define PPQN_DIV_4 (PPQN / 4) @@ -20,9 +24,11 @@ #define ADC_TIM_PERIOD 2000 #define DAC_1VO_ARR_SIZE 72 +#define FIRMWARE_VERSION_SIZE 8 -#define SETTINGS_DAC_1VO 0 -#define SETTINGS_BENDER_MIN DAC_1VO_ARR_SIZE +#define SETTINGS_FIRMWARE_VERSION 0 +#define SETTINGS_DAC_1VO FIRMWARE_VERSION_SIZE +#define SETTINGS_BENDER_MIN (SETTINGS_DAC_1VO + DAC_1VO_ARR_SIZE) #define SETTINGS_BENDER_MAX (SETTINGS_BENDER_MIN + 1) #define SETTINGS_BENDER_MODE (SETTINGS_BENDER_MAX + 1) #define SETTINGS_PITCH_BEND_RANGE (SETTINGS_BENDER_MODE + 1) diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index 9784bb7..6d91d73 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -497,6 +497,32 @@ void GlobalControl::handleButtonRelease(int pad) } } +bool validateFirmwareVersionMatch() { + char *string = FIRMWARE_VERSION; + int string_len = strlen(string); + + if (string_len > FIRMWARE_VERSION_SIZE) { + string_len = FIRMWARE_VERSION_SIZE; + } + + // if any of the values in the buffer don't match the respective values in the firmware string array + // then there is no version match, and the config settings need to be reset / cleared / not used + bool versionMatch; + for (int i = 0; i < string_len; i++) + { + if (SETTINGS_BUFFER[i] == (uint32_t)string[i]) + { + versionMatch = true; + } + else + { + versionMatch = false; + break; + } + } + return versionMatch; +} + /** * NOTE: Careful with the creation of this buffer, as it is quite large and may push the memory past its limits */ @@ -505,9 +531,12 @@ void GlobalControl::loadCalibrationDataFromFlash() Flash flash; flash.read(FLASH_CONFIG_ADDR, (uint32_t *)SETTINGS_BUFFER, SETTINGS_BUFFER_SIZE); - bool dataIsClean = flash.validate(SETTINGS_BUFFER, 4); + bool configDataEmpty = flash.validate(SETTINGS_BUFFER, 4); + + bool isVersionMatch = validateFirmwareVersionMatch(); - if (dataIsClean) + // if no config data or firmware version mismatch, load default configuration + if (configDataEmpty || !isVersionMatch) { // load default 1VO values logger_log("\nChannel Settings Source: DEFAULT"); @@ -516,12 +545,12 @@ void GlobalControl::loadCalibrationDataFromFlash() channels[chan]->output.resetVoltageMap(); } } - else - { // if it does, load the data from flash + else // load the data from flash + { logger_log("\nChannel Settings Source: FLASH"); for (int chan = 0; chan < CHANNEL_COUNT; chan++) { - for (int i = SETTINGS_DAC_1VO; i < DAC_1VO_ARR_SIZE; i++) + for (int i = SETTINGS_DAC_1VO; i < DAC_1VO_ARR_SIZE + SETTINGS_DAC_1VO; i++) { channels[chan]->output.dacVoltageMap[i] = (uint16_t)getSettingsBufferValue(i, chan); } @@ -547,7 +576,17 @@ void GlobalControl::saveCalibrationDataToFlash() int buffer_position = 0; for (int chan = 0; chan < CHANNEL_COUNT; chan++) // channel iterrator { - for (int i = SETTINGS_DAC_1VO; i < DAC_1VO_ARR_SIZE; i++) // dac array iterrator + // copy firmware version into buffer + char *string = FIRMWARE_VERSION; + int string_len = strlen(string); + + for (int i = 0; i < string_len; i++) + { + SETTINGS_BUFFER[i] = (uint32_t)string[i]; + } + + + for (int i = SETTINGS_DAC_1VO; i < DAC_1VO_ARR_SIZE + SETTINGS_DAC_1VO; i++) // dac array iterrator { setSettingsBufferValue(i, chan, channels[chan]->output.dacVoltageMap[i]); } diff --git a/Makefile b/Makefile index db07fc1..879c572 100644 --- a/Makefile +++ b/Makefile @@ -26,6 +26,8 @@ SERIAL_DEBUG ?= 0 # optimization OPT = -Og +# get firmware version from git +FIRMWARE_VERSION = $(shell git rev-parse --short HEAD) ####################################### # paths @@ -236,6 +238,9 @@ ifeq ($(SERIAL_DEBUG), 1) CFLAGS += -DSERIAL_DEBUG=1 endif +# pass the firmware version into program +CFLAGS += -DFIRMWARE_VERSION=\"$(FIRMWARE_VERSION)\" + # Generate dependency information CFLAGS += -MMD -MP -MF"$(@:%.o=%.d)" From 6fe1a59862bcf1379be38f56b14be9afdd762485 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 13 Mar 2023 07:14:47 -0400 Subject: [PATCH 24/44] created functions and setup flash to store and recall a single channels sequence data --- .vscode/.cortex-debug.peripherals.state.json | 2 +- .vscode/settings.json | 4 +- API/Inc/Flash.h | 1 + API/Src/Flash.cpp | 25 ++++++++- Degree/Inc/SuperSeq.h | 9 ++-- Degree/Inc/main.h | 8 ++- Degree/Src/GlobalControl.cpp | 53 +++++++++++++++++++- Degree/Src/SuperSeq.cpp | 49 ++++++++++++++++++ Degree/Src/TouchChannel.cpp | 11 ++-- 9 files changed, 148 insertions(+), 14 deletions(-) diff --git a/.vscode/.cortex-debug.peripherals.state.json b/.vscode/.cortex-debug.peripherals.state.json index 47214df..e3a3ec6 100644 --- a/.vscode/.cortex-debug.peripherals.state.json +++ b/.vscode/.cortex-debug.peripherals.state.json @@ -1 +1 @@ -[{"node":"FLASH","expanded":true,"format":0,"pinned":false}] \ No newline at end of file +[{"node":"FLASH","expanded":true,"format":0,"pinned":false},{"node":"FLASH.ACR","expanded":true,"format":0},{"node":"FLASH.KEYR","expanded":true,"format":0},{"node":"FLASH.SR","expanded":true,"format":0},{"node":"FLASH.OPTCR","expanded":true,"format":0}] \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 819fbb4..784b063 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -68,6 +68,8 @@ "forward_list": "cpp", "list": "cpp", "condition_variable": "cpp", - "mutex": "cpp" + "mutex": "cpp", + "ranges": "cpp", + "span": "cpp" } } \ No newline at end of file diff --git a/API/Inc/Flash.h b/API/Inc/Flash.h index 0d54b16..33e49e3 100644 --- a/API/Inc/Flash.h +++ b/API/Inc/Flash.h @@ -71,6 +71,7 @@ class Flash { HAL_StatusTypeDef write(uint32_t address, uint32_t *data, int size); void read(uint32_t address, uint32_t *rxBuffer, int size); + uint32_t read_word(void *address); uint32_t getSector(uint32_t Address); bool validate(uint32_t *data, int size); diff --git a/API/Src/Flash.cpp b/API/Src/Flash.cpp index dd824d6..7d538af 100644 --- a/API/Src/Flash.cpp +++ b/API/Src/Flash.cpp @@ -83,6 +83,16 @@ HAL_StatusTypeDef Flash::erase(uint32_t address) return status; } +/** + * @brief Write to flash memory + * @note flash memory can only be written to by changing its value from a 1 to a 0. + * Once a bit in the flash memory is set to 0, it cannot be changed back to 1 without erasing the entire sector. + * + * @param address flash memory address you wish to write to + * @param data pointer to an array containing the data you want to store in flash + * @param size size of data array + * @return HAL_StatusTypeDef + */ HAL_StatusTypeDef Flash::write(uint32_t address, uint32_t *data, int size) { HAL_StatusTypeDef status; @@ -111,7 +121,7 @@ HAL_StatusTypeDef Flash::write(uint32_t address, uint32_t *data, int size) flashError = HAL_FLASH_GetError(); } else { size--; - address += 4; + address += 4; // 1 "word" == 4 bytes data++; } } @@ -124,7 +134,7 @@ HAL_StatusTypeDef Flash::write(uint32_t address, uint32_t *data, int size) } /** - * @brief Read data starting at defined address + * @brief Read data starting at defined address and load it into a buffer * * @param address Address to begin reading from * @param rxBuffer The buffer to read data into. Must be of type uint32_t @@ -141,6 +151,17 @@ void Flash::read(uint32_t address, uint32_t *rxBuffer, int size) } } +/** + * @brief read a single 32-bit word at a specific memory address and return it + * + * @param address + * @return uint32_t + */ +uint32_t Flash::read_word(void *address) +{ + return *(uint32_t *)address; +} + /** * @brief Check if data read from flash has been cleared / not written too by testing contents of buffer * diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index 1885e30..2ec5667 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -37,6 +37,7 @@ class SuperSeq { setQuantizeAmount(QUANT::EIGTH); }; + SequenceNode events[MAX_SEQ_LENGTH_PPQN]; Bender *bender; // you need the instance of a bender for determing its idle value when clearing / initializing bender events QUANT quantizeAmount; @@ -99,6 +100,11 @@ class SuperSeq { void setEventData(int position, uint8_t degree, uint8_t octave, bool gate, bool status); + uint32_t encodeEventData(int position); + void decodeEventData(int position, uint32_t data); + void storeSequenceConfigData(uint32_t *arr); + void loadSequenceConfigData(uint32_t *arr); + uint8_t getEventDegree(int position); uint8_t getEventOctave(int position); uint8_t getActiveDegrees(int position); @@ -117,7 +123,4 @@ class SuperSeq { uint8_t setActiveOctaveBits(uint8_t octaves); void logSequenceToConsole(); - -private: - SequenceNode events[MAX_SEQ_LENGTH_PPQN]; }; \ No newline at end of file diff --git a/Degree/Inc/main.h b/Degree/Inc/main.h index 3a74e83..9d38e95 100644 --- a/Degree/Inc/main.h +++ b/Degree/Inc/main.h @@ -38,6 +38,12 @@ #define CALIBRATION_ARR_SIZE (SETTINGS_QUANTIZE_AMOUNT + 1) #define SETTINGS_BUFFER_SIZE (CALIBRATION_ARR_SIZE * CHANNEL_COUNT) +#define FLASH_CONFIG_ADDR ADDR_FLASH_SECTOR_7 + +#define FLASH_SEQUENCE_CONFIG_ADDR (uint32_t)0x080605e0 +#define FLASH_SEQUENCE_DATA_ADDR (uint32_t)0x08060600 +#define FLASH_CHANNEL_CONFIG_ADDR (uint32_t)0x080605c0 + #define OCTAVE_COUNT 4 #define DEGREE_COUNT 8 @@ -119,8 +125,6 @@ #define MCP23017_DEGREES_ADDR 0x20 // 0100000 #define MCP23017_CTRL_ADDR 0x24 // 0100100 -#define FLASH_CONFIG_ADDR ADDR_FLASH_SECTOR_7 // sector 7 - enum CHAN { A, diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index 6d91d73..e6de096 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -561,6 +561,27 @@ void GlobalControl::loadCalibrationDataFromFlash() channels[chan]->sequence.setQuantizeAmount(static_cast(getSettingsBufferValue(SETTINGS_QUANTIZE_AMOUNT, chan))); channels[chan]->sequence.setLength(getSettingsBufferValue(SETTINGS_SEQ_LENGTH, chan)); } + + // load channel config + uint32_t temp = flash.read_word((void *)FLASH_CHANNEL_CONFIG_ADDR); + channels[0]->playbackMode = (TouchChannel::PlaybackMode)temp; + + // load sequence data + uint32_t sequence_config[4]; + flash.read(FLASH_SEQUENCE_CONFIG_ADDR, sequence_config, 4); + channels[0]->sequence.loadSequenceConfigData(sequence_config); + + if (channels[0]->sequence.containsEvents()) + { + // read a single word (4 bytes) into array, then decode it and store in seqeuence event struct + int addr = 0; + for (int i = 0; i < channels[0]->sequence.lengthPPQN; i++) + { + uint32_t event_data = flash.read_word((void *)FLASH_SEQUENCE_DATA_ADDR + addr); + channels[0]->sequence.decodeEventData(i, event_data); + addr += 4; + } + } } } @@ -600,8 +621,38 @@ void GlobalControl::saveCalibrationDataToFlash() } // now load this buffer into flash memory Flash flash; - flash.erase(FLASH_CONFIG_ADDR); // should flash.erase be moved into flash.write method? + flash.erase(FLASH_CONFIG_ADDR); flash.write(FLASH_CONFIG_ADDR, SETTINGS_BUFFER, SETTINGS_BUFFER_SIZE); + + // store channel config (playbackMode) + uint32_t channel_config[1]; + channel_config[0] = (uint32_t)channels[0]->playbackMode; + flash.write(FLASH_CHANNEL_CONFIG_ADDR, channel_config, 1); + + // store sequence config for each channel + uint32_t sequence_config[4]; + channels[0]->sequence.storeSequenceConfigData(sequence_config); + flash.write(FLASH_SEQUENCE_CONFIG_ADDR, sequence_config, 4); + + // if a sequence exists, store that in flash as well + if (channels[0]->sequence.containsEvents()) + { + // Max sequence size per channel = (32 bits per event / 4 bytes) * MAX_SEQ_LENGTH_PPQN = 12,288 bytes + uint16_t flashWrites = channels[0]->sequence.length; + uint32_t sequence_data[PPQN]; + // ensure no RAM overflow by writing to flash in smaller chunks + for (int x = 0; x < flashWrites; x++) + { + for (int i = 0; i < PPQN; i++) + { + int pos = (x * PPQN) + i; + sequence_data[i] = channels[0]->sequence.encodeEventData(pos); // encode sequence data to be stored in 32-bit chunks + } + + flash.write(FLASH_SEQUENCE_DATA_ADDR + (x * 96 * 4), sequence_data, PPQN); + } + } + // flash the grid of leds on and off for a sec then exit this->display->flash(3, 300); this->display->clear(); diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index 96b1d10..6b4a119 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -438,6 +438,55 @@ void SuperSeq::setEventData(int position, uint8_t degree, uint8_t octave, bool g events[position].data = data; } +/** + * @brief combine all event struct data into a 32-bit number + * + * @param position + * @return uint32_t + */ +uint32_t SuperSeq::encodeEventData(int position) +{ + uint32_t data; + data = events[position].bend; // 16-bits + data = (data << 8) | events[position].activeDegrees; // 8-bits + data = (data << 8) | events[position].data; // 8-bits + return data; +} + +/** + * @brief unpack event data and store into event struct + * + * @param position + * @param data data chunk from flash + */ +void SuperSeq::decodeEventData(int position, uint32_t data) +{ + events[position].bend = (uint16_t)(data >> 16); + events[position].activeDegrees = (uint8_t)((data & 0x0000FF00) >> 8); + events[position].data = (uint8_t)(data & 0x000000FF); +} + +/** + * @brief store sequence configuration into an array (to be stored in flash) + * + * @param arr + * @return uint32_t + */ +void SuperSeq::storeSequenceConfigData(uint32_t *arr) { + arr[0] = this->length; + arr[1] = this->lengthPPQN; + arr[2] = this->containsBendEvents; + arr[3] = this->containsTouchEvents; +} + +void SuperSeq::loadSequenceConfigData(uint32_t *arr) +{ + this->length = (int)arr[0]; + this->lengthPPQN = (int)arr[1]; + this->containsBendEvents = (bool)arr[2]; + this->containsTouchEvents = (bool)arr[3]; +} + uint8_t SuperSeq::getEventDegree(int position) { return events[position].getDegree(); diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 0a988ab..fabe294 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -33,8 +33,6 @@ void TouchChannel::init() bender->attachIdleCallback(callback(this, &TouchChannel::benderIdleCallback)); bender->attachTriStateCallback(callback(this, &TouchChannel::benderTriStateCallback)); - sequence.init(); // really important sequencer initializes after the bender gets initialized - // initialize channel touch pads touchPads->init(); touchPads->attachInterruptCallback(callback(this, &TouchChannel::handleTouchInterrupt)); @@ -42,10 +40,15 @@ void TouchChannel::init() touchPads->attachCallbackReleased(callback(this, &TouchChannel::onRelease)); touchPads->enable(); - // you should actually be accessing a global settings buffer display->drawSpiral(channelIndex, true, PWM::PWM_HIGH, 25); + + // flash settings sensitive below + if (!sequence.containsEvents()) // don't init if sequence was loaded from flash + { + sequence.init(); // really important sequencer initializes after the bender gets initialized + } setPlaybackMode(playbackMode); // value of playbackMode gets loaded and assigned from flash - setBenderMode((BenderMode)currBenderMode); // value of playbackMode gets loaded and assigned from flash + setBenderMode((BenderMode)currBenderMode); logPeripherals(); } From c2aebab2ade8766938609cdce664fe5e58f51594 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 13 Mar 2023 17:49:56 -0400 Subject: [PATCH 25/44] closes #185 save channel sequence in flash --- Degree/Inc/main.h | 34 +++++++------- Degree/Src/GlobalControl.cpp | 86 ++++++++++++++++++++---------------- 2 files changed, 68 insertions(+), 52 deletions(-) diff --git a/Degree/Inc/main.h b/Degree/Inc/main.h index 9d38e95..f91effc 100644 --- a/Degree/Inc/main.h +++ b/Degree/Inc/main.h @@ -18,6 +18,10 @@ #define PPQN_DIV_4 (PPQN / 4) #define PPQN_DIV_8 (PPQN / 8) +#define DEFAULT_SEQ_LENGTH 8 +#define MAX_SEQ_LENGTH 32 +#define MAX_SEQ_LENGTH_PPQN (MAX_SEQ_LENGTH * PPQN) + #define CHANNEL_COUNT 4 #define ADC_DMA_BUFF_SIZE 9 #define ADC_TIM_PRESCALER 100 @@ -40,17 +44,18 @@ #define FLASH_CONFIG_ADDR ADDR_FLASH_SECTOR_7 -#define FLASH_SEQUENCE_CONFIG_ADDR (uint32_t)0x080605e0 -#define FLASH_SEQUENCE_DATA_ADDR (uint32_t)0x08060600 -#define FLASH_CHANNEL_CONFIG_ADDR (uint32_t)0x080605c0 +#define FLASH_CHANNEL_CONFIG_SIZE 32 // 32 bytes = 8 32-bit words (one row in flash) +#define FLASH_SEQUENCE_CONFIG_SIZE 32 +#define FLASH_SEQUENCE_DATA_SIZE MAX_SEQ_LENGTH_PPQN + +#define FLASH_CHANNEL_CONFIG_ADDR (uint32_t)0x08060600 +#define FLASH_SEQUENCE_CONFIG_ADDR (FLASH_CHANNEL_CONFIG_ADDR + FLASH_CHANNEL_CONFIG_SIZE) +#define FLASH_SEQUENCE_DATA_ADDR (FLASH_SEQUENCE_CONFIG_ADDR + FLASH_SEQUENCE_CONFIG_SIZE) +#define FLASH_CHANNEL_BLOCK_SIZE ((FLASH_SEQUENCE_DATA_ADDR + FLASH_SEQUENCE_DATA_SIZE) - FLASH_CHANNEL_CONFIG_ADDR + (uint32_t)0x1000) #define OCTAVE_COUNT 4 #define DEGREE_COUNT 8 -#define DEFAULT_SEQ_LENGTH 8 -#define MAX_SEQ_LENGTH 32 -#define MAX_SEQ_LENGTH_PPQN (MAX_SEQ_LENGTH * PPQN) - #define BENDER_DAC_ZERO 32767 #define BENDER_NOISE_THRESHOLD 1000 @@ -125,14 +130,13 @@ #define MCP23017_DEGREES_ADDR 0x20 // 0100000 #define MCP23017_CTRL_ADDR 0x24 // 0100100 -enum CHAN -{ - A, - B, - C, - D, - ALL -}; + enum CHAN { + A, + B, + C, + D, + ALL + }; typedef enum CHAN CHAN; #define ISR_ID_TOGGLE_SWITCHES 0 diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index e6de096..5d3575e 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -562,26 +562,32 @@ void GlobalControl::loadCalibrationDataFromFlash() channels[chan]->sequence.setLength(getSettingsBufferValue(SETTINGS_SEQ_LENGTH, chan)); } - // load channel config - uint32_t temp = flash.read_word((void *)FLASH_CHANNEL_CONFIG_ADDR); - channels[0]->playbackMode = (TouchChannel::PlaybackMode)temp; + for (int chan = 0; chan < CHANNEL_COUNT; chan++) + { + uint32_t address_offset = FLASH_CHANNEL_BLOCK_SIZE * chan; + + // load channel config + uint32_t temp = flash.read_word((void *)(FLASH_CHANNEL_CONFIG_ADDR + address_offset)); + channels[chan]->playbackMode = (TouchChannel::PlaybackMode)temp; - // load sequence data - uint32_t sequence_config[4]; - flash.read(FLASH_SEQUENCE_CONFIG_ADDR, sequence_config, 4); - channels[0]->sequence.loadSequenceConfigData(sequence_config); + // load sequence data + uint32_t sequence_config[4]; + flash.read(FLASH_SEQUENCE_CONFIG_ADDR + address_offset, sequence_config, 4); + channels[chan]->sequence.loadSequenceConfigData(sequence_config); - if (channels[0]->sequence.containsEvents()) - { - // read a single word (4 bytes) into array, then decode it and store in seqeuence event struct - int addr = 0; - for (int i = 0; i < channels[0]->sequence.lengthPPQN; i++) + if (channels[chan]->sequence.containsEvents()) { - uint32_t event_data = flash.read_word((void *)FLASH_SEQUENCE_DATA_ADDR + addr); - channels[0]->sequence.decodeEventData(i, event_data); - addr += 4; + // read a single word (4 bytes) into array, then decode it and store in seqeuence event struct + int addr = 0; + for (int i = 0; i < channels[chan]->sequence.lengthPPQN; i++) + { + uint32_t event_data = flash.read_word((void *)(FLASH_SEQUENCE_DATA_ADDR + addr + address_offset)); + channels[chan]->sequence.decodeEventData(i, event_data); + addr += 4; + } } } + } } @@ -624,32 +630,38 @@ void GlobalControl::saveCalibrationDataToFlash() flash.erase(FLASH_CONFIG_ADDR); flash.write(FLASH_CONFIG_ADDR, SETTINGS_BUFFER, SETTINGS_BUFFER_SIZE); - // store channel config (playbackMode) - uint32_t channel_config[1]; - channel_config[0] = (uint32_t)channels[0]->playbackMode; - flash.write(FLASH_CHANNEL_CONFIG_ADDR, channel_config, 1); - - // store sequence config for each channel - uint32_t sequence_config[4]; - channels[0]->sequence.storeSequenceConfigData(sequence_config); - flash.write(FLASH_SEQUENCE_CONFIG_ADDR, sequence_config, 4); - - // if a sequence exists, store that in flash as well - if (channels[0]->sequence.containsEvents()) + // save sequence data to flash + for (int chan = 0; chan < CHANNEL_COUNT; chan++) { - // Max sequence size per channel = (32 bits per event / 4 bytes) * MAX_SEQ_LENGTH_PPQN = 12,288 bytes - uint16_t flashWrites = channels[0]->sequence.length; - uint32_t sequence_data[PPQN]; - // ensure no RAM overflow by writing to flash in smaller chunks - for (int x = 0; x < flashWrites; x++) + uint32_t address_offset = FLASH_CHANNEL_BLOCK_SIZE * chan; + + // store channel config (playbackMode) + uint32_t channel_config[1]; + channel_config[0] = (uint32_t)channels[chan]->playbackMode; + flash.write(FLASH_CHANNEL_CONFIG_ADDR + address_offset, channel_config, 1); + + // store sequence config for each channel + uint32_t sequence_config[4]; + channels[chan]->sequence.storeSequenceConfigData(sequence_config); + flash.write(FLASH_SEQUENCE_CONFIG_ADDR + address_offset, sequence_config, 4); + + // if a sequence exists, store that in flash as well + if (channels[chan]->sequence.containsEvents()) { - for (int i = 0; i < PPQN; i++) + // Max sequence size per channel = (32 bits per event / 4 bytes) * MAX_SEQ_LENGTH_PPQN = 12,288 bytes + uint16_t flashWrites = channels[chan]->sequence.length; + uint32_t sequence_data[PPQN]; + // ensure no RAM overflow by writing to flash in smaller chunks + for (int x = 0; x < flashWrites; x++) { - int pos = (x * PPQN) + i; - sequence_data[i] = channels[0]->sequence.encodeEventData(pos); // encode sequence data to be stored in 32-bit chunks + for (int i = 0; i < PPQN; i++) + { + int pos = (x * PPQN) + i; + sequence_data[i] = channels[chan]->sequence.encodeEventData(pos); // encode sequence data to be stored in 32-bit chunks + } + uint32_t address = (FLASH_SEQUENCE_DATA_ADDR + (x * 96 * 4)) + address_offset; + flash.write(address, sequence_data, PPQN); } - - flash.write(FLASH_SEQUENCE_DATA_ADDR + (x * 96 * 4), sequence_data, PPQN); } } From 441de18027c242530d1b3e6d309248613a6f7f5d Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 13 Mar 2023 17:03:00 -0400 Subject: [PATCH 26/44] changed total flash size for program memory --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 879c572..04a5453 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ ###################################### TARGET = ok-dev-board -FLASH_SIZE = $$((512 * 1024)) # 512 kB +FLASH_SIZE = $$((256 * 1024)) # 256 kB (Sector 6 and 7 used for config data) RAM_SIZE = $$((128 * 1024)) # 128 kB ###################################### From 52382a33b1f841c7ded56cc4d8621339d061e8d4 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 13 Mar 2023 17:03:39 -0400 Subject: [PATCH 27/44] refactored remaining channel config values to use new storage schema --- Degree/Inc/TouchChannel.h | 3 +++ Degree/Inc/main.h | 12 +++++++----- Degree/Src/GlobalControl.cpp | 36 ++++++++++++++++-------------------- Degree/Src/SuperSeq.cpp | 2 ++ Degree/Src/TouchChannel.cpp | 12 ++++++++++++ 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/Degree/Inc/TouchChannel.h b/Degree/Inc/TouchChannel.h index a2067e3..bacee0a 100644 --- a/Degree/Inc/TouchChannel.h +++ b/Degree/Inc/TouchChannel.h @@ -228,6 +228,9 @@ namespace DEGREE { uint8_t calculateRatchet(uint16_t bend); void handleRatchet(int position, uint16_t value); + void copyConfigData(uint32_t *arr); + void loadConfigData(uint32_t *arr); + void initializeCalibration(); void logPeripherals(); diff --git a/Degree/Inc/main.h b/Degree/Inc/main.h index f91effc..09ec6e1 100644 --- a/Degree/Inc/main.h +++ b/Degree/Inc/main.h @@ -34,12 +34,8 @@ #define SETTINGS_DAC_1VO FIRMWARE_VERSION_SIZE #define SETTINGS_BENDER_MIN (SETTINGS_DAC_1VO + DAC_1VO_ARR_SIZE) #define SETTINGS_BENDER_MAX (SETTINGS_BENDER_MIN + 1) -#define SETTINGS_BENDER_MODE (SETTINGS_BENDER_MAX + 1) -#define SETTINGS_PITCH_BEND_RANGE (SETTINGS_BENDER_MODE + 1) -#define SETTINGS_SEQ_LENGTH (SETTINGS_PITCH_BEND_RANGE + 1) -#define SETTINGS_QUANTIZE_AMOUNT (SETTINGS_SEQ_LENGTH + 1) -#define CALIBRATION_ARR_SIZE (SETTINGS_QUANTIZE_AMOUNT + 1) +#define CALIBRATION_ARR_SIZE (SETTINGS_BENDER_MAX + 1) #define SETTINGS_BUFFER_SIZE (CALIBRATION_ARR_SIZE * CHANNEL_COUNT) #define FLASH_CONFIG_ADDR ADDR_FLASH_SECTOR_7 @@ -49,6 +45,12 @@ #define FLASH_SEQUENCE_DATA_SIZE MAX_SEQ_LENGTH_PPQN #define FLASH_CHANNEL_CONFIG_ADDR (uint32_t)0x08060600 +#define SETTINGS_PLAYBACK_MODE_INDEX 0 +#define SETTINGS_BENDER_MIN_INDEX 1 +#define SETTINGS_BENDER_MAX_INDEX 2 +#define SETTINGS_BENDER_MODE_INDEX 3 +#define SETTINGS_PITCH_BEND_RANGE_INDEX 4 + #define FLASH_SEQUENCE_CONFIG_ADDR (FLASH_CHANNEL_CONFIG_ADDR + FLASH_CHANNEL_CONFIG_SIZE) #define FLASH_SEQUENCE_DATA_ADDR (FLASH_SEQUENCE_CONFIG_ADDR + FLASH_SEQUENCE_CONFIG_SIZE) #define FLASH_CHANNEL_BLOCK_SIZE ((FLASH_SEQUENCE_DATA_ADDR + FLASH_SEQUENCE_DATA_SIZE) - FLASH_CHANNEL_CONFIG_ADDR + (uint32_t)0x1000) diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index 5d3575e..2b37ec8 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -556,10 +556,6 @@ void GlobalControl::loadCalibrationDataFromFlash() } channels[chan]->bender->setMinBend(getSettingsBufferValue(SETTINGS_BENDER_MIN, chan)); channels[chan]->bender->setMaxBend(getSettingsBufferValue(SETTINGS_BENDER_MAX, chan)); - channels[chan]->currBenderMode = getSettingsBufferValue(SETTINGS_BENDER_MODE, chan); - channels[chan]->output.setPitchBendRange(getSettingsBufferValue(SETTINGS_PITCH_BEND_RANGE, chan)); - channels[chan]->sequence.setQuantizeAmount(static_cast(getSettingsBufferValue(SETTINGS_QUANTIZE_AMOUNT, chan))); - channels[chan]->sequence.setLength(getSettingsBufferValue(SETTINGS_SEQ_LENGTH, chan)); } for (int chan = 0; chan < CHANNEL_COUNT; chan++) @@ -567,8 +563,9 @@ void GlobalControl::loadCalibrationDataFromFlash() uint32_t address_offset = FLASH_CHANNEL_BLOCK_SIZE * chan; // load channel config - uint32_t temp = flash.read_word((void *)(FLASH_CHANNEL_CONFIG_ADDR + address_offset)); - channels[chan]->playbackMode = (TouchChannel::PlaybackMode)temp; + uint32_t channel_config[8]; + flash.read(FLASH_CHANNEL_CONFIG_ADDR + address_offset, channel_config, 8); + channels[chan]->loadConfigData(channel_config); // load sequence data uint32_t sequence_config[4]; @@ -611,19 +608,18 @@ void GlobalControl::saveCalibrationDataToFlash() { SETTINGS_BUFFER[i] = (uint32_t)string[i]; } - + //------------------------------------------------------------ + for (int i = SETTINGS_DAC_1VO; i < DAC_1VO_ARR_SIZE + SETTINGS_DAC_1VO; i++) // dac array iterrator { setSettingsBufferValue(i, chan, channels[chan]->output.dacVoltageMap[i]); } // load max and min Bender calibration data into buffer (two 16bit chars) - setSettingsBufferValue(SETTINGS_BENDER_MIN, chan, channels[chan]->bender->adc.getInputMin()); - setSettingsBufferValue(SETTINGS_BENDER_MAX, chan, channels[chan]->bender->adc.getInputMax()); - setSettingsBufferValue(SETTINGS_BENDER_MODE, chan, channels[chan]->currBenderMode); - setSettingsBufferValue(SETTINGS_PITCH_BEND_RANGE, chan, channels[chan]->output.pbRangeIndex); - setSettingsBufferValue(SETTINGS_QUANTIZE_AMOUNT, chan, (uint16_t)channels[chan]->sequence.quantizeAmount); - setSettingsBufferValue(SETTINGS_SEQ_LENGTH, chan, channels[chan]->sequence.length); + setSettingsBufferValue(SETTINGS_BENDER_MIN, chan, channels[chan]->bender->adc.getInputMin()); // this + setSettingsBufferValue(SETTINGS_BENDER_MAX, chan, channels[chan]->bender->adc.getInputMax()); // this + + //------------------------------------------------------------ } // now load this buffer into flash memory Flash flash; @@ -635,15 +631,15 @@ void GlobalControl::saveCalibrationDataToFlash() { uint32_t address_offset = FLASH_CHANNEL_BLOCK_SIZE * chan; - // store channel config (playbackMode) - uint32_t channel_config[1]; - channel_config[0] = (uint32_t)channels[chan]->playbackMode; - flash.write(FLASH_CHANNEL_CONFIG_ADDR + address_offset, channel_config, 1); + // store channel config + uint32_t channel_config[8]; + channels[chan]->copyConfigData(channel_config); + flash.write(FLASH_CHANNEL_CONFIG_ADDR + address_offset, channel_config, 8); - // store sequence config for each channel - uint32_t sequence_config[4]; + // store sequence config + uint32_t sequence_config[8]; channels[chan]->sequence.storeSequenceConfigData(sequence_config); - flash.write(FLASH_SEQUENCE_CONFIG_ADDR + address_offset, sequence_config, 4); + flash.write(FLASH_SEQUENCE_CONFIG_ADDR + address_offset, sequence_config, 8); // if a sequence exists, store that in flash as well if (channels[chan]->sequence.containsEvents()) diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index 6b4a119..9a46cce 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -477,6 +477,7 @@ void SuperSeq::storeSequenceConfigData(uint32_t *arr) { arr[1] = this->lengthPPQN; arr[2] = this->containsBendEvents; arr[3] = this->containsTouchEvents; + arr[4] = (uint32_t)this->quantizeAmount; } void SuperSeq::loadSequenceConfigData(uint32_t *arr) @@ -485,6 +486,7 @@ void SuperSeq::loadSequenceConfigData(uint32_t *arr) this->lengthPPQN = (int)arr[1]; this->containsBendEvents = (bool)arr[2]; this->containsTouchEvents = (bool)arr[3]; + this->quantizeAmount = (enum QUANT)arr[4]; } uint8_t SuperSeq::getEventDegree(int position) diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index fabe294..d70aabe 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -1352,4 +1352,16 @@ void TouchChannel::handleQuantAmountLEDs() setDegreeLed(6, LedState::TOGGLE, false); } } +} + +void TouchChannel::copyConfigData(uint32_t *arr) { + arr[0] = (uint32_t)this->playbackMode; + arr[1] = this->currBenderMode; + arr[2] = this->output.pbRangeIndex; +} + +void TouchChannel::loadConfigData(uint32_t *arr) { + this->playbackMode = (TouchChannel::PlaybackMode)arr[0]; + this->currBenderMode = arr[1]; + this->output.setPitchBendRange(arr[2]); } \ No newline at end of file From 5bec171d3ea2b14c7e3eea179c55de927a3caf02 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sat, 18 Mar 2023 16:52:14 -0400 Subject: [PATCH 28/44] split channel calibration data and config data into two different sectors of flash --- API/Inc/Flash.h | 8 +- API/Src/Flash.cpp | 32 ++++++++ Degree/Inc/GlobalControl.h | 7 +- Degree/Inc/main.h | 32 ++++---- Degree/Src/GlobalControl.cpp | 153 ++++++++++++++++------------------- 5 files changed, 124 insertions(+), 108 deletions(-) diff --git a/API/Inc/Flash.h b/API/Inc/Flash.h index 33e49e3..b811457 100644 --- a/API/Inc/Flash.h +++ b/API/Inc/Flash.h @@ -69,6 +69,9 @@ class Flash { HAL_StatusTypeDef erase(uint32_t address); HAL_StatusTypeDef write(uint32_t address, uint32_t *data, int size); + void write(uint32_t address, uint32_t data); + + void copySector(uint32_t sourceSector, uint32_t targetSector, uint32_t size); void read(uint32_t address, uint32_t *rxBuffer, int size); uint32_t read_word(void *address); @@ -76,8 +79,9 @@ class Flash { uint32_t getSector(uint32_t Address); bool validate(uint32_t *data, int size); -private: - static Mutex _mutex; HAL_StatusTypeDef unlock(uint32_t sector); HAL_StatusTypeDef lock(); + + private: + static Mutex _mutex; }; \ No newline at end of file diff --git a/API/Src/Flash.cpp b/API/Src/Flash.cpp index 7d538af..389c4d8 100644 --- a/API/Src/Flash.cpp +++ b/API/Src/Flash.cpp @@ -133,6 +133,38 @@ HAL_StatusTypeDef Flash::write(uint32_t address, uint32_t *data, int size) return status; } +void Flash::write(uint32_t address, uint32_t data) +{ + HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, (uint64_t)data); +} + +/** + * @brief Copy contents of a sector in flash into another sector in flash. Requires no buffer + * as it copies word by word + * + * @param sourceSector sector you wish to copy + * @param targetSector sector you wish to store the copy in + * @param size how much of the source you want to copy + */ +void Flash::copySector(uint32_t sourceSector, uint32_t targetSector, uint32_t size) +{ + // Step 1: clear Sector 6 + this->erase(targetSector); + + // Step 2: copy all data from Sector 7 into Sector 6 + this->unlock(sourceSector); + uint32_t read_address = sourceSector; + uint32_t write_address = targetSector; + while (read_address < (sourceSector + size)) + { + uint32_t word = this->read_word((void *)read_address); + this->write(write_address, word); + read_address += 4; + write_address += 4; + } + this->lock(); +} + /** * @brief Read data starting at defined address and load it into a buffer * diff --git a/Degree/Inc/GlobalControl.h b/Degree/Inc/GlobalControl.h index c2e3846..d2d848a 100644 --- a/Degree/Inc/GlobalControl.h +++ b/Degree/Inc/GlobalControl.h @@ -130,9 +130,6 @@ namespace DEGREE { void deleteCalibrationDataFromFlash(); void resetCalibrationDataToDefault(); void resetCalibration1VO(int chan); - int calculatePositionInSettingsBuffer(int data_index, int channel_index); - int getSettingsBufferValue(int position, int channel); - void setSettingsBufferValue(int position, int channel, int data); void log_system_status(); @@ -180,10 +177,12 @@ namespace DEGREE { enum Gestures : uint16_t { QUANTIZE_AMOUNT = SHIFT | PB_RANGE, - CALIBRATE_BENDER = SHIFT | BEND_MODE, // SHIFT + BEND_MODE + CALIBRATE_BENDER = SHIFT | BEND_MODE, + RESET_BENDER_CAL_DATA = SHIFT | BEND_MODE | FREEZE, SETTINGS_RESET = SHIFT | FREEZE, // SHIFT + FREEZE SETTINGS_SAVE = SHIFT | RECORD, CALIBRATE_1VO = SHIFT | CMODE, + RESET_1VO_CAL_DATA = SHIFT | CMODE | FREEZE, CLEAR_SEQ_ALL = CLEAR_SEQ_BEND | CLEAR_SEQ_TOUCH, ENTER_HARDWARE_TEST = SHIFT | SEQ_LENGTH | QUANTIZE_SEQ | CMODE, LOG_SYSTEM_STATUS = SHIFT | QUANTIZE_SEQ | SEQ_LENGTH diff --git a/Degree/Inc/main.h b/Degree/Inc/main.h index 09ec6e1..3f2371f 100644 --- a/Degree/Inc/main.h +++ b/Degree/Inc/main.h @@ -28,29 +28,27 @@ #define ADC_TIM_PERIOD 2000 #define DAC_1VO_ARR_SIZE 72 -#define FIRMWARE_VERSION_SIZE 8 -#define SETTINGS_FIRMWARE_VERSION 0 -#define SETTINGS_DAC_1VO FIRMWARE_VERSION_SIZE -#define SETTINGS_BENDER_MIN (SETTINGS_DAC_1VO + DAC_1VO_ARR_SIZE) -#define SETTINGS_BENDER_MAX (SETTINGS_BENDER_MIN + 1) +#define SETTINGS_BUFFER_SIZE DAC_1VO_ARR_SIZE -#define CALIBRATION_ARR_SIZE (SETTINGS_BENDER_MAX + 1) -#define SETTINGS_BUFFER_SIZE (CALIBRATION_ARR_SIZE * CHANNEL_COUNT) +#define FLASH_ROW_SIZE 32 // 8 4-byte words -#define FLASH_CONFIG_ADDR ADDR_FLASH_SECTOR_7 +#define FLASH_CALIBRATION_ADDR ADDR_FLASH_SECTOR_6 +#define FLASH_CONFIG_ADDR ADDR_FLASH_SECTOR_7 -#define FLASH_CHANNEL_CONFIG_SIZE 32 // 32 bytes = 8 32-bit words (one row in flash) -#define FLASH_SEQUENCE_CONFIG_SIZE 32 -#define FLASH_SEQUENCE_DATA_SIZE MAX_SEQ_LENGTH_PPQN +#define FLASH_FIRMWARE_VERSION_ADDR FLASH_CALIBRATION_ADDR +#define FLASH_FIRMWARE_VERSION_SIZE (FLASH_ROW_SIZE * 2) // pad lots of space for firmware version just incase +#define FLASH_1VO_CALIBRATION_ADDR (FLASH_FIRMWARE_VERSION_ADDR + FLASH_FIRMWARE_VERSION_SIZE) +#define FLASH_1VO_CAL_SIZE (DAC_1VO_ARR_SIZE * 4) // 72 @ 4 bytes per entry +#define FLASH_BENDER_CALIBRATION_ADDR (FLASH_1VO_CALIBRATION_ADDR + FLASH_1VO_CAL_SIZE) +#define FLASH_BENDER_CALIBRATION_SIZE FLASH_ROW_SIZE +#define FLASH_CALIBRATION_BLOCK_SIZE ((FLASH_BENDER_CALIBRATION_ADDR + FLASH_BENDER_CALIBRATION_SIZE) - FLASH_CALIBRATION_ADDR) -#define FLASH_CHANNEL_CONFIG_ADDR (uint32_t)0x08060600 -#define SETTINGS_PLAYBACK_MODE_INDEX 0 -#define SETTINGS_BENDER_MIN_INDEX 1 -#define SETTINGS_BENDER_MAX_INDEX 2 -#define SETTINGS_BENDER_MODE_INDEX 3 -#define SETTINGS_PITCH_BEND_RANGE_INDEX 4 +#define FLASH_CHANNEL_CONFIG_SIZE FLASH_ROW_SIZE +#define FLASH_SEQUENCE_CONFIG_SIZE FLASH_ROW_SIZE +#define FLASH_SEQUENCE_DATA_SIZE (MAX_SEQ_LENGTH_PPQN * 4) +#define FLASH_CHANNEL_CONFIG_ADDR FLASH_CONFIG_ADDR #define FLASH_SEQUENCE_CONFIG_ADDR (FLASH_CHANNEL_CONFIG_ADDR + FLASH_CHANNEL_CONFIG_SIZE) #define FLASH_SEQUENCE_DATA_ADDR (FLASH_SEQUENCE_CONFIG_ADDR + FLASH_SEQUENCE_CONFIG_SIZE) #define FLASH_CHANNEL_BLOCK_SIZE ((FLASH_SEQUENCE_DATA_ADDR + FLASH_SEQUENCE_DATA_SIZE) - FLASH_CHANNEL_CONFIG_ADDR + (uint32_t)0x1000) diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index 2b37ec8..c59225f 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -497,12 +497,12 @@ void GlobalControl::handleButtonRelease(int pad) } } -bool validateFirmwareVersionMatch() { +bool validateFirmwareVersionMatch(uint32_t *arr) { char *string = FIRMWARE_VERSION; int string_len = strlen(string); - if (string_len > FIRMWARE_VERSION_SIZE) { - string_len = FIRMWARE_VERSION_SIZE; + if (string_len > FLASH_FIRMWARE_VERSION_SIZE) { + string_len = FLASH_FIRMWARE_VERSION_SIZE; } // if any of the values in the buffer don't match the respective values in the firmware string array @@ -510,7 +510,7 @@ bool validateFirmwareVersionMatch() { bool versionMatch; for (int i = 0; i < string_len; i++) { - if (SETTINGS_BUFFER[i] == (uint32_t)string[i]) + if (arr[i] == (uint32_t)string[i]) { versionMatch = true; } @@ -529,11 +529,11 @@ bool validateFirmwareVersionMatch() { void GlobalControl::loadCalibrationDataFromFlash() { Flash flash; - flash.read(FLASH_CONFIG_ADDR, (uint32_t *)SETTINGS_BUFFER, SETTINGS_BUFFER_SIZE); + flash.read(FLASH_FIRMWARE_VERSION_ADDR, (uint32_t *)SETTINGS_BUFFER, FLASH_FIRMWARE_VERSION_SIZE); bool configDataEmpty = flash.validate(SETTINGS_BUFFER, 4); - bool isVersionMatch = validateFirmwareVersionMatch(); + bool isVersionMatch = validateFirmwareVersionMatch(SETTINGS_BUFFER); // if no config data or firmware version mismatch, load default configuration if (configDataEmpty || !isVersionMatch) @@ -548,43 +548,53 @@ void GlobalControl::loadCalibrationDataFromFlash() else // load the data from flash { logger_log("\nChannel Settings Source: FLASH"); + // load calibration data for (int chan = 0; chan < CHANNEL_COUNT; chan++) { - for (int i = SETTINGS_DAC_1VO; i < DAC_1VO_ARR_SIZE + SETTINGS_DAC_1VO; i++) + uint32_t address_offset = FLASH_CALIBRATION_BLOCK_SIZE * chan; + flash.read(FLASH_1VO_CALIBRATION_ADDR + address_offset, SETTINGS_BUFFER, DAC_1VO_ARR_SIZE); + for (int i = 0; i < DAC_1VO_ARR_SIZE; i++) { - channels[chan]->output.dacVoltageMap[i] = (uint16_t)getSettingsBufferValue(i, chan); + channels[chan]->output.dacVoltageMap[i] = (uint16_t)SETTINGS_BUFFER[i]; } - channels[chan]->bender->setMinBend(getSettingsBufferValue(SETTINGS_BENDER_MIN, chan)); - channels[chan]->bender->setMaxBend(getSettingsBufferValue(SETTINGS_BENDER_MAX, chan)); + flash.read(FLASH_BENDER_CALIBRATION_ADDR, SETTINGS_BUFFER, 2); + channels[chan]->bender->setMinBend((uint16_t)SETTINGS_BUFFER[0]); + channels[chan]->bender->setMaxBend((uint16_t)SETTINGS_BUFFER[1]); } - for (int chan = 0; chan < CHANNEL_COUNT; chan++) + // load channel config and sequence data + configDataEmpty = flash.validate(SETTINGS_BUFFER, 8); // the first 8 words should all equal 0xFFFFFFFF + if (!configDataEmpty) // if not empty, load channel config data { - uint32_t address_offset = FLASH_CHANNEL_BLOCK_SIZE * chan; - - // load channel config - uint32_t channel_config[8]; - flash.read(FLASH_CHANNEL_CONFIG_ADDR + address_offset, channel_config, 8); - channels[chan]->loadConfigData(channel_config); - - // load sequence data - uint32_t sequence_config[4]; - flash.read(FLASH_SEQUENCE_CONFIG_ADDR + address_offset, sequence_config, 4); - channels[chan]->sequence.loadSequenceConfigData(sequence_config); - - if (channels[chan]->sequence.containsEvents()) + for (int chan = 0; chan < CHANNEL_COUNT; chan++) { - // read a single word (4 bytes) into array, then decode it and store in seqeuence event struct - int addr = 0; - for (int i = 0; i < channels[chan]->sequence.lengthPPQN; i++) + uint32_t address_offset = FLASH_CHANNEL_BLOCK_SIZE * chan; + + // load channel config + uint32_t channel_config[8]; + flash.read(FLASH_CHANNEL_CONFIG_ADDR + address_offset, channel_config, 8); + channels[chan]->loadConfigData(channel_config); + + // load sequence data + uint32_t sequence_config[4]; + flash.read(FLASH_SEQUENCE_CONFIG_ADDR + address_offset, sequence_config, 4); + channels[chan]->sequence.loadSequenceConfigData(sequence_config); + + if (channels[chan]->sequence.containsEvents()) { - uint32_t event_data = flash.read_word((void *)(FLASH_SEQUENCE_DATA_ADDR + addr + address_offset)); - channels[chan]->sequence.decodeEventData(i, event_data); - addr += 4; + // read a single word (4 bytes) into array, then decode it and store in seqeuence event struct + int addr = 0; + for (int i = 0; i < channels[chan]->sequence.lengthPPQN; i++) + { + uint32_t event_data = flash.read_word((void *)(FLASH_SEQUENCE_DATA_ADDR + addr + address_offset)); + channels[chan]->sequence.decodeEventData(i, event_data); + addr += 4; + } } } } + } } @@ -597,36 +607,41 @@ void GlobalControl::loadCalibrationDataFromFlash() void GlobalControl::saveCalibrationDataToFlash() { this->display->fill(PWM::PWM_MID, true); + + Flash flash; + flash.erase(FLASH_CALIBRATION_ADDR); + + // Step 1: copy firmware version to flash + char *string = FIRMWARE_VERSION; + int string_len = strlen(string); + uint32_t firmware_version[string_len]; + for (int i = 0; i < string_len; i++) + { + firmware_version[i] = (uint32_t)string[i]; + } + flash.write(FLASH_FIRMWARE_VERSION_ADDR, firmware_version, string_len); + + // Step 2: copy calibration data into flash int buffer_position = 0; for (int chan = 0; chan < CHANNEL_COUNT; chan++) // channel iterrator { - // copy firmware version into buffer - char *string = FIRMWARE_VERSION; - int string_len = strlen(string); - - for (int i = 0; i < string_len; i++) - { - SETTINGS_BUFFER[i] = (uint32_t)string[i]; - } - - //------------------------------------------------------------ + uint32_t address_offset = FLASH_CALIBRATION_BLOCK_SIZE * chan; - for (int i = SETTINGS_DAC_1VO; i < DAC_1VO_ARR_SIZE + SETTINGS_DAC_1VO; i++) // dac array iterrator + // 1VO calibration data + for (int i = 0; i < DAC_1VO_ARR_SIZE; i++) { - setSettingsBufferValue(i, chan, channels[chan]->output.dacVoltageMap[i]); + SETTINGS_BUFFER[i] = channels[chan]->output.dacVoltageMap[i]; } - // load max and min Bender calibration data into buffer (two 16bit chars) - setSettingsBufferValue(SETTINGS_BENDER_MIN, chan, channels[chan]->bender->adc.getInputMin()); // this - setSettingsBufferValue(SETTINGS_BENDER_MAX, chan, channels[chan]->bender->adc.getInputMax()); // this - - //------------------------------------------------------------ + flash.write(FLASH_1VO_CALIBRATION_ADDR + address_offset, SETTINGS_BUFFER, DAC_1VO_ARR_SIZE); + + // max and min Bender calibration data + SETTINGS_BUFFER[0] = channels[chan]->bender->adc.getInputMin(); + SETTINGS_BUFFER[1] = channels[chan]->bender->adc.getInputMax(); + flash.write(FLASH_BENDER_CALIBRATION_ADDR + address_offset, SETTINGS_BUFFER, 2); } - // now load this buffer into flash memory - Flash flash; - flash.erase(FLASH_CONFIG_ADDR); - flash.write(FLASH_CONFIG_ADDR, SETTINGS_BUFFER, SETTINGS_BUFFER_SIZE); // save sequence data to flash + flash.erase(FLASH_CONFIG_ADDR); for (int chan = 0; chan < CHANNEL_COUNT; chan++) { uint32_t address_offset = FLASH_CHANNEL_BLOCK_SIZE * chan; @@ -675,6 +690,7 @@ void GlobalControl::deleteCalibrationDataFromFlash() display->fill(127, true); Flash flash; + flash.erase(FLASH_CALIBRATION_ADDR); flash.erase(FLASH_CONFIG_ADDR); for (int i = 0; i < DISPLAY_COLUMN_COUNT; i++) @@ -702,40 +718,7 @@ void GlobalControl::resetCalibration1VO(int chan) { // basically just reset a channels voltage map to default and then save as usual channels[chan]->output.resetVoltageMap(); - this->saveCalibrationDataToFlash(); -} - -/** - * @brief Calibration data for all channels is stored in a single buffer, this function returns the relative - * position of a channels calibration data inside that buffer - * - * @param data_index - * @param channel_index - * @return int - */ -int GlobalControl::calculatePositionInSettingsBuffer(int data_index, int channel_index) -{ - return (data_index + CALIBRATION_ARR_SIZE * channel_index); -} - -/** - * @brief Calibration data for all channels is stored in a single buffer, this function returns the value - * at the given position of that buffer - * - * @param data_index - * @param channel_index - * @return int - */ -int GlobalControl::getSettingsBufferValue(int position, int channel) -{ - position = calculatePositionInSettingsBuffer(position, channel); - return (int)SETTINGS_BUFFER[position]; -} - -void GlobalControl::setSettingsBufferValue(int position, int channel, int data) -{ - position = calculatePositionInSettingsBuffer(position, channel); - SETTINGS_BUFFER[position] = data; + this->saveCalibrationDataToFlash(); // change this to just save calibration data } /** From 4fcf90ff5e1f732a57975f3ea8e67c08644d6537 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sat, 18 Mar 2023 17:23:50 -0400 Subject: [PATCH 29/44] channel config and calibration data is now completely independant --- Degree/Inc/GlobalControl.h | 5 ++ Degree/Src/GlobalControl.cpp | 101 +++++++++++++++++++++++++---------- 2 files changed, 78 insertions(+), 28 deletions(-) diff --git a/Degree/Inc/GlobalControl.h b/Degree/Inc/GlobalControl.h index d2d848a..a55ec0a 100644 --- a/Degree/Inc/GlobalControl.h +++ b/Degree/Inc/GlobalControl.h @@ -128,6 +128,11 @@ namespace DEGREE { void loadCalibrationDataFromFlash(); void saveCalibrationDataToFlash(); void deleteCalibrationDataFromFlash(); + + void loadChannelConfigDataFromFlash(); + void saveChannelConfigDataToFlash(); + void deleteChannelConfigDataFromFlash(); + void resetCalibrationDataToDefault(); void resetCalibration1VO(int chan); diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index c59225f..0fecfc9 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -7,6 +7,7 @@ uint32_t SETTINGS_BUFFER[SETTINGS_BUFFER_SIZE]; void GlobalControl::init() { suspend_sequencer_task(); this->loadCalibrationDataFromFlash(); + this->loadChannelConfigDataFromFlash(); display->init(); display->clear(); @@ -337,12 +338,12 @@ void GlobalControl::handleButtonPress(int pad) { this->handleChannelGesture(callback(this, &GlobalControl::resetCalibration1VO)); } else { - this->resetCalibrationDataToDefault(); + this->deleteChannelConfigDataFromFlash(); } break; case Gestures::SETTINGS_SAVE: - this->saveCalibrationDataToFlash(); + this->saveChannelConfigDataToFlash(); break; case Gestures::CALIBRATE_1VO: @@ -561,40 +562,47 @@ void GlobalControl::loadCalibrationDataFromFlash() channels[chan]->bender->setMinBend((uint16_t)SETTINGS_BUFFER[0]); channels[chan]->bender->setMaxBend((uint16_t)SETTINGS_BUFFER[1]); } + } +} + +void GlobalControl::loadChannelConfigDataFromFlash() +{ + Flash flash; + flash.read(FLASH_FIRMWARE_VERSION_ADDR, (uint32_t *)SETTINGS_BUFFER, FLASH_FIRMWARE_VERSION_SIZE); - // load channel config and sequence data - configDataEmpty = flash.validate(SETTINGS_BUFFER, 8); // the first 8 words should all equal 0xFFFFFFFF - if (!configDataEmpty) // if not empty, load channel config data + bool configDataEmpty = flash.validate(SETTINGS_BUFFER, 8); // the first 8 words should all equal 0xFFFFFFFF + + bool isVersionMatch = validateFirmwareVersionMatch(SETTINGS_BUFFER); + + // load channel config and sequence data + if (!configDataEmpty && isVersionMatch) + { + for (int chan = 0; chan < CHANNEL_COUNT; chan++) { - for (int chan = 0; chan < CHANNEL_COUNT; chan++) - { - uint32_t address_offset = FLASH_CHANNEL_BLOCK_SIZE * chan; + uint32_t address_offset = FLASH_CHANNEL_BLOCK_SIZE * chan; - // load channel config - uint32_t channel_config[8]; - flash.read(FLASH_CHANNEL_CONFIG_ADDR + address_offset, channel_config, 8); - channels[chan]->loadConfigData(channel_config); + // load channel config + uint32_t channel_config[8]; + flash.read(FLASH_CHANNEL_CONFIG_ADDR + address_offset, channel_config, 8); + channels[chan]->loadConfigData(channel_config); - // load sequence data - uint32_t sequence_config[4]; - flash.read(FLASH_SEQUENCE_CONFIG_ADDR + address_offset, sequence_config, 4); - channels[chan]->sequence.loadSequenceConfigData(sequence_config); + // load sequence data + uint32_t sequence_config[4]; + flash.read(FLASH_SEQUENCE_CONFIG_ADDR + address_offset, sequence_config, 4); + channels[chan]->sequence.loadSequenceConfigData(sequence_config); - if (channels[chan]->sequence.containsEvents()) + if (channels[chan]->sequence.containsEvents()) + { + // read a single word (4 bytes) into array, then decode it and store in seqeuence event struct + int addr = 0; + for (int i = 0; i < channels[chan]->sequence.lengthPPQN; i++) { - // read a single word (4 bytes) into array, then decode it and store in seqeuence event struct - int addr = 0; - for (int i = 0; i < channels[chan]->sequence.lengthPPQN; i++) - { - uint32_t event_data = flash.read_word((void *)(FLASH_SEQUENCE_DATA_ADDR + addr + address_offset)); - channels[chan]->sequence.decodeEventData(i, event_data); - addr += 4; - } + uint32_t event_data = flash.read_word((void *)(FLASH_SEQUENCE_DATA_ADDR + addr + address_offset)); + channels[chan]->sequence.decodeEventData(i, event_data); + addr += 4; } } } - - } } @@ -640,7 +648,21 @@ void GlobalControl::saveCalibrationDataToFlash() flash.write(FLASH_BENDER_CALIBRATION_ADDR + address_offset, SETTINGS_BUFFER, 2); } - // save sequence data to flash + // flash the grid of leds on and off for a sec then exit + this->display->flash(3, 300); + this->display->clear(); + logger_log("\nSaved Calibration Data to Flash"); +} + +/** + * @brief Save channel sequence data and configuration data to flash + * + */ +void GlobalControl::saveChannelConfigDataToFlash() +{ + this->display->fill(PWM::PWM_MID, true); + + Flash flash; flash.erase(FLASH_CONFIG_ADDR); for (int chan = 0; chan < CHANNEL_COUNT; chan++) { @@ -691,6 +713,29 @@ void GlobalControl::deleteCalibrationDataFromFlash() Flash flash; flash.erase(FLASH_CALIBRATION_ADDR); + + for (int i = 0; i < DISPLAY_COLUMN_COUNT; i++) + { + int led = DISPLAY_COLUMN_COUNT - 1 - i; // go backwards + display->setColumn(led, 30, true); + vTaskDelay(50); + } + display->setScene(SCENE::SEQUENCER); + display->redrawScene(); +} + +/** + * @brief delete / clear all channel config data in flash + * + */ +void GlobalControl::deleteChannelConfigDataFromFlash() +{ + display->setScene(SCENE::SETTINGS); + display->resetScene(); + display->enableBlink(); + display->fill(127, true); + + Flash flash; flash.erase(FLASH_CONFIG_ADDR); for (int i = 0; i < DISPLAY_COLUMN_COUNT; i++) From 0e7816616ed548b74d9ee950237823084bf86be3 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sat, 18 Mar 2023 17:56:39 -0400 Subject: [PATCH 30/44] fixed flash validate --- API/Src/Flash.cpp | 18 +++++++++++++----- Degree/Src/GlobalControl.cpp | 5 +++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/API/Src/Flash.cpp b/API/Src/Flash.cpp index 389c4d8..5719ab8 100644 --- a/API/Src/Flash.cpp +++ b/API/Src/Flash.cpp @@ -203,11 +203,19 @@ uint32_t Flash::read_word(void *address) */ bool Flash::validate(uint32_t *data, int size) { - size = size > 65000 ? 65000 : size; // protect - uint32_t validator = 0; - for (int i = 0; i < size; i++) - validator += (uint16_t)data[i]; // uint16 for overflow - return validator == size * 0xFFFF ? true : false; + bool dataIsClear = false; + for (int i = 0; i < size; i++) { + if (data[i] == 0xFFFFFFFF) + { + dataIsClear = true; + } + else + { + dataIsClear = false; + break; + } + } + return dataIsClear; } /** diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index 0fecfc9..2330659 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -568,10 +568,11 @@ void GlobalControl::loadCalibrationDataFromFlash() void GlobalControl::loadChannelConfigDataFromFlash() { Flash flash; - flash.read(FLASH_FIRMWARE_VERSION_ADDR, (uint32_t *)SETTINGS_BUFFER, FLASH_FIRMWARE_VERSION_SIZE); - bool configDataEmpty = flash.validate(SETTINGS_BUFFER, 8); // the first 8 words should all equal 0xFFFFFFFF + flash.read(FLASH_CONFIG_ADDR, (uint32_t *)SETTINGS_BUFFER, 8); + bool configDataEmpty = flash.validate(SETTINGS_BUFFER, 8); // if empty the first 8 words should all equal 0xFFFFFFFF + flash.read(FLASH_FIRMWARE_VERSION_ADDR, (uint32_t *)SETTINGS_BUFFER, FLASH_FIRMWARE_VERSION_SIZE); bool isVersionMatch = validateFirmwareVersionMatch(SETTINGS_BUFFER); // load channel config and sequence data From f2960473b2d8988cae62bad05348c8fa4b803a32 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 17 Apr 2023 18:20:53 -0400 Subject: [PATCH 31/44] Closes #202, disabled certain menu actions when record is enabled --- Degree/Src/GlobalControl.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index 2330659..f845e49 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -279,6 +279,8 @@ void GlobalControl::handleButtonPress(int pad) switch (pad) { case CMODE: + if (recordEnabled == true) break; + for (int i = 0; i < 4; i++) { if (touchPads->padIsTouched(i, currTouched)) @@ -290,6 +292,7 @@ void GlobalControl::handleButtonPress(int pad) case SHIFT: break; case FREEZE: + if (recordEnabled == true) break; freezeLED.write(HIGH); this->handleFreeze(true); break; @@ -311,6 +314,7 @@ void GlobalControl::handleButtonPress(int pad) break; case Gestures::CALIBRATE_BENDER: + if (recordEnabled == true) break; if (this->mode == CALIBRATING_BENDER) { this->saveCalibrationDataToFlash(); @@ -334,6 +338,8 @@ void GlobalControl::handleButtonPress(int pad) break; case Gestures::SETTINGS_RESET: + if (recordEnabled == true) break; + if (gestureFlag) { this->handleChannelGesture(callback(this, &GlobalControl::resetCalibration1VO)); @@ -343,10 +349,14 @@ void GlobalControl::handleButtonPress(int pad) break; case Gestures::SETTINGS_SAVE: + if (recordEnabled == true) break; + this->saveChannelConfigDataToFlash(); break; case Gestures::CALIBRATE_1VO: + if (recordEnabled == true) break; + actionExitFlag = ACTION_EXIT_STAGE_1; // set exit flag mode = ControlMode::VCO_CALIBRATION; suspend_sequencer_task(); @@ -381,6 +391,8 @@ void GlobalControl::handleButtonPress(int pad) break; case PB_RANGE: + if (recordEnabled == true) break; + for (int i = 0; i < CHANNEL_COUNT; i++) { channels[i]->setUIMode(TouchChannel::UIMode::UI_PITCH_BEND_RANGE); @@ -388,6 +400,7 @@ void GlobalControl::handleButtonPress(int pad) break; case QUANTIZE_AMOUNT: + if (recordEnabled == true) break; actionExitFlag = ACTION_EXIT_STAGE_1; mode = ControlMode::SETTING_QUANTIZE_AMOUNT; for (int i = 0; i < CHANNEL_COUNT; i++) @@ -397,6 +410,7 @@ void GlobalControl::handleButtonPress(int pad) break; case SEQ_LENGTH: + if (recordEnabled == true) break; actionExitFlag = ACTION_EXIT_STAGE_1; mode = ControlMode::SETTING_SEQUENCE_LENGTH; for (int chan = 0; chan < CHANNEL_COUNT; chan++) @@ -439,12 +453,16 @@ void GlobalControl::handleButtonRelease(int pad) switch (pad) { case FREEZE: + if (recordEnabled == true) + break; freezeLED.write(LOW); handleFreeze(false); break; case RESET: break; case PB_RANGE: + if (recordEnabled == true) + break; for (int i = 0; i < CHANNEL_COUNT; i++) { channels[i]->setUIMode(TouchChannel::UIMode::UI_PLAYBACK); From d5851315557f882e188de9472caa00bf869c2016 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 17 Apr 2023 17:38:03 -0400 Subject: [PATCH 32/44] closes #198 sequence override snapback on release --- Degree/Inc/SuperSeq.h | 1 + Degree/Src/TouchChannel.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index 2ec5667..b742a51 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -58,6 +58,7 @@ class SuperSeq { bool bendEnabled; // flag used for overriding current recorded bend with active bend bool containsTouchEvents;// flag indicating if a sequence has any touch events bool containsBendEvents; // flag indicating if a sequence has any bend events + bool snapback; // flag indicating there was a snapback event (when you want to re-trigger the last handled event) void init(); void reset(); diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index d70aabe..3779823 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -391,6 +391,9 @@ void TouchChannel::handleReleasePlaybackEvent(uint8_t pad) } else { + triggerNote(pad, currOctave, NOTE_OFF); + triggerNote(sequence.getEventDegree(sequence.prevEventPos), sequence.getEventOctave(sequence.prevEventPos), NOTE_ON); + sequence.snapback = true; sequence.enablePlayback(); } triggerNote(pad, currOctave, NOTE_OFF); @@ -1069,6 +1072,11 @@ void TouchChannel::handleSequence(int position) return; } + if (sequence.snapback) { + sequence.snapback = false; + triggerNote(sequence.getEventDegree(sequence.prevEventPos), sequence.getEventOctave(sequence.prevEventPos), NOTE_OFF); + } + // Handle Touch Events (degrees) if (sequence.containsTouchEvents) { From b1fcdf4e96e5cb55b00a65cae0890f71c4fcb8e2 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Tue, 18 Apr 2023 18:46:30 -0400 Subject: [PATCH 33/44] manual fix of merge conflict --- Degree/Inc/SuperSeq.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index 5777e39..1a8c3ed 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -140,7 +140,4 @@ class SuperSeq { void attachRecordOverflowCallback(Callback func) { recordOverflowCallback = func; } - -private: - SequenceNode events[MAX_SEQ_LENGTH_PPQN]; }; \ No newline at end of file From c170f3a8f987b0bdb14eda4df02a53baed6c231a Mon Sep 17 00:00:00 2001 From: scottc11 Date: Thu, 11 May 2023 00:00:42 +0200 Subject: [PATCH 34/44] removed uneeded class --- .vscode/c_cpp_properties.json | 3 ++- Degree/Inc/SequencerClock.h | 42 ----------------------------------- 2 files changed, 2 insertions(+), 43 deletions(-) delete mode 100644 Degree/Inc/SequencerClock.h diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index a041143..fda55a6 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -23,12 +23,13 @@ "${workspaceFolder}/Drivers/STM32F4xx_HAL_Driver/*", "${workspaceFolder}/Drivers/STM32F4xx_HAL_Driver/Inc/*", "${workspaceFolder}/Drivers/STM32F4xx_HAL_Driver/Src/*", + "${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F4xx/Include", + "${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F4xx/Source", "${workspaceFolder}/middleware/FreeRTOS/Source/*", "${workspaceFolder}/middleware/FreeRTOS/Source/portable/GCC/ARM_CM4F/*", "${workspaceFolder}/middleware/FreeRTOS/Source/portable/MemMang/*", "${workspaceFolder}/middleware/FreeRTOS/Source/CMSIS_RTOS_V2/*", "${workspaceFolder}/middleware/FreeRTOS/Source/include/*", - "${workspaceFolder}/etl/include/**", "${workspaceFolder}/ok-drivers/**", "${workspaceFolder}/System/*", "${workspaceFolder}/System/Inc/*", diff --git a/Degree/Inc/SequencerClock.h b/Degree/Inc/SequencerClock.h deleted file mode 100644 index 2389248..0000000 --- a/Degree/Inc/SequencerClock.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -/** - * A possible implemntation of TIMx could be to set some of its channels in OC (Output Compare) mode. - * - * The HAL_TIM_OC_DelayElapsedCallback() is automatically called by the HAL every time a TIMx Channel CCRx register matches the TIMx counter. - * - * I think the best approach would be to setup TIMx in slave mode, so it recieves its clocking speed from a Master Timer - * Then, you could configure each of the 4 channels of TIMx in "Output Compare Mode", such that each channel triggers an interupt - * at a division of TIMx CNT register. - * - * Ex. - * - * The TIMx CNT overflow is your BPM. - * TIMx->CCR1 == TIMx->ARR / 3 // triplets? - * TIMx->CCR2 == TIMx->ARR / 4 // 4 - * TIMx->CCR3 == TIMx->ARR / 8 // 8th - * TIMx->CCR4 == TIMx->ARR / 16 // 16th - * - * You would need to update each channels CCRx value everytime an OC interupt fires for a given channel. To do this, - * - * - * However, what is the difference of doing this vs. just using a variety of counters inside the ARR overflow interupt? - * - * I think the benefit you would get is you are doing addition instead of division for every tick. - * Because with the latter, you need to divide the step length down into sb divisions every time an - * IC event takes place, where as if you used OC mode, the subdivisions of the main step would be - * hard coded? But then you are doing an addition everytime a subtick occurs... The work around to - * this would be to "using the DMA mode and a pre-initialized vector, eventually stored in the flash - * memory by using the const modifier" - Mastering STM32: Output Compare Mode -*/ - -/** using TIM1, create a class which holds all timing data required to keep a Sequencer class in - * sync with an external clock signal -*/ - -#include "main.h" - -class SequencerClock { -public: - SequencerClock(){}; -}; \ No newline at end of file From 0a0779505e987318899ec4fdd40572337d3a60fb Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sat, 20 May 2023 11:06:11 -0400 Subject: [PATCH 35/44] Revert "closes #198 sequence override snapback on release" This reverts commit d5851315557f882e188de9472caa00bf869c2016. --- Degree/Inc/SuperSeq.h | 1 - Degree/Src/TouchChannel.cpp | 8 -------- 2 files changed, 9 deletions(-) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index 1a8c3ed..e82d283 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -70,7 +70,6 @@ class SuperSeq { static bool recordArmed; // when true, recording will be enabled the next time bar overflows static bool recordDisarmed; // when true, recording will be disabled the next time bar overflows - bool snapback; // flag indicating there was a snapback event (when you want to re-trigger the last handled event) void init(); void reset(); diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 08145b1..0301479 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -392,9 +392,6 @@ void TouchChannel::handleReleasePlaybackEvent(uint8_t pad) } else { - triggerNote(pad, currOctave, NOTE_OFF); - triggerNote(sequence.getEventDegree(sequence.prevEventPos), sequence.getEventOctave(sequence.prevEventPos), NOTE_ON); - sequence.snapback = true; sequence.enablePlayback(); } triggerNote(pad, currOctave, NOTE_OFF); @@ -1072,11 +1069,6 @@ void TouchChannel::handleSequence(int position) return; } - if (sequence.snapback) { - sequence.snapback = false; - triggerNote(sequence.getEventDegree(sequence.prevEventPos), sequence.getEventOctave(sequence.prevEventPos), NOTE_OFF); - } - // Handle Touch Events (degrees) if (sequence.containsTouchEvents) { From 9eb588cfb3bbd26c31868a7f2f37447049bdb2ec Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sun, 4 Jun 2023 09:23:16 -0400 Subject: [PATCH 36/44] fixed bug in IC mode so steps get handled more accurately. --- .vscode/c_cpp_properties.json | 3 ++- API/Inc/SuperClock.h | 1 + API/Src/SuperClock.cpp | 27 +++++++++++++++++++-------- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index fda55a6..1b85473 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -4,7 +4,8 @@ "name": "Mac", "compilerPath": "/opt/homebrew/bin/arm-none-eabi-gcc", "defines": [ - "USE_HAL_DRIVER" + "USE_HAL_DRIVER", + "STM32F446xx" ], "includePath": [ "${workspaceFolder}/API/*", diff --git a/API/Inc/SuperClock.h b/API/Inc/SuperClock.h index 68bed73..1e90830 100644 --- a/API/Inc/SuperClock.h +++ b/API/Inc/SuperClock.h @@ -63,6 +63,7 @@ class SuperClock { void start(); void reset(); void setStepsPerBar(int steps); + void handleStep(); void setPulseFrequency(uint32_t ticks); uint16_t convertADCReadToTicks(uint16_t min, uint16_t max, uint16_t value); diff --git a/API/Src/SuperClock.cpp b/API/Src/SuperClock.cpp index 4d6adcc..be4520a 100644 --- a/API/Src/SuperClock.cpp +++ b/API/Src/SuperClock.cpp @@ -161,8 +161,10 @@ void SuperClock::handleInputCaptureCallback() { // almost always, there will need to be at least 1 pulse not yet executed prior to an input capture, // so you must execute all remaining until - if (pulse < PPQN) + // would doing the while loop in here achieve the same result? While also + if (pulse < PPQN - 1) { + handleStep(); if (resetCallback) { resetCallback(pulse); @@ -215,20 +217,29 @@ void SuperClock::handleOverflowCallback() } else { if (externalInputMode) { - __HAL_TIM_DISABLE(&htim4); // halt TIM4 // external input will reset pulse to 0 and resume TIM4 in input capture callback + __HAL_TIM_DISABLE(&htim4); // halt TIM4 + handleStep(); } else { pulse = 0; - if (step < stepsPerBar - 1) { - step++; - } else { - step = 0; - if (barResetCallback) barResetCallback(); - } + handleStep(); } } } +void SuperClock::handleStep() { + if (step < stepsPerBar - 1) + { + step++; + } + else + { + step = 0; + if (barResetCallback) + barResetCallback(); + } +} + void SuperClock::attachInputCaptureCallback(Callback func) { input_capture_callback = func; From 089a4de7e2e4ae4effee6228d0588c563a7e7137 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sun, 4 Jun 2023 09:25:40 -0400 Subject: [PATCH 37/44] fixed bug to better set lenght when loading sequence data --- Degree/Src/SuperSeq.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Degree/Src/SuperSeq.cpp b/Degree/Src/SuperSeq.cpp index babbb41..3d51743 100644 --- a/Degree/Src/SuperSeq.cpp +++ b/Degree/Src/SuperSeq.cpp @@ -482,8 +482,7 @@ void SuperSeq::storeSequenceConfigData(uint32_t *arr) { void SuperSeq::loadSequenceConfigData(uint32_t *arr) { - this->length = (int)arr[0]; - this->lengthPPQN = (int)arr[1]; + this->setLength((int)arr[0]); this->containsBendEvents = (bool)arr[2]; this->containsTouchEvents = (bool)arr[3]; this->quantizeAmount = (enum QUANT)arr[4]; From c630fb7852ee27c382a09ab347523c907138be0d Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sun, 4 Jun 2023 09:36:51 -0400 Subject: [PATCH 38/44] Revert "closes #198 sequence override snapback on release" This reverts commit d5851315557f882e188de9472caa00bf869c2016. --- Degree/Inc/SuperSeq.h | 1 - Degree/Src/TouchChannel.cpp | 8 -------- 2 files changed, 9 deletions(-) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index b742a51..2ec5667 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -58,7 +58,6 @@ class SuperSeq { bool bendEnabled; // flag used for overriding current recorded bend with active bend bool containsTouchEvents;// flag indicating if a sequence has any touch events bool containsBendEvents; // flag indicating if a sequence has any bend events - bool snapback; // flag indicating there was a snapback event (when you want to re-trigger the last handled event) void init(); void reset(); diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 3779823..d70aabe 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -391,9 +391,6 @@ void TouchChannel::handleReleasePlaybackEvent(uint8_t pad) } else { - triggerNote(pad, currOctave, NOTE_OFF); - triggerNote(sequence.getEventDegree(sequence.prevEventPos), sequence.getEventOctave(sequence.prevEventPos), NOTE_ON); - sequence.snapback = true; sequence.enablePlayback(); } triggerNote(pad, currOctave, NOTE_OFF); @@ -1072,11 +1069,6 @@ void TouchChannel::handleSequence(int position) return; } - if (sequence.snapback) { - sequence.snapback = false; - triggerNote(sequence.getEventDegree(sequence.prevEventPos), sequence.getEventOctave(sequence.prevEventPos), NOTE_OFF); - } - // Handle Touch Events (degrees) if (sequence.containsTouchEvents) { From fbfbf4f06427f14a862ea73e09f25ff91538f039 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Wed, 7 Jun 2023 07:31:58 -0400 Subject: [PATCH 39/44] fixed + tweaked 1VO calibration routine, added back ADC external queue --- Degree/Inc/AnalogHandle.h | 4 +-- Degree/Inc/MultiChanADC.h | 2 -- Degree/Src/AnalogHandle.cpp | 8 ++++- Degree/Tasks/Inc/task_calibration.h | 9 ----- Degree/Tasks/Inc/task_handles.h | 3 +- Degree/Tasks/Src/task_calibration.cpp | 51 +++++++++++++-------------- Degree/Tasks/Src/task_controller.cpp | 5 +++ 7 files changed, 40 insertions(+), 42 deletions(-) diff --git a/Degree/Inc/AnalogHandle.h b/Degree/Inc/AnalogHandle.h index 4cc1afc..4565214 100644 --- a/Degree/Inc/AnalogHandle.h +++ b/Degree/Inc/AnalogHandle.h @@ -1,10 +1,10 @@ #pragma once #include "main.h" +#include "task_handles.h" #include "Callback.h" #include "filters.h" #include "okSemaphore.h" -#include "okQueue.h" #include "logger.h" #define ADC_SAMPLE_COUNTER_LIMIT 2000 @@ -19,9 +19,9 @@ class AnalogHandle { AnalogHandle(PinName pin); int index; - okSemaphore denoisingSemaphore; okSemaphore sampleSemaphore; + bool queueSample; // whether or not to send the processed sample to an external queue uint16_t idleNoiseThreshold; // how much noise an idle input signal contains uint16_t avgValueWhenIdle; // where the sensor sits when "idle" (only relevant for sensors) uint16_t noiseCeiling; // highest read noise value when idle diff --git a/Degree/Inc/MultiChanADC.h b/Degree/Inc/MultiChanADC.h index 844f70e..5e8c0e9 100644 --- a/Degree/Inc/MultiChanADC.h +++ b/Degree/Inc/MultiChanADC.h @@ -23,8 +23,6 @@ extern TIM_HandleTypeDef htim3; #define ADC_SAMPLE_RATE_HZ 2000 -extern TIM_HandleTypeDef htim3; - void multi_chan_adc_init(); void multi_chan_adc_start(); diff --git a/Degree/Src/AnalogHandle.cpp b/Degree/Src/AnalogHandle.cpp index f230a9d..373b120 100644 --- a/Degree/Src/AnalogHandle.cpp +++ b/Degree/Src/AnalogHandle.cpp @@ -1,5 +1,6 @@ #include "AnalogHandle.h" +QueueHandle_t qh_adc_sample_ready; SemaphoreHandle_t AnalogHandle::semaphore; AnalogHandle *AnalogHandle::_instances[ADC_DMA_BUFF_SIZE] = {0}; @@ -125,8 +126,11 @@ void AnalogHandle::sampleReadyCallback(uint16_t sample) // maybe calulate median here sampleSemaphore.give(); } - } + if (queueSample) { + xQueueSend(qh_adc_sample_ready, &currValue, portMAX_DELAY); + } + } @@ -150,6 +154,7 @@ void AnalogHandle::RouteConversionCompleteCallback() // static */ void AnalogHandle::sampleReadyTask(void *params) { logger_log_task_watermark(); + qh_adc_sample_ready = xQueueCreate(16, sizeof(uint16_t)); while (1) { xSemaphoreTake(AnalogHandle::semaphore, portMAX_DELAY); @@ -174,6 +179,7 @@ void AnalogHandle::log_noise_threshold_to_console(char const *source_id) } void AnalogHandle::log_min_max(char const *source_id) { + logger_log("\n"); logger_log(source_id); logger_log(" Signal Max: "); logger_log(this->inputMax); diff --git a/Degree/Tasks/Inc/task_calibration.h b/Degree/Tasks/Inc/task_calibration.h index 818d9bd..91042e4 100644 --- a/Degree/Tasks/Inc/task_calibration.h +++ b/Degree/Tasks/Inc/task_calibration.h @@ -9,14 +9,5 @@ #include "ArrayMethods.h" #include "PitchFrequencies.h" -#define CALIBRATION_DAC_SETTLE_TIME 2 // how long in ticks the task should delay to let the DAC settle (before sampling the frequency again) -#define CALIBRATION_SAMPLE_RATE_HZ 16000 // set ADC timer overflow frequency to 16000hz (twice the freq of B8) -#define DEFAULT_VOLTAGE_ADJMNT 400 -#define MAX_CALIB_ATTEMPTS 20 // how many times the calibrator will try and match the given frequency -#define MAX_FREQ_SAMPLES 25 // how many frequency calculations we want to use to obtain our average frequency prediction of the input. The higher the number, the more accurate the result -#define ZERO_CROSS_THRESHOLD 2000 // for handling hysterisis at zero crossing point NOTE: If set around 500 freq readings become very unstable - -void initializeCalibration(); void taskObtainSignalFrequency(void *params); -float calculateAverageFreq(); void taskCalibrate(void *params); \ No newline at end of file diff --git a/Degree/Tasks/Inc/task_handles.h b/Degree/Tasks/Inc/task_handles.h index 5120dc2..bf4d369 100644 --- a/Degree/Tasks/Inc/task_handles.h +++ b/Degree/Tasks/Inc/task_handles.h @@ -3,7 +3,7 @@ #include "main.h" #include "BitwiseMethods.h" -#define TUNING_TOLERANCE 0.5f // tolerable frequency tuning difference +#define TUNING_TOLERANCE 0.4f // tolerable frequency tuning difference extern TaskHandle_t main_task_handle; extern TaskHandle_t thStartCalibration; @@ -15,6 +15,7 @@ extern TaskHandle_t thInterruptHandler; extern QueueHandle_t qhInterruptQueue; extern QueueHandle_t tuner_queue; +extern QueueHandle_t qh_adc_sample_ready; enum CTRL_ACTION { diff --git a/Degree/Tasks/Src/task_calibration.cpp b/Degree/Tasks/Src/task_calibration.cpp index fffa8bc..b72e8d3 100644 --- a/Degree/Tasks/Src/task_calibration.cpp +++ b/Degree/Tasks/Src/task_calibration.cpp @@ -25,28 +25,37 @@ void taskObtainSignalFrequency(void *params) float vcoFrequency = 0; // the calculated frequency sample int frequencySampleCounter = 0; // index for storing new frequency sample into freqSamples array float avgFrequencySum = 0; // the sum of all new frequency samples (for calculating a running average afterwards) - - channel->adc.disableFilter(); // must not filter ADC input + int CALIBRATION_SAMPLE_RATE_HZ = 16000; // set ADC timer overflow frequency to 16000hz (twice the freq of B8) + int MAX_FREQ_SAMPLES = 25; // how many frequency calculations we want to use to obtain our average frequency prediction of the input. The higher the number, the more accurate the result + int ZERO_CROSS_THRESHOLD = 1000; // for handling hysterisis at zero crossing point NOTE: If set around 500 freq readings become very unstable + + channel->adc.disableFilter(); // must not filter ADC input + multi_chan_adc_set_sample_rate(&hadc1, &htim3, CALIBRATION_SAMPLE_RATE_HZ); // set ADC timer overflow frequency to 16000hz (twice the freq of B8) // sample peak to peak; okSemaphore *sem = channel->adc.beginMinMaxSampling(2000); // sampling time should be longer than the lowest possible note frequency sem->wait(); channel->adc.log_min_max("CV"); + channel->adc.queueSample = true; signalZeroCrossing = channel->adc.getInputMedian(); sem_obtain_freq = xSemaphoreCreateBinary(); sem_calibrate = xSemaphoreCreateBinary(); + uint16_t sample = 0; + xSemaphoreGive(sem_obtain_freq); // i think you can remove this semaphore now logger_log_task_watermark(); while (1) { - xSemaphoreTake(sem_obtain_freq, portMAX_DELAY); - + xSemaphoreTake(sem_obtain_freq, portMAX_DELAY); // wait for tuner task to give the semaphore + // ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // wait for a notification from the AnalogHandle::sampleReadyCallback + xQueueReceive(qh_adc_sample_ready, &sample, portMAX_DELAY); + uint16_t curr_adc_sample = channel->adc.read_u16(); // obtain the sample from DMA buffer // NEGATIVE SLOPE @@ -97,14 +106,14 @@ void taskObtainSignalFrequency(void *params) void taskCalibrate(void *params) { TouchChannel *channel = (TouchChannel *)params; - + uint16_t CALIBRATION_DAC_SETTLE_TIME = 2; // how long in ticks the task should delay to let the DAC settle (before sampling the frequency again) + uint16_t MAX_CALIB_ATTEMPTS = 10; // how many times the calibrator will try and match the given frequency + uint16_t DEFAULT_VOLTAGE_ADJMNT = 200; // how much to adjust the DAC output everytime we overshoot / undershoot the target frequency uint16_t dacAdjustment = DEFAULT_VOLTAGE_ADJMNT; int calibrationAttemps = 0; // for limiting the number of calibration attempts float currAvgFreq; float prevAvgFreq; uint16_t newDacValue; - float targetFreq; // where we want the frequency to get to - int targetFreqIndex; // current voltage map iteration + initial pitch index int iteration = 0; // the current interation in the voltage map array int initialPitchIndex = 0; // the index of the initial target frequency in PITCH_FREQ_ARR bool initialized = false; // @@ -142,8 +151,8 @@ void taskCalibrate(void *params) } // obtain the target frequency - targetFreqIndex = initialPitchIndex + iteration; - targetFreq = PITCH_FREQ_ARR[targetFreqIndex]; + int targetFreqIndex = initialPitchIndex + iteration; // current voltage map iteration + initial pitch index + float targetFreq = PITCH_FREQ_ARR[targetFreqIndex]; // where we want the frequency to get to // if currAvgFreq is close enough to targetFreq, or max cal attempts has been reached, break out of while loop and move to the next 'note' if ((currAvgFreq <= targetFreq + TUNING_TOLERANCE && currAvgFreq >= targetFreq - TUNING_TOLERANCE) || calibrationAttemps > MAX_CALIB_ATTEMPTS) @@ -177,39 +186,27 @@ void taskCalibrate(void *params) iteration++; newDacValue = channel->output.dacVoltageMap[iteration]; // prepare for the next iteration vTaskDelay(CALIBRATION_DAC_SETTLE_TIME + targetFreqIndex > 69 ? 10 : 0); // wait for DAC to settle - xSemaphoreGive(sem_obtain_freq); + xSemaphoreGive(sem_obtain_freq); // give semaphore back to tuner task } else { // every time currAvgFreq over/undershoots the desired frequency, decrement the 'dacAdjustment' value by half. if (currAvgFreq > targetFreq + TUNING_TOLERANCE) // overshot target freq { - if (prevAvgFreq < targetFreq - TUNING_TOLERANCE) - { - dacAdjustment = (dacAdjustment / 2) + 1; // + 1 so it never becomes zero - } - if (newDacValue - dacAdjustment > newDacValue) // catch overflow past 0 - { - newDacValue -= dacAdjustment; - } + dacAdjustment = (dacAdjustment / 2) + 1; // + 1 so it never becomes zero + newDacValue -= dacAdjustment; } else if (currAvgFreq < targetFreq - TUNING_TOLERANCE) // undershot target freq { - if (prevAvgFreq > targetFreq + TUNING_TOLERANCE) - { - dacAdjustment = (dacAdjustment / 2) + 1; // so it never becomes zero - } - if (newDacValue + dacAdjustment < newDacValue) // catch overflow above 65535 - { - newDacValue += dacAdjustment; - } + dacAdjustment = (dacAdjustment / 2) + 1; // so it never becomes zero + newDacValue += dacAdjustment; } prevAvgFreq = currAvgFreq; calibrationAttemps++; channel->output.dac->write(channel->output.dacChannel, newDacValue); vTaskDelay(CALIBRATION_DAC_SETTLE_TIME + targetFreqIndex > 69 ? 10 : 0); // wait for DAC to settle - xSemaphoreGive(sem_obtain_freq); + xSemaphoreGive(sem_obtain_freq); // give semaphore back to tuner task } } diff --git a/Degree/Tasks/Src/task_controller.cpp b/Degree/Tasks/Src/task_controller.cpp index 4dc2cc4..409b4f8 100644 --- a/Degree/Tasks/Src/task_controller.cpp +++ b/Degree/Tasks/Src/task_controller.cpp @@ -24,6 +24,11 @@ void task_controller(void *params) case CTRL_ACTION::EXIT_1VO_CALIBRATION: vTaskDelete(thCalibrate); vTaskDelete(thStartCalibration); + for (int i = 0; i < 4; i++) + { + controller->channels[i]->adc.queueSample = false; + } + // offload all this shit to a task with a much higher stack size multi_chan_adc_set_sample_rate(&hadc1, &htim3, ADC_SAMPLE_RATE_HZ); From 5866cff2c48d5ea4844944f70fcd6be9cdb58946 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Thu, 8 Jun 2023 19:41:34 -0400 Subject: [PATCH 40/44] closes #207 , select pad synced to channels now --- Degree/Inc/TouchChannel.h | 6 ++- Degree/Src/GlobalControl.cpp | 3 +- Degree/Src/TouchChannel.cpp | 54 +++++++++++++--------- Degree/Tasks/Inc/task_sequence_handler.h | 1 + Degree/Tasks/Src/task_sequence_handler.cpp | 16 +++++++ 5 files changed, 56 insertions(+), 24 deletions(-) diff --git a/Degree/Inc/TouchChannel.h b/Degree/Inc/TouchChannel.h index bacee0a..0f2f542 100644 --- a/Degree/Inc/TouchChannel.h +++ b/Degree/Inc/TouchChannel.h @@ -133,6 +133,9 @@ namespace DEGREE { bool led_state[16]; + bool selectPadIsTouched; // each channel has a select pad in the control section + bool armSelectPadRelease; // if an event happens while selectPadIsTouched == true, should the pad be released, we want to treat the next event as if it is still touched + uint8_t currRatchetRate; uint8_t prevRatchetRate; @@ -154,8 +157,9 @@ namespace DEGREE { int freezeStep; // the position of sequence when freeze was enabled // Quantiser members + bool overrideQuantizer; // will temporarily disable the CV input uint8_t activeDegrees; // 8 bits to determine which scale degrees are presently active/inactive (active = 1, inactive= 0) - uint8_t activeOctaves; // 4-bits to represent the current octaves external CV will get mapped to (active = 1, inactive= 0) + uint8_t activeOctaves; // 4-bits to represent the current octaves external CV will get mapped to (active = 1, inactive= 0) int numActiveDegrees; // number of degrees which are active (to quantize voltage input) int numActiveOctaves; // number of active octaves for mapping CV to int activeDegreeLimit; // the max number of degrees allowed to be enabled at one time. diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index f845e49..30b8f4d 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -220,7 +220,8 @@ void GlobalControl::pollTouchPads() { #else currTouched = touchPads->touched(); #endif - + // queue select pad + dispatch_sequencer_event(CHAN::ALL, SEQ::HANDLE_SELECT_PAD, 0); if (currTouched == 0x00) { gestureFlag = false; } else { diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index d70aabe..effdc12 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -318,7 +318,12 @@ void TouchChannel::handleTouchPlaybackEvent(uint8_t pad) triggerNote(pad, currOctave, NOTE_ON); break; case QUANTIZER: - setActiveDegrees(bitwise_write_bit(activeDegrees, pad, !bitwise_read_bit(activeDegrees, pad))); + if (selectPadIsTouched) { + overrideQuantizer = true; + triggerNote(pad, currOctave, NOTE_ON); + } else { + setActiveDegrees(bitwise_write_bit(activeDegrees, pad, !bitwise_read_bit(activeDegrees, pad))); + } break; case QUANTIZER_LOOP: // every touch detected, take a snapshot of all active degree values and apply them to the sequence @@ -396,6 +401,11 @@ void TouchChannel::handleReleasePlaybackEvent(uint8_t pad) triggerNote(pad, currOctave, NOTE_OFF); break; case QUANTIZER: + if (touchPads->padIsTouched() == false) { + overrideQuantizer = false; + armSelectPadRelease = false; + handleCVInput(); + } break; case QUANTIZER_LOOP: sequence.disableOverdub(); @@ -550,7 +560,7 @@ void TouchChannel::setLED(int io_pin, LedState state, bool isPlaybackEvent) _leds->setOnTime(io_pin, 0); break; case DIM_LOW: - _leds->setPWM(io_pin, 10); + _leds->setPWM(io_pin, 15); break; case DIM_MED: _leds->setPWM(io_pin, 127); @@ -941,27 +951,27 @@ void TouchChannel::handleCVInput() // prevent duplicate triggering of that same degree / octave if (currDegree != activeDegreeValues[i].noteIndex || currOctave != octave) // NOTE: currOctave used to be prevOctave 🤷‍♂️ { - triggerNote(currDegree, prevOctave, NOTE_OFF); // set previous triggered degree - - // re-DIM previously degree LED - if (bitwise_read_bit(activeDegrees, prevDegree)) - { - setDegreeLed(currDegree, BLINK_OFF, true); - setDegreeLed(currDegree, DIM_LOW, true); - } - - // trigger the new degree, and set its LED to blink - triggerNote(activeDegreeValues[i].noteIndex, octave, NOTE_ON); - setDegreeLed(activeDegreeValues[i].noteIndex, LedState::BLINK_ON, true); - - // re-DIM previous Octave LED - if (bitwise_read_bit(activeOctaves, prevOctave)) - { - setOctaveLed(prevOctave, LedState::BLINK_OFF, true); - setOctaveLed(prevOctave, LedState::DIM_LOW, true); + if (!overrideQuantizer) { + triggerNote(currDegree, prevOctave, NOTE_OFF); // set previous triggered degree + + // re-DIM previously degree LED + if (bitwise_read_bit(activeDegrees, prevDegree)) + { + setDegreeLed(currDegree, DIM_LOW, true); + } + + // trigger the new degree, and set its LED to blink + triggerNote(activeDegreeValues[i].noteIndex, octave, NOTE_ON); + setDegreeLed(activeDegreeValues[i].noteIndex, LedState::DIM_MED, true); + + // re-DIM previous Octave LED + if (bitwise_read_bit(activeOctaves, prevOctave)) + { + setOctaveLed(prevOctave, LedState::DIM_LOW, true); + } + // BLINK active quantized octave + setOctaveLed(octave, LedState::DIM_MED, true); } - // BLINK active quantized octave - setOctaveLed(octave, LedState::BLINK_ON, true); } break; // break from loop as soon as we can } diff --git a/Degree/Tasks/Inc/task_sequence_handler.h b/Degree/Tasks/Inc/task_sequence_handler.h index 2a8f465..e8ced72 100644 --- a/Degree/Tasks/Inc/task_sequence_handler.h +++ b/Degree/Tasks/Inc/task_sequence_handler.h @@ -20,6 +20,7 @@ enum class SEQ QUANTIZE, CORRECT, HANDLE_TOUCH, + HANDLE_SELECT_PAD, HANDLE_DEGREE, DISPLAY }; diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index 3d839bd..9f72ec3 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -42,6 +42,22 @@ void task_sequence_handler(void *params) ctrl->channels[channel]->touchPads->handleTouch(); // this will trigger either onTouch() or onRelease() break; + case SEQ::HANDLE_SELECT_PAD: + for (int i = 0; i < CHANNEL_COUNT; i++) + { + if (ctrl->touchPads->padIsTouched(i, ctrl->currTouched)) + { + // you might want to put this into the sequence queue to avoid race conditions + ctrl->channels[i]->selectPadIsTouched = true; + } + else + { + ctrl->channels[i]->selectPadIsTouched = false; + ctrl->channels[i]->armSelectPadRelease = true; + } + } + break; + case SEQ::HANDLE_DEGREE: for (int i = 0; i < CHANNEL_COUNT; i++) ctrl->channels[i]->updateDegrees(); From 249e624151bfe78721fbc026a799e914a6015f50 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Thu, 8 Jun 2023 21:58:56 -0400 Subject: [PATCH 41/44] fixed CV input quantization hysteresis --- Degree/Inc/TouchChannel.h | 2 ++ Degree/Src/TouchChannel.cpp | 70 ++++++++++++++++++++----------------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/Degree/Inc/TouchChannel.h b/Degree/Inc/TouchChannel.h index 0f2f542..afd930e 100644 --- a/Degree/Inc/TouchChannel.h +++ b/Degree/Inc/TouchChannel.h @@ -158,6 +158,8 @@ namespace DEGREE { // Quantiser members bool overrideQuantizer; // will temporarily disable the CV input + uint16_t currCV; + uint16_t prevCV; uint8_t activeDegrees; // 8 bits to determine which scale degrees are presently active/inactive (active = 1, inactive= 0) uint8_t activeOctaves; // 4-bits to represent the current octaves external CV will get mapped to (active = 1, inactive= 0) int numActiveDegrees; // number of degrees which are active (to quantize voltage input) diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index effdc12..1f5d3d4 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -8,7 +8,7 @@ void TouchChannel::init() output.init(); // must init this first (for the dac) - adc.setFilter(0.1); + adc.setFilter(0.05); // initialize LED Driver _leds->init(); @@ -918,7 +918,7 @@ void TouchChannel::setActiveDegreeLimit(int value) void TouchChannel::handleCVInput() { // NOTE: CV voltage input is inverted, so everything needs to be flipped to make more sense - uint16_t cvInput = CV_MAX - adc.read_u16(); + currCV = CV_MAX - adc.read_u16(); // We only want trigger events in quantizer mode, so if the gate gets set HIGH, make sure to set it back to low the very next tick if (gateState == HIGH) @@ -930,51 +930,57 @@ void TouchChannel::handleCVInput() // determin which octave the CV value will get mapped to for (int i = 0; i < numActiveOctaves; i++) { - if (cvInput < activeOctaveValues[i].threshold) + if (currCV < activeOctaveValues[i].threshold) { octave = activeOctaveValues[i].octave; - refinedValue = i == 0 ? cvInput : cvInput - activeOctaveValues[i - 1].threshold; // remap adc value to a number between 0 and octaveThreshold + refinedValue = i == 0 ? currCV : currCV - activeOctaveValues[i - 1].threshold; // remap adc value to a number between 0 and octaveThreshold break; } } - // BEFORE THIS BLOCK: - // check if the newly mapped note and octave values are different then before + // Schmitt trigger thresholds + int thresholdHigh = prevCV + 150; // Adjust the high threshold as needed + int thresholdLow = prevCV - 150; // Adjust the low threshold as needed - // latch incoming ADC value to DAC value - for (int i = 0; i < numActiveDegrees; i++) + if (currCV < thresholdLow || currCV > thresholdHigh) { - // if the calculated value is less than threshold - if (refinedValue < activeDegreeValues[i].threshold) + // latch incoming ADC value to DAC value + for (int i = 0; i < numActiveDegrees; i++) { - // prevent duplicate triggering of that same degree / octave - if (currDegree != activeDegreeValues[i].noteIndex || currOctave != octave) // NOTE: currOctave used to be prevOctave 🤷‍♂️ + // if the calculated value is less than threshold + if (refinedValue < activeDegreeValues[i].threshold) { - if (!overrideQuantizer) { - triggerNote(currDegree, prevOctave, NOTE_OFF); // set previous triggered degree - - // re-DIM previously degree LED - if (bitwise_read_bit(activeDegrees, prevDegree)) + // prevent duplicate triggering of that same degree / octave + if (currDegree != activeDegreeValues[i].noteIndex || currOctave != octave) // NOTE: currOctave used to be prevOctave 🤷‍♂️ { - setDegreeLed(currDegree, DIM_LOW, true); + if (!overrideQuantizer) + { + triggerNote(currDegree, prevOctave, NOTE_OFF); // set previous triggered degree + + // re-DIM previously degree LED + if (bitwise_read_bit(activeDegrees, prevDegree)) + { + setDegreeLed(currDegree, DIM_LOW, true); + } + + // trigger the new degree, and set its LED to blink + triggerNote(activeDegreeValues[i].noteIndex, octave, NOTE_ON); + setDegreeLed(activeDegreeValues[i].noteIndex, LedState::DIM_MED, true); + + // re-DIM previous Octave LED + if (bitwise_read_bit(activeOctaves, prevOctave)) + { + setOctaveLed(prevOctave, LedState::DIM_LOW, true); + } + // BLINK active quantized octave + setOctaveLed(octave, LedState::DIM_MED, true); + } } - - // trigger the new degree, and set its LED to blink - triggerNote(activeDegreeValues[i].noteIndex, octave, NOTE_ON); - setDegreeLed(activeDegreeValues[i].noteIndex, LedState::DIM_MED, true); - - // re-DIM previous Octave LED - if (bitwise_read_bit(activeOctaves, prevOctave)) - { - setOctaveLed(prevOctave, LedState::DIM_LOW, true); - } - // BLINK active quantized octave - setOctaveLed(octave, LedState::DIM_MED, true); - } + break; // break from loop as soon as we can } - break; // break from loop as soon as we can } + prevCV = currCV; } } From 38cba9b82953ed8c9d91e998046901b16a096b9b Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sat, 10 Jun 2023 09:38:21 -0400 Subject: [PATCH 42/44] closes #200 reset seq display after freeze --- Degree/Inc/SuperSeq.h | 1 - Degree/Src/TouchChannel.cpp | 15 +++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Degree/Inc/SuperSeq.h b/Degree/Inc/SuperSeq.h index e82d283..a4a425f 100644 --- a/Degree/Inc/SuperSeq.h +++ b/Degree/Inc/SuperSeq.h @@ -56,7 +56,6 @@ class SuperSeq { int newEventPos; // when a new event is created, we store the position in this variable in case we need it for something (ie. sequence overdubing) int progressDiviser; // lengthPPQN / 16 (num LEDs used) - int progressCounter; // current int progress; bool adaptiveLength; // flag determining if the sequence length should increase past its current length diff --git a/Degree/Src/TouchChannel.cpp b/Degree/Src/TouchChannel.cpp index 3240df4..6553e0b 100644 --- a/Degree/Src/TouchChannel.cpp +++ b/Degree/Src/TouchChannel.cpp @@ -503,13 +503,19 @@ void TouchChannel::freeze(bool state) freezeChannel = state; if (freezeChannel == true) { freezeStep = sequence.currStep; // log the last sequence led to be illuminated - // maybe blink the degree LEDs? - // maybe blink the display LED sequence was frozen at? + if (sequence.containsEvents() && sequence.playbackEnabled) { + display->enableBlink(); + drawSequenceToDisplay(true); + } } else { // reset last led in sequence before freeze - if (sequence.playbackEnabled && sequence.currStep != freezeStep) + if (sequence.containsEvents() && sequence.playbackEnabled) { - setSequenceLED(freezeStep, PWM::PWM_LOW_MID, false); + if (sequence.currStep < freezeStep) { + display->clear(channelIndex); + } + display->disableBlink(); + drawSequenceToDisplay(false); } } } @@ -1187,6 +1193,7 @@ TouchChannel::setSequenceLED(uint8_t step, uint8_t pwm, bool blink) */ void TouchChannel::drawSequenceToDisplay(bool blink) { + sequence.setProgress(); // overkill, but ensures all the LEDs get lit int counter = 0; while (counter <= sequence.progress) { From ce30a84209cd9ef8360f77a51952b1d5f584baea Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 12 Jun 2023 17:13:19 -0400 Subject: [PATCH 43/44] finished writing the logger queue --- API/Inc/logger.h | 21 +++++++++++++---- API/Src/logger.cpp | 56 +++++++++++++++++++++++++++++++++++---------- Degree/Src/main.cpp | 3 +-- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/API/Inc/logger.h b/API/Inc/logger.h index 14c6928..0a17231 100644 --- a/API/Inc/logger.h +++ b/API/Inc/logger.h @@ -11,6 +11,21 @@ #define OK_UART_TX (PinName) PC_10 #endif +#define MAX_MESSAGE_LENGTH 50 + +typedef union +{ + bool boolValue; + int intValue; + char *charValue; +} QueueMessage; + +void logger_queue_message(char const *message); +void logger_queue_message(int num); +void logger_queue_message_ISR(char const *message); +void logger_queue_message_ISR(int num); +void task_logger(void *params); + void logger_init(); void logger_log(char const *str); @@ -46,8 +61,4 @@ void logger_log_arr(T arr[], int length) void logger_log_system_config(); void logger_log_task_watermark(void); -void logger_log_task_watermark(TaskHandle_t task_handle); - -void logger_queue_message(uint16_t message); - -void TASK_logger(void *params); \ No newline at end of file +void logger_log_task_watermark(TaskHandle_t task_handle); \ No newline at end of file diff --git a/API/Src/logger.cpp b/API/Src/logger.cpp index 8516a75..b845d9c 100644 --- a/API/Src/logger.cpp +++ b/API/Src/logger.cpp @@ -140,25 +140,57 @@ void logger_log_task_watermark(TaskHandle_t task_handle) { logger_log((uint32_t)stackSpace); } -static QueueHandle_t logger_queue = NULL; -void logger_queue_message(uint16_t message) { +QueueHandle_t qhLoggerQueue = NULL; +QueueHandle_t qhLoggerIntQueue = NULL; + +void logger_queue_message(char const *message) { + const char *_message = message; + xQueueSend(qhLoggerQueue, _message, portMAX_DELAY); +} + +void logger_queue_message(int num) +{ + int _num = num; + xQueueSend(qhLoggerIntQueue, &_num, portMAX_DELAY); +} + +void logger_queue_message_ISR(char const *message) +{ + BaseType_t xHigherPriorityTaskWoken; + xHigherPriorityTaskWoken = pdFALSE; + const char *_message = message; + xQueueSendFromISR(qhLoggerQueue, _message, &xHigherPriorityTaskWoken); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +void logger_queue_message_ISR(int num) +{ BaseType_t xHigherPriorityTaskWoken; xHigherPriorityTaskWoken = pdFALSE; - uint16_t _message = message; - xQueueSendFromISR(logger_queue, (void *)&message, &xHigherPriorityTaskWoken); + int _num = num; + xQueueSendFromISR(qhLoggerIntQueue, &_num, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } -void TASK_logger(void *params) { - uint16_t pulse; - logger_queue = xQueueCreate(32, sizeof(uint16_t)); +void task_logger(void *params) +{ + char message[MAX_MESSAGE_LENGTH]; + int integerMessage; + qhLoggerIntQueue = xQueueCreate(16, sizeof(int)); + qhLoggerQueue = xQueueCreate(16, sizeof(char[MAX_MESSAGE_LENGTH])); + logger_log("\nLogger task started \n"); + logger_log_task_watermark(); while (1) { - if (xQueueReceive(logger_queue, &pulse, portMAX_DELAY)) + + if (xQueueReceive(qhLoggerQueue, message, portMAX_DELAY)) + { + logger_log(message); + } + if (xQueueReceive(qhLoggerIntQueue, &integerMessage, portMAX_DELAY)) { - logger_log("\n"); - logger_log(pulse); + logger_log("\nQ:"); + logger_log(integerMessage); } - - } + } } \ No newline at end of file diff --git a/Degree/Src/main.cpp b/Degree/Src/main.cpp index 993d270..9969f92 100644 --- a/Degree/Src/main.cpp +++ b/Degree/Src/main.cpp @@ -104,9 +104,8 @@ int main(void) multi_chan_adc_init(); multi_chan_adc_start(); HAL_Delay(100); - - xTaskCreate(TASK_logger, "logger", RTOS_STACK_SIZE_MIN, NULL, RTOS_PRIORITY_LOW, NULL); + xTaskCreate(task_logger, "logger", RTOS_STACK_SIZE_MIN, NULL, RTOS_PRIORITY_LOW, NULL); xTaskCreate(taskMain, "taskMain", 512, NULL, 1, &main_task_handle); xTaskCreate(task_controller, "controller", RTOS_STACK_SIZE_MIN, &glblCtrl, RTOS_PRIORITY_HIGH, NULL); xTaskCreate(task_interrupt_handler, "ISR handler", RTOS_STACK_SIZE_MIN, &glblCtrl, RTOS_PRIORITY_HIGH + 1, NULL); From 956a1f5ee4d91f7c7f4d2286e162a6ae52b0824d Mon Sep 17 00:00:00 2001 From: scottc11 Date: Mon, 12 Jun 2023 17:13:54 -0400 Subject: [PATCH 44/44] handled reset when externally clocked --- API/Src/SuperClock.cpp | 10 ++++----- Degree/Src/GlobalControl.cpp | 23 ++++++++++----------- Degree/Tasks/Src/task_interrupt_handler.cpp | 6 +++--- Degree/Tasks/Src/task_sequence_handler.cpp | 14 ++++++++++--- 4 files changed, 30 insertions(+), 23 deletions(-) diff --git a/API/Src/SuperClock.cpp b/API/Src/SuperClock.cpp index be4520a..559c5e4 100644 --- a/API/Src/SuperClock.cpp +++ b/API/Src/SuperClock.cpp @@ -23,7 +23,9 @@ void SuperClock::reset() { __HAL_TIM_SetCounter(&htim2, 0); // not certain this has to happen, just assuming __HAL_TIM_SetCounter(&htim4, 0); - this->pulse = 0; + if (!externalInputMode) { // special for timing! + this->pulse = 0; + } this->step = 0; } @@ -159,9 +161,7 @@ void SuperClock::setPulseFrequency(uint32_t ticks) */ void SuperClock::handleInputCaptureCallback() { - // almost always, there will need to be at least 1 pulse not yet executed prior to an input capture, - // so you must execute all remaining until - // would doing the while loop in here achieve the same result? While also + // almost always, there will need to be at least 1 pulse not yet executed prior to an input capture, so you must execute all remaining until if (pulse < PPQN - 1) { handleStep(); @@ -205,7 +205,7 @@ void SuperClock::handleOverflowCallback() { if (ppqnCallback) ppqnCallback(pulse); // when clock inits, this ensures the 0ith pulse will get handled - + // by checking this first, you have the chance to reset any sequences prior to executing their 0ith pulse if (pulse == 0) { if (stepCallback) diff --git a/Degree/Src/GlobalControl.cpp b/Degree/Src/GlobalControl.cpp index 7c9d36b..6674289 100644 --- a/Degree/Src/GlobalControl.cpp +++ b/Degree/Src/GlobalControl.cpp @@ -305,10 +305,10 @@ void GlobalControl::handleButtonPress(int pad) { for (int i = 0; i < CHANNEL_COUNT; i++) { if (touchPads->padIsTouched(i, currTouched)) - dispatch_sequencer_event(CHAN(i), SEQ::RESET, 0); + dispatch_sequencer_event(CHAN(i), SEQ::RESET_ARM, 0); } } else { - dispatch_sequencer_event(CHAN::ALL, SEQ::RESET, 0); + dispatch_sequencer_event(CHAN::ALL, SEQ::RESET_ARM, 0); } break; @@ -844,6 +844,15 @@ void GlobalControl::handleStepCallback(uint16_t step) dispatch_sequencer_event_ISR(CHAN::ALL, SEQ::QUARTER_NOTE_OVERFLOW, 0); } +/** + * @brief function that triggers at the begining of every bar + * + */ +void GlobalControl::handleBarReset() +{ + dispatch_sequencer_event_ISR(CHAN::ALL, SEQ::BAR_RESET, 0); +} + /** * @brief draw the current clock time signature to the display * @@ -857,16 +866,6 @@ void GlobalControl::drawTimeSignatureToDisplay() } } - -/** - * @brief function that triggers at the begining of every bar - * - */ -void GlobalControl::handleBarReset() -{ - dispatch_sequencer_event_ISR(CHAN::ALL, SEQ::BAR_RESET, 0); -} - void GlobalControl::handleFreeze(bool freeze) { // set freeze variable here, and check in clock callback if freeze is true/false freezeBtn = freeze; diff --git a/Degree/Tasks/Src/task_interrupt_handler.cpp b/Degree/Tasks/Src/task_interrupt_handler.cpp index d11e39b..25225ab 100644 --- a/Degree/Tasks/Src/task_interrupt_handler.cpp +++ b/Degree/Tasks/Src/task_interrupt_handler.cpp @@ -21,15 +21,15 @@ void task_interrupt_handler(void *params) switch (isr_source) { case ISR_ID_TOGGLE_SWITCHES: - logger_log("\n### Toggle Switch ISR ###\n"); + // logger_log("\n### Toggle Switch ISR ###\n"); global_control->switches->updateDegreeStates(); break; case ISR_ID_TACTILE_BUTTONS: - logger_log("\n### Tactile Buttons ISR ###\n"); + // logger_log("\n### Tactile Buttons ISR ###\n"); global_control->pollButtons(); break; case ISR_ID_TOUCH_PADS: - logger_log("\n### Touch Pads ISR ###\n"); + // logger_log("\n### Touch Pads ISR ###\n"); global_control->pollTouchPads(); break; default: diff --git a/Degree/Tasks/Src/task_sequence_handler.cpp b/Degree/Tasks/Src/task_sequence_handler.cpp index 8556bbf..9df2eae 100644 --- a/Degree/Tasks/Src/task_sequence_handler.cpp +++ b/Degree/Tasks/Src/task_sequence_handler.cpp @@ -80,15 +80,20 @@ void task_sequence_handler(void *params) break; case SEQ::RESET_ARM: - resetArmed = true; + // reset button pressed, and in clock ect mode, so we set + if (ctrl->clock->externalInputMode) { + resetArmed = true; + } else { + dispatch_sequencer_event(channel, SEQ::RESET, 0); + } break; // time this so that it triggers: // if pressed 12 PPQN before beat 1, wait to reset once beat 1 happens // if < 12 ppqn after beat 1, reset immediately case SEQ::RESET: - resetArmed = false; // nyi ctrl->clock->reset(); + ctrl->freezeLED.write(1); if (channel == CHAN::ALL) { for (int i = 0; i < CHANNEL_COUNT; i++) @@ -126,6 +131,7 @@ void task_sequence_handler(void *params) } break; + // when the bar overflows back to step 1 (ex. step 4 to step 1) case SEQ::BAR_RESET: if (SuperSeq::recordArmed) { SuperSeq::recordArmed = false; @@ -136,9 +142,11 @@ void task_sequence_handler(void *params) } break; + // every qaurternote case SEQ::QUARTER_NOTE_OVERFLOW: if (resetArmed) { - dispatch_sequencer_event(CHAN::ALL, SEQ::RESET, 0); + resetArmed = false; + dispatch_sequencer_event(channel, SEQ::RESET, 0); } break;