/// <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;
        }