1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "BsOggVorbisDecoder.h" |
4 | #include "FileSystem/BsDataStream.h" |
5 | #include <vorbis/codec.h> |
6 | |
7 | namespace bs |
8 | { |
9 | size_t oggRead(void* ptr, size_t size, size_t nmemb, void* data) |
10 | { |
11 | OggDecoderData* decoderData = static_cast<OggDecoderData*>(data); |
12 | return static_cast<std::size_t>(decoderData->stream->read(ptr, size * nmemb)); |
13 | } |
14 | |
15 | int oggSeek(void* data, ogg_int64_t offset, int whence) |
16 | { |
17 | OggDecoderData* decoderData = static_cast<OggDecoderData*>(data); |
18 | switch (whence) |
19 | { |
20 | case SEEK_SET: |
21 | offset += decoderData->offset; |
22 | break; |
23 | case SEEK_CUR: |
24 | offset += decoderData->stream->tell(); |
25 | break; |
26 | case SEEK_END: |
27 | offset = std::max(0, (INT32)decoderData->stream->size() - 1); |
28 | break; |
29 | } |
30 | |
31 | decoderData->stream->seek((UINT32)offset); |
32 | return (int)(decoderData->stream->tell() - decoderData->offset); |
33 | } |
34 | |
35 | long oggTell(void* data) |
36 | { |
37 | OggDecoderData* decoderData = static_cast<OggDecoderData*>(data); |
38 | return (long)(decoderData->stream->tell() - decoderData->offset); |
39 | } |
40 | |
41 | static ov_callbacks callbacks = { &oggRead, &oggSeek, nullptr, &oggTell }; |
42 | |
43 | OggVorbisDecoder::OggVorbisDecoder() |
44 | { |
45 | mOggVorbisFile.datasource = nullptr; |
46 | } |
47 | |
48 | OggVorbisDecoder::~OggVorbisDecoder() |
49 | { |
50 | if (mOggVorbisFile.datasource != nullptr) |
51 | ov_clear(&mOggVorbisFile); |
52 | } |
53 | |
54 | bool OggVorbisDecoder::isValid(const SPtr<DataStream>& stream, UINT32 offset) |
55 | { |
56 | stream->seek(offset); |
57 | mDecoderData.stream = stream; |
58 | mDecoderData.offset = offset; |
59 | |
60 | OggVorbis_File file; |
61 | if (ov_test_callbacks(&mDecoderData, &file, nullptr, 0, callbacks) == 0) |
62 | { |
63 | ov_clear(&file); |
64 | return true; |
65 | } |
66 | |
67 | return false; |
68 | } |
69 | |
70 | bool OggVorbisDecoder::open(const SPtr<DataStream>& stream, AudioDataInfo& info, UINT32 offset) |
71 | { |
72 | if (stream == nullptr) |
73 | return false; |
74 | |
75 | stream->seek(offset); |
76 | mDecoderData.stream = stream; |
77 | mDecoderData.offset = offset; |
78 | |
79 | int status = ov_open_callbacks(&mDecoderData, &mOggVorbisFile, nullptr, 0, callbacks); |
80 | if (status < 0) |
81 | { |
82 | LOGERR("Failed to open Ogg Vorbis file." ); |
83 | return false; |
84 | } |
85 | |
86 | vorbis_info* vorbisInfo = ov_info(&mOggVorbisFile, -1); |
87 | info.numChannels = vorbisInfo->channels; |
88 | info.sampleRate = vorbisInfo->rate; |
89 | info.numSamples = (UINT32)(ov_pcm_total(&mOggVorbisFile, -1) * vorbisInfo->channels); |
90 | info.bitDepth = 16; |
91 | |
92 | mChannelCount = info.numChannels; |
93 | return true; |
94 | } |
95 | |
96 | void OggVorbisDecoder::seek(UINT32 offset) |
97 | { |
98 | ov_pcm_seek(&mOggVorbisFile, offset / mChannelCount); |
99 | } |
100 | |
101 | UINT32 OggVorbisDecoder::read(UINT8* samples, UINT32 numSamples) |
102 | { |
103 | UINT32 numReadSamples = 0; |
104 | while (numReadSamples < numSamples) |
105 | { |
106 | INT32 bytesToRead = (INT32)(numSamples - numReadSamples) * sizeof(INT16); |
107 | UINT32 bytesRead = ov_read(&mOggVorbisFile, (char*)samples, bytesToRead, 0, 2, 1, nullptr); |
108 | if (bytesRead > 0) |
109 | { |
110 | UINT32 samplesRead = bytesRead / sizeof(INT16); |
111 | numReadSamples += samplesRead; |
112 | samples += samplesRead * sizeof(INT16); |
113 | } |
114 | else |
115 | break; |
116 | } |
117 | |
118 | return numReadSamples; |
119 | } |
120 | } |
121 | |