Skip to content

Commit cca3b87

Browse files
authored
Merge pull request #6 from wgd-modular/play-mode
feat: Add different play modes
2 parents 00ae139 + ffda08a commit cca3b87

2 files changed

Lines changed: 125 additions & 10 deletions

File tree

MANUAL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
- **Sequencer Probability:** Controls the probability over which sequence is outputted at the Prob Out jack.
1414
_Secondary Function:_ Choose between 7 different quantization scales. Each scale is represented by a color from the encoding below, and the knob position from left-to-right corresponds to the table entries top-to-bottom:
1515
- **Steps 1-8:** Set Voltage for each step of the sequence
16-
_Secondary Function:_ Step 1: Set sequence length
16+
_Secondary Function:_ Step 1: Set sequence length Step 2: Set playmode (1: Forward 2: Backward 3: Ping Pong 4: Random)
1717

1818
| LED Color | RGB | Scale |
1919
|-----------|-----------------|--------------------------------|

giardiniera-firmware.ino

Lines changed: 124 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ int now = 0;
5252
bool lastButtonState = HIGH;
5353
int lengthA = 8;
5454
int lengthB = 8;
55+
int directionA = 0;
56+
int directionB = 0;
57+
int directionStateA = 1;
58+
int directionStateB = 1;
5559

5660
// --- POT movement tracking (mode-aware) ----------------
5761
int potLastStablePrimary[3]; // baseline used when button NOT pressed
@@ -64,6 +68,11 @@ int seqKnobLastStableSecondary[2];
6468
int seqKnobLastRaw[2];
6569
unsigned long seqKnobLastCheck[2];
6670

71+
int seqKnob2LastStablePrimary[2];
72+
int seqKnob2LastStableSecondary[2];
73+
int seqKnob2LastRaw[2];
74+
unsigned long seqKnob2LastCheck[2];
75+
6776

6877
const unsigned long POT_CHECK_INTERVAL = 2000UL; // µs between physical reads (~2 ms)
6978
const int POT_NOISE_THRESHOLD = 4; // ignore tiny ±4 jitter
@@ -111,6 +120,19 @@ void setup() {
111120
seqKnobLastRaw[1] = vB;
112121
seqKnobLastCheck[1] = micros();
113122

123+
int vA2 = readMux(MUX_SIG0, 1);
124+
seqKnob2LastStablePrimary[0] = vA2;
125+
seqKnob2LastStableSecondary[0] = vA2;
126+
seqKnob2LastRaw[0] = vA2;
127+
seqKnob2LastCheck[0] = micros();
128+
delayMicroseconds(60);
129+
130+
int vB2 = readMux(MUX_SIG1, 1);
131+
seqKnob2LastStablePrimary[1] = vB2;
132+
seqKnob2LastStableSecondary[1] = vB2;
133+
seqKnob2LastRaw[1] = vB2;
134+
seqKnob2LastCheck[1] = micros();
135+
114136
// Initialize DAC (MCP4728)
115137
if (!dac.begin()) {
116138
while (1); // DAC initialization failed
@@ -146,6 +168,20 @@ void syncPotBaselinesOnModeChange(bool toSecondary) {
146168
seqKnobLastStablePrimary[1] = vB;
147169
seqKnobLastRaw[1] = vB;
148170
}
171+
172+
int vA2 = readMux(MUX_SIG0, 1);
173+
int vB2 = readMux(MUX_SIG1, 1);
174+
if (toSecondary) {
175+
seqKnob2LastStableSecondary[0] = vA2;
176+
seqKnob2LastRaw[0] = vA2;
177+
seqKnob2LastStableSecondary[1] = vB2;
178+
seqKnob2LastRaw[1] = vB2;
179+
} else {
180+
seqKnob2LastStablePrimary[0] = vA2;
181+
seqKnob2LastRaw[0] = vA2;
182+
seqKnob2LastStablePrimary[1] = vB2;
183+
seqKnob2LastRaw[1] = vB2;
184+
}
149185
}
150186

151187
void loop() {
@@ -177,9 +213,11 @@ void loop() {
177213
bool p2_moved = hasPotMoved(2);
178214
bool seqA_moved = hasSeqKnobMoved(0);
179215
bool seqB_moved = hasSeqKnobMoved(1);
216+
bool seqA2_moved = hasSeqKnob2Moved(0);
217+
bool seqB2_moved = hasSeqKnob2Moved(1);
180218

181219
// Update controls
182-
readControls(p0_moved, p1_moved, p2_moved, seqA_moved, seqB_moved);
220+
readControls(p0_moved, p1_moved, p2_moved, seqA_moved, seqB_moved, seqA2_moved, seqB2_moved);
183221

184222
if (digitalRead(BUTTON_PIN) == LOW) {
185223
if(p0_moved) {
@@ -192,6 +230,10 @@ void loop() {
192230
updateScalingVisualization(map(lengthA, 1, 8, 0, 1023), 200, 20, 0);
193231
} else if (seqB_moved) {
194232
updateScalingVisualization(map(lengthB, 1, 8, 0, 1023), 0, 200, 200);
233+
} else if (seqA2_moved) {
234+
updateScalingVisualization(map(directionA, 0, 3, 0, 1023), 200, 20, 0);
235+
} else if (seqB2_moved) {
236+
updateScalingVisualization(map(directionB, 0, 3, 0, 1023), 0, 200, 200);
195237
}
196238
}
197239

@@ -210,15 +252,15 @@ void handleClockPulse() {
210252
now = micros();
211253
static int counterA = 0, counterB = 0;
212254

213-
// Advance sequence steps based on clock division
255+
// Advance sequence steps based on clock division and playback mode
214256
if (++counterA >= divisionA) {
215257
counterA = 0;
216-
step1 = (step1 + 1) % lengthA;
258+
step1 = getNextStep(step1, lengthA, directionA, directionStateA);
217259
}
218260

219261
if (++counterB >= divisionB) {
220262
counterB = 0;
221-
step2 = (step2 + 1) % lengthB;
263+
step2 = getNextStep(step2, lengthB, directionB, directionStateB);
222264
}
223265

224266
// Update LEDs with sequences
@@ -266,21 +308,41 @@ void updateLEDs() {
266308
strip.show();
267309
}
268310

269-
void readControls(bool p0_moved, bool p1_moved, bool p2_moved, bool seqA_moved, bool seqB_moved) {
311+
void readControls(bool p0_moved, bool p1_moved, bool p2_moved, bool seqA_moved, bool seqB_moved, bool seqA2_moved, bool seqB2_moved) {
270312
// Read next pots for sequences A and B
271-
int nextStepA = (step1 + 1) % NUM_LEDS;
272-
int nextStepB = (step2 + 1) % NUM_LEDS;
313+
int nextStepA = getNextStep(step1, lengthA, directionA, directionStateA);
314+
int nextStepB = getNextStep(step2, lengthB, directionB, directionStateB);
273315
if (digitalRead(BUTTON_PIN) == LOW) {
274316
if (seqA_moved) {
275317
int currentLengthKnobA = readMux(MUX_SIG0, 0);
276318
lengthA = map(currentLengthKnobA, 0, 1023, 1, 8);
277319
if (step1 >= lengthA) step1 = 0;
278320
}
279-
321+
280322
if (seqB_moved) {
281323
int currentLengthKnobB = readMux(MUX_SIG1, 0);
282324
lengthB = map(currentLengthKnobB, 0, 1023, 1, 8);
283-
if (step2 >= lengthB) step2 = 0;
325+
if (step1 >= lengthB) step1 = 0;
326+
}
327+
328+
if (seqA2_moved) {
329+
int currentDirectionKnobA = readMux(MUX_SIG0, 1);
330+
directionA = currentDirectionKnobA / 256;
331+
if (directionA > 3) directionA = 3;
332+
if (directionA == 2) {
333+
directionStateA = 1;
334+
step1 = 0;
335+
}
336+
}
337+
338+
if (seqB2_moved) {
339+
int currentDirectionKnobB = readMux(MUX_SIG1, 1);
340+
directionB = currentDirectionKnobB / 256;
341+
if (directionB > 3) directionB = 3;
342+
if (directionB == 2) {
343+
directionStateB = 1;
344+
step2 = 0;
345+
}
284346
}
285347

286348
if (nextStepA != 0) {
@@ -354,6 +416,27 @@ void outputSequenceToDAC() {
354416
}
355417
}
356418

419+
int getNextStep(int currentStep, int length, int direction, int &directionState) {
420+
if (direction == 0) {
421+
return (currentStep + 1) % length;
422+
} else if (direction == 1) {
423+
return (currentStep - 1 + length) % length;
424+
} else if (direction == 2) {
425+
int nextStep = currentStep + directionState;
426+
if (nextStep >= length - 1) {
427+
nextStep = length - 1;
428+
directionState = -1;
429+
} else if (nextStep <= 0) {
430+
nextStep = 0;
431+
directionState = 1;
432+
}
433+
return nextStep;
434+
} else if (direction == 3) {
435+
return random(0, length);
436+
}
437+
return currentStep;
438+
}
439+
357440
void updateScalingVisualization(int value, int r, int g, int b) {
358441
strip.clear();
359442

@@ -507,6 +590,38 @@ bool hasSeqKnobMoved(int seqIndex) {
507590
return moved;
508591
}
509592

593+
bool hasSeqKnob2Moved(int seqIndex) {
594+
bool secondaryMode = (digitalRead(BUTTON_PIN) == LOW);
595+
596+
unsigned long nowTime = micros();
597+
if (nowTime - seqKnob2LastCheck[seqIndex] < POT_CHECK_INTERVAL) {
598+
return false;
599+
}
600+
601+
int sigPin = (seqIndex == 0) ? MUX_SIG0 : MUX_SIG1;
602+
int currentValue = readMux(sigPin, 1);
603+
604+
if (abs(currentValue - seqKnob2LastRaw[seqIndex]) <= POT_NOISE_THRESHOLD) {
605+
currentValue = seqKnob2LastRaw[seqIndex];
606+
}
607+
seqKnob2LastRaw[seqIndex] = currentValue;
608+
609+
bool moved = false;
610+
if (secondaryMode) {
611+
if (abs(currentValue - seqKnob2LastStableSecondary[seqIndex]) > POT_MOVE_THRESHOLD) {
612+
seqKnob2LastStableSecondary[seqIndex] = currentValue;
613+
moved = true;
614+
}
615+
} else {
616+
if (abs(currentValue - seqKnob2LastStablePrimary[seqIndex]) > POT_MOVE_THRESHOLD) {
617+
seqKnob2LastStablePrimary[seqIndex] = currentValue;
618+
moved = true;
619+
}
620+
}
621+
622+
seqKnob2LastCheck[seqIndex] = nowTime;
623+
return moved;
624+
}
510625

511626
const int scales[8][36] = {
512627
// Chromatic scale

0 commit comments

Comments
 (0)