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
35 #include "sample_cvt.h"
38 extern inline void LockBufferList(ALCdevice
*device
);
39 extern inline void UnlockBufferList(ALCdevice
*device
);
40 extern inline ALsizei
FrameSizeFromUserFmt(enum UserFmtChannels chans
, enum UserFmtType type
);
41 extern inline ALsizei
FrameSizeFromFmt(enum FmtChannels chans
, enum FmtType type
);
43 static ALbuffer
*AllocBuffer(ALCcontext
*context
);
44 static void FreeBuffer(ALCdevice
*device
, ALbuffer
*buffer
);
45 static const ALchar
*NameFromUserFmtType(enum UserFmtType type
);
46 static void LoadData(ALCcontext
*context
, ALbuffer
*buffer
, ALuint freq
, ALsizei size
,
47 enum UserFmtChannels SrcChannels
, enum UserFmtType SrcType
,
48 const ALvoid
*data
, ALbitfieldSOFT access
);
49 static ALboolean
DecomposeUserFormat(ALenum format
, enum UserFmtChannels
*chans
, enum UserFmtType
*type
);
50 static ALsizei
SanitizeAlignment(enum UserFmtType type
, ALsizei align
);
52 static inline ALbuffer
*LookupBuffer(ALCdevice
*device
, ALuint id
)
54 BufferSubList
*sublist
;
55 ALuint lidx
= (id
-1) >> 6;
56 ALsizei slidx
= (id
-1) & 0x3f;
58 if(UNLIKELY(lidx
>= VECTOR_SIZE(device
->BufferList
)))
60 sublist
= &VECTOR_ELEM(device
->BufferList
, lidx
);
61 if(UNLIKELY(sublist
->FreeMask
& (U64(1)<<slidx
)))
63 return sublist
->Buffers
+ slidx
;
67 #define INVALID_STORAGE_MASK ~(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT)
68 #define MAP_READ_WRITE_FLAGS (AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT)
69 #define INVALID_MAP_FLAGS ~(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT)
72 AL_API ALvoid AL_APIENTRY
alGenBuffers(ALsizei n
, ALuint
*buffers
)
77 context
= GetContextRef();
81 alSetError(context
, AL_INVALID_VALUE
, "Generating %d buffers", n
);
82 else for(cur
= 0;cur
< n
;cur
++)
84 ALbuffer
*buffer
= AllocBuffer(context
);
87 alDeleteBuffers(cur
, buffers
);
91 buffers
[cur
] = buffer
->id
;
94 ALCcontext_DecRef(context
);
97 AL_API ALvoid AL_APIENTRY
alDeleteBuffers(ALsizei n
, const ALuint
*buffers
)
104 context
= GetContextRef();
107 device
= context
->Device
;
109 LockBufferList(device
);
112 alSetError(context
, AL_INVALID_VALUE
, "Deleting %d buffers", n
);
121 /* Check for valid Buffer ID, and make sure it's not in use. */
122 if((ALBuf
=LookupBuffer(device
, buffers
[i
])) == NULL
)
124 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffers
[i
]);
127 if(ReadRef(&ALBuf
->ref
) != 0)
129 alSetError(context
, AL_INVALID_OPERATION
, "Deleting in-use buffer %u", buffers
[i
]);
135 if((ALBuf
=LookupBuffer(device
, buffers
[i
])) != NULL
)
136 FreeBuffer(device
, ALBuf
);
140 UnlockBufferList(device
);
141 ALCcontext_DecRef(context
);
144 AL_API ALboolean AL_APIENTRY
alIsBuffer(ALuint buffer
)
149 context
= GetContextRef();
150 if(!context
) return AL_FALSE
;
152 LockBufferList(context
->Device
);
153 ret
= ((!buffer
|| LookupBuffer(context
->Device
, buffer
)) ?
155 UnlockBufferList(context
->Device
);
157 ALCcontext_DecRef(context
);
163 AL_API ALvoid AL_APIENTRY
alBufferData(ALuint buffer
, ALenum format
, const ALvoid
*data
, ALsizei size
, ALsizei freq
)
164 { alBufferStorageSOFT(buffer
, format
, data
, size
, freq
, 0); }
166 AL_API
void AL_APIENTRY
alBufferStorageSOFT(ALuint buffer
, ALenum format
, const ALvoid
*data
, ALsizei size
, ALsizei freq
, ALbitfieldSOFT flags
)
168 enum UserFmtChannels srcchannels
= UserFmtMono
;
169 enum UserFmtType srctype
= UserFmtUByte
;
174 context
= GetContextRef();
177 device
= context
->Device
;
178 LockBufferList(device
);
179 if(UNLIKELY((albuf
=LookupBuffer(device
, buffer
)) == NULL
))
180 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
181 else if(UNLIKELY(size
< 0))
182 alSetError(context
, AL_INVALID_VALUE
, "Negative storage size %d", size
);
183 else if(UNLIKELY(freq
< 1))
184 alSetError(context
, AL_INVALID_VALUE
, "Invalid sample rate %d", freq
);
185 else if(UNLIKELY((flags
&INVALID_STORAGE_MASK
) != 0))
186 alSetError(context
, AL_INVALID_VALUE
, "Invalid storage flags 0x%x",
187 flags
&INVALID_STORAGE_MASK
);
188 else if(UNLIKELY((flags
&AL_MAP_PERSISTENT_BIT_SOFT
) && !(flags
&MAP_READ_WRITE_FLAGS
)))
189 alSetError(context
, AL_INVALID_VALUE
,
190 "Declaring persistently mapped storage without read or write access");
191 else if(UNLIKELY(DecomposeUserFormat(format
, &srcchannels
, &srctype
) == AL_FALSE
))
192 alSetError(context
, AL_INVALID_ENUM
, "Invalid format 0x%04x", format
);
194 LoadData(context
, albuf
, freq
, size
, srcchannels
, srctype
, data
, flags
);
196 UnlockBufferList(device
);
197 ALCcontext_DecRef(context
);
200 AL_API
void* AL_APIENTRY
alMapBufferSOFT(ALuint buffer
, ALsizei offset
, ALsizei length
, ALbitfieldSOFT access
)
207 context
= GetContextRef();
208 if(!context
) return retval
;
210 device
= context
->Device
;
211 LockBufferList(device
);
212 if(UNLIKELY((albuf
=LookupBuffer(device
, buffer
)) == NULL
))
213 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
214 else if(UNLIKELY((access
&INVALID_MAP_FLAGS
) != 0))
215 alSetError(context
, AL_INVALID_VALUE
, "Invalid map flags 0x%x", access
&INVALID_MAP_FLAGS
);
216 else if(UNLIKELY(!(access
&MAP_READ_WRITE_FLAGS
)))
217 alSetError(context
, AL_INVALID_VALUE
, "Mapping buffer %u without read or write access",
221 ALbitfieldSOFT unavailable
= (albuf
->Access
^access
) & access
;
222 if(UNLIKELY(ReadRef(&albuf
->ref
) != 0 && !(access
&AL_MAP_PERSISTENT_BIT_SOFT
)))
223 alSetError(context
, AL_INVALID_OPERATION
,
224 "Mapping in-use buffer %u without persistent mapping", buffer
);
225 else if(UNLIKELY(albuf
->MappedAccess
!= 0))
226 alSetError(context
, AL_INVALID_OPERATION
, "Mapping already-mapped buffer %u", buffer
);
227 else if(UNLIKELY((unavailable
&AL_MAP_READ_BIT_SOFT
)))
228 alSetError(context
, AL_INVALID_VALUE
,
229 "Mapping buffer %u for reading without read access", buffer
);
230 else if(UNLIKELY((unavailable
&AL_MAP_WRITE_BIT_SOFT
)))
231 alSetError(context
, AL_INVALID_VALUE
,
232 "Mapping buffer %u for writing without write access", buffer
);
233 else if(UNLIKELY((unavailable
&AL_MAP_PERSISTENT_BIT_SOFT
)))
234 alSetError(context
, AL_INVALID_VALUE
,
235 "Mapping buffer %u persistently without persistent access", buffer
);
236 else if(UNLIKELY(offset
< 0 || offset
>= albuf
->OriginalSize
||
237 length
<= 0 || length
> albuf
->OriginalSize
- offset
))
238 alSetError(context
, AL_INVALID_VALUE
, "Mapping invalid range %d+%d for buffer %u",
239 offset
, length
, buffer
);
242 retval
= (ALbyte
*)albuf
->data
+ offset
;
243 albuf
->MappedAccess
= access
;
244 albuf
->MappedOffset
= offset
;
245 albuf
->MappedSize
= length
;
248 UnlockBufferList(device
);
250 ALCcontext_DecRef(context
);
254 AL_API
void AL_APIENTRY
alUnmapBufferSOFT(ALuint buffer
)
260 context
= GetContextRef();
263 device
= context
->Device
;
264 LockBufferList(device
);
265 if((albuf
=LookupBuffer(device
, buffer
)) == NULL
)
266 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
267 else if(albuf
->MappedAccess
== 0)
268 alSetError(context
, AL_INVALID_OPERATION
, "Unmapping unmapped buffer %u", buffer
);
271 albuf
->MappedAccess
= 0;
272 albuf
->MappedOffset
= 0;
273 albuf
->MappedSize
= 0;
275 UnlockBufferList(device
);
277 ALCcontext_DecRef(context
);
280 AL_API
void AL_APIENTRY
alFlushMappedBufferSOFT(ALuint buffer
, ALsizei offset
, ALsizei length
)
286 context
= GetContextRef();
289 device
= context
->Device
;
290 LockBufferList(device
);
291 if(UNLIKELY((albuf
=LookupBuffer(device
, buffer
)) == NULL
))
292 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
293 else if(UNLIKELY(!(albuf
->MappedAccess
&AL_MAP_WRITE_BIT_SOFT
)))
294 alSetError(context
, AL_INVALID_OPERATION
,
295 "Flushing buffer %u while not mapped for writing", buffer
);
296 else if(UNLIKELY(offset
< albuf
->MappedOffset
||
297 offset
>= albuf
->MappedOffset
+albuf
->MappedSize
||
298 length
<= 0 || length
> albuf
->MappedOffset
+albuf
->MappedSize
-offset
))
299 alSetError(context
, AL_INVALID_VALUE
, "Flushing invalid range %d+%d on buffer %u",
300 offset
, length
, buffer
);
303 /* FIXME: Need to use some method of double-buffering for the mixer and
304 * app to hold separate memory, which can be safely transfered
305 * asynchronously. Currently we just say the app shouldn't write where
306 * OpenAL's reading, and hope for the best...
308 ATOMIC_THREAD_FENCE(almemory_order_seq_cst
);
310 UnlockBufferList(device
);
312 ALCcontext_DecRef(context
);
315 AL_API ALvoid AL_APIENTRY
alBufferSubDataSOFT(ALuint buffer
, ALenum format
, const ALvoid
*data
, ALsizei offset
, ALsizei length
)
317 enum UserFmtChannels srcchannels
= UserFmtMono
;
318 enum UserFmtType srctype
= UserFmtUByte
;
323 context
= GetContextRef();
326 device
= context
->Device
;
327 LockBufferList(device
);
328 if(UNLIKELY((albuf
=LookupBuffer(device
, buffer
)) == NULL
))
329 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
330 else if(UNLIKELY(DecomposeUserFormat(format
, &srcchannels
, &srctype
) == AL_FALSE
))
331 alSetError(context
, AL_INVALID_ENUM
, "Invalid format 0x%04x", format
);
334 ALsizei unpack_align
, align
;
340 unpack_align
= ATOMIC_LOAD_SEQ(&albuf
->UnpackAlign
);
341 align
= SanitizeAlignment(srctype
, unpack_align
);
342 if(UNLIKELY(align
< 1))
343 alSetError(context
, AL_INVALID_VALUE
, "Invalid unpack alignment %d", unpack_align
);
344 else if(UNLIKELY((long)srcchannels
!= (long)albuf
->FmtChannels
||
345 srctype
!= albuf
->OriginalType
))
346 alSetError(context
, AL_INVALID_ENUM
, "Unpacking data with mismatched format");
347 else if(UNLIKELY(align
!= albuf
->OriginalAlign
))
348 alSetError(context
, AL_INVALID_VALUE
,
349 "Unpacking data with alignment %u does not match original alignment %u",
350 align
, albuf
->OriginalAlign
);
351 else if(UNLIKELY(albuf
->MappedAccess
!= 0))
352 alSetError(context
, AL_INVALID_OPERATION
, "Unpacking data into mapped buffer %u",
356 num_chans
= ChannelsFromFmt(albuf
->FmtChannels
);
357 frame_size
= num_chans
* BytesFromFmt(albuf
->FmtType
);
358 if(albuf
->OriginalType
== UserFmtIMA4
)
359 byte_align
= ((align
-1)/2 + 4) * num_chans
;
360 else if(albuf
->OriginalType
== UserFmtMSADPCM
)
361 byte_align
= ((align
-2)/2 + 7) * num_chans
;
363 byte_align
= align
* frame_size
;
365 if(UNLIKELY(offset
< 0 || length
< 0 || offset
> albuf
->OriginalSize
||
366 length
> albuf
->OriginalSize
-offset
))
367 alSetError(context
, AL_INVALID_VALUE
, "Invalid data sub-range %d+%d on buffer %u",
368 offset
, length
, buffer
);
369 else if(UNLIKELY((offset
%byte_align
) != 0))
370 alSetError(context
, AL_INVALID_VALUE
,
371 "Sub-range offset %d is not a multiple of frame size %d (%d unpack alignment)",
372 offset
, byte_align
, align
);
373 else if(UNLIKELY((length
%byte_align
) != 0))
374 alSetError(context
, AL_INVALID_VALUE
,
375 "Sub-range length %d is not a multiple of frame size %d (%d unpack alignment)",
376 length
, byte_align
, align
);
379 /* offset -> byte offset, length -> sample count */
380 offset
= offset
/byte_align
* align
* frame_size
;
381 length
= length
/byte_align
* align
;
383 dst
= (ALbyte
*)albuf
->data
+ offset
;
384 if(srctype
== UserFmtIMA4
&& albuf
->FmtType
== FmtShort
)
385 Convert_ALshort_ALima4(dst
, data
, num_chans
, length
, align
);
386 else if(srctype
== UserFmtMSADPCM
&& albuf
->FmtType
== FmtShort
)
387 Convert_ALshort_ALmsadpcm(dst
, data
, num_chans
, length
, align
);
390 assert((long)srctype
== (long)albuf
->FmtType
);
391 memcpy(dst
, data
, length
*frame_size
);
396 UnlockBufferList(device
);
398 ALCcontext_DecRef(context
);
402 AL_API
void AL_APIENTRY
alBufferSamplesSOFT(ALuint
UNUSED(buffer
),
403 ALuint
UNUSED(samplerate
), ALenum
UNUSED(internalformat
), ALsizei
UNUSED(samples
),
404 ALenum
UNUSED(channels
), ALenum
UNUSED(type
), const ALvoid
*UNUSED(data
))
408 context
= GetContextRef();
411 alSetError(context
, AL_INVALID_OPERATION
, "alBufferSamplesSOFT not supported");
413 ALCcontext_DecRef(context
);
416 AL_API
void AL_APIENTRY
alBufferSubSamplesSOFT(ALuint
UNUSED(buffer
),
417 ALsizei
UNUSED(offset
), ALsizei
UNUSED(samples
),
418 ALenum
UNUSED(channels
), ALenum
UNUSED(type
), const ALvoid
*UNUSED(data
))
422 context
= GetContextRef();
425 alSetError(context
, AL_INVALID_OPERATION
, "alBufferSubSamplesSOFT not supported");
427 ALCcontext_DecRef(context
);
430 AL_API
void AL_APIENTRY
alGetBufferSamplesSOFT(ALuint
UNUSED(buffer
),
431 ALsizei
UNUSED(offset
), ALsizei
UNUSED(samples
),
432 ALenum
UNUSED(channels
), ALenum
UNUSED(type
), ALvoid
*UNUSED(data
))
436 context
= GetContextRef();
439 alSetError(context
, AL_INVALID_OPERATION
, "alGetBufferSamplesSOFT not supported");
441 ALCcontext_DecRef(context
);
444 AL_API ALboolean AL_APIENTRY
alIsBufferFormatSupportedSOFT(ALenum
UNUSED(format
))
448 context
= GetContextRef();
449 if(!context
) return AL_FALSE
;
451 alSetError(context
, AL_INVALID_OPERATION
, "alIsBufferFormatSupportedSOFT not supported");
453 ALCcontext_DecRef(context
);
458 AL_API
void AL_APIENTRY
alBufferf(ALuint buffer
, ALenum param
, ALfloat
UNUSED(value
))
463 context
= GetContextRef();
466 device
= context
->Device
;
467 LockBufferList(device
);
468 if(UNLIKELY(LookupBuffer(device
, buffer
) == NULL
))
469 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
473 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer float property 0x%04x", param
);
475 UnlockBufferList(device
);
477 ALCcontext_DecRef(context
);
481 AL_API
void AL_APIENTRY
alBuffer3f(ALuint buffer
, ALenum param
, ALfloat
UNUSED(value1
), ALfloat
UNUSED(value2
), ALfloat
UNUSED(value3
))
486 context
= GetContextRef();
489 device
= context
->Device
;
490 LockBufferList(device
);
491 if(UNLIKELY(LookupBuffer(device
, buffer
) == NULL
))
492 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
496 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer 3-float property 0x%04x", param
);
498 UnlockBufferList(device
);
500 ALCcontext_DecRef(context
);
504 AL_API
void AL_APIENTRY
alBufferfv(ALuint buffer
, ALenum param
, const ALfloat
*values
)
509 context
= GetContextRef();
512 device
= context
->Device
;
513 LockBufferList(device
);
514 if(UNLIKELY(LookupBuffer(device
, buffer
) == NULL
))
515 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
516 else if(UNLIKELY(!values
))
517 alSetError(context
, AL_INVALID_VALUE
, "NULL pointer");
521 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer float-vector property 0x%04x", param
);
523 UnlockBufferList(device
);
525 ALCcontext_DecRef(context
);
529 AL_API
void AL_APIENTRY
alBufferi(ALuint buffer
, ALenum param
, ALint value
)
535 context
= GetContextRef();
538 device
= context
->Device
;
539 LockBufferList(device
);
540 if(UNLIKELY((albuf
=LookupBuffer(device
, buffer
)) == NULL
))
541 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
544 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT
:
545 if(UNLIKELY(value
< 0))
546 alSetError(context
, AL_INVALID_VALUE
, "Invalid unpack block alignment %d", value
);
548 ATOMIC_STORE_SEQ(&albuf
->UnpackAlign
, value
);
551 case AL_PACK_BLOCK_ALIGNMENT_SOFT
:
552 if(UNLIKELY(value
< 0))
553 alSetError(context
, AL_INVALID_VALUE
, "Invalid pack block alignment %d", value
);
555 ATOMIC_STORE_SEQ(&albuf
->PackAlign
, value
);
559 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer integer property 0x%04x", param
);
561 UnlockBufferList(device
);
563 ALCcontext_DecRef(context
);
567 AL_API
void AL_APIENTRY
alBuffer3i(ALuint buffer
, ALenum param
, ALint
UNUSED(value1
), ALint
UNUSED(value2
), ALint
UNUSED(value3
))
572 context
= GetContextRef();
575 device
= context
->Device
;
576 LockBufferList(device
);
577 if(UNLIKELY(LookupBuffer(device
, buffer
) == NULL
))
578 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
582 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer 3-integer property 0x%04x", param
);
584 UnlockBufferList(device
);
586 ALCcontext_DecRef(context
);
590 AL_API
void AL_APIENTRY
alBufferiv(ALuint buffer
, ALenum param
, const ALint
*values
)
600 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT
:
601 case AL_PACK_BLOCK_ALIGNMENT_SOFT
:
602 alBufferi(buffer
, param
, values
[0]);
607 context
= GetContextRef();
610 device
= context
->Device
;
611 LockBufferList(device
);
612 if(UNLIKELY((albuf
=LookupBuffer(device
, buffer
)) == NULL
))
613 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
614 else if(UNLIKELY(!values
))
615 alSetError(context
, AL_INVALID_VALUE
, "NULL pointer");
618 case AL_LOOP_POINTS_SOFT
:
619 if(UNLIKELY(ReadRef(&albuf
->ref
) != 0))
620 alSetError(context
, AL_INVALID_OPERATION
, "Modifying in-use buffer %u's loop points",
622 else if(UNLIKELY(values
[0] >= values
[1] || values
[0] < 0 || values
[1] > albuf
->SampleLen
))
623 alSetError(context
, AL_INVALID_VALUE
, "Invalid loop point range %d -> %d o buffer %u",
624 values
[0], values
[1], buffer
);
627 albuf
->LoopStart
= values
[0];
628 albuf
->LoopEnd
= values
[1];
633 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer integer-vector property 0x%04x",
636 UnlockBufferList(device
);
638 ALCcontext_DecRef(context
);
642 AL_API ALvoid AL_APIENTRY
alGetBufferf(ALuint buffer
, ALenum param
, ALfloat
*value
)
648 context
= GetContextRef();
651 device
= context
->Device
;
652 LockBufferList(device
);
653 if(UNLIKELY((albuf
=LookupBuffer(device
, buffer
)) == NULL
))
654 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
655 else if(UNLIKELY(!value
))
656 alSetError(context
, AL_INVALID_VALUE
, "NULL pointer");
660 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer float property 0x%04x", param
);
662 UnlockBufferList(device
);
664 ALCcontext_DecRef(context
);
668 AL_API
void AL_APIENTRY
alGetBuffer3f(ALuint buffer
, ALenum param
, ALfloat
*value1
, ALfloat
*value2
, ALfloat
*value3
)
673 context
= GetContextRef();
676 device
= context
->Device
;
677 LockBufferList(device
);
678 if(UNLIKELY(LookupBuffer(device
, buffer
) == NULL
))
679 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
680 else if(UNLIKELY(!value1
|| !value2
|| !value3
))
681 alSetError(context
, AL_INVALID_VALUE
, "NULL pointer");
685 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer 3-float property 0x%04x", param
);
687 UnlockBufferList(device
);
689 ALCcontext_DecRef(context
);
693 AL_API
void AL_APIENTRY
alGetBufferfv(ALuint buffer
, ALenum param
, ALfloat
*values
)
700 case AL_SEC_LENGTH_SOFT
:
701 alGetBufferf(buffer
, param
, values
);
705 context
= GetContextRef();
708 device
= context
->Device
;
709 LockBufferList(device
);
710 if(UNLIKELY(LookupBuffer(device
, buffer
) == NULL
))
711 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
712 else if(UNLIKELY(!values
))
713 alSetError(context
, AL_INVALID_VALUE
, "NULL pointer");
717 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer float-vector property 0x%04x", param
);
719 UnlockBufferList(device
);
721 ALCcontext_DecRef(context
);
725 AL_API ALvoid AL_APIENTRY
alGetBufferi(ALuint buffer
, ALenum param
, ALint
*value
)
731 context
= GetContextRef();
734 device
= context
->Device
;
735 LockBufferList(device
);
736 if(UNLIKELY((albuf
=LookupBuffer(device
, buffer
)) == NULL
))
737 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
738 else if(UNLIKELY(!value
))
739 alSetError(context
, AL_INVALID_VALUE
, "NULL pointer");
743 *value
= albuf
->Frequency
;
747 *value
= BytesFromFmt(albuf
->FmtType
) * 8;
751 *value
= ChannelsFromFmt(albuf
->FmtChannels
);
755 *value
= albuf
->SampleLen
* FrameSizeFromFmt(albuf
->FmtChannels
,
759 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT
:
760 *value
= ATOMIC_LOAD_SEQ(&albuf
->UnpackAlign
);
763 case AL_PACK_BLOCK_ALIGNMENT_SOFT
:
764 *value
= ATOMIC_LOAD_SEQ(&albuf
->PackAlign
);
768 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer integer property 0x%04x", param
);
770 UnlockBufferList(device
);
772 ALCcontext_DecRef(context
);
776 AL_API
void AL_APIENTRY
alGetBuffer3i(ALuint buffer
, ALenum param
, ALint
*value1
, ALint
*value2
, ALint
*value3
)
781 context
= GetContextRef();
784 device
= context
->Device
;
785 LockBufferList(device
);
786 if(UNLIKELY(LookupBuffer(device
, buffer
) == NULL
))
787 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
788 else if(UNLIKELY(!value1
|| !value2
|| !value3
))
789 alSetError(context
, AL_INVALID_VALUE
, "NULL pointer");
793 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer 3-integer property 0x%04x", param
);
795 UnlockBufferList(device
);
797 ALCcontext_DecRef(context
);
801 AL_API
void AL_APIENTRY
alGetBufferiv(ALuint buffer
, ALenum param
, ALint
*values
)
813 case AL_INTERNAL_FORMAT_SOFT
:
814 case AL_BYTE_LENGTH_SOFT
:
815 case AL_SAMPLE_LENGTH_SOFT
:
816 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT
:
817 case AL_PACK_BLOCK_ALIGNMENT_SOFT
:
818 alGetBufferi(buffer
, param
, values
);
822 context
= GetContextRef();
825 device
= context
->Device
;
826 LockBufferList(device
);
827 if(UNLIKELY((albuf
=LookupBuffer(device
, buffer
)) == NULL
))
828 alSetError(context
, AL_INVALID_NAME
, "Invalid buffer ID %u", buffer
);
829 else if(UNLIKELY(!values
))
830 alSetError(context
, AL_INVALID_VALUE
, "NULL pointer");
833 case AL_LOOP_POINTS_SOFT
:
834 values
[0] = albuf
->LoopStart
;
835 values
[1] = albuf
->LoopEnd
;
839 alSetError(context
, AL_INVALID_ENUM
, "Invalid buffer integer-vector property 0x%04x",
842 UnlockBufferList(device
);
844 ALCcontext_DecRef(context
);
848 static const ALchar
*NameFromUserFmtType(enum UserFmtType type
)
852 case UserFmtUByte
: return "Unsigned Byte";
853 case UserFmtShort
: return "Signed Short";
854 case UserFmtFloat
: return "Float32";
855 case UserFmtDouble
: return "Float64";
856 case UserFmtMulaw
: return "muLaw";
857 case UserFmtAlaw
: return "aLaw";
858 case UserFmtIMA4
: return "IMA4 ADPCM";
859 case UserFmtMSADPCM
: return "MSADPCM";
861 return "<internal type error>";
867 * Loads the specified data into the buffer, using the specified format.
869 static void LoadData(ALCcontext
*context
, ALbuffer
*ALBuf
, ALuint freq
, ALsizei size
, enum UserFmtChannels SrcChannels
, enum UserFmtType SrcType
, const ALvoid
*data
, ALbitfieldSOFT access
)
871 enum FmtChannels DstChannels
= FmtMono
;
872 enum FmtType DstType
= FmtUByte
;
873 ALsizei NumChannels
, FrameSize
;
874 ALsizei SrcByteAlign
;
880 if(UNLIKELY(ReadRef(&ALBuf
->ref
) != 0 || ALBuf
->MappedAccess
!= 0))
881 SETERR_RETURN(context
, AL_INVALID_OPERATION
,, "Modifying storage for in-use buffer %u",
884 /* Currently no channel configurations need to be converted. */
887 case UserFmtMono
: DstChannels
= FmtMono
; break;
888 case UserFmtStereo
: DstChannels
= FmtStereo
; break;
889 case UserFmtRear
: DstChannels
= FmtRear
; break;
890 case UserFmtQuad
: DstChannels
= FmtQuad
; break;
891 case UserFmtX51
: DstChannels
= FmtX51
; break;
892 case UserFmtX61
: DstChannels
= FmtX61
; break;
893 case UserFmtX71
: DstChannels
= FmtX71
; break;
894 case UserFmtBFormat2D
: DstChannels
= FmtBFormat2D
; break;
895 case UserFmtBFormat3D
: DstChannels
= FmtBFormat3D
; break;
897 if(UNLIKELY((long)SrcChannels
!= (long)DstChannels
))
898 SETERR_RETURN(context
, AL_INVALID_ENUM
,, "Invalid format");
900 /* IMA4 and MSADPCM convert to 16-bit short. */
903 case UserFmtUByte
: DstType
= FmtUByte
; break;
904 case UserFmtShort
: DstType
= FmtShort
; break;
905 case UserFmtFloat
: DstType
= FmtFloat
; break;
906 case UserFmtDouble
: DstType
= FmtDouble
; break;
907 case UserFmtAlaw
: DstType
= FmtAlaw
; break;
908 case UserFmtMulaw
: DstType
= FmtMulaw
; break;
909 case UserFmtIMA4
: DstType
= FmtShort
; break;
910 case UserFmtMSADPCM
: DstType
= FmtShort
; break;
913 /* TODO: Currently we can only map samples when they're not converted. To
914 * allow it would need some kind of double-buffering to hold onto a copy of
917 if((access
&MAP_READ_WRITE_FLAGS
))
919 if(UNLIKELY((long)SrcType
!= (long)DstType
))
920 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "%s samples cannot be mapped",
921 NameFromUserFmtType(SrcType
));
924 unpackalign
= ATOMIC_LOAD_SEQ(&ALBuf
->UnpackAlign
);
925 if(UNLIKELY((align
=SanitizeAlignment(SrcType
, unpackalign
)) < 1))
926 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Invalid unpack alignment %d for %s samples",
927 unpackalign
, NameFromUserFmtType(SrcType
));
929 if((access
&AL_PRESERVE_DATA_BIT_SOFT
))
931 /* Can only preserve data with the same format and alignment. */
932 if(UNLIKELY(ALBuf
->FmtChannels
!= DstChannels
|| ALBuf
->OriginalType
!= SrcType
))
933 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Preserving data of mismatched format");
934 if(UNLIKELY(ALBuf
->OriginalAlign
!= align
))
935 SETERR_RETURN(context
, AL_INVALID_VALUE
,, "Preserving data of mismatched alignment");
938 /* Convert the input/source size in bytes to sample frames using the unpack
941 if(SrcType
== UserFmtIMA4
)
942 SrcByteAlign
= ((align
-1)/2 + 4) * ChannelsFromUserFmt(SrcChannels
);
943 else if(SrcType
== UserFmtMSADPCM
)
944 SrcByteAlign
= ((align
-2)/2 + 7) * ChannelsFromUserFmt(SrcChannels
);
946 SrcByteAlign
= align
* FrameSizeFromUserFmt(SrcChannels
, SrcType
);
947 if(UNLIKELY((size
%SrcByteAlign
) != 0))
948 SETERR_RETURN(context
, AL_INVALID_VALUE
,,
949 "Data size %d is not a multiple of frame size %d (%d unpack alignment)",
950 size
, SrcByteAlign
, align
);
952 if(UNLIKELY(size
/ SrcByteAlign
> INT_MAX
/ align
))
953 SETERR_RETURN(context
, AL_OUT_OF_MEMORY
,,
954 "Buffer size overflow, %d blocks x %d samples per block", size
/SrcByteAlign
, align
);
955 frames
= size
/ SrcByteAlign
* align
;
957 /* Convert the sample frames to the number of bytes needed for internal
960 NumChannels
= ChannelsFromFmt(DstChannels
);
961 FrameSize
= NumChannels
* BytesFromFmt(DstType
);
962 if(UNLIKELY(frames
> INT_MAX
/FrameSize
))
963 SETERR_RETURN(context
, AL_OUT_OF_MEMORY
,,
964 "Buffer size overflow, %d frames x %d bytes per frame", frames
, FrameSize
);
965 newsize
= frames
*FrameSize
;
967 /* Round up to the next 16-byte multiple. This could reallocate only when
968 * increasing or the new size is less than half the current, but then the
969 * buffer's AL_SIZE would not be very reliable for accounting buffer memory
970 * usage, and reporting the real size could cause problems for apps that
971 * use AL_SIZE to try to get the buffer's play length.
973 if(LIKELY(newsize
<= INT_MAX
-15))
974 newsize
= (newsize
+15) & ~0xf;
975 if(newsize
!= ALBuf
->BytesAlloc
)
977 void *temp
= al_malloc(16, (size_t)newsize
);
978 if(UNLIKELY(!temp
&& newsize
))
979 SETERR_RETURN(context
, AL_OUT_OF_MEMORY
,, "Failed to allocate %d bytes of storage",
981 if((access
&AL_PRESERVE_DATA_BIT_SOFT
))
983 ALsizei tocopy
= mini(newsize
, ALBuf
->BytesAlloc
);
984 if(tocopy
> 0) memcpy(temp
, ALBuf
->data
, tocopy
);
986 al_free(ALBuf
->data
);
988 ALBuf
->BytesAlloc
= newsize
;
991 if(SrcType
== UserFmtIMA4
)
993 assert(DstType
== FmtShort
);
994 if(data
!= NULL
&& ALBuf
->data
!= NULL
)
995 Convert_ALshort_ALima4(ALBuf
->data
, data
, NumChannels
, frames
, align
);
996 ALBuf
->OriginalAlign
= align
;
998 else if(SrcType
== UserFmtMSADPCM
)
1000 assert(DstType
== FmtShort
);
1001 if(data
!= NULL
&& ALBuf
->data
!= NULL
)
1002 Convert_ALshort_ALmsadpcm(ALBuf
->data
, data
, NumChannels
, frames
, align
);
1003 ALBuf
->OriginalAlign
= align
;
1007 assert((long)SrcType
== (long)DstType
);
1008 if(data
!= NULL
&& ALBuf
->data
!= NULL
)
1009 memcpy(ALBuf
->data
, data
, frames
*FrameSize
);
1010 ALBuf
->OriginalAlign
= 1;
1012 ALBuf
->OriginalSize
= size
;
1013 ALBuf
->OriginalType
= SrcType
;
1015 ALBuf
->Frequency
= freq
;
1016 ALBuf
->FmtChannels
= DstChannels
;
1017 ALBuf
->FmtType
= DstType
;
1018 ALBuf
->Access
= access
;
1020 ALBuf
->SampleLen
= frames
;
1021 ALBuf
->LoopStart
= 0;
1022 ALBuf
->LoopEnd
= ALBuf
->SampleLen
;
1026 ALsizei
BytesFromUserFmt(enum UserFmtType type
)
1030 case UserFmtUByte
: return sizeof(ALubyte
);
1031 case UserFmtShort
: return sizeof(ALshort
);
1032 case UserFmtFloat
: return sizeof(ALfloat
);
1033 case UserFmtDouble
: return sizeof(ALdouble
);
1034 case UserFmtMulaw
: return sizeof(ALubyte
);
1035 case UserFmtAlaw
: return sizeof(ALubyte
);
1036 case UserFmtIMA4
: break; /* not handled here */
1037 case UserFmtMSADPCM
: break; /* not handled here */
1041 ALsizei
ChannelsFromUserFmt(enum UserFmtChannels chans
)
1045 case UserFmtMono
: return 1;
1046 case UserFmtStereo
: return 2;
1047 case UserFmtRear
: return 2;
1048 case UserFmtQuad
: return 4;
1049 case UserFmtX51
: return 6;
1050 case UserFmtX61
: return 7;
1051 case UserFmtX71
: return 8;
1052 case UserFmtBFormat2D
: return 3;
1053 case UserFmtBFormat3D
: return 4;
1057 static ALboolean
DecomposeUserFormat(ALenum format
, enum UserFmtChannels
*chans
,
1058 enum UserFmtType
*type
)
1060 static const struct {
1062 enum UserFmtChannels channels
;
1063 enum UserFmtType type
;
1065 { AL_FORMAT_MONO8
, UserFmtMono
, UserFmtUByte
},
1066 { AL_FORMAT_MONO16
, UserFmtMono
, UserFmtShort
},
1067 { AL_FORMAT_MONO_FLOAT32
, UserFmtMono
, UserFmtFloat
},
1068 { AL_FORMAT_MONO_DOUBLE_EXT
, UserFmtMono
, UserFmtDouble
},
1069 { AL_FORMAT_MONO_IMA4
, UserFmtMono
, UserFmtIMA4
},
1070 { AL_FORMAT_MONO_MSADPCM_SOFT
, UserFmtMono
, UserFmtMSADPCM
},
1071 { AL_FORMAT_MONO_MULAW
, UserFmtMono
, UserFmtMulaw
},
1072 { AL_FORMAT_MONO_ALAW_EXT
, UserFmtMono
, UserFmtAlaw
},
1074 { AL_FORMAT_STEREO8
, UserFmtStereo
, UserFmtUByte
},
1075 { AL_FORMAT_STEREO16
, UserFmtStereo
, UserFmtShort
},
1076 { AL_FORMAT_STEREO_FLOAT32
, UserFmtStereo
, UserFmtFloat
},
1077 { AL_FORMAT_STEREO_DOUBLE_EXT
, UserFmtStereo
, UserFmtDouble
},
1078 { AL_FORMAT_STEREO_IMA4
, UserFmtStereo
, UserFmtIMA4
},
1079 { AL_FORMAT_STEREO_MSADPCM_SOFT
, UserFmtStereo
, UserFmtMSADPCM
},
1080 { AL_FORMAT_STEREO_MULAW
, UserFmtStereo
, UserFmtMulaw
},
1081 { AL_FORMAT_STEREO_ALAW_EXT
, UserFmtStereo
, UserFmtAlaw
},
1083 { AL_FORMAT_REAR8
, UserFmtRear
, UserFmtUByte
},
1084 { AL_FORMAT_REAR16
, UserFmtRear
, UserFmtShort
},
1085 { AL_FORMAT_REAR32
, UserFmtRear
, UserFmtFloat
},
1086 { AL_FORMAT_REAR_MULAW
, UserFmtRear
, UserFmtMulaw
},
1088 { AL_FORMAT_QUAD8_LOKI
, UserFmtQuad
, UserFmtUByte
},
1089 { AL_FORMAT_QUAD16_LOKI
, UserFmtQuad
, UserFmtShort
},
1091 { AL_FORMAT_QUAD8
, UserFmtQuad
, UserFmtUByte
},
1092 { AL_FORMAT_QUAD16
, UserFmtQuad
, UserFmtShort
},
1093 { AL_FORMAT_QUAD32
, UserFmtQuad
, UserFmtFloat
},
1094 { AL_FORMAT_QUAD_MULAW
, UserFmtQuad
, UserFmtMulaw
},
1096 { AL_FORMAT_51CHN8
, UserFmtX51
, UserFmtUByte
},
1097 { AL_FORMAT_51CHN16
, UserFmtX51
, UserFmtShort
},
1098 { AL_FORMAT_51CHN32
, UserFmtX51
, UserFmtFloat
},
1099 { AL_FORMAT_51CHN_MULAW
, UserFmtX51
, UserFmtMulaw
},
1101 { AL_FORMAT_61CHN8
, UserFmtX61
, UserFmtUByte
},
1102 { AL_FORMAT_61CHN16
, UserFmtX61
, UserFmtShort
},
1103 { AL_FORMAT_61CHN32
, UserFmtX61
, UserFmtFloat
},
1104 { AL_FORMAT_61CHN_MULAW
, UserFmtX61
, UserFmtMulaw
},
1106 { AL_FORMAT_71CHN8
, UserFmtX71
, UserFmtUByte
},
1107 { AL_FORMAT_71CHN16
, UserFmtX71
, UserFmtShort
},
1108 { AL_FORMAT_71CHN32
, UserFmtX71
, UserFmtFloat
},
1109 { AL_FORMAT_71CHN_MULAW
, UserFmtX71
, UserFmtMulaw
},
1111 { AL_FORMAT_BFORMAT2D_8
, UserFmtBFormat2D
, UserFmtUByte
},
1112 { AL_FORMAT_BFORMAT2D_16
, UserFmtBFormat2D
, UserFmtShort
},
1113 { AL_FORMAT_BFORMAT2D_FLOAT32
, UserFmtBFormat2D
, UserFmtFloat
},
1114 { AL_FORMAT_BFORMAT2D_MULAW
, UserFmtBFormat2D
, UserFmtMulaw
},
1116 { AL_FORMAT_BFORMAT3D_8
, UserFmtBFormat3D
, UserFmtUByte
},
1117 { AL_FORMAT_BFORMAT3D_16
, UserFmtBFormat3D
, UserFmtShort
},
1118 { AL_FORMAT_BFORMAT3D_FLOAT32
, UserFmtBFormat3D
, UserFmtFloat
},
1119 { AL_FORMAT_BFORMAT3D_MULAW
, UserFmtBFormat3D
, UserFmtMulaw
},
1123 for(i
= 0;i
< COUNTOF(list
);i
++)
1125 if(list
[i
].format
== format
)
1127 *chans
= list
[i
].channels
;
1128 *type
= list
[i
].type
;
1136 ALsizei
BytesFromFmt(enum FmtType type
)
1140 case FmtUByte
: return sizeof(ALubyte
);
1141 case FmtShort
: return sizeof(ALshort
);
1142 case FmtFloat
: return sizeof(ALfloat
);
1143 case FmtDouble
: return sizeof(ALdouble
);
1144 case FmtMulaw
: return sizeof(ALubyte
);
1145 case FmtAlaw
: return sizeof(ALubyte
);
1149 ALsizei
ChannelsFromFmt(enum FmtChannels chans
)
1153 case FmtMono
: return 1;
1154 case FmtStereo
: return 2;
1155 case FmtRear
: return 2;
1156 case FmtQuad
: return 4;
1157 case FmtX51
: return 6;
1158 case FmtX61
: return 7;
1159 case FmtX71
: return 8;
1160 case FmtBFormat2D
: return 3;
1161 case FmtBFormat3D
: return 4;
1166 static ALsizei
SanitizeAlignment(enum UserFmtType type
, ALsizei align
)
1173 if(type
== UserFmtIMA4
)
1175 /* Here is where things vary:
1176 * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel
1177 * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel
1181 if(type
== UserFmtMSADPCM
)
1186 if(type
== UserFmtIMA4
)
1188 /* IMA4 block alignment must be a multiple of 8, plus 1. */
1189 if((align
&7) == 1) return align
;
1192 if(type
== UserFmtMSADPCM
)
1194 /* MSADPCM block alignment must be a multiple of 2. */
1195 if((align
&1) == 0) return align
;
1203 static ALbuffer
*AllocBuffer(ALCcontext
*context
)
1205 ALCdevice
*device
= context
->Device
;
1206 BufferSubList
*sublist
, *subend
;
1207 ALbuffer
*buffer
= NULL
;
1211 almtx_lock(&device
->BufferLock
);
1212 sublist
= VECTOR_BEGIN(device
->BufferList
);
1213 subend
= VECTOR_END(device
->BufferList
);
1214 for(;sublist
!= subend
;++sublist
)
1216 if(sublist
->FreeMask
)
1218 slidx
= CTZ64(sublist
->FreeMask
);
1219 buffer
= sublist
->Buffers
+ slidx
;
1224 if(UNLIKELY(!buffer
))
1226 const BufferSubList empty_sublist
= { 0, NULL
};
1227 /* Don't allocate so many list entries that the 32-bit ID could
1230 if(UNLIKELY(VECTOR_SIZE(device
->BufferList
) >= 1<<25))
1232 almtx_unlock(&device
->BufferLock
);
1233 alSetError(context
, AL_OUT_OF_MEMORY
, "Too many buffers allocated");
1236 lidx
= (ALsizei
)VECTOR_SIZE(device
->BufferList
);
1237 VECTOR_PUSH_BACK(device
->BufferList
, empty_sublist
);
1238 sublist
= &VECTOR_BACK(device
->BufferList
);
1239 sublist
->FreeMask
= ~U64(0);
1240 sublist
->Buffers
= al_calloc(16, sizeof(ALbuffer
)*64);
1241 if(UNLIKELY(!sublist
->Buffers
))
1243 VECTOR_POP_BACK(device
->BufferList
);
1244 almtx_unlock(&device
->BufferLock
);
1245 alSetError(context
, AL_OUT_OF_MEMORY
, "Failed to allocate buffer batch");
1250 buffer
= sublist
->Buffers
+ slidx
;
1253 memset(buffer
, 0, sizeof(*buffer
));
1255 /* Add 1 to avoid buffer ID 0. */
1256 buffer
->id
= ((lidx
<<6) | slidx
) + 1;
1258 sublist
->FreeMask
&= ~(U64(1)<<slidx
);
1259 almtx_unlock(&device
->BufferLock
);
1264 static void FreeBuffer(ALCdevice
*device
, ALbuffer
*buffer
)
1266 ALuint id
= buffer
->id
- 1;
1267 ALsizei lidx
= id
>> 6;
1268 ALsizei slidx
= id
& 0x3f;
1270 al_free(buffer
->data
);
1271 memset(buffer
, 0, sizeof(*buffer
));
1273 VECTOR_ELEM(device
->BufferList
, lidx
).FreeMask
|= U64(1) << slidx
;
1278 * ReleaseALBuffers()
1280 * INTERNAL: Called to destroy any buffers that still exist on the device
1282 ALvoid
ReleaseALBuffers(ALCdevice
*device
)
1284 BufferSubList
*sublist
= VECTOR_BEGIN(device
->BufferList
);
1285 BufferSubList
*subend
= VECTOR_END(device
->BufferList
);
1286 size_t leftover
= 0;
1287 for(;sublist
!= subend
;++sublist
)
1289 ALuint64 usemask
= ~sublist
->FreeMask
;
1292 ALsizei idx
= CTZ64(usemask
);
1293 ALbuffer
*buffer
= sublist
->Buffers
+ idx
;
1295 al_free(buffer
->data
);
1296 memset(buffer
, 0, sizeof(*buffer
));
1299 usemask
&= ~(U64(1) << idx
);
1301 sublist
->FreeMask
= ~usemask
;
1304 WARN("(%p) Deleted "SZFMT
" Buffer%s\n", device
, leftover
, (leftover
==1)?"":"s");