diff --git a/xdf.cpp b/xdf.cpp index 1891640..2ada86e 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 @@ -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() { @@ -40,14 +41,14 @@ int Xdf::load_xdf(std::string filename) /* //uncompress if necessary - char ext[_MAX_EXT]; //for file extension + 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 - } - */ + _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 @@ -79,254 +80,110 @@ int Xdf::load_xdf(std::string filename) //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] - { - char* buffer = new char[ChLen - 2]; - file.read(buffer, ChLen - 2); - fileHeader = buffer; + { + char* buffer = new char[ChLen - 2]; + file.read(buffer, ChLen - 2); + fileHeader = buffer; - pugi::xml_document doc; + pugi::xml_document doc; - doc.load_buffer_inplace(buffer, ChLen - 2); + doc.load_buffer_inplace(buffer, ChLen - 2); - pugi::xml_node info = doc.child("info"); + pugi::xml_node info = doc.child("info"); - version = info.child("version").text().as_float(); + version = info.child("version").text().as_float(); - delete[] buffer; - } + 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); + //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; + pugi::xml_document doc; - //read [Content] - char* buffer = new char[ChLen - 6]; - file.read(buffer, ChLen - 6); - streams[index].streamHeader = buffer; + //read [Content] + char* buffer = new char[ChLen - 6]; + file.read(buffer, ChLen - 6); + streams[index].streamHeader = buffer; - doc.load_buffer_inplace(buffer, ChLen - 6); + doc.load_buffer_inplace(buffer, ChLen - 6); - pugi::xml_node info = doc.child("info"); - pugi::xml_node desc = info.child("desc"); + 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(); + 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")) - { - streams[index].info.channels.emplace_back(); + 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()) - streams[index].info.channels.back().emplace(entry.name(), entry.child_value()); - } + 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; + 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; - } + 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()) - { - 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); - - //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 [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()) { - //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); - } + index = idmap.size(); + idmap.emplace_back(streamID); + streams.emplace_back(); } - } - 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); + else + index = std::distance(idmap.begin(), it); - //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 + //read [NumSampleBytes], [NumSamples] + uint64_t numSamp = readLength(file); - 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++) @@ -334,7 +191,7 @@ int Xdf::load_xdf(std::string filename) //read or deduce time stamp auto tsBytes = readBin(file); - double ts; //temporary time stamp + double ts; //temporary time stamp if (tsBytes == 8) { @@ -349,140 +206,140 @@ int Xdf::load_xdf(std::string filename) 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) + if (streams[index].info.channel_format.compare("string") == 0) { - Xdf::readBin(file, &ts); - streams[index].time_stamps.emplace_back(ts); + 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 { - ts = streams[index].last_timestamp + streams[index].sampling_interval; - streams[index].time_stamps.emplace_back(ts); + //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); + } + } } - - 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); - - char* buffer = new char[length + 1]; - file.read(buffer, length); - buffer[length] = '\0'; - eventMap.emplace_back(std::make_pair(buffer, ts), index); - delete[] buffer; - streams[index].last_timestamp = ts; } } - } 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()) { - index = idmap.size(); - idmap.emplace_back(streamID); - streams.emplace_back(); - } - else - index = std::distance(idmap.begin(), it); + 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; + double collectionTime; + double offsetValue; - Xdf::readBin(file, &collectionTime); - Xdf::readBin(file, &offsetValue); + Xdf::readBin(file, &collectionTime); + Xdf::readBin(file, &offsetValue); - streams[index].clock_times.emplace_back(collectionTime); - streams[index].clock_values.emplace_back(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()) { - index = idmap.size(); - idmap.emplace_back(streamID); - streams.emplace_back(); - } - else - index = std::distance(idmap.begin(), it); + 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; + char* buffer = new char[ChLen - 6]; + file.read(buffer, ChLen - 6); + streams[index].streamFooter = buffer; - doc.load_buffer_inplace(buffer, ChLen - 6); + doc.load_buffer_inplace(buffer, ChLen - 6); - pugi::xml_node info = doc.child("info"); + 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; - } + 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, ...) + case 5: //skip other chunk types (Boundary, ...) file.seekg(ChLen - 2, file.cur); break; default: @@ -496,7 +353,7 @@ int Xdf::load_xdf(std::string filename) 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; //========================================================== @@ -534,18 +391,18 @@ int Xdf::load_xdf(std::string filename) 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++; } @@ -561,15 +418,15 @@ 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++; } @@ -579,7 +436,8 @@ void Xdf::syncTimeStamps() } } - 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 } } @@ -591,7 +449,7 @@ 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) { @@ -612,10 +470,11 @@ void Xdf::syncTimeStamps() } 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(); - } + streams[k].info.last_timestamp = streams[k].time_stamps.back(); + } } } } @@ -628,18 +487,19 @@ void Xdf::resample(int userSrate) clock_t time = clock(); #define BUF_SIZE 8192 - for (auto &stream : streams) + for (auto& stream : streams) { if (!stream.time_series.empty() && - stream.info.nominal_srate != userSrate && - stream.info.nominal_srate != 0) + !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, @@ -650,38 +510,67 @@ void Xdf::resample(int userSrate) // 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]; - - 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 // just reset the state: - smarc_reset_pstate(pstate,pfilt); + smarc_reset_pstate(pstate, pfilt); delete[] inbuf; delete[] outbuf; @@ -708,11 +597,11 @@ void Xdf::resample(int userSrate) 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); @@ -731,7 +620,7 @@ uint64_t Xdf::readLength(std::ifstream &file) break; default: std::cout << "Invalid variable-length integer length (" - << static_cast(bytes) << ") encountered.\n"; + << static_cast(bytes) << ") encountered.\n"; return 0; } @@ -741,7 +630,7 @@ uint64_t Xdf::readLength(std::ifstream &file) 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)) { @@ -749,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; @@ -769,38 +658,47 @@ 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) + 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 } } @@ -810,7 +708,7 @@ 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; @@ -828,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 @@ -844,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(); @@ -856,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; @@ -865,35 +764,37 @@ void Xdf::getHighestSampleRate() void Xdf::loadSampleRateMap() { - for (auto const &stream : streams) + for (auto const& stream : streams) sampleRateMap.emplace(stream.info.nominal_srate); } -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() { - 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); @@ -902,19 +803,19 @@ void Xdf::calcEffectiveSrate() 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; } } } @@ -934,7 +835,8 @@ 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 @@ -944,7 +846,7 @@ 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 @@ -952,12 +854,12 @@ int Xdf::writeEventsToXDF(std::string file_path) //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); @@ -975,7 +877,7 @@ int Xdf::writeEventsToXDF(std::string file_path) file.write((char*)&numSamples, 4); //samples - for (auto const &event : userCreatedEvents) + for (auto const& event : userCreatedEvents) { //TimeStampBytes file.put(8); @@ -1021,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'; @@ -1048,9 +950,9 @@ 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'; @@ -1073,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) { - 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; } diff --git a/xdf.h b/xdf.h index 47e73a1..4e6b77f 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. */ @@ -312,6 +313,7 @@ class Xdf * \return the read data */ template T readBin(std::istream& is, T* obj = nullptr); + }; #endif // XDF_H