From 963d53c60b5074d842b317b8e15ce9b61835ae3f Mon Sep 17 00:00:00 2001 From: Melissa Key <24309569+melissakey@users.noreply.github.com> Date: Fri, 8 Nov 2024 14:59:55 -0500 Subject: [PATCH 1/7] timeseries that can handle strings --- xdf.cpp | 342 +++++++++++++++----------------------------------------- xdf.h | 22 +++- 2 files changed, 110 insertions(+), 254 deletions(-) diff --git a/xdf.cpp b/xdf.cpp index 1891640..2ac48f4 100644 --- a/xdf.cpp +++ b/xdf.cpp @@ -20,7 +20,7 @@ #include #include -#include //pugi XML parser +#include "pugixml.hpp" //pugi XML parser #include #include #include "smarc/smarc.h" //resampling library @@ -28,6 +28,7 @@ #include //std::accumulate #include // bind2nd #include +#include Xdf::Xdf() { @@ -176,250 +177,41 @@ int Xdf::load_xdf(std::string filename) //read [NumSampleBytes], [NumSamples] uint64_t numSamp = readLength(file); - - //check the data type - if (streams[index].info.channel_format.compare("float32") == 0) - { - //if the time series is empty - if (streams[index].time_series.empty()) - streams[index].time_series.resize(streams[index].info.channel_count); - - //for each sample - for (size_t i = 0; i < numSamp; i++) - { - //read or deduce time stamp - auto tsBytes = readBin(file); - - double ts; //temporary time stamp - - if (tsBytes == 8) - { - Xdf::readBin(file, &ts); - streams[index].time_stamps.emplace_back(ts); - } - else - { - ts = streams[index].last_timestamp + streams[index].sampling_interval; - streams[index].time_stamps.emplace_back(ts); - } - - streams[index].last_timestamp = ts; - - //read the data - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - float data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } - } - else if (streams[index].info.channel_format.compare("double64") == 0) - { - //if the time series is empty - if (streams[index].time_series.empty()) - streams[index].time_series.resize(streams[index].info.channel_count); - - //for each sample - for (size_t i = 0; i < numSamp; i++) - { - //read or deduce time stamp - auto tsBytes = readBin(file); - - double ts; //temporary time stamp - - if (tsBytes == 8) - { - Xdf::readBin(file, &ts); - streams[index].time_stamps.emplace_back(ts); - } - else - { - ts = streams[index].last_timestamp + streams[index].sampling_interval; - streams[index].time_stamps.emplace_back(ts); - } - - streams[index].last_timestamp = ts; - - //read the data - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - double data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } - } - else if (streams[index].info.channel_format.compare("int8") == 0) - { - //if the time series is empty - if (streams[index].time_series.empty()) - streams[index].time_series.resize(streams[index].info.channel_count); - - //for each sample - for (size_t i = 0; i < numSamp; i++) - { - //read or deduce time stamp - auto tsBytes = readBin(file); - - double ts; //temporary time stamp - - if (tsBytes == 8) - { - Xdf::readBin(file, &ts); - streams[index].time_stamps.emplace_back(ts); - } - else - { - ts = streams[index].last_timestamp + streams[index].sampling_interval; - streams[index].time_stamps.emplace_back(ts); - } - - streams[index].last_timestamp = ts; - - //read the data - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - int8_t data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } - } - else if (streams[index].info.channel_format.compare("int16") == 0) - { - //if the time series is empty - if (streams[index].time_series.empty()) - streams[index].time_series.resize(streams[index].info.channel_count); - - //for each sample - for (size_t i = 0; i < numSamp; i++) - { - //read or deduce time stamp - auto tsBytes = readBin(file); - - double ts; //temporary time stamp - - if (tsBytes == 8) - { - Xdf::readBin(file, &ts); - streams[index].time_stamps.emplace_back(ts); - } - else - { - ts = streams[index].last_timestamp + streams[index].sampling_interval; - streams[index].time_stamps.emplace_back(ts); - } - - streams[index].last_timestamp = ts; - - //read the data - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - int16_t data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } - } - else if (streams[index].info.channel_format.compare("int32") == 0) - { - //if the time series is empty - if (streams[index].time_series.empty()) - streams[index].time_series.resize(streams[index].info.channel_count); - - //for each sample - for (size_t i = 0; i < numSamp; i++) - { - //read or deduce time stamp - auto tsBytes = readBin(file); - - double ts; //temporary time stamp - - if (tsBytes == 8) - { - Xdf::readBin(file, &ts); - streams[index].time_stamps.emplace_back(ts); - } - else - { - ts = streams[index].last_timestamp + streams[index].sampling_interval; - streams[index].time_stamps.emplace_back(ts); - } - - streams[index].last_timestamp = ts; - - //read the data - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - int32_t data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } - } - else if (streams[index].info.channel_format.compare("int64") == 0) - { - //if the time series is empty - if (streams[index].time_series.empty()) - streams[index].time_series.resize(streams[index].info.channel_count); - - //for each sample - for (size_t i = 0; i < numSamp; i++) - { - //read or deduce time stamp - auto tsBytes = readBin(file); - - double ts; //temporary time stamp - - if (tsBytes == 8) - { - Xdf::readBin(file, &ts); - streams[index].time_stamps.emplace_back(ts); - } - else - { - ts = streams[index].last_timestamp + streams[index].sampling_interval; - streams[index].time_stamps.emplace_back(ts); - } - - streams[index].last_timestamp = ts; - - //read the data - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - int64_t data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } - } - else if (streams[index].info.channel_format.compare("string") == 0) - { - //for each event - for (size_t i = 0; i < numSamp; i++) - { - //read or deduce time stamp - auto tsBytes = readBin(file); - - double ts; //temporary time stamp - - if (tsBytes == 8) - Xdf::readBin(file, &ts); - else - ts = streams[index].last_timestamp + streams[index].sampling_interval; - - //read the event - auto length = Xdf::readLength(file); - + + //if the time series is empty + if (streams[index].time_series.empty()) + streams[index].time_series.resize(streams[index].info.channel_count); + + auto tsBytes = readBin(file); + double lastTimestamp = streams[index].stream.last_timestamp; + + for (size_t i = 0; i < numSamp; i++) { + double ts = readTimestamp(file, lastTimestamp, streams[index].stream.sampling_interval); + streams[index].time_stamps.emplace_back(ts); + + // Based on the channel format, read and store the data + if(streams[index].info.channel_format.compare("float32")) { + readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); + } else if(streams[index].info.channel_format.compare("double64")) { + readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); + } else if(streams[index].info.channel_format.compare("int8")) { + readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); + } else if(streams[index].info.channel_format.compare("int16")) { + readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); + } else if(streams[index].info.channel_format.compare("int32")) { + readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); + } else if(streams[index].info.channel_format.compare("int64")) { + readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); + } else if(streams[index].info.channel_format.compare("string")) { + auto length = Xdf::readLength(file); char* buffer = new char[length + 1]; file.read(buffer, length); buffer[length] = '\0'; - eventMap.emplace_back(std::make_pair(buffer, ts), index); + + streams[index].time_series[v].emplace_back(buffer); delete[] buffer; - streams[index].last_timestamp = ts; - } - } + } + } } break; case 4: //read [ClockOffset] chunk @@ -659,24 +451,44 @@ void Xdf::resample(int userSrate) double* inbuf = new double[row.size()]; double* outbuf = new double[OUT_BUF_SIZE]; - - std::copy(row.begin(), row.end(), inbuf); - - read = row.size(); - + // Fill inbuf with the numeric values from the row + for(auto& val : row) { + std::visit([&inbuf, &read](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_arithmetic_v) { + inbuf[read++] = static_cast(arg); // Convert to double + } + }, val); + } // resample signal block written = smarc_resample(pfilt, pstate, inbuf, read, outbuf, OUT_BUF_SIZE); - // do what you want with your output - row.resize(written); - std::copy ( outbuf, outbuf+written, row.begin() ); + // Replace original values with the resampled output + read = 0; + for (auto& val : row) { + // Only replace numeric values + std::visit([&outbuf, &read](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_arithmetic_v) { + arg = static_cast(outbuf[read++]); + } + }, val); + } // flushing last values written = smarc_resample_flush(pfilt, pstate, outbuf, OUT_BUF_SIZE); - // do what you want with your output - row.resize(row.size() + written); - std::copy ( outbuf, outbuf+written, row.begin() + row.size() - written ); + // Add any remaining flushed values + read = 0; + for (auto& val : row) { + // Only replace numeric values + std::visit([&outbuf, &read](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_arithmetic_v) { + arg = static_cast(outbuf[read++]); + } + }, val); + } // you are done with converting your signal. // If you want to reuse the same converter to process another signal @@ -869,6 +681,7 @@ void Xdf::loadSampleRateMap() sampleRateMap.emplace(stream.info.nominal_srate); } +/* void Xdf::detrend() { for (auto &stream : streams) @@ -882,6 +695,7 @@ void Xdf::detrend() } } } +*/ void Xdf::calcEffectiveSrate() { @@ -1095,3 +909,27 @@ template T Xdf::readBin(std::istream& is, T* obj) { is.read(reinterpret_cast(obj), sizeof(T)); return *obj; } + +// Generic function to read data of any type +template +void readData(std::ifstream &file, std::vector> &time_series, int channel_count) { + for (int v = 0; v < channel_count; ++v) { + T data; + Xdf::readBin(file, &data); + time_series[v].emplace_back(data); + } +} + +double readTimestamp(std::ifstream &file, double &lastTimestamp, double samplingInterval) { + auto tsBytes = readBin(file); + double ts; + + if (tsBytes == 8) { + Xdf::readBin(file, &ts); + } else { + ts = lastTimestamp + samplingInterval; + } + + lastTimestamp = ts; + return ts; +} \ No newline at end of file diff --git a/xdf.h b/xdf.h index 47e73a1..ddec88c 100644 --- a/xdf.h +++ b/xdf.h @@ -27,6 +27,7 @@ #include #include #include +#include /*! \class Xdf * @@ -54,7 +55,7 @@ class Xdf struct Stream { //! A 2D vector which stores the time series of a stream. Each row represents a channel. - std::vector > time_series; + std::vector>> time_series; std::vector time_stamps; /*!< A vector to store time stamps. */ std::string streamHeader; /*!< Raw XML of stream header chunk. */ std::string streamFooter; /*!< Raw XML of stream footer chunk. */ @@ -174,7 +175,7 @@ class Xdf * * \sa offsets */ - void detrend(); + //void detrend(); /*! * \brief Delete the time stamps vectors when no longer needed to @@ -312,6 +313,23 @@ class Xdf * \return the read data */ template T readBin(std::istream& is, T* obj = nullptr); + + /* + * \brief Template function to read data of any type and store it in the time_series container. + * \param file The input file stream to read the data from. + * \param time_series A reference to a 2D vector storing the time series data for each channel. + * \param channel_count The number of channels to read data for. + */ + template void readData(std::ifstream &file, std::vector> &time_series, int channel_count); + + /*! + * \brief Utility function to handle time stamp reading or deduction. + * \param file The input file stream from which the time stamp is read. + * \param lastTimestamp The last timestamp value to calculate the new timestamp if needed. + * \param samplingInterval The interval between samples, used for calculating the timestamp when necessary. + * \return The calculated or read timestamp. + */ + double readTimestamp(std::ifstream &file, double &lastTimestamp, double samplingInterval) }; #endif // XDF_H From 49d379cdca7698509f689d246cd91c11159835f4 Mon Sep 17 00:00:00 2001 From: Melissa Key <24309569+melissakey@users.noreply.github.com> Date: Fri, 8 Nov 2024 16:22:39 -0500 Subject: [PATCH 2/7] <# bug fixes in variant adapatation #> --- xdf.cpp | 133 ++++++++++++++++++++++++++++++++++---------------------- xdf.h | 18 +------- 2 files changed, 81 insertions(+), 70 deletions(-) diff --git a/xdf.cpp b/xdf.cpp index 2ac48f4..c25cac6 100644 --- a/xdf.cpp +++ b/xdf.cpp @@ -178,41 +178,91 @@ int Xdf::load_xdf(std::string filename) //read [NumSampleBytes], [NumSamples] uint64_t numSamp = readLength(file); - //if the time series is empty - if (streams[index].time_series.empty()) - streams[index].time_series.resize(streams[index].info.channel_count); - - auto tsBytes = readBin(file); - double lastTimestamp = streams[index].stream.last_timestamp; - - for (size_t i = 0; i < numSamp; i++) { - double ts = readTimestamp(file, lastTimestamp, streams[index].stream.sampling_interval); - streams[index].time_stamps.emplace_back(ts); - - // Based on the channel format, read and store the data - if(streams[index].info.channel_format.compare("float32")) { - readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); - } else if(streams[index].info.channel_format.compare("double64")) { - readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); - } else if(streams[index].info.channel_format.compare("int8")) { - readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); - } else if(streams[index].info.channel_format.compare("int16")) { - readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); - } else if(streams[index].info.channel_format.compare("int32")) { - readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); - } else if(streams[index].info.channel_format.compare("int64")) { - readData(file, streams[index].stream.time_series, streams[index].stream.info.channel_count); - } else if(streams[index].info.channel_format.compare("string")) { - auto length = Xdf::readLength(file); + //if the time series is empty + if (streams[index].time_series.empty()) + streams[index].time_series.resize(streams[index].info.channel_count); + + //for each sample + for (size_t i = 0; i < numSamp; i++) + { + //read or deduce time stamp + auto tsBytes = readBin(file); + + double ts; //temporary time stamp + + if (tsBytes == 8) + { + Xdf::readBin(file, &ts); + streams[index].time_stamps.emplace_back(ts); + } + else + { + ts = streams[index].last_timestamp + streams[index].sampling_interval; + streams[index].time_stamps.emplace_back(ts); + } + + streams[index].last_timestamp = ts; + + if(streams[index].info.channel_format.compare("string") == 0) { + + std::vector> data; + + for(int v = 0; v < streams[index].info.channel_count; ++v) { + auto length = Xdf::readLength(file); char* buffer = new char[length + 1]; file.read(buffer, length); buffer[length] = '\0'; streams[index].time_series[v].emplace_back(buffer); - delete[] buffer; } - } - } + } else { + //read the data + if(streams[index].info.channel_format.compare("float32") == 0) { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + float data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } else if(streams[index].info.channel_format.compare("double64") == 0) { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + double data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } else if(streams[index].info.channel_format.compare("int8_t") == 0) { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + int8_t data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } else if(streams[index].info.channel_format.compare("int16_t") == 0) { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + int16_t data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } else if(streams[index].info.channel_format.compare("int32_t") == 0) { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + int data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } else if(streams[index].info.channel_format.compare("int64_t") == 0) { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + int64_t data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } + } + } + } break; case 4: //read [ClockOffset] chunk { @@ -423,6 +473,7 @@ void Xdf::resample(int userSrate) for (auto &stream : streams) { if (!stream.time_series.empty() && + !stream.info.channel_format.compare("string") && stream.info.nominal_srate != userSrate && stream.info.nominal_srate != 0) { @@ -909,27 +960,3 @@ template T Xdf::readBin(std::istream& is, T* obj) { is.read(reinterpret_cast(obj), sizeof(T)); return *obj; } - -// Generic function to read data of any type -template -void readData(std::ifstream &file, std::vector> &time_series, int channel_count) { - for (int v = 0; v < channel_count; ++v) { - T data; - Xdf::readBin(file, &data); - time_series[v].emplace_back(data); - } -} - -double readTimestamp(std::ifstream &file, double &lastTimestamp, double samplingInterval) { - auto tsBytes = readBin(file); - double ts; - - if (tsBytes == 8) { - Xdf::readBin(file, &ts); - } else { - ts = lastTimestamp + samplingInterval; - } - - lastTimestamp = ts; - return ts; -} \ No newline at end of file diff --git a/xdf.h b/xdf.h index ddec88c..b3374e4 100644 --- a/xdf.h +++ b/xdf.h @@ -313,23 +313,7 @@ class Xdf * \return the read data */ template T readBin(std::istream& is, T* obj = nullptr); - - /* - * \brief Template function to read data of any type and store it in the time_series container. - * \param file The input file stream to read the data from. - * \param time_series A reference to a 2D vector storing the time series data for each channel. - * \param channel_count The number of channels to read data for. - */ - template void readData(std::ifstream &file, std::vector> &time_series, int channel_count); - - /*! - * \brief Utility function to handle time stamp reading or deduction. - * \param file The input file stream from which the time stamp is read. - * \param lastTimestamp The last timestamp value to calculate the new timestamp if needed. - * \param samplingInterval The interval between samples, used for calculating the timestamp when necessary. - * \return The calculated or read timestamp. - */ - double readTimestamp(std::ifstream &file, double &lastTimestamp, double samplingInterval) + }; #endif // XDF_H From 725f6e7c0e2c59447a6aad515c3624959d1b06f5 Mon Sep 17 00:00:00 2001 From: Melissa Key <24309569+melissakey@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:00:23 -0500 Subject: [PATCH 3/7] <# incorporated improvements from PR review #> --- xdf.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/xdf.cpp b/xdf.cpp index c25cac6..1e6db33 100644 --- a/xdf.cpp +++ b/xdf.cpp @@ -179,8 +179,9 @@ int Xdf::load_xdf(std::string filename) uint64_t numSamp = readLength(file); //if the time series is empty - if (streams[index].time_series.empty()) - streams[index].time_series.resize(streams[index].info.channel_count); + if (Stream stream = streams[index]; stream.time_series.empty()) { + stream.time_series.resize(stream.info.channel_count); + } //for each sample for (size_t i = 0; i < numSamp; i++) @@ -205,8 +206,6 @@ int Xdf::load_xdf(std::string filename) if(streams[index].info.channel_format.compare("string") == 0) { - std::vector> data; - for(int v = 0; v < streams[index].info.channel_count; ++v) { auto length = Xdf::readLength(file); char* buffer = new char[length + 1]; From da69f4464083edd7855e12dd3cc5a448d7b548c0 Mon Sep 17 00:00:00 2001 From: Melissa Key <24309569+melissakey@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:52:35 -0500 Subject: [PATCH 4/7] consistent indentation --- xdf.cpp | 771 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 390 insertions(+), 381 deletions(-) diff --git a/xdf.cpp b/xdf.cpp index 1e6db33..b4614dd 100644 --- a/xdf.cpp +++ b/xdf.cpp @@ -12,7 +12,7 @@ //GNU General Public License for more details. //You should have received a copy of the GNU General Public License -//along with this program. If not, see . +//along with this program. If not, see . //If you have questions, contact author at yida.lin@outlook.com @@ -38,28 +38,28 @@ int Xdf::load_xdf(std::string filename) { clock_t time; time = clock(); - - + + /* //uncompress if necessary - char ext[_MAX_EXT]; //for file extension - - _splitpath_s ( argv[1], NULL, NULL, NULL, NULL, NULL, NULL, ext, NULL ); - if (strcmp(ext, ".xdfz") == 0) - { - //uncompress - } - */ - + char ext[_MAX_EXT]; //for file extension + + _splitpath_s ( argv[1], NULL, NULL, NULL, NULL, NULL, NULL, ext, NULL ); + if (strcmp(ext, ".xdfz") == 0) + { + //uncompress + } + */ + std::vector idmap; //remaps stream id's onto indices in streams - - + + //=================================================================== //========================= parse the file ========================== //=================================================================== - - + + std::ifstream file(filename, std::ios::in | std::ios::binary); - + if (file.is_open()) { //read [MagicCode] @@ -70,296 +70,305 @@ int Xdf::load_xdf(std::string filename) if (magicNumber.size() == 4) break; } - + if (magicNumber.compare("XDF:")) { std::cout << "This is not a valid XDF file.('" << filename << "')\n"; return -1; } - + //for each chunk while (1) { uint64_t ChLen = readLength(file);//chunk length - + if (ChLen == 0) break; - + uint16_t tag; //read tag of the chunk, 6 possibilities readBin(file, &tag); - + switch (tag) { - case 1: //[FileHeader] - { - char* buffer = new char[ChLen - 2]; - file.read(buffer, ChLen - 2); - fileHeader = buffer; - - pugi::xml_document doc; - - doc.load_buffer_inplace(buffer, ChLen - 2); - - pugi::xml_node info = doc.child("info"); - - version = info.child("version").text().as_float(); - - delete[] buffer; - } - break; - case 2: //read [StreamHeader] chunk - { - //read [StreamID] - uint32_t streamID; - int index; - Xdf::readBin(file, &streamID); - std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; - if (it == idmap.end()) - { - index = idmap.size(); - idmap.emplace_back(streamID); - streams.emplace_back(); - } - else - index = std::distance(idmap.begin(), it); - - - pugi::xml_document doc; - - //read [Content] - char* buffer = new char[ChLen - 6]; - file.read(buffer, ChLen - 6); - streams[index].streamHeader = buffer; - - doc.load_buffer_inplace(buffer, ChLen - 6); - - pugi::xml_node info = doc.child("info"); - pugi::xml_node desc = info.child("desc"); - - streams[index].info.channel_count = info.child("channel_count").text().as_int(); - streams[index].info.nominal_srate = info.child("nominal_srate").text().as_double(); - streams[index].info.name = info.child("name").text().get(); - streams[index].info.type = info.child("type").text().get(); - streams[index].info.channel_format = info.child("channel_format").text().get(); - - for (auto channel = desc.child("channels").child("channel"); channel; channel = channel.next_sibling("channel")) + case 1: //[FileHeader] { - streams[index].info.channels.emplace_back(); - - for (auto const &entry : channel.children()) - streams[index].info.channels.back().emplace(entry.name(), entry.child_value()); + char* buffer = new char[ChLen - 2]; + file.read(buffer, ChLen - 2); + fileHeader = buffer; + + pugi::xml_document doc; + + doc.load_buffer_inplace(buffer, ChLen - 2); + + pugi::xml_node info = doc.child("info"); + + version = info.child("version").text().as_float(); + + delete[] buffer; } - - if (streams[index].info.nominal_srate > 0) - streams[index].sampling_interval = 1 / streams[index].info.nominal_srate; - else - streams[index].sampling_interval = 0; - - delete[] buffer; - } - break; - case 3: //read [Samples] chunk - { - //read [StreamID] - uint32_t streamID; - int index; - Xdf::readBin(file, &streamID); - std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; - if (it == idmap.end()) + break; + case 2: //read [StreamHeader] chunk { - index = idmap.size(); - idmap.emplace_back(streamID); - streams.emplace_back(); - } - else - index = std::distance(idmap.begin(), it); - - - //read [NumSampleBytes], [NumSamples] - uint64_t numSamp = readLength(file); - - //if the time series is empty - if (Stream stream = streams[index]; stream.time_series.empty()) { - stream.time_series.resize(stream.info.channel_count); - } - - //for each sample - for (size_t i = 0; i < numSamp; i++) - { - //read or deduce time stamp - auto tsBytes = readBin(file); + //read [StreamID] + uint32_t streamID; + int index; + Xdf::readBin(file, &streamID); + std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; + if (it == idmap.end()) + { + index = idmap.size(); + idmap.emplace_back(streamID); + streams.emplace_back(); + } + else + index = std::distance(idmap.begin(), it); + + + pugi::xml_document doc; + + //read [Content] + char* buffer = new char[ChLen - 6]; + file.read(buffer, ChLen - 6); + streams[index].streamHeader = buffer; - double ts; //temporary time stamp + doc.load_buffer_inplace(buffer, ChLen - 6); - if (tsBytes == 8) + pugi::xml_node info = doc.child("info"); + pugi::xml_node desc = info.child("desc"); + + streams[index].info.channel_count = info.child("channel_count").text().as_int(); + streams[index].info.nominal_srate = info.child("nominal_srate").text().as_double(); + streams[index].info.name = info.child("name").text().get(); + streams[index].info.type = info.child("type").text().get(); + streams[index].info.channel_format = info.child("channel_format").text().get(); + + for (auto channel = desc.child("channels").child("channel"); channel; channel = channel.next_sibling("channel")) { - Xdf::readBin(file, &ts); - streams[index].time_stamps.emplace_back(ts); + streams[index].info.channels.emplace_back(); + + for (auto const &entry : channel.children()) + streams[index].info.channels.back().emplace(entry.name(), entry.child_value()); } + + if (streams[index].info.nominal_srate > 0) + streams[index].sampling_interval = 1 / streams[index].info.nominal_srate; else + streams[index].sampling_interval = 0; + + delete[] buffer; + } + break; + case 3: //read [Samples] chunk + { + //read [StreamID] + uint32_t streamID; + int index; + Xdf::readBin(file, &streamID); + std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; + if (it == idmap.end()) { - ts = streams[index].last_timestamp + streams[index].sampling_interval; - streams[index].time_stamps.emplace_back(ts); + index = idmap.size(); + idmap.emplace_back(streamID); + streams.emplace_back(); } + else + index = std::distance(idmap.begin(), it); - streams[index].last_timestamp = ts; - if(streams[index].info.channel_format.compare("string") == 0) { - - for(int v = 0; v < streams[index].info.channel_count; ++v) { - auto length = Xdf::readLength(file); - char* buffer = new char[length + 1]; - file.read(buffer, length); - buffer[length] = '\0'; - - streams[index].time_series[v].emplace_back(buffer); - } - } else { - //read the data - if(streams[index].info.channel_format.compare("float32") == 0) { - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - float data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } else if(streams[index].info.channel_format.compare("double64") == 0) { - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - double data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } else if(streams[index].info.channel_format.compare("int8_t") == 0) { - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - int8_t data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } else if(streams[index].info.channel_format.compare("int16_t") == 0) { - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - int16_t data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } else if(streams[index].info.channel_format.compare("int32_t") == 0) { - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - int data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } - } else if(streams[index].info.channel_format.compare("int64_t") == 0) { - for (int v = 0; v < streams[index].info.channel_count; ++v) - { - int64_t data; - Xdf::readBin(file, &data); - streams[index].time_series[v].emplace_back(data); - } + //read [NumSampleBytes], [NumSamples] + uint64_t numSamp = readLength(file); + + //if the time series is empty + if (Stream stream = streams[index]; stream.time_series.empty()) + { + stream.time_series.resize(stream.info.channel_count); + } + + //for each sample + for (size_t i = 0; i < numSamp; i++) + { + //read or deduce time stamp + auto tsBytes = readBin(file); + + double ts; //temporary time stamp + + if (tsBytes == 8) + { + Xdf::readBin(file, &ts); + streams[index].time_stamps.emplace_back(ts); + } + else + { + ts = streams[index].last_timestamp + streams[index].sampling_interval; + streams[index].time_stamps.emplace_back(ts); + } + + streams[index].last_timestamp = ts; + + if(streams[index].info.channel_format.compare("string") == 0) + { + + for(int v = 0; v < streams[index].info.channel_count; ++v) + { + auto length = Xdf::readLength(file); + char* buffer = new char[length + 1]; + file.read(buffer, length); + buffer[length] = '\0'; + + streams[index].time_series[v].emplace_back(buffer); + } + } else { + //read the data + if(streams[index].info.channel_format.compare("float32") == 0) + { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + float data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } else if(streams[index].info.channel_format.compare("double64") == 0) + + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + double data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } else if(streams[index].info.channel_format.compare("int8_t") == 0) + { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + int8_t data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } else if(streams[index].info.channel_format.compare("int16_t") == 0) + { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + int16_t data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } else if(streams[index].info.channel_format.compare("int32_t") == 0) + { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + int data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } else if(streams[index].info.channel_format.compare("int64_t") == 0) + { + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + int64_t data; + Xdf::readBin(file, &data); + streams[index].time_series[v].emplace_back(data); + } + } } } - } - } - break; - case 4: //read [ClockOffset] chunk - { - uint32_t streamID; - int index; - Xdf::readBin(file, &streamID); - std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; - if (it == idmap.end()) + } + break; + case 4: //read [ClockOffset] chunk { - index = idmap.size(); - idmap.emplace_back(streamID); - streams.emplace_back(); + uint32_t streamID; + int index; + Xdf::readBin(file, &streamID); + std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; + if (it == idmap.end()) + { + index = idmap.size(); + idmap.emplace_back(streamID); + streams.emplace_back(); + } + else + index = std::distance(idmap.begin(), it); + + + double collectionTime; + double offsetValue; + + Xdf::readBin(file, &collectionTime); + Xdf::readBin(file, &offsetValue); + + streams[index].clock_times.emplace_back(collectionTime); + streams[index].clock_values.emplace_back(offsetValue); } - else - index = std::distance(idmap.begin(), it); - - - double collectionTime; - double offsetValue; - - Xdf::readBin(file, &collectionTime); - Xdf::readBin(file, &offsetValue); - - streams[index].clock_times.emplace_back(collectionTime); - streams[index].clock_values.emplace_back(offsetValue); - } - break; - case 6: //read [StreamFooter] chunk - { - pugi::xml_document doc; - - //read [StreamID] - uint32_t streamID; - int index; - Xdf::readBin(file, &streamID); - std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; - if (it == idmap.end()) + break; + case 6: //read [StreamFooter] chunk { - index = idmap.size(); - idmap.emplace_back(streamID); - streams.emplace_back(); + pugi::xml_document doc; + + //read [StreamID] + uint32_t streamID; + int index; + Xdf::readBin(file, &streamID); + std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; + if (it == idmap.end()) + { + index = idmap.size(); + idmap.emplace_back(streamID); + streams.emplace_back(); + } + else + index = std::distance(idmap.begin(), it); + + + char* buffer = new char[ChLen - 6]; + file.read(buffer, ChLen - 6); + streams[index].streamFooter = buffer; + + doc.load_buffer_inplace(buffer, ChLen - 6); + + pugi::xml_node info = doc.child("info"); + + streams[index].info.first_timestamp = info.child("first_timestamp").text().as_double(); + streams[index].info.last_timestamp = info.child("last_timestamp").text().as_double(); + streams[index].info.measured_srate = info.child("measured_srate").text().as_double(); + streams[index].info.sample_count = info.child("sample_count").text().as_int(); + delete[] buffer; } - else - index = std::distance(idmap.begin(), it); - - - char* buffer = new char[ChLen - 6]; - file.read(buffer, ChLen - 6); - streams[index].streamFooter = buffer; - - doc.load_buffer_inplace(buffer, ChLen - 6); - - pugi::xml_node info = doc.child("info"); - - streams[index].info.first_timestamp = info.child("first_timestamp").text().as_double(); - streams[index].info.last_timestamp = info.child("last_timestamp").text().as_double(); - streams[index].info.measured_srate = info.child("measured_srate").text().as_double(); - streams[index].info.sample_count = info.child("sample_count").text().as_int(); - delete[] buffer; - } - break; - case 5: //skip other chunk types (Boundary, ...) - file.seekg(ChLen - 2, file.cur); - break; - default: - std::cout << "Unknown chunk encountered.\n"; - break; + break; + case 5: //skip other chunk types (Boundary, ...) + file.seekg(ChLen - 2, file.cur); + break; + default: + std::cout << "Unknown chunk encountered.\n"; + break; } } - - + + //calculate how much time it takes to read the data clock_t halfWay = clock() - time; - + std::cout << "it took " << halfWay << " clicks (" << ((float)halfWay) / CLOCKS_PER_SEC << " seconds)" - << " reading XDF data" << std::endl; - - + << " reading XDF data" << std::endl; + + //========================================================== //=============find the min and max time stamps============= //========================================================== - + syncTimeStamps(); - + findMinMax(); - + findMajSR(); - + getHighestSampleRate(); - + loadSampleRateMap(); - + calcTotalChannel(); - + loadDictionary(); - + calcEffectiveSrate(); - + //loading finishes, close file file.close(); } @@ -368,7 +377,7 @@ int Xdf::load_xdf(std::string filename) std::cout << "Unable to open file" << std::endl; return 1; } - + return 0; } @@ -381,7 +390,7 @@ void Xdf::syncTimeStamps() { size_t m = 0; // index iterating through stream.time_stamps size_t n = 0; // index iterating through stream.clock_times - + while (m < stream.time_stamps.size()) { if (stream.clock_times[n] < stream.time_stamps[m]) @@ -400,14 +409,14 @@ void Xdf::syncTimeStamps() } } } - + // Sync event time stamps for (auto &elem : this->eventMap) { if (!this->streams[elem.second].clock_times.empty()) { size_t k = 0; // index iterating through streams[elem.second].clock_times - + while (k < this->streams[elem.second].clock_times.size() - 1) { if (this->streams[elem.second].clock_times[k+1] < elem.first.second) @@ -419,11 +428,11 @@ void Xdf::syncTimeStamps() break; } } - + elem.first.second += this->streams[elem.second].clock_values[k]; // apply the last offset value to the timestamp; if there hasn't yet been an offset value take the first recorded one } } - + // Update first and last time stamps in stream footer for (size_t k = 0; k < this->streams.size(); k++) { @@ -431,7 +440,7 @@ void Xdf::syncTimeStamps() { double min = NAN; double max = NAN; - + for (auto const &elem : this->eventMap) { if (elem.second == (int)k) @@ -440,14 +449,14 @@ void Xdf::syncTimeStamps() { min = elem.first.second; } - + if (std::isnan(max) || elem.first.second > max) { max = elem.first.second; } } } - + streams[k].info.first_timestamp = min; streams[k].info.last_timestamp = max; } @@ -455,8 +464,8 @@ void Xdf::syncTimeStamps() { if (streams[k].time_stamps.size() > 0) { streams[k].info.first_timestamp = streams[k].time_stamps.front(); - streams[k].info.last_timestamp = streams[k].time_stamps.back(); - } + streams[k].info.last_timestamp = streams[k].time_stamps.back(); + } } } } @@ -465,16 +474,16 @@ void Xdf::resample(int userSrate) { //if user entered a preferred sample rate, we resample all the channels to that sample rate //Otherwise, we resample all channels to the sample rate that has the most channels - + clock_t time = clock(); - + #define BUF_SIZE 8192 for (auto &stream : streams) { if (!stream.time_series.empty() && !stream.info.channel_format.compare("string") && - stream.info.nominal_srate != userSrate && - stream.info.nominal_srate != 0) + stream.info.nominal_srate != userSrate && + stream.info.nominal_srate != 0) { int fsin = stream.info.nominal_srate; // input samplerate int fsout = userSrate; // output samplerate @@ -482,16 +491,16 @@ void Xdf::resample(int userSrate) double rp = 0.1; // passband ripple factor double rs = 140; // stopband attenuation double tol = 0.000001; // tolerance - + // initialize smarc filter struct PFilter* pfilt = smarc_init_pfilter(fsin, fsout, bandwidth, rp, rs, tol, NULL, 0); if (pfilt == NULL) continue; - + // initialize smarc filter state struct PState* pstate = smarc_init_pstate(pfilt); - + for (auto &row : stream.time_series) { // initialize buffers @@ -500,77 +509,77 @@ void Xdf::resample(int userSrate) const int OUT_BUF_SIZE = (int) smarc_get_output_buffer_size(pfilt, row.size()); double* inbuf = new double[row.size()]; double* outbuf = new double[OUT_BUF_SIZE]; - + // Fill inbuf with the numeric values from the row for(auto& val : row) { std::visit([&inbuf, &read](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_arithmetic_v) { + if constexpr (std::is_arithmetic_v) { inbuf[read++] = static_cast(arg); // Convert to double } }, val); } // resample signal block written = smarc_resample(pfilt, pstate, inbuf, read, outbuf, OUT_BUF_SIZE); - + // Replace original values with the resampled output read = 0; for (auto& val : row) { // Only replace numeric values std::visit([&outbuf, &read](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_arithmetic_v) { - arg = static_cast(outbuf[read++]); + if constexpr (std::is_arithmetic_v) { + arg = static_cast(outbuf[read++]); } }, val); } - + // flushing last values written = smarc_resample_flush(pfilt, pstate, outbuf, OUT_BUF_SIZE); - + // Add any remaining flushed values read = 0; for (auto& val : row) { // Only replace numeric values std::visit([&outbuf, &read](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_arithmetic_v) { - arg = static_cast(outbuf[read++]); + if constexpr (std::is_arithmetic_v) { + arg = static_cast(outbuf[read++]); } }, val); } - + // you are done with converting your signal. // If you want to reuse the same converter to process another signal // just reset the state: smarc_reset_pstate(pstate,pfilt); - + delete[] inbuf; delete[] outbuf; } // release smarc filter state smarc_destroy_pstate(pstate); - + // release smarc filter smarc_destroy_pfilter(pfilt); } } //resampling finishes here - - + + //====================================================================== //===========Calculating total length & total channel count============= //====================================================================== - - + + calcTotalLength(userSrate); - + adjustTotalLength(); - + time = clock() - time; - + std::cout << "it took " << time << " clicks (" << ((float)time) / CLOCKS_PER_SEC << " seconds)" - << " resampling" << std::endl; + << " resampling" << std::endl; } //function of reading the length of each chunk @@ -579,24 +588,24 @@ uint64_t Xdf::readLength(std::ifstream &file) uint8_t bytes = 0; Xdf::readBin(file, &bytes); uint64_t length = 0; - + switch (bytes) { - case 1: - length = readBin(file); - break; - case 4: - length = readBin(file); - break; - case 8: - length = readBin(file); - break; - default: - std::cout << "Invalid variable-length integer length (" - << static_cast(bytes) << ") encountered.\n"; - return 0; + case 1: + length = readBin(file); + break; + case 4: + length = readBin(file); + break; + case 8: + length = readBin(file); + break; + default: + std::cout << "Invalid variable-length integer length (" + << static_cast(bytes) << ") encountered.\n"; + return 0; } - + return length; } @@ -616,7 +625,7 @@ void Xdf::findMinMax() if (!std::isnan(stream.info.first_timestamp) && stream.info.first_timestamp < minTS) minTS = stream.info.first_timestamp; } - + //find the max timestamp of all streams for (auto const &stream : streams) { @@ -630,17 +639,17 @@ void Xdf::findMajSR() // find out which sample rate has the most channels typedef int sampRate; typedef int numChannel; - - std::vector > srateMap; // pairs of all the streams - + + std::vector > srateMap; // pairs of all the streams + //find out whether a sample rate already exists in srateMap for (auto const &stream : streams) { if (stream.info.nominal_srate != 0) { - std::vector >::iterator it {std::find_if(srateMap.begin(), srateMap.end(), - [&](const std::pair &element) - {return element.first == stream.info.nominal_srate; })} ; + std::vector >::iterator it {std::find_if(srateMap.begin(), srateMap.end(), + [&](const std::pair &element) + {return element.first == stream.info.nominal_srate; })} ; //if it doesn't, add it here if (it == srateMap.end()) srateMap.emplace_back(stream.info.nominal_srate, stream.info.channel_count); @@ -652,15 +661,15 @@ void Xdf::findMajSR() } } } - + if(srateMap.size() > 0){ //search the srateMap to see which sample rate has the most channels int index (std::distance(srateMap.begin(), std::max_element(srateMap.begin(),srateMap.end(), - [] (const std::pair &largest, - const std::pair &first) - { return largest.second < first.second; }))); - + [] (const std::pair &largest, + const std::pair &first) + { return largest.second < first.second; }))); + majSR = srateMap[index].first; //the sample rate that has the most channels } else { majSR = 0; //if there are no streams with a fixed sample reate @@ -675,7 +684,7 @@ void Xdf::calcTotalChannel() if(!streams[c].time_series.empty()) { totalCh += streams[c].info.channel_count; - + for (int i = 0; i < streams[c].info.channel_count; i++) streamMap.emplace_back(c); } @@ -732,20 +741,20 @@ void Xdf::loadSampleRateMap() } /* -void Xdf::detrend() -{ - for (auto &stream : streams) - { - for (auto &row : stream.time_series) - { - long double init = 0.0; - long double mean = std::accumulate(row.begin(), row.end(), init) / row.size(); - for(auto &val: row) val -= mean; - offsets.emplace_back(mean); - } - } -} -*/ + void Xdf::detrend() + { + for (auto &stream : streams) + { + for (auto &row : stream.time_series) + { + long double init = 0.0; + long double mean = std::accumulate(row.begin(), row.end(), init) / row.size(); + for(auto &val: row) val -= mean; + offsets.emplace_back(mean); + } + } + } + */ void Xdf::calcEffectiveSrate() { @@ -756,29 +765,29 @@ void Xdf::calcEffectiveSrate() try { stream.info.effective_sample_rate - = stream.info.sample_count / - (stream.info.last_timestamp - stream.info.first_timestamp); - + = stream.info.sample_count / + (stream.info.last_timestamp - stream.info.first_timestamp); + if (stream.info.effective_sample_rate) effectiveSampleRateVector.emplace_back(stream.info.effective_sample_rate); - + pugi::xml_document doc; doc.load_string(stream.streamFooter.c_str()); pugi::xml_node sampleCount = doc.child("info").child("sample_count"); pugi::xml_node effectiveSampleRate - = doc.child("info").insert_child_after("effective_sample_rate", sampleCount); + = doc.child("info").insert_child_after("effective_sample_rate", sampleCount); effectiveSampleRate.append_child(pugi::node_pcdata) - .set_value(std::to_string(stream.info.effective_sample_rate).c_str()); - + .set_value(std::to_string(stream.info.effective_sample_rate).c_str()); + std::stringstream buffer; doc.save(buffer); - + stream.streamFooter = buffer.str(); } catch (std::exception &e) { std::cerr << "Error calculating effective sample rate. " - << e.what() << std::endl; + << e.what() << std::endl; } } } @@ -790,7 +799,7 @@ int Xdf::writeEventsToXDF(std::string file_path) { std::fstream file; file.open(file_path, std::ios::app | std::ios::binary); - + if (file.is_open()) { //start to append to new XDF file @@ -800,7 +809,7 @@ int Xdf::writeEventsToXDF(std::string file_path) //length int length = streams[userAddedStream].streamHeader.size() + 6; //+6 because of the length int itself and short int tag file.write((char*)&length, 4); - + //tag short tag = 2; file.write((char*)&tag, 2); @@ -809,7 +818,7 @@ int Xdf::writeEventsToXDF(std::string file_path) file.write((char*)&streamNumber, 4); //content file.write(streams[userAddedStream].streamHeader.c_str(), length - 6);//length - 6 is the string length - + //write samples chunk //Num Length Bytes file.put(8); @@ -818,13 +827,13 @@ int Xdf::writeEventsToXDF(std::string file_path) int64_t stringTotalLength = 0; for (auto const &event : userCreatedEvents) stringTotalLength += event.first.size(); - + int64_t sampleChunkLength = 2 + 4 + 1 + 4 + - userCreatedEvents.size() * - (1 + 8 + 1 + 4) + stringTotalLength; + userCreatedEvents.size() * + (1 + 8 + 1 + 4) + stringTotalLength; file.write((char*)&sampleChunkLength, 8); - - + + //tag tag = 3; file.write((char*)&tag, 2); @@ -833,32 +842,32 @@ int Xdf::writeEventsToXDF(std::string file_path) //content //NumSamplesBytes file.put(4); - + //Num Samples int numSamples = userCreatedEvents.size(); file.write((char*)&numSamples, 4); - + //samples for (auto const &event : userCreatedEvents) { //TimeStampBytes file.put(8); - + //Optional Time Stamp double timeStamp = event.second; file.write((char*)&timeStamp, 8); - + //Num Length Bytes file.put(4); - + //Length int stringLength = event.first.length(); file.write((char*)&stringLength, 4); - + //String Content file.write(event.first.c_str(), stringLength); } - + file.close(); } else @@ -867,16 +876,16 @@ int Xdf::writeEventsToXDF(std::string file_path) return -1; //Error } } - + std::cout << "Successfully wrote to XDF file." << std::endl; - + return 0; //Success } void Xdf::createLabels() { size_t channelCount = 0; - + for (size_t st = 0; st < streams.size(); st++) { if (streams[st].info.channels.size()) @@ -885,10 +894,10 @@ void Xdf::createLabels() { // +1 for 1 based numbers; for user convenience only. The internal computation is still 0 based std::string label = "Stream " + std::to_string(st + 1) + " - Channel " + std::to_string(ch + 1) - + " - " + std::to_string((int)streams[st].info.nominal_srate) + " Hz\n"; - + + " - " + std::to_string((int)streams[st].info.nominal_srate) + " Hz\n"; + label += streams[st].info.name + '\n'; - + for (auto const &entry : streams[st].info.channels[ch]) { if (entry.second != "") @@ -902,7 +911,7 @@ void Xdf::createLabels() label.append("baseline ").append(std::to_string(offsets[channelCount])); } labels.emplace_back(label); - + channelCount++; } } @@ -912,12 +921,12 @@ void Xdf::createLabels() { // +1 for 1 based numbers; for user convenience only. The internal computation is still 0 based std::string label = "Stream " + std::to_string(st + 1) + - " - Channel " + std::to_string(ch + 1) + " - " + - std::to_string((int)streams[st].info.nominal_srate) + - " Hz\n" + streams[st].info.name + '\n' + streams[st].info.type + '\n'; - + " - Channel " + std::to_string(ch + 1) + " - " + + std::to_string((int)streams[st].info.nominal_srate) + + " Hz\n" + streams[st].info.name + '\n' + streams[st].info.type + '\n'; + label += streams[st].info.name + '\n'; - + if (offsets.size()) { if (offsets[channelCount] >= 0) @@ -925,9 +934,9 @@ void Xdf::createLabels() else label.append("baseline ").append(std::to_string(offsets[channelCount])); } - + labels.emplace_back(label); - + channelCount++; } } @@ -953,9 +962,9 @@ void Xdf::loadDictionary() } } -template T Xdf::readBin(std::istream& is, T* obj) { - T dummy; - if(!obj) obj = &dummy; - is.read(reinterpret_cast(obj), sizeof(T)); - return *obj; +template T Xdf::readBin(std::istream& is, T* obj) { + T dummy; + if(!obj) obj = &dummy; + is.read(reinterpret_cast(obj), sizeof(T)); + return *obj; } From afc81b4ba02e96bdbe1ce9ab53b0488bd6a93ddd Mon Sep 17 00:00:00 2001 From: Melissa Key <24309569+melissakey@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:15:38 -0500 Subject: [PATCH 5/7] fixed typos --- xdf.cpp | 24 ++++++++++++------------ xdf.h | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/xdf.cpp b/xdf.cpp index b4614dd..0e2e3af 100644 --- a/xdf.cpp +++ b/xdf.cpp @@ -228,7 +228,7 @@ int Xdf::load_xdf(std::string filename) streams[index].time_series[v].emplace_back(data); } } else if(streams[index].info.channel_format.compare("double64") == 0) - + { for (int v = 0; v < streams[index].info.channel_count; ++v) { double data; @@ -514,7 +514,7 @@ void Xdf::resample(int userSrate) for(auto& val : row) { std::visit([&inbuf, &read](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_arithmetic_v) { + if constexpr (std::is_arithmetic_v) { inbuf[read++] = static_cast(arg); // Convert to double } }, val); @@ -528,8 +528,8 @@ void Xdf::resample(int userSrate) // Only replace numeric values std::visit([&outbuf, &read](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_arithmetic_v) { - arg = static_cast(outbuf[read++]); + if constexpr (std::is_arithmetic_v) { + arg = static_cast(outbuf[read++]); } }, val); } @@ -543,8 +543,8 @@ void Xdf::resample(int userSrate) // Only replace numeric values std::visit([&outbuf, &read](auto&& arg) { using T = std::decay_t; - if constexpr (std::is_arithmetic_v) { - arg = static_cast(outbuf[read++]); + if constexpr (std::is_arithmetic_v) { + arg = static_cast(outbuf[read++]); } }, val); } @@ -640,15 +640,15 @@ void Xdf::findMajSR() typedef int sampRate; typedef int numChannel; - std::vector > srateMap; // pairs of all the streams + std::vector > srateMap; // pairs of all the streams //find out whether a sample rate already exists in srateMap for (auto const &stream : streams) { if (stream.info.nominal_srate != 0) { - std::vector >::iterator it {std::find_if(srateMap.begin(), srateMap.end(), - [&](const std::pair &element) + std::vector >::iterator it {std::find_if(srateMap.begin(), srateMap.end(), + [&](const std::pair &element) {return element.first == stream.info.nominal_srate; })} ; //if it doesn't, add it here if (it == srateMap.end()) @@ -666,8 +666,8 @@ void Xdf::findMajSR() //search the srateMap to see which sample rate has the most channels int index (std::distance(srateMap.begin(), std::max_element(srateMap.begin(),srateMap.end(), - [] (const std::pair &largest, - const std::pair &first) + [] (const std::pair &largest, + const std::pair &first) { return largest.second < first.second; }))); majSR = srateMap[index].first; //the sample rate that has the most channels @@ -962,7 +962,7 @@ void Xdf::loadDictionary() } } -template T Xdf::readBin(std::istream& is, T* obj) { +template T Xdf::readBin(std::istream& is, T* obj) { T dummy; if(!obj) obj = &dummy; is.read(reinterpret_cast(obj), sizeof(T)); diff --git a/xdf.h b/xdf.h index b3374e4..4e6b77f 100644 --- a/xdf.h +++ b/xdf.h @@ -175,7 +175,7 @@ class Xdf * * \sa offsets */ - //void detrend(); + void detrend(); /*! * \brief Delete the time stamps vectors when no longer needed to From 5963a0101b6dc960fbb5f630c1ed34cef37b4784 Mon Sep 17 00:00:00 2001 From: Melissa Key <24309569+melissakey@users.noreply.github.com> Date: Fri, 15 Nov 2024 12:16:05 -0500 Subject: [PATCH 6/7] updated to use C++20 (matching main file) --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 42f9a91..79b7fe0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ else() target_include_directories(xdf PRIVATE pugixml) endif() -target_compile_features(xdf PUBLIC cxx_std_11) +target_compile_features(xdf PUBLIC cxx_std_20) set_target_properties(xdf PROPERTIES OUTPUT_NAME xdf PUBLIC_HEADER xdf.h) target_include_directories(xdf PUBLIC $ @@ -68,7 +68,7 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libxdfConfig.cmake # set(CPACK_STRIP_FILES ON) # set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) -set(CPACK_PACKAGE_CONTACT "yl3842@columbia.edu") +set(CPACK_PACKAGE_CONTACT "yida.lin@outlook.com") if(APPLE) set(CPACK_GENERATOR TBZ2) elseif(WIN32) From b4de105f1b3729dfde1aab81db4f98b6435ea220 Mon Sep 17 00:00:00 2001 From: Melissa Key <24309569+melissakey@users.noreply.github.com> Date: Fri, 15 Nov 2024 14:07:11 -0500 Subject: [PATCH 7/7] reverted to streams[index].timeseries.empty() in line 183 --- xdf.cpp | 538 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 285 insertions(+), 253 deletions(-) diff --git a/xdf.cpp b/xdf.cpp index 0e2e3af..2ada86e 100644 --- a/xdf.cpp +++ b/xdf.cpp @@ -38,28 +38,28 @@ int Xdf::load_xdf(std::string filename) { clock_t time; time = clock(); - - + + /* //uncompress if necessary char ext[_MAX_EXT]; //for file extension - + _splitpath_s ( argv[1], NULL, NULL, NULL, NULL, NULL, NULL, ext, NULL ); if (strcmp(ext, ".xdfz") == 0) { //uncompress } */ - + std::vector idmap; //remaps stream id's onto indices in streams - - + + //=================================================================== //========================= parse the file ========================== //=================================================================== - - + + std::ifstream file(filename, std::ios::in | std::ios::binary); - + if (file.is_open()) { //read [MagicCode] @@ -70,50 +70,50 @@ int Xdf::load_xdf(std::string filename) if (magicNumber.size() == 4) break; } - + if (magicNumber.compare("XDF:")) { std::cout << "This is not a valid XDF file.('" << filename << "')\n"; return -1; } - + //for each chunk while (1) { - uint64_t ChLen = readLength(file);//chunk length - + uint64_t ChLen = readLength(file); //chunk length + if (ChLen == 0) break; - - uint16_t tag; //read tag of the chunk, 6 possibilities + + uint16_t tag; //read tag of the chunk, 6 possibilities readBin(file, &tag); - + switch (tag) { - case 1: //[FileHeader] + case 1: //[FileHeader] { char* buffer = new char[ChLen - 2]; file.read(buffer, ChLen - 2); fileHeader = buffer; - + pugi::xml_document doc; - + doc.load_buffer_inplace(buffer, ChLen - 2); - + pugi::xml_node info = doc.child("info"); - + version = info.child("version").text().as_float(); - + delete[] buffer; } - break; - case 2: //read [StreamHeader] chunk + break; + case 2: //read [StreamHeader] chunk { //read [StreamID] uint32_t streamID; int index; Xdf::readBin(file, &streamID); - std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; + std::vector::iterator it{std::find(idmap.begin(), idmap.end(), streamID)}; if (it == idmap.end()) { index = idmap.size(); @@ -122,49 +122,50 @@ int Xdf::load_xdf(std::string filename) } else index = std::distance(idmap.begin(), it); - - + + pugi::xml_document doc; - + //read [Content] char* buffer = new char[ChLen - 6]; file.read(buffer, ChLen - 6); streams[index].streamHeader = buffer; - + doc.load_buffer_inplace(buffer, ChLen - 6); - + pugi::xml_node info = doc.child("info"); pugi::xml_node desc = info.child("desc"); - + streams[index].info.channel_count = info.child("channel_count").text().as_int(); streams[index].info.nominal_srate = info.child("nominal_srate").text().as_double(); streams[index].info.name = info.child("name").text().get(); streams[index].info.type = info.child("type").text().get(); streams[index].info.channel_format = info.child("channel_format").text().get(); - - for (auto channel = desc.child("channels").child("channel"); channel; channel = channel.next_sibling("channel")) + + for (auto channel = desc.child("channels").child("channel"); channel; channel = channel. + next_sibling("channel")) { streams[index].info.channels.emplace_back(); - - for (auto const &entry : channel.children()) + + for (auto const& entry : channel.children()) streams[index].info.channels.back().emplace(entry.name(), entry.child_value()); } - + if (streams[index].info.nominal_srate > 0) streams[index].sampling_interval = 1 / streams[index].info.nominal_srate; else streams[index].sampling_interval = 0; - + delete[] buffer; } - break; - case 3: //read [Samples] chunk + break; + case 3: //read [Samples] chunk { //read [StreamID] uint32_t streamID; int index; Xdf::readBin(file, &streamID); - std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; + std::vector::iterator it{std::find(idmap.begin(), idmap.end(), streamID)}; if (it == idmap.end()) { index = idmap.size(); @@ -173,25 +174,25 @@ int Xdf::load_xdf(std::string filename) } else index = std::distance(idmap.begin(), it); - - + + //read [NumSampleBytes], [NumSamples] uint64_t numSamp = readLength(file); - + //if the time series is empty - if (Stream stream = streams[index]; stream.time_series.empty()) + if (streams[index].time_series.empty()) { - stream.time_series.resize(stream.info.channel_count); + streams[index].time_series.resize(streams[index].info.channel_count); } - + //for each sample for (size_t i = 0; i < numSamp; i++) { //read or deduce time stamp auto tsBytes = readBin(file); - - double ts; //temporary time stamp - + + double ts; //temporary time stamp + if (tsBytes == 8) { Xdf::readBin(file, &ts); @@ -202,24 +203,25 @@ int Xdf::load_xdf(std::string filename) ts = streams[index].last_timestamp + streams[index].sampling_interval; streams[index].time_stamps.emplace_back(ts); } - + streams[index].last_timestamp = ts; - - if(streams[index].info.channel_format.compare("string") == 0) + + if (streams[index].info.channel_format.compare("string") == 0) { - - for(int v = 0; v < streams[index].info.channel_count; ++v) + for (int v = 0; v < streams[index].info.channel_count; ++v) { auto length = Xdf::readLength(file); char* buffer = new char[length + 1]; file.read(buffer, length); buffer[length] = '\0'; - + streams[index].time_series[v].emplace_back(buffer); } - } else { + } + else + { //read the data - if(streams[index].info.channel_format.compare("float32") == 0) + if (streams[index].info.channel_format.compare("float32") == 0) { for (int v = 0; v < streams[index].info.channel_count; ++v) { @@ -227,7 +229,8 @@ int Xdf::load_xdf(std::string filename) Xdf::readBin(file, &data); streams[index].time_series[v].emplace_back(data); } - } else if(streams[index].info.channel_format.compare("double64") == 0) + } + else if (streams[index].info.channel_format.compare("double64") == 0) { for (int v = 0; v < streams[index].info.channel_count; ++v) { @@ -235,7 +238,8 @@ int Xdf::load_xdf(std::string filename) Xdf::readBin(file, &data); streams[index].time_series[v].emplace_back(data); } - } else if(streams[index].info.channel_format.compare("int8_t") == 0) + } + else if (streams[index].info.channel_format.compare("int8_t") == 0) { for (int v = 0; v < streams[index].info.channel_count; ++v) { @@ -243,7 +247,8 @@ int Xdf::load_xdf(std::string filename) Xdf::readBin(file, &data); streams[index].time_series[v].emplace_back(data); } - } else if(streams[index].info.channel_format.compare("int16_t") == 0) + } + else if (streams[index].info.channel_format.compare("int16_t") == 0) { for (int v = 0; v < streams[index].info.channel_count; ++v) { @@ -251,7 +256,8 @@ int Xdf::load_xdf(std::string filename) Xdf::readBin(file, &data); streams[index].time_series[v].emplace_back(data); } - } else if(streams[index].info.channel_format.compare("int32_t") == 0) + } + else if (streams[index].info.channel_format.compare("int32_t") == 0) { for (int v = 0; v < streams[index].info.channel_count; ++v) { @@ -259,7 +265,8 @@ int Xdf::load_xdf(std::string filename) Xdf::readBin(file, &data); streams[index].time_series[v].emplace_back(data); } - } else if(streams[index].info.channel_format.compare("int64_t") == 0) + } + else if (streams[index].info.channel_format.compare("int64_t") == 0) { for (int v = 0; v < streams[index].info.channel_count; ++v) { @@ -271,13 +278,13 @@ int Xdf::load_xdf(std::string filename) } } } - break; - case 4: //read [ClockOffset] chunk + break; + case 4: //read [ClockOffset] chunk { uint32_t streamID; int index; Xdf::readBin(file, &streamID); - std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; + std::vector::iterator it{std::find(idmap.begin(), idmap.end(), streamID)}; if (it == idmap.end()) { index = idmap.size(); @@ -286,27 +293,27 @@ int Xdf::load_xdf(std::string filename) } else index = std::distance(idmap.begin(), it); - - + + double collectionTime; double offsetValue; - + Xdf::readBin(file, &collectionTime); Xdf::readBin(file, &offsetValue); - + streams[index].clock_times.emplace_back(collectionTime); streams[index].clock_values.emplace_back(offsetValue); } - break; - case 6: //read [StreamFooter] chunk + break; + case 6: //read [StreamFooter] chunk { pugi::xml_document doc; - + //read [StreamID] uint32_t streamID; int index; Xdf::readBin(file, &streamID); - std::vector::iterator it {std::find(idmap.begin(),idmap.end(),streamID)}; + std::vector::iterator it{std::find(idmap.begin(), idmap.end(), streamID)}; if (it == idmap.end()) { index = idmap.size(); @@ -315,60 +322,60 @@ int Xdf::load_xdf(std::string filename) } else index = std::distance(idmap.begin(), it); - - + + char* buffer = new char[ChLen - 6]; file.read(buffer, ChLen - 6); streams[index].streamFooter = buffer; - + doc.load_buffer_inplace(buffer, ChLen - 6); - + pugi::xml_node info = doc.child("info"); - + streams[index].info.first_timestamp = info.child("first_timestamp").text().as_double(); streams[index].info.last_timestamp = info.child("last_timestamp").text().as_double(); streams[index].info.measured_srate = info.child("measured_srate").text().as_double(); streams[index].info.sample_count = info.child("sample_count").text().as_int(); delete[] buffer; } - break; - case 5: //skip other chunk types (Boundary, ...) - file.seekg(ChLen - 2, file.cur); - break; - default: - std::cout << "Unknown chunk encountered.\n"; - break; + break; + case 5: //skip other chunk types (Boundary, ...) + file.seekg(ChLen - 2, file.cur); + break; + default: + std::cout << "Unknown chunk encountered.\n"; + break; } } - - + + //calculate how much time it takes to read the data clock_t halfWay = clock() - time; - + std::cout << "it took " << halfWay << " clicks (" << ((float)halfWay) / CLOCKS_PER_SEC << " seconds)" - << " reading XDF data" << std::endl; - - + << " reading XDF data" << std::endl; + + //========================================================== //=============find the min and max time stamps============= //========================================================== - + syncTimeStamps(); - + findMinMax(); - + findMajSR(); - + getHighestSampleRate(); - + loadSampleRateMap(); - + calcTotalChannel(); - + loadDictionary(); - + calcEffectiveSrate(); - + //loading finishes, close file file.close(); } @@ -377,25 +384,25 @@ int Xdf::load_xdf(std::string filename) std::cout << "Unable to open file" << std::endl; return 1; } - + return 0; } void Xdf::syncTimeStamps() { // Sync time stamps - for (auto &stream : this->streams) + for (auto& stream : this->streams) { if (!stream.clock_times.empty()) { - size_t m = 0; // index iterating through stream.time_stamps - size_t n = 0; // index iterating through stream.clock_times - + size_t m = 0; // index iterating through stream.time_stamps + size_t n = 0; // index iterating through stream.clock_times + while (m < stream.time_stamps.size()) { if (stream.clock_times[n] < stream.time_stamps[m]) { - while (n < stream.clock_times.size() - 1 && stream.clock_times[n+1] < stream.time_stamps[m]) + while (n < stream.clock_times.size() - 1 && stream.clock_times[n + 1] < stream.time_stamps[m]) { n++; } @@ -409,17 +416,17 @@ void Xdf::syncTimeStamps() } } } - + // Sync event time stamps - for (auto &elem : this->eventMap) + for (auto& elem : this->eventMap) { if (!this->streams[elem.second].clock_times.empty()) { - size_t k = 0; // index iterating through streams[elem.second].clock_times - + size_t k = 0; // index iterating through streams[elem.second].clock_times + while (k < this->streams[elem.second].clock_times.size() - 1) { - if (this->streams[elem.second].clock_times[k+1] < elem.first.second) + if (this->streams[elem.second].clock_times[k + 1] < elem.first.second) { k++; } @@ -428,11 +435,12 @@ void Xdf::syncTimeStamps() break; } } - - elem.first.second += this->streams[elem.second].clock_values[k]; // apply the last offset value to the timestamp; if there hasn't yet been an offset value take the first recorded one + + elem.first.second += this->streams[elem.second].clock_values[k]; + // apply the last offset value to the timestamp; if there hasn't yet been an offset value take the first recorded one } } - + // Update first and last time stamps in stream footer for (size_t k = 0; k < this->streams.size(); k++) { @@ -440,8 +448,8 @@ void Xdf::syncTimeStamps() { double min = NAN; double max = NAN; - - for (auto const &elem : this->eventMap) + + for (auto const& elem : this->eventMap) { if (elem.second == (int)k) { @@ -449,20 +457,21 @@ void Xdf::syncTimeStamps() { min = elem.first.second; } - + if (std::isnan(max) || elem.first.second > max) { max = elem.first.second; } } } - + streams[k].info.first_timestamp = min; streams[k].info.last_timestamp = max; } else { - if (streams[k].time_stamps.size() > 0) { + if (streams[k].time_stamps.size() > 0) + { streams[k].info.first_timestamp = streams[k].time_stamps.front(); streams[k].info.last_timestamp = streams[k].time_stamps.back(); } @@ -474,145 +483,154 @@ void Xdf::resample(int userSrate) { //if user entered a preferred sample rate, we resample all the channels to that sample rate //Otherwise, we resample all channels to the sample rate that has the most channels - + clock_t time = clock(); - + #define BUF_SIZE 8192 - for (auto &stream : streams) + for (auto& stream : streams) { if (!stream.time_series.empty() && !stream.info.channel_format.compare("string") && stream.info.nominal_srate != userSrate && stream.info.nominal_srate != 0) { - int fsin = stream.info.nominal_srate; // input samplerate - int fsout = userSrate; // output samplerate - double bandwidth = 0.95; // bandwidth - double rp = 0.1; // passband ripple factor - double rs = 140; // stopband attenuation - double tol = 0.000001; // tolerance - + int fsin = stream.info.nominal_srate; // input samplerate + int fsout = userSrate; // output samplerate + double bandwidth = 0.95; // bandwidth + double rp = 0.1; // passband ripple factor + double rs = 140; // stopband attenuation + double tol = 0.000001; // tolerance + // initialize smarc filter struct PFilter* pfilt = smarc_init_pfilter(fsin, fsout, bandwidth, rp, rs, tol, NULL, 0); if (pfilt == NULL) continue; - + // initialize smarc filter state struct PState* pstate = smarc_init_pstate(pfilt); - - for (auto &row : stream.time_series) + + for (auto& row : stream.time_series) { // initialize buffers int read = 0; int written = 0; - const int OUT_BUF_SIZE = (int) smarc_get_output_buffer_size(pfilt, row.size()); + const int OUT_BUF_SIZE = (int)smarc_get_output_buffer_size(pfilt, row.size()); double* inbuf = new double[row.size()]; double* outbuf = new double[OUT_BUF_SIZE]; - + // Fill inbuf with the numeric values from the row - for(auto& val : row) { - std::visit([&inbuf, &read](auto&& arg) { + for (auto& val : row) + { + std::visit([&inbuf, &read](auto&& arg) + { using T = std::decay_t; - if constexpr (std::is_arithmetic_v) { + if constexpr (std::is_arithmetic_v) + { inbuf[read++] = static_cast(arg); // Convert to double } }, val); } // resample signal block written = smarc_resample(pfilt, pstate, inbuf, read, outbuf, OUT_BUF_SIZE); - + // Replace original values with the resampled output read = 0; - for (auto& val : row) { + for (auto& val : row) + { // Only replace numeric values - std::visit([&outbuf, &read](auto&& arg) { + std::visit([&outbuf, &read](auto&& arg) + { using T = std::decay_t; - if constexpr (std::is_arithmetic_v) { + if constexpr (std::is_arithmetic_v) + { arg = static_cast(outbuf[read++]); } }, val); } - + // flushing last values written = smarc_resample_flush(pfilt, pstate, outbuf, OUT_BUF_SIZE); - + // Add any remaining flushed values read = 0; - for (auto& val : row) { + for (auto& val : row) + { // Only replace numeric values - std::visit([&outbuf, &read](auto&& arg) { + std::visit([&outbuf, &read](auto&& arg) + { using T = std::decay_t; - if constexpr (std::is_arithmetic_v) { + if constexpr (std::is_arithmetic_v) + { arg = static_cast(outbuf[read++]); } }, val); } - + // you are done with converting your signal. // If you want to reuse the same converter to process another signal // just reset the state: - smarc_reset_pstate(pstate,pfilt); - + smarc_reset_pstate(pstate, pfilt); + delete[] inbuf; delete[] outbuf; } // release smarc filter state smarc_destroy_pstate(pstate); - + // release smarc filter smarc_destroy_pfilter(pfilt); } } //resampling finishes here - - + + //====================================================================== //===========Calculating total length & total channel count============= //====================================================================== - - + + calcTotalLength(userSrate); - + adjustTotalLength(); - + time = clock() - time; - + std::cout << "it took " << time << " clicks (" << ((float)time) / CLOCKS_PER_SEC << " seconds)" - << " resampling" << std::endl; + << " resampling" << std::endl; } //function of reading the length of each chunk -uint64_t Xdf::readLength(std::ifstream &file) +uint64_t Xdf::readLength(std::ifstream& file) { uint8_t bytes = 0; Xdf::readBin(file, &bytes); uint64_t length = 0; - + switch (bytes) { - case 1: - length = readBin(file); - break; - case 4: - length = readBin(file); - break; - case 8: - length = readBin(file); - break; - default: - std::cout << "Invalid variable-length integer length (" + case 1: + length = readBin(file); + break; + case 4: + length = readBin(file); + break; + case 8: + length = readBin(file); + break; + default: + std::cout << "Invalid variable-length integer length (" << static_cast(bytes) << ") encountered.\n"; - return 0; + return 0; } - + return length; } void Xdf::findMinMax() { //find the smallest timestamp of all streams - for (auto const &stream : streams) + for (auto const& stream : streams) { if (!std::isnan(stream.info.first_timestamp)) { @@ -620,14 +638,14 @@ void Xdf::findMinMax() break; } } - for (auto const &stream : streams) + for (auto const& stream : streams) { if (!std::isnan(stream.info.first_timestamp) && stream.info.first_timestamp < minTS) minTS = stream.info.first_timestamp; } - + //find the max timestamp of all streams - for (auto const &stream : streams) + for (auto const& stream : streams) { if (!std::isnan(stream.info.last_timestamp) && stream.info.last_timestamp > maxTS) maxTS = stream.info.last_timestamp; @@ -639,39 +657,48 @@ void Xdf::findMajSR() // find out which sample rate has the most channels typedef int sampRate; typedef int numChannel; - - std::vector > srateMap; // pairs of all the streams - + + std::vector> srateMap; // pairs of all the streams + //find out whether a sample rate already exists in srateMap - for (auto const &stream : streams) + for (auto const& stream : streams) { if (stream.info.nominal_srate != 0) { - std::vector >::iterator it {std::find_if(srateMap.begin(), srateMap.end(), - [&](const std::pair &element) - {return element.first == stream.info.nominal_srate; })} ; + std::vector>::iterator it{ + std::find_if(srateMap.begin(), srateMap.end(), + [&](const std::pair& element) + { + return element.first == stream.info.nominal_srate; + }) + }; //if it doesn't, add it here if (it == srateMap.end()) srateMap.emplace_back(stream.info.nominal_srate, stream.info.channel_count); //if it already exists, add additional channel numbers to that sample rate else { - int index (std::distance(srateMap.begin(),it)) ; + int index(std::distance(srateMap.begin(), it)); srateMap[index].second += stream.info.channel_count; } } } - - if(srateMap.size() > 0){ + + if (srateMap.size() > 0) + { //search the srateMap to see which sample rate has the most channels - int index (std::distance(srateMap.begin(), - std::max_element(srateMap.begin(),srateMap.end(), - [] (const std::pair &largest, - const std::pair &first) - { return largest.second < first.second; }))); - + int index(std::distance(srateMap.begin(), + std::max_element(srateMap.begin(), srateMap.end(), + [](const std::pair& largest, + const std::pair& first) + { + return largest.second < first.second; + }))); + majSR = srateMap[index].first; //the sample rate that has the most channels - } else { + } + else + { majSR = 0; //if there are no streams with a fixed sample reate } } @@ -681,10 +708,10 @@ void Xdf::calcTotalChannel() //calculating total channel count, and indexing them onto streamMap for (size_t c = 0; c < streams.size(); c++) { - if(!streams[c].time_series.empty()) + if (!streams[c].time_series.empty()) { totalCh += streams[c].info.channel_count; - + for (int i = 0; i < streams[c].info.channel_count; i++) streamMap.emplace_back(c); } @@ -699,11 +726,12 @@ void Xdf::calcTotalLength(int sampleRate) void Xdf::freeUpTimeStamps() { //free up as much memory as possible - for (auto &stream : streams) + for (auto& stream : streams) { //we don't need to keep all the time stamps unless it's a stream with irregular samples //filter irregular streams and string streams - if (stream.info.nominal_srate != 0 && !stream.time_stamps.empty() && stream.info.channel_format.compare("string")) + if (stream.info.nominal_srate != 0 && !stream.time_stamps.empty() && stream.info.channel_format. + compare("string")) { std::vector nothing; //however we still need to keep the first time stamp of each stream to decide at which position the signal should start @@ -715,9 +743,9 @@ void Xdf::freeUpTimeStamps() void Xdf::adjustTotalLength() { - for (auto const &stream : streams) + for (auto const& stream : streams) { - if(!stream.time_series.empty()) + if (!stream.time_series.empty()) { if (totalLen < stream.time_series.front().size()) totalLen = stream.time_series.front().size(); @@ -727,7 +755,7 @@ void Xdf::adjustTotalLength() void Xdf::getHighestSampleRate() { - for (auto const &stream : streams) + for (auto const& stream : streams) { if (stream.info.nominal_srate > maxSR) maxSR = stream.info.nominal_srate; @@ -736,7 +764,7 @@ void Xdf::getHighestSampleRate() void Xdf::loadSampleRateMap() { - for (auto const &stream : streams) + for (auto const& stream : streams) sampleRateMap.emplace(stream.info.nominal_srate); } @@ -758,36 +786,36 @@ void Xdf::loadSampleRateMap() void Xdf::calcEffectiveSrate() { - for (auto &stream : streams) + for (auto& stream : streams) { if (stream.info.nominal_srate) { try { stream.info.effective_sample_rate - = stream.info.sample_count / - (stream.info.last_timestamp - stream.info.first_timestamp); - + = stream.info.sample_count / + (stream.info.last_timestamp - stream.info.first_timestamp); + if (stream.info.effective_sample_rate) effectiveSampleRateVector.emplace_back(stream.info.effective_sample_rate); - + pugi::xml_document doc; doc.load_string(stream.streamFooter.c_str()); pugi::xml_node sampleCount = doc.child("info").child("sample_count"); pugi::xml_node effectiveSampleRate - = doc.child("info").insert_child_after("effective_sample_rate", sampleCount); + = doc.child("info").insert_child_after("effective_sample_rate", sampleCount); effectiveSampleRate.append_child(pugi::node_pcdata) - .set_value(std::to_string(stream.info.effective_sample_rate).c_str()); - + .set_value(std::to_string(stream.info.effective_sample_rate).c_str()); + std::stringstream buffer; doc.save(buffer); - + stream.streamFooter = buffer.str(); } - catch (std::exception &e) + catch (std::exception& e) { std::cerr << "Error calculating effective sample rate. " - << e.what() << std::endl; + << e.what() << std::endl; } } } @@ -799,7 +827,7 @@ int Xdf::writeEventsToXDF(std::string file_path) { std::fstream file; file.open(file_path, std::ios::app | std::ios::binary); - + if (file.is_open()) { //start to append to new XDF file @@ -807,9 +835,10 @@ int Xdf::writeEventsToXDF(std::string file_path) //Num Length Bytes file.put(4); //length - int length = streams[userAddedStream].streamHeader.size() + 6; //+6 because of the length int itself and short int tag + int length = streams[userAddedStream].streamHeader.size() + 6; + //+6 because of the length int itself and short int tag file.write((char*)&length, 4); - + //tag short tag = 2; file.write((char*)&tag, 2); @@ -817,23 +846,23 @@ int Xdf::writeEventsToXDF(std::string file_path) int streamNumber = userAddedStream + 1; //+1 because the stream IDs in XDF are 1 based instead of 0 based file.write((char*)&streamNumber, 4); //content - file.write(streams[userAddedStream].streamHeader.c_str(), length - 6);//length - 6 is the string length - + file.write(streams[userAddedStream].streamHeader.c_str(), length - 6); //length - 6 is the string length + //write samples chunk //Num Length Bytes file.put(8); //length //add the bytes of all following actions together int64_t stringTotalLength = 0; - for (auto const &event : userCreatedEvents) + for (auto const& event : userCreatedEvents) stringTotalLength += event.first.size(); - + int64_t sampleChunkLength = 2 + 4 + 1 + 4 + - userCreatedEvents.size() * - (1 + 8 + 1 + 4) + stringTotalLength; + userCreatedEvents.size() * + (1 + 8 + 1 + 4) + stringTotalLength; file.write((char*)&sampleChunkLength, 8); - - + + //tag tag = 3; file.write((char*)&tag, 2); @@ -842,32 +871,32 @@ int Xdf::writeEventsToXDF(std::string file_path) //content //NumSamplesBytes file.put(4); - + //Num Samples int numSamples = userCreatedEvents.size(); file.write((char*)&numSamples, 4); - + //samples - for (auto const &event : userCreatedEvents) + for (auto const& event : userCreatedEvents) { //TimeStampBytes file.put(8); - + //Optional Time Stamp double timeStamp = event.second; file.write((char*)&timeStamp, 8); - + //Num Length Bytes file.put(4); - + //Length int stringLength = event.first.length(); file.write((char*)&stringLength, 4); - + //String Content file.write(event.first.c_str(), stringLength); } - + file.close(); } else @@ -876,16 +905,16 @@ int Xdf::writeEventsToXDF(std::string file_path) return -1; //Error } } - + std::cout << "Successfully wrote to XDF file." << std::endl; - + return 0; //Success } void Xdf::createLabels() { size_t channelCount = 0; - + for (size_t st = 0; st < streams.size(); st++) { if (streams[st].info.channels.size()) @@ -894,11 +923,11 @@ void Xdf::createLabels() { // +1 for 1 based numbers; for user convenience only. The internal computation is still 0 based std::string label = "Stream " + std::to_string(st + 1) + " - Channel " + std::to_string(ch + 1) - + " - " + std::to_string((int)streams[st].info.nominal_srate) + " Hz\n"; - + + " - " + std::to_string((int)streams[st].info.nominal_srate) + " Hz\n"; + label += streams[st].info.name + '\n'; - - for (auto const &entry : streams[st].info.channels[ch]) + + for (auto const& entry : streams[st].info.channels[ch]) { if (entry.second != "") label += entry.first + " : " + entry.second + '\n'; @@ -911,7 +940,7 @@ void Xdf::createLabels() label.append("baseline ").append(std::to_string(offsets[channelCount])); } labels.emplace_back(label); - + channelCount++; } } @@ -921,12 +950,12 @@ void Xdf::createLabels() { // +1 for 1 based numbers; for user convenience only. The internal computation is still 0 based std::string label = "Stream " + std::to_string(st + 1) + - " - Channel " + std::to_string(ch + 1) + " - " + - std::to_string((int)streams[st].info.nominal_srate) + - " Hz\n" + streams[st].info.name + '\n' + streams[st].info.type + '\n'; - + " - Channel " + std::to_string(ch + 1) + " - " + + std::to_string((int)streams[st].info.nominal_srate) + + " Hz\n" + streams[st].info.name + '\n' + streams[st].info.type + '\n'; + label += streams[st].info.name + '\n'; - + if (offsets.size()) { if (offsets[channelCount] >= 0) @@ -934,9 +963,9 @@ void Xdf::createLabels() else label.append("baseline ").append(std::to_string(offsets[channelCount])); } - + labels.emplace_back(label); - + channelCount++; } } @@ -946,25 +975,28 @@ void Xdf::createLabels() void Xdf::loadDictionary() { //loop through the eventMap - for (auto const &entry : eventMap) + for (auto const& entry : eventMap) { //search the dictionary to see whether an event is already in it - auto it = std::find(dictionary.begin(),dictionary.end(),entry.first.first); + auto it = std::find(dictionary.begin(), dictionary.end(), entry.first.first); //if it isn't yet if (it == dictionary.end()) - { //add it to the dictionary, also store its index into eventType vector for future use + { + //add it to the dictionary, also store its index into eventType vector for future use eventType.emplace_back(dictionary.size()); dictionary.emplace_back(entry.first.first); } //if it's already in there - else //store its index into eventType vector + else //store its index into eventType vector eventType.emplace_back(std::distance(dictionary.begin(), it)); } } -template T Xdf::readBin(std::istream& is, T* obj) { +template +T Xdf::readBin(std::istream& is, T* obj) +{ T dummy; - if(!obj) obj = &dummy; + if (!obj) obj = &dummy; is.read(reinterpret_cast(obj), sizeof(T)); return *obj; }