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
10namespace 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}