% --
% This class loads and hanles the aufdio features included with the MTT
% Library
% 
% Features are extracted and then porcessed by son's RBM toolbox
% --- 
classdef MTTMixedFeatureSlaney08GenreBasicSmRBM < MTTAudioFeature & handle
    
    properties(Constant = true)
        
        my_revision = str2double(substr('$Rev: 457 $',  5, -1));
    end
    
    properties
        
        % ---
        % Set default parameters
        % ---
        my_basetype = 'MTTMixedFeatureSlaney08GenreBasicSm';
        
        my_params = MTTAudioFeature.inherited_params(...
            'MTTMixedFeatureSlaney08GenreBasicSm', ...
            ... % --- 
            ... % Following: RBM params
            ... % ---
            'norm_pre_rbm', 0, ... % norm before RBM?
            'norm_post_rbm', 0, ... % norm before RBM?
            'rbm_hidNum',200, ...   % number of hidden units
            'rbm_eNum', 100, ...
            'rbm_bNum', 1, ...
            'rbm_gNum', 1, ...
            'rbm_lrate1' , 0.05, ... % initial learning rate
            'rbm_lrate2', 0.1, ... %  learning rate
            'rbm_momentum', 0.5, ...
            'rbm_cost', 0.00002, ... % cost function
            'rbm_N', 50, ...
            'rbm_MAX_INC', 10 ...
        );
    end
    % ---
    % member functions
    % ---
    methods
        
        % ---
        % constructor: pointer to feature in database
        % ---
        function feature = MTTMixedFeatureSlaney08GenreBasicSmRBM(varargin)

            feature = feature@MTTAudioFeature(varargin{:});
        end
        
        % ---
        % load feature data from xml file
        % ---
        function data = extract(feature, clip)
        % load feature data by parsing xml
            
            global globalvars;
            
            % ---
            % we extract the base features, and save 
            % the pointers to these.
            % the main work is then done in the define_global_transf
            % and finalise functions.
            % ---
            data.basefeat = clip.features(feature.my_basetype,...
                feature.my_params); 

            % save info data
            data.info.type = class(feature);
            data.info.owner = clip;
            data.info.owner_id = clip.id;
            data.info.creatorrev = feature.my_revision;
            
            % save param data
            data.info.params = feature.my_params;
            
            % prepare field for final features
            data.final.vector = [];
            data.final.dim = 0;
            data.final.vector_info.labels = {};
        end
        
        function define_global_transform(features)
            
            if numel(features) == 1
                error ('Insert feature array for this method');
            end
            
            % ---
            % We collect all the relevant genretag 
            % features and get the transform on this basis.
            % ---
            for i = 1:numel(features)
            	basef(i) = features(i).data.basefeat;
            end
            
            % call the features own transsform function
            basef.define_global_transform();
            
            % ---
            % finalise the basic features, and 
            % get the feature vectors;
            % ---
            X = basef.vector();
            
            % check dataset dimension
            if numel(features) < basef.dim;
                
                error ('Not enough feature vectors for RBM calculation. need %d samples', ...
                    basef.dim);
            end
            
            % ---
            % NOTE: should the data be normalised and scaled to -1:1 
            % instead of being in a range of 0-1 AND max-min = 1
            % --- 
            if features(1).my_params.norm_pre_rbm == 1
                                
                [X, pstd] = mapminmax(X,-1,1);
                common.rbm.pre_norm = pstd;
            elseif features(1).my_params.norm_pre_rbm == 2
                
                [X, pstd] = mapstd(X,0,1);
                common.rbm.pre_norm = pstd;
            end

            
            % ---
            % TRAIN RBM
            % ---
            conf.sNum   = size(X,2);
            conf.eNum   = features(1).my_params.rbm_eNum;
            conf.bNum   = features(1).my_params.rbm_bNum;
            conf.gNum   = features(1).my_params.rbm_gNum;
            conf.hidNum   = features(1).my_params.rbm_hidNum;
            conf.MAX_INC   = features(1).my_params.rbm_MAX_INC;
            conf.N   = features(1).my_params.rbm_N;
            conf.params = [ features(1).my_params.rbm_lrate1 features(1).my_params.rbm_lrate2 ...
                           features(1).my_params.rbm_momentum features(1).my_params.rbm_cost];          
            W1 = zeros(0,0);
            [common.rbm.W1 common.rbm.vB1 common.rbm.hB1] = training_rbm_(conf,W1,X');
            
            % ---
            % This transforms the features using the learnt RBM
            % ---
            Y = logistic(X' * common.rbm.W1 + repmat(common.rbm.hB1,conf.sNum,1))';

            
            % normalise values after processing
            if features(1).my_params.norm_post_rbm
                
                [Y,pmm] = mapminmax(Y,0,1);
                common.rbm.post_norm = pmm;
            end
            
            % ---
            % set common feature values for mixed features
            % ---
            features(1).my_db.set_common(common);            
            
            % save the transformed features straight away!
            features.finalise(Y);
        end
        
        function finalise(feature, final)
        % applies a final transformation and
        % collects the information of this feature within a single vector
        % see info for types in specific dimensions
        
            max_size = feature(1).my_params.rbm_hidNum;

            % prepare information
            info = {'RBM'};
            info(2:max_size) = num2cell(2:max_size);
        
            % check if features have been finalised already
            if nargin == 2 && isempty(final)
                
                % the final vector etc already are set to zero;
                return;
                
            elseif nargin == 2 && (numel(feature) == size(final, 2))
                    
                for i = 1:numel(feature)
                    
                    % save final vector and description
                    feature(i).data.final.vector = final(:,i);
                    feature(i).data.final.dim = max_size;
                    feature(i).data.final.vector_info.labels = info;
                end
                
            else
            % features have to be transformed first
            % ---
            % TODO: this code remains untested
            % ---
            
            % check for neccesary parameters
                if isempty(feature(1).my_db.commondb)

                    error('Define the global transformation first')
                    return;
                end
             

                for i = 1:numel(feature)

                    % check for neccesary parameters
                    if isempty(feature(i).my_db.commondb)

                        error('Define the global transformation first')
                    end
                    
                    % ---
                    % get feature vector and apply transformation
                    % ---
                    X = feature(i).data.basefeat.vector();
                    
                    % ---
                    % apply normalisation used for removing mean 
                    % in training data
                    % ---
                    if feature(1).my_params.norm_pre_rbm == 1
                                
                        X = mapminmax('apply', X, feature(1).common.rbm.pre_norm);
                    elseif feature(1).my_params.norm_pre_rbm == 2

                        X = mapstd('apply', X, feature(1).common.rbm.pre_norm);
                    end
            
                    % ---
                    % RBM: This transforms the features using the learnt RBM
                    % ---
                    conf.sNum = size(X,1);
                    vec = logistic(X * common.rbm.W1 + repmat(common.rbm.hB1,conf.sNum,1));

                    
                    % normalise pca values after transformation
                    if feature(1).my_params.norm_post_rbm

                        vec = mapminmax('apply', vec,...
                            feature(1).common.rbm.post_norm);
                    end
                    
                    % ---
                    % cut vector to final size. 
                    % NOTE: this should be done before
                    % transformation to reduce computation time
                    % ---
                    vec = vec(1:max_size);
                    
                    % save final vector and description
                    feature(i).data.final.vector = vec;
                    feature(i).data.final.dim = numel(vec);
                    feature(i).data.final.vector_info.labels = info;
                end
            end
        end
        
    end
end