diff --git a/.github/workflows/texttospeech.yaml b/.github/workflows/texttospeech.yaml new file mode 100644 index 0000000000..39cd82f5ad --- /dev/null +++ b/.github/workflows/texttospeech.yaml @@ -0,0 +1,68 @@ +name: texttospeech +on: + push: + branches: + - main + paths: + - 'texttospeech/**' + pull_request: + paths: + - 'texttospeech/**' + pull_request_target: + types: [labeled] + paths: + - 'texttospeech/**' + schedule: + - cron: '0 0 * * 0' +jobs: + test: + if: ${{ github.event.action != 'labeled' || github.event.label.name == 'actions:force-run' }} + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: 'write' + pull-requests: 'write' + id-token: 'write' + steps: + - uses: actions/checkout@v3.1.0 + with: + ref: ${{github.event.pull_request.head.sha}} + - uses: 'google-github-actions/auth@v1.0.0' + with: + workload_identity_provider: 'projects/1046198160504/locations/global/workloadIdentityPools/github-actions-pool/providers/github-actions-provider' + service_account: 'kokoro-system-test@long-door-651.iam.gserviceaccount.com' + create_credentials_file: 'true' + access_token_lifetime: 600s + - uses: actions/setup-node@v3.5.1 + with: + node-version: 16 + - run: npm install + working-directory: texttospeech + - run: npm test + working-directory: texttospeech + env: + MOCHA_REPORTER_SUITENAME: texttospeech + MOCHA_REPORTER_OUTPUT: texttospeech_sponge_log.xml + MOCHA_REPORTER: xunit + - if: ${{ github.event.action == 'labeled' && github.event.label.name == 'actions:force-run' }} + uses: actions/github-script@v6 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + try { + await github.rest.issues.removeLabel({ + name: 'actions:force-run', + owner: 'GoogleCloudPlatform', + repo: 'nodejs-docs-samples', + issue_number: context.payload.pull_request.number + }); + } catch (e) { + if (!e.message.includes('Label does not exist')) { + throw e; + } + } + - if: ${{ github.event_name == 'schedule'}} + run: | + curl https://github.com/googleapis/repo-automation-bots/releases/download/flakybot-1.1.0/flakybot -o flakybot -s -L + chmod +x ./flakybot + ./flakybot --repo GoogleCloudPlatform/nodejs-docs-samples --commit_hash ${{github.sha}} --build_url https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} diff --git a/.github/workflows/workflows.json b/.github/workflows/workflows.json index e3c110eeed..fa274e2139 100644 --- a/.github/workflows/workflows.json +++ b/.github/workflows/workflows.json @@ -61,6 +61,7 @@ "scheduler", "speech", "talent", + "texttospeech", "translate", "video-intelligence", "contact-center-insights", diff --git a/CODEOWNERS b/CODEOWNERS index f973e3bb0d..ab1bb75f7e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -42,7 +42,14 @@ monitoring/opencensus @GoogleCloudPlatform/nodejs-samples-reviewers # Data & AI contact-center-insights @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers +datalabeling @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers +document-ai @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers +mediatranslation @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers +speech @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers talent @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers +texttospeech @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers +translate @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers +video-intelligence @GoogleCloudPlatform/dee-data-ai @GoogleCloudPlatform/nodejs-samples-reviewers # DEE-PO error-reporting @GoogleCloudPlatform/dee-platform-ops @GoogleCloudPlatform/nodejs-samples-reviewers \ No newline at end of file diff --git a/texttospeech/.eslintrc.yml b/texttospeech/.eslintrc.yml new file mode 100644 index 0000000000..282535f55f --- /dev/null +++ b/texttospeech/.eslintrc.yml @@ -0,0 +1,3 @@ +--- +rules: + no-console: off diff --git a/texttospeech/audioProfile.js b/texttospeech/audioProfile.js new file mode 100644 index 0000000000..c991f33f61 --- /dev/null +++ b/texttospeech/audioProfile.js @@ -0,0 +1,65 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; +async function main( + text = 'Hello Everybody! This is an audio profile optimized sound byte.', + outputFile = './resources/phone.mp3', + languageCode = 'en-US', + ssmlGender = 'FEMALE' +) { + //[START tts_synthesize_text_audio_profile] + + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + // const text = 'Text you want to vocalize'; + // const outputFile = 'YOUR_OUTPUT_FILE_LOCAtION; + // const languageCode = 'LANGUAGE_CODE_FOR_OUTPUT'; + // const ssmlGender = 'SSML_GENDER_OF_SPEAKER'; + + // Imports the Google Cloud client library + const speech = require('@google-cloud/text-to-speech'); + const fs = require('fs'); + const util = require('util'); + + // Creates a client + const client = new speech.TextToSpeechClient(); + + async function synthesizeWithEffectsProfile() { + // Add one or more effects profiles to array. + // Refer to documentation for more details: + // https://cloud.google.com/text-to-speech/docs/audio-profiles + const effectsProfileId = ['telephony-class-application']; + + const request = { + input: {text: text}, + voice: {languageCode: languageCode, ssmlGender: ssmlGender}, + audioConfig: {audioEncoding: 'MP3', effectsProfileId: effectsProfileId}, + }; + + const [response] = await client.synthesizeSpeech(request); + const writeFile = util.promisify(fs.writeFile); + await writeFile(outputFile, response.audioContent, 'binary'); + console.log(`Audio content written to file: ${outputFile}`); + } + // [END tts_synthesize_text_audio_profile] + + synthesizeWithEffectsProfile(); +} + +main(...process.argv.slice(2)).catch(err => { + console.error(err); + process.exitCode = 1; +}); diff --git a/texttospeech/hello.mp3 b/texttospeech/hello.mp3 new file mode 100644 index 0000000000..726773ed65 Binary files /dev/null and b/texttospeech/hello.mp3 differ diff --git a/texttospeech/listVoices.js b/texttospeech/listVoices.js new file mode 100644 index 0000000000..84792d2ab0 --- /dev/null +++ b/texttospeech/listVoices.js @@ -0,0 +1,53 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +async function listVoices() { + // [START tts_list_voices] + const textToSpeech = require('@google-cloud/text-to-speech'); + + const client = new textToSpeech.TextToSpeechClient(); + + const [result] = await client.listVoices({}); + const voices = result.voices; + + console.log('Voices:'); + voices.forEach(voice => { + console.log(`Name: ${voice.name}`); + console.log(` SSML Voice Gender: ${voice.ssmlGender}`); + console.log(` Natural Sample Rate Hertz: ${voice.naturalSampleRateHertz}`); + console.log(' Supported languages:'); + voice.languageCodes.forEach(languageCode => { + console.log(` ${languageCode}`); + }); + }); + // [END tts_list_voices] +} + +async function main() { +require(`yargs`) // eslint-disable-line + .demand(1) + .command('list-voices', 'List supported voices.', {}, () => listVoices()) + .example('node $0 list-voices') + .wrap(120) + .recommendCommands() + .epilogue( + 'For more information, see https://cloud.google.com/text-to-speech/docs' + ) + .help() + .strict().argv; +} + +main().catch(console.error); diff --git a/texttospeech/package.json b/texttospeech/package.json new file mode 100644 index 0000000000..4af1875f74 --- /dev/null +++ b/texttospeech/package.json @@ -0,0 +1,24 @@ +{ + "name": "nodejs-docs-samples-text-to-speech", + "private": true, + "license": "Apache-2.0", + "author": "Google Inc.", + "repository": "googleapis/nodejs-text-to-speech", + "files": [ + "*.js" + ], + "engines": { + "node": ">=12.0.0" + }, + "scripts": { + "test": "mocha --timeout=60000" + }, + "dependencies": { + "@google-cloud/text-to-speech": "^4.0.4", + "yargs": "^16.0.0" + }, + "devDependencies": { + "chai": "^4.2.0", + "mocha": "^8.0.0" + } +} diff --git a/texttospeech/quickstart.js b/texttospeech/quickstart.js new file mode 100644 index 0000000000..92718a9a98 --- /dev/null +++ b/texttospeech/quickstart.js @@ -0,0 +1,51 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +function main() { + // [START tts_quickstart] + // Imports the Google Cloud client library + const textToSpeech = require('@google-cloud/text-to-speech'); + + // Import other required libraries + const fs = require('fs'); + const util = require('util'); + // Creates a client + const client = new textToSpeech.TextToSpeechClient(); + async function quickStart() { + // The text to synthesize + const text = 'hello, world!'; + + // Construct the request + const request = { + input: {text: text}, + // Select the language and SSML voice gender (optional) + voice: {languageCode: 'en-US', ssmlGender: 'NEUTRAL'}, + // select the type of audio encoding + audioConfig: {audioEncoding: 'MP3'}, + }; + + // Performs the text-to-speech request + const [response] = await client.synthesizeSpeech(request); + // Write the binary audio content to a local file + const writeFile = util.promisify(fs.writeFile); + await writeFile('output.mp3', response.audioContent, 'binary'); + console.log('Audio content written to file: output.mp3'); + } + quickStart(); + // [END tts_quickstart] +} + +main(...process.argv.slice(2)); diff --git a/texttospeech/resources/example.ssml b/texttospeech/resources/example.ssml new file mode 100644 index 0000000000..1e20716f0d --- /dev/null +++ b/texttospeech/resources/example.ssml @@ -0,0 +1,4 @@ +123 Street Ln, Small Town, IL 12345 USA +1 Jenny St & Number St, Tutone City, CA 86753 +1 Piazza del Fibonacci, 12358 Pisa, Italy + \ No newline at end of file diff --git a/texttospeech/resources/example.txt b/texttospeech/resources/example.txt new file mode 100644 index 0000000000..9cd7d74db3 --- /dev/null +++ b/texttospeech/resources/example.txt @@ -0,0 +1,3 @@ +123 Street Ln, Small Town, IL 12345 USA +1 Jenny St & Number St, Tutone City, CA 86753 +1 Piazza del Fibonacci, 12358 Pisa, Italy diff --git a/texttospeech/resources/expected_example.mp3 b/texttospeech/resources/expected_example.mp3 new file mode 100644 index 0000000000..2e4f7f2f6e Binary files /dev/null and b/texttospeech/resources/expected_example.mp3 differ diff --git a/texttospeech/resources/hello.ssml b/texttospeech/resources/hello.ssml new file mode 100644 index 0000000000..df7bf9eee3 --- /dev/null +++ b/texttospeech/resources/hello.ssml @@ -0,0 +1 @@ +Hello there. diff --git a/texttospeech/resources/hello.txt b/texttospeech/resources/hello.txt new file mode 100644 index 0000000000..c12abce9f3 --- /dev/null +++ b/texttospeech/resources/hello.txt @@ -0,0 +1 @@ +Hello there. diff --git a/texttospeech/ssmlAddresses.js b/texttospeech/ssmlAddresses.js new file mode 100644 index 0000000000..26caf86539 --- /dev/null +++ b/texttospeech/ssmlAddresses.js @@ -0,0 +1,126 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +function main( + inFile = 'resources/example.txt', + outFile = 'resources/example.mp3' +) { + // [START tts_ssml_address_test] + /** + * TODO (developer): Uncomment these variables before running the sample + * + */ + // inFile = 'resources/example.txt', + // outFile = 'resources/example.mp3' + // [START tts_ssml_address_imports] + // Imports the Google Cloud client library + const textToSpeech = require('@google-cloud/text-to-speech'); + + // Import other required libraries + const fs = require('fs'); + //const escape = require('escape-html'); + const util = require('util'); + // [END tts_ssml_address_imports] + + // [START tts_ssml_address_audio] + /** + * Generates synthetic audio from a String of SSML text. + * + * Given a string of SSML text and an output file name, this function + * calls the Text-to-Speech API. The API returns a synthetic audio + * version of the text, formatted according to the SSML commands. This + * function saves the synthetic audio to the designated output file. + * + * ARGS + * ssmlText: String of tagged SSML text + * outfile: String name of file under which to save audio output + * RETURNS + * nothing + * + */ + async function ssmlToAudio(ssmlText, outFile) { + // Creates a client + const client = new textToSpeech.TextToSpeechClient(); + + // Constructs the request + const request = { + // Select the text to synthesize + input: {ssml: ssmlText}, + // Select the language and SSML Voice Gender (optional) + voice: {languageCode: 'en-US', ssmlGender: 'MALE'}, + // Select the type of audio encoding + audioConfig: {audioEncoding: 'MP3'}, + }; + + // Performs the Text-to-Speech request + const [response] = await client.synthesizeSpeech(request); + // Write the binary audio content to a local file + const writeFile = util.promisify(fs.writeFile); + await writeFile(outFile, response.audioContent, 'binary'); + console.log('Audio content written to file ' + outFile); + } + // [END tts_ssml_address_audio] + + // [START tts_ssml_address_ssml] + /** + * Generates SSML text from plaintext. + * + * Given an input filename, this function converts the contents of the input text file + * into a String of tagged SSML text. This function formats the SSML String so that, + * when synthesized, the synthetic audio will pause for two seconds between each line + * of the text file. This function also handles special text characters which might + * interfere with SSML commands. + * + * ARGS + * inputfile: String name of plaintext file + * RETURNS + * a String of SSML text based on plaintext input + * + */ + function textToSsml(inputFile) { + let rawLines = ''; + // Read input file + try { + rawLines = fs.readFileSync(inputFile, 'utf8'); + } catch (e) { + console.log('Error:', e.stack); + return; + } + + // Replace special characters with HTML Ampersand Character Codes + // These codes prevent the API from confusing text with SSML tags + // For example, '<' --> '<' and '&' --> '&' + let escapedLines = rawLines; + escapedLines = escapedLines.replace(/&/g, '&'); + escapedLines = escapedLines.replace(/"/g, '"'); + escapedLines = escapedLines.replace(//g, '>'); + + // Convert plaintext to SSML + // Tag SSML so that there is a 2 second pause between each address + const expandedNewline = escapedLines.replace(/\n/g, '\n'); + const ssml = '' + expandedNewline + ''; + + // Return the concatenated String of SSML + return ssml; + } + // [END tts_ssml_address_ssml] + const ssml = textToSsml(inFile); + ssmlToAudio(ssml, outFile); + // [END tts_ssml_address_test] +} + +main(...process.argv.slice(2)); diff --git a/texttospeech/synthesize.js b/texttospeech/synthesize.js new file mode 100644 index 0000000000..8344d7193c --- /dev/null +++ b/texttospeech/synthesize.js @@ -0,0 +1,166 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; +async function synthesizeText(text, outputFile) { + // [START tts_synthesize_text] + const textToSpeech = require('@google-cloud/text-to-speech'); + const fs = require('fs'); + const util = require('util'); + + const client = new textToSpeech.TextToSpeechClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const text = 'Text to synthesize, eg. hello'; + // const outputFile = 'Local path to save audio file to, e.g. output.mp3'; + + const request = { + input: {text: text}, + voice: {languageCode: 'en-US', ssmlGender: 'FEMALE'}, + audioConfig: {audioEncoding: 'MP3'}, + }; + const [response] = await client.synthesizeSpeech(request); + const writeFile = util.promisify(fs.writeFile); + await writeFile(outputFile, response.audioContent, 'binary'); + console.log(`Audio content written to file: ${outputFile}`); + // [END tts_synthesize_text] +} + +async function synthesizeSsml(ssml, outputFile) { + // [START tts_synthesize_ssml] + const textToSpeech = require('@google-cloud/text-to-speech'); + const fs = require('fs'); + const util = require('util'); + + const client = new textToSpeech.TextToSpeechClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const ssml = 'Hello there.'; + // const outputFile = 'Local path to save audio file to, e.g. output.mp3'; + + const request = { + input: {ssml: ssml}, + voice: {languageCode: 'en-US', ssmlGender: 'FEMALE'}, + audioConfig: {audioEncoding: 'MP3'}, + }; + + const [response] = await client.synthesizeSpeech(request); + const writeFile = util.promisify(fs.writeFile); + await writeFile(outputFile, response.audioContent, 'binary'); + console.log(`Audio content written to file: ${outputFile}`); + // [END tts_synthesize_ssml] +} + +async function synthesizeTextFile(textFile, outputFile) { + // [START tts_synthesize_text_file] + const textToSpeech = require('@google-cloud/text-to-speech'); + const fs = require('fs'); + const util = require('util'); + + const client = new textToSpeech.TextToSpeechClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const textFile = 'Local path to text file, eg. input.txt'; + // const outputFile = 'Local path to save audio file to, e.g. output.mp3'; + + const request = { + input: {text: fs.readFileSync(textFile)}, + voice: {languageCode: 'en-US', ssmlGender: 'FEMALE'}, + audioConfig: {audioEncoding: 'MP3'}, + }; + + const [response] = await client.synthesizeSpeech(request); + const writeFile = util.promisify(fs.writeFile); + await writeFile(outputFile, response.audioContent, 'binary'); + console.log(`Audio content written to file: ${outputFile}`); + // [END tts_synthesize_text_file] +} + +async function synthesizeSsmlFile(ssmlFile, outputFile) { + // [START tts_synthesize_ssml_file] + const textToSpeech = require('@google-cloud/text-to-speech'); + const fs = require('fs'); + const util = require('util'); + + const client = new textToSpeech.TextToSpeechClient(); + + /** + * TODO(developer): Uncomment the following lines before running the sample. + */ + // const ssmlFile = 'Local path to SSML file, eg. input.ssml'; + // const outputFile = 'Local path to save audio file to, e.g. output.mp3'; + + const request = { + input: {ssml: fs.readFileSync(ssmlFile)}, + voice: {languageCode: 'en-US', ssmlGender: 'FEMALE'}, + audioConfig: {audioEncoding: 'MP3'}, + }; + + const [response] = await client.synthesizeSpeech(request); + const writeFile = util.promisify(fs.writeFile); + await writeFile(outputFile, response.audioContent, 'binary'); + console.log(`Audio content written to file: ${outputFile}`); + // [END tts_synthesize_ssml_file] +} + +async function main() { +require(`yargs`) // eslint-disable-line + .demand(1) + .command('text ', 'Synthesizes audio file from text', {}, opts => + synthesizeText(opts.text, opts.outputFile) + ) + .command('ssml ', 'Synthesizes audio file from SSML', {}, opts => + synthesizeSsml(opts.ssml, opts.outputFile) + ) + .command( + 'text-file ', + 'Synthesizes audio file from text in a file', + {}, + opts => synthesizeTextFile(opts.textFile, opts.outputFile) + ) + .command( + 'ssml-file ', + 'Synthesizes audio file from SSML in a file', + {}, + opts => synthesizeSsmlFile(opts.ssmlFile, opts.outputFile) + ) + .options({ + outputFile: { + alias: 'o', + default: 'output.mp3', + global: true, + requiresArg: true, + type: 'string', + }, + }) + .example('node $0 text "hello" -o hello.mp3') + .example('node $0 ssml "Hello there." -o hello.mp3') + .example('node $0 text-file resources/hello.txt -o output.mp3') + .example('node $0 ssml-file resources/hello.ssml -o output.mp3') + .wrap(120) + .recommendCommands() + .epilogue( + 'For more information, see https://cloud.google.com/text-to-speech/docs' + ) + .help() + .strict().argv; +} + +main().catch(console.error); diff --git a/texttospeech/test/.eslintrc.yml b/texttospeech/test/.eslintrc.yml new file mode 100644 index 0000000000..ed97d539c0 --- /dev/null +++ b/texttospeech/test/.eslintrc.yml @@ -0,0 +1 @@ +--- diff --git a/texttospeech/test/audioProfile.test.js b/texttospeech/test/audioProfile.test.js new file mode 100644 index 0000000000..44069e0691 --- /dev/null +++ b/texttospeech/test/audioProfile.test.js @@ -0,0 +1,47 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const fs = require('fs'); +const {assert} = require('chai'); +const {describe, it, after} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cmd = 'node audioProfile.js'; +const text = + '"Hello Everybody! This is an Audio Profile Optimized Sound Byte."'; +const outputFile = 'phonetest.mp3'; + +describe('audio profile', () => { + after(() => { + function unlink(outputFile) { + try { + fs.unlinkSync(outputFile); + } catch (err) { + // Ignore error + } + } + [outputFile].map(unlink); + }); + + it('should synthesize human audio using hardware profile', async () => { + assert.strictEqual(fs.existsSync(outputFile), false); + const output = execSync(`${cmd} ${text} ${outputFile}`); + assert.match(output, new RegExp('Audio content written to file:')); + assert.ok(fs.existsSync(outputFile)); + }); +}); diff --git a/texttospeech/test/listVoices.test.js b/texttospeech/test/listVoices.test.js new file mode 100644 index 0000000000..7af1e9579e --- /dev/null +++ b/texttospeech/test/listVoices.test.js @@ -0,0 +1,31 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const {assert} = require('chai'); +const {describe, it} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cmd = 'node listVoices.js'; + +describe('list voices', () => { + it('should list voices', async () => { + const stdout = execSync(`${cmd} list-voices`); + assert.match(stdout, /SSML Voice Gender: FEMALE/); + assert.match(stdout, /Natural Sample Rate Hertz: 24000/); + }); +}); diff --git a/texttospeech/test/quickstart.test.js b/texttospeech/test/quickstart.test.js new file mode 100644 index 0000000000..cd1460c7b5 --- /dev/null +++ b/texttospeech/test/quickstart.test.js @@ -0,0 +1,41 @@ +// Copyright 2017 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const fs = require('fs'); +const {assert} = require('chai'); +const {describe, it, after} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const outputFile = 'output.mp3'; + +describe('quickstart', () => { + after(() => { + try { + fs.unlinkSync(outputFile); + } catch (err) { + // Ignore error + } + }); + + it('should synthesize speech to local mp3 file', () => { + assert.strictEqual(fs.existsSync(outputFile), false); + const stdout = execSync('node quickstart'); + assert.match(stdout, /Audio content written to file: output.mp3/); + assert.ok(fs.existsSync(outputFile)); + }); +}); diff --git a/texttospeech/test/ssmlAddresses.test.js b/texttospeech/test/ssmlAddresses.test.js new file mode 100644 index 0000000000..beaa932150 --- /dev/null +++ b/texttospeech/test/ssmlAddresses.test.js @@ -0,0 +1,52 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const fs = require('fs'); +const {assert} = require('chai'); +const {describe, it, before, after} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cmd = 'node ssmlAddresses.js'; +const outputFile = 'resources/example.mp3'; + +describe('ssmlAddresses', () => { + // delete 'resources/example.mp3' file if it already exists + before(() => { + try { + fs.unlinkSync(outputFile); + } catch (e) { + // don't throw an exception + } + }); + + // delete 'resources/example.mp3' file + after(() => { + fs.unlinkSync(outputFile); + assert.strictEqual(fs.existsSync(outputFile), false); + }); + + it('synthesize speech to local mp3 file', async () => { + assert.strictEqual(fs.existsSync(outputFile), false); + const stdout = execSync(`${cmd}`); + assert.match( + stdout, + /Audio content written to file resources\/example.mp3/ + ); + assert.strictEqual(fs.existsSync(outputFile), true); + }); +}); diff --git a/texttospeech/test/synthesize.test.js b/texttospeech/test/synthesize.test.js new file mode 100644 index 0000000000..ad50a48547 --- /dev/null +++ b/texttospeech/test/synthesize.test.js @@ -0,0 +1,88 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const {assert} = require('chai'); +const {describe, it, afterEach} = require('mocha'); +const cp = require('child_process'); + +const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'}); + +const cmd = 'node synthesize.js'; +const text = 'Hello there.'; +const ssml = 'Hello there.'; +const outputFile = 'test-output.mp3'; +const files = ['hello.txt', 'hello.ssml'].map(name => { + return { + name, + localPath: path.resolve(path.join(__dirname, `../resources/${name}`)), + }; +}); + +describe('synthesize', () => { + afterEach(() => { + try { + fs.unlinkSync(outputFile); + } catch (err) { + // Ignore error + } + }); + + it('should synthesize audio from text', async () => { + assert.strictEqual(fs.existsSync(outputFile), false); + const output = execSync(`${cmd} text '${text}' --outputFile ${outputFile}`); + assert.match( + output, + new RegExp(`Audio content written to file: ${outputFile}`) + ); + assert.ok(fs.existsSync(outputFile)); + }); + + it('should synthesize audio from ssml', async () => { + assert.strictEqual(fs.existsSync(outputFile), false); + const output = execSync(`${cmd} ssml "${ssml}" --outputFile ${outputFile}`); + assert.match( + output, + new RegExp(`Audio content written to file: ${outputFile}`) + ); + assert.ok(fs.existsSync(outputFile)); + }); + + it('should synthesize audio from text file', async () => { + assert.strictEqual(fs.existsSync(outputFile), false); + const output = execSync( + `${cmd} text-file ${files[0].localPath} --outputFile ${outputFile}` + ); + assert.match( + output, + new RegExp(`Audio content written to file: ${outputFile}`) + ); + assert.ok(fs.existsSync(outputFile)); + }); + + it('should synthesize audio from ssml file', async () => { + assert.strictEqual(fs.existsSync(outputFile), false); + const output = execSync( + `${cmd} ssml-file ${files[1].localPath} --outputFile ${outputFile}` + ); + assert.match( + output, + new RegExp(`Audio content written to file: ${outputFile}`) + ); + assert.ok(fs.existsSync(outputFile)); + }); +});