diff --git a/code/thinkdsp.py b/code/thinkdsp.py index f4de8f98..cfe810c6 100644 --- a/code/thinkdsp.py +++ b/code/thinkdsp.py @@ -59,7 +59,7 @@ def __init__(self, filename='sound.wav', framerate=11025): self.fp.setnchannels(self.nchannels) self.fp.setsampwidth(self.sampwidth) self.fp.setframerate(self.framerate) - + def write(self, wave): """Writes a wave. @@ -92,14 +92,14 @@ def read_wave(filename='sound.wav'): nframes = fp.getnframes() sampwidth = fp.getsampwidth() framerate = fp.getframerate() - + z_str = fp.readframes(nframes) - + fp.close() dtype_map = {1:numpy.int8, 2:numpy.int16} assert sampwidth in dtype_map - + ys = numpy.fromstring(z_str, dtype=dtype_map[sampwidth]) wave = Wave(ys, framerate) return wave @@ -123,7 +123,7 @@ class _SpectrumParent(object): @property def max_freq(self): return self.framerate / 2.0 - + @property def freq_res(self): return self.max_freq / (len(self.fs) - 1) @@ -131,7 +131,7 @@ def freq_res(self): def plot(self, low=0, high=None, **options): """Plots amplitude vs frequency. - low: int index to start at + low: int index to start at high: int index to end at """ thinkplot.plot(self.fs[low:high], self.amps[low:high], **options) @@ -139,7 +139,7 @@ def plot(self, low=0, high=None, **options): def plot_power(self, low=0, high=None, **options): """Plots power vs frequency. - low: int index to start at + low: int index to start at high: int index to end at """ thinkplot.plot(self.fs[low:high], self.power[low:high], **options) @@ -183,7 +183,7 @@ def __add__(self, other): return Spectrum(hs, self.framerate) __radd__ = __add__ - + @property def real(self): @@ -271,7 +271,7 @@ def make_wave(self): class IntegratedSpectrum(object): """Represents the integral of a spectrum.""" - + def __init__(self, cs, fs): """Initializes an integrated spectrum: @@ -284,7 +284,7 @@ def __init__(self, cs, fs): def plot_power(self, low=0, high=None, expo=False, **options): """Plots the integrated spectrum. - low: int index to start at + low: int index to start at high: int index to end at """ cs = self.cs[low:high] @@ -400,7 +400,7 @@ def make_wave(self): for t, spectrum in sorted(self.spec_map.iteritems()): wave = spectrum.make_wave() n = len(wave) - + if self.window_func: window = 1 / self.window_func(n) wave.window(window) @@ -453,7 +453,7 @@ def __or__(self, other): """Concatenates two waves. other: Wave - + returns: Wave """ if self.framerate != other.framerate: @@ -567,8 +567,12 @@ def plot(self, **options): """Plots the wave. """ - n = len(self.ys) - ts = numpy.linspace(0, self.duration, n) + dt = 1.0 / self.framerate + ts = numpy.arange(self.start, self.start + self.duration, dt) + # Due to erroneous division, the ts calculated above and below + # have slightly different elements + #n = len(self.ys) + #ts = numpy.linspace(self.start, self.start + self.duration, n) thinkplot.plot(ts, self.ys, **options) def corr(self, other): @@ -581,7 +585,7 @@ def corr(self, other): mat = self.cov_mat(other) corr = mat[0][1] / math.sqrt(mat[0][0] * mat[1][1]) return corr - + def cov_mat(self, other): """Covariance matrix of two waves. @@ -680,7 +684,7 @@ def quantize(ys, bound, dtype): if max(ys) > 1 or min(ys) < -1: print 'Warning: normalizing before quantizing.' ys = normalize(ys) - + zs = (ys * bound).astype(dtype) return zs @@ -749,7 +753,7 @@ def plot(self, framerate=11025): duration = self.period * 3 wave = self.make_wave(duration, start=0, framerate=framerate) wave.plot() - + def make_wave(self, duration=1, start=0, framerate=11025): """Makes a Wave object. @@ -760,7 +764,7 @@ def make_wave(self, duration=1, start=0, framerate=11025): returns: Wave """ dt = 1.0 / framerate - ts = numpy.arange(start, duration, dt) + ts = numpy.arange(start, start + duration, dt) ys = self.evaluate(ts) return Wave(ys, framerate=framerate, start=start) @@ -781,7 +785,7 @@ def infer_framerate(ts): class SumSignal(Signal): """Represents the sum of signals.""" - + def __init__(self, *args): """Initializes the sum. @@ -806,7 +810,7 @@ def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ return sum(sig.evaluate(ts) for sig in self.signals) @@ -814,7 +818,7 @@ def evaluate(self, ts): class Sinusoid(Signal): """Represents a sinusoidal signal.""" - + def __init__(self, freq=440, amp=1.0, offset=0, func=numpy.sin): """Initializes a sinusoidal signal. @@ -840,7 +844,7 @@ def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ phases = PI2 * self.freq * ts + self.offset @@ -854,7 +858,7 @@ def CosSignal(freq=440, amp=1.0, offset=0): freq: float frequency in Hz amp: float amplitude, 1.0 is nominal max offset: float phase offset in radians - + returns: Sinusoid object """ return Sinusoid(freq, amp, offset, func=numpy.cos) @@ -866,7 +870,7 @@ def SinSignal(freq=440, amp=1.0, offset=0): freq: float frequency in Hz amp: float amplitude, 1.0 is nominal max offset: float phase offset in radians - + returns: Sinusoid object """ return Sinusoid(freq, amp, offset, func=numpy.sin) @@ -874,12 +878,12 @@ def SinSignal(freq=440, amp=1.0, offset=0): class SquareSignal(Sinusoid): """Represents a square signal.""" - + def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ cycles = self.freq * ts + self.offset / PI2 @@ -890,12 +894,12 @@ def evaluate(self, ts): class SawtoothSignal(Sinusoid): """Represents a sawtooth signal.""" - + def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ cycles = self.freq * ts + self.offset / PI2 @@ -906,12 +910,12 @@ def evaluate(self, ts): class ParabolicSignal(Sinusoid): """Represents a parabolic signal.""" - + def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ cycles = self.freq * ts + self.offset / PI2 @@ -923,12 +927,12 @@ def evaluate(self, ts): class GlottalSignal(Sinusoid): """Represents a periodic signal that resembles a glottal signal.""" - + def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ cycles = self.freq * ts + self.offset / PI2 @@ -940,12 +944,12 @@ def evaluate(self, ts): class TriangleSignal(Sinusoid): """Represents a triangle signal.""" - + def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ cycles = self.freq * ts + self.offset / PI2 @@ -957,7 +961,7 @@ def evaluate(self, ts): class Chirp(Signal): """Represents a signal with variable frequency.""" - + def __init__(self, start=440, end=880, amp=1.0): """Initializes a linear chirp. @@ -981,7 +985,7 @@ def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ freqs = numpy.linspace(self.start, self.end, len(ts)-1) @@ -1005,12 +1009,12 @@ def _evaluate(self, ts, freqs): class ExpoChirp(Chirp): """Represents a signal with varying frequency.""" - + def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ start, end = math.log10(self.start), math.log10(self.end) @@ -1020,12 +1024,12 @@ def evaluate(self, ts): class SilentSignal(Signal): """Represents silence.""" - + def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ return numpy.zeros(len(ts)) @@ -1033,7 +1037,7 @@ def evaluate(self, ts): class _Noise(Signal): """Represents a noise signal (abstract parent class).""" - + def __init__(self, amp=1.0): """Initializes a white noise signal. @@ -1057,7 +1061,7 @@ def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ ys = numpy.random.uniform(-self.amp, self.amp, len(ts)) @@ -1071,7 +1075,7 @@ def evaluate(self, ts): """Evaluates the signal at the given times. ts: float array of times - + returns: float wave array """ ys = numpy.random.normal(0, 1, len(ts)) @@ -1089,7 +1093,7 @@ def evaluate(self, ts): a uniform random series. ts: float array of times - + returns: float wave array """ #dys = numpy.random.normal(0, 1, len(ts)) @@ -1182,7 +1186,7 @@ def midi_to_freq(midi_num): """Converts MIDI note number to frequency. midi_num: int MIDI note number - + returns: float frequency in Hz """ x = (midi_num - 69) / 12.0 @@ -1240,7 +1244,7 @@ def main(): return wfile = WavFileWriter() - for sig_cons in [SinSignal, TriangleSignal, SawtoothSignal, + for sig_cons in [SinSignal, TriangleSignal, SawtoothSignal, GlottalSignal, ParabolicSignal, SquareSignal]: print sig_cons sig = sig_cons(440) diff --git a/trunk/book.tex b/trunk/book.tex index d9eb89f6..0dc3fbda 100644 --- a/trunk/book.tex +++ b/trunk/book.tex @@ -451,7 +451,7 @@ \section{Signals} starts at $\cos \pi/2$, which is 0. A sine signal with {\tt offset=0} also starts at 0. In fact, -a cosine signal with {\tt offset=pi/2} is identical to a sine +a sine signal with {\tt offset=pi/2} is identical to a cosine signal with {\tt offset=0}. Signals have an \verb"__add__" method, so you can use the {\tt +}