Add WAV support
Adds WAV support building off of PCMBuffer function
also robodeath 😺
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1,3 @@
|
|||||||
/build
|
/build
|
||||||
|
/.vscode
|
||||||
|
*.log
|
||||||
|
@@ -42,6 +42,13 @@ namespace ReMixer {
|
|||||||
/// @see LoadOGGVorbisFile().
|
/// @see LoadOGGVorbisFile().
|
||||||
static Sound FromOGGVorbisFile(const std::filesystem::path &file_name);
|
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.
|
/// 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 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.
|
/// @param sampleRate A uint that indicates the samples-per-second of the sound data.
|
||||||
@@ -87,6 +94,12 @@ namespace ReMixer {
|
|||||||
/// @see FromOGGVorbisFile().
|
/// @see FromOGGVorbisFile().
|
||||||
void LoadOGGVorbisFile(const std::filesystem::path &file_name);
|
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.
|
/// 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 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.
|
/// @param sampleRate A uint that indicates the samples-per-second of the sound data.
|
||||||
|
4
main.cpp
4
main.cpp
@@ -39,7 +39,8 @@
|
|||||||
//PulseStream test_stream2 = test.CreateStream("Another Test Stream");
|
//PulseStream test_stream2 = test.CreateStream("Another Test Stream");
|
||||||
|
|
||||||
//Sound test_sound = Sound::FromPCMFile("output.raw");
|
//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 sine_sound = Sound::FromSineWave(5.f, 0.0025f, 3000, 0);
|
||||||
Sound sawtooth_sound = Sound::FromSawtoothWave(5.f, 0.025f, 2000, 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, square_sound);
|
||||||
server.Play(test_stream, noise_sound);
|
server.Play(test_stream, noise_sound);
|
||||||
server.Play(test_stream, test_sound);
|
server.Play(test_stream, test_sound);
|
||||||
|
server.Play(test_stream, wav_sound);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
std::this_thread::sleep_for(25ms);
|
std::this_thread::sleep_for(25ms);
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
#include <vorbis/vorbisfile.h>
|
#include <vorbis/vorbisfile.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
|
||||||
ReMixer::Sound ReMixer::Sound::FromOGGVorbisFile(const std::filesystem::path &file_name) {
|
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;
|
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) {
|
ReMixer::Sound ReMixer::Sound::FromPCMFile(const std::filesystem::path &file_name, uint sample_rate, StreamMode stream_mode) {
|
||||||
Sound sound;
|
Sound sound;
|
||||||
sound.LoadPCMFile(file_name, sample_rate, stream_mode);
|
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 {
|
uint ReMixer::Sound::Channels() const {
|
||||||
if (stream_mode == StreamMode::MONO) return 1;
|
if (stream_mode == StreamMode::MONO) return 1;
|
||||||
if (stream_mode == StreamMode::STEREO) return 2;
|
if (stream_mode == StreamMode::STEREO) return 2;
|
||||||
|
BIN
test-sounds/robodeath.wav
Normal file
BIN
test-sounds/robodeath.wav
Normal file
Binary file not shown.
Reference in New Issue
Block a user