Mercurial > hg > massef
view MASSEF.m @ 38:c7d11a428a0d tip master
Merge branch 'develop'
* develop:
Updated copyright year.
author | Christopher Hummersone <c.hummersone@surrey.ac.uk> |
---|---|
date | Tue, 16 May 2017 12:15:34 +0100 |
parents | e14df3f8139e |
children |
line wrap: on
line source
classdef MASSEF < handle %MASSEF Multichannel audio source separation evaluation framework. % % The multichannel audio source separation evaluation framework is % designed to faciliate the development and evaluation of audio % source separation algorithms. Algorithms and estimates are evaluated % using a range of metrics, including SNR, STOI, and PEASS. The choice of % metrics is configurable. Furthermore, if the algorithm is intended to % perform localisation, then this can also be evaluated. % % The framework can be run in two ways: % 1) by providing iosr.bss.mixture objects and separation algorithms, % or % 2) providing estimate and true source wav files. % % If 1), the framework generates the mixture(s), calculates the ideal % binary and ratio masks, provides the mixture(s) to the separation % algorithm(s), and evaluates the outputs of the separation algorithm(s). % The framework also evaluates: the ideal masks for the purposes of % comparison, and any azimuth/elevation estimates returned by the % algorithm. Use the EXECUTE() method to operate in this mode. % % If 2), the framework evaluates only the supplied estimate(s) using % signal-based metrics. Use the EVALUATE() method to operate in this % mode. % % Sources may have any number of channels; the framework evaluates each % channel. The use of iosr.bss.mixture objects facilitate the evaluation % of spatialised mixtures (e.g. binaural). % % Type % % MASSEF.start % MASSEF.doc % % for more information. % % MASSEF can be used to evaluate and compare separation algorithms, % provided that the algorithms conform to the required format. Consult % the help documentation for more information. % % MASSEF properties: % blocksize - When using the parallel computing toolbox, % this parameter determines the maximum number % of parallel.FevalFuture objects that are % considered at any one time. The default is % 128. % creationDate - Date the object was created (read-only). % dir - The MASSEF installation directory % (read-only). % evalPEASS - A logical value indicating whether PEASS % evaluation should be executed. The default is % false. % evalSTOI - A logical value indicating whether STOI % evaluation should be executed. The default is % false. % parpool - A parallel.Pool object on which to perform % the parallel separations. If the parallel % computing toolbox is available, MASSEF % will use the current pool by default (as % determined by gcp('nocreate')), if one is % open. If the toolbox is not available, or no % pool is open, separations will be performed % serially. % results - A MASSEFresults object containing results % generated by the framework (read-only). % results_filename - The name of the results file % returned when MASSEF.EXECUTE finishes. % The default is 'Results/results.mat'. % saveDate - Date the object was last saved (read-only). % % MASSEF methods: % MASSEF - Create an instance of MASSEF. % evaluate - Run the framework using input audio files. % execute - Run the framework using the input mixtures % and separators. % save - Save the framework's data and results. % Static methods: % doc - Display the framework documentation. % start - Start the framework. % install - Download and install MASSEF dependencies. % % See also IOSR.BSS.MIXTURE, MASSEFRESULTS. % Copyright 2016 University of Surrey. properties blocksize % number of simultaneous parallel.FevalFuture objects evalPEASS % logical flag determing evaluation using PEASS evalSTOI % logical flag determing evaluation using STOI parpool % parallel.pool object on which to perform separations results_filename % the name of the results file end properties (Constant) dir = fileparts(which(mfilename('fullpath'))) % directory in which this file is stored end properties (SetAccess = private) results % the results data creationDate % date this object was created saveDate % date this object was last saved end properties (Access = private) hWaitBar % handle to waitbar iterations % total iterations PEASSoptions % PEASS options end properties (Constant, Access = private) IDEAL = -1 % algorithm number for ideal estimates end methods % constructor function obj = MASSEF(options) %MASSEF Create an instance of MASSEF % % M = MASSEF instantiates MASSEF, returning an instance to M. % Evaluations are performed using the EXECUTE method. % % M = MASSEF(OPTIONS) instantiates MASSEF using the options % contained in the scalar structure OPTIONS. See MASSEF for a % description of valid fields. if nargin<1 options = struct; end options = obj.validate_options(options); obj.evalPEASS = options.evalPEASS; obj.evalSTOI = options.evalSTOI; obj.parpool = options.parpool; obj.results_filename = options.results_filename; obj.blocksize = options.blocksize; % create empty results object array obj.results = MASSEFresults(); obj.creationDate = date; end % validate properties function set.blocksize(obj,val) assert(isnumeric(val) && isscalar(val),'MASSEF:blocksize:invalid','blocksize must be a numeric scalar.') assert(round(val)==val,'MASSEF:invalidBlocksize','blocksize must be an integer.') assert(val>=1,'MASSEF:invalidBlocksize','blocksize must be greater than or equal to 1.') obj.blocksize = val; end function set.evalPEASS(obj,val) assert(islogical(val) && isscalar(val),'MASSEF:evalPEASS:invalid','evalPEASS must be a logical scalar.') obj.evalPEASS = val; end function set.evalSTOI(obj,val) assert(islogical(val) && isscalar(val),'MASSEF:evalSTOI:invalid','evalSTOI must be a logical scalar.') obj.evalSTOI = val; end function set.parpool(obj,val) if ~isempty(val) assert(isa(val,'parallel.Pool'),'MASSEF:parpool:invalid','parpool must be a parallel.pool object.') end obj.parpool = val; end function set.results_filename(obj,val) assert(ischar(val),'MASSEF:results_filename:invalid','results_filename must be a char array') obj.results_filename = val; end % other methods function execute(obj,mixtures,separators) %EXECUTE Run the framework using the input mixtures and separators % % OBJ.EXECUTE(MIXTURES) runs MASSEF using the array of % iosr.bss.mixture objects MIXTURES and calculates performance % metrics using only ideal estimates (IBM and IRM). % % OBJ.EXECUTE(MIXTURES,SEPARATORS) runs MASSEF using the array of % iosr.bss.mixture objects MIXTURES and the array (or cell array) % of instances of separation algorithms contained in SEPARATORS. % Separation is performed for all combinations of these % variables. % % MASSEF.EXECUTE(MIXTURES,SEPARATORS) runs MASSEF using the % MASSEF instance MASSEF, the array of IOSR.BSS.MIXTURE objects % MIXTURES and the array (or cell array) of instances of % separation algorithms contained in SEPARATORS. Separation is % performed for all combinations of separators and mixtures. The % separation algorithm instances contained in SEPARATORS should % conform to the required format. Type % % MASSEF.doc % % for more information. % % The EXECUTE method performs evaluations of the algorithm % according to the data returned by the algorithm, and the % options provided to MASSEF. % % If the separation algorithm returns a signal, then % MASSEF.EXECUTE evaluates: % % - signal-to-noise ratio (SNR); % - signal-to-ideal-noise ratio (SINR) (the SNR with respect % to the signal reconstructed using the ideal binary and % ratio masks). % % In addition, if 'evalPEASS' is true, then PEASS and BSSeval % evaluation is performed. If 'evalSTOI' is true, then STOI % evaluation is performed. % % If the separation algorithm returns a mask, and if the ideal % mask dimensions match the estimated mask dimensions, then % MASSEF.EXECUTE evaluates: % % - ideal mask ratio (IMR) with respect to the ideal binary % and ratio masks. % % The EXECUTE method also calculates and evaluates the ideal % binary and ratio masks using the signal-level metrics utilised % for the algorithm evaluation. The masks are calculated using % the settings that are local to each mixture object. The masks % are then resynthesised using the inverse transform. For the % gammatone filterbank, a windowed-sinc function is used for % resynthesis in order to minimise the magnitude and phase % distortion. % % Lastly, the framework captures the estimated azimuth and % elevation of the targets and interferers, if they are returned. % % Once the evaluation is complete, the MASSEF object, which % contains the results data in MASSEF.results as a MASSEFresults % object, is saved to the file given by MASSEF.results_filename. currDir = pwd; % check the mixtures assert(isa(mixtures,'iosr.bss.mixture'),'MASSEF:execute:invalidMixtures','The MIXTURES input must contain one or more objects of class ''iosr.bss.mixture''.') if exist('separators','var')~=1 separators = {}; end % check separators have required property and method if ~isempty(separators) if ~iscell(separators) % force to cell array separators_old = separators; separators = cell(size(separators_old)); for s = 1:numel(separators) separators{s} = separators_old(s); end end for s = 1:numel(separators) % check each separator obj.check_separator(separators{s}) end end directory = fileparts(which([mfilename '.m'])); cd(directory) tempdir = [cd filesep 'massef_temp']; % ensure temp directory exists: if exist(tempdir,'dir')~=7 success = mkdir(tempdir); if ~success error('MASSEF:execute:mkdir','Unable to create directory %s. Please create it.',tempdir) end end %% IVs % enumerate combinations of mixtures and separators [IVs,obj.iterations] = obj.initialise_IVs(length(mixtures),length(separators)); %% PEASS options obj.PEASSoptions.destDir = [tempdir filesep]; obj.PEASSoptions.segmentationFactor = 1; if exist(obj.PEASSoptions.destDir,'dir')~=7 mkdir(obj.PEASSoptions.destDir) end %% Perform separations for each mixture and separator % create a waitbar obj.hWaitBar = obj.initWaitDisp(); if ~MASSEF.pctexists() || isempty(obj.parpool) % run in serial linear loop fprintf('Performing %d separations serially.\n',obj.iterations) nchars = obj.updateWaitDisp(0,0); for M = 1:obj.iterations % processing for this iteration sepnum = IVs(M).algo_num; mixnum = IVs(M).mixture_num; if isempty(separators) obj.process(mixtures(mixnum), ... {},mixnum,sepnum,M); else obj.process(mixtures(mixnum), ... separators{sepnum},mixnum,sepnum,M); end % Check to see if the cancel button was pressed if obj.breakWaitDisp() break; end % Update waitbar nchars = obj.updateWaitDisp(M,nchars); end else % execute asynchronously on parallel workers fprintf('Performing %d separations on %d workers.\n',obj.iterations,obj.parpool.NumWorkers) nchars = obj.updateWaitDisp(0,0); % work on BLOCKSIZE chunks of data at a time totalNumCompleted = 0; % overall complete count for display N = 1; % block start while N <= obj.iterations % work through blocks % do not exceed number of iterations blockMax = min(obj.iterations,N+obj.blocksize-1); % pass iterations to pool for M = blockMax:-1:N % backwards prevents growing the array sepnum = IVs(M).algo_num; mixnum = IVs(M).mixture_num; if isempty(separators) futures(M-N+1) = parfeval(obj.parpool,@obj.process,1,mixtures(mixnum), ... {},mixnum,sepnum,N+M-1); else futures(M-N+1) = parfeval(obj.parpool,@obj.process,1,mixtures(mixnum), ... separators{sepnum},mixnum,sepnum,N+M-1); end end % this cancels futures when cancelFutures is destroyed cancelFutures = onCleanup(@() cancel(futures)); % capture data returned by workers numCompleted = 0; while numCompleted < blockMax-N+1 % return completed iteration [~,data] = fetchNext(futures); % return data obj.results = [obj.results; data]; obj.results = obj.results.merge(); numCompleted = numCompleted + 1; % increment local counter totalNumCompleted = totalNumCompleted + 1; % increment total counter % Check to see if the cancel button was pressed br = obj.breakWaitDisp(); if br; break; end % Update waitbar nchars = obj.updateWaitDisp(totalNumCompleted,nchars); end % cancel the futures cancel(futures); clear futures; % be sure to break out of this loop too if br; break; end % move to the next data block N = N + obj.blocksize; end end % clean up delete(obj.hWaitBar); %% Wrap up obj.results.algorithmInfo(obj.IDEAL,'algorithmLabel','Ideal'); if ~isempty(separators) for n = 1:length(separators) obj.results.algorithmInfo(n,'algorithmLabel',separators{n}.label); end end for n = 1:length(mixtures) obj.results.mixtureInfo(n,'azi_sep',mixtures(n).azi_sep, ... 'elevation', mixtures(n).elevation, ... 'filename_t', mixtures(n).target.filename, ... 'filename_i', mixtures(n).int_fns, ... 'sofa_path', mixtures(n).sofa_path, ... 'target_azi', mixtures(n).target.azimuth, ... 'target_ele', mixtures(n).target.elevation, ... 'tir', mixtures(n).tir ... ); mixtures(n).clearCache(); mixtures(n).deleteFiles(); end obj.save(); save(obj.results_filename,'mixtures','separators','-append'); % delete temporary files delete(sprintf('%s*',[obj.PEASSoptions.destDir filesep])) fprintf('\n') cd(currDir); disp('MASSEF finished.'); end function evaluate(obj,originalFiles,estimateFile,tag,mixnum,sepnum,estnum) %EVALUATE Run the framework using input audio files % % OBJ.EVALUATE(ORIGINALFILES,ESTIMATEFILE) runs the framework % using the true sources provided in the wav files whose % filenames are contained in the cell array ORIGINALFILES (the % target source is the first one) and the estimate provided in % the wav file with filename ESTIMATEFILE. % % The method may be called as many times as desired. Use % OBJ.SAVE() when finished to save the framework and its data to % a file. % % OBJ.EVALUATE(ORIGINALFILES,ESTIMATEFILE,TAG) writes the char % array TAG to the results data. Use the tag to identify % different estimates in the results data. % % OBJ.EVALUATE(ORIGINALFILES,ESTIMATEFILE,TAG,MIXNUM) uses the % mixture number MIXNUM to identify the separation of a % particular mixture. MIXNUM is a key that can be used with % MASSEFRESULTS.MIXTUREINFO() in order to add information about a % particular mixture. % % OBJ.EVALUATE(ORIGINALFILES,ESTIMATEFILE,TAG,MIXNUM,SEPNUM) uses % the separator number SEPNUM to identify the separation from a % particular algorithm. SEPNUM is a key that can be used with % MASSEFRESULTS.ALGORITHMINFO() in order to add information about a % particular algorithm. % % OBJ.EVALUATE(ORIGINALFILES,ESTIMATEFILE,TAG,MIXNUM,SEPNUM,ESTNUM) % uses the estimate number ESTNUM to identify different estimates % from a given algorithm (e.g. a binary or soft mask output). % % See also IOSR.BSS.MASSEFRESULTS. if exist('tag','var')~=1 tag = ''; else assert(ischar(tag),'MASSEF:evaluate:invalidTag','TAG must be a char array') end if exist('mixnum','var')~=1 mixnum = 1; else assert(isscalar(mixnum),'MASSEF:evaluate:invalidMixnum','MIXNUM must be a scalar') end if exist('sepnum','var')~=1 sepnum = 1; else assert(isscalar(sepnum),'MASSEF:evaluate:invalidSepnum','SEPNUM must be a scalar') end if exist('estnum','var')~=1 estnum = 1; else assert(isscalar(estnum),'MASSEF:evaluate:invalidEstnum','ESTNUM must be a scalar') end [estimate,fs] = audioread(estimateFile); target = audioread(originalFiles{1}); interferers = zeros(1,size(target,2)); for i = 2:numel(originalFiles) int = audioread(originalFiles{i}); newlength = max(length(int),length(interferers)); int = obj.setlength(int,newlength); interferers = obj.setlength(interferers,newlength); interferers = interferers + int; end for C = 1:size(target,2) % iterate through each channel % SNR snr = iosr.bss.calcSnr(estimate(:,min(C,size(estimate,2))),target(:,C)); obj.results.input(mixnum,sepnum,estnum,'SNR',C,tag,snr); % STOI if obj.evalSTOI stoi = taal2011(target(:,C), estimate(:,min(C,size(estimate,2))), fs); obj.results.input(mixnum,sepnum,estnum,'STOI',C,tag,stoi); end end % PEASS if obj.evalPEASS obj.evaluatePEASS(estimateFile,originalFiles, ... mixnum,sepnum,estnum,1,tag); end end function save(obj) %SAVE Save the framework's data and results % % OBJ.SAVE() save the MASSEF framework object to the file % determined by OBJ.RESULTS_FILENAME. The method is called % automatically when using the EXECUTE() method; in this case, % the mixtures and separators are also saved. % Create results files if exist(obj.results_filename,'file')==2 % do not overwrite newfilename = obj.results_filename; while exist(newfilename,'file')==2 fprintf('\n') newfilename = input(... ['The results file ''' newfilename ''' already exists.\nPlease enter a new filename:\n'],'s'); end obj.results_filename = newfilename; end obj.saveDate = date; save(obj.results_filename,'obj'); fprintf('\n') disp(['File saved to: ' obj.results_filename]); end end % public methods methods (Static) function doc %DOC Display the framework documentation. try web(fullfile(MASSEF.dir, 'help_html', 'help_Index.html'), '-helpbrowser') catch web(fullfile(MASSEF.dir, 'help_html', 'help_Index.html')) end end function start %START Start the framework. % % MASSEF.START starts the framework and its dependencies, % adds the required folders to the Matlab path, and updates the % HTML help documentation. addpath(cd,... [MASSEF.dir filesep 'Library'],... [MASSEF.dir filesep 'Utilities'],... [MASSEF.dir filesep 'Utilities' filesep 'AMT'],... [MASSEF.dir filesep 'Utilities' filesep 'PEASS'],... [MASSEF.dir filesep 'Utilities' filesep 'PEASS' filesep 'gammatone'],... [MASSEF.dir filesep 'Stimuli']); SOFAstart(0); amtstart; d = pwd; cd([MASSEF.dir filesep 'help_html' filesep 'source']) publishHelp; cd(d); end function install %INSTALL Download and install MASSEF dependencies. % % MASSEF.INSTALL downloads and installs the MASSEF % dependencies. These dependencies are: % - The Auditory Modelling Toolbox % (http://amtoolbox.sourceforge.net); % - The Large Time-Frequency Analysis Toolbox % (http://ltfat.sourceforge.net); % - Perceptual Evaluation methods for Audio Source Separation % Toolkit (http://bass-db.gforge.inria.fr/peass/); % - IoSR Matlab Toolbox % (https://github.com/IoSR-Surrey/MatlabToolbox) % - SOFA API % (https://sourceforge.net/projects/sofacoustics/). currDir = pwd; directory = MASSEF.dir; cd(directory); %% download and install STOI amt_folder = [directory filesep 'Utilities' filesep 'AMT']; ltfat_folder = [directory filesep 'Utilities' filesep 'AMT' filesep 'thirdparty' filesep 'ltfat']; if ~(exist(amt_folder, 'dir') == 7) % AMT amt_filename = 'amtoolbox-0.9.7.zip'; websave(amt_filename,'http://vorboss.dl.sourceforge.net/project/amtoolbox/amtoolbox-0.9.7.zip'); unzip(amt_filename,amt_folder); amt_temp_folder = [amt_folder filesep 'release' filesep]; movefile([amt_temp_folder '*'],[amt_folder filesep]); delete(amt_filename) rmdir(amt_temp_folder,'s') % LTFAT ltfat_filename = 'ltfat-2.1.2.tgz'; websave(ltfat_filename,'http://netix.dl.sourceforge.net/project/ltfat/ltfat/2.0/ltfat-2.1.2.tgz'); untar(ltfat_filename,ltfat_folder); ltfat_temp_folder = [ltfat_folder filesep 'ltfat' filesep]; movefile([ltfat_temp_folder '*'],[ltfat_folder filesep]); delete(ltfat_filename) rmdir(ltfat_temp_folder,'s') else display(strcat('Found existing AMT Toolbox directory: ', amt_folder)) end %% download and install PEASS % install dir peass_folder = [directory filesep 'Utilities' filesep 'PEASS']; % PEASS if ~(exist(peass_folder, 'dir') == 7) peass_filename = 'PEASS-Software-v2.0.zip'; websave(peass_filename,'http://bass-db.gforge.inria.fr/peass/PEASS-Software-v2.0.zip'); unzip(peass_filename,peass_folder); peass_temp_folder = [peass_folder filesep 'PEASS-Software-v2.0' filesep]; movefile([peass_temp_folder '*'],[peass_folder filesep]); % clean up delete(peass_filename) rmdir(peass_temp_folder,'s') else display(strcat('Found existing PEASS directory: ', peass_folder)) end % Hair cell model adapt_filename = 'adapt_loop.zip'; websave(adapt_filename,'http://medi.uni-oldenburg.de/download/demo/adaption-loops/adapt_loop.zip'); unzip(adapt_filename,peass_folder); cd(peass_folder); compile; cd(directory); %% download and install IoSR Matlab Toolbox if ~MASSEF.gitExists % IoSR Matlab Toolbox if ~(exist([directory filesep 'Library' filesep '+iosr'], 'dir') == 7) iosrMTB = 'iosr.zip'; iosr_dir = [directory filesep 'Library']; websave(iosrMTB,'https://github.com/IoSR-Surrey/MatlabToolbox/archive/master.zip'); unzip(iosrMTB,iosr_dir); movefile([iosr_dir filesep 'MatlabToolbox-master' filesep '*'],[iosr_dir filesep]); delete(iosrMTB) rmdir([iosr_dir filesep 'MatlabToolbox-master'],'s') else display('Found existing IoSR Matlab Toolbox directory') end end iosr.install; %% Remaining clean up delete(adapt_filename) cd(currDir); disp('MASSEF successfully installed.') end end % Static public methods methods (Static, Hidden) function e = gitExists %GITEXISTS Check if install is a git repo e = exist([MASSEF.dir filesep '.git'],'dir')==7; end end methods (Access = private) function r = process(obj,mix,separator,mixnum,sepnum,iteration) %PROCESS Main callback for performing the separation and analysis %% Create mixture % write mixture, target and interferers mix.write(sprintf('%smix-%d.wav',obj.PEASSoptions.destDir,mixnum)); originalFiles = {mix.filename_t, mix.filename_i}; % create mixture signal mixture = mix.signal; %% Analyse separator % calculate ideal outputs ibm = mix.ibm; irm = mix.irm; output_ibm = mix.applyMask(ibm); output_irm = mix.applyMask(irm); if ~isempty(separator) % calculate separator output [signals,masks,est_azis,est_eles] = separator.separate(mixture); % apply mask if signals are not calculated if isempty(signals) && ~isempty(masks) for E = 1:size(masks,4) signals(:,:,E) = mix.applyMask(masks(:,:,:,E)); end end % Evaluate signals if ~isempty(signals) for E = 1:size(signals,3) estimateFileBase = sprintf('%ssignal-%d',obj.PEASSoptions.destDir,iteration); estimateFile = sprintf('%s.wav',estimateFileBase); audiowrite(estimateFile,iosr.dsp.audio.normalize(signals(:,:,E)),mix.fs); obj.evaluate(originalFiles,estimateFile,... separator.estTag{E},mixnum,sepnum,E); % SINR for C = 1:size(mixture,2) sinr = iosr.bss.calcSnr(signals(:,C,E),output_irm(:,C)); obj.results.input(mixnum,sepnum,E,'SINR',C,separator.estTag{E},sinr) end % delete temp files delete(estimateFile); end end % estimated azimuths if ~isempty(est_azis) for s = 1:length(est_azis) if s==1 obj.results.input(mixnum,sepnum,1,'est_azi_target',1,[],est_azis(s)) else obj.results.input(mixnum,sepnum,1,sprintf('est_azi_int_%02d',s-1),1,[],est_azis(s)) end end end % estimated elevation if ~isempty(est_eles) for s = 1:length(est_eles) if s==1 obj.results.input(mixnum,sepnum,1,'est_ele_target',1,[],est_eles(s)) else obj.results.input(mixnum,sepnum,1,sprintf('est_ele_int_%02d',s-1),1,[],est_eles(s)) end end end % only do mask comparisons when the masks are equal size masks_size = size(masks); ideal_size = size(ibm); if isequal(masks_size(1:2),ideal_size(1:2)) for E = 1:size(masks,4) % iterature through each estimate/mask for C = 1:size(mixture,2) % iterate through each channel maskC = masks(:,:,min(C,size(masks,3)),E); % IMR - binary imrb = iosr.bss.calcImr(maskC,ibm(:,:,C)); obj.results.input(mixnum,sepnum,E,'IMR (IBM)',C,separator.estTag{E},imrb) % IMR - ratio imrr = iosr.bss.calcImr(maskC,irm(:,:,C)); obj.results.input(mixnum,sepnum,E,'IMR (IRM)',C,separator.estTag{E},imrr) end end end end %% Analyse ideal masks if sepnum==1 % evaluations of ideal masks ibmFile = sprintf('%sibm-%d.wav',obj.PEASSoptions.destDir,iteration); audiowrite(ibmFile,iosr.dsp.audio.normalize(output_ibm),mix.fs); irmFile = sprintf('%sirm-%d.wav',obj.PEASSoptions.destDir,iteration); audiowrite(irmFile,iosr.dsp.audio.normalize(output_irm),mix.fs); obj.evaluate(originalFiles,ibmFile,... 'Binary',mixnum,obj.IDEAL,1); obj.evaluate(originalFiles,irmFile,... 'Ratio',mixnum,obj.IDEAL,2); % delete temp files delete(ibmFile); delete(irmFile); end r = obj.results; end function evaluatePEASS(obj,estimateFile,originalfiles, ... mixnum,sepnum,estnum,channelnum,esttag) %EVALUATE Evaluate resynthesised signals against ground truth % calculate metrics PEASS_output = PEASS_ObjectiveMeasure(originalfiles,estimateFile,obj.PEASSoptions); % write data to outputs obj.results.input(mixnum,sepnum,estnum,'OPS',channelnum,esttag,PEASS_output.OPS) obj.results.input(mixnum,sepnum,estnum,'TPS',channelnum,esttag,PEASS_output.TPS) obj.results.input(mixnum,sepnum,estnum,'IPS',channelnum,esttag,PEASS_output.IPS) obj.results.input(mixnum,sepnum,estnum,'APS',channelnum,esttag,PEASS_output.APS) obj.results.input(mixnum,sepnum,estnum,'SDR',channelnum,esttag,PEASS_output.SDR) obj.results.input(mixnum,sepnum,estnum,'SAR',channelnum,esttag,PEASS_output.SAR) obj.results.input(mixnum,sepnum,estnum,'SIR',channelnum,esttag,PEASS_output.SIR) obj.results.input(mixnum,sepnum,estnum,'ISR',channelnum,esttag,PEASS_output.ISR) [pathstr,name] = fileparts(estimateFile); delete(fullfile(pathstr,[name '_true.wav'])); delete(fullfile(pathstr,[name '_eTarget.wav'])); delete(fullfile(pathstr,[name '_eInterf.wav'])); delete(fullfile(pathstr,[name '_eArtif.wav'])); end function nchars = updateWaitDisp(obj,numComplete,nchars) %UPDATEWAITDISP Update the wait display fraction = numComplete/obj.iterations; if ~isempty(obj.hWaitBar) % update waitbar if it exists waitbar(fraction, obj.hWaitBar); end fprintf(1,repmat('\b',1,nchars)); % delete old message if numComplete==obj.iterations format = '%.0f'; % special format for 100% else format = '%.1f'; % normal format end % display message and return number of chars to delete for next message nchars = fprintf(1,[format '%% complete.'],100*fraction); end function b = breakWaitDisp(obj) %BREAKWAITDISP Determine whether to break the calculations b = false; % don't by default if ~isempty(obj.hWaitBar) % if waitbar exists if getappdata(obj.hWaitBar, 'Cancelled') % determine if "Cancel" was clicked b = true; % break if so end end if b % display message if breaking fprintf('\nMASSEF cancelled.\n'); end end end % private methods methods (Static, Access = private) function options = validate_options(options) %VALIDATE_OPTIONS Validate the options structure input to this function % check format if numel(options)>1 error('MASSEF:validate_options:invalidInput','The input to MASSEF should be a 1x1 structure. Cell-array fields should be encapsulated within a single cell.') end fields_in = fieldnames(options); % default values if MASSEF.pctexists() default_parpool = gcp('nocreate'); else default_parpool = []; end default_values = struct( ... 'evalPEASS',false, ... 'evalSTOI',false, ... 'parpool',default_parpool, ... 'results_filename',[MASSEF.dir filesep 'Results' filesep 'results.mat'], ... 'blocksize',128 ... ); def_fields = fieldnames(default_values); % check for invalid options for r = 1:length(fields_in) assert(any(strcmpi(fields_in{r},def_fields)),'MASSEF:validate_options:invalidOption',['Invalid option ''' fields_in{r} ''' specified.']) end % write defaults for r = 1:numel(def_fields) field_name = def_fields{r}; if ~isfield(options,field_name) options.(field_name) = default_values.(field_name); end end end function [IVs,iterations] = initialise_IVs(numMixtures,numSeparators) %INITIALISE_IVs Initialise experiment results structure % Independent variables space IV_space = [numMixtures max(1,numSeparators)]; iterations = prod(IV_space); % total number of results assert(numMixtures>0,'MASSEF:initialise_IVs:noMixtures','No mixtures have been specified') %assert(numSeparators>0,'No separators have been specified') IVs = struct('algo_num',[],'mixture_num',[]); % fill IV cells with data for N = 1:iterations [n,p] = ind2sub(IV_space,N); IVs(N).algo_num = p; IVs(N).mixture_num = n; end end function check_separator(obj) %CHECK_SEPARATOR Check separator meets requirements assert(isprop(obj,'label'),'MASSEF:check_separator:invalidSepNoLabel',['The object of class ''' class(obj) ''' does not have the required property ''label''']) assert(ischar(obj.label),'MASSEF:check_separator:invalidSepLabelProp',['The ''label'' property of the ''' class(obj) ''' object should return a char array']) assert(isprop(obj,'estTag'),'MASSEF:check_separator:invalidSepNoEstTag',['The object of class ''' class(obj) ''' does not have the required property ''estTag''']) assert(iscellstr(obj.estTag),'MASSEF:check_separator:invalidSepEstTagProp',['The ''estTag'' property of the ''' class(obj) ''' object should return a cell array of strings']) assert(ismethod(obj,'separate'),'MASSEF:check_separator:invalidSepNoSepMethod',['The object of class ''' class(obj) ''' does not have the required method ''separate''']) end function e = pctexists %PCTEXISTS Determine whether the parallel computing toolbox exists if isempty(ver('distcomp')) e = false; else e = true; end end function h = initWaitDisp %INITWAITDISP Initialise the waiting display if usejava('jvm') % only call waitbar if JVM is running h = waitbar(0, 'MASSEF processing...', 'CreateCancelBtn', ... @(src, event) setappdata(gcbf(), 'Cancelled', true)); setappdata(h, 'Cancelled', false); else % do nothing h = []; end end function y = setlength(x,signal_length) %SETLENGTH Crop or zero-pad signal to specified length d = size(x); if size(x,1)>signal_length % need to crop subsidx = [{1:signal_length} repmat({':'},1,ndims(x)-1)]; y = x(subsidx{:}); elseif size(x,1)<signal_length % need to zero-pad y = [x; zeros([signal_length-size(x,1),d(2:end)])]; else % do nothing y = x; end end end % static private methods end