|
3 | 3 | // all UI components are programmed as PageComponent to be reused on different pages
|
4 | 4 | //
|
5 | 5 |
|
6 |
| -// used by page module for themed function button display |
7 |
| -void functionDrawCallback(const char * f1, const char * f2, uint8_t f1_state, uint8_t f2_state) |
8 |
| -{ |
9 |
| - // menu action |
10 |
| - uCtrl.oled->print(f1, 8, 1+((14-strlen(f1))/2)); |
11 |
| - uCtrl.oled->print(f2, 8, 14+((14-strlen(f2))/2)); |
12 |
| - // state variation |
13 |
| - if (f1_state == 1) { |
14 |
| - uCtrl.oled->display->drawBox(0, 57, 63, 7); |
15 |
| - } |
16 |
| - if (f2_state == 1) { |
17 |
| - uCtrl.oled->display->drawBox(64, 57, 64, 7); |
18 |
| - } |
19 |
| - // horizontal line |
20 |
| - uCtrl.oled->display->drawBox(0, 56, 128, 1); |
21 |
| - // vertical separator |
22 |
| - //uCtrl.oled->display->drawBox(64, 59, 1, 5); |
23 |
| -} |
24 |
| - |
25 | 6 | // for a 1x grid 1x line size
|
26 | 7 | // large == true ? 2x grid size
|
27 | 8 | // used by a lot of simple components
|
@@ -238,6 +219,183 @@ struct TopBar : PageComponent {
|
238 | 219 | }
|
239 | 220 | } topBarComponent;
|
240 | 221 |
|
| 222 | +// pattern data for mute automation grid |
| 223 | +typedef struct |
| 224 | +{ |
| 225 | + uint16_t map_303 = 0xFFFF; |
| 226 | + uint16_t map_808[TRACK_NUMBER_808] = {0xFFFF}; |
| 227 | +} MUTE_PATTERN; |
| 228 | + |
| 229 | +// the control knob will handle midi cc for the last selected midi cc of selected track inside this component |
| 230 | +struct MutePatternControl : PageComponent { |
| 231 | + |
| 232 | + // layout: |
| 233 | + // mute automation pattern style driven |
| 234 | + // 4x16 grid style: |
| 235 | + // last 4 grids are mute automation clip change/status |
| 236 | + // 12 grids are for mute elements(on 808 max 12, for 303 1 big mute slot) |
| 237 | + // f1 = << (...) |
| 238 | + // f2 = >> (...) |
| 239 | + // this will be extended in the future to allow more patterns to be used, 4 for now! |
| 240 | + MUTE_PATTERN mute_pattern[4]; |
| 241 | + uint8_t current_pattern = 0; |
| 242 | + uint8_t selected_pattern = 0; |
| 243 | + uint8_t selected_mute_chn = 0; |
| 244 | + |
| 245 | + MutePatternControl() |
| 246 | + { |
| 247 | + // we want this component to be 4 lines max and 16 grids navigable object |
| 248 | + line_size = 4; |
| 249 | + grid_size = 2; |
| 250 | + // enable more complex nav by updating selector pointer of page component |
| 251 | + update_selector = true; |
| 252 | + } |
| 253 | + |
| 254 | + void view() { |
| 255 | + |
| 256 | + // update voice info for 808 |
| 257 | + //... |
| 258 | + |
| 259 | + // mute pattern |
| 260 | + for (uint8_t i=0; i < 4; i++) { |
| 261 | + // mute grid |
| 262 | + // 303 |
| 263 | + if (AcidSequencer.is303(_selected_track)) { |
| 264 | + // print as not muted |
| 265 | + uCtrl.oled->drawBox(y+(i*8), x, 6, 86, i+1 == selected_line ? true : false); |
| 266 | + // if muted then just empty the box for ui feedback |
| 267 | + if (!GET_BIT(mute_pattern[i].map_303, _selected_track)) { |
| 268 | + uCtrl.oled->drawBox(y+(i*8)+1, x+1, 4, 84, i+1 == selected_line ? true : false); |
| 269 | + } |
| 270 | + // 808 |
| 271 | + } else { |
| 272 | + uint8_t block_size = ceil(86.00 / VOICE_MAX_SIZE_808); |
| 273 | + for (uint8_t j=0; j < VOICE_MAX_SIZE_808; j++) { |
| 274 | + uCtrl.oled->drawBox(y+(i*8), x+(j*block_size), 6, block_size-2, i+1 == selected_line && j == selected_mute_chn ? true : false); |
| 275 | + if (!GET_BIT(mute_pattern[i].map_808[_selected_track-TRACK_NUMBER_303], j)) { |
| 276 | + uCtrl.oled->drawBox(y+(i*8)+1, x+(j*block_size)+1, 4, block_size-4, i+1 == selected_line && j == selected_mute_chn ? true : false); |
| 277 | + } |
| 278 | + } |
| 279 | + } |
| 280 | + |
| 281 | + // pattern selector status |
| 282 | + uCtrl.oled->print(i+1, line+i, 22, i+1 == selected_line ? true : false); |
| 283 | + if (current_pattern == i) |
| 284 | + uCtrl.oled->print("<", line+i, 25); |
| 285 | + } |
| 286 | + |
| 287 | + // track info |
| 288 | + if (AcidSequencer.is303(_selected_track)) { |
| 289 | + //if (AcidSequencer.stepOn(_selected_track, AcidSequencer.getCurrentStep(_selected_track)) && _playing) { |
| 290 | + // uCtrl.oled->print(AcidSequencer.getNoteString(AcidSequencer.getStepData(_selected_track, AcidSequencer.getCurrentStep(_selected_track))), line+4, 1); |
| 291 | + //} |
| 292 | + // 808 |
| 293 | + } else { |
| 294 | + uCtrl.oled->print(AcidSequencer.getTrackVoiceName(_selected_track, AcidSequencer.getTrackVoice(_selected_track)), line+4, 1); |
| 295 | + } |
| 296 | + |
| 297 | + setF1("<<"); |
| 298 | + setF2(">>"); |
| 299 | + } |
| 300 | + |
| 301 | + void nav(uint8_t dir) { |
| 302 | + |
| 303 | + switch (dir) { |
| 304 | + case LEFT: |
| 305 | + if (selected_mute_chn == 0) { |
| 306 | + selected_mute_chn = AcidSequencer.is303(_selected_track) ? 0 : VOICE_MAX_SIZE_808-1; |
| 307 | + } else { |
| 308 | + --selected_mute_chn; |
| 309 | + } |
| 310 | + if (!AcidSequencer.is303(_selected_track)) { |
| 311 | + AcidSequencer.setTrackVoice(_selected_track, selected_mute_chn); |
| 312 | + } |
| 313 | + break; |
| 314 | + case RIGHT: |
| 315 | + if (selected_mute_chn == VOICE_MAX_SIZE_808-1) { |
| 316 | + selected_mute_chn = 0; |
| 317 | + } else { |
| 318 | + ++selected_mute_chn; |
| 319 | + } |
| 320 | + if (!AcidSequencer.is303(_selected_track)) { |
| 321 | + AcidSequencer.setTrackVoice(_selected_track, selected_mute_chn); |
| 322 | + } |
| 323 | + break; |
| 324 | + } |
| 325 | + |
| 326 | + } |
| 327 | + |
| 328 | + void change(int16_t data) { |
| 329 | + // INCREMENT -1 |
| 330 | + // DECREMENT -2 |
| 331 | + // INCREMENT_SECONDARY -3 |
| 332 | + // DECREMENT_SECONDARY -4 |
| 333 | + uint8_t pattern = selected_line-1; |
| 334 | + if (data == INCREMENT) { |
| 335 | + if (AcidSequencer.is303(_selected_track)) { |
| 336 | + SET_BIT(mute_pattern[pattern].map_303, _selected_track); |
| 337 | + } else { |
| 338 | + SET_BIT(mute_pattern[pattern].map_808[_selected_track-TRACK_NUMBER_303], selected_mute_chn); |
| 339 | + } |
| 340 | + // selected is same as current? update tracks mute state |
| 341 | + if (pattern == current_pattern) |
| 342 | + changePattern(current_pattern); |
| 343 | + } else if (data == DECREMENT) { |
| 344 | + if (AcidSequencer.is303(_selected_track)) { |
| 345 | + CLR_BIT(mute_pattern[pattern].map_303, _selected_track); |
| 346 | + } else { |
| 347 | + CLR_BIT(mute_pattern[pattern].map_808[_selected_track-TRACK_NUMBER_303], selected_mute_chn); |
| 348 | + } |
| 349 | + if (pattern == current_pattern) |
| 350 | + changePattern(current_pattern); |
| 351 | + } |
| 352 | + } |
| 353 | + |
| 354 | + void function1() { |
| 355 | + current_pattern = --current_pattern % 4; |
| 356 | + changePattern(current_pattern); |
| 357 | + } |
| 358 | + |
| 359 | + void function2() { |
| 360 | + current_pattern = ++current_pattern % 4; |
| 361 | + changePattern(current_pattern); |
| 362 | + } |
| 363 | + |
| 364 | + void changePattern(uint8_t pattern) { |
| 365 | + // apply mute schema to 303s |
| 366 | + for (uint8_t i=0; i < TRACK_NUMBER_303; i++) { |
| 367 | + AcidSequencer.setMute(i, !GET_BIT(mute_pattern[pattern].map_303, i)); |
| 368 | + } |
| 369 | + |
| 370 | + // apply mute schema to 808s |
| 371 | + for (uint8_t i=0; i < TRACK_NUMBER_808; i++) { |
| 372 | + for (uint8_t j=0; j < VOICE_MAX_SIZE_808; j++) { |
| 373 | + AcidSequencer.setMute(i+TRACK_NUMBER_303, j, !GET_BIT(mute_pattern[pattern].map_808[i], j)); |
| 374 | + } |
| 375 | + } |
| 376 | + } |
| 377 | + |
| 378 | + void updateCurrentMuteState(uint8_t track, uint8_t voice = 0) |
| 379 | + { |
| 380 | + if (AcidSequencer.is303(track)) { |
| 381 | + if (AcidSequencer.getMute(track)) { |
| 382 | + CLR_BIT(mute_pattern[current_pattern].map_303, track); |
| 383 | + } else { |
| 384 | + SET_BIT(mute_pattern[current_pattern].map_303, track); |
| 385 | + } |
| 386 | + } else { |
| 387 | + if (AcidSequencer.getMute(track, AcidSequencer.getTrackVoice(track))) { |
| 388 | + CLR_BIT(mute_pattern[current_pattern].map_808[track-TRACK_NUMBER_303], voice); |
| 389 | + } else { |
| 390 | + SET_BIT(mute_pattern[current_pattern].map_808[track-TRACK_NUMBER_303], voice); |
| 391 | + } |
| 392 | + // update to selected voice too |
| 393 | + selected_mute_chn = voice; |
| 394 | + } |
| 395 | + } |
| 396 | + |
| 397 | +} mutePatternComponent; |
| 398 | + |
241 | 399 | struct StepSequencer : PageComponent {
|
242 | 400 |
|
243 | 401 | // step sequencer theme editor
|
@@ -383,7 +541,7 @@ struct StepSequencer : PageComponent {
|
383 | 541 | // f1 and f2
|
384 | 542 | // Selectors
|
385 | 543 | if (selected_line == 1) {
|
386 |
| - setF1("mute", AcidSequencer.getMute(_selected_track)); |
| 544 | + setF1("mute", AcidSequencer.is303(_selected_track) ? AcidSequencer.getMute(_selected_track) : AcidSequencer.getMute(_selected_track, AcidSequencer.getTrackVoice(_selected_track))); |
387 | 545 | // you can only paste if the selected selector is cleared otherwise it will shows copy
|
388 | 546 | setF2("clear");
|
389 | 547 | // Steps
|
@@ -600,8 +758,15 @@ struct StepSequencer : PageComponent {
|
600 | 758 |
|
601 | 759 | void function1() {
|
602 | 760 | if (selected_line == 1) {
|
603 |
| - // mute track(subtrack for 808) |
604 |
| - AcidSequencer.setMute(_selected_track, !AcidSequencer.getMute(_selected_track)); |
| 761 | + // mute track(voice for 808) |
| 762 | + if (AcidSequencer.is303(_selected_track)) { |
| 763 | + AcidSequencer.setMute(_selected_track, !AcidSequencer.getMute(_selected_track)); |
| 764 | + // keep mutePatternComponent updated for current mute state |
| 765 | + mutePatternComponent.updateCurrentMuteState(_selected_track); |
| 766 | + } else { |
| 767 | + AcidSequencer.setMute(_selected_track, AcidSequencer.getTrackVoice(_selected_track), !AcidSequencer.getMute(_selected_track, AcidSequencer.getTrackVoice(_selected_track))); |
| 768 | + mutePatternComponent.updateCurrentMuteState(_selected_track, AcidSequencer.getTrackVoice(_selected_track)); |
| 769 | + } |
605 | 770 | } else if (selected_line >= 2) {
|
606 | 771 | // 303 and 808 uses the same accent button f1
|
607 | 772 | AcidSequencer.setAccent(_selected_track, selected_step, !AcidSequencer.accentOn(_selected_track, selected_step));
|
@@ -638,41 +803,6 @@ struct StepSequencer : PageComponent {
|
638 | 803 |
|
639 | 804 | } stepSequencerComponent;
|
640 | 805 |
|
641 |
| -struct MutePatternControl : PageComponent { |
642 |
| - |
643 |
| - // layout: |
644 |
| - // mute automation pattern style driven |
645 |
| - // 4x16 grid style: |
646 |
| - // last 4 grids are mute automation clip change/status |
647 |
| - // 12 grids are for mute elements(on 808 max 12, for 303 1 big mute slot) |
648 |
| - |
649 |
| - MutePatternControl() |
650 |
| - { |
651 |
| - // we want this component to be 4 lines max and 16 grids navigable object |
652 |
| - line_size = 4; |
653 |
| - grid_size = 16; |
654 |
| - // enable more complex nav by updating selector pointer of page component |
655 |
| - update_selector = true; |
656 |
| - } |
657 |
| - |
658 |
| - void view() { |
659 |
| - |
660 |
| - } |
661 |
| - |
662 |
| - void change(int16_t data) { |
663 |
| - |
664 |
| - } |
665 |
| - |
666 |
| - void function1() { |
667 |
| - |
668 |
| - } |
669 |
| - |
670 |
| - void function2() { |
671 |
| - |
672 |
| - } |
673 |
| - |
674 |
| -} mutePatternComponent; |
675 |
| - |
676 | 806 | struct TrackLength : PageComponent {
|
677 | 807 |
|
678 | 808 | void view() {
|
@@ -914,3 +1044,55 @@ struct TrackFill : PageComponent {
|
914 | 1044 | }
|
915 | 1045 |
|
916 | 1046 | } fillComponent;
|
| 1047 | + |
| 1048 | +// general functions |
| 1049 | + |
| 1050 | +// used by page module for themed function button display |
| 1051 | +void functionDrawCallback(const char * f1, const char * f2, uint8_t f1_state, uint8_t f2_state) |
| 1052 | +{ |
| 1053 | + // menu action |
| 1054 | + uCtrl.oled->print(f1, 8, 1+((14-strlen(f1))/2)); |
| 1055 | + uCtrl.oled->print(f2, 8, 14+((14-strlen(f2))/2)); |
| 1056 | + // state variation |
| 1057 | + if (f1_state == 1) { |
| 1058 | + uCtrl.oled->display->drawBox(0, 57, 63, 7); |
| 1059 | + } |
| 1060 | + if (f2_state == 1) { |
| 1061 | + uCtrl.oled->display->drawBox(64, 57, 64, 7); |
| 1062 | + } |
| 1063 | + // horizontal line |
| 1064 | + uCtrl.oled->display->drawBox(0, 56, 128, 1); |
| 1065 | + // vertical separator |
| 1066 | + //uCtrl.oled->display->drawBox(64, 59, 1, 5); |
| 1067 | +} |
| 1068 | + |
| 1069 | +void playStop() |
| 1070 | +{ |
| 1071 | + if (_playing) |
| 1072 | + uClock.stop(); |
| 1073 | + else |
| 1074 | + uClock.start(); |
| 1075 | +} |
| 1076 | + |
| 1077 | +void tempoSetup() |
| 1078 | +{ |
| 1079 | + uCtrl.page->selectComponent(topBarComponent); |
| 1080 | +} |
| 1081 | + |
| 1082 | +void previousTrack() |
| 1083 | +{ |
| 1084 | + if (_selected_track == 0) { |
| 1085 | + _selected_track = AcidSequencer.getTrackNumber() - 1; |
| 1086 | + } else { |
| 1087 | + --_selected_track; |
| 1088 | + } |
| 1089 | +} |
| 1090 | + |
| 1091 | +void nextTrack() |
| 1092 | +{ |
| 1093 | + if (_selected_track == AcidSequencer.getTrackNumber() - 1) { |
| 1094 | + _selected_track = 0; |
| 1095 | + } else { |
| 1096 | + ++_selected_track; |
| 1097 | + } |
| 1098 | +} |
0 commit comments