Code: Alles auswählen
import math
from operator import methodcaller
from itertools import imap, islice, cycle
from ossaudiodev import (SNDCTL_DSP_SPEED, SNDCTL_DSP_CHANNELS,
SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, AFMT_S16_LE)
from fcntl import ioctl
from struct import pack
WAVES = {
'sin': math.sin,
'sq': lambda x: -1 if int(x/math.pi) % 2 else 1,
'saw': lambda x: 1 - (x/math.pi) % 2
}
class Oscillator(object):
"""
Oscillator with 'sin' (sinus), 'sq' (square) and 'saw' (sawtooth)
wave forms.
"""
def __init__(self, freq=440.0, wave='sin', rate=44100, offset=0.0,
amplitude=1.0):
self.amplitude = (1 if amplitude > 1 else
(0 if amplitude < 0 else amplitude))
period = int(rate / freq)
self.table = [WAVES[wave](2 * math.pi / period * i) * self.amplitude
for i in xrange(period)][::-1]
self.wave = wave
self.length = len(self.table)
self.pos = 0
self._offset = 0.0
self.offset = offset
@property
def offset(self):
return self._offset
@offset.setter
def offset(self, value):
# float: start offset relative to period
if isinstance(value, float):
if 0.0 <= value < 1.0:
self._offset = value
self.pos = self.length - int(self.length * self._offset)
return
# int: start offset at pos
if isinstance(value, int):
self._offset = value
self.pos = self.length - self._offset % self.length
def next(self):
if not self.pos:
self.pos = self.length
self.pos -= 1
return self.table[self.pos]
def __iter__(self):
return self
def additive(a, b):
"""
Simple additive synthesis.
"""
while True:
yield (a.next() + b.next()) / 2
def am(a, b):
"""
Amplitude modulation.
"""
while True:
yield a.next() * (1 + b.next()) / (1 + b.amplitude)
def fm(carrier, modulator, beta):
"""
Frequency modulation.
"""
# TODO: Kinda broken since not stackable - allow samples as carrier with interpolation?
c_x = [2*math.pi / carrier.length * i for i in xrange(carrier.length)][::-1]
c_x = c_x[carrier.pos:] + c_x[:carrier.pos]
c = cycle(c_x)
while True:
yield WAVES[carrier.wave](c.next() + beta * modulator.next())
class SoundEnvironment(object):
"""
/dev/dsp only atm. (hardcoded with 44100, 2 channels, signed 16bit LE)
"""
def __init__(self, duration=1.0):
self.duration = duration
self._oscillators = []
self.channels = {'left': [], 'right': []}
self.rate = 44100
self.f = None
self.reset()
def reset(self):
if self.f:
self.f.close()
self.f = open('/dev/dsp', 'w')
ioctl(self.f.fileno(), SNDCTL_DSP_CHANNELS, pack('<l', 2))
ioctl(self.f.fileno(), SNDCTL_DSP_SETFMT, pack('<l', AFMT_S16_LE))
ioctl(self.f.fileno(), SNDCTL_DSP_SPEED, pack('<l', self.rate))
#ioctl(self.f.fileno(), SNDCTL_DSP_RESET, pack('<l', 0))
def add_oscillator(self, oscillator, channel='left'):
self.channels[channel].append(oscillator)
def clear_oscillators(self, channel='left'):
self.channels[channel] = []
def play(self, left, right, duration=1.0):
frames = int(duration * self.rate * self.duration)
left += self.channels['left']
right += self.channels['right']
len_left = len(left) + 1
len_right = len(right) + 1
for _ in xrange(frames):
v_left = sum(imap(methodcaller('next'), left))
v_right = sum(imap(methodcaller('next'), right))
self.f.write(pack('<h', int(v_right * 32767 / len_right)) +
pack('<h', int(v_left * 32767 / len_left)))
def plot(it, *args):
"""
Plot oscillator or generator with plot(seq, [start,] stop [, step]).
"""
#import matplotlib
#matplotlib.use('QT4Agg')
from matplotlib import pyplot
s = slice(*args)
r = range(s.start or 0, s.stop, s.step or 1)
l = list(islice(it, *args))
pyplot.plot(r, l)
pyplot.plot(r, l, 'rx')
pyplot.show()
def equal():
"""
Equal intonation.
"""
f = lambda x: 262 * 2 ** (float(x)/12)
return {
'h0': f(-1),
'c1': f(0), 'd1': f(2), 'eb1': f(3), 'e1': f(4), 'f1': f(5),
'g1': f(7), 'ab1': f(8), 'a1': f(9), 'hb1': f(10), 'h1': f(11),
'c2': f(12), 'd2': f(14), 'eb2': f(15), 'e2': f(16), 'f2': f(17),
'g2': f(19), 'ab2': f(20), 'a2': f(21), 'hb2': f(22), 'h2': f(23),
'c3': f(24), 'f#1': f(6)}
EQUAL = equal()
# examples
def naturals(se, steps):
def empty():
while True:
yield 1
o = Oscillator(220.0)
a = additive(o, empty())
for _ in xrange(steps):
se.play([a], [a])
a = additive(a, o)
def maria(se):
o = Oscillator
t = EQUAL
se.play([o(t['g1'], 'sq')], [o(t['g1'], 'saw')], 0.5)
se.play([o(t['g1'], 'sq')], [o(t['f1'], 'saw')], 0.5)
se.play([o(t['c2'], 'sq')], [o(t['eb1'], 'saw')], 1.5)
se.play([o(t['d2'], 'sq')], [o(t['g1'], 'saw')], 0.5)
se.play([o(t['eb2'], 'sq')], [o(t['c2'], 'saw')], 1.0)
se.play([o(t['g2'], 'sq')], [o(t['eb2'], 'saw')], 1.0)
se.play([o(t['eb2'], 'sq')], [o(t['c2'], 'saw')], 1.0)
se.play([o(t['d2'], 'sq')], [o(t['c2'], 'saw')], 0.5)
se.play([o(t['c2'], 'sq')], [o(t['c2'], 'saw')], 0.5)
se.play([o(t['d2'], 'sq')], [o(t['g1'], 'saw')], 2.0)
se.play([o(t['eb2'], 'sq')], [o(t['c2'], 'saw')], 1.0)
se.play([o(t['eb2'], 'sq')], [o(t['c2'], 'saw')], 0.5)
se.play([o(t['eb2'], 'sq')], [o(t['c2'], 'saw')], 0.5)
se.play([o(t['f2'], 'sq')], [o(t['hb1'], 'saw')], 1.0)
se.play([o(t['f2'], 'sq')], [o(t['hb1'], 'saw')], 0.5)
se.play([o(t['f2'], 'sq')], [o(t['f1'], 'saw')], 0.5)
se.play([o(t['g2'], 'sq')], [o(t['eb1'], 'saw')], 2.0)
def bach(se):
o = Oscillator
t = EQUAL
for _ in xrange(2):
se.add_oscillator(o(t['c1']))
se.play([], [])
se.add_oscillator(o(t['e1']))
se.play([], [])
se.play([], [o(t['g1'])])
se.play([], [o(t['c2'])])
se.play([], [o(t['e2'])])
se.play([], [o(t['g1'])])
se.play([], [o(t['c2'])])
se.play([], [o(t['e2'])])
se.clear_oscillators('left')
def synthi_example(se):
for i in xrange(3):
for f in xrange(200, 800, 5):
se.play([fm(Oscillator(300.0), Oscillator(float(f)), .5)],
[fm(Oscillator(300.0), Oscillator(float(f), 'sq'), .5)], .01)
for f in xrange(800, 200, -5):
se.play([fm(Oscillator(300.0), Oscillator(float(f), 'sq'), .5)],
[fm(Oscillator(300.0), Oscillator(float(f)), .5)], .01)
def slow_leslie(se):
t = EQUAL
for _1, _2, _3 in [(t['e2'], t['c2'], t['g1']), (t['d2'], t['g1'], t['d1']),
(t['c2'], t['g1'], t['e1']), (t['a1'], t['f#1'], t['d1']),
(t['h1'], t['g1'], t['d1'])]:
a1 = am(fm(Oscillator(_1), Oscillator(0.65, offset=.5, amplitude=.2), 20),
Oscillator(1.3, amplitude=.4, offset=.5))
a2 = am(fm(Oscillator(_2), Oscillator(1.3, offset=.5, amplitude=.1), 10),
Oscillator(1.3, amplitude=.4, offset=.5))
a3 = am(fm(Oscillator(_3), Oscillator(1.3, offset=.5, amplitude=.1), 4),
Oscillator(1.3, amplitude=.4, offset=.5))
b1 = am(fm(Oscillator(_1), Oscillator(0.65, amplitude=.2), 20),
Oscillator(1.3, amplitude=.4))
b2 = am(fm(Oscillator(_2), Oscillator(1.3, amplitude=.1), 10),
Oscillator(1.3, amplitude=.4))
b3 = am(fm(Oscillator(_3), Oscillator(1.3, amplitude=.1), 4),
Oscillator(1.3, amplitude=.4))
se.play([a1, a2, a3], [b1, b2, b3])
if __name__ == '__main__':
se = SoundEnvironment()
# natural intonation scale (weird additive synthesis)
naturals(se, 15)
# examples with 'notes' in equal intonation
se.duration = .5
maria(se)
se.duration = .2
bach(se)
# synthi style with fm
se.duration = 1
synthi_example(se)
# rotary speaker alike
slow_leslie(se)