Skip to content

Commit 157eee0

Browse files
committed
added loop synchronization and doc
1 parent 0db339a commit 157eee0

File tree

2 files changed

+122
-31
lines changed

2 files changed

+122
-31
lines changed

examples/sound_loop/sketch.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,20 @@ function setup() {
3434
// create a part with 8 spaces, where each space represents 1/16th note (default)
3535
looper1 = new p5.SoundLoop(function(time){
3636
click.play();
37-
}, "4n");
37+
console.log('looper1 '+this.clock._nextTick);
38+
}, 1);
3839

3940
looper2 = new p5.SoundLoop(function(time){
4041

4142
beatbox.play();
42-
}, 4/3);
43+
// console.log(this.clock.ticks);
44+
console.log('looper2 ' + this.clock._nextTick);
45+
}, "16n");
4346

4447
looper1.start();
45-
looper2.start();
4648

49+
// looper1.start();
4750

48-
looper1.bpm = 120;
49-
looper2.bpm = 120;
50-
5151
}
5252

5353

src/soundLoop.js

Lines changed: 116 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@ define(function (require) {
44
var p5sound = require('master');
55
var Clock = require('Tone/core/Clock');
66

7+
/**
8+
* SoundLoop
9+
*
10+
* @class p5.SoundLoop
11+
* @constructor
12+
*/
713
p5.SoundLoop = function(callback, interval, BPM) {
814

915
this.callback = callback;
1016

11-
//set private variables
12-
this._interval = interval;
13-
14-
//musicalTimeMode is true if time is specified as "4n", "8n" ...etc
1517
this.musicalTimeMode = typeof this._interval === 'number' ? false : true;
18+
this._interval = interval;
1619

1720
//These variables should only be modified if using musicalTimeMode
1821
//If these variables are modified when interval is measured in seconds,
@@ -22,47 +25,126 @@ define(function (require) {
2225
this._timeSignature = 4;
2326
this._bpm = BPM || 60;
2427

28+
this.isPlaying = false;
2529
var self = this;
2630
this.clock = new Clock({
2731
'callback' : function(time) {
2832
var timeFromNow = time - p5sound.audiocontext.currentTime;
29-
self.callback(timeFromNow);
33+
if (timeFromNow > 0) {self.callback(timeFromNow);}
3034
},
31-
'frequency' : this.calcFreq()
35+
'frequency' : this._calcFreq()
3236
});
3337
};
3438

35-
39+
/**
40+
* Start the loop
41+
* @method start
42+
* @param {Number} [timeFromNow] schedule a starting time
43+
*/
3644
p5.SoundLoop.prototype.start = function(timeFromNow) {
3745
var t = timeFromNow || 0;
3846
var now = p5sound.audiocontext.currentTime;
39-
this.clock.start(now + t);
47+
if (!this.isPlaying) {
48+
this.clock.start(now + t);
49+
this.isPlaying = true;
50+
}
4051
};
4152

53+
/**
54+
* Stop the loop
55+
* @method stop
56+
* @param {Number} [timeFromNow] schedule a stopping time
57+
*/
4258
p5.SoundLoop.prototype.stop = function(timeFromNow) {
4359
var t = timeFromNow || 0;
4460
var now = p5sound.audiocontext.currentTime;
45-
this.clock.stop(now + t);
61+
if (this.isPlaying) {
62+
this.clock.stop(now + t);
63+
this.isPlaying = false;
64+
}
4665
};
66+
/**
67+
* Start the loop
68+
* @method pause
69+
* @param {Number} [timeFromNow] schedule a pausing time
70+
*/
71+
p5.SoundLoop.prototype.pause = function(timeFromNow) {
72+
var t = timeFromNow || 0;
73+
if (this.isPlaying) {
74+
this.clock.pause(t);
75+
this.isPlaying = false;
76+
}
77+
};
78+
79+
//use synced start to start 2 loops at the same time
80+
//OR
81+
//sync the start of one loop with one that is already playing
82+
//loop.syncedStart(someother loop)
83+
//
84+
//
85+
//loopToStart.syncedStart( loopToSyncWtih );
86+
//
87+
/**
88+
* Synchronize loops. Use this method to start two more more loops in synchronization
89+
* or to start a loop in synchronization with a loop that is already playing
90+
* This method will schedule the implicit loop in sync with the explicit master loop
91+
* i.e. loopToStart.syncedStart(loopToSyncWith)
92+
*
93+
* @method syncedStart
94+
* @param {Object} otherLoop a p5.SoundLoop to sync with
95+
* @param {Number} [timeFromNow] Start the loops in sync after timeFromNow seconds
96+
*/
97+
p5.SoundLoop.prototype.syncedStart = function(otherLoop, timeFromNow) {
98+
var t = timeFromNow || 0;
99+
var now = p5sound.audiocontext.currentTime;
47100

48-
p5.SoundLoop.prototype.pause = function(time) {
49-
this.clock.pause(time);
101+
if (!otherLoop.isPlaying) {
102+
otherLoop.clock.start(now + t);
103+
otherLoop.isPlaying = true;
104+
this.clock.start(now + t);
105+
this.isPlaying = true;
106+
} else if (otherLoop.isPlaying) {
107+
var time = otherLoop.clock._nextTick - p5sound.audiocontext.currentTime;
108+
this.clock.start(now + time);
109+
this.isPlaying = true;
110+
}
50111
};
51112

113+
114+
115+
/**
116+
* Updates frequency value, reflected in next callback
117+
* @private
118+
* @method _update
119+
*/
52120
p5.SoundLoop.prototype._update = function() {
53-
this.clock.frequency.value = this.calcFreq();
121+
this.clock.frequency.value = this._calcFreq();
54122
};
55123

56-
p5.SoundLoop.prototype.calcFreq = function() {
124+
/**
125+
* Calculate the frequency of the clock's callback based on bpm, interval, and timesignature
126+
* @private
127+
* @method _calcFreq
128+
* @return {Number} new clock frequency value
129+
*/
130+
p5.SoundLoop.prototype._calcFreq = function() {
57131
if (typeof this._interval === 'number') {
58-
console.log(this._interval);
132+
this.musicalTimeMode = false;
59133
return this._bpm / 60 / this._interval * (this._timeSignature / 4);
60134
} else if (typeof this._interval === 'string') {
135+
this.musicalTimeMode = true;
61136
return this._bpm / 60 / this._convertNotation(this._interval) * (this._timeSignature / 4);
62137
}
63138
};
64139

65-
//TIME NOTATION FUNCS
140+
/**
141+
* Convert notation from musical time format to seconds
142+
* Uses <a href = "https://github.com/Tonejs/Tone.js/wiki/Time">Tone.Time</a> convention
143+
* @private
144+
* @method _convertNotation
145+
* @param {String} value value to be converted
146+
* @return {Number} converted value in seconds
147+
*/
66148
p5.SoundLoop.prototype._convertNotation = function(value) {
67149
var type = value.slice(-1);
68150
value = Number(value.slice(0,-1));
@@ -72,33 +154,44 @@ define(function (require) {
72154
case 'n':
73155
return this._note(value);
74156
default:
75-
//console.warn(something);
157+
console.warn('Specified interval is not formatted correctly. See Tone.js '+
158+
'timing reference for more info: https://github.com/Tonejs/Tone.js/wiki/Time');
76159
}
77-
78160
};
79161

80-
162+
/**
163+
* Helper conversion methods of measure and note
164+
* @private
165+
* @method _measure
166+
* @method _note
167+
*/
81168
p5.SoundLoop.prototype._measure = function(value) {
82169
return value * this._timeSignature;
83170
};
84-
85171
p5.SoundLoop.prototype._note = function(value) {
86172
return this._timeSignature / value ;
87173
};
88174

89-
// PUBLIC VARIABLES
175+
176+
/**
177+
* Getters and Setters, setting any paramter will result in a change in the clock's
178+
* frequency, that will be reflected after the next callback
179+
* @param {Number} bpm
180+
* @param {Number} timeSignature
181+
* @param {Number/String} interval [description]
182+
*/
90183
Object.defineProperty(p5.SoundLoop.prototype, 'bpm', {
91184
get : function() {
92185
return this._bpm;
93186
},
94187
set : function(bpm) {
95-
if (!this.musicalTimeMode) {
96-
console.warn('Changing the BPM in "seconds" mode is not advised. '+
188+
if (!this.musicalTimeMode) {
189+
console.warn('Changing the BPM in "seconds" mode is not advised. '+
97190
'This will make the specified time interval inaccurate. '+
98191
'8 second interval at 60BPM in 4/4 time will be 8 seconds long ' +
99192
'8 second interval at 120BPM in 5/4 time will be 3.2 seconds long. '+
100193
'Use musical timing notation ("2n", "4n", "1m"...etc');
101-
}
194+
}
102195
this._bpm = bpm;
103196
this._update();
104197
}
@@ -131,7 +224,5 @@ define(function (require) {
131224
}
132225
});
133226

134-
135227
return p5.SoundLoop;
136-
137228
});

0 commit comments

Comments
 (0)