wolffd@0
|
1 function features = xml_parse_mtt(file)
|
wolffd@0
|
2 % features = xml_parse_mtt(file)
|
wolffd@0
|
3 %
|
wolffd@0
|
4 % parses the magnatagatune xml file to retrieve the audio
|
wolffd@0
|
5 % analysis results. The file isexpected to use the deprecated
|
wolffd@0
|
6 % EchoNest XML format
|
wolffd@0
|
7 %
|
wolffd@0
|
8 % time always travels an the horizontal axis
|
wolffd@0
|
9
|
wolffd@0
|
10 % ---
|
wolffd@0
|
11 % TODO: Add Error checking to certan tags as chroma number,
|
wolffd@0
|
12 % loudness max etc.
|
wolffd@0
|
13 % ---
|
wolffd@0
|
14
|
wolffd@0
|
15 fdata = fileread(file);
|
wolffd@0
|
16 try
|
wolffd@0
|
17 % ---
|
wolffd@0
|
18 % TODO: check for other than the p-code exceptions
|
wolffd@0
|
19 % ---
|
wolffd@0
|
20
|
wolffd@0
|
21 data = xml_parseany(fdata);
|
wolffd@0
|
22 catch exception
|
wolffd@0
|
23 end
|
wolffd@0
|
24 %% ------------------------------------------------------------------------
|
wolffd@0
|
25 % ---
|
wolffd@0
|
26 % copy general track data
|
wolffd@0
|
27 % ---
|
wolffd@0
|
28
|
wolffd@0
|
29 fields = fieldnames(data.track{1}.ATTRIBUTE);
|
wolffd@0
|
30 for i = 1:length(fields)
|
wolffd@0
|
31 features.(fields{i}) = str2double(data.track{1}.ATTRIBUTE.(fields{i}));
|
wolffd@0
|
32 end
|
wolffd@0
|
33
|
wolffd@0
|
34 %% ------------------------------------------------------------------------
|
wolffd@0
|
35 % ---
|
wolffd@0
|
36 % get harmonic analysis. this is stored in segments
|
wolffd@0
|
37 % ---
|
wolffd@0
|
38 dsegments = data.track{1}.segments{1}.segment;
|
wolffd@0
|
39
|
wolffd@0
|
40 for i = 1:numel(dsegments)
|
wolffd@0
|
41
|
wolffd@0
|
42 % get start and duration
|
wolffd@0
|
43 segments(i).start = str2double(dsegments{i}.ATTRIBUTE.start);
|
wolffd@0
|
44 segments(i).duration = str2double(dsegments{i}.ATTRIBUTE.duration);
|
wolffd@0
|
45
|
wolffd@0
|
46 % ---
|
wolffd@0
|
47 % NOTE: for the chroma and mfcc features, we assume that the classes
|
wolffd@0
|
48 % are always saved and parsed in correct order, thus we can afford to
|
wolffd@0
|
49 % refrain from saving the class number with the class
|
wolffd@0
|
50 % ---
|
wolffd@0
|
51
|
wolffd@0
|
52 % assemble chroma features
|
wolffd@0
|
53 segments(i).pitches = zeros(12,1);
|
wolffd@0
|
54 for j = 1:12
|
wolffd@0
|
55
|
wolffd@0
|
56 segments(i).pitches(j) = str2double(...
|
wolffd@0
|
57 dsegments{i}.pitches{1}.pitch{j}.CONTENT);
|
wolffd@0
|
58 end
|
wolffd@0
|
59
|
wolffd@0
|
60 % assemble mfcc features;
|
wolffd@0
|
61 segments(i).timbre = zeros(numel(dsegments{i}.timbre{1}.coeff),1);
|
wolffd@0
|
62 for j = 1:numel(dsegments{i}.timbre{1}.coeff)
|
wolffd@0
|
63
|
wolffd@0
|
64 segments(i).timbre(j) = str2double(...
|
wolffd@0
|
65 dsegments{i}.timbre{1}.coeff{j}.CONTENT);
|
wolffd@0
|
66 end
|
wolffd@0
|
67
|
wolffd@0
|
68 % get loudness measurements in dB and time
|
wolffd@0
|
69 segments(i).loudness = str2double(...
|
wolffd@0
|
70 dsegments{i}.loudness{1}.dB{1}.CONTENT);
|
wolffd@0
|
71
|
wolffd@0
|
72 segments(i).loudness_time = str2double(...
|
wolffd@0
|
73 dsegments{i}.loudness{1}.dB{1}.ATTRIBUTE.time);
|
wolffd@0
|
74
|
wolffd@0
|
75 segments(i).loudness_max = str2double(...
|
wolffd@0
|
76 dsegments{i}.loudness{1}.dB{2}.CONTENT);
|
wolffd@0
|
77
|
wolffd@0
|
78 segments(i).loudness_max_time = str2double(...
|
wolffd@0
|
79 dsegments{i}.loudness{1}.dB{2}.ATTRIBUTE.time);
|
wolffd@0
|
80 end
|
wolffd@0
|
81
|
wolffd@0
|
82 features.segments = segments;
|
wolffd@0
|
83
|
wolffd@0
|
84 %% ------------------------------------------------------------------------
|
wolffd@0
|
85 % ---
|
wolffd@0
|
86 % get sections
|
wolffd@0
|
87 % ---
|
wolffd@0
|
88 dsections = data.track{1}.sections{1}.section;
|
wolffd@0
|
89
|
wolffd@0
|
90 secstart = zeros(1,numel(dsections));
|
wolffd@0
|
91 secduration = zeros(1,numel(dsections));
|
wolffd@0
|
92 for i = 1:numel(dsections)
|
wolffd@0
|
93 sections(i).start = str2double(dsections{i}.ATTRIBUTE.start);
|
wolffd@0
|
94 sections(i).duration = str2double(dsections{i}.ATTRIBUTE.duration);
|
wolffd@0
|
95 end
|
wolffd@0
|
96
|
wolffd@0
|
97 features.sections = sections;
|
wolffd@0
|
98
|
wolffd@0
|
99 %% ------------------------------------------------------------------------
|
wolffd@0
|
100 % ---
|
wolffd@0
|
101 % get beat and rythm data. the metric data is structured
|
wolffd@0
|
102 % hierarchically, as each bar contains several beats,
|
wolffd@0
|
103 % which contaisn several tatums.
|
wolffd@0
|
104 % NOTE: Although the metrum and tempo have been evaluated and fixed
|
wolffd@0
|
105 % on a global scale, the number of bars and tatum vary greatly.
|
wolffd@0
|
106 % ---
|
wolffd@0
|
107 dbars = data.track{1}.meter{1}.bar;
|
wolffd@0
|
108
|
wolffd@0
|
109 for i = 1:numel(dbars)
|
wolffd@0
|
110
|
wolffd@0
|
111 % get bar information
|
wolffd@0
|
112 bars(i).confidence = str2double(dbars{i}.ATTRIBUTE.conf);
|
wolffd@0
|
113 for j = 1:numel(dbars{i}.beat)
|
wolffd@0
|
114
|
wolffd@0
|
115 % get beat information
|
wolffd@0
|
116 bars(i).beat(j).confidence = str2double(dbars{i}.beat{j}.ATTRIBUTE.conf);
|
wolffd@0
|
117
|
wolffd@0
|
118 for k = 1:numel(dbars{i}.beat{j}.tatum)
|
wolffd@0
|
119
|
wolffd@0
|
120 % get tatum information
|
wolffd@0
|
121 if ~isempty(dbars{i}.beat{j}.tatum{k}.ATTRIBUTE)
|
wolffd@0
|
122 bars(i).beat(j).tatum(k).time = str2double(dbars{i}.beat{j}.tatum{k}.CONTENT);
|
wolffd@0
|
123 bars(i).beat(j).tatum(k).confidence = str2double(dbars{i}.beat{j}.tatum{k}.ATTRIBUTE.conf);
|
wolffd@0
|
124
|
wolffd@0
|
125 else
|
wolffd@0
|
126 % save empty struct
|
wolffd@0
|
127 bars(i).beat(j).tatum = struct([]);
|
wolffd@0
|
128 end
|
wolffd@0
|
129
|
wolffd@0
|
130 end
|
wolffd@0
|
131 end
|
wolffd@0
|
132 end
|
wolffd@0
|
133
|
wolffd@0
|
134 features.bars = bars;
|
wolffd@0
|
135
|