Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

speech_recognition_request

- Renamed speech_recognition_request to google_one_shot_remote_engine
- Audio encoder moved here (previously was in speech_recognizer).
Rationale: Audio encoding is not a requirement of speech recognition. It is a need of the google_one_shot_remote_engine, due to its remote nature.

speech_recognition_engine
- Extracted the interface SpeechRecognitionEngine (and, accordingly, Delegate) from the former speech_recognition_request.
Rationale: google_one_shot_remote_engine is just one of the possible engines (yet currently the only one) that can be exploited to recognize speech.

speech_recognition_result
- Added SpeechRecognitionError, encapsulating the error code and further error details (for specializing audio errors, since chrome needs them).

speech_recognition_manager
- Minor adaptions to the new speech_recognizer.cc
- Replaced the SpeechRecognitionRequestDelegate interface with the brand new speech_recognition_event_listener

BUG=116954
TEST=none

Review URL: http://codereview.chromium.org/9663066

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@128896 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information...
commit 7527adf41d86050b32e0f67a93e0a6aae24ec22c 1 parent 5ce89bc
primiano@chromium.org authored
Showing with 556 additions and 340 deletions.
  1. +1 −0  chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
  2. +1 −0  chrome/browser/speech/speech_input_extension_apitest.cc
  3. +4 −3 chrome/browser/speech/speech_input_extension_manager.cc
  4. +3 −2 chrome/browser/speech/speech_input_extension_manager.h
  5. +129 −57 content/browser/speech/{speech_recognition_request.cc → google_one_shot_remote_engine.cc}
  6. +84 −0 content/browser/speech/google_one_shot_remote_engine.h
  7. +28 −18 ...nt/browser/speech/{speech_recognition_request_unittest.cc → google_one_shot_remote_engine_unittest.cc}
  8. +1 −0  content/browser/speech/speech_recognition_browsertest.cc
  9. +92 −0 content/browser/speech/speech_recognition_engine.h
  10. +26 −21 content/browser/speech/speech_recognition_manager_impl.cc
  11. +1 −1  content/browser/speech/speech_recognition_manager_impl.h
  12. +0 −90 content/browser/speech/speech_recognition_request.h
  13. +70 −66 content/browser/speech/speech_recognizer_impl.cc
  14. +32 −33 content/browser/speech/speech_recognizer_impl.h
  15. +20 −19 content/browser/speech/speech_recognizer_impl_unittest.cc
  16. +1 −1  content/common/speech_recognition_messages.h
  17. +3 −2 content/content_browser.gypi
  18. +1 −0  content/content_common.gypi
  19. +1 −1  content/content_tests.gypi
  20. +4 −2 content/public/browser/speech_recognition_event_listener.h
  21. +1 −1  content/public/browser/speech_recognition_manager_delegate.h
  22. +1 −1  content/public/browser/speech_recognizer.h
  23. +51 −0 content/public/common/speech_recognition_error.h
  24. +1 −2  content/public/common/speech_recognition_result.cc
  25. +0 −20 content/public/common/speech_recognition_result.h
View
1  chrome/browser/speech/chrome_speech_recognition_manager_delegate.cc
@@ -18,6 +18,7 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/speech_recognition_manager.h"
+#include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
View
1  chrome/browser/speech/speech_input_extension_apitest.cc
@@ -13,6 +13,7 @@
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
#include "content/public/browser/speech_recognition_event_listener.h"
+#include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h"
#include "testing/gtest/include/gtest/gtest.h"
View
7 chrome/browser/speech/speech_input_extension_manager.cc
@@ -24,6 +24,7 @@
#include "content/public/browser/notification_service.h"
#include "content/public/browser/speech_recognition_manager.h"
#include "content/public/browser/speech_recognizer.h"
+#include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h"
using content::BrowserThread;
@@ -340,10 +341,10 @@ void SpeechInputExtensionManager::DidStartReceivingAudioOnUIThread() {
}
void SpeechInputExtensionManager::OnRecognitionError(
- int caller_id, const content::SpeechRecognitionErrorCode& error) {
+ int caller_id, const content::SpeechRecognitionError& error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(caller_id, kSpeechCallerId);
- VLOG(1) << "OnRecognitionError: " << error;
+ VLOG(1) << "OnRecognitionError: " << error.code;
base::AutoLock auto_lock(state_lock_);
if (state_ == kShutdown)
@@ -355,7 +356,7 @@ void SpeechInputExtensionManager::OnRecognitionError(
std::string event_error_code;
bool report_to_event = true;
- switch (error) {
+ switch (error.code) {
case content::SPEECH_RECOGNITION_ERROR_NONE:
break;
View
5 chrome/browser/speech/speech_input_extension_manager.h
@@ -21,6 +21,8 @@ class SpeechInputExtensionNotification;
namespace content {
class NotificationRegistrar;
+struct SpeechRecognitionError;
+struct SpeechRecognitionResult;
class SpeechRecognizer;
}
@@ -124,8 +126,7 @@ class SpeechInputExtensionManager
virtual void OnRecognitionResult(
int caller_id, const content::SpeechRecognitionResult& result) OVERRIDE;
virtual void OnRecognitionError(
- int caller_id,
- const content::SpeechRecognitionErrorCode& error) OVERRIDE;
+ int caller_id, const content::SpeechRecognitionError& error) OVERRIDE;
virtual void OnAudioLevelsChange(int caller_id, float volume,
float noise_volume) OVERRIDE;
virtual void OnRecognitionEnd(int caller_id) OVERRIDE;
View
186 content/browser/speech/speech_recognition_request.cc → ...t/browser/speech/google_one_shot_remote_engine.cc
@@ -2,16 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "content/browser/speech/speech_recognition_request.h"
+#include "content/browser/speech/google_one_shot_remote_engine.h"
#include <vector>
#include "base/json/json_reader.h"
+#include "base/memory/scoped_ptr.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/values.h"
#include "content/browser/speech/audio_buffer.h"
#include "content/common/net/url_fetcher_impl.h"
+#include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h"
#include "net/base/escape.h"
#include "net/base/load_flags.h"
@@ -19,6 +21,10 @@
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_status.h"
+using content::SpeechRecognitionError;
+using content::SpeechRecognitionHypothesis;
+using content::SpeechRecognitionResult;
+
namespace {
const char* const kDefaultSpeechRecognitionUrl =
@@ -27,13 +33,20 @@ const char* const kStatusString = "status";
const char* const kHypothesesString = "hypotheses";
const char* const kUtteranceString = "utterance";
const char* const kConfidenceString = "confidence";
-
+const int kWebServiceStatusNoError = 0;
+const int kWebServiceStatusNoSpeech = 4;
+const int kWebServiceStatusNoMatch = 5;
+const int kDefaultConfigSampleRate = 8000;
+const int kDefaultConfigBitsPerSample = 16;
+const speech::AudioEncoder::Codec kDefaultAudioCodec =
+ speech::AudioEncoder::CODEC_FLAC;
// TODO(satish): Remove this hardcoded value once the page is allowed to
// set this via an attribute.
const int kMaxResults = 6;
bool ParseServerResponse(const std::string& response_body,
- content::SpeechRecognitionResult* result) {
+ SpeechRecognitionResult* result,
+ SpeechRecognitionError* error) {
if (response_body.empty()) {
LOG(WARNING) << "ParseServerResponse: Response was empty.";
return false;
@@ -67,19 +80,21 @@ bool ParseServerResponse(const std::string& response_body,
// Process the status.
switch (status) {
- case content::SPEECH_RECOGNITION_ERROR_NONE:
- case content::SPEECH_RECOGNITION_ERROR_NO_SPEECH:
- case content::SPEECH_RECOGNITION_ERROR_NO_MATCH:
- break;
-
- default:
- // Other status codes should not be returned by the server.
- VLOG(1) << "ParseServerResponse: unexpected status code " << status;
- return false;
+ case kWebServiceStatusNoError:
+ break;
+ case kWebServiceStatusNoSpeech:
+ error->code = content::SPEECH_RECOGNITION_ERROR_NO_SPEECH;
+ return false;
+ case kWebServiceStatusNoMatch:
+ error->code = content::SPEECH_RECOGNITION_ERROR_NO_MATCH;
+ return false;
+ default:
+ error->code = content::SPEECH_RECOGNITION_ERROR_NETWORK;
+ // Other status codes should not be returned by the server.
+ VLOG(1) << "ParseServerResponse: unexpected status code " << status;
+ return false;
}
- result->error = static_cast<content::SpeechRecognitionErrorCode>(status);
-
// Get the hypotheses.
Value* hypotheses_value = NULL;
if (!response_object->Get(kHypothesesString, &hypotheses_value)) {
@@ -96,6 +111,8 @@ bool ParseServerResponse(const std::string& response_body,
const ListValue* hypotheses_list = static_cast<ListValue*>(hypotheses_value);
+ // For now we support only single shot recognition, so we are giving only a
+ // final result, consisting of one fragment (with one or more hypotheses).
size_t index = 0;
for (; index < hypotheses_list->GetSize(); ++index) {
Value* hypothesis = NULL;
@@ -113,6 +130,7 @@ bool ParseServerResponse(const std::string& response_body,
const DictionaryValue* hypothesis_value =
static_cast<DictionaryValue*>(hypothesis);
string16 utterance;
+
if (!hypothesis_value->GetString(kUtteranceString, &utterance)) {
LOG(WARNING) << "ParseServerResponse: Missing utterance value.";
break;
@@ -121,16 +139,14 @@ bool ParseServerResponse(const std::string& response_body,
// It is not an error if the 'confidence' field is missing.
double confidence = 0.0;
hypothesis_value->GetDouble(kConfidenceString, &confidence);
-
- result->hypotheses.push_back(content::SpeechRecognitionHypothesis(
- utterance, confidence));
+ result->hypotheses.push_back(SpeechRecognitionHypothesis(utterance,
+ confidence));
}
if (index < hypotheses_list->GetSize()) {
result->hypotheses.clear();
return false;
}
-
return true;
}
@@ -138,28 +154,34 @@ bool ParseServerResponse(const std::string& response_body,
namespace speech {
-int SpeechRecognitionRequest::url_fetcher_id_for_tests = 0;
+const int GoogleOneShotRemoteEngine::kAudioPacketIntervalMs = 100;
+int GoogleOneShotRemoteEngine::url_fetcher_id_for_tests = 0;
-SpeechRecognitionRequest::SpeechRecognitionRequest(
- net::URLRequestContextGetter* context, Delegate* delegate)
- : url_context_(context),
- delegate_(delegate) {
- DCHECK(delegate);
+GoogleOneShotRemoteEngineConfig::GoogleOneShotRemoteEngineConfig()
+ : filter_profanities(false),
+ audio_sample_rate(kDefaultConfigSampleRate),
+ audio_num_bits_per_sample(kDefaultConfigBitsPerSample) {
}
-SpeechRecognitionRequest::~SpeechRecognitionRequest() {}
+GoogleOneShotRemoteEngineConfig::~GoogleOneShotRemoteEngineConfig() {}
-void SpeechRecognitionRequest::Start(const std::string& language,
- const std::string& grammar,
- bool filter_profanities,
- const std::string& hardware_info,
- const std::string& origin_url,
- const std::string& content_type) {
- DCHECK(!url_fetcher_.get());
+GoogleOneShotRemoteEngine::GoogleOneShotRemoteEngine(
+ net::URLRequestContextGetter* context)
+ : url_context_(context) {
+}
- std::vector<std::string> parts;
+GoogleOneShotRemoteEngine::~GoogleOneShotRemoteEngine() {}
+
+void GoogleOneShotRemoteEngine::SetConfig(
+ const GoogleOneShotRemoteEngineConfig& config) {
+ config_ = config;
+}
+
+void GoogleOneShotRemoteEngine::StartRecognition() {
+ DCHECK(delegate());
+ DCHECK(!url_fetcher_.get());
+ std::string lang_param = config_.language;
- std::string lang_param = language;
if (lang_param.empty() && url_context_) {
// If no language is provided then we use the first from the accepted
// language list. If this list is empty then it defaults to "en-US".
@@ -171,58 +193,108 @@ void SpeechRecognitionRequest::Start(const std::string& language,
size_t separator = accepted_language_list.find_first_of(",;");
lang_param = accepted_language_list.substr(0, separator);
}
+
if (lang_param.empty())
lang_param = "en-US";
+
+ std::vector<std::string> parts;
parts.push_back("lang=" + net::EscapeQueryParamValue(lang_param, true));
- if (!grammar.empty())
- parts.push_back("lm=" + net::EscapeQueryParamValue(grammar, true));
- if (!hardware_info.empty())
- parts.push_back("xhw=" + net::EscapeQueryParamValue(hardware_info, true));
+ if (!config_.grammar.empty())
+ parts.push_back("lm=" + net::EscapeQueryParamValue(config_.grammar, true));
+
+ if (!config_.hardware_info.empty())
+ parts.push_back("xhw=" + net::EscapeQueryParamValue(config_.hardware_info,
+ true));
parts.push_back("maxresults=" + base::IntToString(kMaxResults));
- parts.push_back(filter_profanities ? "pfilter=2" : "pfilter=0");
+ parts.push_back(config_.filter_profanities ? "pfilter=2" : "pfilter=0");
GURL url(std::string(kDefaultSpeechRecognitionUrl) + JoinString(parts, '&'));
+ encoder_.reset(AudioEncoder::Create(kDefaultAudioCodec,
+ config_.audio_sample_rate,
+ config_.audio_num_bits_per_sample));
+ DCHECK(encoder_.get());
url_fetcher_.reset(URLFetcherImpl::Create(url_fetcher_id_for_tests,
url,
URLFetcherImpl::POST,
this));
- url_fetcher_->SetChunkedUpload(content_type);
+ url_fetcher_->SetChunkedUpload(encoder_->mime_type());
url_fetcher_->SetRequestContext(url_context_);
- url_fetcher_->SetReferrer(origin_url);
+ url_fetcher_->SetReferrer(config_.origin_url);
// The speech recognition API does not require user identification as part
// of requests, so we don't send cookies or auth data for these requests to
// prevent any accidental connection between users who are logged into the
// domain for other services (e.g. bookmark sync) with the speech requests.
- url_fetcher_->SetLoadFlags(
- net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES |
- net::LOAD_DO_NOT_SEND_AUTH_DATA);
+ url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES |
+ net::LOAD_DO_NOT_SEND_COOKIES |
+ net::LOAD_DO_NOT_SEND_AUTH_DATA);
url_fetcher_->Start();
}
-void SpeechRecognitionRequest::UploadAudioChunk(const AudioChunk& audio_chunk,
- bool is_last_chunk) {
+void GoogleOneShotRemoteEngine::EndRecognition() {
+ url_fetcher_.reset();
+}
+
+void GoogleOneShotRemoteEngine::TakeAudioChunk(const AudioChunk& data) {
+ DCHECK(url_fetcher_.get());
+ DCHECK(encoder_.get());
+ DCHECK_EQ(data.bytes_per_sample(), config_.audio_num_bits_per_sample / 8);
+ encoder_->Encode(data);
+ scoped_ptr<AudioChunk> encoded_data(encoder_->GetEncodedDataAndClear());
+ url_fetcher_->AppendChunkToUpload(encoded_data->AsString(), false);
+}
+
+void GoogleOneShotRemoteEngine::AudioChunksEnded() {
DCHECK(url_fetcher_.get());
- url_fetcher_->AppendChunkToUpload(audio_chunk.AsString(), is_last_chunk);
+ DCHECK(encoder_.get());
+
+ // UploadAudioChunk requires a non-empty final buffer. So we encode a packet
+ // of silence in case encoder had no data already.
+ std::vector<int16> samples(
+ config_.audio_sample_rate * kAudioPacketIntervalMs / 1000);
+ AudioChunk dummy_chunk(reinterpret_cast<uint8*>(&samples[0]),
+ samples.size() * sizeof(int16),
+ encoder_->bits_per_sample() / 8);
+ encoder_->Encode(dummy_chunk);
+ encoder_->Flush();
+ scoped_ptr<AudioChunk> encoded_dummy_data(encoder_->GetEncodedDataAndClear());
+ DCHECK(!encoded_dummy_data->IsEmpty());
+ encoder_.reset();
+
+ url_fetcher_->AppendChunkToUpload(encoded_dummy_data->AsString(), true);
}
-void SpeechRecognitionRequest::OnURLFetchComplete(
+void GoogleOneShotRemoteEngine::OnURLFetchComplete(
const content::URLFetcher* source) {
DCHECK_EQ(url_fetcher_.get(), source);
-
- content::SpeechRecognitionResult result;
+ SpeechRecognitionResult result;
+ SpeechRecognitionError error(content::SPEECH_RECOGNITION_ERROR_NETWORK);
std::string data;
- if (!source->GetStatus().is_success() || source->GetResponseCode() != 200 ||
- !source->GetResponseAsString(&data) ||
- !ParseServerResponse(data, &result)) {
- result.error = content::SPEECH_RECOGNITION_ERROR_NETWORK;
- }
- DVLOG(1) << "SpeechRecognitionRequest: Invoking delegate with result.";
+ // The default error code in case of parse errors is NETWORK_FAILURE, however
+ // ParseServerResponse can change the error to a more appropriate one.
+ bool error_occurred = (!source->GetStatus().is_success() ||
+ source->GetResponseCode() != 200 ||
+ !source->GetResponseAsString(&data) ||
+ !ParseServerResponse(data, &result, &error));
url_fetcher_.reset();
- delegate_->SetRecognitionResult(result);
+ if (error_occurred) {
+ DVLOG(1) << "GoogleOneShotRemoteEngine: Network Error " << error.code;
+ delegate()->OnSpeechRecognitionEngineError(error);
+ } else {
+ DVLOG(1) << "GoogleOneShotRemoteEngine: Invoking delegate with result.";
+ delegate()->OnSpeechRecognitionEngineResult(result);
+ }
+}
+
+bool GoogleOneShotRemoteEngine::IsRecognitionPending() const {
+ return url_fetcher_ != NULL;
+}
+
+int GoogleOneShotRemoteEngine::GetDesiredAudioChunkDurationMs() const {
+ return kAudioPacketIntervalMs;
}
} // namespace speech
View
84 content/browser/speech/google_one_shot_remote_engine.h
@@ -0,0 +1,84 @@
+// 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 CONTENT_BROWSER_SPEECH_GOOGLE_ONE_SHOT_REMOTE_ENGINE_H_
+#define CONTENT_BROWSER_SPEECH_GOOGLE_ONE_SHOT_REMOTE_ENGINE_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "content/browser/speech/audio_encoder.h"
+#include "content/browser/speech/speech_recognition_engine.h"
+#include "content/common/content_export.h"
+#include "content/public/common/url_fetcher_delegate.h"
+#include "googleurl/src/gurl.h"
+
+class URLFetcher;
+
+namespace content {
+struct SpeechRecognitionResult;
+}
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace speech {
+
+class AudioChunk;
+
+struct GoogleOneShotRemoteEngineConfig {
+ std::string language;
+ std::string grammar;
+ bool filter_profanities;
+ std::string hardware_info;
+ std::string origin_url;
+ int audio_sample_rate;
+ int audio_num_bits_per_sample;
+
+ GoogleOneShotRemoteEngineConfig();
+ ~GoogleOneShotRemoteEngineConfig();
+};
+
+// Implements a SpeechRecognitionEngine by means of remote interaction with
+// Google speech recognition webservice.
+class CONTENT_EXPORT GoogleOneShotRemoteEngine
+ : public NON_EXPORTED_BASE(SpeechRecognitionEngine),
+ public content::URLFetcherDelegate {
+ public:
+ // Duration of each audio packet.
+ static const int kAudioPacketIntervalMs;
+ // ID passed to URLFetcher::Create(). Used for testing.
+ static int url_fetcher_id_for_tests;
+
+ explicit GoogleOneShotRemoteEngine(net::URLRequestContextGetter* context);
+ virtual ~GoogleOneShotRemoteEngine();
+ void SetConfig(const GoogleOneShotRemoteEngineConfig& config);
+
+ // SpeechRecognitionEngine methods.
+ virtual void StartRecognition() OVERRIDE;
+ virtual void EndRecognition() OVERRIDE;
+ virtual void TakeAudioChunk(const AudioChunk& data) OVERRIDE;
+ virtual void AudioChunksEnded() OVERRIDE;
+ virtual bool IsRecognitionPending() const OVERRIDE;
+ virtual int GetDesiredAudioChunkDurationMs() const OVERRIDE;
+
+ // content::URLFetcherDelegate methods.
+ virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE;
+
+ private:
+ GoogleOneShotRemoteEngineConfig config_;
+ scoped_ptr<content::URLFetcher> url_fetcher_;
+ scoped_refptr<net::URLRequestContextGetter> url_context_;
+ scoped_ptr<AudioEncoder> encoder_;
+
+ DISALLOW_COPY_AND_ASSIGN(GoogleOneShotRemoteEngine);
+};
+
+} // namespace speech
+
+#endif // CONTENT_BROWSER_SPEECH_GOOGLE_ONE_SHOT_REMOTE_ENGINE_H_
View
46 ...ser/speech/speech_recognition_request_unittest.cc → .../speech/google_one_shot_remote_engine_unittest.cc
@@ -5,7 +5,8 @@
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
#include "content/browser/speech/audio_buffer.h"
-#include "content/browser/speech/speech_recognition_request.h"
+#include "content/browser/speech/google_one_shot_remote_engine.h"
+#include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h"
#include "content/test/test_url_fetcher_factory.h"
#include "net/url_request/url_request_context_getter.h"
@@ -14,37 +15,46 @@
namespace speech {
-class SpeechRecognitionRequestTest : public SpeechRecognitionRequestDelegate,
- public testing::Test {
+class GoogleOneShotRemoteEngineTest
+ : public SpeechRecognitionEngineDelegate,
+ public testing::Test {
public:
- SpeechRecognitionRequestTest() { }
+ GoogleOneShotRemoteEngineTest()
+ : error_(content::SPEECH_RECOGNITION_ERROR_NONE) {}
// Creates a speech recognition request and invokes it's URL fetcher delegate
// with the given test data.
void CreateAndTestRequest(bool success, const std::string& http_response);
// SpeechRecognitionRequestDelegate methods.
- virtual void SetRecognitionResult(
+ virtual void OnSpeechRecognitionEngineResult(
const content::SpeechRecognitionResult& result) OVERRIDE {
result_ = result;
}
+ virtual void OnSpeechRecognitionEngineError(
+ const content::SpeechRecognitionError& error) OVERRIDE {
+ error_ = error.code;
+ }
+
protected:
MessageLoop message_loop_;
TestURLFetcherFactory url_fetcher_factory_;
+ content::SpeechRecognitionErrorCode error_;
content::SpeechRecognitionResult result_;
};
-void SpeechRecognitionRequestTest::CreateAndTestRequest(
+void GoogleOneShotRemoteEngineTest::CreateAndTestRequest(
bool success, const std::string& http_response) {
- SpeechRecognitionRequest request(NULL, this);
- request.Start(std::string(), std::string(), false, std::string(),
- std::string(), std::string());
+ GoogleOneShotRemoteEngine client(NULL);
unsigned char dummy_audio_buffer_data[2] = {'\0', '\0'};
AudioChunk dummy_audio_chunk(&dummy_audio_buffer_data[0],
sizeof(dummy_audio_buffer_data),
2 /* bytes per sample */);
- request.UploadAudioChunk(dummy_audio_chunk, true);
+ client.set_delegate(this);
+ client.StartRecognition();
+ client.TakeAudioChunk(dummy_audio_chunk);
+ client.AudioChunksEnded();
TestURLFetcher* fetcher = url_fetcher_factory_.GetFetcherByID(0);
ASSERT_TRUE(fetcher);
@@ -60,12 +70,12 @@ void SpeechRecognitionRequestTest::CreateAndTestRequest(
// Parsed response will be available in result_.
}
-TEST_F(SpeechRecognitionRequestTest, BasicTest) {
+TEST_F(GoogleOneShotRemoteEngineTest, BasicTest) {
// Normal success case with one result.
CreateAndTestRequest(true,
"{\"status\":0,\"hypotheses\":"
"[{\"utterance\":\"123456\",\"confidence\":0.9}]}");
- EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NONE);
+ EXPECT_EQ(error_, content::SPEECH_RECOGNITION_ERROR_NONE);
EXPECT_EQ(1U, result_.hypotheses.size());
EXPECT_EQ(ASCIIToUTF16("123456"), result_.hypotheses[0].utterance);
EXPECT_EQ(0.9, result_.hypotheses[0].confidence);
@@ -75,7 +85,7 @@ TEST_F(SpeechRecognitionRequestTest, BasicTest) {
"{\"status\":0,\"hypotheses\":["
"{\"utterance\":\"hello\",\"confidence\":0.9},"
"{\"utterance\":\"123456\",\"confidence\":0.5}]}");
- EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NONE);
+ EXPECT_EQ(error_, content::SPEECH_RECOGNITION_ERROR_NONE);
EXPECT_EQ(2u, result_.hypotheses.size());
EXPECT_EQ(ASCIIToUTF16("hello"), result_.hypotheses[0].utterance);
EXPECT_EQ(0.9, result_.hypotheses[0].confidence);
@@ -84,28 +94,28 @@ TEST_F(SpeechRecognitionRequestTest, BasicTest) {
// Zero results.
CreateAndTestRequest(true, "{\"status\":0,\"hypotheses\":[]}");
- EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NONE);
+ EXPECT_EQ(error_, content::SPEECH_RECOGNITION_ERROR_NONE);
EXPECT_EQ(0U, result_.hypotheses.size());
// Http failure case.
CreateAndTestRequest(false, "");
- EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NETWORK);
+ EXPECT_EQ(error_, content::SPEECH_RECOGNITION_ERROR_NETWORK);
EXPECT_EQ(0U, result_.hypotheses.size());
// Invalid status case.
CreateAndTestRequest(true, "{\"status\":\"invalid\",\"hypotheses\":[]}");
- EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NETWORK);
+ EXPECT_EQ(error_, content::SPEECH_RECOGNITION_ERROR_NETWORK);
EXPECT_EQ(0U, result_.hypotheses.size());
// Server-side error case.
CreateAndTestRequest(true, "{\"status\":1,\"hypotheses\":[]}");
- EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NETWORK);
+ EXPECT_EQ(error_, content::SPEECH_RECOGNITION_ERROR_NETWORK);
EXPECT_EQ(0U, result_.hypotheses.size());
// Malformed JSON case.
CreateAndTestRequest(true, "{\"status\":0,\"hypotheses\":"
"[{\"unknownkey\":\"hello\"}]}");
- EXPECT_EQ(result_.error, content::SPEECH_RECOGNITION_ERROR_NETWORK);
+ EXPECT_EQ(error_, content::SPEECH_RECOGNITION_ERROR_NETWORK);
EXPECT_EQ(0U, result_.hypotheses.size());
}
View
1  content/browser/speech/speech_recognition_browsertest.cc
@@ -17,6 +17,7 @@
#include "content/browser/tab_contents/tab_contents.h"
#include "content/public/browser/notification_types.h"
#include "content/public/common/content_switches.h"
+#include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
View
92 content/browser/speech/speech_recognition_engine.h
@@ -0,0 +1,92 @@
+// 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 CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_ENGINE_H_
+#define CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_ENGINE_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace content {
+struct SpeechRecognitionResult;
+struct SpeechRecognitionError;
+}
+
+namespace speech {
+
+class AudioChunk;
+
+// This interface models the basic contract that a speech recognition engine,
+// either working locally or relying on a remote web-service, must obey.
+// The expected call sequence for exported methods is:
+// StartRecognition Mandatory at beginning of SR.
+// TakeAudioChunk For every audio chunk pushed.
+// AudioChunksEnded Finalize the audio stream (omitted in case of errors).
+// EndRecognition Mandatory at end of SR (even on errors).
+// No delegate callback is allowed before Initialize() or after Cleanup().
+class SpeechRecognitionEngine {
+ public:
+ // Interface for receiving callbacks from this object.
+ class Delegate {
+ public:
+ // Called whenever a result is retrieved. It might be issued several times,
+ // (e.g., in the case of continuous speech recognition engine
+ // implementations).
+ virtual void OnSpeechRecognitionEngineResult(
+ const content::SpeechRecognitionResult& result) = 0;
+ virtual void OnSpeechRecognitionEngineError(
+ const content::SpeechRecognitionError& error) = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ virtual ~SpeechRecognitionEngine() {}
+
+ // Called when the speech recognition begins, before any TakeAudioChunk call.
+ virtual void StartRecognition() = 0;
+
+ // End any recognition activity and don't make any further callback.
+ // Must be always called to close the corresponding StartRecognition call,
+ // even in case of errors.
+ // No further TakeAudioChunk/AudioChunksEnded calls are allowed after this.
+ virtual void EndRecognition() = 0;
+
+ // Push a chunk of uncompressed audio data, where the chunk length agrees with
+ // GetDesiredAudioChunkDurationMs().
+ virtual void TakeAudioChunk(const AudioChunk& data) = 0;
+
+ // Notifies the engine that audio capture has completed and no more chunks
+ // will be pushed. The engine, however, can still provide further results
+ // using the audio chunks collected so far.
+ virtual void AudioChunksEnded() = 0;
+
+ // Checks wheter recognition of pushed audio data is pending.
+ virtual bool IsRecognitionPending() const = 0;
+
+ // Retrieves the desired duration, in milliseconds, of pushed AudioChunk(s).
+ virtual int GetDesiredAudioChunkDurationMs() const = 0;
+
+ // set_delegate detached from constructor for lazy dependency injection.
+ void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+
+ protected:
+ Delegate* delegate() const { return delegate_; }
+
+ private:
+ Delegate* delegate_;
+};
+
+// This typedef is to workaround the issue with certain versions of
+// Visual Studio where it gets confused between multiple Delegate
+// classes and gives a C2500 error. (I saw this error on the try bots -
+// the workaround was not needed for my machine).
+typedef SpeechRecognitionEngine::Delegate SpeechRecognitionEngineDelegate;
+
+} // namespace speech
+
+#endif // CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_ENGINE_H_
View
47 content/browser/speech/speech_recognition_manager_impl.cc
@@ -203,24 +203,11 @@ void SpeechRecognitionManagerImpl::StartRecognitionForRequest(int caller_id) {
// If we are currently recording audio for another caller, abort that cleanly.
if (recording_caller_id_)
CancelRecognitionAndInformDelegate(recording_caller_id_);
-
- if (!HasAudioInputDevices()) {
- if (delegate_) {
- delegate_->ShowMicError(caller_id,
- SpeechRecognitionManagerDelegate::MIC_ERROR_NO_DEVICE_AVAILABLE);
- }
- } else if (IsCapturingAudio()) {
- if (delegate_) {
- delegate_->ShowMicError(
- caller_id, SpeechRecognitionManagerDelegate::MIC_ERROR_DEVICE_IN_USE);
- }
- } else {
- recording_caller_id_ = caller_id;
- requests_[caller_id].is_active = true;
- requests_[caller_id].recognizer->StartRecognition();
- if (delegate_)
- delegate_->ShowWarmUp(caller_id);
- }
+ recording_caller_id_ = caller_id;
+ requests_[caller_id].is_active = true;
+ requests_[caller_id].recognizer->StartRecognition();
+ if (delegate_)
+ delegate_->ShowWarmUp(caller_id);
}
void SpeechRecognitionManagerImpl::CancelRecognitionForRequest(int caller_id) {
@@ -288,8 +275,12 @@ void SpeechRecognitionManagerImpl::OnRecognitionResult(
}
void SpeechRecognitionManagerImpl::OnAudioEnd(int caller_id) {
+ if (recording_caller_id_ != caller_id)
+ return;
DCHECK_EQ(recording_caller_id_, caller_id);
DCHECK(HasPendingRequest(caller_id));
+ if (!requests_[caller_id].is_active)
+ return;
recording_caller_id_ = 0;
GetDelegate(caller_id)->DidCompleteRecording(caller_id);
if (delegate_)
@@ -297,6 +288,8 @@ void SpeechRecognitionManagerImpl::OnAudioEnd(int caller_id) {
}
void SpeechRecognitionManagerImpl::OnRecognitionEnd(int caller_id) {
+ if (!HasPendingRequest(caller_id) || !requests_[caller_id].is_active)
+ return;
GetDelegate(caller_id)->DidCompleteRecognition(caller_id);
requests_.erase(caller_id);
if (delegate_)
@@ -310,12 +303,24 @@ void SpeechRecognitionManagerImpl::OnSoundEnd(int caller_id) {
}
void SpeechRecognitionManagerImpl::OnRecognitionError(
- int caller_id, const content::SpeechRecognitionErrorCode& error) {
+ int caller_id, const content::SpeechRecognitionError& error) {
+ DCHECK(HasPendingRequest(caller_id));
if (caller_id == recording_caller_id_)
recording_caller_id_ = 0;
requests_[caller_id].is_active = false;
- if (delegate_)
- delegate_->ShowRecognizerError(caller_id, error);
+ if (delegate_) {
+ if (error.code == content::SPEECH_RECOGNITION_ERROR_AUDIO &&
+ error.details == content::SPEECH_AUDIO_ERROR_DETAILS_NO_MIC) {
+ delegate_->ShowMicError(caller_id,
+ SpeechRecognitionManagerDelegate::MIC_ERROR_NO_DEVICE_AVAILABLE);
+ } else if (error.code == content::SPEECH_RECOGNITION_ERROR_AUDIO &&
+ error.details == content::SPEECH_AUDIO_ERROR_DETAILS_IN_USE) {
+ delegate_->ShowMicError(
+ caller_id, SpeechRecognitionManagerDelegate::MIC_ERROR_DEVICE_IN_USE);
+ } else {
+ delegate_->ShowRecognizerError(caller_id, error.code);
+ }
+ }
}
void SpeechRecognitionManagerImpl::OnAudioStart(int caller_id) {
View
2  content/browser/speech/speech_recognition_manager_impl.h
@@ -82,7 +82,7 @@ class CONTENT_EXPORT SpeechRecognitionManagerImpl
virtual void OnRecognitionResult(
int caller_id, const content::SpeechRecognitionResult& result) OVERRIDE;
virtual void OnRecognitionError(
- int caller_id, const content::SpeechRecognitionErrorCode& error) OVERRIDE;
+ int caller_id, const content::SpeechRecognitionError& error) OVERRIDE;
virtual void OnAudioLevelsChange(
int caller_id, float volume, float noise_volume) OVERRIDE;
View
90 content/browser/speech/speech_recognition_request.h
@@ -1,90 +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 CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_REQUEST_H_
-#define CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_REQUEST_H_
-#pragma once
-
-#include <string>
-
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "content/common/content_export.h"
-#include "content/public/common/url_fetcher_delegate.h"
-#include "googleurl/src/gurl.h"
-
-class URLFetcher;
-
-namespace content {
-struct SpeechRecognitionResult;
-}
-
-namespace net {
-class URLRequestContextGetter;
-}
-
-namespace speech {
-
-class AudioChunk;
-
-// Provides a simple interface for sending recorded speech data to the server
-// and get back recognition results.
-class SpeechRecognitionRequest : public content::URLFetcherDelegate {
- public:
- // ID passed to URLFetcher::Create(). Used for testing.
- CONTENT_EXPORT static int url_fetcher_id_for_tests;
-
- // Interface for receiving callbacks from this object.
- class CONTENT_EXPORT Delegate {
- public:
- virtual void SetRecognitionResult(
- const content::SpeechRecognitionResult& result) = 0;
-
- protected:
- virtual ~Delegate() {}
- };
-
- // |url| is the server address to which the request wil be sent.
- CONTENT_EXPORT SpeechRecognitionRequest(net::URLRequestContextGetter* context,
- Delegate* delegate);
-
- CONTENT_EXPORT virtual ~SpeechRecognitionRequest();
-
- // Sends a new request with the given audio data, returns true if successful.
- // The same object can be used to send multiple requests but only after the
- // previous request has completed.
- CONTENT_EXPORT void Start(const std::string& language,
- const std::string& grammar,
- bool filter_profanities,
- const std::string& hardware_info,
- const std::string& origin_url,
- const std::string& content_type);
-
- // Send a single chunk of audio immediately to the server.
- CONTENT_EXPORT void UploadAudioChunk(const AudioChunk& audio_chunk,
- bool is_last_chunk);
-
- CONTENT_EXPORT bool HasPendingRequest() { return url_fetcher_ != NULL; }
-
- // content::URLFetcherDelegate methods.
- virtual void OnURLFetchComplete(const content::URLFetcher* source) OVERRIDE;
-
- private:
- scoped_refptr<net::URLRequestContextGetter> url_context_;
- Delegate* delegate_;
- scoped_ptr<content::URLFetcher> url_fetcher_;
-
- DISALLOW_COPY_AND_ASSIGN(SpeechRecognitionRequest);
-};
-
-// This typedef is to workaround the issue with certain versions of
-// Visual Studio where it gets confused between multiple Delegate
-// classes and gives a C2500 error. (I saw this error on the try bots -
-// the workaround was not needed for my machine).
-typedef SpeechRecognitionRequest::Delegate SpeechRecognitionRequestDelegate;
-
-} // namespace speech
-
-#endif // CONTENT_BROWSER_SPEECH_SPEECH_RECOGNITION_REQUEST_H_
View
136 content/browser/speech/speech_recognizer_impl.cc
@@ -8,17 +8,21 @@
#include "base/time.h"
#include "content/browser/browser_main_loop.h"
#include "content/browser/speech/audio_buffer.h"
-#include "content/public/browser/speech_recognition_event_listener.h"
+#include "content/browser/speech/google_one_shot_remote_engine.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/speech_recognition_event_listener.h"
+#include "content/public/browser/speech_recognizer.h"
+#include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h"
#include "net/url_request/url_request_context_getter.h"
using content::BrowserMainLoop;
using content::BrowserThread;
+using content::SpeechRecognitionError;
using content::SpeechRecognitionEventListener;
+using content::SpeechRecognitionResult;
using content::SpeechRecognizer;
using media::AudioInputController;
-using std::string;
namespace {
@@ -64,18 +68,22 @@ SpeechRecognizer* SpeechRecognizer::Create(
bool filter_profanities,
const std::string& hardware_info,
const std::string& origin_url) {
- return new speech::SpeechRecognizerImpl(
- listener, caller_id, language, grammar, context_getter,
- filter_profanities, hardware_info, origin_url);
+ return new speech::SpeechRecognizerImpl(listener,
+ caller_id,
+ language,
+ grammar,
+ context_getter,
+ filter_profanities,
+ hardware_info,
+ origin_url);
}
namespace speech {
const int SpeechRecognizerImpl::kAudioSampleRate = 16000;
-const int SpeechRecognizerImpl::kAudioPacketIntervalMs = 100;
const ChannelLayout SpeechRecognizerImpl::kChannelLayout = CHANNEL_LAYOUT_MONO;
const int SpeechRecognizerImpl::kNumBitsPerAudioSample = 16;
-const int SpeechRecognizerImpl::kNoSpeechTimeoutSec = 8;
+const int SpeechRecognizerImpl::kNoSpeechTimeoutMs = 8000;
const int SpeechRecognizerImpl::kEndpointerEstimationTimeMs = 300;
SpeechRecognizerImpl::SpeechRecognizerImpl(
@@ -88,19 +96,18 @@ SpeechRecognizerImpl::SpeechRecognizerImpl(
const std::string& hardware_info,
const std::string& origin_url)
: listener_(listener),
+ testing_audio_manager_(NULL),
+ endpointer_(kAudioSampleRate),
+ context_getter_(context_getter),
caller_id_(caller_id),
language_(language),
grammar_(grammar),
filter_profanities_(filter_profanities),
hardware_info_(hardware_info),
origin_url_(origin_url),
- context_getter_(context_getter),
- codec_(AudioEncoder::CODEC_FLAC),
- encoder_(NULL),
- endpointer_(kAudioSampleRate),
num_samples_recorded_(0),
- audio_level_(0.0f),
- audio_manager_(NULL) {
+ audio_level_(0.0f) {
+ DCHECK(listener_ != NULL);
endpointer_.set_speech_input_complete_silence_length(
base::Time::kMicrosecondsPerSecond / 2);
endpointer_.set_long_speech_input_complete_silence_length(
@@ -113,42 +120,40 @@ SpeechRecognizerImpl::~SpeechRecognizerImpl() {
// Recording should have stopped earlier due to the endpointer or
// |StopRecording| being called.
DCHECK(!audio_controller_.get());
- DCHECK(!request_.get() || !request_->HasPendingRequest());
- DCHECK(!encoder_.get());
+ DCHECK(!recognition_engine_.get() ||
+ !recognition_engine_->IsRecognitionPending());
endpointer_.EndSession();
}
-bool SpeechRecognizerImpl::StartRecognition() {
+void SpeechRecognizerImpl::StartRecognition() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(!audio_controller_.get());
- DCHECK(!request_.get() || !request_->HasPendingRequest());
- DCHECK(!encoder_.get());
+ DCHECK(!recognition_engine_.get() ||
+ !recognition_engine_->IsRecognitionPending());
// The endpointer needs to estimate the environment/background noise before
// starting to treat the audio as user input. In |HandleOnData| we wait until
// such time has passed before switching to user input mode.
endpointer_.SetEnvironmentEstimationMode();
- encoder_.reset(AudioEncoder::Create(codec_, kAudioSampleRate,
- kNumBitsPerAudioSample));
- int samples_per_packet = (kAudioSampleRate * kAudioPacketIntervalMs) / 1000;
+ AudioManager* audio_manager = (testing_audio_manager_ != NULL) ?
+ testing_audio_manager_ :
+ BrowserMainLoop::GetAudioManager();
+ const int samples_per_packet = kAudioSampleRate *
+ GoogleOneShotRemoteEngine::kAudioPacketIntervalMs / 1000;
AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, kChannelLayout,
kAudioSampleRate, kNumBitsPerAudioSample,
samples_per_packet);
- audio_controller_ = AudioInputController::Create(
- audio_manager_ ? audio_manager_ : BrowserMainLoop::GetAudioManager(),
- this, params);
+ audio_controller_ = AudioInputController::Create(audio_manager, this, params);
DCHECK(audio_controller_.get());
VLOG(1) << "SpeechRecognizer starting record.";
num_samples_recorded_ = 0;
audio_controller_->Record();
-
- return true;
}
void SpeechRecognizerImpl::AbortRecognition() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK(audio_controller_.get() || request_.get());
+ DCHECK(audio_controller_.get() || recognition_engine_.get());
// Stop recording if required.
if (audio_controller_.get()) {
@@ -156,8 +161,7 @@ void SpeechRecognizerImpl::AbortRecognition() {
}
VLOG(1) << "SpeechRecognizer canceling recognition.";
- encoder_.reset();
- request_.reset();
+ recognition_engine_.reset();
}
void SpeechRecognizerImpl::StopAudioCapture() {
@@ -169,30 +173,16 @@ void SpeechRecognizerImpl::StopAudioCapture() {
return;
CloseAudioControllerSynchronously();
-
listener_->OnSoundEnd(caller_id_);
listener_->OnAudioEnd(caller_id_);
- // UploadAudioChunk requires a non-empty final buffer. So we encode a packet
- // of silence in case encoder had no data already.
- std::vector<short> samples((kAudioSampleRate * kAudioPacketIntervalMs) /
- 1000);
- AudioChunk dummy_chunk(reinterpret_cast<uint8*>(&samples[0]),
- samples.size() * sizeof(short),
- encoder_->bits_per_sample() / 8);
- encoder_->Encode(dummy_chunk);
- encoder_->Flush();
- scoped_ptr<AudioChunk> encoded_data(encoder_->GetEncodedDataAndClear());
- DCHECK(!encoded_data->IsEmpty());
- encoder_.reset();
-
// If we haven't got any audio yet end the recognition sequence here.
- if (request_ == NULL) {
+ if (recognition_engine_ == NULL) {
// Guard against the listener freeing us until we finish our job.
scoped_refptr<SpeechRecognizerImpl> me(this);
listener_->OnRecognitionEnd(caller_id_);
} else {
- request_->UploadAudioChunk(*encoded_data, true /* is_last_chunk */);
+ recognition_engine_->AudioChunksEnded();
}
}
@@ -237,24 +227,32 @@ void SpeechRecognizerImpl::HandleOnData(AudioChunk* raw_audio) {
bool speech_was_heard_before_packet = endpointer_.DidStartReceivingSpeech();
- encoder_->Encode(*raw_audio);
float rms;
endpointer_.ProcessAudio(*raw_audio, &rms);
bool did_clip = DetectClipping(*raw_audio);
num_samples_recorded_ += raw_audio->NumSamples();
- if (request_ == NULL) {
+ if (recognition_engine_ == NULL) {
// This was the first audio packet recorded, so start a request to the
// server to send the data and inform the listener.
listener_->OnAudioStart(caller_id_);
- request_.reset(new SpeechRecognitionRequest(context_getter_.get(), this));
- request_->Start(language_, grammar_, filter_profanities_,
- hardware_info_, origin_url_, encoder_->mime_type());
+ GoogleOneShotRemoteEngineConfig google_sr_config;
+ google_sr_config.language = language_;
+ google_sr_config.grammar = grammar_;
+ google_sr_config.audio_sample_rate = kAudioSampleRate;
+ google_sr_config.audio_num_bits_per_sample = kNumBitsPerAudioSample;
+ google_sr_config.filter_profanities = filter_profanities_;
+ google_sr_config.hardware_info = hardware_info_;
+ google_sr_config.origin_url = origin_url_;
+ GoogleOneShotRemoteEngine* google_sr_engine =
+ new GoogleOneShotRemoteEngine(context_getter_.get());
+ google_sr_engine->SetConfig(google_sr_config);
+ recognition_engine_.reset(google_sr_engine);
+ recognition_engine_->set_delegate(this);
+ recognition_engine_->StartRecognition();
}
- scoped_ptr<AudioChunk> encoded_data(encoder_->GetEncodedDataAndClear());
- DCHECK(!encoded_data->IsEmpty());
- request_->UploadAudioChunk(*encoded_data, false /* is_last_chunk */);
+ recognition_engine_->TakeAudioChunk(*raw_audio);
if (endpointer_.IsEstimatingEnvironment()) {
// Check if we have gathered enough audio for the endpointer to do
@@ -270,7 +268,7 @@ void SpeechRecognizerImpl::HandleOnData(AudioChunk* raw_audio) {
// Check if we have waited too long without hearing any speech.
bool speech_was_heard_after_packet = endpointer_.DidStartReceivingSpeech();
if (!speech_was_heard_after_packet &&
- num_samples_recorded_ >= kNoSpeechTimeoutSec * kAudioSampleRate) {
+ num_samples_recorded_ >= (kNoSpeechTimeoutMs / 1000) * kAudioSampleRate) {
InformErrorAndAbortRecognition(
content::SPEECH_RECOGNITION_ERROR_NO_SPEECH);
return;
@@ -302,19 +300,19 @@ void SpeechRecognizerImpl::HandleOnData(AudioChunk* raw_audio) {
StopAudioCapture();
}
-void SpeechRecognizerImpl::SetRecognitionResult(
+void SpeechRecognizerImpl::OnSpeechRecognitionEngineResult(
const content::SpeechRecognitionResult& result) {
- if (result.error != content::SPEECH_RECOGNITION_ERROR_NONE) {
- InformErrorAndAbortRecognition(result.error);
- return;
- }
-
// Guard against the listener freeing us until we finish our job.
scoped_refptr<SpeechRecognizerImpl> me(this);
listener_->OnRecognitionResult(caller_id_, result);
listener_->OnRecognitionEnd(caller_id_);
}
+void SpeechRecognizerImpl::OnSpeechRecognitionEngineError(
+ const content::SpeechRecognitionError& error) {
+ InformErrorAndAbortRecognition(error.code);
+}
+
void SpeechRecognizerImpl::InformErrorAndAbortRecognition(
content::SpeechRecognitionErrorCode error) {
DCHECK_NE(error, content::SPEECH_RECOGNITION_ERROR_NONE);
@@ -338,17 +336,23 @@ void SpeechRecognizerImpl::CloseAudioControllerSynchronously() {
audio_controller_ = NULL; // Releases the ref ptr.
}
-void SpeechRecognizerImpl::SetAudioManagerForTesting(
- AudioManager* audio_manager) {
- audio_manager_ = audio_manager;
-}
-
bool SpeechRecognizerImpl::IsActive() const {
- return (request_.get() != NULL);
+ return (recognition_engine_.get() != NULL);
}
bool SpeechRecognizerImpl::IsCapturingAudio() const {
return (audio_controller_.get() != NULL);
}
+const SpeechRecognitionEngine&
+ SpeechRecognizerImpl::recognition_engine() const {
+ return *(recognition_engine_.get());
+}
+
+void SpeechRecognizerImpl::SetAudioManagerForTesting(
+ AudioManager* audio_manager) {
+ testing_audio_manager_ = audio_manager;
+}
+
+
} // namespace speech
View
65 content/browser/speech/speech_recognizer_impl.h
@@ -5,22 +5,22 @@
#ifndef CONTENT_BROWSER_SPEECH_SPEECH_RECOGNIZER_IMPL_H_
#define CONTENT_BROWSER_SPEECH_SPEECH_RECOGNIZER_IMPL_H_
-#include <list>
-#include <utility>
-
-#include "base/compiler_specific.h"
+#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
-#include "content/browser/speech/audio_encoder.h"
#include "content/browser/speech/endpointer/endpointer.h"
-#include "content/browser/speech/speech_recognition_request.h"
+#include "content/browser/speech/speech_recognition_engine.h"
#include "content/public/browser/speech_recognizer.h"
-#include "content/public/common/speech_recognition_result.h"
+#include "content/public/common/speech_recognition_error.h"
#include "media/audio/audio_input_controller.h"
-
-class AudioManager;
+#include "net/url_request/url_request_context_getter.h"
namespace content {
class SpeechRecognitionEventListener;
+struct SpeechRecognitionResult;
+}
+
+namespace media {
+class AudioInputController;
}
namespace speech {
@@ -28,34 +28,34 @@ namespace speech {
// Records audio, sends recorded audio to server and translates server response
// to recognition result.
class CONTENT_EXPORT SpeechRecognizerImpl
- : NON_EXPORTED_BASE(public content::SpeechRecognizer),
+ : public NON_EXPORTED_BASE(content::SpeechRecognizer),
public media::AudioInputController::EventHandler,
- public SpeechRecognitionRequestDelegate {
+ public NON_EXPORTED_BASE(SpeechRecognitionEngineDelegate) {
public:
static const int kAudioSampleRate;
- static const int kAudioPacketIntervalMs; // Duration of each audio packet.
static const ChannelLayout kChannelLayout;
static const int kNumBitsPerAudioSample;
- static const int kNoSpeechTimeoutSec;
+ static const int kNoSpeechTimeoutMs;
static const int kEndpointerEstimationTimeMs;
- SpeechRecognizerImpl(content::SpeechRecognitionEventListener* listener,
- int caller_id,
- const std::string& language,
- const std::string& grammar,
- net::URLRequestContextGetter* context_getter,
- bool filter_profanities,
- const std::string& hardware_info,
- const std::string& origin_url);
-
+ SpeechRecognizerImpl(
+ content::SpeechRecognitionEventListener* listener,
+ int caller_id,
+ const std::string& language,
+ const std::string& grammar,
+ net::URLRequestContextGetter* context_getter,
+ bool filter_profanities,
+ const std::string& hardware_info,
+ const std::string& origin_url);
virtual ~SpeechRecognizerImpl();
// content::SpeechRecognizer methods.
- virtual bool StartRecognition() OVERRIDE;
+ virtual void StartRecognition() OVERRIDE;
virtual void AbortRecognition() OVERRIDE;
virtual void StopAudioCapture() OVERRIDE;
virtual bool IsActive() const OVERRIDE;
virtual bool IsCapturingAudio() const OVERRIDE;
+ const SpeechRecognitionEngine& recognition_engine() const;
// AudioInputController::EventHandler methods.
virtual void OnCreated(media::AudioInputController* controller) OVERRIDE {}
@@ -66,9 +66,11 @@ class CONTENT_EXPORT SpeechRecognizerImpl
const uint8* data,
uint32 size) OVERRIDE;
- // SpeechRecognitionRequest::Delegate methods.
- virtual void SetRecognitionResult(
+ // SpeechRecognitionEngineDelegate methods.
+ virtual void OnSpeechRecognitionEngineResult(
const content::SpeechRecognitionResult& result) OVERRIDE;
+ virtual void OnSpeechRecognitionEngineError(
+ const content::SpeechRecognitionError& error) OVERRIDE;
private:
friend class SpeechRecognizerImplTest;
@@ -88,22 +90,19 @@ class CONTENT_EXPORT SpeechRecognizerImpl
void SetAudioManagerForTesting(AudioManager* audio_manager);
content::SpeechRecognitionEventListener* listener_;
+ AudioManager* testing_audio_manager_;
+ scoped_ptr<SpeechRecognitionEngine> recognition_engine_;
+ Endpointer endpointer_;
+ scoped_refptr<media::AudioInputController> audio_controller_;
+ scoped_refptr<net::URLRequestContextGetter> context_getter_;
int caller_id_;
std::string language_;
std::string grammar_;
bool filter_profanities_;
std::string hardware_info_;
std::string origin_url_;
-
- scoped_ptr<SpeechRecognitionRequest> request_;
- scoped_refptr<media::AudioInputController> audio_controller_;
- scoped_refptr<net::URLRequestContextGetter> context_getter_;
- AudioEncoder::Codec codec_;
- scoped_ptr<AudioEncoder> encoder_;
- Endpointer endpointer_;
int num_samples_recorded_;
float audio_level_;
- AudioManager* audio_manager_;
DISALLOW_COPY_AND_ASSIGN(SpeechRecognizerImpl);
};
View
39 content/browser/speech/speech_recognizer_impl_unittest.cc
@@ -5,6 +5,7 @@
#include <vector>
#include "content/browser/browser_thread_impl.h"
+#include "content/browser/speech/google_one_shot_remote_engine.h"
#include "content/browser/speech/speech_recognizer_impl.h"
#include "content/public/browser/speech_recognition_event_listener.h"
#include "content/test/test_url_fetcher_factory.h"
@@ -104,7 +105,7 @@ class SpeechRecognizerImplTest : public content::SpeechRecognitionEventListener,
recognizer_->SetAudioManagerForTesting(audio_manager_.get());
int audio_packet_length_bytes =
(SpeechRecognizerImpl::kAudioSampleRate *
- SpeechRecognizerImpl::kAudioPacketIntervalMs *
+ GoogleOneShotRemoteEngine::kAudioPacketIntervalMs *
ChannelLayoutToChannelCount(SpeechRecognizerImpl::kChannelLayout) *
SpeechRecognizerImpl::kNumBitsPerAudioSample) / (8 * 1000);
audio_packet_.resize(audio_packet_length_bytes);
@@ -126,8 +127,8 @@ class SpeechRecognizerImplTest : public content::SpeechRecognitionEventListener,
virtual void OnRecognitionError(
int caller_id,
- const content::SpeechRecognitionErrorCode& error) OVERRIDE {
- error_ = error;
+ const content::SpeechRecognitionError& error) OVERRIDE {
+ error_ = error.code;
}
virtual void OnAudioLevelsChange(int caller_id, float volume,
@@ -189,7 +190,7 @@ class SpeechRecognizerImplTest : public content::SpeechRecognitionEventListener,
TEST_F(SpeechRecognizerImplTest, StopNoData) {
// Check for callbacks when stopping record before any audio gets recorded.
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
recognizer_->AbortRecognition();
EXPECT_FALSE(audio_ended_);
EXPECT_FALSE(recognition_ended_);
@@ -201,7 +202,7 @@ TEST_F(SpeechRecognizerImplTest, StopNoData) {
TEST_F(SpeechRecognizerImplTest, CancelNoData) {
// Check for callbacks when canceling recognition before any audio gets
// recorded.
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
recognizer_->StopAudioCapture();
EXPECT_TRUE(audio_ended_);
EXPECT_TRUE(recognition_ended_);
@@ -213,7 +214,7 @@ TEST_F(SpeechRecognizerImplTest, CancelNoData) {
TEST_F(SpeechRecognizerImplTest, StopWithData) {
// Start recording, give some data and then stop. This should wait for the
// network callback to arrive before completion.
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
@@ -260,7 +261,7 @@ TEST_F(SpeechRecognizerImplTest, StopWithData) {
TEST_F(SpeechRecognizerImplTest, CancelWithData) {
// Start recording, give some data and then cancel. This should create
// a network request but give no callbacks.
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
@@ -279,7 +280,7 @@ TEST_F(SpeechRecognizerImplTest, CancelWithData) {
TEST_F(SpeechRecognizerImplTest, ConnectionError) {
// Start recording, give some data and then stop. Issue the network callback
// with a connection error and verify that the recognizer bubbles the error up
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
@@ -314,7 +315,7 @@ TEST_F(SpeechRecognizerImplTest, ConnectionError) {
TEST_F(SpeechRecognizerImplTest, ServerError) {
// Start recording, give some data and then stop. Issue the network callback
// with a 500 error and verify that the recognizer bubbles the error up
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
@@ -347,7 +348,7 @@ TEST_F(SpeechRecognizerImplTest, ServerError) {
TEST_F(SpeechRecognizerImplTest, AudioControllerErrorNoData) {
// Check if things tear down properly if AudioInputController threw an error.
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
@@ -363,7 +364,7 @@ TEST_F(SpeechRecognizerImplTest, AudioControllerErrorNoData) {
TEST_F(SpeechRecognizerImplTest, AudioControllerErrorWithData) {
// Check if things tear down properly if AudioInputController threw an error
// after giving some audio data.
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
@@ -382,15 +383,15 @@ TEST_F(SpeechRecognizerImplTest, AudioControllerErrorWithData) {
TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackIssued) {
// Start recording and give a lot of packets with audio samples set to zero.
// This should trigger the no-speech detector and issue a callback.
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
controller = audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
- int num_packets = (SpeechRecognizerImpl::kNoSpeechTimeoutSec * 1000) /
- SpeechRecognizerImpl::kAudioPacketIntervalMs;
+ int num_packets = (SpeechRecognizerImpl::kNoSpeechTimeoutMs) /
+ GoogleOneShotRemoteEngine::kAudioPacketIntervalMs;
// The vector is already filled with zero value samples on create.
for (int i = 0; i < num_packets; ++i) {
controller->event_handler()->OnData(controller, &audio_packet_[0],
@@ -409,15 +410,15 @@ TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackNotIssued) {
// and then some more with reasonably loud audio samples. This should be
// treated as normal speech input and the no-speech detector should not get
// triggered.
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
controller = audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
- int num_packets = (SpeechRecognizerImpl::kNoSpeechTimeoutSec * 1000) /
- SpeechRecognizerImpl::kAudioPacketIntervalMs;
+ int num_packets = (SpeechRecognizerImpl::kNoSpeechTimeoutMs) /
+ GoogleOneShotRemoteEngine::kAudioPacketIntervalMs;
// The vector is already filled with zero value samples on create.
for (int i = 0; i < num_packets / 2; ++i) {
@@ -444,7 +445,7 @@ TEST_F(SpeechRecognizerImplTest, SetInputVolumeCallback) {
// and then some more with reasonably loud audio samples. Check that we don't
// get the callback during estimation phase, then get zero for the silence
// samples and proper volume for the loud audio.
- EXPECT_TRUE(recognizer_->StartRecognition());
+ recognizer_->StartRecognition();
TestAudioInputController* controller =
audio_input_controller_factory_.controller();
ASSERT_TRUE(controller);
@@ -453,7 +454,7 @@ TEST_F(SpeechRecognizerImplTest, SetInputVolumeCallback) {
// Feed some samples to begin with for the endpointer to do noise estimation.
int num_packets = SpeechRecognizerImpl::kEndpointerEstimationTimeMs /
- SpeechRecognizerImpl::kAudioPacketIntervalMs;
+ GoogleOneShotRemoteEngine::kAudioPacketIntervalMs;
FillPacketWithNoise();
for (int i = 0; i < num_packets; ++i) {
controller->event_handler()->OnData(controller, &audio_packet_[0],
View
2  content/common/speech_recognition_messages.h
@@ -6,6 +6,7 @@
#include <string>
+#include "content/public/common/speech_recognition_error.h"
#include "content/public/common/speech_recognition_result.h"
#include "ipc/ipc_message_macros.h"
#include "ipc/ipc_param_traits.h"
@@ -21,7 +22,6 @@ IPC_STRUCT_TRAITS_BEGIN(content::SpeechRecognitionHypothesis)
IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(content::SpeechRecognitionResult)
- IPC_STRUCT_TRAITS_MEMBER(error)
IPC_STRUCT_TRAITS_MEMBER(hypotheses)
IPC_STRUCT_TRAITS_END()
View
5 content/content_browser.gypi
@@ -620,12 +620,13 @@
'browser/speech/endpointer/energy_endpointer.h',
'browser/speech/endpointer/energy_endpointer_params.cc',
'browser/speech/endpointer/energy_endpointer_params.h',
+ 'browser/speech/google_one_shot_remote_engine.cc',
+ 'browser/speech/google_one_shot_remote_engine.h',
'browser/speech/input_tag_speech_dispatcher_host.cc',
'browser/speech/input_tag_speech_dispatcher_host.h',
+ 'browser/speech/speech_recognition_engine.h',
'browser/speech/speech_recognition_manager_impl.cc',
'browser/speech/speech_recognition_manager_impl.h',
- 'browser/speech/speech_recognition_request.cc',
- 'browser/speech/speech_recognition_request.h',
'browser/speech/speech_recognizer_impl.cc',
'browser/speech/speech_recognizer_impl.h',
'browser/ssl/ssl_cert_error_handler.cc',
View
1  content/content_common.gypi
@@ -77,6 +77,7 @@
'public/common/serialized_script_value.h',
'public/common/show_desktop_notification_params.cc',
'public/common/show_desktop_notification_params.h',
+ 'public/common/speech_recognition_error.h',
'public/common/speech_recognition_result.h',
'public/common/speech_recognition_result.cc',
'public/common/ssl_status.cc',
View
2  content/content_tests.gypi
@@ -249,7 +249,7 @@
'browser/resolve_proxy_msg_helper_unittest.cc',
'browser/site_instance_impl_unittest.cc',
'browser/speech/endpointer/endpointer_unittest.cc',
- 'browser/speech/speech_recognition_request_unittest.cc',
+ 'browser/speech/google_one_shot_remote_engine_unittest.cc',
'browser/speech/speech_recognizer_impl_unittest.cc',
'browser/ssl/ssl_host_state_unittest.cc',
'browser/system_message_window_win_unittest.cc',
View
6 content/public/browser/speech_recognition_event_listener.h
@@ -7,10 +7,12 @@
#include "base/basictypes.h"
#include "content/common/content_export.h"
-#include "content/public/common/speech_recognition_result.h"
namespace content {
+struct SpeechRecognitionError;
+struct SpeechRecognitionResult;
+
// The interface to be implemented by consumers interested in receiving
// speech recognition events.
class CONTENT_EXPORT SpeechRecognitionEventListener {
@@ -46,7 +48,7 @@ class CONTENT_EXPORT SpeechRecognitionEventListener {
// The recognition has already been cancelled when this call is made and
// no more events will be raised.
virtual void OnRecognitionError(int caller_id,
- const SpeechRecognitionErrorCode& error) = 0;
+ const SpeechRecognitionError& error) = 0;
// Informs of a change in the captured audio level, useful if displaying
// a microphone volume indicator while recording.
View
2  content/public/browser/speech_recognition_manager_delegate.h
@@ -8,7 +8,7 @@
#include <string>
-#include "content/public/common/speech_recognition_result.h"
+#include "content/public/common/speech_recognition_error.h"
namespace gfx {
class Rect;
View
2  content/public/browser/speech_recognizer.h
@@ -46,7 +46,7 @@ class SpeechRecognizer : public base::RefCountedThreadSafe<SpeechRecognizer> {
// though each recognition request can be made only after the previous one
// completes (i.e. after receiving
// SpeechRecognitionEventListener::OnRecognitionEnd).
- virtual bool StartRecognition() = 0;
+ virtual void StartRecognition() = 0;
// Stops recording audio and cancels recognition. Any audio recorded so far
// gets discarded.
View
51 content/public/common/speech_recognition_error.h
@@ -0,0 +1,51 @@
+// 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 CONTENT_PUBLIC_COMMON_SPEECH_RECOGNITION_ERROR_H_
+#define CONTENT_PUBLIC_COMMON_SPEECH_RECOGNITION_ERROR_H_
+
+namespace content {
+
+// This enumeration follows the values described here:
+// http://www.w3.org/2005/Incubator/htmlspeech/2010/10/google-api-draft.html#speech-input-error
+enum SpeechRecognitionErrorCode {
+ // There was no error.
+ SPEECH_RECOGNITION_ERROR_NONE = 0,
+ // The user or a script aborted speech input.
+ SPEECH_RECOGNITION_ERROR_ABORTED,
+ // There was an error with recording audio.
+ SPEECH_RECOGNITION_ERROR_AUDIO,
+ // There was a network error.
+ SPEECH_RECOGNITION_ERROR_NETWORK,
+ // No speech heard before timeout.
+ SPEECH_RECOGNITION_ERROR_NO_SPEECH,
+ // Speech was heard, but could not be interpreted.
+ SPEECH_RECOGNITION_ERROR_NO_MATCH,
+ // There was an error in the speech recognition grammar.
+ SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR,
+};
+
+// Error details for the SPEECH_RECOGNITION_ERROR_AUDIO error.
+enum SpeechAudioErrorDetails {
+ SPEECH_AUDIO_ERROR_DETAILS_NONE = 0,
+ SPEECH_AUDIO_ERROR_DETAILS_NO_MIC,
+ SPEECH_AUDIO_ERROR_DETAILS_IN_USE
+};
+
+struct CONTENT_EXPORT SpeechRecognitionError {
+ SpeechRecognitionErrorCode code;
+ SpeechAudioErrorDetails details;
+
+ SpeechRecognitionError(SpeechRecognitionErrorCode code_value)
+ : code(code_value),
+ details(SPEECH_AUDIO_ERROR_DETAILS_NONE) {}
+ SpeechRecognitionError(SpeechRecognitionErrorCode code_value,
+ SpeechAudioErrorDetails details_value)
+ : code(code_value),
+ details(details_value) {}
+};
+
+} // namespace content
+
+#endif // CONTENT_PUBLIC_COMMON_SPEECH_RECOGNITION_ERROR_H_
View
3  content/public/common/speech_recognition_result.cc
@@ -6,8 +6,7 @@
namespace content {
-SpeechRecognitionResult::SpeechRecognitionResult()
- : error(SPEECH_RECOGNITION_ERROR_NONE) {
+SpeechRecognitionResult::SpeechRecognitionResult() {
}
SpeechRecognitionResult::~SpeechRecognitionResult() {
View
20 content/public/common/speech_recognition_result.h
@@ -29,27 +29,7 @@ struct SpeechRecognitionHypothesis {
typedef std::vector<SpeechRecognitionHypothesis>
SpeechRecognitionHypothesisArray;
-// This enumeration follows the values described here:
-// http://www.w3.org/2005/Incubator/htmlspeech/2010/10/google-api-draft.html#speech-input-error
-enum SpeechRecognitionErrorCode {
- // There was no error.
- SPEECH_RECOGNITION_ERROR_NONE = 0,
- // The user or a script aborted speech input.
- SPEECH_RECOGNITION_ERROR_ABORTED,
- // There was an error with recording audio.
- SPEECH_RECOGNITION_ERROR_AUDIO,
- // There was a network error.
- SPEECH_RECOGNITION_ERROR_NETWORK,
- // No speech heard before timeout.
- SPEECH_RECOGNITION_ERROR_NO_SPEECH,
- // Speech was heard, but could not be interpreted.
- SPEECH_RECOGNITION_ERROR_NO_MATCH,
- // There was an error in the speech recognition grammar.
- SPEECH_RECOGNITION_ERROR_BAD_GRAMMAR,
-};
-
struct CONTENT_EXPORT SpeechRecognitionResult {
- SpeechRecognitionErrorCode error;
SpeechRecognitionHypothesisArray hypotheses;
SpeechRecognitionResult();
Please sign in to comment.
Something went wrong with that request. Please try again.