classdef Echo < audioPlugin
%Echo Add echo effect to an audio signal.
%
%   ECHO = audiopluginexample.Echo() returns an object ECHO with properties
%   set to their default values.
%
%   Echo methods:
%
%   Y = process(ECHO, X) adds echo effect to the audio input X based on the
%   properties specified in the object ECHO and returns it as output Y.
%   Each column of X is treated as individual input channels.
%
%   Echo properties:
%
%   Gain          - Amplitude gain 
%   FeedbackLevel - Feedback gain 
%   Delay         - Base delay (s)
%   WetDryMix     - Wet/dry signal mix
%
%   % Example 1: Simulate Echo in MATLAB.
%   reader = dsp.AudioFileReader(...
%       SamplesPerFrame=1024, PlayCount=1);
%
%   player = audioDeviceWriter(SampleRate=reader.SampleRate);
%
%   echo = audiopluginexample.Echo;
%
%   parameterTuner(echo)
%   while ~isDone(reader)
%       x = reader();
%       y = process(echo, x);
%       player(y);
%       drawnow limitrate;
%   end
%
%   release(reader)
%   release(player)
%
%   % Example 2: Launch a test bench for the volume and echo objects
%   volume = audiopluginexample.VolumeController;
%   echo = audiopluginexample.Echo;
%   audioTestBench(volume,echo)
%
%   See also: audiopluginexample.VolumeController, audiopluginexample.Chorus,
%   audiopluginexample.Flanger

%   Copyright 2015-2023 The MathWorks, Inc.

%#codegen
    
    properties
        %Gain Gain of delay branch
        %   Specify the gain as a positive scalar value. This value must
        %   be in the range between 0 and 1. The default value is 0.5.
        Gain = 0.5

        %FeedbackLevel Feedback gain
        %   Specify the feedback gain as a positive scalar value.
        %   This value must be in the range from 0 to 0.5. A value
        %   of 0 disables the feedback. The default value is 0.35.
        FeedbackLevel = 0.35

        %Delay Base delay (s)
        %   Specify the base delay for the echo effect as positive
        %   scalar value in seconds. This value value must be in the
        %   range between 0 and 1 seconds. The default value is 0.5.
        Delay = 0.5
        
        %WetDryMix Wet/dry signal mix
        %   Specify the wet/dry signal mix as a positive scalar. This value must
        %   be in the range between 0 to 1. For example, a wet/dry mix value of
        %   0.6 outputs a signal that is 60% wet (with effect applied) and 40%
        %   dry (unprocessed). The default value is 0.5.
        WetDryMix = 0.5
    end
    
    properties (Constant)
        % audioPluginInterface manages the number of input/output channels
        % and uses audioPluginParameter to generate plugin UI parameters.
        PluginInterface = audioPluginInterface( ...
            audioPluginParameter("Gain", DisplayName="Gain", ...
                DisplayNameLocation="above", Mapping={"lin",0,1}, ...
                Style="vslider", Layout=[2 1]), ...
            audioPluginParameter("FeedbackLevel", DisplayName="Feedback", ...
                DisplayNameLocation="above", Mapping={"lin",0,.5}, ...
                Style="vslider", Layout=[2 2]), ...
            audioPluginParameter("Delay", DisplayName="Base delay", Label="s", ...
                DisplayNameLocation="above", Mapping={"lin",0,1}, ...
                Style="rotaryknob", Layout=[4 1]), ...
            audioPluginParameter("WetDryMix", DisplayName="Wet/dry mix", ...
                DisplayNameLocation="above", Mapping={"lin",0,1}, ...
                Style="rotaryknob", Layout=[4 2]), ...
            audioPluginGridLayout(RowHeight=[30 190 30 100], ...
                ColumnWidth=[100 100], Padding=[10 10 10 30]), ...
            BackgroundImage=audiopluginexample.private.mwatlogo, ...
            PluginName="Echo", ...
            VendorName="MathWorks", ...
            VendorVersion="3.2.1", ...
            UniqueId="4pvz", ...
            InputChannels=2, ...
            OutputChannels=2)
    end
    
    properties (Access=private)        
        %pFractionalDelay DelayFilter object for fractional delay with
        %linear interpolation
        pFractionalDelay
        
        %pSR Sample rate
        pSR
    end
    
    methods
        function obj = Echo
            fs = getSampleRate(obj);
            obj.pFractionalDelay = audioexample.DelayFilter( ...
                FeedbackLevel=0.35, SampleRate=fs);
            obj.pSR = fs;
        end
        
        function set.FeedbackLevel(obj, val)
            obj.pFractionalDelay.FeedbackLevel = val; %#ok<MCSUP>
        end
        function val = get.FeedbackLevel(obj)
            val = obj.pFractionalDelay.FeedbackLevel;
        end
        
        function reset(obj)
            % Reset sample rate
            fs = getSampleRate(obj);
            obj.pSR = fs;
            
            % Reset delay
            obj.pFractionalDelay.SampleRate = fs;
            reset(obj.pFractionalDelay);
        end
        
        function y = process(obj, x)
            delayInSamples = obj.Delay*obj.pSR;
            
            % Delay the input
            xd = obj.pFractionalDelay(delayInSamples, x);
            
            % Calculate output by adding wet and dry signal in appropriate
            % ratio
            mix = obj.WetDryMix;
            y = (1-mix)*x + (mix)*(obj.Gain.*xd);
        end

        function s = saveobj(obj)
            s = saveobj@audioPlugin(obj);
            s.pFractionalDelay = matlab.System.saveObject(obj.pFractionalDelay);
            s.FeedbackLevel = obj.FeedbackLevel;
            s.WetDryMix = obj.WetDryMix;
            s.Delay = obj.Delay;
            s.Gain = obj.Gain;
            s.pSR = obj.pSR;
        end
        function obj = reload(obj,s)
            obj = reload@audioPlugin(obj,s);
            obj.pFractionalDelay = matlab.System.loadObject(s.pFractionalDelay);
            obj.FeedbackLevel = s.FeedbackLevel;
            obj.WetDryMix = s.WetDryMix;
            obj.Delay = s.Delay;
            obj.Gain = s.Gain;
            obj.pSR = s.pSR;
        end
    end

    methods(Static)
        function obj = loadobj(s)
            if isstruct(s)
                obj = audiopluginexample.Echo;
                obj = reload(obj,s);
            end
        end     
    end
end
