Skip to content
Permalink
Browse files Browse the repository at this point in the history
Added function to replace characters with spaces and use it to saniti…
…ze text

Change-Id: I45ebf2da031f5526c202642cd8946b7202a44e53
  • Loading branch information
pasindud committed Sep 25, 2018
1 parent c145d46 commit f6660e6
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 11 deletions.
27 changes: 16 additions & 11 deletions festival_model_server/api.js
Expand Up @@ -28,21 +28,22 @@ router.get('/healthz', (_req, res) => res.json('OK'));
router.get('/alignment', (req, res) => {
const { text } = req.query;
// TODO(twattanavekin): Test Unicode text.
const escapedText = JSON.stringify(text).slice(1, -1);
const sanitizedText = utils.replaceCharactersWithSpaces(text);

const { festvoxFile, voiceName, localFolder } = utils.getVoiceSettings();

// Example command:
// read txt; TEXT=${txt};
// ${FESTIVALDIR}/bin/festival
// -b festvox/goog_vb_unison_cg.scm -b "(voice_goog_vb_unison_cg)"
// -b "(Parameter.set 'Audio_Method 'Audio_Command)"
// -b "(Parameter.set 'Audio_Command \"\")"
// -b "(utt.relation.print (SayText \" hello \") 'Segment)"
// -b "(utt.relation.print (SayText \" ${TEXT} \") 'Segment)";

const cmd = `${FESTIVAL_BIN} -b ${festvoxFile} -b "(${voiceName})" \
const cmd = `read txt; TEXT=\$\{txt\}; ${FESTIVAL_BIN} -b ${festvoxFile} -b "(${voiceName})" \
-b "(Parameter.set 'Audio_Method 'Audio_Command)" \
-b "(Parameter.set 'Audio_Command \\"\\")" \
-b "(utt.relation.print (SayText \\" ${escapedText} \\") 'Segment)"`;
-b "(utt.relation.print (SayText \\" \$\{TEXT\} \\") 'Segment)";`;

const options = {
cwd: localFolder,
Expand All @@ -52,38 +53,40 @@ router.get('/alignment', (req, res) => {

console.log(`Running command - \n${cmd}`);

exec(cmd, options, (err, stdout, stderr) => {
const child = exec(cmd, options, (err, stdout, stderr) => {
const errMsg = utils.getExecErrorMessage(err, stderr);
if (errMsg) {
return res.status(500).send(errMsg);
}
return res.status(200).send(utils.getAlignmentData(stdout.toString()));
});
child.stdin.write(sanitizedText);
child.stdin.end();
});

/** GET synthesize voice based on a voice model */
router.get('/tts', (req, res) => {
const { text, type } = req.query;
// TODO(twattanavekin): Test Unicode text.
const escapedText = JSON.stringify(text).slice(1, -1);
const sanitizedText = utils.replaceCharactersWithSpaces(text);
console.log(`Synthesizing ${sanitizedText}`);

console.log(`Synthesize "${text}"`);
const { festvoxFile, voiceName, localFolder } = utils.getVoiceSettings();

// Example command:
// echo "Hello world" | festival/bin/text2wave \
// festival/bin/text2wave \
// -eval festvox/goog_vb_unison_cg.scm \
// -eval "(voice_vb_unison_cg)"
const cmd = `echo "${escapedText}" \
| ${TEXT_TO_WAV_BIN} -eval ${festvoxFile} -eval "(${voiceName})"`;
const cmd = `${TEXT_TO_WAV_BIN} -eval ${festvoxFile} -eval "(${voiceName})"`;
console.log(`Running command - \n${cmd}`);

const options = {
cwd: localFolder,
timeout: 10000,
encoding: 'buffer',
};
exec(cmd, options, (err, stdout, stderr) => {

const child = exec(cmd, options, (err, stdout, stderr) => {
const errMsg = utils.getExecErrorMessage(err, stderr);
if (errMsg) {
return res.status(500).send(errMsg);
Expand All @@ -93,6 +96,8 @@ router.get('/tts', (req, res) => {
const data = type === 'base64' ? stdout.toString('base64') : stdout;
return res.send(data);
});
child.stdin.write(sanitizedText);
child.stdin.end();
});

module.exports = router;
24 changes: 24 additions & 0 deletions festival_model_server/settings.js
@@ -0,0 +1,24 @@
// Copyright 2018 Google Inc.
//
// 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
//
// https://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.

/**
* Module containing settings for merlin server.
*/
const settings = {
// Festival synthesis pipeline might not handle these characters as expected

This comment has been minimized.

Copy link
@mikkorantalainen

mikkorantalainen Nov 14, 2020

I think this comment should say that not replacing these characters with spaces will open a security hole.

// and also they are typically not needed during tts synthesis.
// Therefore we replace them with spaces.
CHARACTERS_TO_REPLACE_WITH_SPACES: '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~',
};
module.exports = settings;
28 changes: 28 additions & 0 deletions festival_model_server/tests/UtilsTests.js
Expand Up @@ -14,6 +14,34 @@

const utils = require('../utils');

describe('Testing utils replaceCharactersWithSpaces method', () => {
it('The function should return characters replaced with spaces and trim', () => {
const testData = [
['`<text>`', 'text'],
['`text`', 'text'],
['$(text)', 'text'],
['"\$(text\)', 'text'],
['\"\$(text\)', 'text'],
['${{{text}', 'text'],
['\$\{text\}', 'text'],
['\${text"}', 'text'],
['"${{text"}', 'text'],
['"${text"}', 'text'],
["\"ශ්රී ලංකාව'\"", 'ශ්රී ලංකාව'],
['"$text"', 'text'],
['$text', 'text'],
[",./;'[]*\-=", ''],
['echo `<text>`', 'echo text'],
['echo $("text")', 'echo text'],
['echo \$\{text\}', 'echo text'],
];

testData.forEach(function(entry) {
expect(utils.replaceCharactersWithSpaces(entry[0])).toEqual(entry[1]);
});
});
});

describe('Testing utils getAlignmentData method', () => {
const generateTestData = () => {
let testContent = '()\n'; // Alignment data starts with this.
Expand Down
14 changes: 14 additions & 0 deletions festival_model_server/utils.js
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

const settings = require('./settings');

/**
* Module containing util methods.
*/
Expand Down Expand Up @@ -87,8 +89,20 @@ const getVoiceSettings = () => ({
localFolder: MODEL_LOCAL_PATH,
});

/**
* Returns the original string with characters
* specified in settings replaced with spaces.
* @text {String} text to be replaced.
*/
const replaceCharactersWithSpaces = (text) => {
const re = new RegExp(`[${settings.CHARACTERS_TO_REPLACE_WITH_SPACES}]`, 'g');
const newText = text.replace(re, ' ').trim();
return newText;
};

module.exports = {
getAlignmentData,
getExecErrorMessage,
getVoiceSettings,
replaceCharactersWithSpaces,
};

0 comments on commit f6660e6

Please sign in to comment.