Sim holds one whole game (tick, snapshots, checksum) and talks to the screen only through Cues, which the client's Scene plays and the server ignores. All drawing moved to scene.cpp. Match takes this screen's wishes as request flags instead of reading the menu, input and settings. The sim library builds without SDL and the server links it, ready to run games itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
171 lines
3.7 KiB
C++
171 lines
3.7 KiB
C++
#include "audio.h"
|
|
#include "renderer.h"
|
|
#include <SDL3/SDL.h>
|
|
|
|
#include <stb_vorbis.c>
|
|
|
|
constexpr float PITCH_VARIANCE = 0.06f;
|
|
constexpr Uint64 SOUND_GAP = 40 * SDL_NS_PER_MS;
|
|
constexpr const char* SOUND_NAMES[SOUND_COUNT] = {
|
|
"beam",
|
|
"blast",
|
|
"boomerang",
|
|
"boomerang_heavy",
|
|
"bounce",
|
|
"bow",
|
|
"bow_heavy",
|
|
"break",
|
|
"cast",
|
|
"charge",
|
|
"chat",
|
|
"crawl",
|
|
"dash",
|
|
"death",
|
|
"detonate",
|
|
"detonate_empty",
|
|
"duck",
|
|
"echo",
|
|
"equip",
|
|
"explosion",
|
|
"fall",
|
|
"finish",
|
|
"fireball",
|
|
"flail",
|
|
"flail_heavy",
|
|
"flail_land",
|
|
"flame",
|
|
"grapple",
|
|
"grapple_heavy",
|
|
"hit_blunt",
|
|
"hit_blunt_heavy",
|
|
"hit_bullet",
|
|
"hit_bullet_heavy",
|
|
"hit_energy",
|
|
"hit_energy_heavy",
|
|
"hit_explosive",
|
|
"hit_explosive_heavy",
|
|
"hit_fire",
|
|
"hit_fire_heavy",
|
|
"hit_lash",
|
|
"hit_lash_heavy",
|
|
"hit_magic",
|
|
"hit_magic_heavy",
|
|
"hit_pierce",
|
|
"hit_pierce_heavy",
|
|
"hit_slash",
|
|
"hit_slash_heavy",
|
|
"impact_arrow",
|
|
"impact_blaster",
|
|
"impact_boomerang",
|
|
"impact_bullet",
|
|
"impact_mine",
|
|
"jump",
|
|
"land",
|
|
"latch",
|
|
"level_down",
|
|
"level_up",
|
|
"mine",
|
|
"poke",
|
|
"poke_heavy",
|
|
"pound",
|
|
"punch",
|
|
"ready",
|
|
"ricochet",
|
|
"rocket",
|
|
"rocket_heavy",
|
|
"roll",
|
|
"select",
|
|
"shotgun",
|
|
"shotgun_heavy",
|
|
"slam",
|
|
"slash",
|
|
"slash_heavy",
|
|
"step",
|
|
"swap",
|
|
"swing",
|
|
"swing_heavy",
|
|
"teleport",
|
|
"teleport_warp",
|
|
"unduck",
|
|
"unequip",
|
|
"uppercut",
|
|
"uzi",
|
|
"uzi_heavy",
|
|
"whip",
|
|
"whip_heavy",
|
|
"wisp",
|
|
"zap",
|
|
};
|
|
|
|
void Audio::init()
|
|
{
|
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO))
|
|
device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, nullptr);
|
|
if (!device)
|
|
{
|
|
SDL_Log("Failed to open audio: %s", SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < SOUND_COUNT; i++)
|
|
{
|
|
std::string path = path_get("sfx/") + SOUND_NAMES[i] + ".ogg";
|
|
Sound& sound = sounds[i];
|
|
sound.frames = stb_vorbis_decode_filename(path.c_str(), &sound.channels, &sound.rate, &sound.samples);
|
|
if (sound.frames <= 0)
|
|
SDL_Log("Failed to load %s", path.c_str());
|
|
}
|
|
|
|
SDL_AudioSpec deviceSpec = {};
|
|
SDL_GetAudioDeviceFormat(device, &deviceSpec, nullptr);
|
|
for (SDL_AudioStream*& voice : voices)
|
|
{
|
|
voice = SDL_CreateAudioStream(&deviceSpec, &deviceSpec);
|
|
SDL_BindAudioStream(device, voice);
|
|
}
|
|
}
|
|
|
|
// Plays a sound with a slight random pitch and returns the voice used, or -1 if it was skipped.
|
|
int Audio::play(SoundId id)
|
|
{
|
|
if (id == SOUND_NONE)
|
|
return -1;
|
|
Sound& sound = sounds[id];
|
|
Uint64 now = SDL_GetTicksNS();
|
|
if (!device || sound.frames <= 0 || now - sound.played < SOUND_GAP)
|
|
return -1;
|
|
sound.played = now;
|
|
|
|
int voice = voiceNext;
|
|
for (int i = 0; i < VOICE_COUNT; i++)
|
|
{
|
|
if (SDL_GetAudioStreamQueued(voices[i]) == 0)
|
|
{
|
|
voice = i;
|
|
break;
|
|
}
|
|
}
|
|
voiceNext = (voice + 1) % VOICE_COUNT;
|
|
|
|
SDL_AudioStream* stream = voices[voice];
|
|
SDL_AudioSpec spec = {SDL_AUDIO_S16, sound.channels, sound.rate};
|
|
SDL_ClearAudioStream(stream);
|
|
SDL_SetAudioStreamFormat(stream, &spec, nullptr);
|
|
SDL_SetAudioStreamFrequencyRatio(stream, random_get(1 - PITCH_VARIANCE, 1 + PITCH_VARIANCE));
|
|
SDL_PutAudioStreamData(stream, sound.samples, sound.frames * sound.channels * sizeof(short));
|
|
return voice;
|
|
}
|
|
|
|
// Sets the volume of everything played, from silent at 0 to full at 1.
|
|
void Audio::gain_set(float gain)
|
|
{
|
|
if (device)
|
|
SDL_SetAudioDeviceGain(device, gain);
|
|
}
|
|
|
|
void Audio::stop(int voice)
|
|
{
|
|
if (voice >= 0)
|
|
SDL_ClearAudioStream(voices[voice]);
|
|
}
|