Skip to content

Commit

Permalink
🔨 Move audio asset loading to AssetManager, update audio engine APIs …
Browse files Browse the repository at this point in the history
…accordingly
  • Loading branch information
SeedyROM committed Dec 1, 2024
1 parent 760cad4 commit ce65fa8
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 94 deletions.
19 changes: 19 additions & 0 deletions src/engine/assets/asset_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ AssetHandle<T> AssetManager::load(const std::string &path) {
m_totalAssets--;
throw std::runtime_error(createInfo.errorMsg);
}
// Load AudioFile
} else if constexpr (std::is_same_v<T, AudioFile>) {
AudioFile::CreateInfo createInfo;
if (auto audioFile = AudioFile::createFromFile(path, createInfo)) {
auto asset = std::make_shared<AudioFile>(std::move(*audioFile));
m_assets.try_emplace(path, asset, 1);
m_loadedAssets++;
return AssetHandle<T>(asset);
} else {
m_totalAssets--;
throw std::runtime_error(createInfo.errorMsg);
}
} else {
// Asset type-specific loading logic here
throw std::runtime_error("Unsupported asset type");
Expand Down Expand Up @@ -143,4 +155,11 @@ AssetManager::loadAsync<Texture>(const std::string &);
template bool AssetManager::exists<Texture>(const std::string &) const;
template void AssetManager::remove<Texture>(const std::string &);

template AssetHandle<AudioFile>
AssetManager::load<AudioFile>(const std::string &);
template std::future<AssetHandle<AudioFile>>
AssetManager::loadAsync<AudioFile>(const std::string &);
template bool AssetManager::exists<AudioFile>(const std::string &) const;
template void AssetManager::remove<AudioFile>(const std::string &);

} // namespace ste
1 change: 1 addition & 0 deletions src/engine/assets/asset_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <vector>

#include "engine/async/thread_pool.h"
#include "engine/audio/audio_file.h"
#include "engine/rendering/shader.h"
#include "engine/rendering/texture.h"

Expand Down
54 changes: 27 additions & 27 deletions src/engine/audio/audio_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,11 @@ void AudioChannel::mix(float *buffer, size_t frames) {
m_position = static_cast<size_t>(readPosition);
}

void AudioChannel::play(std::shared_ptr<AudioFile> file, float vol) {
m_currentFile = std::move(file);
void AudioChannel::play(AudioFile *file, float vol) {
if (!file)
return;

m_currentFile = file;
m_position = 0;
m_volume = vol;
m_targetVolume = vol;
Expand All @@ -90,7 +93,7 @@ void AudioChannel::play(std::shared_ptr<AudioFile> file, float vol) {

void AudioChannel::stop() {
m_active = false;
m_currentFile.reset();
m_currentFile = nullptr;
m_position = 0;
}

Expand Down Expand Up @@ -217,37 +220,34 @@ AudioEngine::~AudioEngine() {
stopAll();
}

void AudioEngine::playSound(const std::string &path, float m_volume) {
try {
auto file = std::make_shared<AudioFile>(path);
int channel = findFreeChannel();
if (channel != -1) {
m_commandQueue.pushPlay(file, m_volume, channel);
}
} catch (const AudioFileException &e) {
std::cerr << "Failed to load sound file: " << e.what() << std::endl;
// Handle or propagate error
// Could log error or throw depending on your error handling strategy
void AudioEngine::playSound(AssetHandle<AudioFile> sound, float volume) {
if (!sound.isValid()) {
std::cerr << "Attempted to play invalid sound asset" << std::endl;
return;
}

int channel = findFreeChannel();
if (channel != -1) {
m_commandQueue.pushPlay(sound.operator->(), volume, channel);
}
}

void AudioEngine::playMusic(const std::string &path, bool loop) {
try {
auto file = std::make_shared<AudioFile>(path);
file->setLooping(loop);
void AudioEngine::playMusic(AssetHandle<AudioFile> music, bool loop) {
if (!music.isValid()) {
std::cerr << "Attempted to play invalid music asset" << std::endl;
return;
}

// Stop any currently playing music
stopChannel(0);
// Set looping state on the audio file
music->setLooping(loop);

// Queue the new music
m_commandQueue.pushPlay(file, 1.0f, 0); // Channel 0 reserved for music
} catch (const AudioFileException &e) {
std::cerr << "Failed to load sound file: " << e.what() << std::endl;
// Stop any currently playing music
stopChannel(0);

// Handle or propagate error
}
// Queue the new music
m_commandQueue.pushPlay(music.operator->(), 1.0f,
0); // Channel 0 reserved for music
}

void AudioEngine::stopChannel(int channelId) {
if (channelId >= 0 && channelId < static_cast<int>(MAX_CHANNELS)) {
m_commandQueue.pushStop(channelId);
Expand Down
9 changes: 5 additions & 4 deletions src/engine/audio/audio_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "audio_file.h"
#include "audio_queue.h"
#include "engine/assets/asset_manager.h"

namespace ste {

Expand All @@ -18,7 +19,7 @@ class AudioChannel {
// Core audio functions
void update(float deltaTime);
void mix(float *buffer, size_t frames);
void play(std::shared_ptr<AudioFile> file, float vol = 1.0f);
void play(AudioFile *file, float vol = 1.0f);
void stop();

// State control
Expand All @@ -36,7 +37,7 @@ class AudioChannel {

private:
// Audio data
std::shared_ptr<AudioFile> m_currentFile;
AudioFile *m_currentFile = nullptr;
size_t m_position = 0;

// Volume control
Expand Down Expand Up @@ -76,8 +77,8 @@ class AudioEngine {
AudioEngine &operator=(AudioEngine &&) = delete;

// Main interface
void playSound(const std::string &path, float volume = 1.0f);
void playMusic(const std::string &path, bool loop = true);
void playSound(AssetHandle<AudioFile> sound, float volume = 1.0f);
void playMusic(AssetHandle<AudioFile> music, bool loop = true);
void stopChannel(int channelId);
void stopAll();
void setChannelVolume(int channelId, float volume);
Expand Down
114 changes: 83 additions & 31 deletions src/engine/audio/audio_file.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// audio_file.cpp
#include "audio_file.h"

#include <algorithm>
#include <cstring>
#include <fstream>

#include <vorbis/vorbisfile.h>

namespace ste {
Expand All @@ -18,10 +18,10 @@ struct WAVHeader {
char fmt[4]; // "fmt "
uint32_t fmtSize; // Format chunk size
uint16_t audioFormat; // Audio format (1 = PCM)
uint16_t numChannels; // Number of m_channels
uint32_t m_sampleRate; // Sample rate
uint16_t numChannels; // Number of channels
uint32_t sampleRate; // Sample rate
uint32_t byteRate; // Bytes per second
uint16_t blockAlign; // Bytes per sample * m_channels
uint16_t blockAlign; // Bytes per sample * channels
uint16_t bitsPerSample; // Bits per sample
};
#pragma pack(pop)
Expand All @@ -33,21 +33,42 @@ bool fileExists(const std::string &path) {
}
} // namespace

AudioFile::AudioFile(const std::string &path) : m_filename(path) {
std::optional<AudioFile> AudioFile::createFromFile(const std::string &path,
CreateInfo &createInfo) {
if (!fileExists(path)) {
throw AudioFileException("File not found: " + path);
createInfo.success = false;
createInfo.errorMsg = "File not found: " + path;
return std::nullopt;
}

std::vector<float> samples;
uint32_t sampleRate;
uint32_t channels;
bool success = false;

std::string ext = getFileExtension(path);
if (ext == "wav") {
loadWAV(path);
success = loadWAV(path, samples, sampleRate, channels, createInfo);
} else if (ext == "ogg") {
loadOGG(path);
success = loadOGG(path, samples, sampleRate, channels, createInfo);
} else {
throw AudioFileException("Unsupported file format: " + ext);
createInfo.success = false;
createInfo.errorMsg = "Unsupported file format: " + ext;
return std::nullopt;
}

if (!success) {
return std::nullopt;
}

return AudioFile(path, std::move(samples), sampleRate, channels);
}

AudioFile::AudioFile(const std::string &filename, std::vector<float> &&samples,
uint32_t sampleRate, uint32_t channels)
: m_samples(std::move(samples)), m_filename(filename),
m_sampleRate(sampleRate), m_channels(channels) {}

AudioFile::~AudioFile() = default;

AudioFile::AudioFile(AudioFile &&other) noexcept
Expand All @@ -66,10 +87,14 @@ AudioFile &AudioFile::operator=(AudioFile &&other) noexcept {
return *this;
}

void AudioFile::loadWAV(const std::string &path) {
bool AudioFile::loadWAV(const std::string &path, std::vector<float> &samples,
uint32_t &sampleRate, uint32_t &channels,
CreateInfo &createInfo) {
std::ifstream file(path, std::ios::binary);
if (!file) {
throw AudioFileException("Failed to open WAV file: " + path);
createInfo.success = false;
createInfo.errorMsg = "Failed to open WAV file: " + path;
return false;
}

// Read and validate header
Expand All @@ -79,15 +104,21 @@ void AudioFile::loadWAV(const std::string &path) {
if (std::strncmp(header.riff, "RIFF", 4) != 0 ||
std::strncmp(header.wave, "WAVE", 4) != 0 ||
std::strncmp(header.fmt, "fmt ", 4) != 0) {
throw AudioFileException("Invalid WAV file format");
createInfo.success = false;
createInfo.errorMsg = "Invalid WAV file format";
return false;
}

if (header.audioFormat != 1) { // PCM = 1
throw AudioFileException("Unsupported WAV format: non-PCM");
createInfo.success = false;
createInfo.errorMsg = "Unsupported WAV format: non-PCM";
return false;
}

if (header.bitsPerSample != 16) {
throw AudioFileException("Unsupported WAV format: not 16-bit");
createInfo.success = false;
createInfo.errorMsg = "Unsupported WAV format: not 16-bit";
return false;
}

// Find data chunk
Expand All @@ -106,23 +137,35 @@ void AudioFile::loadWAV(const std::string &path) {
file.read(reinterpret_cast<char *>(pcmData.data()), chunkSize);

// Store audio properties
m_sampleRate = header.m_sampleRate;
m_channels = header.numChannels;
sampleRate = header.sampleRate;
channels = header.numChannels;

// Convert to float m_samples
convertToFloat(pcmData);
// Convert to float samples
convertToFloat(pcmData, samples);
return true;
}

void AudioFile::loadOGG(const std::string &path) {
bool AudioFile::loadOGG(const std::string &path, std::vector<float> &samples,
uint32_t &sampleRate, uint32_t &channels,
CreateInfo &createInfo) {
OggVorbis_File vf;
if (ov_fopen(path.c_str(), &vf) != 0) {
throw AudioFileException("Failed to open OGG file: " + path);
createInfo.success = false;
createInfo.errorMsg = "Failed to open OGG file: " + path;
return false;
}

// Get file info
vorbis_info *vi = ov_info(&vf, -1);
m_sampleRate = static_cast<uint32_t>(vi->rate);
m_channels = static_cast<uint32_t>(vi->channels);
if (!vi) {
createInfo.success = false;
createInfo.errorMsg = "Failed to get OGG file info";
ov_clear(&vf);
return false;
}

sampleRate = static_cast<uint32_t>(vi->rate);
channels = static_cast<uint32_t>(vi->channels);

// Read all PCM data
std::vector<int16_t> pcmData;
Expand All @@ -132,25 +175,34 @@ void AudioFile::loadOGG(const std::string &path) {

while ((bytesRead = ov_read(&vf, buffer, sizeof(buffer), 0, 2, 1,
&currentSection)) > 0) {
size_t m_samplesRead = bytesRead / sizeof(int16_t);
size_t samplesRead = bytesRead / sizeof(int16_t);
size_t currentSize = pcmData.size();
pcmData.resize(currentSize + m_samplesRead);
pcmData.resize(currentSize + samplesRead);
std::memcpy(pcmData.data() + currentSize, buffer, bytesRead);
}

if (bytesRead < 0) {
createInfo.success = false;
createInfo.errorMsg = "Error reading OGG file data";
ov_clear(&vf);
return false;
}

ov_clear(&vf);

// Convert to float m_samples
convertToFloat(pcmData);
// Convert to float samples
convertToFloat(pcmData, samples);
return true;
}

void AudioFile::convertToFloat(const std::vector<int16_t> &pcmData) {
m_samples.resize(pcmData.size());
void AudioFile::convertToFloat(const std::vector<int16_t> &pcmData,
std::vector<float> &samples) {
samples.resize(pcmData.size());
const float scale = 1.0f / 32768.0f; // Scale factor for 16-bit audio

// Convert m_samples from int16 to float
// Convert samples from int16 to float
for (size_t i = 0; i < pcmData.size(); ++i) {
m_samples[i] = static_cast<float>(pcmData[i]) * scale;
samples[i] = static_cast<float>(pcmData[i]) * scale;
}
}

Expand All @@ -164,4 +216,4 @@ std::string AudioFile::getFileExtension(const std::string &path) {
return "";
}

}; // namespace ste
} // namespace ste
Loading

0 comments on commit ce65fa8

Please sign in to comment.