BubbleBobbleRemake / DuvelEngine / SDLSoundSystem.cpp
SDLSoundSystem.cpp
Raw
#include "SDL_mixer.h"
#include "SDLSoundSystem.h"

SDLSoundSystem::SDLSoundSystem()
{
    if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) < 0) {
        std::cerr << "SDL_mixer could not initialize! SDL_mixer Error: " << Mix_GetError() << std::endl;
    }

    m_Thread = std::thread(&SDLSoundSystem::LoadingThread, this);
}

void SDLSoundSystem::LoadingThread()
{
    while (m_IsRunning)
    {
        Sound_Id soundToLoad{ USHRT_MAX };

        {
            std::unique_lock<std::mutex> lock(m_LoadQueueMutex);
            m_LoadConditionVariable.wait(lock, [this] { return !m_IsRunning || !m_LoadQueue.empty(); });

            if (!m_IsRunning)
                break;

            if (!m_LoadQueue.empty())
            {
                soundToLoad = m_LoadQueue.front().first;
                std::get<2>(m_Sounds[soundToLoad]) = SoundStatus::Loading;
                m_LoadQueue.pop();
            }
        }

        if (soundToLoad != USHRT_MAX)
        {
            Load(m_Sounds[soundToLoad]);
            std::get<2>(m_Sounds[soundToLoad]) = SoundStatus::Loaded;

            // Check and play if this sound is in the play-after-load queue
            if (std::find(m_PlayAfterLoadQueue.begin(), m_PlayAfterLoadQueue.end(), soundToLoad) != m_PlayAfterLoadQueue.end())
            {
                Play(soundToLoad, 1.0f);
                m_PlayAfterLoadQueue.remove(soundToLoad);
            }
        }
    }
}

SDLSoundSystem::~SDLSoundSystem()
{
    {
        std::lock_guard<std::mutex> lock(m_LoadQueueMutex);
        m_IsRunning = false;
    }
    m_LoadConditionVariable.notify_one();
    m_Thread.join();

    for (auto& sound : m_Sounds)
    {
        Mix_FreeChunk(std::get<1>(sound.second));
        std::get<1>(sound.second) = nullptr;
    }

    for (auto& music : m_Music)
    {
        Mix_FreeMusic(music.second.second);
        music.second.second = nullptr;
    }

    Mix_CloseAudio();
}

void SDLSoundSystem::RegisterMusic(const Sound_Id musicId, const std::string& filepath)
{
    m_Music[musicId].second = Mix_LoadMUS(filepath.c_str());
    if (m_Music[musicId].second == nullptr)
    {
        std::cerr << "Error loading music: " << Mix_GetError() << std::endl;
    }
}

void SDLSoundSystem::StopMusic()
{
    if (Mix_PlayingMusic() == 1)
    {
        Mix_HaltMusic();
    }
}

void SDLSoundSystem::PlayMusic(const Sound_Id musicId, const float volume, const int loops)
{
    if (m_Music.count(musicId) == 0)
    {
        std::cerr << "Music with ID " << musicId << " not registered." << std::endl;
        return;
    }

    if (Mix_PlayingMusic() == 1)
    {
        Mix_HaltMusic();
    }
    volume;
    if (Mix_PlayMusic(m_Music[musicId].second, loops) == -1)
    {
        std::cerr << "Error playing music: " << Mix_GetError() << std::endl;
        return;
    }
}

void SDLSoundSystem::Play(const Sound_Id soundId, const float volume)
{
    auto soundStatus = std::get<2>(m_Sounds[soundId]);

    switch (soundStatus)
    {
    case SoundStatus::Loaded:
    {
        //std::lock_guard<std::mutex> playLock(m_PlayMutex);
        Mix_VolumeChunk(std::get<1>(m_Sounds[soundId]), int(MIX_MAX_VOLUME * volume));
        if (Mix_PlayChannel(-1, std::get<1>(m_Sounds[soundId]), 0) == -1)
        {
            std::cerr << "Error playing sound: " << Mix_GetError() << std::endl;
        }
    }
    break;

    case SoundStatus::Unloaded:
    {
        std::unique_lock<std::mutex> lock(m_LoadQueueMutex);
        m_LoadQueue.emplace(soundId, volume);

        // Also add it to the play-after-load queue
        m_PlayAfterLoadQueue.push_back(soundId);

        lock.unlock();
        m_LoadConditionVariable.notify_one();
    }
    break;

    case SoundStatus::Loading:
        // Just ensure it will play once loaded
        m_PlayAfterLoadQueue.push_back(soundId);
        break;
    }
}

void SDLSoundSystem::Load(std::tuple<std::string, Mix_Chunk*, SoundStatus>& sound)
{
    std::get<1>(sound) = Mix_LoadWAV(std::get<0>(sound).c_str());
}

void SDLSoundSystem::RegisterSound(const Sound_Id soundId, const std::string& filepath)
{
    std::tuple<std::string, Mix_Chunk*, SoundStatus> soundData(filepath, nullptr, SoundStatus::Unloaded);
    m_Sounds[soundId] = soundData;
}