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