ComplexSpectrogram.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /* ComplexSpectrogram.cpp
  2. *
  3. * Copyright (C) 2014-2015 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. #include "ComplexSpectrogram.h"
  19. #include "Sound_and_Spectrum.h"
  20. #include "oo_DESTROY.h"
  21. #include "ComplexSpectrogram_def.h"
  22. #include "oo_COPY.h"
  23. #include "ComplexSpectrogram_def.h"
  24. #include "oo_EQUAL.h"
  25. #include "ComplexSpectrogram_def.h"
  26. #include "oo_CAN_WRITE_AS_ENCODING.h"
  27. #include "ComplexSpectrogram_def.h"
  28. #include "oo_WRITE_TEXT.h"
  29. #include "ComplexSpectrogram_def.h"
  30. #include "oo_WRITE_BINARY.h"
  31. #include "ComplexSpectrogram_def.h"
  32. #include "oo_READ_TEXT.h"
  33. #include "ComplexSpectrogram_def.h"
  34. #include "oo_READ_BINARY.h"
  35. #include "ComplexSpectrogram_def.h"
  36. #include "oo_DESCRIPTION.h"
  37. #include "ComplexSpectrogram_def.h"
  38. Thing_implement (ComplexSpectrogram, Matrix, 2);
  39. autoComplexSpectrogram ComplexSpectrogram_create (double tmin, double tmax, integer nt, double dt,
  40. double t1, double fmin, double fmax, integer nf, double df, double f1) {
  41. try {
  42. autoComplexSpectrogram me = Thing_new (ComplexSpectrogram);
  43. Matrix_init (me.get(), tmin, tmax, nt, dt, t1, fmin, fmax, nf, df, f1);
  44. my phase = NUMmatrix <double> (1, my ny, 1, my nx);
  45. return me;
  46. } catch (MelderError) {
  47. Melder_throw (U"ComplexSpectrogram not created.");
  48. }
  49. }
  50. autoComplexSpectrogram Sound_to_ComplexSpectrogram (Sound me, double windowLength, double timeStep) {
  51. try {
  52. double samplingFrequency = 1.0 / my dx, myDuration = my xmax - my xmin, t1;
  53. Melder_require (windowLength <= myDuration,
  54. U"Your sound is too short:\nit should be at least as long as one window length.");
  55. integer nsamp_window = Melder_ifloor (windowLength / my dx);
  56. integer halfnsamp_window = nsamp_window / 2 - 1;
  57. nsamp_window = halfnsamp_window * 2;
  58. Melder_require (nsamp_window > 1,
  59. U"There should be atleast two samples in the window.");
  60. integer numberOfFrames;
  61. Sampled_shortTermAnalysis (me, windowLength, timeStep, & numberOfFrames, & t1);
  62. // Compute sampling of the spectrum
  63. integer numberOfFrequencies = halfnsamp_window + 1;
  64. double df = samplingFrequency / (numberOfFrequencies - 1);
  65. autoComplexSpectrogram thee = ComplexSpectrogram_create (my xmin, my xmax, numberOfFrames, timeStep, t1, 0.0, 0.5 * samplingFrequency, numberOfFrequencies, df, 0.0);
  66. //
  67. autoSound analysisWindow = Sound_create (1, 0.0, nsamp_window * my dx, nsamp_window, my dx, 0.5 * my dx);
  68. for (integer iframe = 1; iframe <= numberOfFrames; iframe ++) {
  69. double t = Sampled_indexToX (thee.get(), iframe);
  70. integer leftSample = Sampled_xToLowIndex (me, t), rightSample = leftSample + 1;
  71. integer startSample = rightSample - halfnsamp_window;
  72. integer endSample = leftSample + halfnsamp_window;
  73. Melder_assert (startSample >= 1);
  74. Melder_assert (endSample <= my nx);
  75. for (integer j = 1; j <= nsamp_window; j ++) {
  76. analysisWindow -> z [1] [j] = my z [1] [startSample - 1 + j];
  77. }
  78. // window ?
  79. autoSpectrum spec = Sound_to_Spectrum (analysisWindow.get(), false);
  80. thy z [1] [iframe] = spec -> z [1] [1] * spec -> z [1] [1];
  81. thy phase[1][iframe] = 0.0;
  82. for (integer ifreq = 2; ifreq <= numberOfFrequencies - 1; ifreq ++) {
  83. double x = spec -> z [1] [ifreq], y = spec -> z [2] [ifreq];
  84. thy z [ifreq] [iframe] = x * x + y * y; // power
  85. thy phase [ifreq] [iframe] = atan2 (y, x); // phase [-pi,+pi]
  86. }
  87. // even number of samples
  88. thy z [numberOfFrequencies] [iframe] = spec -> z [1] [numberOfFrequencies] * spec -> z [1][numberOfFrequencies];
  89. thy phase [numberOfFrequencies] [iframe] = 0.0;
  90. }
  91. return thee;
  92. } catch (MelderError) {
  93. Melder_throw (me, U": no ComplexSpectrogram created.");
  94. }
  95. }
  96. autoSound ComplexSpectrogram_to_Sound (ComplexSpectrogram me, double stretchFactor) {
  97. try {
  98. /* original number of samples is odd: imaginary part of last spectral value is zero ->
  99. * phase is either zero or +/-pi
  100. */
  101. double pi = atan2 (0.0, - 0.5);
  102. double samplingFrequency = 2.0 * my ymax;
  103. double lastFrequency = my y1 + (my ny - 1) * my dy, lastPhase = my phase[my ny][1];
  104. int originalNumberOfSamplesProbablyOdd = (lastPhase != 0.0 && lastPhase != pi && lastPhase != -pi) ||
  105. my ymax - lastFrequency > 0.25 * my dx;
  106. Melder_require (my y1 == 0.0,
  107. U"A Fourier-transformable ComplexSpectrogram should have a first frequency of 0 Hz, not ", my y1, U" Hz.");
  108. integer nsamp_window = 2 * my ny - (originalNumberOfSamplesProbablyOdd ? 1 : 2 );
  109. integer halfnsamp_window = nsamp_window / 2;
  110. double synthesisWindowDuration = nsamp_window / samplingFrequency;
  111. autoSpectrum spectrum = Spectrum_create (my ymax, my ny);
  112. autoSound synthesisWindow = Sound_createSimple (1, synthesisWindowDuration, samplingFrequency);
  113. double newDuration = (my xmax - my xmin) * stretchFactor;
  114. autoSound thee = Sound_createSimple (1, newDuration, samplingFrequency); //TODO
  115. double thyStartTime;
  116. for (integer iframe = 1; iframe <= my nx; iframe ++) {
  117. // "original" sound :
  118. double tmid = Sampled_indexToX (me, iframe);
  119. integer leftSample = Sampled_xToLowIndex (thee.get(), tmid);
  120. integer rightSample = leftSample + 1;
  121. integer startSample = rightSample - halfnsamp_window;
  122. double startTime = Sampled_indexToX (thee.get(), startSample);
  123. if (iframe == 1) {
  124. thyStartTime = Sampled_indexToX (thee.get(), startSample);
  125. }
  126. //integer endSample = leftSample + halfnsamp_window;
  127. // New Sound with stretch
  128. integer thyStartSample = Sampled_xToLowIndex (thee.get(),thyStartTime);
  129. double thyEndTime = thyStartTime + my dx * stretchFactor;
  130. integer thyEndSample = Sampled_xToLowIndex (thee.get(), thyEndTime);
  131. integer stretchedStepSizeSamples = thyEndSample - thyStartSample + 1;
  132. //double extraTime = (thyStartSample - startSample + 1) * thy dx;
  133. double extraTime = (thyStartTime - startTime);
  134. spectrum -> z[1][1] = sqrt (my z[1][iframe]);
  135. for (integer ifreq = 2; ifreq <= my ny; ifreq ++) {
  136. double f = my y1 + (ifreq - 1) * my dy;
  137. double a = sqrt (my z [ifreq] [iframe]);
  138. double phi = my phase [ifreq] [iframe], intPart;
  139. double extraPhase = 2.0 * pi * modf (extraTime * f, & intPart); // fractional part
  140. phi += extraPhase;
  141. spectrum -> z [1] [ifreq] = a * cos (phi);
  142. spectrum -> z [2] [ifreq] = a * sin (phi);
  143. }
  144. autoSound synthesis = Spectrum_to_Sound (spectrum.get());
  145. // Where should the sound be placed?
  146. integer thyEndSampleP = Melder_ifloor (fmin (thyStartSample + synthesis -> nx - 1, thyStartSample + stretchedStepSizeSamples - 1)); // guard against extreme stretches
  147. if (iframe == my nx) {
  148. thyEndSampleP = Melder_ifloor (fmin (thy nx, thyStartSample + synthesis -> nx - 1)); // ppgb: waarom naar beneden afgerond?
  149. }
  150. for (integer j = thyStartSample; j <= thyEndSampleP; j++) {
  151. thy z [1] [j] = synthesis -> z [1] [j - thyStartSample + 1];
  152. }
  153. thyStartTime += my dx * stretchFactor;
  154. }
  155. return thee;
  156. } catch (MelderError) {
  157. Melder_throw (me, U": no Sound created.");
  158. }
  159. }
  160. #if 0
  161. static autoSound ComplexSpectrogram_to_Sound2 (ComplexSpectrogram me, double stretchFactor) {
  162. try {
  163. /* original number of samples is odd: imaginary part of last spectral value is zero ->
  164. * phase is either zero or pi
  165. */
  166. double pi = atan2 (0.0, - 0.5);
  167. double samplingFrequency = 2.0 * my ymax;
  168. double lastFrequency = my y1 + (my ny - 1) * my dy;
  169. int originalNumberOfSamplesProbablyOdd = (my phase [my ny] [1] != 0.0 && my phase[my ny] [1] != pi) || my ymax - lastFrequency > 0.25 * my dx;
  170. if (my y1 != 0.0) {
  171. Melder_throw (U"A Fourier-transformable Spectrum must have a first frequency of 0 Hz, not ", my y1, U" Hz.");
  172. }
  173. integer numberOfSamples = 2 * my ny - (originalNumberOfSamplesProbablyOdd ? 1 : 2 );
  174. double synthesisWindowDuration = numberOfSamples / samplingFrequency;
  175. autoSpectrum spectrum = Spectrum_create (my ymax, my ny);
  176. autoSound synthesisWindow = Sound_createSimple (1, synthesisWindowDuration, samplingFrequency);
  177. integer stepSizeSamples = my dx * samplingFrequency * stretchFactor;
  178. double newDuration = (my xmax - my xmin) * stretchFactor + 0.05;
  179. autoSound thee = Sound_createSimple (1, newDuration, samplingFrequency); //TODO
  180. integer istart = 1, iend = istart + stepSizeSamples - 1;
  181. for (integer iframe = 1; iframe <= my nx; iframe++) {
  182. spectrum -> z[1][1] = sqrt (my z[1][iframe]);
  183. for (integer ifreq = 2; ifreq <= my ny; ifreq++) {
  184. double f = my y1 + (ifreq - 1) * my dy;
  185. double a = sqrt (my z[ifreq][iframe]);
  186. double phi = my phase[ifreq][iframe];
  187. double extraPhase = 2.0 * pi * (stretchFactor - 1.0) * my dx * f;
  188. phi += extraPhase;
  189. spectrum -> z[1][ifreq] = a * cos (phi);
  190. spectrum -> z[2][ifreq] = a * sin (phi);
  191. }
  192. autoSound synthesis = Spectrum_to_Sound (spectrum.get());
  193. for (integer j = istart; j <= iend; j++) {
  194. thy z[1][j] = synthesis -> z[1][j - istart + 1];
  195. }
  196. istart = iend + 1; iend = istart + stepSizeSamples - 1;
  197. }
  198. return thee;
  199. } catch (MelderError) {
  200. Melder_throw (me, U": no Sound created.");
  201. }
  202. }
  203. #endif
  204. autoSpectrogram ComplexSpectrogram_to_Spectrogram (ComplexSpectrogram me) {
  205. try {
  206. autoSpectrogram thee = Spectrogram_create (my xmin, my xmax, my nx, my dx, my x1, my ymin, my ymax, my ny, my dy, my y1);
  207. matrixcopy_preallocated (thy z.get(), my z.get());
  208. return thee;
  209. } catch (MelderError) {
  210. Melder_throw (me, U": not converted to Spectrogram.");
  211. }
  212. }
  213. autoSpectrum ComplexSpectrogram_to_Spectrum (ComplexSpectrogram me, double time) {
  214. try {
  215. integer iframe = Sampled_xToLowIndex (me, time); // ppgb: geen Sampled_xToIndex gebruiken voor integers (afrondingen altijd expliciet maken)
  216. iframe = iframe < 1 ? 1 : (iframe > my nx ? my nx : iframe);
  217. autoSpectrum thee = Spectrum_create (my ymax, my ny);
  218. for (integer ifreq = 1; ifreq <= my ny; ifreq ++) {
  219. double a = sqrt (my z [ifreq] [iframe]);
  220. double phi = my phase [ifreq] [iframe];
  221. thy z [1] [ifreq] = a * cos (phi);
  222. thy z [2] [ifreq] = a * sin (phi);
  223. }
  224. return thee;
  225. } catch (MelderError) {
  226. Melder_throw (me, U": no Spectrum created.");
  227. }
  228. }
  229. /* End of file ComplexSpectrogram.cpp */