Unverified Commit a466b3c2 authored by jimchen90's avatar jimchen90 Committed by GitHub
Browse files

Add Bass with Biquad (#661)



* Add bass with biquad

* Update functional.py

Add the normalization coefficients

* Update test_sox_compatibility.py

In test_sox_compatibility.py file, I add two bass tests: one test sets gain = 30, atol = 1e-4, the other sets gain = 40, atol = 1.5e-4. The details can be seen in pytorch#676

* Update torchscript_consistency_impl.py

Add torchscript test

* Add flake8 test
Co-authored-by: default avatarJi Chen <jimchen90@devfair0160.h2.fair>
parent 53e5074c
...@@ -113,6 +113,11 @@ Functions to perform common audio operations. ...@@ -113,6 +113,11 @@ Functions to perform common audio operations.
.. autofunction:: treble_biquad .. autofunction:: treble_biquad
:hidden:`bass_biquad`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. autofunction:: bass_biquad
:hidden:`deemph_biquad` :hidden:`deemph_biquad`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......
...@@ -265,6 +265,28 @@ class TestFunctionalFiltering(TestCase): ...@@ -265,6 +265,28 @@ class TestFunctionalFiltering(TestCase):
self.assertEqual(output_waveform, sox_output_waveform, atol=1e-4, rtol=1e-5) self.assertEqual(output_waveform, sox_output_waveform, atol=1e-4, rtol=1e-5)
@unittest.skipIf("sox" not in BACKENDS, "sox not available")
@AudioBackendScope("sox")
def test_bass(self):
"""
Test biquad bass filter, compare to SoX implementation
"""
central_freq = 1000
q = 0.707
gain = 40
noise_filepath = common_utils.get_asset_path('whitenoise.wav')
E = torchaudio.sox_effects.SoxEffectsChain()
E.set_input_file(noise_filepath)
E.append_effect_to_chain("bass", [gain, central_freq, str(q) + 'q'])
sox_output_waveform, sr = E.sox_build_flow_effects()
waveform, sample_rate = torchaudio.load(noise_filepath, normalization=True)
output_waveform = F.bass_biquad(waveform, sample_rate, gain, central_freq, q)
self.assertEqual(output_waveform, sox_output_waveform, atol=1.5e-4, rtol=1e-5)
@unittest.skipIf("sox" not in BACKENDS, "sox not available") @unittest.skipIf("sox" not in BACKENDS, "sox not available")
@AudioBackendScope("sox") @AudioBackendScope("sox")
def test_deemph(self): def test_deemph(self):
......
...@@ -367,6 +367,21 @@ class Functional(common_utils.TestBaseMixin): ...@@ -367,6 +367,21 @@ class Functional(common_utils.TestBaseMixin):
self._assert_consistency(func, waveform) self._assert_consistency(func, waveform)
def test_bass(self):
if self.dtype == torch.float64:
raise unittest.SkipTest("This test is known to fail for float64")
waveform = common_utils.get_whitenoise(sample_rate=44100)
def func(tensor):
sample_rate = 44100
gain = 40.
central_freq = 1000.
q = 0.707
return F.bass_biquad(tensor, sample_rate, gain, central_freq, q)
self._assert_consistency(func, waveform)
def test_deemph(self): def test_deemph(self):
if self.dtype == torch.float64: if self.dtype == torch.float64:
raise unittest.SkipTest("This test is known to fail for float64") raise unittest.SkipTest("This test is known to fail for float64")
......
...@@ -29,6 +29,7 @@ __all__ = [ ...@@ -29,6 +29,7 @@ __all__ = [
"equalizer_biquad", "equalizer_biquad",
"band_biquad", "band_biquad",
"treble_biquad", "treble_biquad",
"bass_biquad",
"deemph_biquad", "deemph_biquad",
"riaa_biquad", "riaa_biquad",
"biquad", "biquad",
...@@ -988,6 +989,47 @@ def treble_biquad( ...@@ -988,6 +989,47 @@ def treble_biquad(
return biquad(waveform, b0, b1, b2, a0, a1, a2) return biquad(waveform, b0, b1, b2, a0, a1, a2)
def bass_biquad(
waveform: Tensor,
sample_rate: int,
gain: float,
central_freq: float = 100,
Q: float = 0.707
) -> Tensor:
r"""Design a bass tone-control effect. Similar to SoX implementation.
Args:
waveform (Tensor): audio waveform of dimension of `(..., time)`
sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz)
gain (float): desired gain at the boost (or attenuation) in dB.
central_freq (float, optional): central frequency (in Hz). (Default: ``100``)
Q (float, optional): https://en.wikipedia.org/wiki/Q_factor (Default: ``0.707``).
Returns:
Tensor: Waveform of dimension of `(..., time)`
References:
http://sox.sourceforge.net/sox.html
https://www.w3.org/2011/audio/audio-eq-cookbook.html#APF
"""
w0 = 2 * math.pi * central_freq / sample_rate
alpha = math.sin(w0) / 2 / Q
A = math.exp(gain / 40 * math.log(10))
temp1 = 2 * math.sqrt(A) * alpha
temp2 = (A - 1) * math.cos(w0)
temp3 = (A + 1) * math.cos(w0)
b0 = A * ((A + 1) - temp2 + temp1)
b1 = 2 * A * ((A - 1) - temp3)
b2 = A * ((A + 1) - temp2 - temp1)
a0 = (A + 1) + temp2 + temp1
a1 = -2 * ((A - 1) + temp3)
a2 = (A + 1) + temp2 - temp1
return biquad(waveform, b0 / a0, b1 / a0, b2 / a0, a0 / a0, a1 / a0, a2 / a0)
def deemph_biquad( def deemph_biquad(
waveform: Tensor, waveform: Tensor,
sample_rate: int sample_rate: int
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment