// file: $(NEDC_NFC)/class/cpp/Edf/edf_01.cc // // This file contains header-related and I/O-related methods. // // Revision History: // // 20240317 (JP): fixed a format issue with phys_max/min // 20240307 (JP): refactored code to expand set/get methods // // local include files // #include "Edf.h" //***************************************************************************** // // public methods: display methods // //***************************************************************************** // method: print_header // // arguments: // FILE* fp: file pointer to send the output (input) // char* prefix: a prefix to use before each field is printed // // return: an boolean value indicating status // // This method dumps the header to a file pointer (typically stdout). // It is assumed the header is already in memory. // bool Edf::print_header(FILE* fp_a, const char* prefix_a) { // (1) version information // fprintf(fp_a, "%sBlock 1: Version Information\n", prefix_a); fprintf(fp_a, "%s version = [%s]\n\n", prefix_a, hdr_version_d); // (2) local patient information // fprintf(fp_a, "%sBlock 2: Local Patient Information\n", prefix_a); fprintf(fp_a, "%s lpti_patient_id = [%s]\n", prefix_a, hdr_lpti_patient_id_d); fprintf(fp_a, "%s lpti_gender = [%s]\n", prefix_a, hdr_lpti_gender_d); fprintf(fp_a, "%s lpti_dob = [%s]\n", prefix_a, hdr_lpti_dob_d); fprintf(fp_a, "%s lpti_full_name = [%s]\n", prefix_a, hdr_lpti_full_name_d); fprintf(fp_a, "%s lpti_age = [%s]\n\n", prefix_a, hdr_lpti_age_d); // (3) local recording information // fprintf(fp_a, "%sBlock 3: Local Recording Information\n", prefix_a); fprintf(fp_a, "%s lrci_start_date_label = [%s]\n", prefix_a, hdr_lrci_start_date_label_d); fprintf(fp_a, "%s lrci_start_date = [%s]\n", prefix_a, hdr_lrci_start_date_d); fprintf(fp_a, "%s lrci_eeg_id = [%s]\n", prefix_a, hdr_lrci_eeg_id_d); fprintf(fp_a, "%s lrci_tech = [%s]\n", prefix_a, hdr_lrci_tech_d); fprintf(fp_a, "%s lrci_machine = [%s]\n\n", prefix_a, hdr_lrci_machine_d); // (4) general header information // fprintf(fp_a, "%sBlock 4: General Header Information\n", prefix_a); fprintf(fp_a, "%s ghdi_start_date = [%s]\n", prefix_a, hdr_ghdi_start_date_d); fprintf(fp_a, "%s ghdi_start_time = [%s]\n", prefix_a, hdr_ghdi_start_time_d); fprintf(fp_a, "%s ghdi_hsize = [%ld]\n", prefix_a, hdr_ghdi_hsize_d); fprintf(fp_a, "%s ghdi_file_type = [%s]\n", prefix_a, hdr_ghdi_file_type_d); fprintf(fp_a, "%s ghdi_reserved = [%s]\n", prefix_a, hdr_ghdi_reserved_d); fprintf(fp_a, "%s ghdi_num_recs = [%ld]\n", prefix_a, hdr_ghdi_num_recs_d); fprintf(fp_a, "%s ghdi_dur_rec = [%lf]\n", prefix_a, hdr_ghdi_dur_rec_d); fprintf(fp_a, "%s ghdi_nsig_rec = [%ld]\n\n", prefix_a, hdr_ghdi_nsig_rec_d); // (5) channel-specific information // fprintf(fp_a, "%sBlock 5: Channel-Specific Information\n", prefix_a); fprintf(fp_a,"%s chan_labels (%ld) = ", prefix_a, hdr_ghdi_nsig_rec_d); for (int i = 0; i < hdr_ghdi_nsig_rec_d - 1; i++) { fprintf(fp_a, "[%s], ", hdr_chan_labels_d[i]); } fprintf(fp_a, "[%s]\n", hdr_chan_labels_d[hdr_ghdi_nsig_rec_d - 1]); fprintf(fp_a,"%s chan_trans_type (%ld) = ", prefix_a, hdr_ghdi_nsig_rec_d); for (int i = 0; i < hdr_ghdi_nsig_rec_d - 1; i++) { fprintf(fp_a, "[%s], ", hdr_chan_trans_types_d[i]); } fprintf(fp_a, "[%s]\n", hdr_chan_trans_types_d[hdr_ghdi_nsig_rec_d - 1]); fprintf(fp_a,"%s chan_phys_dim (%ld) = ", prefix_a, hdr_ghdi_nsig_rec_d); for (int i = 0; i < hdr_ghdi_nsig_rec_d - 1; i++) { fprintf(fp_a, "[%s], ", hdr_chan_phys_dim_d[i]); } fprintf(fp_a, "[%s]\n", hdr_chan_phys_dim_d[hdr_ghdi_nsig_rec_d - 1]); fprintf(fp_a,"%s chan_phys_min (%ld) = ", prefix_a, hdr_ghdi_nsig_rec_d); for (int i = 0; i < hdr_ghdi_nsig_rec_d - 1; i++) { fprintf(fp_a, "[%10.3f], ", hdr_chan_phys_min_d[i]); } fprintf(fp_a, "[%10.3f]\n", hdr_chan_phys_min_d[hdr_ghdi_nsig_rec_d - 1]); fprintf(fp_a,"%s chan_phys_max (%ld) = ", prefix_a, hdr_ghdi_nsig_rec_d); for (int i = 0; i < hdr_ghdi_nsig_rec_d - 1; i++) { fprintf(fp_a, "[%10.3f], ", hdr_chan_phys_max_d[i]); } fprintf(fp_a, "[%10.3f]\n", hdr_chan_phys_max_d[hdr_ghdi_nsig_rec_d - 1]); fprintf(fp_a,"%s chan_dig_min (%ld) = ", prefix_a, hdr_ghdi_nsig_rec_d); for (int i = 0; i < hdr_ghdi_nsig_rec_d - 1; i++) { fprintf(fp_a, "[%10ld], ", hdr_chan_dig_min_d[i]); } fprintf(fp_a, "[%10ld]\n", hdr_chan_dig_min_d[hdr_ghdi_nsig_rec_d - 1]); fprintf(fp_a,"%s chan_dig_max (%ld) = ", prefix_a, hdr_ghdi_nsig_rec_d); for (int i = 0; i < hdr_ghdi_nsig_rec_d - 1; i++) { fprintf(fp_a, "[%10ld], ", hdr_chan_dig_max_d[i]); } fprintf(fp_a, "[%10ld]\n", hdr_chan_dig_max_d[hdr_ghdi_nsig_rec_d - 1]); fprintf(fp_a,"%s chan_prefilt (%ld) = ", prefix_a, hdr_ghdi_nsig_rec_d); for (int i = 0; i < hdr_ghdi_nsig_rec_d - 1; i++) { fprintf(fp_a, "[%s], ", hdr_chan_prefilt_d[i]); } fprintf(fp_a, "[%s]\n", hdr_chan_prefilt_d[hdr_ghdi_nsig_rec_d - 1]); fprintf(fp_a,"%s chan_rec_size (%ld) = ", prefix_a, hdr_ghdi_nsig_rec_d); for (int i = 0; i < hdr_ghdi_nsig_rec_d - 1; i++) { fprintf(fp_a, "[%10ld], ", hdr_chan_rec_size_d[i]); } fprintf(fp_a, "[%10ld]\n", hdr_chan_rec_size_d[hdr_ghdi_nsig_rec_d - 1]); fprintf(fp_a, "%s\n", prefix_a); // (6) derived values // fprintf(fp_a, "%sBlock 6: Derived Values\n", prefix_a); fprintf(fp_a, "%s hdr_sample_frequency = %10.1f\n", prefix_a, hdr_sample_frequency_d); fprintf(fp_a, "%s hdr_num_channels_signal = %10ld\n", prefix_a, hdr_num_channels_signal_d); fprintf(fp_a, "%s hdr_num_channels_annotation = %10ld\n", prefix_a, hdr_num_channels_annotation_d); fprintf(fp_a, "%s duration of recording (secs) = %10.1f\n", prefix_a, (float)(hdr_ghdi_dur_rec_d * hdr_ghdi_num_recs_d)); fprintf(fp_a, "%s per channel sample frequencies:\n", prefix_a); for (long i = 0; i < hdr_ghdi_nsig_rec_d; i++) { fprintf(fp_a, "%s channel[%4ld]: %10.1f Hz (%s)\n", prefix_a, i, get_sample_frequency(i), hdr_chan_labels_d[i]); } // exit gracefully // return true; } // method: print_header // // arguments: // char* fname: filename (input) // FILE* fp: file pointer to send the output (input) // char* prefix: a prefix to use before each field is printed // // return: an boolean value indicating status // // This method dumps the header to a file pointer (typically stdout). // bool Edf::print_header(const char* fname_a, FILE* fp_a, const char* prefix_a) { // declare local variables // VVectorDouble sig; // read the header from a file // if (!Edf::get_header(fname_a)) { fprintf(stdout, "Error: %s (line: %d) %s: error opening (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fname_a); } // print the header from a file // Edf::print_header(fp_a, prefix_a); // exit gracefully // return true; } // method: debug // // arguments: // VVectorDouble& sig: the signal to be debugged (input) // FILE* fp: the output file pointer (input) // // return: a boolean value indicating status // // This method generates some simple debug information for a signal. // bool Edf::debug(VVectorDouble& sig_a, FILE* fp_a) { // display the channel information // fprintf(fp_a, "no. channels = %ld\n", (long)sig_a.size()); fprintf(fp_a, "no. samples per channel = %ld\n", (long)sig_a[0].size()); // display some signal values // long max_nchans = (long)Itgl::min(3, sig_a.size()); long max_nsamps = (long)Itgl::min(3, sig_a[0].size()); for (long i = 0; i < max_nchans; i++) { fprintf(fp_a, "channel number %ld:\n", i); for (long j = 0; j < max_nsamps; j++) { fprintf(fp_a, "sig[%ld][%ld] = %f\n", i, j, sig_a[i][j]); } } // exit gracefully // return true; } // method: debug // // arguments: // VVVectorDouble& feat: the feature stream to be debugged (input) // FILE* fp: the output file pointer (input) // // return: a boolean value indicating status // // This method generates some simple debug information for a feature stream. // bool Edf::debug(VVVectorDouble& feat_a, FILE* fp_a) { // display the channel information // fprintf(fp_a, "no. channels = %ld\n", (long)feat_a.size()); fprintf(fp_a, "no. frames per channel = %ld\n", (long)feat_a[0].size()); fprintf(fp_a, "no. features per frame = %ld\n", (long)feat_a[0][0].size()); // display some signal values // long max_nchans = (long)Itgl::min(3, feat_a.size()); long max_nframes = (long)Itgl::min(3, feat_a[0].size()); for (long i = 0; i < max_nchans; i++) { fprintf(fp_a, "channel number %ld:\n", i); for (long j = 0; j < max_nframes; j++) { fprintf(fp_a, "frame number %ld:\n", j); for (long k = 0; j < (long)feat_a[i][j].size(); j++) { fprintf(fp_a, "feat[%ld][%ld][%ld] = %f\n", i, j, k, feat_a[i][j][k]); } } } // exit gracefully // return true; } //***************************************************************************** // // public methods: header-related methods // //***************************************************************************** // method: get_header // // arguments: // char* fn: the file name (input) // // return: a boolean value indicating status // // This method opens a file and reads the header. // bool Edf::get_header(const char* fn_a) { // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching an EDF header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // open the file if it is not already open // if (fp_d == (FILE*)NULL) { fp_d = fopen(fn_a, "r"); if (fp_d == (FILE*)NULL) { fprintf(stdout, "Error: %s (line: %d) %s: error opening (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); return false; } } // get the header // bool status = Edf::get_header(fp_d); if (status == false) { fprintf(stdout, "Error: %s (line: %d) %s: error fetching the header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); return false; } // exit gracefully // return true; } // method: get_header // // arguments: // FILE* fp: file pointer (input) // // return: a boolean value indicating status // // This method fetches the header from an EDF file. // bool Edf::get_header(FILE* fp_a) { // declare local variables // long nbytes = 0; long num_items; char buf[MAX_TMP_BSIZE]; char cbuf[EDF_LPTI_TSIZE]; // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching an EDF header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // rewind the file // rewind(fp_a); // (1) version information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (1)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } if ((nbytes = fread(hdr_version_d, 1, EDF_VERS_BSIZE, fp_a)) != EDF_VERS_BSIZE) { return false; } hdr_version_d[EDF_VERS_BSIZE] = (char)NULL; if (strcmp(hdr_version_d, EDF_VERS) != 0) { return false; } // (2) local patient information // // Unfortunately, some EDF files don't contain all the information // they should. This often occurs because the deidentification // process overwrites this information. So we zero out the buffers // that won't be filled if the information is missing. // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (2)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } hdr_lpti_patient_id_d[0] = (char)NULL; hdr_lpti_gender_d[0] = (char)NULL; hdr_lpti_dob_d[0] = (char)NULL; hdr_lpti_full_name_d[0] = (char)NULL; hdr_lpti_age_d[0] = (char)NULL; if ((nbytes = fread(buf, 1, EDF_LPTI_BSIZE, fp_a)) != EDF_LPTI_BSIZE) { return false; } buf[EDF_LPTI_BSIZE] = (char)NULL; num_items = sscanf(buf, "%s%s%s%s%s", hdr_lpti_patient_id_d, hdr_lpti_gender_d, hdr_lpti_dob_d, hdr_lpti_full_name_d, hdr_lpti_age_d); if (num_items == 0) { return false; } // (3) local recording information // // Unfortunately, some EDF files don't contain all the information // they should. This often occurs because the deidentification // process overwrites this information. So we zero out the buffers // that won't be filled if the information is missing. // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (3)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } hdr_lrci_start_date_label_d[0] = (char)NULL; hdr_lrci_start_date_d[0] = (char)NULL; hdr_lrci_eeg_id_d[0] = (char)NULL; hdr_lrci_tech_d[0] = (char)NULL; hdr_lrci_machine_d[0] = (char)NULL; if ((nbytes = fread(buf, 1, EDF_LRCI_BSIZE, fp_a)) != EDF_LRCI_BSIZE) { return false; } buf[EDF_LRCI_BSIZE] = (char)NULL; num_items = sscanf(buf, "%s%s%s%s%s", hdr_lrci_start_date_label_d, hdr_lrci_start_date_d, hdr_lrci_eeg_id_d, hdr_lrci_tech_d, hdr_lrci_machine_d); if (num_items == 0) { return false; } // (4) general header information // // get the fourth block of data (non-local information) // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (4)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } if ((nbytes = fread(buf, 1, EDF_GHDI_BSIZE, fp_a)) != EDF_GHDI_BSIZE) { return false; } buf[EDF_GHDI_BSIZE] = (char)NULL; memcpy(hdr_ghdi_start_date_d, buf + 0, 8); hdr_ghdi_start_date_d[8] = (char)NULL; memcpy(hdr_ghdi_start_time_d, buf + 8, 8); hdr_ghdi_start_time_d[8] = (char)NULL; memcpy(&cbuf, buf + 16, 8); cbuf[8] = (char)NULL; hdr_ghdi_hsize_d = atof(cbuf); memcpy(hdr_ghdi_file_type_d, buf + 24, 5); hdr_ghdi_file_type_d[5] = (char)NULL; memcpy(hdr_ghdi_reserved_d, buf + 29, 39); hdr_ghdi_reserved_d[39] = (char)NULL; memcpy(&cbuf, buf + 68, 8); cbuf[8] = (char)NULL; hdr_ghdi_num_recs_d = atoi(cbuf); memcpy(&cbuf, buf + 76, 8); cbuf[8] = (char)NULL; hdr_ghdi_dur_rec_d = atof(cbuf); memcpy(&cbuf, buf + 84, 4); cbuf[4] = (char)NULL; hdr_ghdi_nsig_rec_d = atoi(cbuf); // (5) contains channel-specific data // // (5a) read channel labels // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5a)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } long buf_end = hdr_ghdi_nsig_rec_d * EDF_LABL_BSIZE; if ((nbytes = fread(buf, 1, buf_end, fp_a)) != buf_end) { return false; } buf[buf_end] = (char)NULL; hdr_num_channels_annotation_d = 0; for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { // create space for the labels // Edf::resize(hdr_chan_labels_d[i], EDF_LABL_BSIZE + 1, false); // look for the annotation labels // char tbuf[EDF_LABL_BSIZE+1]; strncpy(tbuf, &buf[EDF_LABL_BSIZE * i], EDF_LABL_BSIZE); tbuf[EDF_LABL_BSIZE] = (char)NULL; Edf::trim_whitespace_and_upcase(hdr_chan_labels_d[i], tbuf); if ((strstr(hdr_chan_labels_d[i], ANNOTATION) != (char*)NULL)) { hdr_num_channels_annotation_d++; } } // (5b) read the transducer type // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5b)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } buf_end = hdr_ghdi_nsig_rec_d * EDF_TRNT_BSIZE; if ((nbytes = fread(buf, 1, buf_end, fp_a)) != buf_end) { return false; } buf[buf_end] = (char)NULL; for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { // create space for the transducer type // Edf::resize(hdr_chan_trans_types_d[i], EDF_TRNT_BSIZE + 1, false); // copy it: use memcpy here to keep -Wall happy // char tbuf[EDF_TRNT_BSIZE + 1]; memcpy(tbuf, &buf[EDF_TRNT_BSIZE * i], EDF_TRNT_BSIZE); tbuf[EDF_TRNT_BSIZE] = (char)NULL; trim_whitespace(hdr_chan_trans_types_d[i], tbuf); } // (5c) read the physical dimension // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5c)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } buf_end = hdr_ghdi_nsig_rec_d * EDF_PDIM_BSIZE; if ((nbytes = fread(buf, 1, buf_end, fp_a)) != buf_end) { return false; } buf[buf_end] = (char)NULL; for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { // create space for the phys dimension // Edf::resize(hdr_chan_phys_dim_d[i], EDF_PDIM_BSIZE + 1, false); // copy it // char tbuf[EDF_PDIM_BSIZE + 1]; memcpy(tbuf, &buf[EDF_PDIM_BSIZE * i], EDF_PDIM_BSIZE); tbuf[EDF_PDIM_BSIZE] = (char)NULL; trim_whitespace(hdr_chan_phys_dim_d[i], tbuf); } // (5d) read the physical minimum // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5d)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } buf_end = hdr_ghdi_nsig_rec_d * EDF_PMIN_BSIZE; if ((nbytes = fread(buf, 1, buf_end, fp_a)) != buf_end) { return false; } buf[buf_end] = (char)NULL; for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { char tstr[EDF_PMIN_BSIZE + 1]; strncpy(tstr, &buf[EDF_PMIN_BSIZE * i], EDF_PMIN_BSIZE); tstr[EDF_PMIN_BSIZE] = (char)NULL; hdr_chan_phys_min_d[i] = atof(tstr); } // (5e) read the physical maximum // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5e)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } buf_end = hdr_ghdi_nsig_rec_d * EDF_PMAX_BSIZE; if ((nbytes = fread(buf, 1, buf_end, fp_a)) != buf_end) { return false; } buf[buf_end] = (char)NULL; for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { char tstr[EDF_PMAX_BSIZE + 1]; strncpy(tstr, &buf[EDF_PMAX_BSIZE * i], EDF_PMAX_BSIZE); tstr[EDF_PMAX_BSIZE] = (char)NULL; hdr_chan_phys_max_d[i] = atof(tstr); } // (5f) read the digital minimums // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5f)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } buf_end = hdr_ghdi_nsig_rec_d * EDF_DMIN_BSIZE; if ((nbytes = fread(buf, 1, buf_end, fp_a)) != buf_end) { return false; } buf[buf_end] = (char)NULL; for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { char tstr[EDF_DMIN_BSIZE + 1]; strncpy(tstr, &buf[EDF_DMIN_BSIZE * i], EDF_DMIN_BSIZE); tstr[EDF_DMIN_BSIZE] = (char)NULL; hdr_chan_dig_min_d[i] = atoi(tstr); } // (5g) read the digital maximums // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5g)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } buf_end = hdr_ghdi_nsig_rec_d * EDF_DMAX_BSIZE; if ((nbytes = fread(buf, 1, buf_end, fp_a)) != buf_end) { return false; } buf[buf_end] = (char)NULL; for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { hdr_chan_dig_max_d[i] = EDF_SIG_MAXVAL; char tstr[EDF_DMAX_BSIZE + 1]; strncpy(tstr, &buf[EDF_DMAX_BSIZE * i], EDF_DMAX_BSIZE); tstr[EDF_DMAX_BSIZE] = (char)NULL; hdr_chan_dig_max_d[i] = atoi(tstr); } // (5h) read the prefilt labels // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5h)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } buf_end = hdr_ghdi_nsig_rec_d * EDF_PREF_BSIZE; if ((nbytes = fread(buf, 1, buf_end, fp_a)) != buf_end) { return false; } buf[buf_end] = (char)NULL; for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { // create space for the prefilt // Edf::resize(hdr_chan_prefilt_d[i], EDF_PREF_BSIZE + 1, false); // copy it // char tbuf[EDF_PREF_BSIZE + 1]; memcpy(tbuf, &buf[EDF_PREF_BSIZE * i], EDF_PREF_BSIZE); tbuf[EDF_PREF_BSIZE] = (char)NULL; trim_whitespace(hdr_chan_prefilt_d[i], tbuf); } // (5i) read the rec sizes // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5i)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } buf_end = hdr_ghdi_nsig_rec_d * EDF_RECS_BSIZE; if ((nbytes = fread(buf, 1, buf_end, fp_a)) != buf_end) { return false; } buf[buf_end] = (char)NULL; for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { char tstr[EDF_RECS_BSIZE + 1]; strncpy(tstr, &buf[EDF_RECS_BSIZE * i], EDF_RECS_BSIZE); tstr[EDF_RECS_BSIZE] = (char)NULL; hdr_chan_rec_size_d[i] = atoi(tstr); } // (5j) the last chunk of the header is reserved space // that we don't need to read. however, we need to advance the // file pointer to be safe. // fseek(fp_a, hdr_ghdi_hsize_d, SEEK_SET); if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5j)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // (6) compute some derived values // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (6)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } hdr_sample_frequency_d = (float)hdr_chan_rec_size_d[0] / (float)hdr_ghdi_dur_rec_d; hdr_num_channels_signal_d = hdr_ghdi_nsig_rec_d - hdr_num_channels_annotation_d; // exit gracefully // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done fetching an EDF header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } return true; } // method: get_header_size // // arguments: // FILE* fp: file pointer (input) // // return: a long containing the header size in bytes // // This method returns the header size. If the header size is not // known, it reads the header. // long Edf::get_header_size(FILE* fp_a) { // check for a NULL file pointer // if ((fp_a != (FILE*)NULL) && (hdr_ghdi_hsize_d < 0)) { if (!get_header(fp_a)) { fprintf(stdout, "Error: %s (line: %d) %s: error fetching header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); return ERR_FILE; } } // return the header size // return hdr_ghdi_hsize_d; } // method: put_header // // arguments: // FILE* fp: file pointer (input) // // return: a boolean value indicating status // // This method writes the header to a binary edf file. // bool Edf::put_header(FILE* fp_a) { // declare local variables // long len; long nbytes = 0; char buf[MAX_TMP_BSIZE]; char cbuf[EDF_LPTI_TSIZE]; // set size of tbuf so that it accounts for null character // char tbuf[EDF_LPTI_BSIZE + 1]; // clean up fields that might have bad characters // memset(hdr_ghdi_reserved_d, (int)Itgl::SPACE[0], 39); // keep a counter of the number of bytes written for debug purposes // long nbytes_written = 0; // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: putting an EDF header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // rewind the file // rewind(fp_a); // (1) version information // if ((nbytes = fwrite(hdr_version_d, 1, EDF_VERS_BSIZE, fp_a)) != EDF_VERS_BSIZE) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (1) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (2) local patient information: // we need to fill null characters with spaces // sprintf(buf, "%s %s %s %s %s", hdr_lpti_patient_id_d, hdr_lpti_gender_d, hdr_lpti_dob_d, hdr_lpti_full_name_d, hdr_lpti_age_d); len = strlen(buf); for (long i = len; i <= EDF_LPTI_BSIZE; i++) { buf[i] = Itgl::SPACE[0]; } if ((nbytes = fwrite(buf, 1, EDF_LPTI_BSIZE, fp_a)) != EDF_LPTI_BSIZE) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (2) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (3) local recording information // sprintf(buf, "%s %s %s %s %s", hdr_lrci_start_date_label_d, hdr_lrci_start_date_d, hdr_lrci_eeg_id_d, hdr_lrci_tech_d, hdr_lrci_machine_d); Edf::pad_whitespace(buf, EDF_LRCI_BSIZE); if ((nbytes = fwrite(buf, 1, EDF_LRCI_BSIZE, fp_a)) != EDF_LRCI_BSIZE) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (3) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (4) general header information // memcpy(buf + 0, hdr_ghdi_start_date_d, 8); memcpy(buf + 8, hdr_ghdi_start_time_d, 8); sprintf(cbuf, "%ld", hdr_ghdi_hsize_d); Edf::pad_whitespace(cbuf, 8); memcpy(buf + 16, &cbuf, 8); memcpy(buf + 24, hdr_ghdi_file_type_d, 5); memcpy(tbuf, hdr_ghdi_reserved_d, 39); memcpy(buf + 29, tbuf, 39); sprintf(cbuf, "%ld", hdr_ghdi_num_recs_d); Edf::pad_whitespace(cbuf, 8); memcpy(buf + 68, &cbuf, 8); sprintf(cbuf, "%f", hdr_ghdi_dur_rec_d); Edf::pad_whitespace(cbuf, 8); memcpy(buf + 76, &cbuf, 8); sprintf(cbuf, "%ld", hdr_ghdi_nsig_rec_d); Edf::pad_whitespace(cbuf, 4); memcpy(buf + 84, &cbuf, 4); if ((nbytes = fwrite(buf, 1, EDF_GHDI_BSIZE, fp_a)) != EDF_GHDI_BSIZE) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (4) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (5) contains channel-specific data // // (5a) write channel labels // for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { sprintf(tbuf, "%s", hdr_chan_labels_d[i]); Edf::pad_whitespace(tbuf, EDF_LABL_BSIZE); tbuf[EDF_LABL_BSIZE] = (char)NULL; strcpy(&buf[EDF_LABL_BSIZE * i], tbuf); } long buf_end = hdr_ghdi_nsig_rec_d * EDF_LABL_BSIZE; if ((nbytes = fwrite(buf, 1, buf_end, fp_a)) != buf_end) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (5a) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (5b) write the transducer type // for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { sprintf(tbuf, "%s", hdr_chan_trans_types_d[i]); Edf::pad_whitespace(tbuf, EDF_TRNT_BSIZE); tbuf[EDF_TRNT_BSIZE] = (char)NULL; strcpy(&buf[EDF_TRNT_BSIZE * i], tbuf); } buf_end = hdr_ghdi_nsig_rec_d * EDF_TRNT_BSIZE; if ((nbytes = fwrite(buf, 1, buf_end, fp_a)) != buf_end) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (5b) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (5c) write the physical dimension // for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { sprintf(tbuf, "%s", hdr_chan_phys_dim_d[i]); Edf::pad_whitespace(tbuf, EDF_PDIM_BSIZE); tbuf[EDF_PDIM_BSIZE] = (char)NULL; strcpy(&buf[EDF_PDIM_BSIZE * i], tbuf); } buf_end = hdr_ghdi_nsig_rec_d * EDF_PDIM_BSIZE; if ((nbytes = fwrite(buf, 1, buf_end, fp_a)) != buf_end) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (5c) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (5d and 5e) write the physical minimum and maximum: // note that by convention, we write the 7 most significant digits // of the magnitude. // // (5d-1): find the max magnitude and grab the 7 most significant digits // the negative sign for the min consumes one character, so // we use (EDF_PDIM_BSIZE - 1). // float phys_maxmag[hdr_ghdi_nsig_rec_d]; char phys_maxmag_str[hdr_ghdi_nsig_rec_d][EDF_PDIM_BSIZE + 1]; char tstr[EDF_TRNT_BSIZE]; for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { phys_maxmag[i] = Itgl::maxmag(hdr_chan_phys_min_d[i], hdr_chan_phys_max_d[i]); sprintf(tstr, "%-20.10f", phys_maxmag[i]); memcpy(phys_maxmag_str[i], tstr, EDF_PDIM_BSIZE - 1); } // (5d-2): write min to a file by adding "-" to the min string. // for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { long loc = EDF_PMIN_BSIZE * i; buf[loc] = Itgl::MINUS[0]; loc++; memcpy(&buf[loc], phys_maxmag_str[i], EDF_PDIM_BSIZE - 1); } buf_end = hdr_ghdi_nsig_rec_d * EDF_PMIN_BSIZE; if ((nbytes = fwrite(buf, 1, buf_end, fp_a)) != buf_end) { return false; } nbytes_written += nbytes; // (5e): write max to a file by adding a space at the end // for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { long loc = EDF_PMIN_BSIZE * i; memcpy(&buf[loc], phys_maxmag_str[i], EDF_PDIM_BSIZE - 1); buf[loc + EDF_PDIM_BSIZE - 1] = Itgl::SPACE[0]; } buf_end = hdr_ghdi_nsig_rec_d * EDF_PMIN_BSIZE; if ((nbytes = fwrite(buf, 1, buf_end, fp_a)) != buf_end) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: %s (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, "wrote section (5d and 5e)", nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (5f) write the digital minimums // for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { sprintf(tbuf, "%ld", hdr_chan_dig_min_d[i]); Edf::pad_whitespace(tbuf, EDF_DMIN_BSIZE); tbuf[EDF_DMIN_BSIZE] = (char)NULL; strcpy(&buf[EDF_DMIN_BSIZE * i], tbuf); } buf_end = hdr_ghdi_nsig_rec_d * EDF_DMIN_BSIZE; if ((nbytes = fwrite(buf, 1, buf_end, fp_a)) != buf_end) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (5f) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (5g) write the digital maximums // for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { sprintf(tbuf, "%ld", hdr_chan_dig_max_d[i]); Edf::pad_whitespace(tbuf, EDF_DMAX_BSIZE); tbuf[EDF_DMAX_BSIZE] = (char)NULL; strcpy(&buf[EDF_DMAX_BSIZE * i], tbuf); } buf_end = hdr_ghdi_nsig_rec_d * EDF_DMAX_BSIZE; if ((nbytes = fwrite(buf, 1, buf_end, fp_a)) != buf_end) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (5g) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (5h) write the prefilt labels // for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { sprintf(tbuf, "%s", hdr_chan_prefilt_d[i]); Edf::pad_whitespace(tbuf, EDF_PREF_BSIZE); tbuf[EDF_PREF_BSIZE] = (char)NULL; strcpy(&buf[EDF_PREF_BSIZE * i], tbuf); } buf_end = hdr_ghdi_nsig_rec_d * EDF_PREF_BSIZE; if ((nbytes = fwrite(buf, 1, buf_end, fp_a)) != buf_end) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (5h) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (5i) write the rec sizes // for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { sprintf(tbuf, "%ld", hdr_chan_rec_size_d[i]); Edf::pad_whitespace(tbuf, EDF_RECS_BSIZE); tbuf[EDF_RECS_BSIZE] = (char)NULL; strcpy(&buf[EDF_RECS_BSIZE * i], tbuf); } buf_end = hdr_ghdi_nsig_rec_d * EDF_RECS_BSIZE; if ((nbytes = fwrite(buf, 1, buf_end, fp_a)) != buf_end) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (5i) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // (5j) the last chunk of the header is reserved space: // write blanks long nbytes_left = hdr_ghdi_hsize_d - nbytes_written; memset(buf, Itgl::SPACE[0], nbytes_left); if ((nbytes = fwrite(buf, 1, nbytes_left, fp_a)) != nbytes_left) { return false; } nbytes_written += nbytes; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: wrote section (5j) (%ld / %ld, %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes, nbytes_written, hdr_ghdi_hsize_d); } // exit gracefully // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done wrote an EDF header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } return true; } // method: copy_header // // arguments: // Edf& hd: header (input) // // return: a boolean value indicating status // // This method copies the header from the argument Edf object to // the destination object (this). // bool Edf::copy_header(Edf& hd) { // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying an EDF header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // (1) version information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (1)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } hdr_version_d[EDF_VERS_BSIZE] = (char)NULL; memcpy(hdr_version_d, hd.hdr_version_d, EDF_VERS_BSIZE); // (2) local patient information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (2)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } strcpy(hdr_lpti_patient_id_d, hd.hdr_lpti_patient_id_d); strcpy(hdr_lpti_gender_d, hd.hdr_lpti_gender_d); strcpy(hdr_lpti_dob_d, hd.hdr_lpti_dob_d); strcpy(hdr_lpti_full_name_d, hd.hdr_lpti_full_name_d); strcpy(hdr_lpti_age_d, hd.hdr_lpti_age_d); // (3) local recording information // // Unfortunately, some EDF files don't contain all the information // they should. This often occurs because the deidentification // process overwrites this information. So we zero out the buffers // that won't be filled if the information is missing. // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (3)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } strcpy(hdr_lrci_start_date_label_d, hd.hdr_lrci_start_date_label_d); strcpy(hdr_lrci_start_date_d, hd.hdr_lrci_start_date_d); strcpy(hdr_lrci_eeg_id_d, hd.hdr_lrci_eeg_id_d); strcpy(hdr_lrci_tech_d, hd.hdr_lrci_tech_d); strcpy(hdr_lrci_machine_d, hd.hdr_lrci_machine_d); // (4) general header information // // get the fourth block of data (non-local information) // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (4)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } strcpy(hdr_ghdi_start_date_d, hd.hdr_ghdi_start_date_d); strcpy(hdr_ghdi_start_time_d, hd.hdr_ghdi_start_time_d); hdr_ghdi_hsize_d = hd.hdr_ghdi_hsize_d; strcpy(hdr_ghdi_file_type_d, hd.hdr_ghdi_file_type_d); strcpy(hdr_ghdi_reserved_d, hd.hdr_ghdi_reserved_d); hdr_ghdi_num_recs_d = hd.hdr_ghdi_num_recs_d; hdr_ghdi_dur_rec_d = hd.hdr_ghdi_dur_rec_d; hdr_ghdi_nsig_rec_d = hd.hdr_ghdi_nsig_rec_d; // (5) contains channel-specific data // // (5a) read channel labels // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (5a)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { // create space for the labels // Edf::resize(hdr_chan_labels_d[i], EDF_LABL_BSIZE + 1, false); // copy the annotation labels // strcpy(hdr_chan_labels_d[i], hd.hdr_chan_labels_d[i]); } hdr_num_channels_annotation_d = hd.hdr_num_channels_annotation_d; // (5b) read the transducer type // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (5b)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { // create space for the transducer type and copy // Edf::resize(hdr_chan_trans_types_d[i], EDF_TRNT_BSIZE + 1, false); memcpy(hdr_chan_trans_types_d[i], hd.hdr_chan_trans_types_d[i], EDF_TRNT_BSIZE); } // (5c) read the physical dimension // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (5c)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { // create space for the phys dimension and copy // Edf::resize(hdr_chan_phys_dim_d[i], EDF_PDIM_BSIZE + 1, false); memcpy(hdr_chan_phys_dim_d[i], hd.hdr_chan_phys_dim_d[i], EDF_PDIM_BSIZE); } // (5d) read the physical minimum // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (5d)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { hdr_chan_phys_min_d[i] = hd.hdr_chan_phys_min_d[i]; } // (5e) read the physical maximum // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (5e)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { hdr_chan_phys_max_d[i] = hd.hdr_chan_phys_max_d[i]; } // (5f) read the digital minimums // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (5f)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { hdr_chan_dig_min_d[i] = hd.hdr_chan_dig_min_d[i]; } // (5g) read the digital maximums // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (5g)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { hdr_chan_dig_max_d[i] = hd.hdr_chan_dig_max_d[i]; } // (5h) read the prefilt labels // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (5h)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { // create space for the prefilt and copy // Edf::resize(hdr_chan_prefilt_d[i], EDF_PREF_BSIZE + 1, false); memcpy(hdr_chan_prefilt_d[i], hd.hdr_chan_prefilt_d[i], EDF_PREF_BSIZE); } // (5i) read the rec sizes // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: fetching (5i)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } for (int i = 0; i < hdr_ghdi_nsig_rec_d; i++) { hdr_chan_rec_size_d[i] = hd.hdr_chan_rec_size_d[i]; } // (5j) the last chunk of the header is reserved space that // doesn't need to be copied. // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (5j)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // (6) compute some derived values // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: copying (6)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } hdr_sample_frequency_d = (float)hdr_chan_rec_size_d[0] / (float)hdr_ghdi_dur_rec_d; hdr_num_channels_signal_d = hdr_ghdi_nsig_rec_d - hdr_num_channels_annotation_d; // exit gracefully // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done copying an EDF header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } return true; } //***************************************************************************** // // public methods: read/write signal data // //***************************************************************************** // method: get_signal // // arguments: // VVectorDouble& sig: the EEG signal data (output) // FILE* fp: an open file pointer // bool sc: scale the signal based on header data (input) // the default is to do the standard EDF scaling // // return: a boolean value indicating status // // This method reads the signal data from an open file. // bool Edf::get_signal(VVectorDouble& sig_a, FILE* fp_a, bool sc_a) { // get the size of the file on disk // rewind(fp_a); fseek(fp_a, 0L, SEEK_END); long file_size_in_bytes = ftell(fp_a); rewind(fp_a); if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: file size = %ld bytes\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, file_size_in_bytes); } // position the file to the beginning of the data // using the header information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: starting get_signal\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } if (fseek(fp_a, hdr_ghdi_hsize_d, SEEK_SET)) { return false; } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: file positioning complete\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // create space to hold the entire signal // Edf::resize(sig_a, hdr_ghdi_nsig_rec_d, false); for (long i = 0; i < hdr_ghdi_nsig_rec_d; i++) { Edf::resize(sig_a[i], hdr_ghdi_num_recs_d * hdr_chan_rec_size_d[i], false); if ((dbgl_d > Dbgl::DETAILED) && (i < DEF_DBG_NF)) { fprintf(stdout, "%s (line: %d) %s: sig_a dimensions (%ld row, %ld cols)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, hdr_ghdi_num_recs_d * hdr_chan_rec_size_d[i]); } } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: signal vector resized\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // create temporary space for I/O // long bsize = 0; for (long i = 0; i < (long)sig_a.size(); i++) { bsize = Itgl::max(hdr_chan_rec_size_d[i], bsize); } short int buf[bsize]; // loop over all records // for (long i = 0; i < hdr_ghdi_num_recs_d; i++) { // loop over all channels // for (long j = 0; j < hdr_ghdi_nsig_rec_d; j++) { // display debug message // if ((dbgl_d > Dbgl::DETAILED) && (i < DEF_DBG_NF) && (j < DEF_DBG_NF)) { fprintf(stdout, "%s (line: %d) %s: reading record no. [%ld %ld]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, j); } // read the data // long num_samps = hdr_chan_rec_size_d[j]; long num_samps_read = fread(buf, sizeof(short int), num_samps, fp_a); if (num_samps_read != num_samps) { long tot_size = hdr_num_channels_signal_d * hdr_ghdi_num_recs_d * (long)hdr_sample_frequency_d * 2; fprintf (stdout, "Error: %s (line: %d) %s: %s (%ld) %s (%ld x %ld x %ld %s = %ld) %s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, "file size on disk", file_size_in_bytes, "and signal size", hdr_num_channels_signal_d, hdr_ghdi_num_recs_d, (long)hdr_sample_frequency_d, "x 2 bytes/samp = ", tot_size, "do not match"); return false; } if ((dbgl_d > Dbgl::DETAILED) && (i < DEF_DBG_NF) && (j < DEF_DBG_NF)) { fprintf(stdout, "%s (line: %d) %s: [%ld %ld] read data complete\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, j); } // compute scale factors: // the data must be scaled from digital to physical signal levels. // a dc offset is also computed according to the standard. // note that for some data, the max and min values are zero, // so we must check this. if it is zero, we ignore it by making // the scale factor 1 and the bias 0; // double sum_n = hdr_chan_phys_max_d[j] - hdr_chan_phys_min_d[j]; double sum_d = (double)(hdr_chan_dig_max_d[j] - hdr_chan_dig_min_d[j]); double sum = 1.0; double dc = 0; if (sum_d != 0) { sum = sum_n / sum_d; dc = hdr_chan_phys_max_d[j] - sum * (double)hdr_chan_dig_max_d[j]; } if ((dbgl_d > Dbgl::DETAILED) && (i < DEF_DBG_NF) && (j < DEF_DBG_NF)) { fprintf(stdout, "%s (line: %d) %s: [%ld %ld] dc offset = %f (%f, %f, %f)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, j, dc, sum_n, sum_d, sum); } // transfer the data to the double precision output // long offset = i * hdr_chan_rec_size_d[j]; for (int k = 0; k < num_samps; k++) { if (sc_a == true) { sig_a[j][offset++] = sum * (double)buf[k] + dc; } else { sig_a[j][offset++] = (double)buf[k]; } } if ((dbgl_d > Dbgl::DETAILED) && (i < DEF_DBG_NF) && (j < DEF_DBG_NF)) { fprintf(stdout, "%s (line: %d) %s: [%ld %ld] data transfer complete\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, j); } } // display debug message // if ((dbgl_d > Dbgl::BRIEF) && (i < DEF_DBG_NF)) { fprintf(stdout, "%s (line: %d) %s: done reading record no. %ld\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i); } } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done with get_signal\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: put_signal // // arguments: // VVectorDouble& sig: the EEG signal data (input) // FILE* fp: an open file pointer // bool sc: scale the signal based on header data (input) // the default is to do the standard EDF scaling // // return: a boolean value indicating status // // This method write the signal data to an open file. // bool Edf::put_signal(VVectorDouble& sig_a, FILE* fp_a, bool sc_a) { // display an informational message // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: starting put_signal\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // position the file to the beginning of the data // using the header information // if (fseek(fp_a, hdr_ghdi_hsize_d, SEEK_SET)) { return false; } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: file positioning complete\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // create temporary space // long bsize = 0; for (long i = 0; i < (long)sig_a.size(); i++) { bsize = Itgl::max(hdr_chan_rec_size_d[i], bsize); } short int buf[bsize]; if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: temp space created [%ld samples]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, bsize); } // loop over all records // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: starting main write loop [%ld recs]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, hdr_ghdi_num_recs_d); } for (long i = 0; i < hdr_ghdi_num_recs_d; i++) { // display debug message // if ((dbgl_d > Dbgl::DETAILED) && (i < DEF_DBG_NF)) { fprintf(stdout, "%s (line: %d) %s: writing record no. %ld out of %ld\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, hdr_chan_rec_size_d[i]); } // loop over all channels // for (long j = 0; j < hdr_ghdi_nsig_rec_d; j++) { // compute scale factors (see the read method): in this case // we invert the scale factor. // double sum_n = hdr_chan_phys_max_d[j] - hdr_chan_phys_min_d[j]; double sum_d = hdr_chan_dig_max_d[j] - hdr_chan_dig_min_d[j]; double sum = 1.0; double dc = 0; if (sum_d != 0) { sum = sum_n / sum_d; dc = hdr_chan_phys_max_d[j] - sum * hdr_chan_dig_max_d[j]; } double isum = 1.0 / sum; if ((dbgl_d > Dbgl::DETAILED) && (i < DEF_DBG_NF) && (j < DEF_DBG_NF)) { fprintf(stdout, "%s (line: %d) %s: ", __FILE__, __LINE__, __PRETTY_FUNCTION__); fprintf(stdout, "%s %ld block: %ld size = %ld] %s [%f %f %f %f]\n", "[rec: ", i, j, hdr_chan_rec_size_d[j], "[n/d/scale/dc] =", sum_n, sum_d, sum, dc); } // transfer the data to the double precision output // long num_samps = hdr_chan_rec_size_d[j]; long offset = i * hdr_chan_rec_size_d[j]; for (long k = 0; k < num_samps; k++) { if (sc_a == true) { buf[k] = round(((sig_a[j][offset++] - dc) * isum)); } else { buf[k] = (short int)round(sig_a[j][offset++]); } // printf("...[%ld %ld %ld] [%f %d] [%f %f %f %f %f]\n", // i, j, k, // sig_a[j][offset-1],buf[k], // sum_n, sum_d, sum, isum, dc); } // write the data // long num_samps_write = fwrite(buf, sizeof(short int), num_samps, fp_a); if (num_samps_write != num_samps) { return false; } } // display debug message // if ((dbgl_d > Dbgl::DETAILED) && (i < DEF_DBG_NF)) { fprintf(stdout, "%s (line: %d) %s: done writing record no. %ld\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i); } } // display an informational message // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done with put_signal\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: read_edf // // arguments: // VVectorDouble& sig: the EEG signal data (output) // char* fn: input filename (input) // bool sc: scale the signal based on header data (input) // the default is to do the standard EDF scaling // bool rsig: if true, read the signal (input) // // return: a boolean value indicating status // // This method opens an EDF file, reads the signal data channel by channel // into the signal matrix, and closes the file. // bool Edf::read_edf(VVectorDouble& sig_a, const char* fn_a, bool sc_a, bool rsig_a) { // copy the filename // Edf::resize(fn_d, strlen(fn_a) + 1); strcpy(fn_d, fn_a); // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: opening an EDF file (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); } // open the file // fp_d = fopen(fn_a, "r"); if (fp_d == (FILE*)NULL) { fprintf(stdout, "Error: %s (line: %d) %s: error opening (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); return false; } // get the size of the file on disk // rewind(fp_d); fseek(fp_d, 0L, SEEK_END); long file_size_in_bytes = ftell(fp_d); rewind(fp_d); if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: file size = %ld bytes\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, file_size_in_bytes); } // load the header // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: loading an EDF header (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); } if (!Edf::get_header(fp_d)) { fprintf(stdout, "Error: %s (line: %d) %s: error in get_header (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); return false; } // display debug information // if (dbgl_d > Dbgl::BRIEF) { Edf::print_header(stdout); } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done loading an EDF header (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); } // exit if we only need to load the header: // this was done to accommodate very long signals // if (rsig_a == false) { fclose(fp_d); fp_d = (FILE*)NULL; return true; } // load the signal // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: loading an EDF signal (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); } if (!Edf::get_signal(sig_a, fp_d, sc_a)) { fprintf(stdout, "Error: %s (line: %d) %s: error in get_signal (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); return false; } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done loading an EDF signal (%s)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); } // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: closing an EDF file\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // close the file // if (fp_d != (FILE*)NULL) { fclose(fp_d); fp_d = (FILE*)NULL; } // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done closing an EDF file\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: write_edf // // arguments: // VVectorDouble& sig: signal data (input) // char* fn: output filename (input) // bool sc: scale the signal based on header data (input) // the default is to do the standard EDF scaling // // return: a boolean value indicating status // // This method writes an Edf file. // bool Edf::write_edf(VVectorDouble& sig_a, const char* fn_a, const bool sc_a) { // declare local variables // long status = true; // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: beginning write edf\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // change the file type to generic EDF: note that as of 20240204, // we are clearing the file type to make these files compatible // with various open source software packages. // memcpy(hdr_ghdi_file_type_d, EDF_FTYP, EDF_FTYP_BSIZE); // open the file // FILE* fp = fopen(fn_a, "w"); if (fp == (FILE*)NULL) { fprintf(stdout, "Error: %s (line: %d) %s: file is null\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); return false; } // write the header // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: writing header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } if (!Edf::put_header(fp)) { fclose(fp); return false; } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done writing header\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // write the signal // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: writing signal\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } if (!Edf::put_signal(sig_a, fp, sc_a)) { fclose(fp); return false; } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done writing signal\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // close the file // fclose(fp); // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: end of edf_write\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return status; } //***************************************************************************** // // public methods: read/write feature data // //***************************************************************************** // method: read_features // // arguments: // VVVectorDouble& feat: feature data (output) // char* fn: input filename (input) // // return: a boolean value indicating status // // This method reads feature data from a file. It branches on the // type of the file. // // This method is a little fragile since raw and htk files don't really // have identifiers. If the exact filename doesn't exist, it must be // an htk file, since these are written as multiple single channel files // with a modified filename. // bool Edf::read_features(VVVectorDouble& feat_a, const char* fn_a) { // declare local variables // FFMT ffmt; // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: begin reading a feature file\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // check the file type using informational methods // if (is_htk(fn_a)) { ffmt = FFMT_HTK; } else if (is_kaldi(fn_a)) { ffmt = FFMT_KALDI; } else if (is_raw(fn_a)) { ffmt = FFMT_RAW; } else { return false; } // check for one of three things: raw, htk, kaldi // if (ffmt == FFMT_RAW) { return Edf::read_features_raw(feat_a, fn_a); } else if (ffmt == FFMT_HTK) { return Edf::read_features_htk(feat_a, fn_a); } else if (ffmt == FFMT_KALDI) { return Edf::read_features_kaldi(feat_a, fn_a); } else { fprintf(stdout, "Error: %s (line: %d) %s: error choosing input type\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); return false; } // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done reading a feature file\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: read_features_raw // // arguments: // VVVectorDouble& feat: feature data (output) // char* fn: input filename (input) // // return: a boolean value indicating status // // This method reads feature data from a raw file. // bool Edf::read_features_raw(VVVectorDouble& feat_a, const char* fn_a) { // declare local variables // long nbytes = 0; long status = true; // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: beginning binary read [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, fn_a); } // open the file // FILE* fp = fopen(fn_a, "r"); if (fp == (FILE*)NULL) { return false; } // get the size of the file // fseek(fp, 0, SEEK_END); long fsize = ftell(fp); rewind(fp); // read and check the dimensions // int32_t dims[2]; if (fread(dims, sizeof(int32_t), 2, fp) != 2) { fclose(fp); return false; } if ((dims[0] < 0) || (dims[1] < 0) || (((long)dims[0]*(long)dims[1]) > fsize)) { fclose(fp); return false; } // size the feature vector // nbytes += 2 * sizeof(int32_t); Edf::resize(feat_a, dims[0], false); for (long i = 0; i < dims[0]; i++) { Edf::resize(feat_a[i], dims[1], false); } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: (nchan, nframes) %ld %ld\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, (long)dims[0], (long)dims[1]); } // loop over all channels and frames // for (long i = 0; i < dims[0]; i++) { for (long j = 0; j < dims[1]; j++) { // read the vector size from the file // int32_t ndim; if (fread(&ndim, sizeof(int32_t), 1, fp) != 1) { fclose(fp); return false; } nbytes += sizeof(int32_t); Edf::resize(feat_a[i][j], ndim, false); // read the vector // float tmp_buf[ndim]; if ((long)fread(tmp_buf, sizeof(float), ndim, fp) != ndim) { fclose(fp); return false; } nbytes += ndim * sizeof(float); // convert the float to a double // for (long k = 0; k < ndim; k++) { feat_a[i][j][k] = tmp_buf[k]; } if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: (chan, nf, fdim) %ld %ld %ld [%f]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, j, (long)feat_a[i][j].size(), feat_a[i][j][0]); } } } // close the file // fclose(fp); // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: end of binary read [%ld bytes]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes); } // exit gracefully // return status; }; // method: read_features_htk // // arguments: // VVVectorDouble& feat: feature matrix (output) // char* fn: base filename (input) // // return: a boolean value indicating status // // This method reads each channel of a multichannel HTK signal into // a single feature vector data structure. In this version, the user // provides a list of filenames which are mapped to channel 0, channel 1, // ... channel N. Since HTK feature files can only hold one channel of // feature data, an EDF file must be split into multiple files. // bool Edf::read_features_htk(VVVectorDouble& feat_a, const char* fn_a) { // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: begin reading htk formatted files\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // build the filelist // long nf; char* fnames[MAX_NCHANS]; if (Edf::get_matching_filenames(fnames, nf, fn_a, fnmod_d) == false) { return false; } if (dbgl_d >= Dbgl::MEDIUM) { for (long i = 0; i < nf; i++) { fprintf(stdout, "%s (line: %d) %s: channel %ld - [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, fnames[i]); } } // create space for the feature data // if ((long)feat_a.size() != nf) { Edf::resize(feat_a, nf, false); } // loop over all files in the list // for(long i = 0; i < nf; i++){ if (read_htk_channel(feat_a[i], fdur_d, fnames[i]) == false) { fprintf(stdout, "Error: %s (line: %d) %s: error reading channel %ld - [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, fnames[i]); return false; } } // clean up memory // Edf::cleanup(fnames, nf); // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done reading htk formatted files\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: read_features_kaldi // // arguments: // VVVectorDouble& feat: feature matrix (output) // char* fn: base filename (input) // // return: a boolean value indicating status // // This method reads each channel of a multichannel Kaldi ARK file into // a single feature vector structure. // bool Edf::read_features_kaldi(VVVectorDouble& feat_a, const char* fn_a) { // declare local variables // unsigned char buf[sizeof(float)]; // 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: begin reading kaldi formatted file", __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. the // second line keeps the -Wall option happy. // char static_assert_float32[1 - (2 * ((sizeof(float) * CHAR_BIT) != 32))]; if (static_assert_float32 == (char*)NULL) {exit(EXIT_FAILURE);} // open the input Kaldi file for reading // FILE* fp = fopen(fn_a, "r"); if (fp == (FILE*)NULL) { return false; } // get the name from the header and its length // char hname[Itgl::MAX_MSTR_LENGTH]; fread(hname, Itgl::MAX_MSTR_LENGTH, 1, fp); long hname_length = strlen(hname); // get the header size // long header_size = KALDI_HEADER_FIXED_SIZE + hname_length; // go to frame size and read it // int32_t frame_size; fseek(fp, hname_length + KALDI_FRAME_FIXED_LOC, SEEK_SET); if (fread(&buf, sizeof(int32_t), 1, fp) != 1) { fclose(fp); return false; } if (big_endian) { Edf::swap_bytes(&frame_size, buf, sizeof(int32_t)); } else { memcpy(&frame_size, buf, sizeof(int32_t)); } // go to vector size and read it // int32_t vec_size; fseek(fp, KALDI_VECTOR_FIXED_LOC - KALDI_FRAME_FIXED_LOC - sizeof(float), SEEK_CUR); if (fread(&buf, sizeof(int32_t), 1, fp) != 1) { fclose(fp); return false; } if (big_endian) { Edf::swap_bytes(&vec_size, buf, sizeof(int32_t)); } else { memcpy(&vec_size, buf, sizeof(int32_t)); } // calculate the difference in ark access indexes // int32_t ark_index_dif = vec_size * frame_size * sizeof(float) + header_size; // find the end of file // fseek(fp, 0, SEEK_END); long eof = ftell(fp); // find the number of channels in the file // long num_channels = 0; fseek(fp, 0, SEEK_SET); while (ftell(fp) < eof) { num_channels += 1; fseek(fp, ark_index_dif, SEEK_CUR); } // initialize the feature matrix: // resize the matrix so it can fit the information from each channel; // if ((long)feat_a.size() != num_channels) { Edf::resize(feat_a, num_channels, false); } // resize each channel of the matrix so it can fit all of the frames // for (long i = 0; i < num_channels; i++) { if ((long)feat_a[i].size() != frame_size){ Edf::resize(feat_a[i], frame_size, false); } // resize each frame of the matrix so it can fit all of the vectors // for (long j = 0; j < frame_size; j++) { if ((long)feat_a[i][j].size() != vec_size) { Edf::resize(feat_a[i][j], vec_size, false); } } } // initialize variables to read features // float sum; float tmp_buf[vec_size]; // start reading the features: // Kaldi ark files are written in little endian format. This loop will loop // through all of the channels in the file and skip the headers to go right // to the feature information. Then, it will loop through the frames and // store each vector a temporary buffer. Perform an endian swap if the // system is not little endian, and then store the value into the feature // matrix. // fseek(fp, 0, SEEK_SET); for (long i = 0; i < num_channels; i++) { // skip the header for each channel // fseek(fp, header_size, SEEK_CUR); // loop over all frames and elements // for (long j = 0; j < frame_size; j++) { // 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 k = 0; k < vec_size; k++){ // byte swap if necessary // if (big_endian) { Edf::swap_bytes(&sum, &tmp_buf[k], sizeof(float)); } else { sum = tmp_buf[k]; } // covert to single precision floating point number // feat_a[i][j][k] = sum; } } } // close the file // fclose(fp); // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: end of reading features\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: write_features // // arguments: // VVVectorDouble& feat: feature data (output) // char* fn: output filename (input) // FFMT ffmt: feature file format specifier (input) // // // return: a boolean value indicating status // // This method writes feature to a file. It branches on the // type of the file. // bool Edf::write_features(VVVectorDouble& feat_a, const char* fn_a, FFMT ffmt_a) { // check for one of three things: htk, raw, or kaldi // if (ffmt_a == FFMT_HTK) { return Edf::write_features_htk(feat_a, fn_a); } // case 2: if it is a kaldi feature file // else if (ffmt_a == FFMT_KALDI) { return Edf::write_features_kaldi(feat_a, fn_a); } // case 3: if it is a raw feature file // else if (ffmt_a == FFMT_RAW) { return Edf::write_features_raw(feat_a, fn_a); } // case 4: unrecognized filetype // else { fprintf(stdout, "Error: %s (line: %d) %s: unrecognized filetype\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: write_features_raw // // arguments: // VVVectorDouble& feat: matrix (input) // char* fn: output filename (input) // // return: a boolean value indicating status // // This method writes feature vectors to a file. They are written // frame by frame. For each frame, a vector is written. // The file is written as a binary file in the following order: // // (1) number of rows (number of channels) (4-byte int) // (2) number of cols (number of frames) (4-byte int) // (3) channel #0, frame #0: // no. of features (4-byte int) // features (4-byte float) // channel #0, frame #1: ... // channel #0, last frame: ... // channel #1, frame #0: ... // // All data is written in a binary format. // bool Edf::write_features_raw(VVVectorDouble& feat_a, const char* fn_a) { // declare local variables // long nbytes = 0; long status = true; // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: beginning binary write\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // open the file // FILE* fp = fopen(fn_a, "w"); if (fp == (FILE*)NULL) { return false; } // write the dimensions // int32_t dims[2]; dims[0] = feat_a.size(); dims[1] = feat_a[0].size(); if (fwrite(dims, sizeof(int32_t), 2, fp) != 2) { fclose(fp); return false; } nbytes += 2 * sizeof(int32_t); if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: (nchan, nframes) %ld %ld\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, (long)dims[0], (long)dims[1]); } // loop over all channels and frames // for (long i = 0; i < dims[0]; i++) { for (long j = 0; j < dims[1]; j++) { if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: (chan, nf, fdim) %ld %ld %ld\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, j, (long)feat_a[i][j].size()); } // grab the vector size and write it to the file // int32_t ndim = feat_a[i][j].size(); if (fwrite(&ndim, sizeof(int32_t), 1, fp) != 1) { fclose(fp); return false; } // convert the double vector to a float // float tmp_buf[ndim]; for (long k = 0; k < ndim; k++) { tmp_buf[k] = (float)feat_a[i][j][k]; } // write the vector // if ((long)fwrite(tmp_buf, sizeof(float), ndim, fp) != ndim) { fclose(fp); return false; } nbytes += sizeof(int32_t) + ndim * sizeof(float); } } // close the file // fclose(fp); // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: end of binary write [%ld bytes]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, nbytes); } // exit gracefully // return status; }; // method: write_features_htk // // arguments: // VVVectorDouble& feat: feature matrix (input) // char* fn: output filenames - one per channel (input) // // return: a boolean value indicating status // // This method writes each channel of a multichannel EDF signal into // a one channel per file format file in an HTK binary format. // // See write_htk_channel for more information on why the frame duration // is needed as an argument. // // All data is written in a binary format. // bool Edf::write_features_htk(VVVectorDouble& feat_a, const char* fn_a) { // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: begin writing htk formatted files\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // create a filelist // long nf = feat_a.size(); char* fnames[nf]; if (Edf::create_filelist(fnames, nf, fn_a, fnmod_d) == false) { return false; } if (dbgl_d >= Dbgl::MEDIUM) { for (long i = 0; i < nf; i++) { fprintf(stdout, "%s (line: %d) %s: channel %ld - [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, fnames[i]); } } // loop over all files in the list // for(long i = 0; i < nf; i++){ if (write_htk_channel(feat_a[i], fdur_d, fnames[i]) == false) { fprintf(stdout, "Error: %s (line: %d) %s: error writing channel %ld - [%s]\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, i, fnames[i]); return false; } } // clean up memory // Edf::cleanup(fnames, nf); // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done writing htk formatted files\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } // method: write_features_kaldi // // arguments: // VVVectorDouble& feat: feature matrix (input) // char* fn: output filename (input) // // return: a boolean value indicating status // // This method writes each channel of a multichannel EDF signal into // one file in a Kaldi ARK format. // bool Edf::write_features_kaldi(VVVectorDouble& feat_a, const char* fn_a) { // declare local variables // unsigned char buf[sizeof(float)]; // 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: begin writing kaldi formatted files", __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);} // open the output Kaldi ark file for writing // FILE* fp = fopen(fn_a, "w"); if (fp == (FILE*)NULL){ return false; } // get the basename of the input edf file from the header // long nc = feat_a.size(); char* onames[nc]; Edf::create_filelist(onames, nc, fn_a, fnmod_d); // loop through the channels of the feature matrix // for (long i = 0; i < nc; i++) { // remove the extension and any slashes from the name in the list // char* oname_no_ext = strtok(onames[i], Itgl::DOT); char* token = strtok(oname_no_ext, Itgl::SLASH); while (token != NULL) { oname_no_ext = token; token = strtok(NULL, Itgl::SLASH); } // 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[i][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)); fclose(fp); return false; } //check that the vector size is the same for all frames of data // int32_t num_frames = feat_a[i].size(); for (long j = 0; j < num_frames; j++) { if ((long)feat_a[i][j].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][j].size(), vec_size); fclose(fp); return false; } } // get the basename of the input edf file, and then write the header // if (fprintf(fp, "%s %c%s %c", oname_no_ext, (char)NULL, KALDI_HEADER_STR, KALDI_HEADER_EOT) < 0){ fprintf(stdout, "Error: %s (line: %d) %s: %s - [%s]", __FILE__, __LINE__, __PRETTY_FUNCTION__, "error writing to header", fn_a); fclose(fp); 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 second formatted string // if (fprintf(fp, "%c", KALDI_HEADER_EOT) < 0) { fprintf(stdout, "Error: %s (line: %d) %s: %s - [%s]", __FILE__, __LINE__, __PRETTY_FUNCTION__, "error writing second formatted string", fn_a); fclose(fp); return false; } // write vector size // if (big_endian) { Edf::swap_bytes(buf, &vec_size, sizeof(int32_t)); } else { memcpy(buf, &vec_size, sizeof(int32_t)); } if (fwrite(&buf, sizeof(int32_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 %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, "(nf, fdim, fdur)", (long)num_frames, vec_size, (long)fdur_d); } // initialize variables for writing the features // float sum; float tmp_buf[vec_size]; // loop over all frames and vectors to write the data: // Kaldi ark files are written in little endian format, so the values // written from the feature matrix to the output file may have to be // byte-swapped if the system is not little endian. The data from the // feature matrix is then written to the output Kaldi Ark file. // for (long j = 0; j < num_frames; j++) { for (long k = 0; k < vec_size; k++) { // convert to a single precision floating point number // sum = feat_a[i][j][k]; // byte swap if necessary // if (big_endian) { Edf::swap_bytes(&tmp_buf[k], &sum, sizeof(float)); } else { tmp_buf[k] = 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::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done writing kaldi formatted file\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); } // exit gracefully // return true; } //***************************************************************************** // // public methods: informational methods // //***************************************************************************** // method: is_edf // // arguments: // const char* fn_a: input filename // // return: an boolean value that is true if the file is an EDF file // // This method tests whether a file is an EDF file by examining the // first 8 bytes in the header. // bool Edf::is_edf(const char* fn_a) { // declare local variables // bool status; // open the file // FILE* fp = fopen(fn_a, "r"); if (fp == (FILE*)NULL) { return false; } // read the first field // if (fread(hdr_version_d, 1, EDF_VERS_BSIZE, fp) != EDF_VERS_BSIZE) { fclose(fp); return false; } hdr_version_d[EDF_VERS_BSIZE] = (char)NULL; // check if it is the correct sequence // if (strcmp(hdr_version_d, EDF_VERS) == 0) { status = true; } else { status = false; } // close the file // fclose(fp); // exit gracefully // return status; } // method: is_filelist // // arguments: // char* fname: the filename to be checked (input) // // return: a boolean value that is true if the file contains filenames // // This method attempts to determine if a file is a filelist or // a binary file. It uses a heuristic check of the byte sequences. // // Note that this method is extremely funky in the way it checks // the type of file. Since HTK multichannel files use a basename, // the file does not technically exist. Therefore, if an open // of fname_a fails, it might possibly be an HTK file. // bool Edf::is_filelist(const char* fname_a) { // declare local variables // bool status = true; // declare a buffer to be used to scan for an unprintable character // char buf[FLIST_BSIZE]; // open the file // FILE* fp = fopen(fname_a, "r"); if (fp == (FILE*)NULL) { return false; } // read a fixed number of bytes // long nbytes = fread(buf, 1, FLIST_BSIZE, fp); // search for non-ASCII characters // long i = 0; while (i < nbytes) { if ((!isspace(buf[i])) && (!isprint(buf[i]))) { status = false; break; } i++; } // close the file // fclose(fp); // exit gracefully // return status; } // method: is_raw // // arguments: // char* fname: the filename to be checked (input) // // return: a boolean value that is true if the file is a raw file // // This method looks for specific header information, such as number // of channels. It is not very robust and is very heuristic. // bool Edf::is_raw(const char* fname_a) { // declare local variables // unsigned char buf[FLIST_BSIZE]; // open the file // FILE* fp = fopen(fname_a, "r"); if (fp == (FILE*)NULL) { return false; } // read a block and look for non-ASCII characters // long nbytes = fread(buf, sizeof(unsigned char), FLIST_BSIZE, fp); if (nbytes <= 0) { fclose(fp); return false; } long n = 0; long non_ascii = false; while (n < nbytes) { if ((!isspace(buf[n])) && (!isprint(buf[n]))) { non_ascii = true; break; } n++; } // check if we found binary data // if (non_ascii == false) { fclose(fp); return false; } // close the file // fclose(fp); // exit gracefully // return true; } // method: is_hea // // arguments: // char* fname: the header filename to be checked (input) // // return: a boolean value that is true if the file is a TNMG header file // // This method looks for specific header information, such as number // of channels. It is not very robust and is very heuristic. // bool Edf::is_hea(const char* fname_a) { // declare local variables // // open the file // FILE* fp = fopen(fname_a, "r"); if (fp == (FILE*)NULL) { return false; } // read the first line // char buf[Itgl::MAX_LSTR_LENGTH]; if (fgets(buf, Itgl::MAX_LSTR_LENGTH, fp) == (char*)NULL) { fclose(fp); return false; } // grab the number of channels // long num_channels; sscanf(buf, "%*s%ld", &num_channels); // rewind the file // rewind(fp); // count the number of lines // long nl = 0; while (fgets(buf, Itgl::MAX_LSTR_LENGTH, fp) != (char*)NULL) { nl++; } // close the file // fclose(fp); // check the counts // if (num_channels < (long)1) { return false; } else if ((nl - (long)1) == num_channels) { return true; } // exit gracefully // return false; } // method: is_htk // // arguments: // char* fname: the filename to be checked (input) // // return: a boolean value that is true if the file is an htk file // // This method looks for specific header information, such as the htk code. // bool Edf::is_htk(const char* fname_a) { // declare local variables // double fdur; bool status = false; unsigned char buf[4]; // build the modified filename // char* onames[1]; Edf::create_filelist(onames, 1, fname_a, fnmod_d); // 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 check [%s] ", __FILE__, __LINE__, __PRETTY_FUNCTION__, fname_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(onames[0], "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)); } // read the frame duration in 100 mic-sec 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 = 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)); } // check the htk code to determine if this is an htk file // if ((htk_code == 9) && (num_frames > 0) && (vec_size > 0) && (fdur > 0) && (sample_size > 0)) { status = true; } else { status = false; } // close the file // fclose(fp); // 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, (long)sample_size, (long)htk_code); } // clean up memory // delete [] onames[0]; // exit gracefully // return status; } // method: is_kaldi // // arguments: // char* fname: the filename to be checked (input) // // return: a boolean value that is true if the file is a kaldi file // // This method looks for header information at kaldi-specific byte locations. // bool Edf::is_kaldi(const char *fname_a) { // declare local variables // unsigned char buf[sizeof(float)]; bool status = false; // 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 check [%s] ", __FILE__, __LINE__, __PRETTY_FUNCTION__, fname_a); if (big_endian == false) { fprintf(stdout, " byte order = little endian\n"); } else { fprintf(stdout, " byte order = big endian\n"); } } // open the file for reading // FILE* fp = fopen(fname_a, "r"); if (fp == (FILE*)NULL) { return false; } // get size of header // char hname[Itgl::MAX_MSTR_LENGTH]; fread(hname, Itgl::MAX_MSTR_LENGTH, 1, fp); // check the size of a float: // a float most 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);} // get the size of the name from the header // long hname_size = strlen(hname); // go to end of name in header // fseek(fp, hname_size, SEEK_SET); // go to BFM string and read it // char bfm_str[Itgl::MAX_SSTR_LENGTH]; fseek(fp, 1, SEEK_CUR); if (fread(&bfm_str, strlen(KALDI_HEADER_STR), 1, fp) != 1) { fclose(fp); return false; } // go to frame size and read it // int32_t frame_size; fseek(fp, KALDI_FRAME_FIXED_LOC + hname_size, SEEK_SET); if (fread(&buf, sizeof(int32_t), 1, fp) != 1) { fclose(fp); return false; } if (big_endian) { Edf::swap_bytes(&frame_size, buf, sizeof(int32_t)); } else { memcpy(&frame_size, buf, sizeof(int32_t)); } // go to vector size and read it // int32_t vec_size; fseek(fp, KALDI_VECTOR_FIXED_LOC - KALDI_FRAME_FIXED_LOC - sizeof(float), SEEK_CUR); if (fread(&buf, sizeof(int32_t), 1, fp) != 1) { fclose(fp); return false; } if (big_endian) { Edf::swap_bytes(&vec_size, buf, sizeof(int32_t)); } else { memcpy(&vec_size, buf, sizeof(int32_t)); } // check the values read from the file to see if it is a kaldi ark file // if ((frame_size > 0) && (vec_size > 0) && !strcmp(bfm_str, KALDI_HEADER_STR)) { status = true; } // close the file // fclose(fp); // display debugging information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: %s (%s %ld %ld)\n", __FILE__, __LINE__, __PRETTY_FUNCTION__, "(modified file name from header, frame size, vector size)", hname, (long)frame_size, (long)vec_size); } // exit gracefully // return status; } //***************************************************************************** // // public methods: deidentify data // //***************************************************************************** // method: deidentify // // arguments: // char* subj: subject id (input) // char* sess: session id (input) // char* tech: technician id (input) // // return: a boolean value indicating status // // This method overwrites selected fields in a header. // bool Edf::deidentify(const char* subj_a, const char* sess_a, const char* tech_a) { // declare local variables // // bool status = true; // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: starting deidentify\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); // display the initial values that we will be changing // fprintf(stdout, " ghdi_file_type = [%s]\n", hdr_ghdi_file_type_d); fprintf(stdout, " lpti_patient_id = [%s]\n", hdr_lpti_patient_id_d); fprintf(stdout, " lpti_dob = [%s]\n", hdr_lpti_dob_d); fprintf(stdout, " lpti_full_name = [%s]\n", hdr_lpti_full_name_d); fprintf(stdout, " lpti_age = [%s]\n", hdr_lpti_age_d); fprintf(stdout, " lrci_start_date = [%s]\n", hdr_lrci_start_date_d); fprintf(stdout, " lrci_eeg_id = [%s]\n", hdr_lrci_eeg_id_d); fprintf(stdout, " lrci_tech = [%s]\n", hdr_lrci_tech_d); fprintf(stdout, " ghdi_start_date = [%s]\n", hdr_ghdi_start_date_d); fprintf(stdout, " ghdi_start_time = [%s]\n", hdr_ghdi_start_time_d); } // (0) change the file type to generic EDF // memcpy(hdr_ghdi_file_type_d, EDF_FTYP, EDF_FTYP_BSIZE); // (1) clear the patient ID // strcpy(hdr_lpti_patient_id_d, subj_a); // (2) convert the dob to the beginning of the year // memcpy(hdr_lpti_dob_d, "01-JAN", (size_t)6); // (3) convert the full name // strcpy(hdr_lpti_full_name_d, subj_a); // (4) compute the approximate age based on the difference between // the dob and the start date: // if the age is greater than 89, set it to 999 // char* str1 = rindex(hdr_lpti_dob_d, '-'); str1++; char* str2 = rindex(hdr_lrci_start_date_d, '-'); str2++; long num_years = atoi(str2) - atoi(str1); if (num_years > MAX_NUM_YEARS) { num_years = DEF_NUM_YEARS; } sprintf(hdr_lpti_age_d, "Age:%d", (short)num_years); // (5) replace the start date with only the year // memcpy(hdr_lrci_start_date_d, "01-JAN", 6); // (6) replace the eeg_id // sprintf(hdr_lrci_eeg_id_d, "%s_%s", subj_a, sess_a); // (7) replace technician // strcpy(hdr_lrci_tech_d, tech_a); // (8) replace the start date and time // memcpy(hdr_ghdi_start_date_d, "01.01", 5); strcpy(hdr_ghdi_start_time_d, "00.00.00"); // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done with deidentify\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); // display the new values // fprintf(stdout, " ghdi_file_type = [%s]\n", hdr_ghdi_file_type_d); fprintf(stdout, " lpti_patient_id = [%s]\n", hdr_lpti_patient_id_d); fprintf(stdout, " lpti_dob = [%s]\n", hdr_lpti_dob_d); fprintf(stdout, " lpti_full_name = [%s]\n", hdr_lpti_full_name_d); fprintf(stdout, " lpti_age = [%s]\n", hdr_lpti_age_d); fprintf(stdout, " lrci_start_date = [%s]\n", hdr_lrci_start_date_d); fprintf(stdout, " lrci_eeg_id = [%s]\n", hdr_lrci_eeg_id_d); fprintf(stdout, " lrci_tech = [%s]\n", hdr_lrci_tech_d); fprintf(stdout, " ghdi_start_date = [%s]\n", hdr_ghdi_start_date_d); fprintf(stdout, " ghdi_start_time = [%s]\n", hdr_ghdi_start_time_d); } // exit gracefully // return true; } // method: anonymize // // arguments: // char* subj: subject id (input) // char* sess: session id (input) // char* tech: technician id (input) // // return: a boolean value indicating status // // This method overwrites selected fields in a header with blanks. // bool Edf::anonymize(const char* subj_a, const char* sess_a, const char* tech_a) { // declare local variables // // bool status = true; // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: starting anonymize\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); // display the initial values that we will be changing // fprintf(stdout, " ghdi_file_type = [%s]\n", hdr_ghdi_file_type_d); fprintf(stdout, " lpti_patient_id = [%s]\n", hdr_lpti_patient_id_d); fprintf(stdout, " lpti_dob = [%s]\n", hdr_lpti_dob_d); fprintf(stdout, " lpti_full_name = [%s]\n", hdr_lpti_full_name_d); fprintf(stdout, " lpti_age = [%s]\n", hdr_lpti_age_d); fprintf(stdout, " lrci_eeg_id = [%s]\n", hdr_lrci_eeg_id_d); fprintf(stdout, " lrci_tech = [%s]\n", hdr_lrci_tech_d); } // (0) change the file type to generic EDF // memcpy(hdr_ghdi_file_type_d, EDF_FTYP, EDF_FTYP_BSIZE); // (1) clear the patient ID // strcpy(hdr_lpti_patient_id_d, subj_a); // (2) clear the dob // memcpy(hdr_lpti_dob_d, "01-JAN-0000", (size_t)11); // (3) convert the full name // strcpy(hdr_lpti_full_name_d, subj_a); // (5) replace the eeg_id // sprintf(hdr_lrci_eeg_id_d, "%s_%s", subj_a, sess_a); // (6) replace technician // strcpy(hdr_lrci_tech_d, "XXX"); // (7) replace the start date with only the year // memcpy(hdr_lrci_start_date_d, "01-JAN", 6); memcpy(hdr_ghdi_start_date_d, "01.01", 5); strcpy(hdr_ghdi_start_time_d, "00.00.00"); // (8) if the age is over 89, fix it // char* str1 = rindex(hdr_lpti_age_d, ':'); str1++; if (atoi(str1) > 89) { sprintf(hdr_lpti_age_d, "Age:%d", (short)999); } // display debug information // if (dbgl_d > Dbgl::BRIEF) { fprintf(stdout, "%s (line: %d) %s: done with anonymize\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); // display the new values // fprintf(stdout, " ghdi_file_type = [%s]\n", hdr_ghdi_file_type_d); fprintf(stdout, " lpti_patient_id = [%s]\n", hdr_lpti_patient_id_d); fprintf(stdout, " lpti_dob = [%s]\n", hdr_lpti_dob_d); fprintf(stdout, " lpti_full_name = [%s]\n", hdr_lpti_full_name_d); fprintf(stdout, " lpti_age = [%s]\n", hdr_lpti_age_d); fprintf(stdout, " lrci_eeg_id = [%s]\n", hdr_lrci_eeg_id_d); fprintf(stdout, " lrci_tech = [%s]\n", hdr_lrci_tech_d); } // exit gracefully // return true; } // // end of file