Tone.js/Tone/core/context/OfflineContext.ts

119 lines
3.1 KiB
TypeScript
Raw Normal View History

2024-05-03 14:10:40 +00:00
import { createOfflineAudioContext } from "../context/AudioContext.js";
import { Context } from "../context/Context.js";
import { Seconds } from "../type/Units.js";
import { isOfflineAudioContext } from "../util/AdvancedTypeCheck.js";
import { ToneAudioBuffer } from "./ToneAudioBuffer.js";
2019-05-23 18:00:49 +00:00
/**
2019-08-30 16:02:18 +00:00
* Wrapper around the OfflineAudioContext
2019-08-26 17:44:43 +00:00
* @category Core
2020-04-29 22:12:52 +00:00
* @example
* // generate a single channel, 0.5 second buffer
2020-07-19 00:47:07 +00:00
* const context = new Tone.OfflineContext(1, 0.5, 44100);
2020-04-29 22:12:52 +00:00
* const osc = new Tone.Oscillator({ context });
* context.render().then(buffer => {
* console.log(buffer.numberOfChannels, buffer.duration);
* });
2019-05-23 18:00:49 +00:00
*/
export class OfflineContext extends Context {
2019-09-04 23:18:44 +00:00
readonly name: string = "OfflineContext";
2019-05-23 18:00:49 +00:00
/**
2019-09-14 20:39:18 +00:00
* A private reference to the duration
2019-05-23 18:00:49 +00:00
*/
private readonly _duration: Seconds;
/**
2019-09-14 20:39:18 +00:00
* An artificial clock source
2019-05-23 18:00:49 +00:00
*/
private _currentTime: Seconds = 0;
/**
* Private reference to the OfflineAudioContext.
*/
protected _context!: OfflineAudioContext;
readonly isOffline: boolean = true;
2019-08-26 17:44:43 +00:00
/**
* @param channels The number of channels to render
* @param duration The duration to render in seconds
* @param sampleRate the sample rate to render at
2019-08-26 17:44:43 +00:00
*/
2024-05-03 15:09:28 +00:00
constructor(channels: number, duration: Seconds, sampleRate: number);
2019-08-30 16:02:18 +00:00
constructor(context: OfflineAudioContext);
2019-08-19 16:59:07 +00:00
constructor() {
2019-05-23 18:00:49 +00:00
super({
clockSource: "offline",
2024-05-03 15:09:28 +00:00
context: isOfflineAudioContext(arguments[0])
? arguments[0]
: createOfflineAudioContext(
arguments[0],
arguments[1] * arguments[2],
arguments[2]
),
2019-05-23 18:00:49 +00:00
lookAhead: 0,
2024-05-03 15:09:28 +00:00
updateInterval: isOfflineAudioContext(arguments[0])
? 128 / arguments[0].sampleRate
: 128 / arguments[2],
2019-05-23 18:00:49 +00:00
});
2024-05-03 15:09:28 +00:00
this._duration = isOfflineAudioContext(arguments[0])
? arguments[0].length / arguments[0].sampleRate
: arguments[1];
2019-05-23 18:00:49 +00:00
}
/**
2019-09-14 20:39:18 +00:00
* Override the now method to point to the internal clock time
2019-05-23 18:00:49 +00:00
*/
now(): Seconds {
return this._currentTime;
}
/**
* Same as this.now()
*/
get currentTime(): Seconds {
return this._currentTime;
}
/**
* Render just the clock portion of the audio context.
2019-05-23 18:00:49 +00:00
*/
private async _renderClock(asynchronous: boolean): Promise<void> {
let index = 0;
2019-05-23 18:00:49 +00:00
while (this._duration - this._currentTime >= 0) {
// invoke all the callbacks on that time
this.emit("tick");
2020-04-29 22:12:52 +00:00
// increment the clock in block-sized chunks
2019-08-19 17:01:37 +00:00
this._currentTime += 128 / this.sampleRate;
2020-04-29 22:12:52 +00:00
// yield once a second of audio
index++;
const yieldEvery = Math.floor(this.sampleRate / 128);
if (asynchronous && index % yieldEvery === 0) {
2024-05-03 15:09:28 +00:00
await new Promise((done) => setTimeout(done, 1));
}
2019-05-23 18:00:49 +00:00
}
}
2019-05-23 18:00:49 +00:00
/**
* Render the output of the OfflineContext
2019-10-23 03:04:52 +00:00
* @param asynchronous If the clock should be rendered asynchronously, which will not block the main thread, but be slightly slower.
*/
2019-11-17 18:09:19 +00:00
async render(asynchronous = true): Promise<ToneAudioBuffer> {
await this.workletsAreReady();
await this._renderClock(asynchronous);
const buffer = await this._context.startRendering();
return new ToneAudioBuffer(buffer);
2019-05-23 18:00:49 +00:00
}
/**
2019-09-14 20:39:18 +00:00
* Close the context
2019-05-23 18:00:49 +00:00
*/
close(): Promise<void> {
return Promise.resolve();
2019-05-23 18:00:49 +00:00
}
}