diff options
-rw-r--r-- | src/main.c | 26 | ||||
-rw-r--r-- | src/sequencer.c | 172 |
2 files changed, 152 insertions, 46 deletions
@@ -22,6 +22,8 @@ int main(void) { | |||
22 | 22 | ||
23 | // Initialize sprite button overlay. | 23 | // Initialize sprite button overlay. |
24 | init_sprite_pal(0, COLOR_WHITE); | 24 | init_sprite_pal(0, COLOR_WHITE); |
25 | init_sprite_pal(16, COLOR_CYAN); | ||
26 | init_sprite_pal(32, COLOR_RED); | ||
25 | init_sprites(0); | 27 | init_sprites(0); |
26 | init_sequencer_sprites(); | 28 | init_sequencer_sprites(); |
27 | 29 | ||
@@ -38,31 +40,16 @@ int main(void) { | |||
38 | SOUND_DSOUND_MASTER = SOUND_DMG100; | 40 | SOUND_DSOUND_MASTER = SOUND_DMG100; |
39 | 41 | ||
40 | // Initialize timer. | 42 | // Initialize timer. |
41 | int bpm = 120; | ||
42 | |||
43 | while(true) { | 43 | while(true) { |
44 | bios_vblank_wait(); | 44 | bios_vblank_wait(); |
45 | poll_keys(); | 45 | poll_keys(); |
46 | if (key_hold(KEY_UP)) { | ||
47 | bpm += 1; | ||
48 | set_time(bpm); | ||
49 | } | ||
50 | if (key_hold(KEY_DOWN)) { | ||
51 | bpm -= 1; | ||
52 | set_time(bpm); | ||
53 | } | ||
54 | |||
55 | if (key_pressed(KEY_START)) { | ||
56 | step_counter = 0; | ||
57 | set_time(bpm); | ||
58 | } | ||
59 | if (key_pressed(KEY_SELECT)) { | ||
60 | TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; | ||
61 | } | ||
62 | 46 | ||
47 | handle_sequencer_input(); | ||
63 | update_sequencer_sprites(); | 48 | update_sequencer_sprites(); |
49 | render_sequencer_sprites(); | ||
64 | 50 | ||
65 | // txt_position(1,6); | 51 | // DEBUG: Output |
52 | // txt_position(1,0); | ||
66 | // txt_clear_line(); | 53 | // txt_clear_line(); |
67 | // txt_printf(" BPM: %d\n\n", bpm); | 54 | // txt_printf(" BPM: %d\n\n", bpm); |
68 | 55 | ||
@@ -70,7 +57,6 @@ int main(void) { | |||
70 | // txt_printf(" Step: %d\n", step_counter); | 57 | // txt_printf(" Step: %d\n", step_counter); |
71 | // txt_clear_line(); | 58 | // txt_clear_line(); |
72 | // txt_printf(" Note: %s\n", note_names[active_note]); | 59 | // txt_printf(" Note: %s\n", note_names[active_note]); |
73 | render_sequencer_sprites(); | ||
74 | }; | 60 | }; |
75 | 61 | ||
76 | return 0; | 62 | return 0; |
diff --git a/src/sequencer.c b/src/sequencer.c index 8ebb9af..f459d0f 100644 --- a/src/sequencer.c +++ b/src/sequencer.c | |||
@@ -77,6 +77,28 @@ u32 sprite_note_names[] = { | |||
77 | 0x000000e0, 0x202020e0, 0x0000000e, 0x0a0e0a0e, | 77 | 0x000000e0, 0x202020e0, 0x0000000e, 0x0a0e0a0e, |
78 | }; | 78 | }; |
79 | 79 | ||
80 | u32 sprite_trigger_button[] = { | ||
81 | 0xfe020202, 0x02020202, 0xff808080, 0x80808080, | ||
82 | 0x02020202, 0x02020202, 0x80808080, 0x80808080, | ||
83 | 0x02020202, 0x020202fe, 0x80808080, 0x808080ff, | ||
84 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, | ||
85 | }; | ||
86 | |||
87 | u32 sprite_trigger_active_indicator[] = { | ||
88 | 0x00000000, 0x00f80000, | ||
89 | }; | ||
90 | |||
91 | u32 sprite_trigger_selection[] = { | ||
92 | 0x0f010101, 0x00000000, 0x80000000, 0x00000000, | ||
93 | 0x07040404, 0x00000000, 0x00000000, 0x00000000, | ||
94 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, | ||
95 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, | ||
96 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, | ||
97 | 0x00000000, 0x00000000, 0x00000000, 0x00000000, | ||
98 | 0x0101010f, 0x00000000, 0x00000080, 0x00000000, | ||
99 | 0x04040407, 0x00000000, 0x00000000, 0x00000000, | ||
100 | }; | ||
101 | |||
80 | typedef struct SeqTrigger { | 102 | typedef struct SeqTrigger { |
81 | bool trigger; | 103 | bool trigger; |
82 | Note note; | 104 | Note note; |
@@ -105,6 +127,7 @@ static SeqTrigger sequence_synth[] = { | |||
105 | {false, NOTE_A_5}, | 127 | {false, NOTE_A_5}, |
106 | }; | 128 | }; |
107 | 129 | ||
130 | static int bpm = 120; | ||
108 | static int step_counter = 0; | 131 | static int step_counter = 0; |
109 | static Note active_note; | 132 | static Note active_note; |
110 | 133 | ||
@@ -133,15 +156,15 @@ set_time(int bpm) { | |||
133 | TIMER_CTRL_0 = TIMER_CTRL_IRQ | TIMER_CTRL_ENABLE | TIMER_CTRL_FREQ_3; | 156 | TIMER_CTRL_0 = TIMER_CTRL_IRQ | TIMER_CTRL_ENABLE | TIMER_CTRL_FREQ_3; |
134 | } | 157 | } |
135 | 158 | ||
136 | static size_t note_sprites_id = 0; | ||
137 | |||
138 | // | 159 | // |
139 | // Sequencer sprite memory. | 160 | // Sequencer sprite memory. |
140 | // | 161 | // |
141 | // Currently we load all sequencer sprite memory in the VRAM: | 162 | // Currently we load all sequencer sprite memory in the VRAM: |
142 | // | 163 | // |
143 | // - Note names: 0-73x2 tiles | 164 | // - Note names: 73x2 tiles |
144 | // - ... | 165 | // - Sprite trigger button: 8 tiles |
166 | // - Duration indicator (TODO) | ||
167 | // - Current step marker. | ||
145 | // | 168 | // |
146 | // The order of OBJs correspond to: | 169 | // The order of OBJs correspond to: |
147 | // | 170 | // |
@@ -163,33 +186,24 @@ typedef struct SeqSprite { | |||
163 | u16 obj_attr_2; | 186 | u16 obj_attr_2; |
164 | } SeqSprite; | 187 | } SeqSprite; |
165 | 188 | ||
166 | #define SEQ_POS_X 20 | 189 | #define SEQ_POS_X 45 |
167 | #define SEQ_POS_Y 20 | 190 | #define SEQ_POS_Y 50 |
191 | #define SEQ_TRIG_DIST 20 | ||
168 | 192 | ||
169 | // SeqSprite seq_sprites[] = { | 193 | int trig_selection_loc = 1; |
170 | // // 000-015: Step note names. | ||
171 | // { | ||
172 | // .x = SEQ_POS_X, | ||
173 | // .y = SEQ_POS_Y, | ||
174 | // .obj_attr_0 = OBJ_SHAPE_WIDE, | ||
175 | // .obj_attr_1 = OBJ_SHAPE_SMALL, | ||
176 | // .obj_attr_2 = 0, | ||
177 | // }, | ||
178 | // }; | ||
179 | 194 | ||
180 | SeqSprite seq_sprites[16] = {0}; | 195 | SeqSprite seq_sprites[34] = {0}; |
181 | 196 | ||
182 | void | 197 | void |
183 | init_sequencer_sprites() { | 198 | init_sequencer_sprites(void) { |
184 | // Load note sprites. | 199 | // Sprite note names. |
185 | note_sprites_id = load_packed_sprite_data(&sprite_note_names, 2, 73); | 200 | size_t sprite_id = load_packed_sprite_data(&sprite_note_names, 2, 73); |
186 | |||
187 | for (size_t i = 0; i < 16; ++i) { | 201 | for (size_t i = 0; i < 16; ++i) { |
188 | int x = SEQ_POS_X + i * 16; | 202 | int x = SEQ_POS_X + i * SEQ_TRIG_DIST; |
189 | int y = SEQ_POS_Y; | 203 | int y = SEQ_POS_Y; |
190 | if (i >= 8) { | 204 | if (i >= 8) { |
191 | y += 32; | 205 | y += 32; |
192 | x -= 8 * 16; | 206 | x -= 8 * SEQ_TRIG_DIST; |
193 | } | 207 | } |
194 | seq_sprites[i].x = x; | 208 | seq_sprites[i].x = x; |
195 | seq_sprites[i].y = y; | 209 | seq_sprites[i].y = y; |
@@ -199,18 +213,61 @@ init_sequencer_sprites() { | |||
199 | // TODO: They should start hidden until the update function changes that. | 213 | // TODO: They should start hidden until the update function changes that. |
200 | // seq_sprites[i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_HIDDEN; | 214 | // seq_sprites[i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_HIDDEN; |
201 | seq_sprites[i].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); | 215 | seq_sprites[i].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); |
216 | seq_sprites[i].obj_attr_2 = sprites[sprite_id].tile_start; | ||
217 | } | ||
218 | |||
219 | // Trigger boxes. | ||
220 | sprite_id = load_packed_sprite_data(&sprite_trigger_button, 8, 1); | ||
221 | for (size_t i = 0; i < 16; ++i) { | ||
222 | int x = SEQ_POS_X + i * SEQ_TRIG_DIST; | ||
223 | int y = SEQ_POS_Y; | ||
224 | if (i >= 8) { | ||
225 | y += 32; | ||
226 | x -= 8 * SEQ_TRIG_DIST; | ||
227 | } | ||
228 | seq_sprites[i + 16].id = obj_counter++; | ||
229 | seq_sprites[i + 16].obj_attr_0 = OBJ_SHAPE_TALL | OBJ_Y_COORD(y); | ||
230 | seq_sprites[i + 16].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); | ||
231 | seq_sprites[i + 16].obj_attr_2 = sprites[sprite_id].tile_start; | ||
232 | } | ||
233 | |||
234 | sprite_id = load_packed_sprite_data(&sprite_trigger_active_indicator, 1, 1); | ||
235 | // TODO: No need for a for loop. | ||
236 | { | ||
237 | int x = SEQ_POS_X; | ||
238 | int y = SEQ_POS_Y + 15; | ||
239 | seq_sprites[32].id = obj_counter++; | ||
240 | seq_sprites[32].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); | ||
241 | seq_sprites[32].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); | ||
242 | seq_sprites[32].obj_attr_2 = sprites[sprite_id].tile_start | OBJ_PAL_BANK(1); | ||
243 | } | ||
244 | |||
245 | sprite_id = load_packed_sprite_data(&sprite_trigger_selection, 16, 1); | ||
246 | // TODO: No need for a for loop. | ||
247 | { | ||
248 | int x = SEQ_POS_X - 1 + trig_selection_loc * SEQ_TRIG_DIST; | ||
249 | int y = SEQ_POS_Y - 2; | ||
250 | if (trig_selection_loc >= 8) { | ||
251 | y += 32; | ||
252 | x -= 8 * SEQ_TRIG_DIST; | ||
253 | } | ||
254 | seq_sprites[33].id = obj_counter++; | ||
255 | seq_sprites[33].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); | ||
256 | seq_sprites[33].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); | ||
257 | seq_sprites[33].obj_attr_2 = sprites[sprite_id].tile_start | OBJ_PAL_BANK(2); | ||
202 | } | 258 | } |
203 | } | 259 | } |
204 | 260 | ||
205 | void | 261 | void |
206 | update_sequencer_sprites() { | 262 | update_sequencer_sprites(void) { |
207 | // 000-015: Step note names. | 263 | // 000-015: Step note names. |
208 | for (size_t i = 0; i < 16; ++i) { | 264 | for (size_t i = 0; i < 16; ++i) { |
209 | // Each note name is made of 2 8x8 tiles (16x8). | 265 | // Each note name is made of 2 8x8 tiles (16x8). |
210 | size_t base_tile = sequence_synth[i].note * 2; | 266 | size_t base_tile = sequence_synth[i].note * 2; |
211 | 267 | ||
212 | // TODO: Show the note name if is within the duration, hide when trigger | 268 | // TODO: Show the note name if is within the duration, hide when trigger |
213 | // is off or duration of previous note was cut short. | 269 | // is off or duration of previous note was cut short. If not triggered |
270 | // but note name appears, needs to be on a different palette bank. | ||
214 | if (!sequence_synth[i].trigger) { | 271 | if (!sequence_synth[i].trigger) { |
215 | seq_sprites[i].obj_attr_0 |= OBJ_HIDDEN; | 272 | seq_sprites[i].obj_attr_0 |= OBJ_HIDDEN; |
216 | } else { | 273 | } else { |
@@ -219,10 +276,73 @@ update_sequencer_sprites() { | |||
219 | 276 | ||
220 | seq_sprites[i].obj_attr_2 = base_tile; | 277 | seq_sprites[i].obj_attr_2 = base_tile; |
221 | } | 278 | } |
279 | |||
280 | // 33: Sequence indicator. | ||
281 | { | ||
282 | int x = SEQ_POS_X + step_counter * SEQ_TRIG_DIST + 3; | ||
283 | int y = SEQ_POS_Y + 15; | ||
284 | if (step_counter >= 8) { | ||
285 | y += 32; | ||
286 | x -= 8 * SEQ_TRIG_DIST; | ||
287 | } | ||
288 | seq_sprites[32].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); // TODO: Mask and update instead. | ||
289 | seq_sprites[32].obj_attr_1 = OBJ_SIZE_SMALL | OBJ_X_COORD(x); // TODO: Mask and update instead. | ||
290 | } | ||
291 | |||
292 | // 34: Trigger selection. | ||
293 | { | ||
294 | int x = SEQ_POS_X - 1 + trig_selection_loc * SEQ_TRIG_DIST; | ||
295 | int y = SEQ_POS_Y - 2; | ||
296 | if (trig_selection_loc >= 8) { | ||
297 | y += 32; | ||
298 | x -= 8 * SEQ_TRIG_DIST; | ||
299 | } | ||
300 | // TODO: Mask and update instead. | ||
301 | seq_sprites[33].obj_attr_0 = OBJ_SHAPE_SQUARE | OBJ_Y_COORD(y); | ||
302 | seq_sprites[33].obj_attr_1 = OBJ_SIZE_BIG | OBJ_X_COORD(x); | ||
303 | } | ||
304 | } | ||
305 | |||
306 | void | ||
307 | handle_sequencer_input(void) { | ||
308 | if (key_pressed(KEY_LEFT)) { | ||
309 | if (trig_selection_loc == 0) { | ||
310 | trig_selection_loc = 15; | ||
311 | } else { | ||
312 | trig_selection_loc = CLAMP(trig_selection_loc - 1, 0, 15); | ||
313 | } | ||
314 | } | ||
315 | if (key_pressed(KEY_RIGHT)) { | ||
316 | if (trig_selection_loc == 15) { | ||
317 | trig_selection_loc = 0; | ||
318 | } else { | ||
319 | trig_selection_loc = CLAMP(trig_selection_loc + 1, 0, 15); | ||
320 | } | ||
321 | } | ||
322 | if (key_pressed(KEY_UP) || key_pressed(KEY_DOWN)) { | ||
323 | trig_selection_loc = (trig_selection_loc + 8) % 16; | ||
324 | } | ||
325 | if (key_pressed(KEY_B)) { | ||
326 | sequence_synth[trig_selection_loc].trigger ^= 1; | ||
327 | } | ||
328 | if (key_pressed(KEY_L)) { | ||
329 | sequence_synth[trig_selection_loc].note = CLAMP(sequence_synth[trig_selection_loc].note - 1, NOTE_C_2, NOTE_C_8); | ||
330 | } | ||
331 | if (key_pressed(KEY_R)) { | ||
332 | sequence_synth[trig_selection_loc].note = CLAMP(sequence_synth[trig_selection_loc].note + 1, NOTE_C_2, NOTE_C_8); | ||
333 | } | ||
334 | |||
335 | if (key_pressed(KEY_START)) { | ||
336 | step_counter = 0; | ||
337 | set_time(bpm); | ||
338 | } | ||
339 | if (key_pressed(KEY_SELECT)) { | ||
340 | TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; | ||
341 | } | ||
222 | } | 342 | } |
223 | 343 | ||
224 | void | 344 | void |
225 | render_sequencer_sprites() { | 345 | render_sequencer_sprites(void) { |
226 | size_t len = sizeof(seq_sprites) / sizeof(SeqSprite); | 346 | size_t len = sizeof(seq_sprites) / sizeof(SeqSprite); |
227 | for (size_t i = 0; i < len; ++i) { | 347 | for (size_t i = 0; i < len; ++i) { |
228 | size_t id = seq_sprites[i].id; | 348 | size_t id = seq_sprites[i].id; |