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
11 namespace media {
12
13 static const int kChannelCount = 2;
14 static const int kFrameCount = 1024;
15
16 class AudioHashTest : public testing::Test {
17 public:
AudioHashTest()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
GenerateUniqueChannels(AudioBus * audio_bus)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
~AudioHashTest()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.
TEST_F(AudioHashTest,Equivalence)52 TEST_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.
TEST_F(AudioHashTest,SampleOrder)63 TEST_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.
TEST_F(AudioHashTest,ChannelOrder)77 TEST_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.
TEST_F(AudioHashTest,BusOrder)95 TEST_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.
TEST_F(AudioHashTest,EmptyBusOrder)108 TEST_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.
TEST_F(AudioHashTest,HashIgnoresUpdateOrder)124 TEST_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.
TEST_F(AudioHashTest,VerifySimilarHash)144 TEST_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