Skip to content

Commit c0e0ae7

Browse files
committed
Merge changes from BCILAB: *handle chopped-off clockoffset chunk; *handle 0-length chunks; *handle chopped sample chunk
1 parent ca9ed26 commit c0e0ae7

File tree

1 file changed

+39
-21
lines changed

1 file changed

+39
-21
lines changed

load_xdf.m

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
% Import an XDF file.
44
% [Streams,FileHeader] = load_xdf(Filename, Options...)
55
%
6-
% This is a MATLAB importer for mult-stream XDF (Extensible Data Format) recordings. All
6+
% This is a MATLAB importer for multi-stream XDF (Extensible Data Format) recordings. All
77
% information covered by the XDF 1.0 specification is imported, plus any additional meta-data
88
% associated with streams or with the container file itself.
99
%
@@ -231,6 +231,7 @@
231231
f = fopen(filename,'r','ieee-le.l64'); % file handle
232232
closer = onCleanup(@()close_file(f,filename)); % object that closes the file when the function exits
233233

234+
filesize = getfield(dir(filename),'bytes');
234235

235236
% there is a fast C mex file for the inner loop, but it's
236237
% not necessarily available for every platform
@@ -264,9 +265,9 @@
264265
end
265266

266267

267-
% ======================
268-
% === parse the file ===
269-
% ======================
268+
% ======================================================
269+
% === parse the file ([SomeText] refers to XDF Spec) ===
270+
% ======================================================
270271

271272
% read [MagicCode]
272273
if ~strcmp(fread(f,4,'*char')','XDF:')
@@ -279,9 +280,21 @@
279280
% read [NumLengthBytes], [Length]
280281
len = double(read_varlen_int(f));
281282
if ~len
282-
break; end
283+
if ftell(f) < filesize-1024
284+
fprintf(' got zero-length chunk, scanning forward to next boundary chunk.\n');
285+
scan_forward(f);
286+
continue;
287+
else
288+
if opts.Verbose
289+
fprintf(' reached end of file.\n'); end
290+
break;
291+
end
292+
end
283293
% read [Tag]
284-
switch fread(f,1,'uint16')
294+
tag = fread(f,1,'uint16');
295+
if opts.Verbose
296+
fprintf(' read tag: %i at %d bytes, length=%d\n',tag,ftell(f),len); end
297+
switch tag
285298
case 3 % read [Samples] chunk
286299
try
287300
% read [StreamId]
@@ -328,9 +341,9 @@
328341
temp(id).time_stamps{end+1} = timestamps;
329342
catch e
330343
% an error occurred (perhaps a chopped-off file): emit a warning
331-
% and return the file up to this point
332-
warning(e.identifier, '%s', e.message);
333-
break;
344+
% and scan forward to the next recognized chunk.
345+
fprintf(' got error "%s" (%s), scanning forward to next boundary chunk.\n',e.identifier,e.message);
346+
scan_forward(f);
334347
end
335348
case 2 % read [StreamHeader] chunk
336349
% read [StreamId]
@@ -372,14 +385,23 @@
372385
case 1 % read [FileHeader] chunk
373386
fileheader = parse_xml_struct(fread(f,len-2,'*char')');
374387
case 4 % read [ClockOffset] chunk
375-
% read [StreamId]
376-
id = idmap(fread(f,1,'uint32'));
377-
% read [CollectionTime]
378-
temp(id).clock_times(end+1) = fread(f,1,'double');
379-
% read [OffsetValue]
380-
temp(id).clock_values(end+1) = fread(f,1,'double');
388+
try
389+
% read [StreamId]
390+
id = idmap(fread(f,1,'uint32'));
391+
% read [CollectionTime]
392+
temp(id).clock_times(end+1) = fread(f,1,'double');
393+
% read [OffsetValue]
394+
temp(id).clock_values(end+1) = fread(f,1,'double');
395+
catch e
396+
% an error occurred (perhaps a chopped-off file): emit a
397+
% warning and scan forward to the next recognized chunk
398+
fprintf(' got error "%s" (%s), scanning forward to next boundary chunk.\n',e.identifier,e.message);
399+
scan_forward(f);
400+
end
401+
case 5 % read [Boundary] chunk
402+
fread(f, len-2, '*uint8');
381403
otherwise
382-
% skip other chunk types (Boundary, ...)
404+
% skip other chunk types
383405
fread(f,len-2,'*uint8');
384406
end
385407
end
@@ -513,13 +535,9 @@
513535
% delays)
514536
if opts.Verbose
515537
disp(' performing jitter removal...'); end
516-
517-
518538
for k=1:length(temp)
519-
520539
if ~isempty(temp(k).time_stamps) && temp(k).srate
521540

522-
523541
if isfield(streams{k}.info.desc, 'synchronization') && ...
524542
isfield(streams{k}.info.desc.synchronization, 'can_drop_samples') && ...
525543
strcmp(streams{k}.info.desc.synchronization.can_drop_samples, 'true')
@@ -575,7 +593,7 @@
575593
if ~isempty(temp(k).time_stamps)
576594
temp(k).effective_srate = (length(temp(k).time_stamps) - 1) / (temp(k).time_stamps(end) - temp(k).time_stamps(1));
577595
else
578-
temp(k).effective_srate = 0;
596+
temp(k).effective_srate = 0; % BCILAB sets this to NaN
579597
end
580598
% transfer the information into the output structs
581599
streams{k}.info.effective_srate = temp(k).effective_srate;

0 commit comments

Comments
 (0)