Skip to content

Commit 02790c9

Browse files
JunSherntherewasaguy
authored andcommitted
Fractal music example by @JunShern (#308)
* Add new example fractal_music with working code, not yet polished * Change to +/- pitch steering with random durations * Change from +/- to A,B,C symbols, and use textWidth for accurate highlighting
1 parent 45431f9 commit 02790c9

File tree

3 files changed

+152
-0
lines changed

3 files changed

+152
-0
lines changed

examples/fractal_music/index.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<head>
2+
<script language="javascript" type="text/javascript" src="../../lib/p5.js"></script>
3+
4+
<script language="javascript" type="text/javascript" src="../../lib/addons/p5.dom.js"></script>
5+
6+
<script language="javascript" type="text/javascript" src="../../lib/p5.sound.js"></script>
7+
8+
<script language="javascript" type="text/javascript" src="sketch.js"></script>
9+
10+
</head>

examples/fractal_music/sketch.js

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/**
2+
* Example: Fractal Music
3+
* Generate a fractal pattern of music using L-Systems.
4+
*
5+
* We begin with an initial sequence of tokens, and use a programmer-
6+
* defined set of rules to generate new tokens. Here, we apply the rules:
7+
* A -> C, F, E
8+
* C -> G, E
9+
* E -> C, G
10+
* F -> A, C
11+
* G -> E, F, C
12+
* to each token in existing sequences to generate new sequences. The
13+
* tokens used here are plain note names from a fixed octave. Note durations
14+
* are selected randomly from a palette of "nice" durations.
15+
*/
16+
17+
var rules = {
18+
"A":["C","F","E"],
19+
"C":["G","E"],
20+
"E":["C","G"],
21+
"F":["A","C"],
22+
"G":["E","F","C"]
23+
};
24+
var seqIndex = 0;
25+
var noteIndex = -1;
26+
var initSeq = ["C"];
27+
var newTokens = [];
28+
var sequences = [initSeq, []];
29+
var maxNumSequences = 8;
30+
var maxSequenceLength = 30;
31+
32+
var generatingTokenColor;
33+
var newTokenColor;
34+
var fontSize = 20;
35+
var highlightWidth = fontSize;
36+
var highlightHeight = fontSize;
37+
38+
function setup() {
39+
createCanvas(720, 400);
40+
textAlign(CENTER, CENTER);
41+
textFont('monospace');
42+
textSize(fontSize);
43+
generatingTokenColor = color(200,250,255);
44+
newTokenColor = color(240);
45+
46+
synth = new p5.PolySynth();
47+
sloop = new p5.SoundLoop(soundLoop, 0.7);
48+
49+
playPauseButton = createButton('Play/Pause');
50+
playPauseButton.position(width - 2*playPauseButton.size().width, height - 2*playPauseButton.size().height);
51+
playPauseButton.mousePressed(togglePlayPause);
52+
stepButton = createButton("Step");
53+
stepButton.position(2*stepButton.size().width, height - 2*stepButton.size().height);
54+
stepButton.mousePressed(stepSoundLoop)
55+
}
56+
57+
function soundLoop(cycleStartTime) {
58+
noteIndex++;
59+
if (noteIndex >= min(sequences[seqIndex].length, maxSequenceLength)) {
60+
nextSequence();
61+
}
62+
var token = sequences[seqIndex][noteIndex];
63+
64+
var pitch = token + "4";
65+
var velocity = 0.8;
66+
var beatSeconds = 0.5; // Define 1 beat as half a second
67+
var duration = random([beatSeconds, beatSeconds/2, beatSeconds/2, beatSeconds/4]);
68+
this.interval = duration;
69+
synth.play(pitch, velocity, cycleStartTime, duration);
70+
71+
newTokens = rules[token];
72+
sequences[seqIndex+1] = sequences[seqIndex+1].concat(newTokens);
73+
// If the sequence overruns maxSequenceLength, truncate it and proceed to next sequence
74+
if (sequences[seqIndex+1].length >= maxSequenceLength) {
75+
sequences[seqIndex+1] = sequences[seqIndex+1].slice(0, maxSequenceLength);
76+
nextSequence();
77+
}
78+
}
79+
80+
function draw() {
81+
background(255);
82+
83+
highlightNote(seqIndex, noteIndex, generatingTokenColor);
84+
for (var i=0; i<newTokens.length; i++) {
85+
highlightNote(seqIndex + 1, sequences[seqIndex + 1].length - 1 - i, newTokenColor);
86+
}
87+
88+
textAlign(CENTER, CENTER);
89+
noStroke();
90+
for (var i=0; i<sequences.length; i++) {
91+
fill(255 - 195 * (i+1) / sequences.length);
92+
if (i == sequences.length - 1) {
93+
fill(0, 150, 255); // Generated tokens text
94+
}
95+
var seq = sequences[i];
96+
var lineHeight = fontSize + 10;
97+
text(seq.join(" "), width/2, height*2/3 - lineHeight * (sequences.length - i - 1));
98+
}
99+
}
100+
101+
function togglePlayPause() {
102+
// Play/pause
103+
if (sloop.isPlaying) {
104+
sloop.pause();
105+
} else {
106+
sloop.maxIterations = Infinity;
107+
sloop.start();
108+
}
109+
}
110+
111+
function stepSoundLoop() {
112+
sloop.stop();
113+
soundLoop(0);
114+
}
115+
116+
function nextSequence() {
117+
noteIndex = 0;
118+
seqIndex++;
119+
sequences.push([]); // Add a new empty sequence
120+
// If the number of sequences overruns maxNumSequences, remove oldest
121+
if (sequences.length > maxNumSequences) {
122+
seqIndex--;
123+
sequences.shift(); // Removes first element from array
124+
}
125+
}
126+
127+
function highlightNote(seqInd, noteInd, highlightColor) {
128+
if (noteInd < 0) return; // Skip if we haven't started
129+
// X position of character
130+
var seqWidth = textWidth(sequences[seqInd].join(" "));
131+
var xOffset = width/2 - seqWidth/2;
132+
var x = xOffset + seqWidth * noteInd / sequences[seqInd].length;
133+
// Y position of character
134+
var lineHeight = fontSize + 10;
135+
var y = fontSize + height * 2 / 3 - lineHeight * (sequences.length - seqInd - 0);
136+
137+
strokeWeight(1);
138+
stroke(150);
139+
fill(highlightColor);
140+
rect(x, y, highlightWidth, highlightHeight);
141+
}

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ <h2>p5.sound
3636
<div><a href="examples/FFT_scaleOneThirdOctave">FFT_scaleOneThirdOctave</a></div>
3737
<div><a href="examples/Filter_BandPass">Filter_BandPass</a></div>
3838
<div><a href="examples/Filter_LowPass">Filter_LowPass</a></div>
39+
<div><a href="examples/fractal_music">fractal_music</a></div>
3940
<div><a href="examples/genetic_music">genetic_music</a></div>
4041
<div><a href="examples/granular_sampler">granular_sampler</a></div>
4142
<div><a href="examples/granular_sampler_psynth">granular_sampler_psynth</a></div>

0 commit comments

Comments
 (0)