Files
pong_raylib/src/audio_manager.odin
T

148 lines
4.4 KiB
Odin

package game
import rl "vendor:raylib"
Fade_State :: enum {
None,
In,
Out,
}
// setup sound IDs so gameplay code never
// touches raw Raylib sound handles
Sound_ID :: enum {
Ball_Collision,
Score_Goal,
UI_Select,
UI_Confirm,
}
Audio_Manager :: struct {
sounds: map[Sound_ID]rl.Sound,
music: rl.Music,
music_loaded: bool,
sfx_vol: f32, // User SFX volume setting
user_music_vol: f32, // User Music volume setting
current_music_vol: f32, // Live interpolated output volume
// Runtime Fade State
fade_state: Fade_State,
fade_elapsed_sec: f32,
fade_duration_sec: f32,
}
// ------------------------------------------------------------------------------
// Lifecycle procs
// ------------------------------------------------------------------------------
audio_init :: proc(audio: ^Audio_Manager) {
rl.InitAudioDevice()
audio.sounds = make(map[Sound_ID]rl.Sound)
// Load SFX
audio.sfx_vol = 0.5
audio.sounds[.UI_Select] = rl.LoadSound("assets/audio/ui/select.ogg")
audio.sounds[.Ball_Collision] = rl.LoadSound("assets/audio/gameplay/ballhit.ogg")
// Load Music Stream
audio.user_music_vol = 0.1
audio.current_music_vol = 0.0
audio.fade_state = .None
audio.music = rl.LoadMusicStream("assets/audio/gameplay/music/track_1.wav")
audio.music_loaded = true
rl.SetMusicVolume(audio.music, audio.current_music_vol)
}
audio_update :: proc(audio: ^Audio_Manager, dt: f32) {
if !audio.music_loaded do return
// Process Fade if the audio state machine requires
if audio.fade_state != .None {
audio.fade_elapsed_sec += dt
// convert elapsed seconds over target seconds into a 0.0 -> 1.0 percentage
progress_pct := clamp(audio.fade_elapsed_sec / audio.fade_duration_sec, 0.0, 1.0)
#partial switch audio.fade_state {
case .In:
audio.current_music_vol = rl.Lerp(0.0, audio.user_music_vol, progress_pct)
// progress is 100% or more stop fading in music
if progress_pct >= 1.0 do audio.fade_state = .None
case .Out:
audio.current_music_vol = rl.Lerp(audio.user_music_vol, 0.0, progress_pct)
// progress is 100% or more stop fading out music and stop the stream
if progress_pct >= 1.0 {
audio.fade_state = .None
rl.StopMusicStream(audio.music)
}
}
} else {
// not fading, output matches the user setting directly
audio.current_music_vol = audio.user_music_vol
}
rl.SetMusicVolume(audio.music, audio.current_music_vol)
// keep audio stream buffer fed
if rl.IsMusicStreamPlaying(audio.music) {
rl.UpdateMusicStream(audio.music)
}
}
audio_manager_quit :: proc(audio: ^Audio_Manager) {
for _, sound in audio.sounds {
rl.UnloadSound(sound)
}
delete(audio.sounds)
rl.UnloadMusicStream(audio.music)
rl.CloseAudioDevice()
}
// ------------------------------------------------------------------------------
// Utilities
// ------------------------------------------------------------------------------
audio_play_music :: proc(audio: ^Audio_Manager) {
if audio.music_loaded {
// Only trigger start if it isn't playing already
if !rl.IsMusicStreamPlaying(audio.music) {
rl.PlayMusicStream(audio.music)
}
}
}
audio_stop_music :: proc(audio: ^Audio_Manager) {
if audio.music_loaded && rl.IsMusicStreamPlaying(audio.music) {
rl.StopMusicStream(audio.music)
}
}
audio_play_sfx :: proc(audio: ^Audio_Manager, id: Sound_ID) {
if sound, ok := audio.sounds[id]; ok {
rl.SetSoundVolume(sound, audio.sfx_vol)
rl.PlaySound(sound)
}
}
// ------------------------------------------------------------------------------
// Audio Fading
// ------------------------------------------------------------------------------
audio_fade_in :: proc(audio: ^Audio_Manager, duration: f32 = 1.0) {
if !audio.music_loaded do return
if !rl.IsMusicStreamPlaying(audio.music) {
rl.PlayMusicStream(audio.music)
}
audio.fade_state = .In
audio.fade_duration_sec = max(0.001, duration)
audio.fade_elapsed_sec = 0.0
audio.current_music_vol = 0.0
rl.SetMusicVolume(audio.music, 0.0)
}
audio_fade_out :: proc(audio: ^Audio_Manager, duration: f32 = 1.0) {
if !audio.music_loaded || !rl.IsMusicStreamPlaying(audio.music) do return
audio.fade_state = .Out
audio.fade_duration_sec = max(0.001, duration)
audio.fade_elapsed_sec = 0.0
}