EDFlib for C/C++
EDFlib is a programming library for C/C++ to read/write EDF+/BDF+ files. (It also reads old-type EDF/BDF files.)
EDF means European Data Format. BDF is the 24-bits version of EDF.
For a Java version, look here. For a Python version, look here.
- supports reading and writing of 16-bit (EDF) and 24-bit (BDF) resolution
- supports annotations (events)
- strives for strict standard compliance, generated files adheres to the standard
- API supports wide range of sample formats
- supports multiple annotation channels
- supports wide range of samplerate combinations
The library consists of only two files: edflib.h and edflib.c
and has been tested using the GCC compiler on Linux and MinGW on windows.
MS visual C compiler has not been tested but should work.
Documentation
Documentation
Download
This is free software, it is experimental and available under a very permissive BSD style license.
This means that you can use this library in (commercial) closed-source software.
Despite this software is intend to be usefull, there is no warranty, use this software at your own risk.
The sourcecode
- version 1.26 June 8, 2024
- When opening a file, in case a signal label is named "EDF Annotations " and the file is not EDF+, change the label to "EDF_Annotations " (in memory)
in order to avoid a possible conflict when exporting the file to EDF+.
- EDF validator: reserved field of signals: allow text with BDF files only (not BDF+).
- version 1.25 January 2, 2024
- Added function edf_set_annot_chan_idx_pos().
- EDF validator: check also reserved fields.
- EDF validator: check for non-printable characters in annotations.
- version 1.24 April 22, 2023
- Increased resolution for writing annotations (from 100uSec. to 1uSec.).
- Made it easier to compile a shared library.
- version 1.23 October 15, 2022
- Added checks for endiannes and storage types.
- version 1.22 July 16, 2022
- Added numerical value for annotation duration to the header struct for convenience.
- Added numerical values for subject birthdate to the header struct for convenience.
- Added macro for Android builds.
- version 1.21 February 4, 2022
- Fixed a bug where EDFplus patient and recording subfields where not read correctly if they started with an 'X'.
- version 1.20 October 31, 2021
- Added a new optional function edfopen_file_writeonly_with_params() for convenience.
- When reading samples that are outside the minimum - maximum range as specified in the EDF header,
clip the affected samples using the limits as specified in the EDF header.
- version 1.19 January 1, 2021
- version 1.18 June 24, 2020
- Make sure the subsecond starttime is read also when a file is opened with parameter "EDFLIB_DO_NOT_READ_ANNOTATIONS".
- version 1.17 May 14, 2020
- Fixed a bug that caused the header structure field "starttime_subsecond" to be always zero.
- Added the function edf_set_subsecond_starttime().
- version 1.16 September 02, 2019
- Increase range of edf_set_micro_datarecord_duration().
- Replace strcpy() functions with strlcpy()
- version 1.15 November 17, 2018
- Added support for scientific notation of numbers.
- version 1.14 July 14, 2018
- Fixed a bug that could cause not to write the obligatory space between the subfields of patient name and patient additional.
- version 1.13 January 2, 2018
- Fix some possible memory leaks.
- Fix some possible out of bounds memory access.
- Added unit testing.
- Check for violating maximum datarecord size.
- Improved write speed.
- Added a function that can set the datarecord duration into the micro-seconds range.
- version 1.12 July 30, 2017
- Fix erroneous reading of the datarecord duration field in the header when the number has a sign.
- Avoid unnecessary rounding errors.
- version 1.11 February 28, 2015
- Improved memory management for annotations.
- version 1.10 December 26, 2013
- Added the following functions: edflib_is_file_used(), edflib_get_number_of_open_files(), edflib_get_handle(),
edfwrite_digital_short_samples(), edf_blockwrite_digital_short_samples(), edf_blockwrite_digital_3byte_samples().
- version 1.09 June 17, 2011
- Solved a bug that prevented to open large files (>2.1GB) on a Mac.
- version 1.08 December 13, 2010
- Solved a bug that could cause writing wrong values into the physical maximum/minimum fields.
Thanks to Shahin Akhter for reporting this issue.
- After writing a datarecord, the stream will be flushed.
- version 1.07 September 28, 2010
- Solved a bug that made it impossible to open a file after opening and closing the same file before.
- version 1.06 September 27, 2010
- Bugfix / improvement release.
- version 1.05 September 17, 2010
- Bugfix release.
- Added an extra function "edf_set_datarecord_duration()".
- version 1.04 Juli 13, 2010
- Fixed a bug that caused errors depending to localization (countrysettings)
which caused dots and comma's mangled when used in countries where a comma is used
as a decimal separator. Thanks to Holger Nahrstaedt for reporting this issue.
- Changed the macro's SEEK_SET, SEEK_CUR and SEEK_END to EDFSEEK_SET, EDFSEEK_CUR and EDFSEEK_END.
- Made it possible to write a file with zero signals/channels which contains only annotations.
- version 1.03 Januari 7, 2010
- Fixed compile errors on MAC OSX.
- Fixed some possible memoryleaks.
Thanks to David Brooks, New Zealand, for reporting these issues.
Feedback to: teuniz@protonmail.com
EDF for Labview
A collection of VI's to write files in the European Data Format for Labview.
Other EDF software
Notes:
In EDF, the sensitivity (e.g. uV/bit) and offset are stored using four parameters:
digital maximum and minimum, and physical maximum and minimum.
Here, digital means the raw data coming from a sensor or ADC. Physical means the units like uV.
The sensitivity in units/bit is calculated as follows:
units per bit = (physical max - physical min) / (digital max - digital min)
The digital offset is calculated as follows:
offset = (physical max / units per bit) - digital max
For a better explanation about the relation between digital data and physical data, read the document Coding Schemes Used with Data Converters.
note: An EDF file usually contains multiple so-called datarecords. One datarecord usually has a duration of one second (this is the default but it is not mandatory!).
In that case a file with a duration of five minutes contains 300 datarecords. The duration of a datarecord can be freely choosen but, if possible, use values from
0.1 to 1 second for easier handling. Just make sure that the total size of one datarecord, expressed in bytes, does not exceed 10MByte (15MBytes for BDF(+)).
The recommendation of a maximum datarecordsize of 61440 bytes in the EDF and EDF+ specification was usefull in the time people were still using DOS as their main operating system.
Using DOS and fast (near) pointers (16-bit pointers), the maximum allocatable block of memory was 64KByte.
This is not a concern anymore so the maximum datarecord size now is limited to 10MByte for EDF(+) and 15MByte for BDF(+).
This helps to accommodate for higher samplingrates used by modern, fast Analog to Digital Converters.
EDF header character encoding: The EDF specification says that only ASCII characters are allowed.
EDFlib will automatically convert characters with accents, umlauts, tilde, etc. to their "normal" equivalent without the accent/umlaut/tilde/etc.
The description/name of an EDF+ annotation on the other hand, is encoded in UTF-8.
Documententation
Documentation
How do I calculate the samplefrequency of a signal when I read a file?
samplefrequency = ((double)hdr.signalparam[edfsignal].smp_in_datarecord / (double)hdr.datarecord_duration) * EDFLIB_TIME_DIMENSION;
How do I jump to n seconds from the start of the file when I read a file?
If n is an integer (has no fraction):
long long offset;
if(!(hdr.datarecord_duration % EDFLIB_TIME_DIMENSION))
{
offset = (n / (hdr.datarecord_duration / EDFLIB_TIME_DIMENSION)) * hdr.signalparam[edfsignal].smp_in_datarecord;
}
else
{
offset = (long long)(((double)n / ((double)hdr.datarecord_duration / (double)EDFLIB_TIME_DIMENSION)) * (double)hdr.signalparam[edfsignal].smp_in_datarecord);
}
edfseek(handle, edfsignal, offset, EDFSEEK_SET)));
If n has a fraction:
long long offset;
offset = (long long)(((double)n / ((double)hdr.datarecord_duration / (double)EDFLIB_TIME_DIMENSION)) * (double)hdr.signalparam[edfsignal].smp_in_datarecord);
edfseek(handle, edfsignal, offset, EDFSEEK_SET)));
More info