Skip to content

Commit

Permalink
change tts timestamp
Browse files Browse the repository at this point in the history
  • Loading branch information
ttop32 committed Mar 26, 2024
1 parent 6051c14 commit a6c5e2c
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 62 deletions.
34 changes: 14 additions & 20 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function addMessageListener() {
playTtsQueue(request.data);
sendResponse({});
} else if (request.type === "stopTTS") {
stopTts();
stopTts(request.data.timestamp);
sendResponse({});
} else if (request.type === "recordTooltipText") {
recordHistory(request.data);
Expand Down Expand Up @@ -276,47 +276,41 @@ async function playTtsQueue({
targetLang,
voiceTarget,
voiceRepeat,
timestamp,
}) {
var sourceText = util.filterEmoji(sourceText);
var targetText = util.filterEmoji(targetText);
var ttsTarget = voiceTarget || setting["voiceTarget"];
var ttsRepeat = voiceRepeat || setting["voiceRepeat"];
ttsRepeat = Number(ttsRepeat);
stopTts();
await delay(10);

var startTimeStamp = Date.now();
for (var i = 0; i < ttsRepeat; i++) {
if (ttsTarget == "source") {
await playTts(sourceText, sourceLang, startTimeStamp);
await playTts(sourceText, sourceLang, timestamp);
} else if (ttsTarget == "target") {
await playTts(targetText, targetLang, startTimeStamp);
await playTts(targetText, targetLang, timestamp);
} else if (ttsTarget == "sourcetarget") {
await playTts(sourceText, sourceLang, startTimeStamp);
await playTts(targetText, targetLang, startTimeStamp);
await playTts(sourceText, sourceLang, timestamp);
await playTts(targetText, targetLang, timestamp);
} else if (ttsTarget == "targetsource") {
await playTts(targetText, targetLang, startTimeStamp);
await playTts(sourceText, sourceLang, startTimeStamp);
await playTts(targetText, targetLang, timestamp);
await playTts(sourceText, sourceLang, timestamp);
}
}
}

function stopTts() {
stopTtsTimestamp = Date.now();
tts["BrowserTTS"].stopTTS();
function stopTts(timestamp = Date.now()) {
for (const key in tts) {
tts[key].stopTTS(timestamp);
}
}

async function playTts(text, lang, startTimeStamp) {
if (startTimeStamp < stopTtsTimestamp) {
return;
}
async function playTts(text, lang, timestamp) {
var volume = setting["voiceVolume"];
var rate = setting["voiceRate"];
var voiceFullName = setting?.["ttsVoice_" + lang];
var isExternalTts = /^(BingTTS|GoogleTranslateTTS).*/.test(voiceFullName);
var voice = isExternalTts ? voiceFullName.split("_")[1] : voiceFullName;
var engine = isExternalTts ? voiceFullName.split("_")[0] : "BrowserTTS";
await tts[engine].playTTS(text, voice, lang, rate, volume);
await tts[engine].playTTS(text, voice, lang, rate, volume, timestamp);
}

//detect tab swtich ===================================
Expand Down
18 changes: 11 additions & 7 deletions src/contentScript.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ async function stageTooltipText(text, actionType, range) {
text = util.filterWord(text); //filter out one that is url,no normal char
var isTtsOn = keyDownList[setting["TTSWhen"]];
var isTooltipOn = keyDownList[setting["showTooltipWhen"]];

var timestamp = Number(Date.now());
// skip if mouse target is tooltip or no text, if no new word or tab is not activated
// hide tooltip, if no text
// if tooltip is off, hide tooltip
Expand Down Expand Up @@ -157,7 +157,7 @@ async function stageTooltipText(text, actionType, range) {
}
//if use_tts is on or activation key is pressed, do tts
if (isTtsOn) {
util.requestTTS(text, sourceLang, targetText, targetLang);
util.requestTTS(text, sourceLang, targetText, targetLang, timestamp);
}
}

Expand Down Expand Up @@ -499,7 +499,6 @@ function handleMouseKeyUp(e) {
}

function holdKeydownList(key) {
// skip text key
if (key && !keyDownList[key] && !isCharKey(key)) {
keyDownList[key] = true;
restartWordProcess();
Expand All @@ -513,14 +512,19 @@ async function stopTTSbyCombKey(key) {
return;
}
// stop tts if combination key ex crtl+c
for (let i = 0; i < 10; i++) {
await delay(200);
util.requestStopTTS();
}
util.requestStopTTS(Date.now() + 500);
}

function isCharKey(key) {
return /Key|Digit|Numpad/.test(key);
}
function isStageKey(key) {
return [
setting["TTSWhen"],
setting["showTooltipWhen"],
setting["keyDownMouseoverTextSwap"],
].includes(key);
}

function releaseKeydownList(key) {
keyDownList[key] = false;
Expand Down
39 changes: 26 additions & 13 deletions src/tts/baseTTS.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,53 @@ import browser from "webextension-polyfill";
import * as util from "/src/util";

export default class BaseTTS {
static async playTTS(text, voice, lang, rate, volume) {
try {
var volume = Number(volume);
var rate = Number(rate);
await this.playTTSEngine(text, voice, lang, rate, volume);
} catch (error) {
console.log(error);
static stopTtsTimestamp = 0;

static async playTTS(text, voice, lang, rate, volume, timestamp) {
await this.stopTTS(timestamp);
await this.callTTSEngine(text, voice, lang, rate, volume, timestamp);
}
static async callTTSEngine(text, voice, lang, rate, volume, timestamp) {
if (Number(timestamp) < this.stopTtsTimestamp) {
return;
}
text = util.filterEmoji(text);
text = util.filterHtmlTag(text);
volume = Number(volume);
rate = Number(rate);
await this.playTTSEngine(text, voice, lang, rate, volume, timestamp);
}

static async playTTSEngine(text, voice, lang, rate, volume) {
static async playTTSEngine(text, voice, lang, rate, volume, timestamp) {
throw new Error("Not implemented");
}

static stopTTS() {
static async stopTTS(timestamp = Date.now()) {
timestamp = Number(timestamp);
if (timestamp < this.stopTtsTimestamp) {
return;
}
this.stopTtsTimestamp = timestamp;
browser?.tts?.stop(); //remove prev voice
this.stopTtsOffscreen();
await this.stopTtsOffscreen(timestamp);
}

static async playSound(source, rate, volume) {
static async playAudioOffscreen(source, rate, volume, timestamp) {
await this.createOffscreen();
await util.sendMessage({
type: "playAudioOffscreen",
data: {
source,
rate,
volume,
timestamp,
},
});
}

static async stopTtsOffscreen() {
static async stopTtsOffscreen(timestamp) {
await this.createOffscreen();
util.sendMessage({ type: "stopTTSOffscreen" });
await util.sendMessage({ type: "stopTTSOffscreen", data: { timestamp } });
}

// Create the offscreen document
Expand Down
4 changes: 2 additions & 2 deletions src/tts/bingTTS.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import * as util from "/src/util";
let bingTtsUrl = "https://www.bing.com/tfettts";

export default class BingTTS extends BaseTTS {
static async playTTSEngine(text, voice, lang, rate, volume) {
static async playTTSEngine(text, voice, lang, rate, volume, timestamp) {
var ttsBlob = await this.requestBingTtsBlob(text, voice, rate, volume);
var base64Url = await util.getBase64Url(ttsBlob);
await this.playSound(base64Url, 1, volume);
await this.playAudioOffscreen(base64Url, 1, volume, timestamp);
}

static async requestBingTtsBlob(
Expand Down
29 changes: 27 additions & 2 deletions src/tts/googleTranslateTTS.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,35 @@
import BaseTTS from "./baseTTS";

export default class GoogleTranslateTTS extends BaseTTS {
static async playTTSEngine(text, voice, lang, rate, volume) {
static async playTTSEngine(text, voice, lang, rate, volume, timestamp) {
var textList = this.splitText(text, lang);
for (const sentence of textList) {
await this.playTTSByGoogle(
sentence,
voice,
lang,
rate,
volume,
timestamp
);
}
}
static async playTTSByGoogle(text, voice, lang, rate, volume, timestamp) {
var googleTranslateTtsUrl = `https://translate.googleapis.com/translate_tts?ie=UTF-8&q=${encodeURIComponent(
text
)}&tl=${lang}&client=tw-ob`;
await this.playSound(googleTranslateTtsUrl, rate, volume);
await this.playAudioOffscreen(
googleTranslateTtsUrl,
rate,
volume,
timestamp
);
}

static splitText(text, lang) {
var segmenter = new Intl.Segmenter(lang, { granularity: "sentence" });
var wordsMeta = [...segmenter.segment(text)];
var wordList = wordsMeta.map((word) => word.segment);
return wordList;
}
}
53 changes: 40 additions & 13 deletions src/tts/offscreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,45 @@ import browser from "webextension-polyfill";

var audio;
const speech = window.speechSynthesis;
var stopTtsTimestamp = 0;

// Listen for messages from the extension
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
(async () => {
if (request.type === "playAudioOffscreen") {
stopAudio();
await playAudio(request.data);
sendResponse({});
} else if (request.type === "playSpeechTTSOffscreen") {
await playSpeechTTS(request.data);
sendResponse({});
} else if (request.type === "stopTTSOffscreen") {
stopAudio();
await stopAudio(request.data);
sendResponse({});
}
})();

return true;
});

function playAudio({ source, rate = 1.0, volume = 1.0 }) {
return new Promise((resolve, reject) => {
audio = new Audio(source);
audio.volume = volume;
audio.playbackRate = rate;
audio.onended = () => resolve();
audio.onloadeddata = () => audio.play();
function playAudio({ source, rate = 1.0, volume = 1.0, timestamp }) {
return new Promise(async (resolve, reject) => {
try {
audio = new Audio(source);
audio.volume = volume;
audio.playbackRate = rate;
audio.onended = () => resolve();
audio.onloadeddata = () => {
if (Number(timestamp) < stopTtsTimestamp) {
stopAudioHTML();
resolve();
return;
}
audio.play();
};
} catch (error) {
console.log(error);
resolve();
}
});
}

Expand All @@ -55,10 +67,25 @@ async function playSpeechTTS({ text, voice, lang, rate = 1.0, volume = 1.0 }) {
});
}

function stopAudio() {
if (audio) {
audio.pause();
audio.currentTime = 0;
async function stopAudio({ timestamp }) {
if (Number(timestamp) < stopTtsTimestamp) {
return;
}
stopTtsTimestamp = Number(timestamp);
stopAudioSpeech();
await stopAudioHTML();
}
function stopAudioSpeech() {
speech?.cancel();
}
function stopAudioHTML() {
return new Promise((resolve, reject) => {
if (!audio) {
resolve();
return;
}
audio.pause();
audio.currentTime = 0;
resolve();
});
}
4 changes: 2 additions & 2 deletions src/tts/speechTTS.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import * as util from "/src/util";
import BaseTTS from "./baseTTS";

export default class SpeechTTS extends BaseTTS {
static async playTTSEngine(text, voice, lang, rate, volume) {
static async playTTSEngine(text, voice, lang, rate, volume, timestamp) {
await this.createOffscreen();
await util.sendMessage({
type: "playSpeechTTSOffscreen",
data: { text, voice, lang, rate, volume },
data: { text, voice, lang, rate, volume, timestamp },
});
}
}
19 changes: 16 additions & 3 deletions src/util/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ export function filterEmoji(word) {
""
);
}
export function filterHtmlTag(word) {
return word.replace(/([<>])/g, "");
}

export function truncate(str, n) {
return str.length > n ? str.slice(0, n - 1) + "..." : str;
Expand Down Expand Up @@ -695,7 +698,8 @@ export async function requestTTS(
sourceText,
sourceLang,
targetText,
targetLang
targetLang,
timestamp = Date.now()
) {
return await sendMessage({
type: "tts",
Expand All @@ -704,6 +708,7 @@ export async function requestTTS(
sourceLang,
targetText,
targetLang,
timestamp,
},
});
}
Expand All @@ -720,21 +725,29 @@ export async function requestImage(text) {
});
}

export async function requestTTSSingle(sourceText, sourceLang) {
export async function requestTTSSingle(
sourceText,
sourceLang,
timestamp = Date.now()
) {
return await sendMessage({
type: "tts",
data: {
sourceText,
sourceLang,
voiceTarget: "source",
voiceRepeat: "1",
timestamp,
},
});
}

export async function requestStopTTS() {
export async function requestStopTTS(timestamp) {
return await sendMessage({
type: "stopTTS",
data: {
timestamp,
},
});
}

Expand Down

0 comments on commit a6c5e2c

Please sign in to comment.