1 /* Sound_and_Spectrum.cpp
2 *
3 * Copyright (C) 1992-2005,2011,2012,2015-2021 Paul Boersma
4 *
5 * This code is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 *
10 * This code is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this work. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "Sound_and_Spectrum.h"
20 #include "NUM2.h"
21
Sound_to_Spectrum(Sound me,bool fast)22 autoSpectrum Sound_to_Spectrum (Sound me, bool fast) {
23 try {
24 const integer numberOfFourierSamples = ( fast ? Melder_iroundUpToPowerOfTwo (my nx) : my nx );
25 const integer numberOfChannels = my ny;
26 const integer numberOfFrequencies = numberOfFourierSamples / 2 + 1; // 4 samples -> cos0 cos1 sin1 cos2; 5 samples -> cos0 cos1 sin1 cos2 sin2
27
28 autoVEC data = zero_VEC (numberOfFourierSamples);
29 if (numberOfChannels == 1) {
30 data.part (1, my nx) <<= my z.row (1);
31 //data.part (my nx + 1, numberOfFourierSamples) <<= 0.0; // superfluous, because they are already zero
32 } else {
33 /*
34 Multiple channels: take the average.
35 */
36 for (integer ichan = 1; ichan <= numberOfChannels; ichan ++)
37 data.part (1, my nx) += my z.row (ichan);
38 data.part (1, my nx) *= 1.0 / numberOfChannels;
39 }
40
41 autoNUMfft_Table fourierTable;
42 NUMfft_Table_init (& fourierTable, numberOfFourierSamples);
43 NUMfft_forward (& fourierTable, data.get());
44
45 autoSpectrum thee = Spectrum_create (0.5 / my dx, numberOfFrequencies);
46 thy dx = 1.0 / (my dx * numberOfFourierSamples); // override, just in case numberOfFourierSamples is odd
47 const VEC re = thy z.row (1);
48 const VEC im = thy z.row (2);
49 const double scaling = my dx;
50 re [1] = data [1] * scaling;
51 im [1] = 0.0;
52 for (integer i = 2; i < numberOfFrequencies; i ++) {
53 re [i] = data [i + i - 2] * scaling; // data [2], data [4], ...
54 im [i] = data [i + i - 1] * scaling; // data [3], data [5], ...
55 }
56 if ((numberOfFourierSamples & 1) != 0) {
57 if (numberOfFourierSamples > 1) {
58 re [numberOfFrequencies] = data [numberOfFourierSamples - 1] * scaling;
59 im [numberOfFrequencies] = data [numberOfFourierSamples] * scaling;
60 }
61 } else {
62 re [numberOfFrequencies] = data [numberOfFourierSamples] * scaling;
63 im [numberOfFrequencies] = 0.0;
64 }
65 return thee;
66 } catch (MelderError) {
67 Melder_throw (me, U": not converted to Spectrum.");
68 }
69 }
70
Spectrum_to_Sound(Spectrum me)71 autoSound Spectrum_to_Sound (Spectrum me) {
72 try {
73 const constVEC re = my z.row (1), im = my z.row (2);
74 const double lastFrequency = my x1 + (my nx - 1) * my dx;
75 const bool originalNumberOfSamplesProbablyOdd = ( im [my nx] != 0.0 || my xmax - lastFrequency > 0.25 * my dx );
76 if (my x1 != 0.0)
77 Melder_throw (U"A Fourier-transformable Spectrum must have a first frequency of 0 Hz, not ", my x1, U" Hz.");
78 const integer numberOfSamples = 2 * my nx - ( originalNumberOfSamplesProbablyOdd ? 1 : 2 );
79 autoSound thee = Sound_createSimple (1, 1.0 / my dx, numberOfSamples * my dx);
80 const VEC amp = thy z.row (1);
81 const double scaling = my dx;
82 amp [1] = re [1] * scaling;
83 for (integer i = 2; i < my nx; i ++) {
84 amp [i + i - 1] = re [i] * scaling;
85 amp [i + i] = im [i] * scaling;
86 }
87 if (originalNumberOfSamplesProbablyOdd) {
88 amp [numberOfSamples] = re [my nx] * scaling;
89 if (numberOfSamples > 1)
90 amp [2] = im [my nx] * scaling;
91 } else {
92 amp [2] = re [my nx] * scaling;
93 }
94 NUMrealft (amp, -1);
95 return thee;
96 } catch (MelderError) {
97 Melder_throw (me, U": not converted to Sound.");
98 }
99 }
100
Spectrum_lpcSmoothing(Spectrum me,int numberOfPeaks,double preemphasisFrequency)101 autoSpectrum Spectrum_lpcSmoothing (Spectrum me, int numberOfPeaks, double preemphasisFrequency) {
102 try {
103 const integer numberOfCoefficients = 2 * numberOfPeaks;
104
105 autoSound sound = Spectrum_to_Sound (me);
106 VECpreemphasize_f_inplace (sound -> z.row (1), sound -> dx, preemphasisFrequency);
107 autoVEC a = raw_VEC (numberOfCoefficients);
108 const double gain = VECburg (a.get(), sound -> z.row (1));
109 for (integer i = 1; i <= numberOfCoefficients; i ++)
110 a [i] = - a [i];
111 autoSpectrum thee = Data_copy (me);
112
113 const integer nfft = 2 * (thy nx - 1);
114 const integer ndata = Melder_clippedRight (numberOfCoefficients, nfft - 1);
115 const double scale = 10.0 * (gain > 0.0 ? sqrt (gain) : 1.0) / numberOfCoefficients;
116 autoVEC data = zero_VEC (nfft);
117 data [1] = 1.0;
118 for (integer i = 1; i <= ndata; i ++)
119 data [i + 1] = a [i];
120 NUMrealft (data.get(), 1);
121 const VEC re = thy z.row (1);
122 const VEC im = thy z.row (2);
123 re [1] = scale / data [1];
124 im [1] = 0.0;
125 const integer halfnfft = nfft / 2;
126 for (integer i = 2; i <= halfnfft; i ++) {
127 const double realPart = data [i + i - 1], imaginaryPart = data [i + i];
128 re [i] = scale / hypot (realPart, imaginaryPart) / (1.0 + thy dx * (i - 1) / preemphasisFrequency);
129 im [i] = 0.0;
130 }
131 re [halfnfft + 1] = scale / data [2] / (1.0 + thy dx * halfnfft / preemphasisFrequency);
132 im [halfnfft + 1] = 0.0;
133 return thee;
134 } catch (MelderError) {
135 Melder_throw (me, U": not smoothed.");
136 }
137 }
138
Sound_filter_formula(Sound me,conststring32 formula,Interpreter interpreter)139 autoSound Sound_filter_formula (Sound me, conststring32 formula, Interpreter interpreter) {
140 try {
141 autoSound thee = Data_copy (me);
142 if (my ny == 1) {
143 autoSpectrum spec = Sound_to_Spectrum (me, true);
144 Matrix_formula (spec.get(), formula, interpreter, nullptr);
145 autoSound him = Spectrum_to_Sound (spec.get());
146 thy z.row (1) <<= his z.row (1).part (1, thy nx);
147 } else {
148 for (integer ichan = 1; ichan <= my ny; ichan ++) {
149 autoSound channel = Sound_extractChannel (me, ichan);
150 autoSpectrum spec = Sound_to_Spectrum (channel.get(), true);
151 Matrix_formula (spec.get(), formula, interpreter, nullptr);
152 autoSound him = Spectrum_to_Sound (spec.get());
153 thy z.row (ichan) <<= his z.row (1).part (1, thy nx);
154 }
155 }
156 return thee;
157 } catch (MelderError) {
158 Melder_throw (me, U": not filtered (with formula).");
159 }
160 }
161
Sound_filter_passHannBand(Sound me,double fmin,double fmax,double smooth)162 autoSound Sound_filter_passHannBand (Sound me, double fmin, double fmax, double smooth) {
163 try {
164 autoSound thee = Data_copy (me);
165 if (my ny == 1) {
166 autoSpectrum spec = Sound_to_Spectrum (me, true);
167 Spectrum_passHannBand (spec.get(), fmin, fmax, smooth);
168 autoSound him = Spectrum_to_Sound (spec.get());
169 thy z.row (1) <<= his z.row (1).part (1, thy nx);
170 } else {
171 for (integer ichan = 1; ichan <= my ny; ichan ++) {
172 autoSound channel = Sound_extractChannel (me, ichan);
173 autoSpectrum spec = Sound_to_Spectrum (channel.get(), true);
174 Spectrum_passHannBand (spec.get(), fmin, fmax, smooth);
175 autoSound him = Spectrum_to_Sound (spec.get());
176 thy z.row (ichan) <<= his z.row (1).part (1, thy nx);
177 }
178 }
179 return thee;
180 } catch (MelderError) {
181 Melder_throw (me, U": not filtered (pass Hann band).");
182 }
183 }
184
Sound_filter_stopHannBand(Sound me,double fmin,double fmax,double smooth)185 autoSound Sound_filter_stopHannBand (Sound me, double fmin, double fmax, double smooth) {
186 try {
187 autoSound thee = Data_copy (me);
188 if (my ny == 1) {
189 autoSpectrum spec = Sound_to_Spectrum (me, true);
190 Spectrum_stopHannBand (spec.get(), fmin, fmax, smooth);
191 autoSound him = Spectrum_to_Sound (spec.get());
192 thy z.row (1) <<= his z.row (1).part (1, thy nx);
193 } else {
194 for (integer ichan = 1; ichan <= my ny; ichan ++) {
195 autoSound channel = Sound_extractChannel (me, ichan);
196 autoSpectrum spec = Sound_to_Spectrum (channel.get(), true);
197 Spectrum_stopHannBand (spec.get(), fmin, fmax, smooth);
198 autoSound him = Spectrum_to_Sound (spec.get());
199 thy z.row (ichan) <<= his z.row (1).part (1, thy nx);
200 }
201 }
202 return thee;
203 } catch (MelderError) {
204 Melder_throw (me, U": not filtered (stop Hann band).");
205 }
206 }
207
208 /* End of file Sound_and_Spectrum.cpp */
209