""" Each generator will return float samples from -1.0 to 1.0, which can be converted to actual audio with 8, 16, 24, or 32 bit depth using the SiganlGenerator.to_audio_segment() method (on any of it's subclasses). See Wikipedia's "waveform" page for info on some of the generators included here: http://en.wikipedia.org/wiki/Waveform """ import math import array import itertools import random from .audio_segment import AudioSegment from .utils import ( db_to_float, get_frame_width, get_array_type, get_min_max_value ) class SignalGenerator(object): def __init__(self, sample_rate=44100, bit_depth=16): self.sample_rate = sample_rate self.bit_depth = bit_depth def to_audio_segment(self, duration=1000.0, volume=0.0): """ Duration in milliseconds (default: 1 second) Volume in DB relative to maximum amplitude (default 0.0 dBFS, which is the maximum value) """ minval, maxval = get_min_max_value(self.bit_depth) sample_width = get_frame_width(self.bit_depth) array_type = get_array_type(self.bit_depth) gain = db_to_float(volume) sample_count = int(self.sample_rate * (duration / 1000.0)) sample_data = (int(val * maxval * gain) for val in self.generate()) sample_data = itertools.islice(sample_data, 0, sample_count) data = array.array(array_type, sample_data) try: data = data.tobytes() except: data = data.tostring() return AudioSegment(data=data, metadata={ "channels": 1, "sample_width": sample_width, "frame_rate": self.sample_rate, "frame_width": sample_width, }) def generate(self): raise NotImplementedError("SignalGenerator subclasses must implement the generate() method, and *should not* call the superclass implementation.") class Sine(SignalGenerator): def __init__(self, freq, **kwargs): super(Sine, self).__init__(**kwargs) self.freq = freq def generate(self): sine_of = (self.freq * 2 * math.pi) / self.sample_rate sample_n = 0 while True: yield math.sin(sine_of * sample_n) sample_n += 1 class Pulse(SignalGenerator): def __init__(self, freq, duty_cycle=0.5, **kwargs): super(Pulse, self).__init__(**kwargs) self.freq = freq self.duty_cycle = duty_cycle def generate(self): sample_n = 0 # in samples cycle_length = self.sample_rate / float(self.freq) pulse_length = cycle_length * self.duty_cycle while True: if (sample_n % cycle_length) < pulse_length: yield 1.0 else: yield -1.0 sample_n += 1 class Square(Pulse): def __init__(self, freq, **kwargs): kwargs['duty_cycle'] = 0.5 super(Square, self).__init__(freq, **kwargs) class Sawtooth(SignalGenerator): def __init__(self, freq, duty_cycle=1.0, **kwargs): super(Sawtooth, self).__init__(**kwargs) self.freq = freq self.duty_cycle = duty_cycle def generate(self): sample_n = 0 # in samples cycle_length = self.sample_rate / float(self.freq) midpoint = cycle_length * self.duty_cycle ascend_length = midpoint descend_length = cycle_length - ascend_length while True: cycle_position = sample_n % cycle_length if cycle_position < midpoint: yield (2 * cycle_position / ascend_length) - 1.0 else: yield 1.0 - (2 * (cycle_position - midpoint) / descend_length) sample_n += 1 class Triangle(Sawtooth): def __init__(self, freq, **kwargs): kwargs['duty_cycle'] = 0.5 super(Triangle, self).__init__(freq, **kwargs) class WhiteNoise(SignalGenerator): def generate(self): while True: yield (random.random() * 2) - 1.0