-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlab_beatsyncfeat_ext.m
181 lines (157 loc) · 5.98 KB
/
lab_beatsyncfeat_ext.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
function [tonics, modes, beatnums, chromas, atslopes, domnotes] = lab_beatsyncfeat_ext(indir, outdir)
% function [tonics, modes, beatnums, chromas, atslopes, domnotes] = lab_beatsyncfeat_ext(indir, outdir)
%
% Extract the beat-synchronous features for all audio files in a given
% directory. Additionally, parse the annotated tonics, modes, and beats
% according to the formats specified by the Queen Mary University
% Annotations (http://isophonics.net/datasets). Note: this function
% assumes the audio files and the annotation files are named the same.
%
% Parameters:
% indir the directory containing the audio (mp3/wav/au/aiff)
% and annotation (.lab & .bts) files.
% outdir optional output directory of where to write the
% extracted features as .csv files.
%
% Output:
% tonics the sequence of tonic (root) notes from the .lab files
% modes the sequence of modes (key families) from the .lab files
% beatnums the beat numbers from the .bts files
% chromas the extracted beat-synchronous chromagrams
% atslopes the extracted beat-synchronous attack slopes
% domnotes the extracted beat-synchronous dominant notes
%
% License:
% UCCS MIR Key Detection
% Copyright (C) 2012 Devon Bryant
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU Affero 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 Affero General Public License for more details.
%
% You should have received a copy of the GNU Affero General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
files = dir(indir);
notes = Notes();
filesMap = containers.Map();
for i=1:length(files)
if ~files(i).isdir
fname = files(i).name;
% Test Audio File
splitname = regexpi(fname, '\.(mp3|wav|au|aiff?)', 'split');
fidx = 0;
if length(splitname) > 1
fidx = 1;
else
% Test Key File
splitname = regexpi(fname, '\.lab', 'split');
if length(splitname) > 1
fidx = 2;
else
% Test Beats File
splitname = regexpi(fname, '\.bts', 'split');
if length(splitname) > 1
fidx = 3;
end
end
end
if fidx > 0
sname = splitname{1};
if ~isKey(filesMap, sname)
filesMap(sname) = cell(1,3);
end
fileEntry = filesMap(sname);
fileEntry{fidx} = strcat(indir, '/', fname);
filesMap(sname) = fileEntry;
end
end
end
tonics = cell(1,filesMap.length);
modes = cell(1,filesMap.length);
beatnums = cell(1,filesMap.length);
chromas = cell(1,filesMap.length);
atslopes = cell(1,filesMap.length);
domnotes = cell(1,filesMap.length);
file_keys = keys(filesMap);
sr = 11025;
for i=1:length(file_keys)
fileEntry = filesMap(file_keys{i});
audiofname = fileEntry{1};
keysfname = fileEntry{2};
btsfname = fileEntry{3};
% Extract the various beat-sync features from the audio file
[data,sr,bts,chromas{i},domnotes{i},atslopes{i}] = beatsyncfeat(audiofname, sr);
% Get the lab keys data (start time and key)
[tonics{i} modes{i}] = labfeats(keysfname, bts);
if btsfname
% Get the beat numbers data (1,2,3,4,1,2,3,4...)
beatnums{i} = btfeats(btsfname, bts);
end
if nargin > 1
% Write out the features to file
beatsyncfeatcsvwrite(strcat(outdir, '/', file_keys{i}, '.chroma.csv'), bts, chromas{i}, 1, 44100, 1024);
beatsyncfeatcsvwrite(strcat(outdir, '/', file_keys{i}, '.atslope.csv'), bts, atslopes{i});
beatsynccellcsvwrite(strcat(outdir, '/', file_keys{i}, '.domnotes.csv'), bts, domnotes{i});
beatsynccellcsvwrite(strcat(outdir, '/', file_keys{i}, '.tonics.csv'), bts, tonics{i});
beatsynccellcsvwrite(strcat(outdir, '/', file_keys{i}, '.modes.csv'), bts, modes{i});
if btsfname
beatsynccellcsvwrite(strcat(outdir, '/', file_keys{i}, '.btnums.csv'), bts, beatnums{i});
end
end
end
end
function [tonics families] = labfeats(labfile, bts)
keysfid = fopen(labfile);
keysdata = textscan(keysfid, '%f %*f %*s %s');
fclose(keysfid);
keytimes = {};
keytonics = {};
keyfamilies = {};
for k=1:length(keysdata{1})
if ~isempty(keysdata{2}{k})
keytimes{end+1} = keysdata{1}(k);
% Split the key (e.g. 'C#:minor' or 'F') into root/family
keyval = regexpi(keysdata{2}{k}, '\w*#?\w*', 'match');
keytonics{end+1} = keyval{1};
if length(keyval) > 1
keyfamilies{end+1} = keyval{2};
else
keyfamilies{end+1} = 'major';
end
end
end
tonics = bts_align(bts,keytimes,keytonics);
families = bts_align(bts,keytimes,keyfamilies);
% Fill in any missing values
% tonics = fillvals(tonics);
% families = fillvals(families);
end
function [beatnums] = btfeats(btfile, bts)
btsfid = fopen(btfile);
btsdata = textscan(btsfid, '%f %d');
fclose(btsfid);
bttimes = num2cell(btsdata{1}');
btnums = num2cell(btsdata{2}');
beatnums = bts_align(bts,bttimes,btnums);
% Fill in any missing values
% beatnums = fillvals(beatnums);
end
function B = fillvals(B)
i = 1;
while isempty(B{i}); i = i+1; end
% Initial value
lastval = B{i};
for i=1:length(B)
if isempty(B{i})
B{i} = lastval;
else
lastval = B{i};
end
end
end