function features = xml_parse_mtt(file)
% features = xml_parse_mtt(file)
% 
% parses the magnatagatune xml file to retrieve the audio
% analysis results. The file isexpected to use the deprecated 
% EchoNest XML format
%
% time always travels an the horizontal axis

% ---
% TODO: Add Error checking to certan tags as chroma number, 
%  loudness max etc.
% ---

fdata = fileread(file);
try
    % ---
    % TODO: check for other than the p-code exceptions
    % ---
    
    data = xml_parseany(fdata);
catch exception
end
%% ------------------------------------------------------------------------
% ---
% copy general track data
% ---

fields = fieldnames(data.track{1}.ATTRIBUTE);
for i = 1:length(fields)
    features.(fields{i}) = str2double(data.track{1}.ATTRIBUTE.(fields{i}));
end

%% ------------------------------------------------------------------------
% ---
% get harmonic analysis. this is stored in segments
% ---
dsegments = data.track{1}.segments{1}.segment;

for i = 1:numel(dsegments)
    
    % get start and duration
    segments(i).start = str2double(dsegments{i}.ATTRIBUTE.start);
    segments(i).duration = str2double(dsegments{i}.ATTRIBUTE.duration);
    
    % ---
    % NOTE: for the chroma and mfcc features, we assume that the classes
    %  are always saved and parsed in correct order, thus we can afford to 
    %  refrain from saving the class number with the class
    % ---
    
    % assemble chroma features
    segments(i).pitches = zeros(12,1);
    for j = 1:12
        
        segments(i).pitches(j) = str2double(...
            dsegments{i}.pitches{1}.pitch{j}.CONTENT);
    end
    
   % assemble mfcc features;
    segments(i).timbre = zeros(numel(dsegments{i}.timbre{1}.coeff),1);
    for j = 1:numel(dsegments{i}.timbre{1}.coeff)
        
        segments(i).timbre(j) = str2double(...
            dsegments{i}.timbre{1}.coeff{j}.CONTENT);
    end
    
    % get loudness measurements in dB and time
    segments(i).loudness = str2double(...
        dsegments{i}.loudness{1}.dB{1}.CONTENT);
    
    segments(i).loudness_time = str2double(...
        dsegments{i}.loudness{1}.dB{1}.ATTRIBUTE.time);
    
    segments(i).loudness_max = str2double(...
        dsegments{i}.loudness{1}.dB{2}.CONTENT);
    
    segments(i).loudness_max_time = str2double(...
        dsegments{i}.loudness{1}.dB{2}.ATTRIBUTE.time);
end

features.segments = segments;

%% ------------------------------------------------------------------------
% ---
% get sections
% ---
dsections = data.track{1}.sections{1}.section;

secstart = zeros(1,numel(dsections));
secduration = zeros(1,numel(dsections));
for i = 1:numel(dsections)
    sections(i).start = str2double(dsections{i}.ATTRIBUTE.start);
    sections(i).duration = str2double(dsections{i}.ATTRIBUTE.duration);
end

features.sections = sections;

%% ------------------------------------------------------------------------
% ---
% get beat and rythm data. the metric data is structured 
% hierarchically, as each bar contains several beats,
% which contaisn several tatums. 
% NOTE: Although the metrum and tempo have been evaluated and fixed
% on a global scale, the number of bars and tatum vary greatly.
% ---
dbars = data.track{1}.meter{1}.bar;

for i = 1:numel(dbars)
    
    % get bar information
    bars(i).confidence = str2double(dbars{i}.ATTRIBUTE.conf);
    for j = 1:numel(dbars{i}.beat)
        
        % get beat information
        bars(i).beat(j).confidence = str2double(dbars{i}.beat{j}.ATTRIBUTE.conf);
        
        for k = 1:numel(dbars{i}.beat{j}.tatum)
            
            % get tatum information
            if ~isempty(dbars{i}.beat{j}.tatum{k}.ATTRIBUTE)
                bars(i).beat(j).tatum(k).time = str2double(dbars{i}.beat{j}.tatum{k}.CONTENT);
                bars(i).beat(j).tatum(k).confidence = str2double(dbars{i}.beat{j}.tatum{k}.ATTRIBUTE.conf);
            
            else 
                % save empty struct
                bars(i).beat(j).tatum = struct([]);
            end
            
        end
    end
end

features.bars = bars;

