refactor: move text-to-speech out of chromium_src (#15024)
* chore: add tts patch and buildflag, makes tts work again * chore: add tts patch and buildflag, makes tts work again * fix: make things compile * build: add relevant tts files for linux * fix: update patch and patch description, should now compile on mac * build: move chrome specific sources under chromium_src:chrome target * build: enable_extensions again We are depending on them, check `//electron/chromium_src:chrome` target for more info. * fix: update tts.patch to receive notifications about browser context destruction * fix: extend browser process from chrome layer The global state g_browser_process is shared between //chrome and //electron. * spec: add basic speech synthesis test * spec: skip speech tests on ci * build: fix compilation on windows
This commit is contained in:
Родитель
5788600c46
Коммит
95696c9456
21
BUILD.gn
21
BUILD.gn
|
@ -8,7 +8,6 @@ import("//build/config/win/manifest.gni")
|
|||
import("//pdf/features.gni")
|
||||
import("//services/service_manager/public/service_manifest.gni")
|
||||
import("//third_party/ffmpeg/ffmpeg_options.gni")
|
||||
import("//third_party/widevine/cdm/widevine.gni")
|
||||
import("//tools/grit/grit_rule.gni")
|
||||
import("//tools/grit/repack.gni")
|
||||
import("//tools/v8_context_snapshot/v8_context_snapshot.gni")
|
||||
|
@ -295,15 +294,7 @@ static_library("electron_lib") {
|
|||
sources = filenames.lib_sources
|
||||
set_sources_assignment_filter(sources_assignment_filter)
|
||||
|
||||
sources += [
|
||||
"$target_gen_dir/atom_natives.h",
|
||||
"//extensions/browser/app_window/size_constraints.cc",
|
||||
"//extensions/browser/app_window/size_constraints.h",
|
||||
"//extensions/common/constants.cc",
|
||||
"//extensions/common/constants.h",
|
||||
"//extensions/common/url_pattern.cc",
|
||||
"//extensions/common/url_pattern.h",
|
||||
]
|
||||
sources += [ "$target_gen_dir/atom_natives.h" ]
|
||||
|
||||
if (is_component_build) {
|
||||
defines += [ "NODE_SHARED_MODE" ]
|
||||
|
@ -440,16 +431,6 @@ static_library("electron_lib") {
|
|||
if (enable_pepper_flash) {
|
||||
deps += [ "components/pepper_flash" ]
|
||||
}
|
||||
|
||||
if (enable_widevine) {
|
||||
sources += [
|
||||
"//chrome/renderer/media/chrome_key_systems.cc",
|
||||
"//chrome/renderer/media/chrome_key_systems.h",
|
||||
"//chrome/renderer/media/chrome_key_systems_provider.cc",
|
||||
"//chrome/renderer/media/chrome_key_systems_provider.h",
|
||||
]
|
||||
deps += [ "//components/cdm/renderer" ]
|
||||
}
|
||||
}
|
||||
|
||||
electron_paks("packed_resources") {
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include "atom/common/native_mate_converters/net_converter.h"
|
||||
#include "atom/common/native_mate_converters/network_converter.h"
|
||||
#include "atom/common/native_mate_converters/value_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "atom/common/options_switches.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/environment.h"
|
||||
|
@ -54,6 +53,12 @@
|
|||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/gfx/image/image.h"
|
||||
|
||||
// clang-format off
|
||||
// This header should be declared at the end to avoid
|
||||
// redefinition errors.
|
||||
#include "atom/common/node_includes.h" // NOLINT(build/include_alpha)
|
||||
// clang-format on
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "atom/browser/ui/win/jump_list.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
|
|
|
@ -40,7 +40,6 @@
|
|||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/browser/printing/printing_message_filter.h"
|
||||
#include "chrome/browser/speech/tts_message_filter.h"
|
||||
#include "components/net_log/chrome_net_log.h"
|
||||
#include "content/public/browser/browser_ppapi_host.h"
|
||||
#include "content/public/browser/client_certificate_delegate.h"
|
||||
|
@ -85,6 +84,10 @@
|
|||
#include "atom/browser/fake_location_provider.h"
|
||||
#endif // BUILDFLAG(OVERRIDE_LOCATION_PROVIDER)
|
||||
|
||||
#if BUILDFLAG(ENABLE_TTS)
|
||||
#include "chrome/browser/speech/tts_message_filter.h"
|
||||
#endif // BUILDFLAG(ENABLE_TTS)
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace atom {
|
||||
|
@ -207,7 +210,10 @@ void AtomBrowserClient::RenderProcessWillLaunch(
|
|||
return;
|
||||
|
||||
host->AddFilter(new printing::PrintingMessageFilter(process_id));
|
||||
host->AddFilter(new TtsMessageFilter(process_id, host->GetBrowserContext()));
|
||||
|
||||
#if BUILDFLAG(ENABLE_TTS)
|
||||
host->AddFilter(new TtsMessageFilter(host->GetBrowserContext()));
|
||||
#endif
|
||||
|
||||
ProcessPreferences prefs;
|
||||
auto* web_preferences =
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "brightray/common/application_info.h"
|
||||
#include "chrome/common/chrome_paths.h"
|
||||
#include "chrome/common/pref_names.h"
|
||||
#include "components/keyed_service/content/browser_context_dependency_manager.h"
|
||||
#include "components/prefs/json_pref_store.h"
|
||||
#include "components/prefs/pref_registry_simple.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
|
@ -119,6 +120,8 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition,
|
|||
proxy_config_monitor_ = std::make_unique<ProxyConfigMonitor>(prefs_.get());
|
||||
io_handle_ = new URLRequestContextGetter::Handle(weak_factory_.GetWeakPtr());
|
||||
cookie_change_notifier_ = std::make_unique<CookieChangeNotifier>(this);
|
||||
|
||||
BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(this);
|
||||
}
|
||||
|
||||
AtomBrowserContext::~AtomBrowserContext() {
|
||||
|
@ -126,6 +129,9 @@ AtomBrowserContext::~AtomBrowserContext() {
|
|||
NotifyWillBeDestroyed(this);
|
||||
ShutdownStoragePartitions();
|
||||
io_handle_->ShutdownOnUIThread();
|
||||
// Notify any keyed services of browser context destruction.
|
||||
BrowserContextDependencyManager::GetInstance()->DestroyBrowserContextServices(
|
||||
this);
|
||||
}
|
||||
|
||||
void AtomBrowserContext::InitPrefs() {
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "atom/common/node_bindings.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/browser_process_impl.h"
|
||||
#include "chrome/browser/icon_manager.h"
|
||||
#include "chrome/browser/net/chrome_net_log_helper.h"
|
||||
#include "components/net_log/chrome_net_log.h"
|
||||
|
@ -68,7 +68,7 @@ AtomBrowserMainParts* AtomBrowserMainParts::self_ = nullptr;
|
|||
|
||||
AtomBrowserMainParts::AtomBrowserMainParts(
|
||||
const content::MainFunctionParams& params)
|
||||
: fake_browser_process_(new BrowserProcess),
|
||||
: fake_browser_process_(new BrowserProcessImpl),
|
||||
browser_(new Browser),
|
||||
node_bindings_(NodeBindings::Create(NodeBindings::BROWSER)),
|
||||
atom_bindings_(new AtomBindings(uv_default_loop())),
|
||||
|
|
|
@ -107,7 +107,7 @@ class AtomBrowserMainParts : public brightray::BrowserMainParts {
|
|||
#endif
|
||||
|
||||
// A fake BrowserProcess object that used to feed the source code from chrome.
|
||||
std::unique_ptr<BrowserProcess> fake_browser_process_;
|
||||
std::unique_ptr<BrowserProcessImpl> fake_browser_process_;
|
||||
|
||||
// Pointer to exit code.
|
||||
int* exit_code_ = nullptr;
|
||||
|
|
|
@ -30,6 +30,10 @@ bool IsViewApiEnabled() {
|
|||
return BUILDFLAG(ENABLE_VIEW_API);
|
||||
}
|
||||
|
||||
bool IsTtsEnabled() {
|
||||
return BUILDFLAG(ENABLE_TTS);
|
||||
}
|
||||
|
||||
void Initialize(v8::Local<v8::Object> exports,
|
||||
v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context,
|
||||
|
@ -41,6 +45,7 @@ void Initialize(v8::Local<v8::Object> exports,
|
|||
dict.SetMethod("isFakeLocationProviderEnabled",
|
||||
&IsFakeLocationProviderEnabled);
|
||||
dict.SetMethod("isViewApiEnabled", &IsViewApiEnabled);
|
||||
dict.SetMethod("isTtsEnabled", &IsTtsEnabled);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -7,4 +7,3 @@
|
|||
#include "atom/common/api/api_messages.h"
|
||||
#include "chrome/common/chrome_utility_printing_messages.h"
|
||||
#include "chrome/common/print_messages.h"
|
||||
#include "chrome/common/tts_messages.h"
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "base/strings/string_split.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "chrome/renderer/printing/print_web_view_helper.h"
|
||||
#include "chrome/renderer/tts_dispatcher.h"
|
||||
#include "content/public/common/content_constants.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
|
@ -51,6 +50,10 @@
|
|||
#include "chrome/renderer/pepper/pepper_helper.h"
|
||||
#endif // BUILDFLAG(ENABLE_PEPPER_FLASH)
|
||||
|
||||
#if BUILDFLAG(ENABLE_TTS)
|
||||
#include "chrome/renderer/tts_dispatcher.h"
|
||||
#endif // BUILDFLAG(ENABLE_TTS)
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
@ -204,7 +207,11 @@ void RendererClientBase::DidClearWindowObject(
|
|||
std::unique_ptr<blink::WebSpeechSynthesizer>
|
||||
RendererClientBase::OverrideSpeechSynthesizer(
|
||||
blink::WebSpeechSynthesizerClient* client) {
|
||||
#if BUILDFLAG(ENABLE_TTS)
|
||||
return std::make_unique<TtsDispatcher>(client);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RendererClientBase::OverrideCreatePlugin(
|
||||
|
|
|
@ -6,7 +6,6 @@ v8_promise_internal_field_count = 1
|
|||
v8_typed_array_max_size_in_heap = 0
|
||||
|
||||
enable_cdm_host_verification = false
|
||||
enable_extensions = false
|
||||
proprietary_codecs = true
|
||||
ffmpeg_branding = "Chrome"
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ buildflag_header("buildflags") {
|
|||
"ENABLE_VIEW_API=$enable_view_api",
|
||||
"ENABLE_PEPPER_FLASH=$enable_pepper_flash",
|
||||
"ENABLE_PDF_VIEWER=$enable_pdf_viewer",
|
||||
"ENABLE_TTS=$enable_tts",
|
||||
"OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ declare_args() {
|
|||
|
||||
enable_pdf_viewer = false
|
||||
|
||||
enable_tts = true
|
||||
|
||||
# Provide a fake location provider for mocking
|
||||
# the geolocation responses. Disable it if you
|
||||
# need to test with chromium's location provider.
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
# found in the LICENSE file.
|
||||
|
||||
import("//electron/buildflags/buildflags.gni")
|
||||
import("//third_party/widevine/cdm/widevine.gni")
|
||||
|
||||
# Builds some of the chrome sources that Electron depends
|
||||
# on unconditionally.
|
||||
source_set("chrome") {
|
||||
# Builds some of the chrome sources that Electron depends on.
|
||||
static_library("chrome") {
|
||||
visibility = [ "//electron:electron_lib" ]
|
||||
sources = [
|
||||
"//chrome/browser/browser_process.cc",
|
||||
"//chrome/browser/browser_process.h",
|
||||
"//chrome/browser/extensions/global_shortcut_listener.cc",
|
||||
"//chrome/browser/extensions/global_shortcut_listener.h",
|
||||
"//chrome/browser/extensions/global_shortcut_listener_mac.h",
|
||||
|
@ -31,18 +33,15 @@ source_set("chrome") {
|
|||
"//chrome/browser/net/proxy_service_factory.h",
|
||||
"//chrome/browser/ssl/security_state_tab_helper.cc",
|
||||
"//chrome/browser/ssl/security_state_tab_helper.h",
|
||||
"//chrome/common/chrome_constants.cc",
|
||||
"//chrome/common/chrome_constants.h",
|
||||
"//chrome/common/chrome_switches.cc",
|
||||
"//chrome/common/chrome_switches.h",
|
||||
"//chrome/common/secure_origin_whitelist.cc",
|
||||
"//chrome/common/secure_origin_whitelist.h",
|
||||
"//extensions/browser/app_window/size_constraints.cc",
|
||||
"//extensions/browser/app_window/size_constraints.h",
|
||||
]
|
||||
public_deps = [
|
||||
"//content/public/browser",
|
||||
]
|
||||
deps = [
|
||||
"//chrome/common:constants",
|
||||
"//chrome/common",
|
||||
"//components/keyed_service/content",
|
||||
"//components/proxy_config",
|
||||
"//components/security_state/content",
|
||||
]
|
||||
|
@ -66,4 +65,38 @@ source_set("chrome") {
|
|||
]
|
||||
deps += [ "//ui/snapshot" ]
|
||||
}
|
||||
|
||||
if (enable_tts) {
|
||||
sources += [
|
||||
"//chrome/browser/speech/tts_controller.h",
|
||||
"//chrome/browser/speech/tts_controller_impl.cc",
|
||||
"//chrome/browser/speech/tts_controller_impl.h",
|
||||
"//chrome/browser/speech/tts_mac.mm",
|
||||
"//chrome/browser/speech/tts_message_filter.cc",
|
||||
"//chrome/browser/speech/tts_message_filter.h",
|
||||
"//chrome/browser/speech/tts_platform.cc",
|
||||
"//chrome/browser/speech/tts_platform.h",
|
||||
"//chrome/browser/speech/tts_win.cc",
|
||||
"//chrome/common/tts_messages.h",
|
||||
"//chrome/common/tts_utterance_request.cc",
|
||||
"//chrome/common/tts_utterance_request.h",
|
||||
"//chrome/renderer/tts_dispatcher.cc",
|
||||
"//chrome/renderer/tts_dispatcher.h",
|
||||
]
|
||||
|
||||
if (is_linux) {
|
||||
sources += [ "//chrome/browser/speech/tts_linux.cc" ]
|
||||
deps += [ "//third_party/speech-dispatcher" ]
|
||||
}
|
||||
}
|
||||
|
||||
if (enable_widevine) {
|
||||
sources += [
|
||||
"//chrome/renderer/media/chrome_key_systems.cc",
|
||||
"//chrome/renderer/media/chrome_key_systems.h",
|
||||
"//chrome/renderer/media/chrome_key_systems_provider.cc",
|
||||
"//chrome/renderer/media/chrome_key_systems_provider.h",
|
||||
]
|
||||
deps += [ "//components/cdm/renderer" ]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/browser_process.h"
|
||||
|
||||
#include "chrome/browser/printing/print_job_manager.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
BrowserProcess* g_browser_process = NULL;
|
||||
|
||||
BrowserProcess::BrowserProcess()
|
||||
: print_job_manager_(new printing::PrintJobManager) {
|
||||
g_browser_process = this;
|
||||
}
|
||||
|
||||
BrowserProcess::~BrowserProcess() {
|
||||
g_browser_process = NULL;
|
||||
}
|
||||
|
||||
void BrowserProcess::SetApplicationLocale(const std::string& locale) {
|
||||
locale_ = locale;
|
||||
}
|
||||
|
||||
std::string BrowserProcess::GetApplicationLocale() {
|
||||
return locale_;
|
||||
}
|
||||
|
||||
printing::PrintJobManager* BrowserProcess::print_job_manager() {
|
||||
return print_job_manager_.get();
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This interface is for managing the global services of the application. Each
|
||||
// service is lazily created when requested the first time. The service getters
|
||||
// will return NULL if the service is not available, so callers must check for
|
||||
// this condition.
|
||||
|
||||
#ifndef CHROME_BROWSER_BROWSER_PROCESS_H_
|
||||
#define CHROME_BROWSER_BROWSER_PROCESS_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace printing {
|
||||
class PrintJobManager;
|
||||
}
|
||||
|
||||
// NOT THREAD SAFE, call only from the main thread.
|
||||
// These functions shouldn't return NULL unless otherwise noted.
|
||||
class BrowserProcess {
|
||||
public:
|
||||
BrowserProcess();
|
||||
~BrowserProcess();
|
||||
|
||||
void SetApplicationLocale(const std::string& locale);
|
||||
std::string GetApplicationLocale();
|
||||
|
||||
printing::PrintJobManager* print_job_manager();
|
||||
|
||||
private:
|
||||
std::unique_ptr<printing::PrintJobManager> print_job_manager_;
|
||||
std::string locale_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BrowserProcess);
|
||||
};
|
||||
|
||||
extern BrowserProcess* g_browser_process;
|
||||
|
||||
#endif // CHROME_BROWSER_BROWSER_PROCESS_H_
|
|
@ -0,0 +1,217 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/browser_process_impl.h"
|
||||
|
||||
#include "chrome/browser/printing/print_job_manager.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
|
||||
BrowserProcessImpl::BrowserProcessImpl()
|
||||
: print_job_manager_(new printing::PrintJobManager) {
|
||||
g_browser_process = this;
|
||||
}
|
||||
|
||||
BrowserProcessImpl::~BrowserProcessImpl() {
|
||||
g_browser_process = nullptr;
|
||||
}
|
||||
|
||||
bool BrowserProcessImpl::IsShuttingDown() {
|
||||
return false;
|
||||
}
|
||||
|
||||
metrics_services_manager::MetricsServicesManager*
|
||||
BrowserProcessImpl::GetMetricsServicesManager() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
metrics::MetricsService* BrowserProcessImpl::metrics_service() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rappor::RapporServiceImpl* BrowserProcessImpl::rappor_service() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ProfileManager* BrowserProcessImpl::profile_manager() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PrefService* BrowserProcessImpl::local_state() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
net::URLRequestContextGetter* BrowserProcessImpl::system_request_context() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
scoped_refptr<network::SharedURLLoaderFactory>
|
||||
BrowserProcessImpl::shared_url_loader_factory() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
variations::VariationsService* BrowserProcessImpl::variations_service() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BrowserProcessPlatformPart* BrowserProcessImpl::platform_part() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
extensions::EventRouterForwarder*
|
||||
BrowserProcessImpl::extension_event_router_forwarder() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NotificationUIManager* BrowserProcessImpl::notification_ui_manager() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NotificationPlatformBridge* BrowserProcessImpl::notification_platform_bridge() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IOThread* BrowserProcessImpl::io_thread() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SystemNetworkContextManager*
|
||||
BrowserProcessImpl::system_network_context_manager() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
network::NetworkQualityTracker* BrowserProcessImpl::network_quality_tracker() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WatchDogThread* BrowserProcessImpl::watchdog_thread() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
policy::ChromeBrowserPolicyConnector*
|
||||
BrowserProcessImpl::browser_policy_connector() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
policy::PolicyService* BrowserProcessImpl::policy_service() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IconManager* BrowserProcessImpl::icon_manager() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GpuModeManager* BrowserProcessImpl::gpu_mode_manager() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
printing::PrintPreviewDialogController*
|
||||
BrowserProcessImpl::print_preview_dialog_controller() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
printing::BackgroundPrintingManager*
|
||||
BrowserProcessImpl::background_printing_manager() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IntranetRedirectDetector* BrowserProcessImpl::intranet_redirect_detector() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DownloadStatusUpdater* BrowserProcessImpl::download_status_updater() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DownloadRequestLimiter* BrowserProcessImpl::download_request_limiter() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BackgroundModeManager* BrowserProcessImpl::background_mode_manager() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StatusTray* BrowserProcessImpl::status_tray() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
safe_browsing::SafeBrowsingService*
|
||||
BrowserProcessImpl::safe_browsing_service() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
safe_browsing::ClientSideDetectionService*
|
||||
BrowserProcessImpl::safe_browsing_detection_service() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
subresource_filter::ContentRulesetService*
|
||||
BrowserProcessImpl::subresource_filter_ruleset_service() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
optimization_guide::OptimizationGuideService*
|
||||
BrowserProcessImpl::optimization_guide_service() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
net_log::ChromeNetLog* BrowserProcessImpl::net_log() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
component_updater::ComponentUpdateService*
|
||||
BrowserProcessImpl::component_updater() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
component_updater::SupervisedUserWhitelistInstaller*
|
||||
BrowserProcessImpl::supervised_user_whitelist_installer() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MediaFileSystemRegistry* BrowserProcessImpl::media_file_system_registry() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
WebRtcLogUploader* BrowserProcessImpl::webrtc_log_uploader() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
network_time::NetworkTimeTracker* BrowserProcessImpl::network_time_tracker() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gcm::GCMDriver* BrowserProcessImpl::gcm_driver() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
resource_coordinator::TabManager* BrowserProcessImpl::GetTabManager() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shell_integration::DefaultWebClientState
|
||||
BrowserProcessImpl::CachedDefaultWebClientState() {
|
||||
return shell_integration::UNKNOWN_DEFAULT;
|
||||
}
|
||||
|
||||
prefs::InProcessPrefServiceFactory* BrowserProcessImpl::pref_service_factory()
|
||||
const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
content::NetworkConnectionTracker*
|
||||
BrowserProcessImpl::network_connection_tracker() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BrowserProcessImpl::SetApplicationLocale(const std::string& locale) {
|
||||
locale_ = locale;
|
||||
}
|
||||
|
||||
const std::string& BrowserProcessImpl::GetApplicationLocale() {
|
||||
return locale_;
|
||||
}
|
||||
|
||||
printing::PrintJobManager* BrowserProcessImpl::print_job_manager() {
|
||||
return print_job_manager_.get();
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// This interface is for managing the global services of the application. Each
|
||||
// service is lazily created when requested the first time. The service getters
|
||||
// will return NULL if the service is not available, so callers must check for
|
||||
// this condition.
|
||||
|
||||
#ifndef CHROME_BROWSER_BROWSER_PROCESS_IMPL_H_
|
||||
#define CHROME_BROWSER_BROWSER_PROCESS_IMPL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "services/network/public/cpp/shared_url_loader_factory.h"
|
||||
|
||||
namespace printing {
|
||||
class PrintJobManager;
|
||||
}
|
||||
|
||||
// Empty definition for std::unique_ptr
|
||||
class BackgroundModeManager {};
|
||||
|
||||
// NOT THREAD SAFE, call only from the main thread.
|
||||
// These functions shouldn't return NULL unless otherwise noted.
|
||||
class BrowserProcessImpl : public BrowserProcess {
|
||||
public:
|
||||
BrowserProcessImpl();
|
||||
~BrowserProcessImpl() override;
|
||||
|
||||
void ResourceDispatcherHostCreated() override {}
|
||||
void EndSession() override {}
|
||||
void FlushLocalStateAndReply(base::OnceClosure reply) override {}
|
||||
bool IsShuttingDown() override;
|
||||
|
||||
metrics_services_manager::MetricsServicesManager* GetMetricsServicesManager()
|
||||
override;
|
||||
metrics::MetricsService* metrics_service() override;
|
||||
rappor::RapporServiceImpl* rappor_service() override;
|
||||
ProfileManager* profile_manager() override;
|
||||
PrefService* local_state() override;
|
||||
net::URLRequestContextGetter* system_request_context() override;
|
||||
scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory()
|
||||
override;
|
||||
variations::VariationsService* variations_service() override;
|
||||
BrowserProcessPlatformPart* platform_part() override;
|
||||
extensions::EventRouterForwarder* extension_event_router_forwarder() override;
|
||||
NotificationUIManager* notification_ui_manager() override;
|
||||
NotificationPlatformBridge* notification_platform_bridge() override;
|
||||
IOThread* io_thread() override;
|
||||
SystemNetworkContextManager* system_network_context_manager() override;
|
||||
network::NetworkQualityTracker* network_quality_tracker() override;
|
||||
WatchDogThread* watchdog_thread() override;
|
||||
policy::ChromeBrowserPolicyConnector* browser_policy_connector() override;
|
||||
policy::PolicyService* policy_service() override;
|
||||
IconManager* icon_manager() override;
|
||||
GpuModeManager* gpu_mode_manager() override;
|
||||
printing::PrintPreviewDialogController* print_preview_dialog_controller()
|
||||
override;
|
||||
printing::BackgroundPrintingManager* background_printing_manager() override;
|
||||
IntranetRedirectDetector* intranet_redirect_detector() override;
|
||||
DownloadStatusUpdater* download_status_updater() override;
|
||||
DownloadRequestLimiter* download_request_limiter() override;
|
||||
BackgroundModeManager* background_mode_manager() override;
|
||||
StatusTray* status_tray() override;
|
||||
safe_browsing::SafeBrowsingService* safe_browsing_service() override;
|
||||
safe_browsing::ClientSideDetectionService* safe_browsing_detection_service()
|
||||
override;
|
||||
subresource_filter::ContentRulesetService*
|
||||
subresource_filter_ruleset_service() override;
|
||||
optimization_guide::OptimizationGuideService* optimization_guide_service()
|
||||
override;
|
||||
net_log::ChromeNetLog* net_log() override;
|
||||
component_updater::ComponentUpdateService* component_updater() override;
|
||||
component_updater::SupervisedUserWhitelistInstaller*
|
||||
supervised_user_whitelist_installer() override;
|
||||
MediaFileSystemRegistry* media_file_system_registry() override;
|
||||
WebRtcLogUploader* webrtc_log_uploader() override;
|
||||
network_time::NetworkTimeTracker* network_time_tracker() override;
|
||||
gcm::GCMDriver* gcm_driver() override;
|
||||
resource_coordinator::TabManager* GetTabManager() override;
|
||||
shell_integration::DefaultWebClientState CachedDefaultWebClientState()
|
||||
override;
|
||||
prefs::InProcessPrefServiceFactory* pref_service_factory() const override;
|
||||
content::NetworkConnectionTracker* network_connection_tracker() override;
|
||||
void CreateDevToolsProtocolHandler() override {}
|
||||
void CreateDevToolsAutoOpener() override {}
|
||||
void set_background_mode_manager_for_test(
|
||||
std::unique_ptr<BackgroundModeManager> manager) override {}
|
||||
#if (defined(OS_WIN) || defined(OS_LINUX))
|
||||
void StartAutoupdateTimer() override {}
|
||||
#endif
|
||||
void SetApplicationLocale(const std::string& locale) override;
|
||||
const std::string& GetApplicationLocale() override;
|
||||
printing::PrintJobManager* print_job_manager() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<printing::PrintJobManager> print_job_manager_;
|
||||
std::string locale_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(BrowserProcessImpl);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_BROWSER_PROCESS_IMPL_H_
|
|
@ -1,336 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_
|
||||
#define CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
class Utterance;
|
||||
class TtsPlatformImpl;
|
||||
|
||||
namespace base {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
}
|
||||
|
||||
// Events sent back from the TTS engine indicating the progress.
|
||||
enum TtsEventType {
|
||||
TTS_EVENT_START,
|
||||
TTS_EVENT_END,
|
||||
TTS_EVENT_WORD,
|
||||
TTS_EVENT_SENTENCE,
|
||||
TTS_EVENT_MARKER,
|
||||
TTS_EVENT_INTERRUPTED,
|
||||
TTS_EVENT_CANCELLED,
|
||||
TTS_EVENT_ERROR,
|
||||
TTS_EVENT_PAUSE,
|
||||
TTS_EVENT_RESUME
|
||||
};
|
||||
|
||||
enum TtsGenderType { TTS_GENDER_NONE, TTS_GENDER_MALE, TTS_GENDER_FEMALE };
|
||||
|
||||
// Returns true if this event type is one that indicates an utterance
|
||||
// is finished and can be destroyed.
|
||||
bool IsFinalTtsEventType(TtsEventType event_type);
|
||||
|
||||
// The continuous parameters that apply to a given utterance.
|
||||
struct UtteranceContinuousParameters {
|
||||
UtteranceContinuousParameters();
|
||||
|
||||
double rate;
|
||||
double pitch;
|
||||
double volume;
|
||||
};
|
||||
|
||||
// Information about one voice.
|
||||
struct VoiceData {
|
||||
VoiceData();
|
||||
VoiceData(const VoiceData&);
|
||||
~VoiceData();
|
||||
|
||||
std::string name;
|
||||
std::string lang;
|
||||
TtsGenderType gender;
|
||||
std::string extension_id;
|
||||
std::set<TtsEventType> events;
|
||||
|
||||
// If true, the synthesis engine is a remote network resource.
|
||||
// It may be higher latency and may incur bandwidth costs.
|
||||
bool remote;
|
||||
|
||||
// If true, this is implemented by this platform's subclass of
|
||||
// TtsPlatformImpl. If false, this is implemented by an extension.
|
||||
bool native;
|
||||
std::string native_voice_identifier;
|
||||
};
|
||||
|
||||
// Interface that delegates TTS requests to user-installed extensions.
|
||||
class TtsEngineDelegate {
|
||||
public:
|
||||
virtual ~TtsEngineDelegate() {}
|
||||
|
||||
// Return a list of all available voices registered.
|
||||
virtual void GetVoices(content::BrowserContext* browser_context,
|
||||
std::vector<VoiceData>* out_voices) = 0;
|
||||
|
||||
// Speak the given utterance by sending an event to the given TTS engine.
|
||||
virtual void Speak(Utterance* utterance, const VoiceData& voice) = 0;
|
||||
|
||||
// Stop speaking the given utterance by sending an event to the target
|
||||
// associated with this utterance.
|
||||
virtual void Stop(Utterance* utterance) = 0;
|
||||
|
||||
// Pause in the middle of speaking this utterance.
|
||||
virtual void Pause(Utterance* utterance) = 0;
|
||||
|
||||
// Resume speaking this utterance.
|
||||
virtual void Resume(Utterance* utterance) = 0;
|
||||
|
||||
// Load the built-in component extension for ChromeOS.
|
||||
virtual bool LoadBuiltInTtsExtension(
|
||||
content::BrowserContext* browser_context) = 0;
|
||||
};
|
||||
|
||||
// Class that wants to receive events on utterances.
|
||||
class UtteranceEventDelegate {
|
||||
public:
|
||||
virtual ~UtteranceEventDelegate() {}
|
||||
virtual void OnTtsEvent(Utterance* utterance,
|
||||
TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message) = 0;
|
||||
};
|
||||
|
||||
// Class that wants to be notified when the set of
|
||||
// voices has changed.
|
||||
class VoicesChangedDelegate {
|
||||
public:
|
||||
virtual ~VoicesChangedDelegate() {}
|
||||
virtual void OnVoicesChanged() = 0;
|
||||
};
|
||||
|
||||
// One speech utterance.
|
||||
class Utterance {
|
||||
public:
|
||||
// Construct an utterance given a profile and a completion task to call
|
||||
// when the utterance is done speaking. Before speaking this utterance,
|
||||
// its other parameters like text, rate, pitch, etc. should all be set.
|
||||
explicit Utterance(content::BrowserContext* browser_context);
|
||||
~Utterance();
|
||||
|
||||
// Sends an event to the delegate. If the event type is TTS_EVENT_END
|
||||
// or TTS_EVENT_ERROR, deletes the utterance. If |char_index| is -1,
|
||||
// uses the last good value.
|
||||
void OnTtsEvent(TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message);
|
||||
|
||||
// Finish an utterance without sending an event to the delegate.
|
||||
void Finish();
|
||||
|
||||
// Getters and setters for the text to speak and other speech options.
|
||||
void set_text(const std::string& text) { text_ = text; }
|
||||
const std::string& text() const { return text_; }
|
||||
|
||||
void set_options(const base::Value* options);
|
||||
const base::Value* options() const { return options_.get(); }
|
||||
|
||||
void set_src_extension_id(const std::string& src_extension_id) {
|
||||
src_extension_id_ = src_extension_id;
|
||||
}
|
||||
const std::string& src_extension_id() { return src_extension_id_; }
|
||||
|
||||
void set_src_id(int src_id) { src_id_ = src_id; }
|
||||
int src_id() { return src_id_; }
|
||||
|
||||
void set_src_url(const GURL& src_url) { src_url_ = src_url; }
|
||||
const GURL& src_url() { return src_url_; }
|
||||
|
||||
void set_voice_name(const std::string& voice_name) {
|
||||
voice_name_ = voice_name;
|
||||
}
|
||||
const std::string& voice_name() const { return voice_name_; }
|
||||
|
||||
void set_lang(const std::string& lang) { lang_ = lang; }
|
||||
const std::string& lang() const { return lang_; }
|
||||
|
||||
void set_gender(TtsGenderType gender) { gender_ = gender; }
|
||||
TtsGenderType gender() const { return gender_; }
|
||||
|
||||
void set_continuous_parameters(const UtteranceContinuousParameters& params) {
|
||||
continuous_parameters_ = params;
|
||||
}
|
||||
const UtteranceContinuousParameters& continuous_parameters() {
|
||||
return continuous_parameters_;
|
||||
}
|
||||
|
||||
void set_can_enqueue(bool can_enqueue) { can_enqueue_ = can_enqueue; }
|
||||
bool can_enqueue() const { return can_enqueue_; }
|
||||
|
||||
void set_required_event_types(const std::set<TtsEventType>& types) {
|
||||
required_event_types_ = types;
|
||||
}
|
||||
const std::set<TtsEventType>& required_event_types() const {
|
||||
return required_event_types_;
|
||||
}
|
||||
|
||||
void set_desired_event_types(const std::set<TtsEventType>& types) {
|
||||
desired_event_types_ = types;
|
||||
}
|
||||
const std::set<TtsEventType>& desired_event_types() const {
|
||||
return desired_event_types_;
|
||||
}
|
||||
|
||||
const std::string& extension_id() const { return extension_id_; }
|
||||
void set_extension_id(const std::string& extension_id) {
|
||||
extension_id_ = extension_id;
|
||||
}
|
||||
|
||||
UtteranceEventDelegate* event_delegate() const {
|
||||
return event_delegate_.get();
|
||||
}
|
||||
void set_event_delegate(
|
||||
base::WeakPtr<UtteranceEventDelegate> event_delegate) {
|
||||
event_delegate_ = event_delegate;
|
||||
}
|
||||
|
||||
// Getters and setters for internal state.
|
||||
content::BrowserContext* browser_context() const { return browser_context_; }
|
||||
int id() const { return id_; }
|
||||
bool finished() const { return finished_; }
|
||||
|
||||
private:
|
||||
// The BrowserContext that initiated this utterance.
|
||||
content::BrowserContext* browser_context_;
|
||||
|
||||
// The extension ID of the extension providing TTS for this utterance, or
|
||||
// empty if native TTS is being used.
|
||||
std::string extension_id_;
|
||||
|
||||
// The unique ID of this utterance, used to associate callback functions
|
||||
// with utterances.
|
||||
int id_;
|
||||
|
||||
// The id of the next utterance, so we can associate requests with
|
||||
// responses.
|
||||
static int next_utterance_id_;
|
||||
|
||||
// The text to speak.
|
||||
std::string text_;
|
||||
|
||||
// The full options arg passed to tts.speak, which may include fields
|
||||
// other than the ones we explicitly parse, below.
|
||||
std::unique_ptr<base::Value> options_;
|
||||
|
||||
// The extension ID of the extension that called speak() and should
|
||||
// receive events.
|
||||
std::string src_extension_id_;
|
||||
|
||||
// The source extension's ID of this utterance, so that it can associate
|
||||
// events with the appropriate callback.
|
||||
int src_id_;
|
||||
|
||||
// The URL of the page where the source extension called speak.
|
||||
GURL src_url_;
|
||||
|
||||
// The delegate to be called when an utterance event is fired.
|
||||
base::WeakPtr<UtteranceEventDelegate> event_delegate_;
|
||||
|
||||
// The parsed options.
|
||||
std::string voice_name_;
|
||||
std::string lang_;
|
||||
TtsGenderType gender_;
|
||||
UtteranceContinuousParameters continuous_parameters_;
|
||||
bool can_enqueue_;
|
||||
std::set<TtsEventType> required_event_types_;
|
||||
std::set<TtsEventType> desired_event_types_;
|
||||
|
||||
// The index of the current char being spoken.
|
||||
int char_index_;
|
||||
|
||||
// True if this utterance received an event indicating it's done.
|
||||
bool finished_;
|
||||
};
|
||||
|
||||
// Singleton class that manages text-to-speech for the TTS and TTS engine
|
||||
// extension APIs, maintaining a queue of pending utterances and keeping
|
||||
// track of all state.
|
||||
class TtsController {
|
||||
public:
|
||||
// Get the single instance of this class.
|
||||
static TtsController* GetInstance();
|
||||
|
||||
// Returns true if we're currently speaking an utterance.
|
||||
virtual bool IsSpeaking() = 0;
|
||||
|
||||
// Speak the given utterance. If the utterance's can_enqueue flag is true
|
||||
// and another utterance is in progress, adds it to the end of the queue.
|
||||
// Otherwise, interrupts any current utterance and speaks this one
|
||||
// immediately.
|
||||
virtual void SpeakOrEnqueue(Utterance* utterance) = 0;
|
||||
|
||||
// Stop all utterances and flush the queue. Implies leaving pause mode
|
||||
// as well.
|
||||
virtual void Stop() = 0;
|
||||
|
||||
// Pause the speech queue. Some engines may support pausing in the middle
|
||||
// of an utterance.
|
||||
virtual void Pause() = 0;
|
||||
|
||||
// Resume speaking.
|
||||
virtual void Resume() = 0;
|
||||
|
||||
// Handle events received from the speech engine. Events are forwarded to
|
||||
// the callback function, and in addition, completion and error events
|
||||
// trigger finishing the current utterance and starting the next one, if
|
||||
// any.
|
||||
virtual void OnTtsEvent(int utterance_id,
|
||||
TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message) = 0;
|
||||
|
||||
// Return a list of all available voices, including the native voice,
|
||||
// if supported, and all voices registered by extensions.
|
||||
virtual void GetVoices(content::BrowserContext* browser_context,
|
||||
std::vector<VoiceData>* out_voices) = 0;
|
||||
|
||||
// Called by the extension system or platform implementation when the
|
||||
// list of voices may have changed and should be re-queried.
|
||||
virtual void VoicesChanged() = 0;
|
||||
|
||||
// Add a delegate that wants to be notified when the set of voices changes.
|
||||
virtual void AddVoicesChangedDelegate(VoicesChangedDelegate* delegate) = 0;
|
||||
|
||||
// Remove delegate that wants to be notified when the set of voices changes.
|
||||
virtual void RemoveVoicesChangedDelegate(VoicesChangedDelegate* delegate) = 0;
|
||||
|
||||
// Set the delegate that processes TTS requests with user-installed
|
||||
// extensions.
|
||||
virtual void SetTtsEngineDelegate(TtsEngineDelegate* delegate) = 0;
|
||||
|
||||
// Get the delegate that processes TTS requests with user-installed
|
||||
// extensions.
|
||||
virtual TtsEngineDelegate* GetTtsEngineDelegate() = 0;
|
||||
|
||||
// For unit testing.
|
||||
virtual void SetPlatformImpl(TtsPlatformImpl* platform_impl) = 0;
|
||||
virtual int QueueSize() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~TtsController() {}
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_H_
|
|
@ -1,447 +0,0 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/speech/tts_controller_impl.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/values.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/speech/tts_platform.h"
|
||||
|
||||
namespace {
|
||||
// A value to be used to indicate that there is no char index available.
|
||||
const int kInvalidCharIndex = -1;
|
||||
|
||||
// Given a language/region code of the form 'fr-FR', returns just the basic
|
||||
// language portion, e.g. 'fr'.
|
||||
std::string TrimLanguageCode(std::string lang) {
|
||||
if (lang.size() >= 5 && lang[2] == '-')
|
||||
return lang.substr(0, 2);
|
||||
else
|
||||
return lang;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsFinalTtsEventType(TtsEventType event_type) {
|
||||
return (event_type == TTS_EVENT_END || event_type == TTS_EVENT_INTERRUPTED ||
|
||||
event_type == TTS_EVENT_CANCELLED || event_type == TTS_EVENT_ERROR);
|
||||
}
|
||||
|
||||
//
|
||||
// UtteranceContinuousParameters
|
||||
//
|
||||
|
||||
UtteranceContinuousParameters::UtteranceContinuousParameters()
|
||||
: rate(-1), pitch(-1), volume(-1) {}
|
||||
|
||||
//
|
||||
// VoiceData
|
||||
//
|
||||
|
||||
VoiceData::VoiceData()
|
||||
: gender(TTS_GENDER_NONE), remote(false), native(false) {}
|
||||
|
||||
VoiceData::VoiceData(const VoiceData&) = default;
|
||||
|
||||
VoiceData::~VoiceData() = default;
|
||||
|
||||
//
|
||||
// Utterance
|
||||
//
|
||||
|
||||
// static
|
||||
int Utterance::next_utterance_id_ = 0;
|
||||
|
||||
Utterance::Utterance(content::BrowserContext* browser_context)
|
||||
: browser_context_(browser_context),
|
||||
id_(next_utterance_id_++),
|
||||
src_id_(-1),
|
||||
gender_(TTS_GENDER_NONE),
|
||||
can_enqueue_(false),
|
||||
char_index_(0),
|
||||
finished_(false) {
|
||||
options_.reset(new base::DictionaryValue());
|
||||
}
|
||||
|
||||
Utterance::~Utterance() {
|
||||
DCHECK(finished_);
|
||||
}
|
||||
|
||||
void Utterance::OnTtsEvent(TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message) {
|
||||
if (char_index >= 0)
|
||||
char_index_ = char_index;
|
||||
if (IsFinalTtsEventType(event_type))
|
||||
finished_ = true;
|
||||
|
||||
if (event_delegate_)
|
||||
event_delegate_->OnTtsEvent(this, event_type, char_index, error_message);
|
||||
if (finished_)
|
||||
event_delegate_.reset();
|
||||
}
|
||||
|
||||
void Utterance::Finish() {
|
||||
finished_ = true;
|
||||
}
|
||||
|
||||
void Utterance::set_options(const base::Value* options) {
|
||||
options_.reset(options->DeepCopy());
|
||||
}
|
||||
|
||||
TtsController* TtsController::GetInstance() {
|
||||
return TtsControllerImpl::GetInstance();
|
||||
}
|
||||
|
||||
//
|
||||
// TtsControllerImpl
|
||||
//
|
||||
|
||||
// static
|
||||
TtsControllerImpl* TtsControllerImpl::GetInstance() {
|
||||
return base::Singleton<TtsControllerImpl>::get();
|
||||
}
|
||||
|
||||
TtsControllerImpl::TtsControllerImpl()
|
||||
: current_utterance_(NULL),
|
||||
paused_(false),
|
||||
platform_impl_(NULL),
|
||||
tts_engine_delegate_(NULL) {}
|
||||
|
||||
TtsControllerImpl::~TtsControllerImpl() {
|
||||
if (current_utterance_) {
|
||||
current_utterance_->Finish();
|
||||
delete current_utterance_;
|
||||
}
|
||||
|
||||
// Clear any queued utterances too.
|
||||
ClearUtteranceQueue(false); // Don't sent events.
|
||||
}
|
||||
|
||||
void TtsControllerImpl::SpeakOrEnqueue(Utterance* utterance) {
|
||||
// If we're paused and we get an utterance that can't be queued,
|
||||
// flush the queue but stay in the paused state.
|
||||
if (paused_ && !utterance->can_enqueue()) {
|
||||
Stop();
|
||||
paused_ = true;
|
||||
delete utterance;
|
||||
return;
|
||||
}
|
||||
|
||||
if (paused_ || (IsSpeaking() && utterance->can_enqueue())) {
|
||||
utterance_queue_.push(utterance);
|
||||
} else {
|
||||
Stop();
|
||||
SpeakNow(utterance);
|
||||
}
|
||||
}
|
||||
|
||||
void TtsControllerImpl::SpeakNow(Utterance* utterance) {
|
||||
// Ensure we have all built-in voices loaded. This is a no-op if already
|
||||
// loaded.
|
||||
bool loaded_built_in =
|
||||
GetPlatformImpl()->LoadBuiltInTtsExtension(utterance->browser_context());
|
||||
|
||||
// Get all available voices and try to find a matching voice.
|
||||
std::vector<VoiceData> voices;
|
||||
GetVoices(utterance->browser_context(), &voices);
|
||||
int index = GetMatchingVoice(utterance, voices);
|
||||
|
||||
VoiceData voice;
|
||||
if (index != -1) {
|
||||
// Select the matching voice.
|
||||
voice = voices[index];
|
||||
} else {
|
||||
// However, if no match was found on a platform without native tts voices,
|
||||
// attempt to get a voice based only on the current locale without respect
|
||||
// to any supplied voice names.
|
||||
std::vector<VoiceData> native_voices;
|
||||
|
||||
if (GetPlatformImpl()->PlatformImplAvailable())
|
||||
GetPlatformImpl()->GetVoices(&native_voices);
|
||||
|
||||
if (native_voices.empty() && !voices.empty()) {
|
||||
// TODO(dtseng): Notify extension caller of an error.
|
||||
utterance->set_voice_name("");
|
||||
// TODO(gaochun): Replace the global variable g_browser_process with
|
||||
// GetContentClient()->browser() to eliminate the dependency of browser
|
||||
// once TTS implementation was moved to content.
|
||||
utterance->set_lang(g_browser_process->GetApplicationLocale());
|
||||
index = GetMatchingVoice(utterance, voices);
|
||||
|
||||
// If even that fails, just take the first available voice.
|
||||
if (index == -1)
|
||||
index = 0;
|
||||
voice = voices[index];
|
||||
} else {
|
||||
// Otherwise, simply give native voices a chance to handle this utterance.
|
||||
voice.native = true;
|
||||
}
|
||||
}
|
||||
|
||||
GetPlatformImpl()->WillSpeakUtteranceWithVoice(utterance, voice);
|
||||
|
||||
if (!voice.native) {
|
||||
#if !defined(OS_ANDROID)
|
||||
DCHECK(!voice.extension_id.empty());
|
||||
current_utterance_ = utterance;
|
||||
utterance->set_extension_id(voice.extension_id);
|
||||
if (tts_engine_delegate_)
|
||||
tts_engine_delegate_->Speak(utterance, voice);
|
||||
bool sends_end_event =
|
||||
voice.events.find(TTS_EVENT_END) != voice.events.end();
|
||||
if (!sends_end_event) {
|
||||
utterance->Finish();
|
||||
delete utterance;
|
||||
current_utterance_ = NULL;
|
||||
SpeakNextUtterance();
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
// It's possible for certain platforms to send start events immediately
|
||||
// during |speak|.
|
||||
current_utterance_ = utterance;
|
||||
GetPlatformImpl()->clear_error();
|
||||
bool success = GetPlatformImpl()->Speak(utterance->id(), utterance->text(),
|
||||
utterance->lang(), voice,
|
||||
utterance->continuous_parameters());
|
||||
if (!success)
|
||||
current_utterance_ = NULL;
|
||||
|
||||
// If the native voice wasn't able to process this speech, see if
|
||||
// the browser has built-in TTS that isn't loaded yet.
|
||||
if (!success && loaded_built_in) {
|
||||
utterance_queue_.push(utterance);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
utterance->OnTtsEvent(TTS_EVENT_ERROR, kInvalidCharIndex,
|
||||
GetPlatformImpl()->error());
|
||||
delete utterance;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TtsControllerImpl::Stop() {
|
||||
paused_ = false;
|
||||
if (current_utterance_ && !current_utterance_->extension_id().empty()) {
|
||||
#if !defined(OS_ANDROID)
|
||||
if (tts_engine_delegate_)
|
||||
tts_engine_delegate_->Stop(current_utterance_);
|
||||
#endif
|
||||
} else {
|
||||
GetPlatformImpl()->clear_error();
|
||||
GetPlatformImpl()->StopSpeaking();
|
||||
}
|
||||
|
||||
if (current_utterance_)
|
||||
current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex,
|
||||
std::string());
|
||||
FinishCurrentUtterance();
|
||||
ClearUtteranceQueue(true); // Send events.
|
||||
}
|
||||
|
||||
void TtsControllerImpl::Pause() {
|
||||
paused_ = true;
|
||||
if (current_utterance_ && !current_utterance_->extension_id().empty()) {
|
||||
#if !defined(OS_ANDROID)
|
||||
if (tts_engine_delegate_)
|
||||
tts_engine_delegate_->Pause(current_utterance_);
|
||||
#endif
|
||||
} else if (current_utterance_) {
|
||||
GetPlatformImpl()->clear_error();
|
||||
GetPlatformImpl()->Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void TtsControllerImpl::Resume() {
|
||||
paused_ = false;
|
||||
if (current_utterance_ && !current_utterance_->extension_id().empty()) {
|
||||
#if !defined(OS_ANDROID)
|
||||
if (tts_engine_delegate_)
|
||||
tts_engine_delegate_->Resume(current_utterance_);
|
||||
#endif
|
||||
} else if (current_utterance_) {
|
||||
GetPlatformImpl()->clear_error();
|
||||
GetPlatformImpl()->Resume();
|
||||
} else {
|
||||
SpeakNextUtterance();
|
||||
}
|
||||
}
|
||||
|
||||
void TtsControllerImpl::OnTtsEvent(int utterance_id,
|
||||
TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message) {
|
||||
// We may sometimes receive completion callbacks "late", after we've
|
||||
// already finished the utterance (for example because another utterance
|
||||
// interrupted or we got a call to Stop). This is normal and we can
|
||||
// safely just ignore these events.
|
||||
if (!current_utterance_ || utterance_id != current_utterance_->id()) {
|
||||
return;
|
||||
}
|
||||
current_utterance_->OnTtsEvent(event_type, char_index, error_message);
|
||||
if (current_utterance_->finished()) {
|
||||
FinishCurrentUtterance();
|
||||
SpeakNextUtterance();
|
||||
}
|
||||
}
|
||||
|
||||
void TtsControllerImpl::GetVoices(content::BrowserContext* browser_context,
|
||||
std::vector<VoiceData>* out_voices) {
|
||||
#if !defined(OS_ANDROID)
|
||||
if (browser_context && tts_engine_delegate_)
|
||||
tts_engine_delegate_->GetVoices(browser_context, out_voices);
|
||||
#endif
|
||||
|
||||
TtsPlatformImpl* platform_impl = GetPlatformImpl();
|
||||
if (platform_impl) {
|
||||
// Ensure we have all built-in voices loaded. This is a no-op if already
|
||||
// loaded.
|
||||
platform_impl->LoadBuiltInTtsExtension(browser_context);
|
||||
if (platform_impl->PlatformImplAvailable())
|
||||
platform_impl->GetVoices(out_voices);
|
||||
}
|
||||
}
|
||||
|
||||
bool TtsControllerImpl::IsSpeaking() {
|
||||
return current_utterance_ != NULL || GetPlatformImpl()->IsSpeaking();
|
||||
}
|
||||
|
||||
void TtsControllerImpl::FinishCurrentUtterance() {
|
||||
if (current_utterance_) {
|
||||
if (!current_utterance_->finished())
|
||||
current_utterance_->OnTtsEvent(TTS_EVENT_INTERRUPTED, kInvalidCharIndex,
|
||||
std::string());
|
||||
delete current_utterance_;
|
||||
current_utterance_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void TtsControllerImpl::SpeakNextUtterance() {
|
||||
if (paused_)
|
||||
return;
|
||||
|
||||
// Start speaking the next utterance in the queue. Keep trying in case
|
||||
// one fails but there are still more in the queue to try.
|
||||
while (!utterance_queue_.empty() && !current_utterance_) {
|
||||
Utterance* utterance = utterance_queue_.front();
|
||||
utterance_queue_.pop();
|
||||
SpeakNow(utterance);
|
||||
}
|
||||
}
|
||||
|
||||
void TtsControllerImpl::ClearUtteranceQueue(bool send_events) {
|
||||
while (!utterance_queue_.empty()) {
|
||||
Utterance* utterance = utterance_queue_.front();
|
||||
utterance_queue_.pop();
|
||||
if (send_events)
|
||||
utterance->OnTtsEvent(TTS_EVENT_CANCELLED, kInvalidCharIndex,
|
||||
std::string());
|
||||
else
|
||||
utterance->Finish();
|
||||
delete utterance;
|
||||
}
|
||||
}
|
||||
|
||||
void TtsControllerImpl::SetPlatformImpl(TtsPlatformImpl* platform_impl) {
|
||||
platform_impl_ = platform_impl;
|
||||
}
|
||||
|
||||
int TtsControllerImpl::QueueSize() {
|
||||
return static_cast<int>(utterance_queue_.size());
|
||||
}
|
||||
|
||||
TtsPlatformImpl* TtsControllerImpl::GetPlatformImpl() {
|
||||
if (!platform_impl_)
|
||||
platform_impl_ = TtsPlatformImpl::GetInstance();
|
||||
return platform_impl_;
|
||||
}
|
||||
|
||||
int TtsControllerImpl::GetMatchingVoice(const Utterance* utterance,
|
||||
std::vector<VoiceData>& voices) {
|
||||
// Make two passes: the first time, do strict language matching
|
||||
// ('fr-FR' does not match 'fr-CA'). The second time, do prefix
|
||||
// language matching ('fr-FR' matches 'fr' and 'fr-CA')
|
||||
for (int pass = 0; pass < 2; ++pass) {
|
||||
for (size_t i = 0; i < voices.size(); ++i) {
|
||||
const VoiceData& voice = voices[i];
|
||||
|
||||
if (!utterance->extension_id().empty() &&
|
||||
utterance->extension_id() != voice.extension_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!voice.name.empty() && !utterance->voice_name().empty() &&
|
||||
voice.name != utterance->voice_name()) {
|
||||
continue;
|
||||
}
|
||||
if (!voice.lang.empty() && !utterance->lang().empty()) {
|
||||
std::string voice_lang = voice.lang;
|
||||
std::string utterance_lang = utterance->lang();
|
||||
if (pass == 1) {
|
||||
voice_lang = TrimLanguageCode(voice_lang);
|
||||
utterance_lang = TrimLanguageCode(utterance_lang);
|
||||
}
|
||||
if (voice_lang != utterance_lang) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (voice.gender != TTS_GENDER_NONE &&
|
||||
utterance->gender() != TTS_GENDER_NONE &&
|
||||
voice.gender != utterance->gender()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (utterance->required_event_types().size() > 0) {
|
||||
bool has_all_required_event_types = true;
|
||||
for (std::set<TtsEventType>::const_iterator iter =
|
||||
utterance->required_event_types().begin();
|
||||
iter != utterance->required_event_types().end(); ++iter) {
|
||||
if (voice.events.find(*iter) == voice.events.end()) {
|
||||
has_all_required_event_types = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!has_all_required_event_types)
|
||||
continue;
|
||||
}
|
||||
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void TtsControllerImpl::VoicesChanged() {
|
||||
for (std::set<VoicesChangedDelegate*>::iterator iter =
|
||||
voices_changed_delegates_.begin();
|
||||
iter != voices_changed_delegates_.end(); ++iter) {
|
||||
(*iter)->OnVoicesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void TtsControllerImpl::AddVoicesChangedDelegate(
|
||||
VoicesChangedDelegate* delegate) {
|
||||
voices_changed_delegates_.insert(delegate);
|
||||
}
|
||||
|
||||
void TtsControllerImpl::RemoveVoicesChangedDelegate(
|
||||
VoicesChangedDelegate* delegate) {
|
||||
voices_changed_delegates_.erase(delegate);
|
||||
}
|
||||
|
||||
void TtsControllerImpl::SetTtsEngineDelegate(TtsEngineDelegate* delegate) {
|
||||
tts_engine_delegate_ = delegate;
|
||||
}
|
||||
|
||||
TtsEngineDelegate* TtsControllerImpl::GetTtsEngineDelegate() {
|
||||
return tts_engine_delegate_;
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_
|
||||
#define CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "chrome/browser/speech/tts_controller.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
}
|
||||
|
||||
// Singleton class that manages text-to-speech for the TTS and TTS engine
|
||||
// extension APIs, maintaining a queue of pending utterances and keeping
|
||||
// track of all state.
|
||||
class TtsControllerImpl : public TtsController {
|
||||
public:
|
||||
// Get the single instance of this class.
|
||||
static TtsControllerImpl* GetInstance();
|
||||
|
||||
// TtsController methods
|
||||
bool IsSpeaking() override;
|
||||
void SpeakOrEnqueue(Utterance* utterance) override;
|
||||
void Stop() override;
|
||||
void Pause() override;
|
||||
void Resume() override;
|
||||
void OnTtsEvent(int utterance_id,
|
||||
TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message) override;
|
||||
void GetVoices(content::BrowserContext* browser_context,
|
||||
std::vector<VoiceData>* out_voices) override;
|
||||
void VoicesChanged() override;
|
||||
void AddVoicesChangedDelegate(VoicesChangedDelegate* delegate) override;
|
||||
void RemoveVoicesChangedDelegate(VoicesChangedDelegate* delegate) override;
|
||||
void SetTtsEngineDelegate(TtsEngineDelegate* delegate) override;
|
||||
TtsEngineDelegate* GetTtsEngineDelegate() override;
|
||||
void SetPlatformImpl(TtsPlatformImpl* platform_impl) override;
|
||||
int QueueSize() override;
|
||||
|
||||
protected:
|
||||
TtsControllerImpl();
|
||||
~TtsControllerImpl() override;
|
||||
|
||||
private:
|
||||
// Get the platform TTS implementation (or injected mock).
|
||||
TtsPlatformImpl* GetPlatformImpl();
|
||||
|
||||
// Start speaking the given utterance. Will either take ownership of
|
||||
// |utterance| or delete it if there's an error. Returns true on success.
|
||||
void SpeakNow(Utterance* utterance);
|
||||
|
||||
// Clear the utterance queue. If send_events is true, will send
|
||||
// TTS_EVENT_CANCELLED events on each one.
|
||||
void ClearUtteranceQueue(bool send_events);
|
||||
|
||||
// Finalize and delete the current utterance.
|
||||
void FinishCurrentUtterance();
|
||||
|
||||
// Start speaking the next utterance in the queue.
|
||||
void SpeakNextUtterance();
|
||||
|
||||
// Given an utterance and a vector of voices, return the
|
||||
// index of the voice that best matches the utterance.
|
||||
int GetMatchingVoice(const Utterance* utterance,
|
||||
std::vector<VoiceData>& voices);
|
||||
|
||||
friend struct base::DefaultSingletonTraits<TtsControllerImpl>;
|
||||
|
||||
// The current utterance being spoken.
|
||||
Utterance* current_utterance_;
|
||||
|
||||
// Whether the queue is paused or not.
|
||||
bool paused_;
|
||||
|
||||
// A queue of utterances to speak after the current one finishes.
|
||||
std::queue<Utterance*> utterance_queue_;
|
||||
|
||||
// A set of delegates that want to be notified when the voices change.
|
||||
std::set<VoicesChangedDelegate*> voices_changed_delegates_;
|
||||
|
||||
// A pointer to the platform implementation of text-to-speech, for
|
||||
// dependency injection.
|
||||
TtsPlatformImpl* platform_impl_;
|
||||
|
||||
// The delegate that processes TTS requests with user-installed extensions.
|
||||
TtsEngineDelegate* tts_engine_delegate_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TtsControllerImpl);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_SPEECH_TTS_CONTROLLER_IMPL_H_
|
|
@ -1,349 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/debug/leak_annotations.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/task_scheduler/post_task.h"
|
||||
#include "chrome/browser/speech/tts_platform.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
|
||||
#include "library_loaders/libspeechd.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace {
|
||||
|
||||
const char kNotSupportedError[] =
|
||||
"Native speech synthesis not supported on this platform.";
|
||||
|
||||
struct SPDChromeVoice {
|
||||
std::string name;
|
||||
std::string module;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class TtsPlatformImplLinux : public TtsPlatformImpl {
|
||||
public:
|
||||
bool PlatformImplAvailable() override;
|
||||
bool Speak(int utterance_id,
|
||||
const std::string& utterance,
|
||||
const std::string& lang,
|
||||
const VoiceData& voice,
|
||||
const UtteranceContinuousParameters& params) override;
|
||||
bool StopSpeaking() override;
|
||||
void Pause() override;
|
||||
void Resume() override;
|
||||
bool IsSpeaking() override;
|
||||
void GetVoices(std::vector<VoiceData>* out_voices) override;
|
||||
|
||||
void OnSpeechEvent(SPDNotificationType type);
|
||||
|
||||
// Get the single instance of this class.
|
||||
static TtsPlatformImplLinux* GetInstance();
|
||||
|
||||
private:
|
||||
TtsPlatformImplLinux();
|
||||
~TtsPlatformImplLinux() override;
|
||||
|
||||
// Initiate the connection with the speech dispatcher.
|
||||
void Initialize();
|
||||
|
||||
// Resets the connection with speech dispatcher.
|
||||
void Reset();
|
||||
|
||||
static void NotificationCallback(size_t msg_id,
|
||||
size_t client_id,
|
||||
SPDNotificationType type);
|
||||
|
||||
static void IndexMarkCallback(size_t msg_id,
|
||||
size_t client_id,
|
||||
SPDNotificationType state,
|
||||
char* index_mark);
|
||||
|
||||
static SPDNotificationType current_notification_;
|
||||
|
||||
base::Lock initialization_lock_;
|
||||
LibSpeechdLoader libspeechd_loader_;
|
||||
SPDConnection* conn_;
|
||||
|
||||
// These apply to the current utterance only.
|
||||
std::string utterance_;
|
||||
int utterance_id_;
|
||||
|
||||
// Map a string composed of a voicename and module to the voicename. Used to
|
||||
// uniquely identify a voice across all available modules.
|
||||
std::unique_ptr<std::map<std::string, SPDChromeVoice>> all_native_voices_;
|
||||
|
||||
friend struct base::DefaultSingletonTraits<TtsPlatformImplLinux>;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplLinux);
|
||||
};
|
||||
|
||||
// static
|
||||
SPDNotificationType TtsPlatformImplLinux::current_notification_ = SPD_EVENT_END;
|
||||
|
||||
TtsPlatformImplLinux::TtsPlatformImplLinux() : utterance_id_(0) {
|
||||
const base::CommandLine& command_line =
|
||||
*base::CommandLine::ForCurrentProcess();
|
||||
if (!command_line.HasSwitch(switches::kEnableSpeechDispatcher))
|
||||
return;
|
||||
|
||||
base::PostTaskWithTraits(
|
||||
FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
|
||||
base::Bind(&TtsPlatformImplLinux::Initialize, base::Unretained(this)));
|
||||
}
|
||||
|
||||
void TtsPlatformImplLinux::Initialize() {
|
||||
base::AutoLock lock(initialization_lock_);
|
||||
|
||||
if (!libspeechd_loader_.Load("libspeechd.so.2"))
|
||||
return;
|
||||
|
||||
{
|
||||
// spd_open has memory leaks which are hard to suppress.
|
||||
// http://crbug.com/317360
|
||||
ANNOTATE_SCOPED_MEMORY_LEAK;
|
||||
conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", NULL,
|
||||
SPD_MODE_THREADED);
|
||||
}
|
||||
if (!conn_)
|
||||
return;
|
||||
|
||||
// Register callbacks for all events.
|
||||
conn_->callback_begin = conn_->callback_end = conn_->callback_cancel =
|
||||
conn_->callback_pause = conn_->callback_resume = &NotificationCallback;
|
||||
|
||||
conn_->callback_im = &IndexMarkCallback;
|
||||
|
||||
libspeechd_loader_.spd_set_notification_on(conn_, SPD_BEGIN);
|
||||
libspeechd_loader_.spd_set_notification_on(conn_, SPD_END);
|
||||
libspeechd_loader_.spd_set_notification_on(conn_, SPD_CANCEL);
|
||||
libspeechd_loader_.spd_set_notification_on(conn_, SPD_PAUSE);
|
||||
libspeechd_loader_.spd_set_notification_on(conn_, SPD_RESUME);
|
||||
}
|
||||
|
||||
TtsPlatformImplLinux::~TtsPlatformImplLinux() {
|
||||
base::AutoLock lock(initialization_lock_);
|
||||
if (conn_) {
|
||||
libspeechd_loader_.spd_close(conn_);
|
||||
conn_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void TtsPlatformImplLinux::Reset() {
|
||||
base::AutoLock lock(initialization_lock_);
|
||||
if (conn_)
|
||||
libspeechd_loader_.spd_close(conn_);
|
||||
conn_ = libspeechd_loader_.spd_open("chrome", "extension_api", NULL,
|
||||
SPD_MODE_THREADED);
|
||||
}
|
||||
|
||||
bool TtsPlatformImplLinux::PlatformImplAvailable() {
|
||||
if (!initialization_lock_.Try())
|
||||
return false;
|
||||
bool result = libspeechd_loader_.loaded() && (conn_ != NULL);
|
||||
initialization_lock_.Release();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TtsPlatformImplLinux::Speak(int utterance_id,
|
||||
const std::string& utterance,
|
||||
const std::string& lang,
|
||||
const VoiceData& voice,
|
||||
const UtteranceContinuousParameters& params) {
|
||||
if (!PlatformImplAvailable()) {
|
||||
error_ = kNotSupportedError;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Speech dispatcher's speech params are around 3x at either limit.
|
||||
float rate = params.rate > 3 ? 3 : params.rate;
|
||||
rate = params.rate < 0.334 ? 0.334 : rate;
|
||||
float pitch = params.pitch > 3 ? 3 : params.pitch;
|
||||
pitch = params.pitch < 0.334 ? 0.334 : pitch;
|
||||
|
||||
std::map<std::string, SPDChromeVoice>::iterator it =
|
||||
all_native_voices_->find(voice.name);
|
||||
if (it != all_native_voices_->end()) {
|
||||
libspeechd_loader_.spd_set_output_module(conn_, it->second.module.c_str());
|
||||
libspeechd_loader_.spd_set_synthesis_voice(conn_, it->second.name.c_str());
|
||||
}
|
||||
|
||||
// Map our multiplicative range to Speech Dispatcher's linear range.
|
||||
// .334 = -100.
|
||||
// 3 = 100.
|
||||
libspeechd_loader_.spd_set_voice_rate(conn_, 100 * log10(rate) / log10(3));
|
||||
libspeechd_loader_.spd_set_voice_pitch(conn_, 100 * log10(pitch) / log10(3));
|
||||
|
||||
// Support languages other than the default
|
||||
if (!lang.empty())
|
||||
libspeechd_loader_.spd_set_language(conn_, lang.c_str());
|
||||
|
||||
utterance_ = utterance;
|
||||
utterance_id_ = utterance_id;
|
||||
|
||||
if (libspeechd_loader_.spd_say(conn_, SPD_TEXT, utterance.c_str()) == -1) {
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TtsPlatformImplLinux::StopSpeaking() {
|
||||
if (!PlatformImplAvailable())
|
||||
return false;
|
||||
if (libspeechd_loader_.spd_stop(conn_) == -1) {
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TtsPlatformImplLinux::Pause() {
|
||||
if (!PlatformImplAvailable())
|
||||
return;
|
||||
libspeechd_loader_.spd_pause(conn_);
|
||||
}
|
||||
|
||||
void TtsPlatformImplLinux::Resume() {
|
||||
if (!PlatformImplAvailable())
|
||||
return;
|
||||
libspeechd_loader_.spd_resume(conn_);
|
||||
}
|
||||
|
||||
bool TtsPlatformImplLinux::IsSpeaking() {
|
||||
return current_notification_ == SPD_EVENT_BEGIN;
|
||||
}
|
||||
|
||||
void TtsPlatformImplLinux::GetVoices(std::vector<VoiceData>* out_voices) {
|
||||
if (!all_native_voices_.get()) {
|
||||
all_native_voices_.reset(new std::map<std::string, SPDChromeVoice>());
|
||||
char** modules = libspeechd_loader_.spd_list_modules(conn_);
|
||||
if (!modules)
|
||||
return;
|
||||
for (int i = 0; modules[i]; i++) {
|
||||
char* module = modules[i];
|
||||
libspeechd_loader_.spd_set_output_module(conn_, module);
|
||||
SPDVoice** native_voices =
|
||||
libspeechd_loader_.spd_list_synthesis_voices(conn_);
|
||||
if (!native_voices) {
|
||||
free(module);
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; native_voices[j]; j++) {
|
||||
SPDVoice* native_voice = native_voices[j];
|
||||
SPDChromeVoice native_data;
|
||||
native_data.name = native_voice->name;
|
||||
native_data.module = module;
|
||||
std::string key;
|
||||
key.append(native_data.name);
|
||||
key.append(" ");
|
||||
key.append(native_data.module);
|
||||
all_native_voices_->insert(
|
||||
std::pair<std::string, SPDChromeVoice>(key, native_data));
|
||||
free(native_voices[j]);
|
||||
}
|
||||
free(modules[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (std::map<std::string, SPDChromeVoice>::iterator it =
|
||||
all_native_voices_->begin();
|
||||
it != all_native_voices_->end(); it++) {
|
||||
out_voices->push_back(VoiceData());
|
||||
VoiceData& voice = out_voices->back();
|
||||
voice.native = true;
|
||||
voice.name = it->first;
|
||||
voice.events.insert(TTS_EVENT_START);
|
||||
voice.events.insert(TTS_EVENT_END);
|
||||
voice.events.insert(TTS_EVENT_CANCELLED);
|
||||
voice.events.insert(TTS_EVENT_MARKER);
|
||||
voice.events.insert(TTS_EVENT_PAUSE);
|
||||
voice.events.insert(TTS_EVENT_RESUME);
|
||||
}
|
||||
}
|
||||
|
||||
void TtsPlatformImplLinux::OnSpeechEvent(SPDNotificationType type) {
|
||||
TtsController* controller = TtsController::GetInstance();
|
||||
switch (type) {
|
||||
case SPD_EVENT_BEGIN:
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, std::string());
|
||||
break;
|
||||
case SPD_EVENT_RESUME:
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME, 0, std::string());
|
||||
break;
|
||||
case SPD_EVENT_END:
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, utterance_.size(),
|
||||
std::string());
|
||||
break;
|
||||
case SPD_EVENT_PAUSE:
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE, utterance_.size(),
|
||||
std::string());
|
||||
break;
|
||||
case SPD_EVENT_CANCEL:
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_CANCELLED, 0,
|
||||
std::string());
|
||||
break;
|
||||
case SPD_EVENT_INDEX_MARK:
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, 0, std::string());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void TtsPlatformImplLinux::NotificationCallback(size_t msg_id,
|
||||
size_t client_id,
|
||||
SPDNotificationType type) {
|
||||
// We run Speech Dispatcher in threaded mode, so these callbacks should always
|
||||
// be in a separate thread.
|
||||
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
||||
current_notification_ = type;
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
|
||||
base::Unretained(TtsPlatformImplLinux::GetInstance()),
|
||||
type));
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void TtsPlatformImplLinux::IndexMarkCallback(size_t msg_id,
|
||||
size_t client_id,
|
||||
SPDNotificationType state,
|
||||
char* index_mark) {
|
||||
// TODO(dtseng): index_mark appears to specify an index type supplied by a
|
||||
// client. Need to explore how this is used before hooking it up with existing
|
||||
// word, sentence events.
|
||||
// We run Speech Dispatcher in threaded mode, so these callbacks should always
|
||||
// be in a separate thread.
|
||||
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
||||
current_notification_ = state;
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&TtsPlatformImplLinux::OnSpeechEvent,
|
||||
base::Unretained(TtsPlatformImplLinux::GetInstance()),
|
||||
state));
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
TtsPlatformImplLinux* TtsPlatformImplLinux::GetInstance() {
|
||||
return base::Singleton<
|
||||
TtsPlatformImplLinux,
|
||||
base::LeakySingletonTraits<TtsPlatformImplLinux>>::get();
|
||||
}
|
||||
|
||||
// static
|
||||
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
|
||||
return TtsPlatformImplLinux::GetInstance();
|
||||
}
|
|
@ -1,344 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/mac/scoped_nsobject.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
#include "chrome/browser/speech/tts_controller.h"
|
||||
#include "chrome/browser/speech/tts_platform.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
class TtsPlatformImplMac;
|
||||
|
||||
@interface ChromeTtsDelegate : NSObject <NSSpeechSynthesizerDelegate> {
|
||||
@private
|
||||
TtsPlatformImplMac* ttsImplMac_; // weak.
|
||||
}
|
||||
|
||||
- (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac;
|
||||
|
||||
@end
|
||||
|
||||
// Subclass of NSSpeechSynthesizer that takes an utterance
|
||||
// string on initialization, retains it and only allows it
|
||||
// to be spoken once.
|
||||
//
|
||||
// We construct a new NSSpeechSynthesizer for each utterance, for
|
||||
// two reasons:
|
||||
// 1. To associate delegate callbacks with a particular utterance,
|
||||
// without assuming anything undocumented about the protocol.
|
||||
// 2. To work around http://openradar.appspot.com/radar?id=2854403,
|
||||
// where Nuance voices don't retain the utterance string and
|
||||
// crash when trying to call willSpeakWord.
|
||||
@interface SingleUseSpeechSynthesizer : NSSpeechSynthesizer {
|
||||
@private
|
||||
base::scoped_nsobject<NSString> utterance_;
|
||||
bool didSpeak_;
|
||||
}
|
||||
|
||||
- (id)initWithUtterance:(NSString*)utterance;
|
||||
- (bool)startSpeakingRetainedUtterance;
|
||||
- (bool)startSpeakingString:(NSString*)utterance;
|
||||
|
||||
@end
|
||||
|
||||
class TtsPlatformImplMac : public TtsPlatformImpl {
|
||||
public:
|
||||
bool PlatformImplAvailable() override { return true; }
|
||||
|
||||
bool Speak(int utterance_id,
|
||||
const std::string& utterance,
|
||||
const std::string& lang,
|
||||
const VoiceData& voice,
|
||||
const UtteranceContinuousParameters& params) override;
|
||||
|
||||
bool StopSpeaking() override;
|
||||
|
||||
void Pause() override;
|
||||
|
||||
void Resume() override;
|
||||
|
||||
bool IsSpeaking() override;
|
||||
|
||||
void GetVoices(std::vector<VoiceData>* out_voices) override;
|
||||
|
||||
// Called by ChromeTtsDelegate when we get a callback from the
|
||||
// native speech engine.
|
||||
void OnSpeechEvent(NSSpeechSynthesizer* sender,
|
||||
TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message);
|
||||
|
||||
// Get the single instance of this class.
|
||||
static TtsPlatformImplMac* GetInstance();
|
||||
|
||||
private:
|
||||
TtsPlatformImplMac();
|
||||
~TtsPlatformImplMac() override;
|
||||
|
||||
base::scoped_nsobject<SingleUseSpeechSynthesizer> speech_synthesizer_;
|
||||
base::scoped_nsobject<ChromeTtsDelegate> delegate_;
|
||||
int utterance_id_;
|
||||
std::string utterance_;
|
||||
int last_char_index_;
|
||||
bool paused_;
|
||||
|
||||
friend struct base::DefaultSingletonTraits<TtsPlatformImplMac>;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplMac);
|
||||
};
|
||||
|
||||
// static
|
||||
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
|
||||
return TtsPlatformImplMac::GetInstance();
|
||||
}
|
||||
|
||||
bool TtsPlatformImplMac::Speak(int utterance_id,
|
||||
const std::string& utterance,
|
||||
const std::string& lang,
|
||||
const VoiceData& voice,
|
||||
const UtteranceContinuousParameters& params) {
|
||||
// TODO: convert SSML to SAPI xml. http://crbug.com/88072
|
||||
utterance_ = utterance;
|
||||
paused_ = false;
|
||||
|
||||
NSString* utterance_nsstring =
|
||||
[NSString stringWithUTF8String:utterance_.c_str()];
|
||||
|
||||
// Deliberately construct a new speech synthesizer every time Speak is
|
||||
// called, otherwise there's no way to know whether calls to the delegate
|
||||
// apply to the current utterance or a previous utterance. In
|
||||
// experimentation, the overhead of constructing and destructing a
|
||||
// NSSpeechSynthesizer is minimal.
|
||||
speech_synthesizer_.reset([[SingleUseSpeechSynthesizer alloc]
|
||||
initWithUtterance:utterance_nsstring]);
|
||||
[speech_synthesizer_ setDelegate:delegate_];
|
||||
|
||||
if (!voice.native_voice_identifier.empty()) {
|
||||
NSString* native_voice_identifier =
|
||||
[NSString stringWithUTF8String:voice.native_voice_identifier.c_str()];
|
||||
[speech_synthesizer_ setVoice:native_voice_identifier];
|
||||
}
|
||||
|
||||
utterance_id_ = utterance_id;
|
||||
|
||||
// TODO: support languages other than the default: crbug.com/88059
|
||||
|
||||
if (params.rate >= 0.0) {
|
||||
// The TTS api defines rate via words per minute. Let 200 be the default.
|
||||
[speech_synthesizer_ setObject:[NSNumber numberWithInt:params.rate * 200]
|
||||
forProperty:NSSpeechRateProperty
|
||||
error:nil];
|
||||
}
|
||||
|
||||
if (params.pitch >= 0.0) {
|
||||
// The input is a float from 0.0 to 2.0, with 1.0 being the default.
|
||||
// Get the default pitch for this voice and modulate it by 50% - 150%.
|
||||
NSError* errorCode;
|
||||
NSNumber* defaultPitchObj =
|
||||
[speech_synthesizer_ objectForProperty:NSSpeechPitchBaseProperty
|
||||
error:&errorCode];
|
||||
int defaultPitch = defaultPitchObj ? [defaultPitchObj intValue] : 48;
|
||||
int newPitch = static_cast<int>(defaultPitch * (0.5 * params.pitch + 0.5));
|
||||
[speech_synthesizer_ setObject:[NSNumber numberWithInt:newPitch]
|
||||
forProperty:NSSpeechPitchBaseProperty
|
||||
error:nil];
|
||||
}
|
||||
|
||||
if (params.volume >= 0.0) {
|
||||
[speech_synthesizer_ setObject:[NSNumber numberWithFloat:params.volume]
|
||||
forProperty:NSSpeechVolumeProperty
|
||||
error:nil];
|
||||
}
|
||||
|
||||
bool success = [speech_synthesizer_ startSpeakingRetainedUtterance];
|
||||
if (success) {
|
||||
TtsController* controller = TtsController::GetInstance();
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0, "");
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool TtsPlatformImplMac::StopSpeaking() {
|
||||
if (speech_synthesizer_.get()) {
|
||||
[speech_synthesizer_ stopSpeaking];
|
||||
speech_synthesizer_.reset(nil);
|
||||
}
|
||||
paused_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TtsPlatformImplMac::Pause() {
|
||||
if (speech_synthesizer_.get() && utterance_id_ && !paused_) {
|
||||
[speech_synthesizer_ pauseSpeakingAtBoundary:NSSpeechImmediateBoundary];
|
||||
paused_ = true;
|
||||
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE,
|
||||
last_char_index_, "");
|
||||
}
|
||||
}
|
||||
|
||||
void TtsPlatformImplMac::Resume() {
|
||||
if (speech_synthesizer_.get() && utterance_id_ && paused_) {
|
||||
[speech_synthesizer_ continueSpeaking];
|
||||
paused_ = false;
|
||||
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME,
|
||||
last_char_index_, "");
|
||||
}
|
||||
}
|
||||
|
||||
bool TtsPlatformImplMac::IsSpeaking() {
|
||||
if (speech_synthesizer_)
|
||||
return [speech_synthesizer_ isSpeaking];
|
||||
return false;
|
||||
}
|
||||
|
||||
void TtsPlatformImplMac::GetVoices(std::vector<VoiceData>* outVoices) {
|
||||
NSArray* voices = [NSSpeechSynthesizer availableVoices];
|
||||
|
||||
// Create a new temporary array of the available voices with
|
||||
// the default voice first.
|
||||
NSMutableArray* orderedVoices =
|
||||
[NSMutableArray arrayWithCapacity:[voices count]];
|
||||
NSString* defaultVoice = [NSSpeechSynthesizer defaultVoice];
|
||||
if (defaultVoice) {
|
||||
[orderedVoices addObject:defaultVoice];
|
||||
}
|
||||
for (NSString* voiceIdentifier in voices) {
|
||||
if (![voiceIdentifier isEqualToString:defaultVoice])
|
||||
[orderedVoices addObject:voiceIdentifier];
|
||||
}
|
||||
|
||||
for (NSString* voiceIdentifier in orderedVoices) {
|
||||
outVoices->push_back(VoiceData());
|
||||
VoiceData& data = outVoices->back();
|
||||
|
||||
NSDictionary* attributes =
|
||||
[NSSpeechSynthesizer attributesForVoice:voiceIdentifier];
|
||||
NSString* name = [attributes objectForKey:NSVoiceName];
|
||||
NSString* gender = [attributes objectForKey:NSVoiceGender];
|
||||
NSString* localeIdentifier =
|
||||
[attributes objectForKey:NSVoiceLocaleIdentifier];
|
||||
|
||||
data.native = true;
|
||||
data.native_voice_identifier = base::SysNSStringToUTF8(voiceIdentifier);
|
||||
data.name = base::SysNSStringToUTF8(name);
|
||||
|
||||
NSDictionary* localeComponents =
|
||||
[NSLocale componentsFromLocaleIdentifier:localeIdentifier];
|
||||
NSString* language = [localeComponents objectForKey:NSLocaleLanguageCode];
|
||||
NSString* country = [localeComponents objectForKey:NSLocaleCountryCode];
|
||||
if (language && country) {
|
||||
data.lang =
|
||||
[[NSString stringWithFormat:@"%@-%@", language, country] UTF8String];
|
||||
} else {
|
||||
data.lang = base::SysNSStringToUTF8(language);
|
||||
}
|
||||
if ([gender isEqualToString:NSVoiceGenderMale])
|
||||
data.gender = TTS_GENDER_MALE;
|
||||
else if ([gender isEqualToString:NSVoiceGenderFemale])
|
||||
data.gender = TTS_GENDER_FEMALE;
|
||||
else
|
||||
data.gender = TTS_GENDER_NONE;
|
||||
data.events.insert(TTS_EVENT_START);
|
||||
data.events.insert(TTS_EVENT_END);
|
||||
data.events.insert(TTS_EVENT_WORD);
|
||||
data.events.insert(TTS_EVENT_ERROR);
|
||||
data.events.insert(TTS_EVENT_CANCELLED);
|
||||
data.events.insert(TTS_EVENT_INTERRUPTED);
|
||||
data.events.insert(TTS_EVENT_PAUSE);
|
||||
data.events.insert(TTS_EVENT_RESUME);
|
||||
}
|
||||
}
|
||||
|
||||
void TtsPlatformImplMac::OnSpeechEvent(NSSpeechSynthesizer* sender,
|
||||
TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message) {
|
||||
// Don't send events from an utterance that's already completed.
|
||||
// This depends on the fact that we construct a new NSSpeechSynthesizer
|
||||
// each time we call Speak.
|
||||
if (sender != speech_synthesizer_.get())
|
||||
return;
|
||||
|
||||
if (event_type == TTS_EVENT_END)
|
||||
char_index = utterance_.size();
|
||||
TtsController* controller = TtsController::GetInstance();
|
||||
controller->OnTtsEvent(utterance_id_, event_type, char_index, error_message);
|
||||
last_char_index_ = char_index;
|
||||
}
|
||||
|
||||
TtsPlatformImplMac::TtsPlatformImplMac() {
|
||||
utterance_id_ = -1;
|
||||
paused_ = false;
|
||||
|
||||
delegate_.reset([[ChromeTtsDelegate alloc] initWithPlatformImplMac:this]);
|
||||
}
|
||||
|
||||
TtsPlatformImplMac::~TtsPlatformImplMac() {}
|
||||
|
||||
// static
|
||||
TtsPlatformImplMac* TtsPlatformImplMac::GetInstance() {
|
||||
return base::Singleton<TtsPlatformImplMac>::get();
|
||||
}
|
||||
|
||||
@implementation ChromeTtsDelegate
|
||||
|
||||
- (id)initWithPlatformImplMac:(TtsPlatformImplMac*)ttsImplMac {
|
||||
if ((self = [super init])) {
|
||||
ttsImplMac_ = ttsImplMac;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender
|
||||
didFinishSpeaking:(BOOL)finished_speaking {
|
||||
ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_END, 0, "");
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender
|
||||
willSpeakWord:(NSRange)character_range
|
||||
ofString:(NSString*)string {
|
||||
ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_WORD, character_range.location,
|
||||
"");
|
||||
}
|
||||
|
||||
- (void)speechSynthesizer:(NSSpeechSynthesizer*)sender
|
||||
didEncounterErrorAtIndex:(NSUInteger)character_index
|
||||
ofString:(NSString*)string
|
||||
message:(NSString*)message {
|
||||
std::string message_utf8 = base::SysNSStringToUTF8(message);
|
||||
ttsImplMac_->OnSpeechEvent(sender, TTS_EVENT_ERROR, character_index,
|
||||
message_utf8);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation SingleUseSpeechSynthesizer
|
||||
|
||||
- (id)initWithUtterance:(NSString*)utterance {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
utterance_.reset([utterance retain]);
|
||||
didSpeak_ = false;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (bool)startSpeakingRetainedUtterance {
|
||||
CHECK(!didSpeak_);
|
||||
CHECK(utterance_);
|
||||
didSpeak_ = true;
|
||||
return [super startSpeakingString:utterance_];
|
||||
}
|
||||
|
||||
- (bool)startSpeakingString:(NSString*)utterance {
|
||||
CHECK(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,176 +0,0 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/speech/tts_message_filter.h"
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/logging.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
TtsMessageFilter::TtsMessageFilter(int render_process_id,
|
||||
content::BrowserContext* browser_context)
|
||||
: BrowserMessageFilter(TtsMsgStart),
|
||||
render_process_id_(render_process_id),
|
||||
browser_context_(browser_context),
|
||||
weak_ptr_factory_(this) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
TtsController::GetInstance()->AddVoicesChangedDelegate(this);
|
||||
|
||||
// Balanced in OnChannelClosingInUIThread() to keep the ref-count be non-zero
|
||||
// until all WeakPtr's are invalidated.
|
||||
AddRef();
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OverrideThreadForMessage(const IPC::Message& message,
|
||||
BrowserThread::ID* thread) {
|
||||
switch (message.type()) {
|
||||
case TtsHostMsg_InitializeVoiceList::ID:
|
||||
case TtsHostMsg_Speak::ID:
|
||||
case TtsHostMsg_Pause::ID:
|
||||
case TtsHostMsg_Resume::ID:
|
||||
case TtsHostMsg_Cancel::ID:
|
||||
*thread = BrowserThread::UI;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool TtsMessageFilter::OnMessageReceived(const IPC::Message& message) {
|
||||
bool handled = true;
|
||||
IPC_BEGIN_MESSAGE_MAP(TtsMessageFilter, message)
|
||||
IPC_MESSAGE_HANDLER(TtsHostMsg_InitializeVoiceList, OnInitializeVoiceList)
|
||||
IPC_MESSAGE_HANDLER(TtsHostMsg_Speak, OnSpeak)
|
||||
IPC_MESSAGE_HANDLER(TtsHostMsg_Pause, OnPause)
|
||||
IPC_MESSAGE_HANDLER(TtsHostMsg_Resume, OnResume)
|
||||
IPC_MESSAGE_HANDLER(TtsHostMsg_Cancel, OnCancel)
|
||||
IPC_MESSAGE_UNHANDLED(handled = false)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
return handled;
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OnChannelClosing() {
|
||||
BrowserThread::PostTask(
|
||||
BrowserThread::UI, FROM_HERE,
|
||||
base::Bind(&TtsMessageFilter::OnChannelClosingInUIThread, this));
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OnDestruct() const {
|
||||
BrowserThread::DeleteOnUIThread::Destruct(this);
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OnInitializeVoiceList() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
TtsController* tts_controller = TtsController::GetInstance();
|
||||
std::vector<VoiceData> voices;
|
||||
tts_controller->GetVoices(browser_context_, &voices);
|
||||
|
||||
std::vector<TtsVoice> out_voices;
|
||||
out_voices.resize(voices.size());
|
||||
for (size_t i = 0; i < voices.size(); ++i) {
|
||||
TtsVoice& out_voice = out_voices[i];
|
||||
out_voice.voice_uri = voices[i].name;
|
||||
out_voice.name = voices[i].name;
|
||||
out_voice.lang = voices[i].lang;
|
||||
out_voice.local_service = !voices[i].remote;
|
||||
out_voice.is_default = (i == 0);
|
||||
}
|
||||
Send(new TtsMsg_SetVoiceList(out_voices));
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OnSpeak(const TtsUtteranceRequest& request) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
|
||||
std::unique_ptr<Utterance> utterance(new Utterance(browser_context_));
|
||||
utterance->set_src_id(request.id);
|
||||
utterance->set_text(request.text);
|
||||
utterance->set_lang(request.lang);
|
||||
utterance->set_voice_name(request.voice);
|
||||
utterance->set_can_enqueue(true);
|
||||
|
||||
UtteranceContinuousParameters params;
|
||||
params.rate = request.rate;
|
||||
params.pitch = request.pitch;
|
||||
params.volume = request.volume;
|
||||
utterance->set_continuous_parameters(params);
|
||||
|
||||
utterance->set_event_delegate(weak_ptr_factory_.GetWeakPtr());
|
||||
|
||||
TtsController::GetInstance()->SpeakOrEnqueue(utterance.release());
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OnPause() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
TtsController::GetInstance()->Pause();
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OnResume() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
TtsController::GetInstance()->Resume();
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OnCancel() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
TtsController::GetInstance()->Stop();
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OnTtsEvent(Utterance* utterance,
|
||||
TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message) {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
switch (event_type) {
|
||||
case TTS_EVENT_START:
|
||||
Send(new TtsMsg_DidStartSpeaking(utterance->src_id()));
|
||||
break;
|
||||
case TTS_EVENT_END:
|
||||
Send(new TtsMsg_DidFinishSpeaking(utterance->src_id()));
|
||||
break;
|
||||
case TTS_EVENT_WORD:
|
||||
Send(new TtsMsg_WordBoundary(utterance->src_id(), char_index));
|
||||
break;
|
||||
case TTS_EVENT_SENTENCE:
|
||||
Send(new TtsMsg_SentenceBoundary(utterance->src_id(), char_index));
|
||||
break;
|
||||
case TTS_EVENT_MARKER:
|
||||
Send(new TtsMsg_MarkerEvent(utterance->src_id(), char_index));
|
||||
break;
|
||||
case TTS_EVENT_INTERRUPTED:
|
||||
Send(new TtsMsg_WasInterrupted(utterance->src_id()));
|
||||
break;
|
||||
case TTS_EVENT_CANCELLED:
|
||||
Send(new TtsMsg_WasCancelled(utterance->src_id()));
|
||||
break;
|
||||
case TTS_EVENT_ERROR:
|
||||
Send(
|
||||
new TtsMsg_SpeakingErrorOccurred(utterance->src_id(), error_message));
|
||||
break;
|
||||
case TTS_EVENT_PAUSE:
|
||||
Send(new TtsMsg_DidPauseSpeaking(utterance->src_id()));
|
||||
break;
|
||||
case TTS_EVENT_RESUME:
|
||||
Send(new TtsMsg_DidResumeSpeaking(utterance->src_id()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OnVoicesChanged() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
OnInitializeVoiceList();
|
||||
}
|
||||
|
||||
void TtsMessageFilter::OnChannelClosingInUIThread() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
TtsController::GetInstance()->RemoveVoicesChangedDelegate(this);
|
||||
|
||||
weak_ptr_factory_.InvalidateWeakPtrs();
|
||||
Release(); // Balanced in TtsMessageFilter().
|
||||
}
|
||||
|
||||
TtsMessageFilter::~TtsMessageFilter() {
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
DCHECK(!weak_ptr_factory_.HasWeakPtrs());
|
||||
TtsController::GetInstance()->RemoveVoicesChangedDelegate(this);
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_
|
||||
#define CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "chrome/browser/speech/tts_controller.h"
|
||||
#include "chrome/common/tts_messages.h"
|
||||
#include "content/public/browser/browser_message_filter.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
}
|
||||
|
||||
class TtsMessageFilter : public content::BrowserMessageFilter,
|
||||
public UtteranceEventDelegate,
|
||||
public VoicesChangedDelegate {
|
||||
public:
|
||||
explicit TtsMessageFilter(int render_process_id,
|
||||
content::BrowserContext* browser_context);
|
||||
|
||||
// content::BrowserMessageFilter implementation.
|
||||
void OverrideThreadForMessage(const IPC::Message& message,
|
||||
content::BrowserThread::ID* thread) override;
|
||||
bool OnMessageReceived(const IPC::Message& message) override;
|
||||
void OnChannelClosing() override;
|
||||
void OnDestruct() const override;
|
||||
|
||||
// UtteranceEventDelegate implementation.
|
||||
void OnTtsEvent(Utterance* utterance,
|
||||
TtsEventType event_type,
|
||||
int char_index,
|
||||
const std::string& error_message) override;
|
||||
|
||||
// VoicesChangedDelegate implementation.
|
||||
void OnVoicesChanged() override;
|
||||
|
||||
private:
|
||||
friend class content::BrowserThread;
|
||||
friend class base::DeleteHelper<TtsMessageFilter>;
|
||||
|
||||
~TtsMessageFilter() override;
|
||||
|
||||
void OnInitializeVoiceList();
|
||||
void OnSpeak(const TtsUtteranceRequest& utterance);
|
||||
void OnPause();
|
||||
void OnResume();
|
||||
void OnCancel();
|
||||
|
||||
void OnChannelClosingInUIThread();
|
||||
|
||||
int render_process_id_;
|
||||
content::BrowserContext* browser_context_;
|
||||
|
||||
base::WeakPtrFactory<TtsMessageFilter> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TtsMessageFilter);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_SPEECH_TTS_MESSAGE_FILTER_H_
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/browser/speech/tts_platform.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
bool TtsPlatformImpl::LoadBuiltInTtsExtension(
|
||||
content::BrowserContext* browser_context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string TtsPlatformImpl::error() {
|
||||
return error_;
|
||||
}
|
||||
|
||||
void TtsPlatformImpl::clear_error() {
|
||||
error_ = std::string();
|
||||
}
|
||||
|
||||
void TtsPlatformImpl::set_error(const std::string& error) {
|
||||
error_ = error;
|
||||
}
|
||||
|
||||
void TtsPlatformImpl::WillSpeakUtteranceWithVoice(const Utterance* utterance,
|
||||
const VoiceData& voice_data) {
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_
|
||||
#define CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "chrome/browser/speech/tts_controller.h"
|
||||
|
||||
// Abstract class that defines the native platform TTS interface,
|
||||
// subclassed by specific implementations on Win, Mac, etc.
|
||||
class TtsPlatformImpl {
|
||||
public:
|
||||
static TtsPlatformImpl* GetInstance();
|
||||
|
||||
// Returns true if this platform implementation is supported and available.
|
||||
virtual bool PlatformImplAvailable() = 0;
|
||||
|
||||
// Some platforms may provide a built-in TTS extension. Returns true
|
||||
// if the extension was not previously loaded and is now loading, and
|
||||
// false if it's already loaded or if there's no extension to load.
|
||||
// Will call TtsController::RetrySpeakingQueuedUtterances when
|
||||
// the extension finishes loading.
|
||||
virtual bool LoadBuiltInTtsExtension(
|
||||
content::BrowserContext* browser_context);
|
||||
|
||||
// Speak the given utterance with the given parameters if possible,
|
||||
// and return true on success. Utterance will always be nonempty.
|
||||
// If rate, pitch, or volume are -1.0, they will be ignored.
|
||||
//
|
||||
// The TtsController will only try to speak one utterance at
|
||||
// a time. If it wants to interrupt speech, it will always call Stop
|
||||
// before speaking again.
|
||||
virtual bool Speak(int utterance_id,
|
||||
const std::string& utterance,
|
||||
const std::string& lang,
|
||||
const VoiceData& voice,
|
||||
const UtteranceContinuousParameters& params) = 0;
|
||||
|
||||
// Stop speaking immediately and return true on success.
|
||||
virtual bool StopSpeaking() = 0;
|
||||
|
||||
// Returns whether any speech is on going.
|
||||
virtual bool IsSpeaking() = 0;
|
||||
|
||||
// Append information about voices provided by this platform implementation
|
||||
// to |out_voices|.
|
||||
virtual void GetVoices(std::vector<VoiceData>* out_voices) = 0;
|
||||
|
||||
// Pause the current utterance, if any, until a call to Resume,
|
||||
// Speak, or StopSpeaking.
|
||||
virtual void Pause() = 0;
|
||||
|
||||
// Resume speaking the current utterance, if it was paused.
|
||||
virtual void Resume() = 0;
|
||||
|
||||
// Allows the platform to monitor speech commands and the voices used
|
||||
// for each one.
|
||||
virtual void WillSpeakUtteranceWithVoice(const Utterance* utterance,
|
||||
const VoiceData& voice_data);
|
||||
|
||||
virtual std::string error();
|
||||
virtual void clear_error();
|
||||
virtual void set_error(const std::string& error);
|
||||
|
||||
protected:
|
||||
TtsPlatformImpl() {}
|
||||
|
||||
// On some platforms this may be a leaky singleton - do not rely on the
|
||||
// destructor being called! http://crbug.com/122026
|
||||
virtual ~TtsPlatformImpl() {}
|
||||
|
||||
std::string error_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImpl);
|
||||
};
|
||||
|
||||
#endif // CHROME_BROWSER_SPEECH_TTS_PLATFORM_H_
|
|
@ -1,311 +0,0 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <math.h>
|
||||
#include <objbase.h>
|
||||
#include <sapi.h>
|
||||
#include <sphelper.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/values.h"
|
||||
#include "base/win/scoped_co_mem.h"
|
||||
#include "chrome/browser/speech/tts_controller.h"
|
||||
#include "chrome/browser/speech/tts_platform.h"
|
||||
|
||||
namespace {
|
||||
// ISpObjectToken key and value names.
|
||||
const wchar_t kAttributesKey[] = L"Attributes";
|
||||
const wchar_t kGenderValue[] = L"Gender";
|
||||
const wchar_t kLanguageValue[] = L"Language";
|
||||
} // anonymous namespace.
|
||||
|
||||
class TtsPlatformImplWin : public TtsPlatformImpl {
|
||||
public:
|
||||
bool PlatformImplAvailable() override { return true; }
|
||||
|
||||
bool Speak(int utterance_id,
|
||||
const std::string& utterance,
|
||||
const std::string& lang,
|
||||
const VoiceData& voice,
|
||||
const UtteranceContinuousParameters& params) override;
|
||||
|
||||
bool StopSpeaking() override;
|
||||
|
||||
void Pause() override;
|
||||
|
||||
void Resume() override;
|
||||
|
||||
bool IsSpeaking() override;
|
||||
|
||||
void GetVoices(std::vector<VoiceData>* out_voices) override;
|
||||
|
||||
// Get the single instance of this class.
|
||||
static TtsPlatformImplWin* GetInstance();
|
||||
|
||||
static void __stdcall SpeechEventCallback(WPARAM w_param, LPARAM l_param);
|
||||
|
||||
private:
|
||||
TtsPlatformImplWin();
|
||||
~TtsPlatformImplWin() override {}
|
||||
|
||||
void OnSpeechEvent();
|
||||
void SetVoiceFromName(const std::string& name);
|
||||
Microsoft::WRL::ComPtr<ISpVoice> speech_synthesizer_;
|
||||
|
||||
// These apply to the current utterance only.
|
||||
std::wstring utterance_;
|
||||
int utterance_id_;
|
||||
int prefix_len_;
|
||||
ULONG stream_number_;
|
||||
int char_position_;
|
||||
bool paused_;
|
||||
std::string last_voice_name_;
|
||||
friend struct base::DefaultSingletonTraits<TtsPlatformImplWin>;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TtsPlatformImplWin);
|
||||
};
|
||||
|
||||
// static
|
||||
TtsPlatformImpl* TtsPlatformImpl::GetInstance() {
|
||||
return TtsPlatformImplWin::GetInstance();
|
||||
}
|
||||
|
||||
bool TtsPlatformImplWin::Speak(int utterance_id,
|
||||
const std::string& src_utterance,
|
||||
const std::string& lang,
|
||||
const VoiceData& voice,
|
||||
const UtteranceContinuousParameters& params) {
|
||||
std::wstring prefix;
|
||||
std::wstring suffix;
|
||||
|
||||
if (!speech_synthesizer_.Get())
|
||||
return false;
|
||||
SetVoiceFromName(voice.name);
|
||||
if (params.rate >= 0.0) {
|
||||
// Map our multiplicative range of 0.1x to 10.0x onto Microsoft's
|
||||
// linear range of -10 to 10:
|
||||
// 0.1 -> -10
|
||||
// 1.0 -> 0
|
||||
// 10.0 -> 10
|
||||
speech_synthesizer_->SetRate(static_cast<int32_t>(10 * log10(params.rate)));
|
||||
}
|
||||
|
||||
if (params.pitch >= 0.0) {
|
||||
// The TTS api allows a range of -10 to 10 for speech pitch.
|
||||
// TODO(dtseng): cleanup if we ever use any other properties that
|
||||
// require xml.
|
||||
std::wstring pitch_value =
|
||||
base::IntToString16(static_cast<int>(params.pitch * 10 - 10));
|
||||
prefix = L"<pitch absmiddle=\"" + pitch_value + L"\">";
|
||||
suffix = L"</pitch>";
|
||||
}
|
||||
|
||||
if (params.volume >= 0.0) {
|
||||
// The TTS api allows a range of 0 to 100 for speech volume.
|
||||
speech_synthesizer_->SetVolume(static_cast<uint16_t>(params.volume * 100));
|
||||
}
|
||||
|
||||
// TODO(dmazzoni): convert SSML to SAPI xml. http://crbug.com/88072
|
||||
|
||||
utterance_ = base::UTF8ToWide(src_utterance);
|
||||
utterance_id_ = utterance_id;
|
||||
char_position_ = 0;
|
||||
std::wstring merged_utterance = prefix + utterance_ + suffix;
|
||||
prefix_len_ = prefix.size();
|
||||
|
||||
HRESULT result = speech_synthesizer_->Speak(merged_utterance.c_str(),
|
||||
SPF_ASYNC, &stream_number_);
|
||||
return (result == S_OK);
|
||||
}
|
||||
|
||||
bool TtsPlatformImplWin::StopSpeaking() {
|
||||
if (speech_synthesizer_.Get()) {
|
||||
// Clear the stream number so that any further events relating to this
|
||||
// utterance are ignored.
|
||||
stream_number_ = 0;
|
||||
|
||||
if (IsSpeaking()) {
|
||||
// Stop speech by speaking the empty string with the purge flag.
|
||||
speech_synthesizer_->Speak(L"", SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL);
|
||||
}
|
||||
if (paused_) {
|
||||
speech_synthesizer_->Resume();
|
||||
paused_ = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TtsPlatformImplWin::Pause() {
|
||||
if (speech_synthesizer_.Get() && utterance_id_ && !paused_) {
|
||||
speech_synthesizer_->Pause();
|
||||
paused_ = true;
|
||||
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_PAUSE,
|
||||
char_position_, "");
|
||||
}
|
||||
}
|
||||
|
||||
void TtsPlatformImplWin::Resume() {
|
||||
if (speech_synthesizer_.Get() && utterance_id_ && paused_) {
|
||||
speech_synthesizer_->Resume();
|
||||
paused_ = false;
|
||||
TtsController::GetInstance()->OnTtsEvent(utterance_id_, TTS_EVENT_RESUME,
|
||||
char_position_, "");
|
||||
}
|
||||
}
|
||||
|
||||
bool TtsPlatformImplWin::IsSpeaking() {
|
||||
if (speech_synthesizer_.Get()) {
|
||||
SPVOICESTATUS status;
|
||||
HRESULT result = speech_synthesizer_->GetStatus(&status, NULL);
|
||||
if (result == S_OK) {
|
||||
if (status.dwRunningState == 0 || // 0 == waiting to speak
|
||||
status.dwRunningState == SPRS_IS_SPEAKING) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TtsPlatformImplWin::GetVoices(std::vector<VoiceData>* out_voices) {
|
||||
Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
|
||||
unsigned long voice_count;
|
||||
if (S_OK !=
|
||||
SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf()))
|
||||
return;
|
||||
if (S_OK != voice_tokens->GetCount(&voice_count))
|
||||
return;
|
||||
for (unsigned i = 0; i < voice_count; i++) {
|
||||
VoiceData voice;
|
||||
Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
|
||||
if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL))
|
||||
return;
|
||||
base::win::ScopedCoMem<WCHAR> description;
|
||||
if (S_OK != SpGetDescription(voice_token.Get(), &description))
|
||||
continue;
|
||||
voice.name = base::WideToUTF8(description.get());
|
||||
Microsoft::WRL::ComPtr<ISpDataKey> attributes;
|
||||
if (S_OK != voice_token->OpenKey(kAttributesKey, attributes.GetAddressOf()))
|
||||
continue;
|
||||
base::win::ScopedCoMem<WCHAR> gender;
|
||||
if (S_OK == attributes->GetStringValue(kGenderValue, &gender)) {
|
||||
if (0 == _wcsicmp(gender.get(), L"male"))
|
||||
voice.gender = TTS_GENDER_MALE;
|
||||
else if (0 == _wcsicmp(gender.get(), L"female"))
|
||||
voice.gender = TTS_GENDER_FEMALE;
|
||||
}
|
||||
base::win::ScopedCoMem<WCHAR> language;
|
||||
if (S_OK == attributes->GetStringValue(kLanguageValue, &language)) {
|
||||
int lcid_value;
|
||||
base::HexStringToInt(base::WideToUTF8(language.get()), &lcid_value);
|
||||
LCID lcid = MAKELCID(lcid_value, SORT_DEFAULT);
|
||||
WCHAR locale_name[LOCALE_NAME_MAX_LENGTH] = {0};
|
||||
LCIDToLocaleName(lcid, locale_name, LOCALE_NAME_MAX_LENGTH, 0);
|
||||
voice.lang = base::WideToUTF8(locale_name);
|
||||
}
|
||||
voice.native = true;
|
||||
voice.events.insert(TTS_EVENT_START);
|
||||
voice.events.insert(TTS_EVENT_END);
|
||||
voice.events.insert(TTS_EVENT_MARKER);
|
||||
voice.events.insert(TTS_EVENT_WORD);
|
||||
voice.events.insert(TTS_EVENT_SENTENCE);
|
||||
voice.events.insert(TTS_EVENT_PAUSE);
|
||||
voice.events.insert(TTS_EVENT_RESUME);
|
||||
out_voices->push_back(voice);
|
||||
}
|
||||
}
|
||||
|
||||
void TtsPlatformImplWin::OnSpeechEvent() {
|
||||
TtsController* controller = TtsController::GetInstance();
|
||||
SPEVENT event;
|
||||
while (S_OK == speech_synthesizer_->GetEvents(1, &event, NULL)) {
|
||||
if (event.ulStreamNum != stream_number_)
|
||||
continue;
|
||||
|
||||
switch (event.eEventId) {
|
||||
case SPEI_START_INPUT_STREAM:
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_START, 0,
|
||||
std::string());
|
||||
break;
|
||||
case SPEI_END_INPUT_STREAM:
|
||||
char_position_ = utterance_.size();
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_END, char_position_,
|
||||
std::string());
|
||||
break;
|
||||
case SPEI_TTS_BOOKMARK:
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_MARKER, char_position_,
|
||||
std::string());
|
||||
break;
|
||||
case SPEI_WORD_BOUNDARY:
|
||||
char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_WORD, char_position_,
|
||||
std::string());
|
||||
break;
|
||||
case SPEI_SENTENCE_BOUNDARY:
|
||||
char_position_ = static_cast<ULONG>(event.lParam) - prefix_len_;
|
||||
controller->OnTtsEvent(utterance_id_, TTS_EVENT_SENTENCE,
|
||||
char_position_, std::string());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void TtsPlatformImplWin::SetVoiceFromName(const std::string& name) {
|
||||
if (name.empty() || name == last_voice_name_)
|
||||
return;
|
||||
last_voice_name_ = name;
|
||||
Microsoft::WRL::ComPtr<IEnumSpObjectTokens> voice_tokens;
|
||||
unsigned long voice_count;
|
||||
if (S_OK !=
|
||||
SpEnumTokens(SPCAT_VOICES, NULL, NULL, voice_tokens.GetAddressOf()))
|
||||
return;
|
||||
if (S_OK != voice_tokens->GetCount(&voice_count))
|
||||
return;
|
||||
for (unsigned i = 0; i < voice_count; i++) {
|
||||
Microsoft::WRL::ComPtr<ISpObjectToken> voice_token;
|
||||
if (S_OK != voice_tokens->Next(1, voice_token.GetAddressOf(), NULL))
|
||||
return;
|
||||
base::win::ScopedCoMem<WCHAR> description;
|
||||
if (S_OK != SpGetDescription(voice_token.Get(), &description))
|
||||
continue;
|
||||
if (name == base::WideToUTF8(description.get())) {
|
||||
speech_synthesizer_->SetVoice(voice_token.Get());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
TtsPlatformImplWin::TtsPlatformImplWin()
|
||||
: utterance_id_(0),
|
||||
prefix_len_(0),
|
||||
stream_number_(0),
|
||||
char_position_(0),
|
||||
paused_(false) {
|
||||
::CoCreateInstance(CLSID_SpVoice, nullptr, CLSCTX_ALL,
|
||||
IID_PPV_ARGS(&speech_synthesizer_));
|
||||
if (speech_synthesizer_.Get()) {
|
||||
ULONGLONG event_mask =
|
||||
SPFEI(SPEI_START_INPUT_STREAM) | SPFEI(SPEI_TTS_BOOKMARK) |
|
||||
SPFEI(SPEI_WORD_BOUNDARY) | SPFEI(SPEI_SENTENCE_BOUNDARY) |
|
||||
SPFEI(SPEI_END_INPUT_STREAM);
|
||||
speech_synthesizer_->SetInterest(event_mask, event_mask);
|
||||
speech_synthesizer_->SetNotifyCallbackFunction(
|
||||
TtsPlatformImplWin::SpeechEventCallback, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
TtsPlatformImplWin* TtsPlatformImplWin::GetInstance() {
|
||||
return base::Singleton<TtsPlatformImplWin,
|
||||
base::LeakySingletonTraits<TtsPlatformImplWin>>::get();
|
||||
}
|
||||
|
||||
// static
|
||||
void TtsPlatformImplWin::SpeechEventCallback(WPARAM w_param, LPARAM l_param) {
|
||||
GetInstance()->OnSpeechEvent();
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Multiply-included message file, hence no include guard.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "chrome/common/tts_utterance_request.h"
|
||||
#include "ipc/ipc_message_macros.h"
|
||||
#include "ipc/ipc_param_traits.h"
|
||||
|
||||
#define IPC_MESSAGE_START TtsMsgStart
|
||||
|
||||
IPC_STRUCT_TRAITS_BEGIN(TtsUtteranceRequest)
|
||||
IPC_STRUCT_TRAITS_MEMBER(id)
|
||||
IPC_STRUCT_TRAITS_MEMBER(text)
|
||||
IPC_STRUCT_TRAITS_MEMBER(lang)
|
||||
IPC_STRUCT_TRAITS_MEMBER(voice)
|
||||
IPC_STRUCT_TRAITS_MEMBER(volume)
|
||||
IPC_STRUCT_TRAITS_MEMBER(rate)
|
||||
IPC_STRUCT_TRAITS_MEMBER(pitch)
|
||||
IPC_STRUCT_TRAITS_END()
|
||||
|
||||
IPC_STRUCT_TRAITS_BEGIN(TtsVoice)
|
||||
IPC_STRUCT_TRAITS_MEMBER(voice_uri)
|
||||
IPC_STRUCT_TRAITS_MEMBER(name)
|
||||
IPC_STRUCT_TRAITS_MEMBER(lang)
|
||||
IPC_STRUCT_TRAITS_MEMBER(local_service)
|
||||
IPC_STRUCT_TRAITS_MEMBER(is_default)
|
||||
IPC_STRUCT_TRAITS_END()
|
||||
|
||||
// Renderer -> Browser messages.
|
||||
|
||||
IPC_MESSAGE_CONTROL0(TtsHostMsg_InitializeVoiceList)
|
||||
IPC_MESSAGE_CONTROL1(TtsHostMsg_Speak, TtsUtteranceRequest)
|
||||
IPC_MESSAGE_CONTROL0(TtsHostMsg_Pause)
|
||||
IPC_MESSAGE_CONTROL0(TtsHostMsg_Resume)
|
||||
IPC_MESSAGE_CONTROL0(TtsHostMsg_Cancel)
|
||||
|
||||
// Browser -> Renderer messages.
|
||||
|
||||
IPC_MESSAGE_CONTROL1(TtsMsg_SetVoiceList, std::vector<TtsVoice>)
|
||||
IPC_MESSAGE_CONTROL1(TtsMsg_DidStartSpeaking, int /* utterance id */)
|
||||
IPC_MESSAGE_CONTROL1(TtsMsg_DidFinishSpeaking, int /* utterance id */)
|
||||
IPC_MESSAGE_CONTROL1(TtsMsg_DidPauseSpeaking, int /* utterance id */)
|
||||
IPC_MESSAGE_CONTROL1(TtsMsg_DidResumeSpeaking, int /* utterance id */)
|
||||
IPC_MESSAGE_CONTROL2(TtsMsg_WordBoundary,
|
||||
int /* utterance id */,
|
||||
int /* char index */)
|
||||
IPC_MESSAGE_CONTROL2(TtsMsg_SentenceBoundary,
|
||||
int /* utterance id */,
|
||||
int /* char index */)
|
||||
IPC_MESSAGE_CONTROL2(TtsMsg_MarkerEvent,
|
||||
int /* utterance id */,
|
||||
int /* char index */)
|
||||
IPC_MESSAGE_CONTROL1(TtsMsg_WasInterrupted, int /* utterance id */)
|
||||
IPC_MESSAGE_CONTROL1(TtsMsg_WasCancelled, int /* utterance id */)
|
||||
IPC_MESSAGE_CONTROL2(TtsMsg_SpeakingErrorOccurred,
|
||||
int /* utterance id */,
|
||||
std::string /* error message */)
|
|
@ -1,20 +0,0 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/common/tts_utterance_request.h"
|
||||
|
||||
TtsUtteranceRequest::TtsUtteranceRequest()
|
||||
: id(0), volume(1.0), rate(1.0), pitch(1.0) {}
|
||||
|
||||
TtsUtteranceRequest::~TtsUtteranceRequest() {}
|
||||
|
||||
TtsVoice::TtsVoice() : local_service(true), is_default(false) {}
|
||||
|
||||
TtsVoice::TtsVoice(const TtsVoice&) = default;
|
||||
|
||||
TtsVoice::~TtsVoice() {}
|
||||
|
||||
TtsUtteranceResponse::TtsUtteranceResponse() : id(0) {}
|
||||
|
||||
TtsUtteranceResponse::~TtsUtteranceResponse() {}
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_
|
||||
#define CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/strings/string16.h"
|
||||
|
||||
struct TtsUtteranceRequest {
|
||||
TtsUtteranceRequest();
|
||||
~TtsUtteranceRequest();
|
||||
|
||||
int id;
|
||||
std::string text;
|
||||
std::string lang;
|
||||
std::string voice;
|
||||
float volume;
|
||||
float rate;
|
||||
float pitch;
|
||||
};
|
||||
|
||||
struct TtsVoice {
|
||||
TtsVoice();
|
||||
TtsVoice(const TtsVoice&);
|
||||
~TtsVoice();
|
||||
|
||||
std::string voice_uri;
|
||||
std::string name;
|
||||
std::string lang;
|
||||
bool local_service;
|
||||
bool is_default;
|
||||
};
|
||||
|
||||
struct TtsUtteranceResponse {
|
||||
TtsUtteranceResponse();
|
||||
~TtsUtteranceResponse();
|
||||
|
||||
int id;
|
||||
};
|
||||
|
||||
#endif // CHROME_COMMON_TTS_UTTERANCE_REQUEST_H_
|
|
@ -1,198 +0,0 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "chrome/renderer/tts_dispatcher.h"
|
||||
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "chrome/common/tts_messages.h"
|
||||
#include "chrome/common/tts_utterance_request.h"
|
||||
#include "content/public/renderer/render_thread.h"
|
||||
#include "third_party/blink/public/platform/web_speech_synthesis_utterance.h"
|
||||
#include "third_party/blink/public/platform/web_speech_synthesis_voice.h"
|
||||
#include "third_party/blink/public/platform/web_string.h"
|
||||
#include "third_party/blink/public/platform/web_vector.h"
|
||||
|
||||
using blink::WebSpeechSynthesisUtterance;
|
||||
using blink::WebSpeechSynthesisVoice;
|
||||
using blink::WebSpeechSynthesizerClient;
|
||||
using blink::WebString;
|
||||
using blink::WebVector;
|
||||
using content::RenderThread;
|
||||
|
||||
int TtsDispatcher::next_utterance_id_ = 1;
|
||||
|
||||
TtsDispatcher::TtsDispatcher(WebSpeechSynthesizerClient* client)
|
||||
: synthesizer_client_(client) {
|
||||
RenderThread::Get()->AddObserver(this);
|
||||
}
|
||||
|
||||
TtsDispatcher::~TtsDispatcher() {
|
||||
RenderThread::Get()->RemoveObserver(this);
|
||||
}
|
||||
|
||||
bool TtsDispatcher::OnControlMessageReceived(const IPC::Message& message) {
|
||||
IPC_BEGIN_MESSAGE_MAP(TtsDispatcher, message)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_SetVoiceList, OnSetVoiceList)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_DidStartSpeaking, OnDidStartSpeaking)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_DidFinishSpeaking, OnDidFinishSpeaking)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_DidPauseSpeaking, OnDidPauseSpeaking)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_DidResumeSpeaking, OnDidResumeSpeaking)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_WordBoundary, OnWordBoundary)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_SentenceBoundary, OnSentenceBoundary)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_MarkerEvent, OnMarkerEvent)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_WasInterrupted, OnWasInterrupted)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_WasCancelled, OnWasCancelled)
|
||||
IPC_MESSAGE_HANDLER(TtsMsg_SpeakingErrorOccurred, OnSpeakingErrorOccurred)
|
||||
IPC_END_MESSAGE_MAP()
|
||||
|
||||
// Always return false because there may be multiple TtsDispatchers
|
||||
// and we want them all to have a chance to handle this message.
|
||||
return false;
|
||||
}
|
||||
|
||||
void TtsDispatcher::UpdateVoiceList() {
|
||||
RenderThread::Get()->Send(new TtsHostMsg_InitializeVoiceList());
|
||||
}
|
||||
|
||||
void TtsDispatcher::Speak(const WebSpeechSynthesisUtterance& web_utterance) {
|
||||
int id = next_utterance_id_++;
|
||||
|
||||
utterance_id_map_[id] = web_utterance;
|
||||
|
||||
TtsUtteranceRequest utterance;
|
||||
utterance.id = id;
|
||||
utterance.text = web_utterance.GetText().Utf8();
|
||||
utterance.lang = web_utterance.Lang().Utf8();
|
||||
utterance.voice = web_utterance.Voice().Utf8();
|
||||
utterance.volume = web_utterance.Volume();
|
||||
utterance.rate = web_utterance.Rate();
|
||||
utterance.pitch = web_utterance.Pitch();
|
||||
RenderThread::Get()->Send(new TtsHostMsg_Speak(utterance));
|
||||
}
|
||||
|
||||
void TtsDispatcher::Pause() {
|
||||
RenderThread::Get()->Send(new TtsHostMsg_Pause());
|
||||
}
|
||||
|
||||
void TtsDispatcher::Resume() {
|
||||
RenderThread::Get()->Send(new TtsHostMsg_Resume());
|
||||
}
|
||||
|
||||
void TtsDispatcher::Cancel() {
|
||||
RenderThread::Get()->Send(new TtsHostMsg_Cancel());
|
||||
}
|
||||
|
||||
WebSpeechSynthesisUtterance TtsDispatcher::FindUtterance(int utterance_id) {
|
||||
base::hash_map<int, WebSpeechSynthesisUtterance>::const_iterator iter =
|
||||
utterance_id_map_.find(utterance_id);
|
||||
if (iter == utterance_id_map_.end())
|
||||
return WebSpeechSynthesisUtterance();
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnSetVoiceList(const std::vector<TtsVoice>& voices) {
|
||||
WebVector<WebSpeechSynthesisVoice> out_voices(voices.size());
|
||||
for (size_t i = 0; i < voices.size(); ++i) {
|
||||
out_voices[i] = WebSpeechSynthesisVoice();
|
||||
out_voices[i].SetVoiceURI(WebString::FromUTF8(voices[i].voice_uri));
|
||||
out_voices[i].SetName(WebString::FromUTF8(voices[i].name));
|
||||
out_voices[i].SetLanguage(WebString::FromUTF8(voices[i].lang));
|
||||
out_voices[i].SetIsLocalService(voices[i].local_service);
|
||||
out_voices[i].SetIsDefault(voices[i].is_default);
|
||||
}
|
||||
synthesizer_client_->SetVoiceList(out_voices);
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnDidStartSpeaking(int utterance_id) {
|
||||
if (utterance_id_map_.find(utterance_id) == utterance_id_map_.end())
|
||||
return;
|
||||
|
||||
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
|
||||
if (utterance.IsNull())
|
||||
return;
|
||||
|
||||
synthesizer_client_->DidStartSpeaking(utterance);
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnDidFinishSpeaking(int utterance_id) {
|
||||
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
|
||||
if (utterance.IsNull())
|
||||
return;
|
||||
|
||||
synthesizer_client_->DidFinishSpeaking(utterance);
|
||||
utterance_id_map_.erase(utterance_id);
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnDidPauseSpeaking(int utterance_id) {
|
||||
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
|
||||
if (utterance.IsNull())
|
||||
return;
|
||||
|
||||
synthesizer_client_->DidPauseSpeaking(utterance);
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnDidResumeSpeaking(int utterance_id) {
|
||||
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
|
||||
if (utterance.IsNull())
|
||||
return;
|
||||
|
||||
synthesizer_client_->DidResumeSpeaking(utterance);
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnWordBoundary(int utterance_id, int char_index) {
|
||||
CHECK(char_index >= 0);
|
||||
|
||||
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
|
||||
if (utterance.IsNull())
|
||||
return;
|
||||
|
||||
synthesizer_client_->WordBoundaryEventOccurred(
|
||||
utterance, static_cast<unsigned>(char_index));
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnSentenceBoundary(int utterance_id, int char_index) {
|
||||
CHECK(char_index >= 0);
|
||||
|
||||
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
|
||||
if (utterance.IsNull())
|
||||
return;
|
||||
|
||||
synthesizer_client_->SentenceBoundaryEventOccurred(
|
||||
utterance, static_cast<unsigned>(char_index));
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnMarkerEvent(int utterance_id, int char_index) {
|
||||
// Not supported yet.
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnWasInterrupted(int utterance_id) {
|
||||
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
|
||||
if (utterance.IsNull())
|
||||
return;
|
||||
|
||||
// The web speech API doesn't support "interrupted".
|
||||
synthesizer_client_->DidFinishSpeaking(utterance);
|
||||
utterance_id_map_.erase(utterance_id);
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnWasCancelled(int utterance_id) {
|
||||
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
|
||||
if (utterance.IsNull())
|
||||
return;
|
||||
|
||||
// The web speech API doesn't support "cancelled".
|
||||
synthesizer_client_->DidFinishSpeaking(utterance);
|
||||
utterance_id_map_.erase(utterance_id);
|
||||
}
|
||||
|
||||
void TtsDispatcher::OnSpeakingErrorOccurred(int utterance_id,
|
||||
const std::string& error_message) {
|
||||
WebSpeechSynthesisUtterance utterance = FindUtterance(utterance_id);
|
||||
if (utterance.IsNull())
|
||||
return;
|
||||
|
||||
// The web speech API doesn't support an error message.
|
||||
synthesizer_client_->SpeakingErrorOccurred(utterance);
|
||||
utterance_id_map_.erase(utterance_id);
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CHROME_RENDERER_TTS_DISPATCHER_H_
|
||||
#define CHROME_RENDERER_TTS_DISPATCHER_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/hash_tables.h"
|
||||
#include "content/public/renderer/render_thread_observer.h"
|
||||
#include "third_party/blink/public/platform/web_speech_synthesizer.h"
|
||||
#include "third_party/blink/public/platform/web_speech_synthesizer_client.h"
|
||||
|
||||
namespace IPC {
|
||||
class Message;
|
||||
}
|
||||
|
||||
struct TtsVoice;
|
||||
|
||||
// TtsDispatcher is a delegate for methods used by Blink for speech synthesis
|
||||
// APIs. It's the complement of TtsDispatcherHost (owned by RenderViewHost).
|
||||
// Each TtsDispatcher is owned by the WebSpeechSynthesizerClient in Blink;
|
||||
// it registers itself to listen to IPC upon construction and unregisters
|
||||
// itself when deleted. There can be multiple TtsDispatchers alive at once,
|
||||
// so each one routes IPC messages to its WebSpeechSynthesizerClient only if
|
||||
// the utterance id (which is globally unique) matches.
|
||||
class TtsDispatcher : public blink::WebSpeechSynthesizer,
|
||||
public content::RenderThreadObserver {
|
||||
public:
|
||||
explicit TtsDispatcher(blink::WebSpeechSynthesizerClient* client);
|
||||
~TtsDispatcher() override;
|
||||
|
||||
private:
|
||||
// RenderProcessObserver override.
|
||||
bool OnControlMessageReceived(const IPC::Message& message) override;
|
||||
|
||||
// blink::WebSpeechSynthesizer implementation.
|
||||
void UpdateVoiceList() override;
|
||||
void Speak(const blink::WebSpeechSynthesisUtterance& utterance) override;
|
||||
void Pause() override;
|
||||
void Resume() override;
|
||||
void Cancel() override;
|
||||
|
||||
blink::WebSpeechSynthesisUtterance FindUtterance(int utterance_id);
|
||||
|
||||
void OnSetVoiceList(const std::vector<TtsVoice>& voices);
|
||||
void OnDidStartSpeaking(int utterance_id);
|
||||
void OnDidFinishSpeaking(int utterance_id);
|
||||
void OnDidPauseSpeaking(int utterance_id);
|
||||
void OnDidResumeSpeaking(int utterance_id);
|
||||
void OnWordBoundary(int utterance_id, int char_index);
|
||||
void OnSentenceBoundary(int utterance_id, int char_index);
|
||||
void OnMarkerEvent(int utterance_id, int char_index);
|
||||
void OnWasInterrupted(int utterance_id);
|
||||
void OnWasCancelled(int utterance_id);
|
||||
void OnSpeakingErrorOccurred(int utterance_id,
|
||||
const std::string& error_message);
|
||||
|
||||
// The WebKit client class that we use to send events back to the JS world.
|
||||
// Weak reference, this will be valid as long as this object exists.
|
||||
blink::WebSpeechSynthesizerClient* synthesizer_client_;
|
||||
|
||||
// Next utterance id, used to map response IPCs to utterance objects.
|
||||
static int next_utterance_id_;
|
||||
|
||||
// Map from id to utterance objects.
|
||||
base::hash_map<int, blink::WebSpeechSynthesisUtterance> utterance_id_map_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TtsDispatcher);
|
||||
};
|
||||
|
||||
#endif // CHROME_RENDERER_TTS_DISPATCHER_H_
|
|
@ -1,52 +0,0 @@
|
|||
// This is generated file. Do not modify directly.
|
||||
// Path to the code generator:
|
||||
// tools/generate_library_loader/generate_library_loader.py .
|
||||
|
||||
#ifndef LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H
|
||||
#define LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H
|
||||
|
||||
#include "third_party/speech-dispatcher/libspeechd.h"
|
||||
#define LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN
|
||||
|
||||
#include <string>
|
||||
|
||||
class LibSpeechdLoader {
|
||||
public:
|
||||
LibSpeechdLoader();
|
||||
~LibSpeechdLoader();
|
||||
|
||||
bool Load(const std::string& library_name)
|
||||
__attribute__((warn_unused_result));
|
||||
|
||||
bool loaded() const { return loaded_; }
|
||||
|
||||
decltype(&::spd_open) spd_open;
|
||||
decltype(&::spd_say) spd_say;
|
||||
decltype(&::spd_stop) spd_stop;
|
||||
decltype(&::spd_close) spd_close;
|
||||
decltype(&::spd_pause) spd_pause;
|
||||
decltype(&::spd_resume) spd_resume;
|
||||
decltype(&::spd_set_notification_on) spd_set_notification_on;
|
||||
decltype(&::spd_set_voice_rate) spd_set_voice_rate;
|
||||
decltype(&::spd_set_voice_pitch) spd_set_voice_pitch;
|
||||
decltype(&::spd_list_synthesis_voices) spd_list_synthesis_voices;
|
||||
decltype(&::spd_set_synthesis_voice) spd_set_synthesis_voice;
|
||||
decltype(&::spd_list_modules) spd_list_modules;
|
||||
decltype(&::spd_set_output_module) spd_set_output_module;
|
||||
decltype(&::spd_set_language) spd_set_language;
|
||||
|
||||
private:
|
||||
void CleanUp(bool unload);
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
void* library_;
|
||||
#endif
|
||||
|
||||
bool loaded_;
|
||||
|
||||
// Disallow copy constructor and assignment operator.
|
||||
LibSpeechdLoader(const LibSpeechdLoader&);
|
||||
void operator=(const LibSpeechdLoader&);
|
||||
};
|
||||
|
||||
#endif // LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H
|
|
@ -1,252 +0,0 @@
|
|||
// This is generated file. Do not modify directly.
|
||||
// Path to the code generator:
|
||||
// tools/generate_library_loader/generate_library_loader.py .
|
||||
|
||||
#include "library_loaders/libspeechd.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
// Put these sanity checks here so that they fire at most once
|
||||
// (to avoid cluttering the build output).
|
||||
#if !defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) && \
|
||||
!defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
#error neither LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN nor LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED defined
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN) && \
|
||||
defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
#error both LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN and LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED defined
|
||||
#endif
|
||||
|
||||
LibSpeechdLoader::LibSpeechdLoader() : loaded_(false) {}
|
||||
|
||||
LibSpeechdLoader::~LibSpeechdLoader() {
|
||||
CleanUp(loaded_);
|
||||
}
|
||||
|
||||
bool LibSpeechdLoader::Load(const std::string& library_name) {
|
||||
if (loaded_)
|
||||
return false;
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
|
||||
if (!library_)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_open =
|
||||
reinterpret_cast<decltype(this->spd_open)>(dlsym(library_, "spd_open"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_open = &::spd_open;
|
||||
#endif
|
||||
if (!spd_open) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_say =
|
||||
reinterpret_cast<decltype(this->spd_say)>(dlsym(library_, "spd_say"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_say = &::spd_say;
|
||||
#endif
|
||||
if (!spd_say) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_stop =
|
||||
reinterpret_cast<decltype(this->spd_stop)>(dlsym(library_, "spd_stop"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_stop = &::spd_stop;
|
||||
#endif
|
||||
if (!spd_stop) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_close =
|
||||
reinterpret_cast<decltype(this->spd_close)>(dlsym(library_, "spd_close"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_close = &::spd_close;
|
||||
#endif
|
||||
if (!spd_close) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_pause =
|
||||
reinterpret_cast<decltype(this->spd_pause)>(dlsym(library_, "spd_pause"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_pause = &::spd_pause;
|
||||
#endif
|
||||
if (!spd_pause) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_resume = reinterpret_cast<decltype(this->spd_resume)>(
|
||||
dlsym(library_, "spd_resume"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_resume = &::spd_resume;
|
||||
#endif
|
||||
if (!spd_resume) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_set_notification_on =
|
||||
reinterpret_cast<decltype(this->spd_set_notification_on)>(
|
||||
dlsym(library_, "spd_set_notification_on"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_set_notification_on = &::spd_set_notification_on;
|
||||
#endif
|
||||
if (!spd_set_notification_on) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_set_voice_rate = reinterpret_cast<decltype(this->spd_set_voice_rate)>(
|
||||
dlsym(library_, "spd_set_voice_rate"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_set_voice_rate = &::spd_set_voice_rate;
|
||||
#endif
|
||||
if (!spd_set_voice_rate) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_set_voice_pitch = reinterpret_cast<decltype(this->spd_set_voice_pitch)>(
|
||||
dlsym(library_, "spd_set_voice_pitch"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_set_voice_pitch = &::spd_set_voice_pitch;
|
||||
#endif
|
||||
if (!spd_set_voice_pitch) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_list_synthesis_voices =
|
||||
reinterpret_cast<decltype(this->spd_list_synthesis_voices)>(
|
||||
dlsym(library_, "spd_list_synthesis_voices"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_list_synthesis_voices = &::spd_list_synthesis_voices;
|
||||
#endif
|
||||
if (!spd_list_synthesis_voices) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_set_synthesis_voice =
|
||||
reinterpret_cast<decltype(this->spd_set_synthesis_voice)>(
|
||||
dlsym(library_, "spd_set_synthesis_voice"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_set_synthesis_voice = &::spd_set_synthesis_voice;
|
||||
#endif
|
||||
if (!spd_set_synthesis_voice) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_list_modules = reinterpret_cast<decltype(this->spd_list_modules)>(
|
||||
dlsym(library_, "spd_list_modules"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_list_modules = &::spd_list_modules;
|
||||
#endif
|
||||
if (!spd_list_modules) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_set_output_module =
|
||||
reinterpret_cast<decltype(this->spd_set_output_module)>(
|
||||
dlsym(library_, "spd_set_output_module"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_set_output_module = &::spd_set_output_module;
|
||||
#endif
|
||||
if (!spd_set_output_module) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
spd_set_language = reinterpret_cast<decltype(this->spd_set_language)>(
|
||||
dlsym(library_, "spd_set_language"));
|
||||
#endif
|
||||
#if defined( \
|
||||
LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DT_NEEDED)
|
||||
spd_set_language = &::spd_set_language;
|
||||
#endif
|
||||
if (!spd_set_language) {
|
||||
CleanUp(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
loaded_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void LibSpeechdLoader::CleanUp(bool unload) {
|
||||
#if defined(LIBRARY_LOADER_OUT_RELEASE_GEN_LIBRARY_LOADERS_LIBSPEECHD_H_DLOPEN)
|
||||
if (unload) {
|
||||
dlclose(library_);
|
||||
library_ = NULL;
|
||||
}
|
||||
#endif
|
||||
loaded_ = false;
|
||||
spd_open = NULL;
|
||||
spd_say = NULL;
|
||||
spd_stop = NULL;
|
||||
spd_close = NULL;
|
||||
spd_pause = NULL;
|
||||
spd_resume = NULL;
|
||||
spd_set_notification_on = NULL;
|
||||
spd_set_voice_rate = NULL;
|
||||
spd_set_voice_pitch = NULL;
|
||||
spd_list_synthesis_voices = NULL;
|
||||
spd_set_synthesis_voice = NULL;
|
||||
spd_list_modules = NULL;
|
||||
spd_set_output_module = NULL;
|
||||
spd_set_language = NULL;
|
||||
}
|
|
@ -577,8 +577,8 @@ filenames = {
|
|||
"atom/renderer/web_worker_observer.h",
|
||||
"atom/utility/atom_content_utility_client.cc",
|
||||
"atom/utility/atom_content_utility_client.h",
|
||||
"chromium_src/chrome/browser/browser_process.cc",
|
||||
"chromium_src/chrome/browser/browser_process.h",
|
||||
"chromium_src/chrome/browser/browser_process_impl.cc",
|
||||
"chromium_src/chrome/browser/browser_process_impl.h",
|
||||
"chromium_src/chrome/browser/chrome_process_finder_win.cc",
|
||||
"chromium_src/chrome/browser/chrome_process_finder_win.h",
|
||||
"chromium_src/chrome/browser/chrome_notification_types.h",
|
||||
|
@ -604,16 +604,6 @@ filenames = {
|
|||
"chromium_src/chrome/browser/process_singleton_posix.cc",
|
||||
"chromium_src/chrome/browser/process_singleton_win.cc",
|
||||
"chromium_src/chrome/browser/process_singleton.h",
|
||||
"chromium_src/chrome/browser/speech/tts_controller.h",
|
||||
"chromium_src/chrome/browser/speech/tts_controller_impl.cc",
|
||||
"chromium_src/chrome/browser/speech/tts_controller_impl.h",
|
||||
"chromium_src/chrome/browser/speech/tts_linux.cc",
|
||||
"chromium_src/chrome/browser/speech/tts_mac.mm",
|
||||
"chromium_src/chrome/browser/speech/tts_message_filter.cc",
|
||||
"chromium_src/chrome/browser/speech/tts_message_filter.h",
|
||||
"chromium_src/chrome/browser/speech/tts_platform.cc",
|
||||
"chromium_src/chrome/browser/speech/tts_platform.h",
|
||||
"chromium_src/chrome/browser/speech/tts_win.cc",
|
||||
"chromium_src/chrome/browser/ui/browser_dialogs.h",
|
||||
"chromium_src/chrome/browser/ui/cocoa/color_chooser_mac.mm",
|
||||
"chromium_src/chrome/browser/ui/views/color_chooser_aura.cc",
|
||||
|
@ -622,9 +612,6 @@ filenames = {
|
|||
"chromium_src/chrome/browser/ui/views/frame/global_menu_bar_registrar_x11.h",
|
||||
"chromium_src/chrome/common/print_messages.cc",
|
||||
"chromium_src/chrome/common/print_messages.h",
|
||||
"chromium_src/chrome/common/tts_messages.h",
|
||||
"chromium_src/chrome/common/tts_utterance_request.cc",
|
||||
"chromium_src/chrome/common/tts_utterance_request.h",
|
||||
"chromium_src/chrome/renderer/printing/print_web_view_helper.cc",
|
||||
"chromium_src/chrome/renderer/printing/print_web_view_helper_linux.cc",
|
||||
"chromium_src/chrome/renderer/printing/print_web_view_helper_mac.mm",
|
||||
|
@ -632,11 +619,7 @@ filenames = {
|
|||
"chromium_src/chrome/renderer/printing/print_web_view_helper.h",
|
||||
"chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.cc",
|
||||
"chromium_src/chrome/renderer/spellchecker/spellcheck_worditerator.h",
|
||||
"chromium_src/chrome/renderer/tts_dispatcher.cc",
|
||||
"chromium_src/chrome/renderer/tts_dispatcher.h",
|
||||
"chromium_src/chrome/utility/utility_message_handler.h",
|
||||
"chromium_src/library_loaders/libspeechd_loader.cc",
|
||||
"chromium_src/library_loaders/libspeechd.h",
|
||||
]
|
||||
|
||||
lib_sources_nss = [
|
||||
|
|
|
@ -380,7 +380,7 @@ patches:
|
|||
file: allow_nested_error_trackers.patch
|
||||
description: |
|
||||
Only one X11ErrorTracker should exist at a time, but upstream has a bug
|
||||
where two can exist if running in headless mode --
|
||||
where two can exist if running in headless mode --
|
||||
ui::(anonymous namespace)::SupportsEWMH() [inner tracker is created]
|
||||
ui::WmSupportsHint()
|
||||
ui::IsX11WindowFullScreen()
|
||||
|
@ -430,11 +430,6 @@ patches:
|
|||
description: |
|
||||
Compilation of those files fails with the Chromium 68.
|
||||
Remove the patch during the Chromium 69 upgrade.
|
||||
-
|
||||
author: deepak1556 <hop2deep@gmail.com>
|
||||
file: disable_extensions_gn.patch
|
||||
description: |
|
||||
Fix build files generation when chrome extensions are disabled.
|
||||
-
|
||||
author: Jeremy Apthorp <nornagon@nornagon.net>
|
||||
file: crashpad_http_status.patch
|
||||
|
@ -503,3 +498,12 @@ patches:
|
|||
(and aren't compiling command.cc), it's safe to duplicate the
|
||||
definition. A candidate for upstreaming would be to move the IsMediaKey
|
||||
function into //ui.
|
||||
-
|
||||
author: Heilig Benedek <benecene@gmail.com>
|
||||
file: tts.patch
|
||||
description: |
|
||||
* Adds patch in //chrome/browser/speech/tts_controller_impl.cc
|
||||
to disable calls using chrome profile class.
|
||||
* Adds patch in //chrome/browser/speech/tts_message_filter.cc
|
||||
to remove reference to browser context when its signaled for
|
||||
destruction from content layer.
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
From 790e1e19af133252e95e3a4de8c8e7a3a011bf02 Mon Sep 17 00:00:00 2001
|
||||
From: deepak1556 <hop2deep@gmail.com>
|
||||
Date: Thu, 20 Sep 2018 17:50:48 -0700
|
||||
Subject: disable_extensions_gn.patch
|
||||
|
||||
Fix build files generation when chrome extensions are disabled.
|
||||
|
||||
diff --git a/chrome/browser/apps/app_shim/BUILD.gn b/chrome/browser/apps/app_shim/BUILD.gn
|
||||
index f8a6d1868788..350c3572ec54 100644
|
||||
--- a/chrome/browser/apps/app_shim/BUILD.gn
|
||||
+++ b/chrome/browser/apps/app_shim/BUILD.gn
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
+import("//extensions/buildflags/buildflags.gni")
|
||||
|
||||
# This is the part of the Chrome browser process responsible for launching and
|
||||
# communicating with app_shim processes on Mac.
|
||||
@@ -14,8 +15,6 @@ source_set("app_shim") {
|
||||
"app_shim_host_manager_mac.mm",
|
||||
"apps_page_shim_handler.h",
|
||||
"apps_page_shim_handler.mm",
|
||||
- "extension_app_shim_handler_mac.cc",
|
||||
- "extension_app_shim_handler_mac.h",
|
||||
"unix_domain_socket_acceptor.cc",
|
||||
"unix_domain_socket_acceptor.h",
|
||||
]
|
||||
@@ -24,7 +23,16 @@ source_set("app_shim") {
|
||||
"//chrome/common:mojo_bindings",
|
||||
"//content/public/browser",
|
||||
"//content/public/common",
|
||||
- "//extensions/browser",
|
||||
- "//extensions/common",
|
||||
]
|
||||
+
|
||||
+ if (enable_extensions) {
|
||||
+ sources += [
|
||||
+ "extension_app_shim_handler_mac.cc",
|
||||
+ "extension_app_shim_handler_mac.h",
|
||||
+ ]
|
||||
+ deps += [
|
||||
+ "//extensions/browser",
|
||||
+ "//extensions/common",
|
||||
+ ]
|
||||
+ }
|
||||
}
|
||||
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
|
||||
index 1b14d628a63b..00401612c191 100644
|
||||
--- a/chrome/browser/ui/BUILD.gn
|
||||
+++ b/chrome/browser/ui/BUILD.gn
|
||||
@@ -2570,7 +2570,10 @@ split_static_library("ui") {
|
||||
"views/tabs/window_finder_mac.mm",
|
||||
]
|
||||
|
||||
- deps += [ "//extensions/components/native_app_window" ]
|
||||
+
|
||||
+ if (enable_extensions) {
|
||||
+ deps += [ "//extensions/components/native_app_window" ]
|
||||
+ }
|
||||
|
||||
# Truly cocoa-browser-specific sources. These are secondary UI pieces that
|
||||
# are obsolete before mac_views_browser will ever ship, so they aren't
|
||||
--
|
||||
2.17.0
|
|
@ -0,0 +1,164 @@
|
|||
diff --git a/chrome/browser/speech/tts_controller_impl.cc b/chrome/browser/speech/tts_controller_impl.cc
|
||||
index 2ca56c3a247e..f58e33b3c019 100644
|
||||
--- a/chrome/browser/speech/tts_controller_impl.cc
|
||||
+++ b/chrome/browser/speech/tts_controller_impl.cc
|
||||
@@ -624,12 +624,14 @@ const PrefService* TtsControllerImpl::GetPrefService(
|
||||
const Utterance* utterance) {
|
||||
const PrefService* prefs = nullptr;
|
||||
// The utterance->browser_context() is null in tests.
|
||||
+#if 0
|
||||
if (utterance->browser_context()) {
|
||||
const Profile* profile =
|
||||
Profile::FromBrowserContext(utterance->browser_context());
|
||||
if (profile)
|
||||
prefs = profile->GetPrefs();
|
||||
}
|
||||
+#endif
|
||||
return prefs;
|
||||
}
|
||||
|
||||
diff --git a/chrome/browser/speech/tts_message_filter.cc b/chrome/browser/speech/tts_message_filter.cc
|
||||
index 013c7a9c60f9..73a244a726e3 100644
|
||||
--- a/chrome/browser/speech/tts_message_filter.cc
|
||||
+++ b/chrome/browser/speech/tts_message_filter.cc
|
||||
@@ -9,14 +9,40 @@
|
||||
#include "base/bind.h"
|
||||
#include "base/logging.h"
|
||||
#include "chrome/browser/chrome_notification_types.h"
|
||||
+#if 0
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
+#endif
|
||||
#include "chrome/common/tts_messages.h"
|
||||
+#include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/notification_service.h"
|
||||
#include "content/public/browser/render_process_host.h"
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
+namespace {
|
||||
+
|
||||
+class TtsMessageFilterShutdownNotifierFactory
|
||||
+ : public BrowserContextKeyedServiceShutdownNotifierFactory {
|
||||
+ public:
|
||||
+ static TtsMessageFilterShutdownNotifierFactory* GetInstance() {
|
||||
+ return base::Singleton<TtsMessageFilterShutdownNotifierFactory>::get();
|
||||
+ }
|
||||
+
|
||||
+ private:
|
||||
+ friend struct base::DefaultSingletonTraits<
|
||||
+ TtsMessageFilterShutdownNotifierFactory>;
|
||||
+
|
||||
+ TtsMessageFilterShutdownNotifierFactory()
|
||||
+ : BrowserContextKeyedServiceShutdownNotifierFactory("TtsMessageFilter") {}
|
||||
+
|
||||
+ ~TtsMessageFilterShutdownNotifierFactory() override {}
|
||||
+
|
||||
+ DISALLOW_COPY_AND_ASSIGN(TtsMessageFilterShutdownNotifierFactory);
|
||||
+};
|
||||
+
|
||||
+} // namespace
|
||||
+
|
||||
TtsMessageFilter::TtsMessageFilter(content::BrowserContext* browser_context)
|
||||
: BrowserMessageFilter(TtsMsgStart),
|
||||
browser_context_(browser_context),
|
||||
@@ -24,28 +50,27 @@ TtsMessageFilter::TtsMessageFilter(content::BrowserContext* browser_context)
|
||||
CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
TtsController::GetInstance()->AddVoicesChangedDelegate(this);
|
||||
|
||||
- // TODO(dmazzoni): make it so that we can listen for a BrowserContext
|
||||
- // being destroyed rather than a Profile. http://crbug.com/444668
|
||||
- Profile* profile = Profile::FromBrowserContext(browser_context);
|
||||
- notification_registrar_.Add(this,
|
||||
- chrome::NOTIFICATION_PROFILE_DESTROYED,
|
||||
- content::Source<Profile>(profile));
|
||||
+ browser_context_shutdown_notifier_ =
|
||||
+ TtsMessageFilterShutdownNotifierFactory::GetInstance()
|
||||
+ ->Get(browser_context)
|
||||
+ ->Subscribe(base::Bind(&TtsMessageFilter::BrowserContextDestroyed,
|
||||
+ base::RetainedRef(this)));
|
||||
|
||||
// Balanced in OnChannelClosingInUIThread() to keep the ref-count be non-zero
|
||||
// until all pointers to this class are invalidated.
|
||||
AddRef();
|
||||
}
|
||||
|
||||
-void TtsMessageFilter::OverrideThreadForMessage(
|
||||
- const IPC::Message& message, BrowserThread::ID* thread) {
|
||||
+void TtsMessageFilter::OverrideThreadForMessage(const IPC::Message& message,
|
||||
+ BrowserThread::ID* thread) {
|
||||
switch (message.type()) {
|
||||
- case TtsHostMsg_InitializeVoiceList::ID:
|
||||
- case TtsHostMsg_Speak::ID:
|
||||
- case TtsHostMsg_Pause::ID:
|
||||
- case TtsHostMsg_Resume::ID:
|
||||
- case TtsHostMsg_Cancel::ID:
|
||||
- *thread = BrowserThread::UI;
|
||||
- break;
|
||||
+ case TtsHostMsg_InitializeVoiceList::ID:
|
||||
+ case TtsHostMsg_Speak::ID:
|
||||
+ case TtsHostMsg_Pause::ID:
|
||||
+ case TtsHostMsg_Resume::ID:
|
||||
+ case TtsHostMsg_Cancel::ID:
|
||||
+ *thread = BrowserThread::UI;
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,10 +232,8 @@ void TtsMessageFilter::Cleanup() {
|
||||
TtsController::GetInstance()->RemoveUtteranceEventDelegate(this);
|
||||
}
|
||||
|
||||
-void TtsMessageFilter::Observe(
|
||||
- int type,
|
||||
- const content::NotificationSource& source,
|
||||
- const content::NotificationDetails& details) {
|
||||
+void TtsMessageFilter::BrowserContextDestroyed() {
|
||||
+ CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
||||
browser_context_ = nullptr;
|
||||
- notification_registrar_.RemoveAll();
|
||||
+ browser_context_shutdown_notifier_.reset();
|
||||
}
|
||||
diff --git a/chrome/browser/speech/tts_message_filter.h b/chrome/browser/speech/tts_message_filter.h
|
||||
index cc9e2806b5c3..d21fb42f1aca 100644
|
||||
--- a/chrome/browser/speech/tts_message_filter.h
|
||||
+++ b/chrome/browser/speech/tts_message_filter.h
|
||||
@@ -9,10 +9,9 @@
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "chrome/browser/speech/tts_controller.h"
|
||||
+#include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
|
||||
#include "content/public/browser/browser_message_filter.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
-#include "content/public/browser/notification_observer.h"
|
||||
-#include "content/public/browser/notification_registrar.h"
|
||||
|
||||
namespace content {
|
||||
class BrowserContext;
|
||||
@@ -22,7 +21,6 @@ struct TtsUtteranceRequest;
|
||||
|
||||
class TtsMessageFilter
|
||||
: public content::BrowserMessageFilter,
|
||||
- public content::NotificationObserver,
|
||||
public UtteranceEventDelegate,
|
||||
public VoicesChangedDelegate {
|
||||
public:
|
||||
@@ -64,15 +62,13 @@ class TtsMessageFilter
|
||||
// about to be deleted.
|
||||
bool Valid();
|
||||
|
||||
- // content::NotificationObserver implementation.
|
||||
- void Observe(int type,
|
||||
- const content::NotificationSource& source,
|
||||
- const content::NotificationDetails& details) override;
|
||||
+ void BrowserContextDestroyed();
|
||||
|
||||
+ std::unique_ptr<KeyedServiceShutdownNotifier::Subscription>
|
||||
+ browser_context_shutdown_notifier_;
|
||||
content::BrowserContext* browser_context_;
|
||||
mutable base::Lock mutex_;
|
||||
mutable bool valid_;
|
||||
- content::NotificationRegistrar notification_registrar_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TtsMessageFilter);
|
||||
};
|
|
@ -1,4 +1,6 @@
|
|||
const assert = require('assert')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const fs = require('fs')
|
||||
const http = require('http')
|
||||
const path = require('path')
|
||||
|
@ -12,6 +14,9 @@ const { app, BrowserWindow, ipcMain, protocol, session, webContents } = remote
|
|||
const isCI = remote.getGlobal('isCi')
|
||||
const features = process.atomBinding('features')
|
||||
|
||||
const { expect } = chai
|
||||
chai.use(dirtyChai)
|
||||
|
||||
/* Most of the APIs here don't use standard callbacks */
|
||||
/* eslint-disable standard/no-callback-literal */
|
||||
|
||||
|
@ -1269,4 +1274,41 @@ describe('chromium feature', () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('SpeechSynthesis', () => {
|
||||
before(function () {
|
||||
if (isCI || !features.isTtsEnabled()) {
|
||||
this.skip()
|
||||
}
|
||||
})
|
||||
|
||||
it('should emit lifecycle events', async () => {
|
||||
const sentence = `long sentence which will take at least a few seconds to
|
||||
utter so that it's possible to pause and resume before the end`
|
||||
const utter = new SpeechSynthesisUtterance(sentence)
|
||||
// Create a dummy utterence so that speech synthesis state
|
||||
// is initialized for later calls.
|
||||
speechSynthesis.speak(new SpeechSynthesisUtterance())
|
||||
speechSynthesis.cancel()
|
||||
speechSynthesis.speak(utter)
|
||||
// paused state after speak()
|
||||
expect(speechSynthesis.paused).to.be.false()
|
||||
await new Promise((resolve) => { utter.onstart = resolve })
|
||||
// paused state after start event
|
||||
expect(speechSynthesis.paused).to.be.false()
|
||||
|
||||
speechSynthesis.pause()
|
||||
// paused state changes async, right before the pause event
|
||||
expect(speechSynthesis.paused).to.be.false()
|
||||
await new Promise((resolve) => { utter.onpause = resolve })
|
||||
expect(speechSynthesis.paused).to.be.true()
|
||||
|
||||
speechSynthesis.resume()
|
||||
await new Promise((resolve) => { utter.onresume = resolve })
|
||||
// paused state after resume event
|
||||
expect(speechSynthesis.paused).to.be.false()
|
||||
|
||||
await new Promise((resolve) => { utter.onend = resolve })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Загрузка…
Ссылка в новой задаче