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) |