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