Cepstrum.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. /* Cepstrum.cpp
  2. *
  3. * Copyright (C) 1994-2016 David Weenink
  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. See the GNU
  13. * 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. djmw 20010514
  20. djmw 20020812 GPL header
  21. djmw 20080122 Version 1: float -> double
  22. djmw 20110304 Thing_new
  23. */
  24. #include "Cepstrum.h"
  25. #include "NUM2.h"
  26. #include "Vector.h"
  27. Thing_implement (Cepstrum, Matrix, 2);
  28. Thing_implement (PowerCepstrum, Cepstrum, 2); // derives from Matrix therefore also version 2
  29. double structCepstrum :: v_getValueAtSample (integer isamp, integer which, int /* units */) {
  30. if (which == 0) {
  31. return z [1] [isamp];
  32. } else {
  33. // dB's
  34. return 20.0 * log10 (fabs (z [1] [isamp]) + 1e-30);
  35. }
  36. return undefined;
  37. }
  38. double structPowerCepstrum :: v_getValueAtSample (integer isamp, integer which, int /* units */) {
  39. if (which == 0) {
  40. return z [1] [isamp];
  41. } else {
  42. // dB's
  43. return 10.0 * log10 (z [1] [isamp] + 1e-30); // always positive
  44. }
  45. return undefined;
  46. }
  47. autoCepstrum Cepstrum_create (double qmax, integer nq) {
  48. try {
  49. autoCepstrum me = Thing_new (Cepstrum);
  50. double dq = qmax / (nq - 1);
  51. Matrix_init (me.get(), 0.0, qmax, nq, dq, 0.0, 1.0, 1.0, 1, 1, 1.0);
  52. return me;
  53. } catch (MelderError) {
  54. Melder_throw (U"Cepstrum not created.");
  55. }
  56. }
  57. autoPowerCepstrum Cepstrum_downto_PowerCepstrum (Cepstrum me ) {
  58. try {
  59. autoPowerCepstrum thee = PowerCepstrum_create (my xmax, my nx);
  60. for (integer i = 1; i <= my nx; i ++) {
  61. thy z [1] [i] = my z [1] [i] * my z [1] [i];
  62. }
  63. return thee;
  64. } catch (MelderError) {
  65. Melder_throw (me, U" not converted.");
  66. }
  67. }
  68. autoPowerCepstrum PowerCepstrum_create (double qmax, integer nq) {
  69. try {
  70. autoPowerCepstrum me = Thing_new (PowerCepstrum);
  71. double dq = qmax / (nq - 1);
  72. Matrix_init (me.get(), 0.0, qmax, nq, dq, 0.0, 1.0, 1.0, 1, 1, 1.0);
  73. return me;
  74. } catch (MelderError) {
  75. Melder_throw (U"PowerCepstrum not created.");
  76. }
  77. }
  78. static void _Cepstrum_draw (Cepstrum me, Graphics g, double qmin, double qmax, double minimum, double maximum, int power, int garnish) {
  79. int autoscaling = minimum >= maximum;
  80. Graphics_setInner (g);
  81. if (qmax <= qmin) {
  82. qmin = my xmin; qmax = my xmax;
  83. }
  84. integer imin, imax;
  85. if (! Matrix_getWindowSamplesX (me, qmin, qmax, & imin, & imax)) {
  86. return;
  87. }
  88. autoNUMvector<double> y (imin, imax);
  89. for (integer i = imin; i <= imax; i ++) {
  90. y [i] = my v_getValueAtSample (i, (power ? 1 : 0), 0);
  91. }
  92. if (autoscaling) {
  93. NUMvector_extrema (y.peek(), imin, imax, & minimum, & maximum);
  94. } else {
  95. for (integer i = imin; i <= imax; i ++) {
  96. if (y [i] > maximum) {
  97. y [i] = maximum;
  98. } else if (y [i] < minimum) {
  99. y [i] = minimum;
  100. }
  101. }
  102. }
  103. Graphics_setWindow (g, qmin, qmax, minimum, maximum);
  104. Graphics_function (g, y.peek(), imin, imax, Matrix_columnToX (me, imin), Matrix_columnToX (me, imax));
  105. Graphics_unsetInner (g);
  106. if (garnish) {
  107. Graphics_drawInnerBox (g);
  108. Graphics_textBottom (g, true, U"Quefrency (s)");
  109. Graphics_marksBottom (g, 2, true, true, false);
  110. Graphics_textLeft (g, true, power ? U"Amplitude (dB)" : U"Amplitude");
  111. Graphics_marksLeft (g, 2, true, true, false);
  112. }
  113. }
  114. void Cepstrum_drawLinear (Cepstrum me, Graphics g, double qmin, double qmax, double minimum, double maximum, int garnish) {
  115. _Cepstrum_draw (me, g, qmin, qmax, minimum, maximum, 0, garnish);
  116. }
  117. void PowerCepstrum_draw (PowerCepstrum me, Graphics g, double qmin, double qmax, double dBminimum, double dBmaximum, int garnish) {
  118. _Cepstrum_draw (me, g, qmin, qmax, dBminimum, dBmaximum, 1, garnish);
  119. }
  120. void PowerCepstrum_drawTiltLine (PowerCepstrum me, Graphics g, double qmin, double qmax, double dBminimum, double dBmaximum, double qstart, double qend, int lineType, int method) {
  121. Graphics_setInner (g);
  122. if (qmax <= qmin) {
  123. qmin = my xmin; qmax = my xmax;
  124. }
  125. if (dBminimum >= dBmaximum) { // autoscaling
  126. integer imin, imax;
  127. if (! Matrix_getWindowSamplesX (me, qmin, qmax, & imin, & imax)) {
  128. return;
  129. }
  130. integer numberOfPoints = imax - imin + 1;
  131. dBminimum = dBmaximum = my v_getValueAtSample (imin, 1, 0);
  132. for (integer i = 2; i <= numberOfPoints; i ++) {
  133. integer isamp = imin + i - 1;
  134. double y = my v_getValueAtSample (isamp, 1, 0);
  135. dBmaximum = y > dBmaximum ? y : dBmaximum;
  136. dBminimum = y < dBminimum ? y : dBminimum;
  137. }
  138. }
  139. Graphics_setWindow (g, qmin, qmax, dBminimum, dBmaximum);
  140. qend = qend == 0 ? my xmax : qend;
  141. if (qend <= qstart) {
  142. qend = my xmax; qstart = my xmin;
  143. }
  144. qstart = qstart < my xmin ? my xmin : qstart;
  145. qend = qend > my xmax ? my xmax : qend;
  146. double a, intercept;
  147. PowerCepstrum_fitTiltLine (me, qstart, qend, &a, &intercept, lineType, method);
  148. /*
  149. * Don't draw part outside window
  150. */
  151. double lineWidth = Graphics_inqLineWidth (g);
  152. Graphics_setLineWidth (g, 2);
  153. if (lineType == 2) {
  154. integer n = 500;
  155. double dq = (qend - qstart) / (n + 1);
  156. double q1 = qstart;
  157. if (qstart <= 0) {
  158. qstart = 0.1 * dq; // some small offset to avoid log(0)
  159. n--;
  160. }
  161. autoNUMvector<double> y (1, n);
  162. for (integer i = 1; i <= n; i ++) {
  163. double q = q1 + (i - 1) * dq;
  164. y [i] = a * log (q) + intercept;
  165. }
  166. Graphics_function (g, y.peek(), 1, n, qstart, qend);
  167. } else {
  168. double y1 = a * qstart + intercept, y2 = a * qend + intercept;
  169. if (y1 >= dBminimum && y2 >= dBminimum) {
  170. Graphics_line (g, qstart, y1, qend, y2);
  171. } else if (y1 < dBminimum) {
  172. qstart = (dBminimum - intercept) / a;
  173. Graphics_line (g, qstart, dBminimum, qend, y2);
  174. } else if (y2 < dBminimum) {
  175. qend = (dBminimum - intercept) / a;
  176. Graphics_line (g, qstart, y1, qend, dBminimum);
  177. } else {
  178. // don't draw anything below lower limit?
  179. }
  180. }
  181. Graphics_setLineWidth (g, lineWidth);
  182. Graphics_unsetInner (g);
  183. }
  184. /* Fit line y = ax+b (lineType ==1) or y = a log(x) + b (lineType == 2) on interval [qmin,qmax]
  185. * method == 1 : Least squares fit
  186. * method == 2 : Theil's partial robust fit
  187. */
  188. void PowerCepstrum_fitTiltLine (PowerCepstrum me, double qmin, double qmax, double *p_a, double *p_intercept, int lineType, int method) {
  189. try {
  190. double a, intercept;
  191. if (qmax <= qmin) {
  192. qmin = my xmin; qmax = my xmax;
  193. }
  194. integer imin, imax;
  195. if (! Matrix_getWindowSamplesX (me, qmin, qmax, & imin, & imax)) {
  196. return;
  197. }
  198. imin = (lineType == 2 && imin == 1) ? 2 : imin; // log(0) is undefined!
  199. integer numberOfPoints = imax - imin + 1;
  200. if (numberOfPoints < 2) {
  201. Melder_throw (U"Not enough points for fit.");
  202. }
  203. autoNUMvector<double> y (1, numberOfPoints);
  204. autoNUMvector<double> x (1, numberOfPoints);
  205. for (integer i = 1; i <= numberOfPoints; i ++) {
  206. integer isamp = imin + i - 1;
  207. x [i] = my x1 + (isamp - 1) * my dx;
  208. if (lineType == 2) {
  209. x [i] = log (x [i]);
  210. }
  211. y [i] = my v_getValueAtSample (isamp, 1, 0);
  212. }
  213. if (method == 3) { // try local maxima first
  214. autoNUMvector<double> ym (1, numberOfPoints / 2 + 1);
  215. autoNUMvector<double> xm (1, numberOfPoints / 2 + 1);
  216. integer numberOfLocalPeaks = 0;
  217. // forget y [1] if y [2]<y [1] and y [n] if y [n-1]<y [n] !
  218. for (integer i = 2; i <= numberOfPoints; i ++) {
  219. if (y [i - 1] <= y [i] && y [i] > y [i + 1]) {
  220. ym [++ numberOfLocalPeaks] = y [i];
  221. xm [numberOfLocalPeaks] = x [i];
  222. }
  223. }
  224. if (numberOfLocalPeaks > numberOfPoints / 10) {
  225. for (integer i = 1; i <= numberOfLocalPeaks; i ++) {
  226. x [i] = xm [i]; y [i] = ym [i];
  227. }
  228. numberOfPoints = numberOfLocalPeaks;
  229. }
  230. method = 2; // robust fit of peaks
  231. }
  232. // fit a straight line through (x,y)'s
  233. NUMlineFit (x.peek(), y.peek(), numberOfPoints, & a, & intercept, method);
  234. if (p_intercept) { *p_intercept = intercept; }
  235. if (p_a) { *p_a = a; }
  236. } catch (MelderError) {
  237. Melder_throw (me, U": couldn't fit a line.");
  238. }
  239. }
  240. #if 0
  241. // Hillenbrand subtracts dB values and if the result is negative it is made zero
  242. static void PowerCepstrum_subtractTiltLine_inline2 (PowerCepstrum me, double slope, double intercept, int lineType) {
  243. for (integer j = 1; j <= my nx; j ++) {
  244. double q = my x1 + (j - 1) * my dx;
  245. q = j == 1 ? 0.5 * my dx : q; // approximation
  246. double xq = lineType == 2 ? log(q) : q;
  247. double db_background = slope * xq + intercept;
  248. double db_cepstrum = my v_getValueAtSample (j, 1, 0);
  249. double diff = exp ((db_cepstrum - db_background) * NUMln10 / 10) - 1e-30;
  250. my z [1] [j] = diff;
  251. }
  252. }
  253. #endif
  254. // clip with tilt line
  255. static void PowerCepstrum_subtractTiltLine_inplace (PowerCepstrum me, double slope, double intercept, int lineType) {
  256. for (integer j = 1; j <= my nx; j ++) {
  257. double q = my x1 + (j - 1) * my dx;
  258. q = j == 1 ? 0.5 * my dx : q; // approximation
  259. double xq = lineType == 2 ? log(q) : q;
  260. double db_background = slope * xq + intercept;
  261. double db_cepstrum = my v_getValueAtSample (j, 1, 0);
  262. double diff = db_cepstrum - db_background;
  263. if (diff < 0) {
  264. diff = 0;
  265. }
  266. my z [1] [j] = exp (diff * NUMln10 / 10.0) - 1e-30;
  267. }
  268. }
  269. void PowerCepstrum_subtractTilt_inplace (PowerCepstrum me, double qstartFit, double qendFit, int lineType, int fitMethod) {
  270. double slope, intercept;
  271. PowerCepstrum_fitTiltLine (me, qstartFit, qendFit, &slope, &intercept, lineType, fitMethod);
  272. PowerCepstrum_subtractTiltLine_inplace (me, slope, intercept, lineType);
  273. }
  274. autoPowerCepstrum PowerCepstrum_subtractTilt (PowerCepstrum me, double qstartFit, double qendFit, int lineType, int fitMethod) {
  275. try {
  276. autoPowerCepstrum thee = Data_copy (me);
  277. PowerCepstrum_subtractTilt_inplace (thee.get(), qstartFit, qendFit, lineType, fitMethod);
  278. return thee;
  279. } catch (MelderError) {
  280. Melder_throw (me, U": couldn't subtract tilt line.");
  281. }
  282. }
  283. void PowerCepstrum_smooth_inplace (PowerCepstrum me, double quefrencyAveragingWindow, integer numberOfIterations) {
  284. try {
  285. integer numberOfQuefrencyBins = Melder_ifloor (quefrencyAveragingWindow / my dx);
  286. if (numberOfQuefrencyBins > 1) {
  287. autoVEC qin = VECcopy (my z.row(1));
  288. autoVEC qout = VECraw (my nx);
  289. double *xin, *xout;
  290. for (integer k = 1; k <= numberOfIterations; k ++)
  291. if (k % 2 == 1)
  292. VECsmoothByMovingAverage_preallocated (qout.get(), qin.get(), numberOfQuefrencyBins);
  293. else
  294. VECsmoothByMovingAverage_preallocated (qin.get(), qout.get(), numberOfQuefrencyBins);
  295. VECcopy_preallocated (my z.row(1), numberOfIterations % 2 == 1 ? qout.get() : qin.get());
  296. }
  297. } catch (MelderError) {
  298. Melder_throw (me, U": not smoothed.");
  299. }
  300. }
  301. autoPowerCepstrum PowerCepstrum_smooth (PowerCepstrum me, double quefrencyAveragingWindow, integer numberOfIterations) {
  302. autoPowerCepstrum thee = Data_copy (me);
  303. PowerCepstrum_smooth_inplace (thee.get(), quefrencyAveragingWindow, numberOfIterations);
  304. return thee;
  305. }
  306. void PowerCepstrum_getMaximumAndQuefrency (PowerCepstrum me, double pitchFloor, double pitchCeiling, int interpolation, double *p_peakdB, double *p_quefrency) {
  307. double peakdB, quefrency;
  308. autoPowerCepstrum thee = Data_copy (me);
  309. double lowestQuefrency = 1.0 / pitchCeiling, highestQuefrency = 1.0 / pitchFloor;
  310. for (integer i = 1; i <= my nx; i ++) {
  311. thy z [1] [i] = my v_getValueAtSample (i, 1, 0); // 10 log val^2
  312. }
  313. Vector_getMaximumAndX ((Vector) thee.get(), lowestQuefrency, highestQuefrency, 1, interpolation, & peakdB, & quefrency); // FIXME cast
  314. if (p_peakdB) {
  315. *p_peakdB = peakdB;
  316. }
  317. if (p_quefrency) {
  318. *p_quefrency = quefrency;
  319. }
  320. }
  321. #if 0
  322. static void Cepstrum_getZ (Cepstrum me, integer imin, integer imax, double peakdB, double slope, double intercept, int lineType, double *z) {
  323. integer ndata = imax - imin + 1;
  324. autoNUMvector<double> dabs (1, ndata);
  325. for (integer i = imin; i <= imax; i ++) {
  326. double q = my x1 + (i - 1) * my dx;
  327. q = ( i == 1 ? 0.5 * my dx : q ); // approximation
  328. double xq = ( lineType == 2 ? log (q) : q );
  329. double db_background = slope * xq + intercept;
  330. double db_cepstrum = my v_getValueAtSample (i, 1, 0);
  331. double diff = exp ((db_cepstrum - db_background) * NUMln10 / 10.0) - 1e-30;
  332. //double diff = fabs (db_cepstrum - db_background);
  333. dabs [i - imin + 1] = diff;
  334. }
  335. double q50 = NUMquantile (ndata, dabs.peek(), 0.5);
  336. double peak = exp (peakdB * NUMln10 / 10.0) - 1e-30;
  337. if (z) {
  338. *z = peak / q50;
  339. }
  340. }
  341. #endif
  342. double PowerCepstrum_getRNR (PowerCepstrum me, double pitchFloor, double pitchCeiling, double f0fractionalWidth) {
  343. double rnr = undefined;
  344. double qmin = 1.0 / pitchCeiling, qmax = 1.0 / pitchFloor, peakdB, qpeak;
  345. PowerCepstrum_getMaximumAndQuefrency (me, pitchFloor, pitchCeiling, 2, &peakdB, &qpeak);
  346. integer imin, imax;
  347. if (! Matrix_getWindowSamplesX (me, qmin, qmax, & imin, & imax)) {
  348. return rnr;
  349. }
  350. integer ndata = imax - imin + 1;
  351. if (ndata < 2) {
  352. return rnr;
  353. }
  354. // how many peaks in interval ?
  355. integer npeaks = 2;
  356. while (qpeak > 0 && qpeak * npeaks <= qmax) { npeaks++; }
  357. npeaks--;
  358. double sum = 0, sumr = 0;
  359. for (integer i = imin; i <= imax; i ++) {
  360. double val = my v_getValueAtSample (i, 0, 0);
  361. double qx = my x1 + (i - 1) * my dx;
  362. sum += val;
  363. // is qx within an interval around a multiple of the peak's q ?
  364. for (integer j = 1; j <= npeaks; j ++) {
  365. double f0c = 1.0 / (j * qpeak);
  366. double f0clow = f0c * (1.0 - f0fractionalWidth);
  367. double f0chigh = f0c * (1.0 + f0fractionalWidth);
  368. double qclow = 1.0 / f0chigh;
  369. double qchigh = ( f0fractionalWidth >= 1 ? qmax : 1.0 / f0clow );
  370. if (qx >= qclow && qx <= qchigh) { // yes in rahmonic interval
  371. sumr += val;
  372. break;
  373. }
  374. }
  375. }
  376. rnr = sumr >= sum ? 1000000 : sumr / (sum - sumr);
  377. return rnr;
  378. }
  379. double PowerCepstrum_getPeakProminence_hillenbrand (PowerCepstrum me, double pitchFloor, double pitchCeiling, double *qpeak) {
  380. double slope, intercept, quefrency, peakdB;
  381. PowerCepstrum_fitTiltLine (me, 0.001, 0, &slope, &intercept, 1, 1);
  382. autoPowerCepstrum thee = Data_copy (me);
  383. PowerCepstrum_subtractTiltLine_inplace (thee.get(), slope, intercept, 1);
  384. PowerCepstrum_getMaximumAndQuefrency (thee.get(), pitchFloor, pitchCeiling, 0, & peakdB, & quefrency);
  385. if (qpeak) {
  386. *qpeak = quefrency;
  387. }
  388. return peakdB;
  389. }
  390. double PowerCepstrum_getPeakProminence (PowerCepstrum me, double pitchFloor, double pitchCeiling, int interpolation, double qstartFit, double qendFit, int lineType, int fitMethod, double *p_qpeak) {
  391. double slope, intercept, qpeak, peakdB;
  392. PowerCepstrum_fitTiltLine (me, qstartFit, qendFit, &slope, &intercept, lineType, fitMethod);
  393. PowerCepstrum_getMaximumAndQuefrency (me, pitchFloor, pitchCeiling, interpolation, & peakdB, & qpeak);
  394. double xq = lineType == 2 ? log(qpeak) : qpeak;
  395. double db_background = slope * xq + intercept;
  396. double cpp = peakdB - db_background;
  397. if (p_qpeak) {
  398. *p_qpeak = qpeak;
  399. }
  400. return cpp;
  401. }
  402. autoMatrix PowerCepstrum_to_Matrix (PowerCepstrum me) {
  403. try {
  404. autoMatrix thee = Thing_new (Matrix);
  405. my structMatrix :: v_copy (thee.get());
  406. return thee;
  407. } catch (MelderError) {
  408. Melder_throw (me, U": no Matrix created.");
  409. }
  410. }
  411. autoPowerCepstrum Matrix_to_PowerCepstrum (Matrix me) {
  412. try {
  413. if (my ny != 1) {
  414. Melder_throw (U"Matrix should have exactly 1 row.");
  415. }
  416. autoPowerCepstrum thee = Thing_new (PowerCepstrum);
  417. my structMatrix :: v_copy (thee.get());
  418. return thee;
  419. } catch (MelderError) {
  420. Melder_throw (me, U": not converted to PowerCepstrum.");
  421. }
  422. }
  423. autoPowerCepstrum Matrix_to_PowerCepstrum_row (Matrix me, integer row) {
  424. try {
  425. autoPowerCepstrum thee = PowerCepstrum_create (my xmax, my nx);
  426. if (row < 1 || row > my ny) {
  427. Melder_throw (U"Row number should be between 1 and ", my ny, U" inclusive.");
  428. }
  429. NUMvector_copyElements (my z [row], thy z [1], 1, my nx);
  430. return thee;
  431. } catch (MelderError) {
  432. Melder_throw (me, U": no PowerCepstrum created.");
  433. }
  434. }
  435. autoPowerCepstrum Matrix_to_PowerCepstrum_column (Matrix me, integer col) {
  436. try {
  437. autoPowerCepstrum thee = PowerCepstrum_create (my ymax, my ny);
  438. if (col < 1 || col > my nx) {
  439. Melder_throw (U"Column number should be between 1 and ", my nx, U" inclusive.");
  440. }
  441. for (integer i = 1; i <= my ny; i ++) {
  442. thy z [1] [i] = my z [i] [col];
  443. }
  444. return thee;
  445. } catch (MelderError) {
  446. Melder_throw (me, U": no PowerCepstrum created.");
  447. }
  448. }
  449. /* End of file Cepstrum.cpp */