EEG_extensions.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. /* EEG_extensions.cpp
  2. *
  3. * Copyright (C) 2012-2017 David Weenink, 2015,2017,2018 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. #include "ICA.h"
  19. #include "EEG_extensions.h"
  20. #include "NUM2.h"
  21. #include "Sound_and_PCA.h"
  22. #include "Sound_extensions.h"
  23. #include "Spectrum_extensions.h"
  24. #include "Sound_and_MixingMatrix.h"
  25. #include "Sound_and_Spectrum.h"
  26. static autoEEG EEG_copyWithoutSound (EEG me) {
  27. try {
  28. autoEEG thee = EEG_create (my xmin, my xmax);
  29. thy numberOfChannels = my numberOfChannels;
  30. thy textgrid = Data_copy (my textgrid.get());
  31. thy channelNames = STRVECclone (my channelNames.get());
  32. return thee;
  33. } catch (MelderError) {
  34. Melder_throw (me, U": not copied.");
  35. }
  36. }
  37. static autoINTVEC EEG_channelNames_to_channelNumbers (EEG me, string32vector channelNames) {
  38. try {
  39. autoINTVEC channelNumbers = INTVECzero (channelNames.size);
  40. for (integer i = 1; i <= channelNames.size; i ++) {
  41. for (integer j = 1; j <= my numberOfChannels; j ++) {
  42. if (Melder_equ (channelNames [i], my channelNames [j].get())) {
  43. channelNumbers [i] = j;
  44. }
  45. }
  46. if (channelNumbers [i] == 0) {
  47. Melder_throw (U"Channel name \"", channelNames [i], U"\" not found.");
  48. }
  49. }
  50. return channelNumbers;
  51. } catch (MelderError) {
  52. Melder_throw (me, U": channelNames not found.");
  53. }
  54. }
  55. static void EEG_setChannelNames_selected (EEG me, conststring32 precursor, constINTVEC channelNumbers) {
  56. autoMelderString name;
  57. conststring32 zero = U"0";
  58. for (integer i = 1; i <= channelNumbers.size; i ++) {
  59. MelderString_copy (& name, precursor);
  60. if (my numberOfChannels > 100) {
  61. if (i < 10) {
  62. MelderString_append (& name, zero);
  63. }
  64. if (i < 100) {
  65. MelderString_append (& name, zero);
  66. }
  67. } else if (i < 10) {
  68. MelderString_append (& name, zero);
  69. }
  70. MelderString_append (& name, i);
  71. EEG_setChannelName (me, channelNumbers [i], name.string);
  72. }
  73. }
  74. autoCrossCorrelationTable EEG_to_CrossCorrelationTable (EEG me, double startTime, double endTime, double lagStep, conststring32 channelRanges)
  75. {
  76. try {
  77. // autowindow
  78. if (startTime == endTime) {
  79. startTime = my xmin;
  80. endTime = my xmax;
  81. }
  82. // don't allow times outside domain
  83. if (startTime < my xmin) {
  84. startTime = my xmin;
  85. }
  86. if (endTime > my xmax) {
  87. endTime = my xmax;
  88. }
  89. autoEEG thee = EEG_extractPart (me, startTime, endTime, true);
  90. autoINTVEC channels = NUMstring_getElementsOfRanges (channelRanges, thy numberOfChannels, U"channel", true);
  91. autoSound soundPart = Sound_copyChannelRanges (thy sound.get(), channelRanges);
  92. autoCrossCorrelationTable him = Sound_to_CrossCorrelationTable (soundPart.get(), startTime, endTime, lagStep);
  93. // assign channel names
  94. for (integer i = 1; i <= channels.size; i ++) {
  95. integer ichannel = channels [i];
  96. conststring32 label = my channelNames [ichannel].get();
  97. TableOfReal_setRowLabel (him.get(), i, label);
  98. TableOfReal_setColumnLabel (him.get(), i, label);
  99. }
  100. return him;
  101. } catch (MelderError) {
  102. Melder_throw (me, U": no CrossCorrelationTable calculated.");
  103. }
  104. }
  105. autoCovariance EEG_to_Covariance (EEG me, double startTime, double endTime, conststring32 channelRanges)
  106. {
  107. try {
  108. double lagStep = 0.0;
  109. autoCrossCorrelationTable thee = EEG_to_CrossCorrelationTable (me, startTime, endTime, lagStep, channelRanges);
  110. autoCovariance him = Thing_new (Covariance);
  111. thy structCrossCorrelationTable :: v_copy (him.get());
  112. return him;
  113. } catch (MelderError) {
  114. Melder_throw (me, U": no Covariance calculated.");
  115. }
  116. }
  117. autoCrossCorrelationTableList EEG_to_CrossCorrelationTableList (EEG me,
  118. double startTime, double endTime, integer numberOfCrossCorrelations, double lagStep, conststring32 channelRanges)
  119. {
  120. try {
  121. // autowindow
  122. if (startTime == endTime) {
  123. startTime = my xmin;
  124. endTime = my xmax;
  125. }
  126. // don't allow times outside domain
  127. if (startTime < my xmin) {
  128. startTime = my xmin;
  129. }
  130. if (endTime > my xmax) {
  131. endTime = my xmax;
  132. }
  133. autoEEG thee = EEG_extractPart (me, startTime, endTime, true);
  134. autoINTVEC channels = NUMstring_getElementsOfRanges (channelRanges, thy numberOfChannels, U"channel", true);
  135. autoSound soundPart = Sound_copyChannelRanges (thy sound.get(), channelRanges);
  136. autoCrossCorrelationTableList him = Sound_to_CrossCorrelationTableList (soundPart.get(),
  137. startTime, endTime, numberOfCrossCorrelations, lagStep);
  138. return him;
  139. } catch (MelderError) {
  140. Melder_throw (me, U": no CrossCorrelationTables calculated.");
  141. }
  142. }
  143. autoPCA EEG_to_PCA (EEG me, double startTime, double endTime, conststring32 channelRanges, int fromCorrelation) {
  144. try {
  145. autoCovariance cov = EEG_to_Covariance (me, startTime, endTime, channelRanges);
  146. autoPCA him;
  147. if (fromCorrelation) {
  148. autoCorrelation cor = SSCP_to_Correlation (cov.get());
  149. him = SSCP_to_PCA (cor.get());
  150. } else {
  151. him = SSCP_to_PCA (cov.get());
  152. }
  153. return him;
  154. } catch (MelderError) {
  155. Melder_throw (me, U": no PCA calculated.");
  156. }
  157. }
  158. autoEEG EEG_PCA_to_EEG_whiten (EEG me, PCA thee, integer numberOfComponents) {
  159. try {
  160. if (numberOfComponents <= 0 || numberOfComponents > thy numberOfEigenvalues) {
  161. numberOfComponents = thy numberOfEigenvalues;
  162. }
  163. numberOfComponents = ( numberOfComponents > my numberOfChannels ? my numberOfChannels : numberOfComponents );
  164. Melder_assert (thy labels.size == thy dimension);
  165. autoINTVEC channelNumbers = EEG_channelNames_to_channelNumbers (me, thy labels.get());
  166. autoEEG him = Data_copy (me);
  167. autoSound white = Sound_PCA_whitenSelectedChannels (my sound.get(), thee, numberOfComponents, channelNumbers.get());
  168. for (integer i = 1; i <= channelNumbers.size; i ++) {
  169. integer ichannel = channelNumbers [i];
  170. NUMvector_copyElements<double> (white -> z [i], his sound -> z [ichannel], 1, his sound -> nx);
  171. }
  172. EEG_setChannelNames_selected (him.get(), U"wh", channelNumbers.get());
  173. return him;
  174. } catch(MelderError) {
  175. Melder_throw (me, U": not whitened with ", thee);
  176. }
  177. }
  178. autoEEG EEG_PCA_to_EEG_principalComponents (EEG me, PCA thee, integer numberOfComponents) {
  179. try {
  180. if (numberOfComponents <= 0 || numberOfComponents > thy numberOfEigenvalues) {
  181. numberOfComponents = thy numberOfEigenvalues;
  182. }
  183. numberOfComponents = numberOfComponents > my numberOfChannels ? my numberOfChannels : numberOfComponents;
  184. Melder_assert (thy labels.size == thy dimension);
  185. autoINTVEC channelNumbers = EEG_channelNames_to_channelNumbers (me, thy labels.get());
  186. autoEEG him = Data_copy (me);
  187. autoSound pc = Sound_PCA_to_Sound_pc_selectedChannels (my sound.get(), thee, numberOfComponents, channelNumbers.get());
  188. for (integer i = 1; i <= channelNumbers.size; i ++) {
  189. integer ichannel = channelNumbers [i];
  190. NUMvector_copyElements<double> (pc -> z [i], his sound -> z [ichannel], 1, his sound -> nx);
  191. }
  192. EEG_setChannelNames_selected (him.get(), U"pc", channelNumbers.get());
  193. return him;
  194. } catch (MelderError) {
  195. Melder_throw (me, U": not projected.");
  196. }
  197. }
  198. void EEG_to_EEG_bss (EEG me, double startTime, double endTime, integer numberOfCrossCorrelations, double lagStep, conststring32 channelRanges,
  199. int whiteningMethod, int diagonalizerMethod, integer maxNumberOfIterations, double tol,
  200. autoEEG *p_resultingEEG, autoMixingMatrix *p_resultingMixingMatrix)
  201. {
  202. try {
  203. // autowindow
  204. if (startTime == endTime) {
  205. startTime = my xmin;
  206. endTime = my xmax;
  207. }
  208. // don't allow times outside domain
  209. if (startTime < my xmin) {
  210. startTime = my xmin;
  211. }
  212. if (endTime > my xmax) {
  213. endTime = my xmax;
  214. }
  215. autoINTVEC channelNumbers = NUMstring_getElementsOfRanges (channelRanges, my numberOfChannels, U"channel", true);
  216. autoEEG thee = EEG_extractPart (me, startTime, endTime, true);
  217. if (whiteningMethod != 0) {
  218. bool fromCorrelation = ( whiteningMethod == 2 );
  219. autoPCA pca = EEG_to_PCA (thee.get(), thy xmin, thy xmax, channelRanges, fromCorrelation);
  220. autoEEG white = EEG_PCA_to_EEG_whiten (thee.get(), pca.get(), 0);
  221. thee = white.move();
  222. }
  223. autoMixingMatrix mm = Sound_to_MixingMatrix (thy sound.get(),
  224. startTime, endTime, numberOfCrossCorrelations, lagStep,
  225. maxNumberOfIterations, tol, diagonalizerMethod);
  226. autoEEG him = EEG_copyWithoutSound (me);
  227. his sound = Sound_MixingMatrix_unmix (my sound.get(), mm.get());
  228. EEG_setChannelNames_selected (him.get(), U"ic", channelNumbers.get());
  229. // Calculate the cross-correlations between eye-channels and the ic's
  230. if (p_resultingEEG) *p_resultingEEG = thee.move();
  231. if (p_resultingMixingMatrix) *p_resultingMixingMatrix = mm.move();
  232. } catch (MelderError) {
  233. Melder_throw (me, U": no independent components determined.");
  234. }
  235. }
  236. autoSound EEG_to_Sound_modulated (EEG me, double baseFrequency, double channelBandwidth, conststring32 channelRanges) {
  237. try {
  238. autoINTVEC channelNumbers = NUMstring_getElementsOfRanges (channelRanges, my numberOfChannels, U"channel", true);
  239. double maxFreq = baseFrequency + my numberOfChannels * channelBandwidth;
  240. double samplingFrequency = 2.0 * maxFreq;
  241. samplingFrequency = samplingFrequency < 44100.0 ? 44100.0 : samplingFrequency;
  242. autoSound thee = Sound_createSimple (1, my xmax - my xmin, samplingFrequency);
  243. for (integer i = 1; i <= channelNumbers.size; i ++) {
  244. integer ichannel = channelNumbers [i];
  245. double fbase = baseFrequency;// + (ichannel - 1) * channelBandwidth;
  246. autoSound si = Sound_extractChannel (my sound.get(), ichannel);
  247. autoSpectrum spi = Sound_to_Spectrum (si.get(), true);
  248. Spectrum_passHannBand (spi.get(), 0.5, channelBandwidth - 0.5, 0.5);
  249. autoSpectrum spi_shifted = Spectrum_shiftFrequencies (spi.get(), fbase, samplingFrequency / 2.0, 30);
  250. autoSound resampled = Spectrum_to_Sound (spi_shifted.get());
  251. integer nx = resampled -> nx < thy nx ? resampled -> nx : thy nx;
  252. for (integer j = 1; j <= nx; j ++) {
  253. thy z [1] [j] += resampled -> z [1] [j];
  254. }
  255. }
  256. Vector_scale (thee.get(), 0.99);
  257. return thee;
  258. } catch (MelderError) {
  259. Melder_throw (me, U": no playable sound created.");
  260. }
  261. }
  262. autoSound EEG_to_Sound_frequencyShifted (EEG me, integer channel, double frequencyShift, double samplingFrequency, double maxAmp) {
  263. try {
  264. autoSound si = Sound_extractChannel (my sound.get(), channel);
  265. autoSpectrum spi = Sound_to_Spectrum (si.get(), true);
  266. autoSpectrum spi_shifted = Spectrum_shiftFrequencies (spi.get(), frequencyShift, samplingFrequency / 2.0, 30);
  267. autoSound thee = Spectrum_to_Sound (spi_shifted.get());
  268. if (maxAmp > 0) {
  269. Vector_scale (thee.get(), maxAmp);
  270. }
  271. return thee;
  272. } catch (MelderError) {
  273. Melder_throw (me, U": channel not converted to sound.");
  274. }
  275. }
  276. /* End of file EEG_extensions.cpp */