Avi Drissman | 4e1b7bc3 | 2022-09-15 14:03:50 | [diff] [blame] | 1 | // Copyright 2013 The Chromium Authors |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
danakj | 569d4de84 | 2024-04-03 16:53:41 | [diff] [blame] | 5 | #include "content/browser/speech/speech_recognizer_impl.h" |
| 6 | |
avi | b734894 | 2015-12-25 20:57:10 | [diff] [blame] | 7 | #include <stddef.h> |
| 8 | #include <stdint.h> |
| 9 | |
Peter Boström | dd7e40ec | 2021-04-05 20:40:10 | [diff] [blame] | 10 | #include <memory> |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 11 | #include <vector> |
| 12 | |
Tom Sepez | 3e42510 | 2025-07-23 12:16:20 | [diff] [blame] | 13 | #include "base/compiler_specific.h" |
danakj | 569d4de84 | 2024-04-03 16:53:41 | [diff] [blame] | 14 | #include "base/containers/span.h" |
Avi Drissman | adac2199 | 2023-01-11 23:46:39 | [diff] [blame] | 15 | #include "base/functional/bind.h" |
| 16 | #include "base/functional/callback_helpers.h" |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 17 | #include "base/memory/ref_counted.h" |
Henrique Ferreiro | 3252b35 | 2025-02-10 17:16:59 | [diff] [blame] | 18 | #include "base/memory/scoped_refptr.h" |
danakj | 569d4de84 | 2024-04-03 16:53:41 | [diff] [blame] | 19 | #include "base/numerics/byte_conversions.h" |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 20 | #include "base/run_loop.h" |
Daniel Cheng | 17390fd | 2025-06-07 06:38:26 | [diff] [blame] | 21 | #include "base/strings/string_view_util.h" |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 22 | #include "base/synchronization/waitable_event.h" |
Ken Rockot | 7296440 | 2019-12-06 10:40:51 | [diff] [blame] | 23 | #include "base/test/scoped_feature_list.h" |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 24 | #include "base/threading/thread.h" |
Yaowei Zhou | f7df39c | 2024-02-29 04:53:48 | [diff] [blame] | 25 | #include "content/browser/speech/network_speech_recognition_engine_impl.h" |
evliu | ad7f9cc | 2020-06-18 01:10:41 | [diff] [blame] | 26 | #include "content/public/browser/google_streaming_api.pb.h" |
Evan Liu | 881ab7a | 2024-08-01 21:54:51 | [diff] [blame] | 27 | #include "content/public/browser/speech_recognition_audio_forwarder_config.h" |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 28 | #include "content/public/browser/speech_recognition_event_listener.h" |
Ken Rockot | 7296440 | 2019-12-06 10:40:51 | [diff] [blame] | 29 | #include "content/public/common/content_features.h" |
Gabriel Charette | c710874 | 2019-08-23 03:31:40 | [diff] [blame] | 30 | #include "content/public/test/browser_task_environment.h" |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 31 | #include "media/audio/audio_device_description.h" |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 32 | #include "media/audio/audio_system_impl.h" |
[email protected] | fad64e7 | 2012-03-13 23:57:51 | [diff] [blame] | 33 | #include "media/audio/fake_audio_input_stream.h" |
| 34 | #include "media/audio/fake_audio_output_stream.h" |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 35 | #include "media/audio/mock_audio_manager.h" |
alokp | 2cbd4fc | 2017-05-13 04:13:24 | [diff] [blame] | 36 | #include "media/audio/test_audio_thread.h" |
[email protected] | f361a0d | 2014-06-19 12:38:56 | [diff] [blame] | 37 | #include "media/base/audio_bus.h" |
Fredrik Hernqvist | 35a8135 | 2024-03-04 15:03:55 | [diff] [blame] | 38 | #include "media/base/audio_glitch_info.h" |
David Sanders | d9088476 | 2025-06-11 01:09:54 | [diff] [blame] | 39 | #include "media/base/audio_sample_types.h" |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 40 | #include "media/base/test_helpers.h" |
Julie Jeongeun Kim | 8280a46 | 2019-09-11 14:25:33 | [diff] [blame] | 41 | #include "mojo/public/cpp/bindings/remote.h" |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 42 | #include "mojo/public/cpp/system/data_pipe.h" |
| 43 | #include "mojo/public/cpp/system/data_pipe_utils.h" |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 44 | #include "net/base/net_errors.h" |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 45 | #include "net/http/http_response_headers.h" |
| 46 | #include "net/http/http_util.h" |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 47 | #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 Gadani | b867478 | 2019-12-11 16:22:58 | [diff] [blame] | 49 | #include "services/network/public/mojom/url_response_head.mojom.h" |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 50 | #include "services/network/test/test_url_loader_factory.h" |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 51 | #include "testing/gtest/include/gtest/gtest.h" |
| 52 | |
[email protected] | 67dfea90 | 2012-04-03 01:49:09 | [diff] [blame] | 53 | using media::AudioInputStream; |
[email protected] | 67dfea90 | 2012-04-03 01:49:09 | [diff] [blame] | 54 | using media::AudioOutputStream; |
| 55 | using media::AudioParameters; |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 56 | |
[email protected] | fcb8e021 | 2012-10-29 11:57:18 | [diff] [blame] | 57 | namespace content { |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 58 | |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 59 | namespace { |
| 60 | |
| 61 | class 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 Sundbom | 48711ad | 2018-05-24 14:00:39 | [diff] [blame] | 71 | MOCK_METHOD1(SetOutputDeviceForAec, |
| 72 | void(const std::string& output_device_id)); |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 73 | |
| 74 | protected: |
| 75 | ~MockCapturerSource() override = default; |
| 76 | }; |
| 77 | |
| 78 | } // namespace |
| 79 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 80 | class SpeechRecognizerImplTest : public SpeechRecognitionEventListener, |
| 81 | public testing::Test { |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 82 | public: |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 83 | SpeechRecognizerImplTest() |
Henrique Ferreiro | 3252b35 | 2025-02-10 17:16:59 | [diff] [blame] | 84 | : audio_capturer_source_( |
| 85 | base::MakeRefCounted<testing::NiceMock<MockCapturerSource>>()), |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 86 | recognition_started_(false), |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 87 | recognition_ended_(false), |
[email protected] | e5a00ca | 2010-08-27 13:34:09 | [diff] [blame] | 88 | result_received_(false), |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 89 | audio_started_(false), |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 90 | audio_ended_(false), |
| 91 | sound_started_(false), |
| 92 | sound_ended_(false), |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 93 | error_(media::mojom::SpeechRecognitionErrorCode::kNone), |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 94 | volume_(-1.0f) { |
Ken Rockot | 7296440 | 2019-12-06 10:40:51 | [diff] [blame] | 95 | // 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] | c91bb26 | 2012-06-27 10:56:45 | [diff] [blame] | 100 | // SpeechRecognizer takes ownership of sr_engine. |
Yaowei Zhou | f7df39c | 2024-02-29 04:53:48 | [diff] [blame] | 101 | std::unique_ptr<NetworkSpeechRecognitionEngineImpl> sr_engine = |
| 102 | std::make_unique<NetworkSpeechRecognitionEngineImpl>( |
| 103 | base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
Evan Liu | c1300964 | 2024-11-06 23:09:28 | [diff] [blame] | 104 | &url_loader_factory_)); |
Yaowei Zhou | f7df39c | 2024-02-29 04:53:48 | [diff] [blame] | 105 | NetworkSpeechRecognitionEngineImpl::Config config; |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 106 | config.audio_num_bits_per_sample = |
| 107 | SpeechRecognizerImpl::kNumBitsPerAudioSample; |
| 108 | config.audio_sample_rate = SpeechRecognizerImpl::kAudioSampleRate; |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 109 | config.filter_profanities = false; |
| 110 | sr_engine->SetConfig(config); |
| 111 | |
[email protected] | c766aa9 | 2012-06-22 16:57:14 | [diff] [blame] | 112 | const int kTestingSessionId = 1; |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 113 | |
Peter Boström | dd7e40ec | 2021-04-05 20:40:10 | [diff] [blame] | 114 | audio_manager_ = std::make_unique<media::MockAudioManager>( |
| 115 | std::make_unique<media::TestAudioThread>(true)); |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 116 | audio_manager_->SetInputStreamParameters( |
| 117 | media::AudioParameters::UnavailableDeviceParams()); |
Olga Sharonova | e7b1eb2 | 2017-09-13 14:41:47 | [diff] [blame] | 118 | audio_system_ = |
| 119 | std::make_unique<media::AudioSystemImpl>(audio_manager_.get()); |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 120 | SpeechRecognizerImpl::SetAudioEnvironmentForTesting( |
| 121 | audio_system_.get(), audio_capturer_source_.get()); |
Evan Liu | 881ab7a | 2024-08-01 21:54:51 | [diff] [blame] | 122 | recognizer_ = new SpeechRecognizerImpl(this, audio_system_.get(), |
| 123 | kTestingSessionId, false, false, |
| 124 | std::move(sr_engine), std::nullopt); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 125 | |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 126 | int audio_packet_length_bytes = |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 127 | (SpeechRecognizerImpl::kAudioSampleRate * |
Yaowei Zhou | f7df39c | 2024-02-29 04:53:48 | [diff] [blame] | 128 | NetworkSpeechRecognitionEngineImpl::kAudioPacketIntervalMs * |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 129 | ChannelLayoutToChannelCount(SpeechRecognizerImpl::kChannelLayout) * |
Yaowei Zhou | f7df39c | 2024-02-29 04:53:48 | [diff] [blame] | 130 | SpeechRecognizerImpl::kNumBitsPerAudioSample) / |
| 131 | (8 * 1000); |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 132 | audio_packet_.resize(audio_packet_length_bytes); |
[email protected] | f361a0d | 2014-06-19 12:38:56 | [diff] [blame] | 133 | |
| 134 | const int channels = |
| 135 | ChannelLayoutToChannelCount(SpeechRecognizerImpl::kChannelLayout); |
Raul Tambre | b1da244 | 2019-04-07 18:18:03 | [diff] [blame] | 136 | int bytes_per_sample = SpeechRecognizerImpl::kNumBitsPerAudioSample / 8; |
| 137 | const int frames = audio_packet_length_bytes / channels / bytes_per_sample; |
[email protected] | f361a0d | 2014-06-19 12:38:56 | [diff] [blame] | 138 | audio_bus_ = media::AudioBus::Create(channels, frames); |
| 139 | audio_bus_->Zero(); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 140 | } |
| 141 | |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 142 | ~SpeechRecognizerImplTest() override { |
| 143 | SpeechRecognizerImpl::SetAudioEnvironmentForTesting(nullptr, nullptr); |
| 144 | audio_manager_->Shutdown(); |
| 145 | } |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 146 | |
Daniel Cheng | 9c9fa1a | 2022-01-14 03:42:11 | [diff] [blame] | 147 | [[nodiscard]] bool GetUpstreamRequest( |
| 148 | const network::TestURLLoaderFactory::PendingRequest** |
| 149 | pending_request_out) { |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 150 | return GetPendingRequest(pending_request_out, "/up"); |
| 151 | } |
| 152 | |
Daniel Cheng | 9c9fa1a | 2022-01-14 03:42:11 | [diff] [blame] | 153 | [[nodiscard]] bool GetDownstreamRequest( |
| 154 | const network::TestURLLoaderFactory::PendingRequest** |
| 155 | pending_request_out) { |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 156 | return GetPendingRequest(pending_request_out, "/down"); |
| 157 | } |
| 158 | |
Daniel Cheng | 9c9fa1a | 2022-01-14 03:42:11 | [diff] [blame] | 159 | [[nodiscard]] bool GetPendingRequest( |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 160 | const network::TestURLLoaderFactory::PendingRequest** pending_request_out, |
Daniel Cheng | 9c9fa1a | 2022-01-14 03:42:11 | [diff] [blame] | 161 | const char* url_substring) { |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 162 | for (const auto& pending_request : |
| 163 | *url_loader_factory_.pending_requests()) { |
John Abd-El-Malek | 04199db | 2018-06-28 17:33:39 | [diff] [blame] | 164 | if (pending_request.request.url.spec().find(url_substring) != |
| 165 | std::string::npos) { |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 166 | *pending_request_out = &pending_request; |
| 167 | return true; |
| 168 | } |
| 169 | } |
| 170 | return false; |
| 171 | } |
| 172 | |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 173 | 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] | fcb8e021 | 2012-10-29 11:57:18 | [diff] [blame] | 191 | // Overridden from SpeechRecognitionEventListener: |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 192 | void OnAudioStart(int session_id) override { |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 193 | audio_started_ = true; |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 194 | CheckEventsConsistency(); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 195 | } |
| 196 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 197 | void OnAudioEnd(int session_id) override { |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 198 | audio_ended_ = true; |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 199 | CheckEventsConsistency(); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 200 | } |
| 201 | |
Adithya Srinivasan | e75e328 | 2018-06-01 15:09:00 | [diff] [blame] | 202 | void OnRecognitionResults( |
| 203 | int session_id, |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 204 | const std::vector<media::mojom::WebSpeechRecognitionResultPtr>& results) |
Adithya Srinivasan | c35bf396 | 2018-06-12 14:28:14 | [diff] [blame] | 205 | override { |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 206 | result_received_ = true; |
| 207 | } |
| 208 | |
Adithya Srinivasan | c35bf396 | 2018-06-12 14:28:14 | [diff] [blame] | 209 | void OnRecognitionError( |
| 210 | int session_id, |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 211 | const media::mojom::SpeechRecognitionError& error) override { |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 212 | EXPECT_TRUE(recognition_started_); |
| 213 | EXPECT_FALSE(recognition_ended_); |
[email protected] | 7527adf4 | 2012-03-26 11:56:02 | [diff] [blame] | 214 | error_ = error.code; |
[email protected] | e5a00ca | 2010-08-27 13:34:09 | [diff] [blame] | 215 | } |
| 216 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 217 | void OnAudioLevelsChange(int session_id, |
| 218 | float volume, |
| 219 | float noise_volume) override { |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 220 | volume_ = volume; |
[email protected] | 3b283d6 | 2011-03-01 18:38:36 | [diff] [blame] | 221 | noise_volume_ = noise_volume; |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 222 | } |
| 223 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 224 | void OnRecognitionEnd(int session_id) override { |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 225 | recognition_ended_ = true; |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 226 | CheckEventsConsistency(); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 227 | } |
| 228 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 229 | void OnRecognitionStart(int session_id) override { |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 230 | recognition_started_ = true; |
| 231 | CheckEventsConsistency(); |
| 232 | } |
| 233 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 234 | void OnSoundStart(int session_id) override { |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 235 | sound_started_ = true; |
| 236 | CheckEventsConsistency(); |
| 237 | } |
| 238 | |
dcheng | c2282aa | 2014-10-21 12:07:58 | [diff] [blame] | 239 | void OnSoundEnd(int session_id) override { |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 240 | sound_ended_ = true; |
| 241 | CheckEventsConsistency(); |
| 242 | } |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 243 | |
[email protected] | f361a0d | 2014-06-19 12:38:56 | [diff] [blame] | 244 | void CopyPacketToAudioBus() { |
Raul Tambre | b1da244 | 2019-04-07 18:18:03 | [diff] [blame] | 245 | static_assert(SpeechRecognizerImpl::kNumBitsPerAudioSample == 16, |
| 246 | "FromInterleaved expects 2 bytes."); |
[email protected] | f361a0d | 2014-06-19 12:38:56 | [diff] [blame] | 247 | // Copy the created signal into an audio bus in a deinterleaved format. |
Raul Tambre | b1da244 | 2019-04-07 18:18:03 | [diff] [blame] | 248 | audio_bus_->FromInterleaved<media::SignedInt16SampleTypeTraits>( |
Tom Sepez | 3e42510 | 2025-07-23 12:16:20 | [diff] [blame] | 249 | UNSAFE_TODO(reinterpret_cast<int16_t*>(audio_packet_.data())), |
| 250 | audio_bus_->frames()); |
[email protected] | f361a0d | 2014-06-19 12:38:56 | [diff] [blame] | 251 | } |
| 252 | |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 253 | 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) |
avi | b734894 | 2015-12-25 20:57:10 | [diff] [blame] | 256 | audio_packet_[i] = static_cast<uint8_t>(i); |
[email protected] | f361a0d | 2014-06-19 12:38:56 | [diff] [blame] | 257 | CopyPacketToAudioBus(); |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 258 | } |
| 259 | |
[email protected] | 3b283d6 | 2011-03-01 18:38:36 | [diff] [blame] | 260 | 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] | f361a0d | 2014-06-19 12:38:56 | [diff] [blame] | 267 | CopyPacketToAudioBus(); |
[email protected] | 3b283d6 | 2011-03-01 18:38:36 | [diff] [blame] | 268 | } |
| 269 | |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 270 | void Capture(media::AudioBus* data) { |
| 271 | auto* capture_callback = |
| 272 | static_cast<media::AudioCapturerSource::CaptureCallback*>( |
| 273 | recognizer_.get()); |
Fredrik Hernqvist | 590f6576 | 2024-11-14 13:42:15 | [diff] [blame] | 274 | capture_callback->Capture(data, base::TimeTicks::Now(), {}, 0.0); |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 275 | } |
| 276 | |
| 277 | void OnCaptureError() { |
| 278 | auto* capture_callback = |
| 279 | static_cast<media::AudioCapturerSource::CaptureCallback*>( |
| 280 | recognizer_.get()); |
Tony Herre | 003731d | 2021-05-25 22:09:56 | [diff] [blame] | 281 | capture_callback->OnCaptureError( |
| 282 | media::AudioCapturerSource::ErrorCode::kUnknown, ""); |
tommi | ce9a251 | 2017-01-16 18:35:18 | [diff] [blame] | 283 | } |
| 284 | |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 285 | void WaitForAudioThreadToPostDeviceInfo() { |
| 286 | media::WaitableMessageLoopEvent event; |
alokp | 2cbd4fc | 2017-05-13 04:13:24 | [diff] [blame] | 287 | audio_manager_->GetTaskRunner()->PostTaskAndReply( |
Peter Kasting | 341e1fb | 2018-02-24 00:03:01 | [diff] [blame] | 288 | FROM_HERE, base::DoNothing(), event.GetClosure()); |
alokp | 2cbd4fc | 2017-05-13 04:13:24 | [diff] [blame] | 289 | // Runs the loop and waits for the audio thread to call event's closure, |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 290 | // which means AudioSystem reply containing device parameters is already |
| 291 | // queued on the main thread. |
| 292 | event.RunAndWait(); |
| 293 | } |
| 294 | |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 295 | protected: |
Ken Rockot | 7296440 | 2019-12-06 10:40:51 | [diff] [blame] | 296 | base::test::ScopedFeatureList feature_list_; |
Gabriel Charette | 798fde7 | 2019-08-20 22:24:04 | [diff] [blame] | 297 | BrowserTaskEnvironment task_environment_; |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 298 | network::TestURLLoaderFactory url_loader_factory_; |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 299 | scoped_refptr<SpeechRecognizerImpl> recognizer_; |
alokp | 2cbd4fc | 2017-05-13 04:13:24 | [diff] [blame] | 300 | std::unique_ptr<media::MockAudioManager> audio_manager_; |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 301 | std::unique_ptr<media::AudioSystem> audio_system_; |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 302 | scoped_refptr<MockCapturerSource> audio_capturer_source_; |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 303 | bool recognition_started_; |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 304 | bool recognition_ended_; |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 305 | bool result_received_; |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 306 | bool audio_started_; |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 307 | bool audio_ended_; |
| 308 | bool sound_started_; |
| 309 | bool sound_ended_; |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 310 | media::mojom::SpeechRecognitionErrorCode error_; |
avi | b734894 | 2015-12-25 20:57:10 | [diff] [blame] | 311 | std::vector<uint8_t> audio_packet_; |
dcheng | 5971627 | 2016-04-09 05:19:08 | [diff] [blame] | 312 | std::unique_ptr<media::AudioBus> audio_bus_; |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 313 | float volume_; |
[email protected] | 3b283d6 | 2011-03-01 18:38:36 | [diff] [blame] | 314 | float noise_volume_; |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 315 | }; |
| 316 | |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 317 | TEST_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); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 322 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 323 | WaitForAudioThreadToPostDeviceInfo(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 324 | base::RunLoop().RunUntilIdle(); // EVENT_START processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 325 | EXPECT_TRUE(recognition_started_); |
| 326 | EXPECT_FALSE(audio_started_); |
| 327 | EXPECT_FALSE(result_received_); |
Dale Curtis | 175ea99 | 2023-09-28 22:42:42 | [diff] [blame] | 328 | OnCaptureError(); |
| 329 | base::RunLoop().RunUntilIdle(); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 330 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kAudioCapture, error_); |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 331 | CheckFinalEventsConsistency(); |
| 332 | } |
| 333 | |
Dale Curtis | 175ea99 | 2023-09-28 22:42:42 | [diff] [blame] | 334 | TEST_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 Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 348 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_); |
Dale Curtis | 175ea99 | 2023-09-28 22:42:42 | [diff] [blame] | 349 | recognizer_->AbortRecognition(); |
| 350 | base::RunLoop().RunUntilIdle(); |
| 351 | CheckFinalEventsConsistency(); |
| 352 | } |
| 353 | |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 354 | TEST_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. |
alokp | 2cbd4fc | 2017-05-13 04:13:24 | [diff] [blame] | 361 | audio_manager_->GetTaskRunner()->PostTask( |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 362 | FROM_HERE, |
tzik | e2aca99 | 2017-09-05 08:50:54 | [diff] [blame] | 363 | base::BindOnce(&base::WaitableEvent::Wait, base::Unretained(&event))); |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 364 | |
| 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 Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 378 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_); |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 379 | CheckFinalEventsConsistency(); |
| 380 | } |
| 381 | |
| 382 | TEST_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. |
alokp | 2cbd4fc | 2017-05-13 04:13:24 | [diff] [blame] | 389 | audio_manager_->GetTaskRunner()->PostTask( |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 390 | FROM_HERE, |
tzik | e2aca99 | 2017-09-05 08:50:54 | [diff] [blame] | 391 | base::BindOnce(&base::WaitableEvent::Wait, base::Unretained(&event))); |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 392 | |
| 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 Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 406 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_); |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 407 | CheckFinalEventsConsistency(); |
| 408 | } |
| 409 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 410 | TEST_F(SpeechRecognizerImplTest, StopNoData) { |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 411 | // Check for callbacks when stopping record before any audio gets recorded. |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 412 | recognizer_->StartRecognition( |
| 413 | media::AudioDeviceDescription::kDefaultDeviceId); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 414 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 415 | WaitForAudioThreadToPostDeviceInfo(); |
[email protected] | 12f4fb9 | 2012-05-16 10:30:16 | [diff] [blame] | 416 | recognizer_->StopAudioCapture(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 417 | base::RunLoop().RunUntilIdle(); // EVENT_START and EVENT_STOP processing. |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 418 | EXPECT_TRUE(recognition_started_); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 419 | EXPECT_FALSE(audio_started_); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 420 | EXPECT_FALSE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 421 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 422 | CheckFinalEventsConsistency(); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 423 | } |
| 424 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 425 | TEST_F(SpeechRecognizerImplTest, CancelNoData) { |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 426 | // Check for callbacks when canceling recognition before any audio gets |
| 427 | // recorded. |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 428 | recognizer_->StartRecognition( |
| 429 | media::AudioDeviceDescription::kDefaultDeviceId); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 430 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 431 | WaitForAudioThreadToPostDeviceInfo(); |
[email protected] | 12f4fb9 | 2012-05-16 10:30:16 | [diff] [blame] | 432 | recognizer_->AbortRecognition(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 433 | base::RunLoop().RunUntilIdle(); // EVENT_START and EVENT_ABORT processing. |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 434 | EXPECT_TRUE(recognition_started_); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 435 | EXPECT_FALSE(audio_started_); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 436 | EXPECT_FALSE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 437 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kAborted, error_); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 438 | CheckFinalEventsConsistency(); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 439 | } |
| 440 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 441 | TEST_F(SpeechRecognizerImplTest, StopWithData) { |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 442 | // Start recording, give some data and then stop. This should wait for the |
| 443 | // network callback to arrive before completion. |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 444 | recognizer_->StartRecognition( |
| 445 | media::AudioDeviceDescription::kDefaultDeviceId); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 446 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 447 | WaitForAudioThreadToPostDeviceInfo(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 448 | base::RunLoop().RunUntilIdle(); // EVENT_START processing. |
[email protected] | deedcc8 | 2011-03-04 23:19:01 | [diff] [blame] | 449 | |
| 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 Kim | 8280a46 | 2019-09-11 14:25:33 | [diff] [blame] | 455 | mojo::Remote<network::mojom::ChunkedDataPipeGetter> chunked_data_pipe_getter; |
Robert Sesek | 76af669 | 2021-01-29 20:57:23 | [diff] [blame] | 456 | 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] | deedcc8 | 2011-03-04 23:19:01 | [diff] [blame] | 460 | for (size_t i = 0; i < kNumChunks; ++i) { |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 461 | Capture(audio_bus_.get()); |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 462 | |
| 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-Malek | 04199db | 2018-06-28 17:33:39 | [diff] [blame] | 469 | ASSERT_TRUE(upstream_request->request.request_body); |
| 470 | ASSERT_EQ(1u, upstream_request->request.request_body->elements()->size()); |
Yutaka Hirano | 69d394f | 2021-01-13 11:38:43 | [diff] [blame] | 471 | 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 Sesek | 76af669 | 2021-01-29 20:57:23 | [diff] [blame] | 477 | chunked_data_pipe_getter->StartReading(std::move(producer_handle)); |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 478 | } |
| 479 | |
| 480 | std::string data; |
| 481 | while (true) { |
| 482 | base::RunLoop().RunUntilIdle(); |
| 483 | |
Lukasz Anforowicz | 2a672e3 | 2024-06-14 17:27:49 | [diff] [blame] | 484 | base::span<const uint8_t> buffer; |
| 485 | MojoResult result = |
| 486 | consumer_handle->BeginReadData(MOJO_READ_DATA_FLAG_NONE, buffer); |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 487 | if (result == MOJO_RESULT_OK) { |
Lukasz Anforowicz | 2a672e3 | 2024-06-14 17:27:49 | [diff] [blame] | 488 | data.append(base::as_string_view(buffer)); |
| 489 | consumer_handle->EndReadData(buffer.size()); |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 490 | 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] | deedcc8 | 2011-03-04 23:19:01 | [diff] [blame] | 503 | } |
| 504 | |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 505 | recognizer_->StopAudioCapture(); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 506 | base::RunLoop().RunUntilIdle(); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 507 | EXPECT_TRUE(audio_started_); |
| 508 | EXPECT_TRUE(audio_ended_); |
| 509 | EXPECT_FALSE(recognition_ended_); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 510 | EXPECT_FALSE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 511 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 512 | |
hans | fa81aea | 2016-04-14 17:41:44 | [diff] [blame] | 513 | // 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 Benjamin | b54ed30 | 2024-07-12 14:47:25 | [diff] [blame] | 524 | msg_string.insert(0u, base::as_string_view(base::U32ToBigEndian( |
danakj | 569d4de84 | 2024-04-03 16:53:41 | [diff] [blame] | 525 | base::checked_cast<uint32_t>(msg_string.size())))); |
[email protected] | 5915a01 | 2011-09-26 14:21:27 | [diff] [blame] | 526 | |
hans | fa81aea | 2016-04-14 17:41:44 | [diff] [blame] | 527 | // Issue the network callback to complete the process. |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 528 | const network::TestURLLoaderFactory::PendingRequest* downstream_request; |
| 529 | ASSERT_TRUE(GetDownstreamRequest(&downstream_request)); |
John Abd-El-Malek | 04199db | 2018-06-28 17:33:39 | [diff] [blame] | 530 | url_loader_factory_.AddResponse(downstream_request->request.url.spec(), |
| 531 | msg_string); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 532 | base::RunLoop().RunUntilIdle(); |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 533 | |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 534 | EXPECT_TRUE(recognition_ended_); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 535 | EXPECT_TRUE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 536 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 537 | CheckFinalEventsConsistency(); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 538 | } |
| 539 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 540 | TEST_F(SpeechRecognizerImplTest, CancelWithData) { |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 541 | // Start recording, give some data and then cancel. |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 542 | recognizer_->StartRecognition( |
| 543 | media::AudioDeviceDescription::kDefaultDeviceId); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 544 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 545 | WaitForAudioThreadToPostDeviceInfo(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 546 | base::RunLoop().RunUntilIdle(); // EVENT_START processing. |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 547 | Capture(audio_bus_.get()); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 548 | base::RunLoop().RunUntilIdle(); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 549 | recognizer_->AbortRecognition(); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 550 | base::RunLoop().RunUntilIdle(); |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 551 | // There should be both upstream and downstream pending requests. |
| 552 | ASSERT_EQ(2u, url_loader_factory_.pending_requests()->size()); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 553 | EXPECT_TRUE(recognition_started_); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 554 | EXPECT_TRUE(audio_started_); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 555 | EXPECT_FALSE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 556 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kAborted, error_); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 557 | CheckFinalEventsConsistency(); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 558 | } |
| 559 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 560 | TEST_F(SpeechRecognizerImplTest, ConnectionError) { |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 561 | // 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 |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 563 | recognizer_->StartRecognition( |
| 564 | media::AudioDeviceDescription::kDefaultDeviceId); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 565 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 566 | WaitForAudioThreadToPostDeviceInfo(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 567 | base::RunLoop().RunUntilIdle(); // EVENT_START processing. |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 568 | Capture(audio_bus_.get()); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 569 | base::RunLoop().RunUntilIdle(); |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 570 | // There should be both upstream and downstream pending requests. |
| 571 | ASSERT_EQ(2u, url_loader_factory_.pending_requests()->size()); |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 572 | |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 573 | recognizer_->StopAudioCapture(); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 574 | base::RunLoop().RunUntilIdle(); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 575 | EXPECT_TRUE(audio_started_); |
| 576 | EXPECT_TRUE(audio_ended_); |
| 577 | EXPECT_FALSE(recognition_ended_); |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 578 | EXPECT_FALSE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 579 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_); |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 580 | |
| 581 | // Issue the network callback to complete the process. |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 582 | const network::TestURLLoaderFactory::PendingRequest* pending_request; |
| 583 | ASSERT_TRUE(GetUpstreamRequest(&pending_request)); |
| 584 | url_loader_factory_.AddResponse( |
Lucas Furukawa Gadani | b867478 | 2019-12-11 16:22:58 | [diff] [blame] | 585 | pending_request->request.url, network::mojom::URLResponseHead::New(), "", |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 586 | network::URLLoaderCompletionStatus(net::ERR_CONNECTION_REFUSED)); |
| 587 | |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 588 | base::RunLoop().RunUntilIdle(); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 589 | EXPECT_TRUE(recognition_ended_); |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 590 | EXPECT_FALSE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 591 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNetwork, error_); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 592 | CheckFinalEventsConsistency(); |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 593 | } |
| 594 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 595 | TEST_F(SpeechRecognizerImplTest, ServerError) { |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 596 | // 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 |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 598 | recognizer_->StartRecognition( |
| 599 | media::AudioDeviceDescription::kDefaultDeviceId); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 600 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 601 | WaitForAudioThreadToPostDeviceInfo(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 602 | base::RunLoop().RunUntilIdle(); // EVENT_START processing. |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 603 | Capture(audio_bus_.get()); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 604 | base::RunLoop().RunUntilIdle(); |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 605 | // There should be both upstream and downstream pending requests. |
| 606 | ASSERT_EQ(2u, url_loader_factory_.pending_requests()->size()); |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 607 | |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 608 | recognizer_->StopAudioCapture(); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 609 | base::RunLoop().RunUntilIdle(); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 610 | EXPECT_TRUE(audio_started_); |
| 611 | EXPECT_TRUE(audio_ended_); |
| 612 | EXPECT_FALSE(recognition_ended_); |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 613 | EXPECT_FALSE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 614 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_); |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 615 | |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 616 | const network::TestURLLoaderFactory::PendingRequest* pending_request; |
| 617 | ASSERT_TRUE(GetUpstreamRequest(&pending_request)); |
Lucas Furukawa Gadani | b867478 | 2019-12-11 16:22:58 | [diff] [blame] | 618 | auto response = network::mojom::URLResponseHead::New(); |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 619 | const char kHeaders[] = "HTTP/1.0 500 Internal Server Error"; |
Lucas Furukawa Gadani | b867478 | 2019-12-11 16:22:58 | [diff] [blame] | 620 | response->headers = base::MakeRefCounted<net::HttpResponseHeaders>( |
David Benjamin | 1384c040 | 2019-04-29 18:55:52 | [diff] [blame] | 621 | net::HttpUtil::AssembleRawHeaders(kHeaders)); |
Lucas Furukawa Gadani | b867478 | 2019-12-11 16:22:58 | [diff] [blame] | 622 | url_loader_factory_.AddResponse(pending_request->request.url, |
| 623 | std::move(response), "", |
Matt Menke | 7b2266e | 2018-06-07 19:32:09 | [diff] [blame] | 624 | network::URLLoaderCompletionStatus()); |
| 625 | |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 626 | base::RunLoop().RunUntilIdle(); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 627 | EXPECT_TRUE(recognition_ended_); |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 628 | EXPECT_FALSE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 629 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNetwork, error_); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 630 | CheckFinalEventsConsistency(); |
[email protected] | 441c6c3 | 2011-03-07 17:24:13 | [diff] [blame] | 631 | } |
| 632 | |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 633 | TEST_F(SpeechRecognizerImplTest, OnCaptureError_PropagatesError) { |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 634 | // Check if things tear down properly if AudioInputController threw an error. |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 635 | recognizer_->StartRecognition( |
| 636 | media::AudioDeviceDescription::kDefaultDeviceId); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 637 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 638 | WaitForAudioThreadToPostDeviceInfo(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 639 | base::RunLoop().RunUntilIdle(); // EVENT_START processing. |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 640 | |
| 641 | OnCaptureError(); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 642 | base::RunLoop().RunUntilIdle(); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 643 | EXPECT_TRUE(recognition_started_); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 644 | EXPECT_FALSE(audio_started_); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 645 | EXPECT_FALSE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 646 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kAudioCapture, error_); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 647 | CheckFinalEventsConsistency(); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 648 | } |
| 649 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 650 | TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackIssued) { |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 651 | // 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. |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 653 | recognizer_->StartRecognition( |
| 654 | media::AudioDeviceDescription::kDefaultDeviceId); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 655 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 656 | WaitForAudioThreadToPostDeviceInfo(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 657 | base::RunLoop().RunUntilIdle(); // EVENT_START processing. |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 658 | |
Yaowei Zhou | f7df39c | 2024-02-29 04:53:48 | [diff] [blame] | 659 | int num_packets = |
| 660 | (SpeechRecognizerImpl::kNoSpeechTimeoutMs) / |
| 661 | NetworkSpeechRecognitionEngineImpl::kAudioPacketIntervalMs + |
| 662 | 1; |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 663 | // The vector is already filled with zero value samples on create. |
| 664 | for (int i = 0; i < num_packets; ++i) { |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 665 | Capture(audio_bus_.get()); |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 666 | } |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 667 | base::RunLoop().RunUntilIdle(); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 668 | EXPECT_TRUE(recognition_started_); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 669 | EXPECT_TRUE(audio_started_); |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 670 | EXPECT_FALSE(result_received_); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 671 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNoSpeech, error_); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 672 | CheckFinalEventsConsistency(); |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 673 | } |
| 674 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 675 | TEST_F(SpeechRecognizerImplTest, NoSpeechCallbackNotIssued) { |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 676 | // 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. |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 680 | recognizer_->StartRecognition( |
| 681 | media::AudioDeviceDescription::kDefaultDeviceId); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 682 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 683 | WaitForAudioThreadToPostDeviceInfo(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 684 | base::RunLoop().RunUntilIdle(); // EVENT_START processing. |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 685 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 686 | int num_packets = (SpeechRecognizerImpl::kNoSpeechTimeoutMs) / |
Yaowei Zhou | f7df39c | 2024-02-29 04:53:48 | [diff] [blame] | 687 | NetworkSpeechRecognitionEngineImpl::kAudioPacketIntervalMs; |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 688 | |
| 689 | // The vector is already filled with zero value samples on create. |
| 690 | for (int i = 0; i < num_packets / 2; ++i) { |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 691 | Capture(audio_bus_.get()); |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 692 | } |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 693 | |
| 694 | FillPacketWithTestWaveform(); |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 695 | for (int i = 0; i < num_packets / 2; ++i) { |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 696 | Capture(audio_bus_.get()); |
[email protected] | 6523152d | 2010-09-09 13:41:24 | [diff] [blame] | 697 | } |
| 698 | |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 699 | base::RunLoop().RunUntilIdle(); |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 700 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 701 | EXPECT_TRUE(audio_started_); |
| 702 | EXPECT_FALSE(audio_ended_); |
| 703 | EXPECT_FALSE(recognition_ended_); |
| 704 | recognizer_->AbortRecognition(); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 705 | base::RunLoop().RunUntilIdle(); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 706 | CheckFinalEventsConsistency(); |
[email protected] | 4b9a8c3 | 2010-08-12 19:57:31 | [diff] [blame] | 707 | } |
| 708 | |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 709 | TEST_F(SpeechRecognizerImplTest, SetInputVolumeCallback) { |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 710 | // 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. |
olka | 251dd569 | 2016-04-27 15:50:17 | [diff] [blame] | 714 | recognizer_->StartRecognition( |
| 715 | media::AudioDeviceDescription::kDefaultDeviceId); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 716 | base::RunLoop().RunUntilIdle(); // EVENT_PREPARE processing. |
olka | ef762c9 | 2017-02-06 16:45:16 | [diff] [blame] | 717 | WaitForAudioThreadToPostDeviceInfo(); |
olka | 0bf2c9cd | 2017-02-10 11:56:24 | [diff] [blame] | 718 | base::RunLoop().RunUntilIdle(); // EVENT_START processing. |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 719 | |
| 720 | // Feed some samples to begin with for the endpointer to do noise estimation. |
[email protected] | ce1adc34 | 2013-05-20 13:35:43 | [diff] [blame] | 721 | int num_packets = SpeechRecognizerImpl::kEndpointerEstimationTimeMs / |
Yaowei Zhou | f7df39c | 2024-02-29 04:53:48 | [diff] [blame] | 722 | NetworkSpeechRecognitionEngineImpl::kAudioPacketIntervalMs; |
[email protected] | 3b283d6 | 2011-03-01 18:38:36 | [diff] [blame] | 723 | FillPacketWithNoise(); |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 724 | for (int i = 0; i < num_packets; ++i) { |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 725 | Capture(audio_bus_.get()); |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 726 | } |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 727 | base::RunLoop().RunUntilIdle(); |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 728 | EXPECT_EQ(-1.0f, volume_); // No audio volume set yet. |
| 729 | |
| 730 | // The vector is already filled with zero value samples on create. |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 731 | Capture(audio_bus_.get()); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 732 | base::RunLoop().RunUntilIdle(); |
[email protected] | 9598dce | 2011-03-14 16:59:58 | [diff] [blame] | 733 | EXPECT_FLOAT_EQ(0.74939233f, volume_); |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 734 | |
| 735 | FillPacketWithTestWaveform(); |
Marina Ciocea | 68e948e | 2018-05-10 22:26:17 | [diff] [blame] | 736 | Capture(audio_bus_.get()); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 737 | base::RunLoop().RunUntilIdle(); |
[email protected] | 2e50f6d7 | 2013-06-17 14:41:38 | [diff] [blame] | 738 | EXPECT_NEAR(0.89926866f, volume_, 0.00001f); |
[email protected] | 9598dce | 2011-03-14 16:59:58 | [diff] [blame] | 739 | EXPECT_FLOAT_EQ(0.75071919f, noise_volume_); |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 740 | |
Evan Liu | d725228 | 2024-05-16 20:10:32 | [diff] [blame] | 741 | EXPECT_EQ(media::mojom::SpeechRecognitionErrorCode::kNone, error_); |
[email protected] | d1a4c79 | 2012-03-14 20:29:51 | [diff] [blame] | 742 | EXPECT_FALSE(audio_ended_); |
| 743 | EXPECT_FALSE(recognition_ended_); |
| 744 | recognizer_->AbortRecognition(); |
fdoray | 896bea1 | 2016-06-10 15:52:01 | [diff] [blame] | 745 | base::RunLoop().RunUntilIdle(); |
[email protected] | 2ba0644 | 2012-04-13 13:06:39 | [diff] [blame] | 746 | CheckFinalEventsConsistency(); |
[email protected] | fc89d8ae | 2010-09-16 12:07:57 | [diff] [blame] | 747 | } |
| 748 | |
[email protected] | fcb8e021 | 2012-10-29 11:57:18 | [diff] [blame] | 749 | } // namespace content |