www.gusucode.com > hdlverifier 案例代码 matlab源码程序 > hdlfilter/hdltonecontrol.m

    %% HDL Tone Control Filter Bank
% This example illustrates how to generate HDL code for bank of 24
% first-order shelving filters that implement an audio tone control with
% 1 dB steps from -6 dB to +6 dB for bass and treble.
%
% The filters are analytically designed using a simple formula for a
% first-order filter with one pole and one zero on the real axis.
%
% A filter bank is designed since changing the filter coefficients
% on-the-fly can lead to transients in the audio (clicks and pops) as
% the boost/cut control is moved.  With a bank of filters running
% continuously, the appropriate filter is selected from the bank of
% filters when the output is near any zero crossing to avoid these
% transients.

% Copyright 2004-2015 The MathWorks, Inc.

%% Set up the Parameters
% Use the CD sampling rate of 44.1 kHz with bass and treble corners at
% 100 Hz and 1600Hz.

Fs  = 44100;                            % all in Hz
Fcb =   100;
Fct =  1600;

%% Define the Tangent Frequency Mapping Parameters
% Map the corner frequencies by the tangent to move from the analog to
% the digital domain.  Then, define the range of cut and boost to be
% applied, choosing a 12 dB total range in 1 dB steps.  Convert decibels
% to linear gain and separate the boost and cut vectors.

basstan   = tan(pi*Fcb/Fs);
trebletan = tan(pi*Fct/Fs);

dbrange  = [-6:-1, +1:+6];              % -6 dB to +6 dB 
linrange = 10.^(dbrange/20);
boost = linrange(linrange>1);
cut   = linrange(linrange<=1);
Nfilters = 2 * length(dbrange);         % 2X for bass and treble

%% Design the Filter Bank
% Complete the bilinear transform on the poles, then compute the
% zeros of the filters based on the desired boost or cut.  Since boost
% and cut are vectors, we can design all the filters at the same time
% using vector arithmetic. Note that a1 is always one in these filters.

a2_bass_boost    = (basstan - 1) / (basstan + 1);
b1_bass_boost   = 1 + ((1 + a2_bass_boost) .* (boost - 1)) / 2;
b2_bass_boost   = a2_bass_boost + ...
                  ((1 + a2_bass_boost) .* (boost - 1)) / 2;

a2_bass_cut      = (basstan - cut) / (basstan + cut);
b1_bass_cut     = 1 + ((1 + a2_bass_cut) .* (cut - 1)) / 2;
b2_bass_cut     = a2_bass_cut + ((1 + a2_bass_cut) .* (cut - 1)) / 2;

a2_treble_boost  = (trebletan - 1) / (trebletan + 1); 
b1_treble_boost   = 1 + ((1 - a2_treble_boost) .* (boost - 1)) / 2;
b2_treble_boost   = a2_treble_boost + ...
                    ((a2_treble_boost - 1) .* (boost - 1)) / 2;

a2_treble_cut      = (cut .* trebletan - 1) / (cut .* trebletan + 1);
b1_treble_cut     = 1 + ((1 - a2_treble_cut) .* (cut - 1)) / 2;
b2_treble_cut     = a2_treble_cut + ...
                    ((a2_treble_cut - 1) .* (cut - 1)) / 2;

%% Build the Filter Bank
% Build the numerator and denominator arrays for the entire filter
% bank. Then build a cell array of filters in {b,a,b,a,...}  form for
% fvtool. Preallocate the cell array for speed.

filterbank = cell(1, 2*Nfilters);     % 2X for numerator and denominator
% Duplicate a2's into vectors
a2_bass_boost   = repmat(a2_bass_boost,   1, length(boost));
a2_bass_cut     = repmat(a2_bass_cut,     1, length(cut));
a2_treble_boost = repmat(a2_treble_boost, 1, length(boost));
a2_treble_cut   = repmat(a2_treble_cut,   1, length(cut));

filterbank_num = [b1_bass_cut, b1_bass_boost, b1_treble_cut, b1_treble_boost ; ...
                  b2_bass_cut, b2_bass_boost, b2_treble_cut, b2_treble_boost ]';
% a1 is always one
filterbank_den = [ones(1, Nfilters); ...
                  a2_bass_cut, a2_bass_boost, a2_treble_cut, a2_treble_boost]';

filterbank(1:2:end) = num2cell(filterbank_num, 2);
filterbank(2:2:end) = num2cell(filterbank_den, 2);

%% Check the Response of the Filter Bank
% Use fvtool in log frequency mode to see the audio band more clearly.
% Also set the sampling frequency.

fvtool(filterbank{:}, 'FrequencyScale', 'log', 'Fs', Fs);

%% Create the Quantized Filter Bank
% Create a quantized filter for each double-precision filter designed
% above.  Assume CD-quality input of 16 bits and an output word length
% of 18 bits to allow for the +6 dB gain with some headroom.

quantizedfilterbank = cell(1, Nfilters);
for n = 1:Nfilters
  quantizedfilterbank{n} = dsp.BiquadFilter('Structure','Direct Form I');
  quantizedfilterbank{n}.SOSMatrix = [filterbank_num(n,:),0,...
                                      filterbank_den(n,:),0];

  quantizedfilterbank{n}.NumeratorCoefficientsDataType         = 'Custom';
  quantizedfilterbank{n}.CustomNumeratorCoefficientsDataType   = numerictype([],16);
  quantizedfilterbank{n}.CustomDenominatorCoefficientsDataType = numerictype([],16);
  quantizedfilterbank{n}.CustomScaleValuesDataType             = numerictype([],16);
  
  quantizedfilterbank{n}.OutputDataType       = 'Custom';
  quantizedfilterbank{n}.CustomOutputDataType = numerictype([],18,15);
  
  quantizedfilterbank{n}.SectionOutputDataType       = 'Custom';
  quantizedfilterbank{n}.CustomSectionOutputDataType = numerictype([],18,15);
  quantizedfilterbank{n}.NumeratorProductDataType    = 'Full precision';
  quantizedfilterbank{n}.DenominatorProductDataType  = 'Full precision';
  
  quantizedfilterbank{n}.NumeratorAccumulatorDataType         = 'Custom';
  quantizedfilterbank{n}.CustomNumeratorAccumulatorDataType   = numerictype([],34,30);
  quantizedfilterbank{n}.DenominatorAccumulatorDataType       = 'Custom';
  quantizedfilterbank{n}.CustomDenominatorAccumulatorDataType = numerictype([],34,29);
  
  quantizedfilterbank{n}.RoundingMethod  = 'Floor';
  quantizedfilterbank{n}.OverflowAction  = 'Wrap';
end

%% Check the Response of the Quantized Filter Bank
% Check the quantized filter bank using fvtool again in log frequency
% mode with the sampling rate set.

fvtool(quantizedfilterbank{:}, 'FrequencyScale', 'log', 'Fs', Fs,'Arithmetic','fixed');

%% Generate HDL for the Filter Bank and Test Benches 
% Generate HDL for each of the 24 first-order filters and test benches
% to check each design. The target language here is Verilog.
%
% Use the canonic sign-digit (CSD) techniques to avoid using multipliers
% in the design.  Specify this with the 'CoeffMultipliers', 'CSD'
% property-value pair.  Since the results of using this optimization are
% not always numerically identical to regular multiplication that
% results in overflows, set the test bench 'ErrorMargin' property to 1
% bit of allowable error.
%
% Create a custom stimulus to illustrate the gain of filters by
% generating one-half cycle of a 20 Hz tone and 250 cycles of a 10 kHz
% tone. Use the low frequency tone for the bass boost/cut filters and
% the high frequency tone for the treble boost/cut filters.
%
% Create a temporary work directory.  
%
% To generate VHDL code instead, change the property 'TargetLanguage',
% from 'Verilog' to 'VHDL'.

bassuserstim = sin(2*pi*20/Fs*(0:Fs/40));
trebuserstim = sin(2*pi*10000/Fs*(0:Fs/40));
workingdir   = tempname;

for n = 1:Nfilters/2
  generatehdl(quantizedfilterbank{n},...
              'Name', ['tonecontrol', num2str(n)],...
              'TargetDirectory', workingdir,...
              'InputDataType', numerictype(1,16,15),...
              'TargetLanguage', 'Verilog',...
              'CoeffMultipliers','CSD', ...
              'GenerateHDLTestbench','on', ...
              'TestBenchUserStimulus', bassuserstim, ...
              'ErrorMargin', 1);           
end

for n = Nfilters/2+1:Nfilters
  generatehdl(quantizedfilterbank{n},...
              'Name', ['tonecontrol', num2str(n)],...
              'TargetDirectory', workingdir,...
              'InputDataType', numerictype(1,16,15),...
              'TargetLanguage', 'Verilog',...
              'CoeffMultipliers','CSD', ...
              'GenerateHDLTestbench','on', ...
              'TestBenchUserStimulus', bassuserstim, ...
              'ErrorMargin', 1);
end

%% ModelSim(R) Simulation Results
% The following display shows the ModelSim(R) HDL simulator running these
% test benches.
% 
% Bass response to the 20 Hz tone:

%%
% <<../bass_screen.jpg>>
%
% Treble response to the 10 kHz tone:

%%
% <<../treble_screen.jpg>>

%% Conclusion
% You designed a filter bank of double-precision bass and treble
% boost/cut first order filters directly using the bilinear
% transform. You then used the filter coefficients to create a bank of
% quantized filters with CD-quality 16-bit inputs and 18-bit outputs.
% After checking the response of the quantized filters, you generated
% Verilog code for each filter in the filter bank along with a Verilog
% test bench that used a custom input stimulus for the bass and treble
% filters.
%
% To complete the solution of providing tone controls to an audio
% system, you can add a cross-fader to the outputs of each section of
% the filter bank.  These cross-faders should take several sample times
% to switch smoothly from one boost or cut step to the next.
%
% Using a full bank of filters is only one approach to solving this type
% of problem. Another approach would be to use two filters for each band
% (bass and treble) with programmable coefficients that can be changed
% under software control.  One of the two filters would be the current
% setting, while the other would be the next setting.  As you adjusted
% the tone controls, the software would ping-pong between the filters
% exchanging current and next with a simple fader.  The trade-off is
% that the constant coefficient filter bank shown above is uses no
% multipliers while the seemingly simpler ping-pong scheme requires
% several multipliers.