Improve error reporting for buffers
[openal-soft.git] / OpenAL32 / alBuffer.c
blob474773b7555d8ef1588535d500ae9ce3effd0753
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 #include <limits.h>
27 #ifdef HAVE_MALLOC_H
28 #include <malloc.h>
29 #endif
31 #include "alMain.h"
32 #include "alu.h"
33 #include "alError.h"
34 #include "alBuffer.h"
35 #include "alThunk.h"
36 #include "sample_cvt.h"
39 extern inline void LockBuffersRead(ALCdevice *device);
40 extern inline void UnlockBuffersRead(ALCdevice *device);
41 extern inline void LockBuffersWrite(ALCdevice *device);
42 extern inline void UnlockBuffersWrite(ALCdevice *device);
43 extern inline struct ALbuffer *LookupBuffer(ALCdevice *device, ALuint id);
44 extern inline struct ALbuffer *RemoveBuffer(ALCdevice *device, ALuint id);
45 extern inline ALsizei FrameSizeFromUserFmt(enum UserFmtChannels chans, enum UserFmtType type);
46 extern inline ALsizei FrameSizeFromFmt(enum FmtChannels chans, enum FmtType type);
48 static void LoadData(ALCcontext *context, ALbuffer *buffer, ALuint freq, ALsizei frames,
49 enum UserFmtChannels SrcChannels, enum UserFmtType SrcType,
50 const ALvoid *data, ALsizei align, ALbitfieldSOFT access);
51 static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans, enum UserFmtType *type);
52 static ALsizei SanitizeAlignment(enum UserFmtType type, ALsizei align);
55 #define INVALID_STORAGE_MASK ~(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT | AL_PRESERVE_DATA_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT)
56 #define MAP_READ_WRITE_FLAGS (AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT)
57 #define INVALID_MAP_FLAGS ~(AL_MAP_READ_BIT_SOFT | AL_MAP_WRITE_BIT_SOFT | AL_MAP_PERSISTENT_BIT_SOFT)
60 AL_API ALvoid AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers)
62 ALCcontext *context;
63 ALsizei cur = 0;
65 context = GetContextRef();
66 if(!context) return;
68 if(!(n >= 0))
69 alSetError(context, AL_INVALID_VALUE, "Generating %d buffers", n);
70 else for(cur = 0;cur < n;cur++)
72 ALbuffer *buffer = NewBuffer(context);
73 if(!buffer)
75 alDeleteBuffers(cur, buffers);
76 break;
79 buffers[cur] = buffer->id;
82 ALCcontext_DecRef(context);
85 AL_API ALvoid AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers)
87 ALCdevice *device;
88 ALCcontext *context;
89 ALbuffer *ALBuf;
90 ALsizei i;
92 context = GetContextRef();
93 if(!context) return;
95 device = context->Device;
97 LockBuffersWrite(device);
98 if(UNLIKELY(n < 0))
100 alSetError(context, AL_INVALID_VALUE, "Deleting %d buffers", n);
101 goto done;
104 for(i = 0;i < n;i++)
106 if(!buffers[i])
107 continue;
109 /* Check for valid Buffer ID, and make sure it's not in use. */
110 if((ALBuf=LookupBuffer(device, buffers[i])) == NULL)
112 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffers[i]);
113 goto done;
115 if(ReadRef(&ALBuf->ref) != 0)
117 alSetError(context, AL_INVALID_OPERATION, "Deleting in-use buffer %u", buffers[i]);
118 goto done;
121 for(i = 0;i < n;i++)
123 if((ALBuf=LookupBuffer(device, buffers[i])) != NULL)
124 DeleteBuffer(device, ALBuf);
127 done:
128 UnlockBuffersWrite(device);
129 ALCcontext_DecRef(context);
132 AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer)
134 ALCcontext *context;
135 ALboolean ret;
137 context = GetContextRef();
138 if(!context) return AL_FALSE;
140 LockBuffersRead(context->Device);
141 ret = ((!buffer || LookupBuffer(context->Device, buffer)) ?
142 AL_TRUE : AL_FALSE);
143 UnlockBuffersRead(context->Device);
145 ALCcontext_DecRef(context);
147 return ret;
151 AL_API ALvoid AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq)
152 { alBufferStorageSOFT(buffer, format, data, size, freq, 0); }
154 AL_API void AL_APIENTRY alBufferStorageSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei freq, ALbitfieldSOFT flags)
156 enum UserFmtChannels srcchannels = UserFmtMono;
157 enum UserFmtType srctype = UserFmtUByte;
158 ALCdevice *device;
159 ALCcontext *context;
160 ALbuffer *albuf;
161 ALsizei framesize = 1;
162 ALsizei align;
164 context = GetContextRef();
165 if(!context) return;
167 device = context->Device;
168 LockBuffersRead(device);
169 if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL))
170 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
171 else if(UNLIKELY(size < 0))
172 alSetError(context, AL_INVALID_VALUE, "Negative storage size %d", size);
173 else if(UNLIKELY(freq < 1))
174 alSetError(context, AL_INVALID_VALUE, "Invalid sample rate %d", freq);
175 else if(UNLIKELY((flags&INVALID_STORAGE_MASK) != 0))
176 alSetError(context, AL_INVALID_VALUE, "Invalid storage flags 0x%x",
177 flags&INVALID_STORAGE_MASK);
178 else if(UNLIKELY((flags&AL_MAP_PERSISTENT_BIT_SOFT) && !(flags&MAP_READ_WRITE_FLAGS)))
179 alSetError(context, AL_INVALID_VALUE,
180 "Declaring persistently mapped storage without read or write access");
181 else if(UNLIKELY(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE))
182 alSetError(context, AL_INVALID_ENUM, "Invalid format 0x%04x", format);
183 else if(UNLIKELY((align=SanitizeAlignment(srctype, ATOMIC_LOAD_SEQ(&albuf->UnpackAlign))) < 1))
184 alSetError(context, AL_INVALID_VALUE, "Invalid unpack alignment");
185 else
187 switch(srctype)
189 case UserFmtUByte:
190 case UserFmtShort:
191 case UserFmtFloat:
192 case UserFmtDouble:
193 case UserFmtMulaw:
194 case UserFmtAlaw:
195 framesize = FrameSizeFromUserFmt(srcchannels, srctype) * align;
196 break;
198 case UserFmtIMA4:
199 framesize = ((align-1)/2 + 4) * ChannelsFromUserFmt(srcchannels);
200 break;
202 case UserFmtMSADPCM:
203 framesize = ((align-2)/2 + 7) * ChannelsFromUserFmt(srcchannels);
204 break;
206 if((size%framesize) != 0)
207 alSetError(context, AL_INVALID_VALUE,
208 "Data size %d is not a multiple of frame size %d (%d unpack alignment)",
209 size, framesize, align);
210 else
211 LoadData(context, albuf, freq, size/framesize*align, srcchannels, srctype, data, align,
212 flags);
215 UnlockBuffersRead(device);
216 ALCcontext_DecRef(context);
219 AL_API void* AL_APIENTRY alMapBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length, ALbitfieldSOFT access)
221 void *retval = NULL;
222 ALCdevice *device;
223 ALCcontext *context;
224 ALbuffer *albuf;
226 context = GetContextRef();
227 if(!context) return retval;
229 device = context->Device;
230 LockBuffersRead(device);
231 if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL))
232 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
233 else if(UNLIKELY((access&INVALID_MAP_FLAGS) != 0))
234 alSetError(context, AL_INVALID_VALUE, "Invalid map flags 0x%x", access&INVALID_MAP_FLAGS);
235 else if(UNLIKELY(!(access&MAP_READ_WRITE_FLAGS)))
236 alSetError(context, AL_INVALID_VALUE, "Mapping buffer %u without read or write access",
237 buffer);
238 else
240 ALbitfieldSOFT unavailable;
241 WriteLock(&albuf->lock);
242 unavailable = (albuf->Access^access) & access;
243 if(UNLIKELY(ReadRef(&albuf->ref) != 0 && !(access&AL_MAP_PERSISTENT_BIT_SOFT)))
244 alSetError(context, AL_INVALID_OPERATION,
245 "Mapping in-use buffer %u without persistent mapping", buffer);
246 else if(UNLIKELY(albuf->MappedAccess != 0))
247 alSetError(context, AL_INVALID_OPERATION, "Mapping already-mapped buffer %u", buffer);
248 else if(UNLIKELY((unavailable&AL_MAP_READ_BIT_SOFT)))
249 alSetError(context, AL_INVALID_VALUE,
250 "Mapping buffer %u for reading without read access", buffer);
251 else if(UNLIKELY((unavailable&AL_MAP_WRITE_BIT_SOFT)))
252 alSetError(context, AL_INVALID_VALUE,
253 "Mapping buffer %u for writing without write access", buffer);
254 else if(UNLIKELY((unavailable&AL_MAP_PERSISTENT_BIT_SOFT)))
255 alSetError(context, AL_INVALID_VALUE,
256 "Mapping buffer %u persistently without persistent access", buffer);
257 else if(UNLIKELY(offset < 0 || offset >= albuf->OriginalSize ||
258 length <= 0 || length > albuf->OriginalSize - offset))
259 alSetError(context, AL_INVALID_VALUE, "Mapping invalid range %d+%d for buffer %u",
260 offset, length, buffer);
261 else
263 retval = (ALbyte*)albuf->data + offset;
264 albuf->MappedAccess = access;
265 albuf->MappedOffset = offset;
266 albuf->MappedSize = length;
269 WriteUnlock(&albuf->lock);
271 UnlockBuffersRead(device);
273 ALCcontext_DecRef(context);
274 return retval;
277 AL_API void AL_APIENTRY alUnmapBufferSOFT(ALuint buffer)
279 ALCdevice *device;
280 ALCcontext *context;
281 ALbuffer *albuf;
283 context = GetContextRef();
284 if(!context) return;
286 device = context->Device;
287 LockBuffersRead(device);
288 if((albuf=LookupBuffer(device, buffer)) == NULL)
289 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
290 else
292 WriteLock(&albuf->lock);
293 if(albuf->MappedAccess == 0)
294 alSetError(context, AL_INVALID_OPERATION, "Unmapping unmapped buffer %u", buffer);
295 else
297 albuf->MappedAccess = 0;
298 albuf->MappedOffset = 0;
299 albuf->MappedSize = 0;
301 WriteUnlock(&albuf->lock);
303 UnlockBuffersRead(device);
305 ALCcontext_DecRef(context);
308 AL_API void AL_APIENTRY alFlushMappedBufferSOFT(ALuint buffer, ALsizei offset, ALsizei length)
310 ALCdevice *device;
311 ALCcontext *context;
312 ALbuffer *albuf;
314 context = GetContextRef();
315 if(!context) return;
317 device = context->Device;
318 LockBuffersRead(device);
319 if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL))
320 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
321 else
323 WriteLock(&albuf->lock);
324 if(UNLIKELY(!(albuf->MappedAccess&AL_MAP_WRITE_BIT_SOFT)))
325 alSetError(context, AL_INVALID_OPERATION,
326 "Flushing buffer %u while not mapped for writing", buffer);
327 else if(UNLIKELY(offset < albuf->MappedOffset ||
328 offset >= albuf->MappedOffset+albuf->MappedSize ||
329 length <= 0 || length > albuf->MappedOffset+albuf->MappedSize-offset))
330 alSetError(context, AL_INVALID_VALUE, "Flushing invalid range %d+%d on buffer %u",
331 offset, length, buffer);
332 else
334 /* FIXME: Need to use some method of double-buffering for the mixer
335 * and app to hold separate memory, which can be safely transfered
336 * asynchronously. Currently we just say the app shouldn't write
337 * where OpenAL's reading, and hope for the best...
339 ATOMIC_THREAD_FENCE(almemory_order_seq_cst);
341 WriteUnlock(&albuf->lock);
343 UnlockBuffersRead(device);
345 ALCcontext_DecRef(context);
348 AL_API ALvoid AL_APIENTRY alBufferSubDataSOFT(ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length)
350 enum UserFmtChannels srcchannels = UserFmtMono;
351 enum UserFmtType srctype = UserFmtUByte;
352 ALCdevice *device;
353 ALCcontext *context;
354 ALbuffer *albuf;
356 context = GetContextRef();
357 if(!context) return;
359 device = context->Device;
360 LockBuffersRead(device);
361 if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL))
362 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
363 else if(UNLIKELY(DecomposeUserFormat(format, &srcchannels, &srctype) == AL_FALSE))
364 alSetError(context, AL_INVALID_ENUM, "Invalid format 0x%04x", format);
365 else
367 ALsizei unpack_align, align;
368 ALsizei byte_align;
369 ALsizei frame_size;
370 ALsizei num_chans;
371 void *dst;
373 WriteLock(&albuf->lock);
374 unpack_align = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
375 align = SanitizeAlignment(srctype, unpack_align);
376 if(UNLIKELY(align < 1))
377 alSetError(context, AL_INVALID_VALUE, "Invalid unpack alignment %d", unpack_align);
378 else if(UNLIKELY((long)srcchannels != (long)albuf->FmtChannels ||
379 srctype != albuf->OriginalType))
380 alSetError(context, AL_INVALID_ENUM, "Unpacking data with mismatched format");
381 else if(UNLIKELY(align != albuf->OriginalAlign))
382 alSetError(context, AL_INVALID_VALUE,
383 "Unpacking data with alignment %u does not match original alignment %u",
384 align, albuf->OriginalAlign);
385 else if(UNLIKELY(albuf->MappedAccess != 0))
386 alSetError(context, AL_INVALID_OPERATION, "Unpacking data into mapped buffer %u",
387 buffer);
388 else
390 num_chans = ChannelsFromFmt(albuf->FmtChannels);
391 frame_size = num_chans * BytesFromFmt(albuf->FmtType);
392 if(albuf->OriginalType == UserFmtIMA4)
393 byte_align = ((align-1)/2 + 4) * num_chans;
394 else if(albuf->OriginalType == UserFmtMSADPCM)
395 byte_align = ((align-2)/2 + 7) * num_chans;
396 else
397 byte_align = align * frame_size;
399 if(UNLIKELY(offset < 0 || length < 0 || offset > albuf->OriginalSize ||
400 length > albuf->OriginalSize-offset))
401 alSetError(context, AL_INVALID_VALUE, "Invalid data sub-range %d+%d on buffer %u",
402 offset, length, buffer);
403 else if(UNLIKELY((offset%byte_align) != 0))
404 alSetError(context, AL_INVALID_VALUE,
405 "Sub-range offset %d is not a multiple of frame size %d (%d unpack alignment)",
406 offset, byte_align, align);
407 else if(UNLIKELY((length%byte_align) != 0))
408 alSetError(context, AL_INVALID_VALUE,
409 "Sub-range length %d is not a multiple of frame size %d (%d unpack alignment)",
410 length, byte_align, align);
411 else
413 /* offset -> byte offset, length -> sample count */
414 offset = offset/byte_align * align * frame_size;
415 length = length/byte_align * align;
417 dst = (ALbyte*)albuf->data + offset;
418 if(srctype == UserFmtIMA4 && albuf->FmtType == FmtShort)
419 Convert_ALshort_ALima4(dst, data, num_chans, length, align);
420 else if(srctype == UserFmtMSADPCM && albuf->FmtType == FmtShort)
421 Convert_ALshort_ALmsadpcm(dst, data, num_chans, length, align);
422 else
424 assert((long)srctype == (long)albuf->FmtType);
425 memcpy(dst, data, length*frame_size);
430 WriteUnlock(&albuf->lock);
432 UnlockBuffersRead(device);
434 ALCcontext_DecRef(context);
438 AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint UNUSED(buffer),
439 ALuint UNUSED(samplerate), ALenum UNUSED(internalformat), ALsizei UNUSED(samples),
440 ALenum UNUSED(channels), ALenum UNUSED(type), const ALvoid *UNUSED(data))
442 ALCcontext *context;
444 context = GetContextRef();
445 if(!context) return;
447 alSetError(context, AL_INVALID_OPERATION, "alBufferSamplesSOFT not supported");
449 ALCcontext_DecRef(context);
452 AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint UNUSED(buffer),
453 ALsizei UNUSED(offset), ALsizei UNUSED(samples),
454 ALenum UNUSED(channels), ALenum UNUSED(type), const ALvoid *UNUSED(data))
456 ALCcontext *context;
458 context = GetContextRef();
459 if(!context) return;
461 alSetError(context, AL_INVALID_OPERATION, "alBufferSubSamplesSOFT not supported");
463 ALCcontext_DecRef(context);
466 AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint UNUSED(buffer),
467 ALsizei UNUSED(offset), ALsizei UNUSED(samples),
468 ALenum UNUSED(channels), ALenum UNUSED(type), ALvoid *UNUSED(data))
470 ALCcontext *context;
472 context = GetContextRef();
473 if(!context) return;
475 alSetError(context, AL_INVALID_OPERATION, "alGetBufferSamplesSOFT not supported");
477 ALCcontext_DecRef(context);
480 AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum UNUSED(format))
482 ALCcontext *context;
484 context = GetContextRef();
485 if(!context) return AL_FALSE;
487 alSetError(context, AL_INVALID_OPERATION, "alIsBufferFormatSupportedSOFT not supported");
489 ALCcontext_DecRef(context);
490 return AL_FALSE;
494 AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat UNUSED(value))
496 ALCdevice *device;
497 ALCcontext *context;
499 context = GetContextRef();
500 if(!context) return;
502 device = context->Device;
503 LockBuffersRead(device);
504 if(UNLIKELY(LookupBuffer(device, buffer) == NULL))
505 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
506 else switch(param)
508 default:
509 alSetError(context, AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
511 UnlockBuffersRead(device);
513 ALCcontext_DecRef(context);
517 AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat UNUSED(value1), ALfloat UNUSED(value2), ALfloat UNUSED(value3))
519 ALCdevice *device;
520 ALCcontext *context;
522 context = GetContextRef();
523 if(!context) return;
525 device = context->Device;
526 LockBuffersRead(device);
527 if(UNLIKELY(LookupBuffer(device, buffer) == NULL))
528 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
529 else switch(param)
531 default:
532 alSetError(context, AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
534 UnlockBuffersRead(device);
536 ALCcontext_DecRef(context);
540 AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values)
542 ALCdevice *device;
543 ALCcontext *context;
545 context = GetContextRef();
546 if(!context) return;
548 device = context->Device;
549 LockBuffersRead(device);
550 if(UNLIKELY(LookupBuffer(device, buffer) == NULL))
551 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
552 else if(UNLIKELY(!values))
553 alSetError(context, AL_INVALID_VALUE, "NULL pointer");
554 else switch(param)
556 default:
557 alSetError(context, AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
559 UnlockBuffersRead(device);
561 ALCcontext_DecRef(context);
565 AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value)
567 ALCdevice *device;
568 ALCcontext *context;
569 ALbuffer *albuf;
571 context = GetContextRef();
572 if(!context) return;
574 device = context->Device;
575 LockBuffersRead(device);
576 if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL))
577 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
578 else switch(param)
580 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
581 if(UNLIKELY(value < 0))
582 alSetError(context, AL_INVALID_VALUE, "Invalid unpack block alignment %d", value);
583 else
584 ATOMIC_STORE_SEQ(&albuf->UnpackAlign, value);
585 break;
587 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
588 if(UNLIKELY(value < 0))
589 alSetError(context, AL_INVALID_VALUE, "Invalid pack block alignment %d", value);
590 else
591 ATOMIC_STORE_SEQ(&albuf->PackAlign, value);
592 break;
594 default:
595 alSetError(context, AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
597 UnlockBuffersRead(device);
599 ALCcontext_DecRef(context);
603 AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint UNUSED(value1), ALint UNUSED(value2), ALint UNUSED(value3))
605 ALCdevice *device;
606 ALCcontext *context;
608 context = GetContextRef();
609 if(!context) return;
611 device = context->Device;
612 LockBuffersRead(device);
613 if(UNLIKELY(LookupBuffer(device, buffer) == NULL))
614 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
615 else switch(param)
617 default:
618 alSetError(context, AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
620 UnlockBuffersRead(device);
622 ALCcontext_DecRef(context);
626 AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values)
628 ALCdevice *device;
629 ALCcontext *context;
630 ALbuffer *albuf;
632 if(values)
634 switch(param)
636 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
637 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
638 alBufferi(buffer, param, values[0]);
639 return;
643 context = GetContextRef();
644 if(!context) return;
646 device = context->Device;
647 LockBuffersRead(device);
648 if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL))
649 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
650 else if(UNLIKELY(!values))
651 alSetError(context, AL_INVALID_VALUE, "NULL pointer");
652 else switch(param)
654 case AL_LOOP_POINTS_SOFT:
655 WriteLock(&albuf->lock);
656 if(UNLIKELY(ReadRef(&albuf->ref) != 0))
657 alSetError(context, AL_INVALID_OPERATION, "Modifying in-use buffer %u's loop points",
658 buffer);
659 else if(UNLIKELY(values[0] >= values[1] || values[0] < 0 || values[1] > albuf->SampleLen))
660 alSetError(context, AL_INVALID_VALUE, "Invalid loop point range %d -> %d o buffer %u",
661 values[0], values[1], buffer);
662 else
664 albuf->LoopStart = values[0];
665 albuf->LoopEnd = values[1];
667 WriteUnlock(&albuf->lock);
668 break;
670 default:
671 alSetError(context, AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x",
672 param);
674 UnlockBuffersRead(device);
676 ALCcontext_DecRef(context);
680 AL_API ALvoid AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value)
682 ALCdevice *device;
683 ALCcontext *context;
684 ALbuffer *albuf;
686 context = GetContextRef();
687 if(!context) return;
689 device = context->Device;
690 LockBuffersRead(device);
691 if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL))
692 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
693 else if(UNLIKELY(!value))
694 alSetError(context, AL_INVALID_VALUE, "NULL pointer");
695 else switch(param)
697 default:
698 alSetError(context, AL_INVALID_ENUM, "Invalid buffer float property 0x%04x", param);
700 UnlockBuffersRead(device);
702 ALCcontext_DecRef(context);
706 AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
708 ALCdevice *device;
709 ALCcontext *context;
711 context = GetContextRef();
712 if(!context) return;
714 device = context->Device;
715 LockBuffersRead(device);
716 if(UNLIKELY(LookupBuffer(device, buffer) == NULL))
717 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
718 else if(UNLIKELY(!value1 || !value2 || !value3))
719 alSetError(context, AL_INVALID_VALUE, "NULL pointer");
720 else switch(param)
722 default:
723 alSetError(context, AL_INVALID_ENUM, "Invalid buffer 3-float property 0x%04x", param);
725 UnlockBuffersRead(device);
727 ALCcontext_DecRef(context);
731 AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values)
733 ALCdevice *device;
734 ALCcontext *context;
736 switch(param)
738 case AL_SEC_LENGTH_SOFT:
739 alGetBufferf(buffer, param, values);
740 return;
743 context = GetContextRef();
744 if(!context) return;
746 device = context->Device;
747 LockBuffersRead(device);
748 if(UNLIKELY(LookupBuffer(device, buffer) == NULL))
749 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
750 else if(UNLIKELY(!values))
751 alSetError(context, AL_INVALID_VALUE, "NULL pointer");
752 else switch(param)
754 default:
755 alSetError(context, AL_INVALID_ENUM, "Invalid buffer float-vector property 0x%04x", param);
757 UnlockBuffersRead(device);
759 ALCcontext_DecRef(context);
763 AL_API ALvoid AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value)
765 ALCdevice *device;
766 ALCcontext *context;
767 ALbuffer *albuf;
769 context = GetContextRef();
770 if(!context) return;
772 device = context->Device;
773 LockBuffersRead(device);
774 if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL))
775 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
776 else if(UNLIKELY(!value))
777 alSetError(context, AL_INVALID_VALUE, "NULL pointer");
778 else switch(param)
780 case AL_FREQUENCY:
781 *value = albuf->Frequency;
782 break;
784 case AL_BITS:
785 *value = BytesFromFmt(albuf->FmtType) * 8;
786 break;
788 case AL_CHANNELS:
789 *value = ChannelsFromFmt(albuf->FmtChannels);
790 break;
792 case AL_SIZE:
793 ReadLock(&albuf->lock);
794 *value = albuf->SampleLen * FrameSizeFromFmt(albuf->FmtChannels,
795 albuf->FmtType);
796 ReadUnlock(&albuf->lock);
797 break;
799 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
800 *value = ATOMIC_LOAD_SEQ(&albuf->UnpackAlign);
801 break;
803 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
804 *value = ATOMIC_LOAD_SEQ(&albuf->PackAlign);
805 break;
807 default:
808 alSetError(context, AL_INVALID_ENUM, "Invalid buffer integer property 0x%04x", param);
810 UnlockBuffersRead(device);
812 ALCcontext_DecRef(context);
816 AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3)
818 ALCdevice *device;
819 ALCcontext *context;
821 context = GetContextRef();
822 if(!context) return;
824 device = context->Device;
825 LockBuffersRead(device);
826 if(UNLIKELY(LookupBuffer(device, buffer) == NULL))
827 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
828 else if(UNLIKELY(!value1 || !value2 || !value3))
829 alSetError(context, AL_INVALID_VALUE, "NULL pointer");
830 else switch(param)
832 default:
833 alSetError(context, AL_INVALID_ENUM, "Invalid buffer 3-integer property 0x%04x", param);
835 UnlockBuffersRead(device);
837 ALCcontext_DecRef(context);
841 AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values)
843 ALCdevice *device;
844 ALCcontext *context;
845 ALbuffer *albuf;
847 switch(param)
849 case AL_FREQUENCY:
850 case AL_BITS:
851 case AL_CHANNELS:
852 case AL_SIZE:
853 case AL_INTERNAL_FORMAT_SOFT:
854 case AL_BYTE_LENGTH_SOFT:
855 case AL_SAMPLE_LENGTH_SOFT:
856 case AL_UNPACK_BLOCK_ALIGNMENT_SOFT:
857 case AL_PACK_BLOCK_ALIGNMENT_SOFT:
858 alGetBufferi(buffer, param, values);
859 return;
862 context = GetContextRef();
863 if(!context) return;
865 device = context->Device;
866 LockBuffersRead(device);
867 if(UNLIKELY((albuf=LookupBuffer(device, buffer)) == NULL))
868 alSetError(context, AL_INVALID_NAME, "Invalid buffer ID %u", buffer);
869 else if(UNLIKELY(!values))
870 alSetError(context, AL_INVALID_VALUE, "NULL pointer");
871 else switch(param)
873 case AL_LOOP_POINTS_SOFT:
874 ReadLock(&albuf->lock);
875 values[0] = albuf->LoopStart;
876 values[1] = albuf->LoopEnd;
877 ReadUnlock(&albuf->lock);
878 break;
880 default:
881 alSetError(context, AL_INVALID_ENUM, "Invalid buffer integer-vector property 0x%04x",
882 param);
884 UnlockBuffersRead(device);
886 ALCcontext_DecRef(context);
891 * LoadData
893 * Loads the specified data into the buffer, using the specified format.
895 static void LoadData(ALCcontext *context, ALbuffer *ALBuf, ALuint freq, ALsizei frames, enum UserFmtChannels SrcChannels, enum UserFmtType SrcType, const ALvoid *data, ALsizei align, ALbitfieldSOFT access)
897 enum FmtChannels DstChannels = FmtMono;
898 enum FmtType DstType = FmtUByte;
899 ALsizei NumChannels, FrameSize;
900 ALsizei newsize;
902 /* Currently no channels need to be converted. */
903 switch(SrcChannels)
905 case UserFmtMono: DstChannels = FmtMono; break;
906 case UserFmtStereo: DstChannels = FmtStereo; break;
907 case UserFmtRear: DstChannels = FmtRear; break;
908 case UserFmtQuad: DstChannels = FmtQuad; break;
909 case UserFmtX51: DstChannels = FmtX51; break;
910 case UserFmtX61: DstChannels = FmtX61; break;
911 case UserFmtX71: DstChannels = FmtX71; break;
912 case UserFmtBFormat2D: DstChannels = FmtBFormat2D; break;
913 case UserFmtBFormat3D: DstChannels = FmtBFormat3D; break;
915 if(UNLIKELY((long)SrcChannels != (long)DstChannels))
916 SETERR_RETURN(context, AL_INVALID_ENUM,, "Invalid format");
918 /* IMA4 and MSADPCM convert to 16-bit short. */
919 switch(SrcType)
921 case UserFmtUByte: DstType = FmtUByte; break;
922 case UserFmtShort: DstType = FmtShort; break;
923 case UserFmtFloat: DstType = FmtFloat; break;
924 case UserFmtDouble: DstType = FmtDouble; break;
925 case UserFmtAlaw: DstType = FmtAlaw; break;
926 case UserFmtMulaw: DstType = FmtMulaw; break;
927 case UserFmtIMA4: DstType = FmtShort; break;
928 case UserFmtMSADPCM: DstType = FmtShort; break;
931 if(access != 0)
933 if(UNLIKELY((long)SrcType != (long)DstType))
934 SETERR_RETURN(context, AL_INVALID_VALUE,, "Format cannot be mapped or preserved");
937 NumChannels = ChannelsFromFmt(DstChannels);
938 FrameSize = NumChannels * BytesFromFmt(DstType);
940 if(UNLIKELY(frames > INT_MAX/FrameSize))
941 SETERR_RETURN(context, AL_OUT_OF_MEMORY,,
942 "Buffer size overflow, %d frames x %d bytes per frame", frames, FrameSize);
943 newsize = frames*FrameSize;
945 WriteLock(&ALBuf->lock);
946 if(UNLIKELY(ReadRef(&ALBuf->ref) != 0 || ALBuf->MappedAccess != 0))
948 WriteUnlock(&ALBuf->lock);
949 SETERR_RETURN(context, AL_INVALID_OPERATION,, "Modifying storage for in-use buffer");
952 if((access&AL_PRESERVE_DATA_BIT_SOFT))
954 /* Can only preserve data with the same format and alignment. */
955 if(UNLIKELY(ALBuf->FmtChannels != DstChannels || ALBuf->OriginalType != SrcType))
957 WriteUnlock(&ALBuf->lock);
958 SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched format");
960 if(UNLIKELY(ALBuf->OriginalAlign != align))
962 WriteUnlock(&ALBuf->lock);
963 SETERR_RETURN(context, AL_INVALID_VALUE,, "Preserving data of mismatched alignment");
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))
980 WriteUnlock(&ALBuf->lock);
981 SETERR_RETURN(context, AL_OUT_OF_MEMORY,, "Failed to allocate %d bytes of storage",
982 newsize);
984 if((access&AL_PRESERVE_DATA_BIT_SOFT))
986 ALsizei tocopy = mini(newsize, ALBuf->BytesAlloc);
987 if(tocopy > 0) memcpy(temp, ALBuf->data, tocopy);
989 al_free(ALBuf->data);
990 ALBuf->data = temp;
991 ALBuf->BytesAlloc = newsize;
994 ALBuf->OriginalType = SrcType;
995 if(SrcType == UserFmtIMA4)
997 ALsizei byte_align = ((align-1)/2 + 4) * NumChannels;
998 ALBuf->OriginalSize = frames / align * byte_align;
999 ALBuf->OriginalAlign = align;
1000 assert(DstType == FmtShort);
1001 if(data != NULL && ALBuf->data != NULL)
1002 Convert_ALshort_ALima4(ALBuf->data, data, NumChannels, frames, align);
1004 else if(SrcType == UserFmtMSADPCM)
1006 ALsizei byte_align = ((align-2)/2 + 7) * NumChannels;
1007 ALBuf->OriginalSize = frames / align * byte_align;
1008 ALBuf->OriginalAlign = align;
1009 assert(DstType == FmtShort);
1010 if(data != NULL && ALBuf->data != NULL)
1011 Convert_ALshort_ALmsadpcm(ALBuf->data, data, NumChannels, frames, align);
1013 else
1015 ALBuf->OriginalSize = frames * FrameSize;
1016 ALBuf->OriginalAlign = 1;
1017 assert((long)SrcType == (long)DstType);
1018 if(data != NULL && ALBuf->data != NULL)
1019 memcpy(ALBuf->data, data, frames*FrameSize);
1022 ALBuf->Frequency = freq;
1023 ALBuf->FmtChannels = DstChannels;
1024 ALBuf->FmtType = DstType;
1025 ALBuf->Access = access;
1027 ALBuf->SampleLen = frames;
1028 ALBuf->LoopStart = 0;
1029 ALBuf->LoopEnd = ALBuf->SampleLen;
1031 WriteUnlock(&ALBuf->lock);
1035 ALsizei BytesFromUserFmt(enum UserFmtType type)
1037 switch(type)
1039 case UserFmtUByte: return sizeof(ALubyte);
1040 case UserFmtShort: return sizeof(ALshort);
1041 case UserFmtFloat: return sizeof(ALfloat);
1042 case UserFmtDouble: return sizeof(ALdouble);
1043 case UserFmtMulaw: return sizeof(ALubyte);
1044 case UserFmtAlaw: return sizeof(ALubyte);
1045 case UserFmtIMA4: break; /* not handled here */
1046 case UserFmtMSADPCM: break; /* not handled here */
1048 return 0;
1050 ALsizei ChannelsFromUserFmt(enum UserFmtChannels chans)
1052 switch(chans)
1054 case UserFmtMono: return 1;
1055 case UserFmtStereo: return 2;
1056 case UserFmtRear: return 2;
1057 case UserFmtQuad: return 4;
1058 case UserFmtX51: return 6;
1059 case UserFmtX61: return 7;
1060 case UserFmtX71: return 8;
1061 case UserFmtBFormat2D: return 3;
1062 case UserFmtBFormat3D: return 4;
1064 return 0;
1066 static ALboolean DecomposeUserFormat(ALenum format, enum UserFmtChannels *chans,
1067 enum UserFmtType *type)
1069 static const struct {
1070 ALenum format;
1071 enum UserFmtChannels channels;
1072 enum UserFmtType type;
1073 } list[] = {
1074 { AL_FORMAT_MONO8, UserFmtMono, UserFmtUByte },
1075 { AL_FORMAT_MONO16, UserFmtMono, UserFmtShort },
1076 { AL_FORMAT_MONO_FLOAT32, UserFmtMono, UserFmtFloat },
1077 { AL_FORMAT_MONO_DOUBLE_EXT, UserFmtMono, UserFmtDouble },
1078 { AL_FORMAT_MONO_IMA4, UserFmtMono, UserFmtIMA4 },
1079 { AL_FORMAT_MONO_MSADPCM_SOFT, UserFmtMono, UserFmtMSADPCM },
1080 { AL_FORMAT_MONO_MULAW, UserFmtMono, UserFmtMulaw },
1081 { AL_FORMAT_MONO_ALAW_EXT, UserFmtMono, UserFmtAlaw },
1083 { AL_FORMAT_STEREO8, UserFmtStereo, UserFmtUByte },
1084 { AL_FORMAT_STEREO16, UserFmtStereo, UserFmtShort },
1085 { AL_FORMAT_STEREO_FLOAT32, UserFmtStereo, UserFmtFloat },
1086 { AL_FORMAT_STEREO_DOUBLE_EXT, UserFmtStereo, UserFmtDouble },
1087 { AL_FORMAT_STEREO_IMA4, UserFmtStereo, UserFmtIMA4 },
1088 { AL_FORMAT_STEREO_MSADPCM_SOFT, UserFmtStereo, UserFmtMSADPCM },
1089 { AL_FORMAT_STEREO_MULAW, UserFmtStereo, UserFmtMulaw },
1090 { AL_FORMAT_STEREO_ALAW_EXT, UserFmtStereo, UserFmtAlaw },
1092 { AL_FORMAT_REAR8, UserFmtRear, UserFmtUByte },
1093 { AL_FORMAT_REAR16, UserFmtRear, UserFmtShort },
1094 { AL_FORMAT_REAR32, UserFmtRear, UserFmtFloat },
1095 { AL_FORMAT_REAR_MULAW, UserFmtRear, UserFmtMulaw },
1097 { AL_FORMAT_QUAD8_LOKI, UserFmtQuad, UserFmtUByte },
1098 { AL_FORMAT_QUAD16_LOKI, UserFmtQuad, UserFmtShort },
1100 { AL_FORMAT_QUAD8, UserFmtQuad, UserFmtUByte },
1101 { AL_FORMAT_QUAD16, UserFmtQuad, UserFmtShort },
1102 { AL_FORMAT_QUAD32, UserFmtQuad, UserFmtFloat },
1103 { AL_FORMAT_QUAD_MULAW, UserFmtQuad, UserFmtMulaw },
1105 { AL_FORMAT_51CHN8, UserFmtX51, UserFmtUByte },
1106 { AL_FORMAT_51CHN16, UserFmtX51, UserFmtShort },
1107 { AL_FORMAT_51CHN32, UserFmtX51, UserFmtFloat },
1108 { AL_FORMAT_51CHN_MULAW, UserFmtX51, UserFmtMulaw },
1110 { AL_FORMAT_61CHN8, UserFmtX61, UserFmtUByte },
1111 { AL_FORMAT_61CHN16, UserFmtX61, UserFmtShort },
1112 { AL_FORMAT_61CHN32, UserFmtX61, UserFmtFloat },
1113 { AL_FORMAT_61CHN_MULAW, UserFmtX61, UserFmtMulaw },
1115 { AL_FORMAT_71CHN8, UserFmtX71, UserFmtUByte },
1116 { AL_FORMAT_71CHN16, UserFmtX71, UserFmtShort },
1117 { AL_FORMAT_71CHN32, UserFmtX71, UserFmtFloat },
1118 { AL_FORMAT_71CHN_MULAW, UserFmtX71, UserFmtMulaw },
1120 { AL_FORMAT_BFORMAT2D_8, UserFmtBFormat2D, UserFmtUByte },
1121 { AL_FORMAT_BFORMAT2D_16, UserFmtBFormat2D, UserFmtShort },
1122 { AL_FORMAT_BFORMAT2D_FLOAT32, UserFmtBFormat2D, UserFmtFloat },
1123 { AL_FORMAT_BFORMAT2D_MULAW, UserFmtBFormat2D, UserFmtMulaw },
1125 { AL_FORMAT_BFORMAT3D_8, UserFmtBFormat3D, UserFmtUByte },
1126 { AL_FORMAT_BFORMAT3D_16, UserFmtBFormat3D, UserFmtShort },
1127 { AL_FORMAT_BFORMAT3D_FLOAT32, UserFmtBFormat3D, UserFmtFloat },
1128 { AL_FORMAT_BFORMAT3D_MULAW, UserFmtBFormat3D, UserFmtMulaw },
1130 ALuint i;
1132 for(i = 0;i < COUNTOF(list);i++)
1134 if(list[i].format == format)
1136 *chans = list[i].channels;
1137 *type = list[i].type;
1138 return AL_TRUE;
1142 return AL_FALSE;
1145 ALsizei BytesFromFmt(enum FmtType type)
1147 switch(type)
1149 case FmtUByte: return sizeof(ALubyte);
1150 case FmtShort: return sizeof(ALshort);
1151 case FmtFloat: return sizeof(ALfloat);
1152 case FmtDouble: return sizeof(ALdouble);
1153 case FmtMulaw: return sizeof(ALubyte);
1154 case FmtAlaw: return sizeof(ALubyte);
1156 return 0;
1158 ALsizei ChannelsFromFmt(enum FmtChannels chans)
1160 switch(chans)
1162 case FmtMono: return 1;
1163 case FmtStereo: return 2;
1164 case FmtRear: return 2;
1165 case FmtQuad: return 4;
1166 case FmtX51: return 6;
1167 case FmtX61: return 7;
1168 case FmtX71: return 8;
1169 case FmtBFormat2D: return 3;
1170 case FmtBFormat3D: return 4;
1172 return 0;
1175 static ALsizei SanitizeAlignment(enum UserFmtType type, ALsizei align)
1177 if(align < 0)
1178 return 0;
1180 if(align == 0)
1182 if(type == UserFmtIMA4)
1184 /* Here is where things vary:
1185 * nVidia and Apple use 64+1 sample frames per block -> block_size=36 bytes per channel
1186 * Most PC sound software uses 2040+1 sample frames per block -> block_size=1024 bytes per channel
1188 return 65;
1190 if(type == UserFmtMSADPCM)
1191 return 64;
1192 return 1;
1195 if(type == UserFmtIMA4)
1197 /* IMA4 block alignment must be a multiple of 8, plus 1. */
1198 if((align&7) == 1) return align;
1199 return 0;
1201 if(type == UserFmtMSADPCM)
1203 /* MSADPCM block alignment must be a multiple of 2. */
1204 if((align&1) == 0) return align;
1205 return 0;
1208 return align;
1212 ALbuffer *NewBuffer(ALCcontext *context)
1214 ALCdevice *device = context->Device;
1215 ALbuffer *buffer;
1216 ALenum err;
1218 buffer = al_calloc(16, sizeof(ALbuffer));
1219 if(!buffer)
1220 SETERR_RETURN(context, AL_OUT_OF_MEMORY, NULL, "Failed to allocate buffer object");
1221 RWLockInit(&buffer->lock);
1222 buffer->Access = 0;
1223 buffer->MappedAccess = 0;
1225 err = NewThunkEntry(&buffer->id);
1226 if(err == AL_NO_ERROR)
1227 err = InsertUIntMapEntry(&device->BufferMap, buffer->id, buffer);
1228 if(err != AL_NO_ERROR)
1230 FreeThunkEntry(buffer->id);
1231 memset(buffer, 0, sizeof(ALbuffer));
1232 al_free(buffer);
1234 SETERR_RETURN(context, err, NULL, "Failed to set buffer ID");
1237 return buffer;
1240 void DeleteBuffer(ALCdevice *device, ALbuffer *buffer)
1242 RemoveBuffer(device, buffer->id);
1243 FreeThunkEntry(buffer->id);
1245 al_free(buffer->data);
1247 memset(buffer, 0, sizeof(*buffer));
1248 al_free(buffer);
1253 * ReleaseALBuffers()
1255 * INTERNAL: Called to destroy any buffers that still exist on the device
1257 ALvoid ReleaseALBuffers(ALCdevice *device)
1259 ALsizei i;
1260 for(i = 0;i < device->BufferMap.size;i++)
1262 ALbuffer *temp = device->BufferMap.values[i];
1263 device->BufferMap.values[i] = NULL;
1265 al_free(temp->data);
1267 FreeThunkEntry(temp->id);
1268 memset(temp, 0, sizeof(ALbuffer));
1269 al_free(temp);