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