Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| window.speech2speechState = { | |
| isReadingMic: false, | |
| elapsedRecording: 0, | |
| s2s_running: false | |
| } | |
| // Populate the microphone dropdown with the available options | |
| navigator.mediaDevices.enumerateDevices().then(devices => { | |
| devices = devices.filter(device => device.kind=="audioinput" && device.deviceId!="default" && device.deviceId!="communications") | |
| devices.forEach(device => { | |
| const option = createElem("option", device.label) | |
| option.value = device.deviceId | |
| setting_mic_selection.appendChild(option) | |
| }) | |
| setting_mic_selection.addEventListener("change", () => { | |
| window.userSettings.microphone = setting_mic_selection.value | |
| window.saveUserSettings() | |
| window.initMic() | |
| }) | |
| if (Object.keys(window.userSettings).includes("microphone")) { | |
| setting_mic_selection.value = window.userSettings.microphone | |
| } else { | |
| window.userSettings.microphone = setting_mic_selection.value | |
| window.saveUserSettings() | |
| } | |
| }) | |
| window.initMic = () => { | |
| return new Promise(resolve => { | |
| const deviceId = window.userSettings.microphone | |
| navigator.mediaDevices.getUserMedia({audio: {deviceId: deviceId}}).then(stream => { | |
| const audio_context = new AudioContext | |
| const input = audio_context.createMediaStreamSource(stream) | |
| window.speech2speechState.stream = stream | |
| resolve() | |
| }).catch(err => { | |
| console.log(err) | |
| resolve() | |
| }) | |
| }) | |
| } | |
| window.initMic() | |
| const animateRecordingProgress = () => { | |
| const percentDone = (Date.now() - window.speech2speechState.elapsedRecording) / 10000 | |
| if (percentDone >= 1 && percentDone!=Infinity) { | |
| if (window.speech2speechState.isReadingMic) { | |
| window.stopRecord() | |
| } | |
| } else { | |
| const circle = mic_progress_SVG_circle | |
| const radius = circle.r.baseVal.value | |
| const circumference = radius * 2 * Math.PI | |
| const offset = circumference - percentDone * circumference | |
| circle.style.strokeDasharray = `${circumference} ${circumference}` | |
| circle.style.strokeDashoffset = circumference | |
| circle.style.strokeDashoffset = Math.round(offset) | |
| requestAnimationFrame(animateRecordingProgress) | |
| } | |
| } | |
| window.clearVCMicSpinnerProgress = (percent=0) => { | |
| const circle = mic_progress_SVG_circle | |
| circle.style.stroke = "transparent" | |
| const radius = circle.r.baseVal.value | |
| const circumference = radius * 2 * Math.PI | |
| const offset = circumference - percent * circumference | |
| circle.style.strokeDasharray = `${circumference} ${circumference}` | |
| circle.style.strokeDashoffset = circumference | |
| circle.style.strokeDashoffset = Math.floor(offset) | |
| } | |
| window.clearVCMicSpinnerProgress = clearVCMicSpinnerProgress | |
| window.startRecord = async () => { | |
| doFetch(`http://localhost:8008/start_microphone_recording`, { | |
| method: "Post" | |
| }) | |
| window.speech2speechState.isReadingMic = true | |
| window.speech2speechState.elapsedRecording = Date.now() | |
| window.clearVCMicSpinnerProgress() | |
| mic_progress_SVG_circle.style.stroke = "red" | |
| requestAnimationFrame(animateRecordingProgress) | |
| } | |
| window.outputS2SRecording = (outPath, callback) => { | |
| toggleSpinnerButtons() | |
| doFetch(`http://localhost:8008/move_recorded_file`, { | |
| method: "Post", | |
| body: JSON.stringify({ | |
| file_path: outPath | |
| }) | |
| }).then(r=>r.text()).then(res => { | |
| callback() | |
| }) | |
| } | |
| window.useWavFileForspeech2speech = (fileName) => { | |
| // let sequence = dialogueInput.value.trim().replace("β¦", "...") | |
| // For some reason, the samplePlay audio element does not update the source when the file name is the same | |
| const tempFileNum = `${Math.random().toString().split(".")[1]}` | |
| let tempFileLocation = `${path}/output/temp-${tempFileNum}.wav` | |
| let style_emb = window.currentModel.audioPreviewPath // Default to the preview audio file, if an embedding can't be found in the json - This shouldn't happen | |
| try { | |
| style_emb = window.currentModel.games[0].base_speaker_emb // If this fails, the json isn't complete | |
| } catch (e) { | |
| console.log(e) | |
| } | |
| if (window.wavesurfer) { | |
| window.wavesurfer.stop() | |
| wavesurferContainer.style.opacity = 0 | |
| } | |
| window.tempFileLocation = `${__dirname.replace("/javascript", "").replace("\\javascript", "")}/output/temp-${tempFileNum}.wav` | |
| const options = { | |
| hz: window.userSettings.audio.hz, | |
| padStart: window.userSettings.audio.padStart, | |
| padEnd: window.userSettings.audio.padEnd, | |
| bit_depth: window.userSettings.audio.bitdepth, | |
| amplitude: window.userSettings.audio.amplitude, | |
| pitchMult: window.userSettings.audio.pitchMult, | |
| tempo: window.userSettings.audio.tempo, | |
| deessing: window.userSettings.audio.deessing, | |
| nr: window.userSettings.audio.nr, | |
| nf: window.userSettings.audio.nf, | |
| useNR: window.userSettings.audio.useNR, | |
| useSR: useSRCkbx.checked, | |
| useCleanup: useCleanupCkbx.checked | |
| } | |
| doFetch(`http://localhost:8008/runSpeechToSpeech`, { | |
| method: "Post", | |
| body: JSON.stringify({ | |
| input_path: fileName, | |
| useSR: useSRCkbx.checked, | |
| useCleanup: useCleanupCkbx.checked, | |
| isBatchMode: false, | |
| style_emb: style_emb_select.value=="default" ? window.currentModel.games[0].base_speaker_emb : style_emb_select.value.split(",").map(v=>parseFloat(v)), | |
| audio_out_path: tempFileLocation, | |
| doPitchShift: window.userSettings.s2s_prePitchShift, | |
| removeNoise: window.userSettings.s2s_removeNoise, // Removed from UI | |
| removeNoiseStrength: window.userSettings.s2s_noiseRemStrength, // Removed from UI | |
| vc_strength: window.userSettings.vc_strength, | |
| n_speakers: undefined, | |
| modelPath: undefined, | |
| voiceId: undefined, | |
| options: JSON.stringify(options) | |
| }) | |
| }).then(r=>r.text()).then(res => { | |
| // This block of code sometimes gets called before the audio file has actually finished flushing to file | |
| // I need a better way to make sure that this doesn't get called until it IS finished, but "for now", | |
| // I've set up some recursive re-attempts, below doTheRest | |
| window.clearVCMicSpinnerProgress() | |
| mic_progress_SVG.style.animation = "none" | |
| if (res=="TOO_SHORT") { | |
| window.toggleSpinnerButtons(true) | |
| window.errorModal(`<h3>${window.i18n.VC_TOO_SHORT}</h3>`) | |
| return | |
| } | |
| generateVoiceButton.disabled = true | |
| let hasLoaded = false | |
| let numRetries = 0 | |
| window.toggleSpinnerButtons(true) | |
| const doTheRest = () => { | |
| if (hasLoaded) { | |
| return | |
| } | |
| // window.wavesurfer = undefined | |
| tempFileLocation = tempFileLocation.replaceAll(/\\/g, "/") | |
| tempFileLocation = tempFileLocation.replaceAll('/resources/app/resources/app', "/resources/app") | |
| tempFileLocation = tempFileLocation.replaceAll('/resources/app', "") | |
| dialogueInput.value = "" | |
| textEditorElem.innerHTML = "" | |
| window.isGenerating = false | |
| window.speech2speechState.s2s_running = true | |
| if (res.includes("Traceback")) { | |
| window.errorModal(`<h3>${window.i18n.SOMETHING_WENT_WRONG}</h3>${res.replaceAll("\n", "<br>")}`) | |
| } else if (res.includes("ERROR:APP_VERSION")) { | |
| const speech2speechModelVersion = "v"+res.split(",")[1] | |
| window.errorModal(`${window.i18n.ERR_XVASPEECH_MODEL_VERSION.replace("_1", speech2speechModelVersion)} ${window.appVersion}`) | |
| } else { | |
| keepSampleButton.disabled = false | |
| window.tempFileLocation = tempFileLocation | |
| // Wavesurfer | |
| if (!window.wavesurfer) { | |
| window.initWaveSurfer(window.tempFileLocation) | |
| } else { | |
| window.wavesurfer.load(window.tempFileLocation) | |
| } | |
| window.wavesurfer.on("ready", () => { | |
| hasLoaded = true | |
| wavesurferContainer.style.opacity = 1 | |
| if (window.userSettings.autoPlayGen) { | |
| if (window.userSettings.playChangedAudio) { | |
| const playbackStartEnd = window.sequenceEditor.getChangedTimeStamps(start_index, end_index, window.wavesurfer.getDuration()) | |
| if (playbackStartEnd) { | |
| wavesurfer.play(playbackStartEnd[0], playbackStartEnd[1]) | |
| } else { | |
| wavesurfer.play() | |
| } | |
| } else { | |
| wavesurfer.play() | |
| } | |
| window.sequenceEditor.adjustedLetters = new Set() | |
| samplePlayPause.innerHTML = window.i18n.PAUSE | |
| } | |
| }) | |
| // Persistance across sessions | |
| localStorage.setItem("tempFileLocation", tempFileLocation) | |
| generateVoiceButton.innerHTML = window.i18n.GENERATE_VOICE | |
| if (window.userSettings.s2s_autogenerate) { | |
| speech2speechState.s2s_autogenerate = true | |
| generateVoiceButton.click() | |
| } | |
| keepSampleButton.dataset.newFileLocation = `${window.userSettings[`outpath_${window.currentGame.gameId}`]}/${title.dataset.modelId}/vc_${tempFileNum}.wav` | |
| keepSampleButton.disabled = false | |
| keepSampleButton.style.display = "block" | |
| samplePlayPause.style.display = "block" | |
| setTimeout(doTheRest, 100) | |
| } | |
| } | |
| doTheRest() | |
| }).catch(e => { | |
| console.log(e) | |
| window.toggleSpinnerButtons(true) | |
| window.errorModal(`<h3>${window.i18n.SOMETHING_WENT_WRONG}</h3>`) | |
| mic_progress_SVG.style.animation = "none" | |
| }) | |
| } | |
| window.stopRecord = (cancelled) => { | |
| fs.writeFileSync(`${window.path}/python/temp_stop_recording`, "") | |
| if (!cancelled) { | |
| window.clearVCMicSpinnerProgress(0.35) | |
| mic_progress_SVG.style.animation = "spin 1.5s linear infinite" | |
| mic_progress_SVG_circle.style.stroke = "white" | |
| const fileName = `${__dirname.replace("\\javascript", "").replace("/javascript", "").replace(/\\/g,"/")}/output/recorded_file.wav` | |
| window.sequenceEditor.clear() | |
| window.outputS2SRecording(fileName, () => { | |
| window.useWavFileForspeech2speech(fileName) | |
| }) | |
| } | |
| window.speech2speechState.isReadingMic = false | |
| window.speech2speechState.elapsedRecording = 0 | |
| window.clearVCMicSpinnerProgress() | |
| } | |
| window.micClickHandler = (ctrlKey) => { | |
| if (window.speech2speechState.isReadingMic) { | |
| window.stopRecord() | |
| } else { | |
| if (window.currentModel && generateVoiceButton.innerHTML == window.i18n.GENERATE_VOICE) { | |
| if (window.currentModel.modelType.toLowerCase()=="xvapitch") { | |
| window.startRecord() | |
| } | |
| } else { | |
| window.errorModal(window.i18n.LOAD_TARGET_MODEL) | |
| } | |
| } | |
| } | |
| mic_SVG.addEventListener("mouseenter", () => { | |
| if (!window.currentModel || window.currentModel.modelType.toLowerCase()!="xvapitch") { | |
| s2s_voiceId_selected_label.style.display = "inline-block" | |
| } | |
| }) | |
| mic_SVG.addEventListener("mouseleave", () => { | |
| s2s_voiceId_selected_label.style.display = "none" | |
| }) | |
| mic_SVG.addEventListener("click", event => window.micClickHandler(event.ctrlKey)) | |
| mic_SVG.addEventListener("contextmenu", () => { | |
| if (window.speech2speechState.isReadingMic) { | |
| window.stopRecord(true) | |
| } else { | |
| const audioPreview = createElem("audio", {autoplay: false}, createElem("source", { | |
| src: `${__dirname.replace("\\javascript", "").replace("/javascript", "").replace(/\\/g,"/")}/output/recorded_file_post${window.userSettings.s2s_prePitchShift?"_praat":""}.wav` | |
| })) | |
| audioPreview.setSinkId(window.userSettings.base_speaker) | |
| } | |
| }) | |
| window.clearVCMicSpinnerProgress() | |
| // File dragging | |
| window.uploadS2SFile = (eType, event) => { | |
| if (["dragenter", "dragover"].includes(eType)) { | |
| clearVCMicSpinnerProgress(1) | |
| mic_progress_SVG_circle.style.stroke = "white" | |
| } | |
| if (["dragleave", "drop"].includes(eType)) { | |
| window.clearVCMicSpinnerProgress() | |
| } | |
| event.preventDefault() | |
| event.stopPropagation() | |
| if (eType=="drop") { | |
| if (window.currentModel && generateVoiceButton.innerHTML == window.i18n.GENERATE_VOICE) { | |
| const dataTransfer = event.dataTransfer | |
| const files = Array.from(dataTransfer.files) | |
| const file = files[0] | |
| if (!file.name.endsWith(".wav")) { | |
| window.errorModal(window.i18n.ONLY_WAV_S2S) | |
| return | |
| } | |
| clearVCMicSpinnerProgress(0.35) | |
| // mic_progress_SVG.style.animation = "spin 1.5s linear infinite" | |
| // mic_progress_SVG_circle.style.stroke = "white" | |
| const fileName = `${__dirname.replace("\\javascript", "").replace("/javascript", "").replace(/\\/g,"/")}/output/recorded_file.wav` | |
| fs.copyFileSync(file.path, fileName) | |
| window.sequenceEditor.clear() | |
| toggleSpinnerButtons() | |
| window.useWavFileForspeech2speech(fileName) | |
| } else { | |
| window.errorModal(window.i18n.LOAD_TARGET_MODEL) | |
| } | |
| } | |
| } | |
| micContainer.addEventListener("dragenter", event => window.uploadS2SFile("dragenter", event), false) | |
| micContainer.addEventListener("dragleave", event => window.uploadS2SFile("dragleave", event), false) | |
| micContainer.addEventListener("dragover", event => window.uploadS2SFile("dragover", event), false) | |
| micContainer.addEventListener("drop", event => window.uploadS2SFile("drop", event), false) | |
| // Disable page navigation on badly dropped file | |
| window.document.addEventListener("dragover", event => event.preventDefault(), false) | |
| window.document.addEventListener("drop", event => event.preventDefault(), false) |