diff options
author | Bad Diode <bd@badd10de.dev> | 2023-04-23 15:48:59 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2023-04-23 15:48:59 +0200 |
commit | ff6e784e7c5ebe223666c6c631305397ad358289 (patch) | |
tree | 0f87823d48366a6beb8d36d7eea5dc33663d7abd | |
parent | deb9c48fbd3dc5854de4ae3a04dc999029c10ae0 (diff) | |
download | stepper-ff6e784e7c5ebe223666c6c631305397ad358289.tar.gz stepper-ff6e784e7c5ebe223666c6c631305397ad358289.zip |
Start decoupling of rendering from update passes
-rw-r--r-- | src/assets.c | 129 | ||||
-rw-r--r-- | src/clipboard.c | 319 | ||||
-rw-r--r-- | src/drawing.c | 1237 | ||||
-rw-r--r-- | src/globals.c | 104 | ||||
-rw-r--r-- | src/main.c | 8 | ||||
-rw-r--r-- | src/patterns.c | 236 | ||||
-rw-r--r-- | src/profiling.c | 39 | ||||
-rw-r--r-- | src/renderer_m0.c | 78 | ||||
-rw-r--r-- | src/save.c | 37 | ||||
-rw-r--r-- | src/sequencer.c | 2079 |
10 files changed, 2170 insertions, 2096 deletions
diff --git a/src/assets.c b/src/assets.c new file mode 100644 index 0000000..74d27d2 --- /dev/null +++ b/src/assets.c | |||
@@ -0,0 +1,129 @@ | |||
1 | #ifndef STEPPER_ASSETS_C | ||
2 | #define STEPPER_ASSETS_C | ||
3 | |||
4 | // | ||
5 | // Sprites. | ||
6 | // | ||
7 | |||
8 | static const u32 note_name_sprites[] = { | ||
9 | 0xe0000000, 0xe0202020, 0x0e000000, 0x0e020e08, | ||
10 | 0x98000000, 0x98a8a8a8, 0x38000000, 0x390a3b20, | ||
11 | 0x60000000, 0x60a0a0a0, 0x0e000000, 0x0e020e08, | ||
12 | 0xb8000000, 0xb8889888, 0x38000000, 0x390a3b20, | ||
13 | 0xe0000000, 0xe0206020, 0x0e000000, 0x0e020e08, | ||
14 | 0xe0000000, 0x20206020, 0x0e000000, 0x0e020e08, | ||
15 | 0xb8000000, 0xb8a88888, 0x38000000, 0x390a3b20, | ||
16 | 0xe0000000, 0xe0a02020, 0x0e000000, 0x0e020e08, | ||
17 | 0xb8000000, 0xa8b8a8a8, 0x38000000, 0x390a3b20, | ||
18 | 0xe0000000, 0xa0e0a0a0, 0x0e000000, 0x0e020e08, | ||
19 | 0xb8000000, 0xb8a898a8, 0x38000000, 0x390a3b20, | ||
20 | 0xe0000000, 0xe0a060a0, 0x0e000000, 0x0e020e08, | ||
21 | 0xe0000000, 0xe0202020, 0x0e000000, 0x0e080c08, | ||
22 | 0x98000000, 0x98a8a8a8, 0x38000000, 0x39223320, | ||
23 | 0x60000000, 0x60a0a0a0, 0x0e000000, 0x0e080c08, | ||
24 | 0xb8000000, 0xb8889888, 0x38000000, 0x39223320, | ||
25 | 0xe0000000, 0xe0206020, 0x0e000000, 0x0e080c08, | ||
26 | 0xe0000000, 0x20206020, 0x0e000000, 0x0e080c08, | ||
27 | 0xb8000000, 0xb8a88888, 0x38000000, 0x39223320, | ||
28 | 0xe0000000, 0xe0a02020, 0x0e000000, 0x0e080c08, | ||
29 | 0xb8000000, 0xa8b8a8a8, 0x38000000, 0x39223320, | ||
30 | 0xe0000000, 0xa0e0a0a0, 0x0e000000, 0x0e080c08, | ||
31 | 0xb8000000, 0xb8a898a8, 0x38000000, 0x39223320, | ||
32 | 0xe0000000, 0xe0a060a0, 0x0e000000, 0x0e080c08, | ||
33 | 0xe0000000, 0xe0202020, 0x0a000000, 0x08080e0a, | ||
34 | 0x98000000, 0x98a8a8a8, 0x28000000, 0x21223b28, | ||
35 | 0x60000000, 0x60a0a0a0, 0x0a000000, 0x08080e0a, | ||
36 | 0xb8000000, 0xb8889888, 0x28000000, 0x21223b28, | ||
37 | 0xe0000000, 0xe0206020, 0x0a000000, 0x08080e0a, | ||
38 | 0xe0000000, 0x20206020, 0x0a000000, 0x08080e0a, | ||
39 | 0xb8000000, 0xb8a88888, 0x28000000, 0x21223b28, | ||
40 | 0xe0000000, 0xe0a02020, 0x0a000000, 0x08080e0a, | ||
41 | 0xb8000000, 0xa8b8a8a8, 0x28000000, 0x21223b28, | ||
42 | 0xe0000000, 0xa0e0a0a0, 0x0a000000, 0x08080e0a, | ||
43 | 0xb8000000, 0xb8a898a8, 0x28000000, 0x21223b28, | ||
44 | 0xe0000000, 0xe0a060a0, 0x0a000000, 0x08080e0a, | ||
45 | 0xe0000000, 0xe0202020, 0x0e000000, 0x0e080e02, | ||
46 | 0x98000000, 0x98a8a8a8, 0x38000000, 0x39223b08, | ||
47 | 0x60000000, 0x60a0a0a0, 0x0e000000, 0x0e080e02, | ||
48 | 0xb8000000, 0xb8889888, 0x38000000, 0x39223b08, | ||
49 | 0xe0000000, 0xe0206020, 0x0e000000, 0x0e080e02, | ||
50 | 0xe0000000, 0x20206020, 0x0e000000, 0x0e080e02, | ||
51 | 0xb8000000, 0xb8a88888, 0x38000000, 0x39223b08, | ||
52 | 0xe0000000, 0xe0a02020, 0x0e000000, 0x0e080e02, | ||
53 | 0xb8000000, 0xa8b8a8a8, 0x38000000, 0x39223b08, | ||
54 | 0xe0000000, 0xa0e0a0a0, 0x0e000000, 0x0e080e02, | ||
55 | 0xb8000000, 0xb8a898a8, 0x38000000, 0x39223b08, | ||
56 | 0xe0000000, 0xe0a060a0, 0x0e000000, 0x0e080e02, | ||
57 | 0xe0000000, 0xe0202020, 0x0e000000, 0x0e0a0e02, | ||
58 | 0x98000000, 0x98a8a8a8, 0x38000000, 0x392a3b08, | ||
59 | 0x60000000, 0x60a0a0a0, 0x0e000000, 0x0e0a0e02, | ||
60 | 0xb8000000, 0xb8889888, 0x38000000, 0x392a3b08, | ||
61 | 0xe0000000, 0xe0206020, 0x0e000000, 0x0e0a0e02, | ||
62 | 0xe0000000, 0x20206020, 0x0e000000, 0x0e0a0e02, | ||
63 | 0xb8000000, 0xb8a88888, 0x38000000, 0x392a3b08, | ||
64 | 0xe0000000, 0xe0a02020, 0x0e000000, 0x0e0a0e02, | ||
65 | 0xb8000000, 0xa8b8a8a8, 0x38000000, 0x392a3b08, | ||
66 | 0xe0000000, 0xa0e0a0a0, 0x0e000000, 0x0e0a0e02, | ||
67 | 0xb8000000, 0xb8a898a8, 0x38000000, 0x392a3b08, | ||
68 | 0xe0000000, 0xe0a060a0, 0x0e000000, 0x0e0a0e02, | ||
69 | 0xe0000000, 0xe0202020, 0x0e000000, 0x02020408, | ||
70 | 0x98000000, 0x98a8a8a8, 0x38000000, 0x090a1320, | ||
71 | 0x60000000, 0x60a0a0a0, 0x0e000000, 0x02020408, | ||
72 | 0xb8000000, 0xb8889888, 0x38000000, 0x090a1320, | ||
73 | 0xe0000000, 0xe0206020, 0x0e000000, 0x02020408, | ||
74 | 0xe0000000, 0x20206020, 0x0e000000, 0x02020408, | ||
75 | 0xb8000000, 0xb8a88888, 0x38000000, 0x090a1320, | ||
76 | 0xe0000000, 0xe0a02020, 0x0e000000, 0x02020408, | ||
77 | 0xb8000000, 0xa8b8a8a8, 0x38000000, 0x090a1320, | ||
78 | 0xe0000000, 0xa0e0a0a0, 0x0e000000, 0x02020408, | ||
79 | 0xb8000000, 0xb8a898a8, 0x38000000, 0x090a1320, | ||
80 | 0xe0000000, 0xe0a060a0, 0x0e000000, 0x02020408, | ||
81 | 0xe0000000, 0xe0202020, 0x0e000000, 0x0e0a0e0a, | ||
82 | }; | ||
83 | |||
84 | u32 ch_btn_sprite[] = { | ||
85 | // CH1 | ||
86 | 0x888e80ff, 0xff808e88, | ||
87 | 0xa1a100ff, 0xff00a9e1, | ||
88 | 0x101010f0, 0xf0101010, | ||
89 | |||
90 | // CH2 | ||
91 | 0x919d80ff, 0xff809d91, | ||
92 | 0x424200ff, 0xff0052c2, | ||
93 | 0x909010f0, 0xf0109090, | ||
94 | |||
95 | // CH3 | ||
96 | 0xa2ba80ff, 0xff80baa3, | ||
97 | 0x858500ff, 0xff00a585, | ||
98 | 0x505010f0, 0xf0105050, | ||
99 | |||
100 | // CH4 | ||
101 | 0xa2ba80ff, 0xff80baa3, | ||
102 | 0x858500ff, 0xff00a485, | ||
103 | 0x505010f0, 0xf010d050, | ||
104 | }; | ||
105 | |||
106 | static const u32 default_wave_buttons[] = { | ||
107 | 0xff013149, 0x850101ff, 0x3f202028, 0x2423203f, | ||
108 | 0xff016151, 0x49c501ff, 0x3f202c2a, 0x2928203f, | ||
109 | 0xff017d45, 0x45c501ff, 0x3f202828, 0x282f203f, | ||
110 | 0xff014911, 0x812501ff, 0x3f202128, 0x2420203f, | ||
111 | }; | ||
112 | |||
113 | // | ||
114 | // Wave data. | ||
115 | // | ||
116 | |||
117 | static const u32 sine_wave[16] = { | ||
118 | 0xefdebc89, 0x98cbedfe, 0x10214376, 0x67341201, | ||
119 | }; | ||
120 | |||
121 | static const u32 saw_wave[16] = { | ||
122 | 0x67452301, 0xefcdab89, 0x67452301, 0xefcdab89, | ||
123 | }; | ||
124 | |||
125 | static const u32 square_wave[16] = { | ||
126 | 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, | ||
127 | }; | ||
128 | |||
129 | #endif // STEPPER_ASSETS_C | ||
diff --git a/src/clipboard.c b/src/clipboard.c new file mode 100644 index 0000000..7491d4b --- /dev/null +++ b/src/clipboard.c | |||
@@ -0,0 +1,319 @@ | |||
1 | void clipboard_paste(void); | ||
2 | void clipboard_copy(void); | ||
3 | |||
4 | typedef enum ClipboardType { | ||
5 | CLIP_EMPTY, | ||
6 | CLIP_TRIG, | ||
7 | CLIP_PARAM_CH1, | ||
8 | CLIP_PARAM_CH2, | ||
9 | CLIP_PARAM_CH3, | ||
10 | CLIP_PARAM_CH4, | ||
11 | CLIP_PATTERN, | ||
12 | CLIP_CHANNEL, | ||
13 | } ClipboardType; | ||
14 | |||
15 | typedef struct Clipboard { | ||
16 | ClipboardType type; | ||
17 | u8 src_pat; | ||
18 | u8 src_chan; | ||
19 | u8 src_trig; | ||
20 | } Clipboard; | ||
21 | |||
22 | static Clipboard clipboard = {CLIP_EMPTY, 0, 0, 0}; | ||
23 | |||
24 | void | ||
25 | clipboard_paste(void) { | ||
26 | Pattern *pat_dst = &patterns[pattern_selection_loc]; | ||
27 | Pattern *pat_src = &patterns[clipboard.src_pat]; | ||
28 | |||
29 | if (input_handler == handle_trigger_selection) { | ||
30 | if (clipboard.type == CLIP_TRIG) { | ||
31 | // Copy notes or parameters when applicable. | ||
32 | switch (clipboard.src_chan) { | ||
33 | case 0: { | ||
34 | switch (channel_selection_loc) { | ||
35 | case 0: { | ||
36 | pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; | ||
37 | pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
38 | } break; | ||
39 | case 1: { | ||
40 | pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; | ||
41 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
42 | } break; | ||
43 | case 2: { | ||
44 | pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; | ||
45 | } break; | ||
46 | case 3: { | ||
47 | pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; | ||
48 | } break; | ||
49 | } | ||
50 | } break; | ||
51 | case 1: { | ||
52 | switch (channel_selection_loc) { | ||
53 | case 0: { | ||
54 | pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; | ||
55 | pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume; | ||
56 | pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time; | ||
57 | pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction; | ||
58 | pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle; | ||
59 | } break; | ||
60 | case 1: { | ||
61 | pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; | ||
62 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig]; | ||
63 | } break; | ||
64 | case 2: { | ||
65 | pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; | ||
66 | } break; | ||
67 | case 3: { | ||
68 | pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; | ||
69 | } break; | ||
70 | } | ||
71 | } break; | ||
72 | case 2: { | ||
73 | switch (channel_selection_loc) { | ||
74 | case 0: { | ||
75 | pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; | ||
76 | } break; | ||
77 | case 1: { | ||
78 | pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; | ||
79 | } break; | ||
80 | case 2: { | ||
81 | pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; | ||
82 | pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig]; | ||
83 | } break; | ||
84 | case 3: { | ||
85 | pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; | ||
86 | } break; | ||
87 | } | ||
88 | } break; | ||
89 | case 3: { | ||
90 | switch (channel_selection_loc) { | ||
91 | case 0: { | ||
92 | pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; | ||
93 | } break; | ||
94 | case 1: { | ||
95 | pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; | ||
96 | } break; | ||
97 | case 2: { | ||
98 | pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; | ||
99 | } break; | ||
100 | case 3: { | ||
101 | pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; | ||
102 | pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig]; | ||
103 | } break; | ||
104 | } | ||
105 | } break; | ||
106 | } | ||
107 | } | ||
108 | // Only paste the params for the respective trigger. | ||
109 | if (clipboard.type == CLIP_PARAM_CH1) { | ||
110 | switch (channel_selection_loc) { | ||
111 | case 0: { | ||
112 | pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
113 | } break; | ||
114 | case 1: { | ||
115 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
116 | } break; | ||
117 | } | ||
118 | } | ||
119 | if (clipboard.type == CLIP_PARAM_CH2) { | ||
120 | switch (channel_selection_loc) { | ||
121 | case 0: { | ||
122 | pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume; | ||
123 | pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time; | ||
124 | pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction; | ||
125 | pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle; | ||
126 | } break; | ||
127 | case 1: { | ||
128 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig]; | ||
129 | } break; | ||
130 | } | ||
131 | } | ||
132 | if (clipboard.type == CLIP_PARAM_CH3 && channel_selection_loc == clipboard.src_chan) { | ||
133 | pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig]; | ||
134 | } | ||
135 | if (clipboard.type == CLIP_PARAM_CH4 && channel_selection_loc == clipboard.src_chan) { | ||
136 | pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig]; | ||
137 | } | ||
138 | draw_triggers(); | ||
139 | draw_parameters(); | ||
140 | } else if (input_handler == handle_param_selection_sq1 && clipboard.type == CLIP_PARAM_CH1) { | ||
141 | pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
142 | draw_parameters(); | ||
143 | } else if (input_handler == handle_param_selection_sq1 && clipboard.type == CLIP_PARAM_CH2) { | ||
144 | pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume; | ||
145 | pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time; | ||
146 | pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction; | ||
147 | pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle; | ||
148 | draw_parameters(); | ||
149 | } else if (input_handler == handle_param_selection_sq2 && clipboard.type == CLIP_PARAM_CH2) { | ||
150 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig]; | ||
151 | draw_parameters(); | ||
152 | } else if (input_handler == handle_param_selection_sq2 && clipboard.type == CLIP_PARAM_CH1) { | ||
153 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
154 | draw_parameters(); | ||
155 | } else if (input_handler == handle_param_selection_wave && clipboard.type == CLIP_PARAM_CH3) { | ||
156 | pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig]; | ||
157 | draw_parameters(); | ||
158 | } else if (input_handler == handle_param_selection_noise && clipboard.type == CLIP_PARAM_CH4) { | ||
159 | pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig]; | ||
160 | draw_parameters(); | ||
161 | } else if (input_handler == handle_channel_selection && clipboard.type == CLIP_CHANNEL) { | ||
162 | // Copy notes from a different channel OR notes and parameters | ||
163 | // from a different pattern. | ||
164 | if (clipboard.src_chan == channel_selection_loc) { | ||
165 | switch (clipboard.src_chan) { | ||
166 | case 0: { pat_dst->ch1 = pat_src->ch1; } break; | ||
167 | case 1: { pat_dst->ch2 = pat_src->ch2; } break; | ||
168 | case 2: { pat_dst->ch3 = pat_src->ch3; } break; | ||
169 | case 3: { pat_dst->ch4 = pat_src->ch4; } break; | ||
170 | } | ||
171 | } else { | ||
172 | switch (clipboard.src_chan) { | ||
173 | case 0: { | ||
174 | switch (channel_selection_loc) { | ||
175 | case 0: { | ||
176 | for (size_t i = 0; i < 16; i++) { | ||
177 | pat_dst->ch1.notes[i] = pat_src->ch1.notes[i]; | ||
178 | } | ||
179 | } break; | ||
180 | case 1: { | ||
181 | for (size_t i = 0; i < 16; i++) { | ||
182 | pat_dst->ch2.notes[i] = pat_src->ch1.notes[i]; | ||
183 | } | ||
184 | } break; | ||
185 | case 2: { | ||
186 | for (size_t i = 0; i < 16; i++) { | ||
187 | pat_dst->ch3.notes[i] = pat_src->ch1.notes[i]; | ||
188 | } | ||
189 | } break; | ||
190 | case 3: { | ||
191 | for (size_t i = 0; i < 16; i++) { | ||
192 | pat_dst->ch4.notes[i] = pat_src->ch1.notes[i]; | ||
193 | } | ||
194 | } break; | ||
195 | } | ||
196 | } break; | ||
197 | case 1: { | ||
198 | switch (channel_selection_loc) { | ||
199 | case 0: { | ||
200 | for (size_t i = 0; i < 16; i++) { | ||
201 | pat_dst->ch1.notes[i] = pat_src->ch2.notes[i]; | ||
202 | } | ||
203 | } break; | ||
204 | case 1: { | ||
205 | for (size_t i = 0; i < 16; i++) { | ||
206 | pat_dst->ch2.notes[i] = pat_src->ch2.notes[i]; | ||
207 | } | ||
208 | } break; | ||
209 | case 2: { | ||
210 | for (size_t i = 0; i < 16; i++) { | ||
211 | pat_dst->ch3.notes[i] = pat_src->ch2.notes[i]; | ||
212 | } | ||
213 | } break; | ||
214 | case 3: { | ||
215 | for (size_t i = 0; i < 16; i++) { | ||
216 | pat_dst->ch4.notes[i] = pat_src->ch2.notes[i]; | ||
217 | } | ||
218 | } break; | ||
219 | } | ||
220 | } break; | ||
221 | case 2: { | ||
222 | switch (channel_selection_loc) { | ||
223 | case 0: { | ||
224 | for (size_t i = 0; i < 16; i++) { | ||
225 | pat_dst->ch1.notes[i] = pat_src->ch3.notes[i]; | ||
226 | } | ||
227 | } break; | ||
228 | case 1: { | ||
229 | for (size_t i = 0; i < 16; i++) { | ||
230 | pat_dst->ch2.notes[i] = pat_src->ch3.notes[i]; | ||
231 | } | ||
232 | } break; | ||
233 | case 2: { | ||
234 | for (size_t i = 0; i < 16; i++) { | ||
235 | pat_dst->ch3.notes[i] = pat_src->ch3.notes[i]; | ||
236 | } | ||
237 | } break; | ||
238 | case 3: { | ||
239 | for (size_t i = 0; i < 16; i++) { | ||
240 | pat_dst->ch4.notes[i] = pat_src->ch3.notes[i]; | ||
241 | } | ||
242 | } break; | ||
243 | } | ||
244 | } break; | ||
245 | case 3: { | ||
246 | switch (channel_selection_loc) { | ||
247 | case 0: { | ||
248 | for (size_t i = 0; i < 16; i++) { | ||
249 | pat_dst->ch1.notes[i] = pat_src->ch4.notes[i]; | ||
250 | } | ||
251 | } break; | ||
252 | case 1: { | ||
253 | for (size_t i = 0; i < 16; i++) { | ||
254 | pat_dst->ch2.notes[i] = pat_src->ch4.notes[i]; | ||
255 | } | ||
256 | } break; | ||
257 | case 2: { | ||
258 | for (size_t i = 0; i < 16; i++) { | ||
259 | pat_dst->ch3.notes[i] = pat_src->ch4.notes[i]; | ||
260 | } | ||
261 | } break; | ||
262 | case 3: { | ||
263 | for (size_t i = 0; i < 16; i++) { | ||
264 | pat_dst->ch4.notes[i] = pat_src->ch4.notes[i]; | ||
265 | } | ||
266 | } break; | ||
267 | } | ||
268 | } break; | ||
269 | } | ||
270 | } | ||
271 | draw_channels(); | ||
272 | draw_triggers(); | ||
273 | } else if (input_handler == handle_pattern_selection && clipboard.type == CLIP_PATTERN) { | ||
274 | // Copy an entire pattern. | ||
275 | if (pattern_selection_loc != clipboard.src_pat) { | ||
276 | *pat_dst = *pat_src; | ||
277 | draw_channels(); | ||
278 | draw_triggers(); | ||
279 | } | ||
280 | } | ||
281 | } | ||
282 | |||
283 | void | ||
284 | clipboard_copy(void) { | ||
285 | if (input_handler == handle_trigger_selection) { | ||
286 | clipboard.type = CLIP_TRIG; | ||
287 | clipboard.src_pat = pattern_selection_loc; | ||
288 | clipboard.src_chan = channel_selection_loc; | ||
289 | clipboard.src_trig = trig_selection_loc; | ||
290 | } else if (input_handler == handle_param_selection_sq1) { | ||
291 | clipboard.type = CLIP_PARAM_CH1; | ||
292 | clipboard.src_pat = pattern_selection_loc; | ||
293 | clipboard.src_chan = channel_selection_loc; | ||
294 | clipboard.src_trig = trig_selection_loc; | ||
295 | } else if (input_handler == handle_param_selection_sq2) { | ||
296 | clipboard.type = CLIP_PARAM_CH2; | ||
297 | clipboard.src_pat = pattern_selection_loc; | ||
298 | clipboard.src_chan = channel_selection_loc; | ||
299 | clipboard.src_trig = trig_selection_loc; | ||
300 | } else if (input_handler == handle_param_selection_wave) { | ||
301 | clipboard.type = CLIP_PARAM_CH3; | ||
302 | clipboard.src_pat = pattern_selection_loc; | ||
303 | clipboard.src_chan = channel_selection_loc; | ||
304 | clipboard.src_trig = trig_selection_loc; | ||
305 | } else if (input_handler == handle_param_selection_noise) { | ||
306 | clipboard.type = CLIP_PARAM_CH4; | ||
307 | clipboard.src_pat = pattern_selection_loc; | ||
308 | clipboard.src_chan = channel_selection_loc; | ||
309 | clipboard.src_trig = trig_selection_loc; | ||
310 | } else if (input_handler == handle_channel_selection) { | ||
311 | clipboard.type = CLIP_CHANNEL; | ||
312 | clipboard.src_pat = pattern_selection_loc; | ||
313 | clipboard.src_chan = channel_selection_loc; | ||
314 | } else if (input_handler == handle_pattern_selection) { | ||
315 | clipboard.type = CLIP_PATTERN; | ||
316 | clipboard.src_pat = pattern_selection_loc; | ||
317 | } | ||
318 | } | ||
319 | |||
diff --git a/src/drawing.c b/src/drawing.c new file mode 100644 index 0000000..4f08e39 --- /dev/null +++ b/src/drawing.c | |||
@@ -0,0 +1,1237 @@ | |||
1 | // | ||
2 | // Channel render functions. | ||
3 | // | ||
4 | |||
5 | void | ||
6 | draw_channel_sprite(size_t x, size_t y, u8 clr, u8 idx) { | ||
7 | draw_icn(x, y, &ch_btn_sprite[0 + 6 * idx], clr, 0, 0); | ||
8 | draw_icn(x + 8, y, &ch_btn_sprite[2 + 6 * idx], clr, 0, 0); | ||
9 | draw_icn(x + 16, y, &ch_btn_sprite[4 + 6 * idx], clr, 0, 0); | ||
10 | } | ||
11 | |||
12 | void | ||
13 | draw_channels(void) { | ||
14 | for (size_t i = 0; i < 4; i++) { | ||
15 | bool active = false; | ||
16 | switch (i) { | ||
17 | case 0: { | ||
18 | active = patterns[pattern_selection_loc].ch1.active; | ||
19 | } break; | ||
20 | case 1: { | ||
21 | active = patterns[pattern_selection_loc].ch2.active; | ||
22 | } break; | ||
23 | case 2: { | ||
24 | active = patterns[pattern_selection_loc].ch3.active; | ||
25 | } break; | ||
26 | case 3: { | ||
27 | active = patterns[pattern_selection_loc].ch4.active; | ||
28 | } break; | ||
29 | } | ||
30 | u8 clr = active ? COL_FG : COL_GREY; | ||
31 | size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; | ||
32 | draw_channel_sprite(CHAN_START_X, y, active, i); | ||
33 | } | ||
34 | } | ||
35 | |||
36 | void | ||
37 | draw_channel_cursor(size_t i, u8 clr) { | ||
38 | size_t offset_x = 0; | ||
39 | size_t offset_y = CHAN_H + i * CHAN_OFFSET_Y + 1; | ||
40 | size_t x0 = CHAN_START_X + offset_x; | ||
41 | size_t x1 = CHAN_START_X + offset_x + CHAN_W; | ||
42 | size_t y = CHAN_START_Y + offset_y; | ||
43 | draw_line(x0, y, x1, y, clr); | ||
44 | } | ||
45 | |||
46 | // | ||
47 | // Trigger render functions. | ||
48 | // | ||
49 | |||
50 | void | ||
51 | clear_trigger(size_t i) { | ||
52 | size_t offset_x = TRIG_OFFSET_X * (i % 8); | ||
53 | size_t offset_y = i < 8 ? 0 : TRIG_OFFSET_Y; | ||
54 | size_t x0 = TRIG_START_X + offset_x + 1; | ||
55 | size_t x1 = TRIG_START_X + offset_x + TRIG_W - 1; | ||
56 | size_t y0 = TRIG_START_Y + offset_y + 1; | ||
57 | size_t y1 = TRIG_START_Y + offset_y + TRIG_H - 4; | ||
58 | draw_filled_rect(x0, y0, x1, y1, COL_BG); | ||
59 | } | ||
60 | |||
61 | void | ||
62 | draw_trigger(size_t chan, size_t i) { | ||
63 | TriggerNote trig = {0}; | ||
64 | switch (chan) { | ||
65 | case 0: { | ||
66 | trig = patterns[pattern_selection_loc].ch1.notes[i]; | ||
67 | } break; | ||
68 | case 1: { | ||
69 | trig = patterns[pattern_selection_loc].ch2.notes[i]; | ||
70 | } break; | ||
71 | case 2: { | ||
72 | trig = patterns[pattern_selection_loc].ch3.notes[i]; | ||
73 | } break; | ||
74 | case 3: { | ||
75 | trig = patterns[pattern_selection_loc].ch4.notes[i]; | ||
76 | } break; | ||
77 | } | ||
78 | if (trig.active) { | ||
79 | size_t offset_x = TRIG_OFFSET_X * (i % 8); | ||
80 | size_t offset_y = i < 8 ? 0 : TRIG_OFFSET_Y; | ||
81 | size_t x = TRIG_START_X + offset_x; | ||
82 | size_t y = TRIG_START_Y + offset_y; | ||
83 | u32 *tile = ¬e_name_sprites[4 * trig.note]; | ||
84 | draw_icn(x, y, &tile[0], COL_FG, 1, 0); | ||
85 | draw_icn(x + 8, y, &tile[2], COL_FG, 1, 0); | ||
86 | } else { | ||
87 | clear_trigger(i); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | void | ||
92 | draw_trig_cursor(size_t i, u8 clr) { | ||
93 | size_t offset_x = TRIG_OFFSET_X * (i % 8); | ||
94 | size_t offset_y = i < 8 ? 2 : 2 + TRIG_OFFSET_Y; | ||
95 | size_t x0 = TRIG_START_X + offset_x; | ||
96 | size_t x1 = TRIG_START_X + TRIG_W + offset_x; | ||
97 | size_t y = TRIG_START_Y + TRIG_H + offset_y; | ||
98 | draw_line(x0, y, x1, y, clr); | ||
99 | } | ||
100 | |||
101 | void | ||
102 | draw_right_col_cursor(u8 clr) { | ||
103 | size_t x0 = 0; | ||
104 | size_t x1 = 0; | ||
105 | size_t y = 0; | ||
106 | switch (right_col_selection_loc) { | ||
107 | case R_COL_BPM: { | ||
108 | x0 = BPM_START_X; | ||
109 | x1 = x0 + R_COL_W; | ||
110 | y = BPM_START_Y + BPM_H + 2; | ||
111 | } break; | ||
112 | case R_COL_STOP: { | ||
113 | x0 = STOP_START_X; | ||
114 | x1 = x0 + R_COL_W; | ||
115 | y = STOP_START_Y + PLAY_STOP_H + 2; | ||
116 | } break; | ||
117 | case R_COL_PLAY: { | ||
118 | x0 = PLAY_START_X; | ||
119 | x1 = x0 + R_COL_W; | ||
120 | y = PLAY_START_Y + PLAY_STOP_H + 2; | ||
121 | } break; | ||
122 | case R_COL_BANK_A: { | ||
123 | x0 = BANK_START_X; | ||
124 | x1 = x0 + PAT_W; | ||
125 | y = BANK_START_Y + PAT_H + 2; | ||
126 | } break; | ||
127 | case R_COL_BANK_B: { | ||
128 | x0 = BANK_START_X; | ||
129 | x1 = x0 + PAT_W; | ||
130 | y = BANK_START_Y + PAT_H + 2 + 1 * PAT_OFFSET_Y; | ||
131 | } break; | ||
132 | case R_COL_BANK_C: { | ||
133 | x0 = BANK_START_X; | ||
134 | x1 = x0 + PAT_W; | ||
135 | y = BANK_START_Y + PAT_H + 2 + 2 * PAT_OFFSET_Y; | ||
136 | } break; | ||
137 | case R_COL_BANK_D: { | ||
138 | x0 = BANK_START_X; | ||
139 | x1 = x0 + PAT_W; | ||
140 | y = BANK_START_Y + PAT_H + 2 + 3 * PAT_OFFSET_Y; | ||
141 | } break; | ||
142 | } | ||
143 | draw_line(x0, y, x1, y, clr); | ||
144 | } | ||
145 | |||
146 | void | ||
147 | draw_current_step(u8 col) { | ||
148 | size_t offset_x = TRIG_OFFSET_X * (step_counter % 8); | ||
149 | size_t offset_y = step_counter < 8 ? 2 : 2 + TRIG_OFFSET_Y; | ||
150 | size_t x0 = TRIG_START_X + 3 + offset_x; | ||
151 | size_t x1 = TRIG_START_X - 3 + TRIG_W + offset_x; | ||
152 | size_t y = TRIG_START_Y - 4 + TRIG_H + offset_y; | ||
153 | draw_line(x0, y, x1, y, col); | ||
154 | } | ||
155 | |||
156 | void | ||
157 | draw_bank_buttons() { | ||
158 | size_t x = BANK_START_X; | ||
159 | size_t y = BANK_START_Y; | ||
160 | // txt_drawf_small("BANK", x - 2, y - 10, 4, COL_FG); | ||
161 | char bank_names[] = { | ||
162 | 'A', 'B', 'C', 'D', | ||
163 | }; | ||
164 | for (int i = 0; i < 4; i++) { | ||
165 | int color = COL_GREY; | ||
166 | if (i == current_bank) { | ||
167 | color = COL_FG; | ||
168 | } | ||
169 | draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG); | ||
170 | draw_rect(x, y, x + PAT_W, y + PAT_H, color); | ||
171 | // txt_drawc(bank_names[i], x + 4, y + 2, 6, color); | ||
172 | y += PAT_OFFSET_Y; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | void | ||
177 | draw_pattern_buttons() { | ||
178 | size_t x = PAT_START_X; | ||
179 | size_t y = PAT_START_Y; | ||
180 | // txt_drawf_small("PAT", x, y - 10, 4, COL_FG); | ||
181 | char pat_names[] = { | ||
182 | 'A', 'B', 'C', 'D', | ||
183 | 'E', 'F', 'G', 'H', | ||
184 | }; | ||
185 | for (int i = 0; i < 8; i++) { | ||
186 | int color = COL_GREY; | ||
187 | if (i == current_pattern) { | ||
188 | color = COL_FG; | ||
189 | } | ||
190 | if (i == next_pattern && current_pattern != next_pattern) { | ||
191 | color = COL_BLUE; | ||
192 | } | ||
193 | draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG); | ||
194 | draw_rect(x, y, x + PAT_W, y + PAT_H, color); | ||
195 | // txt_drawc(pat_names[i], x + 4, y + 2, 6, color); | ||
196 | y += PAT_OFFSET_Y; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | void | ||
201 | draw_pattern_cursor(size_t i, u8 clr) { | ||
202 | size_t offset_x = 0; | ||
203 | size_t offset_y = PAT_H + i * PAT_OFFSET_Y + 2; | ||
204 | size_t x0 = PAT_START_X + offset_x; | ||
205 | size_t x1 = PAT_START_X + offset_x + PAT_W; | ||
206 | size_t y = PAT_START_Y + offset_y; | ||
207 | draw_line(x0, y, x1, y, clr); | ||
208 | } | ||
209 | |||
210 | void | ||
211 | draw_play() { | ||
212 | size_t x = PLAY_START_X; | ||
213 | size_t y = PLAY_START_Y; | ||
214 | draw_filled_rect(x, y, x + R_COL_W, y + PLAY_STOP_H, COL_BG); | ||
215 | draw_rect(x, y, x + R_COL_W, y + PLAY_STOP_H, COL_CYAN); | ||
216 | if (play_status == 1) { | ||
217 | // Pause button | ||
218 | draw_filled_rect(x + 10, y + 3, x + 11, y + 7, COL_CYAN); | ||
219 | draw_filled_rect(x + 13, y + 3, x + 14, y + 7, COL_CYAN); | ||
220 | } else { | ||
221 | // Play button | ||
222 | x += 1; | ||
223 | draw_line(x + 10, y + 2, x + 10, y + 8, COL_CYAN); | ||
224 | draw_line(x + 11, y + 3, x + 11, y + 7, COL_CYAN); | ||
225 | draw_line(x + 12, y + 4, x + 12, y + 6, COL_CYAN); | ||
226 | draw_line(x + 13, y + 5, x + 13, y + 5, COL_CYAN); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | void | ||
231 | draw_stop() { | ||
232 | size_t x = STOP_START_X; | ||
233 | size_t y = STOP_START_Y; | ||
234 | draw_rect(x, y, x + R_COL_W, y + PLAY_STOP_H, COL_RED); | ||
235 | draw_filled_rect(x + 10, y + 3, x + 14, y + 7, COL_RED); | ||
236 | } | ||
237 | |||
238 | void | ||
239 | draw_bpm() { | ||
240 | size_t x = BPM_START_X; | ||
241 | size_t y = BPM_START_Y; | ||
242 | |||
243 | // Draw bounding box. | ||
244 | draw_filled_rect(x, y, x + R_COL_W, y + BPM_H, COL_BG); | ||
245 | draw_rect(x, y, x + R_COL_W, y + BPM_H, COL_FG); | ||
246 | draw_line(x + 5, y, x + 19, y, COL_BG); | ||
247 | // txt_drawf_small("BPM", x + 5, y - 4, 4, COL_FG); | ||
248 | |||
249 | // Make sure its horizontally centered if only 2 digits | ||
250 | int bpm = patterns[pattern_selection_loc].bpm; | ||
251 | if (bpm >= 100) { | ||
252 | txt_drawf("%d", x + 3, y + 7, 6, COL_FG, bpm); | ||
253 | } else { | ||
254 | txt_drawf("%d", x + 6, y + 7, 6, COL_FG, bpm); | ||
255 | } | ||
256 | } | ||
257 | |||
258 | void | ||
259 | draw_triggers(void) { | ||
260 | for (size_t i = 0; i < 16; i++) { | ||
261 | size_t offset_x = TRIG_OFFSET_X * (i % 8); | ||
262 | size_t offset_y = i < 8 ? 0 : 0 + TRIG_OFFSET_Y; | ||
263 | size_t x0 = TRIG_START_X + offset_x; | ||
264 | size_t x1 = TRIG_START_X + offset_x + TRIG_W; | ||
265 | size_t y0 = TRIG_START_Y + offset_y; | ||
266 | size_t y1 = TRIG_START_Y + offset_y + TRIG_H; | ||
267 | draw_rect(x0, y0, x1, y1, COL_FG); | ||
268 | clear_trigger(i); | ||
269 | draw_trigger(channel_selection_loc, i); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | void | ||
274 | draw_note(u8 note, u8 clr) { | ||
275 | size_t octave = note / 12; | ||
276 | size_t value = note % 12; | ||
277 | |||
278 | size_t x0 = 0; | ||
279 | size_t y0 = 0; | ||
280 | size_t x1 = 0; | ||
281 | size_t y1 = 0; | ||
282 | switch (value) { | ||
283 | // White notes. | ||
284 | case 0:{ | ||
285 | x0 = PIANO_START_X + 2 + octave * 28; | ||
286 | x1 = x0 + 1; | ||
287 | y0 = PIANO_START_Y + 2; | ||
288 | y1 = PIANO_START_Y - 2 + PIANO_H; | ||
289 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
290 | x0 = PIANO_START_X + 2 + octave * 28 + 2; | ||
291 | x1 = x0; | ||
292 | y0 = y0 + 9; | ||
293 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
294 | } break; | ||
295 | case 2:{ | ||
296 | x0 = PIANO_START_X + 2 + octave * 28 + 5; | ||
297 | x1 = x0; | ||
298 | y0 = PIANO_START_Y + 2; | ||
299 | y1 = PIANO_START_Y - 2 + 12; | ||
300 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
301 | x0 = PIANO_START_X + 2 + octave * 28 + 4; | ||
302 | x1 = x0 + 2; | ||
303 | y0 = PIANO_START_Y - 2 + 13; | ||
304 | y1 = y0 + 7; | ||
305 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
306 | } break; | ||
307 | case 4:{ | ||
308 | x0 = PIANO_START_X + 2 + octave * 28 + 9; | ||
309 | x1 = x0 + 1; | ||
310 | y0 = PIANO_START_Y + 2; | ||
311 | y1 = PIANO_START_Y - 2 + 12; | ||
312 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
313 | x0 = PIANO_START_X + 2 + octave * 28 + 8; | ||
314 | x1 = x0 + 2; | ||
315 | y0 = PIANO_START_Y - 2 + 13; | ||
316 | y1 = y0 + 7; | ||
317 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
318 | } break; | ||
319 | case 5:{ | ||
320 | x0 = PIANO_START_X + 2 + octave * 28 + 12; | ||
321 | x1 = x0 + 1; | ||
322 | y0 = PIANO_START_Y + 2; | ||
323 | y1 = PIANO_START_Y - 2 + PIANO_H; | ||
324 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
325 | x0 = PIANO_START_X + 2 + octave * 28 + 14; | ||
326 | x1 = x0; | ||
327 | y0 = y0 + 9; | ||
328 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
329 | } break; | ||
330 | case 7:{ | ||
331 | x0 = PIANO_START_X + 2 + octave * 28 + 17; | ||
332 | x1 = x0; | ||
333 | y0 = PIANO_START_Y + 2; | ||
334 | y1 = PIANO_START_Y - 2 + 12; | ||
335 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
336 | x0 = PIANO_START_X + 2 + octave * 28 + 16; | ||
337 | x1 = x0 + 2; | ||
338 | y0 = PIANO_START_Y - 2 + 13; | ||
339 | y1 = y0 + 7; | ||
340 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
341 | } break; | ||
342 | case 9:{ | ||
343 | x0 = PIANO_START_X + 2 + octave * 28 + 21; | ||
344 | x1 = x0; | ||
345 | y0 = PIANO_START_Y + 2; | ||
346 | y1 = PIANO_START_Y - 2 + 12; | ||
347 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
348 | x0 = PIANO_START_X + 2 + octave * 28 + 20; | ||
349 | x1 = x0 + 2; | ||
350 | y0 = PIANO_START_Y - 2 + 13; | ||
351 | y1 = y0 + 7; | ||
352 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
353 | } break; | ||
354 | case 11: { | ||
355 | x0 = PIANO_START_X + 2 + octave * 28 + 25; | ||
356 | x1 = x0 + 1; | ||
357 | y0 = PIANO_START_Y + 2; | ||
358 | y1 = PIANO_START_Y - 2 + 12; | ||
359 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
360 | x0 = PIANO_START_X + 2 + octave * 28 + 24; | ||
361 | x1 = x0 + 2; | ||
362 | y0 = PIANO_START_Y - 2 + 13; | ||
363 | y1 = y0 + 7; | ||
364 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
365 | } break; | ||
366 | default: { | ||
367 | if (clr == COL_FG) { | ||
368 | clr = COL_BG; | ||
369 | } | ||
370 | y0 = PIANO_START_Y + 2; | ||
371 | y1 = PIANO_START_Y - 2 + 11; | ||
372 | switch (value) { | ||
373 | case 1: { | ||
374 | x0 = PIANO_START_X + 2 + octave * 28 + 3; | ||
375 | } break; | ||
376 | case 3: { | ||
377 | x0 = PIANO_START_X + 2 + octave * 28 + 7; | ||
378 | } break; | ||
379 | case 6: { | ||
380 | x0 = PIANO_START_X + 2 + octave * 28 + 15; | ||
381 | } break; | ||
382 | case 8: { | ||
383 | x0 = PIANO_START_X + 2 + octave * 28 + 19; | ||
384 | } break; | ||
385 | case 10: { | ||
386 | x0 = PIANO_START_X + 2 + octave * 28 + 23; | ||
387 | } break; | ||
388 | } | ||
389 | x1 = x0; | ||
390 | draw_line(x0, y0, x1, y1, clr); | ||
391 | } break; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | void | ||
396 | draw_piano(void) { | ||
397 | size_t x0 = PIANO_START_X; | ||
398 | size_t x1 = PIANO_START_X + PIANO_W; | ||
399 | size_t y0 = PIANO_START_Y; | ||
400 | size_t y1 = PIANO_START_Y + PIANO_H; | ||
401 | draw_rect(x0, y0, x1, y1, COL_FG); | ||
402 | for (size_t i = 0; i < 12 * 6; i++) { | ||
403 | draw_note(i, COL_FG); | ||
404 | } | ||
405 | } | ||
406 | |||
407 | void | ||
408 | draw_params_cursor_wave(size_t i, u8 clr) { | ||
409 | u8 x_positions[] = { | ||
410 | // 32 half bytes (Wave A). | ||
411 | 0, 4, 8, 12, 16, 20, 24, 28, | ||
412 | 34, 38, 42, 46, 50, 54, 58, 62, | ||
413 | 0, 4, 8, 12, 16, 20, 24, 28, | ||
414 | 34, 38, 42, 46, 50, 54, 58, 62, | ||
415 | // 32 half bytes (Wave B). | ||
416 | 70, 74, 78, 82, 86, 90, 94, 98, | ||
417 | 104, 108, 112, 116, 120, 124, 128, 132, | ||
418 | 70, 74, 78, 82, 86, 90, 94, 98, | ||
419 | 104, 108, 112, 116, 120, 124, 128, 132, | ||
420 | // Default wave A. | ||
421 | 1, 18, 35, 52, | ||
422 | // Default wave B. | ||
423 | 71, 88, 105, 122, | ||
424 | // Mode selection. | ||
425 | 141, | ||
426 | // Volume selection. | ||
427 | 141, | ||
428 | }; | ||
429 | u8 y_positions[] = { | ||
430 | // 32 half bytes (Wave A) | ||
431 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
432 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
433 | 8, 8, 8, 8, 8, 8, 8, 8, | ||
434 | 8, 8, 8, 8, 8, 8, 8, 8, | ||
435 | // 32 half bytes (Wave B) | ||
436 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
437 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
438 | 8, 8, 8, 8, 8, 8, 8, 8, | ||
439 | 8, 8, 8, 8, 8, 8, 8, 8, | ||
440 | // Default wave A. | ||
441 | 20, 20, 20, 20, | ||
442 | // Default wave B. | ||
443 | 20, 20, 20, 20, | ||
444 | // Mode selection. | ||
445 | 20, | ||
446 | // Volume selection. | ||
447 | 0, | ||
448 | }; | ||
449 | size_t cursor_length = 0; | ||
450 | if (i < 64) { | ||
451 | cursor_length = 4; | ||
452 | } else if (i < 72) { | ||
453 | cursor_length = 13; | ||
454 | } else { | ||
455 | cursor_length = 30; | ||
456 | } | ||
457 | size_t x = PARAMS_START_X + x_positions[i] - 1; | ||
458 | size_t y = PARAMS_START_Y + PARAMS_H - 23 + y_positions[i]; | ||
459 | draw_line(x, y, x + cursor_length, y, clr); | ||
460 | } | ||
461 | |||
462 | void | ||
463 | draw_params_cursor_noise(size_t i, u8 clr) { | ||
464 | u8 x_positions[] = { | ||
465 | 0, // Bit mode. | ||
466 | 31, // Env. Vol. | ||
467 | 59, // Env. Direction. | ||
468 | 87, // Env. Time. | ||
469 | }; | ||
470 | u8 y_positions[] = { | ||
471 | 20, // Bit mode. | ||
472 | 20, // Env. Vol. | ||
473 | 20, // Env. Direction. | ||
474 | 20, // Env. Time. | ||
475 | }; | ||
476 | size_t cursor_length = 24; | ||
477 | size_t x = PARAMS_START_X + x_positions[i] + 30; | ||
478 | size_t y = PARAMS_START_Y + PARAMS_H - 23 + y_positions[i]; | ||
479 | draw_line(x, y, x + cursor_length, y, clr); | ||
480 | } | ||
481 | |||
482 | void | ||
483 | draw_params_cursor_square(size_t i, u8 clr, bool sweep) { | ||
484 | size_t x_offset = sweep ? 0 : 30; | ||
485 | u8 x_positions[] = { | ||
486 | 0, // Duty. | ||
487 | 31, // Env. Vol. | ||
488 | 59, // Env. Direction. | ||
489 | 87, // Env. Time. | ||
490 | 118, // Sweep Number. | ||
491 | 146, // Sweep Time. | ||
492 | 132, // Sweep Direction. | ||
493 | }; | ||
494 | u8 y_positions[] = { | ||
495 | 20, // Duty. | ||
496 | 20, // Env. Vol. | ||
497 | 20, // Env. Direction. | ||
498 | 20, // Env. Time. | ||
499 | 20, // Sweep Number. | ||
500 | 20, // Sweep Time. | ||
501 | 0, // Sweep Direction. | ||
502 | }; | ||
503 | size_t cursor_length = 24; | ||
504 | size_t x = PARAMS_START_X + x_positions[i] + x_offset; | ||
505 | size_t y = PARAMS_START_Y + PARAMS_H - 23 + y_positions[i]; | ||
506 | draw_line(x, y, x + cursor_length, y, clr); | ||
507 | } | ||
508 | |||
509 | void | ||
510 | draw_params_cursor(size_t i, u8 clr) { | ||
511 | switch (channel_selection_loc) { | ||
512 | case 0: { | ||
513 | draw_params_cursor_square(i, clr, true); | ||
514 | } break; | ||
515 | case 1: { | ||
516 | draw_params_cursor_square(i, clr, false); | ||
517 | } break; | ||
518 | case 2: { | ||
519 | draw_params_cursor_wave(i, clr); | ||
520 | } break; | ||
521 | case 3: { | ||
522 | draw_params_cursor_noise(i, clr); | ||
523 | } break; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | IWRAM_CODE | ||
528 | void | ||
529 | draw_wave_pattern(u8 *pattern, int x, int y, u8 clr) { | ||
530 | for (size_t i = 0; i < 16; ++i) { | ||
531 | u8 byte = pattern[i]; | ||
532 | u8 first = (byte >> 4) & 0xF; | ||
533 | u8 second = byte & 0xF; | ||
534 | u8 a = x + i * 4; | ||
535 | u8 b = y + 16; | ||
536 | draw_pixel(a, b - first, clr); | ||
537 | draw_pixel(a + 1, b - first, clr); | ||
538 | draw_pixel(a + 2, b - second, clr); | ||
539 | draw_pixel(a + 3, b - second, clr); | ||
540 | } | ||
541 | } | ||
542 | |||
543 | IWRAM_CODE | ||
544 | void | ||
545 | clear_parameters(void) { | ||
546 | size_t x0 = PARAMS_START_X; | ||
547 | size_t y0 = PARAMS_START_Y; | ||
548 | size_t x1 = PARAMS_START_X + PARAMS_W; | ||
549 | size_t y1 = PARAMS_START_Y + PARAMS_H - 1; | ||
550 | draw_filled_rect(x0, y0, x1, y1, COL_BG); | ||
551 | } | ||
552 | |||
553 | IWRAM_CODE | ||
554 | void | ||
555 | draw_parameters_wave(void) { | ||
556 | // Draw current wave data. | ||
557 | Pattern *pat = &patterns[pattern_selection_loc]; | ||
558 | { | ||
559 | u8 *wave_a = pat->ch3.params[trig_selection_loc].wave_a; | ||
560 | u8 *wave_b = pat->ch3.params[trig_selection_loc].wave_b; | ||
561 | |||
562 | size_t x = PARAMS_START_X; | ||
563 | size_t y = PARAMS_START_Y + 13; | ||
564 | |||
565 | // Wave Patterns. | ||
566 | draw_wave_pattern(wave_a, x, y, COL_WAVE_A); | ||
567 | draw_wave_pattern(wave_b, x + 70, y, COL_WAVE_B); | ||
568 | |||
569 | // Wave text. | ||
570 | x -= 2; | ||
571 | // txt_drawf_small("%02x%02x%02x%02x", x, y + 20, 4, COL_FG, | ||
572 | // wave_a[0], wave_a[1], wave_a[2], wave_a[3]); | ||
573 | // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 20, 4, COL_FG, | ||
574 | // wave_a[4], wave_a[5], wave_a[6], wave_a[7]); | ||
575 | // txt_drawf_small("%02x%02x%02x%02x", x, y + 28, 4, COL_FG, | ||
576 | // wave_a[8], wave_a[9], wave_a[10], wave_a[11]); | ||
577 | // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 28, 4, COL_FG, | ||
578 | // wave_a[12], wave_a[13], wave_a[14], wave_a[15]); | ||
579 | |||
580 | x += 70; | ||
581 | // txt_drawf_small("%02x%02x%02x%02x", x, y + 20, 4, COL_FG, | ||
582 | // wave_b[0], wave_b[1], wave_b[2], wave_b[3]); | ||
583 | // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 20, 4, COL_FG, | ||
584 | // wave_b[4], wave_b[5], wave_b[6], wave_b[7]); | ||
585 | // txt_drawf_small("%02x%02x%02x%02x", x, y + 28, 4, COL_FG, | ||
586 | // wave_b[8], wave_b[9], wave_b[10], wave_b[11]); | ||
587 | // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 28, 4, COL_FG, | ||
588 | // wave_b[12], wave_b[13], wave_b[14], wave_b[15]); | ||
589 | } | ||
590 | |||
591 | // Draw default wave buttons. | ||
592 | { | ||
593 | // Tile *wave_tiles = ASSETS_DEFAULT_WAVES; | ||
594 | size_t x = PARAMS_START_X; | ||
595 | size_t y = PARAMS_START_Y + PARAMS_H - 12; | ||
596 | for (size_t i = 0, k = 0; i < 4 * 2; i += 2, k++) { | ||
597 | // draw_tile(x + 17 * k, y, wave_tiles + i, COL_FG, true); | ||
598 | // draw_tile(x + 17 * k + 8, y, wave_tiles + i + 1, COL_FG, true); | ||
599 | } | ||
600 | for (size_t i = 0, k = 0; i < 4 * 2; i += 2, k++) { | ||
601 | // draw_tile(x + 17 * k + 70, y, wave_tiles + i, COL_FG, true); | ||
602 | // draw_tile(x + 17 * k + 8 + 70, y, wave_tiles + i + 1, COL_FG, true); | ||
603 | } | ||
604 | } | ||
605 | |||
606 | // Mode selection. | ||
607 | { | ||
608 | size_t x = PARAMS_START_X + 140; | ||
609 | size_t y = PARAMS_START_Y + PARAMS_H - 22; | ||
610 | draw_line(x, y + 4, x + 5, y + 4, COL_FG); | ||
611 | draw_line(x + 25, y + 4, x + 30, y + 4, COL_FG); | ||
612 | draw_line(x, y + 5, x, y + 16, COL_FG); | ||
613 | draw_line(x + 30, y + 5, x + 30, y + 17, COL_FG); | ||
614 | draw_line(x, y + 17, x + 30, y + 17, COL_FG); | ||
615 | // txt_drawf_small("mode", x + 6, y, 4, COL_FG); | ||
616 | |||
617 | switch (pat->ch3.params[trig_selection_loc].wave_mode) { | ||
618 | case 0: { | ||
619 | txt_drawf("A", x + 12, y + 7, 6, COL_FG); | ||
620 | } break; | ||
621 | case 1: { | ||
622 | txt_drawf("B", x + 12, y + 7, 6, COL_FG); | ||
623 | } break; | ||
624 | case 2: { | ||
625 | txt_drawf("A+B", x + 6, y + 7, 6, COL_FG); | ||
626 | } break; | ||
627 | } | ||
628 | } | ||
629 | |||
630 | // Wave volume. | ||
631 | { | ||
632 | size_t x = PARAMS_START_X + 140; | ||
633 | size_t y = PARAMS_START_Y + PARAMS_H - 45; | ||
634 | draw_line(x, y + 7, x + 7, y + 7, COL_FG); | ||
635 | draw_line(x + 23, y + 7, x + 30, y + 7, COL_FG); | ||
636 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
637 | draw_line(x + 30, y + 8, x + 30, y + 19, COL_FG); | ||
638 | draw_line(x, y + 20, x + 30, y + 20, COL_FG); | ||
639 | // txt_drawf_small("vol", x + 8, y + 3, 4, COL_FG); | ||
640 | |||
641 | switch (pat->ch3.params[trig_selection_loc].wave_volume) { | ||
642 | case 0: { | ||
643 | txt_drawf("0", x + 12, y + 10, 6, COL_FG); | ||
644 | } break; | ||
645 | case 1: { | ||
646 | txt_drawf("25", x + 9, y + 10, 6, COL_FG); | ||
647 | } break; | ||
648 | case 2: { | ||
649 | txt_drawf("50", x + 9, y + 10, 6, COL_FG); | ||
650 | } break; | ||
651 | case 3: { | ||
652 | txt_drawf("75", x + 9, y + 10, 6, COL_FG); | ||
653 | } break; | ||
654 | case 4: { | ||
655 | txt_drawf("100", x + 6, y + 10, 6, COL_FG); | ||
656 | } break; | ||
657 | } | ||
658 | } | ||
659 | } | ||
660 | |||
661 | void | ||
662 | draw_parameters_square(ChannelSquareParams *params, bool sweep) { | ||
663 | size_t x_offset = sweep ? 0 : 30; | ||
664 | |||
665 | // Duty cycle. | ||
666 | { | ||
667 | // Shape drawing. | ||
668 | { | ||
669 | size_t x = PARAMS_START_X + x_offset; | ||
670 | size_t y = PARAMS_START_Y + PARAMS_H - 44; | ||
671 | |||
672 | size_t x0 = x + 2; | ||
673 | size_t x1 = x0; | ||
674 | size_t x2 = x0; | ||
675 | size_t x3 = x0; | ||
676 | size_t x4 = x0; | ||
677 | size_t x5 = x0; | ||
678 | size_t y0 = y + 14; | ||
679 | size_t y1 = y + 2; | ||
680 | |||
681 | switch (params->duty_cycle) { | ||
682 | case 0: { | ||
683 | x1 += 4; | ||
684 | x2 += 6; | ||
685 | x3 += 13; | ||
686 | x4 += 15; | ||
687 | x5 += 20; | ||
688 | } break; | ||
689 | case 1: { | ||
690 | x1 += 4; | ||
691 | x2 += 7; | ||
692 | x3 += 13; | ||
693 | x4 += 16; | ||
694 | x5 += 20; | ||
695 | } break; | ||
696 | case 2: { | ||
697 | x1 += 3; | ||
698 | x2 += 8; | ||
699 | x3 += 12; | ||
700 | x4 += 17; | ||
701 | x5 += 20; | ||
702 | } break; | ||
703 | case 3: { | ||
704 | x1 += 2; | ||
705 | x2 += 9; | ||
706 | x3 += 11; | ||
707 | x4 += 18; | ||
708 | x5 += 20; | ||
709 | } break; | ||
710 | } | ||
711 | draw_line(x0, y0, x1, y0, COL_RED); | ||
712 | draw_line(x1, y1, x1, y0, COL_RED); | ||
713 | draw_line(x1, y1, x2, y1, COL_RED); | ||
714 | draw_line(x2, y1, x2, y0, COL_RED); | ||
715 | draw_line(x2, y0, x3, y0, COL_RED); | ||
716 | draw_line(x3, y1, x3, y0, COL_RED); | ||
717 | draw_line(x3, y1, x4, y1, COL_RED); | ||
718 | draw_line(x4, y1, x4, y0, COL_RED); | ||
719 | draw_line(x4, y0, x5, y0, COL_RED); | ||
720 | |||
721 | // Bounding box. | ||
722 | draw_rect(x, y - 3, x + 24, y + 18, COL_RED); | ||
723 | } | ||
724 | |||
725 | // Param box. | ||
726 | { | ||
727 | size_t x = PARAMS_START_X + x_offset; | ||
728 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
729 | draw_line(x, y + 7, x + 2, y + 7, COL_FG); | ||
730 | draw_line(x + 22, y + 7, x + 24, y + 7, COL_FG); | ||
731 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
732 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
733 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
734 | // txt_drawf_small("duty", x + 3, y + 3, 4, COL_FG); | ||
735 | |||
736 | switch (params->duty_cycle) { | ||
737 | case 0: { | ||
738 | txt_drawf("12", x + 6, y + 10, 6, COL_FG); | ||
739 | } break; | ||
740 | case 1: { | ||
741 | txt_drawf("25", x + 6, y + 10, 6, COL_FG); | ||
742 | } break; | ||
743 | case 2: { | ||
744 | txt_drawf("50", x + 6, y + 10, 6, COL_FG); | ||
745 | } break; | ||
746 | case 3: { | ||
747 | txt_drawf("75", x + 6, y + 10, 6, COL_FG); | ||
748 | } break; | ||
749 | } | ||
750 | } | ||
751 | } | ||
752 | |||
753 | // Envelope. | ||
754 | { | ||
755 | // Env. drawing. | ||
756 | { | ||
757 | // Bounding box. | ||
758 | { | ||
759 | size_t x0 = PARAMS_START_X + 31 + x_offset; | ||
760 | size_t y0 = PARAMS_START_Y + PARAMS_H - 47; | ||
761 | size_t x1 = x0 + 79; | ||
762 | size_t y1 = y0 + 21; | ||
763 | draw_rect(x0, y0, x1, y1, COL_CYAN); | ||
764 | } | ||
765 | |||
766 | size_t x = PARAMS_START_X + 42 + x_offset; | ||
767 | size_t y = PARAMS_START_Y + PARAMS_H - 44; | ||
768 | size_t x0 = x; | ||
769 | size_t y0 = y + 15 - params->env_volume; | ||
770 | size_t x1 = x + 8 * params->env_time; | ||
771 | size_t y1 = params->env_direction == 0 ? y + 15 : y; | ||
772 | size_t x2 = x + 8 * 7 + 1; | ||
773 | size_t y2 = y1; | ||
774 | |||
775 | // Env. | ||
776 | if (params->env_time == 0) { | ||
777 | draw_line(x1, y0, x2, y0, COL_CYAN); | ||
778 | } else { | ||
779 | draw_line(x0, y0, x1, y1, COL_CYAN); | ||
780 | draw_line(x1, y1, x2, y2, COL_CYAN); | ||
781 | } | ||
782 | } | ||
783 | |||
784 | // Env. volume. | ||
785 | { | ||
786 | size_t x = PARAMS_START_X + 31 + x_offset; | ||
787 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
788 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
789 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
790 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
791 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
792 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
793 | // txt_drawf_small("vol", x + 5, y + 3, 4, COL_FG); | ||
794 | |||
795 | switch (params->env_volume) { | ||
796 | case 0: { | ||
797 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
798 | } break; | ||
799 | case 1: { | ||
800 | txt_drawf("6", x + 9, y + 10, 6, COL_FG); | ||
801 | } break; | ||
802 | case 2: { | ||
803 | txt_drawf("13", x + 6, y + 10, 6, COL_FG); | ||
804 | } break; | ||
805 | case 3: { | ||
806 | txt_drawf("20", x + 6, y + 10, 6, COL_FG); | ||
807 | } break; | ||
808 | case 4: { | ||
809 | txt_drawf("26", x + 6, y + 10, 6, COL_FG); | ||
810 | } break; | ||
811 | case 5: { | ||
812 | txt_drawf("33", x + 6, y + 10, 6, COL_FG); | ||
813 | } break; | ||
814 | case 6: { | ||
815 | txt_drawf("40", x + 6, y + 10, 6, COL_FG); | ||
816 | } break; | ||
817 | case 7: { | ||
818 | txt_drawf("46", x + 6, y + 10, 6, COL_FG); | ||
819 | } break; | ||
820 | case 8: { | ||
821 | txt_drawf("53", x + 6, y + 10, 6, COL_FG); | ||
822 | } break; | ||
823 | case 9: { | ||
824 | txt_drawf("60", x + 6, y + 10, 6, COL_FG); | ||
825 | } break; | ||
826 | case 10: { | ||
827 | txt_drawf("66", x + 6, y + 10, 6, COL_FG); | ||
828 | } break; | ||
829 | case 11: { | ||
830 | txt_drawf("73", x + 6, y + 10, 6, COL_FG); | ||
831 | } break; | ||
832 | case 12: { | ||
833 | txt_drawf("80", x + 6, y + 10, 6, COL_FG); | ||
834 | } break; | ||
835 | case 13: { | ||
836 | txt_drawf("86", x + 6, y + 10, 6, COL_FG); | ||
837 | } break; | ||
838 | case 14: { | ||
839 | txt_drawf("93", x + 6, y + 10, 6, COL_FG); | ||
840 | } break; | ||
841 | case 15: { | ||
842 | txt_drawf("100", x + 3, y + 10, 6, COL_FG); | ||
843 | } break; | ||
844 | } | ||
845 | } | ||
846 | |||
847 | // Env. direction | ||
848 | { | ||
849 | size_t x = PARAMS_START_X + 59 + x_offset; | ||
850 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
851 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
852 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
853 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
854 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
855 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
856 | // txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG); | ||
857 | |||
858 | char arr_up[2] = { 0x19, 0 }; | ||
859 | char arr_down[2] = { 0x18, 0 }; | ||
860 | switch (params->env_direction) { | ||
861 | case 0: { | ||
862 | txt_drawf(arr_up, x + 9, y + 11, 6, COL_FG); | ||
863 | } break; | ||
864 | case 1: { | ||
865 | txt_drawf(arr_down, x + 9, y + 11, 6, COL_FG); | ||
866 | } break; | ||
867 | } | ||
868 | } | ||
869 | |||
870 | // Env. time. | ||
871 | { | ||
872 | size_t x = PARAMS_START_X + 87 + x_offset; | ||
873 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
874 | draw_line(x, y + 7, x + 2, y + 7, COL_FG); | ||
875 | draw_line(x + 22, y + 7, x + 24, y + 7, COL_FG); | ||
876 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
877 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
878 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
879 | // txt_drawf_small("time", x + 3, y + 3, 4, COL_FG); | ||
880 | |||
881 | switch (params->env_time) { | ||
882 | case 0: { | ||
883 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
884 | } break; | ||
885 | case 1: { | ||
886 | txt_drawf("14", x + 6, y + 10, 6, COL_FG); | ||
887 | } break; | ||
888 | case 2: { | ||
889 | txt_drawf("28", x + 6, y + 10, 6, COL_FG); | ||
890 | } break; | ||
891 | case 3: { | ||
892 | txt_drawf("42", x + 6, y + 10, 6, COL_FG); | ||
893 | } break; | ||
894 | case 4: { | ||
895 | txt_drawf("57", x + 6, y + 10, 6, COL_FG); | ||
896 | } break; | ||
897 | case 5: { | ||
898 | txt_drawf("71", x + 6, y + 10, 6, COL_FG); | ||
899 | } break; | ||
900 | case 6: { | ||
901 | txt_drawf("85", x + 6, y + 10, 6, COL_FG); | ||
902 | } break; | ||
903 | case 7: { | ||
904 | txt_drawf("100", x + 3, y + 10, 6, COL_FG); | ||
905 | } break; | ||
906 | } | ||
907 | } | ||
908 | } | ||
909 | |||
910 | // Sweep number. | ||
911 | if (sweep) { | ||
912 | size_t x = PARAMS_START_X + 118; | ||
913 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
914 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
915 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
916 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
917 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
918 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
919 | // txt_drawf_small("num", x + 5, y + 3, 4, COL_FG); | ||
920 | |||
921 | switch (params->sweep_number) { | ||
922 | case 0: { | ||
923 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
924 | } break; | ||
925 | case 1: { | ||
926 | txt_drawf("1", x + 9, y + 10, 6, COL_FG); | ||
927 | } break; | ||
928 | case 2: { | ||
929 | txt_drawf("2", x + 9, y + 10, 6, COL_FG); | ||
930 | } break; | ||
931 | case 3: { | ||
932 | txt_drawf("3", x + 9, y + 10, 6, COL_FG); | ||
933 | } break; | ||
934 | case 4: { | ||
935 | txt_drawf("4", x + 9, y + 10, 6, COL_FG); | ||
936 | } break; | ||
937 | case 5: { | ||
938 | txt_drawf("5", x + 9, y + 10, 6, COL_FG); | ||
939 | } break; | ||
940 | case 6: { | ||
941 | txt_drawf("6", x + 9, y + 10, 6, COL_FG); | ||
942 | } break; | ||
943 | case 7: { | ||
944 | txt_drawf("7", x + 9, y + 10, 6, COL_FG); | ||
945 | } break; | ||
946 | } | ||
947 | } | ||
948 | |||
949 | // Sweep time. | ||
950 | if (sweep) { | ||
951 | size_t x = PARAMS_START_X + 146; | ||
952 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
953 | draw_line(x, y + 7, x + 2, y + 7, COL_FG); | ||
954 | draw_line(x + 22, y + 7, x + 24, y + 7, COL_FG); | ||
955 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
956 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
957 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
958 | // txt_drawf_small("time", x + 3, y + 3, 4, COL_FG); | ||
959 | |||
960 | switch (params->sweep_time) { | ||
961 | case 0: { | ||
962 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
963 | } break; | ||
964 | case 1: { | ||
965 | txt_drawf("1", x + 9, y + 10, 6, COL_FG); | ||
966 | } break; | ||
967 | case 2: { | ||
968 | txt_drawf("2", x + 9, y + 10, 6, COL_FG); | ||
969 | } break; | ||
970 | case 3: { | ||
971 | txt_drawf("3", x + 9, y + 10, 6, COL_FG); | ||
972 | } break; | ||
973 | case 4: { | ||
974 | txt_drawf("4", x + 9, y + 10, 6, COL_FG); | ||
975 | } break; | ||
976 | case 5: { | ||
977 | txt_drawf("5", x + 9, y + 10, 6, COL_FG); | ||
978 | } break; | ||
979 | case 6: { | ||
980 | txt_drawf("6", x + 9, y + 10, 6, COL_FG); | ||
981 | } break; | ||
982 | case 7: { | ||
983 | txt_drawf("7", x + 9, y + 10, 6, COL_FG); | ||
984 | } break; | ||
985 | } | ||
986 | } | ||
987 | |||
988 | // Sweep direction. | ||
989 | if (sweep) { | ||
990 | size_t x = PARAMS_START_X + 132; | ||
991 | size_t y = PARAMS_START_Y + PARAMS_H - 45; | ||
992 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
993 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
994 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
995 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
996 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
997 | // txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG); | ||
998 | |||
999 | char arr_up[2] = { 0x19, 0 }; | ||
1000 | char arr_down[2] = { 0x18, 0 }; | ||
1001 | switch (params->sweep_direction) { | ||
1002 | case 0: { | ||
1003 | txt_drawf(arr_up, x + 9, y + 11, 6, COL_FG); | ||
1004 | } break; | ||
1005 | case 1: { | ||
1006 | txt_drawf(arr_down, x + 9, y + 11, 6, COL_FG); | ||
1007 | } break; | ||
1008 | } | ||
1009 | } | ||
1010 | |||
1011 | // Labels. | ||
1012 | { | ||
1013 | size_t x = PARAMS_START_X + x_offset; | ||
1014 | size_t y = PARAMS_START_Y + PARAMS_H - 45; | ||
1015 | // txt_drawf_small("shape", x + 1, y - 12, 4, COL_FG); | ||
1016 | // txt_drawf_small("envelope", x + 54, y - 12, 4, COL_FG); | ||
1017 | if (sweep) { | ||
1018 | // txt_drawf_small("sweep", x + 133, y - 12, 4, COL_FG); | ||
1019 | } | ||
1020 | } | ||
1021 | } | ||
1022 | |||
1023 | void | ||
1024 | draw_parameters_noise(void) { | ||
1025 | size_t x_offset = 30; | ||
1026 | Pattern *pat = &patterns[pattern_selection_loc]; | ||
1027 | ChannelNoiseParams *params = &pat->ch4.params[trig_selection_loc]; | ||
1028 | |||
1029 | // Bit mode. | ||
1030 | { | ||
1031 | // Param box. | ||
1032 | { | ||
1033 | size_t x = PARAMS_START_X + x_offset; | ||
1034 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1035 | draw_line(x, y + 7, x + 2, y + 7, COL_FG); | ||
1036 | draw_line(x + 22, y + 7, x + 24, y + 7, COL_FG); | ||
1037 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1038 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1039 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1040 | // txt_drawf_small("mode", x + 3, y + 3, 4, COL_FG); | ||
1041 | |||
1042 | switch (params->bit_mode) { | ||
1043 | case 0: { | ||
1044 | txt_drawf("A", x + 9, y + 10, 6, COL_FG); | ||
1045 | } break; | ||
1046 | case 1: { | ||
1047 | txt_drawf("B", x + 9, y + 10, 6, COL_FG); | ||
1048 | } break; | ||
1049 | } | ||
1050 | } | ||
1051 | } | ||
1052 | |||
1053 | // Envelope. | ||
1054 | { | ||
1055 | // Env. drawing. | ||
1056 | { | ||
1057 | // Bounding box. | ||
1058 | { | ||
1059 | size_t x0 = PARAMS_START_X + 31 + x_offset; | ||
1060 | size_t y0 = PARAMS_START_Y + PARAMS_H - 47; | ||
1061 | size_t x1 = x0 + 79; | ||
1062 | size_t y1 = y0 + 21; | ||
1063 | draw_rect(x0, y0, x1, y1, COL_CYAN); | ||
1064 | } | ||
1065 | |||
1066 | size_t x = PARAMS_START_X + 42 + x_offset; | ||
1067 | size_t y = PARAMS_START_Y + PARAMS_H - 44; | ||
1068 | size_t x0 = x; | ||
1069 | size_t y0 = y + 15 - params->env_volume; | ||
1070 | size_t x1 = x + 8 * params->env_time; | ||
1071 | size_t y1 = params->env_direction == 0 ? y + 15 : y; | ||
1072 | size_t x2 = x + 8 * 7 + 1; | ||
1073 | size_t y2 = y1; | ||
1074 | |||
1075 | // Env. | ||
1076 | if (params->env_time == 0) { | ||
1077 | draw_line(x1, y0, x2, y0, COL_CYAN); | ||
1078 | } else { | ||
1079 | draw_line(x0, y0, x1, y1, COL_CYAN); | ||
1080 | draw_line(x1, y1, x2, y2, COL_CYAN); | ||
1081 | } | ||
1082 | } | ||
1083 | |||
1084 | // Env. volume. | ||
1085 | { | ||
1086 | size_t x = PARAMS_START_X + 31 + x_offset; | ||
1087 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1088 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
1089 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
1090 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1091 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1092 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1093 | // txt_drawf_small("vol", x + 5, y + 3, 4, COL_FG); | ||
1094 | |||
1095 | switch (params->env_volume) { | ||
1096 | case 0: { | ||
1097 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
1098 | } break; | ||
1099 | case 1: { | ||
1100 | txt_drawf("6", x + 9, y + 10, 6, COL_FG); | ||
1101 | } break; | ||
1102 | case 2: { | ||
1103 | txt_drawf("13", x + 6, y + 10, 6, COL_FG); | ||
1104 | } break; | ||
1105 | case 3: { | ||
1106 | txt_drawf("20", x + 6, y + 10, 6, COL_FG); | ||
1107 | } break; | ||
1108 | case 4: { | ||
1109 | txt_drawf("26", x + 6, y + 10, 6, COL_FG); | ||
1110 | } break; | ||
1111 | case 5: { | ||
1112 | txt_drawf("33", x + 6, y + 10, 6, COL_FG); | ||
1113 | } break; | ||
1114 | case 6: { | ||
1115 | txt_drawf("40", x + 6, y + 10, 6, COL_FG); | ||
1116 | } break; | ||
1117 | case 7: { | ||
1118 | txt_drawf("46", x + 6, y + 10, 6, COL_FG); | ||
1119 | } break; | ||
1120 | case 8: { | ||
1121 | txt_drawf("53", x + 6, y + 10, 6, COL_FG); | ||
1122 | } break; | ||
1123 | case 9: { | ||
1124 | txt_drawf("60", x + 6, y + 10, 6, COL_FG); | ||
1125 | } break; | ||
1126 | case 10: { | ||
1127 | txt_drawf("66", x + 6, y + 10, 6, COL_FG); | ||
1128 | } break; | ||
1129 | case 11: { | ||
1130 | txt_drawf("73", x + 6, y + 10, 6, COL_FG); | ||
1131 | } break; | ||
1132 | case 12: { | ||
1133 | txt_drawf("80", x + 6, y + 10, 6, COL_FG); | ||
1134 | } break; | ||
1135 | case 13: { | ||
1136 | txt_drawf("86", x + 6, y + 10, 6, COL_FG); | ||
1137 | } break; | ||
1138 | case 14: { | ||
1139 | txt_drawf("93", x + 6, y + 10, 6, COL_FG); | ||
1140 | } break; | ||
1141 | case 15: { | ||
1142 | txt_drawf("100", x + 3, y + 10, 6, COL_FG); | ||
1143 | } break; | ||
1144 | } | ||
1145 | } | ||
1146 | |||
1147 | // Env. direction | ||
1148 | { | ||
1149 | size_t x = PARAMS_START_X + 59 + x_offset; | ||
1150 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1151 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
1152 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
1153 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1154 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1155 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1156 | // txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG); | ||
1157 | |||
1158 | char arr_up[2] = { 0x19, 0 }; | ||
1159 | char arr_down[2] = { 0x18, 0 }; | ||
1160 | switch (params->env_direction) { | ||
1161 | case 0: { | ||
1162 | txt_drawf(arr_up, x + 9, y + 11, 6, COL_FG); | ||
1163 | } break; | ||
1164 | case 1: { | ||
1165 | txt_drawf(arr_down, x + 9, y + 11, 6, COL_FG); | ||
1166 | } break; | ||
1167 | } | ||
1168 | } | ||
1169 | |||
1170 | // Env. time. | ||
1171 | { | ||
1172 | size_t x = PARAMS_START_X + 87 + x_offset; | ||
1173 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1174 | draw_line(x, y + 7, x + 2, y + 7, COL_FG); | ||
1175 | draw_line(x + 22, y + 7, x + 24, y + 7, COL_FG); | ||
1176 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1177 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1178 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1179 | // txt_drawf_small("time", x + 3, y + 3, 4, COL_FG); | ||
1180 | |||
1181 | switch (params->env_time) { | ||
1182 | case 0: { | ||
1183 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
1184 | } break; | ||
1185 | case 1: { | ||
1186 | txt_drawf("14", x + 6, y + 10, 6, COL_FG); | ||
1187 | } break; | ||
1188 | case 2: { | ||
1189 | txt_drawf("28", x + 6, y + 10, 6, COL_FG); | ||
1190 | } break; | ||
1191 | case 3: { | ||
1192 | txt_drawf("42", x + 6, y + 10, 6, COL_FG); | ||
1193 | } break; | ||
1194 | case 4: { | ||
1195 | txt_drawf("57", x + 6, y + 10, 6, COL_FG); | ||
1196 | } break; | ||
1197 | case 5: { | ||
1198 | txt_drawf("71", x + 6, y + 10, 6, COL_FG); | ||
1199 | } break; | ||
1200 | case 6: { | ||
1201 | txt_drawf("85", x + 6, y + 10, 6, COL_FG); | ||
1202 | } break; | ||
1203 | case 7: { | ||
1204 | txt_drawf("100", x + 3, y + 10, 6, COL_FG); | ||
1205 | } break; | ||
1206 | } | ||
1207 | } | ||
1208 | } | ||
1209 | |||
1210 | // Labels. | ||
1211 | { | ||
1212 | size_t x = PARAMS_START_X + x_offset; | ||
1213 | size_t y = PARAMS_START_Y + PARAMS_H - 45; | ||
1214 | // txt_drawf_small("envelope", x + 54, y - 12, 4, COL_FG); | ||
1215 | } | ||
1216 | } | ||
1217 | |||
1218 | void | ||
1219 | draw_parameters(void) { | ||
1220 | clear_parameters(); | ||
1221 | Pattern *pat = &patterns[pattern_selection_loc]; | ||
1222 | switch (channel_selection_loc) { | ||
1223 | case 0: { | ||
1224 | draw_parameters_square(&pat->ch1.params[trig_selection_loc], true); | ||
1225 | } break; | ||
1226 | case 1: { | ||
1227 | draw_parameters_square(&pat->ch2.params[trig_selection_loc], false); | ||
1228 | } break; | ||
1229 | case 2: { | ||
1230 | draw_parameters_wave(); | ||
1231 | } break; | ||
1232 | case 3: { | ||
1233 | draw_parameters_noise(); | ||
1234 | } break; | ||
1235 | } | ||
1236 | } | ||
1237 | |||
diff --git a/src/globals.c b/src/globals.c new file mode 100644 index 0000000..bf0c5b6 --- /dev/null +++ b/src/globals.c | |||
@@ -0,0 +1,104 @@ | |||
1 | // | ||
2 | // Globals. | ||
3 | // | ||
4 | |||
5 | static int step_counter = 0; | ||
6 | int trig_selection_loc = 0; | ||
7 | int param_selection_loc = 0; | ||
8 | int channel_selection_loc = 0; | ||
9 | int pattern_selection_loc = 0; | ||
10 | int right_col_selection_loc = 0; | ||
11 | int play_status = 0; | ||
12 | static int current_pattern = 0; | ||
13 | static int next_pattern = 0; | ||
14 | static int current_bank = 0; | ||
15 | |||
16 | // | ||
17 | // Color indexes. | ||
18 | // | ||
19 | |||
20 | #define COL_BG 0 | ||
21 | #define COL_FG 1 | ||
22 | #define COL_RED 2 | ||
23 | #define COL_BLUE 3 | ||
24 | #define COL_CYAN 4 | ||
25 | #define COL_GREY 5 | ||
26 | |||
27 | // Theme colors. | ||
28 | #define COL_CURSOR COL_BLUE | ||
29 | #define COL_NOTE_PRESSED COL_GREY | ||
30 | #define COL_WAVE_A COL_RED | ||
31 | #define COL_WAVE_B COL_CYAN | ||
32 | |||
33 | #define CHAN_W 19 | ||
34 | #define CHAN_H 8 | ||
35 | #define CHAN_START_X 29 | ||
36 | #define CHAN_START_Y 92 | ||
37 | #define CHAN_OFFSET_Y 12 | ||
38 | |||
39 | #define TRIG_W 15 | ||
40 | #define TRIG_H 24 | ||
41 | #define TRIG_START_X 58 | ||
42 | #define TRIG_START_Y 92 | ||
43 | #define TRIG_OFFSET_X (TRIG_W + 3) | ||
44 | #define TRIG_OFFSET_Y (TRIG_H + 7) | ||
45 | |||
46 | #define PIANO_W 170 | ||
47 | #define PIANO_H 20 | ||
48 | #define PIANO_START_X 29 | ||
49 | #define PIANO_START_Y 65 | ||
50 | #define PIANO_NOTE_W 2 | ||
51 | |||
52 | #define PARAMS_W 170 | ||
53 | #define PARAMS_H 64 | ||
54 | #define PARAMS_START_X 29 | ||
55 | #define PARAMS_START_Y 1 | ||
56 | |||
57 | #define R_SIDEBAR_X ((TRIG_START_X) + (TRIG_OFFSET_X) * 8 + 4) | ||
58 | #define L_SIDEBAR_X ((CHAN_START_X) - 26) | ||
59 | |||
60 | #define PAT_START_X (L_SIDEBAR_X + 5) | ||
61 | #define PAT_START_Y 18 | ||
62 | #define PAT_W 14 | ||
63 | #define PAT_H 12 | ||
64 | #define PAT_OFFSET_Y 17 | ||
65 | |||
66 | #define R_COL_W 24 | ||
67 | #define BPM_START_X (R_SIDEBAR_X) | ||
68 | #define BPM_START_Y (TRIG_START_Y + TRIG_H + 9) | ||
69 | #define BPM_H 22 | ||
70 | |||
71 | #define PLAY_START_X (R_SIDEBAR_X) | ||
72 | #define PLAY_START_Y (TRIG_START_Y) | ||
73 | #define STOP_START_X (R_SIDEBAR_X) | ||
74 | #define STOP_START_Y (TRIG_START_Y + 14) | ||
75 | #define PLAY_STOP_H (10) | ||
76 | |||
77 | #define BANK_START_X (R_SIDEBAR_X + 5) | ||
78 | #define BANK_START_Y (PAT_START_Y) | ||
79 | |||
80 | #define SEQ_N_CHANNELS 4 | ||
81 | |||
82 | enum RIGHT_COL_LOC { | ||
83 | R_COL_BPM = 0, | ||
84 | R_COL_STOP = 1, | ||
85 | R_COL_PLAY = 2, | ||
86 | R_COL_BANK_D = 3, | ||
87 | R_COL_BANK_C = 4, | ||
88 | R_COL_BANK_B = 5, | ||
89 | R_COL_BANK_A = 6, | ||
90 | }; | ||
91 | |||
92 | // Input handling works using a FSM. The input handler is switched to whichever | ||
93 | // function controls each section. For example, channel selection or trigger | ||
94 | // selection. | ||
95 | void (*input_handler)(void); | ||
96 | |||
97 | void handle_trigger_selection(void); | ||
98 | void handle_channel_selection(void); | ||
99 | void handle_pattern_selection(void); | ||
100 | void handle_param_selection_sq1(void); | ||
101 | void handle_param_selection_sq2(void); | ||
102 | void handle_param_selection_wave(void); | ||
103 | void handle_param_selection_noise(void); | ||
104 | void handle_right_col_selection(void); | ||
@@ -21,7 +21,11 @@ WITH REGARD TO THIS SOFTWARE. | |||
21 | 21 | ||
22 | void | 22 | void |
23 | render(void) { | 23 | render(void) { |
24 | // TODO: Fix small font rendering. | ||
25 | // TODO: Draw remaining sprites. | ||
26 | // TODO: Decouple update from rendering. | ||
24 | PROF(screen_fill(0), clear_cycles); | 27 | PROF(screen_fill(0), clear_cycles); |
28 | PROF(draw_rect(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, 1), clear_cycles); | ||
25 | PROF(draw_triggers(), draw_trigs_cycles); | 29 | PROF(draw_triggers(), draw_trigs_cycles); |
26 | PROF(draw_channels(), draw_btn_cycles); | 30 | PROF(draw_channels(), draw_btn_cycles); |
27 | PROF(draw_pattern_buttons(), draw_btn_cycles); | 31 | PROF(draw_pattern_buttons(), draw_btn_cycles); |
@@ -30,6 +34,10 @@ render(void) { | |||
30 | PROF(draw_play(), draw_btn_cycles); | 34 | PROF(draw_play(), draw_btn_cycles); |
31 | PROF(draw_stop(), draw_btn_cycles); | 35 | PROF(draw_stop(), draw_btn_cycles); |
32 | PROF(draw_piano(), draw_piano_cycles); | 36 | PROF(draw_piano(), draw_piano_cycles); |
37 | // TODO: Draw the notes currently playing with a fade off animation for the | ||
38 | // first 3 channels. | ||
39 | TriggerNote *trig = get_current_trig(); | ||
40 | PROF(draw_note(trig->note, COL_NOTE_PRESSED), draw_piano_cycles); | ||
33 | PROF(draw_parameters(), draw_param_cycles); | 41 | PROF(draw_parameters(), draw_param_cycles); |
34 | PROF(draw_trig_cursor(trig_selection_loc, COL_CURSOR), draw_cursor_cycles); | 42 | PROF(draw_trig_cursor(trig_selection_loc, COL_CURSOR), draw_cursor_cycles); |
35 | PROF(draw_channel_cursor(channel_selection_loc, COL_GREY), draw_cursor_cycles); | 43 | PROF(draw_channel_cursor(channel_selection_loc, COL_GREY), draw_cursor_cycles); |
diff --git a/src/patterns.c b/src/patterns.c new file mode 100644 index 0000000..09f7bae --- /dev/null +++ b/src/patterns.c | |||
@@ -0,0 +1,236 @@ | |||
1 | // | ||
2 | // Structs. | ||
3 | // | ||
4 | |||
5 | typedef struct TriggerNote { | ||
6 | bool active; | ||
7 | Note note; | ||
8 | } TriggerNote; | ||
9 | |||
10 | typedef struct ChannelSquareParams { | ||
11 | u8 env_volume; | ||
12 | u8 env_time; | ||
13 | u8 env_direction; | ||
14 | u8 duty_cycle; | ||
15 | u8 sweep_number; | ||
16 | u8 sweep_time; | ||
17 | u8 sweep_direction; | ||
18 | } ChannelSquareParams; | ||
19 | |||
20 | typedef struct ChannelWaveParams { | ||
21 | u8 wave_volume; | ||
22 | u8 wave_mode; | ||
23 | u32 wave_a[4]; | ||
24 | u32 wave_b[4]; | ||
25 | } ChannelWaveParams; | ||
26 | |||
27 | typedef struct ChannelNoiseParams { | ||
28 | u8 env_volume; | ||
29 | u8 env_time; | ||
30 | u8 env_direction; | ||
31 | u8 bit_mode; | ||
32 | } ChannelNoiseParams; | ||
33 | |||
34 | typedef struct ChannelSquare { | ||
35 | bool active; | ||
36 | TriggerNote notes[16]; | ||
37 | ChannelSquareParams params[16]; | ||
38 | } ChannelSquare; | ||
39 | |||
40 | typedef struct ChannelWave { | ||
41 | bool active; | ||
42 | TriggerNote notes[16]; | ||
43 | ChannelWaveParams params[16]; | ||
44 | } ChannelWave; | ||
45 | |||
46 | typedef struct ChannelNoise { | ||
47 | bool active; | ||
48 | TriggerNote notes[16]; | ||
49 | ChannelNoiseParams params[16]; | ||
50 | } ChannelNoise; | ||
51 | |||
52 | typedef struct Pattern { | ||
53 | ChannelSquare ch1; | ||
54 | ChannelSquare ch2; | ||
55 | ChannelWave ch3; | ||
56 | ChannelNoise ch4; | ||
57 | int bpm; | ||
58 | u8 bank; | ||
59 | } Pattern; | ||
60 | |||
61 | // | ||
62 | // Defaults. | ||
63 | // | ||
64 | |||
65 | const ChannelSquare default_ch1 = { | ||
66 | .notes = { | ||
67 | {true, NOTE_C_4}, | ||
68 | {true, NOTE_D_SHARP_4}, | ||
69 | {true, NOTE_G_4}, | ||
70 | {true, NOTE_A_SHARP_4}, | ||
71 | {true, NOTE_C_4}, | ||
72 | {true, NOTE_D_SHARP_4}, | ||
73 | {true, NOTE_G_4}, | ||
74 | {true, NOTE_A_SHARP_4}, | ||
75 | {true, NOTE_C_4}, | ||
76 | {true, NOTE_D_SHARP_4}, | ||
77 | {true, NOTE_G_4}, | ||
78 | {true, NOTE_A_SHARP_4}, | ||
79 | {true, NOTE_C_4}, | ||
80 | {true, NOTE_D_SHARP_4}, | ||
81 | {true, NOTE_G_4}, | ||
82 | {true, NOTE_A_SHARP_4}, | ||
83 | }, | ||
84 | .params = { | ||
85 | {8, 4, 0, 2, 0, 0, 0}, | ||
86 | {8, 4, 0, 2, 0, 0, 0}, | ||
87 | {8, 4, 0, 2, 0, 0, 0}, | ||
88 | {8, 4, 0, 2, 0, 0, 0}, | ||
89 | {8, 4, 0, 2, 0, 0, 0}, | ||
90 | {8, 4, 0, 2, 0, 0, 0}, | ||
91 | {8, 4, 0, 2, 0, 0, 0}, | ||
92 | {8, 4, 0, 2, 0, 0, 0}, | ||
93 | {8, 4, 0, 2, 0, 0, 0}, | ||
94 | {8, 4, 0, 2, 0, 0, 0}, | ||
95 | {8, 4, 0, 2, 0, 0, 0}, | ||
96 | {8, 4, 0, 2, 0, 0, 0}, | ||
97 | {8, 4, 0, 2, 0, 0, 0}, | ||
98 | {8, 4, 0, 2, 0, 0, 0}, | ||
99 | {8, 4, 0, 2, 0, 0, 0}, | ||
100 | {8, 4, 0, 2, 0, 0, 0}, | ||
101 | }, | ||
102 | .active = true, | ||
103 | }; | ||
104 | |||
105 | const ChannelSquare default_ch2 = { | ||
106 | .notes = { | ||
107 | {true, NOTE_C_3}, | ||
108 | {true, NOTE_C_3}, | ||
109 | {true, NOTE_C_3}, | ||
110 | {true, NOTE_C_3}, | ||
111 | {true, NOTE_C_3}, | ||
112 | {true, NOTE_C_3}, | ||
113 | {true, NOTE_C_3}, | ||
114 | {true, NOTE_C_3}, | ||
115 | {true, NOTE_C_3}, | ||
116 | {true, NOTE_C_3}, | ||
117 | {true, NOTE_C_3}, | ||
118 | {true, NOTE_C_3}, | ||
119 | {true, NOTE_C_3}, | ||
120 | {true, NOTE_C_3}, | ||
121 | {true, NOTE_C_3}, | ||
122 | {true, NOTE_C_3}, | ||
123 | }, | ||
124 | .params = { | ||
125 | {8, 4, 0, 2, 0, 0, 0}, | ||
126 | {8, 4, 0, 2, 0, 0, 0}, | ||
127 | {8, 4, 0, 2, 0, 0, 0}, | ||
128 | {8, 4, 0, 2, 0, 0, 0}, | ||
129 | {8, 4, 0, 2, 0, 0, 0}, | ||
130 | {8, 4, 0, 2, 0, 0, 0}, | ||
131 | {8, 4, 0, 2, 0, 0, 0}, | ||
132 | {8, 4, 0, 2, 0, 0, 0}, | ||
133 | {8, 4, 0, 2, 0, 0, 0}, | ||
134 | {8, 4, 0, 2, 0, 0, 0}, | ||
135 | {8, 4, 0, 2, 0, 0, 0}, | ||
136 | {8, 4, 0, 2, 0, 0, 0}, | ||
137 | {8, 4, 0, 2, 0, 0, 0}, | ||
138 | {8, 4, 0, 2, 0, 0, 0}, | ||
139 | {8, 4, 0, 2, 0, 0, 0}, | ||
140 | {8, 4, 0, 2, 0, 0, 0}, | ||
141 | }, | ||
142 | .active = true, | ||
143 | }; | ||
144 | |||
145 | const ChannelWave default_ch3 = { | ||
146 | .notes = { | ||
147 | {true, NOTE_C_5}, | ||
148 | {true, NOTE_C_5}, | ||
149 | {true, NOTE_C_5}, | ||
150 | {true, NOTE_A_SHARP_5}, | ||
151 | {true, NOTE_A_SHARP_5}, | ||
152 | {true, NOTE_C_5}, | ||
153 | {true, NOTE_C_5}, | ||
154 | {true, NOTE_G_5}, | ||
155 | {true, NOTE_C_5}, | ||
156 | {true, NOTE_C_5}, | ||
157 | {true, NOTE_C_5}, | ||
158 | {true, NOTE_A_SHARP_5}, | ||
159 | {true, NOTE_A_SHARP_5}, | ||
160 | {true, NOTE_C_5}, | ||
161 | {true, NOTE_C_5}, | ||
162 | {true, NOTE_G_5}, | ||
163 | }, | ||
164 | .params = { | ||
165 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
166 | {3, 0, {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
167 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
168 | {3, 0, {0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
169 | {3, 0, {0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
170 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
171 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
172 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
173 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
174 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
175 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
176 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
177 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
178 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
179 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
180 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
181 | }, | ||
182 | .active = true, | ||
183 | }; | ||
184 | |||
185 | const ChannelNoise default_ch4 = { | ||
186 | .notes = { | ||
187 | {true, NOTE_D_SHARP_4}, | ||
188 | {false, NOTE_E_6}, | ||
189 | {false, NOTE_E_6}, | ||
190 | {false, NOTE_E_6}, | ||
191 | {true, NOTE_D_SHARP_4}, | ||
192 | {false, NOTE_E_6}, | ||
193 | {false, NOTE_E_6}, | ||
194 | {false, NOTE_E_6}, | ||
195 | {true, NOTE_D_SHARP_4}, | ||
196 | {false, NOTE_E_6}, | ||
197 | {false, NOTE_E_6}, | ||
198 | {false, NOTE_E_6}, | ||
199 | {true, NOTE_D_SHARP_4}, | ||
200 | {false, NOTE_E_6}, | ||
201 | {false, NOTE_E_6}, | ||
202 | {false, NOTE_E_6}, | ||
203 | }, | ||
204 | .params = { | ||
205 | {0xF, 0x2, 0, 0}, | ||
206 | {0xF, 0x2, 0, 0}, | ||
207 | {0xF, 0x2, 0, 0}, | ||
208 | {0xF, 0x2, 0, 0}, | ||
209 | {0xF, 0x2, 0, 0}, | ||
210 | {0xF, 0x2, 0, 0}, | ||
211 | {0xF, 0x2, 0, 0}, | ||
212 | {0xF, 0x2, 0, 0}, | ||
213 | {0xF, 0x2, 0, 0}, | ||
214 | {0xF, 0x2, 0, 0}, | ||
215 | {0xF, 0x2, 0, 0}, | ||
216 | {0xF, 0x2, 0, 0}, | ||
217 | {0xF, 0x2, 0, 0}, | ||
218 | {0xF, 0x2, 0, 0}, | ||
219 | {0xF, 0x2, 0, 0}, | ||
220 | {0xF, 0x2, 0, 0}, | ||
221 | }, | ||
222 | .active = true, | ||
223 | }; | ||
224 | |||
225 | const int default_bpm = 90; | ||
226 | |||
227 | static Pattern patterns[8] = { | ||
228 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
229 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
230 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
231 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
232 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
233 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
234 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
235 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
236 | }; | ||
diff --git a/src/profiling.c b/src/profiling.c index 7a4f4ad..a3b073b 100644 --- a/src/profiling.c +++ b/src/profiling.c | |||
@@ -41,22 +41,16 @@ | |||
41 | #endif | 41 | #endif |
42 | 42 | ||
43 | static bool profile_show = true; | 43 | static bool profile_show = true; |
44 | static bool profile_bg_show = true; | ||
45 | 44 | ||
46 | #define PROF_SHOW() \ | 45 | #define PROF_SHOW() \ |
47 | do { \ | 46 | do { \ |
48 | if (key_tap(KEY_START)) {\ | 47 | if (key_tap(KEY_START)) {\ |
49 | profile_show ^= 1;\ | 48 | profile_show ^= 1;\ |
50 | }\ | 49 | }\ |
51 | if (key_tap(KEY_SELECT)) {\ | ||
52 | profile_bg_show ^= 1;\ | ||
53 | }\ | ||
54 | if (profile_show) {\ | 50 | if (profile_show) {\ |
55 | txt_color(1);\ | 51 | txt_color(1);\ |
56 | txt_position((PROF_SHOW_X), (PROF_SHOW_Y));\ | 52 | txt_position((PROF_SHOW_X), (PROF_SHOW_Y));\ |
57 | if (profile_bg_show) {\ | 53 | draw_filled_rect((PROF_SHOW_X), (PROF_SHOW_X), 8 * 14, 8 * 10, 0);\ |
58 | draw_filled_rect((PROF_SHOW_X), (PROF_SHOW_X), 8 * 18, 8 * 14, 0);\ | ||
59 | }\ | ||
60 | txt_printf("VIDEO\n");\ | 54 | txt_printf("VIDEO\n");\ |
61 | txt_printf(">CLEAR %.8lu\n", avg_clear_cycles);\ | 55 | txt_printf(">CLEAR %.8lu\n", avg_clear_cycles);\ |
62 | txt_printf(">FLIP %.8lu\n", avg_flip_cycles);\ | 56 | txt_printf(">FLIP %.8lu\n", avg_flip_cycles);\ |
@@ -66,25 +60,24 @@ static bool profile_bg_show = true; | |||
66 | txt_printf(">PARAM %.8lu\n", avg_draw_param_cycles);\ | 60 | txt_printf(">PARAM %.8lu\n", avg_draw_param_cycles);\ |
67 | txt_printf(">PIANO %.8lu\n", avg_draw_piano_cycles);\ | 61 | txt_printf(">PIANO %.8lu\n", avg_draw_piano_cycles);\ |
68 | txt_printf(">CURSOR %.8lu\n", avg_draw_cursor_cycles);\ | 62 | txt_printf(">CURSOR %.8lu\n", avg_draw_cursor_cycles);\ |
69 | txt_printf(">RENDER %.8lu\n", avg_render_cycles);\ | 63 | txt_printf("RENDER %.8lu\n", avg_render_cycles);\ |
64 | txt_printf("INPUT %.8lu\n", avg_input_cycles);\ | ||
70 | txt_printf("TOTAL %.8lu\n", avg_frame_cycles);\ | 65 | txt_printf("TOTAL %.8lu\n", avg_frame_cycles);\ |
71 | txt_render();\ | 66 | txt_render();\ |
72 | }\ | 67 | }\ |
73 | if (profile_bg_show) {\ | 68 | u32 frame_time =\ |
74 | u32 frame_time =\ | 69 | FP_DIV(\ |
75 | FP_DIV(\ | 70 | FP_NUM(avg_frame_cycles + 1, 2),\ |
76 | FP_NUM(avg_frame_cycles + 1, 2),\ | 71 | FP_NUM(2809, 2),\ |
77 | FP_NUM(2809, 2),\ | 72 | 2) * 166;\ |
78 | 2) * 166;\ | 73 | u32 fps =\ |
79 | u32 fps =\ | 74 | FP_DIV(\ |
80 | FP_DIV(\ | 75 | FP_NUM(280896 * 60, 2),\ |
81 | FP_NUM(280896 * 60, 2),\ | 76 | FP_NUM(avg_frame_cycles + 1, 2),\ |
82 | FP_NUM(avg_frame_cycles + 1, 2),\ | 77 | 2);\ |
83 | 2);\ | 78 | draw_filled_rect(8 * 18, 0, 239, 16, 0);\ |
84 | draw_filled_rect(8 * 18, 0, 239, 16, 0);\ | 79 | txt_drawf("TIME: %.6lu", 8 * 18, 0, 1, frame_time >> 2);\ |
85 | txt_drawf("TIME: %.6lu", 8 * 18, 0, 1, frame_time >> 2);\ | 80 | txt_drawf("MAX FPS: %.4lu", 8 * 18, 8, 1, (fps >> 2) + 1);\ |
86 | txt_drawf("MAX FPS:%.4lu", 8 * 18, 8, 1, (fps >> 2) + 1);\ | ||
87 | }\ | ||
88 | } while (0) | 81 | } while (0) |
89 | 82 | ||
90 | static u32 prof_frame_counter = 0; | 83 | static u32 prof_frame_counter = 0; |
diff --git a/src/renderer_m0.c b/src/renderer_m0.c index 8bd4263..00874ea 100644 --- a/src/renderer_m0.c +++ b/src/renderer_m0.c | |||
@@ -615,6 +615,84 @@ draw_icn(size_t x, size_t y, u8 *sprite, u8 clr, u8 flip_x, u8 flip_y) { | |||
615 | dirty_tiles[tile_y] |= dirty; | 615 | dirty_tiles[tile_y] |= dirty; |
616 | } | 616 | } |
617 | 617 | ||
618 | IWRAM_CODE | ||
619 | void | ||
620 | draw_tile(size_t x, size_t y, Tile *tile, u8 clr) { | ||
621 | BOUNDCHECK_SCREEN(x, y); | ||
622 | size_t tile_x0 = x / 8; | ||
623 | size_t tile_x1 = (x + 7) / 8; | ||
624 | size_t tile_y = y / 8; | ||
625 | size_t start_col = x % 8; | ||
626 | size_t start_row = y % 8; | ||
627 | size_t shift_left = start_col * 4; | ||
628 | size_t shift_right = (8 - start_col) * 4; | ||
629 | u32 dirty = (1 << tile_x0) | (1 << tile_x1); | ||
630 | u32 *dst = &backbuf[start_row + (tile_x0 + tile_y * 32) * 8]; | ||
631 | // BOUNDCHECK_SCREEN(x, y); | ||
632 | |||
633 | // // Find row position for the given x/y coordinates. | ||
634 | // size_t tile_x = x / 8; | ||
635 | // size_t tile_y = y / 8; | ||
636 | // size_t start_col = x % 8; | ||
637 | // size_t start_row = y % 8; | ||
638 | |||
639 | // // Get a pointer to the backbuffer and the tile row. | ||
640 | // size_t pos = start_row + (tile_x + tile_y * 32) * 8; | ||
641 | // u32 *backbuffer = &BACKBUF[pos]; | ||
642 | u32 *row = tile; | ||
643 | u32 row_mask_left = 0xFFFFFFFF << shift_left; | ||
644 | u32 row_mask_right = 0xFFFFFFFF >> shift_right; | ||
645 | |||
646 | // Draw the tiles. There are 4 possible cases: | ||
647 | // 1. The tile is exactly at the tile boundary. | ||
648 | // 2. The tile spans 2 tiles horizontally. | ||
649 | // 3. The tile spans 2 tiles vertically. | ||
650 | // 4. The tile spans 4 tiles. | ||
651 | if (start_col == 0 && start_row == 0) { | ||
652 | for (size_t i = 0; i < (8 - start_row); i++, dst++) { | ||
653 | BOUNDCHECK_SCREEN(x, y + i); | ||
654 | dst[0] = (dst[0] & ~row_mask_left) | row[i] * clr; | ||
655 | } | ||
656 | dirty_tiles[tile_y] |= 1 << tile_x0; | ||
657 | } else if (start_row == 0) { | ||
658 | for (size_t i = 0; i < 8; i++, dst++) { | ||
659 | BOUNDCHECK_SCREEN(x, y + i); | ||
660 | dst[0] = (dst[0] & ~row_mask_left) | (row[i] * clr << shift_left); | ||
661 | dst[8] = (dst[8] & ~row_mask_right) | (row[i] * clr >> shift_right); | ||
662 | } | ||
663 | dirty_tiles[tile_y] |= 1 << tile_x0; | ||
664 | dirty_tiles[tile_y] |= 1 << (tile_x0 + 1); | ||
665 | } else if (start_col == 0) { | ||
666 | for (size_t i = 0; i < (8 - start_row); i++, dst++) { | ||
667 | BOUNDCHECK_SCREEN(x, y + i); | ||
668 | dst[0] = (dst[0] & ~row_mask_left) | row[i] * clr; | ||
669 | } | ||
670 | dst += 8 * 31; | ||
671 | for (size_t i = (8 - start_row); i < 8; i++, dst++) { | ||
672 | BOUNDCHECK_SCREEN(x, y + i); | ||
673 | dst[0] = (dst[0] & ~row_mask_left) | row[i] * clr; | ||
674 | } | ||
675 | dirty_tiles[tile_y] |= 1 << tile_x0; | ||
676 | dirty_tiles[tile_y + 1] |= 1 << tile_x0; | ||
677 | } else { | ||
678 | for (size_t i = 0; i < (8 - start_row); i++, dst++) { | ||
679 | BOUNDCHECK_SCREEN(x, y + i); | ||
680 | dst[0] = (dst[0] & ~row_mask_left) | (row[i] * clr << shift_left); | ||
681 | dst[8] = (dst[8] & ~row_mask_right) | (row[i] * clr >> shift_right); | ||
682 | } | ||
683 | dst += 8 * 31; | ||
684 | for (size_t i = (8 - start_row); i < 8; i++, dst++) { | ||
685 | BOUNDCHECK_SCREEN(x, y + i); | ||
686 | dst[0] = (dst[0] & ~row_mask_left) | (row[i] * clr << shift_left); | ||
687 | dst[8] = (dst[8] & ~row_mask_right) | (row[i] * clr >> shift_right); | ||
688 | } | ||
689 | dirty_tiles[tile_y] |= 1 << tile_x0; | ||
690 | dirty_tiles[tile_y] |= 1 << (tile_x0 + 1); | ||
691 | dirty_tiles[tile_y + 1] |= 1 << tile_x0; | ||
692 | dirty_tiles[tile_y + 1] |= 1 << (tile_x0 + 1); | ||
693 | } | ||
694 | } | ||
695 | |||
618 | // | 696 | // |
619 | // Flipping buffers/copying memory. | 697 | // Flipping buffers/copying memory. |
620 | // | 698 | // |
diff --git a/src/save.c b/src/save.c new file mode 100644 index 0000000..45a099d --- /dev/null +++ b/src/save.c | |||
@@ -0,0 +1,37 @@ | |||
1 | // | ||
2 | // Save data. | ||
3 | // | ||
4 | |||
5 | typedef struct Metadata { | ||
6 | u8 blank; | ||
7 | u32 magic; | ||
8 | int current_bank; | ||
9 | int current_pattern; | ||
10 | } Metadata; | ||
11 | |||
12 | static Metadata metadata = {0}; | ||
13 | |||
14 | void | ||
15 | sram_read(u8 *dst, u16 pos, u16 n_bytes) { | ||
16 | for (size_t i = 0; i < n_bytes; ++i) { | ||
17 | dst[i] = SRAM[pos + i]; | ||
18 | } | ||
19 | } | ||
20 | |||
21 | void | ||
22 | sram_write(u8 *src, u16 pos, u16 n_bytes) { | ||
23 | for (size_t i = 0; i < n_bytes; ++i) { | ||
24 | SRAM[pos + i] = src[i]; | ||
25 | } | ||
26 | } | ||
27 | |||
28 | void | ||
29 | save_bank(int i) { | ||
30 | sram_write(&patterns, sizeof(Metadata) + i * sizeof(patterns), sizeof(patterns)); | ||
31 | } | ||
32 | |||
33 | void | ||
34 | load_bank(int i) { | ||
35 | sram_read(&patterns, sizeof(Metadata) + i * sizeof(patterns), sizeof(patterns)); | ||
36 | } | ||
37 | |||
diff --git a/src/sequencer.c b/src/sequencer.c index ff3904d..fd47d0b 100644 --- a/src/sequencer.c +++ b/src/sequencer.c | |||
@@ -1,1740 +1,13 @@ | |||
1 | #include "globals.c" | ||
1 | #include "rng.c" | 2 | #include "rng.c" |
2 | #include "text.h" | 3 | #include "text.h" |
3 | 4 | #include "assets.c" | |
4 | void | 5 | #include "patterns.c" |
5 | sram_read(u8 *dst, u16 pos, u16 n_bytes) { | 6 | #include "save.c" |
6 | for (size_t i = 0; i < n_bytes; ++i) { | 7 | #include "drawing.c" |
7 | dst[i] = SRAM[pos + i]; | 8 | #include "clipboard.c" |
8 | } | ||
9 | } | ||
10 | |||
11 | void | ||
12 | sram_write(u8 *src, u16 pos, u16 n_bytes) { | ||
13 | for (size_t i = 0; i < n_bytes; ++i) { | ||
14 | SRAM[pos + i] = src[i]; | ||
15 | } | ||
16 | } | ||
17 | 9 | ||
18 | void set_time(int bpm); | 10 | void set_time(int bpm); |
19 | void clipboard_paste(void); | ||
20 | void clipboard_copy(void); | ||
21 | |||
22 | // | ||
23 | // Color indexes. | ||
24 | // | ||
25 | |||
26 | #define COL_BG 0 | ||
27 | #define COL_FG 1 | ||
28 | #define COL_RED 2 | ||
29 | #define COL_BLUE 3 | ||
30 | #define COL_CYAN 4 | ||
31 | #define COL_GREY 5 | ||
32 | |||
33 | // Theme colors. | ||
34 | #define COL_CURSOR COL_BLUE | ||
35 | #define COL_NOTE_PRESSED COL_GREY | ||
36 | #define COL_WAVE_A COL_RED | ||
37 | #define COL_WAVE_B COL_CYAN | ||
38 | |||
39 | #define CHAN_W 19 | ||
40 | #define CHAN_H 8 | ||
41 | #define CHAN_START_X 29 | ||
42 | #define CHAN_START_Y 92 | ||
43 | #define CHAN_OFFSET_Y 12 | ||
44 | |||
45 | #define TRIG_W 15 | ||
46 | #define TRIG_H 24 | ||
47 | #define TRIG_START_X 58 | ||
48 | #define TRIG_START_Y 92 | ||
49 | #define TRIG_OFFSET_X (TRIG_W + 3) | ||
50 | #define TRIG_OFFSET_Y (TRIG_H + 7) | ||
51 | |||
52 | #define PIANO_W 170 | ||
53 | #define PIANO_H 20 | ||
54 | #define PIANO_START_X 29 | ||
55 | #define PIANO_START_Y 65 | ||
56 | #define PIANO_NOTE_W 2 | ||
57 | |||
58 | #define PARAMS_W 170 | ||
59 | #define PARAMS_H 64 | ||
60 | #define PARAMS_START_X 29 | ||
61 | #define PARAMS_START_Y 1 | ||
62 | |||
63 | #define R_SIDEBAR_X ((TRIG_START_X) + (TRIG_OFFSET_X) * 8 + 4) | ||
64 | #define L_SIDEBAR_X ((CHAN_START_X) - 26) | ||
65 | |||
66 | #define PAT_START_X (L_SIDEBAR_X + 5) | ||
67 | #define PAT_START_Y 18 | ||
68 | #define PAT_W 14 | ||
69 | #define PAT_H 12 | ||
70 | #define PAT_OFFSET_Y 17 | ||
71 | |||
72 | #define R_COL_W 24 | ||
73 | #define BPM_START_X (R_SIDEBAR_X) | ||
74 | #define BPM_START_Y (TRIG_START_Y + TRIG_H + 9) | ||
75 | #define BPM_H 22 | ||
76 | |||
77 | #define PLAY_START_X (R_SIDEBAR_X) | ||
78 | #define PLAY_START_Y (TRIG_START_Y) | ||
79 | #define STOP_START_X (R_SIDEBAR_X) | ||
80 | #define STOP_START_Y (TRIG_START_Y + 14) | ||
81 | #define PLAY_STOP_H (10) | ||
82 | |||
83 | #define BANK_START_X (R_SIDEBAR_X + 5) | ||
84 | #define BANK_START_Y (PAT_START_Y) | ||
85 | |||
86 | #define SEQ_N_CHANNELS 4 | ||
87 | |||
88 | // | ||
89 | // Assets. | ||
90 | // | ||
91 | |||
92 | #define N_TILES_NOTE_NAMES 73 * 2 | ||
93 | #define N_TILES_CHAN_BTSN 4 * 3 | ||
94 | #define N_TILES_WAVE_BTNS 4 * 2 | ||
95 | |||
96 | #define ASSETS_NOTE_NAMES ((u32*)(MEM_VRAM + KB(32))) | ||
97 | #define ASSETS_CHANNEL_BUTTONS (ASSETS_NOTE_NAMES + (N_TILES_NOTE_NAMES * 8)) | ||
98 | #define ASSETS_DEFAULT_WAVES (ASSETS_CHANNEL_BUTTONS + (N_TILES_CHAN_BTSN * 8)) | ||
99 | |||
100 | static const u32 note_names[] = { | ||
101 | 0x000000e0, 0x202020e0, 0x0000000e, 0x080e020e, | ||
102 | 0x00000098, 0xa8a8a898, 0x00000038, 0x203b0a39, | ||
103 | 0x00000060, 0xa0a0a060, 0x0000000e, 0x080e020e, | ||
104 | 0x000000b8, 0x889888b8, 0x00000038, 0x203b0a39, | ||
105 | 0x000000e0, 0x206020e0, 0x0000000e, 0x080e020e, | ||
106 | 0x000000e0, 0x20602020, 0x0000000e, 0x080e020e, | ||
107 | 0x000000b8, 0x8888a8b8, 0x00000038, 0x203b0a39, | ||
108 | 0x000000e0, 0x2020a0e0, 0x0000000e, 0x080e020e, | ||
109 | 0x000000b8, 0xa8a8b8a8, 0x00000038, 0x203b0a39, | ||
110 | 0x000000e0, 0xa0a0e0a0, 0x0000000e, 0x080e020e, | ||
111 | 0x000000b8, 0xa898a8b8, 0x00000038, 0x203b0a39, | ||
112 | 0x000000e0, 0xa060a0e0, 0x0000000e, 0x080e020e, | ||
113 | 0x000000e0, 0x202020e0, 0x0000000e, 0x080c080e, | ||
114 | 0x00000098, 0xa8a8a898, 0x00000038, 0x20332239, | ||
115 | 0x00000060, 0xa0a0a060, 0x0000000e, 0x080c080e, | ||
116 | 0x000000b8, 0x889888b8, 0x00000038, 0x20332239, | ||
117 | 0x000000e0, 0x206020e0, 0x0000000e, 0x080c080e, | ||
118 | 0x000000e0, 0x20602020, 0x0000000e, 0x080c080e, | ||
119 | 0x000000b8, 0x8888a8b8, 0x00000038, 0x20332239, | ||
120 | 0x000000e0, 0x2020a0e0, 0x0000000e, 0x080c080e, | ||
121 | 0x000000b8, 0xa8a8b8a8, 0x00000038, 0x20332239, | ||
122 | 0x000000e0, 0xa0a0e0a0, 0x0000000e, 0x080c080e, | ||
123 | 0x000000b8, 0xa898a8b8, 0x00000038, 0x20332239, | ||
124 | 0x000000e0, 0xa060a0e0, 0x0000000e, 0x080c080e, | ||
125 | 0x000000e0, 0x202020e0, 0x0000000a, 0x0a0e0808, | ||
126 | 0x00000098, 0xa8a8a898, 0x00000028, 0x283b2221, | ||
127 | 0x00000060, 0xa0a0a060, 0x0000000a, 0x0a0e0808, | ||
128 | 0x000000b8, 0x889888b8, 0x00000028, 0x283b2221, | ||
129 | 0x000000e0, 0x206020e0, 0x0000000a, 0x0a0e0808, | ||
130 | 0x000000e0, 0x20602020, 0x0000000a, 0x0a0e0808, | ||
131 | 0x000000b8, 0x8888a8b8, 0x00000028, 0x283b2221, | ||
132 | 0x000000e0, 0x2020a0e0, 0x0000000a, 0x0a0e0808, | ||
133 | 0x000000b8, 0xa8a8b8a8, 0x00000028, 0x283b2221, | ||
134 | 0x000000e0, 0xa0a0e0a0, 0x0000000a, 0x0a0e0808, | ||
135 | 0x000000b8, 0xa898a8b8, 0x00000028, 0x283b2221, | ||
136 | 0x000000e0, 0xa060a0e0, 0x0000000a, 0x0a0e0808, | ||
137 | 0x000000e0, 0x202020e0, 0x0000000e, 0x020e080e, | ||
138 | 0x00000098, 0xa8a8a898, 0x00000038, 0x083b2239, | ||
139 | 0x00000060, 0xa0a0a060, 0x0000000e, 0x020e080e, | ||
140 | 0x000000b8, 0x889888b8, 0x00000038, 0x083b2239, | ||
141 | 0x000000e0, 0x206020e0, 0x0000000e, 0x020e080e, | ||
142 | 0x000000e0, 0x20602020, 0x0000000e, 0x020e080e, | ||
143 | 0x000000b8, 0x8888a8b8, 0x00000038, 0x083b2239, | ||
144 | 0x000000e0, 0x2020a0e0, 0x0000000e, 0x020e080e, | ||
145 | 0x000000b8, 0xa8a8b8a8, 0x00000038, 0x083b2239, | ||
146 | 0x000000e0, 0xa0a0e0a0, 0x0000000e, 0x020e080e, | ||
147 | 0x000000b8, 0xa898a8b8, 0x00000038, 0x083b2239, | ||
148 | 0x000000e0, 0xa060a0e0, 0x0000000e, 0x020e080e, | ||
149 | 0x000000e0, 0x202020e0, 0x0000000e, 0x020e0a0e, | ||
150 | 0x00000098, 0xa8a8a898, 0x00000038, 0x083b2a39, | ||
151 | 0x00000060, 0xa0a0a060, 0x0000000e, 0x020e0a0e, | ||
152 | 0x000000b8, 0x889888b8, 0x00000038, 0x083b2a39, | ||
153 | 0x000000e0, 0x206020e0, 0x0000000e, 0x020e0a0e, | ||
154 | 0x000000e0, 0x20602020, 0x0000000e, 0x020e0a0e, | ||
155 | 0x000000b8, 0x8888a8b8, 0x00000038, 0x083b2a39, | ||
156 | 0x000000e0, 0x2020a0e0, 0x0000000e, 0x020e0a0e, | ||
157 | 0x000000b8, 0xa8a8b8a8, 0x00000038, 0x083b2a39, | ||
158 | 0x000000e0, 0xa0a0e0a0, 0x0000000e, 0x020e0a0e, | ||
159 | 0x000000b8, 0xa898a8b8, 0x00000038, 0x083b2a39, | ||
160 | 0x000000e0, 0xa060a0e0, 0x0000000e, 0x020e0a0e, | ||
161 | 0x000000e0, 0x202020e0, 0x0000000e, 0x08040202, | ||
162 | 0x00000098, 0xa8a8a898, 0x00000038, 0x20130a09, | ||
163 | 0x00000060, 0xa0a0a060, 0x0000000e, 0x08040202, | ||
164 | 0x000000b8, 0x889888b8, 0x00000038, 0x20130a09, | ||
165 | 0x000000e0, 0x206020e0, 0x0000000e, 0x08040202, | ||
166 | 0x000000e0, 0x20602020, 0x0000000e, 0x08040202, | ||
167 | 0x000000b8, 0x8888a8b8, 0x00000038, 0x20130a09, | ||
168 | 0x000000e0, 0x2020a0e0, 0x0000000e, 0x08040202, | ||
169 | 0x000000b8, 0xa8a8b8a8, 0x00000038, 0x20130a09, | ||
170 | 0x000000e0, 0xa0a0e0a0, 0x0000000e, 0x08040202, | ||
171 | 0x000000b8, 0xa898a8b8, 0x00000038, 0x20130a09, | ||
172 | 0x000000e0, 0xa060a0e0, 0x0000000e, 0x08040202, | ||
173 | 0x000000e0, 0x202020e0, 0x0000000e, 0x0a0e0a0e, | ||
174 | }; | ||
175 | |||
176 | static const u32 channel_buttons[] = { | ||
177 | 0xff017111, 0x117101ff, 0xff008585, 0x879500ff, | ||
178 | 0x0f080808, 0x0808080f, 0xff01b989, 0x89b901ff, | ||
179 | 0xff004242, 0x434a00ff, 0x0f080909, 0x0909080f, | ||
180 | 0xff015d45, 0xc55d01ff, 0xff00a1a1, 0xa1a500ff, | ||
181 | 0x0f080a0a, 0x0a0a080f, 0xff015d45, 0xc55d01ff, | ||
182 | 0xff00a1a1, 0xa12500ff, 0x0f080a0a, 0x0a0b080f, | ||
183 | 0xff01c141, 0xc14101ff, 0xff00151c, 0x141400ff, | ||
184 | 0x0f080808, 0x0808080f, | ||
185 | }; | ||
186 | |||
187 | static const u32 default_wave_buttons[] = { | ||
188 | 0xff013149, 0x850101ff, 0x3f202028, 0x2423203f, | ||
189 | 0xff016151, 0x49c501ff, 0x3f202c2a, 0x2928203f, | ||
190 | 0xff017d45, 0x45c501ff, 0x3f202828, 0x282f203f, | ||
191 | 0xff014911, 0x812501ff, 0x3f202128, 0x2420203f, | ||
192 | }; | ||
193 | |||
194 | // | ||
195 | // Wave data. | ||
196 | // | ||
197 | |||
198 | static const u32 sine_wave[16] = { | ||
199 | 0xefdebc89, 0x98cbedfe, 0x10214376, 0x67341201, | ||
200 | }; | ||
201 | |||
202 | static const u32 saw_wave[16] = { | ||
203 | 0x67452301, 0xefcdab89, 0x67452301, 0xefcdab89, | ||
204 | }; | ||
205 | |||
206 | static const u32 square_wave[16] = { | ||
207 | 0xffffffff, 0xffffffff, 0x00000000, 0x00000000, | ||
208 | }; | ||
209 | |||
210 | // | ||
211 | // Globals. | ||
212 | // | ||
213 | |||
214 | const int default_bpm = 75; | ||
215 | static int step_counter = 0; | ||
216 | int trig_selection_loc = 0; | ||
217 | int param_selection_loc = 0; | ||
218 | int channel_selection_loc = 0; | ||
219 | int pattern_selection_loc = 0; | ||
220 | int right_col_selection_loc = 0; | ||
221 | int play_status = 0; | ||
222 | static int current_pattern = 0; | ||
223 | static int next_pattern = 0; | ||
224 | static int current_bank = 0; | ||
225 | |||
226 | enum RIGHT_COL_LOC { | ||
227 | R_COL_BPM = 0, | ||
228 | R_COL_STOP = 1, | ||
229 | R_COL_PLAY = 2, | ||
230 | R_COL_BANK_D = 3, | ||
231 | R_COL_BANK_C = 4, | ||
232 | R_COL_BANK_B = 5, | ||
233 | R_COL_BANK_A = 6, | ||
234 | }; | ||
235 | |||
236 | typedef struct TriggerNote { | ||
237 | bool active; | ||
238 | Note note; | ||
239 | } TriggerNote; | ||
240 | |||
241 | typedef struct ChannelSquareParams { | ||
242 | u8 env_volume; | ||
243 | u8 env_time; | ||
244 | u8 env_direction; | ||
245 | u8 duty_cycle; | ||
246 | u8 sweep_number; | ||
247 | u8 sweep_time; | ||
248 | u8 sweep_direction; | ||
249 | } ChannelSquareParams; | ||
250 | |||
251 | typedef struct ChannelWaveParams { | ||
252 | u8 wave_volume; | ||
253 | u8 wave_mode; | ||
254 | u32 wave_a[4]; | ||
255 | u32 wave_b[4]; | ||
256 | } ChannelWaveParams; | ||
257 | |||
258 | typedef struct ChannelNoiseParams { | ||
259 | u8 env_volume; | ||
260 | u8 env_time; | ||
261 | u8 env_direction; | ||
262 | u8 bit_mode; | ||
263 | } ChannelNoiseParams; | ||
264 | |||
265 | typedef struct ChannelSquare { | ||
266 | bool active; | ||
267 | TriggerNote notes[16]; | ||
268 | ChannelSquareParams params[16]; | ||
269 | } ChannelSquare; | ||
270 | |||
271 | typedef struct ChannelWave { | ||
272 | bool active; | ||
273 | TriggerNote notes[16]; | ||
274 | ChannelWaveParams params[16]; | ||
275 | } ChannelWave; | ||
276 | |||
277 | typedef struct ChannelNoise { | ||
278 | bool active; | ||
279 | TriggerNote notes[16]; | ||
280 | ChannelNoiseParams params[16]; | ||
281 | } ChannelNoise; | ||
282 | |||
283 | const ChannelSquare default_ch1 = { | ||
284 | .notes = { | ||
285 | {true, NOTE_C_4}, | ||
286 | {true, NOTE_D_SHARP_4}, | ||
287 | {true, NOTE_G_4}, | ||
288 | {true, NOTE_A_SHARP_4}, | ||
289 | {true, NOTE_C_4}, | ||
290 | {true, NOTE_D_SHARP_4}, | ||
291 | {true, NOTE_G_4}, | ||
292 | {true, NOTE_A_SHARP_4}, | ||
293 | {true, NOTE_C_4}, | ||
294 | {true, NOTE_D_SHARP_4}, | ||
295 | {true, NOTE_G_4}, | ||
296 | {true, NOTE_A_SHARP_4}, | ||
297 | {true, NOTE_C_4}, | ||
298 | {true, NOTE_D_SHARP_4}, | ||
299 | {true, NOTE_G_4}, | ||
300 | {true, NOTE_A_SHARP_4}, | ||
301 | }, | ||
302 | .params = { | ||
303 | {8, 4, 0, 2, 0, 0, 0}, | ||
304 | {8, 4, 0, 2, 0, 0, 0}, | ||
305 | {8, 4, 0, 2, 0, 0, 0}, | ||
306 | {8, 4, 0, 2, 0, 0, 0}, | ||
307 | {8, 4, 0, 2, 0, 0, 0}, | ||
308 | {8, 4, 0, 2, 0, 0, 0}, | ||
309 | {8, 4, 0, 2, 0, 0, 0}, | ||
310 | {8, 4, 0, 2, 0, 0, 0}, | ||
311 | {8, 4, 0, 2, 0, 0, 0}, | ||
312 | {8, 4, 0, 2, 0, 0, 0}, | ||
313 | {8, 4, 0, 2, 0, 0, 0}, | ||
314 | {8, 4, 0, 2, 0, 0, 0}, | ||
315 | {8, 4, 0, 2, 0, 0, 0}, | ||
316 | {8, 4, 0, 2, 0, 0, 0}, | ||
317 | {8, 4, 0, 2, 0, 0, 0}, | ||
318 | {8, 4, 0, 2, 0, 0, 0}, | ||
319 | }, | ||
320 | .active = true, | ||
321 | }; | ||
322 | |||
323 | const ChannelSquare default_ch2 = { | ||
324 | .notes = { | ||
325 | {true, NOTE_C_3}, | ||
326 | {true, NOTE_C_3}, | ||
327 | {true, NOTE_C_3}, | ||
328 | {true, NOTE_C_3}, | ||
329 | {true, NOTE_C_3}, | ||
330 | {true, NOTE_C_3}, | ||
331 | {true, NOTE_C_3}, | ||
332 | {true, NOTE_C_3}, | ||
333 | {true, NOTE_C_3}, | ||
334 | {true, NOTE_C_3}, | ||
335 | {true, NOTE_C_3}, | ||
336 | {true, NOTE_C_3}, | ||
337 | {true, NOTE_C_3}, | ||
338 | {true, NOTE_C_3}, | ||
339 | {true, NOTE_C_3}, | ||
340 | {true, NOTE_C_3}, | ||
341 | }, | ||
342 | .params = { | ||
343 | {8, 4, 0, 2, 0, 0, 0}, | ||
344 | {8, 4, 0, 2, 0, 0, 0}, | ||
345 | {8, 4, 0, 2, 0, 0, 0}, | ||
346 | {8, 4, 0, 2, 0, 0, 0}, | ||
347 | {8, 4, 0, 2, 0, 0, 0}, | ||
348 | {8, 4, 0, 2, 0, 0, 0}, | ||
349 | {8, 4, 0, 2, 0, 0, 0}, | ||
350 | {8, 4, 0, 2, 0, 0, 0}, | ||
351 | {8, 4, 0, 2, 0, 0, 0}, | ||
352 | {8, 4, 0, 2, 0, 0, 0}, | ||
353 | {8, 4, 0, 2, 0, 0, 0}, | ||
354 | {8, 4, 0, 2, 0, 0, 0}, | ||
355 | {8, 4, 0, 2, 0, 0, 0}, | ||
356 | {8, 4, 0, 2, 0, 0, 0}, | ||
357 | {8, 4, 0, 2, 0, 0, 0}, | ||
358 | {8, 4, 0, 2, 0, 0, 0}, | ||
359 | }, | ||
360 | .active = true, | ||
361 | }; | ||
362 | |||
363 | const ChannelWave default_ch3 = { | ||
364 | .notes = { | ||
365 | {true, NOTE_C_5}, | ||
366 | {true, NOTE_C_5}, | ||
367 | {true, NOTE_C_5}, | ||
368 | {true, NOTE_A_SHARP_5}, | ||
369 | {true, NOTE_A_SHARP_5}, | ||
370 | {true, NOTE_C_5}, | ||
371 | {true, NOTE_C_5}, | ||
372 | {true, NOTE_G_5}, | ||
373 | {true, NOTE_C_5}, | ||
374 | {true, NOTE_C_5}, | ||
375 | {true, NOTE_C_5}, | ||
376 | {true, NOTE_A_SHARP_5}, | ||
377 | {true, NOTE_A_SHARP_5}, | ||
378 | {true, NOTE_C_5}, | ||
379 | {true, NOTE_C_5}, | ||
380 | {true, NOTE_G_5}, | ||
381 | }, | ||
382 | .params = { | ||
383 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
384 | {3, 0, {0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
385 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
386 | {3, 0, {0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
387 | {3, 0, {0x00000000, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
388 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
389 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
390 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
391 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
392 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
393 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
394 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
395 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
396 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
397 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
398 | {3, 0, {0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000},{0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000}}, | ||
399 | }, | ||
400 | .active = true, | ||
401 | }; | ||
402 | |||
403 | const ChannelNoise default_ch4 = { | ||
404 | .notes = { | ||
405 | {true, NOTE_D_SHARP_4}, | ||
406 | {false, NOTE_E_6}, | ||
407 | {false, NOTE_E_6}, | ||
408 | {false, NOTE_E_6}, | ||
409 | {true, NOTE_D_SHARP_4}, | ||
410 | {false, NOTE_E_6}, | ||
411 | {false, NOTE_E_6}, | ||
412 | {false, NOTE_E_6}, | ||
413 | {true, NOTE_D_SHARP_4}, | ||
414 | {false, NOTE_E_6}, | ||
415 | {false, NOTE_E_6}, | ||
416 | {false, NOTE_E_6}, | ||
417 | {true, NOTE_D_SHARP_4}, | ||
418 | {false, NOTE_E_6}, | ||
419 | {false, NOTE_E_6}, | ||
420 | {false, NOTE_E_6}, | ||
421 | }, | ||
422 | .params = { | ||
423 | {0xF, 0x2, 0, 0}, | ||
424 | {0xF, 0x2, 0, 0}, | ||
425 | {0xF, 0x2, 0, 0}, | ||
426 | {0xF, 0x2, 0, 0}, | ||
427 | {0xF, 0x2, 0, 0}, | ||
428 | {0xF, 0x2, 0, 0}, | ||
429 | {0xF, 0x2, 0, 0}, | ||
430 | {0xF, 0x2, 0, 0}, | ||
431 | {0xF, 0x2, 0, 0}, | ||
432 | {0xF, 0x2, 0, 0}, | ||
433 | {0xF, 0x2, 0, 0}, | ||
434 | {0xF, 0x2, 0, 0}, | ||
435 | {0xF, 0x2, 0, 0}, | ||
436 | {0xF, 0x2, 0, 0}, | ||
437 | {0xF, 0x2, 0, 0}, | ||
438 | {0xF, 0x2, 0, 0}, | ||
439 | }, | ||
440 | .active = true, | ||
441 | }; | ||
442 | |||
443 | typedef struct Metadata { | ||
444 | u8 blank; | ||
445 | u32 magic; | ||
446 | int current_bank; | ||
447 | int current_pattern; | ||
448 | } Metadata; | ||
449 | |||
450 | static Metadata metadata = {0}; | ||
451 | |||
452 | typedef struct Pattern { | ||
453 | ChannelSquare ch1; | ||
454 | ChannelSquare ch2; | ||
455 | ChannelWave ch3; | ||
456 | ChannelNoise ch4; | ||
457 | int bpm; | ||
458 | u8 bank; | ||
459 | } Pattern; | ||
460 | |||
461 | static Pattern patterns[8] = { | ||
462 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
463 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
464 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
465 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
466 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
467 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
468 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
469 | {default_ch1, default_ch2, default_ch3, default_ch4, default_bpm, 0}, | ||
470 | }; | ||
471 | |||
472 | typedef enum ClipboardType { | ||
473 | CLIP_EMPTY, | ||
474 | CLIP_TRIG, | ||
475 | CLIP_PARAM_CH1, | ||
476 | CLIP_PARAM_CH2, | ||
477 | CLIP_PARAM_CH3, | ||
478 | CLIP_PARAM_CH4, | ||
479 | CLIP_PATTERN, | ||
480 | CLIP_CHANNEL, | ||
481 | } ClipboardType; | ||
482 | |||
483 | typedef struct Clipboard { | ||
484 | ClipboardType type; | ||
485 | u8 src_pat; | ||
486 | u8 src_chan; | ||
487 | u8 src_trig; | ||
488 | } Clipboard; | ||
489 | |||
490 | static Clipboard clipboard = {CLIP_EMPTY, 0, 0, 0}; | ||
491 | |||
492 | void | ||
493 | save_bank(int i) { | ||
494 | sram_write(&patterns, sizeof(Metadata) + i * sizeof(patterns), sizeof(patterns)); | ||
495 | } | ||
496 | |||
497 | void | ||
498 | load_bank(int i) { | ||
499 | sram_read(&patterns, sizeof(Metadata) + i * sizeof(patterns), sizeof(patterns)); | ||
500 | } | ||
501 | |||
502 | // | ||
503 | // Channel render functions. | ||
504 | // | ||
505 | |||
506 | void | ||
507 | draw_channels(void) { | ||
508 | // Contains 5 channel buttons: Ch. 1-4 + FM. We are only drawing the DMG | ||
509 | // channels for now, since FM may take some time to develop. | ||
510 | Tile *channel_tiles = ASSETS_CHANNEL_BUTTONS; | ||
511 | size_t k = 0; | ||
512 | for (size_t i = 0; i < 4; i++) { | ||
513 | bool active = false; | ||
514 | switch (i) { | ||
515 | case 0: { | ||
516 | active = patterns[pattern_selection_loc].ch1.active; | ||
517 | } break; | ||
518 | case 1: { | ||
519 | active = patterns[pattern_selection_loc].ch2.active; | ||
520 | } break; | ||
521 | case 2: { | ||
522 | active = patterns[pattern_selection_loc].ch3.active; | ||
523 | } break; | ||
524 | case 3: { | ||
525 | active = patterns[pattern_selection_loc].ch4.active; | ||
526 | } break; | ||
527 | } | ||
528 | u8 clr = active ? COL_FG : COL_GREY; | ||
529 | size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; | ||
530 | // draw_tile(CHAN_START_X, y, channel_tiles + k++, clr, false); | ||
531 | // draw_tile(CHAN_START_X + 8, y, channel_tiles + k++, clr, false); | ||
532 | // draw_tile(CHAN_START_X + 16, y, channel_tiles + k++, clr, false); | ||
533 | } | ||
534 | } | ||
535 | |||
536 | void | ||
537 | draw_channel_cursor(size_t i, u8 clr) { | ||
538 | size_t offset_x = 0; | ||
539 | size_t offset_y = CHAN_H + i * CHAN_OFFSET_Y + 1; | ||
540 | size_t x0 = CHAN_START_X + offset_x; | ||
541 | size_t x1 = CHAN_START_X + offset_x + CHAN_W; | ||
542 | size_t y = CHAN_START_Y + offset_y; | ||
543 | draw_line(x0, y, x1, y, clr); | ||
544 | } | ||
545 | |||
546 | // | ||
547 | // Trigger render functions. | ||
548 | // | ||
549 | |||
550 | void | ||
551 | clear_trigger(size_t i) { | ||
552 | size_t offset_x = TRIG_OFFSET_X * (i % 8); | ||
553 | size_t offset_y = i < 8 ? 0 : TRIG_OFFSET_Y; | ||
554 | size_t x0 = TRIG_START_X + offset_x + 1; | ||
555 | size_t x1 = TRIG_START_X + offset_x + TRIG_W - 1; | ||
556 | size_t y0 = TRIG_START_Y + offset_y + 1; | ||
557 | size_t y1 = TRIG_START_Y + offset_y + TRIG_H - 4; | ||
558 | draw_filled_rect(x0, y0, x1, y1, COL_BG); | ||
559 | } | ||
560 | |||
561 | void | ||
562 | draw_trigger(size_t chan, size_t i) { | ||
563 | TriggerNote trig = {0}; | ||
564 | switch (chan) { | ||
565 | case 0: { | ||
566 | trig = patterns[pattern_selection_loc].ch1.notes[i]; | ||
567 | } break; | ||
568 | case 1: { | ||
569 | trig = patterns[pattern_selection_loc].ch2.notes[i]; | ||
570 | } break; | ||
571 | case 2: { | ||
572 | trig = patterns[pattern_selection_loc].ch3.notes[i]; | ||
573 | } break; | ||
574 | case 3: { | ||
575 | trig = patterns[pattern_selection_loc].ch4.notes[i]; | ||
576 | } break; | ||
577 | } | ||
578 | if (trig.active) { | ||
579 | size_t offset_x = TRIG_OFFSET_X * (i % 8); | ||
580 | size_t offset_y = i < 8 ? 0 : TRIG_OFFSET_Y; | ||
581 | size_t x = TRIG_START_X + offset_x; | ||
582 | size_t y = TRIG_START_Y + offset_y; | ||
583 | Tile *tiles = ASSETS_NOTE_NAMES; | ||
584 | tiles += 2 * trig.note; | ||
585 | // draw_tile(x, y, tiles, COL_FG, true); | ||
586 | // draw_tile(x + 8, y, tiles + 1, COL_FG, true); | ||
587 | } else { | ||
588 | clear_trigger(i); | ||
589 | } | ||
590 | } | ||
591 | |||
592 | void | ||
593 | draw_trig_cursor(size_t i, u8 clr) { | ||
594 | size_t offset_x = TRIG_OFFSET_X * (i % 8); | ||
595 | size_t offset_y = i < 8 ? 2 : 2 + TRIG_OFFSET_Y; | ||
596 | size_t x0 = TRIG_START_X + offset_x; | ||
597 | size_t x1 = TRIG_START_X + TRIG_W + offset_x; | ||
598 | size_t y = TRIG_START_Y + TRIG_H + offset_y; | ||
599 | draw_line(x0, y, x1, y, clr); | ||
600 | } | ||
601 | |||
602 | void | ||
603 | draw_right_col_cursor(u8 clr) { | ||
604 | size_t x0 = 0; | ||
605 | size_t x1 = 0; | ||
606 | size_t y = 0; | ||
607 | switch (right_col_selection_loc) { | ||
608 | case R_COL_BPM: { | ||
609 | x0 = BPM_START_X; | ||
610 | x1 = x0 + R_COL_W; | ||
611 | y = BPM_START_Y + BPM_H + 2; | ||
612 | } break; | ||
613 | case R_COL_STOP: { | ||
614 | x0 = STOP_START_X; | ||
615 | x1 = x0 + R_COL_W; | ||
616 | y = STOP_START_Y + PLAY_STOP_H + 2; | ||
617 | } break; | ||
618 | case R_COL_PLAY: { | ||
619 | x0 = PLAY_START_X; | ||
620 | x1 = x0 + R_COL_W; | ||
621 | y = PLAY_START_Y + PLAY_STOP_H + 2; | ||
622 | } break; | ||
623 | case R_COL_BANK_A: { | ||
624 | x0 = BANK_START_X; | ||
625 | x1 = x0 + PAT_W; | ||
626 | y = BANK_START_Y + PAT_H + 2; | ||
627 | } break; | ||
628 | case R_COL_BANK_B: { | ||
629 | x0 = BANK_START_X; | ||
630 | x1 = x0 + PAT_W; | ||
631 | y = BANK_START_Y + PAT_H + 2 + 1 * PAT_OFFSET_Y; | ||
632 | } break; | ||
633 | case R_COL_BANK_C: { | ||
634 | x0 = BANK_START_X; | ||
635 | x1 = x0 + PAT_W; | ||
636 | y = BANK_START_Y + PAT_H + 2 + 2 * PAT_OFFSET_Y; | ||
637 | } break; | ||
638 | case R_COL_BANK_D: { | ||
639 | x0 = BANK_START_X; | ||
640 | x1 = x0 + PAT_W; | ||
641 | y = BANK_START_Y + PAT_H + 2 + 3 * PAT_OFFSET_Y; | ||
642 | } break; | ||
643 | } | ||
644 | draw_line(x0, y, x1, y, clr); | ||
645 | } | ||
646 | |||
647 | void | ||
648 | draw_current_step(u8 col) { | ||
649 | size_t offset_x = TRIG_OFFSET_X * (step_counter % 8); | ||
650 | size_t offset_y = step_counter < 8 ? 2 : 2 + TRIG_OFFSET_Y; | ||
651 | size_t x0 = TRIG_START_X + 3 + offset_x; | ||
652 | size_t x1 = TRIG_START_X - 3 + TRIG_W + offset_x; | ||
653 | size_t y = TRIG_START_Y - 4 + TRIG_H + offset_y; | ||
654 | draw_line(x0, y, x1, y, col); | ||
655 | } | ||
656 | |||
657 | void | ||
658 | draw_bank_buttons() { | ||
659 | size_t x = BANK_START_X; | ||
660 | size_t y = BANK_START_Y; | ||
661 | // txt_drawf_small("BANK", x - 2, y - 10, 4, COL_FG); | ||
662 | char bank_names[] = { | ||
663 | 'A', 'B', 'C', 'D', | ||
664 | }; | ||
665 | for (int i = 0; i < 4; i++) { | ||
666 | int color = COL_GREY; | ||
667 | if (i == current_bank) { | ||
668 | color = COL_FG; | ||
669 | } | ||
670 | draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG); | ||
671 | draw_rect(x, y, x + PAT_W, y + PAT_H, color); | ||
672 | // txt_drawc(bank_names[i], x + 4, y + 2, 6, color); | ||
673 | y += PAT_OFFSET_Y; | ||
674 | } | ||
675 | } | ||
676 | |||
677 | void | ||
678 | draw_pattern_buttons() { | ||
679 | size_t x = PAT_START_X; | ||
680 | size_t y = PAT_START_Y; | ||
681 | // txt_drawf_small("PAT", x, y - 10, 4, COL_FG); | ||
682 | char pat_names[] = { | ||
683 | 'A', 'B', 'C', 'D', | ||
684 | 'E', 'F', 'G', 'H', | ||
685 | }; | ||
686 | for (int i = 0; i < 8; i++) { | ||
687 | int color = COL_GREY; | ||
688 | if (i == current_pattern) { | ||
689 | color = COL_FG; | ||
690 | } | ||
691 | if (i == next_pattern && current_pattern != next_pattern) { | ||
692 | color = COL_BLUE; | ||
693 | } | ||
694 | draw_filled_rect(x, y, x + PAT_W, y + PAT_H, COL_BG); | ||
695 | draw_rect(x, y, x + PAT_W, y + PAT_H, color); | ||
696 | // txt_drawc(pat_names[i], x + 4, y + 2, 6, color); | ||
697 | y += PAT_OFFSET_Y; | ||
698 | } | ||
699 | } | ||
700 | |||
701 | void | ||
702 | draw_pattern_cursor(size_t i, u8 clr) { | ||
703 | size_t offset_x = 0; | ||
704 | size_t offset_y = PAT_H + i * PAT_OFFSET_Y + 2; | ||
705 | size_t x0 = PAT_START_X + offset_x; | ||
706 | size_t x1 = PAT_START_X + offset_x + PAT_W; | ||
707 | size_t y = PAT_START_Y + offset_y; | ||
708 | draw_line(x0, y, x1, y, clr); | ||
709 | } | ||
710 | |||
711 | void | ||
712 | draw_play() { | ||
713 | size_t x = PLAY_START_X; | ||
714 | size_t y = PLAY_START_Y; | ||
715 | draw_filled_rect(x, y, x + R_COL_W, y + PLAY_STOP_H, COL_BG); | ||
716 | draw_rect(x, y, x + R_COL_W, y + PLAY_STOP_H, COL_CYAN); | ||
717 | if (play_status == 1) { | ||
718 | // Pause button | ||
719 | draw_filled_rect(x + 10, y + 3, x + 11, y + 7, COL_CYAN); | ||
720 | draw_filled_rect(x + 13, y + 3, x + 14, y + 7, COL_CYAN); | ||
721 | } else { | ||
722 | // Play button | ||
723 | x += 1; | ||
724 | draw_line(x + 10, y + 2, x + 10, y + 8, COL_CYAN); | ||
725 | draw_line(x + 11, y + 3, x + 11, y + 7, COL_CYAN); | ||
726 | draw_line(x + 12, y + 4, x + 12, y + 6, COL_CYAN); | ||
727 | draw_line(x + 13, y + 5, x + 13, y + 5, COL_CYAN); | ||
728 | } | ||
729 | } | ||
730 | |||
731 | void | ||
732 | draw_stop() { | ||
733 | size_t x = STOP_START_X; | ||
734 | size_t y = STOP_START_Y; | ||
735 | draw_rect(x, y, x + R_COL_W, y + PLAY_STOP_H, COL_RED); | ||
736 | draw_filled_rect(x + 10, y + 3, x + 14, y + 7, COL_RED); | ||
737 | } | ||
738 | |||
739 | void | ||
740 | draw_bpm() { | ||
741 | size_t x = BPM_START_X; | ||
742 | size_t y = BPM_START_Y; | ||
743 | |||
744 | // Draw bounding box. | ||
745 | draw_filled_rect(x, y, x + R_COL_W, y + BPM_H, COL_BG); | ||
746 | draw_rect(x, y, x + R_COL_W, y + BPM_H, COL_FG); | ||
747 | draw_line(x + 5, y, x + 19, y, COL_BG); | ||
748 | // txt_drawf_small("BPM", x + 5, y - 4, 4, COL_FG); | ||
749 | |||
750 | // Make sure its horizontally centered if only 2 digits | ||
751 | int bpm = patterns[pattern_selection_loc].bpm; | ||
752 | if (bpm >= 100) { | ||
753 | txt_drawf("%d", x + 3, y + 7, 6, COL_FG, bpm); | ||
754 | } else { | ||
755 | txt_drawf("%d", x + 6, y + 7, 6, COL_FG, bpm); | ||
756 | } | ||
757 | } | ||
758 | |||
759 | void | ||
760 | draw_triggers(void) { | ||
761 | for (size_t i = 0; i < 16; i++) { | ||
762 | size_t offset_x = TRIG_OFFSET_X * (i % 8); | ||
763 | size_t offset_y = i < 8 ? 0 : 0 + TRIG_OFFSET_Y; | ||
764 | size_t x0 = TRIG_START_X + offset_x; | ||
765 | size_t x1 = TRIG_START_X + offset_x + TRIG_W; | ||
766 | size_t y0 = TRIG_START_Y + offset_y; | ||
767 | size_t y1 = TRIG_START_Y + offset_y + TRIG_H; | ||
768 | draw_rect(x0, y0, x1, y1, COL_FG); | ||
769 | clear_trigger(i); | ||
770 | draw_trigger(channel_selection_loc, i); | ||
771 | } | ||
772 | } | ||
773 | |||
774 | void | ||
775 | draw_note(u8 note, u8 clr) { | ||
776 | size_t octave = note / 12; | ||
777 | size_t value = note % 12; | ||
778 | |||
779 | size_t x0 = 0; | ||
780 | size_t y0 = 0; | ||
781 | size_t x1 = 0; | ||
782 | size_t y1 = 0; | ||
783 | switch (value) { | ||
784 | // White notes. | ||
785 | case 0:{ | ||
786 | x0 = PIANO_START_X + 2 + octave * 28; | ||
787 | x1 = x0 + 1; | ||
788 | y0 = PIANO_START_Y + 2; | ||
789 | y1 = PIANO_START_Y - 2 + PIANO_H; | ||
790 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
791 | x0 = PIANO_START_X + 2 + octave * 28 + 2; | ||
792 | x1 = x0; | ||
793 | y0 = y0 + 9; | ||
794 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
795 | } break; | ||
796 | case 2:{ | ||
797 | x0 = PIANO_START_X + 2 + octave * 28 + 5; | ||
798 | x1 = x0; | ||
799 | y0 = PIANO_START_Y + 2; | ||
800 | y1 = PIANO_START_Y - 2 + 12; | ||
801 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
802 | x0 = PIANO_START_X + 2 + octave * 28 + 4; | ||
803 | x1 = x0 + 2; | ||
804 | y0 = PIANO_START_Y - 2 + 13; | ||
805 | y1 = y0 + 7; | ||
806 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
807 | } break; | ||
808 | case 4:{ | ||
809 | x0 = PIANO_START_X + 2 + octave * 28 + 9; | ||
810 | x1 = x0 + 1; | ||
811 | y0 = PIANO_START_Y + 2; | ||
812 | y1 = PIANO_START_Y - 2 + 12; | ||
813 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
814 | x0 = PIANO_START_X + 2 + octave * 28 + 8; | ||
815 | x1 = x0 + 2; | ||
816 | y0 = PIANO_START_Y - 2 + 13; | ||
817 | y1 = y0 + 7; | ||
818 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
819 | } break; | ||
820 | case 5:{ | ||
821 | x0 = PIANO_START_X + 2 + octave * 28 + 12; | ||
822 | x1 = x0 + 1; | ||
823 | y0 = PIANO_START_Y + 2; | ||
824 | y1 = PIANO_START_Y - 2 + PIANO_H; | ||
825 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
826 | x0 = PIANO_START_X + 2 + octave * 28 + 14; | ||
827 | x1 = x0; | ||
828 | y0 = y0 + 9; | ||
829 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
830 | } break; | ||
831 | case 7:{ | ||
832 | x0 = PIANO_START_X + 2 + octave * 28 + 17; | ||
833 | x1 = x0; | ||
834 | y0 = PIANO_START_Y + 2; | ||
835 | y1 = PIANO_START_Y - 2 + 12; | ||
836 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
837 | x0 = PIANO_START_X + 2 + octave * 28 + 16; | ||
838 | x1 = x0 + 2; | ||
839 | y0 = PIANO_START_Y - 2 + 13; | ||
840 | y1 = y0 + 7; | ||
841 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
842 | } break; | ||
843 | case 9:{ | ||
844 | x0 = PIANO_START_X + 2 + octave * 28 + 21; | ||
845 | x1 = x0; | ||
846 | y0 = PIANO_START_Y + 2; | ||
847 | y1 = PIANO_START_Y - 2 + 12; | ||
848 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
849 | x0 = PIANO_START_X + 2 + octave * 28 + 20; | ||
850 | x1 = x0 + 2; | ||
851 | y0 = PIANO_START_Y - 2 + 13; | ||
852 | y1 = y0 + 7; | ||
853 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
854 | } break; | ||
855 | case 11: { | ||
856 | x0 = PIANO_START_X + 2 + octave * 28 + 25; | ||
857 | x1 = x0 + 1; | ||
858 | y0 = PIANO_START_Y + 2; | ||
859 | y1 = PIANO_START_Y - 2 + 12; | ||
860 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
861 | x0 = PIANO_START_X + 2 + octave * 28 + 24; | ||
862 | x1 = x0 + 2; | ||
863 | y0 = PIANO_START_Y - 2 + 13; | ||
864 | y1 = y0 + 7; | ||
865 | draw_filled_rect(x0, y0, x1, y1, clr); | ||
866 | } break; | ||
867 | default: { | ||
868 | if (clr == COL_FG) { | ||
869 | clr = COL_BG; | ||
870 | } | ||
871 | y0 = PIANO_START_Y + 2; | ||
872 | y1 = PIANO_START_Y - 2 + 11; | ||
873 | switch (value) { | ||
874 | case 1: { | ||
875 | x0 = PIANO_START_X + 2 + octave * 28 + 3; | ||
876 | } break; | ||
877 | case 3: { | ||
878 | x0 = PIANO_START_X + 2 + octave * 28 + 7; | ||
879 | } break; | ||
880 | case 6: { | ||
881 | x0 = PIANO_START_X + 2 + octave * 28 + 15; | ||
882 | } break; | ||
883 | case 8: { | ||
884 | x0 = PIANO_START_X + 2 + octave * 28 + 19; | ||
885 | } break; | ||
886 | case 10: { | ||
887 | x0 = PIANO_START_X + 2 + octave * 28 + 23; | ||
888 | } break; | ||
889 | } | ||
890 | x1 = x0; | ||
891 | draw_line(x0, y0, x1, y1, clr); | ||
892 | } break; | ||
893 | } | ||
894 | } | ||
895 | |||
896 | void | ||
897 | draw_piano(void) { | ||
898 | size_t x0 = PIANO_START_X; | ||
899 | size_t x1 = PIANO_START_X + PIANO_W; | ||
900 | size_t y0 = PIANO_START_Y; | ||
901 | size_t y1 = PIANO_START_Y + PIANO_H; | ||
902 | draw_rect(x0, y0, x1, y1, COL_FG); | ||
903 | for (size_t i = 0; i < 12 * 6; i++) { | ||
904 | draw_note(i, COL_FG); | ||
905 | } | ||
906 | } | ||
907 | |||
908 | void | ||
909 | draw_params_cursor_wave(size_t i, u8 clr) { | ||
910 | u8 x_positions[] = { | ||
911 | // 32 half bytes (Wave A). | ||
912 | 0, 4, 8, 12, 16, 20, 24, 28, | ||
913 | 34, 38, 42, 46, 50, 54, 58, 62, | ||
914 | 0, 4, 8, 12, 16, 20, 24, 28, | ||
915 | 34, 38, 42, 46, 50, 54, 58, 62, | ||
916 | // 32 half bytes (Wave B). | ||
917 | 70, 74, 78, 82, 86, 90, 94, 98, | ||
918 | 104, 108, 112, 116, 120, 124, 128, 132, | ||
919 | 70, 74, 78, 82, 86, 90, 94, 98, | ||
920 | 104, 108, 112, 116, 120, 124, 128, 132, | ||
921 | // Default wave A. | ||
922 | 1, 18, 35, 52, | ||
923 | // Default wave B. | ||
924 | 71, 88, 105, 122, | ||
925 | // Mode selection. | ||
926 | 141, | ||
927 | // Volume selection. | ||
928 | 141, | ||
929 | }; | ||
930 | u8 y_positions[] = { | ||
931 | // 32 half bytes (Wave A) | ||
932 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
933 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
934 | 8, 8, 8, 8, 8, 8, 8, 8, | ||
935 | 8, 8, 8, 8, 8, 8, 8, 8, | ||
936 | // 32 half bytes (Wave B) | ||
937 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
938 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
939 | 8, 8, 8, 8, 8, 8, 8, 8, | ||
940 | 8, 8, 8, 8, 8, 8, 8, 8, | ||
941 | // Default wave A. | ||
942 | 20, 20, 20, 20, | ||
943 | // Default wave B. | ||
944 | 20, 20, 20, 20, | ||
945 | // Mode selection. | ||
946 | 20, | ||
947 | // Volume selection. | ||
948 | 0, | ||
949 | }; | ||
950 | size_t cursor_length = 0; | ||
951 | if (i < 64) { | ||
952 | cursor_length = 4; | ||
953 | } else if (i < 72) { | ||
954 | cursor_length = 13; | ||
955 | } else { | ||
956 | cursor_length = 30; | ||
957 | } | ||
958 | size_t x = PARAMS_START_X + x_positions[i] - 1; | ||
959 | size_t y = PARAMS_START_Y + PARAMS_H - 23 + y_positions[i]; | ||
960 | draw_line(x, y, x + cursor_length, y, clr); | ||
961 | } | ||
962 | |||
963 | void | ||
964 | draw_params_cursor_noise(size_t i, u8 clr) { | ||
965 | u8 x_positions[] = { | ||
966 | 0, // Bit mode. | ||
967 | 31, // Env. Vol. | ||
968 | 59, // Env. Direction. | ||
969 | 87, // Env. Time. | ||
970 | }; | ||
971 | u8 y_positions[] = { | ||
972 | 20, // Bit mode. | ||
973 | 20, // Env. Vol. | ||
974 | 20, // Env. Direction. | ||
975 | 20, // Env. Time. | ||
976 | }; | ||
977 | size_t cursor_length = 24; | ||
978 | size_t x = PARAMS_START_X + x_positions[i] + 30; | ||
979 | size_t y = PARAMS_START_Y + PARAMS_H - 23 + y_positions[i]; | ||
980 | draw_line(x, y, x + cursor_length, y, clr); | ||
981 | } | ||
982 | |||
983 | void | ||
984 | draw_params_cursor_square(size_t i, u8 clr, bool sweep) { | ||
985 | size_t x_offset = sweep ? 0 : 30; | ||
986 | u8 x_positions[] = { | ||
987 | 0, // Duty. | ||
988 | 31, // Env. Vol. | ||
989 | 59, // Env. Direction. | ||
990 | 87, // Env. Time. | ||
991 | 118, // Sweep Number. | ||
992 | 146, // Sweep Time. | ||
993 | 132, // Sweep Direction. | ||
994 | }; | ||
995 | u8 y_positions[] = { | ||
996 | 20, // Duty. | ||
997 | 20, // Env. Vol. | ||
998 | 20, // Env. Direction. | ||
999 | 20, // Env. Time. | ||
1000 | 20, // Sweep Number. | ||
1001 | 20, // Sweep Time. | ||
1002 | 0, // Sweep Direction. | ||
1003 | }; | ||
1004 | size_t cursor_length = 24; | ||
1005 | size_t x = PARAMS_START_X + x_positions[i] + x_offset; | ||
1006 | size_t y = PARAMS_START_Y + PARAMS_H - 23 + y_positions[i]; | ||
1007 | draw_line(x, y, x + cursor_length, y, clr); | ||
1008 | } | ||
1009 | |||
1010 | void | ||
1011 | draw_params_cursor(size_t i, u8 clr) { | ||
1012 | switch (channel_selection_loc) { | ||
1013 | case 0: { | ||
1014 | draw_params_cursor_square(i, clr, true); | ||
1015 | } break; | ||
1016 | case 1: { | ||
1017 | draw_params_cursor_square(i, clr, false); | ||
1018 | } break; | ||
1019 | case 2: { | ||
1020 | draw_params_cursor_wave(i, clr); | ||
1021 | } break; | ||
1022 | case 3: { | ||
1023 | draw_params_cursor_noise(i, clr); | ||
1024 | } break; | ||
1025 | } | ||
1026 | } | ||
1027 | |||
1028 | IWRAM_CODE | ||
1029 | void | ||
1030 | draw_wave_pattern(u8 *pattern, int x, int y, u8 clr) { | ||
1031 | for (size_t i = 0; i < 16; ++i) { | ||
1032 | u8 byte = pattern[i]; | ||
1033 | u8 first = (byte >> 4) & 0xF; | ||
1034 | u8 second = byte & 0xF; | ||
1035 | u8 a = x + i * 4; | ||
1036 | u8 b = y + 16; | ||
1037 | draw_pixel(a, b - first, clr); | ||
1038 | draw_pixel(a + 1, b - first, clr); | ||
1039 | draw_pixel(a + 2, b - second, clr); | ||
1040 | draw_pixel(a + 3, b - second, clr); | ||
1041 | } | ||
1042 | } | ||
1043 | |||
1044 | IWRAM_CODE | ||
1045 | void | ||
1046 | clear_parameters(void) { | ||
1047 | size_t x0 = PARAMS_START_X; | ||
1048 | size_t y0 = PARAMS_START_Y; | ||
1049 | size_t x1 = PARAMS_START_X + PARAMS_W; | ||
1050 | size_t y1 = PARAMS_START_Y + PARAMS_H - 1; | ||
1051 | draw_filled_rect(x0, y0, x1, y1, COL_BG); | ||
1052 | } | ||
1053 | |||
1054 | IWRAM_CODE | ||
1055 | void | ||
1056 | draw_parameters_wave(void) { | ||
1057 | // Draw current wave data. | ||
1058 | Pattern *pat = &patterns[pattern_selection_loc]; | ||
1059 | { | ||
1060 | u8 *wave_a = pat->ch3.params[trig_selection_loc].wave_a; | ||
1061 | u8 *wave_b = pat->ch3.params[trig_selection_loc].wave_b; | ||
1062 | |||
1063 | size_t x = PARAMS_START_X; | ||
1064 | size_t y = PARAMS_START_Y + 13; | ||
1065 | |||
1066 | // Wave Patterns. | ||
1067 | draw_wave_pattern(wave_a, x, y, COL_WAVE_A); | ||
1068 | draw_wave_pattern(wave_b, x + 70, y, COL_WAVE_B); | ||
1069 | |||
1070 | // Wave text. | ||
1071 | x -= 2; | ||
1072 | // txt_drawf_small("%02x%02x%02x%02x", x, y + 20, 4, COL_FG, | ||
1073 | // wave_a[0], wave_a[1], wave_a[2], wave_a[3]); | ||
1074 | // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 20, 4, COL_FG, | ||
1075 | // wave_a[4], wave_a[5], wave_a[6], wave_a[7]); | ||
1076 | // txt_drawf_small("%02x%02x%02x%02x", x, y + 28, 4, COL_FG, | ||
1077 | // wave_a[8], wave_a[9], wave_a[10], wave_a[11]); | ||
1078 | // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 28, 4, COL_FG, | ||
1079 | // wave_a[12], wave_a[13], wave_a[14], wave_a[15]); | ||
1080 | |||
1081 | x += 70; | ||
1082 | // txt_drawf_small("%02x%02x%02x%02x", x, y + 20, 4, COL_FG, | ||
1083 | // wave_b[0], wave_b[1], wave_b[2], wave_b[3]); | ||
1084 | // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 20, 4, COL_FG, | ||
1085 | // wave_b[4], wave_b[5], wave_b[6], wave_b[7]); | ||
1086 | // txt_drawf_small("%02x%02x%02x%02x", x, y + 28, 4, COL_FG, | ||
1087 | // wave_b[8], wave_b[9], wave_b[10], wave_b[11]); | ||
1088 | // txt_drawf_small("%02x%02x%02x%02x", x + 34, y + 28, 4, COL_FG, | ||
1089 | // wave_b[12], wave_b[13], wave_b[14], wave_b[15]); | ||
1090 | } | ||
1091 | |||
1092 | // Draw default wave buttons. | ||
1093 | { | ||
1094 | Tile *wave_tiles = ASSETS_DEFAULT_WAVES; | ||
1095 | size_t x = PARAMS_START_X; | ||
1096 | size_t y = PARAMS_START_Y + PARAMS_H - 12; | ||
1097 | for (size_t i = 0, k = 0; i < 4 * 2; i += 2, k++) { | ||
1098 | // draw_tile(x + 17 * k, y, wave_tiles + i, COL_FG, true); | ||
1099 | // draw_tile(x + 17 * k + 8, y, wave_tiles + i + 1, COL_FG, true); | ||
1100 | } | ||
1101 | for (size_t i = 0, k = 0; i < 4 * 2; i += 2, k++) { | ||
1102 | // draw_tile(x + 17 * k + 70, y, wave_tiles + i, COL_FG, true); | ||
1103 | // draw_tile(x + 17 * k + 8 + 70, y, wave_tiles + i + 1, COL_FG, true); | ||
1104 | } | ||
1105 | } | ||
1106 | |||
1107 | // Mode selection. | ||
1108 | { | ||
1109 | size_t x = PARAMS_START_X + 140; | ||
1110 | size_t y = PARAMS_START_Y + PARAMS_H - 22; | ||
1111 | draw_line(x, y + 4, x + 5, y + 4, COL_FG); | ||
1112 | draw_line(x + 25, y + 4, x + 30, y + 4, COL_FG); | ||
1113 | draw_line(x, y + 5, x, y + 16, COL_FG); | ||
1114 | draw_line(x + 30, y + 5, x + 30, y + 17, COL_FG); | ||
1115 | draw_line(x, y + 17, x + 30, y + 17, COL_FG); | ||
1116 | // txt_drawf_small("mode", x + 6, y, 4, COL_FG); | ||
1117 | |||
1118 | switch (pat->ch3.params[trig_selection_loc].wave_mode) { | ||
1119 | case 0: { | ||
1120 | txt_drawf("A", x + 12, y + 7, 6, COL_FG); | ||
1121 | } break; | ||
1122 | case 1: { | ||
1123 | txt_drawf("B", x + 12, y + 7, 6, COL_FG); | ||
1124 | } break; | ||
1125 | case 2: { | ||
1126 | txt_drawf("A+B", x + 6, y + 7, 6, COL_FG); | ||
1127 | } break; | ||
1128 | } | ||
1129 | } | ||
1130 | |||
1131 | // Wave volume. | ||
1132 | { | ||
1133 | size_t x = PARAMS_START_X + 140; | ||
1134 | size_t y = PARAMS_START_Y + PARAMS_H - 45; | ||
1135 | draw_line(x, y + 7, x + 7, y + 7, COL_FG); | ||
1136 | draw_line(x + 23, y + 7, x + 30, y + 7, COL_FG); | ||
1137 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1138 | draw_line(x + 30, y + 8, x + 30, y + 19, COL_FG); | ||
1139 | draw_line(x, y + 20, x + 30, y + 20, COL_FG); | ||
1140 | // txt_drawf_small("vol", x + 8, y + 3, 4, COL_FG); | ||
1141 | |||
1142 | switch (pat->ch3.params[trig_selection_loc].wave_volume) { | ||
1143 | case 0: { | ||
1144 | txt_drawf("0", x + 12, y + 10, 6, COL_FG); | ||
1145 | } break; | ||
1146 | case 1: { | ||
1147 | txt_drawf("25", x + 9, y + 10, 6, COL_FG); | ||
1148 | } break; | ||
1149 | case 2: { | ||
1150 | txt_drawf("50", x + 9, y + 10, 6, COL_FG); | ||
1151 | } break; | ||
1152 | case 3: { | ||
1153 | txt_drawf("75", x + 9, y + 10, 6, COL_FG); | ||
1154 | } break; | ||
1155 | case 4: { | ||
1156 | txt_drawf("100", x + 6, y + 10, 6, COL_FG); | ||
1157 | } break; | ||
1158 | } | ||
1159 | } | ||
1160 | } | ||
1161 | |||
1162 | void | ||
1163 | draw_parameters_square(ChannelSquareParams *params, bool sweep) { | ||
1164 | size_t x_offset = sweep ? 0 : 30; | ||
1165 | |||
1166 | // Duty cycle. | ||
1167 | { | ||
1168 | // Shape drawing. | ||
1169 | { | ||
1170 | size_t x = PARAMS_START_X + x_offset; | ||
1171 | size_t y = PARAMS_START_Y + PARAMS_H - 44; | ||
1172 | |||
1173 | size_t x0 = x + 2; | ||
1174 | size_t x1 = x0; | ||
1175 | size_t x2 = x0; | ||
1176 | size_t x3 = x0; | ||
1177 | size_t x4 = x0; | ||
1178 | size_t x5 = x0; | ||
1179 | size_t y0 = y + 14; | ||
1180 | size_t y1 = y + 2; | ||
1181 | |||
1182 | switch (params->duty_cycle) { | ||
1183 | case 0: { | ||
1184 | x1 += 4; | ||
1185 | x2 += 6; | ||
1186 | x3 += 13; | ||
1187 | x4 += 15; | ||
1188 | x5 += 20; | ||
1189 | } break; | ||
1190 | case 1: { | ||
1191 | x1 += 4; | ||
1192 | x2 += 7; | ||
1193 | x3 += 13; | ||
1194 | x4 += 16; | ||
1195 | x5 += 20; | ||
1196 | } break; | ||
1197 | case 2: { | ||
1198 | x1 += 3; | ||
1199 | x2 += 8; | ||
1200 | x3 += 12; | ||
1201 | x4 += 17; | ||
1202 | x5 += 20; | ||
1203 | } break; | ||
1204 | case 3: { | ||
1205 | x1 += 2; | ||
1206 | x2 += 9; | ||
1207 | x3 += 11; | ||
1208 | x4 += 18; | ||
1209 | x5 += 20; | ||
1210 | } break; | ||
1211 | } | ||
1212 | draw_line(x0, y0, x1, y0, COL_RED); | ||
1213 | draw_line(x1, y1, x1, y0, COL_RED); | ||
1214 | draw_line(x1, y1, x2, y1, COL_RED); | ||
1215 | draw_line(x2, y1, x2, y0, COL_RED); | ||
1216 | draw_line(x2, y0, x3, y0, COL_RED); | ||
1217 | draw_line(x3, y1, x3, y0, COL_RED); | ||
1218 | draw_line(x3, y1, x4, y1, COL_RED); | ||
1219 | draw_line(x4, y1, x4, y0, COL_RED); | ||
1220 | draw_line(x4, y0, x5, y0, COL_RED); | ||
1221 | |||
1222 | // Bounding box. | ||
1223 | draw_rect(x, y - 3, x + 24, y + 18, COL_RED); | ||
1224 | } | ||
1225 | |||
1226 | // Param box. | ||
1227 | { | ||
1228 | size_t x = PARAMS_START_X + x_offset; | ||
1229 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1230 | draw_line(x, y + 7, x + 2, y + 7, COL_FG); | ||
1231 | draw_line(x + 22, y + 7, x + 24, y + 7, COL_FG); | ||
1232 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1233 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1234 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1235 | // txt_drawf_small("duty", x + 3, y + 3, 4, COL_FG); | ||
1236 | |||
1237 | switch (params->duty_cycle) { | ||
1238 | case 0: { | ||
1239 | txt_drawf("12", x + 6, y + 10, 6, COL_FG); | ||
1240 | } break; | ||
1241 | case 1: { | ||
1242 | txt_drawf("25", x + 6, y + 10, 6, COL_FG); | ||
1243 | } break; | ||
1244 | case 2: { | ||
1245 | txt_drawf("50", x + 6, y + 10, 6, COL_FG); | ||
1246 | } break; | ||
1247 | case 3: { | ||
1248 | txt_drawf("75", x + 6, y + 10, 6, COL_FG); | ||
1249 | } break; | ||
1250 | } | ||
1251 | } | ||
1252 | } | ||
1253 | |||
1254 | // Envelope. | ||
1255 | { | ||
1256 | // Env. drawing. | ||
1257 | { | ||
1258 | // Bounding box. | ||
1259 | { | ||
1260 | size_t x0 = PARAMS_START_X + 31 + x_offset; | ||
1261 | size_t y0 = PARAMS_START_Y + PARAMS_H - 47; | ||
1262 | size_t x1 = x0 + 79; | ||
1263 | size_t y1 = y0 + 21; | ||
1264 | draw_rect(x0, y0, x1, y1, COL_CYAN); | ||
1265 | } | ||
1266 | |||
1267 | size_t x = PARAMS_START_X + 42 + x_offset; | ||
1268 | size_t y = PARAMS_START_Y + PARAMS_H - 44; | ||
1269 | size_t x0 = x; | ||
1270 | size_t y0 = y + 15 - params->env_volume; | ||
1271 | size_t x1 = x + 8 * params->env_time; | ||
1272 | size_t y1 = params->env_direction == 0 ? y + 15 : y; | ||
1273 | size_t x2 = x + 8 * 7 + 1; | ||
1274 | size_t y2 = y1; | ||
1275 | |||
1276 | // Env. | ||
1277 | if (params->env_time == 0) { | ||
1278 | draw_line(x1, y0, x2, y0, COL_CYAN); | ||
1279 | } else { | ||
1280 | draw_line(x0, y0, x1, y1, COL_CYAN); | ||
1281 | draw_line(x1, y1, x2, y2, COL_CYAN); | ||
1282 | } | ||
1283 | } | ||
1284 | |||
1285 | // Env. volume. | ||
1286 | { | ||
1287 | size_t x = PARAMS_START_X + 31 + x_offset; | ||
1288 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1289 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
1290 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
1291 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1292 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1293 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1294 | // txt_drawf_small("vol", x + 5, y + 3, 4, COL_FG); | ||
1295 | |||
1296 | switch (params->env_volume) { | ||
1297 | case 0: { | ||
1298 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
1299 | } break; | ||
1300 | case 1: { | ||
1301 | txt_drawf("6", x + 9, y + 10, 6, COL_FG); | ||
1302 | } break; | ||
1303 | case 2: { | ||
1304 | txt_drawf("13", x + 6, y + 10, 6, COL_FG); | ||
1305 | } break; | ||
1306 | case 3: { | ||
1307 | txt_drawf("20", x + 6, y + 10, 6, COL_FG); | ||
1308 | } break; | ||
1309 | case 4: { | ||
1310 | txt_drawf("26", x + 6, y + 10, 6, COL_FG); | ||
1311 | } break; | ||
1312 | case 5: { | ||
1313 | txt_drawf("33", x + 6, y + 10, 6, COL_FG); | ||
1314 | } break; | ||
1315 | case 6: { | ||
1316 | txt_drawf("40", x + 6, y + 10, 6, COL_FG); | ||
1317 | } break; | ||
1318 | case 7: { | ||
1319 | txt_drawf("46", x + 6, y + 10, 6, COL_FG); | ||
1320 | } break; | ||
1321 | case 8: { | ||
1322 | txt_drawf("53", x + 6, y + 10, 6, COL_FG); | ||
1323 | } break; | ||
1324 | case 9: { | ||
1325 | txt_drawf("60", x + 6, y + 10, 6, COL_FG); | ||
1326 | } break; | ||
1327 | case 10: { | ||
1328 | txt_drawf("66", x + 6, y + 10, 6, COL_FG); | ||
1329 | } break; | ||
1330 | case 11: { | ||
1331 | txt_drawf("73", x + 6, y + 10, 6, COL_FG); | ||
1332 | } break; | ||
1333 | case 12: { | ||
1334 | txt_drawf("80", x + 6, y + 10, 6, COL_FG); | ||
1335 | } break; | ||
1336 | case 13: { | ||
1337 | txt_drawf("86", x + 6, y + 10, 6, COL_FG); | ||
1338 | } break; | ||
1339 | case 14: { | ||
1340 | txt_drawf("93", x + 6, y + 10, 6, COL_FG); | ||
1341 | } break; | ||
1342 | case 15: { | ||
1343 | txt_drawf("100", x + 3, y + 10, 6, COL_FG); | ||
1344 | } break; | ||
1345 | } | ||
1346 | } | ||
1347 | |||
1348 | // Env. direction | ||
1349 | { | ||
1350 | size_t x = PARAMS_START_X + 59 + x_offset; | ||
1351 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1352 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
1353 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
1354 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1355 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1356 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1357 | // txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG); | ||
1358 | |||
1359 | char arr_up[2] = { 0x19, 0 }; | ||
1360 | char arr_down[2] = { 0x18, 0 }; | ||
1361 | switch (params->env_direction) { | ||
1362 | case 0: { | ||
1363 | txt_drawf(arr_up, x + 9, y + 11, 6, COL_FG); | ||
1364 | } break; | ||
1365 | case 1: { | ||
1366 | txt_drawf(arr_down, x + 9, y + 11, 6, COL_FG); | ||
1367 | } break; | ||
1368 | } | ||
1369 | } | ||
1370 | |||
1371 | // Env. time. | ||
1372 | { | ||
1373 | size_t x = PARAMS_START_X + 87 + x_offset; | ||
1374 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1375 | draw_line(x, y + 7, x + 2, y + 7, COL_FG); | ||
1376 | draw_line(x + 22, y + 7, x + 24, y + 7, COL_FG); | ||
1377 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1378 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1379 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1380 | // txt_drawf_small("time", x + 3, y + 3, 4, COL_FG); | ||
1381 | |||
1382 | switch (params->env_time) { | ||
1383 | case 0: { | ||
1384 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
1385 | } break; | ||
1386 | case 1: { | ||
1387 | txt_drawf("14", x + 6, y + 10, 6, COL_FG); | ||
1388 | } break; | ||
1389 | case 2: { | ||
1390 | txt_drawf("28", x + 6, y + 10, 6, COL_FG); | ||
1391 | } break; | ||
1392 | case 3: { | ||
1393 | txt_drawf("42", x + 6, y + 10, 6, COL_FG); | ||
1394 | } break; | ||
1395 | case 4: { | ||
1396 | txt_drawf("57", x + 6, y + 10, 6, COL_FG); | ||
1397 | } break; | ||
1398 | case 5: { | ||
1399 | txt_drawf("71", x + 6, y + 10, 6, COL_FG); | ||
1400 | } break; | ||
1401 | case 6: { | ||
1402 | txt_drawf("85", x + 6, y + 10, 6, COL_FG); | ||
1403 | } break; | ||
1404 | case 7: { | ||
1405 | txt_drawf("100", x + 3, y + 10, 6, COL_FG); | ||
1406 | } break; | ||
1407 | } | ||
1408 | } | ||
1409 | } | ||
1410 | |||
1411 | // Sweep number. | ||
1412 | if (sweep) { | ||
1413 | size_t x = PARAMS_START_X + 118; | ||
1414 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1415 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
1416 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
1417 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1418 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1419 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1420 | // txt_drawf_small("num", x + 5, y + 3, 4, COL_FG); | ||
1421 | |||
1422 | switch (params->sweep_number) { | ||
1423 | case 0: { | ||
1424 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
1425 | } break; | ||
1426 | case 1: { | ||
1427 | txt_drawf("1", x + 9, y + 10, 6, COL_FG); | ||
1428 | } break; | ||
1429 | case 2: { | ||
1430 | txt_drawf("2", x + 9, y + 10, 6, COL_FG); | ||
1431 | } break; | ||
1432 | case 3: { | ||
1433 | txt_drawf("3", x + 9, y + 10, 6, COL_FG); | ||
1434 | } break; | ||
1435 | case 4: { | ||
1436 | txt_drawf("4", x + 9, y + 10, 6, COL_FG); | ||
1437 | } break; | ||
1438 | case 5: { | ||
1439 | txt_drawf("5", x + 9, y + 10, 6, COL_FG); | ||
1440 | } break; | ||
1441 | case 6: { | ||
1442 | txt_drawf("6", x + 9, y + 10, 6, COL_FG); | ||
1443 | } break; | ||
1444 | case 7: { | ||
1445 | txt_drawf("7", x + 9, y + 10, 6, COL_FG); | ||
1446 | } break; | ||
1447 | } | ||
1448 | } | ||
1449 | |||
1450 | // Sweep time. | ||
1451 | if (sweep) { | ||
1452 | size_t x = PARAMS_START_X + 146; | ||
1453 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1454 | draw_line(x, y + 7, x + 2, y + 7, COL_FG); | ||
1455 | draw_line(x + 22, y + 7, x + 24, y + 7, COL_FG); | ||
1456 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1457 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1458 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1459 | // txt_drawf_small("time", x + 3, y + 3, 4, COL_FG); | ||
1460 | |||
1461 | switch (params->sweep_time) { | ||
1462 | case 0: { | ||
1463 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
1464 | } break; | ||
1465 | case 1: { | ||
1466 | txt_drawf("1", x + 9, y + 10, 6, COL_FG); | ||
1467 | } break; | ||
1468 | case 2: { | ||
1469 | txt_drawf("2", x + 9, y + 10, 6, COL_FG); | ||
1470 | } break; | ||
1471 | case 3: { | ||
1472 | txt_drawf("3", x + 9, y + 10, 6, COL_FG); | ||
1473 | } break; | ||
1474 | case 4: { | ||
1475 | txt_drawf("4", x + 9, y + 10, 6, COL_FG); | ||
1476 | } break; | ||
1477 | case 5: { | ||
1478 | txt_drawf("5", x + 9, y + 10, 6, COL_FG); | ||
1479 | } break; | ||
1480 | case 6: { | ||
1481 | txt_drawf("6", x + 9, y + 10, 6, COL_FG); | ||
1482 | } break; | ||
1483 | case 7: { | ||
1484 | txt_drawf("7", x + 9, y + 10, 6, COL_FG); | ||
1485 | } break; | ||
1486 | } | ||
1487 | } | ||
1488 | |||
1489 | // Sweep direction. | ||
1490 | if (sweep) { | ||
1491 | size_t x = PARAMS_START_X + 132; | ||
1492 | size_t y = PARAMS_START_Y + PARAMS_H - 45; | ||
1493 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
1494 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
1495 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1496 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1497 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1498 | // txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG); | ||
1499 | |||
1500 | char arr_up[2] = { 0x19, 0 }; | ||
1501 | char arr_down[2] = { 0x18, 0 }; | ||
1502 | switch (params->sweep_direction) { | ||
1503 | case 0: { | ||
1504 | txt_drawf(arr_up, x + 9, y + 11, 6, COL_FG); | ||
1505 | } break; | ||
1506 | case 1: { | ||
1507 | txt_drawf(arr_down, x + 9, y + 11, 6, COL_FG); | ||
1508 | } break; | ||
1509 | } | ||
1510 | } | ||
1511 | |||
1512 | // Labels. | ||
1513 | { | ||
1514 | size_t x = PARAMS_START_X + x_offset; | ||
1515 | size_t y = PARAMS_START_Y + PARAMS_H - 45; | ||
1516 | // txt_drawf_small("shape", x + 1, y - 12, 4, COL_FG); | ||
1517 | // txt_drawf_small("envelope", x + 54, y - 12, 4, COL_FG); | ||
1518 | if (sweep) { | ||
1519 | // txt_drawf_small("sweep", x + 133, y - 12, 4, COL_FG); | ||
1520 | } | ||
1521 | } | ||
1522 | } | ||
1523 | |||
1524 | void | ||
1525 | draw_parameters_noise(void) { | ||
1526 | size_t x_offset = 30; | ||
1527 | Pattern *pat = &patterns[pattern_selection_loc]; | ||
1528 | ChannelNoiseParams *params = &pat->ch4.params[trig_selection_loc]; | ||
1529 | |||
1530 | // Bit mode. | ||
1531 | { | ||
1532 | // Param box. | ||
1533 | { | ||
1534 | size_t x = PARAMS_START_X + x_offset; | ||
1535 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1536 | draw_line(x, y + 7, x + 2, y + 7, COL_FG); | ||
1537 | draw_line(x + 22, y + 7, x + 24, y + 7, COL_FG); | ||
1538 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1539 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1540 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1541 | // txt_drawf_small("mode", x + 3, y + 3, 4, COL_FG); | ||
1542 | |||
1543 | switch (params->bit_mode) { | ||
1544 | case 0: { | ||
1545 | txt_drawf("A", x + 9, y + 10, 6, COL_FG); | ||
1546 | } break; | ||
1547 | case 1: { | ||
1548 | txt_drawf("B", x + 9, y + 10, 6, COL_FG); | ||
1549 | } break; | ||
1550 | } | ||
1551 | } | ||
1552 | } | ||
1553 | |||
1554 | // Envelope. | ||
1555 | { | ||
1556 | // Env. drawing. | ||
1557 | { | ||
1558 | // Bounding box. | ||
1559 | { | ||
1560 | size_t x0 = PARAMS_START_X + 31 + x_offset; | ||
1561 | size_t y0 = PARAMS_START_Y + PARAMS_H - 47; | ||
1562 | size_t x1 = x0 + 79; | ||
1563 | size_t y1 = y0 + 21; | ||
1564 | draw_rect(x0, y0, x1, y1, COL_CYAN); | ||
1565 | } | ||
1566 | |||
1567 | size_t x = PARAMS_START_X + 42 + x_offset; | ||
1568 | size_t y = PARAMS_START_Y + PARAMS_H - 44; | ||
1569 | size_t x0 = x; | ||
1570 | size_t y0 = y + 15 - params->env_volume; | ||
1571 | size_t x1 = x + 8 * params->env_time; | ||
1572 | size_t y1 = params->env_direction == 0 ? y + 15 : y; | ||
1573 | size_t x2 = x + 8 * 7 + 1; | ||
1574 | size_t y2 = y1; | ||
1575 | |||
1576 | // Env. | ||
1577 | if (params->env_time == 0) { | ||
1578 | draw_line(x1, y0, x2, y0, COL_CYAN); | ||
1579 | } else { | ||
1580 | draw_line(x0, y0, x1, y1, COL_CYAN); | ||
1581 | draw_line(x1, y1, x2, y2, COL_CYAN); | ||
1582 | } | ||
1583 | } | ||
1584 | |||
1585 | // Env. volume. | ||
1586 | { | ||
1587 | size_t x = PARAMS_START_X + 31 + x_offset; | ||
1588 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1589 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
1590 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
1591 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1592 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1593 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1594 | // txt_drawf_small("vol", x + 5, y + 3, 4, COL_FG); | ||
1595 | |||
1596 | switch (params->env_volume) { | ||
1597 | case 0: { | ||
1598 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
1599 | } break; | ||
1600 | case 1: { | ||
1601 | txt_drawf("6", x + 9, y + 10, 6, COL_FG); | ||
1602 | } break; | ||
1603 | case 2: { | ||
1604 | txt_drawf("13", x + 6, y + 10, 6, COL_FG); | ||
1605 | } break; | ||
1606 | case 3: { | ||
1607 | txt_drawf("20", x + 6, y + 10, 6, COL_FG); | ||
1608 | } break; | ||
1609 | case 4: { | ||
1610 | txt_drawf("26", x + 6, y + 10, 6, COL_FG); | ||
1611 | } break; | ||
1612 | case 5: { | ||
1613 | txt_drawf("33", x + 6, y + 10, 6, COL_FG); | ||
1614 | } break; | ||
1615 | case 6: { | ||
1616 | txt_drawf("40", x + 6, y + 10, 6, COL_FG); | ||
1617 | } break; | ||
1618 | case 7: { | ||
1619 | txt_drawf("46", x + 6, y + 10, 6, COL_FG); | ||
1620 | } break; | ||
1621 | case 8: { | ||
1622 | txt_drawf("53", x + 6, y + 10, 6, COL_FG); | ||
1623 | } break; | ||
1624 | case 9: { | ||
1625 | txt_drawf("60", x + 6, y + 10, 6, COL_FG); | ||
1626 | } break; | ||
1627 | case 10: { | ||
1628 | txt_drawf("66", x + 6, y + 10, 6, COL_FG); | ||
1629 | } break; | ||
1630 | case 11: { | ||
1631 | txt_drawf("73", x + 6, y + 10, 6, COL_FG); | ||
1632 | } break; | ||
1633 | case 12: { | ||
1634 | txt_drawf("80", x + 6, y + 10, 6, COL_FG); | ||
1635 | } break; | ||
1636 | case 13: { | ||
1637 | txt_drawf("86", x + 6, y + 10, 6, COL_FG); | ||
1638 | } break; | ||
1639 | case 14: { | ||
1640 | txt_drawf("93", x + 6, y + 10, 6, COL_FG); | ||
1641 | } break; | ||
1642 | case 15: { | ||
1643 | txt_drawf("100", x + 3, y + 10, 6, COL_FG); | ||
1644 | } break; | ||
1645 | } | ||
1646 | } | ||
1647 | |||
1648 | // Env. direction | ||
1649 | { | ||
1650 | size_t x = PARAMS_START_X + 59 + x_offset; | ||
1651 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1652 | draw_line(x, y + 7, x + 4, y + 7, COL_FG); | ||
1653 | draw_line(x + 20, y + 7, x + 24, y + 7, COL_FG); | ||
1654 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1655 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1656 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1657 | // txt_drawf_small("dir", x + 5, y + 3, 4, COL_FG); | ||
1658 | |||
1659 | char arr_up[2] = { 0x19, 0 }; | ||
1660 | char arr_down[2] = { 0x18, 0 }; | ||
1661 | switch (params->env_direction) { | ||
1662 | case 0: { | ||
1663 | txt_drawf(arr_up, x + 9, y + 11, 6, COL_FG); | ||
1664 | } break; | ||
1665 | case 1: { | ||
1666 | txt_drawf(arr_down, x + 9, y + 11, 6, COL_FG); | ||
1667 | } break; | ||
1668 | } | ||
1669 | } | ||
1670 | |||
1671 | // Env. time. | ||
1672 | { | ||
1673 | size_t x = PARAMS_START_X + 87 + x_offset; | ||
1674 | size_t y = PARAMS_START_Y + PARAMS_H - 25; | ||
1675 | draw_line(x, y + 7, x + 2, y + 7, COL_FG); | ||
1676 | draw_line(x + 22, y + 7, x + 24, y + 7, COL_FG); | ||
1677 | draw_line(x, y + 8, x, y + 19, COL_FG); | ||
1678 | draw_line(x + 24, y + 8, x + 24, y + 19, COL_FG); | ||
1679 | draw_line(x, y + 20, x + 24, y + 20, COL_FG); | ||
1680 | // txt_drawf_small("time", x + 3, y + 3, 4, COL_FG); | ||
1681 | |||
1682 | switch (params->env_time) { | ||
1683 | case 0: { | ||
1684 | txt_drawf("0", x + 9, y + 10, 6, COL_FG); | ||
1685 | } break; | ||
1686 | case 1: { | ||
1687 | txt_drawf("14", x + 6, y + 10, 6, COL_FG); | ||
1688 | } break; | ||
1689 | case 2: { | ||
1690 | txt_drawf("28", x + 6, y + 10, 6, COL_FG); | ||
1691 | } break; | ||
1692 | case 3: { | ||
1693 | txt_drawf("42", x + 6, y + 10, 6, COL_FG); | ||
1694 | } break; | ||
1695 | case 4: { | ||
1696 | txt_drawf("57", x + 6, y + 10, 6, COL_FG); | ||
1697 | } break; | ||
1698 | case 5: { | ||
1699 | txt_drawf("71", x + 6, y + 10, 6, COL_FG); | ||
1700 | } break; | ||
1701 | case 6: { | ||
1702 | txt_drawf("85", x + 6, y + 10, 6, COL_FG); | ||
1703 | } break; | ||
1704 | case 7: { | ||
1705 | txt_drawf("100", x + 3, y + 10, 6, COL_FG); | ||
1706 | } break; | ||
1707 | } | ||
1708 | } | ||
1709 | } | ||
1710 | |||
1711 | // Labels. | ||
1712 | { | ||
1713 | size_t x = PARAMS_START_X + x_offset; | ||
1714 | size_t y = PARAMS_START_Y + PARAMS_H - 45; | ||
1715 | // txt_drawf_small("envelope", x + 54, y - 12, 4, COL_FG); | ||
1716 | } | ||
1717 | } | ||
1718 | |||
1719 | void | ||
1720 | draw_parameters(void) { | ||
1721 | clear_parameters(); | ||
1722 | Pattern *pat = &patterns[pattern_selection_loc]; | ||
1723 | switch (channel_selection_loc) { | ||
1724 | case 0: { | ||
1725 | draw_parameters_square(&pat->ch1.params[trig_selection_loc], true); | ||
1726 | } break; | ||
1727 | case 1: { | ||
1728 | draw_parameters_square(&pat->ch2.params[trig_selection_loc], false); | ||
1729 | } break; | ||
1730 | case 2: { | ||
1731 | draw_parameters_wave(); | ||
1732 | } break; | ||
1733 | case 3: { | ||
1734 | draw_parameters_noise(); | ||
1735 | } break; | ||
1736 | } | ||
1737 | } | ||
1738 | 11 | ||
1739 | void | 12 | void |
1740 | irq_timer(void) { | 13 | irq_timer(void) { |
@@ -1901,20 +174,6 @@ get_current_trig(void) { | |||
1901 | return NULL; | 174 | return NULL; |
1902 | } | 175 | } |
1903 | 176 | ||
1904 | // Input handling works using a FSM. The input handler is switched to whichever | ||
1905 | // function controls each section. For example, channel selection or trigger | ||
1906 | // selection. | ||
1907 | void (*input_handler)(void); | ||
1908 | |||
1909 | void handle_trigger_selection(void); | ||
1910 | void handle_channel_selection(void); | ||
1911 | void handle_pattern_selection(void); | ||
1912 | void handle_param_selection_sq1(void); | ||
1913 | void handle_param_selection_sq2(void); | ||
1914 | void handle_param_selection_wave(void); | ||
1915 | void handle_param_selection_noise(void); | ||
1916 | void handle_right_col_selection(void); | ||
1917 | |||
1918 | void | 177 | void |
1919 | handle_channel_selection(void) { | 178 | handle_channel_selection(void) { |
1920 | if (key_tap(KEY_B)) { | 179 | if (key_tap(KEY_B)) { |
@@ -2624,302 +883,6 @@ handle_trigger_selection(void) { | |||
2624 | } | 883 | } |
2625 | 884 | ||
2626 | void | 885 | void |
2627 | clipboard_paste(void) { | ||
2628 | Pattern *pat_dst = &patterns[pattern_selection_loc]; | ||
2629 | Pattern *pat_src = &patterns[clipboard.src_pat]; | ||
2630 | |||
2631 | if (input_handler == handle_trigger_selection) { | ||
2632 | if (clipboard.type == CLIP_TRIG) { | ||
2633 | // Copy notes or parameters when applicable. | ||
2634 | switch (clipboard.src_chan) { | ||
2635 | case 0: { | ||
2636 | switch (channel_selection_loc) { | ||
2637 | case 0: { | ||
2638 | pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; | ||
2639 | pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
2640 | } break; | ||
2641 | case 1: { | ||
2642 | pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; | ||
2643 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
2644 | } break; | ||
2645 | case 2: { | ||
2646 | pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; | ||
2647 | } break; | ||
2648 | case 3: { | ||
2649 | pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch1.notes[clipboard.src_trig]; | ||
2650 | } break; | ||
2651 | } | ||
2652 | } break; | ||
2653 | case 1: { | ||
2654 | switch (channel_selection_loc) { | ||
2655 | case 0: { | ||
2656 | pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; | ||
2657 | pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume; | ||
2658 | pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time; | ||
2659 | pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction; | ||
2660 | pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle; | ||
2661 | } break; | ||
2662 | case 1: { | ||
2663 | pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; | ||
2664 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig]; | ||
2665 | } break; | ||
2666 | case 2: { | ||
2667 | pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; | ||
2668 | } break; | ||
2669 | case 3: { | ||
2670 | pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch2.notes[clipboard.src_trig]; | ||
2671 | } break; | ||
2672 | } | ||
2673 | } break; | ||
2674 | case 2: { | ||
2675 | switch (channel_selection_loc) { | ||
2676 | case 0: { | ||
2677 | pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; | ||
2678 | } break; | ||
2679 | case 1: { | ||
2680 | pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; | ||
2681 | } break; | ||
2682 | case 2: { | ||
2683 | pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; | ||
2684 | pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig]; | ||
2685 | } break; | ||
2686 | case 3: { | ||
2687 | pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch3.notes[clipboard.src_trig]; | ||
2688 | } break; | ||
2689 | } | ||
2690 | } break; | ||
2691 | case 3: { | ||
2692 | switch (channel_selection_loc) { | ||
2693 | case 0: { | ||
2694 | pat_dst->ch1.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; | ||
2695 | } break; | ||
2696 | case 1: { | ||
2697 | pat_dst->ch2.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; | ||
2698 | } break; | ||
2699 | case 2: { | ||
2700 | pat_dst->ch3.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; | ||
2701 | } break; | ||
2702 | case 3: { | ||
2703 | pat_dst->ch4.notes[trig_selection_loc] = pat_src->ch4.notes[clipboard.src_trig]; | ||
2704 | pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig]; | ||
2705 | } break; | ||
2706 | } | ||
2707 | } break; | ||
2708 | } | ||
2709 | } | ||
2710 | // Only paste the params for the respective trigger. | ||
2711 | if (clipboard.type == CLIP_PARAM_CH1) { | ||
2712 | switch (channel_selection_loc) { | ||
2713 | case 0: { | ||
2714 | pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
2715 | } break; | ||
2716 | case 1: { | ||
2717 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
2718 | } break; | ||
2719 | } | ||
2720 | } | ||
2721 | if (clipboard.type == CLIP_PARAM_CH2) { | ||
2722 | switch (channel_selection_loc) { | ||
2723 | case 0: { | ||
2724 | pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume; | ||
2725 | pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time; | ||
2726 | pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction; | ||
2727 | pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle; | ||
2728 | } break; | ||
2729 | case 1: { | ||
2730 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig]; | ||
2731 | } break; | ||
2732 | } | ||
2733 | } | ||
2734 | if (clipboard.type == CLIP_PARAM_CH3 && channel_selection_loc == clipboard.src_chan) { | ||
2735 | pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig]; | ||
2736 | } | ||
2737 | if (clipboard.type == CLIP_PARAM_CH4 && channel_selection_loc == clipboard.src_chan) { | ||
2738 | pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig]; | ||
2739 | } | ||
2740 | draw_triggers(); | ||
2741 | draw_parameters(); | ||
2742 | } else if (input_handler == handle_param_selection_sq1 && clipboard.type == CLIP_PARAM_CH1) { | ||
2743 | pat_dst->ch1.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
2744 | draw_parameters(); | ||
2745 | } else if (input_handler == handle_param_selection_sq1 && clipboard.type == CLIP_PARAM_CH2) { | ||
2746 | pat_dst->ch1.params[trig_selection_loc].env_volume = pat_src->ch2.params[clipboard.src_trig].env_volume; | ||
2747 | pat_dst->ch1.params[trig_selection_loc].env_time = pat_src->ch2.params[clipboard.src_trig].env_time; | ||
2748 | pat_dst->ch1.params[trig_selection_loc].env_direction = pat_src->ch2.params[clipboard.src_trig].env_direction; | ||
2749 | pat_dst->ch1.params[trig_selection_loc].duty_cycle = pat_src->ch2.params[clipboard.src_trig].duty_cycle; | ||
2750 | draw_parameters(); | ||
2751 | } else if (input_handler == handle_param_selection_sq2 && clipboard.type == CLIP_PARAM_CH2) { | ||
2752 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch2.params[clipboard.src_trig]; | ||
2753 | draw_parameters(); | ||
2754 | } else if (input_handler == handle_param_selection_sq2 && clipboard.type == CLIP_PARAM_CH1) { | ||
2755 | pat_dst->ch2.params[trig_selection_loc] = pat_src->ch1.params[clipboard.src_trig]; | ||
2756 | draw_parameters(); | ||
2757 | } else if (input_handler == handle_param_selection_wave && clipboard.type == CLIP_PARAM_CH3) { | ||
2758 | pat_dst->ch3.params[trig_selection_loc] = pat_src->ch3.params[clipboard.src_trig]; | ||
2759 | draw_parameters(); | ||
2760 | } else if (input_handler == handle_param_selection_noise && clipboard.type == CLIP_PARAM_CH4) { | ||
2761 | pat_dst->ch4.params[trig_selection_loc] = pat_src->ch4.params[clipboard.src_trig]; | ||
2762 | draw_parameters(); | ||
2763 | } else if (input_handler == handle_channel_selection && clipboard.type == CLIP_CHANNEL) { | ||
2764 | // Copy notes from a different channel OR notes and parameters | ||
2765 | // from a different pattern. | ||
2766 | if (clipboard.src_chan == channel_selection_loc) { | ||
2767 | switch (clipboard.src_chan) { | ||
2768 | case 0: { pat_dst->ch1 = pat_src->ch1; } break; | ||
2769 | case 1: { pat_dst->ch2 = pat_src->ch2; } break; | ||
2770 | case 2: { pat_dst->ch3 = pat_src->ch3; } break; | ||
2771 | case 3: { pat_dst->ch4 = pat_src->ch4; } break; | ||
2772 | } | ||
2773 | } else { | ||
2774 | switch (clipboard.src_chan) { | ||
2775 | case 0: { | ||
2776 | switch (channel_selection_loc) { | ||
2777 | case 0: { | ||
2778 | for (size_t i = 0; i < 16; i++) { | ||
2779 | pat_dst->ch1.notes[i] = pat_src->ch1.notes[i]; | ||
2780 | } | ||
2781 | } break; | ||
2782 | case 1: { | ||
2783 | for (size_t i = 0; i < 16; i++) { | ||
2784 | pat_dst->ch2.notes[i] = pat_src->ch1.notes[i]; | ||
2785 | } | ||
2786 | } break; | ||
2787 | case 2: { | ||
2788 | for (size_t i = 0; i < 16; i++) { | ||
2789 | pat_dst->ch3.notes[i] = pat_src->ch1.notes[i]; | ||
2790 | } | ||
2791 | } break; | ||
2792 | case 3: { | ||
2793 | for (size_t i = 0; i < 16; i++) { | ||
2794 | pat_dst->ch4.notes[i] = pat_src->ch1.notes[i]; | ||
2795 | } | ||
2796 | } break; | ||
2797 | } | ||
2798 | } break; | ||
2799 | case 1: { | ||
2800 | switch (channel_selection_loc) { | ||
2801 | case 0: { | ||
2802 | for (size_t i = 0; i < 16; i++) { | ||
2803 | pat_dst->ch1.notes[i] = pat_src->ch2.notes[i]; | ||
2804 | } | ||
2805 | } break; | ||
2806 | case 1: { | ||
2807 | for (size_t i = 0; i < 16; i++) { | ||
2808 | pat_dst->ch2.notes[i] = pat_src->ch2.notes[i]; | ||
2809 | } | ||
2810 | } break; | ||
2811 | case 2: { | ||
2812 | for (size_t i = 0; i < 16; i++) { | ||
2813 | pat_dst->ch3.notes[i] = pat_src->ch2.notes[i]; | ||
2814 | } | ||
2815 | } break; | ||
2816 | case 3: { | ||
2817 | for (size_t i = 0; i < 16; i++) { | ||
2818 | pat_dst->ch4.notes[i] = pat_src->ch2.notes[i]; | ||
2819 | } | ||
2820 | } break; | ||
2821 | } | ||
2822 | } break; | ||
2823 | case 2: { | ||
2824 | switch (channel_selection_loc) { | ||
2825 | case 0: { | ||
2826 | for (size_t i = 0; i < 16; i++) { | ||
2827 | pat_dst->ch1.notes[i] = pat_src->ch3.notes[i]; | ||
2828 | } | ||
2829 | } break; | ||
2830 | case 1: { | ||
2831 | for (size_t i = 0; i < 16; i++) { | ||
2832 | pat_dst->ch2.notes[i] = pat_src->ch3.notes[i]; | ||
2833 | } | ||
2834 | } break; | ||
2835 | case 2: { | ||
2836 | for (size_t i = 0; i < 16; i++) { | ||
2837 | pat_dst->ch3.notes[i] = pat_src->ch3.notes[i]; | ||
2838 | } | ||
2839 | } break; | ||
2840 | case 3: { | ||
2841 | for (size_t i = 0; i < 16; i++) { | ||
2842 | pat_dst->ch4.notes[i] = pat_src->ch3.notes[i]; | ||
2843 | } | ||
2844 | } break; | ||
2845 | } | ||
2846 | } break; | ||
2847 | case 3: { | ||
2848 | switch (channel_selection_loc) { | ||
2849 | case 0: { | ||
2850 | for (size_t i = 0; i < 16; i++) { | ||
2851 | pat_dst->ch1.notes[i] = pat_src->ch4.notes[i]; | ||
2852 | } | ||
2853 | } break; | ||
2854 | case 1: { | ||
2855 | for (size_t i = 0; i < 16; i++) { | ||
2856 | pat_dst->ch2.notes[i] = pat_src->ch4.notes[i]; | ||
2857 | } | ||
2858 | } break; | ||
2859 | case 2: { | ||
2860 | for (size_t i = 0; i < 16; i++) { | ||
2861 | pat_dst->ch3.notes[i] = pat_src->ch4.notes[i]; | ||
2862 | } | ||
2863 | } break; | ||
2864 | case 3: { | ||
2865 | for (size_t i = 0; i < 16; i++) { | ||
2866 | pat_dst->ch4.notes[i] = pat_src->ch4.notes[i]; | ||
2867 | } | ||
2868 | } break; | ||
2869 | } | ||
2870 | } break; | ||
2871 | } | ||
2872 | } | ||
2873 | draw_channels(); | ||
2874 | draw_triggers(); | ||
2875 | } else if (input_handler == handle_pattern_selection && clipboard.type == CLIP_PATTERN) { | ||
2876 | // Copy an entire pattern. | ||
2877 | if (pattern_selection_loc != clipboard.src_pat) { | ||
2878 | *pat_dst = *pat_src; | ||
2879 | draw_channels(); | ||
2880 | draw_triggers(); | ||
2881 | } | ||
2882 | } | ||
2883 | } | ||
2884 | |||
2885 | void | ||
2886 | clipboard_copy(void) { | ||
2887 | if (input_handler == handle_trigger_selection) { | ||
2888 | clipboard.type = CLIP_TRIG; | ||
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_sq1) { | ||
2893 | clipboard.type = CLIP_PARAM_CH1; | ||
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_sq2) { | ||
2898 | clipboard.type = CLIP_PARAM_CH2; | ||
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_wave) { | ||
2903 | clipboard.type = CLIP_PARAM_CH3; | ||
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_param_selection_noise) { | ||
2908 | clipboard.type = CLIP_PARAM_CH4; | ||
2909 | clipboard.src_pat = pattern_selection_loc; | ||
2910 | clipboard.src_chan = channel_selection_loc; | ||
2911 | clipboard.src_trig = trig_selection_loc; | ||
2912 | } else if (input_handler == handle_channel_selection) { | ||
2913 | clipboard.type = CLIP_CHANNEL; | ||
2914 | clipboard.src_pat = pattern_selection_loc; | ||
2915 | clipboard.src_chan = channel_selection_loc; | ||
2916 | } else if (input_handler == handle_pattern_selection) { | ||
2917 | clipboard.type = CLIP_PATTERN; | ||
2918 | clipboard.src_pat = pattern_selection_loc; | ||
2919 | } | ||
2920 | } | ||
2921 | |||
2922 | void | ||
2923 | handle_sequencer_input(void) { | 886 | handle_sequencer_input(void) { |
2924 | if (key_tap(KEY_START)) { | 887 | if (key_tap(KEY_START)) { |
2925 | // Stop the sequencer or start playing from the beginning. | 888 | // Stop the sequencer or start playing from the beginning. |
@@ -2944,17 +907,7 @@ handle_sequencer_input(void) { | |||
2944 | } | 907 | } |
2945 | 908 | ||
2946 | void | 909 | void |
2947 | load_assets(void) { | ||
2948 | unpack_tiles(note_names, ASSETS_NOTE_NAMES, N_TILES_NOTE_NAMES); | ||
2949 | unpack_tiles(channel_buttons, ASSETS_CHANNEL_BUTTONS, N_TILES_CHAN_BTSN); | ||
2950 | unpack_tiles(default_wave_buttons, ASSETS_DEFAULT_WAVES, N_TILES_WAVE_BTNS); | ||
2951 | } | ||
2952 | |||
2953 | void | ||
2954 | sequencer_init(void) { | 910 | sequencer_init(void) { |
2955 | // Unpack non-sprite tiles directly on the VRAM. | ||
2956 | load_assets(); | ||
2957 | |||
2958 | // Load the previous bank from SRAM or initialize it if needed. | 911 | // Load the previous bank from SRAM or initialize it if needed. |
2959 | sram_read(&metadata, 0, sizeof(Metadata)); | 912 | sram_read(&metadata, 0, sizeof(Metadata)); |
2960 | if (metadata.magic != 0xbadd10de) { | 913 | if (metadata.magic != 0xbadd10de) { |
@@ -2972,28 +925,8 @@ sequencer_init(void) { | |||
2972 | load_bank(metadata.current_bank); | 925 | load_bank(metadata.current_bank); |
2973 | } | 926 | } |
2974 | 927 | ||
2975 | // Initialize background objects and sprites. | ||
2976 | draw_triggers(); | ||
2977 | draw_channels(); | ||
2978 | draw_piano(); | ||
2979 | TriggerNote *trig = get_current_trig(); | ||
2980 | draw_note(trig->note, COL_NOTE_PRESSED); | ||
2981 | |||
2982 | // Draw screen border frame. | ||
2983 | draw_rect(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, 1); | ||
2984 | |||
2985 | // Initialize input handler. | 928 | // Initialize input handler. |
2986 | input_handler = handle_trigger_selection; | 929 | input_handler = handle_trigger_selection; |
2987 | draw_trig_cursor(trig_selection_loc, COL_CURSOR); | ||
2988 | draw_channel_cursor(channel_selection_loc, COL_GREY); | ||
2989 | draw_pattern_cursor(pattern_selection_loc, COL_GREY); | ||
2990 | draw_current_step(COL_RED); | ||
2991 | draw_parameters(); | ||
2992 | draw_bpm(); | ||
2993 | draw_play(); | ||
2994 | draw_stop(); | ||
2995 | draw_pattern_buttons(); | ||
2996 | draw_bank_buttons(); | ||
2997 | 930 | ||
2998 | // Initialize sound system. | 931 | // Initialize sound system. |
2999 | SOUND_STATUS = SOUND_ENABLE; | 932 | SOUND_STATUS = SOUND_ENABLE; |