| @@ -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) | |||