def test_equals(self): fb1 = FrequencyBand(20, 20000) scale1 = LinearScale(fb1, 100) fb2 = FrequencyBand(20, 20000) scale2 = LinearScale(fb2, 100) self.assertEqual(scale1, scale2)
def test_not_equal_when_bands_differ(self): fb1 = FrequencyBand(20, 20000) scale1 = LinearScale(fb1, 100) fb2 = FrequencyBand(20, 20000) scale2 = LinearScale(fb2, 50) self.assertNotEqual(scale1, scale2)
def test_can_slice_frequency_dim_with_negative_stop_hz(self): scale = LinearScale(FrequencyBand(0, 100), 10) arr = ArrayWithUnits( np.zeros((13, 10)), [IdentityDimension(), FrequencyDimension(scale)]) sliced = arr[:, :-Hertz(20)] self.assertEqual((13, 8), sliced.shape) self.assertEqual( FrequencyDimension(LinearScale(FrequencyBand(0, 80), 8)), sliced.dimensions[-1])
def test_from_example(self): td = TimeDimension(Seconds(1), Seconds(1)) fd = FrequencyDimension(LinearScale(FrequencyBand(20, 22050), 100)) tf = ArrayWithUnits(np.ones((30, 100)), [td, fd]) from_example = ArrayWithUnits.from_example(np.ones((30, 100)), tf) self.assertEqual(tf.shape, from_example.shape) self.assertItemsEqual(tf.dimensions, from_example.dimensions)
def test_can_slice_frequency_dim_with_start_and_end_hz(self): scale = LinearScale(FrequencyBand(0, 100), 10) arr = ArrayWithUnits( np.zeros((13, 10)), [IdentityDimension(), FrequencyDimension(scale)]) sliced = arr[:, Hertz(20):Hertz(80)] self.assertEqual((13, 7), sliced.shape)
def fft(x, axis=-1, padding_samples=0): """ Apply an FFT along the given dimension, and with the specified amount of zero-padding Args: x (ArrayWithUnits): an :class:`~zounds.core.ArrayWithUnits` instance which has one or more :class:`~zounds.timeseries.TimeDimension` axes axis (int): The axis along which the fft should be applied padding_samples (int): The number of padding zeros to apply along axis before performing the FFT """ if padding_samples > 0: padded = np.concatenate( [x, np.zeros((len(x), padding_samples), dtype=x.dtype)], axis=axis) else: padded = x transformed = np.fft.rfft(padded, axis=axis, norm='ortho') sr = audio_sample_rate(int(Seconds(1) / x.dimensions[axis].frequency)) scale = LinearScale.from_sample_rate(sr, transformed.shape[-1]) new_dimensions = list(x.dimensions) new_dimensions[axis] = FrequencyDimension(scale) return ArrayWithUnits(transformed, new_dimensions)
def test_can_invert_frequency_weighting(self): td = TimeDimension(Seconds(1), Seconds(1)) fd = FrequencyDimension(LinearScale(FrequencyBand(20, 22050), 100)) tf = ArrayWithUnits(np.random.random_sample((90, 100)), [td, fd]) weighted = tf * AWeighting() inverted = weighted / AWeighting() np.testing.assert_allclose(tf, inverted)
def test_can_apply_a_weighting_to_time_frequency_representation(self): td = TimeDimension(Seconds(1), Seconds(1)) fd = FrequencyDimension(LinearScale(FrequencyBand(20, 22050), 100)) tf = ArrayWithUnits(np.ones((90, 100)), [td, fd]) weighting = AWeighting() result = tf * weighting self.assertGreater(result[0, -1], result[0, 0])
def test_can_get_weights_from_tf_representation(self): td = TimeDimension(Seconds(1), Seconds(1)) fd = FrequencyDimension(LinearScale(FrequencyBand(20, 22050), 100)) tf = ArrayWithUnits(np.ones((90, 100)), [td, fd]) weighting = AWeighting() weights = weighting.weights(tf) self.assertEqual((100, ), weights.shape)
def test_matches_fftfreq(self): samplerate = SR44100() n_bands = 2048 fft_freqs = np.fft.rfftfreq(n_bands, 1 / int(samplerate)) bands = LinearScale.from_sample_rate(samplerate, n_bands // 2) linear_freqs = np.array([b.start_hz for b in bands]) np.testing.assert_allclose(linear_freqs, fft_freqs[:-1])
def test_can_construct_instance(self): frequency = Seconds(1) duration = Seconds(1) scale = LinearScale(FrequencyBand(20, 22050), 100) td = TimeDimension(frequency, duration) fd = FrequencyDimension(scale) tf = ArrayWithUnits(np.zeros((30, 100)), [td, fd]) self.assertIsInstance(tf, ArrayWithUnits)
def test_not_equal_when_scale_differs(self): fb1 = FrequencyBand(20, 20000) scale1 = LinearScale(fb1, 100) fb2 = FrequencyBand(20, 20000) scale2 = GeometricScale(20, 20000, 0.01, 100) self.assertNotEqual(scale1, scale2)
def test_can_use_negative_axis_indices_max(self): td = TimeDimension(Seconds(1), Seconds(1)) fd = FrequencyDimension(LinearScale(FrequencyBand(20, 22050), 100)) tf = ArrayWithUnits(np.ones((30, 100)), [td, fd]) result = tf.max(axis=-1) self.assertIsInstance(result, ArrayWithUnits) self.assertEqual(1, len(result.dimensions)) self.assertEqual((30,), result.shape) self.assertIsInstance(result.dimensions[0], TimeDimension)
def test_sum_along_frequency_axis(self): td = TimeDimension(Seconds(1), Seconds(1)) fd = FrequencyDimension(LinearScale(FrequencyBand(20, 22050), 100)) tf = ArrayWithUnits(np.ones((30, 100)), [td, fd]) result = tf.sum(axis=1) self.assertIsInstance(result, ArrayWithUnits) self.assertEqual(1, len(result.dimensions)) self.assertEqual((30,), result.shape) self.assertIsInstance(result.dimensions[0], TimeDimension)
def _process(self, data): transformed = self._process_raw(data) sr = audio_sample_rate(data.dimensions[1].samples_per_second) scale = LinearScale.from_sample_rate(sr, transformed.shape[1]) yield ArrayWithUnits( transformed, [data.dimensions[0], FrequencyDimension(scale)])
def _process(self, data): raw = self._process_raw(data) sr = audio_sample_rate( int(data.shape[1] / data.dimensions[0].duration_in_seconds)) scale = LinearScale.from_sample_rate( sr, data.shape[1], always_even=self.scale_always_even) yield ArrayWithUnits( raw, [data.dimensions[0], FrequencyDimension(scale)])
def test_can_multiply_by_array(self): frequency = Seconds(1) duration = Seconds(1) scale = LinearScale(FrequencyBand(20, 22050), 100) td = TimeDimension(frequency, duration) fd = FrequencyDimension(scale) tf = ArrayWithUnits(np.ones((30, 100)), [td, fd]) result = tf * np.ones(100) self.assertIsInstance(result, ArrayWithUnits) np.testing.assert_allclose(tf, result)
def test_can_access_time_slice_and_int_index(self): tf = ArrayWithUnits( np.ones((10, 10)), dimensions=[ TimeDimension(Seconds(1), Seconds(1)), FrequencyDimension(LinearScale(FrequencyBand(0, 1000), 10)) ]) sliced = tf[TimeSlice(start=Seconds(1), duration=Seconds(2)), 0] self.assertEqual((2,), sliced.shape) self.assertIsInstance(sliced.dimensions[0], TimeDimension)
def test_can_access_int_index_and_frequency_band(self): tf = ArrayWithUnits( np.ones((10, 10)), dimensions=[ TimeDimension(Seconds(1), Seconds(1)), FrequencyDimension(LinearScale(FrequencyBand(0, 1000), 10)) ]) sliced = tf[0, FrequencyBand(201, 400)] self.assertEqual((2,), sliced.shape) self.assertIsInstance(sliced.dimensions[0], FrequencyDimension)
def test_can_slice_frequency_dimension_with_integer_indices(self): frequency = Seconds(1) duration = Seconds(1) scale = LinearScale(FrequencyBand(20, 22050), 100) td = TimeDimension(frequency, duration) fd = FrequencyDimension(scale) tf = ArrayWithUnits(np.zeros((30, 100)), [td, fd]) sliced = tf[:, 10: 20] self.assertEqual((30, 10), sliced.shape) self.assertIsInstance(sliced, ArrayWithUnits)
def test_can_use_keepdims_with_sum(self): td = TimeDimension(Seconds(1), Seconds(1)) fd = FrequencyDimension(LinearScale(FrequencyBand(20, 22050), 100)) tf = ArrayWithUnits(np.ones((30, 100)), [td, fd]) result = tf.sum(axis=-1, keepdims=True) self.assertIsInstance(result, ArrayWithUnits) self.assertEqual(2, len(result.dimensions)) self.assertEqual((30, 1), result.shape) self.assertIsInstance(result.dimensions[0], TimeDimension) self.assertIsInstance(result.dimensions[1], IdentityDimension)
def _process(self, data): transformed = dct(data, norm='ortho', axis=self._axis) sr = audio_sample_rate( int(data.shape[1] / data.dimensions[0].duration_in_seconds)) scale = LinearScale.from_sample_rate( sr, transformed.shape[-1], always_even=self.scale_always_even) yield ArrayWithUnits( transformed, [data.dimensions[0], FrequencyDimension(scale)])
def test_raises_if_scale_length_does_not_match_frequency_dimension(self): frequency = Seconds(1) duration = Seconds(1) scale = LinearScale(FrequencyBand(20, 22050), 1000) td = TimeDimension(frequency, duration) fd = FrequencyDimension(scale) self.assertRaises( ValueError, lambda: ArrayWithUnits(np.ones((30, 100)), [td, fd]))
def test_can_add_axis_at_end(self): _id = IdentityDimension() td = TimeDimension(Seconds(1), Seconds(1)) fd = FrequencyDimension(LinearScale(FrequencyBand(20, 22050), 100)) tf = ArrayWithUnits(np.ones((3, 30, 100)), [_id, td, fd]) tf2 = tf[..., None] self.assertEqual(4, tf2.ndim) self.assertIsInstance(tf2.dimensions[0], IdentityDimension) self.assertIsInstance(tf2.dimensions[1], TimeDimension) self.assertIsInstance(tf2.dimensions[2], FrequencyDimension) self.assertIsInstance(tf2.dimensions[3], IdentityDimension)
def test_can_get_all_even_sized_bands(self): samplerate = SR44100() scale = LinearScale.from_sample_rate(samplerate, 44100, always_even=True) log_scale = GeometricScale(20, 20000, 0.01, 64) slices = [scale.get_slice(band) for band in log_scale] sizes = [s.stop - s.start for s in slices] self.assertTrue( not any([s % 2 for s in sizes]), 'All slice sizes should be even but were {sizes}'.format( **locals()))
def test_can_use_tuple_indices_for_first_dimension(self): tf = ArrayWithUnits( np.ones((10, 10)), dimensions=[ TimeDimension(Seconds(1), Seconds(1)), FrequencyDimension(LinearScale(FrequencyBand(0, 1000), 10)) ]) subset = tf[tuple([2, 4, 6]), ...] self.assertEqual((3, 10), subset.shape) self.assertIsInstance(subset, ArrayWithUnits) self.assertIsInstance(subset.dimensions[0], TimeDimension) self.assertIsInstance(subset.dimensions[1], FrequencyDimension)
def test_ellipsis(self): scale = LinearScale(FrequencyBand(0, 10000), 100) arr = ArrayWithUnits( np.zeros((10, 3, 100)), [IdentityDimension(), TimeDimension(Seconds(1)), FrequencyDimension(scale)]) sliced = arr[..., FrequencyBand(1000, 5000)] self.assertEqual((10, 3, 41), sliced.shape) self.assertIsInstance(sliced.dimensions[0], IdentityDimension) self.assertIsInstance(sliced.dimensions[1], TimeDimension) self.assertIsInstance(sliced.dimensions[2], FrequencyDimension)
def test_can_slice_freq_dimension_with_freq_band_spanning_bins(self): frequency = Seconds(1) duration = Seconds(1) scale = LinearScale(FrequencyBand(20, 22050), 100) td = TimeDimension(frequency, duration) fd = FrequencyDimension(scale) tf = ArrayWithUnits(np.zeros((30, 100)), [td, fd]) bands = list(scale) wide_band = FrequencyBand(bands[0].start_hz, bands[9].stop_hz) sliced = tf[:, wide_band] self.assertEqual((30, 10), sliced.shape) self.assertIsInstance(sliced, ArrayWithUnits)
def test_can_iterate_over_time_frequency_representation(self): tf = ArrayWithUnits( np.ones((10, 10)), dimensions=[ TimeDimension(Seconds(1), Seconds(1)), FrequencyDimension(LinearScale(FrequencyBand(0, 1000), 10)) ]) rows = [row for row in tf] self.assertEqual(10, len(rows)) for row in rows: self.assertIsInstance(row, ArrayWithUnits) self.assertIsInstance(row.dimensions[0], FrequencyDimension) self.assertEqual((10,), row.shape)
def test_scale_is_modified_after_slice(self): frequency = Seconds(1) duration = Seconds(1) scale = LinearScale(FrequencyBand(20, 22050), 100) td = TimeDimension(frequency, duration) fd = FrequencyDimension(scale) tf = ArrayWithUnits(np.zeros((30, 100)), [td, fd]) bands = list(scale) wide_band = FrequencyBand(bands[0].start_hz, bands[9].stop_hz) sliced = tf[:, wide_band] self.assertEqual((30, 10), sliced.shape) self.assertIsInstance(sliced, ArrayWithUnits) self.assertLess(sliced.dimensions[1].scale.stop_hz, scale.stop_hz) self.assertEqual(10, sliced.dimensions[1].scale.n_bands)