function [Output1,Output2] = Afun_OpenephysToNSx_v2(PlotOpenEphysRawData,OpenEphysExportToPlexon,SaveExtraFile)
% Read OpenEphys file and converted to NSx compatible format
% Output: 
   % Output1=NSx (Structure): suggest to load as NSx
   % Output2=EphysToMat (Structure): suggest to load as EphysToMat
   % Also save another file: OpenEphys¡Äexportdatexxxx.....mat (depends on Input3)
% input:
   % PlotOpenEphysRawData=0; % Plot OpenEphys Raw Data if ==1, (will take time)
   % OpenEphysExportToPlexon=0 ; % Save EphysOutputOFS if ==1 (for offline sorter)
   % SaveExtraFile=0;     % if ==1 save a mat file called "OpenEphys_dd-mmm-yyyy time.mat" in the local folder
% Function information: 
   % Openephys To Mat File Converter
   % V1: can input 'setting.xml' and read continuous data from channels except Analog/Digital inputs
   % V2: read all channels: CH, ADC and AUX (in this order)
   % Note: Channels: ChannelID: 1~128
   %       AnalogInput: ChannelID: 129~136
   %       AuxillaryInput(3D accelerometer): ChannelID: 901~903    
   % last update:2020/03/09
%% Temporary inputs to create function (removed these lines when turn to a function)
%close all;
%clear all;

%PlotOpenEphysRawData=0; % Plot OpenEphys Raw Data if ==1, (will take time)
%OpenEphysExportToPlexon=0; % Save EphysOutputOFS if ==1 (for offline sorter)
%% Step1: Browse OpenEphys '.xml' setting file
[settingsXmlName,settingpathName] = uigetfile({'*.xml','. XML File'},'select a settings file');
if isequal(settingsXmlName,0)
    warning('User selected Cancel');
else
ephysInfo = getEphysInfo(settingpathName);
FileNamesLegend={'Event','Spikes','Continuous','AnInput','Auxillary'};
FileNames=cell(5,1);
FileHeadersLegend={'Event headers','Spike headers','Continuous headers','AnInput headers','Aux headers'};
FileHeaders=cell(5,1);
% Events File
FileNames{1,1}=[settingpathName ephysInfo.events_file];
FileHeaders{1,1}=getFileHeader([settingpathName ephysInfo.events_file]);
% Spike Channels
FileNames{2,1}=cell(size(ephysInfo.electrodes,1),2);
FileHeaders{2,1}=cell(size(ephysInfo.electrodes,1),1);
for i=1:size(ephysInfo.electrodes,1)
    electrodeName = ephysInfo.electrodes{i,1};
    electrodeName(electrodeName==' ') = '';
    if ~isempty(strfind(electrodeName,'SingleElectrode')) 
        electrodeName=strrep(electrodeName,'SingleElectrode','SE');
    end
    if ~isempty(strfind(electrodeName,'Stereotrode'))
        electrodeName=strrep(electrodeName,'Stereotrode','ST');
    end
    if ~isempty(strfind(electrodeName,'Tetrode'))
        electrodeName=strrep(electrodeName,'Tetrode','TT');
    end
    SpikeFileName = [settingpathName electrodeName '.spikes'];
    FileNames{2,1}{i,1} = SpikeFileName;
    FileNames{2,1}{i,2} = ephysInfo.electrodes{i,2};
    FileHeaders{2,1}{i,1}=getFileHeader(SpikeFileName);
end
clear i electrodeName SpikeFileName;
% Continuous Channels
for i=1:size(ephysInfo.processors,1)
    if strcmp(ephysInfo.processors{i,2},'Rhythm FPGA')
        FileNames{3,1}=cell(size(ephysInfo.processors{i,3},2),2);
        FileHeaders{3,1}=cell(size(ephysInfo.processors{i,3},2),1);
        for j=1:size(ephysInfo.processors{i,3},2)
            ContinuousFileName = [settingpathName num2str(ephysInfo.processors{i,1}) '_CH' num2str(ephysInfo.processors{i,3}(1,j)) '.continuous'];
            FileNames{3,1}{j,1}=ContinuousFileName;
            FileNames{3,1}{j,2}=ephysInfo.processors{i,3}(1,j);
            FileHeaders{3,1}{j,1}=getFileHeader(ContinuousFileName);
        end
    end
end
% Analog Input Channels 
for i=1:size(ephysInfo.processors,1)
    if strcmp(ephysInfo.processors{i,2},'Rhythm FPGA')
        FileNames{4,1}=cell(size(ephysInfo.processors{i,4},2),2);
        FileHeaders{4,1}=cell(size(ephysInfo.processors{i,4},2),1);
        for j=1:size(ephysInfo.processors{i,4},2)
            AnInputFileName = [settingpathName num2str(ephysInfo.processors{i,1}) '_ADC' num2str(ephysInfo.processors{i,4}(1,j)) '.continuous'];
            FileNames{4,1}{j,1}=AnInputFileName;
            FileNames{4,1}{j,2}=ephysInfo.processors{i,4}(1,j);
            FileHeaders{4,1}{j,1}=getFileHeader(AnInputFileName);
        end
    end
end

% Auxillary Input Channels 
for i=1:size(ephysInfo.processors,1)
    if strcmp(ephysInfo.processors{i,2},'Rhythm FPGA')
        FileNames{5,1}=cell(size(ephysInfo.processors{i,5},2),2);
        FileHeaders{5,1}=cell(size(ephysInfo.processors{i,5},2),1);
        for j=1:size(ephysInfo.processors{i,5},2)
            AuxFileName = [settingpathName num2str(ephysInfo.processors{i,1}) '_AUX' num2str(ephysInfo.processors{i,5}(1,j)) '.continuous'];
            FileNames{5,1}{j,1}=AuxFileName;
            FileNames{5,1}{j,2}=ephysInfo.processors{i,5}(1,j);
            FileHeaders{5,1}{j,1}=getFileHeader(AuxFileName);
        end
    end
end

clear i j ContinuousFileName AnInputFileName AuxFileName;
% Save data
EphysToMat.XMLfilepath=settingpathName;
EphysToMat.XMLname=settingsXmlName;
EphysToMat.ephysInfo=ephysInfo;
EphysToMat.FileNamesLegend=FileNamesLegend';
EphysToMat.FileNames=FileNames;
EphysToMat.FileHeadersLegend=FileHeadersLegend';
EphysToMat.FileHeaders=FileHeaders;
end
%% Step2: Parse OpenEphys .continuous file
ConDataLegend={'Fullpath & FileName','Channel info','Timestamps','Samples'};
EphysToMat.ConDataLegend=ConDataLegend;
for i=1:1:size(EphysToMat.FileNames{3,1},1) % ch1~128
    %[data, timestamps, info]=load_open_ephys(EphysToMat.FileNames{3,1}{i,1}); % laod .continuous file
    [data, ~, info]=load_open_ephys(EphysToMat.FileNames{3,1}{i,1}); % remove timestamps to reduce size
    EphysToMat.ConData{i,1}=EphysToMat.FileNames{3,1}{i,1};
    EphysToMat.ConData{i,2}=info;
    %EphysToMat.ConData{i,3}=timestamps';
    %EphysToMat.ConData{i,4}=data';
    NSx.Data(i,:)=data';
    NSx.MetaTags.ChannelID(i,1)=EphysToMat.FileNames{3,1}{i,2};
    NSx.ElectrodesInfo(i).ElectrodeID=NSx.MetaTags.ChannelID(i,1);
    NSx.ElectrodesInfo(i).Label=EphysToMat.ConData{i, 2}.header.channel;

end
for i=1:1:size(EphysToMat.FileNames{4,1}) % analog channel
    %[data, timestamps, info]=load_open_ephys(EphysToMat.FileNames{4,1}{i,1});
    [data, ~, info]=load_open_ephys(EphysToMat.FileNames{4,1}{i,1}); % remove timestamps to reduce size
    itemnum=size(EphysToMat.ConData,1);
    EphysToMat.ConData{itemnum+1,1}=EphysToMat.FileNames{4,1}{i,1};
    EphysToMat.ConData{itemnum+1,2}=info;
    %EphysToMat.ConData{itemnum+1,3}=timestamps';
    %EphysToMat.ConData{itemnum+1,4}=data';
    NSx.Data(itemnum+1,:)=data';
    NSx.MetaTags.ChannelID(itemnum+1,1)=EphysToMat.FileNames{4,1}{i,2}+128;
    NSx.ElectrodesInfo(itemnum+1).ElectrodeID=NSx.MetaTags.ChannelID(itemnum+1,1);
    NSx.ElectrodesInfo(itemnum+1).Label=EphysToMat.ConData{itemnum+1, 2}.header.channel;
end
for i=1:1:size(EphysToMat.FileNames{5,1}) % auxillary channel
    %[data, timestamps, info]=load_open_ephys(EphysToMat.FileNames{5,1}{i,1});
    [data, ~, info]=load_open_ephys(EphysToMat.FileNames{5,1}{i,1});
    itemnum=size(EphysToMat.ConData,1);
    EphysToMat.ConData{itemnum+1,1}=EphysToMat.FileNames{5,1}{i,1};
    EphysToMat.ConData{itemnum+1,2}=info;
    %EphysToMat.ConData{itemnum+1,3}=timestamps';
    %EphysToMat.ConData{itemnum+1,4}=data';
    NSx.Data(itemnum+1,:)=data';
    NSx.MetaTags.ChannelID(itemnum+1,1)=EphysToMat.FileNames{5,1}{i,2}+900;
    NSx.ElectrodesInfo(itemnum+1).ElectrodeID=NSx.MetaTags.ChannelID(itemnum+1,1);
    NSx.ElectrodesInfo(itemnum+1).Label=EphysToMat.ConData{itemnum+1, 2}.header.channel;
end

% plot data
if PlotOpenEphysRawData==1
for i=1:1:size(EphysToMat.ConData,1)
    %------------------------------Stacked------------------------------%
    figure(1);
    subplot(size(EphysToMat.ConData,1),1,i);
    set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0 1 1]); %full screen
    set(gcf, 'Toolbar', 'none', 'Menu', 'none'); %hide toolbar
    %-----------------------------Seperated-----------------------------%
    %figure(i);
    %set(gcf, 'Position',  [200, 50, 1500, 500]);
    %-------------------------------------------------------------------%
    rawfig=plot(EphysToMat.ConData{i,3},EphysToMat.ConData{i,4});
    CM = jet(size(EphysToMat.ConData,1)); % Change line color
    set(rawfig,'Color',CM(i,:));
    xlabel('Time(sec)','FontWeight','bold','FontSize',10);
    ylabel('Amplitude(uV)','FontWeight','bold','FontSize',10);
    set(gca,'XGrid','off','YGrid','off','LineWidth',1.5,'FontWeight','bold','fontsize',10,'XMinorTick','off','YMinorTick','off');
    t1=title(['Continuous ',EphysToMat.ConData{i,2}.header.channel,'_SR',num2str(EphysToMat.ConData{i,2}.header.sampleRate),'_',EphysToMat.ConData{i,2}.header.date_created],'FontSize',12,'Interpreter','none');
    set(t1,'position',get(t1,'position')+[0 0.1 0]);
    box off;
end
end
%savefilename1=['EphysOuputRaw_',EphysToMat.ConData{1,2}.header.date_created];
%save(savefilename1,'EphysToMat');

%% Convert mat file to Plexon Offline Sorter readable file (.mat [channel] X [samples] array)
if OpenEphysExportToPlexon==1
OFS=NSx.Data;
savefilename2=['EphysOutputOFS_',EphysToMat.ConData{1,2}.header.date_created];
save(savefilename2,'OFS','-v7.3');
end
%% Convert mat file to Neuralynx .ncs file (for other sorting softwares)
%   Mat2NlxCSC(FileName, AppendToFileFlag, ExportMode, ExportModeVector,
%              FieldSelectionFlags, Timestamps, ChannelNumbers,
%              SampleFrequencies, NumberOfValidSamples, Samples, Header);
%   EXAMPLE: Mat2NlxCSC('test.ncs', 0, 1, 1, [1 1 1 1 1 1], Timestamps,
%                      ChannelNumbers, SampleFrequencies, NumberOfValidSamples,
%                      Samples, Header);
%t=1;
%for i=1:512:size(EphysToMat.timestamps,1)
%    Timestamps(1,t)=EphysToMat.timestamps(i,1)*1000;
%    t=t+1;
%end
%d=1;
%for i=1:512:size(EphysToMat.data,1)
%    Samples(1:512,d)=EphysToMat.data(i:i+511,1);
%    d=d+1;
%end
%Mat2NlxCSC('test.ncs', 0, 1, 1, [1 0 0 0 1 0], Timestamps, Samples);
%% Save file to NSx compatible format
NSx.MetaTags.Filename=['OpenEphys_',EphysToMat.ConData{1,2}.header.date_created];
NSx.MetaTags.FileExt=['.mat'];
NSx.MetaTags.FilePath=EphysToMat.XMLfilepath;
SourceFile=[NSx.MetaTags.Filename NSx.MetaTags.FileExt];
NSx.MetaTags.SamplingFreq=EphysToMat.FileHeaders{1, 1}.sampleRate;

if SaveExtraFile==1;
save(SourceFile,'EphysToMat','NSx','-v7.3');
end

Output1=NSx;
Output2=EphysToMat;
end
%% Read XML file
function info = getEphysInfo(directory)
% STEP 1: GET FULL PATH FOR DIRECTORY
directory = get_full_path(directory);

% STEP 2: PARSE THE 'SETTINGS.XML' FILE
directory_contents = dir(directory); % dir() list the contents in the folder
DOMnode = xmlread([directory filesep 'settings.xml']);
xRoot =DOMnode.getDocumentElement;

processors = cell(0);
electrodes = cell(0);

% LOOP THROUGH TOP-LEVEL NODES
for i = 1:xRoot.getChildNodes.getLength-1
    if strcmp(xRoot.item(i).getNodeName, 'SIGNALCHAIN')
        xSignalChain = xRoot.item(i);
        processorIndex = 0;
        % LOOP THROUGH PROCESSOR NODES
        for j = 1:xSignalChain.getChildNodes.getLength-1
            if strcmp(xSignalChain.item(j).getNodeName, 'PROCESSOR')
                processorIndex = processorIndex + 1;
                xProcessor = xSignalChain.item(j);
                nodeId = str2num(char(xProcessor.getAttributes.item(0).getValue));
                processorName = char(xProcessor.getAttributes.item(8).getValue);
                processors{processorIndex,1} = nodeId;
                processors{processorIndex,2} = processorName;
                processors{processorIndex,3} = []; % empty cell for channels
                processors{processorIndex,4} = []; % empty cell for AnIput channels
                processors{processorIndex,5} = []; % empty cell for Auxillary channels
                if ( strcmp(processorName, 'Filters/Spike Detector') || strcmp(processorName, 'Filters/Spike Sorter'))
                    if (strcmp(processorName, 'Filters/Spike Sorter'))
                        xProcessor = xProcessor.item(1);
                        nameAttrIdx = 3;
                    else
                        nameAttrIdx = 0;
                    end
                    electrodeIndex = 0;
                     % LOOP THROUGH ELECTRODE NODES IN SPIKE DETECTOR
                    for k = 1:xProcessor.getChildNodes.getLength-1
                        if strcmp(xProcessor.item(k).getNodeName, 'ELECTRODE')
                           electrodeIndex = electrodeIndex + 1;
                           xElectrode = xProcessor.item(k);
                           electrodeName = char(xElectrode.getAttributes.item(nameAttrIdx).getValue);
                           channels = [ ];
                           % LOOP THROUGH CHANNEL NODES FOR ELECTRODE
                           for m = 1:xElectrode.getChildNodes.getLength-1
                              if strcmp(xElectrode.item(m).getNodeName, 'SUBCHANNEL')
                                  xChannel = xElectrode.item(m);
                                  ch = str2num(char(xChannel.getAttributes.item(0).getValue));
                                  channels = [channels ch];
                              end
                           end
                           electrodes{electrodeIndex,1} = electrodeName;
                           electrodes{electrodeIndex,2} = channels;
                        end                
                    end
                end
            end
        end
    end
end

% STEP 3: FIND THE AVAILABLE FILES
for filenum = 1:length(directory_contents)
   if numel(strfind(directory_contents(filenum).name, '.continuous')) > 0
       fname = directory_contents(filenum).name;
      % disp(fname)
       underscore = strfind(fname, '_');
       chstr = strfind(fname, 'CH');
       anstr = strfind(fname, 'ADC');
       austr = strfind(fname, 'AUX');
       dot = strfind(fname, '.');
       nodeId = str2num(fname(1:underscore-1));
       chNum = str2num(fname(chstr+2:dot-1));
       anNum = str2num(fname(anstr+3:dot-1));
       auNum = str2num(fname(austr+3:dot-1));
      % disp(nodeId)
       for n = 1:size(processors,1)
           if nodeId == processors{n,1} % Ex. 'Rhythm FPGA' processor
               processors{n,3} = [processors{n,3} chNum]; % channels
               processors{n,4} = [processors{n,4} anNum]; % analog input channels
               processors{n,5} = [processors{n,5} auNum]; % auxillary channels
           end
       end
   elseif numel(strfind(directory_contents(filenum).name, '.spikes')) > 0
       % don't need to do anything here
   elseif ((numel(strfind(directory_contents(filenum).name, '.events')) > 0)...
       && ((numel(strfind(directory_contents(filenum).name, 'messages'))) <= 0))
       info.events_file = directory_contents(filenum).name;
   end
end

for n = 1:size(processors,1)
    processors{n,3} = sort(processors{n,3});
    processors{n,4} = sort(processors{n,4});
    processors{n,5} = sort(processors{n,5});
end
info.electrodes = electrodes;
info.processors = processors;
end
%% Get full path
function output_path = get_full_path(input_path)
% 
% If a truncated path is given (e.g., ~/Folder/file), this function
% returns the full path (e.g., /home/username/Folder/file).
%
% It should be cross-platform compatible.
%
if strcmp(input_path(1),'~') % need full path for 'xmlread()' to work properly
    if ispc
        userDir = winqueryreg('HKEY_CURRENT_USER',...
            ['Software\Microsoft\Windows\CurrentVersion\' ...
             'Explorer\Shell Folders'],'Personal');
    else
        userDir = char(java.lang.System.getProperty('user.home'));
    end
    output_path = [userDir input_path(2:end)];
else
    output_path = input_path;
end
end
%% Get file header
function Header=getFileHeader(FileName)
    fid = fopen(FileName);
    hdr = fread(fid, 1024, 'char*1');
    eval(char(hdr'));
    Header=header;
end
%% Load OpenEphys Data (.continuous .spike .event)
function [data, timestamps, info] = load_open_ephys(filename, varargin)
%
% [data, timestamps, info] = load_open_ephys_data(filename, [outputFormat])
%
%   Loads continuous, event, or spike data files into Matlab.
%
%   Inputs:
%
%     filename: path to file
%     outputFormat: (optional) If omitted, continuous data is output in double format and is scaled to reflect microvolts. 
%                   If this argument is 'unscaledInt16' and the file contains continuous data, the output data will be in
%                   int16 format and will not be scaled; this data must be manually converted to a floating-point format
%                   and multiplied by info.header.bitVolts to obtain microvolt values. This feature is intended to save memory
%                   for operations involving large amounts of data.
%
%
%   Outputs:
%
%     data: either an array continuous samples (in microvolts unless outputFormat is specified, see above),
%           a matrix of spike waveforms (in microvolts),
%           or an array of event channels (integers)
%
%     timestamps: in seconds
%
%     info: structure with header and other information
%
%
%
%   DISCLAIMER:
%
%   Both the Open Ephys data format and this m-file are works in progress.
%   There's no guarantee that they will preserve the integrity of your
%   data. They will both be updated rather frequently, so try to use the
%   most recent version of this file, if possible.
%
%

%
%     ------------------------------------------------------------------
%
%     Copyright (C) 2014 Open Ephys
%
%     ------------------------------------------------------------------
%
%     This program is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
%     This program is distributed in the hope that it will be useful,
%     but WITHOUT ANY WARRANTY; without even the implied warranty of
%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%     GNU General Public License for more details.
%
%     <http://www.gnu.org/licenses/>.
%

[~,~,filetype] = fileparts(filename);
if ~any(strcmp(filetype,{'.events','.continuous','.spikes'}))
    error('File extension not recognized. Please use a ''.continuous'', ''.spikes'', or ''.events'' file.');
end

bInt16Out = false;
if nargin > 2
    error('Too many input arguments.');
elseif nargin == 2
    if strcmpi(varargin{1}, 'unscaledInt16')
        bInt16Out = true;
    else
        error('Unrecognized output format.');
    end
end

fid = fopen(filename);
fseek(fid,0,'eof');
filesize = ftell(fid);

NUM_HEADER_BYTES = 1024;
fseek(fid,0,'bof');
hdr = fread(fid, NUM_HEADER_BYTES, 'char*1');
info = getHeader(hdr);
if isfield(info.header, 'version')
    version = info.header.version;
else
    version = 0.0;
end

switch filetype
    case '.events'
        bStr = {'timestamps' 'sampleNum' 'eventType' 'nodeId' 'eventId' 'data' 'recNum'};
        bTypes = {'int64' 'uint16' 'uint8' 'uint8' 'uint8' 'uint8' 'uint16'};      
        bRepeat = {1 1 1 1 1 1 1};
        dblock = struct('Repeat',bRepeat,'Types', bTypes,'Str',bStr);
        if version < 0.2, dblock(7) = [];  end
        if version < 0.1, dblock(1).Types = 'uint64'; end
    case '.continuous'
        SAMPLES_PER_RECORD = 1024;
        bStr = {'ts' 'nsamples' 'recNum' 'data' 'recordMarker'};
        bTypes = {'int64' 'uint16' 'uint16' 'int16' 'uint8'};
        bRepeat = {1 1 1 SAMPLES_PER_RECORD 10};
        dblock = struct('Repeat',bRepeat,'Types', bTypes,'Str',bStr);
        if version < 0.2, dblock(3) = []; end
        if version < 0.1, dblock(1).Types = 'uint64'; dblock(2).Types = 'int16'; end
    case '.spikes'
        num_channels = info.header.num_channels;
        num_samples = 40; 
        bStr = {'eventType' 'timestamps' 'timestamps_software' 'source' 'nChannels' 'nSamples' 'sortedId' 'electrodeID' 'channel' 'color' 'pcProj' 'samplingFrequencyHz' 'data' 'gain' 'threshold' 'recordingNumber'};
        bTypes = {'uint8' 'int64' 'int64' 'uint16' 'uint16' 'uint16' 'uint16' 'uint16' 'uint16' 'uint8' 'float32' 'uint16' 'uint16' 'float32' 'uint16' 'uint16'};
        bRepeat = {1 1 1 1 1 1 1 1 1 3 2 1 num_channels*num_samples num_channels num_channels 1};
        dblock = struct('Repeat',bRepeat,'Types', bTypes,'Str',bStr);
        if version < 0.4,  dblock(7:12) = []; dblock(8).Types = 'uint16'; end
        if version == 0.3, dblock = [dblock(1), struct('Repeat',1,'Types','uint32','Str','ts'), dblock(2:end)]; end
        if version < 0.3, dblock(2) = []; end
        if version < 0.2, dblock(9) = []; end
        if version < 0.1, dblock(2).Types = 'uint64'; end
end
blockBytes = str2double(regexp({dblock.Types},'\d{1,2}$','match', 'once')) ./8 .* cell2mat({dblock.Repeat});
numIdx = floor((filesize - NUM_HEADER_BYTES)/sum(blockBytes));

switch filetype
    case '.events'
        timestamps = segRead('timestamps')./info.header.sampleRate;
        info.sampleNum = segRead('sampleNum');
        info.eventType = segRead('eventType');
        info.nodeId = segRead('nodeId');
        info.eventId = segRead('eventId');
        data = segRead('data');
        if version >= 0.2, info.recNum = segRead('recNum'); end
    case '.continuous'
        if nargout>1
            info.ts = segRead('ts');
        end            
        info.nsamples = segRead('nsamples');
        if ~all(info.nsamples == SAMPLES_PER_RECORD)&& version >= 0.1, error('Found corrupted record'); end
        if version >= 0.2, info.recNum = segRead('recNum'); end
        
        % read in continuous data
        if bInt16Out
            data = segRead_int16('data', 'b');
        else
            data = segRead('data', 'b') .* info.header.bitVolts;
        end
        
        if nargout>1 % do not create timestamp arrays unless they are requested
            timestamps = nan(size(data));
            current_sample = 0;
            for record = 1:length(info.ts)
                timestamps(current_sample+1:current_sample+info.nsamples(record)) = info.ts(record):info.ts(record)+info.nsamples(record)-1;
                current_sample = current_sample + info.nsamples(record);
            end
            timestamps = timestamps./info.header.sampleRate;
        end
    case '.spikes'
        timestamps = segRead('timestamps')./info.header.sampleRate;
        info.source = segRead('source');
        info.samplenum = segRead('nSamples');
        info.gain = permute(reshape(segRead('gain'), num_channels, numIdx), [2 1]);
        info.thresh = permute(reshape(segRead('threshold'), num_channels, numIdx), [2 1]);
        if version >= 0.4, info.sortedId = segRead('sortedId'); end
        if version >= 0.2, info.recNum = segRead('recordingNumber'); end
        data = permute(reshape(segRead('data'), num_samples, num_channels, numIdx), [3 1 2]);
        data = (data-32768)./ permute(repmat(info.gain/1000,[1 1 num_samples]), [1 3 2]);
end
fclose(fid);
function seg = segRead_int16(segName, mf)
    %% This function is specifically for reading continuous data. 
    %  It keeps the data in int16 precision, which can drastically decrease
    %  memory consumption
    if nargin == 1, mf = 'l'; end
    segNum = find(strcmp({dblock.Str},segName));
    fseek(fid, sum(blockBytes(1:segNum-1))+NUM_HEADER_BYTES, 'bof'); 
    seg = fread(fid, numIdx*dblock(segNum).Repeat, [sprintf('%d*%s', ...
        dblock(segNum).Repeat,dblock(segNum).Types) '=>int16'], sum(blockBytes) - blockBytes(segNum), mf);
end
function seg = segRead(segName, mf)
    if nargin == 1, mf = 'l'; end
    segNum = find(strcmp({dblock.Str},segName));
    fseek(fid, sum(blockBytes(1:segNum-1))+NUM_HEADER_BYTES, 'bof'); 
    seg = fread(fid, numIdx*dblock(segNum).Repeat, sprintf('%d*%s', ...
        dblock(segNum).Repeat,dblock(segNum).Types), sum(blockBytes) - blockBytes(segNum), mf);
end
end
function info = getHeader(hdr)
eval(char(hdr'));
info.header = header;
end