Skip to content

Commit

Permalink
Refactor TTS initialization to give replay_gpx a voice
Browse files Browse the repository at this point in the history
  • Loading branch information
steinbro committed Jul 3, 2024
1 parent 750aa3b commit 05f3c08
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 45 deletions.
26 changes: 25 additions & 1 deletion src/js/audio/sound.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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() {
Expand Down
2 changes: 2 additions & 0 deletions src/js/replay.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
51 changes: 7 additions & 44 deletions src/js/visual/voicecontrols.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 () {
Expand All @@ -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;

0 comments on commit 05f3c08

Please sign in to comment.