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