From e0f36dac15c8ded3d467ae52754776396c360e0a Mon Sep 17 00:00:00 2001 From: Avaer Kazmer Date: Thu, 1 Mar 2018 20:35:12 -0800 Subject: [PATCH] Add vorbis decoder callbacks support --- src/VorbisDecoder.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 2 deletions(-) diff --git a/src/VorbisDecoder.cpp b/src/VorbisDecoder.cpp index bc30e90..28c65e2 100644 --- a/src/VorbisDecoder.cpp +++ b/src/VorbisDecoder.cpp @@ -25,6 +25,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "VorbisDecoder.h" #include "libvorbis/include/vorbis/vorbisfile.h" +#include using namespace nqr; @@ -94,6 +95,64 @@ public: if (!readInternal(totalSamples)) throw std::runtime_error("could not read any data"); } + + VorbisDecoderInternal(AudioData * d, const std::vector & memory) : d(d) + { + fileHandle = new OggVorbis_File(); + + data = std::move(memory); + ov_callbacks callbacks = { + read_func, + seek_func, + close_func, + tell_func, + }; + if (auto r = ov_open_callbacks(this, fileHandle, nullptr, 0, callbacks) != 0) { + std::cerr << errorAsString(r) << std::endl; + throw std::runtime_error("Failed to open ogg vorbis file"); + } + + if (auto r = ov_test_callbacks(this, fileHandle, nullptr, 0, callbacks) != 0) + { + std::cerr << errorAsString(r) << std::endl; + throw std::runtime_error("File is not a valid ogg vorbis file"); + } + + if (auto r = ov_test_open(fileHandle) != 0) + { + std::cerr << errorAsString(r) << std::endl; + throw std::runtime_error("ov_test_open failed"); + } + + // Don't need to fclose() after an open -- vorbis does this internally + + vorbis_info *ovInfo = ov_info(fileHandle, -1); + + if (ovInfo == nullptr) + { + throw std::runtime_error("Reading metadata failed"); + } + + if (auto r = ov_streams(fileHandle) != 1) + { + std::cerr << errorAsString(r) << std::endl; + throw std::runtime_error( "Unsupported: file contains multiple bitstreams"); + } + + d->sampleRate = int(ovInfo->rate); + d->channelCount = ovInfo->channels; + d->sourceFormat = MakeFormatForBits(32, true, false); + d->lengthSeconds = double(getLengthInSeconds()); + d->frameSize = ovInfo->channels * GetFormatBitsPerSample(d->sourceFormat); + + // Samples in a single channel + auto totalSamples = size_t(getTotalSamples()); + + d->samples.resize(totalSamples * d->channelCount); + + if (!readInternal(totalSamples)) + throw std::runtime_error("could not read any data"); + } ~VorbisDecoderInternal() { @@ -163,7 +222,28 @@ public: ////////////////////// // vorbis callbacks // ////////////////////// - + + static size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { + VorbisDecoderInternal *decoder = (VorbisDecoderInternal *)datasource; + size_t readLength = std::min(size * nmemb, decoder->data.size() - decoder->dataPos); + if (readLength > 0) { + memcpy(ptr, decoder->data.data(), readLength); + decoder->dataPos += readLength; + } + return readLength; + } + static int seek_func(void *datasource, ogg_int64_t offset, int whence) { + VorbisDecoderInternal *decoder = (VorbisDecoderInternal *)datasource; + decoder->dataPos = std::min(offset, decoder->data.size()); + return 0; + } + static int close_func(void *datasource) { + // nothing + } + static long tell_func(void *datasource) { + VorbisDecoderInternal *decoder = (VorbisDecoderInternal *)datasource; + return decoder->dataPos; + } //@todo: implement streaming support private: @@ -172,6 +252,8 @@ private: OggVorbis_File * fileHandle; AudioData * d; + std::vector data; + size_t dataPos; inline int64_t getTotalSamples() const { return int64_t(ov_pcm_total(const_cast(fileHandle), -1)); } inline int64_t getLengthInSeconds() const { return int64_t(ov_time_total(const_cast(fileHandle), -1)); } @@ -190,7 +272,7 @@ void VorbisDecoder::LoadFromPath(AudioData * data, const std::string & path) void VorbisDecoder::LoadFromBuffer(AudioData * data, const std::vector & memory) { - throw LoadBufferNotImplEx(); + VorbisDecoderInternal decoder(data, memory); } std::vector VorbisDecoder::GetSupportedFileExtensions()