diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.c | 65 | ||||
-rw-r--r-- | src/rng.c | 15 | ||||
-rw-r--r-- | src/sequencer.c | 566 |
3 files changed, 586 insertions, 60 deletions
@@ -13,6 +13,7 @@ WITH REGARD TO THIS SOFTWARE. | |||
13 | 13 | ||
14 | #include "filesystem.c" | 14 | #include "filesystem.c" |
15 | #include "renderer.c" | 15 | #include "renderer.c" |
16 | #include "sequencer.c" | ||
16 | 17 | ||
17 | // | 18 | // |
18 | // Config parameters. | 19 | // Config parameters. |
@@ -37,11 +38,14 @@ WITH REGARD TO THIS SOFTWARE. | |||
37 | txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+1);\ | 38 | txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+1);\ |
38 | txt_printf("FLIP: %lu ", flip_cycles);\ | 39 | txt_printf("FLIP: %lu ", flip_cycles);\ |
39 | txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+2);\ | 40 | txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+2);\ |
41 | txt_printf("INPUT: %lu ", input_cycles);\ | ||
42 | txt_position((PROF_SHOW_X), (PROF_SHOW_Y)+3);\ | ||
40 | txt_printf("FRAME: %lu ", frame_counter);\ | 43 | txt_printf("FRAME: %lu ", frame_counter);\ |
41 | frame_counter++;\ | 44 | frame_counter++;\ |
42 | } while (0) | 45 | } while (0) |
43 | #define PROF_INIT() \ | 46 | #define PROF_INIT() \ |
44 | u32 frame_counter = 0;\ | 47 | u32 frame_counter = 0;\ |
48 | u32 input_cycles = 0;\ | ||
45 | u32 eval_cycles = 0;\ | 49 | u32 eval_cycles = 0;\ |
46 | u32 flip_cycles = 0; | 50 | u32 flip_cycles = 0; |
47 | #else | 51 | #else |
@@ -50,66 +54,6 @@ WITH REGARD TO THIS SOFTWARE. | |||
50 | #define PROF_INIT() | 54 | #define PROF_INIT() |
51 | #endif | 55 | #endif |
52 | 56 | ||
53 | #define TRIG_W 15 | ||
54 | #define TRIG_H 24 | ||
55 | #define TRIG_START_X 66 | ||
56 | #define TRIG_START_Y 90 | ||
57 | #define TRIG_OFFSET_X (TRIG_W + 4) | ||
58 | #define TRIG_OFFSET_Y (TRIG_H + 7) | ||
59 | |||
60 | void | ||
61 | draw_triggers(void) { | ||
62 | for (size_t i = 0; i < 8; i++) { | ||
63 | size_t x0 = TRIG_START_X + TRIG_OFFSET_X * i; | ||
64 | size_t x1 = TRIG_START_X + TRIG_W + TRIG_OFFSET_X * i; | ||
65 | size_t y0 = TRIG_START_Y; | ||
66 | size_t y1 = TRIG_START_Y + TRIG_H; | ||
67 | draw_rect(x0, y0, x1, y1, 1); | ||
68 | } | ||
69 | for (size_t i = 0; i < 8; i++) { | ||
70 | size_t x0 = TRIG_START_X + TRIG_OFFSET_X * i; | ||
71 | size_t x1 = TRIG_START_X + TRIG_W + TRIG_OFFSET_X * i; | ||
72 | size_t y0 = TRIG_START_Y + TRIG_OFFSET_Y; | ||
73 | size_t y1 = TRIG_START_Y + TRIG_H + TRIG_OFFSET_Y; | ||
74 | draw_rect(x0, y0, x1, y1, 1); | ||
75 | } | ||
76 | } | ||
77 | |||
78 | #define CHAN_START_X 35 | ||
79 | #define CHAN_START_Y 90 | ||
80 | #define CHAN_OFFSET_Y 12 | ||
81 | |||
82 | void | ||
83 | draw_channels(void) { | ||
84 | // Contains 5 channel buttons: Ch. 1-4 + FM. We are only drawing the DMG | ||
85 | // channels for now, since FM may take some time to develop. | ||
86 | u32 channel_buttons[] = { | ||
87 | 0xff017111, 0x117101ff, 0xff008585, 0x879500ff, | ||
88 | 0x0f080808, 0x0808080f, 0xff01b989, 0x89b901ff, | ||
89 | 0xff004242, 0x434a00ff, 0x0f080909, 0x0909080f, | ||
90 | 0xff015d45, 0xc55d01ff, 0xff00a1a1, 0xa1a500ff, | ||
91 | 0x0f080a0a, 0x0a0a080f, 0xff015d45, 0xc55d01ff, | ||
92 | 0xff00a1a1, 0xa12500ff, 0x0f080a0a, 0x0a0b080f, | ||
93 | 0xff01c141, 0xc14101ff, 0xff00151c, 0x141400ff, | ||
94 | 0x0f080808, 0x0808080f, | ||
95 | }; | ||
96 | Tile channel_tiles[3 * 4] = {0}; | ||
97 | unpack_tiles(channel_buttons, channel_tiles, 3 * 4); | ||
98 | size_t k = 0; | ||
99 | for (size_t i = 0; i < 4; i++) { | ||
100 | size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; | ||
101 | draw_tile(CHAN_START_X, y, channel_tiles + k++, false); | ||
102 | draw_tile(CHAN_START_X + 8, y, channel_tiles + k++, false); | ||
103 | draw_tile(CHAN_START_X + 16, y, channel_tiles + k++, false); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | void | ||
108 | sequencer_init(void) { | ||
109 | draw_triggers(); | ||
110 | draw_channels(); | ||
111 | } | ||
112 | |||
113 | int main(void) { | 57 | int main(void) { |
114 | // Adjust system wait times. | 58 | // Adjust system wait times. |
115 | SYSTEM_WAIT = SYSTEM_WAIT_CARTRIDGE; | 59 | SYSTEM_WAIT = SYSTEM_WAIT_CARTRIDGE; |
@@ -132,6 +76,7 @@ int main(void) { | |||
132 | while (true) { | 76 | while (true) { |
133 | bios_vblank_wait(); | 77 | bios_vblank_wait(); |
134 | PROF(flip_buffer(), flip_cycles); | 78 | PROF(flip_buffer(), flip_cycles); |
79 | PROF(handle_sequencer_input(), input_cycles); | ||
135 | PROF_SHOW(); | 80 | PROF_SHOW(); |
136 | } | 81 | } |
137 | 82 | ||
diff --git a/src/rng.c b/src/rng.c new file mode 100644 index 0000000..96760b8 --- /dev/null +++ b/src/rng.c | |||
@@ -0,0 +1,15 @@ | |||
1 | u16 rng_state; | ||
2 | |||
3 | u32 hash16(u32 input, u32 key) { | ||
4 | u32 hash = input * key; | ||
5 | return ((hash >> 16) ^ hash) & 0xFFFF; | ||
6 | } | ||
7 | |||
8 | u16 rng16() { | ||
9 | rng_state += 0xbadd; | ||
10 | return hash16(rng_state, 0x10de); | ||
11 | } | ||
12 | |||
13 | u32 rng32() { | ||
14 | return (rng16() << 16) | rng16(); | ||
15 | } | ||
diff --git a/src/sequencer.c b/src/sequencer.c new file mode 100644 index 0000000..43e6ffc --- /dev/null +++ b/src/sequencer.c | |||
@@ -0,0 +1,566 @@ | |||
1 | #include "rng.c" | ||
2 | |||
3 | // | ||
4 | // Globals. | ||
5 | // | ||
6 | typedef enum { | ||
7 | SEQ_SELECT_TRIGGER, | ||
8 | SEQ_SELECT_CHANNEL, | ||
9 | SEQ_SELECT_PARAMETER, | ||
10 | } SeqSelect; | ||
11 | static int bpm = 115; | ||
12 | static int step_counter = 0; | ||
13 | int trig_selection_loc = 0; | ||
14 | int param_selection_loc = 64; | ||
15 | int channel_selection_loc = 2; | ||
16 | SeqSelect current_selection = SEQ_SELECT_TRIGGER; | ||
17 | |||
18 | // | ||
19 | // Wave data. | ||
20 | // | ||
21 | |||
22 | // TODO: Make them u32. | ||
23 | static const u8 sine_wave[16] = { | ||
24 | 0x89, 0xBC, 0xDE, 0xEF, | ||
25 | 0xFE, 0xED, 0xCB, 0x98, | ||
26 | 0x76, 0x43, 0x21, 0x10, | ||
27 | 0x01, 0x12, 0x34, 0x67, | ||
28 | }; | ||
29 | |||
30 | static const u8 saw_wave[16] = { | ||
31 | 0x01, 0x23, 0x45, 0x67, | ||
32 | 0x89, 0xab, 0xcd, 0xef, | ||
33 | 0x01, 0x23, 0x45, 0x67, | ||
34 | 0x89, 0xab, 0xcd, 0xef, | ||
35 | }; | ||
36 | |||
37 | static const u8 square_wave[16] = { | ||
38 | 0xff, 0xff, 0xff, 0xff, | ||
39 | 0xff, 0xff, 0xff, 0xff, | ||
40 | 0x00, 0x00, 0x00, 0x00, | ||
41 | 0x00, 0x00, 0x00, 0x00, | ||
42 | }; | ||
43 | |||
44 | // TODO: Split into individual trigger types. | ||
45 | typedef struct SeqTrigger { | ||
46 | bool trigger; | ||
47 | Note note; | ||
48 | u8 env_volume; | ||
49 | u8 env_time; | ||
50 | u8 env_direction; | ||
51 | u8 duty_cycle; | ||
52 | u8 sweep_number; | ||
53 | u8 sweep_time; | ||
54 | u8 sweep_direction; | ||
55 | u8 wave_volume; | ||
56 | u8 wave_mode; | ||
57 | u8 wave_a[16]; | ||
58 | u8 wave_b[16]; | ||
59 | } SeqTrigger; | ||
60 | |||
61 | static SeqTrigger sequences[3][16] = { | ||
62 | // Synth 1. | ||
63 | { | ||
64 | {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
65 | {false, NOTE_D_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
66 | {false, NOTE_E_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
67 | {false, NOTE_F_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
68 | {false, NOTE_G_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
69 | {false, NOTE_A_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
70 | {false, NOTE_B_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
71 | {false, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
72 | {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
73 | {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
74 | {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
75 | {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
76 | {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
77 | {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
78 | {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
79 | {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
80 | }, | ||
81 | // Synth 2. | ||
82 | { | ||
83 | {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
84 | {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
85 | {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
86 | {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
87 | {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
88 | {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
89 | {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
90 | {false, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
91 | {false, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
92 | {false, NOTE_B_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
93 | {false, NOTE_A_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
94 | {false, NOTE_G_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
95 | {false, NOTE_F_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
96 | {false, NOTE_E_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
97 | {false, NOTE_D_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
98 | {false, NOTE_C_4, 8, 4, 0, 2, 0, 0, 0, 0, 0, {0}, {0}}, | ||
99 | }, | ||
100 | // Synth 3. | ||
101 | { | ||
102 | {true, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
103 | {true, NOTE_D_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
104 | {true, NOTE_E_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
105 | {true, NOTE_F_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
106 | {true, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
107 | {true, NOTE_A_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
108 | {true, NOTE_B_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
109 | {true, NOTE_C_6, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
110 | {true, NOTE_C_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
111 | {true, NOTE_D_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
112 | {true, NOTE_E_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
113 | {true, NOTE_F_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
114 | {true, NOTE_G_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
115 | {true, NOTE_A_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
116 | {true, NOTE_B_5, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
117 | {true, NOTE_C_6, 8, 4, 0, 2, 0, 0, 0, 3, 0, {0xffffffff, 0x00000000, 0xffffffff, 0x00000000}, {0}}, | ||
118 | }, | ||
119 | }; | ||
120 | |||
121 | |||
122 | #define TRIG_W 15 | ||
123 | #define TRIG_H 24 | ||
124 | #define TRIG_START_X 66 | ||
125 | #define TRIG_START_Y 90 | ||
126 | #define TRIG_OFFSET_X (TRIG_W + 4) | ||
127 | #define TRIG_OFFSET_Y (TRIG_H + 7) | ||
128 | |||
129 | void | ||
130 | draw_triggers(void) { | ||
131 | for (size_t i = 0; i < 8; i++) { | ||
132 | size_t x0 = TRIG_START_X + TRIG_OFFSET_X * i; | ||
133 | size_t x1 = TRIG_START_X + TRIG_W + TRIG_OFFSET_X * i; | ||
134 | size_t y0 = TRIG_START_Y; | ||
135 | size_t y1 = TRIG_START_Y + TRIG_H; | ||
136 | draw_rect(x0, y0, x1, y1, 1); | ||
137 | } | ||
138 | for (size_t i = 0; i < 8; i++) { | ||
139 | size_t x0 = TRIG_START_X + TRIG_OFFSET_X * i; | ||
140 | size_t x1 = TRIG_START_X + TRIG_W + TRIG_OFFSET_X * i; | ||
141 | size_t y0 = TRIG_START_Y + TRIG_OFFSET_Y; | ||
142 | size_t y1 = TRIG_START_Y + TRIG_H + TRIG_OFFSET_Y; | ||
143 | draw_rect(x0, y0, x1, y1, 1); | ||
144 | } | ||
145 | } | ||
146 | |||
147 | #define CHAN_START_X 35 | ||
148 | #define CHAN_START_Y 90 | ||
149 | #define CHAN_OFFSET_Y 12 | ||
150 | |||
151 | void | ||
152 | draw_channels(void) { | ||
153 | // Contains 5 channel buttons: Ch. 1-4 + FM. We are only drawing the DMG | ||
154 | // channels for now, since FM may take some time to develop. | ||
155 | u32 channel_buttons[] = { | ||
156 | 0xff017111, 0x117101ff, 0xff008585, 0x879500ff, | ||
157 | 0x0f080808, 0x0808080f, 0xff01b989, 0x89b901ff, | ||
158 | 0xff004242, 0x434a00ff, 0x0f080909, 0x0909080f, | ||
159 | 0xff015d45, 0xc55d01ff, 0xff00a1a1, 0xa1a500ff, | ||
160 | 0x0f080a0a, 0x0a0a080f, 0xff015d45, 0xc55d01ff, | ||
161 | 0xff00a1a1, 0xa12500ff, 0x0f080a0a, 0x0a0b080f, | ||
162 | 0xff01c141, 0xc14101ff, 0xff00151c, 0x141400ff, | ||
163 | 0x0f080808, 0x0808080f, | ||
164 | }; | ||
165 | Tile channel_tiles[3 * 4] = {0}; | ||
166 | unpack_tiles(channel_buttons, channel_tiles, 3 * 4); | ||
167 | size_t k = 0; | ||
168 | for (size_t i = 0; i < 4; i++) { | ||
169 | size_t y = CHAN_START_Y + i * CHAN_OFFSET_Y; | ||
170 | draw_tile(CHAN_START_X, y, channel_tiles + k++, false); | ||
171 | draw_tile(CHAN_START_X + 8, y, channel_tiles + k++, false); | ||
172 | draw_tile(CHAN_START_X + 16, y, channel_tiles + k++, false); | ||
173 | } | ||
174 | } | ||
175 | |||
176 | void | ||
177 | irq_timer_0(void) { | ||
178 | Note active_note; | ||
179 | { | ||
180 | SeqTrigger *trig = &sequences[0][step_counter]; | ||
181 | active_note = trig->note; | ||
182 | if (trig->trigger) { | ||
183 | SOUND_SQUARE1_SWEEP = SOUND_SWEEP_NUMBER(trig->sweep_number) | ||
184 | | SOUND_SWEEP_DIR(trig->sweep_direction) | ||
185 | | SOUND_SWEEP_TIME(trig->sweep_time); | ||
186 | SOUND_SQUARE1_CTRL = SOUND_SQUARE_ENV_VOL(trig->env_volume) | ||
187 | | SOUND_SQUARE_ENV_TIME(trig->env_time) | ||
188 | | SOUND_SQUARE_ENV_DIR(trig->env_direction) | ||
189 | | SOUND_SQUARE_DUTY(trig->duty_cycle); | ||
190 | SOUND_SQUARE1_FREQ = SOUND_FREQ_RESET | ||
191 | | sound_rates[active_note]; | ||
192 | } | ||
193 | } | ||
194 | { | ||
195 | SeqTrigger *trig = &sequences[1][step_counter]; | ||
196 | active_note = trig->note; | ||
197 | if (trig->trigger) { | ||
198 | SOUND_SQUARE2_CTRL = SOUND_SQUARE_ENV_VOL(trig->env_volume) | ||
199 | | SOUND_SQUARE_ENV_TIME(trig->env_time) | ||
200 | | SOUND_SQUARE_ENV_DIR(trig->env_direction) | ||
201 | | SOUND_SQUARE_DUTY(trig->duty_cycle); | ||
202 | SOUND_SQUARE2_FREQ = SOUND_FREQ_RESET | ||
203 | | sound_rates[active_note]; | ||
204 | } | ||
205 | } | ||
206 | { | ||
207 | SeqTrigger *trig = &sequences[2][step_counter]; | ||
208 | active_note = trig->note; | ||
209 | if (trig->trigger) { | ||
210 | // Update both banks. | ||
211 | // TODO: Actually depends on which bank is selected, no need to | ||
212 | // update both if only one is playing. | ||
213 | // TODO: Should we compare if previous and current wave are the | ||
214 | // same before updating? | ||
215 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(1); | ||
216 | memcpy(SOUND_WAVE_RAM, trig->wave_a, 16); | ||
217 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); | ||
218 | memcpy(SOUND_WAVE_RAM, trig->wave_b, 16); | ||
219 | |||
220 | switch (trig->wave_mode) { | ||
221 | case 0: { | ||
222 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) | ||
223 | | SOUND_WAVE_BANK_SELECT(0); | ||
224 | } break; | ||
225 | case 1: { | ||
226 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) | ||
227 | | SOUND_WAVE_BANK_SELECT(1); | ||
228 | } break; | ||
229 | case 2: { | ||
230 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(1) | ||
231 | | SOUND_WAVE_BANK_SELECT(0); | ||
232 | } break; | ||
233 | case 3: { | ||
234 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(1) | ||
235 | | SOUND_WAVE_BANK_SELECT(1); | ||
236 | } break; | ||
237 | } | ||
238 | SOUND_WAVE_MODE |= SOUND_WAVE_ENABLE; | ||
239 | |||
240 | switch (trig->wave_volume) { | ||
241 | case 0: { | ||
242 | SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; | ||
243 | } break; | ||
244 | case 1: { | ||
245 | SOUND_WAVE_CTRL = SOUND_WAVE_VOL_25; | ||
246 | } break; | ||
247 | case 2: { | ||
248 | SOUND_WAVE_CTRL = SOUND_WAVE_VOL_50; | ||
249 | } break; | ||
250 | case 3: { | ||
251 | SOUND_WAVE_CTRL = SOUND_WAVE_VOL_75; | ||
252 | } break; | ||
253 | case 4: { | ||
254 | SOUND_WAVE_CTRL = SOUND_WAVE_VOL_100; | ||
255 | } break; | ||
256 | } | ||
257 | SOUND_WAVE_FREQ = SOUND_FREQ_RESET | ||
258 | | sound_rates[active_note]; | ||
259 | } | ||
260 | } | ||
261 | step_counter = (step_counter + 1) % 16; | ||
262 | } | ||
263 | |||
264 | #define SEQ_N_CHANNELS 3 | ||
265 | |||
266 | void | ||
267 | handle_sequencer_input(void) { | ||
268 | poll_keys(); | ||
269 | SeqTrigger *trig = &sequences[channel_selection_loc][trig_selection_loc]; | ||
270 | if (current_selection == SEQ_SELECT_TRIGGER) { | ||
271 | if (key_tap(KEY_LEFT)) { | ||
272 | if (trig_selection_loc == 0 || trig_selection_loc == 8) { | ||
273 | current_selection = SEQ_SELECT_CHANNEL; | ||
274 | } else { | ||
275 | trig_selection_loc = MAX(trig_selection_loc - 1, 0); | ||
276 | } | ||
277 | } else if (key_tap(KEY_RIGHT)) { | ||
278 | if (trig_selection_loc != 7) { | ||
279 | trig_selection_loc = MIN(trig_selection_loc + 1, 15); | ||
280 | } | ||
281 | } else if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { | ||
282 | trig_selection_loc = (trig_selection_loc + 8) % 16; | ||
283 | } else if (key_tap(KEY_B)) { | ||
284 | trig->trigger ^= 1; | ||
285 | } else if (key_tap(KEY_L)) { | ||
286 | if (trig->trigger) { | ||
287 | trig->note = MAX(trig->note - 1, NOTE_C_2); | ||
288 | } | ||
289 | } else if (key_tap(KEY_R)) { | ||
290 | if (trig->trigger) { | ||
291 | trig->note = MIN( trig->note + 1, NOTE_C_8); | ||
292 | } | ||
293 | } else if (key_tap(KEY_A)) { | ||
294 | // Switch to parameter selection. | ||
295 | current_selection = SEQ_SELECT_PARAMETER; | ||
296 | } | ||
297 | } else if (current_selection == SEQ_SELECT_PARAMETER) { | ||
298 | if (channel_selection_loc < 2) { | ||
299 | // Move through the selected synth parameters. | ||
300 | if (key_tap(KEY_LEFT)) { | ||
301 | int max_param = 6; | ||
302 | if (channel_selection_loc == 1) { | ||
303 | max_param = 3; | ||
304 | } | ||
305 | if (param_selection_loc == 0) { | ||
306 | param_selection_loc = max_param; | ||
307 | } else { | ||
308 | param_selection_loc = MAX(param_selection_loc - 1, 0); | ||
309 | } | ||
310 | } | ||
311 | if (key_tap(KEY_RIGHT)) { | ||
312 | int max_param = 6; | ||
313 | if (channel_selection_loc == 1) { | ||
314 | max_param = 3; | ||
315 | } | ||
316 | if (param_selection_loc == max_param) { | ||
317 | param_selection_loc = 0; | ||
318 | } else { | ||
319 | param_selection_loc = MIN(param_selection_loc + 1, max_param); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | // Adjust the parameters up or down. | ||
324 | if (key_tap(KEY_L) || key_tap(KEY_R)) { | ||
325 | int inc; | ||
326 | if (key_tap(KEY_L)) { | ||
327 | inc = -1; | ||
328 | } else { | ||
329 | inc = 1; | ||
330 | } | ||
331 | switch (param_selection_loc) { | ||
332 | case 0: { | ||
333 | trig->env_volume = CLAMP(trig->env_volume + inc, 0, 15); | ||
334 | } break; | ||
335 | case 1: { | ||
336 | trig->env_time = CLAMP(trig->env_time + inc, 0, 7); | ||
337 | } break; | ||
338 | case 2: { | ||
339 | trig->env_direction ^= 1; | ||
340 | } break; | ||
341 | case 3: { | ||
342 | trig->duty_cycle = CLAMP(trig->duty_cycle + inc, 0, 3); | ||
343 | } break; | ||
344 | case 4: { | ||
345 | trig->sweep_number = CLAMP(trig->sweep_number + inc, 0, 7); | ||
346 | } break; | ||
347 | case 5: { | ||
348 | trig->sweep_time = CLAMP(trig->sweep_time + inc, 0, 7); | ||
349 | } break; | ||
350 | case 6: { | ||
351 | if (trig->sweep_direction == 0) { | ||
352 | trig->sweep_direction = 1; | ||
353 | } else { | ||
354 | trig->sweep_direction = 0; | ||
355 | } | ||
356 | } break; | ||
357 | } | ||
358 | } | ||
359 | } else if (channel_selection_loc == 2) { | ||
360 | if (key_tap(KEY_LEFT) || key_tap(KEY_RIGHT)) { | ||
361 | int inc = 0; | ||
362 | int loc = param_selection_loc; | ||
363 | if (key_tap(KEY_RIGHT)) { | ||
364 | if (loc == 15 || loc == 31) { | ||
365 | inc = 17; | ||
366 | } else if (loc != 47 && loc != 63){ | ||
367 | inc = 1; | ||
368 | } | ||
369 | } else { | ||
370 | if (loc == 32 || loc == 48) { | ||
371 | inc = -17; | ||
372 | } else if (loc != 16 && loc != 64){ | ||
373 | inc = -1; | ||
374 | } | ||
375 | } | ||
376 | param_selection_loc = CLAMP(loc + inc, 0, 71); | ||
377 | } | ||
378 | if (key_tap(KEY_UP) || key_tap(KEY_DOWN)) { | ||
379 | int inc = 0; | ||
380 | int loc = param_selection_loc; | ||
381 | if (key_tap(KEY_UP)) { | ||
382 | if ((loc >= 16 && loc < 32) || (loc >= 48 && loc < 64)) { | ||
383 | inc = -16; | ||
384 | } else if (loc == 64) { | ||
385 | inc = -48; | ||
386 | } else if (loc == 65) { | ||
387 | inc = -45; | ||
388 | } else if (loc == 66) { | ||
389 | inc = -42; | ||
390 | } else if (loc == 67) { | ||
391 | inc = -39; | ||
392 | } else if (loc == 68) { | ||
393 | inc = -20; | ||
394 | } else if (loc == 69) { | ||
395 | inc = -17; | ||
396 | } else if (loc == 70) { | ||
397 | inc = -14; | ||
398 | } else if (loc == 71) { | ||
399 | inc = -11; | ||
400 | } | ||
401 | } else { | ||
402 | if (loc < 16 || (loc >= 32 && loc < 48)) { | ||
403 | inc = 16; | ||
404 | } else if (loc >= 16 && loc <= 19){ | ||
405 | inc = 48 - (loc - 16); | ||
406 | } else if (loc >= 20 && loc <= 23){ | ||
407 | inc = 45 - (loc - 20); | ||
408 | } else if (loc >= 24 && loc <= 27){ | ||
409 | inc = 42 - (loc - 24); | ||
410 | } else if (loc >= 28 && loc <= 31){ | ||
411 | inc = 39 - (loc - 28); | ||
412 | } else if (loc >= 48 && loc <= 51){ | ||
413 | inc = 20 - (loc - 48); | ||
414 | } else if (loc >= 52 && loc <= 55){ | ||
415 | inc = 17 - (loc - 52); | ||
416 | } else if (loc >= 56 && loc <= 59){ | ||
417 | inc = 14 - (loc - 56); | ||
418 | } else if (loc >= 60 && loc <= 63){ | ||
419 | inc = 11 - (loc - 60); | ||
420 | } | ||
421 | } | ||
422 | param_selection_loc = CLAMP(loc + inc, 0, 71); | ||
423 | } | ||
424 | if (key_tap(KEY_R) || key_tap(KEY_L)) { | ||
425 | int odd = param_selection_loc & 0x1; | ||
426 | int inc; | ||
427 | if (key_tap(KEY_R)) { | ||
428 | inc = 1; | ||
429 | } else { | ||
430 | inc = -1; | ||
431 | } | ||
432 | // Wave: AA BB CC DD ... | ||
433 | // ^^ | ||
434 | // |`- odd | ||
435 | // `-- even | ||
436 | if (param_selection_loc < 32) { | ||
437 | u8 byte_number = param_selection_loc >> 1; | ||
438 | u8 byte = sequences[2][trig_selection_loc].wave_a[byte_number]; | ||
439 | if (odd) { | ||
440 | byte = (~0xF & byte) | ((byte + inc) & 0xF); | ||
441 | } else { | ||
442 | byte = (0xF & byte) | (((byte >> 4) + inc) & 0xF) << 4; | ||
443 | } | ||
444 | sequences[2][trig_selection_loc].wave_a[byte_number] = byte; | ||
445 | } else if (param_selection_loc < 64){ | ||
446 | u8 byte_number = (param_selection_loc - 32) >> 1; | ||
447 | u8 byte = sequences[2][trig_selection_loc].wave_b[byte_number]; | ||
448 | if (odd) { | ||
449 | byte = (~0xF & byte) | (byte + inc); | ||
450 | } else { | ||
451 | byte = (0xF & byte) | ((byte >> 4) + inc) << 4; | ||
452 | } | ||
453 | sequences[2][trig_selection_loc].wave_b[byte_number] = byte; | ||
454 | } else if (param_selection_loc == 64){ | ||
455 | memcpy(&trig->wave_a, &sine_wave, 16); | ||
456 | } else if (param_selection_loc == 65){ | ||
457 | memcpy(&trig->wave_a, &saw_wave, 16); | ||
458 | } else if (param_selection_loc == 66){ | ||
459 | memcpy(&trig->wave_a, &square_wave, 16); | ||
460 | } else if (param_selection_loc == 67){ | ||
461 | u32 rand_wave[4] = { | ||
462 | rng32(), rng32(), rng32(), rng32(), | ||
463 | }; | ||
464 | memcpy(&trig->wave_a, &rand_wave, 16); | ||
465 | } else if (param_selection_loc == 68){ | ||
466 | memcpy(&trig->wave_b, &sine_wave, 16); | ||
467 | } else if (param_selection_loc == 69){ | ||
468 | memcpy(&trig->wave_b, &saw_wave, 16); | ||
469 | } else if (param_selection_loc == 70){ | ||
470 | memcpy(&trig->wave_b, &square_wave, 16); | ||
471 | } else if (param_selection_loc == 71){ | ||
472 | u32 rand_wave[4] = { | ||
473 | rng32(), rng32(), rng32(), rng32(), | ||
474 | }; | ||
475 | memcpy(&trig->wave_b, &rand_wave, 16); | ||
476 | } | ||
477 | } | ||
478 | } | ||
479 | |||
480 | // Go back to trigger selection. | ||
481 | if (key_tap(KEY_A)) { | ||
482 | current_selection = SEQ_SELECT_TRIGGER; | ||
483 | } | ||
484 | |||
485 | // Enable disable trigger. | ||
486 | if (key_tap(KEY_B)) { | ||
487 | trig->trigger ^= 1; | ||
488 | } | ||
489 | } else if (current_selection == SEQ_SELECT_CHANNEL) { | ||
490 | if (key_tap(KEY_RIGHT)) { | ||
491 | current_selection = SEQ_SELECT_TRIGGER; | ||
492 | trig_selection_loc = 0; | ||
493 | param_selection_loc = 0; | ||
494 | } | ||
495 | if (key_tap(KEY_UP)) { | ||
496 | if (channel_selection_loc == 0) { | ||
497 | channel_selection_loc = SEQ_N_CHANNELS - 1; | ||
498 | } else { | ||
499 | channel_selection_loc = MAX(channel_selection_loc - 1, 0); | ||
500 | } | ||
501 | } | ||
502 | if (key_tap(KEY_DOWN)) { | ||
503 | if (channel_selection_loc == SEQ_N_CHANNELS - 1) { | ||
504 | channel_selection_loc = 0; | ||
505 | } else { | ||
506 | channel_selection_loc = MIN(channel_selection_loc + 1, SEQ_N_CHANNELS); | ||
507 | } | ||
508 | } | ||
509 | } | ||
510 | |||
511 | if (key_tap(KEY_START)) { | ||
512 | step_counter = 0; | ||
513 | if ((TIMER_CTRL_0 & TIMER_CTRL_ENABLE) == 0) { | ||
514 | set_time(bpm); | ||
515 | } else { | ||
516 | TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; | ||
517 | SOUND_SQUARE1_CTRL = 0; | ||
518 | SOUND_SQUARE2_CTRL = 0; | ||
519 | SOUND_WAVE_CTRL = 0; | ||
520 | } | ||
521 | } | ||
522 | if (key_tap(KEY_SELECT)) { | ||
523 | TIMER_CTRL_0 ^= TIMER_CTRL_ENABLE; | ||
524 | SOUND_SQUARE1_CTRL = 0; | ||
525 | SOUND_SQUARE2_CTRL = 0; | ||
526 | SOUND_WAVE_CTRL = 0; | ||
527 | } | ||
528 | |||
529 | if (key_tap(KEY_LEFT) | ||
530 | || key_tap(KEY_RIGHT) | ||
531 | || key_tap(KEY_UP) | ||
532 | || key_tap(KEY_DOWN) | ||
533 | || key_tap(KEY_L) | ||
534 | || key_tap(KEY_R) | ||
535 | ) { | ||
536 | } | ||
537 | } | ||
538 | |||
539 | void | ||
540 | set_time(int bpm) { | ||
541 | // The number of ticks of a 1024 cycle clock in a step based on the BPM can | ||
542 | // be calculated as: | ||
543 | // X bpm -> 60000 / 4 / bpm = Y ms = Ye-3 s | ||
544 | // Y ms -> Ye-3 / 59.99e-9 / 1024 = Z ticks | ||
545 | // We have to operate on integer values, so the numbers have been | ||
546 | // precalculated to `n_ticks = 244181 / bmp` | ||
547 | int n_ticks = -244181 / bpm; | ||
548 | irs_set(IRQ_TIMER_0, irq_timer_0); | ||
549 | TIMER_DATA_0 = n_ticks; | ||
550 | TIMER_CTRL_0 = TIMER_CTRL_IRQ | TIMER_CTRL_ENABLE | TIMER_CTRL_FREQ_3; | ||
551 | } | ||
552 | |||
553 | void | ||
554 | sequencer_init(void) { | ||
555 | // Initialize background objects and sprites. | ||
556 | draw_triggers(); | ||
557 | draw_channels(); | ||
558 | |||
559 | // Initialize sound system. | ||
560 | SOUND_STATUS = SOUND_ENABLE; | ||
561 | SOUND_DMG_MASTER = sound_volume(SOUND_SQUARE1 | ||
562 | | SOUND_SQUARE2 | ||
563 | | SOUND_WAVE, 3); | ||
564 | SOUND_DSOUND_MASTER = SOUND_DMG25; | ||
565 | set_time(bpm); | ||
566 | } | ||