wolffd@0: classdef MTTMixedFeatureStober11 < MTTAudioFeature & handle wolffd@0: % --- wolffd@0: % This Class contains wolffd@0: % features are extracted wolffd@0: % as described in Slaney 08 - LEARNING A METRIC FOR MUSIC SIMILARITY wolffd@0: % wolffd@0: % The usual workflow for these features constist of three steps wolffd@0: % 1. extract: extracts the basic single-file dependent features wolffd@0: % 2. define_global_transform: calculates the global feature wolffd@0: % transformation parameters wolffd@0: % 3. finalise: applies the common transformations to a specific feature wolffd@0: % --- wolffd@0: wolffd@0: properties(Constant = true) wolffd@0: wolffd@0: % svn hook wolffd@0: my_revision = str2double(substr('$Rev$', 5, -1)); wolffd@0: end wolffd@0: wolffd@0: properties wolffd@0: % --- wolffd@0: % Set default parameters wolffd@0: % --- wolffd@0: my_params = struct(... wolffd@0: 'stob_lowaudio', 1, ... wolffd@0: 'stob_highaudio', 1, ... % wolffd@0: 'stob_tags', 1, ... % wolffd@0: 'stob_norm', 1 ... wolffd@0: ); wolffd@0: end wolffd@0: wolffd@0: % --- wolffd@0: % member functions wolffd@0: % --- wolffd@0: methods wolffd@0: wolffd@0: % --- wolffd@0: % constructor: pointer to feature in database wolffd@0: % --- wolffd@0: function feature = MTTMixedFeatureStober11(varargin) wolffd@0: wolffd@0: feature = feature@MTTAudioFeature(varargin{:}); wolffd@0: wolffd@0: end wolffd@0: % --- wolffd@0: % extract feature data from raw audio features wolffd@0: % --- wolffd@0: function data = extract(feature, clip) wolffd@0: % --- wolffd@0: % get features. this includes possible wolffd@0: % local normalisations wolffd@0: % --- wolffd@0: wolffd@0: global globalvars; wolffd@0: global stobbase; wolffd@0: wolffd@0: if isempty(stobbase); wolffd@0: stobbase = load('features_stober'); wolffd@0: end wolffd@0: wolffd@0: % --- wolffd@0: % NOTE: we define feature sets which are included / wolffd@0: % excluded according to the specified parameters wolffd@0: % --- wolffd@0: wolffd@0: lowAudio = {'pitchMean', 'pitchSdev', 'timbreMean', 'timbreSdev'}; wolffd@0: wolffd@0: % highAudio features more or less correspond to slaney 08 features wolffd@0: hiAudio = {'energy','key','loudness','timeSignature',... wolffd@0: 'danceability', 'mode', 'tempo'}; wolffd@0: wolffd@0: metadat = {'tags'}; wolffd@0: wolffd@0: allowedFeat = {}; wolffd@0: wolffd@0: % Select the features to keep wolffd@0: if feature(1).my_params.stob_lowaudio wolffd@0: allowedFeat = {allowedFeat{:}, lowAudio{:}}; wolffd@0: end wolffd@0: if feature(1).my_params.stob_highaudio wolffd@0: allowedFeat = {allowedFeat{:}, hiAudio{:}}; wolffd@0: end wolffd@0: if feature(1).my_params.stob_tags wolffd@0: allowedFeat = {allowedFeat{:}, metadat{:}}; wolffd@0: end wolffd@0: wolffd@0: % get the actual clip id wolffd@0: idx = find(stobbase.clipIds == clip.id); wolffd@0: wolffd@0: % --- wolffd@0: % NOTE: we just copy everything in a big matrix and then wolffd@0: % normalise the data later wolffd@0: % --- wolffd@0: data.vector_info = {}; wolffd@0: data.stobraw = []; wolffd@0: fields = fieldnames(stobbase); wolffd@0: for i = 1:numel(fields) wolffd@0: wolffd@0: % skip clip ID field wolffd@0: if strcmp(fields{i},'clipIds'); wolffd@0: continue; wolffd@0: end wolffd@0: wolffd@0: % skip unwanted features wolffd@0: if isempty(strcellfind(allowedFeat, fields{i})) wolffd@0: continue; wolffd@0: end wolffd@0: wolffd@0: % --- wolffd@0: % TODO: special case for tag features, including wolffd@0: % the tag names wolffd@0: % --- wolffd@0: wolffd@0: % put field info into right position wolffd@0: data.vector_info{numel(data.stobraw)+1} = fields{i}; wolffd@0: wolffd@0: % add data to feature wolffd@0: if size(stobbase.(fields{i}),1) == 1 wolffd@0: data.stobraw(end+1) = stobbase.(fields{i})(idx); wolffd@0: else wolffd@0: % concatenate vector wolffd@0: tmpdat = stobbase.(fields{i})(idx,:); wolffd@0: data.stobraw(end+1:end+numel(tmpdat)) = tmpdat; wolffd@0: end wolffd@0: end wolffd@0: % padd further info struct wolffd@0: data.vector_info(end+1:numel(data.stobraw)) =... wolffd@0: cell(numel(data.stobraw) - numel(data.vector_info) , 1); wolffd@0: wolffd@0: % --- wolffd@0: % prepare field for final features wolffd@0: % --- wolffd@0: data.final.vector = []; wolffd@0: data.final.vector_info = struct(); wolffd@0: data.final.dim = 0; wolffd@0: wolffd@0: % save info data wolffd@0: data.info.type = 'MTTMixedFeatureStober11'; wolffd@0: data.info.owner_id = clip.id; wolffd@0: data.info.creatorrev = feature.my_revision; wolffd@0: wolffd@0: % save parameters wolffd@0: data.info.params = feature.my_params; wolffd@0: end wolffd@0: wolffd@0: function define_global_transform(features) wolffd@0: % calculate and set normalization factors from the group of wolffd@0: % input features. These features will be set for the full database wolffd@0: wolffd@0: final = zeros(numel(features(1).data.stobraw), numel(features)); wolffd@0: for i = 1:numel(features) wolffd@0: if ~isempty(features(i).data.stobraw) wolffd@0: final(:,i) = features(i).data.stobraw'; wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: if features(1).my_params.stob_norm wolffd@0: if numel(features) == 1 wolffd@0: error ('Insert feature array for this method, or set normalisation to 0'); wolffd@0: end wolffd@0: wolffd@0: % --- wolffd@0: % here, we only need to define the post-normalisation wolffd@0: % --- wolffd@0: [final, pstd] = mapminmax(final,0,1); wolffd@0: common.stobstats.post_norm = pstd; wolffd@0: wolffd@0: % --- wolffd@0: % NOTE: whitening as in slaney?? wolffd@0: % Would make reading the wolffd@0: % mahal matrices really hard wolffd@0: % --- wolffd@0: wolffd@0: features(1).my_db.set_common(common); wolffd@0: wolffd@0: else wolffd@0: wolffd@0: features(1).my_db.set_common([1]); wolffd@0: end wolffd@0: wolffd@0: % save the normalised features straight away! wolffd@0: features.finalise(final); wolffd@0: end wolffd@0: wolffd@0: wolffd@0: function finalise(features, final) wolffd@0: % applies a final transformation and wolffd@0: % collects the information of this feature within a single vector wolffd@0: % see info for types in specific dimensions wolffd@0: % check if features have been finalised already wolffd@0: wolffd@0: % --- wolffd@0: % set feature labelling wolffd@0: % --- wolffd@0: wolffd@0: info = {}; wolffd@0: wolffd@0: % --- wolffd@0: % construct resulting feature vector out of features wolffd@0: % --- wolffd@0: if nargin == 2 && isempty(final) wolffd@0: wolffd@0: % the final vector etc already are set to zero; wolffd@0: return; wolffd@0: wolffd@0: elseif nargin == 2 && (numel(features) == size(final, 2)) wolffd@0: % the features have already been preassembled wolffd@0: wolffd@0: for i = 1:numel(features) wolffd@0: wolffd@0: % check for neccesary parameters wolffd@0: if isempty(features(i).my_db.commondb) wolffd@0: wolffd@0: error('Define the global transformation first') wolffd@0: return; wolffd@0: end wolffd@0: wolffd@0: features(i).data.final.vector = final(:,i); wolffd@0: features(i).data.final.dim = size(final,1); wolffd@0: wolffd@0: % fill up info struct and append to feature wolffd@0: features(i).data.final.vector_info.labels = ... wolffd@0: features(i).data.vector_info; wolffd@0: end wolffd@0: else wolffd@0: % --- wolffd@0: % if features have been added after gettin gnormalisation wolffd@0: % parameters, ther should be still an option to include wolffd@0: % them wolffd@0: % --- wolffd@0: wolffd@0: for i = 1:numel(features) wolffd@0: wolffd@0: % check for neccesary parameters wolffd@0: if isempty(features(i).my_db.commondb) wolffd@0: wolffd@0: error('Define the global transformation first') wolffd@0: return; wolffd@0: end wolffd@0: wolffd@0: final = features(i).data.stobraw'; wolffd@0: wolffd@0: if features(1).my_params.stob_norm == 1 wolffd@0: wolffd@0: [final] = mapminmax('apply', final, features(1).common.stobstats.post_norm); wolffd@0: end wolffd@0: wolffd@0: features(i).data.final.vector = final; wolffd@0: features(i).data.final.dim = size(final,1); wolffd@0: wolffd@0: % fill up info struct and append to feature wolffd@0: features(i).data.final.vector_info.labels = ... wolffd@0: features(i).data.vector_info; wolffd@0: end wolffd@0: wolffd@0: end wolffd@0: wolffd@0: % --- wolffd@0: % TODO: Maybe delete more basic features again at this point? wolffd@0: % --- wolffd@0: end wolffd@0: wolffd@0: % --- wolffd@0: % destructor: do we really want to remove this wolffd@0: % from the database? No, but wolffd@0: % TODO: create marker for unused objects in db, and a cleanup wolffd@0: % function wolffd@0: % --- wolffd@0: function delete(feature) wolffd@0: wolffd@0: end wolffd@0: end wolffd@0: end