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
34 #include "alListener.h"
35 #include "alAuxEffectSlot.h"
38 #include "mixer_defs.h"
41 static_assert((INT_MAX
>>FRACTIONBITS
)/MAX_PITCH
> BUFFERSIZE
,
42 "MAX_PITCH and/or BUFFERSIZE are too large for FRACTIONBITS!");
44 extern inline void InitiatePositionArrays(ALsizei frac
, ALint increment
, ALsizei
*restrict frac_arr
, ALint
*restrict pos_arr
, ALsizei size
);
47 /* BSinc requires up to 11 extra samples before the current position, and 12 after. */
48 static_assert(MAX_PRE_SAMPLES
>= 11, "MAX_PRE_SAMPLES must be at least 11!");
49 static_assert(MAX_POST_SAMPLES
>= 12, "MAX_POST_SAMPLES must be at least 12!");
52 enum Resampler ResamplerDefault
= LinearResampler
;
54 static MixerFunc MixSamples
= Mix_C
;
55 static HrtfMixerFunc MixHrtfSamples
= MixHrtf_C
;
56 HrtfMixerBlendFunc MixHrtfBlendSamples
= MixHrtfBlend_C
;
58 MixerFunc
SelectMixer(void)
61 if((CPUCapFlags
&CPU_CAP_NEON
))
65 if((CPUCapFlags
&CPU_CAP_SSE
))
71 RowMixerFunc
SelectRowMixer(void)
74 if((CPUCapFlags
&CPU_CAP_NEON
))
78 if((CPUCapFlags
&CPU_CAP_SSE
))
84 static inline HrtfMixerFunc
SelectHrtfMixer(void)
87 if((CPUCapFlags
&CPU_CAP_NEON
))
91 if((CPUCapFlags
&CPU_CAP_SSE
))
97 static inline HrtfMixerBlendFunc
SelectHrtfBlendMixer(void)
100 if((CPUCapFlags
&CPU_CAP_NEON
))
101 return MixHrtfBlend_Neon
;
104 if((CPUCapFlags
&CPU_CAP_SSE
))
105 return MixHrtfBlend_SSE
;
107 return MixHrtfBlend_C
;
110 ResamplerFunc
SelectResampler(enum Resampler resampler
)
115 return Resample_point32_C
;
116 case LinearResampler
:
118 if((CPUCapFlags
&CPU_CAP_NEON
))
119 return Resample_lerp32_Neon
;
122 if((CPUCapFlags
&CPU_CAP_SSE4_1
))
123 return Resample_lerp32_SSE41
;
126 if((CPUCapFlags
&CPU_CAP_SSE2
))
127 return Resample_lerp32_SSE2
;
129 return Resample_lerp32_C
;
132 if((CPUCapFlags
&CPU_CAP_NEON
))
133 return Resample_fir4_32_Neon
;
136 if((CPUCapFlags
&CPU_CAP_SSE4_1
))
137 return Resample_fir4_32_SSE41
;
140 if((CPUCapFlags
&CPU_CAP_SSE3
))
141 return Resample_fir4_32_SSE3
;
143 return Resample_fir4_32_C
;
146 if((CPUCapFlags
&CPU_CAP_NEON
))
147 return Resample_bsinc32_Neon
;
150 if((CPUCapFlags
&CPU_CAP_SSE
))
151 return Resample_bsinc32_SSE
;
153 return Resample_bsinc32_C
;
156 return Resample_point32_C
;
160 void aluInitMixer(void)
164 if(ConfigValueStr(NULL
, NULL
, "resampler", &str
))
166 if(strcasecmp(str
, "point") == 0 || strcasecmp(str
, "none") == 0)
167 ResamplerDefault
= PointResampler
;
168 else if(strcasecmp(str
, "linear") == 0)
169 ResamplerDefault
= LinearResampler
;
170 else if(strcasecmp(str
, "sinc4") == 0)
171 ResamplerDefault
= FIR4Resampler
;
172 else if(strcasecmp(str
, "bsinc") == 0)
173 ResamplerDefault
= BSincResampler
;
174 else if(strcasecmp(str
, "cubic") == 0 || strcasecmp(str
, "sinc8") == 0)
176 WARN("Resampler option \"%s\" is deprecated, using sinc4\n", str
);
177 ResamplerDefault
= FIR4Resampler
;
182 long n
= strtol(str
, &end
, 0);
183 if(*end
== '\0' && (n
== PointResampler
|| n
== LinearResampler
|| n
== FIR4Resampler
))
184 ResamplerDefault
= n
;
186 WARN("Invalid resampler: %s\n", str
);
190 MixHrtfBlendSamples
= SelectHrtfBlendMixer();
191 MixHrtfSamples
= SelectHrtfMixer();
192 MixSamples
= SelectMixer();
196 static inline ALfloat
Sample_ALbyte(ALbyte val
)
197 { return val
* (1.0f
/128.0f
); }
199 static inline ALfloat
Sample_ALshort(ALshort val
)
200 { return val
* (1.0f
/32768.0f
); }
202 static inline ALfloat
Sample_ALfloat(ALfloat val
)
205 #define DECL_TEMPLATE(T) \
206 static inline void Load_##T(ALfloat *dst, const T *src, ALint srcstep, ALsizei samples)\
209 for(i = 0;i < samples;i++) \
210 dst[i] = Sample_##T(src[i*srcstep]); \
213 DECL_TEMPLATE(ALbyte
)
214 DECL_TEMPLATE(ALshort
)
215 DECL_TEMPLATE(ALfloat
)
219 static void LoadSamples(ALfloat
*dst
, const ALvoid
*src
, ALint srcstep
, enum FmtType srctype
, ALsizei samples
)
224 Load_ALbyte(dst
, src
, srcstep
, samples
);
227 Load_ALshort(dst
, src
, srcstep
, samples
);
230 Load_ALfloat(dst
, src
, srcstep
, samples
);
235 static inline void SilenceSamples(ALfloat
*dst
, ALsizei samples
)
238 for(i
= 0;i
< samples
;i
++)
243 static const ALfloat
*DoFilters(ALfilterState
*lpfilter
, ALfilterState
*hpfilter
,
244 ALfloat
*restrict dst
, const ALfloat
*restrict src
,
245 ALsizei numsamples
, enum ActiveFilters type
)
251 ALfilterState_processPassthru(lpfilter
, src
, numsamples
);
252 ALfilterState_processPassthru(hpfilter
, src
, numsamples
);
256 ALfilterState_process(lpfilter
, dst
, src
, numsamples
);
257 ALfilterState_processPassthru(hpfilter
, dst
, numsamples
);
260 ALfilterState_processPassthru(lpfilter
, src
, numsamples
);
261 ALfilterState_process(hpfilter
, dst
, src
, numsamples
);
265 for(i
= 0;i
< numsamples
;)
268 ALsizei todo
= mini(256, numsamples
-i
);
270 ALfilterState_process(lpfilter
, temp
, src
+i
, todo
);
271 ALfilterState_process(hpfilter
, dst
+i
, temp
, todo
);
280 ALboolean
MixSource(ALvoice
*voice
, ALsource
*Source
, ALCdevice
*Device
, ALsizei SamplesToDo
)
282 ALbufferlistitem
*BufferListItem
;
283 ALbufferlistitem
*BufferLoopItem
;
284 ALsizei NumChannels
, SampleSize
;
285 ResamplerFunc Resample
;
298 /* Get source info */
299 isplaying
= true; /* Will only be called while playing. */
300 DataPosInt
= ATOMIC_LOAD(&voice
->position
, almemory_order_acquire
);
301 DataPosFrac
= ATOMIC_LOAD(&voice
->position_fraction
, almemory_order_relaxed
);
302 BufferListItem
= ATOMIC_LOAD(&voice
->current_buffer
, almemory_order_relaxed
);
303 BufferLoopItem
= ATOMIC_LOAD(&voice
->loop_buffer
, almemory_order_relaxed
);
304 NumChannels
= voice
->NumChannels
;
305 SampleSize
= voice
->SampleSize
;
306 increment
= voice
->Step
;
308 IrSize
= (Device
->HrtfHandle
? Device
->HrtfHandle
->irSize
: 0);
310 Resample
= ((increment
== FRACTIONONE
&& DataPosFrac
== 0) ?
311 Resample_copy32_C
: voice
->Resampler
);
313 Counter
= (voice
->Flags
&VOICE_IS_FADING
) ? SamplesToDo
: 0;
318 ALsizei SrcBufferSize
, DstBufferSize
;
320 /* Figure out how many buffer samples will be needed */
321 DataSize64
= SamplesToDo
-OutPos
;
322 DataSize64
*= increment
;
323 DataSize64
+= DataPosFrac
+FRACTIONMASK
;
324 DataSize64
>>= FRACTIONBITS
;
325 DataSize64
+= MAX_POST_SAMPLES
+MAX_PRE_SAMPLES
;
327 SrcBufferSize
= (ALsizei
)mini64(DataSize64
, BUFFERSIZE
);
329 /* Figure out how many samples we can actually mix from this. */
330 DataSize64
= SrcBufferSize
;
331 DataSize64
-= MAX_POST_SAMPLES
+MAX_PRE_SAMPLES
;
332 DataSize64
<<= FRACTIONBITS
;
333 DataSize64
-= DataPosFrac
;
335 DstBufferSize
= (ALsizei
)((DataSize64
+(increment
-1)) / increment
);
336 DstBufferSize
= mini(DstBufferSize
, (SamplesToDo
-OutPos
));
338 /* Some mixers like having a multiple of 4, so try to give that unless
339 * this is the last update. */
340 if(OutPos
+DstBufferSize
< SamplesToDo
)
343 for(chan
= 0;chan
< NumChannels
;chan
++)
345 const ALfloat
*ResampledData
;
346 ALfloat
*SrcData
= Device
->SourceData
;
349 /* Load the previous samples into the source data first. */
350 memcpy(SrcData
, voice
->PrevSamples
[chan
], MAX_PRE_SAMPLES
*sizeof(ALfloat
));
351 SrcDataSize
= MAX_PRE_SAMPLES
;
353 if(Source
->SourceType
== AL_STATIC
)
355 const ALbuffer
*ALBuffer
= BufferListItem
->buffer
;
356 const ALubyte
*Data
= ALBuffer
->data
;
359 /* Offset buffer data to current channel */
360 Data
+= chan
*SampleSize
;
362 /* If current pos is beyond the loop range, do not loop */
363 if(!BufferLoopItem
|| DataPosInt
>= ALBuffer
->LoopEnd
)
365 BufferLoopItem
= NULL
;
367 /* Load what's left to play from the source buffer, and
368 * clear the rest of the temp buffer */
369 DataSize
= minu(SrcBufferSize
- SrcDataSize
,
370 ALBuffer
->SampleLen
- DataPosInt
);
372 LoadSamples(&SrcData
[SrcDataSize
], &Data
[DataPosInt
* NumChannels
*SampleSize
],
373 NumChannels
, ALBuffer
->FmtType
, DataSize
);
374 SrcDataSize
+= DataSize
;
376 SilenceSamples(&SrcData
[SrcDataSize
], SrcBufferSize
- SrcDataSize
);
377 SrcDataSize
+= SrcBufferSize
- SrcDataSize
;
381 ALsizei LoopStart
= ALBuffer
->LoopStart
;
382 ALsizei LoopEnd
= ALBuffer
->LoopEnd
;
384 /* Load what's left of this loop iteration, then load
385 * repeats of the loop section */
386 DataSize
= minu(SrcBufferSize
- SrcDataSize
, LoopEnd
- DataPosInt
);
388 LoadSamples(&SrcData
[SrcDataSize
], &Data
[DataPosInt
* NumChannels
*SampleSize
],
389 NumChannels
, ALBuffer
->FmtType
, DataSize
);
390 SrcDataSize
+= DataSize
;
392 DataSize
= LoopEnd
-LoopStart
;
393 while(SrcBufferSize
> SrcDataSize
)
395 DataSize
= mini(SrcBufferSize
- SrcDataSize
, DataSize
);
397 LoadSamples(&SrcData
[SrcDataSize
], &Data
[LoopStart
* NumChannels
*SampleSize
],
398 NumChannels
, ALBuffer
->FmtType
, DataSize
);
399 SrcDataSize
+= DataSize
;
405 /* Crawl the buffer queue to fill in the temp buffer */
406 ALbufferlistitem
*tmpiter
= BufferListItem
;
407 ALsizei pos
= DataPosInt
;
409 while(tmpiter
&& SrcBufferSize
> SrcDataSize
)
411 const ALbuffer
*ALBuffer
;
412 if((ALBuffer
=tmpiter
->buffer
) != NULL
)
414 const ALubyte
*Data
= ALBuffer
->data
;
415 ALsizei DataSize
= ALBuffer
->SampleLen
;
417 /* Skip the data already played */
422 Data
+= (pos
*NumChannels
+ chan
)*SampleSize
;
426 DataSize
= minu(SrcBufferSize
- SrcDataSize
, DataSize
);
427 LoadSamples(&SrcData
[SrcDataSize
], Data
, NumChannels
,
428 ALBuffer
->FmtType
, DataSize
);
429 SrcDataSize
+= DataSize
;
432 tmpiter
= ATOMIC_LOAD(&tmpiter
->next
, almemory_order_acquire
);
433 if(!tmpiter
&& BufferLoopItem
)
434 tmpiter
= BufferLoopItem
;
437 SilenceSamples(&SrcData
[SrcDataSize
], SrcBufferSize
- SrcDataSize
);
438 SrcDataSize
+= SrcBufferSize
- SrcDataSize
;
443 /* Store the last source samples used for next time. */
444 memcpy(voice
->PrevSamples
[chan
],
445 &SrcData
[(increment
*DstBufferSize
+ DataPosFrac
)>>FRACTIONBITS
],
446 MAX_PRE_SAMPLES
*sizeof(ALfloat
)
449 /* Now resample, then filter and mix to the appropriate outputs. */
450 ResampledData
= Resample(&voice
->ResampleState
,
451 &SrcData
[MAX_PRE_SAMPLES
], DataPosFrac
, increment
,
452 Device
->ResampledData
, DstBufferSize
455 DirectParams
*parms
= &voice
->Direct
.Params
[chan
];
456 const ALfloat
*samples
;
459 &parms
->LowPass
, &parms
->HighPass
, Device
->FilteredData
,
460 ResampledData
, DstBufferSize
, voice
->Direct
.FilterType
462 if(!(voice
->Flags
&VOICE_HAS_HRTF
))
465 memcpy(parms
->Gains
.Current
, parms
->Gains
.Target
,
466 sizeof(parms
->Gains
.Current
));
467 if(!(voice
->Flags
&VOICE_HAS_NFC
))
468 MixSamples(samples
, voice
->Direct
.Channels
, voice
->Direct
.Buffer
,
469 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
,
474 ALfloat
*nfcsamples
= Device
->NFCtrlData
;
475 ALsizei chanoffset
= 0;
478 voice
->Direct
.ChannelsPerOrder
[0], voice
->Direct
.Buffer
,
479 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
,
482 chanoffset
+= voice
->Direct
.ChannelsPerOrder
[0];
483 #define APPLY_NFC_MIX(order) \
484 if(voice->Direct.ChannelsPerOrder[order] > 0) \
486 NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], nfcsamples, \
487 samples, DstBufferSize); \
488 MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
489 voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
490 parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
492 chanoffset += voice->Direct.ChannelsPerOrder[order]; \
502 MixHrtfParams hrtfparams
;
506 lidx
= GetChannelIdxByName(Device
->RealOut
, FrontLeft
);
507 ridx
= GetChannelIdxByName(Device
->RealOut
, FrontRight
);
508 assert(lidx
!= -1 && ridx
!= -1);
512 /* No fading, just overwrite the old HRTF params. */
513 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
515 else if(!(parms
->Hrtf
.Old
.Gain
> GAIN_SILENCE_THRESHOLD
))
517 /* The old HRTF params are silent, so overwrite the old
518 * coefficients with the new, and reset the old gain to
519 * 0. The future mix will then fade from silence.
521 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
522 parms
->Hrtf
.Old
.Gain
= 0.0f
;
528 /* Fade between the coefficients over 128 samples. */
529 fademix
= mini(DstBufferSize
, 128);
531 /* The new coefficients need to fade in completely
532 * since they're replacing the old ones. To keep the
533 * gain fading consistent, interpolate between the old
534 * and new target gains given how much of the fade time
537 gain
= lerp(parms
->Hrtf
.Old
.Gain
, parms
->Hrtf
.Target
.Gain
,
538 minf(1.0f
, (ALfloat
)fademix
/Counter
));
539 hrtfparams
.Coeffs
= SAFE_CONST(ALfloat2
*,parms
->Hrtf
.Target
.Coeffs
);
540 hrtfparams
.Delay
[0] = parms
->Hrtf
.Target
.Delay
[0];
541 hrtfparams
.Delay
[1] = parms
->Hrtf
.Target
.Delay
[1];
542 hrtfparams
.Gain
= 0.0f
;
543 hrtfparams
.GainStep
= gain
/ (ALfloat
)fademix
;
546 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
547 samples
, voice
->Offset
, OutPos
, IrSize
, &parms
->Hrtf
.Old
,
548 &hrtfparams
, &parms
->Hrtf
.State
, fademix
550 /* Update the old parameters with the result. */
551 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
552 if(fademix
< Counter
)
553 parms
->Hrtf
.Old
.Gain
= hrtfparams
.Gain
;
556 if(fademix
< DstBufferSize
)
558 ALsizei todo
= DstBufferSize
- fademix
;
559 ALfloat gain
= parms
->Hrtf
.Target
.Gain
;
561 /* Interpolate the target gain if the gain fading lasts
562 * longer than this mix.
564 if(Counter
> DstBufferSize
)
565 gain
= lerp(parms
->Hrtf
.Old
.Gain
, gain
,
566 (ALfloat
)todo
/(Counter
-fademix
));
568 hrtfparams
.Coeffs
= SAFE_CONST(ALfloat2
*,parms
->Hrtf
.Target
.Coeffs
);
569 hrtfparams
.Delay
[0] = parms
->Hrtf
.Target
.Delay
[0];
570 hrtfparams
.Delay
[1] = parms
->Hrtf
.Target
.Delay
[1];
571 hrtfparams
.Gain
= parms
->Hrtf
.Old
.Gain
;
572 hrtfparams
.GainStep
= (gain
- parms
->Hrtf
.Old
.Gain
) / (ALfloat
)todo
;
574 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
575 samples
+fademix
, voice
->Offset
+fademix
, OutPos
+fademix
, IrSize
,
576 &hrtfparams
, &parms
->Hrtf
.State
, todo
578 /* Store the interpolated gain or the final target gain
579 * depending if the fade is done.
581 if(DstBufferSize
< Counter
)
582 parms
->Hrtf
.Old
.Gain
= gain
;
584 parms
->Hrtf
.Old
.Gain
= parms
->Hrtf
.Target
.Gain
;
589 for(send
= 0;send
< Device
->NumAuxSends
;send
++)
591 SendParams
*parms
= &voice
->Send
[send
].Params
[chan
];
592 const ALfloat
*samples
;
594 if(!voice
->Send
[send
].Buffer
)
598 &parms
->LowPass
, &parms
->HighPass
, Device
->FilteredData
,
599 ResampledData
, DstBufferSize
, voice
->Send
[send
].FilterType
603 memcpy(parms
->Gains
.Current
, parms
->Gains
.Target
,
604 sizeof(parms
->Gains
.Current
));
605 MixSamples(samples
, voice
->Send
[send
].Channels
, voice
->Send
[send
].Buffer
,
606 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
, DstBufferSize
610 /* Update positions */
611 DataPosFrac
+= increment
*DstBufferSize
;
612 DataPosInt
+= DataPosFrac
>>FRACTIONBITS
;
613 DataPosFrac
&= FRACTIONMASK
;
615 OutPos
+= DstBufferSize
;
616 voice
->Offset
+= DstBufferSize
;
617 Counter
= maxi(DstBufferSize
, Counter
) - DstBufferSize
;
620 /* Handle looping sources */
623 const ALbuffer
*ALBuffer
;
624 ALsizei DataSize
= 0;
625 ALsizei LoopStart
= 0;
628 if((ALBuffer
=BufferListItem
->buffer
) != NULL
)
630 DataSize
= ALBuffer
->SampleLen
;
631 LoopStart
= ALBuffer
->LoopStart
;
632 LoopEnd
= ALBuffer
->LoopEnd
;
633 if(LoopEnd
> DataPosInt
)
637 if(BufferLoopItem
&& Source
->SourceType
== AL_STATIC
)
639 assert(LoopEnd
> LoopStart
);
640 DataPosInt
= ((DataPosInt
-LoopStart
)%(LoopEnd
-LoopStart
)) + LoopStart
;
644 if(DataSize
> DataPosInt
)
647 BufferListItem
= ATOMIC_LOAD(&BufferListItem
->next
, almemory_order_acquire
);
650 BufferListItem
= BufferLoopItem
;
660 DataPosInt
-= DataSize
;
662 } while(isplaying
&& OutPos
< SamplesToDo
);
664 voice
->Flags
|= VOICE_IS_FADING
;
666 /* Update source info */
667 ATOMIC_STORE(&voice
->position
, DataPosInt
, almemory_order_relaxed
);
668 ATOMIC_STORE(&voice
->position_fraction
, DataPosFrac
, almemory_order_relaxed
);
669 ATOMIC_STORE(&voice
->current_buffer
, BufferListItem
, almemory_order_release
);