blob: 4bdbb488e5ccbf527bfd01f5cd9262400944dd03 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2013 The Chromium Authors
[email protected]4b9a8c32010-08-12 19:57:312// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
danakj569d4de842024-04-03 16:53:415#include "content/browser/speech/speech_recognizer_impl.h"
6
avib7348942015-12-25 20:57:107#include <stddef.h>
8#include <stdint.h>
9
Peter Boströmdd7e40ec2021-04-05 20:40:1010#include <memory>
[email protected]6523152d2010-09-09 13:41:2411#include <vector>
12
Tom Sepez3e425102025-07-23 12:16:2013#include "base/compiler_specific.h"
danakj569d4de842024-04-03 16:53:4114#include "base/containers/span.h"
Avi Drissmanadac21992023-01-11 23:46:3915#include "base/functional/bind.h"
16#include "base/functional/callback_helpers.h"
Matt Menke7b2266e2018-06-07 19:32:0917#include "base/memory/ref_counted.h"
Henrique Ferreiro3252b352025-02-10 17:16:5918#include "base/memory/scoped_refptr.h"
danakj569d4de842024-04-03 16:53:4119#include "base/numerics/byte_conversions.h"
fdoray896bea12016-06-10 15:52:0120#include "base/run_loop.h"
Daniel Cheng17390fd2025-06-07 06:38:2621#include "base/strings/string_view_util.h"
olkaef762c92017-02-06 16:45:1622#include "base/synchronization/waitable_event.h"
Ken Rockot72964402019-12-06 10:40:5123#include "base/test/scoped_feature_list.h"
olkaef762c92017-02-06 16:45:1624#include "base/threading/thread.h"
Yaowei Zhouf7df39c2024-02-29 04:53:4825#include "content/browser/speech/network_speech_recognition_engine_impl.h"
evliuad7f9cc2020-06-18 01:10:4126#include "content/public/browser/google_streaming_api.pb.h"
Evan Liu881ab7a2024-08-01 21:54:5127#include "content/public/browser/speech_recognition_audio_forwarder_config.h"
[email protected]d1a4c792012-03-14 20:29:5128#include "content/public/browser/speech_recognition_event_listener.h"
Ken Rockot72964402019-12-06 10:40:5129#include "content/public/common/content_features.h"
Gabriel Charettec7108742019-08-23 03:31:4030#include "content/public/test/browser_task_environment.h"
olka251dd5692016-04-27 15:50:1731#include "media/audio/audio_device_description.h"
olkaef762c92017-02-06 16:45:1632#include "media/audio/audio_system_impl.h"
[email protected]fad64e72012-03-13 23:57:5133#include "media/audio/fake_audio_input_stream.h"
34#include "media/audio/fake_audio_output_stream.h"
[email protected]ce1adc342013-05-20 13:35:4335#include "media/audio/mock_audio_manager.h"
alokp2cbd4fc2017-05-13 04:13:2436#include "media/audio/test_audio_thread.h"
[email protected]f361a0d2014-06-19 12:38:5637#include "media/base/audio_bus.h"
Fredrik Hernqvist35a81352024-03-04 15:03:5538#include "media/base/audio_glitch_info.h"
David Sandersd90884762025-06-11 01:09:5439#include "media/base/audio_sample_types.h"
olkaef762c92017-02-06 16:45:1640#include "media/base/test_helpers.h"
Julie Jeongeun Kim8280a462019-09-11 14:25:3341#include "mojo/public/cpp/bindings/remote.h"
Matt Menke7b2266e2018-06-07 19:32:0942#include "mojo/public/cpp/system/data_pipe.h"
43#include "mojo/public/cpp/system/data_pipe_utils.h"
[email protected]441c6c32011-03-07 17:24:1344#include "net/base/net_errors.h"
Matt Menke7b2266e2018-06-07 19:32:0945#include "net/http/http_response_headers.h"
46#include "net/http/http_util.h"
Matt Menke7b2266e2018-06-07 19:32:0947#include "services/network/public/cpp/url_loader_completion_status.h"
48#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
Lucas Furukawa Gadanib8674782019-12-11 16:22:5849#include "services/network/public/mojom/url_response_head.mojom.h"
Matt Menke7b2266e2018-06-07 19:32:0950#include "services/network/test/test_url_loader_factory.h"
[email protected]4b9a8c32010-08-12 19:57:3151#include "testing/gtest/include/gtest/gtest.h"
52
[email protected]67dfea902012-04-03 01:49:0953using media::AudioInputStream;
[email protected]67dfea902012-04-03 01:49:0954using media::AudioOutputStream;
55using media::AudioParameters;
[email protected]4b9a8c32010-08-12 19:57:3156
[email protected]fcb8e0212012-10-29 11:57:1857namespace content {
[email protected]4b9a8c32010-08-12 19:57:3158
Marina Ciocea68e948e2018-05-10 22:26:1759namespace {
60
61class MockCapturerSource : public media::AudioCapturerSource {
62 public:
63 MockCapturerSource() = default;
64 MOCK_METHOD2(Initialize,
65 void(const media::AudioParameters& params,
66 CaptureCallback* callback));
67 MOCK_METHOD0(Start, void());
68 MOCK_METHOD0(Stop, void());
69 MOCK_METHOD1(SetAutomaticGainControl, void(bool enable));
70 MOCK_METHOD1(SetVolume, void(double volume));
Oskar Sundbom48711ad2018-05-24 14:00:3971 MOCK_METHOD1(SetOutputDeviceForAec,
72 void(const std::string& output_device_id));
Marina Ciocea68e948e2018-05-10 22:26:1773
74 protected:
75 ~MockCapturerSource() override = default;
76};
77
78} // namespace
79
[email protected]ce1adc342013-05-20 13:35:4380class SpeechRecognizerImplTest : public SpeechRecognitionEventListener,
81 public testing::Test {
[email protected]4b9a8c32010-08-12 19:57:3182 public:
[email protected]ce1adc342013-05-20 13:35:4383 SpeechRecognizerImplTest()
Henrique Ferreiro3252b352025-02-10 17:16:5984 : audio_capturer_source_(
85 base::MakeRefCounted<testing::NiceMock<MockCapturerSource>>()),
Marina Ciocea68e948e2018-05-10 22:26:1786 recognition_started_(false),
[email protected]d1a4c792012-03-14 20:29:5187 recognition_ended_(false),
[email protected]e5a00ca2010-08-27 13:34:0988 result_received_(false),
[email protected]d1a4c792012-03-14 20:29:5189 audio_started_(false),
[email protected]2ba06442012-04-13 13:06:3990 audio_ended_(false),
91 sound_started_(false),
92 sound_ended_(false),
Evan Liud7252282024-05-16 20:10:3293 error_(media::mojom::SpeechRecognitionErrorCode::kNone),
[email protected]fc89d8ae2010-09-16 12:07:5794 volume_(-1.0f) {
Ken Rockot72964402019-12-06 10:40:5195 // This test environment is not set up to support out-of-process services.
96 feature_list_.InitWithFeatures(
97 /*enabled_features=*/{},
98 /*disabled_features=*/{features::kAudioServiceOutOfProcess});
99
[email protected]c91bb262012-06-27 10:56:45100 // SpeechRecognizer takes ownership of sr_engine.
Yaowei Zhouf7df39c2024-02-29 04:53:48101 std::unique_ptr<NetworkSpeechRecognitionEngineImpl> sr_engine =
102 std::make_unique<NetworkSpeechRecognitionEngineImpl>(
103 base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
Evan Liuc13009642024-11-06 23:09:28104 &url_loader_factory_));
Yaowei Zhouf7df39c2024-02-29 04:53:48105 NetworkSpeechRecognitionEngineImpl::Config config;
[email protected]ce1adc342013-05-20 13:35:43106 config.audio_num_bits_per_sample =
107 SpeechRecognizerImpl::kNumBitsPerAudioSample;
108 config.audio_sample_rate = SpeechRecognizerImpl::kAudioSampleRate;
[email protected]2ba06442012-04-13 13:06:39109 config.filter_profanities = false;
110 sr_engine->SetConfig(config);
111
[email protected]c766aa92012-06-22 16:57:14112 const int kTestingSessionId = 1;
olkaef762c92017-02-06 16:45:16113
Peter Boströmdd7e40ec2021-04-05 20:40:10114 audio_manager_ = std::make_unique<media::MockAudioManager>(
115 std::make_unique<media::TestAudioThread>(true));
olkaef762c92017-02-06 16:45:16116 audio_manager_->SetInputStreamParameters(
117 media::AudioParameters::UnavailableDeviceParams());
Olga Sharonovae7b1eb22017-09-13 14:41:47118 audio_system_ =
119 std::make_unique<media::AudioSystemImpl>(audio_manager_.get());
Marina Ciocea68e948e2018-05-10 22:26:17120 SpeechRecognizerImpl::SetAudioEnvironmentForTesting(
121 audio_system_.get(), audio_capturer_source_.get());
Evan Liu881ab7a2024-08-01 21:54:51122 recognizer_ = new SpeechRecognizerImpl(this, audio_system_.get(),
123 kTestingSessionId, false, false,
124 std::move(sr_engine), std::nullopt);
[email protected]2ba06442012-04-13 13:06:39125
[email protected]6523152d2010-09-09 13:41:24126 int audio_packet_length_bytes =
[email protected]ce1adc342013-05-20 13:35:43127 (SpeechRecognizerImpl::kAudioSampleRate *
Yaowei Zhouf7df39c2024-02-29 04:53:48128 NetworkSpeechRecognitionEngineImpl::kAudioPacketIntervalMs *
[email protected]ce1adc342013-05-20 13:35:43129 ChannelLayoutToChannelCount(SpeechRecognizerImpl::kChannelLayout) *
Yaowei Zhouf7df39c2024-02-29 04:53:48130 SpeechRecognizerImpl::kNumBitsPerAudioSample) /
131 (8 * 1000);
[email protected]6523152d2010-09-09 13:41:24132 audio_packet_.resize(audio_packet_length_bytes);
[email protected]f361a0d2014-06-19 12:38:56133
134 const int channels =
135 ChannelLayoutToChannelCount(SpeechRecognizerImpl::kChannelLayout);
Raul Tambreb1da2442019-04-07 18:18:03136 int bytes_per_sample = SpeechRecognizerImpl::kNumBitsPerAudioSample / 8;
137 const int frames = audio_packet_length_bytes / channels / bytes_per_sample;
[email protected]f361a0d2014-06-19 12:38:56138 audio_bus_ = media::AudioBus::Create(channels, frames);
139 audio_bus_->Zero();
[email protected]4b9a8c32010-08-12 19:57:31140 }
141
Marina Ciocea68e948e2018-05-10 22:26:17142 ~SpeechRecognizerImplTest() override {
143 SpeechRecognizerImpl::SetAudioEnvironmentForTesting(nullptr, nullptr);
144 audio_manager_->Shutdown();
145 }
olkaef762c92017-02-06 16:45:16146
Daniel Cheng9c9fa1a2022-01-14 03:42:11147 [[nodiscard]] bool GetUpstreamRequest(
148 const network::TestURLLoaderFactory::PendingRequest**
149 pending_request_out) {
Matt Menke7b2266e2018-06-07 19:32:09150 return GetPendingRequest(pending_request_out, "/up");
151 }
152
Daniel Cheng9c9fa1a2022-01-14 03:42:11153 [[nodiscard]] bool GetDownstreamRequest(
154 const network::TestURLLoaderFactory::PendingRequest**
155 pending_request_out) {
Matt Menke7b2266e2018-06-07 19:32:09156 return GetPendingRequest(pending_request_out, "/down");
157 }
158
Daniel Cheng9c9fa1a2022-01-14 03:42:11159 [[nodiscard]] bool GetPendingRequest(
Matt Menke7b2266e2018-06-07 19:32:09160 const network::TestURLLoaderFactory::PendingRequest** pending_request_out,
Daniel Cheng9c9fa1a2022-01-14 03:42:11161 const char* url_substring) {
Matt Menke7b2266e2018-06-07 19:32:09162 for (const auto& pending_request :
163 *url_loader_factory_.pending_requests()) {
John Abd-El-Malek04199db2018-06-28 17:33:39164 if (pending_request.request.url.spec().find(url_substring) !=
165 std::string::npos) {
Matt Menke7b2266e2018-06-07 19:32:09166 *pending_request_out = &pending_request;
167 return true;
168 }
169 }
170 return false;
171 }
172
[email protected]2ba06442012-04-13 13:06:39173 void CheckEventsConsistency() {
174 // Note: "!x || y" == "x implies y".
175 EXPECT_TRUE(!recognition_ended_ || recognition_started_);
176 EXPECT_TRUE(!audio_ended_ || audio_started_);
177 EXPECT_TRUE(!sound_ended_ || sound_started_);
178 EXPECT_TRUE(!audio_started_ || recognition_started_);
179 EXPECT_TRUE(!sound_started_ || audio_started_);
180 EXPECT_TRUE(!audio_ended_ || (sound_ended_ || !sound_started_));
181 EXPECT_TRUE(!recognition_ended_ || (audio_ended_ || !audio_started_));
182 }
183
184 void CheckFinalEventsConsistency() {
185 // Note: "!(x ^ y)" == "(x && y) || (!x && !x)".
186 EXPECT_FALSE(recognition_started_ ^ recognition_ended_);
187 EXPECT_FALSE(audio_started_ ^ audio_ended_);
188 EXPECT_FALSE(sound_started_ ^ sound_ended_);
189 }
190
[email protected]fcb8e0212012-10-29 11:57:18191 // Overridden from SpeechRecognitionEventListener:
dchengc2282aa2014-10-21 12:07:58192 void OnAudioStart(int session_id) override {
[email protected]d1a4c792012-03-14 20:29:51193 audio_started_ = true;
[email protected]2ba06442012-04-13 13:06:39194 CheckEventsConsistency();
[email protected]d1a4c792012-03-14 20:29:51195 }
196
dchengc2282aa2014-10-21 12:07:58197 void OnAudioEnd(int session_id) override {
[email protected]d1a4c792012-03-14 20:29:51198 audio_ended_ = true;
[email protected]2ba06442012-04-13 13:06:39199 CheckEventsConsistency();
[email protected]d1a4c792012-03-14 20:29:51200 }
201
Adithya Srinivasane75e3282018-06-01 15:09:00202 void OnRecognitionResults(
203 int session_id,
Evan Liud7252282024-05-16 20:10:32204 const std::vector<media::mojom::WebSpeechRecognitionResultPtr>& results)
Adithya Srinivasanc35bf3962018-06-12 14:28:14205 override {
[email protected]4b9a8c32010-08-12 19:57:31206 result_received_ = true;
207 }
208
Adithya Srinivasanc35bf3962018-06-12 14:28:14209 void OnRecognitionError(
210 int session_id,
Evan Liud7252282024-05-16 20:10:32211 const media::mojom::SpeechRecognitionError& error) override {
[email protected]2ba06442012-04-13 13:06:39212 EXPECT_TRUE(recognition_started_);
213 EXPECT_FALSE(recognition_ended_);
[email protected]7527adf42012-03-26 11:56:02214 error_ = error.code;
[email protected]e5a00ca2010-08-27 13:34:09215 }
216
dchengc2282aa2014-10-21 12:07:58217 void OnAudioLevelsChange(int session_id,
218 float volume,
219 float noise_volume) override {
[email protected]fc89d8ae2010-09-16 12:07:57220 volume_ = volume;
[email protected]3b283d62011-03-01 18:38:36221 noise_volume_ = noise_volume;
[email protected]fc89d8ae2010-09-16 12:07:57222 }
223
dchengc2282aa2014-10-21 12:07:58224 void OnRecognitionEnd(int session_id) override {
[email protected]d1a4c792012-03-14 20:29:51225 recognition_ended_ = true;
[email protected]2ba06442012-04-13 13:06:39226 CheckEventsConsistency();
[email protected]d1a4c792012-03-14 20:29:51227 }
228
dchengc2282aa2014-10-21 12:07:58229 void OnRecognitionStart(int session_id) override {
[email protected]2ba06442012-04-13 13:06:39230 recognition_started_ = true;
231 CheckEventsConsistency();
232 }
233
dchengc2282aa2014-10-21 12:07:58234 void OnSoundStart(int session_id) override {
[email protected]2ba06442012-04-13 13:06:39235 sound_started_ = true;
236 CheckEventsConsistency();
237 }
238
dchengc2282aa2014-10-21 12:07:58239 void OnSoundEnd(int session_id) override {
[email protected]2ba06442012-04-13 13:06:39240 sound_ended_ = true;
241 CheckEventsConsistency();
242 }
[email protected]d1a4c792012-03-14 20:29:51243
[email protected]f361a0d2014-06-19 12:38:56244 void CopyPacketToAudioBus() {
Raul Tambreb1da2442019-04-07 18:18:03245 static_assert(SpeechRecognizerImpl::kNumBitsPerAudioSample == 16,
246 "FromInterleaved expects 2 bytes.");
[email protected]f361a0d2014-06-19 12:38:56247 // Copy the created signal into an audio bus in a deinterleaved format.
Raul Tambreb1da2442019-04-07 18:18:03248 audio_bus_->FromInterleaved<media::SignedInt16SampleTypeTraits>(
Tom Sepez3e425102025-07-23 12:16:20249 UNSAFE_TODO(reinterpret_cast<int16_t*>(audio_packet_.data())),
250 audio_bus_->frames());
[email protected]f361a0d2014-06-19 12:38:56251 }
252
[email protected]fc89d8ae2010-09-16 12:07:57253 void FillPacketWithTestWaveform() {
254 // Fill the input with a simple pattern, a 125Hz sawtooth waveform.
255 for (size_t i = 0; i < audio_packet_.size(); ++i)
avib7348942015-12-25 20:57:10256 audio_packet_[i] = static_cast<uint8_t>(i);
[email protected]f361a0d2014-06-19 12:38:56257 CopyPacketToAudioBus();
[email protected]fc89d8ae2010-09-16 12:07:57258 }
259
[email protected]3b283d62011-03-01 18:38:36260 void FillPacketWithNoise() {
261 int value = 0;
262 int factor = 175;
263 for (size_t i = 0; i < audio_packet_.size(); ++i) {
264 value += factor;
265 audio_packet_[i] = value % 100;
266 }
[email protected]f361a0d2014-06-19 12:38:56267 CopyPacketToAudioBus();
[email protected]3b283d62011-03-01 18:38:36268 }
269
Marina Ciocea68e948e2018-05-10 22:26:17270 void Capture(media::AudioBus* data) {
271 auto* capture_callback =
272 static_cast<media::AudioCapturerSource::CaptureCallback*>(
273 recognizer_.get());
Fredrik Hernqvist590f65762024-11-14 13:42:15274 capture_callback->Capture(data, base::TimeTicks::Now(), {}, 0.0);
Marina Ciocea68e948e2018-05-10 22:26:17275 }
276
277 void OnCaptureError() {
278 auto* capture_callback =
279 static_cast<media::AudioCapturerSource::CaptureCallback*>(
280 recognizer_.get());
Tony Herre003731d2021-05-25 22:09:56281 capture_callback->OnCaptureError(
282 media::AudioCapturerSource::ErrorCode::kUnknown, "");
tommice9a2512017-01-16 18:35:18283 }
284
olkaef762c92017-02-06 16:45:16285 void WaitForAudioThreadToPostDeviceInfo() {
286 media::WaitableMessageLoopEvent event;
alokp2cbd4fc2017-05-13 04:13:24287 audio_manager_->GetTaskRunner()->PostTaskAndReply(
Peter Kasting341e1fb2018-02-24 00:03:01288 FROM_HERE, base::DoNothing(), event.GetClosure());
alokp2cbd4fc2017-05-13 04:13:24289 // Runs the loop and waits for the audio thread to call event's closure,
olkaef762c92017-02-06 16:45:16290 // which means AudioSystem reply containing device parameters is already
291 // queued on the main thread.
292 event.RunAndWait();
293 }
294
[email protected]4b9a8c32010-08-12 19:57:31295 protected:
Ken Rockot72964402019-12-06 10:40:51296 base::test::ScopedFeatureList feature_list_;
Gabriel Charette798fde72019-08-20 22:24:04297 BrowserTaskEnvironment task_environment_;
Matt Menke7b2266e2018-06-07 19:32:09298 network::TestURLLoaderFactory url_loader_factory_;
[email protected]ce1adc342013-05-20 13:35:43299 scoped_refptr<SpeechRecognizerImpl> recognizer_;
alokp2cbd4fc2017-05-13 04:13:24300 std::unique_ptr<media::MockAudioManager> audio_manager_;
olkaef762c92017-02-06 16:45:16301 std::unique_ptr<media::AudioSystem> audio_system_;
Marina Ciocea68e948e2018-05-10 22:26:17302 scoped_refptr<MockCapturerSource> audio_capturer_source_;
[email protected]2ba06442012-04-13 13:06:39303 bool recognition_started_;
[email protected]d1a4c792012-03-14 20:29:51304 bool recognition_ended_;
[email protected]4b9a8c32010-08-12 19:57:31305 bool result_received_;
[email protected]d1a4c792012-03-14 20:29:51306 bool audio_started_;
[email protected]2ba06442012-04-13 13:06:39307 bool audio_ended_;
308 bool sound_started_;
309 bool sound_ended_;
Evan Liud7252282024-05-16 20:10:32310 media::mojom::SpeechRecognitionErrorCode error_;
avib7348942015-12-25 20:57:10311 std::vector<uint8_t> audio_packet_;
dcheng59716272016-04-09 05:19:08312 std::unique_ptr<media::AudioBus> audio_bus_;
[email protected]fc89d8ae2010-09-16 12:07:57313 float volume_;
[email protected]3b283d62011-03-01 18:38:36314 float noise_volume_;
[email protected]4b9a8c32010-08-12 19:57:31315};
316
olkaef762c92017-02-06 16:45:16317TEST_F(SpeechRecognizerImplTest, StartNoInputDevices) {
318 // Check for callbacks when stopping record before any audio gets recorded.
319 audio_manager_->SetHasInputDevices(false);
320 recognizer_->StartRecognition(
321 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24322 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16323 WaitForAudioThreadToPostDeviceInfo();
olka0bf2c9cd2017-02-10 11:56:24324 base::RunLoop().RunUntilIdle(); // EVENT_START processing.
olkaef762c92017-02-06 16:45:16325 EXPECT_TRUE(recognition_started_);
326 EXPECT_FALSE(audio_started_);
327 EXPECT_FALSE(result_received_);
Dale Curtis175ea992023-09-28 22:42:42328 OnCaptureError();
329 base::RunLoop().RunUntilIdle();
Evan Liud7252282024-05-16 20:10:32330 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kAudioCapture, error_);
olkaef762c92017-02-06 16:45:16331 CheckFinalEventsConsistency();
332}
333
Dale Curtis175ea992023-09-28 22:42:42334TEST_F(SpeechRecognizerImplTest, StartFakeInputDevice) {
335 // Check for callbacks when stopping record before any audio gets recorded.
336 audio_manager_->SetHasInputDevices(false);
337 recognizer_->StartRecognition(
338 media::AudioDeviceDescription::kDefaultDeviceId);
339 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
340 WaitForAudioThreadToPostDeviceInfo();
341 base::RunLoop().RunUntilIdle(); // EVENT_START processing.
342 Capture(audio_bus_.get());
343 recognizer_->StopAudioCapture();
344 base::RunLoop().RunUntilIdle();
345 EXPECT_TRUE(recognition_started_);
346 EXPECT_TRUE(audio_started_);
347 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32348 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_);
Dale Curtis175ea992023-09-28 22:42:42349 recognizer_->AbortRecognition();
350 base::RunLoop().RunUntilIdle();
351 CheckFinalEventsConsistency();
352}
353
olkaef762c92017-02-06 16:45:16354TEST_F(SpeechRecognizerImplTest, StopBeforeDeviceInfoReceived) {
355 // Check for callbacks when stopping record before reply is received from
356 // AudioSystem.
357 base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
358 base::WaitableEvent::InitialState::NOT_SIGNALED);
359
360 // Block audio thread.
alokp2cbd4fc2017-05-13 04:13:24361 audio_manager_->GetTaskRunner()->PostTask(
olkaef762c92017-02-06 16:45:16362 FROM_HERE,
tzike2aca992017-09-05 08:50:54363 base::BindOnce(&base::WaitableEvent::Wait, base::Unretained(&event)));
olkaef762c92017-02-06 16:45:16364
365 recognizer_->StartRecognition(
366 media::AudioDeviceDescription::kDefaultDeviceId);
367 recognizer_->StopAudioCapture();
368 base::RunLoop().RunUntilIdle();
369
370 // Release audio thread and receive a callback from it.
371 event.Signal();
372 WaitForAudioThreadToPostDeviceInfo();
373 base::RunLoop().RunUntilIdle();
374
375 EXPECT_TRUE(recognition_started_);
376 EXPECT_FALSE(audio_started_);
377 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32378 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_);
olkaef762c92017-02-06 16:45:16379 CheckFinalEventsConsistency();
380}
381
382TEST_F(SpeechRecognizerImplTest, CancelBeforeDeviceInfoReceived) {
383 // Check for callbacks when stopping record before reply is received from
384 // AudioSystem.
385 base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC,
386 base::WaitableEvent::InitialState::NOT_SIGNALED);
387
388 // Block audio thread.
alokp2cbd4fc2017-05-13 04:13:24389 audio_manager_->GetTaskRunner()->PostTask(
olkaef762c92017-02-06 16:45:16390 FROM_HERE,
tzike2aca992017-09-05 08:50:54391 base::BindOnce(&base::WaitableEvent::Wait, base::Unretained(&event)));
olkaef762c92017-02-06 16:45:16392
393 recognizer_->StartRecognition(
394 media::AudioDeviceDescription::kDefaultDeviceId);
395 recognizer_->AbortRecognition();
396 base::RunLoop().RunUntilIdle();
397
398 // Release audio thread and receive a callback from it.
399 event.Signal();
400 WaitForAudioThreadToPostDeviceInfo();
401 base::RunLoop().RunUntilIdle();
402
403 EXPECT_TRUE(recognition_started_);
404 EXPECT_FALSE(audio_started_);
405 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32406 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_);
olkaef762c92017-02-06 16:45:16407 CheckFinalEventsConsistency();
408}
409
[email protected]ce1adc342013-05-20 13:35:43410TEST_F(SpeechRecognizerImplTest, StopNoData) {
[email protected]4b9a8c32010-08-12 19:57:31411 // Check for callbacks when stopping record before any audio gets recorded.
olka251dd5692016-04-27 15:50:17412 recognizer_->StartRecognition(
413 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24414 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16415 WaitForAudioThreadToPostDeviceInfo();
[email protected]12f4fb92012-05-16 10:30:16416 recognizer_->StopAudioCapture();
olka0bf2c9cd2017-02-10 11:56:24417 base::RunLoop().RunUntilIdle(); // EVENT_START and EVENT_STOP processing.
[email protected]2ba06442012-04-13 13:06:39418 EXPECT_TRUE(recognition_started_);
[email protected]d1a4c792012-03-14 20:29:51419 EXPECT_FALSE(audio_started_);
[email protected]2ba06442012-04-13 13:06:39420 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32421 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_);
[email protected]2ba06442012-04-13 13:06:39422 CheckFinalEventsConsistency();
[email protected]4b9a8c32010-08-12 19:57:31423}
424
[email protected]ce1adc342013-05-20 13:35:43425TEST_F(SpeechRecognizerImplTest, CancelNoData) {
[email protected]4b9a8c32010-08-12 19:57:31426 // Check for callbacks when canceling recognition before any audio gets
427 // recorded.
olka251dd5692016-04-27 15:50:17428 recognizer_->StartRecognition(
429 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24430 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16431 WaitForAudioThreadToPostDeviceInfo();
[email protected]12f4fb92012-05-16 10:30:16432 recognizer_->AbortRecognition();
olka0bf2c9cd2017-02-10 11:56:24433 base::RunLoop().RunUntilIdle(); // EVENT_START and EVENT_ABORT processing.
[email protected]2ba06442012-04-13 13:06:39434 EXPECT_TRUE(recognition_started_);
[email protected]d1a4c792012-03-14 20:29:51435 EXPECT_FALSE(audio_started_);
[email protected]2ba06442012-04-13 13:06:39436 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32437 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kAborted, error_);
[email protected]2ba06442012-04-13 13:06:39438 CheckFinalEventsConsistency();
[email protected]4b9a8c32010-08-12 19:57:31439}
440
[email protected]ce1adc342013-05-20 13:35:43441TEST_F(SpeechRecognizerImplTest, StopWithData) {
[email protected]4b9a8c32010-08-12 19:57:31442 // Start recording, give some data and then stop. This should wait for the
443 // network callback to arrive before completion.
olka251dd5692016-04-27 15:50:17444 recognizer_->StartRecognition(
445 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24446 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16447 WaitForAudioThreadToPostDeviceInfo();
olka0bf2c9cd2017-02-10 11:56:24448 base::RunLoop().RunUntilIdle(); // EVENT_START processing.
[email protected]deedcc82011-03-04 23:19:01449
450 // Try sending 5 chunks of mock audio data and verify that each of them
451 // resulted immediately in a packet sent out via the network. This verifies
452 // that we are streaming out encoded data as chunks without waiting for the
453 // full recording to complete.
454 const size_t kNumChunks = 5;
Julie Jeongeun Kim8280a462019-09-11 14:25:33455 mojo::Remote<network::mojom::ChunkedDataPipeGetter> chunked_data_pipe_getter;
Robert Sesek76af6692021-01-29 20:57:23456 mojo::ScopedDataPipeProducerHandle producer_handle;
457 mojo::ScopedDataPipeConsumerHandle consumer_handle;
458 ASSERT_EQ(mojo::CreateDataPipe(nullptr, producer_handle, consumer_handle),
459 MOJO_RESULT_OK);
[email protected]deedcc82011-03-04 23:19:01460 for (size_t i = 0; i < kNumChunks; ++i) {
Marina Ciocea68e948e2018-05-10 22:26:17461 Capture(audio_bus_.get());
Matt Menke7b2266e2018-06-07 19:32:09462
463 if (i == 0) {
464 // Set up data channel to read chunked upload data. Must be done after the
465 // first OnData() call.
466 base::RunLoop().RunUntilIdle();
467 const network::TestURLLoaderFactory::PendingRequest* upstream_request;
468 ASSERT_TRUE(GetUpstreamRequest(&upstream_request));
John Abd-El-Malek04199db2018-06-28 17:33:39469 ASSERT_TRUE(upstream_request->request.request_body);
470 ASSERT_EQ(1u, upstream_request->request.request_body->elements()->size());
Yutaka Hirano69d394f2021-01-13 11:38:43471 auto& element =
472 (*upstream_request->request.request_body->elements_mutable())[0];
473 ASSERT_EQ(network::DataElement::Tag::kChunkedDataPipe, element.type());
474 chunked_data_pipe_getter.Bind(
475 element.As<network::DataElementChunkedDataPipe>()
476 .ReleaseChunkedDataPipeGetter());
Robert Sesek76af6692021-01-29 20:57:23477 chunked_data_pipe_getter->StartReading(std::move(producer_handle));
Matt Menke7b2266e2018-06-07 19:32:09478 }
479
480 std::string data;
481 while (true) {
482 base::RunLoop().RunUntilIdle();
483
Lukasz Anforowicz2a672e32024-06-14 17:27:49484 base::span<const uint8_t> buffer;
485 MojoResult result =
486 consumer_handle->BeginReadData(MOJO_READ_DATA_FLAG_NONE, buffer);
Matt Menke7b2266e2018-06-07 19:32:09487 if (result == MOJO_RESULT_OK) {
Lukasz Anforowicz2a672e32024-06-14 17:27:49488 data.append(base::as_string_view(buffer));
489 consumer_handle->EndReadData(buffer.size());
Matt Menke7b2266e2018-06-07 19:32:09490 continue;
491 }
492 if (result == MOJO_RESULT_SHOULD_WAIT) {
493 // Some data has already been read, assume there's no more to read.
494 if (!data.empty())
495 break;
496 continue;
497 }
498
499 FAIL() << "Mojo pipe closed unexpectedly";
500 }
501
502 EXPECT_FALSE(data.empty());
[email protected]deedcc82011-03-04 23:19:01503 }
504
[email protected]d1a4c792012-03-14 20:29:51505 recognizer_->StopAudioCapture();
fdoray896bea12016-06-10 15:52:01506 base::RunLoop().RunUntilIdle();
[email protected]d1a4c792012-03-14 20:29:51507 EXPECT_TRUE(audio_started_);
508 EXPECT_TRUE(audio_ended_);
509 EXPECT_FALSE(recognition_ended_);
[email protected]4b9a8c32010-08-12 19:57:31510 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32511 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_);
[email protected]4b9a8c32010-08-12 19:57:31512
hansfa81aea2016-04-14 17:41:44513 // Create a response string.
514 proto::SpeechRecognitionEvent proto_event;
515 proto_event.set_status(proto::SpeechRecognitionEvent::STATUS_SUCCESS);
516 proto::SpeechRecognitionResult* proto_result = proto_event.add_result();
517 proto_result->set_final(true);
518 proto::SpeechRecognitionAlternative* proto_alternative =
519 proto_result->add_alternative();
520 proto_alternative->set_confidence(0.5f);
521 proto_alternative->set_transcript("123");
522 std::string msg_string;
523 proto_event.SerializeToString(&msg_string);
David Benjaminb54ed302024-07-12 14:47:25524 msg_string.insert(0u, base::as_string_view(base::U32ToBigEndian(
danakj569d4de842024-04-03 16:53:41525 base::checked_cast<uint32_t>(msg_string.size()))));
[email protected]5915a012011-09-26 14:21:27526
hansfa81aea2016-04-14 17:41:44527 // Issue the network callback to complete the process.
Matt Menke7b2266e2018-06-07 19:32:09528 const network::TestURLLoaderFactory::PendingRequest* downstream_request;
529 ASSERT_TRUE(GetDownstreamRequest(&downstream_request));
John Abd-El-Malek04199db2018-06-28 17:33:39530 url_loader_factory_.AddResponse(downstream_request->request.url.spec(),
531 msg_string);
fdoray896bea12016-06-10 15:52:01532 base::RunLoop().RunUntilIdle();
Matt Menke7b2266e2018-06-07 19:32:09533
[email protected]d1a4c792012-03-14 20:29:51534 EXPECT_TRUE(recognition_ended_);
[email protected]4b9a8c32010-08-12 19:57:31535 EXPECT_TRUE(result_received_);
Evan Liud7252282024-05-16 20:10:32536 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_);
[email protected]2ba06442012-04-13 13:06:39537 CheckFinalEventsConsistency();
[email protected]4b9a8c32010-08-12 19:57:31538}
539
[email protected]ce1adc342013-05-20 13:35:43540TEST_F(SpeechRecognizerImplTest, CancelWithData) {
[email protected]2ba06442012-04-13 13:06:39541 // Start recording, give some data and then cancel.
olka251dd5692016-04-27 15:50:17542 recognizer_->StartRecognition(
543 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24544 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16545 WaitForAudioThreadToPostDeviceInfo();
olka0bf2c9cd2017-02-10 11:56:24546 base::RunLoop().RunUntilIdle(); // EVENT_START processing.
Marina Ciocea68e948e2018-05-10 22:26:17547 Capture(audio_bus_.get());
fdoray896bea12016-06-10 15:52:01548 base::RunLoop().RunUntilIdle();
[email protected]d1a4c792012-03-14 20:29:51549 recognizer_->AbortRecognition();
fdoray896bea12016-06-10 15:52:01550 base::RunLoop().RunUntilIdle();
Matt Menke7b2266e2018-06-07 19:32:09551 // There should be both upstream and downstream pending requests.
552 ASSERT_EQ(2u, url_loader_factory_.pending_requests()->size());
[email protected]2ba06442012-04-13 13:06:39553 EXPECT_TRUE(recognition_started_);
[email protected]d1a4c792012-03-14 20:29:51554 EXPECT_TRUE(audio_started_);
[email protected]4b9a8c32010-08-12 19:57:31555 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32556 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kAborted, error_);
[email protected]2ba06442012-04-13 13:06:39557 CheckFinalEventsConsistency();
[email protected]4b9a8c32010-08-12 19:57:31558}
559
[email protected]ce1adc342013-05-20 13:35:43560TEST_F(SpeechRecognizerImplTest, ConnectionError) {
[email protected]441c6c32011-03-07 17:24:13561 // Start recording, give some data and then stop. Issue the network callback
562 // with a connection error and verify that the recognizer bubbles the error up
olka251dd5692016-04-27 15:50:17563 recognizer_->StartRecognition(
564 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24565 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16566 WaitForAudioThreadToPostDeviceInfo();
olka0bf2c9cd2017-02-10 11:56:24567 base::RunLoop().RunUntilIdle(); // EVENT_START processing.
Marina Ciocea68e948e2018-05-10 22:26:17568 Capture(audio_bus_.get());
fdoray896bea12016-06-10 15:52:01569 base::RunLoop().RunUntilIdle();
Matt Menke7b2266e2018-06-07 19:32:09570 // There should be both upstream and downstream pending requests.
571 ASSERT_EQ(2u, url_loader_factory_.pending_requests()->size());
[email protected]441c6c32011-03-07 17:24:13572
[email protected]d1a4c792012-03-14 20:29:51573 recognizer_->StopAudioCapture();
fdoray896bea12016-06-10 15:52:01574 base::RunLoop().RunUntilIdle();
[email protected]d1a4c792012-03-14 20:29:51575 EXPECT_TRUE(audio_started_);
576 EXPECT_TRUE(audio_ended_);
577 EXPECT_FALSE(recognition_ended_);
[email protected]441c6c32011-03-07 17:24:13578 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32579 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_);
[email protected]441c6c32011-03-07 17:24:13580
581 // Issue the network callback to complete the process.
Matt Menke7b2266e2018-06-07 19:32:09582 const network::TestURLLoaderFactory::PendingRequest* pending_request;
583 ASSERT_TRUE(GetUpstreamRequest(&pending_request));
584 url_loader_factory_.AddResponse(
Lucas Furukawa Gadanib8674782019-12-11 16:22:58585 pending_request->request.url, network::mojom::URLResponseHead::New(), "",
Matt Menke7b2266e2018-06-07 19:32:09586 network::URLLoaderCompletionStatus(net::ERR_CONNECTION_REFUSED));
587
fdoray896bea12016-06-10 15:52:01588 base::RunLoop().RunUntilIdle();
[email protected]2ba06442012-04-13 13:06:39589 EXPECT_TRUE(recognition_ended_);
[email protected]441c6c32011-03-07 17:24:13590 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32591 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNetwork, error_);
[email protected]2ba06442012-04-13 13:06:39592 CheckFinalEventsConsistency();
[email protected]441c6c32011-03-07 17:24:13593}
594
[email protected]ce1adc342013-05-20 13:35:43595TEST_F(SpeechRecognizerImplTest, ServerError) {
[email protected]441c6c32011-03-07 17:24:13596 // Start recording, give some data and then stop. Issue the network callback
597 // with a 500 error and verify that the recognizer bubbles the error up
olka251dd5692016-04-27 15:50:17598 recognizer_->StartRecognition(
599 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24600 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16601 WaitForAudioThreadToPostDeviceInfo();
olka0bf2c9cd2017-02-10 11:56:24602 base::RunLoop().RunUntilIdle(); // EVENT_START processing.
Marina Ciocea68e948e2018-05-10 22:26:17603 Capture(audio_bus_.get());
fdoray896bea12016-06-10 15:52:01604 base::RunLoop().RunUntilIdle();
Matt Menke7b2266e2018-06-07 19:32:09605 // There should be both upstream and downstream pending requests.
606 ASSERT_EQ(2u, url_loader_factory_.pending_requests()->size());
[email protected]441c6c32011-03-07 17:24:13607
[email protected]d1a4c792012-03-14 20:29:51608 recognizer_->StopAudioCapture();
fdoray896bea12016-06-10 15:52:01609 base::RunLoop().RunUntilIdle();
[email protected]d1a4c792012-03-14 20:29:51610 EXPECT_TRUE(audio_started_);
611 EXPECT_TRUE(audio_ended_);
612 EXPECT_FALSE(recognition_ended_);
[email protected]441c6c32011-03-07 17:24:13613 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32614 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_);
[email protected]441c6c32011-03-07 17:24:13615
Matt Menke7b2266e2018-06-07 19:32:09616 const network::TestURLLoaderFactory::PendingRequest* pending_request;
617 ASSERT_TRUE(GetUpstreamRequest(&pending_request));
Lucas Furukawa Gadanib8674782019-12-11 16:22:58618 auto response = network::mojom::URLResponseHead::New();
Matt Menke7b2266e2018-06-07 19:32:09619 const char kHeaders[] = "HTTP/1.0 500 Internal Server Error";
Lucas Furukawa Gadanib8674782019-12-11 16:22:58620 response->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
David Benjamin1384c0402019-04-29 18:55:52621 net::HttpUtil::AssembleRawHeaders(kHeaders));
Lucas Furukawa Gadanib8674782019-12-11 16:22:58622 url_loader_factory_.AddResponse(pending_request->request.url,
623 std::move(response), "",
Matt Menke7b2266e2018-06-07 19:32:09624 network::URLLoaderCompletionStatus());
625
fdoray896bea12016-06-10 15:52:01626 base::RunLoop().RunUntilIdle();
[email protected]2ba06442012-04-13 13:06:39627 EXPECT_TRUE(recognition_ended_);
[email protected]441c6c32011-03-07 17:24:13628 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32629 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNetwork, error_);
[email protected]2ba06442012-04-13 13:06:39630 CheckFinalEventsConsistency();
[email protected]441c6c32011-03-07 17:24:13631}
632
Marina Ciocea68e948e2018-05-10 22:26:17633TEST_F(SpeechRecognizerImplTest, OnCaptureError_PropagatesError) {
[email protected]4b9a8c32010-08-12 19:57:31634 // Check if things tear down properly if AudioInputController threw an error.
olka251dd5692016-04-27 15:50:17635 recognizer_->StartRecognition(
636 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24637 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16638 WaitForAudioThreadToPostDeviceInfo();
olka0bf2c9cd2017-02-10 11:56:24639 base::RunLoop().RunUntilIdle(); // EVENT_START processing.
Marina Ciocea68e948e2018-05-10 22:26:17640
641 OnCaptureError();
fdoray896bea12016-06-10 15:52:01642 base::RunLoop().RunUntilIdle();
[email protected]2ba06442012-04-13 13:06:39643 EXPECT_TRUE(recognition_started_);
[email protected]d1a4c792012-03-14 20:29:51644 EXPECT_FALSE(audio_started_);
[email protected]4b9a8c32010-08-12 19:57:31645 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32646 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kAudioCapture, error_);
[email protected]2ba06442012-04-13 13:06:39647 CheckFinalEventsConsistency();
[email protected]4b9a8c32010-08-12 19:57:31648}
649
[email protected]ce1adc342013-05-20 13:35:43650TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackIssued) {
[email protected]6523152d2010-09-09 13:41:24651 // Start recording and give a lot of packets with audio samples set to zero.
652 // This should trigger the no-speech detector and issue a callback.
olka251dd5692016-04-27 15:50:17653 recognizer_->StartRecognition(
654 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24655 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16656 WaitForAudioThreadToPostDeviceInfo();
olka0bf2c9cd2017-02-10 11:56:24657 base::RunLoop().RunUntilIdle(); // EVENT_START processing.
[email protected]6523152d2010-09-09 13:41:24658
Yaowei Zhouf7df39c2024-02-29 04:53:48659 int num_packets =
660 (SpeechRecognizerImpl::kNoSpeechTimeoutMs) /
661 NetworkSpeechRecognitionEngineImpl::kAudioPacketIntervalMs +
662 1;
[email protected]6523152d2010-09-09 13:41:24663 // The vector is already filled with zero value samples on create.
664 for (int i = 0; i < num_packets; ++i) {
Marina Ciocea68e948e2018-05-10 22:26:17665 Capture(audio_bus_.get());
[email protected]6523152d2010-09-09 13:41:24666 }
fdoray896bea12016-06-10 15:52:01667 base::RunLoop().RunUntilIdle();
[email protected]2ba06442012-04-13 13:06:39668 EXPECT_TRUE(recognition_started_);
[email protected]d1a4c792012-03-14 20:29:51669 EXPECT_TRUE(audio_started_);
[email protected]6523152d2010-09-09 13:41:24670 EXPECT_FALSE(result_received_);
Evan Liud7252282024-05-16 20:10:32671 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNoSpeech, error_);
[email protected]2ba06442012-04-13 13:06:39672 CheckFinalEventsConsistency();
[email protected]6523152d2010-09-09 13:41:24673}
674
[email protected]ce1adc342013-05-20 13:35:43675TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackNotIssued) {
[email protected]6523152d2010-09-09 13:41:24676 // Start recording and give a lot of packets with audio samples set to zero
677 // and then some more with reasonably loud audio samples. This should be
678 // treated as normal speech input and the no-speech detector should not get
679 // triggered.
olka251dd5692016-04-27 15:50:17680 recognizer_->StartRecognition(
681 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24682 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16683 WaitForAudioThreadToPostDeviceInfo();
olka0bf2c9cd2017-02-10 11:56:24684 base::RunLoop().RunUntilIdle(); // EVENT_START processing.
[email protected]6523152d2010-09-09 13:41:24685
[email protected]ce1adc342013-05-20 13:35:43686 int num_packets = (SpeechRecognizerImpl::kNoSpeechTimeoutMs) /
Yaowei Zhouf7df39c2024-02-29 04:53:48687 NetworkSpeechRecognitionEngineImpl::kAudioPacketIntervalMs;
[email protected]6523152d2010-09-09 13:41:24688
689 // The vector is already filled with zero value samples on create.
690 for (int i = 0; i < num_packets / 2; ++i) {
Marina Ciocea68e948e2018-05-10 22:26:17691 Capture(audio_bus_.get());
[email protected]6523152d2010-09-09 13:41:24692 }
[email protected]fc89d8ae2010-09-16 12:07:57693
694 FillPacketWithTestWaveform();
[email protected]6523152d2010-09-09 13:41:24695 for (int i = 0; i < num_packets / 2; ++i) {
Marina Ciocea68e948e2018-05-10 22:26:17696 Capture(audio_bus_.get());
[email protected]6523152d2010-09-09 13:41:24697 }
698
fdoray896bea12016-06-10 15:52:01699 base::RunLoop().RunUntilIdle();
Evan Liud7252282024-05-16 20:10:32700 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_);
[email protected]d1a4c792012-03-14 20:29:51701 EXPECT_TRUE(audio_started_);
702 EXPECT_FALSE(audio_ended_);
703 EXPECT_FALSE(recognition_ended_);
704 recognizer_->AbortRecognition();
fdoray896bea12016-06-10 15:52:01705 base::RunLoop().RunUntilIdle();
[email protected]2ba06442012-04-13 13:06:39706 CheckFinalEventsConsistency();
[email protected]4b9a8c32010-08-12 19:57:31707}
708
[email protected]ce1adc342013-05-20 13:35:43709TEST_F(SpeechRecognizerImplTest, SetInputVolumeCallback) {
[email protected]fc89d8ae2010-09-16 12:07:57710 // Start recording and give a lot of packets with audio samples set to zero
711 // and then some more with reasonably loud audio samples. Check that we don't
712 // get the callback during estimation phase, then get zero for the silence
713 // samples and proper volume for the loud audio.
olka251dd5692016-04-27 15:50:17714 recognizer_->StartRecognition(
715 media::AudioDeviceDescription::kDefaultDeviceId);
olka0bf2c9cd2017-02-10 11:56:24716 base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing.
olkaef762c92017-02-06 16:45:16717 WaitForAudioThreadToPostDeviceInfo();
olka0bf2c9cd2017-02-10 11:56:24718 base::RunLoop().RunUntilIdle(); // EVENT_START processing.
[email protected]fc89d8ae2010-09-16 12:07:57719
720 // Feed some samples to begin with for the endpointer to do noise estimation.
[email protected]ce1adc342013-05-20 13:35:43721 int num_packets = SpeechRecognizerImpl::kEndpointerEstimationTimeMs /
Yaowei Zhouf7df39c2024-02-29 04:53:48722 NetworkSpeechRecognitionEngineImpl::kAudioPacketIntervalMs;
[email protected]3b283d62011-03-01 18:38:36723 FillPacketWithNoise();
[email protected]fc89d8ae2010-09-16 12:07:57724 for (int i = 0; i < num_packets; ++i) {
Marina Ciocea68e948e2018-05-10 22:26:17725 Capture(audio_bus_.get());
[email protected]fc89d8ae2010-09-16 12:07:57726 }
fdoray896bea12016-06-10 15:52:01727 base::RunLoop().RunUntilIdle();
[email protected]fc89d8ae2010-09-16 12:07:57728 EXPECT_EQ(-1.0f, volume_); // No audio volume set yet.
729
730 // The vector is already filled with zero value samples on create.
Marina Ciocea68e948e2018-05-10 22:26:17731 Capture(audio_bus_.get());
fdoray896bea12016-06-10 15:52:01732 base::RunLoop().RunUntilIdle();
[email protected]9598dce2011-03-14 16:59:58733 EXPECT_FLOAT_EQ(0.74939233f, volume_);
[email protected]fc89d8ae2010-09-16 12:07:57734
735 FillPacketWithTestWaveform();
Marina Ciocea68e948e2018-05-10 22:26:17736 Capture(audio_bus_.get());
fdoray896bea12016-06-10 15:52:01737 base::RunLoop().RunUntilIdle();
[email protected]2e50f6d72013-06-17 14:41:38738 EXPECT_NEAR(0.89926866f, volume_, 0.00001f);
[email protected]9598dce2011-03-14 16:59:58739 EXPECT_FLOAT_EQ(0.75071919f, noise_volume_);
[email protected]fc89d8ae2010-09-16 12:07:57740
Evan Liud7252282024-05-16 20:10:32741 EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_);
[email protected]d1a4c792012-03-14 20:29:51742 EXPECT_FALSE(audio_ended_);
743 EXPECT_FALSE(recognition_ended_);
744 recognizer_->AbortRecognition();
fdoray896bea12016-06-10 15:52:01745 base::RunLoop().RunUntilIdle();
[email protected]2ba06442012-04-13 13:06:39746 CheckFinalEventsConsistency();
[email protected]fc89d8ae2010-09-16 12:07:57747}
748
[email protected]fcb8e0212012-10-29 11:57:18749} // namespace content