From 28e126bf247f5d735e00f6e5f2b1ce81a5f63d9c Mon Sep 17 00:00:00 2001 From: Ji Chen Date: Mon, 25 May 2020 19:06:46 -0700 Subject: [PATCH 1/5] add bass with biquad --- docs/source/functional.rst | 5 ++++ test/test_sox_compatibility.py | 22 ++++++++++++++++++ torchaudio/functional.py | 42 ++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/docs/source/functional.rst b/docs/source/functional.rst index 5f68666539..da5cae0516 100644 --- a/docs/source/functional.rst +++ b/docs/source/functional.rst @@ -113,6 +113,11 @@ Functions to perform common audio operations. .. autofunction:: treble_biquad +:hidden:`bass_biquad` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autofunction:: bass_biquad + :hidden:`deemph_biquad` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/test/test_sox_compatibility.py b/test/test_sox_compatibility.py index 2c8394ff45..142f5633e4 100644 --- a/test/test_sox_compatibility.py +++ b/test/test_sox_compatibility.py @@ -265,6 +265,28 @@ def test_treble(self): 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=1e-4, rtol=1e-5) + @unittest.skipIf("sox" not in BACKENDS, "sox not available") @AudioBackendScope("sox") def test_deemph(self): diff --git a/torchaudio/functional.py b/torchaudio/functional.py index 94ff81526a..7354e168cd 100644 --- a/torchaudio/functional.py +++ b/torchaudio/functional.py @@ -29,6 +29,7 @@ "equalizer_biquad", "band_biquad", "treble_biquad", + "bass_biquad", "deemph_biquad", "riaa_biquad", "biquad", @@ -983,6 +984,47 @@ def treble_biquad( 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, b1, b2, a0, a1, a2) + + def deemph_biquad( waveform: Tensor, sample_rate: int From da5a9979c36d64c9320e83a3dfe4302a1f3cc0df Mon Sep 17 00:00:00 2001 From: jimchen90 <65626796+jimchen90@users.noreply.github.com> Date: Tue, 2 Jun 2020 14:08:35 -0400 Subject: [PATCH 2/5] Update functional.py Add the normalization coefficients --- torchaudio/functional.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/torchaudio/functional.py b/torchaudio/functional.py index 7354e168cd..a698bed0c6 100644 --- a/torchaudio/functional.py +++ b/torchaudio/functional.py @@ -1022,7 +1022,7 @@ def bass_biquad( a1 = -2 * ((A - 1) + temp3) a2 = (A + 1) + temp2 - temp1 - return biquad(waveform, b0, b1, b2, a0, a1, a2) + return biquad(waveform, b0/a0, b1/a0, b2/a0, a0/a0, a1/a0, a2/a0) def deemph_biquad( From 26351a07a088f7f36d6d53d15d7a887c737dd1f8 Mon Sep 17 00:00:00 2001 From: jimchen90 <65626796+jimchen90@users.noreply.github.com> Date: Tue, 2 Jun 2020 14:28:11 -0400 Subject: [PATCH 3/5] 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 --- test/test_sox_compatibility.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/test/test_sox_compatibility.py b/test/test_sox_compatibility.py index 142f5633e4..26ee536bb1 100644 --- a/test/test_sox_compatibility.py +++ b/test/test_sox_compatibility.py @@ -285,8 +285,30 @@ def test_bass(self): 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=1e-4, rtol=1e-5) + self.assertEqual(output_waveform, sox_output_waveform, atol=1.5e-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 = 30 + 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=1e-4, rtol=1e-5) + @unittest.skipIf("sox" not in BACKENDS, "sox not available") @AudioBackendScope("sox") def test_deemph(self): From 5ef7fd065b447c2868f479665dc2260e13765983 Mon Sep 17 00:00:00 2001 From: jimchen90 <65626796+jimchen90@users.noreply.github.com> Date: Tue, 2 Jun 2020 14:30:29 -0400 Subject: [PATCH 4/5] Update torchscript_consistency_impl.py Add torchscript test --- test/torchscript_consistency_impl.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/torchscript_consistency_impl.py b/test/torchscript_consistency_impl.py index 4ee9149843..d46f96f6cc 100644 --- a/test/torchscript_consistency_impl.py +++ b/test/torchscript_consistency_impl.py @@ -366,6 +366,21 @@ def func(tensor): return F.treble_biquad(tensor, sample_rate, gain, central_freq, q) 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): if self.dtype == torch.float64: From cb926adeb76621e9ff1f72f7e99d9fc737236341 Mon Sep 17 00:00:00 2001 From: Ji Chen Date: Tue, 2 Jun 2020 14:31:53 -0700 Subject: [PATCH 5/5] add flake8 test --- test/test_sox_compatibility.py | 22 ---------------------- test/torchscript_consistency_impl.py | 2 +- torchaudio/functional.py | 2 +- 3 files changed, 2 insertions(+), 24 deletions(-) diff --git a/test/test_sox_compatibility.py b/test/test_sox_compatibility.py index 26ee536bb1..5239f56ae8 100644 --- a/test/test_sox_compatibility.py +++ b/test/test_sox_compatibility.py @@ -287,28 +287,6 @@ def test_bass(self): self.assertEqual(output_waveform, sox_output_waveform, atol=1.5e-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 = 30 - - 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=1e-4, rtol=1e-5) - @unittest.skipIf("sox" not in BACKENDS, "sox not available") @AudioBackendScope("sox") def test_deemph(self): diff --git a/test/torchscript_consistency_impl.py b/test/torchscript_consistency_impl.py index d46f96f6cc..b8967be41a 100644 --- a/test/torchscript_consistency_impl.py +++ b/test/torchscript_consistency_impl.py @@ -366,7 +366,7 @@ def func(tensor): return F.treble_biquad(tensor, sample_rate, gain, central_freq, q) 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") diff --git a/torchaudio/functional.py b/torchaudio/functional.py index a698bed0c6..8491e6dafc 100644 --- a/torchaudio/functional.py +++ b/torchaudio/functional.py @@ -1022,7 +1022,7 @@ def bass_biquad( 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) + return biquad(waveform, b0 / a0, b1 / a0, b2 / a0, a0 / a0, a1 / a0, a2 / a0) def deemph_biquad(