Skip to content

Commit 1154470

Browse files
committed
feat: OfflineContext yields thread every second of audio rendered
so that it doesn't block the thread. fixes #436
1 parent 92816c3 commit 1154470

File tree

2 files changed

+34
-5
lines changed

2 files changed

+34
-5
lines changed

Tone/core/context/OfflineContext.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,22 @@ context("OfflineContext", () => {
3838
}
3939
});
4040
});
41+
42+
it("can render audio not async", () => {
43+
const ctx = new OfflineContext(1, 0.2, 44100);
44+
const osc = ctx.createOscillator();
45+
osc.connect(ctx.rawContext.destination);
46+
osc.start(0.1);
47+
return ctx.render(false).then(buffer => {
48+
expect(buffer).to.have.property("length");
49+
expect(buffer).to.have.property("sampleRate");
50+
const array = buffer.getChannelData(0);
51+
for (let i = 0; i < array.length; i++) {
52+
if (array[i] !== 0) {
53+
expect(i / array.length).to.be.closeTo(0.5, 0.01);
54+
break;
55+
}
56+
}
57+
});
58+
});
4159
});

Tone/core/context/OfflineContext.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,34 @@ export class OfflineContext extends Context {
6666
}
6767

6868
/**
69-
* Render just the clock portion of the audiocontext.
69+
* Render just the clock portion of the audio context.
7070
*/
71-
private _renderClock(): void {
71+
private async _renderClock(asynchronous: boolean): Promise<void> {
72+
let index = 0;
7273
while (this._duration - this._currentTime >= 0) {
74+
7375
// invoke all the callbacks on that time
7476
this.emit("tick");
75-
// increment the clock in 5ms chunks
77+
78+
// increment the clock in block-sized chunks
7679
this._currentTime += 128 / this.sampleRate;
80+
81+
// yield once a second of audio
82+
index++;
83+
const yieldEvery = Math.floor(this.sampleRate / 128);
84+
if (asynchronous && index % yieldEvery === 0) {
85+
await new Promise(done => setTimeout(done, 1));
86+
}
7787
}
7888
}
7989

8090
/**
8191
* Render the output of the OfflineContext
92+
* @param async If the clock should be rendered asynchronously, which will not block the main thread, but be slightly slower.
8293
*/
83-
async render(): Promise<AudioBuffer> {
94+
async render(asynchronous: boolean = true): Promise<AudioBuffer> {
8495
await this.workletsAreReady();
85-
this._renderClock();
96+
await this._renderClock(asynchronous);
8697
return this._context.startRendering();
8798
}
8899

0 commit comments

Comments
 (0)