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