Create-Engine/src/src/core/audio/AudioEngine.cpp

208 lines
5.3 KiB
C++

#include "AudioEngine.h"
#include <iostream>
// Static member definitions
ma_engine AudioEngine::s_Engine;
std::unordered_map<uint64_t, ma_sound*> AudioEngine::s_SoundMap;
std::unordered_map<uint64_t, PlayingInfo> AudioEngine::s_PlayingInfoMap;
std::vector<float> AudioEngine::s_MasterVU;
bool AudioEngine::Init() {
ma_engine_config config = ma_engine_config_init();
config.onProcess = AudioEngine::EngineProcessCallback;
config.pProcessUserData = nullptr;
ma_result r = ma_engine_init(&config, &s_Engine);
if (r != MA_SUCCESS) {
Logger::LogError("AudioEngine: ma_engine_init failed (%d)", r);
return false;
}
Logger::LogVerbose("AudioEngine initialized with master-output callback");
return true;
}
void AudioEngine::Shutdown() {
// Uninit & delete every sound
for (auto& kv : s_SoundMap) {
ma_sound_uninit(kv.second);
delete kv.second;
}
s_SoundMap.clear();
s_PlayingInfoMap.clear();
ma_engine_uninit(&s_Engine);
}
void AudioEngine::Play(uint64_t uaid, bool loop) {
// 1) Tear down existing instance (if any)
Cleanup(uaid);
// 2) Fetch and validate asset
const AssetInfo* asset = AssetManager::GetAssetByID(uaid);
if (!asset || !asset->loaded || asset->type != AssetType::Audio) {
Logger::LogError("AudioEngine::Play invalid asset %llu", uaid);
return;
}
// 3) Heap-allocate and init the sound from file
ma_sound* sound = new ma_sound;
if (ma_sound_init_from_file(
&s_Engine,
asset->path.c_str(),
/*flags=*/0,
/*pGroup=*/nullptr,
/*pFence=*/nullptr,
sound
) != MA_SUCCESS)
{
Logger::LogError("AudioEngine::Play failed to load '%s'", asset->path.c_str());
delete sound;
return;
}
// 4) Store and start playback
s_SoundMap[uaid] = sound;
ma_sound_set_looping(sound, loop ? MA_TRUE : MA_FALSE);
ma_sound_start(sound);
// 5) Compute totalTime
ma_uint64 frames = 0;
ma_sound_get_length_in_pcm_frames(sound, &frames);
double sampleRate = ma_engine_get_sample_rate(&s_Engine);
// 6) Push PlayingInfo
PlayingInfo info;
info.uaid = uaid;
info.name = asset->filename;
info.currentTime= 0.0;
info.totalTime = double(frames) / sampleRate;
info.looping = loop;
info.hasStarted = false;
info.sound = sound;
s_PlayingInfoMap[uaid] = info;
}
void AudioEngine::Stop(uint64_t uaid) {
if (auto it = s_SoundMap.find(uaid); it != s_SoundMap.end()) {
ma_sound_stop(it->second);
}
}
void AudioEngine::StopAll() {
for (auto& kv : s_SoundMap) {
ma_sound_stop(kv.second);
}
}
void AudioEngine::Unload(uint64_t uaid) {
Cleanup(uaid);
}
void AudioEngine::CleanupSound(uint64_t uaid)
{
auto itSound = s_SoundMap.find(uaid);
if (itSound != s_SoundMap.end()) {
ma_sound_uninit(itSound->second);
delete itSound->second;
s_SoundMap.erase(itSound);
}
}
void AudioEngine::Update()
{
for (auto it = s_PlayingInfoMap.begin(); it != s_PlayingInfoMap.end(); )
{
uint64_t uaid = it->first;
PlayingInfo &info = it->second;
ma_sound* s = info.sound;
bool shouldErase = (!s) ||
(!info.looping && info.hasStarted && !ma_sound_is_playing(s));
if (shouldErase) {
CleanupSound(uaid);
it = s_PlayingInfoMap.erase(it);
} else {
if (!info.hasStarted && ma_sound_is_playing(s)) {
info.hasStarted = true;
}
ma_uint64 cursor = 0;
if (ma_sound_get_cursor_in_pcm_frames(s, &cursor) == MA_SUCCESS) {
info.currentTime = double(cursor) / ma_engine_get_sample_rate(&s_Engine);
}
++it;
}
}
}
void AudioEngine::SetVolume(uint64_t uaid, float v) {
if (auto it = s_SoundMap.find(uaid); it != s_SoundMap.end()) {
ma_sound_set_volume(it->second, v);
}
}
void AudioEngine::SetLooping(uint64_t uaid, bool loop) {
if (auto it = s_SoundMap.find(uaid); it != s_SoundMap.end()) {
ma_sound_set_looping(it->second, loop ? MA_TRUE : MA_FALSE);
}
}
void AudioEngine::Cleanup(uint64_t uaid) {
if (auto it = s_SoundMap.find(uaid); it != s_SoundMap.end()) {
ma_sound_uninit(it->second);
delete it->second;
s_SoundMap.erase(it);
}
s_PlayingInfoMap.erase(uaid);
}
const std::unordered_map<uint64_t, PlayingInfo>& AudioEngine::GetPlayingInfoMap() {
return s_PlayingInfoMap;
}
void AudioEngine::EngineProcessCallback(
void* /*pUserData*/,
float* pFramesOut,
ma_uint64 frameCount)
{
// Fetch the playback device and its channel count
ma_device* pDevice = ma_engine_get_device(&s_Engine);
uint32_t channels = pDevice->playback.channels;
// Ensure our master VU vector matches the channel count
s_MasterVU.assign(channels, 0.0f);
// Compute peak per channel
for (ma_uint64 i = 0; i < frameCount; ++i) {
for (uint32_t ch = 0; ch < channels; ++ch) {
float v = fabsf(pFramesOut[i * channels + ch]);
if (v > s_MasterVU[ch]) {
s_MasterVU[ch] = v;
}
}
}
}
const std::vector<float>& AudioEngine::GetMasterVU()
{
return s_MasterVU;
}