diff --git a/ysynth.py b/ysynth.py index 0cfc3e8..ca53370 100644 --- a/ysynth.py +++ b/ysynth.py @@ -5,6 +5,8 @@ from pymod import _ysynth_init, _ysynth_init, _ysynth_set_callback from math import cos, sin, pi, modf from itertools import izip +import numpy as np +from scipy.signal import lfilter __all__ = ['YSynth', 'Sin', 'Cos', 'Saw', 'RevSaw', 'Square', 'Pulse'] @@ -50,13 +52,15 @@ class YSynth(object): # Process audio graph buf_len = len(channels[0]) - for i in xrange(buf_len): - next_sample = next(self.graph) - for j in xrange(len(channels)): - channels[j][i] = next_sample + self.chunk_size = buf_len - # Advance sampleclock - self.samples += 1 + next_chunk = next(self.graph) + for j in xrange(len(channels)): + for i in xrange(buf_len): + channels[j][i] = next_chunk[i] + + # Advance sampleclock + self.samples += buf_len def set_graph(self, graph): graph.set_synth(self) @@ -288,13 +292,25 @@ class YOscillator(YAudioGraphNode): The generated cycle ranges from 0.0 to 1.0 exclusive. """ - cycle = 0.0 - samples = 0 + last_cycle = [0.0] for freq, in l: - cycle = freq * samples / float(self.synth.samplerate) - yield modf(cycle)[0] - samples = (samples + 1) % self.synth.samplerate + # The last_cycle will only be a list during the initial + # iteration, perfect for a 'once' statement, we want + # to force the first cycle value to 0. + if isinstance(last_cycle, list): + if isinstance(freq, np.ndarray): + ifreq = freq[0] + else: + ifreq = freq + last_cycle = [-(ifreq / float(self.synth.samplerate))] + + # Compute cycle using IIR filter and np.fmod + cycle, last_cycle = lfilter([1], [1, -1], freq / + float(self.synth.samplerate) * + np.ones(self.synth.chunk_size), zi=last_cycle) + yield np.fmod(cycle, 1.0) + last_cycle = np.fmod(last_cycle, 1.0) return self.oscillate(cycle_gen()) @@ -308,7 +324,7 @@ class Sin(YOscillator): """ def oscillate(self, l): for pos in l: - yield sin(pos * 2 * pi) + yield np.sin(2 * pi * pos) class Cos(YOscillator): """ @@ -316,7 +332,7 @@ class Cos(YOscillator): """ def oscillate(self, l): for pos in l: - yield cos(pos * 2 * pi) + yield np.cos(2 * pi * pos) class Saw(YOscillator): """ @@ -340,15 +356,17 @@ class Square(YOscillator): """ def oscillate(self, l): for pos in l: - yield 1.0 if pos < 0.5 else -1.0 + #yield 1.0 if pos < 0.5 else -1.0 + pos = pos - 0.5 + yield -(np.abs(pos) / pos) class Pulse(YOscillator): """ Pulse oscillator """ def oscillate(self, l): - prev_pos = 1.0 + zi = [1.0] for pos in l: - yield 1.0 if pos < prev_pos else 0.0 - prev_pos = pos + pos, zi = lfilter([-1, 1], [1], pos, zi=zi) + yield np.array(pos > 0, dtype=float)