From baf7ef85a34540d26b3385f650a80a52cbb2309d Mon Sep 17 00:00:00 2001 From: Luis Blanco Date: Thu, 29 Mar 2018 22:35:28 +0300 Subject: [PATCH] implemented OGG decoding from memory --- src/VorbisDecoder.cpp | 166 +++++++++++++++++++++++++++++++----------- 1 file changed, 124 insertions(+), 42 deletions(-) diff --git a/src/VorbisDecoder.cpp b/src/VorbisDecoder.cpp index bc30e90..16ce6a2 100644 --- a/src/VorbisDecoder.cpp +++ b/src/VorbisDecoder.cpp @@ -28,12 +28,31 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace nqr; -//@todo: implement decoding from memory (c.f. http://stackoverflow.com/questions/13437422/libvorbis-audio-decode-from-memory-in-c) class VorbisDecoderInternal { public: + VorbisDecoderInternal(AudioData * d, const std::vector & memory) : d(d) + { + void *data = const_cast(memory.data()); + + ogg_file t; + t.curPtr = t.filePtr = static_cast(data); + t.fileSize = memory.size(); + + fileHandle = new OggVorbis_File; + memset(fileHandle, 0, sizeof(OggVorbis_File)); + + ov_callbacks callbacks; + callbacks.read_func = AR_readOgg; + callbacks.seek_func = AR_seekOgg; + callbacks.close_func = AR_closeOgg; + callbacks.tell_func = AR_tellOgg; + + loadAudioData(&t, callbacks); + } + VorbisDecoderInternal(AudioData * d, std::string filepath) : d(d) { fileHandle = new OggVorbis_File(); @@ -53,46 +72,7 @@ public: if (!f) throw std::runtime_error("Can't open file"); - if (auto r = ov_test_callbacks(f, fileHandle, nullptr, 0, OV_CALLBACKS_DEFAULT) != 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"); + loadAudioData(f, OV_CALLBACKS_DEFAULT); } ~VorbisDecoderInternal() @@ -168,6 +148,13 @@ public: private: + struct ogg_file + { + char* curPtr; + char* filePtr; + size_t fileSize; + }; + NO_COPY(VorbisDecoderInternal); OggVorbis_File * fileHandle; @@ -177,6 +164,101 @@ private: inline int64_t getLengthInSeconds() const { return int64_t(ov_time_total(const_cast(fileHandle), -1)); } inline int64_t getCurrentSample() const { return int64_t(ov_pcm_tell(const_cast(fileHandle))); } + static size_t AR_readOgg(void* dst, size_t size1, size_t size2, void* fh) + { + ogg_file* of = reinterpret_cast(fh); + size_t len = size1 * size2; + if ( of->curPtr + len > of->filePtr + of->fileSize ) + { + len = of->filePtr + of->fileSize - of->curPtr; + } + memcpy( dst, of->curPtr, len ); + of->curPtr += len; + return len; + } + + static int AR_seekOgg( void *fh, ogg_int64_t to, int type ) { + ogg_file* of = reinterpret_cast(fh); + + switch( type ) { + case SEEK_CUR: + of->curPtr += to; + break; + case SEEK_END: + of->curPtr = of->filePtr + of->fileSize - to; + break; + case SEEK_SET: + of->curPtr = of->filePtr + to; + break; + default: + return -1; + } + if ( of->curPtr < of->filePtr ) { + of->curPtr = of->filePtr; + return -1; + } + if ( of->curPtr > of->filePtr + of->fileSize ) { + of->curPtr = of->filePtr + of->fileSize; + return -1; + } + return 0; + } + + static int AR_closeOgg(void* fh) + { + return 0; + } + + static long AR_tellOgg( void *fh ) + { + ogg_file* of = reinterpret_cast(fh); + return (of->curPtr - of->filePtr); + } + + void loadAudioData(void *source, ov_callbacks callbacks) + { + if (auto r = ov_test_callbacks(source, 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"); + } + }; ////////////////////// @@ -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()