diff options
author | Bad Diode <bd@badd10de.dev> | 2021-05-02 12:32:15 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2021-05-02 12:32:15 +0200 |
commit | c9fbc44912f54a0263b6c1514f086cc1b5ef3079 (patch) | |
tree | e0fd4ae540ef86f22dc36f7cff673decf5aaab98 | |
parent | 01e6349dfb76abaf9d87d94369b17c14b678e416 (diff) | |
download | gba-experiments-c9fbc44912f54a0263b6c1514f086cc1b5ef3079.tar.gz gba-experiments-c9fbc44912f54a0263b6c1514f086cc1b5ef3079.zip |
Start work on seq sprite rendering
-rw-r--r-- | src/main.c | 81 | ||||
-rw-r--r-- | src/sequencer.c | 218 |
2 files changed, 230 insertions, 69 deletions
@@ -5,6 +5,7 @@ | |||
5 | #include "background-tiles.c" | 5 | #include "background-tiles.c" |
6 | #include "sprites.h" | 6 | #include "sprites.h" |
7 | #include "text.h" | 7 | #include "text.h" |
8 | #include "sequencer.c" | ||
8 | 9 | ||
9 | // | 10 | // |
10 | // Main functions. | 11 | // Main functions. |
@@ -13,63 +14,6 @@ | |||
13 | // TODO: Cleanup OBJ/OAM memory copying and access. | 14 | // TODO: Cleanup OBJ/OAM memory copying and access. |
14 | // | 15 | // |
15 | 16 | ||
16 | typedef struct SeqTrigger { | ||
17 | bool trigger; | ||
18 | Note note; | ||
19 | // TODO: ... | ||
20 | } SeqTrigger; | ||
21 | |||
22 | static SeqTrigger sequence_synth_1 [] = { | ||
23 | {true, NOTE_D_4}, | ||
24 | {true, NOTE_F_4}, | ||
25 | {true, NOTE_A_4}, | ||
26 | {true, NOTE_C_5}, | ||
27 | |||
28 | {true, NOTE_D_4}, | ||
29 | {false, NOTE_D_4}, | ||
30 | {false, NOTE_D_4}, | ||
31 | {false, NOTE_D_4}, | ||
32 | |||
33 | {true, NOTE_D_4}, | ||
34 | {true, NOTE_F_4}, | ||
35 | {true, NOTE_A_4}, | ||
36 | {true, NOTE_C_5}, | ||
37 | |||
38 | {true, NOTE_D_4}, | ||
39 | {false, NOTE_D_4}, | ||
40 | {true, NOTE_A_4}, | ||
41 | {false, NOTE_A_5}, | ||
42 | }; | ||
43 | |||
44 | int step_counter = 0; | ||
45 | Note active_note; | ||
46 | void | ||
47 | irq_timer_0(void) { | ||
48 | active_note = sequence_synth_1[step_counter].note; | ||
49 | if (sequence_synth_1[step_counter].trigger) { | ||
50 | SOUND_SQUARE1_CTRL = SOUND_SQUARE_ENV_VOL(13) | SOUND_SQUARE_ENV_TIME(4) | SOUND_SQUARE_DUTY(2); | ||
51 | SOUND_SQUARE1_FREQ = SOUND_SQUARE_RESET | sound_rates[active_note]; | ||
52 | } | ||
53 | |||
54 | step_counter = (step_counter + 1) % 16; | ||
55 | } | ||
56 | |||
57 | void | ||
58 | set_time(int bpm) { | ||
59 | // TIMER_CTRL_0 = TIMER_CTRL_DISABLE; | ||
60 | |||
61 | // The number of ticks of a 1024 cycle clock in a step based on the BPM can | ||
62 | // be calculated as: | ||
63 | // X bpm -> 60000 / 4 / bpm = Y ms = Ye-3 s | ||
64 | // Y ms -> Ye-3 / 59.99e-9 /1024 = Z ticks | ||
65 | // We have to operate on integer values, so the numbers have been | ||
66 | // precalculated to `n_ticks = 244181 / bmp` | ||
67 | int n_ticks = -244181 / bpm; | ||
68 | |||
69 | irs_set(IRQ_TIMER_0, irq_timer_0); | ||
70 | TIMER_DATA_0 = n_ticks; | ||
71 | TIMER_CTRL_0 = TIMER_CTRL_IRQ | TIMER_CTRL_ENABLE | TIMER_CTRL_FREQ_3; | ||
72 | } | ||
73 | 17 | ||
74 | int main(void) { | 18 | int main(void) { |
75 | // Configure the display in mode 0 to show OBJs, where tile memory is | 19 | // Configure the display in mode 0 to show OBJs, where tile memory is |
@@ -79,10 +23,10 @@ int main(void) { | |||
79 | // Initialize sprite button overlay. | 23 | // Initialize sprite button overlay. |
80 | init_sprite_pal(0, COLOR_WHITE); | 24 | init_sprite_pal(0, COLOR_WHITE); |
81 | init_sprites(0); | 25 | init_sprites(0); |
82 | init_button_sprites(); | 26 | init_sequencer_sprites(); |
83 | 27 | ||
84 | // Initialize text engine. | 28 | // Initialize text engine. |
85 | txt_init(0, COLOR_RED, 0); | 29 | // txt_init(0, COLOR_RED, 0); |
86 | 30 | ||
87 | // Register interrupts. | 31 | // Register interrupts. |
88 | irq_init(); | 32 | irq_init(); |
@@ -90,7 +34,7 @@ int main(void) { | |||
90 | 34 | ||
91 | // turn sound on | 35 | // turn sound on |
92 | SOUND_STATUS = SOUND_ENABLE; | 36 | SOUND_STATUS = SOUND_ENABLE; |
93 | SOUND_DMG_MASTER = sound_volume(SOUND_SQUARE1 | SOUND_SQUARE2, 7); | 37 | SOUND_DMG_MASTER = sound_volume(SOUND_SQUARE1 | SOUND_SQUARE2, 3); |
94 | SOUND_DSOUND_MASTER = SOUND_DMG100; | 38 | SOUND_DSOUND_MASTER = SOUND_DMG100; |
95 | 39 | ||
96 | // Initialize timer. | 40 | // Initialize timer. |
@@ -116,16 +60,15 @@ int main(void) { | |||
116 | TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; | 60 | TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; |
117 | } | 61 | } |
118 | 62 | ||
119 | txt_position(1,6); | 63 | // txt_position(1,6); |
120 | txt_clear_line(); | 64 | // txt_clear_line(); |
121 | txt_printf(" BPM: %d\n\n", bpm); | 65 | // txt_printf(" BPM: %d\n\n", bpm); |
122 | |||
123 | txt_clear_line(); | ||
124 | txt_printf(" Step: %d\n", step_counter); | ||
125 | txt_clear_line(); | ||
126 | txt_printf(" Note: %s\n", note_names[active_note]); | ||
127 | 66 | ||
128 | update_button_sprites(); | 67 | // txt_clear_line(); |
68 | // txt_printf(" Step: %d\n", step_counter); | ||
69 | // txt_clear_line(); | ||
70 | // txt_printf(" Note: %s\n", note_names[active_note]); | ||
71 | render_sequencer_sprites(); | ||
129 | }; | 72 | }; |
130 | 73 | ||
131 | return 0; | 74 | return 0; |
diff --git a/src/sequencer.c b/src/sequencer.c new file mode 100644 index 0000000..4231def --- /dev/null +++ b/src/sequencer.c | |||
@@ -0,0 +1,218 @@ | |||
1 | #define SPRITE_NOTE_NAMES_W 16 | ||
2 | #define SPRITE_NOTE_NAMES_H 8 | ||
3 | |||
4 | u32 sprite_note_names[] = { | ||
5 | 0x77417111, 0x77000000, 0x00000000, 0x00000000, | ||
6 | 0x13157555, 0x33000000, 0x07040701, 0x07000000, | ||
7 | 0x73457515, 0x73000000, 0x00000000, 0x00000000, | ||
8 | 0x17117351, 0x37000000, 0x07040701, 0x07000000, | ||
9 | 0x77417311, 0x77000000, 0x00000000, 0x00000000, | ||
10 | 0x77417311, 0x71000000, 0x00000000, 0x00000000, | ||
11 | 0x17117155, 0x37000000, 0x07040701, 0x07000000, | ||
12 | 0x77417115, 0x77000000, 0x00000000, 0x00000000, | ||
13 | 0x17157557, 0x35000000, 0x07040701, 0x07000000, | ||
14 | 0x77457517, 0x75000000, 0x00000000, 0x00000000, | ||
15 | 0x17157355, 0x37000000, 0x07040701, 0x07000000, | ||
16 | 0x77457315, 0x77000000, 0x00000000, 0x00000000, | ||
17 | 0x77416141, 0x77000000, 0x00000000, 0x00000000, | ||
18 | 0x13157555, 0x33000000, 0x07040604, 0x07000000, | ||
19 | 0x73456545, 0x73000000, 0x00000000, 0x00000000, | ||
20 | 0x17117351, 0x37000000, 0x07040604, 0x07000000, | ||
21 | 0x77416341, 0x77000000, 0x00000000, 0x00000000, | ||
22 | 0x77416341, 0x71000000, 0x00000000, 0x00000000, | ||
23 | 0x17117155, 0x37000000, 0x07040604, 0x07000000, | ||
24 | 0x77416145, 0x77000000, 0x00000000, 0x00000000, | ||
25 | 0x17157557, 0x35000000, 0x07040604, 0x07000000, | ||
26 | 0x77456547, 0x75000000, 0x00000000, 0x00000000, | ||
27 | 0x17157355, 0x37000000, 0x07040604, 0x07000000, | ||
28 | 0x77456345, 0x77000000, 0x00000000, 0x00000000, | ||
29 | 0x47615171, 0x47000000, 0x00000000, 0x00000000, | ||
30 | 0x13157555, 0x33000000, 0x04060507, 0x04000000, | ||
31 | 0x43655575, 0x43000000, 0x00000000, 0x00000000, | ||
32 | 0x17117351, 0x37000000, 0x04060507, 0x04000000, | ||
33 | 0x47615371, 0x47000000, 0x00000000, 0x00000000, | ||
34 | 0x47615371, 0x41000000, 0x00000000, 0x00000000, | ||
35 | 0x17117155, 0x37000000, 0x04060507, 0x04000000, | ||
36 | 0x47615175, 0x47000000, 0x00000000, 0x00000000, | ||
37 | 0x17157557, 0x35000000, 0x04060507, 0x04000000, | ||
38 | 0x47655577, 0x45000000, 0x00000000, 0x00000000, | ||
39 | 0x17157355, 0x37000000, 0x04060507, 0x04000000, | ||
40 | 0x47655375, 0x47000000, 0x00000000, 0x00000000, | ||
41 | 0x77117141, 0x77000000, 0x00000000, 0x00000000, | ||
42 | 0x13157555, 0x33000000, 0x07010704, 0x07000000, | ||
43 | 0x73157545, 0x73000000, 0x00000000, 0x00000000, | ||
44 | 0x17117351, 0x37000000, 0x07010704, 0x07000000, | ||
45 | 0x77117341, 0x77000000, 0x00000000, 0x00000000, | ||
46 | 0x77117341, 0x71000000, 0x00000000, 0x00000000, | ||
47 | 0x17117155, 0x37000000, 0x07010704, 0x07000000, | ||
48 | 0x77117145, 0x77000000, 0x00000000, 0x00000000, | ||
49 | 0x17157557, 0x35000000, 0x07010704, 0x07000000, | ||
50 | 0x77157547, 0x75000000, 0x00000000, 0x00000000, | ||
51 | 0x17157355, 0x37000000, 0x07010704, 0x07000000, | ||
52 | 0x77157345, 0x77000000, 0x00000000, 0x00000000, | ||
53 | 0x77117151, 0x77000000, 0x00000000, 0x00000000, | ||
54 | 0x13157555, 0x33000000, 0x07010705, 0x07000000, | ||
55 | 0x73157555, 0x73000000, 0x00000000, 0x00000000, | ||
56 | 0x17117351, 0x37000000, 0x07010705, 0x07000000, | ||
57 | 0x77117351, 0x77000000, 0x00000000, 0x00000000, | ||
58 | 0x77117351, 0x71000000, 0x00000000, 0x00000000, | ||
59 | 0x17117155, 0x37000000, 0x07010705, 0x07000000, | ||
60 | 0x77117155, 0x77000000, 0x00000000, 0x00000000, | ||
61 | 0x17157557, 0x35000000, 0x07010705, 0x07000000, | ||
62 | 0x77157557, 0x75000000, 0x00000000, 0x00000000, | ||
63 | 0x17157355, 0x37000000, 0x07010705, 0x07000000, | ||
64 | 0x77157355, 0x77000000, 0x00000000, 0x00000000, | ||
65 | 0x77412111, 0x17000000, 0x00000000, 0x00000000, | ||
66 | 0x13157555, 0x33000000, 0x07040201, 0x01000000, | ||
67 | 0x73452515, 0x13000000, 0x00000000, 0x00000000, | ||
68 | 0x17117351, 0x37000000, 0x07040201, 0x01000000, | ||
69 | 0x77412311, 0x17000000, 0x00000000, 0x00000000, | ||
70 | 0x77412311, 0x11000000, 0x00000000, 0x00000000, | ||
71 | 0x17117155, 0x37000000, 0x07040201, 0x01000000, | ||
72 | 0x77412115, 0x17000000, 0x00000000, 0x00000000, | ||
73 | 0x17157557, 0x35000000, 0x07040201, 0x01000000, | ||
74 | 0x77452517, 0x15000000, 0x00000000, 0x00000000, | ||
75 | 0x17157355, 0x37000000, 0x07040201, 0x01000000, | ||
76 | 0x77452315, 0x17000000, 0x00000000, 0x00000000, | ||
77 | 0x77517151, 0x77000000, 0x00000000, 0x00000000, | ||
78 | }; | ||
79 | |||
80 | typedef struct SeqTrigger { | ||
81 | bool trigger; | ||
82 | Note note; | ||
83 | // TODO: ... | ||
84 | } SeqTrigger; | ||
85 | |||
86 | static SeqTrigger sequence_synth[] = { | ||
87 | {true, NOTE_D_4}, | ||
88 | {true, NOTE_F_4}, | ||
89 | {true, NOTE_A_4}, | ||
90 | {true, NOTE_C_5}, | ||
91 | |||
92 | {true, NOTE_D_4}, | ||
93 | {false, NOTE_D_4}, | ||
94 | {false, NOTE_D_4}, | ||
95 | {false, NOTE_D_4}, | ||
96 | |||
97 | {true, NOTE_D_4}, | ||
98 | {true, NOTE_F_4}, | ||
99 | {true, NOTE_A_4}, | ||
100 | {true, NOTE_C_5}, | ||
101 | |||
102 | {true, NOTE_D_4}, | ||
103 | {false, NOTE_D_4}, | ||
104 | {true, NOTE_A_4}, | ||
105 | {false, NOTE_A_5}, | ||
106 | }; | ||
107 | |||
108 | static int step_counter = 0; | ||
109 | static Note active_note; | ||
110 | |||
111 | void | ||
112 | irq_timer_0(void) { | ||
113 | active_note = sequence_synth[step_counter].note; | ||
114 | if (sequence_synth[step_counter].trigger) { | ||
115 | SOUND_SQUARE1_CTRL = SOUND_SQUARE_ENV_VOL(13) | SOUND_SQUARE_ENV_TIME(4) | SOUND_SQUARE_DUTY(2); | ||
116 | SOUND_SQUARE1_FREQ = SOUND_SQUARE_RESET | sound_rates[active_note]; | ||
117 | } | ||
118 | step_counter = (step_counter + 1) % 16; | ||
119 | } | ||
120 | |||
121 | void | ||
122 | set_time(int bpm) { | ||
123 | // The number of ticks of a 1024 cycle clock in a step based on the BPM can | ||
124 | // be calculated as: | ||
125 | // X bpm -> 60000 / 4 / bpm = Y ms = Ye-3 s | ||
126 | // Y ms -> Ye-3 / 59.99e-9 /1024 = Z ticks | ||
127 | // We have to operate on integer values, so the numbers have been | ||
128 | // precalculated to `n_ticks = 244181 / bmp` | ||
129 | int n_ticks = -244181 / bpm; | ||
130 | |||
131 | irs_set(IRQ_TIMER_0, irq_timer_0); | ||
132 | TIMER_DATA_0 = n_ticks; | ||
133 | TIMER_CTRL_0 = TIMER_CTRL_IRQ | TIMER_CTRL_ENABLE | TIMER_CTRL_FREQ_3; | ||
134 | } | ||
135 | |||
136 | static size_t note_sprites_id = 0; | ||
137 | |||
138 | // | ||
139 | // Sequencer sprite memory. | ||
140 | // | ||
141 | // Currently we load all sequencer sprite memory in the VRAM: | ||
142 | // | ||
143 | // - Note names: 0-73x2 tiles | ||
144 | // - ... | ||
145 | // | ||
146 | // The order of OBJs correspond to: | ||
147 | // | ||
148 | // - 000-015 step note names. | ||
149 | // - 015-029 step trigger steps. | ||
150 | // - 030-045 step duration indicators. | ||
151 | // - 046-046 current step marker. | ||
152 | // - 047-047 trigger selection indicator. | ||
153 | // | ||
154 | |||
155 | size_t obj_counter = 0; | ||
156 | |||
157 | typedef struct SeqSprite { | ||
158 | u8 id; | ||
159 | int x; | ||
160 | int y; | ||
161 | u16 obj_attr_0; | ||
162 | u16 obj_attr_1; | ||
163 | u16 obj_attr_2; | ||
164 | } SeqSprite; | ||
165 | |||
166 | #define SEQ_POS_X 20 | ||
167 | #define SEQ_POS_Y 20 | ||
168 | |||
169 | // SeqSprite seq_sprites[] = { | ||
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 | |||
180 | SeqSprite seq_sprites[16] = {0}; | ||
181 | |||
182 | void | ||
183 | init_sequencer_sprites() { | ||
184 | // Load note sprites. | ||
185 | note_sprites_id = load_packed_sprite_data(&sprite_note_names, 2, 73); | ||
186 | |||
187 | for (size_t i = 0; i < 16; ++i) { | ||
188 | int x = SEQ_POS_X + i * 16; | ||
189 | int y = SEQ_POS_Y; | ||
190 | if (i >= 8) { | ||
191 | y += 32; | ||
192 | x -= 8 * 16; | ||
193 | } | ||
194 | seq_sprites[i].x = x; | ||
195 | seq_sprites[i].y = y; | ||
196 | |||
197 | seq_sprites[i].id = obj_counter++; | ||
198 | seq_sprites[i].obj_attr_0 = OBJ_SHAPE_WIDE | OBJ_Y_COORD(y); | ||
199 | // TODO: They should start hidden until the update function changes that. | ||
200 | // 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); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | void | ||
206 | render_sequencer_sprites() { | ||
207 | size_t len = sizeof(seq_sprites) / sizeof(SeqSprite); | ||
208 | for (size_t i = 0; i < len; ++i) { | ||
209 | size_t id = seq_sprites[i].id; | ||
210 | // TODO: This needs to be done in an update function, not here. | ||
211 | size_t base_tile = sequence_synth[i].note; | ||
212 | seq_sprites[i].obj_attr_2 = base_tile; | ||
213 | |||
214 | OBJ_ATTR_0(id) = seq_sprites[i].obj_attr_0; | ||
215 | OBJ_ATTR_1(id) = seq_sprites[i].obj_attr_1; | ||
216 | OBJ_ATTR_2(id) = seq_sprites[i].obj_attr_2; | ||
217 | } | ||
218 | } | ||