-
Notifications
You must be signed in to change notification settings - Fork 0
/
beatlounge.html
454 lines (402 loc) · 15.6 KB
/
beatlounge.html
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=1024" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<title>Making Music with Python and BeatLounge</title>
<meta name="description" content="impress.js is a presentation tool based on the power of CSS3 transforms and transitions in modern browsers and inspired by the idea behind prezi.com." />
<meta name="author" content="Bartek Szopka" />
<link href="http://fonts.googleapis.com/css?family=Open+Sans:regular,semibold,italic,italicsemibold|PT+Sans:400,700,400italic,700italic|PT+Serif:400,700,400italic,700italic" rel="stylesheet" />
<link href="css/impress-demo.css" rel="stylesheet" />
<link href="css/sh_whitengrey.css" rel="stylesheet" />
<script type="text/javascript" src="js/sh_main.min.js"></script>
<script type="text/javascript" src="js/lang/sh_python.js"></script>
<link rel="shortcut icon" href="favicon.png" />
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
</head>
<body class="impress-not-supported">
<div class="fallback-message">
<p>Your browser <b>doesn't support the features required</b> by impress.js, so you are presented with a simplified version of this presentation.</p>
<p>For the best experience please use the latest <b>Chrome</b>, <b>Safari</b> or <b>Firefox</b> browser.</p>
</div>
<div id="impress">
<div id="title">
<em>Beatlounge</em>
</div>
<div id="start" class="step slide" data-x="-1000" data-y="-1500">
<q>Making Music with Python and BeatLounge</q>
<br/><br/>
<pre>April 12, 2012 @ PyATL
Drew Smathers
IRC/twitter/github/launchpad: <strong>djfroofy</strong>
</pre>
</div>
<div class="step slide" data-x="0" data-y="-1500">
<q>Computer Music</q>
<ul><li>Musical Analysis</li>
<li>Musical Transcription</li>
<li>DSP (real time, post processing)</li>
<li>Computer-Generated Music</li>
<li>Live Coding</li>
</ul>
</div>
<div class="step slide" data-x="0" data-y="-1500" data-z="-1000" data-rotate-y="40">
<q>What this talk is mostly about ...</q>
<ul>
<li>Computer-Generated Music</li>
<li>Live Coding</li>
<li>BeatLounge</li>
<li>... which is a software sequencer that builds on other Python
libraries used in computer music</li>
</ul>
<p>...with Python of course</p>
</div>
<div class="step slide" data-x="1000" data-y="-1500">
<q>Computer Music Languages and Live Coding Software</q>
<ul><li>Max/MSP</li>
<li>Pure Data</li>
<li>ChucK</li>
<li>SuperCollider</li>
<li>CSound</li>
<li>Impromptu</li>
<li>...</li>
<li>Beatlounge</li>
</ul>
</div>
<div class="step slide" data-x="2000" data-y="-1500">
<q>Making a Sound with Python</q>
<ul>
<li>pyaudio</li>
<li>fluidsynth</li>
<li>pygame/SDL</li>
<li>pyo</li>
</ul>
<q>Recv/Send Music Data with Python</q>
<ul>
<li>MIDI: pyPortMidi etc</li>
<li>OSC: txosc etc</li>
<li>Custom: HTTP, JSON-RPC, AMP, etc</li>
</ul>
</div>
<div class="step slide" data-x="3000" data-y="-1500">
<q>MIDI Overview</q>
<p>Focus: Communicating music events including pitch, velocity and control signals</p>
<p>Around since the 80s ... (note range: [0,127])<p>
<p>0=C, 1=C#, 2=D, ..., 11=B</p>
<p>Use NOTE % 12 to compute octave</p>
<pre class="sh_python">
NOTEON [0,127] [0,127]
NOTEOFF [0,127]
</pre>
</div>
<div class="step slide" data-x="3000" data-y="-1500" data-z="-2000" data-rotate-x="40">
<q>A Note on Notes</q>
<p>MIDI can be used arbitrarily but more often than not application is
for 12TET music. Begin with C=0. A half step is 1.</p>
<pre class="sh_python">
C, Cs, D, Eb, E, F, Fs, G, Gs, A, As, B = range(12)
chromatic_scale = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
major_scale = [C, D, E, F, G, A, B]
minor_scale = aeolian = [C, D, Eb, F, G, Gs, As]
# raise an octave
notes = [note + 12 for note in major_scale]
</pre>
</div>
<div class="step slide" data-x="3000" data-y="-2500" data-z="-2000" data-rotate-x="-40">
<q>A Note on Notes (continued)</q>
<p>A chord is typically a triads (3 notes). These are fun to play with.
You can sound them at the same time or play in sequence (arpeggio).</p>
<pre class="sh_python">
from itertools import combinations
C, Cs, D, Eb, E, F, Fs, G, Gs, A, As, B = range(12)
major_scale = [C, D, E, F, G, A, B]
all_triads = combinations(major_scale, 3)
</pre>
</div>
<div class="step slide" data-x="4000" data-y="-1500">
<q>Fluidsynth</q>
<p>Provides a MIDI interfaces for playing sound fonts</p>
<pre class="sh_python">
import time
from fluidsynth import Synth
fs = Synth()
fs.start('coreaudio')
sfid = fs.sfload("example.sf2")
track = 0
fs.program_select(track, sfid, 0, 0)
# Play some notes
fs.noteon(track, 60, 127)
time.sleep(1)
fs.noteoff(track, 60)
fs.noteon(track, 64, 127)
</pre>
</div>
<div class="step slide" data-x="5000" data-y="-1500">
<q>BeatLounge (bl.instrument.fsynth)</q>
<p>Wraps pyfluidsynth. Some C-ish APIs hidden: sfload(), program_select(), track etc</p>
<pre class="sh_python">
from bl.instrument.fsynth import Instrument
instr = Instrument('example.sf2', connection='mono')
instr.playnote(60, 127)
time.sleep(1)
instr.stopnote(60)
instr.playnote(64, 127)
</pre>
</div>
<div class="step slide" data-x="6000" data-y="-1500">
<q>BeatLounge (bl.comps.midiexample)</q>
<pre class="sh_python">
...
from bl.midi import MonitorHandler, MidiDispatcher
from bl.midi import getInput, getOutput, init
init()
midi_in = getInput('LPK25')
midi_out = getOutput('LPK25')
handler = MonitorHandler({1:instr})
disp = MidiDispatcher(midi_in, [handler])
disp.start()
sender = ClockSender(midi_out)
sender.start()
</pre>
</div>
<div class="step slide" data-x="7000" data-y="-1500">
<q>"Strongly Timed"</q>
<p>
Terminology originated with ChucK. Strongly timed computer music systems provide a
"consistent, sample-synchronous view of time" ... "embeds timing control directly
in the code"</p>
<em>-- http://soundlab.cs.princeton.edu/publications/chuck_sigmm2004.pdf</em>
</p>
</div>
<div class="step slide" data-x="8000" data-y="-1500" data-z="-1000" data-rotate="90">
<q>"Strongly Timed" Pop Quiz</q>
<p>Question: Is the following system strongly-timed?</p>
<pre class="sh_python">
import time
tick = 60. / 130. / 24.
def my_music_player(instruments, schedules):
while True:
time.sleep(tick)
for (instr, schedule) in zip(instruments, schedules):
(t, command, args) = schedule[0]
if time.time() == t:
schedule.pop(0)
if command == 'noteon':
instr.playnote(*args)
elif command == 'noteoff':
instr.stopnote(*args)
</pre>
</div>
<div class="step slide" data-x="9000" data-y="-1500" data-z="-1000" data-rotate="180">
<q>"Strongly Timed" Pop Quiz (continued)</q>
<p>Answer: <strong>NO</strong> ... So many problems</p>
<pre class="sh_python">
# tick + <op time> + <preemption time>
while True:
time.sleep(tick)
# <preemption time>
for (instr, schedule) in zip(instruments, schedules):
# <op time> + <preemption time>
(t, command, args) = schedule[0]
# Equality may never pass - we might end up "ahead"
# of t forever. So ... this is not sample-synchronous.
if time.time() == t:
# ...
</pre>
</div>
<div class="step slide" data-x="10000" data-y="-1500" data-z="-1000" data-rotate="270">
<q>Is BeatLounge Strongly Timed?</q>
<p>Answer: <strong>ALMOST</strong> ... So many problems</p>
<p></p>
<p>
Systems like ChucK are strongly timed and support for very high sample rates.
BeatLounge (or CPython, really) is "good enough" for MIDI sequencing but not fast enough
for DSP. <Insert Actual Metrics>
</p>
<p>
BeatLounge provides sample synchronous view of time: clock.ticks vs time.time()
</p>
</div>
<div class="step slide" data-x="8000" data-y="-1500">
<q>bl.scheduler.BeatClock - Isochronous scheduler</q>
<ul>
<li>Uses <strong>twisted.internet.task.LoopingCall</strong></li>
<li>Sample-synchronous view of time: clock.ticks</li>
<li>Schedules function calls based on virtual clock</li>
<li>Time quantum: tick</li>
<li>Length of a tick depends on TPM (ticks per minute)</li>
<li>standard divisions: quarter: eighth, 1/16, triplets, etc</li>
</ul>
</div>
<div class="step slide" data-x="9000" data-y="-1500">
<q>bl.scheduler.ScheduledEvent</q >
<pre class="sh_python">
# a song.py
...
from itertools import cycle
import random
notes, octaves = [60, 64, 67], [-12, 0, 12]
velocity = cycle([127,80,120,90])
def play_instr():
note = random.choice(notes) + random.choice(octaves)
clock.callLater(18, instr.stopnote, note)
instr.playnote(note, velocity.next())
event = clock.schedule(play_instr)
# Start on the next measure, repeat call every 1/8 note
event.startAfter((1,1), (1,8))
...
</pre>
</div>
<div class="step slide" data-x="10000" data-y="-1500">
<q>BeatLounge "Live Coding": bl.console</q>
<pre class="sh_python">
$ python bl/console.py
>>> from tutor import song1
>>> song1.event.stopAfter((4,1))
</pre>
<p>Hint: Loading soundfonts is very expensive. So ... load
all resources and initialize before starting (not during) performance.
</p>
</div>
<div class="step slide" data-x="11000" data-y="-1500">
<q>bl.console (continued)</q>
<pre>
$ python bl/console.py --help
Usage: console.py [options]
Options:
-c, --channels= Number of channels or a label: stereo,
mono, quad [default: stereo]
-l, --logfile= Path to logfile [default: child.log]
-b, --bpm= The tempo in beats per minute [default: 130]
-t, --tpb= Ticks per beat [default: 24]
-m, --meter= The meter (default 4/4) [default: 4/4]
--version Display Twisted version and exit.
--help Display this help and exit.
</pre>
</div>
<div class="step slide" data-x="12000" data-y="-1500">
<q>bl.console (continued)</q>
Audio device can be specified as well:
<pre>
$ python bl/console.py -m 3/4 -b 150 jack
</pre>
</div>
<div class="step slide" data-x="13000" data-y="-1500">
<q>"Arpeggiator"</q>
<p>Popular feature on early synthesizers. Take notes held down to form arpeggio: sequencing of notes.</p>
<p>Today: software arpegiators. Play a sequence of notes: up, down, up + down, random, in order, ...</p>
<p>Different algorithms can be layered: play X (up, down, ...) over N octaves</p>
</div>
<div class="step slide" data-x="14000" data-y="-1500">
<q>Arpeggiators in BeatLounge (bl.player)</q>
<ul>
<li>Player (alias for NotePlayer), ChordPlayer</li>
<li>player = Player(instrument, noteFactory, velocityFactory, ...)</li>
<li>startPlaying() # start at next measure</li>
<li>stopPlaying() # stop at next measure</li>
</ul>
<strong>API is actually horrible so I won't go into detail. In flux.</strong>
</div>
<div class="step slide" data-x="15000" data-y="-1500">
<q>Arpeggiators in BeatLounge (bl.player)</q>
<pre class="sh_python">
...
from bl.player import Player
notes = cycle([60, 64, 67, 64]).next
velocity = cycle([127,90,120,80]).next
dtt = clock.meter.divisionToTicks
player = Player(instr, notes, velocity,
stop=12, interval=dtt(1,8))
player.startPlaying()
</pre>
</div>
<div class="step slide" data-x="16000" data-y="-1500">
<q>Arpeggiators in BeatLounge (bl.arp)</q>
<ul>
<li>Arp.reset(sequence)</li>
<li>AscArp # up</li>
<li>DescArp # down</li>
<li>RandomArp</li>
<li>ArpSwitcher.switch(arp) ... Adder, OctaveArp</li>
</ul>
</div>
<div class="step slide" data-x="17000" data-y="-1500">
<q>Arpeggiators in BeatLounge (bl.arp)</q>
<pre class="sh_python">
...
from bl.arp import Adder, OrderedArp,
notes = Adder(OctaveArp(RandomArp([0, 4, 7, 9]))
notes.amount = 36
velocity = OrderedArp([127,90,110,80,120,127,90,100])
dtt = clock.meter.divisionToTicks
player = Player(instr, notes, velocity,
stop=12, interval=dtt(1,8))
player.startPlaying()
</pre>
</p>
</div>
<div class="step slide" data-x="17000" data-z="-200" data-y="-2500">
<q>"Rudiments"</q>
<p>"... one of the basic patterns used in rudimental drumming. These patterns of drum strokes can be combined in many ways to create music."
<p>--<a href="http://en.wikipedia.org/wiki/Drum_rudiment">http://en.wikipedia.org/wiki/Drum_rudiment</a>
<p><strong>Examples</strong>: Five Stroke Roll, Six Stroke Roll, Paradiddle, etc.
<p><strong>Components</strong>: Strokes ([R,L,R,L,R,R]) + Timing
</div>
<div class="step slide" data-x="17000" data-z="-400" data-y="-3500">
<q>Rudiments in BeatLounge</q>
<pre class="sh_python">
from bl.rudiments import FiveStrokeRoll
fsr = FiveStrokeRoll()
drum_player = RudimentSchedulePlayer(drum, fsr, 35, 25)
# RudimentSchedulePlayer first gets the stroke from the rudiment:
list(fsr.strokes(35, 25)) = [35, 35, 25, 25, 35,
25, 25, 35, 35, 25]
# ... then a cycle over the time values
list(fsr.time()) == [0, 6, 12, 18, 24,
48, 54, 60, 66, 72]
</pre>
</div>
<div class="step slide" data-x="18000" data-y="-1500">
<q>Actors</q>
<pre class="sh_python">
def change_phrase(arp, phrases):
for i in range(128):
phrase = random.choice(phrases[i:])
for j in range(8):
phrase2 = []
for (i1, i2) in zip([phrase[j]] * 4 + [phrase[-j]] * 4, phrase):
phrase2.extend([i1,i2])
arp.reset(phrase2[:8] + phrase + phrase2[8:] + phrase)
yield
actor = clock.schedule(change_phrase(arp, phrases).next)
actor.startAfter((1,1), (4,1))
</pre>
</div>
<div class="step slide" data-x="19000" data-y="-1500">
<q>Demo Time</q>
<pre class="sh_python">
</pre>
</div>
<div class="step slide" data-x="20000" data-y="-1500">
<q>Links</q>
<p>BeatLounge: <a href="https://github.com/djfroofy/beatlounge">https://github.com/djfroofy/beatlounge</a></p>
<p>pyfluidsynth: <a href="http://code.google.com/p/pyfluidsynth/">http://code.google.com/p/pyfluidsynth</a></p>
<p>pyPortMidi: <a href="http://alumni.media.mit.edu/~harrison/code.html">http://alumni.media.mit.edu/~harrison/code.html</a></p>
<p>txosc: <a href="https://bitbucket.org/arjan/txosc/wiki/Home">https://bitbucket.org/arjan/txosc/wiki/Home</a></p>
<p>pyo: <a href="http://code.google.com/p/pyo/">http://code.google.com/p/pyo/</a></p>
</div>
</div>
<div class="hint">
<p>Use a spacebar or arrow keys to navigate</p>
</div>
<script>
if ("ontouchstart" in document.documentElement) {
document.querySelector(".hint").innerHTML = "<p>Tap on the left or right to navigate</p>";
}
</script>
<script src="js/impress.js"></script>
<script>impress().init();</script>
<script>sh_highlightDocument();</script>
</body>
</html>