classdef SynthesisBuffer < dsp.internal.AsyncBuffercgHelper
    %SynthesisBuffer Synthesis buffer object for batch analysis
    %   sBuff = synthesisBuffer returns a buffer object derived from
    %   dsp.AsyncBuffer with the write method overwritten to enable overlap
    %   writing. You can specify the number of samples of overlap writing
    %   by the Overlap property.  You can specify the action to take on
    %   overlap writing as 'add' or 'average'. 'add' is useful when
    %   implementing overlap-and-add reconstruction. 'average' is useful
    %   when maintaining a derived value, such as pitch, which is
    %   calculated with overlapped analysis frames. See
    %   audiopluginexample.SpectralSubtractor for a use of the 'add'
    %   functionality. See audiopluginexample.SpeechPitchDetector for an
    %   example of the 'average' functionality.
    
    %   Copyright 2016-2022 The MathWorks, Inc.
    %#codegen
    
    %-----------------------------------------------------------------------
    % Public Properties
    %-----------------------------------------------------------------------
    properties
        Overlap = int32(0);
        OverlapAction = 'add';
    end
    
    methods
        %-------------------------------------------------------------------
        % Constructor
        %-------------------------------------------------------------------
        function obj = SynthesisBuffer(varargin)
        end
    end

    methods
        
        %-------------------------------------------------------------------
        % Circular Implementation - Write to AsyncBuffer
        %-------------------------------------------------------------------
        function overrun = write(obj,in)
            % This write method enables the user to overlap-and-add
            % previously written samples.
            oneCast  = int32(1);
            zeroCast = int32(0);
            
            % Write properties to local variables
            wPtr = obj.WritePointer;
            rPtr = obj.ReadPointer;
            cap  = obj.Capacity + oneCast;
            
            overrun  = zeroCast;
            
            row      = int32(size(in,1));
            
            overlap = int32(obj.Overlap);
            
            if (wPtr<oneCast) || (wPtr>cap)
                % This if-statement is required for codegen with dynamic
                % memory allocation set to false.  This if-statement cannot
                % be hit due to dsp.AsyncBuffer's internal book-keeping
                % of read and write pointers, so the error message does not
                % need to be in the message catalog.
                error('Pointers must be bounded.')
            end
            
            if overlap == zeroCast
                c = wPtr + row - oneCast;
                if c > cap                             %   |---w------| c
                    c = c - cap;
                    bc = [wPtr:cap,oneCast:c];
                    if wPtr <= rPtr                    %   |---c---w-r-|
                        overrun = cap - rPtr + oneCast + c;
                    elseif rPtr <= c                   %   |-r-c---w---|
                        overrun = c - rPtr + oneCast;
                    end
                else                                   %   |---w---c---|
                    bc = wPtr:c;
                    if (wPtr<=rPtr) && (rPtr<=c)       %   |---w-r-c---|
                        overrun = c - rPtr + oneCast;
                    end
                end
                obj.Cache(bc(oneCast:row),:) = in;
                
            else % overlap ~= 0
                a = wPtr - overlap;
                c = a + row - oneCast;
                
                if (a<oneCast)                      % a |---w---c-------|
                    a = a + cap;                    %   |---w---c---a---|
                    overlapIdx = [a:cap,oneCast:wPtr-oneCast];
                    inputIdx = wPtr:c;
                    
                    if (wPtr<=rPtr) && (rPtr<=c)    %   |---w-r-c---a---|
                        overrun = c - rPtr + oneCast;
                    %elseif rPtr<wPtr               %   |-r-w---c---a---|
                        % no overrun
                    %elseif rPtr>c && <=a           %   |---w---c-r-a---|
                        % no overrun
                    %elseif rPtr>a                  %   |---w---c---a-r-|
                        % no overrun
                    end
                    
                elseif (a>=oneCast) && (c>cap)      %   |-------a---w---| c
                    c = c - cap;                    %   |---c---a---w---|
                    overlapIdx = a:wPtr-oneCast;
                    inputIdx = [wPtr:cap,oneCast:c];
                    
                    % Overrun can only occur if the write pointer (w) passes r
                    % on the way to c. It cannot occur in the overlapped
                    % portion (a to w).
                    if rPtr <= c                    %   |-r-c---a---w---|
                        overrun = c - rPtr + oneCast;
                    elseif rPtr >= wPtr             %   |---c---a---w-r-|
                        overrun = cap - rPtr + oneCast + c;
                    %elseif rPtr > c & < a          %   |---c-r-a---w---|
                        % no overrun
                    %elseif rPtr > a                %   |---c-a-r---w---|
                        % no overrun
                    end
                    
                else                                %   |---a---w---c---|
                    overlapIdx = a:wPtr-oneCast;
                    inputIdx = wPtr:c;
                    
                    if (rPtr>wPtr) && (rPtr<=c)     %   |---a---w-r-c---|
                        overrun = c - rPtr + oneCast;
                    %elseif rPtr < a                %   |-r-a---w---c---|
                        % no overrun
                    %elseif (rPtr>=a) && (rPtr<=w)  %   |---a-r-w---c---|
                        % no overrun
                    %elseif rPtr > c                %   |---a---w---c-r-|
                        % no overrun
                    end
                end
                switch obj.OverlapAction 
                    case 'add'
                    obj.Cache([overlapIdx,inputIdx],:) = [obj.Cache(overlapIdx,:)+in(oneCast:overlap,:);...
                        in(overlap+oneCast:end,:)];
                    case 'average'
                    obj.Cache([overlapIdx,inputIdx],:) = [mean(cat(3,obj.Cache(overlapIdx,:),in(oneCast:overlap,:)),3,'omitnan');...
                         in(overlap+oneCast:end,:)];
                end
                
            end

            if (c+oneCast)>cap
                wPtr = oneCast;
            else
                wPtr = c + oneCast;
            end
            if overrun ~= zeroCast
                rPtr = wPtr;
            end
            
            % Update properties
            obj.CumulativeOverrun = obj.CumulativeOverrun + overrun;
            obj.WritePointer      = wPtr;
            obj.ReadPointer       = rPtr;
        end
    end

    methods (Access=protected)
        %-------------------------------------------------------------------
        % Load object (used for clone method)
        %-------------------------------------------------------------------
        function loadObjectImpl(obj,s,wasLocked)
            loadObjectImpl@dsp.internal.AsyncBuffercgHelper(obj,s,wasLocked);
        end

        %-------------------------------------------------------------------
        % Save object (used for clone method)
        %-------------------------------------------------------------------
        function s = saveObjectImpl(obj)
            s = saveObjectImpl@dsp.internal.AsyncBuffercgHelper(obj);
        end
    end
end