rlm@0: /** rlm@0: * OpenAL cross platform audio library rlm@0: * Copyright (C) 1999-2007 by authors. rlm@0: * This library is free software; you can redistribute it and/or rlm@0: * modify it under the terms of the GNU Library General Public rlm@0: * License as published by the Free Software Foundation; either rlm@0: * version 2 of the License, or (at your option) any later version. rlm@0: * rlm@0: * This library is distributed in the hope that it will be useful, rlm@0: * but WITHOUT ANY WARRANTY; without even the implied warranty of rlm@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU rlm@0: * Library General Public License for more details. rlm@0: * rlm@0: * You should have received a copy of the GNU Library General Public rlm@0: * License along with this library; if not, write to the rlm@0: * Free Software Foundation, Inc., 59 Temple Place - Suite 330, rlm@0: * Boston, MA 02111-1307, USA. rlm@0: * Or go to http://www.gnu.org/copyleft/lgpl.html rlm@0: */ rlm@0: rlm@0: #include "config.h" rlm@0: rlm@0: #include rlm@0: #include rlm@0: #include rlm@0: #include rlm@0: #include rlm@0: rlm@0: #include "alMain.h" rlm@0: #include "AL/al.h" rlm@0: #include "AL/alc.h" rlm@0: #include "alSource.h" rlm@0: #include "alBuffer.h" rlm@0: #include "alListener.h" rlm@0: #include "alAuxEffectSlot.h" rlm@0: #include "alu.h" rlm@0: #include "bs2b.h" rlm@0: rlm@0: rlm@0: static __inline ALdouble point32(const ALfloat *vals, ALint step, ALint frac) rlm@0: { return vals[0]; (void)step; (void)frac; } rlm@0: static __inline ALdouble lerp32(const ALfloat *vals, ALint step, ALint frac) rlm@0: { return lerp(vals[0], vals[step], frac * (1.0/FRACTIONONE)); } rlm@0: static __inline ALdouble cubic32(const ALfloat *vals, ALint step, ALint frac) rlm@0: { return cubic(vals[-step], vals[0], vals[step], vals[step+step], rlm@0: frac * (1.0/FRACTIONONE)); } rlm@0: rlm@0: static __inline ALdouble point16(const ALshort *vals, ALint step, ALint frac) rlm@0: { return vals[0] * (1.0/32767.0); (void)step; (void)frac; } rlm@0: static __inline ALdouble lerp16(const ALshort *vals, ALint step, ALint frac) rlm@0: { return lerp(vals[0], vals[step], frac * (1.0/FRACTIONONE)) * (1.0/32767.0); } rlm@0: static __inline ALdouble cubic16(const ALshort *vals, ALint step, ALint frac) rlm@0: { return cubic(vals[-step], vals[0], vals[step], vals[step+step], rlm@0: frac * (1.0/FRACTIONONE)) * (1.0/32767.0); } rlm@0: rlm@0: static __inline ALdouble point8(const ALbyte *vals, ALint step, ALint frac) rlm@0: { return vals[0] * (1.0/127.0); (void)step; (void)frac; } rlm@0: static __inline ALdouble lerp8(const ALbyte *vals, ALint step, ALint frac) rlm@0: { return lerp(vals[0], vals[step], frac * (1.0/FRACTIONONE)) * (1.0/127.0); } rlm@0: static __inline ALdouble cubic8(const ALbyte *vals, ALint step, ALint frac) rlm@0: { return cubic(vals[-step], vals[0], vals[step], vals[step+step], rlm@0: frac * (1.0/FRACTIONONE)) * (1.0/127.0); } rlm@0: rlm@0: #ifdef __GNUC__ rlm@0: #define LIKELY(x) __builtin_expect(!!(x), 1) rlm@0: #define UNLIKELY(x) __builtin_expect(!!(x), 0) rlm@0: #else rlm@0: #define LIKELY(x) (x) rlm@0: #define UNLIKELY(x) (x) rlm@0: #endif rlm@0: rlm@0: #if defined(__ARM_NEON__) && defined(HAVE_ARM_NEON_H) rlm@0: #include rlm@0: rlm@0: static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2], rlm@0: ALfloat (*RESTRICT Coeffs)[2], rlm@0: ALfloat left, ALfloat right) rlm@0: { rlm@0: ALuint c; rlm@0: float32x4_t leftright4; rlm@0: { rlm@0: float32x2_t leftright2 = vdup_n_f32(0.0); rlm@0: leftright2 = vset_lane_f32(left, leftright2, 0); rlm@0: leftright2 = vset_lane_f32(right, leftright2, 1); rlm@0: leftright4 = vcombine_f32(leftright2, leftright2); rlm@0: } rlm@0: for(c = 0;c < HRIR_LENGTH;c += 2) rlm@0: { rlm@0: const ALuint o0 = (Offset+c)&HRIR_MASK; rlm@0: const ALuint o1 = (o0+1)&HRIR_MASK; rlm@0: float32x4_t vals = vcombine_f32(vld1_f32((float32_t*)&Values[o0][0]), rlm@0: vld1_f32((float32_t*)&Values[o1][0])); rlm@0: float32x4_t coefs = vld1q_f32((float32_t*)&Coeffs[c][0]); rlm@0: rlm@0: vals = vmlaq_f32(vals, coefs, leftright4); rlm@0: rlm@0: vst1_f32((float32_t*)&Values[o0][0], vget_low_f32(vals)); rlm@0: vst1_f32((float32_t*)&Values[o1][0], vget_high_f32(vals)); rlm@0: } rlm@0: } rlm@0: rlm@0: #else rlm@0: rlm@0: static __inline void ApplyCoeffs(ALuint Offset, ALfloat (*RESTRICT Values)[2], rlm@0: ALfloat (*RESTRICT Coeffs)[2], rlm@0: ALfloat left, ALfloat right) rlm@0: { rlm@0: ALuint c; rlm@0: for(c = 0;c < HRIR_LENGTH;c++) rlm@0: { rlm@0: const ALuint off = (Offset+c)&HRIR_MASK; rlm@0: Values[off][0] += Coeffs[c][0] * left; rlm@0: Values[off][1] += Coeffs[c][1] * right; rlm@0: } rlm@0: } rlm@0: rlm@0: #endif rlm@0: rlm@0: #define DECL_TEMPLATE(T, sampler) \ rlm@0: static void Mix_Hrtf_##T##_##sampler(ALsource *Source, ALCdevice *Device, \ rlm@0: const ALvoid *srcdata, ALuint *DataPosInt, ALuint *DataPosFrac, \ rlm@0: ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) \ rlm@0: { \ rlm@0: const ALuint NumChannels = Source->NumChannels; \ rlm@0: const T *RESTRICT data = srcdata; \ rlm@0: const ALint *RESTRICT DelayStep = Source->Params.HrtfDelayStep; \ rlm@0: ALfloat (*RESTRICT DryBuffer)[MAXCHANNELS]; \ rlm@0: ALfloat *RESTRICT ClickRemoval, *RESTRICT PendingClicks; \ rlm@0: ALfloat (*RESTRICT CoeffStep)[2] = Source->Params.HrtfCoeffStep; \ rlm@0: ALuint pos, frac; \ rlm@0: FILTER *DryFilter; \ rlm@0: ALuint BufferIdx; \ rlm@0: ALuint increment; \ rlm@0: ALuint i, out, c; \ rlm@0: ALfloat value; \ rlm@0: \ rlm@0: increment = Source->Params.Step; \ rlm@0: \ rlm@0: DryBuffer = Device->DryBuffer; \ rlm@0: ClickRemoval = Device->ClickRemoval; \ rlm@0: PendingClicks = Device->PendingClicks; \ rlm@0: DryFilter = &Source->Params.iirFilter; \ rlm@0: \ rlm@0: pos = 0; \ rlm@0: frac = *DataPosFrac; \ rlm@0: \ rlm@0: for(i = 0;i < NumChannels;i++) \ rlm@0: { \ rlm@0: ALfloat (*RESTRICT TargetCoeffs)[2] = Source->Params.HrtfCoeffs[i]; \ rlm@0: ALuint *RESTRICT TargetDelay = Source->Params.HrtfDelay[i]; \ rlm@0: ALfloat *RESTRICT History = Source->HrtfHistory[i]; \ rlm@0: ALfloat (*RESTRICT Values)[2] = Source->HrtfValues[i]; \ rlm@0: ALint Counter = maxu(Source->HrtfCounter, OutPos) - OutPos; \ rlm@0: ALuint Offset = Source->HrtfOffset + OutPos; \ rlm@0: ALfloat Coeffs[HRIR_LENGTH][2]; \ rlm@0: ALuint Delay[2]; \ rlm@0: ALfloat left, right; \ rlm@0: \ rlm@0: pos = 0; \ rlm@0: frac = *DataPosFrac; \ rlm@0: \ rlm@0: for(c = 0;c < HRIR_LENGTH;c++) \ rlm@0: { \ rlm@0: Coeffs[c][0] = TargetCoeffs[c][0] - (CoeffStep[c][0]*Counter); \ rlm@0: Coeffs[c][1] = TargetCoeffs[c][1] - (CoeffStep[c][1]*Counter); \ rlm@0: } \ rlm@0: \ rlm@0: Delay[0] = TargetDelay[0] - (DelayStep[0]*Counter) + 32768; \ rlm@0: Delay[1] = TargetDelay[1] - (DelayStep[1]*Counter) + 32768; \ rlm@0: \ rlm@0: if(LIKELY(OutPos == 0)) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ rlm@0: value = lpFilter2PC(DryFilter, i, value); \ rlm@0: \ rlm@0: History[Offset&SRC_HISTORY_MASK] = value; \ rlm@0: left = History[(Offset-(Delay[0]>>16))&SRC_HISTORY_MASK]; \ rlm@0: right = History[(Offset-(Delay[1]>>16))&SRC_HISTORY_MASK]; \ rlm@0: \ rlm@0: ClickRemoval[FRONT_LEFT] -= Values[(Offset+1)&HRIR_MASK][0] + \ rlm@0: Coeffs[0][0] * left; \ rlm@0: ClickRemoval[FRONT_RIGHT] -= Values[(Offset+1)&HRIR_MASK][1] + \ rlm@0: Coeffs[0][1] * right; \ rlm@0: } \ rlm@0: for(BufferIdx = 0;BufferIdx < BufferSize && Counter > 0;BufferIdx++) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ rlm@0: value = lpFilter2P(DryFilter, i, value); \ rlm@0: \ rlm@0: History[Offset&SRC_HISTORY_MASK] = value; \ rlm@0: left = History[(Offset-(Delay[0]>>16))&SRC_HISTORY_MASK]; \ rlm@0: right = History[(Offset-(Delay[1]>>16))&SRC_HISTORY_MASK]; \ rlm@0: \ rlm@0: Delay[0] += DelayStep[0]; \ rlm@0: Delay[1] += DelayStep[1]; \ rlm@0: \ rlm@0: Values[Offset&HRIR_MASK][0] = 0.0f; \ rlm@0: Values[Offset&HRIR_MASK][1] = 0.0f; \ rlm@0: Offset++; \ rlm@0: \ rlm@0: for(c = 0;c < HRIR_LENGTH;c++) \ rlm@0: { \ rlm@0: const ALuint off = (Offset+c)&HRIR_MASK; \ rlm@0: Values[off][0] += Coeffs[c][0] * left; \ rlm@0: Values[off][1] += Coeffs[c][1] * right; \ rlm@0: Coeffs[c][0] += CoeffStep[c][0]; \ rlm@0: Coeffs[c][1] += CoeffStep[c][1]; \ rlm@0: } \ rlm@0: \ rlm@0: DryBuffer[OutPos][FRONT_LEFT] += Values[Offset&HRIR_MASK][0]; \ rlm@0: DryBuffer[OutPos][FRONT_RIGHT] += Values[Offset&HRIR_MASK][1]; \ rlm@0: \ rlm@0: frac += increment; \ rlm@0: pos += frac>>FRACTIONBITS; \ rlm@0: frac &= FRACTIONMASK; \ rlm@0: OutPos++; \ rlm@0: Counter--; \ rlm@0: } \ rlm@0: \ rlm@0: Delay[0] >>= 16; \ rlm@0: Delay[1] >>= 16; \ rlm@0: for(;BufferIdx < BufferSize;BufferIdx++) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ rlm@0: value = lpFilter2P(DryFilter, i, value); \ rlm@0: \ rlm@0: History[Offset&SRC_HISTORY_MASK] = value; \ rlm@0: left = History[(Offset-Delay[0])&SRC_HISTORY_MASK]; \ rlm@0: right = History[(Offset-Delay[1])&SRC_HISTORY_MASK]; \ rlm@0: \ rlm@0: Values[Offset&HRIR_MASK][0] = 0.0f; \ rlm@0: Values[Offset&HRIR_MASK][1] = 0.0f; \ rlm@0: Offset++; \ rlm@0: \ rlm@0: ApplyCoeffs(Offset, Values, Coeffs, left, right); \ rlm@0: DryBuffer[OutPos][FRONT_LEFT] += Values[Offset&HRIR_MASK][0]; \ rlm@0: DryBuffer[OutPos][FRONT_RIGHT] += Values[Offset&HRIR_MASK][1]; \ rlm@0: \ rlm@0: frac += increment; \ rlm@0: pos += frac>>FRACTIONBITS; \ rlm@0: frac &= FRACTIONMASK; \ rlm@0: OutPos++; \ rlm@0: } \ rlm@0: if(LIKELY(OutPos == SamplesToDo)) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ rlm@0: value = lpFilter2PC(DryFilter, i, value); \ rlm@0: \ rlm@0: History[Offset&SRC_HISTORY_MASK] = value; \ rlm@0: left = History[(Offset-Delay[0])&SRC_HISTORY_MASK]; \ rlm@0: right = History[(Offset-Delay[1])&SRC_HISTORY_MASK]; \ rlm@0: \ rlm@0: PendingClicks[FRONT_LEFT] += Values[(Offset+1)&HRIR_MASK][0] + \ rlm@0: Coeffs[0][0] * left; \ rlm@0: PendingClicks[FRONT_RIGHT] += Values[(Offset+1)&HRIR_MASK][1] + \ rlm@0: Coeffs[0][1] * right; \ rlm@0: } \ rlm@0: OutPos -= BufferSize; \ rlm@0: } \ rlm@0: \ rlm@0: for(out = 0;out < Device->NumAuxSends;out++) \ rlm@0: { \ rlm@0: ALeffectslot *Slot = Source->Params.Send[out].Slot; \ rlm@0: ALfloat WetSend; \ rlm@0: ALfloat *RESTRICT WetBuffer; \ rlm@0: ALfloat *RESTRICT WetClickRemoval; \ rlm@0: ALfloat *RESTRICT WetPendingClicks; \ rlm@0: FILTER *WetFilter; \ rlm@0: \ rlm@0: if(!Slot || Slot->effect.type == AL_EFFECT_NULL) \ rlm@0: continue; \ rlm@0: \ rlm@0: WetBuffer = Slot->WetBuffer; \ rlm@0: WetClickRemoval = Slot->ClickRemoval; \ rlm@0: WetPendingClicks = Slot->PendingClicks; \ rlm@0: WetFilter = &Source->Params.Send[out].iirFilter; \ rlm@0: WetSend = Source->Params.Send[out].WetGain; \ rlm@0: \ rlm@0: for(i = 0;i < NumChannels;i++) \ rlm@0: { \ rlm@0: pos = 0; \ rlm@0: frac = *DataPosFrac; \ rlm@0: \ rlm@0: if(LIKELY(OutPos == 0)) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ rlm@0: value = lpFilter1PC(WetFilter, i, value); \ rlm@0: \ rlm@0: WetClickRemoval[0] -= value * WetSend; \ rlm@0: } \ rlm@0: for(BufferIdx = 0;BufferIdx < BufferSize;BufferIdx++) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ rlm@0: value = lpFilter1P(WetFilter, i, value); \ rlm@0: \ rlm@0: WetBuffer[OutPos] += value * WetSend; \ rlm@0: \ rlm@0: frac += increment; \ rlm@0: pos += frac>>FRACTIONBITS; \ rlm@0: frac &= FRACTIONMASK; \ rlm@0: OutPos++; \ rlm@0: } \ rlm@0: if(LIKELY(OutPos == SamplesToDo)) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ rlm@0: value = lpFilter1PC(WetFilter, i, value); \ rlm@0: \ rlm@0: WetPendingClicks[0] += value * WetSend; \ rlm@0: } \ rlm@0: OutPos -= BufferSize; \ rlm@0: } \ rlm@0: } \ rlm@0: *DataPosInt += pos; \ rlm@0: *DataPosFrac = frac; \ rlm@0: } rlm@0: rlm@0: DECL_TEMPLATE(ALfloat, point32) rlm@0: DECL_TEMPLATE(ALfloat, lerp32) rlm@0: DECL_TEMPLATE(ALfloat, cubic32) rlm@0: rlm@0: DECL_TEMPLATE(ALshort, point16) rlm@0: DECL_TEMPLATE(ALshort, lerp16) rlm@0: DECL_TEMPLATE(ALshort, cubic16) rlm@0: rlm@0: DECL_TEMPLATE(ALbyte, point8) rlm@0: DECL_TEMPLATE(ALbyte, lerp8) rlm@0: DECL_TEMPLATE(ALbyte, cubic8) rlm@0: rlm@0: #undef DECL_TEMPLATE rlm@0: rlm@0: rlm@0: #define DECL_TEMPLATE(T, sampler) \ rlm@0: static void Mix_##T##_##sampler(ALsource *Source, ALCdevice *Device, \ rlm@0: const ALvoid *srcdata, ALuint *DataPosInt, ALuint *DataPosFrac, \ rlm@0: ALuint OutPos, ALuint SamplesToDo, ALuint BufferSize) \ rlm@0: { \ rlm@0: const ALuint NumChannels = Source->NumChannels; \ rlm@0: const T *RESTRICT data = srcdata; \ rlm@0: ALfloat (*DryBuffer)[MAXCHANNELS]; \ rlm@0: ALfloat *ClickRemoval, *PendingClicks; \ rlm@0: ALuint pos, frac; \ rlm@0: ALfloat DrySend[MAXCHANNELS][MAXCHANNELS]; \ rlm@0: FILTER *DryFilter; \ rlm@0: ALuint BufferIdx; \ rlm@0: ALuint increment; \ rlm@0: ALuint i, out, c; \ rlm@0: ALfloat value; \ rlm@0: \ rlm@0: increment = Source->Params.Step; \ rlm@0: \ rlm@0: DryBuffer = Device->DryBuffer; \ rlm@0: ClickRemoval = Device->ClickRemoval; \ rlm@0: PendingClicks = Device->PendingClicks; \ rlm@0: DryFilter = &Source->Params.iirFilter; \ rlm@0: for(i = 0;i < NumChannels;i++) \ rlm@0: { \ rlm@0: for(c = 0;c < MAXCHANNELS;c++) \ rlm@0: DrySend[i][c] = Source->Params.DryGains[i][c]; \ rlm@0: } \ rlm@0: \ rlm@0: pos = 0; \ rlm@0: frac = *DataPosFrac; \ rlm@0: \ rlm@0: for(i = 0;i < NumChannels;i++) \ rlm@0: { \ rlm@0: pos = 0; \ rlm@0: frac = *DataPosFrac; \ rlm@0: \ rlm@0: if(OutPos == 0) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ rlm@0: \ rlm@0: value = lpFilter2PC(DryFilter, i, value); \ rlm@0: for(c = 0;c < MAXCHANNELS;c++) \ rlm@0: ClickRemoval[c] -= value*DrySend[i][c]; \ rlm@0: } \ rlm@0: for(BufferIdx = 0;BufferIdx < BufferSize;BufferIdx++) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ rlm@0: \ rlm@0: value = lpFilter2P(DryFilter, i, value); \ rlm@0: for(c = 0;c < MAXCHANNELS;c++) \ rlm@0: DryBuffer[OutPos][c] += value*DrySend[i][c]; \ rlm@0: \ rlm@0: frac += increment; \ rlm@0: pos += frac>>FRACTIONBITS; \ rlm@0: frac &= FRACTIONMASK; \ rlm@0: OutPos++; \ rlm@0: } \ rlm@0: if(OutPos == SamplesToDo) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels, frac); \ rlm@0: \ rlm@0: value = lpFilter2PC(DryFilter, i, value); \ rlm@0: for(c = 0;c < MAXCHANNELS;c++) \ rlm@0: PendingClicks[c] += value*DrySend[i][c]; \ rlm@0: } \ rlm@0: OutPos -= BufferSize; \ rlm@0: } \ rlm@0: \ rlm@0: for(out = 0;out < Device->NumAuxSends;out++) \ rlm@0: { \ rlm@0: ALeffectslot *Slot = Source->Params.Send[out].Slot; \ rlm@0: ALfloat WetSend; \ rlm@0: ALfloat *WetBuffer; \ rlm@0: ALfloat *WetClickRemoval; \ rlm@0: ALfloat *WetPendingClicks; \ rlm@0: FILTER *WetFilter; \ rlm@0: \ rlm@0: if(!Slot || Slot->effect.type == AL_EFFECT_NULL) \ rlm@0: continue; \ rlm@0: \ rlm@0: WetBuffer = Slot->WetBuffer; \ rlm@0: WetClickRemoval = Slot->ClickRemoval; \ rlm@0: WetPendingClicks = Slot->PendingClicks; \ rlm@0: WetFilter = &Source->Params.Send[out].iirFilter; \ rlm@0: WetSend = Source->Params.Send[out].WetGain; \ rlm@0: \ rlm@0: for(i = 0;i < NumChannels;i++) \ rlm@0: { \ rlm@0: pos = 0; \ rlm@0: frac = *DataPosFrac; \ rlm@0: \ rlm@0: if(OutPos == 0) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ rlm@0: \ rlm@0: value = lpFilter1PC(WetFilter, i, value); \ rlm@0: WetClickRemoval[0] -= value * WetSend; \ rlm@0: } \ rlm@0: for(BufferIdx = 0;BufferIdx < BufferSize;BufferIdx++) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ rlm@0: \ rlm@0: value = lpFilter1P(WetFilter, i, value); \ rlm@0: WetBuffer[OutPos] += value * WetSend; \ rlm@0: \ rlm@0: frac += increment; \ rlm@0: pos += frac>>FRACTIONBITS; \ rlm@0: frac &= FRACTIONMASK; \ rlm@0: OutPos++; \ rlm@0: } \ rlm@0: if(OutPos == SamplesToDo) \ rlm@0: { \ rlm@0: value = sampler(data + pos*NumChannels + i, NumChannels,frac);\ rlm@0: \ rlm@0: value = lpFilter1PC(WetFilter, i, value); \ rlm@0: WetPendingClicks[0] += value * WetSend; \ rlm@0: } \ rlm@0: OutPos -= BufferSize; \ rlm@0: } \ rlm@0: } \ rlm@0: *DataPosInt += pos; \ rlm@0: *DataPosFrac = frac; \ rlm@0: } rlm@0: rlm@0: DECL_TEMPLATE(ALfloat, point32) rlm@0: DECL_TEMPLATE(ALfloat, lerp32) rlm@0: DECL_TEMPLATE(ALfloat, cubic32) rlm@0: rlm@0: DECL_TEMPLATE(ALshort, point16) rlm@0: DECL_TEMPLATE(ALshort, lerp16) rlm@0: DECL_TEMPLATE(ALshort, cubic16) rlm@0: rlm@0: DECL_TEMPLATE(ALbyte, point8) rlm@0: DECL_TEMPLATE(ALbyte, lerp8) rlm@0: DECL_TEMPLATE(ALbyte, cubic8) rlm@0: rlm@0: #undef DECL_TEMPLATE rlm@0: rlm@0: rlm@0: #define DECL_TEMPLATE(sampler) \ rlm@0: static MixerFunc Select_##sampler(enum FmtType FmtType) \ rlm@0: { \ rlm@0: switch(FmtType) \ rlm@0: { \ rlm@0: case FmtByte: \ rlm@0: return Mix_ALbyte_##sampler##8; \ rlm@0: case FmtShort: \ rlm@0: return Mix_ALshort_##sampler##16; \ rlm@0: case FmtFloat: \ rlm@0: return Mix_ALfloat_##sampler##32; \ rlm@0: } \ rlm@0: return NULL; \ rlm@0: } rlm@0: rlm@0: DECL_TEMPLATE(point) rlm@0: DECL_TEMPLATE(lerp) rlm@0: DECL_TEMPLATE(cubic) rlm@0: rlm@0: #undef DECL_TEMPLATE rlm@0: rlm@0: MixerFunc SelectMixer(ALbuffer *Buffer, enum Resampler Resampler) rlm@0: { rlm@0: switch(Resampler) rlm@0: { rlm@0: case POINT_RESAMPLER: rlm@0: return Select_point(Buffer->FmtType); rlm@0: case LINEAR_RESAMPLER: rlm@0: return Select_lerp(Buffer->FmtType); rlm@0: case CUBIC_RESAMPLER: rlm@0: return Select_cubic(Buffer->FmtType); rlm@0: case RESAMPLER_MIN: rlm@0: case RESAMPLER_MAX: rlm@0: break; rlm@0: } rlm@0: return NULL; rlm@0: } rlm@0: rlm@0: #define DECL_TEMPLATE(sampler) \ rlm@0: static MixerFunc Select_Hrtf_##sampler(enum FmtType FmtType) \ rlm@0: { \ rlm@0: switch(FmtType) \ rlm@0: { \ rlm@0: case FmtByte: \ rlm@0: return Mix_Hrtf_ALbyte_##sampler##8; \ rlm@0: case FmtShort: \ rlm@0: return Mix_Hrtf_ALshort_##sampler##16; \ rlm@0: case FmtFloat: \ rlm@0: return Mix_Hrtf_ALfloat_##sampler##32; \ rlm@0: } \ rlm@0: return NULL; \ rlm@0: } rlm@0: rlm@0: DECL_TEMPLATE(point) rlm@0: DECL_TEMPLATE(lerp) rlm@0: DECL_TEMPLATE(cubic) rlm@0: rlm@0: #undef DECL_TEMPLATE rlm@0: rlm@0: MixerFunc SelectHrtfMixer(ALbuffer *Buffer, enum Resampler Resampler) rlm@0: { rlm@0: switch(Resampler) rlm@0: { rlm@0: case POINT_RESAMPLER: rlm@0: return Select_Hrtf_point(Buffer->FmtType); rlm@0: case LINEAR_RESAMPLER: rlm@0: return Select_Hrtf_lerp(Buffer->FmtType); rlm@0: case CUBIC_RESAMPLER: rlm@0: return Select_Hrtf_cubic(Buffer->FmtType); rlm@0: case RESAMPLER_MIN: rlm@0: case RESAMPLER_MAX: rlm@0: break; rlm@0: } rlm@0: return NULL; rlm@0: } rlm@0: rlm@0: rlm@0: ALvoid MixSource(ALsource *Source, ALCdevice *Device, ALuint SamplesToDo) rlm@0: { rlm@0: ALbufferlistitem *BufferListItem; rlm@0: ALuint DataPosInt, DataPosFrac; rlm@0: ALuint BuffersPlayed; rlm@0: ALboolean Looping; rlm@0: ALuint increment; rlm@0: enum Resampler Resampler; rlm@0: ALenum State; rlm@0: ALuint OutPos; rlm@0: ALuint FrameSize; rlm@0: ALint64 DataSize64; rlm@0: ALuint i; rlm@0: rlm@0: /* Get source info */ rlm@0: State = Source->state; rlm@0: BuffersPlayed = Source->BuffersPlayed; rlm@0: DataPosInt = Source->position; rlm@0: DataPosFrac = Source->position_fraction; rlm@0: Looping = Source->bLooping; rlm@0: increment = Source->Params.Step; rlm@0: Resampler = Source->Resampler; rlm@0: FrameSize = Source->NumChannels * Source->SampleSize; rlm@0: rlm@0: /* Get current buffer queue item */ rlm@0: BufferListItem = Source->queue; rlm@0: for(i = 0;i < BuffersPlayed;i++) rlm@0: BufferListItem = BufferListItem->next; rlm@0: rlm@0: OutPos = 0; rlm@0: do { rlm@0: const ALuint BufferPrePadding = ResamplerPrePadding[Resampler]; rlm@0: const ALuint BufferPadding = ResamplerPadding[Resampler]; rlm@0: ALubyte StackData[STACK_DATA_SIZE]; rlm@0: ALubyte *SrcData = StackData; rlm@0: ALuint SrcDataSize = 0; rlm@0: ALuint BufferSize; rlm@0: rlm@0: /* Figure out how many buffer bytes will be needed */ rlm@0: DataSize64 = SamplesToDo-OutPos+1; rlm@0: DataSize64 *= increment; rlm@0: DataSize64 += DataPosFrac+FRACTIONMASK; rlm@0: DataSize64 >>= FRACTIONBITS; rlm@0: DataSize64 += BufferPadding+BufferPrePadding; rlm@0: DataSize64 *= FrameSize; rlm@0: rlm@0: BufferSize = ((DataSize64 > STACK_DATA_SIZE) ? STACK_DATA_SIZE : DataSize64); rlm@0: BufferSize -= BufferSize%FrameSize; rlm@0: rlm@0: if(Source->lSourceType == AL_STATIC) rlm@0: { rlm@0: const ALbuffer *ALBuffer = Source->Buffer; rlm@0: const ALubyte *Data = ALBuffer->data; rlm@0: ALuint DataSize; rlm@0: ALuint pos; rlm@0: rlm@0: /* If current pos is beyond the loop range, do not loop */ rlm@0: if(Looping == AL_FALSE || DataPosInt >= (ALuint)ALBuffer->LoopEnd) rlm@0: { rlm@0: Looping = AL_FALSE; rlm@0: rlm@0: if(DataPosInt >= BufferPrePadding) rlm@0: pos = (DataPosInt-BufferPrePadding)*FrameSize; rlm@0: else rlm@0: { rlm@0: DataSize = (BufferPrePadding-DataPosInt)*FrameSize; rlm@0: DataSize = minu(BufferSize, DataSize); rlm@0: rlm@0: memset(&SrcData[SrcDataSize], 0, DataSize); rlm@0: SrcDataSize += DataSize; rlm@0: BufferSize -= DataSize; rlm@0: rlm@0: pos = 0; rlm@0: } rlm@0: rlm@0: /* Copy what's left to play in the source buffer, and clear the rlm@0: * rest of the temp buffer */ rlm@0: DataSize = ALBuffer->size - pos; rlm@0: DataSize = minu(BufferSize, DataSize); rlm@0: rlm@0: memcpy(&SrcData[SrcDataSize], &Data[pos], DataSize); rlm@0: SrcDataSize += DataSize; rlm@0: BufferSize -= DataSize; rlm@0: rlm@0: memset(&SrcData[SrcDataSize], 0, BufferSize); rlm@0: SrcDataSize += BufferSize; rlm@0: BufferSize -= BufferSize; rlm@0: } rlm@0: else rlm@0: { rlm@0: ALuint LoopStart = ALBuffer->LoopStart; rlm@0: ALuint LoopEnd = ALBuffer->LoopEnd; rlm@0: rlm@0: if(DataPosInt >= LoopStart) rlm@0: { rlm@0: pos = DataPosInt-LoopStart; rlm@0: while(pos < BufferPrePadding) rlm@0: pos += LoopEnd-LoopStart; rlm@0: pos -= BufferPrePadding; rlm@0: pos += LoopStart; rlm@0: pos *= FrameSize; rlm@0: } rlm@0: else if(DataPosInt >= BufferPrePadding) rlm@0: pos = (DataPosInt-BufferPrePadding)*FrameSize; rlm@0: else rlm@0: { rlm@0: DataSize = (BufferPrePadding-DataPosInt)*FrameSize; rlm@0: DataSize = minu(BufferSize, DataSize); rlm@0: rlm@0: memset(&SrcData[SrcDataSize], 0, DataSize); rlm@0: SrcDataSize += DataSize; rlm@0: BufferSize -= DataSize; rlm@0: rlm@0: pos = 0; rlm@0: } rlm@0: rlm@0: /* Copy what's left of this loop iteration, then copy repeats rlm@0: * of the loop section */ rlm@0: DataSize = LoopEnd*FrameSize - pos; rlm@0: DataSize = minu(BufferSize, DataSize); rlm@0: rlm@0: memcpy(&SrcData[SrcDataSize], &Data[pos], DataSize); rlm@0: SrcDataSize += DataSize; rlm@0: BufferSize -= DataSize; rlm@0: rlm@0: DataSize = (LoopEnd-LoopStart) * FrameSize; rlm@0: while(BufferSize > 0) rlm@0: { rlm@0: DataSize = minu(BufferSize, DataSize); rlm@0: rlm@0: memcpy(&SrcData[SrcDataSize], &Data[LoopStart*FrameSize], DataSize); rlm@0: SrcDataSize += DataSize; rlm@0: BufferSize -= DataSize; rlm@0: } rlm@0: } rlm@0: } rlm@0: else rlm@0: { rlm@0: /* Crawl the buffer queue to fill in the temp buffer */ rlm@0: ALbufferlistitem *BufferListIter = BufferListItem; rlm@0: ALuint pos; rlm@0: rlm@0: if(DataPosInt >= BufferPrePadding) rlm@0: pos = (DataPosInt-BufferPrePadding)*FrameSize; rlm@0: else rlm@0: { rlm@0: pos = (BufferPrePadding-DataPosInt)*FrameSize; rlm@0: while(pos > 0) rlm@0: { rlm@0: if(!BufferListIter->prev && !Looping) rlm@0: { rlm@0: ALuint DataSize = minu(BufferSize, pos); rlm@0: rlm@0: memset(&SrcData[SrcDataSize], 0, DataSize); rlm@0: SrcDataSize += DataSize; rlm@0: BufferSize -= DataSize; rlm@0: rlm@0: pos = 0; rlm@0: break; rlm@0: } rlm@0: rlm@0: if(BufferListIter->prev) rlm@0: BufferListIter = BufferListIter->prev; rlm@0: else rlm@0: { rlm@0: while(BufferListIter->next) rlm@0: BufferListIter = BufferListIter->next; rlm@0: } rlm@0: rlm@0: if(BufferListIter->buffer) rlm@0: { rlm@0: if((ALuint)BufferListIter->buffer->size > pos) rlm@0: { rlm@0: pos = BufferListIter->buffer->size - pos; rlm@0: break; rlm@0: } rlm@0: pos -= BufferListIter->buffer->size; rlm@0: } rlm@0: } rlm@0: } rlm@0: rlm@0: while(BufferListIter && BufferSize > 0) rlm@0: { rlm@0: const ALbuffer *ALBuffer; rlm@0: if((ALBuffer=BufferListIter->buffer) != NULL) rlm@0: { rlm@0: const ALubyte *Data = ALBuffer->data; rlm@0: ALuint DataSize = ALBuffer->size; rlm@0: rlm@0: /* Skip the data already played */ rlm@0: if(DataSize <= pos) rlm@0: pos -= DataSize; rlm@0: else rlm@0: { rlm@0: Data += pos; rlm@0: DataSize -= pos; rlm@0: pos -= pos; rlm@0: rlm@0: DataSize = minu(BufferSize, DataSize); rlm@0: memcpy(&SrcData[SrcDataSize], Data, DataSize); rlm@0: SrcDataSize += DataSize; rlm@0: BufferSize -= DataSize; rlm@0: } rlm@0: } rlm@0: BufferListIter = BufferListIter->next; rlm@0: if(!BufferListIter && Looping) rlm@0: BufferListIter = Source->queue; rlm@0: else if(!BufferListIter) rlm@0: { rlm@0: memset(&SrcData[SrcDataSize], 0, BufferSize); rlm@0: SrcDataSize += BufferSize; rlm@0: BufferSize -= BufferSize; rlm@0: } rlm@0: } rlm@0: } rlm@0: rlm@0: /* Figure out how many samples we can mix. */ rlm@0: DataSize64 = SrcDataSize / FrameSize; rlm@0: DataSize64 -= BufferPadding+BufferPrePadding; rlm@0: DataSize64 <<= FRACTIONBITS; rlm@0: DataSize64 -= increment; rlm@0: DataSize64 -= DataPosFrac; rlm@0: rlm@0: BufferSize = (ALuint)((DataSize64+(increment-1)) / increment); rlm@0: BufferSize = minu(BufferSize, (SamplesToDo-OutPos)); rlm@0: rlm@0: SrcData += BufferPrePadding*FrameSize; rlm@0: Source->Params.DoMix(Source, Device, SrcData, &DataPosInt, &DataPosFrac, rlm@0: OutPos, SamplesToDo, BufferSize); rlm@0: OutPos += BufferSize; rlm@0: rlm@0: /* Handle looping sources */ rlm@0: while(1) rlm@0: { rlm@0: const ALbuffer *ALBuffer; rlm@0: ALuint DataSize = 0; rlm@0: ALuint LoopStart = 0; rlm@0: ALuint LoopEnd = 0; rlm@0: rlm@0: if((ALBuffer=BufferListItem->buffer) != NULL) rlm@0: { rlm@0: DataSize = ALBuffer->size / FrameSize; rlm@0: LoopStart = ALBuffer->LoopStart; rlm@0: LoopEnd = ALBuffer->LoopEnd; rlm@0: if(LoopEnd > DataPosInt) rlm@0: break; rlm@0: } rlm@0: rlm@0: if(Looping && Source->lSourceType == AL_STATIC) rlm@0: { rlm@0: BufferListItem = Source->queue; rlm@0: DataPosInt = ((DataPosInt-LoopStart)%(LoopEnd-LoopStart)) + LoopStart; rlm@0: break; rlm@0: } rlm@0: rlm@0: if(DataSize > DataPosInt) rlm@0: break; rlm@0: rlm@0: if(BufferListItem->next) rlm@0: { rlm@0: BufferListItem = BufferListItem->next; rlm@0: BuffersPlayed++; rlm@0: } rlm@0: else if(Looping) rlm@0: { rlm@0: BufferListItem = Source->queue; rlm@0: BuffersPlayed = 0; rlm@0: } rlm@0: else rlm@0: { rlm@0: State = AL_STOPPED; rlm@0: BufferListItem = Source->queue; rlm@0: BuffersPlayed = Source->BuffersInQueue; rlm@0: DataPosInt = 0; rlm@0: DataPosFrac = 0; rlm@0: break; rlm@0: } rlm@0: rlm@0: DataPosInt -= DataSize; rlm@0: } rlm@0: } while(State == AL_PLAYING && OutPos < SamplesToDo); rlm@0: rlm@0: /* Update source info */ rlm@0: Source->state = State; rlm@0: Source->BuffersPlayed = BuffersPlayed; rlm@0: Source->position = DataPosInt; rlm@0: Source->position_fraction = DataPosFrac; rlm@0: Source->Buffer = BufferListItem->buffer; rlm@0: Source->HrtfOffset += OutPos; rlm@0: if(State == AL_PLAYING) rlm@0: { rlm@0: Source->HrtfCounter = maxu(Source->HrtfCounter, OutPos) - OutPos; rlm@0: Source->HrtfMoving = AL_TRUE; rlm@0: } rlm@0: else rlm@0: { rlm@0: Source->HrtfCounter = 0; rlm@0: Source->HrtfMoving = AL_FALSE; rlm@0: } rlm@0: }