Pitch_to_PointProcess.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /* Pitch_to_PointProcess.cpp
  2. *
  3. * Copyright (C) 1992-2011,2014,2015,2016,2017 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. * pb 2002/07/16 GPL
  20. * pb 2003/02/26 Sound_Pitch_to_PointProcess_peaks
  21. * pb 2003/05/17 introduced silence threshold
  22. * pb 2003/05/20 removed bug in global peak
  23. * pb 2003/05/22 changed 1.2 to 1.25
  24. * pb 2004/05/11 undefined pitch is `undefined` rather than 0.0
  25. * pb 2004/11/01 Pitch_getVoicedIntervalAfter clips to my xmax
  26. * pb 2004/11/28 repaired memory leak in Pitch_to_PointProcess
  27. * pb 2004/11/28 truncated tleft in Pitch_getVoicedIntervalAfter to my xmin (otherwise, getValue can crash)
  28. * pb 2005/06/16 units
  29. * pb 2007/01/26 compatible with stereo sounds
  30. * pb 2008/01/19 double
  31. * pb 2011/06/04 C++
  32. */
  33. #include "Pitch_to_PointProcess.h"
  34. #include "PitchTier_to_PointProcess.h"
  35. #include "Pitch_to_PitchTier.h"
  36. #include <ctype.h>
  37. autoPointProcess Pitch_to_PointProcess (Pitch pitch) {
  38. try {
  39. autoPitchTier pitchTier = Pitch_to_PitchTier (pitch);
  40. autoPointProcess point = PitchTier_Pitch_to_PointProcess (pitchTier.get(), pitch);
  41. return point;
  42. } catch (MelderError) {
  43. Melder_throw (pitch, U": not converted to PointProcess.");
  44. }
  45. }
  46. static int Pitch_getVoicedIntervalAfter (Pitch me, double after, double *tleft, double *tright) {
  47. integer ileft = Sampled_xToHighIndex (me, after), iright;
  48. if (ileft > my nx) return 0; // offright
  49. if (ileft < 1) ileft = 1; // offleft
  50. /* Search for first voiced frame. */
  51. for (; ileft <= my nx; ileft ++)
  52. if (Pitch_isVoiced_i (me, ileft)) break;
  53. if (ileft > my nx) return 0; // offright
  54. /* Search for last voiced frame. */
  55. for (iright = ileft; iright <= my nx; iright ++)
  56. if (! Pitch_isVoiced_i (me, iright)) break;
  57. iright --;
  58. *tleft = Sampled_indexToX (me, ileft) - 0.5 * my dx; // the whole frame is considered voiced
  59. *tright = Sampled_indexToX (me, iright) + 0.5 * my dx;
  60. if (*tleft >= my xmax - 0.5 * my dx) return 0;
  61. if (*tleft < my xmin) *tleft = my xmin;
  62. if (*tright > my xmax) *tright = my xmax;
  63. if (*tright <= after) return 0;
  64. return 1;
  65. }
  66. static double findExtremum_3 (double *channel1_base, double *channel2_base, integer d, integer n, int includeMaxima, int includeMinima) {
  67. double *channel1 = channel1_base + d, *channel2 = channel2_base ? channel2_base + d : nullptr;
  68. int includeAll = includeMaxima == includeMinima;
  69. integer imin = 1, imax = 1, i, iextr;
  70. double minimum, maximum;
  71. if (n < 3) {
  72. if (n <= 0) return 0.0; // outside
  73. else if (n == 1) return 1.0;
  74. else { // n == 2
  75. double x1 = channel2 ? 0.5 * (channel1 [1] + channel2 [1]) : channel1 [1];
  76. double x2 = channel2 ? 0.5 * (channel1 [2] + channel2 [2]) : channel1 [2];
  77. double xleft = includeAll ? fabs (x1) : includeMaxima ? x1 : - x1;
  78. double xright = includeAll ? fabs (x2) : includeMaxima ? x2 : - x2;
  79. if (xleft > xright) return 1.0;
  80. else if (xleft < xright) return 2.0;
  81. else return 1.5;
  82. }
  83. }
  84. minimum = maximum = channel2 ? 0.5 * (channel1 [1] + channel2 [1]) : channel1 [1];
  85. for (i = 2; i <= n; i ++) {
  86. double value = channel2 ? 0.5 * (channel1 [i] + channel2 [i]) : channel1 [i];
  87. if (value < minimum) { minimum = value; imin = i; }
  88. if (value > maximum) { maximum = value; imax = i; }
  89. }
  90. if (minimum == maximum) {
  91. return 0.5 * (n + 1.0); // all equal
  92. }
  93. iextr = includeAll ? ( fabs (minimum) > fabs (maximum) ? imin : imax ) : includeMaxima ? imax : imin;
  94. if (iextr == 1) return 1.0;
  95. if (iextr == n) return (double) n;
  96. /* Parabolic interpolation. */
  97. /* We do NOT need fabs here: we look for a genuine extremum. */
  98. double valueMid = channel2 ? 0.5 * (channel1 [iextr] + channel2 [iextr]) : channel1 [iextr];
  99. double valueLeft = channel2 ? 0.5 * (channel1 [iextr - 1] + channel2 [iextr - 1]) : channel1 [iextr - 1];
  100. double valueRight = channel2 ? 0.5 * (channel1 [iextr + 1] + channel2 [iextr + 1]) : channel1 [iextr + 1];
  101. return iextr + 0.5 * (valueRight - valueLeft) / (2 * valueMid - valueLeft - valueRight);
  102. }
  103. static double Sound_findExtremum (Sound me, double tmin, double tmax, int includeMaxima, int includeMinima) {
  104. integer imin = Sampled_xToLowIndex (me, tmin), imax = Sampled_xToHighIndex (me, tmax);
  105. Melder_assert (isdefined (tmin));
  106. Melder_assert (isdefined (tmax));
  107. if (imin < 1) imin = 1;
  108. if (imax > my nx) imax = my nx;
  109. double iextremum = findExtremum_3 (my z [1], my ny > 1 ? my z [2] : nullptr, imin - 1, imax - imin + 1, includeMaxima, includeMinima);
  110. if (iextremum != 0.0)
  111. return my x1 + (imin - 1 + iextremum - 1) * my dx;
  112. else
  113. return (tmin + tmax) / 2;
  114. }
  115. static double Sound_findMaximumCorrelation (Sound me, double t1, double windowLength, double tmin2, double tmax2, double *tout, double *peak) {
  116. double maximumCorrelation = -1.0; // smart 'impossible' starting value
  117. double r1_best = undefined, r3_best = undefined, ir = undefined; // assignments not necessary, but extra safe
  118. double r1 = 0.0, r2 = 0.0, r3 = 0.0;
  119. double halfWindowLength = 0.5 * windowLength;
  120. integer ileft1 = Sampled_xToNearestIndex ((Sampled) me, t1 - halfWindowLength);
  121. integer iright1 = Sampled_xToNearestIndex ((Sampled) me, t1 + halfWindowLength);
  122. integer ileft2min = Sampled_xToLowIndex ((Sampled) me, tmin2 - halfWindowLength);
  123. integer ileft2max = Sampled_xToHighIndex ((Sampled) me, tmax2 - halfWindowLength);
  124. *peak = 0.0; // default
  125. Melder_assert (ileft2max >= ileft2min); // if the loop is never executed, the result will be garbage
  126. for (integer ileft2 = ileft2min; ileft2 <= ileft2max; ileft2 ++) {
  127. double norm1 = 0.0, norm2 = 0.0, product = 0.0, localPeak = 0.0;
  128. for (integer ichan = 1; ichan <= my ny; ichan ++) {
  129. for (integer i1 = ileft1, i2 = ileft2; i1 <= iright1; i1 ++, i2 ++) {
  130. if (i1 < 1 || i1 > my nx || i2 < 1 || i2 > my nx) continue;
  131. double amp1 = my z [ichan] [i1], amp2 = my z [ichan] [i2];
  132. norm1 += amp1 * amp1;
  133. norm2 += amp2 * amp2;
  134. product += amp1 * amp2;
  135. if (fabs (amp2) > localPeak)
  136. localPeak = fabs (amp2);
  137. }
  138. }
  139. r1 = r2; // >= 0
  140. r2 = r3; // >= 0
  141. r3 = ( product != 0.0 ? product / (sqrt (norm1 * norm2)) : 0.0 ); // >= 0
  142. if (r2 > maximumCorrelation /* true on first test */ && r2 >= r1 && r2 >= r3) {
  143. r1_best = r1;
  144. maximumCorrelation = r2;
  145. r3_best = r3;
  146. ir = ileft2 - 1;
  147. *peak = localPeak;
  148. }
  149. }
  150. /*
  151. * Improve the result by means of parabolic interpolation.
  152. */
  153. if (maximumCorrelation > -1.0) { // was maximumCorrelation ever assigned to?...
  154. // ...then r1_best and r3_best and ir must also have been assigned to:
  155. Melder_assert (isdefined (r1_best) && isdefined (r3_best) && isdefined (ir));
  156. double d2r = 2 * maximumCorrelation - r1_best - r3_best;
  157. if (d2r != 0.0) {
  158. double dr = 0.5 * (r3_best - r1_best);
  159. maximumCorrelation += 0.5 * dr * dr / d2r;
  160. ir += dr / d2r;
  161. }
  162. *tout = t1 + (ir - ileft1) * my dx;
  163. }
  164. return maximumCorrelation;
  165. }
  166. autoPointProcess Sound_Pitch_to_PointProcess_cc (Sound sound, Pitch pitch) {
  167. try {
  168. autoPointProcess point = PointProcess_create (sound -> xmin, sound -> xmax, 10);
  169. double t = pitch -> xmin;
  170. double addedRight = -1e308;
  171. double globalPeak = Vector_getAbsoluteExtremum (sound, sound -> xmin, sound -> xmax, 0), peak;
  172. /*
  173. * Cycle over all voiced intervals.
  174. */
  175. autoMelderProgress progress (U"Sound & Pitch: To PointProcess...");
  176. for (;;) {
  177. double tleft, tright;
  178. if (! Pitch_getVoicedIntervalAfter (pitch, t, & tleft, & tright)) break;
  179. Melder_assert (tright > t);
  180. /*
  181. * Go to the middle of the voice stretch.
  182. */
  183. double tmiddle = (tleft + tright) / 2;
  184. Melder_progress ((tmiddle - sound -> xmin) / (sound -> xmax - sound -> xmin), U"Sound & Pitch to PointProcess");
  185. double f0middle = Pitch_getValueAtTime (pitch, tmiddle, kPitch_unit::HERTZ, Pitch_LINEAR);
  186. /*
  187. * Our first point is near this middle.
  188. */
  189. if (isundef (f0middle)) {
  190. Melder_fatal (U"Sound_Pitch_to_PointProcess_cc:"
  191. U" tleft ", tleft,
  192. U", tright ", tright,
  193. U", f0middle ", f0middle
  194. );
  195. }
  196. double tmax = Sound_findExtremum (sound, tmiddle - 0.5 / f0middle, tmiddle + 0.5 / f0middle, true, true);
  197. Melder_assert (isdefined (tmax));
  198. PointProcess_addPoint (point.get(), tmax);
  199. double tsave = tmax;
  200. for (;;) {
  201. double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit::HERTZ, Pitch_LINEAR), correlation;
  202. if (isundef (f0)) break;
  203. correlation = Sound_findMaximumCorrelation (sound, tmax, 1.0 / f0, tmax - 1.25 / f0, tmax - 0.8 / f0, & tmax, & peak);
  204. if (correlation == -1) /*break*/ tmax -= 1.0 / f0; // this one period will drop out
  205. if (tmax < tleft) {
  206. if (correlation > 0.7 && peak > 0.023333 * globalPeak && tmax - addedRight > 0.8 / f0) {
  207. PointProcess_addPoint (point.get(), tmax);
  208. }
  209. break;
  210. }
  211. if (correlation > 0.3 && (peak == 0.0 || peak > 0.01 * globalPeak)) {
  212. if (tmax - addedRight > 0.8 / f0) { // do not fill in a short originally unvoiced interval twice
  213. PointProcess_addPoint (point.get(), tmax);
  214. }
  215. }
  216. }
  217. tmax = tsave;
  218. for (;;) {
  219. double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit::HERTZ, Pitch_LINEAR), correlation;
  220. if (isundef (f0)) break;
  221. correlation = Sound_findMaximumCorrelation (sound, tmax, 1.0 / f0, tmax + 0.8 / f0, tmax + 1.25 / f0, & tmax, & peak);
  222. if (correlation == -1) /*break*/ tmax += 1.0 / f0;
  223. if (tmax > tright) {
  224. if (correlation > 0.7 && peak > 0.023333 * globalPeak) {
  225. PointProcess_addPoint (point.get(), tmax);
  226. addedRight = tmax;
  227. }
  228. break;
  229. }
  230. if (correlation > 0.3 && (peak == 0.0 || peak > 0.01 * globalPeak)) {
  231. PointProcess_addPoint (point.get(), tmax);
  232. addedRight = tmax;
  233. }
  234. }
  235. t = tright;
  236. }
  237. return point;
  238. } catch (MelderError) {
  239. Melder_throw (sound, U" & ", pitch, U": not converted to PointProcess (cc).");
  240. }
  241. }
  242. autoPointProcess Sound_Pitch_to_PointProcess_peaks (Sound sound, Pitch pitch, int includeMaxima, int includeMinima) {
  243. try {
  244. autoPointProcess point = PointProcess_create (sound -> xmin, sound -> xmax, 10);
  245. double t = pitch -> xmin;
  246. double addedRight = -1e308;
  247. /*
  248. * Cycle over all voiced intervals.
  249. */
  250. autoMelderProgress progress (U"Sound & Pitch: To PointProcess");
  251. for (;;) {
  252. double tleft, tright;
  253. if (! Pitch_getVoicedIntervalAfter (pitch, t, & tleft, & tright)) break;
  254. /*
  255. * Go to the middle of the voiced interval.
  256. */
  257. double tmiddle = (tleft + tright) / 2;
  258. Melder_progress ((tmiddle - sound -> xmin) / (sound -> xmax - sound -> xmin), U"Sound & Pitch: To PointProcess");
  259. double f0middle = Pitch_getValueAtTime (pitch, tmiddle, kPitch_unit::HERTZ, Pitch_LINEAR);
  260. /*
  261. * Our first point is near this middle.
  262. */
  263. Melder_assert (isdefined (f0middle));
  264. double tmax = Sound_findExtremum (sound, tmiddle - 0.5 / f0middle, tmiddle + 0.5 / f0middle, includeMaxima, includeMinima);
  265. Melder_assert (isdefined (tmax));
  266. PointProcess_addPoint (point.get(), tmax);
  267. double tsave = tmax;
  268. for (;;) {
  269. double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit::HERTZ, Pitch_LINEAR);
  270. if (isundef (f0)) break;
  271. tmax = Sound_findExtremum (sound, tmax - 1.25 / f0, tmax - 0.8 / f0, includeMaxima, includeMinima);
  272. if (tmax < tleft) {
  273. if (tmax - addedRight > 0.8 / f0) {
  274. PointProcess_addPoint (point.get(), tmax);
  275. }
  276. break;
  277. }
  278. if (tmax - addedRight > 0.8 / f0) { // do not fill in a short originally unvoiced interval twice
  279. PointProcess_addPoint (point.get(), tmax);
  280. }
  281. }
  282. tmax = tsave;
  283. for (;;) {
  284. double f0 = Pitch_getValueAtTime (pitch, tmax, kPitch_unit::HERTZ, Pitch_LINEAR);
  285. if (isundef (f0)) break;
  286. tmax = Sound_findExtremum (sound, tmax + 0.8 / f0, tmax + 1.25 / f0, includeMaxima, includeMinima);
  287. if (tmax > tright) {
  288. PointProcess_addPoint (point.get(), tmax);
  289. addedRight = tmax;
  290. break;
  291. }
  292. PointProcess_addPoint (point.get(), tmax);
  293. addedRight = tmax;
  294. }
  295. t = tright;
  296. }
  297. return point;
  298. } catch (MelderError) {
  299. Melder_throw (sound, U" & ", pitch, U": not converted to PointProcess (peaks).");
  300. }
  301. }
  302. /* End of file Pitch_to_PointProcess.cpp */