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 "BsOAAudioClip.h" |
4 | #include "BsOggVorbisEncoder.h" |
5 | #include "BsOggVorbisDecoder.h" |
6 | #include "FileSystem/BsDataStream.h" |
7 | #include "BsOAAudio.h" |
8 | #include "AL/al.h" |
9 | |
10 | namespace bs |
11 | { |
12 | OAAudioClip::OAAudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc) |
13 | :AudioClip(samples, streamSize, numSamples, desc) |
14 | { } |
15 | |
16 | OAAudioClip::~OAAudioClip() |
17 | { |
18 | if (mBufferId != (UINT32)-1) |
19 | alDeleteBuffers(1, &mBufferId); |
20 | } |
21 | |
22 | void OAAudioClip::initialize() |
23 | { |
24 | { |
25 | Lock lock(mMutex); // Needs to be called even if stream data is null, to ensure memory fence is added so the |
26 | // other thread sees properly initialized AudioClip members |
27 | |
28 | AudioDataInfo info; |
29 | info.bitDepth = mDesc.bitDepth; |
30 | info.numChannels = mDesc.numChannels; |
31 | info.numSamples = mNumSamples; |
32 | info.sampleRate = mDesc.frequency; |
33 | |
34 | // If we need to keep source data, read everything into memory and keep a copy |
35 | if (mKeepSourceData) |
36 | { |
37 | mStreamData->seek(mStreamOffset); |
38 | |
39 | UINT8* sampleBuffer = (UINT8*)bs_alloc(mStreamSize); |
40 | mStreamData->read(sampleBuffer, mStreamSize); |
41 | |
42 | mSourceStreamData = bs_shared_ptr_new<MemoryDataStream>(sampleBuffer, mStreamSize); |
43 | mSourceStreamSize = mStreamSize; |
44 | } |
45 | |
46 | // Load decompressed data into a sound buffer |
47 | bool loadDecompressed = |
48 | mDesc.readMode == AudioReadMode::LoadDecompressed || |
49 | (mDesc.readMode == AudioReadMode::LoadCompressed && mDesc.format == AudioFormat::PCM); |
50 | |
51 | if(loadDecompressed) |
52 | { |
53 | // Read all data into memory |
54 | SPtr<DataStream> stream; |
55 | UINT32 offset = 0; |
56 | if (mSourceStreamData != nullptr) // If it's already loaded in memory, use it directly |
57 | stream = mSourceStreamData; |
58 | else |
59 | { |
60 | stream = mStreamData; |
61 | offset = mStreamOffset; |
62 | } |
63 | |
64 | UINT32 bufferSize = info.numSamples * (info.bitDepth / 8); |
65 | UINT8* sampleBuffer = (UINT8*)bs_stack_alloc(bufferSize); |
66 | |
67 | // Decompress from Ogg |
68 | if (mDesc.format == AudioFormat::VORBIS) |
69 | { |
70 | OggVorbisDecoder reader; |
71 | if (reader.open(stream, info, offset)) |
72 | reader.read(sampleBuffer, info.numSamples); |
73 | else |
74 | LOGERR("Failed decompressing AudioClip stream." ); |
75 | } |
76 | // Load directly |
77 | else |
78 | { |
79 | stream->seek(offset); |
80 | stream->read(sampleBuffer, bufferSize); |
81 | } |
82 | |
83 | alGenBuffers(1, &mBufferId); |
84 | gOAAudio()._writeToOpenALBuffer(mBufferId, sampleBuffer, info); |
85 | |
86 | mStreamData = nullptr; |
87 | mStreamOffset = 0; |
88 | mStreamSize = 0; |
89 | |
90 | bs_stack_free(sampleBuffer); |
91 | } |
92 | // Load compressed data for streaming from memory |
93 | else if(mDesc.readMode == AudioReadMode::LoadCompressed) |
94 | { |
95 | // If reading from file, make a copy of data in memory, otherwise just take ownership of the existing buffer |
96 | if (mStreamData->isFile()) |
97 | { |
98 | if (mSourceStreamData != nullptr) // If it's already loaded in memory, use it directly |
99 | mStreamData = mSourceStreamData; |
100 | else |
101 | { |
102 | UINT8* data = (UINT8*)bs_alloc(mStreamSize); |
103 | |
104 | mStreamData->seek(mStreamOffset); |
105 | mStreamData->read(data, mStreamSize); |
106 | |
107 | mStreamData = bs_shared_ptr_new<MemoryDataStream>(data, mStreamSize); |
108 | } |
109 | |
110 | mStreamOffset = 0; |
111 | } |
112 | } |
113 | // Keep original stream for streaming from file |
114 | else |
115 | { |
116 | // Do nothing |
117 | } |
118 | |
119 | if (mDesc.format == AudioFormat::VORBIS && mDesc.readMode != AudioReadMode::LoadDecompressed) |
120 | { |
121 | mNeedsDecompression = true; |
122 | |
123 | if (mStreamData != nullptr) |
124 | { |
125 | if (!mVorbisReader.open(mStreamData, info, mStreamOffset)) |
126 | LOGERR("Failed decompressing AudioClip stream." ); |
127 | } |
128 | } |
129 | } |
130 | |
131 | AudioClip::initialize(); |
132 | } |
133 | |
134 | void OAAudioClip::getSamples(UINT8* samples, UINT32 offset, UINT32 count) const |
135 | { |
136 | Lock lock(mMutex); |
137 | |
138 | // Try to read from normal stream, and if that fails read from in-memory stream if it exists |
139 | if (mStreamData != nullptr) |
140 | { |
141 | if (mNeedsDecompression) |
142 | { |
143 | mVorbisReader.seek(offset); |
144 | mVorbisReader.read(samples, count); |
145 | } |
146 | else |
147 | { |
148 | UINT32 bytesPerSample = mDesc.bitDepth / 8; |
149 | UINT32 size = count * bytesPerSample; |
150 | UINT32 streamOffset = mStreamOffset + offset * bytesPerSample; |
151 | |
152 | mStreamData->seek(streamOffset); |
153 | mStreamData->read(samples, size); |
154 | } |
155 | |
156 | return; |
157 | } |
158 | |
159 | if (mSourceStreamData != nullptr) |
160 | { |
161 | assert(!mNeedsDecompression); // Normal stream must exist if decompressing |
162 | |
163 | const UINT32 bytesPerSample = mDesc.bitDepth / 8; |
164 | UINT32 size = count * bytesPerSample; |
165 | UINT32 streamOffset = offset * bytesPerSample; |
166 | |
167 | mSourceStreamData->seek(streamOffset); |
168 | mSourceStreamData->read(samples, size); |
169 | return; |
170 | } |
171 | |
172 | LOGWRN("Attempting to read samples while sample data is not available." ); |
173 | } |
174 | |
175 | SPtr<DataStream> OAAudioClip::getSourceStream(UINT32& size) |
176 | { |
177 | Lock lock(mMutex); |
178 | |
179 | size = mSourceStreamSize; |
180 | mSourceStreamData->seek(0); |
181 | |
182 | return mSourceStreamData; |
183 | } |
184 | } |