add gain control

This commit is contained in:
wataru 2023-02-12 18:19:22 +09:00
parent 59a56d5507
commit b87f7983b3
9 changed files with 771 additions and 41 deletions

View File

@ -1 +1,10 @@
<!doctype html><html style="width:100%;height:100%;overflow:hidden"><head><meta charset="utf-8"/><title>Voice Changer Client Demo</title><script defer="defer" src="index.js"></script></head><body style="width:100%;height:100%;margin:0"><div id="app" style="width:100%;height:100%"></div></body></html>
<!DOCTYPE html>
<html style="width: 100%; height: 100%; overflow: hidden">
<head>
<meta charset="utf-8" />
<title>Voice Changer Client Demo</title>
<script defer src="index.js"></script></head>
<body style="width: 100%; height: 100%; margin: 0px">
<div id="app" style="width: 100%; height: 100%"></div>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -1,31 +0,0 @@
/*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
/**
* @license React
* react-dom.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* @license React
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

View File

@ -151,7 +151,7 @@ export const useSpeakerSetting = (props: UseSpeakerSettingProps) => {
</div>
{srcIdRow}
{dstIdRow}
{editSpeakerIdMappingRow}
{/* {editSpeakerIdMappingRow} */}
{f0FactorRow}
</>
)

View File

@ -23,18 +23,38 @@ export const useServerControl = (props: UseServerControlProps) => {
const stopClassName = isStarted ? "body-button-stanby" : "body-button-active"
return (
<div className="body-row split-3-3-4 left-padding-1 guided">
<div className="body-row split-3-2-2-3 left-padding-1 guided">
<div className="body-item-title left-padding-1">Start</div>
<div className="body-button-container">
<div onClick={onStartClicked} className={startClassName}>start</div>
<div onClick={onStopClicked} className={stopClassName}>stop</div>
</div>
<div>
<div className="body-input-container split-4-4-2">
<div>gain(in)</div>
<input type="range" className="body-item-input" min="0.0" max="1.0" step="0.1" value={props.clientState.clientSetting.setting.inputGain} onChange={(e) => {
props.clientState.clientSetting.setInputGain(Number(e.target.value))
}}></input>
<div>{props.clientState.clientSetting.setting.inputGain}</div>
</div>
<div className="body-input-container split-4-4-2">
<div>gain(out)</div>
<input type="range" className="body-item-input" min="0.0" max="1.0" step="0.1" value={props.clientState.clientSetting.setting.outputGain} onChange={(e) => {
props.clientState.clientSetting.setOutputGain(Number(e.target.value))
}}></input>
<div>{props.clientState.clientSetting.setting.outputGain}</div>
</div>
</div>
<div className="body-input-container">
</div>
</div>
)
}, [isStarted, props.clientState.clientSetting.start, props.clientState.clientSetting.stop])
}, [isStarted, props.clientState.clientSetting.start, props.clientState.clientSetting.stop,
props.clientState.clientSetting.setInputGain, props.clientState.clientSetting.setting.inputGain,
props.clientState.clientSetting.setOutputGain, props.clientState.clientSetting.setting.outputGain
])
const performanceRow = useMemo(() => {
return (

View File

@ -125,6 +125,87 @@ body {
width: 40%;
}
}
.split-2-5-3 {
display: flex;
width: 100%;
justify-content: center;
margin: 1px 0px 1px 0px;
& > div:nth-child(1) {
left: 0px;
width: 20%;
}
& > div:nth-child(2) {
left: 20%;
width: 50%;
}
& > div:nth-child(3) {
left: 70%;
width: 30%;
}
}
.split-4-4-2 {
display: flex;
width: 100%;
justify-content: center;
margin: 1px 0px 1px 0px;
& > div:nth-child(1) {
left: 0px;
width: 40%;
}
& > div:nth-child(2) {
left: 40%;
width: 40%;
}
& > div:nth-child(3) {
left: 80%;
width: 20%;
}
}
.split-3-2-2-3 {
display: flex;
width: 100%;
justify-content: center;
margin: 1px 0px 1px 0px;
& > div:nth-child(1) {
left: 0px;
width: 30%;
}
& > div:nth-child(2) {
left: 30%;
width: 20%;
}
& > div:nth-child(3) {
left: 50%;
width: 20%;
}
& > div:nth-child(4) {
left: 70%;
width: 30%;
}
}
.split-3-2-3-2 {
display: flex;
width: 100%;
justify-content: center;
margin: 1px 0px 1px 0px;
& > div:nth-child(1) {
left: 0px;
width: 30%;
}
& > div:nth-child(2) {
left: 30%;
width: 20%;
}
& > div:nth-child(3) {
left: 50%;
width: 30%;
}
& > div:nth-child(4) {
left: 80%;
width: 20%;
}
}
.split-3-1-2-4 {
display: flex;
width: 100%;
@ -303,6 +384,9 @@ body {
color: rgb(30, 30, 30);
font-size: 0.7rem;
}
.body-input-container {
display: flex;
}
.body-item-input {
width: 90%;
}

View File

@ -24,11 +24,15 @@ export class VoiceChangerClient {
private currentMediaStream: MediaStream | null = null
private currentMediaStreamAudioSourceNode: MediaStreamAudioSourceNode | null = null
private outputNodeFromVF: MediaStreamAudioDestinationNode | null = null
private inputGainNode: GainNode | null = null
private outputGainNode: GainNode | null = null
private micStream: MicrophoneStream | null = null
private audioStreamer!: AudioStreamer
private vcNode!: VoiceChangerWorkletNode
private currentMediaStreamAudioDestinationNode!: MediaStreamAudioDestinationNode
private inputGain = 1.0
private promiseForInitialize: Promise<void>
private _isVoiceChanging = false
@ -76,7 +80,9 @@ export class VoiceChangerClient {
this.vcNode = new VoiceChangerWorkletNode(this.ctx, voiceChangerWorkletListener); // vc node
this.currentMediaStreamAudioDestinationNode = this.ctx.createMediaStreamDestination() // output node
this.vcNode.connect(this.currentMediaStreamAudioDestinationNode) // vc node -> output node
this.outputGainNode = this.ctx.createGain()
this.vcNode.connect(this.outputGainNode) // vc node -> output node
this.outputGainNode.connect(this.currentMediaStreamAudioDestinationNode)
// (vc nodeにはaudio streamerのcallbackでデータが投げ込まれる)
this.audioStreamer = new AudioStreamer(this.callbacks, audioStreamerListeners, { objectMode: true, })
this.audioStreamer.setInputChunkNum(DefaultVoiceChangerClientSetting.inputChunkNum)
@ -149,14 +155,21 @@ export class VoiceChangerClient {
// connect nodes.
if (this.currentDevice && forceVfDisable == false) {
this.currentMediaStreamAudioSourceNode = this.ctx.createMediaStreamSource(this.currentMediaStream) // input node
this.inputGainNode = this.ctx.createGain()
this.inputGainNode.gain.value = this.inputGain
this.currentMediaStreamAudioSourceNode.connect(this.inputGainNode)
this.currentDevice.chooseNewInnerDevice(this.currentMediaStream)
const voiceFocusNode = await this.currentDevice.createAudioNode(this.ctx); // vf node
this.currentMediaStreamAudioSourceNode.connect(voiceFocusNode.start) // input node -> vf node
this.inputGainNode.connect(voiceFocusNode.start) // input node -> vf node
voiceFocusNode.end.connect(this.outputNodeFromVF!)
this.micStream.setStream(this.outputNodeFromVF!.stream) // vf node -> mic stream
} else {
console.log("VF disabled")
this.micStream.setStream(this.currentMediaStream) // input device -> mic stream
this.inputGainNode = this.ctx.createGain()
this.inputGainNode.gain.value = this.inputGain
const inputDestinationNodeForMicStream = this.ctx.createMediaStreamDestination()
this.micStream.setStream(inputDestinationNodeForMicStream.stream) // input device -> mic stream
}
this.micStream.pipe(this.audioStreamer) // mic stream -> audio streamer
if (!this._isVoiceChanging) {
@ -252,6 +265,22 @@ export class VoiceChangerClient {
return this.configurator.updateSettings(key, val)
}
setInputGain = (val: number) => {
this.inputGain = val
if (!this.inputGainNode) {
return
}
this.inputGainNode.gain.value = val
}
setOutputGain = (val: number) => {
if (!this.outputGainNode) {
return
}
this.outputGainNode.gain.value = val
}
// Information
getClientSettings = () => {
return this.audioStreamer.getSettings()

View File

@ -35,6 +35,9 @@ export type VoiceChangerClientSetting = {
correspondences: Correspondence[],
forceVfDisable: boolean,
voiceChangerMode: VoiceChangerMode,
inputGain: number
outputGain: number
}
export type WorkletSetting = {
@ -183,6 +186,8 @@ export const DefaultVoiceChangerClientSetting: VoiceChangerClientSetting = {
correspondences: [],
forceVfDisable: false,
voiceChangerMode: "realtime",
inputGain: 1.0,
outputGain: 1.0
}
export const DefaultWorkletSetting: WorkletSetting = {

View File

@ -22,7 +22,8 @@ export type ClientSettingState = {
setSampleRate: (num: SampleRate) => void
setSpeakers: (speakers: Speaker[]) => void
setCorrespondences: (file: File | null) => Promise<void>
setInputGain: (val: number) => void
setOutputGain: (val: number) => void
start: () => Promise<void>
stop: () => Promise<void>
reloadClientSetting: () => Promise<void>
@ -64,6 +65,7 @@ export const useClientSetting = (props: UseClientSettingProps): ClientSettingSta
props.voiceChangerClient.setInputChunkNum(settingRef.current.inputChunkNum)
props.voiceChangerClient.setProtocol(settingRef.current.protocol)
props.voiceChangerClient.setVoiceChangerMode(settingRef.current.voiceChangerMode)
props.voiceChangerClient.setInputGain(settingRef.current.inputGain)
// Input, bufferSize, VoiceFocus Disableは_setInputで設定
_setInput()
@ -207,6 +209,23 @@ export const useClientSetting = (props: UseClientSettingProps): ClientSettingSta
}
}, [props.voiceChangerClient])
const setInputGain = useMemo(() => {
return (val: number) => {
if (!props.voiceChangerClient) return
props.voiceChangerClient.setInputGain(val)
settingRef.current.inputGain = val
setSetting({ ...settingRef.current })
}
}, [props.voiceChangerClient])
const setOutputGain = useMemo(() => {
return (val: number) => {
if (!props.voiceChangerClient) return
props.voiceChangerClient.setOutputGain(val)
settingRef.current.outputGain = val
setSetting({ ...settingRef.current })
}
}, [props.voiceChangerClient])
//////////////
// 操作
/////////////
@ -245,6 +264,8 @@ export const useClientSetting = (props: UseClientSettingProps): ClientSettingSta
setSampleRate,
setSpeakers,
setCorrespondences,
setInputGain,
setOutputGain,
start,
stop,