Bugfix:
- device output recorder button is showed in server device mode. Feature: - server device monitor Improve: - default uvicorn error log
This commit is contained in:
parent
b051d08ae0
commit
75668e1534
2
client/demo/dist/index.js
vendored
2
client/demo/dist/index.js
vendored
File diff suppressed because one or more lines are too long
1767
client/demo/package-lock.json
generated
1767
client/demo/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -26,14 +26,14 @@
|
||||
"@babel/preset-env": "^7.22.5",
|
||||
"@babel/preset-react": "^7.22.5",
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@types/node": "^20.3.2",
|
||||
"@types/node": "^20.3.3",
|
||||
"@types/react": "^18.2.14",
|
||||
"@types/react-dom": "^18.2.6",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"babel-loader": "^9.1.2",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.8.1",
|
||||
"eslint": "^8.43.0",
|
||||
"eslint": "^8.44.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
@ -54,7 +54,7 @@
|
||||
"webpack-dev-server": "^4.15.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dannadori/voice-changer-client-js": "^1.0.155",
|
||||
"@dannadori/voice-changer-client-js": "^1.0.157",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.4.0",
|
||||
|
@ -1,35 +1,34 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react"
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider"
|
||||
import { fileSelectorAsDataURL, useIndexedDB, } from "@dannadori/voice-changer-client-js"
|
||||
import { useGuiState } from "../001_GuiStateProvider"
|
||||
import { AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_CONVERTED, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK, AUDIO_ELEMENT_FOR_TEST_ORIGINAL, INDEXEDDB_KEY_AUDIO_OUTPUT } from "../../../const"
|
||||
import React, { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useAppState } from "../../../001_provider/001_AppStateProvider";
|
||||
import { fileSelectorAsDataURL, useIndexedDB } from "@dannadori/voice-changer-client-js";
|
||||
import { useGuiState } from "../001_GuiStateProvider";
|
||||
import { AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_CONVERTED, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK, AUDIO_ELEMENT_FOR_TEST_ORIGINAL, INDEXEDDB_KEY_AUDIO_OUTPUT } from "../../../const";
|
||||
|
||||
export type DeviceAreaProps = {
|
||||
}
|
||||
export type DeviceAreaProps = {};
|
||||
|
||||
export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
const { setting, serverSetting, audioContext, setAudioOutputElementId, initializedRef, setVoiceChangerClientSetting, startOutputRecording, stopOutputRecording } = useAppState()
|
||||
const { isConverting, audioInputForGUI, inputAudioDeviceInfo, setAudioInputForGUI, fileInputEchoback, setFileInputEchoback, setAudioOutputForGUI, audioOutputForGUI, outputAudioDeviceInfo } = useGuiState()
|
||||
const [inputHostApi, setInputHostApi] = useState<string>("ALL")
|
||||
const [outputHostApi, setOutputHostApi] = useState<string>("ALL")
|
||||
const audioSrcNode = useRef<MediaElementAudioSourceNode>()
|
||||
const { setting, serverSetting, audioContext, setAudioOutputElementId, initializedRef, setVoiceChangerClientSetting, startOutputRecording, stopOutputRecording } = useAppState();
|
||||
const { isConverting, audioInputForGUI, inputAudioDeviceInfo, setAudioInputForGUI, fileInputEchoback, setFileInputEchoback, setAudioOutputForGUI, audioOutputForGUI, outputAudioDeviceInfo } = useGuiState();
|
||||
const [inputHostApi, setInputHostApi] = useState<string>("ALL");
|
||||
const [outputHostApi, setOutputHostApi] = useState<string>("ALL");
|
||||
const audioSrcNode = useRef<MediaElementAudioSourceNode>();
|
||||
|
||||
const { getItem, setItem } = useIndexedDB({ clientType: null })
|
||||
const [outputRecordingStarted, setOutputRecordingStarted] = useState<boolean>(false)
|
||||
const { getItem, setItem } = useIndexedDB({ clientType: null });
|
||||
const [outputRecordingStarted, setOutputRecordingStarted] = useState<boolean>(false);
|
||||
|
||||
// (1) Audio Mode
|
||||
const deviceModeRow = useMemo(() => {
|
||||
const enableServerAudio = serverSetting.serverSetting.enableServerAudio
|
||||
const clientChecked = enableServerAudio == 1 ? false : true
|
||||
const serverChecked = enableServerAudio == 1 ? true : false
|
||||
const enableServerAudio = serverSetting.serverSetting.enableServerAudio;
|
||||
const clientChecked = enableServerAudio == 1 ? false : true;
|
||||
const serverChecked = enableServerAudio == 1 ? true : false;
|
||||
|
||||
const onDeviceModeChanged = (val: number) => {
|
||||
if (isConverting) {
|
||||
alert("cannot change mode when voice conversion is enabled")
|
||||
return
|
||||
alert("cannot change mode when voice conversion is enabled");
|
||||
return;
|
||||
}
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, enableServerAudio: val })
|
||||
}
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, enableServerAudio: val });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
@ -37,131 +36,182 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-noise-container">
|
||||
<div className="config-sub-area-noise-checkbox-container">
|
||||
<input type="radio" id="client-device" name="device-mode" checked={clientChecked} onChange={() => { onDeviceModeChanged(0) }} /> <label htmlFor="client-device">client</label>
|
||||
<input
|
||||
type="radio"
|
||||
id="client-device"
|
||||
name="device-mode"
|
||||
checked={clientChecked}
|
||||
onChange={() => {
|
||||
onDeviceModeChanged(0);
|
||||
}}
|
||||
/>{" "}
|
||||
<label htmlFor="client-device">client</label>
|
||||
</div>
|
||||
<div className="config-sub-area-noise-checkbox-container">
|
||||
<input className="left-padding-1" type="radio" id="server-device" name="device-mode" checked={serverChecked} onChange={() => { onDeviceModeChanged(1) }} />
|
||||
<input
|
||||
className="left-padding-1"
|
||||
type="radio"
|
||||
id="server-device"
|
||||
name="device-mode"
|
||||
checked={serverChecked}
|
||||
onChange={() => {
|
||||
onDeviceModeChanged(1);
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="server-device">server</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, isConverting])
|
||||
|
||||
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, isConverting]);
|
||||
|
||||
// (2) Audio Input
|
||||
// キャッシュの設定は反映(たぶん、設定操作の時も起動していしまう。が問題は起こらないはず)
|
||||
useEffect(() => {
|
||||
if (typeof setting.voiceChangerClientSetting.audioInput == "string") {
|
||||
if (inputAudioDeviceInfo.find(x => {
|
||||
// console.log("COMPARE:", x.deviceId, appState.clientSetting.setting.audioInput)
|
||||
return x.deviceId == setting.voiceChangerClientSetting.audioInput
|
||||
})) {
|
||||
setAudioInputForGUI(setting.voiceChangerClientSetting.audioInput)
|
||||
if (
|
||||
inputAudioDeviceInfo.find((x) => {
|
||||
// console.log("COMPARE:", x.deviceId, appState.clientSetting.setting.audioInput)
|
||||
return x.deviceId == setting.voiceChangerClientSetting.audioInput;
|
||||
})
|
||||
) {
|
||||
setAudioInputForGUI(setting.voiceChangerClientSetting.audioInput);
|
||||
}
|
||||
}
|
||||
}, [inputAudioDeviceInfo, setting.voiceChangerClientSetting.audioInput])
|
||||
}, [inputAudioDeviceInfo, setting.voiceChangerClientSetting.audioInput]);
|
||||
|
||||
// (2-1) クライアント
|
||||
const clientAudioInputRow = useMemo(() => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title left-padding-1">input</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<select className="body-select" value={audioInputForGUI} onChange={async (e) => {
|
||||
setAudioInputForGUI(e.target.value)
|
||||
if (e.target.value != "file") {
|
||||
try {
|
||||
await setVoiceChangerClientSetting({ ...setting.voiceChangerClientSetting, audioInput: e.target.value })
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
console.error(e)
|
||||
setAudioInputForGUI("none")
|
||||
await setVoiceChangerClientSetting({ ...setting.voiceChangerClientSetting, audioInput: null })
|
||||
<select
|
||||
className="body-select"
|
||||
value={audioInputForGUI}
|
||||
onChange={async (e) => {
|
||||
setAudioInputForGUI(e.target.value);
|
||||
if (e.target.value != "file") {
|
||||
try {
|
||||
await setVoiceChangerClientSetting({ ...setting.voiceChangerClientSetting, audioInput: e.target.value });
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
console.error(e);
|
||||
setAudioInputForGUI("none");
|
||||
await setVoiceChangerClientSetting({ ...setting.voiceChangerClientSetting, audioInput: null });
|
||||
}
|
||||
}
|
||||
}
|
||||
}}>
|
||||
{
|
||||
inputAudioDeviceInfo.map(x => {
|
||||
return <option key={x.deviceId} value={x.deviceId}>{x.label}</option>
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
{inputAudioDeviceInfo.map((x) => {
|
||||
return (
|
||||
<option key={x.deviceId} value={x.deviceId}>
|
||||
{x.label}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [setVoiceChangerClientSetting, setting, inputAudioDeviceInfo, audioInputForGUI, serverSetting.serverSetting.enableServerAudio])
|
||||
|
||||
);
|
||||
}, [setVoiceChangerClientSetting, setting, inputAudioDeviceInfo, audioInputForGUI, serverSetting.serverSetting.enableServerAudio]);
|
||||
|
||||
// (2-2) サーバ
|
||||
const serverAudioInputRow = useMemo(() => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 0) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const devices = serverSetting.serverSetting.serverAudioInputDevices
|
||||
const hostAPIs = new Set(devices.map(x => { return x.hostAPI }))
|
||||
const hostAPIOptions = Array.from(hostAPIs).map((x, index) => { return <option value={x} key={index} >{x}</option> })
|
||||
const devices = serverSetting.serverSetting.serverAudioInputDevices;
|
||||
const hostAPIs = new Set(
|
||||
devices.map((x) => {
|
||||
return x.hostAPI;
|
||||
})
|
||||
);
|
||||
const hostAPIOptions = Array.from(hostAPIs).map((x, index) => {
|
||||
return (
|
||||
<option value={x} key={index}>
|
||||
{x}
|
||||
</option>
|
||||
);
|
||||
});
|
||||
|
||||
const filteredDevice = devices.map((x, index) => {
|
||||
if (inputHostApi != "ALL" && x.hostAPI != inputHostApi) {
|
||||
return null
|
||||
}
|
||||
return <option value={x.index} key={index}>[{x.hostAPI}]{x.name}</option>
|
||||
}).filter(x => x != null)
|
||||
const filteredDevice = devices
|
||||
.map((x, index) => {
|
||||
if (inputHostApi != "ALL" && x.hostAPI != inputHostApi) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<option value={x.index} key={index}>
|
||||
[{x.hostAPI}]{x.name}
|
||||
</option>
|
||||
);
|
||||
})
|
||||
.filter((x) => x != null);
|
||||
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title left-padding-1">input</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-control-field-auido-io">
|
||||
<select className="config-sub-area-control-field-auido-io-filter" name="kinds" id="kinds" value={inputHostApi} onChange={(e) => { setInputHostApi(e.target.value) }}>
|
||||
<option value="ALL" key="ALL" >ALL</option>
|
||||
<select
|
||||
className="config-sub-area-control-field-auido-io-filter"
|
||||
name="kinds"
|
||||
id="kinds"
|
||||
value={inputHostApi}
|
||||
onChange={(e) => {
|
||||
setInputHostApi(e.target.value);
|
||||
}}
|
||||
>
|
||||
<option value="ALL" key="ALL">
|
||||
ALL
|
||||
</option>
|
||||
{hostAPIOptions}
|
||||
</select>
|
||||
<select className="config-sub-area-control-field-auido-io-select" value={serverSetting.serverSetting.serverInputDeviceId} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverInputDeviceId: Number(e.target.value) })
|
||||
|
||||
}}>
|
||||
<select
|
||||
className="config-sub-area-control-field-auido-io-select"
|
||||
value={serverSetting.serverSetting.serverInputDeviceId}
|
||||
onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverInputDeviceId: Number(e.target.value) });
|
||||
}}
|
||||
>
|
||||
{filteredDevice}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
}, [inputHostApi, serverSetting.serverSetting, serverSetting.updateServerSettings, serverSetting.serverSetting.enableServerAudio])
|
||||
);
|
||||
}, [inputHostApi, serverSetting.serverSetting, serverSetting.updateServerSettings, serverSetting.serverSetting.enableServerAudio]);
|
||||
|
||||
// (2-3) File
|
||||
useEffect(() => {
|
||||
[AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK].forEach(x => {
|
||||
const audio = document.getElementById(x) as HTMLAudioElement
|
||||
[AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK].forEach((x) => {
|
||||
const audio = document.getElementById(x) as HTMLAudioElement;
|
||||
if (audio) {
|
||||
audio.volume = fileInputEchoback ? 1 : 0
|
||||
audio.volume = fileInputEchoback ? 1 : 0;
|
||||
}
|
||||
})
|
||||
}, [fileInputEchoback])
|
||||
});
|
||||
}, [fileInputEchoback]);
|
||||
|
||||
const audioInputMediaRow = useMemo(() => {
|
||||
if (audioInputForGUI != "file" || serverSetting.serverSetting.enableServerAudio == 1) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const onFileLoadClicked = async () => {
|
||||
const url = await fileSelectorAsDataURL("")
|
||||
const url = await fileSelectorAsDataURL("");
|
||||
|
||||
// input stream for client.
|
||||
const audio = document.getElementById(AUDIO_ELEMENT_FOR_TEST_CONVERTED) as HTMLAudioElement
|
||||
audio.pause()
|
||||
audio.srcObject = null
|
||||
audio.src = url
|
||||
await audio.play()
|
||||
const audio = document.getElementById(AUDIO_ELEMENT_FOR_TEST_CONVERTED) as HTMLAudioElement;
|
||||
audio.pause();
|
||||
audio.srcObject = null;
|
||||
audio.src = url;
|
||||
await audio.play();
|
||||
if (!audioSrcNode.current) {
|
||||
audioSrcNode.current = audioContext!.createMediaElementSource(audio);
|
||||
}
|
||||
@ -169,27 +219,27 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
audioSrcNode.current = audioContext!.createMediaElementSource(audio);
|
||||
}
|
||||
|
||||
const dst = audioContext.createMediaStreamDestination()
|
||||
audioSrcNode.current.connect(dst)
|
||||
const dst = audioContext.createMediaStreamDestination();
|
||||
audioSrcNode.current.connect(dst);
|
||||
try {
|
||||
setVoiceChangerClientSetting({ ...setting.voiceChangerClientSetting, audioInput: dst.stream })
|
||||
setVoiceChangerClientSetting({ ...setting.voiceChangerClientSetting, audioInput: dst.stream });
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
const audio_echo = document.getElementById(AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) as HTMLAudioElement
|
||||
audio_echo.srcObject = dst.stream
|
||||
audio_echo.play()
|
||||
audio_echo.volume = 0
|
||||
setFileInputEchoback(false)
|
||||
const audio_echo = document.getElementById(AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) as HTMLAudioElement;
|
||||
audio_echo.srcObject = dst.stream;
|
||||
audio_echo.play();
|
||||
audio_echo.volume = 0;
|
||||
setFileInputEchoback(false);
|
||||
|
||||
// original stream to play.
|
||||
const audio_org = document.getElementById(AUDIO_ELEMENT_FOR_TEST_ORIGINAL) as HTMLAudioElement
|
||||
audio_org.src = url
|
||||
audio_org.pause()
|
||||
}
|
||||
const audio_org = document.getElementById(AUDIO_ELEMENT_FOR_TEST_ORIGINAL) as HTMLAudioElement;
|
||||
audio_org.src = url;
|
||||
audio_org.pause();
|
||||
};
|
||||
|
||||
const echobackClass = fileInputEchoback ? "config-sub-area-control-field-wav-file-echoback-button-active" : "config-sub-area-control-field-wav-file-echoback-button"
|
||||
const echobackClass = fileInputEchoback ? "config-sub-area-control-field-wav-file-echoback-button-active" : "config-sub-area-control-field-wav-file-echoback-button";
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-field">
|
||||
@ -202,190 +252,332 @@ export const DeviceArea = (_props: DeviceAreaProps) => {
|
||||
<div>
|
||||
<img className="config-sub-area-control-field-wav-file-folder" src="./assets/icons/folder.svg" onClick={onFileLoadClicked} />
|
||||
</div>
|
||||
<div className={echobackClass} onClick={() => { setFileInputEchoback(!fileInputEchoback) }}>
|
||||
<div
|
||||
className={echobackClass}
|
||||
onClick={() => {
|
||||
setFileInputEchoback(!fileInputEchoback);
|
||||
}}
|
||||
>
|
||||
echo{fileInputEchoback}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [audioInputForGUI, fileInputEchoback, serverSetting.serverSetting])
|
||||
|
||||
|
||||
|
||||
);
|
||||
}, [audioInputForGUI, fileInputEchoback, serverSetting.serverSetting]);
|
||||
|
||||
// (3) Audio Output
|
||||
useEffect(() => {
|
||||
const loadCache = async () => {
|
||||
const key = await getItem(INDEXEDDB_KEY_AUDIO_OUTPUT)
|
||||
const key = await getItem(INDEXEDDB_KEY_AUDIO_OUTPUT);
|
||||
if (key) {
|
||||
setAudioOutputForGUI(key as string)
|
||||
setAudioOutputForGUI(key as string);
|
||||
}
|
||||
}
|
||||
loadCache()
|
||||
}, [])
|
||||
};
|
||||
loadCache();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const setAudioOutput = async () => {
|
||||
const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices();
|
||||
|
||||
[AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_ORIGINAL, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK].forEach(x => {
|
||||
const audio = document.getElementById(x) as HTMLAudioElement
|
||||
[AUDIO_ELEMENT_FOR_PLAY_RESULT, AUDIO_ELEMENT_FOR_TEST_ORIGINAL, AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK].forEach((x) => {
|
||||
const audio = document.getElementById(x) as HTMLAudioElement;
|
||||
if (audio) {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||
|
||||
// Server Audio を使う場合はElementから音は出さない。
|
||||
audio.volume = 0
|
||||
audio.volume = 0;
|
||||
} else if (audioOutputForGUI == "none") {
|
||||
// @ts-ignore
|
||||
audio.setSinkId("")
|
||||
audio.setSinkId("");
|
||||
if (x == AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) {
|
||||
audio.volume = 0
|
||||
audio.volume = 0;
|
||||
} else {
|
||||
audio.volume = 0
|
||||
audio.volume = 0;
|
||||
}
|
||||
} else {
|
||||
const audioOutputs = mediaDeviceInfos.filter(x => { return x.kind == "audiooutput" })
|
||||
const found = audioOutputs.some(x => { return x.deviceId == audioOutputForGUI })
|
||||
const audioOutputs = mediaDeviceInfos.filter((x) => {
|
||||
return x.kind == "audiooutput";
|
||||
});
|
||||
const found = audioOutputs.some((x) => {
|
||||
return x.deviceId == audioOutputForGUI;
|
||||
});
|
||||
if (found) {
|
||||
// @ts-ignore // 例外キャッチできないので事前にIDチェックが必要らしい。!?
|
||||
audio.setSinkId(audioOutputForGUI)
|
||||
audio.setSinkId(audioOutputForGUI);
|
||||
} else {
|
||||
console.warn("No audio output device. use default")
|
||||
console.warn("No audio output device. use default");
|
||||
}
|
||||
|
||||
if (x == AUDIO_ELEMENT_FOR_TEST_CONVERTED_ECHOBACK) {
|
||||
audio.volume = fileInputEchoback ? 1 : 0
|
||||
audio.volume = fileInputEchoback ? 1 : 0;
|
||||
} else {
|
||||
audio.volume = 1
|
||||
audio.volume = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
setAudioOutput()
|
||||
}, [audioOutputForGUI, fileInputEchoback, serverSetting.serverSetting.enableServerAudio])
|
||||
|
||||
});
|
||||
};
|
||||
setAudioOutput();
|
||||
}, [audioOutputForGUI, fileInputEchoback, serverSetting.serverSetting.enableServerAudio]);
|
||||
|
||||
// (3-1) クライアント
|
||||
const clientAudioOutputRow = useMemo(() => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title left-padding-1">output</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<select className="body-select" value={audioOutputForGUI} onChange={(e) => {
|
||||
setAudioOutputForGUI(e.target.value)
|
||||
setItem(INDEXEDDB_KEY_AUDIO_OUTPUT, e.target.value)
|
||||
}}>
|
||||
{
|
||||
outputAudioDeviceInfo.map(x => {
|
||||
return <option key={x.deviceId} value={x.deviceId}>{x.label}</option>
|
||||
})
|
||||
}
|
||||
<select
|
||||
className="body-select"
|
||||
value={audioOutputForGUI}
|
||||
onChange={(e) => {
|
||||
setAudioOutputForGUI(e.target.value);
|
||||
setItem(INDEXEDDB_KEY_AUDIO_OUTPUT, e.target.value);
|
||||
}}
|
||||
>
|
||||
{outputAudioDeviceInfo.map((x) => {
|
||||
return (
|
||||
<option key={x.deviceId} value={x.deviceId}>
|
||||
{x.label}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [serverSetting.serverSetting.enableServerAudio, outputAudioDeviceInfo, audioOutputForGUI])
|
||||
);
|
||||
}, [serverSetting.serverSetting.enableServerAudio, outputAudioDeviceInfo, audioOutputForGUI]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("initializedRef.current", initializedRef.current)
|
||||
setAudioOutputElementId(AUDIO_ELEMENT_FOR_PLAY_RESULT)
|
||||
}, [initializedRef.current])
|
||||
console.log("initializedRef.current", initializedRef.current);
|
||||
setAudioOutputElementId(AUDIO_ELEMENT_FOR_PLAY_RESULT);
|
||||
}, [initializedRef.current]);
|
||||
|
||||
// (3-2) サーバ
|
||||
const serverAudioOutputRow = useMemo(() => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 0) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
const devices = serverSetting.serverSetting.serverAudioOutputDevices
|
||||
const hostAPIs = new Set(devices.map(x => { return x.hostAPI }))
|
||||
const hostAPIOptions = Array.from(hostAPIs).map((x, index) => { return <option value={x} key={index} >{x}</option> })
|
||||
const devices = serverSetting.serverSetting.serverAudioOutputDevices;
|
||||
const hostAPIs = new Set(
|
||||
devices.map((x) => {
|
||||
return x.hostAPI;
|
||||
})
|
||||
);
|
||||
const hostAPIOptions = Array.from(hostAPIs).map((x, index) => {
|
||||
return (
|
||||
<option value={x} key={index}>
|
||||
{x}
|
||||
</option>
|
||||
);
|
||||
});
|
||||
|
||||
const filteredDevice = devices.map((x, index) => {
|
||||
if (outputHostApi != "ALL" && x.hostAPI != outputHostApi) {
|
||||
return null
|
||||
}
|
||||
return <option value={x.index} key={index}>[{x.hostAPI}]{x.name}</option>
|
||||
}).filter(x => x != null)
|
||||
const filteredDevice = devices
|
||||
.map((x, index) => {
|
||||
if (outputHostApi != "ALL" && x.hostAPI != outputHostApi) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<option value={x.index} key={index}>
|
||||
[{x.hostAPI}]{x.name}
|
||||
</option>
|
||||
);
|
||||
})
|
||||
.filter((x) => x != null);
|
||||
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title left-padding-1">output</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-control-field-auido-io">
|
||||
<select className="config-sub-area-control-field-auido-io-filter" name="kinds" id="kinds" value={outputHostApi} onChange={(e) => { setOutputHostApi(e.target.value) }}>
|
||||
<option value="ALL" key="ALL" >ALL</option>
|
||||
<select
|
||||
className="config-sub-area-control-field-auido-io-filter"
|
||||
name="kinds"
|
||||
id="kinds"
|
||||
value={outputHostApi}
|
||||
onChange={(e) => {
|
||||
setOutputHostApi(e.target.value);
|
||||
}}
|
||||
>
|
||||
<option value="ALL" key="ALL">
|
||||
ALL
|
||||
</option>
|
||||
{hostAPIOptions}
|
||||
</select>
|
||||
<select className="config-sub-area-control-field-auido-io-select" value={serverSetting.serverSetting.serverOutputDeviceId} onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverOutputDeviceId: Number(e.target.value) })
|
||||
}}>
|
||||
<select
|
||||
className="config-sub-area-control-field-auido-io-select"
|
||||
value={serverSetting.serverSetting.serverOutputDeviceId}
|
||||
onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverOutputDeviceId: Number(e.target.value) });
|
||||
}}
|
||||
>
|
||||
{filteredDevice}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}, [outputHostApi, serverSetting.serverSetting, serverSetting.updateServerSettings, serverSetting.serverSetting.enableServerAudio])
|
||||
|
||||
);
|
||||
}, [outputHostApi, serverSetting.serverSetting, serverSetting.updateServerSettings, serverSetting.serverSetting.enableServerAudio]);
|
||||
|
||||
// (4) レコーダー
|
||||
const outputRecorderRow = useMemo(() => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 1) {
|
||||
return <></>
|
||||
return <></>;
|
||||
}
|
||||
const onOutputRecordStartClicked = async () => {
|
||||
setOutputRecordingStarted(true)
|
||||
await startOutputRecording()
|
||||
}
|
||||
setOutputRecordingStarted(true);
|
||||
await startOutputRecording();
|
||||
};
|
||||
const onOutputRecordStopClicked = async () => {
|
||||
setOutputRecordingStarted(false)
|
||||
const record = await stopOutputRecording()
|
||||
downloadRecord(record)
|
||||
}
|
||||
setOutputRecordingStarted(false);
|
||||
const record = await stopOutputRecording();
|
||||
downloadRecord(record);
|
||||
};
|
||||
|
||||
const startClassName = outputRecordingStarted ? "config-sub-area-button-active" : "config-sub-area-button"
|
||||
const stopClassName = outputRecordingStarted ? "config-sub-area-button" : "config-sub-area-button-active"
|
||||
const startClassName = outputRecordingStarted ? "config-sub-area-button-active" : "config-sub-area-button";
|
||||
const stopClassName = outputRecordingStarted ? "config-sub-area-button" : "config-sub-area-button-active";
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title">REC.</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-buttons">
|
||||
<div onClick={onOutputRecordStartClicked} className={startClassName}>start</div>
|
||||
<div onClick={onOutputRecordStopClicked} className={stopClassName}>stop</div>
|
||||
<div onClick={onOutputRecordStartClicked} className={startClassName}>
|
||||
start
|
||||
</div>
|
||||
<div onClick={onOutputRecordStopClicked} className={stopClassName}>
|
||||
stop
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}, [outputRecordingStarted, startOutputRecording, stopOutputRecording, serverSetting.serverSetting.enableServerAudio]);
|
||||
|
||||
}, [outputRecordingStarted, startOutputRecording, stopOutputRecording])
|
||||
// (5) サンプリングレート
|
||||
const sampleRateRow = useMemo(() => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 0) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title left-padding-1">S.R.</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-control-field-auido-io">
|
||||
<select
|
||||
className="config-sub-area-control-field-sample-rate-select"
|
||||
value={serverSetting.serverSetting.serverAudioSampleRate}
|
||||
onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverAudioSampleRate: Number(e.target.value) });
|
||||
}}
|
||||
>
|
||||
{[16000, 32000, 44100, 48000, 96000, 192000].map((x) => {
|
||||
return (
|
||||
<option key={x} value={x}>
|
||||
{x}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [serverSetting.serverSetting, serverSetting.updateServerSettings, serverSetting.serverSetting.enableServerAudio]);
|
||||
|
||||
// (6) Monitor
|
||||
const serverMonitorRow = useMemo(() => {
|
||||
if (serverSetting.serverSetting.enableServerAudio == 0) {
|
||||
return <></>;
|
||||
}
|
||||
const devices = serverSetting.serverSetting.serverAudioOutputDevices;
|
||||
const hostAPIs = new Set(
|
||||
devices.map((x) => {
|
||||
return x.hostAPI;
|
||||
})
|
||||
);
|
||||
const hostAPIOptions = Array.from(hostAPIs).map((x, index) => {
|
||||
return (
|
||||
<option value={x} key={index}>
|
||||
{x}
|
||||
</option>
|
||||
);
|
||||
});
|
||||
|
||||
const filteredDevice = devices
|
||||
.map((x, index) => {
|
||||
if (outputHostApi != "ALL" && x.hostAPI != outputHostApi) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<option value={x.index} key={index}>
|
||||
[{x.hostAPI}]{x.name}
|
||||
</option>
|
||||
);
|
||||
})
|
||||
.filter((x) => x != null);
|
||||
filteredDevice.unshift(
|
||||
<option value={-1} key={-1}>
|
||||
None
|
||||
</option>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="config-sub-area-control">
|
||||
<div className="config-sub-area-control-title left-padding-1">monitor</div>
|
||||
<div className="config-sub-area-control-field">
|
||||
<div className="config-sub-area-control-field-auido-io">
|
||||
<select
|
||||
className="config-sub-area-control-field-auido-io-filter"
|
||||
name="kinds"
|
||||
id="kinds"
|
||||
value={outputHostApi}
|
||||
onChange={(e) => {
|
||||
setOutputHostApi(e.target.value);
|
||||
}}
|
||||
>
|
||||
<option value="ALL" key="ALL">
|
||||
ALL
|
||||
</option>
|
||||
{hostAPIOptions}
|
||||
</select>
|
||||
<select
|
||||
className="config-sub-area-control-field-auido-io-select"
|
||||
value={serverSetting.serverSetting.serverMonitorDeviceId}
|
||||
onChange={(e) => {
|
||||
serverSetting.updateServerSettings({ ...serverSetting.serverSetting, serverMonitorDeviceId: Number(e.target.value) });
|
||||
}}
|
||||
>
|
||||
{filteredDevice}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [outputHostApi, serverSetting.serverSetting, serverSetting.updateServerSettings, serverSetting.serverSetting.enableServerAudio]);
|
||||
|
||||
return (
|
||||
<div className="config-sub-area">
|
||||
{deviceModeRow}
|
||||
{sampleRateRow}
|
||||
{clientAudioInputRow}
|
||||
{serverAudioInputRow}
|
||||
{audioInputMediaRow}
|
||||
{clientAudioOutputRow}
|
||||
{serverAudioOutputRow}
|
||||
{serverMonitorRow}
|
||||
|
||||
{outputRecorderRow}
|
||||
<audio hidden id={AUDIO_ELEMENT_FOR_PLAY_RESULT}></audio>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
const downloadRecord = (data: Float32Array) => {
|
||||
|
||||
const writeString = (view: DataView, offset: number, string: string) => {
|
||||
for (var i = 0; i < string.length; i++) {
|
||||
view.setUint8(offset + i, string.charCodeAt(i));
|
||||
@ -395,7 +587,7 @@ const downloadRecord = (data: Float32Array) => {
|
||||
const floatTo16BitPCM = (output: DataView, offset: number, input: Float32Array) => {
|
||||
for (var i = 0; i < input.length; i++, offset += 2) {
|
||||
var s = Math.max(-1, Math.min(1, input[i]));
|
||||
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
|
||||
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
|
||||
}
|
||||
};
|
||||
|
||||
@ -403,10 +595,10 @@ const downloadRecord = (data: Float32Array) => {
|
||||
const view = new DataView(buffer);
|
||||
|
||||
// https://www.youfit.co.jp/archives/1418
|
||||
writeString(view, 0, 'RIFF'); // RIFFヘッダ
|
||||
writeString(view, 0, "RIFF"); // RIFFヘッダ
|
||||
view.setUint32(4, 32 + data.length * 2, true); // これ以降のファイルサイズ
|
||||
writeString(view, 8, 'WAVE'); // WAVEヘッダ
|
||||
writeString(view, 12, 'fmt '); // fmtチャンク
|
||||
writeString(view, 8, "WAVE"); // WAVEヘッダ
|
||||
writeString(view, 12, "fmt "); // fmtチャンク
|
||||
view.setUint32(16, 16, true); // fmtチャンクのバイト数
|
||||
view.setUint16(20, 1, true); // フォーマットID
|
||||
view.setUint16(22, 1, true); // チャンネル数
|
||||
@ -414,10 +606,10 @@ const downloadRecord = (data: Float32Array) => {
|
||||
view.setUint32(28, 48000 * 2, true); // データ速度
|
||||
view.setUint16(32, 2, true); // ブロックサイズ
|
||||
view.setUint16(34, 16, true); // サンプルあたりのビット数
|
||||
writeString(view, 36, 'data'); // dataチャンク
|
||||
writeString(view, 36, "data"); // dataチャンク
|
||||
view.setUint32(40, data.length * 2, true); // 波形データのバイト数
|
||||
floatTo16BitPCM(view, 44, data); // 波形データ
|
||||
const audioBlob = new Blob([view], { type: 'audio/wav' });
|
||||
const audioBlob = new Blob([view], { type: "audio/wav" });
|
||||
|
||||
const url = URL.createObjectURL(audioBlob);
|
||||
const a = document.createElement("a");
|
||||
@ -427,4 +619,4 @@ const downloadRecord = (data: Float32Array) => {
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
};
|
||||
|
1780
client/lib/package-lock.json
generated
1780
client/lib/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@dannadori/voice-changer-client-js",
|
||||
"version": "1.0.155",
|
||||
"version": "1.0.157",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"directories": {
|
||||
@ -27,10 +27,10 @@
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@types/audioworklet": "^0.0.48",
|
||||
"@types/node": "^20.3.2",
|
||||
"@types/node": "^20.3.3",
|
||||
"@types/react": "18.2.14",
|
||||
"@types/react-dom": "18.2.6",
|
||||
"eslint": "^8.43.0",
|
||||
"eslint": "^8.44.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.32.2",
|
||||
@ -47,7 +47,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/readable-stream": "^2.3.15",
|
||||
"amazon-chime-sdk-js": "^3.14.1",
|
||||
"amazon-chime-sdk-js": "^3.15.0",
|
||||
"buffer": "^6.0.3",
|
||||
"localforage": "^1.10.0",
|
||||
"react": "^18.2.0",
|
||||
|
@ -83,12 +83,15 @@ export const ServerSettingKey = {
|
||||
|
||||
"enableServerAudio": "enableServerAudio",
|
||||
"serverAudioStated": "serverAudioStated",
|
||||
"serverAudioSampleRate": "serverAudioSampleRate",
|
||||
"serverInputAudioSampleRate": "serverInputAudioSampleRate",
|
||||
"serverOutputAudioSampleRate": "serverOutputAudioSampleRate",
|
||||
"serverMonitorAudioSampleRate": "serverMonitorAudioSampleRate",
|
||||
"serverInputAudioBufferSize": "serverInputAudioBufferSize",
|
||||
"serverOutputAudioBufferSize": "serverOutputAudioBufferSize",
|
||||
"serverInputDeviceId": "serverInputDeviceId",
|
||||
"serverOutputDeviceId": "serverOutputDeviceId",
|
||||
"serverMonitorDeviceId": "serverMonitorDeviceId",
|
||||
"serverReadChunkSize": "serverReadChunkSize",
|
||||
"serverInputAudioGain": "serverInputAudioGain",
|
||||
"serverOutputAudioGain": "serverOutputAudioGain",
|
||||
@ -138,12 +141,15 @@ export type VoiceChangerServerSetting = {
|
||||
|
||||
enableServerAudio: number // 0:off, 1:on
|
||||
serverAudioStated: number // 0:off, 1:on
|
||||
serverAudioSampleRate: number
|
||||
serverInputAudioSampleRate: number
|
||||
serverOutputAudioSampleRate: number
|
||||
serverMonitorAudioSampleRate: number
|
||||
serverInputAudioBufferSize: number
|
||||
serverOutputAudioBufferSize: number
|
||||
serverInputDeviceId: number
|
||||
serverOutputDeviceId: number
|
||||
serverMonitorDeviceId: number
|
||||
serverReadChunkSize: number
|
||||
serverInputAudioGain: number
|
||||
serverOutputAudioGain: number
|
||||
@ -306,12 +312,15 @@ export const DefaultServerSetting: ServerInfo = {
|
||||
|
||||
enableServerAudio: 0,
|
||||
serverAudioStated: 0,
|
||||
serverAudioSampleRate: 48000,
|
||||
serverInputAudioSampleRate: 48000,
|
||||
serverOutputAudioSampleRate: 48000,
|
||||
serverMonitorAudioSampleRate: 48000,
|
||||
serverInputAudioBufferSize: 1024 * 24,
|
||||
serverOutputAudioBufferSize: 1024 * 24,
|
||||
serverInputDeviceId: -1,
|
||||
serverOutputDeviceId: -1,
|
||||
serverMonitorDeviceId: -1,
|
||||
serverReadChunkSize: 256,
|
||||
serverInputAudioGain: 1.0,
|
||||
serverOutputAudioGain: 1.0,
|
||||
|
8
server/.vscode/settings.json
vendored
8
server/.vscode/settings.json
vendored
@ -9,11 +9,9 @@
|
||||
"editor.formatOnSave": true // ファイル保存時に自動フォーマット
|
||||
},
|
||||
"python.formatting.blackArgs": ["--line-length", "550"],
|
||||
"flake8.args": [
|
||||
"--ignore=E501,E402,E722,E741,E203,W503"
|
||||
// "--max-line-length=150",
|
||||
// "--max-complexity=20"
|
||||
],
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.linting.flake8Args": [
|
||||
"--max-line-length=99999"
|
||||
],
|
||||
"python.linting.enabled": true
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ setup_loggers()
|
||||
|
||||
def setupArgParser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--logLevel", type=str, default="critical", help="Log level info|critical. (default: critical)")
|
||||
parser.add_argument("--logLevel", type=str, default="error", help="Log level info|critical|error. (default: error)")
|
||||
parser.add_argument("-p", type=int, default=18888, help="port")
|
||||
parser.add_argument("--https", type=strtobool, default=False, help="use https")
|
||||
parser.add_argument("--httpsKey", type=str, default="ssl.key", help="path for the key of https")
|
||||
@ -94,13 +94,16 @@ PORT = args.p
|
||||
|
||||
|
||||
def localServer(logLevel: str = "critical"):
|
||||
uvicorn.run(
|
||||
f"{os.path.basename(__file__)[:-3]}:app_socketio",
|
||||
host="0.0.0.0",
|
||||
port=int(PORT),
|
||||
reload=False if hasattr(sys, "_MEIPASS") else True,
|
||||
log_level=logLevel,
|
||||
)
|
||||
try:
|
||||
uvicorn.run(
|
||||
f"{os.path.basename(__file__)[:-3]}:app_socketio",
|
||||
host="0.0.0.0",
|
||||
port=int(PORT),
|
||||
reload=False if hasattr(sys, "_MEIPASS") else True,
|
||||
log_level=logLevel,
|
||||
)
|
||||
except Exception as e:
|
||||
print("[Voice Changer] Web Server Launch Exception", e)
|
||||
|
||||
|
||||
if __name__ == "MMVCServerSIO":
|
||||
@ -204,15 +207,19 @@ if __name__ == "__main__":
|
||||
# サーバ起動
|
||||
if args.https:
|
||||
# HTTPS サーバ起動
|
||||
uvicorn.run(
|
||||
f"{os.path.basename(__file__)[:-3]}:app_socketio",
|
||||
host="0.0.0.0",
|
||||
port=int(PORT),
|
||||
reload=False if hasattr(sys, "_MEIPASS") else True,
|
||||
ssl_keyfile=key_path,
|
||||
ssl_certfile=cert_path,
|
||||
log_level=args.logLevel,
|
||||
)
|
||||
try:
|
||||
uvicorn.run(
|
||||
f"{os.path.basename(__file__)[:-3]}:app_socketio",
|
||||
host="0.0.0.0",
|
||||
port=int(PORT),
|
||||
reload=False if hasattr(sys, "_MEIPASS") else True,
|
||||
ssl_keyfile=key_path,
|
||||
ssl_certfile=cert_path,
|
||||
log_level=args.logLevel,
|
||||
)
|
||||
except Exception as e:
|
||||
print("[Voice Changer] Web Server Launch Exception", e)
|
||||
|
||||
else:
|
||||
p = mp.Process(name="p", target=localServer, args=(args.logLevel,))
|
||||
p.start()
|
||||
|
@ -23,6 +23,8 @@ ModelType: TypeAlias = Literal[
|
||||
|
||||
STORED_SETTING_FILE = "stored_setting.json"
|
||||
|
||||
SERVER_DEVICE_SAMPLE_RATES = [16000, 32000, 44100, 48000, 96000, 192000]
|
||||
|
||||
tmpdir = tempfile.TemporaryDirectory()
|
||||
SSL_KEY_DIR = os.path.join(tmpdir.name, "keys") if hasattr(sys, "_MEIPASS") else "keys"
|
||||
MODEL_DIR = os.path.join(tmpdir.name, "logs") if hasattr(sys, "_MEIPASS") else "logs"
|
||||
|
@ -76,7 +76,6 @@ class MMVC_Rest_Fileuploader:
|
||||
except Exception as e:
|
||||
print("[Voice Changer] post_update_settings ex:", e)
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
|
||||
def post_load_model(
|
||||
|
@ -3,19 +3,19 @@ import os
|
||||
|
||||
|
||||
class IORecorder:
|
||||
def __init__(self, inputFilename: str, outputFilename: str, samplingRate: int):
|
||||
def __init__(self, inputFilename: str, outputFilename: str, inputSamplingRate: int, outputSamplingRate: int):
|
||||
self._clearFile(inputFilename)
|
||||
self._clearFile(outputFilename)
|
||||
|
||||
self.fi = wave.open(inputFilename, "wb")
|
||||
self.fi.setnchannels(1)
|
||||
self.fi.setsampwidth(2)
|
||||
self.fi.setframerate(samplingRate)
|
||||
self.fi.setframerate(inputSamplingRate)
|
||||
|
||||
self.fo = wave.open(outputFilename, "wb")
|
||||
self.fo.setnchannels(1)
|
||||
self.fo.setsampwidth(2)
|
||||
self.fo.setframerate(samplingRate)
|
||||
self.fo.setframerate(outputSamplingRate)
|
||||
|
||||
def _clearFile(self, filename: str):
|
||||
if os.path.exists(filename):
|
||||
|
@ -1,6 +1,9 @@
|
||||
import sounddevice as sd
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from const import ServerAudioDeviceTypes
|
||||
import numpy as np
|
||||
# from const import SERVER_DEVICE_SAMPLE_RATES
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -12,6 +15,40 @@ class ServerAudioDevice:
|
||||
maxInputChannels: int = 0
|
||||
maxOutputChannels: int = 0
|
||||
default_samplerate: int = 0
|
||||
available_samplerates: list[int] = field(default_factory=lambda: [])
|
||||
|
||||
|
||||
def dummy_callback(data: np.ndarray, frames, times, status):
|
||||
pass
|
||||
|
||||
|
||||
def checkSamplingRate(deviceId: int, desiredSamplingRate: int, type: ServerAudioDeviceTypes):
|
||||
if type == "input":
|
||||
try:
|
||||
with sd.InputStream(
|
||||
device=deviceId,
|
||||
callback=dummy_callback,
|
||||
dtype="float32",
|
||||
samplerate=desiredSamplingRate
|
||||
):
|
||||
pass
|
||||
return True
|
||||
except Exception as e: # NOQA
|
||||
# print("[checkSamplingRate]", e)
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
with sd.OutputStream(
|
||||
device=deviceId,
|
||||
callback=dummy_callback,
|
||||
dtype="float32",
|
||||
samplerate=desiredSamplingRate
|
||||
):
|
||||
pass
|
||||
return True
|
||||
except Exception as e: # NOQA
|
||||
# print("[checkSamplingRate]", e)
|
||||
return False
|
||||
|
||||
|
||||
def list_audio_device():
|
||||
@ -20,7 +57,7 @@ def list_audio_device():
|
||||
except Exception as e:
|
||||
print("[Voice Changer] ex:query_devices")
|
||||
print(e)
|
||||
return [], []
|
||||
raise e
|
||||
|
||||
inputAudioDeviceList = [d for d in audioDeviceList if d["max_input_channels"] > 0]
|
||||
outputAudioDeviceList = [d for d in audioDeviceList if d["max_output_channels"] > 0]
|
||||
@ -55,4 +92,20 @@ def list_audio_device():
|
||||
)
|
||||
serverAudioOutputDevices.append(serverOutputAudioDevice)
|
||||
|
||||
# print("check sample rate1")
|
||||
# for d in serverAudioInputDevices:
|
||||
# print("check sample rate1-1")
|
||||
# for sr in SERVER_DEVICE_SAMPLE_RATES:
|
||||
# print("check sample rate1-2")
|
||||
# if checkSamplingRate(d.index, sr, "input"):
|
||||
# d.available_samplerates.append(sr)
|
||||
# print("check sample rate2")
|
||||
# for d in serverAudioOutputDevices:
|
||||
# print("check sample rate2-1")
|
||||
# for sr in SERVER_DEVICE_SAMPLE_RATES:
|
||||
# print("check sample rate2-2")
|
||||
# if checkSamplingRate(d.index, sr, "output"):
|
||||
# d.available_samplerates.append(sr)
|
||||
# print("check sample rate3")
|
||||
|
||||
return serverAudioInputDevices, serverAudioOutputDevices
|
||||
|
@ -1,8 +1,9 @@
|
||||
from dataclasses import dataclass, asdict
|
||||
|
||||
import numpy as np
|
||||
from const import SERVER_DEVICE_SAMPLE_RATES
|
||||
|
||||
from voice_changer.Local.AudioDeviceList import list_audio_device
|
||||
from voice_changer.Local.AudioDeviceList import checkSamplingRate, list_audio_device
|
||||
import time
|
||||
import sounddevice as sd
|
||||
from voice_changer.utils.Timer import Timer
|
||||
@ -10,6 +11,8 @@ import librosa
|
||||
|
||||
from voice_changer.utils.VoiceChangerModel import AudioInOut
|
||||
from typing import Protocol
|
||||
from typing import Literal, TypeAlias
|
||||
AudioDeviceKind: TypeAlias = Literal["input", "output"]
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -18,12 +21,21 @@ class ServerDeviceSettings:
|
||||
serverAudioStated: int = 0 # 0:off, 1:on
|
||||
serverInputAudioSampleRate: int = 44100
|
||||
serverOutputAudioSampleRate: int = 44100
|
||||
serverMonitorAudioSampleRate: int = 44100
|
||||
|
||||
serverAudioSampleRate: int = 44100
|
||||
# serverAudioSampleRate: int = 16000
|
||||
# serverAudioSampleRate: int = 48000
|
||||
|
||||
serverInputDeviceId: int = -1
|
||||
serverOutputDeviceId: int = -1
|
||||
serverMonitorDeviceId: int = -1 # -1 でモニター無効
|
||||
serverReadChunkSize: int = 256
|
||||
serverInputAudioGain: float = 1.0
|
||||
serverOutputAudioGain: float = 1.0
|
||||
|
||||
exclusiveMode: bool = False
|
||||
|
||||
|
||||
EditableServerDeviceSettings = {
|
||||
"intData": [
|
||||
@ -31,14 +43,20 @@ EditableServerDeviceSettings = {
|
||||
"serverAudioStated",
|
||||
"serverInputAudioSampleRate",
|
||||
"serverOutputAudioSampleRate",
|
||||
"serverMonitorAudioSampleRate",
|
||||
"serverAudioSampleRate",
|
||||
"serverInputDeviceId",
|
||||
"serverOutputDeviceId",
|
||||
"serverMonitorDeviceId",
|
||||
"serverReadChunkSize",
|
||||
],
|
||||
"floatData": [
|
||||
"serverInputAudioGain",
|
||||
"serverOutputAudioGain",
|
||||
],
|
||||
"boolData": [
|
||||
"exclusiveMode"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@ -52,7 +70,10 @@ class ServerDeviceCallbacks(Protocol):
|
||||
def get_processing_sampling_rate(self):
|
||||
...
|
||||
|
||||
def setSamplingRate(self, sr: int):
|
||||
def setInputSamplingRate(self, sr: int):
|
||||
...
|
||||
|
||||
def setOutputSamplingRate(self, sr: int):
|
||||
...
|
||||
|
||||
|
||||
@ -60,6 +81,10 @@ class ServerDevice:
|
||||
def __init__(self, serverDeviceCallbacks: ServerDeviceCallbacks):
|
||||
self.settings = ServerDeviceSettings()
|
||||
self.serverDeviceCallbacks = serverDeviceCallbacks
|
||||
self.out_wav = None
|
||||
self.mon_wav = None
|
||||
self.serverAudioInputDevices = None
|
||||
self.serverAudioOutputDevices = None
|
||||
|
||||
def getServerInputAudioDevice(self, index: int):
|
||||
audioinput, _audiooutput = list_audio_device()
|
||||
@ -94,7 +119,221 @@ class ServerDevice:
|
||||
except Exception as e:
|
||||
print("[Voice Changer] ex:", e)
|
||||
|
||||
def audioInput_callback(self, indata: np.ndarray, frames, times, status):
|
||||
try:
|
||||
indata = indata * self.settings.serverInputAudioGain
|
||||
with Timer("all_inference_time") as t:
|
||||
unpackedData = librosa.to_mono(indata.T) * 32768.0
|
||||
unpackedData = unpackedData.astype(np.int16)
|
||||
out_wav, times = self.serverDeviceCallbacks.on_request(unpackedData)
|
||||
self.out_wav = out_wav
|
||||
self.mon_wav = out_wav
|
||||
all_inference_time = t.secs
|
||||
self.performance = [all_inference_time] + times
|
||||
self.serverDeviceCallbacks.emitTo(self.performance)
|
||||
self.performance = [round(x * 1000) for x in self.performance]
|
||||
except Exception as e:
|
||||
print("[Voice Changer][ServerDevice][audioInput_callback] ex:", e)
|
||||
# import traceback
|
||||
# traceback.print_exc()
|
||||
|
||||
def audioOutput_callback(self, outdata: np.ndarray, frames, times, status):
|
||||
try:
|
||||
if self.out_wav is None:
|
||||
return
|
||||
out_wav = self.out_wav
|
||||
outputChannels = outdata.shape[1]
|
||||
outdata[:] = np.repeat(out_wav, outputChannels).reshape(-1, outputChannels) / 32768.0
|
||||
outdata[:] = outdata * self.settings.serverOutputAudioGain
|
||||
except Exception as e:
|
||||
print("[Voice Changer][ServerDevice][audioOutput_callback] ex:", e)
|
||||
# import traceback
|
||||
# traceback.print_exc()
|
||||
|
||||
def audioMonitor_callback(self, outdata: np.ndarray, frames, times, status):
|
||||
try:
|
||||
if self.mon_wav is None:
|
||||
return
|
||||
mon_wav = self.mon_wav
|
||||
outputChannels = outdata.shape[1]
|
||||
outdata[:] = np.repeat(mon_wav, outputChannels).reshape(-1, outputChannels) / 32768.0
|
||||
outdata[:] = outdata * self.settings.serverOutputAudioGain # GainはOutputのものをを流用
|
||||
# Monitorモードが有効の場合はサンプリングレートはmonitorデバイスが優先されているためリサンプリング不要
|
||||
except Exception as e:
|
||||
print("[Voice Changer][ServerDevice][audioMonitor_callback] ex:", e)
|
||||
# import traceback
|
||||
# traceback.print_exc()
|
||||
|
||||
def start(self):
|
||||
currentModelSamplingRate = -1
|
||||
while True:
|
||||
if self.settings.serverAudioStated == 0 or self.settings.serverInputDeviceId == -1:
|
||||
time.sleep(2)
|
||||
else:
|
||||
sd._terminate()
|
||||
sd._initialize()
|
||||
|
||||
# Curret Device ID
|
||||
currentServerInputDeviceId = self.settings.serverInputDeviceId
|
||||
currentServerOutputDeviceId = self.settings.serverOutputDeviceId
|
||||
currentServerMonitorDeviceId = self.settings.serverMonitorDeviceId
|
||||
|
||||
# Device 特定
|
||||
serverInputAudioDevice = self.getServerInputAudioDevice(self.settings.serverInputDeviceId)
|
||||
serverOutputAudioDevice = self.getServerOutputAudioDevice(self.settings.serverOutputDeviceId)
|
||||
serverMonitorAudioDevice = None
|
||||
if self.settings.serverMonitorDeviceId != -1:
|
||||
serverMonitorAudioDevice = self.getServerOutputAudioDevice(self.settings.serverMonitorDeviceId)
|
||||
|
||||
# Generate ExtraSetting
|
||||
inputExtraSetting = None
|
||||
outputExtraSetting = None
|
||||
if self.settings.exclusiveMode:
|
||||
if "WASAPI" in serverInputAudioDevice.hostAPI:
|
||||
inputExtraSetting = sd.WasapiSettings(exclusive=True)
|
||||
if "WASAPI" in serverOutputAudioDevice.hostAPI:
|
||||
outputExtraSetting = sd.WasapiSettings(exclusive=True)
|
||||
monitorExtraSetting = None
|
||||
if self.settings.exclusiveMode and serverMonitorAudioDevice is not None:
|
||||
if "WASAPI" in serverMonitorAudioDevice.hostAPI:
|
||||
monitorExtraSetting = sd.WasapiSettings(exclusive=True)
|
||||
|
||||
print("Devices:")
|
||||
print(" [Input]:", serverInputAudioDevice, inputExtraSetting)
|
||||
print(" [Output]:", serverOutputAudioDevice, outputExtraSetting)
|
||||
print(" [Monitor]:", serverMonitorAudioDevice, monitorExtraSetting)
|
||||
|
||||
# Deviceがなかったらいったんスリープ
|
||||
if serverInputAudioDevice is None or serverOutputAudioDevice is None:
|
||||
print("serverInputAudioDevice or serverOutputAudioDevice is None")
|
||||
time.sleep(2)
|
||||
continue
|
||||
|
||||
# サンプリングレート
|
||||
# 同一サンプリングレートに統一(変換時にサンプルが不足する場合があるため。パディング方法が明らかになれば、それぞれ設定できるかも)
|
||||
currentAudioSampleRate = self.settings.serverAudioSampleRate
|
||||
try:
|
||||
currentModelSamplingRate = self.serverDeviceCallbacks.get_processing_sampling_rate()
|
||||
except Exception as e:
|
||||
print("[Voice Changer] ex: get_processing_sampling_rate", e)
|
||||
time.sleep(2)
|
||||
continue
|
||||
|
||||
self.settings.serverInputAudioSampleRate = currentAudioSampleRate
|
||||
self.settings.serverOutputAudioSampleRate = currentAudioSampleRate
|
||||
self.settings.serverMonitorAudioSampleRate = currentAudioSampleRate
|
||||
|
||||
# Sample Rate Check
|
||||
inputAudioSampleRateAvailable = checkSamplingRate(self.settings.serverInputDeviceId, self.settings.serverInputAudioSampleRate, "input")
|
||||
outputAudioSampleRateAvailable = checkSamplingRate(self.settings.serverOutputDeviceId, self.settings.serverOutputAudioSampleRate, "output")
|
||||
monitorAudioSampleRateAvailable = checkSamplingRate(self.settings.serverMonitorDeviceId, self.settings.serverMonitorAudioSampleRate, "output") if serverMonitorAudioDevice else True
|
||||
|
||||
print("Sample Rate:")
|
||||
print(f" [Model]: {currentModelSamplingRate}")
|
||||
print(f" [Input]: {self.settings.serverInputAudioSampleRate} -> {inputAudioSampleRateAvailable}")
|
||||
print(f" [Output]: {self.settings.serverOutputAudioSampleRate} -> {outputAudioSampleRateAvailable}")
|
||||
if serverMonitorAudioDevice is not None:
|
||||
print(f" [Monitor]: {self.settings.serverMonitorAudioSampleRate} -> {monitorAudioSampleRateAvailable}")
|
||||
|
||||
if inputAudioSampleRateAvailable and outputAudioSampleRateAvailable and monitorAudioSampleRateAvailable:
|
||||
pass
|
||||
else:
|
||||
print("Sample Rate is not supported by device:")
|
||||
print("Checking Available Sample Rate:")
|
||||
availableInputSampleRate = []
|
||||
availableOutputSampleRate = []
|
||||
availableMonitorSampleRate = []
|
||||
for sr in SERVER_DEVICE_SAMPLE_RATES:
|
||||
if checkSamplingRate(self.settings.serverInputDeviceId, sr, "input"):
|
||||
availableInputSampleRate.append(sr)
|
||||
if checkSamplingRate(self.settings.serverOutputDeviceId, sr, "output"):
|
||||
availableOutputSampleRate.append(sr)
|
||||
if serverMonitorAudioDevice is not None:
|
||||
if checkSamplingRate(self.settings.serverMonitorDeviceId, sr, "output"):
|
||||
availableMonitorSampleRate.append(sr)
|
||||
print("Available Sample Rate:")
|
||||
print(f" [Input]: {availableInputSampleRate}")
|
||||
print(f" [Output]: {availableOutputSampleRate}")
|
||||
if serverMonitorAudioDevice is not None:
|
||||
print(f" [Monitor]: {availableMonitorSampleRate}")
|
||||
|
||||
print("continue... ")
|
||||
time.sleep(2)
|
||||
continue
|
||||
|
||||
self.serverDeviceCallbacks.setInputSamplingRate(self.settings.serverInputAudioSampleRate)
|
||||
self.serverDeviceCallbacks.setOutputSamplingRate(self.settings.serverOutputAudioSampleRate)
|
||||
|
||||
# Blockサイズを計算
|
||||
currentInputChunkNum = self.settings.serverReadChunkSize
|
||||
block_frame = currentInputChunkNum * 128
|
||||
sd.default.blocksize = block_frame
|
||||
|
||||
# main loop
|
||||
try:
|
||||
with sd.InputStream(
|
||||
callback=self.audioInput_callback,
|
||||
dtype="float32",
|
||||
device=self.settings.serverInputDeviceId,
|
||||
blocksize=block_frame,
|
||||
samplerate=self.settings.serverInputAudioSampleRate,
|
||||
channels=serverInputAudioDevice.maxInputChannels,
|
||||
extra_settings=inputExtraSetting
|
||||
):
|
||||
with sd.OutputStream(
|
||||
callback=self.audioOutput_callback,
|
||||
dtype="float32",
|
||||
device=self.settings.serverOutputDeviceId,
|
||||
blocksize=block_frame,
|
||||
samplerate=self.settings.serverOutputAudioSampleRate,
|
||||
channels=serverOutputAudioDevice.maxOutputChannels,
|
||||
extra_settings=outputExtraSetting
|
||||
):
|
||||
if self.settings.serverMonitorDeviceId != -1:
|
||||
with sd.OutputStream(
|
||||
callback=self.audioMonitor_callback,
|
||||
dtype="float32",
|
||||
device=self.settings.serverMonitorDeviceId,
|
||||
blocksize=block_frame,
|
||||
samplerate=self.settings.serverMonitorAudioSampleRate,
|
||||
channels=serverMonitorAudioDevice.maxOutputChannels,
|
||||
extra_settings=monitorExtraSetting
|
||||
):
|
||||
while (
|
||||
self.settings.serverAudioStated == 1 and
|
||||
currentServerInputDeviceId == self.settings.serverInputDeviceId and
|
||||
currentServerOutputDeviceId == self.settings.serverOutputDeviceId and
|
||||
currentServerMonitorDeviceId == self.settings.serverMonitorDeviceId and
|
||||
currentModelSamplingRate == self.serverDeviceCallbacks.get_processing_sampling_rate() and
|
||||
currentInputChunkNum == self.settings.serverReadChunkSize and
|
||||
currentAudioSampleRate == self.settings.serverAudioSampleRate
|
||||
):
|
||||
time.sleep(2)
|
||||
print(f"[Voice Changer] server audio performance {self.performance}")
|
||||
print(f" status: started:{self.settings.serverAudioStated}, model_sr:{currentModelSamplingRate}, chunk:{currentInputChunkNum}")
|
||||
print(f" input : id:{self.settings.serverInputDeviceId}, sr:{self.settings.serverInputAudioSampleRate}, ch:{serverInputAudioDevice.maxInputChannels}")
|
||||
print(f" output : id:{self.settings.serverOutputDeviceId}, sr:{self.settings.serverOutputAudioSampleRate}, ch:{serverOutputAudioDevice.maxOutputChannels}")
|
||||
print(f" monitor: id:{self.settings.serverMonitorDeviceId}, sr:{self.settings.serverMonitorAudioSampleRate}, ch:{serverMonitorAudioDevice.maxOutputChannels}")
|
||||
else:
|
||||
while (
|
||||
self.settings.serverAudioStated == 1 and
|
||||
currentServerInputDeviceId == self.settings.serverInputDeviceId and
|
||||
currentServerOutputDeviceId == self.settings.serverOutputDeviceId and
|
||||
currentServerMonitorDeviceId == self.settings.serverMonitorDeviceId and
|
||||
currentModelSamplingRate == self.serverDeviceCallbacks.get_processing_sampling_rate() and
|
||||
currentInputChunkNum == self.settings.serverReadChunkSize and
|
||||
currentAudioSampleRate == self.settings.serverAudioSampleRate
|
||||
):
|
||||
time.sleep(2)
|
||||
print(f"[Voice Changer] server audio performance {self.performance}")
|
||||
print(f" status: started:{self.settings.serverAudioStated}, model_sr:{currentModelSamplingRate}, chunk:{currentInputChunkNum}]")
|
||||
print(f" input : id:{self.settings.serverInputDeviceId}, sr:{self.settings.serverInputAudioSampleRate}, ch:{serverInputAudioDevice.maxInputChannels}")
|
||||
print(f" output : id:{self.settings.serverOutputDeviceId}, sr:{self.settings.serverOutputAudioSampleRate}, ch:{serverOutputAudioDevice.maxOutputChannels}")
|
||||
except Exception as e:
|
||||
print("[Voice Changer] processing, ex:", e)
|
||||
time.sleep(2)
|
||||
|
||||
def start2(self):
|
||||
# currentInputDeviceId = -1
|
||||
# currentOutputDeviceId = -1
|
||||
# currentInputChunkNum = -1
|
||||
@ -140,13 +379,15 @@ class ServerDevice:
|
||||
):
|
||||
pass
|
||||
self.settings.serverInputAudioSampleRate = currentModelSamplingRate
|
||||
self.serverDeviceCallbacks.setSamplingRate(currentModelSamplingRate)
|
||||
self.serverDeviceCallbacks.setInputSamplingRate(currentModelSamplingRate)
|
||||
self.serverDeviceCallbacks.setOutputSamplingRate(currentModelSamplingRate)
|
||||
print(f"[Voice Changer] sample rate {self.settings.serverInputAudioSampleRate}")
|
||||
except Exception as e:
|
||||
print("[Voice Changer] ex: fallback to device default samplerate", e)
|
||||
print("[Voice Changer] device default samplerate", serverInputAudioDevice.default_samplerate)
|
||||
self.settings.serverInputAudioSampleRate = round(serverInputAudioDevice.default_samplerate)
|
||||
self.serverDeviceCallbacks.setSamplingRate(round(serverInputAudioDevice.default_samplerate))
|
||||
self.serverDeviceCallbacks.setInputSamplingRate(round(serverInputAudioDevice.default_samplerate))
|
||||
self.serverDeviceCallbacks.setOutputSamplingRate(round(serverInputAudioDevice.default_samplerate))
|
||||
|
||||
sd.default.samplerate = self.settings.serverInputAudioSampleRate
|
||||
sd.default.blocksize = block_frame
|
||||
@ -171,10 +412,15 @@ class ServerDevice:
|
||||
|
||||
def get_info(self):
|
||||
data = asdict(self.settings)
|
||||
audioinput, audiooutput = list_audio_device()
|
||||
data["serverAudioInputDevices"] = audioinput
|
||||
data["serverAudioOutputDevices"] = audiooutput
|
||||
try:
|
||||
audioinput, audiooutput = list_audio_device()
|
||||
self.serverAudioInputDevices = audioinput
|
||||
self.serverAudioOutputDevices = audiooutput
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
data["serverAudioInputDevices"] = self.serverAudioInputDevices
|
||||
data["serverAudioOutputDevices"] = self.serverAudioOutputDevices
|
||||
return data
|
||||
|
||||
def update_settings(self, key: str, val: str | int | float):
|
||||
|
@ -77,7 +77,7 @@ class DeviceManager(object):
|
||||
def getDeviceMemory(self, id: int):
|
||||
try:
|
||||
return torch.cuda.get_device_properties(id).total_memory
|
||||
# except Exception as e:
|
||||
except:
|
||||
# print(e)
|
||||
except Exception as e:
|
||||
# except:
|
||||
print(e)
|
||||
return 0
|
||||
|
@ -13,7 +13,7 @@ from voice_changer.IORecorder import IORecorder
|
||||
from voice_changer.utils.LoadModelParams import LoadModelParams
|
||||
|
||||
from voice_changer.utils.Timer import Timer
|
||||
from voice_changer.utils.VoiceChangerModel import AudioInOut
|
||||
from voice_changer.utils.VoiceChangerModel import AudioInOut, VoiceChangerModel
|
||||
from Exceptions import (
|
||||
DeviceCannotSupportHalfPrecisionException,
|
||||
DeviceChangingException,
|
||||
@ -32,6 +32,7 @@ STREAM_OUTPUT_FILE = os.path.join(TMP_DIR, "out.wav")
|
||||
@dataclass
|
||||
class VoiceChangerSettings:
|
||||
inputSampleRate: int = 48000 # 48000 or 24000
|
||||
outputSampleRate: int = 48000 # 48000 or 24000
|
||||
|
||||
crossFadeOffsetRate: float = 0.1
|
||||
crossFadeEndRate: float = 0.9
|
||||
@ -71,7 +72,7 @@ class VoiceChanger:
|
||||
self.currentCrossFadeOverlapSize = 0 # setting
|
||||
self.crossfadeSize = 0 # calculated
|
||||
|
||||
self.voiceChanger = None
|
||||
self.voiceChanger: VoiceChangerModel | None = None
|
||||
self.modelType: ModelType | None = None
|
||||
self.params = params
|
||||
self.gpu_num = torch.cuda.device_count()
|
||||
@ -115,6 +116,7 @@ class VoiceChanger:
|
||||
|
||||
if key == "serverAudioStated" and val == 0:
|
||||
self.settings.inputSampleRate = 48000
|
||||
self.settings.outputSampleRate = 48000
|
||||
|
||||
if key in self.settings.intData:
|
||||
setattr(self.settings, key, int(val))
|
||||
@ -123,7 +125,7 @@ class VoiceChanger:
|
||||
if key == "recordIO" and val == 1:
|
||||
if hasattr(self, "ioRecorder"):
|
||||
self.ioRecorder.close()
|
||||
self.ioRecorder = IORecorder(STREAM_INPUT_FILE, STREAM_OUTPUT_FILE, self.settings.inputSampleRate)
|
||||
self.ioRecorder = IORecorder(STREAM_INPUT_FILE, STREAM_OUTPUT_FILE, self.settings.inputSampleRate, self.settings.outputSampleRate)
|
||||
if key == "recordIO" and val == 0:
|
||||
if hasattr(self, "ioRecorder"):
|
||||
self.ioRecorder.close()
|
||||
@ -268,10 +270,10 @@ class VoiceChanger:
|
||||
# 後処理
|
||||
with Timer("post-process") as t:
|
||||
result = result.astype(np.int16)
|
||||
if self.settings.inputSampleRate != processing_sampling_rate:
|
||||
if self.settings.outputSampleRate != processing_sampling_rate:
|
||||
# print(
|
||||
# "samplingrate",
|
||||
# self.settings.inputSampleRate,
|
||||
# "output samplingrate",
|
||||
# self.settings.outputSampleRate,
|
||||
# processing_sampling_rate,
|
||||
# )
|
||||
outputData = cast(
|
||||
@ -279,13 +281,13 @@ class VoiceChanger:
|
||||
resampy.resample(
|
||||
result,
|
||||
processing_sampling_rate,
|
||||
self.settings.inputSampleRate,
|
||||
self.settings.outputSampleRate,
|
||||
).astype(np.int16),
|
||||
)
|
||||
else:
|
||||
outputData = result
|
||||
|
||||
print_convert_processing(f" Output data size of {result.shape[0]}/{processing_sampling_rate}hz {outputData.shape[0]}/{self.settings.inputSampleRate}hz")
|
||||
print_convert_processing(f" Output data size of {result.shape[0]}/{processing_sampling_rate}hz {outputData.shape[0]}/{self.settings.outputSampleRate}hz")
|
||||
|
||||
if receivedData.shape[0] != outputData.shape[0]:
|
||||
# print(
|
||||
|
@ -50,9 +50,12 @@ class VoiceChangerManager(ServerDeviceCallbacks):
|
||||
def get_processing_sampling_rate(self):
|
||||
return self.voiceChanger.get_processing_sampling_rate()
|
||||
|
||||
def setSamplingRate(self, sr: int):
|
||||
def setInputSamplingRate(self, sr: int):
|
||||
self.voiceChanger.settings.inputSampleRate = sr
|
||||
|
||||
def setOutputSamplingRate(self, sr: int):
|
||||
self.voiceChanger.settings.outputSampleRate = sr
|
||||
|
||||
############################
|
||||
# VoiceChangerManager
|
||||
############################
|
||||
@ -78,7 +81,7 @@ class VoiceChangerManager(ServerDeviceCallbacks):
|
||||
self.update_settings(key, val)
|
||||
|
||||
def store_setting(self, key: str, val: str | int | float):
|
||||
saveItemForServerDevice = ["enableServerAudio", "serverInputDeviceId", "serverOutputDeviceId", "serverReadChunkSize", "serverInputAudioGain", "serverOutputAudioGain"]
|
||||
saveItemForServerDevice = ["enableServerAudio", "serverAudioSampleRate", "serverInputDeviceId", "serverOutputDeviceId", "serverMonitorDeviceId", "serverReadChunkSize", "serverInputAudioGain", "serverOutputAudioGain"]
|
||||
saveItemForVoiceChanger = ["crossFadeOffsetRate", "crossFadeEndRate", "crossFadeOverlapSize"]
|
||||
saveItemForVoiceChangerManager = ["modelSlotIndex"]
|
||||
saveItemForRVC = ["extraConvertSize", "gpu", "silentThreshold"]
|
||||
|
Loading…
x
Reference in New Issue
Block a user