void structEEG :: v_info () { structDaata :: v_info (); MelderInfo_writeLine (U"Time domain:"); MelderInfo_writeLine (U" Start time: ", our xmin, U" seconds"); MelderInfo_writeLine (U" End time: ", our xmax, U" seconds"); MelderInfo_writeLine (U" Total duration: ", our xmax - our xmin, U" seconds"); if (our sound) { MelderInfo_writeLine (U"Time sampling of the signal:"); MelderInfo_writeLine (U" Number of samples: ", our sound -> nx); MelderInfo_writeLine (U" Sampling period: ", our sound -> dx, U" seconds"); MelderInfo_writeLine (U" Sampling frequency: ", Melder_single (1.0 / our sound -> dx), U" Hz"); MelderInfo_writeLine (U" First sample centred at: ", our sound -> x1, U" seconds"); } MelderInfo_writeLine (U"Number of cap electrodes: ", EEG_getNumberOfCapElectrodes (this)); MelderInfo_writeLine (U"Number of external electrodes: ", EEG_getNumberOfExternalElectrodes (this)); MelderInfo_writeLine (U"Number of extra sensors: ", EEG_getNumberOfExtraSensors (this)); }
static ERPTier EEG_PointProcess_to_ERPTier (EEG me, PointProcess events, double fromTime, double toTime) { try { autoERPTier thee = Thing_new (ERPTier); Function_init (thee.peek(), fromTime, toTime); thy numberOfChannels = my numberOfChannels - EEG_getNumberOfExtraSensors (me); Melder_assert (thy numberOfChannels > 0); thy channelNames = NUMvector <wchar_t *> (1, thy numberOfChannels); for (long ichan = 1; ichan <= thy numberOfChannels; ichan ++) { thy channelNames [ichan] = Melder_wcsdup (my channelNames [ichan]); } long numberOfEvents = events -> nt; thy events = SortedSetOfDouble_create (); double soundDuration = toTime - fromTime; double samplingPeriod = my sound -> dx; long numberOfSamples = floor (soundDuration / samplingPeriod) + 1; if (numberOfSamples < 1) Melder_throw (L"Time window too short."); double midTime = 0.5 * (fromTime + toTime); double soundPhysicalDuration = numberOfSamples * samplingPeriod; double firstTime = midTime - 0.5 * soundPhysicalDuration + 0.5 * samplingPeriod; // distribute the samples evenly over the time domain for (long ievent = 1; ievent <= numberOfEvents; ievent ++) { double eegEventTime = events -> t [ievent]; autoERPPoint event = Thing_new (ERPPoint); event -> number = eegEventTime; event -> erp = Sound_create (thy numberOfChannels, fromTime, toTime, numberOfSamples, samplingPeriod, firstTime); double erpEventTime = 0.0; double eegSample = 1 + (eegEventTime - my sound -> x1) / samplingPeriod; double erpSample = 1 + (erpEventTime - firstTime) / samplingPeriod; long sampleDifference = round (eegSample - erpSample); for (long ichannel = 1; ichannel <= thy numberOfChannels; ichannel ++) { for (long isample = 1; isample <= numberOfSamples; isample ++) { long jsample = isample + sampleDifference; event -> erp -> z [ichannel] [isample] = jsample < 1 || jsample > my sound -> nx ? 0.0 : my sound -> z [ichannel] [jsample]; } } Collection_addItem (thy events, event.transfer()); } return thee.transfer(); } catch (MelderError) { Melder_throw (me, ": ERP analysis not performed."); } }
autoEEG EEG_readFromBdfFile (MelderFile file) { try { autofile f = Melder_fopen (file, "rb"); char buffer [81]; fread (buffer, 1, 8, f); buffer [8] = '\0'; bool is24bit = buffer [0] == (char) 255; fread (buffer, 1, 80, f); buffer [80] = '\0'; trace (U"Local subject identification: \"", Melder_peek8to32 (buffer), U"\""); fread (buffer, 1, 80, f); buffer [80] = '\0'; trace (U"Local recording identification: \"", Melder_peek8to32 (buffer), U"\""); fread (buffer, 1, 8, f); buffer [8] = '\0'; trace (U"Start date of recording: \"", Melder_peek8to32 (buffer), U"\""); fread (buffer, 1, 8, f); buffer [8] = '\0'; trace (U"Start time of recording: \"", Melder_peek8to32 (buffer), U"\""); fread (buffer, 1, 8, f); buffer [8] = '\0'; long numberOfBytesInHeaderRecord = atol (buffer); trace (U"Number of bytes in header record: ", numberOfBytesInHeaderRecord); fread (buffer, 1, 44, f); buffer [44] = '\0'; trace (U"Version of data format: \"", Melder_peek8to32 (buffer), U"\""); fread (buffer, 1, 8, f); buffer [8] = '\0'; long numberOfDataRecords = strtol (buffer, nullptr, 10); trace (U"Number of data records: ", numberOfDataRecords); fread (buffer, 1, 8, f); buffer [8] = '\0'; double durationOfDataRecord = atof (buffer); trace (U"Duration of a data record: ", durationOfDataRecord); fread (buffer, 1, 4, f); buffer [4] = '\0'; long numberOfChannels = atol (buffer); trace (U"Number of channels in data record: ", numberOfChannels); if (numberOfBytesInHeaderRecord != (numberOfChannels + 1) * 256) Melder_throw (U"Number of bytes in header record (", numberOfBytesInHeaderRecord, U") doesn't match number of channels (", numberOfChannels, U")."); autostring32vector channelNames (1, numberOfChannels); for (long ichannel = 1; ichannel <= numberOfChannels; ichannel ++) { fread (buffer, 1, 16, f); buffer [16] = '\0'; // labels of the channels /* * Strip all final spaces. */ for (int i = 15; i >= 0; i --) { if (buffer [i] == ' ') { buffer [i] = '\0'; } else { break; } } channelNames [ichannel] = Melder_8to32 (buffer); trace (U"Channel <<", channelNames [ichannel], U">>"); } bool hasLetters = str32equ (channelNames [numberOfChannels], U"EDF Annotations"); double samplingFrequency = NUMundefined; for (long channel = 1; channel <= numberOfChannels; channel ++) { fread (buffer, 1, 80, f); buffer [80] = '\0'; // transducer type } for (long channel = 1; channel <= numberOfChannels; channel ++) { fread (buffer, 1, 8, f); buffer [8] = '\0'; // physical dimension of channels } autoNUMvector <double> physicalMinimum (1, numberOfChannels); for (long ichannel = 1; ichannel <= numberOfChannels; ichannel ++) { fread (buffer, 1, 8, f); buffer [8] = '\0'; physicalMinimum [ichannel] = atof (buffer); } autoNUMvector <double> physicalMaximum (1, numberOfChannels); for (long ichannel = 1; ichannel <= numberOfChannels; ichannel ++) { fread (buffer, 1, 8, f); buffer [8] = '\0'; physicalMaximum [ichannel] = atof (buffer); } autoNUMvector <double> digitalMinimum (1, numberOfChannels); for (long ichannel = 1; ichannel <= numberOfChannels; ichannel ++) { fread (buffer, 1, 8, f); buffer [8] = '\0'; digitalMinimum [ichannel] = atof (buffer); } autoNUMvector <double> digitalMaximum (1, numberOfChannels); for (long ichannel = 1; ichannel <= numberOfChannels; ichannel ++) { fread (buffer, 1, 8, f); buffer [8] = '\0'; digitalMaximum [ichannel] = atof (buffer); } for (long channel = 1; channel <= numberOfChannels; channel ++) { fread (buffer, 1, 80, f); buffer [80] = '\0'; // prefiltering } long numberOfSamplesPerDataRecord = 0; for (long channel = 1; channel <= numberOfChannels; channel ++) { fread (buffer, 1, 8, f); buffer [8] = '\0'; // number of samples in each data record long numberOfSamplesInThisDataRecord = atol (buffer); if (samplingFrequency == NUMundefined) { numberOfSamplesPerDataRecord = numberOfSamplesInThisDataRecord; samplingFrequency = numberOfSamplesInThisDataRecord / durationOfDataRecord; } if (numberOfSamplesInThisDataRecord / durationOfDataRecord != samplingFrequency) Melder_throw (U"Number of samples per data record in channel ", channel, U" (", numberOfSamplesInThisDataRecord, U") doesn't match sampling frequency of channel 1 (", samplingFrequency, U")."); } for (long channel = 1; channel <= numberOfChannels; channel ++) { fread (buffer, 1, 32, f); buffer [32] = '\0'; // reserved } double duration = numberOfDataRecords * durationOfDataRecord; autoEEG him = EEG_create (0, duration); his numberOfChannels = numberOfChannels; autoSound me = Sound_createSimple (numberOfChannels, duration, samplingFrequency); Melder_assert (my nx == numberOfSamplesPerDataRecord * numberOfDataRecords); autoNUMvector <unsigned char> dataBuffer (0L, 3 * numberOfSamplesPerDataRecord - 1); for (long record = 1; record <= numberOfDataRecords; record ++) { for (long channel = 1; channel <= numberOfChannels; channel ++) { double factor = channel == numberOfChannels ? 1.0 : physicalMinimum [channel] / digitalMinimum [channel]; if (channel < numberOfChannels - EEG_getNumberOfExtraSensors (him.peek())) factor /= 1000000.0; if (is24bit) { fread (& dataBuffer [0], 3, numberOfSamplesPerDataRecord, f); unsigned char *p = & dataBuffer [0]; for (long i = 1; i <= numberOfSamplesPerDataRecord; i ++) { long sample = i + (record - 1) * numberOfSamplesPerDataRecord; Melder_assert (sample <= my nx); uint8_t lowByte = *p ++, midByte = *p ++, highByte = *p ++; uint32_t externalValue = ((uint32_t) highByte << 16) | ((uint32_t) midByte << 8) | (uint32_t) lowByte; if ((highByte & 128) != 0) // is the 24-bit sign bit on? externalValue |= 0xFF000000; // extend negative sign to 32 bits my z [channel] [sample] = (int32_t) externalValue * factor; } } else { fread (& dataBuffer [0], 2, numberOfSamplesPerDataRecord, f); unsigned char *p = & dataBuffer [0]; for (long i = 1; i <= numberOfSamplesPerDataRecord; i ++) { long sample = i + (record - 1) * numberOfSamplesPerDataRecord; Melder_assert (sample <= my nx); uint8 lowByte = *p ++, highByte = *p ++; uint16 externalValue = (uint16) ((uint16) highByte << 8) | (uint16) lowByte; my z [channel] [sample] = (int16) externalValue * factor; } } } } int numberOfStatusBits = 8; for (long i = 1; i <= my nx; i ++) { unsigned long value = (long) my z [numberOfChannels] [i]; if (value & 0x0000FF00) { numberOfStatusBits = 16; } } autoTextGrid thee; if (hasLetters) { thee = TextGrid_create (0, duration, U"Mark Trigger", U"Mark Trigger"); autoMelderString letters; double time = NUMundefined; for (long i = 1; i <= my nx; i ++) { unsigned long value = (long) my z [numberOfChannels] [i]; for (int byte = 1; byte <= numberOfStatusBits / 8; byte ++) { unsigned long mask = byte == 1 ? 0x000000ff : 0x0000ff00; char32 kar = byte == 1 ? (value & mask) : (value & mask) >> 8; if (kar != U'\0' && kar != 20) { MelderString_appendCharacter (& letters, kar); } else if (letters. string [0] != U'\0') { if (letters. string [0] == U'+') { if (NUMdefined (time)) { try { TextGrid_insertPoint (thee.peek(), 1, time, U""); } catch (MelderError) { Melder_throw (U"Did not insert empty mark (", letters. string, U") on Mark tier."); } time = NUMundefined; // defensive } time = Melder_atof (& letters. string [1]); MelderString_empty (& letters); } else { if (! NUMdefined (time)) { Melder_throw (U"Undefined time for label at sample ", i, U"."); } try { if (Melder_nequ (letters. string, U"Trigger-", 8)) { try { TextGrid_insertPoint (thee.peek(), 2, time, & letters. string [8]); } catch (MelderError) { Melder_clearError (); trace (U"Duplicate trigger at ", time, U" seconds: ", & letters. string [8]); } } else { TextGrid_insertPoint (thee.peek(), 1, time, & letters. string [0]); } } catch (MelderError) { Melder_throw (U"Did not insert mark (", letters. string, U") on Trigger tier."); } time = NUMundefined; // crucial MelderString_empty (& letters); } } } } if (NUMdefined (time)) { TextGrid_insertPoint (thee.peek(), 1, time, U""); time = NUMundefined; // defensive } } else { thee = TextGrid_create (0, duration, numberOfStatusBits == 8 ? U"S1 S2 S3 S4 S5 S6 S7 S8" : U"S1 S2 S3 S4 S5 S6 S7 S8 S9 S10 S11 S12 S13 S14 S15 S16", U""); for (int bit = 1; bit <= numberOfStatusBits; bit ++) { unsigned long bitValue = 1 << (bit - 1); IntervalTier tier = (IntervalTier) thy tiers -> item [bit]; for (long i = 1; i <= my nx; i ++) { unsigned long previousValue = i == 1 ? 0 : (long) my z [numberOfChannels] [i - 1]; unsigned long thisValue = (long) my z [numberOfChannels] [i]; if ((thisValue & bitValue) != (previousValue & bitValue)) { double time = i == 1 ? 0.0 : my x1 + (i - 1.5) * my dx; if (time != 0.0) TextGrid_insertBoundary (thee.peek(), bit, time); if ((thisValue & bitValue) != 0) TextGrid_setIntervalText (thee.peek(), bit, tier -> intervals -> size, U"1"); } } } } f.close (file); his channelNames = channelNames.transfer(); his sound = me.move(); his textgrid = thee.move(); if (EEG_getNumberOfCapElectrodes (him.peek()) == 32) { EEG_setChannelName (him.peek(), 1, U"Fp1"); EEG_setChannelName (him.peek(), 2, U"AF3"); EEG_setChannelName (him.peek(), 3, U"F7"); EEG_setChannelName (him.peek(), 4, U"F3"); EEG_setChannelName (him.peek(), 5, U"FC1"); EEG_setChannelName (him.peek(), 6, U"FC5"); EEG_setChannelName (him.peek(), 7, U"T7"); EEG_setChannelName (him.peek(), 8, U"C3"); EEG_setChannelName (him.peek(), 9, U"CP1"); EEG_setChannelName (him.peek(), 10, U"CP5"); EEG_setChannelName (him.peek(), 11, U"P7"); EEG_setChannelName (him.peek(), 12, U"P3"); EEG_setChannelName (him.peek(), 13, U"Pz"); EEG_setChannelName (him.peek(), 14, U"PO3"); EEG_setChannelName (him.peek(), 15, U"O1"); EEG_setChannelName (him.peek(), 16, U"Oz"); EEG_setChannelName (him.peek(), 17, U"O2"); EEG_setChannelName (him.peek(), 18, U"PO4"); EEG_setChannelName (him.peek(), 19, U"P4"); EEG_setChannelName (him.peek(), 20, U"P8"); EEG_setChannelName (him.peek(), 21, U"CP6"); EEG_setChannelName (him.peek(), 22, U"CP2"); EEG_setChannelName (him.peek(), 23, U"C4"); EEG_setChannelName (him.peek(), 24, U"T8"); EEG_setChannelName (him.peek(), 25, U"FC6"); EEG_setChannelName (him.peek(), 26, U"FC2"); EEG_setChannelName (him.peek(), 27, U"F4"); EEG_setChannelName (him.peek(), 28, U"F8"); EEG_setChannelName (him.peek(), 29, U"AF4"); EEG_setChannelName (him.peek(), 30, U"Fp2"); EEG_setChannelName (him.peek(), 31, U"Fz"); EEG_setChannelName (him.peek(), 32, U"Cz"); } else if (EEG_getNumberOfCapElectrodes (him.peek()) == 64) { EEG_setChannelName (him.peek(), 1, U"Fp1"); EEG_setChannelName (him.peek(), 2, U"AF7"); EEG_setChannelName (him.peek(), 3, U"AF3"); EEG_setChannelName (him.peek(), 4, U"F1"); EEG_setChannelName (him.peek(), 5, U"F3"); EEG_setChannelName (him.peek(), 6, U"F5"); EEG_setChannelName (him.peek(), 7, U"F7"); EEG_setChannelName (him.peek(), 8, U"FT7"); EEG_setChannelName (him.peek(), 9, U"FC5"); EEG_setChannelName (him.peek(), 10, U"FC3"); EEG_setChannelName (him.peek(), 11, U"FC1"); EEG_setChannelName (him.peek(), 12, U"C1"); EEG_setChannelName (him.peek(), 13, U"C3"); EEG_setChannelName (him.peek(), 14, U"C5"); EEG_setChannelName (him.peek(), 15, U"T7"); EEG_setChannelName (him.peek(), 16, U"TP7"); EEG_setChannelName (him.peek(), 17, U"CP5"); EEG_setChannelName (him.peek(), 18, U"CP3"); EEG_setChannelName (him.peek(), 19, U"CP1"); EEG_setChannelName (him.peek(), 20, U"P1"); EEG_setChannelName (him.peek(), 21, U"P3"); EEG_setChannelName (him.peek(), 22, U"P5"); EEG_setChannelName (him.peek(), 23, U"P7"); EEG_setChannelName (him.peek(), 24, U"P9"); EEG_setChannelName (him.peek(), 25, U"PO7"); EEG_setChannelName (him.peek(), 26, U"PO3"); EEG_setChannelName (him.peek(), 27, U"O1"); EEG_setChannelName (him.peek(), 28, U"Iz"); EEG_setChannelName (him.peek(), 29, U"Oz"); EEG_setChannelName (him.peek(), 30, U"POz"); EEG_setChannelName (him.peek(), 31, U"Pz"); EEG_setChannelName (him.peek(), 32, U"CPz"); EEG_setChannelName (him.peek(), 33, U"Fpz"); EEG_setChannelName (him.peek(), 34, U"Fp2"); EEG_setChannelName (him.peek(), 35, U"AF8"); EEG_setChannelName (him.peek(), 36, U"AF4"); EEG_setChannelName (him.peek(), 37, U"AFz"); EEG_setChannelName (him.peek(), 38, U"Fz"); EEG_setChannelName (him.peek(), 39, U"F2"); EEG_setChannelName (him.peek(), 40, U"F4"); EEG_setChannelName (him.peek(), 41, U"F6"); EEG_setChannelName (him.peek(), 42, U"F8"); EEG_setChannelName (him.peek(), 43, U"FT8"); EEG_setChannelName (him.peek(), 44, U"FC6"); EEG_setChannelName (him.peek(), 45, U"FC4"); EEG_setChannelName (him.peek(), 46, U"FC2"); EEG_setChannelName (him.peek(), 47, U"FCz"); EEG_setChannelName (him.peek(), 48, U"Cz"); EEG_setChannelName (him.peek(), 49, U"C2"); EEG_setChannelName (him.peek(), 50, U"C4"); EEG_setChannelName (him.peek(), 51, U"C6"); EEG_setChannelName (him.peek(), 52, U"T8"); EEG_setChannelName (him.peek(), 53, U"TP8"); EEG_setChannelName (him.peek(), 54, U"CP6"); EEG_setChannelName (him.peek(), 55, U"CP4"); EEG_setChannelName (him.peek(), 56, U"CP2"); EEG_setChannelName (him.peek(), 57, U"P2"); EEG_setChannelName (him.peek(), 58, U"P4"); EEG_setChannelName (him.peek(), 59, U"P6"); EEG_setChannelName (him.peek(), 60, U"P8"); EEG_setChannelName (him.peek(), 61, U"P10"); EEG_setChannelName (him.peek(), 62, U"PO8"); EEG_setChannelName (him.peek(), 63, U"PO4"); EEG_setChannelName (him.peek(), 64, U"O2"); } return him; } catch (MelderError) {