Source code for zounds.timeseries.samplerate


from .duration import Picoseconds
from collections import namedtuple
import numpy as np
from .duration import Seconds

Stride = namedtuple('Stride', ['frequency', 'duration'])


[docs]class SampleRate(object): """ `SampleRate` describes the constant frequency at which samples are taken from a continuous signal, and the duration of each sample. Instances of this class could describe an audio sampling rate (e.g. 44.1kHz) or the strided windows often used in short-time fourier transforms Args: frequency (numpy.timedelta64): The frequency at which the signal is sampled duration (numpy.timedelta64): The duration of each sample Raises: ValueError: when frequency or duration are less than or equal to zero Examples: >>> from zounds import Seconds, SampleRate >>> sr = SampleRate(Seconds(1), Seconds(2)) >>> sr.frequency numpy.timedelta64(1,'s') >>> sr.duration numpy.timedelta64(2,'s') >>> sr.overlap numpy.timedelta64(1,'s') >>> sr.overlap_ratio 0.5 See Also: :class:`SR96000` :class:`SR48000` :class:`SR44100` :class:`SR22050` :class:`SR11025` """ def __init__(self, frequency, duration): if frequency.astype(np.int) <= 0: raise ValueError('frequency must be positive') if duration.astype(np.int) <= 0: raise ValueError('duration must be positive') self.frequency = frequency self.duration = duration super(SampleRate, self).__init__() def __str__(self): f = self.frequency / Seconds(1) d = self.duration / Seconds(1) return '{self.__class__.__name__}(f={f}, d={d})'.format(**locals()) def __repr__(self): return self.__str__() def __iter__(self): return iter((self.frequency, self.duration)) def __len__(self): return 2 def __eq__(self, other): return \ self.frequency == other.frequency \ and self.duration == other.duration @property def overlap(self): """ For sampling schemes that overlap, return a :class:`numpy.timedelta64` instance representing the duration of overlap between each sample """ return self.duration - self.frequency @property def overlap_ratio(self): """ For sampling schemes that overlap, return the ratio of overlap to sample duration """ return self.overlap / self.duration @property def samples_per_second(self): return int(Picoseconds(int(1e12)) / self.frequency) def __int__(self): return self.samples_per_second @property def nyquist(self): return self.samples_per_second // 2 def __mul__(self, other): try: if len(other) == 1: other *= 2 except TypeError: other = (other, other) freq = self.frequency * other[0] duration = (self.frequency * other[1]) + self.overlap new = SampleRate(freq, duration) return new def discrete_samples(self, ts): td = next(dim for dim in ts.dimensions if hasattr(dim, 'frequency')) windowsize = np.round((self.duration - td.overlap) / td.frequency) stepsize = np.round(self.frequency / td.frequency) return int(stepsize), int(windowsize) def resample(self, ratio): orig_freq = Picoseconds(int(self.frequency / Picoseconds(1))) orig_duration = Picoseconds(int(self.duration / Picoseconds(1))) f = orig_freq * ratio d = orig_duration * ratio return SampleRate(f, d)
class AudioSampleRate(SampleRate): def __init__(self, samples_per_second, suggested_window, suggested_hop): self.suggested_hop = suggested_hop self.suggested_window = suggested_window self.one_sample = Picoseconds(int(1e12)) // samples_per_second super(AudioSampleRate, self).__init__(self.one_sample, self.one_sample) def half_lapped(self): return SampleRate( self.one_sample * self.suggested_hop, self.one_sample * self.suggested_window) def windowing_scheme(self, duration_samples, frequency_samples=None): frequency_samples = frequency_samples or duration_samples return SampleRate( self.frequency * frequency_samples, self.duration * duration_samples)
[docs]class SR96000(AudioSampleRate): """ A :class:`SampleRate` representing the common audio sampling rate 96kHz Examples: >>> from zounds import SR96000 >>> sr = SR96000() >>> sr.samples_per_second 96000 >>> int(sr) 96000 >>> sr.nyquist 48000 """ def __init__(self): super(SR96000, self).__init__(96000, 4096, 2048)
[docs]class SR48000(AudioSampleRate): """ A :class:`SampleRate` representing the common audio sampling rate 48kHz Examples: >>> from zounds import SR48000 >>> sr = SR48000() >>> sr.samples_per_second 48000 >>> int(sr) 48000 >>> sr.nyquist 24000 """ def __init__(self): super(SR48000, self).__init__(48000, 2048, 1024)
[docs]class SR44100(AudioSampleRate): """ A :class:`SampleRate` representing the common audio sampling rate 44.1kHz Examples: >>> from zounds import SR44100 >>> sr = SR44100() >>> sr.samples_per_second 44100 >>> int(sr) 44100 >>> sr.nyquist 22050 """ def __init__(self): super(SR44100, self).__init__(44100, 2048, 1024)
[docs]class SR22050(AudioSampleRate): """ A :class:`SampleRate` representing the common audio sampling rate 22.025kHz Examples: >>> from zounds import SR22050 >>> sr = SR22050() >>> sr.samples_per_second 22050 >>> int(sr) 22050 >>> sr.nyquist 11025 """ def __init__(self): super(SR22050, self).__init__(22050, 1024, 512)
class SR16000(AudioSampleRate): """ A :class:`SampleRate` representing the common audio sampling rate 16kHz Examples: >>> from zounds import SR16000 >>> sr = SR16000() >>> sr.samples_per_second 16000 >>> int(sr) 16000 >>> sr.nyquist 8000 """ def __init__(self): super(SR16000, self).__init__(16000, 512, 256)
[docs]class SR11025(AudioSampleRate): """ A :class:`SampleRate` representing the common audio sampling rate 11.025kHz Examples: >>> from zounds import SR11025 >>> sr = SR11025() >>> sr.samples_per_second 11025 >>> int(sr) 11025 >>> sr.nyquist 5512 """ def __init__(self): super(SR11025, self).__init__(11025, 512, 256)
_samplerates = ( SR96000(), SR48000(), SR44100(), SR22050(), SR16000(), SR11025()) def audio_sample_rate(samples_per_second): for sr in _samplerates: if samples_per_second == sr.samples_per_second: return sr raise ValueError( '{samples_per_second} is an invalid sample rate'.format(**locals())) def nearest_audio_sample_rate(samples_per_second): samplerates = np.array([s.samples_per_second for s in _samplerates]) diffs = np.abs(samples_per_second - samplerates) return _samplerates[np.argmin(diffs)] class HalfLapped(SampleRate): def __init__(self, window_at_44100=2048, hop_at_44100=1024): one_sample_at_44100 = Picoseconds(int(1e12)) / 44100. window = one_sample_at_44100 * window_at_44100 step = one_sample_at_44100 * hop_at_44100 super(HalfLapped, self).__init__(step, window)