From 8147769c1b10ca0d7cc950d53ff2027b4680ccb8 Mon Sep 17 00:00:00 2001 From: scottc11 Date: Sat, 3 Dec 2022 16:03:50 -0500 Subject: [PATCH] #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) {