public static void Write8Bit(BinaryFile wavfile, double[][] sound, int samplecount, int channels) { int i = 0; int ic = 0; double val; byte @byte = new byte(); #if DEBUG Console.Write("Write8Bit...\n"); #endif for (i = 0; i < samplecount; i++) { for (ic = 0; ic < channels; ic++) { val = GlobalMembersUtil.RoundOff((sound[ic][i] + 1.0) * 128.0); if (val > 255) { val = 255; } if (val < 0) { val = 0; } @byte = (byte)val; wavfile.Write(@byte); } } }
/// <summary> /// Windowd Sinc method /// </summary> /// <param name="length">Length</param> /// <param name="bw">Bandwidth</param> /// <returns></returns> public static double[] WindowedSinc_max(int length, double bw) { // http://www.dspguide.com/ch16/1.htm int i = 0; int bwl = 0; // integer transition bandwidth double tbw; // double transition bandwidth double[] h; // kernel double x; // position in the antiderivate of the Blackman function of the sample we're at double coef; // coefficient obtained from the function tbw = bw * (double)(length - 1); bwl = GlobalMembersUtil.RoundUp(tbw); h = new double[length]; for (i = 1; i < length; i++) { h[i] = 1.0; } for (i = 0; i < bwl; i++) { x = (double)i / tbw; // position calculation between 0.0 and 1.0 // antiderivative of the Blackman window function coef = 0.42 * x - (0.5 / (2.0 * PI)) * Math.Sin(2.0 * PI * x) + (0.08 / (4.0 * PI)) * Math.Sin(4.0 * PI * x); coef *= 1.0 / 0.42; h[i + 1] = coef; h[length - 1 - i] = coef; } return(h); }
public static void Write32Bit(BinaryFile wavfile, double[][] sound, int samplecount, int channels) { int i = 0; int ic = 0; double val; #if DEBUG Console.Write("Write32Bit...\n"); #endif for (i = 0; i < samplecount; i++) { for (ic = 0; ic < channels; ic++) { val = GlobalMembersUtil.RoundOff(sound[ic][i] * 2147483648.0); if (val > 2147483647.0) { val = 2147483647.0; } if (val < -2147483648.0) { val = -2147483648.0; } wavfile.Write((int)val); } } }
public static void WriteWaveFile(BinaryFile wavfile, double[][] sound, int channels, int samplecount, int samplerate, int format_param) { int i = 0; int[] tag = { 1179011410, 0, 1163280727, 544501094, 16, 1, 1, 0, 0, 0, 0, 1635017060, 0, 0 }; #if DEBUG Console.Write("WriteWaveFile...\n"); #endif //********WAV tags generation******** tag[12] = samplecount * (format_param / 8) * channels; tag[1] = tag[12] + 36; tag[7] = samplerate; tag[8] = samplerate * format_param / 8; tag[9] = format_param / 8; tag[6] = channels; tag[10] = format_param; if ((format_param == 8) || (format_param == 16)) { tag[5] = 1; } if (format_param == 32) { tag[5] = 3; } //--------WAV tags generation-------- // tag writing for (i = 0; i < 13; i++) { if ((i == 5) || (i == 6) || (i == 9) || (i == 10)) { GlobalMembersUtil.WriteUInt16((ushort)tag[i], wavfile); } else { GlobalMembersUtil.WriteUInt32((uint)tag[i], wavfile); } } if (format_param == 8) { Write8Bit(wavfile, sound, samplecount, channels); } if (format_param == 16) { Write16Bit(wavfile, sound, samplecount, channels); } if (format_param == 32) { Write32BitFloat(wavfile, sound, samplecount, channels); } wavfile.Close(); }
public static Int32 NextPrime(Int32 x) { while (GlobalMembersUtil.SmallPrimes(x) != 1) { x++; } return(x); }
public static Int32 RoundUp(double x) { if (GlobalMembersUtil.FMod(x, 1.0) == 0) { return((Int32)x); } else { return((Int32)x + 1); } }
public static int GetWaveOutParameters() { int bps = 0; do { Console.Write("Bits per sample (8/16/32) [16] : "); bps = (int)GlobalMembersUtil.GetFloat(); if (bps == 0) { bps = 16; } }while (bps != 8 && bps != 16 && bps != 32); return(bps); }
/// <summary> /// Downsampling of the signal by a Blackman function /// </summary> /// <param name="in">Signal</param> /// <param name="Mi">Mi is the original signal's length</param> /// <param name="Mo">Mo is the output signal's length</param> /// <returns>Downsampled Signal</returns> public static double[] BlackmanDownsampling(double[] @in, int Mi, int Mo) { int i = 0; // general purpose iterators int j = 0; double[] @out = new double[Mo]; double pos_in; // position in the original signal double x; // position of the iterator in the blackman(x) formula double ratio; // scaling ratio (> 1.0) double ratio_i; // ratio^-1 double coef; // Blackman coefficient double coef_sum; // sum of coefficients, used for weighting /* * 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; coef_sum = 0; for (j = GlobalMembersUtil.RoundUp(pos_in - ratio); j <= pos_in + ratio; j++) { if (j >= 0 && j < Mi) // if the read sample is within bounds { x = j - pos_in + ratio; // calculate position within the Blackman function coef = 0.42 - 0.5 * Math.Cos(PI * x * ratio_i) + 0.08 * Math.Cos(2 * PI * x * ratio_i); coef_sum += coef; @out[i] += @in[j] * coef; // convolve } } @out[i] /= coef_sum; } return(@out); }
public static double DoubleRandom() { return(((double)GlobalMembersUtil.Random() * (1.0 / 2147483648.0)) - 1.0); }
/// <summary> /// Noise synthesis mode /// </summary> /// <param name="d">Image</param> /// <param name="Xsize">Specifies the desired width of the spectrogram</param> /// <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="pixpersec">Time resolution in Pixels Per Second</param> /// <param name="bpo">Frequency resolution in Bands Per Octave</param> /// <returns></returns> public static double[] SynthesizeNoise(ref double[][] d, ref int Xsize, ref int bands, ref int samplecount, ref int samplerate, ref double basefreq, ref double pixpersec, ref double bpo) { double[] s; // final signal double coef; double[] noise; // filtered looped noise double loop_size_sec = LOOP_SIZE_SEC; // size of the filter bank loop, in seconds. Later to be taken from user input int loop_size = 0; // size of the filter bank loop, in samples. Deduced from loop_size_sec int loop_size_min = 0; // minimum required size for the filter bank loop, in samples. Calculated from the longest windowed sinc's length double[] pink_noise; // original pink noise (in the frequency domain) double mag; // parameters for the creation of pink_noise's samples double phase; double[] envelope; // interpolated envelope double[] lut; // Blackman Sqaure look-up table double[] freq; // frequency look-up table double maxfreq; // central frequency of the last band int i = 0; // general purpose iterator int ib = 0; // bands iterator int il = 0; // loop iterator 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 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 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)); } clockA = GlobalMembersUtil.GetTime(); samplecount = (int)GlobalMembersUtil.RoundOff(Xsize / pixpersec); // calculation of the length of the final signal Console.Write("Sound duration : {0:f3} s\n", (double)samplecount / samplerate); // allocation of the final signal s = new double[samplecount]; // allocation of the interpolated envelope envelope = new double[samplecount]; //********Loop size calculation******** loop_size = (int)loop_size_sec * samplerate; if (LOGBASE == 1.0) { loop_size_min = (int)GlobalMembersUtil.RoundOff(4.0 * 5.0 / freq[1] - freq[0]); // linear mode } else { loop_size_min = (int)GlobalMembersUtil.RoundOff(2.0 * 5.0 / ((freq[0] * Math.Pow(2.0, -1.0 / (bpo))) * (1.0 - Math.Pow(2.0, -1.0 / bpo)))); // this is the estimate of how many samples the longest FIR will take up in the time domain } if (loop_size_min > loop_size) { loop_size = loop_size_min; } loop_size = GlobalMembersUtil.NextPrime(loop_size); // enlarge the loop_size to the next multiple of short primes in order to make IFFTs faster //--------Loop size calculation-------- //********Pink noise generation******** pink_noise = new double[loop_size]; for (i = 1; i < (loop_size + 1) >> 1; i++) { mag = Math.Pow((double)i, 0.5 - 0.5 * LOGBASE); // FIXME something's not necessarily right with that formula phase = GlobalMembersUtil.DoubleRandom() * PI; // random phase between -pi and +pi pink_noise[i] = mag * Math.Cos(phase); // real part pink_noise[loop_size - i] = mag * Math.Sin(phase); // imaginary part } //--------Pink noise generation-------- // allocate noise noise = new double[loop_size]; lut = GlobalMembersDsp.BlackmanSquareLookupTable(ref BMSQ_LUT_SIZE); // Blackman Square look-up table initalisation for (ib = 0; ib < bands; ib++) { Console.Write("{0,4:D}/{1:D}\r", ib + 1, bands); // reset filtered noise for (i = 0; i < loop_size; i++) { noise[i] = 0; // reset sband } //********Filtering******** Fa = (int)GlobalMembersUtil.RoundOff(GlobalMembersDsp.LogPositionToFrequency((double)(ib - 1) / (double)(bands - 1), basefreq, maxfreq) * loop_size); Fd = (int)GlobalMembersUtil.RoundOff(GlobalMembersDsp.LogPositionToFrequency((double)(ib + 1) / (double)(bands - 1), basefreq, maxfreq) * loop_size); La = GlobalMembersDsp.FrequencyToLogPosition((double)Fa / (double)loop_size, basefreq, maxfreq); Ld = GlobalMembersDsp.FrequencyToLogPosition((double)Fd / (double)loop_size, basefreq, maxfreq); if (Fd > loop_size / 2) { Fd = loop_size / 2; // stop reading if reaching the Nyquist frequency } if (Fa < 1) { Fa = 1; } Console.Write("{0,4:D}/{1:D} {2:f2} Hz - {3:f2} Hz\r", ib + 1, bands, (double)Fa * samplerate / loop_size, (double)Fd * samplerate / loop_size); for (i = Fa; i < Fd; i++) { Li = GlobalMembersDsp.FrequencyToLogPosition((double)i / (double)loop_size, 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 noise[i + 1] = pink_noise[i + 1] * coef; noise[loop_size - 1 - i] = pink_noise[loop_size - 1 - i] * coef; } //--------Filtering-------- GlobalMembersDsp.FFT(ref noise, ref noise, loop_size, FFTMethod.IDFT); // IFFT of the filtered noise // blank the envelope for (i = 0; i < samplecount; i++) { envelope[i] = 0; // reset sband } GlobalMembersDsp.BlackmanSquareInterpolation(ref d[bands - ib - 1], ref envelope, ref Xsize, ref samplecount, ref lut, BMSQ_LUT_SIZE); // interpolation of the envelope il = 0; for (i = 0; i < samplecount; i++) { s[i] += envelope[i] * noise[il]; // modulation il++; // increment loop iterator if (il == loop_size) // if the array iterator has reached the end of the array, it's reset { il = 0; } } } Console.Write("\n"); GlobalMembersDsp.Normalize(ref s, ref samplecount, 1.0); return(s); }
/// <summary> /// Sine synthesis mode /// </summary> /// <param name="d">Image (d is the original image - spectrogram)</param> /// <param name="Xsize">Specifies the desired width of the spectrogram</param> /// <param name="bands">Specifies the desired height of the spectrogram (total count of bands)</param> /// <param name="samplecount">Number of samples (samplecount is the output sound's length)</param> /// <param name="samplerate">Sample rate</param> /// <param name="basefreq">Minimum frequency in Hertz</param> /// <param name="pixpersec">Time resolution in Pixels Per Second</param> /// <param name="bpo">Frequency resolution in Bands Per Octave</param> /// <returns></returns> public static double[] SynthesizeSine(ref double[][] d, ref int Xsize, ref int bands, ref int samplecount, ref int samplerate, ref double basefreq, ref double pixpersec, ref double bpo) { double[] s; // s is the output sound double[] freq; // freq is the band's central frequency double[] filter; double[] sband; // sband is the band's envelope upsampled and shifted up in frequency int sbsize = 0; // sbsize is the length of sband double[] sine = new double[4]; // sine is the random sine look-up table double rphase; // rphase is the band's sine's random phase int i = 0; // i is a general purpose iterator int ib = 0; // ib is the band iterator int Fc = 0; // Fc is the index of the band's centre in the frequency domain on the new signal int Bc = 0; // Bc is the index of the band's centre in the frequency domain on sband (its imaginary match being sbsize-Bc) int Mh = 0; // Mh is the length of the real or imaginary part of the envelope's FFT, DC element included and Nyquist element excluded int Mn = 0; // Mn is the length of the real or imaginary part of the sound's FFT, DC element included and Nyquist element excluded freq = GlobalMembersDsp.FrequencyArray(basefreq, bands, bpo); clockA = GlobalMembersUtil.GetTime(); sbsize = GlobalMembersUtil.NextPrime(Xsize * 2); // In Circular mode keep it to sbsize = Xsize * 2; samplecount = (int)GlobalMembersUtil.RoundOff(Xsize / pixpersec); Console.Write("Sound duration : {0:f3} s\n", (double)samplecount / samplerate); samplecount = (int)GlobalMembersUtil.RoundOff(0.5 * sbsize / pixpersec); // Do not change this value as it would stretch envelopes s = new double[samplecount]; // allocation of the shifted band sband = new double[sbsize]; // Bc is the index of the band's centre in the frequency domain on sband (its imaginary match being sbsize-Bc) Bc = (int)GlobalMembersUtil.RoundOff(0.25 * (double)sbsize); // Mh is the length of the real or imaginary part of the envelope's FFT, DC element included and Nyquist element excluded Mh = (sbsize + 1) >> 1; // Mn is the length of the real or imaginary part of the sound's FFT, DC element included and Nyquist element excluded Mn = (samplecount + 1) >> 1; filter = GlobalMembersDsp.WindowedSinc_max(Mh, 1.0 / GlobalMembersArss.TRANSITION_BW_SYNT); // generation of the frequency-domain filter for (ib = 0; ib < bands; ib++) { // reset sband for (i = 0; i < sbsize; i++) { sband[i] = 0; // reset sband } //********Frequency shifting******** rphase = GlobalMembersUtil.DoubleRandom() * PI; // random phase between -pi and +pi for (i = 0; i < 4; i++) { // generating the random sine LUT sine[i] = Math.Cos(i * 2.0 * PI * 0.25 + rphase); } for (i = 0; i < Xsize; i++) // envelope sampling rate * 2 and frequency shifting by 0.25 { if ((i & 1) == 0) { sband[i << 1] = d[bands - ib - 1][i] * sine[0]; sband[(i << 1) + 1] = d[bands - ib - 1][i] * sine[1]; } else { sband[i << 1] = d[bands - ib - 1][i] * sine[2]; sband[(i << 1) + 1] = d[bands - ib - 1][i] * sine[3]; } } //--------Frequency shifting-------- GlobalMembersDsp.FFT(ref sband, ref sband, sbsize, FFTMethod.DFT); // FFT of the envelope // Fc is the index of the band's centre in the frequency domain on the new signal Fc = (int)GlobalMembersUtil.RoundOff(freq[ib] * samplecount); // band's centre index (envelope's DC element) Console.Write("{0,4:D}/{1:D} {2:f2} Hz\r", ib + 1, bands, (double)Fc * samplerate / samplecount); //********Write FFT******** for (i = 1; i < Mh; i++) { if (Fc - Bc + i > 0 && Fc - Bc + i < Mn) // if we're between frequencies 0 and 0.5 of the new signal and that we're not at Fc { s[i + Fc - Bc] += sband[i] * filter[i]; // Real part s[samplecount - (i + Fc - Bc)] += sband[sbsize - i] * filter[i]; // Imaginary part } } //--------Write FFT-------- } Console.Write("\n"); GlobalMembersDsp.FFT(ref s, ref s, samplecount, FFTMethod.IDFT); // IFFT of the final sound samplecount = (int)GlobalMembersUtil.RoundOff(Xsize / pixpersec); // chopping tails by ignoring them GlobalMembersDsp.Normalize(ref s, ref samplecount, 1.0); return(s); }
public static double[][] ReadWaveFile(BinaryFile wavfile, ref int channels, ref int samplecount, ref int samplerate) { double[][] sound; int i = 0; int ic = 0; int[] tag = new int[13]; // Size Description Value // tag[0] 4 RIFF Header RIFF (1179011410) // tag[1] 4 RIFF data size // tag[2] 4 form-type (WAVE etc) (1163280727) // tag[3] 4 Chunk ID "fmt " (0x666D7420) = 544501094 // tag[4] 4 Chunk Data Size 16 + extra format bytes // tag[5] 2 Compression code 1 - 65,535 // tag[6] 2 Number of channels 1 - 65,535 // tag[7] 4 Sample rate 1 - 0xFFFFFFFF // tag[8] 4 Average bytes per second 1 - 0xFFFFFFFF // tag[9] 2 Block align 1 - 65,535 (4) // tag[10] 2 Significant bits per sample 2 - 65,535 (32) // tag[11] 4 IEEE = 1952670054 (0x74636166) = fact chunk // PCM = 1635017060 (0x61746164) (datachunk = 1635017060) // tag[12] 4 IEEE = 4 , PCM = 5292000 (0x0050BFE0) #if DEBUG Console.Write("ReadWaveFile...\n"); #endif for (i = 0; i < 13; i++) // tag reading { tag[i] = 0; if ((i == 5) || (i == 6) || (i == 9) || (i == 10)) { tag[i] = GlobalMembersUtil.ReadUInt16(wavfile); } else { tag[i] = (int)GlobalMembersUtil.ReadUInt32(wavfile); } } //********File format checking******** if (tag[0] != 1179011410 || tag[2] != 1163280727) { Console.Error.WriteLine("This file is not in WAVE format\n"); GlobalMembersUtil.WinReturn(); Environment.Exit(1); } if (tag[3] != 544501094 || tag[4] != 16 || tag[11] != 1635017060) // { Console.Error.WriteLine("This WAVE file format is not currently supported\n"); GlobalMembersUtil.WinReturn(); Environment.Exit(1); } if (tag[10] == 24) { Console.Error.WriteLine("24 bit PCM WAVE files is not currently supported\n"); GlobalMembersUtil.WinReturn(); Environment.Exit(1); } if (tag[5] != WAVE_FORMAT_PCM) { Console.Error.WriteLine("Non PCM WAVE files is not currently supported\n"); GlobalMembersUtil.WinReturn(); Environment.Exit(1); } //--------File format checking-------- channels = tag[6]; samplecount = tag[12] / (tag[10] / 8) / channels; samplerate = tag[7]; sound = new double[channels][]; for (ic = 0; ic < channels; ic++) { sound[ic] = new double[samplecount]; } //********Data loading******** if (tag[10] == 8) { Read8Bit(wavfile, sound, samplecount, channels); } if (tag[10] == 16) { Read16Bit(wavfile, sound, samplecount, channels); } if (tag[10] == 32) { if (tag[5] == WAVE_FORMAT_PCM) { Read32Bit(wavfile, sound, samplecount, channels); } else if (tag[5] == WAVE_FORMAT_IEEE_FLOAT) { Read32BitFloat(wavfile, sound, samplecount, channels); } } //--------Data loading-------- wavfile.Close(); return(sound); }
/// <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 } } } }
public static void BMPWrite(BinaryFile bmpfile, double[][] image, int y, int x) { int i = 0; // various iterators int iy = 0; int ix = 0; int ic = 0; int filesize = 0; int imagesize = 0; byte zerobytes = new byte(); byte val = new byte(); byte zero = 0; double vald; #if DEBUG Console.Write("BMPWrite...\n"); #endif zerobytes = (byte)(4 - ((x * 3) & 3)); // computation of zero bytes if (zerobytes == 4) { zerobytes = 0; } //********Tags******** filesize = 56 + ((x * 3) + zerobytes) * y; imagesize = 2 + ((x * 3) + zerobytes) * y; GlobalMembersUtil.WriteUInt16(19778, bmpfile); GlobalMembersUtil.WriteUInt32((UInt32)filesize, bmpfile); GlobalMembersUtil.WriteUInt32(0, bmpfile); GlobalMembersUtil.WriteUInt32(54, bmpfile); GlobalMembersUtil.WriteUInt32(40, bmpfile); GlobalMembersUtil.WriteUInt32((UInt32)x, bmpfile); GlobalMembersUtil.WriteUInt32((UInt32)y, bmpfile); GlobalMembersUtil.WriteUInt16(1, bmpfile); GlobalMembersUtil.WriteUInt32(24, bmpfile); GlobalMembersUtil.WriteUInt16(0, bmpfile); GlobalMembersUtil.WriteUInt32((UInt32)imagesize, bmpfile); GlobalMembersUtil.WriteUInt32(2834, bmpfile); GlobalMembersUtil.WriteUInt32(2834, bmpfile); GlobalMembersUtil.WriteUInt32(0, bmpfile); GlobalMembersUtil.WriteUInt32(0, bmpfile); //--------Tags-------- for (iy = y - 1; iy != -1; iy--) // backwards writing { for (ix = 0; ix < x; ix++) { // define color vald = image[iy][ix] * 255.0; if (vald > 255.0) { vald = 255.0; } if (vald < 0.0) { vald = 0.0; } val = (byte)vald; for (ic = 2; ic != -1; ic--) { bmpfile.Write(val); } } // write padding bytes for (i = 0; i < zerobytes; i++) { bmpfile.Write(zero); } } GlobalMembersUtil.WriteUInt16(0, bmpfile); bmpfile.Close(); #if DEBUG Console.Write("Image size : {0:D}x{1:D}\n", x, y); #endif }
public static double[][] BMPRead(BinaryFile bmpfile, ref int y, ref int x) { int iy = 0; // various iterators int ix = 0; int ic = 0; int offset = 0; double[][] image; byte zerobytes = new byte(); byte val = new byte(); #if DEBUG Console.Write("BMPRead...\n"); #endif if (GlobalMembersUtil.ReadUInt16(bmpfile) != 19778) // "BM" format tag check { Console.Error.WriteLine("This file is not in BMP format\n"); GlobalMembersUtil.WinReturn(); Environment.Exit(1); } bmpfile.Seek(8, SeekOrigin.Current); // skipping useless tags offset = (int)GlobalMembersUtil.ReadUInt32(bmpfile) - 54; // header offset bmpfile.Seek(4, SeekOrigin.Current); // skipping useless tags x = (int)GlobalMembersUtil.ReadUInt32(bmpfile); y = (int)GlobalMembersUtil.ReadUInt32(bmpfile); bmpfile.Seek(2, SeekOrigin.Current); // skipping useless tags if (GlobalMembersUtil.ReadUInt16(bmpfile) != 24) // Only format supported { Console.Error.WriteLine("Wrong BMP format, BMP images must be in 24-bit colour\n"); GlobalMembersUtil.WinReturn(); Environment.Exit(1); } bmpfile.Seek(24 + offset, SeekOrigin.Current); // skipping useless tags // image allocation image = new double[y][]; for (iy = 0; iy < y; iy++) { image[iy] = new double[x]; } zerobytes = (byte)(4 - ((x * 3) & 3)); if (zerobytes == 4) { zerobytes = 0; } for (iy = y - 1; iy != -1; iy--) // backwards reading { for (ix = 0; ix < x; ix++) { for (ic = 2; ic != -1; ic--) { val = bmpfile.ReadByte(); image[iy][ix] += (double)val * (1.0 / (255.0 * 3.0)); // Conversion to grey by averaging the three channels } } bmpfile.Seek(zerobytes, SeekOrigin.Current); // skipping padding bytes } bmpfile.Close(); return(image); }
public static void Main(string[] args) { /* * CommonUtils.FFT.FFTTesting.TimeSpectrograms(); * Console.ReadKey(); * return; * * int _channels = 0; * int _samplecount = 0; * int _samplerate = 0; * string _filenameIn = @"C:\Users\perivar.nerseth\Music\Sleep Away16.wav"; * string _filenameOut = @"C:\Users\perivar.nerseth\Music\Sleep Away16-test2.png"; * double[][] _sound = GlobalMembersSoundIO.ReadWaveFile(_filenameIn, ref _channels, ref _samplecount, ref _samplerate); // Sound input * double [] _s = _sound[0]; * * Spectrogram spectrogram = new Spectrogram(); * Bitmap bmp = spectrogram.to_image(ref _s, _samplerate); * bmp.Save(_filenameOut); * Console.ReadKey(); * return; * double[] out1 = new double[_samplecount]; * double[] out2 = new double[_samplecount]; * GlobalMembersDsp.FFT(ref _s, ref out1, _samplecount, GlobalMembersDsp.FFTMethod.DFT); * GlobalMembersDsp.FFT(ref out1, ref out2, _samplecount, GlobalMembersDsp.FFTMethod.IDFT); * * _sound[0] = out2; * * BinaryFile binOut = new BinaryFile(_filenameOut, BinaryFile.ByteOrder.LittleEndian, true); * GlobalMembersSoundIO.WriteWaveFile(binOut, _sound, 1, _samplecount, _samplerate, 16); * * Console.ReadKey(); * return; */ int argc = args.Length; BinaryFile fin; BinaryFile fout; int i = 0; double[][] sound; double[][] image; double basefreq = 0; double maxfreq = 0; double pixpersec = 0; double bpo = 0; double brightness = 1; int channels = 0; int samplecount = 0; int samplerate = 0; int Xsize = 0; int Ysize = 0; int format_param = 0; long clockb = 0; byte mode = 0; string in_name = null; string out_name = null; // initialisation of global using defaults defined in dsp.h GlobalMembersDsp.PI = PI_D; GlobalMembersDsp.LOGBASE = LOGBASE_D; GlobalMembersDsp.LOOP_SIZE_SEC = LOOP_SIZE_SEC_D; GlobalMembersDsp.BMSQ_LUT_SIZE = BMSQ_LUT_SIZE_D; #if QUIET GlobalMembersUtil.quiet = true; #else GlobalMembersUtil.quiet = false; #endif Console.Write("The Analysis & Resynthesis Sound Spectrograph {0}\n", version); RandomNumbers.Seed((int)DateTime.Now.Millisecond); bool doHelp = false; for (i = 0; i < argc; i++) { if (string.Compare(args[i], "/?") == 0) // DOS friendly help { doHelp = true; } if (args[i][0] != '-') // if the argument is not a function { if (in_name == null) // if the input file name hasn't been filled in yet { in_name = args[i]; } else if (out_name == null) // if the input name has been filled but not the output name yet { out_name = args[i]; } else // if both names have already been filled in { Console.Error.WriteLine("You can only have two file names as parameters.\nRemove parameter \"%s\".\nExiting with error.\n", args[i]); Environment.Exit(1); } } else // if the argument is a parameter { if (string.Compare(args[i], "--analysis") == 0 || string.Compare(args[i], "-a") == 0) { mode = 1; } if (string.Compare(args[i], "--sine") == 0 || string.Compare(args[i], "-s") == 0) { mode = 2; } if (string.Compare(args[i], "--noise") == 0 || string.Compare(args[i], "-n") == 0) { mode = 3; } if (string.Compare(args[i], "--quiet") == 0 || string.Compare(args[i], "-q") == 0) { GlobalMembersUtil.quiet = true; } if (string.Compare(args[i], "--linear") == 0 || string.Compare(args[i], "-l") == 0) { GlobalMembersDsp.LOGBASE = 1.0; } if (string.Compare(args[i], "--sample-rate") == 0 || string.Compare(args[i], "-r") == 0) { if (StringUtils.IsNumeric(args[++i])) { samplerate = Convert.ToInt32(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--min-freq") == 0 || string.Compare(args[i], "-min") == 0) { if (StringUtils.IsNumeric(args[++i])) { basefreq = Convert.ToDouble(args[i]); if (basefreq == 0) { basefreq = Double.MinValue; // Set it to this extremely close-to-zero number so that it's considered set } } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--max-freq") == 0 || string.Compare(args[i], "-max") == 0) { if (StringUtils.IsNumeric(args[++i])) { maxfreq = Convert.ToDouble(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--bpo") == 0 || string.Compare(args[i], "-b") == 0) { if (StringUtils.IsNumeric(args[++i])) { bpo = Convert.ToDouble(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--pps") == 0 || string.Compare(args[i], "-p") == 0) { if (StringUtils.IsNumeric(args[++i])) { pixpersec = Convert.ToDouble(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--height") == 0 || string.Compare(args[i], "-y") == 0) { if (StringUtils.IsNumeric(args[++i])) { Ysize = Convert.ToInt32(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--width") == 0 || string.Compare(args[i], "-x") == 0) { if (StringUtils.IsNumeric(args[++i])) { Xsize = Convert.ToInt32(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--loop-size") == 0) { if (StringUtils.IsNumeric(args[++i])) { GlobalMembersDsp.LOOP_SIZE_SEC = Convert.ToInt32(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--log-base") == 0) { if (StringUtils.IsNumeric(args[++i])) { GlobalMembersDsp.LOGBASE = Convert.ToDouble(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--bmsq-lut-size") == 0) { if (StringUtils.IsNumeric(args[++i])) { GlobalMembersDsp.BMSQ_LUT_SIZE = Convert.ToInt32(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--pi") == 0) // lol { if (StringUtils.IsNumeric(args[++i])) { GlobalMembersDsp.PI = Convert.ToDouble(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--format-param") == 0 || string.Compare(args[i], "-f") == 0) { if (StringUtils.IsNumeric(args[++i])) { format_param = Convert.ToInt32(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } if (string.Compare(args[i], "--brightness") == 0 || string.Compare(args[i], "-g") == 0) { if (StringUtils.IsNumeric(args[++i])) { brightness = Convert.ToDouble(args[i]); } else { Console.Error.WriteLine(MSG_NUMBER_EXPECTED, args[i - 1]); Environment.Exit(1); } } // TODO implement --duration, -d if (string.Compare(args[i], "--version") == 0 || string.Compare(args[i], "-v") == 0) { Console.Write("Copyright (C) 2005-2008 Michel Rouzic\nProgram last modified by its author on {0}\n", date); Environment.Exit(0); } if (doHelp || string.Compare(args[i], "--help") == 0 || string.Compare(args[i], "-h") == 0) { GlobalMembersArss.PrintHelp(); Environment.Exit(0); } if (string.Compare(args[i], "--adv-help") == 0) { GlobalMembersArss.PrintAdvancedHelp(); Environment.Exit(0); } } } if (in_name != null) // if in_name has already been filled in { fin = new BinaryFile(in_name); // try to open it if (fin == null) { Console.Error.WriteLine("The input file {0} could not be found\nExiting with error.\n", in_name); Environment.Exit(1); } Console.Write("Input file : {0}\n", in_name); } else { if (GlobalMembersUtil.quiet) { Console.Error.WriteLine("Please specify an input file.\nExiting with error.\n"); Environment.Exit(1); } Console.Write("Type 'help' to read the manual page\n"); do { fin = null; Console.Write("Input file : "); in_name = GlobalMembersUtil.GetString(); if (string.Compare(in_name, "help") == 0) // if 'help' has been typed { fin = null; GlobalMembersArss.PrintHelp(); // print the help } else { if (File.Exists(in_name)) { fin = new BinaryFile(in_name); } } }while (fin == null); } if (out_name != null) // if out_name has already been filled in { fout = new BinaryFile(out_name, BinaryFile.ByteOrder.LittleEndian, true); if (fout == null) { Console.Error.WriteLine("The output file {0} could not be opened.\nPlease make sure it isn't opened by any other program and press Return.\nExiting with error.\n", out_name); Environment.Exit(1); } Console.Write("Output file : {0}\n", out_name); } else { if (GlobalMembersUtil.quiet) { Console.Error.WriteLine("Please specify an output file.\nExiting with error.\n"); Environment.Exit(1); } Console.Write("Output file : "); out_name = GlobalMembersUtil.GetString(); fout = null; if (out_name != null && out_name != "") { fout = new BinaryFile(out_name, BinaryFile.ByteOrder.LittleEndian, true); } while (fout == null) { Console.Write("Output file : "); out_name = GlobalMembersUtil.GetString(); if (out_name != null && out_name != "") { fout = new BinaryFile(out_name, BinaryFile.ByteOrder.LittleEndian, true); } } // we will never get here cause BinaryFile does not return a null while (fout == null) { Console.Error.WriteLine("The output file {0} could not be opened.\nPlease make sure it isn't opened by any other program and press Return.\n", out_name); Console.Read(); fout = new BinaryFile(out_name, BinaryFile.ByteOrder.LittleEndian, true); } } // make the string lowercase in_name = in_name.ToLower(); if (mode == 0 && in_name.EndsWith(".wav")) { mode = 1; // Automatic switch to the Analysis mode } if (mode == 0) { do { if (GlobalMembersUtil.quiet) { Console.Error.WriteLine("Please specify an operation mode.\nUse either --analysis (-a), --sine (-s) or --noise (-n).\nExiting with error.\n"); Environment.Exit(1); } Console.Write("Choose the mode (Press 1, 2 or 3) :\n\t1. Analysis\n\t2. Sine synthesis\n\t3. Noise synthesis\n> "); mode = (byte)GlobalMembersUtil.GetFloat(); }while (mode != 1 && mode != 2 && mode != 3); } if (mode == 1) { //sound = GlobalMembersSound_io.wav_in(fin, ref channels, ref samplecount, ref samplerate); // Sound input fin.Close(); sound = GlobalMembersSoundIO.ReadWaveFile(in_name, ref channels, ref samplecount, ref samplerate); // Sound input #if DEBUG Console.Write("samplecount : {0:D}\nchannels : {1:D}\n", samplecount, channels); #endif GlobalMembersArss.SettingsInput(ref Ysize, ref samplecount, ref samplerate, ref basefreq, ref maxfreq, ref pixpersec, ref bpo, ref Xsize, 0); // User settings input image = GlobalMembersDsp.Analyze(ref sound[0], ref samplecount, ref samplerate, ref Xsize, ref Ysize, ref bpo, ref pixpersec, ref basefreq); // Analysis if (brightness != 1.0) { GlobalMembersDsp.BrightnessControl(ref image, ref Ysize, ref Xsize, 1.0 / brightness); } GlobalMembersImageIO.BMPWrite(fout, image, Ysize, Xsize); // Image output } if (mode == 2 || mode == 3) { sound = new double[1][]; image = GlobalMembersImageIO.BMPRead(fin, ref Ysize, ref Xsize); // Image input // if the output format parameter is undefined if (format_param == 0) { if (!GlobalMembersUtil.quiet) // if prompting is allowed { format_param = GlobalMembersSoundIO.GetWaveOutParameters(); } else { format_param = 32; // default is 32 } } GlobalMembersArss.SettingsInput(ref Ysize, ref samplecount, ref samplerate, ref basefreq, ref maxfreq, ref pixpersec, ref bpo, ref Xsize, 1); // User settings input if (brightness != 1.0) { GlobalMembersDsp.BrightnessControl(ref image, ref Ysize, ref Xsize, brightness); } if (mode == 2) { sound[0] = GlobalMembersDsp.SynthesizeSine(ref image, ref Xsize, ref Ysize, ref samplecount, ref samplerate, ref basefreq, ref pixpersec, ref bpo); // Sine synthesis } else { sound[0] = GlobalMembersDsp.SynthesizeNoise(ref image, ref Xsize, ref Ysize, ref samplecount, ref samplerate, ref basefreq, ref pixpersec, ref bpo); // Noise synthesis } GlobalMembersSoundIO.WriteWaveFile(fout, sound, 1, samplecount, samplerate, format_param); } clockb = GlobalMembersUtil.GetTime(); TimeSpan duration = TimeSpan.FromTicks((clockb - GlobalMembersDsp.clockA)); Console.Write("Processing time : {0:D2} m {1:D2} s {1:D2} ms\n", duration.Minutes, duration.Seconds, duration.Milliseconds); GlobalMembersUtil.WinReturn(); }
/// <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 }
/// <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); }