mirror of
https://github.com/w-okada/voice-changer.git
synced 2025-01-23 13:35:12 +03:00
quality control update
This commit is contained in:
parent
25a673b66f
commit
47489571da
4
client/demo/dist/index.js
vendored
4
client/demo/dist/index.js
vendored
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
import { BufferSize, DownSamplingMode, F0Detector, Protocol, SampleRate, VoiceChangerMode } from "@dannadori/voice-changer-client-js"
|
||||
import React, { useMemo, useState } from "react"
|
||||
import React, { useEffect, useMemo, useState } from "react"
|
||||
import { ClientState } from "@dannadori/voice-changer-client-js";
|
||||
|
||||
|
||||
@ -10,9 +10,31 @@ export type UseQualityControlProps = {
|
||||
export type QualityControlState = {
|
||||
qualityControl: JSX.Element;
|
||||
}
|
||||
const reloadDevices = async () => {
|
||||
try {
|
||||
const ms = await navigator.mediaDevices.getUserMedia({ video: false, audio: true });
|
||||
ms.getTracks().forEach(x => { x.stop() })
|
||||
} catch (e) {
|
||||
console.warn("Enumerate device error::", e)
|
||||
}
|
||||
const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices();
|
||||
const audioOutputs = mediaDeviceInfos.filter(x => { return x.kind == "audiooutput" })
|
||||
|
||||
return audioOutputs
|
||||
}
|
||||
|
||||
|
||||
export const useQualityControl = (props: UseQualityControlProps): QualityControlState => {
|
||||
const [showQualityControl, setShowQualityControl] = useState<boolean>(false)
|
||||
const [outputAudioDeviceInfo, setOutputAudioDeviceInfo] = useState<MediaDeviceInfo[]>([])
|
||||
const [audioOutputForGUI, setAudioOutputForGUI] = useState<string>("none")
|
||||
useEffect(() => {
|
||||
const initialize = async () => {
|
||||
const audioInfo = await reloadDevices()
|
||||
setOutputAudioDeviceInfo(audioInfo)
|
||||
}
|
||||
initialize()
|
||||
}, [])
|
||||
|
||||
|
||||
const noiseControlRow = useMemo(() => {
|
||||
@ -94,23 +116,90 @@ export const useQualityControl = (props: UseQualityControlProps): QualityControl
|
||||
|
||||
|
||||
const recordIORow = useMemo(() => {
|
||||
const setReocrdIO = async (val: number) => {
|
||||
await props.clientState.serverSetting.setRecordIO(val)
|
||||
if (val == 0) {
|
||||
const imageContainer = document.getElementById("quality-control-analyze-image-container") as HTMLDivElement
|
||||
imageContainer.innerHTML = ""
|
||||
const image = document.createElement("img")
|
||||
image.src = "/tmp/analyze-dio.png?" + new Date().getTime()
|
||||
imageContainer.appendChild(image)
|
||||
const image2 = document.createElement("img")
|
||||
image2.src = "/tmp/analyze-harvest.png?" + new Date().getTime()
|
||||
imageContainer.appendChild(image2)
|
||||
|
||||
const wavContainer = document.getElementById("quality-control-analyze-wav-container") as HTMLDivElement
|
||||
wavContainer.innerHTML = ""
|
||||
const media1 = document.createElement("audio") as HTMLAudioElement
|
||||
media1.src = "/tmp/in.wav?" + new Date().getTime()
|
||||
media1.controls = true
|
||||
// @ts-ignore
|
||||
media1.setSinkId(audioOutputForGUI)
|
||||
wavContainer.appendChild(media1)
|
||||
const media2 = document.createElement("audio") as HTMLAudioElement
|
||||
media2.src = "/tmp/out.wav?" + new Date().getTime()
|
||||
media2.controls = true
|
||||
// @ts-ignore
|
||||
media2.setSinkId(audioOutputForGUI)
|
||||
wavContainer.appendChild(media2)
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className="body-row split-3-7 left-padding-1 guided">
|
||||
<div className="body-item-title left-padding-1 ">recordIO</div>
|
||||
<div className="body-select-container">
|
||||
<select className="body-select" value={props.clientState.serverSetting.setting.recordIO} onChange={(e) => {
|
||||
props.clientState.serverSetting.setRecordIO(Number(e.target.value))
|
||||
}}>
|
||||
{
|
||||
Object.values([0, 1]).map(x => {
|
||||
return <option key={x} value={x}>{x}</option>
|
||||
})
|
||||
}
|
||||
</select>
|
||||
<>
|
||||
<div className="body-row split-3-7 left-padding-1 guided">
|
||||
<div className="body-item-title left-padding-1 ">recordIO</div>
|
||||
<div className="body-select-container">
|
||||
<select className="body-select" value={props.clientState.serverSetting.setting.recordIO} onChange={(e) => {
|
||||
setReocrdIO(Number(e.target.value))
|
||||
}}>
|
||||
{
|
||||
Object.values([0, 1]).map(x => {
|
||||
return <option key={x} value={x}>{x}</option>
|
||||
})
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="body-row split-3-7 left-padding-1 guided">
|
||||
<div className="body-item-title left-padding-1 ">
|
||||
<div>
|
||||
Spectrogram
|
||||
</div>
|
||||
<div>
|
||||
<span>(left: dio, right:harvest)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="body-image-container-quality-analyze" id="quality-control-analyze-image-container">
|
||||
</div>
|
||||
</div>
|
||||
<div className="body-row split-3-7 left-padding-1 guided">
|
||||
<div className="body-item-title left-padding-1 ">
|
||||
<div>
|
||||
wav (left:input, right:output)
|
||||
</div>
|
||||
<select className="body-select" value={audioOutputForGUI} onChange={(e) => {
|
||||
setAudioOutputForGUI(e.target.value)
|
||||
const wavContainer = document.getElementById("quality-control-analyze-wav-container") as HTMLDivElement
|
||||
wavContainer.childNodes.forEach(x => {
|
||||
if (x instanceof HTMLAudioElement) {
|
||||
//@ts-ignore
|
||||
x.setSinkId(e.target.value)
|
||||
}
|
||||
})
|
||||
}}>
|
||||
{
|
||||
outputAudioDeviceInfo.map(x => {
|
||||
return <option key={x.deviceId} value={x.deviceId}>{x.label}</option>
|
||||
})
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
<div className="body-wav-container-quality-analyze" id="quality-control-analyze-wav-container">
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}, [props.clientState.serverSetting.setting.recordIO, props.clientState.serverSetting.setRecordIO])
|
||||
}, [props.clientState.serverSetting.setting.recordIO, props.clientState.serverSetting.setRecordIO, outputAudioDeviceInfo, audioOutputForGUI])
|
||||
|
||||
const QualityControlContent = useMemo(() => {
|
||||
if (!showQualityControl) return <></>
|
||||
|
@ -444,8 +444,16 @@ body {
|
||||
}
|
||||
}
|
||||
}
|
||||
.body-select-container {
|
||||
.body-select {
|
||||
color: rgb(30, 30, 30);
|
||||
.body-select {
|
||||
color: rgb(30, 30, 30);
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.body-image-container-quality-analyze,
|
||||
.body-wav-container-quality-analyze {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
& > img {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,13 @@ providers = ['OpenVINOExecutionProvider', "CUDAExecutionProvider", "DmlExecution
|
||||
|
||||
import wave
|
||||
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import pylab
|
||||
import librosa
|
||||
import librosa.display
|
||||
SAMPLING_RATE = 24000
|
||||
|
||||
|
||||
class MockStream:
|
||||
"""
|
||||
@ -194,6 +201,18 @@ class VoiceChanger():
|
||||
|
||||
return data
|
||||
|
||||
def _get_f0_dio(self, y, sr=SAMPLING_RATE):
|
||||
_f0, time = pw.dio(y, sr, frame_period=5)
|
||||
f0 = pw.stonemask(y, _f0, time, sr)
|
||||
time = np.linspace(0, y.shape[0] / sr, len(time))
|
||||
return f0, time
|
||||
|
||||
def _get_f0_harvest(self, y, sr=SAMPLING_RATE):
|
||||
_f0, time = pw.harvest(y, sr, frame_period=5)
|
||||
f0 = pw.stonemask(y, _f0, time, sr)
|
||||
time = np.linspace(0, y.shape[0] / sr, len(time))
|
||||
return f0, time
|
||||
|
||||
def update_setteings(self, key: str, val: any):
|
||||
if key == "onnxExecutionProvider" and self.onnx_session != None:
|
||||
if val == "CUDAExecutionProvider":
|
||||
@ -215,6 +234,32 @@ class VoiceChanger():
|
||||
self.unpackedData_length = 0
|
||||
if key == "recordIO" and val == 1:
|
||||
self._setupRecordIO()
|
||||
if key == "recordIO" and val == 0:
|
||||
try:
|
||||
stream_input_file = os.path.join(TMP_DIR, "in.wav")
|
||||
analyze_file_dio = os.path.join(TMP_DIR, "analyze-dio.png")
|
||||
analyze_file_harvest = os.path.join(TMP_DIR, "analyze-harvest.png")
|
||||
y, sr = librosa.load(stream_input_file, SAMPLING_RATE)
|
||||
y = y.astype(np.float64)
|
||||
spec = librosa.amplitude_to_db(np.abs(librosa.stft(y, n_fft=2048, win_length=2048, hop_length=128)), ref=np.max)
|
||||
f0_dio, times = self._get_f0_dio(y)
|
||||
f0_harvest, times = self._get_f0_harvest(y)
|
||||
|
||||
pylab.close()
|
||||
HOP_LENGTH = 128
|
||||
img = librosa.display.specshow(spec, sr=SAMPLING_RATE, hop_length=HOP_LENGTH, x_axis='time', y_axis='log', )
|
||||
pylab.plot(times, f0_dio, label='f0', color=(0, 1, 1, 0.6), linewidth=3)
|
||||
pylab.savefig(analyze_file_dio)
|
||||
|
||||
pylab.close()
|
||||
HOP_LENGTH = 128
|
||||
img = librosa.display.specshow(spec, sr=SAMPLING_RATE, hop_length=HOP_LENGTH, x_axis='time', y_axis='log', )
|
||||
pylab.plot(times, f0_harvest, label='f0', color=(0, 1, 1, 0.6), linewidth=3)
|
||||
pylab.savefig(analyze_file_harvest)
|
||||
|
||||
except Exception as e:
|
||||
print("recordIO exception", e)
|
||||
|
||||
elif key in self.settings.floatData:
|
||||
setattr(self.settings, key, float(val))
|
||||
elif key in self.settings.strData:
|
||||
|
Loading…
Reference in New Issue
Block a user