From e8fb592ce96ef0f702668e90537bc8422a33adcd Mon Sep 17 00:00:00 2001 From: Bas Weelinck Date: Mon, 30 Apr 2012 19:13:13 +0200 Subject: [PATCH] Delay works. --- hoi2.py | 24 ++++++++++++++++++++++++ ysynth.py | 46 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 hoi2.py diff --git a/hoi2.py b/hoi2.py new file mode 100644 index 0000000..79f67b6 --- /dev/null +++ b/hoi2.py @@ -0,0 +1,24 @@ + +from ysynth import * + +synth = YSynth() +#synth.set_graph(Sin(440 + 110 * Sin(1))) +#synth.set_graph((Saw(440) + Sin(550)) * 0.25) +#synth.set_graph(Square(220) * 0.5) +#synth.set_graph(Sin(440 + 44 * Sin(4) + 110 * Square(0.5)) * 0.1) +#synth.set_graph(Sin(440 * (Saw(0.5) + 1.0) * 0.5) * 0.05) +#synth.set_graph(Sin(220) * 0.5) +#synth.set_graph(Pulse(220) * 0.25) +#synth.set_graph(Sin(440) * 0.25) +#synth.set_graph(WhiteNoise() * 1.0) + +# Should sound vagely similar to a flanged whitenoise signal +#noise = WhiteNoise() +#synth.set_graph((noise + noise[50 + 100 * (Sin(0.1) + 1)]) * 0.5 * (RevSaw(2) + 1) * 0.5) +synth.set_graph(Sin(440) * RevSaw(5) * 0.2) + +def harmonics(osc, freq, count): + return reduce(lambda a, b: a + b, + (osc(freq * i) for i in xrange(1, count + 2))) \ + / (count + 1) + diff --git a/ysynth.py b/ysynth.py index 2866ccb..31f08e3 100644 --- a/ysynth.py +++ b/ysynth.py @@ -8,7 +8,7 @@ from itertools import izip import numpy as np from scipy.signal import lfilter -__all__ = ['YSynth', 'Sin', 'Cos', 'Saw', 'RevSaw', 'Square', 'Pulse'] +__all__ = ['YSynth', 'Sin', 'Cos', 'Saw', 'RevSaw', 'Square', 'Pulse', 'WhiteNoise'] class YSynth(object): """ @@ -269,14 +269,35 @@ class Divisor(YAudioGraphNode): yield a / b # Sample delay +# Currently uses a fixed buffer of 4096 samples +# should support dynamic buffer size in the future class Delay(YAudioGraphNode): def __call__(self, l): - buf = [0.0] * 4096 + buf = None samples = 0 + for sig, delay in l: - buf[samples] = sig - yield buf[(samples - delay) % 4096] - samples = (samples + 1) % 4096 + if buf is None: + if len(sig.shape) == 2: + buf = np.zeros((4096, sig.shape[1])) + else: + buf = np.zeros((4096,)) + + # Update delay buffer + if samples + self.synth.chunk_size > buf.shape[0]: + s_left = buf.shape[0] - samples + buf[samples:] = sig[:s_left] + buf[:self.synth.chunk_size - s_left] = sig[s_left:] + else: + buf[samples:samples + self.synth.chunk_size] = sig + + # Construct delayed signal from delay input + delayed_idx = np.array((np.arange(samples, samples + + self.synth.chunk_size) - delay) % buf.shape[0], dtype=int) + yield buf[delayed_idx] + + # Finally update sample pointer + samples = (samples + self.synth.chunk_size) % buf.shape[0] # Base oscillator class class YOscillator(YAudioGraphNode): @@ -319,6 +340,14 @@ class YOscillator(YAudioGraphNode): def oscillate(self, l): raise NotImplementedError("Inherit this class") +# Noise signals +# XXX: I am probably not White Noise, fix me +# up at a later time. +class WhiteNoise(YAudioGraphNode): + def __call__(self, l): + while True: + yield np.random.rand(self.synth.chunk_size) + # Basic oscillators class Sin(YOscillator): """ @@ -372,3 +401,10 @@ class Pulse(YOscillator): pos, zi = lfilter([-1, 1], [1], pos, zi=zi) yield np.array(pos > 0, dtype=float) +# Flanger effect +class Flanger(YAudioGraphNode): + """ + Perform virtual tape flange by mixing the + input signal with a delayed version. + """ +