1 /* Spectrogram.cpp
2 *
3 * Copyright (C) 1992-2008,2011,2012,2015-2018,2020 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 "Spectrogram.h"
20
21 Thing_implement (Spectrogram, Matrix, 2);
22
v_info()23 void structSpectrogram :: v_info () {
24 structDaata :: v_info ();
25 MelderInfo_writeLine (U"Time domain:");
26 MelderInfo_writeLine (U" Start time: ", xmin, U" seconds");
27 MelderInfo_writeLine (U" End time: ", xmax, U" seconds");
28 MelderInfo_writeLine (U" Total duration: ", xmax - xmin, U" seconds");
29 MelderInfo_writeLine (U"Time sampling:");
30 MelderInfo_writeLine (U" Number of time slices (frames): ", nx);
31 MelderInfo_writeLine (U" Time step (frame distance): ", dx, U" seconds");
32 MelderInfo_writeLine (U" First time slice (frame centre) at: ", x1, U" seconds");
33 MelderInfo_writeLine (U"Frequency domain:");
34 MelderInfo_writeLine (U" Lowest frequency: ", ymin, U" Hz");
35 MelderInfo_writeLine (U" Highest frequency: ", ymax, U" Hz");
36 MelderInfo_writeLine (U" Total bandwidth: ", ymax - ymin, U" Hz");
37 MelderInfo_writeLine (U"Frequency sampling:");
38 MelderInfo_writeLine (U" Number of frequency bands (bins): ", ny);
39 MelderInfo_writeLine (U" Frequency step (bin width): ", dy, U" Hz");
40 MelderInfo_writeLine (U" First frequency band around (bin centre at): ", y1, U" Hz");
41 }
42
Spectrogram_create(double tmin,double tmax,integer nt,double dt,double t1,double fmin,double fmax,integer nf,double df,double f1)43 autoSpectrogram Spectrogram_create (double tmin, double tmax, integer nt, double dt, double t1,
44 double fmin, double fmax, integer nf, double df, double f1)
45 {
46 try {
47 autoSpectrogram me = Thing_new (Spectrogram);
48 Matrix_init (me.get(), tmin, tmax, nt, dt, t1, fmin, fmax, nf, df, f1);
49 return me;
50 } catch (MelderError) {
51 Melder_throw (U"Spectrogram not created.");
52 }
53 }
54
Spectrogram_paintInside(Spectrogram me,Graphics g,double tmin,double tmax,double fmin,double fmax,double maximum,int autoscaling,double dynamic,double preemphasis,double dynamicCompression)55 void Spectrogram_paintInside (Spectrogram me, Graphics g, double tmin, double tmax, double fmin, double fmax,
56 double maximum, int autoscaling, double dynamic, double preemphasis, double dynamicCompression)
57 {
58 Function_unidirectionalAutowindow (me, & tmin, & tmax);
59 if (fmax <= fmin) {
60 fmin = my ymin;
61 fmax = my ymax;
62 }
63 integer itmin, itmax, ifmin, ifmax;
64 const auto nt = Matrix_getWindowSamplesX (me, tmin - 0.49999 * my dx, tmax + 0.49999 * my dx, & itmin, & itmax);
65 const auto nf = Matrix_getWindowSamplesY (me, fmin - 0.49999 * my dy, fmax + 0.49999 * my dy, & ifmin, & ifmax);
66 if (nt == 0 || nf == 0)
67 return;
68 Graphics_setWindow (g, tmin, tmax, fmin, fmax);
69 auto preemphasisFactorBuffer = zero_VEC (nf);
70 double *preemphasisFactor = & preemphasisFactorBuffer [1 - ifmin];
71 auto dynamicFactorBuffer = zero_VEC (nt);
72 double *dynamicFactor = & dynamicFactorBuffer [1 - itmin];
73 /* Pre-emphasis in place; also compute maximum after pre-emphasis. */
74 for (integer ifreq = ifmin; ifreq <= ifmax; ifreq ++) {
75 preemphasisFactor [ifreq] = (preemphasis / NUMln2) * log (ifreq * my dy / 1000.0);
76 for (integer itime = itmin; itime <= itmax; itime ++) {
77 double value = my z [ifreq] [itime]; // power
78 value = (10.0/NUMln10) * log ((value + 1e-30) / 4.0e-10) + preemphasisFactor [ifreq]; // dB
79 if (value > dynamicFactor [itime])
80 dynamicFactor [itime] = value; // local maximum
81 my z [ifreq] [itime] = value;
82 }
83 }
84 /* Compute global maximum. */
85 if (autoscaling) {
86 maximum = 0.0;
87 for (integer itime = itmin; itime <= itmax; itime ++)
88 if (dynamicFactor [itime] > maximum)
89 maximum = dynamicFactor [itime];
90 }
91 /* Dynamic compression in place. */
92 for (integer itime = itmin; itime <= itmax; itime ++) {
93 dynamicFactor [itime] = dynamicCompression * (maximum - dynamicFactor [itime]);
94 for (integer ifreq = ifmin; ifreq <= ifmax; ifreq ++)
95 my z [ifreq] [itime] += dynamicFactor [itime];
96 }
97 Graphics_image (g, my z.part (ifmin, ifmax, itmin, itmax),
98 Matrix_columnToX (me, itmin - 0.5),
99 Matrix_columnToX (me, itmax + 0.5),
100 Matrix_rowToY (me, ifmin - 0.5),
101 Matrix_rowToY (me, ifmax + 0.5),
102 maximum - dynamic, maximum
103 );
104 for (integer ifreq = ifmin; ifreq <= ifmax; ifreq ++)
105 for (integer itime = itmin; itime <= itmax; itime ++) {
106 const double value = 4.0e-10 * exp ((my z [ifreq] [itime] - dynamicFactor [itime]
107 - preemphasisFactor [ifreq]) * (NUMln10 / 10.0)) - 1e-30;
108 my z [ifreq] [itime] = Melder_clippedLeft (0.0, value);
109 }
110 }
111
Spectrogram_paint(Spectrogram me,Graphics g,double tmin,double tmax,double fmin,double fmax,double maximum,int autoscaling,double dynamic,double preemphasis,double dynamicCompression,bool garnish)112 void Spectrogram_paint (Spectrogram me, Graphics g,
113 double tmin, double tmax, double fmin, double fmax, double maximum, int autoscaling,
114 double dynamic, double preemphasis, double dynamicCompression,
115 bool garnish)
116 {
117 Graphics_setInner (g);
118 Spectrogram_paintInside (me, g, tmin, tmax, fmin, fmax, maximum, autoscaling, dynamic, preemphasis, dynamicCompression);
119 Graphics_unsetInner (g);
120 if (garnish) {
121 Graphics_drawInnerBox (g);
122 Graphics_textBottom (g, true, U"Time (s)");
123 Graphics_marksBottom (g, 2, true, true, false);
124 Graphics_marksLeft (g, 2, true, true, false);
125 Graphics_textLeft (g, true, U"Frequency (Hz)");
126 }
127 }
128
Matrix_to_Spectrogram(Matrix me)129 autoSpectrogram Matrix_to_Spectrogram (Matrix me) {
130 try {
131 autoSpectrogram thee = Spectrogram_create (my xmin, my xmax, my nx, my dx, my x1, my ymin, my ymax, my ny, my dy, my y1);
132 thy z.all() <<= my z.all();
133 return thee;
134 } catch (MelderError) {
135 Melder_throw (me, U": not converted to Spectrogram.");
136 }
137 }
138
Spectrogram_to_Matrix(Spectrogram me)139 autoMatrix Spectrogram_to_Matrix (Spectrogram me) {
140 try {
141 autoMatrix thee = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, my ymin, my ymax, my ny, my dy, my y1);
142 thy z.all() <<= my z.all();
143 return thee;
144 } catch (MelderError) {
145 Melder_throw (me, U": not converted to Matrix.");
146 }
147 }
148
149 /* End of Spectrogram.cpp */
150