summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2021-05-02 15:44:40 +0200
committerBad Diode <bd@badd10de.dev>2021-05-02 15:44:40 +0200
commit685cc9d6bfb1c376f1231ce7c271c6ab6900b3d9 (patch)
tree156b5dae085e79f2da33a284fc02ec3017378033
parent70cf18e60a945927d1ca24e480b62a675b822f89 (diff)
downloadgba-experiments-685cc9d6bfb1c376f1231ce7c271c6ab6900b3d9.tar.gz
gba-experiments-685cc9d6bfb1c376f1231ce7c271c6ab6900b3d9.zip
Add trigger note control
-rw-r--r--src/main.c26
-rw-r--r--src/sequencer.c172
2 files changed, 152 insertions, 46 deletions
diff --git a/src/main.c b/src/main.c
index 08816f3..d64208e 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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
80u32 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
87u32 sprite_trigger_active_indicator[] = {
88 0x00000000, 0x00f80000,
89};
90
91u32 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
80typedef struct SeqTrigger { 102typedef 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
130static int bpm = 120;
108static int step_counter = 0; 131static int step_counter = 0;
109static Note active_note; 132static 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
136static 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[] = { 193int 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
180SeqSprite seq_sprites[16] = {0}; 195SeqSprite seq_sprites[34] = {0};
181 196
182void 197void
183init_sequencer_sprites() { 198init_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
205void 261void
206update_sequencer_sprites() { 262update_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
306void
307handle_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
224void 344void
225render_sequencer_sprites() { 345render_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;