GBAlatro
A Demake of Balatro for the GBA
Loading...
Searching...
No Matches
audio_utils.c
Go to the documentation of this file.
1
7#include "audio_utils.h"
8
9#include "game_variables.h"
10#include "mgba_logger.h"
11#include "state_machine.h"
12#include "util.h"
13
14#include <maxmod.h>
15#include <stdlib.h>
16
17typedef struct
18{
19 s32 target;
20 s32 stride;
22
23typedef struct
24{
25 s32 current;
26 AudioParamReq req;
28
29// clang-format off
30#define AUDIO_PARAM_REQ_DEFAULT { .target = 0, .stride = 0 }
31#define AUDIO_PARAM_DEFINE(init_val) { .current = init_val, .req = AUDIO_PARAM_REQ_DEFAULT }
32// clang-format on
33
34typedef struct
35{
36 AudioParam pitch;
37 AudioParam tempo;
38 AudioParam volume;
40
41static const u32 DEFAULT_PITCH = 0x400;
42static const u32 DEFAULT_TEMPO = 0x400;
43static const u32 DEFAULT_VOLUME = MM_MODULE_FULL_VOLUME;
44static const u32 MUSIC_CHANGE_FRAMES = 75;
45static const u32 VOLUME_CHANGE_FRAMES = MUSIC_CHANGE_FRAMES / 3;
46
47static void set_audio_param_req(AudioParam* param, s32 target, s32 steps);
48static void speed_change_update(void);
49
50static MusicPlayerState music_player = {
51 .pitch = AUDIO_PARAM_DEFINE(DEFAULT_PITCH),
52 .tempo = AUDIO_PARAM_DEFINE(DEFAULT_TEMPO),
53 .volume = AUDIO_PARAM_DEFINE(DEFAULT_VOLUME),
54};
55
56static StateInfo state_info[] = {
57 STATE_INFO_UPDATE_FN_ONLY(speed_change_update),
58};
59
60static StateMachine song_speed_sm = STATE_MACHINE_DEFINE(state_info, 1);
61
62static void set_audio_param_req(AudioParam* param, s32 target, s32 steps)
63{
64 int offset = target - param->current;
65
66 // if '0' set to '1'
67 steps |= !steps;
68 param->req.stride = offset / steps;
69 param->req.stride = !param->req.stride ? SIGN(offset) : param->req.stride;
70 param->req.target = target;
71}
72
78static inline bool audio_param_update(AudioParam* param)
79{
80 if (abs(param->current - param->req.target) <= abs(param->req.stride))
81 {
82 param->current = param->req.target;
83 return true;
84 }
85 param->current += param->req.stride;
86 return false;
87}
88
89static void speed_change_update(void)
90{
91 bool tempo_reached = audio_param_update(&music_player.tempo);
92 bool pitch_reached = audio_param_update(&music_player.pitch);
93 bool volume_reached = audio_param_update(&music_player.volume);
94
95 mmSetModuleTempo(music_player.tempo.current);
96 mmSetModulePitch(music_player.pitch.current);
97 set_volume(music_player.volume.current);
98
99 if (tempo_reached && pitch_reached && volume_reached)
100 state_machine_remove(&song_speed_sm);
101}
102
103void play_sfx(mm_word id, mm_word rate, mm_byte volume)
104{
105 int adj_volume = volume * g_game_vars.sound_volume / VOLUME_OPTION_MAX;
106 mm_sound_effect sfx = {
107 {id},
108 rate,
109 0,
110 adj_volume,
112 };
113 mmEffectEx(&sfx);
114}
115
117{
118 const u32 slow_music_speed = 0x200;
119 // Don't adjust the volume if already on the lowest setting, otherwise it's not audible
120 u32 vol = g_game_vars.music_volume;
121 // Don't divide the audio by half if it's one, just sounds bad
122 // This works too, not for negative numbers. It's neat.
123 // vol |= !((vol - 1) > 0);
124 vol = (vol == 1) ? 1 : vol / 2;
125 u32 target_vol = volume_module_step_to_val(vol);
126
127 set_audio_param_req(&music_player.pitch, slow_music_speed, MUSIC_CHANGE_FRAMES);
128 set_audio_param_req(&music_player.tempo, slow_music_speed, MUSIC_CHANGE_FRAMES);
129 set_audio_param_req(&music_player.volume, target_vol, VOLUME_CHANGE_FRAMES);
130
131 state_machine_register(&song_speed_sm);
132 state_machine_change_state(&song_speed_sm, 0);
133}
134
136{
137 u32 target_vol = volume_module_step_to_val(g_game_vars.music_volume);
138 set_audio_param_req(&music_player.pitch, DEFAULT_PITCH, MUSIC_CHANGE_FRAMES);
139 set_audio_param_req(&music_player.tempo, DEFAULT_TEMPO, MUSIC_CHANGE_FRAMES);
140 set_audio_param_req(&music_player.volume, target_vol, VOLUME_CHANGE_FRAMES);
141
142 state_machine_register(&song_speed_sm);
143 state_machine_change_state(&song_speed_sm, 0);
144}
145
146void set_volume(int volume)
147{
148 music_player.volume.current = volume;
149 mmSetModuleVolume(volume);
150}
void play_regular_music(void)
Play music at a normal pitch and tempo.
void play_lose_music(void)
Play music at a low pitch and slow tempo (lose screen)
void play_sfx(mm_word id, mm_word rate, mm_byte volume)
Play a sound effect, wrapper for mmEffectEx() See https://maxmod.org/ref/functions/mm_sound_effect....
static bool audio_param_update(AudioParam *param)
Update the AudioParam for the music transition state machine for audio transitions.
Definition audio_utils.c:78
Utilities for using maxmod to play sound effects.
#define MM_MODULE_FULL_VOLUME
The maximum volume for maxmod module volume (main theme)
Definition audio_utils.h:17
int volume_module_step_to_val(unsigned char step)
Get MaxMod module audio value from VOLUME_OPTION value.
Definition audio_utils.h:85
#define SFX_DEFAULT_PAN
The default stereo pan, will always be center pan.
Definition audio_utils.h:49
Game global game variables struct definition.
Interface to interact with the mgba logger.
State Machine.
void state_machine_remove(StateMachine *state_machine)
Remove a statemachine's update function.
void state_machine_register(StateMachine *state_machine)
Register a statemachine to run it's update function once per frame.
void state_machine_change_state(StateMachine *state_machine, int new_state)
Calls the current state's on_exit, the new state's on_init, and sets the active update fn.
State machine callbacks.
State machine instance.
Utilities relating around number string representation and protected arithmatic helper functions.
#define SIGN(x)
Get the sign (signum) of an integer.
Definition util.h:35