static ALvoid ALchorusState_update(ALchorusState *state, ALCdevice *Device, const ALeffectslot *Slot) { static const ALfloat left_dir[3] = { -1.0f, 0.0f, 0.0f }; static const ALfloat right_dir[3] = { 1.0f, 0.0f, 0.0f }; ALfloat frequency = (ALfloat)Device->Frequency; ALfloat rate; ALint phase; switch(Slot->EffectProps.Chorus.Waveform) { case AL_CHORUS_WAVEFORM_TRIANGLE: state->waveform = CWF_Triangle; break; case AL_CHORUS_WAVEFORM_SINUSOID: state->waveform = CWF_Sinusoid; break; } state->depth = Slot->EffectProps.Chorus.Depth; state->feedback = Slot->EffectProps.Chorus.Feedback; state->delay = fastf2i(Slot->EffectProps.Chorus.Delay * frequency); /* Gains for left and right sides */ ComputeDirectionalGains(Device, left_dir, Slot->Gain, state->Gain[0]); ComputeDirectionalGains(Device, right_dir, Slot->Gain, state->Gain[1]); phase = Slot->EffectProps.Chorus.Phase; rate = Slot->EffectProps.Chorus.Rate; if(!(rate > 0.0f)) { state->lfo_scale = 0.0f; state->lfo_range = 1; state->lfo_disp = 0; } else { /* Calculate LFO coefficient */ state->lfo_range = fastf2u(frequency/rate + 0.5f); switch(state->waveform) { case CWF_Triangle: state->lfo_scale = 4.0f / state->lfo_range; break; case CWF_Sinusoid: state->lfo_scale = F_TAU / state->lfo_range; break; } /* Calculate lfo phase displacement */ state->lfo_disp = fastf2i(state->lfo_range * (phase/360.0f)); } }
static ALvoid ALdedicatedState_update(ALdedicatedState *state, ALCdevice *device, const ALeffectslot *Slot) { ALfloat Gain; ALuint i; for(i = 0;i < MAX_OUTPUT_CHANNELS;i++) state->gains[i] = 0.0f; Gain = Slot->Gain * Slot->EffectProps.Dedicated.Gain; if(Slot->EffectType == AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT) { int idx; if((idx=GetChannelIdxByName(device, LFE)) != -1) state->gains[idx] = Gain; } else if(Slot->EffectType == AL_EFFECT_DEDICATED_DIALOGUE) { int idx; /* Dialog goes to the front-center speaker if it exists, otherwise it * plays from the front-center location. */ if((idx=GetChannelIdxByName(device, FrontCenter)) != -1) state->gains[idx] = Gain; else { static const ALfloat front_dir[3] = { 0.0f, 0.0f, -1.0f }; ComputeDirectionalGains(device, front_dir, Gain, state->gains); } } }
void ComputeAngleGains(const ALCdevice *device, ALfloat angle, ALfloat elevation, ALfloat ingain, ALfloat gains[MAX_OUTPUT_CHANNELS]) { ALfloat dir[3] = { sinf(angle) * cosf(elevation), sinf(elevation), -cosf(angle) * cosf(elevation) }; ComputeDirectionalGains(device, dir, ingain, gains); }
ALvoid CalcSourceParams(ALvoice *voice, const ALsource *ALSource, const ALCcontext *ALContext) { ALCdevice *Device = ALContext->Device; aluVector Position, Velocity, Direction, SourceToListener; ALfloat InnerAngle,OuterAngle,Angle,Distance,ClampedDist; ALfloat MinVolume,MaxVolume,MinDist,MaxDist,Rolloff; ALfloat ConeVolume,ConeHF,SourceVolume,ListenerGain; ALfloat DopplerFactor, SpeedOfSound; ALfloat AirAbsorptionFactor; ALfloat RoomAirAbsorption[MAX_SENDS]; ALbufferlistitem *BufferListItem; ALfloat Attenuation; ALfloat RoomAttenuation[MAX_SENDS]; ALfloat MetersPerUnit; ALfloat RoomRolloffBase; ALfloat RoomRolloff[MAX_SENDS]; ALfloat DecayDistance[MAX_SENDS]; ALfloat DryGain; ALfloat DryGainHF; ALfloat DryGainLF; ALboolean DryGainHFAuto; ALfloat WetGain[MAX_SENDS]; ALfloat WetGainHF[MAX_SENDS]; ALfloat WetGainLF[MAX_SENDS]; ALboolean WetGainAuto; ALboolean WetGainHFAuto; ALfloat Pitch; ALuint Frequency; ALint NumSends; ALint i, j; DryGainHF = 1.0f; DryGainLF = 1.0f; for(i = 0;i < MAX_SENDS;i++) { WetGainHF[i] = 1.0f; WetGainLF[i] = 1.0f; } /* Get context/device properties */ DopplerFactor = ALContext->DopplerFactor * ALSource->DopplerFactor; SpeedOfSound = ALContext->SpeedOfSound * ALContext->DopplerVelocity; NumSends = Device->NumAuxSends; Frequency = Device->Frequency; /* Get listener properties */ ListenerGain = ALContext->Listener->Gain; MetersPerUnit = ALContext->Listener->MetersPerUnit; /* Get source properties */ SourceVolume = ALSource->Gain; MinVolume = ALSource->MinGain; MaxVolume = ALSource->MaxGain; Pitch = ALSource->Pitch; Position = ALSource->Position; Direction = ALSource->Direction; Velocity = ALSource->Velocity; MinDist = ALSource->RefDistance; MaxDist = ALSource->MaxDistance; Rolloff = ALSource->RollOffFactor; InnerAngle = ALSource->InnerAngle; OuterAngle = ALSource->OuterAngle; AirAbsorptionFactor = ALSource->AirAbsorptionFactor; DryGainHFAuto = ALSource->DryGainHFAuto; WetGainAuto = ALSource->WetGainAuto; WetGainHFAuto = ALSource->WetGainHFAuto; RoomRolloffBase = ALSource->RoomRolloffFactor; voice->Direct.OutBuffer = Device->DryBuffer; voice->Direct.OutChannels = Device->NumChannels; for(i = 0;i < NumSends;i++) { ALeffectslot *Slot = ALSource->Send[i].Slot; if(!Slot && i == 0) Slot = Device->DefaultSlot; if(!Slot || Slot->EffectType == AL_EFFECT_NULL) { Slot = NULL; RoomRolloff[i] = 0.0f; DecayDistance[i] = 0.0f; RoomAirAbsorption[i] = 1.0f; } else if(Slot->AuxSendAuto) { RoomRolloff[i] = RoomRolloffBase; if(IsReverbEffect(Slot->EffectType)) { RoomRolloff[i] += Slot->EffectProps.Reverb.RoomRolloffFactor; DecayDistance[i] = Slot->EffectProps.Reverb.DecayTime * SPEEDOFSOUNDMETRESPERSEC; RoomAirAbsorption[i] = Slot->EffectProps.Reverb.AirAbsorptionGainHF; } else { DecayDistance[i] = 0.0f; RoomAirAbsorption[i] = 1.0f; } } else { /* If the slot's auxiliary send auto is off, the data sent to the * effect slot is the same as the dry path, sans filter effects */ RoomRolloff[i] = Rolloff; DecayDistance[i] = 0.0f; RoomAirAbsorption[i] = AIRABSORBGAINHF; } if(!Slot || Slot->EffectType == AL_EFFECT_NULL) voice->Send[i].OutBuffer = NULL; else voice->Send[i].OutBuffer = Slot->WetBuffer; } /* Transform source to listener space (convert to head relative) */ if(ALSource->HeadRelative == AL_FALSE) { const aluMatrix *Matrix = &ALContext->Listener->Params.Matrix; /* Transform source vectors */ aluMatrixVector(&Position, Matrix); aluMatrixVector(&Velocity, Matrix); aluMatrixVector(&Direction, Matrix); } else { const aluVector *lvelocity = &ALContext->Listener->Params.Velocity; /* Offset the source velocity to be relative of the listener velocity */ Velocity.v[0] += lvelocity->v[0]; Velocity.v[1] += lvelocity->v[1]; Velocity.v[2] += lvelocity->v[2]; } aluNormalize(Direction.v); SourceToListener.v[0] = -Position.v[0]; SourceToListener.v[1] = -Position.v[1]; SourceToListener.v[2] = -Position.v[2]; SourceToListener.v[3] = 0.0f; Distance = aluNormalize(SourceToListener.v); /* Calculate distance attenuation */ ClampedDist = Distance; Attenuation = 1.0f; for(i = 0;i < NumSends;i++) RoomAttenuation[i] = 1.0f; switch(ALContext->SourceDistanceModel ? ALSource->DistanceModel : ALContext->DistanceModel) { case InverseDistanceClamped: ClampedDist = clampf(ClampedDist, MinDist, MaxDist); if(MaxDist < MinDist) break; /*fall-through*/ case InverseDistance: if(MinDist > 0.0f) { ALfloat dist = lerp(MinDist, ClampedDist, Rolloff); if(dist > 0.0f) Attenuation = MinDist / dist; for(i = 0;i < NumSends;i++) { dist = lerp(MinDist, ClampedDist, RoomRolloff[i]); if(dist > 0.0f) RoomAttenuation[i] = MinDist / dist; } } break; case LinearDistanceClamped: ClampedDist = clampf(ClampedDist, MinDist, MaxDist); if(MaxDist < MinDist) break; /*fall-through*/ case LinearDistance: if(MaxDist != MinDist) { Attenuation = 1.0f - (Rolloff*(ClampedDist-MinDist)/(MaxDist - MinDist)); Attenuation = maxf(Attenuation, 0.0f); for(i = 0;i < NumSends;i++) { RoomAttenuation[i] = 1.0f - (RoomRolloff[i]*(ClampedDist-MinDist)/(MaxDist - MinDist)); RoomAttenuation[i] = maxf(RoomAttenuation[i], 0.0f); } } break; case ExponentDistanceClamped: ClampedDist = clampf(ClampedDist, MinDist, MaxDist); if(MaxDist < MinDist) break; /*fall-through*/ case ExponentDistance: if(ClampedDist > 0.0f && MinDist > 0.0f) { Attenuation = powf(ClampedDist/MinDist, -Rolloff); for(i = 0;i < NumSends;i++) RoomAttenuation[i] = powf(ClampedDist/MinDist, -RoomRolloff[i]); } break; case DisableDistance: ClampedDist = MinDist; break; } /* Source Gain + Attenuation */ DryGain = SourceVolume * Attenuation; for(i = 0;i < NumSends;i++) WetGain[i] = SourceVolume * RoomAttenuation[i]; /* Distance-based air absorption */ if(AirAbsorptionFactor > 0.0f && ClampedDist > MinDist) { ALfloat meters = (ClampedDist-MinDist) * MetersPerUnit; DryGainHF *= powf(AIRABSORBGAINHF, AirAbsorptionFactor*meters); for(i = 0;i < NumSends;i++) WetGainHF[i] *= powf(RoomAirAbsorption[i], AirAbsorptionFactor*meters); } if(WetGainAuto) { ALfloat ApparentDist = 1.0f/maxf(Attenuation, 0.00001f) - 1.0f; /* Apply a decay-time transformation to the wet path, based on the * attenuation of the dry path. * * Using the apparent distance, based on the distance attenuation, the * initial decay of the reverb effect is calculated and applied to the * wet path. */ for(i = 0;i < NumSends;i++) { if(DecayDistance[i] > 0.0f) WetGain[i] *= powf(0.001f/*-60dB*/, ApparentDist/DecayDistance[i]); } } /* Calculate directional soundcones */ Angle = RAD2DEG(acosf(aluDotproduct(&Direction, &SourceToListener)) * ConeScale) * 2.0f; if(Angle > InnerAngle && Angle <= OuterAngle) { ALfloat scale = (Angle-InnerAngle) / (OuterAngle-InnerAngle); ConeVolume = lerp(1.0f, ALSource->OuterGain, scale); ConeHF = lerp(1.0f, ALSource->OuterGainHF, scale); } else if(Angle > OuterAngle) { ConeVolume = ALSource->OuterGain; ConeHF = ALSource->OuterGainHF; } else { ConeVolume = 1.0f; ConeHF = 1.0f; } DryGain *= ConeVolume; if(WetGainAuto) { for(i = 0;i < NumSends;i++) WetGain[i] *= ConeVolume; } if(DryGainHFAuto) DryGainHF *= ConeHF; if(WetGainHFAuto) { for(i = 0;i < NumSends;i++) WetGainHF[i] *= ConeHF; } /* Clamp to Min/Max Gain */ DryGain = clampf(DryGain, MinVolume, MaxVolume); for(i = 0;i < NumSends;i++) WetGain[i] = clampf(WetGain[i], MinVolume, MaxVolume); /* Apply gain and frequency filters */ DryGain *= ALSource->Direct.Gain * ListenerGain; DryGainHF *= ALSource->Direct.GainHF; DryGainLF *= ALSource->Direct.GainLF; for(i = 0;i < NumSends;i++) { WetGain[i] *= ALSource->Send[i].Gain * ListenerGain; WetGainHF[i] *= ALSource->Send[i].GainHF; WetGainLF[i] *= ALSource->Send[i].GainLF; } /* Calculate velocity-based doppler effect */ if(DopplerFactor > 0.0f) { const aluVector *lvelocity = &ALContext->Listener->Params.Velocity; ALfloat VSS, VLS; if(SpeedOfSound < 1.0f) { DopplerFactor *= 1.0f/SpeedOfSound; SpeedOfSound = 1.0f; } VSS = aluDotproduct(&Velocity, &SourceToListener) * DopplerFactor; VLS = aluDotproduct(lvelocity, &SourceToListener) * DopplerFactor; Pitch *= clampf(SpeedOfSound-VLS, 1.0f, SpeedOfSound*2.0f - 1.0f) / clampf(SpeedOfSound-VSS, 1.0f, SpeedOfSound*2.0f - 1.0f); } BufferListItem = ATOMIC_LOAD(&ALSource->queue); while(BufferListItem != NULL) { ALbuffer *ALBuffer; if((ALBuffer=BufferListItem->buffer) != NULL) { /* Calculate fixed-point stepping value, based on the pitch, buffer * frequency, and output frequency. */ Pitch = Pitch * ALBuffer->Frequency / Frequency; if(Pitch > (ALfloat)MAX_PITCH) voice->Step = MAX_PITCH<<FRACTIONBITS; else { voice->Step = fastf2i(Pitch*FRACTIONONE); if(voice->Step == 0) voice->Step = 1; } break; } BufferListItem = BufferListItem->next; } if(Device->Hrtf_Mode == FullHrtf) { /* Use a binaural HRTF algorithm for stereo headphone playback */ aluVector dir = {{ 0.0f, 0.0f, -1.0f, 0.0f }}; ALfloat ev = 0.0f, az = 0.0f; ALfloat radius = ALSource->Radius; ALfloat dirfact = 1.0f; voice->Direct.OutBuffer += voice->Direct.OutChannels; voice->Direct.OutChannels = 2; if(Distance > FLT_EPSILON) { dir.v[0] = -SourceToListener.v[0]; dir.v[1] = -SourceToListener.v[1]; dir.v[2] = -SourceToListener.v[2] * ZScale; /* Calculate elevation and azimuth only when the source is not at * the listener. This prevents +0 and -0 Z from producing * inconsistent panning. Also, clamp Y in case FP precision errors * cause it to land outside of -1..+1. */ ev = asinf(clampf(dir.v[1], -1.0f, 1.0f)); az = atan2f(dir.v[0], -dir.v[2]); } if(radius > 0.0f) { if(radius >= Distance) dirfact *= Distance / radius * 0.5f; else dirfact *= 1.0f - (asinf(radius / Distance) / F_PI); } /* Check to see if the HRIR is already moving. */ if(voice->Direct.Moving) { ALfloat delta; delta = CalcFadeTime(voice->Direct.LastGain, DryGain, &voice->Direct.LastDir, &dir); /* If the delta is large enough, get the moving HRIR target * coefficients, target delays, steppping values, and counter. */ if(delta > 0.000015f) { ALuint counter = GetMovingHrtfCoeffs(Device->Hrtf, ev, az, dirfact, DryGain, delta, voice->Direct.Counter, voice->Direct.Hrtf[0].Params.Coeffs, voice->Direct.Hrtf[0].Params.Delay, voice->Direct.Hrtf[0].Params.CoeffStep, voice->Direct.Hrtf[0].Params.DelayStep ); voice->Direct.Counter = counter; voice->Direct.LastGain = DryGain; voice->Direct.LastDir = dir; } } else { /* Get the initial (static) HRIR coefficients and delays. */ GetLerpedHrtfCoeffs(Device->Hrtf, ev, az, dirfact, DryGain, voice->Direct.Hrtf[0].Params.Coeffs, voice->Direct.Hrtf[0].Params.Delay); voice->Direct.Counter = 0; voice->Direct.Moving = AL_TRUE; voice->Direct.LastGain = DryGain; voice->Direct.LastDir = dir; } voice->IsHrtf = AL_TRUE; } else { MixGains *gains = voice->Direct.Gains[0]; ALfloat dir[3] = { 0.0f, 0.0f, -1.0f }; ALfloat radius = ALSource->Radius; ALfloat Target[MAX_OUTPUT_CHANNELS]; /* Get the localized direction, and compute panned gains. */ if(Distance > FLT_EPSILON) { dir[0] = -SourceToListener.v[0]; dir[1] = -SourceToListener.v[1]; dir[2] = -SourceToListener.v[2] * ZScale; } if(radius > 0.0f) { ALfloat dirfact; if(radius >= Distance) dirfact = Distance / radius * 0.5f; else dirfact = 1.0f - (asinf(radius / Distance) / F_PI); dir[0] *= dirfact; dir[1] *= dirfact; dir[2] *= dirfact; } ComputeDirectionalGains(Device, dir, DryGain, Target); for(j = 0;j < MAX_OUTPUT_CHANNELS;j++) gains[j].Target = Target[j]; UpdateDryStepping(&voice->Direct, 1, (voice->Direct.Moving ? 64 : 0)); voice->Direct.Moving = AL_TRUE; voice->IsHrtf = AL_FALSE; } for(i = 0;i < NumSends;i++) { voice->Send[i].Gain.Target = WetGain[i]; UpdateWetStepping(&voice->Send[i], (voice->Send[i].Moving ? 64 : 0)); voice->Send[i].Moving = AL_TRUE; } { ALfloat gainhf = maxf(0.01f, DryGainHF); ALfloat gainlf = maxf(0.01f, DryGainLF); ALfloat hfscale = ALSource->Direct.HFReference / Frequency; ALfloat lfscale = ALSource->Direct.LFReference / Frequency; voice->Direct.Filters[0].ActiveType = AF_None; if(gainhf != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_LowPass; if(gainlf != 1.0f) voice->Direct.Filters[0].ActiveType |= AF_HighPass; ALfilterState_setParams( &voice->Direct.Filters[0].LowPass, ALfilterType_HighShelf, gainhf, hfscale, 0.0f ); ALfilterState_setParams( &voice->Direct.Filters[0].HighPass, ALfilterType_LowShelf, gainlf, lfscale, 0.0f ); } for(i = 0;i < NumSends;i++) { ALfloat gainhf = maxf(0.01f, WetGainHF[i]); ALfloat gainlf = maxf(0.01f, WetGainLF[i]); ALfloat hfscale = ALSource->Send[i].HFReference / Frequency; ALfloat lfscale = ALSource->Send[i].LFReference / Frequency; voice->Send[i].Filters[0].ActiveType = AF_None; if(gainhf != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_LowPass; if(gainlf != 1.0f) voice->Send[i].Filters[0].ActiveType |= AF_HighPass; ALfilterState_setParams( &voice->Send[i].Filters[0].LowPass, ALfilterType_HighShelf, gainhf, hfscale, 0.0f ); ALfilterState_setParams( &voice->Send[i].Filters[0].HighPass, ALfilterType_LowShelf, gainlf, lfscale, 0.0f ); } }