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

%   Copyright 2015-2023 The MathWorks, Inc.

%#codegen
    
    %----------------------------------------------------------------------
    % Public properties
    %----------------------------------------------------------------------
    properties
        %Delay Base delay (s)
        %   Specify the base delay for the flanger effect as positive scalar
        %   value in seconds. This value value must be in the range between
        %   0 and 0.1 seconds. The default value is 0.001.
        Delay = 0.001

        %Depth Amplitude of modulator
        %   Specify the amplitude of the modulating sine wave as a positive
        %   scalar value. The sine wave is added to the base delay value to
        %   modulate the delay sinusoidally. This value must be in the range
        %   between 0 and 50. The default value is 0.30.
        Depth = 30
        
        %Rate Frequency of modulator (Hz)
        %   Specify the frequency of the sine wave as a positive scalar value
        %   in Hz. This property controls the flanging rate. This value must
        %   be in the range between 0 and 0.5 Hz. The default value is 0.25.
        Rate = 0.25
        
        %FeedbackLevel Feedback gain
        %   Specify the feedback gain as a positive scalar value.
        %   This value must be in the range from 0 to 1. A value
        %   of 0 disables the feedback. The default value is 0.4.
        FeedbackLevel = 0.4

        %WetDryMix Wet/dry 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
        
    %----------------------------------------------------------------------
    % Constant properties
    %----------------------------------------------------------------------
    properties (Constant)
        % audioPluginInterface manages the number of input/output channels
        % and uses audioPluginParameter to generate plugin UI parameters.
        PluginInterface = audioPluginInterface( ...
            audioPluginParameter("Delay", DisplayName="Delay", Label="s", ...
                DisplayNameLocation="above", Mapping={"lin",0,.1}, ...
                Style="rotaryknob", Layout=[2 1]), ...
            audioPluginParameter("Depth", DisplayName="Depth", ...
                DisplayNameLocation="above", Mapping={"lin",0,50}, ...
                Style="rotaryknob", Layout=[2 2]), ...
            audioPluginParameter("Rate", DisplayName="Rate", ...
                DisplayNameLocation="above", Mapping={"lin",0,.5}, ...
                Style="rotaryknob", Layout=[2 3]), ...
            audioPluginParameter("FeedbackLevel", DisplayName="Feedback", ...
                DisplayNameLocation="above", Mapping={"lin",0,1}, ...
                Style="rotaryknob", Layout=[2 4]), ...
            audioPluginParameter("WetDryMix", DisplayName="Wet/dry mix", ...
                DisplayNameLocation="above", Mapping={"lin",0,1}, ...
                Style="rotaryknob", Layout=[2 5]), ...
            audioPluginGridLayout(RowHeight=[20 100], ...
                ColumnWidth=[100 100 100 100 100], Padding=[10 10 10 30]), ...
            BackgroundImage=audiopluginexample.private.mwatlogo, ...
            PluginName="Flanger", ...
            VendorName="MathWorks", ...
            VendorVersion="3.2.1", ...
            UniqueId="py9d", ...
            InputChannels=2, ...
            OutputChannels=2);        
    end
    
    %----------------------------------------------------------------------
    % Private properties
    %----------------------------------------------------------------------
    properties (Access = private, Hidden)
        %pFractionalDelay DelayFilter object for fractional delay with
        %linear interpolation
        pFractionalDelay
        
        %pSine1 Oscillator
        pSine
        
        %pSR Sample rate
        pSR
    end
    
    %----------------------------------------------------------------------
    % public methods
    %----------------------------------------------------------------------
    methods
        function obj = Flanger()
            fs = getSampleRate(obj);
            
            % Create the modulators
            obj.pSine = audioOscillator('Frequency', 0.25,...
                'Amplitude', 30, 'SampleRate', fs);
            
            % Creating fractional delay
            obj.pFractionalDelay = audioexample.DelayFilter( ...
                'FeedbackLevel', 0.4, ...
                'SampleRate', fs);
            
            obj.pSR = fs;
        end
        
        function set.Depth(obj, val)
            obj.pSine.Amplitude = val;%#ok<MCSUP>
        end
        function val = get.Depth(obj)
            val = obj.pSine.Amplitude;
        end
        
        function set.Rate(obj, val)
            obj.pSine.Frequency = val;%#ok<MCSUP>
        end
        function val = get.Rate(obj)
            val = obj.pSine.Frequency;
        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 modulator
            obj.pSine.SampleRate = fs;
            reset(obj.pSine)
            
            % Reset delay
            obj.pFractionalDelay.SampleRate = fs;
            reset(obj.pFractionalDelay)
        end
        
        function y = process(obj, x)
            fs = obj.pSR;
            oscillator = obj.pSine;
            delay = obj.pFractionalDelay;
            
            numSamples = size(x,1);

            % Compute the base delay value in samples
            delayInSamples = obj.Delay*fs;
            
            % Update the frame size of oscillator object
            oscillator.SamplesPerFrame = numSamples;
            
            % Create modulated delay vector
            delayVector = delayInSamples+oscillator();
            
            % Get delayed input
            xd = delay(delayVector,x);

            % Calculate output by adding wet and dry signal in
            % appropriate ratio
            mix = obj.WetDryMix;
            y = (1-mix)*x + mix.*xd;
        end

        function s = saveobj(obj)
            s = saveobj@audioPlugin(obj);
            s.pFractionalDelay = matlab.System.saveObject(obj.pFractionalDelay);
            s.pSine = matlab.System.saveObject(obj.pSine);
            s.pSR = obj.pSR;
            s.Delay = obj.Delay;
            s.Depth = obj.Depth;
            s.Rate = obj.Rate;
            s.FeedbackLevel = obj.FeedbackLevel;
            s.WetDryMix = obj.WetDryMix;
        end
        function obj = reload(obj,s)
            obj = reload@audioPlugin(obj,s);
            obj.pSR = s.pSR;
            obj.Delay = s.Delay;
            obj.Depth = s.Depth;
            obj.Rate = s.Rate;
            obj.FeedbackLevel = s.FeedbackLevel;
            obj.WetDryMix = s.WetDryMix;
            obj.pFractionalDelay = matlab.System.loadObject(s.pFractionalDelay);
            obj.pSine = matlab.System.loadObject(s.pSine);
        end
    end
    methods(Static)
        function obj = loadobj(s)
            if isstruct(s)
                obj = audiopluginexample.Flanger;
                obj = reload(obj,s);
            end
        end
    end
end
