Skip to content

Commit a572536

Browse files
committed
Comprehensive overhaul: Add constants to replace event keys; tidy up examples; remove some cruft; improve naming
1 parent 326ace4 commit a572536

26 files changed

+560
-420
lines changed

README.md

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,53 +13,56 @@ Most of the major parts of isobar are subclasses of `Pattern`, which implement's
1313
# Usage
1414

1515
```python
16-
from isobar import *
17-
18-
# create a repeating sequence with scalar transposition:
19-
# [ 36, 38, 43, 39, ... ]
20-
a = PSeq([ 0, 2, 7, 3 ]) + 36
21-
22-
# apply pattern-wise transposition
23-
# [ 36, 50, 43, 51, ... ]
24-
a = a + PSeq([ 0, 12 ])
25-
26-
# create a geometric chromatic series, repeated back and forth
27-
b = PSeries(0, 1, 12) + 72
28-
b = PPingPong(b)
29-
b = PLoop(b)
30-
31-
# create an velocity series, with emphasis every 4th note,
32-
# plus a random walk to create gradual dynamic changes
33-
amp = PSeq([ 50, 35, 25, 35 ]) + PBrown(0, 1, -20, 20)
34-
35-
# a Timeline schedules events at a given BPM.
36-
# by default, send these over the first MIDI output.
37-
timeline = Timeline(120)
38-
39-
# assign each of our Patterns to particular properties
40-
timeline.sched({ 'note': a, 'dur': 1, 'gate': 2 })
41-
timeline.sched({ 'note': b, 'dur': 0.25, 'amp': amp })
42-
43-
timeline.run()
44-
16+
import isobar as iso
17+
18+
#------------------------------------------------------------------------
19+
# Create a geometric series on a minor scale.
20+
# PingPong plays the series forward then backward. PLoop loops forever.
21+
#------------------------------------------------------------------------
22+
arpeggio = iso.PSeries(0, 2, 6)
23+
arpeggio = iso.PDegree(arpeggio, iso.Scale.minor) + 72
24+
arpeggio = iso.PPingPong(arpeggio)
25+
arpeggio = iso.PLoop(arpeggio)
26+
27+
#------------------------------------------------------------------------
28+
# Create a velocity sequence, with emphasis every 4th note,
29+
# plus a random walk to create gradual dynamic changes.
30+
# Amplitudes are in the MIDI velocity range (0..127).
31+
#------------------------------------------------------------------------
32+
amplitude = iso.PSequence([50, 35, 25, 35]) + iso.PBrown(0, 1, -20, 20)
33+
34+
#------------------------------------------------------------------------
35+
# A Timeline schedules events at a given BPM.
36+
#------------------------------------------------------------------------
37+
timeline = iso.Timeline(120)
38+
39+
#------------------------------------------------------------------------
40+
# Schedule events, with properties mapped to the Pattern objects.
41+
#------------------------------------------------------------------------
42+
timeline.schedule({
43+
iso.EVENT_NOTE: arpeggio,
44+
iso.EVENT_DURATION: 0.25,
45+
iso.EVENT_AMPLITUDE: amplitude
46+
})
4547
```
4648

4749
## Examples
4850

49-
More examples are available in the 'examples' directory with this
51+
More examples are available in the [examples](examples) directory with this
5052
distribution:
5153

52-
* [ex-basics.py](examples/ex-basics.py)
53-
* [ex-lsystem-grapher.py](examples/ex-lsystem-grapher.py)
54-
* [ex-lsystem-rhythm.py](examples/ex-lsystem-rhythm.py)
55-
* [ex-lsystem-stochastic.py](examples/ex-lsystem-stochastic.py)
56-
* [ex-markov-learner.py](examples/ex-markov-learner.py)
57-
* [ex-permut-degree.py](examples/ex-permut-degree.py)
58-
* [ex-piano-phase.py](examples/ex-piano-phase.py)
59-
* [ex-prime-composition.py](examples/ex-prime-composition.py)
60-
* [ex-subsequence.py](examples/ex-subsequence.py)
61-
* [ex-walk.py](examples/ex-walk.py)
62-
54+
* [00.ex-hello-world.py](examples/00.ex-hello-world.py)
55+
* [01.ex-basics.py](examples/01.ex-basics.py)
56+
* [02.ex-subsequence.py](examples/02.ex-subsequence.py)
57+
* [03.ex-euclidean.py](examples/03.ex-euclidean.py)
58+
* [04.ex-permutations.py](examples/04.ex-permutations.py)
59+
* [05.ex-piano-phase.py](examples/05.ex-piano-phase.py)
60+
* [06.ex-walk.py](examples/06.ex-walk.py)
61+
* [10.ex-lsystem-stochastic.py](examples/10.ex-lsystem-stochastic.py)
62+
* [11.ex-lsystem-rhythm.py](examples/11.ex-lsystem-rhythm.py)
63+
* [12.ex-lsystem-grapher.py](examples/12.ex-lsystem-grapher.py)
64+
* [20.ex-markov-learner.py](examples/20.ex-markov-learner.py)
65+
* [30.ex-midifile-write.py](examples/30.ex-midifile-write.py)
6366

6467
## Classes
6568

@@ -82,11 +85,11 @@ Pattern classes:
8285

8386
CORE (core.py)
8487
Pattern - Abstract superclass of all pattern generators.
85-
PConst - Pattern returning a fixed value
88+
PConstant - Pattern returning a fixed value
8689
PRef - Pattern containing a reference to another pattern
8790
PDict - Dict of patterns
91+
PDictKey - Request a specified key from a dictionary.
8892
PIndex - Request a specified index from an array.
89-
PKey - Request a specified key from a dictionary.
9093
PConcat - Concatenate the output of multiple sequences.
9194
PAdd - Add elements of two patterns (shorthand: patternA + patternB)
9295
PSub - Subtract elements of two patterns (shorthand: patternA - patternB)
@@ -99,12 +102,12 @@ Pattern classes:
99102
PRShift - Binary right-shift (shorthand: patternA << patternB)
100103

101104
SEQUENCE (sequence.py)
102-
PSeq - Sequence of values based on an array
105+
PSequence - Sequence of values based on an array
103106
PSeries - Arithmetic series, beginning at <start>, increment by <step>
104107
PRange - Similar to PSeries, but specify a max/step value.
105108
PGeom - Geometric series, beginning at <start>, multiplied by <step>
106109
PLoop - Repeats a finite <pattern> for <n> repeats.
107-
PConcat - Concatenate the output of multiple finite sequences
110+
PConcatenate - Concatenate the output of multiple finite sequences
108111
PPingPong - Ping-pong input pattern back and forth N times.
109112
PCreep - Loop <length>-note segment, progressing <creep> notes after <count> repeats.
110113
PStutter - Play each note of <pattern> <count> times.
@@ -159,7 +162,7 @@ Pattern classes:
159162
PMarkov - First-order Markov chain.
160163

161164
LSYSTEM (lsystem.py)
162-
PLSys - integer sequence derived from Lindenmayer systems
165+
PLSystem - integer sequence derived from Lindenmayer systems
163166

164167
WARP (warp.py)
165168
PWInterpolate - Requests a new target warp value from <pattern> every <length> beats

examples/00.ex-hello-world.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env python3
2+
3+
#------------------------------------------------------------------------
4+
# isobar: ex-hello-world
5+
#
6+
# Introductory example. Plays a one-octave chromatic scale.
7+
# Make sure you have a MIDI device connected.
8+
#------------------------------------------------------------------------
9+
10+
import isobar as iso
11+
12+
import logging
13+
logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")
14+
15+
series = iso.PRange(0, 73, 1)
16+
timeline = iso.Timeline(120)
17+
18+
timeline.schedule({
19+
iso.EVENT_NOTE: series,
20+
iso.EVENT_DURATION: 1
21+
})
22+
23+
timeline.run()
Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/env python
1+
#!/usr/bin/env python3
22

33
#------------------------------------------------------------------------
44
# isobar: ex-basics
@@ -7,48 +7,58 @@
77
# sequences, randomness, scheduling and parameter mapping.
88
#------------------------------------------------------------------------
99

10-
from isobar import *
11-
from isobar.io.midi import MidiOut
10+
import isobar as iso
1211

1312
import logging
13+
1414
logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")
1515

1616
#------------------------------------------------------------------------
17-
# Create a repeating sequence with scalar transposition:
18-
# [ 36, 38, 43, 39, 36, 38, 43, 39, ... ]
17+
# Create a geometric series on a minor scale.
18+
# PingPong plays the series forward then backward. PLoop loops forever.
1919
#------------------------------------------------------------------------
20-
a = PSeq([ 0, 2, 7, 3 ]) + 36
20+
arpeggio = iso.PSeries(0, 2, 6)
21+
arpeggio = iso.PDegree(arpeggio, iso.Scale.minor) + 72
22+
arpeggio = iso.PPingPong(arpeggio)
23+
arpeggio = iso.PLoop(arpeggio)
2124

2225
#------------------------------------------------------------------------
23-
# Apply pattern-wise transposition
24-
# [ 36, 50, 43, 51, ... ]
26+
# Create a velocity sequence, with emphasis every 4th note,
27+
# plus a random walk to create gradual dynamic changes.
28+
# Amplitudes are in the MIDI velocity range (0..127).
2529
#------------------------------------------------------------------------
26-
a = a + PSeq([ 0, 12, 24 ])
30+
amplitude = iso.PSequence([50, 35, 25, 35]) + iso.PBrown(0, 1, -20, 20)
2731

2832
#------------------------------------------------------------------------
29-
# Create a geometric chromatic series, repeated back and forth
33+
# Create a repeating sequence with scalar transposition:
34+
# [ 36, 38, 43, 39, 36, 38, 43, 39, ... ]
3035
#------------------------------------------------------------------------
31-
b = PSeries(0, 2, 6) + 72
32-
b = PPingPong(b)
33-
b = PLoop(b)
36+
bassline = iso.PSequence([0, 2, 7, 3]) + 36
3437

3538
#------------------------------------------------------------------------
36-
# Create an velocity series, with emphasis every 4th note,
37-
# plus a random walk to create gradual dynamic changes
39+
# Repeat each note 3 times, and transpose each into a different octave
40+
# [ 36, 48, 60, 38, 50, 62, ... ]
3841
#------------------------------------------------------------------------
39-
amp = PSeq([ 50, 35, 25, 35 ]) + PBrown(0, 1, -20, 20)
42+
bassline = iso.PStutter(bassline, 3) + iso.PSequence([0, 12, 24])
4043

4144
#------------------------------------------------------------------------
4245
# A Timeline schedules events at a given BPM.
4346
# by default, send these over the first MIDI output.
4447
#------------------------------------------------------------------------
45-
output = MidiOut()
46-
timeline = Timeline(120, output)
48+
output = iso.io.midi.MidiOut()
49+
timeline = iso.Timeline(120, output)
4750

4851
#------------------------------------------------------------------------
4952
# Schedule events, with properties mapped to the Pattern objects.
5053
#------------------------------------------------------------------------
51-
timeline.sched({ 'note': a, 'dur': 1, 'gate': 2 })
52-
timeline.sched({ 'note': b, 'dur': 0.25, 'amp': amp })
54+
timeline.schedule({
55+
iso.EVENT_NOTE: arpeggio,
56+
iso.EVENT_DURATION: 0.25,
57+
iso.EVENT_AMPLITUDE: amplitude
58+
})
59+
timeline.schedule({
60+
iso.EVENT_NOTE: bassline,
61+
iso.EVENT_DURATION: 1
62+
})
5363

5464
timeline.run()

examples/02.ex-subsequence.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env python3
2+
3+
#------------------------------------------------------------------------
4+
# isobar: ex-subsequence
5+
#------------------------------------------------------------------------
6+
7+
import isobar as iso
8+
9+
import logging
10+
11+
logging.basicConfig(level=logging.DEBUG, format="[%(asctime)s] %(message)s")
12+
13+
scale = iso.Scale.pelog
14+
sequence = iso.PDegree(iso.PBrown(0, 3, -12, 12, repeats=False), scale)
15+
16+
offset = iso.PStutter(iso.pattern.PWhite(0, 4), 2)
17+
sequence = iso.PSubsequence(sequence, offset, 4)
18+
sequence = iso.PPermut(sequence)
19+
sequence = sequence + 64
20+
sequence = iso.PReset(sequence, iso.PImpulse(24))
21+
22+
amp = iso.pattern.PSequence([45, 35, 25, 40]) + iso.PBrown(0, 1, -15, 10)
23+
24+
gate = iso.pattern.PBrown(1.5, 0.01, 0.6, 2.5)
25+
26+
timeline = iso.Timeline(120)
27+
timeline.schedule({
28+
iso.EVENT_NOTE: sequence.copy(),
29+
iso.EVENT_AMPLITUDE: amp,
30+
iso.EVENT_DURATION: 0.25,
31+
iso.EVENT_GATE: gate
32+
})
33+
timeline.schedule({
34+
iso.EVENT_NOTE: sequence.copy() + 24,
35+
iso.EVENT_AMPLITUDE: amp.copy(),
36+
iso.EVENT_DURATION: 0.5,
37+
iso.EVENT_GATE: gate.copy()
38+
})
39+
timeline.schedule({
40+
iso.EVENT_NOTE: sequence.copy() - 24,
41+
iso.EVENT_AMPLITUDE: 10 + amp.copy() * 0.5,
42+
iso.EVENT_DURATION: iso.PChoice([4, 4, 6, 8]),
43+
iso.EVENT_GATE: gate.copy()
44+
})
45+
46+
timeline.run()

examples/03.ex-euclidean.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env python3
2+
3+
#------------------------------------------------------------------------
4+
# isobar: ex-euclidean
5+
#
6+
# Uses Euclidean rhythms to generate multiple polyrhythmic voices.
7+
#------------------------------------------------------------------------
8+
9+
import isobar as iso
10+
11+
import logging
12+
logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")
13+
14+
timeline = iso.Timeline(100)
15+
16+
timeline.schedule({
17+
iso.EVENT_NOTE: 60 * iso.PEuclidean(5, 8),
18+
iso.EVENT_DURATION: 0.25
19+
}, delay=0.0)
20+
timeline.schedule({
21+
iso.EVENT_NOTE: 62 * iso.PEuclidean(5, 13),
22+
iso.EVENT_DURATION: 0.5
23+
}, delay=0.25)
24+
timeline.schedule({
25+
iso.EVENT_NOTE: 64 * iso.PEuclidean(7, 15),
26+
iso.EVENT_DURATION: 0.5
27+
}, delay=0.5)
28+
timeline.schedule({
29+
iso.EVENT_NOTE: 67 * iso.PEuclidean(6, 19),
30+
iso.EVENT_DURATION: 0.25
31+
}, delay=0.75)
32+
timeline.schedule({
33+
iso.EVENT_NOTE: 71 * iso.PEuclidean(7, 23),
34+
iso.EVENT_DURATION: 0.5
35+
}, delay=1.0)
36+
37+
timeline.run()

0 commit comments

Comments
 (0)