From 214b81e54a5969b61f95e2ca5c2e2c16ac6ae9cf Mon Sep 17 00:00:00 2001 From: Alberto Barradas Date: Fri, 26 Jun 2026 18:55:41 +0200 Subject: [PATCH] Read all channels of multi-channel string streams The [Samples] chunk handler reads strings one length-prefixed value per sample, but a string sample actually contains channel_count values, just like every numeric branch (which already loops `for v < channel_count`). For a string stream with more than one channel this reads only the first channel and leaves the remaining channels' bytes unconsumed, so the file cursor is stranded mid-chunk. libxdf then interprets those leftover bytes as the next chunk header, producing a garbage length and breaking the import (crash or corrupt read). Fix: loop over channel_count and read one length-prefixed string per channel, emplacing each into eventMap at the same time stamp. This both restores correct byte alignment and captures every channel's marker. Closes #19. This is the C++/libxdf manifestation of the same root cause we fixed in the Julia reader XDF.jl (cbrnr/XDF.jl#23): the string-reading path was missing the per-channel inner loop present in the numeric paths, so a two-channel string marker stream (e.g. the twochannel_string_marker.xdf example file with "Marker 0A"/"Marker 0B") was misread. pyxdf already handles this correctly; this brings libxdf to parity. Co-Authored-By: Claude Opus 4.8 (1M context) --- xdf.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/xdf.cpp b/xdf.cpp index 1891640..21e1dbb 100644 --- a/xdf.cpp +++ b/xdf.cpp @@ -409,14 +409,22 @@ int Xdf::load_xdf(std::string filename) 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; + //read one length-prefixed string per channel: a string + //sample contains channel_count values, just like the + //numeric branches above. Reading only a single string + //here leaves the remaining channels' bytes unconsumed, + //which desyncs the file cursor and breaks the next chunk. + for (int v = 0; v < streams[index].info.channel_count; ++v) + { + //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; } }