diff --git a/src/js/audio/sound.js b/src/js/audio/sound.js index 4720b47..8b5a725 100644 --- a/src/js/audio/sound.js +++ b/src/js/audio/sound.js @@ -69,7 +69,6 @@ export async function playSpatialSpeech(text, voice, rate, x, y) { // Cancel the current speech source if any TextToSpeech.stop(); - return TextToSpeech.speak({ text, voice: typeof voice !== "undefined" ? voice.voiceIndex : voice, @@ -124,6 +123,31 @@ export function createSpatialPlayer(locationProvider) { // Cancel the current sound and speech sources TextToSpeech.stop(); }, + + async loadVoices() { + // Build list of available voices + return TextToSpeech.getSupportedVoices().then((voices) => { + // add "voiceIndex" as it is required by the TextToSpeech.speak + voices.voices.forEach(function (voice, index) { + voice.voiceIndex = index; + }); + + const voicesEn = voices.voices.filter((voice) => + voice.lang.startsWith("en") + ); + const voicesNames = new Set(voicesEn.map((voice) => voice.name)); + + player.voices = Array.from(voicesNames).map((name) => + voicesEn.find((voice) => voice.name === name) + ); + + // Select the system default voice by default + const systemDefaultVoice = player.voices.find((voice) => voice.default) || 0; + player.setVoice(systemDefaultVoice); + + return player.voices; + }); + }, }; async function playNext() { diff --git a/src/js/replay.js b/src/js/replay.js index 855ff89..a283d9a 100644 --- a/src/js/replay.js +++ b/src/js/replay.js @@ -20,6 +20,8 @@ document.addEventListener('DOMContentLoaded', function () { const recentCalloutsList = createRecentCalloutList(locationProvider, audioQueue, map); let gpxPlayer = null; // to be initialized on file selection + audioQueue.loadVoices(); + // Register for updates to location // (no need to separately watch heading changes in GPX simulation) locationProvider.events.addEventListener('locationUpdated', e => { diff --git a/src/js/visual/voicecontrols.js b/src/js/visual/voicecontrols.js index 0e0e2a2..6f4702e 100644 --- a/src/js/visual/voicecontrols.js +++ b/src/js/visual/voicecontrols.js @@ -1,6 +1,5 @@ // Copyright (c) Daniel W. Steinbrook. // with many thanks to ChatGPT -import { TextToSpeech } from "@capacitor-community/text-to-speech"; function createVoiceControls(audioQueue) { // Fetch available voices @@ -10,61 +9,29 @@ function createVoiceControls(audioQueue) { const increaseRate = document.getElementById("increaseRate"); const rateValue = document.getElementById("rateValue"); - // Just for testing - // TextToSpeech.speak({ - // text: "This is a sample text.", - // lang: "en-US", - // rate: 1.0, - // pitch: 1.0, - // volume: 1.0, - // category: "ambient", - // }); - // Populate voice selector - function populateVoices() { + async function populateVoices() { // Populate voice list with all English voices - audioQueue.voices = []; - - TextToSpeech.getSupportedVoices().then((voices) => { - // add "voiceIndex" as it is required by the TextToSpeech.speak - voices.voices.forEach(function (voice, index) { - voice.voiceIndex = index; - }); - - const voicesEn = voices.voices.filter((voice) => - voice.lang.startsWith("en") - ); - const voicesNames = new Set(voicesEn.map((voice) => voice.name)); - - audioQueue.voices = Array.from(voicesNames).map((name) => - voicesEn.find((voice) => voice.name === name) - ); - + return audioQueue.loadVoices().then((voices) => { // Remove them to avoid duplicates while (voiceSelect.childNodes[0] != null) { voiceSelect.childNodes[0].remove(); } - // console.log(`I'll add ${audioQueue.voices.length} voices to the list`); audioQueue.voices.forEach(function (voice, index) { const option = document.createElement("option"); option.value = index; option.textContent = "🗣 " + voice.name; voiceSelect.appendChild(option); - - console.log(`VOICE ${voice.name} with value ${index} APPENDED`); }); - // set initial voice - audioQueue.setVoice(voiceSelect.value); }); } - populateVoices(); - // Select the system default voice by default - const systemDefaultVoice = audioQueue.voices.find((voice) => voice.default); - if (systemDefaultVoice) { - voiceSelect.value = audioQueue.voices.indexOf(systemDefaultVoice); - } + populateVoices().then(() => { + // Set voice and rate to match initial form values + audioQueue.setRate(parseFloat(rateValue.textContent)); + audioQueue.setVoice(voiceSelect.value); + }) // Update voices when they change window.speechSynthesis.onvoiceschanged = function () { @@ -84,10 +51,6 @@ function createVoiceControls(audioQueue) { voiceSelect.addEventListener("change", function () { audioQueue.setVoice(voiceSelect.value); }); - - // Set voice and rate to match initial form values - audioQueue.setRate(parseFloat(rateValue.textContent)); - audioQueue.setVoice(voiceSelect.value); } export default createVoiceControls;