/* ***************************************************************************** * * Copyright (c) 2009 - 2024 Teunis van Beelen * All rights reserved. * * Email: teuniz@protonmail.com * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ***************************************************************************** */ /* compile with options "-D_LARGEFILE64_SOURCE -D_LARGEFILE_SOURCE" */ #include "edflib.h" #define EDFLIB_VERSION (127) #define EDFLIB_MAXFILES (64) #if defined(__APPLE__) || defined(__MACH__) || defined(__APPLE_CC__) || defined(__HAIKU__) || defined(__ANDROID__) #define fopeno fopen #else #define fseeko fseeko64 #define ftello ftello64 #define fopeno fopen64 #endif #ifdef _WIN32 #ifndef __MINGW32__ /* needed for visual c */ #undef fseeko #define fseeko _fseeki64 #undef ftello #define ftello _ftelli64 #undef fopeno #define fopeno fopen #endif #endif /* max length of annotation's description in bytes * you may modify this number in order to create more space for longer description strings */ #define EDFLIB_WRITE_MAX_ANNOTATION_LEN (40) /* number of bytes in datarecord for the annotations track, must be a multiple of six * do not modify this macro */ #if ((EDFLIB_WRITE_MAX_ANNOTATION_LEN + 80) % 6) #define EDFLIB_ANNOTATION_BYTES ((((EDFLIB_WRITE_MAX_ANNOTATION_LEN + 80) / 6) + 1) * 6) #else #define EDFLIB_ANNOTATION_BYTES (EDFLIB_WRITE_MAX_ANNOTATION_LEN + 80) #endif /* for writing only */ #define EDFLIB_MAX_ANNOTATION_CHANNELS (64) #define EDFLIB_ANNOT_MEMBLOCKSZ (1000) typedef struct { char label[17]; char transducer[81]; char physdimension[9]; double phys_min; double phys_max; int dig_min; int dig_max; char prefilter[81]; int smp_per_record; char reserved[33]; double offset; int buf_offset; double bitvalue; int annotation; long long sample_pntr; } edfparamblock_t; typedef struct { FILE *file_hdl; char path[1024]; int writemode; char version[32]; char patient[81]; char recording[81]; char plus_patientcode[81]; char plus_sex[16]; char plus_birthdate[16]; int plus_birthdate_day; int plus_birthdate_month; int plus_birthdate_year; char plus_patient_name[81]; char plus_patient_additional[81]; char plus_startdate[16]; char plus_admincode[81]; char plus_technician[81]; char plus_equipment[81]; char plus_recording_additional[81]; long long l_starttime; int startdate_day; int startdate_month; int startdate_year; int starttime_second; int starttime_minute; int starttime_hour; char reserved[45]; int hdrsize; int edfsignals; long long datarecords; int recordsize; int annot_ch[EDFLIB_MAXSIGNALS]; int nr_annot_chns; int mapped_signals[EDFLIB_MAXSIGNALS]; int edf; int edfplus; int bdf; int bdfplus; int discontinuous; int signal_write_sequence_pos; long long starttime_offset; double data_record_duration; long long long_data_record_duration; int annots_in_file; int annotlist_sz; int total_annot_bytes; int eq_sf; char *wrbuf; int wrbufsize; int annot_chan_idx_pos; edfparamblock_t *edfparam; } edfhdrblock_t; typedef struct { long long onset; long long duration_l; char duration[20]; char annotation[EDFLIB_MAX_ANNOTATION_LEN + 1]; } edf_read_annotationblock_t; static edf_read_annotationblock_t *annotationslist[EDFLIB_MAXFILES]; typedef struct { long long onset; long long duration; char annotation[EDFLIB_WRITE_MAX_ANNOTATION_LEN + 1]; } edf_write_annotationblock_t; static edf_write_annotationblock_t *write_annotationslist[EDFLIB_MAXFILES]; static int edf_files_open=0; static edfhdrblock_t *hdrlist[EDFLIB_MAXFILES]; static edfhdrblock_t * edflib_check_edf_file(FILE *, int *); static int edflib_is_integer_number(char *); static int edflib_is_number(char *); static long long edflib_get_long_duration(char *); static int edflib_get_annotations(edfhdrblock_t *, int, int); static int edflib_is_duration_number(char *); static int edflib_is_onset_number(char *); static long long edflib_get_long_time(char *); static int edflib_write_edf_header(edfhdrblock_t *); static void edflib_latin1_to_ascii(char *, int); static void edflib_latin12utf8(char *, int); static void edflib_remove_padding_trailing_spaces(char *); static int edflib_atoi_nonlocalized(const char *); static double edflib_atof_nonlocalized(const char *); static int edflib_snprint_number_nonlocalized(char *, double, int); /* static int edflib_sprint_int_number_nonlocalized(char *, int, int, int); */ static int edflib_snprint_ll_number_nonlocalized(char *, long long, int, int, int); static int edflib_fprint_int_number_nonlocalized(FILE *, int, int, int); static int edflib_fprint_ll_number_nonlocalized(FILE *, long long, int, int); static int edflib_write_tal(edfhdrblock_t *, FILE *); static int edflib_strlcpy(char *, const char *, int); static int edflib_strlcat(char *, const char *, int); EDFLIB_API int edflib_is_file_used(const char *path) { int i; for(i=0; ipath))) return 1; } } return 0; } EDFLIB_API int edflib_get_number_of_open_files() { return edf_files_open; } EDFLIB_API int edflib_get_handle(int file_number) { int i, file_count=0; for(i=0; ihandle = -1; /* avoid surprises! */ if((sizeof(char) != 1) || (sizeof(short) != 2) || (sizeof(int) != 4) || (sizeof(long long) != 8) || (sizeof(float) != 4) || (sizeof(double) != 8)) { edfhdr->filetype = EDFLIB_ARCH_ERROR; return -1; } /* check endianness! */ byte_order_test_var.one = 0x03020100; if((byte_order_test_var.four[0] != 0) || (byte_order_test_var.four[1] != 1) || (byte_order_test_var.four[2] != 2) || (byte_order_test_var.four[3] != 3)) { edfhdr->filetype = EDFLIB_ARCH_ERROR; return -1; } if((read_annotations_mode<0)||(read_annotations_mode>2)) { edfhdr->filetype = EDFLIB_INVALID_READ_ANNOTS_VALUE; return -1; } if(edf_files_open>=EDFLIB_MAXFILES) { edfhdr->filetype = EDFLIB_MAXFILES_REACHED; return -1; } for(i=0; ipath))) { edfhdr->filetype = EDFLIB_FILE_ALREADY_OPENED; return -1; } } } file = fopeno(path, "rb"); if(file==NULL) { edfhdr->filetype = EDFLIB_NO_SUCH_FILE_OR_DIRECTORY; return -1; } hdr = edflib_check_edf_file(file, &edf_error); if(hdr==NULL) { edfhdr->filetype = edf_error; fclose(file); return -1; } if(hdr->discontinuous) { edfhdr->filetype = EDFLIB_FILE_IS_DISCONTINUOUS; free(hdr->edfparam); free(hdr); fclose(file); return -1; } for(i=0; ihandle = i; break; } } if((hdr->edf)&&(!(hdr->edfplus))) { edfhdr->filetype = EDFLIB_FILETYPE_EDF; } if(hdr->edfplus) { edfhdr->filetype = EDFLIB_FILETYPE_EDFPLUS; } if((hdr->bdf)&&(!(hdr->bdfplus))) { edfhdr->filetype = EDFLIB_FILETYPE_BDF; } if(hdr->bdfplus) { edfhdr->filetype = EDFLIB_FILETYPE_BDFPLUS; } edfhdr->edfsignals = hdr->edfsignals - hdr->nr_annot_chns; edfhdr->file_duration = hdr->long_data_record_duration * hdr->datarecords; edfhdr->startdate_day = hdr->startdate_day; edfhdr->startdate_month = hdr->startdate_month; edfhdr->startdate_year = hdr->startdate_year; edfhdr->starttime_hour = hdr->starttime_hour; edfhdr->starttime_second = hdr->starttime_second; edfhdr->starttime_minute = hdr->starttime_minute; edfhdr->starttime_subsecond = hdr->starttime_offset; edfhdr->datarecords_in_file = hdr->datarecords; edfhdr->datarecord_duration = hdr->long_data_record_duration; annotationslist[edfhdr->handle] = NULL; hdr->annotlist_sz = 0; hdr->annots_in_file = 0; edfhdr->annotations_in_file = 0LL; if((!(hdr->edfplus))&&(!(hdr->bdfplus))) { edflib_strlcpy(edfhdr->patient, hdr->patient, 81); edflib_strlcpy(edfhdr->recording, hdr->recording, 81); } else { edflib_strlcpy(edfhdr->patientcode, hdr->plus_patientcode, 81); edflib_strlcpy(edfhdr->sex, hdr->plus_sex, 16); #if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif edflib_strlcpy(edfhdr->gender, hdr->plus_sex, 16); #if defined(__GNUC__) #pragma GCC diagnostic pop #endif edflib_strlcpy(edfhdr->birthdate, hdr->plus_birthdate, 16); edfhdr->birthdate_day = hdr->plus_birthdate_day; edfhdr->birthdate_month = hdr->plus_birthdate_month; edfhdr->birthdate_year = hdr->plus_birthdate_year; edflib_strlcpy(edfhdr->patient_name, hdr->plus_patient_name, 81); edflib_strlcpy(edfhdr->patient_additional, hdr->plus_patient_additional, 81); edflib_strlcpy(edfhdr->admincode, hdr->plus_admincode, 81); edflib_strlcpy(edfhdr->technician, hdr->plus_technician, 81); edflib_strlcpy(edfhdr->equipment, hdr->plus_equipment, 81); edflib_strlcpy(edfhdr->recording_additional, hdr->plus_recording_additional, 81); if(edflib_get_annotations(hdr, edfhdr->handle, read_annotations_mode)) { edfhdr->filetype = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; fclose(file); free(hdr->edfparam); hdr->edfparam = NULL; free(hdr); hdr = NULL; hdrlist[edfhdr->handle] = NULL; free(annotationslist[edfhdr->handle]); annotationslist[edfhdr->handle] = NULL; return -1; } edfhdr->starttime_subsecond = hdr->starttime_offset; edfhdr->annotations_in_file = hdr->annots_in_file; } edflib_strlcpy(hdr->path, path, 1024); edf_files_open++; j = 0; for(i=0; iedfsignals; i++) { if(!(hdr->edfparam[i].annotation)) { hdr->mapped_signals[j++] = i; } } for(i=0; iedfsignals; i++) { channel = hdr->mapped_signals[i]; edflib_strlcpy(edfhdr->signalparam[i].label, hdr->edfparam[channel].label, 17); edflib_strlcpy(edfhdr->signalparam[i].transducer, hdr->edfparam[channel].transducer, 81); edflib_strlcpy(edfhdr->signalparam[i].physdimension, hdr->edfparam[channel].physdimension, 9); edflib_strlcpy(edfhdr->signalparam[i].prefilter, hdr->edfparam[channel].prefilter, 81); edfhdr->signalparam[i].smp_in_file = hdr->edfparam[channel].smp_per_record * hdr->datarecords; edfhdr->signalparam[i].phys_max = hdr->edfparam[channel].phys_max; edfhdr->signalparam[i].phys_min = hdr->edfparam[channel].phys_min; edfhdr->signalparam[i].dig_max = hdr->edfparam[channel].dig_max; edfhdr->signalparam[i].dig_min = hdr->edfparam[channel].dig_min; edfhdr->signalparam[i].smp_in_datarecord = hdr->edfparam[channel].smp_per_record; } return 0; } EDFLIB_API int edfclose_file(int handle) { edf_write_annotationblock_t *annot2; int i, j, k, n, p, err, datrecsize, nmemb; long long offset, datarecords; char str[EDFLIB_ANNOTATION_BYTES * 2]; edfhdrblock_t *hdr; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; hdr = hdrlist[handle]; if(hdr->writemode) { if(hdr->datarecords == 0LL) { err = edflib_write_edf_header(hdr); if(err) { fclose(hdr->file_hdl); free(hdr->edfparam); free(hdr->wrbuf); free(hdr); hdrlist[handle] = NULL; free(write_annotationslist[handle]); write_annotationslist[handle] = NULL; edf_files_open--; return err; } for(k=0; kannots_in_file; k++) { p = edflib_fprint_ll_number_nonlocalized(hdr->file_hdl, (hdr->datarecords * hdr->long_data_record_duration + hdr->starttime_offset) / EDFLIB_TIME_DIMENSION, 0, 1); if((hdr->long_data_record_duration % EDFLIB_TIME_DIMENSION) || (hdr->starttime_offset)) { fputc('.', hdr->file_hdl); p++; p += edflib_fprint_ll_number_nonlocalized(hdr->file_hdl, (hdr->datarecords * hdr->long_data_record_duration + hdr->starttime_offset) % EDFLIB_TIME_DIMENSION, 7, 0); } fputc(20, hdr->file_hdl); fputc(20, hdr->file_hdl); p += 2; for(; ptotal_annot_bytes; p++) { fputc(0, hdr->file_hdl); } hdr->datarecords++; } } if(hdr->datarecords<100000000LL) { fseeko(hdr->file_hdl, 236LL, SEEK_SET); p = edflib_fprint_int_number_nonlocalized(hdr->file_hdl, (int)(hdr->datarecords), 0, 0); if(p < 2) { fputc(' ', hdr->file_hdl); } } datarecords = 0LL; offset = (long long)((hdr->edfsignals + hdr->nr_annot_chns + 1) * 256); datrecsize = hdr->total_annot_bytes; for(i=0; iedfsignals; i++) { if(hdr->edf) { if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { offset += (long long)(hdr->edfparam[i].smp_per_record * 2); } else if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i < (hdr->edfsignals / 2)) { offset += (long long)(hdr->edfparam[i].smp_per_record * 2); } } datrecsize += (hdr->edfparam[i].smp_per_record * 2); } else { if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { offset += (long long)(hdr->edfparam[i].smp_per_record * 3); } else if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i < (hdr->edfsignals / 2)) { offset += (long long)(hdr->edfparam[i].smp_per_record * 3); } } datrecsize += (hdr->edfparam[i].smp_per_record * 3); } } j = 0; for(k=0; kannots_in_file; k++) { annot2 = write_annotationslist[handle] + k; annot2->onset += hdr->starttime_offset / 10LL; p = 0; if(j==0) // first annotation signal { if(fseeko(hdr->file_hdl, offset, SEEK_SET)) { break; } p += edflib_snprint_ll_number_nonlocalized(str, (datarecords * hdr->long_data_record_duration + hdr->starttime_offset) / EDFLIB_TIME_DIMENSION, 0, 1, EDFLIB_ANNOTATION_BYTES * 2); if((hdr->long_data_record_duration % EDFLIB_TIME_DIMENSION) || (hdr->starttime_offset)) { str[p++] = '.'; n = edflib_snprint_ll_number_nonlocalized(str + p, (datarecords * hdr->long_data_record_duration + hdr->starttime_offset) % EDFLIB_TIME_DIMENSION, 7, 0, (EDFLIB_ANNOTATION_BYTES * 2) - p); p += n; } str[p++] = 20; str[p++] = 20; str[p++] = 0; } n = edflib_snprint_ll_number_nonlocalized(str + p, annot2->onset / 1000000LL, 0, 1, (EDFLIB_ANNOTATION_BYTES * 2) - p); p += n; if(annot2->onset % 1000000LL) { str[p++] = '.'; n = edflib_snprint_ll_number_nonlocalized(str + p, annot2->onset % 1000000LL, 6, 0, (EDFLIB_ANNOTATION_BYTES * 2) - p); p += n; } if(annot2->duration>=0LL) { str[p++] = 21; n = edflib_snprint_ll_number_nonlocalized(str + p, annot2->duration / 1000000LL, 0, 0, (EDFLIB_ANNOTATION_BYTES * 2) - p); p += n; if(annot2->duration % 1000000LL) { str[p++] = '.'; n = edflib_snprint_ll_number_nonlocalized(str + p, annot2->duration % 1000000LL, 6, 0, (EDFLIB_ANNOTATION_BYTES * 2) - p); p += n; } } str[p++] = 20; for(i=0; iannotation[i]==0) { break; } str[p++] = annot2->annotation[i]; } str[p++] = 20; for(; pfile_hdl); if(nmemb != 1) { break; } j++; if(j >= hdr->nr_annot_chns) { j = 0; offset += datrecsize; datarecords++; if(datarecords>=hdr->datarecords) { break; } } } free(write_annotationslist[handle]); } else { free(annotationslist[handle]); } fclose(hdr->file_hdl); free(hdr->edfparam); free(hdr->wrbuf); free(hdr); hdrlist[handle] = NULL; edf_files_open--; return 0; } EDFLIB_API long long edfseek(int handle, int edfsignal, long long offset, int whence) { long long smp_in_file; int channel; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(edfsignal<0) return -1; if(hdrlist[handle]->writemode) return -1; if(edfsignal>=(hdrlist[handle]->edfsignals - hdrlist[handle]->nr_annot_chns)) return -1; channel = hdrlist[handle]->mapped_signals[edfsignal]; smp_in_file = hdrlist[handle]->edfparam[channel].smp_per_record * hdrlist[handle]->datarecords; if(whence==EDFSEEK_SET) { hdrlist[handle]->edfparam[channel].sample_pntr = offset; } if(whence==EDFSEEK_CUR) { hdrlist[handle]->edfparam[channel].sample_pntr += offset; } if(whence==EDFSEEK_END) { hdrlist[handle]->edfparam[channel].sample_pntr = (hdrlist[handle]->edfparam[channel].smp_per_record * hdrlist[handle]->datarecords) + offset; } if(hdrlist[handle]->edfparam[channel].sample_pntr > smp_in_file) { hdrlist[handle]->edfparam[channel].sample_pntr = smp_in_file; } if(hdrlist[handle]->edfparam[channel].sample_pntr < 0LL) { hdrlist[handle]->edfparam[channel].sample_pntr = 0LL; } return hdrlist[handle]->edfparam[channel].sample_pntr; } EDFLIB_API long long edftell(int handle, int edfsignal) { int channel; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(edfsignal<0) return -1; if(hdrlist[handle]->writemode) return -1; if(edfsignal>=(hdrlist[handle]->edfsignals - hdrlist[handle]->nr_annot_chns)) return -1; channel = hdrlist[handle]->mapped_signals[edfsignal]; return hdrlist[handle]->edfparam[channel].sample_pntr; } EDFLIB_API int edfrewind(int handle, int edfsignal) { int channel; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(edfsignal<0) return -1; if(hdrlist[handle]->writemode) return -1; if(edfsignal>=(hdrlist[handle]->edfsignals - hdrlist[handle]->nr_annot_chns)) return -1; channel = hdrlist[handle]->mapped_signals[edfsignal]; hdrlist[handle]->edfparam[channel].sample_pntr = 0LL; return 0; } EDFLIB_API int edfread_physical_samples(int handle, int edfsignal, int n, double *buf) { int bytes_per_smpl=2, tmp, i, channel; double phys_bitvalue, phys_offset; long long smp_in_file, offset, sample_pntr, smp_per_record, jump; edfhdrblock_t *hdr; union { unsigned int one; signed int one_signed; unsigned short two[2]; signed short two_signed[2]; unsigned char four[4]; } var; FILE *file; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(edfsignal<0) return -1; if(hdrlist[handle]->writemode) return -1; if(edfsignal>=(hdrlist[handle]->edfsignals - hdrlist[handle]->nr_annot_chns)) return -1; channel = hdrlist[handle]->mapped_signals[edfsignal]; if(n<0LL) return -1; if(n==0LL) return 0LL; hdr = hdrlist[handle]; if(hdr->edf) { bytes_per_smpl = 2; } if(hdr->bdf) { bytes_per_smpl = 3; } smp_in_file = hdr->edfparam[channel].smp_per_record * hdr->datarecords; if((hdr->edfparam[channel].sample_pntr + n) > smp_in_file) { n = smp_in_file - hdr->edfparam[channel].sample_pntr; if(n==0) return 0LL; if(n<0) return -1; } file = hdr->file_hdl; offset = hdr->hdrsize; offset += (hdr->edfparam[channel].sample_pntr / hdr->edfparam[channel].smp_per_record) * hdr->recordsize; offset += hdr->edfparam[channel].buf_offset; offset += ((hdr->edfparam[channel].sample_pntr % hdr->edfparam[channel].smp_per_record) * bytes_per_smpl); fseeko(file, offset, SEEK_SET); sample_pntr = hdr->edfparam[channel].sample_pntr; smp_per_record = hdr->edfparam[channel].smp_per_record; jump = hdr->recordsize - (smp_per_record * bytes_per_smpl); phys_bitvalue = hdr->edfparam[channel].bitvalue; phys_offset = hdr->edfparam[channel].offset; if(hdr->edf) { for(i=0; i hdr->edfparam[channel].dig_max) { var.two_signed[0] = hdr->edfparam[channel].dig_max; } else if(var.two_signed[0] < hdr->edfparam[channel].dig_min) { var.two_signed[0] = hdr->edfparam[channel].dig_min; } buf[i] = phys_bitvalue * (phys_offset + (double)var.two_signed[0]); sample_pntr++; } } if(hdr->bdf) { for(i=0; i hdr->edfparam[channel].dig_max) { var.one_signed = hdr->edfparam[channel].dig_max; } else if(var.one_signed < hdr->edfparam[channel].dig_min) { var.one_signed = hdr->edfparam[channel].dig_min; } buf[i] = phys_bitvalue * (phys_offset + (double)var.one_signed); sample_pntr++; } } hdr->edfparam[channel].sample_pntr = sample_pntr; return n; } EDFLIB_API int edfread_digital_samples(int handle, int edfsignal, int n, int *buf) { int bytes_per_smpl=2, tmp, i, channel; long long smp_in_file, offset, sample_pntr, smp_per_record, jump; edfhdrblock_t *hdr; union { unsigned int one; signed int one_signed; unsigned short two[2]; signed short two_signed[2]; unsigned char four[4]; } var; FILE *file; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(edfsignal<0) return -1; if(hdrlist[handle]->writemode) return -1; if(edfsignal>=(hdrlist[handle]->edfsignals - hdrlist[handle]->nr_annot_chns)) return -1; channel = hdrlist[handle]->mapped_signals[edfsignal]; if(n<0LL) return -1; if(n==0LL) return 0LL; hdr = hdrlist[handle]; if(hdr->edf) { bytes_per_smpl = 2; } if(hdr->bdf) { bytes_per_smpl = 3; } smp_in_file = hdr->edfparam[channel].smp_per_record * hdr->datarecords; if((hdr->edfparam[channel].sample_pntr + n) > smp_in_file) { n = smp_in_file - hdr->edfparam[channel].sample_pntr; if(n==0) { return 0LL; } if(n<0) { return -1; } } file = hdr->file_hdl; offset = hdr->hdrsize; offset += (hdr->edfparam[channel].sample_pntr / hdr->edfparam[channel].smp_per_record) * hdr->recordsize; offset += hdr->edfparam[channel].buf_offset; offset += ((hdr->edfparam[channel].sample_pntr % hdr->edfparam[channel].smp_per_record) * bytes_per_smpl); fseeko(file, offset, SEEK_SET); sample_pntr = hdr->edfparam[channel].sample_pntr; smp_per_record = hdr->edfparam[channel].smp_per_record; jump = hdr->recordsize - (smp_per_record * bytes_per_smpl); if(hdr->edf) { for(i=0; i hdr->edfparam[channel].dig_max) { var.two_signed[0] = hdr->edfparam[channel].dig_max; } else if(var.two_signed[0] < hdr->edfparam[channel].dig_min) { var.two_signed[0] = hdr->edfparam[channel].dig_min; } buf[i] = var.two_signed[0]; sample_pntr++; } } if(hdr->bdf) { for(i=0; i hdr->edfparam[channel].dig_max) { var.one_signed = hdr->edfparam[channel].dig_max; } else if(var.one_signed < hdr->edfparam[channel].dig_min) { var.one_signed = hdr->edfparam[channel].dig_min; } buf[i] = var.one_signed; sample_pntr++; } } hdr->edfparam[channel].sample_pntr = sample_pntr; return n; } EDFLIB_API int edf_get_annotation(int handle, int n, edflib_annotation_t *annot) { memset(annot, 0, sizeof(edflib_annotation_t)); if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(hdrlist[handle]->writemode) return -1; if(n<0) return -1; if(n>=hdrlist[handle]->annots_in_file) return -1; annot->onset = (annotationslist[handle] + n)->onset; annot->duration_l = (annotationslist[handle] + n)->duration_l; edflib_strlcpy(annot->duration, (annotationslist[handle] + n)->duration, 20); edflib_strlcpy(annot->annotation, (annotationslist[handle] + n)->annotation, EDFLIB_MAX_ANNOTATION_LEN + 1); return 0; } static edfhdrblock_t * edflib_check_edf_file(FILE *inputfile, int *edf_error) { int i, j, p, r=0, n, dotposition, error; char *edf_hdr, scratchpad[128], scratchpad2[64]; edfhdrblock_t *edfhdr; /***************** check header ******************************/ edf_hdr = (char *)calloc(1, 256); if(edf_hdr==NULL) { *edf_error = EDFLIB_MALLOC_ERROR; return NULL; } edfhdr = (edfhdrblock_t *)calloc(1, sizeof(edfhdrblock_t)); if(edfhdr==NULL) { free(edf_hdr); *edf_error = EDFLIB_MALLOC_ERROR; return NULL; } rewind(inputfile); if(fread(edf_hdr, 256, 1, inputfile)!=1) { *edf_error = EDFLIB_FILE_READ_ERROR; free(edf_hdr); free(edfhdr); return NULL; } /**************************** VERSION ***************************************/ strncpy(scratchpad, edf_hdr, 8); scratchpad[8] = 0; if(((signed char *)scratchpad)[0]==-1) /* BDF-file */ { for(i=1; i<8; i++) { if((scratchpad[i]<32)||(scratchpad[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } if(strcmp(scratchpad + 1, "BIOSEMI")) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } edfhdr->bdf = 1; } else /* EDF-file */ { for(i=0; i<8; i++) { if((scratchpad[i]<32)||(scratchpad[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } if(strcmp(scratchpad, "0 ")) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } edfhdr->edf = 1; } strncpy(edfhdr->version, edf_hdr, 8); edfhdr->version[8] = 0; if(edfhdr->bdf) edfhdr->version[0] = '.'; /********************* PATIENTNAME *********************************************/ strncpy(scratchpad, edf_hdr + 8, 80); scratchpad[80] = 0; for(i=0; i<80; i++) { if((((unsigned char *)scratchpad)[i]<32)||(((unsigned char *)scratchpad)[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } strncpy(edfhdr->patient, edf_hdr + 8, 80); edfhdr->patient[80] = 0; /********************* RECORDING *********************************************/ strncpy(scratchpad, edf_hdr + 88, 80); scratchpad[80] = 0; for(i=0; i<80; i++) { if((((unsigned char *)scratchpad)[i]<32)||(((unsigned char *)scratchpad)[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } strncpy(edfhdr->recording, edf_hdr + 88, 80); edfhdr->recording[80] = 0; /********************* STARTDATE *********************************************/ strncpy(scratchpad, edf_hdr + 168, 8); scratchpad[8] = 0; for(i=0; i<8; i++) { if((scratchpad[i]<32)||(scratchpad[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } error = 0; if((edf_hdr[170]!='.')||(edf_hdr[173]!='.')) error = 1; if((edf_hdr[168]<48)||(edf_hdr[168]>57)) error = 1; if((edf_hdr[169]<48)||(edf_hdr[169]>57)) error = 1; if((edf_hdr[171]<48)||(edf_hdr[171]>57)) error = 1; if((edf_hdr[172]<48)||(edf_hdr[172]>57)) error = 1; if((edf_hdr[174]<48)||(edf_hdr[174]>57)) error = 1; if((edf_hdr[175]<48)||(edf_hdr[175]>57)) error = 1; strncpy(scratchpad, edf_hdr + 168, 8); if(error) { scratchpad[8] = 0; *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } scratchpad[2] = 0; scratchpad[5] = 0; scratchpad[8] = 0; if((edflib_atoi_nonlocalized(scratchpad)<1)||(edflib_atoi_nonlocalized(scratchpad)>31)) { strncpy(scratchpad, edf_hdr + 168, 8); scratchpad[8] = 0; *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } if((edflib_atoi_nonlocalized(scratchpad+3)<1)||(edflib_atoi_nonlocalized(scratchpad+3)>12)) { strncpy(scratchpad, edf_hdr + 168, 8); scratchpad[8] = 0; *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } edfhdr->startdate_day = edflib_atoi_nonlocalized(scratchpad); edfhdr->startdate_month = edflib_atoi_nonlocalized(scratchpad + 3); edfhdr->startdate_year = edflib_atoi_nonlocalized(scratchpad + 6); if(edfhdr->startdate_year>84) { edfhdr->startdate_year += 1900; } else { edfhdr->startdate_year += 2000; } /********************* STARTTIME *********************************************/ strncpy(scratchpad, edf_hdr + 176, 8); scratchpad[8] = 0; for(i=0; i<8; i++) { if((scratchpad[i]<32)||(scratchpad[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } error = 0; if((edf_hdr[178]!='.')||(edf_hdr[181]!='.')) error = 1; if((edf_hdr[176]<48)||(edf_hdr[176]>57)) error = 1; if((edf_hdr[177]<48)||(edf_hdr[177]>57)) error = 1; if((edf_hdr[179]<48)||(edf_hdr[179]>57)) error = 1; if((edf_hdr[180]<48)||(edf_hdr[180]>57)) error = 1; if((edf_hdr[182]<48)||(edf_hdr[182]>57)) error = 1; if((edf_hdr[183]<48)||(edf_hdr[183]>57)) error = 1; strncpy(scratchpad, edf_hdr + 176, 8); if(error) { strncpy(scratchpad, edf_hdr + 176, 8); scratchpad[8] = 0; *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } scratchpad[2] = 0; scratchpad[5] = 0; scratchpad[8] = 0; if(edflib_atoi_nonlocalized(scratchpad)>23) { strncpy(scratchpad, edf_hdr + 176, 8); scratchpad[8] = 0; *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } if(edflib_atoi_nonlocalized(scratchpad+3)>59) { strncpy(scratchpad, edf_hdr + 176, 8); scratchpad[8] = 0; *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } if(edflib_atoi_nonlocalized(scratchpad+6)>59) { strncpy(scratchpad, edf_hdr + 176, 8); scratchpad[8] = 0; *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } edfhdr->starttime_hour = edflib_atoi_nonlocalized(scratchpad); edfhdr->starttime_minute = edflib_atoi_nonlocalized(scratchpad + 3); edfhdr->starttime_second = edflib_atoi_nonlocalized(scratchpad + 6); edfhdr->l_starttime = 3600 * edflib_atoi_nonlocalized(scratchpad); edfhdr->l_starttime += 60 * edflib_atoi_nonlocalized(scratchpad + 3); edfhdr->l_starttime += edflib_atoi_nonlocalized(scratchpad + 6); edfhdr->l_starttime *= EDFLIB_TIME_DIMENSION; /***************** NUMBER OF SIGNALS IN HEADER *******************************/ strncpy(scratchpad, edf_hdr + 252, 4); scratchpad[4] = 0; for(i=0; i<4; i++) { if((scratchpad[i]<32)||(scratchpad[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } if(edflib_is_integer_number(scratchpad)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } edfhdr->edfsignals = edflib_atoi_nonlocalized(scratchpad); if(edfhdr->edfsignals<1) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } if(edfhdr->edfsignals>EDFLIB_MAXSIGNALS) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } /***************** NUMBER OF BYTES IN HEADER *******************************/ strncpy(scratchpad, edf_hdr + 184, 8); scratchpad[8] = 0; for(i=0; i<8; i++) { if((scratchpad[i]<32)||(scratchpad[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } if(edflib_is_integer_number(scratchpad)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } n = edflib_atoi_nonlocalized(scratchpad); if((edfhdr->edfsignals * 256 + 256)!=n) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } /********************* RESERVED FIELD *************************************/ edfhdr->edfplus = 0; edfhdr->discontinuous = 0; strncpy(scratchpad, edf_hdr + 192, 44); scratchpad[44] = 0; for(i=0; i<44; i++) { if((scratchpad[i]<32)||(scratchpad[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } if(edfhdr->edf) { if(!strncmp(scratchpad, "EDF+C ", 44)) { edfhdr->edfplus = 1; edfhdr->discontinuous = 0; } else if(!strncmp(scratchpad, "EDF+D ", 44)) { edfhdr->edfplus = 1; edfhdr->discontinuous = 1; } else if(strncmp(scratchpad, " ", 44)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } if(edfhdr->bdf) { if(!strncmp(scratchpad, "BDF+C ", 44)) { edfhdr->bdfplus = 1; edfhdr->discontinuous = 0; } if(!strncmp(scratchpad, "BDF+D ", 44)) { edfhdr->bdfplus = 1; edfhdr->discontinuous = 1; } } strncpy(edfhdr->reserved, edf_hdr + 192, 44); edfhdr->reserved[44] = 0; /********************* NUMBER OF DATARECORDS *************************************/ strncpy(scratchpad, edf_hdr + 236, 8); scratchpad[8] = 0; for(i=0; i<8; i++) { if((scratchpad[i]<32)||(scratchpad[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } if(edflib_is_integer_number(scratchpad)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } edfhdr->datarecords = edflib_atoi_nonlocalized(scratchpad); if(edfhdr->datarecords<1) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } /********************* DATARECORD DURATION *************************************/ strncpy(scratchpad, edf_hdr + 244, 8); scratchpad[8] = 0; for(i=0; i<8; i++) { if((scratchpad[i]<32)||(scratchpad[i]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } } if(edflib_is_number(scratchpad)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } edfhdr->data_record_duration = edflib_atof_nonlocalized(scratchpad); if(edfhdr->data_record_duration < -0.000001) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr); return NULL; } edfhdr->long_data_record_duration = edflib_get_long_duration(scratchpad); free(edf_hdr); /********************* START WITH THE SIGNALS IN THE HEADER *********************/ edf_hdr = (char *)calloc(1, (edfhdr->edfsignals + 1) * 256); if(edf_hdr==NULL) { *edf_error = EDFLIB_MALLOC_ERROR; free(edfhdr); return NULL; } rewind(inputfile); if(fread(edf_hdr, (edfhdr->edfsignals + 1) * 256, 1, inputfile)!=1) { *edf_error = EDFLIB_FILE_READ_ERROR; free(edf_hdr); free(edfhdr); return NULL; } edfhdr->edfparam = (edfparamblock_t *)calloc(1, sizeof(edfparamblock_t) * edfhdr->edfsignals); if(edfhdr->edfparam==NULL) { *edf_error = EDFLIB_MALLOC_ERROR; free(edf_hdr); free(edfhdr); return NULL; } /**************************** LABELS *************************************/ edfhdr->nr_annot_chns = 0; for(i=0; iedfsignals; i++) { strncpy(scratchpad, edf_hdr + 256 + (i * 16), 16); for(j=0; j<16; j++) { if((scratchpad[j]<32)||(scratchpad[j]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } if(!strncmp(scratchpad, "EDF Annotations ", 16)) { if(edfhdr->edfplus) { edfhdr->annot_ch[edfhdr->nr_annot_chns] = i; edfhdr->nr_annot_chns++; edfhdr->edfparam[i].annotation = 1; } else { scratchpad[3] = '_'; } } if(!strncmp(scratchpad, "BDF Annotations ", 16)) { if(edfhdr->bdfplus) { edfhdr->annot_ch[edfhdr->nr_annot_chns] = i; edfhdr->nr_annot_chns++; edfhdr->edfparam[i].annotation = 1; } else { scratchpad[3] = '_'; } } memcpy(edfhdr->edfparam[i].label, scratchpad, 16); edfhdr->edfparam[i].label[16] = 0; } if(edfhdr->edfplus&&(!edfhdr->nr_annot_chns)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } if(edfhdr->bdfplus&&(!edfhdr->nr_annot_chns)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } if((edfhdr->edfsignals!=edfhdr->nr_annot_chns)||((!edfhdr->edfplus)&&(!edfhdr->bdfplus))) { if(edfhdr->data_record_duration<0.0000001) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } /**************************** TRANSDUCER TYPES *************************************/ for(i=0; iedfsignals; i++) { strncpy(scratchpad, edf_hdr + 256 + (edfhdr->edfsignals * 16) + (i * 80), 80); for(j=0; j<80; j++) { if((scratchpad[j]<32)||(scratchpad[j]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } strncpy(edfhdr->edfparam[i].transducer, edf_hdr + 256 + (edfhdr->edfsignals * 16) + (i * 80), 80); edfhdr->edfparam[i].transducer[80] = 0; if((edfhdr->edfplus) || (edfhdr->bdfplus)) { if(edfhdr->edfparam[i].annotation) { for(j=0; j<80; j++) { if(edfhdr->edfparam[i].transducer[j]!=' ') { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } } } } /**************************** PHYSICAL DIMENSIONS *************************************/ for(i=0; iedfsignals; i++) { strncpy(scratchpad, edf_hdr + 256 + (edfhdr->edfsignals * 96) + (i * 8), 8); for(j=0; j<8; j++) { if((scratchpad[j]<32)||(scratchpad[j]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } strncpy(edfhdr->edfparam[i].physdimension, edf_hdr + 256 + (edfhdr->edfsignals * 96) + (i * 8), 8); edfhdr->edfparam[i].physdimension[8] = 0; } /**************************** PHYSICAL MINIMUMS *************************************/ for(i=0; iedfsignals; i++) { strncpy(scratchpad, edf_hdr + 256 + (edfhdr->edfsignals * 104) + (i * 8), 8); scratchpad[8] = 0; for(j=0; j<8; j++) { if((scratchpad[j]<32)||(scratchpad[j]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } if(edflib_is_number(scratchpad)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } edfhdr->edfparam[i].phys_min = edflib_atof_nonlocalized(scratchpad); } /**************************** PHYSICAL MAXIMUMS *************************************/ for(i=0; iedfsignals; i++) { strncpy(scratchpad, edf_hdr + 256 + (edfhdr->edfsignals * 112) + (i * 8), 8); scratchpad[8] = 0; for(j=0; j<8; j++) { if((scratchpad[j]<32)||(scratchpad[j]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } if(edflib_is_number(scratchpad)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } edfhdr->edfparam[i].phys_max = edflib_atof_nonlocalized(scratchpad); if(edfhdr->edfparam[i].phys_max==edfhdr->edfparam[i].phys_min) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } /**************************** DIGITAL MINIMUMS *************************************/ for(i=0; iedfsignals; i++) { strncpy(scratchpad, edf_hdr + 256 + (edfhdr->edfsignals * 120) + (i * 8), 8); scratchpad[8] = 0; for(j=0; j<8; j++) { if((scratchpad[j]<32)||(scratchpad[j]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } if(edflib_is_integer_number(scratchpad)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } n = edflib_atoi_nonlocalized(scratchpad); if(edfhdr->edfplus) { if(edfhdr->edfparam[i].annotation) { if(n!=-0x8000) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } } if(edfhdr->bdfplus) { if(edfhdr->edfparam[i].annotation) { if(n!=-0x800000) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } } if(edfhdr->edf) { if((n>0x7fff)||(n<-0x8000)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } if(edfhdr->bdf) { if((n>0x7fffff)||(n<-0x800000)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } edfhdr->edfparam[i].dig_min = n; } /**************************** DIGITAL MAXIMUMS *************************************/ for(i=0; iedfsignals; i++) { strncpy(scratchpad, edf_hdr + 256 + (edfhdr->edfsignals * 128) + (i * 8), 8); scratchpad[8] = 0; for(j=0; j<8; j++) { if((scratchpad[j]<32)||(scratchpad[j]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } if(edflib_is_integer_number(scratchpad)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } n = edflib_atoi_nonlocalized(scratchpad); if(edfhdr->edfplus) { if(edfhdr->edfparam[i].annotation) { if(n!=0x7fff) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } } if(edfhdr->bdfplus) { if(edfhdr->edfparam[i].annotation) { if(n!=0x7fffff) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } } if(edfhdr->edf) { if((n>0x7fff)||(n<-0x8000)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } else { if((n>0x7fffff)||(n<-0x800000)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } edfhdr->edfparam[i].dig_max = n; if(edfhdr->edfparam[i].dig_max<(edfhdr->edfparam[i].dig_min + 1)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } /**************************** PREFILTER FIELDS *************************************/ for(i=0; iedfsignals; i++) { strncpy(scratchpad, edf_hdr + 256 + (edfhdr->edfsignals * 136) + (i * 80), 80); for(j=0; j<80; j++) { if((scratchpad[j]<32)||(scratchpad[j]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } strncpy(edfhdr->edfparam[i].prefilter, edf_hdr + 256 + (edfhdr->edfsignals * 136) + (i * 80), 80); edfhdr->edfparam[i].prefilter[80] = 0; if((edfhdr->edfplus) || (edfhdr->bdfplus)) { if(edfhdr->edfparam[i].annotation) { for(j=0; j<80; j++) { if(edfhdr->edfparam[i].prefilter[j]!=' ') { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } } } } /*********************** NR OF SAMPLES IN EACH DATARECORD ********************/ edfhdr->recordsize = 0; for(i=0; iedfsignals; i++) { strncpy(scratchpad, edf_hdr + 256 + (edfhdr->edfsignals * 216) + (i * 8), 8); scratchpad[8] = 0; for(j=0; j<8; j++) { if((scratchpad[j]<32)||(scratchpad[j]>126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } if(edflib_is_integer_number(scratchpad)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } n = edflib_atoi_nonlocalized(scratchpad); if(n<1) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } edfhdr->edfparam[i].smp_per_record = n; edfhdr->recordsize += n; } if(edfhdr->bdf) { edfhdr->recordsize *= 3; if(edfhdr->recordsize > (15 * 1024 * 1024)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } else { edfhdr->recordsize *= 2; if(edfhdr->recordsize > (10 * 1024 * 1024)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } /**************************** RESERVED FIELDS *************************************/ for(i=0; iedfsignals; i++) { strncpy(scratchpad, edf_hdr + 256 + (edfhdr->edfsignals * 224) + (i * 32), 32); if(edfhdr->edf || edfhdr->bdfplus) { for(j=0; j<32; j++) { if(scratchpad[j] != ' ') { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } } else { for(j=0; j<32; j++) { if((scratchpad[j] < 32) || (scratchpad[j] > 126)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } } strncpy(edfhdr->edfparam[i].reserved, edf_hdr + 256 + (edfhdr->edfsignals * 224) + (i * 32), 32); edfhdr->edfparam[i].reserved[32] = 0; } /********************* EDF+ PATIENTNAME *********************************************/ if(edfhdr->edfplus || edfhdr->bdfplus) { error = 0; dotposition = 0; strncpy(scratchpad, edf_hdr + 8, 80); scratchpad[80] = 0; for(i=0; i<80; i++) { if(scratchpad[i]==' ') { dotposition = i; break; } } dotposition++; if((dotposition>73)||(dotposition<2)) error = 1; if(scratchpad[dotposition + 2]!='X') { if(dotposition>65) error = 1; } if((scratchpad[dotposition]!='M')&&(scratchpad[dotposition]!='F')&&(scratchpad[dotposition]!='X')) error = 1; dotposition++; if(scratchpad[dotposition]!=' ') error = 1; if(scratchpad[dotposition + 1]=='X') { if(scratchpad[dotposition + 2]!=' ') error = 1; if(scratchpad[dotposition + 3]==' ') error = 1; } else { if(scratchpad[dotposition + 12]!=' ') error = 1; if(scratchpad[dotposition + 13]==' ') error = 1; dotposition++; strncpy(scratchpad2, scratchpad + dotposition, 11); scratchpad2[11] = 0; if((scratchpad2[2]!='-')||(scratchpad2[6]!='-')) error = 1; scratchpad2[2] = 0; scratchpad2[6] = 0; if((scratchpad2[0]<48)||(scratchpad2[0]>57)) error = 1; if((scratchpad2[1]<48)||(scratchpad2[1]>57)) error = 1; if((scratchpad2[7]<48)||(scratchpad2[7]>57)) error = 1; if((scratchpad2[8]<48)||(scratchpad2[8]>57)) error = 1; if((scratchpad2[9]<48)||(scratchpad2[9]>57)) error = 1; if((scratchpad2[10]<48)||(scratchpad2[10]>57)) error = 1; if((edflib_atoi_nonlocalized(scratchpad2)<1)||(edflib_atoi_nonlocalized(scratchpad2)>31)) error = 1; if(strcmp(scratchpad2 + 3, "JAN")) if(strcmp(scratchpad2 + 3, "FEB")) if(strcmp(scratchpad2 + 3, "MAR")) if(strcmp(scratchpad2 + 3, "APR")) if(strcmp(scratchpad2 + 3, "MAY")) if(strcmp(scratchpad2 + 3, "JUN")) if(strcmp(scratchpad2 + 3, "JUL")) if(strcmp(scratchpad2 + 3, "AUG")) if(strcmp(scratchpad2 + 3, "SEP")) if(strcmp(scratchpad2 + 3, "OCT")) if(strcmp(scratchpad2 + 3, "NOV")) if(strcmp(scratchpad2 + 3, "DEC")) error = 1; } if(error) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } p = 0; if((edfhdr->patient[p]=='X') && (edfhdr->patient[p+1]==' ')) { edfhdr->plus_patientcode[0] = 0; p += 2; } else { for(i=0; i<(80-p); i++) { if(edfhdr->patient[i+p]==' ') { break; } edfhdr->plus_patientcode[i] = edfhdr->patient[i+p]; if(edfhdr->plus_patientcode[i]=='_') edfhdr->plus_patientcode[i] = ' '; } edfhdr->plus_patientcode[i] = 0; p += i + 1; } if(edfhdr->patient[p]=='M') { edflib_strlcpy(edfhdr->plus_sex, "Male", 16); } if(edfhdr->patient[p]=='F') { edflib_strlcpy(edfhdr->plus_sex, "Female", 16); } if(edfhdr->patient[p]=='X') { edfhdr->plus_sex[0] = 0; } for(i=0; i<(80-p);i++) { if(edfhdr->patient[i+p]==' ') { break; } } p += i + 1; if(edfhdr->patient[p]=='X') { edfhdr->plus_birthdate[0] = 0; edfhdr->plus_birthdate_day = 0; edfhdr->plus_birthdate_month = 0; edfhdr->plus_birthdate_year = 0; p += 2; } else { for(i=0; i<(80-p); i++) { if(edfhdr->patient[i+p]==' ') { break; } edfhdr->plus_birthdate[i] = edfhdr->patient[i+p]; } edfhdr->plus_birthdate[2] = ' '; edfhdr->plus_birthdate[3] += 32; edfhdr->plus_birthdate[4] += 32; edfhdr->plus_birthdate[5] += 32; edfhdr->plus_birthdate[6] = ' '; edfhdr->plus_birthdate[11] = 0; p += i + 1; edfhdr->plus_birthdate_day = edflib_atoi_nonlocalized(edfhdr->plus_birthdate); if(!strncmp(edfhdr->plus_birthdate + 3, "jan", 3)) { edfhdr->plus_birthdate_month = 1; } else if(!strncmp(edfhdr->plus_birthdate + 3, "feb", 3)) { edfhdr->plus_birthdate_month = 2; } else if(!strncmp(edfhdr->plus_birthdate + 3, "mar", 3)) { edfhdr->plus_birthdate_month = 3; } else if(!strncmp(edfhdr->plus_birthdate + 3, "apr", 3)) { edfhdr->plus_birthdate_month = 4; } else if(!strncmp(edfhdr->plus_birthdate + 3, "may", 3)) { edfhdr->plus_birthdate_month = 5; } else if(!strncmp(edfhdr->plus_birthdate + 3, "jun", 3)) { edfhdr->plus_birthdate_month = 6; } else if(!strncmp(edfhdr->plus_birthdate + 3, "jul", 3)) { edfhdr->plus_birthdate_month = 7; } else if(!strncmp(edfhdr->plus_birthdate + 3, "aug", 3)) { edfhdr->plus_birthdate_month = 8; } else if(!strncmp(edfhdr->plus_birthdate + 3, "sep", 3)) { edfhdr->plus_birthdate_month = 9; } else if(!strncmp(edfhdr->plus_birthdate + 3, "oct", 3)) { edfhdr->plus_birthdate_month = 10; } else if(!strncmp(edfhdr->plus_birthdate + 3, "nov", 3)) { edfhdr->plus_birthdate_month = 11; } else if(!strncmp(edfhdr->plus_birthdate + 3, "dec", 3)) { edfhdr->plus_birthdate_month = 12; } else { edfhdr->plus_birthdate_month = 0; } edfhdr->plus_birthdate_year = edflib_atoi_nonlocalized(edfhdr->plus_birthdate + 7); } for(i=0; i<(80-p);i++) { if(edfhdr->patient[i+p]==' ') { break; } edfhdr->plus_patient_name[i] = edfhdr->patient[i+p]; if(edfhdr->plus_patient_name[i]=='_') edfhdr->plus_patient_name[i] = ' '; } edfhdr->plus_patient_name[i] = 0; p += i + 1; for(i=0; i<(80-p);i++) { edfhdr->plus_patient_additional[i] = edfhdr->patient[i+p]; } edfhdr->plus_patient_additional[i] = 0; p += i + 1; } /********************* EDF+ RECORDINGFIELD *********************************************/ if(edfhdr->edfplus || edfhdr->bdfplus) { error = 0; strncpy(scratchpad, edf_hdr + 88, 80); scratchpad[80] = 0; if(strncmp(scratchpad, "Startdate ", 10)) error = 1; if(scratchpad[10]=='X') { if(scratchpad[11]!=' ') error = 1; if(scratchpad[12]==' ') error = 1; p = 12; } else { if(scratchpad[21]!=' ') error = 1; if(scratchpad[22]==' ') error = 1; p = 22; strncpy(scratchpad2, scratchpad + 10, 11); scratchpad2[11] = 0; if((scratchpad2[2]!='-')||(scratchpad2[6]!='-')) error = 1; scratchpad2[2] = 0; scratchpad2[6] = 0; if((scratchpad2[0]<48)||(scratchpad2[0]>57)) error = 1; if((scratchpad2[1]<48)||(scratchpad2[1]>57)) error = 1; if((scratchpad2[7]<48)||(scratchpad2[7]>57)) error = 1; if((scratchpad2[8]<48)||(scratchpad2[8]>57)) error = 1; if((scratchpad2[9]<48)||(scratchpad2[9]>57)) error = 1; if((scratchpad2[10]<48)||(scratchpad2[10]>57)) error = 1; if((edflib_atoi_nonlocalized(scratchpad2)<1)||(edflib_atoi_nonlocalized(scratchpad2)>31)) error = 1; r = 0; if(!strcmp(scratchpad2 + 3, "JAN")) r = 1; else if(!strcmp(scratchpad2 + 3, "FEB")) r = 2; else if(!strcmp(scratchpad2 + 3, "MAR")) r = 3; else if(!strcmp(scratchpad2 + 3, "APR")) r = 4; else if(!strcmp(scratchpad2 + 3, "MAY")) r = 5; else if(!strcmp(scratchpad2 + 3, "JUN")) r = 6; else if(!strcmp(scratchpad2 + 3, "JUL")) r = 7; else if(!strcmp(scratchpad2 + 3, "AUG")) r = 8; else if(!strcmp(scratchpad2 + 3, "SEP")) r = 9; else if(!strcmp(scratchpad2 + 3, "OCT")) r = 10; else if(!strcmp(scratchpad2 + 3, "NOV")) r = 11; else if(!strcmp(scratchpad2 + 3, "DEC")) r = 12; else error = 1; } n = 0; for(i=p; i<80; i++) { if(i>78) { error = 1; break; } if(scratchpad[i]==' ') { n++; if(scratchpad[i + 1]==' ') { error = 1; break; } } if(n>1) break; } if(error) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } if(edf_hdr[98]!='X') { error = 0; strncpy(scratchpad, edf_hdr + 168, 8); scratchpad[2] = 0; scratchpad[5] = 0; scratchpad[8] = 0; if(edflib_atoi_nonlocalized(scratchpad)!=edflib_atoi_nonlocalized(scratchpad2)) error = 1; if(edflib_atoi_nonlocalized(scratchpad+3)!=r) error = 1; if(edflib_atoi_nonlocalized(scratchpad+6)!=edflib_atoi_nonlocalized(scratchpad2+9)) error = 1; if(error) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } if(edfhdr->startdate_year != edflib_atoi_nonlocalized(scratchpad2 + 7)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } } p = 10; for(i=0; i<(80-p); i++) { if(edfhdr->recording[i+p]==' ') { break; } edfhdr->plus_startdate[i] = edfhdr->recording[i+p]; } edfhdr->plus_startdate[2] = ' '; edfhdr->plus_startdate[3] += 32; edfhdr->plus_startdate[4] += 32; edfhdr->plus_startdate[5] += 32; edfhdr->plus_startdate[6] = ' '; edfhdr->plus_startdate[11] = 0; p += i + 1; if((edfhdr->recording[p]=='X') && (edfhdr->recording[p+1]==' ')) { edfhdr->plus_admincode[0] = 0; p += 2; } else { for(i=0; i<(80-p); i++) { if(edfhdr->recording[i+p]==' ') { break; } edfhdr->plus_admincode[i] = edfhdr->recording[i+p]; if(edfhdr->plus_admincode[i]=='_') edfhdr->plus_admincode[i] = ' '; } edfhdr->plus_admincode[i] = 0; p += i + 1; } if((edfhdr->recording[p]=='X') && (edfhdr->recording[p+1]==' ')) { edfhdr->plus_technician[0] = 0; p += 2; } else { for(i=0; i<(80-p); i++) { if(edfhdr->recording[i+p]==' ') { break; } edfhdr->plus_technician[i] = edfhdr->recording[i+p]; if(edfhdr->plus_technician[i]=='_') edfhdr->plus_technician[i] = ' '; } edfhdr->plus_technician[i] = 0; p += i + 1; } if((edfhdr->recording[p]=='X') && (edfhdr->recording[p+1]==' ')) { edfhdr->plus_equipment[0] = 0; p += 2; } else { for(i=0; i<(80-p); i++) { if(edfhdr->recording[i+p]==' ') { break; } edfhdr->plus_equipment[i] = edfhdr->recording[i+p]; if(edfhdr->plus_equipment[i]=='_') edfhdr->plus_equipment[i] = ' '; } edfhdr->plus_equipment[i] = 0; p += i + 1; } for(i=0; i<(80-p);i++) { edfhdr->plus_recording_additional[i] = edfhdr->recording[i+p]; } edfhdr->plus_recording_additional[i] = 0; p += i + 1; } /********************* FILESIZE *********************************************/ edfhdr->hdrsize = edfhdr->edfsignals * 256 + 256; fseeko(inputfile, 0LL, SEEK_END); if(ftello(inputfile)!=(edfhdr->recordsize * edfhdr->datarecords + edfhdr->hdrsize)) { *edf_error = EDFLIB_FILE_CONTAINS_FORMAT_ERRORS; free(edf_hdr); free(edfhdr->edfparam); free(edfhdr); return NULL; } n = 0; for(i=0; iedfsignals; i++) { edfhdr->edfparam[i].buf_offset = n; if(edfhdr->bdf) n += edfhdr->edfparam[i].smp_per_record * 3; else n += edfhdr->edfparam[i].smp_per_record * 2; edfhdr->edfparam[i].bitvalue = (edfhdr->edfparam[i].phys_max - edfhdr->edfparam[i].phys_min) / (edfhdr->edfparam[i].dig_max - edfhdr->edfparam[i].dig_min); edfhdr->edfparam[i].offset = edfhdr->edfparam[i].phys_max / edfhdr->edfparam[i].bitvalue - edfhdr->edfparam[i].dig_max; } edfhdr->file_hdl = inputfile; free(edf_hdr); return edfhdr; } static int edflib_is_integer_number(char *str) { int i=0, l, hasspace = 0, hassign=0, digit=0; l = strlen(str); if(!l) return 1; if((str[0]=='+')||(str[0]=='-')) { hassign++; i++; } for(; i57)) { return 1; } else { if(hasspace) { return 1; } digit++; } } } if(digit) return 0; else return 1; } static int edflib_is_number(char *str) { int i=0, l, hasspace = 0, hassign=0, digit=0, hasdot=0, hasexp=0; l = strlen(str); if(!l) return 1; if((str[0]=='+')||(str[0]=='-')) { hassign++; i++; } for(; i57))&&str[i]!='.') { return 1; } else { if(hasspace) { return 1; } if(str[i]=='.') { if(hasdot) return 1; hasdot++; } else { digit++; } } } } if(hasexp) { if(++i==l) { return 1; } if((str[i]=='+')||(str[i]=='-')) { hassign++; i++; } for(; i57)) { return 1; } else { if(hasspace) { return 1; } digit++; } } } } if(digit) return 0; else return 1; } static long long edflib_get_long_duration(char *str) { int i, len=8, hasdot=0, dotposition=0; long long value=0, radix; if((str[0] == '+') || (str[0] == '-')) { for(i=0; i<7; i++) { str[i] = str[i+1]; } str[7] = ' '; } for(i=0; i<8; i++) { if(str[i]==' ') { len = i; break; } } for(i=0; i=0; i--) { value += ((long long)(str[i] - 48)) * radix; radix *= 10; } radix = EDFLIB_TIME_DIMENSION / 10; for(i=dotposition+1; i=0; i--) { value += ((long long)(str[i] - 48)) * radix; radix *= 10; } } return value; } EDFLIB_API int edflib_version(void) { return EDFLIB_VERSION; } static int edflib_get_annotations(edfhdrblock_t *edfhdr, int hdl, int read_annotations_mode) { int i, j, k, p, r=0, n, edfsignals, datarecords, recordsize, discontinuous, *annot_ch, nr_annot_chns, max, onset, duration, duration_start, zero, max_tal_ln, error, annots_in_record, annots_in_tal, samplesize=2; char *scratchpad=NULL, *cnv_buf=NULL, *time_in_txt=NULL, *duration_in_txt=NULL; long long data_record_duration, elapsedtime, time_tmp=0; FILE *inputfile=NULL; edfparamblock_t *edfparam=NULL; edf_read_annotationblock_t *new_annotation=NULL, *malloc_list=NULL; inputfile = edfhdr->file_hdl; edfsignals = edfhdr->edfsignals; recordsize = edfhdr->recordsize; edfparam = edfhdr->edfparam; nr_annot_chns = edfhdr->nr_annot_chns; datarecords = edfhdr->datarecords; data_record_duration = edfhdr->long_data_record_duration; discontinuous = edfhdr->discontinuous; annot_ch = edfhdr->annot_ch; if(edfhdr->edfplus) { samplesize = 2; } if(edfhdr->bdfplus) { samplesize = 3; } cnv_buf = (char *)calloc(1, recordsize); if(cnv_buf==NULL) { return 1; } max_tal_ln = 0; for(i=0; i=EDFLIB_TIME_DIMENSION) || (time_tmp<0LL)) { error = 2; goto END; } else { edfhdr->starttime_offset = time_tmp; if(read_annotations_mode==EDFLIB_DO_NOT_READ_ANNOTATIONS) { error = 0; goto END_OUT; } } } elapsedtime = time_tmp; error = 0; break; } } } } for(k=0; k1) { error = 34; goto END; } zero = 0; if((scratchpad[n]==20)||(scratchpad[n]==21)) { if(scratchpad[n]==21) { if(duration||duration_start||onset||annots_in_tal) { /* it's not allowed to have multiple duration fields */ error = 35; /* in one TAL or to have a duration field which is */ goto END; /* not immediately behind the onsetfield */ } duration_start = 1; } if((scratchpad[n]==20)&&onset&&(!duration_start)) { if(r||annots_in_record) { if(n >= 0) { if(edfhdr->annots_in_file >= edfhdr->annotlist_sz) { malloc_list = (edf_read_annotationblock_t *)realloc(annotationslist[hdl], sizeof(edf_read_annotationblock_t) * (edfhdr->annotlist_sz + EDFLIB_ANNOT_MEMBLOCKSZ)); if(malloc_list==NULL) { free(cnv_buf); free(scratchpad); free(time_in_txt); free(duration_in_txt); return -1; } annotationslist[hdl] = malloc_list; edfhdr->annotlist_sz += EDFLIB_ANNOT_MEMBLOCKSZ; } new_annotation = annotationslist[hdl] + edfhdr->annots_in_file; new_annotation->annotation[0] = 0; if(duration) { edflib_strlcpy(new_annotation->duration, duration_in_txt, 20); new_annotation->duration_l = edflib_get_long_time(duration_in_txt); } else { new_annotation->duration[0] = 0; new_annotation->duration_l = -EDFLIB_TIME_DIMENSION; } for(j=0; jannotation[j] = scratchpad[j]; if((((unsigned char *)new_annotation->annotation)[j] < 32) || (((unsigned char *)new_annotation->annotation)[j] == 127)) { if((new_annotation->annotation[j] != '\t') && (new_annotation->annotation[j] != '\n') && (new_annotation->annotation[j] != '\r')) { error = 38; /* non-printable characters in description string */ goto END; } } } new_annotation->annotation[j] = 0; new_annotation->onset = edflib_get_long_time(time_in_txt); new_annotation->onset -= edfhdr->starttime_offset; edfhdr->annots_in_file++; if(read_annotations_mode==EDFLIB_READ_ANNOTATIONS) { if(!(strncmp(new_annotation->annotation, "Recording ends", 14))) { if(nr_annot_chns==1) { goto END; } } } } } annots_in_tal++; annots_in_record++; n = 0; continue; } if(!onset) { scratchpad[n] = 0; if(edflib_is_onset_number(scratchpad)) { error = 36; goto END; } onset = 1; n = 0; edflib_strlcpy(time_in_txt, scratchpad, max_tal_ln + 3); continue; } if(duration_start) { scratchpad[n] = 0; if(edflib_is_duration_number(scratchpad)) { error = 37; goto END; } for(j=0; j126)) { duration_in_txt[j] = '.'; } } duration_in_txt[j] = 0; duration = 1; duration_start = 0; n = 0; continue; } } n++; } END: /****************** end ************************/ if(error) { free(cnv_buf); free(scratchpad); free(time_in_txt); free(duration_in_txt); return 9; } } } END_OUT: free(cnv_buf); free(scratchpad); free(time_in_txt); free(duration_in_txt); return 0; } static int edflib_is_duration_number(char *str) { int i, l, hasdot = 0; l = strlen(str); if(!l) return 1; if((str[0] == '.')||(str[l-1] == '.')) return 1; for(i=0; i57)) return 1; } } return 0; } static int edflib_is_onset_number(char *str) { int i, l, hasdot = 0; l = strlen(str); if(l<2) return 1; if((str[0]!='+')&&(str[0]!='-')) return 1; if((str[1] == '.')||(str[l-1] == '.')) return 1; for(i=1; i57)) return 1; } } return 0; } static long long edflib_get_long_time(char *str) { int i, len, hasdot=0, dotposition=0, neg=0; long long value=0, radix; if(str[0] == '+') { str++; } else if(str[0] == '-') { neg = 1; str++; } len = strlen(str); for(i=0; i=0; i--) { value += ((long long)(str[i] - 48)) * radix; radix *= 10; } radix = EDFLIB_TIME_DIMENSION / 10; for(i=dotposition+1; i=0; i--) { value += ((long long)(str[i] - 48)) * radix; radix *= 10; } } if(neg) value = -value; return value; } static void edflib_latin1_to_ascii(char *str, int len) { /* ISO 8859-1 except for characters 0x80 to 0x9f which are taken from the extension CP-1252 */ int i, value; const char conv_table[]=".E.,F\".++^.Se.zY.i....|....<...-....\'u.....>...?AAAAAAECEEEEIIIIDNOOOOOxOUUUUYtsaaaaaaeceeeeiiiidnooooo:0uuuuyty"; for(i=0; i31)&&(value<127)) { continue; } if(value < 32) { str[i] = '.'; continue; } str[i] = conv_table[value - 127]; } } static void edflib_latin12utf8(char *latin1_str, int len) { int i, j; unsigned char *str, tmp_str[512]; str = (unsigned char *)latin1_str; j = 0; for(i=0; i126)&&(str[i]<160)) tmp_str[j] = '.'; if(str[i]>159) { if((len-j)<2) { tmp_str[j] = ' '; } else { tmp_str[j] = 192 + (str[i]>>6); j++; tmp_str[j] = 128 + (str[i]&63); } } j++; if(j>=len) break; } for(i=0; i=EDFLIB_MAXFILES) { return EDFLIB_MAXFILES_REACHED; } for(i=0; ipath))) { return EDFLIB_FILE_ALREADY_OPENED; } } } if((number_of_signals<0) || (number_of_signals>=EDFLIB_MAXSIGNALS)) { return EDFLIB_NUMBER_OF_SIGNALS_INVALID; } hdr = (edfhdrblock_t *)calloc(1, sizeof(edfhdrblock_t)); if(hdr==NULL) { return EDFLIB_MALLOC_ERROR; } hdr->edfparam = (edfparamblock_t *)calloc(1, sizeof(edfparamblock_t) * number_of_signals); if(hdr->edfparam==NULL) { free(hdr); return EDFLIB_MALLOC_ERROR; } hdr->writemode = 1; hdr->edfsignals = number_of_signals; handle = -1; for(i=0; iedfparam); free(hdr); return EDFLIB_MAXFILES_REACHED; } write_annotationslist[handle] = NULL; hdr->annotlist_sz = 0; hdr->annots_in_file = 0; file = fopeno(path, "wb"); if(file==NULL) { free(hdr->edfparam); hdr->edfparam = NULL; free(hdr); hdr = NULL; hdrlist[handle] = NULL; return EDFLIB_NO_SUCH_FILE_OR_DIRECTORY; } hdr->file_hdl = file; edflib_strlcpy(hdr->path, path, 1024); edf_files_open++; if(filetype==EDFLIB_FILETYPE_EDFPLUS) { hdr->edf = 1; hdr->edfplus = 1; } if(filetype==EDFLIB_FILETYPE_BDFPLUS) { hdr->bdf = 1; hdr->bdfplus = 1; } hdr->long_data_record_duration = EDFLIB_TIME_DIMENSION; hdr->data_record_duration = 1.0; hdr->nr_annot_chns = 1; return handle; } EDFLIB_API int edf_set_samplefrequency(int handle, int edfsignal, int samplefrequency) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(edfsignal<0) return -1; if(!hdrlist[handle]->writemode) return -1; if(edfsignal>=hdrlist[handle]->edfsignals) return -1; if(samplefrequency<1) return -1; if(hdrlist[handle]->datarecords) return -1; hdrlist[handle]->edfparam[edfsignal].smp_per_record = samplefrequency; return 0; } EDFLIB_API int edf_set_number_of_annotation_signals(int handle, int annot_signals) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((annot_signals < 1) || (annot_signals > EDFLIB_MAX_ANNOTATION_CHANNELS)) return -1; hdrlist[handle]->nr_annot_chns = annot_signals; return 0; } EDFLIB_API int edf_set_datarecord_duration(int handle, int duration) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((duration < 100) || (duration > 6000000)) return -1; hdrlist[handle]->long_data_record_duration = (long long)duration * 100LL; if(hdrlist[handle]->long_data_record_duration < (EDFLIB_TIME_DIMENSION * 10LL)) { hdrlist[handle]->long_data_record_duration /= 10LL; hdrlist[handle]->long_data_record_duration *= 10LL; } else { hdrlist[handle]->long_data_record_duration /= 100LL; hdrlist[handle]->long_data_record_duration *= 100LL; } hdrlist[handle]->data_record_duration = ((double)(hdrlist[handle]->long_data_record_duration)) / EDFLIB_TIME_DIMENSION; return 0; } EDFLIB_API int edf_set_micro_datarecord_duration(int handle, int duration) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((duration < 1) || (duration > 9999)) return -1; hdrlist[handle]->long_data_record_duration = (long long)duration * 10LL; hdrlist[handle]->data_record_duration = ((double)(hdrlist[handle]->long_data_record_duration)) / EDFLIB_TIME_DIMENSION; return 0; } EDFLIB_API int edf_set_subsecond_starttime(int handle, int subsecond) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((subsecond < 0) || (subsecond > 9999999)) return -1; hdrlist[handle]->starttime_offset = (long long)subsecond; return 0; } EDFLIB_API int edfwrite_digital_short_samples(int handle, short *buf) { int i, error, sf, digmax, digmin, edfsignal, value; FILE *file; edfhdrblock_t *hdr; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->edfsignals == 0) return -1; if(hdrlist[handle]->bdf == 1) return -1; hdr = hdrlist[handle]; file = hdr->file_hdl; edfsignal = hdr->signal_write_sequence_pos; if(!hdr->datarecords) { if(!edfsignal) { error = edflib_write_edf_header(hdr); if(error) return error; } } sf = hdr->edfparam[edfsignal].smp_per_record; digmax = hdr->edfparam[edfsignal].dig_max; digmin = hdr->edfparam[edfsignal].dig_min; if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { if(hdr->signal_write_sequence_pos == 0) { if(edflib_write_tal(hdr, file)) return -1; } } else if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(hdr->signal_write_sequence_pos == (hdr->edfsignals / 2)) { if(edflib_write_tal(hdr, file)) return -1; } } if(hdr->edf) { if((digmax != 0x7fff) || (digmin != -0x8000)) { for(i=0; idigmax) buf[i] = digmax; if(buf[i]wrbufsize < (sf * 3)) { free(hdr->wrbuf); hdr->wrbufsize = 0; hdr->wrbuf = (char *)malloc(sf * 3); if(hdr->wrbuf == NULL) return -1; hdr->wrbufsize = sf * 3; } for(i=0; idigmax) value = digmax; if(valuewrbuf[i * 3] = value & 0xff; hdr->wrbuf[i * 3 + 1] = (value >> 8) & 0xff; hdr->wrbuf[i * 3 + 2] = (value >> 16) & 0xff; } if(fwrite(hdr->wrbuf, sf * 3, 1, file) != 1) return -1; } hdr->signal_write_sequence_pos++; if(hdr->signal_write_sequence_pos == hdr->edfsignals) { hdr->signal_write_sequence_pos = 0; if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { if(edflib_write_tal(hdr, file)) return -1; } hdr->datarecords++; fflush(file); } return 0; } EDFLIB_API int edfwrite_digital_samples(int handle, int *buf) { int i, error, sf, digmax, digmin, edfsignal, value; FILE *file; edfhdrblock_t *hdr; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->edfsignals == 0) return -1; hdr = hdrlist[handle]; file = hdr->file_hdl; edfsignal = hdr->signal_write_sequence_pos; if(!hdr->datarecords) { if(!edfsignal) { error = edflib_write_edf_header(hdr); if(error) return error; } } sf = hdr->edfparam[edfsignal].smp_per_record; digmax = hdr->edfparam[edfsignal].dig_max; digmin = hdr->edfparam[edfsignal].dig_min; if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { if(hdr->signal_write_sequence_pos == 0) { if(edflib_write_tal(hdr, file)) return -1; } } else if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(hdr->signal_write_sequence_pos == (hdr->edfsignals / 2)) { if(edflib_write_tal(hdr, file)) return -1; } } if(hdr->edf) { if(hdr->wrbufsize < (sf * 2)) { free(hdr->wrbuf); hdr->wrbufsize = 0; hdr->wrbuf = (char *)malloc(sf * 2); if(hdr->wrbuf == NULL) return -1; hdr->wrbufsize = sf * 2; } for(i=0; idigmax) value = digmax; if(valuewrbuf[i * 2] = value & 0xff; hdr->wrbuf[i * 2 + 1] = (value >> 8) & 0xff; } if(fwrite(hdr->wrbuf, sf * 2, 1, file) != 1) return -1; } else // BDF { if(hdr->wrbufsize < (sf * 3)) { free(hdr->wrbuf); hdr->wrbufsize = 0; hdr->wrbuf = (char *)malloc(sf * 3); if(hdr->wrbuf == NULL) return -1; hdr->wrbufsize = sf * 3; } for(i=0; idigmax) value = digmax; if(valuewrbuf[i * 3] = value & 0xff; hdr->wrbuf[i * 3 + 1] = (value >> 8) & 0xff; hdr->wrbuf[i * 3 + 2] = (value >> 16) & 0xff; } if(fwrite(hdr->wrbuf, sf * 3, 1, file) != 1) return -1; } hdr->signal_write_sequence_pos++; if(hdr->signal_write_sequence_pos == hdr->edfsignals) { hdr->signal_write_sequence_pos = 0; if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { if(edflib_write_tal(hdr, file)) return -1; } hdr->datarecords++; fflush(file); } return 0; } EDFLIB_API int edf_blockwrite_digital_samples(int handle, int *buf) { int i, j, error, sf, digmax, digmin, edfsignals, buf_offset, value; FILE *file; edfhdrblock_t *hdr; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->edfsignals == 0) return -1; if(hdrlist[handle]->signal_write_sequence_pos) return -1; hdr = hdrlist[handle]; file = hdr->file_hdl; edfsignals = hdr->edfsignals; if(!hdr->datarecords) { error = edflib_write_edf_header(hdr); if(error) return error; } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { if(edflib_write_tal(hdr, file)) return -1; } for(j=0, buf_offset=0; jedfparam[j].smp_per_record; digmax = hdr->edfparam[j].dig_max; digmin = hdr->edfparam[j].dig_min; if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(j == (edfsignals / 2)) { if(edflib_write_tal(hdr, file)) return -1; } } if(hdr->edf) { if(hdr->wrbufsize < (sf * 2)) { free(hdr->wrbuf); hdr->wrbufsize = 0; hdr->wrbuf = (char *)malloc(sf * 2); if(hdr->wrbuf == NULL) return -1; hdr->wrbufsize = sf * 2; } for(i=0; idigmax) value = digmax; if(valuewrbuf[i * 2] = value & 0xff; hdr->wrbuf[i * 2 + 1] = (value >> 8) & 0xff; } if(fwrite(hdr->wrbuf, sf * 2, 1, file) != 1) return -1; } else // BDF { if(hdr->wrbufsize < (sf * 3)) { free(hdr->wrbuf); hdr->wrbufsize = 0; hdr->wrbuf = (char *)malloc(sf * 3); if(hdr->wrbuf == NULL) return -1; hdr->wrbufsize = sf * 3; } for(i=0; idigmax) value = digmax; if(valuewrbuf[i * 3] = value & 0xff; hdr->wrbuf[i * 3 + 1] = (value >> 8) & 0xff; hdr->wrbuf[i * 3 + 2] = (value >> 16) & 0xff; } if(fwrite(hdr->wrbuf, sf * 3, 1, file) != 1) return -1; } buf_offset += sf; } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { if(edflib_write_tal(hdr, file)) return -1; } hdr->datarecords++; fflush(file); return 0; } EDFLIB_API int edf_blockwrite_digital_short_samples(int handle, short *buf) { int i, j, error, sf, digmax, digmin, edfsignals, buf_offset, value; FILE *file; edfhdrblock_t *hdr; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->edfsignals == 0) return -1; if(hdrlist[handle]->signal_write_sequence_pos) return -1; if(hdrlist[handle]->bdf == 1) return -1; hdr = hdrlist[handle]; file = hdr->file_hdl; edfsignals = hdr->edfsignals; if(!hdr->datarecords) { error = edflib_write_edf_header(hdr); if(error) { return error; } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { if(edflib_write_tal(hdr, file)) return -1; } for(j=0, buf_offset=0; jedfparam[j].smp_per_record; digmax = hdr->edfparam[j].dig_max; digmin = hdr->edfparam[j].dig_min; if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(j == (edfsignals / 2)) { if(edflib_write_tal(hdr, file)) return -1; } } if(hdr->edf) { if((digmax != 0x7fff) || (digmin != -0x8000)) { for(i=0; i digmax) { buf[i + buf_offset] = digmax; } if(buf[i + buf_offset] < digmin) { buf[i + buf_offset] = digmin; } } } if(fwrite(buf + buf_offset, sf * 2, 1, file) != 1) return -1; } else // BDF { if(hdr->wrbufsize < (sf * 3)) { free(hdr->wrbuf); hdr->wrbufsize = 0; hdr->wrbuf = (char *)malloc(sf * 3); if(hdr->wrbuf == NULL) return -1; hdr->wrbufsize = sf * 3; } for(i=0; idigmax) value = digmax; if(valuewrbuf[i * 3] = value & 0xff; hdr->wrbuf[i * 3 + 1] = (value >> 8) & 0xff; hdr->wrbuf[i * 3 + 2] = (value >> 16) & 0xff; } if(fwrite(hdr->wrbuf, sf * 3, 1, file) != 1) return -1; } buf_offset += sf; } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { if(edflib_write_tal(hdr, file)) return -1; } hdr->datarecords++; fflush(file); return 0; } EDFLIB_API int edf_blockwrite_digital_3byte_samples(int handle, void *buf) { int j, error, edfsignals, total_samples, mid_samples; FILE *file; edfhdrblock_t *hdr; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->edfsignals == 0) return -1; if(hdrlist[handle]->signal_write_sequence_pos) return -1; if(hdrlist[handle]->bdf != 1) return -1; hdr = hdrlist[handle]; file = hdr->file_hdl; edfsignals = hdr->edfsignals; if(!hdr->datarecords) { error = edflib_write_edf_header(hdr); if(error) return error; } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { if(edflib_write_tal(hdr, file)) return -1; } for(j=0, total_samples=0, mid_samples=0; jedfparam[j].smp_per_record; if(j < (edfsignals / 2)) mid_samples = total_samples; } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(mid_samples) { if(fwrite(buf, mid_samples * 3, 1, file) != 1) return -1; if(edflib_write_tal(hdr, file)) return -1; if(total_samples > mid_samples) { if(fwrite(((unsigned char *)buf) + (mid_samples * 3), (total_samples - mid_samples) * 3, 1, file) != 1) return -1; } } else { if(edflib_write_tal(hdr, file)) return -1; if(fwrite(buf, total_samples * 3, 1, file) != 1) return -1; } } else { if(fwrite(buf, total_samples * 3, 1, file) != 1) return -1; } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { if(edflib_write_tal(hdr, file)) return -1; } hdr->datarecords++; fflush(file); return 0; } EDFLIB_API int edfwrite_physical_samples(int handle, double *buf) { int i, error, sf, digmax, digmin, value, edfsignal; double bitvalue, phys_offset; FILE *file; edfhdrblock_t *hdr; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->edfsignals == 0) return -1; hdr = hdrlist[handle]; file = hdr->file_hdl; edfsignal = hdr->signal_write_sequence_pos; if(!hdr->datarecords) { if(!edfsignal) { error = edflib_write_edf_header(hdr); if(error) return error; } } sf = hdr->edfparam[edfsignal].smp_per_record; digmax = hdr->edfparam[edfsignal].dig_max; digmin = hdr->edfparam[edfsignal].dig_min; bitvalue = hdr->edfparam[edfsignal].bitvalue; phys_offset = hdr->edfparam[edfsignal].offset; if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { if(hdr->signal_write_sequence_pos == 0) { if(edflib_write_tal(hdr, file)) return -1; } } else if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(hdr->signal_write_sequence_pos == (hdr->edfsignals / 2)) { if(edflib_write_tal(hdr, file)) return -1; } } if(hdr->edf) { if(hdr->wrbufsize < (sf * 2)) { free(hdr->wrbuf); hdr->wrbufsize = 0; hdr->wrbuf = (char *)malloc(sf * 2); if(hdr->wrbuf == NULL) return -1; hdr->wrbufsize = sf * 2; } for(i=0; idigmax) value = digmax; if(valuewrbuf[i * 2] = value & 0xff; hdr->wrbuf[i * 2 + 1] = (value >> 8) & 0xff; } if(fwrite(hdr->wrbuf, sf * 2, 1, file) != 1) return -1; } else // BDF { if(hdr->wrbufsize < (sf * 3)) { free(hdr->wrbuf); hdr->wrbufsize = 0; hdr->wrbuf = (char *)malloc(sf * 3); if(hdr->wrbuf == NULL) return -1; hdr->wrbufsize = sf * 3; } for(i=0; idigmax) value = digmax; if(valuewrbuf[i * 3] = value & 0xff; hdr->wrbuf[i * 3 + 1] = (value >> 8) & 0xff; hdr->wrbuf[i * 3 + 2] = (value >> 16) & 0xff; } if(fwrite(hdr->wrbuf, sf * 3, 1, file) != 1) return -1; } hdr->signal_write_sequence_pos++; if(hdr->signal_write_sequence_pos == hdr->edfsignals) { hdr->signal_write_sequence_pos = 0; if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { if(edflib_write_tal(hdr, file)) return -1; } hdr->datarecords++; fflush(file); } return 0; } EDFLIB_API int edf_blockwrite_physical_samples(int handle, double *buf) { int i, j, error, sf, digmax, digmin, edfsignals, buf_offset, value; double bitvalue, phys_offset; FILE *file; edfhdrblock_t *hdr; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->edfsignals == 0) return -1; if(hdrlist[handle]->signal_write_sequence_pos) return -1; hdr = hdrlist[handle]; file = hdr->file_hdl; edfsignals = hdr->edfsignals; if(!hdr->datarecords) { error = edflib_write_edf_header(hdr); if(error) return error; } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { if(edflib_write_tal(hdr, file)) return -1; } for(j=0, buf_offset=0; jedfparam[j].smp_per_record; digmax = hdr->edfparam[j].dig_max; digmin = hdr->edfparam[j].dig_min; bitvalue = hdr->edfparam[j].bitvalue; phys_offset = hdr->edfparam[j].offset; if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(j == (edfsignals / 2)) { if(edflib_write_tal(hdr, file)) return -1; } } if(hdr->edf) { if(hdr->wrbufsize < (sf * 2)) { free(hdr->wrbuf); hdr->wrbufsize = 0; hdr->wrbuf = (char *)malloc(sf * 2); if(hdr->wrbuf == NULL) return -1; hdr->wrbufsize = sf * 2; } for(i=0; idigmax) value = digmax; if(valuewrbuf[i * 2] = value & 0xff; hdr->wrbuf[i * 2 + 1] = (value >> 8) & 0xff; } if(fwrite(hdr->wrbuf, sf * 2, 1, file) != 1) return -1; } else // BDF { if(hdr->wrbufsize < (sf * 3)) { free(hdr->wrbuf); hdr->wrbufsize = 0; hdr->wrbuf = (char *)malloc(sf * 3); if(hdr->wrbuf == NULL) return -1; hdr->wrbufsize = sf * 3; } for(i=0; idigmax) value = digmax; if(valuewrbuf[i * 3] = value & 0xff; hdr->wrbuf[i * 3 + 1] = (value >> 8) & 0xff; hdr->wrbuf[i * 3 + 2] = (value >> 16) & 0xff; } if(fwrite(hdr->wrbuf, sf * 3, 1, file) != 1) return -1; } buf_offset += sf; } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { if(edflib_write_tal(hdr, file)) return -1; } hdr->datarecords++; fflush(file); return 0; } static int edflib_write_edf_header(edfhdrblock_t *hdr) { int i, j, p, q, len, rest, edfsignals; char str[128]; struct tm *date_time; time_t elapsed_time; FILE *file; file = hdr->file_hdl; edfsignals = hdr->edfsignals; if(edfsignals<0) return EDFLIB_NO_SIGNALS; if(edfsignals>EDFLIB_MAXSIGNALS) return EDFLIB_TOO_MANY_SIGNALS; hdr->eq_sf = 1; hdr->recordsize = 0; hdr->total_annot_bytes = EDFLIB_ANNOTATION_BYTES * hdr->nr_annot_chns; for(i=0; iedfparam[i].smp_per_record<1) return EDFLIB_NO_SAMPLES_IN_RECORD; if(hdr->edfparam[i].dig_max==hdr->edfparam[i].dig_min) return EDFLIB_DIGMIN_IS_DIGMAX; if(hdr->edfparam[i].dig_maxedfparam[i].dig_min) return EDFLIB_DIGMAX_LOWER_THAN_DIGMIN; if(hdr->edfparam[i].phys_max==hdr->edfparam[i].phys_min) return EDFLIB_PHYSMIN_IS_PHYSMAX; hdr->recordsize += hdr->edfparam[i].smp_per_record; if(i > 0) { if(hdr->edfparam[i].smp_per_record != hdr->edfparam[i-1].smp_per_record) { hdr->eq_sf = 0; } } } if(hdr->edf) { hdr->recordsize *= 2; hdr->recordsize += hdr->total_annot_bytes; if(hdr->recordsize > (10 * 1024 * 1024)) /* datarecord size should not exceed 10MB for EDF */ { return EDFLIB_DATARECORD_SIZE_TOO_BIG; } /* if your application gets hit by this limitation, lower the value for the datarecord duration */ /* using the function edf_set_datarecord_duration() */ } else { hdr->recordsize *= 3; hdr->recordsize += hdr->total_annot_bytes; if(hdr->recordsize > (15 * 1024 * 1024)) /* datarecord size should not exceed 15MB for BDF */ { return EDFLIB_DATARECORD_SIZE_TOO_BIG; } /* if your application gets hit by this limitation, lower the value for the datarecord duration */ /* using the function edf_set_datarecord_duration() */ } for(i=0; iedfparam[i].bitvalue = (hdr->edfparam[i].phys_max - hdr->edfparam[i].phys_min) / (hdr->edfparam[i].dig_max - hdr->edfparam[i].dig_min); hdr->edfparam[i].offset = hdr->edfparam[i].phys_max / hdr->edfparam[i].bitvalue - hdr->edfparam[i].dig_max; } rewind(file); if(hdr->edf) { fprintf(file, "0 "); } else { fprintf(file, "\xff" "BIOSEMI"); } p = 0; if(hdr->plus_birthdate[0]==0) { rest = 72; } else { rest = 62; } len = strlen(hdr->plus_patientcode); if(len && rest) { if(len>rest) { len = rest; rest = 0; } else { rest -= len; } edflib_strlcpy(str, hdr->plus_patientcode, 128); edflib_latin1_to_ascii(str, len); str[len] = 0; for(i=0; iplus_sex[0]=='M') { fputc('M', file); } else { if(hdr->plus_sex[0]=='F') { fputc('F', file); } else { fputc('X', file); } } fputc(' ', file); p +=2; if(hdr->plus_birthdate[0]==0) { fputc('X', file); fputc(' ', file); p +=2; } else { fputc(hdr->plus_birthdate[0], file); fputc(hdr->plus_birthdate[1], file); fputc('-', file); q = edflib_atoi_nonlocalized(&(hdr->plus_birthdate[3])); switch(q) { case 1: fprintf(file, "JAN"); break; case 2: fprintf(file, "FEB"); break; case 3: fprintf(file, "MAR"); break; case 4: fprintf(file, "APR"); break; case 5: fprintf(file, "MAY"); break; case 6: fprintf(file, "JUN"); break; case 7: fprintf(file, "JUL"); break; case 8: fprintf(file, "AUG"); break; case 9: fprintf(file, "SEP"); break; case 10: fprintf(file, "OCT"); break; case 11: fprintf(file, "NOV"); break; case 12: fprintf(file, "DEC"); break; } fputc('-', file); fputc(hdr->plus_birthdate[6], file); fputc(hdr->plus_birthdate[7], file); fputc(hdr->plus_birthdate[8], file); fputc(hdr->plus_birthdate[9], file); fputc(' ', file); p += 12; } len = strlen(hdr->plus_patient_name); if(len && rest) { if(len>rest) { len = rest; rest = 0; } else { rest -= len; } edflib_strlcpy(str, hdr->plus_patient_name, 128); edflib_latin1_to_ascii(str, len); str[len] = 0; for(i=0; iplus_patient_additional); if(len && rest) { if(len>rest) { len = rest; } edflib_strlcpy(str, hdr->plus_patient_additional, 128); edflib_latin1_to_ascii(str, len); str[len] = 0; p += fprintf(file, "%s", str); } for(; p<80; p++) { fputc(' ', file); } if(!hdr->startdate_year) { elapsed_time = time(NULL); date_time = localtime(&elapsed_time); hdr->startdate_year = date_time->tm_year + 1900; hdr->startdate_month = date_time->tm_mon + 1; hdr->startdate_day = date_time->tm_mday; hdr->starttime_hour = date_time->tm_hour; hdr->starttime_minute = date_time->tm_min; hdr->starttime_second = date_time->tm_sec % 60; } p = 0; p += fprintf(file, "Startdate %02u-", hdr->startdate_day); switch(hdr->startdate_month) { case 1 : fprintf(file, "JAN"); break; case 2 : fprintf(file, "FEB"); break; case 3 : fprintf(file, "MAR"); break; case 4 : fprintf(file, "APR"); break; case 5 : fprintf(file, "MAY"); break; case 6 : fprintf(file, "JUN"); break; case 7 : fprintf(file, "JUL"); break; case 8 : fprintf(file, "AUG"); break; case 9 : fprintf(file, "SEP"); break; case 10 : fprintf(file, "OCT"); break; case 11 : fprintf(file, "NOV"); break; case 12 : fprintf(file, "DEC"); break; } p += 3; fputc('-', file); p++; p += edflib_fprint_int_number_nonlocalized(file, hdr->startdate_year, 4, 0); fputc(' ', file); p++; rest = 52; len = strlen(hdr->plus_admincode); if(len && rest) { if(len>rest) { len = rest; rest = 0; } else { rest -= len; } edflib_strlcpy(str, hdr->plus_admincode, 128); edflib_latin1_to_ascii(str, len); str[len] = 0; for(i=0; iplus_technician); if(len && rest) { if(len>rest) { len = rest; rest = 0; } else { rest -= len; } edflib_strlcpy(str, hdr->plus_technician, 128); edflib_latin1_to_ascii(str, len); str[len] = 0; for(i=0; iplus_equipment); if(len && rest) { if(len>rest) { len = rest; rest = 0; } else { rest -= len; } edflib_strlcpy(str, hdr->plus_equipment, 128); edflib_latin1_to_ascii(str, len); str[len] = 0; for(i=0; iplus_recording_additional); if(len && rest) { if(len>rest) { len = rest; } edflib_strlcpy(str, hdr->plus_recording_additional, 128); edflib_latin1_to_ascii(str, len); str[len] = 0; p += fprintf(file, "%s", str); } for(; p<80; p++) { fputc(' ', file); } fprintf(file, "%02u.%02u.%02u", hdr->startdate_day, hdr->startdate_month, (hdr->startdate_year % 100)); fprintf(file, "%02u.%02u.%02u", hdr->starttime_hour, hdr->starttime_minute, hdr->starttime_second); p = edflib_fprint_int_number_nonlocalized(file, (edfsignals + hdr->nr_annot_chns + 1) * 256, 0, 0); for(; p<8; p++) { fputc(' ', file); } if(hdr->edf) { fprintf(file, "EDF+C"); } else { fprintf(file, "BDF+C"); } for(i=0; i<39; i++) { fputc(' ', file); } fprintf(file, "-1 "); if(hdr->long_data_record_duration == EDFLIB_TIME_DIMENSION) { fprintf(file, "1 "); } else { edflib_snprint_number_nonlocalized(str, hdr->data_record_duration, 128); edflib_strlcat(str, " ", 128); str[8] = 0; fprintf(file, "%s", str); } p = edflib_fprint_int_number_nonlocalized(file, edfsignals + hdr->nr_annot_chns, 0, 0); for(; p<4; p++) { fputc(' ', file); } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { fprintf(file, "EDF Annotations "); } else { fprintf(file, "BDF Annotations "); } } } for(i=0; iannot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i == (edfsignals / 2)) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { fprintf(file, "EDF Annotations "); } else { fprintf(file, "BDF Annotations "); } } } } len = strlen(hdr->edfparam[i].label); edflib_latin1_to_ascii(hdr->edfparam[i].label, len); for(j=0; jedfparam[i].label[j], file); } for(; j<16; j++) { fputc(' ', file); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { fprintf(file, "EDF Annotations "); } else { fprintf(file, "BDF Annotations "); } } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { for(j=0; j<(80 * hdr->nr_annot_chns); j++) { fputc(' ', file); } } for(i=0; iannot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i == (edfsignals / 2)) { for(j=0; j<(80 * hdr->nr_annot_chns); j++) { fputc(' ', file); } } } len = strlen(hdr->edfparam[i].transducer); edflib_latin1_to_ascii(hdr->edfparam[i].transducer, len); for(j=0; jedfparam[i].transducer[j], file); } for(; j<80; j++) { fputc(' ', file); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { for(j=0; j<(80 * hdr->nr_annot_chns); j++) { fputc(' ', file); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { for(j=0; jnr_annot_chns; j++) { fprintf(file, " "); } } for(i=0; iannot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i == (edfsignals / 2)) { for(j=0; jnr_annot_chns; j++) { fprintf(file, " "); } } } len = strlen(hdr->edfparam[i].physdimension); edflib_latin1_to_ascii(hdr->edfparam[i].physdimension, len); for(j=0; jedfparam[i].physdimension[j], file); } for(; j<8; j++) { fputc(' ', file); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { for(j=0; jnr_annot_chns; j++) { fprintf(file, " "); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { for(j=0; jnr_annot_chns; j++) { fprintf(file, "-1 "); } } for(i=0; iannot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i == (edfsignals / 2)) { for(j=0; jnr_annot_chns; j++) { fprintf(file, "-1 "); } } } p = edflib_snprint_number_nonlocalized(str, hdr->edfparam[i].phys_min, 128); for(; p<8; p++) { str[p] = ' '; } str[8] = 0; fprintf(file, "%s", str); } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { for(j=0; jnr_annot_chns; j++) { fprintf(file, "-1 "); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { for(j=0; jnr_annot_chns; j++) { fprintf(file, "1 "); } } for(i=0; iannot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i == (edfsignals / 2)) { for(j=0; jnr_annot_chns; j++) { fprintf(file, "1 "); } } } p = edflib_snprint_number_nonlocalized(str, hdr->edfparam[i].phys_max, 128); for(; p<8; p++) { str[p] = ' '; } str[8] = 0; fprintf(file, "%s", str); } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { for(j=0; jnr_annot_chns; j++) { fprintf(file, "1 "); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { fprintf(file, "-32768 "); } else { fprintf(file, "-8388608"); } } } for(i=0; iannot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i == (edfsignals / 2)) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { fprintf(file, "-32768 "); } else { fprintf(file, "-8388608"); } } } } p = edflib_fprint_int_number_nonlocalized(file, hdr->edfparam[i].dig_min, 0, 0); for(; p<8; p++) { fputc(' ', file); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { fprintf(file, "-32768 "); } else { fprintf(file, "-8388608"); } } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { fprintf(file, "32767 "); } else { fprintf(file, "8388607 "); } } } for(i=0; iannot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i == (edfsignals / 2)) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { fprintf(file, "32767 "); } else { fprintf(file, "8388607 "); } } } } p = edflib_fprint_int_number_nonlocalized(file, hdr->edfparam[i].dig_max, 0, 0); for(; p<8; p++) { fputc(' ', file); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { fprintf(file, "32767 "); } else { fprintf(file, "8388607 "); } } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { for(i=0; inr_annot_chns; i++) { for(j=0; j<80; j++) { fputc(' ', file); } } } for(i=0; iannot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i == (edfsignals / 2)) { for(j=0; j<(80 * hdr->nr_annot_chns); j++) { fputc(' ', file); } } } len = strlen(hdr->edfparam[i].prefilter); edflib_latin1_to_ascii(hdr->edfparam[i].prefilter, len); for(j=0; jedfparam[i].prefilter[j], file); } for(; j<80; j++) { fputc(' ', file); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { for(i=0; inr_annot_chns; i++) { for(j=0; j<80; j++) { fputc(' ', file); } } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_START) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { p = edflib_fprint_int_number_nonlocalized(file, EDFLIB_ANNOTATION_BYTES / 2, 0, 0); for(; p<8; p++) { fputc(' ', file); } } else { p = edflib_fprint_int_number_nonlocalized(file, EDFLIB_ANNOTATION_BYTES / 3, 0, 0); for(; p<8; p++) { fputc(' ', file); } } } } for(i=0; iannot_chan_idx_pos == EDF_ANNOT_IDX_POS_MIDDLE) { if(i == (edfsignals / 2)) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { p = edflib_fprint_int_number_nonlocalized(file, EDFLIB_ANNOTATION_BYTES / 2, 0, 0); for(; p<8; p++) { fputc(' ', file); } } else { p = edflib_fprint_int_number_nonlocalized(file, EDFLIB_ANNOTATION_BYTES / 3, 0, 0); for(; p<8; p++) { fputc(' ', file); } } } } } p = edflib_fprint_int_number_nonlocalized(file, hdr->edfparam[i].smp_per_record, 0, 0); for(; p<8; p++) { fputc(' ', file); } } if(hdr->annot_chan_idx_pos == EDF_ANNOT_IDX_POS_END) { for(j=0; jnr_annot_chns; j++) { if(hdr->edf) { p = edflib_fprint_int_number_nonlocalized(file, EDFLIB_ANNOTATION_BYTES / 2, 0, 0); for(; p<8; p++) { fputc(' ', file); } } else { p = edflib_fprint_int_number_nonlocalized(file, EDFLIB_ANNOTATION_BYTES / 3, 0, 0); for(; p<8; p++) { fputc(' ', file); } } } } for(i=0; i<((edfsignals + hdr->nr_annot_chns) * 32); i++) { fputc(' ', file); } return 0; } EDFLIB_API int edf_set_label(int handle, int edfsignal, const char *label) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((edfsignal<0) || (edfsignal>=hdrlist[handle]->edfsignals)) return -1; if(!strncmp(label, "EDF Annotations", 15)) return -1; if(!strncmp(label, "BDF Annotations", 15)) return -1; strncpy(hdrlist[handle]->edfparam[edfsignal].label, label, 16); hdrlist[handle]->edfparam[edfsignal].label[16] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->edfparam[edfsignal].label); return 0; } EDFLIB_API int edf_set_physical_dimension(int handle, int edfsignal, const char *phys_dim) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((edfsignal<0) || (edfsignal>=hdrlist[handle]->edfsignals)) return -1; strncpy(hdrlist[handle]->edfparam[edfsignal].physdimension, phys_dim, 8); hdrlist[handle]->edfparam[edfsignal].physdimension[8] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->edfparam[edfsignal].physdimension); return 0; } EDFLIB_API int edf_set_physical_maximum(int handle, int edfsignal, double phys_max) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((edfsignal<0) || (edfsignal>=hdrlist[handle]->edfsignals)) return -1; hdrlist[handle]->edfparam[edfsignal].phys_max = phys_max; return 0; } EDFLIB_API int edf_set_physical_minimum(int handle, int edfsignal, double phys_min) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((edfsignal<0) || (edfsignal>=hdrlist[handle]->edfsignals)) return -1; hdrlist[handle]->edfparam[edfsignal].phys_min = phys_min; return 0; } EDFLIB_API int edf_set_digital_maximum(int handle, int edfsignal, int dig_max) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((edfsignal<0) || (edfsignal>=hdrlist[handle]->edfsignals)) return -1; if(hdrlist[handle]->edf) { if(dig_max > 0x7fff) return -1; } else { if(dig_max > 0x7fffff) return -1; } hdrlist[handle]->edfparam[edfsignal].dig_max = dig_max; return 0; } EDFLIB_API int edf_set_digital_minimum(int handle, int edfsignal, int dig_min) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((edfsignal<0) || (edfsignal>=hdrlist[handle]->edfsignals)) return -1; if(hdrlist[handle]->edf) { if(dig_min < (-0x8000)) return -1; } else { if(dig_min < (-0x800000)) return -1; } hdrlist[handle]->edfparam[edfsignal].dig_min = dig_min; return 0; } EDFLIB_API int edf_set_patientname(int handle, const char *patientname) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; strncpy(hdrlist[handle]->plus_patient_name, patientname, 80); hdrlist[handle]->plus_patient_name[80] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->plus_patient_name); return 0; } EDFLIB_API int edf_set_patientcode(int handle, const char *patientcode) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; strncpy(hdrlist[handle]->plus_patientcode, patientcode, 80); hdrlist[handle]->plus_patientcode[80] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->plus_patientcode); return 0; } EDFLIB_API int edf_set_sex(int handle, int sex) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((sex<0)||(sex>1)) return -1; if(sex) { hdrlist[handle]->plus_sex[0] = 'M'; } else { hdrlist[handle]->plus_sex[0] = 'F'; } hdrlist[handle]->plus_sex[1] = 0; return 0; } EDFLIB_API int edf_set_gender(int handle, int sex) { return edf_set_sex(handle, sex); } EDFLIB_API int edf_set_birthdate(int handle, int birthdate_year, int birthdate_month, int birthdate_day) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((birthdate_year<1800) || (birthdate_year>3000) || (birthdate_month<1) || (birthdate_month>12) || (birthdate_day<1) || (birthdate_day>31)) { return -1; } sprintf(hdrlist[handle]->plus_birthdate, "%02i.%02i.%02i%02i", birthdate_day, birthdate_month, birthdate_year / 100, birthdate_year % 100); hdrlist[handle]->plus_birthdate[10] = 0; return 0; } EDFLIB_API int edf_set_patient_additional(int handle, const char *patient_additional) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; strncpy(hdrlist[handle]->plus_patient_additional, patient_additional, 80); hdrlist[handle]->plus_patient_additional[80] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->plus_patient_additional); return 0; } EDFLIB_API int edf_set_admincode(int handle, const char *admincode) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; strncpy(hdrlist[handle]->plus_admincode, admincode, 80); hdrlist[handle]->plus_admincode[80] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->plus_admincode); return 0; } EDFLIB_API int edf_set_technician(int handle, const char *technician) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; strncpy(hdrlist[handle]->plus_technician, technician, 80); hdrlist[handle]->plus_technician[80] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->plus_technician); return 0; } EDFLIB_API int edf_set_equipment(int handle, const char *equipment) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; strncpy(hdrlist[handle]->plus_equipment, equipment, 80); hdrlist[handle]->plus_equipment[80] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->plus_equipment); return 0; } EDFLIB_API int edf_set_recording_additional(int handle, const char *recording_additional) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; strncpy(hdrlist[handle]->plus_recording_additional, recording_additional, 80); hdrlist[handle]->plus_recording_additional[80] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->plus_recording_additional); return 0; } EDFLIB_API int edf_set_startdatetime(int handle, int startdate_year, int startdate_month, int startdate_day, int starttime_hour, int starttime_minute, int starttime_second) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((startdate_year<1985) || (startdate_year>2084) || (startdate_month<1) || (startdate_month>12) || (startdate_day<1) || (startdate_day>31) || (starttime_hour<0) || (starttime_hour>23) || (starttime_minute<0) || (starttime_minute>59) || (starttime_second<0) || (starttime_second>59)) { return -1; } hdrlist[handle]->startdate_year = startdate_year; hdrlist[handle]->startdate_month = startdate_month; hdrlist[handle]->startdate_day = startdate_day; hdrlist[handle]->starttime_hour = starttime_hour; hdrlist[handle]->starttime_minute = starttime_minute; hdrlist[handle]->starttime_second = starttime_second; return 0; } EDFLIB_API int edfwrite_annotation_utf8(int handle, long long onset, long long duration, const char *description) { if(duration > 0LL) duration *= 100LL; return edfwrite_annotation_utf8_hr(handle, onset * 100LL, duration, description); } EDFLIB_API int edfwrite_annotation_utf8_hr(int handle, long long onset, long long duration, const char *description) { int i; edf_write_annotationblock_t *list_annot, *malloc_list; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(onset<0LL) return -1; if(hdrlist[handle]->annots_in_file >= hdrlist[handle]->annotlist_sz) { malloc_list = (edf_write_annotationblock_t *)realloc(write_annotationslist[handle], sizeof(edf_write_annotationblock_t) * (hdrlist[handle]->annotlist_sz + EDFLIB_ANNOT_MEMBLOCKSZ)); if(malloc_list==NULL) { return -1; } write_annotationslist[handle] = malloc_list; hdrlist[handle]->annotlist_sz += EDFLIB_ANNOT_MEMBLOCKSZ; } list_annot = write_annotationslist[handle] + hdrlist[handle]->annots_in_file; list_annot->onset = onset; list_annot->duration = duration; strncpy(list_annot->annotation, description, EDFLIB_WRITE_MAX_ANNOTATION_LEN); list_annot->annotation[EDFLIB_WRITE_MAX_ANNOTATION_LEN] = 0; for(i=0; ; i++) { if(list_annot->annotation[i] == 0) { break; } if(((unsigned char *)(list_annot->annotation))[i] < 32) { list_annot->annotation[i] = '.'; } } hdrlist[handle]->annots_in_file++; return 0; } EDFLIB_API int edfwrite_annotation_latin1(int handle, long long onset, long long duration, const char *description) { if(duration > 0LL) duration *= 100LL; return edfwrite_annotation_latin1_hr(handle, onset * 100LL, duration, description); } EDFLIB_API int edfwrite_annotation_latin1_hr(int handle, long long onset, long long duration, const char *description) { edf_write_annotationblock_t *list_annot, *malloc_list; char str[EDFLIB_WRITE_MAX_ANNOTATION_LEN + 1]; if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(onset<0LL) return -1; if(hdrlist[handle]->annots_in_file >= hdrlist[handle]->annotlist_sz) { malloc_list = (edf_write_annotationblock_t *)realloc(write_annotationslist[handle], sizeof(edf_write_annotationblock_t) * (hdrlist[handle]->annotlist_sz + EDFLIB_ANNOT_MEMBLOCKSZ)); if(malloc_list==NULL) { return -1; } write_annotationslist[handle] = malloc_list; hdrlist[handle]->annotlist_sz += EDFLIB_ANNOT_MEMBLOCKSZ; } list_annot = write_annotationslist[handle] + hdrlist[handle]->annots_in_file; list_annot->onset = onset; list_annot->duration = duration; strncpy(str, description, EDFLIB_WRITE_MAX_ANNOTATION_LEN); str[EDFLIB_WRITE_MAX_ANNOTATION_LEN] = 0; edflib_latin12utf8(str, strlen(str)); strncpy(list_annot->annotation, str, EDFLIB_WRITE_MAX_ANNOTATION_LEN); list_annot->annotation[EDFLIB_WRITE_MAX_ANNOTATION_LEN] = 0; hdrlist[handle]->annots_in_file++; return 0; } EDFLIB_API int edf_set_annot_chan_idx_pos(int handle, int pos) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((pos < 0) || (pos > 2)) return -1; hdrlist[handle]->annot_chan_idx_pos = pos; return 0; } static void edflib_remove_padding_trailing_spaces(char *str) { int i; while(str[0]==' ') { for(i=0; ; i++) { if(str[i]==0) { break; } str[i] = str[i+1]; } } for(i = strlen(str); i>0; i--) { if(str[i-1]==' ') { str[i-1] = 0; } else { break; } } } EDFLIB_API int edf_set_prefilter(int handle, int edfsignal, const char *prefilter) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((edfsignal<0) || (edfsignal>=hdrlist[handle]->edfsignals)) return -1; if(hdrlist[handle]->datarecords) return -1; strncpy(hdrlist[handle]->edfparam[edfsignal].prefilter, prefilter, 80); hdrlist[handle]->edfparam[edfsignal].prefilter[80] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->edfparam[edfsignal].prefilter); return 0; } EDFLIB_API int edf_set_transducer(int handle, int edfsignal, const char *transducer) { if((handle<0)||(handle>=EDFLIB_MAXFILES)) return -1; if(hdrlist[handle]==NULL) return -1; if(!hdrlist[handle]->writemode) return -1; if(hdrlist[handle]->datarecords) return -1; if((edfsignal<0) || (edfsignal>=hdrlist[handle]->edfsignals)) return -1; if(hdrlist[handle]->datarecords) return -1; strncpy(hdrlist[handle]->edfparam[edfsignal].transducer, transducer, 80); hdrlist[handle]->edfparam[edfsignal].transducer[80] = 0; edflib_remove_padding_trailing_spaces(hdrlist[handle]->edfparam[edfsignal].transducer); return 0; } /* minimum is the minimum digits that will be printed (minus sign not included), leading zero's will be added if necessary */ /* if sign is zero, only negative numbers will have the sign '-' character */ /* if sign is one, the sign '+' or '-' character will always be printed */ /* returns the amount of characters printed */ static int edflib_fprint_int_number_nonlocalized(FILE *file, int q, int minimum, int sign) { int flag=0, z, i, j=0, base = 1000000000; if(minimum < 0) { minimum = 0; } if(minimum > 9) { flag = 1; } if(q < 0) { fputc('-', file); j++; base = -base; } else { if(sign) { fputc('+', file); j++; } } for(i=10; i; i--) { if(minimum == i) { flag = 1; } z = q / base; q %= base; if(z || flag) { fputc('0' + z, file); j++; flag = 1; } base /= 10; } if(!flag) { fputc('0', file); j++; } return j; } /* minimum is the minimum digits that will be printed (minus sign not included), leading zero's will be added if necessary */ /* if sign is zero, only negative numbers will have the sign '-' character */ /* if sign is one, the sign '+' or '-' character will always be printed */ /* returns the amount of characters printed */ static int edflib_fprint_ll_number_nonlocalized(FILE *file, long long q, int minimum, int sign) { int flag=0, z, i, j=0; long long base = 1000000000000000000LL; if(minimum < 0) { minimum = 0; } if(minimum > 18) { flag = 1; } if(q < 0LL) { fputc('-', file); j++; base = -base; } else { if(sign) { fputc('+', file); j++; } } for(i=19; i; i--) { if(minimum == i) { flag = 1; } z = q / base; q %= base; if(z || flag) { fputc('0' + z, file); j++; flag = 1; } base /= 10LL; } if(!flag) { fputc('0', file); j++; } return j; } /* minimum is the minimum digits that will be printed (minus sign not included), leading zero's will be added if necessary */ /* if sign is zero, only negative numbers will have the sign '-' character */ /* if sign is one, the sign '+' or '-' character will always be printed */ /* returns the amount of characters printed */ /* static int edflib_sprint_int_number_nonlocalized(char *str, int q, int minimum, int sign) { int flag=0, z, i, j=0, base = 1000000000; if(minimum < 0) { minimum = 0; } if(minimum > 9) { flag = 1; } if(q < 0) { str[j++] = '-'; q = -q; } else { if(sign) { str[j++] = '+'; } } for(i=10; i; i--) { if(minimum == i) { flag = 1; } z = q / base; q %= base; if(z || flag) { str[j++] = '0' + z; flag = 1; } base /= 10; } if(!flag) { str[j++] = '0'; } str[j] = 0; return j; } */ /* minimum is the minimum digits that will be printed (minus sign not included), leading zero's will be added if necessary */ /* if sign is zero, only negative numbers will have the sign '-' character */ /* if sign is one, the sign '+' or '-' character will always be printed */ /* returns the amount of characters printed */ static int edflib_snprint_ll_number_nonlocalized(char *dest, long long q, int minimum, int sign, int sz) { int flag=0, z, i, j=0; long long base = 1000000000000000000LL; if(sz < 1) { return 0; } if(minimum < 0) { minimum = 0; } if(minimum > 18) { flag = 1; } if(q < 0LL) { dest[j++] = '-'; base = -base; } else { if(sign) { dest[j++] = '+'; } } if(j == sz) { dest[--j] = 0; return j; } for(i=19; i; i--) { if(minimum == i) { flag = 1; } z = q / base; q %= base; if(z || flag) { dest[j++] = '0' + z; if(j == sz) { dest[--j] = 0; return j; } flag = 1; } base /= 10LL; } if(!flag) { dest[j++] = '0'; } if(j == sz) { dest[--j] = 0; return j; } dest[j] = 0; return j; } static int edflib_snprint_number_nonlocalized(char *dest, double val, int sz) { int flag=0, z, i, j=0, q, base = 1000000000; double var; if(sz < 1) return 0; q = (int)val; var = val - q; if(val < 0.0) { dest[j++] = '-'; if(q < 0) { base = -base; } } if(j == sz) { dest[--j] = 0; return j; } for(i=10; i; i--) { z = q / base; q %= base; if(z || flag) { dest[j++] = '0' + z; if(j == sz) { dest[--j] = 0; return j; } flag = 1; } base /= 10; } if(!flag) { dest[j++] = '0'; } if(j == sz) { dest[--j] = 0; return j; } base = 100000000; var *= (base * 10); q = (int)var; if(q < 0) { base = -base; } if(!q) { dest[j] = 0; return j; } dest[j++] = '.'; if(j == sz) { dest[--j] = 0; return j; } for(i=9; i; i--) { z = q / base; q %= base; dest[j++] = '0' + z; if(j == sz) { dest[--j] = 0; return j; } base /= 10; } dest[j] = 0; j--; for(; j>0; j--) { if(dest[j] == '0') { dest[j] = 0; } else { j++; break; } } return j; } static double edflib_atof_nonlocalized(const char *str) { int i=0, j, dot_pos=-1, decimals=0, sign=1, exp_pos=-1, exp_sign=1, exp_val=0; double value, value2=0.0; value = edflib_atoi_nonlocalized(str); while(str[i] == ' ') { i++; } if((str[i] == '+') || (str[i] == '-')) { if(str[i] == '-') { sign = -1; } i++; } for(; ; i++) { if(str[i] == 0) { break; } if((str[i] == 'e') || (str[i] == 'E')) { exp_pos = i; break; } if(((str[i] < '0') || (str[i] > '9')) && (str[i] != '.')) { break; } if(dot_pos >= 0) { if((str[i] >= '0') && (str[i] <= '9')) { decimals++; } else { break; } } if(str[i] == '.') { if(dot_pos < 0) { dot_pos = i; } } } if(decimals) { value2 = edflib_atoi_nonlocalized(str + dot_pos + 1) * sign; i = 1; while(decimals--) { i *= 10; } value2 /= i; value += value2; } if(exp_pos > 0) { i = exp_pos + 1; if(str[i]) { if(str[i] == '+') { i++; } else if(str[i] == '-') { exp_sign = -1; i++; } if(str[i]) { exp_val = edflib_atoi_nonlocalized(str + i); if(exp_val > 0) { for(j=0; j 0) { value *= 10; } else { value /= 10; } } } } } } return value; } static int edflib_atoi_nonlocalized(const char *str) { int i=0, value=0, sign=1; while(str[i] == ' ') { i++; } if((str[i] == '+') || (str[i] == '-')) { if(str[i] == '-') { sign = -1; } i++; } for( ; ; i++) { if(str[i] == 0) { break; } if((str[i] < '0') || (str[i] > '9')) { break; } value *= 10; value += ((str[i] - '0') * sign); } return value; } static int edflib_write_tal(edfhdrblock_t *hdr, FILE *file) { int p; char str[EDFLIB_ANNOTATION_BYTES * (EDFLIB_MAX_ANNOTATION_CHANNELS + 1)]; p = edflib_snprint_ll_number_nonlocalized(str, (hdr->datarecords * hdr->long_data_record_duration + hdr->starttime_offset) / EDFLIB_TIME_DIMENSION, 0, 1, EDFLIB_ANNOTATION_BYTES * (EDFLIB_MAX_ANNOTATION_CHANNELS + 1)); if((hdr->long_data_record_duration % EDFLIB_TIME_DIMENSION) || (hdr->starttime_offset)) { str[p++] = '.'; p += edflib_snprint_ll_number_nonlocalized(str + p, (hdr->datarecords * hdr->long_data_record_duration + hdr->starttime_offset) % EDFLIB_TIME_DIMENSION, 7, 0, (EDFLIB_ANNOTATION_BYTES * (EDFLIB_MAX_ANNOTATION_CHANNELS + 1)) - p); } str[p++] = 20; str[p++] = 20; for(; ptotal_annot_bytes; p++) { str[p] = 0; } if(fwrite(str, hdr->total_annot_bytes, 1, file) != 1) { return -1; } return 0; } static int edflib_strlcpy(char *dst, const char *src, int sz) { int srclen; sz--; srclen = strlen(src); if(srclen > sz) srclen = sz; memcpy(dst, src, srclen); dst[srclen] = 0; return srclen; } static int edflib_strlcat(char *dst, const char *src, int sz) { int srclen, dstlen; dstlen = strlen(dst); sz -= dstlen + 1; if(!sz) return dstlen; srclen = strlen(src); if(srclen > sz) srclen = sz; memcpy(dst + dstlen, src, srclen); dst[dstlen + srclen] = 0; return (dstlen + srclen); }