-
Notifications
You must be signed in to change notification settings - Fork 16
/
karplus-strong-example.py
executable file
·74 lines (63 loc) · 3.63 KB
/
karplus-strong-example.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/usr/bin/env python3
#--------------------------------------------------------------------------------
# SignalFlow: Karplus-Strong string synthesis.
# https://en.wikipedia.org/wiki/Karplus%E2%80%93Strong_string_synthesis
#--------------------------------------------------------------------------------
from signalflow import *
import numpy as np
import random
def main():
feedback = 0.99
note_interval = 0.125
#--------------------------------------------------------------------------------
# Karplus-Strong synthesis requires a short delay line whose delay time
# is 1/F of the synthesized frequency. Because SignalFlow reads and writes
# buffers (including the feedback notes) on a block-by-block basis, this delay
# time can only be as short as the graph's global block size, so set a small
# block size so we can render a reasonable range of frequencies.
#--------------------------------------------------------------------------------
config = AudioGraphConfig()
config.output_buffer_size = 128
graph = AudioGraph(config=config, start=True)
graph.poll(2)
#--------------------------------------------------------------------------------
# Short burst of noise to excite the delay line
#--------------------------------------------------------------------------------
excitation_buf = Buffer(np.random.uniform(-1, 1, 256))
player = BufferPlayer(excitation_buf)
#--------------------------------------------------------------------------------
# Set up a buffer that will be used as the feedback line's delay ringbuffer.
#--------------------------------------------------------------------------------
feedback_buf = Buffer(1, 2048)
feedback_reader = FeedbackBufferReader(feedback_buf)
#--------------------------------------------------------------------------------
# Pick a random frequency each time a note is played. If `clock` is non-zero,
# RandomExponential will generate a random signal at audio rate. Zero it and
# it will only generate when triggered.
#--------------------------------------------------------------------------------
random_frequency = RandomExponential(1, 6, clock=0)
rounded_frequency = Round(random_frequency) * 50
#--------------------------------------------------------------------------------
# Write to the feedback buffer.
#--------------------------------------------------------------------------------
output = SVFilter(player * 0.5 + feedback_reader, "low_pass", 4000)
feedback_writer = FeedbackBufferWriter(feedback_buf, output * feedback, 1/rounded_frequency)
#--------------------------------------------------------------------------------
# Play the rendered output as a stereo signal.
# Note that the feedback_writer also needs to be "played". This is because
# the audio synthesis works by pulling each of the Graph's outputs in turn,
# and recursing through each of their inputs. As the FeedbackBufferWriter
# is not an input to any other node, it must be processed explicitly.
#--------------------------------------------------------------------------------
graph.play([output, output])
graph.play(feedback_writer)
#--------------------------------------------------------------------------------
# Periodically, re-generate the excitation buffer and generate a new note.
#--------------------------------------------------------------------------------
while True:
graph.wait(note_interval)
excitation_buf.fill(lambda n: random.uniform(-1, 1))
player.trigger()
random_frequency.trigger()
if __name__ == "__main__":
main()