import sys
import wave
import math
import struct
import random
import argparse
from itertools import *
def grouper(n, iterable, fillvalue=None):
"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
def sine_wave(frequency=440.0, framerate=44100, amplitude=0.5):
'''
Generate a sine wave at a given frequency of infinite length.
'''
period = int(framerate / frequency)
if amplitude > 1.0: amplitude = 1.0
if amplitude < 0.0: amplitude = 0.0
lookup_table = [float(amplitude) * math.sin(2.0*math.pi*float(frequency)*(float(i%period)/float(framerate))) for i in xrange(period)]
return (lookup_table[i%period] for i in count(0))
def square_wave(frequency=440.0, framerate=44100, amplitude=0.5):
for s in sine_wave(frequency, framerate, amplitude):
if s > 0:
yield amplitude
elif s < 0:
yield -amplitude
else:
yield 0.0
def damped_wave(frequency=440.0, framerate=44100, amplitude=0.5, length=44100):
if amplitude > 1.0: amplitude = 1.0
if amplitude < 0.0: amplitude = 0.0
return (math.exp(-(float(i%length)/float(framerate))) * s for i, s in enumerate(sine_wave(frequency, framerate, amplitude)))
def white_noise(amplitude=0.5):
'''
Generate random samples.
'''
return (float(amplitude) * random.uniform(-1, 1) for i in count(0))
def compute_samples(channels, nsamples=None):
'''
create a generator which computes the samples.
essentially it creates a sequence of the sum of each function in the channel
at each sample in the file for each channel.
'''
return islice(izip(*(imap(sum, izip(*channel)) for channel in channels)), nsamples)
def write_wavefile(filename, samples, nframes=None, nchannels=2, sampwidth=2, framerate=44100, bufsize=2048):
"Write samples to a wavefile."
if nframes is None:
nframes = -1
w = wave.open(filename, 'w')
w.setparams((nchannels, sampwidth, framerate, nframes, 'NONE', 'not compressed'))
max_amplitude = float(int((2 ** (sampwidth * 8)) / 2) - 1)
for chunk in grouper(bufsize, samples):
frames = ''.join(''.join(struct.pack('h', int(max_amplitude * sample)) for sample in channels) for channels in chunk if channels is not None)
w.writeframesraw(frames)
w.close()
return filename
def write_pcm(f, samples, sampwidth=2, framerate=44100, bufsize=2048):
"Write samples as raw PCM data."
max_amplitude = float(int((2 ** (sampwidth * 8)) / 2) - 1)
for chunk in grouper(bufsize, samples):
frames = ''.join(''.join(struct.pack('h', int(max_amplitude * sample)) for sample in channels) for channels in chunk if channels is not None)
f.write(frames)
f.close()
return filename
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--channels', help="Number of channels to produce", default=2, type=int)
parser.add_argument('-b', '--bits', help="Number of bits in each sample", choices=(16,), default=16, type=int)
parser.add_argument('-r', '--rate', help="Sample rate in Hz", default=44100, type=int)
parser.add_argument('-t', '--time', help="Duration of the wave in seconds.", default=60, type=int)
parser.add_argument('-a', '--amplitude', help="Amplitude of the wave on a scale of 0.0-1.0.", default=0.5, type=float)
parser.add_argument('-f', '--frequency', help="Frequency of the wave in Hz", default=440.0, type=float)
parser.add_argument('filename', help="The file to generate.")
args = parser.parse_args()
channels = ((sine_wave(args.frequency, args.rate, args.amplitude),) for i in range(args.channels))
samples = compute_samples(channels, args.rate * args.time)
if args.filename == '-':
filename = sys.stdout
else:
filename = args.filename
write_wavefile(filename, samples, args.rate * args.time, args.channels, args.bits / 8, args.rate)
if __name__ == "__main__":
main()