diff --git a/src/audio/audio.cpp b/src/audio/audio.cpp index b06043b2..5dc3cd58 100644 --- a/src/audio/audio.cpp +++ b/src/audio/audio.cpp @@ -121,7 +121,9 @@ Sound* audio::createSound(std::shared_ptr pcm, bool keepPCM) { PCMStream* audio::openPCMStream(const fs::path& file) { std::string ext = file.extension().u8string(); - if (ext == ".ogg" || ext == ".OGG") { + if (ext == ".wav" || ext == ".WAV") { + return wav::create_stream(file); + } else if (ext == ".ogg" || ext == ".OGG") { return ogg::create_stream(file); } throw std::runtime_error("unsupported audio stream format"); diff --git a/src/coders/wav.cpp b/src/coders/wav.cpp index 578ddb21..ad3c1599 100644 --- a/src/coders/wav.cpp +++ b/src/coders/wav.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "../audio/audio.h" @@ -27,7 +28,108 @@ std::int32_t convert_to_int(char* buffer, std::size_t len){ return a; } -audio::PCM* wav::load_pcm(const std::filesystem::path& file, bool headerOnly) { +/// @brief Seekable WAV-file PCM stream +class WavStream : public audio::PCMStream { + std::ifstream in; + uint channels; + uint bytesPerSample; + uint sampleRate; + size_t totalSize; + size_t totalSamples; + size_t initialPosition; +public: + WavStream( + std::ifstream in, + uint channels, + uint bitsPerSample, + uint sampleRate, + size_t size + ) : in(std::move(in)), + channels(channels), + bytesPerSample(bitsPerSample/8), + sampleRate(sampleRate), + totalSize(size) + { + totalSamples = totalSize / channels / bytesPerSample; + initialPosition = in.tellg(); + } + + size_t read(char* buffer, size_t bufferSize, bool loop) override { + if (!isOpen()) { + return 0; + } + long bytes = 0; + size_t size = 0; + do { + do { + in.read(buffer, bufferSize); + if (in.failbit) { + std::cerr << "Wav::load_pcm: I/O error ocurred" << std::endl; + continue; + } + bytes = in.gcount(); + size += bytes; + bufferSize -= bytes; + buffer += bytes; + } while (bytes > 0 && bufferSize > 0); + + if (bufferSize == 0) { + break; + } + + if (loop) { + seek(0); + } + if (bufferSize == 0) { + return size; + } + } while (loop); + return size; + } + + void close() override { + if (!isOpen()) + return; + in.close(); + } + + bool isOpen() const override { + return in.is_open(); + } + + size_t getTotalSamples() const override { + return totalSamples; + } + + audio::duration_t getTotalDuration() const override { + return totalSamples / static_cast(sampleRate); + } + + uint getChannels() const override { + return channels; + } + + uint getSampleRate() const override { + return sampleRate; + } + + uint getBitsPerSample() const override { + return bytesPerSample * 8; + } + + bool isSeekable() const override { + return true; + } + + void seek(size_t position) override { + if (!isOpen()) + return; + position %= totalSamples; + in.seekg(initialPosition + position * channels * bytesPerSample); + } +}; + +audio::PCMStream* wav::create_stream(const std::filesystem::path& file) { std::ifstream in(file, std::ios::binary); if(!in.is_open()){ throw std::runtime_error("could not to open file '"+file.u8string()+"'"); @@ -105,14 +207,24 @@ audio::PCM* wav::load_pcm(const std::filesystem::path& file, bool headerOnly) { if(in.fail()){ throw std::runtime_error("fail state set on the file"); } + return new WavStream(std::move(in), channels, bitsPerSample, sampleRate, size); +} + +audio::PCM* wav::load_pcm(const std::filesystem::path& file, bool headerOnly) { + std::unique_ptr stream(wav::create_stream(file)); + + size_t totalSamples = stream->getTotalSamples(); + uint channels = stream->getChannels(); + uint bitsPerSample = stream->getBitsPerSample(); + uint sampleRate = stream->getSampleRate(); std::vector data; - size_t totalSamples = size / (bitsPerSample / 8); if (!headerOnly) { + size_t size = stream->getTotalSamples() * + (stream->getBitsPerSample()/8) * + stream->getChannels(); data.resize(size); - if (!in.read(data.data(), size)) { - throw std::runtime_error("could not load wav data of '"+file.u8string()+"'"); - } + stream->read(data.data(), size, false); } return new audio::PCM(std::move(data), totalSamples, channels, bitsPerSample, sampleRate, true); } diff --git a/src/coders/wav.h b/src/coders/wav.h index 209d5c25..5a8a2095 100644 --- a/src/coders/wav.h +++ b/src/coders/wav.h @@ -5,10 +5,12 @@ namespace audio { struct PCM; + class PCMStream; } namespace wav { extern audio::PCM* load_pcm(const std::filesystem::path& file, bool headerOnly); + extern audio::PCMStream* create_stream(const std::filesystem::path& file); } #endif // CODERS_WAV_H_