// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <gtest/gtest.h>
extern "C" {
#include "cras_apm_list.h"
#include "cras_audio_area.h"
#include "cras_dsp_pipeline.h"
#include "cras_iodev.h"
#include "cras_iodev_list.h"
#include "cras_types.h"
#include "float_buffer.h"
#include "webrtc_apm.h"
}
namespace {
static void *stream_ptr = reinterpret_cast<void *>(0x123);
static void *dev_ptr = reinterpret_cast<void *>(0x345);
static void *dev_ptr2 = reinterpret_cast<void *>(0x678);
static struct cras_apm_list *list;
static struct cras_audio_area fake_audio_area;
static unsigned int dsp_util_interleave_frames;
static unsigned int webrtc_apm_process_stream_f_called;
static unsigned int webrtc_apm_process_reverse_stream_f_called;
static device_enabled_callback_t device_enabled_callback_val;
static struct ext_dsp_module *ext_dsp_module_value;
static struct cras_iodev fake_iodev;
TEST(ApmList, ApmListCreate) {
list = cras_apm_list_create(stream_ptr, 0);
EXPECT_EQ((void *)NULL, list);
list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
EXPECT_NE((void *)NULL, list);
EXPECT_EQ(APM_ECHO_CANCELLATION, cras_apm_list_get_effects(list));
cras_apm_list_destroy(list);
}
TEST(ApmList, AddRemoveApm) {
struct cras_audio_format fmt;
fmt.num_channels = 2;
fmt.frame_rate = 48000;
fmt.format = SND_PCM_FORMAT_S16_LE;
list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
EXPECT_NE((void *)NULL, list);
EXPECT_NE((void *)NULL, cras_apm_list_add(list, dev_ptr, &fmt));
EXPECT_EQ((void *)NULL, cras_apm_list_get(list, dev_ptr2));
EXPECT_NE((void *)NULL, cras_apm_list_add(list, dev_ptr2, &fmt));
EXPECT_NE((void *)NULL, cras_apm_list_get(list, dev_ptr));
cras_apm_list_remove(list, dev_ptr);
EXPECT_EQ((void *)NULL, cras_apm_list_get(list, dev_ptr));
EXPECT_NE((void *)NULL, cras_apm_list_get(list, dev_ptr2));
cras_apm_list_remove(list, dev_ptr2);
EXPECT_EQ((void *)NULL, cras_apm_list_get(list, dev_ptr2));
cras_apm_list_destroy(list);
}
TEST(ApmList, ApmProcessForwardBuffer) {
struct cras_apm *apm;
struct cras_audio_format fmt;
struct cras_audio_area *area;
struct float_buffer *buf;
fmt.num_channels = 2;
fmt.frame_rate = 48000;
fmt.format = SND_PCM_FORMAT_S16_LE;
list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
EXPECT_NE((void *)NULL, list);
apm = cras_apm_list_add(list, dev_ptr, &fmt);
buf = float_buffer_create(500, 2);
float_buffer_written(buf, 300);
webrtc_apm_process_stream_f_called = 0;
cras_apm_list_process(apm, buf, 0);
EXPECT_EQ(0, webrtc_apm_process_stream_f_called);
area = cras_apm_list_get_processed(apm);
EXPECT_EQ(0, area->frames);
float_buffer_reset(buf);
float_buffer_written(buf, 200);
cras_apm_list_process(apm, buf, 0);
area = cras_apm_list_get_processed(apm);
EXPECT_EQ(1, webrtc_apm_process_stream_f_called);
EXPECT_EQ(480, dsp_util_interleave_frames);
EXPECT_EQ(480, area->frames);
/* Put some processed frames. Another apm_list process will not call
* into webrtc_apm because the processed buffer is not yet empty.
*/
cras_apm_list_put_processed(apm, 200);
float_buffer_reset(buf);
float_buffer_written(buf, 500);
cras_apm_list_process(apm, buf, 0);
EXPECT_EQ(1, webrtc_apm_process_stream_f_called);
/* Put another 280 processed frames, so it's now ready for webrtc_apm
* to process another chunk of 480 frames (10ms) data.
*/
cras_apm_list_put_processed(apm, 280);
cras_apm_list_process(apm, buf, 0);
EXPECT_EQ(2, webrtc_apm_process_stream_f_called);
float_buffer_destroy(&buf);
cras_apm_list_destroy(list);
}
TEST(ApmList, ApmProcessReverseData) {
struct cras_apm *apm;
struct cras_audio_format fmt;
struct float_buffer *buf;
float *const *rp;
unsigned int nread;
struct cras_iodev fake_iodev;
fmt.num_channels = 2;
fmt.frame_rate = 48000;
fmt.format = SND_PCM_FORMAT_S16_LE;
fake_iodev.direction = CRAS_STREAM_OUTPUT;
device_enabled_callback_val = NULL;
ext_dsp_module_value = NULL;
webrtc_apm_process_reverse_stream_f_called = 0;
cras_apm_list_init("");
EXPECT_NE((void *)NULL, device_enabled_callback_val);
device_enabled_callback_val(&fake_iodev, NULL);
EXPECT_NE((void *)NULL, ext_dsp_module_value);
EXPECT_NE((void *)NULL, ext_dsp_module_value->ports);
buf = float_buffer_create(500, 2);
float_buffer_written(buf, 500);
nread = 500;
rp = float_buffer_read_pointer(buf, 0, &nread);
for (int i = 0; i < buf->num_channels; i++)
ext_dsp_module_value->ports[i] = rp[i];
ext_dsp_module_value->configure(ext_dsp_module_value,
800, 2, 48000);
ext_dsp_module_value->run(ext_dsp_module_value, 500);
EXPECT_EQ(0, webrtc_apm_process_reverse_stream_f_called);
list = cras_apm_list_create(stream_ptr, APM_ECHO_CANCELLATION);
EXPECT_NE((void *)NULL, list);
apm = cras_apm_list_add(list, dev_ptr, &fmt);
ext_dsp_module_value->run(ext_dsp_module_value, 250);
EXPECT_EQ(0, webrtc_apm_process_reverse_stream_f_called);
ext_dsp_module_value->run(ext_dsp_module_value, 250);
EXPECT_EQ(1, webrtc_apm_process_reverse_stream_f_called);
float_buffer_destroy(&buf);
cras_apm_list_deinit();
}
extern "C" {
int cras_iodev_list_set_device_enabled_callback(
device_enabled_callback_t enabled_cb,
device_disabled_callback_t disabled_cb,
void *cb_data)
{
device_enabled_callback_val = enabled_cb;
return 0;
}
struct cras_iodev *cras_iodev_list_get_first_enabled_iodev(
enum CRAS_STREAM_DIRECTION direction)
{
return &fake_iodev;
}
void cras_iodev_set_ext_dsp_module(struct cras_iodev *iodev,
struct ext_dsp_module *ext)
{
ext_dsp_module_value = ext;
}
struct cras_audio_area *cras_audio_area_create(int num_channels)
{
return &fake_audio_area;
}
void cras_audio_area_destroy(struct cras_audio_area *area)
{
}
void cras_audio_area_config_channels(struct cras_audio_area *area,
const struct cras_audio_format *fmt)
{
}
void cras_audio_area_config_buf_pointers(struct cras_audio_area *area,
const struct cras_audio_format *fmt,
uint8_t *base_buffer)
{
}
void dsp_util_interleave(float *const *input, int16_t *output, int channels,
snd_pcm_format_t format, int frames)
{
dsp_util_interleave_frames = frames;
}
struct aec_config *aec_config_get(const char *device_config_dir)
{
return NULL;
}
void aec_config_dump(struct aec_config *config)
{
}
struct apm_config *apm_config_get(const char *device_config_dir)
{
return NULL;
}
void apm_config_dump(struct apm_config *config)
{
}
webrtc_apm webrtc_apm_create(unsigned int num_channels,
unsigned int frame_rate,
struct aec_config *aec_config,
struct apm_config *apm_config)
{
return reinterpret_cast<webrtc_apm>(0x11);
}
void webrtc_apm_destroy(webrtc_apm apm)
{
return;
}
int webrtc_apm_process_stream_f(webrtc_apm ptr,
int num_channels,
int rate,
float *const *data)
{
webrtc_apm_process_stream_f_called++;
return 0;
}
int webrtc_apm_process_reverse_stream_f(
webrtc_apm ptr,
int num_channels, int rate,
float *const *data)
{
webrtc_apm_process_reverse_stream_f_called++;
return 0;
}
int webrtc_apm_aec_dump(webrtc_apm ptr, void** work_queue,
int start, FILE *handle)
{
return 0;
}
} // extern "C"
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}