From f927f879fdb2a1908507a0b9cde37f9ff13b5d0a Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 7 Sep 2022 22:48:06 +0800 Subject: [PATCH 1/6] Exclude empty text in utterances --- packages/text-to-speech/src/htmlToSsml.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/text-to-speech/src/htmlToSsml.ts b/packages/text-to-speech/src/htmlToSsml.ts index e1008001a..9cf45a533 100644 --- a/packages/text-to-speech/src/htmlToSsml.ts +++ b/packages/text-to-speech/src/htmlToSsml.ts @@ -300,7 +300,7 @@ export const htmlToSpeechFile = ( wordOffset, node.nodeName === 'BLOCKQUOTE' ? options.secondaryVoice : undefined ) - utterances.push(utterance) + utterance.wordCount > 0 && utterances.push(utterance) wordOffset += utterance.wordCount } } From 3cf3d404822740faa410e38da2dc947e2cd9871e Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 7 Sep 2022 22:48:40 +0800 Subject: [PATCH 2/6] Replace speech-file with speech in the route --- packages/api/src/routers/article_router.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/routers/article_router.ts b/packages/api/src/routers/article_router.ts index 89f164838..e1415b6d4 100644 --- a/packages/api/src/routers/article_router.ts +++ b/packages/api/src/routers/article_router.ts @@ -29,7 +29,7 @@ interface SpeechInput { secondaryVoice?: string priority?: 'low' | 'high' } -const outputFormats = ['mp3', 'speech-marks', 'speech-file'] +const outputFormats = ['mp3', 'speech-marks', 'speech'] const logger = buildLogger('app.dispatch') export function articleRouter() { From 9a62a3c31ecae7b65f55bfef126cd04173e8376e Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 7 Sep 2022 22:51:44 +0800 Subject: [PATCH 3/6] Add pageId to speech-file --- packages/api/src/routers/article_router.ts | 4 ++-- packages/api/src/textToSpeech.d.ts | 1 - packages/text-to-speech/src/htmlToSsml.ts | 3 --- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/api/src/routers/article_router.ts b/packages/api/src/routers/article_router.ts index e1415b6d4..00c5d94ab 100644 --- a/packages/api/src/routers/article_router.ts +++ b/packages/api/src/routers/article_router.ts @@ -102,7 +102,7 @@ export function articleRouter() { }, }) - if (outputFormat === 'speech-file') { + if (outputFormat === 'speech') { const page = await getPageById(articleId) if (!page) { return res.status(404).send('Page not found') @@ -112,7 +112,7 @@ export function articleRouter() { secondaryVoice: secondaryVoice, language: page.language, }) - return res.send(speechFile) + return res.send({ ...speechFile, pageId: articleId }) } const existingSpeech = await getRepository(Speech).findOne({ diff --git a/packages/api/src/textToSpeech.d.ts b/packages/api/src/textToSpeech.d.ts index 051b9be8f..f27ff5aec 100644 --- a/packages/api/src/textToSpeech.d.ts +++ b/packages/api/src/textToSpeech.d.ts @@ -21,7 +21,6 @@ declare module '@omnivore/text-to-speech-handler' { export interface SpeechFile { wordCount: number - averageWPM: number language: string defaultVoice: string utterances: Utterance[] diff --git a/packages/text-to-speech/src/htmlToSsml.ts b/packages/text-to-speech/src/htmlToSsml.ts index 9cf45a533..4aff78796 100644 --- a/packages/text-to-speech/src/htmlToSsml.ts +++ b/packages/text-to-speech/src/htmlToSsml.ts @@ -16,7 +16,6 @@ export interface Utterance { export interface SpeechFile { wordCount: number - averageWPM: number language: string defaultVoice: string utterances: Utterance[] @@ -37,7 +36,6 @@ export type SSMLOptions = { language?: string } -const WORDS_PER_MINUTE = 200 const DEFAULT_LANGUAGE = 'en-US' const DEFAULT_VOICE = 'en-US-JennyNeural' const DEFAULT_RATE = 1.25 @@ -307,7 +305,6 @@ export const htmlToSpeechFile = ( return { wordCount: wordOffset, - averageWPM: WORDS_PER_MINUTE, language: options.language || DEFAULT_LANGUAGE, defaultVoice: options.primaryVoice || DEFAULT_VOICE, utterances, From 8e8a6dccd00e8086c36e16f8436c7f83454203bd Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 7 Sep 2022 22:55:07 +0800 Subject: [PATCH 4/6] Change rate to string type --- packages/api/src/textToSpeech.d.ts | 2 +- packages/text-to-speech/src/htmlToSsml.ts | 4 ++-- packages/text-to-speech/src/index.ts | 4 ++-- packages/text-to-speech/src/textToSpeech.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/api/src/textToSpeech.d.ts b/packages/api/src/textToSpeech.d.ts index f27ff5aec..45e43969d 100644 --- a/packages/api/src/textToSpeech.d.ts +++ b/packages/api/src/textToSpeech.d.ts @@ -7,7 +7,7 @@ declare module '@omnivore/text-to-speech-handler' { export interface SSMLOptions { primaryVoice?: string secondaryVoice?: string - rate?: number + rate?: string language?: string } diff --git a/packages/text-to-speech/src/htmlToSsml.ts b/packages/text-to-speech/src/htmlToSsml.ts index 4aff78796..5988453e6 100644 --- a/packages/text-to-speech/src/htmlToSsml.ts +++ b/packages/text-to-speech/src/htmlToSsml.ts @@ -32,13 +32,13 @@ export type SSMLItem = { export type SSMLOptions = { primaryVoice?: string secondaryVoice?: string - rate?: number + rate?: string language?: string } const DEFAULT_LANGUAGE = 'en-US' const DEFAULT_VOICE = 'en-US-JennyNeural' -const DEFAULT_RATE = 1.25 +const DEFAULT_RATE = '1.0' const ANCHOR_ELEMENTS_BLOCKED_ATTRIBUTES = [ 'omnivore-highlight-id', diff --git a/packages/text-to-speech/src/index.ts b/packages/text-to-speech/src/index.ts index 5ca4d54f7..3d35fa3a8 100644 --- a/packages/text-to-speech/src/index.ts +++ b/packages/text-to-speech/src/index.ts @@ -13,7 +13,7 @@ import { htmlToSpeechFile } from './htmlToSsml' interface UtteranceInput { voice?: string - rate?: number + rate?: string language?: string text: string idx: string @@ -24,7 +24,7 @@ interface HTMLInput { text: string voice?: string language?: string - rate?: number + rate?: string complimentaryVoice?: string bucket: string } diff --git a/packages/text-to-speech/src/textToSpeech.ts b/packages/text-to-speech/src/textToSpeech.ts index f9679c6c4..4169e5401 100644 --- a/packages/text-to-speech/src/textToSpeech.ts +++ b/packages/text-to-speech/src/textToSpeech.ts @@ -14,7 +14,7 @@ export interface TextToSpeechInput { voice?: string language?: string textType?: 'html' | 'utterance' - rate?: number + rate?: string secondaryVoice?: string audioStream?: NodeJS.ReadWriteStream } From 967fb3c6d50fdacc5ba2ba0e2ebad797f275b430 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 7 Sep 2022 22:56:37 +0800 Subject: [PATCH 5/6] Do not throw error if text is empty --- packages/text-to-speech/src/index.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/text-to-speech/src/index.ts b/packages/text-to-speech/src/index.ts index 3d35fa3a8..63f9a6631 100644 --- a/packages/text-to-speech/src/index.ts +++ b/packages/text-to-speech/src/index.ts @@ -160,9 +160,6 @@ export const textToSpeechStreamingHandler = Sentry.GCPFunction.wrapHttpFunction( try { const utteranceInput = req.body as UtteranceInput - if (!utteranceInput.text) { - return res.status(400).send({ errorCode: 'INVALID_DATA' }) - } const input: TextToSpeechInput = { ...utteranceInput, textType: 'utterance', From 68a932ad0b9ac45bc077bed9d780af85b4105a3b Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Wed, 7 Sep 2022 23:13:12 +0800 Subject: [PATCH 6/6] Escape entityName in ssml --- packages/text-to-speech/src/textToSpeech.ts | 3 ++- packages/text-to-speech/test/htmlToSsml.test.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/text-to-speech/src/textToSpeech.ts b/packages/text-to-speech/src/textToSpeech.ts index 4169e5401..449668548 100644 --- a/packages/text-to-speech/src/textToSpeech.ts +++ b/packages/text-to-speech/src/textToSpeech.ts @@ -8,6 +8,7 @@ import { SpeechSynthesizer, } from 'microsoft-cognitiveservices-speech-sdk' import { endSsml, htmlToSsmlItems, ssmlItemText, startSsml } from './htmlToSsml' +import * as _ from 'underscore' export interface TextToSpeechInput { text: string @@ -150,7 +151,7 @@ export const synthesizeTextToSpeech = async ( // for utterance const start = startSsml(ssmlOptions) wordOffset = -start.length - const ssml = `${start}${input.text}${endSsml()}` + const ssml = `${start}${_.escape(input.text)}${endSsml()}` const result = await speakSsmlAsyncPromise(ssml) return { audioData: Buffer.from(result.audioData), diff --git a/packages/text-to-speech/test/htmlToSsml.test.ts b/packages/text-to-speech/test/htmlToSsml.test.ts index 5f3b6bc90..0ab3acdf1 100644 --- a/packages/text-to-speech/test/htmlToSsml.test.ts +++ b/packages/text-to-speech/test/htmlToSsml.test.ts @@ -7,7 +7,7 @@ describe('htmlToSsmlItems', () => { primaryVoice: 'test-primary', secondaryVoice: 'test-secondary', language: 'en-US', - rate: 1, + rate: '1.0', } describe('a simple html file', () => {