Beispiel #1
0
        void ProcessCommon(AudioReader reader, ref float[] impulse)
        {
            int targetLen = QMath.Base2Ceil((int)reader.Length) << 1;

            Complex[] commonSpectrum = new Complex[targetLen];
            FFTCache  cache          = new FFTCache(targetLen);

            for (int ch = 0; ch < reader.ChannelCount; ++ch)
            {
                float[] channel = new float[targetLen];
                WaveformUtils.ExtractChannel(impulse, channel, ch, reader.ChannelCount);

                Complex[] spectrum = Measurements.FFT(channel, cache);
                for (int band = 0; band < spectrum.Length; ++band)
                {
                    commonSpectrum[band] += spectrum[band];
                }
            }

            float mul = 1f / reader.ChannelCount;

            for (int band = 0; band < commonSpectrum.Length; ++band)
            {
                commonSpectrum[band] = (commonSpectrum[band] * mul).Invert();
            }

            Array.Resize(ref impulse, impulse.Length << 1);
            for (int ch = 0; ch < reader.ChannelCount; ++ch)
            {
                Convolver filter = GetFilter(commonSpectrum, 1, reader.SampleRate);
                filter.Process(impulse, ch, reader.ChannelCount);
            }
        }
Beispiel #2
0
        static Dictionary <double, float[]> GetGainDifferences(List <HRTFSetEntry> entries, List <double> hValues)
        {
            Dictionary <double, float[]> result = new Dictionary <double, float[]>(); // Distance; array by hValues

            for (int entry = 0, count = entries.Count; entry < count; ++entry)
            {
                HRTFSetEntry setEntry = entries[entry];
                float        maxGain = float.MinValue, minGain = float.MaxValue;
                for (int channel = 0; channel < setEntry.Data.Length; ++channel)
                {
                    float peak = QMath.GainToDb(WaveformUtils.GetRMS(setEntry.Data[channel]));
                    if (maxGain < peak)
                    {
                        maxGain = peak;
                    }
                    if (minGain > peak)
                    {
                        minGain = peak;
                    }
                }
                if (!result.ContainsKey(setEntry.Distance))
                {
                    result[setEntry.Distance] = new float[hValues.Count];
                }
                result[setEntry.Distance][hValues.IndexOf(setEntry.Azimuth)] = maxGain - minGain;
            }
            return(result);
        }
Beispiel #3
0
        void ProcessPerChannel(AudioReader reader, ref float[] impulse)
        {
            int targetLen = QMath.Base2Ceil((int)reader.Length) << 1;

            Convolver[] filters = new Convolver[reader.ChannelCount];
            FFTCache    cache   = new FFTCache(targetLen);

            for (int ch = 0; ch < reader.ChannelCount; ++ch)
            {
                float[] channel = new float[targetLen];
                WaveformUtils.ExtractChannel(impulse, channel, ch, reader.ChannelCount);

                Complex[] spectrum = Measurements.FFT(channel, cache);
                for (int band = 0; band < spectrum.Length; ++band)
                {
                    spectrum[band] = spectrum[band].Invert();
                }
                filters[ch] = GetFilter(spectrum, WaveformUtils.GetRMS(channel), reader.SampleRate);
            }

            Array.Resize(ref impulse, impulse.Length << 1);
            for (int ch = 0; ch < reader.ChannelCount; ++ch)
            {
                filters[ch].Process(impulse, ch, reader.ChannelCount);
            }
        }
Beispiel #4
0
        /// <summary>
        /// Read all stream data from this block, without separating frames.
        /// </summary>
        public byte[] GetData()
        {
            reader.Position = firstFrame;
            int length = QMath.Sum(frameSizes);

            return(reader.ReadBytes(length));
        }
Beispiel #5
0
        private QVector2IntDir getExitForRect(QRectInt rect)
        {
            QMazeOutputDirection dir;
            int ix = QMath.getRandom(0, rect.width);
            int iy;

            if (ix == 0 || ix == rect.width - 1)
            {
                iy  = QMath.getRandom(0, rect.height);
                dir = (ix == 0 ? QMazeOutputDirection.W : QMazeOutputDirection.E);
            }
            else
            {
                if (QMath.getRandom() > 0.5)
                {
                    iy  = 0;
                    dir = QMazeOutputDirection.N;
                }
                else
                {
                    iy  = rect.height - 1;
                    dir = QMazeOutputDirection.S;
                }
            }
            return(new QVector2IntDir(ix, iy, dir));
        }
Beispiel #6
0
 /// <summary>
 /// Convert a float array to complex a size that's ready for FFT.
 /// </summary>
 public static Complex[] ParseForFFT(this float[] source)
 {
     Complex[] result = new Complex[QMath.Base2Ceil(source.Length)];
     for (int i = 0; i < source.Length; ++i)
     {
         result[i].Real = source[i];
     }
     return(result);
 }
Beispiel #7
0
        /// <summary>
        /// Moves a graph's average value to 0.
        /// </summary>
        public static void Normalize(float[] graph)
        {
            float avg = QMath.Average(graph);

            for (int i = 0; i < graph.Length; ++i)
            {
                graph[i] -= avg;
            }
        }
Beispiel #8
0
        private void generateNextLevel()
        {
            baseMazeEngine.destroyImmediateMazeGeometry();

            List <QVector2IntDir> exitPositionList = baseMazeEngine.getExitPositionList();

            if (exitPositionList.Count > 1)
            {
                exitPositionList.RemoveAt(0);
                baseMazeEngine.setExitPositionList(exitPositionList);
            }

            List <QVector2Int> obstaclePositionList = new List <QVector2Int>();

            if (prevMazeEngine == null)
            {
                prevRect = new QRectInt(QMath.getRandom(1, baseMazeEngine.getMazeWidth() - CHILD_MAZE_SIZE - 2),
                                        QMath.getRandom(1, baseMazeEngine.getMazeHeight() - CHILD_MAZE_SIZE - 2), CHILD_MAZE_SIZE, CHILD_MAZE_SIZE);
                obstaclePositionList.AddRange(rectToList(prevRect));
                prevMazeEngine = createChildMaze(prevRect, childMazeEngine_1);
                prevMazeEngine.generateMaze();

                player.setPosition(prevMazeEngine.transform.TransformPoint(prevMazeEngine.getFinishPositionList()[0].toVector3()));
            }
            else
            {
                prevMazeEngine.destroyImmediateMazeGeometry();
                prevRect       = nextRect;
                prevMazeEngine = nextMazeEngine;
                obstaclePositionList.AddRange(rectToList(prevRect));
            }

            nextRect = new QRectInt(QMath.getRandom(1, baseMazeEngine.getMazeWidth() - CHILD_MAZE_SIZE - 2),
                                    QMath.getRandom(1, baseMazeEngine.getMazeHeight() - CHILD_MAZE_SIZE - 2), CHILD_MAZE_SIZE, CHILD_MAZE_SIZE);
            while (isRectNear(prevRect, nextRect))
            {
                nextRect.x = QMath.getRandom(1, baseMazeEngine.getMazeWidth() - CHILD_MAZE_SIZE - 2);
                nextRect.y = QMath.getRandom(1, baseMazeEngine.getMazeHeight() - CHILD_MAZE_SIZE - 2);
            }

            obstaclePositionList.AddRange(rectToList(nextRect));

            baseMazeEngine.setObstaclePositionList(obstaclePositionList);
            nextMazeEngine = createChildMaze(nextRect, prevMazeEngine == childMazeEngine_1 ? childMazeEngine_2 : childMazeEngine_1);
            nextMazeEngine.generateMaze();
            List <QVector2IntDir> nextMazeEngineFinishPositionList = nextMazeEngine.getFinishPositionList();

            finishTransform.parent        = nextMazeEngine.getMazeData()[nextMazeEngineFinishPositionList[0].x][nextMazeEngineFinishPositionList[0].y].geometry.transform;
            finishTransform.localPosition = new Vector3();

            player.setGoal(nextMazeEngine.transform.TransformPoint(nextMazeEngineFinishPositionList[0].toVector3()), goalReachedHandler);

            baseMazeEngine.generateMaze();

            currentLevel++;
            levelText.text = "LEVEL: " + currentLevel;
        }
Beispiel #9
0
        public void TruncateTest()
        {
            Decimal i = 16.151m;
            int     midPointRouting = 2;
            Decimal expected        = 16.15m;
            Decimal actual;

            actual = QMath.Truncate(i, midPointRouting);
            Assert.AreEqual(expected, actual);
        }
Beispiel #10
0
        public void CeillingTest_d()
        {
            Decimal i = 16.156m;
            int     midPointRouting = 2;
            Decimal expected        = 16.16m;
            Decimal actual;

            actual = QMath.Ceilling(i, midPointRouting);
            Assert.AreEqual(expected, actual);
        }
Beispiel #11
0
        //TODO Switch to latest versions of .NET and implement a BigInt Version.

        /// <summary>
        /// Rounds the given value to the specified number of significant figures/digits.
        /// </summary>
        /// <param name="val"> The value to be rounded </param>
        /// <param name="digits"> the number of significant figures </param>
        /// <returns> the rounded number </returns>
        public static double RoundToSignificantDigits(double val, int digits)
        {
            if (val == 0)
            {
                return(0);
            }

            double scale = QMath.Pow(10, QMath.Floor(QMath.Log10(QMath.Abs(val))) + 1);

            return(scale * QMath.Round(val / scale, digits));
        }
Beispiel #12
0
#pragma warning disable CS0675 // Bitwise-or operator used on a sign-extended operand
        /// <summary>
        /// Reads the next VINT from a stream, does not cut the leading 1.
        /// </summary>
        public static long ReadTag(Stream reader)
        {
            int  first      = reader.ReadByte();
            int  extraBytes = QMath.LeadingZerosInByte(first);
            long value      = first;

            for (int i = 0; i < extraBytes; ++i)
            {
                value = (value << 8) | reader.ReadByte();
            }
            return(value);
        }
Beispiel #13
0
        /// <summary>
        /// Apply variable smoothing (in octaves) on a graph drawn with
        /// <see cref="ConvertToGraph(float[], double, double, int, int)"/>.
        /// </summary>
        public static float[] SmoothGraph(float[] samples, float startFreq, float endFreq, float startOctave, float endOctave)
        {
            float[] startGraph = SmoothGraph(samples, startFreq, endFreq, startOctave),
            endGraph = SmoothGraph(samples, startFreq, endFreq, endOctave),
            output   = new float[samples.Length];
            float positioner = 1f / samples.Length;

            for (int i = 0; i < samples.Length; ++i)
            {
                output[i] = QMath.Lerp(startGraph[i], endGraph[i], i * positioner);
            }
            return(output);
        }
Beispiel #14
0
        /// <summary>
        /// Cache the samples if the source should be rendered. This wouldn't be thread safe.
        /// </summary>
        /// <returns>The collection should be performed, as all requirements are met</returns>
        protected internal virtual bool Precollect()
        {
            if (delay > 0)
            {
                delay -= listener.UpdateRate;
                return(false);
            }
            if (listener.sourceDistances.Contains(distance))
            {
                if (listener.AudioQuality != QualityModes.Low)
                {
                    if (DopplerLevel == 0)
                    {
                        calculatedPitch = Pitch;
                    }
                    else
                    {
                        float dopplerTarget = Pitch + lastDoppler * // c / (c - dv), dv = ds / dt
                                              (SpeedOfSound / (SpeedOfSound - (lastDistance - distance) / listener.pulseDelta) - 1);
                        lastDoppler = Math.Clamp(QMath.Lerp(lastDoppler, dopplerTarget,
                                                            10 * listener.UpdateRate / (float)listener.SampleRate), 0, DopplerLevel);
                        calculatedPitch = Math.Clamp(lastDoppler, .5f, 3f);
                    }
                }
                else
                {
                    calculatedPitch = 1; // Disable any pitch change on low quality
                }

                resampleMult      = listener.SampleRate != Clip.SampleRate ? (float)Clip.SampleRate / listener.SampleRate : 1;
                baseUpdateRate    = (int)(listener.UpdateRate * calculatedPitch);
                PitchedUpdateRate = (int)(baseUpdateRate * resampleMult);
                if (samples.Length != PitchedUpdateRate)
                {
                    samples = new float[PitchedUpdateRate];
                }
                if (Clip.Channels == 2 && leftSamples.Length != PitchedUpdateRate)
                {
                    leftSamples  = new float[PitchedUpdateRate];
                    rightSamples = new float[PitchedUpdateRate];
                }
                Rendered = GetSamples();
                if (rendered.Length != Listener.Channels.Length * listener.UpdateRate)
                {
                    rendered = new float[Listener.Channels.Length * listener.UpdateRate];
                }
                return(true);
            }
            Rendered = null;
            return(false);
        }
Beispiel #15
0
        /// <summary>
        /// Constructs an optimized convolution.
        /// </summary>
        public FastConvolver(float[] impulse, int delay = 0)
        {
            int fftSize = 2 << QMath.Log2Ceil(impulse.Length); // Zero padding for the falloff to have space

            cache  = new FFTCache(fftSize);
            filter = new Complex[fftSize];
            for (int sample = 0; sample < impulse.Length; ++sample)
            {
                filter[sample].Real = impulse[sample];
            }
            filter.InPlaceFFT(cache);
            present    = new Complex[fftSize];
            future     = new float[fftSize + delay];
            this.delay = delay;
        }
 public int UpdateMe(Node node, Person me)
 {
     me.angleQ = QMath.RadLimitQ(me.angleQ + node.angleDeltaQ);
     me.xQ    += QMath.CosQ(me.angleQ, me.speedRev);
     me.zQ    += QMath.SinQ(me.angleQ, me.speedRev);
     me.steps--;
     if (me.steps == 0)
     {
         return((int)node.NodeFeedback.targetReached);
     }
     else
     {
         return((int)node.NodeFeedback.notFinished);
     }
 }
Beispiel #17
0
 /// <summary>
 /// Gets the number of minimum bits required to represent this integer. Of course, all integers take the same space but a lower integer can be represented by
 /// less bits.
 /// </summary>
 /// <param name="number"> the integer </param>
 /// <returns> the bits required to represent said integer. if negative, will return 32 as negative values require the flag. input the absolute value
 /// if you wish the minimum bits if the number was not negative to be returned. </returns>
 public static int GetIntMinimumBits(int number)
 {
     if (number < 0)
     {
         return(32);
     }
     for (int i = 31; i >= 0; i--)
     {
         if (number % QMath.Pow(2, i) != number)
         {
             return(i + 1);                                    //TODO Left here.
         }
     }
     return(1);
 }
Beispiel #18
0
        void ProcessImpulse(object sender, RoutedEventArgs e)
        {
            if (browser.ShowDialog().Value)
            {
                AudioReader reader  = AudioReader.Open(browser.FileName);
                float[]     impulse = reader.Read();
                float       gain    = 1;
                if (keepGain.IsChecked.Value)
                {
                    gain = WaveformUtils.GetPeak(impulse);
                }

                if (commonEQ.IsChecked.Value)
                {
                    ProcessCommon(reader, ref impulse);
                }
                else
                {
                    ProcessPerChannel(reader, ref impulse);
                }

                if (keepGain.IsChecked.Value)
                {
                    WaveformUtils.Gain(impulse, gain / WaveformUtils.GetPeak(impulse));
                }

                BitDepth bits = reader.Bits;
                if (forceFloat.IsChecked.Value)
                {
                    bits = BitDepth.Float32;
                }

                int targetLen = QMath.Base2Ceil((int)reader.Length);
                if (separateExport.IsChecked.Value)
                {
                    ReferenceChannel[] channels = ChannelPrototype.GetStandardMatrix(reader.ChannelCount);
                    for (int ch = 0; ch < reader.ChannelCount; ++ch)
                    {
                        string exportName  = Path.GetFileName(browser.FileName);
                        int    idx         = exportName.LastIndexOf('.');
                        string channelName = ChannelPrototype.Mapping[(int)channels[ch]].Name;
                        exporter.FileName = $"{exportName[..idx]} - {channelName}{exportName[idx..]}";
        private void generateNextPart()
        {
            QMazeEngine mazeEngine = (QMazeEngine)GameObject.Instantiate(mazeEnginePrefab);

            mazeEngine.getMazePiecePack().getPiece(QMazePieceType.Intersection).use = false;
            mazeEngine.transform.position = new Vector3(currentPartId * mazeEngine.getMazeWidth() * mazeEngine.getMazePieceWidth(), 0, 0);
            parts.Enqueue(mazeEngine);

            if (currentPartId == 0)
            {
                List <QVector2IntDir> startPositionList = new List <QVector2IntDir>();
                startPositionList.Add(new QVector2IntDir(0, 0, QMazeOutputDirection.NotSpecified));
                mazeEngine.setStartPositionList(startPositionList);

                lastExitY = QMath.getRandom(0, mazeEngine.getMazeHeight() - 1);

                List <QVector2IntDir> exitPositionList = new List <QVector2IntDir>();
                exitPositionList.Add(new QVector2IntDir(mazeEngine.getMazeWidth() - 1, lastExitY, QMazeOutputDirection.E));
                mazeEngine.setExitPositionList(exitPositionList);
            }
            else
            {
                List <QVector2IntDir> exitPositionList = new List <QVector2IntDir>();
                exitPositionList.Add(new QVector2IntDir(0, lastExitY, QMazeOutputDirection.W));

                lastExitY = QMath.getRandom(0, mazeEngine.getMazeHeight() - 1);

                exitPositionList.Add(new QVector2IntDir(mazeEngine.getMazeWidth() - 1, lastExitY, QMazeOutputDirection.E));
                mazeEngine.setExitPositionList(exitPositionList);
            }

            GameObject block = (GameObject)GameObject.Instantiate(blockPrefab);

            block.transform.parent   = mazeEngine.gameObject.transform;
            block.transform.position = new Vector3(((currentPartId + 1) * mazeEngine.getMazeWidth() - 0.5f) * mazeEngine.getMazePieceWidth(), 0, -lastExitY * mazeEngine.getMazePieceHeight());
            block.GetComponent <QBlock>().triggerHandlerEvent += blockHandler;
            mazeEngine.generateMazeAsync(this, 0.016f);

            levelText.text = "LEVEL: " + currentPartId;
            currentPartId++;
        }
Beispiel #20
0
        /// <summary>
        /// Constructs an optimized convolution.
        /// </summary>
        public DualConvolver(float[] impulse1, float[] impulse2, int delay1 = 0, int delay2 = 0)
        {
            int impulseLength = Math.Max(impulse1.Length, impulse2.Length);
            int fftSize       = 2 << QMath.Log2Ceil(impulseLength); // Zero padding for the falloff to have space

            cache  = new FFTCache(fftSize);
            filter = new Complex[fftSize];
            for (int sample = 0; sample < impulse1.Length; ++sample)
            {
                filter[sample].Real = impulse1[sample];
            }
            for (int sample = 0; sample < impulse2.Length; ++sample)
            {
                filter[sample].Imaginary = impulse2[sample];
            }
            filter.InPlaceFFT(cache);
            present     = new Complex[fftSize];
            future      = new Complex[fftSize + Math.Max(delay1, delay2)];
            this.delay1 = delay1;
            this.delay2 = delay2;
        }
Beispiel #21
0
 /// <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);
     }
 }
Beispiel #22
0
        /// <summary>
        /// Resamples a single channel with medium quality (linear interpolation).
        /// </summary>
        /// <param name="samples">Samples of the source channel</param>
        /// <param name="to">New sample count</param>
        /// <returns>Returns a resampled version of the given array</returns>
        public static float[] Lerp(float[] samples, int to)
        {
            if (samples.Length == to)
            {
                return(samples);
            }
            float[] output    = new float[to];
            float   ratio     = samples.Length / (float)to;
            int     lerpUntil = (int)((samples.Length - 1) / ratio); // Halving point where i * ratio would be over the array

            for (int i = 0; i < lerpUntil; ++i)
            {
                int sample = (int)(i * ratio);
                output[i] = QMath.Lerp(samples[sample], samples[sample + 1], i * ratio % 1);
            }
            for (int i = lerpUntil; i < to; ++i)
            {
                output[i] = samples[(int)(i * ratio)];
            }
            return(output);
        }
Beispiel #23
0
 void Update()
 {
     float[][] samples = Source.cavernSource.Rendered;
     if (samples != null)
     {
         float peakSize = float.NegativeInfinity;
         for (int channel = 0; channel < samples.Length; ++channel)
         {
             float channelSize = 20 * (float)Math.Log10(WaveformUtils.GetPeak(samples[channel]));
             if (channelSize < -600)
             {
                 channelSize = -600;
             }
             if (peakSize < channelSize)
             {
                 peakSize = channelSize;
             }
         }
         float size = Mathf.Clamp(peakSize / -DynamicRange + 1, 0, 1);
         scale = QMath.Lerp(scale, (MaxSize - MinSize) * size + MinSize, 1 - Smoothing);
         transform.localScale = new Vector3(scale, scale, scale);
     }
 }
Beispiel #24
0
        void ObjectBasicInfo(BitExtractor extractor, bool readAllBlocks)
        {
            int blocks = readAllBlocks ? 3 : extractor.Read(2);

            // Gain
            if ((blocks & 2) != 0)
            {
                int gainHelper = extractor.Read(2);
                gain = gainHelper switch {
                    0 => 1,
                    1 => 0,
                    2 => (gainHelper = extractor.Read(6)) < 15 ?
                    QMath.DbToGain(15 - gainHelper) : QMath.DbToGain(14 - gainHelper),
                    _ => - 1,
                };
            }

            // Priority - unneccessary, everything's rendered
            if ((blocks & 1) != 0 && !extractor.ReadBit())
            {
                extractor.Skip(5);
            }
        }
Beispiel #25
0
        /// <summary>
        /// Reads the next VINT from a stream, cuts the leading 1, reads the correct value.
        /// </summary>
        public static long ReadValue(Stream reader)
        {
            long value = ReadTag(reader);

            return(value - (1L << QMath.BitsAfterMSB(value)));
        }
Beispiel #26
0
        /// <summary>
        /// For E-AC-3, data for multiple blocks is included in an audio frame header.
        /// </summary>
        void AudioFrame()
        {
            expstre      = header.Blocks != 6 || extractor.ReadBit();
            ahte         = header.Blocks == 6 && extractor.ReadBit();
            snroffststr  = extractor.Read(2);
            transproce   = extractor.ReadBit();
            blkswe       = extractor.ReadBit();
            dithflage    = extractor.ReadBit();
            bamode       = extractor.ReadBit();
            frmfgaincode = extractor.ReadBit();
            dbaflde      = extractor.ReadBit();
            skipflde     = extractor.ReadBit();
            spxattene    = extractor.ReadBit();

            if (header.ChannelMode > 1)   // Not mono
            {
                cplstre[0] = true;
                cplinu[0]  = extractor.ReadBit();
                for (int block = 1; block < cplstre.Length; ++block)
                {
                    if (cplstre[block] = extractor.ReadBit())
                    {
                        cplinu[block] = extractor.ReadBit();
                    }
                    else
                    {
                        cplinu[block] = cplinu[block - 1];
                    }
                }
            }
            else
            {
                for (int block = 1; block < cplstre.Length; ++block)
                {
                    cplinu[block] = false;
                }
            }

            // Exponent strategy data init
            if (expstre)
            {
                for (int block = 0; block < cplexpstr.Length; ++block)
                {
                    if (cplinu[block])
                    {
                        cplexpstr[block] = (ExpStrat)extractor.Read(2);
                    }
                    for (int channel = 0; channel < channels.Length; ++channel)
                    {
                        chexpstr[block][channel] = (ExpStrat)extractor.Read(2);
                    }
                }
            }
            else
            {
                int ncplblks = 0;
                for (int block = 0; block < cplinu.Length; ++block)
                {
                    if (cplinu[block])
                    {
                        ++ncplblks;
                    }
                }
                if (header.ChannelMode > 1 && ncplblks > 0)
                {
                    frmcplexpstr = extractor.Read(5);
                }
                for (int channel = 0; channel < channels.Length; ++channel)
                {
                    frmchexpstr[channel] = extractor.Read(5);
                }

                for (int block = 0; block < cplexpstr.Length; ++block)
                {
                    cplexpstr[block] = frmcplexpstr_tbl[frmcplexpstr][block];
                    for (int channel = 0; channel < channels.Length; ++channel)
                    {
                        chexpstr[block][channel] = frmcplexpstr_tbl[frmchexpstr[channel]][block];
                    }
                }
            }

            if (header.LFE)
            {
                for (int block = 0; block < lfeexpstr.Length; ++block)
                {
                    lfeexpstr[block] = extractor.ReadBit();
                }
            }

            // Converter exponent strategy data
            if (header.StreamType == StreamTypes.Independent &&
                (convexpstre = header.Blocks == 6 || extractor.ReadBit()))
            {
                for (int channel = 0; channel < channels.Length; ++channel)
                {
                    convexpstr[channel] = extractor.Read(5);
                }
            }

            // AHT data
            if (ahte)
            {
                throw new UnsupportedFeatureException("AHT");
            }

            // Audio frame SNR offset data
            if (snroffststr == 0)
            {
                frmcsnroffst = extractor.Read(6);
                frmfsnroffst = extractor.Read(4);
            }

            // Transient pre-noise processing data
            if (transproce)
            {
                for (int channel = 0; channel < channels.Length; ++channel)
                {
                    if (chintransproc[channel] = extractor.ReadBit())
                    {
                        transprocloc[channel] = extractor.Read(10);
                        transproclen[channel] = extractor.Read(8);
                    }
                }
            }

            // Spectral extension attenuation data
            if (spxattene)
            {
                for (int ch = 0; ch < channels.Length; ++ch)
                {
                    if (chinspxatten[ch] = extractor.ReadBit())
                    {
                        spxattencod[ch] = extractor.Read(5);
                    }
                }
            }

            if (blkstrtinfoe = header.Blocks != 1 && extractor.ReadBit())
            {
                int nblkstrtbits = (header.Blocks - 1) * (4 + QMath.Log2Ceil(header.WordsPerSyncframe));
                blkstrtinfo = extractor.Read(nblkstrtbits);
            }

            // Syntax state init
            for (int channel = 0; channel < channels.Length; ++channel)
            {
                firstspxcos[channel] = true;
                firstcplcos[channel] = true;
            }
            firstcplleak = true;
        }
Beispiel #27
0
        /// <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;
                }
            }
        }
Beispiel #28
0
        void Update()
        {
            if (!CavernSource)
            {
                OnDisable();
                return;
            }
            for (int row = 0; row < Rows; ++row)
            {
                for (int column = 0; column < Columns; ++column)
                {
                    SeatMovements[row][column].Height = 200;
                }
            }
            int lastRow = Rows - 1, lastColumn = Columns - 1;

            if (CavernSource[0] != null) // Front left
            {
                SeatMovements[0][0].Height = CavernSource[0].Height;
            }
            if (CavernSource[1] != null) // Front right
            {
                SeatMovements[0][lastColumn].Height = CavernSource[1].Height;
            }
            if (CavernSource[2] != null && CavernSource[2].Height != Cavernizer.unsetHeight) // Center
            {
                SeatMovements[0][lastColumn / 2].Height = CavernSource[2].Height;
            }
            if (CavernSource[6] != null) // Side left
            {
                SeatMovements[lastRow][0].Height = CavernSource[6].Height;
            }
            if (CavernSource[7] != null) // Side right
            {
                SeatMovements[lastRow][lastColumn].Height = CavernSource[7].Height;
            }
            // Addition is okay, and should be used, as the rears are near the sides in the back corners.
            if (CavernSource[4] != null) // Rear left
            {
                SeatMovements[lastRow][0].Height += CavernSource[4].Height;
            }
            if (CavernSource[5] != null) // Rear right
            {
                SeatMovements[lastRow][lastColumn].Height += CavernSource[5].Height;
            }
            SpatializedChannel rearCenter = CavernSource.GetChannel(ReferenceChannel.RearCenter);

            if (rearCenter != null) // Rear center
            {
                SeatMovements[lastRow][lastColumn / 2].Height = rearCenter.Height;
            }
            // Use the front channels for moving all seats if nothing else is available for the rear sides
            if (SeatMovements[lastRow][0].Height == 200)
            {
                SeatMovements[lastRow][0].Height = SeatMovements[0][0].Height;
            }
            if (SeatMovements[lastRow][lastColumn].Height == 200)
            {
                SeatMovements[lastRow][lastColumn].Height = SeatMovements[0][lastColumn].Height;
            }
            // Seat position interpolation
            for (int row = 0; row < Rows; ++row)
            {
                int prev = 0;
                for (int column = 0; column < Columns; ++column)
                {
                    if (SeatMovements[row][column].Height != 200)
                    {
                        float lerpDiv = column - prev;
                        for (int oldColumn = prev; oldColumn < column; ++oldColumn)
                        {
                            SeatMovements[row][oldColumn].Height =
                                QMath.Lerp(SeatMovements[row][prev].Height, SeatMovements[row][column].Height, (oldColumn - prev) / lerpDiv);
                        }
                        prev = column;
                    }
                }
                if (prev != lastColumn)
                {
                    float lerpDiv = lastColumn - prev;
                    for (int oldColumn = prev; oldColumn < lastColumn; ++oldColumn)
                    {
                        SeatMovements[row][oldColumn].Height =
                            QMath.Lerp(SeatMovements[row][prev].Height, SeatMovements[row][lastColumn].Height, (oldColumn - prev) / lerpDiv);
                    }
                }
            }
            for (int column = 0; column < Columns; ++column)
            {
                int prev = 0;
                for (int row = 0; row < Rows; ++row)
                {
                    if (SeatMovements[row][column].Height != 200)
                    {
                        float lerpDiv = row - prev;
                        for (int oldRow = prev; oldRow < row; ++oldRow)
                        {
                            SeatMovements[oldRow][column].Height =
                                QMath.Lerp(SeatMovements[prev][column].Height, SeatMovements[row][column].Height, (oldRow - prev) / lerpDiv);
                        }
                        prev = row;
                    }
                }
                if (prev != lastRow)
                {
                    float lerpDiv = lastRow - prev;
                    for (int oldRow = prev; oldRow < lastRow; ++oldRow)
                    {
                        SeatMovements[oldRow][column].Height = QMath.Lerp(SeatMovements[prev][column].Height, SeatMovements[lastRow][column].Height,
                                                                          (oldRow - prev) / lerpDiv);
                    }
                }
            }
            // Seat rotation interpolation
            for (int row = 0; row < Rows; ++row)
            {
                SeatMovements[row][0].Rotation.z = Mathf.Clamp((SeatMovements[row][1].Height - SeatMovements[row][0].Height) * RotationConstant * 2,
                                                               -MaxRotationSide, MaxRotationSide);
                for (int column = 1; column < lastColumn; ++column)
                {
                    SeatMovements[row][column].Rotation.z = Mathf.Clamp((SeatMovements[row][column + 1].Height - SeatMovements[row][column - 1].Height) *
                                                                        RotationConstant, -MaxRotationSide, MaxRotationSide);
                }
                SeatMovements[row][lastColumn].Rotation.z = Mathf.Clamp((SeatMovements[row][lastColumn].Height - SeatMovements[row][lastColumn - 1].Height) *
                                                                        RotationConstant * 2, -MaxRotationSide, MaxRotationSide);
            }
            for (int column = 0; column < Columns; ++column)
            {
                SeatMovements[0][column].Rotation.x = Mathf.Clamp((SeatMovements[1][column].Height - SeatMovements[0][column].Height) * RotationConstant * 2, -20, 20);
                for (int row = 1; row < lastRow; ++row)
                {
                    SeatMovements[row][column].Rotation.x = Mathf.Clamp((SeatMovements[row + 1][column].Height - SeatMovements[row - 1][column].Height) *
                                                                        RotationConstant, -MaxRotationFace, MaxRotationFace);
                }
                SeatMovements[lastRow][column].Rotation.x = Mathf.Clamp((SeatMovements[lastRow][column].Height - SeatMovements[lastRow - 1][column].Height) *
                                                                        RotationConstant * 2, -MaxRotationFace, MaxRotationFace);
            }
        }
Beispiel #29
0
        /// <summary>
        /// Process the source and returns a mix to be added to the output.
        /// </summary>
        protected internal virtual float[] Collect()
        {
            // Preparations, clean environment
            int channels   = Listener.Channels.Length,
                updateRate = listener.UpdateRate;

            Array.Clear(rendered, 0, rendered.Length);

            // Render audio if not muted
            if (!Mute)
            {
                int clipChannels = Clip.Channels;

                // 3D renderer preprocessing
                if (SpatialBlend != 0)
                {
                    if (listener.AudioQuality >= QualityModes.High && clipChannels != 1)   // Mono downmix above medium quality
                    {
                        Array.Clear(samples, 0, PitchedUpdateRate);
                        for (int channel = 0; channel < clipChannels; ++channel)
                        {
                            WaveformUtils.Mix(Rendered[channel], samples);
                        }
                        WaveformUtils.Gain(samples, 1f / clipChannels);
                    }
                    else     // First channel only otherwise
                    {
                        Array.Copy(Rendered[0], samples, PitchedUpdateRate);
                    }
                }

                // 1D renderer
                if (SpatialBlend != 1)
                {
                    float volume1D = Volume * (1f - SpatialBlend);
                    // 1:1 mix for non-stereo sources
                    if (clipChannels != 2)
                    {
                        samples = Resample.Adaptive(samples, updateRate, listener.AudioQuality);
                        WriteOutput(samples, rendered, volume1D, channels);
                    }

                    // Full side mix for stereo sources
                    else
                    {
                        Array.Copy(Rendered[0], leftSamples, PitchedUpdateRate);
                        Array.Copy(Rendered[1], rightSamples, PitchedUpdateRate);
                        leftSamples  = Resample.Adaptive(leftSamples, updateRate, listener.AudioQuality);
                        rightSamples = Resample.Adaptive(rightSamples, updateRate, listener.AudioQuality);
                        Stereo1DMix(volume1D);
                    }
                }

                // 3D mix, if the source is in range
                if (SpatialBlend != 0 && distance < listener.Range)
                {
                    Vector3 direction       = (Position - listener.Position).RotateInverse(listener.Rotation);
                    float   rolloffDistance = GetRolloff();
                    samples        = Resample.Adaptive(samples, updateRate, listener.AudioQuality);
                    baseUpdateRate = samples.Length;
                    // Apply filter if set
                    if (SpatialFilter != null)
                    {
                        SpatialFilter.Process(samples);
                    }

                    // Distance simulation for HRTF
                    // TODO: gain correction for this in both engines
                    if (DistanceSimulation && Listener.HeadphoneVirtualizer)
                    {
                        if (distancer == null)
                        {
                            distancer = new Distancer(this);
                        }
                        distancer.Generate(direction.X > 0, samples);
                    }

                    // ------------------------------------------------------------------
                    // Balance-based engine for symmetrical layouts
                    // ------------------------------------------------------------------
                    if (Listener.IsSymmetric)
                    {
                        float volume3D = Volume * rolloffDistance * SpatialBlend;
                        if (!LFE)
                        {
                            // Find a bounding box
                            int bottomFrontLeft  = -1,
                                bottomFrontRight = -1,
                                bottomRearLeft   = -1,
                                bottomRearRight  = -1,
                                topFrontLeft     = -1,
                                topFrontRight    = -1,
                                topRearLeft      = -1,
                                topRearRight     = -1;
                            // Closest layers on Y and Z axes
                            float closestTop    = 78,
                                  closestBottom = -73,
                                  closestTF     = 75,
                                  closestTR     = -73,
                                  closestBF     = 75,
                                  closestBR     = -69;
                            // Find closest horizontal layers
                            if (Listener.HeadphoneVirtualizer)
                            {
                                direction = direction.WarpToCube() / Listener.EnvironmentSize;
                            }
                            else
                            {
                                direction /= Listener.EnvironmentSize;
                            }
                            for (int channel = 0; channel < channels; ++channel)
                            {
                                if (!Listener.Channels[channel].LFE)
                                {
                                    float channelY = Listener.Channels[channel].CubicalPos.Y;
                                    if (channelY < direction.Y)
                                    {
                                        if (channelY > closestBottom)
                                        {
                                            closestBottom = channelY;
                                        }
                                    }
                                    else if (channelY < closestTop)
                                    {
                                        closestTop = channelY;
                                    }
                                }
                            }
                            for (int channel = 0; channel < channels; ++channel)
                            {
                                if (!Listener.Channels[channel].LFE)
                                {
                                    Vector3 channelPos = Listener.Channels[channel].CubicalPos;
                                    if (channelPos.Y == closestBottom)   // Bottom layer
                                    {
                                        AssignHorizontalLayer(channel, ref bottomFrontLeft, ref bottomFrontRight,
                                                              ref bottomRearLeft, ref bottomRearRight, ref closestBF, ref closestBR,
                                                              direction, channelPos);
                                    }
                                    if (channelPos.Y == closestTop)   // Top layer
                                    {
                                        AssignHorizontalLayer(channel, ref topFrontLeft, ref topFrontRight,
                                                              ref topRearLeft, ref topRearRight,
                                                              ref closestTF, ref closestTR, direction, channelPos);
                                    }
                                }
                            }
                            // Fix incomplete top layer
                            FixIncompleteLayer(ref topFrontLeft, ref topFrontRight, ref topRearLeft, ref topRearRight);

                            // When the bottom layer is completely empty (= the source is below all channels), copy the top layer
                            if (bottomFrontLeft == -1 && bottomFrontRight == -1 &&
                                bottomRearLeft == -1 && bottomRearRight == -1)
                            {
                                bottomFrontLeft  = topFrontLeft;
                                bottomFrontRight = topFrontRight;
                                bottomRearLeft   = topRearLeft;
                                bottomRearRight  = topRearRight;
                            }
                            // Fix incomplete bottom layer
                            else
                            {
                                FixIncompleteLayer(ref bottomFrontLeft, ref bottomFrontRight,
                                                   ref bottomRearLeft, ref bottomRearRight);
                            }

                            // When the top layer is completely empty (= the source is above all channels), copy the bottom layer
                            if (topFrontLeft == -1 || topFrontRight == -1 || topRearLeft == -1 || topRearRight == -1)
                            {
                                topFrontLeft  = bottomFrontLeft;
                                topFrontRight = bottomFrontRight;
                                topRearLeft   = bottomRearLeft;
                                topRearRight  = bottomRearRight;
                            }

                            // Spatial mix gain precalculation
                            Vector2 layerVol = new Vector2(.5f); // (bottom; top)
                            if (topFrontLeft != bottomFrontLeft) // Height ratio calculation
                            {
                                float bottomY = Listener.Channels[bottomFrontLeft].CubicalPos.Y;
                                layerVol.Y = (direction.Y - bottomY) / (Listener.Channels[topFrontLeft].CubicalPos.Y - bottomY);
                                layerVol.X = 1f - layerVol.Y;
                            }

                            // Length ratios (bottom; top)
                            Vector2 frontVol = new Vector2(LengthRatio(bottomRearLeft, bottomFrontLeft, direction.Z),
                                                           LengthRatio(topRearLeft, topFrontLeft, direction.Z));
                            // Width ratios
                            float BFRVol        = WidthRatio(bottomFrontLeft, bottomFrontRight, direction.X),
                                  BRRVol        = WidthRatio(bottomRearLeft, bottomRearRight, direction.X),
                                  TFRVol        = WidthRatio(topFrontLeft, topFrontRight, direction.X),
                                  TRRVol        = WidthRatio(topRearLeft, topRearRight, direction.X),
                                  innerVolume3D = volume3D;
                            if (Size != 0)
                            {
                                frontVol       = QMath.Lerp(frontVol, new Vector2(.5f), Size);
                                BFRVol         = QMath.Lerp(BFRVol, .5f, Size);
                                BRRVol         = QMath.Lerp(BRRVol, .5f, Size);
                                TFRVol         = QMath.Lerp(TFRVol, .5f, Size);
                                TRRVol         = QMath.Lerp(TRRVol, .5f, Size);
                                innerVolume3D *= 1f - Size;
                                float extraChannelVolume = volume3D * Size / channels;
                                for (int channel = 0; channel < channels; ++channel)
                                {
                                    WriteOutput(samples, rendered, extraChannelVolume, channel, channels);
                                }
                            }

                            // Spatial mix gain finalization
                            Vector2 rearVol = new Vector2(1) - frontVol;
                            layerVol *= innerVolume3D;
                            frontVol *= layerVol;
                            rearVol  *= layerVol;
                            WriteOutput(samples, rendered, frontVol.X * (1f - BFRVol), bottomFrontLeft, channels);
                            WriteOutput(samples, rendered, frontVol.X * BFRVol, bottomFrontRight, channels);
                            WriteOutput(samples, rendered, rearVol.X * (1f - BRRVol), bottomRearLeft, channels);
                            WriteOutput(samples, rendered, rearVol.X * BRRVol, bottomRearRight, channels);
                            WriteOutput(samples, rendered, frontVol.Y * (1f - TFRVol), topFrontLeft, channels);
                            WriteOutput(samples, rendered, frontVol.Y * TFRVol, topFrontRight, channels);
                            WriteOutput(samples, rendered, rearVol.Y * (1f - TRRVol), topRearLeft, channels);
                            WriteOutput(samples, rendered, rearVol.Y * TRRVol, topRearRight, channels);
                        }
                        // LFE mix
                        if (!listener.LFESeparation || LFE)
                        {
                            for (int channel = 0; channel < channels; ++channel)
                            {
                                if (Listener.Channels[channel].LFE)
                                {
                                    WriteOutput(samples, rendered, volume3D, channel, channels);
                                }
                            }
                        }
                    }

                    // ------------------------------------------------------------------
                    // Directional/distance-based engine for asymmetrical layouts
                    // ------------------------------------------------------------------
                    else
                    {
                        // Angle match calculations
                        float[] angleMatches;
                        if (listener.AudioQuality >= QualityModes.High)
                        {
                            angleMatches = CalculateAngleMatches(channels, direction);
                        }
                        else
                        {
                            angleMatches = LinearizeAngleMatches(channels, direction);
                        }

                        // Object size extension
                        if (Size != 0)
                        {
                            float maxAngleMatch = angleMatches[0];
                            for (int channel = 1; channel < channels; ++channel)
                            {
                                if (maxAngleMatch < angleMatches[channel])
                                {
                                    maxAngleMatch = angleMatches[channel];
                                }
                            }
                            for (int channel = 0; channel < channels; ++channel)
                            {
                                angleMatches[channel] = QMath.Lerp(angleMatches[channel], maxAngleMatch, Size);
                            }
                        }

                        // Only use the closest 3 speakers on non-Perfect qualities or in Theatre mode
                        if (listener.AudioQuality != QualityModes.Perfect || Listener.EnvironmentType == Environments.Theatre)
                        {
                            float top0 = 0, top1 = 0, top2 = 0;
                            for (int channel = 0; channel < channels; ++channel)
                            {
                                if (!Listener.Channels[channel].LFE)
                                {
                                    float match = angleMatches[channel];
                                    if (top0 < match)
                                    {
                                        top2 = top1;
                                        top1 = top0;
                                        top0 = match;
                                    }
                                    else if (top1 < match)
                                    {
                                        top2 = top1;
                                        top1 = match;
                                    }
                                    else if (top2 < match)
                                    {
                                        top2 = match;
                                    }
                                }
                            }
                            for (int channel = 0; channel < channels; ++channel)
                            {
                                if (!Listener.Channels[channel].LFE &&
                                    angleMatches[channel] != top0 && angleMatches[channel] != top1 &&
                                    angleMatches[channel] != top2)
                                {
                                    angleMatches[channel] = 0;
                                }
                            }
                        }
                        float totalAngleMatch = 0;
                        for (int channel = 0; channel < channels; ++channel)
                        {
                            totalAngleMatch += angleMatches[channel] * angleMatches[channel];
                        }
                        totalAngleMatch = (float)Math.Sqrt(totalAngleMatch);

                        // Place in sphere, write data to output channels
                        float volume3D = Volume * rolloffDistance * SpatialBlend / totalAngleMatch;
                        for (int channel = 0; channel < channels; ++channel)
                        {
                            if (Listener.Channels[channel].LFE)
                            {
                                if (!listener.LFESeparation || LFE)
                                {
                                    WriteOutput(samples, rendered, volume3D * totalAngleMatch, channel, channels);
                                }
                            }
                            else if (!LFE && angleMatches[channel] != 0)
                            {
                                WriteOutput(samples, rendered, volume3D * angleMatches[channel], channel, channels);
                            }
                        }
                    }
                }
            }

            // Timing
            TimeSamples += PitchedUpdateRate;
            if (TimeSamples >= Clip.Samples)
            {
                if (Loop)
                {
                    TimeSamples %= Clip.Samples;
                }
                else
                {
                    TimeSamples = 0;
                    IsPlaying   = false;
                }
            }
            return(rendered);
        }
Beispiel #30
0
        /// <summary>
        /// Reads the next signed VINT from a stream, cuts the leading 1, reads the correct value.
        /// </summary>
        public static long ReadSignedValue(Stream reader)
        {
            long value = ReadTag(reader);

            return(value - (3L << (QMath.BitsAfterMSB(value) - 1)) + 1);
        }