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から音は出さない。
|
||||
audio.volume = 0;
|
||||
} else if (audioOutputForGUI == "none") {
|
||||
try {
|
||||
// @ts-ignore
|
||||
audio.setSinkId("");
|
||||
} catch (e) {
|
||||
console.error("catch:" + e);
|
||||
}
|
||||
if (x == AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) {
|
||||
audio.volume = 0;
|
||||
} else {
|
||||
@ -404,8 +408,12 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
return x.deviceId == audioOutputForGUI;
|
||||
});
|
||||
if (found) {
|
||||
try {
|
||||
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||
audio.setSinkId(audioOutputForGUI);
|
||||
} catch (e) {
|
||||
console.error("catch:" + e);
|
||||
}
|
||||
} else {
|
||||
console.warn("No audio output device. use default");
|
||||
}
|
||||
@ -620,9 +628,13 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
// Server Audio を使う場合はElementから音は出さない。
|
||||
audio.volume = 0;
|
||||
} else if (audioMonitorForGUI == "none") {
|
||||
try {
|
||||
// @ts-ignore
|
||||
audio.setSinkId("");
|
||||
audio.volume = 0;
|
||||
} catch (e) {
|
||||
console.error("catch:" + e);
|
||||
}
|
||||
} else {
|
||||
const audioOutputs = mediaDeviceInfos.filter((x) => {
|
||||
return x.kind == "audiooutput";
|
||||
@ -631,9 +643,13 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
return x.deviceId == audioMonitorForGUI;
|
||||
});
|
||||
if (found) {
|
||||
try {
|
||||
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||
audio.setSinkId(audioMonitorForGUI);
|
||||
audio.volume = 1;
|
||||
} catch (e) {
|
||||
console.error("catch:" + e);
|
||||
}
|
||||
} else {
|
||||
console.warn("No audio output device. use default");
|
||||
}
|
||||
|
@ -1,44 +1,50 @@
|
||||
import React, { useMemo, useState } from "react"
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||
import { useGuiState } from "../001_GuiStateProvider"
|
||||
import { AUDIO_ELEMENT_FOR_SAMPLING_INPUT, AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT } from "../../../const"
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
import { useGuiState } from "../001_GuiStateProvider";
|
||||
import { AUDIO_ELEMENT_FOR_SAMPLING_INPUT, AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT } from "../../../const";
|
||||
|
||||
export type RecorderAreaProps = {
|
||||
}
|
||||
export type RecorderAreaProps = {};
|
||||
|
||||
export const RecorderArea = (_props: RecorderAreaProps) => {
|
||||
const { serverSetting } = useAppState()
|
||||
const { audioOutputForAnalyzer, setAudioOutputForAnalyzer, outputAudioDeviceInfo } = useGuiState()
|
||||
|
||||
const [serverIORecording, setServerIORecording] = useState<boolean>(false)
|
||||
const { serverSetting } = useAppState();
|
||||
const { audioOutputForAnalyzer, setAudioOutputForAnalyzer, outputAudioDeviceInfo } = useGuiState();
|
||||
|
||||
const [serverIORecording, setServerIORecording] = useState<boolean>(false);
|
||||
|
||||
const serverIORecorderRow = useMemo(() => {
|
||||
const onServerIORecordStartClicked = async () => {
|
||||
setServerIORecording(true)
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 1 })
|
||||
}
|
||||
setServerIORecording(true);
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 1 });
|
||||
};
|
||||
const onServerIORecordStopClicked = async () => {
|
||||
setServerIORecording(false)
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 0 })
|
||||
setServerIORecording(false);
|
||||
await serverSetting.updateServerSettings({ ...serverSetting.serverSetting, recordIO: 0 });
|
||||
|
||||
// set wav (input)
|
||||
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement
|
||||
wavInput.src = "/tmp/in.wav?" + new Date().getTime()
|
||||
wavInput.controls = true
|
||||
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement;
|
||||
wavInput.src = "/tmp/in.wav?" + new Date().getTime();
|
||||
wavInput.controls = true;
|
||||
try {
|
||||
// @ts-ignore
|
||||
wavInput.setSinkId(audioOutputForAnalyzer)
|
||||
|
||||
// set wav (output)
|
||||
const wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement
|
||||
wavOutput.src = "/tmp/out.wav?" + new Date().getTime()
|
||||
wavOutput.controls = true
|
||||
// @ts-ignore
|
||||
wavOutput.setSinkId(audioOutputForAnalyzer)
|
||||
wavInput.setSinkId(audioOutputForAnalyzer);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
const startClassName = serverIORecording ? "config-sub-area-button-active" : "config-sub-area-button"
|
||||
const stopClassName = serverIORecording ? "config-sub-area-button" : "config-sub-area-button-active"
|
||||
// set wav (output)
|
||||
const wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement;
|
||||
wavOutput.src = "/tmp/out.wav?" + new Date().getTime();
|
||||
wavOutput.controls = true;
|
||||
try {
|
||||
// @ts-ignore
|
||||
wavOutput.setSinkId(audioOutputForAnalyzer);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
|
||||
const startClassName = serverIORecording ? "config-sub-area-button-active" : "config-sub-area-button";
|
||||
const stopClassName = serverIORecording ? "config-sub-area-button" : "config-sub-area-button-active";
|
||||
return (
|
||||
<>
|
||||
<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-field">
|
||||
<div className="config-sub-area-buttons">
|
||||
<div onClick={onServerIORecordStartClicked} className={startClassName}>start</div>
|
||||
<div onClick={onServerIORecordStopClicked} className={stopClassName}>stop</div>
|
||||
<div onClick={onServerIORecordStartClicked} className={startClassName}>
|
||||
start
|
||||
</div>
|
||||
<div onClick={onServerIORecordStopClicked} className={stopClassName}>
|
||||
stop
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="config-sub-area-control left-padding-1">
|
||||
<div className="config-sub-area-control-title">output</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-control-field-auido-io">
|
||||
<select className="body-select" value={audioOutputForAnalyzer} onChange={(e) => {
|
||||
setAudioOutputForAnalyzer(e.target.value)
|
||||
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement
|
||||
const wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement
|
||||
<select
|
||||
className="body-select"
|
||||
value={audioOutputForAnalyzer}
|
||||
onChange={(e) => {
|
||||
setAudioOutputForAnalyzer(e.target.value);
|
||||
const wavInput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_INPUT) as HTMLAudioElement;
|
||||
const wavOutput = document.getElementById(AUDIO_ELEMENT_FOR_SAMPLING_OUTPUT) as HTMLAudioElement;
|
||||
try {
|
||||
//@ts-ignore
|
||||
wavInput.setSinkId(e.target.value)
|
||||
wavInput.setSinkId(e.target.value);
|
||||
//@ts-ignore
|
||||
wavOutput.setSinkId(e.target.value)
|
||||
}}>
|
||||
{
|
||||
outputAudioDeviceInfo.map(x => {
|
||||
wavOutput.setSinkId(e.target.value);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{outputAudioDeviceInfo
|
||||
.map((x) => {
|
||||
if (x.deviceId == "none") {
|
||||
return null
|
||||
}
|
||||
return <option key={x.deviceId} value={x.deviceId}>{x.label}</option>
|
||||
}).filter(x => { return x != null })
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<option key={x.deviceId} value={x.deviceId}>
|
||||
{x.label}
|
||||
</option>
|
||||
);
|
||||
})
|
||||
.filter((x) => {
|
||||
return x != null;
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -102,17 +125,9 @@ export const RecorderArea = (_props: RecorderAreaProps) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</>
|
||||
)
|
||||
|
||||
}, [serverIORecording, audioOutputForAnalyzer, outputAudioDeviceInfo, serverSetting.updateServerSettings])
|
||||
|
||||
return (
|
||||
<div className="config-sub-area">
|
||||
{serverIORecorderRow}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
);
|
||||
}, [serverIORecording, audioOutputForAnalyzer, outputAudioDeviceInfo, serverSetting.updateServerSettings]);
|
||||
|
||||
return <div className="config-sub-area">{serverIORecorderRow}</div>;
|
||||
};
|
||||
|
@ -48,6 +48,7 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
console.log("[AudioWorkletProcessor] created.");
|
||||
this.initialized = true;
|
||||
this.port.onmessage = this.handleMessage.bind(this);
|
||||
}
|
||||
@ -106,18 +107,27 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
||||
// 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}`);
|
||||
if (this.playBuffer.length > (f32Data.length / this.BLOCK_SIZE) * 1.5) {
|
||||
console.log(
|
||||
`[worklet] Truncate ${this.playBuffer.length} > ${
|
||||
f32Data.length / this.BLOCK_SIZE
|
||||
}`
|
||||
);
|
||||
this.trancateBuffer();
|
||||
}
|
||||
|
||||
const concatedF32Data = new Float32Array(this.unpushedF32Data.length + f32Data.length);
|
||||
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);
|
||||
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);
|
||||
@ -131,7 +141,11 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
||||
this.port.postMessage(volumeResponse);
|
||||
};
|
||||
|
||||
process(_inputs: Float32Array[][], outputs: Float32Array[][], _parameters: Record<string, Float32Array>) {
|
||||
process(
|
||||
_inputs: Float32Array[][],
|
||||
outputs: Float32Array[][],
|
||||
_parameters: Record<string, Float32Array>
|
||||
) {
|
||||
if (!this.initialized) {
|
||||
console.warn("[worklet] worklet_process not ready");
|
||||
return true;
|
||||
@ -171,7 +185,6 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
||||
// }
|
||||
// }
|
||||
let voice = this.playBuffer.shift();
|
||||
|
||||
if (voice) {
|
||||
this.volume = this.calcVol(voice, this.volume);
|
||||
const volumeResponse: VoiceChangerWorkletProcessorResponse = {
|
||||
@ -188,4 +201,7 @@ class VoiceChangerWorkletProcessor extends AudioWorkletProcessor {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
registerProcessor("voice-changer-worklet-processor", VoiceChangerWorkletProcessor);
|
||||
registerProcessor(
|
||||
"voice-changer-worklet-processor",
|
||||
VoiceChangerWorkletProcessor
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user