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
+ } ;
0 commit comments