"use strict" window.openEmbeddingsWindow = () => { closeModal(undefined, embeddingsContainer).then(() => { embeddingsContainer.style.opacity = 0 embeddingsContainer.style.display = "flex" requestAnimationFrame(() => requestAnimationFrame(() => embeddingsContainer.style.opacity = 1)) requestAnimationFrame(() => requestAnimationFrame(() => chromeBar.style.opacity = 1)) }) } window.embeddingsState = { data: {}, allData: {}, clickedObject: undefined, spritesOn: true, gendersOn: false, voiceCheckboxes: [], isReady: false, isOpen: false, sceneData: {}, mouseIsDown: false, rightMouseIsDown: false, mousePos: {x: 0, y: 0} } function componentToHex(c) { c = Math.min(255, c) var hex = c.toString(16); return hex.length == 1 ? "0" + hex : hex; } function rgbToHex(r, g, b) { return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); } function hexToRgb(hex, normalize) { var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); const colour = result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; if (normalize && colour) { colour.r /= 255 colour.g /= 255 colour.b /= 255 } return colour } window.populateGamesList = () => { embeddingsGamesListContainer.innerHTML = "" Object.keys(window.gameAssets).sort((a,b)=>a { const gameSelectContainer = createElem("div") const gameCheckbox = createElem(`input#embs_${gameId}`, {type: "checkbox"}) gameCheckbox.checked = true const gameButton = createElem("button.fixedColour") gameButton.style.setProperty("background-color", `#${window.embeddingsState.gameColours[gameId]}`, "important") gameButton.style.display = "flex" gameButton.style.alignItems = "center" gameButton.style.margin = "auto" gameButton.style.marginTop = "8px" const buttonLabel = createElem("span", window.embeddingsState.gameTitles[gameId]) gameButton.addEventListener("click", e => { if (e.target==gameButton || e.target==buttonLabel) { gameCheckbox.click() window.populateVoicesList() window.computeEmbsAndDimReduction() } }) gameButton.addEventListener("contextmenu", e => { if (e.target==gameButton || e.target==buttonLabel) { Array.from(embeddingsGamesListContainer.querySelectorAll("input")).forEach(ckbx => ckbx.checked = false) gameCheckbox.click() window.populateVoicesList() window.computeEmbsAndDimReduction() } }) gameCheckbox.addEventListener("click", () => { window.populateVoicesList() window.computeEmbsAndDimReduction() }) gameButton.appendChild(gameCheckbox) gameButton.appendChild(buttonLabel) gameSelectContainer.appendChild(gameButton) embeddingsGamesListContainer.appendChild(gameSelectContainer) }) } window.populateVoicesList = () => { const enabledGames = Array.from(embeddingsGamesListContainer.querySelectorAll("input")) .map(elem => [elem.checked, elem.id.replace("embs_", "")]) .filter(checkedId => checkedId[0]) .map(checkedId => checkedId[1].replace("embs_", "")) const checkboxes = [] embeddingsRecordsContainer.innerHTML = "" Object.keys(window.games).forEach(gameId => { if (!enabledGames.includes(gameId)) { return } window.games[gameId].models.forEach(model => { model.variants.forEach(variant => { const voiceRowElem = createElem("div") const voiceCkbx = createElem(`input#embsVoice_${variant.voiceId}`, {type: "checkbox"}) voiceCkbx.checked = true voiceCkbx.addEventListener("click", () => { window.computeEmbsAndDimReduction() }) checkboxes.push(voiceCkbx) const showVoiceBtn = createElem("button.smallButton.fixedColour", "Show") showVoiceBtn.style.background = `#${window.embeddingsState.gameColours[model.gameId]}` showVoiceBtn.addEventListener("click", () => { if (!voiceCkbx.checked) { return window.errorModal(window.i18n.VEMB_VOICE_NOT_ENABLED) } const point = window.embeddingsState.sceneData.points.find(point => point.data.voiceId==variant.voiceId) window.embeddingsState.sceneData.controls.target.set(point.position.x, point.position.y, point.position.z) const cameraPos = window.embeddingsState.sceneData.camera.position const deltaX = (point.position.x - cameraPos.x) const deltaY = (point.position.y - cameraPos.y) const deltaZ = (point.position.z - cameraPos.z) window.embeddingsState.sceneData.camera.position.set(cameraPos.x+deltaX/2, cameraPos.y+deltaY/2, cameraPos.z+deltaZ/2) }) const nameElem = createElem("div", model.voiceName+(model.variants.length>1?` (${variant.variantName})`:"")) nameElem.title = model.voiceName const gameElem = createElem("div", window.embeddingsState.gameTitles[model.gameId]) gameElem.title = window.embeddingsState.gameTitles[model.gameId] const voiceGender = createElem("div", variant.gender) voiceGender.title = variant.gender voiceRowElem.appendChild(createElem("div", voiceCkbx)) voiceRowElem.appendChild(createElem("div", showVoiceBtn)) voiceRowElem.appendChild(nameElem) voiceRowElem.appendChild(gameElem) voiceRowElem.appendChild(voiceGender) if (embeddingsSearchBar.value.length && !model.voiceName.toLowerCase().trim().includes(embeddingsSearchBar.value.toLowerCase().trim())) { return } embeddingsRecordsContainer.appendChild(voiceRowElem) }) }) }) window.embeddingsState.voiceCheckboxes = checkboxes } embeddingsSearchBar.addEventListener("keyup", () => window.populateVoicesList()) window.initDataMappings = () => { window.embeddingsState.voiceIdToModel = {} window.embeddingsState.gameShortIdToGameId = {} window.embeddingsState.gameColours = {} window.embeddingsState.gameTitles = {} const idToGame = {} Object.keys(window.gameAssets).forEach(gameId => { const id = window.gameAssets[gameId].gameCode idToGame[id] = gameId }) Object.keys(window.gameAssets).forEach(gameId => { // Short game ID to full game ID const gameShortId = window.gameAssets[gameId].gameCode.toLowerCase() window.embeddingsState.gameShortIdToGameId[gameShortId] = gameId.toLowerCase() // Game title const title = window.gameAssets[gameId].gameName window.embeddingsState.gameTitles[gameId] = title // Game colour let colour = window.gameAssets[gameId].themeColourPrimary colour = colour.length==3 ? `${colour[0]}${colour[0]}${colour[1]}${colour[1]}${colour[2]}${colour[2]}` : colour window.embeddingsState.gameColours[gameId] = colour }) Object.keys(window.games).forEach(gameId => { // Voice Id to model data window.games[gameId].models.forEach(model => { model.variants.forEach(variant => { window.embeddingsState.voiceIdToModel[variant.voiceId] = JSON.parse(JSON.stringify(model)) if (model.variants.length>1) { let voiceName = window.embeddingsState.voiceIdToModel[variant.voiceId].voiceName voiceName = `${voiceName} (${variant.variantName})` window.embeddingsState.voiceIdToModel[variant.voiceId].voiceName = voiceName } }) }) }) } window.initEmbeddingsScene = () => { window.initDataMappings() window.populateGamesList() window.populateVoicesList() embeddingsSceneContainer.addEventListener("mousedown", (event) => { window.embeddingsState.mousePos.x = parseInt(event.layerX) window.embeddingsState.mousePos.y = parseInt(event.layerY) }) embeddingsSceneContainer.addEventListener("mouseup", (event) => { const mouseX = parseInt(event.layerX) const mouseY = parseInt(event.layerY) if (event.button==0) { if (Math.abs(mouseX-window.embeddingsState.mousePos.x)<10 && Math.abs(mouseY-window.embeddingsState.mousePos.y)<10) { window.embeddingsState.mouseIsDown = true setTimeout(() => {window.embeddingsState.mouseIsDown = false}, 100) } } else if (event.button==2) { if (Math.abs(mouseX-window.embeddingsState.mousePos.x)<10 && Math.abs(mouseY-window.embeddingsState.mousePos.y)<10) { window.embeddingsState.rightMouseIsDown = true setTimeout(() => {window.embeddingsState.rightMouseIsDown = false}, 100) } } }) window.embeddingsState.isReady = false const SPHERE_RADIUS = 3 const SPHERE_V_COUNT = 50 // Renderer window.embeddingsState.renderer = new THREE.WebGLRenderer({alpha: true, antialias: true}) window.embeddingsState.renderer.setPixelRatio( window.devicePixelRatio ) window.embeddingsState.renderer.setSize(embeddingsSceneContainer.offsetWidth, embeddingsSceneContainer.offsetHeight) embeddingsSceneContainer.appendChild(window.embeddingsState.renderer.domElement) // Scene and camera const scene = new THREE.Scene() window.embeddingsState.sceneData.camera = new THREE.PerspectiveCamera(60, embeddingsSceneContainer.offsetWidth/embeddingsSceneContainer.offsetHeight, 0.001, 100000) window.embeddingsState.sceneData.camera.position.set( -100, 0, 0 ) // Controls window.embeddingsState.sceneData.controls = new THREE.OrbitControls(window.embeddingsState.sceneData.camera, window.embeddingsState.renderer.domElement) window.embeddingsState.sceneData.controls2 = new THREE.TrackballControls(window.embeddingsState.sceneData.camera, window.embeddingsState.renderer.domElement) window.embeddingsState.sceneData.controls.target.set(window.embeddingsState.sceneData.camera.position.x+0.15, window.embeddingsState.sceneData.camera.position.y, window.embeddingsState.sceneData.camera.position.z) window.embeddingsState.sceneData.controls.enableDamping = true window.embeddingsState.sceneData.controls.dampingFactor = 0.025 window.embeddingsState.sceneData.controls.screenSpacePanning = true window.embeddingsState.sceneData.controls.rotateSpeed = 1/6 window.embeddingsState.sceneData.controls.panSpeed = 1 window.embeddingsState.sceneData.controls.minDistance = 50 window.embeddingsState.sceneData.controls.maxDistance = 500 window.embeddingsState.sceneData.controls2.noRotate = true window.embeddingsState.sceneData.controls2.noPan = true window.embeddingsState.sceneData.controls2.noZoom = true window.embeddingsState.sceneData.controls2.zoomSpeed = 1/2// 1.5 window.embeddingsState.sceneData.controls2.dynamicDampingFactor = 0.2 const light = new THREE.DirectionalLight( 0xffffff, 0.5 ) light.position.set( -1, 1, 1 ).normalize() scene.add(light) scene.add(new THREE.AmbientLight( 0xffffff, 0.5 )) // Mouse event ray caster const raycaster = new THREE.Raycaster() const mouse = new THREE.Vector2() window.embeddingsState.renderer.domElement.addEventListener("mousemove", event => { const sizeY = event.target.height const sizeX = event.target.width mouse.x = event.offsetX / sizeX * 2 - 1 mouse.y = -event.offsetY / sizeY * 2 + 1 }, false) window.embeddingsState.sceneData.sprites = [] window.embeddingsState.sceneData.points = [] window.refreshEmbeddingsScenePoints = () => { const enabledGames = Array.from(embeddingsGamesListContainer.querySelectorAll("input")) .map(elem => [elem.checked, elem.id.replace("embs_", "")]) .filter(checkedId => checkedId[0]) .map(checkedId => checkedId[1].replace("embs_", "")) const data = window.embeddingsState.data const newDataNames = Object.keys(data) const oldDataKept = [] const newSprites = [] const newPoints = [] // Remove any existing data ;[window.embeddingsState.sceneData.points, window.embeddingsState.sceneData.sprites].forEach(dataList => { dataList.forEach(object => { const objectName = object.data.voiceId if (newDataNames.includes(objectName)) { oldDataKept.push(objectName) const coords = { x: parseFloat(data[objectName][0]), y: parseFloat(data[objectName][1])-(object.data.type=="text"?SPHERE_RADIUS*1.5:0), z: parseFloat(data[objectName][2]) } object.data.isMoving = true object.data.newPos = coords if (object.data.type=="text") { newSprites.push(object) } else { newPoints.push(object) } } else { scene.remove(object) } }) }) // Add the new data window.embeddingsState.sceneData.sprites = newSprites window.embeddingsState.sceneData.points = newPoints Object.keys(data).forEach(voiceId => { if (oldDataKept.includes(voiceId)) { return } const game = Object.keys(window.embeddingsState.gameShortIdToGameId).includes(voiceId.split("_")[0]) ? window.embeddingsState.gameShortIdToGameId[voiceId.split("_")[0]] : "other" let gender if (Object.keys(window.embeddingsState.voiceIdToModel).includes(voiceId)) { gender = window.embeddingsState.voiceIdToModel[voiceId].gender || window.embeddingsState.voiceIdToModel[voiceId].variants[0].gender } else { gender = window.embeddingsState.allData[voiceId].voiceGender } gender = gender ? gender.toLowerCase() : "other" // if (!enabledGames.includes(game)) { // return // } // Filter out data by gender // if (gender=="male" && !embeddingsMalesCkbx.checked) { // return // } // if (gender=="female" && !embeddingsFemalesCkbx.checked) { // return // } // if (gender=="other" && !embeddingsOtherGendersCkbx.checked) { // return // } // Colour dict const colour = hexToRgb("#"+window.embeddingsState.gameColours[game]) const genderColours = { "f": {r: 200, g: 0, b: 0}, "m": {r: 0, g: 0, b: 200}, "o": {r: 85, g: 85, b: 85}, } const coords = { x: parseFloat(data[voiceId][0]), y: parseFloat(data[voiceId][1]), z: parseFloat(data[voiceId][2]) } const genderColour = gender=="female" ? genderColours["f"] : (gender=="male" ? genderColours["m"] : genderColours["o"]) const pointGeometry = new THREE.SphereGeometry(SPHERE_RADIUS, SPHERE_V_COUNT, SPHERE_V_COUNT) const pointMaterial = new THREE.MeshLambertMaterial({ color: window.embeddingsState.gendersOn ? rgbToHex(genderColour.r, genderColour.g, genderColour.b) : "#"+window.embeddingsState.gameColours[game], transparent: true }) pointMaterial.emissive.emissiveIntensity = 1 // Point sphere const point = new THREE.Mesh(pointGeometry, pointMaterial) point.position.x = coords.x point.position.y = coords.y point.position.z = coords.z point.name = `point|${voiceId}` point.data = { type: "point", voiceId: voiceId, game: game, gameColour: {r: colour.r, g: colour.g, b: colour.b}, genderColour: genderColour } window.embeddingsState.sceneData.points.push(point) scene.add(point) let voiceName if (Object.keys(window.embeddingsState.voiceIdToModel).includes(voiceId)) { voiceName = window.embeddingsState.voiceIdToModel[voiceId].voiceName } else { voiceName = window.embeddingsState.allData[voiceId].voiceName } // Text sprite const sprite = new THREE.TextSprite({ text: voiceName, fontFamily: 'Helvetica, sans-serif', fontSize: 2, strokeColor: '#ffffff', strokeWidth: 0, color: '#24ff00', material: {color: "white"} }) sprite.position.x = coords.x sprite.position.y = coords.y-SPHERE_RADIUS*1.5 sprite.position.z = coords.z sprite.name = `sprite|${voiceId}` sprite.data = {type: "text", voiceId: voiceId, game: game} window.embeddingsState.sceneData.sprites.push(sprite) scene.add(sprite) }) } window.refreshEmbeddingsScenePoints() let hoveredObject = undefined let clickedObject = undefined window.embeddings_render = () => { if (!window.embeddingsState.isReady) { return } requestAnimationFrame(window.embeddings_render) const target = window.embeddingsState.sceneData.controls.target window.embeddingsState.sceneData.controls.update() window.embeddingsState.sceneData.controls2.target.set(target.x, target.y, target.z) window.embeddingsState.sceneData.controls2.update() window.embeddingsState.sceneData.camera.updateMatrixWorld() raycaster.setFromCamera( mouse, window.embeddingsState.sceneData.camera ); // Move objects [window.embeddingsState.sceneData.points, window.embeddingsState.sceneData.sprites].forEach(dataList => { dataList.forEach(object => { if (object.data.isMoving) { if (Math.abs(object.position.x-object.data.newPos.x)>0.005) { object.position.x += (object.data.newPos.x - object.position.x) / 20 object.position.y += (object.data.newPos.y - object.position.y) / 20 object.position.z += (object.data.newPos.z - object.position.z) / 20 } else { object.data.isMoving = false object.data.newPos = undefined } } }) }) // Handle mouse events let intersects = raycaster.intersectObjects(scene.children, true) if (intersects.length) { if (intersects.length>2) { intersects = [intersects.find(it => it.object.data.type=="point")] } if (intersects.length==0 || intersects[0]==undefined || intersects[0].object==undefined || intersects[0].object.data.type=="text") { window.embeddingsState.renderer.render(scene, window.embeddingsState.sceneData.camera) return } window.embeddingsState.renderer.domElement.style.cursor = "pointer" if (hoveredObject != undefined && hoveredObject.object.data.voiceId!=intersects[0].object.data.voiceId) { const colour = window.embeddingsState.gendersOn ? hoveredObject.object.data.genderColour : hoveredObject.object.data.gameColour hoveredObject.object.material.color.r = Math.min(1, colour.r/255) hoveredObject.object.material.color.g = Math.min(1, colour.g/255) hoveredObject.object.material.color.b = Math.min(1, colour.b/255) } hoveredObject = intersects[0] const colour = window.embeddingsState.gendersOn ? hoveredObject.object.data.genderColour : hoveredObject.object.data.gameColour hoveredObject.object.material.color.r = Math.min(1, colour.r/255*1.5) hoveredObject.object.material.color.g = Math.min(1, colour.g/255*1.5) hoveredObject.object.material.color.b = Math.min(1, colour.b/255*1.5) // Right click does voice audio preview if (window.embeddingsState.rightMouseIsDown) { window.embeddingsState.rightMouseIsDown = false const voiceId = hoveredObject.object.data.voiceId const gameId = hoveredObject.object.data.game const modelsPathForGame = window.userSettings[`modelspath_${gameId}`] const audioPreviewPath = `${modelsPathForGame}/${voiceId}.wav` if (fs.existsSync(audioPreviewPath)) { const audioPreview = createElem("audio", {autoplay: false}, createElem("source", { src: audioPreviewPath })) audioPreview.setSinkId(window.userSettings.base_speaker) } else { window.errorModal(window.i18n.VEMB_NO_PREVIEW) } } // Left click does voice click if (window.embeddingsState.mouseIsDown) { if (window.embeddingsState.clickedObject==undefined || window.embeddingsState.clickedObject.voiceId!=hoveredObject.object.data.voiceId) { if (window.embeddingsState.clickedObject!=undefined) { window.embeddingsState.clickedObject.object.material.emissive.setRGB(0,0,0) } window.embeddingsState.clickedObject = { voiceId: hoveredObject.object.data.voiceId, game: hoveredObject.object.data.game, object: hoveredObject.object } hoveredObject.object.material.emissive.setRGB(0, 1, 0) const voiceId = window.embeddingsState.clickedObject.object.data.voiceId if (Object.keys(window.embeddingsState.voiceIdToModel).includes(voiceId)) { embeddingsVoiceGameDisplay.innerHTML = window.embeddingsState.gameTitles[window.embeddingsState.clickedObject.object.data.game] embeddingsVoiceNameDisplay.innerHTML = window.embeddingsState.voiceIdToModel[voiceId].voiceName embeddingsVoiceGenderDisplay.innerHTML = window.embeddingsState.voiceIdToModel[voiceId].gender || window.embeddingsState.voiceIdToModel[voiceId].variants[0].gender } else { embeddingsVoiceGameDisplay.innerHTML = window.embeddingsState.gameTitles[window.embeddingsState.clickedObject.object.data.game] embeddingsVoiceNameDisplay.innerHTML = window.embeddingsState.allData[voiceId].voiceName embeddingsVoiceGenderDisplay.innerHTML = window.embeddingsState.allData[voiceId].voiceGender } } } } else { window.embeddingsState.renderer.domElement.style.cursor = "default" if (hoveredObject != undefined) { const colour = window.embeddingsState.gendersOn ? hoveredObject.object.data.genderColour : hoveredObject.object.data.gameColour hoveredObject.object.material.color.r = Math.min(1, colour.r/255) hoveredObject.object.material.color.g = Math.min(1, colour.g/255) hoveredObject.object.material.color.b = Math.min(1, colour.b/255) } if (window.embeddingsState.mouseIsDown && window.embeddingsState.clickedObject!=undefined) { window.embeddingsState.clickedObject.object.material.emissive.setRGB(0,0,0) window.embeddingsState.clickedObject = undefined embeddingsVoiceGameDisplay.innerHTML = "" embeddingsVoiceNameDisplay.innerHTML = "" embeddingsVoiceGenderDisplay.innerHTML = "" } } window.embeddingsState.renderer.render(scene, window.embeddingsState.sceneData.camera) } window.embeddingsState.isReady = true window.embeddings_render() window.toggleSprites = () => { window.embeddingsState.spritesOn = !window.embeddingsState.spritesOn window.embeddingsState.sceneData.sprites.forEach(sprite => { sprite.material.visible = window.embeddingsState.spritesOn }) } window.toggleGenders = () => { window.embeddingsState.gendersOn = !window.embeddingsState.gendersOn window.embeddingsState.sceneData.points.forEach(point => { const colour = window.embeddingsState.gendersOn ? point.data.genderColour : point.data.gameColour point.material.color.r = Math.min(1, colour.r/255) point.material.color.g = Math.min(1, colour.g/255) point.material.color.b = Math.min(1, colour.b/255) }) } window.addEventListener("resize", () => { if (window.embeddingsState.isOpen) { window.embeddings_updateSize() } }) } window.embeddings_updateSize = () => { if (window.embeddingsState.isReady) { window.embeddingsState.sceneData.camera.aspect = embeddingsSceneContainer.offsetWidth/embeddingsSceneContainer.offsetHeight window.embeddingsState.sceneData.camera.updateProjectionMatrix() window.embeddingsState.renderer.setSize(embeddingsSceneContainer.offsetWidth, embeddingsSceneContainer.offsetHeight) } } embeddingsPreviewButton.addEventListener("click", () => { if (window.embeddingsState.clickedObject) { const voiceId = window.embeddingsState.clickedObject.object.data.voiceId const gameId = window.embeddingsState.clickedObject.object.data.game const modelsPathForGame = window.userSettings[`modelspath_${gameId}`] const audioPreviewPath = `${modelsPathForGame}/${voiceId}.wav` if (fs.existsSync(audioPreviewPath)) { const audioPreview = createElem("audio", {autoplay: false}, createElem("source", { src: audioPreviewPath })) audioPreview.setSinkId(window.userSettings.base_speaker) } else { window.errorModal(window.i18n.VEMB_NO_PREVIEW) } } else { window.errorModal(window.i18n.VEMB_SELECT_VOICE_FIRST) } }) embeddingsLoadButton.addEventListener("click", () => { if (window.embeddingsState.clickedObject) { const voiceId = window.embeddingsState.clickedObject.object.data.voiceId const gameId = window.embeddingsState.clickedObject.object.data.game const modelsPathForGame = window.userSettings[`modelspath_${gameId}`] const modelPath = `${modelsPathForGame}/${voiceId}.pt` if (fs.existsSync(modelPath)) { window.changeGame(window.gameAssets[gameId]) // Simulate voice loading through the UI let voiceName window.games[gameId].models.forEach(model => { model.variants.forEach(variant => { if (variant.voiceId==voiceId) { voiceName = model.voiceName } }) }) const voiceButton = Array.from(voiceTypeContainer.children).find(button => button.innerHTML==voiceName) voiceButton.click() closeModal().then(() => { generateVoiceButton.click() }) } else { window.errorModal(window.i18n.VEMB_NO_MODEL) } } else { window.errorModal(window.i18n.VEMB_SELECT_VOICE_FIRST) } }) embeddingsKey.addEventListener("change", () => { if (embeddingsKey.value=="embsKey_game" && window.embeddingsState.gendersOn) { window.toggleGenders() } else if (embeddingsKey.value=="embsKey_gender" && !window.embeddingsState.gendersOn) { window.toggleGenders() } }) embeddingsMalesCkbx.addEventListener("change", () => window.computeEmbsAndDimReduction()) embeddingsFemalesCkbx.addEventListener("change", () => window.computeEmbsAndDimReduction()) embeddingsOtherGendersCkbx.addEventListener("change", () => window.computeEmbsAndDimReduction()) embeddingsOnlyInstalledCkbx.addEventListener("change", () => window.computeEmbsAndDimReduction()) embeddingsAlgorithm.addEventListener("change", () => window.computeEmbsAndDimReduction()) window.computeEmbsAndDimReduction = (includeAllVoices=false) => { const enabledGames = Array.from(embeddingsGamesListContainer.querySelectorAll("input")) .map(elem => [elem.checked, elem.id.replace("embs_", "")]) .filter(checkedId => checkedId[0]) .map(checkedId => checkedId[1].replace("embs_", "")) const enabledVoices = window.embeddingsState.voiceCheckboxes .map(elem => [elem.checked, elem.id.replace("embsVoice_", "")]) .filter(checkedId => checkedId[0]) .map(checkedId => checkedId[1].replace("embsVoice_", "")) if (enabledVoices.length<=2) { return window.errorModal(window.i18n.EMBEDDINGS_NEED_AT_LEAST_3) } // Get together a list of voiceId->.wav path mappings const mappings = [] if (includeAllVoices) { Object.keys(window.games).forEach(gameId => { const modelsPathForGame = window.userSettings[`modelspath_${gameId}`] window.games[gameId].models.forEach(model => { model.variants.forEach(variant => { const audioPreviewPath = `${modelsPathForGame}/${variant.voiceId}.wav` if (fs.existsSync(audioPreviewPath)) { mappings.push(`${variant.voiceId}=${audioPreviewPath}=${model.voiceName}=${variant.gender}=${gameId}`) } }) }) }) } else { Object.keys(window.embeddingsState.allData).forEach(voiceId => { try { const voiceMeta = window.embeddingsState.allData[voiceId] const gender = voiceMeta.voiceGender.toLowerCase() // Filter game-level voices if (!enabledGames.includes(voiceMeta.gameId)) { return } // Filter out by voice if (!enabledVoices.includes(voiceId)) { return } // Filter out data by gender if (gender=="male" && !embeddingsMalesCkbx.checked) { return } if (gender=="female" && !embeddingsFemalesCkbx.checked) { return } if (gender=="other" && !embeddingsOtherGendersCkbx.checked) { return } const modelsPathForGame = window.userSettings[`modelspath_${voiceMeta.gameId}`] let audioPreviewPath = `${modelsPathForGame}/${voiceId}.wav` if (!fs.existsSync(audioPreviewPath)) { audioPreviewPath = "" } mappings.push(`${voiceId}=${audioPreviewPath}=${voiceMeta.voiceName}=${voiceMeta.voiceGender.toLowerCase()}=${voiceMeta.gameId}`) } catch (e) {console.log(e)} }) } if (mappings.length<=2) { return window.errorModal(window.i18n.EMBEDDINGS_NEED_AT_LEAST_3) } window.spinnerModal(window.i18n.VEMB_RECOMPUTING) doFetch(`http://localhost:8008/computeEmbsAndDimReduction`, { method: "Post", body: JSON.stringify({ mappings: mappings.join("\n"), onlyInstalled: embeddingsOnlyInstalledCkbx.checked, algorithm: embeddingsAlgorithm.value.split("_")[1], includeAllVoices }) }).then(r=>r.text()).then(res => { window.embeddingsState.data = {} res.split("\n").forEach(voiceMetaAndCoords => { const voiceId = voiceMetaAndCoords.split("=")[0] const voiceName = voiceMetaAndCoords.split("=")[1] const voiceGender = voiceMetaAndCoords.split("=")[2] const gameId = voiceMetaAndCoords.split("=")[3] const coords = voiceMetaAndCoords.split("=")[4].split(",").map(v => parseFloat(v)) window.embeddingsState.data[voiceId] = coords if (includeAllVoices) { window.embeddingsState.allData[voiceId] = { voiceName, voiceGender, coords, gameId } } }) window.refreshEmbeddingsScenePoints() closeModal(undefined, embeddingsContainer) }).catch(e => { console.log(e) if (e.code =="ENOENT") { closeModal(null, modalContainer).then(() => { window.errorModal(window.i18n.ERR_SERVER) }) } }) } embeddingsCloseHelpUI.addEventListener("click", () => { embeddingsHelpUI.style.display = "none" })