Beispiel #1
0
 public static Int32 RoundUp(double x)
 {
     if (GlobalMembersUtil.FMod(x, 1.0) == 0)
     {
         return((Int32)x);
     }
     else
     {
         return((Int32)x + 1);
     }
 }
Beispiel #2
0
    /// <summary>
    /// Get Settings from User Input or Config file
    /// </summary>
    /// <param name="bands">Specifies the desired height of the spectrogram (bands)</param>
    /// <param name="samplecount">Number of samples</param>
    /// <param name="samplerate">Sample rate</param>
    /// <param name="basefreq">Base frequency in Hertz</param>
    /// <param name="maxfreq">Maximum frequency in Hertz</param>
    /// <param name="pixpersec">Time resolution in Pixels Per Second</param>
    /// <param name="bandsperoctave">Frequency resolution in Bands Per Octave</param>
    /// <param name="Xsize">Specifies the desired width of the spectrogram</param>
    /// <param name="mode">0 = Analysis mode, 1 = Synthesis mode</param>
    public static void SettingsInput(ref int bands, ref int samplecount, ref int samplerate, ref double basefreq, ref double maxfreq, ref double pixpersec, ref double bandsperoctave, ref int Xsize, int mode)
    {
        /* mode :
         * 0 = Analysis mode
         * 1 = Synthesis mode
         */
        int    i = 0;
        double gf;
        double f;
        double trash;
        double ma;          // maximum allowed frequency
        int    unset   = 0; // count of unset interdependant settings
        int    set_min = 0;
        int    set_max = 0;
        int    set_bpo = 0;
        int    set_y   = 0;
        int    set_pps = 0;
        int    set_x   = 0;

                #if DEBUG
        Console.Write("settingsinput...\n");
                #endif

        // Path to the configuration file
        string     configFileName = "arss.conf";
        TextReader freqcfg        = null;
        if (File.Exists(configFileName))
        {
            freqcfg = new StreamReader(configFileName);
        }

        if (samplerate == 0)         // if we're in synthesis mode and that no samplerate has been defined yet
        {
            if (GlobalMembersUtil.quiet)
            {
                Console.Error.WriteLine("Please provide a sample rate for your output sound.\nUse --sample-rate (-r).\nExiting with error.\n");
                Environment.Exit(1);
            }
            //********Output settings querying********

            Console.Write("Sample rate [44100] : ");         // Query for a samplerate
            samplerate = (int)GlobalMembersUtil.GetFloat();
            if (samplerate == 0 || samplerate < -2147483647) // The -2147483647 check is used for the sake of compatibility with C90
            {
                samplerate = 44100;                          // Default value
            }
            //--------Output settings querying--------
        }

        if (basefreq != 0)         // count unset interdependant frequency-domain settings
        {
            set_min = 1;
        }

        if (maxfreq != 0)
        {
            set_max = 1;
        }

        if (bandsperoctave != 0)
        {
            set_bpo = 1;
        }

        if (bands != 0)
        {
            set_y = 1;
        }

        unset = set_min + set_max + set_bpo + set_y;

        if (unset == 4)         // if too many settings are set
        {
            if (mode == 0)
            {
                Console.Error.WriteLine("You have set one parameter too many.\nUnset either --min-freq (-min), --max-freq (-max), --bpo (-b)\nExiting with error.\n");
            }
            if (mode == 1)
            {
                Console.Error.WriteLine("You have set one parameter too many.\nUnset either --min-freq (-min), --max-freq (-max), --bpo (-b) or --height (-y)\nExiting with error.\n");
            }
            Environment.Exit(1);
        }

        if (pixpersec != 0)
        {
            set_pps = 1;
        }

        if (Xsize != 0)
        {
            set_x = 1;
        }

        if (set_x + set_pps == 2 && mode == 0)
        {
            Console.Error.WriteLine("You cannot define both the image width and the horizontal resolution.\nUnset either --pps (-p) or --width (-x)\nExiting with error.\n");
            Environment.Exit(1);
        }

        if (freqcfg != null)         // load settings from file if it exists
        {
            if (basefreq == 0)       // load values from it if they haven't been set yet
            {
                basefreq = double.Parse(freqcfg.ReadLine());
            }
            else
            {
                trash = double.Parse(freqcfg.ReadLine());
            }

            if (maxfreq == 0)
            {
                maxfreq = double.Parse(freqcfg.ReadLine());
            }
            else
            {
                trash = double.Parse(freqcfg.ReadLine());
            }

            if (bandsperoctave == 0)
            {
                bandsperoctave = double.Parse(freqcfg.ReadLine());
            }
            else
            {
                trash = double.Parse(freqcfg.ReadLine());
            }

            if (pixpersec == 0)
            {
                pixpersec = double.Parse(freqcfg.ReadLine());
            }
            else
            {
                trash = double.Parse(freqcfg.ReadLine());
            }
        }
        else
        {
            if (basefreq == 0)             // otherwise load default values
            {
                basefreq = 27.5;
            }
            if (maxfreq == 0)
            {
                maxfreq = 20000;
            }
            if (bandsperoctave == 0)
            {
                bandsperoctave = 12;
            }
            if (pixpersec == 0)
            {
                pixpersec = 150;
            }
        }
        if (freqcfg != null)
        {
            freqcfg.Close();
        }

        if (unset < 3 && set_min == 0)
        {
            if (GlobalMembersUtil.quiet)
            {
                Console.Error.WriteLine("Please define a minimum frequency.\nUse --min-freq (-min).\nExiting with error.\n");
                Environment.Exit(1);
            }
            Console.Write("Min. frequency (Hz) [{0:f3}]: ", basefreq);
            gf = GlobalMembersUtil.GetFloat();
            if (gf != 0)
            {
                basefreq = gf;
            }
            unset++;
            set_min = 1;
        }
        basefreq /= samplerate;         // turn basefreq from Hz to fractions of samplerate

        if (unset < 3 && set_bpo == 0)
        {
            if (GlobalMembersUtil.quiet)
            {
                Console.Error.WriteLine("Please define a bands per octave setting.\nUse --bpo (-b).\nExiting with error.\n");
                Environment.Exit(1);
            }
            Console.Write("Bands per octave [{0:f3}]: ", bandsperoctave);
            gf = GlobalMembersUtil.GetFloat();
            if (gf != 0)
            {
                bandsperoctave = gf;
            }
            unset++;
            set_bpo = 1;
        }

        if (unset < 3 && set_max == 0)
        {
            i = 0;
            do
            {
                i++;
                f = basefreq * Math.Pow(GlobalMembersDsp.LOGBASE, (i / bandsperoctave));
            }while (f < 0.5);

            ma = basefreq * Math.Pow(GlobalMembersDsp.LOGBASE, ((i - 2) / bandsperoctave)) * samplerate;           // max allowed frequency

            if (maxfreq > ma)
            {
                if (GlobalMembersUtil.FMod(ma, 1.0) == 0.0)
                {
                    maxfreq = ma;                     // replaces the "Upper frequency limit above Nyquist frequency" warning
                }
                else
                {
                    maxfreq = ma - GlobalMembersUtil.FMod(ma, 1.0);
                }
            }

            if (mode == 0)             // if we're in Analysis mode
            {
                if (GlobalMembersUtil.quiet)
                {
                    Console.Error.WriteLine("Please define a maximum frequency.\nUse --max-freq (-max).\nExiting with error.\n");
                    Environment.Exit(1);
                }
                Console.Write("Max. frequency (Hz) (up to {0:f3}) [{1:f3}]: ", ma, maxfreq);
                gf = GlobalMembersUtil.GetFloat();
                if (gf != 0)
                {
                    maxfreq = gf;
                }

                if (maxfreq > ma)
                {
                    if (GlobalMembersUtil.FMod(ma, 1.0) == 0.0)
                    {
                        maxfreq = ma;                         // replaces the "Upper frequency limit above Nyquist frequency" warning
                    }
                    else
                    {
                        maxfreq = ma - GlobalMembersUtil.FMod(ma, 1.0);
                    }
                }
            }

            unset++;
            set_max = 1;
        }

        if (set_min == 0)
        {
            basefreq = Math.Pow(GlobalMembersDsp.LOGBASE, (bands - 1) / bandsperoctave) * maxfreq;           // calculate the lower frequency in Hz
            Console.Write("Min. frequency : {0:f3} Hz\n", basefreq);
            basefreq /= samplerate;
        }

        if (set_max == 0)
        {
            maxfreq = Math.Pow(GlobalMembersDsp.LOGBASE, (bands - 1) / bandsperoctave) * (basefreq * samplerate);           // calculate the upper frequency in Hz
            Console.Write("Max. frequency : {0:f3} Hz\n", maxfreq);
        }

        if (set_y == 0)
        {
            bands = 1 + (int)GlobalMembersUtil.RoundOff(bandsperoctave * (GlobalMembersUtil.Log(maxfreq) - GlobalMembersUtil.Log(basefreq * samplerate)));
            Console.Write("Bands : {0:D}\n", bands);
        }

        if (set_bpo == 0)
        {
            if (GlobalMembersDsp.LOGBASE == 1.0)
            {
                bandsperoctave = maxfreq / samplerate;
            }
            else
            {
                bandsperoctave = (bands - 1) / (GlobalMembersUtil.Log(maxfreq) - GlobalMembersUtil.Log(basefreq * samplerate));
            }
            Console.Write("Bands per octave : {0:f3}\n", bandsperoctave);
        }

        if (set_x == 1 && mode == 0)                                              // If we're in Analysis mode and that X is set (by the user)
        {
            pixpersec = (double)Xsize * (double)samplerate / (double)samplecount; // calculate pixpersec
            Console.Write("Pixels per second : {0:f3}\n", pixpersec);
        }

        if ((mode == 0 && set_x == 0 && set_pps == 0) || (mode == 1 && set_pps == 0))         // If in Analysis mode none are set or pixpersec isn't set in Synthesis mode
        {
            if (GlobalMembersUtil.quiet)
            {
                Console.Error.WriteLine("Please define a pixels per second setting.\nUse --pps (-p).\nExiting with error.\n");
                Environment.Exit(1);
            }
            Console.Write("Pixels per second [{0:f3}]: ", pixpersec);
            gf = GlobalMembersUtil.GetFloat();
            if (gf != 0)
            {
                pixpersec = gf;
            }
        }

        basefreq *= samplerate;                                   // turn back to Hz just for the sake of writing to the file

        TextWriter freqcfgOut = new StreamWriter(configFileName); // saving settings to a file

        if (freqcfgOut == null)
        {
            Console.Error.WriteLine("Cannot write to configuration file");
            Environment.Exit(1);
        }
        freqcfgOut.WriteLine(basefreq);
        freqcfgOut.WriteLine(maxfreq);
        freqcfgOut.WriteLine(bandsperoctave);
        freqcfgOut.WriteLine(pixpersec);
        freqcfgOut.Close();

        basefreq  /= samplerate;        // basefreq is now in fraction of the sampling rate instead of Hz
        pixpersec /= samplerate;        // pixpersec is now in fraction of the sampling rate instead of Hz
    }
Beispiel #3
0
    /// <summary>
    /// Analyze the input
    /// </summary>
    /// <param name="s">Sound (original signal)</param>
    /// <param name="samplecount">Sample count (samplecount is the original signal's orginal length)</param>
    /// <param name="samplerate">Sample rate</param>
    /// <param name="Xsize">Specifies the desired width of the spectrogram</param>
    /// <param name="bands">Specifies the desired height of the spectrogram (bands is the total count of bands)</param>
    /// <param name="bpo">Frequency resolution in Bands Per Octave</param>
    /// <param name="pixpersec">Time resolution in Pixels Per Second</param>
    /// <param name="basefreq">Minimum frequency in Hertz</param>
    /// <returns>Image</returns>
    public static double[][] Analyze(ref double[] s, ref int samplecount, ref int samplerate, ref int Xsize, ref int bands, ref double bpo, ref double pixpersec, ref double basefreq)
    {
        int i  = 0;                     // i is a general purpose iterator
        int ib = 0;                     // ib is the band iterator
        int Mb = 0;                     // Mb is the length of the original signal once zero-padded (always even)
        int Mc = 0;                     // Mc is the length of the filtered signal
        int Md = 0;                     // Md is the length of the envelopes once downsampled (constant)
        int Fa = 0;                     // Fa is the index of the band's start in the frequency domain
        int Fd = 0;                     // Fd is the index of the band's end in the frequency domain

        double[][] @out;                // @out is the output image
        double[]   h;
        double[]   freq;                // freq is the band's central frequency
        double[]   t;                   // t is temporary pointer to a new version of the signal being worked on
        double     coef;                // coef is a temporary modulation coefficient
        double     La;                  // La is the log2 of the frequency of Fa
        double     Ld;                  // Ld is the log2 of the frequency of Fd
        double     Li;                  // Li is the iterative frequency between La and Ld defined logarithmically
        double     maxfreq;             // maxfreq is the central frequency of the last band

        freq = GlobalMembersDsp.FrequencyArray(basefreq, bands, bpo);

        if (LOGBASE == 1.0)
        {
            maxfreq = bpo;             // in linear mode we use bpo to store the maxfreq since we couldn't deduce maxfreq otherwise
        }
        else
        {
            maxfreq = basefreq * Math.Pow(LOGBASE, ((double)(bands - 1) / bpo));
        }

        Xsize = (int)(samplecount * pixpersec);

        if (GlobalMembersUtil.FMod((double)samplecount * pixpersec, 1.0) != 0.0)
        {
            // round-up
            Xsize++;
        }
        Console.Write("Image size : {0:D}x{1:D}\n", Xsize, bands);

        @out = new double[bands][];

        clockA = GlobalMembersUtil.GetTime();

        //********ZEROPADDING********	Note : Don't do it in Circular mode
        // Mb is the length of the original signal once zero-padded (always even)
        if (LOGBASE == 1.0)
        {
            Mb = samplecount - 1 + (int)GlobalMembersUtil.RoundOff(5.0 / freq[1] - freq[0]);           // linear mode
        }
        else
        {
            Mb = samplecount - 1 + (int)GlobalMembersUtil.RoundOff(2.0 * 5.0 / ((freq[0] * Math.Pow(LOGBASE, -1.0 / (bpo))) * (1.0 - Math.Pow(LOGBASE, -1.0 / bpo))));
        }

        if (Mb % 2 == 1)      // if Mb is odd
        {
            Mb++;             // make it even (for the sake of simplicity)
        }

        Mc = 0;
        Md = 0;

        Mb = (int)GlobalMembersUtil.RoundOff((double)GlobalMembersUtil.NextPrime((int)GlobalMembersUtil.RoundOff(Mb * pixpersec)) / pixpersec);

        // Md is the length of the envelopes once downsampled (constant)
        Md = (int)GlobalMembersUtil.RoundOff(Mb * pixpersec);

        // realloc to the zeropadded size
        Array.Resize <double>(ref s, Mb);

        // zero-out the padded area. Equivalent of : for (i=samplecount; i<Mb; i++) s[i] = 0;
        for (i = samplecount; i < Mb; i++)
        {
            s[i] = 0;
        }
        //--------ZEROPADDING--------

        //Export.exportCSV(String.Format("samples_before_fft.csv"), s, 256);
        GlobalMembersDsp.FFT(ref s, ref s, Mb, FFTMethod.DFT);         // In-place FFT of the original zero-padded signal
        //Export.exportCSV(String.Format("samples_after_fft.csv"), s, 256);

        for (ib = 0; ib < bands; ib++)
        {
            //********Filtering********
            Fa = (int)GlobalMembersUtil.RoundOff(GlobalMembersDsp.LogPositionToFrequency((double)(ib - 1) / (double)(bands - 1), basefreq, maxfreq) * Mb);
            Fd = (int)GlobalMembersUtil.RoundOff(GlobalMembersDsp.LogPositionToFrequency((double)(ib + 1) / (double)(bands - 1), basefreq, maxfreq) * Mb);
            La = GlobalMembersDsp.FrequencyToLogPosition((double)Fa / (double)Mb, basefreq, maxfreq);
            Ld = GlobalMembersDsp.FrequencyToLogPosition((double)Fd / (double)Mb, basefreq, maxfreq);

            if (Fd > Mb / 2)
            {
                Fd = Mb / 2;               // stop reading if reaching the Nyquist frequency
            }
            if (Fa < 1)
            {
                Fa = 1;
            }

            // Mc is the length of the filtered signal
            Mc = (Fd - Fa) * 2 + 1;
            // '*2' because the filtering is on both real and imaginary parts,
            // '+1' for the DC.
            // No Nyquist component since the signal length is necessarily odd

            if (Md > Mc)               // if the band is going to be too narrow
            {
                Mc = Md;
            }

            if (Md < Mc)               // round the larger bands up to the next integer made of 2^n * 3^m
            {
                Mc = GlobalMembersUtil.NextPrime(Mc);
            }

            Console.Write("{0,4:D}/{1:D} (FFT size: {2,6:D})   {3:f2} Hz - {4:f2} Hz\r", ib + 1, bands, Mc, (double)Fa * samplerate / Mb, (double)Fd * samplerate / Mb);

            @out[bands - ib - 1] = new double[Mc + 1];

            for (i = 0; i < Fd - Fa; i++)
            {
                Li   = GlobalMembersDsp.FrequencyToLogPosition((double)(i + Fa) / (double)Mb, basefreq, maxfreq); // calculation of the logarithmic position
                Li   = (Li - La) / (Ld - La);
                coef = 0.5 - 0.5 * Math.Cos(2.0 * PI * Li);                                                       // Hann function

                @out[bands - ib - 1][i + 1]      = s[i + 1 + Fa] * coef;
                @out[bands - ib - 1][Mc - 1 - i] = s[Mb - Fa - 1 - i] * coef;
            }
            //--------Filtering--------
            //Export.exportCSV(String.Format("test/band_{0}_filtered.csv", bands-ib-1), @out[bands-ib-1]);

            //********90° rotation********
            h = new double[Mc + 1];

            // Rotation : Re' = Im; Im' = -Re
            for (i = 0; i < Fd - Fa; i++)
            {
                h[i + 1]      = @out[bands - ib - 1][Mc - 1 - i];           // Re' = Im
                h[Mc - 1 - i] = -@out[bands - ib - 1][i + 1];               // Im' = -Re
            }
            //--------90° rotation--------

            //********Envelope detection********
            //Export.exportCSV(String.Format("test/band_{0}_rotated.csv", bands-ib-1), @out[bands-ib-1]);

            GlobalMembersDsp.FFT(ref @out[bands - ib - 1], ref @out[bands - ib - 1], Mc, FFTMethod.IDFT);       // In-place IFFT of the filtered band signal
            GlobalMembersDsp.FFT(ref h, ref h, Mc, FFTMethod.IDFT);                                             // In-place IFFT of the filtered band signal rotated by 90°

            //Export.exportCSV(String.Format("test/band_{0}_before.csv", bands-ib-1), @out[bands-ib-1]);

            for (i = 0; i < Mc; i++)
            {
                // TODO: why does the above crash?!
                //for (i = 0; i < @out[bands-ib-1].Length; i++) {
                // Magnitude of the analytic signal
                double x        = @out[bands - ib - 1][i];
                double y        = h[i];
                double xSquared = x * x;
                double ySquared = y * y;
                double mag      = Math.Sqrt(xSquared + ySquared);
                @out[bands - ib - 1][i] = mag;
            }

            Array.Clear(h, 0, h.Length);
            //--------Envelope detection--------

            //********Downsampling********
            if (Mc < Md)                                             // if the band doesn't have to be resampled
            {
                Array.Resize <double>(ref @out[bands - ib - 1], Md); // simply ignore the end of it
            }

            if (Mc > Md)               // If the band *has* to be downsampled
            {
                t = @out[bands - ib - 1];
                @out[bands - ib - 1] = GlobalMembersDsp.BlackmanDownsampling(@out[bands - ib - 1], Mc, Md);         // Blackman downsampling

                Array.Clear(t, 0, t.Length);
            }
            //--------Downsampling--------

            Array.Resize <double>(ref @out[bands - ib - 1], Xsize);        // Tail chopping

            //Export.exportCSV(String.Format("test/band_{0}_after.csv", bands-ib-1), @out[bands-ib-1]);
        }

        Console.Write("\n");

        GlobalMembersDsp.Normalize(ref @out, ref Xsize, ref bands, 1.0);

        //Export.exportCSV(String.Format("out.csv"), @out);
        return(@out);
    }
Beispiel #4
0
    /// <summary>
    /// Interpolation based on an estimate of the Blackman Square function,
    /// which is a Blackman function convolved with a square.
    /// It's like smoothing the result of a nearest neighbour interpolation
    /// with a Blackman FIR
    /// </summary>
    /// <param name="in">Signal in</param>
    /// <param name="out">Signal out</param>
    /// <param name="Mi">Mi is the original signal's length</param>
    /// <param name="Mo">Mo is the output signal's length</param>
    /// <param name="lut">Blackman Square Lookup Table</param>
    /// <param name="lut_size">Defines the number of elements in the Blackman Square look-up table. It's best to make it small enough to be entirely cached</param>
    public static void BlackmanSquareInterpolation(ref double[] @in, ref double[] @out, ref int Mi, ref int Mo, ref double[] lut, int lut_size)
    {
        int    i = 0;        // general purpose iterators
        int    j = 0;
        double pos_in;       // position in the original signal
        double x;            // position of the iterator in the blackman_square(x) formula
        double ratio;        // scaling ratio (> 1.0)
        double ratio_i;      // ratio^-1
        double coef;         // Blackman square final coefficient
        double pos_lut;      // Index on the look-up table
        int    pos_luti = 0; // Integer index on the look-up table
        double mod_pos;      // modulo of the position on the look-up table
        double y0;           // values of the two closest values on the LUT
        double y1;
        double foo     = (double)lut_size / 3.0;
        int    j_start = 0;      // boundary values for the j loop
        int    j_stop  = 0;

        /*
         * Mi is the original signal's length
         * Mo is the output signal's length
         */
        ratio   = (double)Mi / Mo;
        ratio_i = 1.0 / ratio;

        for (i = 0; i < Mo; i++)
        {
            pos_in = (double)i * ratio;

            j_stop = (int)(pos_in + 1.5);

            j_start = j_stop - 2;
            if (j_start < 0)
            {
                j_start = 0;
            }

            // The boundary check is done after j_start is calculated to avoid miscalculating it
            if (j_stop >= Mi)
            {
                j_stop = Mi - 1;
            }

            for (j = j_start; j <= j_stop; j++)
            {
                x        = j - pos_in + 1.5;          // calculate position within the Blackman square function in the [0.0 ; 3.0] range
                pos_lut  = x * foo;
                pos_luti = (int)pos_lut;

                mod_pos = GlobalMembersUtil.FMod(pos_lut, 1.0);                 // modulo of the index

                if (pos_luti + 1 < lut.Length)
                {
                    y0   = lut[pos_luti];                   // interpolate linearly between the two closest values
                    y1   = lut[pos_luti + 1];
                    coef = y0 + mod_pos * (y1 - y0);        // linear interpolation

                    @out[i] += @in[j] * coef;               // convolve
                }
            }
        }
    }