private void Apply3DImpl(AudioListener listener, AudioEmitter emitter) { var vectDirWorldBase = emitter.Position - listener.Position; var baseChangeMat = Matrix.Identity; baseChangeMat.Row2 = new Vector4(listener.Up, 0); baseChangeMat.Row3 = new Vector4(listener.Forward, 0); baseChangeMat.Row1 = new Vector4(Vector3.Cross(listener.Up, listener.Forward), 0); var vectDirListBase = Vector3.TransformNormal(vectDirWorldBase, baseChangeMat); var azimut = 180f * (float)(Math.Atan2(vectDirListBase.X, vectDirListBase.Z)/Math.PI); var elevation = 180f * (float)(Math.Atan2(vectDirListBase.Y, new Vector2(vectDirListBase.X, vectDirListBase.Z).Length())/Math.PI); var distance = vectDirListBase.Length(); ComputeDopplerFactor(listener,emitter); AudioVoice.Apply3D(azimut, elevation, distance / emitter.DistanceScale, MathUtil.Clamp((float)Math.Pow(2, Pitch) * dopplerPitchFactor, 0.5f, 2f)); }
internal void Apply3DImpl(AudioListener listener, AudioEmitter emitter) { ////////////////////////////////////////////////////////////// // 1. First let's calculate the parameters to set to the voice var inputChannels = soundEffect.WaveFormat.Channels; var outputChannels = MasterVoice.VoiceDetails.InputChannelCount; if (inputChannels != 1 || outputChannels != 2) throw new AudioSystemInternalException("Error in Apply3DImpl only mono sounds are supposed to be localizable"); var list = new Listener { Position = listener.Position.ToSharpDX(), Velocity = listener.Velocity.ToSharpDX(), OrientFront = listener.Forward.ToSharpDX(), OrientTop = listener.Up.ToSharpDX() }; var emit = new Emitter { Position = emitter.Position.ToSharpDX(), Velocity = emitter.Velocity.ToSharpDX(), DopplerScaler = emitter.DopplerScale, CurveDistanceScaler = emitter.DistanceScale, ChannelRadius = 0f, // Multi-channels localizable sound are considered as source of multiple sounds coming from the same location. ChannelCount = inputChannels }; var dspSettings = new DspSettings(inputChannels, outputChannels); AudioEngine.X3DAudio.Calculate(list, emit, CalculateFlags.Matrix | CalculateFlags.LpfDirect, dspSettings); ///////////////////////////////////////////////////////////// // 2. Now let's set the voice parameters to simulate a 3D voice. // 2.1 The Doppler effect due to the difference of speed between the emitter and listener ComputeDopplerFactor(listener, emitter); UpdatePitch(); // 2.2 The channel attenuations due to the source localization. localizationChannelVolumes = new[] { dspSettings.MatrixCoefficients[0], dspSettings.MatrixCoefficients[1] }; // only mono sound can be localized so matrix should be 2*1 UpdateStereoVolumes(); }
/// <summary> /// Applies 3D positioning to the sound. /// More precisely adjust the channel volumes and pitch of the sound, /// such that the sound source seems to come from the <paramref name="emitter"/> to the listener/>. /// </summary> /// <param name="emitter">The emitter that correspond to this sound</param> /// <remarks> /// <see cref="Apply3D"/> can be used only on mono-sounds. /// <para> /// The final resulting pitch depends on the listener and emitter relative velocity. /// The final resulting channel volumes depend on the listener and emitter relative positions and the value of <see cref="IPlayableSound.Volume"/>. /// </para> /// </remarks> public void Apply3D(AudioEmitter emitter) { if (engine.State == AudioEngineState.Invalidated) return; if (!spatialized) return; if (emitter == null) throw new ArgumentNullException(nameof(emitter)); emitter.Apply3D(Source); }
public void Apply3D(AudioListener listener, AudioEmitter emitter) { DefaultInstance.Apply3D(listener, emitter); }
private void Apply3DImpl(AudioListener listener, AudioEmitter emitter) { // Since android has no function available to perform sound 3D localization by default, here we try to mimic the behaviour of XAudio2 // After an analysis of the XAudio2 left/right stereo balance with respect to 3D world position, // it could be found the volume repartition is symmetric to the Up/Down and Front/Back planes. // Moreover the left/right repartition seems to follow a third degree polynomial function: // Volume_left(a) = 2(c-1)*a^3 - 3(c-1)*a^2 + c*a , where c is a constant close to c = 1.45f and a is the angle normalized bwt [0,1] // Volume_right(a) = 1-Volume_left(a) // As for signal attenuation wrt distance the model follows a simple inverse square law function as explained in XAudio2 documentation // ( http://msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.x3daudio.x3daudio_emitter(v=vs.85).aspx ) // Volume(d) = 1 , if d <= ScaleDistance where d is the distance to the listener // Volume(d) = ScaleDistance / d , if d >= ScaleDistance where d is the distance to the listener // 1. Attenuation due to distance. var vecListEmit = emitter.Position - listener.Position; var distListEmit = vecListEmit.Length(); var attenuationFactor = distListEmit <= emitter.DistanceScale ? 1f : emitter.DistanceScale / distListEmit; // 2. Left/Right balance. var repartRight = 0.5f; var worldToList = Matrix.Identity; var rightVec = Vector3.Cross(listener.Forward, listener.Up); worldToList.Column1 = new Vector4(rightVec, 0); worldToList.Column2 = new Vector4(listener.Forward, 0); worldToList.Column3 = new Vector4(listener.Up, 0); var vecListEmitListBase = Vector3.TransformNormal(vecListEmit, worldToList); var vecListEmitListBase2 = (Vector2)vecListEmitListBase; if (vecListEmitListBase2.Length() > 0) { const float c = 1.45f; var absAlpha = Math.Abs(Math.Atan2(vecListEmitListBase2.Y, vecListEmitListBase2.X)); var normAlpha = (float)(absAlpha / (Math.PI / 2)); if (absAlpha > Math.PI / 2) normAlpha = 2 - normAlpha; repartRight = 0.5f * (2 * (c - 1) * normAlpha * normAlpha * normAlpha - 3 * (c - 1) * normAlpha * normAlpha * normAlpha + c * normAlpha); if (absAlpha > Math.PI / 2) repartRight = 1 - repartRight; } // Set the volumes. localizationChannelVolumes = new[] { attenuationFactor * (1f - repartRight), attenuationFactor * repartRight }; UpdateStereoVolumes(); // 3. Calculation of the Doppler effect ComputeDopplerFactor(listener, emitter); UpdatePitch(); }
internal void Apply3DImpl(AudioListener listener, AudioEmitter emitter) { throw new NotImplementedException(); }
private void ComputeDopplerFactor(AudioListener listener, AudioEmitter emitter) { // To evaluate the Doppler effect we calculate the distance to the listener from one wave to the next one and divide it by the sound speed // we use 343m/s for the sound speed which correspond to the sound speed in the air. // we use 600Hz for the sound frequency which correspond to the middle of the human hearable sounds frequencies. const float SoundSpeed = 343f; const float SoundFreq = 600f; const float SoundPeriod = 1 / SoundFreq; // avoid useless calculations. if (emitter.DopplerScale <= float.Epsilon || (emitter.Velocity == Vector3.Zero && listener.Velocity == Vector3.Zero)) { dopplerPitchFactor = 1f; return; } var vecListEmit = emitter.Position - listener.Position; var distListEmit = vecListEmit.Length(); var vecListEmitSpeed = emitter.Velocity - listener.Velocity; if (Vector3.Dot(vecListEmitSpeed, Vector3.Normalize(vecListEmit)) < -SoundSpeed) // emitter and listener are getting closer more quickly than the speed of the sound. { dopplerPitchFactor = float.PositiveInfinity; // will be clamped later return; } var timeSinceLastWaveArrived = 0f; // time elapsed since the previous wave arrived to the listener. var lastWaveDistToListener = 0f; // the distance that the last wave still have to travel to arrive to the listener. const float DistLastWave = SoundPeriod * SoundSpeed; // distance traveled by the previous wave. if (DistLastWave > distListEmit) timeSinceLastWaveArrived = (DistLastWave - distListEmit) / SoundSpeed; else lastWaveDistToListener = distListEmit - DistLastWave; var nextVecListEmit = vecListEmit + SoundPeriod * vecListEmitSpeed; var nextWaveDistToListener = nextVecListEmit.Length(); var timeBetweenTwoWaves = timeSinceLastWaveArrived + (nextWaveDistToListener - lastWaveDistToListener) / SoundSpeed; var apparentFrequency = 1 / timeBetweenTwoWaves; dopplerPitchFactor = (float) Math.Pow(apparentFrequency / SoundFreq, emitter.DopplerScale); }
public void Apply3D(AudioListener listener, AudioEmitter emitter) { CheckNotDisposed(); if (listener == null) throw new ArgumentNullException("listener"); if(emitter == null) throw new ArgumentNullException("emitter"); if(soundEffect.WaveFormat.Channels > 1) throw new InvalidOperationException("Apply3D cannot be used on multi-channels sounds."); // reset Pan its default values. if (Pan != 0) Pan = 0; if (EngineState != AudioEngineState.Invalidated) Apply3DImpl(listener, emitter); }