Skip to content

Commit a2c6dd8

Browse files
crowdusleahecole
authored andcommitted
feat: code samples for ssml addresses tutorial (#257)
* feat: code samples for ssml addresses tutorial * fix: lint * fix: lint * fix: lint * fix: debugging delete example.mp3 file * fix: mp3 test file deletion * fix: refactored tests * fix: region tag formatting for main * wrapping main
1 parent fb5ac72 commit a2c6dd8

File tree

5 files changed

+186
-0
lines changed

5 files changed

+186
-0
lines changed

texttospeech/resources/example.ssml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<speak>123 Street Ln, Small Town, IL 12345 USA
2+
<break time="2s"/>1 Jenny St &amp; Number St, Tutone City, CA 86753
3+
<break time="2s"/>1 Piazza del Fibonacci, 12358 Pisa, Italy
4+
<break time="2s"/></speak>

texttospeech/resources/example.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
123 Street Ln, Small Town, IL 12345 USA
2+
1 Jenny St & Number St, Tutone City, CA 86753
3+
1 Piazza del Fibonacci, 12358 Pisa, Italy
82.9 KB
Binary file not shown.

texttospeech/ssmlAddresses.js

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* Copyright 2019 Google LLC
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
'use strict';
17+
18+
function main(
19+
inFile = 'resources/example.txt',
20+
outFile = 'resources/example.mp3'
21+
) {
22+
// [START tts_ssml_address_test]
23+
/**
24+
* TODO (developer): Uncomment these variables before running the sample
25+
*
26+
*/
27+
// inFile = 'resources/example.txt',
28+
// outFile = 'resources/example.mp3'
29+
// [START tts_ssml_address_imports]
30+
// Imports the Google Cloud client library
31+
const textToSpeech = require('@google-cloud/text-to-speech');
32+
33+
// Import other required libraries
34+
const fs = require('fs');
35+
//const escape = require('escape-html');
36+
const util = require('util');
37+
// [END tts_ssml_address_imports]
38+
39+
// [START tts_ssml_address_audio]
40+
/**
41+
* Generates synthetic audio from a String of SSML text.
42+
*
43+
* Given a string of SSML text and an output file name, this function
44+
* calls the Text-to-Speech API. The API returns a synthetic audio
45+
* version of the text, formatted according to the SSML commands. This
46+
* function saves the synthetic audio to the designated output file.
47+
*
48+
* ARGS
49+
* ssmlText: String of tagged SSML text
50+
* outfile: String name of file under which to save audio output
51+
* RETURNS
52+
* nothing
53+
*
54+
*/
55+
async function ssmlToAudio(ssmlText, outFile) {
56+
// Creates a client
57+
const client = new textToSpeech.TextToSpeechClient();
58+
59+
// Constructs the request
60+
const request = {
61+
// Select the text to synthesize
62+
input: {ssml: ssmlText},
63+
// Select the language and SSML Voice Gender (optional)
64+
voice: {languageCode: 'en-US', ssmlGender: 'MALE'},
65+
// Select the type of audio encoding
66+
audioConfig: {audioEncoding: 'MP3'},
67+
};
68+
69+
// Performs the Text-to-Speech request
70+
const [response] = await client.synthesizeSpeech(request);
71+
// Write the binary audio content to a local file
72+
const writeFile = util.promisify(fs.writeFile);
73+
await writeFile(outFile, response.audioContent, 'binary');
74+
console.log('Audio content written to file ' + outFile);
75+
}
76+
// [END tts_ssml_address_audio]
77+
78+
// [START tts_ssml_address_ssml]
79+
/**
80+
* Generates SSML text from plaintext.
81+
*
82+
* Given an input filename, this function converts the contents of the input text file
83+
* into a String of tagged SSML text. This function formats the SSML String so that,
84+
* when synthesized, the synthetic audio will pause for two seconds between each line
85+
* of the text file. This function also handles special text characters which might
86+
* interfere with SSML commands.
87+
*
88+
* ARGS
89+
* inputfile: String name of plaintext file
90+
* RETURNS
91+
* a String of SSML text based on plaintext input
92+
*
93+
*/
94+
function textToSsml(inputFile) {
95+
let rawLines = '';
96+
// Read input file
97+
try {
98+
rawLines = fs.readFileSync(inputFile, 'utf8');
99+
} catch (e) {
100+
console.log('Error:', e.stack);
101+
return;
102+
}
103+
104+
// Replace special characters with HTML Ampersand Character Codes
105+
// These codes prevent the API from confusing text with SSML tags
106+
// For example, '<' --> '&lt;' and '&' --> '&amp;'
107+
let escapedLines = rawLines;
108+
escapedLines = escapedLines.replace(/&/g, '&amp;');
109+
escapedLines = escapedLines.replace(/"/g, '&quot;');
110+
escapedLines = escapedLines.replace(/</g, '&lt;');
111+
escapedLines = escapedLines.replace(/>/g, '&gt;');
112+
113+
// Convert plaintext to SSML
114+
// Tag SSML so that there is a 2 second pause between each address
115+
const expandedNewline = escapedLines.replace(/\n/g, '\n<break time="2s"/>');
116+
const ssml = '<speak>' + expandedNewline + '</speak>';
117+
118+
// Return the concatenated String of SSML
119+
return ssml;
120+
}
121+
// [END tts_ssml_address_ssml]
122+
const ssml = textToSsml(inFile);
123+
ssmlToAudio(ssml, outFile);
124+
// [END tts_ssml_address_test]
125+
}
126+
127+
main(...process.argv.slice(2));
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Copyright 2019 Google LLC
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
'use strict';
17+
18+
const fs = require('fs');
19+
const {assert} = require('chai');
20+
const cp = require('child_process');
21+
22+
const execSync = cmd => cp.execSync(cmd, {encoding: 'utf-8'});
23+
24+
const cmd = 'node ssmlAddresses.js';
25+
const outputFile = 'resources/example.mp3';
26+
27+
describe('ssmlAddresses', () => {
28+
// delete 'resources/example.mp3' file if it already exists
29+
before(function() {
30+
try {
31+
fs.unlinkSync(outputFile);
32+
} catch (e) {
33+
// don't throw an exception
34+
}
35+
});
36+
37+
// delete 'resources/example.mp3' file
38+
after(function() {
39+
fs.unlinkSync(outputFile);
40+
assert.strictEqual(fs.existsSync(outputFile), false);
41+
});
42+
43+
it('synthesize speech to local mp3 file', async () => {
44+
assert.strictEqual(fs.existsSync(outputFile), false);
45+
const stdout = execSync(`${cmd}`);
46+
assert.match(
47+
stdout,
48+
/Audio content written to file resources\/example.mp3/
49+
);
50+
assert.strictEqual(fs.existsSync(outputFile), true);
51+
});
52+
});

0 commit comments

Comments
 (0)