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_point_C
;
116 case LinearResampler
:
118 if((CPUCapFlags
&CPU_CAP_NEON
))
119 return Resample_lerp_Neon
;
122 if((CPUCapFlags
&CPU_CAP_SSE4_1
))
123 return Resample_lerp_SSE41
;
126 if((CPUCapFlags
&CPU_CAP_SSE2
))
127 return Resample_lerp_SSE2
;
129 return Resample_lerp_C
;
132 if((CPUCapFlags
&CPU_CAP_NEON
))
133 return Resample_fir4_Neon
;
136 if((CPUCapFlags
&CPU_CAP_SSE4_1
))
137 return Resample_fir4_SSE41
;
140 if((CPUCapFlags
&CPU_CAP_SSE3
))
141 return Resample_fir4_SSE3
;
143 return Resample_fir4_C
;
144 case BSinc12Resampler
:
145 case BSinc24Resampler
:
147 if((CPUCapFlags
&CPU_CAP_NEON
))
148 return Resample_bsinc_Neon
;
151 if((CPUCapFlags
&CPU_CAP_SSE
))
152 return Resample_bsinc_SSE
;
154 return Resample_bsinc_C
;
157 return Resample_point_C
;
161 void aluInitMixer(void)
165 if(ConfigValueStr(NULL
, NULL
, "resampler", &str
))
167 if(strcasecmp(str
, "point") == 0 || strcasecmp(str
, "none") == 0)
168 ResamplerDefault
= PointResampler
;
169 else if(strcasecmp(str
, "linear") == 0)
170 ResamplerDefault
= LinearResampler
;
171 else if(strcasecmp(str
, "sinc4") == 0)
172 ResamplerDefault
= FIR4Resampler
;
173 else if(strcasecmp(str
, "bsinc12") == 0)
174 ResamplerDefault
= BSinc12Resampler
;
175 else if(strcasecmp(str
, "bsinc24") == 0)
176 ResamplerDefault
= BSinc24Resampler
;
177 else if(strcasecmp(str
, "bsinc") == 0)
179 WARN("Resampler option \"%s\" is deprecated, using bsinc12\n", str
);
180 ResamplerDefault
= BSinc12Resampler
;
182 else if(strcasecmp(str
, "cubic") == 0 || strcasecmp(str
, "sinc8") == 0)
184 WARN("Resampler option \"%s\" is deprecated, using sinc4\n", str
);
185 ResamplerDefault
= FIR4Resampler
;
190 long n
= strtol(str
, &end
, 0);
191 if(*end
== '\0' && (n
== PointResampler
|| n
== LinearResampler
|| n
== FIR4Resampler
))
192 ResamplerDefault
= n
;
194 WARN("Invalid resampler: %s\n", str
);
198 MixHrtfBlendSamples
= SelectHrtfBlendMixer();
199 MixHrtfSamples
= SelectHrtfMixer();
200 MixSamples
= SelectMixer();
204 static inline ALfloat
Sample_ALbyte(ALbyte val
)
205 { return val
* (1.0f
/128.0f
); }
207 static inline ALfloat
Sample_ALshort(ALshort val
)
208 { return val
* (1.0f
/32768.0f
); }
210 static inline ALfloat
Sample_ALfloat(ALfloat val
)
213 #define DECL_TEMPLATE(T) \
214 static inline void Load_##T(ALfloat *restrict dst, const T *restrict src, \
215 ALint srcstep, ALsizei samples) \
218 for(i = 0;i < samples;i++) \
219 dst[i] = Sample_##T(src[i*srcstep]); \
222 DECL_TEMPLATE(ALbyte
)
223 DECL_TEMPLATE(ALshort
)
224 DECL_TEMPLATE(ALfloat
)
228 static void LoadSamples(ALfloat
*restrict dst
, const ALvoid
*restrict src
, ALint srcstep
,
229 enum FmtType srctype
, ALsizei samples
)
234 Load_ALbyte(dst
, src
, srcstep
, samples
);
237 Load_ALshort(dst
, src
, srcstep
, samples
);
240 Load_ALfloat(dst
, src
, srcstep
, samples
);
245 static inline void SilenceSamples(ALfloat
*dst
, ALsizei samples
)
248 for(i
= 0;i
< samples
;i
++)
253 static const ALfloat
*DoFilters(ALfilterState
*lpfilter
, ALfilterState
*hpfilter
,
254 ALfloat
*restrict dst
, const ALfloat
*restrict src
,
255 ALsizei numsamples
, enum ActiveFilters type
)
261 ALfilterState_processPassthru(lpfilter
, src
, numsamples
);
262 ALfilterState_processPassthru(hpfilter
, src
, numsamples
);
266 ALfilterState_process(lpfilter
, dst
, src
, numsamples
);
267 ALfilterState_processPassthru(hpfilter
, dst
, numsamples
);
270 ALfilterState_processPassthru(lpfilter
, src
, numsamples
);
271 ALfilterState_process(hpfilter
, dst
, src
, numsamples
);
275 for(i
= 0;i
< numsamples
;)
278 ALsizei todo
= mini(256, numsamples
-i
);
280 ALfilterState_process(lpfilter
, temp
, src
+i
, todo
);
281 ALfilterState_process(hpfilter
, dst
+i
, temp
, todo
);
290 ALboolean
MixSource(ALvoice
*voice
, ALsource
*Source
, ALCdevice
*Device
, ALsizei SamplesToDo
)
292 ALbufferlistitem
*BufferListItem
;
293 ALbufferlistitem
*BufferLoopItem
;
294 ALsizei NumChannels
, SampleSize
;
295 ResamplerFunc Resample
;
309 /* Get source info */
310 isplaying
= true; /* Will only be called while playing. */
311 isstatic
= Source
->SourceType
== AL_STATIC
;
312 DataPosInt
= ATOMIC_LOAD(&voice
->position
, almemory_order_acquire
);
313 DataPosFrac
= ATOMIC_LOAD(&voice
->position_fraction
, almemory_order_relaxed
);
314 BufferListItem
= ATOMIC_LOAD(&voice
->current_buffer
, almemory_order_relaxed
);
315 BufferLoopItem
= ATOMIC_LOAD(&voice
->loop_buffer
, almemory_order_relaxed
);
316 NumChannels
= voice
->NumChannels
;
317 SampleSize
= voice
->SampleSize
;
318 increment
= voice
->Step
;
320 IrSize
= (Device
->HrtfHandle
? Device
->HrtfHandle
->irSize
: 0);
322 Resample
= ((increment
== FRACTIONONE
&& DataPosFrac
== 0) ?
323 Resample_copy_C
: voice
->Resampler
);
325 Counter
= (voice
->Flags
&VOICE_IS_FADING
) ? SamplesToDo
: 0;
330 ALsizei SrcBufferSize
, DstBufferSize
;
332 /* Figure out how many buffer samples will be needed */
333 DataSize64
= SamplesToDo
-OutPos
;
334 DataSize64
*= increment
;
335 DataSize64
+= DataPosFrac
+FRACTIONMASK
;
336 DataSize64
>>= FRACTIONBITS
;
337 DataSize64
+= MAX_POST_SAMPLES
+MAX_PRE_SAMPLES
;
339 SrcBufferSize
= (ALsizei
)mini64(DataSize64
, BUFFERSIZE
);
341 /* Figure out how many samples we can actually mix from this. */
342 DataSize64
= SrcBufferSize
;
343 DataSize64
-= MAX_POST_SAMPLES
+MAX_PRE_SAMPLES
;
344 DataSize64
<<= FRACTIONBITS
;
345 DataSize64
-= DataPosFrac
;
347 DstBufferSize
= (ALsizei
)((DataSize64
+(increment
-1)) / increment
);
348 DstBufferSize
= mini(DstBufferSize
, (SamplesToDo
-OutPos
));
350 /* Some mixers like having a multiple of 4, so try to give that unless
351 * this is the last update. */
352 if(OutPos
+DstBufferSize
< SamplesToDo
)
355 for(chan
= 0;chan
< NumChannels
;chan
++)
357 const ALfloat
*ResampledData
;
358 ALfloat
*SrcData
= Device
->SourceData
;
361 /* Load the previous samples into the source data first. */
362 memcpy(SrcData
, voice
->PrevSamples
[chan
], MAX_PRE_SAMPLES
*sizeof(ALfloat
));
363 SrcDataSize
= MAX_PRE_SAMPLES
;
367 const ALbuffer
*ALBuffer
= BufferListItem
->buffer
;
368 const ALubyte
*Data
= ALBuffer
->data
;
371 /* Offset buffer data to current channel */
372 Data
+= chan
*SampleSize
;
374 /* If current pos is beyond the loop range, do not loop */
375 if(!BufferLoopItem
|| DataPosInt
>= ALBuffer
->LoopEnd
)
377 BufferLoopItem
= NULL
;
379 /* Load what's left to play from the source buffer, and
380 * clear the rest of the temp buffer */
381 DataSize
= minu(SrcBufferSize
- SrcDataSize
,
382 ALBuffer
->SampleLen
- DataPosInt
);
384 LoadSamples(&SrcData
[SrcDataSize
], &Data
[DataPosInt
* NumChannels
*SampleSize
],
385 NumChannels
, ALBuffer
->FmtType
, DataSize
);
386 SrcDataSize
+= DataSize
;
388 SilenceSamples(&SrcData
[SrcDataSize
], SrcBufferSize
- SrcDataSize
);
389 SrcDataSize
+= SrcBufferSize
- SrcDataSize
;
393 ALsizei LoopStart
= ALBuffer
->LoopStart
;
394 ALsizei LoopEnd
= ALBuffer
->LoopEnd
;
396 /* Load what's left of this loop iteration, then load
397 * repeats of the loop section */
398 DataSize
= minu(SrcBufferSize
- SrcDataSize
, LoopEnd
- DataPosInt
);
400 LoadSamples(&SrcData
[SrcDataSize
], &Data
[DataPosInt
* NumChannels
*SampleSize
],
401 NumChannels
, ALBuffer
->FmtType
, DataSize
);
402 SrcDataSize
+= DataSize
;
404 DataSize
= LoopEnd
-LoopStart
;
405 while(SrcBufferSize
> SrcDataSize
)
407 DataSize
= mini(SrcBufferSize
- SrcDataSize
, DataSize
);
409 LoadSamples(&SrcData
[SrcDataSize
], &Data
[LoopStart
* NumChannels
*SampleSize
],
410 NumChannels
, ALBuffer
->FmtType
, DataSize
);
411 SrcDataSize
+= DataSize
;
417 /* Crawl the buffer queue to fill in the temp buffer */
418 ALbufferlistitem
*tmpiter
= BufferListItem
;
419 ALsizei pos
= DataPosInt
;
421 while(tmpiter
&& SrcBufferSize
> SrcDataSize
)
423 const ALbuffer
*ALBuffer
;
424 if((ALBuffer
=tmpiter
->buffer
) != NULL
)
426 const ALubyte
*Data
= ALBuffer
->data
;
427 ALsizei DataSize
= ALBuffer
->SampleLen
;
429 /* Skip the data already played */
434 Data
+= (pos
*NumChannels
+ chan
)*SampleSize
;
438 DataSize
= minu(SrcBufferSize
- SrcDataSize
, DataSize
);
439 LoadSamples(&SrcData
[SrcDataSize
], Data
, NumChannels
,
440 ALBuffer
->FmtType
, DataSize
);
441 SrcDataSize
+= DataSize
;
444 tmpiter
= ATOMIC_LOAD(&tmpiter
->next
, almemory_order_acquire
);
445 if(!tmpiter
&& BufferLoopItem
)
446 tmpiter
= BufferLoopItem
;
449 SilenceSamples(&SrcData
[SrcDataSize
], SrcBufferSize
- SrcDataSize
);
450 SrcDataSize
+= SrcBufferSize
- SrcDataSize
;
455 /* Store the last source samples used for next time. */
456 memcpy(voice
->PrevSamples
[chan
],
457 &SrcData
[(increment
*DstBufferSize
+ DataPosFrac
)>>FRACTIONBITS
],
458 MAX_PRE_SAMPLES
*sizeof(ALfloat
)
461 /* Now resample, then filter and mix to the appropriate outputs. */
462 ResampledData
= Resample(&voice
->ResampleState
,
463 &SrcData
[MAX_PRE_SAMPLES
], DataPosFrac
, increment
,
464 Device
->ResampledData
, DstBufferSize
467 DirectParams
*parms
= &voice
->Direct
.Params
[chan
];
468 const ALfloat
*samples
;
471 &parms
->LowPass
, &parms
->HighPass
, Device
->FilteredData
,
472 ResampledData
, DstBufferSize
, voice
->Direct
.FilterType
474 if(!(voice
->Flags
&VOICE_HAS_HRTF
))
477 memcpy(parms
->Gains
.Current
, parms
->Gains
.Target
,
478 sizeof(parms
->Gains
.Current
));
479 if(!(voice
->Flags
&VOICE_HAS_NFC
))
480 MixSamples(samples
, voice
->Direct
.Channels
, voice
->Direct
.Buffer
,
481 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
,
486 ALfloat
*nfcsamples
= Device
->NFCtrlData
;
487 ALsizei chanoffset
= 0;
490 voice
->Direct
.ChannelsPerOrder
[0], voice
->Direct
.Buffer
,
491 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
,
494 chanoffset
+= voice
->Direct
.ChannelsPerOrder
[0];
495 #define APPLY_NFC_MIX(order) \
496 if(voice->Direct.ChannelsPerOrder[order] > 0) \
498 NfcFilterUpdate##order(&parms->NFCtrlFilter[order-1], nfcsamples, \
499 samples, DstBufferSize); \
500 MixSamples(nfcsamples, voice->Direct.ChannelsPerOrder[order], \
501 voice->Direct.Buffer+chanoffset, parms->Gains.Current+chanoffset, \
502 parms->Gains.Target+chanoffset, Counter, OutPos, DstBufferSize \
504 chanoffset += voice->Direct.ChannelsPerOrder[order]; \
514 MixHrtfParams hrtfparams
;
518 lidx
= GetChannelIdxByName(Device
->RealOut
, FrontLeft
);
519 ridx
= GetChannelIdxByName(Device
->RealOut
, FrontRight
);
520 assert(lidx
!= -1 && ridx
!= -1);
524 /* No fading, just overwrite the old HRTF params. */
525 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
527 else if(!(parms
->Hrtf
.Old
.Gain
> GAIN_SILENCE_THRESHOLD
))
529 /* The old HRTF params are silent, so overwrite the old
530 * coefficients with the new, and reset the old gain to
531 * 0. The future mix will then fade from silence.
533 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
534 parms
->Hrtf
.Old
.Gain
= 0.0f
;
540 /* Fade between the coefficients over 128 samples. */
541 fademix
= mini(DstBufferSize
, 128);
543 /* The new coefficients need to fade in completely
544 * since they're replacing the old ones. To keep the
545 * gain fading consistent, interpolate between the old
546 * and new target gains given how much of the fade time
549 gain
= lerp(parms
->Hrtf
.Old
.Gain
, parms
->Hrtf
.Target
.Gain
,
550 minf(1.0f
, (ALfloat
)fademix
/Counter
));
551 hrtfparams
.Coeffs
= SAFE_CONST(ALfloat2
*,parms
->Hrtf
.Target
.Coeffs
);
552 hrtfparams
.Delay
[0] = parms
->Hrtf
.Target
.Delay
[0];
553 hrtfparams
.Delay
[1] = parms
->Hrtf
.Target
.Delay
[1];
554 hrtfparams
.Gain
= 0.0f
;
555 hrtfparams
.GainStep
= gain
/ (ALfloat
)fademix
;
558 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
559 samples
, voice
->Offset
, OutPos
, IrSize
, &parms
->Hrtf
.Old
,
560 &hrtfparams
, &parms
->Hrtf
.State
, fademix
562 /* Update the old parameters with the result. */
563 parms
->Hrtf
.Old
= parms
->Hrtf
.Target
;
564 if(fademix
< Counter
)
565 parms
->Hrtf
.Old
.Gain
= hrtfparams
.Gain
;
568 if(fademix
< DstBufferSize
)
570 ALsizei todo
= DstBufferSize
- fademix
;
571 ALfloat gain
= parms
->Hrtf
.Target
.Gain
;
573 /* Interpolate the target gain if the gain fading lasts
574 * longer than this mix.
576 if(Counter
> DstBufferSize
)
577 gain
= lerp(parms
->Hrtf
.Old
.Gain
, gain
,
578 (ALfloat
)todo
/(Counter
-fademix
));
580 hrtfparams
.Coeffs
= SAFE_CONST(ALfloat2
*,parms
->Hrtf
.Target
.Coeffs
);
581 hrtfparams
.Delay
[0] = parms
->Hrtf
.Target
.Delay
[0];
582 hrtfparams
.Delay
[1] = parms
->Hrtf
.Target
.Delay
[1];
583 hrtfparams
.Gain
= parms
->Hrtf
.Old
.Gain
;
584 hrtfparams
.GainStep
= (gain
- parms
->Hrtf
.Old
.Gain
) / (ALfloat
)todo
;
586 voice
->Direct
.Buffer
[lidx
], voice
->Direct
.Buffer
[ridx
],
587 samples
+fademix
, voice
->Offset
+fademix
, OutPos
+fademix
, IrSize
,
588 &hrtfparams
, &parms
->Hrtf
.State
, todo
590 /* Store the interpolated gain or the final target gain
591 * depending if the fade is done.
593 if(DstBufferSize
< Counter
)
594 parms
->Hrtf
.Old
.Gain
= gain
;
596 parms
->Hrtf
.Old
.Gain
= parms
->Hrtf
.Target
.Gain
;
601 for(send
= 0;send
< Device
->NumAuxSends
;send
++)
603 SendParams
*parms
= &voice
->Send
[send
].Params
[chan
];
604 const ALfloat
*samples
;
606 if(!voice
->Send
[send
].Buffer
)
610 &parms
->LowPass
, &parms
->HighPass
, Device
->FilteredData
,
611 ResampledData
, DstBufferSize
, voice
->Send
[send
].FilterType
615 memcpy(parms
->Gains
.Current
, parms
->Gains
.Target
,
616 sizeof(parms
->Gains
.Current
));
617 MixSamples(samples
, voice
->Send
[send
].Channels
, voice
->Send
[send
].Buffer
,
618 parms
->Gains
.Current
, parms
->Gains
.Target
, Counter
, OutPos
, DstBufferSize
622 /* Update positions */
623 DataPosFrac
+= increment
*DstBufferSize
;
624 DataPosInt
+= DataPosFrac
>>FRACTIONBITS
;
625 DataPosFrac
&= FRACTIONMASK
;
627 OutPos
+= DstBufferSize
;
628 voice
->Offset
+= DstBufferSize
;
629 Counter
= maxi(DstBufferSize
, Counter
) - DstBufferSize
;
632 /* Handle looping sources */
635 const ALbuffer
*ALBuffer
;
636 ALsizei DataSize
= 0;
637 ALsizei LoopStart
= 0;
640 if((ALBuffer
=BufferListItem
->buffer
) != NULL
)
642 DataSize
= ALBuffer
->SampleLen
;
643 LoopStart
= ALBuffer
->LoopStart
;
644 LoopEnd
= ALBuffer
->LoopEnd
;
645 if(LoopEnd
> DataPosInt
)
649 if(isstatic
&& BufferLoopItem
)
651 assert(LoopEnd
> LoopStart
);
652 DataPosInt
= ((DataPosInt
-LoopStart
)%(LoopEnd
-LoopStart
)) + LoopStart
;
656 if(DataSize
> DataPosInt
)
659 BufferListItem
= ATOMIC_LOAD(&BufferListItem
->next
, almemory_order_acquire
);
662 BufferListItem
= BufferLoopItem
;
672 DataPosInt
-= DataSize
;
674 } while(isplaying
&& OutPos
< SamplesToDo
);
676 voice
->Flags
|= VOICE_IS_FADING
;
678 /* Update source info */
679 ATOMIC_STORE(&voice
->position
, DataPosInt
, almemory_order_relaxed
);
680 ATOMIC_STORE(&voice
->position_fraction
, DataPosFrac
, almemory_order_relaxed
);
681 ATOMIC_STORE(&voice
->current_buffer
, BufferListItem
, almemory_order_release
);