diff --git a/lib/tasks/say.js b/lib/tasks/say.js index 2ed044fb..285b9bf3 100644 --- a/lib/tasks/say.js +++ b/lib/tasks/say.js @@ -6,7 +6,7 @@ class TaskSay extends Task { super(logger, opts); this.preconditions = TaskPreconditions.Endpoint; - this.text = this.data.text; + this.text = Array.isArray(this.data.text) ? this.data.text : [this.data.text]; this.loop = this.data.loop || 1; this.earlyMedia = this.data.earlyMedia === true || (parentTask && parentTask.earlyMedia); this.synthesizer = this.data.synthesizer || {}; @@ -20,21 +20,23 @@ class TaskSay extends Task { await super.exec(cs); this.ep = ep; try { - let filepath; - const opts = Object.assign({ - text: this.text, - vendor: cs.speechSynthesisVendor, - language: cs.speechSynthesisLanguage, - voice: cs.speechSynthesisVoice - }, this.synthesizer); - + const filepath = []; while (!this.killed && this.loop--) { - if (!filepath) { - this.logger.debug('TaskSay:exec - retrieving synthesized audio'); - filepath = await synthAudio(opts); - cs.trackTmpFile(filepath); - } - await ep.play(filepath); + let segment = 0; + do { + if (filepath.length <= segment) { + const opts = Object.assign({ + text: this.text[segment], + vendor: cs.speechSynthesisVendor, + language: cs.speechSynthesisLanguage, + voice: cs.speechSynthesisVoice + }, this.synthesizer); + const path = await synthAudio(opts); + filepath.push(path); + cs.trackTmpFile(path); + } + await ep.play(filepath[segment]); + } while (++segment < this.text.length); } } catch (err) { this.logger.info(err, 'TaskSay:exec error'); diff --git a/lib/tasks/specs.json b/lib/tasks/specs.json index d940e98a..14dc2413 100644 --- a/lib/tasks/specs.json +++ b/lib/tasks/specs.json @@ -28,7 +28,7 @@ }, "say": { "properties": { - "text": "string", + "text": "string|array", "loop": "number", "synthesizer": "#synthesizer", "earlyMedia": "boolean" diff --git a/lib/tasks/task.js b/lib/tasks/task.js index f9dc0c63..d9c1770d 100644 --- a/lib/tasks/task.js +++ b/lib/tasks/task.js @@ -117,21 +117,21 @@ class Task extends Emitter { const dSpec = specData.properties[dKey]; debug(`Task:validate validating property ${dKey} with value ${JSON.stringify(dVal)}`); - if (typeof dSpec === 'string' && ['number', 'string', 'object', 'boolean'].includes(dSpec)) { - // simple types - if (typeof dVal !== specData.properties[dKey]) { - throw new Error(`${name}: property ${dKey} has invalid data type`); - } - } - else if (typeof dSpec === 'string' && dSpec === 'array') { + if (typeof dSpec === 'string' && dSpec === 'array') { if (!Array.isArray(dVal)) throw new Error(`${name}: property ${dKey} is not an array`); } else if (typeof dSpec === 'string' && dSpec.includes('|')) { const types = dSpec.split('|').map((t) => t.trim()); - if (!types.includes(typeof dVal)) { + if (!types.includes(typeof dVal) && !(types.includes('array') && Array.isArray(dVal))) { throw new Error(`${name}: property ${dKey} has invalid data type, must be one of ${types}`); } } + else if (typeof dSpec === 'string' && ['number', 'string', 'object', 'boolean'].includes(dSpec)) { + // simple types + if (typeof dVal !== specData.properties[dKey]) { + throw new Error(`${name}: property ${dKey} has invalid data type`); + } + } else if (Array.isArray(dSpec) && dSpec[0].startsWith('#')) { const name = dSpec[0].slice(1); for (const item of dVal) { diff --git a/test/data/good/say-text-array.json b/test/data/good/say-text-array.json new file mode 100644 index 00000000..094caa4f --- /dev/null +++ b/test/data/good/say-text-array.json @@ -0,0 +1,9 @@ +{ + "say": { + "text": ["hi there", "John"], + "synthesizer": { + "vendor": "google", + "language": "en-US" + } + } +} \ No newline at end of file diff --git a/test/data/good/say.json b/test/data/good/say.json new file mode 100644 index 00000000..1379e124 --- /dev/null +++ b/test/data/good/say.json @@ -0,0 +1,9 @@ +{ + "say": { + "text": "hi there", + "synthesizer": { + "vendor": "google", + "language": "en-US" + } + } +} \ No newline at end of file diff --git a/test/unit-tests.js b/test/unit-tests.js index 9caecca9..ecbe8d55 100644 --- a/test/unit-tests.js +++ b/test/unit-tests.js @@ -39,6 +39,12 @@ test('app payload parsing tests', (t) => { task = makeTask(logger, require('./data/good/pause')); t.ok(task.name === 'pause', 'parsed pause'); + task = makeTask(logger, require('./data/good/say')); + t.ok(task.name === 'say', 'parsed say'); + + task = makeTask(logger, require('./data/good/say-text-array')); + t.ok(task.name === 'say', 'parsed say with multiple segments'); + const alt = require('./data/good/alternate-syntax'); const normalize = require('../lib/utils/normalize-jambones'); normalize(logger, alt).forEach((t) => {