aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2023-04-05 11:37:49 +0200
committerBad Diode <bd@badd10de.dev>2023-04-05 11:37:49 +0200
commit76be354cf2d924a8cbdd7fb4ac4f6b9c1546762f (patch)
treeb2793691afb8592382e5b439ffa20c604a480b21
parentb252d394036673d2ed33a8516304e8b4f7a732d2 (diff)
downloadstepper-76be354cf2d924a8cbdd7fb4ac4f6b9c1546762f.tar.gz
stepper-76be354cf2d924a8cbdd7fb4ac4f6b9c1546762f.zip
Add clipboard copy/paste
-rw-r--r--Makefile2
-rw-r--r--src/sequencer.c333
2 files changed, 326 insertions, 9 deletions
diff --git a/Makefile b/Makefile
index 369ad26..0310567 100644
--- a/Makefile
+++ b/Makefile
@@ -44,7 +44,7 @@ CFLAGS += $(INC_FLAGS)
44CFLAGS += $(CONFIG) 44CFLAGS += $(CONFIG)
45LDFLAGS := $(ARCH) $(SPECS) 45LDFLAGS := $(ARCH) $(SPECS)
46LDLIBS := $(LIBGBA) 46LDLIBS := $(LIBGBA)
47RELEASE_CFLAGS := -DNDEBUG -O3 47RELEASE_CFLAGS := -DNDEBUG -O2
48DEBUG_CFLAGS := -DDEBUG -O2 -g 48DEBUG_CFLAGS := -DDEBUG -O2 -g
49 49
50.PHONY: clean run 50.PHONY: clean run
diff --git a/src/sequencer.c b/src/sequencer.c
index 4b83f5f..6944b39 100644
--- a/src/sequencer.c
+++ b/src/sequencer.c
@@ -15,9 +15,6 @@ sram_write(u8 *src, u16 pos, u16 n_bytes) {
15 } 15 }
16} 16}
17 17
18// TODO
19// - Preview sound keys?
20// - Copy paste trigs/notes/params
21void set_time(int bpm); 18void set_time(int bpm);
22 19
23// 20//
@@ -419,7 +416,6 @@ const ChannelNoise default_ch4 = {
419 {false, NOTE_E_6}, 416 {false, NOTE_E_6},
420 {false, NOTE_E_6}, 417 {false, NOTE_E_6},
421 {false, NOTE_E_6}, 418 {false, NOTE_E_6},
422 {false, NOTE_G_4},
423 }, 419 },
424 .params = { 420 .params = {
425 {0xF, 0x2, 0, 0}, 421 {0xF, 0x2, 0, 0},
@@ -2602,17 +2598,338 @@ handle_trigger_selection(void) {
2602 } 2598 }
2603} 2599}
2604 2600
2601typedef enum ClipboardType {
2602 CLIP_EMPTY,
2603 CLIP_TRIG,
2604 CLIP_PARAM_CH1,
2605 CLIP_PARAM_CH2,
2606 CLIP_PARAM_CH3,
2607 CLIP_PARAM_CH4,
2608 CLIP_PATTERN,
2609 CLIP_CHANNEL,
2610} ClipboardType;
2611
2612typedef struct Clipboard {
2613 ClipboardType type;
2614 u8 src_pat;
2615 u8 src_chan;
2616 u8 src_trig;
2617} Clipboard;
2618
2619static Clipboard clipboard = {CLIP_EMPTY, 0, 0, 0};
2620
2621void
2622clipboard_paste(void) {
2623 Pattern *pat_dst = &patterns[pattern_selection_loc];
2624 Pattern *pat_src = &patterns[clipboard.src_pat];
2625
2626 if (input_handler == handle_trigger_selection) {
2627 if (clipboard.type == CLIP_TRIG) {
2628 // Copy notes or parameters when applicable.
2629 switch (clipboard.src_chan) {
2630 case 0: {
2631 switch (channel_selection_loc) {
2632 case 0: {
2633 pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig];
2634 pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig];
2635 } break;
2636 case 1: {
2637 pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig];
2638 pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig];
2639 } break;
2640 case 2: {
2641 pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig];
2642 } break;
2643 case 3: {
2644 pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig];
2645 } break;
2646 }
2647 } break;
2648 case 1: {
2649 switch (channel_selection_loc) {
2650 case 0: {
2651 pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig];
2652 pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume;
2653 pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time;
2654 pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction;
2655 pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle;
2656 } break;
2657 case 1: {
2658 pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig];
2659 pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig];
2660 } break;
2661 case 2: {
2662 pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig];
2663 } break;
2664 case 3: {
2665 pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig];
2666 } break;
2667 }
2668 } break;
2669 case 2: {
2670 switch (channel_selection_loc) {
2671 case 0: {
2672 pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig];
2673 } break;
2674 case 1: {
2675 pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig];
2676 } break;
2677 case 2: {
2678 pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig];
2679 pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig];
2680 } break;
2681 case 3: {
2682 pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig];
2683 } break;
2684 }
2685 } break;
2686 case 3: {
2687 switch (channel_selection_loc) {
2688 case 0: {
2689 pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig];
2690 } break;
2691 case 1: {
2692 pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig];
2693 } break;
2694 case 2: {
2695 pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig];
2696 } break;
2697 case 3: {
2698 pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig];
2699 pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig];
2700 } break;
2701 }
2702 } break;
2703 }
2704 }
2705 // Only paste the params for the respective trigger.
2706 if (clipboard.type == CLIP_PARAM_CH1) {
2707 switch (channel_selection_loc) {
2708 case 0: {
2709 pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig];
2710 } break;
2711 case 1: {
2712 pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig];
2713 } break;
2714 }
2715 }
2716 if (clipboard.type == CLIP_PARAM_CH2) {
2717 switch (channel_selection_loc) {
2718 case 0: {
2719 pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume;
2720 pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time;
2721 pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction;
2722 pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle;
2723 } break;
2724 case 1: {
2725 pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig];
2726 } break;
2727 }
2728 }
2729 if (clipboard.type == CLIP_PARAM_CH3 && channel_selection_loc == clipboard.src_chan) {
2730 pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig];
2731 }
2732 if (clipboard.type == CLIP_PARAM_CH4 && channel_selection_loc == clipboard.src_chan) {
2733 pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig];
2734 }
2735 draw_triggers();
2736 draw_parameters();
2737 } else if (input_handler == handle_param_selection_sq1 && clipboard.type == CLIP_PARAM_CH1) {
2738 pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig];
2739 draw_parameters();
2740 } else if (input_handler == handle_param_selection_sq1 && clipboard.type == CLIP_PARAM_CH2) {
2741 pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume;
2742 pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time;
2743 pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction;
2744 pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle;
2745 draw_parameters();
2746 } else if (input_handler == handle_param_selection_sq2 && clipboard.type == CLIP_PARAM_CH2) {
2747 pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig];
2748 draw_parameters();
2749 } else if (input_handler == handle_param_selection_sq2 && clipboard.type == CLIP_PARAM_CH1) {
2750 pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig];
2751 draw_parameters();
2752 } else if (input_handler == handle_param_selection_wave && clipboard.type == CLIP_PARAM_CH3) {
2753 pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig];
2754 draw_parameters();
2755 } else if (input_handler == handle_param_selection_noise && clipboard.type == CLIP_PARAM_CH4) {
2756 pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig];
2757 draw_parameters();
2758 } else if (input_handler == handle_channel_selection && clipboard.type == CLIP_CHANNEL) {
2759 // Copy notes from a different channel OR notes and parameters
2760 // from a different pattern.
2761 if (clipboard.src_chan == channel_selection_loc) {
2762 switch (clipboard.src_chan) {
2763 case 0: { pat_dst->ch1 = pat_src->ch1; } break;
2764 case 1: { pat_dst->ch2 = pat_src->ch2; } break;
2765 case 2: { pat_dst->ch3 = pat_src->ch3; } break;
2766 case 3: { pat_dst->ch4 = pat_src->ch4; } break;
2767 }
2768 } else {
2769 switch (clipboard.src_chan) {
2770 case 0: {
2771 switch (channel_selection_loc) {
2772 case 0: {
2773 for (size_t i = 0; i < 16; i++) {
2774 pat_dst->ch1.notes[i] = pat_src->ch1.notes[i];
2775 }
2776 } break;
2777 case 1: {
2778 for (size_t i = 0; i < 16; i++) {
2779 pat_dst->ch2.notes[i] = pat_src->ch1.notes[i];
2780 }
2781 } break;
2782 case 2: {
2783 for (size_t i = 0; i < 16; i++) {
2784 pat_dst->ch3.notes[i] = pat_src->ch1.notes[i];
2785 }
2786 } break;
2787 case 3: {
2788 for (size_t i = 0; i < 16; i++) {
2789 pat_dst->ch4.notes[i] = pat_src->ch1.notes[i];
2790 }
2791 } break;
2792 }
2793 } break;
2794 case 1: {
2795 switch (channel_selection_loc) {
2796 case 0: {
2797 for (size_t i = 0; i < 16; i++) {
2798 pat_dst->ch1.notes[i] = pat_src->ch2.notes[i];
2799 }
2800 } break;
2801 case 1: {
2802 for (size_t i = 0; i < 16; i++) {
2803 pat_dst->ch2.notes[i] = pat_src->ch2.notes[i];
2804 }
2805 } break;
2806 case 2: {
2807 for (size_t i = 0; i < 16; i++) {
2808 pat_dst->ch3.notes[i] = pat_src->ch2.notes[i];
2809 }
2810 } break;
2811 case 3: {
2812 for (size_t i = 0; i < 16; i++) {
2813 pat_dst->ch4.notes[i] = pat_src->ch2.notes[i];
2814 }
2815 } break;
2816 }
2817 } break;
2818 case 2: {
2819 switch (channel_selection_loc) {
2820 case 0: {
2821 for (size_t i = 0; i < 16; i++) {
2822 pat_dst->ch1.notes[i] = pat_src->ch3.notes[i];
2823 }
2824 } break;
2825 case 1: {
2826 for (size_t i = 0; i < 16; i++) {
2827 pat_dst->ch2.notes[i] = pat_src->ch3.notes[i];
2828 }
2829 } break;
2830 case 2: {
2831 for (size_t i = 0; i < 16; i++) {
2832 pat_dst->ch3.notes[i] = pat_src->ch3.notes[i];
2833 }
2834 } break;
2835 case 3: {
2836 for (size_t i = 0; i < 16; i++) {
2837 pat_dst->ch4.notes[i] = pat_src->ch3.notes[i];
2838 }
2839 } break;
2840 }
2841 } break;
2842 case 3: {
2843 switch (channel_selection_loc) {
2844 case 0: {
2845 for (size_t i = 0; i < 16; i++) {
2846 pat_dst->ch1.notes[i] = pat_src->ch4.notes[i];
2847 }
2848 } break;
2849 case 1: {
2850 for (size_t i = 0; i < 16; i++) {
2851 pat_dst->ch2.notes[i] = pat_src->ch4.notes[i];
2852 }
2853 } break;
2854 case 2: {
2855 for (size_t i = 0; i < 16; i++) {
2856 pat_dst->ch3.notes[i] = pat_src->ch4.notes[i];
2857 }
2858 } break;
2859 case 3: {
2860 for (size_t i = 0; i < 16; i++) {
2861 pat_dst->ch4.notes[i] = pat_src->ch4.notes[i];
2862 }
2863 } break;
2864 }
2865 } break;
2866 }
2867 }
2868 draw_channels();
2869 draw_triggers();
2870 } else if (input_handler == handle_pattern_selection && clipboard.type == CLIP_PATTERN) {
2871 // Copy an entire pattern.
2872 if (pattern_selection_loc != clipboard.src_pat) {
2873 *pat_dst = *pat_src;
2874 draw_channels();
2875 draw_triggers();
2876 }
2877 }
2878}
2879
2880void
2881clipboard_copy(void) {
2882 if (input_handler == handle_trigger_selection) {
2883 clipboard.type = CLIP_TRIG;
2884 clipboard.src_pat = pattern_selection_loc;
2885 clipboard.src_chan = channel_selection_loc;
2886 clipboard.src_trig = trig_selection_loc;
2887 } else if (input_handler == handle_param_selection_sq1) {
2888 clipboard.type = CLIP_PARAM_CH1;
2889 clipboard.src_pat = pattern_selection_loc;
2890 clipboard.src_chan = channel_selection_loc;
2891 clipboard.src_trig = trig_selection_loc;
2892 } else if (input_handler == handle_param_selection_sq2) {
2893 clipboard.type = CLIP_PARAM_CH2;
2894 clipboard.src_pat = pattern_selection_loc;
2895 clipboard.src_chan = channel_selection_loc;
2896 clipboard.src_trig = trig_selection_loc;
2897 } else if (input_handler == handle_param_selection_wave) {
2898 clipboard.type = CLIP_PARAM_CH3;
2899 clipboard.src_pat = pattern_selection_loc;
2900 clipboard.src_chan = channel_selection_loc;
2901 clipboard.src_trig = trig_selection_loc;
2902 } else if (input_handler == handle_param_selection_noise) {
2903 clipboard.type = CLIP_PARAM_CH4;
2904 clipboard.src_pat = pattern_selection_loc;
2905 clipboard.src_chan = channel_selection_loc;
2906 clipboard.src_trig = trig_selection_loc;
2907 } else if (input_handler == handle_channel_selection) {
2908 clipboard.type = CLIP_CHANNEL;
2909 clipboard.src_pat = pattern_selection_loc;
2910 clipboard.src_chan = channel_selection_loc;
2911 } else if (input_handler == handle_pattern_selection) {
2912 clipboard.type = CLIP_PATTERN;
2913 clipboard.src_pat = pattern_selection_loc;
2914 }
2915}
2916
2605void 2917void
2606handle_sequencer_input(void) { 2918handle_sequencer_input(void) {
2607 poll_keys(); 2919 poll_keys();
2608 input_handler();
2609 2920
2610 if (key_tap(KEY_START)) { 2921 if (key_tap(KEY_START)) {
2611 // Stop the sequencer or start playing from the beginning. 2922 // Stop the sequencer or start playing from the beginning.
2612 toggle_playing(); 2923 toggle_playing();
2613 } else if (key_tap(KEY_SELECT)) { 2924 } else if (key_hold(KEY_SELECT)) {
2614 // Play/pause. 2925 // Clipboard combo.
2615 pause_playing(); 2926 if (key_tap(KEY_A)) {
2927 clipboard_paste();
2928 } else if (key_tap(KEY_B)){
2929 clipboard_copy();
2930 }
2931 } else {
2932 input_handler();
2616 } 2933 }
2617} 2934}
2618 2935