1 /**
2  * OpenAL cross platform audio library
3  * Copyright (C) 1999-2007 by authors.
4  * This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Library General Public
6  *  License as published by the Free Software Foundation; either
7  *  version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  *  License along with this library; if not, write to the
16  *  Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  * Or go to http://www.gnu.org/copyleft/lgpl.html
19  */
20 
21 #include "config.h"
22 
23 #include "buffer.h"
24 
25 #include <algorithm>
26 #include <array>
27 #include <atomic>
28 #include <cassert>
29 #include <cstdint>
30 #include <cstdlib>
31 #include <cstring>
32 #include <iterator>
33 #include <limits>
34 #include <memory>
35 #include <mutex>
36 #include <new>
37 #include <numeric>
38 #include <utility>
39 
40 #include "AL/al.h"
41 #include "AL/alc.h"
42 #include "AL/alext.h"
43 
44 #include "albit.h"
45 #include "albyte.h"
46 #include "alcmain.h"
47 #include "alcontext.h"
48 #include "almalloc.h"
49 #include "alnumeric.h"
50 #include "aloptional.h"
51 #include "atomic.h"
52 #include "core/except.h"
53 #include "inprogext.h"
54 #include "opthelpers.h"
55 
56 
57 namespace {
58 
59 constexpr int MaxAdpcmChannels{2};
60 
61 /* IMA ADPCM Stepsize table */
62 constexpr int IMAStep_size[89] = {
63        7,    8,    9,   10,   11,   12,   13,   14,   16,   17,   19,
64       21,   23,   25,   28,   31,   34,   37,   41,   45,   50,   55,
65       60,   66,   73,   80,   88,   97,  107,  118,  130,  143,  157,
66      173,  190,  209,  230,  253,  279,  307,  337,  371,  408,  449,
67      494,  544,  598,  658,  724,  796,  876,  963, 1060, 1166, 1282,
68     1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660,
69     4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493,10442,
70    11487,12635,13899,15289,16818,18500,20350,22358,24633,27086,29794,
71    32767
72 };
73 
74 /* IMA4 ADPCM Codeword decode table */
75 constexpr int IMA4Codeword[16] = {
76     1, 3, 5, 7, 9, 11, 13, 15,
77    -1,-3,-5,-7,-9,-11,-13,-15,
78 };
79 
getFactoryByType(EffectSlotType type)80 /* IMA4 ADPCM Step index adjust decode table */
81 constexpr int IMA4Index_adjust[16] = {
82    -1,-1,-1,-1, 2, 4, 6, 8,
83    -1,-1,-1,-1, 2, 4, 6, 8
84 };
85 
86 
87 /* MSADPCM Adaption table */
88 constexpr int MSADPCMAdaption[16] = {
LookupEffectSlot(ALCcontext * context,ALuint id)89     230, 230, 230, 230, 307, 409, 512, 614,
90     768, 614, 512, 409, 307, 230, 230, 230
91 };
92 
93 /* MSADPCM Adaption Coefficient tables */
94 constexpr int MSADPCMAdaptionCoeff[7][2] = {
95     { 256,    0 },
96     { 512, -256 },
97     {   0,    0 },
98     { 192,   64 },
99     { 240,    0 },
100     { 460, -208 },
101     { 392, -232 }
LookupEffect(ALCdevice * device,ALuint id)102 };
103 
104 
105 void DecodeIMA4Block(int16_t *dst, const al::byte *src, size_t numchans, size_t align)
106 {
107     int sample[MaxAdpcmChannels]{};
108     int index[MaxAdpcmChannels]{};
109     ALuint code[MaxAdpcmChannels]{};
110 
111     for(size_t c{0};c < numchans;c++)
112     {
113         sample[c] = al::to_integer<int>(src[0]) | (al::to_integer<int>(src[1])<<8);
114         sample[c] = (sample[c]^0x8000) - 32768;
LookupBuffer(ALCdevice * device,ALuint id)115         src += 2;
116         index[c] = al::to_integer<int>(src[0]) | (al::to_integer<int>(src[1])<<8);
117         index[c] = clampi((index[c]^0x8000) - 32768, 0, 88);
118         src += 2;
119 
120         *(dst++) = static_cast<int16_t>(sample[c]);
121     }
122 
123     for(size_t i{1};i < align;i++)
124     {
125         if((i&7) == 1)
126         {
127             for(size_t c{0};c < numchans;c++)
128             {
GetEffectBuffer(ALbuffer * buffer)129                 code[c] = al::to_integer<ALuint>(src[0]) | (al::to_integer<ALuint>(src[1])<< 8) |
130                     (al::to_integer<ALuint>(src[2])<<16) | (al::to_integer<ALuint>(src[3])<<24);
131                 src += 4;
132             }
133         }
134 
135         for(size_t c{0};c < numchans;c++)
AddActiveEffectSlots(const al::span<ALeffectslot * > auxslots,ALCcontext * context)136         {
137             const ALuint nibble{code[c]&0xf};
138             code[c] >>= 4;
139 
140             sample[c] += IMA4Codeword[nibble] * IMAStep_size[index[c]] / 8;
141             sample[c] = clampi(sample[c], -32768, 32767);
142 
143             index[c] += IMA4Index_adjust[nibble];
144             index[c] = clampi(index[c], 0, 88);
145 
146             *(dst++) = static_cast<int16_t>(sample[c]);
147         }
148     }
149 }
150 
151 void DecodeMSADPCMBlock(int16_t *dst, const al::byte *src, size_t numchans, size_t align)
152 {
153     uint8_t blockpred[MaxAdpcmChannels]{};
154     int delta[MaxAdpcmChannels]{};
155     int16_t samples[MaxAdpcmChannels][2]{};
156 
157     for(size_t c{0};c < numchans;c++)
158     {
159         blockpred[c] = std::min<ALubyte>(al::to_integer<ALubyte>(src[0]), 6);
160         ++src;
161     }
162     for(size_t c{0};c < numchans;c++)
163     {
164         delta[c] = al::to_integer<int>(src[0]) | (al::to_integer<int>(src[1])<<8);
165         delta[c] = (delta[c]^0x8000) - 32768;
166         src += 2;
167     }
168     for(size_t c{0};c < numchans;c++)
169     {
170         samples[c][0] = static_cast<ALshort>(al::to_integer<int>(src[0]) |
171             (al::to_integer<int>(src[1])<<8));
172         src += 2;
173     }
174     for(size_t c{0};c < numchans;c++)
175     {
176         samples[c][1] = static_cast<ALshort>(al::to_integer<int>(src[0]) |
177             (al::to_integer<int>(src[1])<<8));
178         src += 2;
179     }
RemoveActiveEffectSlots(const al::span<ALeffectslot * > auxslots,ALCcontext * context)180 
181     /* Second sample is written first. */
182     for(size_t c{0};c < numchans;c++)
183         *(dst++) = samples[c][1];
184     for(size_t c{0};c < numchans;c++)
185         *(dst++) = samples[c][0];
186 
187     int num{0};
188     for(size_t i{2};i < align;i++)
189     {
190         for(size_t c{0};c < numchans;c++)
191         {
192             /* Read the nibble (first is in the upper bits). */
193             al::byte nibble;
194             if(!(num++ & 1))
__anon38130ab10402(EffectSlot *slot) 195                 nibble = *src >> 4;
196             else
197                 nibble = *(src++) & 0x0f;
198 
199             int pred{(samples[c][0]*MSADPCMAdaptionCoeff[blockpred[c]][0] +
200                 samples[c][1]*MSADPCMAdaptionCoeff[blockpred[c]][1]) / 256};
201             pred += (al::to_integer<int>(nibble^0x08) - 0x08) * delta[c];
202             pred  = clampi(pred, -32768, 32767);
203 
204             samples[c][1] = samples[c][0];
205             samples[c][0] = static_cast<int16_t>(pred);
206 
207             delta[c] = (MSADPCMAdaption[al::to_integer<ALubyte>(nibble)] * delta[c]) / 256;
208             delta[c] = maxi(16, delta[c]);
209 
210             *(dst++) = static_cast<int16_t>(pred);
211         }
212     }
213 }
214 
215 void Convert_int16_ima4(int16_t *dst, const al::byte *src, size_t numchans, size_t len,
216     size_t align)
217 {
218     assert(numchans <= MaxAdpcmChannels);
219     const size_t byte_align{((align-1)/2 + 4) * numchans};
EffectSlotTypeFromEnum(ALenum type)220 
221     len /= align;
222     while(len--)
223     {
224         DecodeIMA4Block(dst, src, numchans, align);
225         src += byte_align;
226         dst += align*numchans;
227     }
228 }
229 
230 void Convert_int16_msadpcm(int16_t *dst, const al::byte *src, size_t numchans, size_t len,
231     size_t align)
232 {
233     assert(numchans <= MaxAdpcmChannels);
234     const size_t byte_align{((align-2)/2 + 7) * numchans};
235 
236     len /= align;
237     while(len--)
238     {
239         DecodeMSADPCMBlock(dst, src, numchans, align);
240         src += byte_align;
241         dst += align*numchans;
242     }
243 }
244 
245 
EnsureEffectSlots(ALCcontext * context,size_t needed)246 ALuint BytesFromUserFmt(UserFmtType type) noexcept
247 {
248     switch(type)
249     {
250     case UserFmtUByte: return sizeof(uint8_t);
251     case UserFmtShort: return sizeof(int16_t);
252     case UserFmtFloat: return sizeof(float);
253     case UserFmtDouble: return sizeof(double);
254     case UserFmtMulaw: return sizeof(uint8_t);
255     case UserFmtAlaw: return sizeof(uint8_t);
256     case UserFmtIMA4: break; /* not handled here */
257     case UserFmtMSADPCM: break; /* not handled here */
258     }
259     return 0;
260 }
261 ALuint ChannelsFromUserFmt(UserFmtChannels chans, ALuint ambiorder) noexcept
262 {
263     switch(chans)
264     {
265     case UserFmtMono: return 1;
266     case UserFmtStereo: return 2;
267     case UserFmtRear: return 2;
268     case UserFmtQuad: return 4;
269     case UserFmtX51: return 6;
270     case UserFmtX61: return 7;
271     case UserFmtX71: return 8;
272     case UserFmtBFormat2D: return (ambiorder*2) + 1;
AllocEffectSlot(ALCcontext * context)273     case UserFmtBFormat3D: return (ambiorder+1) * (ambiorder+1);
274     }
275     return 0;
276 }
__anon38130ab10602(const EffectSlotSubList &entry) 277 
278 al::optional<AmbiLayout> AmbiLayoutFromEnum(ALenum layout)
279 {
280     switch(layout)
281     {
282     case AL_FUMA_SOFT: return al::make_optional(AmbiLayout::FuMa);
283     case AL_ACN_SOFT: return al::make_optional(AmbiLayout::ACN);
284     }
285     return al::nullopt;
286 }
287 ALenum EnumFromAmbiLayout(AmbiLayout layout)
288 {
289     switch(layout)
290     {
291     case AmbiLayout::FuMa: return AL_FUMA_SOFT;
292     case AmbiLayout::ACN: return AL_ACN_SOFT;
FreeEffectSlot(ALCcontext * context,ALeffectslot * slot)293     }
294     throw std::runtime_error{"Invalid AmbiLayout: "+std::to_string(int(layout))};
295 }
296 
297 al::optional<AmbiScaling> AmbiScalingFromEnum(ALenum scale)
298 {
299     switch(scale)
300     {
301     case AL_FUMA_SOFT: return al::make_optional(AmbiScaling::FuMa);
302     case AL_SN3D_SOFT: return al::make_optional(AmbiScaling::SN3D);
303     case AL_N3D_SOFT: return al::make_optional(AmbiScaling::N3D);
304     }
305     return al::nullopt;
306 }
307 ALenum EnumFromAmbiScaling(AmbiScaling scale)
308 {
309     switch(scale)
310     {
311     case AmbiScaling::FuMa: return AL_FUMA_SOFT;
312     case AmbiScaling::SN3D: return AL_SN3D_SOFT;
313     case AmbiScaling::N3D: return AL_SN3D_SOFT;
314     }
315     throw std::runtime_error{"Invalid AmbiScaling: "+std::to_string(int(scale))};
316 }
alGenAuxiliaryEffectSlots(ALsizei n,ALuint * effectslots)317 
318 
319 constexpr ALbitfieldSOFT INVALID_STORAGE_MASK{~unsigned(AL_MAP_READ_BIT_SOFT |
320     AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT)};
321 constexpr ALbitfieldSOFT MAP_READ_WRITE_FLAGS{AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT};
322 constexpr ALbitfieldSOFT INVALID_MAP_FLAGS{~unsigned(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT |
323     AL_MAP_PERSISTENT_BIT_SOFT)};
324 
325 
326 bool EnsureBuffers(ALCdevice *device, size_t needed)
327 {
328     size_t count{std::accumulate(device->BufferList.cbegin(), device->BufferList.cend(), size_t{0},
329         [](size_t cur, const BufferSubList &sublist) noexcept -> size_t
330         { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
331 
332     while(needed > count)
333     {
334         if UNLIKELY(device->BufferList.size() >= 1<<25)
335             return false;
336 
337         device->BufferList.emplace_back();
338         auto sublist = device->BufferList.end() - 1;
339         sublist->FreeMask = ~0_u64;
340         sublist->Buffers = static_cast<ALbuffer*>(al_calloc(alignof(ALbuffer), sizeof(ALbuffer)*64));
341         if UNLIKELY(!sublist->Buffers)
342         {
343             device->BufferList.pop_back();
344             return false;
345         }
346         count += 64;
347     }
348     return true;
349 }
350 
351 ALbuffer *AllocBuffer(ALCdevice *device)
352 {
353     auto sublist = std::find_if(device->BufferList.begin(), device->BufferList.end(),
354         [](const BufferSubList &entry) noexcept -> bool
355         { return entry.FreeMask != 0; }
356     );
357 
358     auto lidx = static_cast<ALuint>(std::distance(device->BufferList.begin(), sublist));
359     auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
360 
361     ALbuffer *buffer{::new (sublist->Buffers + slidx) ALbuffer{}};
362 
363     /* Add 1 to avoid buffer ID 0. */
364     buffer->id = ((lidx<<6) | slidx) + 1;
365 
366     sublist->FreeMask &= ~(1_u64 << slidx);
367 
alDeleteAuxiliaryEffectSlots(ALsizei n,const ALuint * effectslots)368     return buffer;
369 }
370 
371 void FreeBuffer(ALCdevice *device, ALbuffer *buffer)
372 {
373     const ALuint id{buffer->id - 1};
374     const size_t lidx{id >> 6};
375     const ALuint slidx{id & 0x3f};
376 
377     al::destroy_at(buffer);
378 
379     device->BufferList[lidx].FreeMask |= 1_u64 << slidx;
380 }
381 
382 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
383 {
384     const size_t lidx{(id-1) >> 6};
385     const ALuint slidx{(id-1) & 0x3f};
386 
387     if UNLIKELY(lidx >= device->BufferList.size())
388         return nullptr;
389     BufferSubList &sublist = device->BufferList[lidx];
390     if UNLIKELY(sublist.FreeMask & (1_u64 << slidx))
391         return nullptr;
392     return sublist.Buffers + slidx;
393 }
394 
395 
396 ALuint SanitizeAlignment(UserFmtType type, ALuint align)
397 {
398     if(align == 0)
399     {
400         if(type == UserFmtIMA4)
401         {
402             /* Here is where things vary:
403              * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel
404              * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel
405              */
406             return 65;
407         }
408         if(type == UserFmtMSADPCM)
409             return 64;
410         return 1;
411     }
412 
413     if(type == UserFmtIMA4)
414     {
415         /* IMA4 block alignment must be a multiple of 8, plus 1. */
416         if((align&7) == 1) return static_cast<ALuint>(align);
417         return 0;
418     }
419     if(type == UserFmtMSADPCM)
420     {
421         /* MSADPCM block alignment must be a multiple of 2. */
422         if((align&1) == 0) return static_cast<ALuint>(align);
423         return 0;
424     }
425 
426     return static_cast<ALuint>(align);
427 }
428 
429 
430 const ALchar *NameFromUserFmtType(UserFmtType type)
431 {
alIsAuxiliaryEffectSlot(ALuint effectslot)432     switch(type)
433     {
434     case UserFmtUByte: return "UInt8";
435     case UserFmtShort: return "Int16";
436     case UserFmtFloat: return "Float32";
437     case UserFmtDouble: return "Float64";
438     case UserFmtMulaw: return "muLaw";
439     case UserFmtAlaw: return "aLaw";
440     case UserFmtIMA4: return "IMA4 ADPCM";
441     case UserFmtMSADPCM: return "MSADPCM";
442     }
443     return "<internal type error>";
444 }
445 
446 /** Loads the specified data into the buffer, using the specified format. */
alAuxiliaryEffectSlotPlaySOFT(ALuint slotid)447 void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
448     UserFmtChannels SrcChannels, UserFmtType SrcType, const al::byte *SrcData,
449     ALbitfieldSOFT access)
450 {
451     if UNLIKELY(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0)
452         SETERR_RETURN(context, AL_INVALID_OPERATION,, "Modifying storage for in-use buffer %u",
453                       ALBuf->id);
454 
455     /* Currently no channel configurations need to be converted. */
456     FmtChannels DstChannels{FmtMono};
457     switch(SrcChannels)
458     {
459     case UserFmtMono: DstChannels = FmtMono; break;
460     case UserFmtStereo: DstChannels = FmtStereo; break;
461     case UserFmtRear: DstChannels = FmtRear; break;
462     case UserFmtQuad: DstChannels = FmtQuad; break;
463     case UserFmtX51: DstChannels = FmtX51; break;
464     case UserFmtX61: DstChannels = FmtX61; break;
465     case UserFmtX71: DstChannels = FmtX71; break;
466     case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
467     case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
468     }
469     if UNLIKELY(static_cast<long>(SrcChannels) != static_cast<long>(DstChannels))
470         SETERR_RETURN(context, AL_INVALID_ENUM, , "Invalid format");
471 
472     /* IMA4 and MSADPCM convert to 16-bit short. */
473     FmtType DstType{FmtUByte};
474     switch(SrcType)
475     {
476     case UserFmtUByte: DstType = FmtUByte; break;
477     case UserFmtShort: DstType = FmtShort; break;
478     case UserFmtFloat: DstType = FmtFloat; break;
479     case UserFmtDouble: DstType = FmtDouble; break;
480     case UserFmtAlaw: DstType = FmtAlaw; break;
481     case UserFmtMulaw: DstType = FmtMulaw; break;
482     case UserFmtIMA4: DstType = FmtShort; break;
483     case UserFmtMSADPCM: DstType = FmtShort; break;
484     }
485 
486     /* TODO: Currently we can only map samples when they're not converted. To
487      * allow it would need some kind of double-buffering to hold onto a copy of
488      * the original data.
489      */
490     if((access&MAP_READ_WRITE_FLAGS))
491     {
492         if UNLIKELY(static_cast<long>(SrcType) != static_cast<long>(DstType))
493             SETERR_RETURN(context, AL_INVALID_VALUE,, "%s samples cannot be mapped",
494                 NameFromUserFmtType(SrcType));
495     }
496 
497     const ALuint unpackalign{ALBuf->UnpackAlign};
498     const ALuint align{SanitizeAlignment(SrcType, unpackalign)};
499     if UNLIKELY(align < 1)
500         SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid unpack alignment %u for %s samples",
501             unpackalign, NameFromUserFmtType(SrcType));
502 
503     const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ?
504         ALBuf->UnpackAmbiOrder : 0};
505 
506     if((access&AL_PRESERVE_DATA_BIT_SOFT))
507     {
508         /* Can only preserve data with the same format and alignment. */
509         if UNLIKELY(ALBuf->mChannels != DstChannels || ALBuf->OriginalType != SrcType)
510             SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched format");
511         if UNLIKELY(ALBuf->OriginalAlign != align)
512             SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched alignment");
513         if(ALBuf->mAmbiOrder != ambiorder)
514             SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched order");
515     }
516 
517     /* Convert the input/source size in bytes to sample frames using the unpack
518      * block alignment.
519      */
520     const ALuint SrcByteAlign{ChannelsFromUserFmt(SrcChannels, ambiorder) *
521         ((SrcType == UserFmtIMA4) ? (align-1)/2 + 4 :
522         (SrcType == UserFmtMSADPCM) ? (align-2)/2 + 7 :
523         (align * BytesFromUserFmt(SrcType)))};
524     if UNLIKELY((size%SrcByteAlign) != 0)
525         SETERR_RETURN(context, AL_INVALID_VALUE,,
526             "Data size %d is not a multiple of frame size %d (%d unpack alignment)",
527             size, SrcByteAlign, align);
528 
529     if UNLIKELY(size/SrcByteAlign > std::numeric_limits<ALsizei>::max()/align)
530         SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
531             "Buffer size overflow, %d blocks x %d samples per block", size/SrcByteAlign, align);
532     const ALuint frames{size / SrcByteAlign * align};
533 
534     /* Convert the sample frames to the number of bytes needed for internal
535      * storage.
536      */
537     ALuint NumChannels{ChannelsFromFmt(DstChannels, ambiorder)};
538     ALuint FrameSize{NumChannels * BytesFromFmt(DstType)};
539     if UNLIKELY(frames > std::numeric_limits<size_t>::max()/FrameSize)
540         SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
541             "Buffer size overflow, %d frames x %d bytes per frame", frames, FrameSize);
542     size_t newsize{static_cast<size_t>(frames) * FrameSize};
543 
544     /* Round up to the next 16-byte multiple. This could reallocate only when
545      * increasing or the new size is less than half the current, but then the
546      * buffer's AL_SIZE would not be very reliable for accounting buffer memory
547      * usage, and reporting the real size could cause problems for apps that
548      * use AL_SIZE to try to get the buffer's play length.
549      */
550     newsize = RoundUp(newsize, 16);
551     if(newsize != ALBuf->mData.size())
552     {
553         auto newdata = al::vector<al::byte,16>(newsize, al::byte{});
554         if((access&AL_PRESERVE_DATA_BIT_SOFT))
555         {
556             const size_t tocopy{minz(newdata.size(), ALBuf->mData.size())};
557             std::copy_n(ALBuf->mData.begin(), tocopy, newdata.begin());
558         }
559         newdata.swap(ALBuf->mData);
560     }
561 
562     if(SrcType == UserFmtIMA4)
563     {
564         assert(DstType == FmtShort);
565         if(SrcData != nullptr && !ALBuf->mData.empty())
566             Convert_int16_ima4(reinterpret_cast<int16_t*>(ALBuf->mData.data()), SrcData,
567                 NumChannels, frames, align);
568         ALBuf->OriginalAlign = align;
569     }
570     else if(SrcType == UserFmtMSADPCM)
571     {
572         assert(DstType == FmtShort);
573         if(SrcData != nullptr && !ALBuf->mData.empty())
574             Convert_int16_msadpcm(reinterpret_cast<int16_t*>(ALBuf->mData.data()), SrcData,
575                 NumChannels, frames, align);
576         ALBuf->OriginalAlign = align;
577     }
578     else
579     {
580         assert(static_cast<long>(SrcType) == static_cast<long>(DstType));
581         if(SrcData != nullptr && !ALBuf->mData.empty())
582             std::copy_n(SrcData, frames*FrameSize, ALBuf->mData.begin());
583         ALBuf->OriginalAlign = 1;
584     }
585     ALBuf->OriginalSize = size;
586     ALBuf->OriginalType = SrcType;
587 
588     ALBuf->Access = access;
589 
590     ALBuf->mSampleRate = static_cast<ALuint>(freq);
591     ALBuf->mChannels = DstChannels;
592     ALBuf->mType = DstType;
593     ALBuf->mAmbiOrder = ambiorder;
594 
595     ALBuf->mCallback = nullptr;
596     ALBuf->mUserData = nullptr;
597 
598     ALBuf->mSampleLen = frames;
599     ALBuf->mLoopStart = 0;
600     ALBuf->mLoopEnd = ALBuf->mSampleLen;
601 }
602 
603 /** Prepares the buffer to use the specified callback, using the specified format. */
604 void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
605     UserFmtChannels SrcChannels, UserFmtType SrcType, LPALBUFFERCALLBACKTYPESOFT callback,
606     void *userptr)
607 {
608     if UNLIKELY(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0)
609         SETERR_RETURN(context, AL_INVALID_OPERATION,, "Modifying callback for in-use buffer %u",
610             ALBuf->id);
611 
612     /* Currently no channel configurations need to be converted. */
613     FmtChannels DstChannels{FmtMono};
614     switch(SrcChannels)
615     {
616     case UserFmtMono: DstChannels = FmtMono; break;
617     case UserFmtStereo: DstChannels = FmtStereo; break;
618     case UserFmtRear: DstChannels = FmtRear; break;
619     case UserFmtQuad: DstChannels = FmtQuad; break;
620     case UserFmtX51: DstChannels = FmtX51; break;
621     case UserFmtX61: DstChannels = FmtX61; break;
622     case UserFmtX71: DstChannels = FmtX71; break;
623     case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
624     case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
625     }
626     if UNLIKELY(static_cast<long>(SrcChannels) != static_cast<long>(DstChannels))
627         SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid format");
628 
629     /* IMA4 and MSADPCM convert to 16-bit short. Not supported with callbacks. */
630     FmtType DstType{FmtUByte};
631     switch(SrcType)
632     {
633     case UserFmtUByte: DstType = FmtUByte; break;
634     case UserFmtShort: DstType = FmtShort; break;
635     case UserFmtFloat: DstType = FmtFloat; break;
636     case UserFmtDouble: DstType = FmtDouble; break;
637     case UserFmtAlaw: DstType = FmtAlaw; break;
638     case UserFmtMulaw: DstType = FmtMulaw; break;
639     case UserFmtIMA4: DstType = FmtShort; break;
640     case UserFmtMSADPCM: DstType = FmtShort; break;
641     }
642     if UNLIKELY(static_cast<long>(SrcType) != static_cast<long>(DstType))
643         SETERR_RETURN(context, AL_INVALID_ENUM,, "Unsupported callback format");
644 
645     const ALuint ambiorder{(DstChannels == FmtBFormat2D || DstChannels == FmtBFormat3D) ?
646         ALBuf->UnpackAmbiOrder : 0};
647 
648     al::vector<al::byte,16>(FrameSizeFromFmt(DstChannels, DstType, ambiorder) *
649         size_t{BufferLineSize + (MaxResamplerPadding>>1)}).swap(ALBuf->mData);
650 
651     ALBuf->mCallback = callback;
652     ALBuf->mUserData = userptr;
653 
654     ALBuf->OriginalType = SrcType;
655     ALBuf->OriginalSize = 0;
656     ALBuf->OriginalAlign = 1;
657     ALBuf->Access = 0;
658 
659     ALBuf->mSampleRate = static_cast<ALuint>(freq);
660     ALBuf->mChannels = DstChannels;
661     ALBuf->mType = DstType;
662     ALBuf->mAmbiOrder = ambiorder;
663 
664     ALBuf->mSampleLen = 0;
665     ALBuf->mLoopStart = 0;
666     ALBuf->mLoopEnd = ALBuf->mSampleLen;
667 }
668 
669 
670 struct DecompResult { UserFmtChannels channels; UserFmtType type; };
671 al::optional<DecompResult> DecomposeUserFormat(ALenum format)
672 {
673     struct FormatMap {
674         ALenum format;
alAuxiliaryEffectSlotiv(ALuint effectslot,ALenum param,const ALint * values)675         UserFmtChannels channels;
676         UserFmtType type;
677     };
678     static const std::array<FormatMap,46> UserFmtList{{
679         { AL_FORMAT_MONO8,             UserFmtMono, UserFmtUByte   },
680         { AL_FORMAT_MONO16,            UserFmtMono, UserFmtShort   },
681         { AL_FORMAT_MONO_FLOAT32,      UserFmtMono, UserFmtFloat   },
682         { AL_FORMAT_MONO_DOUBLE_EXT,   UserFmtMono, UserFmtDouble  },
683         { AL_FORMAT_MONO_IMA4,         UserFmtMono, UserFmtIMA4    },
684         { AL_FORMAT_MONO_MSADPCM_SOFT, UserFmtMono, UserFmtMSADPCM },
685         { AL_FORMAT_MONO_MULAW,        UserFmtMono, UserFmtMulaw   },
686         { AL_FORMAT_MONO_ALAW_EXT,     UserFmtMono, UserFmtAlaw    },
687 
688         { AL_FORMAT_STEREO8,             UserFmtStereo, UserFmtUByte   },
689         { AL_FORMAT_STEREO16,            UserFmtStereo, UserFmtShort   },
690         { AL_FORMAT_STEREO_FLOAT32,      UserFmtStereo, UserFmtFloat   },
691         { AL_FORMAT_STEREO_DOUBLE_EXT,   UserFmtStereo, UserFmtDouble  },
692         { AL_FORMAT_STEREO_IMA4,         UserFmtStereo, UserFmtIMA4    },
693         { AL_FORMAT_STEREO_MSADPCM_SOFT, UserFmtStereo, UserFmtMSADPCM },
694         { AL_FORMAT_STEREO_MULAW,        UserFmtStereo, UserFmtMulaw   },
695         { AL_FORMAT_STEREO_ALAW_EXT,     UserFmtStereo, UserFmtAlaw    },
696 
697         { AL_FORMAT_REAR8,      UserFmtRear, UserFmtUByte },
698         { AL_FORMAT_REAR16,     UserFmtRear, UserFmtShort },
699         { AL_FORMAT_REAR32,     UserFmtRear, UserFmtFloat },
700         { AL_FORMAT_REAR_MULAW, UserFmtRear, UserFmtMulaw },
701 
702         { AL_FORMAT_QUAD8_LOKI,  UserFmtQuad, UserFmtUByte },
703         { AL_FORMAT_QUAD16_LOKI, UserFmtQuad, UserFmtShort },
704 
705         { AL_FORMAT_QUAD8,      UserFmtQuad, UserFmtUByte },
alAuxiliaryEffectSlotf(ALuint effectslot,ALenum param,ALfloat value)706         { AL_FORMAT_QUAD16,     UserFmtQuad, UserFmtShort },
707         { AL_FORMAT_QUAD32,     UserFmtQuad, UserFmtFloat },
708         { AL_FORMAT_QUAD_MULAW, UserFmtQuad, UserFmtMulaw },
709 
710         { AL_FORMAT_51CHN8,      UserFmtX51, UserFmtUByte },
711         { AL_FORMAT_51CHN16,     UserFmtX51, UserFmtShort },
712         { AL_FORMAT_51CHN32,     UserFmtX51, UserFmtFloat },
713         { AL_FORMAT_51CHN_MULAW, UserFmtX51, UserFmtMulaw },
714 
715         { AL_FORMAT_61CHN8,      UserFmtX61, UserFmtUByte },
716         { AL_FORMAT_61CHN16,     UserFmtX61, UserFmtShort },
717         { AL_FORMAT_61CHN32,     UserFmtX61, UserFmtFloat },
718         { AL_FORMAT_61CHN_MULAW, UserFmtX61, UserFmtMulaw },
719 
720         { AL_FORMAT_71CHN8,      UserFmtX71, UserFmtUByte },
721         { AL_FORMAT_71CHN16,     UserFmtX71, UserFmtShort },
722         { AL_FORMAT_71CHN32,     UserFmtX71, UserFmtFloat },
723         { AL_FORMAT_71CHN_MULAW, UserFmtX71, UserFmtMulaw },
724 
725         { AL_FORMAT_BFORMAT2D_8,       UserFmtBFormat2D, UserFmtUByte },
726         { AL_FORMAT_BFORMAT2D_16,      UserFmtBFormat2D, UserFmtShort },
727         { AL_FORMAT_BFORMAT2D_FLOAT32, UserFmtBFormat2D, UserFmtFloat },
728         { AL_FORMAT_BFORMAT2D_MULAW,   UserFmtBFormat2D, UserFmtMulaw },
729 
730         { AL_FORMAT_BFORMAT3D_8,       UserFmtBFormat3D, UserFmtUByte },
731         { AL_FORMAT_BFORMAT3D_16,      UserFmtBFormat3D, UserFmtShort },
732         { AL_FORMAT_BFORMAT3D_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
733         { AL_FORMAT_BFORMAT3D_MULAW,   UserFmtBFormat3D, UserFmtMulaw },
alAuxiliaryEffectSlotfv(ALuint effectslot,ALenum param,const ALfloat * values)734     }};
735 
736     for(const auto &fmt : UserFmtList)
737     {
738         if(fmt.format == format)
739             return al::make_optional<DecompResult>({fmt.channels, fmt.type});
740     }
741     return al::nullopt;
742 }
743 
744 } // namespace
745 
746 
747 AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers)
748 START_API_FUNC
749 {
750     ContextRef context{GetContextRef()};
751     if UNLIKELY(!context) return;
752 
753     if UNLIKELY(n < 0)
754         context->setError(AL_INVALID_VALUE, "Generating %d buffers", n);
755     if UNLIKELY(n <= 0) return;
756 
757     ALCdevice *device{context->mDevice.get()};
758     std::lock_guard<std::mutex> _{device->BufferLock};
759     if(!EnsureBuffers(device, static_cast<ALuint>(n)))
760     {
761         context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d buffer%s", n, (n==1)?"":"s");
alGetAuxiliaryEffectSloti(ALuint effectslot,ALenum param,ALint * value)762         return;
763     }
764 
765     if LIKELY(n == 1)
766     {
767         /* Special handling for the easy and normal case. */
768         ALbuffer *buffer{AllocBuffer(device)};
769         buffers[0] = buffer->id;
770     }
771     else
772     {
773         /* Store the allocated buffer IDs in a separate local list, to avoid
774          * modifying the user storage in case of failure.
775          */
776         al::vector<ALuint> ids;
777         ids.reserve(static_cast<ALuint>(n));
778         do {
779             ALbuffer *buffer{AllocBuffer(device)};
780             ids.emplace_back(buffer->id);
781         } while(--n);
782         std::copy(ids.begin(), ids.end(), buffers);
783     }
784 }
785 END_API_FUNC
786 
787 AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
788 START_API_FUNC
789 {
790     ContextRef context{GetContextRef()};
791     if UNLIKELY(!context) return;
792 
793     if UNLIKELY(n < 0)
794         context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n);
795     if UNLIKELY(n <= 0) return;
796 
797     ALCdevice *device{context->mDevice.get()};
798     std::lock_guard<std::mutex> _{device->BufferLock};
799 
800     /* First try to find any buffers that are invalid or in-use. */
801     auto validate_buffer = [device, &context](const ALuint bid) -> bool
802     {
alGetAuxiliaryEffectSlotiv(ALuint effectslot,ALenum param,ALint * values)803         if(!bid) return true;
804         ALbuffer *ALBuf{LookupBuffer(device, bid)};
805         if UNLIKELY(!ALBuf)
806         {
807             context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", bid);
808             return false;
809         }
810         if UNLIKELY(ReadRef(ALBuf->ref) != 0)
811         {
812             context->setError(AL_INVALID_OPERATION, "Deleting in-use buffer %u", bid);
813             return false;
814         }
815         return true;
816     };
817     const ALuint *buffers_end = buffers + n;
818     auto invbuf = std::find_if_not(buffers, buffers_end, validate_buffer);
819     if UNLIKELY(invbuf != buffers_end) return;
820 
821     /* All good. Delete non-0 buffer IDs. */
822     auto delete_buffer = [device](const ALuint bid) -> void
823     {
824         ALbuffer *buffer{bid ? LookupBuffer(device, bid) : nullptr};
825         if(buffer) FreeBuffer(device, buffer);
826     };
827     std::for_each(buffers, buffers_end, delete_buffer);
828 }
829 END_API_FUNC
830 
831 AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
832 START_API_FUNC
833 {
alGetAuxiliaryEffectSlotf(ALuint effectslot,ALenum param,ALfloat * value)834     ContextRef context{GetContextRef()};
835     if LIKELY(context)
836     {
837         ALCdevice *device{context->mDevice.get()};
838         std::lock_guard<std::mutex> _{device->BufferLock};
839         if(!buffer || LookupBuffer(device, buffer))
840             return AL_TRUE;
841     }
842     return AL_FALSE;
843 }
844 END_API_FUNC
845 
846 
847 AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
848 START_API_FUNC
849 { alBufferStorageSOFT(buffer, format, data, size, freq, 0); }
850 END_API_FUNC
851 
852 AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags)
853 START_API_FUNC
854 {
855     ContextRef context{GetContextRef()};
856     if UNLIKELY(!context) return;
alGetAuxiliaryEffectSlotfv(ALuint effectslot,ALenum param,ALfloat * values)857 
858     ALCdevice *device{context->mDevice.get()};
859     std::lock_guard<std::mutex> _{device->BufferLock};
860 
861     ALbuffer *albuf = LookupBuffer(device, buffer);
862     if UNLIKELY(!albuf)
863         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
864     else if UNLIKELY(size < 0)
865         context->setError(AL_INVALID_VALUE, "Negative storage size %d", size);
866     else if UNLIKELY(freq < 1)
867         context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
868     else if UNLIKELY((flags&INVALID_STORAGE_MASK) != 0)
869         context->setError(AL_INVALID_VALUE, "Invalid storage flags 0x%x",
870             flags&INVALID_STORAGE_MASK);
871     else if UNLIKELY((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS))
872         context->setError(AL_INVALID_VALUE,
873             "Declaring persistently mapped storage without read or write access");
874     else
875     {
876         auto usrfmt = DecomposeUserFormat(format);
877         if UNLIKELY(!usrfmt)
878             context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
879         else
880             LoadData(context.get(), albuf, freq, static_cast<ALuint>(size), usrfmt->channels,
881                 usrfmt->type, static_cast<const al::byte*>(data), flags);
882     }
883 }
884 END_API_FUNC
ALeffectslot()885 
886 AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access)
887 START_API_FUNC
888 {
889     ContextRef context{GetContextRef()};
890     if UNLIKELY(!context) return nullptr;
891 
892     ALCdevice *device{context->mDevice.get()};
893     std::lock_guard<std::mutex> _{device->BufferLock};
894 
895     ALbuffer *albuf = LookupBuffer(device, buffer);
896     if UNLIKELY(!albuf)
897         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
898     else if UNLIKELY((access&INVALID_MAP_FLAGS) != 0)
899         context->setError(AL_INVALID_VALUE, "Invalid map flags 0x%x", access&INVALID_MAP_FLAGS);
900     else if UNLIKELY(!(access&MAP_READ_WRITE_FLAGS))
901         context->setError(AL_INVALID_VALUE, "Mapping buffer %u without read or write access",
902             buffer);
903     else
904     {
905         ALbitfieldSOFT unavailable = (albuf->Access^access) & access;
906         if UNLIKELY(ReadRef(albuf->ref) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT))
907             context->setError(AL_INVALID_OPERATION,
908                 "Mapping in-use buffer %u without persistent mapping", buffer);
909         else if UNLIKELY(albuf->MappedAccess != 0)
910             context->setError(AL_INVALID_OPERATION, "Mapping already-mapped buffer %u", buffer);
911         else if UNLIKELY((unavailable&AL_MAP_READ_BIT_SOFT))
912             context->setError(AL_INVALID_VALUE,
913                 "Mapping buffer %u for reading without read access", buffer);
914         else if UNLIKELY((unavailable&AL_MAP_WRITE_BIT_SOFT))
915             context->setError(AL_INVALID_VALUE,
916                 "Mapping buffer %u for writing without write access", buffer);
917         else if UNLIKELY((unavailable&AL_MAP_PERSISTENT_BIT_SOFT))
918             context->setError(AL_INVALID_VALUE,
919                 "Mapping buffer %u persistently without persistent access", buffer);
920         else if UNLIKELY(offset < 0 || length <= 0
921             || static_cast<ALuint>(offset) >= albuf->OriginalSize
922             || static_cast<ALuint>(length) > albuf->OriginalSize - static_cast<ALuint>(offset))
923             context->setError(AL_INVALID_VALUE, "Mapping invalid range %d+%d for buffer %u",
924                 offset, length, buffer);
925         else
926         {
927             void *retval{albuf->mData.data() + offset};
928             albuf->MappedAccess = access;
929             albuf->MappedOffset = offset;
930             albuf->MappedSize = length;
931             return retval;
932         }
933     }
934 
935     return nullptr;
936 }
937 END_API_FUNC
938 
939 AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer)
940 START_API_FUNC
941 {
942     ContextRef context{GetContextRef()};
943     if UNLIKELY(!context) return;
944 
945     ALCdevice *device{context->mDevice.get()};
946     std::lock_guard<std::mutex> _{device->BufferLock};
947 
948     ALbuffer *albuf = LookupBuffer(device, buffer);
949     if UNLIKELY(!albuf)
950         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
951     else if UNLIKELY(albuf->MappedAccess == 0)
952         context->setError(AL_INVALID_OPERATION, "Unmapping unmapped buffer %u", buffer);
953     else
954     {
955         albuf->MappedAccess = 0;
956         albuf->MappedOffset = 0;
957         albuf->MappedSize = 0;
updateProps(ALCcontext * context)958     }
959 }
960 END_API_FUNC
961 
962 AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length)
963 START_API_FUNC
964 {
965     ContextRef context{GetContextRef()};
966     if UNLIKELY(!context) return;
967 
968     ALCdevice *device{context->mDevice.get()};
969     std::lock_guard<std::mutex> _{device->BufferLock};
970 
971     ALbuffer *albuf = LookupBuffer(device, buffer);
972     if UNLIKELY(!albuf)
973         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
974     else if UNLIKELY(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT))
975         context->setError(AL_INVALID_OPERATION, "Flushing buffer %u while not mapped for writing",
976             buffer);
977     else if UNLIKELY(offset < albuf->MappedOffset || length <= 0
978         || offset >= albuf->MappedOffset+albuf->MappedSize
979         || length > albuf->MappedOffset+albuf->MappedSize-offset)
980         context->setError(AL_INVALID_VALUE, "Flushing invalid range %d+%d on buffer %u", offset,
981             length, buffer);
982     else
983     {
984         /* FIXME: Need to use some method of double-buffering for the mixer and
985          * app to hold separate memory, which can be safely transfered
986          * asynchronously. Currently we just say the app shouldn't write where
987          * OpenAL's reading, and hope for the best...
988          */
989         std::atomic_thread_fence(std::memory_order_seq_cst);
990     }
991 }
992 END_API_FUNC
993 
UpdateAllEffectSlotProps(ALCcontext * context)994 AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length)
995 START_API_FUNC
996 {
997     ContextRef context{GetContextRef()};
998     if UNLIKELY(!context) return;
999 
1000     ALCdevice *device{context->mDevice.get()};
1001     std::lock_guard<std::mutex> _{device->BufferLock};
1002 
1003     ALbuffer *albuf = LookupBuffer(device, buffer);
1004     if UNLIKELY(!albuf)
1005     {
1006         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1007         return;
1008     }
1009 
1010     auto usrfmt = DecomposeUserFormat(format);
1011     if UNLIKELY(!usrfmt)
1012     {
1013         context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
1014         return;
1015     }
1016 
1017     ALuint unpack_align{albuf->UnpackAlign};
1018     ALuint align{SanitizeAlignment(usrfmt->type, unpack_align)};
1019     if UNLIKELY(align < 1)
1020         context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u", unpack_align);
1021     else if UNLIKELY(long{usrfmt->channels} != long{albuf->mChannels}
1022         || usrfmt->type != albuf->OriginalType)
1023         context->setError(AL_INVALID_ENUM, "Unpacking data with mismatched format");
1024     else if UNLIKELY(align != albuf->OriginalAlign)
1025         context->setError(AL_INVALID_VALUE,
1026             "Unpacking data with alignment %u does not match original alignment %u", align,
1027             albuf->OriginalAlign);
1028     else if UNLIKELY(albuf->isBFormat() && albuf->UnpackAmbiOrder != albuf->mAmbiOrder)
1029         context->setError(AL_INVALID_VALUE, "Unpacking data with mismatched ambisonic order");
1030     else if UNLIKELY(albuf->MappedAccess != 0)
1031         context->setError(AL_INVALID_OPERATION, "Unpacking data into mapped buffer %u", buffer);
1032     else
1033     {
1034         ALuint num_chans{albuf->channelsFromFmt()};
1035         ALuint frame_size{num_chans * albuf->bytesFromFmt()};
1036         ALuint byte_align{
1037             (albuf->OriginalType == UserFmtIMA4) ? ((align-1)/2 + 4) * num_chans :
1038             (albuf->OriginalType == UserFmtMSADPCM) ? ((align-2)/2 + 7) * num_chans :
1039             (align * frame_size)
1040         };
1041 
1042         if UNLIKELY(offset < 0 || length < 0 || static_cast<ALuint>(offset) > albuf->OriginalSize
1043             || static_cast<ALuint>(length) > albuf->OriginalSize-static_cast<ALuint>(offset))
1044             context->setError(AL_INVALID_VALUE, "Invalid data sub-range %d+%d on buffer %u",
1045                 offset, length, buffer);
1046         else if UNLIKELY((static_cast<ALuint>(offset)%byte_align) != 0)
1047             context->setError(AL_INVALID_VALUE,
1048                 "Sub-range offset %d is not a multiple of frame size %d (%d unpack alignment)",
1049                 offset, byte_align, align);
1050         else if UNLIKELY((static_cast<ALuint>(length)%byte_align) != 0)
1051             context->setError(AL_INVALID_VALUE,
1052                 "Sub-range length %d is not a multiple of frame size %d (%d unpack alignment)",
1053                 length, byte_align, align);
1054         else
1055         {
1056             /* offset -> byte offset, length -> sample count */
1057             size_t byteoff{static_cast<ALuint>(offset)/byte_align * align * frame_size};
1058             size_t samplen{static_cast<ALuint>(length)/byte_align * align};
1059 
1060             void *dst = albuf->mData.data() + byteoff;
1061             if(usrfmt->type == UserFmtIMA4 && albuf->mType == FmtShort)
1062                 Convert_int16_ima4(static_cast<int16_t*>(dst), static_cast<const al::byte*>(data),
1063                     num_chans, samplen, align);
1064             else if(usrfmt->type == UserFmtMSADPCM && albuf->mType == FmtShort)
1065                 Convert_int16_msadpcm(static_cast<int16_t*>(dst),
1066                     static_cast<const al::byte*>(data), num_chans, samplen, align);
1067             else
1068             {
1069                 assert(long{usrfmt->type} == long{albuf->mType});
1070                 memcpy(dst, data, size_t{samplen} * frame_size);
1071             }
1072         }
1073     }
1074 }
1075 END_API_FUNC
1076 
1077 
1078 AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint /*buffer*/, ALuint /*samplerate*/,
1079     ALenum /*internalformat*/, ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/,
1080     const ALvoid* /*data*/)
1081 START_API_FUNC
1082 {
1083     ContextRef context{GetContextRef()};
1084     if UNLIKELY(!context) return;
1085 
1086     context->setError(AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported");
1087 }
1088 END_API_FUNC
1089 
1090 AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/,
1091     ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, const ALvoid* /*data*/)
1092 START_API_FUNC
1093 {
1094     ContextRef context{GetContextRef()};
1095     if UNLIKELY(!context) return;
1096 
1097     context->setError(AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported");
1098 }
1099 END_API_FUNC
1100 
1101 AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/,
1102     ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, ALvoid* /*data*/)
1103 START_API_FUNC
1104 {
1105     ContextRef context{GetContextRef()};
1106     if UNLIKELY(!context) return;
1107 
1108     context->setError(AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported");
1109 }
1110 END_API_FUNC
1111 
1112 AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum /*format*/)
1113 START_API_FUNC
1114 {
1115     ContextRef context{GetContextRef()};
1116     if UNLIKELY(!context) return AL_FALSE;
1117 
1118     context->setError(AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported");
1119     return AL_FALSE;
1120 }
1121 END_API_FUNC
1122 
1123 
1124 AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat /*value*/)
1125 START_API_FUNC
1126 {
1127     ContextRef context{GetContextRef()};
1128     if UNLIKELY(!context) return;
1129 
1130     ALCdevice *device{context->mDevice.get()};
1131     std::lock_guard<std::mutex> _{device->BufferLock};
1132 
1133     if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
1134         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1135     else switch(param)
1136     {
1137     default:
1138         context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
1139     }
1140 }
1141 END_API_FUNC
1142 
1143 AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param,
1144     ALfloat /*value1*/, ALfloat /*value2*/, ALfloat /*value3*/)
1145 START_API_FUNC
1146 {
1147     ContextRef context{GetContextRef()};
1148     if UNLIKELY(!context) return;
1149 
1150     ALCdevice *device{context->mDevice.get()};
1151     std::lock_guard<std::mutex> _{device->BufferLock};
1152 
1153     if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
1154         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1155     else switch(param)
1156     {
1157     default:
1158         context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
1159     }
1160 }
1161 END_API_FUNC
1162 
1163 AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values)
1164 START_API_FUNC
1165 {
1166     ContextRef context{GetContextRef()};
1167     if UNLIKELY(!context) return;
1168 
1169     ALCdevice *device{context->mDevice.get()};
1170     std::lock_guard<std::mutex> _{device->BufferLock};
1171 
1172     if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
1173         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1174     else if UNLIKELY(!values)
1175         context->setError(AL_INVALID_VALUE, "NULL pointer");
1176     else switch(param)
1177     {
1178     default:
1179         context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
1180     }
1181 }
1182 END_API_FUNC
1183 
1184 
1185 AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
1186 START_API_FUNC
1187 {
1188     ContextRef context{GetContextRef()};
1189     if UNLIKELY(!context) return;
1190 
1191     ALCdevice *device{context->mDevice.get()};
1192     std::lock_guard<std::mutex> _{device->BufferLock};
1193 
1194     ALbuffer *albuf = LookupBuffer(device, buffer);
1195     if UNLIKELY(!albuf)
1196         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1197     else switch(param)
1198     {
1199     case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1200         if UNLIKELY(value < 0)
1201             context->setError(AL_INVALID_VALUE, "Invalid unpack block alignment %d", value);
1202         else
1203             albuf->UnpackAlign = static_cast<ALuint>(value);
1204         break;
1205 
1206     case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1207         if UNLIKELY(value < 0)
1208             context->setError(AL_INVALID_VALUE, "Invalid pack block alignment %d", value);
1209         else
1210             albuf->PackAlign = static_cast<ALuint>(value);
1211         break;
1212 
1213     case AL_AMBISONIC_LAYOUT_SOFT:
1214         if UNLIKELY(ReadRef(albuf->ref) != 0)
1215             context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic layout",
1216                 buffer);
1217         else if UNLIKELY(value != AL_FUMA_SOFT && value != AL_ACN_SOFT)
1218             context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic layout %04x", value);
1219         else
1220             albuf->mAmbiLayout = AmbiLayoutFromEnum(value).value();
1221         break;
1222 
1223     case AL_AMBISONIC_SCALING_SOFT:
1224         if UNLIKELY(ReadRef(albuf->ref) != 0)
1225             context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic scaling",
1226                 buffer);
1227         else if UNLIKELY(value != AL_FUMA_SOFT && value != AL_SN3D_SOFT && value != AL_N3D_SOFT)
1228             context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic scaling %04x", value);
1229         else
1230             albuf->mAmbiScaling = AmbiScalingFromEnum(value).value();
1231         break;
1232 
1233     case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1234         if UNLIKELY(value < 1 || value > 14)
1235             context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic order %d", value);
1236         else
1237             albuf->UnpackAmbiOrder = static_cast<ALuint>(value);
1238         break;
1239 
1240     default:
1241         context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
1242     }
1243 }
1244 END_API_FUNC
1245 
1246 AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param,
1247     ALint /*value1*/, ALint /*value2*/, ALint /*value3*/)
1248 START_API_FUNC
1249 {
1250     ContextRef context{GetContextRef()};
1251     if UNLIKELY(!context) return;
1252 
1253     ALCdevice *device{context->mDevice.get()};
1254     std::lock_guard<std::mutex> _{device->BufferLock};
1255 
1256     if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
1257         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1258     else switch(param)
1259     {
1260     default:
1261         context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
1262     }
1263 }
1264 END_API_FUNC
1265 
1266 AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values)
1267 START_API_FUNC
1268 {
1269     if(values)
1270     {
1271         switch(param)
1272         {
1273         case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1274         case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1275         case AL_AMBISONIC_LAYOUT_SOFT:
1276         case AL_AMBISONIC_SCALING_SOFT:
1277         case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1278             alBufferi(buffer, param, values[0]);
1279             return;
1280         }
1281     }
1282 
1283     ContextRef context{GetContextRef()};
1284     if UNLIKELY(!context) return;
1285 
1286     ALCdevice *device{context->mDevice.get()};
1287     std::lock_guard<std::mutex> _{device->BufferLock};
1288 
1289     ALbuffer *albuf = LookupBuffer(device, buffer);
1290     if UNLIKELY(!albuf)
1291         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1292     else if UNLIKELY(!values)
1293         context->setError(AL_INVALID_VALUE, "NULL pointer");
1294     else switch(param)
1295     {
1296     case AL_LOOP_POINTS_SOFT:
1297         if UNLIKELY(ReadRef(albuf->ref) != 0)
1298             context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's loop points",
1299                 buffer);
1300         else if UNLIKELY(values[0] < 0 || values[0] >= values[1]
1301             || static_cast<ALuint>(values[1]) > albuf->mSampleLen)
1302             context->setError(AL_INVALID_VALUE, "Invalid loop point range %d -> %d on buffer %u",
1303                 values[0], values[1], buffer);
1304         else
1305         {
1306             albuf->mLoopStart = static_cast<ALuint>(values[0]);
1307             albuf->mLoopEnd = static_cast<ALuint>(values[1]);
1308         }
1309         break;
1310 
1311     default:
1312         context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param);
1313     }
1314 }
1315 END_API_FUNC
1316 
1317 
1318 AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value)
1319 START_API_FUNC
1320 {
1321     ContextRef context{GetContextRef()};
1322     if UNLIKELY(!context) return;
1323 
1324     ALCdevice *device{context->mDevice.get()};
1325     std::lock_guard<std::mutex> _{device->BufferLock};
1326 
1327     ALbuffer *albuf = LookupBuffer(device, buffer);
1328     if UNLIKELY(!albuf)
1329         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1330     else if UNLIKELY(!value)
1331         context->setError(AL_INVALID_VALUE, "NULL pointer");
1332     else switch(param)
1333     {
1334     default:
1335         context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
1336     }
1337 }
1338 END_API_FUNC
1339 
1340 AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
1341 START_API_FUNC
1342 {
1343     ContextRef context{GetContextRef()};
1344     if UNLIKELY(!context) return;
1345 
1346     ALCdevice *device{context->mDevice.get()};
1347     std::lock_guard<std::mutex> _{device->BufferLock};
1348 
1349     if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
1350         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1351     else if UNLIKELY(!value1 || !value2 || !value3)
1352         context->setError(AL_INVALID_VALUE, "NULL pointer");
1353     else switch(param)
1354     {
1355     default:
1356         context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
1357     }
1358 }
1359 END_API_FUNC
1360 
1361 AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values)
1362 START_API_FUNC
1363 {
1364     switch(param)
1365     {
1366     case AL_SEC_LENGTH_SOFT:
1367         alGetBufferf(buffer, param, values);
1368         return;
1369     }
1370 
1371     ContextRef context{GetContextRef()};
1372     if UNLIKELY(!context) return;
1373 
1374     ALCdevice *device{context->mDevice.get()};
1375     std::lock_guard<std::mutex> _{device->BufferLock};
1376 
1377     if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
1378         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1379     else if UNLIKELY(!values)
1380         context->setError(AL_INVALID_VALUE, "NULL pointer");
1381     else switch(param)
1382     {
1383     default:
1384         context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
1385     }
1386 }
1387 END_API_FUNC
1388 
1389 
1390 AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value)
1391 START_API_FUNC
1392 {
1393     ContextRef context{GetContextRef()};
1394     if UNLIKELY(!context) return;
1395 
1396     ALCdevice *device{context->mDevice.get()};
1397     std::lock_guard<std::mutex> _{device->BufferLock};
1398     ALbuffer *albuf = LookupBuffer(device, buffer);
1399     if UNLIKELY(!albuf)
1400         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1401     else if UNLIKELY(!value)
1402         context->setError(AL_INVALID_VALUE, "NULL pointer");
1403     else switch(param)
1404     {
1405     case AL_FREQUENCY:
1406         *value = static_cast<ALint>(albuf->mSampleRate);
1407         break;
1408 
1409     case AL_BITS:
1410         *value = static_cast<ALint>(albuf->bytesFromFmt() * 8);
1411         break;
1412 
1413     case AL_CHANNELS:
1414         *value = static_cast<ALint>(albuf->channelsFromFmt());
1415         break;
1416 
1417     case AL_SIZE:
1418         *value = static_cast<ALint>(albuf->mSampleLen * albuf->frameSizeFromFmt());
1419         break;
1420 
1421     case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1422         *value = static_cast<ALint>(albuf->UnpackAlign);
1423         break;
1424 
1425     case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1426         *value = static_cast<ALint>(albuf->PackAlign);
1427         break;
1428 
1429     case AL_AMBISONIC_LAYOUT_SOFT:
1430         *value = EnumFromAmbiLayout(albuf->mAmbiLayout);
1431         break;
1432 
1433     case AL_AMBISONIC_SCALING_SOFT:
1434         *value = EnumFromAmbiScaling(albuf->mAmbiScaling);
1435         break;
1436 
1437     case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1438         *value = static_cast<int>(albuf->UnpackAmbiOrder);
1439         break;
1440 
1441     default:
1442         context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
1443     }
1444 }
1445 END_API_FUNC
1446 
1447 AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3)
1448 START_API_FUNC
1449 {
1450     ContextRef context{GetContextRef()};
1451     if UNLIKELY(!context) return;
1452 
1453     ALCdevice *device{context->mDevice.get()};
1454     std::lock_guard<std::mutex> _{device->BufferLock};
1455     if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
1456         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1457     else if UNLIKELY(!value1 || !value2 || !value3)
1458         context->setError(AL_INVALID_VALUE, "NULL pointer");
1459     else switch(param)
1460     {
1461     default:
1462         context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
1463     }
1464 }
1465 END_API_FUNC
1466 
1467 AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values)
1468 START_API_FUNC
1469 {
1470     switch(param)
1471     {
1472     case AL_FREQUENCY:
1473     case AL_BITS:
1474     case AL_CHANNELS:
1475     case AL_SIZE:
1476     case AL_INTERNAL_FORMAT_SOFT:
1477     case AL_BYTE_LENGTH_SOFT:
1478     case AL_SAMPLE_LENGTH_SOFT:
1479     case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1480     case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1481     case AL_AMBISONIC_LAYOUT_SOFT:
1482     case AL_AMBISONIC_SCALING_SOFT:
1483     case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1484         alGetBufferi(buffer, param, values);
1485         return;
1486     }
1487 
1488     ContextRef context{GetContextRef()};
1489     if UNLIKELY(!context) return;
1490 
1491     ALCdevice *device{context->mDevice.get()};
1492     std::lock_guard<std::mutex> _{device->BufferLock};
1493     ALbuffer *albuf = LookupBuffer(device, buffer);
1494     if UNLIKELY(!albuf)
1495         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1496     else if UNLIKELY(!values)
1497         context->setError(AL_INVALID_VALUE, "NULL pointer");
1498     else switch(param)
1499     {
1500     case AL_LOOP_POINTS_SOFT:
1501         values[0] = static_cast<ALint>(albuf->mLoopStart);
1502         values[1] = static_cast<ALint>(albuf->mLoopEnd);
1503         break;
1504 
1505     default:
1506         context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param);
1507     }
1508 }
1509 END_API_FUNC
1510 
1511 
1512 AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq,
1513     LPALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr, ALbitfieldSOFT flags)
1514 START_API_FUNC
1515 {
1516     ContextRef context{GetContextRef()};
1517     if UNLIKELY(!context) return;
1518 
1519     ALCdevice *device{context->mDevice.get()};
1520     std::lock_guard<std::mutex> _{device->BufferLock};
1521 
1522     ALbuffer *albuf = LookupBuffer(device, buffer);
1523     if UNLIKELY(!albuf)
1524         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1525     else if UNLIKELY(freq < 1)
1526         context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
1527     else if UNLIKELY(callback == nullptr)
1528         context->setError(AL_INVALID_VALUE, "NULL callback");
1529     else if UNLIKELY(flags != 0)
1530         context->setError(AL_INVALID_VALUE, "Invalid callback flags 0x%x", flags);
1531     else
1532     {
1533         auto usrfmt = DecomposeUserFormat(format);
1534         if UNLIKELY(!usrfmt)
1535             context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
1536         else
1537             PrepareCallback(context.get(), albuf, freq, usrfmt->channels, usrfmt->type, callback,
1538                 userptr);
1539     }
1540 }
1541 END_API_FUNC
1542 
1543 AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **value)
1544 START_API_FUNC
1545 {
1546     ContextRef context{GetContextRef()};
1547     if UNLIKELY(!context) return;
1548 
1549     ALCdevice *device{context->mDevice.get()};
1550     std::lock_guard<std::mutex> _{device->BufferLock};
1551     ALbuffer *albuf = LookupBuffer(device, buffer);
1552     if UNLIKELY(!albuf)
1553         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1554     else if UNLIKELY(!value)
1555         context->setError(AL_INVALID_VALUE, "NULL pointer");
1556     else switch(param)
1557     {
1558     case AL_BUFFER_CALLBACK_FUNCTION_SOFT:
1559         *value = reinterpret_cast<void*>(albuf->mCallback);
1560         break;
1561     case AL_BUFFER_CALLBACK_USER_PARAM_SOFT:
1562         *value = albuf->mUserData;
1563         break;
1564 
1565     default:
1566         context->setError(AL_INVALID_ENUM, "Invalid buffer pointer property 0x%04x", param);
1567     }
1568 }
1569 END_API_FUNC
1570 
1571 AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3)
1572 START_API_FUNC
1573 {
1574     ContextRef context{GetContextRef()};
1575     if UNLIKELY(!context) return;
1576 
1577     ALCdevice *device{context->mDevice.get()};
1578     std::lock_guard<std::mutex> _{device->BufferLock};
1579     if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
1580         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1581     else if UNLIKELY(!value1 || !value2 || !value3)
1582         context->setError(AL_INVALID_VALUE, "NULL pointer");
1583     else switch(param)
1584     {
1585     default:
1586         context->setError(AL_INVALID_ENUM, "Invalid buffer 3-pointer property 0x%04x", param);
1587     }
1588 }
1589 END_API_FUNC
1590 
1591 AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **values)
1592 START_API_FUNC
1593 {
1594     switch(param)
1595     {
1596     case AL_BUFFER_CALLBACK_FUNCTION_SOFT:
1597     case AL_BUFFER_CALLBACK_USER_PARAM_SOFT:
1598         alGetBufferPtrSOFT(buffer, param, values);
1599         return;
1600     }
1601 
1602     ContextRef context{GetContextRef()};
1603     if UNLIKELY(!context) return;
1604 
1605     ALCdevice *device{context->mDevice.get()};
1606     std::lock_guard<std::mutex> _{device->BufferLock};
1607     if UNLIKELY(LookupBuffer(device, buffer) == nullptr)
1608         context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1609     else if UNLIKELY(!values)
1610         context->setError(AL_INVALID_VALUE, "NULL pointer");
1611     else switch(param)
1612     {
1613     default:
1614         context->setError(AL_INVALID_ENUM, "Invalid buffer pointer-vector property 0x%04x", param);
1615     }
1616 }
1617 END_API_FUNC
1618 
1619 
1620 BufferSubList::~BufferSubList()
1621 {
1622     uint64_t usemask{~FreeMask};
1623     while(usemask)
1624     {
1625         const int idx{al::countr_zero(usemask)};
1626         al::destroy_at(Buffers+idx);
1627         usemask &= ~(1_u64 << idx);
1628     }
1629     FreeMask = ~usemask;
1630     al_free(Buffers);
1631     Buffers = nullptr;
1632 }
1633