Skip to content

Commit

Permalink
distutils style
Browse files Browse the repository at this point in the history
  • Loading branch information
zacharydenton committed Feb 9, 2012
1 parent c9d5ae0 commit 7e692af
Show file tree
Hide file tree
Showing 14 changed files with 681 additions and 0 deletions.
2 changes: 2 additions & 0 deletions MANIFEST
@@ -0,0 +1,2 @@
README.markdown
setup.py
9 changes: 9 additions & 0 deletions README.markdown
@@ -0,0 +1,9 @@
## Introduction

`wavebender` is an audio synthesis library for Python.

## Usage

```
from wavebender import *
```
18 changes: 18 additions & 0 deletions wavebender.py → ...b.linux-x86_64-2.6/wavebender/__init__.py 100755 → 100644
@@ -1,4 +1,10 @@
#!/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
Expand All @@ -7,6 +13,18 @@
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
Expand Down
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.
20 changes: 20 additions & 0 deletions setup.py
@@ -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',],
)
129 changes: 129 additions & 0 deletions wavebender/__init__.py
@@ -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()

0 comments on commit 7e692af

Please sign in to comment.