classdef Stack < handle
    %Stack LIFO buffer
    %   S = audioexample.Stack(Cap,NumChan,DataType) returns a Stack
    %   object, S, with capacity set to Cap, number of channels set to
    %   NumChan, and data type set to DataType. The capacity, number of
    %   channels, and data type are immutable and cannot be modified after
    %   construction.
    %
    %   Stack methods:
    %
    %   write    - Write to buffer
    %   read     - Read from buffer
    %   reset    - Reset the internal states to initial conditions
    %
    %   Stack properties:
    %
    %   Capacity - Number of writable/readable rows in buffer
    %   NumChan  - Number of writable/readable columns in buffer
    %   DataType - Type of data stored in buffer
    %   
    %
    %   % EXAMPLE
    %     buffer = Stack(1024,1,'double');
    % 
    %   % Create a source
    %     source = audioOscillator;
    %     source.SamplesPerFrame = 32;
    % 
    %   % Processing loop
    %     for i = 1:100
    %         x = source();
    %     
    %         write(buffer,x);
    %     
    %         y = read(buffer,1024);
    %     
    %         plot(y);
    %         pause(0.5);
    %     end
    %
    % See also dsp.AsyncBuffer, dsp.Delay.

    % Copyright 2017-2021 The MathWorks, Inc.
    
    %#codegen
    
    properties (SetAccess = immutable, GetAccess = public)
        %CAPACITY Number of writable/readable rows in buffer
        % Set this property during construction. This property is
        % immutable.
        Capacity
        
        %NUMCHAN Number of writable/readable columns in buffer
        % Set this property during construction. This property is
        % immutable.
        NumChan
        
        %DATATYPE Type of data stored in buffer
        % Set this property during construction. This property is
        % immutable.
        DataType
    end
    properties (SetAccess = private, GetAccess = private)
        %pCache CAPACITY-by-NUMCHAN matrix of type DATATYPE
        pCache
        %pPointer Indexing pointer
        pPointer = int32(1);
    end
    properties (Constant, GetAccess = public)
        %MaxCapacity Max capacity (number of rows) of buffer. Used as a
        % reasonable limit for codegen reasons.
        MaxCapacity = 1e7;
    end
    
    %----------------------------------------------------------------------
    % Protected methods
    %----------------------------------------------------------------------
    methods
        %------------------------------------------------------------------
        % Construction
        %------------------------------------------------------------------
        function obj = Stack(cap,numChan,dataType)
            coder.internal.errorIf(cap>obj.MaxCapacity || round(cap)~= cap || cap < 2,...
                'audio:Stack:invalidBufferCapacity', obj.MaxCapacity);
            obj.Capacity = int32(cap);
            obj.NumChan  = int32(numChan);
            obj.DataType = dataType;
            obj.pCache   = zeros(cap,numChan,dataType);
        end
        
        %------------------------------------------------------------------
        % Reset
        %------------------------------------------------------------------
        function reset(obj)
            %RESET Reset the internal states to initial conditions
            %
            % reset(S) returns the pCache to a zero matrix and resets the
            % pPointer
            obj.pPointer = int32(1);
            obj.pCache(:)= zeros(1,1,'like',obj.pCache);
        end
        
        %------------------------------------------------------------------
        % Write
        %------------------------------------------------------------------
        function write(obj,in)
            %WRITE Write to buffer
            %
            % write(S,x) writes the samples, x, to the
            %   buffering object, S.
            
            % Write properties to local variables -------------------------
            one    = int32(1);
            zero   = int32(0);
            ptr    = obj.pPointer;
            cap    = obj.Capacity;
            maxCap = obj.MaxCapacity;
            row    = int32(size(in,1));
            %--------------------------------------------------------------
            
            % Push wPtr up one and wrap if necessary ----------------------
            ptr = ptr + one;
            if (ptr>cap)
                ptr = one;
            end
            
            % Write new data to pCache ------------------------------------
            q = ptr + row - one;
            assert(ptr>zero);assert(cap<maxCap);assert(q<maxCap); % Assertions required for codegen
            if q > cap                  %   |----p------| q
                q = q - cap;            %   |-q--p------|
                bc = [ptr:cap,one:q];
            else                        %   |---p---q---|
                bc = ptr:q;
            end
            obj.pCache(bc,:) = in;
            obj.pPointer     = q;
            % -------------------------------------------------------------
        end

        %------------------------------------------------------------------
        % Read
        %------------------------------------------------------------------
        function out = read(obj,numRows)
            %OUTPUT Read from buffer
            %
            % out = read(S,NumRows) returns NumRows samples from each
            %   channel (column) of buffer.

            % Write properties to local variables -------------------------
            ptr    = obj.pPointer;
            cap    = obj.Capacity;
            maxCap = obj.MaxCapacity;
            one    = int32(1);
            zero   = int32(0);
            %--------------------------------------------------------------
            
            % Error check input and cast ----------------------------------
            validateattributes(numRows,{'numeric'},...
                {'nonnegative','real','finite','integer','scalar','<=',cap},'read','number of rows');
            numRowsCast = int32(numRows);
            % -------------------------------------------------------------
            
            % Calculate read index and then read from pCache --------------
            n = ptr - numRowsCast + one;
            assert(cap<maxCap);assert(ptr<maxCap) % Assertions required for codegen
            if (n<=zero)                   %  n  |---p-------|
                n = n + cap;               %     |---p---n---|
                assert(n>zero);                   % Assertion required for codegen
                readIdx = [n:cap,one:ptr];
            else                           %     |---n---p---|
                readIdx = n:ptr;
            end
            out = obj.pCache(readIdx,:);
            %--------------------------------------------------------------
        end
    end
end

