Skip to content

Commit a1d49b9

Browse files
committed
Add markov implementation, works but unpolished
1 parent 3e5c085 commit a1d49b9

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

examples/markov_music/index.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
<style>
11+
body {
12+
margin: 0;
13+
}
14+
</style>
15+
</head>

examples/markov_music/sketch.js

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
var synth;
2+
var velocity = 0.7; // From 0-1
3+
var baseNote = 60;
4+
var keyOrder = "ASDFGHJKL";
5+
var keyScale = [0,2,4,5,7,9,11,12,14];
6+
var keyStates = [0,0,0,0,0,0,0,0,0];
7+
8+
var sloop;
9+
var transition_table = {};
10+
var nodes = [];
11+
var graph;
12+
var latestNodeId;
13+
var playing = false;
14+
var timeQuantization = 0.1;
15+
var sphereRadius = 200;
16+
17+
function setup() {
18+
createCanvas(window.innerWidth, window.innerHeight);
19+
frameRate(15);
20+
21+
synth = new p5.PolySynth();
22+
23+
graph = new Graph();
24+
25+
sloop = new p5.SoundLoop(soundLoop, "16n");
26+
mloop = new p5.SoundLoop(metronomeLoop, timeQuantization);
27+
mloop.start();
28+
29+
playPauseButton = createButton("Play / Pause");
30+
playPauseButton.position(20, 20);
31+
playPauseButton.mousePressed(togglePlayPause);
32+
}
33+
34+
function metronomeLoop(cycleStartTime) {
35+
for (var i=0; i<keyStates.length; i++) {
36+
if (keyStates[i] > 0) {
37+
keyStates[i] = keyStates[i] + 1;
38+
console.log(keyStates[i]);
39+
}
40+
}
41+
}
42+
43+
function soundLoop(cycleStartTime) {
44+
// Transition to a random new node
45+
latestNodeId = random(graph.edges[latestNodeId]);
46+
// Play the sound of this node
47+
var midiNoteNumber = graph.nodes[latestNodeId].pitch;
48+
var freq = midiToFreq(midiNoteNumber);
49+
var duration = graph.nodes[latestNodeId].duration * timeQuantization;
50+
synth.play(freq, velocity, cycleStartTime, duration);
51+
this.interval = duration;
52+
}
53+
54+
function togglePlayPause() {
55+
if (sloop.isPlaying) {
56+
sloop.stop();
57+
} else {
58+
sloop.start();
59+
}
60+
}
61+
62+
function keyPressed() {
63+
var keyIndex = keyOrder.indexOf(key);
64+
// Check if valid note key pressed
65+
if (keyIndex >= 0) {
66+
// Update key state
67+
keyStates[keyIndex] = 1;
68+
// Play synth
69+
var midiNoteNumber = baseNote + keyScale[keyIndex]; // 0-127; 60 is Middle C (C4)
70+
var freq = midiToFreq(midiNoteNumber);
71+
synth.noteAttack(freq, velocity, 0);
72+
}
73+
}
74+
75+
function keyReleased() {
76+
var keyIndex = keyOrder.indexOf(key);
77+
// Check if valid note key pressed
78+
if (keyIndex >= 0) {
79+
// Stop synth
80+
midiNoteNumber = baseNote + keyScale[keyIndex]; // 0-127; 60 is Middle C (C4)
81+
freq = midiToFreq(midiNoteNumber);
82+
synth.noteRelease(freq, 0);
83+
// Register node
84+
graph.registerNewNode(midiNoteNumber, keyStates[keyIndex]);
85+
// Update key state
86+
keyStates[keyIndex] = 0;
87+
}
88+
}
89+
90+
function draw() {
91+
background(255);
92+
noStroke();
93+
fill(245);
94+
ellipse(width/2, height/2, sphereRadius*2, sphereRadius*2);
95+
96+
// Draw edges
97+
for (var i=0; i<graph.edges.length; i++) {
98+
var startNode = i;
99+
if (startNode == latestNodeId) {
100+
strokeWeight(1);
101+
stroke(graph.nodes[startNode].color[0], graph.nodes[startNode].color[1], graph.nodes[startNode].color[2]);
102+
} else {
103+
strokeWeight(1);
104+
stroke(200, 100);
105+
}
106+
for (var j=0; j<graph.edges[i].length; j++) {
107+
var endNode = graph.edges[i][j];
108+
line(graph.nodes[startNode].position.x, graph.nodes[startNode].position.y, graph.nodes[endNode].position.x, graph.nodes[endNode].position.y);
109+
}
110+
}
111+
112+
// Draw nodes
113+
for (var i=0; i<graph.nodes.length; i++) {
114+
graph.nodes[i].checkEdges();
115+
graph.nodes[i].update();
116+
graph.nodes[i].display();
117+
}
118+
}
119+
120+
function Node(id, pitch, duration) {
121+
this.id = id;
122+
this.color = [255, 0, 100];
123+
this.diameter = 10;
124+
this.position = createVector(width/2 + random(-100,100), height/2 + random(-100,100));
125+
this.velocity = createVector(random(-1,1), random(-1,1));
126+
this.pitch = pitch;
127+
this.duration = duration;
128+
}
129+
Node.prototype.distance = function(node) {
130+
var squaredDist = (this.pitch - node.pitch)**2 + (this.duration - node.duration)**2;
131+
return squaredDist;
132+
}
133+
Node.prototype.applyForce = function(force) {
134+
var f = p5.Vector.div(force,this.diameter);
135+
this.acceleration.add(f);
136+
};
137+
Node.prototype.update = function() {
138+
// Velocity changes according to acceleration
139+
this.velocity.add(this.acceleration);
140+
// position changes by velocity
141+
this.position.add(this.velocity);
142+
};
143+
Node.prototype.display = function() {
144+
noStroke();
145+
// Highlight latest node
146+
if (this.id == latestNodeId) {
147+
fill(this.color[0], this.color[1], this.color[2]);
148+
ellipse(this.position.x,this.position.y,this.diameter,this.diameter);
149+
} else {
150+
fill(200);
151+
ellipse(this.position.x,this.position.y,this.diameter,this.diameter);
152+
}
153+
};
154+
Node.prototype.checkEdges = function() {
155+
var distanceFromCenter = sqrt((this.position.y - height/2)**2 + (this.position.x - width/2)**2);
156+
if (distanceFromCenter > sphereRadius) {
157+
this.velocity.x *= -1;
158+
this.velocity.y *= -1;
159+
}
160+
};
161+
162+
// Graph data structure code adapted from
163+
// http://blog.benoitvallon.com/data-structures-in-javascript/the-graph-data-structure/
164+
function Graph() {
165+
this.nodes = [];
166+
this.nodeIds = [];
167+
this.edges = [];
168+
this.numberOfEdges = 0;
169+
}
170+
Graph.prototype.findNode = function(node) {
171+
for (var i=0; i<this.nodes.length; i++) {
172+
if (abs(node.distance(this.nodes[i])) == 0) {
173+
return i;
174+
}
175+
}
176+
return -1; // Not found, add to end of array
177+
}
178+
Graph.prototype.registerNewNode = function(midiNoteNumber, duration) {
179+
var node = new Node(0, midiNoteNumber, duration);
180+
var nodeId = graph.findNode(node);
181+
// If necessary, create the node
182+
if (nodeId == -1) {
183+
nodeId = this.nodes.length;
184+
this.addNode(node);
185+
}
186+
node.id = nodeId;
187+
// Add an edge from the previous node to this one
188+
if (latestNodeId != null) { // On the first start it will be null
189+
this.addEdge(latestNodeId, nodeId);
190+
}
191+
latestNodeId = nodeId;
192+
};
193+
Graph.prototype.addNode = function(node) {
194+
var nodeId = this.nodes.length;
195+
this.nodeIds.push(nodeId);
196+
this.nodes.push(node);
197+
this.edges[nodeId] = [];
198+
}
199+
Graph.prototype.addEdge = function(nodeId1, nodeId2) {
200+
this.edges[nodeId1].push(nodeId2);
201+
this.numberOfEdges++;
202+
};

index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ <h2>p5.sound
4343
<div><a href="examples/loadSound_with_Drag_and_Drop">loadSound_with_Drag_and_Drop</a></div>
4444
<div><a href="examples/loop_stepSequencer">loop_stepSequencer</a></div>
4545
<div><a href="examples/looper_simple">looper_simple</a></div>
46+
<div><a href="examples/markov_music">markov_music</a></div>
4647
<div><a href="examples/micFFT">micFFT</a></div>
4748
<div><a href="examples/micLevel">micLevel</a></div>
4849
<div><a href="examples/micLevel_on_off">micLevel_on_off</a></div>
@@ -72,6 +73,7 @@ <h2>p5.sound
7273
<div><a href="examples/Reverb_convolve">Reverb_convolve</a></div>
7374
<div><a href="examples/Reverb_convolve_FFT">Reverb_convolve_FFT</a></div>
7475
<div><a href="examples/soundfile_playMode">soundfile_playMode</a></div>
76+
<div><a href="examples/soundfile_remove_cue">soundfile_remove_cue</a></div>
7577
<div><a href="examples/soundfileMod_AM">soundfileMod_AM</a></div>
7678
<div><a href="examples/soundFormats">soundFormats</a></div>
7779
<div><a href="examples/spatial_panning">spatial_panning</a></div>

0 commit comments

Comments
 (0)