tts_linux.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <math.h>
  5. #include <map>
  6. #include <memory>
  7. #include "base/command_line.h"
  8. #include "base/debug/leak_annotations.h"
  9. #include "base/memory/singleton.h"
  10. #include "base/synchronization/lock.h"
  11. #include "chrome/browser/speech/tts_platform.h"
  12. #include "content/public/browser/browser_thread.h"
  13. #include "content/public/common/content_switches.h"
  14. #include "library_loaders/libspeechd.h"
  15. using content::BrowserThread;
  16. namespace {
  17. const char kNotSupportedError[] =
  18. "Native speech synthesis not supported on this platform.";
  19. struct SPDChromeVoice {
  20. std::string name;
  21. std::string module;
  22. };
  23. } // namespace
  24. class TtsPlatformImplLinux : public TtsPlatformImpl {
  25. public:
  26. bool PlatformImplAvailable() override;
  27. bool Speak(int utterance_id,
  28. const std::string& utterance,
  29. const std::string& lang,
  30. const VoiceData& voice,
  31. const UtteranceContinuousParameters& params) override;
  32. bool StopSpeaking() override;
  33. void Pause() override;
  34. void Resume() override;
  35. bool IsSpeaking() override;
  36. void GetVoices(std::vector<VoiceData>* out_voices) override;
  37. void OnSpeechEvent(SPDNotificationType type);
  38. // Get the single instance of this class.
  39. static TtsPlatformImplLinux* GetInstance();
  40. private:
  41. TtsPlatformImplLinux();
  42. ~TtsPlatformImplLinux() override;
  43. // Initiate the connection with the speech dispatcher.
  44. void Initialize();
  45. // Resets the connection with speech dispatcher.
  46. void Reset();
  47. static void NotificationCallback(size_t msg_id,
  48. size_t client_id,
  49. SPDNotificationType type);
  50. static void IndexMarkCallback(size_t msg_id,
  51. size_t client_id,
  52. SPDNotificationType state,
  53. char* index_mark);
  54. static SPDNotificationType current_notification_;
  55. base::Lock initialization_lock_;
  56. LibSpeechdLoader libspeechd_loader_;
  57. SPDConnection* conn_;
  58. // These apply to the current utterance only.
  59. std::string utterance_;
  60. int utterance_id_;
  61. // Map a string composed of a voicename and module to the voicename. Used to
  62. // uniquely identify a voice across all available modules.
  63. std::unique_ptr<std::map<std::string, SPDChromeVoice>> all_native_voices_;
  64. friend struct base::DefaultSingletonTraits<TtsPlatformImplLinux>;
  65. DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux);
  66. };
  67. // static
  68. SPDNotificationType TtsPlatformImplLinux::current_notification_ = SPD_EVENT_END;
  69. TtsPlatformImplLinux::TtsPlatformImplLinux() : utterance_id_(0) {
  70. const base::CommandLine& command_line =
  71. *base::CommandLine::ForCurrentProcess();
  72. if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher))
  73. return;
  74. BrowserThread::PostTask(
  75. BrowserThread::FILE, FROM_HERE,
  76. base::Bind(&TtsPlatformImplLinux::Initialize, base::Unretained(this)));
  77. }
  78. void TtsPlatformImplLinux::Initialize() {
  79. base::AutoLock lock(initialization_lock_);
  80. if (!libspeechd_loader_.Load("libspeechd.so.2"))
  81. return;
  82. {
  83. // spd_open has memory leaks which are hard to suppress.
  84. // http://crbug.com/317360
  85. ANNOTATE_SCOPED_MEMORY_LEAK;
  86. conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", NULL,
  87. SPD_MODE_THREADED);
  88. }
  89. if (!conn_)
  90. return;
  91. // Register callbacks for all events.
  92. conn_->callback_begin = conn_->callback_end = conn_->callback_cancel =
  93. conn_->callback_pause = conn_->callback_resume = &NotificationCallback;
  94. conn_->callback_im = &IndexMarkCallback;
  95. libspeechd_loader_.spd_set_notification_on(conn_, SPD_BEGIN);
  96. libspeechd_loader_.spd_set_notification_on(conn_, SPD_END);
  97. libspeechd_loader_.spd_set_notification_on(conn_, SPD_CANCEL);
  98. libspeechd_loader_.spd_set_notification_on(conn_, SPD_PAUSE);
  99. libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME);
  100. }
  101. TtsPlatformImplLinux::~TtsPlatformImplLinux() {
  102. base::AutoLock lock(initialization_lock_);
  103. if (conn_) {
  104. libspeechd_loader_.spd_close(conn_);
  105. conn_ = NULL;
  106. }
  107. }
  108. void TtsPlatformImplLinux::Reset() {
  109. base::AutoLock lock(initialization_lock_);
  110. if (conn_)
  111. libspeechd_loader_.spd_close(conn_);
  112. conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", NULL,
  113. SPD_MODE_THREADED);
  114. }
  115. bool TtsPlatformImplLinux::PlatformImplAvailable() {
  116. if (!initialization_lock_.Try())
  117. return false;
  118. bool result = libspeechd_loader_.loaded() && (conn_ != NULL);
  119. initialization_lock_.Release();
  120. return result;
  121. }
  122. bool TtsPlatformImplLinux::Speak(int utterance_id,
  123. const std::string& utterance,
  124. const std::string& lang,
  125. const VoiceData& voice,
  126. const UtteranceContinuousParameters& params) {
  127. if (!PlatformImplAvailable()) {
  128. error_ = kNotSupportedError;
  129. return false;
  130. }
  131. // Speech dispatcher's speech params are around 3x at either limit.
  132. float rate = params.rate > 3 ? 3 : params.rate;
  133. rate = params.rate < 0.334 ? 0.334 : rate;
  134. float pitch = params.pitch > 3 ? 3 : params.pitch;
  135. pitch = params.pitch < 0.334 ? 0.334 : pitch;
  136. std::map<std::string, SPDChromeVoice>::iterator it =
  137. all_native_voices_->find(voice.name);
  138. if (it != all_native_voices_->end()) {
  139. libspeechd_loader_.spd_set_output_module(conn_, it->second.module.c_str());
  140. libspeechd_loader_.spd_set_synthesis_voice(conn_, it->second.name.c_str());
  141. }
  142. // Map our multiplicative range to Speech Dispatcher's linear range.
  143. // .334 = -100.
  144. // 3 = 100.
  145. libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3));
  146. libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3));
  147. // Support languages other than the default
  148. if (!lang.empty())
  149. libspeechd_loader_.spd_set_language(conn_, lang.c_str());
  150. utterance_ = utterance;
  151. utterance_id_ = utterance_id;
  152. if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, utterance.c_str()) == -1) {
  153. Reset();
  154. return false;
  155. }
  156. return true;
  157. }
  158. bool TtsPlatformImplLinux::StopSpeaking() {
  159. if (!PlatformImplAvailable())
  160. return false;
  161. if (libspeechd_loader_.spd_stop(conn_) == -1) {
  162. Reset();
  163. return false;
  164. }
  165. return true;
  166. }
  167. void TtsPlatformImplLinux::Pause() {
  168. if (!PlatformImplAvailable())
  169. return;
  170. libspeechd_loader_.spd_pause(conn_);
  171. }
  172. void TtsPlatformImplLinux::Resume() {
  173. if (!PlatformImplAvailable())
  174. return;
  175. libspeechd_loader_.spd_resume(conn_);
  176. }
  177. bool TtsPlatformImplLinux::IsSpeaking() {
  178. return current_notification_ == SPD_EVENT_BEGIN;
  179. }
  180. void TtsPlatformImplLinux::GetVoices(std::vector<VoiceData>* out_voices) {
  181. if (!all_native_voices_.get()) {
  182. all_native_voices_.reset(new std::map<std::string, SPDChromeVoice>());
  183. char** modules = libspeechd_loader_.spd_list_modules(conn_);
  184. if (!modules)
  185. return;
  186. for (int i = 0; modules[i]; i++) {
  187. char* module = modules[i];
  188. libspeechd_loader_.spd_set_output_module(conn_, module);
  189. SPDVoice** native_voices =
  190. libspeechd_loader_.spd_list_synthesis_voices(conn_);
  191. if (!native_voices) {
  192. free(module);
  193. continue;
  194. }
  195. for (int j = 0; native_voices[j]; j++) {
  196. SPDVoice* native_voice = native_voices[j];
  197. SPDChromeVoice native_data;
  198. native_data.name = native_voice->name;
  199. native_data.module = module;
  200. std::string key;
  201. key.append(native_data.name);
  202. key.append(" ");
  203. key.append(native_data.module);
  204. all_native_voices_->insert(
  205. std::pair<std::string, SPDChromeVoice>(key, native_data));
  206. free(native_voices[j]);
  207. }
  208. free(modules[i]);
  209. }
  210. }
  211. for (std::map<std::string, SPDChromeVoice>::iterator it =
  212. all_native_voices_->begin();
  213. it != all_native_voices_->end(); it++) {
  214. out_voices->push_back(VoiceData());
  215. VoiceData& voice = out_voices->back();
  216. voice.native = true;
  217. voice.name = it->first;
  218. voice.events.insert(TTS_EVENT_START);
  219. voice.events.insert(TTS_EVENT_END);
  220. voice.events.insert(TTS_EVENT_CANCELLED);
  221. voice.events.insert(TTS_EVENT_MARKER);
  222. voice.events.insert(TTS_EVENT_PAUSE);
  223. voice.events.insert(TTS_EVENT_RESUME);
  224. }
  225. }
  226. void TtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) {
  227. TtsController* controller = TtsController::GetInstance();
  228. switch (type) {
  229. case SPD_EVENT_BEGIN:
  230. controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
  231. break;
  232. case SPD_EVENT_RESUME:
  233. controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, std::string());
  234. break;
  235. case SPD_EVENT_END:
  236. controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, utterance_.size(),
  237. std::string());
  238. break;
  239. case SPD_EVENT_PAUSE:
  240. controller->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE, utterance_.size(),
  241. std::string());
  242. break;
  243. case SPD_EVENT_CANCEL:
  244. controller->OnTtsEvent(utterance_id_, TTS_EVENT_CANCELLED, 0,
  245. std::string());
  246. break;
  247. case SPD_EVENT_INDEX_MARK:
  248. controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string());
  249. break;
  250. }
  251. }
  252. // static
  253. void TtsPlatformImplLinux::NotificationCallback(size_t msg_id,
  254. size_t client_id,
  255. SPDNotificationType type) {
  256. // We run Speech Dispatcher in threaded mode, so these callbacks should always
  257. // be in a separate thread.
  258. if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
  259. current_notification_ = type;
  260. BrowserThread::PostTask(
  261. BrowserThread::UI, FROM_HERE,
  262. base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
  263. base::Unretained(TtsPlatformImplLinux::GetInstance()),
  264. type));
  265. }
  266. }
  267. // static
  268. void TtsPlatformImplLinux::IndexMarkCallback(size_t msg_id,
  269. size_t client_id,
  270. SPDNotificationType state,
  271. char* index_mark) {
  272. // TODO(dtseng): index_mark appears to specify an index type supplied by a
  273. // client. Need to explore how this is used before hooking it up with existing
  274. // word, sentence events.
  275. // We run Speech Dispatcher in threaded mode, so these callbacks should always
  276. // be in a separate thread.
  277. if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
  278. current_notification_ = state;
  279. BrowserThread::PostTask(
  280. BrowserThread::UI, FROM_HERE,
  281. base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
  282. base::Unretained(TtsPlatformImplLinux::GetInstance()),
  283. state));
  284. }
  285. }
  286. // static
  287. TtsPlatformImplLinux* TtsPlatformImplLinux::GetInstance() {
  288. return base::Singleton<
  289. TtsPlatformImplLinux,
  290. base::LeakySingletonTraits<TtsPlatformImplLinux>>::get();
  291. }
  292. // static
  293. TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
  294. return TtsPlatformImplLinux::GetInstance();
  295. }