Add a method to check if a channel config and sample type are supported
[alure.git] / src / buffer.cpp
blob4774b8d91f4b0a76124f0e5d922d0a1190f086ca
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;
159 ALURE_API const char *GetSampleTypeName(SampleType type)
161 switch(type)
163 case SampleType::UInt8: return "Unsigned 8-bit";
164 case SampleType::Int16: return "Signed 16-bit";
165 case SampleType::Float32: return "32-bit float";
166 case SampleType::Mulaw: return "Mulaw";
168 throw std::runtime_error("Invalid type");
171 ALURE_API const char *GetChannelConfigName(ChannelConfig cfg)
173 switch(cfg)
175 case ChannelConfig::Mono: return "Mono";
176 case ChannelConfig::Stereo: return "Stereo";
177 case ChannelConfig::Rear: return "Rear";
178 case ChannelConfig::Quad: return "Quadrophonic";
179 case ChannelConfig::X51: return "5.1 Surround";
180 case ChannelConfig::X61: return "6.1 Surround";
181 case ChannelConfig::X71: return "7.1 Surround";
182 case ChannelConfig::BFormat2D: return "B-Format 2D";
183 case ChannelConfig::BFormat3D: return "B-Format 3D";
185 throw std::runtime_error("Invalid config");
189 ALURE_API ALuint FramesToBytes(ALuint frames, ChannelConfig chans, SampleType type)
191 ALuint size = frames;
192 switch(chans)
194 case ChannelConfig::Mono: size *= 1; break;
195 case ChannelConfig::Stereo: size *= 2; break;
196 case ChannelConfig::Rear: size *= 2; break;
197 case ChannelConfig::Quad: size *= 4; break;
198 case ChannelConfig::X51: size *= 6; break;
199 case ChannelConfig::X61: size *= 7; break;
200 case ChannelConfig::X71: size *= 8; break;
201 case ChannelConfig::BFormat2D: size *= 3; break;
202 case ChannelConfig::BFormat3D: size *= 4; break;
204 switch(type)
206 case SampleType::UInt8: size *= 1; break;
207 case SampleType::Int16: size *= 2; break;
208 case SampleType::Float32: size *= 4; break;
209 case SampleType::Mulaw: size *= 1; break;
212 return size;
215 ALURE_API ALuint BytesToFrames(ALuint bytes, ChannelConfig chans, SampleType type)
217 return bytes / FramesToBytes(1, chans, type);
221 ALenum GetFormat(ChannelConfig chans, SampleType type)
223 #define RETURN_FMT(x) do { \
224 ALenum fmt = alGetEnumValue(x); \
225 if(fmt != AL_NONE && fmt != -1) \
226 return fmt; \
227 } while(0)
228 if(type == SampleType::UInt8)
230 if(chans == ChannelConfig::Mono) return AL_FORMAT_MONO8;
231 if(chans == ChannelConfig::Stereo) return AL_FORMAT_STEREO8;
232 if(ALContext::GetCurrent()->hasExtension(EXT_MCFORMATS))
234 if(chans == ChannelConfig::Rear) RETURN_FMT("AL_FORMAT_REAR8");
235 if(chans == ChannelConfig::Quad) RETURN_FMT("AL_FORMAT_QUAD8");
236 if(chans == ChannelConfig::X51) RETURN_FMT("AL_FORMAT_51CHN8");
237 if(chans == ChannelConfig::X61) RETURN_FMT("AL_FORMAT_61CHN8");
238 if(chans == ChannelConfig::X71) RETURN_FMT("AL_FORMAT_71CHN8");
240 if(ALContext::GetCurrent()->hasExtension(EXT_BFORMAT))
242 if(chans == ChannelConfig::BFormat2D) RETURN_FMT("AL_FORMAT_BFORMAT2D_8");
243 if(chans == ChannelConfig::BFormat3D) RETURN_FMT("AL_FORMAT_BFORMAT3D_8");
246 else if(type == SampleType::Int16)
248 if(chans == ChannelConfig::Mono) return AL_FORMAT_MONO16;
249 if(chans == ChannelConfig::Stereo) return AL_FORMAT_STEREO16;
250 if(ALContext::GetCurrent()->hasExtension(EXT_MCFORMATS))
252 if(chans == ChannelConfig::Rear) RETURN_FMT("AL_FORMAT_REAR16");
253 if(chans == ChannelConfig::Quad) RETURN_FMT("AL_FORMAT_QUAD16");
254 if(chans == ChannelConfig::X51) RETURN_FMT("AL_FORMAT_51CHN16");
255 if(chans == ChannelConfig::X61) RETURN_FMT("AL_FORMAT_61CHN16");
256 if(chans == ChannelConfig::X71) RETURN_FMT("AL_FORMAT_71CHN16");
258 if(ALContext::GetCurrent()->hasExtension(EXT_BFORMAT))
260 if(chans == ChannelConfig::BFormat2D) RETURN_FMT("AL_FORMAT_BFORMAT2D_16");
261 if(chans == ChannelConfig::BFormat3D) RETURN_FMT("AL_FORMAT_BFORMAT3D_16");
264 else if(type == SampleType::Float32 && ALContext::GetCurrent()->hasExtension(EXT_FLOAT32))
266 if(chans == ChannelConfig::Mono) return AL_FORMAT_MONO_FLOAT32;
267 if(chans == ChannelConfig::Stereo) return AL_FORMAT_STEREO_FLOAT32;
268 if(ALContext::GetCurrent()->hasExtension(EXT_MCFORMATS))
270 if(chans == ChannelConfig::Rear) RETURN_FMT("AL_FORMAT_REAR32");
271 if(chans == ChannelConfig::Quad) RETURN_FMT("AL_FORMAT_QUAD32");
272 if(chans == ChannelConfig::X51) RETURN_FMT("AL_FORMAT_51CHN32");
273 if(chans == ChannelConfig::X61) RETURN_FMT("AL_FORMAT_61CHN32");
274 if(chans == ChannelConfig::X71) RETURN_FMT("AL_FORMAT_71CHN32");
276 if(ALContext::GetCurrent()->hasExtension(EXT_BFORMAT))
278 if(chans == ChannelConfig::BFormat2D) RETURN_FMT("AL_FORMAT_BFORMAT2D_FLOAT32");
279 if(chans == ChannelConfig::BFormat3D) RETURN_FMT("AL_FORMAT_BFORMAT3D_FLOAT32");
282 else if(type == SampleType::Mulaw && ALContext::GetCurrent()->hasExtension(EXT_MULAW))
284 if(chans == ChannelConfig::Mono) return AL_FORMAT_MONO_MULAW;
285 if(chans == ChannelConfig::Stereo) return AL_FORMAT_STEREO_MULAW;
286 if(ALContext::GetCurrent()->hasExtension(EXT_MULAW_MCFORMATS))
288 if(chans == ChannelConfig::Rear) RETURN_FMT("AL_FORMAT_REAR_MULAW");
289 if(chans == ChannelConfig::Quad) RETURN_FMT("AL_FORMAT_QUAD_MULAW");
290 if(chans == ChannelConfig::X51) RETURN_FMT("AL_FORMAT_51CHN_MULAW");
291 if(chans == ChannelConfig::X61) RETURN_FMT("AL_FORMAT_61CHN_MULAW");
292 if(chans == ChannelConfig::X71) RETURN_FMT("AL_FORMAT_71CHN_MULAW");
294 if(ALContext::GetCurrent()->hasExtension(EXT_MULAW_BFORMAT))
296 if(chans == ChannelConfig::BFormat2D) RETURN_FMT("AL_FORMAT_BFORMAT2D_MULAW");
297 if(chans == ChannelConfig::BFormat3D) RETURN_FMT("AL_FORMAT_BFORMAT3D_MULAW");
300 #undef RETURN_FMT
302 return AL_NONE;