| @@ -5,6 +5,8 @@ | |||||
| from pymod import _ysynth_init, _ysynth_init, _ysynth_set_callback | from pymod import _ysynth_init, _ysynth_init, _ysynth_set_callback | ||||
| from math import cos, sin, pi, modf | from math import cos, sin, pi, modf | ||||
| from itertools import izip | 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'] | ||||
| @@ -50,13 +52,15 @@ class YSynth(object): | |||||
| # Process audio graph | # Process audio graph | ||||
| buf_len = len(channels[0]) | 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): | def set_graph(self, graph): | ||||
| graph.set_synth(self) | graph.set_synth(self) | ||||
| @@ -288,13 +292,25 @@ class YOscillator(YAudioGraphNode): | |||||
| The generated cycle ranges from 0.0 to 1.0 exclusive. | The generated cycle ranges from 0.0 to 1.0 exclusive. | ||||
| """ | """ | ||||
| cycle = 0.0 | |||||
| samples = 0 | |||||
| last_cycle = [0.0] | |||||
| for freq, in l: | 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()) | return self.oscillate(cycle_gen()) | ||||
| @@ -308,7 +324,7 @@ class Sin(YOscillator): | |||||
| """ | """ | ||||
| def oscillate(self, l): | def oscillate(self, l): | ||||
| for pos in l: | for pos in l: | ||||
| yield sin(pos * 2 * pi) | |||||
| yield np.sin(2 * pi * pos) | |||||
| class Cos(YOscillator): | class Cos(YOscillator): | ||||
| """ | """ | ||||
| @@ -316,7 +332,7 @@ class Cos(YOscillator): | |||||
| """ | """ | ||||
| def oscillate(self, l): | def oscillate(self, l): | ||||
| for pos in l: | for pos in l: | ||||
| yield cos(pos * 2 * pi) | |||||
| yield np.cos(2 * pi * pos) | |||||
| class Saw(YOscillator): | class Saw(YOscillator): | ||||
| """ | """ | ||||
| @@ -340,15 +356,17 @@ class Square(YOscillator): | |||||
| """ | """ | ||||
| def oscillate(self, l): | def oscillate(self, l): | ||||
| for pos in 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): | class Pulse(YOscillator): | ||||
| """ | """ | ||||
| Pulse oscillator | Pulse oscillator | ||||
| """ | """ | ||||
| def oscillate(self, l): | def oscillate(self, l): | ||||
| prev_pos = 1.0 | |||||
| zi = [1.0] | |||||
| for pos in l: | 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) | |||||