recorder-audio-worklet-processor
Version:
The AudioWorkletProcessor which is used by the recorder-audio-worklet package.
125 lines (101 loc) • 4.25 kB
text/typescript
import { IAudioWorkletProcessor, IPauseMessageEvent, IRecordMessageEvent, IResumeMessageEvent, IStopMessageEvent } from './interfaces';
export class RecorderAudioWorkletProcessor extends AudioWorkletProcessor implements IAudioWorkletProcessor {
public static parameterDescriptors = [];
private _encoderPort: null | MessagePort;
private _numberOfChannels: number;
private _state: 'active' | 'inactive' | 'paused' | 'recording' | 'stopped';
constructor() {
super();
this._encoderPort = null;
this._numberOfChannels = 0;
this._state = 'inactive';
this.port.onmessage = ({ data }: IPauseMessageEvent | IRecordMessageEvent | IResumeMessageEvent | IStopMessageEvent) => {
if (data.method === 'pause') {
if (this._state === 'active' || this._state === 'recording') {
this._state = 'paused';
this._sendAcknowledgement(data.id);
} else {
this._sendUnexpectedStateError(data.id);
}
} else if (data.method === 'record') {
if (this._state === 'inactive') {
this._encoderPort = data.params.encoderPort;
this._state = 'active';
this._sendAcknowledgement(data.id);
} else {
this._sendUnexpectedStateError(data.id);
}
} else if (data.method === 'resume') {
if (this._state === 'paused') {
this._state = 'active';
this._sendAcknowledgement(data.id);
} else {
this._sendUnexpectedStateError(data.id);
}
} else if (data.method === 'stop') {
if ((this._state === 'active' || this._state === 'paused' || this._state === 'recording') && this._encoderPort !== null) {
this._stop(this._encoderPort);
this._sendAcknowledgement(data.id);
} else {
this._sendUnexpectedStateError(data.id);
}
} else if (typeof (<MessageEvent['data']>data).id === 'number') {
this.port.postMessage({
error: {
code: -32601,
message: 'The requested method is not supported.'
},
id: <number>(<MessageEvent['data']>data).id
});
}
};
}
public process([input]: Float32Array[][]): boolean {
if (this._state === 'inactive' || this._state === 'paused') {
return true;
}
if (this._state === 'active') {
if (input === undefined) {
throw new Error('No channelData was received for the first input.');
}
if (input.length === 0) {
return true;
}
this._state = 'recording';
}
if (this._state === 'recording' && this._encoderPort !== null) {
if (input === undefined) {
throw new Error('No channelData was received for the first input.');
}
if (input.length === 0) {
this._encoderPort.postMessage(Array.from({ length: this._numberOfChannels }, () => 128));
} else {
this._encoderPort.postMessage(
input,
input.map(({ buffer }) => buffer)
);
this._numberOfChannels = input.length;
}
return true;
}
return false;
}
private _sendAcknowledgement(id: number): void {
this.port.postMessage({ id, result: null });
}
private _sendUnexpectedStateError(id: number): void {
this.port.postMessage({
error: {
code: -32603,
message: 'The internal state does not allow to process the given message.'
},
id
});
}
private _stop(encoderPort: MessagePort): void {
encoderPort.postMessage([]);
encoderPort.close();
this._encoderPort = null;
this._state = 'stopped';
}
}