Browse Source

add channel muting in loop station

Thomas Buck 6 months ago
parent
commit
b1e76905c6
9 changed files with 89 additions and 120 deletions
  1. 7
    2
      docs/src/loopstation.md
  2. 2
    2
      docs/src/usage.md
  3. 0
    1
      flash.sh
  4. 1
    1
      include/buttons.h
  5. 2
    0
      include/ui.h
  6. 2
    2
      src/buttons.c
  7. 2
    2
      src/main.c
  8. 64
    18
      src/sequence.c
  9. 9
    92
      src/ui.c

+ 7
- 2
docs/src/loopstation.md View File

@@ -1,16 +1,21 @@
1 1
 # Loop Station
2 2
 
3
+[![LARS Loop Station controls](https://www.xythobuz.de/img/lars_loop_controls_small.jpg)](https://www.xythobuz.de/img/lars_loop_controls.jpg)
4
+
3 5
 In this mode the first three buttons play each corresponding output channel.
4 6
 
5
-The fourth button, with the LED always lit, is the record button.
7
+The fourth (and eighth) button, with the LED always lit, is the record button.
6 8
 Hold it down for a while and release it to set the length of the loop.
7 9
 
8 10
 Notes played while the record button is held will be looped.
9 11
 
12
+The fifth, sixth and seventh buttons are channel mute buttons.
13
+While holding these down the corresponding channel will not play any notes.
14
+
10 15
 The top-right button below the encoder clears the recorded loop in different ways:
11 16
 
12 17
  * Pressing it on its own will clear all channels of the current loop, keeping the set length.
13
- * Presssing it while holding down one of the channel mute buttons will clear only this channel in the current loop.
18
+ * Pressing it while holding down one of the channel mute buttons will clear only this channel in the current loop.
14 19
  * Pressing it while holding down the left REC button will clear the current loop, including the length.
15 20
  * Pressing it while holding down the right REC button will clear all loops in all banks.
16 21
 

+ 2
- 2
docs/src/usage.md View File

@@ -13,7 +13,7 @@ Take a look at the sub-page for each mode for details.
13 13
 
14 14
 You can hold down some buttons while powering on the device to get different effects:
15 15
 
16
-* hold the `REC` button to enter the configuration menu
16
+* hold the `Clear` button to enter the configuration menu
17 17
 * hold the encoder `Click` button to show version information of the firmware
18
-* hold both `REC` and `Click` to enter the button test mode
18
+* hold both `Clear` and `Click` to enter the button test mode
19 19
 * hold any combination of three buttons to skip the boot animation

+ 0
- 1
flash.sh View File

@@ -26,7 +26,6 @@ if [ ! -e $DISK ]
26 26
 then
27 27
     echo Resetting Raspberry Pi Pico
28 28
     echo -n -e "\\x18" > $SERIAL
29
-    echo -n -e "\\x18" > $SERIAL
30 29
 fi
31 30
 
32 31
 echo -n Waiting for disk to appear

+ 1
- 1
include/buttons.h View File

@@ -33,7 +33,7 @@ enum buttons {
33 33
     BTN_G,
34 34
     BTN_H,
35 35
 
36
-    BTN_REC,
36
+    BTN_CLEAR,
37 37
     BTN_CLICK,
38 38
     NUM_BTNS // count
39 39
 };

+ 2
- 0
include/ui.h View File

@@ -46,4 +46,6 @@ void ui_midi_set(uint8_t channel, uint8_t note, uint8_t velocity);
46 46
 
47 47
 enum machine_modes ui_get_machinemode(void);
48 48
 
49
+void ui_redraw(void);
50
+
49 51
 #endif // __UI_H__

+ 2
- 2
src/buttons.c View File

@@ -36,7 +36,7 @@ static const uint gpio_num_proto[NUM_BTNS] = {
36 36
     0xFF, // BTN_G
37 37
     0xFF, // BTN_H
38 38
 
39
-    14, // BTN_REC
39
+    14, // BTN_CLEAR
40 40
     16, // BTN_CLICK
41 41
 };
42 42
 
@@ -53,7 +53,7 @@ static const uint gpio_num_v2[NUM_BTNS] = {
53 53
     0xFF, // BTN_F
54 54
     0xFF, // BTN_G
55 55
     0xFF, // BTN_H
56
-    0xFF, // BTN_REC
56
+    0xFF, // BTN_CLEAR
57 57
 
58 58
     20, // BTN_CLICK
59 59
 };

+ 2
- 2
src/main.c View File

@@ -105,9 +105,9 @@ static void animate_boot_combos(void) {
105 105
     uint32_t cnt = debug_count_buttons();
106 106
 
107 107
     // handle special button combos on boot
108
-    if ((cnt == 2) && debug_buttons[BTN_REC] && debug_buttons[BTN_CLICK]) {
108
+    if ((cnt == 2) && debug_buttons[BTN_CLEAR] && debug_buttons[BTN_CLICK]) {
109 109
         lcd_debug_buttons();
110
-    } else if ((cnt == 1) && debug_buttons[BTN_REC]) {
110
+    } else if ((cnt == 1) && debug_buttons[BTN_CLEAR]) {
111 111
         // enter settings menu
112 112
         settings_init();
113 113
         settings_run();

+ 64
- 18
src/sequence.c View File

@@ -36,6 +36,7 @@ static uint32_t last_i = 0;
36 36
 static uint32_t bank = 0;
37 37
 static uint32_t max_banks_currently = 0;
38 38
 static uint32_t channel = 0;
39
+static bool button_held[NUM_BTNS] = {0};
39 40
 
40 41
 static enum channels sequence[MAX_BEATS] = {0};
41 42
 
@@ -117,9 +118,17 @@ static bool sequence_get(uint32_t beat, enum channels ch) {
117 118
     return false;
118 119
 }
119 120
 
120
-void sequence_handle_button_loopstation(enum buttons btn, bool rec) {
121
+void sequence_handle_button_loopstation(enum buttons btn, bool val) {
122
+    button_held[btn] = val;
123
+
121 124
     switch (btn) {
122 125
         case BTN_A: {
126
+            // only react to button down events
127
+            if (!val) {
128
+                break;
129
+            }
130
+
131
+            // trigger outputs and LEDs
123 132
             pulse_trigger_out(0, mem_data()->ch_timings[0]);
124 133
             pulse_trigger_led(0, mem_data()->ch_timings[0]);
125 134
             pulse_trigger_led(4, mem_data()->ch_timings[0]);
@@ -127,6 +136,12 @@ void sequence_handle_button_loopstation(enum buttons btn, bool rec) {
127 136
         }
128 137
 
129 138
         case BTN_B: {
139
+            // only react to button down events
140
+            if (!val) {
141
+                break;
142
+            }
143
+
144
+            // trigger outputs and LEDs
130 145
             pulse_trigger_out(1, mem_data()->ch_timings[1]);
131 146
             pulse_trigger_led(1, mem_data()->ch_timings[1]);
132 147
             pulse_trigger_led(5, mem_data()->ch_timings[1]);
@@ -134,25 +149,50 @@ void sequence_handle_button_loopstation(enum buttons btn, bool rec) {
134 149
         }
135 150
 
136 151
         case BTN_C: {
152
+            // only react to button down events
153
+            if (!val) {
154
+                break;
155
+            }
156
+
157
+            // trigger outputs and LEDs
137 158
             pulse_trigger_out(2, mem_data()->ch_timings[2]);
138 159
             pulse_trigger_led(2, mem_data()->ch_timings[2]);
139 160
             pulse_trigger_led(6, mem_data()->ch_timings[2]);
140 161
             break;
141 162
         }
142 163
 
143
-        case BTN_E: {
144
-            // TODO mute/unmute according to rec param
145
-            return; // not recording sequence!
164
+        case BTN_D:
165
+        case BTN_H: {
166
+            sequence_looptime(!val);
167
+            if (!val) {
168
+                ui_redraw();
169
+            }
170
+            break;
146 171
         }
147 172
 
148
-        case BTN_F: {
149
-            // TODO mute/unmute according to rec param
150
-            return; // not recording sequence!
151
-        }
173
+        case BTN_CLEAR: {
174
+            // only react to button down events
175
+            if (!val) {
176
+                break;
177
+            }
152 178
 
153
-        case BTN_G: {
154
-            // TODO mute/unmute according to rec param
155
-            return; // not recording sequence!
179
+            if (button_held[BTN_H]) {
180
+                // right REC: clear all loops in all banks
181
+                // TODO other banks
182
+                sequence_init();
183
+            } else if (button_held[BTN_D]) {
184
+                // left REC: clear the current loop, including the length
185
+                sequence_init();
186
+            } else if (button_held[BTN_E] || button_held[BTN_F] || button_held[BTN_G]) {
187
+                // channel mute buttons: clear only this channel in the current loop
188
+                // TODO
189
+            } else {
190
+                // on its own: clear all channels of the current loop, keeping the set length
191
+                // TODO
192
+            }
193
+
194
+            ui_redraw();
195
+            break;
156 196
         }
157 197
 
158 198
         default: {
@@ -160,22 +200,20 @@ void sequence_handle_button_loopstation(enum buttons btn, bool rec) {
160 200
         }
161 201
     }
162 202
 
163
-    if (rec) {
203
+    // set marker in sequence when one of the REC buttons was held
204
+    if ((button_held[BTN_D] || button_held[BTN_H]) && val) {
164 205
         switch (btn) {
165
-            case BTN_A:
166
-            case BTN_E: {
206
+            case BTN_A: {
167 207
                 sequence_set(last_i, CH_KICK, true);
168 208
                 break;
169 209
             }
170 210
 
171
-            case BTN_B:
172
-            case BTN_F: {
211
+            case BTN_B: {
173 212
                 sequence_set(last_i, CH_SNARE, true);
174 213
                 break;
175 214
             }
176 215
 
177
-            case BTN_C:
178
-            case BTN_G: {
216
+            case BTN_C: {
179 217
                 sequence_set(last_i, CH_HIHAT, true);
180 218
                 break;
181 219
             }
@@ -238,8 +276,16 @@ void sequence_run(void) {
238 276
         }
239 277
 
240 278
         for (uint ch = 0; ch < NUM_CHANNELS; ch++) {
279
+            // skip muted channels
280
+            if ((ui_get_machinemode() == MODE_LOOPSTATION) && button_held[BTN_E + ch]) {
281
+                continue;
282
+            }
283
+
241 284
             if (sequence[i] & (1 << ch)) {
285
+                // trigger channel solenoid
242 286
                 pulse_trigger_out(ch, mem_data()->ch_timings[ch]);
287
+
288
+                // flash LEDs in loopstation mode
243 289
                 if (ui_get_machinemode() == MODE_LOOPSTATION) {
244 290
                     pulse_trigger_led(ch, mem_data()->ch_timings[ch]);
245 291
                     pulse_trigger_led(ch + 4, mem_data()->ch_timings[ch]);

+ 9
- 92
src/ui.c View File

@@ -79,7 +79,6 @@ static bool allowed_settings[MACHINE_NUM_MODES][SETTING_NUM_MODES] = {
79 79
     },
80 80
 };
81 81
 
82
-static bool rec_held_down = false;
83 82
 static enum ui_settings ui_setting = 0;
84 83
 static enum machine_modes machine_mode = 0;
85 84
 static uint32_t last_bat_fetch = 0;
@@ -91,7 +90,7 @@ enum machine_modes ui_get_machinemode(void) {
91 90
     return machine_mode;
92 91
 }
93 92
 
94
-static void ui_redraw(void) {
93
+void ui_redraw(void) {
95 94
     char mode[64] = {0};
96 95
     char val[64] = {0};
97 96
     char bat[64] = {0};
@@ -184,93 +183,6 @@ static void ui_redraw(void) {
184 183
     lcd_draw(mode, val, bat);
185 184
 }
186 185
 
187
-static void ui_buttons_loopstation(enum buttons btn, bool val) {
188
-    switch (btn) {
189
-        case BTN_A:
190
-        case BTN_B:
191
-        case BTN_C: {
192
-            if (val) {
193
-                sequence_handle_button_loopstation(btn, rec_held_down);
194
-            }
195
-            break;
196
-        }
197
-
198
-        case BTN_E:
199
-        case BTN_F:
200
-        case BTN_G: {
201
-            sequence_handle_button_loopstation(btn, val);
202
-            break;
203
-        }
204
-
205
-        case BTN_REC: {
206
-            // reset sequence
207
-            sequence_init();
208
-            ui_redraw();
209
-            break;
210
-        }
211
-
212
-        case BTN_D:
213
-        case BTN_H: {
214
-            rec_held_down = val;
215
-            sequence_looptime(!val);
216
-            if (!val) {
217
-                ui_redraw();
218
-            }
219
-            break;
220
-        }
221
-
222
-        default: {
223
-            debug("invalid btn: %d", btn);
224
-            break;
225
-        }
226
-    }
227
-}
228
-
229
-static void ui_buttons_drummachine(enum buttons btn, bool val) {
230
-    switch (btn) {
231
-        case BTN_A:
232
-        case BTN_B:
233
-        case BTN_C:
234
-        case BTN_D:
235
-        case BTN_E:
236
-        case BTN_F:
237
-        case BTN_G:
238
-        case BTN_H:
239
-        case BTN_REC: {
240
-            if (val) {
241
-                sequence_handle_button_drummachine(btn);
242
-            }
243
-            break;
244
-        }
245
-
246
-        default: {
247
-            debug("invalid btn: %d", btn);
248
-            break;
249
-        }
250
-    }
251
-}
252
-
253
-static void ui_buttons_midi(enum buttons btn, bool val) {
254
-    switch (btn) {
255
-        case BTN_A:
256
-        case BTN_B:
257
-        case BTN_C:
258
-        case BTN_D:
259
-        case BTN_E:
260
-        case BTN_F:
261
-        case BTN_G:
262
-        case BTN_H: {
263
-            if (val) {
264
-                usb_midi_tx(midi_tx, btn - BTN_A, 0x7F);
265
-                pulse_trigger_led(btn - BTN_A, mem_data()->ch_timings[0]);
266
-            }
267
-        }
268
-
269
-        default:
270
-            break;
271
-    }
272
-}
273
-
274 186
 static void ui_buttons(enum buttons btn, bool val) {
275 187
     switch (btn) {
276 188
         case BTN_CLICK: {
@@ -288,17 +200,22 @@ static void ui_buttons(enum buttons btn, bool val) {
288 200
         default: {
289 201
             switch (machine_mode) {
290 202
                 case MODE_LOOPSTATION: {
291
-                    ui_buttons_loopstation(btn, val);
203
+                    sequence_handle_button_loopstation(btn, val);
292 204
                     break;
293 205
                 }
294 206
 
295 207
                 case MODE_DRUMMACHINE: {
296
-                    ui_buttons_drummachine(btn, val);
208
+                    if (val) {
209
+                        sequence_handle_button_drummachine(btn);
210
+                    }
297 211
                     break;
298 212
                 }
299 213
 
300 214
                 case MODE_MIDI: {
301
-                    ui_buttons_midi(btn, val);
215
+                    if (val) {
216
+                        usb_midi_tx(midi_tx, btn - BTN_A, 0x7F);
217
+                        pulse_trigger_led(btn - BTN_A, mem_data()->ch_timings[0]);
218
+                    }
302 219
                     break;
303 220
                 }
304 221
 

Loading…
Cancel
Save