1 // Copyright (c) 2013 The Chromium OS 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 <gtest/gtest.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8
9 extern "C" {
10 #include "cras_audio_area.h"
11 #include "cras_iodev.h"
12 #include "cras_iodev_list.h"
13 #include "cras_loopback_iodev.h"
14 #include "cras_shm.h"
15 #include "cras_types.h"
16 #include "dev_stream.h"
17 #include "utlist.h"
18 }
19
20 namespace {
21
22 static const unsigned int kBufferFrames = 16384;
23 static const unsigned int kFrameBytes = 4;
24 static const unsigned int kBufferSize = kBufferFrames * kFrameBytes;
25
26 static struct timespec time_now;
27 static cras_audio_area* dummy_audio_area;
28 static loopback_hook_data_t loop_hook;
29 static struct cras_iodev* enabled_dev;
30 static unsigned int cras_iodev_list_add_input_called;
31 static unsigned int cras_iodev_list_rm_input_called;
32 static unsigned int cras_iodev_list_set_device_enabled_callback_called;
33 static device_enabled_callback_t device_enabled_callback_cb;
34 static device_disabled_callback_t device_disabled_callback_cb;
35 static void* device_enabled_callback_cb_data;
36 static int cras_iodev_list_register_loopback_called;
37 static int cras_iodev_list_unregister_loopback_called;
38
39 class LoopBackTestSuite : public testing::Test {
40 protected:
SetUp()41 virtual void SetUp() {
42 dummy_audio_area = (cras_audio_area*)calloc(
43 1, sizeof(*dummy_audio_area) + sizeof(cras_channel_area) * 2);
44 for (unsigned int i = 0; i < kBufferSize; i++) {
45 buf_[i] = rand();
46 }
47 fmt_.frame_rate = 48000;
48 fmt_.num_channels = 2;
49 fmt_.format = SND_PCM_FORMAT_S16_LE;
50
51 loop_in_ = loopback_iodev_create(LOOPBACK_POST_MIX_PRE_DSP);
52 EXPECT_EQ(1, cras_iodev_list_add_input_called);
53 loop_in_->format = &fmt_;
54
55 loop_hook = NULL;
56 cras_iodev_list_add_input_called = 0;
57 cras_iodev_list_rm_input_called = 0;
58 cras_iodev_list_set_device_enabled_callback_called = 0;
59 cras_iodev_list_register_loopback_called = 0;
60 cras_iodev_list_unregister_loopback_called = 0;
61 }
62
TearDown()63 virtual void TearDown() {
64 loopback_iodev_destroy(loop_in_);
65 EXPECT_EQ(1, cras_iodev_list_rm_input_called);
66 EXPECT_EQ(NULL, device_enabled_callback_cb);
67 EXPECT_EQ(NULL, device_disabled_callback_cb);
68 free(dummy_audio_area);
69 }
70
71 uint8_t buf_[kBufferSize];
72 struct cras_audio_format fmt_;
73 struct cras_iodev* loop_in_;
74 };
75
TEST_F(LoopBackTestSuite,InstallLoopHook)76 TEST_F(LoopBackTestSuite, InstallLoopHook) {
77 struct cras_iodev iodev;
78 struct timespec tstamp;
79
80 iodev.direction = CRAS_STREAM_OUTPUT;
81 iodev.format = &fmt_;
82 iodev.streams = NULL;
83 iodev.info.idx = 123;
84 enabled_dev = &iodev;
85
86 // Open loopback devices.
87 EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
88 EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
89 EXPECT_EQ(1, cras_iodev_list_register_loopback_called);
90
91 // Signal an output device is enabled.
92 device_enabled_callback_cb(&iodev, device_enabled_callback_cb_data);
93
94 // Expect that a hook was added to the iodev
95 EXPECT_EQ(2, cras_iodev_list_register_loopback_called);
96 ASSERT_NE(reinterpret_cast<loopback_hook_data_t>(NULL), loop_hook);
97
98 // Check zero frames queued.
99 EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
100
101 device_disabled_callback_cb(&iodev, device_enabled_callback_cb_data);
102 EXPECT_EQ(1, cras_iodev_list_unregister_loopback_called);
103 EXPECT_EQ(3, cras_iodev_list_register_loopback_called);
104
105 enabled_dev->info.idx = 456;
106 device_enabled_callback_cb(&iodev, device_enabled_callback_cb_data);
107 EXPECT_EQ(4, cras_iodev_list_register_loopback_called);
108
109 // Close loopback devices.
110 EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
111 EXPECT_EQ(2, cras_iodev_list_unregister_loopback_called);
112 EXPECT_EQ(2, cras_iodev_list_set_device_enabled_callback_called);
113 }
114
TEST_F(LoopBackTestSuite,SelectDevFromAToB)115 TEST_F(LoopBackTestSuite, SelectDevFromAToB) {
116 struct cras_iodev iodev1, iodev2;
117
118 iodev1.direction = CRAS_STREAM_OUTPUT;
119 iodev2.direction = CRAS_STREAM_OUTPUT;
120 enabled_dev = &iodev1;
121
122 enabled_dev->info.idx = 111;
123 EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
124 EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
125 EXPECT_EQ(1, cras_iodev_list_register_loopback_called);
126
127 /* Not the current sender being disabled, assert unregister not called. */
128 iodev2.info.idx = 222;
129 device_disabled_callback_cb(&iodev2, device_enabled_callback_cb_data);
130 EXPECT_EQ(0, cras_iodev_list_unregister_loopback_called);
131 EXPECT_EQ(1, cras_iodev_list_register_loopback_called);
132
133 enabled_dev = &iodev2;
134 device_disabled_callback_cb(&iodev1, device_enabled_callback_cb_data);
135 EXPECT_EQ(1, cras_iodev_list_unregister_loopback_called);
136 EXPECT_EQ(2, cras_iodev_list_register_loopback_called);
137
138 EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
139 }
140
141 // Test how loopback works if there isn't any output devices open.
TEST_F(LoopBackTestSuite,OpenIdleSystem)142 TEST_F(LoopBackTestSuite, OpenIdleSystem) {
143 cras_audio_area* area;
144 unsigned int nread = 1024;
145 struct timespec tstamp;
146 int rc;
147
148 // No active output device.
149 enabled_dev = NULL;
150 time_now.tv_sec = 100;
151 time_now.tv_nsec = 0;
152
153 EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
154 EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
155
156 // Should be 480 samples after 480/frame rate seconds
157 time_now.tv_nsec += 480 * 1e9 / 48000;
158 EXPECT_EQ(480, loop_in_->frames_queued(loop_in_, &tstamp));
159
160 // Verify frames from loopback record.
161 loop_in_->get_buffer(loop_in_, &area, &nread);
162 EXPECT_EQ(480, nread);
163 memset(buf_, 0, nread * kFrameBytes);
164 rc = memcmp(area->channels[0].buf, buf_, nread * kFrameBytes);
165 EXPECT_EQ(0, rc);
166 loop_in_->put_buffer(loop_in_, nread);
167
168 // Check zero frames queued.
169 EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
170
171 EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
172 }
173
TEST_F(LoopBackTestSuite,SimpleLoopback)174 TEST_F(LoopBackTestSuite, SimpleLoopback) {
175 cras_audio_area* area;
176 unsigned int nframes = 1024;
177 unsigned int nread = 1024;
178 int rc;
179 struct cras_iodev iodev;
180 struct dev_stream stream;
181 struct timespec tstamp;
182
183 iodev.streams = &stream;
184 enabled_dev = &iodev;
185
186 loop_in_->configure_dev(loop_in_);
187 ASSERT_NE(reinterpret_cast<void*>(NULL), loop_hook);
188
189 // Loopback callback for the hook.
190 loop_hook(buf_, nframes, &fmt_, loop_in_);
191
192 // Verify frames from loopback record.
193 loop_in_->get_buffer(loop_in_, &area, &nread);
194 EXPECT_EQ(nframes, nread);
195 rc = memcmp(area->channels[0].buf, buf_, nframes * kFrameBytes);
196 EXPECT_EQ(0, rc);
197 loop_in_->put_buffer(loop_in_, nread);
198
199 // Check zero frames queued.
200 EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
201
202 EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
203 }
204
205 // TODO(chinyue): Test closing last iodev while streaming loopback data.
206
207 /* Stubs */
208 extern "C" {
209
cras_audio_area_config_buf_pointers(struct cras_audio_area * area,const struct cras_audio_format * fmt,uint8_t * base_buffer)210 void cras_audio_area_config_buf_pointers(struct cras_audio_area* area,
211 const struct cras_audio_format* fmt,
212 uint8_t* base_buffer) {
213 dummy_audio_area->channels[0].buf = base_buffer;
214 }
215
cras_iodev_free_audio_area(struct cras_iodev * iodev)216 void cras_iodev_free_audio_area(struct cras_iodev* iodev) {}
217
cras_iodev_free_format(struct cras_iodev * iodev)218 void cras_iodev_free_format(struct cras_iodev* iodev) {}
219
cras_iodev_init_audio_area(struct cras_iodev * iodev,int num_channels)220 void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) {
221 iodev->area = dummy_audio_area;
222 }
223
cras_iodev_add_node(struct cras_iodev * iodev,struct cras_ionode * node)224 void cras_iodev_add_node(struct cras_iodev* iodev, struct cras_ionode* node) {
225 DL_APPEND(iodev->nodes, node);
226 }
227
cras_iodev_set_active_node(struct cras_iodev * iodev,struct cras_ionode * node)228 void cras_iodev_set_active_node(struct cras_iodev* iodev,
229 struct cras_ionode* node) {}
cras_iodev_list_register_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,unsigned int output_dev_idx,loopback_hook_data_t hook_data,loopback_hook_control_t hook_start,unsigned int loopback_dev_idx)230 void cras_iodev_list_register_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,
231 unsigned int output_dev_idx,
232 loopback_hook_data_t hook_data,
233 loopback_hook_control_t hook_start,
234 unsigned int loopback_dev_idx) {
235 cras_iodev_list_register_loopback_called++;
236 loop_hook = hook_data;
237 }
238
cras_iodev_list_unregister_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,unsigned int output_dev_idx,unsigned int loopback_dev_idx)239 void cras_iodev_list_unregister_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,
240 unsigned int output_dev_idx,
241 unsigned int loopback_dev_idx) {
242 cras_iodev_list_unregister_loopback_called++;
243 }
244
cras_iodev_list_add_input(struct cras_iodev * input)245 int cras_iodev_list_add_input(struct cras_iodev* input) {
246 cras_iodev_list_add_input_called++;
247 return 0;
248 }
249
cras_iodev_list_rm_input(struct cras_iodev * input)250 int cras_iodev_list_rm_input(struct cras_iodev* input) {
251 cras_iodev_list_rm_input_called++;
252 return 0;
253 }
254
cras_iodev_list_set_device_enabled_callback(device_enabled_callback_t enabled_cb,device_disabled_callback_t disabled_cb,void * cb_data)255 int cras_iodev_list_set_device_enabled_callback(
256 device_enabled_callback_t enabled_cb,
257 device_disabled_callback_t disabled_cb,
258 void* cb_data) {
259 cras_iodev_list_set_device_enabled_callback_called++;
260 device_enabled_callback_cb = enabled_cb;
261 device_disabled_callback_cb = disabled_cb;
262 device_enabled_callback_cb_data = cb_data;
263 return 0;
264 }
265
clock_gettime(clockid_t clk_id,struct timespec * tp)266 int clock_gettime(clockid_t clk_id, struct timespec* tp) {
267 *tp = time_now;
268 return 0;
269 }
270
cras_iodev_list_get_first_enabled_iodev(enum CRAS_STREAM_DIRECTION direction)271 struct cras_iodev* cras_iodev_list_get_first_enabled_iodev(
272 enum CRAS_STREAM_DIRECTION direction) {
273 return enabled_dev;
274 }
275
276 } // extern "C"
277
278 } // namespace
279
main(int argc,char ** argv)280 int main(int argc, char** argv) {
281 ::testing::InitGoogleTest(&argc, argv);
282 return RUN_ALL_TESTS();
283 }
284