mirror of
https://github.com/w-okada/voice-changer.git
synced 2025-02-03 00:33:57 +03:00
add gain control
This commit is contained in:
parent
59a56d5507
commit
b87f7983b3
11
client/demo/dist/index.html
vendored
11
client/demo/dist/index.html
vendored
@ -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>
|
||||
|
597
client/demo/dist/index.js
vendored
597
client/demo/dist/index.js
vendored
File diff suppressed because one or more lines are too long
31
client/demo/dist/index.js.LICENSE.txt
vendored
31
client/demo/dist/index.js.LICENSE.txt
vendored
@ -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.
|
||||
*/
|
@ -151,7 +151,7 @@ export const useSpeakerSetting = (props: UseSpeakerSettingProps) => {
|
||||
</div>
|
||||
{srcIdRow}
|
||||
{dstIdRow}
|
||||
{editSpeakerIdMappingRow}
|
||||
{/* {editSpeakerIdMappingRow} */}
|
||||
{f0FactorRow}
|
||||
</>
|
||||
)
|
||||
|
@ -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 (
|
||||
|
@ -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%;
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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 = {
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user