Add macros for the ambisonic order masks
[openal-soft.git] / OpenAL32 / alBuffer.cpp
blob6bc965c266d17bdd981eaf50bdcfd5adc7e47df8
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2007 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #ifdef HAVE_MALLOC_H
27 #include <malloc.h>
28 #endif
30 #include <tuple>
31 #include <array>
32 #include <vector>
33 #include <limits>
34 #include <algorithm>
36 #include "alMain.h"
37 #include "alcontext.h"
38 #include "alu.h"
39 #include "alError.h"
40 #include "alBuffer.h"
41 #include "sample_cvt.h"
44 namespace {
46 constexpr ALbitfieldSOFT INVALID_STORAGE_MASK{~unsigned(AL_MAP_READ_BIT_SOFT |
47 AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT)};
48 constexpr ALbitfieldSOFT MAP_READ_WRITE_FLAGS{AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT};
49 constexpr ALbitfieldSOFT INVALID_MAP_FLAGS{~unsigned(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT |
50 AL_MAP_PERSISTENT_BIT_SOFT)};
53 ALbuffer *AllocBuffer(ALCcontext *context)
55 ALCdevice *device{context->Device};
56 std::lock_guard<std::mutex> _{device->BufferLock};
57 auto sublist = std::find_if(device->BufferList.begin(), device->BufferList.end(),
58 [](const BufferSubList &entry) noexcept -> bool
59 { return entry.FreeMask != 0; }
62 auto lidx = static_cast<ALsizei>(std::distance(device->BufferList.begin(), sublist));
63 ALbuffer *buffer{nullptr};
64 ALsizei slidx{0};
65 if(LIKELY(sublist != device->BufferList.end()))
67 slidx = CTZ64(sublist->FreeMask);
68 buffer = sublist->Buffers + slidx;
70 else
72 /* Don't allocate so many list entries that the 32-bit ID could
73 * overflow...
75 if(UNLIKELY(device->BufferList.size() >= 1<<25))
77 alSetError(context, AL_OUT_OF_MEMORY, "Too many buffers allocated");
78 return nullptr;
80 device->BufferList.emplace_back();
81 sublist = device->BufferList.end() - 1;
82 sublist->FreeMask = ~U64(0);
83 sublist->Buffers = reinterpret_cast<ALbuffer*>(al_calloc(16, sizeof(ALbuffer)*64));
84 if(UNLIKELY(!sublist->Buffers))
86 device->BufferList.pop_back();
87 alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate buffer batch");
88 return nullptr;
91 slidx = 0;
92 buffer = sublist->Buffers + slidx;
95 buffer = new (buffer) ALbuffer{};
96 /* Add 1 to avoid buffer ID 0. */
97 buffer->id = ((lidx<<6) | slidx) + 1;
99 sublist->FreeMask &= ~(U64(1)<<slidx);
101 return buffer;
104 void FreeBuffer(ALCdevice *device, ALbuffer *buffer)
106 ALuint id{buffer->id - 1};
107 ALsizei lidx = id >> 6;
108 ALsizei slidx = id & 0x3f;
110 buffer->~ALbuffer();
112 device->BufferList[lidx].FreeMask |= U64(1) << slidx;
115 inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
117 ALuint lidx = (id-1) >> 6;
118 ALsizei slidx = (id-1) & 0x3f;
120 if(UNLIKELY(lidx >= device->BufferList.size()))
121 return nullptr;
122 BufferSubList &sublist = device->BufferList[lidx];
123 if(UNLIKELY(sublist.FreeMask & (U64(1)<<slidx)))
124 return nullptr;
125 return sublist.Buffers + slidx;
129 ALsizei SanitizeAlignment(UserFmtType type, ALsizei align)
131 if(align < 0)
132 return 0;
134 if(align == 0)
136 if(type == UserFmtIMA4)
138 /* Here is where things vary:
139 * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel
140 * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel
142 return 65;
144 if(type == UserFmtMSADPCM)
145 return 64;
146 return 1;
149 if(type == UserFmtIMA4)
151 /* IMA4 block alignment must be a multiple of 8, plus 1. */
152 if((align&7) == 1) return align;
153 return 0;
155 if(type == UserFmtMSADPCM)
157 /* MSADPCM block alignment must be a multiple of 2. */
158 if((align&1) == 0) return align;
159 return 0;
162 return align;
166 const ALchar *NameFromUserFmtType(UserFmtType type)
168 switch(type)
170 case UserFmtUByte: return "Unsigned Byte";
171 case UserFmtShort: return "Signed Short";
172 case UserFmtFloat: return "Float32";
173 case UserFmtDouble: return "Float64";
174 case UserFmtMulaw: return "muLaw";
175 case UserFmtAlaw: return "aLaw";
176 case UserFmtIMA4: return "IMA4 ADPCM";
177 case UserFmtMSADPCM: return "MSADPCM";
179 return "<internal type error>";
183 * LoadData
185 * Loads the specified data into the buffer, using the specified format.
187 void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALuint freq, ALsizei size, UserFmtChannels SrcChannels, UserFmtType SrcType, const ALvoid *data, ALbitfieldSOFT access)
189 if(UNLIKELY(ReadRef(&ALBuf->ref) != 0 || ALBuf->MappedAccess != 0))
190 SETERR_RETURN(context, AL_INVALID_OPERATION,, "Modifying storage for in-use buffer %u",
191 ALBuf->id);
193 /* Currently no channel configurations need to be converted. */
194 FmtChannels DstChannels{FmtMono};
195 switch(SrcChannels)
197 case UserFmtMono: DstChannels = FmtMono; break;
198 case UserFmtStereo: DstChannels = FmtStereo; break;
199 case UserFmtRear: DstChannels = FmtRear; break;
200 case UserFmtQuad: DstChannels = FmtQuad; break;
201 case UserFmtX51: DstChannels = FmtX51; break;
202 case UserFmtX61: DstChannels = FmtX61; break;
203 case UserFmtX71: DstChannels = FmtX71; break;
204 case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
205 case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
207 if(UNLIKELY((long)SrcChannels != (long)DstChannels))
208 SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid format");
210 /* IMA4 and MSADPCM convert to 16-bit short. */
211 FmtType DstType{FmtUByte};
212 switch(SrcType)
214 case UserFmtUByte: DstType = FmtUByte; break;
215 case UserFmtShort: DstType = FmtShort; break;
216 case UserFmtFloat: DstType = FmtFloat; break;
217 case UserFmtDouble: DstType = FmtDouble; break;
218 case UserFmtAlaw: DstType = FmtAlaw; break;
219 case UserFmtMulaw: DstType = FmtMulaw; break;
220 case UserFmtIMA4: DstType = FmtShort; break;
221 case UserFmtMSADPCM: DstType = FmtShort; break;
224 /* TODO: Currently we can only map samples when they're not converted. To
225 * allow it would need some kind of double-buffering to hold onto a copy of
226 * the original data.
228 if((access&MAP_READ_WRITE_FLAGS))
230 if(UNLIKELY((long)SrcType != (long)DstType))
231 SETERR_RETURN(context, AL_INVALID_VALUE,, "%s samples cannot be mapped",
232 NameFromUserFmtType(SrcType));
235 ALsizei unpackalign{ALBuf->UnpackAlign.load()};
236 ALsizei align{SanitizeAlignment(SrcType, unpackalign)};
237 if(UNLIKELY(align < 1))
238 SETERR_RETURN(context, AL_INVALID_VALUE,, "Invalid unpack alignment %d for %s samples",
239 unpackalign, NameFromUserFmtType(SrcType));
241 if((access&AL_PRESERVE_DATA_BIT_SOFT))
243 /* Can only preserve data with the same format and alignment. */
244 if(UNLIKELY(ALBuf->FmtChannels != DstChannels || ALBuf->OriginalType != SrcType))
245 SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched format");
246 if(UNLIKELY(ALBuf->OriginalAlign != align))
247 SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched alignment");
250 /* Convert the input/source size in bytes to sample frames using the unpack
251 * block alignment.
253 ALsizei SrcByteAlign{
254 (SrcType == UserFmtIMA4) ? ((align-1)/2 + 4) * ChannelsFromUserFmt(SrcChannels) :
255 (SrcType == UserFmtMSADPCM) ? ((align-2)/2 + 7) * ChannelsFromUserFmt(SrcChannels) :
256 (align * FrameSizeFromUserFmt(SrcChannels, SrcType))
258 if(UNLIKELY((size%SrcByteAlign) != 0))
259 SETERR_RETURN(context, AL_INVALID_VALUE,,
260 "Data size %d is not a multiple of frame size %d (%d unpack alignment)",
261 size, SrcByteAlign, align);
263 if(UNLIKELY(size/SrcByteAlign > std::numeric_limits<ALsizei>::max()/align))
264 SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
265 "Buffer size overflow, %d blocks x %d samples per block", size/SrcByteAlign, align);
266 ALsizei frames{size / SrcByteAlign * align};
268 /* Convert the sample frames to the number of bytes needed for internal
269 * storage.
271 ALsizei NumChannels{ChannelsFromFmt(DstChannels)};
272 ALsizei FrameSize{NumChannels * BytesFromFmt(DstType)};
273 if(UNLIKELY(frames > std::numeric_limits<ALsizei>::max()/FrameSize))
274 SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
275 "Buffer size overflow, %d frames x %d bytes per frame", frames, FrameSize);
276 ALsizei newsize{frames*FrameSize};
278 /* Round up to the next 16-byte multiple. This could reallocate only when
279 * increasing or the new size is less than half the current, but then the
280 * buffer's AL_SIZE would not be very reliable for accounting buffer memory
281 * usage, and reporting the real size could cause problems for apps that
282 * use AL_SIZE to try to get the buffer's play length.
284 if(LIKELY(newsize <= std::numeric_limits<ALsizei>::max()-15))
285 newsize = (newsize+15) & ~0xf;
286 if(newsize != ALBuf->BytesAlloc)
288 al::vector<ALbyte,16> newdata(newsize);
289 if((access&AL_PRESERVE_DATA_BIT_SOFT))
291 ALsizei tocopy{std::min(newsize, ALBuf->BytesAlloc)};
292 std::copy_n(ALBuf->mData.begin(), tocopy, newdata.begin());
294 ALBuf->mData = std::move(newdata);
295 ALBuf->BytesAlloc = newsize;
298 if(SrcType == UserFmtIMA4)
300 assert(DstType == FmtShort);
301 if(data != nullptr && !ALBuf->mData.empty())
302 Convert_ALshort_ALima4(reinterpret_cast<ALshort*>(ALBuf->mData.data()),
303 static_cast<const ALubyte*>(data), NumChannels, frames, align);
304 ALBuf->OriginalAlign = align;
306 else if(SrcType == UserFmtMSADPCM)
308 assert(DstType == FmtShort);
309 if(data != nullptr && !ALBuf->mData.empty())
310 Convert_ALshort_ALmsadpcm(reinterpret_cast<ALshort*>(ALBuf->mData.data()),
311 static_cast<const ALubyte*>(data), NumChannels, frames, align);
312 ALBuf->OriginalAlign = align;
314 else
316 assert((long)SrcType == (long)DstType);
317 if(data != nullptr && !ALBuf->mData.empty())
318 std::copy_n(static_cast<const ALbyte*>(data), frames*FrameSize, ALBuf->mData.begin());
319 ALBuf->OriginalAlign = 1;
321 ALBuf->OriginalSize = size;
322 ALBuf->OriginalType = SrcType;
324 ALBuf->Frequency = freq;
325 ALBuf->FmtChannels = DstChannels;
326 ALBuf->FmtType = DstType;
327 ALBuf->Access = access;
329 ALBuf->SampleLen = frames;
330 ALBuf->LoopStart = 0;
331 ALBuf->LoopEnd = ALBuf->SampleLen;
334 using DecompResult = std::tuple<bool, UserFmtChannels, UserFmtType>;
335 DecompResult DecomposeUserFormat(ALenum format)
337 struct FormatMap {
338 ALenum format;
339 UserFmtChannels channels;
340 UserFmtType type;
342 static constexpr std::array<FormatMap,46> UserFmtList{{
343 { AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte },
344 { AL_FORMAT_MONO16, UserFmtMono, UserFmtShort },
345 { AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat },
346 { AL_FORMAT_MONO_DOUBLE_EXT, UserFmtMono, UserFmtDouble },
347 { AL_FORMAT_MONO_IMA4, UserFmtMono, UserFmtIMA4 },
348 { AL_FORMAT_MONO_MSADPCM_SOFT, UserFmtMono, UserFmtMSADPCM },
349 { AL_FORMAT_MONO_MULAW, UserFmtMono, UserFmtMulaw },
350 { AL_FORMAT_MONO_ALAW_EXT, UserFmtMono, UserFmtAlaw },
352 { AL_FORMAT_STEREO8, UserFmtStereo, UserFmtUByte },
353 { AL_FORMAT_STEREO16, UserFmtStereo, UserFmtShort },
354 { AL_FORMAT_STEREO_FLOAT32, UserFmtStereo, UserFmtFloat },
355 { AL_FORMAT_STEREO_DOUBLE_EXT, UserFmtStereo, UserFmtDouble },
356 { AL_FORMAT_STEREO_IMA4, UserFmtStereo, UserFmtIMA4 },
357 { AL_FORMAT_STEREO_MSADPCM_SOFT, UserFmtStereo, UserFmtMSADPCM },
358 { AL_FORMAT_STEREO_MULAW, UserFmtStereo, UserFmtMulaw },
359 { AL_FORMAT_STEREO_ALAW_EXT, UserFmtStereo, UserFmtAlaw },
361 { AL_FORMAT_REAR8, UserFmtRear, UserFmtUByte },
362 { AL_FORMAT_REAR16, UserFmtRear, UserFmtShort },
363 { AL_FORMAT_REAR32, UserFmtRear, UserFmtFloat },
364 { AL_FORMAT_REAR_MULAW, UserFmtRear, UserFmtMulaw },
366 { AL_FORMAT_QUAD8_LOKI, UserFmtQuad, UserFmtUByte },
367 { AL_FORMAT_QUAD16_LOKI, UserFmtQuad, UserFmtShort },
369 { AL_FORMAT_QUAD8, UserFmtQuad, UserFmtUByte },
370 { AL_FORMAT_QUAD16, UserFmtQuad, UserFmtShort },
371 { AL_FORMAT_QUAD32, UserFmtQuad, UserFmtFloat },
372 { AL_FORMAT_QUAD_MULAW, UserFmtQuad, UserFmtMulaw },
374 { AL_FORMAT_51CHN8, UserFmtX51, UserFmtUByte },
375 { AL_FORMAT_51CHN16, UserFmtX51, UserFmtShort },
376 { AL_FORMAT_51CHN32, UserFmtX51, UserFmtFloat },
377 { AL_FORMAT_51CHN_MULAW, UserFmtX51, UserFmtMulaw },
379 { AL_FORMAT_61CHN8, UserFmtX61, UserFmtUByte },
380 { AL_FORMAT_61CHN16, UserFmtX61, UserFmtShort },
381 { AL_FORMAT_61CHN32, UserFmtX61, UserFmtFloat },
382 { AL_FORMAT_61CHN_MULAW, UserFmtX61, UserFmtMulaw },
384 { AL_FORMAT_71CHN8, UserFmtX71, UserFmtUByte },
385 { AL_FORMAT_71CHN16, UserFmtX71, UserFmtShort },
386 { AL_FORMAT_71CHN32, UserFmtX71, UserFmtFloat },
387 { AL_FORMAT_71CHN_MULAW, UserFmtX71, UserFmtMulaw },
389 { AL_FORMAT_BFORMAT2D_8, UserFmtBFormat2D, UserFmtUByte },
390 { AL_FORMAT_BFORMAT2D_16, UserFmtBFormat2D, UserFmtShort },
391 { AL_FORMAT_BFORMAT2D_FLOAT32, UserFmtBFormat2D, UserFmtFloat },
392 { AL_FORMAT_BFORMAT2D_MULAW, UserFmtBFormat2D, UserFmtMulaw },
394 { AL_FORMAT_BFORMAT3D_8, UserFmtBFormat3D, UserFmtUByte },
395 { AL_FORMAT_BFORMAT3D_16, UserFmtBFormat3D, UserFmtShort },
396 { AL_FORMAT_BFORMAT3D_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
397 { AL_FORMAT_BFORMAT3D_MULAW, UserFmtBFormat3D, UserFmtMulaw },
400 DecompResult ret{};
401 for(const auto &fmt : UserFmtList)
403 if(fmt.format == format)
405 std::get<0>(ret) = true;
406 std::get<1>(ret) = fmt.channels;
407 std::get<2>(ret) = fmt.type;
408 break;
411 return ret;
414 } // namespace
417 AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers)
419 ContextRef context{GetContextRef()};
420 if(UNLIKELY(!context)) return;
422 if(UNLIKELY(n < 0))
424 alSetError(context.get(), AL_INVALID_VALUE, "Generating %d buffers", n);
425 return;
428 if(LIKELY(n == 1))
430 /* Special handling for the easy and normal case. */
431 ALbuffer *buffer = AllocBuffer(context.get());
432 if(buffer) buffers[0] = buffer->id;
434 else if(n > 1)
436 /* Store the allocated buffer IDs in a separate local list, to avoid
437 * modifying the user storage in case of failure.
439 al::vector<ALuint> ids;
440 ids.reserve(n);
441 do {
442 ALbuffer *buffer = AllocBuffer(context.get());
443 if(!buffer)
445 alDeleteBuffers(static_cast<ALsizei>(ids.size()), ids.data());
446 return;
449 ids.emplace_back(buffer->id);
450 } while(--n);
451 std::copy(ids.begin(), ids.end(), buffers);
455 AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
457 ContextRef context{GetContextRef()};
458 if(UNLIKELY(!context)) return;
460 if(UNLIKELY(n < 0))
462 alSetError(context.get(), AL_INVALID_VALUE, "Deleting %d buffers", n);
463 return;
465 if(UNLIKELY(n == 0))
466 return;
468 ALCdevice *device = context->Device;
469 std::lock_guard<std::mutex> _{device->BufferLock};
471 /* First try to find any buffers that are invalid or in-use. */
472 const ALuint *buffers_end = buffers + n;
473 auto invbuf = std::find_if(buffers, buffers_end,
474 [device, &context](ALuint bid) -> bool
476 if(!bid) return false;
477 ALbuffer *ALBuf = LookupBuffer(device, bid);
478 if(UNLIKELY(!ALBuf))
480 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", bid);
481 return true;
483 if(UNLIKELY(ReadRef(&ALBuf->ref) != 0))
485 alSetError(context.get(), AL_INVALID_OPERATION, "Deleting in-use buffer %u", bid);
486 return true;
488 return false;
491 if(LIKELY(invbuf == buffers_end))
493 /* All good. Delete non-0 buffer IDs. */
494 std::for_each(buffers, buffers_end,
495 [device](ALuint bid) -> void
497 ALbuffer *buffer{bid ? LookupBuffer(device, bid) : nullptr};
498 if(buffer) FreeBuffer(device, buffer);
504 AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
506 ContextRef context{GetContextRef()};
507 if(LIKELY(context))
509 ALCdevice *device = context->Device;
510 std::lock_guard<std::mutex> _{device->BufferLock};
511 if(!buffer || LookupBuffer(device, buffer))
512 return AL_TRUE;
514 return AL_FALSE;
518 AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
519 { alBufferStorageSOFT(buffer, format, data, size, freq, 0); }
521 AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags)
523 ContextRef context{GetContextRef()};
524 if(UNLIKELY(!context)) return;
526 ALCdevice *device = context->Device;
527 std::lock_guard<std::mutex> _{device->BufferLock};
529 ALbuffer *albuf = LookupBuffer(device, buffer);
530 if(UNLIKELY(!albuf))
531 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
532 else if(UNLIKELY(size < 0))
533 alSetError(context.get(), AL_INVALID_VALUE, "Negative storage size %d", size);
534 else if(UNLIKELY(freq < 1))
535 alSetError(context.get(), AL_INVALID_VALUE, "Invalid sample rate %d", freq);
536 else if(UNLIKELY((flags&INVALID_STORAGE_MASK) != 0))
537 alSetError(context.get(), AL_INVALID_VALUE, "Invalid storage flags 0x%x",
538 flags&INVALID_STORAGE_MASK);
539 else if(UNLIKELY((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS)))
540 alSetError(context.get(), AL_INVALID_VALUE,
541 "Declaring persistently mapped storage without read or write access");
542 else
544 UserFmtType srctype{UserFmtUByte};
545 UserFmtChannels srcchannels{UserFmtMono};
546 bool success;
548 std::tie(success, srcchannels, srctype) = DecomposeUserFormat(format);
549 if(UNLIKELY(!success))
550 alSetError(context.get(), AL_INVALID_ENUM, "Invalid format 0x%04x", format);
551 else
552 LoadData(context.get(), albuf, freq, size, srcchannels, srctype, data, flags);
556 AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access)
558 ContextRef context{GetContextRef()};
559 if(UNLIKELY(!context)) return nullptr;
561 ALCdevice *device = context->Device;
562 std::lock_guard<std::mutex> _{device->BufferLock};
564 ALbuffer *albuf = LookupBuffer(device, buffer);
565 if(UNLIKELY(!albuf))
566 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
567 else if(UNLIKELY((access&INVALID_MAP_FLAGS) != 0))
568 alSetError(context.get(), AL_INVALID_VALUE, "Invalid map flags 0x%x", access&INVALID_MAP_FLAGS);
569 else if(UNLIKELY(!(access&MAP_READ_WRITE_FLAGS)))
570 alSetError(context.get(), AL_INVALID_VALUE, "Mapping buffer %u without read or write access",
571 buffer);
572 else
574 ALbitfieldSOFT unavailable = (albuf->Access^access) & access;
575 if(UNLIKELY(ReadRef(&albuf->ref) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT)))
576 alSetError(context.get(), AL_INVALID_OPERATION,
577 "Mapping in-use buffer %u without persistent mapping", buffer);
578 else if(UNLIKELY(albuf->MappedAccess != 0))
579 alSetError(context.get(), AL_INVALID_OPERATION, "Mapping already-mapped buffer %u", buffer);
580 else if(UNLIKELY((unavailable&AL_MAP_READ_BIT_SOFT)))
581 alSetError(context.get(), AL_INVALID_VALUE,
582 "Mapping buffer %u for reading without read access", buffer);
583 else if(UNLIKELY((unavailable&AL_MAP_WRITE_BIT_SOFT)))
584 alSetError(context.get(), AL_INVALID_VALUE,
585 "Mapping buffer %u for writing without write access", buffer);
586 else if(UNLIKELY((unavailable&AL_MAP_PERSISTENT_BIT_SOFT)))
587 alSetError(context.get(), AL_INVALID_VALUE,
588 "Mapping buffer %u persistently without persistent access", buffer);
589 else if(UNLIKELY(offset < 0 || offset >= albuf->OriginalSize ||
590 length <= 0 || length > albuf->OriginalSize - offset))
591 alSetError(context.get(), AL_INVALID_VALUE, "Mapping invalid range %d+%d for buffer %u",
592 offset, length, buffer);
593 else
595 void *retval = albuf->mData.data() + offset;
596 albuf->MappedAccess = access;
597 albuf->MappedOffset = offset;
598 albuf->MappedSize = length;
599 return retval;
603 return nullptr;
606 AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer)
608 ContextRef context{GetContextRef()};
609 if(UNLIKELY(!context)) return;
611 ALCdevice *device = context->Device;
612 std::lock_guard<std::mutex> _{device->BufferLock};
614 ALbuffer *albuf = LookupBuffer(device, buffer);
615 if(UNLIKELY(!albuf))
616 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
617 else if(albuf->MappedAccess == 0)
618 alSetError(context.get(), AL_INVALID_OPERATION, "Unmapping unmapped buffer %u", buffer);
619 else
621 albuf->MappedAccess = 0;
622 albuf->MappedOffset = 0;
623 albuf->MappedSize = 0;
627 AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length)
629 ContextRef context{GetContextRef()};
630 if(UNLIKELY(!context)) return;
632 ALCdevice *device = context->Device;
633 std::lock_guard<std::mutex> _{device->BufferLock};
635 ALbuffer *albuf = LookupBuffer(device, buffer);
636 if(UNLIKELY(!albuf))
637 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
638 else if(UNLIKELY(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT)))
639 alSetError(context.get(), AL_INVALID_OPERATION,
640 "Flushing buffer %u while not mapped for writing", buffer);
641 else if(UNLIKELY(offset < albuf->MappedOffset ||
642 offset >= albuf->MappedOffset+albuf->MappedSize ||
643 length <= 0 || length > albuf->MappedOffset+albuf->MappedSize-offset))
644 alSetError(context.get(), AL_INVALID_VALUE, "Flushing invalid range %d+%d on buffer %u",
645 offset, length, buffer);
646 else
648 /* FIXME: Need to use some method of double-buffering for the mixer and
649 * app to hold separate memory, which can be safely transfered
650 * asynchronously. Currently we just say the app shouldn't write where
651 * OpenAL's reading, and hope for the best...
653 std::atomic_thread_fence(std::memory_order_seq_cst);
657 AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length)
659 ContextRef context{GetContextRef()};
660 if(UNLIKELY(!context)) return;
662 ALCdevice *device = context->Device;
663 std::lock_guard<std::mutex> _{device->BufferLock};
665 ALbuffer *albuf = LookupBuffer(device, buffer);
666 if(UNLIKELY(!albuf))
668 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
669 return;
672 UserFmtType srctype{UserFmtUByte};
673 UserFmtChannels srcchannels{UserFmtMono};
674 bool success;
675 std::tie(success, srcchannels, srctype) = DecomposeUserFormat(format);
676 if(UNLIKELY(!success))
678 alSetError(context.get(), AL_INVALID_ENUM, "Invalid format 0x%04x", format);
679 return;
682 ALsizei unpack_align{albuf->UnpackAlign.load()};
683 ALsizei align{SanitizeAlignment(srctype, unpack_align)};
684 if(UNLIKELY(align < 1))
685 alSetError(context.get(), AL_INVALID_VALUE, "Invalid unpack alignment %d", unpack_align);
686 else if(UNLIKELY((long)srcchannels != (long)albuf->FmtChannels ||
687 srctype != albuf->OriginalType))
688 alSetError(context.get(), AL_INVALID_ENUM, "Unpacking data with mismatched format");
689 else if(UNLIKELY(align != albuf->OriginalAlign))
690 alSetError(context.get(), AL_INVALID_VALUE,
691 "Unpacking data with alignment %u does not match original alignment %u",
692 align, albuf->OriginalAlign);
693 else if(UNLIKELY(albuf->MappedAccess != 0))
694 alSetError(context.get(), AL_INVALID_OPERATION, "Unpacking data into mapped buffer %u",
695 buffer);
696 else
698 ALsizei num_chans{ChannelsFromFmt(albuf->FmtChannels)};
699 ALsizei frame_size{num_chans * BytesFromFmt(albuf->FmtType)};
700 ALsizei byte_align{
701 (albuf->OriginalType == UserFmtIMA4) ? ((align-1)/2 + 4) * num_chans :
702 (albuf->OriginalType == UserFmtMSADPCM) ? ((align-2)/2 + 7) * num_chans :
703 (align * frame_size)
706 if(UNLIKELY(offset < 0 || length < 0 || offset > albuf->OriginalSize ||
707 length > albuf->OriginalSize-offset))
708 alSetError(context.get(), AL_INVALID_VALUE, "Invalid data sub-range %d+%d on buffer %u",
709 offset, length, buffer);
710 else if(UNLIKELY((offset%byte_align) != 0))
711 alSetError(context.get(), AL_INVALID_VALUE,
712 "Sub-range offset %d is not a multiple of frame size %d (%d unpack alignment)",
713 offset, byte_align, align);
714 else if(UNLIKELY((length%byte_align) != 0))
715 alSetError(context.get(), AL_INVALID_VALUE,
716 "Sub-range length %d is not a multiple of frame size %d (%d unpack alignment)",
717 length, byte_align, align);
718 else
720 /* offset -> byte offset, length -> sample count */
721 offset = offset/byte_align * align * frame_size;
722 length = length/byte_align * align;
724 void *dst = albuf->mData.data() + offset;
725 if(srctype == UserFmtIMA4 && albuf->FmtType == FmtShort)
726 Convert_ALshort_ALima4(static_cast<ALshort*>(dst),
727 static_cast<const ALubyte*>(data), num_chans, length, align);
728 else if(srctype == UserFmtMSADPCM && albuf->FmtType == FmtShort)
729 Convert_ALshort_ALmsadpcm(static_cast<ALshort*>(dst),
730 static_cast<const ALubyte*>(data), num_chans, length, align);
731 else
733 assert((long)srctype == (long)albuf->FmtType);
734 memcpy(dst, data, length*frame_size);
741 AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint UNUSED(buffer),
742 ALuint UNUSED(samplerate), ALenum UNUSED(internalformat), ALsizei UNUSED(samples),
743 ALenum UNUSED(channels), ALenum UNUSED(type), const ALvoid *UNUSED(data))
745 ContextRef context{GetContextRef()};
746 if(UNLIKELY(!context)) return;
748 alSetError(context.get(), AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported");
751 AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint UNUSED(buffer),
752 ALsizei UNUSED(offset), ALsizei UNUSED(samples),
753 ALenum UNUSED(channels), ALenum UNUSED(type), const ALvoid *UNUSED(data))
755 ContextRef context{GetContextRef()};
756 if(UNLIKELY(!context)) return;
758 alSetError(context.get(), AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported");
761 AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint UNUSED(buffer),
762 ALsizei UNUSED(offset), ALsizei UNUSED(samples),
763 ALenum UNUSED(channels), ALenum UNUSED(type), ALvoid *UNUSED(data))
765 ContextRef context{GetContextRef()};
766 if(UNLIKELY(!context)) return;
768 alSetError(context.get(), AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported");
771 AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum UNUSED(format))
773 ContextRef context{GetContextRef()};
774 if(!context) return AL_FALSE;
776 alSetError(context.get(), AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported");
777 return AL_FALSE;
781 AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(value))
783 ContextRef context{GetContextRef()};
784 if(UNLIKELY(!context)) return;
786 ALCdevice *device = context->Device;
787 std::lock_guard<std::mutex> _{device->BufferLock};
789 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
790 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
791 else switch(param)
793 default:
794 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
799 AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(value1), ALfloat UNUSED(value2), ALfloat UNUSED(value3))
801 ContextRef context{GetContextRef()};
802 if(UNLIKELY(!context)) return;
804 ALCdevice *device = context->Device;
805 std::lock_guard<std::mutex> _{device->BufferLock};
807 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
808 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
809 else switch(param)
811 default:
812 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
817 AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values)
819 ContextRef context{GetContextRef()};
820 if(UNLIKELY(!context)) return;
822 ALCdevice *device = context->Device;
823 std::lock_guard<std::mutex> _{device->BufferLock};
825 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
826 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
827 else if(UNLIKELY(!values))
828 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
829 else switch(param)
831 default:
832 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
837 AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
839 ContextRef context{GetContextRef()};
840 if(UNLIKELY(!context)) return;
842 ALCdevice *device = context->Device;
843 std::lock_guard<std::mutex> _{device->BufferLock};
845 ALbuffer *albuf = LookupBuffer(device, buffer);
846 if(UNLIKELY(!albuf))
847 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
848 else switch(param)
850 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
851 if(UNLIKELY(value < 0))
852 alSetError(context.get(), AL_INVALID_VALUE, "Invalid unpack block alignment %d", value);
853 else
854 albuf->UnpackAlign.store(value);
855 break;
857 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
858 if(UNLIKELY(value < 0))
859 alSetError(context.get(), AL_INVALID_VALUE, "Invalid pack block alignment %d", value);
860 else
861 albuf->PackAlign.store(value);
862 break;
864 default:
865 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
870 AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint UNUSED(value1), ALint UNUSED(value2), ALint UNUSED(value3))
872 ContextRef context{GetContextRef()};
873 if(UNLIKELY(!context)) return;
875 ALCdevice *device = context->Device;
876 std::lock_guard<std::mutex> _{device->BufferLock};
878 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
879 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
880 else switch(param)
882 default:
883 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
888 AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values)
890 if(values)
892 switch(param)
894 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
895 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
896 alBufferi(buffer, param, values[0]);
897 return;
901 ContextRef context{GetContextRef()};
902 if(UNLIKELY(!context)) return;
904 ALCdevice *device = context->Device;
905 std::lock_guard<std::mutex> _{device->BufferLock};
907 ALbuffer *albuf = LookupBuffer(device, buffer);
908 if(UNLIKELY(!albuf))
909 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
910 else if(UNLIKELY(!values))
911 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
912 else switch(param)
914 case AL_LOOP_POINTS_SOFT:
915 if(UNLIKELY(ReadRef(&albuf->ref) != 0))
916 alSetError(context.get(), AL_INVALID_OPERATION, "Modifying in-use buffer %u's loop points",
917 buffer);
918 else if(UNLIKELY(values[0] >= values[1] || values[0] < 0 || values[1] > albuf->SampleLen))
919 alSetError(context.get(), AL_INVALID_VALUE, "Invalid loop point range %d -> %d o buffer %u",
920 values[0], values[1], buffer);
921 else
923 albuf->LoopStart = values[0];
924 albuf->LoopEnd = values[1];
926 break;
928 default:
929 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x",
930 param);
935 AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value)
937 ContextRef context{GetContextRef()};
938 if(UNLIKELY(!context)) return;
940 ALCdevice *device = context->Device;
941 std::lock_guard<std::mutex> _{device->BufferLock};
943 ALbuffer *albuf = LookupBuffer(device, buffer);
944 if(UNLIKELY(!albuf))
945 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
946 else if(UNLIKELY(!value))
947 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
948 else switch(param)
950 default:
951 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
956 AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
958 ContextRef context{GetContextRef()};
959 if(UNLIKELY(!context)) return;
961 ALCdevice *device = context->Device;
962 std::lock_guard<std::mutex> _{device->BufferLock};
964 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
965 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
966 else if(UNLIKELY(!value1 || !value2 || !value3))
967 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
968 else switch(param)
970 default:
971 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
976 AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values)
978 switch(param)
980 case AL_SEC_LENGTH_SOFT:
981 alGetBufferf(buffer, param, values);
982 return;
985 ContextRef context{GetContextRef()};
986 if(UNLIKELY(!context)) return;
988 ALCdevice *device = context->Device;
989 std::lock_guard<std::mutex> _{device->BufferLock};
991 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
992 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
993 else if(UNLIKELY(!values))
994 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
995 else switch(param)
997 default:
998 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
1003 AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value)
1005 ContextRef context{GetContextRef()};
1006 if(UNLIKELY(!context)) return;
1008 ALCdevice *device = context->Device;
1009 std::lock_guard<std::mutex> _{device->BufferLock};
1010 ALbuffer *albuf = LookupBuffer(device, buffer);
1011 if(UNLIKELY(!albuf))
1012 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1013 else if(UNLIKELY(!value))
1014 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
1015 else switch(param)
1017 case AL_FREQUENCY:
1018 *value = albuf->Frequency;
1019 break;
1021 case AL_BITS:
1022 *value = BytesFromFmt(albuf->FmtType) * 8;
1023 break;
1025 case AL_CHANNELS:
1026 *value = ChannelsFromFmt(albuf->FmtChannels);
1027 break;
1029 case AL_SIZE:
1030 *value = albuf->SampleLen * FrameSizeFromFmt(albuf->FmtChannels,
1031 albuf->FmtType);
1032 break;
1034 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1035 *value = albuf->UnpackAlign.load();
1036 break;
1038 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1039 *value = albuf->PackAlign.load();
1040 break;
1042 default:
1043 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
1048 AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3)
1050 ContextRef context{GetContextRef()};
1051 if(UNLIKELY(!context)) return;
1053 ALCdevice *device = context->Device;
1054 std::lock_guard<std::mutex> _{device->BufferLock};
1055 if(UNLIKELY(LookupBuffer(device, buffer) == nullptr))
1056 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1057 else if(UNLIKELY(!value1 || !value2 || !value3))
1058 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
1059 else switch(param)
1061 default:
1062 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
1067 AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values)
1069 switch(param)
1071 case AL_FREQUENCY:
1072 case AL_BITS:
1073 case AL_CHANNELS:
1074 case AL_SIZE:
1075 case AL_INTERNAL_FORMAT_SOFT:
1076 case AL_BYTE_LENGTH_SOFT:
1077 case AL_SAMPLE_LENGTH_SOFT:
1078 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
1079 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
1080 alGetBufferi(buffer, param, values);
1081 return;
1084 ContextRef context{GetContextRef()};
1085 if(UNLIKELY(!context)) return;
1087 ALCdevice *device = context->Device;
1088 std::lock_guard<std::mutex> _{device->BufferLock};
1089 ALbuffer *albuf = LookupBuffer(device, buffer);
1090 if(UNLIKELY(!albuf))
1091 alSetError(context.get(), AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
1092 else if(UNLIKELY(!values))
1093 alSetError(context.get(), AL_INVALID_VALUE, "NULL pointer");
1094 else switch(param)
1096 case AL_LOOP_POINTS_SOFT:
1097 values[0] = albuf->LoopStart;
1098 values[1] = albuf->LoopEnd;
1099 break;
1101 default:
1102 alSetError(context.get(), AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x",
1103 param);
1108 ALsizei BytesFromUserFmt(UserFmtType type)
1110 switch(type)
1112 case UserFmtUByte: return sizeof(ALubyte);
1113 case UserFmtShort: return sizeof(ALshort);
1114 case UserFmtFloat: return sizeof(ALfloat);
1115 case UserFmtDouble: return sizeof(ALdouble);
1116 case UserFmtMulaw: return sizeof(ALubyte);
1117 case UserFmtAlaw: return sizeof(ALubyte);
1118 case UserFmtIMA4: break; /* not handled here */
1119 case UserFmtMSADPCM: break; /* not handled here */
1121 return 0;
1123 ALsizei ChannelsFromUserFmt(UserFmtChannels chans)
1125 switch(chans)
1127 case UserFmtMono: return 1;
1128 case UserFmtStereo: return 2;
1129 case UserFmtRear: return 2;
1130 case UserFmtQuad: return 4;
1131 case UserFmtX51: return 6;
1132 case UserFmtX61: return 7;
1133 case UserFmtX71: return 8;
1134 case UserFmtBFormat2D: return 3;
1135 case UserFmtBFormat3D: return 4;
1137 return 0;
1140 ALsizei BytesFromFmt(FmtType type)
1142 switch(type)
1144 case FmtUByte: return sizeof(ALubyte);
1145 case FmtShort: return sizeof(ALshort);
1146 case FmtFloat: return sizeof(ALfloat);
1147 case FmtDouble: return sizeof(ALdouble);
1148 case FmtMulaw: return sizeof(ALubyte);
1149 case FmtAlaw: return sizeof(ALubyte);
1151 return 0;
1153 ALsizei ChannelsFromFmt(FmtChannels chans)
1155 switch(chans)
1157 case FmtMono: return 1;
1158 case FmtStereo: return 2;
1159 case FmtRear: return 2;
1160 case FmtQuad: return 4;
1161 case FmtX51: return 6;
1162 case FmtX61: return 7;
1163 case FmtX71: return 8;
1164 case FmtBFormat2D: return 3;
1165 case FmtBFormat3D: return 4;
1167 return 0;
1171 BufferSubList::~BufferSubList()
1173 ALuint64 usemask = ~FreeMask;
1174 while(usemask)
1176 ALsizei idx{CTZ64(usemask)};
1177 Buffers[idx].~ALbuffer();
1178 usemask &= ~(U64(1) << idx);
1180 FreeMask = ~usemask;
1181 al_free(Buffers);
1182 Buffers = nullptr;