91 lines
3.3 KiB
C++
91 lines
3.3 KiB
C++
// Audio.h - MP3 playlist playback (Windows Media Foundation backend) + SDL3 stream
|
|
#pragma once
|
|
#include <SDL3/SDL.h>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <random>
|
|
#include <cstdint>
|
|
#include <thread>
|
|
#include <mutex>
|
|
#include <atomic>
|
|
#include "../core/interfaces/IAudioSystem.h"
|
|
|
|
struct AudioTrack {
|
|
std::string path;
|
|
std::vector<int16_t> pcm;
|
|
int channels = 2;
|
|
int rate = 44100;
|
|
size_t cursor = 0;
|
|
bool ok = false;
|
|
};
|
|
|
|
class Audio : public IAudioSystem {
|
|
public:
|
|
static Audio& instance();
|
|
|
|
// IAudioSystem interface implementation
|
|
void playSound(const std::string& name) override;
|
|
void playMusic(const std::string& name) override;
|
|
void stopMusic() override;
|
|
void setMasterVolume(float volume) override;
|
|
void setMusicVolume(float volume) override;
|
|
void setSoundVolume(float volume) override;
|
|
bool isMusicPlaying() const override;
|
|
|
|
// Existing Audio class methods
|
|
bool init(); // initialize backend (MF on Windows)
|
|
void addTrack(const std::string& path); // decode MP3 -> PCM16 stereo 44100
|
|
void addTrackAsync(const std::string& path); // add track for background loading
|
|
void startBackgroundLoading(); // start background thread for loading
|
|
void waitForLoadingComplete(); // wait for all tracks to finish loading
|
|
bool isLoadingComplete() const; // check if background loading is done
|
|
int getLoadedTrackCount() const; // get number of tracks loaded so far
|
|
void shuffle(); // randomize order
|
|
void start(); // begin playback
|
|
void skipToNextTrack(); // advance to the next music track
|
|
void toggleMute();
|
|
void setMuted(bool m);
|
|
bool isMuted() const { return muted; }
|
|
|
|
// Menu music support
|
|
void setMenuTrack(const std::string& path);
|
|
void playMenuMusic();
|
|
void playGameMusic();
|
|
|
|
// Queue a sound effect to mix over the music (pcm can be mono/stereo, any rate; will be converted)
|
|
void playSfx(const std::vector<int16_t>& pcm, int channels, int rate, float volume);
|
|
void shutdown();
|
|
private:
|
|
Audio()=default; ~Audio()=default; Audio(const Audio&)=delete; Audio& operator=(const Audio&)=delete;
|
|
static void SDLCALL streamCallback(void* userdata, SDL_AudioStream* stream, int additional, int total);
|
|
void feed(Uint32 bytesWanted, SDL_AudioStream* stream);
|
|
void nextTrack();
|
|
bool ensureStream();
|
|
void backgroundLoadingThread(); // background thread function
|
|
|
|
std::vector<AudioTrack> tracks;
|
|
AudioTrack menuTrack;
|
|
bool isMenuMusic = false;
|
|
int current=-1; bool playing=false; bool muted=false; std::mt19937 rng{std::random_device{}()};
|
|
SDL_AudioStream* audioStream=nullptr; SDL_AudioSpec outSpec{}; int outChannels=2; int outRate=44100; bool mfStarted=false;
|
|
|
|
// Threading support
|
|
std::vector<std::string> pendingTracks;
|
|
std::thread loadingThread;
|
|
std::mutex tracksMutex;
|
|
std::mutex pendingTracksMutex;
|
|
std::atomic<bool> loadingComplete{false};
|
|
std::atomic<bool> loadingAbort{false};
|
|
std::atomic<int> loadedCount{0};
|
|
|
|
// SFX mixing support
|
|
struct SfxPlay { std::vector<int16_t> pcm; size_t cursor=0; };
|
|
std::vector<SfxPlay> activeSfx;
|
|
std::mutex sfxMutex;
|
|
|
|
// Volume control
|
|
float m_masterVolume = 1.0f;
|
|
float m_musicVolume = 1.0f;
|
|
float m_sfxVolume = 1.0f;
|
|
};
|