Home | History | Annotate | Download | only in speech
      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 "bindings/v8/ExceptionState.h"
     30 #include "core/dom/ExecutionContext.h"
     31 #include "modules/speech/SpeechSynthesisEvent.h"
     32 #include "platform/speech/PlatformSpeechSynthesisVoice.h"
     33 #include "wtf/CurrentTime.h"
     34 
     35 namespace WebCore {
     36 
     37 PassRefPtr<SpeechSynthesis> SpeechSynthesis::create(ExecutionContext* context)
     38 {
     39     return adoptRef(new SpeechSynthesis(context));
     40 }
     41 
     42 SpeechSynthesis::SpeechSynthesis(ExecutionContext* context)
     43     : ContextLifecycleObserver(context)
     44     , m_platformSpeechSynthesizer(PlatformSpeechSynthesizer::create(this))
     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 ExecutionContext* SpeechSynthesis::executionContext() const
     56 {
     57     return ContextLifecycleObserver::executionContext();
     58 }
     59 
     60 void SpeechSynthesis::voicesDidChange()
     61 {
     62     m_voiceList.clear();
     63     if (!executionContext()->activeDOMObjectsAreStopped())
     64         dispatchEvent(Event::create(EventTypeNames::voiceschanged));
     65 }
     66 
     67 const Vector<RefPtr<SpeechSynthesisVoice> >& SpeechSynthesis::getVoices()
     68 {
     69     if (m_voiceList.size())
     70         return m_voiceList;
     71 
     72     // If the voiceList is empty, that's the cue to get the voices from the platform again.
     73     const Vector<RefPtr<PlatformSpeechSynthesisVoice> >& platformVoices = m_platformSpeechSynthesizer->voiceList();
     74     size_t voiceCount = platformVoices.size();
     75     for (size_t k = 0; k < voiceCount; k++)
     76         m_voiceList.append(SpeechSynthesisVoice::create(platformVoices[k]));
     77 
     78     return m_voiceList;
     79 }
     80 
     81 bool SpeechSynthesis::speaking() const
     82 {
     83     // If we have a current speech utterance, then that means we're assumed to be in a speaking state.
     84     // This state is independent of whether the utterance happens to be paused.
     85     return currentSpeechUtterance();
     86 }
     87 
     88 bool SpeechSynthesis::pending() const
     89 {
     90     // This is true if there are any utterances that have not started.
     91     // That means there will be more than one in the queue.
     92     return m_utteranceQueue.size() > 1;
     93 }
     94 
     95 bool SpeechSynthesis::paused() const
     96 {
     97     return m_isPaused;
     98 }
     99 
    100 void SpeechSynthesis::startSpeakingImmediately()
    101 {
    102     SpeechSynthesisUtterance* utterance = currentSpeechUtterance();
    103     ASSERT(utterance);
    104 
    105     utterance->setStartTime(monotonicallyIncreasingTime());
    106     m_isPaused = false;
    107     m_platformSpeechSynthesizer->speak(utterance->platformUtterance());
    108 }
    109 
    110 void SpeechSynthesis::speak(SpeechSynthesisUtterance* utterance, ExceptionState& exceptionState)
    111 {
    112     if (!utterance) {
    113         exceptionState.throwTypeError("Invalid utterance argument");
    114         return;
    115     }
    116 
    117     m_utteranceQueue.append(utterance);
    118 
    119     // If the queue was empty, speak this immediately and add it to the queue.
    120     if (m_utteranceQueue.size() == 1)
    121         startSpeakingImmediately();
    122 }
    123 
    124 void SpeechSynthesis::cancel()
    125 {
    126     // Remove all the items from the utterance queue.
    127     m_utteranceQueue.clear();
    128     m_platformSpeechSynthesizer->cancel();
    129 }
    130 
    131 void SpeechSynthesis::pause()
    132 {
    133     if (!m_isPaused)
    134         m_platformSpeechSynthesizer->pause();
    135 }
    136 
    137 void SpeechSynthesis::resume()
    138 {
    139     if (!currentSpeechUtterance())
    140         return;
    141     m_platformSpeechSynthesizer->resume();
    142 }
    143 
    144 void SpeechSynthesis::fireEvent(const AtomicString& type, SpeechSynthesisUtterance* utterance, unsigned long charIndex, const String& name)
    145 {
    146     if (!executionContext()->activeDOMObjectsAreStopped())
    147         utterance->dispatchEvent(SpeechSynthesisEvent::create(type, charIndex, (currentTime() - utterance->startTime()), name));
    148 }
    149 
    150 void SpeechSynthesis::handleSpeakingCompleted(SpeechSynthesisUtterance* utterance, bool errorOccurred)
    151 {
    152     ASSERT(utterance);
    153 
    154     bool didJustFinishCurrentUtterance = false;
    155     // If the utterance that completed was the one we're currently speaking,
    156     // remove it from the queue and start speaking the next one.
    157     if (utterance == currentSpeechUtterance()) {
    158         m_utteranceQueue.removeFirst();
    159         didJustFinishCurrentUtterance = true;
    160     }
    161 
    162     // Always fire the event, because the platform may have asynchronously
    163     // sent an event on an utterance before it got the message that we
    164     // canceled it, and we should always report to the user what actually
    165     // happened.
    166 
    167     fireEvent(errorOccurred ? EventTypeNames::error : EventTypeNames::end, utterance, 0, String());
    168 
    169     // Start the next utterance if we just finished one and one was pending.
    170     if (didJustFinishCurrentUtterance && !m_utteranceQueue.isEmpty())
    171         startSpeakingImmediately();
    172 }
    173 
    174 void SpeechSynthesis::boundaryEventOccurred(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance, SpeechBoundary boundary, unsigned charIndex)
    175 {
    176     DEFINE_STATIC_LOCAL(const String, wordBoundaryString, ("word"));
    177     DEFINE_STATIC_LOCAL(const String, sentenceBoundaryString, ("sentence"));
    178 
    179     switch (boundary) {
    180     case SpeechWordBoundary:
    181         fireEvent(EventTypeNames::boundary, static_cast<SpeechSynthesisUtterance*>(utterance->client()), charIndex, wordBoundaryString);
    182         break;
    183     case SpeechSentenceBoundary:
    184         fireEvent(EventTypeNames::boundary, static_cast<SpeechSynthesisUtterance*>(utterance->client()), charIndex, sentenceBoundaryString);
    185         break;
    186     default:
    187         ASSERT_NOT_REACHED();
    188     }
    189 }
    190 
    191 void SpeechSynthesis::didStartSpeaking(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance)
    192 {
    193     if (utterance->client())
    194         fireEvent(EventTypeNames::start, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String());
    195 }
    196 
    197 void SpeechSynthesis::didPauseSpeaking(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance)
    198 {
    199     m_isPaused = true;
    200     if (utterance->client())
    201         fireEvent(EventTypeNames::pause, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String());
    202 }
    203 
    204 void SpeechSynthesis::didResumeSpeaking(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance)
    205 {
    206     m_isPaused = false;
    207     if (utterance->client())
    208         fireEvent(EventTypeNames::resume, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String());
    209 }
    210 
    211 void SpeechSynthesis::didFinishSpeaking(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance)
    212 {
    213     if (utterance->client())
    214         handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance*>(utterance->client()), false);
    215 }
    216 
    217 void SpeechSynthesis::speakingErrorOccurred(PassRefPtr<PlatformSpeechSynthesisUtterance> utterance)
    218 {
    219     if (utterance->client())
    220         handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance*>(utterance->client()), true);
    221 }
    222 
    223 SpeechSynthesisUtterance* SpeechSynthesis::currentSpeechUtterance() const
    224 {
    225     if (!m_utteranceQueue.isEmpty())
    226         return m_utteranceQueue.first().get();
    227     return 0;
    228 }
    229 
    230 const AtomicString& SpeechSynthesis::interfaceName() const
    231 {
    232     return EventTargetNames::SpeechSynthesisUtterance;
    233 }
    234 
    235 } // namespace WebCore
    236