Skip to content

Instantly share code, notes, and snippets.

@Lanny
Last active December 25, 2015 11:38
Show Gist options
  • Save Lanny/6970041 to your computer and use it in GitHub Desktop.
Save Lanny/6970041 to your computer and use it in GitHub Desktop.
Generate a really crappy .wav containing the main theme of Ode to Joy. Can generate other crappy .wav's as long as they only use the same 5 notes.
#!/usr/bin/python
import wave
import random
from math import pi, sin, cos
OPTIONS = {}
Hz_MAP = {
'c': 130.813,
'd': 146.832,
'e': 164.814,
'f': 174.614,
'g': 195.997
}
def sin_wave_oscillator(hz, options=OPTIONS):
conv_const = hz*pi
def inner(base, dt):
return sin(dt*conv_const)
return inner
def adsr_envelope(attack, decay, sustain, sustain_duration, release,
options=OPTIONS):
peak_sustain_diff = sustain - 1.
decay_start = attack
sustain_start = decay_start + decay
release_start = sustain_start + sustain_duration
release_end = release_start + release
def inner(base, dt):
if dt < decay_start:
return base * (dt/attack)
elif dt > decay_start and dt < sustain_start:
return base * (((dt-decay_start)/decay) * peak_sustain_diff + 1.)
elif dt > sustain_start and dt < release_start:
return base * sustain
elif dt > release_start < release_end:
return base * (dt-release_start)/release * sustain
else: return 0
return inner
def apply_amplitude(amp, options=OPTIONS):
def inner(base, dt):
return base * amp
return inner
def compose_stack(*args):
stack = args
def inner(base, dt):
result = base
for f in stack:
result = f(result, dt)
return result
return inner
def sample_to_str(sample, options=OPTIONS):
width = options['sample_width']
sample_range = 2**(8*width)
sample = int((sample + .5) * sample_range)
bytes = [0]*width
for shift_by in range(width):
bytes[width - 1 - shift_by] = sample & 255
sample = sample >> 8
return ''.join(map(chr, bytes))
def apply(duration, fn, options=OPTIONS):
frame_count = int(duration*options['frame_rate'])
arr = [0]*frame_count
for i in xrange(0, frame_count):
dt = float(i)/float(frame_count) * duration
arr[i] = fn(None, dt)
return ''.join(map(sample_to_str, arr))
def noise(duration, frame_rate):
return ''.join([chr(random.randint(0, 255)) for _ in range(int(duration*frame_rate))])
if __name__ == '__main__':
out_w = wave.open('out.wav', 'w')
OPTIONS['frame_rate'] = 44100
OPTIONS['channels'] = 1
OPTIONS['sample_width'] = 1
out_w.setnchannels(OPTIONS['channels'])
out_w.setsampwidth(OPTIONS['sample_width'])
out_w.setframerate(OPTIONS['frame_rate'])
ode_to_joy = 'eefggfedccdeedd'
for note in ode_to_joy:
tone_gen = compose_stack(
sin_wave_oscillator(Hz_MAP[note]),
adsr_envelope(.0, .0, 1., .25, .0)
#apply_amplitude(.25)
)
frames = apply(.5, tone_gen)
out_w.writeframes(frames)
out_w.close()
print 'Done!'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment