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