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 SpeechSynthesis* SpeechSynthesis::create(ExecutionContext* context)
     38 {
     39     return adoptRefCountedGarbageCollectedWillBeNoop(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(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() && !executionContext()->activeDOMObjectsAreStopped())
     64         dispatchEvent(Event::create(EventTypeNames::voiceschanged));
     65 }
     66 
     67 const HeapVector<Member<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 HeapVector<Member<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].get()));
     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.
    120     if (m_utteranceQueue.size() == 1)
    121         startSpeakingImmediately();
    122 }
    123 
    124 void SpeechSynthesis::cancel()
    125 {
    126     // Remove all the items from the utterance queue. The platform
    127     // may still have references to some of these utterances and may
    128     // fire events on them asynchronously.
    129     m_utteranceQueue.clear();
    130     m_platformSpeechSynthesizer->cancel();
    131 }
    132 
    133 void SpeechSynthesis::pause()
    134 {
    135     if (!m_isPaused)
    136         m_platformSpeechSynthesizer->pause();
    137 }
    138 
    139 void SpeechSynthesis::resume()
    140 {
    141     if (!currentSpeechUtterance())
    142         return;
    143     m_platformSpeechSynthesizer->resume();
    144 }
    145 
    146 void SpeechSynthesis::fireEvent(const AtomicString& type, SpeechSynthesisUtterance* utterance, unsigned long charIndex, const String& name)
    147 {
    148     if (executionContext() && !executionContext()->activeDOMObjectsAreStopped())
    149         utterance->dispatchEvent(SpeechSynthesisEvent::create(type, charIndex, (currentTime() - utterance->startTime()), name));
    150 }
    151 
    152 void SpeechSynthesis::handleSpeakingCompleted(SpeechSynthesisUtterance* utterance, bool errorOccurred)
    153 {
    154     ASSERT(utterance);
    155 
    156     bool didJustFinishCurrentUtterance = false;
    157     // If the utterance that completed was the one we're currently speaking,
    158     // remove it from the queue and start speaking the next one.
    159     if (utterance == currentSpeechUtterance()) {
    160         m_utteranceQueue.removeFirst();
    161         didJustFinishCurrentUtterance = true;
    162     }
    163 
    164     // Always fire the event, because the platform may have asynchronously
    165     // sent an event on an utterance before it got the message that we
    166     // canceled it, and we should always report to the user what actually
    167     // happened.
    168     fireEvent(errorOccurred ? EventTypeNames::error : EventTypeNames::end, utterance, 0, String());
    169 
    170     // Start the next utterance if we just finished one and one was pending.
    171     if (didJustFinishCurrentUtterance && !m_utteranceQueue.isEmpty())
    172         startSpeakingImmediately();
    173 }
    174 
    175 void SpeechSynthesis::boundaryEventOccurred(PlatformSpeechSynthesisUtterance* utterance, SpeechBoundary boundary, unsigned charIndex)
    176 {
    177     DEFINE_STATIC_LOCAL(const String, wordBoundaryString, ("word"));
    178     DEFINE_STATIC_LOCAL(const String, sentenceBoundaryString, ("sentence"));
    179 
    180     switch (boundary) {
    181     case SpeechWordBoundary:
    182         fireEvent(EventTypeNames::boundary, static_cast<SpeechSynthesisUtterance*>(utterance->client()), charIndex, wordBoundaryString);
    183         break;
    184     case SpeechSentenceBoundary:
    185         fireEvent(EventTypeNames::boundary, static_cast<SpeechSynthesisUtterance*>(utterance->client()), charIndex, sentenceBoundaryString);
    186         break;
    187     default:
    188         ASSERT_NOT_REACHED();
    189     }
    190 }
    191 
    192 void SpeechSynthesis::didStartSpeaking(PlatformSpeechSynthesisUtterance* utterance)
    193 {
    194     if (utterance->client())
    195         fireEvent(EventTypeNames::start, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String());
    196 }
    197 
    198 void SpeechSynthesis::didPauseSpeaking(PlatformSpeechSynthesisUtterance* utterance)
    199 {
    200     m_isPaused = true;
    201     if (utterance->client())
    202         fireEvent(EventTypeNames::pause, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String());
    203 }
    204 
    205 void SpeechSynthesis::didResumeSpeaking(PlatformSpeechSynthesisUtterance* utterance)
    206 {
    207     m_isPaused = false;
    208     if (utterance->client())
    209         fireEvent(EventTypeNames::resume, static_cast<SpeechSynthesisUtterance*>(utterance->client()), 0, String());
    210 }
    211 
    212 void SpeechSynthesis::didFinishSpeaking(PlatformSpeechSynthesisUtterance* utterance)
    213 {
    214     if (utterance->client())
    215         handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance*>(utterance->client()), false);
    216 }
    217 
    218 void SpeechSynthesis::speakingErrorOccurred(PlatformSpeechSynthesisUtterance* utterance)
    219 {
    220     if (utterance->client())
    221         handleSpeakingCompleted(static_cast<SpeechSynthesisUtterance*>(utterance->client()), true);
    222 }
    223 
    224 SpeechSynthesisUtterance* SpeechSynthesis::currentSpeechUtterance() const
    225 {
    226     if (!m_utteranceQueue.isEmpty())
    227         return m_utteranceQueue.first().get();
    228     return 0;
    229 }
    230 
    231 const AtomicString& SpeechSynthesis::interfaceName() const
    232 {
    233     return EventTargetNames::SpeechSynthesis;
    234 }
    235 
    236 void SpeechSynthesis::trace(Visitor* visitor)
    237 {
    238     visitor->trace(m_platformSpeechSynthesizer);
    239     visitor->trace(m_voiceList);
    240     visitor->trace(m_utteranceQueue);
    241     PlatformSpeechSynthesizerClient::trace(visitor);
    242     EventTargetWithInlineData::trace(visitor);
    243 }
    244 
    245 } // namespace WebCore
    246