diff options
-rw-r--r-- | chrome/browser/speech/speech_input_manager.cc | 79 | ||||
-rw-r--r-- | chrome/browser/speech/speech_recognition_request.cc | 9 | ||||
-rw-r--r-- | chrome/browser/speech/speech_recognition_request.h | 1 | ||||
-rw-r--r-- | chrome/browser/speech/speech_recognition_request_unittest.cc | 3 | ||||
-rw-r--r-- | chrome/browser/speech/speech_recognizer.cc | 6 | ||||
-rw-r--r-- | chrome/browser/speech/speech_recognizer.h | 5 | ||||
-rw-r--r-- | chrome/browser/speech/speech_recognizer_unittest.cc | 2 | ||||
-rw-r--r-- | chrome/chrome_dll.gypi | 1 | ||||
-rw-r--r-- | chrome/installer/util/wmi.cc | 40 | ||||
-rw-r--r-- | chrome/installer/util/wmi.h | 9 | ||||
-rw-r--r-- | media/audio/audio_manager.h | 5 | ||||
-rw-r--r-- | media/audio/audio_manager_base.cc | 4 | ||||
-rw-r--r-- | media/audio/audio_manager_base.h | 2 | ||||
-rw-r--r-- | media/audio/win/audio_manager_win.cc | 116 | ||||
-rw-r--r-- | media/audio/win/audio_manager_win.h | 1 |
15 files changed, 273 insertions, 10 deletions
diff --git a/chrome/browser/speech/speech_input_manager.cc b/chrome/browser/speech/speech_input_manager.cc index 181234e..7149223 100644 --- a/chrome/browser/speech/speech_input_manager.cc +++ b/chrome/browser/speech/speech_input_manager.cc @@ -5,18 +5,82 @@ #include "chrome/browser/speech/speech_input_manager.h" #include "app/l10n_util.h" +#include "base/lock.h" #include "base/ref_counted.h" #include "base/singleton.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_thread.h" +#include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/speech/speech_input_bubble_controller.h" #include "chrome/browser/speech/speech_recognizer.h" #include "chrome/browser/tab_contents/infobar_delegate.h" #include "chrome/browser/tab_contents/tab_contents.h" #include "chrome/browser/tab_contents/tab_util.h" +#include "chrome/common/pref_names.h" #include "grit/generated_resources.h" #include "media/audio/audio_manager.h" #include <map> +#if defined(OS_WIN) +#include "chrome/installer/util/wmi.h" +#endif + +namespace { + +// Asynchronously fetches the PC and audio hardware/driver info on windows if +// the user has opted into UMA. This information is sent with speech input +// requests to the server for identifying and improving quality issues with +// specific device configurations. +class HardwareInfo : public base::RefCountedThreadSafe<HardwareInfo> { + public: + HardwareInfo() {} + +#if defined(OS_WIN) + void Refresh() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + // UMA opt-in can be checked only from the UI thread, so switch to that. + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, &HardwareInfo::CheckUMAAndGetHardwareInfo)); + } + + void CheckUMAAndGetHardwareInfo() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (g_browser_process->local_state()->GetBoolean( + prefs::kMetricsReportingEnabled)) { + // Access potentially slow OS calls from the FILE thread. + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, &HardwareInfo::GetHardwareInfo)); + } + } + + void GetHardwareInfo() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + AutoLock lock(lock_); + value_ = UTF16ToUTF8( + installer::WMIComputerSystem::GetModel() + L"|" + + AudioManager::GetAudioManager()->GetAudioInputDeviceModel()); + } + + std::string value() { + AutoLock lock(lock_); + return value_; + } + + private: + Lock lock_; + std::string value_; + +#else // defined(OS_WIN) + void Refresh() {} + std::string value() { return std::string(); } +#endif // defined(OS_WIN) + + DISALLOW_COPY_AND_ASSIGN(HardwareInfo); +}; + +} + namespace speech_input { class SpeechInputManagerImpl : public SpeechInputManager, @@ -75,6 +139,7 @@ class SpeechInputManagerImpl : public SpeechInputManager, SpeechRecognizerMap requests_; int recording_caller_id_; scoped_refptr<SpeechInputBubbleController> bubble_controller_; + scoped_refptr<HardwareInfo> hardware_info_; }; SpeechInputManager* SpeechInputManager::Get() { @@ -114,10 +179,22 @@ void SpeechInputManagerImpl::StartRecognition( bubble_controller_->CreateBubble(caller_id, render_process_id, render_view_id, element_rect); + if (!hardware_info_.get()) { + hardware_info_ = new HardwareInfo(); + // Since hardware info is optional with speech input requests, we start an + // asynchronous fetch here and move on with recording audio. This first + // speech input request would send an empty string for hardware info and + // subsequent requests may have the hardware info available if the fetch + // completed before them. This way we don't end up stalling the user with + // a long wait and disk seeks when they click on a UI element and start + // speaking. + hardware_info_->Refresh(); + } + SpeechInputRequest* request = &requests_[caller_id]; request->delegate = delegate; request->recognizer = new SpeechRecognizer(this, caller_id, language, - grammar); + grammar, hardware_info_->value()); request->is_active = false; StartRecognitionForRequest(caller_id); diff --git a/chrome/browser/speech/speech_recognition_request.cc b/chrome/browser/speech/speech_recognition_request.cc index b9ee40b..63a6c92 100644 --- a/chrome/browser/speech/speech_recognition_request.cc +++ b/chrome/browser/speech/speech_recognition_request.cc @@ -9,7 +9,6 @@ #include "app/l10n_util.h" #include "base/json/json_reader.h" #include "base/string_util.h" -#include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/net/url_request_context_getter.h" #include "net/base/escape.h" @@ -20,7 +19,7 @@ namespace { const char* const kDefaultSpeechRecognitionUrl = - "http://www.google.com/speech-api/v1/recognize?client=chromium&"; + "https://www.google.com/speech-api/v1/recognize?client=chromium&"; const char* const kHypothesesString = "hypotheses"; const char* const kUtteranceString = "utterance"; const char* const kConfidenceString = "confidence"; @@ -123,6 +122,7 @@ SpeechRecognitionRequest::~SpeechRecognitionRequest() {} bool SpeechRecognitionRequest::Send(const std::string& language, const std::string& grammar, + const std::string& hardware_info, const std::string& content_type, const std::string& audio_data) { DCHECK(!url_fetcher_.get()); @@ -145,7 +145,10 @@ bool SpeechRecognitionRequest::Send(const std::string& language, parts.push_back("lang=" + EscapeQueryParamValue(lang_param, true)); if (!grammar.empty()) - parts.push_back("grammar=" + EscapeQueryParamValue(grammar, true)); + parts.push_back("lm=" + EscapeQueryParamValue(grammar, true)); + if (!hardware_info.empty()) + parts.push_back("xhw=" + EscapeQueryParamValue(hardware_info, true)); + GURL url(std::string(kDefaultSpeechRecognitionUrl) + JoinString(parts, '&')); url_fetcher_.reset(URLFetcher::Create(url_fetcher_id_for_tests, diff --git a/chrome/browser/speech/speech_recognition_request.h b/chrome/browser/speech/speech_recognition_request.h index 89beed1..585b0a4 100644 --- a/chrome/browser/speech/speech_recognition_request.h +++ b/chrome/browser/speech/speech_recognition_request.h @@ -46,6 +46,7 @@ class SpeechRecognitionRequest : public URLFetcher::Delegate { // previous request has completed. bool Send(const std::string& language, const std::string& grammar, + const std::string& hardware_info, const std::string& content_type, const std::string& audio_data); diff --git a/chrome/browser/speech/speech_recognition_request_unittest.cc b/chrome/browser/speech/speech_recognition_request_unittest.cc index a87bba8..e328eb4 100644 --- a/chrome/browser/speech/speech_recognition_request_unittest.cc +++ b/chrome/browser/speech/speech_recognition_request_unittest.cc @@ -45,7 +45,8 @@ class SpeechRecognitionRequestTest : public SpeechRecognitionRequestDelegate, void SpeechRecognitionRequestTest::CreateAndTestRequest( bool success, const std::string& http_response) { SpeechRecognitionRequest request(NULL, this); - request.Send(std::string(), std::string(), std::string(), std::string()); + request.Send(std::string(), std::string(), std::string(), std::string(), + std::string()); TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0); ASSERT_TRUE(fetcher); URLRequestStatus status; diff --git a/chrome/browser/speech/speech_recognizer.cc b/chrome/browser/speech/speech_recognizer.cc index d56013a..4905914 100644 --- a/chrome/browser/speech/speech_recognizer.cc +++ b/chrome/browser/speech/speech_recognizer.cc @@ -110,11 +110,13 @@ void SpeexEncoder::Encode(const short* samples, SpeechRecognizer::SpeechRecognizer(Delegate* delegate, int caller_id, const std::string& language, - const std::string& grammar) + const std::string& grammar, + const std::string& hardware_info) : delegate_(delegate), caller_id_(caller_id), language_(language), grammar_(grammar), + hardware_info_(hardware_info), encoder_(new SpeexEncoder()), endpointer_(kAudioSampleRate), num_samples_recorded_(0), @@ -216,7 +218,7 @@ void SpeechRecognizer::StopRecording() { DCHECK(!request_.get()); request_.reset(new SpeechRecognitionRequest( Profile::GetDefaultRequestContext(), this)); - request_->Send(language_, grammar_, kContentTypeSpeex, data); + request_->Send(language_, grammar_, hardware_info_, kContentTypeSpeex, data); ReleaseAudioBuffers(); // No need to keep the audio anymore. } diff --git a/chrome/browser/speech/speech_recognizer.h b/chrome/browser/speech/speech_recognizer.h index 61226f4..3b0f707 100644 --- a/chrome/browser/speech/speech_recognizer.h +++ b/chrome/browser/speech/speech_recognizer.h @@ -76,7 +76,8 @@ class SpeechRecognizer SpeechRecognizer(Delegate* delegate, int caller_id, const std::string& language, - const std::string& grammar); + const std::string& grammar, + const std::string& hardware_info); ~SpeechRecognizer(); // Starts audio recording and does recognition after recording ends. The same @@ -112,6 +113,7 @@ class SpeechRecognizer private: void ReleaseAudioBuffers(); void InformErrorAndCancelRecognition(ErrorCode error); + void SendRecordedAudioToServer(); void HandleOnError(int error_code); // Handles OnError in the IO thread. @@ -122,6 +124,7 @@ class SpeechRecognizer int caller_id_; std::string language_; std::string grammar_; + std::string hardware_info_; // Buffer holding the recorded audio. Owns the strings inside the list. typedef std::list<std::string*> AudioBufferQueue; diff --git a/chrome/browser/speech/speech_recognizer_unittest.cc b/chrome/browser/speech/speech_recognizer_unittest.cc index d71876f..44967b1 100644 --- a/chrome/browser/speech/speech_recognizer_unittest.cc +++ b/chrome/browser/speech/speech_recognizer_unittest.cc @@ -24,7 +24,7 @@ class SpeechRecognizerTest : public SpeechRecognizerDelegate, : io_thread_(BrowserThread::IO, &message_loop_), ALLOW_THIS_IN_INITIALIZER_LIST( recognizer_(new SpeechRecognizer(this, 1, std::string(), - std::string()))), + std::string(), std::string()))), recording_complete_(false), recognition_complete_(false), result_received_(false), diff --git a/chrome/chrome_dll.gypi b/chrome/chrome_dll.gypi index 3fbacbb..36b9af0 100644 --- a/chrome/chrome_dll.gypi +++ b/chrome/chrome_dll.gypi @@ -35,6 +35,7 @@ 'urlmon.dll', 'imm32.dll', 'iphlpapi.dll', + 'setupapi.dll', ], # Set /SUBSYSTEM:WINDOWS for chrome.dll (for consistency). 'SubSystem': '2', diff --git a/chrome/installer/util/wmi.cc b/chrome/installer/util/wmi.cc index fca2076..e873277 100644 --- a/chrome/installer/util/wmi.cc +++ b/chrome/installer/util/wmi.cc @@ -9,6 +9,7 @@ #include "base/basictypes.h" #include "base/win/scoped_bstr.h" #include "base/win/scoped_comptr.h" +#include "base/win/scoped_variant.h" #pragma comment(lib, "wbemuuid.lib") @@ -150,4 +151,43 @@ bool WMIProcess::Launch(const std::wstring& command_line, int* process_id) { return true; } +string16 WMIComputerSystem::GetModel() { + base::win::ScopedComPtr<IWbemServices> services; + if (!WMI::CreateLocalConnection(true, services.Receive())) + return string16(); + + base::win::ScopedBstr query_language(L"WQL"); + base::win::ScopedBstr query(L"SELECT * FROM Win32_ComputerSystem"); + base::win::ScopedComPtr<IEnumWbemClassObject> enumerator; + HRESULT hr = services->ExecQuery( + query_language, query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, + enumerator.Receive()); + if (FAILED(hr) || !enumerator) + return string16(); + + base::win::ScopedComPtr<IWbemClassObject> class_object; + ULONG items_returned = 0; + hr = enumerator->Next(WBEM_INFINITE, 1, class_object.Receive(), + &items_returned); + if (!items_returned) + return string16(); + + base::win::ScopedVariant manufacturer; + class_object->Get(L"Manufacturer", 0, manufacturer.Receive(), 0, 0); + base::win::ScopedVariant model; + class_object->Get(L"Model", 0, model.Receive(), 0, 0); + + string16 model_string; + if (manufacturer.type() == VT_BSTR) { + model_string = V_BSTR(&manufacturer); + if (model.type() == VT_BSTR) + model_string += L" "; + } + if (model.type() == VT_BSTR) + model_string += V_BSTR(&model); + + return model_string; +} + } // namespace installer diff --git a/chrome/installer/util/wmi.h b/chrome/installer/util/wmi.h index 5d0a1a9..03106dc 100644 --- a/chrome/installer/util/wmi.h +++ b/chrome/installer/util/wmi.h @@ -21,6 +21,7 @@ #define CHROME_INSTALLER_UTIL_WMI_H_ #pragma once +#include "base/string16.h" #include <string> #include <wbemidl.h> @@ -73,6 +74,14 @@ class WMIProcess { static bool Launch(const std::wstring& command_line, int* process_id); }; +// This class contains functionality of the WMI class 'Win32_ComputerSystem' +// more info: http://msdn.microsoft.com/en-us/library/aa394102(VS.85).aspx +class WMIComputerSystem { + public: + // Returns a human readable string for the model/make of this computer. + static string16 GetModel(); +}; + } // namespace installer #endif // CHROME_INSTALLER_UTIL_WMI_H_ diff --git a/media/audio/audio_manager.h b/media/audio/audio_manager.h index 26f0acf..cba88a9 100644 --- a/media/audio/audio_manager.h +++ b/media/audio/audio_manager.h @@ -6,6 +6,7 @@ #define MEDIA_AUDIO_AUDIO_MANAGER_H_ #include "base/basictypes.h" +#include "base/string16.h" #include "media/audio/audio_parameters.h" class AudioInputStream; @@ -29,6 +30,10 @@ class AudioManager { // sample rates. virtual bool HasAudioInputDevices() = 0; + // Returns a human readable string for the model/make of the active audio + // input device for this computer. + virtual string16 GetAudioInputDeviceModel() = 0; + // Factory for all the supported stream formats. |params| defines parameters // of the audio stream to be created. // diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc index 7fa4761..95a47f1 100644 --- a/media/audio/audio_manager_base.cc +++ b/media/audio/audio_manager_base.cc @@ -13,6 +13,10 @@ void AudioManagerBase::Init() { initialized_ = audio_thread_.Start(); } +string16 AudioManagerBase::GetAudioInputDeviceModel() { + return string16(); +} + MessageLoop* AudioManagerBase::GetMessageLoop() { DCHECK(initialized_); return audio_thread_.message_loop(); diff --git a/media/audio/audio_manager_base.h b/media/audio/audio_manager_base.h index 6d01c26..9665982 100644 --- a/media/audio/audio_manager_base.h +++ b/media/audio/audio_manager_base.h @@ -17,6 +17,8 @@ class AudioManagerBase : public AudioManager { virtual MessageLoop* GetMessageLoop(); + virtual string16 GetAudioInputDeviceModel(); + protected: virtual ~AudioManagerBase() {} diff --git a/media/audio/win/audio_manager_win.cc b/media/audio/win/audio_manager_win.cc index a2fbd92..84dba63 100644 --- a/media/audio/win/audio_manager_win.cc +++ b/media/audio/win/audio_manager_win.cc @@ -5,9 +5,15 @@ #include "media/audio/audio_io.h" #include <windows.h> +#include <objbase.h> // This has to be before initguid.h +#include <initguid.h> #include <mmsystem.h> +#include <setupapi.h> #include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" #include "media/audio/fake_audio_input_stream.h" #include "media/audio/fake_audio_output_stream.h" #include "media/audio/win/audio_manager_win.h" @@ -15,6 +21,17 @@ #include "media/audio/win/waveout_output_win.h" #include "media/base/limits.h" +// Libraries required for the SetupAPI and Wbem APIs used here. +#pragma comment(lib, "setupapi.lib") + +// The following are defined in various DDK headers, and we (re)define them +// here to avoid adding the DDK as a chrome dependency. +#define DRV_QUERYDEVICEINTERFACE 0x80c +#define DRVM_MAPPER_PREFERRED_GET 0x2015 +#define DRV_QUERYDEVICEINTERFACESIZE 0x80d +DEFINE_GUID(AM_KSCATEGORY_AUDIO, 0x6994ad04, 0x93ef, 0x11d0, + 0xa3, 0xcc, 0x00, 0xa0, 0xc9, 0x22, 0x31, 0x96); + namespace { // Up to 8 channels can be passed to the driver. @@ -30,7 +47,45 @@ const int kWinMaxInputChannels = 2; // play. const int kNumInputBuffers = 3; -} // namespace. +// Returns a string containing the given device's description and installed +// driver version. +string16 GetDeviceAndDriverInfo(HDEVINFO device_info, + SP_DEVINFO_DATA* device_data) { + // Save the old install params setting and set a flag for the
+ // SetupDiBuildDriverInfoList below to return only the installed drivers.
+ SP_DEVINSTALL_PARAMS old_device_install_params;
+ old_device_install_params.cbSize = sizeof(old_device_install_params);
+ SetupDiGetDeviceInstallParams(device_info, device_data,
+ &old_device_install_params);
+ SP_DEVINSTALL_PARAMS device_install_params = old_device_install_params;
+ device_install_params.FlagsEx |= DI_FLAGSEX_INSTALLEDDRIVER;
+ SetupDiSetDeviceInstallParams(device_info, device_data,
+ &device_install_params);
+
+ SP_DRVINFO_DATA driver_data;
+ driver_data.cbSize = sizeof(driver_data);
+ string16 device_and_driver_info;
+ if (SetupDiBuildDriverInfoList(device_info, device_data,
+ SPDIT_COMPATDRIVER)) {
+ if (SetupDiEnumDriverInfo(device_info, device_data, SPDIT_COMPATDRIVER, 0,
+ &driver_data)) {
+ DWORDLONG version = driver_data.DriverVersion;
+ device_and_driver_info = string16(driver_data.Description) + L" v" +
+ base::IntToString16((version >> 48) & 0xffff) + L"." +
+ base::IntToString16((version >> 32) & 0xffff) + L"." +
+ base::IntToString16((version >> 16) & 0xffff) + L"." +
+ base::IntToString16(version & 0xffff);
+ }
+ SetupDiDestroyDriverInfoList(device_info, device_data, SPDIT_COMPATDRIVER);
+ }
+
+ SetupDiSetDeviceInstallParams(device_info, device_data,
+ &old_device_install_params);
+
+ return device_and_driver_info;
+} + +} // namespace bool AudioManagerWin::HasAudioOutputDevices() { return (::waveOutGetNumDevs() != 0); @@ -94,6 +149,65 @@ void AudioManagerWin::UnMuteAll() { AudioManagerWin::~AudioManagerWin() { } +string16 AudioManagerWin::GetAudioInputDeviceModel() { + // Get the default audio capture device and its device interface name. + DWORD device_id = 0;
+ waveInMessage(reinterpret_cast<HWAVEIN>(WAVE_MAPPER),
+ DRVM_MAPPER_PREFERRED_GET,
+ reinterpret_cast<DWORD_PTR>(&device_id), NULL);
+ ULONG device_interface_name_size = 0;
+ waveInMessage(reinterpret_cast<HWAVEIN>(device_id),
+ DRV_QUERYDEVICEINTERFACESIZE,
+ reinterpret_cast<DWORD_PTR>(&device_interface_name_size), 0);
+ string16 device_interface_name;
+ string16::value_type* name_ptr = WriteInto(&device_interface_name,
+ device_interface_name_size / sizeof(string16::value_type));
+ waveInMessage(reinterpret_cast<HWAVEIN>(device_id),
+ DRV_QUERYDEVICEINTERFACE,
+ reinterpret_cast<DWORD_PTR>(name_ptr),
+ static_cast<DWORD_PTR>(device_interface_name_size));
+
+ // Enumerate all audio devices and find the one matching the above device
+ // interface name.
+ HDEVINFO device_info = SetupDiGetClassDevs(
+ &AM_KSCATEGORY_AUDIO, 0, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
+ if (device_info == INVALID_HANDLE_VALUE)
+ return string16();
+
+ DWORD interface_index = 0;
+ SP_DEVICE_INTERFACE_DATA interface_data;
+ interface_data.cbSize = sizeof(interface_data);
+ while (SetupDiEnumDeviceInterfaces(device_info, 0, &AM_KSCATEGORY_AUDIO,
+ interface_index++, &interface_data)) {
+ // Query the size of the struct, allocate it and then query the data.
+ SP_DEVINFO_DATA device_data;
+ device_data.cbSize = sizeof(device_data);
+ DWORD interface_detail_size = 0;
+ SetupDiGetDeviceInterfaceDetail(device_info, &interface_data, 0, 0,
+ &interface_detail_size, &device_data);
+ if (!interface_detail_size)
+ continue;
+
+ scoped_array<char> interface_detail_buffer(new char[interface_detail_size]);
+ SP_DEVICE_INTERFACE_DETAIL_DATA* interface_detail =
+ reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA*>(
+ interface_detail_buffer.get());
+ interface_detail->cbSize = interface_detail_size;
+ if (!SetupDiGetDeviceInterfaceDetail(device_info, &interface_data,
+ interface_detail,
+ interface_detail_size, NULL,
+ &device_data))
+ return string16();
+
+ bool device_found = (device_interface_name == interface_detail->DevicePath);
+
+ if (device_found)
+ return GetDeviceAndDriverInfo(device_info, &device_data);
+ }
+
+ return string16();
+} + // static AudioManager* AudioManager::CreateAudioManager() { return new AudioManagerWin(); diff --git a/media/audio/win/audio_manager_win.h b/media/audio/win/audio_manager_win.h index 2ea4c8f..05c80e4 100644 --- a/media/audio/win/audio_manager_win.h +++ b/media/audio/win/audio_manager_win.h @@ -26,6 +26,7 @@ class AudioManagerWin : public AudioManagerBase { virtual AudioInputStream* MakeAudioInputStream(AudioParameters params); virtual void MuteAll(); virtual void UnMuteAll(); + virtual string16 GetAudioInputDeviceModel(); // Windows-only methods to free a stream created in MakeAudioStream. These // are called internally by the audio stream when it has been closed. |