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
47 #include "alc/context.h"
48 #include "alc/device.h"
49 #include "alc/inprogext.h"
51 #include "alnumeric.h"
52 #include "aloptional.h"
54 #include "core/except.h"
55 #include "core/logging.h"
56 #include "core/voice.h"
57 #include "opthelpers.h"
60 #include "eax/globals.h"
61 #include "eax/x_ram.h"
67 al::optional<AmbiLayout> AmbiLayoutFromEnum(ALenum layout)
71 case AL_FUMA_SOFT: return AmbiLayout::FuMa;
72 case AL_ACN_SOFT: return AmbiLayout::ACN;
76 ALenum EnumFromAmbiLayout(AmbiLayout layout)
80 case AmbiLayout::FuMa: return AL_FUMA_SOFT;
81 case AmbiLayout::ACN: return AL_ACN_SOFT;
83 throw std::runtime_error{"Invalid AmbiLayout: "+std::to_string(int(layout))};
86 al::optional<AmbiScaling> AmbiScalingFromEnum(ALenum scale)
90 case AL_FUMA_SOFT: return AmbiScaling::FuMa;
91 case AL_SN3D_SOFT: return AmbiScaling::SN3D;
92 case AL_N3D_SOFT: return AmbiScaling::N3D;
96 ALenum EnumFromAmbiScaling(AmbiScaling scale)
100 case AmbiScaling::FuMa: return AL_FUMA_SOFT;
101 case AmbiScaling::SN3D: return AL_SN3D_SOFT;
102 case AmbiScaling::N3D: return AL_N3D_SOFT;
103 case AmbiScaling::UHJ: break;
105 throw std::runtime_error{"Invalid AmbiScaling: "+std::to_string(int(scale))};
109 al::optional<EaxStorage> EaxStorageFromEnum(ALenum scale)
113 case AL_STORAGE_AUTOMATIC: return EaxStorage::Automatic;
114 case AL_STORAGE_ACCESSIBLE: return EaxStorage::Accessible;
115 case AL_STORAGE_HARDWARE: return EaxStorage::Hardware;
119 ALenum EnumFromEaxStorage(EaxStorage storage)
123 case EaxStorage::Automatic: return AL_STORAGE_AUTOMATIC;
124 case EaxStorage::Accessible: return AL_STORAGE_ACCESSIBLE;
125 case EaxStorage::Hardware: return AL_STORAGE_HARDWARE;
127 throw std::runtime_error{"Invalid EaxStorage: "+std::to_string(int(storage))};
131 bool eax_x_ram_check_availability(const ALCdevice &device, const ALbuffer &buffer,
132 const ALuint newsize) noexcept
134 ALuint freemem{device.eax_x_ram_free_size};
135 /* If the buffer is currently in "hardware", add its memory to the free
136 * pool since it'll be "replaced".
138 if(buffer.eax_x_ram_is_hardware)
139 freemem += buffer.OriginalSize;
140 return freemem >= newsize;
143 void eax_x_ram_apply(ALCdevice &device, ALbuffer &buffer) noexcept
145 if(buffer.eax_x_ram_is_hardware)
148 if(device.eax_x_ram_free_size >= buffer.OriginalSize)
150 device.eax_x_ram_free_size -= buffer.OriginalSize;
151 buffer.eax_x_ram_is_hardware = true;
155 void eax_x_ram_clear(ALCdevice& al_device, ALbuffer& al_buffer)
157 if(al_buffer.eax_x_ram_is_hardware)
158 al_device.eax_x_ram_free_size += al_buffer.OriginalSize;
159 al_buffer.eax_x_ram_is_hardware = false;
164 constexpr ALbitfieldSOFT INVALID_STORAGE_MASK{~unsigned(AL_MAP_READ_BIT_SOFT |
165 AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT)};
166 constexpr ALbitfieldSOFT MAP_READ_WRITE_FLAGS{AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT};
167 constexpr ALbitfieldSOFT INVALID_MAP_FLAGS{~unsigned(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT |
168 AL_MAP_PERSISTENT_BIT_SOFT)};
171 bool EnsureBuffers(ALCdevice *device, size_t needed)
173 size_t count{std::accumulate(device->BufferList.cbegin(), device->BufferList.cend(), size_t{0},
174 [](size_t cur, const BufferSubList &sublist) noexcept -> size_t
175 { return cur + static_cast<ALuint>(al::popcount(sublist.FreeMask)); })};
177 while(needed > count)
179 if(device->BufferList.size() >= 1<<25) UNLIKELY
182 device->BufferList.emplace_back();
183 auto sublist = device->BufferList.end() - 1;
184 sublist->FreeMask = ~0_u64;
185 sublist->Buffers = static_cast<ALbuffer*>(al_calloc(alignof(ALbuffer), sizeof(ALbuffer)*64));
186 if(!sublist->Buffers) UNLIKELY
188 device->BufferList.pop_back();
196 ALbuffer *AllocBuffer(ALCdevice *device)
198 auto sublist = std::find_if(device->BufferList.begin(), device->BufferList.end(),
199 [](const BufferSubList &entry) noexcept -> bool
200 { return entry.FreeMask != 0; });
201 auto lidx = static_cast<ALuint>(std::distance(device->BufferList.begin(), sublist));
202 auto slidx = static_cast<ALuint>(al::countr_zero(sublist->FreeMask));
205 ALbuffer *buffer{al::construct_at(sublist->Buffers + slidx)};
207 /* Add 1 to avoid buffer ID 0. */
208 buffer->id = ((lidx<<6) | slidx) + 1;
210 sublist->FreeMask &= ~(1_u64 << slidx);
215 void FreeBuffer(ALCdevice *device, ALbuffer *buffer)
218 eax_x_ram_clear(*device, *buffer);
221 const ALuint id{buffer->id - 1};
222 const size_t lidx{id >> 6};
223 const ALuint slidx{id & 0x3f};
225 al::destroy_at(buffer);
227 device->BufferList[lidx].FreeMask |= 1_u64 << slidx;
230 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
232 const size_t lidx{(id-1) >> 6};
233 const ALuint slidx{(id-1) & 0x3f};
235 if(lidx >= device->BufferList.size()) UNLIKELY
237 BufferSubList &sublist = device->BufferList[lidx];
238 if(sublist.FreeMask & (1_u64 << slidx)) UNLIKELY
240 return sublist.Buffers + slidx;
244 ALuint SanitizeAlignment(FmtType type, ALuint align)
250 /* Here is where things vary:
251 * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel
252 * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel
256 if(type == FmtMSADPCM)
263 /* IMA4 block alignment must be a multiple of 8, plus 1. */
264 if((align&7) == 1) return static_cast<ALuint>(align);
267 if(type == FmtMSADPCM)
269 /* MSADPCM block alignment must be a multiple of 2. */
270 if((align&1) == 0) return static_cast<ALuint>(align);
274 return static_cast<ALuint>(align);
278 /** Loads the specified data into the buffer, using the specified format. */
279 void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq, ALuint size,
280 const FmtChannels DstChannels, const FmtType DstType, const al::byte *SrcData,
281 ALbitfieldSOFT access)
283 if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY
284 return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u",
287 const ALuint unpackalign{ALBuf->UnpackAlign};
288 const ALuint align{SanitizeAlignment(DstType, unpackalign)};
289 if(align < 1) UNLIKELY
290 return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples",
291 unpackalign, NameFromFormat(DstType));
293 const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder :
294 (IsUHJ(DstChannels) ? 1 : 0)};
296 if((access&AL_PRESERVE_DATA_BIT_SOFT))
298 /* Can only preserve data with the same format and alignment. */
299 if(ALBuf->mChannels != DstChannels || ALBuf->mType != DstType) UNLIKELY
300 return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched format");
301 if(ALBuf->mBlockAlign != align) UNLIKELY
302 return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched alignment");
303 if(ALBuf->mAmbiOrder != ambiorder) UNLIKELY
304 return context->setError(AL_INVALID_VALUE, "Preserving data of mismatched order");
307 /* Convert the size in bytes to blocks using the unpack block alignment. */
308 const ALuint NumChannels{ChannelsFromFmt(DstChannels, ambiorder)};
309 const ALuint BlockSize{NumChannels *
310 ((DstType == FmtIMA4) ? (align-1)/2 + 4 :
311 (DstType == FmtMSADPCM) ? (align-2)/2 + 7 :
312 (align * BytesFromFmt(DstType)))};
313 if((size%BlockSize) != 0) UNLIKELY
314 return context->setError(AL_INVALID_VALUE,
315 "Data size %d is not a multiple of frame size %d (%d unpack alignment)",
316 size, BlockSize, align);
317 const ALuint blocks{size / BlockSize};
319 if(blocks > std::numeric_limits<ALsizei>::max()/align) UNLIKELY
320 return context->setError(AL_OUT_OF_MEMORY,
321 "Buffer size overflow, %d blocks x %d samples per block", blocks, align);
322 if(blocks > std::numeric_limits<size_t>::max()/BlockSize) UNLIKELY
323 return context->setError(AL_OUT_OF_MEMORY,
324 "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize);
326 const size_t newsize{static_cast<size_t>(blocks) * BlockSize};
329 if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware)
331 ALCdevice &device = *context->mALDevice;
332 if(!eax_x_ram_check_availability(device, *ALBuf, size))
333 return context->setError(AL_OUT_OF_MEMORY,
334 "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size, size);
338 /* This could reallocate only when increasing the size or the new size is
339 * less than half the current, but then the buffer's AL_SIZE would not be
340 * very reliable for accounting buffer memory usage, and reporting the real
341 * size could cause problems for apps that use AL_SIZE to try to get the
342 * buffer's play length.
344 if(newsize != ALBuf->mDataStorage.size())
346 auto newdata = al::vector<al::byte,16>(newsize, al::byte{});
347 if((access&AL_PRESERVE_DATA_BIT_SOFT))
349 const size_t tocopy{minz(newdata.size(), ALBuf->mDataStorage.size())};
350 std::copy_n(ALBuf->mDataStorage.begin(), tocopy, newdata.begin());
352 newdata.swap(ALBuf->mDataStorage);
354 ALBuf->mData = ALBuf->mDataStorage;
356 eax_x_ram_clear(*context->mALDevice, *ALBuf);
359 if(SrcData != nullptr && !ALBuf->mData.empty())
360 std::copy_n(SrcData, blocks*BlockSize, ALBuf->mData.begin());
361 ALBuf->mBlockAlign = (DstType == FmtIMA4 || DstType == FmtMSADPCM) ? align : 1;
363 ALBuf->OriginalSize = size;
365 ALBuf->Access = access;
367 ALBuf->mSampleRate = static_cast<ALuint>(freq);
368 ALBuf->mChannels = DstChannels;
369 ALBuf->mType = DstType;
370 ALBuf->mAmbiOrder = ambiorder;
372 ALBuf->mCallback = nullptr;
373 ALBuf->mUserData = nullptr;
375 ALBuf->mSampleLen = blocks * align;
376 ALBuf->mLoopStart = 0;
377 ALBuf->mLoopEnd = ALBuf->mSampleLen;
380 if(eax_g_is_enabled && ALBuf->eax_x_ram_mode == EaxStorage::Hardware)
381 eax_x_ram_apply(*context->mALDevice, *ALBuf);
385 /** Prepares the buffer to use the specified callback, using the specified format. */
386 void PrepareCallback(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
387 const FmtChannels DstChannels, const FmtType DstType, ALBUFFERCALLBACKTYPESOFT callback,
390 if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY
391 return context->setError(AL_INVALID_OPERATION, "Modifying callback for in-use buffer %u",
394 const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder :
395 (IsUHJ(DstChannels) ? 1 : 0)};
397 const ALuint unpackalign{ALBuf->UnpackAlign};
398 const ALuint align{SanitizeAlignment(DstType, unpackalign)};
399 const ALuint BlockSize{ChannelsFromFmt(DstChannels, ambiorder) *
400 ((DstType == FmtIMA4) ? (align-1)/2 + 4 :
401 (DstType == FmtMSADPCM) ? (align-2)/2 + 7 :
402 (align * BytesFromFmt(DstType)))};
404 /* The maximum number of samples a callback buffer may need to store is a
405 * full mixing line * max pitch * channel count, since it may need to hold
406 * a full line's worth of sample frames before downsampling. An additional
407 * MaxResamplerEdge is needed for "future" samples during resampling (the
408 * voice will hold a history for the past samples).
410 static constexpr size_t line_size{DeviceBase::MixerLineSize*MaxPitch + MaxResamplerEdge};
411 const size_t line_blocks{(line_size + align-1) / align};
413 using BufferVectorType = decltype(ALBuf->mDataStorage);
414 BufferVectorType(line_blocks*BlockSize).swap(ALBuf->mDataStorage);
415 ALBuf->mData = ALBuf->mDataStorage;
418 eax_x_ram_clear(*context->mALDevice, *ALBuf);
421 ALBuf->mCallback = callback;
422 ALBuf->mUserData = userptr;
424 ALBuf->OriginalSize = 0;
427 ALBuf->mBlockAlign = (DstType == FmtIMA4 || DstType == FmtMSADPCM) ? align : 1;
428 ALBuf->mSampleRate = static_cast<ALuint>(freq);
429 ALBuf->mChannels = DstChannels;
430 ALBuf->mType = DstType;
431 ALBuf->mAmbiOrder = ambiorder;
433 ALBuf->mSampleLen = 0;
434 ALBuf->mLoopStart = 0;
435 ALBuf->mLoopEnd = ALBuf->mSampleLen;
438 /** Prepares the buffer to use caller-specified storage. */
439 void PrepareUserPtr(ALCcontext *context, ALbuffer *ALBuf, ALsizei freq,
440 const FmtChannels DstChannels, const FmtType DstType, al::byte *sdata, const ALuint sdatalen)
442 if(ReadRef(ALBuf->ref) != 0 || ALBuf->MappedAccess != 0) UNLIKELY
443 return context->setError(AL_INVALID_OPERATION, "Modifying storage for in-use buffer %u",
446 const ALuint unpackalign{ALBuf->UnpackAlign};
447 const ALuint align{SanitizeAlignment(DstType, unpackalign)};
448 if(align < 1) UNLIKELY
449 return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u for %s samples",
450 unpackalign, NameFromFormat(DstType));
452 auto get_type_alignment = [](const FmtType type) noexcept -> ALuint
454 /* NOTE: This only needs to be the required alignment for the CPU to
455 * read/write the given sample type in the mixer.
459 case FmtUByte: return alignof(ALubyte);
460 case FmtShort: return alignof(ALshort);
461 case FmtFloat: return alignof(ALfloat);
462 case FmtDouble: return alignof(ALdouble);
463 case FmtMulaw: return alignof(ALubyte);
464 case FmtAlaw: return alignof(ALubyte);
466 case FmtMSADPCM: break;
470 const auto typealign = get_type_alignment(DstType);
471 if((reinterpret_cast<uintptr_t>(sdata) & (typealign-1)) != 0)
472 return context->setError(AL_INVALID_VALUE, "Pointer %p is misaligned for %s samples (%u)",
473 static_cast<void*>(sdata), NameFromFormat(DstType), typealign);
475 const ALuint ambiorder{IsBFormat(DstChannels) ? ALBuf->UnpackAmbiOrder :
476 (IsUHJ(DstChannels) ? 1 : 0)};
478 /* Convert the size in bytes to blocks using the unpack block alignment. */
479 const ALuint NumChannels{ChannelsFromFmt(DstChannels, ambiorder)};
480 const ALuint BlockSize{NumChannels *
481 ((DstType == FmtIMA4) ? (align-1)/2 + 4 :
482 (DstType == FmtMSADPCM) ? (align-2)/2 + 7 :
483 (align * BytesFromFmt(DstType)))};
484 if((sdatalen%BlockSize) != 0) UNLIKELY
485 return context->setError(AL_INVALID_VALUE,
486 "Data size %u is not a multiple of frame size %u (%u unpack alignment)",
487 sdatalen, BlockSize, align);
488 const ALuint blocks{sdatalen / BlockSize};
490 if(blocks > std::numeric_limits<ALsizei>::max()/align) UNLIKELY
491 return context->setError(AL_OUT_OF_MEMORY,
492 "Buffer size overflow, %d blocks x %d samples per block", blocks, align);
493 if(blocks > std::numeric_limits<size_t>::max()/BlockSize) UNLIKELY
494 return context->setError(AL_OUT_OF_MEMORY,
495 "Buffer size overflow, %d frames x %d bytes per frame", blocks, BlockSize);
498 if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware)
500 ALCdevice &device = *context->mALDevice;
501 if(!eax_x_ram_check_availability(device, *ALBuf, sdatalen))
502 return context->setError(AL_OUT_OF_MEMORY,
503 "Out of X-RAM memory (avail: %u, needed: %u)", device.eax_x_ram_free_size,
508 decltype(ALBuf->mDataStorage){}.swap(ALBuf->mDataStorage);
509 ALBuf->mData = {static_cast<al::byte*>(sdata), sdatalen};
512 eax_x_ram_clear(*context->mALDevice, *ALBuf);
515 ALBuf->mCallback = nullptr;
516 ALBuf->mUserData = nullptr;
518 ALBuf->OriginalSize = sdatalen;
521 ALBuf->mBlockAlign = (DstType == FmtIMA4 || DstType == FmtMSADPCM) ? align : 1;
522 ALBuf->mSampleRate = static_cast<ALuint>(freq);
523 ALBuf->mChannels = DstChannels;
524 ALBuf->mType = DstType;
525 ALBuf->mAmbiOrder = ambiorder;
527 ALBuf->mSampleLen = blocks * align;
528 ALBuf->mLoopStart = 0;
529 ALBuf->mLoopEnd = ALBuf->mSampleLen;
532 if(ALBuf->eax_x_ram_mode == EaxStorage::Hardware)
533 eax_x_ram_apply(*context->mALDevice, *ALBuf);
538 struct DecompResult { FmtChannels channels; FmtType type; };
539 al::optional<DecompResult> DecomposeUserFormat(ALenum format)
543 FmtChannels channels;
546 static const std::array<FormatMap,63> UserFmtList{{
547 { AL_FORMAT_MONO8, FmtMono, FmtUByte },
548 { AL_FORMAT_MONO16, FmtMono, FmtShort },
549 { AL_FORMAT_MONO_FLOAT32, FmtMono, FmtFloat },
550 { AL_FORMAT_MONO_DOUBLE_EXT, FmtMono, FmtDouble },
551 { AL_FORMAT_MONO_IMA4, FmtMono, FmtIMA4 },
552 { AL_FORMAT_MONO_MSADPCM_SOFT, FmtMono, FmtMSADPCM },
553 { AL_FORMAT_MONO_MULAW, FmtMono, FmtMulaw },
554 { AL_FORMAT_MONO_ALAW_EXT, FmtMono, FmtAlaw },
556 { AL_FORMAT_STEREO8, FmtStereo, FmtUByte },
557 { AL_FORMAT_STEREO16, FmtStereo, FmtShort },
558 { AL_FORMAT_STEREO_FLOAT32, FmtStereo, FmtFloat },
559 { AL_FORMAT_STEREO_DOUBLE_EXT, FmtStereo, FmtDouble },
560 { AL_FORMAT_STEREO_IMA4, FmtStereo, FmtIMA4 },
561 { AL_FORMAT_STEREO_MSADPCM_SOFT, FmtStereo, FmtMSADPCM },
562 { AL_FORMAT_STEREO_MULAW, FmtStereo, FmtMulaw },
563 { AL_FORMAT_STEREO_ALAW_EXT, FmtStereo, FmtAlaw },
565 { AL_FORMAT_REAR8, FmtRear, FmtUByte },
566 { AL_FORMAT_REAR16, FmtRear, FmtShort },
567 { AL_FORMAT_REAR32, FmtRear, FmtFloat },
568 { AL_FORMAT_REAR_MULAW, FmtRear, FmtMulaw },
570 { AL_FORMAT_QUAD8_LOKI, FmtQuad, FmtUByte },
571 { AL_FORMAT_QUAD16_LOKI, FmtQuad, FmtShort },
573 { AL_FORMAT_QUAD8, FmtQuad, FmtUByte },
574 { AL_FORMAT_QUAD16, FmtQuad, FmtShort },
575 { AL_FORMAT_QUAD32, FmtQuad, FmtFloat },
576 { AL_FORMAT_QUAD_MULAW, FmtQuad, FmtMulaw },
578 { AL_FORMAT_51CHN8, FmtX51, FmtUByte },
579 { AL_FORMAT_51CHN16, FmtX51, FmtShort },
580 { AL_FORMAT_51CHN32, FmtX51, FmtFloat },
581 { AL_FORMAT_51CHN_MULAW, FmtX51, FmtMulaw },
583 { AL_FORMAT_61CHN8, FmtX61, FmtUByte },
584 { AL_FORMAT_61CHN16, FmtX61, FmtShort },
585 { AL_FORMAT_61CHN32, FmtX61, FmtFloat },
586 { AL_FORMAT_61CHN_MULAW, FmtX61, FmtMulaw },
588 { AL_FORMAT_71CHN8, FmtX71, FmtUByte },
589 { AL_FORMAT_71CHN16, FmtX71, FmtShort },
590 { AL_FORMAT_71CHN32, FmtX71, FmtFloat },
591 { AL_FORMAT_71CHN_MULAW, FmtX71, FmtMulaw },
593 { AL_FORMAT_BFORMAT2D_8, FmtBFormat2D, FmtUByte },
594 { AL_FORMAT_BFORMAT2D_16, FmtBFormat2D, FmtShort },
595 { AL_FORMAT_BFORMAT2D_FLOAT32, FmtBFormat2D, FmtFloat },
596 { AL_FORMAT_BFORMAT2D_MULAW, FmtBFormat2D, FmtMulaw },
598 { AL_FORMAT_BFORMAT3D_8, FmtBFormat3D, FmtUByte },
599 { AL_FORMAT_BFORMAT3D_16, FmtBFormat3D, FmtShort },
600 { AL_FORMAT_BFORMAT3D_FLOAT32, FmtBFormat3D, FmtFloat },
601 { AL_FORMAT_BFORMAT3D_MULAW, FmtBFormat3D, FmtMulaw },
603 { AL_FORMAT_UHJ2CHN8_SOFT, FmtUHJ2, FmtUByte },
604 { AL_FORMAT_UHJ2CHN16_SOFT, FmtUHJ2, FmtShort },
605 { AL_FORMAT_UHJ2CHN_FLOAT32_SOFT, FmtUHJ2, FmtFloat },
606 { AL_FORMAT_UHJ2CHN_MULAW_SOFT, FmtUHJ2, FmtMulaw },
607 { AL_FORMAT_UHJ2CHN_ALAW_SOFT, FmtUHJ2, FmtAlaw },
608 { AL_FORMAT_UHJ2CHN_IMA4_SOFT, FmtUHJ2, FmtIMA4 },
609 { AL_FORMAT_UHJ2CHN_MSADPCM_SOFT, FmtUHJ2, FmtMSADPCM },
611 { AL_FORMAT_UHJ3CHN8_SOFT, FmtUHJ3, FmtUByte },
612 { AL_FORMAT_UHJ3CHN16_SOFT, FmtUHJ3, FmtShort },
613 { AL_FORMAT_UHJ3CHN_FLOAT32_SOFT, FmtUHJ3, FmtFloat },
614 { AL_FORMAT_UHJ3CHN_MULAW_SOFT, FmtUHJ3, FmtMulaw },
615 { AL_FORMAT_UHJ3CHN_ALAW_SOFT, FmtUHJ3, FmtAlaw },
617 { AL_FORMAT_UHJ4CHN8_SOFT, FmtUHJ4, FmtUByte },
618 { AL_FORMAT_UHJ4CHN16_SOFT, FmtUHJ4, FmtShort },
619 { AL_FORMAT_UHJ4CHN_FLOAT32_SOFT, FmtUHJ4, FmtFloat },
620 { AL_FORMAT_UHJ4CHN_MULAW_SOFT, FmtUHJ4, FmtMulaw },
621 { AL_FORMAT_UHJ4CHN_ALAW_SOFT, FmtUHJ4, FmtAlaw },
624 for(const auto &fmt : UserFmtList)
626 if(fmt.format == format)
627 return al::make_optional<DecompResult>({fmt.channels, fmt.type});
635 AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers)
638 ContextRef context{GetContextRef()};
639 if(!context) UNLIKELY return;
642 context->setError(AL_INVALID_VALUE, "Generating %d buffers", n);
643 if(n <= 0) UNLIKELY return;
645 ALCdevice *device{context->mALDevice.get()};
646 std::lock_guard<std::mutex> _{device->BufferLock};
647 if(!EnsureBuffers(device, static_cast<ALuint>(n)))
649 context->setError(AL_OUT_OF_MEMORY, "Failed to allocate %d buffer%s", n, (n==1)?"":"s");
655 /* Special handling for the easy and normal case. */
656 ALbuffer *buffer{AllocBuffer(device)};
657 buffers[0] = buffer->id;
661 /* Store the allocated buffer IDs in a separate local list, to avoid
662 * modifying the user storage in case of failure.
664 al::vector<ALuint> ids;
665 ids.reserve(static_cast<ALuint>(n));
667 ALbuffer *buffer{AllocBuffer(device)};
668 ids.emplace_back(buffer->id);
670 std::copy(ids.begin(), ids.end(), buffers);
675 AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
678 ContextRef context{GetContextRef()};
679 if(!context) UNLIKELY return;
682 context->setError(AL_INVALID_VALUE, "Deleting %d buffers", n);
683 if(n <= 0) UNLIKELY return;
685 ALCdevice *device{context->mALDevice.get()};
686 std::lock_guard<std::mutex> _{device->BufferLock};
688 /* First try to find any buffers that are invalid or in-use. */
689 auto validate_buffer = [device, &context](const ALuint bid) -> bool
691 if(!bid) return true;
692 ALbuffer *ALBuf{LookupBuffer(device, bid)};
695 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", bid);
698 if(ReadRef(ALBuf->ref) != 0) UNLIKELY
700 context->setError(AL_INVALID_OPERATION, "Deleting in-use buffer %u", bid);
705 const ALuint *buffers_end = buffers + n;
706 auto invbuf = std::find_if_not(buffers, buffers_end, validate_buffer);
707 if(invbuf != buffers_end) UNLIKELY return;
709 /* All good. Delete non-0 buffer IDs. */
710 auto delete_buffer = [device](const ALuint bid) -> void
712 ALbuffer *buffer{bid ? LookupBuffer(device, bid) : nullptr};
713 if(buffer) FreeBuffer(device, buffer);
715 std::for_each(buffers, buffers_end, delete_buffer);
719 AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
722 ContextRef context{GetContextRef()};
725 ALCdevice *device{context->mALDevice.get()};
726 std::lock_guard<std::mutex> _{device->BufferLock};
727 if(!buffer || LookupBuffer(device, buffer))
735 AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
737 { alBufferStorageSOFT(buffer, format, data, size, freq, 0); }
740 AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags)
743 ContextRef context{GetContextRef()};
744 if(!context) UNLIKELY return;
746 ALCdevice *device{context->mALDevice.get()};
747 std::lock_guard<std::mutex> _{device->BufferLock};
749 ALbuffer *albuf = LookupBuffer(device, buffer);
751 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
752 else if(size < 0) UNLIKELY
753 context->setError(AL_INVALID_VALUE, "Negative storage size %d", size);
754 else if(freq < 1) UNLIKELY
755 context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
756 else if((flags&INVALID_STORAGE_MASK) != 0) UNLIKELY
757 context->setError(AL_INVALID_VALUE, "Invalid storage flags 0x%x",
758 flags&INVALID_STORAGE_MASK);
759 else if((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS)) UNLIKELY
760 context->setError(AL_INVALID_VALUE,
761 "Declaring persistently mapped storage without read or write access");
764 auto usrfmt = DecomposeUserFormat(format);
766 context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
769 LoadData(context.get(), albuf, freq, static_cast<ALuint>(size), usrfmt->channels,
770 usrfmt->type, static_cast<const al::byte*>(data), flags);
776 void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size,
780 ContextRef context{GetContextRef()};
781 if(!context) UNLIKELY return;
783 ALCdevice *device{context->mALDevice.get()};
784 std::lock_guard<std::mutex> _{device->BufferLock};
786 ALbuffer *albuf = LookupBuffer(device, buffer);
788 return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
789 if(size < 0) UNLIKELY
790 return context->setError(AL_INVALID_VALUE, "Negative storage size %d", size);
791 if(freq < 1) UNLIKELY
792 return context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
794 auto usrfmt = DecomposeUserFormat(format);
796 return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
798 PrepareUserPtr(context.get(), albuf, freq, usrfmt->channels, usrfmt->type,
799 static_cast<al::byte*>(data), static_cast<ALuint>(size));
803 AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access)
806 ContextRef context{GetContextRef()};
807 if(!context) UNLIKELY return nullptr;
809 ALCdevice *device{context->mALDevice.get()};
810 std::lock_guard<std::mutex> _{device->BufferLock};
812 ALbuffer *albuf = LookupBuffer(device, buffer);
814 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
815 else if((access&INVALID_MAP_FLAGS) != 0) UNLIKELY
816 context->setError(AL_INVALID_VALUE, "Invalid map flags 0x%x", access&INVALID_MAP_FLAGS);
817 else if(!(access&MAP_READ_WRITE_FLAGS)) UNLIKELY
818 context->setError(AL_INVALID_VALUE, "Mapping buffer %u without read or write access",
822 ALbitfieldSOFT unavailable = (albuf->Access^access) & access;
823 if(ReadRef(albuf->ref) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY
824 context->setError(AL_INVALID_OPERATION,
825 "Mapping in-use buffer %u without persistent mapping", buffer);
826 else if(albuf->MappedAccess != 0) UNLIKELY
827 context->setError(AL_INVALID_OPERATION, "Mapping already-mapped buffer %u", buffer);
828 else if((unavailable&AL_MAP_READ_BIT_SOFT)) UNLIKELY
829 context->setError(AL_INVALID_VALUE,
830 "Mapping buffer %u for reading without read access", buffer);
831 else if((unavailable&AL_MAP_WRITE_BIT_SOFT)) UNLIKELY
832 context->setError(AL_INVALID_VALUE,
833 "Mapping buffer %u for writing without write access", buffer);
834 else if((unavailable&AL_MAP_PERSISTENT_BIT_SOFT)) UNLIKELY
835 context->setError(AL_INVALID_VALUE,
836 "Mapping buffer %u persistently without persistent access", buffer);
837 else if(offset < 0 || length <= 0
838 || static_cast<ALuint>(offset) >= albuf->OriginalSize
839 || static_cast<ALuint>(length) > albuf->OriginalSize - static_cast<ALuint>(offset))
841 context->setError(AL_INVALID_VALUE, "Mapping invalid range %d+%d for buffer %u",
842 offset, length, buffer);
845 void *retval{albuf->mData.data() + offset};
846 albuf->MappedAccess = access;
847 albuf->MappedOffset = offset;
848 albuf->MappedSize = length;
857 AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer)
860 ContextRef context{GetContextRef()};
861 if(!context) UNLIKELY return;
863 ALCdevice *device{context->mALDevice.get()};
864 std::lock_guard<std::mutex> _{device->BufferLock};
866 ALbuffer *albuf = LookupBuffer(device, buffer);
868 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
869 else if(albuf->MappedAccess == 0) UNLIKELY
870 context->setError(AL_INVALID_OPERATION, "Unmapping unmapped buffer %u", buffer);
873 albuf->MappedAccess = 0;
874 albuf->MappedOffset = 0;
875 albuf->MappedSize = 0;
880 AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length)
883 ContextRef context{GetContextRef()};
884 if(!context) UNLIKELY return;
886 ALCdevice *device{context->mALDevice.get()};
887 std::lock_guard<std::mutex> _{device->BufferLock};
889 ALbuffer *albuf = LookupBuffer(device, buffer);
891 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
892 else if(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT)) UNLIKELY
893 context->setError(AL_INVALID_OPERATION, "Flushing buffer %u while not mapped for writing",
895 else if(offset < albuf->MappedOffset || length <= 0
896 || offset >= albuf->MappedOffset+albuf->MappedSize
897 || length > albuf->MappedOffset+albuf->MappedSize-offset) UNLIKELY
898 context->setError(AL_INVALID_VALUE, "Flushing invalid range %d+%d on buffer %u", offset,
902 /* FIXME: Need to use some method of double-buffering for the mixer and
903 * app to hold separate memory, which can be safely transfered
904 * asynchronously. Currently we just say the app shouldn't write where
905 * OpenAL's reading, and hope for the best...
907 std::atomic_thread_fence(std::memory_order_seq_cst);
912 AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length)
915 ContextRef context{GetContextRef()};
916 if(!context) UNLIKELY return;
918 ALCdevice *device{context->mALDevice.get()};
919 std::lock_guard<std::mutex> _{device->BufferLock};
921 ALbuffer *albuf = LookupBuffer(device, buffer);
923 return context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
925 auto usrfmt = DecomposeUserFormat(format);
927 return context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
929 const ALuint unpack_align{albuf->UnpackAlign};
930 const ALuint align{SanitizeAlignment(usrfmt->type, unpack_align)};
931 if(align < 1) UNLIKELY
932 return context->setError(AL_INVALID_VALUE, "Invalid unpack alignment %u", unpack_align);
933 if(usrfmt->channels != albuf->mChannels || usrfmt->type != albuf->mType) UNLIKELY
934 return context->setError(AL_INVALID_ENUM, "Unpacking data with mismatched format");
935 if(align != albuf->mBlockAlign) UNLIKELY
936 return context->setError(AL_INVALID_VALUE,
937 "Unpacking data with alignment %u does not match original alignment %u", align,
939 if(albuf->isBFormat() && albuf->UnpackAmbiOrder != albuf->mAmbiOrder) UNLIKELY
940 return context->setError(AL_INVALID_VALUE,
941 "Unpacking data with mismatched ambisonic order");
942 if(albuf->MappedAccess != 0) UNLIKELY
943 return context->setError(AL_INVALID_OPERATION, "Unpacking data into mapped buffer %u",
946 const ALuint num_chans{albuf->channelsFromFmt()};
947 const ALuint byte_align{
948 (albuf->mType == FmtIMA4) ? ((align-1)/2 + 4) * num_chans :
949 (albuf->mType == FmtMSADPCM) ? ((align-2)/2 + 7) * num_chans :
950 (align * albuf->bytesFromFmt() * num_chans)};
952 if(offset < 0 || length < 0 || static_cast<ALuint>(offset) > albuf->OriginalSize
953 || static_cast<ALuint>(length) > albuf->OriginalSize-static_cast<ALuint>(offset))
955 return context->setError(AL_INVALID_VALUE, "Invalid data sub-range %d+%d on buffer %u",
956 offset, length, buffer);
957 if((static_cast<ALuint>(offset)%byte_align) != 0) UNLIKELY
958 return context->setError(AL_INVALID_VALUE,
959 "Sub-range offset %d is not a multiple of frame size %d (%d unpack alignment)",
960 offset, byte_align, align);
961 if((static_cast<ALuint>(length)%byte_align) != 0) UNLIKELY
962 return context->setError(AL_INVALID_VALUE,
963 "Sub-range length %d is not a multiple of frame size %d (%d unpack alignment)",
964 length, byte_align, align);
966 assert(al::to_underlying(usrfmt->type) == al::to_underlying(albuf->mType));
967 memcpy(albuf->mData.data()+offset, data, static_cast<ALuint>(length));
972 AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint /*buffer*/, ALuint /*samplerate*/,
973 ALenum /*internalformat*/, ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/,
974 const ALvoid* /*data*/)
977 ContextRef context{GetContextRef()};
978 if(!context) UNLIKELY return;
980 context->setError(AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported");
984 AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/,
985 ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, const ALvoid* /*data*/)
988 ContextRef context{GetContextRef()};
989 if(!context) UNLIKELY return;
991 context->setError(AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported");
995 AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint /*buffer*/, ALsizei /*offset*/,
996 ALsizei /*samples*/, ALenum /*channels*/, ALenum /*type*/, ALvoid* /*data*/)
999 ContextRef context{GetContextRef()};
1000 if(!context) UNLIKELY return;
1002 context->setError(AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported");
1006 AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum /*format*/)
1009 ContextRef context{GetContextRef()};
1010 if(!context) UNLIKELY return AL_FALSE;
1012 context->setError(AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported");
1018 AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat /*value*/)
1021 ContextRef context{GetContextRef()};
1022 if(!context) UNLIKELY return;
1024 ALCdevice *device{context->mALDevice.get()};
1025 std::lock_guard<std::mutex> _{device->BufferLock};
1027 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1028 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1032 context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
1037 AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param,
1038 ALfloat /*value1*/, ALfloat /*value2*/, ALfloat /*value3*/)
1041 ContextRef context{GetContextRef()};
1042 if(!context) UNLIKELY return;
1044 ALCdevice *device{context->mALDevice.get()};
1045 std::lock_guard<std::mutex> _{device->BufferLock};
1047 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1048 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1052 context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
1057 AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values)
1060 ContextRef context{GetContextRef()};
1061 if(!context) UNLIKELY return;
1063 ALCdevice *device{context->mALDevice.get()};
1064 std::lock_guard<std::mutex> _{device->BufferLock};
1066 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1067 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1068 else if(!values) UNLIKELY
1069 context->setError(AL_INVALID_VALUE, "NULL pointer");
1073 context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
1079 AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
1082 ContextRef context{GetContextRef()};
1083 if(!context) UNLIKELY return;
1085 ALCdevice *device{context->mALDevice.get()};
1086 std::lock_guard<std::mutex> _{device->BufferLock};
1088 ALbuffer *albuf = LookupBuffer(device, buffer);
1090 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1093 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1094 if(value < 0) UNLIKELY
1095 context->setError(AL_INVALID_VALUE, "Invalid unpack block alignment %d", value);
1097 albuf->UnpackAlign = static_cast<ALuint>(value);
1100 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1101 if(value < 0) UNLIKELY
1102 context->setError(AL_INVALID_VALUE, "Invalid pack block alignment %d", value);
1104 albuf->PackAlign = static_cast<ALuint>(value);
1107 case AL_AMBISONIC_LAYOUT_SOFT:
1108 if(ReadRef(albuf->ref) != 0) UNLIKELY
1109 context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic layout",
1111 else if(const auto layout = AmbiLayoutFromEnum(value))
1112 albuf->mAmbiLayout = layout.value();
1114 context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic layout %04x", value);
1117 case AL_AMBISONIC_SCALING_SOFT:
1118 if(ReadRef(albuf->ref) != 0) UNLIKELY
1119 context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's ambisonic scaling",
1121 else if(const auto scaling = AmbiScalingFromEnum(value))
1122 albuf->mAmbiScaling = scaling.value();
1124 context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic scaling %04x", value);
1127 case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1128 if(value < 1 || value > 14) UNLIKELY
1129 context->setError(AL_INVALID_VALUE, "Invalid unpack ambisonic order %d", value);
1131 albuf->UnpackAmbiOrder = static_cast<ALuint>(value);
1135 context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
1140 AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param,
1141 ALint /*value1*/, ALint /*value2*/, ALint /*value3*/)
1144 ContextRef context{GetContextRef()};
1145 if(!context) UNLIKELY return;
1147 ALCdevice *device{context->mALDevice.get()};
1148 std::lock_guard<std::mutex> _{device->BufferLock};
1150 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1151 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1155 context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
1160 AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values)
1167 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1168 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1169 case AL_AMBISONIC_LAYOUT_SOFT:
1170 case AL_AMBISONIC_SCALING_SOFT:
1171 case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1172 alBufferi(buffer, param, values[0]);
1177 ContextRef context{GetContextRef()};
1178 if(!context) UNLIKELY return;
1180 ALCdevice *device{context->mALDevice.get()};
1181 std::lock_guard<std::mutex> _{device->BufferLock};
1183 ALbuffer *albuf = LookupBuffer(device, buffer);
1185 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1186 else if(!values) UNLIKELY
1187 context->setError(AL_INVALID_VALUE, "NULL pointer");
1190 case AL_LOOP_POINTS_SOFT:
1191 if(ReadRef(albuf->ref) != 0) UNLIKELY
1192 context->setError(AL_INVALID_OPERATION, "Modifying in-use buffer %u's loop points",
1194 else if(values[0] < 0 || values[0] >= values[1]
1195 || static_cast<ALuint>(values[1]) > albuf->mSampleLen) UNLIKELY
1196 context->setError(AL_INVALID_VALUE, "Invalid loop point range %d -> %d on buffer %u",
1197 values[0], values[1], buffer);
1200 albuf->mLoopStart = static_cast<ALuint>(values[0]);
1201 albuf->mLoopEnd = static_cast<ALuint>(values[1]);
1206 context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param);
1212 AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value)
1215 ContextRef context{GetContextRef()};
1216 if(!context) UNLIKELY return;
1218 ALCdevice *device{context->mALDevice.get()};
1219 std::lock_guard<std::mutex> _{device->BufferLock};
1221 ALbuffer *albuf = LookupBuffer(device, buffer);
1223 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1224 else if(!value) UNLIKELY
1225 context->setError(AL_INVALID_VALUE, "NULL pointer");
1228 case AL_SEC_LENGTH_SOFT:
1229 *value = (albuf->mSampleRate < 1) ? 0.0f :
1230 (static_cast<float>(albuf->mSampleLen) / static_cast<float>(albuf->mSampleRate));
1234 context->setError(AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
1239 AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
1242 ContextRef context{GetContextRef()};
1243 if(!context) UNLIKELY return;
1245 ALCdevice *device{context->mALDevice.get()};
1246 std::lock_guard<std::mutex> _{device->BufferLock};
1248 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1249 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1250 else if(!value1 || !value2 || !value3) UNLIKELY
1251 context->setError(AL_INVALID_VALUE, "NULL pointer");
1255 context->setError(AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
1260 AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values)
1265 case AL_SEC_LENGTH_SOFT:
1266 alGetBufferf(buffer, param, values);
1270 ContextRef context{GetContextRef()};
1271 if(!context) UNLIKELY return;
1273 ALCdevice *device{context->mALDevice.get()};
1274 std::lock_guard<std::mutex> _{device->BufferLock};
1276 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1277 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1278 else if(!values) UNLIKELY
1279 context->setError(AL_INVALID_VALUE, "NULL pointer");
1283 context->setError(AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
1289 AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value)
1292 ContextRef context{GetContextRef()};
1293 if(!context) UNLIKELY return;
1295 ALCdevice *device{context->mALDevice.get()};
1296 std::lock_guard<std::mutex> _{device->BufferLock};
1297 ALbuffer *albuf = LookupBuffer(device, buffer);
1299 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1300 else if(!value) UNLIKELY
1301 context->setError(AL_INVALID_VALUE, "NULL pointer");
1305 *value = static_cast<ALint>(albuf->mSampleRate);
1309 *value = (albuf->mType == FmtIMA4 || albuf->mType == FmtMSADPCM) ? 4
1310 : static_cast<ALint>(albuf->bytesFromFmt() * 8);
1314 *value = static_cast<ALint>(albuf->channelsFromFmt());
1318 *value = albuf->mCallback ? 0 : static_cast<ALint>(albuf->mData.size());
1321 case AL_BYTE_LENGTH_SOFT:
1322 *value = static_cast<ALint>(albuf->mSampleLen / albuf->mBlockAlign
1323 * albuf->blockSizeFromFmt());
1326 case AL_SAMPLE_LENGTH_SOFT:
1327 *value = static_cast<ALint>(albuf->mSampleLen);
1330 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1331 *value = static_cast<ALint>(albuf->UnpackAlign);
1334 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1335 *value = static_cast<ALint>(albuf->PackAlign);
1338 case AL_AMBISONIC_LAYOUT_SOFT:
1339 *value = EnumFromAmbiLayout(albuf->mAmbiLayout);
1342 case AL_AMBISONIC_SCALING_SOFT:
1343 *value = EnumFromAmbiScaling(albuf->mAmbiScaling);
1346 case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1347 *value = static_cast<int>(albuf->UnpackAmbiOrder);
1351 context->setError(AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
1356 AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3)
1359 ContextRef context{GetContextRef()};
1360 if(!context) UNLIKELY return;
1362 ALCdevice *device{context->mALDevice.get()};
1363 std::lock_guard<std::mutex> _{device->BufferLock};
1364 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1365 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1366 else if(!value1 || !value2 || !value3) UNLIKELY
1367 context->setError(AL_INVALID_VALUE, "NULL pointer");
1371 context->setError(AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
1376 AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values)
1385 case AL_INTERNAL_FORMAT_SOFT:
1386 case AL_BYTE_LENGTH_SOFT:
1387 case AL_SAMPLE_LENGTH_SOFT:
1388 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1389 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1390 case AL_AMBISONIC_LAYOUT_SOFT:
1391 case AL_AMBISONIC_SCALING_SOFT:
1392 case AL_UNPACK_AMBISONIC_ORDER_SOFT:
1393 alGetBufferi(buffer, param, values);
1397 ContextRef context{GetContextRef()};
1398 if(!context) UNLIKELY return;
1400 ALCdevice *device{context->mALDevice.get()};
1401 std::lock_guard<std::mutex> _{device->BufferLock};
1402 ALbuffer *albuf = LookupBuffer(device, buffer);
1404 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1405 else if(!values) UNLIKELY
1406 context->setError(AL_INVALID_VALUE, "NULL pointer");
1409 case AL_LOOP_POINTS_SOFT:
1410 values[0] = static_cast<ALint>(albuf->mLoopStart);
1411 values[1] = static_cast<ALint>(albuf->mLoopEnd);
1415 context->setError(AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x", param);
1421 AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq,
1422 ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr)
1425 ContextRef context{GetContextRef()};
1426 if(!context) UNLIKELY return;
1428 ALCdevice *device{context->mALDevice.get()};
1429 std::lock_guard<std::mutex> _{device->BufferLock};
1431 ALbuffer *albuf = LookupBuffer(device, buffer);
1433 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1434 else if(freq < 1) UNLIKELY
1435 context->setError(AL_INVALID_VALUE, "Invalid sample rate %d", freq);
1436 else if(callback == nullptr) UNLIKELY
1437 context->setError(AL_INVALID_VALUE, "NULL callback");
1440 auto usrfmt = DecomposeUserFormat(format);
1441 if(!usrfmt) UNLIKELY
1442 context->setError(AL_INVALID_ENUM, "Invalid format 0x%04x", format);
1444 PrepareCallback(context.get(), albuf, freq, usrfmt->channels, usrfmt->type, callback,
1450 AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **value)
1453 ContextRef context{GetContextRef()};
1454 if(!context) UNLIKELY return;
1456 ALCdevice *device{context->mALDevice.get()};
1457 std::lock_guard<std::mutex> _{device->BufferLock};
1458 ALbuffer *albuf = LookupBuffer(device, buffer);
1460 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1461 else if(!value) UNLIKELY
1462 context->setError(AL_INVALID_VALUE, "NULL pointer");
1465 case AL_BUFFER_CALLBACK_FUNCTION_SOFT:
1466 *value = reinterpret_cast<void*>(albuf->mCallback);
1468 case AL_BUFFER_CALLBACK_USER_PARAM_SOFT:
1469 *value = albuf->mUserData;
1473 context->setError(AL_INVALID_ENUM, "Invalid buffer pointer property 0x%04x", param);
1478 AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3)
1481 ContextRef context{GetContextRef()};
1482 if(!context) UNLIKELY return;
1484 ALCdevice *device{context->mALDevice.get()};
1485 std::lock_guard<std::mutex> _{device->BufferLock};
1486 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1487 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1488 else if(!value1 || !value2 || !value3) UNLIKELY
1489 context->setError(AL_INVALID_VALUE, "NULL pointer");
1493 context->setError(AL_INVALID_ENUM, "Invalid buffer 3-pointer property 0x%04x", param);
1498 AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **values)
1503 case AL_BUFFER_CALLBACK_FUNCTION_SOFT:
1504 case AL_BUFFER_CALLBACK_USER_PARAM_SOFT:
1505 alGetBufferPtrSOFT(buffer, param, values);
1509 ContextRef context{GetContextRef()};
1510 if(!context) UNLIKELY return;
1512 ALCdevice *device{context->mALDevice.get()};
1513 std::lock_guard<std::mutex> _{device->BufferLock};
1514 if(LookupBuffer(device, buffer) == nullptr) UNLIKELY
1515 context->setError(AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1516 else if(!values) UNLIKELY
1517 context->setError(AL_INVALID_VALUE, "NULL pointer");
1521 context->setError(AL_INVALID_ENUM, "Invalid buffer pointer-vector property 0x%04x", param);
1527 BufferSubList::~BufferSubList()
1529 uint64_t usemask{~FreeMask};
1532 const int idx{al::countr_zero(usemask)};
1533 al::destroy_at(Buffers+idx);
1534 usemask &= ~(1_u64 << idx);
1536 FreeMask = ~usemask;
1543 FORCE_ALIGN ALboolean AL_APIENTRY EAXSetBufferMode(ALsizei n, const ALuint* buffers, ALint value)
1546 #define EAX_PREFIX "[EAXSetBufferMode] "
1548 const auto context = ContextRef{GetContextRef()};
1551 ERR(EAX_PREFIX "%s\n", "No current context.");
1555 if(!eax_g_is_enabled)
1557 context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled.");
1561 const auto storage = EaxStorageFromEnum(value);
1564 context->setError(AL_INVALID_ENUM, EAX_PREFIX "Unsupported X-RAM mode 0x%x", value);
1573 context->setError(AL_INVALID_VALUE, EAX_PREFIX "Buffer count %d out of range", n);
1579 context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Null AL buffers");
1583 auto device = context->mALDevice.get();
1584 std::lock_guard<std::mutex> device_lock{device->BufferLock};
1585 size_t total_needed{0};
1587 // Validate the buffers.
1589 for(auto i = 0;i < n;++i)
1591 const auto bufid = buffers[i];
1592 if(bufid == AL_NONE)
1595 const auto buffer = LookupBuffer(device, bufid);
1596 if(!buffer) UNLIKELY
1598 ERR(EAX_PREFIX "Invalid buffer ID %u.\n", bufid);
1602 /* TODO: Is the store location allowed to change for in-use buffers, or
1603 * only when not set/queued on a source?
1606 if(*storage == EaxStorage::Hardware && !buffer->eax_x_ram_is_hardware)
1608 /* FIXME: This doesn't account for duplicate buffers. When the same
1609 * buffer ID is specified multiple times in the provided list, it
1610 * counts each instance as more memory that needs to fit in X-RAM.
1612 if(std::numeric_limits<size_t>::max()-buffer->OriginalSize < total_needed) UNLIKELY
1614 context->setError(AL_OUT_OF_MEMORY, EAX_PREFIX "Size overflow (%u + %zu)\n",
1615 buffer->OriginalSize, total_needed);
1618 total_needed += buffer->OriginalSize;
1621 if(total_needed > device->eax_x_ram_free_size)
1623 context->setError(AL_OUT_OF_MEMORY,EAX_PREFIX "Out of X-RAM memory (need: %zu, avail: %u)",
1624 total_needed, device->eax_x_ram_free_size);
1630 for(auto i = 0;i < n;++i)
1632 const auto bufid = buffers[i];
1633 if(bufid == AL_NONE)
1636 const auto buffer = LookupBuffer(device, bufid);
1639 if(*storage == EaxStorage::Hardware)
1640 eax_x_ram_apply(*device, *buffer);
1642 eax_x_ram_clear(*device, *buffer);
1643 buffer->eax_x_ram_mode = *storage;
1652 FORCE_ALIGN ALenum AL_APIENTRY EAXGetBufferMode(ALuint buffer, ALint* pReserved)
1655 #define EAX_PREFIX "[EAXGetBufferMode] "
1657 const auto context = ContextRef{GetContextRef()};
1660 ERR(EAX_PREFIX "%s\n", "No current context.");
1664 if(!eax_g_is_enabled)
1666 context->setError(AL_INVALID_OPERATION, EAX_PREFIX "%s", "EAX not enabled.");
1672 context->setError(AL_INVALID_VALUE, EAX_PREFIX "%s", "Non-null reserved parameter");
1676 auto device = context->mALDevice.get();
1677 std::lock_guard<std::mutex> device_lock{device->BufferLock};
1679 const auto al_buffer = LookupBuffer(device, buffer);
1682 context->setError(AL_INVALID_NAME, EAX_PREFIX "Invalid buffer ID %u", buffer);
1686 return EnumFromEaxStorage(al_buffer->eax_x_ram_mode);
1692 #endif // ALSOFT_EAX