/// <summary> /// Gets the gain at a given frequency. /// </summary> public double this[double frequency] { get { int bandCount = bands.Count; if (bandCount == 0) { return(0); } int nextBand = 0, prevBand = 0; while (nextBand != bandCount && bands[nextBand].Frequency < frequency) { prevBand = nextBand; ++nextBand; } if (nextBand != bandCount && nextBand != 0) { return(QMath.Lerp(bands[prevBand].Gain, bands[nextBand].Gain, QMath.LerpInverse(bands[prevBand].Frequency, bands[nextBand].Frequency, frequency))); } return(bands[prevBand].Gain); } }
/// <summary> /// Generate the left/right ear filters. /// </summary> /// <param name="right">The object is to the right of the <see cref="Listener"/>'s forward vector</param> /// <param name="samples">Single-channel downmixed samples to process</param> public void Generate(bool right, float[] samples) { float dirMul = -90; if (right) { dirMul = 90; } Vector3 sourceForward = new Vector3(0, dirMul, 0).RotateInverse(source.listener.Rotation).PlaceInSphere(), dir = source.Position - source.listener.Position; float distance = dir.Length(), rawAngle = (float)Math.Acos(Vector3.Dot(sourceForward, dir) / distance), angle = rawAngle * VectorExtensions.Rad2Deg; distance /= distanceFactor; // Find bounding angles with discrete impulses int smallerAngle = 0; while (smallerAngle < angles.Length && angles[smallerAngle] < angle) { ++smallerAngle; } if (smallerAngle != 0) { --smallerAngle; } int largerAngle = smallerAngle + 1; if (largerAngle == angles.Length) { largerAngle = angles.Length - 1; } float angleRatio = Math.Min(QMath.LerpInverse(angles[smallerAngle], angles[largerAngle], angle), 1); // Find bounding distances with discrete impulses int smallerDistance = 0; while (smallerDistance < distances.Length && distances[smallerDistance] < distance) { ++smallerDistance; } if (smallerDistance != 0) { --smallerDistance; } int largerDistance = smallerDistance + 1; if (largerDistance == distances.Length) { largerDistance = distances.Length - 1; } float distanceRatio = Math.Clamp(QMath.LerpInverse(distances[smallerDistance], distances[largerDistance], distance), 0, 1); // Find impulse candidates and their weight float[][] candidates = new float[4][] { impulses[smallerAngle][smallerDistance], impulses[smallerAngle][largerDistance], impulses[largerAngle][smallerDistance], impulses[largerAngle][largerDistance] }; float[] gains = new float[4] { (float)Math.Sqrt((1 - angleRatio) * (1 - distanceRatio)), (float)Math.Sqrt((1 - angleRatio) * distanceRatio), (float)Math.Sqrt(angleRatio * (1 - distanceRatio)), (float)Math.Sqrt(angleRatio * distanceRatio) }; // Apply the ear canal's response Array.Clear(filter.Impulse, 0, filterSize); for (int candidate = 0; candidate < candidates.Length; ++candidate) { WaveformUtils.Mix(candidates[candidate], filter.Impulse, gains[candidate]); } filter.Process(samples); // Apply gains float angleDiff = (float)(Math.Sin(rawAngle) * .097f); float ratioDiff = (distance + angleDiff) * (VirtualizerFilter.referenceDistance - angleDiff) / ((distance - angleDiff) * (VirtualizerFilter.referenceDistance + angleDiff)); ratioDiff *= ratioDiff; if (right) { if (ratioDiff < 1) { RightGain = ratioDiff; } else { LeftGain = 1 / ratioDiff; } } else { if (ratioDiff < 1) { LeftGain = ratioDiff; } else { RightGain = 1 / ratioDiff; } } }