classdef DampedParameter < handle
    %DampedParameter Damped parameter.
    %   Damped parameter allows a parameter to vary gradually when modified.

    %   Copyright 2016-2022 The MathWorks, Inc.

    %#codegen
    properties
        %Value Target value
        %   Value specifies the target value for the damped parameter.
        %   The parameter will transition to the target value over time
        %   (depending on the transition time).
        Value = 0
        %TransitionTime Transition time (seconds)
        %   TransitionTime specifies transition time between the previous
        %   value and the target value when Value is modified.
        TransitionTime = 0.01
        %SampleRate Sample Rate for the parameter
        SampleRate = 44100
    end

    properties (Access=private)
        Damping
        States = 0
        visualObj
    end

    properties (Constant)
        Tolerance = 0.01;
    end

    methods
        function obj = DampedParameter(Val,D,Fs)
            if nargin > 0
                obj.Value = Val;
            end
            if nargin > 1
                obj.TransitionTime = D;
            end
            if nargin > 2
                obj.SampleRate = Fs;
            end

            computeDampingCoefficient(obj);
        end

        function set.TransitionTime(obj,dly)
            validateattributes(dly, {'numeric'}, ...
                {'finite','real','scalar','nonnegative'},...
                'set.TransitionTime','TransitionTime');

            obj.TransitionTime = dly;
            computeDampingCoefficient(obj);
        end

        function val = getDampedValue(obj,FrameSize)
            x = obj.Value*ones(FrameSize,1);

            % Access damping coefficient
            a = obj.Damping;

            % Access filter states
            z = obj.States;

            % Compute numerator coefficient
            Num = 1-a;

            % Compute the damped parameter value with smooth transitions
            [val,z] = filter(Num,[1 -a],x,z);

            if isempty(coder.target) && ~isempty(obj.visualObj) && isvalid(obj.visualObj)
                step(obj.visualObj,val);
            end

            % Store final states
            obj.States = z;
        end

        function reset(obj)
            % Re-initialize states
            obj.States = 0;
            % Compute initial damping coefficient from transition time
            computeDampingCoefficient(obj);
        end

        function varargout = visualize(obj)
            if isempty(obj.visualObj) || ~isvalid(obj.visualObj)
                obj.visualObj = timescope('TimeSpanSource', 'property', ...
                    'TimeSpan',1,'SampleRate',obj.SampleRate);
            end
            show(obj.visualObj);

            if nargout>0
                varargout{1} = obj.visualObj;
            end
        end
        function s = saveobj(obj)
            s.Value = obj.Value;
            s.TransitionTime = obj.TransitionTime;
            s.SampleRate = obj.SampleRate;
            s.Damping = obj.Damping;
            s.States = obj.States;
        end
        function obj = reload(obj,s)
            obj.Value = s.Value;
            obj.TransitionTime = s.TransitionTime;
            obj.SampleRate = s.SampleRate;
            obj.Damping = s.Damping;
            obj.States = s.States;
        end
    end

    methods (Access = private)
        function computeDampingCoefficient(obj)
            % Find damping coefficient corresponding to desired transition time
            Fs = obj.SampleRate;
            c  = obj.Tolerance;
            la = log(c)/(obj.TransitionTime*Fs);
            obj.Damping = exp(la);
        end
    end
    methods(Static)
        function obj = loadobj(s)
            if isstruct(s)
                obj = audioexample.DampedParameter;
                obj = reload(obj,s);
            end
        end
    end
end

