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
 # Loop Station
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
 In this mode the first three buttons play each corresponding output channel.
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
 Hold it down for a while and release it to set the length of the loop.
8
 Hold it down for a while and release it to set the length of the loop.
7
 
9
 
8
 Notes played while the record button is held will be looped.
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
 The top-right button below the encoder clears the recorded loop in different ways:
15
 The top-right button below the encoder clears the recorded loop in different ways:
11
 
16
 
12
  * Pressing it on its own will clear all channels of the current loop, keeping the set length.
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
  * Pressing it while holding down the left REC button will clear the current loop, including the length.
19
  * Pressing it while holding down the left REC button will clear the current loop, including the length.
15
  * Pressing it while holding down the right REC button will clear all loops in all banks.
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
 
13
 
14
 You can hold down some buttons while powering on the device to get different effects:
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
 * hold the encoder `Click` button to show version information of the firmware
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
 * hold any combination of three buttons to skip the boot animation
19
 * hold any combination of three buttons to skip the boot animation

+ 0
- 1
flash.sh View File

26
 then
26
 then
27
     echo Resetting Raspberry Pi Pico
27
     echo Resetting Raspberry Pi Pico
28
     echo -n -e "\\x18" > $SERIAL
28
     echo -n -e "\\x18" > $SERIAL
29
-    echo -n -e "\\x18" > $SERIAL
30
 fi
29
 fi
31
 
30
 
32
 echo -n Waiting for disk to appear
31
 echo -n Waiting for disk to appear

+ 1
- 1
include/buttons.h View File

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

+ 2
- 0
include/ui.h View File

46
 
46
 
47
 enum machine_modes ui_get_machinemode(void);
47
 enum machine_modes ui_get_machinemode(void);
48
 
48
 
49
+void ui_redraw(void);
50
+
49
 #endif // __UI_H__
51
 #endif // __UI_H__

+ 2
- 2
src/buttons.c View File

36
     0xFF, // BTN_G
36
     0xFF, // BTN_G
37
     0xFF, // BTN_H
37
     0xFF, // BTN_H
38
 
38
 
39
-    14, // BTN_REC
39
+    14, // BTN_CLEAR
40
     16, // BTN_CLICK
40
     16, // BTN_CLICK
41
 };
41
 };
42
 
42
 
53
     0xFF, // BTN_F
53
     0xFF, // BTN_F
54
     0xFF, // BTN_G
54
     0xFF, // BTN_G
55
     0xFF, // BTN_H
55
     0xFF, // BTN_H
56
-    0xFF, // BTN_REC
56
+    0xFF, // BTN_CLEAR
57
 
57
 
58
     20, // BTN_CLICK
58
     20, // BTN_CLICK
59
 };
59
 };

+ 2
- 2
src/main.c View File

105
     uint32_t cnt = debug_count_buttons();
105
     uint32_t cnt = debug_count_buttons();
106
 
106
 
107
     // handle special button combos on boot
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
         lcd_debug_buttons();
109
         lcd_debug_buttons();
110
-    } else if ((cnt == 1) && debug_buttons[BTN_REC]) {
110
+    } else if ((cnt == 1) && debug_buttons[BTN_CLEAR]) {
111
         // enter settings menu
111
         // enter settings menu
112
         settings_init();
112
         settings_init();
113
         settings_run();
113
         settings_run();

+ 64
- 18
src/sequence.c View File

36
 static uint32_t bank = 0;
36
 static uint32_t bank = 0;
37
 static uint32_t max_banks_currently = 0;
37
 static uint32_t max_banks_currently = 0;
38
 static uint32_t channel = 0;
38
 static uint32_t channel = 0;
39
+static bool button_held[NUM_BTNS] = {0};
39
 
40
 
40
 static enum channels sequence[MAX_BEATS] = {0};
41
 static enum channels sequence[MAX_BEATS] = {0};
41
 
42
 
117
     return false;
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
     switch (btn) {
124
     switch (btn) {
122
         case BTN_A: {
125
         case BTN_A: {
126
+            // only react to button down events
127
+            if (!val) {
128
+                break;
129
+            }
130
+
131
+            // trigger outputs and LEDs
123
             pulse_trigger_out(0, mem_data()->ch_timings[0]);
132
             pulse_trigger_out(0, mem_data()->ch_timings[0]);
124
             pulse_trigger_led(0, mem_data()->ch_timings[0]);
133
             pulse_trigger_led(0, mem_data()->ch_timings[0]);
125
             pulse_trigger_led(4, mem_data()->ch_timings[0]);
134
             pulse_trigger_led(4, mem_data()->ch_timings[0]);
127
         }
136
         }
128
 
137
 
129
         case BTN_B: {
138
         case BTN_B: {
139
+            // only react to button down events
140
+            if (!val) {
141
+                break;
142
+            }
143
+
144
+            // trigger outputs and LEDs
130
             pulse_trigger_out(1, mem_data()->ch_timings[1]);
145
             pulse_trigger_out(1, mem_data()->ch_timings[1]);
131
             pulse_trigger_led(1, mem_data()->ch_timings[1]);
146
             pulse_trigger_led(1, mem_data()->ch_timings[1]);
132
             pulse_trigger_led(5, mem_data()->ch_timings[1]);
147
             pulse_trigger_led(5, mem_data()->ch_timings[1]);
134
         }
149
         }
135
 
150
 
136
         case BTN_C: {
151
         case BTN_C: {
152
+            // only react to button down events
153
+            if (!val) {
154
+                break;
155
+            }
156
+
157
+            // trigger outputs and LEDs
137
             pulse_trigger_out(2, mem_data()->ch_timings[2]);
158
             pulse_trigger_out(2, mem_data()->ch_timings[2]);
138
             pulse_trigger_led(2, mem_data()->ch_timings[2]);
159
             pulse_trigger_led(2, mem_data()->ch_timings[2]);
139
             pulse_trigger_led(6, mem_data()->ch_timings[2]);
160
             pulse_trigger_led(6, mem_data()->ch_timings[2]);
140
             break;
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
         default: {
198
         default: {
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
         switch (btn) {
205
         switch (btn) {
165
-            case BTN_A:
166
-            case BTN_E: {
206
+            case BTN_A: {
167
                 sequence_set(last_i, CH_KICK, true);
207
                 sequence_set(last_i, CH_KICK, true);
168
                 break;
208
                 break;
169
             }
209
             }
170
 
210
 
171
-            case BTN_B:
172
-            case BTN_F: {
211
+            case BTN_B: {
173
                 sequence_set(last_i, CH_SNARE, true);
212
                 sequence_set(last_i, CH_SNARE, true);
174
                 break;
213
                 break;
175
             }
214
             }
176
 
215
 
177
-            case BTN_C:
178
-            case BTN_G: {
216
+            case BTN_C: {
179
                 sequence_set(last_i, CH_HIHAT, true);
217
                 sequence_set(last_i, CH_HIHAT, true);
180
                 break;
218
                 break;
181
             }
219
             }
238
         }
276
         }
239
 
277
 
240
         for (uint ch = 0; ch < NUM_CHANNELS; ch++) {
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
             if (sequence[i] & (1 << ch)) {
284
             if (sequence[i] & (1 << ch)) {
285
+                // trigger channel solenoid
242
                 pulse_trigger_out(ch, mem_data()->ch_timings[ch]);
286
                 pulse_trigger_out(ch, mem_data()->ch_timings[ch]);
287
+
288
+                // flash LEDs in loopstation mode
243
                 if (ui_get_machinemode() == MODE_LOOPSTATION) {
289
                 if (ui_get_machinemode() == MODE_LOOPSTATION) {
244
                     pulse_trigger_led(ch, mem_data()->ch_timings[ch]);
290
                     pulse_trigger_led(ch, mem_data()->ch_timings[ch]);
245
                     pulse_trigger_led(ch + 4, mem_data()->ch_timings[ch]);
291
                     pulse_trigger_led(ch + 4, mem_data()->ch_timings[ch]);

+ 9
- 92
src/ui.c View File

79
     },
79
     },
80
 };
80
 };
81
 
81
 
82
-static bool rec_held_down = false;
83
 static enum ui_settings ui_setting = 0;
82
 static enum ui_settings ui_setting = 0;
84
 static enum machine_modes machine_mode = 0;
83
 static enum machine_modes machine_mode = 0;
85
 static uint32_t last_bat_fetch = 0;
84
 static uint32_t last_bat_fetch = 0;
91
     return machine_mode;
90
     return machine_mode;
92
 }
91
 }
93
 
92
 
94
-static void ui_redraw(void) {
93
+void ui_redraw(void) {
95
     char mode[64] = {0};
94
     char mode[64] = {0};
96
     char val[64] = {0};
95
     char val[64] = {0};
97
     char bat[64] = {0};
96
     char bat[64] = {0};
184
     lcd_draw(mode, val, bat);
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
 static void ui_buttons(enum buttons btn, bool val) {
186
 static void ui_buttons(enum buttons btn, bool val) {
275
     switch (btn) {
187
     switch (btn) {
276
         case BTN_CLICK: {
188
         case BTN_CLICK: {
288
         default: {
200
         default: {
289
             switch (machine_mode) {
201
             switch (machine_mode) {
290
                 case MODE_LOOPSTATION: {
202
                 case MODE_LOOPSTATION: {
291
-                    ui_buttons_loopstation(btn, val);
203
+                    sequence_handle_button_loopstation(btn, val);
292
                     break;
204
                     break;
293
                 }
205
                 }
294
 
206
 
295
                 case MODE_DRUMMACHINE: {
207
                 case MODE_DRUMMACHINE: {
296
-                    ui_buttons_drummachine(btn, val);
208
+                    if (val) {
209
+                        sequence_handle_button_drummachine(btn);
210
+                    }
297
                     break;
211
                     break;
298
                 }
212
                 }
299
 
213
 
300
                 case MODE_MIDI: {
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
                     break;
219
                     break;
303
                 }
220
                 }
304
 
221
 

Loading…
Cancel
Save