/// <summary> /// Setup for computing the room impulse response. /// </summary> /// <param name="receiverPos">Receiver position</param> /// <param name="sourcePos">Source position</param> /// <param name="r">Room</param> private void RirGenerator(float3 receiverPos, float3 sourcePos, Room r) { float fs = 16000; int nMic = 1; MicrophoneType micType = MicrophoneType.Omnidirectional; int reflectionOrder = -1; float2 micOrientation = new float2(0, 0); float3 room_dimensions = new float3(r.dimension.x, r.dimension.y, r.dimension.z); if (!r.coefficients.isReflection) { r.coefficients.ToReflection(); } NativeArray <float> beta = new NativeArray <float>(6, Allocator.Temp, NativeArrayOptions.UninitializedMemory); //float* beta = stackalloc float[6]; beta[0] = r.coefficients.frontWall; beta[1] = r.coefficients.backWall; beta[2] = r.coefficients.leftWall; beta[3] = r.coefficients.rightWall; beta[4] = r.coefficients.floor; beta[5] = r.coefficients.ceiling; int nSamples = impulseResponse.Length; ComputeRIR(fs, receiverPos, nMic, sourcePos, room_dimensions, nSamples, ref beta, micType, reflectionOrder, micOrientation); }
public MicrophoneProperties(MicrophoneType _microphoneType, double _deltha, Int32 _count, double _diameter) { microphoneType = _microphoneType; deltha = _deltha; count = _count; diameter = _diameter; }
///////////////////////////////////////////////////////////////////////////////////////////// public static double CountDirectivityRate(MicrophoneType microphoneType, double frequency, double deltha, Int32 count, double diameter) { double result = Integrate.OnClosedInterval(x => (Math.Pow(DirectivityController.CountDirectivityDependence(microphoneType, frequency, deltha, count, diameter, x), 2) * Math.Sin(x)), 0, Math.PI); double resultForReturning = 10 * Math.Log10(2 / result); return resultForReturning; }
///////////////////////////////////////////////////////////////////////////////////////////// public static double CountDirectivityDependence(MicrophoneType microphoneType, double frequency, double deltha, Int32 count, double diameter, double theta) { double wavelenght = (double)331 / (double)frequency; double angle = theta; /* * Микрофон органного типа * */ //* double first = 0.0; double second = 0.0; switch (microphoneType) { case MicrophoneType.MicrophoneTypeOrgan: { mark1: ; first = Math.Sin((count * Math.PI * deltha * (1 - Math.Cos(angle))) / (wavelenght)); second = count * Math.Sin((Math.PI * deltha * (1 - Math.Cos(angle))) / (wavelenght)); if (second == 0.0) { // Судя по всему, функция Math.Cos() достаточно грубо округляет значения и считает что Math.Cos(0.00000000000001) = 1. // Looks like Math.Cos() function round value pretty crude so we need to increase this value manually. angle += 0.00000001; goto mark1; } } break; case MicrophoneType.MicrophoneTypeLinear: { mark2: ; first = Math.Sin(Math.Sin(angle) * Convert.ToDouble(count) * Math.PI * deltha / wavelenght); second = Convert.ToDouble(count) * Math.Sin(Math.Sin(angle) * Math.PI * deltha / wavelenght); if (second == 0.0) { angle = Math.PI; goto mark2; } } break; case MicrophoneType.MicrophoneTypeParabolic: { double radius = diameter / 2; mark2: ; first = (1 + Math.Cos(angle)) * alglib.besselj1((2 * Math.PI * radius * Math.Sin(angle)) / wavelenght) * wavelenght; second = 4 * Math.PI * radius * Math.Sin(angle); /* double k = 2 * Math.PI / wavelenght; first = 2 * alglib.besselj1(k * radius * Math.Sin(angle)); second = k * radius * Math.Sin(angle); */ if (first == 0.0) { angle += 0.000001; goto mark2; } } break; default: { } break; } double result = Math.Abs(first / second); return result; }
/// <summary> /// Computes the room impulse response using Image-Source method. /// </summary> /// <param name="fs">Sampling frequency</param> /// <param name="rr">Receiver position</param> /// <param name="nMicrophones">Number of microphones</param> /// <param name="ss">Source position</param> /// <param name="LL">Room dimensions</param> /// <param name="nSamples">Number of samples</param> /// <param name="beta">Reflection coefficients</param> /// <param name="microphone_type">Microphone type</param> /// <param name="nOrder">Reflection order</param> /// <param name="microphone_angle">Microphone orientation</param> private void ComputeRIR(float fs, float3 rr, int nMicrophones, float3 ss, float3 LL, int nSamples, [NoAlias] ref NativeArray <float> beta, MicrophoneType microphone_type, int nOrder, float2 microphone_angle) { // Temporary variables and constants (high-pass filter) float c = 343; float W = 2 * math.PI * 100 / fs; // The cut-off frequency equals 100 Hz float R1 = math.exp(-W); float B1 = 2 * R1 * math.cos(W); float B2 = -R1 * R1; float A1 = -(1 + R1); //float X0; //float* Y = stackalloc float[3]; //float3 Y = new float3(); // Temporary variables and constants (image-method) float Fc = 0.5f; // The normalized cut-off frequency equals (fs/2) / fs = 0.5 int Tw = (int)(2 * math.round(0.004f * fs)); // The width of the low-pass FIR equals 8 ms float cTs = c / fs; NativeArray <float> LPI = new NativeArray <float>(Tw, Allocator.Temp); float3 r = new float3(); float3 s = new float3(); float3 L = new float3(); float3 Rm = new float3(); float3 Rp_plus_Rm = new float3(); float3 refl = new float3(); //float* LPI = stackalloc float[Tw]; /* * float* r = stackalloc float[3]; * float* s = stackalloc float[3]; * float* L = stackalloc float[3]; * float* Rm = stackalloc float[3]; * float* Rp_plus_Rm = stackalloc float[3]; * float* refl = stackalloc float[3]; */ float fdist, dist; float gain; int startPosition; int n1, n2, n3; int q, j, k; int mx, my, mz; int n; float pow_beta1_mx, pow_beta3_my, pow_beta5_mz; float pow_Rp_plus_Rm0, pow_Rp_plus_Rm1; float t; s[0] = ss[0] / cTs; s[1] = ss[1] / cTs; s[2] = ss[2] / cTs; L[0] = LL[0] / cTs; L[1] = LL[1] / cTs; L[2] = LL[2] / cTs; for (int idxMicrophone = 0; idxMicrophone < nMicrophones; idxMicrophone++) { // [x_1 x_2 ... x_N y_1 y_2 ... y_N z_1 z_2 ... z_N] r[0] = rr[idxMicrophone + 0 * nMicrophones] / cTs; r[1] = rr[idxMicrophone + 1 * nMicrophones] / cTs; r[2] = rr[idxMicrophone + 2 * nMicrophones] / cTs; n1 = (int)math.ceil(nSamples / (2 * L[0])); n2 = (int)math.ceil(nSamples / (2 * L[1])); n3 = (int)math.ceil(nSamples / (2 * L[2])); // Generate room impulse response for (mx = -n1; mx <= n1; mx++) { Rm[0] = 2 * mx * L[0]; pow_beta1_mx = math.pow(beta[1], math.abs(mx)); for (my = -n2; my <= n2; my++) { Rm[1] = 2 * my * L[1]; pow_beta3_my = math.pow(beta[3], math.abs(my)); for (mz = -n3; mz <= n3; mz++) { Rm[2] = 2 * mz * L[2]; pow_beta5_mz = math.pow(beta[5], math.abs(mz)); for (q = 0; q <= 1; q++) { Rp_plus_Rm[0] = (1 - 2 * q) * s[0] - r[0] + Rm[0]; refl[0] = math.pow(beta[0], math.abs(mx - q)) * pow_beta1_mx; pow_Rp_plus_Rm0 = math.pow(Rp_plus_Rm[0], 2); for (j = 0; j <= 1; j++) { Rp_plus_Rm[1] = (1 - 2 * j) * s[1] - r[1] + Rm[1]; refl[1] = math.pow(beta[2], math.abs(my - j)) * pow_beta3_my; pow_Rp_plus_Rm1 = math.pow(Rp_plus_Rm[1], 2); for (k = 0; k <= 1; k++) { Rp_plus_Rm[2] = (1 - 2 * k) * s[2] - r[2] + Rm[2]; refl[2] = math.pow(beta[4], math.abs(mz - k)) * pow_beta5_mz; dist = math.sqrt(pow_Rp_plus_Rm0 + pow_Rp_plus_Rm1 + math.pow(Rp_plus_Rm[2], 2)); if (math.abs(2 * mx - q) + math.abs(2 * my - j) + math.abs(2 * mz - k) <= nOrder || nOrder == -1) { fdist = math.floor(dist); if (fdist < nSamples) { gain = SimMicrophone(Rp_plus_Rm[0], Rp_plus_Rm[1], Rp_plus_Rm[2], microphone_angle, microphone_type) * refl[0] * refl[1] * refl[2] / (4 * math.PI * dist * cTs); for (n = 0; n < Tw; n++) { t = (n - 0.5f * Tw + 1) - (dist - fdist); LPI[n] = 0.5f * (1.0f + math.cos(2.0f * math.PI * t / Tw)) * 2.0f * Fc * Sinc(math.PI * 2.0f * Fc * t); } startPosition = (int)fdist - (Tw / 2) + 1; for (n = 0; n < Tw; n++) { if (startPosition + n >= 0 && startPosition + n < nSamples) { impulseResponse[idxMicrophone + nMicrophones * (startPosition + n)] += gain * LPI[n]; } } } } } } } } } } } LPI.Dispose(); beta.Dispose(); }
/* * The software implementation is inspired by dr.ir. Emanuel Habets' ([email protected]) * implementation of the Image-Source method. * url: https://github.com/ehabets/RIR-Generator/blob/master/rir_generator.cpp * version: 2.2.20201022 */ /* MIT Lisence: * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /// <summary> /// Simulates the microphone type /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <param name="z"></param> /// <param name="microphone_angle"></param> /// <param name="mtype">Microphone type</param> /// <returns></returns> private static float SimMicrophone(float x, float y, float z, float2 microphone_angle, MicrophoneType mtype) { if (mtype == MicrophoneType.Bidirectional || mtype == MicrophoneType.Cardioid || mtype == MicrophoneType.Subcardioid || mtype == MicrophoneType.Hypercardioid) { float gain, vartheta, varphi, rho; /* * Polar Pattern rho * --------------------------- * Bidirectional 0 * Hypercardioid 0.25 * Cardioid 0.5 * Subcardioid 0.75 * Omnidirectional 1 */ switch (mtype) { case MicrophoneType.Bidirectional: rho = 0; break; case MicrophoneType.Hypercardioid: rho = 0.25f; break; case MicrophoneType.Cardioid: rho = 0.5f; break; case MicrophoneType.Subcardioid: rho = 0.75f; break; default: rho = 1; break; } ; vartheta = math.acos(z / math.sqrt(math.pow(x, 2) + math.pow(y, 2) + math.pow(z, 2))); varphi = math.atan2(y, x); gain = math.sin(math.PI / 2 - microphone_angle[1]) * math.sin(vartheta) * math.cos(microphone_angle[0] - varphi) + math.cos(math.PI / 2 - microphone_angle[1]) * math.cos(vartheta); gain = rho + (1 - rho) * gain; return(gain); } else { return(1); } }
public static double CountIntelligibility(MicrophoneType microphoneType, double deltha, Int32 count, double diameter) { /* * Set center and border frequencies for bands. */ double[] frequencyBorders = {/* 90.0, 175.0,*/ 175.0, 355.0, 355.0, 710.0, 710.0, 1400.0, 1400.0, 2800.0, 2800.0, 5600.0, /* 5600.0, 11200.0 */}; double[] centerFrequencies = {/* 125.0, */250.0, 500.0, 1000.0, 2000.0, 4000.0/*, 8000.0 */}; double[] weightCoefficients = {/* 0.01, */0.03, 0.12, 0.20, 0.30, 0.26/*, 0.07 */}; double[] formantParameters = {/* 25.0, */18.0, 14.0, 9.0, 6.0, 5.0/*, 4.0 */}; /* * Count coefficients of perception for each band. */ List<double> coefficientsOfPerception = new List<double>(); for (int i = 0; i < centerFrequencies.Length; i++) { double frequency = centerFrequencies[i]; double SNR = DirectivityController.CountDirectivityRate(microphoneType, frequency, deltha, count, diameter); // dB double val = formantParameters[i]; double Q = SNR - val; double coefficientOfPerception = 0.0; double qAbsValue = Math.Abs(Q); double constDependance = (0.78 + 5.46 * Math.Exp(-4.3 * Math.Pow(10, -3) * (27.3 - qAbsValue * qAbsValue))) / (1 + Math.Pow(10, 0.1 * qAbsValue)); if (Q <= 0) { coefficientOfPerception = constDependance; } else if (Q > 0) { coefficientOfPerception = 1 - constDependance; } coefficientsOfPerception.Add(coefficientOfPerception); } /* * Count spectral indexes of articulation for each band. */ List<double> spectralIndexesOfArticulation = new List<double>(); for (int i = 0; i < centerFrequencies.Length; i++) { double coefficientOfPerception = coefficientsOfPerception[i]; double weightCoefficient = weightCoefficients[i]; double spectralIndexOfArticulation = coefficientOfPerception * weightCoefficient; spectralIndexesOfArticulation.Add(spectralIndexOfArticulation); } /* * Count integral index of articulation for each band. */ double integralIndexOfArticulation = 0.0; for (int i = 0; i < spectralIndexesOfArticulation.Count; i++) { double index = spectralIndexesOfArticulation[i]; integralIndexOfArticulation += index; } /* * Count syllable intelligibility */ double syllableIntelligibility = 0.0; if (integralIndexOfArticulation <= 0.15) { syllableIntelligibility = Math.Pow(4 * integralIndexOfArticulation, 1.43); } else if ( (integralIndexOfArticulation > 0.15) && (integralIndexOfArticulation <= 0.7) ) { syllableIntelligibility = 1.1 * (1 - 1.17 * Math.Exp(-2.9 * integralIndexOfArticulation)); } else if (integralIndexOfArticulation > 0.7) { syllableIntelligibility = 1.01 * (1 - 9.1 * Math.Exp(-6.9 * integralIndexOfArticulation)); } double wordsIntelligibility = 1.05 * (1 - Math.Exp((-6.15 * syllableIntelligibility) / (1 + syllableIntelligibility))); return wordsIntelligibility; }