-
Notifications
You must be signed in to change notification settings - Fork 7
/
index.js
92 lines (89 loc) · 3.3 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/* global SpeechSynthesisUtterance */
module.exports = tts
var events = tts.events = {
SPEAK: 'tts:speak',
CANCEL: 'tts:cancel',
PAUSE: 'tts:pause',
RESUME: 'tts:resume',
VOICES_CHANGED: 'tts:voices-changed',
SET_VOICE: 'tts:set-voice',
SPEECH_START: 'tts:speech-start',
SPEECH_END: 'tts:speech-end',
SPEECH_BOUNDARY: 'tts:speech-boundary',
ERROR: 'tts:error'
}
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 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 () {
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)
}
}
}