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