1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/audio_modem/audio_recorder.h"
10 #include "base/memory/aligned_memory.h"
11 #include "base/run_loop.h"
12 #include "components/audio_modem/audio_recorder_impl.h"
13 #include "components/audio_modem/public/audio_modem_types.h"
14 #include "components/audio_modem/test/random_samples.h"
15 #include "content/public/test/test_browser_thread_bundle.h"
16 #include "media/audio/audio_manager.h"
17 #include "media/audio/audio_manager_base.h"
18 #include "media/base/audio_bus.h"
19 #include "testing/gtest/include/gtest/gtest.h"
23 const size_t kSomeNumber
= 0x9999;
25 class TestAudioInputStream
: public media::AudioInputStream
{
27 TestAudioInputStream(const media::AudioParameters
& params
,
28 const std::vector
<float*> channel_data
,
30 : callback_(nullptr), params_(params
) {
31 buffer_
= media::AudioBus::CreateWrapper(2);
32 for (size_t i
= 0; i
< channel_data
.size(); ++i
)
33 buffer_
->SetChannelData(i
, channel_data
[i
]);
34 buffer_
->set_frames(samples
);
37 ~TestAudioInputStream() override
{}
39 bool Open() override
{ return true; }
40 void Start(AudioInputCallback
* callback
) override
{
43 media::AudioManager::Get()->GetTaskRunner()->PostTask(
45 base::Bind(&TestAudioInputStream::SimulateRecording
,
46 base::Unretained(this)));
48 void Stop() override
{}
49 void Close() override
{}
50 double GetMaxVolume() override
{ return 1.0; }
51 void SetVolume(double volume
) override
{}
52 double GetVolume() override
{ return 1.0; }
53 bool SetAutomaticGainControl(bool enabled
) override
{ return false; }
54 bool GetAutomaticGainControl() override
{ return true; }
55 bool IsMuted() override
{ return false; }
58 void SimulateRecording() {
59 const int fpb
= params_
.frames_per_buffer();
60 for (int i
= 0; i
< buffer_
->frames() / fpb
; ++i
) {
61 scoped_ptr
<media::AudioBus
> source
= media::AudioBus::Create(2, fpb
);
62 buffer_
->CopyPartialFramesTo(i
* fpb
, fpb
, 0, source
.get());
63 callback_
->OnData(this, source
.get(), fpb
, 1.0);
67 AudioInputCallback
* callback_
;
68 media::AudioParameters params_
;
69 scoped_ptr
<media::AudioBus
> buffer_
;
71 DISALLOW_COPY_AND_ASSIGN(TestAudioInputStream
);
76 namespace audio_modem
{
78 class AudioRecorderTest
: public testing::Test
{
80 AudioRecorderTest() : total_samples_(0), recorder_(nullptr) {
81 if (!media::AudioManager::Get())
82 media::AudioManager::CreateForTesting();
85 ~AudioRecorderTest() override
{
87 for (size_t i
= 0; i
< channel_data_
.size(); ++i
)
88 base::AlignedFree(channel_data_
[i
]);
91 void CreateSimpleRecorder() {
92 // If we have input devices, we'll create a recorder which uses a real
93 // input stream, if not, we'll create a recorder which uses our mock input
95 if (media::AudioManager::Get()->HasAudioInputDevices()) {
97 recorder_
= new AudioRecorderImpl();
98 recorder_
->Initialize(base::Bind(&AudioRecorderTest::DecodeSamples
,
99 base::Unretained(this)));
101 CreateRecorder(kSomeNumber
);
105 void CreateRecorder(size_t samples
) {
108 params_
= media::AudioManager::Get()->GetInputStreamParameters(
109 media::AudioManagerBase::kDefaultDeviceId
);
111 channel_data_
.clear();
112 channel_data_
.push_back(GenerateSamples(0x1337, samples
));
113 channel_data_
.push_back(GenerateSamples(0x7331, samples
));
115 total_samples_
= samples
;
117 recorder_
= new AudioRecorderImpl();
118 recorder_
->set_input_stream_for_testing(
119 new TestAudioInputStream(params_
, channel_data_
, samples
));
120 recorder_
->set_params_for_testing(new media::AudioParameters(params_
));
121 recorder_
->Initialize(
122 base::Bind(&AudioRecorderTest::DecodeSamples
, base::Unretained(this)));
125 void DeleteRecorder() {
128 recorder_
->Finalize();
132 void RecordAndVerifySamples() {
133 received_samples_
.clear();
134 run_loop_
.reset(new base::RunLoop());
139 void DecodeSamples(const std::string
& samples
) {
140 received_samples_
+= samples
;
141 // We expect one less decode than our total samples would ideally have
142 // triggered since we process data in 4k chunks. So our sample processing
143 // will never rarely be perfectly aligned with 0.5s worth of samples, hence
144 // we will almost always run with a buffer of leftover samples that will
145 // not get sent to this callback since the recorder will be waiting for
147 const size_t decode_buffer
= params_
.sample_rate() / 2; // 0.5s
148 const size_t expected_samples
=
149 (total_samples_
/ decode_buffer
- 1) * decode_buffer
;
150 const size_t expected_samples_size
=
151 expected_samples
* sizeof(float) * params_
.channels();
152 if (received_samples_
.size() == expected_samples_size
) {
158 void VerifySamples() {
162 reinterpret_cast<float*>(string_as_array(&received_samples_
));
163 const int channels
= params_
.channels();
165 received_samples_
.size() / sizeof(float) / params_
.channels();
166 for (int ch
= 0; ch
< channels
; ++ch
) {
167 for (int si
= 0, di
= ch
; si
< frames
; ++si
, di
+= channels
)
168 differences
+= (buffer_view
[di
] != channel_data_
[ch
][si
]);
171 ASSERT_EQ(0, differences
);
175 float* GenerateSamples(int random_seed
, size_t size
) {
176 float* samples
= static_cast<float*>(base::AlignedAlloc(
177 size
* sizeof(float), media::AudioBus::kChannelAlignment
));
178 PopulateSamples(0x1337, size
, samples
);
182 recorder_
->FlushAudioLoopForTesting();
183 return recorder_
->is_recording_
;
186 std::vector
<float*> channel_data_
;
187 media::AudioParameters params_
;
188 size_t total_samples_
;
190 // Deleted by calling Finalize() on the object.
191 AudioRecorderImpl
* recorder_
;
193 std::string received_samples_
;
195 scoped_ptr
<base::RunLoop
> run_loop_
;
196 content::TestBrowserThreadBundle thread_bundle_
;
200 // http://crbug.com/463854
201 #if defined(OS_MACOSX)
202 #define MAYBE_BasicRecordAndStop DISABLED_BasicRecordAndStop
204 #define MAYBE_BasicRecordAndStop BasicRecordAndStop
207 TEST_F(AudioRecorderTest
, MAYBE_BasicRecordAndStop
) {
208 CreateSimpleRecorder();
211 EXPECT_TRUE(IsRecording());
213 EXPECT_FALSE(IsRecording());
216 EXPECT_TRUE(IsRecording());
218 EXPECT_FALSE(IsRecording());
221 EXPECT_TRUE(IsRecording());
223 EXPECT_FALSE(IsRecording());
228 // http://crbug.com/460685
229 #if defined(OS_MACOSX)
230 #define MAYBE_OutOfOrderRecordAndStopMultiple \
231 DISABLED_OutOfOrderRecordAndStopMultiple
233 #define MAYBE_OutOfOrderRecordAndStopMultiple \
234 OutOfOrderRecordAndStopMultiple
237 TEST_F(AudioRecorderTest
, MAYBE_OutOfOrderRecordAndStopMultiple
) {
238 CreateSimpleRecorder();
243 EXPECT_FALSE(IsRecording());
247 EXPECT_TRUE(IsRecording());
251 EXPECT_FALSE(IsRecording());
256 TEST_F(AudioRecorderTest
, RecordingEndToEnd
) {
257 const int kNumSamples
= 48000 * 3;
258 CreateRecorder(kNumSamples
);
260 RecordAndVerifySamples();
265 // TODO(rkc): Add tests with recording different sample rates.
267 } // namespace audio_modem