예제 #1
0
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)
예제 #2
0
def morlet_filter_bank(samplerate,
                       kernel_size,
                       scale,
                       scaling_factor,
                       normalize=True):
    """
    Create a bank of finite impulse response filters, with
    frequencies centered on the sub-bands of scale
    """
    basis_size = len(scale)
    basis = np.zeros((basis_size, kernel_size), dtype=np.complex128)

    try:
        if len(scaling_factor) != len(scale):
            raise ValueError('scaling factor must have same length as scale')
    except TypeError:
        scaling_factor = np.repeat(float(scaling_factor), len(scale))

    sr = int(samplerate)

    for i, band in enumerate(scale):
        scaling = scaling_factor[i]
        w = band.center_frequency / (scaling * 2 * sr / kernel_size)
        basis[i] = morlet(M=kernel_size, w=w, s=scaling)
    basis = basis.real

    if normalize:
        basis /= np.linalg.norm(basis, axis=-1, keepdims=True) + 1e-8

    basis = ArrayWithUnits(
        basis, [FrequencyDimension(scale),
                TimeDimension(*samplerate)])

    return basis
예제 #3
0
 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])
예제 #4
0
 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)
예제 #5
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)
예제 #6
0
    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)])
예제 #7
0
 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)])
예제 #8
0
    def square(self, n_coeffs, do_overlap_add=False):
        """
        Compute a "square" view of the frequency adaptive transform, by
        resampling each frequency band such that they all contain the same
        number of samples, and performing an overlap-add procedure in the
        case where the sample frequency and duration differ
        :param n_coeffs: The common size to which each frequency band should
        be resampled
        """
        resampled_bands = [
            self._resample(band, n_coeffs) for band in self.iter_bands()
        ]

        stacked = np.vstack(resampled_bands).T

        fdim = FrequencyDimension(self.scale)

        # TODO: This feels like it could be wrapped up nicely elsewhere
        chunk_frequency = Picoseconds(
            int(
                np.round(self.time_dimension.duration / Picoseconds(1) /
                         n_coeffs)))

        td = TimeDimension(frequency=chunk_frequency)

        arr = ConstantRateTimeSeries(
            ArrayWithUnits(stacked.reshape(-1, n_coeffs, self.n_bands),
                           dimensions=[self.time_dimension, td, fdim]))

        if not do_overlap_add:
            return arr

        # Begin the overlap add procedure
        overlap_ratio = self.time_dimension.overlap_ratio

        if overlap_ratio == 0:
            # no overlap add is necessary
            return ArrayWithUnits(stacked, [td, fdim])

        step_size_samples = int(n_coeffs * overlap_ratio)

        first_dim = int(
            np.round((stacked.shape[0] * overlap_ratio) +
                     (n_coeffs * overlap_ratio)))

        output = ArrayWithUnits(np.zeros((first_dim, self.n_bands)),
                                dimensions=[td, fdim])

        for i, chunk in enumerate(arr):
            start = step_size_samples * i
            stop = start + n_coeffs
            output[start:stop] += chunk.reshape((-1, self.n_bands))

        return output
예제 #9
0
    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)])
예제 #10
0
def fir_filter_bank(scale, taps, samplerate, window):
    basis = np.zeros((len(scale), taps))
    basis = ArrayWithUnits(
        basis, [FrequencyDimension(scale),
                TimeDimension(*samplerate)])

    nyq = samplerate.nyquist

    if window.ndim == 1:
        window = repeat(window, len(scale))

    for i, band, win in zip(xrange(len(scale)), scale, window):
        start_hz = max(0, band.start_hz)
        stop_hz = min(nyq, band.stop_hz)
        freqs = np.linspace(start_hz / nyq, stop_hz / nyq, len(win))
        freqs = [0] + list(freqs) + [1]
        gains = [0] + list(win) + [0]
        basis[i] = firwin2(taps, freqs, gains)

    return basis
예제 #11
0
 def test_equal(self):
     fd1 = FrequencyDimension(LinearScale(FrequencyBand(20, 10000), 100))
     fd2 = FrequencyDimension(LinearScale(FrequencyBand(20, 10000), 100))
     self.assertEqual(fd1, fd2)
예제 #12
0
 def test_not_equal(self):
     fd1 = FrequencyDimension(LinearScale(FrequencyBand(20, 10000), 100))
     fd2 = FrequencyDimension(GeometricScale(20, 10000, 0.01, 100))
     self.assertNotEqual(fd1, fd2)
예제 #13
0
def apply_scale(short_time_fft, scale, window=None):
    magnitudes = np.abs(short_time_fft.real)
    spectrogram = scale.apply(magnitudes, window)
    dimensions = short_time_fft.dimensions[:-1] + (FrequencyDimension(scale), )
    return ArrayWithUnits(spectrogram, dimensions)
예제 #14
0
 def _new_dim(self):
     return FrequencyDimension(self.scale)