Avoid using preexisting enum values
[openal-soft.git] / al / buffer.cpp
bloba00df2fe763f773c0a6a53fefb1fb11548d0a998
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 "buffer.h"
25 #include <algorithm>
26 #include <array>
27 #include <atomic>
28 #include <cassert>
29 #include <cstddef>
30 #include <cstdint>
31 #include <cstring>
32 #include <iterator>
33 #include <limits>
34 #include <memory>
35 #include <mutex>
36 #include <numeric>
37 #include <optional>
38 #include <stdexcept>
39 #include <string>
40 #include <unordered_map>
41 #include <utility>
42 #include <vector>
44 #include "AL/al.h"
45 #include "AL/alc.h"
46 #include "AL/alext.h"
48 #include "albit.h"
49 #include "alc/context.h"
50 #include "alc/device.h"
51 #include "alc/inprogext.h"
52 #include "almalloc.h"
53 #include "alnumeric.h"
54 #include "alspan.h"
55 #include "core/device.h"
56 #include "core/logging.h"
57 #include "core/resampler_limits.h"
58 #include "core/voice.h"
59 #include "direct_defs.h"
60 #include "intrusive_ptr.h"
61 #include "opthelpers.h"
63 #ifdef ALSOFT_EAX
64 #include <unordered_set>
66 #include "eax/globals.h"
67 #include "eax/x_ram.h"
68 #endif // ALSOFT_EAX
71 namespace {
73 using SubListAllocator = al::allocator<std::array<ALbuffer,64>>;
75 std::optional<AmbiLayout> AmbiLayoutFromEnum(ALenum layout) noexcept
77 switch(layout)
79 case AL_FUMA_SOFT: return AmbiLayout::FuMa;
80 case AL_ACN_SOFT: return AmbiLayout::ACN;
82 return std::nullopt;
84 ALenum EnumFromAmbiLayout(AmbiLayout layout)
86 switch(layout)
88 case AmbiLayout::FuMa: return AL_FUMA_SOFT;
89 case AmbiLayout::ACN: return AL_ACN_SOFT;
91 throw std::runtime_error{"Invalid AmbiLayout: "+std::to_string(int(layout))};
94 std::optional<AmbiScaling> AmbiScalingFromEnum(ALenum scale) noexcept
96 switch(scale)
98 case AL_FUMA_SOFT: return AmbiScaling::FuMa;
99 case AL_SN3D_SOFT: return AmbiScaling::SN3D;
100 case AL_N3D_SOFT: return AmbiScaling::N3D;
102 return std::nullopt;
104 ALenum EnumFromAmbiScaling(AmbiScaling scale)
106 switch(scale)
108 case AmbiScaling::FuMa: return AL_FUMA_SOFT;
109 case AmbiScaling::SN3D: return AL_SN3D_SOFT;
110 case AmbiScaling::N3D: return AL_N3D_SOFT;
111 case AmbiScaling::UHJ: break;
113 throw std::runtime_error{"Invalid AmbiScaling: "+std::to_string(int(scale))};
116 #ifdef ALSOFT_EAX
117 std::optional<EaxStorage> EaxStorageFromEnum(ALenum scale) noexcept
119 switch(scale)
121 case AL_STORAGE_AUTOMATIC: return EaxStorage::Automatic;
122 case AL_STORAGE_ACCESSIBLE: return EaxStorage::Accessible;
123 case AL_STORAGE_HARDWARE: return EaxStorage::Hardware;
125 return std::nullopt;
127 ALenum EnumFromEaxStorage(EaxStorage storage)
129 switch(storage)
131 case EaxStorage::Automatic: return AL_STORAGE_AUTOMATIC;
132 case EaxStorage::Accessible: return AL_STORAGE_ACCESSIBLE;
133 case EaxStorage::Hardware: return AL_STORAGE_HARDWARE;
135 throw std::runtime_error{"Invalid EaxStorage: "+std::to_string(int(storage))};
139 bool eax_x_ram_check_availability(const ALCdevice &device, const ALbuffer &buffer,
140 const ALuint newsize) noexcept
142 ALuint freemem{device.eax_x_ram_free_size};
143 /* If the buffer is currently in "hardware", add its memory to the free
144 * pool since it'll be "replaced".
146 if(buffer.eax_x_ram_is_hardware)
147 freemem += buffer.OriginalSize;
148 return freemem >= newsize;
151 void eax_x_ram_apply(ALCdevice &device, ALbuffer &buffer) noexcept
153 if(buffer.eax_x_ram_is_hardware)
154 return;
156 if(device.eax_x_ram_free_size >= buffer.OriginalSize)
158 device.eax_x_ram_free_size -= buffer.OriginalSize;
159 buffer.eax_x_ram_is_hardware = true;
163 void eax_x_ram_clear(ALCdevice& al_device, ALbuffer& al_buffer) noexcept
165 if(al_buffer.eax_x_ram_is_hardware)
166 al_device.eax_x_ram_free_size += al_buffer.OriginalSize;
167 al_buffer.eax_x_ram_is_hardware = false;
169 #endif // ALSOFT_EAX
172 constexpr ALbitfieldSOFT INVALID_STORAGE_MASK{~unsigned(AL_MAP_READ_BIT_SOFT |
173 AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT)};
174 constexpr ALbitfieldSOFT MAP_READ_WRITE_FLAGS{AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT};
175 constexpr ALbitfieldSOFT INVALID_MAP_FLAGS{~unsigned(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT |
176 AL_MAP_PERSISTENT_BIT_SOFT)};
179 bool EnsureBuffers(ALCdevice *device, size_t needed)
181 size_t count{std::accumulate(device->BufferList.cbegin(), device->BufferList.cend(), 0_uz,
182 [](size_t cur, const BufferSubList &sublist) noexcept -> size_t
183 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
185 try {
186 while(needed > count)
188 if(device->BufferList.size() >= 1<<25) UNLIKELY
189 return false;
191 BufferSubList sublist{};
192 sublist.FreeMask = ~0_u64;
193 sublist.Buffers = SubListAllocator{}.allocate(1);
194 device->BufferList.emplace_back(std::move(sublist));
195 count += std::tuple_size_v<SubListAllocator::value_type>;
198 catch(...) {
199 return false;
201 return true;
204 ALbuffer *AllocBuffer(ALCdevice *device) noexcept
206 auto sublist = std::find_if(device->BufferList.begin(), device->BufferList.end(),
207 [](const BufferSubList &entry) noexcept -> bool
208 { return entry.FreeMask != 0; });
209 auto lidx = static_cast<ALuint>(std::distance(device->BufferList.begin(), sublist));
210 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
211 ASSUME(slidx < 64);
213 ALbuffer *buffer{al::construct_at(al::to_address(sublist->Buffers->begin() + slidx))};
215 /* Add 1 to avoid buffer ID 0. */
216 buffer->id = ((lidx<<6) | slidx) + 1;
218 sublist->FreeMask &= ~(1_u64 << slidx);
220 return buffer;
223 void FreeBuffer(ALCdevice *device, ALbuffer *buffer)
225 #ifdef ALSOFT_EAX
226 eax_x_ram_clear(*device, *buffer);
227 #endif // ALSOFT_EAX
229 device->mBufferNames.erase(buffer->id);
231 const ALuint id{buffer->id - 1};
232 const size_t lidx{id >> 6};
233 const ALuint slidx{id & 0x3f};
235 std::destroy_at(buffer);
237 device->BufferList[lidx].FreeMask |= 1_u64 << slidx;
240 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
242 const size_t lidx{(id-1) >> 6};
243 const ALuint slidx{(id-1) & 0x3f};
245 if(lidx >= device->BufferList.size()) UNLIKELY
246 return nullptr;
247 BufferSubList &sublist = device->BufferList[lidx];
248 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
249 return nullptr;
250 return al::to_address(sublist.Buffers->begin() + slidx);
254 ALuint SanitizeAlignment(FmtType type, ALuint align)
256 if(align == 0)
258 if(type == FmtIMA4)
260 /* Here is where things vary:
261 * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel
262 * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel
264 return 65;
266 if(type == FmtMSADPCM)
267 return 64;
268 return 1;
271 if(type == FmtIMA4)
273 /* IMA4 block alignment must be a multiple of 8, plus 1. */
274 if((align&7) == 1) return static_cast<ALuint>(align);
275 return 0;
277 if(type == FmtMSADPCM)
279 /* MSADPCM block alignment must be a multiple of 2. */
280 if((align&1) == 0) return static_cast<ALuint>(align);
281 return 0;
284 return static_cast<ALuint>(align);
288 /** Loads the specified data into the buffer, using the specified format. */
289 void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
290 const FmtChannels DstChannels, const FmtType DstType, const std::byte *SrcData,
291 ALbitfieldSOFT access)
293 if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) UNLIKELY
294 return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u",
295 ALBuf->id);
297 const ALuint unpackalign{ALBuf->UnpackAlign};
298 const ALuint align{SanitizeAlignment(DstType, unpackalign)};
299 if(align < 1) UNLIKELY
300 return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples",
301 unpackalign, NameFromFormat(DstType));
303 const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder :
304 (IsUHJ(DstChannels) ? 1 : 0)};
306 if((access&AL_PRESERVE_DATA_BIT_SOFT))
308 /* Can only preserve data with the same format and alignment. */
309 if(ALBuf->mChannels != DstChannels || ALBuf->mType != DstType) UNLIKELY
310 return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched format");
311 if(ALBuf->mBlockAlign != align) UNLIKELY
312 return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched alignment");
313 if(ALBuf->mAmbiOrder != ambiorder) UNLIKELY
314 return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched order");
317 /* Convert the size in bytes to blocks using the unpack block alignment. */
318 const ALuint NumChannels{ChannelsFromFmt(DstChannels, ambiorder)};
319 const ALuint BlockSize{NumChannels *
320 ((DstType == FmtIMA4) ? (align-1)/2 + 4 :
321 (DstType == FmtMSADPCM) ? (align-2)/2 + 7 :
322 (align * BytesFromFmt(DstType)))};
323 if((size%BlockSize) != 0) UNLIKELY
324 return context->setError(AL_INVALID_VALUE,
325 "Data size %d is not a multiple of frame size %d (%d unpack alignment)",
326 size, BlockSize, align);
327 const ALuint blocks{size / BlockSize};
329 if(blocks > std::numeric_limits<ALsizei>::max()/align) UNLIKELY
330 return context->setError(AL_OUT_OF_MEMORY,
331 "Buffer size overflow, %d blocks x %d samples per block", blocks, align);
332 if(blocks > std::numeric_limits<size_t>::max()/BlockSize) UNLIKELY
333 return context->setError(AL_OUT_OF_MEMORY,
334 "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize);
336 const size_t newsize{static_cast<size_t>(blocks) * BlockSize};
338 #ifdef ALSOFT_EAX
339 if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware)
341 ALCdevice &device = *context->mALDevice;
342 if(!eax_x_ram_check_availability(device, *ALBuf, size))
343 return context->setError(AL_OUT_OF_MEMORY,
344 "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size, size);
346 #endif
348 /* This could reallocate only when increasing the size or the new size is
349 * less than half the current, but then the buffer's AL_SIZE would not be
350 * very reliable for accounting buffer memory usage, and reporting the real
351 * size could cause problems for apps that use AL_SIZE to try to get the
352 * buffer's play length.
354 if(newsize != ALBuf->mDataStorage.size())
356 auto newdata = decltype(ALBuf->mDataStorage)(newsize, std::byte{});
357 if((access&AL_PRESERVE_DATA_BIT_SOFT))
359 const size_t tocopy{std::min(newdata.size(), ALBuf->mDataStorage.size())};
360 std::copy_n(ALBuf->mDataStorage.begin(), tocopy, newdata.begin());
362 newdata.swap(ALBuf->mDataStorage);
364 ALBuf->mData = ALBuf->mDataStorage;
365 #ifdef ALSOFT_EAX
366 eax_x_ram_clear(*context->mALDevice, *ALBuf);
367 #endif
369 if(SrcData != nullptr && !ALBuf->mData.empty())
370 std::copy_n(SrcData, blocks*BlockSize, ALBuf->mData.begin());
371 ALBuf->mBlockAlign = (DstType == FmtIMA4 || DstType == FmtMSADPCM) ? align : 1;
373 ALBuf->OriginalSize = size;
375 ALBuf->Access = access;
377 ALBuf->mSampleRate = static_cast<ALuint>(freq);
378 ALBuf->mChannels = DstChannels;
379 ALBuf->mType = DstType;
380 ALBuf->mAmbiOrder = ambiorder;
382 ALBuf->mCallback = nullptr;
383 ALBuf->mUserData = nullptr;
385 ALBuf->mSampleLen = blocks * align;
386 ALBuf->mLoopStart = 0;
387 ALBuf->mLoopEnd = ALBuf->mSampleLen;
389 #ifdef ALSOFT_EAX
390 if(eax_g_is_enabled && ALBuf->eax_x_ram_mode == EaxStorage::Hardware)
391 eax_x_ram_apply(*context->mALDevice, *ALBuf);
392 #endif
395 /** Prepares the buffer to use the specified callback, using the specified format. */
396 void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
397 const FmtChannels DstChannels, const FmtType DstType, ALBUFFERCALLBACKTYPESOFT callback,
398 void *userptr)
400 if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) UNLIKELY
401 return context->setError(AL_INVALID_OPERATION, "Modifying callback for in-use buffer %u",
402 ALBuf->id);
404 const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder :
405 (IsUHJ(DstChannels) ? 1 : 0)};
407 const ALuint unpackalign{ALBuf->UnpackAlign};
408 const ALuint align{SanitizeAlignment(DstType, unpackalign)};
409 if(align < 1) UNLIKELY
410 return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples",
411 unpackalign, NameFromFormat(DstType));
413 const ALuint BlockSize{ChannelsFromFmt(DstChannels, ambiorder) *
414 ((DstType == FmtIMA4) ? (align-1)/2 + 4 :
415 (DstType == FmtMSADPCM) ? (align-2)/2 + 7 :
416 (align * BytesFromFmt(DstType)))};
418 /* The maximum number of samples a callback buffer may need to store is a
419 * full mixing line * max pitch * channel count, since it may need to hold
420 * a full line's worth of sample frames before downsampling. An additional
421 * MaxResamplerEdge is needed for "future" samples during resampling (the
422 * voice will hold a history for the past samples).
424 static constexpr size_t line_size{DeviceBase::MixerLineSize*MaxPitch + MaxResamplerEdge};
425 const size_t line_blocks{(line_size + align-1) / align};
427 using BufferVectorType = decltype(ALBuf->mDataStorage);
428 BufferVectorType(line_blocks*BlockSize).swap(ALBuf->mDataStorage);
429 ALBuf->mData = ALBuf->mDataStorage;
431 #ifdef ALSOFT_EAX
432 eax_x_ram_clear(*context->mALDevice, *ALBuf);
433 #endif
435 ALBuf->mCallback = callback;
436 ALBuf->mUserData = userptr;
438 ALBuf->OriginalSize = 0;
439 ALBuf->Access = 0;
441 ALBuf->mBlockAlign = (DstType == FmtIMA4 || DstType == FmtMSADPCM) ? align : 1;
442 ALBuf->mSampleRate = static_cast<ALuint>(freq);
443 ALBuf->mChannels = DstChannels;
444 ALBuf->mType = DstType;
445 ALBuf->mAmbiOrder = ambiorder;
447 ALBuf->mSampleLen = 0;
448 ALBuf->mLoopStart = 0;
449 ALBuf->mLoopEnd = ALBuf->mSampleLen;
452 /** Prepares the buffer to use caller-specified storage. */
453 void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
454 const FmtChannels DstChannels, const FmtType DstType, std::byte *sdata, const ALuint sdatalen)
456 if(ALBuf->ref.load(std::memory_order_relaxed) != 0 || ALBuf->MappedAccess != 0) UNLIKELY
457 return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u",
458 ALBuf->id);
460 const ALuint unpackalign{ALBuf->UnpackAlign};
461 const ALuint align{SanitizeAlignment(DstType, unpackalign)};
462 if(align < 1) UNLIKELY
463 return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples",
464 unpackalign, NameFromFormat(DstType));
466 auto get_type_alignment = [](const FmtType type) noexcept -> ALuint
468 /* NOTE: This only needs to be the required alignment for the CPU to
469 * read/write the given sample type in the mixer.
471 switch(type)
473 case FmtUByte: return alignof(ALubyte);
474 case FmtShort: return alignof(ALshort);
475 case FmtInt: return alignof(ALint);
476 case FmtFloat: return alignof(ALfloat);
477 case FmtDouble: return alignof(ALdouble);
478 case FmtMulaw: return alignof(ALubyte);
479 case FmtAlaw: return alignof(ALubyte);
480 case FmtIMA4: break;
481 case FmtMSADPCM: break;
483 return 1;
485 const auto typealign = get_type_alignment(DstType);
486 if((reinterpret_cast<uintptr_t>(sdata) & (typealign-1)) != 0)
487 return context->setError(AL_INVALID_VALUE, "Pointer %p is misaligned for %s samples (%u)",
488 static_cast<void*>(sdata), NameFromFormat(DstType), typealign);
490 const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder :
491 (IsUHJ(DstChannels) ? 1 : 0)};
493 /* Convert the size in bytes to blocks using the unpack block alignment. */
494 const ALuint NumChannels{ChannelsFromFmt(DstChannels, ambiorder)};
495 const ALuint BlockSize{NumChannels *
496 ((DstType == FmtIMA4) ? (align-1)/2 + 4 :
497 (DstType == FmtMSADPCM) ? (align-2)/2 + 7 :
498 (align * BytesFromFmt(DstType)))};
499 if((sdatalen%BlockSize) != 0) UNLIKELY
500 return context->setError(AL_INVALID_VALUE,
501 "Data size %u is not a multiple of frame size %u (%u unpack alignment)",
502 sdatalen, BlockSize, align);
503 const ALuint blocks{sdatalen / BlockSize};
505 if(blocks > std::numeric_limits<ALsizei>::max()/align) UNLIKELY
506 return context->setError(AL_OUT_OF_MEMORY,
507 "Buffer size overflow, %d blocks x %d samples per block", blocks, align);
508 if(blocks > std::numeric_limits<size_t>::max()/BlockSize) UNLIKELY
509 return context->setError(AL_OUT_OF_MEMORY,
510 "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize);
512 #ifdef ALSOFT_EAX
513 if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware)
515 ALCdevice &device = *context->mALDevice;
516 if(!eax_x_ram_check_availability(device, *ALBuf, sdatalen))
517 return context->setError(AL_OUT_OF_MEMORY,
518 "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size,
519 sdatalen);
521 #endif
523 decltype(ALBuf->mDataStorage){}.swap(ALBuf->mDataStorage);
524 ALBuf->mData = {static_cast<std::byte*>(sdata), sdatalen};
526 #ifdef ALSOFT_EAX
527 eax_x_ram_clear(*context->mALDevice, *ALBuf);
528 #endif
530 ALBuf->mCallback = nullptr;
531 ALBuf->mUserData = nullptr;
533 ALBuf->OriginalSize = sdatalen;
534 ALBuf->Access = 0;
536 ALBuf->mBlockAlign = (DstType == FmtIMA4 || DstType == FmtMSADPCM) ? align : 1;
537 ALBuf->mSampleRate = static_cast<ALuint>(freq);
538 ALBuf->mChannels = DstChannels;
539 ALBuf->mType = DstType;
540 ALBuf->mAmbiOrder = ambiorder;
542 ALBuf->mSampleLen = blocks * align;
543 ALBuf->mLoopStart = 0;
544 ALBuf->mLoopEnd = ALBuf->mSampleLen;
546 #ifdef ALSOFT_EAX
547 if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware)
548 eax_x_ram_apply(*context->mALDevice, *ALBuf);
549 #endif
553 struct DecompResult { FmtChannels channels; FmtType type; };
554 std::optional<DecompResult> DecomposeUserFormat(ALenum format)
556 struct FormatMap {
557 ALenum format;
558 FmtChannels channels;
559 FmtType type;
561 static constexpr std::array UserFmtList{
562 FormatMap{AL_FORMAT_MONO8, FmtMono, FmtUByte },
563 FormatMap{AL_FORMAT_MONO16, FmtMono, FmtShort },
564 FormatMap{AL_FORMAT_MONO_I32, FmtMono, FmtInt },
565 FormatMap{AL_FORMAT_MONO_FLOAT32, FmtMono, FmtFloat },
566 FormatMap{AL_FORMAT_MONO_DOUBLE_EXT, FmtMono, FmtDouble },
567 FormatMap{AL_FORMAT_MONO_IMA4, FmtMono, FmtIMA4 },
568 FormatMap{AL_FORMAT_MONO_MSADPCM_SOFT, FmtMono, FmtMSADPCM},
569 FormatMap{AL_FORMAT_MONO_MULAW, FmtMono, FmtMulaw },
570 FormatMap{AL_FORMAT_MONO_ALAW_EXT, FmtMono, FmtAlaw },
572 FormatMap{AL_FORMAT_STEREO8, FmtStereo, FmtUByte },
573 FormatMap{AL_FORMAT_STEREO16, FmtStereo, FmtShort },
574 FormatMap{AL_FORMAT_STEREO_I32, FmtStereo, FmtInt },
575 FormatMap{AL_FORMAT_STEREO_FLOAT32, FmtStereo, FmtFloat },
576 FormatMap{AL_FORMAT_STEREO_DOUBLE_EXT, FmtStereo, FmtDouble },
577 FormatMap{AL_FORMAT_STEREO_IMA4, FmtStereo, FmtIMA4 },
578 FormatMap{AL_FORMAT_STEREO_MSADPCM_SOFT, FmtStereo, FmtMSADPCM},
579 FormatMap{AL_FORMAT_STEREO_MULAW, FmtStereo, FmtMulaw },
580 FormatMap{AL_FORMAT_STEREO_ALAW_EXT, FmtStereo, FmtAlaw },
582 FormatMap{AL_FORMAT_REAR8, FmtRear, FmtUByte},
583 FormatMap{AL_FORMAT_REAR16, FmtRear, FmtShort},
584 FormatMap{AL_FORMAT_REAR32, FmtRear, FmtFloat},
585 FormatMap{AL_FORMAT_REAR_I32, FmtRear, FmtInt },
586 FormatMap{AL_FORMAT_REAR_FLOAT32, FmtRear, FmtFloat},
587 FormatMap{AL_FORMAT_REAR_MULAW, FmtRear, FmtMulaw},
589 FormatMap{AL_FORMAT_QUAD8_LOKI, FmtQuad, FmtUByte},
590 FormatMap{AL_FORMAT_QUAD16_LOKI, FmtQuad, FmtShort},
592 FormatMap{AL_FORMAT_QUAD8, FmtQuad, FmtUByte},
593 FormatMap{AL_FORMAT_QUAD16, FmtQuad, FmtShort},
594 FormatMap{AL_FORMAT_QUAD32, FmtQuad, FmtFloat},
595 FormatMap{AL_FORMAT_QUAD_I32, FmtQuad, FmtInt },
596 FormatMap{AL_FORMAT_QUAD_FLOAT32, FmtQuad, FmtFloat},
597 FormatMap{AL_FORMAT_QUAD_MULAW, FmtQuad, FmtMulaw},
599 FormatMap{AL_FORMAT_51CHN8, FmtX51, FmtUByte},
600 FormatMap{AL_FORMAT_51CHN16, FmtX51, FmtShort},
601 FormatMap{AL_FORMAT_51CHN32, FmtX51, FmtFloat},
602 FormatMap{AL_FORMAT_51CHN_I32, FmtX51, FmtInt },
603 FormatMap{AL_FORMAT_51CHN_FLOAT32, FmtX51, FmtFloat},
604 FormatMap{AL_FORMAT_51CHN_MULAW, FmtX51, FmtMulaw},
606 FormatMap{AL_FORMAT_61CHN8, FmtX61, FmtUByte},
607 FormatMap{AL_FORMAT_61CHN16, FmtX61, FmtShort},
608 FormatMap{AL_FORMAT_61CHN32, FmtX61, FmtFloat},
609 FormatMap{AL_FORMAT_61CHN_I32, FmtX61, FmtInt },
610 FormatMap{AL_FORMAT_61CHN_FLOAT32, FmtX61, FmtFloat},
611 FormatMap{AL_FORMAT_61CHN_MULAW, FmtX61, FmtMulaw},
613 FormatMap{AL_FORMAT_71CHN8, FmtX71, FmtUByte},
614 FormatMap{AL_FORMAT_71CHN16, FmtX71, FmtShort},
615 FormatMap{AL_FORMAT_71CHN32, FmtX71, FmtFloat},
616 FormatMap{AL_FORMAT_71CHN_I32, FmtX71, FmtInt },
617 FormatMap{AL_FORMAT_71CHN_FLOAT32, FmtX71, FmtFloat},
618 FormatMap{AL_FORMAT_71CHN_MULAW, FmtX71, FmtMulaw},
620 FormatMap{AL_FORMAT_BFORMAT2D_8, FmtBFormat2D, FmtUByte},
621 FormatMap{AL_FORMAT_BFORMAT2D_16, FmtBFormat2D, FmtShort},
622 FormatMap{AL_FORMAT_BFORMAT2D_FLOAT32, FmtBFormat2D, FmtFloat},
623 FormatMap{AL_FORMAT_BFORMAT2D_MULAW, FmtBFormat2D, FmtMulaw},
625 FormatMap{AL_FORMAT_BFORMAT3D_8, FmtBFormat3D, FmtUByte},
626 FormatMap{AL_FORMAT_BFORMAT3D_16, FmtBFormat3D, FmtShort},
627 FormatMap{AL_FORMAT_BFORMAT3D_FLOAT32, FmtBFormat3D, FmtFloat},
628 FormatMap{AL_FORMAT_BFORMAT3D_MULAW, FmtBFormat3D, FmtMulaw},
630 FormatMap{AL_FORMAT_UHJ2CHN8_SOFT, FmtUHJ2, FmtUByte },
631 FormatMap{AL_FORMAT_UHJ2CHN16_SOFT, FmtUHJ2, FmtShort },
632 FormatMap{AL_FORMAT_UHJ2CHN_I32_SOFT, FmtUHJ2, FmtInt },
633 FormatMap{AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, FmtUHJ2, FmtFloat },
634 FormatMap{AL_FORMAT_UHJ2CHN_MULAW_SOFT, FmtUHJ2, FmtMulaw },
635 FormatMap{AL_FORMAT_UHJ2CHN_ALAW_SOFT, FmtUHJ2, FmtAlaw },
636 FormatMap{AL_FORMAT_UHJ2CHN_IMA4_SOFT, FmtUHJ2, FmtIMA4 },
637 FormatMap{AL_FORMAT_UHJ2CHN_MSADPCM_SOFT, FmtUHJ2, FmtMSADPCM},
639 FormatMap{AL_FORMAT_UHJ3CHN8_SOFT, FmtUHJ3, FmtUByte},
640 FormatMap{AL_FORMAT_UHJ3CHN16_SOFT, FmtUHJ3, FmtShort},
641 FormatMap{AL_FORMAT_UHJ3CHN_I32_SOFT, FmtUHJ3, FmtInt },
642 FormatMap{AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, FmtUHJ3, FmtFloat},
643 FormatMap{AL_FORMAT_UHJ3CHN_MULAW_SOFT, FmtUHJ3, FmtMulaw},
644 FormatMap{AL_FORMAT_UHJ3CHN_ALAW_SOFT, FmtUHJ3, FmtAlaw },
646 FormatMap{AL_FORMAT_UHJ4CHN8_SOFT, FmtUHJ4, FmtUByte},
647 FormatMap{AL_FORMAT_UHJ4CHN16_SOFT, FmtUHJ4, FmtShort},
648 FormatMap{AL_FORMAT_UHJ4CHN_I32_SOFT, FmtUHJ4, FmtInt },
649 FormatMap{AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, FmtUHJ4, FmtFloat},
650 FormatMap{AL_FORMAT_UHJ4CHN_MULAW_SOFT, FmtUHJ4, FmtMulaw},
651 FormatMap{AL_FORMAT_UHJ4CHN_ALAW_SOFT, FmtUHJ4, FmtAlaw },
654 for(const auto &fmt : UserFmtList)
656 if(fmt.format == format)
657 return DecompResult{fmt.channels, fmt.type};
659 return std::nullopt;
662 } // namespace
665 AL_API DECL_FUNC2(void, alGenBuffers, ALsizei,n, ALuint*,buffers)
666 FORCE_ALIGN void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) noexcept
668 if(n < 0) UNLIKELY
669 context->setError(AL_INVALID_VALUE, "Generating %d buffers", n);
670 if(n <= 0) UNLIKELY return;
672 ALCdevice *device{context->mALDevice.get()};
673 std::lock_guard<std::mutex> buflock{device->BufferLock};
674 if(!EnsureBuffers(device, static_cast<ALuint>(n)))
676 context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d buffer%s", n, (n==1)?"":"s");
677 return;
680 const al::span bids{buffers, static_cast<ALuint>(n)};
681 std::generate(bids.begin(), bids.end(), [device]{ return AllocBuffer(device)->id; });
684 AL_API DECL_FUNC2(void, alDeleteBuffers, ALsizei,n, const ALuint*,buffers)
685 FORCE_ALIGN void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n,
686 const ALuint *buffers) noexcept
688 if(n < 0) UNLIKELY
689 context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n);
690 if(n <= 0) UNLIKELY return;
692 ALCdevice *device{context->mALDevice.get()};
693 std::lock_guard<std::mutex> buflock{device->BufferLock};
695 /* First try to find any buffers that are invalid or in-use. */
696 auto validate_buffer = [device,context](const ALuint bid) -> bool
698 if(!bid) return true;
699 ALbuffer *ALBuf{LookupBuffer(device, bid)};
700 if(!ALBuf) UNLIKELY
702 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", bid);
703 return false;
705 if(ALBuf->ref.load(std::memory_order_relaxed) != 0) UNLIKELY
707 context->setError(AL_INVALID_OPERATION, "Deleting in-use buffer %u", bid);
708 return false;
710 return true;
713 const al::span bids{buffers, static_cast<ALuint>(n)};
714 auto invbuf = std::find_if_not(bids.begin(), bids.end(), validate_buffer);
715 if(invbuf != bids.end()) UNLIKELY return;
717 /* All good. Delete non-0 buffer IDs. */
718 auto delete_buffer = [device](const ALuint bid) -> void
720 if(ALbuffer *buffer{bid ? LookupBuffer(device, bid) : nullptr})
721 FreeBuffer(device, buffer);
723 std::for_each(bids.begin(), bids.end(), delete_buffer);
726 AL_API DECL_FUNC1(ALboolean, alIsBuffer, ALuint,buffer)
727 FORCE_ALIGN ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) noexcept
729 ALCdevice *device{context->mALDevice.get()};
730 std::lock_guard<std::mutex> buflock{device->BufferLock};
731 if(!buffer || LookupBuffer(device, buffer))
732 return AL_TRUE;
733 return AL_FALSE;
737 AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) noexcept
739 auto context = GetContextRef();
740 if(!context) UNLIKELY return;
741 alBufferStorageDirectSOFT(context.get(), buffer, format, data, size, freq, 0);
744 FORCE_ALIGN void AL_APIENTRY alBufferDataDirect(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq) noexcept
745 { alBufferStorageDirectSOFT(context, buffer, format, data, size, freq, 0); }
747 AL_API DECL_FUNCEXT6(void, alBufferStorage,SOFT, ALuint,buffer, ALenum,format, const ALvoid*,data, ALsizei,size, ALsizei,freq, ALbitfieldSOFT,flags)
748 FORCE_ALIGN void AL_APIENTRY alBufferStorageDirectSOFT(ALCcontext *context, ALuint buffer,
749 ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags) noexcept
751 ALCdevice *device{context->mALDevice.get()};
752 std::lock_guard<std::mutex> buflock{device->BufferLock};
754 ALbuffer *albuf{LookupBuffer(device, buffer)};
755 if(!albuf) UNLIKELY
756 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
757 else if(size < 0) UNLIKELY
758 context->setError(AL_INVALID_VALUE, "Negative storage size %d", size);
759 else if(freq < 1) UNLIKELY
760 context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
761 else if((flags&INVALID_STORAGE_MASK) != 0) UNLIKELY
762 context->setError(AL_INVALID_VALUE, "Invalid storage flags 0x%x",
763 flags&INVALID_STORAGE_MASK);
764 else if((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS)) UNLIKELY
765 context->setError(AL_INVALID_VALUE,
766 "Declaring persistently mapped storage without read or write access");
767 else
769 auto usrfmt = DecomposeUserFormat(format);
770 if(!usrfmt) UNLIKELY
771 context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
772 else
774 LoadData(context, albuf, freq, static_cast<ALuint>(size), usrfmt->channels,
775 usrfmt->type, static_cast<const std::byte*>(data), flags);
780 FORCE_ALIGN DECL_FUNC5(void, alBufferDataStatic, ALuint,buffer, ALenum,format, ALvoid*,data, ALsizei,size, ALsizei,freq)
781 FORCE_ALIGN void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, const ALuint buffer,
782 ALenum format, ALvoid *data, ALsizei size, ALsizei freq) noexcept
784 ALCdevice *device{context->mALDevice.get()};
785 std::lock_guard<std::mutex> buflock{device->BufferLock};
787 ALbuffer *albuf{LookupBuffer(device, buffer)};
788 if(!albuf) UNLIKELY
789 return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
790 if(size < 0) UNLIKELY
791 return context->setError(AL_INVALID_VALUE, "Negative storage size %d", size);
792 if(freq < 1) UNLIKELY
793 return context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
795 auto usrfmt = DecomposeUserFormat(format);
796 if(!usrfmt) UNLIKELY
797 return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
799 PrepareUserPtr(context, albuf, freq, usrfmt->channels, usrfmt->type,
800 static_cast<std::byte*>(data), static_cast<ALuint>(size));
803 AL_API DECL_FUNCEXT4(void*, alMapBuffer,SOFT, ALuint,buffer, ALsizei,offset, ALsizei,length, ALbitfieldSOFT,access)
804 FORCE_ALIGN void* AL_APIENTRY alMapBufferDirectSOFT(ALCcontext *context, ALuint buffer,
805 ALsizei offset, ALsizei length, ALbitfieldSOFT access) noexcept
807 ALCdevice *device{context->mALDevice.get()};
808 std::lock_guard<std::mutex> buflock{device->BufferLock};
810 ALbuffer *albuf{LookupBuffer(device, buffer)};
811 if(!albuf) UNLIKELY
812 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
813 else if((access&INVALID_MAP_FLAGS) != 0) UNLIKELY
814 context->setError(AL_INVALID_VALUE, "Invalid map flags 0x%x", access&INVALID_MAP_FLAGS);
815 else if(!(access&MAP_READ_WRITE_FLAGS)) UNLIKELY
816 context->setError(AL_INVALID_VALUE, "Mapping buffer %u without read or write access",
817 buffer);
818 else
820 ALbitfieldSOFT unavailable = (albuf->Access^access) & access;
821 if(albuf->ref.load(std::memory_order_relaxed) != 0
822 && !(access&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY
823 context->setError(AL_INVALID_OPERATION,
824 "Mapping in-use buffer %u without persistent mapping", buffer);
825 else if(albuf->MappedAccess != 0) UNLIKELY
826 context->setError(AL_INVALID_OPERATION, "Mapping already-mapped buffer %u", buffer);
827 else if((unavailable&AL_MAP_READ_BIT_SOFT)) UNLIKELY
828 context->setError(AL_INVALID_VALUE,
829 "Mapping buffer %u for reading without read access", buffer);
830 else if((unavailable&AL_MAP_WRITE_BIT_SOFT)) UNLIKELY
831 context->setError(AL_INVALID_VALUE,
832 "Mapping buffer %u for writing without write access", buffer);
833 else if((unavailable&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY
834 context->setError(AL_INVALID_VALUE,
835 "Mapping buffer %u persistently without persistent access", buffer);
836 else if(offset < 0 || length <= 0
837 || static_cast<ALuint>(offset) >= albuf->OriginalSize
838 || static_cast<ALuint>(length) > albuf->OriginalSize - static_cast<ALuint>(offset))
839 UNLIKELY
840 context->setError(AL_INVALID_VALUE, "Mapping invalid range %d+%d for buffer %u",
841 offset, length, buffer);
842 else
844 void *retval{albuf->mData.data() + offset};
845 albuf->MappedAccess = access;
846 albuf->MappedOffset = offset;
847 albuf->MappedSize = length;
848 return retval;
852 return nullptr;
855 AL_API DECL_FUNCEXT1(void, alUnmapBuffer,SOFT, ALuint,buffer)
856 FORCE_ALIGN void AL_APIENTRY alUnmapBufferDirectSOFT(ALCcontext *context, ALuint buffer) noexcept
858 ALCdevice *device{context->mALDevice.get()};
859 std::lock_guard<std::mutex> buflock{device->BufferLock};
861 ALbuffer *albuf{LookupBuffer(device, buffer)};
862 if(!albuf) UNLIKELY
863 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
864 else if(albuf->MappedAccess == 0) UNLIKELY
865 context->setError(AL_INVALID_OPERATION, "Unmapping unmapped buffer %u", buffer);
866 else
868 albuf->MappedAccess = 0;
869 albuf->MappedOffset = 0;
870 albuf->MappedSize = 0;
874 AL_API DECL_FUNCEXT3(void, alFlushMappedBuffer,SOFT, ALuint,buffer, ALsizei,offset, ALsizei,length)
875 FORCE_ALIGN void AL_APIENTRY alFlushMappedBufferDirectSOFT(ALCcontext *context, ALuint buffer,
876 ALsizei offset, ALsizei length) noexcept
878 ALCdevice *device{context->mALDevice.get()};
879 std::lock_guard<std::mutex> buflock{device->BufferLock};
881 ALbuffer *albuf{LookupBuffer(device, buffer)};
882 if(!albuf) UNLIKELY
883 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
884 else if(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT)) UNLIKELY
885 context->setError(AL_INVALID_OPERATION, "Flushing buffer %u while not mapped for writing",
886 buffer);
887 else if(offset < albuf->MappedOffset || length <= 0
888 || offset >= albuf->MappedOffset+albuf->MappedSize
889 || length > albuf->MappedOffset+albuf->MappedSize-offset) UNLIKELY
890 context->setError(AL_INVALID_VALUE, "Flushing invalid range %d+%d on buffer %u", offset,
891 length, buffer);
892 else
894 /* FIXME: Need to use some method of double-buffering for the mixer and
895 * app to hold separate memory, which can be safely transferred
896 * asynchronously. Currently we just say the app shouldn't write where
897 * OpenAL's reading, and hope for the best...
899 std::atomic_thread_fence(std::memory_order_seq_cst);
903 AL_API DECL_FUNCEXT5(void, alBufferSubData,SOFT, ALuint,buffer, ALenum,format, const ALvoid*,data, ALsizei,offset, ALsizei,length)
904 FORCE_ALIGN void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer,
905 ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) noexcept
907 ALCdevice *device{context->mALDevice.get()};
908 std::lock_guard<std::mutex> buflock{device->BufferLock};
910 ALbuffer *albuf{LookupBuffer(device, buffer)};
911 if(!albuf) UNLIKELY
912 return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
914 auto usrfmt = DecomposeUserFormat(format);
915 if(!usrfmt) UNLIKELY
916 return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
918 const ALuint unpack_align{albuf->UnpackAlign};
919 const ALuint align{SanitizeAlignment(usrfmt->type, unpack_align)};
920 if(align < 1) UNLIKELY
921 return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u", unpack_align);
922 if(usrfmt->channels != albuf->mChannels || usrfmt->type != albuf->mType) UNLIKELY
923 return context->setError(AL_INVALID_ENUM, "Unpacking data with mismatched format");
924 if(align != albuf->mBlockAlign) UNLIKELY
925 return context->setError(AL_INVALID_VALUE,
926 "Unpacking data with alignment %u does not match original alignment %u", align,
927 albuf->mBlockAlign);
928 if(albuf->isBFormat() && albuf->UnpackAmbiOrder != albuf->mAmbiOrder) UNLIKELY
929 return context->setError(AL_INVALID_VALUE,
930 "Unpacking data with mismatched ambisonic order");
931 if(albuf->MappedAccess != 0) UNLIKELY
932 return context->setError(AL_INVALID_OPERATION, "Unpacking data into mapped buffer %u",
933 buffer);
935 const ALuint num_chans{albuf->channelsFromFmt()};
936 const ALuint byte_align{
937 (albuf->mType == FmtIMA4) ? ((align-1)/2 + 4) * num_chans :
938 (albuf->mType == FmtMSADPCM) ? ((align-2)/2 + 7) * num_chans :
939 (align * albuf->bytesFromFmt() * num_chans)};
941 if(offset < 0 || length < 0 || static_cast<ALuint>(offset) > albuf->OriginalSize
942 || static_cast<ALuint>(length) > albuf->OriginalSize-static_cast<ALuint>(offset))
943 UNLIKELY
944 return context->setError(AL_INVALID_VALUE, "Invalid data sub-range %d+%d on buffer %u",
945 offset, length, buffer);
946 if((static_cast<ALuint>(offset)%byte_align) != 0) UNLIKELY
947 return context->setError(AL_INVALID_VALUE,
948 "Sub-range offset %d is not a multiple of frame size %d (%d unpack alignment)",
949 offset, byte_align, align);
950 if((static_cast<ALuint>(length)%byte_align) != 0) UNLIKELY
951 return context->setError(AL_INVALID_VALUE,
952 "Sub-range length %d is not a multiple of frame size %d (%d unpack alignment)",
953 length, byte_align, align);
955 assert(al::to_underlying(usrfmt->type) == al::to_underlying(albuf->mType));
956 memcpy(albuf->mData.data()+offset, data, static_cast<ALuint>(length));
960 AL_API DECL_FUNC3(void, alBufferf, ALuint,buffer, ALenum,param, ALfloat,value)
961 FORCE_ALIGN void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param,
962 ALfloat value [[maybe_unused]]) noexcept
964 ALCdevice *device{context->mALDevice.get()};
965 std::lock_guard<std::mutex> buflock{device->BufferLock};
967 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
968 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
969 else switch(param)
971 default:
972 context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
976 AL_API DECL_FUNC5(void, alBuffer3f, ALuint,buffer, ALenum,param, ALfloat,value1, ALfloat,value2, ALfloat,value3)
977 FORCE_ALIGN void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param,
978 ALfloat value1 [[maybe_unused]], ALfloat value2 [[maybe_unused]],
979 ALfloat value3 [[maybe_unused]]) noexcept
981 ALCdevice *device{context->mALDevice.get()};
982 std::lock_guard<std::mutex> buflock{device->BufferLock};
984 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
985 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
986 else switch(param)
988 default:
989 context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
993 AL_API DECL_FUNC3(void, alBufferfv, ALuint,buffer, ALenum,param, const ALfloat*,values)
994 FORCE_ALIGN void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param,
995 const ALfloat *values) noexcept
997 ALCdevice *device{context->mALDevice.get()};
998 std::lock_guard<std::mutex> buflock{device->BufferLock};
1000 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1001 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1002 else if(!values) UNLIKELY
1003 context->setError(AL_INVALID_VALUE, "NULL pointer");
1004 else switch(param)
1006 default:
1007 context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
1012 AL_API DECL_FUNC3(void, alBufferi, ALuint,buffer, ALenum,param, ALint,value)
1013 FORCE_ALIGN void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param,
1014 ALint value) noexcept
1016 ALCdevice *device{context->mALDevice.get()};
1017 std::lock_guard<std::mutex> buflock{device->BufferLock};
1019 ALbuffer *albuf{LookupBuffer(device, buffer)};
1020 if(!albuf) UNLIKELY
1021 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1022 else switch(param)
1024 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1025 if(value < 0) UNLIKELY
1026 context->setError(AL_INVALID_VALUE, "Invalid unpack block alignment %d", value);
1027 else
1028 albuf->UnpackAlign = static_cast<ALuint>(value);
1029 break;
1031 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1032 if(value < 0) UNLIKELY
1033 context->setError(AL_INVALID_VALUE, "Invalid pack block alignment %d", value);
1034 else
1035 albuf->PackAlign = static_cast<ALuint>(value);
1036 break;
1038 case AL_AMBISONIC_LAYOUT_SOFT:
1039 if(albuf->ref.load(std::memory_order_relaxed) != 0) UNLIKELY
1040 context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic layout",
1041 buffer);
1042 else if(const auto layout = AmbiLayoutFromEnum(value))
1043 albuf->mAmbiLayout = layout.value();
1044 else UNLIKELY
1045 context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic layout %04x", value);
1046 break;
1048 case AL_AMBISONIC_SCALING_SOFT:
1049 if(albuf->ref.load(std::memory_order_relaxed) != 0) UNLIKELY
1050 context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic scaling",
1051 buffer);
1052 else if(const auto scaling = AmbiScalingFromEnum(value))
1053 albuf->mAmbiScaling = scaling.value();
1054 else UNLIKELY
1055 context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic scaling %04x", value);
1056 break;
1058 case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1059 if(value < 1 || value > 14) UNLIKELY
1060 context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic order %d", value);
1061 else
1062 albuf->UnpackAmbiOrder = static_cast<ALuint>(value);
1063 break;
1065 default:
1066 context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
1070 AL_API DECL_FUNC5(void, alBuffer3i, ALuint,buffer, ALenum,param, ALint,value1, ALint,value2, ALint,value3)
1071 FORCE_ALIGN void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param,
1072 ALint value1 [[maybe_unused]], ALint value2 [[maybe_unused]], ALint value3 [[maybe_unused]]) noexcept
1074 ALCdevice *device{context->mALDevice.get()};
1075 std::lock_guard<std::mutex> buflock{device->BufferLock};
1077 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1078 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1079 else switch(param)
1081 default:
1082 context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
1086 AL_API DECL_FUNC3(void, alBufferiv, ALuint,buffer, ALenum,param, const ALint*,values)
1087 FORCE_ALIGN void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param,
1088 const ALint *values) noexcept
1090 if(!values) UNLIKELY
1091 return context->setError(AL_INVALID_VALUE, "NULL pointer");
1093 switch(param)
1095 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1096 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1097 case AL_AMBISONIC_LAYOUT_SOFT:
1098 case AL_AMBISONIC_SCALING_SOFT:
1099 case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1100 alBufferiDirect(context, buffer, param, *values);
1101 return;
1104 ALCdevice *device{context->mALDevice.get()};
1105 std::lock_guard<std::mutex> buflock{device->BufferLock};
1107 al::span<const ALint> vals;
1108 ALbuffer *albuf{LookupBuffer(device, buffer)};
1109 if(!albuf) UNLIKELY
1110 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1111 else switch(param)
1113 case AL_LOOP_POINTS_SOFT:
1114 vals = {values, 2_uz};
1115 if(albuf->ref.load(std::memory_order_relaxed) != 0) UNLIKELY
1116 context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's loop points",
1117 buffer);
1118 else if(vals[0] < 0 || vals[0] >= vals[1]
1119 || static_cast<ALuint>(vals[1]) > albuf->mSampleLen) UNLIKELY
1120 context->setError(AL_INVALID_VALUE, "Invalid loop point range %d -> %d on buffer %u",
1121 vals[0], vals[1], buffer);
1122 else
1124 albuf->mLoopStart = static_cast<ALuint>(vals[0]);
1125 albuf->mLoopEnd = static_cast<ALuint>(vals[1]);
1127 break;
1129 default:
1130 context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param);
1135 AL_API DECL_FUNC3(void, alGetBufferf, ALuint,buffer, ALenum,param, ALfloat*,value)
1136 FORCE_ALIGN void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param,
1137 ALfloat *value) noexcept
1139 ALCdevice *device{context->mALDevice.get()};
1140 std::lock_guard<std::mutex> buflock{device->BufferLock};
1142 ALbuffer *albuf{LookupBuffer(device, buffer)};
1143 if(!albuf) UNLIKELY
1144 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1145 else if(!value) UNLIKELY
1146 context->setError(AL_INVALID_VALUE, "NULL pointer");
1147 else switch(param)
1149 case AL_SEC_LENGTH_SOFT:
1150 *value = (albuf->mSampleRate < 1) ? 0.0f :
1151 (static_cast<float>(albuf->mSampleLen) / static_cast<float>(albuf->mSampleRate));
1152 break;
1154 default:
1155 context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
1159 AL_API DECL_FUNC5(void, alGetBuffer3f, ALuint,buffer, ALenum,param, ALfloat*,value1, ALfloat*,value2, ALfloat*,value3)
1160 FORCE_ALIGN void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param,
1161 ALfloat *value1, ALfloat *value2, ALfloat *value3) noexcept
1163 ALCdevice *device{context->mALDevice.get()};
1164 std::lock_guard<std::mutex> buflock{device->BufferLock};
1166 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1167 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1168 else if(!value1 || !value2 || !value3) UNLIKELY
1169 context->setError(AL_INVALID_VALUE, "NULL pointer");
1170 else switch(param)
1172 default:
1173 context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
1177 AL_API DECL_FUNC3(void, alGetBufferfv, ALuint,buffer, ALenum,param, ALfloat*,values)
1178 FORCE_ALIGN void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param,
1179 ALfloat *values) noexcept
1181 switch(param)
1183 case AL_SEC_LENGTH_SOFT:
1184 alGetBufferfDirect(context, buffer, param, values);
1185 return;
1188 ALCdevice *device{context->mALDevice.get()};
1189 std::lock_guard<std::mutex> buflock{device->BufferLock};
1191 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1192 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1193 else if(!values) UNLIKELY
1194 context->setError(AL_INVALID_VALUE, "NULL pointer");
1195 else switch(param)
1197 default:
1198 context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
1203 AL_API DECL_FUNC3(void, alGetBufferi, ALuint,buffer, ALenum,param, ALint*,value)
1204 FORCE_ALIGN void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param,
1205 ALint *value) noexcept
1207 ALCdevice *device{context->mALDevice.get()};
1208 std::lock_guard<std::mutex> buflock{device->BufferLock};
1209 ALbuffer *albuf{LookupBuffer(device, buffer)};
1210 if(!albuf) UNLIKELY
1211 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1212 else if(!value) UNLIKELY
1213 context->setError(AL_INVALID_VALUE, "NULL pointer");
1214 else switch(param)
1216 case AL_FREQUENCY:
1217 *value = static_cast<ALint>(albuf->mSampleRate);
1218 break;
1220 case AL_BITS:
1221 *value = (albuf->mType == FmtIMA4 || albuf->mType == FmtMSADPCM) ? 4
1222 : static_cast<ALint>(albuf->bytesFromFmt() * 8);
1223 break;
1225 case AL_CHANNELS:
1226 *value = static_cast<ALint>(albuf->channelsFromFmt());
1227 break;
1229 case AL_SIZE:
1230 *value = albuf->mCallback ? 0 : static_cast<ALint>(albuf->mData.size());
1231 break;
1233 case AL_BYTE_LENGTH_SOFT:
1234 *value = static_cast<ALint>(albuf->mSampleLen / albuf->mBlockAlign
1235 * albuf->blockSizeFromFmt());
1236 break;
1238 case AL_SAMPLE_LENGTH_SOFT:
1239 *value = static_cast<ALint>(albuf->mSampleLen);
1240 break;
1242 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1243 *value = static_cast<ALint>(albuf->UnpackAlign);
1244 break;
1246 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1247 *value = static_cast<ALint>(albuf->PackAlign);
1248 break;
1250 case AL_AMBISONIC_LAYOUT_SOFT:
1251 *value = EnumFromAmbiLayout(albuf->mAmbiLayout);
1252 break;
1254 case AL_AMBISONIC_SCALING_SOFT:
1255 *value = EnumFromAmbiScaling(albuf->mAmbiScaling);
1256 break;
1258 case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1259 *value = static_cast<int>(albuf->UnpackAmbiOrder);
1260 break;
1262 default:
1263 context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
1267 AL_API DECL_FUNC5(void, alGetBuffer3i, ALuint,buffer, ALenum,param, ALint*,value1, ALint*,value2, ALint*,value3)
1268 FORCE_ALIGN void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param,
1269 ALint *value1, ALint *value2, ALint *value3) noexcept
1271 ALCdevice *device{context->mALDevice.get()};
1272 std::lock_guard<std::mutex> buflock{device->BufferLock};
1273 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1274 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1275 else if(!value1 || !value2 || !value3) UNLIKELY
1276 context->setError(AL_INVALID_VALUE, "NULL pointer");
1277 else switch(param)
1279 default:
1280 context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
1284 AL_API DECL_FUNC3(void, alGetBufferiv, ALuint,buffer, ALenum,param, ALint*,values)
1285 FORCE_ALIGN void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param,
1286 ALint *values) noexcept
1288 switch(param)
1290 case AL_FREQUENCY:
1291 case AL_BITS:
1292 case AL_CHANNELS:
1293 case AL_SIZE:
1294 case AL_INTERNAL_FORMAT_SOFT:
1295 case AL_BYTE_LENGTH_SOFT:
1296 case AL_SAMPLE_LENGTH_SOFT:
1297 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1298 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1299 case AL_AMBISONIC_LAYOUT_SOFT:
1300 case AL_AMBISONIC_SCALING_SOFT:
1301 case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1302 alGetBufferiDirect(context, buffer, param, values);
1303 return;
1306 ALCdevice *device{context->mALDevice.get()};
1307 std::lock_guard<std::mutex> buflock{device->BufferLock};
1309 al::span<ALint> vals;
1310 ALbuffer *albuf{LookupBuffer(device, buffer)};
1311 if(!albuf) UNLIKELY
1312 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1313 else if(!values) UNLIKELY
1314 context->setError(AL_INVALID_VALUE, "NULL pointer");
1315 else switch(param)
1317 case AL_LOOP_POINTS_SOFT:
1318 vals = {values, 2_uz};
1319 vals[0] = static_cast<ALint>(albuf->mLoopStart);
1320 vals[1] = static_cast<ALint>(albuf->mLoopEnd);
1321 break;
1323 default:
1324 context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param);
1329 AL_API DECL_FUNCEXT5(void, alBufferCallback,SOFT, ALuint,buffer, ALenum,format, ALsizei,freq, ALBUFFERCALLBACKTYPESOFT,callback, ALvoid*,userptr)
1330 FORCE_ALIGN void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer,
1331 ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) noexcept
1333 ALCdevice *device{context->mALDevice.get()};
1334 std::lock_guard<std::mutex> buflock{device->BufferLock};
1336 ALbuffer *albuf{LookupBuffer(device, buffer)};
1337 if(!albuf) UNLIKELY
1338 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1339 else if(freq < 1) UNLIKELY
1340 context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
1341 else if(callback == nullptr) UNLIKELY
1342 context->setError(AL_INVALID_VALUE, "NULL callback");
1343 else
1345 auto usrfmt = DecomposeUserFormat(format);
1346 if(!usrfmt) UNLIKELY
1347 context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
1348 else
1349 PrepareCallback(context, albuf, freq, usrfmt->channels, usrfmt->type, callback,
1350 userptr);
1354 AL_API DECL_FUNCEXT3(void, alGetBufferPtr,SOFT, ALuint,buffer, ALenum,param, ALvoid**,value)
1355 FORCE_ALIGN void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer,
1356 ALenum param, ALvoid **value) noexcept
1358 ALCdevice *device{context->mALDevice.get()};
1359 std::lock_guard<std::mutex> buflock{device->BufferLock};
1360 ALbuffer *albuf{LookupBuffer(device, buffer)};
1361 if(!albuf) UNLIKELY
1362 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1363 else if(!value) UNLIKELY
1364 context->setError(AL_INVALID_VALUE, "NULL pointer");
1365 else switch(param)
1367 case AL_BUFFER_CALLBACK_FUNCTION_SOFT:
1368 *value = reinterpret_cast<void*>(albuf->mCallback);
1369 break;
1370 case AL_BUFFER_CALLBACK_USER_PARAM_SOFT:
1371 *value = albuf->mUserData;
1372 break;
1374 default:
1375 context->setError(AL_INVALID_ENUM, "Invalid buffer pointer property 0x%04x", param);
1379 AL_API DECL_FUNCEXT5(void, alGetBuffer3Ptr,SOFT, ALuint,buffer, ALenum,param, ALvoid**,value1, ALvoid**,value2, ALvoid**,value3)
1380 FORCE_ALIGN void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer,
1381 ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) noexcept
1383 ALCdevice *device{context->mALDevice.get()};
1384 std::lock_guard<std::mutex> buflock{device->BufferLock};
1385 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1386 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1387 else if(!value1 || !value2 || !value3) UNLIKELY
1388 context->setError(AL_INVALID_VALUE, "NULL pointer");
1389 else switch(param)
1391 default:
1392 context->setError(AL_INVALID_ENUM, "Invalid buffer 3-pointer property 0x%04x", param);
1396 AL_API DECL_FUNCEXT3(void, alGetBufferPtrv,SOFT, ALuint,buffer, ALenum,param, ALvoid**,values)
1397 FORCE_ALIGN void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer,
1398 ALenum param, ALvoid **values) noexcept
1400 switch(param)
1402 case AL_BUFFER_CALLBACK_FUNCTION_SOFT:
1403 case AL_BUFFER_CALLBACK_USER_PARAM_SOFT:
1404 alGetBufferPtrDirectSOFT(context, buffer, param, values);
1405 return;
1408 ALCdevice *device{context->mALDevice.get()};
1409 std::lock_guard<std::mutex> buflock{device->BufferLock};
1410 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1411 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1412 else if(!values) UNLIKELY
1413 context->setError(AL_INVALID_VALUE, "NULL pointer");
1414 else switch(param)
1416 default:
1417 context->setError(AL_INVALID_ENUM, "Invalid buffer pointer-vector property 0x%04x", param);
1422 AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint /*buffer*/, ALuint /*samplerate*/,
1423 ALenum /*internalformat*/, ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/,
1424 const ALvoid* /*data*/) noexcept
1426 ContextRef context{GetContextRef()};
1427 if(!context) UNLIKELY return;
1429 context->setError(AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported");
1432 AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/,
1433 ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, const ALvoid* /*data*/) noexcept
1435 ContextRef context{GetContextRef()};
1436 if(!context) UNLIKELY return;
1438 context->setError(AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported");
1441 AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/,
1442 ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, ALvoid* /*data*/) noexcept
1444 ContextRef context{GetContextRef()};
1445 if(!context) UNLIKELY return;
1447 context->setError(AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported");
1450 AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum /*format*/) noexcept
1452 ContextRef context{GetContextRef()};
1453 if(!context) UNLIKELY return AL_FALSE;
1455 context->setError(AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported");
1456 return AL_FALSE;
1460 void ALbuffer::SetName(ALCcontext *context, ALuint id, std::string_view name)
1462 ALCdevice *device{context->mALDevice.get()};
1463 std::lock_guard<std::mutex> buflock{device->BufferLock};
1465 auto buffer = LookupBuffer(device, id);
1466 if(!buffer) UNLIKELY
1467 return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", id);
1469 device->mBufferNames.insert_or_assign(id, name);
1473 BufferSubList::~BufferSubList()
1475 if(!Buffers)
1476 return;
1478 uint64_t usemask{~FreeMask};
1479 while(usemask)
1481 const int idx{al::countr_zero(usemask)};
1482 std::destroy_at(al::to_address(Buffers->begin() + idx));
1483 usemask &= ~(1_u64 << idx);
1485 FreeMask = ~usemask;
1486 SubListAllocator{}.deallocate(Buffers, 1);
1487 Buffers = nullptr;
1491 #ifdef ALSOFT_EAX
1492 FORCE_ALIGN DECL_FUNC3(ALboolean, EAXSetBufferMode, ALsizei,n, const ALuint*,buffers, ALint,value)
1493 FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n,
1494 const ALuint *buffers, ALint value) noexcept
1496 #define EAX_PREFIX "[EAXSetBufferMode] "
1498 if(!eax_g_is_enabled)
1500 context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled.");
1501 return AL_FALSE;
1504 const auto storage = EaxStorageFromEnum(value);
1505 if(!storage)
1507 context->setError(AL_INVALID_ENUM, EAX_PREFIX "Unsupported X-RAM mode 0x%x", value);
1508 return AL_FALSE;
1511 if(n == 0)
1512 return AL_TRUE;
1514 if(n < 0)
1516 context->setError(AL_INVALID_VALUE, EAX_PREFIX "Buffer count %d out of range", n);
1517 return AL_FALSE;
1520 if(!buffers)
1522 context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Null AL buffers");
1523 return AL_FALSE;
1526 auto device = context->mALDevice.get();
1527 std::lock_guard<std::mutex> devlock{device->BufferLock};
1529 /* Special-case setting a single buffer, to avoid extraneous allocations. */
1530 if(n == 1)
1532 const auto bufid = *buffers;
1533 if(bufid == AL_NONE)
1534 return AL_TRUE;
1536 const auto buffer = LookupBuffer(device, bufid);
1537 if(!buffer) UNLIKELY
1539 ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid);
1540 return AL_FALSE;
1543 /* TODO: Is the store location allowed to change for in-use buffers, or
1544 * only when not set/queued on a source?
1547 if(*storage == EaxStorage::Hardware)
1549 if(!buffer->eax_x_ram_is_hardware
1550 && buffer->OriginalSize > device->eax_x_ram_free_size) UNLIKELY
1552 context->setError(AL_OUT_OF_MEMORY,
1553 EAX_PREFIX "Out of X-RAM memory (need: %u, avail: %u)", buffer->OriginalSize,
1554 device->eax_x_ram_free_size);
1555 return AL_FALSE;
1558 eax_x_ram_apply(*device, *buffer);
1560 else
1561 eax_x_ram_clear(*device, *buffer);
1562 buffer->eax_x_ram_mode = *storage;
1563 return AL_TRUE;
1566 /* Validate the buffers. */
1567 std::unordered_set<ALbuffer*> buflist;
1568 for(const ALuint bufid : al::span{buffers, static_cast<ALuint>(n)})
1570 if(bufid == AL_NONE)
1571 continue;
1573 const auto buffer = LookupBuffer(device, bufid);
1574 if(!buffer) UNLIKELY
1576 ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid);
1577 return AL_FALSE;
1580 /* TODO: Is the store location allowed to change for in-use buffers, or
1581 * only when not set/queued on a source?
1584 buflist.emplace(buffer);
1587 if(*storage == EaxStorage::Hardware)
1589 size_t total_needed{0};
1590 for(ALbuffer *buffer : buflist)
1592 if(!buffer->eax_x_ram_is_hardware)
1594 if(std::numeric_limits<size_t>::max()-buffer->OriginalSize < total_needed) UNLIKELY
1596 context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Size overflow (%u + %zu)\n",
1597 buffer->OriginalSize, total_needed);
1598 return AL_FALSE;
1600 total_needed += buffer->OriginalSize;
1603 if(total_needed > device->eax_x_ram_free_size)
1605 context->setError(AL_OUT_OF_MEMORY,
1606 EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)", total_needed,
1607 device->eax_x_ram_free_size);
1608 return AL_FALSE;
1612 /* Update the mode. */
1613 for(ALbuffer *buffer : buflist)
1615 if(*storage == EaxStorage::Hardware)
1616 eax_x_ram_apply(*device, *buffer);
1617 else
1618 eax_x_ram_clear(*device, *buffer);
1619 buffer->eax_x_ram_mode = *storage;
1622 return AL_TRUE;
1624 #undef EAX_PREFIX
1627 FORCE_ALIGN DECL_FUNC2(ALenum, EAXGetBufferMode, ALuint,buffer, ALint*,pReserved)
1628 FORCE_ALIGN ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer,
1629 ALint *pReserved) noexcept
1631 #define EAX_PREFIX "[EAXGetBufferMode] "
1633 if(!eax_g_is_enabled)
1635 context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled.");
1636 return AL_NONE;
1639 if(pReserved)
1641 context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Non-null reserved parameter");
1642 return AL_NONE;
1645 auto device = context->mALDevice.get();
1646 std::lock_guard<std::mutex> devlock{device->BufferLock};
1648 const auto al_buffer = LookupBuffer(device, buffer);
1649 if(!al_buffer)
1651 context->setError(AL_INVALID_NAME, EAX_PREFIX "Invalid buffer ID %u", buffer);
1652 return AL_NONE;
1655 return EnumFromEaxStorage(al_buffer->eax_x_ram_mode);
1657 #undef EAX_PREFIX
1660 #endif // ALSOFT_EAX