Use a plain mutex for the property lock
[openal-soft.git] / OpenAL32 / alSource.c
blob025113821d7801611e3511d5a04694d61c97c802
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 <limits.h>
25 #include <math.h>
26 #include <float.h>
28 #include "AL/al.h"
29 #include "AL/alc.h"
30 #include "alMain.h"
31 #include "alError.h"
32 #include "alSource.h"
33 #include "alBuffer.h"
34 #include "alAuxEffectSlot.h"
35 #include "ringbuffer.h"
37 #include "backends/base.h"
39 #include "threads.h"
40 #include "almalloc.h"
43 static ALsource *AllocSource(ALCcontext *context);
44 static void FreeSource(ALCcontext *context, ALsource *source);
45 static void InitSourceParams(ALsource *Source, ALsizei num_sends);
46 static void DeinitSource(ALsource *source, ALsizei num_sends);
47 static void UpdateSourceProps(ALsource *source, ALvoice *voice, ALsizei num_sends, ALCcontext *context);
48 static ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime);
49 static ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime);
50 static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context);
51 static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALsizei *frac);
52 static ALboolean ApplyOffset(ALsource *Source, ALvoice *voice);
54 static inline void LockSourceList(ALCcontext *context)
55 { almtx_lock(&context->SourceLock); }
56 static inline void UnlockSourceList(ALCcontext *context)
57 { almtx_unlock(&context->SourceLock); }
59 static inline ALsource *LookupSource(ALCcontext *context, ALuint id)
61 SourceSubList *sublist;
62 ALuint lidx = (id-1) >> 6;
63 ALsizei slidx = (id-1) & 0x3f;
65 if(UNLIKELY(lidx >= VECTOR_SIZE(context->SourceList)))
66 return NULL;
67 sublist = &VECTOR_ELEM(context->SourceList, lidx);
68 if(UNLIKELY(sublist->FreeMask & (U64(1)<<slidx)))
69 return NULL;
70 return sublist->Sources + slidx;
73 static inline ALbuffer *LookupBuffer(ALCdevice *device, ALuint id)
75 BufferSubList *sublist;
76 ALuint lidx = (id-1) >> 6;
77 ALsizei slidx = (id-1) & 0x3f;
79 if(UNLIKELY(lidx >= VECTOR_SIZE(device->BufferList)))
80 return NULL;
81 sublist = &VECTOR_ELEM(device->BufferList, lidx);
82 if(UNLIKELY(sublist->FreeMask & (U64(1)<<slidx)))
83 return NULL;
84 return sublist->Buffers + slidx;
87 static inline ALfilter *LookupFilter(ALCdevice *device, ALuint id)
89 FilterSubList *sublist;
90 ALuint lidx = (id-1) >> 6;
91 ALsizei slidx = (id-1) & 0x3f;
93 if(UNLIKELY(lidx >= VECTOR_SIZE(device->FilterList)))
94 return NULL;
95 sublist = &VECTOR_ELEM(device->FilterList, lidx);
96 if(UNLIKELY(sublist->FreeMask & (U64(1)<<slidx)))
97 return NULL;
98 return sublist->Filters + slidx;
101 static inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id)
103 id--;
104 if(UNLIKELY(id >= VECTOR_SIZE(context->EffectSlotList)))
105 return NULL;
106 return VECTOR_ELEM(context->EffectSlotList, id);
110 typedef enum SourceProp {
111 srcPitch = AL_PITCH,
112 srcGain = AL_GAIN,
113 srcMinGain = AL_MIN_GAIN,
114 srcMaxGain = AL_MAX_GAIN,
115 srcMaxDistance = AL_MAX_DISTANCE,
116 srcRolloffFactor = AL_ROLLOFF_FACTOR,
117 srcDopplerFactor = AL_DOPPLER_FACTOR,
118 srcConeOuterGain = AL_CONE_OUTER_GAIN,
119 srcSecOffset = AL_SEC_OFFSET,
120 srcSampleOffset = AL_SAMPLE_OFFSET,
121 srcByteOffset = AL_BYTE_OFFSET,
122 srcConeInnerAngle = AL_CONE_INNER_ANGLE,
123 srcConeOuterAngle = AL_CONE_OUTER_ANGLE,
124 srcRefDistance = AL_REFERENCE_DISTANCE,
126 srcPosition = AL_POSITION,
127 srcVelocity = AL_VELOCITY,
128 srcDirection = AL_DIRECTION,
130 srcSourceRelative = AL_SOURCE_RELATIVE,
131 srcLooping = AL_LOOPING,
132 srcBuffer = AL_BUFFER,
133 srcSourceState = AL_SOURCE_STATE,
134 srcBuffersQueued = AL_BUFFERS_QUEUED,
135 srcBuffersProcessed = AL_BUFFERS_PROCESSED,
136 srcSourceType = AL_SOURCE_TYPE,
138 /* ALC_EXT_EFX */
139 srcConeOuterGainHF = AL_CONE_OUTER_GAINHF,
140 srcAirAbsorptionFactor = AL_AIR_ABSORPTION_FACTOR,
141 srcRoomRolloffFactor = AL_ROOM_ROLLOFF_FACTOR,
142 srcDirectFilterGainHFAuto = AL_DIRECT_FILTER_GAINHF_AUTO,
143 srcAuxSendFilterGainAuto = AL_AUXILIARY_SEND_FILTER_GAIN_AUTO,
144 srcAuxSendFilterGainHFAuto = AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO,
145 srcDirectFilter = AL_DIRECT_FILTER,
146 srcAuxSendFilter = AL_AUXILIARY_SEND_FILTER,
148 /* AL_SOFT_direct_channels */
149 srcDirectChannelsSOFT = AL_DIRECT_CHANNELS_SOFT,
151 /* AL_EXT_source_distance_model */
152 srcDistanceModel = AL_DISTANCE_MODEL,
154 /* AL_SOFT_source_latency */
155 srcSampleOffsetLatencySOFT = AL_SAMPLE_OFFSET_LATENCY_SOFT,
156 srcSecOffsetLatencySOFT = AL_SEC_OFFSET_LATENCY_SOFT,
158 /* AL_EXT_STEREO_ANGLES */
159 srcAngles = AL_STEREO_ANGLES,
161 /* AL_EXT_SOURCE_RADIUS */
162 srcRadius = AL_SOURCE_RADIUS,
164 /* AL_EXT_BFORMAT */
165 srcOrientation = AL_ORIENTATION,
167 /* AL_SOFT_source_resampler */
168 srcResampler = AL_SOURCE_RESAMPLER_SOFT,
170 /* AL_SOFT_source_spatialize */
171 srcSpatialize = AL_SOURCE_SPATIALIZE_SOFT,
173 /* ALC_SOFT_device_clock */
174 srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT,
175 srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT,
176 } SourceProp;
178 static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values);
179 static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values);
180 static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values);
182 static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values);
183 static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values);
184 static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values);
186 static inline ALvoice *GetSourceVoice(ALsource *source, ALCcontext *context)
188 ALint idx = source->VoiceIdx;
189 if(idx >= 0 && idx < context->VoiceCount)
191 ALvoice *voice = context->Voices[idx];
192 if(ATOMIC_LOAD(&voice->Source, almemory_order_acquire) == source)
193 return voice;
195 source->VoiceIdx = -1;
196 return NULL;
200 * Returns if the last known state for the source was playing or paused. Does
201 * not sync with the mixer voice.
203 static inline bool IsPlayingOrPaused(ALsource *source)
204 { return source->state == AL_PLAYING || source->state == AL_PAUSED; }
207 * Returns an updated source state using the matching voice's status (or lack
208 * thereof).
210 static inline ALenum GetSourceState(ALsource *source, ALvoice *voice)
212 if(!voice && source->state == AL_PLAYING)
213 source->state = AL_STOPPED;
214 return source->state;
218 * Returns if the source should specify an update, given the context's
219 * deferring state and the source's last known state.
221 static inline bool SourceShouldUpdate(ALsource *source, ALCcontext *context)
223 return !ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire) &&
224 IsPlayingOrPaused(source);
228 /** Can only be called while the mixer is locked! */
229 static void SendStateChangeEvent(ALCcontext *context, ALuint id, ALenum state)
231 ALbitfieldSOFT enabledevt;
232 AsyncEvent evt;
234 enabledevt = ATOMIC_LOAD(&context->EnabledEvts, almemory_order_acquire);
235 if(!(enabledevt&EventType_SourceStateChange)) return;
237 evt.EnumType = EventType_SourceStateChange;
238 evt.Type = AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT;
239 evt.ObjectId = id;
240 evt.Param = state;
241 snprintf(evt.Message, sizeof(evt.Message), "Source ID %u state changed to %s", id,
242 (state==AL_INITIAL) ? "AL_INITIAL" :
243 (state==AL_PLAYING) ? "AL_PLAYING" :
244 (state==AL_PAUSED) ? "AL_PAUSED" :
245 (state==AL_STOPPED) ? "AL_STOPPED" : "<unknown>"
247 /* The mixer may have queued a state change that's not yet been processed,
248 * and we don't want state change messages to occur out of order, so send
249 * it through the async queue to ensure proper ordering.
251 if(ll_ringbuffer_write_space(context->AsyncEvents) > 0)
252 ll_ringbuffer_write(context->AsyncEvents, (const char*)&evt, 1);
253 alsem_post(&context->EventSem);
257 static ALint FloatValsByProp(ALenum prop)
259 if(prop != (ALenum)((SourceProp)prop))
260 return 0;
261 switch((SourceProp)prop)
263 case AL_PITCH:
264 case AL_GAIN:
265 case AL_MIN_GAIN:
266 case AL_MAX_GAIN:
267 case AL_MAX_DISTANCE:
268 case AL_ROLLOFF_FACTOR:
269 case AL_DOPPLER_FACTOR:
270 case AL_CONE_OUTER_GAIN:
271 case AL_SEC_OFFSET:
272 case AL_SAMPLE_OFFSET:
273 case AL_BYTE_OFFSET:
274 case AL_CONE_INNER_ANGLE:
275 case AL_CONE_OUTER_ANGLE:
276 case AL_REFERENCE_DISTANCE:
277 case AL_CONE_OUTER_GAINHF:
278 case AL_AIR_ABSORPTION_FACTOR:
279 case AL_ROOM_ROLLOFF_FACTOR:
280 case AL_DIRECT_FILTER_GAINHF_AUTO:
281 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
282 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
283 case AL_DIRECT_CHANNELS_SOFT:
284 case AL_DISTANCE_MODEL:
285 case AL_SOURCE_RELATIVE:
286 case AL_LOOPING:
287 case AL_SOURCE_STATE:
288 case AL_BUFFERS_QUEUED:
289 case AL_BUFFERS_PROCESSED:
290 case AL_SOURCE_TYPE:
291 case AL_SOURCE_RADIUS:
292 case AL_SOURCE_RESAMPLER_SOFT:
293 case AL_SOURCE_SPATIALIZE_SOFT:
294 return 1;
296 case AL_STEREO_ANGLES:
297 return 2;
299 case AL_POSITION:
300 case AL_VELOCITY:
301 case AL_DIRECTION:
302 return 3;
304 case AL_ORIENTATION:
305 return 6;
307 case AL_SEC_OFFSET_LATENCY_SOFT:
308 case AL_SEC_OFFSET_CLOCK_SOFT:
309 break; /* Double only */
311 case AL_BUFFER:
312 case AL_DIRECT_FILTER:
313 case AL_AUXILIARY_SEND_FILTER:
314 break; /* i/i64 only */
315 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
316 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
317 break; /* i64 only */
319 return 0;
321 static ALint DoubleValsByProp(ALenum prop)
323 if(prop != (ALenum)((SourceProp)prop))
324 return 0;
325 switch((SourceProp)prop)
327 case AL_PITCH:
328 case AL_GAIN:
329 case AL_MIN_GAIN:
330 case AL_MAX_GAIN:
331 case AL_MAX_DISTANCE:
332 case AL_ROLLOFF_FACTOR:
333 case AL_DOPPLER_FACTOR:
334 case AL_CONE_OUTER_GAIN:
335 case AL_SEC_OFFSET:
336 case AL_SAMPLE_OFFSET:
337 case AL_BYTE_OFFSET:
338 case AL_CONE_INNER_ANGLE:
339 case AL_CONE_OUTER_ANGLE:
340 case AL_REFERENCE_DISTANCE:
341 case AL_CONE_OUTER_GAINHF:
342 case AL_AIR_ABSORPTION_FACTOR:
343 case AL_ROOM_ROLLOFF_FACTOR:
344 case AL_DIRECT_FILTER_GAINHF_AUTO:
345 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
346 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
347 case AL_DIRECT_CHANNELS_SOFT:
348 case AL_DISTANCE_MODEL:
349 case AL_SOURCE_RELATIVE:
350 case AL_LOOPING:
351 case AL_SOURCE_STATE:
352 case AL_BUFFERS_QUEUED:
353 case AL_BUFFERS_PROCESSED:
354 case AL_SOURCE_TYPE:
355 case AL_SOURCE_RADIUS:
356 case AL_SOURCE_RESAMPLER_SOFT:
357 case AL_SOURCE_SPATIALIZE_SOFT:
358 return 1;
360 case AL_SEC_OFFSET_LATENCY_SOFT:
361 case AL_SEC_OFFSET_CLOCK_SOFT:
362 case AL_STEREO_ANGLES:
363 return 2;
365 case AL_POSITION:
366 case AL_VELOCITY:
367 case AL_DIRECTION:
368 return 3;
370 case AL_ORIENTATION:
371 return 6;
373 case AL_BUFFER:
374 case AL_DIRECT_FILTER:
375 case AL_AUXILIARY_SEND_FILTER:
376 break; /* i/i64 only */
377 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
378 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
379 break; /* i64 only */
381 return 0;
384 static ALint IntValsByProp(ALenum prop)
386 if(prop != (ALenum)((SourceProp)prop))
387 return 0;
388 switch((SourceProp)prop)
390 case AL_PITCH:
391 case AL_GAIN:
392 case AL_MIN_GAIN:
393 case AL_MAX_GAIN:
394 case AL_MAX_DISTANCE:
395 case AL_ROLLOFF_FACTOR:
396 case AL_DOPPLER_FACTOR:
397 case AL_CONE_OUTER_GAIN:
398 case AL_SEC_OFFSET:
399 case AL_SAMPLE_OFFSET:
400 case AL_BYTE_OFFSET:
401 case AL_CONE_INNER_ANGLE:
402 case AL_CONE_OUTER_ANGLE:
403 case AL_REFERENCE_DISTANCE:
404 case AL_CONE_OUTER_GAINHF:
405 case AL_AIR_ABSORPTION_FACTOR:
406 case AL_ROOM_ROLLOFF_FACTOR:
407 case AL_DIRECT_FILTER_GAINHF_AUTO:
408 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
409 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
410 case AL_DIRECT_CHANNELS_SOFT:
411 case AL_DISTANCE_MODEL:
412 case AL_SOURCE_RELATIVE:
413 case AL_LOOPING:
414 case AL_BUFFER:
415 case AL_SOURCE_STATE:
416 case AL_BUFFERS_QUEUED:
417 case AL_BUFFERS_PROCESSED:
418 case AL_SOURCE_TYPE:
419 case AL_DIRECT_FILTER:
420 case AL_SOURCE_RADIUS:
421 case AL_SOURCE_RESAMPLER_SOFT:
422 case AL_SOURCE_SPATIALIZE_SOFT:
423 return 1;
425 case AL_POSITION:
426 case AL_VELOCITY:
427 case AL_DIRECTION:
428 case AL_AUXILIARY_SEND_FILTER:
429 return 3;
431 case AL_ORIENTATION:
432 return 6;
434 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
435 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
436 break; /* i64 only */
437 case AL_SEC_OFFSET_LATENCY_SOFT:
438 case AL_SEC_OFFSET_CLOCK_SOFT:
439 break; /* Double only */
440 case AL_STEREO_ANGLES:
441 break; /* Float/double only */
443 return 0;
445 static ALint Int64ValsByProp(ALenum prop)
447 if(prop != (ALenum)((SourceProp)prop))
448 return 0;
449 switch((SourceProp)prop)
451 case AL_PITCH:
452 case AL_GAIN:
453 case AL_MIN_GAIN:
454 case AL_MAX_GAIN:
455 case AL_MAX_DISTANCE:
456 case AL_ROLLOFF_FACTOR:
457 case AL_DOPPLER_FACTOR:
458 case AL_CONE_OUTER_GAIN:
459 case AL_SEC_OFFSET:
460 case AL_SAMPLE_OFFSET:
461 case AL_BYTE_OFFSET:
462 case AL_CONE_INNER_ANGLE:
463 case AL_CONE_OUTER_ANGLE:
464 case AL_REFERENCE_DISTANCE:
465 case AL_CONE_OUTER_GAINHF:
466 case AL_AIR_ABSORPTION_FACTOR:
467 case AL_ROOM_ROLLOFF_FACTOR:
468 case AL_DIRECT_FILTER_GAINHF_AUTO:
469 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
470 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
471 case AL_DIRECT_CHANNELS_SOFT:
472 case AL_DISTANCE_MODEL:
473 case AL_SOURCE_RELATIVE:
474 case AL_LOOPING:
475 case AL_BUFFER:
476 case AL_SOURCE_STATE:
477 case AL_BUFFERS_QUEUED:
478 case AL_BUFFERS_PROCESSED:
479 case AL_SOURCE_TYPE:
480 case AL_DIRECT_FILTER:
481 case AL_SOURCE_RADIUS:
482 case AL_SOURCE_RESAMPLER_SOFT:
483 case AL_SOURCE_SPATIALIZE_SOFT:
484 return 1;
486 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
487 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
488 return 2;
490 case AL_POSITION:
491 case AL_VELOCITY:
492 case AL_DIRECTION:
493 case AL_AUXILIARY_SEND_FILTER:
494 return 3;
496 case AL_ORIENTATION:
497 return 6;
499 case AL_SEC_OFFSET_LATENCY_SOFT:
500 case AL_SEC_OFFSET_CLOCK_SOFT:
501 break; /* Double only */
502 case AL_STEREO_ANGLES:
503 break; /* Float/double only */
505 return 0;
509 #define CHECKVAL(x) do { \
510 if(!(x)) \
512 alSetError(Context, AL_INVALID_VALUE, "Value out of range"); \
513 return AL_FALSE; \
515 } while(0)
517 #define DO_UPDATEPROPS() do { \
518 ALvoice *voice; \
519 if(SourceShouldUpdate(Source, Context) && \
520 (voice=GetSourceVoice(Source, Context)) != NULL) \
521 UpdateSourceProps(Source, voice, device->NumAuxSends, Context); \
522 else \
523 ATOMIC_FLAG_CLEAR(&Source->PropsClean, almemory_order_release); \
524 } while(0)
526 static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values)
528 ALCdevice *device = Context->Device;
529 ALint ival;
531 switch(prop)
533 case AL_SEC_OFFSET_LATENCY_SOFT:
534 case AL_SEC_OFFSET_CLOCK_SOFT:
535 /* Query only */
536 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
537 "Setting read-only source property 0x%04x", prop);
539 case AL_PITCH:
540 CHECKVAL(*values >= 0.0f);
542 Source->Pitch = *values;
543 DO_UPDATEPROPS();
544 return AL_TRUE;
546 case AL_CONE_INNER_ANGLE:
547 CHECKVAL(*values >= 0.0f && *values <= 360.0f);
549 Source->InnerAngle = *values;
550 DO_UPDATEPROPS();
551 return AL_TRUE;
553 case AL_CONE_OUTER_ANGLE:
554 CHECKVAL(*values >= 0.0f && *values <= 360.0f);
556 Source->OuterAngle = *values;
557 DO_UPDATEPROPS();
558 return AL_TRUE;
560 case AL_GAIN:
561 CHECKVAL(*values >= 0.0f);
563 Source->Gain = *values;
564 DO_UPDATEPROPS();
565 return AL_TRUE;
567 case AL_MAX_DISTANCE:
568 CHECKVAL(*values >= 0.0f);
570 Source->MaxDistance = *values;
571 DO_UPDATEPROPS();
572 return AL_TRUE;
574 case AL_ROLLOFF_FACTOR:
575 CHECKVAL(*values >= 0.0f);
577 Source->RolloffFactor = *values;
578 DO_UPDATEPROPS();
579 return AL_TRUE;
581 case AL_REFERENCE_DISTANCE:
582 CHECKVAL(*values >= 0.0f);
584 Source->RefDistance = *values;
585 DO_UPDATEPROPS();
586 return AL_TRUE;
588 case AL_MIN_GAIN:
589 CHECKVAL(*values >= 0.0f);
591 Source->MinGain = *values;
592 DO_UPDATEPROPS();
593 return AL_TRUE;
595 case AL_MAX_GAIN:
596 CHECKVAL(*values >= 0.0f);
598 Source->MaxGain = *values;
599 DO_UPDATEPROPS();
600 return AL_TRUE;
602 case AL_CONE_OUTER_GAIN:
603 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
605 Source->OuterGain = *values;
606 DO_UPDATEPROPS();
607 return AL_TRUE;
609 case AL_CONE_OUTER_GAINHF:
610 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
612 Source->OuterGainHF = *values;
613 DO_UPDATEPROPS();
614 return AL_TRUE;
616 case AL_AIR_ABSORPTION_FACTOR:
617 CHECKVAL(*values >= 0.0f && *values <= 10.0f);
619 Source->AirAbsorptionFactor = *values;
620 DO_UPDATEPROPS();
621 return AL_TRUE;
623 case AL_ROOM_ROLLOFF_FACTOR:
624 CHECKVAL(*values >= 0.0f && *values <= 10.0f);
626 Source->RoomRolloffFactor = *values;
627 DO_UPDATEPROPS();
628 return AL_TRUE;
630 case AL_DOPPLER_FACTOR:
631 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
633 Source->DopplerFactor = *values;
634 DO_UPDATEPROPS();
635 return AL_TRUE;
637 case AL_SEC_OFFSET:
638 case AL_SAMPLE_OFFSET:
639 case AL_BYTE_OFFSET:
640 CHECKVAL(*values >= 0.0f);
642 Source->OffsetType = prop;
643 Source->Offset = *values;
645 if(IsPlayingOrPaused(Source))
647 ALvoice *voice;
649 ALCdevice_Lock(Context->Device);
650 /* Double-check that the source is still playing while we have
651 * the lock.
653 voice = GetSourceVoice(Source, Context);
654 if(voice)
656 if(ApplyOffset(Source, voice) == AL_FALSE)
658 ALCdevice_Unlock(Context->Device);
659 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid offset");
662 ALCdevice_Unlock(Context->Device);
664 return AL_TRUE;
666 case AL_SOURCE_RADIUS:
667 CHECKVAL(*values >= 0.0f && isfinite(*values));
669 Source->Radius = *values;
670 DO_UPDATEPROPS();
671 return AL_TRUE;
673 case AL_STEREO_ANGLES:
674 CHECKVAL(isfinite(values[0]) && isfinite(values[1]));
676 Source->StereoPan[0] = values[0];
677 Source->StereoPan[1] = values[1];
678 DO_UPDATEPROPS();
679 return AL_TRUE;
682 case AL_POSITION:
683 CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]));
685 Source->Position[0] = values[0];
686 Source->Position[1] = values[1];
687 Source->Position[2] = values[2];
688 DO_UPDATEPROPS();
689 return AL_TRUE;
691 case AL_VELOCITY:
692 CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]));
694 Source->Velocity[0] = values[0];
695 Source->Velocity[1] = values[1];
696 Source->Velocity[2] = values[2];
697 DO_UPDATEPROPS();
698 return AL_TRUE;
700 case AL_DIRECTION:
701 CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]));
703 Source->Direction[0] = values[0];
704 Source->Direction[1] = values[1];
705 Source->Direction[2] = values[2];
706 DO_UPDATEPROPS();
707 return AL_TRUE;
709 case AL_ORIENTATION:
710 CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) &&
711 isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5]));
713 Source->Orientation[0][0] = values[0];
714 Source->Orientation[0][1] = values[1];
715 Source->Orientation[0][2] = values[2];
716 Source->Orientation[1][0] = values[3];
717 Source->Orientation[1][1] = values[4];
718 Source->Orientation[1][2] = values[5];
719 DO_UPDATEPROPS();
720 return AL_TRUE;
723 case AL_SOURCE_RELATIVE:
724 case AL_LOOPING:
725 case AL_SOURCE_STATE:
726 case AL_SOURCE_TYPE:
727 case AL_DISTANCE_MODEL:
728 case AL_DIRECT_FILTER_GAINHF_AUTO:
729 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
730 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
731 case AL_DIRECT_CHANNELS_SOFT:
732 case AL_SOURCE_RESAMPLER_SOFT:
733 case AL_SOURCE_SPATIALIZE_SOFT:
734 ival = (ALint)values[0];
735 return SetSourceiv(Source, Context, prop, &ival);
737 case AL_BUFFERS_QUEUED:
738 case AL_BUFFERS_PROCESSED:
739 ival = (ALint)((ALuint)values[0]);
740 return SetSourceiv(Source, Context, prop, &ival);
742 case AL_BUFFER:
743 case AL_DIRECT_FILTER:
744 case AL_AUXILIARY_SEND_FILTER:
745 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
746 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
747 break;
750 ERR("Unexpected property: 0x%04x\n", prop);
751 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source float property 0x%04x", prop);
754 static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values)
756 ALCdevice *device = Context->Device;
757 ALbuffer *buffer = NULL;
758 ALfilter *filter = NULL;
759 ALeffectslot *slot = NULL;
760 ALbufferlistitem *oldlist;
761 ALfloat fvals[6];
763 switch(prop)
765 case AL_SOURCE_STATE:
766 case AL_SOURCE_TYPE:
767 case AL_BUFFERS_QUEUED:
768 case AL_BUFFERS_PROCESSED:
769 /* Query only */
770 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
771 "Setting read-only source property 0x%04x", prop);
773 case AL_SOURCE_RELATIVE:
774 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
776 Source->HeadRelative = (ALboolean)*values;
777 DO_UPDATEPROPS();
778 return AL_TRUE;
780 case AL_LOOPING:
781 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
783 Source->Looping = (ALboolean)*values;
784 if(IsPlayingOrPaused(Source))
786 ALvoice *voice = GetSourceVoice(Source, Context);
787 if(voice)
789 if(Source->Looping)
790 ATOMIC_STORE(&voice->loop_buffer, Source->queue, almemory_order_release);
791 else
792 ATOMIC_STORE(&voice->loop_buffer, NULL, almemory_order_release);
794 /* If the source is playing, wait for the current mix to finish
795 * to ensure it isn't currently looping back or reaching the
796 * end.
798 while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1))
799 althrd_yield();
802 return AL_TRUE;
804 case AL_BUFFER:
805 LockBufferList(device);
806 if(!(*values == 0 || (buffer=LookupBuffer(device, *values)) != NULL))
808 UnlockBufferList(device);
809 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid buffer ID %u",
810 *values);
813 if(buffer && buffer->MappedAccess != 0 &&
814 !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
816 UnlockBufferList(device);
817 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
818 "Setting non-persistently mapped buffer %u", buffer->id);
820 else
822 ALenum state = GetSourceState(Source, GetSourceVoice(Source, Context));
823 if(state == AL_PLAYING || state == AL_PAUSED)
825 UnlockBufferList(device);
826 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
827 "Setting buffer on playing or paused source %u", Source->id);
831 oldlist = Source->queue;
832 if(buffer != NULL)
834 /* Add the selected buffer to a one-item queue */
835 ALbufferlistitem *newlist = al_calloc(DEF_ALIGN,
836 FAM_SIZE(ALbufferlistitem, buffers, 1));
837 ATOMIC_INIT(&newlist->next, NULL);
838 newlist->num_buffers = 1;
839 newlist->buffers[0] = buffer;
840 IncrementRef(&buffer->ref);
842 /* Source is now Static */
843 Source->SourceType = AL_STATIC;
844 Source->queue = newlist;
846 else
848 /* Source is now Undetermined */
849 Source->SourceType = AL_UNDETERMINED;
850 Source->queue = NULL;
852 UnlockBufferList(device);
854 /* Delete all elements in the previous queue */
855 while(oldlist != NULL)
857 ALsizei i;
858 ALbufferlistitem *temp = oldlist;
859 oldlist = ATOMIC_LOAD(&temp->next, almemory_order_relaxed);
861 for(i = 0;i < temp->num_buffers;i++)
863 if(temp->buffers[i])
864 DecrementRef(&temp->buffers[i]->ref);
866 al_free(temp);
868 return AL_TRUE;
870 case AL_SEC_OFFSET:
871 case AL_SAMPLE_OFFSET:
872 case AL_BYTE_OFFSET:
873 CHECKVAL(*values >= 0);
875 Source->OffsetType = prop;
876 Source->Offset = *values;
878 if(IsPlayingOrPaused(Source))
880 ALvoice *voice;
882 ALCdevice_Lock(Context->Device);
883 voice = GetSourceVoice(Source, Context);
884 if(voice)
886 if(ApplyOffset(Source, voice) == AL_FALSE)
888 ALCdevice_Unlock(Context->Device);
889 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE,
890 "Invalid source offset");
893 ALCdevice_Unlock(Context->Device);
895 return AL_TRUE;
897 case AL_DIRECT_FILTER:
898 LockFilterList(device);
899 if(!(*values == 0 || (filter=LookupFilter(device, *values)) != NULL))
901 UnlockFilterList(device);
902 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u",
903 *values);
906 if(!filter)
908 Source->Direct.Gain = 1.0f;
909 Source->Direct.GainHF = 1.0f;
910 Source->Direct.HFReference = LOWPASSFREQREF;
911 Source->Direct.GainLF = 1.0f;
912 Source->Direct.LFReference = HIGHPASSFREQREF;
914 else
916 Source->Direct.Gain = filter->Gain;
917 Source->Direct.GainHF = filter->GainHF;
918 Source->Direct.HFReference = filter->HFReference;
919 Source->Direct.GainLF = filter->GainLF;
920 Source->Direct.LFReference = filter->LFReference;
922 UnlockFilterList(device);
923 DO_UPDATEPROPS();
924 return AL_TRUE;
926 case AL_DIRECT_FILTER_GAINHF_AUTO:
927 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
929 Source->DryGainHFAuto = *values;
930 DO_UPDATEPROPS();
931 return AL_TRUE;
933 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
934 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
936 Source->WetGainAuto = *values;
937 DO_UPDATEPROPS();
938 return AL_TRUE;
940 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
941 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
943 Source->WetGainHFAuto = *values;
944 DO_UPDATEPROPS();
945 return AL_TRUE;
947 case AL_DIRECT_CHANNELS_SOFT:
948 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
950 Source->DirectChannels = *values;
951 DO_UPDATEPROPS();
952 return AL_TRUE;
954 case AL_DISTANCE_MODEL:
955 CHECKVAL(*values == AL_NONE ||
956 *values == AL_INVERSE_DISTANCE ||
957 *values == AL_INVERSE_DISTANCE_CLAMPED ||
958 *values == AL_LINEAR_DISTANCE ||
959 *values == AL_LINEAR_DISTANCE_CLAMPED ||
960 *values == AL_EXPONENT_DISTANCE ||
961 *values == AL_EXPONENT_DISTANCE_CLAMPED);
963 Source->DistanceModel = *values;
964 if(Context->SourceDistanceModel)
965 DO_UPDATEPROPS();
966 return AL_TRUE;
968 case AL_SOURCE_RESAMPLER_SOFT:
969 CHECKVAL(*values >= 0 && *values <= ResamplerMax);
971 Source->Resampler = *values;
972 DO_UPDATEPROPS();
973 return AL_TRUE;
975 case AL_SOURCE_SPATIALIZE_SOFT:
976 CHECKVAL(*values >= AL_FALSE && *values <= AL_AUTO_SOFT);
978 Source->Spatialize = *values;
979 DO_UPDATEPROPS();
980 return AL_TRUE;
983 case AL_AUXILIARY_SEND_FILTER:
984 LockEffectSlotList(Context);
985 if(!(values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != NULL))
987 UnlockEffectSlotList(Context);
988 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid effect ID %u",
989 values[0]);
991 if(!((ALuint)values[1] < (ALuint)device->NumAuxSends))
993 UnlockEffectSlotList(Context);
994 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid send %u", values[1]);
996 LockFilterList(device);
997 if(!(values[2] == 0 || (filter=LookupFilter(device, values[2])) != NULL))
999 UnlockFilterList(device);
1000 UnlockEffectSlotList(Context);
1001 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u",
1002 values[2]);
1005 if(!filter)
1007 /* Disable filter */
1008 Source->Send[values[1]].Gain = 1.0f;
1009 Source->Send[values[1]].GainHF = 1.0f;
1010 Source->Send[values[1]].HFReference = LOWPASSFREQREF;
1011 Source->Send[values[1]].GainLF = 1.0f;
1012 Source->Send[values[1]].LFReference = HIGHPASSFREQREF;
1014 else
1016 Source->Send[values[1]].Gain = filter->Gain;
1017 Source->Send[values[1]].GainHF = filter->GainHF;
1018 Source->Send[values[1]].HFReference = filter->HFReference;
1019 Source->Send[values[1]].GainLF = filter->GainLF;
1020 Source->Send[values[1]].LFReference = filter->LFReference;
1022 UnlockFilterList(device);
1024 if(slot != Source->Send[values[1]].Slot && IsPlayingOrPaused(Source))
1026 ALvoice *voice;
1027 /* Add refcount on the new slot, and release the previous slot */
1028 if(slot) IncrementRef(&slot->ref);
1029 if(Source->Send[values[1]].Slot)
1030 DecrementRef(&Source->Send[values[1]].Slot->ref);
1031 Source->Send[values[1]].Slot = slot;
1033 /* We must force an update if the auxiliary slot changed on an
1034 * active source, in case the slot is about to be deleted.
1036 if((voice=GetSourceVoice(Source, Context)) != NULL)
1037 UpdateSourceProps(Source, voice, device->NumAuxSends, Context);
1038 else
1039 ATOMIC_FLAG_CLEAR(&Source->PropsClean, almemory_order_release);
1041 else
1043 if(slot) IncrementRef(&slot->ref);
1044 if(Source->Send[values[1]].Slot)
1045 DecrementRef(&Source->Send[values[1]].Slot->ref);
1046 Source->Send[values[1]].Slot = slot;
1047 DO_UPDATEPROPS();
1049 UnlockEffectSlotList(Context);
1051 return AL_TRUE;
1054 /* 1x float */
1055 case AL_CONE_INNER_ANGLE:
1056 case AL_CONE_OUTER_ANGLE:
1057 case AL_PITCH:
1058 case AL_GAIN:
1059 case AL_MIN_GAIN:
1060 case AL_MAX_GAIN:
1061 case AL_REFERENCE_DISTANCE:
1062 case AL_ROLLOFF_FACTOR:
1063 case AL_CONE_OUTER_GAIN:
1064 case AL_MAX_DISTANCE:
1065 case AL_DOPPLER_FACTOR:
1066 case AL_CONE_OUTER_GAINHF:
1067 case AL_AIR_ABSORPTION_FACTOR:
1068 case AL_ROOM_ROLLOFF_FACTOR:
1069 case AL_SOURCE_RADIUS:
1070 fvals[0] = (ALfloat)*values;
1071 return SetSourcefv(Source, Context, (int)prop, fvals);
1073 /* 3x float */
1074 case AL_POSITION:
1075 case AL_VELOCITY:
1076 case AL_DIRECTION:
1077 fvals[0] = (ALfloat)values[0];
1078 fvals[1] = (ALfloat)values[1];
1079 fvals[2] = (ALfloat)values[2];
1080 return SetSourcefv(Source, Context, (int)prop, fvals);
1082 /* 6x float */
1083 case AL_ORIENTATION:
1084 fvals[0] = (ALfloat)values[0];
1085 fvals[1] = (ALfloat)values[1];
1086 fvals[2] = (ALfloat)values[2];
1087 fvals[3] = (ALfloat)values[3];
1088 fvals[4] = (ALfloat)values[4];
1089 fvals[5] = (ALfloat)values[5];
1090 return SetSourcefv(Source, Context, (int)prop, fvals);
1092 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1093 case AL_SEC_OFFSET_LATENCY_SOFT:
1094 case AL_SEC_OFFSET_CLOCK_SOFT:
1095 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1096 case AL_STEREO_ANGLES:
1097 break;
1100 ERR("Unexpected property: 0x%04x\n", prop);
1101 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x",
1102 prop);
1105 static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values)
1107 ALfloat fvals[6];
1108 ALint ivals[3];
1110 switch(prop)
1112 case AL_SOURCE_TYPE:
1113 case AL_BUFFERS_QUEUED:
1114 case AL_BUFFERS_PROCESSED:
1115 case AL_SOURCE_STATE:
1116 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1117 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1118 /* Query only */
1119 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
1120 "Setting read-only source property 0x%04x", prop);
1122 /* 1x int */
1123 case AL_SOURCE_RELATIVE:
1124 case AL_LOOPING:
1125 case AL_SEC_OFFSET:
1126 case AL_SAMPLE_OFFSET:
1127 case AL_BYTE_OFFSET:
1128 case AL_DIRECT_FILTER_GAINHF_AUTO:
1129 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1130 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1131 case AL_DIRECT_CHANNELS_SOFT:
1132 case AL_DISTANCE_MODEL:
1133 case AL_SOURCE_RESAMPLER_SOFT:
1134 case AL_SOURCE_SPATIALIZE_SOFT:
1135 CHECKVAL(*values <= INT_MAX && *values >= INT_MIN);
1137 ivals[0] = (ALint)*values;
1138 return SetSourceiv(Source, Context, (int)prop, ivals);
1140 /* 1x uint */
1141 case AL_BUFFER:
1142 case AL_DIRECT_FILTER:
1143 CHECKVAL(*values <= UINT_MAX && *values >= 0);
1145 ivals[0] = (ALuint)*values;
1146 return SetSourceiv(Source, Context, (int)prop, ivals);
1148 /* 3x uint */
1149 case AL_AUXILIARY_SEND_FILTER:
1150 CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 &&
1151 values[1] <= UINT_MAX && values[1] >= 0 &&
1152 values[2] <= UINT_MAX && values[2] >= 0);
1154 ivals[0] = (ALuint)values[0];
1155 ivals[1] = (ALuint)values[1];
1156 ivals[2] = (ALuint)values[2];
1157 return SetSourceiv(Source, Context, (int)prop, ivals);
1159 /* 1x float */
1160 case AL_CONE_INNER_ANGLE:
1161 case AL_CONE_OUTER_ANGLE:
1162 case AL_PITCH:
1163 case AL_GAIN:
1164 case AL_MIN_GAIN:
1165 case AL_MAX_GAIN:
1166 case AL_REFERENCE_DISTANCE:
1167 case AL_ROLLOFF_FACTOR:
1168 case AL_CONE_OUTER_GAIN:
1169 case AL_MAX_DISTANCE:
1170 case AL_DOPPLER_FACTOR:
1171 case AL_CONE_OUTER_GAINHF:
1172 case AL_AIR_ABSORPTION_FACTOR:
1173 case AL_ROOM_ROLLOFF_FACTOR:
1174 case AL_SOURCE_RADIUS:
1175 fvals[0] = (ALfloat)*values;
1176 return SetSourcefv(Source, Context, (int)prop, fvals);
1178 /* 3x float */
1179 case AL_POSITION:
1180 case AL_VELOCITY:
1181 case AL_DIRECTION:
1182 fvals[0] = (ALfloat)values[0];
1183 fvals[1] = (ALfloat)values[1];
1184 fvals[2] = (ALfloat)values[2];
1185 return SetSourcefv(Source, Context, (int)prop, fvals);
1187 /* 6x float */
1188 case AL_ORIENTATION:
1189 fvals[0] = (ALfloat)values[0];
1190 fvals[1] = (ALfloat)values[1];
1191 fvals[2] = (ALfloat)values[2];
1192 fvals[3] = (ALfloat)values[3];
1193 fvals[4] = (ALfloat)values[4];
1194 fvals[5] = (ALfloat)values[5];
1195 return SetSourcefv(Source, Context, (int)prop, fvals);
1197 case AL_SEC_OFFSET_LATENCY_SOFT:
1198 case AL_SEC_OFFSET_CLOCK_SOFT:
1199 case AL_STEREO_ANGLES:
1200 break;
1203 ERR("Unexpected property: 0x%04x\n", prop);
1204 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x",
1205 prop);
1208 #undef CHECKVAL
1211 static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values)
1213 ALCdevice *device = Context->Device;
1214 ClockLatency clocktime;
1215 ALuint64 srcclock;
1216 ALint ivals[3];
1217 ALboolean err;
1219 switch(prop)
1221 case AL_GAIN:
1222 *values = Source->Gain;
1223 return AL_TRUE;
1225 case AL_PITCH:
1226 *values = Source->Pitch;
1227 return AL_TRUE;
1229 case AL_MAX_DISTANCE:
1230 *values = Source->MaxDistance;
1231 return AL_TRUE;
1233 case AL_ROLLOFF_FACTOR:
1234 *values = Source->RolloffFactor;
1235 return AL_TRUE;
1237 case AL_REFERENCE_DISTANCE:
1238 *values = Source->RefDistance;
1239 return AL_TRUE;
1241 case AL_CONE_INNER_ANGLE:
1242 *values = Source->InnerAngle;
1243 return AL_TRUE;
1245 case AL_CONE_OUTER_ANGLE:
1246 *values = Source->OuterAngle;
1247 return AL_TRUE;
1249 case AL_MIN_GAIN:
1250 *values = Source->MinGain;
1251 return AL_TRUE;
1253 case AL_MAX_GAIN:
1254 *values = Source->MaxGain;
1255 return AL_TRUE;
1257 case AL_CONE_OUTER_GAIN:
1258 *values = Source->OuterGain;
1259 return AL_TRUE;
1261 case AL_SEC_OFFSET:
1262 case AL_SAMPLE_OFFSET:
1263 case AL_BYTE_OFFSET:
1264 *values = GetSourceOffset(Source, prop, Context);
1265 return AL_TRUE;
1267 case AL_CONE_OUTER_GAINHF:
1268 *values = Source->OuterGainHF;
1269 return AL_TRUE;
1271 case AL_AIR_ABSORPTION_FACTOR:
1272 *values = Source->AirAbsorptionFactor;
1273 return AL_TRUE;
1275 case AL_ROOM_ROLLOFF_FACTOR:
1276 *values = Source->RoomRolloffFactor;
1277 return AL_TRUE;
1279 case AL_DOPPLER_FACTOR:
1280 *values = Source->DopplerFactor;
1281 return AL_TRUE;
1283 case AL_SOURCE_RADIUS:
1284 *values = Source->Radius;
1285 return AL_TRUE;
1287 case AL_STEREO_ANGLES:
1288 values[0] = Source->StereoPan[0];
1289 values[1] = Source->StereoPan[1];
1290 return AL_TRUE;
1292 case AL_SEC_OFFSET_LATENCY_SOFT:
1293 /* Get the source offset with the clock time first. Then get the
1294 * clock time with the device latency. Order is important.
1296 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1297 almtx_lock(&device->BackendLock);
1298 clocktime = V0(device->Backend,getClockLatency)();
1299 almtx_unlock(&device->BackendLock);
1300 if(srcclock == (ALuint64)clocktime.ClockTime)
1301 values[1] = (ALdouble)clocktime.Latency / 1000000000.0;
1302 else
1304 /* If the clock time incremented, reduce the latency by that
1305 * much since it's that much closer to the source offset it got
1306 * earlier.
1308 ALuint64 diff = clocktime.ClockTime - srcclock;
1309 values[1] = (ALdouble)(clocktime.Latency - minu64(clocktime.Latency, diff)) /
1310 1000000000.0;
1312 return AL_TRUE;
1314 case AL_SEC_OFFSET_CLOCK_SOFT:
1315 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1316 values[1] = srcclock / 1000000000.0;
1317 return AL_TRUE;
1319 case AL_POSITION:
1320 values[0] = Source->Position[0];
1321 values[1] = Source->Position[1];
1322 values[2] = Source->Position[2];
1323 return AL_TRUE;
1325 case AL_VELOCITY:
1326 values[0] = Source->Velocity[0];
1327 values[1] = Source->Velocity[1];
1328 values[2] = Source->Velocity[2];
1329 return AL_TRUE;
1331 case AL_DIRECTION:
1332 values[0] = Source->Direction[0];
1333 values[1] = Source->Direction[1];
1334 values[2] = Source->Direction[2];
1335 return AL_TRUE;
1337 case AL_ORIENTATION:
1338 values[0] = Source->Orientation[0][0];
1339 values[1] = Source->Orientation[0][1];
1340 values[2] = Source->Orientation[0][2];
1341 values[3] = Source->Orientation[1][0];
1342 values[4] = Source->Orientation[1][1];
1343 values[5] = Source->Orientation[1][2];
1344 return AL_TRUE;
1346 /* 1x int */
1347 case AL_SOURCE_RELATIVE:
1348 case AL_LOOPING:
1349 case AL_SOURCE_STATE:
1350 case AL_BUFFERS_QUEUED:
1351 case AL_BUFFERS_PROCESSED:
1352 case AL_SOURCE_TYPE:
1353 case AL_DIRECT_FILTER_GAINHF_AUTO:
1354 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1355 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1356 case AL_DIRECT_CHANNELS_SOFT:
1357 case AL_DISTANCE_MODEL:
1358 case AL_SOURCE_RESAMPLER_SOFT:
1359 case AL_SOURCE_SPATIALIZE_SOFT:
1360 if((err=GetSourceiv(Source, Context, (int)prop, ivals)) != AL_FALSE)
1361 *values = (ALdouble)ivals[0];
1362 return err;
1364 case AL_BUFFER:
1365 case AL_DIRECT_FILTER:
1366 case AL_AUXILIARY_SEND_FILTER:
1367 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1368 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1369 break;
1372 ERR("Unexpected property: 0x%04x\n", prop);
1373 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source double property 0x%04x",
1374 prop);
1377 static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values)
1379 ALbufferlistitem *BufferList;
1380 ALdouble dvals[6];
1381 ALboolean err;
1383 switch(prop)
1385 case AL_SOURCE_RELATIVE:
1386 *values = Source->HeadRelative;
1387 return AL_TRUE;
1389 case AL_LOOPING:
1390 *values = Source->Looping;
1391 return AL_TRUE;
1393 case AL_BUFFER:
1394 BufferList = (Source->SourceType == AL_STATIC) ? Source->queue : NULL;
1395 *values = (BufferList && BufferList->num_buffers >= 1 && BufferList->buffers[0]) ?
1396 BufferList->buffers[0]->id : 0;
1397 return AL_TRUE;
1399 case AL_SOURCE_STATE:
1400 *values = GetSourceState(Source, GetSourceVoice(Source, Context));
1401 return AL_TRUE;
1403 case AL_BUFFERS_QUEUED:
1404 if(!(BufferList=Source->queue))
1405 *values = 0;
1406 else
1408 ALsizei count = 0;
1409 do {
1410 count += BufferList->num_buffers;
1411 BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
1412 } while(BufferList != NULL);
1413 *values = count;
1415 return AL_TRUE;
1417 case AL_BUFFERS_PROCESSED:
1418 if(Source->Looping || Source->SourceType != AL_STREAMING)
1420 /* Buffers on a looping source are in a perpetual state of
1421 * PENDING, so don't report any as PROCESSED */
1422 *values = 0;
1424 else
1426 const ALbufferlistitem *BufferList = Source->queue;
1427 const ALbufferlistitem *Current = NULL;
1428 ALsizei played = 0;
1429 ALvoice *voice;
1431 if((voice=GetSourceVoice(Source, Context)) != NULL)
1432 Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1433 else if(Source->state == AL_INITIAL)
1434 Current = BufferList;
1436 while(BufferList && BufferList != Current)
1438 played += BufferList->num_buffers;
1439 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
1440 almemory_order_relaxed);
1442 *values = played;
1444 return AL_TRUE;
1446 case AL_SOURCE_TYPE:
1447 *values = Source->SourceType;
1448 return AL_TRUE;
1450 case AL_DIRECT_FILTER_GAINHF_AUTO:
1451 *values = Source->DryGainHFAuto;
1452 return AL_TRUE;
1454 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1455 *values = Source->WetGainAuto;
1456 return AL_TRUE;
1458 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1459 *values = Source->WetGainHFAuto;
1460 return AL_TRUE;
1462 case AL_DIRECT_CHANNELS_SOFT:
1463 *values = Source->DirectChannels;
1464 return AL_TRUE;
1466 case AL_DISTANCE_MODEL:
1467 *values = Source->DistanceModel;
1468 return AL_TRUE;
1470 case AL_SOURCE_RESAMPLER_SOFT:
1471 *values = Source->Resampler;
1472 return AL_TRUE;
1474 case AL_SOURCE_SPATIALIZE_SOFT:
1475 *values = Source->Spatialize;
1476 return AL_TRUE;
1478 /* 1x float/double */
1479 case AL_CONE_INNER_ANGLE:
1480 case AL_CONE_OUTER_ANGLE:
1481 case AL_PITCH:
1482 case AL_GAIN:
1483 case AL_MIN_GAIN:
1484 case AL_MAX_GAIN:
1485 case AL_REFERENCE_DISTANCE:
1486 case AL_ROLLOFF_FACTOR:
1487 case AL_CONE_OUTER_GAIN:
1488 case AL_MAX_DISTANCE:
1489 case AL_SEC_OFFSET:
1490 case AL_SAMPLE_OFFSET:
1491 case AL_BYTE_OFFSET:
1492 case AL_DOPPLER_FACTOR:
1493 case AL_AIR_ABSORPTION_FACTOR:
1494 case AL_ROOM_ROLLOFF_FACTOR:
1495 case AL_CONE_OUTER_GAINHF:
1496 case AL_SOURCE_RADIUS:
1497 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1498 *values = (ALint)dvals[0];
1499 return err;
1501 /* 3x float/double */
1502 case AL_POSITION:
1503 case AL_VELOCITY:
1504 case AL_DIRECTION:
1505 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1507 values[0] = (ALint)dvals[0];
1508 values[1] = (ALint)dvals[1];
1509 values[2] = (ALint)dvals[2];
1511 return err;
1513 /* 6x float/double */
1514 case AL_ORIENTATION:
1515 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1517 values[0] = (ALint)dvals[0];
1518 values[1] = (ALint)dvals[1];
1519 values[2] = (ALint)dvals[2];
1520 values[3] = (ALint)dvals[3];
1521 values[4] = (ALint)dvals[4];
1522 values[5] = (ALint)dvals[5];
1524 return err;
1526 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1527 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1528 break; /* i64 only */
1529 case AL_SEC_OFFSET_LATENCY_SOFT:
1530 case AL_SEC_OFFSET_CLOCK_SOFT:
1531 break; /* Double only */
1532 case AL_STEREO_ANGLES:
1533 break; /* Float/double only */
1535 case AL_DIRECT_FILTER:
1536 case AL_AUXILIARY_SEND_FILTER:
1537 break; /* ??? */
1540 ERR("Unexpected property: 0x%04x\n", prop);
1541 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x",
1542 prop);
1545 static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values)
1547 ALCdevice *device = Context->Device;
1548 ClockLatency clocktime;
1549 ALuint64 srcclock;
1550 ALdouble dvals[6];
1551 ALint ivals[3];
1552 ALboolean err;
1554 switch(prop)
1556 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1557 /* Get the source offset with the clock time first. Then get the
1558 * clock time with the device latency. Order is important.
1560 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
1561 almtx_lock(&device->BackendLock);
1562 clocktime = V0(device->Backend,getClockLatency)();
1563 almtx_unlock(&device->BackendLock);
1564 if(srcclock == (ALuint64)clocktime.ClockTime)
1565 values[1] = clocktime.Latency;
1566 else
1568 /* If the clock time incremented, reduce the latency by that
1569 * much since it's that much closer to the source offset it got
1570 * earlier.
1572 ALuint64 diff = clocktime.ClockTime - srcclock;
1573 values[1] = clocktime.Latency - minu64(clocktime.Latency, diff);
1575 return AL_TRUE;
1577 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1578 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
1579 values[1] = srcclock;
1580 return AL_TRUE;
1582 /* 1x float/double */
1583 case AL_CONE_INNER_ANGLE:
1584 case AL_CONE_OUTER_ANGLE:
1585 case AL_PITCH:
1586 case AL_GAIN:
1587 case AL_MIN_GAIN:
1588 case AL_MAX_GAIN:
1589 case AL_REFERENCE_DISTANCE:
1590 case AL_ROLLOFF_FACTOR:
1591 case AL_CONE_OUTER_GAIN:
1592 case AL_MAX_DISTANCE:
1593 case AL_SEC_OFFSET:
1594 case AL_SAMPLE_OFFSET:
1595 case AL_BYTE_OFFSET:
1596 case AL_DOPPLER_FACTOR:
1597 case AL_AIR_ABSORPTION_FACTOR:
1598 case AL_ROOM_ROLLOFF_FACTOR:
1599 case AL_CONE_OUTER_GAINHF:
1600 case AL_SOURCE_RADIUS:
1601 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1602 *values = (ALint64)dvals[0];
1603 return err;
1605 /* 3x float/double */
1606 case AL_POSITION:
1607 case AL_VELOCITY:
1608 case AL_DIRECTION:
1609 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1611 values[0] = (ALint64)dvals[0];
1612 values[1] = (ALint64)dvals[1];
1613 values[2] = (ALint64)dvals[2];
1615 return err;
1617 /* 6x float/double */
1618 case AL_ORIENTATION:
1619 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1621 values[0] = (ALint64)dvals[0];
1622 values[1] = (ALint64)dvals[1];
1623 values[2] = (ALint64)dvals[2];
1624 values[3] = (ALint64)dvals[3];
1625 values[4] = (ALint64)dvals[4];
1626 values[5] = (ALint64)dvals[5];
1628 return err;
1630 /* 1x int */
1631 case AL_SOURCE_RELATIVE:
1632 case AL_LOOPING:
1633 case AL_SOURCE_STATE:
1634 case AL_BUFFERS_QUEUED:
1635 case AL_BUFFERS_PROCESSED:
1636 case AL_SOURCE_TYPE:
1637 case AL_DIRECT_FILTER_GAINHF_AUTO:
1638 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1639 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1640 case AL_DIRECT_CHANNELS_SOFT:
1641 case AL_DISTANCE_MODEL:
1642 case AL_SOURCE_RESAMPLER_SOFT:
1643 case AL_SOURCE_SPATIALIZE_SOFT:
1644 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
1645 *values = ivals[0];
1646 return err;
1648 /* 1x uint */
1649 case AL_BUFFER:
1650 case AL_DIRECT_FILTER:
1651 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
1652 *values = (ALuint)ivals[0];
1653 return err;
1655 /* 3x uint */
1656 case AL_AUXILIARY_SEND_FILTER:
1657 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
1659 values[0] = (ALuint)ivals[0];
1660 values[1] = (ALuint)ivals[1];
1661 values[2] = (ALuint)ivals[2];
1663 return err;
1665 case AL_SEC_OFFSET_LATENCY_SOFT:
1666 case AL_SEC_OFFSET_CLOCK_SOFT:
1667 break; /* Double only */
1668 case AL_STEREO_ANGLES:
1669 break; /* Float/double only */
1672 ERR("Unexpected property: 0x%04x\n", prop);
1673 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x",
1674 prop);
1678 AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
1680 ALCcontext *context;
1681 ALsizei cur = 0;
1683 context = GetContextRef();
1684 if(!context) return;
1686 if(!(n >= 0))
1687 alSetError(context, AL_INVALID_VALUE, "Generating %d sources", n);
1688 else for(cur = 0;cur < n;cur++)
1690 ALsource *source = AllocSource(context);
1691 if(!source)
1693 alDeleteSources(cur, sources);
1694 break;
1696 sources[cur] = source->id;
1699 ALCcontext_DecRef(context);
1703 AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
1705 ALCcontext *context;
1706 ALsource *Source;
1707 ALsizei i;
1709 context = GetContextRef();
1710 if(!context) return;
1712 LockSourceList(context);
1713 if(!(n >= 0))
1714 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Deleting %d sources", n);
1716 /* Check that all Sources are valid */
1717 for(i = 0;i < n;i++)
1719 if(LookupSource(context, sources[i]) == NULL)
1720 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]);
1722 for(i = 0;i < n;i++)
1724 if((Source=LookupSource(context, sources[i])) != NULL)
1725 FreeSource(context, Source);
1728 done:
1729 UnlockSourceList(context);
1730 ALCcontext_DecRef(context);
1734 AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
1736 ALCcontext *context;
1737 ALboolean ret;
1739 context = GetContextRef();
1740 if(!context) return AL_FALSE;
1742 LockSourceList(context);
1743 ret = (LookupSource(context, source) ? AL_TRUE : AL_FALSE);
1744 UnlockSourceList(context);
1746 ALCcontext_DecRef(context);
1748 return ret;
1752 AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
1754 ALCcontext *Context;
1755 ALsource *Source;
1757 Context = GetContextRef();
1758 if(!Context) return;
1760 almtx_lock(&Context->PropLock);
1761 LockSourceList(Context);
1762 if((Source=LookupSource(Context, source)) == NULL)
1763 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1764 else if(!(FloatValsByProp(param) == 1))
1765 alSetError(Context, AL_INVALID_ENUM, "Invalid float property 0x%04x", param);
1766 else
1767 SetSourcefv(Source, Context, param, &value);
1768 UnlockSourceList(Context);
1769 almtx_unlock(&Context->PropLock);
1771 ALCcontext_DecRef(Context);
1774 AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
1776 ALCcontext *Context;
1777 ALsource *Source;
1779 Context = GetContextRef();
1780 if(!Context) return;
1782 almtx_lock(&Context->PropLock);
1783 LockSourceList(Context);
1784 if((Source=LookupSource(Context, source)) == NULL)
1785 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1786 else if(!(FloatValsByProp(param) == 3))
1787 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param);
1788 else
1790 ALfloat fvals[3] = { value1, value2, value3 };
1791 SetSourcefv(Source, Context, param, fvals);
1793 UnlockSourceList(Context);
1794 almtx_unlock(&Context->PropLock);
1796 ALCcontext_DecRef(Context);
1799 AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
1801 ALCcontext *Context;
1802 ALsource *Source;
1804 Context = GetContextRef();
1805 if(!Context) return;
1807 almtx_lock(&Context->PropLock);
1808 LockSourceList(Context);
1809 if((Source=LookupSource(Context, source)) == NULL)
1810 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1811 else if(!values)
1812 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
1813 else if(!(FloatValsByProp(param) > 0))
1814 alSetError(Context, AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param);
1815 else
1816 SetSourcefv(Source, Context, param, values);
1817 UnlockSourceList(Context);
1818 almtx_unlock(&Context->PropLock);
1820 ALCcontext_DecRef(Context);
1824 AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
1826 ALCcontext *Context;
1827 ALsource *Source;
1829 Context = GetContextRef();
1830 if(!Context) return;
1832 almtx_lock(&Context->PropLock);
1833 LockSourceList(Context);
1834 if((Source=LookupSource(Context, source)) == NULL)
1835 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1836 else if(!(DoubleValsByProp(param) == 1))
1837 alSetError(Context, AL_INVALID_ENUM, "Invalid double property 0x%04x", param);
1838 else
1840 ALfloat fval = (ALfloat)value;
1841 SetSourcefv(Source, Context, param, &fval);
1843 UnlockSourceList(Context);
1844 almtx_unlock(&Context->PropLock);
1846 ALCcontext_DecRef(Context);
1849 AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
1851 ALCcontext *Context;
1852 ALsource *Source;
1854 Context = GetContextRef();
1855 if(!Context) return;
1857 almtx_lock(&Context->PropLock);
1858 LockSourceList(Context);
1859 if((Source=LookupSource(Context, source)) == NULL)
1860 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1861 else if(!(DoubleValsByProp(param) == 3))
1862 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param);
1863 else
1865 ALfloat fvals[3] = { (ALfloat)value1, (ALfloat)value2, (ALfloat)value3 };
1866 SetSourcefv(Source, Context, param, fvals);
1868 UnlockSourceList(Context);
1869 almtx_unlock(&Context->PropLock);
1871 ALCcontext_DecRef(Context);
1874 AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
1876 ALCcontext *Context;
1877 ALsource *Source;
1878 ALint count;
1880 Context = GetContextRef();
1881 if(!Context) return;
1883 almtx_lock(&Context->PropLock);
1884 LockSourceList(Context);
1885 if((Source=LookupSource(Context, source)) == NULL)
1886 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1887 else if(!values)
1888 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
1889 else if(!((count=DoubleValsByProp(param)) > 0 && count <= 6))
1890 alSetError(Context, AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param);
1891 else
1893 ALfloat fvals[6];
1894 ALint i;
1896 for(i = 0;i < count;i++)
1897 fvals[i] = (ALfloat)values[i];
1898 SetSourcefv(Source, Context, param, fvals);
1900 UnlockSourceList(Context);
1901 almtx_unlock(&Context->PropLock);
1903 ALCcontext_DecRef(Context);
1907 AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
1909 ALCcontext *Context;
1910 ALsource *Source;
1912 Context = GetContextRef();
1913 if(!Context) return;
1915 almtx_lock(&Context->PropLock);
1916 LockSourceList(Context);
1917 if((Source=LookupSource(Context, source)) == NULL)
1918 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1919 else if(!(IntValsByProp(param) == 1))
1920 alSetError(Context, AL_INVALID_ENUM, "Invalid integer property 0x%04x", param);
1921 else
1922 SetSourceiv(Source, Context, param, &value);
1923 UnlockSourceList(Context);
1924 almtx_unlock(&Context->PropLock);
1926 ALCcontext_DecRef(Context);
1929 AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
1931 ALCcontext *Context;
1932 ALsource *Source;
1934 Context = GetContextRef();
1935 if(!Context) return;
1937 almtx_lock(&Context->PropLock);
1938 LockSourceList(Context);
1939 if((Source=LookupSource(Context, source)) == NULL)
1940 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1941 else if(!(IntValsByProp(param) == 3))
1942 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param);
1943 else
1945 ALint ivals[3] = { value1, value2, value3 };
1946 SetSourceiv(Source, Context, param, ivals);
1948 UnlockSourceList(Context);
1949 almtx_unlock(&Context->PropLock);
1951 ALCcontext_DecRef(Context);
1954 AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
1956 ALCcontext *Context;
1957 ALsource *Source;
1959 Context = GetContextRef();
1960 if(!Context) return;
1962 almtx_lock(&Context->PropLock);
1963 LockSourceList(Context);
1964 if((Source=LookupSource(Context, source)) == NULL)
1965 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1966 else if(!values)
1967 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
1968 else if(!(IntValsByProp(param) > 0))
1969 alSetError(Context, AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param);
1970 else
1971 SetSourceiv(Source, Context, param, values);
1972 UnlockSourceList(Context);
1973 almtx_unlock(&Context->PropLock);
1975 ALCcontext_DecRef(Context);
1979 AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
1981 ALCcontext *Context;
1982 ALsource *Source;
1984 Context = GetContextRef();
1985 if(!Context) return;
1987 almtx_lock(&Context->PropLock);
1988 LockSourceList(Context);
1989 if((Source=LookupSource(Context, source)) == NULL)
1990 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1991 else if(!(Int64ValsByProp(param) == 1))
1992 alSetError(Context, AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param);
1993 else
1994 SetSourcei64v(Source, Context, param, &value);
1995 UnlockSourceList(Context);
1996 almtx_unlock(&Context->PropLock);
1998 ALCcontext_DecRef(Context);
2001 AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
2003 ALCcontext *Context;
2004 ALsource *Source;
2006 Context = GetContextRef();
2007 if(!Context) return;
2009 almtx_lock(&Context->PropLock);
2010 LockSourceList(Context);
2011 if((Source=LookupSource(Context, source)) == NULL)
2012 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2013 else if(!(Int64ValsByProp(param) == 3))
2014 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param);
2015 else
2017 ALint64SOFT i64vals[3] = { value1, value2, value3 };
2018 SetSourcei64v(Source, Context, param, i64vals);
2020 UnlockSourceList(Context);
2021 almtx_unlock(&Context->PropLock);
2023 ALCcontext_DecRef(Context);
2026 AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
2028 ALCcontext *Context;
2029 ALsource *Source;
2031 Context = GetContextRef();
2032 if(!Context) return;
2034 almtx_lock(&Context->PropLock);
2035 LockSourceList(Context);
2036 if((Source=LookupSource(Context, source)) == NULL)
2037 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2038 else if(!values)
2039 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2040 else if(!(Int64ValsByProp(param) > 0))
2041 alSetError(Context, AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param);
2042 else
2043 SetSourcei64v(Source, Context, param, values);
2044 UnlockSourceList(Context);
2045 almtx_unlock(&Context->PropLock);
2047 ALCcontext_DecRef(Context);
2051 AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
2053 ALCcontext *Context;
2054 ALsource *Source;
2056 Context = GetContextRef();
2057 if(!Context) return;
2059 LockSourceList(Context);
2060 if((Source=LookupSource(Context, source)) == NULL)
2061 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2062 else if(!value)
2063 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2064 else if(!(FloatValsByProp(param) == 1))
2065 alSetError(Context, AL_INVALID_ENUM, "Invalid float property 0x%04x", param);
2066 else
2068 ALdouble dval;
2069 if(GetSourcedv(Source, Context, param, &dval))
2070 *value = (ALfloat)dval;
2072 UnlockSourceList(Context);
2074 ALCcontext_DecRef(Context);
2078 AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
2080 ALCcontext *Context;
2081 ALsource *Source;
2083 Context = GetContextRef();
2084 if(!Context) return;
2086 LockSourceList(Context);
2087 if((Source=LookupSource(Context, source)) == NULL)
2088 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2089 else if(!(value1 && value2 && value3))
2090 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2091 else if(!(FloatValsByProp(param) == 3))
2092 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param);
2093 else
2095 ALdouble dvals[3];
2096 if(GetSourcedv(Source, Context, param, dvals))
2098 *value1 = (ALfloat)dvals[0];
2099 *value2 = (ALfloat)dvals[1];
2100 *value3 = (ALfloat)dvals[2];
2103 UnlockSourceList(Context);
2105 ALCcontext_DecRef(Context);
2109 AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
2111 ALCcontext *Context;
2112 ALsource *Source;
2113 ALint count;
2115 Context = GetContextRef();
2116 if(!Context) return;
2118 LockSourceList(Context);
2119 if((Source=LookupSource(Context, source)) == NULL)
2120 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2121 else if(!values)
2122 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2123 else if(!((count=FloatValsByProp(param)) > 0 && count <= 6))
2124 alSetError(Context, AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param);
2125 else
2127 ALdouble dvals[6];
2128 if(GetSourcedv(Source, Context, param, dvals))
2130 ALint i;
2131 for(i = 0;i < count;i++)
2132 values[i] = (ALfloat)dvals[i];
2135 UnlockSourceList(Context);
2137 ALCcontext_DecRef(Context);
2141 AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
2143 ALCcontext *Context;
2144 ALsource *Source;
2146 Context = GetContextRef();
2147 if(!Context) return;
2149 LockSourceList(Context);
2150 if((Source=LookupSource(Context, source)) == NULL)
2151 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2152 else if(!value)
2153 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2154 else if(!(DoubleValsByProp(param) == 1))
2155 alSetError(Context, AL_INVALID_ENUM, "Invalid double property 0x%04x", param);
2156 else
2157 GetSourcedv(Source, Context, param, value);
2158 UnlockSourceList(Context);
2160 ALCcontext_DecRef(Context);
2163 AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
2165 ALCcontext *Context;
2166 ALsource *Source;
2168 Context = GetContextRef();
2169 if(!Context) return;
2171 LockSourceList(Context);
2172 if((Source=LookupSource(Context, source)) == NULL)
2173 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2174 else if(!(value1 && value2 && value3))
2175 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2176 else if(!(DoubleValsByProp(param) == 3))
2177 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param);
2178 else
2180 ALdouble dvals[3];
2181 if(GetSourcedv(Source, Context, param, dvals))
2183 *value1 = dvals[0];
2184 *value2 = dvals[1];
2185 *value3 = dvals[2];
2188 UnlockSourceList(Context);
2190 ALCcontext_DecRef(Context);
2193 AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
2195 ALCcontext *Context;
2196 ALsource *Source;
2198 Context = GetContextRef();
2199 if(!Context) return;
2201 LockSourceList(Context);
2202 if((Source=LookupSource(Context, source)) == NULL)
2203 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2204 else if(!values)
2205 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2206 else if(!(DoubleValsByProp(param) > 0))
2207 alSetError(Context, AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param);
2208 else
2209 GetSourcedv(Source, Context, param, values);
2210 UnlockSourceList(Context);
2212 ALCcontext_DecRef(Context);
2216 AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
2218 ALCcontext *Context;
2219 ALsource *Source;
2221 Context = GetContextRef();
2222 if(!Context) return;
2224 LockSourceList(Context);
2225 if((Source=LookupSource(Context, source)) == NULL)
2226 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2227 else if(!value)
2228 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2229 else if(!(IntValsByProp(param) == 1))
2230 alSetError(Context, AL_INVALID_ENUM, "Invalid integer property 0x%04x", param);
2231 else
2232 GetSourceiv(Source, Context, param, value);
2233 UnlockSourceList(Context);
2235 ALCcontext_DecRef(Context);
2239 AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
2241 ALCcontext *Context;
2242 ALsource *Source;
2244 Context = GetContextRef();
2245 if(!Context) return;
2247 LockSourceList(Context);
2248 if((Source=LookupSource(Context, source)) == NULL)
2249 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2250 else if(!(value1 && value2 && value3))
2251 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2252 else if(!(IntValsByProp(param) == 3))
2253 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param);
2254 else
2256 ALint ivals[3];
2257 if(GetSourceiv(Source, Context, param, ivals))
2259 *value1 = ivals[0];
2260 *value2 = ivals[1];
2261 *value3 = ivals[2];
2264 UnlockSourceList(Context);
2266 ALCcontext_DecRef(Context);
2270 AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
2272 ALCcontext *Context;
2273 ALsource *Source;
2275 Context = GetContextRef();
2276 if(!Context) return;
2278 LockSourceList(Context);
2279 if((Source=LookupSource(Context, source)) == NULL)
2280 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2281 else if(!values)
2282 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2283 else if(!(IntValsByProp(param) > 0))
2284 alSetError(Context, AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param);
2285 else
2286 GetSourceiv(Source, Context, param, values);
2287 UnlockSourceList(Context);
2289 ALCcontext_DecRef(Context);
2293 AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
2295 ALCcontext *Context;
2296 ALsource *Source;
2298 Context = GetContextRef();
2299 if(!Context) return;
2301 LockSourceList(Context);
2302 if((Source=LookupSource(Context, source)) == NULL)
2303 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2304 else if(!value)
2305 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2306 else if(!(Int64ValsByProp(param) == 1))
2307 alSetError(Context, AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param);
2308 else
2309 GetSourcei64v(Source, Context, param, value);
2310 UnlockSourceList(Context);
2312 ALCcontext_DecRef(Context);
2315 AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
2317 ALCcontext *Context;
2318 ALsource *Source;
2320 Context = GetContextRef();
2321 if(!Context) return;
2323 LockSourceList(Context);
2324 if((Source=LookupSource(Context, source)) == NULL)
2325 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2326 else if(!(value1 && value2 && value3))
2327 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2328 else if(!(Int64ValsByProp(param) == 3))
2329 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param);
2330 else
2332 ALint64 i64vals[3];
2333 if(GetSourcei64v(Source, Context, param, i64vals))
2335 *value1 = i64vals[0];
2336 *value2 = i64vals[1];
2337 *value3 = i64vals[2];
2340 UnlockSourceList(Context);
2342 ALCcontext_DecRef(Context);
2345 AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
2347 ALCcontext *Context;
2348 ALsource *Source;
2350 Context = GetContextRef();
2351 if(!Context) return;
2353 LockSourceList(Context);
2354 if((Source=LookupSource(Context, source)) == NULL)
2355 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2356 else if(!values)
2357 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2358 else if(!(Int64ValsByProp(param) > 0))
2359 alSetError(Context, AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param);
2360 else
2361 GetSourcei64v(Source, Context, param, values);
2362 UnlockSourceList(Context);
2364 ALCcontext_DecRef(Context);
2368 AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source)
2370 alSourcePlayv(1, &source);
2372 AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
2374 ALCcontext *context;
2375 ALCdevice *device;
2376 ALsource *source;
2377 ALvoice *voice;
2378 ALsizei i, j;
2380 context = GetContextRef();
2381 if(!context) return;
2383 LockSourceList(context);
2384 if(!(n >= 0))
2385 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Playing %d sources", n);
2386 for(i = 0;i < n;i++)
2388 if(!LookupSource(context, sources[i]))
2389 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]);
2392 device = context->Device;
2393 ALCdevice_Lock(device);
2394 /* If the device is disconnected, go right to stopped. */
2395 if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
2397 /* TODO: Send state change event? */
2398 for(i = 0;i < n;i++)
2400 source = LookupSource(context, sources[i]);
2401 source->OffsetType = AL_NONE;
2402 source->Offset = 0.0;
2403 source->state = AL_STOPPED;
2405 ALCdevice_Unlock(device);
2406 goto done;
2409 while(n > context->MaxVoices-context->VoiceCount)
2411 ALsizei newcount = context->MaxVoices << 1;
2412 if(context->MaxVoices >= newcount)
2414 ALCdevice_Unlock(device);
2415 SETERR_GOTO(context, AL_OUT_OF_MEMORY, done,
2416 "Overflow increasing voice count %d -> %d", context->MaxVoices, newcount);
2418 AllocateVoices(context, newcount, device->NumAuxSends);
2421 for(i = 0;i < n;i++)
2423 ALbufferlistitem *BufferList;
2424 ALbuffer *buffer = NULL;
2425 bool start_fading = false;
2426 ALint vidx = -1;
2427 ALsizei s;
2429 source = LookupSource(context, sources[i]);
2430 /* Check that there is a queue containing at least one valid, non zero
2431 * length Buffer.
2433 BufferList = source->queue;
2434 while(BufferList)
2436 ALsizei b;
2437 for(b = 0;b < BufferList->num_buffers;b++)
2439 buffer = BufferList->buffers[b];
2440 if(buffer && buffer->SampleLen > 0) break;
2442 if(buffer && buffer->SampleLen > 0) break;
2443 BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
2446 /* If there's nothing to play, go right to stopped. */
2447 if(UNLIKELY(!BufferList))
2449 /* NOTE: A source without any playable buffers should not have an
2450 * ALvoice since it shouldn't be in a playing or paused state. So
2451 * there's no need to look up its voice and clear the source.
2453 ALenum oldstate = GetSourceState(source, NULL);
2454 source->OffsetType = AL_NONE;
2455 source->Offset = 0.0;
2456 if(oldstate != AL_STOPPED)
2458 source->state = AL_STOPPED;
2459 SendStateChangeEvent(context, source->id, AL_STOPPED);
2461 continue;
2464 voice = GetSourceVoice(source, context);
2465 switch(GetSourceState(source, voice))
2467 case AL_PLAYING:
2468 assert(voice != NULL);
2469 /* A source that's already playing is restarted from the beginning. */
2470 ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_relaxed);
2471 ATOMIC_STORE(&voice->position, 0, almemory_order_relaxed);
2472 ATOMIC_STORE(&voice->position_fraction, 0, almemory_order_release);
2473 continue;
2475 case AL_PAUSED:
2476 assert(voice != NULL);
2477 /* A source that's paused simply resumes. */
2478 ATOMIC_STORE(&voice->Playing, true, almemory_order_release);
2479 source->state = AL_PLAYING;
2480 SendStateChangeEvent(context, source->id, AL_PLAYING);
2481 continue;
2483 default:
2484 break;
2487 /* Make sure this source isn't already active, and if not, look for an
2488 * unused voice to put it in.
2490 assert(voice == NULL);
2491 for(j = 0;j < context->VoiceCount;j++)
2493 if(ATOMIC_LOAD(&context->Voices[j]->Source, almemory_order_acquire) == NULL)
2495 vidx = j;
2496 break;
2499 if(vidx == -1)
2500 vidx = context->VoiceCount++;
2501 voice = context->Voices[vidx];
2502 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
2504 ATOMIC_FLAG_TEST_AND_SET(&source->PropsClean, almemory_order_acquire);
2505 UpdateSourceProps(source, voice, device->NumAuxSends, context);
2507 /* A source that's not playing or paused has any offset applied when it
2508 * starts playing.
2510 if(source->Looping)
2511 ATOMIC_STORE(&voice->loop_buffer, source->queue, almemory_order_relaxed);
2512 else
2513 ATOMIC_STORE(&voice->loop_buffer, NULL, almemory_order_relaxed);
2514 ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_relaxed);
2515 ATOMIC_STORE(&voice->position, 0, almemory_order_relaxed);
2516 ATOMIC_STORE(&voice->position_fraction, 0, almemory_order_relaxed);
2517 if(source->OffsetType != AL_NONE)
2519 ApplyOffset(source, voice);
2520 start_fading = ATOMIC_LOAD(&voice->position, almemory_order_relaxed) != 0 ||
2521 ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed) != 0 ||
2522 ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed) != BufferList;
2525 voice->NumChannels = ChannelsFromFmt(buffer->FmtChannels);
2526 voice->SampleSize = BytesFromFmt(buffer->FmtType);
2528 /* Clear previous samples. */
2529 memset(voice->PrevSamples, 0, sizeof(voice->PrevSamples));
2531 /* Clear the stepping value so the mixer knows not to mix this until
2532 * the update gets applied.
2534 voice->Step = 0;
2536 voice->Flags = start_fading ? VOICE_IS_FADING : 0;
2537 if(source->SourceType == AL_STATIC) voice->Flags |= VOICE_IS_STATIC;
2538 memset(voice->Direct.Params, 0, sizeof(voice->Direct.Params[0])*voice->NumChannels);
2539 for(s = 0;s < device->NumAuxSends;s++)
2540 memset(voice->Send[s].Params, 0, sizeof(voice->Send[s].Params[0])*voice->NumChannels);
2541 if(device->AvgSpeakerDist > 0.0f)
2543 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
2544 (device->AvgSpeakerDist * device->Frequency);
2545 for(j = 0;j < voice->NumChannels;j++)
2546 NfcFilterCreate(&voice->Direct.Params[j].NFCtrlFilter, 0.0f, w1);
2549 ATOMIC_STORE(&voice->Source, source, almemory_order_relaxed);
2550 ATOMIC_STORE(&voice->Playing, true, almemory_order_release);
2551 source->state = AL_PLAYING;
2552 source->VoiceIdx = vidx;
2554 SendStateChangeEvent(context, source->id, AL_PLAYING);
2556 ALCdevice_Unlock(device);
2558 done:
2559 UnlockSourceList(context);
2560 ALCcontext_DecRef(context);
2563 AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source)
2565 alSourcePausev(1, &source);
2567 AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
2569 ALCcontext *context;
2570 ALCdevice *device;
2571 ALsource *source;
2572 ALvoice *voice;
2573 ALsizei i;
2575 context = GetContextRef();
2576 if(!context) return;
2578 LockSourceList(context);
2579 if(!(n >= 0))
2580 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Pausing %d sources", n);
2581 for(i = 0;i < n;i++)
2583 if(!LookupSource(context, sources[i]))
2584 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]);
2587 device = context->Device;
2588 ALCdevice_Lock(device);
2589 for(i = 0;i < n;i++)
2591 source = LookupSource(context, sources[i]);
2592 if((voice=GetSourceVoice(source, context)) != NULL)
2593 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
2594 if(GetSourceState(source, voice) == AL_PLAYING)
2596 source->state = AL_PAUSED;
2597 SendStateChangeEvent(context, source->id, AL_PAUSED);
2600 ALCdevice_Unlock(device);
2602 done:
2603 UnlockSourceList(context);
2604 ALCcontext_DecRef(context);
2607 AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source)
2609 alSourceStopv(1, &source);
2611 AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
2613 ALCcontext *context;
2614 ALCdevice *device;
2615 ALsource *source;
2616 ALvoice *voice;
2617 ALsizei i;
2619 context = GetContextRef();
2620 if(!context) return;
2622 LockSourceList(context);
2623 if(!(n >= 0))
2624 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Stopping %d sources", n);
2625 for(i = 0;i < n;i++)
2627 if(!LookupSource(context, sources[i]))
2628 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]);
2631 device = context->Device;
2632 ALCdevice_Lock(device);
2633 for(i = 0;i < n;i++)
2635 ALenum oldstate;
2636 source = LookupSource(context, sources[i]);
2637 if((voice=GetSourceVoice(source, context)) != NULL)
2639 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
2640 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
2641 voice = NULL;
2643 oldstate = GetSourceState(source, voice);
2644 if(oldstate != AL_INITIAL && oldstate != AL_STOPPED)
2646 source->state = AL_STOPPED;
2647 SendStateChangeEvent(context, source->id, AL_STOPPED);
2649 source->OffsetType = AL_NONE;
2650 source->Offset = 0.0;
2652 ALCdevice_Unlock(device);
2654 done:
2655 UnlockSourceList(context);
2656 ALCcontext_DecRef(context);
2659 AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source)
2661 alSourceRewindv(1, &source);
2663 AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
2665 ALCcontext *context;
2666 ALCdevice *device;
2667 ALsource *source;
2668 ALvoice *voice;
2669 ALsizei i;
2671 context = GetContextRef();
2672 if(!context) return;
2674 LockSourceList(context);
2675 if(!(n >= 0))
2676 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Rewinding %d sources", n);
2677 for(i = 0;i < n;i++)
2679 if(!LookupSource(context, sources[i]))
2680 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]);
2683 device = context->Device;
2684 ALCdevice_Lock(device);
2685 for(i = 0;i < n;i++)
2687 source = LookupSource(context, sources[i]);
2688 if((voice=GetSourceVoice(source, context)) != NULL)
2690 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
2691 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
2692 voice = NULL;
2694 if(GetSourceState(source, voice) != AL_INITIAL)
2696 source->state = AL_INITIAL;
2697 SendStateChangeEvent(context, source->id, AL_INITIAL);
2699 source->OffsetType = AL_NONE;
2700 source->Offset = 0.0;
2702 ALCdevice_Unlock(device);
2704 done:
2705 UnlockSourceList(context);
2706 ALCcontext_DecRef(context);
2710 AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
2712 ALCdevice *device;
2713 ALCcontext *context;
2714 ALsource *source;
2715 ALsizei i;
2716 ALbufferlistitem *BufferListStart;
2717 ALbufferlistitem *BufferList;
2718 ALbuffer *BufferFmt = NULL;
2720 if(nb == 0)
2721 return;
2723 context = GetContextRef();
2724 if(!context) return;
2726 device = context->Device;
2728 LockSourceList(context);
2729 if(!(nb >= 0))
2730 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Queueing %d buffers", nb);
2731 if((source=LookupSource(context, src)) == NULL)
2732 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", src);
2734 if(source->SourceType == AL_STATIC)
2736 /* Can't queue on a Static Source */
2737 SETERR_GOTO(context, AL_INVALID_OPERATION, done, "Queueing onto static source %u", src);
2740 /* Check for a valid Buffer, for its frequency and format */
2741 BufferList = source->queue;
2742 while(BufferList)
2744 for(i = 0;i < BufferList->num_buffers;i++)
2746 if((BufferFmt=BufferList->buffers[i]) != NULL)
2747 break;
2749 if(BufferFmt) break;
2750 BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
2753 LockBufferList(device);
2754 BufferListStart = NULL;
2755 BufferList = NULL;
2756 for(i = 0;i < nb;i++)
2758 ALbuffer *buffer = NULL;
2759 if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == NULL)
2760 SETERR_GOTO(context, AL_INVALID_NAME, buffer_error, "Queueing invalid buffer ID %u",
2761 buffers[i]);
2763 if(!BufferListStart)
2765 BufferListStart = al_calloc(DEF_ALIGN,
2766 FAM_SIZE(ALbufferlistitem, buffers, 1));
2767 BufferList = BufferListStart;
2769 else
2771 ALbufferlistitem *item = al_calloc(DEF_ALIGN,
2772 FAM_SIZE(ALbufferlistitem, buffers, 1));
2773 ATOMIC_STORE(&BufferList->next, item, almemory_order_relaxed);
2774 BufferList = item;
2776 ATOMIC_INIT(&BufferList->next, NULL);
2777 BufferList->num_buffers = 1;
2778 BufferList->buffers[0] = buffer;
2779 if(!buffer) continue;
2781 IncrementRef(&buffer->ref);
2783 if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
2784 SETERR_GOTO(context, AL_INVALID_OPERATION, buffer_error,
2785 "Queueing non-persistently mapped buffer %u", buffer->id);
2787 if(BufferFmt == NULL)
2788 BufferFmt = buffer;
2789 else if(BufferFmt->Frequency != buffer->Frequency ||
2790 BufferFmt->FmtChannels != buffer->FmtChannels ||
2791 BufferFmt->OriginalType != buffer->OriginalType)
2793 alSetError(context, AL_INVALID_OPERATION, "Queueing buffer with mismatched format");
2795 buffer_error:
2796 /* A buffer failed (invalid ID or format), so unlock and release
2797 * each buffer we had. */
2798 while(BufferListStart)
2800 ALbufferlistitem *next = ATOMIC_LOAD(&BufferListStart->next,
2801 almemory_order_relaxed);
2802 for(i = 0;i < BufferListStart->num_buffers;i++)
2804 if((buffer=BufferListStart->buffers[i]) != NULL)
2805 DecrementRef(&buffer->ref);
2807 al_free(BufferListStart);
2808 BufferListStart = next;
2810 UnlockBufferList(device);
2811 goto done;
2814 /* All buffers good. */
2815 UnlockBufferList(device);
2817 /* Source is now streaming */
2818 source->SourceType = AL_STREAMING;
2820 if(!(BufferList=source->queue))
2821 source->queue = BufferListStart;
2822 else
2824 ALbufferlistitem *next;
2825 while((next=ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed)) != NULL)
2826 BufferList = next;
2827 ATOMIC_STORE(&BufferList->next, BufferListStart, almemory_order_release);
2830 done:
2831 UnlockSourceList(context);
2832 ALCcontext_DecRef(context);
2835 AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
2837 ALCcontext *context;
2838 ALsource *source;
2839 ALbufferlistitem *BufferList;
2840 ALbufferlistitem *Current;
2841 ALvoice *voice;
2842 ALsizei i;
2844 context = GetContextRef();
2845 if(!context) return;
2847 LockSourceList(context);
2848 if(!(nb >= 0))
2849 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing %d buffers", nb);
2850 if((source=LookupSource(context, src)) == NULL)
2851 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", src);
2853 /* Nothing to unqueue. */
2854 if(nb == 0) goto done;
2856 if(source->Looping)
2857 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing from looping source %u", src);
2858 if(source->SourceType != AL_STREAMING)
2859 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing from a non-streaming source %u",
2860 src);
2862 /* Make sure enough buffers have been processed to unqueue. */
2863 BufferList = source->queue;
2864 Current = NULL;
2865 if((voice=GetSourceVoice(source, context)) != NULL)
2866 Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
2867 else if(source->state == AL_INITIAL)
2868 Current = BufferList;
2869 if(BufferList == Current)
2870 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing pending buffers");
2872 i = BufferList->num_buffers;
2873 while(i < nb)
2875 /* If the next bufferlist to check is NULL or is the current one, it's
2876 * trying to unqueue pending buffers.
2878 ALbufferlistitem *next = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
2879 if(!next || next == Current)
2880 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing pending buffers");
2881 BufferList = next;
2883 i += BufferList->num_buffers;
2886 while(nb > 0)
2888 ALbufferlistitem *head = source->queue;
2889 ALbufferlistitem *next = ATOMIC_LOAD(&head->next, almemory_order_relaxed);
2890 for(i = 0;i < head->num_buffers && nb > 0;i++,nb--)
2892 ALbuffer *buffer = head->buffers[i];
2893 if(!buffer)
2894 *(buffers++) = 0;
2895 else
2897 *(buffers++) = buffer->id;
2898 DecrementRef(&buffer->ref);
2901 if(i < head->num_buffers)
2903 /* This head has some buffers left over, so move them to the front
2904 * and update the count.
2906 ALsizei j = 0;
2907 while(i < head->num_buffers)
2908 head->buffers[j++] = head->buffers[i++];
2909 head->num_buffers = j;
2910 break;
2913 /* Otherwise, free this item and set the source queue head to the next
2914 * one.
2916 al_free(head);
2917 source->queue = next;
2920 done:
2921 UnlockSourceList(context);
2922 ALCcontext_DecRef(context);
2926 static void InitSourceParams(ALsource *Source, ALsizei num_sends)
2928 ALsizei i;
2930 Source->InnerAngle = 360.0f;
2931 Source->OuterAngle = 360.0f;
2932 Source->Pitch = 1.0f;
2933 Source->Position[0] = 0.0f;
2934 Source->Position[1] = 0.0f;
2935 Source->Position[2] = 0.0f;
2936 Source->Velocity[0] = 0.0f;
2937 Source->Velocity[1] = 0.0f;
2938 Source->Velocity[2] = 0.0f;
2939 Source->Direction[0] = 0.0f;
2940 Source->Direction[1] = 0.0f;
2941 Source->Direction[2] = 0.0f;
2942 Source->Orientation[0][0] = 0.0f;
2943 Source->Orientation[0][1] = 0.0f;
2944 Source->Orientation[0][2] = -1.0f;
2945 Source->Orientation[1][0] = 0.0f;
2946 Source->Orientation[1][1] = 1.0f;
2947 Source->Orientation[1][2] = 0.0f;
2948 Source->RefDistance = 1.0f;
2949 Source->MaxDistance = FLT_MAX;
2950 Source->RolloffFactor = 1.0f;
2951 Source->Gain = 1.0f;
2952 Source->MinGain = 0.0f;
2953 Source->MaxGain = 1.0f;
2954 Source->OuterGain = 0.0f;
2955 Source->OuterGainHF = 1.0f;
2957 Source->DryGainHFAuto = AL_TRUE;
2958 Source->WetGainAuto = AL_TRUE;
2959 Source->WetGainHFAuto = AL_TRUE;
2960 Source->AirAbsorptionFactor = 0.0f;
2961 Source->RoomRolloffFactor = 0.0f;
2962 Source->DopplerFactor = 1.0f;
2963 Source->HeadRelative = AL_FALSE;
2964 Source->Looping = AL_FALSE;
2965 Source->DistanceModel = DefaultDistanceModel;
2966 Source->Resampler = ResamplerDefault;
2967 Source->DirectChannels = AL_FALSE;
2968 Source->Spatialize = SpatializeAuto;
2970 Source->StereoPan[0] = DEG2RAD( 30.0f);
2971 Source->StereoPan[1] = DEG2RAD(-30.0f);
2973 Source->Radius = 0.0f;
2975 Source->Direct.Gain = 1.0f;
2976 Source->Direct.GainHF = 1.0f;
2977 Source->Direct.HFReference = LOWPASSFREQREF;
2978 Source->Direct.GainLF = 1.0f;
2979 Source->Direct.LFReference = HIGHPASSFREQREF;
2980 Source->Send = al_calloc(16, num_sends*sizeof(Source->Send[0]));
2981 for(i = 0;i < num_sends;i++)
2983 Source->Send[i].Slot = NULL;
2984 Source->Send[i].Gain = 1.0f;
2985 Source->Send[i].GainHF = 1.0f;
2986 Source->Send[i].HFReference = LOWPASSFREQREF;
2987 Source->Send[i].GainLF = 1.0f;
2988 Source->Send[i].LFReference = HIGHPASSFREQREF;
2991 Source->Offset = 0.0;
2992 Source->OffsetType = AL_NONE;
2993 Source->SourceType = AL_UNDETERMINED;
2994 Source->state = AL_INITIAL;
2996 Source->queue = NULL;
2998 /* No way to do an 'init' here, so just test+set with relaxed ordering and
2999 * ignore the test.
3001 ATOMIC_FLAG_TEST_AND_SET(&Source->PropsClean, almemory_order_relaxed);
3003 Source->VoiceIdx = -1;
3006 static void DeinitSource(ALsource *source, ALsizei num_sends)
3008 ALbufferlistitem *BufferList;
3009 ALsizei i;
3011 BufferList = source->queue;
3012 while(BufferList != NULL)
3014 ALbufferlistitem *next = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
3015 for(i = 0;i < BufferList->num_buffers;i++)
3017 if(BufferList->buffers[i] != NULL)
3018 DecrementRef(&BufferList->buffers[i]->ref);
3020 al_free(BufferList);
3021 BufferList = next;
3023 source->queue = NULL;
3025 if(source->Send)
3027 for(i = 0;i < num_sends;i++)
3029 if(source->Send[i].Slot)
3030 DecrementRef(&source->Send[i].Slot->ref);
3031 source->Send[i].Slot = NULL;
3033 al_free(source->Send);
3034 source->Send = NULL;
3038 static void UpdateSourceProps(ALsource *source, ALvoice *voice, ALsizei num_sends, ALCcontext *context)
3040 struct ALvoiceProps *props;
3041 ALsizei i;
3043 /* Get an unused property container, or allocate a new one as needed. */
3044 props = ATOMIC_LOAD(&context->FreeVoiceProps, almemory_order_acquire);
3045 if(!props)
3046 props = al_calloc(16, FAM_SIZE(struct ALvoiceProps, Send, num_sends));
3047 else
3049 struct ALvoiceProps *next;
3050 do {
3051 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
3052 } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&context->FreeVoiceProps, &props, next,
3053 almemory_order_acq_rel, almemory_order_acquire) == 0);
3056 /* Copy in current property values. */
3057 props->Pitch = source->Pitch;
3058 props->Gain = source->Gain;
3059 props->OuterGain = source->OuterGain;
3060 props->MinGain = source->MinGain;
3061 props->MaxGain = source->MaxGain;
3062 props->InnerAngle = source->InnerAngle;
3063 props->OuterAngle = source->OuterAngle;
3064 props->RefDistance = source->RefDistance;
3065 props->MaxDistance = source->MaxDistance;
3066 props->RolloffFactor = source->RolloffFactor;
3067 for(i = 0;i < 3;i++)
3068 props->Position[i] = source->Position[i];
3069 for(i = 0;i < 3;i++)
3070 props->Velocity[i] = source->Velocity[i];
3071 for(i = 0;i < 3;i++)
3072 props->Direction[i] = source->Direction[i];
3073 for(i = 0;i < 2;i++)
3075 ALsizei j;
3076 for(j = 0;j < 3;j++)
3077 props->Orientation[i][j] = source->Orientation[i][j];
3079 props->HeadRelative = source->HeadRelative;
3080 props->DistanceModel = source->DistanceModel;
3081 props->Resampler = source->Resampler;
3082 props->DirectChannels = source->DirectChannels;
3083 props->SpatializeMode = source->Spatialize;
3085 props->DryGainHFAuto = source->DryGainHFAuto;
3086 props->WetGainAuto = source->WetGainAuto;
3087 props->WetGainHFAuto = source->WetGainHFAuto;
3088 props->OuterGainHF = source->OuterGainHF;
3090 props->AirAbsorptionFactor = source->AirAbsorptionFactor;
3091 props->RoomRolloffFactor = source->RoomRolloffFactor;
3092 props->DopplerFactor = source->DopplerFactor;
3094 props->StereoPan[0] = source->StereoPan[0];
3095 props->StereoPan[1] = source->StereoPan[1];
3097 props->Radius = source->Radius;
3099 props->Direct.Gain = source->Direct.Gain;
3100 props->Direct.GainHF = source->Direct.GainHF;
3101 props->Direct.HFReference = source->Direct.HFReference;
3102 props->Direct.GainLF = source->Direct.GainLF;
3103 props->Direct.LFReference = source->Direct.LFReference;
3105 for(i = 0;i < num_sends;i++)
3107 props->Send[i].Slot = source->Send[i].Slot;
3108 props->Send[i].Gain = source->Send[i].Gain;
3109 props->Send[i].GainHF = source->Send[i].GainHF;
3110 props->Send[i].HFReference = source->Send[i].HFReference;
3111 props->Send[i].GainLF = source->Send[i].GainLF;
3112 props->Send[i].LFReference = source->Send[i].LFReference;
3115 /* Set the new container for updating internal parameters. */
3116 props = ATOMIC_EXCHANGE_PTR(&voice->Update, props, almemory_order_acq_rel);
3117 if(props)
3119 /* If there was an unused update container, put it back in the
3120 * freelist.
3122 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
3126 void UpdateAllSourceProps(ALCcontext *context)
3128 ALsizei num_sends = context->Device->NumAuxSends;
3129 ALsizei pos;
3131 for(pos = 0;pos < context->VoiceCount;pos++)
3133 ALvoice *voice = context->Voices[pos];
3134 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
3135 if(source && !ATOMIC_FLAG_TEST_AND_SET(&source->PropsClean, almemory_order_acq_rel))
3136 UpdateSourceProps(source, voice, num_sends, context);
3141 /* GetSourceSampleOffset
3143 * Gets the current read offset for the given Source, in 32.32 fixed-point
3144 * samples. The offset is relative to the start of the queue (not the start of
3145 * the current buffer).
3147 static ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime)
3149 ALCdevice *device = context->Device;
3150 const ALbufferlistitem *Current;
3151 ALuint64 readPos;
3152 ALuint refcount;
3153 ALvoice *voice;
3155 do {
3156 Current = NULL;
3157 readPos = 0;
3158 while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
3159 althrd_yield();
3160 *clocktime = GetDeviceClockTime(device);
3162 voice = GetSourceVoice(Source, context);
3163 if(voice)
3165 Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
3167 readPos = (ALuint64)ATOMIC_LOAD(&voice->position, almemory_order_relaxed) << 32;
3168 readPos |= (ALuint64)ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed) <<
3169 (32-FRACTIONBITS);
3171 ATOMIC_THREAD_FENCE(almemory_order_acquire);
3172 } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
3174 if(voice)
3176 const ALbufferlistitem *BufferList = Source->queue;
3177 while(BufferList && BufferList != Current)
3179 ALsizei max_len = 0;
3180 ALsizei i;
3182 for(i = 0;i < BufferList->num_buffers;i++)
3184 ALbuffer *buffer = BufferList->buffers[i];
3185 if(buffer) max_len = maxi(max_len, buffer->SampleLen);
3187 readPos += (ALuint64)max_len << 32;
3188 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
3189 almemory_order_relaxed);
3191 readPos = minu64(readPos, U64(0x7fffffffffffffff));
3194 return (ALint64)readPos;
3197 /* GetSourceSecOffset
3199 * Gets the current read offset for the given Source, in seconds. The offset is
3200 * relative to the start of the queue (not the start of the current buffer).
3202 static ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime)
3204 ALCdevice *device = context->Device;
3205 const ALbufferlistitem *Current;
3206 ALuint64 readPos;
3207 ALuint refcount;
3208 ALdouble offset;
3209 ALvoice *voice;
3211 do {
3212 Current = NULL;
3213 readPos = 0;
3214 while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
3215 althrd_yield();
3216 *clocktime = GetDeviceClockTime(device);
3218 voice = GetSourceVoice(Source, context);
3219 if(voice)
3221 Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
3223 readPos = (ALuint64)ATOMIC_LOAD(&voice->position, almemory_order_relaxed) <<
3224 FRACTIONBITS;
3225 readPos |= ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
3227 ATOMIC_THREAD_FENCE(almemory_order_acquire);
3228 } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
3230 offset = 0.0;
3231 if(voice)
3233 const ALbufferlistitem *BufferList = Source->queue;
3234 const ALbuffer *BufferFmt = NULL;
3235 while(BufferList && BufferList != Current)
3237 ALsizei max_len = 0;
3238 ALsizei i;
3240 for(i = 0;i < BufferList->num_buffers;i++)
3242 ALbuffer *buffer = BufferList->buffers[i];
3243 if(buffer)
3245 if(!BufferFmt) BufferFmt = buffer;
3246 max_len = maxi(max_len, buffer->SampleLen);
3249 readPos += (ALuint64)max_len << FRACTIONBITS;
3250 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
3251 almemory_order_relaxed);
3254 while(BufferList && !BufferFmt)
3256 ALsizei i;
3257 for(i = 0;i < BufferList->num_buffers && !BufferFmt;i++)
3258 BufferFmt = BufferList->buffers[i];
3259 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
3260 almemory_order_relaxed);
3262 assert(BufferFmt != NULL);
3264 offset = (ALdouble)readPos / (ALdouble)FRACTIONONE /
3265 (ALdouble)BufferFmt->Frequency;
3268 return offset;
3271 /* GetSourceOffset
3273 * Gets the current read offset for the given Source, in the appropriate format
3274 * (Bytes, Samples or Seconds). The offset is relative to the start of the
3275 * queue (not the start of the current buffer).
3277 static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
3279 ALCdevice *device = context->Device;
3280 const ALbufferlistitem *Current;
3281 ALuint readPos;
3282 ALsizei readPosFrac;
3283 ALuint refcount;
3284 ALdouble offset;
3285 ALvoice *voice;
3287 do {
3288 Current = NULL;
3289 readPos = readPosFrac = 0;
3290 while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
3291 althrd_yield();
3292 voice = GetSourceVoice(Source, context);
3293 if(voice)
3295 Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
3297 readPos = ATOMIC_LOAD(&voice->position, almemory_order_relaxed);
3298 readPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
3300 ATOMIC_THREAD_FENCE(almemory_order_acquire);
3301 } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
3303 offset = 0.0;
3304 if(voice)
3306 const ALbufferlistitem *BufferList = Source->queue;
3307 const ALbuffer *BufferFmt = NULL;
3308 ALboolean readFin = AL_FALSE;
3309 ALuint totalBufferLen = 0;
3311 while(BufferList != NULL)
3313 ALsizei max_len = 0;
3314 ALsizei i;
3316 readFin = readFin || (BufferList == Current);
3317 for(i = 0;i < BufferList->num_buffers;i++)
3319 ALbuffer *buffer = BufferList->buffers[i];
3320 if(buffer)
3322 if(!BufferFmt) BufferFmt = buffer;
3323 max_len = maxi(max_len, buffer->SampleLen);
3326 totalBufferLen += max_len;
3327 if(!readFin) readPos += max_len;
3329 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
3330 almemory_order_relaxed);
3332 assert(BufferFmt != NULL);
3334 if(Source->Looping)
3335 readPos %= totalBufferLen;
3336 else
3338 /* Wrap back to 0 */
3339 if(readPos >= totalBufferLen)
3340 readPos = readPosFrac = 0;
3343 offset = 0.0;
3344 switch(name)
3346 case AL_SEC_OFFSET:
3347 offset = (readPos + (ALdouble)readPosFrac/FRACTIONONE) / BufferFmt->Frequency;
3348 break;
3350 case AL_SAMPLE_OFFSET:
3351 offset = readPos + (ALdouble)readPosFrac/FRACTIONONE;
3352 break;
3354 case AL_BYTE_OFFSET:
3355 if(BufferFmt->OriginalType == UserFmtIMA4)
3357 ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4;
3358 ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels);
3359 ALuint FrameBlockSize = BufferFmt->OriginalAlign;
3361 /* Round down to nearest ADPCM block */
3362 offset = (ALdouble)(readPos / FrameBlockSize * BlockSize);
3364 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
3366 ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7;
3367 ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels);
3368 ALuint FrameBlockSize = BufferFmt->OriginalAlign;
3370 /* Round down to nearest ADPCM block */
3371 offset = (ALdouble)(readPos / FrameBlockSize * BlockSize);
3373 else
3375 ALuint FrameSize = FrameSizeFromFmt(BufferFmt->FmtChannels,
3376 BufferFmt->FmtType);
3377 offset = (ALdouble)(readPos * FrameSize);
3379 break;
3383 return offset;
3387 /* ApplyOffset
3389 * Apply the stored playback offset to the Source. This function will update
3390 * the number of buffers "played" given the stored offset.
3392 static ALboolean ApplyOffset(ALsource *Source, ALvoice *voice)
3394 ALbufferlistitem *BufferList;
3395 ALuint bufferLen, totalBufferLen;
3396 ALuint offset = 0;
3397 ALsizei frac = 0;
3399 /* Get sample frame offset */
3400 if(!GetSampleOffset(Source, &offset, &frac))
3401 return AL_FALSE;
3403 totalBufferLen = 0;
3404 BufferList = Source->queue;
3405 while(BufferList && totalBufferLen <= offset)
3407 ALsizei max_len = 0;
3408 ALsizei i;
3410 for(i = 0;i < BufferList->num_buffers;i++)
3412 ALbuffer *buffer = BufferList->buffers[i];
3413 if(buffer) max_len = maxi(max_len, buffer->SampleLen);
3415 bufferLen = max_len;
3417 if(bufferLen > offset-totalBufferLen)
3419 /* Offset is in this buffer */
3420 ATOMIC_STORE(&voice->position, offset - totalBufferLen, almemory_order_relaxed);
3421 ATOMIC_STORE(&voice->position_fraction, frac, almemory_order_relaxed);
3422 ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_release);
3423 return AL_TRUE;
3426 totalBufferLen += bufferLen;
3428 BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
3431 /* Offset is out of range of the queue */
3432 return AL_FALSE;
3436 /* GetSampleOffset
3438 * Retrieves the sample offset into the Source's queue (from the Sample, Byte
3439 * or Second offset supplied by the application). This takes into account the
3440 * fact that the buffer format may have been modifed since.
3442 static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALsizei *frac)
3444 const ALbuffer *BufferFmt = NULL;
3445 const ALbufferlistitem *BufferList;
3446 ALdouble dbloff, dblfrac;
3448 /* Find the first valid Buffer in the Queue */
3449 BufferList = Source->queue;
3450 while(BufferList)
3452 ALsizei i;
3453 for(i = 0;i < BufferList->num_buffers && !BufferFmt;i++)
3454 BufferFmt = BufferList->buffers[i];
3455 if(BufferFmt) break;
3456 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
3457 almemory_order_relaxed);
3459 if(!BufferFmt)
3461 Source->OffsetType = AL_NONE;
3462 Source->Offset = 0.0;
3463 return AL_FALSE;
3466 switch(Source->OffsetType)
3468 case AL_BYTE_OFFSET:
3469 /* Determine the ByteOffset (and ensure it is block aligned) */
3470 *offset = (ALuint)Source->Offset;
3471 if(BufferFmt->OriginalType == UserFmtIMA4)
3473 ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4;
3474 *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels);
3475 *offset *= BufferFmt->OriginalAlign;
3477 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
3479 ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7;
3480 *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels);
3481 *offset *= BufferFmt->OriginalAlign;
3483 else
3484 *offset /= FrameSizeFromFmt(BufferFmt->FmtChannels, BufferFmt->FmtType);
3485 *frac = 0;
3486 break;
3488 case AL_SAMPLE_OFFSET:
3489 dblfrac = modf(Source->Offset, &dbloff);
3490 *offset = (ALuint)mind(dbloff, UINT_MAX);
3491 *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0);
3492 break;
3494 case AL_SEC_OFFSET:
3495 dblfrac = modf(Source->Offset*BufferFmt->Frequency, &dbloff);
3496 *offset = (ALuint)mind(dbloff, UINT_MAX);
3497 *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0);
3498 break;
3500 Source->OffsetType = AL_NONE;
3501 Source->Offset = 0.0;
3503 return AL_TRUE;
3507 static ALsource *AllocSource(ALCcontext *context)
3509 ALCdevice *device = context->Device;
3510 SourceSubList *sublist, *subend;
3511 ALsource *source = NULL;
3512 ALsizei lidx = 0;
3513 ALsizei slidx;
3515 almtx_lock(&context->SourceLock);
3516 if(context->NumSources >= device->SourcesMax)
3518 almtx_unlock(&context->SourceLock);
3519 alSetError(context, AL_OUT_OF_MEMORY, "Exceeding %u source limit", device->SourcesMax);
3520 return NULL;
3522 sublist = VECTOR_BEGIN(context->SourceList);
3523 subend = VECTOR_END(context->SourceList);
3524 for(;sublist != subend;++sublist)
3526 if(sublist->FreeMask)
3528 slidx = CTZ64(sublist->FreeMask);
3529 source = sublist->Sources + slidx;
3530 break;
3532 ++lidx;
3534 if(UNLIKELY(!source))
3536 const SourceSubList empty_sublist = { 0, NULL };
3537 /* Don't allocate so many list entries that the 32-bit ID could
3538 * overflow...
3540 if(UNLIKELY(VECTOR_SIZE(context->SourceList) >= 1<<25))
3542 almtx_unlock(&device->BufferLock);
3543 alSetError(context, AL_OUT_OF_MEMORY, "Too many sources allocated");
3544 return NULL;
3546 lidx = (ALsizei)VECTOR_SIZE(context->SourceList);
3547 VECTOR_PUSH_BACK(context->SourceList, empty_sublist);
3548 sublist = &VECTOR_BACK(context->SourceList);
3549 sublist->FreeMask = ~U64(0);
3550 sublist->Sources = al_calloc(16, sizeof(ALsource)*64);
3551 if(UNLIKELY(!sublist->Sources))
3553 VECTOR_POP_BACK(context->SourceList);
3554 almtx_unlock(&context->SourceLock);
3555 alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate source batch");
3556 return NULL;
3559 slidx = 0;
3560 source = sublist->Sources + slidx;
3563 memset(source, 0, sizeof(*source));
3564 InitSourceParams(source, device->NumAuxSends);
3566 /* Add 1 to avoid source ID 0. */
3567 source->id = ((lidx<<6) | slidx) + 1;
3569 context->NumSources++;
3570 sublist->FreeMask &= ~(U64(1)<<slidx);
3571 almtx_unlock(&context->SourceLock);
3573 return source;
3576 static void FreeSource(ALCcontext *context, ALsource *source)
3578 ALCdevice *device = context->Device;
3579 ALuint id = source->id - 1;
3580 ALsizei lidx = id >> 6;
3581 ALsizei slidx = id & 0x3f;
3582 ALvoice *voice;
3584 ALCdevice_Lock(device);
3585 if((voice=GetSourceVoice(source, context)) != NULL)
3587 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
3588 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
3590 ALCdevice_Unlock(device);
3592 DeinitSource(source, device->NumAuxSends);
3593 memset(source, 0, sizeof(*source));
3595 VECTOR_ELEM(context->SourceList, lidx).FreeMask |= U64(1) << slidx;
3596 context->NumSources--;
3599 /* ReleaseALSources
3601 * Destroys all sources in the source map.
3603 ALvoid ReleaseALSources(ALCcontext *context)
3605 ALCdevice *device = context->Device;
3606 SourceSubList *sublist = VECTOR_BEGIN(context->SourceList);
3607 SourceSubList *subend = VECTOR_END(context->SourceList);
3608 size_t leftover = 0;
3609 for(;sublist != subend;++sublist)
3611 ALuint64 usemask = ~sublist->FreeMask;
3612 while(usemask)
3614 ALsizei idx = CTZ64(usemask);
3615 ALsource *source = sublist->Sources + idx;
3617 DeinitSource(source, device->NumAuxSends);
3618 memset(source, 0, sizeof(*source));
3619 ++leftover;
3621 usemask &= ~(U64(1) << idx);
3623 sublist->FreeMask = ~usemask;
3625 if(leftover > 0)
3626 WARN("(%p) Deleted "SZFMT" Source%s\n", device, leftover, (leftover==1)?"":"s");