mirror of
https://github.com/Tonejs/Tone.js
synced 2024-11-16 08:38:00 +00:00
ability to get/set ticks at a given time. more accurate seconds counter using elapsed ticks.
This commit is contained in:
parent
60cdeaa5f3
commit
32d5451293
2 changed files with 282 additions and 9 deletions
|
@ -58,9 +58,9 @@ define(["Tone/core/Tone", "Tone/signal/TickSignal", "Tone/core/TimelineState",
|
|||
* The number of times the callback was invoked. Starts counting at 0
|
||||
* and increments after the callback was invoked.
|
||||
* @type {Ticks}
|
||||
* @readOnly
|
||||
* @private
|
||||
*/
|
||||
this.ticks = 0;
|
||||
this._ticks = 0;
|
||||
|
||||
/**
|
||||
* The state timeline
|
||||
|
@ -69,6 +69,18 @@ define(["Tone/core/Tone", "Tone/signal/TickSignal", "Tone/core/TimelineState",
|
|||
*/
|
||||
this._state = new Tone.TimelineState(Tone.State.Stopped);
|
||||
|
||||
/**
|
||||
* The offset values of the ticks
|
||||
* @type {Tone.Timeline}
|
||||
* @private
|
||||
*/
|
||||
this._tickOffset = new Tone.Timeline();
|
||||
//add the first event
|
||||
this._tickOffset.add({
|
||||
"time" : 0,
|
||||
"ticks" : 0
|
||||
});
|
||||
|
||||
/**
|
||||
* The loop function bound to its context.
|
||||
* This is necessary to remove the event in the end.
|
||||
|
@ -116,8 +128,11 @@ define(["Tone/core/Tone", "Tone/signal/TickSignal", "Tone/core/TimelineState",
|
|||
Tone.Clock.prototype.start = function(time, offset){
|
||||
time = this.toSeconds(time);
|
||||
if (this._state.getValueAtTime(time) !== Tone.State.Started){
|
||||
var ticksAtTime = Math.floor(this.getTicksAtTime(time));
|
||||
offset = Tone.defaultArg(offset, ticksAtTime);
|
||||
this._state.setStateAtTime(Tone.State.Started, time);
|
||||
this._state.get(time).offset = offset;
|
||||
this.setTicksAtTime(offset, time);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
@ -133,6 +148,7 @@ define(["Tone/core/Tone", "Tone/signal/TickSignal", "Tone/core/TimelineState",
|
|||
time = this.toSeconds(time);
|
||||
this._state.cancel(time);
|
||||
this._state.setStateAtTime(Tone.State.Stopped, time);
|
||||
this.setTicksAtTime(0, time);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
@ -144,11 +160,87 @@ define(["Tone/core/Tone", "Tone/signal/TickSignal", "Tone/core/TimelineState",
|
|||
Tone.Clock.prototype.pause = function(time){
|
||||
time = this.toSeconds(time);
|
||||
if (this._state.getValueAtTime(time) === Tone.State.Started){
|
||||
var pausedTicks = this.getTicksAtTime(time);
|
||||
this.setTicksAtTime(pausedTicks, time);
|
||||
this._state.setStateAtTime(Tone.State.Paused, time);
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* The number of times the callback was invoked. Starts counting at 0
|
||||
* and increments after the callback was invoked.
|
||||
* @type {Ticks}
|
||||
*/
|
||||
Object.defineProperty(Tone.Clock.prototype, "ticks", {
|
||||
get : function(){
|
||||
return this._ticks;
|
||||
},
|
||||
set : function(t){
|
||||
this._ticks = t;
|
||||
this.setTicksAtTime(t, this.now());
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The time since ticks=0 that the Clock has been running. Accounts
|
||||
* for tempo curves
|
||||
* @type {Seconds}
|
||||
*/
|
||||
Object.defineProperty(Tone.Clock.prototype, "seconds", {
|
||||
get : function(){
|
||||
var time = this.now();
|
||||
var ticks = this.getTicksAtTime(time);
|
||||
var totalTicks = this.frequency.getTicksAtTime(time);
|
||||
if (totalTicks - ticks > 0){
|
||||
var tickTime = this.frequency.getTimeOfTick(totalTicks - ticks);
|
||||
return this.frequency.ticksToTime(ticks, tickTime).toSeconds();
|
||||
} else {
|
||||
return this.frequency.ticksToTime(ticks, time).toSeconds();
|
||||
}
|
||||
},
|
||||
set : function(s){
|
||||
var now = this.now();
|
||||
var ticks = this.frequency.timeToTicks(s, now-s);
|
||||
this.setTicksAtTime(ticks, now);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the elapsed ticks at the given time
|
||||
* @param {Time} time When to get the ticks
|
||||
* @return {Ticks} The elapsed ticks at the given time.
|
||||
*/
|
||||
Tone.Clock.prototype.getTicksAtTime = function(time){
|
||||
time = this.toSeconds(time);
|
||||
var tickEvent = this._tickOffset.get(time);
|
||||
var offset = tickEvent.ticks;
|
||||
var elapsedTicks = this.frequency.getTicksAtTime(time) - tickEvent.position;
|
||||
if (this._state.getValueAtTime(time) !== Tone.State.Started){
|
||||
return offset;
|
||||
} else {
|
||||
return elapsedTicks + offset;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the clock's ticks at the given time.
|
||||
* @param {Ticks} ticks The tick value to set
|
||||
* @param {Time} time When to set the tick value
|
||||
* @return {Tone.Clock} this
|
||||
*/
|
||||
Tone.Clock.prototype.setTicksAtTime = function(ticks, time){
|
||||
time = this.toSeconds(time);
|
||||
this._ticks = ticks;
|
||||
this._tickOffset.cancel(time);
|
||||
this._tickOffset.add({
|
||||
"time" : time,
|
||||
"ticks" : ticks,
|
||||
"position" : this.frequency.getTicksAtTime(time)
|
||||
});
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* The scheduling loop.
|
||||
* @private
|
||||
|
@ -167,14 +259,12 @@ define(["Tone/core/Tone", "Tone/signal/TickSignal", "Tone/core/TimelineState",
|
|||
this._lastState = event.state;
|
||||
switch(event.state){
|
||||
case Tone.State.Started:
|
||||
if (!Tone.isUndef(event.offset)){
|
||||
this.ticks = event.offset;
|
||||
}
|
||||
this._ticks = event.offset;
|
||||
this._nextTick = event.time;
|
||||
this.emit("start", event.time, this.ticks);
|
||||
this.emit("start", event.time, this._ticks);
|
||||
break;
|
||||
case Tone.State.Stopped:
|
||||
this.ticks = 0;
|
||||
this._ticks = 0;
|
||||
this.emit("stop", event.time);
|
||||
break;
|
||||
case Tone.State.Paused:
|
||||
|
@ -191,9 +281,9 @@ define(["Tone/core/Tone", "Tone/signal/TickSignal", "Tone/core/TimelineState",
|
|||
if (event.state === Tone.State.Started){
|
||||
try {
|
||||
this.callback(tickTime);
|
||||
this.ticks++;
|
||||
this._ticks++;
|
||||
} catch(e){
|
||||
this.ticks++;
|
||||
this._ticks++;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
@ -225,6 +315,8 @@ define(["Tone/core/Tone", "Tone/signal/TickSignal", "Tone/core/TimelineState",
|
|||
this._writable("frequency");
|
||||
this.frequency.dispose();
|
||||
this.frequency = null;
|
||||
this._tickOffset.dispose();
|
||||
this._tickOffset = null;
|
||||
this._boundLoop = null;
|
||||
this._nextTick = Infinity;
|
||||
this.callback = null;
|
||||
|
|
|
@ -190,6 +190,66 @@ define(["Test", "Tone/core/Clock", "helper/Offline", "helper/Supports"],
|
|||
});
|
||||
});
|
||||
|
||||
context("Seconds", function(){
|
||||
|
||||
it("can set the current seconds", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 10);
|
||||
expect(clock.seconds).to.equal(0);
|
||||
clock.seconds = 3;
|
||||
expect(clock.seconds).to.be.closeTo(3, 0.01);
|
||||
clock.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it("can get the seconds", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 10);
|
||||
expect(clock.seconds).to.equal(0);
|
||||
clock.start(0.05);
|
||||
return function(time){
|
||||
if (time > 0.05){
|
||||
expect(clock.seconds).to.be.closeTo(time - 0.05, 0.01);
|
||||
}
|
||||
};
|
||||
}, 0.1);
|
||||
});
|
||||
|
||||
it("can get the seconds during a bpm ramp", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 10);
|
||||
expect(clock.seconds).to.equal(0);
|
||||
clock.start(0.05);
|
||||
clock.frequency.linearRampTo(60, 0.5, 0.5)
|
||||
return function(time){
|
||||
if (time > 0.05){
|
||||
expect(clock.seconds).to.be.closeTo(time - 0.05, 0.01);
|
||||
}
|
||||
};
|
||||
}, 0.7);
|
||||
});
|
||||
|
||||
it("can set seconds during a bpm ramp", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 10);
|
||||
expect(clock.seconds).to.equal(0);
|
||||
clock.start(0.05);
|
||||
clock.frequency.linearRampTo(60, 0.5, 0.5)
|
||||
var changeSeconds = Test.atTime(0.4, function(){
|
||||
clock.seconds = 0
|
||||
});
|
||||
return function(time){
|
||||
changeSeconds(time);
|
||||
if (time > 0.05 && time < 0.4){
|
||||
expect(clock.seconds).to.be.closeTo(time - 0.05, 0.01);
|
||||
} else if (time > 0.4){
|
||||
expect(clock.seconds).to.be.closeTo(time - 0.4, 0.01);
|
||||
}
|
||||
};
|
||||
}, 0.7);
|
||||
});
|
||||
});
|
||||
|
||||
context("Ticks", function(){
|
||||
|
||||
it ("has 0 ticks when first created", function(){
|
||||
|
@ -331,5 +391,126 @@ define(["Test", "Tone/core/Clock", "helper/Offline", "helper/Supports"],
|
|||
});
|
||||
});
|
||||
|
||||
context("[get,set]TicksAtTime", function(){
|
||||
|
||||
it ("always reports 0 if not started", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 20);
|
||||
expect(clock.getTicksAtTime(0)).to.equal(0);
|
||||
expect(clock.getTicksAtTime(1)).to.equal(0);
|
||||
expect(clock.getTicksAtTime(2)).to.equal(0);
|
||||
clock.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it ("can get ticks in the future", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 20);
|
||||
clock.start(1);
|
||||
expect(clock.getTicksAtTime(1)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(1.5)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(2)).to.be.closeTo(20, 0.01);
|
||||
clock.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it ("pauses on last ticks", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 20);
|
||||
clock.start(0).pause(1);
|
||||
expect(clock.getTicksAtTime(0.5)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(1)).to.be.closeTo(20, 0.01);
|
||||
expect(clock.getTicksAtTime(2)).to.be.closeTo(20, 0.01);
|
||||
expect(clock.getTicksAtTime(3)).to.be.closeTo(20, 0.01);
|
||||
clock.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it ("resumes from paused position", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 20);
|
||||
clock.start(0).pause(1).start(2);
|
||||
expect(clock.getTicksAtTime(0.5)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(1)).to.be.closeTo(20, 0.01);
|
||||
expect(clock.getTicksAtTime(2)).to.be.closeTo(20, 0.01);
|
||||
expect(clock.getTicksAtTime(3)).to.be.closeTo(40, 0.01);
|
||||
expect(clock.getTicksAtTime(3.5)).to.be.closeTo(50, 0.01);
|
||||
clock.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it ("can set a tick value at the given time", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 20);
|
||||
clock.start(0);
|
||||
clock.setTicksAtTime(0, 1);
|
||||
clock.setTicksAtTime(0, 2);
|
||||
expect(clock.getTicksAtTime(0)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(0.5)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(1)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(1.5)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(2)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(2.5)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(3)).to.be.closeTo(20, 0.01);
|
||||
clock.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it ("can get a tick position while the frequency is scheduled with setValueAtTime", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 20);
|
||||
clock.start(0);
|
||||
clock.frequency.setValueAtTime(2, 1)
|
||||
clock.setTicksAtTime(0, 1);
|
||||
clock.setTicksAtTime(0, 2);
|
||||
expect(clock.getTicksAtTime(0)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(0.5)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(1)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(1.5)).to.be.closeTo(1, 0.01);
|
||||
expect(clock.getTicksAtTime(2)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(2.5)).to.be.closeTo(1, 0.01);
|
||||
expect(clock.getTicksAtTime(3)).to.be.closeTo(2, 0.01);
|
||||
clock.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it ("can get a tick position while the frequency is scheduled with linearRampTo", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 20);
|
||||
clock.start(0);
|
||||
clock.frequency.linearRampTo(2, 1, 1)
|
||||
clock.setTicksAtTime(0, 1);
|
||||
clock.setTicksAtTime(10, 2);
|
||||
expect(clock.getTicksAtTime(0)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(0.5)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(1)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(1.5)).to.be.closeTo(7.75, 0.01);
|
||||
expect(clock.getTicksAtTime(2)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(2.5)).to.be.closeTo(11, 0.01);
|
||||
expect(clock.getTicksAtTime(3)).to.be.closeTo(12, 0.01);
|
||||
clock.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
it ("can get a tick position while the frequency is scheduled with exponentialRampTo", function(){
|
||||
return Offline(function(){
|
||||
var clock = new Clock(function(){}, 20);
|
||||
clock.start(0);
|
||||
clock.frequency.exponentialRampTo(2, 1, 1)
|
||||
clock.setTicksAtTime(0, 1);
|
||||
clock.setTicksAtTime(10, 2);
|
||||
expect(clock.getTicksAtTime(0)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(0.5)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(1)).to.be.closeTo(0, 0.01);
|
||||
expect(clock.getTicksAtTime(1.5)).to.be.closeTo(5.96, 0.01);
|
||||
expect(clock.getTicksAtTime(2)).to.be.closeTo(10, 0.01);
|
||||
expect(clock.getTicksAtTime(2.5)).to.be.closeTo(11, 0.01);
|
||||
expect(clock.getTicksAtTime(3)).to.be.closeTo(12, 0.01);
|
||||
clock.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue