Sound Sound_resample (Sound me, double samplingFrequency, long precision) { double upfactor = samplingFrequency * my dx; if (fabs (upfactor - 2) < 1e-6) return Sound_upsample (me); if (fabs (upfactor - 1) < 1e-6) return Data_copy (me); try { long numberOfSamples = lround ((my xmax - my xmin) * samplingFrequency); if (numberOfSamples < 1) Melder_throw (U"The resampled Sound would have no samples."); autoSound filtered = NULL; if (upfactor < 1.0) { // need anti-aliasing filter? long nfft = 1, antiTurnAround = 1000; while (nfft < my nx + antiTurnAround * 2) nfft *= 2; autoNUMvector <double> data (1, nfft); filtered.reset (Sound_create (my ny, my xmin, my xmax, my nx, my dx, my x1)); for (long channel = 1; channel <= my ny; channel ++) { for (long i = 1; i <= nfft; i ++) { data [i] = 0; } NUMvector_copyElements (my z [channel], & data [antiTurnAround], 1, my nx); NUMrealft (data.peek(), nfft, 1); // go to the frequency domain for (long i = (long) floor (upfactor * nfft); i <= nfft; i ++) { data [i] = 0; // filter away high frequencies } data [2] = 0.0; NUMrealft (data.peek(), nfft, -1); // return to the time domain double factor = 1.0 / nfft; double *to = filtered -> z [channel]; for (long i = 1; i <= my nx; i ++) { to [i] = data [i + antiTurnAround] * factor; } } me = filtered.peek(); // reference copy; remove at end } autoSound thee = Sound_create (my ny, my xmin, my xmax, numberOfSamples, 1.0 / samplingFrequency, 0.5 * (my xmin + my xmax - (numberOfSamples - 1) / samplingFrequency)); for (long channel = 1; channel <= my ny; channel ++) { double *from = my z [channel]; double *to = thy z [channel]; if (precision <= 1) { for (long i = 1; i <= numberOfSamples; i ++) { double x = Sampled_indexToX (thee.peek(), i); double index = Sampled_xToIndex (me, x); long leftSample = (long) floor (index); double fraction = index - leftSample; to [i] = leftSample < 1 || leftSample >= my nx ? 0.0 : (1 - fraction) * from [leftSample] + fraction * from [leftSample + 1]; } } else { for (long i = 1; i <= numberOfSamples; i ++) { double x = Sampled_indexToX (thee.peek(), i); double index = Sampled_xToIndex (me, x); to [i] = NUM_interpolate_sinc (my z [channel], my nx, index, precision); } } } return thee.transfer(); } catch (MelderError) { Melder_throw (me, U": not resampled."); } }
Sound Sound_upsample (Sound me) { try { long nfft = 1; while (nfft < my nx + 2000) nfft *= 2; autoSound thee = Sound_create (my ny, my xmin, my xmax, my nx * 2, my dx / 2, my x1 - my dx / 4); for (long channel = 1; channel <= my ny; channel ++) { autoNUMvector <double> data (1, 2 * nfft); // zeroing is important... NUMvector_copyElements (my z [channel], & data [1000], 1, my nx); // ...because this fills only part of the sound NUMrealft (data.peek(), nfft, 1); long imin = (long) (nfft * 0.95); for (long i = imin + 1; i <= nfft; i ++) { data [i] *= ((double) (nfft - i)) / (nfft - imin); } data [2] = 0.0; NUMrealft (data.peek(), 2 * nfft, -1); double factor = 1.0 / nfft; for (long i = 1; i <= thy nx; i ++) { thy z [channel] [i] = data [i + 2000] * factor; } } return thee.transfer(); } catch (MelderError) { Melder_throw (me, U": not upsampled."); } }
Sound Sound_resample (Sound me, double samplingFrequency, long precision) { double *data = NULL; double upfactor = samplingFrequency * my dx; long numberOfSamples = floor ((my xmax - my xmin) * samplingFrequency + 0.5), i; Sound thee = NULL, filtered = NULL; if (fabs (upfactor - 2) < 1e-6) return Sound_upsample (me); if (fabs (upfactor - 1) < 1e-6) return (structSound *)Data_copy (me); if (numberOfSamples < 1) return (structSound *)Melder_errorp ("Cannot resample to 0 samples."); thee = Sound_create (my ny, my xmin, my xmax, numberOfSamples, 1.0 / samplingFrequency, 0.5 * (my xmin + my xmax - (numberOfSamples - 1) / samplingFrequency)); cherror if (upfactor < 1.0) { /* Need anti-aliasing filter? */ long nfft = 1, antiTurnAround = 1000; while (nfft < my nx + antiTurnAround * 2) nfft *= 2; data = NUMdvector (1, nfft); cherror filtered = Sound_create (my ny, my xmin, my xmax, my nx, my dx, my x1); cherror for (long channel = 1; channel <= my ny; channel ++) { for (long i = 1; i <= nfft; i ++) { data [i] = 0; } NUMdvector_copyElements (my z [channel], data + antiTurnAround, 1, my nx); NUMrealft (data, nfft, 1); cherror /* Go to the frequency domain. */ for (long i = floor (upfactor * nfft); i <= nfft; i ++) { data [i] = 0; /* Filter away high frequencies. */ } data [2] = 0.0; NUMrealft (data, nfft, -1); cherror /* Return to the time domain. */ double factor = 1.0 / nfft; double *to = filtered -> z [channel]; for (long i = 1; i <= my nx; i ++) { to [i] = data [i + antiTurnAround] * factor; } } me = filtered; /* Reference copy. Remove at end. */ }
Sound Sound_upsample (Sound me) { double *data = NULL; Sound thee = NULL; long nfft = 1; while (nfft < my nx + 2000) nfft *= 2; thee = Sound_create (my ny, my xmin, my xmax, my nx * 2, my dx / 2, my x1); cherror data = NUMdvector (1, 2 * nfft); cherror for (long channel = 1; channel <= my ny; channel ++) { NUMdvector_copyElements (my z [channel], data + 1000, 1, my nx); NUMrealft (data, nfft, 1); cherror long imin = (long) (nfft * 0.95); for (long i = imin + 1; i <= nfft; i ++) { data [i] *= ((double) (nfft - i)) / (nfft - imin); } data [2] = 0.0; NUMrealft (data, 2 * nfft, -1); cherror double factor = 1.0 / nfft; for (long i = 1; i <= thy nx; i ++) { thy z [channel] [i] = data [i + 2000] * factor; } } end: NUMdvector_free (data, 1); iferror forget (thee); return thee; }
autoSound Spectrum_to_Sound (Spectrum me) { try { double *re = my z [1], *im = my z [2]; double lastFrequency = my x1 + (my nx - 1) * my dx; int originalNumberOfSamplesProbablyOdd = im [my nx] != 0.0 || my xmax - lastFrequency > 0.25 * my dx; if (my x1 != 0.0) Melder_throw (U"A Fourier-transformable Spectrum must have a first frequency of 0 Hz, not ", my x1, U" Hz."); long numberOfSamples = 2 * my nx - ( originalNumberOfSamplesProbablyOdd ? 1 : 2 ); autoSound thee = Sound_createSimple (1, 1 / my dx, numberOfSamples * my dx); double *amp = thy z [1]; double scaling = my dx; amp [1] = re [1] * scaling; for (long i = 2; i < my nx; i ++) { amp [i + i - 1] = re [i] * scaling; amp [i + i] = im [i] * scaling; } if (originalNumberOfSamplesProbablyOdd) { amp [numberOfSamples] = re [my nx] * scaling; if (numberOfSamples > 1) amp [2] = im [my nx] * scaling; } else { amp [2] = re [my nx] * scaling; } NUMrealft (amp, numberOfSamples, -1); return thee; } catch (MelderError) { Melder_throw (me, U": not converted to Sound."); } }
autoSpectrum Spectrum_lpcSmoothing (Spectrum me, int numberOfPeaks, double preemphasisFrequency) { try { double gain, a [100]; long numberOfCoefficients = 2 * numberOfPeaks; autoSound sound = Spectrum_to_Sound (me); NUMpreemphasize_f (sound -> z [1], sound -> nx, sound -> dx, preemphasisFrequency); NUMburg (sound -> z [1], sound -> nx, a, numberOfCoefficients, & gain); for (long i = 1; i <= numberOfCoefficients; i ++) a [i] = - a [i]; autoSpectrum thee = Data_copy (me); long nfft = 2 * (thy nx - 1); long ndata = numberOfCoefficients < nfft ? numberOfCoefficients : nfft - 1; double scale = 10 * (gain > 0 ? sqrt (gain) : 1) / numberOfCoefficients; autoNUMvector <double> data (1, nfft); data [1] = 1; for (long i = 1; i <= ndata; i ++) data [i + 1] = a [i]; NUMrealft (data.peek(), nfft, 1); double *re = thy z [1]; double *im = thy z [2]; re [1] = scale / data [1]; im [1] = 0.0; long halfnfft = nfft / 2; for (long i = 2; i <= halfnfft; i ++) { double real = data [i + i - 1], imag = data [i + i]; re [i] = scale / sqrt (real * real + imag * imag) / (1 + thy dx * (i - 1) / preemphasisFrequency); im [i] = 0; } re [halfnfft + 1] = scale / data [2] / (1 + thy dx * halfnfft / preemphasisFrequency); im [halfnfft + 1] = 0.0; return thee; } catch (MelderError) { Melder_throw (me, U": not smoothed."); } }
Sound Sound_autoCorrelate (Sound me, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) { try { long numberOfChannels = my ny, n1 = my nx, n2 = n1 + n1 - 1, nfft = 1; while (nfft < n2) nfft *= 2; autoNUMvector <double> data (1, nfft); double my_xlast = my x1 + (n1 - 1) * my dx; autoSound thee = Sound_create (numberOfChannels, my xmin - my xmax, my xmax - my xmin, n2, my dx, my x1 - my_xlast); for (long channel = 1; channel <= numberOfChannels; channel ++) { double *a = my z [channel]; for (long i = n1; i > 0; i --) data [i] = a [i]; for (long i = n1 + 1; i <= nfft; i ++) data [i] = 0.0; NUMrealft (data.peek(), nfft, 1); data [1] *= data [1]; data [2] *= data [2]; for (long i = 3; i <= nfft; i += 2) { data [i] = data [i] * data [i] + data [i + 1] * data [i + 1]; data [i + 1] = 0.0; // reverse me by taking the conjugate of data1 } NUMrealft (data.peek(), nfft, -1); a = thy z [channel]; for (long i = 1; i < n1; i ++) { a [i] = data [i + (nfft - (n1 - 1))]; // data for the first part ("negative lags") is at the end of data } for (long i = 1; i <= n1; i ++) { a [i + (n1 - 1)] = data [i]; // data for the second part ("positive lags") is at the beginning of data } } switch (signalOutsideTimeDomain) { case kSounds_convolve_signalOutsideTimeDomain_ZERO: { // do nothing } break; case kSounds_convolve_signalOutsideTimeDomain_SIMILAR: { for (long channel = 1; channel <= numberOfChannels; channel ++) { double *a = thy z [channel]; double edge = n1; for (long i = 1; i < edge; i ++) { double factor = edge / i; a [i] *= factor; a [n2 + 1 - i] *= factor; } } } break; //case kSounds_convolve_signalOutsideTimeDomain_PERIODIC: { // do nothing //} break; default: Melder_fatal (U"Sounds_autoCorrelate: unimplemented outside-time-domain strategy ", signalOutsideTimeDomain); } switch (scaling) { case kSounds_convolve_scaling_INTEGRAL: { Vector_multiplyByScalar (thee.peek(), my dx / nfft); } break; case kSounds_convolve_scaling_SUM: { Vector_multiplyByScalar (thee.peek(), 1.0 / nfft); } break; case kSounds_convolve_scaling_NORMALIZE: { double normalizationFactor = Matrix_getNorm (me) * Matrix_getNorm (me); if (normalizationFactor != 0.0) { Vector_multiplyByScalar (thee.peek(), 1.0 / nfft / normalizationFactor); } } break; case kSounds_convolve_scaling_PEAK_099: { Vector_scale (thee.peek(), 0.99); } break; default: Melder_fatal (U"Sounds_autoCorrelate: unimplemented scaling ", scaling); } return thee.transfer(); } catch (MelderError) { Melder_throw (me, U": autocorrelation not computed."); } }
Sound Sounds_crossCorrelate (Sound me, Sound thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) { try { if (my ny > 1 && thy ny > 1 && my ny != thy ny) Melder_throw (U"The numbers of channels of the two sounds have to be equal or 1."); if (my dx != thy dx) Melder_throw (U"The sampling frequencies of the two sounds have to be equal."); long numberOfChannels = my ny > thy ny ? my ny : thy ny; long n1 = my nx, n2 = thy nx; long n3 = n1 + n2 - 1, nfft = 1; while (nfft < n3) nfft *= 2; autoNUMvector <double> data1 (1, nfft); autoNUMvector <double> data2 (1, nfft); double my_xlast = my x1 + (n1 - 1) * my dx; autoSound him = Sound_create (numberOfChannels, thy xmin - my xmax, thy xmax - my xmin, n3, my dx, thy x1 - my_xlast); for (long channel = 1; channel <= numberOfChannels; channel ++) { double *a = my z [my ny == 1 ? 1 : channel]; for (long i = n1; i > 0; i --) data1 [i] = a [i]; for (long i = n1 + 1; i <= nfft; i ++) data1 [i] = 0.0; a = thy z [thy ny == 1 ? 1 : channel]; for (long i = n2; i > 0; i --) data2 [i] = a [i]; for (long i = n2 + 1; i <= nfft; i ++) data2 [i] = 0.0; NUMrealft (data1.peek(), nfft, 1); NUMrealft (data2.peek(), nfft, 1); data2 [1] *= data1 [1]; data2 [2] *= data1 [2]; for (long i = 3; i <= nfft; i += 2) { double temp = data1 [i] * data2 [i] + data1 [i + 1] * data2 [i + 1]; // reverse me by taking the conjugate of data1 data2 [i + 1] = data1 [i] * data2 [i + 1] - data1 [i + 1] * data2 [i]; // reverse me by taking the conjugate of data1 data2 [i] = temp; } NUMrealft (data2.peek(), nfft, -1); a = him -> z [channel]; for (long i = 1; i < n1; i ++) { a [i] = data2 [i + (nfft - (n1 - 1))]; // data for the first part ("negative lags") is at the end of data2 } for (long i = 1; i <= n2; i ++) { a [i + (n1 - 1)] = data2 [i]; // data for the second part ("positive lags") is at the beginning of data2 } } switch (signalOutsideTimeDomain) { case kSounds_convolve_signalOutsideTimeDomain_ZERO: { // do nothing } break; case kSounds_convolve_signalOutsideTimeDomain_SIMILAR: { for (long channel = 1; channel <= numberOfChannels; channel ++) { double *a = his z [channel]; double edge = n1 < n2 ? n1 : n2; for (long i = 1; i < edge; i ++) { double factor = edge / i; a [i] *= factor; a [n3 + 1 - i] *= factor; } } } break; //case kSounds_convolve_signalOutsideTimeDomain_PERIODIC: { // do nothing //} break; default: Melder_fatal (U"Sounds_crossCorrelate: unimplemented outside-time-domain strategy ", signalOutsideTimeDomain); } switch (scaling) { case kSounds_convolve_scaling_INTEGRAL: { Vector_multiplyByScalar (him.peek(), my dx / nfft); } break; case kSounds_convolve_scaling_SUM: { Vector_multiplyByScalar (him.peek(), 1.0 / nfft); } break; case kSounds_convolve_scaling_NORMALIZE: { double normalizationFactor = Matrix_getNorm (me) * Matrix_getNorm (thee); if (normalizationFactor != 0.0) { Vector_multiplyByScalar (him.peek(), 1.0 / nfft / normalizationFactor); } } break; case kSounds_convolve_scaling_PEAK_099: { Vector_scale (him.peek(), 0.99); } break; default: Melder_fatal (U"Sounds_crossCorrelate: unimplemented scaling ", scaling); } return him.transfer(); } catch (MelderError) { Melder_throw (me, U" & ", thee, U": not cross-correlated."); } }