0
$\begingroup$

I am working on sound processing android application project in which I have to input sound and remove noise as much as possible at run-time, somewhat like streaming. I am recording video at one place and play at a different place with very low latency. I tried using Chebyshev filter but found that it didn't work if I had less frame available to process. There was clipping at the boundaries of the sample so when the video was played I could hear hissing like sound.

To demonstrate what actually happened I am attaching a Matlab code that simulates the situation output that I am getting.

Matlab code when run on a long sample give reduced noise output.

clc;clear;close all;
[sample_data, sample_rate] = audioread('save.binary.wav');
Fs = sample_rate;                                       % Sampling Frequency (Hz)
Fn = Fs/2;                                              % Nyquist Frequency (Hz)
Wp = 1000/Fn;                                           % Passband Frequency (Normalised)
Ws = 1010/Fn;                                           % Stopband Frequency (Normalised)
Rp =   1;                                               % Passband Ripple (dB)
Rs = 150;                                               % Stopband Ripple (dB)
[n,Ws] = cheb2ord(Wp,Ws,Rp,Rs);                         % Filter Order
[z,p,k] = cheby2(n,Rs,Ws,'low');                        % Filter Design
[soslp,glp] = zp2sos(z,p,k);                            % Convert To Second-Order-Section For Stability
figure(3)
freqz(soslp, 2^16, Fs)                                  % Filter Bode Plot

filtered_sound = zeros(size(sample_data));
filtered_sound = filtfilt(soslp, glp, sample_data);
sound(filtered_sound, sample_rate)
audiowrite('save.binary.test.wav',filtered_sound,Fs)

While when running on small samples of the big file gives, hissing sound (hear simulated by using small samples in a loop).

clc;clear;close all;
[sample_data, sample_rate] = audioread('save.binary.wav');
Fs = sample_rate;                                       % Sampling Frequency (Hz)
Fn = Fs/2;                                              % Nyquist Frequency (Hz)
Wp = 1000/Fn;                                           % Passband Frequency (Normalised)
Ws = 1010/Fn;                                           % Stopband Frequency (Normalised)
Rp =   1;                                               % Passband Ripple (dB)
Rs = 150;                                               % Stopband Ripple (dB)
[n,Ws] = cheb2ord(Wp,Ws,Rp,Rs);                         % Filter Order
[z,p,k] = cheby2(n,Rs,Ws,'low');                        % Filter Design
[soslp,glp] = zp2sos(z,p,k);                            % Convert To Second-Order-Section For Stability
figure(3)
freqz(soslp, 2^16, Fs)                                  % Filter Bode Plot


BufferSize = 960;
bufferCount = 830400/BufferSize;
filtered_sound = zeros(size(sample_data));

for i = 0:(bufferCount-1)
    filtered_sound(BufferSize*i+(1:BufferSize),:) = filtfilt(soslp, glp, sample_data(BufferSize*i+(1:BufferSize),:));
    sound(filtered_sound(BufferSize*i+(1:BufferSize),:), sample_rate)
end
sound(filtered_sound, sample_rate)
audiowrite('save.binary.test.wav',filtered_sound,Fs)

So what I want to know is which sort of filter is suitable for such an application in which frame length is small due to the requirement of low latency? I tried filters like Butterworth , elliptic, etc . but the results weren't any different.

$\endgroup$
  • $\begingroup$ Can you specify "very low latency" in seconds? Also, what are your filtering requirements? I'm afraid the math doesn't like you: If you need a filter with a 10 Hz transition width, that will have quite some group delay. Nothing to do about that. But a) I doubt you actually need that filter and b) I doubt that a reasonable filter plays any significant role in an android (and thus probably IP-based) communication system. $\endgroup$ – Marcus Müller May 10 at 20:05
  • $\begingroup$ @MarcusMüller I am using Matlab just for prototyping and quick testing but final work is going to be in C++. I know the basics of Fourier transform, z-transform and design of basic filters. If your suggestion is heavily math based then I can still try so please do tell if you have any idea. I think I wasn't clear in describing the actual problem. In the normal app, I am using NDK to record sound and play it. As I need low latency, I had to record video in pieces where each piece had a small number of frames. To simulate this in Matlab I am using a loop to process data instead of entire signal. $\endgroup$ – Mandar Sadye May 11 at 3:42
  • $\begingroup$ thank you for your reply, but you answered none of my questions? Can you specify "very low latency" in seconds? Also, what are your filtering requirements? matlab or C++ is irrelevant to this. $\endgroup$ – Marcus Müller May 11 at 8:45
  • $\begingroup$ @MarcusMüller my target is less than 50ms processing latency and about filtering requirements: I want to retain speech frequency so frequency range around 50 to 400. sorry for missing to answer some of your questions. Thanks for the reply. $\endgroup$ – Mandar Sadye May 11 at 10:22
  • $\begingroup$ ah, nice! So, assuming 46 dB attenuation to be more than sufficient noise suppression, and it really not mattering whether your stop band starts 10 Hz or 100 Hz after the pass band: your filter design variables are totally over the top! That is what makes your filter long. Try with Rs=46, Ws=1200/Fn. $\endgroup$ – Marcus Müller May 11 at 10:30
1
$\begingroup$

Aside from your discontinuity problems, which have nothing to do with your filter, but the way you apply it, there's the fact that you're specifying one system, and then building a different one:

my target is less than 50ms processing latency and about filtering requirements: I want to retain speech frequency so frequency range around 50 to 400.

So, let's see what you did instead!

Fs = sample_rate;                                       % Sampling Frequency (Hz)
Fn = Fs/2;                                              % Nyquist Frequency (Hz)
Wp = 1000/Fn;                                           % Passband Frequency (Normalised)
Ws = 1010/Fn;                                           % Stopband Frequency (Normalised)
Rp =   1;                                               % Passband Ripple (dB)
Rs = 150;                                               % Stopband Ripple (dB)
[n,Ws] = cheb2ord(Wp,Ws,Rp,Rs);                         % Filter Order

That, for me, leads to a filter of length n=131 when using a sampling rate of 16 kHz. That means nearly 1/20 of a second in filter group delay if this was a linear phase FIR; since it's an IIR, it should be lower, but the precise math eludes me at the moment.

So, that Wp = 1kHz filter passband end makes sense – 400 Hz is really a bit low for "nice" comms.

But, your Ws is far too close. You don't really lose that much if you see 200 Hz more noise than when you use only 10 Hz transition width. Also, 150 dB stopband ripple is way over the top – your audio ADC doesn't even mathematically have a dynamic range that is close to that. 46 dB should be more than enough; so:

Ws = 1200/Fn;                                           % Stopband Frequency (Normalised)
Rp =   1;                                               % Passband Ripple (dB)
Rs = 46;                                               % Stopband Ripple (dB)
[n,Ws] = cheb2ord(Wp,Ws,Rp,Rs);                         % Filter Order

now yields a filter length of n=11 coefficients. That's nice and short, isn't it?

Generally, you're building a speech communications system. I'd point out that you're not the first one to do so! And that others have had the resources to run large tests on what filtering and what encoding to use at a given system rate and latency to make speech as well understandable as possible. These codecs do their own filtering, so you don't have to worry about that too much!

These voice coders (vocoders) are abundant – for example, the speech codecs used in GSM are available through free implementations: see libgsm for example.
More modern (free) codecs sound better at the same bitrates; the Opus codec (which superseeds speex) comes to mind; it's optimized for usage in packetized data transmissions and latency. (If you're in the market for ultra-low bitrate communications, try Codec2. It's awesome, but not really what you need.)

So, really, don't code your own vocoder – that's not only hard to do well, it also simply requires a lot of time-intense testing to tune it just right for voice; that is something others have done for you.

$\endgroup$
  • $\begingroup$ Great. those modifications reduced the hissing sound that I was hearing at the boundary of the frame. Also, the delay is less. I will definitely look into libsm and Opus. Thanks a lot for help. $\endgroup$ – Mandar Sadye May 12 at 5:07
  • $\begingroup$ I am interested in knowing how you calculated filter group delay. Also, your statement "but the precise math eludes me at the moment" indirectly mean there is more room for optimization. So, is it possible for you to tell where I can get this information in detail? If me asking this question is against rules of this site then please do tell. I will remove this comment and search on my own as even I feel I am asking for resource related things. $\endgroup$ – Mandar Sadye May 12 at 5:19
  • $\begingroup$ really, ignore libgsm and go straight for Opus; there's 20 years of audio coding research in between. $\endgroup$ – Marcus Müller May 12 at 11:44
  • $\begingroup$ Nonono, I meant the precise math of how the group delay over frequency of an IIR filter can be estimated without going through the full z-Domain polynomial ratio eludes me. That just means that the length of the delay isn't directly obvious to me. It's easy to figure that one out – I think matlab comes with a function grpdelay or so to generate an analysis of that. $\endgroup$ – Marcus Müller May 12 at 11:46
  • $\begingroup$ also, again, hissing is, no matter what filter you use, avoidable. See hotpaw2's answer! You're just doing the filtering wrong. $\endgroup$ – Marcus Müller May 12 at 11:49
1
$\begingroup$

filtfilt filters backwards in time, so the latency can be very large (infinite in theory), depending on the length of the filter's impulse response above your desired noise floor (or quantization floor). Thus filtfilt is best used only for offline filtering.

If you use an ordinary forward-in-time IIR filter, you need to save the internal state variables of your IIR filter(s) between each short frame.

An alternative, for a FIR filter (or if an IIR impulse response tapers below some noise floor in a short enough number of samples), is to use some form of zero-padding plus overlap add/save to carry the impulse response forward.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.