Skip to content

Commit

Permalink
added and corrected functions to write data in BrainVision format
Browse files Browse the repository at this point in the history
  • Loading branch information
BenjaminBlankertz committed Feb 14, 2014
1 parent 34d3a63 commit ddb882d
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 42 deletions.
7 changes: 5 additions & 2 deletions fileio/file_readBV.m
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,15 @@
end

fileNames = cell(1,length(file));
fileTitle = cell(1,length(file));
% use BTB.RawDir as default dir
for filePos = 1:length(file)
if fileutil_isAbsolutePath(file{filePos}),
fileNames{filePos}= file{filePos};
[dmy, fileTitle{filePos}]= fileparts(file{filePos});
else
fileNames{filePos} = fullfile(BTB.RawDir, file{filePos});
fileTitle{filePos}= file{filePos};
end
end

Expand Down Expand Up @@ -211,15 +214,15 @@
firstFileToRead = 1;
firstFileSkip = 0;
lastFileToRead = length(fileNames);
lastFileLength=inf;
lastFileLength = inf;


% check if we want to load the raw data
if isequal(opt.Fs, 'raw'),
opt.Fs= raw_fs;
end
cnt.fs= opt.Fs;
cnt.title = str_vec2str(file);
cnt.title = str_vec2str(fileTitle);
cnt.file = str_vec2str(fileNames);

nChans= length(cnt.clab);
Expand Down
103 changes: 103 additions & 0 deletions fileio/file_writeBV.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
function props= file_writeBV(file, dat, mrk, varargin)
% FILE_WRITEBV - Write EEG file in BrainVision format
%
% Synopsis:
% file_writeBV(FILE, DAT, MAK, <OPT>)
%
% Arguments:
% FILE: CHAR name of the output file
% DAT: STRUCT of continuous or epoch signals
% MRK: STRUCT for markers
% OPT: PROPLIST - Structure or property/value list of optional properties:
% 'Scale': scaling factor used in the generic data format to bring
% data from the int16 range (-32768 - 32767) to uV.
% That means before saving signals are divided by
% this factor.
% Individual scaling factors may be specified for each
% channel in a vector, or a global scaling as scalar;
% default is 'auto'.
% 'WriteMrk': whether to write a marker file or not, default 1.
% 'WriteHdr': whether to write a header file or not, default 1.
% 'Folder': directory to which files are written


global BTB

opt= opt_proplistToStruct(varargin{:});
props= {'WriteMrk' 1 'BOOL'
'WriteHdr' 1 'BOOL'
};
props_writeBVheader= file_writeBVheader;
if nargin==0,
props_writeBVmarkers= file_writeBVmarkers;
props= opt_catProps(props, props_writeBVheader, props_writeBVmarkers);
return;
end

props= opt_importProps(props, props_writeBVheader, ...
{'Scale','Folder','Precision'});
[opt, isdefault]= opt_setDefaults(opt, props, 1);
clear props

if fileutil_isAbsolutePath(file),
fullName= file;
else
fullName= fullfile(opt.Folder, file);
end

[T, nChans, nEpochs]= size(dat.x);
if nEpochs>1,
cntX= permute(dat.x, [2 1 3]);
cntX= reshape(cntX, [nChans T*nEpochs]);
if ~exist('mrk','var'),
nEpochs= floor(size(dat.x,1)/T);
mrk= [];
mrk.time= (1:nEpochs)*T/cnt.fs*1000;;
mrk.y= ones(1,nEpochs);
mrk.className= {'epoch'};
end
else
nEpochs= 0;
cntX= dat.x';
end

if isequal(opt.Scale, 'auto'),
if strcmpi(opt.Precision(1:3), 'int'),
range= double([intmin(opt.Precision) intmax(opt.Precision)]);
else
range= [-1 1]*realmax(opt.Precision);
end
opt.Scale= (max(abs(cntX)+0.0001,[],2))/min(abs(range));
end

if length(opt.Scale)==1,
opt.Scale= opt.Scale*ones(nChans,1);
end
cntX= diag(1./opt.Scale)*cntX;
if any(cntX(:)<range(1) | cntX(:)>range(2)),
warning('data clipped: use other scaling');
end

subdir= fileparts(fullName);
if ~exist(subdir, 'dir'),
parentdir= fileparts(subdir);
if ~exist(parentdir, 'dir'),
error('parent folder of %s not existing', subdir);
end
mkdir(subdir);
end
fid= fopen([fullName '.eeg'], 'wb');
if fid==-1, error(sprintf('cannot write to %s.eeg', fullName)); end
fwrite(fid, cntX, opt.Precision);
fclose(fid);

if opt.WriteHdr,
opt_hdr= struct_copyFields(dat, {'fs','clab'});
opt_hdr= struct_copyFields(opt_hdr, opt, {'Scale', 'Precision'});
opt_hdr.DataPoints= size(cntX,2);
file_writeBVheader(fullName, opt_hdr);
end

if opt.WriteMrk,
file_writeBVmarkers(fullName, mrk, 'Fs',dat.fs);
end
32 changes: 24 additions & 8 deletions fileio/file_writeBVheader.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function file_writeBVheader(file, varargin)
function props= file_writeBVheader(file, varargin)
% FILE_WRITEBVHEADER - Write Header in BrainVision Format
%
% Synopsis:
Expand All @@ -22,9 +22,25 @@ function file_writeBVheader(file, varargin)

global BTB

props= {'Folder' BTB.TmpDir 'CHAR'};
props= {'Folder' BTB.TmpDir 'CHAR'
'Precision' 'int16' 'CHAR(int16 int32 single double)'
'DataFile' '' 'CHAR'
'DataPoints' [] 'CHAR|DOUBLE'
'MarkerFile' '' 'CHAR'
'Impedances' [] 'DOUBLE[-]'
'Fs' [] '!DOUBLE[1]'
'CLab' '' '!CELL(CHAR)'
'Scale' 'auto' 'CHAR|DOUBLE[-]'
};
if nargin==0,
return;
end

misc_checkType(file, 'CHAR');
misc_checkType(varargin, 'PROPLIST');

opt= opt_proplistToStruct(varargin{:});
opt= opt_setDefaults(opt, props);
[opt, isdefault]= opt_setDefaults(opt, props, 1);

if fileutil_isAbsolutePath(file),
fullName= file;
Expand All @@ -33,11 +49,11 @@ function file_writeBVheader(file, varargin)
end

[pathstr, fileName]= fileparts(fullName);
props= {'Precision' 'int16' 'CHAR(int16 int32 single double float32 float64)'
'DataFile' fileName 'CHAR'
'MarkerFile' fileName 'CHAR'
'Impedances' [] 'DOUBLE'};
opt= opt_setDefaults(opt, props);
proplist= {'DataFile' fileName
'MarkerFile' fileName
};
opt= opt_overrideIfDefault(opt, isdefault, proplist);
clear props

if ~ischar(opt.DataPoints), %% not sure, why DataPoints is a string
opt.DataPoints= sprintf('%d', opt.DataPoints);
Expand Down
86 changes: 54 additions & 32 deletions fileio/file_writeBVmarkers.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function file_writeBVmarkers(file, mrk, varargin)
function props= file_writeBVmarkers(file, mrk, varargin)
% FILE_WRITEBVMARKERS - Write Markers in BrainVision Format
%
% Synopsis:
Expand All @@ -9,16 +9,36 @@ function file_writeBVmarkers(file, mrk, varargin)
% MRK: marker structure.
%
% Properties:
% 'Folder': in which files are saved, if FILE is not an absolute path
% 'UseClassLabels': the class labels (given in mrk.y) are taken as markers
% this is default, only if there is no field mrk.event.desc
% 'DataFile': name of the corresponding .eeg file (default FILE)
%
% See also: eegfile_*
%

global BTB

if nargin<2,
mrk= struct('time',[], 'y',[], 'event',struct);
end

default_UseClassLabels= 1;
if isfield(mrk,'event') && isfield(mrk.event,'desc'),
default_UseClassLabels= 0;
end

props= {'Fs' [] '!DOUBLE'
'Folder' BTB.TmpDir 'CHAR'
'UseClassLabels' default_UseClassLabels '!BOOL'
'DataFile' '' 'CHAR'
};
if nargin==0,
return
end

opt= opt_proplistToStruct(varargin{:});
props= {'Folder' BTB.TmpDir 'CHAR'};
opt= opt_setDefaults(opt, props);
[opt, isdefault]= opt_setDefaults(opt, props, 1);

if fileutil_isAbsolutePath(file),
fullName= file;
Expand All @@ -27,8 +47,9 @@ function file_writeBVmarkers(file, mrk, varargin)
end

[pathstr, fileName]= fileparts(fullName);
props= {'DataFile', fileName};
opt= opt_setDefaults(opt, props);
opt= opt_overrideIfDefault(opt, isdefault, 'DataFile', fileName);
% avoid output argument in this case
clear props

fid= fopen([fullName '.vmrk'], 'w');
if fid==-1, error(sprintf('cannot write to %s.vmrk', fullName)); end
Expand All @@ -37,36 +58,37 @@ function file_writeBVmarkers(file, mrk, varargin)
fprintf(fid, [13 10 '[Common Infos]' 13 10]);
fprintf(fid, ['DataFile=%s.eeg' 13 10], opt.DataFile);
fprintf(fid, [13 10 '[Marker Infos]' 13 10]);
if exist('mrk', 'var') & ~isempty(mrk),
if ~isfield(mrk,'type'),
%% simple case, mrk is a usual (bbci) marker struct
fprintf(fid, ['Mk1=New Segment,,1,1,0,00000000000000000000' 13 10]);
for ie= 1:length(mrk.pos),
if isfield(mrk, 'toe'),
mt= mrk.toe(ie);
else
[d, mt]= max(mrk.y(:,ie));
end
if mrk.toe(ie)>0,
fprintf(fid, ['Mk%d=Stimulus,S%3d,%d,1,0' 13 10], ie+1, mt, ...
mrk.pos(ie));
pos= round(mrk.time/1000*opt.Fs);
if opt.UseClassLabels,
[dmy, toe]= max(mrk.y);
desc= cprintf('S%3d', toe);
else
if iscell(mrk.event.desc),
desc= mrk.event.desc;
else
toe= mrk.event.desc;
desc= cell(length(toe), 1);
for k= 1:numel(desc),
if toe(k)>=0,
desc{k}= sprintf('S%3d', toe(k));
else
fprintf(fid, ['Mk%d=Response,R%3d,%d,1,0' 13 10], ie+1, abs(mt), ...
mrk.pos(ie));
desc{k}= sprintf('R%3d', toe(k));
end
end
elseif length(mrk)==1
for im = 1:length(mrk.type)
fprintf(fid, ['Mk%d=%s,%s,%u,%u,%u,%s' 13 10], im, ...
mrk.type{im}, mrk.event.desc{im}, mrk.pos(im), ...
mrk.length(im), mrk.chan(im), mrk.time{im});
end
else
for im= 1:length(mrk),
fprintf(fid, ['Mk%d=%s,%s,%u,%u,%u,%s' 13 10], im, ...
mrk(im).type, mrk(im).event.desc, mrk(im).pos, ...
mrk(im).length, mrk(im).chan, mrk(im).time);
end
end
end
if ~(isfield(mrk.event,'type') && isfield(mrk.event,'clock')),
% simple case, mrk is a usual (bbci) marker struct
fprintf(fid, ['Mk1=New Segment,,1,1,0,00000000000000000000' 13 10]);
for ie= 1:length(pos),
fprintf(fid, ['Mk%d=Stimulus,%s,%d,1,0' 13 10], ie+1, desc(ie), ...
pos(ie));
end
else
for im= 1:length(mrk.event.type)
fprintf(fid, ['Mk%d=%s,%s,%u,%u,%u,%s' 13 10], im, ...
mrk.event.type{im}, desc{im}, pos(im), ...
mrk.event.length(im), mrk.event.chan(im), mrk.event.clock{im});
end
end
fclose(fid);

0 comments on commit ddb882d

Please sign in to comment.