diff --git a/README.md b/README.md index cdcf870..9d89842 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ var choo = require('choo') var html = require('choo/html') var app = choo() -app.use(require('choo-tts')) +app.use(require('choo-tts')()) app.route('/', mainView) app.mount('body') @@ -76,7 +76,7 @@ function speech (state, emitter) { }) } -// later in yout view +// later in your view function view (state, emit) { return html`
@@ -108,8 +108,21 @@ with every speech, but you can set this for a specific speech, if you set an `id property to the object passed to the speak event. ## API -### `tts = require('choo-tts')` -Load the plugin and populate a `tts` object to the state. +### `plugin = tts([opts])` +The plugin accept an options object and return the plugin. The `opts` object +can have the following properties: + +- `voice`: A string with the name of the voice to be selected as `selectedVoice`. +if not set, or not found, will use the browser default voice. +- `rate`: The speed of the utterance to be spoken, can be a value +from 0.1 to 10, defaults to 1. +- `pitch`: The pitch of the utterance to be spoken, can be a value from 0 +to 2, defaults to 1. +- `volume`: The volume of the utterance to be spoken, can be a value from 0 +to 1, defaults to 0.5. + +If everything goes right, then the plugin will populate a `tts` object to the +state. - `tts.state`: Get the state of text-to-speech object. Can be any of the following strings: `PAUSED`, `PENDING`, `SPEAKING` or `READY`. diff --git a/example.js b/example.js index 65aeeb5..1707d81 100644 --- a/example.js +++ b/example.js @@ -2,7 +2,7 @@ var choo = require('choo') var html = require('choo/html') var app = choo() -app.use(require('.')) +app.use(require('.')()) app.use(speech) app.route('/', mainView) app.mount('body') @@ -45,6 +45,6 @@ function speech (state, emitter) { }) emitter.on('tts:speech-end', function ({ event, id }) { window.alert(`speech took ${event.elapsedTime} ms`) - if (id === 'special') window.alert('Tha was special!') + if (id === 'special') window.alert('That was special!') }) } diff --git a/index.js b/index.js index 1771cea..4a2597e 100644 --- a/index.js +++ b/index.js @@ -14,75 +14,80 @@ var events = tts.events = { ERROR: 'tts:error' } -function tts (state, emitter) { - var synth = window.speechSynthesis - try { - if (!synth) throw new Error('tts:error - speechSynthesis not supported') - // set default values - state.tts = {} - state.tts.pitch = 1 - state.tts.rate = 1 - state.tts.volume = 0.5 - // readonly state - Object.defineProperty(state.tts, 'state', { - get: function () { - // check state - return synth.paused ? 'PAUSED' : synth.pending ? 'PENDING' : synth.speaking ? 'SPEAKING' : 'READY' - } - }) - Object.defineProperty(state.tts, 'voices', { - get: function () { - return synth.getVoices() - } - }) - state.tts.selectedVoice = state.tts.voices.filter(voice => voice.default)[0] +function tts (opts) { + opts = opts || {} + return function (state, emitter) { + var synth = window.speechSynthesis + try { + if (!synth) throw new Error('tts:error - speechSynthesis not supported') + // set default values + state.tts = {} + state.tts.pitch = opts.pitch || 1 + state.tts.rate = opts.rate || 1 + state.tts.volume = opts.volume || 0.5 + // readonly state + Object.defineProperty(state.tts, 'state', { + get: function () { + // check state + return synth.paused ? 'PAUSED' : synth.pending ? 'PENDING' : synth.speaking ? 'SPEAKING' : 'READY' + } + }) + Object.defineProperty(state.tts, 'voices', { + get: function () { + return synth.getVoices() + } + }) + state.tts.selectedVoice = state.tts.voices.filter(voice => opts.voice ? voice.name === opts.voice : voice.default)[0] + // if the voice name defined in options wasn't found + state.tts.selectedVoice = state.tts.selectedVoice || state.tts.voices.filter(voice => voice.default)[0] - emitter.on(events.SPEAK, speech => { - var utterance = null - var speechId - // if a string is passed, use values from the state - if (typeof speech === 'string') { - utterance = new SpeechSynthesisUtterance(speech) - utterance.voice = state.tts.selectedVoice - utterance.pitch = state.tts.pitch - utterance.rate = state.tts.rate - utterance.volume = state.tts.volume - } else if (typeof speech === 'object') { - var opts = Object.assign(state.tts, speech) - utterance = new SpeechSynthesisUtterance(speech.text) - utterance.voice = state.tts.selectedVoice - utterance.pitch = opts.pitch - utterance.rate = opts.rate - utterance.volume = opts.volume - speechId = opts.id - } - synth.speak(utterance) - utterance.onstart = function (event) { - emitter.emit(events.SPEECH_START, { event, id: speechId }) - } - utterance.onend = function (event) { - emitter.emit(events.SPEECH_END, { event, id: speechId }) - } - utterance.onboundary = function (event) { - emitter.emit(events.SPEECH_BOUNDARY, { event, id: speechId }) + emitter.on(events.SPEAK, speech => { + var utterance = null + var speechId + // if a string is passed, use values from the state + if (typeof speech === 'string') { + utterance = new SpeechSynthesisUtterance(speech) + utterance.voice = state.tts.selectedVoice + utterance.pitch = state.tts.pitch + utterance.rate = state.tts.rate + utterance.volume = state.tts.volume + } else if (typeof speech === 'object') { + var customOpts = Object.assign(state.tts, speech) + utterance = new SpeechSynthesisUtterance(speech.text) + utterance.voice = state.tts.selectedVoice + utterance.pitch = customOpts.pitch + utterance.rate = customOpts.rate + utterance.volume = customOpts.volume + speechId = customOpts.id + } + synth.speak(utterance) + utterance.onstart = function (event) { + emitter.emit(events.SPEECH_START, { event, id: speechId }) + } + utterance.onend = function (event) { + emitter.emit(events.SPEECH_END, { event, id: speechId }) + } + utterance.onboundary = function (event) { + emitter.emit(events.SPEECH_BOUNDARY, { event, id: speechId }) + } + }) + emitter.on(events.CANCEL, synth.cancel) + emitter.on(events.PAUSE, synth.pause) + emitter.on(events.RESUME, synth.resume) + synth.onvoiceschanged = function () { + state.tts.voices = synth.getVoices() + emitter.emit(events.VOICES_CHANGED) } - }) - emitter.on(events.CANCEL, synth.cancel) - emitter.on(events.PAUSE, synth.pause) - emitter.on(events.RESUME, synth.resume) - synth.onvoiceschanged = function () { - state.tts.voices = synth.getVoices() - emitter.emit(events.VOICES_CHANGED) + emitter.on(events.SET_VOICE, voiceName => { + if (!state.tts.voices) throw new Error('tts: Voices array no set yet, can\'t set voice') + var voice = state.tts.voices.filter(v => { + return v.name === voiceName + })[0] + if (!voice) throw new Error('tts: Voice ' + voiceName + ' not found') + state.tts.selectedVoice = voice + }) + } catch (e) { + emitter.emit(events.ERROR, e) } - emitter.on(events.SET_VOICE, voiceName => { - if (!state.tts.voices) throw new Error('tts: Voices array no set yet, can\'t set voice') - var voice = state.tts.voices.filter(v => { - return v.name === voiceName - })[0] - if (!voice) throw new Error('tts: Voice ' + voiceName + ' not found') - state.tts.selectedVoice = voice - }) - } catch (e) { - emitter.emit(events.ERROR, e) } } diff --git a/package.json b/package.json index 901363f..6c48fe7 100644 --- a/package.json +++ b/package.json @@ -21,5 +21,8 @@ "choo": "^6.6.0", "snazzy": "^7.0.0", "standard": "^10.0.3" + }, + "dependencies": { + "speech-synthesis-recorder": "^1.0.0" } }