How can I implement a stable Shelving filter for short pulses?
4 visualizzazioni (ultimi 30 giorni)
Mostra commenti meno recenti
I don't know much about MATLAB and have only been experimenting with it for 1 week.
I have a SOFA file with a HRTF (Head Related Transfer Function) measurement.
The measurement is faulty and I wanted to apply a simple shelf filter below 350Hz on the left channel. After a lot of research I managed to apply an IIR shelf filter with the "shelvFilt" function. The problem is that the frequency response is very unstable, probably because HRTFs are very short pulses.
The next idea that comes to my mind is an FIR filter, does anyone have an idea how to best implement this?
This is the code with the IIR shelf filter using the SOFAToolbox + a screenshot with the unstable result.
% Load the SOFA file
data = SOFAload('/Users/username/Desktop/HRTF_48000.sofa');
% Extract the left channel data from Data.IR
leftChannelData = squeeze(data.Data.IR(:, 1, :));
% Create the shelving filter
shelvFilt = shelvingFilter(Gain=-1, Slope=1, CutoffFrequency=350, FilterType="lowpass", SampleRate=48000);
% Apply the shelving filter to the left channel data
filteredLeftChannelData = shelvFilt(leftChannelData);
% Update the left channel data in the new SOFA structure
newData = data;
newData.Data.IR(:, 1, :) = filteredLeftChannelData;
% Save the new SOFA structure as a new SOFA file
SOFAsave('/Users/username/Desktop/HRTF_48000_Modified.sofa', newData, 0);
7 Commenti
jibrahim
il 26 Giu 2023
Hi Hasan,
If you attach your SOFA file, I can try to take a look.
Two comments:
1) Starting in release R2023B, you will be able to read and write SOFA files in MATLAB with Audio Toolbox.
2) Make sure leftChannelData is a column vector, as that is what the shevling filter expects. A row vector would be wrong, as each sample of the input would be interpreted as coming from an independent channel.
Risposta accettata
jibrahim
il 27 Giu 2023
Hi Hasan,
Since you are trying to modify a short impulse response, you might want to apply the transformation in the frequency domain, and then go back to the time domain. The following code takes advantage of R2023B functionality (reading SOFA files). The basic idea should work in any release though:
% Design your shelving filter
shelvFilt = shelvingFilter(Gain=-5, Slope=1,...
CutoffFrequency=350, FilterType="lowpass", SampleRate=48000);
% View the filter response
visualize(shelvFilt)
% You can change filter parameters and observe the change in the response
shelvFilt.Gain=-1;
% Get the frequency response of the shelving filter
[b,a] = coeffs(shelvFilt);
NFFT = 1024;
HShelv = freqz(b,a,NFFT);
% Read the SOFA data
s = sofaread('HRTF_DF_Hasan_ITD_encoded_48000.sofa');
% Get one impulse response
imp = squeeze(s.Numerator(10,1,:));
impLen = length(imp);
H = fft(imp,NFFT);
% Apply shelving filter
HNew = H.*HShelv;
% Back to time domain
impNew = ifft(HNew);
impNew = real(impNew(1:impLen,:));
figure
subplot(211)
plot(imp)
subplot(212)
plot(impNew)
figure
freqz(imp,1,NFFT)
hold on
freqz(impNew,1,NFFT)
Once you are happy with your filter, you cabn apply the change to all your impulse responses and generate a new SOFA file. For example:
% Read the SOFA data
s = sofaread('HRTF_DF_Hasan_ITD_encoded_48000.sofa');
% Design your shleving filter
shelvFilt = shelvingFilter(Gain=-5, Slope=1, CutoffFrequency=350, FilterType="lowpass", SampleRate=48000);
visualize(shelvFilt)
% Get the frequency response of the shelving filter
[b,a] = coeffs(shelvFilt);
NFFT = 1024;
HShelv = freqz(b,a,NFFT);
numMeas = size(s.Numerator,1);
impLen = size(s.Numerator,3);
for index=1:numMeas
% Get an impulse response for left channel
imp = squeeze(s.Numerator(index,1,:));
H = fft(imp,NFFT);
% apply shelving filter
H = H.*HShelv;
impNew = ifft(H);
impNew = real(impNew(1:impLen,:));
s.Numerator(index,1,:) = impNew.';
end
% Write new SOFA file
sofawrite('NewData.sofa',s);
If you want to apply a filter on top of your original HRTF data in a streaming loop, you can do something like this:
% Read the SOFA data
s = sofaread('HRTF_DF_Hasan_ITD_encoded_48000.sofa');
% choose your impulse response
imp = squeeze(s.Numerator(1,1,:));
fir = dsp.FrequencyDomainFIRFilter(imp.');
% Design your shleving filter
shelvFilt = shelvingFilter(Gain=-5, Slope=1, CutoffFrequency=350,...
FilterType="lowpass", SampleRate=48000);
visualize(shelvFilt)
% Use this UI to tune the filter as the sim is running
parameterTuner(shelvFilt)
afr = dsp.AudioFileReader('FunkyDrums-48-stereo-25secs.mp3');
adw = audioDeviceWriter(SampleRate=48000);
spect = spectrumAnalyzer(SampleRate=48000,PlotAsTwoSidedSpectrum=false,FrequencyScale="log");
while ~isDone(afr)
frame = afr();
x = fir(frame);
y = shelvFilt(x);
adw(y);
spect(y(:,1))
drawnow limitrate
end
5 Commenti
jibrahim
il 29 Giu 2023
Hi Hasan, you are getting the filter coefficients correctly. I am just pointing out that using the coefficients with the filter function should be exatcly the same as using the objhect directly.
Più risposte (0)
Vedere anche
Categorie
Scopri di più su Measurements and Spatial Audio in Help Center e File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!