Skip to content

Commit c47a21a

Browse files
committed
Redesign visuals for markov example, change timing method to millis()
1 parent ce15769 commit c47a21a

File tree

1 file changed

+67
-52
lines changed

1 file changed

+67
-52
lines changed

examples/markov_music/sketch.js

Lines changed: 67 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,39 @@ var latestNodeId;
2929
// Playback SoundLoops
3030
var sloop;
3131
var playing = false;
32-
var mloop;
3332
var secondsPerTick = 0.1;
34-
var ticksSincePrevEvent = 0;
33+
var prevEventMillis = 0;
34+
var timeQuantizationStep = 100; // Quantize to 10 milliseconds
35+
var maxDuration = 5000;
36+
var longestDurationSoFar = timeQuantizationStep;
37+
// Colors
38+
var DEFAULT_NODE_COLOR = [85, 120, 138];
39+
var ACTIVE_NODE_COLOR = [205, 0, 100];
3540

3641
function setup() {
37-
createCanvas(window.innerWidth, window.innerHeight);
42+
createCanvas(720, 400);
3843
frameRate(15);
44+
angleMode(DEGREES);
3945

4046
graph = new Graph();
4147
synth = new p5.PolySynth();
42-
sloop = new p5.SoundLoop(soundLoop, "16n");
43-
mloop = new p5.SoundLoop(metronomeLoop, secondsPerTick);
44-
mloop.start();
48+
sloop = new p5.SoundLoop(soundLoop, 0.1);
4549

4650
playPauseButton = createButton("Play / Pause");
4751
playPauseButton.position(20, height-40);
4852
playPauseButton.mousePressed(togglePlayPause);
53+
54+
prevEventMillis = millis();
4955
}
5056

5157
function draw() {
52-
background(255);
58+
background(242, 221, 164);
59+
fill(163, 196, 188);
60+
rect(0, 0, width/2, height);
5361
// Draw edges
5462
graph.drawEdges();
5563
// Draw nodes
5664
for (var i=0; i<graph.nodes.length; i++) {
57-
graph.nodes[i].bounceOnBoundaries();
5865
graph.nodes[i].update();
5966
graph.nodes[i].display();
6067
}
@@ -63,11 +70,19 @@ function draw() {
6370
textAlign(CENTER, CENTER);
6471
// If there are no nodes, tell the users to play something
6572
if (graph.nodes.length == 0) {
66-
text("Press any of ASDFGHJKL to add notes to the Markov chain.", width/2, height/8);
73+
text("Press any of ASDFGHJKL to add notes to the Markov chain.", width/2, 20);
6774
}
6875
// If we are at the end of the chain, tell the users
6976
if (latestNodeId != null && graph.edges[latestNodeId].length == 0) {
70-
text("Reached end of Markov chain. Play a new note to add to chain.", width/2, height/2);
77+
text("Reached end of Markov chain. Play a new note to add to chain.", width/2, 20);
78+
sloop.stop();
79+
synth.noteRelease(); // Release all notes
80+
}
81+
82+
if (sloop.isPlaying) {
83+
text("Generating from chain...", width/2, height - 20);
84+
} else {
85+
text("PAUSED", width/2, height - 20);
7186
}
7287
}
7388

@@ -86,12 +101,8 @@ function soundLoop(cycleStartTime) {
86101
latestNodeId = random(graph.edges[latestNodeId]);
87102
}
88103
// Wait for the timeFromPrevEvent of the new node
89-
var duration = graph.nodes[latestNodeId].duration * secondsPerTick;
90-
this.interval = duration;
91-
}
92-
93-
function metronomeLoop(cycleStartTime) {
94-
ticksSincePrevEvent = constrain(ticksSincePrevEvent + 1, 0, 30); // Limit the maximum ticks
104+
var timeSincePrevEvent = graph.nodes[latestNodeId].timeSincePrevEvent / 1000; // Millis to seconds
105+
this.interval = max(timeSincePrevEvent, 0.01); // Cannot have interval of exactly 0
95106
}
96107

97108
function keyPressed() {
@@ -102,12 +113,15 @@ function keyPressed() {
102113
var midiNoteNumber = baseNote + keyScale[keyIndex]; // 0-127; 60 is Middle C (C4)
103114
var freq = midiToFreq(midiNoteNumber);
104115
synth.noteAttack(freq, velocity, 0);
116+
// Update time
117+
var timeSincePrevEvent = min(millis() - prevEventMillis, maxDuration);
118+
prevEventMillis = millis();
119+
var quantizedTimeSincePrevEvent = round(timeSincePrevEvent / timeQuantizationStep) * timeQuantizationStep;
105120
// Register node
106-
graph.registerNewNode(1, midiNoteNumber, ticksSincePrevEvent); //keyStates[keyIndex]);
121+
graph.registerNewNode(1, midiNoteNumber, quantizedTimeSincePrevEvent);
107122
// Activate key state
108123
keyStates[keyIndex] = 1;
109124
}
110-
ticksSincePrevEvent = 0;
111125
}
112126

113127
function keyReleased() {
@@ -118,12 +132,17 @@ function keyReleased() {
118132
midiNoteNumber = baseNote + keyScale[keyIndex]; // 0-127; 60 is Middle C (C4)
119133
freq = midiToFreq(midiNoteNumber);
120134
synth.noteRelease(freq, 0);
135+
// Update time
136+
var timeSincePrevEvent = min(millis() - prevEventMillis, maxDuration);
137+
prevEventMillis = millis();
138+
var quantizedTimeSincePrevEvent = round(timeSincePrevEvent / timeQuantizationStep) * timeQuantizationStep;
121139
// Register node
122-
graph.registerNewNode(0, midiNoteNumber, ticksSincePrevEvent);
140+
graph.registerNewNode(0, midiNoteNumber, quantizedTimeSincePrevEvent);
123141
// Reset key state
124142
keyStates[keyIndex] = 0;
143+
144+
timeSincePrevEvent = 0;
125145
}
126-
ticksSincePrevEvent = 0;
127146
}
128147

129148
function togglePlayPause() {
@@ -136,38 +155,44 @@ function togglePlayPause() {
136155
}
137156

138157
// Class for a single node
139-
// characterized by ID, pitch and duration of the note it represents
140-
function Node(id, type, pitch, duration) {
158+
// characterized by ID, pitch and timeSincePrevEvent of the note it represents
159+
function Node(id, type, pitch, timeSincePrevEvent) {
141160
this.id = id;
142-
this.color = [255, 0, 100];
143-
this.diameter = 10;
144-
this.position = createVector(width/2 + random(-100,100), height/2 + random(-100,100));
145-
this.velocity = createVector(random(-1,1), random(-1,1));
146161
this.type = type; // 1 (note on) or 0 (note off)
147162
this.pitch = pitch;
148-
this.duration = duration;
163+
this.timeSincePrevEvent = timeSincePrevEvent;
164+
this.oscillateCounter = 0;
165+
166+
var x = map(this.timeSincePrevEvent, 0, maxDuration/2, 0, width/2);
167+
if (type === 1) {
168+
x = width / 2 + x;
169+
} else {
170+
x = width / 2 - x;
171+
}
172+
var y = map(this.pitch, baseNote, baseNote + max(keyScale), height*0.9, height*0.1);
173+
this.center = createVector(x, y);
174+
this.position = createVector(x, y);
175+
176+
this.color = ACTIVE_NODE_COLOR;
177+
this.diameter = map(this.timeSincePrevEvent, 0, maxDuration, 2, height/20);
149178
}
150179
Node.prototype.isSimilar = function(node) {
151-
var squaredDist = (this.type - node.type)**2 + (this.pitch - node.pitch)**2 + (this.duration - node.duration)**2;
152-
if (squaredDist == 0) {
180+
if (this.type === node.type && this.pitch === node.pitch && this.duration === node.duration) {
153181
return true;
154182
} else {
155183
return false;
156184
}
157185
};
158-
Node.prototype.applyForce = function(force) {
159-
var f = p5.Vector.div(force,this.diameter);
160-
this.acceleration.add(f);
161-
};
162186
Node.prototype.update = function() {
163-
// Velocity changes according to acceleration
164-
this.velocity.add(this.acceleration);
165-
// position changes by velocity
166-
this.position.add(this.velocity);
187+
var yAmplitude = height / 150;
188+
var xAmplitude = height / 300;
189+
this.position.y = this.center.y + (yAmplitude * sin(this.oscillateCounter));
190+
this.position.x = this.center.x + (xAmplitude * cos(this.oscillateCounter));
191+
this.oscillateCounter = this.oscillateCounter + 6;
167192
};
168193
Node.prototype.display = function() {
169194
noStroke();
170-
var color = [200, 200, 200];
195+
var color = DEFAULT_NODE_COLOR;
171196
if (this.id == latestNodeId) {
172197
// Highlight latest node
173198
color = this.color;
@@ -181,17 +206,7 @@ Node.prototype.display = function() {
181206
strokeWeight(2);
182207
stroke(color[0], color[1], color[2]);
183208
}
184-
ellipse(this.position.x,this.position.y,this.diameter,this.diameter);
185-
};
186-
Node.prototype.bounceOnBoundaries = function() {
187-
if (this.position.x >= width || this.position.x <= 0) {
188-
this.velocity.x *= -1;
189-
this.position.x = constrain(this.position.x, 0, width);
190-
}
191-
if (this.position.y >= height || this.position.y <= 0) {
192-
this.velocity.y *= -1;
193-
this.position.y = constrain(this.position.y, 0, height);
194-
}
209+
ellipse(this.position.x, this.position.y, this.diameter, this.diameter);
195210
};
196211

197212
// Graph data structure code adapted from
@@ -210,8 +225,8 @@ Graph.prototype.findNode = function(node) {
210225
}
211226
return -1; // Not found
212227
};
213-
Graph.prototype.registerNewNode = function(type, midiNoteNumber, duration) {
214-
var node = new Node(0, type, midiNoteNumber, duration);
228+
Graph.prototype.registerNewNode = function(type, midiNoteNumber, timeSincePrevEvent) {
229+
var node = new Node(0, type, midiNoteNumber, timeSincePrevEvent);
215230
var nodeId = graph.findNode(node);
216231
if (nodeId == -1) { // If necessary, create the node
217232
nodeId = this.nodes.length;
@@ -243,7 +258,7 @@ Graph.prototype.drawEdges = function() {
243258
if (startNode == latestNodeId) { // Highlight the latest node's edges
244259
stroke(graph.nodes[startNode].color[0], graph.nodes[startNode].color[1], graph.nodes[startNode].color[2], 100);
245260
} else {
246-
stroke(200, 100);
261+
stroke(DEFAULT_NODE_COLOR);
247262
}
248263
for (var j=0; j<graph.edges[i].length; j++) {
249264
var endNode = graph.edges[i][j];

0 commit comments

Comments
 (0)