ERPTier.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /* ERPTier.cpp
  2. *
  3. * Copyright (C) 2011-2012,2014-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 "ERPTier.h"
  19. #include "oo_DESTROY.h"
  20. #include "ERPTier_def.h"
  21. #include "oo_COPY.h"
  22. #include "ERPTier_def.h"
  23. #include "oo_EQUAL.h"
  24. #include "ERPTier_def.h"
  25. #include "oo_CAN_WRITE_AS_ENCODING.h"
  26. #include "ERPTier_def.h"
  27. #include "oo_WRITE_TEXT.h"
  28. #include "ERPTier_def.h"
  29. #include "oo_READ_TEXT.h"
  30. #include "ERPTier_def.h"
  31. #include "oo_WRITE_BINARY.h"
  32. #include "ERPTier_def.h"
  33. #include "oo_READ_BINARY.h"
  34. #include "ERPTier_def.h"
  35. #include "oo_DESCRIPTION.h"
  36. #include "ERPTier_def.h"
  37. /***** ERPPoint *****/
  38. Thing_implement (ERPPoint, AnyPoint, 0);
  39. /***** ERPTier *****/
  40. Thing_implement (ERPTier, AnyTier, 0);
  41. integer ERPTier_getChannelNumber (ERPTier me, conststring32 channelName) {
  42. for (integer ichan = 1; ichan <= my numberOfChannels; ichan ++) {
  43. if (Melder_equ (my channelNames [ichan].get(), channelName))
  44. return ichan;
  45. }
  46. return 0;
  47. }
  48. double ERPTier_getMean (ERPTier me, integer pointNumber, integer channelNumber, double tmin, double tmax) {
  49. if (pointNumber < 1 || pointNumber > my points.size) return undefined;
  50. if (channelNumber < 1 || channelNumber > my numberOfChannels) return undefined;
  51. ERPPoint point = my points.at [pointNumber];
  52. return Vector_getMean (point -> erp.get(), tmin, tmax, channelNumber);
  53. }
  54. double ERPTier_getMean (ERPTier me, integer pointNumber, conststring32 channelName, double tmin, double tmax) {
  55. return ERPTier_getMean (me, pointNumber, ERPTier_getChannelNumber (me, channelName), tmin, tmax);
  56. }
  57. static autoERPTier EEG_PointProcess_to_ERPTier (EEG me, PointProcess events, double fromTime, double toTime) {
  58. try {
  59. autoERPTier thee = Thing_new (ERPTier);
  60. Function_init (thee.get(), fromTime, toTime);
  61. thy numberOfChannels = my numberOfChannels - EEG_getNumberOfExtraSensors (me);
  62. Melder_assert (thy numberOfChannels > 0);
  63. thy channelNames = STRVECclone (my channelNames.get());
  64. integer numberOfEvents = events -> nt;
  65. double soundDuration = toTime - fromTime;
  66. double samplingPeriod = my sound -> dx;
  67. integer numberOfSamples = Melder_ifloor (soundDuration / samplingPeriod) + 1;
  68. if (numberOfSamples < 1)
  69. Melder_throw (U"Time window too short.");
  70. double midTime = 0.5 * (fromTime + toTime);
  71. double soundPhysicalDuration = numberOfSamples * samplingPeriod;
  72. double firstTime = midTime - 0.5 * soundPhysicalDuration + 0.5 * samplingPeriod; // distribute the samples evenly over the time domain
  73. for (integer ievent = 1; ievent <= numberOfEvents; ievent ++) {
  74. double eegEventTime = events -> t [ievent];
  75. autoERPPoint event = Thing_new (ERPPoint);
  76. event -> number = eegEventTime;
  77. event -> erp = Sound_create (thy numberOfChannels, fromTime, toTime, numberOfSamples, samplingPeriod, firstTime);
  78. double erpEventTime = 0.0;
  79. double eegSample = 1 + (eegEventTime - my sound -> x1) / samplingPeriod;
  80. double erpSample = 1 + (erpEventTime - firstTime) / samplingPeriod;
  81. integer sampleDifference = Melder_iround (eegSample - erpSample);
  82. for (integer ichannel = 1; ichannel <= thy numberOfChannels; ichannel ++) {
  83. for (integer isample = 1; isample <= numberOfSamples; isample ++) {
  84. integer jsample = isample + sampleDifference;
  85. event -> erp -> z [ichannel] [isample] = jsample < 1 || jsample > my sound -> nx ? 0.0 : my sound -> z [ichannel] [jsample];
  86. }
  87. }
  88. thy points. addItem_move (event.move());
  89. }
  90. return thee;
  91. } catch (MelderError) {
  92. Melder_throw (me, U": ERP analysis not performed.");
  93. }
  94. }
  95. autoERPTier EEG_to_ERPTier_bit (EEG me, double fromTime, double toTime, int markerBit) {
  96. try {
  97. autoPointProcess events = TextGrid_getStartingPoints (my textgrid.get(), markerBit, kMelder_string::EQUAL_TO, U"1");
  98. autoERPTier thee = EEG_PointProcess_to_ERPTier (me, events.get(), fromTime, toTime);
  99. return thee;
  100. } catch (MelderError) {
  101. Melder_throw (me, U": ERPTier not created.");
  102. }
  103. }
  104. static autoPointProcess TextGrid_getStartingPoints_multiNumeric (TextGrid me, uint16 number) {
  105. try {
  106. autoPointProcess thee;
  107. int numberOfBits = my tiers->size;
  108. for (int ibit = 0; ibit < numberOfBits; ibit ++) {
  109. (void) TextGrid_checkSpecifiedTierIsIntervalTier (me, ibit + 1);
  110. if (number & (1 << ibit)) {
  111. autoPointProcess bitEvents = TextGrid_getStartingPoints (me, ibit + 1, kMelder_string::EQUAL_TO, U"1");
  112. if (thee) {
  113. thee = PointProcesses_intersection (thee.get(), bitEvents.get());
  114. } else {
  115. thee = bitEvents.move();
  116. }
  117. }
  118. }
  119. for (int ibit = 0; ibit < numberOfBits; ibit ++) {
  120. autoPointProcess bitEvents = TextGrid_getStartingPoints (me, ibit + 1, kMelder_string::EQUAL_TO, U"1");
  121. if (! (number & (1 << ibit))) {
  122. if (thee) {
  123. thee = PointProcesses_difference (thee.get(), bitEvents.get());
  124. } else {
  125. thee = PointProcess_create (my xmin, my xmax, 10);
  126. }
  127. }
  128. }
  129. return thee;
  130. } catch (MelderError) {
  131. Melder_throw (me, U": starting points not converted to PointProcess.");
  132. }
  133. }
  134. autoERPTier EEG_to_ERPTier_marker (EEG me, double fromTime, double toTime, uint16 marker) {
  135. try {
  136. autoPointProcess events = TextGrid_getStartingPoints_multiNumeric (my textgrid.get(), marker);
  137. autoERPTier thee = EEG_PointProcess_to_ERPTier (me, events.get(), fromTime, toTime);
  138. return thee;
  139. } catch (MelderError) {
  140. Melder_throw (me, U": ERPTier not created.");
  141. }
  142. }
  143. autoERPTier EEG_to_ERPTier_triggers (EEG me, double fromTime, double toTime,
  144. kMelder_string which, conststring32 criterion)
  145. {
  146. try {
  147. autoPointProcess events = TextGrid_getPoints (my textgrid.get(), 2, which, criterion);
  148. autoERPTier thee = EEG_PointProcess_to_ERPTier (me, events.get(), fromTime, toTime);
  149. return thee;
  150. } catch (MelderError) {
  151. Melder_throw (me, U": ERPTier not created.");
  152. }
  153. }
  154. autoERPTier EEG_to_ERPTier_triggers_preceded (EEG me, double fromTime, double toTime,
  155. kMelder_string which, conststring32 criterion,
  156. kMelder_string precededBy, conststring32 criterion_precededBy)
  157. {
  158. try {
  159. autoPointProcess events = TextGrid_getPoints_preceded (my textgrid.get(), 2,
  160. which, criterion, precededBy, criterion_precededBy);
  161. autoERPTier thee = EEG_PointProcess_to_ERPTier (me, events.get(), fromTime, toTime);
  162. return thee;
  163. } catch (MelderError) {
  164. Melder_throw (me, U": ERPTier not created.");
  165. }
  166. }
  167. void ERPTier_subtractBaseline (ERPTier me, double tmin, double tmax) {
  168. integer numberOfEvents = my points.size;
  169. if (numberOfEvents < 1)
  170. return; // nothing to do
  171. ERPPoint firstEvent = my points.at [1];
  172. integer numberOfChannels = firstEvent -> erp -> ny;
  173. integer numberOfSamples = firstEvent -> erp -> nx;
  174. for (integer ievent = 1; ievent <= numberOfEvents; ievent ++) {
  175. ERPPoint event = my points.at [ievent];
  176. for (integer ichannel = 1; ichannel <= numberOfChannels; ichannel ++) {
  177. double mean = Vector_getMean (event -> erp.get(), tmin, tmax, ichannel);
  178. double *channel = event -> erp -> z [ichannel];
  179. for (integer isample = 1; isample <= numberOfSamples; isample ++) {
  180. channel [isample] -= mean;
  181. }
  182. }
  183. }
  184. }
  185. void ERPTier_rejectArtefacts (ERPTier me, double threshold) {
  186. integer numberOfEvents = my points.size;
  187. if (numberOfEvents < 1)
  188. return; // nothing to do
  189. ERPPoint firstEvent = my points.at [1];
  190. integer numberOfChannels = firstEvent -> erp -> ny;
  191. integer numberOfSamples = firstEvent -> erp -> nx;
  192. if (numberOfSamples < 1)
  193. return; // nothing to do
  194. for (integer ievent = numberOfEvents; ievent >= 1; ievent --) { // cycle down because of removal
  195. ERPPoint event = my points.at [ievent];
  196. double minimum = event -> erp -> z [1] [1];
  197. double maximum = minimum;
  198. for (integer ichannel = 1; ichannel <= (numberOfChannels & ~ 15); ichannel ++) {
  199. double *channel = event -> erp -> z [ichannel];
  200. for (integer isample = 1; isample <= numberOfSamples; isample ++) {
  201. double value = channel [isample];
  202. if (value < minimum) minimum = value;
  203. if (value > maximum) maximum = value;
  204. }
  205. }
  206. if (minimum < - threshold || maximum > threshold) {
  207. my points. removeItem (ievent);
  208. }
  209. }
  210. }
  211. autoERP ERPTier_extractERP (ERPTier me, integer eventNumber) {
  212. try {
  213. integer numberOfEvents = my points.size;
  214. if (numberOfEvents < 1)
  215. Melder_throw (U"No events.");
  216. ERPTier_checkEventNumber (me, eventNumber);
  217. ERPPoint event = my points.at [eventNumber];
  218. Melder_assert (event -> erp -> ny == my numberOfChannels);
  219. autoERP thee = Thing_new (ERP);
  220. event -> erp -> structSound :: v_copy (thee.get());
  221. thy channelNames = STRVECclone (my channelNames.get());
  222. return thee;
  223. } catch (MelderError) {
  224. Melder_throw (me, U": ERP not extracted.");
  225. }
  226. }
  227. autoERP ERPTier_to_ERP_mean (ERPTier me) {
  228. try {
  229. integer numberOfEvents = my points.size;
  230. if (numberOfEvents < 1)
  231. Melder_throw (U"No events.");
  232. ERPPoint firstEvent = my points.at [1];
  233. Melder_assert (firstEvent -> erp -> ny == my numberOfChannels);
  234. autoERP mean = Thing_new (ERP);
  235. firstEvent -> erp -> structSound :: v_copy (mean.get());
  236. Melder_assert (mean -> ny == my numberOfChannels);
  237. for (integer ievent = 2; ievent <= numberOfEvents; ievent ++) {
  238. ERPPoint event = my points.at [ievent];
  239. Melder_assert (event -> erp -> ny == my numberOfChannels);
  240. mean -> z.get() += event -> erp -> z.get();
  241. }
  242. MATmultiply_inplace (mean -> z.get(), 1.0 / numberOfEvents);
  243. mean -> channelNames = STRVECclone (my channelNames.get());
  244. return mean;
  245. } catch (MelderError) {
  246. Melder_throw (me, U": mean not computed.");
  247. }
  248. }
  249. autoERPTier ERPTier_extractEventsWhereColumn_number (ERPTier me, Table table, integer columnNumber, kMelder_number which, double criterion) {
  250. try {
  251. Table_checkSpecifiedColumnNumberWithinRange (table, columnNumber);
  252. Table_numericize_Assert (table, columnNumber); // extraction should work even if cells are not defined
  253. if (my points.size != table -> rows.size)
  254. Melder_throw (me, U" & ", table, U": the number of rows in the table (", table -> rows.size,
  255. U") doesn't match the number of events (", my points.size, U").");
  256. autoERPTier thee = Thing_new (ERPTier);
  257. Function_init (thee.get(), my xmin, my xmax);
  258. thy numberOfChannels = my numberOfChannels;
  259. thy channelNames = STRVECclone (my channelNames.get());
  260. for (integer ievent = 1; ievent <= my points.size; ievent ++) {
  261. ERPPoint oldEvent = my points.at [ievent];
  262. TableRow row = table -> rows.at [ievent];
  263. if (Melder_numberMatchesCriterion (row -> cells [columnNumber]. number, which, criterion)) {
  264. autoERPPoint newEvent = Data_copy (oldEvent);
  265. thy points. addItem_move (std::move (newEvent));
  266. }
  267. }
  268. if (thy points.size == 0) {
  269. Melder_warning (U"No event matches criterion.");
  270. }
  271. return thee;
  272. } catch (MelderError) {
  273. Melder_throw (me, U": events not extracted.");
  274. }
  275. }
  276. autoERPTier ERPTier_extractEventsWhereColumn_string (ERPTier me, Table table,
  277. integer columnNumber, kMelder_string which, conststring32 criterion)
  278. {
  279. try {
  280. Table_checkSpecifiedColumnNumberWithinRange (table, columnNumber);
  281. if (my points.size != table -> rows.size)
  282. Melder_throw (me, U" & ", table, U": the number of rows in the table (", table -> rows.size,
  283. U") doesn't match the number of events (", my points.size, U").");
  284. autoERPTier thee = Thing_new (ERPTier);
  285. Function_init (thee.get(), my xmin, my xmax);
  286. thy numberOfChannels = my numberOfChannels;
  287. thy channelNames = STRVECclone (my channelNames.get());
  288. for (integer ievent = 1; ievent <= my points.size; ievent ++) {
  289. ERPPoint oldEvent = my points.at [ievent];
  290. TableRow row = table -> rows.at [ievent];
  291. if (Melder_stringMatchesCriterion (row -> cells [columnNumber]. string.get(), which, criterion, true)) {
  292. autoERPPoint newEvent = Data_copy (oldEvent);
  293. thy points. addItem_move (std::move (newEvent));
  294. }
  295. }
  296. if (thy points.size == 0) {
  297. Melder_warning (U"No event matches criterion.");
  298. }
  299. return thee;
  300. } catch (MelderError) {
  301. Melder_throw (me, U": events not extracted.");
  302. }
  303. }
  304. /* End of file ERPTier.cpp */