In [164]:
%pylab inline
from scipy import signal
from functools import partial
import math
import numpy as np
pylab.rcParams['figure.figsize'] = (16, 6)
pylab.rcParams["font.size"] = "14"
import IPython.display as ipd
from ipywidgets import interact, fixed
import ipywidgets as widgets
# settings
SRATE = 22050
# note names to freq for widgets
notes = 'C-,C#,D-,D#-,E-,F-,F#,G-,G#,A-,A#,B-'.split(',')
notes = [n + str(o) for o in range(1,5) for n in notes]
freqs = 55. * 2**(np.arange(3, 3 + len(notes)) / 12.)
notes = list(zip(notes, freqs))
#print(notes)
waveforms = [('saw', 0),('tri', 1), ('sqr', 2)]
In [165]:
def display_wave(a, t, srat=SRATE):
figure(1)
# waveform
plot(t, a)
xlabel('Time (s)')
grid(True)
margins(0.0)
plt.show()
# audio player
ipd.display(ipd.Audio(a, rate=srat))
In [168]:
def drones(frq, ticks, bpm, tpb, voices, waveform, srat=SRATE):
# TODO: make stereo and spread voices
# TODO: other combine modes (AM?)
# TODO: center voice frequencies around the selected tone (right now we only go up)
#
# Things we've tried:
# - using 'sin' as a wave is not interesting (already 'tri' is relative quiet)
# - incrementing num_cycles by 1 is enough, if we do e.g. 2, the pattern will repeat twice
# from ticks and samplerate we calculate the number of samples for desired resync.
# ticks per second
tps = (bpm * tpb) / 60.0
# samples per tick
spt = srat / tps
dur = ticks * spt
print("Duration samples: %f" % dur)
# from the tone frequency we can check what the phase would be at the point of resync.
# samples_per_cycle = srat / note_frq
spc = srat / frq
# we round this to end with phase=0 at the end of the loop
num_cycles = round(dur / spc)
frqs=[]
for v in range(voices):
frqs.append(srat / (dur / num_cycles))
num_cycles += 1
# TODO: do a table with the error for the whole tone range and various bpms
print("Frq error: ", abs(frq - frqs[0]))
print("Frqs: ", *frqs)
osc = None
if waveform == 0:
osc = partial(signal.sawtooth)
elif waveform == 1:
osc = partial(signal.sawtooth, width=0.5)
elif waveform == 2:
osc = partial(signal.square)
twopi = 2.0 * np.pi
t = np.linspace(0, dur/srat, int(dur))
a = np.zeros(len(t))
for v in range(voices):
#a += np.sin(t * twopi * frqs[v])
a += osc(t * twopi * frqs[v])
display_wave(a, t)
In [167]:
interact(drones,
frq=notes, waveform=waveforms,
ticks=widgets.IntSlider(min=1, max=256, step=1, value=16, continuous_update=False),
bpm=widgets.IntSlider(min=60, max=300, step=1, value=125, continuous_update=False),
tpb=widgets.IntSlider(min=1, max=16, step=1, value=4, continuous_update=False),
voices=widgets.IntSlider(min=1, max=10, step=1, value=3),
srat=fixed(SRATE))
Out[167]:
In [ ]: