12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include <math.h>
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include <map>
890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "base/memory/scoped_ptr.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/memory/singleton.h"
11b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/synchronization/lock.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/speech/tts_platform.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/browser_thread.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "library_loaders/libspeechd.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using content::BrowserThread;
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kNotSupportedError[] =
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    "Native speech synthesis not supported on this platform.";
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)struct SPDChromeVoice {
2590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::string name;
2690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::string module;
2790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)};
2890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class TtsPlatformImplLinux : public TtsPlatformImpl {
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual bool PlatformImplAvailable() OVERRIDE;
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual bool Speak(
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      int utterance_id,
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const std::string& utterance,
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const std::string& lang,
38a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      const VoiceData& voice,
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const UtteranceContinuousParameters& params) OVERRIDE;
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual bool StopSpeaking() OVERRIDE;
41868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  virtual void Pause() OVERRIDE;
42868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  virtual void Resume() OVERRIDE;
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual bool IsSpeaking() OVERRIDE;
44a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  virtual void GetVoices(std::vector<VoiceData>* out_voices) OVERRIDE;
45a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void OnSpeechEvent(SPDNotificationType type);
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Get the single instance of this class.
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static TtsPlatformImplLinux* GetInstance();
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TtsPlatformImplLinux();
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual ~TtsPlatformImplLinux();
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
55b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  // Initiate the connection with the speech dispatcher.
56b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  void Initialize();
57b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Resets the connection with speech dispatcher.
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void Reset();
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static void NotificationCallback(size_t msg_id,
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   size_t client_id,
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   SPDNotificationType type);
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static void IndexMarkCallback(size_t msg_id,
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                size_t client_id,
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                SPDNotificationType state,
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                char* index_mark);
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static SPDNotificationType current_notification_;
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  base::Lock initialization_lock_;
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  LibSpeechdLoader libspeechd_loader_;
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SPDConnection* conn_;
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // These apply to the current utterance only.
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string utterance_;
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int utterance_id_;
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Map a string composed of a voicename and module to the voicename. Used to
8190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // uniquely identify a voice across all available modules.
8290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  scoped_ptr<std::map<std::string, SPDChromeVoice> > all_native_voices_;
8390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  friend struct DefaultSingletonTraits<TtsPlatformImplLinux>;
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux);
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)SPDNotificationType TtsPlatformImplLinux::current_notification_ =
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SPD_EVENT_END;
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TtsPlatformImplLinux::TtsPlatformImplLinux()
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : utterance_id_(0) {
95b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  BrowserThread::PostTask(BrowserThread::FILE,
96b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                          FROM_HERE,
97b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                          base::Bind(&TtsPlatformImplLinux::Initialize,
98b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                                     base::Unretained(this)));
99b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
100b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
101b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void TtsPlatformImplLinux::Initialize() {
102b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  base::AutoLock lock(initialization_lock_);
103b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!libspeechd_loader_.Load("libspeechd.so.2"))
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  conn_ = libspeechd_loader_.spd_open(
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "chrome", "extension_api", NULL, SPD_MODE_THREADED);
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!conn_)
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Register callbacks for all events.
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  conn_->callback_begin =
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    conn_->callback_end =
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    conn_->callback_cancel =
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    conn_->callback_pause =
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    conn_->callback_resume =
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    &NotificationCallback;
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  conn_->callback_im = &IndexMarkCallback;
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  libspeechd_loader_.spd_set_notification_on(conn_, SPD_BEGIN);
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  libspeechd_loader_.spd_set_notification_on(conn_, SPD_END);
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  libspeechd_loader_.spd_set_notification_on(conn_, SPD_CANCEL);
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  libspeechd_loader_.spd_set_notification_on(conn_, SPD_PAUSE);
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME);
1272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TtsPlatformImplLinux::~TtsPlatformImplLinux() {
130b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  base::AutoLock lock(initialization_lock_);
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (conn_) {
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    libspeechd_loader_.spd_close(conn_);
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    conn_ = NULL;
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TtsPlatformImplLinux::Reset() {
138b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  base::AutoLock lock(initialization_lock_);
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (conn_)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    libspeechd_loader_.spd_close(conn_);
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  conn_ = libspeechd_loader_.spd_open(
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      "chrome", "extension_api", NULL, SPD_MODE_THREADED);
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TtsPlatformImplLinux::PlatformImplAvailable() {
146b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  if (!initialization_lock_.Try())
147b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return false;
148b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  bool result = libspeechd_loader_.loaded() && (conn_ != NULL);
149b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  initialization_lock_.Release();
150b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return result;
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TtsPlatformImplLinux::Speak(
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int utterance_id,
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& utterance,
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& lang,
157a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    const VoiceData& voice,
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const UtteranceContinuousParameters& params) {
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!PlatformImplAvailable()) {
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    error_ = kNotSupportedError;
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Speech dispatcher's speech params are around 3x at either limit.
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  float rate = params.rate > 3 ? 3 : params.rate;
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  rate = params.rate < 0.334 ? 0.334 : rate;
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  float pitch = params.pitch > 3 ? 3 : params.pitch;
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  pitch = params.pitch < 0.334 ? 0.334 : pitch;
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
17090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::map<std::string, SPDChromeVoice>::iterator it =
17190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      all_native_voices_->find(voice.name);
17290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (it != all_native_voices_->end()) {
17390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    libspeechd_loader_.spd_set_output_module(conn_, it->second.module.c_str());
17490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    libspeechd_loader_.spd_set_synthesis_voice(conn_, it->second.name.c_str());
17590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
17690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Map our multiplicative range to Speech Dispatcher's linear range.
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // .334 = -100.
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // 3 = 100.
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3));
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3));
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  utterance_ = utterance;
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  utterance_id_ = utterance_id;
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, utterance.c_str()) == -1) {
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Reset();
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TtsPlatformImplLinux::StopSpeaking() {
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!PlatformImplAvailable())
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (libspeechd_loader_.spd_stop(conn_) == -1) {
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Reset();
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return true;
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
203868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void TtsPlatformImplLinux::Pause() {
204868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!PlatformImplAvailable())
205868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return;
206868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  libspeechd_loader_.spd_pause(conn_);
207868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
208868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
209868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void TtsPlatformImplLinux::Resume() {
210868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (!PlatformImplAvailable())
211868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return;
212868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  libspeechd_loader_.spd_resume(conn_);
213868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)}
214868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TtsPlatformImplLinux::IsSpeaking() {
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return current_notification_ == SPD_EVENT_BEGIN;
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
219a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)void TtsPlatformImplLinux::GetVoices(
220a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    std::vector<VoiceData>* out_voices) {
22190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (!all_native_voices_.get()) {
22290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    all_native_voices_.reset(new std::map<std::string, SPDChromeVoice>());
22390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    char** modules = libspeechd_loader_.spd_list_modules(conn_);
22490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    if (!modules)
22590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      return;
22690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    for (int i = 0; modules[i]; i++) {
22790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      char* module = modules[i];
22890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      libspeechd_loader_.spd_set_output_module(conn_, module);
22990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      SPDVoice** native_voices =
23090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          libspeechd_loader_.spd_list_synthesis_voices(conn_);
23190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      if (!native_voices) {
23290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        free(module);
23390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        continue;
23490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      }
23590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      for (int j = 0; native_voices[j]; j++) {
23690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        SPDVoice* native_voice = native_voices[j];
23790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        SPDChromeVoice native_data;
23890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        native_data.name = native_voice->name;
23990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        native_data.module = module;
24090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        std::string key;
24190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        key.append(native_data.name);
24290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        key.append(" ");
24390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        key.append(native_data.module);
24490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        all_native_voices_->insert(
24590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)            std::pair<std::string, SPDChromeVoice>(key, native_data));
24690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        free(native_voices[j]);
24790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      }
24890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      free(modules[i]);
24990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    }
25090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
25190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
25290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  for (std::map<std::string, SPDChromeVoice>::iterator it =
25390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)           all_native_voices_->begin();
25490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)       it != all_native_voices_->end();
25590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)       it++) {
25690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    out_voices->push_back(VoiceData());
25790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    VoiceData& voice = out_voices->back();
25890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    voice.native = true;
25990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    voice.name = it->first;
26090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    voice.events.insert(TTS_EVENT_START);
26190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    voice.events.insert(TTS_EVENT_END);
26290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    voice.events.insert(TTS_EVENT_CANCELLED);
26390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    voice.events.insert(TTS_EVENT_MARKER);
264868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    voice.events.insert(TTS_EVENT_PAUSE);
265868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    voice.events.insert(TTS_EVENT_RESUME);
26690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) {
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TtsController* controller = TtsController::GetInstance();
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (type) {
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  case SPD_EVENT_BEGIN:
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    break;
275868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  case SPD_EVENT_RESUME:
276868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, std::string());
277868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    break;
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  case SPD_EVENT_END:
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    controller->OnTtsEvent(
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        utterance_id_, TTS_EVENT_END, utterance_.size(), std::string());
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    break;
282868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  case SPD_EVENT_PAUSE:
283868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    controller->OnTtsEvent(
284868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        utterance_id_, TTS_EVENT_PAUSE, utterance_.size(), std::string());
285868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    break;
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  case SPD_EVENT_CANCEL:
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    controller->OnTtsEvent(
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        utterance_id_, TTS_EVENT_CANCELLED, 0, std::string());
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    break;
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  case SPD_EVENT_INDEX_MARK:
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string());
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    break;
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TtsPlatformImplLinux::NotificationCallback(
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    size_t msg_id, size_t client_id, SPDNotificationType type) {
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We run Speech Dispatcher in threaded mode, so these callbacks should always
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // be in a separate thread.
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    current_notification_ = type;
303b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    BrowserThread::PostTask(
304b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        BrowserThread::UI,
305b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)        FROM_HERE,
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
307b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                   base::Unretained(TtsPlatformImplLinux::GetInstance()),
308b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                   type));
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TtsPlatformImplLinux::IndexMarkCallback(size_t msg_id,
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                      size_t client_id,
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                      SPDNotificationType state,
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                      char* index_mark) {
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(dtseng): index_mark appears to specify an index type supplied by a
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // client. Need to explore how this is used before hooking it up with existing
3192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // word, sentence events.
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We run Speech Dispatcher in threaded mode, so these callbacks should always
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // be in a separate thread.
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    current_notification_ = state;
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        base::Unretained(TtsPlatformImplLinux::GetInstance()),
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        state));
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TtsPlatformImplLinux* TtsPlatformImplLinux::GetInstance() {
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return Singleton<TtsPlatformImplLinux,
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                   LeakySingletonTraits<TtsPlatformImplLinux> >::get();
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return TtsPlatformImplLinux::GetInstance();
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
341