199 lines
4.5 KiB
C++
199 lines
4.5 KiB
C++
#include "audio.hpp"
|
|
|
|
#include <SDL3/SDL_properties.h>
|
|
|
|
#include "../log.hpp"
|
|
|
|
#include <string>
|
|
#include <format>
|
|
#include <unordered_map>
|
|
|
|
using namespace game::util;
|
|
|
|
namespace game::resource
|
|
{
|
|
static std::shared_ptr<MIX_Audio> audio_make(MIX_Audio* audio)
|
|
{
|
|
return std::shared_ptr<MIX_Audio>(audio,
|
|
[](MIX_Audio* a)
|
|
{
|
|
if (a) MIX_DestroyAudio(a);
|
|
});
|
|
}
|
|
|
|
static std::unordered_map<std::string, std::weak_ptr<MIX_Audio>> audioCache{};
|
|
|
|
static std::shared_ptr<MIX_Audio> cache_get(const std::string& key)
|
|
{
|
|
auto it = audioCache.find(key);
|
|
if (it == audioCache.end()) return {};
|
|
|
|
auto cached = it->second.lock();
|
|
if (!cached) audioCache.erase(it);
|
|
return cached;
|
|
}
|
|
|
|
static void cache_set(const std::string& key, const std::shared_ptr<MIX_Audio>& audio)
|
|
{
|
|
if (!audio) return;
|
|
audioCache[key] = audio;
|
|
}
|
|
|
|
MIX_Mixer* Audio::mixer_get()
|
|
{
|
|
static auto mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, nullptr);
|
|
return mixer;
|
|
}
|
|
|
|
void Audio::volume_set(float volume)
|
|
{
|
|
auto mixer = mixer_get();
|
|
MIX_SetMasterGain(mixer, volume);
|
|
}
|
|
|
|
Audio::Audio(const std::filesystem::path& path)
|
|
{
|
|
auto pathString = path.string();
|
|
auto key = std::string("fs:") + pathString;
|
|
internal = cache_get(key);
|
|
if (internal)
|
|
{
|
|
logger.info(std::format("Using cached audio: {}", pathString));
|
|
return;
|
|
}
|
|
|
|
internal = audio_make(MIX_LoadAudio(mixer_get(), pathString.c_str(), true));
|
|
cache_set(key, internal);
|
|
if (internal) logger.info(std::format("Initialized audio: {}", pathString));
|
|
|
|
if (!internal) logger.info(std::format("Failed to intialize audio: {} ({})", pathString, SDL_GetError()));
|
|
}
|
|
|
|
Audio::Audio(const physfs::Path& path)
|
|
{
|
|
if (!path.is_valid())
|
|
{
|
|
logger.error(
|
|
std::format("Failed to initialize audio from PhysicsFS path: {}", path.c_str(), physfs::error_get()));
|
|
return;
|
|
}
|
|
|
|
auto key = std::string("physfs:") + path.c_str();
|
|
internal = cache_get(key);
|
|
if (internal)
|
|
{
|
|
logger.info(std::format("Using cached audio: {}", path.c_str()));
|
|
return;
|
|
}
|
|
|
|
auto buffer = path.read();
|
|
|
|
if (buffer.empty())
|
|
{
|
|
logger.error(
|
|
std::format("Failed to initialize audio from PhysicsFS path: {} ({})", path.c_str(), physfs::error_get()));
|
|
return;
|
|
}
|
|
|
|
auto ioStream = SDL_IOFromConstMem(buffer.data(), buffer.size());
|
|
|
|
internal = audio_make(MIX_LoadAudio_IO(mixer_get(), ioStream, false, true));
|
|
cache_set(key, internal);
|
|
if (internal)
|
|
logger.info(std::format("Initialized audio: {}", path.c_str()));
|
|
else
|
|
logger.info(std::format("Failed to intialize audio: {} ({})", path.c_str(), SDL_GetError()));
|
|
}
|
|
|
|
Audio::Audio(const Audio& other)
|
|
{
|
|
internal = other.internal;
|
|
track = nullptr;
|
|
}
|
|
|
|
Audio::Audio(Audio&& other) noexcept
|
|
{
|
|
internal = std::move(other.internal);
|
|
track = other.track;
|
|
|
|
other.track = nullptr;
|
|
}
|
|
|
|
Audio& Audio::operator=(const Audio& other)
|
|
{
|
|
if (this != &other)
|
|
{
|
|
unload();
|
|
internal = other.internal;
|
|
track = nullptr;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Audio& Audio::operator=(Audio&& other) noexcept
|
|
{
|
|
if (this != &other)
|
|
{
|
|
unload();
|
|
internal = std::move(other.internal);
|
|
track = other.track;
|
|
|
|
other.track = nullptr;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void Audio::unload()
|
|
{
|
|
if (track)
|
|
{
|
|
MIX_DestroyTrack(track);
|
|
track = nullptr;
|
|
}
|
|
internal.reset();
|
|
}
|
|
|
|
void Audio::play(bool isLoop)
|
|
{
|
|
if (!internal) return;
|
|
|
|
auto mixer = mixer_get();
|
|
|
|
if (track && MIX_GetTrackMixer(track) != mixer)
|
|
{
|
|
MIX_DestroyTrack(track);
|
|
track = nullptr;
|
|
}
|
|
|
|
if (!track)
|
|
{
|
|
track = MIX_CreateTrack(mixer);
|
|
if (!track) return;
|
|
}
|
|
|
|
MIX_SetTrackAudio(track, internal.get());
|
|
|
|
SDL_PropertiesID options = 0;
|
|
|
|
if (isLoop)
|
|
{
|
|
options = SDL_CreateProperties();
|
|
if (options) SDL_SetNumberProperty(options, MIX_PROP_PLAY_LOOPS_NUMBER, -1);
|
|
}
|
|
|
|
MIX_PlayTrack(track, options);
|
|
|
|
if (options) SDL_DestroyProperties(options);
|
|
}
|
|
|
|
void Audio::stop()
|
|
{
|
|
if (track) MIX_StopTrack(track, 0);
|
|
}
|
|
|
|
bool Audio::is_playing() const { return track && MIX_TrackPlaying(track); }
|
|
|
|
Audio::~Audio() { unload(); }
|
|
bool Audio::is_valid() const { return (bool)internal; }
|
|
}
|