Refactoring: Project structure, sprite stairs & fog masks rendering fix
This commit is contained in:
+308
@@ -0,0 +1,308 @@
|
||||
#include "sound.h"
|
||||
#include "globals.h"
|
||||
#include <gb/gb.h>
|
||||
|
||||
/**
|
||||
* ==========================================
|
||||
* GAME BOY AUDIO HARDWARE (APU)
|
||||
* ==========================================
|
||||
* Il Game Boy ha 4 canali audio. Noi usiamo:
|
||||
* - Canale 1 (Onda quadra con sweep & envelope): Usato per gli arpeggi rapidi (es. Vittoria).
|
||||
* - Canale 2 (Onda quadra con envelope): Usato per i bassi pulsanti o le melodie tenute.
|
||||
* - Canale 4 (Rumore bianco): Usato per simulare le percussioni (crash o thud) manipolando le frequenze noise.
|
||||
*/
|
||||
|
||||
// --- Variabili di stato interne per i timer musicali ---
|
||||
static volatile uint8_t music_timer = 0;
|
||||
static volatile uint8_t music_step = 0;
|
||||
|
||||
static volatile uint8_t gameover_music_timer = 0;
|
||||
static volatile uint8_t gameover_music_step = 0;
|
||||
|
||||
static volatile uint8_t victory_music_timer = 0;
|
||||
static volatile uint8_t victory_music_step = 0;
|
||||
|
||||
static volatile uint8_t title_music_timer = 0;
|
||||
static volatile uint8_t title_music_step = 0;
|
||||
|
||||
// Note frequencies calculation: (2048 - (131072 / frequency))
|
||||
#define R 0
|
||||
#define N_E2 458
|
||||
#define N_F2 547
|
||||
#define N_G2 742
|
||||
#define N_A2 857
|
||||
#define N_AS2 907
|
||||
#define N_B2 961
|
||||
#define N_C3 1046
|
||||
#define N_CS3 1103
|
||||
#define N_D3 1155
|
||||
#define N_E3 1253
|
||||
#define N_F3 1298
|
||||
#define N_FS3 1340
|
||||
#define N_G3 1380
|
||||
#define N_GS3 1417
|
||||
#define N_A3 1453
|
||||
#define N_AS3 1486
|
||||
#define N_B3 1517
|
||||
#define N_C4 1547
|
||||
#define N_CS4 1575
|
||||
#define N_D4 1602
|
||||
#define N_E4 1651
|
||||
#define N_F4 1673
|
||||
#define N_FS4 1694
|
||||
#define N_G4 1714
|
||||
#define N_GS4 1732
|
||||
#define N_A4 1751
|
||||
#define N_AS4 1767
|
||||
#define N_B4 1783
|
||||
#define N_C5 1798
|
||||
#define N_CS5 1812
|
||||
#define N_D5 1825
|
||||
#define N_E5 1849
|
||||
#define N_F5 1860
|
||||
#define N_FS5 1871
|
||||
#define N_G5 1881
|
||||
#define N_GS5 1890
|
||||
#define N_A5 1899
|
||||
|
||||
// --- Sequences ---
|
||||
|
||||
const uint16_t ch1_seq[128] = {
|
||||
// 1 Dm
|
||||
N_D3, N_A3, N_D4, N_A3, N_D3, N_A3, N_D4, N_A3,
|
||||
// 2 A/C#
|
||||
N_CS3, N_A3, N_CS4, N_A3, N_CS3, N_A3, N_CS4, N_A3,
|
||||
// 3 F/C
|
||||
N_C3, N_A3, N_C4, N_A3, N_C3, N_A3, N_C4, N_A3,
|
||||
// 4 G/B
|
||||
N_B2, N_G3, N_B3, N_G3, N_B2, N_G3, N_B3, N_G3,
|
||||
// 5 Bb
|
||||
N_AS2, N_F3, N_AS3, N_F3, N_AS2, N_F3, N_AS3, N_F3,
|
||||
// 6 F/A
|
||||
N_A2, N_F3, N_A3, N_F3, N_A2, N_F3, N_A3, N_F3,
|
||||
// 7 Gm
|
||||
N_G2, N_D3, N_G3, N_D3, N_G2, N_D3, N_G3, N_D3,
|
||||
// 8 A7
|
||||
N_A2, N_E3, N_A3, N_CS4, N_A2, N_E3, N_A3, N_CS4,
|
||||
// 9 Dm (intensifies: higher arpeggio)
|
||||
N_D3, N_D4, N_F4, N_A4, N_D3, N_D4, N_F4, N_A4,
|
||||
// 10 A/C#
|
||||
N_CS3, N_CS4, N_E4, N_A4, N_CS3, N_CS4, N_E4, N_A4,
|
||||
// 11 F/C
|
||||
N_C3, N_C4, N_F4, N_A4, N_C3, N_C4, N_F4, N_A4,
|
||||
// 12 G/B
|
||||
N_B2, N_B3, N_D4, N_G4, N_B2, N_B3, N_D4, N_G4,
|
||||
// 13 Bb
|
||||
N_AS2, N_AS3, N_D4, N_F4, N_AS2, N_AS3, N_D4, N_F4,
|
||||
// 14 F/A
|
||||
N_A2, N_A3, N_C4, N_F4, N_A2, N_A3, N_C4, N_F4,
|
||||
// 15 Gm
|
||||
N_G2, N_G3, N_AS3, N_D4, N_G2, N_G3, N_AS3, N_D4,
|
||||
// 16 A7 -> Dm resolution
|
||||
N_A2, N_A3, N_CS4, N_E4, N_A2, R, N_D3, R
|
||||
};
|
||||
|
||||
// Victory Arpeggio (fast ascending)
|
||||
const uint16_t victory_seq[16] = {
|
||||
N_C4, N_E4, N_G4, N_C5,
|
||||
N_E5, N_G5, 1950, 1950,
|
||||
N_C5, N_E5, N_G5, 1950,
|
||||
1950, 1950, 1950, R
|
||||
};
|
||||
|
||||
const uint16_t ch2_seq[128] = {
|
||||
// 1 Dm
|
||||
N_F4, R, R, R, N_E4, R, R, R,
|
||||
// 2 A/C#
|
||||
N_E4, R, R, R, N_D4, R, R, R,
|
||||
// 3 F/C
|
||||
N_C4, R, R, R, N_D4, R, N_E4, R,
|
||||
// 4 G/B
|
||||
N_D4, R, R, R, N_F4, R, R, R,
|
||||
// 5 Bb
|
||||
N_D4, R, R, R, N_C4, R, R, R,
|
||||
// 6 F/A
|
||||
N_C4, R, R, R, N_AS3, R, R, R,
|
||||
// 7 Gm
|
||||
N_AS3, R, N_A3, N_AS3, N_C4, R, N_AS3, R,
|
||||
// 8 A7
|
||||
N_A3, R, R, R, R, R, R, R,
|
||||
// 9 Dm
|
||||
N_A4, R, R, R, N_G4, R, N_F4, N_E4,
|
||||
// 10 A/C#
|
||||
N_G4, R, R, R, N_F4, R, N_E4, N_D4,
|
||||
// 11 F/C
|
||||
N_F4, R, R, R, N_E4, R, N_C4, R,
|
||||
// 12 G/B
|
||||
N_D4, R, R, R, N_G4, R, R, R,
|
||||
// 13 Bb
|
||||
N_F4, R, R, R, N_E4, R, N_D4, N_CS4,
|
||||
// 14 F/A
|
||||
N_E4, R, R, R, N_D4, R, N_C4, N_B3,
|
||||
// 15 Gm
|
||||
N_D4, R, R, R, N_C4, R, N_AS3, R,
|
||||
// 16 A7 -> Dm resolution
|
||||
N_A3, R, R, R, R, R, N_D4, R
|
||||
};
|
||||
|
||||
// Eerie pulse note frequencies (96-note sequence for normal gameplay)
|
||||
const uint16_t eerie_reg_vals[] = {
|
||||
// Pattern 1 (A minor)
|
||||
1453, 1546, 1650, 1733, 1754, 1733, 1650, 1546,
|
||||
1453, 1546, 1650, 1733, 1754, 1733, 1650, 1546,
|
||||
1453, 1546, 1650, 1733, 1754, 1733, 1650, 1546,
|
||||
1453, 1546, 1650, 1733, 1754, 1733, 1650, 1546,
|
||||
|
||||
// Pattern 2 (D minor)
|
||||
1303, 1453, 1546, 1650, 1678, 1650, 1546, 1453,
|
||||
1303, 1453, 1546, 1650, 1678, 1650, 1546, 1453,
|
||||
1303, 1453, 1546, 1650, 1678, 1650, 1546, 1453,
|
||||
1303, 1453, 1546, 1650, 1678, 1650, 1546, 1453,
|
||||
|
||||
// Pattern 3 (E7 tension)
|
||||
1369, 1497, 1589, 1709, 1733, 1709, 1589, 1497,
|
||||
1369, 1497, 1589, 1709, 1733, 1709, 1589, 1497,
|
||||
1369, 1497, 1589, 1709, 1733, 1709, 1589, 1497,
|
||||
1369, 1497, 1589, 1709, 1733, 1709, 1589, 1497
|
||||
};
|
||||
|
||||
const uint16_t title_melody[32] = {
|
||||
N_A2, N_E3, N_A3, N_E3, N_C4, N_A3, N_E3, N_A2, // Am
|
||||
N_G2, N_D3, N_G3, N_D3, N_B3, N_G3, N_D3, N_G2, // G
|
||||
N_F2, N_C3, N_F3, N_C3, N_A3, N_F3, N_C3, N_F2, // F
|
||||
N_E2, N_B2, N_E3, N_B2, N_GS3, N_E3, N_B2, N_E2 // E
|
||||
};
|
||||
|
||||
void sound_reset_music_state(void) {
|
||||
music_timer = 0;
|
||||
music_step = 0;
|
||||
gameover_music_timer = 0;
|
||||
gameover_music_step = 0;
|
||||
victory_music_timer = 0;
|
||||
victory_music_step = 0;
|
||||
title_music_timer = 0;
|
||||
title_music_step = 0;
|
||||
}
|
||||
|
||||
static void play_note(uint16_t reg_val) {
|
||||
NR21_REG = 0x80; // 50% duty cycle
|
||||
NR22_REG = 0x73; // volume 7, fade envelope
|
||||
NR23_REG = reg_val & 0xFF;
|
||||
NR24_REG = (reg_val >> 8) | 0x80; // trigger note
|
||||
}
|
||||
|
||||
void play_gameover_step(uint8_t step) {
|
||||
if (step < 128) {
|
||||
uint16_t n1 = ch1_seq[step];
|
||||
if (n1 != R) {
|
||||
NR10_REG = 0x00; // No sweep
|
||||
NR11_REG = 0x80; // 50% duty
|
||||
NR12_REG = 0x73; // volume 7, fast fade for arpeggio pluck
|
||||
NR13_REG = n1 & 0xFF;
|
||||
NR14_REG = (n1 >> 8) | 0x80; // trigger note
|
||||
}
|
||||
|
||||
uint16_t n2 = ch2_seq[step];
|
||||
if (n2 != R) {
|
||||
NR21_REG = 0x80; // 50% duty
|
||||
NR22_REG = 0xA7; // volume 10, long fade for melody sustain
|
||||
NR23_REG = n2 & 0xFF;
|
||||
NR24_REG = (n2 >> 8) | 0x80;
|
||||
}
|
||||
|
||||
// Add dramatic percussion on the first beat of each chord (every 8 steps) using Channel 4 (Noise)
|
||||
if (step % 8 == 0) {
|
||||
NR41_REG = 0x01;
|
||||
NR42_REG = 0xB2; // Volume 11, quick fade
|
||||
// step < 64 gets a lower tympani, step >= 64 gets a crash
|
||||
if (step < 64) {
|
||||
NR43_REG = 0x68; // Low thud freq
|
||||
} else {
|
||||
NR43_REG = 0x42; // Crash freq
|
||||
}
|
||||
NR44_REG = 0x80; // trigger noise
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void play_victory_step(uint8_t step) {
|
||||
if (step < 16) {
|
||||
uint16_t n1 = victory_seq[step];
|
||||
if (n1 != R) {
|
||||
NR10_REG = 0x00;
|
||||
NR11_REG = 0x80; // 50% duty
|
||||
NR12_REG = 0xA7; // bright loud fade
|
||||
NR13_REG = n1 & 0xFF;
|
||||
NR14_REG = (n1 >> 8) | 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Questo interrupt scatta ogni VBL (Vertical Blanking), ovvero ~60 volte al secondo.
|
||||
* Funziona come un mini "sequencer" audio: in base allo stato del gioco, controlla il timer
|
||||
* e riproduce la nota corrispondente all'interno dell'array della traccia.
|
||||
*/
|
||||
void play_music_tick(void) {
|
||||
if (app_state == 0) { // TITLE SCREEN
|
||||
title_music_timer++;
|
||||
if (title_music_timer >= 30) { // 30 frames per note
|
||||
title_music_timer = 0;
|
||||
uint16_t n = title_melody[title_music_step];
|
||||
NR10_REG = 0;
|
||||
NR11_REG = 0x80;
|
||||
NR12_REG = 0x63; // soft fade
|
||||
NR13_REG = n & 0xFF;
|
||||
NR14_REG = (n >> 8) | 0x80;
|
||||
|
||||
// Add a slow bass note every 4 steps
|
||||
if (title_music_step % 4 == 0) {
|
||||
NR21_REG = 0x80;
|
||||
NR22_REG = 0x77;
|
||||
uint16_t bn = n / 2; // Octave lower (divide frequency logic)
|
||||
NR23_REG = bn & 0xFF;
|
||||
NR24_REG = (bn >> 8) | 0x80;
|
||||
}
|
||||
|
||||
title_music_step++;
|
||||
if (title_music_step >= 32) title_music_step = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (game_over) { // ENDGAME
|
||||
if (game_over_timer == 0) { // Se il ritardo di fine partita è terminato
|
||||
if (game_over == 1) { // Defeat (Ghost)
|
||||
gameover_music_timer++;
|
||||
if (gameover_music_timer >= 10) { // 10 frames per note (~6 notes/sec)
|
||||
gameover_music_timer = 0;
|
||||
if (gameover_music_step < 128) {
|
||||
play_gameover_step(gameover_music_step);
|
||||
gameover_music_step++;
|
||||
}
|
||||
}
|
||||
} else if (game_over == 2) { // Victory (Portal)
|
||||
victory_music_timer++;
|
||||
if (victory_music_timer >= 8) { // faster tempo
|
||||
victory_music_timer = 0;
|
||||
if (victory_music_step < 16) {
|
||||
play_victory_step(victory_music_step);
|
||||
victory_music_step++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// GAMEPLAY
|
||||
music_timer++;
|
||||
if (music_timer >= 20) {
|
||||
music_timer = 0;
|
||||
play_note(eerie_reg_vals[music_step]);
|
||||
music_step++;
|
||||
if (music_step >= 96) {
|
||||
music_step = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user