function D = en_analyze(F, verbose)
% D = en_analyze(F, verbose)
% Pass an MP3 file whose filename is F to the Echo Nest
% Analyze API, and return the analyzed data.
% If F is a "music:TR..." key, skip upload and query that track.
% F can also be the bytes of an MP3 file (if of type uint8).
%
% This version uses Analyze v4 and json format for data return.
% It relies on p_json.m .
% 2011-04-11 Dan Ellis dpwe@ee.columbia.edu
API_KEY='9UGMUXTCZ2WT7WWMJ'; % E4896 key
if nargin < 2
verbose = 0;
end
if isnumeric(F) || strncmp(F, 'music:', 6)==0
if isa(F,'uint8')
% F is the bytes read from an mp3 file
d = F;
else
if isnumeric(F)
% maybe accept a waveform - but then we'd need a sampling
% rate. We'd have to mp3write it to a temporary file, then
% upload that.
error('F was numeric but not bytes - unrecognized type');
end
% We were passed an actual file name
% Read in the MP3 file
f = fopen(F);
d = fread(f,Inf,'*uint8'); % Read in byte stream of MP3 file
fclose(f);
end
ddisp('uploading mp3', verbose);
% Post to EN Analyze
% readpost with single argument posts an octet-stream, as
% required for v4; rest of args go into URL
str = urlpostdata('http://developer.echonest.com/api/v4/track/upload', ...
{'api_key',API_KEY,'filetype','mp3', ...
'wait','true','format','json'}, ...
d);
ddisp('upload complete', verbose);
% Grab return
ret = p_json(str);
% Check EN fields
retcode = ret.response.status.code;
if retcode ~= 0 % ret.status.code
disp(['******* upload returned ', num2str(retcode),
' - ',ret.response.status.message]);
D = [];
return
end
% Get track ID
track = ret.response.track.id;
% always report track ID
else
if strncmp(F, 'music:', 6)
F = F(7:end);
if max(F=='/') > 0
F = F(max(find(F=='/'))+1:end);
end
end
track = F;
end
disp(['track ID=',track]);
D.id = track;
% read back the entire analysis structure
%str = urlreadpost('http://developer.echonest.com/api/v4/track/analyze', ...
% {'api_key',API_KEY,'format','json','id',track, ...
% 'bucket','audio_summary','wait','true'});
str = urlread(['http://developer.echonest.com/api/v4/track/profile?', ...
'api_key=',API_KEY,'&format=','json','&id=',track, ...
'&bucket=','audio_summary']);
ddisp('got analyze response', verbose);
% find the full analysis structure
ret = p_json(str);
trackinfo = ret.response.track;
analysis_url = trackinfo.audio_summary.analysis_url;
str = urlread(analysis_url);
ddisp('got analysis url data', verbose);
analysis = p_json(str);
ddisp('parsed analysis url data', verbose);
nsegs = length(analysis.segments);
nchr = length(analysis.segments{1}.pitches);
ntimb = length(analysis.segments{1}.timbre);
D.pitches = zeros(nchr, nsegs);
D.timbre = zeros(ntimb, nsegs);
D.segment = zeros(1, nsegs);
D.segmentduration = zeros(1, nsegs);
D.segmentloudness = zeros(1, nsegs);
D.segmentloudnessmax = zeros(1, nsegs);
D.segmentloudnessmaxtime = zeros(1, nsegs);
%Trial>> str2num(analysis(5).sub(2).sub(3).sub(1).data)
% 3rd chroma coef of 5th frame
for i = 1:nsegs
D.pitches(:,i) = cellfun(@(x) x, analysis.segments{i}.pitches)';
D.timbre(:,i) = cellfun(@(x) x, analysis.segments{i}.timbre)';
% D.segment(i) = d.analysis.segment(i).ATTRIBUTE.start; % D.segment or D.segmentstart?
% D.segmentduration(i) = d.analysis.segment(i).ATTRIBUTE.duration;
% D.segmentloudness(i) = d.analysis.segment(i).loudness.dB(1).CONTENT;
% D.segmentloudnessmax(i) = d.analysis.segment(i).loudness.dB(2).CONTENT;
% D.segmentloudnessmaxtime(i) = d.analysis.segment(i).loudness.dB(2).ATTRIBUTE.time;
end
D.segment = cellfun(@(x) x.start, analysis.segments);
D.segmentduration = cellfun(@(x) x.duration, analysis.segments);
D.segmentloudness = cellfun(@(x) x.loudness_start, analysis.segments);
D.segmentloudnessmax = cellfun(@(x) x.loudness_max, analysis.segments);
D.segmentloudnessmaxtime = cellfun(@(x) x.loudness_max_time, analysis.segments);
ddisp('segment info parsed', verbose);
% Read the tatums
%ntatums = length(d.analysis.tatum);
ntatums = length(analysis.tatums);
D.tatum = cellfun(@(x) x.start, analysis.tatums);
D.tatumconfidence = cellfun(@(x) x.confidence, analysis.tatums);
% Read the beats
nbeats = length(analysis.beats);
D.beat = cellfun(@(x) x.start, analysis.beats);
D.beatconfidence = cellfun(@(x) x.confidence, analysis.beats);
ddisp('beat info parsed', verbose);
% Read the bars
nbars = length(analysis.bars);
D.bar = cellfun(@(x) x.start, analysis.bars);
D.barconfidence = cellfun(@(x) x.confidence, analysis.bars);
ddisp('bar info parsed', verbose);
% Read the sections
nsections = length(analysis.sections);
D.section = cellfun(@(x) x.start, analysis.sections);
D.sectionduration = cellfun(@(x) x.duration, analysis.sections);
D.sectionconfidence = cellfun(@(x) x.confidence, analysis.sections);
ddisp(' parsing remaining fields', verbose);
% Single value attributes
D.end_of_fade_in = analysis.track.end_of_fade_in;
D.start_of_fade_out = analysis.track.start_of_fade_out;
D.key = analysis.track.key;
D.keyconfidence = analysis.track.key_confidence;
D.mode = analysis.track.mode;
D.modeconfidence = analysis.track.mode_confidence;
D.loudness = analysis.track.loudness;
D.tempo = analysis.track.tempo;
D.tempoconfidence = analysis.track.tempo_confidence;
D.time_signature = analysis.track.time_signature;
D.time_signatureconfidence = analysis.track.time_signature_confidence;
% Metadata
% Set whatever we get, but ensure artist, release, and title exist
D.artist = '';
D.release = '';
D.title = '';
metafield = fieldnames(trackinfo);
for i = 1:length(metafield)
field = metafield{i};
val = getfield(trackinfo,field);
if ~isstruct(val)
D = setfield(D,field,val);
end
end
% and for audio_summary fields
metafield = fieldnames(trackinfo.audio_summary);
for i = 1:length(metafield)
field = metafield{i};
val = getfield(trackinfo.audio_summary,field);
if ~isstruct(val)
D = setfield(D,field,val);
end
end
ddisp('done', verbose);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function ddisp(s,v)
if(v)
disp(s)
end