feat: latencyHint is now set in constructor

BREAK: the latencyHint can only be set in the constructor, no longer settable after construction

addresses #658
This commit is contained in:
Yotam Mann 2020-05-12 12:31:17 -04:00
parent 368a7f43cb
commit ba8e82b1ca
6 changed files with 30 additions and 87 deletions

View file

@ -9,8 +9,8 @@ import { isDefined } from "../util/TypeCheck";
/**
* Create a new AudioContext
*/
export function createAudioContext(): AudioContext {
return new stdAudioContext() as unknown as AudioContext;
export function createAudioContext(options?: AudioContextOptions): AudioContext {
return new stdAudioContext(options) as unknown as AudioContext;
}
/**

View file

@ -8,7 +8,7 @@ type Transport = import("../clock/Transport").Transport;
type Listener = import("./Listener").Listener;
type BaseAudioContextSubset = import("./Context").BaseAudioContextSubset;
export type ContextLatencyHint = AudioContextLatencyCategory | "fastest";
export type ContextLatencyHint = AudioContextLatencyCategory;
export abstract class BaseContext extends Emitter<"statechange" | "tick"> implements BaseAudioContextSubset {

View file

@ -43,6 +43,7 @@ describe("Context", () => {
expect(ctx.createDelay()).to.be.have.property("delayTime");
expect(ctx).to.have.property("createConstantSource");
ctx.dispose();
return ctx.close();
});
if (ONLINE_TESTING) {
@ -61,25 +62,28 @@ describe("Context", () => {
expect(ctx.rawContext).has.property("destination");
expect(ctx.rawContext).has.property("sampleRate");
ctx.dispose();
return ctx.close();
});
it("can be constructed with an options object", () => {
const ctx = new Context({
clockSource: "timeout",
latencyHint: "fastest",
latencyHint: "playback",
lookAhead: 0.2,
});
expect(ctx.lookAhead).to.equal(0.2);
expect(ctx.latencyHint).to.equal("fastest");
expect(ctx.latencyHint).to.equal("playback");
expect(ctx.clockSource).to.equal("timeout");
ctx.dispose();
return ctx.close();
});
it("returns 'now' and 'immediate' time", () => {
const ctx = new Context();
expect(ctx.now()).to.be.a("number");
expect(ctx.immediate()).to.be.a("number");
ctx.dispose();
return ctx.close();
});
});
@ -296,60 +300,6 @@ describe("Context", () => {
});
});
// context("Tone", () => {
// it("has a context", () => {
// expect(Tone.context).to.exist;
// expect(Tone.context).to.be.instanceOf(Context);
// });
// it("can set a new context", () => {
// const originalContext = Tone.context;
// Tone.context = new Context();
// return Tone.context.dispose().then(() => {
// Tone.context = originalContext;
// });
// });
// it("has a consistent context after offline rendering", () => {
// const initialContext = Tone.context;
// const initialTransport = Tone.Transport;
// return Offline(() => { }).then(() => {
// expect(Tone.context).to.equal(initialContext);
// expect(Tone.Transport).to.equal(initialTransport);
// });
// });
// it("invokes the resume promise", () => {
// return Tone.context.resume();
// });
// it("invokes init when a new context is set", done => {
// this.timeout(200);
// const initFn = function(context) {
// expect(Tone.context).to.equal(context);
// Context.off("init", initFn);
// done();
// };
// Context.on("init", initFn);
// Tone.context = new Context();
// });
// it("invokes close when a context is disposed", done => {
// this.timeout(200);
// const closeFn = function(context) {
// expect(context).to.be.instanceOf(Context);
// Context.off("close", closeFn);
// // set a new context
// Tone.context = new Context();
// done();
// };
// Context.on("close", closeFn);
// Tone.context.dispose();
// });
// });
context("get/set", () => {
let ctx;
@ -373,19 +323,6 @@ describe("Context", () => {
expect(ctx.updateInterval).to.equal(0.05);
});
it("can set the latencyHint", () => {
ctx.latencyHint = "fastest";
expect(ctx.latencyHint).to.equal("fastest");
expect(ctx.lookAhead).to.be.closeTo(0.01, 0.05);
expect(ctx.updateInterval).to.be.closeTo(0.01, 0.05);
// test all other latency hints
const latencyHints = ["interactive", "playback", "balanced", 0.2];
latencyHints.forEach(hint => {
ctx.latencyHint = hint;
expect(ctx.latencyHint).to.equal(hint);
});
});
it("gets a constant signal", () => {
return ConstantOutput(context => {
const bufferSrc = context.getConstant(1);

View file

@ -65,7 +65,7 @@ export class Context extends BaseContext {
/**
* The default latency hint
*/
private _latencyHint: ContextLatencyHint | Seconds;
private _latencyHint!: ContextLatencyHint | Seconds;
/**
* An object containing all of the constants AudioBufferSourceNodes
@ -121,10 +121,10 @@ export class Context extends BaseContext {
if (options.context) {
this._context = options.context;
} else {
this._context = createAudioContext();
this._context = createAudioContext({
latencyHint: options.latencyHint,
});
}
this._latencyHint = options.latencyHint;
this.lookAhead = options.lookAhead;
this._ticker = new Ticker(this.emit.bind(this, "tick"), options.clockSource, options.updateInterval);
this.on("tick", this._timeoutLoop.bind(this));
@ -133,6 +133,9 @@ export class Context extends BaseContext {
this._context.onstatechange = () => {
this.emit("statechange", this.state);
};
this._setLatencyHint(options.latencyHint);
this.lookAhead = options.lookAhead;
}
static getDefaults(): ContextOptions {
@ -375,13 +378,19 @@ export class Context extends BaseContext {
* "playback" (prioritizes sustained playback), "balanced" (balances
* latency and performance), and "fastest" (lowest latency, might glitch more often).
* @example
* // set the latencyHint to prioritize smooth playback at the expensive of latency
* Tone.context.latencyHint = "playback";
* // prioritize sustained playback
* const context = new Tone.Context({ latencyHint: "playback" });
* // set this context as the global Context
* Tone.setContext(context);
*/
get latencyHint(): ContextLatencyHint | Seconds {
return this._latencyHint;
}
set latencyHint(hint: ContextLatencyHint | Seconds) {
/**
* Update the lookAhead and updateInterval based on the latencyHint
*/
private _setLatencyHint(hint: ContextLatencyHint | Seconds): void {
let lookAheadValue = 0;
this._latencyHint = hint;
if (isString(hint)) {
@ -390,14 +399,11 @@ export class Context extends BaseContext {
lookAheadValue = 0.1;
break;
case "playback":
lookAheadValue = 0.8;
lookAheadValue = 0.5;
break;
case "balanced":
lookAheadValue = 0.25;
break;
case "fastest":
lookAheadValue = 0.01;
break;
}
}
this.lookAhead = lookAheadValue;
@ -405,7 +411,7 @@ export class Context extends BaseContext {
}
/**
* The unwrapped AudioContext.
* The unwrapped AudioContext or OfflineAudioContext
*/
get rawContext(): AnyAudioContext {
return this._context;

2
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "tone",
"version": "14.6.0",
"version": "14.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "tone",
"version": "14.6.0",
"version": "14.7.0",
"description": "A Web Audio framework for making interactive music in the browser.",
"main": "build/Tone.js",
"module": "build/esm/index.js",