Skip to content

Commit

Permalink
feat: OfflineContext yields thread every second of audio rendered
Browse files Browse the repository at this point in the history
so that it doesn't block the thread.

fixes #436
  • Loading branch information
tambien committed Oct 3, 2019
1 parent 92816c3 commit 1154470
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 5 deletions.
18 changes: 18 additions & 0 deletions Tone/core/context/OfflineContext.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,22 @@ context("OfflineContext", () => {
}
});
});

it("can render audio not async", () => {
const ctx = new OfflineContext(1, 0.2, 44100);
const osc = ctx.createOscillator();
osc.connect(ctx.rawContext.destination);
osc.start(0.1);
return ctx.render(false).then(buffer => {
expect(buffer).to.have.property("length");
expect(buffer).to.have.property("sampleRate");
const array = buffer.getChannelData(0);
for (let i = 0; i < array.length; i++) {
if (array[i] !== 0) {
expect(i / array.length).to.be.closeTo(0.5, 0.01);
break;
}
}
});
});
});
21 changes: 16 additions & 5 deletions Tone/core/context/OfflineContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,23 +66,34 @@ export class OfflineContext extends Context {
}

/**
* Render just the clock portion of the audiocontext.
* Render just the clock portion of the audio context.
*/
private _renderClock(): void {
private async _renderClock(asynchronous: boolean): Promise<void> {
let index = 0;
while (this._duration - this._currentTime >= 0) {

// invoke all the callbacks on that time
this.emit("tick");
// increment the clock in 5ms chunks

// increment the clock in block-sized chunks
this._currentTime += 128 / this.sampleRate;

// yield once a second of audio
index++;
const yieldEvery = Math.floor(this.sampleRate / 128);
if (asynchronous && index % yieldEvery === 0) {
await new Promise(done => setTimeout(done, 1));
}
}
}

/**
* Render the output of the OfflineContext
* @param async If the clock should be rendered asynchronously, which will not block the main thread, but be slightly slower.
*/
async render(): Promise<AudioBuffer> {
async render(asynchronous: boolean = true): Promise<AudioBuffer> {
await this.workletsAreReady();
this._renderClock();
await this._renderClock(asynchronous);
return this._context.startRendering();
}

Expand Down

0 comments on commit 1154470

Please sign in to comment.