// file: $(NEDC_NFC)/class/cpp/Edf/edf_08.cc // // This file contains private methods associated with the class Edf. // // Revision History: // // 20240307 (JP): refactored code to expand set/get methods // // local include files // #include "Edf.h" //***************************************************************************** // // private methods: header manipulations // //***************************************************************************** // method: compute_header_size // // arguments: // long nchan: number of channels // // return: a long value containing the size of the header in bytes // // Apparently, an EDF header is designed to be equal to: nchan * 256 + 256. // This method encapsulates this. // long Edf::compute_header_size(long nchan_a) { // return the size // return nchan_a * EDF_BSIZE + EDF_BSIZE; } //***************************************************************************** // // private methods: special I/O methods // //***************************************************************************** // method: read_htk_channel // // arguments: // VVectorDouble& feat: feature matrix (output) // double& fdur: frame duration used by the features (output) // char* fn: output filename (input) // // return: a boolean value indicating status // // This method read a single channel of HTK feature data from a file. // // All data is written in a binary format. // bool Edf::read_htk_channel(VVectorDouble& feat_a, double& fdur_a, const char* fn_a) { // declare local variables // unsigned char buf[4]; // check if we need to byte swap // bool big_endian = Edf::is_big_endian(); // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: beginning read [%s] ", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); if (big_endian == false) { fprintf(stdout, " byte order = little endian\n"); } else { fprintf(stdout, " byte order = big endian\n"); } } // check the size of a float: // a float must be 32 bits long for this code to work properly. so we // check this and make the program crash if this isn't the case. // char static_assert_float32[1 - (2 * ((sizeof(float) * CHAR_BIT) != 32))]; if (static_assert_float32 == (char*)NULL) {exit(EXIT_FAILURE);} // open the file for reading // FILE* fp = fopen(fn_a, "r"); if (fp == (FILE*)NULL) { return false; } // read the number of frames // int32_t num_frames; if (fread(&buf, sizeof(int32_t), 1, fp) != 1) { fclose(fp); return false; } if (!big_endian) { Edf::swap_bytes(&num_frames, buf, sizeof(int32_t)); } else { memcpy(&num_frames, buf, sizeof(int32_t)); } if ((long)feat_a.size() != num_frames) { Edf::resize(feat_a, num_frames, false); } // read the frame duration in 100 msec units // int32_t ifdur; if (fread(&buf, sizeof(int32_t), 1, fp) != 1) { fclose(fp); return false; } if (!big_endian) { Edf::swap_bytes(&ifdur, buf, sizeof(int32_t)); } else { memcpy(&ifdur, buf, sizeof(int32_t)); } fdur_a = ifdur * HTK_FDUR_SCALE; // read the sample size: how many bytes in an entire frame // int16_t sample_size; if (fread(&buf, sizeof(int16_t), 1, fp) != 1) { fclose(fp); return false; } if (!big_endian) { Edf::swap_bytes(&sample_size, buf, sizeof(int16_t)); } else { memcpy(&sample_size, buf, sizeof(int16_t)); } long vec_size = sample_size / sizeof(float); // read the HTK code // int16_t htk_code; if (fread(&buf, sizeof(int16_t), 1, fp) != 1) { fclose(fp); return false; } if (!big_endian) { Edf::swap_bytes(&htk_code, buf, sizeof(int16_t)); } else { memcpy(&htk_code, buf, sizeof(int16_t)); } if (htk_code != 9) { fprintf(stdout, "Error: %s (line: %d) %s: error reading htk code [%d != 9]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, htk_code); fclose(fp); return false; } // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: %s (%ld %ld %f %ld %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, "(nf, fdim, fdur, ssize, htk_code)", (long)num_frames, vec_size, fdur_a, (long)sample_size, (long)htk_code); } // loop over all frames and all elements // float sum; float tmp_buf[vec_size]; for (long i = 0; i < num_frames; i++){ // make space for the output // if ((long)feat_a[i].size() != vec_size) { Edf::resize(feat_a[i], vec_size, false); } // read the vector from the file // if ((long)fread(tmp_buf, sizeof(float), vec_size, fp) != vec_size) { fclose(fp); return false; } // convert the vector to a double // for (long j = 0; j < vec_size; j++) { // byte swap if necessary // if (!big_endian) { Edf::swap_bytes(&sum, &tmp_buf[j], sizeof(float)); } else { sum = tmp_buf[j]; } // convert to a single precision floating point number // feat_a[i][j] = sum; } } // close the file // fclose(fp); // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, " Edf::read_htk_channel(): end of htk_channel read [%s]\n", fn_a); } // exit gracefully // return true; } // method: write_htk_channel // // arguments: // VVectorDouble& feat: feature matrix (input) // double fdur: frame duration used by the features (input) // char* fn: output filename (input) // // return: a boolean value indicating status // // This method writes feature vectors to a file in an HTK binary format. // // All data is written in a binary format. // bool Edf::write_htk_channel(VVectorDouble& feat_a, double fdur_a, const char* fn_a) { // declare local variables // unsigned char buf[4]; // check if we need to byte swap // bool big_endian = Edf::is_big_endian(); // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: beginning write", __FILE__, __LINE__, __PRETTY_FUNCTION__); if (big_endian == false) { fprintf(stdout, " byte order = little endian\n"); } else { fprintf(stdout, " byte order = big endian\n"); } } // check the size of a float: // a float must be 32 bits long for this code to work properly. so we // check this and make the program crash if this isn't the case. // char static_assert_float32[1 - (2 * ((sizeof(float) * CHAR_BIT) != 32))]; if (static_assert_float32 == (char*)NULL) {exit(EXIT_FAILURE);} // check the vector size: // The actual vector size must be less than // 2**15 bytes (no. elements * 4 bytes per element). // long vec_size = feat_a[0].size(); if ((vec_size * sizeof(float)) >= pow(2, sizeof(int16_t) * CHAR_BIT - 1)) { fprintf(stdout, "Error: %s (line: %d) %s: %s [%ld] [%ld]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, "byte count of frame is too large", vec_size * sizeof(float), (long)pow(2, sizeof(int16_t) * CHAR_BIT - 1)); return false; } // check that the vector size is the same for all frames of data // int32_t num_frames = feat_a.size(); for (long i = 0; i < num_frames; i++){ if ((long)feat_a[i].size() != vec_size) { fprintf(stdout, "Error: %s (line: %d) %s: %s [%ld] [%ld]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, "feature vectors must have the same size", (long)feat_a[i].size(), vec_size); return false; } } // open the file for writing // FILE* fp = fopen(fn_a, "w"); if (fp == (FILE*)NULL) { return false; } // write number of frames // if (!big_endian) { Edf::swap_bytes(buf, &num_frames, sizeof(int32_t)); } else { memcpy(buf, &num_frames, sizeof(int32_t)); } if (fwrite(&buf, sizeof(int32_t), 1, fp) != 1) { fclose(fp); return false; } // write frame duration in 100 mic-sec units // int32_t ifdur = round(fdur_a / HTK_FDUR_SCALE); if (!big_endian) { Edf::swap_bytes(buf, &ifdur, sizeof(int32_t)); } else { memcpy(buf, &ifdur, sizeof(int32_t)); } if (fwrite(&buf, sizeof(int32_t), 1, fp) != 1) { fclose(fp); return false; } // write sample size: how many bytes in an entire frame // int16_t sample_size = vec_size * sizeof(float); if (!big_endian) { Edf::swap_bytes(buf, &sample_size, sizeof(int16_t)); } else { memcpy(buf, &sample_size, sizeof(int16_t)); } if (fwrite(&buf, sizeof(int16_t), 1, fp) != 1) { fclose(fp); return false; } // write HTK code: // // A two-byte integer that determines what is in the file. // The first 6 bits are the data type (9 means user defined) // and other bits are used for energy, existence of delta and // delta-delta, comperssion etc. Here we just need the simple case // with its 16 bits set to: 0000000000001001 = 9 // int16_t htk_code = 9; if (!big_endian) { Edf::swap_bytes(buf, &htk_code, sizeof(int16_t)); } else { memcpy(buf, &htk_code, sizeof(int16_t)); } if (fwrite(&buf, sizeof(int16_t), 1, fp) != 1) { fclose(fp); return false; } // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: %s (%ld %ld %f %ld %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, "(nf, fdim, fdur, ssize, htk_code)", (long)num_frames, vec_size, fdur_a, (long)sample_size, (long)htk_code); } // loop over all frames and all elements // float sum; float tmp_buf[vec_size]; for (long i = 0; i < num_frames; i++){ for (long j = 0; j < vec_size; j++) { // convert to a single precision floating point number // sum = feat_a[i][j]; // byte swap if necessary // if (!big_endian) { Edf::swap_bytes(&tmp_buf[j], &sum, sizeof(float)); } else { tmp_buf[j] = sum; } } // write the vector to the file // if ((long)fwrite(tmp_buf, sizeof(float), vec_size, fp) != vec_size) { fclose(fp); return false; } } // close the file // fclose(fp); // display debugging information // if (dbgl_d >= Dbgl::DETAILED) { fprintf(stdout, "%s (line: %d) %s: end of htk_channel write [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); } // exit gracefully // return true; } // method: is_big_endian // // arguments: none // // return: a boolean value that is true if the machine architecture // is big endian // // HTK writes data in big endian format, so we must byte-swap if we are // on a little endian machine. // // All data is written in a binary format. // bool Edf::is_big_endian() { // declare a two-byte int to have a value of 1 // unsigned int i = 1; char* c = (char*) &i; // if the first byte is set, it is little Endian // if (*c) { return false; } // else: big endian // else { return true; } } // method: swap_bytes // // arguments: // // return: a boolean value that is true // // This is meant to be a highly portable method for swapping bytes. Only // two-byte and four-byte swaps are currently supported. // bool Edf::swap_bytes(void* buf_a, void* value_a, long nbytes) { // declare some pointers to avoid compilter warnings on type conversions // unsigned char* dst = (unsigned char*)buf_a; unsigned char* src = (unsigned char*)value_a; // case 1: two bytes - flip the bytes // if (nbytes == 2) { dst[0] = src[1]; dst[1] = src[0]; } // case 2: four bytes - reindex the bytes // else if (nbytes == 4) { dst[0] = src[3]; dst[1] = src[2]; dst[2] = src[1]; dst[3] = src[0]; } // else: unsupported type // else { return false; } // exit gracefully // return true; } //***************************************************************************** // // private methods: string processing methods // //***************************************************************************** // method: uppercase // // arguments: // char* str: a string containing mixed case (input/output) // // return: a boolean value indicating status // // This method uppercases a string. // bool Edf::uppercase(char* str_a) { // loop over all characters // while(*str_a != (char)NULL) { *str_a = toupper((unsigned char)*str_a); str_a++; } // exit gracefully // return true; } // method: find_match // // arguments: // char* label: a label containing the desired channel (input) // long nl: number of labels // char** lbls: an array of labels // MATCH_MODE matmode: an enum indicating the precision of the match (input) // // return: a long integer containing the channel number, // or -1 if it is not found. // // This method finds the channel in the set of labels in the header. // Two match modes are supported: exact (true) and partial (false). // long Edf::find_match(const char* label_a, long nl_a, char** lbls_a, MATCH_MODE matmode_a) { // loop over all labels // for (long i = 0; i < nl_a; i++) { // case: exact match // if ((matmode_a == MATMODE_EXACT) && (strcmp(lbls_a[i], label_a) == 0)) { return i; } // case: partial match // else if ((matmode_a == MATMODE_PARTIAL) && (strstr(lbls_a[i], label_a) != (char*)NULL)) { return i; } } // exit ungracfully // return ERR_MATCH; } // method: trim_whitespace // // arguments: // char* str_out: string with whitespace removed (output) // char* str_in: original string (input) // // return: a boolean value indicating status // // This method removes leading and trailing whitespace from a string. // It also upcases the string. // bool Edf::trim_whitespace(char* str_out_a, const char* str_in_a) { // declare local variables: // we need the beginning and end of the string // long lstr = strlen(str_in_a); char* str_beg = (char*)str_in_a; char* str_end = (char*)str_in_a + lstr; // find the first non-space character // while ((*str_beg == Itgl::SPACE[0]) && (str_beg < str_end)) { str_beg++; } // find the last non-space character // str_end--; while ((*str_end == Itgl::SPACE[0]) && (str_end > str_beg)) { str_end--; } // copy the trimmed string // char* str_o = str_out_a; for (char* ptr = str_beg; ptr <= str_end; ptr++) { *str_o++ = *str_beg++; } *str_o = (char)NULL; // exit gracefully // return true; } // method: trim_whitespace_and_upcase // // arguments: // char* str_out: string with whitespace removed (output) // char* str_in: original string (input) // // return: a boolean value indicating status // // This method removes leading and trailing whitespace from a string. // It also upcases the string. // bool Edf::trim_whitespace_and_upcase(char* str_out_a, const char* str_in_a) { // declare local variables: // we need the beginning and end of the string // long lstr = strlen(str_in_a); char* str_beg = (char*)str_in_a; char* str_end = (char*)str_in_a + lstr; // find the first non-space character // while ((*str_beg == Itgl::SPACE[0]) && (str_beg < str_end)) { str_beg++; } // find the last non-space character // str_end--; while ((*str_end == Itgl::SPACE[0]) && (str_end > str_beg)) { str_end--; } // copy the trimmed string // char* str_o = str_out_a; for (char* ptr = str_beg; ptr <= str_end; ptr++) { *str_o++ = toupper(*str_beg++); } *str_o = (char)NULL; // exit gracefully // return true; } // method: trim_last_character // // arguments: // char* str: string to be trimmed (input/output) // char chr: character to be trimmed (input) // // return: a boolean value indicating status // // This method removes the last character in a string if it is present. // It is typically used to delete a training directory delimiter. Note // that this method only returns true if a character was trimmed. // bool Edf::trim_last_character(char* str_a, char chr_a) { // check for the presence of the character // long pos = strlen(str_a) - 1; if (str_a[pos] == chr_a) { str_a[pos] = (char)NULL; return true; } // exit gracefully: // return false if nothing was replaced // return false; } // method: pad_whitespace // // arguments: // char* str: string with whitespace added (output) // long len: desired length (input) // // return: a boolean value indicating status // // This method added trailing whitespace so the edf header remains // the proper size. // bool Edf::pad_whitespace(char* str_a, long len_a) { // declare local variables: // we need the beginning and end of the string // long lstr = strlen(str_a); // add whitespace // for (long i = lstr; i < len_a; i++) { str_a[i] = Itgl::SPACE[0]; } // add a null character just to be safe: // this is a little dangerous - the string must have an // extra element for this. all the strings declared in the // header file should. // str_a[len_a] = (char)NULL; // exit gracefully // return true; } //***************************************************************************** // // private methods: parsing methods // //***************************************************************************** // method: parse_montage // // arguments: // long& nl_a: the number of labels (output) // char** labels1_a: an array of strings containing the labels (output) // char** labels2_a: an array of strings containing the labels (output) // char* num_chans_a: an array of the channel numbers (output) // char** olabels_a: an array of the name of each channel (output) // char** str_a: an array of strings containing the label list (input) // // return: a boolean indicating status // // This method parses a line and produces a list of labels showing // which channels are to be differenced. // bool Edf::parse_montage(long& nl_a, char** labels1_a, char** labels2_a, char** olabels_a, long* num_chans_a, char** str_a) { // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: entering parse_montage\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // declarations of all the local variables // char* tmp_chan[MAX_NCHANS]; char* tmp_num[MAX_NCHANS]; char* tmp_op[MAX_NCHANS]; char* tmp_labels1[MAX_NCHANS]; char* tmp_labels2[MAX_NCHANS]; // sets the output variable to the number of channel labels // nl_a = num_mlabels_d; // loops over all the elements in the montage_d array // for (long i = 0; i < num_mlabels_d; i++) { // initializes variables // long np_tmp = 0; long nl_tmp = 0; long nn_tmp = 0; //Parses the first element which is the channel number // bool status = Edf::parse_line(nn_tmp, tmp_num, str_a[i], (char*)Itgl::COMMA); if (!status) { fprintf(stdout, "Error: %s (line: %d) %s: invalid montage spec [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, str_a[i]); return false; } // converts the string output a long variable // long channel = atol(tmp_num[0]); for(long j = 0; j < nn_tmp; j++){ delete [] tmp_num [j]; } // ensures there is not a missing channel // if (i != channel) { fprintf(stdout, "Error: %s (line: %d) %s: missing channel [%ld]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i); return false; } // fills the array with the channel number // num_chans_a[i] = channel; chan_num_d[i] = channel; // parses the string up to the comma (cuts off the first channel) // char* pos_c = strstr(str_a[i], Itgl::COMMA); // displays debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: entering parse_line to find chan_labels\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // cuts off any leading comma // while (pos_c[0] == Itgl::COMMA[0]) { pos_c++; } // parses up to the colon which is the name of the channel // status = Edf::parse_line(np_tmp, tmp_chan, pos_c, (char*)Itgl::COLON); if (!status) { fprintf(stdout, "Error: %s (line: %d) %s: invalid montage name\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); return false; } // gets the length of the channel label // long s = strlen(tmp_chan[0]); // Copies the temporary variable onto the chan_labels array // Copies the temporary variable onto the Edf.h variable mchan_d // olabels_a[i] = new char[s+1]; strcpy(olabels_a[i], tmp_chan[0]); for (long j = 0; j < np_tmp; j++) { delete [] tmp_chan[j]; } // parses up to the colon (this isolates the montage labels) // pos_c = strstr(pos_c, Itgl::COLON); // gets rid of any leading colon // while (pos_c[0] == Itgl::COLON[0]) { pos_c++; } while (isspace(*pos_c)) { pos_c++; } // gets rid of leading spaces // while (isspace(*pos_c)) { pos_c++; } // gets the length of the labels (unparse) // long label_len = strlen(pos_c); // initializes temporary variable // tmp_op[nl_tmp] = new char [label_len+1]; // copies the pos_co string to op_tmp // strcpy(tmp_op[nl_tmp], pos_c); tmp_op[nl_tmp][label_len] = (char) NULL; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: entering parse_operands()\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } long test; status = Edf::parse_operands(test, tmp_labels1, tmp_labels2, nl_tmp+1, tmp_op); // copies the temporary value to the mlabels1 and mlabels2 // for (long j = 0; j < nl_tmp+1; j++) { delete [] tmp_op[j]; } long lab_len = strlen(tmp_labels1[0]); labels1_a[i] = new char [lab_len+1]; strcpy(labels1_a[i], tmp_labels1[0]); labels1_a[i][lab_len] = (char) NULL; lab_len = strlen(tmp_labels2[0]); if (tmp_labels2[0] != NULL_NAME) { labels2_a[i] = new char [lab_len+1]; strcpy(labels2_a[i], tmp_labels2[0]); labels2_a[i][lab_len] = (char)NULL; } else { labels2_a[i] = new char[strlen(NULL_NAME) + 1]; strcpy(labels2_a[i], NULL_NAME); } for(long j = 0; j < test; j++){ delete [] tmp_labels1[j]; if (tmp_labels2[j] != NULL_NAME) { delete [] tmp_labels2[j]; } } } // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: leaving parse_montage\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: parse_operands // // arguments: // long& nlo: the number of labels (output) // char** labels1: an array of strings containing the labels (output) // char** labels2: an array of strings containing the labels (output) // long nl: the number of labels (input) // char** labels: a single array of strings containing the labels (input) // // return: a boolean indicating status // // This method parses an array of labels of the form "x -- y" and // splits them into separate arrays. // bool Edf::parse_operands(long& nlo_a, char** labels1_a, char** labels2_a, long nl_a, char** labels_a) { // create output space // nlo_a = nl_a; // loop over all labels // for (long i = 0; i < nl_a; i++) { // find the delimiter // char* sptr = strstr(labels_a[i], Itgl::DASHDASH); // case 1: no delimiter - copy the channel as is // if (sptr == (char*)NULL) { long len = strlen(labels_a[i]); labels1_a[i] = new char[len + 1]; strcpy(labels1_a[i], labels_a[i]); trim_whitespace_and_upcase(labels1_a[i], labels1_a[i]); labels2_a[i] = new char[1]; labels2_a[i] = (char*)NULL_NAME; } // case 2: delimiter - parse the string into two pieces // else { // copy the first part of the string // long len = sptr - labels_a[i] - 1; labels1_a[i] = new char[len + 1]; strncpy(labels1_a[i], labels_a[i], len); trim_whitespace_and_upcase(labels1_a[i], labels1_a[i]); *(labels1_a[i]+len) = (char)NULL; // copy the secon part of the string // len = sptr + 3 - labels_a[i] - 1; labels2_a[i] = new char[len + 1]; strncpy(labels2_a[i], sptr + 3, len); trim_whitespace_and_upcase(labels2_a[i], labels2_a[i]); *(labels2_a[i]+len) = (char)NULL; } } // exit gracefully // return true; } // method: parse_interp_channels // // arguments: // long& nl_i: the number of labels (output) // char*** labels1_a: an array of an array of strings containing the adjacent // channels (output) // VectorLong& num_chans_a: the number of adjacent channels (output) // char** nchans_a: contains the channel that we want to interpolate (output) // char* str_a: a string containing the label list (input) // // return: a boolean indicating status // // This method parses a line assumed to be in the format: // str = EEG FP10: EEG FP1-REF, EEG FP2-REF, EEG FP3-REF, EEG FP4-REF // // The first variable represents the new channel and all the other channels // represent the adjacent channels. Note that EEG labels usually have // spaces in them which can't be ignored. // bool Edf:: parse_interp_channels(long& nl_i, char*** labels1_a, VectorLong& num_chans_a, char** nchans_a, char** str_a){ if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: entering parse_interp_channels\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } //declare local variables // char* tmp_nlabels[MAX_NCHANS]; char* tmp_adlabels[MAX_NCHANS]; Edf::resize(num_chans_a, num_clabels_d); // loops over the whole array // for (long i = 0; i < num_clabels_d; i++) { // initialzes the local variables // long pc = 0; long num_adj_chans = 0; // parses the new channel // bool status = Edf::parse_line(pc, tmp_nlabels, str_a[i], (char*)Itgl::COLON); // displays debugging information // if (!status) { fprintf(stdout, "Error: %s (line: %d) %s: invalid interp channels spec [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, str_a[i]); return false; } // gets the length of the new channel // long nc = strlen(tmp_nlabels[0]); // gets the new channels in an array // nchans_a[i] = new char [nc+1]; // copies the temporary value onto channels // strcpy(nchans_a[i], tmp_nlabels[0]); // parses up to the colon // char* pos_c = strstr(str_a[i], Itgl::COLON); // gets rid of the colon on pos_c // while (pos_c[0] == Itgl::COLON[0]) { pos_c++; } // parses the adjacent channels // status = Edf::parse_line(num_adj_chans, tmp_adlabels, pos_c, (char*)Itgl::COMMA); // stores the number of adjacent channels // num_chans_a[i] = num_adj_chans; // copies the temporary adjacent channels to lables1 // labels1_a[i] = new char*[num_adj_chans]; for (long j = 0; j < num_adj_chans; j++) { labels1_a[i][j] = new char [strlen(tmp_adlabels[j]) + 1]; strcpy(labels1_a[i][j], tmp_adlabels[j]); } // clean memory // Edf::cleanup(tmp_nlabels, pc); // clean memory // Edf::cleanup(tmp_adlabels, num_adj_chans); } // assign output variable related to the number of new channels // nl_i = num_clabels_d; // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: leaving parse_interp_channels\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } //***************************************************************************** // // private methods: filename processing methods // //***************************************************************************** // method: get_matching_filenames // // arguments: // char** fnames: list of filenames (output) // long& nf: number of filenames (output) // char* bfn: base filename (input) // char* fmt: filename modifier format specification (input) // // return: an boolean value indicating status // // This method permutes the basename and searches a directory for // matching filenames. // // Note that this method allocates memory inside of fnames, so that // memory must be cleaned up by the calling method. // bool Edf::get_matching_filenames(char** fnames_a, long& nf_a, const char* bfn_a, const char* fmt_a) { // declare some string buffers to hold filename pieces // char dirname[Itgl::MAX_LSTR_LENGTH]; char bname[Itgl::MAX_MSTR_LENGTH]; char ext[Itgl::MAX_SSTR_LENGTH]; char fname[Itgl::MAX_LSTR_LENGTH]; // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: getting filenames\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // get the directory name // if (Edf::create_matching_filename(dirname, bname, ext, fname, 0, bfn_a, fmt_a) == false) { return false; } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: [dir, base, ext, full] [%s %s %s %s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, dirname, bname, ext, fname); } // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fname = [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fname); } // open the directory: // note that if a directory is not specified, dirname is null, // and opendir handles this okay. // DIR *dir; struct dirent *ent; if ((dir = opendir(dirname)) == NULL) { return false; } // loop over all files starting with channel no. 0. loop until // the first file is not found. // nf_a = 0; bool found = true; while (found) { // form a new filename // if (Edf::create_matching_filename(dirname, bname, ext, fname, nf_a, bfn_a, fmt_a) == false) { closedir(dir); return false; } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: [dir, base, ext, full] [%s %s %s %s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, dirname, bname, ext, fname); } // find it in the directory // rewinddir(dir); bool match = false; while ((ent = readdir(dir)) != NULL) { if (strcmp(ent->d_name, bname) == 0) { match = true; break; } } if (match == true) { fnames_a[nf_a] = new char[strlen(fname) + 1]; strcpy(fnames_a[nf_a], fname); nf_a++; } else { found = false; break; } // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: %ld: [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nf_a - 1, fnames_a[nf_a - 1]); } } // close the directory // closedir(dir); // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done getting filenames\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: create_matching_filenames // // arguments: // // char* dirname: directory name (output) // char* bname: base filename (no directory, no extention) (output) // char* ext: file extention (output) // char* fname: matching filename (output) // long nf: index of file/channel (input) // char* bfn: base filename (input) // char* fmt: filename modifier format specification (input) // // return: an boolean value indicating status // // This method permutes a filename according to a spec provided by the user. // The full pathname is returned (fname) along with its components // (dirname + bname + 'fmt' + ext). // // Note that this method assumes memory is allocated. // bool Edf::create_matching_filename(char* dirname_a, char* bname_a, char* ext_a, char* fname_a, long nf_a, const char* bfn_a, const char* fmt_a) { // declare some string buffers needed to hold filename pieces // char fmt_string[Itgl::MAX_MSTR_LENGTH]; // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: [nf, bfn, fmt] [%ld, %s, %s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nf_a, bfn_a, fmt_a); } // locate some critical points in the input filename // char* ddelim = rindex((char*)bfn_a, Itgl::SLASH[0]); char* edelim = rindex((char*)bfn_a, Itgl::DOT[0]); // parse the input filename into a directory // if (ddelim == (char*)NULL) { strcpy(dirname_a, Itgl::DOT); } else { strncpy(dirname_a, bfn_a, ddelim - bfn_a); dirname_a[ddelim - bfn_a] = (char)NULL; } // parse the input filename into a basename and extension // if (edelim == (char*)NULL) { return false; } else { if (ddelim == (char*)NULL) { strncpy(bname_a, bfn_a, edelim - bfn_a); bname_a[edelim - bfn_a] = (char)NULL; } else { strncpy(bname_a, ddelim + 1, edelim - (ddelim + 1)); bname_a[edelim - (ddelim + 1)] = (char)NULL; } } strcpy(ext_a, edelim); // build a filename template for use with sprintf // strcpy(fmt_string, bname_a); strcat(fmt_string, fmt_a); strcat(fmt_string, ext_a); // form a new filename // sprintf(bname_a, fmt_string, nf_a); strcpy(fname_a, dirname_a); strcat(fname_a, Itgl::SLASH); strcat(fname_a, bname_a); if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: new filename [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fname_a); } // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done creating filename...\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } //***************************************************************************** // // private methods: interpolation methods // //***************************************************************************** // method: interpolate_average // // arguments: // VVectorDouble& new_chano: interpolated channels (output) // VVectorDouble& sigi: original signal in which the interpolated // channels will be added (input) // // return: a boolean value indicating status // // This method performs interpolation averaging the adjacent channels // related to each new channel to be interpolated. // bool Edf::interpolate_average(VVectorDouble& new_chano_a, VVectorDouble& sigi_a) { // declare local variables // bool status = true; // display a debug message // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: beginning interpolation by average\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // create output space to hold new channels: // resize new channel number of rows to the number of new channels // Edf::resize(new_chano_a, num_clabels_d, false); // store position of each adjacent channel within original signal: // this needs to be done since the interpolation relies on these // positions. // long* pos[num_clabels_d]; // loop over new channels numbers and take the position of its // adjacent channels one by one // for(long i = 0; i < num_clabels_d; i++) { // resize columns of position to the number of adjacent channels // related to new_channel i // pos[i] = new long[num_adj_chan_d[i]]; long j_end = num_adj_chan_d[i]; // loop over adjacent channels of new channel i // for(long j = 0; j < j_end; j++) { // take position of adjacent channel j in the original signal // if ((pos[i][j] = Edf::get_channel_pos(adj_chan_labels_d[i][j], smmode_d)) < 0) { fprintf(stdout, "Error: %s (line: %d) %s: no match for [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, adj_chan_labels_d[i][j]); return false; } } } // loop over all channels to be interpolated // for(long i = 0; i < num_clabels_d ; i++) { // number of adjacent channels related to channel i // long nadj_channels = num_adj_chan_d[i]; // create output space: // resize the number of columns to the number of columns of the // the adjacent channels. // long j_end = sigi_a[pos[i][0]].size(); Edf::resize(new_chano_a[i], j_end, false); // loop over the size of adjacent channels' columns // for(long j = 0; j < j_end; j++) { // initialize discrete point j of new channel i // new_chano_a[i][j] = 0; // variable to perform division // double sum = 1.0 / (double)nadj_channels; // loop over the number of adjacent channels // for(long k = 0; k < nadj_channels; k++) { // perform the sum of its discrete points with the values // of adjacent channel k - 1: // new_chano_a[i][j] += sigi_a[pos[i][k]][j]; } // perform division of all elements to complete average // of adjacent channels related to new channel i // new_chano_a[i][j] *= sum; } } // display a debug message // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done interpolation by average\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // clean up memory allocated to position of adjacent channels // for (long i = 0; i < num_clabels_d; i++) { delete [] pos[i]; } // exit gracefully // return status; } // // end of file