1// Copyright 2013 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 "base/logging.h"
6#include "media/base/audio_bus.h"
7#include "media/base/audio_hash.h"
8#include "media/base/fake_audio_render_callback.h"
9#include "testing/gtest/include/gtest/gtest.h"
10
11namespace media {
12
13static const int kChannelCount = 2;
14static const int kFrameCount = 1024;
15
16class AudioHashTest : public testing::Test {
17 public:
18  AudioHashTest()
19      : bus_one_(AudioBus::Create(kChannelCount, kFrameCount)),
20        bus_two_(AudioBus::Create(kChannelCount, kFrameCount)),
21        fake_callback_(0.01) {
22
23    // Fill each channel in each bus with unique data.
24    GenerateUniqueChannels(bus_one_.get());
25    GenerateUniqueChannels(bus_two_.get());
26  }
27
28  void GenerateUniqueChannels(AudioBus* audio_bus) {
29    // Use an AudioBus wrapper to avoid an extra memcpy when filling channels.
30    scoped_ptr<AudioBus> wrapped_bus = AudioBus::CreateWrapper(1);
31    wrapped_bus->set_frames(audio_bus->frames());
32
33    // Since FakeAudioRenderCallback generates only a single channel of unique
34    // audio data, we need to fill each channel manually.
35    for (int ch = 0; ch < audio_bus->channels(); ++ch) {
36      wrapped_bus->SetChannelData(0, audio_bus->channel(ch));
37      fake_callback_.Render(wrapped_bus.get(), 0);
38    }
39  }
40
41  virtual ~AudioHashTest() {}
42
43 protected:
44  scoped_ptr<AudioBus> bus_one_;
45  scoped_ptr<AudioBus> bus_two_;
46  FakeAudioRenderCallback fake_callback_;
47
48  DISALLOW_COPY_AND_ASSIGN(AudioHashTest);
49};
50
51// Ensure the same data hashes the same.
52TEST_F(AudioHashTest, Equivalence) {
53  AudioHash hash_one;
54  hash_one.Update(bus_one_.get(), bus_one_->frames());
55
56  AudioHash hash_two;
57  hash_two.Update(bus_one_.get(), bus_one_->frames());
58
59  EXPECT_EQ(hash_one.ToString(), hash_two.ToString());
60}
61
62// Ensure sample order matters to the hash.
63TEST_F(AudioHashTest, SampleOrder) {
64  AudioHash original_hash;
65  original_hash.Update(bus_one_.get(), bus_one_->frames());
66
67  // Swap a sample in the bus.
68  std::swap(bus_one_->channel(0)[0], bus_one_->channel(0)[1]);
69
70  AudioHash swapped_hash;
71  swapped_hash.Update(bus_one_.get(), bus_one_->frames());
72
73  EXPECT_NE(original_hash.ToString(), swapped_hash.ToString());
74}
75
76// Ensure channel order matters to the hash.
77TEST_F(AudioHashTest, ChannelOrder) {
78  AudioHash original_hash;
79  original_hash.Update(bus_one_.get(), bus_one_->frames());
80
81  // Reverse channel order for the same sample data.
82  const int channels = bus_one_->channels();
83  scoped_ptr<AudioBus> swapped_ch_bus = AudioBus::CreateWrapper(channels);
84  swapped_ch_bus->set_frames(bus_one_->frames());
85  for (int i = channels - 1; i >= 0; --i)
86    swapped_ch_bus->SetChannelData(channels - (i + 1), bus_one_->channel(i));
87
88  AudioHash swapped_hash;
89  swapped_hash.Update(swapped_ch_bus.get(), swapped_ch_bus->frames());
90
91  EXPECT_NE(original_hash.ToString(), swapped_hash.ToString());
92}
93
94// Ensure bus order matters to the hash.
95TEST_F(AudioHashTest, BusOrder) {
96  AudioHash original_hash;
97  original_hash.Update(bus_one_.get(), bus_one_->frames());
98  original_hash.Update(bus_two_.get(), bus_two_->frames());
99
100  AudioHash reordered_hash;
101  reordered_hash.Update(bus_two_.get(), bus_two_->frames());
102  reordered_hash.Update(bus_one_.get(), bus_one_->frames());
103
104  EXPECT_NE(original_hash.ToString(), reordered_hash.ToString());
105}
106
107// Ensure bus order matters to the hash even with empty buses.
108TEST_F(AudioHashTest, EmptyBusOrder) {
109  bus_one_->Zero();
110  bus_two_->Zero();
111
112  AudioHash one_bus_hash;
113  one_bus_hash.Update(bus_one_.get(), bus_one_->frames());
114
115  AudioHash two_bus_hash;
116  two_bus_hash.Update(bus_one_.get(), bus_one_->frames());
117  two_bus_hash.Update(bus_two_.get(), bus_two_->frames());
118
119  EXPECT_NE(one_bus_hash.ToString(), two_bus_hash.ToString());
120}
121
122// Where A = [0, n], ensure hash(A[0:n/2]), hash(A[n/2:n]) and hash(A) result
123// in the same value.
124TEST_F(AudioHashTest, HashIgnoresUpdateOrder) {
125  AudioHash full_hash;
126  full_hash.Update(bus_one_.get(), bus_one_->frames());
127
128  AudioHash half_hash;
129  half_hash.Update(bus_one_.get(), bus_one_->frames() / 2);
130
131  // Create a new bus representing the second half of |bus_one_|.
132  const int half_frames = bus_one_->frames() / 2;
133  const int channels = bus_one_->channels();
134  scoped_ptr<AudioBus> half_bus = AudioBus::CreateWrapper(channels);
135  half_bus->set_frames(half_frames);
136  for (int i = 0; i < channels; ++i)
137    half_bus->SetChannelData(i, bus_one_->channel(i) + half_frames);
138
139  half_hash.Update(half_bus.get(), half_bus->frames());
140  EXPECT_EQ(full_hash.ToString(), half_hash.ToString());
141}
142
143// Ensure approximate hashes pass verification.
144TEST_F(AudioHashTest, VerifySimilarHash) {
145  AudioHash hash_one;
146  hash_one.Update(bus_one_.get(), bus_one_->frames());
147
148  // Twiddle the values inside the first bus.
149  float* channel = bus_one_->channel(0);
150  for (int i = 0; i < bus_one_->frames(); i += bus_one_->frames() / 64)
151    channel[i] += 0.0001f;
152
153  AudioHash hash_two;
154  hash_two.Update(bus_one_.get(), bus_one_->frames());
155
156  EXPECT_EQ(hash_one.ToString(), hash_two.ToString());
157
158  // Twiddle the values too much...
159  for (int i = 0; i < bus_one_->frames(); ++i)
160    channel[i] += 0.0001f;
161
162  AudioHash hash_three;
163  hash_three.Update(bus_one_.get(), bus_one_->frames());
164  EXPECT_NE(hash_one.ToString(), hash_three.ToString());
165}
166
167}  // namespace media
168