Add WAV support

Adds WAV support building off of PCMBuffer function

also robodeath 😺
This commit is contained in:
2025-04-20 20:55:16 +02:00
parent 065daa16fc
commit c0c1371823
5 changed files with 113 additions and 1 deletions

2
.gitignore vendored
View File

@@ -1 +1,3 @@
/build
/.vscode
*.log

View File

@@ -42,6 +42,13 @@ namespace ReMixer {
/// @see LoadOGGVorbisFile().
static Sound FromOGGVorbisFile(const std::filesystem::path &file_name);
/// Returns a Sound object loaded from a WAV file, usually with a .wav or .wave extension.
/// The sample rate and channel mode will be read from the WAV header.
/// @param file_name The full path relative to the executable in which the file is located.
/// @see https://en.wikipedia.org/wiki/WAV
/// @see LoadWAVFile().
static Sound FromWAVFile(const std::filesystem::path &file_name);
/// Returns a sound object loaded from a file containing raw PCM sound data in binary form.
/// @param file_name The full path relative to the executable in which the file is located.
/// @param sampleRate A uint that indicates the samples-per-second of the sound data.
@@ -87,6 +94,12 @@ namespace ReMixer {
/// @see FromOGGVorbisFile().
void LoadOGGVorbisFile(const std::filesystem::path &file_name);
/// Fills this sound instance with data from a file encoded with WAV, usually with a .wav or .wave extension.
/// @param file_name The full path relative to the executable in which the file is located.
/// @see https://en.wikipedia.org/wiki/WAV
/// @see FromWAVFile().
void LoadWAVFile(const std::filesystem::path &file_name);
/// Fills this sound instance with data from a file containing raw PCM sound data.
/// @param file_name The full path relative to the executable in which the file is located.
/// @param sampleRate A uint that indicates the samples-per-second of the sound data.

View File

@@ -39,7 +39,8 @@
//PulseStream test_stream2 = test.CreateStream("Another Test Stream");
//Sound test_sound = Sound::FromPCMFile("output.raw");
Sound test_sound = Sound::FromOGGVorbisFile("wind.ogg");
Sound test_sound = Sound::FromOGGVorbisFile("../test-sounds/wind.ogg");
Sound wav_sound = Sound::FromWAVFile("../test-sounds/robodeath.wav");
Sound sine_sound = Sound::FromSineWave(5.f, 0.0025f, 3000, 0);
Sound sawtooth_sound = Sound::FromSawtoothWave(5.f, 0.025f, 2000, 0);
@@ -53,6 +54,7 @@
server.Play(test_stream, square_sound);
server.Play(test_stream, noise_sound);
server.Play(test_stream, test_sound);
server.Play(test_stream, wav_sound);
while (true) {
std::this_thread::sleep_for(25ms);

View File

@@ -3,6 +3,7 @@
#include <vorbis/vorbisfile.h>
#include <fstream>
#include <format>
#include <cstring>
ReMixer::Sound ReMixer::Sound::FromOGGVorbisFile(const std::filesystem::path &file_name) {
@@ -11,6 +12,12 @@ ReMixer::Sound ReMixer::Sound::FromOGGVorbisFile(const std::filesystem::path &fi
return sound;
}
ReMixer::Sound ReMixer::Sound::FromWAVFile(const std::filesystem::path &file_name) {
Sound sound;
sound.LoadWAVFile(file_name);
return sound;
}
ReMixer::Sound ReMixer::Sound::FromPCMFile(const std::filesystem::path &file_name, uint sample_rate, StreamMode stream_mode) {
Sound sound;
sound.LoadPCMFile(file_name, sample_rate, stream_mode);
@@ -110,6 +117,94 @@ void ReMixer::Sound::LoadOGGVorbisFile(const std::filesystem::path &file_name) {
}
}
void ReMixer::Sound::LoadWAVFile(const std::filesystem::path &file_name) {
std::ifstream file(file_name, std::ios::binary);
if (!file.is_open()) {
throw std::runtime_error(std::format("Failed to open WAV file: {}", file_name.c_str()));
}
auto read_u16 = [&](std::istream &s) -> uint16_t {
uint8_t bytes[2];
s.read(reinterpret_cast<char*>(bytes), 2);
return bytes[0] | (bytes[1] << 8);
};
auto read_u32 = [&](std::istream &s) -> uint32_t {
uint8_t bytes[4];
s.read(reinterpret_cast<char*>(bytes), 4);
return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
};
char riff[4];
file.read(riff, 4);
if (strncmp(riff, "RIFF", 4) != 0) {
throw std::runtime_error("Invalid WAV file: missing RIFF header.");
}
file.ignore(4);
char wave[4];
file.read(wave, 4);
if (strncmp(wave, "WAVE", 4) != 0) {
throw std::runtime_error("Invalid WAV file: missing WAVE header.");
}
uint16_t num_channels = 0;
uint32_t sample_rate_ = 0;
uint16_t bits_per_sample = 0;
std::vector<char> raw_data;
while (!file.eof()) {
char chunk_id[4];
file.read(chunk_id, 4);
if (file.eof()) break;
uint32_t chunk_size = read_u32(file);
if (strncmp(chunk_id, "fmt ", 4) == 0) {
uint16_t audio_format = read_u16(file);
num_channels = read_u16(file);
sample_rate_ = read_u32(file);
file.ignore(6);
bits_per_sample = read_u16(file);
if (audio_format != 1 || bits_per_sample != 16) {
throw std::runtime_error("Unsupported WAV format: only PCM 16-bit is supported.");
}
if (chunk_size > 16) {
file.ignore(chunk_size - 16);
}
}
else if (strncmp(chunk_id, "data", 4) == 0) {
raw_data.resize(chunk_size);
file.read(raw_data.data(), chunk_size);
break;
}
else {
file.ignore(chunk_size);
}
}
if (raw_data.empty()) {
throw std::runtime_error("WAV file contains no audio data.");
}
StreamMode mode;
if (num_channels == 1) {
mode = StreamMode::MONO;
} else if (num_channels == 2) {
mode = StreamMode::STEREO;
} else {
throw std::runtime_error(std::format("Unsupported WAV Channel count: {}", num_channels));
}
std::cout << std::format("Loaded WAV file: {} Hz, {} channel(s), {} bits\n",
sample_rate_, num_channels, bits_per_sample);
LoadPCMBuffer(raw_data, sample_rate_, mode);
}
uint ReMixer::Sound::Channels() const {
if (stream_mode == StreamMode::MONO) return 1;
if (stream_mode == StreamMode::STEREO) return 2;

BIN
test-sounds/robodeath.wav Normal file

Binary file not shown.