mirror of
https://github.com/w-okada/voice-changer.git
synced 2025-01-23 13:35:12 +03:00
add handling setSinkID
This commit is contained in:
parent
a35551906a
commit
1e68e01e39
@ -389,8 +389,12 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
|||||||
// Server Audio を使う場合はElementから音は出さない。
|
// Server Audio を使う場合はElementから音は出さない。
|
||||||
audio.volume = 0;
|
audio.volume = 0;
|
||||||
} else if (audioOutputForGUI == "none") {
|
} else if (audioOutputForGUI == "none") {
|
||||||
// @ts-ignore
|
try {
|
||||||
audio.setSinkId("");
|
// @ts-ignore
|
||||||
|
audio.setSinkId("");
|
||||||
|
} catch (e) {
|
||||||
|
console.error("catch:" + e);
|
||||||
|
}
|
||||||
if (x == AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) {
|
if (x == AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) {
|
||||||
audio.volume = 0;
|
audio.volume = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -404,8 +408,12 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
|||||||
return x.deviceId == audioOutputForGUI;
|
return x.deviceId == audioOutputForGUI;
|
||||||
});
|
});
|
||||||
if (found) {
|
if (found) {
|
||||||
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
try {
|
||||||
audio.setSinkId(audioOutputForGUI);
|
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||||
|
audio.setSinkId(audioOutputForGUI);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("catch:" + e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn("No audio output device. use default");
|
console.warn("No audio output device. use default");
|
||||||
}
|
}
|
||||||
@ -620,9 +628,13 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
|||||||
// Server Audio を使う場合はElementから音は出さない。
|
// Server Audio を使う場合はElementから音は出さない。
|
||||||
audio.volume = 0;
|
audio.volume = 0;
|
||||||
} else if (audioMonitorForGUI == "none") {
|
} else if (audioMonitorForGUI == "none") {
|
||||||
// @ts-ignore
|
try {
|
||||||
audio.setSinkId("");
|
// @ts-ignore
|
||||||
audio.volume = 0;
|
audio.setSinkId("");
|
||||||
|
audio.volume = 0;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("catch:" + e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const audioOutputs = mediaDeviceInfos.filter((x) => {
|
const audioOutputs = mediaDeviceInfos.filter((x) => {
|
||||||
return x.kind == "audiooutput";
|
return x.kind == "audiooutput";
|
||||||
@ -631,9 +643,13 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
|||||||
return x.deviceId == audioMonitorForGUI;
|
return x.deviceId == audioMonitorForGUI;
|
||||||
});
|
});
|
||||||
if (found) {
|
if (found) {
|
||||||
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
try {
|
||||||
audio.setSinkId(audioMonitorForGUI);
|
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||||
audio.volume = 1;
|
audio.setSinkId(audioMonitorForGUI);
|
||||||
|
audio.volume = 1;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("catch:" + e);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn("No audio output device. use default");
|
console.warn("No audio output device. use default");
|
||||||
}
|
}
|
||||||
|
@ -1,44 +1,50 @@
|
|||||||
import React, { useMemo, useState } from "react"
|
import React, { useMemo, useState } from "react";
|
||||||
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||||
import { useGuiState } from "../001_GuiStateProvider"
|
import { useGuiState } from "../001_GuiStateProvider";
|
||||||
import { AUDIO_ELEMENT_FOR_SAMPLING_INPUT, AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT } from "../../../const"
|
import { AUDIO_ELEMENT_FOR_SAMPLING_INPUT, AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT } from "../../../const";
|
||||||
|
|
||||||
export type RecorderAreaProps = {
|
export type RecorderAreaProps = {};
|
||||||
}
|
|
||||||
|
|
||||||
export const RecorderArea = (_props: RecorderAreaProps) => {
|
export const RecorderArea = (_props: RecorderAreaProps) => {
|
||||||
const { serverSetting } = useAppState()
|
const { serverSetting } = useAppState();
|
||||||
const { audioOutputForAnalyzer, setAudioOutputForAnalyzer, outputAudioDeviceInfo } = useGuiState()
|
const { audioOutputForAnalyzer, setAudioOutputForAnalyzer, outputAudioDeviceInfo } = useGuiState();
|
||||||
|
|
||||||
const [serverIORecording, setServerIORecording] = useState<boolean>(false)
|
|
||||||
|
|
||||||
|
const [serverIORecording, setServerIORecording] = useState<boolean>(false);
|
||||||
|
|
||||||
const serverIORecorderRow = useMemo(() => {
|
const serverIORecorderRow = useMemo(() => {
|
||||||
const onServerIORecordStartClicked = async () => {
|
const onServerIORecordStartClicked = async () => {
|
||||||
setServerIORecording(true)
|
setServerIORecording(true);
|
||||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 1 })
|
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 1 });
|
||||||
}
|
};
|
||||||
const onServerIORecordStopClicked = async () => {
|
const onServerIORecordStopClicked = async () => {
|
||||||
setServerIORecording(false)
|
setServerIORecording(false);
|
||||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 0 })
|
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 0 });
|
||||||
|
|
||||||
// set wav (input)
|
// set wav (input)
|
||||||
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement
|
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement;
|
||||||
wavInput.src = "/tmp/in.wav?" + new Date().getTime()
|
wavInput.src = "/tmp/in.wav?" + new Date().getTime();
|
||||||
wavInput.controls = true
|
wavInput.controls = true;
|
||||||
// @ts-ignore
|
try {
|
||||||
wavInput.setSinkId(audioOutputForAnalyzer)
|
// @ts-ignore
|
||||||
|
wavInput.setSinkId(audioOutputForAnalyzer);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
// set wav (output)
|
// set wav (output)
|
||||||
const wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement
|
const wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement;
|
||||||
wavOutput.src = "/tmp/out.wav?" + new Date().getTime()
|
wavOutput.src = "/tmp/out.wav?" + new Date().getTime();
|
||||||
wavOutput.controls = true
|
wavOutput.controls = true;
|
||||||
// @ts-ignore
|
try {
|
||||||
wavOutput.setSinkId(audioOutputForAnalyzer)
|
// @ts-ignore
|
||||||
}
|
wavOutput.setSinkId(audioOutputForAnalyzer);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const startClassName = serverIORecording ? "config-sub-area-button-active" : "config-sub-area-button"
|
const startClassName = serverIORecording ? "config-sub-area-button-active" : "config-sub-area-button";
|
||||||
const stopClassName = serverIORecording ? "config-sub-area-button" : "config-sub-area-button-active"
|
const stopClassName = serverIORecording ? "config-sub-area-button" : "config-sub-area-button-active";
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="config-sub-area-control">
|
<div className="config-sub-area-control">
|
||||||
@ -49,34 +55,51 @@ export const RecorderArea = (_props: RecorderAreaProps) => {
|
|||||||
<div className="config-sub-area-control-title">SIO rec.</div>
|
<div className="config-sub-area-control-title">SIO rec.</div>
|
||||||
<div className="config-sub-area-control-field">
|
<div className="config-sub-area-control-field">
|
||||||
<div className="config-sub-area-buttons">
|
<div className="config-sub-area-buttons">
|
||||||
<div onClick={onServerIORecordStartClicked} className={startClassName}>start</div>
|
<div onClick={onServerIORecordStartClicked} className={startClassName}>
|
||||||
<div onClick={onServerIORecordStopClicked} className={stopClassName}>stop</div>
|
start
|
||||||
|
</div>
|
||||||
|
<div onClick={onServerIORecordStopClicked} className={stopClassName}>
|
||||||
|
stop
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div className="config-sub-area-control left-padding-1">
|
<div className="config-sub-area-control left-padding-1">
|
||||||
<div className="config-sub-area-control-title">output</div>
|
<div className="config-sub-area-control-title">output</div>
|
||||||
<div className="config-sub-area-control-field">
|
<div className="config-sub-area-control-field">
|
||||||
<div className="config-sub-area-control-field-auido-io">
|
<div className="config-sub-area-control-field-auido-io">
|
||||||
<select className="body-select" value={audioOutputForAnalyzer} onChange={(e) => {
|
<select
|
||||||
setAudioOutputForAnalyzer(e.target.value)
|
className="body-select"
|
||||||
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement
|
value={audioOutputForAnalyzer}
|
||||||
const wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement
|
onChange={(e) => {
|
||||||
//@ts-ignore
|
setAudioOutputForAnalyzer(e.target.value);
|
||||||
wavInput.setSinkId(e.target.value)
|
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement;
|
||||||
//@ts-ignore
|
const wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement;
|
||||||
wavOutput.setSinkId(e.target.value)
|
try {
|
||||||
}}>
|
//@ts-ignore
|
||||||
{
|
wavInput.setSinkId(e.target.value);
|
||||||
outputAudioDeviceInfo.map(x => {
|
//@ts-ignore
|
||||||
|
wavOutput.setSinkId(e.target.value);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{outputAudioDeviceInfo
|
||||||
|
.map((x) => {
|
||||||
if (x.deviceId == "none") {
|
if (x.deviceId == "none") {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
return <option key={x.deviceId} value={x.deviceId}>{x.label}</option>
|
return (
|
||||||
}).filter(x => { return x != null })
|
<option key={x.deviceId} value={x.deviceId}>
|
||||||
}
|
{x.label}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter((x) => {
|
||||||
|
return x != null;
|
||||||
|
})}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -102,17 +125,9 @@ export const RecorderArea = (_props: RecorderAreaProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
|
}, [serverIORecording, audioOutputForAnalyzer, outputAudioDeviceInfo, serverSetting.updateServerSettings]);
|
||||||
}, [serverIORecording, audioOutputForAnalyzer, outputAudioDeviceInfo, serverSetting.updateServerSettings])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="config-sub-area">
|
|
||||||
{serverIORecorderRow}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
return <div className="config-sub-area">{serverIORecorderRow}</div>;
|
||||||
|
};
|
||||||
|
@ -1,191 +1,207 @@
|
|||||||
export const RequestType = {
|
export const RequestType = {
|
||||||
voice: "voice",
|
voice: "voice",
|
||||||
config: "config",
|
config: "config",
|
||||||
start: "start",
|
start: "start",
|
||||||
stop: "stop",
|
stop: "stop",
|
||||||
trancateBuffer: "trancateBuffer",
|
trancateBuffer: "trancateBuffer",
|
||||||
} as const;
|
} as const;
|
||||||
export type RequestType = (typeof RequestType)[keyof typeof RequestType];
|
export type RequestType = (typeof RequestType)[keyof typeof RequestType];
|
||||||
|
|
||||||
export const ResponseType = {
|
export const ResponseType = {
|
||||||
volume: "volume",
|
volume: "volume",
|
||||||
inputData: "inputData",
|
inputData: "inputData",
|
||||||
start_ok: "start_ok",
|
start_ok: "start_ok",
|
||||||
stop_ok: "stop_ok",
|
stop_ok: "stop_ok",
|
||||||
} as const;
|
} as const;
|
||||||
export type ResponseType = (typeof ResponseType)[keyof typeof ResponseType];
|
export type ResponseType = (typeof ResponseType)[keyof typeof ResponseType];
|
||||||
|
|
||||||
export type VoiceChangerWorkletProcessorRequest = {
|
export type VoiceChangerWorkletProcessorRequest = {
|
||||||
requestType: RequestType;
|
requestType: RequestType;
|
||||||
voice: Float32Array;
|
voice: Float32Array;
|
||||||
numTrancateTreshold: number;
|
numTrancateTreshold: number;
|
||||||
volTrancateThreshold: number;
|
volTrancateThreshold: number;
|
||||||
volTrancateLength: number;
|
volTrancateLength: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type VoiceChangerWorkletProcessorResponse = {
|
export type VoiceChangerWorkletProcessorResponse = {
|
||||||
responseType: ResponseType;
|
responseType: ResponseType;
|
||||||
volume?: number;
|
volume?: number;
|
||||||
recordData?: Float32Array[];
|
recordData?: Float32Array[];
|
||||||
inputData?: Float32Array;
|
inputData?: Float32Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
||||||
private BLOCK_SIZE = 128;
|
private BLOCK_SIZE = 128;
|
||||||
private initialized = false;
|
private initialized = false;
|
||||||
private volume = 0;
|
private volume = 0;
|
||||||
// private numTrancateTreshold = 100;
|
// private numTrancateTreshold = 100;
|
||||||
// private volTrancateThreshold = 0.0005
|
// private volTrancateThreshold = 0.0005
|
||||||
// private volTrancateLength = 32
|
// private volTrancateLength = 32
|
||||||
// private volTrancateCount = 0
|
// private volTrancateCount = 0
|
||||||
|
|
||||||
private isRecording = false;
|
private isRecording = false;
|
||||||
|
|
||||||
playBuffer: Float32Array[] = [];
|
playBuffer: Float32Array[] = [];
|
||||||
unpushedF32Data: Float32Array = new Float32Array(0);
|
unpushedF32Data: Float32Array = new Float32Array(0);
|
||||||
/**
|
/**
|
||||||
* @constructor
|
* @constructor
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.initialized = true;
|
console.log("[AudioWorkletProcessor] created.");
|
||||||
this.port.onmessage = this.handleMessage.bind(this);
|
this.initialized = true;
|
||||||
|
this.port.onmessage = this.handleMessage.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
calcVol = (data: Float32Array, prevVol: number) => {
|
||||||
|
const sum = data.reduce((prev, cur) => {
|
||||||
|
return prev + cur * cur;
|
||||||
|
}, 0);
|
||||||
|
const rms = Math.sqrt(sum / data.length);
|
||||||
|
return Math.max(rms, prevVol * 0.95);
|
||||||
|
};
|
||||||
|
|
||||||
|
trancateBuffer = () => {
|
||||||
|
console.log("[worklet] Buffer truncated");
|
||||||
|
while (this.playBuffer.length > 2) {
|
||||||
|
this.playBuffer.shift();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
handleMessage(event: any) {
|
||||||
|
const request = event.data as VoiceChangerWorkletProcessorRequest;
|
||||||
|
if (request.requestType === "config") {
|
||||||
|
// this.numTrancateTreshold = request.numTrancateTreshold;
|
||||||
|
// this.volTrancateLength = request.volTrancateLength
|
||||||
|
// this.volTrancateThreshold = request.volTrancateThreshold
|
||||||
|
console.log("[worklet] worklet configured", request);
|
||||||
|
return;
|
||||||
|
} else if (request.requestType === "start") {
|
||||||
|
if (this.isRecording) {
|
||||||
|
console.warn("[worklet] recoring is already started");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isRecording = true;
|
||||||
|
const startResponse: VoiceChangerWorkletProcessorResponse = {
|
||||||
|
responseType: "start_ok",
|
||||||
|
};
|
||||||
|
this.port.postMessage(startResponse);
|
||||||
|
return;
|
||||||
|
} else if (request.requestType === "stop") {
|
||||||
|
if (!this.isRecording) {
|
||||||
|
console.warn("[worklet] recoring is not started");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isRecording = false;
|
||||||
|
const stopResponse: VoiceChangerWorkletProcessorResponse = {
|
||||||
|
responseType: "stop_ok",
|
||||||
|
};
|
||||||
|
this.port.postMessage(stopResponse);
|
||||||
|
return;
|
||||||
|
} else if (request.requestType === "trancateBuffer") {
|
||||||
|
this.trancateBuffer();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
calcVol = (data: Float32Array, prevVol: number) => {
|
const f32Data = request.voice;
|
||||||
const sum = data.reduce((prev, cur) => {
|
// if (this.playBuffer.length > this.numTrancateTreshold) {
|
||||||
return prev + cur * cur;
|
// console.log(`[worklet] Truncate ${this.playBuffer.length} > ${this.numTrancateTreshold}`);
|
||||||
}, 0);
|
// this.trancateBuffer();
|
||||||
const rms = Math.sqrt(sum / data.length);
|
// }
|
||||||
return Math.max(rms, prevVol * 0.95);
|
if (this.playBuffer.length > (f32Data.length / this.BLOCK_SIZE) * 1.5) {
|
||||||
};
|
console.log(
|
||||||
|
`[worklet] Truncate ${this.playBuffer.length} > ${
|
||||||
trancateBuffer = () => {
|
f32Data.length / this.BLOCK_SIZE
|
||||||
console.log("[worklet] Buffer truncated");
|
}`
|
||||||
while (this.playBuffer.length > 2) {
|
);
|
||||||
this.playBuffer.shift();
|
this.trancateBuffer();
|
||||||
}
|
|
||||||
};
|
|
||||||
handleMessage(event: any) {
|
|
||||||
const request = event.data as VoiceChangerWorkletProcessorRequest;
|
|
||||||
if (request.requestType === "config") {
|
|
||||||
// this.numTrancateTreshold = request.numTrancateTreshold;
|
|
||||||
// this.volTrancateLength = request.volTrancateLength
|
|
||||||
// this.volTrancateThreshold = request.volTrancateThreshold
|
|
||||||
console.log("[worklet] worklet configured", request);
|
|
||||||
return;
|
|
||||||
} else if (request.requestType === "start") {
|
|
||||||
if (this.isRecording) {
|
|
||||||
console.warn("[worklet] recoring is already started");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.isRecording = true;
|
|
||||||
const startResponse: VoiceChangerWorkletProcessorResponse = {
|
|
||||||
responseType: "start_ok",
|
|
||||||
};
|
|
||||||
this.port.postMessage(startResponse);
|
|
||||||
return;
|
|
||||||
} else if (request.requestType === "stop") {
|
|
||||||
if (!this.isRecording) {
|
|
||||||
console.warn("[worklet] recoring is not started");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.isRecording = false;
|
|
||||||
const stopResponse: VoiceChangerWorkletProcessorResponse = {
|
|
||||||
responseType: "stop_ok",
|
|
||||||
};
|
|
||||||
this.port.postMessage(stopResponse);
|
|
||||||
return;
|
|
||||||
} else if (request.requestType === "trancateBuffer") {
|
|
||||||
this.trancateBuffer();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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}`);
|
|
||||||
this.trancateBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
for (let i = 0; i < chunkNum; i++) {
|
|
||||||
const block = concatedF32Data.slice(i * this.BLOCK_SIZE, (i + 1) * this.BLOCK_SIZE);
|
|
||||||
this.playBuffer.push(block);
|
|
||||||
}
|
|
||||||
this.unpushedF32Data = concatedF32Data.slice(chunkNum * this.BLOCK_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pushData = (inputData: Float32Array) => {
|
const concatedF32Data = new Float32Array(
|
||||||
const volumeResponse: VoiceChangerWorkletProcessorResponse = {
|
this.unpushedF32Data.length + f32Data.length
|
||||||
responseType: ResponseType.inputData,
|
);
|
||||||
inputData: inputData,
|
concatedF32Data.set(this.unpushedF32Data);
|
||||||
};
|
concatedF32Data.set(f32Data, this.unpushedF32Data.length);
|
||||||
this.port.postMessage(volumeResponse);
|
|
||||||
};
|
|
||||||
|
|
||||||
process(_inputs: Float32Array[][], outputs: Float32Array[][], _parameters: Record<string, Float32Array>) {
|
const chunkNum = Math.floor(concatedF32Data.length / this.BLOCK_SIZE);
|
||||||
if (!this.initialized) {
|
for (let i = 0; i < chunkNum; i++) {
|
||||||
console.warn("[worklet] worklet_process not ready");
|
const block = concatedF32Data.slice(
|
||||||
return true;
|
i * this.BLOCK_SIZE,
|
||||||
}
|
(i + 1) * this.BLOCK_SIZE
|
||||||
|
);
|
||||||
if (this.isRecording) {
|
this.playBuffer.push(block);
|
||||||
if (_inputs.length > 0 && _inputs[0].length > 0) {
|
|
||||||
this.pushData(_inputs[0][0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.playBuffer.length === 0) {
|
|
||||||
// console.log("[worklet] no play buffer");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// console.log("[worklet] play buffer");
|
|
||||||
//// 一定期間無音状態が続いている場合はスキップ。
|
|
||||||
// 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")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
let voice = this.playBuffer.shift();
|
|
||||||
|
|
||||||
if (voice) {
|
|
||||||
this.volume = this.calcVol(voice, this.volume);
|
|
||||||
const volumeResponse: VoiceChangerWorkletProcessorResponse = {
|
|
||||||
responseType: ResponseType.volume,
|
|
||||||
volume: this.volume,
|
|
||||||
};
|
|
||||||
this.port.postMessage(volumeResponse);
|
|
||||||
outputs[0][0].set(voice);
|
|
||||||
if (outputs[0].length == 2) {
|
|
||||||
outputs[0][1].set(voice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
this.unpushedF32Data = concatedF32Data.slice(chunkNum * this.BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pushData = (inputData: Float32Array) => {
|
||||||
|
const volumeResponse: VoiceChangerWorkletProcessorResponse = {
|
||||||
|
responseType: ResponseType.inputData,
|
||||||
|
inputData: inputData,
|
||||||
|
};
|
||||||
|
this.port.postMessage(volumeResponse);
|
||||||
|
};
|
||||||
|
|
||||||
|
process(
|
||||||
|
_inputs: Float32Array[][],
|
||||||
|
outputs: Float32Array[][],
|
||||||
|
_parameters: Record<string, Float32Array>
|
||||||
|
) {
|
||||||
|
if (!this.initialized) {
|
||||||
|
console.warn("[worklet] worklet_process not ready");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isRecording) {
|
||||||
|
if (_inputs.length > 0 && _inputs[0].length > 0) {
|
||||||
|
this.pushData(_inputs[0][0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.playBuffer.length === 0) {
|
||||||
|
// console.log("[worklet] no play buffer");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// console.log("[worklet] play buffer");
|
||||||
|
//// 一定期間無音状態が続いている場合はスキップ。
|
||||||
|
// 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")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
let voice = this.playBuffer.shift();
|
||||||
|
if (voice) {
|
||||||
|
this.volume = this.calcVol(voice, this.volume);
|
||||||
|
const volumeResponse: VoiceChangerWorkletProcessorResponse = {
|
||||||
|
responseType: ResponseType.volume,
|
||||||
|
volume: this.volume,
|
||||||
|
};
|
||||||
|
this.port.postMessage(volumeResponse);
|
||||||
|
outputs[0][0].set(voice);
|
||||||
|
if (outputs[0].length == 2) {
|
||||||
|
outputs[0][1].set(voice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
registerProcessor("voice-changer-worklet-processor", VoiceChangerWorkletProcessor);
|
registerProcessor(
|
||||||
|
"voice-changer-worklet-processor",
|
||||||
|
VoiceChangerWorkletProcessor
|
||||||
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user