def test_amplitude_to_DB_not_channelwise_clamps(self): """Check that clamps are applied per-item, not per channel.""" amplitude_mult = 20. amin = 1e-10 ref = 1.0 db_mult = math.log10(max(amin, ref)) top_db = 40. torch.manual_seed(0) spec = torch.rand([1, 2, 100, 100]) * 200 # Make one channel blow out the other spec[:, 0] += 50 specwise_dbs = F.amplitude_to_DB(spec, amplitude_mult, amin, db_mult, top_db=top_db) channelwise_dbs = torch.stack([ F.amplitude_to_DB(spec[:, i], amplitude_mult, amin, db_mult, top_db=top_db) for i in range(spec.size(-3)) ]) # Just check channelwise gives a different answer. difference = (specwise_dbs - channelwise_dbs).abs() assert (difference >= 1e-5).any()
def test_amplitude_to_DB_itemwise_clamps(self): """Ensure that the clamps are separate for each spectrogram in a batch. The clamp was determined per-batch in a prior implementation, which meant it was determined by the loudest item, thus items weren't independent. See: https://github.com/pytorch/audio/issues/994 """ amplitude_mult = 20. amin = 1e-10 ref = 1.0 db_mult = math.log10(max(amin, ref)) top_db = 20. # Make a batch of noise torch.manual_seed(0) spec = torch.rand([2, 2, 100, 100]) * 200 # Make one item blow out the other spec[0] += 50 batchwise_dbs = F.amplitude_to_DB(spec, amplitude_mult, amin, db_mult, top_db=top_db) itemwise_dbs = torch.stack([ F.amplitude_to_DB(item, amplitude_mult, amin, db_mult, top_db=top_db) for item in spec ]) self.assertEqual(batchwise_dbs, itemwise_dbs)
def test_amplitude_to_DB(self): spec = torch.rand((6, 201)) amin = 1e-10 db_multiplier = 0.0 top_db = 80.0 # Power to DB multiplier = 10.0 ta_out = F.amplitude_to_DB(spec, multiplier, amin, db_multiplier, top_db) lr_out = librosa.core.power_to_db(spec.numpy()) lr_out = torch.from_numpy(lr_out) self.assertEqual(ta_out, lr_out, atol=5e-5, rtol=1e-5) # Amplitude to DB multiplier = 20.0 ta_out = F.amplitude_to_DB(spec, multiplier, amin, db_multiplier, top_db) lr_out = librosa.core.amplitude_to_db(spec.numpy()) lr_out = torch.from_numpy(lr_out) self.assertEqual(ta_out, lr_out, atol=5e-5, rtol=1e-5)
def test_amplitude_to_DB_reversible(self, shape): """Round trip between amplitude and db should return the original for various shape This implicitly also tests `DB_to_amplitude`. """ amplitude_mult = 20. power_mult = 10. amin = 1e-10 ref = 1.0 db_mult = math.log10(max(amin, ref)) torch.manual_seed(0) spec = torch.rand(*shape, dtype=self.dtype, device=self.device) * 200 # Spectrogram amplitude -> DB -> amplitude db = F.amplitude_to_DB(spec, amplitude_mult, amin, db_mult, top_db=None) x2 = F.DB_to_amplitude(db, ref, 0.5) self.assertEqual(x2, spec, atol=5e-5, rtol=1e-5) # Spectrogram power -> DB -> power db = F.amplitude_to_DB(spec, power_mult, amin, db_mult, top_db=None) x2 = F.DB_to_amplitude(db, ref, 1.) self.assertEqual(x2, spec)
def test_DB_to_amplitude(self): # Make some noise x = torch.rand(1000) spectrogram = torchaudio.transforms.Spectrogram() spec = spectrogram(x) amin = 1e-10 ref = 1.0 db_multiplier = math.log10(max(amin, ref)) # Waveform amplitude -> DB -> amplitude multiplier = 20. power = 0.5 db = F.amplitude_to_DB(torch.abs(x), multiplier, amin, db_multiplier, top_db=None) x2 = F.DB_to_amplitude(db, ref, power) self.assertEqual(x2, torch.abs(x), atol=5e-5, rtol=1e-5) # Spectrogram amplitude -> DB -> amplitude db = F.amplitude_to_DB(spec, multiplier, amin, db_multiplier, top_db=None) x2 = F.DB_to_amplitude(db, ref, power) self.assertEqual(x2, spec, atol=5e-5, rtol=1e-5) # Waveform power -> DB -> power multiplier = 10. power = 1. db = F.amplitude_to_DB(x, multiplier, amin, db_multiplier, top_db=None) x2 = F.DB_to_amplitude(db, ref, power) self.assertEqual(x2, torch.abs(x), atol=5e-5, rtol=1e-5) # Spectrogram power -> DB -> power db = F.amplitude_to_DB(spec, multiplier, amin, db_multiplier, top_db=None) x2 = F.DB_to_amplitude(db, ref, power) self.assertEqual(x2, spec, atol=5e-5, rtol=1e-5)
def func(tensor): multiplier = 10.0 amin = 1e-10 db_multiplier = 0.0 top_db = 80.0 return F.amplitude_to_DB(tensor, multiplier, amin, db_multiplier, top_db)
def test_amplitude_to_DB_top_db_clamp(self, shape): """Ensure values are properly clamped when `top_db` is supplied.""" amplitude_mult = 20. amin = 1e-10 ref = 1.0 db_mult = math.log10(max(amin, ref)) top_db = 40. torch.manual_seed(0) # A random tensor is used for increased entropy, but the max and min for # each spectrogram still need to be predictable. The max determines the # decibel cutoff, and the distance from the min must be large enough # that it triggers a clamp. spec = torch.rand(*shape, dtype=self.dtype, device=self.device) # Ensure each spectrogram has a min of 0 and a max of 1. spec -= spec.amin([-2, -1])[..., None, None] spec /= spec.amax([-2, -1])[..., None, None] # Expand the range to (0, 200) - wide enough to properly test clamping. spec *= 200 decibels = F.amplitude_to_DB(spec, amplitude_mult, amin, db_mult, top_db=top_db) # Ensure the clamp was applied below_limit = decibels < 6.0205 assert not below_limit.any(), ( "{} decibel values were below the expected cutoff:\n{}".format( below_limit.sum().item(), decibels)) # Ensure it didn't over-clamp close_to_limit = decibels < 6.0207 assert close_to_limit.any(), ( f"No values were close to the limit. Did it over-clamp?\n{decibels}" )
def spec_to_db(x_spec, top_db=80, amin=1e-5): x_spec = x_spec.transpose(-3, -1).contiguous() x_spec = torch.view_as_complex(x_spec) x_mag = torch.abs(x_spec) x_db = amplitude_to_DB(x_mag, multiplier=20., amin=amin, db_multiplier=math.log10(max(amin, 1.)), top_db=top_db) return x_db
def test_amplitude_to_DB(self): amin = 1e-10 db_multiplier = 0.0 top_db = 80.0 multiplier = 20.0 spec = get_spectrogram(get_whitenoise(device=self.device, dtype=self.dtype), power=1) result = F.amplitude_to_DB(spec, multiplier, amin, db_multiplier, top_db) expected = librosa.core.amplitude_to_db(spec[0].cpu().numpy())[None, ...] self.assertEqual(result, torch.from_numpy(expected))
def forward(self, x: Tensor) -> Tensor: r"""Numerically stable implementation from Librosa. https://librosa.github.io/librosa/_modules/librosa/core/spectrum.html Args: x (Tensor): Input tensor before being converted to decibel scale. Returns: Tensor: Output tensor in decibel scale. """ return F.amplitude_to_DB(x, self.multiplier, self.amin, self.db_multiplier, self.top_db)
def forward(self, x: Tensor) -> Tensor: r"""Numerically stable implementation from Librosa. https://librosa.org/doc/latest/generated/librosa.amplitude_to_db.html Args: x (Tensor): Input tensor before being converted to decibel scale. Returns: Tensor: Output tensor in decibel scale. """ return F.amplitude_to_DB(x, self.multiplier, self.amin, self.db_multiplier, self.top_db)
def test_torchscript_amplitude_to_DB(self): @torch.jit.script def jit_method(spec, multiplier, amin, db_multiplier, top_db): # type: (Tensor, float, float, float, Optional[float]) -> Tensor return F.amplitude_to_DB(spec, multiplier, amin, db_multiplier, top_db) spec = torch.rand((6, 201)) multiplier = 10. amin = 1e-10 db_multiplier = 0. top_db = 80. jit_out = jit_method(spec, multiplier, amin, db_multiplier, top_db) py_out = F.amplitude_to_DB(spec, multiplier, amin, db_multiplier, top_db) self.assertTrue(torch.allclose(jit_out, py_out))
def jit_method(spec, multiplier, amin, db_multiplier, top_db): # type: (Tensor, float, float, float, Optional[float]) -> Tensor return F.amplitude_to_DB(spec, multiplier, amin, db_multiplier, top_db)
def __call__(self, x_spec): x_mag = ConvToMag()(x_spec) x_db = amplitude_to_DB(x_mag, multiplier=20., amin=1e-5, db_multiplier=math.log10(max(1e-5, 1.)), top_db=120.) return x_db