image thumbnail
from STFT, MDCT and inverses. Onset and pitch detection by Antoine Liutkus
Short Time Fourier Transform, MDCT and their inverse. CQT. Onset and Pitch detection.

Signal
classdef Signal < handle
%-------------------------------------------------------------------------
% Class name : Signal
%-------------------------------------------------------------------------
% Description : handles basic Signal operations and transforms. 
%               * wave loading
%               * STFT, with any overlap ratio, any frames length,
%                 any weighting function
%               * MDCT and its inverse
%               * Constant Q Transform (by Jacques Prado)
%               * splitting into frames
%               * pitch detection (for harmonic sounds)
%               * onset detection
%
% Main properties that are read/write 
%   * s : signal
%   * windowLength (ms)
%   * nfft (samples)
%   * overlapRatio (>=0 and <1)
%   * S : stft data
%   * Smdct : MDCT data
%   * CQTBinsPerOctave : number of bins per octave for cQt
%   * CQTMinFreq : min freq for the cQt
%   * CQTMaxFreq : max freq for the cQt
%   * CQTAlign : where to center cQt lobe weights, either 'l' for left or
%               'c' for center
%   * weightingFunction: handle to a function taking the number of points
%     N as an argument and returning the Nx1 weighting window
%
% Main properties that are read only : 
%   * sLength : signal length
%   * nChans : number of channels
%   * nfftUtil : number of bins in the positive frequency domain
%   * framesPositions, nFrames : positions and number of frames
%   * sWin, sWeights : windowed data
%   * Sq : cQt data
%
% examples : 
%   sig = Signal('myfile.wav'); %creates signal for a wave file
%   sig.windowLength = 70;      %sets windows of 70ms
%   sig.overlapRatio = 0.8;     %sets overlap ratio 0 < overlapRatio < 1
%   sig.STFT;                   %performs STFT
%   sig.CQT;                    %same for CQT
%   sig.MDCT;                   %same for MDCT (note that overlap is set to
%                               %               50 percents)
%   mySTFT = sig.S;
%   myMDCT = sig.S;
%   myCQT = sig.Sq
%
%   %modifying s.S for example
%   ...
%
%   waveform = sig.iSTFT();     % gets inverse transform
%
% Note that :
%   * all properties are automatically set relevantly in case of
%     modifications. for example, when nfft is set, windowLength is changed
%     accordingly
%   * STFT, MDCT and CQT produce exactly aligned data
%-------------------------------------------------------------------------
%
% Copyright (c) 2012, Antoine Liutkus, Telecom ParisTech
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without 
% modification, are permitted provided that the following conditions 
% are met:
%
%    *  Redistributions of source code must retain the above copyright 
%       notice, this list of conditions and the following disclaimer.
%    *  Redistributions in binary form must reproduce the above copyright 
%       notice, this list of conditions and the following disclaimer in 
%       the documentation and/or other materials provided with the 
%       distribution.
%    *  Neither the name of Telecom ParisTech nor the names of its 
%       contributors may be used to endorse or promote products derived 
%       from this software without specific prior written permission.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
% HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
% SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
% TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
% PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
% LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
%-------------------------------------------------------------------------
    methods 
        %Constructor
        function self = Signal(varargin)
            %signal properties
            self.singlePrecision = 0;
            self.s = [];
            self.fs = 44100;
            self.sLength = 0;
            self.nChans = 0;
            self.weightingFunction = @hamming;
            
            %STFT properties
            self.S = [];
            self.windowLength = 60;
            self.nfft = 0;
            self.nfftUtil = 0;
            self.overlapRatio = 0.5;
            self.framesPositions = [];
            self.nFrames = 0;
            self.weightingWindow = [];
            self.overlap = 0;
            
            %MDCT properties
            self.Smdct = [];
            
            %CQT properties
            self.CQTKernelComputationNeeded = 1;
            self.CQTSparseKernel = [];
            self.CQTBinsPerOctave = 12;
            self.CQTMinFreq=97.9999;
            self.CQTMaxFreq = self.fs/2;
            self.CQTAlign = 'c';

            %Windowing properties
            self.sWin = [];
            self.sWeights = [];
            
            %Properties listeners
            self.propertiesListeners = {};
            self.propertiesListeners{end+1} = addlistener(self,'s','PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));
            self.propertiesListeners{end+1} = addlistener(self,'singlePrecision','PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));
            self.propertiesListeners{end+1} = addlistener(self,'fs','PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));
            self.propertiesListeners{end+1} = addlistener(self,'weightingFunction','PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));
            self.propertiesListeners{end+1} = addlistener(self,'windowLength' ,'PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));
            self.propertiesListeners{end+1} = addlistener(self,'nfft' ,'PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));
            self.propertiesListeners{end+1} = addlistener(self,'overlapRatio','PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));
            self.propertiesListeners{end+1} = addlistener(self,'S','PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));    
            self.propertiesListeners{end+1} = addlistener(self,'CQTBinsPerOctave','PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));    
            self.propertiesListeners{end+1} = addlistener(self,'CQTMinFreq','PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));    
            self.propertiesListeners{end+1} = addlistener(self,'CQTMaxFreq','PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));    
            self.propertiesListeners{end+1} = addlistener(self,'CQTAlign','PostSet',@(src,evnt)Signal.handlePropertyEvents(self,src,evnt));    
            
            switch nargin
                case 0
                    return;
                case 1
                    switch class(varargin{1})
                        case 'char'
                            %varargin{1} is a file
                            self.LoadFromFile(varargin{1});
                        case 'Signal'
                            self = varargin{1}.copy;
                    end
                case 2
                    %varargin{1} is a signal and varargin{2} is the
                    %sampling frequency
                    self.LoadWF(varargin{1},varargin{2});

            end
        end
        
        function SigOut = getOnsets(self,fMin, fMax)
            %This function returns a vector of length nFrames, containing
            % an onset detection in the signal between frequencies fMin and
            % fMax. It uses the complex spectral difference method.
            if isempty(self.S)
                self.STFT
            end
            
            IFmin = max(1,round(fMin/self.fs*self.nfft));
            IFmax = round(min(fMax/2,fMax/self.fs*self.nfft));
            
            SigFrame = self.S(IFmin:IFmax,:,:);
            
            LenWindow = size(SigFrame,1);
            nCanaux = size(SigFrame,3);
            Nbre = size(SigFrame,2);
            SigOut = zeros(size(SigFrame,2),nCanaux);
            
            for canal = 1:nCanaux
                SigOutInst = zeros(LenWindow,1);
                CurrPhi = angle(SigFrame(:,1,canal));
                SigOut(1) = 0;
                Phi = (unwrap(angle(SigFrame(:,:,canal))));
                DevPhi = [zeros(LenWindow,2) (Phi(:,3:end) - 2*Phi(:,2:end-1) +...
                        Phi(:,1:end-2))];
                    for n = 2:Nbre
                            SigOutInst = sqrt(abs(SigFrame(:,n-1)).^2 + abs(SigFrame(:,n)).^2 -2*abs(SigFrame(:,n)).*abs(SigFrame(:,n-1)).*cos(DevPhi(:, n)));
                            SigOut(n,canal) = sum(SigOutInst);
                    end
                SigOut(:,canal) = 1/max(abs(SigOut(:,canal))).*SigOut(:,canal);
            end
        end
        
        function [pitchs, pitchCandidates, harmonicTemplates, pitchScores]= mainPitch(self, fMin, fMax,pitchStep,tolerance)
            %  [pitchs, pitchCandidates, harmonicTemplates, pitchScores]= mainPitch(self, fMin, fMax,pitchStep,tolerance)
            %  Computes dominant pitch trajectory between fMin and fMax.
            %  tolerance indicates in Hz the allowed variation of the main
            %  pitch from one frame to the next.
            % Returns:
            % * pitchs: a vector giving the detected pitch in Hz for each
            % frame.
            % * pitchCandidates: pitch frequencies used as candidates (Cx1
            %   vector)
            % * harmonicTemplates: template spectrum for each candidate
            %   (FxC) matrix
            % * pitchScores: matrix indicating the score for each candidate
            %   and each frame. (CxT) matrix
            if nargin<5
                tolerance=10;
            end
            if nargin<4
                pitchStep=1;
            end
            if isempty(self.S)
                self.STFT();
            end
            pitchCandidates=fMin:pitchStep:fMax;
            harmonicTemplates=self.KLGLOTTModel(pitchCandidates).^2;
            nPitchs = length(pitchCandidates);
            filterWidth = round(tolerance/pitchStep);
            pitchScores = zeros(nPitchs, self.nFrames);
            prevScore= misc.normalize(ones(nPitchs,1));
            for n = 1:self.nFrames
                if rem(n,round(self.nFrames/10)) == 1
                    disp(sprintf('pitch detection: %d%%',round(n/self.nFrames*100)));
                    drawnow;
                end
                likelihoods = misc.normalize(exp(harmonicTemplates'*(abs(self.S(:,n,1)).^2)));
                pitchScores(:,n) = misc.normalize(misc.normalize(misc.smooth(prevScore,filterWidth)).*likelihoods);
                prevScore = pitchScores(:,n);
            end
            [~, argPitch] = max(pitchScores,[],1);
            pitchs = pitchCandidates(argPitch);
        end
        
        %Data loaders
        function LoadFromFile(self, file)
            [self.s, self.fs] = wavread(file);
            [self.sLength, self.nChans] = size(self.s);
        end 
        function LoadWF(self, waveform, fs)
            self.s = waveform;
            self.fs = fs;
            [self.sLength, self.nChans] = size(self.s);
        end
        
        %Transforms
        function S = STFT(self)
            %Builds Signal STFT, puts it in self.S and returns it.
            weighted_frames = self.split(0);
            self.S = fft(weighted_frames);  
            if self.singlePrecision
                self.S = single(self.S);
            end
            if nargout
                S = self.S;
            end
        end
        
        function V = specgram(self,nSteps)
            %Builds Signal spectrogram, 
            % through averaging nSteps delayed spectrograms
            if nargin == 1
                nSteps = 20;
            end
            weighted_frames = self.split(0);
            V = abs(fft(weighted_frames)).^2;  
            for index = 1:nSteps
                disp(sprintf('specgram, pass %d / %d',index,nSteps));drawnow
                weighted_frames = self.split(index);
                V = V*(1- 1/(index+1)) +1/(index+1)* abs(fft(weighted_frames)).^2;  
            end
            if self.singlePrecision
                V = single(self.S);
            end
        end
        
        function s = iSTFT(self)
            %Computes inverse STFT, puts it in self.s and returns it.
            s = [];
            if isempty(self.S)
                return;
            end
            tempSignal = real(ifft(self.S));
    
            tempLength = self.framesPositions(end)+self.nfft - 1;     
            oldLength = self.sLength;       
            
            s_temp=zeros(tempLength,self.nChans);
            W = zeros(tempLength,self.nChans);

            for index_frame=1:self.nFrames
                    for index_chan=1:self.nChans
                        s_temp(self.framesPositions(index_frame):(self.framesPositions(index_frame)+self.nfft-1),index_chan)= ...
                            s_temp(self.framesPositions(index_frame):(self.framesPositions(index_frame)+self.nfft-1),index_chan) + tempSignal(:,index_frame,index_chan).*self.weightingWindow;
                        W(self.framesPositions(index_frame):(self.framesPositions(index_frame)+self.nfft-1),index_chan) = ...
                            W(self.framesPositions(index_frame):(self.framesPositions(index_frame)+self.nfft-1),index_chan) + self.weightingWindow.^2;
                    end
            end
            s_temp = (s_temp./W);      
            self.s = s_temp(1:oldLength,:);

            if nargout
                s = self.s;
            end
        end
        
        function Smdct = MDCT(self)
            self.overlapRatio = 0.5;
            self.nfft = round(self.nfft/2)*2;
            N = self.nfft;
            J = N/2;
            self.suspendListeners();
            self.s = [zeros(N,self.nChans); self.s; zeros(2*N,self.nChans)];
            self.activateListeners();
            K = floor(size(self.s,1)/J)-1;
            persistent G
            if (isempty(G)) || (size(G, 2) ~= N)
                G = zeros(J,N);
                win = sqrt(Signal.hann_nonzero(N));
                for i = 0:J-1,
                     G(i+1,:) = win.*(sqrt(2/J)*cos((2*i+1)*(2*(0:2*J-1)-J+1)*pi/(4*J)));   
                end
            end
            k = [1:K]';
            indices = ((k-1)*J*ones(1,2*J) + ones(K,1)*[1:2*J])';
            for chan = 1:self.nChans
                temp = self.s(:,chan); 
                self.Smdct(:,:,chan) = G*temp(indices);
            end
            self.nFrames = K;
            if nargout
                Smdct = self.Smdct;
            end
            self.suspendListeners();
            self.s = self.s((self.nfft+1):(self.nfft+self.sLength),:);
            self.activateListeners();
        end
        
        
   function s = iMDCT(self)
            %Computes inverse MDCT, puts it in self.s and returns it.
            s = [];
            if isempty(self.Smdct)
                return;
            end
            persistent invG
            J = self.nfft/2;
            if (isempty(invG)==1) || (size(invG, 1) ~= self.nfft)
                invG = zeros(J,self.nfft);
                win = sqrt(Signal.hann_nonzero(self.nfft));
                for i = 0:J-1,
                    invG(i+1,:) = win.*(sqrt(2/J)*cos((2*i+1)*(2*(0:2*J-1)-J+1)*pi/(4*J)));
                end
                invG = invG';
            end
            signal = zeros(J*(self.nFrames+1), self.nChans);
            for chan = 1:self.nChans
                for t = 1:self.nFrames
                    T = reshape(invG*self.Smdct(:,t,chan),self.nfft/2,2);
                    T2 = [T(:,1:2:end),zeros(J,1)]+[zeros(J,1),T(:,2:2:end)];
                    signal_frame = T2(:);                    
                    indx_frame = (1:self.nfft) + (self.nfft/2)*(t-1);
                    signal(indx_frame,chan) = signal(indx_frame,chan) + signal_frame;                    
                end;
            end
            if ~isempty(self.sLength)
                signal = signal((self.nfft+1):(self.nfft+self.sLength),:);
            end;

            self.s = signal;
            if nargout
                s = self.s;
            end
        end        
        
        
        function s = unsplit(self,tempSignal)
            tempLength = self.framesPositions(end)+self.nfft - 1;     
            oldLength = self.sLength;       
            
            s_temp=zeros(tempLength,self.nChans);
            W = zeros(tempLength,self.nChans);

            for index_frame=1:self.nFrames
                    for index_chan=1:self.nChans
                        s_temp(self.framesPositions(index_frame):(self.framesPositions(index_frame)+self.nfft-1),index_chan)= ...
                            s_temp(self.framesPositions(index_frame):(self.framesPositions(index_frame)+self.nfft-1),index_chan) + tempSignal(:,index_frame,index_chan).*self.weightingWindow;
                        W(self.framesPositions(index_frame):(self.framesPositions(index_frame)+self.nfft-1),index_chan) = ...
                            W(self.framesPositions(index_frame):(self.framesPositions(index_frame)+self.nfft-1),index_chan) + self.weightingWindow.^2;
                    end
            end
            s_temp = (s_temp./W);            
            s = s_temp(1:oldLength,:);
        end
        
        function Sq = CQT(self)
                if self.CQTKernelComputationNeeded
                    self.ComputeSparseKernel;
                end
                nfftKernel = size(self.CQTSparseKernel,1);

                %Compute positions and number of STFT frames
                stepSTFT = self.nfft-self.overlap;
                framesPositionsSTFT = 1:stepSTFT:self.sLength;
                framesPositionsSTFT((framesPositionsSTFT + self.nfft -1) > self.sLength ) = [];
                framesPositionsSTFT(end+1) = framesPositionsSTFT(end) + self.nfft;
                nFramesSTFT = length(framesPositionsSTFT);                
                framesCentersSTFT = framesPositionsSTFT + self.nfft/2;
                

                %Compute CQT frames start so that frames centers are the
                %same than for the STFT
                framesPositionsCQT = round(framesCentersSTFT - nfftKernel/2);
                tempSignal = self.s;
                if framesPositionsCQT(1) < 1
                    tempSignal = [zeros(-framesPositionsCQT(1) + 1, self.nChans) ; tempSignal];
                    framesPositionsCQT = framesPositionsCQT - framesPositionsCQT(1) + 1;
                end
                lEnd = (framesPositionsCQT(end) + nfftKernel - 1) - length(tempSignal);
                finalZeroPadding = zeros(lEnd, self.nChans);
                tempSignal = [tempSignal ; finalZeroPadding];
                win = hamming(nfftKernel);
                win = win(:);
                
                %Compute CQT
                self.Sq = zeros(size(self.CQTSparseKernel,2),nFramesSTFT,self.nChans);
                disp('Computing CQT...')
                for index=1:nFramesSTFT
                    if mod(index, 100) == 1
                        disp(sprintf('     CQT : %0.2f percents',index/nFramesSTFT*100));
                    end                        
                    for index_chan = 1:self.nChans
                        self.Sq(:,index,index_chan) = self.CQTSparseKernel' ...
                                                    * fft(tempSignal(framesPositionsCQT(index):framesPositionsCQT(index)+nfftKernel-1,index_chan).*win);
                    end
                end
                disp('finished');
                
                if nargout 
                    Sq = self.Sq;
                end
        end
        
        

        
        function [sWin, sWeights] = split(self,store,initialPos)
            %default value
            if (nargin <= 1)
                store = 1;
            end
            if nargin <= 2
                initialPos = 1;
            end
            
            %computing splitting parameters
            step = self.nfft-self.overlap;
            self.framesPositions = initialPos:step:self.sLength;
            self.framesPositions((self.framesPositions + self.nfft -1) > self.sLength ) = [];
            self.framesPositions(end+1) = self.framesPositions(end) + step;
            self.nFrames = length(self.framesPositions);
            
            %initializing splitted signal matrix
            sWin = zeros(self.nfft,self.nFrames, self.nChans);
            
            %Initializing weighting window
            win = self.weightingWindow(:)*ones(1,self.nChans);

            %Initializing weighting signal
            tempLength = self.framesPositions(end)+self.nfft - 1;     
            sWeights = zeros(tempLength,1);
            
            %For each frame, compute signal
            for index=1:(self.nFrames - 1)
                sWin(:,index,:) = self.s(self.framesPositions(index):self.framesPositions(index)+self.nfft-1,:).*win;
                sWeights(self.framesPositions(index):(self.framesPositions(index)+self.nfft-1)) = ...
                            sWeights(self.framesPositions(index):(self.framesPositions(index)+self.nfft-1)) + self.weightingWindow;                
            end
            
            %Handle last frame on which zeropadding may be necessary
            lEnd = (self.framesPositions(end) + self.nfft - 1) - self.sLength;
            finalZeroPadding = zeros(lEnd, self.nChans);
            sWin(:,end,:) = [self.s(self.framesPositions(end):end,:) ; finalZeroPadding].*win;
            
            %handle last frame for weighting signal
            sWeights(self.framesPositions(end):(self.framesPositions(end)+self.nfft-1)) = ...
                        sWeights(self.framesPositions(end):(self.framesPositions(end)+self.nfft-1)) + self.weightingWindow;                

            %stores result if necessary
            if store 
                self.sWin = sWin;
                self.sWeights = sWeights;
            end
            if nargout == 0
                clear sWin
                clear sWeights
            end
                
        end
        %copy
        function copied = copy(self)
            copied = Signal;
            copied.suspendListeners
            Meta = ?Signal;
            for index = 1:length(Meta.Properties)
                if strcmp(Meta.Properties{index}.Name, 'propertiesListeners')
                    continue
                end
                eval(['copied.' (Meta.Properties{index}.Name) '= eval([''self.'' (Meta.Properties{index}.Name)]);']);
            end
            copied.activateListeners
        end
        
        function stripCanal(self, channel)
            self.suspendListeners;
            toKeep = setdiff(1:self.nChans,channel);
            self.s = self.s(:,toKeep);
            if ~isempty(self.S)
                self.S = self.S(:,:,toKeep);
            end
            if ~isempty(self.Sq)
                self.Sq = self.Sq(:,:,toKeep);
            end
            if ~isempty(self.sWin)
                self.sWin = self.sWin(:,:,toKeep);
            end
            self.nChans = length(toKeep);
            self.activateListeners;
        end
    end
        
    properties (Access=public, SetObservable=true)
        %Waveform, sampling frequency
        s, fs, singlePrecision
        
        %weighting parameters
        weightingFunction
        
        %STFT parameters and data
        windowLength, nfft, overlapRatio
        S
        
        %CQT parameters
        CQTBinsPerOctave, CQTMinFreq, CQTMaxFreq, CQTAlign
        
        %MDCT
        Smdct
    end
    
    properties (SetAccess = private, GetAccess = public, SetObservable=false)
        %waveform properties
        sLength, nChans, nfftUtil

        %STFT parameters and data
        framesPositions, nFrames
        
        %Windowed data
        sWin, sWeights
        
        %CQT data
        Sq
    end
    properties (Access = private)
        %Properties listeners
        propertiesListeners
        
        %STFT 
        weightingWindow, overlap
        
        %CQT
        CQTKernelComputationNeeded, CQTSparseKernel
    end
    methods (Static)
        function handlePropertyEvents(self,src,evnt)
            switch src.Name 
                case 'singlePrecision'
                    self.S = single(self.S);
                    self.Sq = single(self.Sq);
                case 's' 
                    [self.sLength, self.nChans] = size(self.s);
                case 'fs' 
                    self.nfft = round(self.fs*self.windowLength/1000);
                    self.nfftUtil = round(self.nfft/2);
                    self.overlap = round(self.nfft*self.overlapRatio); 
                    self.CQTKernelComputationNeeded = 1;
                case 'windowLength' 
                    self.nfft = round(self.fs*self.windowLength/1000);
                    self.nfftUtil = round(self.nfft/2);
                    self.overlap = round(self.nfft*self.overlapRatio); 
                    self.CQTKernelComputationNeeded = 1;
                case 'nfft' 
                    self.windowLength = (self.nfft*1000/self.fs);
                    self.overlap = round(self.nfft*self.overlapRatio); 
                    self.CQTKernelComputationNeeded = 1;
                case 'overlapRatio'
                    self.overlap = round(self.nfft*self.overlapRatio); 
                case 'S'
                    self.nFrames = size(self.S,2);
                    self.nfft = size(self.S,1);
                    self.nfftUtil = round(self.nfft/2);
                    self.nChans = size(self.S,3);
                case 'CQTBinsPerOctave'
                    self.CQTKernelComputationNeeded = 1;
                case 'CQTMinFreq'
                    self.CQTKernelComputationNeeded = 1;
                case 'CQTMaxFreq'
                    self.CQTKernelComputationNeeded = 1;
                case 'CQTAlign'
                    self.CQTKernelComputationNeeded = 1;
            end
        
            if self.nfft
                nArgWeightingFunc = nargin(self.weightingFunction);
                if (nArgWeightingFunc<0)||(nArgWeightingFunc==1)
                    self.weightingWindow = feval(self.weightingFunction, self.nfft);
                elseif nArgWeightingFunc==2
                    self.weightingWindow = feval(self.weightingFunction, self.nfft,self.overlapRatio);
                else
                    error('weighting function badly defined.');
                end
            end
        end  
        function w = hann_nonzero(N)
            n = 0:(N-1);
            phi0 = pi / N;
            Delta = 2 * phi0;
            w = 0.5 * (1 - cos( n*Delta + phi0));
        end
    end
    methods (Access = private)
        function suspendListeners(self)
            for index = 1:length(self.propertiesListeners)
                self.propertiesListeners{index}.Enabled = false;
            end
        end
        function activateListeners(self)
            for index = 1:length(self.propertiesListeners)
                self.propertiesListeners{index}.Enabled = true;
            end
        end
       function harmonicTemplates = KLGLOTTModel(self,pitchs)
            nPitchs = length(pitchs);
            harmonicTemplates = zeros(self.nfft,nPitchs);
            AV = 1;
            Oq = 0.05;
            for index = 1:nPitchs
                %build glottal pulse pattern
                T = round(self.fs/pitchs(index));
                HH = zeros(T,1);
                t = 1:round(T*Oq);
                HH(t) = ((27*AV)/(4*T*Oq^2)*(t-1).^2-(27*AV)/(4*T^2*Oq^3)*(t-1).^3);
                nRepeat = ceil(self.nfft/T);
                H = repmat(HH, nRepeat,1);
                H = H(1:self.nfft);
                H = H - mean(H);
                F = abs(fft(hann(self.nfft).*H,self.nfft));
                harmonicTemplates(:,index) = F(:)/sum(abs(F));
            end      
        end        
        function ComputeSparseKernel(self)
            disp(sprintf('compute CQT kernel on freqs %0.2f to %0.2f with %i bins per octave ...',self.CQTMinFreq,self.CQTMaxFreq,self.CQTBinsPerOctave));
            thresh = 0.0075;
            Q = 1 / (2^(1/self.CQTBinsPerOctave)-1);
            K = ceil(self.CQTBinsPerOctave*log2(self.CQTMaxFreq/self.CQTMinFreq));
            fftLen = 2^nextpow2(ceil(Q*self.fs/self.CQTMinFreq));
            self.CQTSparseKernel = [];
            im = sqrt(-1);
            tempKernel = zeros(fftLen,1);
            for k = K:-1:1;
                len = ceil( Q * self.fs / (self.CQTMinFreq*2^((k-1)/self.CQTBinsPerOctave)));
                switch self.CQTAlign
                    case 'l'
                        tempKernel =[hamming(len,'periodic')/sum(hamming(len,'periodic')); zeros(fftLen-len,1)];
                        tempKernel = tempKernel.*exp(2*pi*im*Q*(0:fftLen-1)'/len);
                    case 'c'
                        if rem(len,2)==1
                            len=len-1;
                        end;
                        index = fftLen/2 - len/2;
                        tempKernel =[zeros(index,1) ; 
                                    hamming(len,'periodic')/sum(hamming(len,'periodic')) ;...
                                    zeros(index,1)];
                        tempKernel = tempKernel.*exp(2*pi*im*Q*(0:fftLen-1)'/len);
                    case 'r'
                        tempKernel =[zeros(fftLen-len,1) ;...
                                hamming(len,'periodic')/sum(hamming(len,'periodic'))];
                        tempKernel = tempKernel.*exp(2*pi*im*Q*(0:fftLen-1)'/len);
                end
                specKernel = fft(tempKernel);
                specKernel(find(abs(specKernel)<=thresh))=0;
                self.CQTSparseKernel = sparse([specKernel self.CQTSparseKernel]);
            end
            self.CQTSparseKernel = conj(self.CQTSparseKernel)/fftLen;
            self.CQTKernelComputationNeeded = 0;
        end
    end
end

Contact us