Fix the reverb panning behavior to better fit the spec
[openal-soft.git] / OpenAL32 / alSource.c
blob40b2c4940c37688fd8aa1ffd871a2dad8b859680
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(context->AsyncEvents, (const char*)&evt, 1) == 1)
252 alsem_post(&context->EventSem);
256 static ALint FloatValsByProp(ALenum prop)
258 if(prop != (ALenum)((SourceProp)prop))
259 return 0;
260 switch((SourceProp)prop)
262 case AL_PITCH:
263 case AL_GAIN:
264 case AL_MIN_GAIN:
265 case AL_MAX_GAIN:
266 case AL_MAX_DISTANCE:
267 case AL_ROLLOFF_FACTOR:
268 case AL_DOPPLER_FACTOR:
269 case AL_CONE_OUTER_GAIN:
270 case AL_SEC_OFFSET:
271 case AL_SAMPLE_OFFSET:
272 case AL_BYTE_OFFSET:
273 case AL_CONE_INNER_ANGLE:
274 case AL_CONE_OUTER_ANGLE:
275 case AL_REFERENCE_DISTANCE:
276 case AL_CONE_OUTER_GAINHF:
277 case AL_AIR_ABSORPTION_FACTOR:
278 case AL_ROOM_ROLLOFF_FACTOR:
279 case AL_DIRECT_FILTER_GAINHF_AUTO:
280 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
281 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
282 case AL_DIRECT_CHANNELS_SOFT:
283 case AL_DISTANCE_MODEL:
284 case AL_SOURCE_RELATIVE:
285 case AL_LOOPING:
286 case AL_SOURCE_STATE:
287 case AL_BUFFERS_QUEUED:
288 case AL_BUFFERS_PROCESSED:
289 case AL_SOURCE_TYPE:
290 case AL_SOURCE_RADIUS:
291 case AL_SOURCE_RESAMPLER_SOFT:
292 case AL_SOURCE_SPATIALIZE_SOFT:
293 return 1;
295 case AL_STEREO_ANGLES:
296 return 2;
298 case AL_POSITION:
299 case AL_VELOCITY:
300 case AL_DIRECTION:
301 return 3;
303 case AL_ORIENTATION:
304 return 6;
306 case AL_SEC_OFFSET_LATENCY_SOFT:
307 case AL_SEC_OFFSET_CLOCK_SOFT:
308 break; /* Double only */
310 case AL_BUFFER:
311 case AL_DIRECT_FILTER:
312 case AL_AUXILIARY_SEND_FILTER:
313 break; /* i/i64 only */
314 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
315 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
316 break; /* i64 only */
318 return 0;
320 static ALint DoubleValsByProp(ALenum prop)
322 if(prop != (ALenum)((SourceProp)prop))
323 return 0;
324 switch((SourceProp)prop)
326 case AL_PITCH:
327 case AL_GAIN:
328 case AL_MIN_GAIN:
329 case AL_MAX_GAIN:
330 case AL_MAX_DISTANCE:
331 case AL_ROLLOFF_FACTOR:
332 case AL_DOPPLER_FACTOR:
333 case AL_CONE_OUTER_GAIN:
334 case AL_SEC_OFFSET:
335 case AL_SAMPLE_OFFSET:
336 case AL_BYTE_OFFSET:
337 case AL_CONE_INNER_ANGLE:
338 case AL_CONE_OUTER_ANGLE:
339 case AL_REFERENCE_DISTANCE:
340 case AL_CONE_OUTER_GAINHF:
341 case AL_AIR_ABSORPTION_FACTOR:
342 case AL_ROOM_ROLLOFF_FACTOR:
343 case AL_DIRECT_FILTER_GAINHF_AUTO:
344 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
345 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
346 case AL_DIRECT_CHANNELS_SOFT:
347 case AL_DISTANCE_MODEL:
348 case AL_SOURCE_RELATIVE:
349 case AL_LOOPING:
350 case AL_SOURCE_STATE:
351 case AL_BUFFERS_QUEUED:
352 case AL_BUFFERS_PROCESSED:
353 case AL_SOURCE_TYPE:
354 case AL_SOURCE_RADIUS:
355 case AL_SOURCE_RESAMPLER_SOFT:
356 case AL_SOURCE_SPATIALIZE_SOFT:
357 return 1;
359 case AL_SEC_OFFSET_LATENCY_SOFT:
360 case AL_SEC_OFFSET_CLOCK_SOFT:
361 case AL_STEREO_ANGLES:
362 return 2;
364 case AL_POSITION:
365 case AL_VELOCITY:
366 case AL_DIRECTION:
367 return 3;
369 case AL_ORIENTATION:
370 return 6;
372 case AL_BUFFER:
373 case AL_DIRECT_FILTER:
374 case AL_AUXILIARY_SEND_FILTER:
375 break; /* i/i64 only */
376 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
377 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
378 break; /* i64 only */
380 return 0;
383 static ALint IntValsByProp(ALenum prop)
385 if(prop != (ALenum)((SourceProp)prop))
386 return 0;
387 switch((SourceProp)prop)
389 case AL_PITCH:
390 case AL_GAIN:
391 case AL_MIN_GAIN:
392 case AL_MAX_GAIN:
393 case AL_MAX_DISTANCE:
394 case AL_ROLLOFF_FACTOR:
395 case AL_DOPPLER_FACTOR:
396 case AL_CONE_OUTER_GAIN:
397 case AL_SEC_OFFSET:
398 case AL_SAMPLE_OFFSET:
399 case AL_BYTE_OFFSET:
400 case AL_CONE_INNER_ANGLE:
401 case AL_CONE_OUTER_ANGLE:
402 case AL_REFERENCE_DISTANCE:
403 case AL_CONE_OUTER_GAINHF:
404 case AL_AIR_ABSORPTION_FACTOR:
405 case AL_ROOM_ROLLOFF_FACTOR:
406 case AL_DIRECT_FILTER_GAINHF_AUTO:
407 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
408 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
409 case AL_DIRECT_CHANNELS_SOFT:
410 case AL_DISTANCE_MODEL:
411 case AL_SOURCE_RELATIVE:
412 case AL_LOOPING:
413 case AL_BUFFER:
414 case AL_SOURCE_STATE:
415 case AL_BUFFERS_QUEUED:
416 case AL_BUFFERS_PROCESSED:
417 case AL_SOURCE_TYPE:
418 case AL_DIRECT_FILTER:
419 case AL_SOURCE_RADIUS:
420 case AL_SOURCE_RESAMPLER_SOFT:
421 case AL_SOURCE_SPATIALIZE_SOFT:
422 return 1;
424 case AL_POSITION:
425 case AL_VELOCITY:
426 case AL_DIRECTION:
427 case AL_AUXILIARY_SEND_FILTER:
428 return 3;
430 case AL_ORIENTATION:
431 return 6;
433 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
434 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
435 break; /* i64 only */
436 case AL_SEC_OFFSET_LATENCY_SOFT:
437 case AL_SEC_OFFSET_CLOCK_SOFT:
438 break; /* Double only */
439 case AL_STEREO_ANGLES:
440 break; /* Float/double only */
442 return 0;
444 static ALint Int64ValsByProp(ALenum prop)
446 if(prop != (ALenum)((SourceProp)prop))
447 return 0;
448 switch((SourceProp)prop)
450 case AL_PITCH:
451 case AL_GAIN:
452 case AL_MIN_GAIN:
453 case AL_MAX_GAIN:
454 case AL_MAX_DISTANCE:
455 case AL_ROLLOFF_FACTOR:
456 case AL_DOPPLER_FACTOR:
457 case AL_CONE_OUTER_GAIN:
458 case AL_SEC_OFFSET:
459 case AL_SAMPLE_OFFSET:
460 case AL_BYTE_OFFSET:
461 case AL_CONE_INNER_ANGLE:
462 case AL_CONE_OUTER_ANGLE:
463 case AL_REFERENCE_DISTANCE:
464 case AL_CONE_OUTER_GAINHF:
465 case AL_AIR_ABSORPTION_FACTOR:
466 case AL_ROOM_ROLLOFF_FACTOR:
467 case AL_DIRECT_FILTER_GAINHF_AUTO:
468 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
469 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
470 case AL_DIRECT_CHANNELS_SOFT:
471 case AL_DISTANCE_MODEL:
472 case AL_SOURCE_RELATIVE:
473 case AL_LOOPING:
474 case AL_BUFFER:
475 case AL_SOURCE_STATE:
476 case AL_BUFFERS_QUEUED:
477 case AL_BUFFERS_PROCESSED:
478 case AL_SOURCE_TYPE:
479 case AL_DIRECT_FILTER:
480 case AL_SOURCE_RADIUS:
481 case AL_SOURCE_RESAMPLER_SOFT:
482 case AL_SOURCE_SPATIALIZE_SOFT:
483 return 1;
485 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
486 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
487 return 2;
489 case AL_POSITION:
490 case AL_VELOCITY:
491 case AL_DIRECTION:
492 case AL_AUXILIARY_SEND_FILTER:
493 return 3;
495 case AL_ORIENTATION:
496 return 6;
498 case AL_SEC_OFFSET_LATENCY_SOFT:
499 case AL_SEC_OFFSET_CLOCK_SOFT:
500 break; /* Double only */
501 case AL_STEREO_ANGLES:
502 break; /* Float/double only */
504 return 0;
508 #define CHECKVAL(x) do { \
509 if(!(x)) \
511 alSetError(Context, AL_INVALID_VALUE, "Value out of range"); \
512 return AL_FALSE; \
514 } while(0)
516 #define DO_UPDATEPROPS() do { \
517 ALvoice *voice; \
518 if(SourceShouldUpdate(Source, Context) && \
519 (voice=GetSourceVoice(Source, Context)) != NULL) \
520 UpdateSourceProps(Source, voice, device->NumAuxSends, Context); \
521 else \
522 ATOMIC_FLAG_CLEAR(&Source->PropsClean, almemory_order_release); \
523 } while(0)
525 static ALboolean SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALfloat *values)
527 ALCdevice *device = Context->Device;
528 ALint ival;
530 switch(prop)
532 case AL_SEC_OFFSET_LATENCY_SOFT:
533 case AL_SEC_OFFSET_CLOCK_SOFT:
534 /* Query only */
535 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
536 "Setting read-only source property 0x%04x", prop);
538 case AL_PITCH:
539 CHECKVAL(*values >= 0.0f);
541 Source->Pitch = *values;
542 DO_UPDATEPROPS();
543 return AL_TRUE;
545 case AL_CONE_INNER_ANGLE:
546 CHECKVAL(*values >= 0.0f && *values <= 360.0f);
548 Source->InnerAngle = *values;
549 DO_UPDATEPROPS();
550 return AL_TRUE;
552 case AL_CONE_OUTER_ANGLE:
553 CHECKVAL(*values >= 0.0f && *values <= 360.0f);
555 Source->OuterAngle = *values;
556 DO_UPDATEPROPS();
557 return AL_TRUE;
559 case AL_GAIN:
560 CHECKVAL(*values >= 0.0f);
562 Source->Gain = *values;
563 DO_UPDATEPROPS();
564 return AL_TRUE;
566 case AL_MAX_DISTANCE:
567 CHECKVAL(*values >= 0.0f);
569 Source->MaxDistance = *values;
570 DO_UPDATEPROPS();
571 return AL_TRUE;
573 case AL_ROLLOFF_FACTOR:
574 CHECKVAL(*values >= 0.0f);
576 Source->RolloffFactor = *values;
577 DO_UPDATEPROPS();
578 return AL_TRUE;
580 case AL_REFERENCE_DISTANCE:
581 CHECKVAL(*values >= 0.0f);
583 Source->RefDistance = *values;
584 DO_UPDATEPROPS();
585 return AL_TRUE;
587 case AL_MIN_GAIN:
588 CHECKVAL(*values >= 0.0f);
590 Source->MinGain = *values;
591 DO_UPDATEPROPS();
592 return AL_TRUE;
594 case AL_MAX_GAIN:
595 CHECKVAL(*values >= 0.0f);
597 Source->MaxGain = *values;
598 DO_UPDATEPROPS();
599 return AL_TRUE;
601 case AL_CONE_OUTER_GAIN:
602 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
604 Source->OuterGain = *values;
605 DO_UPDATEPROPS();
606 return AL_TRUE;
608 case AL_CONE_OUTER_GAINHF:
609 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
611 Source->OuterGainHF = *values;
612 DO_UPDATEPROPS();
613 return AL_TRUE;
615 case AL_AIR_ABSORPTION_FACTOR:
616 CHECKVAL(*values >= 0.0f && *values <= 10.0f);
618 Source->AirAbsorptionFactor = *values;
619 DO_UPDATEPROPS();
620 return AL_TRUE;
622 case AL_ROOM_ROLLOFF_FACTOR:
623 CHECKVAL(*values >= 0.0f && *values <= 10.0f);
625 Source->RoomRolloffFactor = *values;
626 DO_UPDATEPROPS();
627 return AL_TRUE;
629 case AL_DOPPLER_FACTOR:
630 CHECKVAL(*values >= 0.0f && *values <= 1.0f);
632 Source->DopplerFactor = *values;
633 DO_UPDATEPROPS();
634 return AL_TRUE;
636 case AL_SEC_OFFSET:
637 case AL_SAMPLE_OFFSET:
638 case AL_BYTE_OFFSET:
639 CHECKVAL(*values >= 0.0f);
641 Source->OffsetType = prop;
642 Source->Offset = *values;
644 if(IsPlayingOrPaused(Source))
646 ALvoice *voice;
648 ALCdevice_Lock(Context->Device);
649 /* Double-check that the source is still playing while we have
650 * the lock.
652 voice = GetSourceVoice(Source, Context);
653 if(voice)
655 if(ApplyOffset(Source, voice) == AL_FALSE)
657 ALCdevice_Unlock(Context->Device);
658 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid offset");
661 ALCdevice_Unlock(Context->Device);
663 return AL_TRUE;
665 case AL_SOURCE_RADIUS:
666 CHECKVAL(*values >= 0.0f && isfinite(*values));
668 Source->Radius = *values;
669 DO_UPDATEPROPS();
670 return AL_TRUE;
672 case AL_STEREO_ANGLES:
673 CHECKVAL(isfinite(values[0]) && isfinite(values[1]));
675 Source->StereoPan[0] = values[0];
676 Source->StereoPan[1] = values[1];
677 DO_UPDATEPROPS();
678 return AL_TRUE;
681 case AL_POSITION:
682 CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]));
684 Source->Position[0] = values[0];
685 Source->Position[1] = values[1];
686 Source->Position[2] = values[2];
687 DO_UPDATEPROPS();
688 return AL_TRUE;
690 case AL_VELOCITY:
691 CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]));
693 Source->Velocity[0] = values[0];
694 Source->Velocity[1] = values[1];
695 Source->Velocity[2] = values[2];
696 DO_UPDATEPROPS();
697 return AL_TRUE;
699 case AL_DIRECTION:
700 CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]));
702 Source->Direction[0] = values[0];
703 Source->Direction[1] = values[1];
704 Source->Direction[2] = values[2];
705 DO_UPDATEPROPS();
706 return AL_TRUE;
708 case AL_ORIENTATION:
709 CHECKVAL(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) &&
710 isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5]));
712 Source->Orientation[0][0] = values[0];
713 Source->Orientation[0][1] = values[1];
714 Source->Orientation[0][2] = values[2];
715 Source->Orientation[1][0] = values[3];
716 Source->Orientation[1][1] = values[4];
717 Source->Orientation[1][2] = values[5];
718 DO_UPDATEPROPS();
719 return AL_TRUE;
722 case AL_SOURCE_RELATIVE:
723 case AL_LOOPING:
724 case AL_SOURCE_STATE:
725 case AL_SOURCE_TYPE:
726 case AL_DISTANCE_MODEL:
727 case AL_DIRECT_FILTER_GAINHF_AUTO:
728 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
729 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
730 case AL_DIRECT_CHANNELS_SOFT:
731 case AL_SOURCE_RESAMPLER_SOFT:
732 case AL_SOURCE_SPATIALIZE_SOFT:
733 ival = (ALint)values[0];
734 return SetSourceiv(Source, Context, prop, &ival);
736 case AL_BUFFERS_QUEUED:
737 case AL_BUFFERS_PROCESSED:
738 ival = (ALint)((ALuint)values[0]);
739 return SetSourceiv(Source, Context, prop, &ival);
741 case AL_BUFFER:
742 case AL_DIRECT_FILTER:
743 case AL_AUXILIARY_SEND_FILTER:
744 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
745 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
746 break;
749 ERR("Unexpected property: 0x%04x\n", prop);
750 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source float property 0x%04x", prop);
753 static ALboolean SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint *values)
755 ALCdevice *device = Context->Device;
756 ALbuffer *buffer = NULL;
757 ALfilter *filter = NULL;
758 ALeffectslot *slot = NULL;
759 ALbufferlistitem *oldlist;
760 ALfloat fvals[6];
762 switch(prop)
764 case AL_SOURCE_STATE:
765 case AL_SOURCE_TYPE:
766 case AL_BUFFERS_QUEUED:
767 case AL_BUFFERS_PROCESSED:
768 /* Query only */
769 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
770 "Setting read-only source property 0x%04x", prop);
772 case AL_SOURCE_RELATIVE:
773 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
775 Source->HeadRelative = (ALboolean)*values;
776 DO_UPDATEPROPS();
777 return AL_TRUE;
779 case AL_LOOPING:
780 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
782 Source->Looping = (ALboolean)*values;
783 if(IsPlayingOrPaused(Source))
785 ALvoice *voice = GetSourceVoice(Source, Context);
786 if(voice)
788 if(Source->Looping)
789 ATOMIC_STORE(&voice->loop_buffer, Source->queue, almemory_order_release);
790 else
791 ATOMIC_STORE(&voice->loop_buffer, NULL, almemory_order_release);
793 /* If the source is playing, wait for the current mix to finish
794 * to ensure it isn't currently looping back or reaching the
795 * end.
797 while((ATOMIC_LOAD(&device->MixCount, almemory_order_acquire)&1))
798 althrd_yield();
801 return AL_TRUE;
803 case AL_BUFFER:
804 LockBufferList(device);
805 if(!(*values == 0 || (buffer=LookupBuffer(device, *values)) != NULL))
807 UnlockBufferList(device);
808 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid buffer ID %u",
809 *values);
812 if(buffer && buffer->MappedAccess != 0 &&
813 !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
815 UnlockBufferList(device);
816 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
817 "Setting non-persistently mapped buffer %u", buffer->id);
819 else
821 ALenum state = GetSourceState(Source, GetSourceVoice(Source, Context));
822 if(state == AL_PLAYING || state == AL_PAUSED)
824 UnlockBufferList(device);
825 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
826 "Setting buffer on playing or paused source %u", Source->id);
830 oldlist = Source->queue;
831 if(buffer != NULL)
833 /* Add the selected buffer to a one-item queue */
834 ALbufferlistitem *newlist = al_calloc(DEF_ALIGN,
835 FAM_SIZE(ALbufferlistitem, buffers, 1));
836 ATOMIC_INIT(&newlist->next, NULL);
837 newlist->num_buffers = 1;
838 newlist->buffers[0] = buffer;
839 IncrementRef(&buffer->ref);
841 /* Source is now Static */
842 Source->SourceType = AL_STATIC;
843 Source->queue = newlist;
845 else
847 /* Source is now Undetermined */
848 Source->SourceType = AL_UNDETERMINED;
849 Source->queue = NULL;
851 UnlockBufferList(device);
853 /* Delete all elements in the previous queue */
854 while(oldlist != NULL)
856 ALsizei i;
857 ALbufferlistitem *temp = oldlist;
858 oldlist = ATOMIC_LOAD(&temp->next, almemory_order_relaxed);
860 for(i = 0;i < temp->num_buffers;i++)
862 if(temp->buffers[i])
863 DecrementRef(&temp->buffers[i]->ref);
865 al_free(temp);
867 return AL_TRUE;
869 case AL_SEC_OFFSET:
870 case AL_SAMPLE_OFFSET:
871 case AL_BYTE_OFFSET:
872 CHECKVAL(*values >= 0);
874 Source->OffsetType = prop;
875 Source->Offset = *values;
877 if(IsPlayingOrPaused(Source))
879 ALvoice *voice;
881 ALCdevice_Lock(Context->Device);
882 voice = GetSourceVoice(Source, Context);
883 if(voice)
885 if(ApplyOffset(Source, voice) == AL_FALSE)
887 ALCdevice_Unlock(Context->Device);
888 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE,
889 "Invalid source offset");
892 ALCdevice_Unlock(Context->Device);
894 return AL_TRUE;
896 case AL_DIRECT_FILTER:
897 LockFilterList(device);
898 if(!(*values == 0 || (filter=LookupFilter(device, *values)) != NULL))
900 UnlockFilterList(device);
901 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u",
902 *values);
905 if(!filter)
907 Source->Direct.Gain = 1.0f;
908 Source->Direct.GainHF = 1.0f;
909 Source->Direct.HFReference = LOWPASSFREQREF;
910 Source->Direct.GainLF = 1.0f;
911 Source->Direct.LFReference = HIGHPASSFREQREF;
913 else
915 Source->Direct.Gain = filter->Gain;
916 Source->Direct.GainHF = filter->GainHF;
917 Source->Direct.HFReference = filter->HFReference;
918 Source->Direct.GainLF = filter->GainLF;
919 Source->Direct.LFReference = filter->LFReference;
921 UnlockFilterList(device);
922 DO_UPDATEPROPS();
923 return AL_TRUE;
925 case AL_DIRECT_FILTER_GAINHF_AUTO:
926 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
928 Source->DryGainHFAuto = *values;
929 DO_UPDATEPROPS();
930 return AL_TRUE;
932 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
933 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
935 Source->WetGainAuto = *values;
936 DO_UPDATEPROPS();
937 return AL_TRUE;
939 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
940 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
942 Source->WetGainHFAuto = *values;
943 DO_UPDATEPROPS();
944 return AL_TRUE;
946 case AL_DIRECT_CHANNELS_SOFT:
947 CHECKVAL(*values == AL_FALSE || *values == AL_TRUE);
949 Source->DirectChannels = *values;
950 DO_UPDATEPROPS();
951 return AL_TRUE;
953 case AL_DISTANCE_MODEL:
954 CHECKVAL(*values == AL_NONE ||
955 *values == AL_INVERSE_DISTANCE ||
956 *values == AL_INVERSE_DISTANCE_CLAMPED ||
957 *values == AL_LINEAR_DISTANCE ||
958 *values == AL_LINEAR_DISTANCE_CLAMPED ||
959 *values == AL_EXPONENT_DISTANCE ||
960 *values == AL_EXPONENT_DISTANCE_CLAMPED);
962 Source->DistanceModel = *values;
963 if(Context->SourceDistanceModel)
964 DO_UPDATEPROPS();
965 return AL_TRUE;
967 case AL_SOURCE_RESAMPLER_SOFT:
968 CHECKVAL(*values >= 0 && *values <= ResamplerMax);
970 Source->Resampler = *values;
971 DO_UPDATEPROPS();
972 return AL_TRUE;
974 case AL_SOURCE_SPATIALIZE_SOFT:
975 CHECKVAL(*values >= AL_FALSE && *values <= AL_AUTO_SOFT);
977 Source->Spatialize = *values;
978 DO_UPDATEPROPS();
979 return AL_TRUE;
982 case AL_AUXILIARY_SEND_FILTER:
983 LockEffectSlotList(Context);
984 if(!(values[0] == 0 || (slot=LookupEffectSlot(Context, values[0])) != NULL))
986 UnlockEffectSlotList(Context);
987 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid effect ID %u",
988 values[0]);
990 if(!((ALuint)values[1] < (ALuint)device->NumAuxSends))
992 UnlockEffectSlotList(Context);
993 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid send %u", values[1]);
995 LockFilterList(device);
996 if(!(values[2] == 0 || (filter=LookupFilter(device, values[2])) != NULL))
998 UnlockFilterList(device);
999 UnlockEffectSlotList(Context);
1000 SETERR_RETURN(Context, AL_INVALID_VALUE, AL_FALSE, "Invalid filter ID %u",
1001 values[2]);
1004 if(!filter)
1006 /* Disable filter */
1007 Source->Send[values[1]].Gain = 1.0f;
1008 Source->Send[values[1]].GainHF = 1.0f;
1009 Source->Send[values[1]].HFReference = LOWPASSFREQREF;
1010 Source->Send[values[1]].GainLF = 1.0f;
1011 Source->Send[values[1]].LFReference = HIGHPASSFREQREF;
1013 else
1015 Source->Send[values[1]].Gain = filter->Gain;
1016 Source->Send[values[1]].GainHF = filter->GainHF;
1017 Source->Send[values[1]].HFReference = filter->HFReference;
1018 Source->Send[values[1]].GainLF = filter->GainLF;
1019 Source->Send[values[1]].LFReference = filter->LFReference;
1021 UnlockFilterList(device);
1023 if(slot != Source->Send[values[1]].Slot && IsPlayingOrPaused(Source))
1025 ALvoice *voice;
1026 /* Add refcount on the new slot, and release the previous slot */
1027 if(slot) IncrementRef(&slot->ref);
1028 if(Source->Send[values[1]].Slot)
1029 DecrementRef(&Source->Send[values[1]].Slot->ref);
1030 Source->Send[values[1]].Slot = slot;
1032 /* We must force an update if the auxiliary slot changed on an
1033 * active source, in case the slot is about to be deleted.
1035 if((voice=GetSourceVoice(Source, Context)) != NULL)
1036 UpdateSourceProps(Source, voice, device->NumAuxSends, Context);
1037 else
1038 ATOMIC_FLAG_CLEAR(&Source->PropsClean, almemory_order_release);
1040 else
1042 if(slot) IncrementRef(&slot->ref);
1043 if(Source->Send[values[1]].Slot)
1044 DecrementRef(&Source->Send[values[1]].Slot->ref);
1045 Source->Send[values[1]].Slot = slot;
1046 DO_UPDATEPROPS();
1048 UnlockEffectSlotList(Context);
1050 return AL_TRUE;
1053 /* 1x float */
1054 case AL_CONE_INNER_ANGLE:
1055 case AL_CONE_OUTER_ANGLE:
1056 case AL_PITCH:
1057 case AL_GAIN:
1058 case AL_MIN_GAIN:
1059 case AL_MAX_GAIN:
1060 case AL_REFERENCE_DISTANCE:
1061 case AL_ROLLOFF_FACTOR:
1062 case AL_CONE_OUTER_GAIN:
1063 case AL_MAX_DISTANCE:
1064 case AL_DOPPLER_FACTOR:
1065 case AL_CONE_OUTER_GAINHF:
1066 case AL_AIR_ABSORPTION_FACTOR:
1067 case AL_ROOM_ROLLOFF_FACTOR:
1068 case AL_SOURCE_RADIUS:
1069 fvals[0] = (ALfloat)*values;
1070 return SetSourcefv(Source, Context, (int)prop, fvals);
1072 /* 3x float */
1073 case AL_POSITION:
1074 case AL_VELOCITY:
1075 case AL_DIRECTION:
1076 fvals[0] = (ALfloat)values[0];
1077 fvals[1] = (ALfloat)values[1];
1078 fvals[2] = (ALfloat)values[2];
1079 return SetSourcefv(Source, Context, (int)prop, fvals);
1081 /* 6x float */
1082 case AL_ORIENTATION:
1083 fvals[0] = (ALfloat)values[0];
1084 fvals[1] = (ALfloat)values[1];
1085 fvals[2] = (ALfloat)values[2];
1086 fvals[3] = (ALfloat)values[3];
1087 fvals[4] = (ALfloat)values[4];
1088 fvals[5] = (ALfloat)values[5];
1089 return SetSourcefv(Source, Context, (int)prop, fvals);
1091 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1092 case AL_SEC_OFFSET_LATENCY_SOFT:
1093 case AL_SEC_OFFSET_CLOCK_SOFT:
1094 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1095 case AL_STEREO_ANGLES:
1096 break;
1099 ERR("Unexpected property: 0x%04x\n", prop);
1100 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x",
1101 prop);
1104 static ALboolean SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const ALint64SOFT *values)
1106 ALfloat fvals[6];
1107 ALint ivals[3];
1109 switch(prop)
1111 case AL_SOURCE_TYPE:
1112 case AL_BUFFERS_QUEUED:
1113 case AL_BUFFERS_PROCESSED:
1114 case AL_SOURCE_STATE:
1115 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1116 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1117 /* Query only */
1118 SETERR_RETURN(Context, AL_INVALID_OPERATION, AL_FALSE,
1119 "Setting read-only source property 0x%04x", prop);
1121 /* 1x int */
1122 case AL_SOURCE_RELATIVE:
1123 case AL_LOOPING:
1124 case AL_SEC_OFFSET:
1125 case AL_SAMPLE_OFFSET:
1126 case AL_BYTE_OFFSET:
1127 case AL_DIRECT_FILTER_GAINHF_AUTO:
1128 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1129 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1130 case AL_DIRECT_CHANNELS_SOFT:
1131 case AL_DISTANCE_MODEL:
1132 case AL_SOURCE_RESAMPLER_SOFT:
1133 case AL_SOURCE_SPATIALIZE_SOFT:
1134 CHECKVAL(*values <= INT_MAX && *values >= INT_MIN);
1136 ivals[0] = (ALint)*values;
1137 return SetSourceiv(Source, Context, (int)prop, ivals);
1139 /* 1x uint */
1140 case AL_BUFFER:
1141 case AL_DIRECT_FILTER:
1142 CHECKVAL(*values <= UINT_MAX && *values >= 0);
1144 ivals[0] = (ALuint)*values;
1145 return SetSourceiv(Source, Context, (int)prop, ivals);
1147 /* 3x uint */
1148 case AL_AUXILIARY_SEND_FILTER:
1149 CHECKVAL(values[0] <= UINT_MAX && values[0] >= 0 &&
1150 values[1] <= UINT_MAX && values[1] >= 0 &&
1151 values[2] <= UINT_MAX && values[2] >= 0);
1153 ivals[0] = (ALuint)values[0];
1154 ivals[1] = (ALuint)values[1];
1155 ivals[2] = (ALuint)values[2];
1156 return SetSourceiv(Source, Context, (int)prop, ivals);
1158 /* 1x float */
1159 case AL_CONE_INNER_ANGLE:
1160 case AL_CONE_OUTER_ANGLE:
1161 case AL_PITCH:
1162 case AL_GAIN:
1163 case AL_MIN_GAIN:
1164 case AL_MAX_GAIN:
1165 case AL_REFERENCE_DISTANCE:
1166 case AL_ROLLOFF_FACTOR:
1167 case AL_CONE_OUTER_GAIN:
1168 case AL_MAX_DISTANCE:
1169 case AL_DOPPLER_FACTOR:
1170 case AL_CONE_OUTER_GAINHF:
1171 case AL_AIR_ABSORPTION_FACTOR:
1172 case AL_ROOM_ROLLOFF_FACTOR:
1173 case AL_SOURCE_RADIUS:
1174 fvals[0] = (ALfloat)*values;
1175 return SetSourcefv(Source, Context, (int)prop, fvals);
1177 /* 3x float */
1178 case AL_POSITION:
1179 case AL_VELOCITY:
1180 case AL_DIRECTION:
1181 fvals[0] = (ALfloat)values[0];
1182 fvals[1] = (ALfloat)values[1];
1183 fvals[2] = (ALfloat)values[2];
1184 return SetSourcefv(Source, Context, (int)prop, fvals);
1186 /* 6x float */
1187 case AL_ORIENTATION:
1188 fvals[0] = (ALfloat)values[0];
1189 fvals[1] = (ALfloat)values[1];
1190 fvals[2] = (ALfloat)values[2];
1191 fvals[3] = (ALfloat)values[3];
1192 fvals[4] = (ALfloat)values[4];
1193 fvals[5] = (ALfloat)values[5];
1194 return SetSourcefv(Source, Context, (int)prop, fvals);
1196 case AL_SEC_OFFSET_LATENCY_SOFT:
1197 case AL_SEC_OFFSET_CLOCK_SOFT:
1198 case AL_STEREO_ANGLES:
1199 break;
1202 ERR("Unexpected property: 0x%04x\n", prop);
1203 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x",
1204 prop);
1207 #undef CHECKVAL
1210 static ALboolean GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALdouble *values)
1212 ALCdevice *device = Context->Device;
1213 ClockLatency clocktime;
1214 ALuint64 srcclock;
1215 ALint ivals[3];
1216 ALboolean err;
1218 switch(prop)
1220 case AL_GAIN:
1221 *values = Source->Gain;
1222 return AL_TRUE;
1224 case AL_PITCH:
1225 *values = Source->Pitch;
1226 return AL_TRUE;
1228 case AL_MAX_DISTANCE:
1229 *values = Source->MaxDistance;
1230 return AL_TRUE;
1232 case AL_ROLLOFF_FACTOR:
1233 *values = Source->RolloffFactor;
1234 return AL_TRUE;
1236 case AL_REFERENCE_DISTANCE:
1237 *values = Source->RefDistance;
1238 return AL_TRUE;
1240 case AL_CONE_INNER_ANGLE:
1241 *values = Source->InnerAngle;
1242 return AL_TRUE;
1244 case AL_CONE_OUTER_ANGLE:
1245 *values = Source->OuterAngle;
1246 return AL_TRUE;
1248 case AL_MIN_GAIN:
1249 *values = Source->MinGain;
1250 return AL_TRUE;
1252 case AL_MAX_GAIN:
1253 *values = Source->MaxGain;
1254 return AL_TRUE;
1256 case AL_CONE_OUTER_GAIN:
1257 *values = Source->OuterGain;
1258 return AL_TRUE;
1260 case AL_SEC_OFFSET:
1261 case AL_SAMPLE_OFFSET:
1262 case AL_BYTE_OFFSET:
1263 *values = GetSourceOffset(Source, prop, Context);
1264 return AL_TRUE;
1266 case AL_CONE_OUTER_GAINHF:
1267 *values = Source->OuterGainHF;
1268 return AL_TRUE;
1270 case AL_AIR_ABSORPTION_FACTOR:
1271 *values = Source->AirAbsorptionFactor;
1272 return AL_TRUE;
1274 case AL_ROOM_ROLLOFF_FACTOR:
1275 *values = Source->RoomRolloffFactor;
1276 return AL_TRUE;
1278 case AL_DOPPLER_FACTOR:
1279 *values = Source->DopplerFactor;
1280 return AL_TRUE;
1282 case AL_SOURCE_RADIUS:
1283 *values = Source->Radius;
1284 return AL_TRUE;
1286 case AL_STEREO_ANGLES:
1287 values[0] = Source->StereoPan[0];
1288 values[1] = Source->StereoPan[1];
1289 return AL_TRUE;
1291 case AL_SEC_OFFSET_LATENCY_SOFT:
1292 /* Get the source offset with the clock time first. Then get the
1293 * clock time with the device latency. Order is important.
1295 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1296 almtx_lock(&device->BackendLock);
1297 clocktime = V0(device->Backend,getClockLatency)();
1298 almtx_unlock(&device->BackendLock);
1299 if(srcclock == (ALuint64)clocktime.ClockTime)
1300 values[1] = (ALdouble)clocktime.Latency / 1000000000.0;
1301 else
1303 /* If the clock time incremented, reduce the latency by that
1304 * much since it's that much closer to the source offset it got
1305 * earlier.
1307 ALuint64 diff = clocktime.ClockTime - srcclock;
1308 values[1] = (ALdouble)(clocktime.Latency - minu64(clocktime.Latency, diff)) /
1309 1000000000.0;
1311 return AL_TRUE;
1313 case AL_SEC_OFFSET_CLOCK_SOFT:
1314 values[0] = GetSourceSecOffset(Source, Context, &srcclock);
1315 values[1] = srcclock / 1000000000.0;
1316 return AL_TRUE;
1318 case AL_POSITION:
1319 values[0] = Source->Position[0];
1320 values[1] = Source->Position[1];
1321 values[2] = Source->Position[2];
1322 return AL_TRUE;
1324 case AL_VELOCITY:
1325 values[0] = Source->Velocity[0];
1326 values[1] = Source->Velocity[1];
1327 values[2] = Source->Velocity[2];
1328 return AL_TRUE;
1330 case AL_DIRECTION:
1331 values[0] = Source->Direction[0];
1332 values[1] = Source->Direction[1];
1333 values[2] = Source->Direction[2];
1334 return AL_TRUE;
1336 case AL_ORIENTATION:
1337 values[0] = Source->Orientation[0][0];
1338 values[1] = Source->Orientation[0][1];
1339 values[2] = Source->Orientation[0][2];
1340 values[3] = Source->Orientation[1][0];
1341 values[4] = Source->Orientation[1][1];
1342 values[5] = Source->Orientation[1][2];
1343 return AL_TRUE;
1345 /* 1x int */
1346 case AL_SOURCE_RELATIVE:
1347 case AL_LOOPING:
1348 case AL_SOURCE_STATE:
1349 case AL_BUFFERS_QUEUED:
1350 case AL_BUFFERS_PROCESSED:
1351 case AL_SOURCE_TYPE:
1352 case AL_DIRECT_FILTER_GAINHF_AUTO:
1353 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1354 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1355 case AL_DIRECT_CHANNELS_SOFT:
1356 case AL_DISTANCE_MODEL:
1357 case AL_SOURCE_RESAMPLER_SOFT:
1358 case AL_SOURCE_SPATIALIZE_SOFT:
1359 if((err=GetSourceiv(Source, Context, (int)prop, ivals)) != AL_FALSE)
1360 *values = (ALdouble)ivals[0];
1361 return err;
1363 case AL_BUFFER:
1364 case AL_DIRECT_FILTER:
1365 case AL_AUXILIARY_SEND_FILTER:
1366 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1367 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1368 break;
1371 ERR("Unexpected property: 0x%04x\n", prop);
1372 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source double property 0x%04x",
1373 prop);
1376 static ALboolean GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint *values)
1378 ALbufferlistitem *BufferList;
1379 ALdouble dvals[6];
1380 ALboolean err;
1382 switch(prop)
1384 case AL_SOURCE_RELATIVE:
1385 *values = Source->HeadRelative;
1386 return AL_TRUE;
1388 case AL_LOOPING:
1389 *values = Source->Looping;
1390 return AL_TRUE;
1392 case AL_BUFFER:
1393 BufferList = (Source->SourceType == AL_STATIC) ? Source->queue : NULL;
1394 *values = (BufferList && BufferList->num_buffers >= 1 && BufferList->buffers[0]) ?
1395 BufferList->buffers[0]->id : 0;
1396 return AL_TRUE;
1398 case AL_SOURCE_STATE:
1399 *values = GetSourceState(Source, GetSourceVoice(Source, Context));
1400 return AL_TRUE;
1402 case AL_BUFFERS_QUEUED:
1403 if(!(BufferList=Source->queue))
1404 *values = 0;
1405 else
1407 ALsizei count = 0;
1408 do {
1409 count += BufferList->num_buffers;
1410 BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
1411 } while(BufferList != NULL);
1412 *values = count;
1414 return AL_TRUE;
1416 case AL_BUFFERS_PROCESSED:
1417 if(Source->Looping || Source->SourceType != AL_STREAMING)
1419 /* Buffers on a looping source are in a perpetual state of
1420 * PENDING, so don't report any as PROCESSED */
1421 *values = 0;
1423 else
1425 const ALbufferlistitem *BufferList = Source->queue;
1426 const ALbufferlistitem *Current = NULL;
1427 ALsizei played = 0;
1428 ALvoice *voice;
1430 if((voice=GetSourceVoice(Source, Context)) != NULL)
1431 Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
1432 else if(Source->state == AL_INITIAL)
1433 Current = BufferList;
1435 while(BufferList && BufferList != Current)
1437 played += BufferList->num_buffers;
1438 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
1439 almemory_order_relaxed);
1441 *values = played;
1443 return AL_TRUE;
1445 case AL_SOURCE_TYPE:
1446 *values = Source->SourceType;
1447 return AL_TRUE;
1449 case AL_DIRECT_FILTER_GAINHF_AUTO:
1450 *values = Source->DryGainHFAuto;
1451 return AL_TRUE;
1453 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1454 *values = Source->WetGainAuto;
1455 return AL_TRUE;
1457 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1458 *values = Source->WetGainHFAuto;
1459 return AL_TRUE;
1461 case AL_DIRECT_CHANNELS_SOFT:
1462 *values = Source->DirectChannels;
1463 return AL_TRUE;
1465 case AL_DISTANCE_MODEL:
1466 *values = Source->DistanceModel;
1467 return AL_TRUE;
1469 case AL_SOURCE_RESAMPLER_SOFT:
1470 *values = Source->Resampler;
1471 return AL_TRUE;
1473 case AL_SOURCE_SPATIALIZE_SOFT:
1474 *values = Source->Spatialize;
1475 return AL_TRUE;
1477 /* 1x float/double */
1478 case AL_CONE_INNER_ANGLE:
1479 case AL_CONE_OUTER_ANGLE:
1480 case AL_PITCH:
1481 case AL_GAIN:
1482 case AL_MIN_GAIN:
1483 case AL_MAX_GAIN:
1484 case AL_REFERENCE_DISTANCE:
1485 case AL_ROLLOFF_FACTOR:
1486 case AL_CONE_OUTER_GAIN:
1487 case AL_MAX_DISTANCE:
1488 case AL_SEC_OFFSET:
1489 case AL_SAMPLE_OFFSET:
1490 case AL_BYTE_OFFSET:
1491 case AL_DOPPLER_FACTOR:
1492 case AL_AIR_ABSORPTION_FACTOR:
1493 case AL_ROOM_ROLLOFF_FACTOR:
1494 case AL_CONE_OUTER_GAINHF:
1495 case AL_SOURCE_RADIUS:
1496 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1497 *values = (ALint)dvals[0];
1498 return err;
1500 /* 3x float/double */
1501 case AL_POSITION:
1502 case AL_VELOCITY:
1503 case AL_DIRECTION:
1504 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1506 values[0] = (ALint)dvals[0];
1507 values[1] = (ALint)dvals[1];
1508 values[2] = (ALint)dvals[2];
1510 return err;
1512 /* 6x float/double */
1513 case AL_ORIENTATION:
1514 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1516 values[0] = (ALint)dvals[0];
1517 values[1] = (ALint)dvals[1];
1518 values[2] = (ALint)dvals[2];
1519 values[3] = (ALint)dvals[3];
1520 values[4] = (ALint)dvals[4];
1521 values[5] = (ALint)dvals[5];
1523 return err;
1525 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1526 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1527 break; /* i64 only */
1528 case AL_SEC_OFFSET_LATENCY_SOFT:
1529 case AL_SEC_OFFSET_CLOCK_SOFT:
1530 break; /* Double only */
1531 case AL_STEREO_ANGLES:
1532 break; /* Float/double only */
1534 case AL_DIRECT_FILTER:
1535 case AL_AUXILIARY_SEND_FILTER:
1536 break; /* ??? */
1539 ERR("Unexpected property: 0x%04x\n", prop);
1540 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer property 0x%04x",
1541 prop);
1544 static ALboolean GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, ALint64 *values)
1546 ALCdevice *device = Context->Device;
1547 ClockLatency clocktime;
1548 ALuint64 srcclock;
1549 ALdouble dvals[6];
1550 ALint ivals[3];
1551 ALboolean err;
1553 switch(prop)
1555 case AL_SAMPLE_OFFSET_LATENCY_SOFT:
1556 /* Get the source offset with the clock time first. Then get the
1557 * clock time with the device latency. Order is important.
1559 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
1560 almtx_lock(&device->BackendLock);
1561 clocktime = V0(device->Backend,getClockLatency)();
1562 almtx_unlock(&device->BackendLock);
1563 if(srcclock == (ALuint64)clocktime.ClockTime)
1564 values[1] = clocktime.Latency;
1565 else
1567 /* If the clock time incremented, reduce the latency by that
1568 * much since it's that much closer to the source offset it got
1569 * earlier.
1571 ALuint64 diff = clocktime.ClockTime - srcclock;
1572 values[1] = clocktime.Latency - minu64(clocktime.Latency, diff);
1574 return AL_TRUE;
1576 case AL_SAMPLE_OFFSET_CLOCK_SOFT:
1577 values[0] = GetSourceSampleOffset(Source, Context, &srcclock);
1578 values[1] = srcclock;
1579 return AL_TRUE;
1581 /* 1x float/double */
1582 case AL_CONE_INNER_ANGLE:
1583 case AL_CONE_OUTER_ANGLE:
1584 case AL_PITCH:
1585 case AL_GAIN:
1586 case AL_MIN_GAIN:
1587 case AL_MAX_GAIN:
1588 case AL_REFERENCE_DISTANCE:
1589 case AL_ROLLOFF_FACTOR:
1590 case AL_CONE_OUTER_GAIN:
1591 case AL_MAX_DISTANCE:
1592 case AL_SEC_OFFSET:
1593 case AL_SAMPLE_OFFSET:
1594 case AL_BYTE_OFFSET:
1595 case AL_DOPPLER_FACTOR:
1596 case AL_AIR_ABSORPTION_FACTOR:
1597 case AL_ROOM_ROLLOFF_FACTOR:
1598 case AL_CONE_OUTER_GAINHF:
1599 case AL_SOURCE_RADIUS:
1600 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1601 *values = (ALint64)dvals[0];
1602 return err;
1604 /* 3x float/double */
1605 case AL_POSITION:
1606 case AL_VELOCITY:
1607 case AL_DIRECTION:
1608 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1610 values[0] = (ALint64)dvals[0];
1611 values[1] = (ALint64)dvals[1];
1612 values[2] = (ALint64)dvals[2];
1614 return err;
1616 /* 6x float/double */
1617 case AL_ORIENTATION:
1618 if((err=GetSourcedv(Source, Context, prop, dvals)) != AL_FALSE)
1620 values[0] = (ALint64)dvals[0];
1621 values[1] = (ALint64)dvals[1];
1622 values[2] = (ALint64)dvals[2];
1623 values[3] = (ALint64)dvals[3];
1624 values[4] = (ALint64)dvals[4];
1625 values[5] = (ALint64)dvals[5];
1627 return err;
1629 /* 1x int */
1630 case AL_SOURCE_RELATIVE:
1631 case AL_LOOPING:
1632 case AL_SOURCE_STATE:
1633 case AL_BUFFERS_QUEUED:
1634 case AL_BUFFERS_PROCESSED:
1635 case AL_SOURCE_TYPE:
1636 case AL_DIRECT_FILTER_GAINHF_AUTO:
1637 case AL_AUXILIARY_SEND_FILTER_GAIN_AUTO:
1638 case AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO:
1639 case AL_DIRECT_CHANNELS_SOFT:
1640 case AL_DISTANCE_MODEL:
1641 case AL_SOURCE_RESAMPLER_SOFT:
1642 case AL_SOURCE_SPATIALIZE_SOFT:
1643 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
1644 *values = ivals[0];
1645 return err;
1647 /* 1x uint */
1648 case AL_BUFFER:
1649 case AL_DIRECT_FILTER:
1650 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
1651 *values = (ALuint)ivals[0];
1652 return err;
1654 /* 3x uint */
1655 case AL_AUXILIARY_SEND_FILTER:
1656 if((err=GetSourceiv(Source, Context, prop, ivals)) != AL_FALSE)
1658 values[0] = (ALuint)ivals[0];
1659 values[1] = (ALuint)ivals[1];
1660 values[2] = (ALuint)ivals[2];
1662 return err;
1664 case AL_SEC_OFFSET_LATENCY_SOFT:
1665 case AL_SEC_OFFSET_CLOCK_SOFT:
1666 break; /* Double only */
1667 case AL_STEREO_ANGLES:
1668 break; /* Float/double only */
1671 ERR("Unexpected property: 0x%04x\n", prop);
1672 SETERR_RETURN(Context, AL_INVALID_ENUM, AL_FALSE, "Invalid source integer64 property 0x%04x",
1673 prop);
1677 AL_API ALvoid AL_APIENTRY alGenSources(ALsizei n, ALuint *sources)
1679 ALCcontext *context;
1680 ALsizei cur = 0;
1682 context = GetContextRef();
1683 if(!context) return;
1685 if(!(n >= 0))
1686 alSetError(context, AL_INVALID_VALUE, "Generating %d sources", n);
1687 else for(cur = 0;cur < n;cur++)
1689 ALsource *source = AllocSource(context);
1690 if(!source)
1692 alDeleteSources(cur, sources);
1693 break;
1695 sources[cur] = source->id;
1698 ALCcontext_DecRef(context);
1702 AL_API ALvoid AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources)
1704 ALCcontext *context;
1705 ALsource *Source;
1706 ALsizei i;
1708 context = GetContextRef();
1709 if(!context) return;
1711 LockSourceList(context);
1712 if(!(n >= 0))
1713 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Deleting %d sources", n);
1715 /* Check that all Sources are valid */
1716 for(i = 0;i < n;i++)
1718 if(LookupSource(context, sources[i]) == NULL)
1719 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]);
1721 for(i = 0;i < n;i++)
1723 if((Source=LookupSource(context, sources[i])) != NULL)
1724 FreeSource(context, Source);
1727 done:
1728 UnlockSourceList(context);
1729 ALCcontext_DecRef(context);
1733 AL_API ALboolean AL_APIENTRY alIsSource(ALuint source)
1735 ALCcontext *context;
1736 ALboolean ret;
1738 context = GetContextRef();
1739 if(!context) return AL_FALSE;
1741 LockSourceList(context);
1742 ret = (LookupSource(context, source) ? AL_TRUE : AL_FALSE);
1743 UnlockSourceList(context);
1745 ALCcontext_DecRef(context);
1747 return ret;
1751 AL_API ALvoid AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value)
1753 ALCcontext *Context;
1754 ALsource *Source;
1756 Context = GetContextRef();
1757 if(!Context) return;
1759 almtx_lock(&Context->PropLock);
1760 LockSourceList(Context);
1761 if((Source=LookupSource(Context, source)) == NULL)
1762 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1763 else if(!(FloatValsByProp(param) == 1))
1764 alSetError(Context, AL_INVALID_ENUM, "Invalid float property 0x%04x", param);
1765 else
1766 SetSourcefv(Source, Context, param, &value);
1767 UnlockSourceList(Context);
1768 almtx_unlock(&Context->PropLock);
1770 ALCcontext_DecRef(Context);
1773 AL_API ALvoid AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
1775 ALCcontext *Context;
1776 ALsource *Source;
1778 Context = GetContextRef();
1779 if(!Context) return;
1781 almtx_lock(&Context->PropLock);
1782 LockSourceList(Context);
1783 if((Source=LookupSource(Context, source)) == NULL)
1784 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1785 else if(!(FloatValsByProp(param) == 3))
1786 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param);
1787 else
1789 ALfloat fvals[3] = { value1, value2, value3 };
1790 SetSourcefv(Source, Context, param, fvals);
1792 UnlockSourceList(Context);
1793 almtx_unlock(&Context->PropLock);
1795 ALCcontext_DecRef(Context);
1798 AL_API ALvoid AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values)
1800 ALCcontext *Context;
1801 ALsource *Source;
1803 Context = GetContextRef();
1804 if(!Context) return;
1806 almtx_lock(&Context->PropLock);
1807 LockSourceList(Context);
1808 if((Source=LookupSource(Context, source)) == NULL)
1809 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1810 else if(!values)
1811 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
1812 else if(!(FloatValsByProp(param) > 0))
1813 alSetError(Context, AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param);
1814 else
1815 SetSourcefv(Source, Context, param, values);
1816 UnlockSourceList(Context);
1817 almtx_unlock(&Context->PropLock);
1819 ALCcontext_DecRef(Context);
1823 AL_API ALvoid AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value)
1825 ALCcontext *Context;
1826 ALsource *Source;
1828 Context = GetContextRef();
1829 if(!Context) return;
1831 almtx_lock(&Context->PropLock);
1832 LockSourceList(Context);
1833 if((Source=LookupSource(Context, source)) == NULL)
1834 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1835 else if(!(DoubleValsByProp(param) == 1))
1836 alSetError(Context, AL_INVALID_ENUM, "Invalid double property 0x%04x", param);
1837 else
1839 ALfloat fval = (ALfloat)value;
1840 SetSourcefv(Source, Context, param, &fval);
1842 UnlockSourceList(Context);
1843 almtx_unlock(&Context->PropLock);
1845 ALCcontext_DecRef(Context);
1848 AL_API ALvoid AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3)
1850 ALCcontext *Context;
1851 ALsource *Source;
1853 Context = GetContextRef();
1854 if(!Context) return;
1856 almtx_lock(&Context->PropLock);
1857 LockSourceList(Context);
1858 if((Source=LookupSource(Context, source)) == NULL)
1859 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1860 else if(!(DoubleValsByProp(param) == 3))
1861 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param);
1862 else
1864 ALfloat fvals[3] = { (ALfloat)value1, (ALfloat)value2, (ALfloat)value3 };
1865 SetSourcefv(Source, Context, param, fvals);
1867 UnlockSourceList(Context);
1868 almtx_unlock(&Context->PropLock);
1870 ALCcontext_DecRef(Context);
1873 AL_API ALvoid AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values)
1875 ALCcontext *Context;
1876 ALsource *Source;
1877 ALint count;
1879 Context = GetContextRef();
1880 if(!Context) return;
1882 almtx_lock(&Context->PropLock);
1883 LockSourceList(Context);
1884 if((Source=LookupSource(Context, source)) == NULL)
1885 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1886 else if(!values)
1887 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
1888 else if(!((count=DoubleValsByProp(param)) > 0 && count <= 6))
1889 alSetError(Context, AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param);
1890 else
1892 ALfloat fvals[6];
1893 ALint i;
1895 for(i = 0;i < count;i++)
1896 fvals[i] = (ALfloat)values[i];
1897 SetSourcefv(Source, Context, param, fvals);
1899 UnlockSourceList(Context);
1900 almtx_unlock(&Context->PropLock);
1902 ALCcontext_DecRef(Context);
1906 AL_API ALvoid AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value)
1908 ALCcontext *Context;
1909 ALsource *Source;
1911 Context = GetContextRef();
1912 if(!Context) return;
1914 almtx_lock(&Context->PropLock);
1915 LockSourceList(Context);
1916 if((Source=LookupSource(Context, source)) == NULL)
1917 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1918 else if(!(IntValsByProp(param) == 1))
1919 alSetError(Context, AL_INVALID_ENUM, "Invalid integer property 0x%04x", param);
1920 else
1921 SetSourceiv(Source, Context, param, &value);
1922 UnlockSourceList(Context);
1923 almtx_unlock(&Context->PropLock);
1925 ALCcontext_DecRef(Context);
1928 AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3)
1930 ALCcontext *Context;
1931 ALsource *Source;
1933 Context = GetContextRef();
1934 if(!Context) return;
1936 almtx_lock(&Context->PropLock);
1937 LockSourceList(Context);
1938 if((Source=LookupSource(Context, source)) == NULL)
1939 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1940 else if(!(IntValsByProp(param) == 3))
1941 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param);
1942 else
1944 ALint ivals[3] = { value1, value2, value3 };
1945 SetSourceiv(Source, Context, param, ivals);
1947 UnlockSourceList(Context);
1948 almtx_unlock(&Context->PropLock);
1950 ALCcontext_DecRef(Context);
1953 AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values)
1955 ALCcontext *Context;
1956 ALsource *Source;
1958 Context = GetContextRef();
1959 if(!Context) return;
1961 almtx_lock(&Context->PropLock);
1962 LockSourceList(Context);
1963 if((Source=LookupSource(Context, source)) == NULL)
1964 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1965 else if(!values)
1966 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
1967 else if(!(IntValsByProp(param) > 0))
1968 alSetError(Context, AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param);
1969 else
1970 SetSourceiv(Source, Context, param, values);
1971 UnlockSourceList(Context);
1972 almtx_unlock(&Context->PropLock);
1974 ALCcontext_DecRef(Context);
1978 AL_API ALvoid AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value)
1980 ALCcontext *Context;
1981 ALsource *Source;
1983 Context = GetContextRef();
1984 if(!Context) return;
1986 almtx_lock(&Context->PropLock);
1987 LockSourceList(Context);
1988 if((Source=LookupSource(Context, source)) == NULL)
1989 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
1990 else if(!(Int64ValsByProp(param) == 1))
1991 alSetError(Context, AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param);
1992 else
1993 SetSourcei64v(Source, Context, param, &value);
1994 UnlockSourceList(Context);
1995 almtx_unlock(&Context->PropLock);
1997 ALCcontext_DecRef(Context);
2000 AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3)
2002 ALCcontext *Context;
2003 ALsource *Source;
2005 Context = GetContextRef();
2006 if(!Context) return;
2008 almtx_lock(&Context->PropLock);
2009 LockSourceList(Context);
2010 if((Source=LookupSource(Context, source)) == NULL)
2011 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2012 else if(!(Int64ValsByProp(param) == 3))
2013 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param);
2014 else
2016 ALint64SOFT i64vals[3] = { value1, value2, value3 };
2017 SetSourcei64v(Source, Context, param, i64vals);
2019 UnlockSourceList(Context);
2020 almtx_unlock(&Context->PropLock);
2022 ALCcontext_DecRef(Context);
2025 AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values)
2027 ALCcontext *Context;
2028 ALsource *Source;
2030 Context = GetContextRef();
2031 if(!Context) return;
2033 almtx_lock(&Context->PropLock);
2034 LockSourceList(Context);
2035 if((Source=LookupSource(Context, source)) == NULL)
2036 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2037 else if(!values)
2038 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2039 else if(!(Int64ValsByProp(param) > 0))
2040 alSetError(Context, AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param);
2041 else
2042 SetSourcei64v(Source, Context, param, values);
2043 UnlockSourceList(Context);
2044 almtx_unlock(&Context->PropLock);
2046 ALCcontext_DecRef(Context);
2050 AL_API ALvoid AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value)
2052 ALCcontext *Context;
2053 ALsource *Source;
2055 Context = GetContextRef();
2056 if(!Context) return;
2058 LockSourceList(Context);
2059 if((Source=LookupSource(Context, source)) == NULL)
2060 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2061 else if(!value)
2062 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2063 else if(!(FloatValsByProp(param) == 1))
2064 alSetError(Context, AL_INVALID_ENUM, "Invalid float property 0x%04x", param);
2065 else
2067 ALdouble dval;
2068 if(GetSourcedv(Source, Context, param, &dval))
2069 *value = (ALfloat)dval;
2071 UnlockSourceList(Context);
2073 ALCcontext_DecRef(Context);
2077 AL_API ALvoid AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
2079 ALCcontext *Context;
2080 ALsource *Source;
2082 Context = GetContextRef();
2083 if(!Context) return;
2085 LockSourceList(Context);
2086 if((Source=LookupSource(Context, source)) == NULL)
2087 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2088 else if(!(value1 && value2 && value3))
2089 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2090 else if(!(FloatValsByProp(param) == 3))
2091 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-float property 0x%04x", param);
2092 else
2094 ALdouble dvals[3];
2095 if(GetSourcedv(Source, Context, param, dvals))
2097 *value1 = (ALfloat)dvals[0];
2098 *value2 = (ALfloat)dvals[1];
2099 *value3 = (ALfloat)dvals[2];
2102 UnlockSourceList(Context);
2104 ALCcontext_DecRef(Context);
2108 AL_API ALvoid AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values)
2110 ALCcontext *Context;
2111 ALsource *Source;
2112 ALint count;
2114 Context = GetContextRef();
2115 if(!Context) return;
2117 LockSourceList(Context);
2118 if((Source=LookupSource(Context, source)) == NULL)
2119 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2120 else if(!values)
2121 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2122 else if(!((count=FloatValsByProp(param)) > 0 && count <= 6))
2123 alSetError(Context, AL_INVALID_ENUM, "Invalid float-vector property 0x%04x", param);
2124 else
2126 ALdouble dvals[6];
2127 if(GetSourcedv(Source, Context, param, dvals))
2129 ALint i;
2130 for(i = 0;i < count;i++)
2131 values[i] = (ALfloat)dvals[i];
2134 UnlockSourceList(Context);
2136 ALCcontext_DecRef(Context);
2140 AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value)
2142 ALCcontext *Context;
2143 ALsource *Source;
2145 Context = GetContextRef();
2146 if(!Context) return;
2148 LockSourceList(Context);
2149 if((Source=LookupSource(Context, source)) == NULL)
2150 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2151 else if(!value)
2152 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2153 else if(!(DoubleValsByProp(param) == 1))
2154 alSetError(Context, AL_INVALID_ENUM, "Invalid double property 0x%04x", param);
2155 else
2156 GetSourcedv(Source, Context, param, value);
2157 UnlockSourceList(Context);
2159 ALCcontext_DecRef(Context);
2162 AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3)
2164 ALCcontext *Context;
2165 ALsource *Source;
2167 Context = GetContextRef();
2168 if(!Context) return;
2170 LockSourceList(Context);
2171 if((Source=LookupSource(Context, source)) == NULL)
2172 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2173 else if(!(value1 && value2 && value3))
2174 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2175 else if(!(DoubleValsByProp(param) == 3))
2176 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-double property 0x%04x", param);
2177 else
2179 ALdouble dvals[3];
2180 if(GetSourcedv(Source, Context, param, dvals))
2182 *value1 = dvals[0];
2183 *value2 = dvals[1];
2184 *value3 = dvals[2];
2187 UnlockSourceList(Context);
2189 ALCcontext_DecRef(Context);
2192 AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values)
2194 ALCcontext *Context;
2195 ALsource *Source;
2197 Context = GetContextRef();
2198 if(!Context) return;
2200 LockSourceList(Context);
2201 if((Source=LookupSource(Context, source)) == NULL)
2202 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2203 else if(!values)
2204 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2205 else if(!(DoubleValsByProp(param) > 0))
2206 alSetError(Context, AL_INVALID_ENUM, "Invalid double-vector property 0x%04x", param);
2207 else
2208 GetSourcedv(Source, Context, param, values);
2209 UnlockSourceList(Context);
2211 ALCcontext_DecRef(Context);
2215 AL_API ALvoid AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value)
2217 ALCcontext *Context;
2218 ALsource *Source;
2220 Context = GetContextRef();
2221 if(!Context) return;
2223 LockSourceList(Context);
2224 if((Source=LookupSource(Context, source)) == NULL)
2225 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2226 else if(!value)
2227 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2228 else if(!(IntValsByProp(param) == 1))
2229 alSetError(Context, AL_INVALID_ENUM, "Invalid integer property 0x%04x", param);
2230 else
2231 GetSourceiv(Source, Context, param, value);
2232 UnlockSourceList(Context);
2234 ALCcontext_DecRef(Context);
2238 AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3)
2240 ALCcontext *Context;
2241 ALsource *Source;
2243 Context = GetContextRef();
2244 if(!Context) return;
2246 LockSourceList(Context);
2247 if((Source=LookupSource(Context, source)) == NULL)
2248 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2249 else if(!(value1 && value2 && value3))
2250 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2251 else if(!(IntValsByProp(param) == 3))
2252 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer property 0x%04x", param);
2253 else
2255 ALint ivals[3];
2256 if(GetSourceiv(Source, Context, param, ivals))
2258 *value1 = ivals[0];
2259 *value2 = ivals[1];
2260 *value3 = ivals[2];
2263 UnlockSourceList(Context);
2265 ALCcontext_DecRef(Context);
2269 AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values)
2271 ALCcontext *Context;
2272 ALsource *Source;
2274 Context = GetContextRef();
2275 if(!Context) return;
2277 LockSourceList(Context);
2278 if((Source=LookupSource(Context, source)) == NULL)
2279 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2280 else if(!values)
2281 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2282 else if(!(IntValsByProp(param) > 0))
2283 alSetError(Context, AL_INVALID_ENUM, "Invalid integer-vector property 0x%04x", param);
2284 else
2285 GetSourceiv(Source, Context, param, values);
2286 UnlockSourceList(Context);
2288 ALCcontext_DecRef(Context);
2292 AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value)
2294 ALCcontext *Context;
2295 ALsource *Source;
2297 Context = GetContextRef();
2298 if(!Context) return;
2300 LockSourceList(Context);
2301 if((Source=LookupSource(Context, source)) == NULL)
2302 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2303 else if(!value)
2304 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2305 else if(!(Int64ValsByProp(param) == 1))
2306 alSetError(Context, AL_INVALID_ENUM, "Invalid integer64 property 0x%04x", param);
2307 else
2308 GetSourcei64v(Source, Context, param, value);
2309 UnlockSourceList(Context);
2311 ALCcontext_DecRef(Context);
2314 AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3)
2316 ALCcontext *Context;
2317 ALsource *Source;
2319 Context = GetContextRef();
2320 if(!Context) return;
2322 LockSourceList(Context);
2323 if((Source=LookupSource(Context, source)) == NULL)
2324 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2325 else if(!(value1 && value2 && value3))
2326 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2327 else if(!(Int64ValsByProp(param) == 3))
2328 alSetError(Context, AL_INVALID_ENUM, "Invalid 3-integer64 property 0x%04x", param);
2329 else
2331 ALint64 i64vals[3];
2332 if(GetSourcei64v(Source, Context, param, i64vals))
2334 *value1 = i64vals[0];
2335 *value2 = i64vals[1];
2336 *value3 = i64vals[2];
2339 UnlockSourceList(Context);
2341 ALCcontext_DecRef(Context);
2344 AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values)
2346 ALCcontext *Context;
2347 ALsource *Source;
2349 Context = GetContextRef();
2350 if(!Context) return;
2352 LockSourceList(Context);
2353 if((Source=LookupSource(Context, source)) == NULL)
2354 alSetError(Context, AL_INVALID_NAME, "Invalid source ID %u", source);
2355 else if(!values)
2356 alSetError(Context, AL_INVALID_VALUE, "NULL pointer");
2357 else if(!(Int64ValsByProp(param) > 0))
2358 alSetError(Context, AL_INVALID_ENUM, "Invalid integer64-vector property 0x%04x", param);
2359 else
2360 GetSourcei64v(Source, Context, param, values);
2361 UnlockSourceList(Context);
2363 ALCcontext_DecRef(Context);
2367 AL_API ALvoid AL_APIENTRY alSourcePlay(ALuint source)
2369 alSourcePlayv(1, &source);
2371 AL_API ALvoid AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources)
2373 ALCcontext *context;
2374 ALCdevice *device;
2375 ALsource *source;
2376 ALvoice *voice;
2377 ALsizei i, j;
2379 context = GetContextRef();
2380 if(!context) return;
2382 LockSourceList(context);
2383 if(!(n >= 0))
2384 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Playing %d sources", n);
2385 for(i = 0;i < n;i++)
2387 if(!LookupSource(context, sources[i]))
2388 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]);
2391 device = context->Device;
2392 ALCdevice_Lock(device);
2393 /* If the device is disconnected, go right to stopped. */
2394 if(!ATOMIC_LOAD(&device->Connected, almemory_order_acquire))
2396 /* TODO: Send state change event? */
2397 for(i = 0;i < n;i++)
2399 source = LookupSource(context, sources[i]);
2400 source->OffsetType = AL_NONE;
2401 source->Offset = 0.0;
2402 source->state = AL_STOPPED;
2404 ALCdevice_Unlock(device);
2405 goto done;
2408 while(n > context->MaxVoices-context->VoiceCount)
2410 ALsizei newcount = context->MaxVoices << 1;
2411 if(context->MaxVoices >= newcount)
2413 ALCdevice_Unlock(device);
2414 SETERR_GOTO(context, AL_OUT_OF_MEMORY, done,
2415 "Overflow increasing voice count %d -> %d", context->MaxVoices, newcount);
2417 AllocateVoices(context, newcount, device->NumAuxSends);
2420 for(i = 0;i < n;i++)
2422 ALbufferlistitem *BufferList;
2423 ALbuffer *buffer = NULL;
2424 bool start_fading = false;
2425 ALint vidx = -1;
2426 ALsizei s;
2428 source = LookupSource(context, sources[i]);
2429 /* Check that there is a queue containing at least one valid, non zero
2430 * length Buffer.
2432 BufferList = source->queue;
2433 while(BufferList)
2435 ALsizei b;
2436 for(b = 0;b < BufferList->num_buffers;b++)
2438 buffer = BufferList->buffers[b];
2439 if(buffer && buffer->SampleLen > 0) break;
2441 if(buffer && buffer->SampleLen > 0) break;
2442 BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
2445 /* If there's nothing to play, go right to stopped. */
2446 if(UNLIKELY(!BufferList))
2448 /* NOTE: A source without any playable buffers should not have an
2449 * ALvoice since it shouldn't be in a playing or paused state. So
2450 * there's no need to look up its voice and clear the source.
2452 ALenum oldstate = GetSourceState(source, NULL);
2453 source->OffsetType = AL_NONE;
2454 source->Offset = 0.0;
2455 if(oldstate != AL_STOPPED)
2457 source->state = AL_STOPPED;
2458 SendStateChangeEvent(context, source->id, AL_STOPPED);
2460 continue;
2463 voice = GetSourceVoice(source, context);
2464 switch(GetSourceState(source, voice))
2466 case AL_PLAYING:
2467 assert(voice != NULL);
2468 /* A source that's already playing is restarted from the beginning. */
2469 ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_relaxed);
2470 ATOMIC_STORE(&voice->position, 0, almemory_order_relaxed);
2471 ATOMIC_STORE(&voice->position_fraction, 0, almemory_order_release);
2472 continue;
2474 case AL_PAUSED:
2475 assert(voice != NULL);
2476 /* A source that's paused simply resumes. */
2477 ATOMIC_STORE(&voice->Playing, true, almemory_order_release);
2478 source->state = AL_PLAYING;
2479 SendStateChangeEvent(context, source->id, AL_PLAYING);
2480 continue;
2482 default:
2483 break;
2486 /* Make sure this source isn't already active, and if not, look for an
2487 * unused voice to put it in.
2489 assert(voice == NULL);
2490 for(j = 0;j < context->VoiceCount;j++)
2492 if(ATOMIC_LOAD(&context->Voices[j]->Source, almemory_order_acquire) == NULL)
2494 vidx = j;
2495 break;
2498 if(vidx == -1)
2499 vidx = context->VoiceCount++;
2500 voice = context->Voices[vidx];
2501 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
2503 ATOMIC_FLAG_TEST_AND_SET(&source->PropsClean, almemory_order_acquire);
2504 UpdateSourceProps(source, voice, device->NumAuxSends, context);
2506 /* A source that's not playing or paused has any offset applied when it
2507 * starts playing.
2509 if(source->Looping)
2510 ATOMIC_STORE(&voice->loop_buffer, source->queue, almemory_order_relaxed);
2511 else
2512 ATOMIC_STORE(&voice->loop_buffer, NULL, almemory_order_relaxed);
2513 ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_relaxed);
2514 ATOMIC_STORE(&voice->position, 0, almemory_order_relaxed);
2515 ATOMIC_STORE(&voice->position_fraction, 0, almemory_order_relaxed);
2516 if(source->OffsetType != AL_NONE)
2518 ApplyOffset(source, voice);
2519 start_fading = ATOMIC_LOAD(&voice->position, almemory_order_relaxed) != 0 ||
2520 ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed) != 0 ||
2521 ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed) != BufferList;
2524 voice->NumChannels = ChannelsFromFmt(buffer->FmtChannels);
2525 voice->SampleSize = BytesFromFmt(buffer->FmtType);
2527 /* Clear previous samples. */
2528 memset(voice->PrevSamples, 0, sizeof(voice->PrevSamples));
2530 /* Clear the stepping value so the mixer knows not to mix this until
2531 * the update gets applied.
2533 voice->Step = 0;
2535 voice->Flags = start_fading ? VOICE_IS_FADING : 0;
2536 if(source->SourceType == AL_STATIC) voice->Flags |= VOICE_IS_STATIC;
2537 memset(voice->Direct.Params, 0, sizeof(voice->Direct.Params[0])*voice->NumChannels);
2538 for(s = 0;s < device->NumAuxSends;s++)
2539 memset(voice->Send[s].Params, 0, sizeof(voice->Send[s].Params[0])*voice->NumChannels);
2540 if(device->AvgSpeakerDist > 0.0f)
2542 ALfloat w1 = SPEEDOFSOUNDMETRESPERSEC /
2543 (device->AvgSpeakerDist * device->Frequency);
2544 for(j = 0;j < voice->NumChannels;j++)
2545 NfcFilterCreate(&voice->Direct.Params[j].NFCtrlFilter, 0.0f, w1);
2548 ATOMIC_STORE(&voice->Source, source, almemory_order_relaxed);
2549 ATOMIC_STORE(&voice->Playing, true, almemory_order_release);
2550 source->state = AL_PLAYING;
2551 source->VoiceIdx = vidx;
2553 SendStateChangeEvent(context, source->id, AL_PLAYING);
2555 ALCdevice_Unlock(device);
2557 done:
2558 UnlockSourceList(context);
2559 ALCcontext_DecRef(context);
2562 AL_API ALvoid AL_APIENTRY alSourcePause(ALuint source)
2564 alSourcePausev(1, &source);
2566 AL_API ALvoid AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources)
2568 ALCcontext *context;
2569 ALCdevice *device;
2570 ALsource *source;
2571 ALvoice *voice;
2572 ALsizei i;
2574 context = GetContextRef();
2575 if(!context) return;
2577 LockSourceList(context);
2578 if(!(n >= 0))
2579 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Pausing %d sources", n);
2580 for(i = 0;i < n;i++)
2582 if(!LookupSource(context, sources[i]))
2583 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]);
2586 device = context->Device;
2587 ALCdevice_Lock(device);
2588 for(i = 0;i < n;i++)
2590 source = LookupSource(context, sources[i]);
2591 if((voice=GetSourceVoice(source, context)) != NULL)
2592 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
2593 if(GetSourceState(source, voice) == AL_PLAYING)
2595 source->state = AL_PAUSED;
2596 SendStateChangeEvent(context, source->id, AL_PAUSED);
2599 ALCdevice_Unlock(device);
2601 done:
2602 UnlockSourceList(context);
2603 ALCcontext_DecRef(context);
2606 AL_API ALvoid AL_APIENTRY alSourceStop(ALuint source)
2608 alSourceStopv(1, &source);
2610 AL_API ALvoid AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources)
2612 ALCcontext *context;
2613 ALCdevice *device;
2614 ALsource *source;
2615 ALvoice *voice;
2616 ALsizei i;
2618 context = GetContextRef();
2619 if(!context) return;
2621 LockSourceList(context);
2622 if(!(n >= 0))
2623 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Stopping %d sources", n);
2624 for(i = 0;i < n;i++)
2626 if(!LookupSource(context, sources[i]))
2627 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]);
2630 device = context->Device;
2631 ALCdevice_Lock(device);
2632 for(i = 0;i < n;i++)
2634 ALenum oldstate;
2635 source = LookupSource(context, sources[i]);
2636 if((voice=GetSourceVoice(source, context)) != NULL)
2638 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
2639 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
2640 voice = NULL;
2642 oldstate = GetSourceState(source, voice);
2643 if(oldstate != AL_INITIAL && oldstate != AL_STOPPED)
2645 source->state = AL_STOPPED;
2646 SendStateChangeEvent(context, source->id, AL_STOPPED);
2648 source->OffsetType = AL_NONE;
2649 source->Offset = 0.0;
2651 ALCdevice_Unlock(device);
2653 done:
2654 UnlockSourceList(context);
2655 ALCcontext_DecRef(context);
2658 AL_API ALvoid AL_APIENTRY alSourceRewind(ALuint source)
2660 alSourceRewindv(1, &source);
2662 AL_API ALvoid AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources)
2664 ALCcontext *context;
2665 ALCdevice *device;
2666 ALsource *source;
2667 ALvoice *voice;
2668 ALsizei i;
2670 context = GetContextRef();
2671 if(!context) return;
2673 LockSourceList(context);
2674 if(!(n >= 0))
2675 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Rewinding %d sources", n);
2676 for(i = 0;i < n;i++)
2678 if(!LookupSource(context, sources[i]))
2679 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", sources[i]);
2682 device = context->Device;
2683 ALCdevice_Lock(device);
2684 for(i = 0;i < n;i++)
2686 source = LookupSource(context, sources[i]);
2687 if((voice=GetSourceVoice(source, context)) != NULL)
2689 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
2690 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
2691 voice = NULL;
2693 if(GetSourceState(source, voice) != AL_INITIAL)
2695 source->state = AL_INITIAL;
2696 SendStateChangeEvent(context, source->id, AL_INITIAL);
2698 source->OffsetType = AL_NONE;
2699 source->Offset = 0.0;
2701 ALCdevice_Unlock(device);
2703 done:
2704 UnlockSourceList(context);
2705 ALCcontext_DecRef(context);
2709 AL_API ALvoid AL_APIENTRY alSourceQueueBuffers(ALuint src, ALsizei nb, const ALuint *buffers)
2711 ALCdevice *device;
2712 ALCcontext *context;
2713 ALsource *source;
2714 ALsizei i;
2715 ALbufferlistitem *BufferListStart;
2716 ALbufferlistitem *BufferList;
2717 ALbuffer *BufferFmt = NULL;
2719 if(nb == 0)
2720 return;
2722 context = GetContextRef();
2723 if(!context) return;
2725 device = context->Device;
2727 LockSourceList(context);
2728 if(!(nb >= 0))
2729 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Queueing %d buffers", nb);
2730 if((source=LookupSource(context, src)) == NULL)
2731 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", src);
2733 if(source->SourceType == AL_STATIC)
2735 /* Can't queue on a Static Source */
2736 SETERR_GOTO(context, AL_INVALID_OPERATION, done, "Queueing onto static source %u", src);
2739 /* Check for a valid Buffer, for its frequency and format */
2740 BufferList = source->queue;
2741 while(BufferList)
2743 for(i = 0;i < BufferList->num_buffers;i++)
2745 if((BufferFmt=BufferList->buffers[i]) != NULL)
2746 break;
2748 if(BufferFmt) break;
2749 BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
2752 LockBufferList(device);
2753 BufferListStart = NULL;
2754 BufferList = NULL;
2755 for(i = 0;i < nb;i++)
2757 ALbuffer *buffer = NULL;
2758 if(buffers[i] && (buffer=LookupBuffer(device, buffers[i])) == NULL)
2759 SETERR_GOTO(context, AL_INVALID_NAME, buffer_error, "Queueing invalid buffer ID %u",
2760 buffers[i]);
2762 if(!BufferListStart)
2764 BufferListStart = al_calloc(DEF_ALIGN,
2765 FAM_SIZE(ALbufferlistitem, buffers, 1));
2766 BufferList = BufferListStart;
2768 else
2770 ALbufferlistitem *item = al_calloc(DEF_ALIGN,
2771 FAM_SIZE(ALbufferlistitem, buffers, 1));
2772 ATOMIC_STORE(&BufferList->next, item, almemory_order_relaxed);
2773 BufferList = item;
2775 ATOMIC_INIT(&BufferList->next, NULL);
2776 BufferList->num_buffers = 1;
2777 BufferList->buffers[0] = buffer;
2778 if(!buffer) continue;
2780 IncrementRef(&buffer->ref);
2782 if(buffer->MappedAccess != 0 && !(buffer->MappedAccess&AL_MAP_PERSISTENT_BIT_SOFT))
2783 SETERR_GOTO(context, AL_INVALID_OPERATION, buffer_error,
2784 "Queueing non-persistently mapped buffer %u", buffer->id);
2786 if(BufferFmt == NULL)
2787 BufferFmt = buffer;
2788 else if(BufferFmt->Frequency != buffer->Frequency ||
2789 BufferFmt->FmtChannels != buffer->FmtChannels ||
2790 BufferFmt->OriginalType != buffer->OriginalType)
2792 alSetError(context, AL_INVALID_OPERATION, "Queueing buffer with mismatched format");
2794 buffer_error:
2795 /* A buffer failed (invalid ID or format), so unlock and release
2796 * each buffer we had. */
2797 while(BufferListStart)
2799 ALbufferlistitem *next = ATOMIC_LOAD(&BufferListStart->next,
2800 almemory_order_relaxed);
2801 for(i = 0;i < BufferListStart->num_buffers;i++)
2803 if((buffer=BufferListStart->buffers[i]) != NULL)
2804 DecrementRef(&buffer->ref);
2806 al_free(BufferListStart);
2807 BufferListStart = next;
2809 UnlockBufferList(device);
2810 goto done;
2813 /* All buffers good. */
2814 UnlockBufferList(device);
2816 /* Source is now streaming */
2817 source->SourceType = AL_STREAMING;
2819 if(!(BufferList=source->queue))
2820 source->queue = BufferListStart;
2821 else
2823 ALbufferlistitem *next;
2824 while((next=ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed)) != NULL)
2825 BufferList = next;
2826 ATOMIC_STORE(&BufferList->next, BufferListStart, almemory_order_release);
2829 done:
2830 UnlockSourceList(context);
2831 ALCcontext_DecRef(context);
2834 AL_API ALvoid AL_APIENTRY alSourceUnqueueBuffers(ALuint src, ALsizei nb, ALuint *buffers)
2836 ALCcontext *context;
2837 ALsource *source;
2838 ALbufferlistitem *BufferList;
2839 ALbufferlistitem *Current;
2840 ALvoice *voice;
2841 ALsizei i;
2843 context = GetContextRef();
2844 if(!context) return;
2846 LockSourceList(context);
2847 if(!(nb >= 0))
2848 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing %d buffers", nb);
2849 if((source=LookupSource(context, src)) == NULL)
2850 SETERR_GOTO(context, AL_INVALID_NAME, done, "Invalid source ID %u", src);
2852 /* Nothing to unqueue. */
2853 if(nb == 0) goto done;
2855 if(source->Looping)
2856 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing from looping source %u", src);
2857 if(source->SourceType != AL_STREAMING)
2858 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing from a non-streaming source %u",
2859 src);
2861 /* Make sure enough buffers have been processed to unqueue. */
2862 BufferList = source->queue;
2863 Current = NULL;
2864 if((voice=GetSourceVoice(source, context)) != NULL)
2865 Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
2866 else if(source->state == AL_INITIAL)
2867 Current = BufferList;
2868 if(BufferList == Current)
2869 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing pending buffers");
2871 i = BufferList->num_buffers;
2872 while(i < nb)
2874 /* If the next bufferlist to check is NULL or is the current one, it's
2875 * trying to unqueue pending buffers.
2877 ALbufferlistitem *next = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
2878 if(!next || next == Current)
2879 SETERR_GOTO(context, AL_INVALID_VALUE, done, "Unqueueing pending buffers");
2880 BufferList = next;
2882 i += BufferList->num_buffers;
2885 while(nb > 0)
2887 ALbufferlistitem *head = source->queue;
2888 ALbufferlistitem *next = ATOMIC_LOAD(&head->next, almemory_order_relaxed);
2889 for(i = 0;i < head->num_buffers && nb > 0;i++,nb--)
2891 ALbuffer *buffer = head->buffers[i];
2892 if(!buffer)
2893 *(buffers++) = 0;
2894 else
2896 *(buffers++) = buffer->id;
2897 DecrementRef(&buffer->ref);
2900 if(i < head->num_buffers)
2902 /* This head has some buffers left over, so move them to the front
2903 * and update the count.
2905 ALsizei j = 0;
2906 while(i < head->num_buffers)
2907 head->buffers[j++] = head->buffers[i++];
2908 head->num_buffers = j;
2909 break;
2912 /* Otherwise, free this item and set the source queue head to the next
2913 * one.
2915 al_free(head);
2916 source->queue = next;
2919 done:
2920 UnlockSourceList(context);
2921 ALCcontext_DecRef(context);
2925 static void InitSourceParams(ALsource *Source, ALsizei num_sends)
2927 ALsizei i;
2929 Source->InnerAngle = 360.0f;
2930 Source->OuterAngle = 360.0f;
2931 Source->Pitch = 1.0f;
2932 Source->Position[0] = 0.0f;
2933 Source->Position[1] = 0.0f;
2934 Source->Position[2] = 0.0f;
2935 Source->Velocity[0] = 0.0f;
2936 Source->Velocity[1] = 0.0f;
2937 Source->Velocity[2] = 0.0f;
2938 Source->Direction[0] = 0.0f;
2939 Source->Direction[1] = 0.0f;
2940 Source->Direction[2] = 0.0f;
2941 Source->Orientation[0][0] = 0.0f;
2942 Source->Orientation[0][1] = 0.0f;
2943 Source->Orientation[0][2] = -1.0f;
2944 Source->Orientation[1][0] = 0.0f;
2945 Source->Orientation[1][1] = 1.0f;
2946 Source->Orientation[1][2] = 0.0f;
2947 Source->RefDistance = 1.0f;
2948 Source->MaxDistance = FLT_MAX;
2949 Source->RolloffFactor = 1.0f;
2950 Source->Gain = 1.0f;
2951 Source->MinGain = 0.0f;
2952 Source->MaxGain = 1.0f;
2953 Source->OuterGain = 0.0f;
2954 Source->OuterGainHF = 1.0f;
2956 Source->DryGainHFAuto = AL_TRUE;
2957 Source->WetGainAuto = AL_TRUE;
2958 Source->WetGainHFAuto = AL_TRUE;
2959 Source->AirAbsorptionFactor = 0.0f;
2960 Source->RoomRolloffFactor = 0.0f;
2961 Source->DopplerFactor = 1.0f;
2962 Source->HeadRelative = AL_FALSE;
2963 Source->Looping = AL_FALSE;
2964 Source->DistanceModel = DefaultDistanceModel;
2965 Source->Resampler = ResamplerDefault;
2966 Source->DirectChannels = AL_FALSE;
2967 Source->Spatialize = SpatializeAuto;
2969 Source->StereoPan[0] = DEG2RAD( 30.0f);
2970 Source->StereoPan[1] = DEG2RAD(-30.0f);
2972 Source->Radius = 0.0f;
2974 Source->Direct.Gain = 1.0f;
2975 Source->Direct.GainHF = 1.0f;
2976 Source->Direct.HFReference = LOWPASSFREQREF;
2977 Source->Direct.GainLF = 1.0f;
2978 Source->Direct.LFReference = HIGHPASSFREQREF;
2979 Source->Send = al_calloc(16, num_sends*sizeof(Source->Send[0]));
2980 for(i = 0;i < num_sends;i++)
2982 Source->Send[i].Slot = NULL;
2983 Source->Send[i].Gain = 1.0f;
2984 Source->Send[i].GainHF = 1.0f;
2985 Source->Send[i].HFReference = LOWPASSFREQREF;
2986 Source->Send[i].GainLF = 1.0f;
2987 Source->Send[i].LFReference = HIGHPASSFREQREF;
2990 Source->Offset = 0.0;
2991 Source->OffsetType = AL_NONE;
2992 Source->SourceType = AL_UNDETERMINED;
2993 Source->state = AL_INITIAL;
2995 Source->queue = NULL;
2997 /* No way to do an 'init' here, so just test+set with relaxed ordering and
2998 * ignore the test.
3000 ATOMIC_FLAG_TEST_AND_SET(&Source->PropsClean, almemory_order_relaxed);
3002 Source->VoiceIdx = -1;
3005 static void DeinitSource(ALsource *source, ALsizei num_sends)
3007 ALbufferlistitem *BufferList;
3008 ALsizei i;
3010 BufferList = source->queue;
3011 while(BufferList != NULL)
3013 ALbufferlistitem *next = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
3014 for(i = 0;i < BufferList->num_buffers;i++)
3016 if(BufferList->buffers[i] != NULL)
3017 DecrementRef(&BufferList->buffers[i]->ref);
3019 al_free(BufferList);
3020 BufferList = next;
3022 source->queue = NULL;
3024 if(source->Send)
3026 for(i = 0;i < num_sends;i++)
3028 if(source->Send[i].Slot)
3029 DecrementRef(&source->Send[i].Slot->ref);
3030 source->Send[i].Slot = NULL;
3032 al_free(source->Send);
3033 source->Send = NULL;
3037 static void UpdateSourceProps(ALsource *source, ALvoice *voice, ALsizei num_sends, ALCcontext *context)
3039 struct ALvoiceProps *props;
3040 ALsizei i;
3042 /* Get an unused property container, or allocate a new one as needed. */
3043 props = ATOMIC_LOAD(&context->FreeVoiceProps, almemory_order_acquire);
3044 if(!props)
3045 props = al_calloc(16, FAM_SIZE(struct ALvoiceProps, Send, num_sends));
3046 else
3048 struct ALvoiceProps *next;
3049 do {
3050 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
3051 } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&context->FreeVoiceProps, &props, next,
3052 almemory_order_acq_rel, almemory_order_acquire) == 0);
3055 /* Copy in current property values. */
3056 props->Pitch = source->Pitch;
3057 props->Gain = source->Gain;
3058 props->OuterGain = source->OuterGain;
3059 props->MinGain = source->MinGain;
3060 props->MaxGain = source->MaxGain;
3061 props->InnerAngle = source->InnerAngle;
3062 props->OuterAngle = source->OuterAngle;
3063 props->RefDistance = source->RefDistance;
3064 props->MaxDistance = source->MaxDistance;
3065 props->RolloffFactor = source->RolloffFactor;
3066 for(i = 0;i < 3;i++)
3067 props->Position[i] = source->Position[i];
3068 for(i = 0;i < 3;i++)
3069 props->Velocity[i] = source->Velocity[i];
3070 for(i = 0;i < 3;i++)
3071 props->Direction[i] = source->Direction[i];
3072 for(i = 0;i < 2;i++)
3074 ALsizei j;
3075 for(j = 0;j < 3;j++)
3076 props->Orientation[i][j] = source->Orientation[i][j];
3078 props->HeadRelative = source->HeadRelative;
3079 props->DistanceModel = source->DistanceModel;
3080 props->Resampler = source->Resampler;
3081 props->DirectChannels = source->DirectChannels;
3082 props->SpatializeMode = source->Spatialize;
3084 props->DryGainHFAuto = source->DryGainHFAuto;
3085 props->WetGainAuto = source->WetGainAuto;
3086 props->WetGainHFAuto = source->WetGainHFAuto;
3087 props->OuterGainHF = source->OuterGainHF;
3089 props->AirAbsorptionFactor = source->AirAbsorptionFactor;
3090 props->RoomRolloffFactor = source->RoomRolloffFactor;
3091 props->DopplerFactor = source->DopplerFactor;
3093 props->StereoPan[0] = source->StereoPan[0];
3094 props->StereoPan[1] = source->StereoPan[1];
3096 props->Radius = source->Radius;
3098 props->Direct.Gain = source->Direct.Gain;
3099 props->Direct.GainHF = source->Direct.GainHF;
3100 props->Direct.HFReference = source->Direct.HFReference;
3101 props->Direct.GainLF = source->Direct.GainLF;
3102 props->Direct.LFReference = source->Direct.LFReference;
3104 for(i = 0;i < num_sends;i++)
3106 props->Send[i].Slot = source->Send[i].Slot;
3107 props->Send[i].Gain = source->Send[i].Gain;
3108 props->Send[i].GainHF = source->Send[i].GainHF;
3109 props->Send[i].HFReference = source->Send[i].HFReference;
3110 props->Send[i].GainLF = source->Send[i].GainLF;
3111 props->Send[i].LFReference = source->Send[i].LFReference;
3114 /* Set the new container for updating internal parameters. */
3115 props = ATOMIC_EXCHANGE_PTR(&voice->Update, props, almemory_order_acq_rel);
3116 if(props)
3118 /* If there was an unused update container, put it back in the
3119 * freelist.
3121 ATOMIC_REPLACE_HEAD(struct ALvoiceProps*, &context->FreeVoiceProps, props);
3125 void UpdateAllSourceProps(ALCcontext *context)
3127 ALsizei num_sends = context->Device->NumAuxSends;
3128 ALsizei pos;
3130 for(pos = 0;pos < context->VoiceCount;pos++)
3132 ALvoice *voice = context->Voices[pos];
3133 ALsource *source = ATOMIC_LOAD(&voice->Source, almemory_order_acquire);
3134 if(source && !ATOMIC_FLAG_TEST_AND_SET(&source->PropsClean, almemory_order_acq_rel))
3135 UpdateSourceProps(source, voice, num_sends, context);
3140 /* GetSourceSampleOffset
3142 * Gets the current read offset for the given Source, in 32.32 fixed-point
3143 * samples. The offset is relative to the start of the queue (not the start of
3144 * the current buffer).
3146 static ALint64 GetSourceSampleOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime)
3148 ALCdevice *device = context->Device;
3149 const ALbufferlistitem *Current;
3150 ALuint64 readPos;
3151 ALuint refcount;
3152 ALvoice *voice;
3154 do {
3155 Current = NULL;
3156 readPos = 0;
3157 while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
3158 althrd_yield();
3159 *clocktime = GetDeviceClockTime(device);
3161 voice = GetSourceVoice(Source, context);
3162 if(voice)
3164 Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
3166 readPos = (ALuint64)ATOMIC_LOAD(&voice->position, almemory_order_relaxed) << 32;
3167 readPos |= (ALuint64)ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed) <<
3168 (32-FRACTIONBITS);
3170 ATOMIC_THREAD_FENCE(almemory_order_acquire);
3171 } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
3173 if(voice)
3175 const ALbufferlistitem *BufferList = Source->queue;
3176 while(BufferList && BufferList != Current)
3178 ALsizei max_len = 0;
3179 ALsizei i;
3181 for(i = 0;i < BufferList->num_buffers;i++)
3183 ALbuffer *buffer = BufferList->buffers[i];
3184 if(buffer) max_len = maxi(max_len, buffer->SampleLen);
3186 readPos += (ALuint64)max_len << 32;
3187 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
3188 almemory_order_relaxed);
3190 readPos = minu64(readPos, U64(0x7fffffffffffffff));
3193 return (ALint64)readPos;
3196 /* GetSourceSecOffset
3198 * Gets the current read offset for the given Source, in seconds. The offset is
3199 * relative to the start of the queue (not the start of the current buffer).
3201 static ALdouble GetSourceSecOffset(ALsource *Source, ALCcontext *context, ALuint64 *clocktime)
3203 ALCdevice *device = context->Device;
3204 const ALbufferlistitem *Current;
3205 ALuint64 readPos;
3206 ALuint refcount;
3207 ALdouble offset;
3208 ALvoice *voice;
3210 do {
3211 Current = NULL;
3212 readPos = 0;
3213 while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
3214 althrd_yield();
3215 *clocktime = GetDeviceClockTime(device);
3217 voice = GetSourceVoice(Source, context);
3218 if(voice)
3220 Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
3222 readPos = (ALuint64)ATOMIC_LOAD(&voice->position, almemory_order_relaxed) <<
3223 FRACTIONBITS;
3224 readPos |= ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
3226 ATOMIC_THREAD_FENCE(almemory_order_acquire);
3227 } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
3229 offset = 0.0;
3230 if(voice)
3232 const ALbufferlistitem *BufferList = Source->queue;
3233 const ALbuffer *BufferFmt = NULL;
3234 while(BufferList && BufferList != Current)
3236 ALsizei max_len = 0;
3237 ALsizei i;
3239 for(i = 0;i < BufferList->num_buffers;i++)
3241 ALbuffer *buffer = BufferList->buffers[i];
3242 if(buffer)
3244 if(!BufferFmt) BufferFmt = buffer;
3245 max_len = maxi(max_len, buffer->SampleLen);
3248 readPos += (ALuint64)max_len << FRACTIONBITS;
3249 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
3250 almemory_order_relaxed);
3253 while(BufferList && !BufferFmt)
3255 ALsizei i;
3256 for(i = 0;i < BufferList->num_buffers && !BufferFmt;i++)
3257 BufferFmt = BufferList->buffers[i];
3258 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
3259 almemory_order_relaxed);
3261 assert(BufferFmt != NULL);
3263 offset = (ALdouble)readPos / (ALdouble)FRACTIONONE /
3264 (ALdouble)BufferFmt->Frequency;
3267 return offset;
3270 /* GetSourceOffset
3272 * Gets the current read offset for the given Source, in the appropriate format
3273 * (Bytes, Samples or Seconds). The offset is relative to the start of the
3274 * queue (not the start of the current buffer).
3276 static ALdouble GetSourceOffset(ALsource *Source, ALenum name, ALCcontext *context)
3278 ALCdevice *device = context->Device;
3279 const ALbufferlistitem *Current;
3280 ALuint readPos;
3281 ALsizei readPosFrac;
3282 ALuint refcount;
3283 ALdouble offset;
3284 ALvoice *voice;
3286 do {
3287 Current = NULL;
3288 readPos = readPosFrac = 0;
3289 while(((refcount=ATOMIC_LOAD(&device->MixCount, almemory_order_acquire))&1))
3290 althrd_yield();
3291 voice = GetSourceVoice(Source, context);
3292 if(voice)
3294 Current = ATOMIC_LOAD(&voice->current_buffer, almemory_order_relaxed);
3296 readPos = ATOMIC_LOAD(&voice->position, almemory_order_relaxed);
3297 readPosFrac = ATOMIC_LOAD(&voice->position_fraction, almemory_order_relaxed);
3299 ATOMIC_THREAD_FENCE(almemory_order_acquire);
3300 } while(refcount != ATOMIC_LOAD(&device->MixCount, almemory_order_relaxed));
3302 offset = 0.0;
3303 if(voice)
3305 const ALbufferlistitem *BufferList = Source->queue;
3306 const ALbuffer *BufferFmt = NULL;
3307 ALboolean readFin = AL_FALSE;
3308 ALuint totalBufferLen = 0;
3310 while(BufferList != NULL)
3312 ALsizei max_len = 0;
3313 ALsizei i;
3315 readFin = readFin || (BufferList == Current);
3316 for(i = 0;i < BufferList->num_buffers;i++)
3318 ALbuffer *buffer = BufferList->buffers[i];
3319 if(buffer)
3321 if(!BufferFmt) BufferFmt = buffer;
3322 max_len = maxi(max_len, buffer->SampleLen);
3325 totalBufferLen += max_len;
3326 if(!readFin) readPos += max_len;
3328 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
3329 almemory_order_relaxed);
3331 assert(BufferFmt != NULL);
3333 if(Source->Looping)
3334 readPos %= totalBufferLen;
3335 else
3337 /* Wrap back to 0 */
3338 if(readPos >= totalBufferLen)
3339 readPos = readPosFrac = 0;
3342 offset = 0.0;
3343 switch(name)
3345 case AL_SEC_OFFSET:
3346 offset = (readPos + (ALdouble)readPosFrac/FRACTIONONE) / BufferFmt->Frequency;
3347 break;
3349 case AL_SAMPLE_OFFSET:
3350 offset = readPos + (ALdouble)readPosFrac/FRACTIONONE;
3351 break;
3353 case AL_BYTE_OFFSET:
3354 if(BufferFmt->OriginalType == UserFmtIMA4)
3356 ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4;
3357 ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels);
3358 ALuint FrameBlockSize = BufferFmt->OriginalAlign;
3360 /* Round down to nearest ADPCM block */
3361 offset = (ALdouble)(readPos / FrameBlockSize * BlockSize);
3363 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
3365 ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7;
3366 ALuint BlockSize = align * ChannelsFromFmt(BufferFmt->FmtChannels);
3367 ALuint FrameBlockSize = BufferFmt->OriginalAlign;
3369 /* Round down to nearest ADPCM block */
3370 offset = (ALdouble)(readPos / FrameBlockSize * BlockSize);
3372 else
3374 ALuint FrameSize = FrameSizeFromFmt(BufferFmt->FmtChannels,
3375 BufferFmt->FmtType);
3376 offset = (ALdouble)(readPos * FrameSize);
3378 break;
3382 return offset;
3386 /* ApplyOffset
3388 * Apply the stored playback offset to the Source. This function will update
3389 * the number of buffers "played" given the stored offset.
3391 static ALboolean ApplyOffset(ALsource *Source, ALvoice *voice)
3393 ALbufferlistitem *BufferList;
3394 ALuint bufferLen, totalBufferLen;
3395 ALuint offset = 0;
3396 ALsizei frac = 0;
3398 /* Get sample frame offset */
3399 if(!GetSampleOffset(Source, &offset, &frac))
3400 return AL_FALSE;
3402 totalBufferLen = 0;
3403 BufferList = Source->queue;
3404 while(BufferList && totalBufferLen <= offset)
3406 ALsizei max_len = 0;
3407 ALsizei i;
3409 for(i = 0;i < BufferList->num_buffers;i++)
3411 ALbuffer *buffer = BufferList->buffers[i];
3412 if(buffer) max_len = maxi(max_len, buffer->SampleLen);
3414 bufferLen = max_len;
3416 if(bufferLen > offset-totalBufferLen)
3418 /* Offset is in this buffer */
3419 ATOMIC_STORE(&voice->position, offset - totalBufferLen, almemory_order_relaxed);
3420 ATOMIC_STORE(&voice->position_fraction, frac, almemory_order_relaxed);
3421 ATOMIC_STORE(&voice->current_buffer, BufferList, almemory_order_release);
3422 return AL_TRUE;
3425 totalBufferLen += bufferLen;
3427 BufferList = ATOMIC_LOAD(&BufferList->next, almemory_order_relaxed);
3430 /* Offset is out of range of the queue */
3431 return AL_FALSE;
3435 /* GetSampleOffset
3437 * Retrieves the sample offset into the Source's queue (from the Sample, Byte
3438 * or Second offset supplied by the application). This takes into account the
3439 * fact that the buffer format may have been modifed since.
3441 static ALboolean GetSampleOffset(ALsource *Source, ALuint *offset, ALsizei *frac)
3443 const ALbuffer *BufferFmt = NULL;
3444 const ALbufferlistitem *BufferList;
3445 ALdouble dbloff, dblfrac;
3447 /* Find the first valid Buffer in the Queue */
3448 BufferList = Source->queue;
3449 while(BufferList)
3451 ALsizei i;
3452 for(i = 0;i < BufferList->num_buffers && !BufferFmt;i++)
3453 BufferFmt = BufferList->buffers[i];
3454 if(BufferFmt) break;
3455 BufferList = ATOMIC_LOAD(&CONST_CAST(ALbufferlistitem*,BufferList)->next,
3456 almemory_order_relaxed);
3458 if(!BufferFmt)
3460 Source->OffsetType = AL_NONE;
3461 Source->Offset = 0.0;
3462 return AL_FALSE;
3465 switch(Source->OffsetType)
3467 case AL_BYTE_OFFSET:
3468 /* Determine the ByteOffset (and ensure it is block aligned) */
3469 *offset = (ALuint)Source->Offset;
3470 if(BufferFmt->OriginalType == UserFmtIMA4)
3472 ALsizei align = (BufferFmt->OriginalAlign-1)/2 + 4;
3473 *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels);
3474 *offset *= BufferFmt->OriginalAlign;
3476 else if(BufferFmt->OriginalType == UserFmtMSADPCM)
3478 ALsizei align = (BufferFmt->OriginalAlign-2)/2 + 7;
3479 *offset /= align * ChannelsFromFmt(BufferFmt->FmtChannels);
3480 *offset *= BufferFmt->OriginalAlign;
3482 else
3483 *offset /= FrameSizeFromFmt(BufferFmt->FmtChannels, BufferFmt->FmtType);
3484 *frac = 0;
3485 break;
3487 case AL_SAMPLE_OFFSET:
3488 dblfrac = modf(Source->Offset, &dbloff);
3489 *offset = (ALuint)mind(dbloff, UINT_MAX);
3490 *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0);
3491 break;
3493 case AL_SEC_OFFSET:
3494 dblfrac = modf(Source->Offset*BufferFmt->Frequency, &dbloff);
3495 *offset = (ALuint)mind(dbloff, UINT_MAX);
3496 *frac = (ALsizei)mind(dblfrac*FRACTIONONE, FRACTIONONE-1.0);
3497 break;
3499 Source->OffsetType = AL_NONE;
3500 Source->Offset = 0.0;
3502 return AL_TRUE;
3506 static ALsource *AllocSource(ALCcontext *context)
3508 ALCdevice *device = context->Device;
3509 SourceSubList *sublist, *subend;
3510 ALsource *source = NULL;
3511 ALsizei lidx = 0;
3512 ALsizei slidx;
3514 almtx_lock(&context->SourceLock);
3515 if(context->NumSources >= device->SourcesMax)
3517 almtx_unlock(&context->SourceLock);
3518 alSetError(context, AL_OUT_OF_MEMORY, "Exceeding %u source limit", device->SourcesMax);
3519 return NULL;
3521 sublist = VECTOR_BEGIN(context->SourceList);
3522 subend = VECTOR_END(context->SourceList);
3523 for(;sublist != subend;++sublist)
3525 if(sublist->FreeMask)
3527 slidx = CTZ64(sublist->FreeMask);
3528 source = sublist->Sources + slidx;
3529 break;
3531 ++lidx;
3533 if(UNLIKELY(!source))
3535 const SourceSubList empty_sublist = { 0, NULL };
3536 /* Don't allocate so many list entries that the 32-bit ID could
3537 * overflow...
3539 if(UNLIKELY(VECTOR_SIZE(context->SourceList) >= 1<<25))
3541 almtx_unlock(&device->BufferLock);
3542 alSetError(context, AL_OUT_OF_MEMORY, "Too many sources allocated");
3543 return NULL;
3545 lidx = (ALsizei)VECTOR_SIZE(context->SourceList);
3546 VECTOR_PUSH_BACK(context->SourceList, empty_sublist);
3547 sublist = &VECTOR_BACK(context->SourceList);
3548 sublist->FreeMask = ~U64(0);
3549 sublist->Sources = al_calloc(16, sizeof(ALsource)*64);
3550 if(UNLIKELY(!sublist->Sources))
3552 VECTOR_POP_BACK(context->SourceList);
3553 almtx_unlock(&context->SourceLock);
3554 alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate source batch");
3555 return NULL;
3558 slidx = 0;
3559 source = sublist->Sources + slidx;
3562 memset(source, 0, sizeof(*source));
3563 InitSourceParams(source, device->NumAuxSends);
3565 /* Add 1 to avoid source ID 0. */
3566 source->id = ((lidx<<6) | slidx) + 1;
3568 context->NumSources++;
3569 sublist->FreeMask &= ~(U64(1)<<slidx);
3570 almtx_unlock(&context->SourceLock);
3572 return source;
3575 static void FreeSource(ALCcontext *context, ALsource *source)
3577 ALCdevice *device = context->Device;
3578 ALuint id = source->id - 1;
3579 ALsizei lidx = id >> 6;
3580 ALsizei slidx = id & 0x3f;
3581 ALvoice *voice;
3583 ALCdevice_Lock(device);
3584 if((voice=GetSourceVoice(source, context)) != NULL)
3586 ATOMIC_STORE(&voice->Source, NULL, almemory_order_relaxed);
3587 ATOMIC_STORE(&voice->Playing, false, almemory_order_release);
3589 ALCdevice_Unlock(device);
3591 DeinitSource(source, device->NumAuxSends);
3592 memset(source, 0, sizeof(*source));
3594 VECTOR_ELEM(context->SourceList, lidx).FreeMask |= U64(1) << slidx;
3595 context->NumSources--;
3598 /* ReleaseALSources
3600 * Destroys all sources in the source map.
3602 ALvoid ReleaseALSources(ALCcontext *context)
3604 ALCdevice *device = context->Device;
3605 SourceSubList *sublist = VECTOR_BEGIN(context->SourceList);
3606 SourceSubList *subend = VECTOR_END(context->SourceList);
3607 size_t leftover = 0;
3608 for(;sublist != subend;++sublist)
3610 ALuint64 usemask = ~sublist->FreeMask;
3611 while(usemask)
3613 ALsizei idx = CTZ64(usemask);
3614 ALsource *source = sublist->Sources + idx;
3616 DeinitSource(source, device->NumAuxSends);
3617 memset(source, 0, sizeof(*source));
3618 ++leftover;
3620 usemask &= ~(U64(1) << idx);
3622 sublist->FreeMask = ~usemask;
3624 if(leftover > 0)
3625 WARN("(%p) Deleted "SZFMT" Source%s\n", device, leftover, (leftover==1)?"":"s");