Sound EEG_to_Sound_modulated (EEG me, double baseFrequency, double channelBandwidth, const wchar_t *channelRanges) { try { long numberOfChannels; autoNUMvector <long> channelNumbers (NUMstring_getElementsOfRanges (channelRanges, my d_numberOfChannels, & numberOfChannels, NULL, L"channel", true), 1); double maxFreq = baseFrequency + my d_numberOfChannels * channelBandwidth; double samplingFrequency = 2 * maxFreq; samplingFrequency = samplingFrequency < 44100 ? 44100 : samplingFrequency; autoSound thee = Sound_createSimple (1, my xmax - my xmin, samplingFrequency); for (long i = 1; i <= numberOfChannels; i++) { long ichannel = channelNumbers[i]; double fbase = baseFrequency;// + (ichannel - 1) * channelBandwidth; autoSound si = Sound_extractChannel (my d_sound, ichannel); autoSpectrum spi = Sound_to_Spectrum (si.peek(), 1); Spectrum_passHannBand (spi.peek(), 0.5, channelBandwidth - 0.5, 0.5); autoSpectrum spi_shifted = Spectrum_shiftFrequencies (spi.peek(), fbase, samplingFrequency / 2, 30); autoSound resampled = Spectrum_to_Sound (spi_shifted.peek()); long nx = resampled -> nx < thy nx ? resampled -> nx : thy nx; for (long j = 1; j <= nx; j++) { thy z[1][j] += resampled -> z[1][j]; } } Vector_scale (thee.peek(), 0.99); return thee.transfer(); } catch (MelderError) { Melder_throw (me, ": no playable sound created."); } }
Cepstrogram Sound_to_Cepstrogram (Sound me, double analysisWidth, double dt, double maximumFrequency) { try { double windowDuration = 2 * analysisWidth; /* gaussian window */ long nFrames; // Convenience: analyse the whole sound into one Cepstrogram_frame if (windowDuration > my dx * my nx) { windowDuration = my dx * my nx; } double t1, samplingFrequency = 2 * maximumFrequency; autoSound sound = Sound_resample (me, samplingFrequency, 50); Sampled_shortTermAnalysis (me, windowDuration, dt, & nFrames, & t1); autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency); autoSound window = Sound_createGaussian (windowDuration, samplingFrequency); double qmin, qmax, dq, q1; long nq; { // laziness: find out the proper dimensions autoSpectrum spec = Sound_to_Spectrum (sframe.peek(), 1); autoCepstrum cepstrum = Spectrum_to_Cepstrum (spec.peek()); qmin = cepstrum -> xmin; qmax = cepstrum -> xmax; dq = cepstrum -> dx; q1 = cepstrum -> x1; nq = cepstrum -> nx; } autoCepstrogram thee = Cepstrogram_create (my xmin, my xmax, nFrames, dt, t1, qmin, qmax, nq, dq, q1); autoMelderProgress progress (L"Cepstrogram analysis"); for (long iframe = 1; iframe <= nFrames; iframe++) { double t = Sampled_indexToX (thee.peek(), iframe); Sound_into_Sound (sound.peek(), sframe.peek(), t - windowDuration / 2); Vector_subtractMean (sframe.peek()); Sounds_multiply (sframe.peek(), window.peek()); autoSpectrum spec = Sound_to_Spectrum (sframe.peek(), 1); autoCepstrum cepstrum = Spectrum_to_Cepstrum (spec.peek()); for (long i = 1; i <= nq; i++) { thy z[i][iframe] = cepstrum -> z[1][i]; } if ((iframe % 10) == 1) { Melder_progress ((double) iframe / nFrames, L"Cepstrogram analysis of frame ", Melder_integer (iframe), L" out of ", Melder_integer (nFrames), L"."); } } return thee.transfer(); } catch (MelderError) { Melder_throw (me, ": no Cepstrogram created."); } }
autoComplexSpectrogram Sound_to_ComplexSpectrogram (Sound me, double windowLength, double timeStep) { try { double samplingFrequency = 1.0 / my dx, myDuration = my xmax - my xmin, t1; if (windowLength > myDuration) { Melder_throw (U"Your sound is too short:\nit should be at least as long as one window length."); } long nsamp_window = (long) floor (windowLength / my dx); long halfnsamp_window = nsamp_window / 2 - 1; nsamp_window = halfnsamp_window * 2; if (nsamp_window < 2) { Melder_throw (U"Your analysis window is too short: less than two samples."); } long numberOfFrames; Sampled_shortTermAnalysis (me, windowLength, timeStep, &numberOfFrames, &t1); // Compute sampling of the spectrum long numberOfFrequencies = halfnsamp_window + 1; double df = samplingFrequency / (numberOfFrequencies - 1); autoComplexSpectrogram thee = ComplexSpectrogram_create (my xmin, my xmax, numberOfFrames, timeStep, t1, 0.0, 0.5 * samplingFrequency, numberOfFrequencies, df, 0.0); // autoSound analysisWindow = Sound_create (1, 0.0, nsamp_window * my dx, nsamp_window, my dx, 0.5 * my dx); for (long iframe = 1; iframe <= numberOfFrames; iframe++) { double t = Sampled_indexToX (thee.get(), iframe); long leftSample = Sampled_xToLowIndex (me, t), rightSample = leftSample + 1; long startSample = rightSample - halfnsamp_window; long endSample = leftSample + halfnsamp_window; Melder_assert (startSample >= 1); Melder_assert (endSample <= my nx); for (long j = 1; j <= nsamp_window; j++) { analysisWindow -> z[1][j] = my z[1][startSample - 1 + j]; } // window ? autoSpectrum spec = Sound_to_Spectrum (analysisWindow.get(), 0); thy z[1][iframe] = spec -> z[1][1] * spec -> z[1][1]; thy phase[1][iframe] = 0.0; for (long ifreq = 2; ifreq <= numberOfFrequencies - 1; ifreq++) { double x = spec -> z[1][ifreq], y = spec -> z[2][ifreq]; thy z[ifreq][iframe] = x * x + y * y; // power thy phase[ifreq][iframe] = atan2 (y, x); // phase [-pi,+pi] } // even number of samples thy z[numberOfFrequencies][iframe] = spec -> z[1][numberOfFrequencies] * spec -> z[1][numberOfFrequencies]; thy phase[numberOfFrequencies][iframe] = 0.0; } return thee; } catch (MelderError) { Melder_throw (me, U": no ComplexSpectrogram created."); } }
Cepstrum Sound_to_Cepstrum (Sound me) { try { autoSpectrum spectrum = Sound_to_Spectrum (me, TRUE); autoCepstrum thee = Spectrum_to_Cepstrum (spectrum.peek()); return thee.transfer(); } catch (MelderError) { Melder_throw (me, ": no Cepstrum calculated."); } }
Ltas PointProcess_Sound_to_Ltas_harmonics (PointProcess pulses, Sound sound, long maximumHarmonic, double shortestPeriod, double longestPeriod, double maximumPeriodFactor) { try { long numberOfPeriods = pulses -> nt - 2; autoLtas ltas = Ltas_create (maximumHarmonic, 1.0); ltas -> xmax = maximumHarmonic; if (numberOfPeriods < 1) Melder_throw ("There are no periods in the point process."); autoMelderProgress progress (L"LTAS (harmonics) analysis..."); for (long ipulse = 2; ipulse < pulses -> nt; ipulse ++) { double leftInterval = pulses -> t [ipulse] - pulses -> t [ipulse - 1]; double rightInterval = pulses -> t [ipulse + 1] - pulses -> t [ipulse]; double intervalFactor = leftInterval > rightInterval ? leftInterval / rightInterval : rightInterval / leftInterval; Melder_progress ((double) ipulse / pulses -> nt, L"Sound & PointProcess: To Ltas: pulse ", Melder_integer (ipulse), L" out of ", Melder_integer (pulses -> nt)); if (leftInterval >= shortestPeriod && leftInterval <= longestPeriod && rightInterval >= shortestPeriod && rightInterval <= longestPeriod && intervalFactor <= maximumPeriodFactor) { /* * We have a period! Compute the spectrum. */ long localMaximumHarmonic; autoSound period = Sound_extractPart (sound, pulses -> t [ipulse] - 0.5 * leftInterval, pulses -> t [ipulse] + 0.5 * rightInterval, kSound_windowShape_RECTANGULAR, 1.0, FALSE); autoSpectrum spectrum = Sound_to_Spectrum (period.peek(), FALSE); localMaximumHarmonic = maximumHarmonic < spectrum -> nx ? maximumHarmonic : spectrum -> nx; for (long iharm = 1; iharm <= localMaximumHarmonic; iharm ++) { double realPart = spectrum -> z [1] [iharm]; double imaginaryPart = spectrum -> z [2] [iharm]; double energy = (realPart * realPart + imaginaryPart * imaginaryPart) * 2.0 * spectrum -> dx; ltas -> z [1] [iharm] += energy; } } else { numberOfPeriods -= 1; } } if (numberOfPeriods < 1) Melder_throw (L"There are no periods in the point process."); for (long iharm = 1; iharm <= ltas -> nx; iharm ++) { if (ltas -> z [1] [iharm] == 0.0) { ltas -> z [1] [iharm] = -300.0; } else { double energyInThisBand = ltas -> z [1] [iharm]; double powerInThisBand = energyInThisBand / (sound -> xmax - sound -> xmin); ltas -> z [1] [iharm] = 10.0 * log10 (powerInThisBand / 4.0e-10); } } return ltas.transfer(); } catch (MelderError) { Melder_throw (sound, " & ", pulses, ": LTAS analysis (harmonics) not performed."); } }
autoSound Sound_filter_formula (Sound me, const char32 *formula, Interpreter interpreter) { try { autoSound thee = Data_copy (me); if (my ny == 1) { autoSpectrum spec = Sound_to_Spectrum (me, true); Matrix_formula ((Matrix) spec.peek(), formula, interpreter, nullptr); autoSound him = Spectrum_to_Sound (spec.peek()); NUMvector_copyElements (his z [1], thy z [1], 1, thy nx); } else { for (long ichan = 1; ichan <= my ny; ichan ++) { autoSound channel = Sound_extractChannel (me, ichan); autoSpectrum spec = Sound_to_Spectrum (channel.peek(), true); Matrix_formula ((Matrix) spec.peek(), formula, interpreter, nullptr); autoSound him = Spectrum_to_Sound (spec.peek()); NUMvector_copyElements (his z [1], thy z [ichan], 1, thy nx); } } return thee; } catch (MelderError) { Melder_throw (me, U": not filtered (with formula)."); } }
Ltas Sound_to_Ltas (Sound me, double bandwidth) { try { autoSpectrum thee = Sound_to_Spectrum (me, TRUE); autoLtas him = Spectrum_to_Ltas (thee.peek(), bandwidth); double correction = -10.0 * log10 (thy dx * my nx * my dx); for (long iband = 1; iband <= his nx; iband ++) { his z [1] [iband] += correction; } return him.transfer(); } catch (MelderError) { Melder_throw (me, ": LTAS analysis not performed."); } }
autoSound Sound_filter_stopHannBand (Sound me, double fmin, double fmax, double smooth) { try { autoSound thee = Data_copy (me); if (my ny == 1) { autoSpectrum spec = Sound_to_Spectrum (me, true); Spectrum_stopHannBand (spec.peek(), fmin, fmax, smooth); autoSound him = Spectrum_to_Sound (spec.peek()); NUMvector_copyElements (his z [1], thy z [1], 1, thy nx); } else { for (long ichan = 1; ichan <= my ny; ichan ++) { autoSound channel = Sound_extractChannel (me, ichan); autoSpectrum spec = Sound_to_Spectrum (channel.peek(), true); Spectrum_stopHannBand (spec.peek(), fmin, fmax, smooth); autoSound him = Spectrum_to_Sound (spec.peek()); NUMvector_copyElements (his z [1], thy z [ichan], 1, thy nx); } } return thee; } catch (MelderError) { Melder_throw (me, U": not filtered (stop Hann band)."); } }
Sound EEG_to_Sound_frequencyShifted (EEG me, long channel, double frequencyShift, double samplingFrequency, double maxAmp) { try { autoSound si = Sound_extractChannel (my d_sound, channel); autoSpectrum spi = Sound_to_Spectrum (si.peek(), 1); autoSpectrum spi_shifted = Spectrum_shiftFrequencies (spi.peek(), frequencyShift, samplingFrequency / 2, 30); autoSound thee = Spectrum_to_Sound (spi_shifted.peek()); if (maxAmp > 0) { Vector_scale (thee.peek(), maxAmp); } return thee.transfer(); } catch (MelderError) { Melder_throw (me, ": channel not converted to sound."); } }
autoCochleagram Sound_to_Cochleagram (Sound me, double dt, double df, double dt_window, double forwardMaskingTime) { try { double duration = my nx * my dx; long nFrames = 1 + (long) floor ((duration - dt_window) / dt); long nsamp_window = (long) floor (dt_window / my dx), halfnsamp_window = nsamp_window / 2 - 1; long nf = lround (25.6 / df); double dampingFactor = forwardMaskingTime > 0.0 ? exp (- dt / forwardMaskingTime) : 0.0; // default 30 ms double integrationCorrection = 1.0 - dampingFactor; nsamp_window = halfnsamp_window * 2; if (nFrames < 2) return autoCochleagram (); double t1 = my x1 + 0.5 * (duration - my dx - (nFrames - 1) * dt); // centre of first frame autoCochleagram thee = Cochleagram_create (my xmin, my xmax, nFrames, dt, t1, df, nf); autoSound window = Sound_createSimple (1, nsamp_window * my dx, 1.0 / my dx); for (long iframe = 1; iframe <= nFrames; iframe ++) { double t = Sampled_indexToX (thee.get(), iframe); long leftSample = Sampled_xToLowIndex (me, t); long rightSample = leftSample + 1; long startSample = rightSample - halfnsamp_window; long endSample = rightSample + halfnsamp_window; if (startSample < 1) { Melder_casual (U"Start sample too small: ", startSample, U" instead of 1."); startSample = 1; } if (endSample > my nx) { Melder_casual (U"End sample too small: ", endSample, U" instead of ", my nx, U"."); endSample = my nx; } /* Copy a window to a frame. */ for (long i = 1; i <= nsamp_window; i ++) window -> z [1] [i] = ( my ny == 1 ? my z[1][i+startSample-1] : 0.5 * (my z[1][i+startSample-1] + my z[2][i+startSample-1]) ) * (0.5 - 0.5 * cos (2.0 * NUMpi * i / (nsamp_window + 1))); autoSpectrum spec = Sound_to_Spectrum (window.get(), true); autoExcitation excitation = Spectrum_to_Excitation (spec.get(), df); for (long ifreq = 1; ifreq <= nf; ifreq ++) thy z [ifreq] [iframe] = excitation -> z [1] [ifreq] + ( iframe > 1 ? dampingFactor * thy z [ifreq] [iframe - 1] : 0 ); } for (long iframe = 1; iframe <= nFrames; iframe ++) for (long ifreq = 1; ifreq <= nf; ifreq ++) thy z [ifreq] [iframe] *= integrationCorrection; return thee; } catch (MelderError) { Melder_throw (me, U": not converted to Cochleagram."); } }
autoPitch Pitch_smooth (Pitch me, double bandWidth) { try { autoPitch interp = Pitch_interpolate (me); autoMatrix matrix1 = Pitch_to_Matrix (interp.peek()); autoSound sound1 = Sound_create (1, 2 * matrix1->xmin - matrix1->xmax, 2 * matrix1->xmax - matrix1->xmin, 3 * matrix1->nx, matrix1->dx, matrix1->x1 - 2 * matrix1->nx * matrix1->dx); long firstVoiced = 0, lastVoiced = 0; for (long i = 1; i <= matrix1 -> nx; i ++) { double f = matrix1 -> z [1] [i]; if (f != 0.0) { if (! firstVoiced) firstVoiced = i; lastVoiced = i; sound1 -> z [1] [i + matrix1 -> nx] = f; } } /* Extrapolate. */ double fextrap = matrix1 -> z [1] [firstVoiced]; firstVoiced += matrix1 -> nx; for (long i = 1; i < firstVoiced; i ++) sound1 -> z [1] [i] = fextrap; fextrap = matrix1 -> z [1] [lastVoiced]; lastVoiced += matrix1 -> nx; for (long i = lastVoiced + 1; i <= sound1 -> nx; i ++) sound1 -> z [1] [i] = fextrap; /* Smooth. */ autoSpectrum spectrum = Sound_to_Spectrum (sound1.peek(), true); for (long i = 1; i <= spectrum -> nx; i ++) { double f = (i - 1) * spectrum -> dx, fT = f / bandWidth, factor = exp (- fT * fT); spectrum -> z [1] [i] *= factor; spectrum -> z [2] [i] *= factor; } autoSound sound2 = Spectrum_to_Sound (spectrum.peek()); autoMatrix matrix2 = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, 1, 1, 1, 1, 1); for (long i = 1; i <= my nx; i ++) { double originalF0 = my frame [i]. candidate [1]. frequency; matrix2 -> z [1] [i] = originalF0 > 0.0 && originalF0 < my ceiling ? sound2 -> z [1] [i + matrix2 -> nx] : 0.0; } autoPitch thee = Matrix_to_Pitch (matrix2.peek()); thy ceiling = my ceiling; return thee; } catch (MelderError) { Melder_throw (me, U": not smoothed."); } }
Ltas Sound_to_Ltas (Sound me, double bandwidth) { Spectrum thee = NULL; Ltas him = NULL; long iband; double correction; thee = Sound_to_Spectrum (me, TRUE); cherror him = Spectrum_to_Ltas (thee, bandwidth); cherror correction = -10 * log10 (thy dx * my nx * my dx); for (iband = 1; iband <= his nx; iband ++) { his z [1] [iband] += correction; } end: iferror forget (him); forget (thee); return him; }
PowerCepstrogram Sound_to_PowerCepstrogram (Sound me, double pitchFloor, double dt, double maximumFrequency, double preEmphasisFrequency) { try { // minimum analysis window has 3 periods of lowest pitch double analysisWidth = 3 / pitchFloor; double windowDuration = 2 * analysisWidth; /* gaussian window */ long nFrames; // Convenience: analyse the whole sound into one Cepstrogram_frame if (windowDuration > my dx * my nx) { windowDuration = my dx * my nx; } double t1, samplingFrequency = 2 * maximumFrequency; autoSound sound = Sound_resample (me, samplingFrequency, 50); Sound_preEmphasis (sound.peek(), preEmphasisFrequency); Sampled_shortTermAnalysis (me, windowDuration, dt, & nFrames, & t1); autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency); autoSound window = Sound_createGaussian (windowDuration, samplingFrequency); // find out the size of the FFT long nfft = 2; while (nfft < sframe -> nx) nfft *= 2; long nq = nfft / 2 + 1; double qmax = 0.5 * nfft / samplingFrequency, dq = qmax / (nq - 1); autoPowerCepstrogram thee = PowerCepstrogram_create (my xmin, my xmax, nFrames, dt, t1, 0, qmax, nq, dq, 0); autoMelderProgress progress (L"Cepstrogram analysis"); for (long iframe = 1; iframe <= nFrames; iframe++) { double t = Sampled_indexToX (thee.peek(), iframe); Sound_into_Sound (sound.peek(), sframe.peek(), t - windowDuration / 2); Vector_subtractMean (sframe.peek()); Sounds_multiply (sframe.peek(), window.peek()); autoSpectrum spec = Sound_to_Spectrum (sframe.peek(), 1); // FFT yes autoPowerCepstrum cepstrum = Spectrum_to_PowerCepstrum (spec.peek()); for (long i = 1; i <= nq; i++) { thy z[i][iframe] = cepstrum -> z[1][i]; } if ((iframe % 10) == 1) { Melder_progress ((double) iframe / nFrames, L"PowerCepstrogram analysis of frame ", Melder_integer (iframe), L" out of ", Melder_integer (nFrames), L"."); } } return thee.transfer(); } catch (MelderError) { Melder_throw (me, ": no PowerCepstrogram created."); } }
Spectrum Cepstrum_to_Spectrum (Cepstrum me) { try { autoSound x = Sound_create (1, my xmin, my xmax, my nx, my dx, my x1); NUMvector_copyElements (my z[1], x -> z[1], 1, my nx); autoSpectrum thee = Sound_to_Spectrum (x.peek(), TRUE); for (long i = 1; i <= thy nx; i++) { double ar = exp (thy z[1][i]); double ai = thy z[2][i]; thy z[1][i] = ar * cos (ai); thy z[2][i] = ar * sin (ai); } return thee.transfer(); } catch (MelderError) { Melder_throw (me, ": no Spectrum created."); } }
Spectrum Spectrum_cepstralSmoothing (Spectrum me, double bandWidth) { try { /* * dB-spectrum is log (power). */ autoSpectrum dBspectrum = Data_copy (me); double *re = dBspectrum -> z [1], *im = dBspectrum -> z [2]; for (long i = 1; i <= dBspectrum -> nx; i ++) { re [i] = log (re [i] * re [i] + im [i] * im [i] + 1e-300); im [i] = 0.0; } /* * Cepstrum is Fourier transform of dB-spectrum. */ autoSound cepstrum = Spectrum_to_Sound (dBspectrum.peek()); /* * Multiply cepstrum by a Gaussian. */ double factor = - bandWidth * bandWidth; for (long i = 1; i <= cepstrum -> nx; i ++) { double t = (i - 1) * cepstrum -> dx; cepstrum -> z [1] [i] *= exp (factor * t * t) * ( i == 1 ? 1 : 2 ); } /* * Smoothed power spectrum is original power spectrum convolved with a Gaussian. */ autoSpectrum thee = Sound_to_Spectrum (cepstrum.peek(), TRUE); /* * Convert power spectrum back into a "complex" spectrum without phase information. */ re = thy z [1], im = thy z [2]; for (long i = 1; i <= thy nx; i ++) { re [i] = exp (0.5 * re [i]); // i.e., sqrt (exp (re [i])) im [i] = 0.0; } return thee.transfer(); } catch (MelderError) { Melder_throw (me, ": cepstral smoothing not computed."); } }
autoSpectrum Cepstrum_to_Spectrum (Cepstrum me) { //TODO power cepstrum try { autoCepstrum cepstrum = Data_copy (me); cepstrum -> z[1][1] = my z[1][1]; for (long i = 2; i <= cepstrum -> nx; i++) { cepstrum -> z[1][i] = 2 * my z[1][i]; } autoSpectrum thee = Sound_to_Spectrum ((Sound) cepstrum.peek(), 1); double *re = thy z[1], *im = thy z[2]; for (long i = 1; i <= thy nx; i ++) { re[i] = exp (0.5 * re[i]); // i.e., sqrt (exp(re [i])) im[i] = 0.0; } return thee; } catch (MelderError) { Melder_throw (me, U": no Spectrum created."); } }
Ltas PointProcess_Sound_to_Ltas (PointProcess pulses, Sound sound, double maximumFrequency, double bandWidth, double shortestPeriod, double longestPeriod, double maximumPeriodFactor) { Ltas ltas = NULL, numbers = NULL; Sound period = NULL; Spectrum spectrum = NULL; long numberOfPeriods = pulses -> nt - 2, ipulse, ifreq, iband, totalNumberOfEnergies = 0; ltas = Ltas_create (maximumFrequency / bandWidth, bandWidth); cherror ltas -> xmax = maximumFrequency; numbers = (structLtas *)Data_copy (ltas); if (numberOfPeriods < 1) error1 (L"Cannot compute an Ltas if there are no periods in the point process.") for (ipulse = 2; ipulse < pulses -> nt; ipulse ++) { double leftInterval = pulses -> t [ipulse] - pulses -> t [ipulse - 1]; double rightInterval = pulses -> t [ipulse + 1] - pulses -> t [ipulse]; double intervalFactor = leftInterval > rightInterval ? leftInterval / rightInterval : rightInterval / leftInterval; Melder_progress4 ((double) ipulse / pulses -> nt, L"Sound & PointProcess: To Ltas: pulse ", Melder_integer (ipulse), L" out of ", Melder_integer (pulses -> nt)); if (leftInterval >= shortestPeriod && leftInterval <= longestPeriod && rightInterval >= shortestPeriod && rightInterval <= longestPeriod && intervalFactor <= maximumPeriodFactor) { /* * We have a period! Compute the spectrum. */ period = Sound_extractPart (sound, pulses -> t [ipulse] - 0.5 * leftInterval, pulses -> t [ipulse] + 0.5 * rightInterval, kSound_windowShape_RECTANGULAR, 1.0, FALSE); cherror spectrum = Sound_to_Spectrum (period, FALSE); cherror for (ifreq = 1; ifreq <= spectrum -> nx; ifreq ++) { double frequency = spectrum -> xmin + (ifreq - 1) * spectrum -> dx; double realPart = spectrum -> z [1] [ifreq]; double imaginaryPart = spectrum -> z [2] [ifreq]; double energy = (realPart * realPart + imaginaryPart * imaginaryPart) * 2.0 * spectrum -> dx /* OLD: * sound -> nx */; iband = ceil (frequency / bandWidth); if (iband >= 1 && iband <= ltas -> nx) { ltas -> z [1] [iband] += energy; numbers -> z [1] [iband] += 1; totalNumberOfEnergies += 1; } } forget (spectrum); forget (period); } else {
static autoSpectrum Sound_to_Spectrum_power (Sound me) { try { autoSpectrum thee = Sound_to_Spectrum (me, true); double scale = 2.0 * thy dx / (my xmax - my xmin); // factor '2' because we combine positive and negative frequencies // thy dx : width of frequency bin // my xmax - my xmin : duration of sound double *re = thy z[1], *im = thy z[2]; for (long i = 1; i <= thy nx; i++) { double power = scale * (re[i] * re[i] + im[i] * im [i]); re[i] = power; im[i] = 0; } // Correction of frequency bins at 0 Hz and nyquist: don't count for two. re[1] *= 0.5; re[thy nx] *= 0.5; return thee; } catch (MelderError) { Melder_throw (me, U": no Spectrum with spectral power created."); } }
static Matrix Sound_to_spectralpower (Sound me) { try { autoSpectrum s = Sound_to_Spectrum (me, TRUE); autoMatrix thee = Matrix_create (s -> xmin, s -> xmax, s -> nx, s -> dx, s -> x1, 1, 1, 1, 1, 1); double scale = 2.0 * s -> dx / (my xmax - my xmin); // factor '2' because of positive and negative frequencies // s -> dx : width of frequncy bin // my xmax - my xmin : duration of sound double *z = thy z[1], *re = s -> z[1], *im = s -> z[2]; for (long i = 1; i <= s -> nx; i++) { z[i] = scale * (re[i] * re[i] + im[i] * im [i]); } // Frequency bins at 0 Hz and nyquist don't count for two. z[1] *= 0.5; z[s -> nx] *= 0.5; return thee.transfer(); } catch (MelderError) { Melder_throw (me, ": no Matrix with spectral power created."); } }
autoMatrix Spectrum_unwrap (Spectrum me) { try { struct tribolet_struct tbs; int remove_linear_part = 1; long nfft = 2; while (nfft < my nx - 1) { nfft *= 2; } nfft *= 2; if (nfft / 2 != my nx - 1) { Melder_throw (U"Dimension of Spectrum is not (power of 2 - 1)."); } autoSound x = Spectrum_to_Sound (me); autoSound nx = Data_copy (x.get()); for (long i = 1; i <= x -> nx; i++) { nx -> z[1][i] *= (i - 1); } autoSpectrum snx = Sound_to_Spectrum (nx.get(), 1); autoMatrix thee = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, 1, 2, 2, 1, 1); // Common variables. tbs.thlinc = THLINC; tbs.thlcon = THLCON; tbs.x = x -> z[1]; tbs.nx = x -> nx; tbs.l = (long) floor (pow (2, EXP2) + 0.1); tbs.ddf = NUM2pi / ( (tbs.l) * nfft); tbs.reverse_sign = my z[1][1] < 0; tbs.count = 0; // Reuse snx : put phase derivative (d/df) in imaginary part. tbs.dvtmn2 = 0; for (long i = 1; i <= my nx; i ++) { double xr = my z[1][i], xi = my z[2][i]; double nxr = snx -> z[1][i], nxi = snx -> z[2][i]; double xmsq = xr * xr + xi * xi; double pdvt = PHADVT (xr, xi, nxr, nxi, xmsq); thy z[1][i] = xmsq; snx -> z[2][i] = pdvt; tbs.dvtmn2 += pdvt; } tbs.dvtmn2 = (2 * tbs.dvtmn2 - snx -> z[2][1] - snx -> z[2][my nx]) / (my nx - 1); autoMelderProgress progress (U"Phase unwrapping"); double pphase = 0, phase = 0; double ppdvt = snx -> z[2][1]; thy z[2][1] = PPVPHA (my z[1][1], my z[2][1], tbs.reverse_sign); for (long i = 2; i <= my nx; i ++) { double pfreq = NUM2pi * (i - 1) / nfft; double pdvt = snx -> z[2][i]; double ppv = PPVPHA (my z[1][i], my z[2][i], tbs.reverse_sign); phase = phase_unwrap (&tbs, pfreq, ppv, pdvt, &pphase, &ppdvt); ppdvt = pdvt; thy z[2][i] = pphase = phase; Melder_progress ( (double) i / my nx, i, U" unwrapped phases from ", my nx, U"."); } long iphase = (long) floor (phase / NUMpi + 0.1); // ppgb: better than truncation toward zero if (remove_linear_part) { phase /= my nx - 1; for (long i = 2; i <= my nx; i ++) { thy z[2][i] -= phase * (i - 1); } } Melder_information (U"Number of spectral values: ", tbs.count); Melder_information (U" iphase = ", iphase); return thee; } catch (MelderError) { Melder_throw (me, U": not unwrapped."); } }
Ltas PointProcess_Sound_to_Ltas (PointProcess pulses, Sound sound, double maximumFrequency, double bandWidth, double shortestPeriod, double longestPeriod, double maximumPeriodFactor) { try { long numberOfPeriods = pulses -> nt - 2, totalNumberOfEnergies = 0; autoLtas ltas = Ltas_create (maximumFrequency / bandWidth, bandWidth); ltas -> xmax = maximumFrequency; autoLtas numbers = Data_copy (ltas.peek()); if (numberOfPeriods < 1) Melder_throw ("Cannot compute an Ltas if there are no periods in the point process."); autoMelderProgress progress (L"Ltas analysis..."); for (long ipulse = 2; ipulse < pulses -> nt; ipulse ++) { double leftInterval = pulses -> t [ipulse] - pulses -> t [ipulse - 1]; double rightInterval = pulses -> t [ipulse + 1] - pulses -> t [ipulse]; double intervalFactor = leftInterval > rightInterval ? leftInterval / rightInterval : rightInterval / leftInterval; Melder_progress ((double) ipulse / pulses -> nt, L"Sound & PointProcess: To Ltas: pulse ", Melder_integer (ipulse), L" out of ", Melder_integer (pulses -> nt)); if (leftInterval >= shortestPeriod && leftInterval <= longestPeriod && rightInterval >= shortestPeriod && rightInterval <= longestPeriod && intervalFactor <= maximumPeriodFactor) { /* * We have a period! Compute the spectrum. */ autoSound period = Sound_extractPart (sound, pulses -> t [ipulse] - 0.5 * leftInterval, pulses -> t [ipulse] + 0.5 * rightInterval, kSound_windowShape_RECTANGULAR, 1.0, FALSE); autoSpectrum spectrum = Sound_to_Spectrum (period.peek(), FALSE); for (long ifreq = 1; ifreq <= spectrum -> nx; ifreq ++) { double frequency = spectrum -> xmin + (ifreq - 1) * spectrum -> dx; double realPart = spectrum -> z [1] [ifreq]; double imaginaryPart = spectrum -> z [2] [ifreq]; double energy = (realPart * realPart + imaginaryPart * imaginaryPart) * 2.0 * spectrum -> dx /* OLD: * sound -> nx */; long iband = ceil (frequency / bandWidth); if (iband >= 1 && iband <= ltas -> nx) { ltas -> z [1] [iband] += energy; numbers -> z [1] [iband] += 1; totalNumberOfEnergies += 1; } } } else { numberOfPeriods -= 1; } } if (numberOfPeriods < 1) Melder_throw ("There are no periods in the point process."); for (long iband = 1; iband <= ltas -> nx; iband ++) { if (numbers -> z [1] [iband] == 0.0) { ltas -> z [1] [iband] = NUMundefined; } else { /* * Each bin now contains a total energy in Pa2 sec. * To convert this to power density, we */ double totalEnergyInThisBand = ltas -> z [1] [iband]; if (0 /* i.e. if you just want to have a spectrum of the voiced parts... */) { double energyDensityInThisBand = totalEnergyInThisBand / ltas -> dx; double powerDensityInThisBand = energyDensityInThisBand / (sound -> xmax - sound -> xmin); ltas -> z [1] [iband] = 10.0 * log10 (powerDensityInThisBand / 4.0e-10); } else { /* * And this is what we really want. The total energy has to be redistributed. */ double meanEnergyInThisBand = totalEnergyInThisBand / numbers -> z [1] [iband]; double meanNumberOfEnergiesPerBand = (double) totalNumberOfEnergies / ltas -> nx; double redistributedEnergyInThisBand = meanEnergyInThisBand * meanNumberOfEnergiesPerBand; double redistributedEnergyDensityInThisBand = redistributedEnergyInThisBand / ltas -> dx; double redistributedPowerDensityInThisBand = redistributedEnergyDensityInThisBand / (sound -> xmax - sound -> xmin); ltas -> z [1] [iband] = 10.0 * log10 (redistributedPowerDensityInThisBand / 4.0e-10); /* OLD: ltas -> z [1] [iband] = 10.0 * log10 (ltas -> z [1] [iband] / numbers -> z [1] [iband] * sound -> nx);*/ } } } for (long iband = 1; iband <= ltas -> nx; iband ++) { if (ltas -> z [1] [iband] == NUMundefined) { long ibandleft = iband - 1, ibandright = iband + 1; while (ibandleft >= 1 && ltas -> z [1] [ibandleft] == NUMundefined) ibandleft --; while (ibandright <= ltas -> nx && ltas -> z [1] [ibandright] == NUMundefined) ibandright ++; if (ibandleft < 1 && ibandright > ltas -> nx) Melder_throw ("Cannot create an Ltas without energy in any bins."); if (ibandleft < 1) { ltas -> z [1] [iband] = ltas -> z [1] [ibandright]; } else if (ibandright > ltas -> nx) { ltas -> z [1] [iband] = ltas -> z [1] [ibandleft]; } else { double frequency = ltas -> x1 + (iband - 1) * ltas -> dx; double fleft = ltas -> x1 + (ibandleft - 1) * ltas -> dx; double fright = ltas -> x1 + (ibandright - 1) * ltas -> dx; ltas -> z [1] [iband] = ((fright - frequency) * ltas -> z [1] [ibandleft] + (frequency - fleft) * ltas -> z [1] [ibandright]) / (fright - fleft); } } } return ltas.transfer(); } catch (MelderError) { Melder_throw (sound, " & ", pulses, ": LTAS analysis not performed."); } }
Pitch Sound_to_Pitch_shs (Sound me, double timeStep, double minimumPitch, double maximumFrequency, double ceiling, long maxnSubharmonics, long maxnCandidates, double compressionFactor, long nPointsPerOctave) { try { double firstTime, newSamplingFrequency = 2 * maximumFrequency; double windowDuration = 2 / minimumPitch, halfWindow = windowDuration / 2; double atans = nPointsPerOctave * NUMlog2 (65.0 / 50.0) - 1; // Number of speech samples in the downsampled signal in each frame: // 100 for windowDuration == 0.04 and newSamplingFrequency == 2500 long nx = lround (windowDuration * newSamplingFrequency); // The minimum number of points for the fft is 256. long nfft = 1; while ( (nfft *= 2) < nx || nfft <= 128) { ; } long nfft2 = nfft / 2 + 1; double frameDuration = nfft / newSamplingFrequency; double df = newSamplingFrequency / nfft; // The number of points on the octave scale double fminl2 = NUMlog2 (minimumPitch), fmaxl2 = NUMlog2 (maximumFrequency); long nFrequencyPoints = (long) floor ((fmaxl2 - fminl2) * nPointsPerOctave); double dfl2 = (fmaxl2 - fminl2) / (nFrequencyPoints - 1); autoSound sound = Sound_resample (me, newSamplingFrequency, 50); long numberOfFrames; Sampled_shortTermAnalysis (sound.peek(), windowDuration, timeStep, &numberOfFrames, &firstTime); autoSound frame = Sound_createSimple (1, frameDuration, newSamplingFrequency); autoSound hamming = Sound_createHamming (nx / newSamplingFrequency, newSamplingFrequency); autoPitch thee = Pitch_create (my xmin, my xmax, numberOfFrames, timeStep, firstTime, ceiling, maxnCandidates); autoNUMvector<double> cc (1, numberOfFrames); autoNUMvector<double> specAmp (1, nfft2); autoNUMvector<double> fl2 (1, nfft2); autoNUMvector<double> yv2 (1, nfft2); autoNUMvector<double> arctg (1, nFrequencyPoints); autoNUMvector<double> al2 (1, nFrequencyPoints); Melder_assert (frame->nx >= nx); Melder_assert (hamming->nx == nx); // Compute the absolute value of the globally largest amplitude w.r.t. the global mean. double globalMean, globalPeak; Sound_localMean (sound.peek(), sound -> xmin, sound -> xmax, &globalMean); Sound_localPeak (sound.peek(), sound -> xmin, sound -> xmax, globalMean, &globalPeak); /* For the cubic spline interpolation we need the frequencies on an octave scale, i.e., a log2 scale. All frequencies must be DIFFERENT, otherwise the cubic spline interpolation will give corrupt results. Because log2(f==0) is not defined, we use the heuristic: f[2]-f[1] == f[3]-f[2]. */ for (long i = 2; i <= nfft2; i++) { fl2[i] = NUMlog2 ( (i - 1) * df); } fl2[1] = 2 * fl2[2] - fl2[3]; // Calculate frequencies regularly spaced on a log2-scale and // the frequency weighting function. for (long i = 1; i <= nFrequencyPoints; i++) { arctg[i] = 0.5 + atan (3 * (i - atans) / nPointsPerOctave) / NUMpi; } // Perform the analysis on all frames. for (long i = 1; i <= numberOfFrames; i++) { Pitch_Frame pitchFrame = &thy frame[i]; double hm = 1, f0, pitch_strength, localMean, localPeak; double tmid = Sampled_indexToX (thee.peek(), i); /* The center of this frame */ long nx_tmp = frame -> nx; // Copy a frame from the sound, apply a hamming window. Get local 'intensity' frame -> nx = nx; /*begin vies */ Sound_into_Sound (sound.peek(), frame.peek(), tmid - halfWindow); Sounds_multiply (frame.peek(), hamming.peek()); Sound_localMean (sound.peek(), tmid - 3 * halfWindow, tmid + 3 * halfWindow, &localMean); Sound_localPeak (sound.peek(), tmid - halfWindow, tmid + halfWindow, localMean, &localPeak); pitchFrame -> intensity = localPeak > globalPeak ? 1 : localPeak / globalPeak; frame -> nx = nx_tmp; /* einde vies */ // Get the Fourier spectrum. autoSpectrum spec = Sound_to_Spectrum (frame.peek(), 1); Melder_assert (spec->nx == nfft2); // From complex spectrum to amplitude spectrum. for (long j = 1; j <= nfft2; j++) { double rs = spec -> z[1][j], is = spec -> z[2][j]; specAmp[j] = sqrt (rs * rs + is * is); } // Enhance the peaks in the spectrum. spec_enhance_SHS (specAmp.peek(), nfft2); // Smooth the enhanced spectrum. spec_smoooth_SHS (specAmp.peek(), nfft2); // Go to a logarithmic scale and perform cubic spline interpolation to get // spectral values for the increased number of frequency points. NUMspline (fl2.peek(), specAmp.peek(), nfft2, 1e30, 1e30, yv2.peek()); for (long j = 1; j <= nFrequencyPoints; j++) { double f = fminl2 + (j - 1) * dfl2; NUMsplint (fl2.peek(), specAmp.peek(), yv2.peek(), nfft2, f, &al2[j]); } // Multiply by frequency selectivity of the auditory system. for (long j = 1; j <= nFrequencyPoints; j++) al2[j] = al2[j] > 0 ? al2[j] * arctg[j] : 0; // The subharmonic summation. Shift spectra in octaves and sum. Pitch_Frame_init (pitchFrame, maxnCandidates); autoNUMvector<double> sumspec (1, nFrequencyPoints); pitchFrame -> nCandidates = 0; /* !!!!! */ for (long m = 1; m <= maxnSubharmonics + 1; m++) { long kb = 1 + (long) floor (nPointsPerOctave * NUMlog2 (m)); for (long k = kb; k <= nFrequencyPoints; k++) { sumspec[k - kb + 1] += al2[k] * hm; } hm *= compressionFactor; } // First register the voiceless candidate (always present). Pitch_Frame_addPitch (pitchFrame, 0, 0, maxnCandidates); /* Get the best local estimates for the pitch as the maxima of the subharmonic sum spectrum by parabolic interpolation on three points: The formula for a parabole with a maximum is: y(x) = a - b (x - c)^2 with a, b, c >= 0 The three points are (-x, y1), (0, y2) and (x, y3). The solution for a (the maximum) and c (the position) is: a = (2 y1 (4 y2 + y3) - y1^2 - (y3 - 4 y2)^2)/( 8 (y1 - 2 y2 + y3) c = dx (y1 - y3) / (2 (y1 - 2 y2 + y3)) (b = (2 y2 - y1 - y3) / (2 dx^2) ) */ for (long k = 2; k <= nFrequencyPoints - 1; k++) { double y1 = sumspec[k - 1], y2 = sumspec[k], y3 = sumspec[k + 1]; if (y2 > y1 && y2 >= y3) { double denum = y1 - 2 * y2 + y3, tmp = y3 - 4 * y2; double x = dfl2 * (y1 - y3) / (2 * denum); double f = pow (2, fminl2 + (k - 1) * dfl2 + x); double strength = (2 * y1 * (4 * y2 + y3) - y1 * y1 - tmp * tmp) / (8 * denum); Pitch_Frame_addPitch (pitchFrame, f, strength, maxnCandidates); } } /* Check whether f0 corresponds to an actual periodicity T = 1 / f0: correlate two signal periods of duration T, one starting at the middle of the interval and one starting T seconds before. If there is periodicity the correlation coefficient should be high. However, some sounds do not show any regularity, or very low frequency and regularity, and nevertheless have a definite pitch, e.g. Shepard sounds. */ Pitch_Frame_getPitch (pitchFrame, &f0, &pitch_strength); if (f0 > 0) { cc[i] = Sound_correlateParts (sound.peek(), tmid - 1.0 / f0, tmid, 1.0 / f0); } } // Base V/UV decision on correlation coefficients. // Resize the pitch strengths w.r.t. the cc. double vuvCriterium = 0.52; for (long i = 1; i <= numberOfFrames; i++) { Pitch_Frame_resizeStrengths (& thy frame[i], cc[i], vuvCriterium); } return thee.transfer(); } catch (MelderError) { Melder_throw (me, U": no Pitch (shs) created."); } }
Sound Sound_deepenBandModulation (Sound me, double enhancement_dB, double flow, double fhigh, double slowModulation, double fastModulation, double bandSmoothing) { try { autoSound thee = Data_copy (me); double maximumFactor = pow (10, enhancement_dB / 20), alpha = sqrt (log (2.0)); double alphaslow = alpha / slowModulation, alphafast = alpha / fastModulation; for (long channel = 1; channel <= my ny; channel ++) { autoSound channelSound = Sound_extractChannel (me, channel); autoSpectrum orgspec = Sound_to_Spectrum (channelSound.peek(), true); /* * Keep the part of the sound that is outside the filter bank. */ autoSpectrum spec = Data_copy (orgspec.peek()); Spectrum_stopHannBand (spec.peek(), flow, fhigh, bandSmoothing); autoSound filtered = Spectrum_to_Sound (spec.peek()); long n = thy nx; double *amp = thy z [channel]; for (long i = 1; i <= n; i ++) amp [i] = filtered -> z [1] [i]; autoMelderProgress progress (U"Deepen band modulation..."); double fmin = flow; while (fmin < fhigh) { /* * Take a one-bark frequency band. */ double fmid_bark = NUMhertzToBark (fmin) + 0.5, ceiling; double fmax = NUMbarkToHertz (NUMhertzToBark (fmin) + 1); if (fmax > fhigh) fmax = fhigh; Melder_progress (fmin / fhigh, U"Band: ", Melder_fixed (fmin, 0), U" ... ", Melder_fixed (fmax, 0), U" Hz"); NUMmatrix_copyElements (orgspec -> z, spec -> z, 1, 2, 1, spec -> nx); Spectrum_passHannBand (spec.peek(), fmin, fmax, bandSmoothing); autoSound band = Spectrum_to_Sound (spec.peek()); /* * Compute a relative intensity contour. */ autoSound intensity = Data_copy (band.peek()); n = intensity -> nx; amp = intensity -> z [1]; for (long i = 1; i <= n; i ++) amp [i] = 10 * log10 (amp [i] * amp [i] + 1e-6); autoSpectrum intensityFilter = Sound_to_Spectrum (intensity.peek(), true); n = intensityFilter -> nx; for (long i = 1; i <= n; i ++) { double frequency = intensityFilter -> x1 + (i - 1) * intensityFilter -> dx; double slow = alphaslow * frequency, fast = alphafast * frequency; double factor = exp (- fast * fast) - exp (- slow * slow); intensityFilter -> z [1] [i] *= factor; intensityFilter -> z [2] [i] *= factor; } intensity.reset (Spectrum_to_Sound (intensityFilter.peek())); n = intensity -> nx; amp = intensity -> z [1]; for (long i = 1; i <= n; i ++) amp [i] = pow (10, amp [i] / 2); /* * Clip to maximum enhancement. */ ceiling = 1 + (maximumFactor - 1.0) * (0.5 - 0.5 * cos (NUMpi * fmid_bark / 13)); for (long i = 1; i <= n; i ++) amp [i] = 1 / (1 / amp [i] + 1 / ceiling); n = thy nx; amp = thy z [channel]; for (long i = 1; i <= n; i ++) amp [i] += band -> z [1] [i] * intensity -> z [1] [i]; fmin = fmax; } } Vector_scale (thee.peek(), 0.99); /* Truncate. */ thy xmin = my xmin; thy xmax = my xmax; thy nx = my nx; thy x1 = my x1; return thee.transfer(); } catch (MelderError) { Melder_throw (me, U": band modulation not deepened."); } }