mirror of
https://github.com/Tonejs/Tone.js
synced 2024-12-28 12:33:12 +00:00
3196b2d68e
fix attempt #2 for AudioBufferSourceNode stop time miscalculation, with updated tests
725 lines
20 KiB
TypeScript
725 lines
20 KiB
TypeScript
import { expect } from "chai";
|
|
import { BasicTests } from "test/helper/Basic";
|
|
import { CompareToFile } from "test/helper/CompareToFile";
|
|
import { atTime, Offline, whenBetween } from "test/helper/Offline";
|
|
import { SourceTests } from "test/helper/SourceTests";
|
|
import { ToneAudioBuffer } from "Tone/core/context/ToneAudioBuffer";
|
|
import { getContext } from "Tone/core/Global";
|
|
import { Player } from "./Player";
|
|
|
|
describe("Player", () => {
|
|
const buffer = new ToneAudioBuffer();
|
|
|
|
beforeEach(() => {
|
|
return buffer.load("./audio/sine.wav");
|
|
});
|
|
|
|
// run the common tests
|
|
BasicTests(Player, buffer);
|
|
SourceTests(Player, buffer);
|
|
|
|
it("matches a file", () => {
|
|
return CompareToFile(
|
|
() => {
|
|
const player = new Player(buffer).toDestination();
|
|
player.start(0.1).stop(0.2);
|
|
player.playbackRate = 2;
|
|
},
|
|
"player.wav",
|
|
0.005
|
|
);
|
|
});
|
|
|
|
context("Constructor", () => {
|
|
it("can be constructed with a Tone.Buffer", () => {
|
|
const player = new Player(buffer);
|
|
expect(player.buffer.get()).to.equal(buffer.get());
|
|
player.dispose();
|
|
});
|
|
|
|
it("can be constructed with an AudioBuffer", () => {
|
|
const player = new Player(buffer.get());
|
|
expect(player.buffer.get()).to.equal(buffer.get());
|
|
player.dispose();
|
|
});
|
|
|
|
it("can be constructed with an unloaded Tone.Buffer", (done) => {
|
|
const playerBuffer = new ToneAudioBuffer("./audio/sine.wav");
|
|
const player = new Player(playerBuffer, () => {
|
|
expect(player.buffer.get()).to.equal(playerBuffer.get());
|
|
player.dispose();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("can be constructed with no arguments", () => {
|
|
const player = new Player();
|
|
// set the buffer
|
|
player.buffer = buffer;
|
|
expect(player.buffer.get()).to.equal(buffer.get());
|
|
player.dispose();
|
|
});
|
|
});
|
|
|
|
context("onstop", () => {
|
|
it("invokes the onstop method when the player is explicitly stopped", () => {
|
|
let wasInvoked = false;
|
|
return Offline(() => {
|
|
const player = new Player({
|
|
onstop: () => {
|
|
wasInvoked = true;
|
|
},
|
|
url: buffer,
|
|
});
|
|
player.start(0).stop(0.1);
|
|
}, 0.2).then(() => {
|
|
expect(wasInvoked).to.equal(true);
|
|
});
|
|
});
|
|
|
|
it("invokes the onstop method when the file is naturally over", () => {
|
|
let wasInvoked = false;
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.start(0);
|
|
player.onstop = () => {
|
|
wasInvoked = true;
|
|
expect(player.state).to.equal("stopped");
|
|
};
|
|
}, buffer.duration * 1.1).then(() => {
|
|
expect(wasInvoked).to.equal(true);
|
|
});
|
|
});
|
|
|
|
it("invokes the onstop method on restart", () => {
|
|
let wasInvoked = 0;
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.start(0).restart(0.1).stop(0.2);
|
|
player.onstop = () => {
|
|
wasInvoked++;
|
|
};
|
|
}, 0.3).then(() => {
|
|
expect(wasInvoked).to.equal(2);
|
|
});
|
|
});
|
|
});
|
|
|
|
context("Loading", () => {
|
|
it("loads a url which was passed in", (done) => {
|
|
const player = new Player("./audio/sine.wav", () => {
|
|
expect(player.loaded).to.be.true;
|
|
player.dispose();
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("loads a url using the load method", () => {
|
|
const player = new Player();
|
|
return player.load("./audio/sine.wav").then(() => {
|
|
expect(player.buffer).to.be.instanceof(ToneAudioBuffer);
|
|
});
|
|
});
|
|
|
|
it("can be created with an options object", () => {
|
|
const player = new Player({
|
|
loop: true,
|
|
url: "./audio/sine.wav",
|
|
});
|
|
player.dispose();
|
|
});
|
|
|
|
it("invokes onerror if no url", (done) => {
|
|
const source = new Player({
|
|
url: "./nosuchfile.wav",
|
|
onerror(e) {
|
|
expect(e).to.be.instanceOf(Error);
|
|
source.dispose();
|
|
done();
|
|
},
|
|
});
|
|
});
|
|
|
|
it("can autostart after loading", (done) => {
|
|
const player = new Player({
|
|
autostart: true,
|
|
onload(): void {
|
|
setTimeout(() => {
|
|
expect(player.state).to.be.equal("started");
|
|
done();
|
|
}, 10);
|
|
},
|
|
url: "./audio/sine.wav",
|
|
});
|
|
});
|
|
});
|
|
|
|
context("Reverse", () => {
|
|
it("can get/set reverse", () => {
|
|
const player = new Player();
|
|
player.reverse = true;
|
|
expect(player.reverse).to.equal(true);
|
|
player.dispose();
|
|
});
|
|
|
|
it("can be played in reverse", () => {
|
|
const shorterBuffer = buffer.slice(0, buffer.duration / 2);
|
|
const audioBuffer = (
|
|
shorterBuffer.get() as AudioBuffer
|
|
).getChannelData(0);
|
|
const lastSample = audioBuffer[audioBuffer.length - 1];
|
|
expect(lastSample).to.not.equal(0);
|
|
return Offline(() => {
|
|
const player = new Player({
|
|
reverse: true,
|
|
url: shorterBuffer.get(),
|
|
}).toDestination();
|
|
player.start(0);
|
|
}).then((buff) => {
|
|
const firstSample = buff.toArray()[0][0];
|
|
expect(firstSample).to.equal(lastSample);
|
|
});
|
|
});
|
|
});
|
|
|
|
context("Looping", () => {
|
|
beforeEach(() => {
|
|
return buffer.load("./audio/short_sine.wav");
|
|
});
|
|
|
|
it("can be set to loop", () => {
|
|
const player = new Player();
|
|
player.loop = true;
|
|
expect(player.loop).to.be.true;
|
|
player.dispose();
|
|
});
|
|
|
|
it("can set the loop points", () => {
|
|
const player = new Player();
|
|
player.loopStart = 0.4;
|
|
expect(player.loopStart).to.equal(0.4);
|
|
player.loopEnd = 0.5;
|
|
expect(player.loopEnd).to.equal(0.5);
|
|
player.setLoopPoints(0, 0.2);
|
|
expect(player.loopStart).to.equal(0);
|
|
expect(player.loopEnd).to.equal(0.2);
|
|
player.dispose();
|
|
});
|
|
|
|
it("loops the audio", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.loop = true;
|
|
player.toDestination();
|
|
player.start(0);
|
|
}, buffer.duration * 1.5).then((buff) => {
|
|
expect(buff.getRmsAtTime(0)).to.be.above(0);
|
|
expect(buff.getRmsAtTime(buffer.duration * 0.5)).to.be.above(0);
|
|
expect(buff.getRmsAtTime(buffer.duration)).to.be.above(0);
|
|
expect(buff.getRmsAtTime(buffer.duration * 1.2)).to.be.above(0);
|
|
});
|
|
});
|
|
|
|
it("setting the loop multiple times has no affect", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.loop = true;
|
|
player.loop = true;
|
|
player.toDestination();
|
|
player.start(0);
|
|
}, buffer.duration * 1.5).then((buff) => {
|
|
expect(buff.getRmsAtTime(0)).to.be.above(0);
|
|
expect(buff.getRmsAtTime(buffer.duration * 0.5)).to.be.above(0);
|
|
expect(buff.getRmsAtTime(buffer.duration)).to.be.above(0);
|
|
expect(buff.getRmsAtTime(buffer.duration * 1.2)).to.be.above(0);
|
|
});
|
|
});
|
|
|
|
it("loops the audio when loop is set after start", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.toDestination();
|
|
player.start(0);
|
|
player.loop = true;
|
|
}, buffer.duration * 1.5).then((buff) => {
|
|
expect(buff.getRmsAtTime(0)).to.be.above(0);
|
|
expect(buff.getRmsAtTime(buffer.duration * 0.5)).to.be.above(0);
|
|
expect(buff.getRmsAtTime(buffer.duration)).to.be.above(0);
|
|
expect(buff.getRmsAtTime(buffer.duration * 1.2)).to.be.above(0);
|
|
});
|
|
});
|
|
|
|
it("offset is the loopStart when set to loop", () => {
|
|
const testSample =
|
|
buffer.toArray(0)[Math.floor(0.1 * getContext().sampleRate)];
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.loopStart = 0.1;
|
|
player.loop = true;
|
|
player.toDestination();
|
|
player.start(0);
|
|
}, 0.05).then((buff) => {
|
|
expect(buff.toArray()[0][0]).to.equal(testSample);
|
|
});
|
|
});
|
|
|
|
it("loops the audio for the specific duration", () => {
|
|
const playDur = buffer.duration * 1.5;
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.loop = true;
|
|
player.toDestination();
|
|
player.start(0, 0, playDur);
|
|
}, buffer.duration * 2).then((buff) => {
|
|
for (let time = 0; time < buffer.duration * 2; time += 0.1) {
|
|
const val = buff.getRmsAtTime(time);
|
|
if (time < playDur - 0.01) {
|
|
expect(val).to.be.greaterThan(0);
|
|
} else if (time > playDur) {
|
|
expect(val).to.equal(0);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
it("correctly compensates if the offset is greater than the loopEnd", () => {
|
|
return Offline(() => {
|
|
// make a ramp between 0-1
|
|
const ramp = new Float32Array(
|
|
Math.floor(getContext().sampleRate * 0.3)
|
|
);
|
|
for (let i = 0; i < ramp.length; i++) {
|
|
ramp[i] = (i / ramp.length) * 0.3;
|
|
}
|
|
const buff = ToneAudioBuffer.fromArray(ramp);
|
|
const player = new Player(buff).toDestination();
|
|
player.loopStart = 0.1;
|
|
player.loopEnd = 0.2;
|
|
player.loop = true;
|
|
player.start(0, 0.35);
|
|
}, 0.05).then((buff) => {
|
|
buff.forEach((sample, time) => {
|
|
if (time < 0.04) {
|
|
expect(sample).to.be.within(0.15, 0.2);
|
|
} else if (time > 0.05 && time < 0.09) {
|
|
expect(sample).to.be.within(0.1, 0.15);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
context("PlaybackRate", () => {
|
|
it("reports itself as completed after the stop time when playbackRate = 1", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.start(0);
|
|
return atTime(buffer.duration + 0.1, () => {
|
|
expect(player.state).to.equal("stopped");
|
|
});
|
|
}, buffer.duration * 1.1);
|
|
});
|
|
|
|
it("no longer reports itself as stopped when playback rate is changed to < 1", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.start(0);
|
|
player.playbackRate = 0.5;
|
|
return atTime(buffer.duration + 0.1, () => {
|
|
expect(player.state).to.equal("started");
|
|
});
|
|
}, buffer.duration * 1.1);
|
|
});
|
|
|
|
it("when end is explicitly scheduled, it does not matter if playbackRate is changed", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.start(0).stop(0.1);
|
|
player.playbackRate = 0.5;
|
|
return atTime(0.11, () => {
|
|
expect(player.state).to.equal("stopped");
|
|
});
|
|
}, buffer.duration);
|
|
});
|
|
});
|
|
|
|
context("Get/Set", () => {
|
|
it("can be set with an options object", () => {
|
|
const player = new Player();
|
|
expect(player.loop).to.be.false;
|
|
player.set({
|
|
fadeIn: 0.1,
|
|
fadeOut: 0.2,
|
|
loop: true,
|
|
loopStart: 0.4,
|
|
});
|
|
expect(player.loop).to.be.true;
|
|
expect(player.loopStart).to.equal(0.4);
|
|
expect(player.fadeIn).to.equal(0.1);
|
|
expect(player.fadeOut).to.equal(0.2);
|
|
player.dispose();
|
|
});
|
|
|
|
it("can set attributes after player is started", () => {
|
|
const player = new Player(buffer);
|
|
expect(player.loop).to.be.false;
|
|
player.start();
|
|
player.set({
|
|
loop: true,
|
|
loopEnd: 0.3,
|
|
loopStart: 0.2,
|
|
playbackRate: 0.9,
|
|
});
|
|
expect(player.loop).to.be.true;
|
|
expect(player.loopStart).to.equal(0.2);
|
|
expect(player.loopEnd).to.equal(0.3);
|
|
expect(player.playbackRate).to.equal(0.9);
|
|
player.dispose();
|
|
});
|
|
|
|
it("can get an options object", () => {
|
|
const player = new Player({
|
|
loop: true,
|
|
loopEnd: 0.3,
|
|
loopStart: 0.2,
|
|
reverse: true,
|
|
url: "./audio/sine.wav",
|
|
});
|
|
expect(player.get().loopStart).to.equal(0.2);
|
|
expect(player.get().loopEnd).to.equal(0.3);
|
|
expect(player.get().loop).to.be.true;
|
|
expect(player.get().reverse).to.be.true;
|
|
player.dispose();
|
|
});
|
|
|
|
it("can get/set the playbackRate", () => {
|
|
const player = new Player();
|
|
player.playbackRate = 0.5;
|
|
expect(player.playbackRate).to.equal(0.5);
|
|
player.dispose();
|
|
});
|
|
});
|
|
|
|
context("Start Scheduling", () => {
|
|
it("can be start with an offset", () => {
|
|
const testSample =
|
|
buffer.toArray(0)[Math.floor(0.1 * getContext().sampleRate)];
|
|
return Offline(() => {
|
|
const player = new Player(buffer.get());
|
|
player.toDestination();
|
|
player.start(0, 0.1);
|
|
}).then((buff) => {
|
|
expect(buff.toArray()[0][0]).to.equal(testSample);
|
|
});
|
|
});
|
|
|
|
it("is stopped and restarted when start is called twice", () => {
|
|
return Offline(() => {
|
|
// make a ramp between 0-1
|
|
const ramp = new Float32Array(
|
|
Math.floor(getContext().sampleRate * 0.3)
|
|
);
|
|
for (let i = 0; i < ramp.length; i++) {
|
|
ramp[i] = i / (ramp.length - 1);
|
|
}
|
|
const buff = new ToneAudioBuffer().fromArray(ramp);
|
|
const player = new Player(buff).toDestination();
|
|
player.start(0);
|
|
player.start(0.1);
|
|
}, 0.31).then((buff) => {
|
|
expect(buff.max()).to.be.lessThan(1);
|
|
});
|
|
});
|
|
|
|
it("only seeks if player is started", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer).toDestination();
|
|
player.seek(0.2, 0.01);
|
|
}, 0.05).then((buff) => {
|
|
expect(buff.isSilent()).to.be.true;
|
|
});
|
|
});
|
|
|
|
it("can seek to a position at the given time", () => {
|
|
return Offline(() => {
|
|
const ramp = new Float32Array(
|
|
Math.floor(getContext().sampleRate * 0.3)
|
|
);
|
|
for (let i = 0; i < ramp.length; i++) {
|
|
ramp[i] = (i / ramp.length) * 0.3;
|
|
}
|
|
const buff = new ToneAudioBuffer().fromArray(ramp);
|
|
const player = new Player(buff).toDestination();
|
|
player.start(0);
|
|
player.seek(0.2, 0.1);
|
|
}, 0.3).then((buff) => {
|
|
buff.forEach((sample, time) => {
|
|
if (time < 0.09) {
|
|
expect(sample).to.be.within(0, 0.1);
|
|
} else if (time > 0.1 && time < 0.19) {
|
|
expect(sample).to.be.within(0.2, 0.3);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
it("can be play for a specific duration", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.toDestination();
|
|
player.start(0).stop(0.1);
|
|
return (time) => {
|
|
whenBetween(time, 0.1, Infinity, () => {
|
|
expect(player.state).to.equal("stopped");
|
|
});
|
|
whenBetween(time, 0, 0.1, () => {
|
|
expect(player.state).to.equal("started");
|
|
});
|
|
};
|
|
}, 0.3).then((buff) => {
|
|
buff.forEachBetween(
|
|
(sample) => {
|
|
expect(sample).to.equal(0);
|
|
},
|
|
0.11,
|
|
0.15
|
|
);
|
|
});
|
|
});
|
|
|
|
it("stops playing if invoked with 'stop' at a sooner time", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.toDestination();
|
|
player.start(0).stop(0.1).stop(0.05);
|
|
}, 0.3).then((buff) => {
|
|
expect(buff.getTimeOfLastSound()).to.be.closeTo(0.05, 0.02);
|
|
});
|
|
});
|
|
|
|
it("stops playing if at the last scheduled 'stop' time", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.toDestination();
|
|
player
|
|
.start(0, 0, 0.05)
|
|
.start(0.1, 0, 0.05)
|
|
.start(0.2, 0, 0.05);
|
|
player.stop(0.1);
|
|
}, 0.3).then((buff) => {
|
|
expect(buff.getTimeOfLastSound()).to.be.closeTo(0.1, 0.02);
|
|
});
|
|
});
|
|
|
|
it("can retrigger multiple sources which all stop at the stop time", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.toDestination();
|
|
player.loop = true;
|
|
player.start(0).start(0.1).start(0.2).stop(0.25);
|
|
}, 0.4).then((buff) => {
|
|
expect(buff.getTimeOfLastSound()).to.be.closeTo(0.25, 0.02);
|
|
});
|
|
});
|
|
|
|
it("can be play for a specific duration passed in the 'start' method", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer);
|
|
player.toDestination();
|
|
player.start(0, 0, 0.1);
|
|
return (time) => {
|
|
whenBetween(time, 0.1, Infinity, () => {
|
|
expect(player.state).to.equal("stopped");
|
|
});
|
|
whenBetween(time, 0, 0.1, () => {
|
|
expect(player.state).to.equal("started");
|
|
});
|
|
};
|
|
}, 0.3).then((buff) => {
|
|
expect(buff.getTimeOfLastSound()).to.be.closeTo(0.1, 0.02);
|
|
});
|
|
});
|
|
|
|
it("reports itself as stopped after a single iterations of the buffer", () => {
|
|
return Offline(() => {
|
|
const player = new Player(buffer).toDestination();
|
|
player.start();
|
|
|
|
return (time) => {
|
|
whenBetween(time, buffer.duration, Infinity, () => {
|
|
expect(player.state).to.equal("stopped");
|
|
});
|
|
whenBetween(time, 0, buffer.duration, () => {
|
|
expect(player.state).to.equal("started");
|
|
});
|
|
};
|
|
}, buffer.duration * 1.1);
|
|
});
|
|
|
|
it("plays synced to the Transport", () => {
|
|
return Offline(({ transport }) => {
|
|
const player = new Player(buffer)
|
|
.sync()
|
|
.start(0)
|
|
.toDestination();
|
|
transport.start(0);
|
|
}, 0.05).then((buff) => {
|
|
expect(buff.isSilent()).to.be.false;
|
|
});
|
|
});
|
|
|
|
it("does not play twice when the offset is very small", () => {
|
|
// addresses #999 and #944
|
|
return CompareToFile(
|
|
() => {
|
|
const player = new Player(buffer).toDestination();
|
|
player.sync().start(0);
|
|
getContext().transport.bpm.value = 125;
|
|
getContext().transport.setLoopPoints(0, "1:0:0");
|
|
getContext().transport.loop = true;
|
|
getContext().transport.start(0);
|
|
},
|
|
"playerSyncLoop.wav",
|
|
0.01
|
|
);
|
|
});
|
|
|
|
it("offsets correctly when started by the Transport", () => {
|
|
const testSample =
|
|
buffer.toArray(0)[
|
|
Math.floor(0.13125 * getContext().sampleRate)
|
|
];
|
|
return Offline(({ transport }) => {
|
|
const player = new Player(buffer)
|
|
.sync()
|
|
.start(0, 0.1)
|
|
.toDestination();
|
|
transport.start(0, 0.03125);
|
|
}, 0.05).then((buff) => {
|
|
expect(buff.toArray()[0][0]).to.equal(testSample);
|
|
});
|
|
});
|
|
|
|
it("starts at the correct position when Transport is offset and playbackRate is not 1", () => {
|
|
return Offline(({ transport }) => {
|
|
// make a ramp between 0-1
|
|
const ramp = new Float32Array(
|
|
Math.floor(getContext().sampleRate * 0.3)
|
|
);
|
|
for (let i = 0; i < ramp.length; i++) {
|
|
ramp[i] = i / ramp.length;
|
|
}
|
|
const buff = ToneAudioBuffer.fromArray(ramp);
|
|
const player = new Player(buff).toDestination();
|
|
player.playbackRate = 0.5;
|
|
player.sync().start(0);
|
|
// start halfway through
|
|
transport.start(0, 0.15);
|
|
}, 0.05).then((buff) => {
|
|
expect(buff.getValueAtTime(0)).to.be.closeTo(0.5, 0.05);
|
|
});
|
|
});
|
|
|
|
it("starts with an offset when synced and started after Transport is running", () => {
|
|
return Offline(({ transport }) => {
|
|
const ramp = new Float32Array(
|
|
Math.floor(getContext().sampleRate * 0.3)
|
|
);
|
|
for (let i = 0; i < ramp.length; i++) {
|
|
ramp[i] = (i / ramp.length) * 0.3;
|
|
}
|
|
const buff = new ToneAudioBuffer().fromArray(ramp);
|
|
const player = new Player(buff).toDestination();
|
|
transport.start(0);
|
|
return atTime(0.1, () => {
|
|
player.sync().start(0);
|
|
});
|
|
}, 0.3).then((buff) => {
|
|
expect(buff.getValueAtTime(0)).to.equal(0);
|
|
expect(buff.getValueAtTime(0.05)).to.equal(0);
|
|
expect(buff.getValueAtTime(0.11)).to.be.closeTo(0.11, 0.01);
|
|
expect(buff.getValueAtTime(0.2)).to.be.closeTo(0.2, 0.01);
|
|
});
|
|
});
|
|
|
|
it("can pass in an offset when synced and started after Transport is running", () => {
|
|
return Offline(({ transport }) => {
|
|
const ramp = new Float32Array(
|
|
Math.floor(getContext().sampleRate * 0.3)
|
|
);
|
|
for (let i = 0; i < ramp.length; i++) {
|
|
ramp[i] = (i / ramp.length) * 0.3;
|
|
}
|
|
const buff = new ToneAudioBuffer().fromArray(ramp);
|
|
const player = new Player(buff).toDestination();
|
|
player.loop = true;
|
|
transport.start(0);
|
|
return atTime(0.1, () => {
|
|
player.sync().start(0, 0.1);
|
|
});
|
|
}, 0.3).then((buff) => {
|
|
expect(buff.getValueAtTime(0)).to.equal(0);
|
|
expect(buff.getValueAtTime(0.05)).to.equal(0);
|
|
expect(buff.getValueAtTime(0.11)).to.be.closeTo(0.21, 0.01);
|
|
expect(buff.getValueAtTime(0.15)).to.be.closeTo(0.25, 0.01);
|
|
expect(buff.getValueAtTime(0.2)).to.be.closeTo(0.0, 0.01);
|
|
expect(buff.getValueAtTime(0.25)).to.be.closeTo(0.05, 0.01);
|
|
});
|
|
});
|
|
|
|
it("fades in and out correctly", () => {
|
|
let duration = 0.5;
|
|
return Offline(() => {
|
|
const onesArray = new Float32Array(
|
|
getContext().sampleRate * duration
|
|
);
|
|
onesArray.forEach((sample, index) => {
|
|
onesArray[index] = 1;
|
|
});
|
|
const onesBuffer = ToneAudioBuffer.fromArray(onesArray);
|
|
const player = new Player({
|
|
url: onesBuffer,
|
|
fadeOut: 0.1,
|
|
fadeIn: 0.1,
|
|
}).toDestination();
|
|
player.start(0);
|
|
}, 0.6).then((buff) => {
|
|
expect(buff.getRmsAtTime(0)).to.be.closeTo(0, 0.1);
|
|
expect(buff.getRmsAtTime(0.05)).to.be.closeTo(0.5, 0.1);
|
|
expect(buff.getRmsAtTime(0.1)).to.be.closeTo(1, 0.1);
|
|
duration -= 0.1;
|
|
expect(buff.getRmsAtTime(duration)).to.be.closeTo(1, 0.1);
|
|
expect(buff.getRmsAtTime(duration + 0.05)).to.be.closeTo(
|
|
0.5,
|
|
0.1
|
|
);
|
|
expect(buff.getRmsAtTime(duration + 0.1)).to.be.closeTo(0, 0.1);
|
|
});
|
|
});
|
|
|
|
it("stops only last activeSource when restarting at intervals < latencyHint", (done) => {
|
|
const originalLookAhead = getContext().lookAhead;
|
|
getContext().lookAhead = .3;
|
|
const player = new Player({
|
|
onload(): void {
|
|
player.start(undefined, undefined, 1);
|
|
setTimeout(() => player.restart(undefined, undefined, 1), 50);
|
|
setTimeout(() => player.restart(undefined, undefined, 1), 100);
|
|
setTimeout(() => player.restart(undefined, undefined, 1), 150);
|
|
setTimeout(() => {
|
|
player.restart(undefined, undefined, 1);
|
|
const checkStopTimes = new Set();
|
|
player["_activeSources"].forEach(source => {
|
|
checkStopTimes.add(source["_stopTime"]);
|
|
});
|
|
getContext().lookAhead = originalLookAhead;
|
|
// ensure each source has a different stopTime
|
|
expect(checkStopTimes.size).to.equal(player["_activeSources"].size);
|
|
done();
|
|
}, 250);
|
|
},
|
|
url: "./audio/sine.wav",
|
|
});
|
|
});
|
|
});
|
|
});
|