aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2023-04-23 15:48:59 +0200
committerBad Diode <bd@badd10de.dev>2023-04-23 15:48:59 +0200
commitff6e784e7c5ebe223666c6c631305397ad358289 (patch)
tree0f87823d48366a6beb8d36d7eea5dc33663d7abd
parentdeb9c48fbd3dc5854de4ae3a04dc999029c10ae0 (diff)
downloadstepper-ff6e784e7c5ebe223666c6c631305397ad358289.tar.gz
stepper-ff6e784e7c5ebe223666c6c631305397ad358289.zip
Start decoupling of rendering from update passes
-rw-r--r--src/assets.c129
-rw-r--r--src/clipboard.c319
-rw-r--r--src/drawing.c1237
-rw-r--r--src/globals.c104
-rw-r--r--src/main.c8
-rw-r--r--src/patterns.c236
-rw-r--r--src/profiling.c39
-rw-r--r--src/renderer_m0.c78
-rw-r--r--src/save.c37
-rw-r--r--src/sequencer.c2079
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
8static 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
84u32 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
106static 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
117static const u32 sine_wave[16] = {
118 0xefdebc89, 0x98cbedfe, 0x10214376, 0x67341201,
119};
120
121static const u32 saw_wave[16] = {
122 0x67452301, 0xefcdab89, 0x67452301, 0xefcdab89,
123};
124
125static 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 @@
1void clipboard_paste(void);
2void clipboard_copy(void);
3
4typedef 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
15typedef struct Clipboard {
16 ClipboardType type;
17 u8 src_pat;
18 u8 src_chan;
19 u8 src_trig;
20} Clipboard;
21
22static Clipboard clipboard = {CLIP_EMPTY, 0, 0, 0};
23
24void
25clipboard_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
283void
284clipboard_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
5void
6draw_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
12void
13draw_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
36void
37draw_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
50void
51clear_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
61void
62draw_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 = &note_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
91void
92draw_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
101void
102draw_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
146void
147draw_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
156void
157draw_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
176void
177draw_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
200void
201draw_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
210void
211draw_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
230void
231draw_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
238void
239draw_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
258void
259draw_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
273void
274draw_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
395void
396draw_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
407void
408draw_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
462void
463draw_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
482void
483draw_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
509void
510draw_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
527IWRAM_CODE
528void
529draw_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
543IWRAM_CODE
544void
545clear_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
553IWRAM_CODE
554void
555draw_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
661void
662draw_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
1023void
1024draw_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
1218void
1219draw_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
5static int step_counter = 0;
6int trig_selection_loc = 0;
7int param_selection_loc = 0;
8int channel_selection_loc = 0;
9int pattern_selection_loc = 0;
10int right_col_selection_loc = 0;
11int play_status = 0;
12static int current_pattern = 0;
13static int next_pattern = 0;
14static 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
82enum 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.
95void (*input_handler)(void);
96
97void handle_trigger_selection(void);
98void handle_channel_selection(void);
99void handle_pattern_selection(void);
100void handle_param_selection_sq1(void);
101void handle_param_selection_sq2(void);
102void handle_param_selection_wave(void);
103void handle_param_selection_noise(void);
104void handle_right_col_selection(void);
diff --git a/src/main.c b/src/main.c
index f89e743..89d0444 100644
--- a/src/main.c
+++ b/src/main.c
@@ -21,7 +21,11 @@ WITH REGARD TO THIS SOFTWARE.
21 21
22void 22void
23render(void) { 23render(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
5typedef struct TriggerNote {
6 bool active;
7 Note note;
8} TriggerNote;
9
10typedef 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
20typedef struct ChannelWaveParams {
21 u8 wave_volume;
22 u8 wave_mode;
23 u32 wave_a[4];
24 u32 wave_b[4];
25} ChannelWaveParams;
26
27typedef struct ChannelNoiseParams {
28 u8 env_volume;
29 u8 env_time;
30 u8 env_direction;
31 u8 bit_mode;
32} ChannelNoiseParams;
33
34typedef struct ChannelSquare {
35 bool active;
36 TriggerNote notes[16];
37 ChannelSquareParams params[16];
38} ChannelSquare;
39
40typedef struct ChannelWave {
41 bool active;
42 TriggerNote notes[16];
43 ChannelWaveParams params[16];
44} ChannelWave;
45
46typedef struct ChannelNoise {
47 bool active;
48 TriggerNote notes[16];
49 ChannelNoiseParams params[16];
50} ChannelNoise;
51
52typedef 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
65const 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
105const 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
145const 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
185const 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
225const int default_bpm = 90;
226
227static 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
43static bool profile_show = true; 43static bool profile_show = true;
44static 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
90static u32 prof_frame_counter = 0; 83static 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
618IWRAM_CODE
619void
620draw_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
5typedef struct Metadata {
6 u8 blank;
7 u32 magic;
8 int current_bank;
9 int current_pattern;
10} Metadata;
11
12static Metadata metadata = {0};
13
14void
15sram_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
21void
22sram_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
28void
29save_bank(int i) {
30 sram_write(&patterns, sizeof(Metadata) + i * sizeof(patterns), sizeof(patterns));
31}
32
33void
34load_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"
4void 5#include "patterns.c"
5sram_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
11void
12sram_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
18void set_time(int bpm); 10void set_time(int bpm);
19void clipboard_paste(void);
20void 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
100static 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
176static 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
187static 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
198static const u32 sine_wave[16] = {
199 0xefdebc89, 0x98cbedfe, 0x10214376, 0x67341201,
200};
201
202static const u32 saw_wave[16] = {
203 0x67452301, 0xefcdab89, 0x67452301, 0xefcdab89,
204};
205
206static const u32 square_wave[16] = {
207 0xffffffff, 0xffffffff, 0x00000000, 0x00000000,
208};
209
210//
211// Globals.
212//
213
214const int default_bpm = 75;
215static int step_counter = 0;
216int trig_selection_loc = 0;
217int param_selection_loc = 0;
218int channel_selection_loc = 0;
219int pattern_selection_loc = 0;
220int right_col_selection_loc = 0;
221int play_status = 0;
222static int current_pattern = 0;
223static int next_pattern = 0;
224static int current_bank = 0;
225
226enum 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
236typedef struct TriggerNote {
237 bool active;
238 Note note;
239} TriggerNote;
240
241typedef 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
251typedef struct ChannelWaveParams {
252 u8 wave_volume;
253 u8 wave_mode;
254 u32 wave_a[4];
255 u32 wave_b[4];
256} ChannelWaveParams;
257
258typedef struct ChannelNoiseParams {
259 u8 env_volume;
260 u8 env_time;
261 u8 env_direction;
262 u8 bit_mode;
263} ChannelNoiseParams;
264
265typedef struct ChannelSquare {
266 bool active;
267 TriggerNote notes[16];
268 ChannelSquareParams params[16];
269} ChannelSquare;
270
271typedef struct ChannelWave {
272 bool active;
273 TriggerNote notes[16];
274 ChannelWaveParams params[16];
275} ChannelWave;
276
277typedef struct ChannelNoise {
278 bool active;
279 TriggerNote notes[16];
280 ChannelNoiseParams params[16];
281} ChannelNoise;
282
283const 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
323const 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
363const 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
403const 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
443typedef struct Metadata {
444 u8 blank;
445 u32 magic;
446 int current_bank;
447 int current_pattern;
448} Metadata;
449
450static Metadata metadata = {0};
451
452typedef struct Pattern {
453 ChannelSquare ch1;
454 ChannelSquare ch2;
455 ChannelWave ch3;
456 ChannelNoise ch4;
457 int bpm;
458 u8 bank;
459} Pattern;
460
461static 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
472typedef 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
483typedef struct Clipboard {
484 ClipboardType type;
485 u8 src_pat;
486 u8 src_chan;
487 u8 src_trig;
488} Clipboard;
489
490static Clipboard clipboard = {CLIP_EMPTY, 0, 0, 0};
491
492void
493save_bank(int i) {
494 sram_write(&patterns, sizeof(Metadata) + i * sizeof(patterns), sizeof(patterns));
495}
496
497void
498load_bank(int i) {
499 sram_read(&patterns, sizeof(Metadata) + i * sizeof(patterns), sizeof(patterns));
500}
501
502//
503// Channel render functions.
504//
505
506void
507draw_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
536void
537draw_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
550void
551clear_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
561void
562draw_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
592void
593draw_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
602void
603draw_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
647void
648draw_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
657void
658draw_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
677void
678draw_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
701void
702draw_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
711void
712draw_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
731void
732draw_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
739void
740draw_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
759void
760draw_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
774void
775draw_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
896void
897draw_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
908void
909draw_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
963void
964draw_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
983void
984draw_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
1010void
1011draw_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
1028IWRAM_CODE
1029void
1030draw_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
1044IWRAM_CODE
1045void
1046clear_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
1054IWRAM_CODE
1055void
1056draw_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
1162void
1163draw_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
1524void
1525draw_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
1719void
1720draw_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
1739void 12void
1740irq_timer(void) { 13irq_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.
1907void (*input_handler)(void);
1908
1909void handle_trigger_selection(void);
1910void handle_channel_selection(void);
1911void handle_pattern_selection(void);
1912void handle_param_selection_sq1(void);
1913void handle_param_selection_sq2(void);
1914void handle_param_selection_wave(void);
1915void handle_param_selection_noise(void);
1916void handle_right_col_selection(void);
1917
1918void 177void
1919handle_channel_selection(void) { 178handle_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
2626void 885void
2627clipboard_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
2885void
2886clipboard_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
2922void
2923handle_sequencer_input(void) { 886handle_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
2946void 909void
2947load_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
2953void
2954sequencer_init(void) { 910sequencer_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;