diff options
author | Bad Diode <bd@badd10de.dev> | 2024-01-16 09:31:31 +0100 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-01-16 09:31:31 +0100 |
commit | 77374dacdee448b9dea733c0e444da07942b3238 (patch) | |
tree | 1fcb19f8e81c59a8ff5a48205b19a7eff052f0fe | |
parent | 22ddbd9d1688aed3220122ac7a513742140ed3b6 (diff) | |
download | stepper-77374dacdee448b9dea733c0e444da07942b3238.tar.gz stepper-77374dacdee448b9dea733c0e444da07942b3238.zip |
[WIP] Refactor ch3 envelope
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | src/globals.c | 39 | ||||
-rw-r--r-- | src/main.c | 1 | ||||
-rw-r--r-- | src/sequencer.c | 175 |
4 files changed, 142 insertions, 75 deletions
@@ -27,7 +27,7 @@ INC_FLAGS := $(addprefix -I,$(INC_DIRS)) | |||
27 | INC_FLAGS += -I$(LIBGBA_SRC) | 27 | INC_FLAGS += -I$(LIBGBA_SRC) |
28 | 28 | ||
29 | # Output library names and executables. | 29 | # Output library names and executables. |
30 | TARGET := STEPPER-v1.8-dev-14 | 30 | TARGET := STEPPER-v1.8-dev-15 |
31 | ELF := $(BUILD_DIR)/$(TARGET).elf | 31 | ELF := $(BUILD_DIR)/$(TARGET).elf |
32 | BIN := $(BUILD_DIR)/$(TARGET).gba | 32 | BIN := $(BUILD_DIR)/$(TARGET).gba |
33 | 33 | ||
diff --git a/src/globals.c b/src/globals.c index 5063d69..c040585 100644 --- a/src/globals.c +++ b/src/globals.c | |||
@@ -1,15 +1,3 @@ | |||
1 | enum WAVES { | ||
2 | WAVE_SIN, | ||
3 | WAVE_SAW, | ||
4 | WAVE_SQUARE, | ||
5 | WAVE_MAX, | ||
6 | }; | ||
7 | |||
8 | #define WAVE_SIZE 4 | ||
9 | #define WAVE_VARS 4 | ||
10 | |||
11 | typedef u32 Wave[WAVE_SIZE]; | ||
12 | |||
13 | // | 1 | // |
14 | // Globals. | 2 | // Globals. |
15 | // | 3 | // |
@@ -221,9 +209,30 @@ bool redraw_scale = true; | |||
221 | bool update_bpm = false; | 209 | bool update_bpm = false; |
222 | u8 bar_counter = 0; | 210 | u8 bar_counter = 0; |
223 | 211 | ||
224 | static Wave wave_active = {0}; | 212 | typedef enum WaveEnv { |
225 | // static Wave wave_target = {0}; | 213 | WAV_ENV_START, |
226 | // TODO: wave env status: OFF, ATTACK, DECAY | 214 | WAV_ENV_ATTACK, |
215 | WAV_ENV_DECAY, | ||
216 | WAV_ENV_END, | ||
217 | WAV_ENV_OFF, | ||
218 | } WaveEnv; | ||
219 | |||
220 | enum WAVES { | ||
221 | WAVE_SIN, | ||
222 | WAVE_SAW, | ||
223 | WAVE_SQUARE, | ||
224 | WAVE_MAX, | ||
225 | }; | ||
226 | |||
227 | #define WAVE_SIZE 4 | ||
228 | #define WAVE_VARS 4 | ||
229 | |||
230 | typedef u32 Wave[WAVE_SIZE]; | ||
231 | |||
232 | static WaveEnv wave_env = WAV_ENV_OFF; | ||
233 | static const Wave *wave_target; | ||
234 | static u32 wave_freq; | ||
227 | static int wave_env_ticks = 0; | 235 | static int wave_env_ticks = 0; |
228 | static int wave_env_attack = 8; | 236 | static int wave_env_attack = 8; |
229 | static int wave_env_decay = 8; | 237 | static int wave_env_decay = 8; |
238 | static int wave_env_prog = 0; | ||
@@ -37,6 +37,7 @@ WITH REGARD TO THIS SOFTWARE. | |||
37 | // - Study saving overhauls for bootleg cartridges. | 37 | // - Study saving overhauls for bootleg cartridges. |
38 | // - When putting a new trigger, make sure it uses the global parameters | 38 | // - When putting a new trigger, make sure it uses the global parameters |
39 | // - Bad performance when selecting patterns | 39 | // - Bad performance when selecting patterns |
40 | // - Add help for attack/decay on ch3 | ||
40 | // | 41 | // |
41 | // Low priority: | 42 | // Low priority: |
42 | // | 43 | // |
diff --git a/src/sequencer.c b/src/sequencer.c index 8863d14..4b5f34f 100644 --- a/src/sequencer.c +++ b/src/sequencer.c | |||
@@ -227,20 +227,33 @@ play_step(void) { | |||
227 | TriggerNote *trig = &pat->ch3.notes[step_counter]; | 227 | TriggerNote *trig = &pat->ch3.notes[step_counter]; |
228 | ChannelWaveParams *params = &pat->ch3.params[step_counter]; | 228 | ChannelWaveParams *params = &pat->ch3.params[step_counter]; |
229 | if (trig->active && should_play(params->prob)) { | 229 | if (trig->active && should_play(params->prob)) { |
230 | Wave wave_zero = { | ||
231 | 0x77777777, 0x77777777, 0x77777777, 0x77777777, | ||
232 | }; | ||
230 | switch (params->wave_mode) { | 233 | switch (params->wave_mode) { |
231 | case 0: { | 234 | case 0: { |
232 | memcpy32(wave_active, waves[params->shape_a][params->type_a], 16); | 235 | wave_target = &waves[params->shape_a][params->type_a]; |
236 | wave_env_attack = params->wave_attack; | ||
237 | wave_env_decay = params->wave_decay; | ||
238 | wave_env = WAV_ENV_START; | ||
239 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); | ||
240 | memcpy32(SOUND_WAVE_RAM, wave_zero, 16); | ||
233 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(1); | 241 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(1); |
234 | memcpy32(SOUND_WAVE_RAM, wave_active, 16); | 242 | memcpy32(SOUND_WAVE_RAM, wave_zero, 16); |
235 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) | 243 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) |
236 | | SOUND_WAVE_BANK_SELECT(0); | 244 | | SOUND_WAVE_BANK_SELECT(0); |
237 | } break; | 245 | } break; |
238 | case 1: { | 246 | case 1: { |
239 | memcpy32(wave_active, waves[params->shape_b][params->type_b], 16); | 247 | wave_target = &waves[params->shape_b][params->type_b]; |
248 | wave_env_attack = params->wave_attack; | ||
249 | wave_env_decay = params->wave_decay; | ||
250 | wave_env = WAV_ENV_START; | ||
240 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); | 251 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); |
241 | memcpy32(SOUND_WAVE_RAM, wave_active, 16); | 252 | memcpy32(SOUND_WAVE_RAM, wave_zero, 16); |
253 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(1); | ||
254 | memcpy32(SOUND_WAVE_RAM, wave_zero, 16); | ||
242 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) | 255 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(0) |
243 | | SOUND_WAVE_BANK_SELECT(1); | 256 | | SOUND_WAVE_BANK_SELECT(0); |
244 | } break; | 257 | } break; |
245 | case 2: { | 258 | case 2: { |
246 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); | 259 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_SELECT(0); |
@@ -249,15 +262,12 @@ play_step(void) { | |||
249 | memcpy32(SOUND_WAVE_RAM, waves[params->shape_a][params->type_a], 16); | 262 | memcpy32(SOUND_WAVE_RAM, waves[params->shape_a][params->type_a], 16); |
250 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(1) | 263 | SOUND_WAVE_MODE = SOUND_WAVE_BANK_MODE(1) |
251 | | SOUND_WAVE_BANK_SELECT(0); | 264 | | SOUND_WAVE_BANK_SELECT(0); |
265 | wave_env = WAV_ENV_OFF; | ||
266 | SOUND_WAVE_MODE |= SOUND_WAVE_ENABLE; | ||
267 | SOUND_WAVE_FREQ = SOUND_FREQ_RESET | sound_rates[trig->note]; | ||
252 | } break; | 268 | } break; |
253 | } | 269 | } |
254 | wave_env_ticks = 0; | 270 | wave_freq = sound_rates[trig->note]; |
255 | wave_env_attack = params->wave_attack; | ||
256 | wave_env_decay = params->wave_decay; | ||
257 | SOUND_WAVE_MODE |= SOUND_WAVE_ENABLE; | ||
258 | |||
259 | SOUND_WAVE_FREQ = SOUND_FREQ_RESET | ||
260 | | sound_rates[trig->note]; | ||
261 | switch (params->wave_volume) { | 271 | switch (params->wave_volume) { |
262 | case 0: { SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; } break; | 272 | case 0: { SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; } break; |
263 | case 1: { SOUND_WAVE_CTRL = SOUND_WAVE_VOL_25; } break; | 273 | case 1: { SOUND_WAVE_CTRL = SOUND_WAVE_VOL_25; } break; |
@@ -354,65 +364,112 @@ static int nseq_ticks = 0; | |||
354 | IWRAM_CODE | 364 | IWRAM_CODE |
355 | void | 365 | void |
356 | wave_ad_tick(void) { | 366 | wave_ad_tick(void) { |
357 | // Play a single step for A+B or no envelope. | 367 | Wave wave_active = {0}; |
358 | if ((SOUND_WAVE_MODE & SOUND_WAVE_BANK_MODE(1)) || !wave_env_decay) { | 368 | env_start: |
359 | if (wave_env_ticks++ == 24) { | 369 | switch (wave_env) { |
360 | SOUND_WAVE_FREQ = SOUND_FREQ_RESET; | 370 | case WAV_ENV_START: { |
361 | SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; | 371 | wave_env_ticks = 0; |
362 | } | 372 | wave_env_prog = 0; |
363 | return; | 373 | SOUND_WAVE_MODE |= SOUND_WAVE_ENABLE; |
364 | } | 374 | SOUND_WAVE_FREQ = SOUND_FREQ_RESET | wave_freq; |
365 | 375 | ||
366 | if (wave_env_ticks++ < wave_env_decay) { | 376 | if (wave_env_attack == 0) { |
367 | return; | 377 | memcpy32(wave_active, wave_target, 16); |
368 | } | 378 | memcpy32(SOUND_WAVE_RAM, wave_active, 16); |
369 | wave_env_ticks = 0; | 379 | SOUND_WAVE_MODE ^= SOUND_WAVE_BANK_SELECT(1); |
370 | 380 | } else { | |
371 | // Decay. | 381 | wave_env = WAV_ENV_ATTACK; |
372 | for (size_t j = 0; j < 4; j++) { | 382 | goto env_start; |
373 | u32 next = 0; | 383 | } |
374 | for (size_t i = 0; i < 8; i++) { | 384 | |
375 | u8 val = (wave_active[j] >> 4 * i) & 0xF; | 385 | if (wave_env_decay == 0) { |
376 | // Operating in 24.8 fixed point: | 386 | wave_env = WAV_ENV_OFF; |
377 | // 0.9 = 230; 0.8 = 205; 0.75 = 192 | 387 | goto env_start; |
378 | int power = 205; | 388 | } else { |
379 | if (val < 0x7) { | 389 | wave_env = WAV_ENV_DECAY; |
380 | int tmp = 0x7 - val; | 390 | } |
381 | tmp *= power; | 391 | return; |
382 | tmp >>= 8; | 392 | } break; |
383 | val = (0x7 - tmp) & 0xf; | 393 | case WAV_ENV_ATTACK: { |
384 | } else if (val > 0x7) { | 394 | // TODO: Attack envelope |
385 | int tmp = (val - 0x7); | 395 | } break; |
386 | tmp *= power; | 396 | case WAV_ENV_DECAY: { |
387 | tmp >>= 8; | 397 | if (wave_env_ticks++ < wave_env_decay) { |
388 | val = tmp + 0x7; | 398 | return; |
389 | if (val <= 0x7) { | 399 | } |
390 | val = 0x7; | 400 | wave_env_ticks = 0; |
401 | |||
402 | // Decay. | ||
403 | int powers[] = { | ||
404 | // FP_NUM(1,8), 230, 205, 192, 150, 100, 50, 0 | ||
405 | // 256, 128, 85, 64, 51, 43, 37, 32, 28, 0 | ||
406 | // Logarithmic volume range. | ||
407 | // 256, 245, 224, 202, 177, 150, 120, 86, 47, 0, | ||
408 | // 0., 57., 104., 143., 177., 208., 235., 259. | ||
409 | 256, 235, 208, 177, 143, 104, 57, 0 | ||
410 | }; | ||
411 | int power = powers[wave_env_prog]; | ||
412 | if (++wave_env_prog >= 8) { | ||
413 | wave_env = WAV_ENV_END; | ||
414 | } | ||
415 | for (size_t j = 0; j < 4; j++) { | ||
416 | u32 next = 0; | ||
417 | u32 prev = (*wave_target)[j]; | ||
418 | for (size_t i = 0; i < 8; i++) { | ||
419 | u8 val = (prev >> 4 * i) & 0xF; | ||
420 | // Operating in 24.8 fixed point: | ||
421 | if (val < 0x7) { | ||
422 | int tmp = 0x7 - val; | ||
423 | tmp *= power; | ||
424 | tmp >>= 8; | ||
425 | val = (0x7 - tmp) & 0xf; | ||
426 | } else if (val > 0x7) { | ||
427 | int tmp = (val - 0x7); | ||
428 | tmp *= power; | ||
429 | tmp >>= 8; | ||
430 | val = tmp + 0x7; | ||
431 | if (val <= 0x7) { | ||
432 | val = 0x7; | ||
433 | } | ||
434 | } | ||
435 | next |= (val << 4 * i); | ||
391 | } | 436 | } |
437 | wave_active[j] = next; | ||
392 | } | 438 | } |
393 | next |= (val << 4 * i); | ||
394 | } | ||
395 | wave_active[j] = next; | ||
396 | } | ||
397 | 439 | ||
398 | // DEBUG: | 440 | // DEBUG: |
399 | // int x = 50; | 441 | // int x = 50; |
400 | // int y = 50; | 442 | // int y = 50; |
401 | // draw_filled_rect(0 + x, 0 + y, 100 + x, 32 + y, COL_BG); | 443 | // draw_filled_rect(0 + x, 0 + y, 100 + x, 32 + y, COL_BG); |
402 | // draw_wave_pattern(&wave_active, 0 + x, 0 + y, 1); | 444 | // draw_wave_pattern(&wave_active, 0 + x, 0 + y, 1); |
403 | 445 | ||
404 | memcpy32(SOUND_WAVE_RAM, wave_active, 16); | 446 | memcpy32(SOUND_WAVE_RAM, wave_active, 16); |
405 | SOUND_WAVE_MODE ^= SOUND_WAVE_BANK_SELECT(1); | 447 | SOUND_WAVE_MODE ^= SOUND_WAVE_BANK_SELECT(1); |
448 | } break; | ||
449 | case WAV_ENV_OFF: { | ||
450 | if (wave_env_ticks++ == 24) { | ||
451 | SOUND_WAVE_FREQ = SOUND_FREQ_RESET; | ||
452 | SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; | ||
453 | wave_env = WAV_ENV_END; | ||
454 | } | ||
455 | return; | ||
456 | } break; | ||
457 | case WAV_ENV_END: { | ||
458 | SOUND_WAVE_FREQ = SOUND_FREQ_RESET; | ||
459 | SOUND_WAVE_CTRL = SOUND_WAVE_MUTE; | ||
460 | return; | ||
461 | } break; | ||
462 | } | ||
406 | } | 463 | } |
407 | 464 | ||
408 | void | 465 | void |
409 | sequencer_tick(void) { | 466 | sequencer_tick(void) { |
410 | wave_ad_tick(); | ||
411 | if (nseq_ticks++ == 0) { | 467 | if (nseq_ticks++ == 0) { |
412 | play_step(); | 468 | play_step(); |
413 | } else if (nseq_ticks == 24) { | 469 | } else if (nseq_ticks == 24) { |
414 | nseq_ticks = 0; | 470 | nseq_ticks = 0; |
415 | } | 471 | } |
472 | wave_ad_tick(); | ||
416 | } | 473 | } |
417 | 474 | ||
418 | void | 475 | void |
@@ -1291,8 +1348,8 @@ set_param_selection_wave(ChannelWaveParams *params, InputHandler return_handler) | |||
1291 | case 4: { params->prob = CLAMP(params->prob + inc * -1, 0, PROB_NUM - 1); } break; | 1348 | case 4: { params->prob = CLAMP(params->prob + inc * -1, 0, PROB_NUM - 1); } break; |
1292 | case 5: { params->wave_mode = CLAMP(params->wave_mode + inc, 0, 2); } break; | 1349 | case 5: { params->wave_mode = CLAMP(params->wave_mode + inc, 0, 2); } break; |
1293 | case 6: { params->wave_volume = CLAMP(params->wave_volume + inc, 0, 4); } break; | 1350 | case 6: { params->wave_volume = CLAMP(params->wave_volume + inc, 0, 4); } break; |
1294 | case 7: { params->wave_attack = CLAMP(params->wave_attack + inc, 0, 24); } break; | 1351 | case 7: { params->wave_attack = CLAMP(params->wave_attack + inc, 0, 16); } break; |
1295 | case 8: { params->wave_decay = CLAMP(params->wave_decay + inc, 0, 24); } break; | 1352 | case 8: { params->wave_decay = CLAMP(params->wave_decay + inc, 0, 16); } break; |
1296 | case 9: { params->pan = CLAMP(params->pan + inc, -1, 1); } break; | 1353 | case 9: { params->pan = CLAMP(params->pan + inc, -1, 1); } break; |
1297 | } | 1354 | } |
1298 | redraw_params = true; | 1355 | redraw_params = true; |