voice-changer/client/lib/worklet/src/voice-changer-worklet-processor.ts

192 lines
6.6 KiB
TypeScript
Raw Normal View History

2023-01-11 21:49:22 +03:00
export const RequestType = {
2023-09-25 07:25:07 +03:00
voice: "voice",
config: "config",
start: "start",
stop: "stop",
trancateBuffer: "trancateBuffer",
} as const;
export type RequestType = (typeof RequestType)[keyof typeof RequestType];
export const ResponseType = {
2023-09-25 07:25:07 +03:00
volume: "volume",
inputData: "inputData",
start_ok: "start_ok",
stop_ok: "stop_ok",
} as const;
export type ResponseType = (typeof ResponseType)[keyof typeof ResponseType];
2023-01-11 21:49:22 +03:00
export type VoiceChangerWorkletProcessorRequest = {
2023-09-25 07:25:07 +03:00
requestType: RequestType;
voice: Float32Array;
numTrancateTreshold: number;
volTrancateThreshold: number;
volTrancateLength: number;
};
2023-01-04 20:28:36 +03:00
export type VoiceChangerWorkletProcessorResponse = {
2023-09-25 07:25:07 +03:00
responseType: ResponseType;
volume?: number;
recordData?: Float32Array[];
inputData?: Float32Array;
};
2023-01-04 20:28:36 +03:00
class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
2023-09-25 07:25:07 +03:00
private BLOCK_SIZE = 128;
2023-01-04 20:28:36 +03:00
private initialized = false;
2023-09-25 07:25:07 +03:00
private volume = 0;
2023-11-03 19:41:51 +03:00
// private numTrancateTreshold = 100;
2023-03-24 10:18:05 +03:00
// private volTrancateThreshold = 0.0005
// private volTrancateLength = 32
// private volTrancateCount = 0
2023-01-11 21:49:22 +03:00
2023-09-25 07:25:07 +03:00
private isRecording = false;
2023-09-25 07:25:07 +03:00
playBuffer: Float32Array[] = [];
2023-10-09 06:15:03 +03:00
unpushedF32Data: Float32Array = new Float32Array(0);
2023-01-04 20:28:36 +03:00
/**
* @constructor
*/
constructor() {
super();
this.initialized = true;
this.port.onmessage = this.handleMessage.bind(this);
}
2023-01-11 21:49:22 +03:00
calcVol = (data: Float32Array, prevVol: number) => {
const sum = data.reduce((prev, cur) => {
2023-09-25 07:25:07 +03:00
return prev + cur * cur;
}, 0);
const rms = Math.sqrt(sum / data.length);
return Math.max(rms, prevVol * 0.95);
};
2023-01-11 21:49:22 +03:00
2023-02-28 05:54:40 +03:00
trancateBuffer = () => {
2023-09-25 07:25:07 +03:00
console.log("[worklet] Buffer truncated");
2023-02-28 05:54:40 +03:00
while (this.playBuffer.length > 2) {
2023-09-25 07:25:07 +03:00
this.playBuffer.shift();
2023-02-28 05:54:40 +03:00
}
2023-09-25 07:25:07 +03:00
};
2023-01-04 20:28:36 +03:00
handleMessage(event: any) {
2023-09-25 07:25:07 +03:00
const request = event.data as VoiceChangerWorkletProcessorRequest;
2023-01-11 21:49:22 +03:00
if (request.requestType === "config") {
2023-11-03 19:41:51 +03:00
// this.numTrancateTreshold = request.numTrancateTreshold;
2023-03-24 10:18:05 +03:00
// this.volTrancateLength = request.volTrancateLength
// this.volTrancateThreshold = request.volTrancateThreshold
2023-09-25 07:25:07 +03:00
console.log("[worklet] worklet configured", request);
return;
2023-02-19 22:49:34 +03:00
} else if (request.requestType === "start") {
if (this.isRecording) {
2023-09-25 07:25:07 +03:00
console.warn("[worklet] recoring is already started");
return;
}
2023-09-25 07:25:07 +03:00
this.isRecording = true;
2023-04-10 21:07:14 +03:00
const startResponse: VoiceChangerWorkletProcessorResponse = {
responseType: "start_ok",
2023-09-25 07:25:07 +03:00
};
2023-04-10 21:07:14 +03:00
this.port.postMessage(startResponse);
2023-09-25 07:25:07 +03:00
return;
2023-02-19 22:49:34 +03:00
} else if (request.requestType === "stop") {
if (!this.isRecording) {
2023-09-25 07:25:07 +03:00
console.warn("[worklet] recoring is not started");
return;
}
2023-09-25 07:25:07 +03:00
this.isRecording = false;
2023-04-10 21:07:14 +03:00
const stopResponse: VoiceChangerWorkletProcessorResponse = {
responseType: "stop_ok",
2023-09-25 07:25:07 +03:00
};
2023-04-10 21:07:14 +03:00
this.port.postMessage(stopResponse);
2023-09-25 07:25:07 +03:00
return;
2023-02-28 05:54:40 +03:00
} else if (request.requestType === "trancateBuffer") {
2023-09-25 07:25:07 +03:00
this.trancateBuffer();
return;
2023-01-11 21:49:22 +03:00
}
2023-11-03 19:41:51 +03:00
const f32Data = request.voice;
// if (this.playBuffer.length > this.numTrancateTreshold) {
// console.log(`[worklet] Truncate ${this.playBuffer.length} > ${this.numTrancateTreshold}`);
// this.trancateBuffer();
// }
if (this.playBuffer.length > f32Data.length / this.BLOCK_SIZE) {
console.log(`[worklet] Truncate ${this.playBuffer.length} > ${f32Data.length / this.BLOCK_SIZE}`);
2023-09-25 07:25:07 +03:00
this.trancateBuffer();
2023-01-04 20:28:36 +03:00
}
2023-10-09 06:15:03 +03:00
const concatedF32Data = new Float32Array(this.unpushedF32Data.length + f32Data.length);
concatedF32Data.set(this.unpushedF32Data);
concatedF32Data.set(f32Data, this.unpushedF32Data.length);
const chunkNum = Math.floor(concatedF32Data.length / this.BLOCK_SIZE);
2023-02-19 22:49:34 +03:00
for (let i = 0; i < chunkNum; i++) {
2023-10-09 06:15:03 +03:00
const block = concatedF32Data.slice(i * this.BLOCK_SIZE, (i + 1) * this.BLOCK_SIZE);
2023-09-25 07:25:07 +03:00
this.playBuffer.push(block);
2023-01-04 20:28:36 +03:00
}
2023-10-09 06:15:03 +03:00
this.unpushedF32Data = concatedF32Data.slice(chunkNum * this.BLOCK_SIZE);
2023-01-04 20:28:36 +03:00
}
2023-02-19 19:00:28 +03:00
pushData = (inputData: Float32Array) => {
const volumeResponse: VoiceChangerWorkletProcessorResponse = {
responseType: ResponseType.inputData,
2023-09-25 07:25:07 +03:00
inputData: inputData,
};
2023-02-19 19:00:28 +03:00
this.port.postMessage(volumeResponse);
2023-09-25 07:25:07 +03:00
};
2023-02-19 19:00:28 +03:00
2023-01-04 20:28:36 +03:00
process(_inputs: Float32Array[][], outputs: Float32Array[][], _parameters: Record<string, Float32Array>) {
if (!this.initialized) {
console.warn("[worklet] worklet_process not ready");
return true;
}
2023-02-19 20:21:51 +03:00
if (this.isRecording) {
if (_inputs.length > 0 && _inputs[0].length > 0) {
2023-09-25 07:25:07 +03:00
this.pushData(_inputs[0][0]);
2023-02-19 20:21:51 +03:00
}
2023-02-19 19:00:28 +03:00
}
2023-01-04 20:28:36 +03:00
if (this.playBuffer.length === 0) {
2023-10-09 06:15:03 +03:00
// console.log("[worklet] no play buffer");
2023-09-25 07:25:07 +03:00
return true;
2023-01-04 20:28:36 +03:00
}
2023-10-09 06:15:03 +03:00
// console.log("[worklet] play buffer");
2023-01-11 21:49:22 +03:00
//// 一定期間無音状態が続いている場合はスキップ。
2023-03-24 10:18:05 +03:00
// let voice: Float32Array | undefined
// while (true) {
// voice = this.playBuffer.shift()
// if (!voice) {
// break
// }
// this.volume = this.calcVol(voice, this.volume)
// if (this.volume < this.volTrancateThreshold) {
// this.volTrancateCount += 1
// } else {
// this.volTrancateCount = 0
// }
// // V.1.5.0よりsilent skipで音飛びするようになったので無効化
// if (this.volTrancateCount < this.volTrancateLength || this.volTrancateLength < 0) {
// break
// } else {
// break
// // console.log("silent...skip")
// }
// }
2023-09-25 07:25:07 +03:00
let voice = this.playBuffer.shift();
2023-01-04 20:28:36 +03:00
2023-01-11 21:49:22 +03:00
if (voice) {
2023-09-25 07:25:07 +03:00
this.volume = this.calcVol(voice, this.volume);
const volumeResponse: VoiceChangerWorkletProcessorResponse = {
responseType: ResponseType.volume,
2023-09-25 07:25:07 +03:00
volume: this.volume,
};
this.port.postMessage(volumeResponse);
2023-09-25 07:25:07 +03:00
outputs[0][0].set(voice);
2023-02-23 20:51:52 +03:00
if (outputs[0].length == 2) {
2023-09-25 07:25:07 +03:00
outputs[0][1].set(voice);
2023-02-23 20:51:52 +03:00
}
2023-01-11 21:49:22 +03:00
}
2023-01-04 20:28:36 +03:00
return true;
}
}
registerProcessor("voice-changer-worklet-processor", VoiceChangerWorkletProcessor);