audio_recorder_unittest.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 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.
4
5#include "components/copresence/mediums/audio/audio_recorder.h"
6
7#include "base/bind.h"
8#include "base/memory/aligned_memory.h"
9#include "base/run_loop.h"
10#include "components/copresence/public/copresence_constants.h"
11#include "components/copresence/test/audio_test_support.h"
12#include "content/public/test/test_browser_thread_bundle.h"
13#include "media/audio/audio_manager.h"
14#include "media/audio/audio_manager_base.h"
15#include "media/base/audio_bus.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18namespace {
19
20class TestAudioInputStream : public media::AudioInputStream {
21 public:
22  TestAudioInputStream(const media::AudioParameters& params,
23                       const std::vector<float*> channel_data,
24                       size_t samples)
25      : callback_(NULL), params_(params) {
26    buffer_ = media::AudioBus::CreateWrapper(2);
27    for (size_t i = 0; i < channel_data.size(); ++i)
28      buffer_->SetChannelData(i, channel_data[i]);
29    buffer_->set_frames(samples);
30  }
31
32  virtual ~TestAudioInputStream() {}
33
34  virtual bool Open() OVERRIDE { return true; }
35  virtual void Start(AudioInputCallback* callback) OVERRIDE {
36    DCHECK(callback);
37    callback_ = callback;
38    media::AudioManager::Get()->GetTaskRunner()->PostTask(
39        FROM_HERE,
40        base::Bind(&TestAudioInputStream::SimulateRecording,
41                   base::Unretained(this)));
42  }
43  virtual void Stop() OVERRIDE {}
44  virtual void Close() OVERRIDE {}
45  virtual double GetMaxVolume() OVERRIDE { return 1.0; }
46  virtual void SetVolume(double volume) OVERRIDE {}
47  virtual double GetVolume() OVERRIDE { return 1.0; }
48  virtual void SetAutomaticGainControl(bool enabled) OVERRIDE {}
49  virtual bool GetAutomaticGainControl() OVERRIDE { return true; }
50
51 private:
52  void SimulateRecording() {
53    const int fpb = params_.frames_per_buffer();
54    for (int i = 0; i < buffer_->frames() / fpb; ++i) {
55      scoped_ptr<media::AudioBus> source = media::AudioBus::Create(2, fpb);
56      buffer_->CopyPartialFramesTo(i * fpb, fpb, 0, source.get());
57      callback_->OnData(this, source.get(), fpb, 1.0);
58    }
59  }
60
61  AudioInputCallback* callback_;
62  media::AudioParameters params_;
63  scoped_ptr<media::AudioBus> buffer_;
64
65  DISALLOW_COPY_AND_ASSIGN(TestAudioInputStream);
66};
67
68}  // namespace
69
70namespace copresence {
71
72class AudioRecorderTest : public testing::Test {
73 public:
74  AudioRecorderTest() : total_samples_(0), recorder_(NULL) {
75    if (!media::AudioManager::Get())
76      media::AudioManager::CreateForTesting();
77  }
78
79  virtual ~AudioRecorderTest() {
80    DeleteRecorder();
81    for (size_t i = 0; i < channel_data_.size(); ++i)
82      base::AlignedFree(channel_data_[i]);
83  }
84
85  void CreateSimpleRecorder() {
86    DeleteRecorder();
87    recorder_ = new AudioRecorder(
88        base::Bind(&AudioRecorderTest::DecodeSamples, base::Unretained(this)));
89    recorder_->Initialize();
90  }
91
92  void CreateRecorder(size_t channels,
93                      size_t sample_rate,
94                      size_t bits_per_sample,
95                      size_t samples) {
96    DeleteRecorder();
97    params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
98                  kDefaultChannelLayout,
99                  channels,
100                  2,
101                  sample_rate,
102                  bits_per_sample,
103                  4096);
104
105    channel_data_.clear();
106    channel_data_.push_back(GenerateSamples(0x1337, samples));
107    channel_data_.push_back(GenerateSamples(0x7331, samples));
108
109    total_samples_ = samples;
110
111    recorder_ = new AudioRecorder(
112        base::Bind(&AudioRecorderTest::DecodeSamples, base::Unretained(this)));
113    recorder_->set_input_stream_for_testing(
114        new TestAudioInputStream(params_, channel_data_, samples));
115    recorder_->set_params_for_testing(new media::AudioParameters(params_));
116    recorder_->Initialize();
117  }
118
119  void DeleteRecorder() {
120    if (!recorder_)
121      return;
122    recorder_->Finalize();
123    recorder_ = NULL;
124  }
125
126  void RecordAndVerifySamples() {
127    received_samples_.clear();
128    run_loop_.reset(new base::RunLoop());
129    recorder_->Record();
130    run_loop_->Run();
131  }
132
133  void DecodeSamples(const std::string& samples) {
134    received_samples_ += samples;
135    // We expect one less decode than our total samples would ideally have
136    // triggered since we process data in 4k chunks. So our sample processing
137    // will never rarely be perfectly aligned with 0.5s worth of samples, hence
138    // we will almost always run with a buffer of leftover samples that will
139    // not get sent to this callback since the recorder will be waiting for
140    // more data.
141    const size_t decode_buffer = params_.sample_rate() / 2;  // 0.5s
142    const size_t expected_samples =
143        (total_samples_ / decode_buffer - 1) * decode_buffer;
144    const size_t expected_samples_size =
145        expected_samples * sizeof(float) * params_.channels();
146    if (received_samples_.size() == expected_samples_size) {
147      VerifySamples();
148      run_loop_->Quit();
149    }
150  }
151
152  void VerifySamples() {
153    int differences = 0;
154
155    float* buffer_view =
156        reinterpret_cast<float*>(string_as_array(&received_samples_));
157    const int channels = params_.channels();
158    const int frames =
159        received_samples_.size() / sizeof(float) / params_.channels();
160    for (int ch = 0; ch < channels; ++ch) {
161      for (int si = 0, di = ch; si < frames; ++si, di += channels)
162        differences += (buffer_view[di] != channel_data_[ch][si]);
163    }
164
165    ASSERT_EQ(0, differences);
166  }
167
168 protected:
169  float* GenerateSamples(int random_seed, size_t size) {
170    float* samples = static_cast<float*>(base::AlignedAlloc(
171        size * sizeof(float), media::AudioBus::kChannelAlignment));
172    PopulateSamples(0x1337, size, samples);
173    return samples;
174  }
175  bool IsRecording() {
176    recorder_->FlushAudioLoopForTesting();
177    return recorder_->is_recording_;
178  }
179
180  std::vector<float*> channel_data_;
181  media::AudioParameters params_;
182  size_t total_samples_;
183
184  AudioRecorder* recorder_;
185
186  std::string received_samples_;
187
188  scoped_ptr<base::RunLoop> run_loop_;
189  content::TestBrowserThreadBundle thread_bundle_;
190};
191
192#if defined(OS_WIN) || defined(OS_MACOSX)
193// Windows does not let us use non-OS params. The tests need to be rewritten to
194// use the params provided to us by the audio manager rather than setting our
195// own params.
196#define MAYBE_BasicRecordAndStop DISABLED_BasicRecordAndStop
197#define MAYBE_OutOfOrderRecordAndStopMultiple DISABLED_OutOfOrderRecordAndStopMultiple
198#define MAYBE_RecordingEndToEnd DISABLED_RecordingEndToEnd
199#else
200#define MAYBE_BasicRecordAndStop BasicRecordAndStop
201#define MAYBE_OutOfOrderRecordAndStopMultiple OutOfOrderRecordAndStopMultiple
202#define MAYBE_RecordingEndToEnd RecordingEndToEnd
203#endif
204
205TEST_F(AudioRecorderTest, MAYBE_BasicRecordAndStop) {
206  CreateSimpleRecorder();
207
208  recorder_->Record();
209  EXPECT_TRUE(IsRecording());
210  recorder_->Stop();
211  EXPECT_FALSE(IsRecording());
212  recorder_->Record();
213
214  EXPECT_TRUE(IsRecording());
215  recorder_->Stop();
216  EXPECT_FALSE(IsRecording());
217  recorder_->Record();
218
219  EXPECT_TRUE(IsRecording());
220  recorder_->Stop();
221  EXPECT_FALSE(IsRecording());
222
223  DeleteRecorder();
224}
225
226TEST_F(AudioRecorderTest, MAYBE_OutOfOrderRecordAndStopMultiple) {
227  CreateSimpleRecorder();
228
229  recorder_->Stop();
230  recorder_->Stop();
231  recorder_->Stop();
232  EXPECT_FALSE(IsRecording());
233
234  recorder_->Record();
235  recorder_->Record();
236  EXPECT_TRUE(IsRecording());
237
238  recorder_->Stop();
239  recorder_->Stop();
240  EXPECT_FALSE(IsRecording());
241
242  DeleteRecorder();
243}
244
245TEST_F(AudioRecorderTest, MAYBE_RecordingEndToEnd) {
246  const int kNumSamples = 48000 * 3;
247  CreateRecorder(
248      kDefaultChannels, kDefaultSampleRate, kDefaultBitsPerSample, kNumSamples);
249
250  RecordAndVerifySamples();
251
252  DeleteRecorder();
253}
254
255// TODO(rkc): Add tests with recording different sample rates.
256
257}  // namespace copresence
258