1 /* PitchTier.cpp
2  *
3  * Copyright (C) 1992-2008,2010-2013,2015-2018,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 "PitchTier.h"
20 
21 Thing_implement (PitchTier, RealTier, 0);
22 
v_info()23 void structPitchTier :: 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"Number of points: ", points.size);
30 	MelderInfo_writeLine (U"Minimum pitch value: ", RealTier_getMinimumValue (this), U" Hz");
31 	MelderInfo_writeLine (U"Maximum pitch value: ", RealTier_getMaximumValue (this), U" Hz");
32 }
33 
PitchTier_create(double tmin,double tmax)34 autoPitchTier PitchTier_create (double tmin, double tmax) {
35 	try {
36 		autoPitchTier me = Thing_new (PitchTier);
37 		RealTier_init (me.get(), tmin, tmax);
38 		return me;
39 	} catch (MelderError) {
40 		Melder_throw (U"PitchTier not created.");
41 	}
42 }
43 
PitchTier_draw(PitchTier me,Graphics g,double tmin,double tmax,double fmin,double fmax,bool garnish,conststring32 method)44 void PitchTier_draw (PitchTier me, Graphics g, double tmin, double tmax,
45 	double fmin, double fmax, bool garnish, conststring32 method)
46 {
47 	RealTier_draw (me, g, tmin, tmax, fmin, fmax, garnish, method, U"Frequency (Hz)");
48 }
49 
PointProcess_upto_PitchTier(PointProcess me,double frequency)50 autoPitchTier PointProcess_upto_PitchTier (PointProcess me, double frequency) {
51 	try {
52 		autoPitchTier thee = PointProcess_upto_RealTier (me, frequency, classPitchTier).static_cast_move<structPitchTier>();
53 		return thee;
54 	} catch (MelderError) {
55 		Melder_throw (me, U": not converted to PitchTier.");
56 	}
57 }
58 
PitchTier_stylize(PitchTier me,double frequencyResolution,bool useSemitones)59 void PitchTier_stylize (PitchTier me, double frequencyResolution, bool useSemitones) {
60 	for (;;) {
61 		integer imin = 0;
62 		double dfmin = 1e308;
63 		for (integer i = 2; i <= my points.size - 1; i ++) {
64 			RealPoint pm = my points.at [i];
65 			RealPoint pl = my points.at [i - 1];
66 			RealPoint pr = my points.at [i + 1];
67 			double expectedFrequency = pl -> value + (pr -> value - pl -> value) /
68 				 (pr -> number - pl -> number) * (pm -> number - pl -> number);
69 			double df = useSemitones ?
70 				12 * fabs (log (pm -> value / expectedFrequency)) / NUMln2:
71 				fabs (pm -> value - expectedFrequency);
72 			if (df < dfmin) {
73 				imin = i;
74 				dfmin = df;
75 			}
76 		}
77 		if (imin == 0 || dfmin > frequencyResolution) break;
78 		my points. removeItem (imin);
79 	}
80 }
81 
PitchTier_writeToSpreadsheetFile(PitchTier me,MelderFile file,bool hasHeader)82 static void PitchTier_writeToSpreadsheetFile (PitchTier me, MelderFile file, bool hasHeader) {
83 	autofile f = Melder_fopen (file, "w");
84 	if (hasHeader)
85 		fprintf (f, "\"ooTextFile\"\n\"PitchTier\"\n%s %s %s\n",
86 			Melder8_double (my xmin), Melder8_double (my xmax), Melder8_integer (my points.size));
87 	for (integer i = 1; i <= my points.size; i ++) {
88 		RealPoint point = my points.at [i];
89 		fprintf (f, "%.17g\t%.17g\n", point -> number, point -> value);
90 	}
91 	f.close (file);
92 }
93 
PitchTier_writeToPitchTierSpreadsheetFile(PitchTier me,MelderFile file)94 void PitchTier_writeToPitchTierSpreadsheetFile (PitchTier me, MelderFile file) {
95 	try {
96 		PitchTier_writeToSpreadsheetFile (me, file, true);
97 	} catch (MelderError) {
98 		Melder_throw (me, U" not written to tab-separated PitchTier file.");
99 	}
100 }
101 
PitchTier_writeToHeaderlessSpreadsheetFile(PitchTier me,MelderFile file)102 void PitchTier_writeToHeaderlessSpreadsheetFile (PitchTier me, MelderFile file) {
103 	try {
104 		PitchTier_writeToSpreadsheetFile (me, file, false);
105 	} catch (MelderError) {
106 		Melder_throw (me, U" not written to tab-separated table file.");
107 	}
108 }
109 
PitchTier_shiftFrequencies(PitchTier me,double tmin,double tmax,double shift,kPitch_unit unit)110 void PitchTier_shiftFrequencies (PitchTier me, double tmin, double tmax, double shift, kPitch_unit unit) {
111 	try {
112 		for (integer i = 1; i <= my points.size; i ++) {
113 			RealPoint point = my points.at [i];
114 			double frequency = point -> value;
115 			if (point -> number < tmin || point -> number > tmax) continue;
116 			switch (unit) {
117 				case kPitch_unit::HERTZ: {
118 					frequency += shift;
119 					if (frequency <= 0.0)
120 						Melder_throw (U"The resulting frequency has to be greater than 0 Hz.");
121 				} break; case kPitch_unit::MEL: {
122 					frequency = NUMhertzToMel (frequency) + shift;
123 					if (frequency <= 0.0)
124 						Melder_throw (U"The resulting frequency has to be greater than 0 mel.");
125 					frequency = NUMmelToHertz (frequency);
126 				} break; case kPitch_unit::LOG_HERTZ: {
127 					frequency = pow (10.0, log10 (frequency) + shift);
128 				} break; case kPitch_unit::SEMITONES_1: {
129 					frequency = NUMsemitonesToHertz (NUMhertzToSemitones (frequency) + shift);
130 				} break; case kPitch_unit::ERB: {
131 					frequency = NUMhertzToErb (frequency) + shift;
132 					if (frequency <= 0.0)
133 						Melder_throw (U"The resulting frequency has to be greater than 0 ERB.");
134 					frequency = NUMerbToHertz (frequency);
135 				}
136 			}
137 			point -> value = frequency;
138 		}
139 	} catch (MelderError) {
140 		Melder_throw (me, U": not all frequencies were shifted.");
141 	}
142 }
143 
PitchTier_multiplyFrequencies(PitchTier me,double tmin,double tmax,double factor)144 void PitchTier_multiplyFrequencies (PitchTier me, double tmin, double tmax, double factor) {
145 	Melder_assert (factor > 0.0);
146 	for (integer i = 1; i <= my points.size; i ++) {
147 		RealPoint point = my points.at [i];
148 		if (point -> number < tmin || point -> number > tmax) continue;
149 		point -> value *= factor;
150 	}
151 }
152 
RealTier_to_PitchTier(RealTier me)153 autoPitchTier RealTier_to_PitchTier (RealTier me) {
154 	try {
155 		autoPitchTier thee = Thing_new (PitchTier);
156 		my structRealTier :: v_copy (thee.get());
157 		return thee;
158 	} catch (MelderError) {
159 		Melder_throw (me, U": not converted to PitchTier.");
160 	}
161 }
162 
163 /* End of file PitchTier.cpp */
164