Use pImpl for Buffers
[alure.git] / src / buffer.cpp
blob283c9a70496b25c8e51e0cd05edf93997e9b37a6
2 #include "config.h"
4 #include "buffer.h"
6 #include <stdexcept>
7 #include <sstream>
8 #include <cstring>
10 #include "al.h"
11 #include "alext.h"
13 #include "context.h"
15 namespace alure
18 void ALBuffer::cleanup()
20 while(!mIsLoaded.load(std::memory_order_acquire))
21 std::this_thread::yield();
22 if(isInUse())
23 throw std::runtime_error("Buffer is in use");
25 alGetError();
26 alDeleteBuffers(1, &mId);
27 if(alGetError() != AL_NO_ERROR)
28 throw std::runtime_error("Buffer failed to delete");
29 mId = 0;
33 void ALBuffer::load(ALuint frames, ALenum format, SharedPtr<Decoder> decoder, const String &name, ALContext *ctx)
35 Vector<ALbyte> data(FramesToBytes(frames, mChannelConfig, mSampleType));
37 ALuint got = decoder->read(data.data(), frames);
38 if(got > 0)
40 frames = got;
41 data.resize(FramesToBytes(frames, mChannelConfig, mSampleType));
43 else
45 ALbyte silence = 0;
46 if(mSampleType == SampleType::UInt8) silence = 0x80;
47 else if(mSampleType == SampleType::Mulaw) silence = 0x7f;
48 std::fill(data.begin(), data.end(), silence);
51 std::pair<uint64_t,uint64_t> loop_pts = decoder->getLoopPoints();
52 if(loop_pts.first >= loop_pts.second)
53 loop_pts = std::make_pair(0, frames);
54 else
56 loop_pts.second = std::min<uint64_t>(loop_pts.second, frames);
57 loop_pts.first = std::min<uint64_t>(loop_pts.first, loop_pts.second-1);
60 ctx->send(&MessageHandler::bufferLoading,
61 name, mChannelConfig, mSampleType, mFrequency, data
64 alBufferData(mId, format, data.data(), data.size(), mFrequency);
65 if(ctx->hasExtension(SOFT_loop_points))
67 ALint pts[2]{(ALint)loop_pts.first, (ALint)loop_pts.second};
68 alBufferiv(mId, AL_LOOP_POINTS_SOFT, pts);
71 mIsLoaded.store(true, std::memory_order_release);
75 ALuint ALBuffer::getLength() const
77 CheckContext(mContext);
78 if(mLoadStatus != BufferLoadStatus::Ready)
79 throw std::runtime_error("Buffer not loaded");
81 ALint size=-1, bits=-1, chans=-1;
82 alGetBufferi(mId, AL_SIZE, &size);
83 alGetBufferi(mId, AL_BITS, &bits);
84 alGetBufferi(mId, AL_CHANNELS, &chans);
85 if(size < 0 || bits < 0 || chans < 0)
86 throw std::runtime_error("Buffer format error");
87 return size / chans * 8 / bits;
90 ALuint ALBuffer::getSize() const
92 CheckContext(mContext);
93 if(mLoadStatus != BufferLoadStatus::Ready)
94 throw std::runtime_error("Buffer not loaded");
96 ALint size = -1;
97 alGetBufferi(mId, AL_SIZE, &size);
98 if(size < 0)
99 throw std::runtime_error("Buffer size error");
100 return size;
103 void ALBuffer::setLoopPoints(ALuint start, ALuint end)
105 ALuint length = getLength();
107 if(isInUse())
108 throw std::runtime_error("Buffer is in use");
110 if(!mContext->hasExtension(SOFT_loop_points))
112 if(start != 0 || end != length)
113 throw std::runtime_error("Loop points not supported");
114 return;
117 if(start >= end || end > length)
118 throw std::runtime_error("Loop points out of range");
120 ALint pts[2]{(ALint)start, (ALint)end};
121 alGetError();
122 alBufferiv(mId, AL_LOOP_POINTS_SOFT, pts);
123 if(alGetError() != AL_NO_ERROR)
124 throw std::runtime_error("Failed to set loop points");
127 std::pair<ALuint,ALuint> ALBuffer::getLoopPoints() const
129 CheckContext(mContext);
130 if(mLoadStatus != BufferLoadStatus::Ready)
131 throw std::runtime_error("Buffer not loaded");
133 if(!mContext->hasExtension(SOFT_loop_points))
134 return std::make_pair(0, getLength());
136 ALint pts[2]{-1,-1};
137 alGetBufferiv(mId, AL_LOOP_POINTS_SOFT, pts);
138 if(pts[0] == -1 || pts[1] == -1)
139 throw std::runtime_error("Failed to get loop points");
141 return std::make_pair(pts[0], pts[1]);
145 BufferLoadStatus ALBuffer::getLoadStatus()
147 /* NOTE: LoadStatus is separate from IsLoaded to force the app to receive
148 * acknowledgement that the buffer is ready before using it. Otherwise, if
149 * the app decides to simply use it after a short wait there's no guarantee
150 * it'll be ready in a consistent manner. It may work some times and fail
151 * others.
153 if(mLoadStatus == BufferLoadStatus::Pending && mIsLoaded.load(std::memory_order_acquire))
154 mLoadStatus = BufferLoadStatus::Ready;
155 return mLoadStatus;
158 using ALuintPair = std::pair<ALuint,ALuint>;
159 DECL_THUNK0(ALuint, Buffer, getLength, const)
160 DECL_THUNK0(ALuint, Buffer, getFrequency, const)
161 DECL_THUNK0(ChannelConfig, Buffer, getChannelConfig, const)
162 DECL_THUNK0(SampleType, Buffer, getSampleType, const)
163 DECL_THUNK0(ALuint, Buffer, getSize, const)
164 DECL_THUNK2(void, Buffer, setLoopPoints,, ALuint, ALuint)
165 DECL_THUNK0(ALuintPair, Buffer, getLoopPoints, const)
166 DECL_THUNK0(Vector<Source*>, Buffer, getSources, const)
167 DECL_THUNK0(BufferLoadStatus, Buffer, getLoadStatus,)
168 DECL_THUNK0(const String&, Buffer, getName, const)
169 DECL_THUNK0(bool, Buffer, isInUse, const)
172 ALURE_API const char *GetSampleTypeName(SampleType type)
174 switch(type)
176 case SampleType::UInt8: return "Unsigned 8-bit";
177 case SampleType::Int16: return "Signed 16-bit";
178 case SampleType::Float32: return "32-bit float";
179 case SampleType::Mulaw: return "Mulaw";
181 throw std::runtime_error("Invalid type");
184 ALURE_API const char *GetChannelConfigName(ChannelConfig cfg)
186 switch(cfg)
188 case ChannelConfig::Mono: return "Mono";
189 case ChannelConfig::Stereo: return "Stereo";
190 case ChannelConfig::Rear: return "Rear";
191 case ChannelConfig::Quad: return "Quadrophonic";
192 case ChannelConfig::X51: return "5.1 Surround";
193 case ChannelConfig::X61: return "6.1 Surround";
194 case ChannelConfig::X71: return "7.1 Surround";
195 case ChannelConfig::BFormat2D: return "B-Format 2D";
196 case ChannelConfig::BFormat3D: return "B-Format 3D";
198 throw std::runtime_error("Invalid config");
202 ALURE_API ALuint FramesToBytes(ALuint frames, ChannelConfig chans, SampleType type)
204 ALuint size = frames;
205 switch(chans)
207 case ChannelConfig::Mono: size *= 1; break;
208 case ChannelConfig::Stereo: size *= 2; break;
209 case ChannelConfig::Rear: size *= 2; break;
210 case ChannelConfig::Quad: size *= 4; break;
211 case ChannelConfig::X51: size *= 6; break;
212 case ChannelConfig::X61: size *= 7; break;
213 case ChannelConfig::X71: size *= 8; break;
214 case ChannelConfig::BFormat2D: size *= 3; break;
215 case ChannelConfig::BFormat3D: size *= 4; break;
217 switch(type)
219 case SampleType::UInt8: size *= 1; break;
220 case SampleType::Int16: size *= 2; break;
221 case SampleType::Float32: size *= 4; break;
222 case SampleType::Mulaw: size *= 1; break;
225 return size;
228 ALURE_API ALuint BytesToFrames(ALuint bytes, ChannelConfig chans, SampleType type)
230 return bytes / FramesToBytes(1, chans, type);
234 ALenum GetFormat(ChannelConfig chans, SampleType type)
236 #define RETURN_FMT(x) do { \
237 ALenum fmt = alGetEnumValue(x); \
238 if(fmt != AL_NONE && fmt != -1) \
239 return fmt; \
240 } while(0)
241 if(type == SampleType::UInt8)
243 if(chans == ChannelConfig::Mono) return AL_FORMAT_MONO8;
244 if(chans == ChannelConfig::Stereo) return AL_FORMAT_STEREO8;
245 if(ALContext::GetCurrent()->hasExtension(EXT_MCFORMATS))
247 if(chans == ChannelConfig::Rear) RETURN_FMT("AL_FORMAT_REAR8");
248 if(chans == ChannelConfig::Quad) RETURN_FMT("AL_FORMAT_QUAD8");
249 if(chans == ChannelConfig::X51) RETURN_FMT("AL_FORMAT_51CHN8");
250 if(chans == ChannelConfig::X61) RETURN_FMT("AL_FORMAT_61CHN8");
251 if(chans == ChannelConfig::X71) RETURN_FMT("AL_FORMAT_71CHN8");
253 if(ALContext::GetCurrent()->hasExtension(EXT_BFORMAT))
255 if(chans == ChannelConfig::BFormat2D) RETURN_FMT("AL_FORMAT_BFORMAT2D_8");
256 if(chans == ChannelConfig::BFormat3D) RETURN_FMT("AL_FORMAT_BFORMAT3D_8");
259 else if(type == SampleType::Int16)
261 if(chans == ChannelConfig::Mono) return AL_FORMAT_MONO16;
262 if(chans == ChannelConfig::Stereo) return AL_FORMAT_STEREO16;
263 if(ALContext::GetCurrent()->hasExtension(EXT_MCFORMATS))
265 if(chans == ChannelConfig::Rear) RETURN_FMT("AL_FORMAT_REAR16");
266 if(chans == ChannelConfig::Quad) RETURN_FMT("AL_FORMAT_QUAD16");
267 if(chans == ChannelConfig::X51) RETURN_FMT("AL_FORMAT_51CHN16");
268 if(chans == ChannelConfig::X61) RETURN_FMT("AL_FORMAT_61CHN16");
269 if(chans == ChannelConfig::X71) RETURN_FMT("AL_FORMAT_71CHN16");
271 if(ALContext::GetCurrent()->hasExtension(EXT_BFORMAT))
273 if(chans == ChannelConfig::BFormat2D) RETURN_FMT("AL_FORMAT_BFORMAT2D_16");
274 if(chans == ChannelConfig::BFormat3D) RETURN_FMT("AL_FORMAT_BFORMAT3D_16");
277 else if(type == SampleType::Float32 && ALContext::GetCurrent()->hasExtension(EXT_FLOAT32))
279 if(chans == ChannelConfig::Mono) return AL_FORMAT_MONO_FLOAT32;
280 if(chans == ChannelConfig::Stereo) return AL_FORMAT_STEREO_FLOAT32;
281 if(ALContext::GetCurrent()->hasExtension(EXT_MCFORMATS))
283 if(chans == ChannelConfig::Rear) RETURN_FMT("AL_FORMAT_REAR32");
284 if(chans == ChannelConfig::Quad) RETURN_FMT("AL_FORMAT_QUAD32");
285 if(chans == ChannelConfig::X51) RETURN_FMT("AL_FORMAT_51CHN32");
286 if(chans == ChannelConfig::X61) RETURN_FMT("AL_FORMAT_61CHN32");
287 if(chans == ChannelConfig::X71) RETURN_FMT("AL_FORMAT_71CHN32");
289 if(ALContext::GetCurrent()->hasExtension(EXT_BFORMAT))
291 if(chans == ChannelConfig::BFormat2D) RETURN_FMT("AL_FORMAT_BFORMAT2D_FLOAT32");
292 if(chans == ChannelConfig::BFormat3D) RETURN_FMT("AL_FORMAT_BFORMAT3D_FLOAT32");
295 else if(type == SampleType::Mulaw && ALContext::GetCurrent()->hasExtension(EXT_MULAW))
297 if(chans == ChannelConfig::Mono) return AL_FORMAT_MONO_MULAW;
298 if(chans == ChannelConfig::Stereo) return AL_FORMAT_STEREO_MULAW;
299 if(ALContext::GetCurrent()->hasExtension(EXT_MULAW_MCFORMATS))
301 if(chans == ChannelConfig::Rear) RETURN_FMT("AL_FORMAT_REAR_MULAW");
302 if(chans == ChannelConfig::Quad) RETURN_FMT("AL_FORMAT_QUAD_MULAW");
303 if(chans == ChannelConfig::X51) RETURN_FMT("AL_FORMAT_51CHN_MULAW");
304 if(chans == ChannelConfig::X61) RETURN_FMT("AL_FORMAT_61CHN_MULAW");
305 if(chans == ChannelConfig::X71) RETURN_FMT("AL_FORMAT_71CHN_MULAW");
307 if(ALContext::GetCurrent()->hasExtension(EXT_MULAW_BFORMAT))
309 if(chans == ChannelConfig::BFormat2D) RETURN_FMT("AL_FORMAT_BFORMAT2D_MULAW");
310 if(chans == ChannelConfig::BFormat3D) RETURN_FMT("AL_FORMAT_BFORMAT3D_MULAW");
313 #undef RETURN_FMT
315 return AL_NONE;