
% This is a streaming audio script that reads audio
% data blockwise from a file, processes the audio
% with a multi-band parametric equalizer and plays
% the audio on the host computer's sound system.

%% Initialization section

% Audio source: audio file reader object
% Replace audio file name with your own file
NS = 8192; % Block size
AFR = dsp.AudioFileReader('Turnabout Is Fair Play.wav','SamplesPerFrame',NS);
fs = AFR.SampleRate; % capture sample rate

% Audio sink: audio player object
% Plays through host PC sound system
AP = dsp.AudioPlayer;

% Specification of the parametric equalizer -
% For this example we set up four equalizer sections.
% Each section will have a tunable center frequency,
% gain and Q factor. Each section will have a fixed
% type, which can be either 'Bass' for a bass shelf
% filter, 'Peak' for a peak filter or 'Treble' for 
% a treble shelf filter. All sections are second order
% biquad filters. The filter types and initial center
% frequencies are specified below. The initial gains
% are all 0 dB and the initial Q values are all 1.
% Note that the Q factor has no effect on the shelf
% filters.

% Set filter types and initial center frequencies
type = {'Bass','Peak','Peak','Treble'};
freq = [80 320 1280 5120];
N = length(freq);

% Here we initialize the GUI which will contain
% sliders to adjust the equalizer in real time.
% Each slider has a name, an initial value and limits.

% Pre-allocate tunable parameter structure.
param(3*N).Name = [];
param(3*N).InitialValue = [];
param(3*N).Limits = [];
for n=1:N % Set 3 sliders for each section
    param(3*(n-1)+1).Name = sprintf('Channel %d Gain (dB)',n);
    param(3*(n-1)+1).InitialValue = 0;
    param(3*(n-1)+1).Limits = [-20, 20];
    param(3*(n-1)+2).Name = sprintf('Channel %d Center Frequency in (Hz)',n);
    param(3*(n-1)+2).InitialValue = freq(n);
    param(3*(n-1)+2).Limits = [freq(n)/2, 2*freq(n)];
    param(3*(n-1)+3).Name = sprintf('Channel %d Q Factor',n);
    param(3*(n-1)+3).InitialValue = 1;
    param(3*(n-1)+3).Limits = [0.2, 2];
end

% The following two lines launch the tunable parameter
% GUI.

clear HelperUnpackUDP;
tuningUI = HelperCreateParamTuningUI(param, 'Parametric Equalizer Tuning');

% Next we set up the equalizer as a BiquadFilter object.
% We set it up so that the equalizer can be tuned in
% real time.

% Create BiquadFilter object
EQ = dsp.BiquadFilter('SOSMatrixSource','Input port',...
                             'ScaleValuesInputPort',false);
% Pre-allocate SOS matrix (in two forms)
numSOS = zeros(3,N);
denSOS = zeros(2,N);
eqsos = zeros(N,6); % Only for frequency response display
% Populate numSOS and denSOS with biquad filter coefficients
for n=1:N
    [b,a] = order2Filt(0,freq(n),1,fs,type(n));
    numSOS(1:3,n) = b;
    denSOS(1:2,n) = a(2:3);
end

% Initialize frequency response display to flat frequency response
heq=semilogx((0:NS-1)*fs/(2*NS),zeros(NS,1));
title('Equalizer Frequency Response');
xlabel('Frequency in Hz');
ylabel('Magnitude in dB');
axis([10 20000 -20 20]);
%% Start streaming

while ~isDone(AFR)
    % Check for new tunable parameters
    [paramNew,simControlFlags] = HelperUnpackUDP();
  
    if simControlFlags.stopSim % Check for stop
        break; % skip to end of script
    end
    drawnow % needed to clear buffers
    if simControlFlags.pauseSim % Check for pause
        continue; % loop until pause is pressed again
    end
    if ~isempty(paramNew) % Check for new parameters
        for n=1:N
            [b,a] = order2Filt(paramNew(3*(n-1)+1),...
                paramNew(3*(n-1)+2),...
                paramNew(3*(n-1)+3),fs,type(n));
            numSOS(1:3,n) = b;
            denSOS(1:2,n) = a(2:3);
            eqsos(n,1:6) = [b a]; % to update plot
        end
        [h,~]=freqz(eqsos,NS); % get new FR
        heq.YData=20*log10(abs(h)); % update plot
        drawnow
    end
    % The rest is straightforward!
    x = step(AFR); % Get block of samples
    y = step(EQ,x,numSOS,denSOS); % Equalize
    step(AP,y); % Play!
end

%% Cleanup
close(gcf); % close FR display
if ishghandle(tuningUI)  % If parameter tuning UI is open, then close it.
    delete(tuningUI);
    drawnow;
    clear hUI
end
release(AP);