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: 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 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 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. 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 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 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 177 void cras_iodev_free_audio_area(struct cras_iodev *iodev) 178 { 179 } 180 181 void cras_iodev_free_format(struct cras_iodev *iodev) 182 { 183 } 184 185 void cras_iodev_init_audio_area(struct cras_iodev *iodev, int num_channels) 186 { 187 iodev->area = dummy_audio_area; 188 } 189 190 void cras_iodev_add_node(struct cras_iodev *iodev, struct cras_ionode *node) 191 { 192 DL_APPEND(iodev->nodes, node); 193 } 194 195 void cras_iodev_set_active_node(struct cras_iodev *iodev, 196 struct cras_ionode *node) 197 { 198 } 199 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 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 216 int cras_iodev_list_add_input(struct cras_iodev *input) 217 { 218 cras_iodev_list_add_input_called++; 219 return 0; 220 } 221 222 int cras_iodev_list_rm_input(struct cras_iodev *input) 223 { 224 cras_iodev_list_rm_input_called++; 225 return 0; 226 } 227 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 240 int clock_gettime(clockid_t clk_id, struct timespec *tp) { 241 *tp = time_now; 242 return 0; 243 } 244 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 255 int main(int argc, char **argv) { 256 ::testing::InitGoogleTest(&argc, argv); 257 return RUN_ALL_TESTS(); 258 } 259