Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c9d5ae0
commit 7e692af
Showing
14 changed files
with
681 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
README.markdown | ||
setup.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
## Introduction | ||
|
||
`wavebender` is an audio synthesis library for Python. | ||
|
||
## Usage | ||
|
||
``` | ||
from wavebender import * | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#!/usr/bin/env python | ||
''' | ||
Installer script for the wavebender module. | ||
''' | ||
|
||
from distutils.core import setup | ||
import wavebender | ||
|
||
setup ( | ||
name = "wavebender", | ||
description = "An audio synthesis library for Python.", | ||
|
||
author = wavebender.__author__, | ||
author_email = wavebender.__author_email__, | ||
version = wavebender.__version__, | ||
url = wavebender.__url__, | ||
long_description = wavebender.__longdescr__, | ||
classifiers = wavebender.__classifiers__, | ||
packages = ['wavebender',], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
#!/usr/bin/env python | ||
""" | ||
An audio synthesis library for Python. | ||
It makes heavy use of the `itertools` module. | ||
Good luck! (This is a work in progress.) | ||
""" | ||
import sys | ||
import wave | ||
import math | ||
import struct | ||
import random | ||
import argparse | ||
from itertools import * | ||
|
||
# metadata | ||
__author__ = 'Zach Denton' | ||
__author_email__ = 'zacharydenton@gmail.com' | ||
__version__ = '0.2' | ||
__url__ = 'http://github.com/zacharydenton/wavebender' | ||
__longdescr__ = ''' | ||
An audio synthesis library for Python. | ||
''' | ||
__classifiers__ = [ | ||
'Topic :: Multimedia :: Sound/Audio :: Sound Synthesis' | ||
] | ||
|
||
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) | ||
|
||
# split the samples into chunks (to reduce memory consumption and improve performance) | ||
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) | ||
|
||
# split the samples into chunks (to reduce memory consumption and improve performance) | ||
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() | ||
|
||
# each channel is defined by infinite functions which are added to produce a sample. | ||
channels = ((sine_wave(args.frequency, args.rate, args.amplitude),) for i in range(args.channels)) | ||
|
||
# convert the channel functions into waveforms | ||
samples = compute_samples(channels, args.rate * args.time) | ||
|
||
# write the samples to a file | ||
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() |
Oops, something went wrong.