Try to improve alffplay underrun device time adjustment
[openal-soft.git] / OpenAL32 / alBuffer.cpp
blobb08518bac6418f4ee5229d18f66a9e644ab14775
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.
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.
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
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #ifdef HAVE_MALLOC_H
27 #include <malloc.h>
28 #endif
30 #include <tuple>
31 #include <array>
32 #include <vector>
33 #include <limits>
34 #include <algorithm>
36 #include "alMain.h"
37 #include "alcontext.h"
38 #include "alu.h"
39 #include "alError.h"
40 #include "alBuffer.h"
41 #include "sample_cvt.h"
44 namespace {
46 constexpr ALbitfieldSOFT INVALID_STORAGE_MASK{~unsigned(AL_MAP_READ_BIT_SOFT |
47 AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT)};
48 constexpr ALbitfieldSOFT MAP_READ_WRITE_FLAGS{AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT};
49 constexpr ALbitfieldSOFT INVALID_MAP_FLAGS{~unsigned(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT |
50 AL_MAP_PERSISTENT_BIT_SOFT)};
53 ALbuffer *AllocBuffer(ALCcontext *context)
55 ALCdevice *device = context->Device;
56 std::unique_lock<almtx_t> buflock{device->BufferLock};
58 auto sublist = std::find_if(device->BufferList.begin(), device->BufferList.end(),
59 [](const BufferSubList &entry) noexcept -> bool
60 { return entry.FreeMask != 0; }
63 auto lidx = std::distance(device->BufferList.begin(), sublist);
64 ALbuffer *buffer{nullptr};
65 ALsizei slidx{0};
66 if(LIKELY(sublist != device->BufferList.end()))
68 slidx = CTZ64(sublist->FreeMask);
69 buffer = sublist->Buffers + slidx;
71 else
73 /* Don't allocate so many list entries that the 32-bit ID could
74 * overflow...
76 if(UNLIKELY(device->BufferList.size() >= 1<<25))
78 buflock.unlock();
79 alSetError(context, AL_OUT_OF_MEMORY, "Too many buffers allocated");
80 return nullptr;
82 device->BufferList.emplace_back();
83 sublist = device->BufferList.end() - 1;
84 sublist->FreeMask = ~U64(0);
85 sublist->Buffers = reinterpret_cast<ALbuffer*>(al_calloc(16, sizeof(ALbuffer)*64));
86 if(UNLIKELY(!sublist->Buffers))
88 device->BufferList.pop_back();
89 buflock.unlock();
90 alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate buffer batch");
91 return nullptr;
94 slidx = 0;
95 buffer = sublist->Buffers + slidx;
98 buffer = new (buffer) ALbuffer{};
99 /* Add 1 to avoid buffer ID 0. */
100 buffer->id = ((lidx<<6) | slidx) + 1;
102 sublist->FreeMask &= ~(U64(1)<<slidx);
104 return buffer;
107 void FreeBuffer(ALCdevice *device, ALbuffer *buffer)
109 ALuint id{buffer->id - 1};
110 ALsizei lidx = id >> 6;
111 ALsizei slidx = id & 0x3f;
113 al_free(buffer->data);
114 buffer->data = nullptr;
115 buffer->~ALbuffer();
117 device->BufferList[lidx].FreeMask |= U64(1) << slidx;
120 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
122 ALuint lidx = (id-1) >> 6;
123 ALsizei slidx = (id-1) & 0x3f;
125 if(UNLIKELY(lidx >= device->BufferList.size()))
126 return nullptr;
127 BufferSubList &sublist = device->BufferList[lidx];
128 if(UNLIKELY(sublist.FreeMask & (U64(1)<<slidx)))
129 return nullptr;
130 return sublist.Buffers + slidx;
134 ALsizei SanitizeAlignment(UserFmtType type, ALsizei align)
136 if(align < 0)
137 return 0;
139 if(align == 0)
141 if(type == UserFmtIMA4)
143 /* Here is where things vary:
144 * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel
145 * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel
147 return 65;
149 if(type == UserFmtMSADPCM)
150 return 64;
151 return 1;
154 if(type == UserFmtIMA4)
156 /* IMA4 block alignment must be a multiple of 8, plus 1. */
157 if((align&7) == 1) return align;
158 return 0;
160 if(type == UserFmtMSADPCM)
162 /* MSADPCM block alignment must be a multiple of 2. */
163 if((align&1) == 0) return align;
164 return 0;
167 return align;
171 const ALchar *NameFromUserFmtType(UserFmtType type)
173 switch(type)
175 case UserFmtUByte: return "Unsigned Byte";
176 case UserFmtShort: return "Signed Short";
177 case UserFmtFloat: return "Float32";
178 case UserFmtDouble: return "Float64";
179 case UserFmtMulaw: return "muLaw";
180 case UserFmtAlaw: return "aLaw";
181 case UserFmtIMA4: return "IMA4 ADPCM";
182 case UserFmtMSADPCM: return "MSADPCM";
184 return "<internal type error>";
188 * LoadData
190 * Loads the specified data into the buffer, using the specified format.
192 void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALuint freq, ALsizei size, UserFmtChannels SrcChannels, UserFmtType SrcType, const ALvoid *data, ALbitfieldSOFT access)
194 if(UNLIKELY(ReadRef(&ALBuf->ref) != 0 || ALBuf->MappedAccess != 0))
195 SETERR_RETURN(context, AL_INVALID_OPERATION,, "Modifying storage for in-use buffer %u",
196 ALBuf->id);
198 /* Currently no channel configurations need to be converted. */
199 FmtChannels DstChannels{FmtMono};
200 switch(SrcChannels)
202 case UserFmtMono: DstChannels = FmtMono; break;
203 case UserFmtStereo: DstChannels = FmtStereo; break;
204 case UserFmtRear: DstChannels = FmtRear; break;
205 case UserFmtQuad: DstChannels = FmtQuad; break;
206 case UserFmtX51: DstChannels = FmtX51; break;
207 case UserFmtX61: DstChannels = FmtX61; break;
208 case UserFmtX71: DstChannels = FmtX71; break;
209 case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
210 case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
212 if(UNLIKELY((long)SrcChannels != (long)DstChannels))
213 SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid format");
215 /* IMA4 and MSADPCM convert to 16-bit short. */
216 FmtType DstType{FmtUByte};
217 switch(SrcType)
219 case UserFmtUByte: DstType = FmtUByte; break;
220 case UserFmtShort: DstType = FmtShort; break;
221 case UserFmtFloat: DstType = FmtFloat; break;
222 case UserFmtDouble: DstType = FmtDouble; break;
223 case UserFmtAlaw: DstType = FmtAlaw; break;
224 case UserFmtMulaw: DstType = FmtMulaw; break;
225 case UserFmtIMA4: DstType = FmtShort; break;
226 case UserFmtMSADPCM: DstType = FmtShort; break;
229 /* TODO: Currently we can only map samples when they're not converted. To
230 * allow it would need some kind of double-buffering to hold onto a copy of
231 * the original data.
233 if((access&MAP_READ_WRITE_FLAGS))
235 if(UNLIKELY((long)SrcType != (long)DstType))
236 SETERR_RETURN(context, AL_INVALID_VALUE,, "%s samples cannot be mapped",
237 NameFromUserFmtType(SrcType));
240 ALsizei unpackalign{ATOMIC_LOAD_SEQ(&ALBuf->UnpackAlign)};
241 ALsizei align{SanitizeAlignment(SrcType, unpackalign)};
242 if(UNLIKELY(align < 1))
243 SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid unpack alignment %d for %s samples",
244 unpackalign, NameFromUserFmtType(SrcType));
246 if((access&AL_PRESERVE_DATA_BIT_SOFT))
248 /* Can only preserve data with the same format and alignment. */
249 if(UNLIKELY(ALBuf->FmtChannels != DstChannels || ALBuf->OriginalType != SrcType))
250 SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched format");
251 if(UNLIKELY(ALBuf->OriginalAlign != align))
252 SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched alignment");
255 /* Convert the input/source size in bytes to sample frames using the unpack
256 * block alignment.
258 ALsizei SrcByteAlign{
259 (SrcType == UserFmtIMA4) ? ((align-1)/2 + 4) * ChannelsFromUserFmt(SrcChannels) :
260 (SrcType == UserFmtMSADPCM) ? ((align-2)/2 + 7) * ChannelsFromUserFmt(SrcChannels) :
261 (align * FrameSizeFromUserFmt(SrcChannels, SrcType))
263 if(UNLIKELY((size%SrcByteAlign) != 0))
264 SETERR_RETURN(context, AL_INVALID_VALUE,,
265 "Data size %d is not a multiple of frame size %d (%d unpack alignment)",
266 size, SrcByteAlign, align);
268 if(UNLIKELY(size/SrcByteAlign > std::numeric_limits<ALsizei>::max()/align))
269 SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
270 "Buffer size overflow, %d blocks x %d samples per block", size/SrcByteAlign, align);
271 ALsizei frames{size / SrcByteAlign * align};
273 /* Convert the sample frames to the number of bytes needed for internal
274 * storage.
276 ALsizei NumChannels{ChannelsFromFmt(DstChannels)};
277 ALsizei FrameSize{NumChannels * BytesFromFmt(DstType)};
278 if(UNLIKELY(frames > std::numeric_limits<ALsizei>::max()/FrameSize))
279 SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
280 "Buffer size overflow, %d frames x %d bytes per frame", frames, FrameSize);
281 ALsizei newsize{frames*FrameSize};
283 /* Round up to the next 16-byte multiple. This could reallocate only when
284 * increasing or the new size is less than half the current, but then the
285 * buffer's AL_SIZE would not be very reliable for accounting buffer memory
286 * usage, and reporting the real size could cause problems for apps that
287 * use AL_SIZE to try to get the buffer's play length.
289 if(LIKELY(newsize <= std::numeric_limits<ALsizei>::max()-15))
290 newsize = (newsize+15) & ~0xf;
291 if(newsize != ALBuf->BytesAlloc)
293 void *temp{al_malloc(16, (size_t)newsize)};
294 if(UNLIKELY(!temp && newsize))
295 SETERR_RETURN(context, AL_OUT_OF_MEMORY,, "Failed to allocate %d bytes of storage",
296 newsize);
297 if((access&AL_PRESERVE_DATA_BIT_SOFT))
299 ALsizei tocopy{std::min(newsize, ALBuf->BytesAlloc)};
300 if(tocopy > 0) memcpy(temp, ALBuf->data, tocopy);
302 al_free(ALBuf->data);
303 ALBuf->data = temp;
304 ALBuf->BytesAlloc = newsize;
307 if(SrcType == UserFmtIMA4)
309 assert(DstType == FmtShort);
310 if(data != nullptr && ALBuf->data != nullptr)
311 Convert_ALshort_ALima4(static_cast<ALshort*>(ALBuf->data),
312 static_cast<const ALubyte*>(data), NumChannels, frames, align);
313 ALBuf->OriginalAlign = align;
315 else if(SrcType == UserFmtMSADPCM)
317 assert(DstType == FmtShort);
318 if(data != nullptr && ALBuf->data != nullptr)
319 Convert_ALshort_ALmsadpcm(static_cast<ALshort*>(ALBuf->data),
320 static_cast<const ALubyte*>(data), NumChannels, frames, align);
321 ALBuf->OriginalAlign = align;
323 else
325 assert((long)SrcType == (long)DstType);
326 if(data != nullptr && ALBuf->data != nullptr)
327 memcpy(ALBuf->data, data, frames*FrameSize);
328 ALBuf->OriginalAlign = 1;
330 ALBuf->OriginalSize = size;
331 ALBuf->OriginalType = SrcType;
333 ALBuf->Frequency = freq;
334 ALBuf->FmtChannels = DstChannels;
335 ALBuf->FmtType = DstType;
336 ALBuf->Access = access;
338 ALBuf->SampleLen = frames;
339 ALBuf->LoopStart = 0;
340 ALBuf->LoopEnd = ALBuf->SampleLen;
343 using DecompResult = std::tuple<bool, UserFmtChannels, UserFmtType>;
344 DecompResult DecomposeUserFormat(ALenum format)
346 struct FormatMap {
347 ALenum format;
348 UserFmtChannels channels;
349 UserFmtType type;
351 static constexpr std::array<FormatMap,46> UserFmtList{{
352 { AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte },
353 { AL_FORMAT_MONO16, UserFmtMono, UserFmtShort },
354 { AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat },
355 { AL_FORMAT_MONO_DOUBLE_EXT, UserFmtMono, UserFmtDouble },
356 { AL_FORMAT_MONO_IMA4, UserFmtMono, UserFmtIMA4 },
357 { AL_FORMAT_MONO_MSADPCM_SOFT, UserFmtMono, UserFmtMSADPCM },
358 { AL_FORMAT_MONO_MULAW, UserFmtMono, UserFmtMulaw },
359 { AL_FORMAT_MONO_ALAW_EXT, UserFmtMono, UserFmtAlaw },
361 { AL_FORMAT_STEREO8, UserFmtStereo, UserFmtUByte },
362 { AL_FORMAT_STEREO16, UserFmtStereo, UserFmtShort },
363 { AL_FORMAT_STEREO_FLOAT32, UserFmtStereo, UserFmtFloat },
364 { AL_FORMAT_STEREO_DOUBLE_EXT, UserFmtStereo, UserFmtDouble },
365 { AL_FORMAT_STEREO_IMA4, UserFmtStereo, UserFmtIMA4 },
366 { AL_FORMAT_STEREO_MSADPCM_SOFT, UserFmtStereo, UserFmtMSADPCM },
367 { AL_FORMAT_STEREO_MULAW, UserFmtStereo, UserFmtMulaw },
368 { AL_FORMAT_STEREO_ALAW_EXT, UserFmtStereo, UserFmtAlaw },
370 { AL_FORMAT_REAR8, UserFmtRear, UserFmtUByte },
371 { AL_FORMAT_REAR16, UserFmtRear, UserFmtShort },
372 { AL_FORMAT_REAR32, UserFmtRear, UserFmtFloat },
373 { AL_FORMAT_REAR_MULAW, UserFmtRear, UserFmtMulaw },
375 { AL_FORMAT_QUAD8_LOKI, UserFmtQuad, UserFmtUByte },
376 { AL_FORMAT_QUAD16_LOKI, UserFmtQuad, UserFmtShort },
378 { AL_FORMAT_QUAD8, UserFmtQuad, UserFmtUByte },
379 { AL_FORMAT_QUAD16, UserFmtQuad, UserFmtShort },
380 { AL_FORMAT_QUAD32, UserFmtQuad, UserFmtFloat },
381 { AL_FORMAT_QUAD_MULAW, UserFmtQuad, UserFmtMulaw },
383 { AL_FORMAT_51CHN8, UserFmtX51, UserFmtUByte },
384 { AL_FORMAT_51CHN16, UserFmtX51, UserFmtShort },
385 { AL_FORMAT_51CHN32, UserFmtX51, UserFmtFloat },
386 { AL_FORMAT_51CHN_MULAW, UserFmtX51, UserFmtMulaw },
388 { AL_FORMAT_61CHN8, UserFmtX61, UserFmtUByte },
389 { AL_FORMAT_61CHN16, UserFmtX61, UserFmtShort },
390 { AL_FORMAT_61CHN32, UserFmtX61, UserFmtFloat },
391 { AL_FORMAT_61CHN_MULAW, UserFmtX61, UserFmtMulaw },
393 { AL_FORMAT_71CHN8, UserFmtX71, UserFmtUByte },
394 { AL_FORMAT_71CHN16, UserFmtX71, UserFmtShort },
395 { AL_FORMAT_71CHN32, UserFmtX71, UserFmtFloat },
396 { AL_FORMAT_71CHN_MULAW, UserFmtX71, UserFmtMulaw },
398 { AL_FORMAT_BFORMAT2D_8, UserFmtBFormat2D, UserFmtUByte },
399 { AL_FORMAT_BFORMAT2D_16, UserFmtBFormat2D, UserFmtShort },
400 { AL_FORMAT_BFORMAT2D_FLOAT32, UserFmtBFormat2D, UserFmtFloat },
401 { AL_FORMAT_BFORMAT2D_MULAW, UserFmtBFormat2D, UserFmtMulaw },
403 { AL_FORMAT_BFORMAT3D_8, UserFmtBFormat3D, UserFmtUByte },
404 { AL_FORMAT_BFORMAT3D_16, UserFmtBFormat3D, UserFmtShort },
405 { AL_FORMAT_BFORMAT3D_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
406 { AL_FORMAT_BFORMAT3D_MULAW, UserFmtBFormat3D, UserFmtMulaw },
409 DecompResult ret{};
410 for(const auto &fmt : UserFmtList)
412 if(fmt.format == format)
414 std::get<0>(ret) = true;
415 std::get<1>(ret) = fmt.channels;
416 std::get<2>(ret) = fmt.type;
417 break;
420 return ret;
423 } // namespace
426 AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers)
428 ContextRef context{GetContextRef()};
429 if(UNLIKELY(!context)) return;
431 if(UNLIKELY(n < 0))
433 alSetError(context.get(), AL_INVALID_VALUE, "Generating %d buffers", n);
434 return;
437 if(LIKELY(n == 1))
439 /* Special handling for the easy and normal case. */
440 ALbuffer *buffer = AllocBuffer(context.get());
441 if(buffer) buffers[0] = buffer->id;
443 else if(n > 1)
445 /* Store the allocated buffer IDs in a separate local list, to avoid
446 * modifying the user storage in case of failure.
448 std::vector<ALuint> ids;
449 ids.reserve(n);
450 do {
451 ALbuffer *buffer = AllocBuffer(context.get());
452 if(!buffer)
454 alDeleteBuffers(ids.size(), ids.data());
455 return;
458 ids.emplace_back(buffer->id);
459 } while(--n);
460 std::copy(ids.begin(), ids.end(), buffers);
464 AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
466 ContextRef context{GetContextRef()};
467 if(UNLIKELY(!context)) return;
469 if(UNLIKELY(n < 0))
471 alSetError(context.get(), AL_INVALID_VALUE, "Deleting %d buffers", n);
472 return;
474 if(UNLIKELY(n == 0))
475 return;
477 ALCdevice *device = context->Device;
478 std::lock_guard<almtx_t> _{device->BufferLock};
480 /* First try to find any buffers that are invalid or in-use. */
481 const ALuint *buffers_end = buffers + n;
482 auto invbuf = std::find_if(buffers, buffers_end,
483 [device, &context](ALuint bid) -> bool
485 if(!bid) return false;
486 ALbuffer *ALBuf = LookupBuffer(device, bid);
487 if(UNLIKELY(!ALBuf))
489 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", bid);
490 return true;
492 if(UNLIKELY(ReadRef(&ALBuf->ref) != 0))
494 alSetError(context.get(), AL_INVALID_OPERATION, "Deleting in-use buffer %u", bid);
495 return true;
497 return false;
500 if(LIKELY(invbuf == buffers_end))
502 /* All good. Delete non-0 buffer IDs. */
503 std::for_each(buffers, buffers_end,
504 [device](ALuint bid) -> void
505 { if(bid) FreeBuffer(device, LookupBuffer(device, bid)); }
510 AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
512 ContextRef context{GetContextRef()};
513 if(LIKELY(context))
515 ALCdevice *device = context->Device;
516 std::lock_guard<almtx_t> _{device->BufferLock};
517 if(!buffer || LookupBuffer(device, buffer))
518 return AL_TRUE;
520 return AL_FALSE;
524 AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
525 { alBufferStorageSOFT(buffer, format, data, size, freq, 0); }
527 AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags)
529 ContextRef context{GetContextRef()};
530 if(UNLIKELY(!context)) return;
532 ALCdevice *device = context->Device;
533 std::lock_guard<almtx_t> _{device->BufferLock};
535 ALbuffer *albuf = LookupBuffer(device, buffer);
536 if(UNLIKELY(!albuf))
537 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
538 else if(UNLIKELY(size < 0))
539 alSetError(context.get(), AL_INVALID_VALUE, "Negative storage size %d", size);
540 else if(UNLIKELY(freq < 1))
541 alSetError(context.get(), AL_INVALID_VALUE, "Invalid sample rate %d", freq);
542 else if(UNLIKELY((flags&INVALID_STORAGE_MASK) != 0))
543 alSetError(context.get(), AL_INVALID_VALUE, "Invalid storage flags 0x%x",
544 flags&INVALID_STORAGE_MASK);
545 else if(UNLIKELY((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS)))
546 alSetError(context.get(), AL_INVALID_VALUE,
547 "Declaring persistently mapped storage without read or write access");
548 else
550 UserFmtType srctype{UserFmtUByte};
551 UserFmtChannels srcchannels{UserFmtMono};
552 bool success;
554 std::tie(success, srcchannels, srctype) = DecomposeUserFormat(format);
555 if(UNLIKELY(!success))
556 alSetError(context.get(), AL_INVALID_ENUM, "Invalid format 0x%04x", format);
557 else
558 LoadData(context.get(), albuf, freq, size, srcchannels, srctype, data, flags);
562 AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access)
564 ContextRef context{GetContextRef()};
565 if(UNLIKELY(!context)) return nullptr;
567 ALCdevice *device = context->Device;
568 std::lock_guard<almtx_t> _{device->BufferLock};
570 ALbuffer *albuf = LookupBuffer(device, buffer);
571 if(UNLIKELY(!albuf))
572 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
573 else if(UNLIKELY((access&INVALID_MAP_FLAGS) != 0))
574 alSetError(context.get(), AL_INVALID_VALUE, "Invalid map flags 0x%x", access&INVALID_MAP_FLAGS);
575 else if(UNLIKELY(!(access&MAP_READ_WRITE_FLAGS)))
576 alSetError(context.get(), AL_INVALID_VALUE, "Mapping buffer %u without read or write access",
577 buffer);
578 else
580 ALbitfieldSOFT unavailable = (albuf->Access^access) & access;
581 if(UNLIKELY(ReadRef(&albuf->ref) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT)))
582 alSetError(context.get(), AL_INVALID_OPERATION,
583 "Mapping in-use buffer %u without persistent mapping", buffer);
584 else if(UNLIKELY(albuf->MappedAccess != 0))
585 alSetError(context.get(), AL_INVALID_OPERATION, "Mapping already-mapped buffer %u", buffer);
586 else if(UNLIKELY((unavailable&AL_MAP_READ_BIT_SOFT)))
587 alSetError(context.get(), AL_INVALID_VALUE,
588 "Mapping buffer %u for reading without read access", buffer);
589 else if(UNLIKELY((unavailable&AL_MAP_WRITE_BIT_SOFT)))
590 alSetError(context.get(), AL_INVALID_VALUE,
591 "Mapping buffer %u for writing without write access", buffer);
592 else if(UNLIKELY((unavailable&AL_MAP_PERSISTENT_BIT_SOFT)))
593 alSetError(context.get(), AL_INVALID_VALUE,
594 "Mapping buffer %u persistently without persistent access", buffer);
595 else if(UNLIKELY(offset < 0 || offset >= albuf->OriginalSize ||
596 length <= 0 || length > albuf->OriginalSize - offset))
597 alSetError(context.get(), AL_INVALID_VALUE, "Mapping invalid range %d+%d for buffer %u",
598 offset, length, buffer);
599 else
601 void *retval = (ALbyte*)albuf->data + offset;
602 albuf->MappedAccess = access;
603 albuf->MappedOffset = offset;
604 albuf->MappedSize = length;
605 return retval;
609 return nullptr;
612 AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer)
614 ContextRef context{GetContextRef()};
615 if(UNLIKELY(!context)) return;
617 ALCdevice *device = context->Device;
618 std::lock_guard<almtx_t> _{device->BufferLock};
620 ALbuffer *albuf = LookupBuffer(device, buffer);
621 if(UNLIKELY(!albuf))
622 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
623 else if(albuf->MappedAccess == 0)
624 alSetError(context.get(), AL_INVALID_OPERATION, "Unmapping unmapped buffer %u", buffer);
625 else
627 albuf->MappedAccess = 0;
628 albuf->MappedOffset = 0;
629 albuf->MappedSize = 0;
633 AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length)
635 ContextRef context{GetContextRef()};
636 if(UNLIKELY(!context)) return;
638 ALCdevice *device = context->Device;
639 std::lock_guard<almtx_t> _{device->BufferLock};
641 ALbuffer *albuf = LookupBuffer(device, buffer);
642 if(UNLIKELY(!albuf))
643 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
644 else if(UNLIKELY(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT)))
645 alSetError(context.get(), AL_INVALID_OPERATION,
646 "Flushing buffer %u while not mapped for writing", buffer);
647 else if(UNLIKELY(offset < albuf->MappedOffset ||
648 offset >= albuf->MappedOffset+albuf->MappedSize ||
649 length <= 0 || length > albuf->MappedOffset+albuf->MappedSize-offset))
650 alSetError(context.get(), AL_INVALID_VALUE, "Flushing invalid range %d+%d on buffer %u",
651 offset, length, buffer);
652 else
654 /* FIXME: Need to use some method of double-buffering for the mixer and
655 * app to hold separate memory, which can be safely transfered
656 * asynchronously. Currently we just say the app shouldn't write where
657 * OpenAL's reading, and hope for the best...
659 std::atomic_thread_fence(std::memory_order_seq_cst);
663 AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length)
665 ContextRef context{GetContextRef()};
666 if(UNLIKELY(!context)) return;
668 ALCdevice *device = context->Device;
669 std::lock_guard<almtx_t> _{device->BufferLock};
671 ALbuffer *albuf = LookupBuffer(device, buffer);
672 if(UNLIKELY(!albuf))
674 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
675 return;
678 UserFmtType srctype{UserFmtUByte};
679 UserFmtChannels srcchannels{UserFmtMono};
680 bool success;
681 std::tie(success, srcchannels, srctype) = DecomposeUserFormat(format);
682 if(UNLIKELY(!success))
684 alSetError(context.get(), AL_INVALID_ENUM, "Invalid format 0x%04x", format);
685 return;
688 ALsizei unpack_align{ATOMIC_LOAD_SEQ(&albuf->UnpackAlign)};
689 ALsizei align{SanitizeAlignment(srctype, unpack_align)};
690 if(UNLIKELY(align < 1))
691 alSetError(context.get(), AL_INVALID_VALUE, "Invalid unpack alignment %d", unpack_align);
692 else if(UNLIKELY((long)srcchannels != (long)albuf->FmtChannels ||
693 srctype != albuf->OriginalType))
694 alSetError(context.get(), AL_INVALID_ENUM, "Unpacking data with mismatched format");
695 else if(UNLIKELY(align != albuf->OriginalAlign))
696 alSetError(context.get(), AL_INVALID_VALUE,
697 "Unpacking data with alignment %u does not match original alignment %u",
698 align, albuf->OriginalAlign);
699 else if(UNLIKELY(albuf->MappedAccess != 0))
700 alSetError(context.get(), AL_INVALID_OPERATION, "Unpacking data into mapped buffer %u",
701 buffer);
702 else
704 ALsizei num_chans{ChannelsFromFmt(albuf->FmtChannels)};
705 ALsizei frame_size{num_chans * BytesFromFmt(albuf->FmtType)};
706 ALsizei byte_align{
707 (albuf->OriginalType == UserFmtIMA4) ? ((align-1)/2 + 4) * num_chans :
708 (albuf->OriginalType == UserFmtMSADPCM) ? ((align-2)/2 + 7) * num_chans :
709 (align * frame_size)
712 if(UNLIKELY(offset < 0 || length < 0 || offset > albuf->OriginalSize ||
713 length > albuf->OriginalSize-offset))
714 alSetError(context.get(), AL_INVALID_VALUE, "Invalid data sub-range %d+%d on buffer %u",
715 offset, length, buffer);
716 else if(UNLIKELY((offset%byte_align) != 0))
717 alSetError(context.get(), AL_INVALID_VALUE,
718 "Sub-range offset %d is not a multiple of frame size %d (%d unpack alignment)",
719 offset, byte_align, align);
720 else if(UNLIKELY((length%byte_align) != 0))
721 alSetError(context.get(), AL_INVALID_VALUE,
722 "Sub-range length %d is not a multiple of frame size %d (%d unpack alignment)",
723 length, byte_align, align);
724 else
726 /* offset -> byte offset, length -> sample count */
727 offset = offset/byte_align * align * frame_size;
728 length = length/byte_align * align;
730 void *dst = static_cast<ALbyte*>(albuf->data) + offset;
731 if(srctype == UserFmtIMA4 && albuf->FmtType == FmtShort)
732 Convert_ALshort_ALima4(static_cast<ALshort*>(dst),
733 static_cast<const ALubyte*>(data), num_chans, length, align);
734 else if(srctype == UserFmtMSADPCM && albuf->FmtType == FmtShort)
735 Convert_ALshort_ALmsadpcm(static_cast<ALshort*>(dst),
736 static_cast<const ALubyte*>(data), num_chans, length, align);
737 else
739 assert((long)srctype == (long)albuf->FmtType);
740 memcpy(dst, data, length*frame_size);
747 AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint UNUSED(buffer),
748 ALuint UNUSED(samplerate), ALenum UNUSED(internalformat), ALsizei UNUSED(samples),
749 ALenum UNUSED(channels), ALenum UNUSED(type), const ALvoid *UNUSED(data))
751 ContextRef context{GetContextRef()};
752 if(UNLIKELY(!context)) return;
754 alSetError(context.get(), AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported");
757 AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint UNUSED(buffer),
758 ALsizei UNUSED(offset), ALsizei UNUSED(samples),
759 ALenum UNUSED(channels), ALenum UNUSED(type), const ALvoid *UNUSED(data))
761 ContextRef context{GetContextRef()};
762 if(UNLIKELY(!context)) return;
764 alSetError(context.get(), AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported");
767 AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint UNUSED(buffer),
768 ALsizei UNUSED(offset), ALsizei UNUSED(samples),
769 ALenum UNUSED(channels), ALenum UNUSED(type), ALvoid *UNUSED(data))
771 ContextRef context{GetContextRef()};
772 if(UNLIKELY(!context)) return;
774 alSetError(context.get(), AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported");
777 AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum UNUSED(format))
779 ContextRef context{GetContextRef()};
780 if(!context) return AL_FALSE;
782 alSetError(context.get(), AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported");
783 return AL_FALSE;
787 AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(value))
789 ContextRef context{GetContextRef()};
790 if(UNLIKELY(!context)) return;
792 ALCdevice *device = context->Device;
793 std::lock_guard<almtx_t> _{device->BufferLock};
795 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
796 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
797 else switch(param)
799 default:
800 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
805 AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(value1), ALfloat UNUSED(value2), ALfloat UNUSED(value3))
807 ContextRef context{GetContextRef()};
808 if(UNLIKELY(!context)) return;
810 ALCdevice *device = context->Device;
811 std::lock_guard<almtx_t> _{device->BufferLock};
813 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
814 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
815 else switch(param)
817 default:
818 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
823 AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values)
825 ContextRef context{GetContextRef()};
826 if(UNLIKELY(!context)) return;
828 ALCdevice *device = context->Device;
829 std::lock_guard<almtx_t> _{device->BufferLock};
831 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
832 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
833 else if(UNLIKELY(!values))
834 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
835 else switch(param)
837 default:
838 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
843 AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
845 ContextRef context{GetContextRef()};
846 if(UNLIKELY(!context)) return;
848 ALCdevice *device = context->Device;
849 std::lock_guard<almtx_t> _{device->BufferLock};
851 ALbuffer *albuf = LookupBuffer(device, buffer);
852 if(UNLIKELY(!albuf))
853 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
854 else switch(param)
856 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
857 if(UNLIKELY(value < 0))
858 alSetError(context.get(), AL_INVALID_VALUE, "Invalid unpack block alignment %d", value);
859 else
860 ATOMIC_STORE_SEQ(&albuf->UnpackAlign, value);
861 break;
863 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
864 if(UNLIKELY(value < 0))
865 alSetError(context.get(), AL_INVALID_VALUE, "Invalid pack block alignment %d", value);
866 else
867 ATOMIC_STORE_SEQ(&albuf->PackAlign, value);
868 break;
870 default:
871 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
876 AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint UNUSED(value1), ALint UNUSED(value2), ALint UNUSED(value3))
878 ContextRef context{GetContextRef()};
879 if(UNLIKELY(!context)) return;
881 ALCdevice *device = context->Device;
882 std::lock_guard<almtx_t> _{device->BufferLock};
884 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
885 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
886 else switch(param)
888 default:
889 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
894 AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values)
896 if(values)
898 switch(param)
900 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
901 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
902 alBufferi(buffer, param, values[0]);
903 return;
907 ContextRef context{GetContextRef()};
908 if(UNLIKELY(!context)) return;
910 ALCdevice *device = context->Device;
911 std::lock_guard<almtx_t> _{device->BufferLock};
913 ALbuffer *albuf = LookupBuffer(device, buffer);
914 if(UNLIKELY(!albuf))
915 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
916 else if(UNLIKELY(!values))
917 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
918 else switch(param)
920 case AL_LOOP_POINTS_SOFT:
921 if(UNLIKELY(ReadRef(&albuf->ref) != 0))
922 alSetError(context.get(), AL_INVALID_OPERATION, "Modifying in-use buffer %u's loop points",
923 buffer);
924 else if(UNLIKELY(values[0] >= values[1] || values[0] < 0 || values[1] > albuf->SampleLen))
925 alSetError(context.get(), AL_INVALID_VALUE, "Invalid loop point range %d -> %d o buffer %u",
926 values[0], values[1], buffer);
927 else
929 albuf->LoopStart = values[0];
930 albuf->LoopEnd = values[1];
932 break;
934 default:
935 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x",
936 param);
941 AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value)
943 ContextRef context{GetContextRef()};
944 if(UNLIKELY(!context)) return;
946 ALCdevice *device = context->Device;
947 std::lock_guard<almtx_t> _{device->BufferLock};
949 ALbuffer *albuf = LookupBuffer(device, buffer);
950 if(UNLIKELY(!albuf))
951 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
952 else if(UNLIKELY(!value))
953 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
954 else switch(param)
956 default:
957 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
962 AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
964 ContextRef context{GetContextRef()};
965 if(UNLIKELY(!context)) return;
967 ALCdevice *device = context->Device;
968 std::lock_guard<almtx_t> _{device->BufferLock};
970 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
971 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
972 else if(UNLIKELY(!value1 || !value2 || !value3))
973 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
974 else switch(param)
976 default:
977 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
982 AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values)
984 switch(param)
986 case AL_SEC_LENGTH_SOFT:
987 alGetBufferf(buffer, param, values);
988 return;
991 ContextRef context{GetContextRef()};
992 if(UNLIKELY(!context)) return;
994 ALCdevice *device = context->Device;
995 std::lock_guard<almtx_t> _{device->BufferLock};
997 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
998 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
999 else if(UNLIKELY(!values))
1000 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
1001 else switch(param)
1003 default:
1004 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
1009 AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value)
1011 ContextRef context{GetContextRef()};
1012 if(UNLIKELY(!context)) return;
1014 ALCdevice *device = context->Device;
1015 std::lock_guard<almtx_t> _{device->BufferLock};
1017 ALbuffer *albuf = LookupBuffer(device, buffer);
1018 if(UNLIKELY(!albuf))
1019 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1020 else if(UNLIKELY(!value))
1021 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
1022 else switch(param)
1024 case AL_FREQUENCY:
1025 *value = albuf->Frequency;
1026 break;
1028 case AL_BITS:
1029 *value = BytesFromFmt(albuf->FmtType) * 8;
1030 break;
1032 case AL_CHANNELS:
1033 *value = ChannelsFromFmt(albuf->FmtChannels);
1034 break;
1036 case AL_SIZE:
1037 *value = albuf->SampleLen * FrameSizeFromFmt(albuf->FmtChannels,
1038 albuf->FmtType);
1039 break;
1041 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1042 *value = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
1043 break;
1045 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1046 *value = ATOMIC_LOAD_SEQ(&albuf->PackAlign);
1047 break;
1049 default:
1050 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
1055 AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3)
1057 ContextRef context{GetContextRef()};
1058 if(UNLIKELY(!context)) return;
1060 ALCdevice *device = context->Device;
1061 std::lock_guard<almtx_t> _{device->BufferLock};
1063 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
1064 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1065 else if(UNLIKELY(!value1 || !value2 || !value3))
1066 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
1067 else switch(param)
1069 default:
1070 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
1075 AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values)
1077 switch(param)
1079 case AL_FREQUENCY:
1080 case AL_BITS:
1081 case AL_CHANNELS:
1082 case AL_SIZE:
1083 case AL_INTERNAL_FORMAT_SOFT:
1084 case AL_BYTE_LENGTH_SOFT:
1085 case AL_SAMPLE_LENGTH_SOFT:
1086 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1087 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1088 alGetBufferi(buffer, param, values);
1089 return;
1092 ContextRef context{GetContextRef()};
1093 if(UNLIKELY(!context)) return;
1095 ALCdevice *device = context->Device;
1096 std::lock_guard<almtx_t> _{device->BufferLock};
1098 ALbuffer *albuf = LookupBuffer(device, buffer);
1099 if(UNLIKELY(!albuf))
1100 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1101 else if(UNLIKELY(!values))
1102 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
1103 else switch(param)
1105 case AL_LOOP_POINTS_SOFT:
1106 values[0] = albuf->LoopStart;
1107 values[1] = albuf->LoopEnd;
1108 break;
1110 default:
1111 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x",
1112 param);
1117 ALsizei BytesFromUserFmt(UserFmtType type)
1119 switch(type)
1121 case UserFmtUByte: return sizeof(ALubyte);
1122 case UserFmtShort: return sizeof(ALshort);
1123 case UserFmtFloat: return sizeof(ALfloat);
1124 case UserFmtDouble: return sizeof(ALdouble);
1125 case UserFmtMulaw: return sizeof(ALubyte);
1126 case UserFmtAlaw: return sizeof(ALubyte);
1127 case UserFmtIMA4: break; /* not handled here */
1128 case UserFmtMSADPCM: break; /* not handled here */
1130 return 0;
1132 ALsizei ChannelsFromUserFmt(UserFmtChannels chans)
1134 switch(chans)
1136 case UserFmtMono: return 1;
1137 case UserFmtStereo: return 2;
1138 case UserFmtRear: return 2;
1139 case UserFmtQuad: return 4;
1140 case UserFmtX51: return 6;
1141 case UserFmtX61: return 7;
1142 case UserFmtX71: return 8;
1143 case UserFmtBFormat2D: return 3;
1144 case UserFmtBFormat3D: return 4;
1146 return 0;
1149 ALsizei BytesFromFmt(FmtType type)
1151 switch(type)
1153 case FmtUByte: return sizeof(ALubyte);
1154 case FmtShort: return sizeof(ALshort);
1155 case FmtFloat: return sizeof(ALfloat);
1156 case FmtDouble: return sizeof(ALdouble);
1157 case FmtMulaw: return sizeof(ALubyte);
1158 case FmtAlaw: return sizeof(ALubyte);
1160 return 0;
1162 ALsizei ChannelsFromFmt(FmtChannels chans)
1164 switch(chans)
1166 case FmtMono: return 1;
1167 case FmtStereo: return 2;
1168 case FmtRear: return 2;
1169 case FmtQuad: return 4;
1170 case FmtX51: return 6;
1171 case FmtX61: return 7;
1172 case FmtX71: return 8;
1173 case FmtBFormat2D: return 3;
1174 case FmtBFormat3D: return 4;
1176 return 0;
1181 * ReleaseALBuffers()
1183 * INTERNAL: Called to destroy any buffers that still exist on the device
1185 ALvoid ReleaseALBuffers(ALCdevice *device)
1187 size_t leftover = 0;
1188 for(auto &sublist : device->BufferList)
1190 ALuint64 usemask = ~sublist.FreeMask;
1191 while(usemask)
1193 ALsizei idx = CTZ64(usemask);
1194 ALbuffer *buffer = sublist.Buffers + idx;
1196 al_free(buffer->data);
1197 buffer->data = nullptr;
1198 buffer->~ALbuffer();
1200 ++leftover;
1202 usemask &= ~(U64(1) << idx);
1204 sublist.FreeMask = ~usemask;
1206 if(leftover > 0)
1207 WARN("(%p) Deleted " SZFMT " Buffer%s\n", device, leftover, (leftover==1)?"":"s");