#!/usr/bin/env python import sys from math import sqrt import wave import numpy scalar = numpy.float32 SAMPLERATE = 44100 def read_wave(filename): w = wave.open(filename) chunks = [] count = 0 while 1: chunk = w.readframes(4096) if not chunk: break count += len(chunk) chunks.append(chunk) a = numpy.empty(count, numpy.float32) i = 0 for chunk in chunks: b = numpy.fromstring(chunk, numpy.int16) n = len(b) a[i:i+n] = b i += n assert i == count/2 x0, x1 = a.min(), a.max() assert x0 1e-8: #centre = (a.max() + a.min()) / 2 #a -= centre a *= float(volume) * 32767 / (a.max() - a.min()) else: print " *** quiet array" return a.astype(numpy.int16) def write_wave(filename, a, samplerate=SAMPLERATE): assert len(a.shape)==1 or a.shape[1] in (1, 2), "odd shape = %r"%(a.shape,) channels = 1 if len(a.shape)==1 else a.shape[1] print "write_wave", filename, 'channels=', channels a = normalize(a) f = wave.open(filename, 'w') f.setnchannels(channels) f.setsampwidth(2) f.setframerate(samplerate) f.writeframesraw(a.tostring()) f.close() _window_cache = {} def window(a, d_idx=None, attack=100, decay=100): #print "window", len(a), d_idx, attack, decay if d_idx is None: d_idx = len(a) key = (d_idx, attack, decay) w = _window_cache.get(key) if w is None: w = numpy.empty(d_idx, dtype=scalar) w[:] = 1. for idx in range(attack): w[int(idx)] = 1.*idx/float(attack) for idx in range(decay): w[int(d_idx - idx-1)] = 1.*idx/float(decay) # ramp up _window_cache[key] = w a = a[0 : d_idx] a = a*w return a def paint(a, b, idx0, idx1=None): if idx1 is None: idx1 = len(b) idx0 = int(idx0) idx1 = int(idx1) adx0, adx1 = idx0, idx0+idx1 bdx0, bdx1 = 0, idx1 try: a[adx0 : adx1] += b[bdx0 : bdx1] except ValueError: print "paint: ValueError" pass def main(source, target, stacks=10, dt=0.1, cycles=30): a = read_wave(source) N = len(a) * cycles A = numpy.zeros((N,), scalar) # use (N, 2) for stereo output dn = dt * SAMPLERATE bs = [a[:len(a) - dn*i] for i in range(stacks)] for b in bs: idx = 0 while idx + len(b) < len(A): b = window(b) paint(A[:], b, idx) idx += len(b) #a = A[len(a):] # skip the first cycle... A[:len(a)] /= sqrt(cycles) # attenuate the first cycle... write_wave(target, A) if __name__ == "__main__": if not sys.argv[1:]: print "USAGE:\n\tphaser.py source.wav target.wav\n" else: source = sys.argv[1] target = sys.argv[2] main(source, target)