1 /* 2 * Copyright (C) 2013 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "modules/speech/SpeechSynthesis.h" 28 29 #include "core/platform/PlatformSpeechSynthesisVoice.h" 30 #include "core/platform/PlatformSpeechSynthesizer.h" 31 #include "modules/speech/SpeechSynthesisEvent.h" 32 #include "modules/speech/SpeechSynthesisUtterance.h" 33 #include "wtf/CurrentTime.h" 34 35 namespace WebCore { 36 37 PassRefPtr<SpeechSynthesis> SpeechSynthesis::create() 38 { 39 return adoptRef(new SpeechSynthesis()); 40 } 41 42 SpeechSynthesis::SpeechSynthesis() 43 : m_platformSpeechSynthesizer(PlatformSpeechSynthesizer::create(this)) 44 , m_currentSpeechUtterance(0) 45 , m_isPaused(false) 46 { 47 ScriptWrappable::init(this); 48 } 49 50 void SpeechSynthesis::setPlatformSynthesizer(PassOwnPtr<PlatformSpeechSynthesizer> synthesizer) 51 { 52 m_platformSpeechSynthesizer = synthesizer; 53 } 54 55 void SpeechSynthesis::voicesDidChange() 56 { 57 m_voiceList.clear(); 58 } 59 60 const Vector<RefPtr<SpeechSynthesisVoice> >& SpeechSynthesis::getVoices() 61 { 62 if (m_voiceList.size()) 63 return m_voiceList; 64 65 // If the voiceList is empty, that's the cue to get the voices from the platform again. 66 const Vector<RefPtr<PlatformSpeechSynthesisVoice> >& platformVoices = m_platformSpeechSynthesizer->voiceList(); 67 size_t voiceCount = platformVoices.size(); 68 for (size_t k = 0; k < voiceCount; k++) 69 m_voiceList.append(SpeechSynthesisVoice::create(platformVoices[k])); 70 71 return m_voiceList; 72 } 73 74 bool SpeechSynthesis::speaking() const 75 { 76 // If we have a current speech utterance, then that means we're assumed to be in a speaking state. 77 // This state is independent of whether the utterance happens to be paused. 78 return m_currentSpeechUtterance; 79 } 80 81 bool SpeechSynthesis::pending() const 82 { 83 // This is true if there are any utterances that have not started. 84 // That means there will be more than one in the queue. 85 return m_utteranceQueue.size() > 1; 86 } 87 88 bool SpeechSynthesis::paused() const 89 { 90 return m_isPaused; 91 } 92 93 void SpeechSynthesis::startSpeakingImmediately(SpeechSynthesisUtterance* utterance) 94 { 95 ASSERT(!m_currentSpeechUtterance); 96 utterance->setStartTime(monotonicallyIncreasingTime()); 97 m_currentSpeechUtterance = utterance; 98 m_isPaused = false; 99 m_platformSpeechSynthesizer->speak(utterance->platformUtterance()); 100 } 101 102 void SpeechSynthesis::speak(SpeechSynthesisUtterance* utterance) 103 { 104 m_utteranceQueue.append(utterance); 105 106 // If the queue was empty, speak this immediately and add it to the queue. 107 if (m_utteranceQueue.size() == 1) 108 startSpeakingImmediately(utterance); 109 } 110 111 void SpeechSynthesis::cancel() 112 { 113 // Remove all the items from the utterance queue. 114 // Hold on to the current utterance so the platform synthesizer can have a chance to clean up. 115 RefPtr<SpeechSynthesisUtterance> current = m_currentSpeechUtterance; 116 m_utteranceQueue.clear(); 117 m_platformSpeechSynthesizer->cancel(); 118 current = 0; 119 120 // The platform should have called back immediately and cleared the current utterance. 121 ASSERT(!m_currentSpeechUtterance); 122 } 123 124 void SpeechSynthesis::pause() 125 { 126 if (!m_isPaused) 127 m_platformSpeechSynthesizer->pause(); 128 } 129 130 void SpeechSynthesis::resume() 131 { 132 if (!m_currentSpeechUtterance) 133 return; 134 m_platformSpeechSynthesizer->resume(); 135 } 136 137 void SpeechSynthesis::fireEvent(const AtomicString& type, SpeechSynthesisUtterance* utterance, unsigned long charIndex, const String& name) 138 { 139 utterance->dispatchEvent(SpeechSynthesisEvent::create(type, charIndex, (currentTime() - utterance->startTime()), name)); 140 } 141 142 void SpeechSynthesis::handleSpeakingCompleted(SpeechSynthesisUtterance* utterance, bool errorOccurred) 143 { 144 ASSERT(utterance); 145 ASSERT(m_currentSpeechUtterance); 146 m_currentSpeechUtterance = 0; 147 148 fireEvent(errorOccurred ? eventNames().errorEvent : eventNames().endEvent, utterance, 0, String()); 149 150 if (m_utteranceQueue.size()) { 151 RefPtr<SpeechSynthesisUtterance> firstUtterance = m_utteranceQueue.first(); 152 ASSERT(firstUtterance == utterance); 153 if (firstUtterance == utterance) 154 m_utteranceQueue.removeFirst(); 155 156 // Start the next job if there is one pending. 157 if (!m_utteranceQueue.isEmpty()) 158 startSpeakingImmediately(m_utteranceQueue.first().get()); 159 } 160 } 161 162 void SpeechSynthesis::boundaryEventOccurred(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance, SpeechBoundary boundary, unsigned charIndex) 163 { 164 DEFINE_STATIC_LOCAL(const String, wordBoundaryString, ("word")); 165 DEFINE_STATIC_LOCAL(const String, sentenceBoundaryString, ("sentence")); 166 167 switch (boundary) { 168 case SpeechWordBoundary: 169 fireEvent(eventNames().boundaryEvent, static_cast<SpeechSynthesisUtterance*>(utterance->client()), charIndex, wordBoundaryString); 170 break; 171 case SpeechSentenceBoundary: 172 fireEvent(eventNames().boundaryEvent, static_cast<SpeechSynthesisUtterance*>(utterance->client()), charIndex, sentenceBoundaryString); 173 break; 174 default: 175 ASSERT_NOT_REACHED(); 176 } 177 } 178 179 void SpeechSynthesis::didStartSpeaking(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance) 180 { 181 if (utterance->client()) 182 fireEvent(eventNames().startEvent, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String()); 183 } 184 185 void SpeechSynthesis::didPauseSpeaking(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance) 186 { 187 m_isPaused = true; 188 if (utterance->client()) 189 fireEvent(eventNames().pauseEvent, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String()); 190 } 191 192 void SpeechSynthesis::didResumeSpeaking(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance) 193 { 194 m_isPaused = false; 195 if (utterance->client()) 196 fireEvent(eventNames().resumeEvent, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String()); 197 } 198 199 void SpeechSynthesis::didFinishSpeaking(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance) 200 { 201 if (utterance->client()) 202 handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance*>(utterance->client()), false); 203 } 204 205 void SpeechSynthesis::speakingErrorOccurred(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance) 206 { 207 if (utterance->client()) 208 handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance*>(utterance->client()), true); 209 } 210 211 } // namespace WebCore 212