15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <math.h>
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <sapi.h>
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/singleton.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_comptr.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/speech/tts_controller.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/speech/tts_platform.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class TtsPlatformImplWin : public TtsPlatformImpl {
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual bool PlatformImplAvailable() {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return true;
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual bool Speak(
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int utterance_id,
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const std::string& utterance,
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const std::string& lang,
26a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      const VoiceData& voice,
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const UtteranceContinuousParameters& params);
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual bool StopSpeaking();
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
31868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  virtual void Pause();
32868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
33868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  virtual void Resume();
34868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual bool IsSpeaking();
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
37a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  virtual void GetVoices(std::vector<VoiceData>* out_voices) OVERRIDE;
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Get the single instance of this class.
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static TtsPlatformImplWin* GetInstance();
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static void __stdcall SpeechEventCallback(WPARAM w_param, LPARAM l_param);
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TtsPlatformImplWin();
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual ~TtsPlatformImplWin() {}
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void OnSpeechEvent();
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::ScopedComPtr<ISpVoice> speech_synthesizer_;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // These apply to the current utterance only.
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::wstring utterance_;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int utterance_id_;
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int prefix_len_;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ULONG stream_number_;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int char_position_;
58868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  bool paused_;
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  friend struct DefaultSingletonTraits<TtsPlatformImplWin>;
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplWin);
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return TtsPlatformImplWin::GetInstance();
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TtsPlatformImplWin::Speak(
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int utterance_id,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& src_utterance,
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& lang,
74a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    const VoiceData& voice,
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const UtteranceContinuousParameters& params) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::wstring prefix;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::wstring suffix;
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!speech_synthesizer_.get())
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(dmazzoni): support languages other than the default: crbug.com/88059
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (params.rate >= 0.0) {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Map our multiplicative range of 0.1x to 10.0x onto Microsoft's
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // linear range of -10 to 10:
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //   0.1 -> -10
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //   1.0 -> 0
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //  10.0 -> 10
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    speech_synthesizer_->SetRate(static_cast<int32>(10 * log10(params.rate)));
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (params.pitch >= 0.0) {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The TTS api allows a range of -10 to 10 for speech pitch.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(dtseng): cleanup if we ever use any other properties that
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // require xml.
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::wstring pitch_value =
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::IntToString16(static_cast<int>(params.pitch * 10 - 10));
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix = L"<pitch absmiddle=\"" + pitch_value + L"\">";
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    suffix = L"</pitch>";
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (params.volume >= 0.0) {
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The TTS api allows a range of 0 to 100 for speech volume.
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    speech_synthesizer_->SetVolume(static_cast<uint16>(params.volume * 100));
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(dmazzoni): convert SSML to SAPI xml. http://crbug.com/88072
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  utterance_ = base::UTF8ToWide(src_utterance);
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  utterance_id_ = utterance_id;
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char_position_ = 0;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::wstring merged_utterance = prefix + utterance_ + suffix;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  prefix_len_ = prefix.size();
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT result = speech_synthesizer_->Speak(
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      merged_utterance.c_str(),
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SPF_ASYNC,
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      &stream_number_);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return (result == S_OK);
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TtsPlatformImplWin::StopSpeaking() {
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (speech_synthesizer_.get()) {
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Clear the stream number so that any further events relating to this
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // utterance are ignored.
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stream_number_ = 0;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (IsSpeaking()) {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Stop speech by speaking the empty string with the purge flag.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      speech_synthesizer_->Speak(L"", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
133868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    if (paused_) {
134868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      speech_synthesizer_->Resume();
135868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      paused_ = false;
136868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    }
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
141868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void TtsPlatformImplWin::Pause() {
142868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (speech_synthesizer_.get() && utterance_id_ && !paused_) {
143868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    speech_synthesizer_->Pause();
144868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    paused_ = true;
145868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    TtsController::GetInstance()->OnTtsEvent(
146868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        utterance_id_, TTS_EVENT_PAUSE, char_position_, "");
147868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
148868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
149868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
150868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void TtsPlatformImplWin::Resume() {
151868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (speech_synthesizer_.get() && utterance_id_ && paused_) {
152868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    speech_synthesizer_->Resume();
153868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    paused_ = false;
154868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    TtsController::GetInstance()->OnTtsEvent(
155868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        utterance_id_, TTS_EVENT_RESUME, char_position_, "");
156868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
157868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
158868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TtsPlatformImplWin::IsSpeaking() {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (speech_synthesizer_.get()) {
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SPVOICESTATUS status;
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    HRESULT result = speech_synthesizer_->GetStatus(&status, NULL);
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (result == S_OK) {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (status.dwRunningState == 0 ||  // 0 == waiting to speak
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          status.dwRunningState == SPRS_IS_SPEAKING) {
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return true;
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
173a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)void TtsPlatformImplWin::GetVoices(
174a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    std::vector<VoiceData>* out_voices) {
175a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  // TODO: get all voices, not just default voice.
176a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  // http://crbug.com/88059
177a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  out_voices->push_back(VoiceData());
178a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  VoiceData& voice = out_voices->back();
179a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  voice.native = true;
180a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  voice.name = "native";
181a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  voice.events.insert(TTS_EVENT_START);
182a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  voice.events.insert(TTS_EVENT_END);
183a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  voice.events.insert(TTS_EVENT_MARKER);
184a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  voice.events.insert(TTS_EVENT_WORD);
185a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  voice.events.insert(TTS_EVENT_SENTENCE);
186868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  voice.events.insert(TTS_EVENT_PAUSE);
187868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  voice.events.insert(TTS_EVENT_RESUME);
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TtsPlatformImplWin::OnSpeechEvent() {
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TtsController* controller = TtsController::GetInstance();
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SPEVENT event;
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (S_OK == speech_synthesizer_->GetEvents(1, &event, NULL)) {
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (event.ulStreamNum != stream_number_)
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    switch (event.eEventId) {
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SPEI_START_INPUT_STREAM:
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      controller->OnTtsEvent(
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          utterance_id_, TTS_EVENT_START, 0, std::string());
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SPEI_END_INPUT_STREAM:
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      char_position_ = utterance_.size();
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      controller->OnTtsEvent(
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          utterance_id_, TTS_EVENT_END, char_position_, std::string());
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SPEI_TTS_BOOKMARK:
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      controller->OnTtsEvent(
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          utterance_id_, TTS_EVENT_MARKER, char_position_, std::string());
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SPEI_WORD_BOUNDARY:
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      controller->OnTtsEvent(
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          utterance_id_, TTS_EVENT_WORD, char_position_,
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          std::string());
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case SPEI_SENTENCE_BOUNDARY:
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      controller->OnTtsEvent(
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          utterance_id_, TTS_EVENT_SENTENCE, char_position_,
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          std::string());
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TtsPlatformImplWin::TtsPlatformImplWin()
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  : utterance_id_(0),
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefix_len_(0),
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    stream_number_(0),
231868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    char_position_(0),
232868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    paused_(false) {
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  speech_synthesizer_.CreateInstance(CLSID_SpVoice);
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (speech_synthesizer_.get()) {
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ULONGLONG event_mask =
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SPFEI(SPEI_START_INPUT_STREAM) |
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SPFEI(SPEI_TTS_BOOKMARK) |
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SPFEI(SPEI_WORD_BOUNDARY) |
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SPFEI(SPEI_SENTENCE_BOUNDARY) |
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        SPFEI(SPEI_END_INPUT_STREAM);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    speech_synthesizer_->SetInterest(event_mask, event_mask);
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    speech_synthesizer_->SetNotifyCallbackFunction(
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        TtsPlatformImplWin::SpeechEventCallback, 0, 0);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TtsPlatformImplWin* TtsPlatformImplWin::GetInstance() {
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return Singleton<TtsPlatformImplWin,
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   LeakySingletonTraits<TtsPlatformImplWin> >::get();
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TtsPlatformImplWin::SpeechEventCallback(
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    WPARAM w_param, LPARAM l_param) {
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GetInstance()->OnSpeechEvent();
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
258