aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2023-08-22 12:28:10 +0200
committerBad Diode <bd@badd10de.dev>2023-08-22 12:28:10 +0200
commite5d61a87ec41443a2e32cd8be1ecc62b8c590251 (patch)
tree26b832481fcb0e93d843e9fe5d4f875e2e5d2232
parent8fc5cc7f53ea3874bae5389f489814869d2abd04 (diff)
downloadstepper-e5d61a87ec41443a2e32cd8be1ecc62b8c590251.tar.gz
stepper-e5d61a87ec41443a2e32cd8be1ecc62b8c590251.zip
Add pattern clearing with SEL+L+R on pattern view
-rw-r--r--src/drawing.c40
-rw-r--r--src/main.c10
-rw-r--r--src/patterns.c17
-rw-r--r--src/sequencer.c80
4 files changed, 109 insertions, 38 deletions
diff --git a/src/drawing.c b/src/drawing.c
index d24307b..253a961 100644
--- a/src/drawing.c
+++ b/src/drawing.c
@@ -3,7 +3,7 @@
3// 3//
4 4
5void 5void
6draw_param_stub(size_t idx, u8 color) { 6draw_param_stub(size_t idx, u8 clr) {
7 if (idx >= 10) { 7 if (idx >= 10) {
8 return; 8 return;
9 } 9 }
@@ -15,12 +15,12 @@ draw_param_stub(size_t idx, u8 color) {
15 } 15 }
16 u8 y1 = y0 + PARAMS_BOX_H; 16 u8 y1 = y0 + PARAMS_BOX_H;
17 for (size_t i = 0; i < PARAMS_BOX_W; i += 2) { 17 for (size_t i = 0; i < PARAMS_BOX_W; i += 2) {
18 draw_pixel(x0 + i + 1, y0, color); 18 draw_pixel(x0 + i + 1, y0, clr);
19 draw_pixel(x0 + i + 1, y1, color); 19 draw_pixel(x0 + i + 1, y1, clr);
20 } 20 }
21 for (size_t i = 0; i < PARAMS_BOX_H; i += 2) { 21 for (size_t i = 0; i < PARAMS_BOX_H; i += 2) {
22 draw_pixel(x0, y0 + i + 1, color); 22 draw_pixel(x0, y0 + i + 1, clr);
23 draw_pixel(x1, y0 + i + 1, color); 23 draw_pixel(x1, y0 + i + 1, clr);
24 } 24 }
25} 25}
26 26
@@ -79,6 +79,9 @@ draw_channels(void) {
79 case 2: { active = patterns[pattern_selection_loc].ch3.active; } break; 79 case 2: { active = patterns[pattern_selection_loc].ch3.active; } break;
80 case 3: { active = patterns[pattern_selection_loc].ch4.active; } break; 80 case 3: { active = patterns[pattern_selection_loc].ch4.active; } break;
81 } 81 }
82 if (patterns[pattern_selection_loc].empty) {
83 active = true;
84 }
82 u8 clr = active ? colors[i] : COL_OFF; 85 u8 clr = active ? colors[i] : COL_OFF;
83 size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; 86 size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y;
84 draw_channel_sprite(CHAN_START_X, y, clr, i); 87 draw_channel_sprite(CHAN_START_X, y, clr, i);
@@ -274,13 +277,25 @@ draw_bank_buttons() {
274} 277}
275 278
276void 279void
280draw_dotted_rect(size_t x0, size_t y0, size_t x1, size_t y1, u8 clr) {
281 size_t w = x1 - x0;
282 size_t h = y1 - y0;
283 for (size_t i = 0; i <= w; i += 2) {
284 draw_pixel(x0 + i, y0, clr);
285 draw_pixel(x0 + i, y1, clr);
286 }
287 for (size_t i = 0; i <= h; i += 2) {
288 draw_pixel(x0, y0 + i, clr);
289 draw_pixel(x1, y0 + i, clr);
290 }
291}
292
293void
277draw_pattern_buttons() { 294draw_pattern_buttons() {
278 // Clear patterns. 295 // Clear patterns.
279 size_t x = PAT_START_X; 296 size_t x = PAT_START_X;
280 size_t y = PAT_START_Y; 297 size_t y = PAT_START_Y;
281 298
282 draw_filled_rect(x, y, x + PAT_W, y + PAT_H * 8, COL_BG);
283
284 txt_drawf_small("PAT", x, y - 10, COL_FG); 299 txt_drawf_small("PAT", x, y - 10, COL_FG);
285 char pat_names[] = { 300 char pat_names[] = {
286 'A', 'B', 'C', 'D', 301 'A', 'B', 'C', 'D',
@@ -294,7 +309,12 @@ draw_pattern_buttons() {
294 if (i == next_pattern && current_pattern != next_pattern) { 309 if (i == next_pattern && current_pattern != next_pattern) {
295 color = COL_ACC_0; 310 color = COL_ACC_0;
296 } 311 }
297 draw_rect(x, y, x + PAT_W, y + PAT_H, color); 312 draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG);
313 if (patterns[i].empty) {
314 draw_dotted_rect(x, y, x + PAT_W, y + PAT_H, color);
315 } else {
316 draw_rect(x, y, x + PAT_W, y + PAT_H, color);
317 }
298 txt_drawc(pat_names[i], x + 4, y + 1, color); 318 txt_drawc(pat_names[i], x + 4, y + 1, color);
299 y += PAT_OFFSET_Y; 319 y += PAT_OFFSET_Y;
300 } 320 }
@@ -404,7 +424,9 @@ draw_triggers(void) {
404 size_t y1 = TRIG_START_Y + offset_y + TRIG_H; 424 size_t y1 = TRIG_START_Y + offset_y + TRIG_H;
405 draw_rect(x0, y0, x1, y1, COL_FG); 425 draw_rect(x0, y0, x1, y1, COL_FG);
406 clear_trigger(i); 426 clear_trigger(i);
407 draw_trigger(channel_selection_loc, i); 427 if (!patterns[pattern_selection_loc].empty) {
428 draw_trigger(channel_selection_loc, i);
429 }
408 } 430 }
409} 431}
410 432
diff --git a/src/main.c b/src/main.c
index 3ddfaac..cff3afa 100644
--- a/src/main.c
+++ b/src/main.c
@@ -40,11 +40,14 @@ WITH REGARD TO THIS SOFTWARE.
40// + Improve "grey" cursor with dithering instead. 40// + Improve "grey" cursor with dithering instead.
41// + New channel icons. 41// + New channel icons.
42// + Update rcol UI and cursor management. 42// + Update rcol UI and cursor management.
43// + Fix bug: Copy/paste don't update the BMP drawing.
44// + Blank patterns could show up as empty on the pattern view for better
45// separation and section organization.
46// + Pattern can be cleared on pattern view with (SEL + L + R). This is
47// reversible with the same combination unless the pattern is modified.
43// - Improve SRAM saving to make room for longer patterns and/or more banks. 48// - Improve SRAM saving to make room for longer patterns and/or more banks.
44// - Make sure there is an ALL notification when modifying channel params so 49// - Make sure there is an ALL notification when modifying channel params so
45// that it's clear it's affecting all triggers. 50// that it's clear it's affecting all triggers.
46// - Blank patterns could show up as empty on the pattern view for better
47// separation and section organization.
48// - Scale mode for entering notes. 51// - Scale mode for entering notes.
49// - Add CLEAR ALL to the settings menu. 52// - Add CLEAR ALL to the settings menu.
50// - Higher resolution clock to allow for microtiming and more accurate tempo. 53// - Higher resolution clock to allow for microtiming and more accurate tempo.
@@ -60,9 +63,6 @@ WITH REGARD TO THIS SOFTWARE.
60// - Add CREDITS to the documentation for now, should probably be a menu item 63// - Add CREDITS to the documentation for now, should probably be a menu item
61// later. 64// later.
62// 65//
63// BUGS
64//
65// - Copy/paste don't update the BMP drawing.
66 66
67// NOTE: (by catbeats) 67// NOTE: (by catbeats)
68// 68//
diff --git a/src/patterns.c b/src/patterns.c
index 621eb14..a1f8f24 100644
--- a/src/patterns.c
+++ b/src/patterns.c
@@ -64,6 +64,7 @@ typedef struct Pattern {
64 ChannelNoise ch4; 64 ChannelNoise ch4;
65 int bpm; 65 int bpm;
66 u8 bank; 66 u8 bank;
67 bool empty;
67} Pattern; 68} Pattern;
68 69
69// 70//
@@ -233,14 +234,14 @@ const ChannelNoise default_ch4 = {
233const int default_bpm = 90; 234const int default_bpm = 90;
234 235
235static Pattern patterns[8] = { 236static Pattern patterns[8] = {
236 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, 237 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0, true},
237 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, 238 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0, true},
238 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, 239 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0, true},
239 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, 240 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0, true},
240 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, 241 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0, true},
241 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, 242 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0, true},
242 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, 243 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0, true},
243 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, 244 {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0, true},
244}; 245};
245 246
246static ChannelSquareParams ch1_params = {8, 4, 0, 2, 0, 0, 0, PROB_100, 0}; 247static ChannelSquareParams ch1_params = {8, 4, 0, 2, 0, 0, 0, PROB_100, 0};
diff --git a/src/sequencer.c b/src/sequencer.c
index 4e3802f..3110c74 100644
--- a/src/sequencer.c
+++ b/src/sequencer.c
@@ -20,6 +20,22 @@ bool update_bpm = false;
20u8 bar_counter = 0; 20u8 bar_counter = 0;
21 21
22void 22void
23clear_pattern(size_t idx) {
24 Pattern *pat = &patterns[idx];
25 *pat = (Pattern){default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0, false};
26 for (size_t i = 0; i < 16; i++) {
27 pat->ch1.notes[i] = (TriggerNote){false, NOTE_C_4};
28 pat->ch2.notes[i] = (TriggerNote){false, NOTE_C_4};
29 pat->ch3.notes[i] = (TriggerNote){false, NOTE_C_4};
30 pat->ch4.notes[i] = (TriggerNote){false, NOTE_C_4};
31 }
32 redraw_pattern_buttons = true;
33 redraw_channels = true;
34 redraw_trigs = true;
35 redraw_bpm = true;
36}
37
38void
23gate_off(void) { 39gate_off(void) {
24 SIO_MODE = SIO_MODE_GP 40 SIO_MODE = SIO_MODE_GP
25 | SIO_SC_OUT(1) 41 | SIO_SC_OUT(1)
@@ -117,7 +133,7 @@ play_step(void) {
117 } 133 }
118 chain.playing = true; 134 chain.playing = true;
119 } 135 }
120 if (pat->ch1.active) { 136 if (pat->ch1.active && !pat->empty) {
121 TriggerNote *trig = &pat->ch1.notes[step_counter]; 137 TriggerNote *trig = &pat->ch1.notes[step_counter];
122 ChannelSquareParams *params = &pat->ch1.params[step_counter]; 138 ChannelSquareParams *params = &pat->ch1.params[step_counter];
123 if (trig->active && should_play(params->prob)) { 139 if (trig->active && should_play(params->prob)) {
@@ -162,7 +178,7 @@ play_step(void) {
162 SOUND_SQUARE1_FREQ = SOUND_FREQ_RESET; 178 SOUND_SQUARE1_FREQ = SOUND_FREQ_RESET;
163 SOUND_SQUARE1_CTRL = 0; 179 SOUND_SQUARE1_CTRL = 0;
164 } 180 }
165 if (pat->ch2.active) { 181 if (pat->ch2.active && !pat->empty) {
166 TriggerNote *trig = &pat->ch2.notes[step_counter]; 182 TriggerNote *trig = &pat->ch2.notes[step_counter];
167 ChannelSquareParams *params = &pat->ch2.params[step_counter]; 183 ChannelSquareParams *params = &pat->ch2.params[step_counter];
168 if (trig->active && should_play(params->prob)) { 184 if (trig->active && should_play(params->prob)) {
@@ -188,7 +204,7 @@ play_step(void) {
188 SOUND_SQUARE2_FREQ = SOUND_FREQ_RESET; 204 SOUND_SQUARE2_FREQ = SOUND_FREQ_RESET;
189 SOUND_SQUARE2_CTRL = 0; 205 SOUND_SQUARE2_CTRL = 0;
190 } 206 }
191 if (pat->ch3.active) { 207 if (pat->ch3.active && !pat->empty) {
192 TriggerNote *trig = &pat->ch3.notes[step_counter]; 208 TriggerNote *trig = &pat->ch3.notes[step_counter];
193 ChannelWaveParams *params = &pat->ch3.params[step_counter]; 209 ChannelWaveParams *params = &pat->ch3.params[step_counter];
194 if (trig->active && should_play(params->prob)) { 210 if (trig->active && should_play(params->prob)) {
@@ -237,7 +253,7 @@ play_step(void) {
237 SOUND_WAVE_FREQ = SOUND_FREQ_RESET; 253 SOUND_WAVE_FREQ = SOUND_FREQ_RESET;
238 SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; 254 SOUND_WAVE_CTRL = SOUND_WAVE_MUTE;
239 } 255 }
240 if (pat->ch4.active) { 256 if (pat->ch4.active && !pat->empty) {
241 TriggerNote *trig = &pat->ch4.notes[step_counter]; 257 TriggerNote *trig = &pat->ch4.notes[step_counter];
242 ChannelNoiseParams *params = &pat->ch4.params[step_counter]; 258 ChannelNoiseParams *params = &pat->ch4.params[step_counter];
243 if (trig->active && should_play(params->prob)) { 259 if (trig->active && should_play(params->prob)) {
@@ -347,6 +363,9 @@ handle_channel_selection(void) {
347 if (key_hold(KEY_SELECT)) { 363 if (key_hold(KEY_SELECT)) {
348 clipboard_copy(); 364 clipboard_copy();
349 } else { 365 } else {
366 if (patterns[pattern_selection_loc].empty) {
367 clear_pattern(pattern_selection_loc);
368 }
350 switch (channel_selection_loc) { 369 switch (channel_selection_loc) {
351 case 0: { pat->ch1.active ^= 1; } break; 370 case 0: { pat->ch1.active ^= 1; } break;
352 case 1: { pat->ch2.active ^= 1; } break; 371 case 1: { pat->ch2.active ^= 1; } break;
@@ -358,6 +377,10 @@ handle_channel_selection(void) {
358 } else if (key_tap(KEY_A)) { 377 } else if (key_tap(KEY_A)) {
359 if (key_hold(KEY_SELECT)) { 378 if (key_hold(KEY_SELECT)) {
360 clipboard_paste(); 379 clipboard_paste();
380 redraw_bpm = true;
381 redraw_trigs = true;
382 redraw_channels = true;
383 redraw_pattern_buttons = true;
361 } else { 384 } else {
362 switch (channel_selection_loc) { 385 switch (channel_selection_loc) {
363 case 0: { input_handler = handle_param_selection_ch1; } break; 386 case 0: { input_handler = handle_param_selection_ch1; } break;
@@ -645,6 +668,9 @@ handle_right_col_selection(void) {
645 set_time(patterns[current_pattern].bpm); 668 set_time(patterns[current_pattern].bpm);
646 } 669 }
647 redraw_bpm = true; 670 redraw_bpm = true;
671 if (patterns[pattern_selection_loc].empty) {
672 clear_pattern(pattern_selection_loc);
673 }
648 } break; 674 } break;
649 // TODO: Scale. 675 // TODO: Scale.
650 } 676 }
@@ -664,6 +690,9 @@ handle_right_col_selection(void) {
664 set_time(patterns[current_pattern].bpm); 690 set_time(patterns[current_pattern].bpm);
665 } 691 }
666 redraw_bpm = true; 692 redraw_bpm = true;
693 if (patterns[pattern_selection_loc].empty) {
694 clear_pattern(pattern_selection_loc);
695 }
667 } break; 696 } break;
668 // TODO: Scale. 697 // TODO: Scale.
669 } 698 }
@@ -748,20 +777,32 @@ handle_pattern_chain(void) {
748 777
749void 778void
750handle_pattern_selection(void) { 779handle_pattern_selection(void) {
751 if (key_tap(KEY_B)) { 780 if (key_hold(KEY_SELECT)) {
752 if (key_hold(KEY_SELECT)) { 781 if ((key_pressed(KEY_L) && key_tap(KEY_R))
782 || (key_pressed(KEY_R) && key_tap(KEY_L))) {
783 patterns[pattern_selection_loc].empty ^= 1;
784 redraw_pattern_buttons = true;
785 redraw_channels = true;
786 redraw_trigs = true;
787 }
788 if (key_tap(KEY_B)) {
753 clipboard_copy(); 789 clipboard_copy();
754 } else { 790 }
755 next_pattern = pattern_selection_loc; 791 if (key_tap(KEY_A)) {
792 clipboard_paste();
793 redraw_bpm = true;
794 redraw_trigs = true;
795 redraw_channels = true;
756 redraw_pattern_buttons = true; 796 redraw_pattern_buttons = true;
757 } 797 }
798 return;
799 }
800 if (key_tap(KEY_B)) {
801 next_pattern = pattern_selection_loc;
802 redraw_pattern_buttons = true;
758 } 803 }
759 if (key_tap(KEY_A)) { 804 if (key_tap(KEY_A)) {
760 if (key_hold(KEY_SELECT)) { 805 input_handler = handle_pattern_chain;
761 clipboard_paste();
762 } else {
763 input_handler = handle_pattern_chain;
764 }
765 } 806 }
766 if (key_tap(KEY_RIGHT)) { 807 if (key_tap(KEY_RIGHT)) {
767 input_handler = handle_channel_selection; 808 input_handler = handle_channel_selection;
@@ -1214,11 +1255,14 @@ void
1214handle_trigger_selection(void) { 1255handle_trigger_selection(void) {
1215 TriggerNote *trig = get_current_trig(); 1256 TriggerNote *trig = get_current_trig();
1216 1257
1258 bool empty = patterns[pattern_selection_loc].empty;
1217 if (key_tap(KEY_B)) { 1259 if (key_tap(KEY_B)) {
1218 if (key_hold(KEY_SELECT)) { 1260 if (key_hold(KEY_SELECT)) {
1219 clipboard_copy(); 1261 clipboard_copy();
1220 } else { 1262 } else {
1221 // Toggle trigger. 1263 if (empty) {
1264 clear_pattern(pattern_selection_loc);
1265 }
1222 trig->active ^= 1; 1266 trig->active ^= 1;
1223 redraw_trigs = true; 1267 redraw_trigs = true;
1224 } 1268 }
@@ -1228,7 +1272,7 @@ handle_trigger_selection(void) {
1228 inc = -12; 1272 inc = -12;
1229 } 1273 }
1230 // Decrease note. 1274 // Decrease note.
1231 if (trig->active) { 1275 if (trig->active && !empty) {
1232 trig->note = MAX((s32)trig->note + inc, (s32)NOTE_C_2); 1276 trig->note = MAX((s32)trig->note + inc, (s32)NOTE_C_2);
1233 } 1277 }
1234 redraw_trigs = true; 1278 redraw_trigs = true;
@@ -1239,7 +1283,7 @@ handle_trigger_selection(void) {
1239 inc = 12; 1283 inc = 12;
1240 } 1284 }
1241 // Increase note. 1285 // Increase note.
1242 if (trig->active) { 1286 if (trig->active && !empty) {
1243 trig->note = MIN((s32)trig->note + inc, (s32)NOTE_C_8 - 1); 1287 trig->note = MIN((s32)trig->note + inc, (s32)NOTE_C_8 - 1);
1244 } 1288 }
1245 redraw_trigs = true; 1289 redraw_trigs = true;
@@ -1275,6 +1319,10 @@ handle_trigger_selection(void) {
1275 } else if (key_tap(KEY_A)) { 1319 } else if (key_tap(KEY_A)) {
1276 if (key_hold(KEY_SELECT)) { 1320 if (key_hold(KEY_SELECT)) {
1277 clipboard_paste(); 1321 clipboard_paste();
1322 redraw_bpm = true;
1323 redraw_trigs = true;
1324 redraw_channels = true;
1325 redraw_pattern_buttons = true;
1278 } else { 1326 } else {
1279 // Switch to parameter selection. 1327 // Switch to parameter selection.
1280 switch (channel_selection_loc) { 1328 switch (channel_selection_loc) {