Don't update context and listener props unnecessarily
[openal-soft.git] / OpenAL32 / alListener.c
blob7062f8c470ac13b35c44dd1cdb2928628aecd5ea
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 1999-2000 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 "alMain.h"
24 #include "alu.h"
25 #include "alError.h"
26 #include "alListener.h"
27 #include "alSource.h"
29 #define DO_UPDATEPROPS() do { \
30 if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire)) \
31 UpdateListenerProps(context); \
32 else \
33 ATOMIC_FLAG_CLEAR(&listener->PropsClean, almemory_order_release); \
34 } while(0)
37 AL_API ALvoid AL_APIENTRY alListenerf(ALenum param, ALfloat value)
39 ALlistener *listener;
40 ALCcontext *context;
42 context = GetContextRef();
43 if(!context) return;
45 listener = context->Listener;
46 WriteLock(&context->PropLock);
47 switch(param)
49 case AL_GAIN:
50 if(!(value >= 0.0f && isfinite(value)))
51 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
52 listener->Gain = value;
53 DO_UPDATEPROPS();
54 break;
56 case AL_METERS_PER_UNIT:
57 if(!(value >= AL_MIN_METERS_PER_UNIT && value <= AL_MAX_METERS_PER_UNIT))
58 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
59 context->MetersPerUnit = value;
60 if(!ATOMIC_LOAD(&context->DeferUpdates, almemory_order_acquire))
61 UpdateContextProps(context);
62 else
63 ATOMIC_FLAG_CLEAR(&context->PropsClean, almemory_order_release);
64 break;
66 default:
67 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
70 done:
71 WriteUnlock(&context->PropLock);
72 ALCcontext_DecRef(context);
76 AL_API ALvoid AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3)
78 ALlistener *listener;
79 ALCcontext *context;
81 context = GetContextRef();
82 if(!context) return;
84 listener = context->Listener;
85 WriteLock(&context->PropLock);
86 switch(param)
88 case AL_POSITION:
89 if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
90 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
91 listener->Position[0] = value1;
92 listener->Position[1] = value2;
93 listener->Position[2] = value3;
94 DO_UPDATEPROPS();
95 break;
97 case AL_VELOCITY:
98 if(!(isfinite(value1) && isfinite(value2) && isfinite(value3)))
99 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
100 listener->Velocity[0] = value1;
101 listener->Velocity[1] = value2;
102 listener->Velocity[2] = value3;
103 DO_UPDATEPROPS();
104 break;
106 default:
107 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
110 done:
111 WriteUnlock(&context->PropLock);
112 ALCcontext_DecRef(context);
116 AL_API ALvoid AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values)
118 ALlistener *listener;
119 ALCcontext *context;
121 if(values)
123 switch(param)
125 case AL_GAIN:
126 case AL_METERS_PER_UNIT:
127 alListenerf(param, values[0]);
128 return;
130 case AL_POSITION:
131 case AL_VELOCITY:
132 alListener3f(param, values[0], values[1], values[2]);
133 return;
137 context = GetContextRef();
138 if(!context) return;
140 listener = context->Listener;
141 WriteLock(&context->PropLock);
142 if(!(values))
143 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
144 switch(param)
146 case AL_ORIENTATION:
147 if(!(isfinite(values[0]) && isfinite(values[1]) && isfinite(values[2]) &&
148 isfinite(values[3]) && isfinite(values[4]) && isfinite(values[5])))
149 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
150 /* AT then UP */
151 listener->Forward[0] = values[0];
152 listener->Forward[1] = values[1];
153 listener->Forward[2] = values[2];
154 listener->Up[0] = values[3];
155 listener->Up[1] = values[4];
156 listener->Up[2] = values[5];
157 DO_UPDATEPROPS();
158 break;
160 default:
161 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
164 done:
165 WriteUnlock(&context->PropLock);
166 ALCcontext_DecRef(context);
170 AL_API ALvoid AL_APIENTRY alListeneri(ALenum param, ALint UNUSED(value))
172 ALCcontext *context;
174 context = GetContextRef();
175 if(!context) return;
177 WriteLock(&context->PropLock);
178 switch(param)
180 default:
181 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
184 done:
185 WriteUnlock(&context->PropLock);
186 ALCcontext_DecRef(context);
190 AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3)
192 ALCcontext *context;
194 switch(param)
196 case AL_POSITION:
197 case AL_VELOCITY:
198 alListener3f(param, (ALfloat)value1, (ALfloat)value2, (ALfloat)value3);
199 return;
202 context = GetContextRef();
203 if(!context) return;
205 WriteLock(&context->PropLock);
206 switch(param)
208 default:
209 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
212 done:
213 WriteUnlock(&context->PropLock);
214 ALCcontext_DecRef(context);
218 AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values)
220 ALCcontext *context;
222 if(values)
224 ALfloat fvals[6];
225 switch(param)
227 case AL_POSITION:
228 case AL_VELOCITY:
229 alListener3f(param, (ALfloat)values[0], (ALfloat)values[1], (ALfloat)values[2]);
230 return;
232 case AL_ORIENTATION:
233 fvals[0] = (ALfloat)values[0];
234 fvals[1] = (ALfloat)values[1];
235 fvals[2] = (ALfloat)values[2];
236 fvals[3] = (ALfloat)values[3];
237 fvals[4] = (ALfloat)values[4];
238 fvals[5] = (ALfloat)values[5];
239 alListenerfv(param, fvals);
240 return;
244 context = GetContextRef();
245 if(!context) return;
247 WriteLock(&context->PropLock);
248 if(!(values))
249 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
250 switch(param)
252 default:
253 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
256 done:
257 WriteUnlock(&context->PropLock);
258 ALCcontext_DecRef(context);
262 AL_API ALvoid AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value)
264 ALCcontext *context;
266 context = GetContextRef();
267 if(!context) return;
269 ReadLock(&context->PropLock);
270 if(!(value))
271 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
272 switch(param)
274 case AL_GAIN:
275 *value = context->Listener->Gain;
276 break;
278 case AL_METERS_PER_UNIT:
279 *value = context->MetersPerUnit;
280 break;
282 default:
283 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
286 done:
287 ReadUnlock(&context->PropLock);
288 ALCcontext_DecRef(context);
292 AL_API ALvoid AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3)
294 ALCcontext *context;
296 context = GetContextRef();
297 if(!context) return;
299 ReadLock(&context->PropLock);
300 if(!(value1 && value2 && value3))
301 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
302 switch(param)
304 case AL_POSITION:
305 *value1 = context->Listener->Position[0];
306 *value2 = context->Listener->Position[1];
307 *value3 = context->Listener->Position[2];
308 break;
310 case AL_VELOCITY:
311 *value1 = context->Listener->Velocity[0];
312 *value2 = context->Listener->Velocity[1];
313 *value3 = context->Listener->Velocity[2];
314 break;
316 default:
317 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
320 done:
321 ReadUnlock(&context->PropLock);
322 ALCcontext_DecRef(context);
326 AL_API ALvoid AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values)
328 ALCcontext *context;
330 switch(param)
332 case AL_GAIN:
333 case AL_METERS_PER_UNIT:
334 alGetListenerf(param, values);
335 return;
337 case AL_POSITION:
338 case AL_VELOCITY:
339 alGetListener3f(param, values+0, values+1, values+2);
340 return;
343 context = GetContextRef();
344 if(!context) return;
346 ReadLock(&context->PropLock);
347 if(!(values))
348 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
349 switch(param)
351 case AL_ORIENTATION:
352 // AT then UP
353 values[0] = context->Listener->Forward[0];
354 values[1] = context->Listener->Forward[1];
355 values[2] = context->Listener->Forward[2];
356 values[3] = context->Listener->Up[0];
357 values[4] = context->Listener->Up[1];
358 values[5] = context->Listener->Up[2];
359 break;
361 default:
362 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
365 done:
366 ReadUnlock(&context->PropLock);
367 ALCcontext_DecRef(context);
371 AL_API ALvoid AL_APIENTRY alGetListeneri(ALenum param, ALint *value)
373 ALCcontext *context;
375 context = GetContextRef();
376 if(!context) return;
378 ReadLock(&context->PropLock);
379 if(!(value))
380 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
381 switch(param)
383 default:
384 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
387 done:
388 ReadUnlock(&context->PropLock);
389 ALCcontext_DecRef(context);
393 AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3)
395 ALCcontext *context;
397 context = GetContextRef();
398 if(!context) return;
400 ReadLock(&context->PropLock);
401 if(!(value1 && value2 && value3))
402 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
403 switch (param)
405 case AL_POSITION:
406 *value1 = (ALint)context->Listener->Position[0];
407 *value2 = (ALint)context->Listener->Position[1];
408 *value3 = (ALint)context->Listener->Position[2];
409 break;
411 case AL_VELOCITY:
412 *value1 = (ALint)context->Listener->Velocity[0];
413 *value2 = (ALint)context->Listener->Velocity[1];
414 *value3 = (ALint)context->Listener->Velocity[2];
415 break;
417 default:
418 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
421 done:
422 ReadUnlock(&context->PropLock);
423 ALCcontext_DecRef(context);
427 AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint* values)
429 ALCcontext *context;
431 switch(param)
433 case AL_POSITION:
434 case AL_VELOCITY:
435 alGetListener3i(param, values+0, values+1, values+2);
436 return;
439 context = GetContextRef();
440 if(!context) return;
442 ReadLock(&context->PropLock);
443 if(!(values))
444 SET_ERROR_AND_GOTO(context, AL_INVALID_VALUE, done);
445 switch(param)
447 case AL_ORIENTATION:
448 // AT then UP
449 values[0] = (ALint)context->Listener->Forward[0];
450 values[1] = (ALint)context->Listener->Forward[1];
451 values[2] = (ALint)context->Listener->Forward[2];
452 values[3] = (ALint)context->Listener->Up[0];
453 values[4] = (ALint)context->Listener->Up[1];
454 values[5] = (ALint)context->Listener->Up[2];
455 break;
457 default:
458 SET_ERROR_AND_GOTO(context, AL_INVALID_ENUM, done);
461 done:
462 ReadUnlock(&context->PropLock);
463 ALCcontext_DecRef(context);
467 void UpdateListenerProps(ALCcontext *context)
469 ALlistener *listener = context->Listener;
470 struct ALlistenerProps *props;
472 /* Get an unused proprty container, or allocate a new one as needed. */
473 props = ATOMIC_LOAD(&listener->FreeList, almemory_order_acquire);
474 if(!props)
475 props = al_calloc(16, sizeof(*props));
476 else
478 struct ALlistenerProps *next;
479 do {
480 next = ATOMIC_LOAD(&props->next, almemory_order_relaxed);
481 } while(ATOMIC_COMPARE_EXCHANGE_PTR_WEAK(&listener->FreeList, &props, next,
482 almemory_order_seq_cst, almemory_order_acquire) == 0);
485 /* Copy in current property values. */
486 props->Position[0] = listener->Position[0];
487 props->Position[1] = listener->Position[1];
488 props->Position[2] = listener->Position[2];
490 props->Velocity[0] = listener->Velocity[0];
491 props->Velocity[1] = listener->Velocity[1];
492 props->Velocity[2] = listener->Velocity[2];
494 props->Forward[0] = listener->Forward[0];
495 props->Forward[1] = listener->Forward[1];
496 props->Forward[2] = listener->Forward[2];
497 props->Up[0] = listener->Up[0];
498 props->Up[1] = listener->Up[1];
499 props->Up[2] = listener->Up[2];
501 props->Gain = listener->Gain;
503 /* Set the new container for updating internal parameters. */
504 props = ATOMIC_EXCHANGE_PTR(&listener->Update, props, almemory_order_acq_rel);
505 if(props)
507 /* If there was an unused update container, put it back in the
508 * freelist.
510 ATOMIC_REPLACE_HEAD(struct ALlistenerProps*, &listener->FreeList, props);