replace common qcom sources with samsung ones

This commit is contained in:
SaschaNes
2025-08-12 22:13:00 +02:00
parent ba24dcded9
commit 6f7753de11
5682 changed files with 2450203 additions and 103634 deletions

View File

@@ -0,0 +1,6 @@
MY_LOCAL_PATH := $(call my-dir)
include $(MY_LOCAL_PATH)/tinyalsa/Android.mk
include $(MY_LOCAL_PATH)/tinyalsa/test/Android.mk
ifneq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
include $(MY_LOCAL_PATH)/tinyalsa/test/gtest/Android.mk
endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,554 @@
/*
** Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**/
#define LOG_TAG "PLUGIN: AGMIO"
#include <stdio.h>
#include <sys/poll.h>
#include <sys/eventfd.h>
#include <alsa/asoundlib.h>
#include <alsa/pcm_external.h>
#include <agm/agm_api.h>
#include <agm/agm_list.h>
#include <snd-card-def.h>
#include "utils.h"
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
enum {
AGM_IO_STATE_OPEN = 1,
AGM_IO_STATE_SETUP,
AGM_IO_STATE_PREPARED,
AGM_IO_STATE_RUNNING,
};
struct agmio_priv {
snd_pcm_ioplug_t io;
int card;
int device;
void *card_node;
void *pcm_node;
uint64_t handle;
struct agm_media_config *media_config;
struct agm_buffer_config *buffer_config;
struct agm_session_config *session_config;
unsigned int period_size;
size_t frame_size;
unsigned int state;
snd_pcm_uframes_t hw_pointer;
snd_pcm_uframes_t boundary;
int event_fd;
/* add private variables here */
};
static int agm_get_session_handle(struct agmio_priv *priv,
uint64_t *handle)
{
if (!priv)
return -EINVAL;
*handle = priv->handle;
if (!*handle)
return -EINVAL;
return 0;
}
static int agm_io_start(snd_pcm_ioplug_t * io)
{
struct agmio_priv *pcm = io->private_data;
uint64_t handle;
int ret;
ret = agm_get_session_handle(pcm, &handle);
if (ret)
return ret;
if (pcm->state < AGM_IO_STATE_PREPARED) {
ret = agm_session_prepare(handle);
errno = ret;
if (ret)
return ret;
pcm->state = AGM_IO_STATE_PREPARED;
}
if (pcm->state != AGM_IO_STATE_RUNNING) {
ret = agm_session_start(handle);
if (!ret)
pcm->state = AGM_IO_STATE_RUNNING;
}
AGM_LOGD("%s: exit\n", __func__);
return ret;
}
static int agm_io_stop(snd_pcm_ioplug_t * io)
{
struct agmio_priv *pcm = io->private_data;
uint64_t handle;
int ret;
ret = agm_get_session_handle(pcm, &handle);
if (ret)
return ret;
ret = agm_session_stop(handle);
AGM_LOGD("%s: exit\n", __func__);
return ret;
}
static int agm_io_drain(snd_pcm_ioplug_t * io)
{
AGM_LOGD("%s: exit\n", __func__);
return 0;
}
static snd_pcm_sframes_t agm_io_pointer(snd_pcm_ioplug_t * io)
{
struct agmio_priv *pcm = io->private_data;
snd_pcm_sframes_t new_hw_ptr;
new_hw_ptr = pcm->hw_pointer;
if (io->stream == SND_PCM_STREAM_CAPTURE) {
if (pcm->hw_pointer == 0)
new_hw_ptr = io->period_size * pcm->frame_size;
}
AGM_LOGD("%s %d\n", __func__, io->state);
return new_hw_ptr;
}
static snd_pcm_sframes_t agm_io_transfer(snd_pcm_ioplug_t * io,
const snd_pcm_channel_area_t * areas,
snd_pcm_uframes_t offset,
snd_pcm_uframes_t size)
{
struct agmio_priv *pcm = io->private_data;
uint64_t handle;
uint8_t *buf = (uint8_t *) areas->addr + (areas->first + areas->step * offset) / 8;
size_t count;
int ret = 0;
ret = agm_get_session_handle(pcm, &handle);
if (ret)
return ret;
if (pcm->state != AGM_IO_STATE_RUNNING) {
ret = agm_io_start(io);
if (ret)
return ret;
}
count = size * pcm->frame_size;
if (io->stream == SND_PCM_STREAM_PLAYBACK)
ret = agm_session_write(handle, buf, &count);
else
ret = agm_session_read(handle, buf, &count);
if (ret == 0) {
ret = snd_pcm_bytes_to_frames(io->pcm, count);
pcm->hw_pointer += ret;
}
if (pcm->hw_pointer > pcm->boundary)
pcm->hw_pointer -= pcm->boundary;
AGM_LOGD("%s: exit\n", __func__);
return ret;
}
static int agm_io_prepare(snd_pcm_ioplug_t * io)
{
uint64_t handle;
struct agmio_priv *pcm = io->private_data;
int ret = 0;
ret = agm_get_session_handle(pcm, &handle);
if (ret)
return ret;
ret = agm_session_prepare(handle);
AGM_LOGD("%s: exit\n", __func__);
return ret;
}
static int agm_io_hw_params(snd_pcm_ioplug_t * io,
snd_pcm_hw_params_t * params)
{
struct agmio_priv *pcm = io->private_data;
struct agm_media_config *media_config;
struct agm_buffer_config *buffer_config;
struct agm_session_config *session_config = NULL;
uint64_t handle;
int ret = 0, sess_mode = 0;
ret = agm_get_session_handle(pcm, &handle);
pcm->frame_size = (snd_pcm_format_physical_width(io->format) * io->channels) / 8;
media_config = pcm->media_config;
buffer_config = pcm->buffer_config;
session_config = pcm->session_config;
media_config->rate = io->rate;
media_config->channels = io->channels;
media_config->format = io->format;
buffer_config->count = io->buffer_size / io->period_size;
pcm->period_size = io->period_size;
buffer_config->size = io->period_size * pcm->frame_size;
pcm->hw_pointer = 0;
snd_card_def_get_int(pcm->pcm_node, "session_mode", &sess_mode);
session_config->dir = (io->stream == SND_PCM_STREAM_PLAYBACK) ? RX : TX;
session_config->sess_mode = sess_mode;
ret = agm_session_set_config(pcm->handle, session_config,
pcm->media_config, pcm->buffer_config);
if (!ret)
pcm->state = AGM_IO_STATE_SETUP;
AGM_LOGD("%s: exit\n", __func__);
return ret;
}
static int agm_io_sw_params(snd_pcm_ioplug_t *io, snd_pcm_sw_params_t *params)
{
struct agmio_priv *pcm = io->private_data;
struct agm_session_config *session_config = NULL;
uint64_t handle = 0;
int ret = 0, sess_mode = 0;
snd_pcm_uframes_t start_threshold;
snd_pcm_uframes_t stop_threshold;
ret = agm_get_session_handle(pcm, &handle);
if (ret)
return ret;
session_config = pcm->session_config;
snd_card_def_get_int(pcm->pcm_node, "session_mode", &sess_mode);
session_config->dir = (io->stream == SND_PCM_STREAM_PLAYBACK) ? RX : TX;
session_config->sess_mode = sess_mode;
snd_pcm_sw_params_get_start_threshold(params, &start_threshold);
snd_pcm_sw_params_get_stop_threshold(params, &stop_threshold);
snd_pcm_sw_params_get_boundary(params, &pcm->boundary);
session_config->start_threshold = (uint32_t)start_threshold;
session_config->stop_threshold = (uint32_t)stop_threshold;
ret = agm_session_set_config(pcm->handle, session_config,
pcm->media_config, pcm->buffer_config);
AGM_LOGD("%s: exit\n", __func__);
return ret;
}
static int agm_io_close(snd_pcm_ioplug_t * io)
{
struct agmio_priv *pcm = io->private_data;
uint64_t handle;
int ret = 0;
ret = agm_get_session_handle(pcm, &handle);
if (ret)
return ret;
ret = agm_session_close(handle);
snd_card_def_put_card(pcm->card_node);
free(pcm->buffer_config);
free(pcm->media_config);
free(pcm->session_config);
free(io->private_data);
AGM_LOGD("%s: exit\n", __func__);
return 0;
}
static int agm_io_pause(snd_pcm_ioplug_t * io, int enable)
{
struct agmio_priv *pcm = io->private_data;
uint64_t handle;
int ret = 0;
ret = agm_get_session_handle(pcm, &handle);
if (ret)
return ret;
if (enable)
ret = agm_session_pause(handle);
else
ret = agm_session_resume(handle);
AGM_LOGD("%s: exit\n", __func__);
return ret;
}
static int agm_io_poll_desc_count(snd_pcm_ioplug_t *io) {
(void)io;
/* TODO : Needed for ULL usecases */
AGM_LOGD("%s: exit\n", __func__);
return 1;
}
static int agm_io_poll_desc(snd_pcm_ioplug_t *io, struct pollfd *pfd,
unsigned int space)
{
struct agmio_priv *pcm = io->private_data;
/* TODO : Needed for ULL usecases, Need update */
if (space != 1) {
AGM_LOGE("%s space %u is not correct!\n", __func__, space);
return -EINVAL;
}
if (io->stream == SND_PCM_STREAM_PLAYBACK) {
pfd[0].fd = pcm->event_fd;
pfd[0].events = POLLOUT;
} else {
pfd[0].fd = pcm->event_fd;
pfd[0].events = POLLIN;
}
AGM_LOGD("%s: exit\n", __func__);
return space;
}
static int agm_io_poll_revents(snd_pcm_ioplug_t *io, struct pollfd *pfd,
unsigned int nfds, unsigned short *revents)
{
struct agmio_priv *pcm = io->private_data;
/* TODO : Needed for ULL usecases, Need update */
if (nfds != 1) {
AGM_LOGE("%s nfds %u is not correct!\n", __func__, nfds);
return -EINVAL;
}
if (pfd[0].revents & POLLIN) {
*revents = POLLIN;
} else if (pfd[0].revents & POLLOUT) {
*revents = POLLOUT;
}
return 0;
}
static const snd_pcm_ioplug_callback_t agm_io_callback = {
.start = agm_io_start,
.stop = agm_io_stop,
.pointer = agm_io_pointer,
.drain = agm_io_drain,
.transfer = agm_io_transfer,
.prepare = agm_io_prepare,
.hw_params = agm_io_hw_params,
.sw_params = agm_io_sw_params,
.close = agm_io_close,
.pause = agm_io_pause,
.poll_descriptors_count = agm_io_poll_desc_count,
.poll_descriptors = agm_io_poll_desc,
.poll_revents = agm_io_poll_revents,
};
static int agm_hw_constraint(struct agmio_priv* priv)
{
snd_pcm_ioplug_t *io = &priv->io;
int ret;
static const snd_pcm_access_t access_list[] = {
SND_PCM_ACCESS_RW_INTERLEAVED,
SND_PCM_ACCESS_MMAP_INTERLEAVED
};
static const unsigned int formats[] = {
SND_PCM_FORMAT_U8,
SND_PCM_FORMAT_S16_LE,
SND_PCM_FORMAT_S32_LE,
SND_PCM_FORMAT_S24_3LE,
SND_PCM_FORMAT_S24_LE,
};
ret = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_ACCESS,
ARRAY_SIZE(access_list),
access_list);
if (ret < 0)
return ret;
ret = snd_pcm_ioplug_set_param_list(io, SND_PCM_IOPLUG_HW_FORMAT,
ARRAY_SIZE(formats), formats);
if (ret < 0)
return ret;
ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_CHANNELS,
1, 8);
if (ret < 0)
return ret;
ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_RATE,
8000, 384000);
if (ret < 0)
return ret;
ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIOD_BYTES,
64, 122880);
if (ret < 0)
return ret;
ret = snd_pcm_ioplug_set_param_minmax(io, SND_PCM_IOPLUG_HW_PERIODS,
1, 8);
if (ret < 0)
return ret;
return 0;
}
SND_PCM_PLUGIN_DEFINE_FUNC(agm)
{
snd_config_iterator_t it, next;
struct agmio_priv *priv = NULL;
long card = 0, device = 100;
struct agm_session_config *session_config;
struct agm_media_config *media_config;
struct agm_buffer_config *buffer_config;
void *card_node, *pcm_node;
enum agm_session_mode sess_mode = AGM_SESSION_DEFAULT;
uint64_t handle;
int ret = 0, session_id = device;
priv = calloc(1, sizeof(*priv));
if (!priv)
return -ENOMEM;
media_config = calloc(1, sizeof(struct agm_media_config));
if (!media_config)
return -ENOMEM;
buffer_config = calloc(1, sizeof(struct agm_buffer_config));
if (!buffer_config)
return -ENOMEM;
session_config = calloc(1, sizeof(struct agm_session_config));
if (!session_config)
return -ENOMEM;
snd_config_for_each(it, next, conf) {
snd_config_t *n = snd_config_iterator_entry(it);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (strcmp(id, "comment") == 0 || strcmp(id, "type") == 0 || strcmp(id, "hint") == 0)
continue;
if (strcmp(id, "card") == 0) {
if (snd_config_get_integer(n, &card) < 0) {
AGM_LOGE("Invalid type for %s", id);
ret = -EINVAL;
goto err_free_priv;
}
AGM_LOGD("card id is %d\n", card);
priv->card = card;
continue;
}
if (strcmp(id, "device") == 0) {
if (snd_config_get_integer(n, &device) < 0) {
AGM_LOGE("Invalid type for %s", id);
ret = -EINVAL;
goto err_free_priv;
}
AGM_LOGD("device id is %d\n", device);
priv->device = device;
continue;
}
}
card_node = snd_card_def_get_card(card);
if (!card_node) {
AGM_LOGE("card node is NULL\n");
ret = -EINVAL;
goto err_free_priv;
}
priv->card_node = card_node;
pcm_node = snd_card_def_get_node(card_node, device, SND_NODE_TYPE_PCM);
if (!pcm_node) {
AGM_LOGE("pcm node is NULL\n");
ret = -EINVAL;
goto err_free_priv;
}
priv->pcm_node = pcm_node;
snd_card_def_get_int(pcm_node, "session_mode", &sess_mode);
session_id = priv->device;
ret = agm_session_open(session_id, sess_mode, &handle);
if (ret) {
AGM_LOGE("handle is NULL\n");
ret = -EINVAL;
goto err_free_priv;
}
priv->media_config = media_config;
priv->buffer_config = buffer_config;
priv->session_config = session_config;
priv->handle = handle;
priv->event_fd = -1;
priv->state = AGM_IO_STATE_OPEN;
priv->io.version = SND_PCM_IOPLUG_VERSION;
priv->io.name = "AGM PCM I/O Plugin";
priv->io.mmap_rw = 0;
priv->io.callback = &agm_io_callback;
priv->io.private_data = priv;
ret = snd_pcm_ioplug_create(&priv->io, name, stream, mode);
if (ret < 0) {
AGM_LOGE("IO plugin create failed\n");
goto err_free_priv;
}
if ((priv->event_fd = eventfd(0, EFD_CLOEXEC)) == -1) {
AGM_LOGE("failed to create event_fd\n");
ret = -EINVAL;
goto err_free_priv;
}
ret = agm_hw_constraint(priv);
if (ret < 0) {
snd_pcm_ioplug_delete(&priv->io);
goto err_free_priv;
}
*pcmp = priv->io.pcm;
return 0;
err_free_priv:
free(priv);
return ret;
}
SND_PCM_PLUGIN_SYMBOL(agm);

View File

@@ -0,0 +1,124 @@
LOCAL_PATH := $(call my-dir)
# Build libagm_pcm_plugin
include $(CLEAR_VARS)
LOCAL_MODULE := libagm_pcm_plugin
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_CFLAGS += -Wall
LOCAL_SRC_FILES := src/agm_pcm_plugin.c
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libarosal_headers
LOCAL_SHARED_LIBRARIES := \
libsndcardparser \
libagmclient \
libutils \
libcutils \
liblog
#if android version is R, refer to qtitinyxx otherwise use upstream ones
#This assumes we would be using AR code only for Android R and subsequent versions.
ifneq ($(filter 11 R, $(PLATFORM_VERSION)),)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa
else
LOCAL_SHARED_LIBRARIES += libtinyalsa
endif
ifeq ($(strip $(AUDIO_FEATURE_ENABLED_DYNAMIC_LOG)), true)
LOCAL_CFLAGS += -DDYNAMIC_LOG_ENABLED
LOCAL_C_INCLUDES += $(TOP)/external/expat/lib/expat.h
LOCAL_SHARED_LIBRARIES += libaudio_log_utils
LOCAL_SHARED_LIBRARIES += libexpat
LOCAL_HEADER_LIBRARIES += libaudiologutils_headers
endif
include $(BUILD_SHARED_LIBRARY)
# Build libagm_mixer_plugin
include $(CLEAR_VARS)
LOCAL_MODULE := libagm_mixer_plugin
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_SRC_FILES := src/agm_mixer_plugin.c
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libarosal_headers
LOCAL_SHARED_LIBRARIES := \
libsndcardparser \
libagmclient \
libcutils \
libutils \
liblog
#if android version is R, refer to qtitinyxx otherwise use upstream ones
#This assumes we would be using AR code only for Android R and subsequent versions.
ifneq ($(filter 11 R, $(PLATFORM_VERSION)),)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa
else
LOCAL_SHARED_LIBRARIES += libtinyalsa
endif
ifeq ($(strip $(AUDIO_FEATURE_ENABLED_DYNAMIC_LOG)), true)
LOCAL_CFLAGS += -DDYNAMIC_LOG_ENABLED
LOCAL_C_INCLUDES += $(TOP)/external/expat/lib/expat.h
LOCAL_SHARED_LIBRARIES += libaudio_log_utils
LOCAL_SHARED_LIBRARIES += libexpat
LOCAL_HEADER_LIBRARIES += libaudiologutils_headers
endif
include $(BUILD_SHARED_LIBRARY)
# Build libagm_compress_plugin
include $(CLEAR_VARS)
LOCAL_MODULE := libagm_compress_plugin
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
LOCAL_SRC_FILES := src/agm_compress_plugin.c
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libarosal_headers
LOCAL_SHARED_LIBRARIES := \
libsndcardparser \
libagmclient \
libutils \
libcutils \
liblog
# Use flag based selection to use QTI vs open source tinycompress project
ifeq ($(TARGET_USES_QTI_TINYCOMPRESS),true)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa\
libqti-tinycompress
else
LOCAL_C_INCLUDES += $(TOP)/external/tinycompress/include
LOCAL_SHARED_LIBRARIES += libtinyalsa\
libtinycompress
endif
ifeq ($(strip $(AUDIO_FEATURE_ENABLED_DYNAMIC_LOG)), true)
LOCAL_CFLAGS += -DDYNAMIC_LOG_ENABLED
LOCAL_C_INCLUDES += $(TOP)/external/expat/lib/expat.h
LOCAL_SHARED_LIBRARIES += libaudio_log_utils
LOCAL_SHARED_LIBRARIES += libexpat
LOCAL_HEADER_LIBRARIES += libaudiologutils_headers
endif
include $(BUILD_SHARED_LIBRARY)

View File

@@ -0,0 +1,68 @@
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = agmplugin.pc
EXTRA_DIST = $(pkgconfig_DATA)
library_includedir = $(includedir)
AM_CFLAGS := -Wno-unused-parameter
if !BUILDSYSTEM_OPENWRT
AM_CFLAGS += -I $(top_srcdir)/include @AGM_CFLAGS@
AM_CFLAGS += @GLIB_CFLAGS@ -Dstrlcpy=g_strlcpy -Dstrlcat=g_strlcat -include glib.h
AM_CFLAGS += -D__unused=__attribute__\(\(__unused__\)\) -DLINUX_ENABLED
AM_CFLAGS += @SNDPARSER_CFLAGS@
AM_CFLAGS += -Wno-unused-parameter
AM_CFLAGS += -DDYNAMIC_LOG_ENABLED
endif
lib_LTLIBRARIES = libagm_pcm_plugin.la
libagm_pcm_plugin_la_SOURCES = src/agm_pcm_plugin.c
libagm_pcm_plugin_la_CFLAGS := $(AM_CFLAGS)
libagm_pcm_plugin_la_LDFLAGS = -ltinyalsa -lsndcardparser -avoid-version -shared
if BUILDSYSTEM_OPENWRT
libagm_pcm_plugin_la_LDFLAGS += -lagmclientwrapper
else
libagm_pcm_plugin_la_LDFLAGS += -lagmclientwrapper -laudio_log_utils
endif
lib_LTLIBRARIES += libagm_pcm_passthrough_plugin.la
libagm_pcm_passthrough_plugin_la_SOURCES = src/agm_pcm_plugin.c
libagm_pcm_passthrough_plugin_la_CFLAGS := $(AM_CFLAGS)
libagm_pcm_passthrough_plugin_la_LDFLAGS = -ltinyalsa -lsndcardparser -avoid-version -shared
if BUILDSYSTEM_OPENWRT
libagm_pcm_passthrough_plugin_la_LDFLAGS += -lagm
else
libagm_pcm_passthrough_plugin_la_LDFLAGS += -lagm -laudio_log_utils
endif
lib_LTLIBRARIES += libagm_compress_plugin.la
libagm_compress_plugin_la_SOURCES = src/agm_compress_plugin.c
libagm_compress_plugin_la_CFLAGS := $(AM_CFLAGS)
libagm_compress_plugin_la_LDFLAGS = -ltinyalsa -ltinycompress -lsndcardparser -lpthread -avoid-version -laudio_log_utils -shared -lagmclientwrapper
lib_LTLIBRARIES += libagm_compress_passthrough_plugin.la
libagm_compress_passthrough_plugin_la_SOURCES = src/agm_compress_plugin.c
libagm_compress_passthrough_plugin_la_CFLAGS := $(AM_CFLAGS)
libagm_compress_passthrough_plugin_la_LDFLAGS = -ltinyalsa -lsndcardparser -lpthread -avoid-version -laudio_log_utils -shared -lagm
lib_LTLIBRARIES += libagm_mixer_plugin.la
libagm_mixer_plugin_la_SOURCES = src/agm_mixer_plugin.c
libagm_mixer_plugin_la_CFLAGS := $(AM_CFLAGS)
libagm_mixer_plugin_la_LDFLAGS = -ltinyalsa -lsndcardparser -avoid-version -shared
if BUILDSYSTEM_OPENWRT
libagm_mixer_plugin_la_LDFLAGS += -lagmclientwrapper
else
libagm_mixer_plugin_la_LDFLAGS += -lagmclientwrapper -laudio_log_utils
endif
lib_LTLIBRARIES += libagm_mixer_passthrough_plugin.la
libagm_mixer_passthrough_plugin_la_SOURCES = src/agm_mixer_plugin.c
libagm_mixer_passthrough_plugin_la_CFLAGS := $(AM_CFLAGS)
libagm_mixer_passthrough_plugin_la_LDFLAGS = -ltinyalsa -lsndcardparser -avoid-version -shared
if BUILDSYSTEM_OPENWRT
libagm_mixer_passthrough_plugin_la_LDFLAGS += -lagm
else
libagm_mixer_passthrough_plugin_la_LDFLAGS += -lagm
libagm_mixer_passthrough_plugin_la_LDFLAGS += -laudio_log_utils
endif

View File

@@ -0,0 +1 @@
# agm_plugin

View File

@@ -0,0 +1,10 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: agmplugin
Description: agm plugin library
Version: @VERSION@
Libs: -L${libdir} -lagmplugin
Cflags: -I${includedir}

View File

@@ -0,0 +1,57 @@
# Requires autoconf tool later than 2.61
AC_PREREQ(2.69)
# Initialize the agm plugin package version 1.0.0
AC_INIT([agmplugin],1.0.0)
# Does not strictly follow GNU Coding standards
AM_INIT_AUTOMAKE([foreign subdir-objects])
# Disables auto rebuilding of configure, Makefile.ins
AM_MAINTAINER_MODE
# Verifies the --srcdir is correct by checking for the path
AC_CONFIG_SRCDIR([src/agm_mixer_plugin.c])
# defines some macros variable to be included by source
AC_CONFIG_MACRO_DIR([m4])
# Checks for programs.
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CXX
AC_PROG_LIBTOOL
AC_PROG_AWK
AC_PROG_CPP
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
PKG_PROG_PKG_CONFIG
AC_ARG_WITH([glib],
AC_HELP_STRING([--with-glib],
[enable glib, Build against glib. Use this when building for HLOS systems which use glib]))
if (test "x${with_glib}" = "xyes"); then
PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.16, dummy=yes,
AC_MSG_ERROR(GThread >= 2.16 is required))
PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, dummy=yes,
AC_MSG_ERROR(GLib >= 2.16 is required))
GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS"
GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS"
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
fi
AM_CONDITIONAL(USE_GLIB, test "x${with_glib}" = "xyes")
# Checks for libraries
PKG_CHECK_MODULES([AGM], [agm])
AC_SUBST([AGM_CFLAGS])
PKG_CHECK_MODULES([SNDPARSER], [sndparser])
AC_SUBST([SNDPARSER_CFLAGS])
AC_ARG_WITH([openwrt],
AS_HELP_STRING([use openwrt (default is no)]),
[with_openwrt=$withval],
[with_openwrt=no])
AM_CONDITIONAL([BUILDSYSTEM_OPENWRT], [test "x${with_openwrt}" = "xyes"])
AC_CONFIG_FILES([ Makefile agmplugin.pc ])
AC_OUTPUT

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,181 @@
/*
* Copyright (c) 2019, 2021 The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <agm/agm_api.h>
#include <agm/utils.h>
#define ARRAY_SIZE(a) \
(sizeof(a) / sizeof(a[0]))
#ifdef DYNAMIC_LOG_ENABLED
#include <log_xml_parser.h>
#define LOG_MASK AGM_MOD_FILE_AGM_DUMMY_IMPL
#include <log_utils.h>
#endif
int agm_session_write(struct session_obj *handle, void *buff, size_t count)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_session_read(struct session_obj *handle, void *buff, size_t count)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_session_stop(struct session_obj *handle)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_session_start(struct session_obj *handle)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_session_prepare(struct session_obj *handle)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_session_close(struct session_obj *handle)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_session_set_config(struct session_obj *handle,
struct agm_session_config *session_config,
struct agm_media_config *media_config,
struct agm_buffer_config *buffer_config)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_session_open(uint32_t session_id, enum agm_session_mode sess_mode,
struct session_obj **handle)
{
struct session_obj *h = malloc(sizeof(100));
AGM_LOGD("%s %d\n", __func__, __LINE__);
*handle = h;
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_audio_intf_set_media_config(uint32_t audio_intf,
struct agm_media_config *media_config)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_audio_intf_set_metadata(uint32_t audio_intf,
struct agm_meta_data *metadata)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_session_set_metadata(uint32_t session_id,
struct agm_meta_data *metadata)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_session_audio_inf_set_metadata(uint32_t session_id,
uint32_t audio_intf,
struct agm_meta_data *metadata)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}
int agm_session_audio_inf_connect(uint32_t session_id,
uint32_t audio_intf, bool state)
{
AGM_LOGD("%s: s_id %u, aif_id %u, %s\n", __func__,
session_id, audio_intf,
state ? "connect" : "disconnect");
return 0;
}
static struct aif_info be_list[] = {
{
.aif_name = "SLIM_0_RX",
.dir = RX,
},
{
.aif_name = "SLIM_1_RX",
.dir = RX,
},
{
.aif_name = "SLIM_0_TX",
.dir = TX,
},
{
.aif_name = "SLIM_1_TX",
.dir = TX,
},
};
int agm_get_aif_info_list(struct aif_info *aif_list, size_t *num_aif_info)
{
int i;
AGM_LOGD("%s %d\n", __func__, __LINE__);
if (*num_aif_info == 0) {
*num_aif_info = ARRAY_SIZE(be_list);
return 0;
}
for (i = 0; i < *num_aif_info; i++)
memcpy((aif_list + i), &be_list[i], sizeof(struct aif_info));
return 0;
}
int agm_session_set_loopback(uint32_t capture_session_id,
uint32_t playback_session_id)
{
AGM_LOGD("%s %d\n", __func__, __LINE__);
return 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,443 @@
/*
** Copyright (c) 2019, 2021, The Linux Foundation. All rights reserved.
**
** Copyright 2011, The Android Open Source Project
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of The Android Open Source Project nor the names of
** its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
** DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <tinyalsa/asoundlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include "agmmixer.h"
#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
#define ID_DATA 0x61746164
#define FORMAT_PCM 1
struct wav_header {
uint32_t riff_id;
uint32_t riff_sz;
uint32_t riff_fmt;
uint32_t fmt_id;
uint32_t fmt_sz;
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
uint32_t data_id;
uint32_t data_sz;
};
int capturing = 1;
static unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
unsigned int usb_device, unsigned int channels, unsigned int rate,
enum pcm_format format, unsigned int period_size,
unsigned int period_count, unsigned int cap_time,
struct device_config *dev_config, unsigned int stream_kv,
unsigned int device_kv, unsigned int instance_kv,
unsigned int devicepp_kv);
static void sigint_handler(int sig)
{
capturing = 0;
}
static void usage(void)
{
printf(" Usage: %s file.wav [-help print usage] [-D card] [-d device]\n"
" [-c channels] [-r rate] [-b bits] [-p period_size]\n"
" [-n n_periods] [-T capture time] [-i intf_name] [-dkv device_kv]\n"
" [-dppkv deviceppkv] : Assign 0 if no device pp in the graph\n"
" [-ikv instance_kv] : Assign 0 if no instance kv in the graph\n"
" [-skv stream_kv]\n"
" [-is_24_LE] : [0-1] Only to be used if user wants to record 32 bps clip\n"
" [-usb_d usb device]\n"
" 0: If bps is 32, and format should be S32_LE\n"
" 1: If bps is 24, and format should be S24_LE\n");
}
int main(int argc, char **argv)
{
FILE *file;
struct wav_header header;
unsigned int card = 100;
unsigned int device = 101;
unsigned int usb_device = 1;
unsigned int channels = 2;
unsigned int rate = 44100;
unsigned int bits = 16;
unsigned int frames;
unsigned int period_size = 1024;
unsigned int period_count = 4;
unsigned int cap_time = 0;
char *intf_name = NULL;
unsigned int device_kv = 0;
struct device_config config;
enum pcm_format format;
int ret = 0;
unsigned int devicepp_kv = 0;
unsigned int stream_kv = 0;
unsigned int instance_kv = INSTANCE_1;
bool is_24_LE = false;
if (argc < 2) {
usage();
return 1;
}
file = fopen(argv[1], "wb");
if (!file) {
printf("Unable to create file '%s'\n", argv[1]);
return 1;
}
/* parse command line arguments */
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
} else if (strcmp(*argv, "-c") == 0) {
argv++;
if (*argv)
channels = atoi(*argv);
} else if (strcmp(*argv, "-r") == 0) {
argv++;
if (*argv)
rate = atoi(*argv);
} else if (strcmp(*argv, "-b") == 0) {
argv++;
if (*argv)
bits = atoi(*argv);
} else if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
} else if (strcmp(*argv, "-p") == 0) {
argv++;
if (*argv)
period_size = atoi(*argv);
} else if (strcmp(*argv, "-n") == 0) {
argv++;
if (*argv)
period_count = atoi(*argv);
} else if (strcmp(*argv, "-T") == 0) {
argv++;
if (*argv)
cap_time = atoi(*argv);
} else if (strcmp(*argv, "-i") == 0) {
argv++;
if (*argv)
intf_name = *argv;
} else if (strcmp(*argv, "-dkv") == 0) {
argv++;
if (*argv)
device_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-skv") == 0) {
argv++;
if (*argv)
stream_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-ikv") == 0) {
argv++;
if (*argv)
instance_kv = atoi(*argv);
} else if (strcmp(*argv, "-dppkv") == 0) {
argv++;
if (*argv)
devicepp_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-is_24_LE") == 0) {
argv++;
if (*argv)
is_24_LE = atoi(*argv);
} else if (strcmp(*argv, "-usb_d") == 0) {
argv++;
if (*argv)
usb_device = atoi(*argv);
}else if (strcmp(*argv, "-help") == 0) {
usage();
}
if (*argv)
argv++;
}
header.riff_id = ID_RIFF;
header.riff_sz = 0;
header.riff_fmt = ID_WAVE;
header.fmt_id = ID_FMT;
header.fmt_sz = 16;
header.audio_format = FORMAT_PCM;
header.num_channels = channels;
header.sample_rate = rate;
switch (bits) {
case 32:
if (is_24_LE)
format = PCM_FORMAT_S24_LE;
else
format = PCM_FORMAT_S32_LE;
break;
case 24:
format = PCM_FORMAT_S24_3LE;
break;
case 16:
format = PCM_FORMAT_S16_LE;
break;
default:
printf("%u bits is not supported.\n", bits);
fclose(file);
return 1;
}
if (intf_name == NULL)
return 1;
ret = get_device_media_config(BACKEND_CONF_FILE, intf_name, &config);
if (ret) {
printf("Invalid input, entry not found for %s\n", intf_name);
fclose(file);
return ret;
}
if (config.format != PCM_FORMAT_INVALID) {
printf("Valid format from backend_conf %d\n", config.format);
config.bits = get_pcm_bit_width(config.format);
}
header.bits_per_sample = pcm_format_to_bits(format);
header.byte_rate = (header.bits_per_sample / 8) * channels * rate;
header.block_align = channels * (header.bits_per_sample / 8);
header.data_id = ID_DATA;
/* leave enough room for header */
fseek(file, sizeof(struct wav_header), SEEK_SET);
/* install signal handler and begin capturing */
signal(SIGINT, sigint_handler);
signal(SIGHUP, sigint_handler);
signal(SIGTERM, sigint_handler);
frames = capture_sample(file, card, device, usb_device, header.num_channels,
header.sample_rate, format,
period_size, period_count, cap_time, &config,
stream_kv, device_kv, instance_kv, devicepp_kv);
printf("Captured %u frames\n", frames);
/* write header now all information is known */
header.data_sz = frames * header.block_align;
header.riff_sz = header.data_sz + sizeof(header) - 8;
fseek(file, 0, SEEK_SET);
fwrite(&header, sizeof(struct wav_header), 1, file);
fclose(file);
return 0;
}
unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
unsigned int usb_device, unsigned int channels, unsigned int rate,
enum pcm_format format, unsigned int period_size,
unsigned int period_count, unsigned int cap_time,
struct device_config *dev_config, unsigned int stream_kv,
unsigned int device_kv, unsigned int instance_kv, unsigned int devicepp_kv)
{
struct pcm_config config;
struct pcm *pcm;
struct mixer *mixer;
char *buffer;
char *intf_name = dev_config->name;
unsigned int size;
unsigned int bytes_read = 0;
unsigned int frames = 0;
struct timespec end;
struct timespec now;
uint32_t miid = 0;
int ret = 0;
struct usbAudioConfig cfg;
uint8_t* payload = NULL;
size_t payloadSize = 0;
stream_kv = stream_kv ? stream_kv : PCM_RECORD;
memset(&config, 0, sizeof(config));
config.channels = channels;
config.rate = rate;
config.period_size = period_size;
config.period_count = period_count;
config.format = format;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;
if (NULL == intf_name) {
printf("No interface name mentioned, Exiting !!!\n");
return 0;
}
mixer = mixer_open(card);
if (!mixer) {
printf("Failed to open mixer\n");
return 0;
}
if(strcmp(intf_name, "USB_AUDIO-TX") == 0) {
dev_config->rate = rate;
dev_config->ch = channels;
}
/* set device/audio_intf media config mixer control */
if (set_agm_device_media_config(mixer, intf_name, dev_config)) {
printf("Failed to set device media config\n");
goto err_close_mixer;
}
/* set audio interface metadata mixer control */
if (set_agm_audio_intf_metadata(mixer, intf_name, device_kv, CAPTURE,
dev_config->rate, dev_config->bits, stream_kv)) {
printf("Failed to set device metadata\n");
goto err_close_mixer;
}
/* set stream metadata mixer control */
if (set_agm_capture_stream_metadata(mixer, device, stream_kv, CAPTURE, STREAM_PCM,
instance_kv)) {
printf("Failed to set pcm metadata\n");
goto err_close_mixer;
}
if (devicepp_kv != 0) {
if (set_agm_streamdevice_metadata(mixer, device, stream_kv, CAPTURE, STREAM_PCM,
intf_name, devicepp_kv)) {
printf("Failed to set pcm metadata\n");
goto err_close_mixer;
}
}
ret = agm_mixer_get_miid (mixer, device, intf_name, STREAM_PCM, TAG_STREAM_MFC, &miid);
if (ret) {
printf("MFC not present for this graph\n");
} else {
if (configure_mfc(mixer, device, intf_name, TAG_STREAM_MFC,
STREAM_PCM, rate, channels, get_pcm_bit_width(format), miid)) {
printf("Failed to configure stream mfc\n");
goto err_close_mixer;
}
}
if (strcmp(intf_name, "USB_AUDIO-TX") == 0) {
ret = agm_mixer_get_miid (mixer, device, intf_name, STREAM_PCM, DEVICE_HW_ENDPOINT_TX, &miid);
if (ret == 0) {
cfg.usb_token = (usb_device << 16)|0x1;
cfg.svc_interval = 0;
get_agm_usb_audio_config_payload(&payload, &payloadSize, miid, &cfg);
if (payloadSize) {
ret = set_agm_device_custom_payload(mixer, intf_name, payload, payloadSize);
} else {
ret = -1;
printf("set_agm_device_custom_payload failed\n");
goto err_close_mixer;
}
} else {
printf("Failed to get miid for USB_AUDIO-TX\n");
goto err_close_mixer;
}
}
/* connect pcm stream to audio intf */
if (connect_agm_audio_intf_to_stream(mixer, device, intf_name, STREAM_PCM, true)) {
printf("Failed to connect pcm to audio interface\n");
goto err_close_mixer;
}
pcm = pcm_open(card, device, PCM_IN, &config);
if (!pcm || !pcm_is_ready(pcm)) {
printf("Unable to open PCM device (%s)\n",
pcm_get_error(pcm));
goto err_close_mixer;
}
size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
buffer = (char *)malloc(size);
if (!buffer) {
printf("Unable to allocate %u bytes\n", size);
goto err_close_pcm;
}
printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate,
pcm_format_to_bits(format));
if (pcm_start(pcm) < 0) {
printf("start error\n");
goto err_close_pcm;
}
clock_gettime(CLOCK_MONOTONIC, &now);
end.tv_sec = now.tv_sec + cap_time;
end.tv_nsec = now.tv_nsec;
while (capturing && !pcm_read(pcm, buffer, size)) {
if (fwrite(buffer, 1, size, file) != size) {
printf("Error capturing sample\n");
break;
}
bytes_read += size;
if (cap_time) {
clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec > end.tv_sec ||
(now.tv_sec == end.tv_sec && now.tv_nsec >= end.tv_nsec))
break;
}
}
frames = pcm_bytes_to_frames(pcm, bytes_read);
free(buffer);
pcm_stop(pcm);
err_close_pcm:
connect_agm_audio_intf_to_stream(mixer, device, intf_name, STREAM_PCM, false);
pcm_close(pcm);
err_close_mixer:
if (payload) {
free(payload);
}
mixer_close(mixer);
return frames;
}

View File

@@ -0,0 +1,234 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __AGMMIXERWRAPPER_H__
#define __AGMMIXERWRAPPER_H__
#include <iostream>
#include "agmmixer.h"
class AgmMixerWrapper {
protected:
struct mixer *mixer;
struct device_config deviceConfig;
struct group_config groupConfig;
struct usbAudioConfig usbAudioConfig;
public:
virtual ~AgmMixerWrapper() = default;
virtual int mixerOpen(unsigned int card) = 0;
virtual int mixerClose(void) = 0;
virtual struct device_config getDeviceMediaConfig(char* filename, char *intf_name) = 0;
virtual int setDeviceMediaConfig(char *intf_name, struct device_config *config) = 0;
virtual int setAudioInterfaceMetadata(char *intf_name, unsigned int dkv,
enum usecase_type usecase, int rate, int bitwidth, uint32_t stream_kv) = 0;
virtual int setStreamMetadata(int device, uint32_t stream_kv, unsigned int instance_kv) = 0;
virtual int setStreamDeviceMetadata(int device, uint32_t stream_kv, char *intf_name,
unsigned int devicepp_kv) = 0;
virtual int connectAudioInterfaceToStream(unsigned int device, char *intf_name) = 0;
virtual int configureMFC(int device, char *intf_name, struct device_config) = 0;
virtual struct group_config getGroupConfig(char *intf_name) = 0;
virtual int setGroupConfig(unsigned int device, char *intf_name, unsigned int device_kv, struct group_config config, unsigned int channels) = 0;
virtual int setDeviceCustomPayload(char *intf_name, int device, unsigned int usb_device) = 0;
virtual int disconnectAudioInterfaceToStream(unsigned int device, char *intf_name) = 0;
};
class AgmMixerWrapperImpl: public AgmMixerWrapper {
public:
int mixerOpen(unsigned int card) override {
int ret = 0;
mixer = mixer_open(card);
if (!mixer) {
std::cout << "Failed to open mixer" << std::endl;
ret = -1;
}
return ret;
}
int mixerClose(void) override {
mixer_close(mixer);
return 0;
}
struct device_config getDeviceMediaConfig(char* filename, char *intf_name) override {
if (get_device_media_config(BACKEND_CONF_FILE, intf_name, &deviceConfig)) {
std::cout << "Invalid input, entry not found for :" << intf_name << std::endl;
}
if (deviceConfig.format != PCM_FORMAT_INVALID) {
deviceConfig.bits = get_pcm_bit_width(deviceConfig.format);
}
return deviceConfig;
}
int setDeviceMediaConfig(char *intf_name, struct device_config *config) override {
int ret = 0;
ret = set_agm_device_media_config(mixer, intf_name, config);
if (ret) {
std::cout << "Failed to set agm device media config " << ret << std::endl;
}
return ret;
}
int setAudioInterfaceMetadata(char *intf_name, unsigned int dkv,
enum usecase_type usecase, int rate, int bitwidth, uint32_t stream_kv) override {
int ret = 0;
ret = set_agm_audio_intf_metadata(mixer, intf_name, dkv, usecase,
rate, bitwidth, stream_kv);
if (ret) {
std::cout << "Failed to set device metadata " << ret << std::endl;
}
return ret;
}
int setStreamMetadata(int device, uint32_t stream_kv, unsigned int instance_kv) override {
return set_agm_stream_metadata(mixer, device, stream_kv, PLAYBACK, STREAM_PCM, instance_kv);
}
int setStreamDeviceMetadata(int device, uint32_t stream_kv, char *intf_name,
unsigned int devicepp_kv) override {
int ret = 0;
if (devicepp_kv == 0) {
std::cout << "There is no devicepp keyvector" << std::endl;
return -1;
}
ret = set_agm_streamdevice_metadata(mixer, device, stream_kv, PLAYBACK, STREAM_PCM, intf_name,
devicepp_kv);
if (ret) {
std::cout << "Failed to set streamdevice metadata " << ret << std::endl;
}
return ret;
}
int setDeviceCustomPayload(char *intf_name, int device, unsigned int usb_device) override {
int ret = 0;
unsigned int miid = 0;
struct usbAudioConfig cfg;
uint8_t* payload;
size_t payloadSize;
ret = agm_mixer_get_miid (mixer, device, intf_name, STREAM_PCM, DEVICE_HW_ENDPOINT_RX, &miid);
if (ret) {
std::cout << "Failed to get miid for USB_AUDIO-TX " << ret << std::endl;
return ret;
}
cfg.usb_token = usb_device << 16;
cfg.svc_interval = 0;
get_agm_usb_audio_config_payload(&payload, &payloadSize, miid, &cfg);
if (payloadSize) {
ret = set_agm_device_custom_payload(mixer, intf_name, payload, payloadSize);
} else {
ret = -1;
std::cout << "set_agm_device_custom_payload failed" << std::endl;
}
return ret;
}
int connectAudioInterfaceToStream(unsigned int device, char *intf_name) override {
int ret = 0;
ret = connect_agm_audio_intf_to_stream(mixer, device, intf_name, STREAM_PCM, true);
if (ret) {
std::cout << "Failed to connect pcm to audio interface " << ret << std::endl;
}
return ret;
}
virtual int configureMFC(int device, char *intf_name, struct device_config config) override {
int ret = 0;
unsigned int miid = 0;
ret = agm_mixer_get_miid(mixer, device, intf_name, STREAM_PCM, PER_STREAM_PER_DEVICE_MFC, &miid);
if (ret) {
std::cout << "MFC not present for this graph " << ret << std::endl;
return ret;
}
ret = configure_mfc(mixer, device, intf_name, PER_STREAM_PER_DEVICE_MFC,
STREAM_PCM, config.rate, config.ch,
config.bits, miid);
if (ret) {
std::cout << "Failed to configure pspd mfc " << ret << std::endl;
return ret;
}
return ret;
}
struct group_config getGroupConfig(char *intf_name) override {
if (isVirtualInterface(intf_name)) {
if (get_group_device_info(BACKEND_CONF_FILE, intf_name, &groupConfig)) {
std::cout << "Failed to get grp device config" << std::endl;
}
}
return groupConfig;
}
int setGroupConfig(unsigned int device, char *intf_name, unsigned int device_kv, struct group_config config, unsigned int channels) override {
int ret = 0;
if (isVirtualInterface(intf_name)) {
ret = set_agm_group_device_config(mixer, intf_name, &config);
if (ret) {
std::cout << "Failed to set grp device config " << ret << std::endl;
return ret;
}
if ((device_kv == SPEAKER) || (device_kv == HANDSET)) {
ret = set_agm_group_mux_config(mixer, device, &config, intf_name, channels);
if (ret) {
std::cout << "Failed to set grp device config " << ret << std::endl;
return ret;
}
}
}
return ret;
}
int disconnectAudioInterfaceToStream(unsigned int device, char *intf_name) override {
int ret = 0;
ret = connect_agm_audio_intf_to_stream(mixer, device, intf_name, STREAM_PCM, false);
if (ret) {
std::cout << "Failed to disconnect pcm to audio interface " << ret << std::endl;
}
return ret;
}
private:
char* isVirtualInterface(char *intfName) {
return strstr(intfName, "VIRT-");
}
};
#endif

View File

@@ -0,0 +1,98 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __AGMPCMWRAPPER_H__
#define __AGMPCMWRAPPER_H__
#include <iostream>
#include "agmmixer.h"
class AgmPcmWrapper {
protected:
struct pcm *pcm;
public:
virtual ~AgmPcmWrapper() = default;
virtual int pcmOpen(unsigned int card, unsigned int device, struct pcm_config *config) = 0;
virtual int pcmFramesToBytes(void) = 0;
virtual int pcmStart(void) = 0;
virtual int pcmWrite(char *buffer, int num_read) = 0;
virtual int pcmStop(void) = 0;
virtual int pcmClose(void) = 0;
};
class AgmPcmWrapperImpl: public AgmPcmWrapper {
public:
int pcmOpen(unsigned int card, unsigned int device, struct pcm_config *config) override {
int ret = 0;
pcm = pcm_open(card, device, PCM_OUT, config);
if (!pcm || !pcm_is_ready(pcm)) {
std::cout << "Unable to open PCM device " << device << " (" << pcm_get_error(pcm) << ")" << std::endl;
ret = -1;
}
return ret;
}
int pcmFramesToBytes(void) override {
return pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
}
int pcmStart(void) override {
int ret = 0;
ret = pcm_start(pcm);
if (ret < 0) {
std::cout << "start error" << std::endl;
pcm_close(pcm);
}
return ret;
}
int pcmWrite(char *buffer, int num_read) override {
int ret = 0;
ret = pcm_write(pcm, buffer, num_read);
if (ret < 0) {
std::cout << "Error playing sample" << std::endl;
}
return ret;
}
int pcmStop(void) override {
return pcm_stop(pcm);
}
int pcmClose(void) override {
return pcm_close(pcm);
}
};
#endif

View File

@@ -0,0 +1,76 @@
/*
** Copyright (c) 2019, 2021, The Linux Foundation. All rights reserved.
**
** Copyright 2011, The Android Open Source Project
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of The Android Open Source Project nor the names of
** its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
** DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "AgmPlayer.h"
#include "PlaybackCommandParser.h"
int main(int argc, char **argv)
{
std::ifstream file;
HeaderParser* riffWaveParser = new RiffWaveParser();
HeaderParser* chunkParser = new ChunkParser();
PlaybackCommandParser playbackCommandParser;
if (argc < 3) {
playbackCommandParser.usage();
exit(1);
}
file.open(argv[1], std::ios::binary);
if (!file) {
std::cout << "Unable to open file" << std::endl;
exit(1);
}
riffWaveParser->parseHeader(file);
if (!riffWaveParser->isValid()) {
std::cout << "It is not a riff/wave file" << std::endl;
file.close();
exit(1);
}
chunkParser->parseHeader(file);
playbackCommandParser.parseCommandLine(argv);
if (playbackCommandParser.getPlaybackCommand().getInterfaceName() == nullptr) {
std::cout << "interface name is NULL" << std::endl;
file.close();
exit(1);
}
AgmPlayer agmPlayer;
agmPlayer.playSample(file, chunkParser->getFormat(), playbackCommandParser.getPlaybackCommand());
file.close();
return 0;
}

View File

@@ -0,0 +1,314 @@
/*
** Copyright (c) 2019, 2021, The Linux Foundation. All rights reserved.
**
** Copyright 2011, The Android Open Source Project
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of The Android Open Source Project nor the names of
** its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
** DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "AgmPlayer.h"
#include "SignalHandler.h"
int AgmPlayer::playSample(std::ifstream& file, ChunkFormat format, PlaybackCommand playbackCommand)
{
int ret = 0;
getPlaybackInfo(&playbackCommand);
setPlaybackInfo(format);
ret = openMixer(card);
if (ret < 0) {
return ret;
}
allocConfigMemory();
ret = setDeviceConfig();
if (ret < 0) {
goto err;
}
ret = setStreamConfig();
if (ret < 0) {
goto err;
}
ret = setDevicePostProcessingConfig();
if (ret < 0) {
goto err;
}
ret = startPlayback(file);
if (ret < 0) {
goto err;
}
stopPlayback();
err:
deallocConfigMemory();
closeMixer();
return ret;
}
void AgmPlayer::getPlaybackInfo(PlaybackCommand* playbackCommand)
{
card = playbackCommand->getCard();
device = playbackCommand->getDevice();
device_kv = playbackCommand->getDeviceKeyVector();
stream_kv = playbackCommand->getStreamKeyVector();
instance_kv = playbackCommand->getInstanceKeyVector();
devicepp_kv = playbackCommand->getDeviceppKeyVector();
haptics = playbackCommand->getHaptics();
intf_name = playbackCommand->getInterfaceName();
intf_num = playbackCommand->getInterfaceNumber();
is_24_LE = playbackCommand->is24LE();
usb_device = playbackCommand->getUsbDevice();
channels = playbackCommand->getChannel();
rate = playbackCommand->getSampleRate();
bits = playbackCommand->getBitWidth();
}
void AgmPlayer::setPlaybackInfo(ChunkFormat format)
{
config.channels = format.num_channels;
config.rate = format.sample_rate;
switch (format.bits_per_sample) {
case 32:
config.format = is_24_LE? PCM_FORMAT_S24_LE : PCM_FORMAT_S32_LE;
break;
case 24:
config.format = PCM_FORMAT_S24_3LE;
break;
case 16:
config.format = PCM_FORMAT_S16_LE;
break;
default:
std::cout << "Unsupported bit width" << std::endl;
break;
}
if (haptics) {
playback_path = HAPTICS;
stream_kv = stream_kv ? stream_kv : HAPTICS_PLAYBACK;
} else {
playback_path = PLAYBACK;
stream_kv = stream_kv ? stream_kv : PCM_LL_PLAYBACK;
}
}
int AgmPlayer::openMixer(unsigned int card)
{
return agmMixer->mixerOpen(card);
}
int AgmPlayer::closeMixer(void)
{
return agmMixer->mixerClose();
}
void AgmPlayer::allocConfigMemory(void)
{
dev_config = new device_config[intf_num];
if (!dev_config) {
std::cout << "Failed to allocate memory for dev config" << std::endl;
return;
}
grp_config = new group_config[intf_num];
if (!grp_config) {
std::cout << "Failed to allocate memory for group config" << std::endl;
return;
}
}
void AgmPlayer::deallocConfigMemory(void)
{
if (dev_config) {
delete[] dev_config;
dev_config = nullptr;
}
if (grp_config) {
delete[] grp_config;
grp_config = nullptr;
}
}
bool AgmPlayer::isUSBInterface(int index)
{
if(intf_name[index] != NULL && strcmp(intf_name[index], "USB_AUDIO-RX") == 0) {
return true;
}
return false;
}
void AgmPlayer::getDeviceMediaConfig(int index)
{
if(isUSBInterface(index)) {
dev_config[index].rate = rate;
dev_config[index].ch = channels;
dev_config[index].bits = bits;
}
else {
dev_config[index] = agmMixer->getDeviceMediaConfig(BACKEND_CONF_FILE, intf_name[index]);
}
}
int AgmPlayer::setDeviceConfig(void)
{
int ret = 0;
for (int index = 0; index < intf_num; index++) {
getDeviceMediaConfig(index);
ret = agmMixer->setDeviceMediaConfig(intf_name[index], &dev_config[index]);
if (ret)
return ret;
ret = agmMixer->setAudioInterfaceMetadata(intf_name[index], device_kv[index], playback_path,
dev_config[index].rate, dev_config[index].bits, stream_kv);
if (ret)
return ret;
}
return ret;
}
int AgmPlayer::setStreamConfig(void)
{
return agmMixer->setStreamMetadata(device, stream_kv, instance_kv);
}
int AgmPlayer::setDevicePostProcessingConfig(void)
{
int ret = 0;
for (int index = 0; index < intf_num; index++) {
ret = agmMixer->setStreamDeviceMetadata(device, stream_kv, intf_name[index], devicepp_kv[index]);
if (ret)
return ret;
if (isUSBInterface(index)) {
ret = agmMixer->setDeviceCustomPayload(intf_name[index], device, usb_device);
if (ret)
return ret;
}
ret = agmMixer->connectAudioInterfaceToStream(device, intf_name[index]);
if (ret)
return ret;
ret = agmMixer->configureMFC(device, intf_name[index], dev_config[index]);
if (ret)
return ret;
grp_config[index] = agmMixer->getGroupConfig(intf_name[index]);
ret = agmMixer->setGroupConfig(device, intf_name[index], device_kv[index], grp_config[index], dev_config[index].ch);
if (ret)
return ret;
}
return ret;
}
int AgmPlayer::startPlayback(std::ifstream& file)
{
int size = 0;
int num_read = 0;
int ret = 0;
char *buffer;
SignalHandler stream;
ret = agmPcm->pcmOpen(card, device, &config);
if (ret < 0)
return ret;
size = agmPcm->pcmFramesToBytes();
buffer = new char[size];
if (!buffer) {
std::cout << "Unable to allocate " << size << " bytes" << std::endl;
return -ENOMEM;
}
ret = agmPcm->pcmStart();
if (ret < 0)
goto err;
stream.open();
do {
file.read(buffer, size);
num_read = file.gcount();
if (num_read > 0) {
ret = agmPcm->pcmWrite(buffer, num_read);
if (ret < 0)
break;
}
} while (!stream.isClosed() && num_read > 0);
err:
if (buffer) {
delete[] buffer;
buffer = nullptr;
}
return ret;
}
void AgmPlayer::stopPlayback(void)
{
agmPcm->pcmStop();
for (int index = 0; index < intf_num; index++) {
agmMixer->disconnectAudioInterfaceToStream(device, intf_name[index]);
}
agmPcm->pcmClose();
}
AgmPlayer::AgmPlayer()
{
agmMixer = new AgmMixerWrapperImpl();
agmPcm = new AgmPcmWrapperImpl();
memset(&config, 0, sizeof(config));
config.period_size = 1024;
config.period_count = 4;
config.format = PCM_FORMAT_S16_LE;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;
};
AgmPlayer::AgmPlayer(AgmMixerWrapper *agmMixer, AgmPcmWrapper *agmPcm)
: agmMixer(agmMixer), agmPcm(agmPcm)
{
memset(&config, 0, sizeof(config));
config.period_size = 1024;
config.period_count = 4;
config.format = PCM_FORMAT_S16_LE;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;
}

View File

@@ -0,0 +1,89 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __AGMPLAY_H__
#define __AGMPLAY_H__
#include <iostream>
#include "ChunkParser.h"
#include "RiffWaveParser.h"
#include "PlaybackCommand.h"
#include "AgmMixerWrapper.h"
#include "AgmPcmWrapper.h"
class AgmPlayer {
public:
AgmPlayer();
AgmPlayer(AgmMixerWrapper *agmMixer, AgmPcmWrapper *agmPcm);
~AgmPlayer() {};
int playSample(std::ifstream& file, ChunkFormat fmt, PlaybackCommand playbackCommand);
private:
unsigned int card;
unsigned int device;
unsigned int *device_kv;
unsigned int stream_kv;
unsigned int instance_kv;
unsigned int *devicepp_kv;
unsigned int usb_device;
unsigned int channels;
unsigned int rate;
unsigned int bits;
bool haptics;
char **intf_name;
int intf_num;
bool is_24_LE;
struct pcm_config config;
struct group_config *grp_config;
struct device_config *dev_config;
enum usecase_type playback_path;
AgmMixerWrapper *agmMixer;
AgmPcmWrapper *agmPcm;
int openMixer(unsigned int card);
int closeMixer(void);
void getPlaybackInfo(PlaybackCommand* playbackCommand);
void setPlaybackInfo(ChunkFormat format);
void allocConfigMemory(void);
void deallocConfigMemory(void);
bool isUSBInterface(int index);
void getDeviceMediaConfig(int index);
int setDeviceConfig(void);
int setStreamConfig(void);
int setDevicePostProcessingConfig(void);
int startPlayback(std::ifstream& file);
void stopPlayback(void);
};
#endif

View File

@@ -0,0 +1,217 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := libagmmixer
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-result
LOCAL_SRC_FILES := agmmixer.c
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libacdb_headers
#if android version is R, refer to qtitinyxx otherwise use upstream ones
#This assumes we would be using AR code only for Android R and subsequent versions.
ifneq ($(filter 11 R, $(PLATFORM_VERSION)),)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa
else
LOCAL_SHARED_LIBRARIES += libtinyalsa
endif
LOCAL_SHARED_LIBRARIES += \
libexpat
include $(BUILD_SHARED_LIBRARY)
# Build agmplay
include $(CLEAR_VARS)
LOCAL_MODULE := agmplay
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-result
LOCAL_CFLAGS += -DBACKEND_CONF_FILE=\"/vendor/etc/backend_conf.xml\"
LOCAL_CFLAGS += -fexceptions
LOCAL_SRC_FILES := AgmPlay.cpp \
AgmPlayer.cpp \
RiffWaveParser.cpp \
ChunkParser.cpp \
PlaybackCommand.cpp \
PlaybackCommandParser.cpp
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libacdb_headers
#if android version is R, refer to qtitinyxx otherwise use upstream ones
#This assumes we would be using AR code only for Android R and subsequent versions.
ifneq ($(filter 11 R, $(PLATFORM_VERSION)),)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa
else
LOCAL_SHARED_LIBRARIES += libtinyalsa
endif
LOCAL_SHARED_LIBRARIES += \
libagmmixer
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := agmcap
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-result
LOCAL_CFLAGS += -DBACKEND_CONF_FILE=\"/vendor/etc/backend_conf.xml\"
LOCAL_SRC_FILES := AgmCap.cpp
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libacdb_headers
#if android version is R, refer to qtitinyxx otherwise use upstream ones
#This assumes we would be using AR code only for Android R and subsequent versions.
ifneq ($(filter 11 R, $(PLATFORM_VERSION)),)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa
else
LOCAL_SHARED_LIBRARIES += libtinyalsa
endif
LOCAL_SHARED_LIBRARIES += \
libagmmixer
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := agmhostless
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-result
LOCAL_CFLAGS += -DBACKEND_CONF_FILE=\"/vendor/etc/backend_conf.xml\"
LOCAL_SRC_FILES := agmhostless.c
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libacdb_headers
#if android version is R, refer to qtitinyxx otherwise use upstream ones
#This assumes we would be using AR code only for Android R and subsequent versions.
ifneq ($(filter 11 R, $(PLATFORM_VERSION)),)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa
else
LOCAL_SHARED_LIBRARIES += libtinyalsa
endif
LOCAL_SHARED_LIBRARIES += \
libagmmixer
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := agmcompressplay
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-result
LOCAL_CFLAGS += -DBACKEND_CONF_FILE=\"/vendor/etc/backend_conf.xml\"
LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
LOCAL_SRC_FILES := agmcompressplay.c
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libacdb_headers
# Use flag based selection to use QTI vs open source tinycompress project
ifeq ($(TARGET_USES_QTI_TINYCOMPRESS),true)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa\
libqti-tinycompress
else
LOCAL_C_INCLUDES += $(TOP)/external/tinycompress/include
LOCAL_SHARED_LIBRARIES += libtinyalsa\
libtinycompress
endif
LOCAL_SHARED_LIBRARIES += \
libagmmixer
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := agmcompresscap
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-result
LOCAL_CFLAGS += -DBACKEND_CONF_FILE=\"/vendor/etc/backend_conf.xml\"
LOCAL_C_INCLUDES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include
LOCAL_ADDITIONAL_DEPENDENCIES += $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr
LOCAL_SRC_FILES := agmcompresscap.c
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libacdb_headers
# Use flag based selection to use QTI vs open source tinycompress project
ifeq ($(TARGET_USES_QTI_TINYCOMPRESS),true)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa\
libqti-tinycompress
else
LOCAL_C_INCLUDES += $(TOP)/external/tinycompress/include
LOCAL_SHARED_LIBRARIES += libtinyalsa\
libtinycompress
endif
LOCAL_SHARED_LIBRARIES += \
libagmmixer
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := agmvoiceui
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-result
LOCAL_CFLAGS += -DBACKEND_CONF_FILE=\"/vendor/etc/backend_conf.xml\"
LOCAL_SRC_FILES := agm_voiceui.c
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libacdb_headers
#if android version is R, refer to qtitinyxx otherwise use upstream ones
#This assumes we would be using AR code only for Android R and subsequent versions.
ifneq ($(filter 11 R, $(PLATFORM_VERSION)),)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa
else
LOCAL_SHARED_LIBRARIES += libtinyalsa
endif
LOCAL_SHARED_LIBRARIES += \
libagmmixer
include $(BUILD_EXECUTABLE)

View File

@@ -0,0 +1,72 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "ChunkParser.h"
void ChunkParser::readHeader(std::ifstream& file)
{
int more_chunks = 1;
do {
file.read((char*)&chunkId, sizeof(chunkId));
file.read((char*)&chunkSize, sizeof(chunkSize));
switch (chunkId) {
case ID_FMT:
parseChunkFormat(file);
break;
case ID_DATA:
more_chunks = 0;
break;
default:
file.seekg(getChunkSize(), std::ios::cur);
}
} while (more_chunks);
}
ChunkFormat ChunkParser::getChuckAudioFormat(void)
{
return chunkFormat;
}
void ChunkParser::parseChunkFormat(std::ifstream& file)
{
file.read((char*)&chunkFormat, sizeof(chunkFormat));
if (getChunkSize() > sizeof(chunkFormat))
file.seekg(getChunkSize() - sizeof(chunkFormat), std::ios::cur);
}
uint32_t ChunkParser::getChunkSize(void)
{
return chunkSize;
}

View File

@@ -0,0 +1,58 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __CHUNKPARSER_H__
#define __CHUNKPARSER_H__
#include "HeaderParser.h"
const uint32_t ID_FMT = 0x20746d66;
const uint32_t ID_DATA = 0x61746164;
class ChunkParser : public HeaderParser {
private:
uint32_t chunkId;
uint32_t chunkSize;
uint32_t getChunkSize(void);
ChunkFormat chunkFormat;
void parseChunkFormat(std::ifstream& file);
public:
~ChunkParser() {}
protected:
void readHeader(std::ifstream& file) override;
ChunkFormat getChuckAudioFormat(void) override;
};
#endif

View File

@@ -0,0 +1,74 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __HEADERPARSER_H__
#define __HEADERPARSER_H__
#include <fstream>
class ChunkFormat {
public:
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
};
class HeaderParser
{
public:
void parseHeader(std::ifstream& file) {
readHeader(file);
}
bool isValid(void) {
return isValidFile();
}
ChunkFormat getFormat(void) {
return getChuckAudioFormat();
}
virtual ~HeaderParser() {}
protected:
ChunkFormat format;
virtual void readHeader(std::ifstream& file) = 0;
virtual bool isValidFile(void) { return true; };
virtual ChunkFormat getChuckAudioFormat(void) { return format; };
};
#endif

View File

@@ -0,0 +1,60 @@
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = agmtest.pc
EXTRA_DIST = $(pkgconfig_DATA)
AM_CFLAGS := -Wno-unused-parameter -Wno-unused-result
if !BUILDSYSTEM_OPENWRT
AM_CFLAGS += -I $(PKG_CONFIG_SYSROOT_DIR)/usr/include/
endif
AM_CFLAGS += @GLIB_CFLAGS@ -Dstrlcpy=g_strlcpy -Dstrlcat=g_strlcat -include glib.h
AM_CFLAGS += @MMHEADERS_CFLAGS@
AM_CFLAGS += -DBACKEND_CONF_FILE=\"/etc/backend_conf.xml\"
lib_LTLIBRARIES = libagmmixer.la
libagmmixer_la_SOURCES = agmmixer.c
libagmmixer_la_CFLAGS := $(AM_CFLAGS)
libagmmixer_la_LDFLAGS = -lcutils -ldl -lexpat -ltinyalsa -avoid-version
bin_PROGRAMS := agmplay
agmplay_SOURCES := agmplay.c
agmplay_la_CFLAGS := $(AM_CFLAGS)
agmplay_LDADD := -ltinyalsa libagmmixer.la
bin_PROGRAMS += agmcap
agmcap_SOURCES := agmcap.c
agmcap_la_CFLAGS := $(AM_CFLAGS)
agmcap_LDADD := -ltinyalsa libagmmixer.la
bin_PROGRAMS += agmhostless
agmhostless_SOURCES := agmhostless.c
agmhostless_la_CFLAGS := $(AM_CFLAGS)
agmhostless_LDADD := -ltinyalsa libagmmixer.la
if !BUILDSYSTEM_OPENWRT
bin_PROGRAMS += agmcompressplay
agmcompressplay_SOURCES := agmcompressplay.c
agmcompressplay_la_CFLAGS := $(AM_CFLAGS)
agmcompressplay_LDADD := -ltinycompress -ltinyalsa libagmmixer.la
bin_PROGRAMS += agmcompresscap
agmcompresscap_SOURCES := agmcompresscap.c
agmcompresscap_la_CFLAGS := $(AM_CFLAGS)
agmcompresscap_LDADD := -ltinycompress -ltinyalsa libagmmixer.la
endif
bin_PROGRAMS += agmvoiceui
agmvoiceui_SOURCES := agm_voiceui.c
agmvoiceui_la_CFLAGS := $(AM_CFLAGS)
agmvoiceui_LDADD := -lpthread -ltinyalsa libagmmixer.la
# install xml files under /etc
root_etcdir = "/etc"
root_etc_SCRIPTS = backend_conf.xml
install-data-hook:
chmod go-x $(DESTDIR)$(root_etcdir)/backend_conf.xml

View File

@@ -0,0 +1,56 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __MOCK_AGMMIXERWRAPPER_H__
#define __MOCK_AGMMIXERWRAPPER_H__
#include "AgmMixerWrapper.h"
class MockAgmMixerWrapper : public AgmMixerWrapper {
public:
MOCK_METHOD(int, mixerOpen, (unsigned int card), (override));
MOCK_METHOD(int, mixerClose, (), (override));
MOCK_METHOD(struct device_config, getDeviceMediaConfig, (char* filename, char *intf_name), (override));
MOCK_METHOD(int, setDeviceMediaConfig, (char *intf_name, struct device_config *config), (override));
MOCK_METHOD(int, setAudioInterfaceMetadata, (char *intf_name, unsigned int dkv, enum usecase_type usecase, int rate, int bitwidth, uint32_t stream_kv), (override));
MOCK_METHOD(int, setStreamMetadata, (int device, uint32_t stream_kv, unsigned int instance_kv), (override));
MOCK_METHOD(int, setStreamDeviceMetadata, (int device, uint32_t stream_kv, char *intf_name, unsigned int devicepp_kv), (override));
MOCK_METHOD(int, connectAudioInterfaceToStream, (unsigned int device, char *intf_name), (override));
MOCK_METHOD(int, configureMFC, (int device, char *intf_name, struct device_config), (override));
MOCK_METHOD(struct group_config, getGroupConfig, (char *intf_name), (override));
MOCK_METHOD(int, setGroupConfig, (unsigned int device, char *intf_name, unsigned int device_kv, struct group_config config, unsigned int channels), (override));
MOCK_METHOD(int, setDeviceCustomPayload, (char *intf_name, int device, unsigned int usb_device), (override));
MOCK_METHOD(int, disconnectAudioInterfaceToStream, (unsigned int device, char *intf_name), (override));
};
#endif

View File

@@ -0,0 +1,48 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __MOCK_AGMPCMWRAPPER_H__
#define __MOCK_AGMPCMWRAPPER_H__
#include "AgmPcmWrapper.h"
class MockAgmPcmWrapper : public AgmPcmWrapper {
public:
MOCK_METHOD(int, pcmOpen, (unsigned int card, unsigned int device, struct pcm_config *config), (override));
MOCK_METHOD(int, pcmFramesToBytes, (), (override));
MOCK_METHOD(int, pcmStart, (), (override));
MOCK_METHOD(int, pcmWrite, (char *buffer, int num_read), (override));
MOCK_METHOD(int, pcmStop, (), (override));
MOCK_METHOD(int, pcmClose, (), (override));
};
#endif

View File

@@ -0,0 +1,235 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <iostream>
#include "PlaybackCommand.h"
void PlaybackCommand::setCard(char **argv)
{
argv++;
if (*argv)
card = atoi(*argv);
}
void PlaybackCommand::setDevice(char **argv)
{
argv++;
if (*argv)
device = atoi(*argv);
}
void PlaybackCommand::setInterfaceNumber(char **argv)
{
argv++;
if (*argv)
intf_num = atoi(*argv);
}
void PlaybackCommand::setStreamKeyVector(char **argv)
{
argv++;
if (*argv)
stream_kv = convert_char_to_hex(*argv);
}
void PlaybackCommand::setInstanceKeyVector(char **argv)
{
argv++;
if (*argv) {
instance_kv = atoi(*argv);
}
}
void PlaybackCommand::setHaptics(char **argv)
{
argv++;
if (*argv)
haptics = atoi(*argv);
}
void PlaybackCommand::setInterfaceName(char **argv)
{
intf_name = (char**) malloc(intf_num * sizeof(char*));
if (!intf_name) {
std::cout << "insufficient memory" << std::endl;
exit(1);
}
for (int i = 0; i < intf_num ; i++){
argv++;
if (*argv)
intf_name[i] = *argv;
}
}
void PlaybackCommand::set24LE(char **argv)
{
argv++;
if (*argv) {
is_24_LE = atoi(*argv);
}
}
void PlaybackCommand::setDeviceppKeyVector(char **argv)
{
devicepp_kv = new unsigned int[intf_num];
if (!devicepp_kv) {
std::cout << "insufficient memory" << std::endl;
exit(1);
}
for (int i = 0; i < intf_num ; i++) {
devicepp_kv[i] = DEVICEPP_RX_AUDIO_MBDRC;
}
for (int i = 0; i < intf_num ; i++)
{
argv++;
if (*argv) {
devicepp_kv[i] = convert_char_to_hex(*argv);
}
}
}
void PlaybackCommand::setDeviceKeyVector(char **argv)
{
device_kv = new unsigned int[intf_num];
if (!device_kv) {
std::cout << "insufficient memory" << std::endl;
exit(1);
}
for (int i = 0; i < intf_num ; i++) {
argv++;
if (*argv) {
device_kv[i] = convert_char_to_hex(*argv);
}
}
}
void PlaybackCommand::setChannel(char **argv)
{
argv++;
if (*argv)
channels = atoi(*argv);
}
void PlaybackCommand::setSampleRate(char **argv)
{
argv++;
if (*argv)
rate = atoi(*argv);
}
void PlaybackCommand::setBitWidth(char **argv)
{
argv++;
if (*argv)
bits = atoi(*argv);
}
void PlaybackCommand::setUsbDevice(char **argv)
{
argv++;
if (*argv)
usb_device = atoi(*argv);
}
unsigned int PlaybackCommand::getCard(void)
{
return card;
}
unsigned int PlaybackCommand::getDevice(void)
{
return device;
}
int PlaybackCommand::getInterfaceNumber()
{
return intf_num;
}
unsigned int PlaybackCommand::getStreamKeyVector()
{
return stream_kv;
}
unsigned int PlaybackCommand::getInstanceKeyVector()
{
return instance_kv;
}
bool PlaybackCommand::getHaptics()
{
return haptics;
}
char **PlaybackCommand::getInterfaceName()
{
return intf_name;
}
bool PlaybackCommand::is24LE()
{
return is_24_LE;
}
unsigned int *PlaybackCommand::getDeviceppKeyVector()
{
return devicepp_kv;
}
unsigned int *PlaybackCommand::getDeviceKeyVector()
{
return device_kv;
}
unsigned int PlaybackCommand::getChannel()
{
return channels;
}
unsigned int PlaybackCommand::getSampleRate()
{
return rate;
}
unsigned int PlaybackCommand::getBitWidth()
{
return bits;
}
unsigned int PlaybackCommand::getUsbDevice()
{
return usb_device;
}

View File

@@ -0,0 +1,128 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __PLAYBACKCOMMAND_H__
#define __PLAYBACKCOMMAND_H__
#include "agmmixer.h"
class PlaybackCommand {
private:
unsigned int card;
unsigned int device;
int intf_num;
unsigned int stream_kv;
unsigned int instance_kv;
unsigned int *devicepp_kv;
unsigned int *device_kv;
unsigned int usb_device;
unsigned int channels;
unsigned int rate;
unsigned int bits;
char **intf_name;
bool haptics;
bool is_24_LE;
public:
PlaybackCommand()
: card(100),
device(100),
intf_num(1),
stream_kv(0),
instance_kv(INSTANCE_1),
haptics(false),
intf_name(nullptr),
is_24_LE(false),
devicepp_kv(nullptr),
device_kv(nullptr),
usb_device(1),
channels(2),
rate(48000),
bits(16)
{
devicepp_kv = new unsigned int[intf_num];
device_kv = new unsigned int[intf_num];
if (!device_kv || !devicepp_kv) {
std::cout << " insufficient memory" << std::endl;
exit(1);
}
device_kv[0] = SPEAKER;
devicepp_kv[0] = DEVICEPP_RX_AUDIO_MBDRC;
}
~PlaybackCommand()
{
if (!devicepp_kv) {
delete[] devicepp_kv;
devicepp_kv = nullptr;
}
if (!device_kv) {
delete[] device_kv;
device_kv = nullptr;
}
}
void setCard(char **argv);
void setDevice(char **argv);
void setInterfaceNumber(char **argv);
void setStreamKeyVector(char **argv);
void setInstanceKeyVector(char **argv);
void setHaptics(char **argv);
void setInterfaceName(char **argv);
void set24LE(char **argv);
void setDeviceKeyVector(char **argv);
void setDeviceppKeyVector(char **argv);
void setChannel(char **argv);
void setSampleRate(char **argv);
void setBitWidth(char **argv);
void setUsbDevice(char **argv);
unsigned int getCard();
unsigned int getDevice();
int getInterfaceNumber();
unsigned int getStreamKeyVector();
unsigned int getInstanceKeyVector();
bool getHaptics();
char **getInterfaceName();
bool is24LE();
unsigned int *getDeviceKeyVector();
unsigned int *getDeviceppKeyVector();
unsigned int getChannel();
unsigned int getSampleRate();
unsigned int getBitWidth();
unsigned int getUsbDevice();
};
#endif

View File

@@ -0,0 +1,95 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "PlaybackCommandParser.h"
void PlaybackCommandParser::parseCommandLine(char **argv)
{
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
playbackCommand.setDevice(argv);
} else if (strcmp(*argv, "-D") == 0) {
playbackCommand.setCard(argv);
} else if (strcmp(*argv, "-num_intf") == 0) {
playbackCommand.setInterfaceNumber(argv);
} else if (strcmp(*argv, "-i") == 0) {
playbackCommand.setInterfaceName(argv);
} else if (strcmp(*argv, "-h") == 0) {
playbackCommand.setHaptics(argv);
} else if (strcmp(*argv, "-dkv") == 0) {
playbackCommand.setDeviceKeyVector(argv);
} else if (strcmp(*argv, "-skv") == 0) {
playbackCommand.setStreamKeyVector(argv);
} else if (strcmp(*argv, "-ikv") == 0) {
playbackCommand.setInstanceKeyVector(argv);
} else if (strcmp(*argv, "-dppkv") == 0) {
playbackCommand.setDeviceppKeyVector(argv);
} else if (strcmp(*argv, "-is_24_LE") == 0) {
playbackCommand.set24LE(argv);
} else if (strcmp(*argv, "-c") == 0) {
playbackCommand.setChannel(argv);
} else if (strcmp(*argv, "-r") == 0) {
playbackCommand.setSampleRate(argv);
} else if (strcmp(*argv, "-b") == 0) {
playbackCommand.setBitWidth(argv);
} else if (strcmp(*argv, "-usb_d") == 0) {
playbackCommand.setUsbDevice(argv);
} else if (strcmp(*argv, "-help") == 0) {
usage();
}
if (*argv)
argv++;
}
}
PlaybackCommand& PlaybackCommandParser::getPlaybackCommand() {
return playbackCommand;
}
void PlaybackCommandParser::usage(void)
{
std::cout << "Usage: %s file.wav [-help print usage] [-D card] [-d device]" << std::endl;
std::cout << "[-c channels] [-r rate] [-b bits]" << std::endl;
std::cout << " [-num_intf num of interfaces followed by interface name]" << std::endl;
std::cout << " [-i intf_name] : Can be multiple if num_intf is more than 1" << std::endl;
std::cout << " [-dkv device_kv] : Can be multiple if num_intf is more than 1" << std::endl;
std::cout << " [-dppkv deviceppkv] : Assign 0 if no device pp in the graph" << std::endl;
std::cout << " [-ikv instance_kv] : Assign 0 if no instance kv in the graph" << std::endl;
std::cout << " [-skv stream_kv] [-h haptics usecase]" << std::endl;
std::cout << " [is_24_LE] : [0-1] Only to be used if user wants to play S24_LE clip" << std::endl;
std::cout << " [-usb_d usb device]" << std::endl;
std::cout << " 0: If clip bps is 32, and format is S32_LE" << std::endl;
std::cout << " 1: If clip bps is 24, and format is S24_LE" << std::endl;
}

View File

@@ -0,0 +1,51 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __PLAYBACKCOMMANDPARSER_H__
#define __PLAYBACKCOMMANDPARSER_H__
#include <iostream>
#include "PlaybackCommand.h"
class PlaybackCommandParser {
private:
PlaybackCommand playbackCommand;
public:
~PlaybackCommandParser() {};
void parseCommandLine(char **argv);
PlaybackCommand& getPlaybackCommand();
void usage(void);
};
#endif

View File

@@ -0,0 +1 @@
# tinyalsa_agmtest

View File

@@ -0,0 +1,49 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include "RiffWaveParser.h"
void RiffWaveParser::readHeader(std::ifstream& file)
{
file.read((char*)&riffId, sizeof(riffId));
file.read((char*)&riffSize, sizeof(riffSize));
file.read((char*)&waveId, sizeof(waveId));
}
bool RiffWaveParser::isValidFile(void)
{
if ((riffId != ID_RIFF) || (waveId != ID_WAVE)) {
return false;
}
return true;
}

View File

@@ -0,0 +1,56 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __RIFFWAVEPARSER_H__
#define __RIFFWAVEPARSER_H__
#include "HeaderParser.h"
const uint32_t ID_RIFF = 0x46464952;
const uint32_t ID_WAVE = 0x45564157;
class RiffWaveParser : public HeaderParser {
private:
uint32_t riffId;
uint32_t riffSize;
uint32_t waveId;
public:
~RiffWaveParser() {}
protected:
void readHeader(std::ifstream& file) override;
bool isValidFile(void) override;
};
#endif

View File

@@ -0,0 +1,72 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __SIGNALHANDLER_H__
#define __SIGNALHANDLER_H__
#include <signal.h>
class SignalHandler {
public:
SignalHandler() : closed(false) {}
void open() {
signal(SIGINT, SignalHandler::signalHandler);
}
void close() {
closed = true;
}
bool isClosed() {
return closed;
}
void Handler(int sig) {
signal(sig, SIG_IGN);
close();
}
private:
bool closed;
static SignalHandler instance;
static void signalHandler(int sig) {
instance.Handler(sig);
}
};
SignalHandler SignalHandler::instance;
#endif

View File

@@ -0,0 +1,476 @@
/*
** Copyright (c) 2019, 2021, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <errno.h>
#include <tinyalsa/asoundlib.h>
#include <sound/asound.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <stdarg.h>
#include <agm/agm_api.h>
#include "agmmixer.h"
static pthread_t event_thread;
static int done = 0;
char *audio_interface_name[] = {
"CODEC_DMA-LPAIF_VA-TX-0",
"CODEC_DMA-LPAIF_VA-TX-1",
"CODEC_DMA-LPAIF_RXTX-TX-3",
"MI2S-LPAIF_AXI-TX-PRIMARY",
"TDM-LPAIF_AXI-TX-PRIMARY",
"AUXPCM-LPAIF_AXI-TX-PRIMARY",
};
char *ec_aif_name[] = {
"CODEC_DMA-LPAIF_WSA-RX-0",
"CODEC_DMA-LPAIF_WSA-RX-1",
"MI2S-LPAIF_AXI-RX-PRIMARY",
"TDM-LPAIF_AXI-RX-PRIMARY",
"AUXPCM-LPAIF_AXI-RX-PRIMARY",
};
static void read_event_data(struct mixer *mixer, char *mixer_str)
{
struct mixer_ctl *ctl;
char *buf = NULL;
unsigned int num_values;
int i, ret;
struct agm_event_cb_params *params;
ctl = mixer_get_ctl_by_name(mixer, mixer_str);
num_values = mixer_ctl_get_num_values(ctl);
printf("%s - %d\n", __func__, num_values);
buf = calloc(1, num_values);
if (!buf) {
printf("Failed to allocate memory for buffer\n");
return;
}
ret = mixer_ctl_get_array(ctl, buf, num_values);
if (ret < 0) {
printf("Failed to mixer_ctl_get_array\n");
free(buf);
return;
}
params = (struct agm_event_cb_params *)buf;
printf("%s params.source_module_id %x\n", __func__, params->source_module_id);
printf("%s params.event_id %d\n", __func__, params->event_id);
printf("%s params.event_payload_size %x\n", __func__, params->event_payload_size);
}
static void event_wait_thread_loop(void *context)
{
struct mixer *mixer = (struct mixer *)context;
int ret = 0;
struct ctl_event mixer_event = {0};
printf("subscribing for event\n");
mixer_subscribe_events(mixer, 1);
printf("going to wait for event\n");
ret = mixer_wait_event(mixer, 30000);
if (ret < 0) {
printf("%s: mixer_wait_event err!, ret = %d\n", __func__, ret);
} else if (ret > 0) {
ret = mixer_read_event(mixer, &mixer_event);
if (ret >= 0) {
printf("Event Received %s\n", mixer_event.data.elem.id.name);
read_event_data(mixer, mixer_event.data.elem.id.name);
} else {
printf("%s: mixer_read failed, ret = %d\n", __func__, ret);
}
done = 1;
}
mixer_subscribe_events(mixer, 0);
}
static void record_lab_buffer(struct pcm *pcm, unsigned int cap_time)
{
struct timespec end;
struct timespec now;
unsigned int size;
char *buffer;
FILE *file;
int ret;
ret = pthread_join(event_thread, (void **) NULL);
if (ret < 0) {
printf("%s: Unable to join event thread\n", __func__);
return;
}
if (!done)
return;
file = fopen("/data/voice_rec.wav", "wb");
if (!file) {
printf("Unable to create voice_rec file\n");
return;
}
size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
buffer = malloc(size);
if (!buffer) {
printf("Unable to allocate %u bytes\n", size);
fclose(file);
return;
}
clock_gettime(CLOCK_MONOTONIC, &now);
end.tv_sec = now.tv_sec + cap_time;
end.tv_nsec = now.tv_nsec;
while (!pcm_read(pcm, buffer, size)) {
if (fwrite(buffer, 1, size, file) != size) {
printf("Error capturing sample\n");
break;
}
if (cap_time) {
clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec > end.tv_sec ||
(now.tv_sec == end.tv_sec && now.tv_nsec >= end.tv_nsec))
break;
}
}
free(buffer);
fclose(file);
}
static int get_file_size(char *filename)
{
FILE *fp;
int size = 0;
fp = fopen(filename, "rb");
if (!fp) {
printf("%s: Unable to open file '%s'\n", __func__, filename);
return 0;
}
fseek(fp, 0, SEEK_END);
size = ftell(fp);
printf("%s - sizeof %s is %d\n", __func__, filename, size);
fseek(fp, 0, SEEK_SET);
fclose(fp);
return size;
}
static void fill_payload(char *filename, int size, void *payload)
{
FILE *fp;
int bytes_read;
fp = fopen(filename, "rb");
if (!fp) {
printf("%s: Unable to open file '%s'\n", __func__, filename);
return;
}
bytes_read = fread((char*)payload, 1, size , fp);
if (bytes_read != size) {
printf("%s failed to read data from file %s, bytes read = %d\n", __func__, filename, bytes_read);
}
fclose(fp);
}
static void* merge_payload(uint32_t miid, int num, int *sum, ...)
{
va_list valist;
int i = 0, total_size = 0, offset = 0;
int *size = calloc(num, sizeof(int));
char **temp = calloc(num, sizeof(char *));
void *payload = NULL;
uint8_t *buf;
uint32_t *module_instance_id = NULL;
if (!size || !temp) {
printf("Failed to allocate memory for size and temp\n");
return NULL;
}
va_start(valist, num);
for (i = 0; i < num; i++) {
temp[i] = va_arg(valist, char *);
size[i] = get_file_size(temp[i]);
total_size += size[i];
}
va_end(valist);
payload = calloc(1, total_size);
if (!payload)
return NULL;
buf = payload;
for (i = 0; i < num; i++) {
fill_payload(temp[i], size[i], buf);
// Update SVA miid
module_instance_id = (uint32_t *)buf;
*module_instance_id = miid;
buf += size[i];
}
*sum = total_size;
/* TODO : free memory */
return payload;
}
void voice_ui_test(unsigned int card, unsigned int device, unsigned int audio_intf, unsigned int cap_time, int ec_aif,
unsigned int device_kv, unsigned int stream_kv, unsigned int instance_kv, unsigned int devicepp_kv)
{
struct mixer *mixer;
char *intf_name = audio_interface_name[audio_intf];
char *ec_intf_name = NULL;
struct pcm_config config;
struct pcm *pcm;
struct device_config dev_config;
int ret = 0;
enum pcm_format format = PCM_FORMAT_S16_LE;
uint32_t miid = 0, param_size = 0;
void *param_buf = NULL;
memset(&config, 0, sizeof(config));
config.channels = 2;
config.rate = 48000;
config.period_size = 1024;
config.period_count = 4;
config.format = format;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;
stream_kv = stream_kv ? stream_kv : VOICE_UI;
dev_config.rate = config.rate;
dev_config.ch = config.channels;
dev_config.bits = get_pcm_bit_width(config.format);
dev_config.format = config.format;
mixer = mixer_open(card);
if (!mixer) {
printf("Failed to open mixer\n");
return;
}
/* set device/audio_intf media config mixer control */
if (set_agm_device_media_config(mixer, intf_name, &dev_config)) {
printf("Failed to set device media config\n");
goto err_close_mixer;
}
/* set audio interface metadata mixer control */
if (set_agm_audio_intf_metadata(mixer, intf_name, 0, CAPTURE, config.rate,
pcm_format_to_bits(format), stream_kv)) {
printf("Failed to set device metadata\n");
goto err_close_mixer;
}
/* set stream metadata mixer control */
if (set_agm_stream_metadata(mixer, device, stream_kv, CAPTURE, STREAM_PCM,
instance_kv)) {
printf("Failed to set pcm metadata\n");
goto err_close_mixer;
}
if (devicepp_kv != 0) {
if (set_agm_streamdevice_metadata(mixer, device, stream_kv, CAPTURE, STREAM_PCM,
intf_name, devicepp_kv)) {
printf("Failed to set pcm metadata\n");
goto err_close_mixer;
}
}
/* connect pcm stream to audio intf */
if (connect_agm_audio_intf_to_stream(mixer, device, intf_name, STREAM_PCM, true)) {
printf("Failed to connect pcm to audio interface\n");
goto err_close_mixer;
}
ret = agm_mixer_get_miid(mixer, device, intf_name, STREAM_PCM, DEVICE_SVA, &miid);
if (ret) {
printf("%s Get MIID from tag data failed\n", __func__);
goto err_close_mixer;
}
param_buf = merge_payload(miid, 3, &param_size, "/vendor/etc/sound_model",
"/vendor/etc/wakeup_config", "/vendor/etc/buffer_config");
if (agm_mixer_set_param(mixer, device, STREAM_PCM, param_buf, param_size)) {
printf("setparam failed\n");
goto err_close_mixer;
}
pcm = pcm_open(card, device, PCM_IN, &config);
if (!pcm || !pcm_is_ready(pcm)) {
printf("Unable to open PCM device (%s)\n",
pcm_get_error(pcm));
goto err_close_mixer;
}
#if 0
/* Load Sound Model */
if (agm_mixer_set_param_with_file(mixer, device, STREAM_PCM, "/etc/sound_model")) {
printf("soundmodel load failed\n");
goto err_close_pcm;
}
/* Register voice wakeup config */
if (agm_mixer_set_param_with_file(mixer, device, STREAM_PCM, "/etc/wakeup_config")) {
printf("wakeup config registration failed\n");
goto err_close_pcm;
}
#endif
// Call API to register event with GSL here
// Same as GSL_CMD_REGISTER_CUSTOM_EVENT
if (agm_mixer_register_event(mixer, device, STREAM_PCM, miid, 1)) {
printf("GSL event registration failed\n");
goto err_close_pcm;
}
#if 0
/* Register buffer config */
if (agm_mixer_set_param_with_file(mixer, device, STREAM_PCM, "/etc/buffer_config")) {
printf("buffer config registration failed\n");
goto err_close_pcm;
}
#endif
/* Setup EC ref path */
if (ec_aif != -1) {
if (agm_mixer_set_ecref_path(mixer, device, STREAM_PCM, ec_aif_name[ec_aif])) {
printf("EC ref path failed\n");
goto err_close_pcm;
}
}
if (pcm_start(pcm) < 0) { // This internally calls pcm_prepare too.
printf("start error\n");
goto err_close_pcm;
}
pthread_create(&event_thread,
(const pthread_attr_t *) NULL, event_wait_thread_loop, mixer);
record_lab_buffer(pcm, cap_time);
/* Reset Engine */
if (agm_mixer_set_param_with_file(mixer, device, STREAM_PCM, "/vendor/etc/engine_reset")) {
printf("stream setup duration configuration failed\n");
goto err_close_pcm;
}
err_close_pcm:
if (ec_aif != -1)
agm_mixer_set_ecref_path(mixer, device, STREAM_PCM, "ZERO");
pcm_close(pcm);
err_close_mixer:
mixer_close(mixer);
printf("completed event test\n");
}
int main(int argc, char **argv)
{
unsigned int device = 100;
unsigned int card = 0;
unsigned int audio_intf = 0;
int ec_aif = -1;
unsigned int cap_time = 5;
unsigned int device_kv = 0;
unsigned int devicepp_kv = DEVICEPP_TX_FLUENCE_FFECNS;
unsigned int stream_kv = 0;
unsigned int instance_kv = INSTANCE_1;
argv += 1;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
}
if (*argv && strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
}
if (*argv && strcmp(*argv, "-i") == 0) {
argv++;
if (*argv)
audio_intf = atoi(*argv);
if (audio_intf >= sizeof(audio_interface_name)/sizeof(char *)) {
printf("Invalid audio interface index denoted by -i\n");
return 1;
}
}
if (*argv && strcmp(*argv, "-e") == 0) {
argv++;
if (*argv)
ec_aif = atoi(*argv);
if (ec_aif >= sizeof(ec_aif_name)/sizeof(char *)) {
printf("Invalid echoref audio interface index denoted by -i\n");
return 1;
}
}
if (*argv && strcmp(*argv, "-T") == 0) {
argv++;
if (*argv)
cap_time = atoi(*argv);
} else if (*argv && strcmp(*argv, "-dkv") == 0) {
argv++;
if (*argv)
device_kv = convert_char_to_hex(*argv);
} else if (*argv && strcmp(*argv, "-skv") == 0) {
argv++;
if (*argv)
stream_kv = convert_char_to_hex(*argv);
} else if (*argv && strcmp(*argv, "-ikv") == 0) {
argv++;
if (*argv)
instance_kv = atoi(*argv);
} else if (*argv && strcmp(*argv, "-dppkv") == 0) {
argv++;
if (*argv)
devicepp_kv = convert_char_to_hex(*argv);
}
if (*argv)
argv++;
}
voice_ui_test(card, device, audio_intf, cap_time, ec_aif, device_kv, stream_kv,
instance_kv, devicepp_kv);
return 0;
}

View File

@@ -0,0 +1,445 @@
/*
** Copyright (c) 2019, 2021, The Linux Foundation. All rights reserved.
**
** Copyright 2011, The Android Open Source Project
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of The Android Open Source Project nor the names of
** its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
** DAMAGE.
**
** Changes from Qualcomm Innovation Center, Inc. are provided under the following license:
** Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <tinyalsa/asoundlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include "agmmixer.h"
#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
#define ID_DATA 0x61746164
#define FORMAT_PCM 1
struct wav_header {
uint32_t riff_id;
uint32_t riff_sz;
uint32_t riff_fmt;
uint32_t fmt_id;
uint32_t fmt_sz;
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
uint32_t data_id;
uint32_t data_sz;
};
int capturing = 1;
static unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
unsigned int usb_device, unsigned int channels, unsigned int rate,
unsigned int bits, enum pcm_format format, unsigned int period_size,
unsigned int period_count, unsigned int cap_time,
struct device_config *dev_config, unsigned int stream_kv,
unsigned int device_kv, unsigned int instance_kv,
unsigned int devicepp_kv);
static void sigint_handler(int sig)
{
capturing = 0;
}
static void usage(void)
{
printf(" Usage: %s file.wav [-help print usage] [-D card] [-d device]\n"
" [-c channels] [-r rate] [-b bits] [-p period_size]\n"
" [-n n_periods] [-T capture time] [-i intf_name] [-dkv device_kv]\n"
" [-dppkv deviceppkv] : Assign 0 if no device pp in the graph\n"
" [-ikv instance_kv] : Assign 0 if no instance kv in the graph\n"
" [-skv stream_kv]\n"
" [-is_24_LE] : [0-1] Only to be used if user wants to record 32 bps clip\n"
" [-usb_d usb device]\n"
" 0: If bps is 32, and format should be S32_LE\n"
" 1: If bps is 24, and format should be S24_LE\n");
}
int main(int argc, char **argv)
{
FILE *file;
struct wav_header header;
unsigned int card = 100;
unsigned int device = 101;
unsigned int usb_device = 1;
unsigned int channels = 2;
unsigned int rate = 44100;
unsigned int bits = 16;
unsigned int frames;
unsigned int period_size = 1024;
unsigned int period_count = 4;
unsigned int cap_time = 0;
char *intf_name = NULL;
unsigned int device_kv = 0;
struct device_config config;
enum pcm_format format;
int ret = 0;
unsigned int devicepp_kv = 0;
unsigned int stream_kv = 0;
unsigned int instance_kv = INSTANCE_1;
bool is_24_LE = false;
if (argc < 2) {
usage();
return 1;
}
file = fopen(argv[1], "wb");
if (!file) {
printf("Unable to create file '%s'\n", argv[1]);
return 1;
}
/* parse command line arguments */
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
} else if (strcmp(*argv, "-c") == 0) {
argv++;
if (*argv)
channels = atoi(*argv);
} else if (strcmp(*argv, "-r") == 0) {
argv++;
if (*argv)
rate = atoi(*argv);
} else if (strcmp(*argv, "-b") == 0) {
argv++;
if (*argv)
bits = atoi(*argv);
} else if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
} else if (strcmp(*argv, "-p") == 0) {
argv++;
if (*argv)
period_size = atoi(*argv);
} else if (strcmp(*argv, "-n") == 0) {
argv++;
if (*argv)
period_count = atoi(*argv);
} else if (strcmp(*argv, "-T") == 0) {
argv++;
if (*argv)
cap_time = atoi(*argv);
} else if (strcmp(*argv, "-i") == 0) {
argv++;
if (*argv)
intf_name = *argv;
} else if (strcmp(*argv, "-dkv") == 0) {
argv++;
if (*argv)
device_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-skv") == 0) {
argv++;
if (*argv)
stream_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-ikv") == 0) {
argv++;
if (*argv)
instance_kv = atoi(*argv);
} else if (strcmp(*argv, "-dppkv") == 0) {
argv++;
if (*argv)
devicepp_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-is_24_LE") == 0) {
argv++;
if (*argv)
is_24_LE = atoi(*argv);
} else if (strcmp(*argv, "-usb_d") == 0) {
argv++;
if (*argv)
usb_device = atoi(*argv);
}else if (strcmp(*argv, "-help") == 0) {
usage();
}
if (*argv)
argv++;
}
header.riff_id = ID_RIFF;
header.riff_sz = 0;
header.riff_fmt = ID_WAVE;
header.fmt_id = ID_FMT;
header.fmt_sz = 16;
header.audio_format = FORMAT_PCM;
header.num_channels = channels;
header.sample_rate = rate;
switch (bits) {
case 32:
if (is_24_LE)
format = PCM_FORMAT_S24_LE;
else
format = PCM_FORMAT_S32_LE;
break;
case 24:
format = PCM_FORMAT_S24_3LE;
break;
case 16:
format = PCM_FORMAT_S16_LE;
break;
default:
printf("%u bits is not supported.\n", bits);
fclose(file);
return 1;
}
if (intf_name == NULL)
return 1;
ret = get_device_media_config(BACKEND_CONF_FILE, intf_name, &config);
if (ret) {
printf("Invalid input, entry not found for %s\n", intf_name);
fclose(file);
return ret;
}
if (config.format != PCM_FORMAT_INVALID) {
printf("Valid format from backend_conf %d\n", config.format);
config.bits = get_pcm_bit_width(config.format);
}
header.bits_per_sample = pcm_format_to_bits(format);
header.byte_rate = (header.bits_per_sample / 8) * channels * rate;
header.block_align = channels * (header.bits_per_sample / 8);
header.data_id = ID_DATA;
/* leave enough room for header */
fseek(file, sizeof(struct wav_header), SEEK_SET);
/* install signal handler and begin capturing */
signal(SIGINT, sigint_handler);
signal(SIGHUP, sigint_handler);
signal(SIGTERM, sigint_handler);
frames = capture_sample(file, card, device, usb_device, header.num_channels,
header.sample_rate, bits, format,
period_size, period_count, cap_time, &config,
stream_kv, device_kv, instance_kv, devicepp_kv);
printf("Captured %u frames\n", frames);
/* write header now all information is known */
header.data_sz = frames * header.block_align;
header.riff_sz = header.data_sz + sizeof(header) - 8;
fseek(file, 0, SEEK_SET);
fwrite(&header, sizeof(struct wav_header), 1, file);
fclose(file);
return 0;
}
unsigned int capture_sample(FILE *file, unsigned int card, unsigned int device,
unsigned int usb_device, unsigned int channels, unsigned int rate,
unsigned int bits, enum pcm_format format, unsigned int period_size,
unsigned int period_count, unsigned int cap_time,
struct device_config *dev_config, unsigned int stream_kv,
unsigned int device_kv, unsigned int instance_kv, unsigned int devicepp_kv)
{
struct pcm_config config;
struct pcm *pcm;
struct mixer *mixer;
char *buffer;
char *intf_name = dev_config->name;
unsigned int size;
unsigned int bytes_read = 0;
unsigned int frames = 0;
struct timespec end;
struct timespec now;
uint32_t miid = 0;
int ret = 0;
struct usbAudioConfig cfg;
uint8_t* payload = NULL;
size_t payloadSize = 0;
stream_kv = stream_kv ? stream_kv : PCM_RECORD;
memset(&config, 0, sizeof(config));
config.channels = channels;
config.rate = rate;
config.period_size = period_size;
config.period_count = period_count;
config.format = format;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;
if (NULL == intf_name) {
printf("No interface name mentioned, Exiting !!!\n");
return 0;
}
mixer = mixer_open(card);
if (!mixer) {
printf("Failed to open mixer\n");
return 0;
}
if(strcmp(intf_name, "USB_AUDIO-TX") == 0) {
dev_config->rate = rate;
dev_config->ch = channels;
dev_config->bits = bits;
dev_config->format = PCM_FORMAT_INVALID;
}
/* set device/audio_intf media config mixer control */
if (set_agm_device_media_config(mixer, intf_name, dev_config)) {
printf("Failed to set device media config\n");
goto err_close_mixer;
}
/* set audio interface metadata mixer control */
if (set_agm_audio_intf_metadata(mixer, intf_name, device_kv, CAPTURE,
dev_config->rate, dev_config->bits, stream_kv)) {
printf("Failed to set device metadata\n");
goto err_close_mixer;
}
/* set stream metadata mixer control */
if (set_agm_capture_stream_metadata(mixer, device, stream_kv, CAPTURE, STREAM_PCM,
instance_kv)) {
printf("Failed to set pcm metadata\n");
goto err_close_mixer;
}
if (devicepp_kv != 0) {
if (set_agm_streamdevice_metadata(mixer, device, stream_kv, CAPTURE, STREAM_PCM,
intf_name, devicepp_kv)) {
printf("Failed to set pcm metadata\n");
goto err_close_mixer;
}
}
ret = agm_mixer_get_miid (mixer, device, intf_name, STREAM_PCM, TAG_STREAM_MFC, &miid);
if (ret) {
printf("MFC not present for this graph\n");
} else {
if (configure_mfc(mixer, device, intf_name, TAG_STREAM_MFC,
STREAM_PCM, rate, channels, get_pcm_bit_width(format), miid)) {
printf("Failed to configure stream mfc\n");
goto err_close_mixer;
}
}
if (strcmp(intf_name, "USB_AUDIO-TX") == 0) {
ret = agm_mixer_get_miid (mixer, device, intf_name, STREAM_PCM, DEVICE_HW_ENDPOINT_TX, &miid);
if (ret == 0) {
cfg.usb_token = (usb_device << 16)|0x1;
cfg.svc_interval = 0;
get_agm_usb_audio_config_payload(&payload, &payloadSize, miid, &cfg);
if (payloadSize) {
ret = set_agm_device_custom_payload(mixer, intf_name, payload, payloadSize);
} else {
ret = -1;
printf("set_agm_device_custom_payload failed\n");
goto err_close_mixer;
}
} else {
printf("Failed to get miid for USB_AUDIO-TX\n");
goto err_close_mixer;
}
}
/* connect pcm stream to audio intf */
if (connect_agm_audio_intf_to_stream(mixer, device, intf_name, STREAM_PCM, true)) {
printf("Failed to connect pcm to audio interface\n");
goto err_close_mixer;
}
pcm = pcm_open(card, device, PCM_IN, &config);
if (!pcm || !pcm_is_ready(pcm)) {
printf("Unable to open PCM device (%s)\n",
pcm_get_error(pcm));
goto err_close_mixer;
}
size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
buffer = malloc(size);
if (!buffer) {
printf("Unable to allocate %u bytes\n", size);
goto err_close_pcm;
}
printf("Capturing sample: %u ch, %u hz, %u bit\n", channels, rate,
pcm_format_to_bits(format));
if (pcm_start(pcm) < 0) {
printf("start error\n");
goto err_close_pcm;
}
clock_gettime(CLOCK_MONOTONIC, &now);
end.tv_sec = now.tv_sec + cap_time;
end.tv_nsec = now.tv_nsec;
while (capturing && !pcm_read(pcm, buffer, size)) {
if (fwrite(buffer, 1, size, file) != size) {
printf("Error capturing sample\n");
break;
}
bytes_read += size;
if (cap_time) {
clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec > end.tv_sec ||
(now.tv_sec == end.tv_sec && now.tv_nsec >= end.tv_nsec))
break;
}
}
frames = pcm_bytes_to_frames(pcm, bytes_read);
free(buffer);
pcm_stop(pcm);
err_close_pcm:
connect_agm_audio_intf_to_stream(mixer, device, intf_name, STREAM_PCM, false);
pcm_close(pcm);
err_close_mixer:
if (payload) {
free(payload);
}
mixer_close(mixer);
return frames;
}

View File

@@ -0,0 +1,594 @@
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
* This code is used under the BSD license.
*
* BSD LICENSE
*
* Copyright (c) 2011-2012, Intel Corporation
* Copyright (c) 2013-2014, Wolfson Microelectronic Ltd.
* All rights reserved.
*
* Author: Vinod Koul <vinod.koul@linux.intel.com>
* Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* LGPL LICENSE
*
* Copyright (c) 2011-2012, Intel Corporation
* Copyright (c) 2013-2014, Wolfson Microelectronic Ltd.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to
* the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Changes from Qualcomm Innovation Center are provided under the following license:
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <stdint.h>
#include <linux/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <stdbool.h>
#include <getopt.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#define __force
//#define __bitwise
#define __user
#include <sound/compress_params.h>
#include <sound/compress_offload.h>
#include <tinycompress/tinycompress.h>
#include "agmmixer.h"
static int verbose;
static int file;
static FILE *finfo;
static bool streamed;
static const unsigned int DEFAULT_CHANNELS = 1;
static const unsigned int DEFAULT_RATE = 44100;
static const unsigned int DEFAULT_FORMAT = SNDRV_PCM_FORMAT_S16_LE;
struct riff_chunk {
char desc[4];
uint32_t size;
} __attribute__((__packed__));
struct wave_header {
struct {
struct riff_chunk chunk;
char format[4];
} __attribute__((__packed__)) riff;
struct {
struct riff_chunk chunk;
uint16_t type;
uint16_t channels;
uint32_t rate;
uint32_t byterate;
uint16_t blockalign;
uint16_t samplebits;
} __attribute__((__packed__)) fmt;
struct {
struct riff_chunk chunk;
} __attribute__((__packed__)) data;
} __attribute__((__packed__));
static const struct wave_header blank_wave_header = {
.riff = {
.chunk = {
.desc = "RIFF",
},
.format = "WAVE",
},
.fmt = {
.chunk = {
.desc = "fmt ", /* Note the space is important here */
.size = sizeof(blank_wave_header.fmt) -
sizeof(blank_wave_header.fmt.chunk),
},
.type = 0x01, /* PCM */
},
.data = {
.chunk = {
.desc = "data",
},
},
};
static void init_wave_header(struct wave_header *header, uint16_t channels,
uint32_t rate, uint16_t samplebits)
{
memcpy(header, &blank_wave_header, sizeof(blank_wave_header));
header->fmt.channels = channels;
header->fmt.rate = rate;
header->fmt.byterate = channels * rate * (samplebits / 8);
header->fmt.blockalign = channels * (samplebits / 8);
header->fmt.samplebits = samplebits;
}
static void size_wave_header(struct wave_header *header, uint32_t size)
{
header->riff.chunk.size = sizeof(*header) -
sizeof(header->riff.chunk) + size;
header->data.chunk.size = size;
}
static void usage(void)
{
fprintf(stderr, "usage: crec [OPTIONS] [filename]\n"
"-D\tcard number\n"
"-d\tdevice node\n"
"-buf\tbuffer size\n"
"-f\tfragments\n"
"-v\tverbose mode\n"
"-l\tlength of record in seconds\n"
" [-help print usage]\n"
" [-c channels] [-r rate] [-b bits]\n"
" [-n n_periods] [-i intf_name] [-dkv device_kv]\n"
" [-dppkv deviceppkv] : Assign 0 if no device pp in the graph\n"
" [-ikv instance_kv] : Assign 0 if no instance kv in the graph\n"
" [-skv stream_kv]");
exit(EXIT_FAILURE);
}
static int print_time(struct compress *compress)
{
unsigned int avail;
struct timespec tstamp;
if (compress_get_hpointer(compress, &avail, &tstamp) != 0) {
fprintf(stderr, "Error querying timestamp\n");
fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
return -1;
} else {
fprintf(finfo, "DSP recorded %jd.%jd\n",
(intmax_t)tstamp.tv_sec, (intmax_t)tstamp.tv_nsec*1000);
}
return 0;
}
static int finish_record(void)
{
struct wave_header header;
int ret;
size_t nread, written;
if (!file)
return -ENOENT;
/* can't rewind if streaming to stdout */
if (streamed)
return 0;
/* Get amount of data written to file */
ret = lseek(file, 0, SEEK_END);
if (ret < 0)
return -errno;
written = ret;
if (written < sizeof(header))
return -ENOENT;
written -= sizeof(header);
/* Sync file header from file */
ret = lseek(file, 0, SEEK_SET);
if (ret < 0)
return -errno;
nread = read(file, &header, sizeof(header));
if (nread != sizeof(header))
return -errno;
/* Update file header */
ret = lseek(file, 0, SEEK_SET);
if (ret < 0)
return -errno;
size_wave_header(&header, written);
written = write(file, &header, sizeof(header));
if (written != sizeof(header))
return -errno;
return 0;
}
static void capture_samples(char *name, unsigned int card, unsigned int device,
unsigned long buffer_size, unsigned int frag,
unsigned int length, unsigned int rate,
unsigned int channels, unsigned int format, struct device_config *dev_config, unsigned int stream_kv,
unsigned int device_kv, unsigned int instance_kv, unsigned int devicepp_kv)
{
struct compr_config config;
struct snd_codec codec;
struct compress *compress;
struct mixer *mixer;
struct wave_header header;
char *buffer;
size_t written;
int read, ret;
unsigned int size, total_read = 0;
unsigned int samplebits;
uint32_t miid = 0;
char *intf_name = dev_config->name;
//TODO: Change default stream_kv to COMPRESS_RECORD later.
stream_kv = stream_kv ? stream_kv : PCM_RECORD;
switch (format) {
case SNDRV_PCM_FORMAT_S32_LE:
samplebits = 32;
break;
default:
samplebits = 16;
break;
}
/* Convert length from seconds to bytes */
length = length * rate * (samplebits / 8) * channels;
if (verbose)
fprintf(finfo, "%s: entry, reading %u bytes\n", __func__, length);
if (!name) {
file = STDOUT_FILENO;
exit(EXIT_FAILURE);
} else {
file = open(name, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (file == -1) {
fprintf(stderr, "Unable to open file '%s'\n", name);
exit(EXIT_FAILURE);
}
}
/* Write a header, will update with size once record is complete */
if (!streamed) {
init_wave_header(&header, channels, rate, samplebits);
written = write(file, &header, sizeof(header));
if (written != sizeof(header)) {
fprintf(stderr, "Error writing output file header: %s\n",
strerror(errno));
goto file_exit;
}
}
memset(&codec, 0, sizeof(codec));
memset(&config, 0, sizeof(config));
codec.id = SND_AUDIOCODEC_PCM;
codec.ch_in = channels;
codec.ch_out = channels;
codec.sample_rate = rate;
if (!codec.sample_rate) {
fprintf(stderr, "invalid sample rate %d\n", rate);
goto file_exit;
}
codec.format = format;
if ((buffer_size != 0) && (frag != 0)) {
config.fragment_size = buffer_size/frag;
config.fragments = frag;
}
config.codec = &codec;
mixer = mixer_open(card);
if (!mixer) {
printf("Failed to open mixer\n");
goto file_exit;
}
/* set device/audio_intf media config mixer control */
if (set_agm_device_media_config(mixer, intf_name, dev_config)) {
printf("Failed to set device media config\n");
goto mixer_exit;
}
/* set audio interface metadata mixer control */
if (set_agm_audio_intf_metadata(mixer, intf_name, device_kv, CAPTURE,
dev_config->rate, dev_config->bits, stream_kv)) {
printf("Failed to set device metadata\n");
goto mixer_exit;
}
/* set audio interface metadata mixer control */
/* Change pcm_record to compress_record */
if (set_agm_capture_stream_metadata(mixer, device, stream_kv, CAPTURE, STREAM_COMPRESS,
instance_kv)) {
printf("Failed to set pcm metadata\n");
goto mixer_exit;
}
if (devicepp_kv != 0) {
if (set_agm_streamdevice_metadata(mixer, device, stream_kv, CAPTURE, STREAM_COMPRESS,
intf_name, devicepp_kv)) {
printf("Failed to set pcm metadata\n");
goto mixer_exit;
}
}
ret = agm_mixer_get_miid (mixer, device, intf_name, STREAM_COMPRESS, TAG_STREAM_MFC, &miid);
if (ret) {
printf("MFC not present for this graph");
} else {
if (configure_mfc(mixer, device, intf_name, TAG_STREAM_MFC,
STREAM_COMPRESS, rate, channels, pcm_format_to_bits(format), miid)) {
printf("Failed to configure pspd mfc\n");
goto mixer_exit;
}
}
/* Note: No common metadata as of now*/
/* connect stream to audio intf */
if (connect_agm_audio_intf_to_stream(mixer, device, intf_name, STREAM_COMPRESS, true)) {
printf("Failed to connect stream to audio interface\n");
goto mixer_exit;
}
compress = compress_open(card, device, COMPRESS_OUT, &config);
if (!compress || !is_compress_ready(compress)) {
fprintf(stderr, "Unable to open Compress device %d:%d\n",
card, device);
fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
goto mixer_exit;
};
if (verbose)
fprintf(finfo, "%s: Opened compress device\n", __func__);
size = config.fragment_size;
buffer = malloc(size * config.fragments);
if (!buffer) {
fprintf(stderr, "Unable to allocate %d bytes\n", size);
goto comp_exit;
}
fprintf(finfo, "Recording file %s On Card %u device %u, with buffer of %lu bytes\n",
name, card, device, buffer_size);
fprintf(finfo, "Codec %u Format %u Channels %u, %u Hz\n",
codec.id, codec.format, codec.ch_out, rate);
compress_start(compress);
if (verbose)
fprintf(finfo, "%s: Capturing audio NOW!!!\n", __func__);
do {
read = compress_read(compress, buffer, size);
if (read < 0) {
fprintf(stderr, "Error reading sample\n");
fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
goto buf_exit;
}
if ((unsigned int)read != size) {
fprintf(stderr, "We read %d, DSP sent %d\n",
size, read);
}
if (read > 0) {
total_read += read;
written = write(file, buffer, read);
if (written != (size_t)read) {
fprintf(stderr, "Error writing output file: %s\n",
strerror(errno));
goto buf_exit;
}
if (verbose) {
print_time(compress);
fprintf(finfo, "%s: read %d\n", __func__, read);
}
}
} while (!length || total_read < length);
ret = compress_stop(compress);
if (ret < 0) {
fprintf(stderr, "Error closing stream\n");
fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
}
ret = finish_record();
if (ret < 0) {
fprintf(stderr, "Failed to finish header: %s\n", strerror(ret));
goto buf_exit;
}
if (verbose)
fprintf(finfo, "%s: exit success\n", __func__);
free(buffer);
close(file);
file = 0;
compress_close(compress);
return;
buf_exit:
free(buffer);
comp_exit:
compress_close(compress);
connect_agm_audio_intf_to_stream(mixer, device, intf_name, STREAM_COMPRESS, false);
mixer_exit:
mixer_close(mixer);
file_exit:
close(file);
if (verbose)
fprintf(finfo, "%s: exit failure\n", __func__);
exit(EXIT_FAILURE);
}
static void sig_handler(int signum __attribute__ ((unused)))
{
finish_record();
if (file)
close(file);
_exit(EXIT_FAILURE);
}
int main(int argc, char **argv)
{
char *file = NULL;
unsigned long buffer_size = 0;
unsigned int card = 0, device = 0, frag = 0, length = 0;
unsigned int rate = DEFAULT_RATE, channels = DEFAULT_CHANNELS;
unsigned int bits = 16;
unsigned int format = DEFAULT_FORMAT;
char* intf_name = NULL;
int ret = 0;
unsigned int devicepp_kv = DEVICEPP_TX_AUDIO_FLUENCE_SMECNS;
unsigned int stream_kv = 0;
unsigned int instance_kv = INSTANCE_1;
struct device_config config;
unsigned int device_kv = 0;
if (signal(SIGINT, sig_handler) == SIG_ERR) {
fprintf(stderr, "Error registering signal handler\n");
exit(EXIT_FAILURE);
}
if (argc < 3)
usage();
verbose = 0;
file = argv[1];
/* parse command line arguments */
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
} else if (strcmp(*argv, "-c") == 0) {
argv++;
if (*argv)
channels = atoi(*argv);
} else if (strcmp(*argv, "-r") == 0) {
argv++;
if (*argv)
rate = atoi(*argv);
} else if (strcmp(*argv, "-b") == 0) {
argv++;
if (*argv)
bits = atoi(*argv);
} else if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
} else if (strcmp(*argv, "-l") == 0) {
argv++;
if (*argv)
length = atoi(*argv);
} else if (strcmp(*argv, "-i") == 0) {
argv++;
if (*argv)
intf_name = *argv;
} else if (strcmp(*argv, "-dkv") == 0) {
argv++;
if (*argv)
device_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-skv") == 0) {
argv++;
if (*argv)
stream_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-ikv") == 0) {
argv++;
if (*argv)
instance_kv = atoi(*argv);
} else if (strcmp(*argv, "-dppkv") == 0) {
argv++;
if (*argv)
devicepp_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-buf") == 0) {
argv++;
if (*argv)
buffer_size = atoi(*argv);
} else if (strcmp(*argv, "-f") == 0) {
argv++;
if (*argv)
frag = atoi(*argv);
} else if (strcmp(*argv, "-v") == 0) {
argv++;
if (*argv)
verbose = atoi(*argv);
} else if (strcmp(*argv, "-help") == 0) {
usage();
}
if (*argv)
argv++;
}
if (intf_name == NULL) {
printf("Invalid audio interface index denoted by -i\n");
exit(EXIT_FAILURE);
}
ret = get_device_media_config(BACKEND_CONF_FILE, intf_name, &config);
if (ret) {
printf("Invalid input, entry not found for %s\n", intf_name);
fclose(file);
return ret;
}
switch (bits) {
case 32:
format = SNDRV_PCM_FORMAT_S32_LE;
break;
default:
format = SNDRV_PCM_FORMAT_S16_LE;
break;
}
capture_samples(file, card, device, buffer_size, frag, length,
rate, channels, format, &config, stream_kv, device_kv, instance_kv,
devicepp_kv);
fprintf(finfo, "Finish capturing... Close Normally\n");
exit(EXIT_SUCCESS);
}

View File

@@ -0,0 +1,653 @@
/*
* Copyright (c) 2019, 2021, The Linux Foundation. All rights reserved.
* This code is used under the BSD license.
*
* BSD LICENSE
*
* Copyright (c) 2011-2012, Intel Corporation
* All rights reserved.
*
* Author: Vinod Koul <vinod.koul@linux.intel.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* LGPL LICENSE
*
* Copyright (c) 2011-2012, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to
* the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
* Changes from Qualcomm Innovation Center are provided under the following license:
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <stdint.h>
#include <linux/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <stdbool.h>
#include <getopt.h>
#include <sys/time.h>
#define __force
//#define __bitwise
#define __user
#include <sound/compress_params.h>
#include <tinycompress/tinycompress.h>
#include "agmmixer.h"
#define MP3_SYNC 0xe0ff
const int mp3_sample_rates[3][3] = {
{44100, 48000, 32000}, /* MPEG-1 */
{22050, 24000, 16000}, /* MPEG-2 */
{11025, 12000, 8000}, /* MPEG-2.5 */
};
const int mp3_bit_rates[3][3][15] = {
{
/* MPEG-1 */
{ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}, /* Layer 1 */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}, /* Layer 2 */
{ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}, /* Layer 3 */
},
{
/* MPEG-2 */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, /* Layer 1 */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* Layer 2 */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* Layer 3 */
},
{
/* MPEG-2.5 */
{ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, /* Layer 1 */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* Layer 2 */
{ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* Layer 3 */
},
};
enum mpeg_version {
MPEG1 = 0,
MPEG2 = 1,
MPEG25 = 2
};
enum mp3_stereo_mode {
STEREO = 0x00,
JOINT = 0x01,
DUAL = 0x02,
MONO = 0x03
};
static int verbose;
static void usage(void)
{
fprintf(stderr, "usage: cplay [OPTIONS] filename\n"
"-D\tcard number\n"
"-d\tdevice node\n"
"-b\tbuffer size\n"
"-f\tfragments\n\n"
"-v\tverbose mode\n"
"-help\tPrints this help list\n\n"
"-t\tcodec type\n\n"
"1 : mp3"
"2 : aac"
"-p\tpause/resume with 2 secs sleep\n\n"
" [-num_intf num of interfaces followed by interface name]\n"
" [-i intf_name] : Can be multiple if num_intf is more than 1\n"
" [-dkv device_kv] : Can be multiple if num_intf is more than 1\n"
" [-dppkv deviceppkv] : Assign 0 if no device pp in the graph\n"
" [-ikv instance_kv] : Assign 0 if no instance kv in the graph\n"
" [-skv stream_kv]");
exit(EXIT_FAILURE);
}
void play_samples(char *name, unsigned int card, unsigned int device, unsigned int *device_kv,
unsigned int stream_kv, unsigned int instance_kv, unsigned int *devicepp_kv,
unsigned long buffer_size, unsigned int frag, unsigned int format,
int pause, char **intf_name, int intf_num);
struct mp3_header {
uint16_t sync;
uint8_t format1;
uint8_t format2;
};
#define ADTS_SYNC 0xF0FF
#define MP3_FORMAT 1
#define AAC_ADTS_FORMAT 2
struct adts_header {
uint32_t sync;
uint8_t format1;
uint8_t format2;
uint8_t format3;
};
uint aac_sample_rates[] = {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 48000, 48000, 48000};
int parse_aac_header(struct adts_header *header, unsigned int *num_channels,
unsigned int *sample_rate, unsigned int *protection_absent)
{
int profile, sample_rate_idx;
/* check sync bits */
if ((header->sync & 0xF0FF) != ADTS_SYNC) {
fprintf(stderr, "Error: Can't find sync word: 0x%X\n", header->sync);
return -1;
}
*protection_absent = (header->sync >> 8) & 0x01;
profile = ((header->sync >> 22) & 0x03) - 1;
sample_rate_idx = ((header->sync >> 18) & 0x0F);
// channel_idx = ((header->format1 >> 7) & 0x07);
// frame_length = ((header->format1 >> 14) & 0x1FFF);
*num_channels = 2;
*sample_rate = aac_sample_rates[sample_rate_idx];
// *size = frame_length - ((protection_absent == 1)? 7: 9));
if (verbose)
printf("%s: exit sr %d\n", __func__, *sample_rate);
return 0;
}
int parse_mp3_header(struct mp3_header *header, unsigned int *num_channels,
unsigned int *sample_rate, unsigned int *bit_rate)
{
int ver_idx, mp3_version, layer, bit_rate_idx, sample_rate_idx, channel_idx;
/* check sync bits */
if ((header->sync & MP3_SYNC) != MP3_SYNC) {
fprintf(stderr, "Error: Can't find sync word\n");
return -1;
}
ver_idx = (header->sync >> 11) & 0x03;
mp3_version = ver_idx == 0 ? MPEG25 : ((ver_idx & 0x1) ? MPEG1 : MPEG2);
layer = 4 - ((header->sync >> 9) & 0x03);
bit_rate_idx = ((header->format1 >> 4) & 0x0f);
sample_rate_idx = ((header->format1 >> 2) & 0x03);
channel_idx = ((header->format2 >> 6) & 0x03);
if (sample_rate_idx == 3 || layer == 4 || bit_rate_idx == 15) {
fprintf(stderr, "Error: Can't find valid header\n");
return -1;
}
*num_channels = (channel_idx == MONO ? 1 : 2);
*sample_rate = mp3_sample_rates[mp3_version][sample_rate_idx];
*bit_rate = (mp3_bit_rates[mp3_version][layer - 1][bit_rate_idx]) * 1000;
if (verbose)
printf("%s: exit\n", __func__);
return 0;
}
int check_codec_format_supported(unsigned int card, unsigned int device, struct snd_codec *codec)
{
if (is_codec_supported(card, device, COMPRESS_IN, codec) == false) {
fprintf(stderr, "Error: This codec or format is not supported by DSP\n");
return -1;
}
return 0;
}
static int print_time(struct compress *compress)
{
unsigned int avail;
struct timespec tstamp;
if (compress_get_hpointer(compress, &avail, &tstamp) != 0) {
fprintf(stderr, "Error querying timestamp\n");
fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
return -1;
} else
fprintf(stderr, "DSP played %ld.%ld\n", tstamp.tv_sec, tstamp.tv_nsec*1000);
return 0;
}
int main(int argc, char **argv)
{
char *file;
unsigned long buffer_size = 0;
char **intf_name = NULL;
int ret = 0, i = 0;
unsigned int card = 0, device = 0, frag = 0, audio_format = 0, pause = 0;
int intf_num = 1;
uint32_t dkv = SPEAKER;
uint32_t dppkv = DEVICEPP_RX_AUDIO_MBDRC;
unsigned int stream_kv = 0;
unsigned int instance_kv = INSTANCE_1;
unsigned int *device_kv = (unsigned int *) malloc(intf_num * sizeof(unsigned int));
unsigned int *devicepp_kv = (unsigned int *) malloc(intf_num * sizeof(unsigned int));
if (!device_kv || !devicepp_kv) {
printf(" insufficient memory\n");
return 1;
}
if (argc < 3) {
usage();
return 1;
}
device_kv[0] = dkv;
devicepp_kv[0] = dppkv;
file = argv[1];
/* parse command line arguments */
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
} else if (strcmp(*argv, "-t") == 0) {
argv++;
if (*argv)
audio_format = atoi(*argv);
} else if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
} else if (strcmp(*argv, "-p") == 0) {
argv++;
if (*argv)
pause = atoi(*argv);
} else if (strcmp(*argv, "-f") == 0) {
argv++;
if (*argv)
frag = atoi(*argv);
} else if (strcmp(*argv, "-v") == 0) {
argv++;
if (*argv)
verbose = atoi(*argv);
} else if (strcmp(*argv, "-num_intf") == 0) {
argv++;
if (*argv)
intf_num = atoi(*argv);
} else if (strcmp(*argv, "-i") == 0) {
intf_name = (char**) malloc(intf_num * sizeof(char*));
if (!intf_name) {
printf("insufficient memory\n");
return 1;
}
for (i = 0; i < intf_num ; i++){
argv++;
if (*argv)
intf_name[i] = *argv;
}
} else if (strcmp(*argv, "-dkv") == 0) {
device_kv = (unsigned int *) realloc(device_kv, intf_num * sizeof(unsigned int));
if (!device_kv) {
printf(" insufficient memory\n");
return 1;
}
for (i = 0; i < intf_num ; i++) {
argv++;
if (*argv) {
device_kv[i] = convert_char_to_hex(*argv);
}
}
} else if (strcmp(*argv, "-skv") == 0) {
argv++;
if (*argv)
stream_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-ikv") == 0) {
argv++;
if (*argv) {
instance_kv = atoi(*argv);
}
} else if (strcmp(*argv, "-dppkv") == 0) {
devicepp_kv = (unsigned int *) realloc(devicepp_kv, intf_num * sizeof(unsigned int));
if (!devicepp_kv) {
printf(" insufficient memory\n");
return 1;
}
for (i = 0; i < intf_num ; i++) {
devicepp_kv[i] = DEVICEPP_RX_AUDIO_MBDRC;
}
for (i = 0; i < intf_num ; i++)
{
argv++;
if(*argv) {
devicepp_kv[i] = convert_char_to_hex(*argv);
}
}
} else if (strcmp(*argv, "-help") == 0) {
usage();
}
if (*argv)
argv++;
}
if (intf_name == NULL)
return 1;
play_samples(file, card, device, device_kv, stream_kv, instance_kv, devicepp_kv,
buffer_size, frag, audio_format, pause,
intf_name, intf_num);
fprintf(stderr, "Finish Playing.... Close Normally\n");
if (device_kv)
free(device_kv);
if (devicepp_kv)
free(devicepp_kv);
if (intf_name)
free(intf_name);
exit(EXIT_SUCCESS);
}
void play_samples(char *name, unsigned int card, unsigned int device, unsigned int *device_kv,
unsigned int stream_kv, unsigned int instance_kv, unsigned int *devicepp_kv,
unsigned long buffer_size, unsigned int frag, unsigned int format,
int pause, char **intf_name, int intf_num)
{
struct compr_config config;
struct snd_codec codec;
struct compress *compress;
struct mp3_header header;
struct adts_header adts_header;
struct mixer *mixer;
FILE *file;
char *buffer;
int num_read, wrote;
unsigned int channels = 0, rate = 0, bits = 0;
struct device_config *dev_config = NULL;
struct group_config *grp_config = NULL;
int size, index, ret = 0;
uint32_t miid = 0;
dev_config = (struct device_config *) malloc(intf_num * sizeof(struct device_config));
if (!dev_config) {
printf("Failed to allocate memory for dev config");
return;
}
grp_config = (struct group_config *) malloc(intf_num * sizeof(struct group_config));
if (!grp_config) {
printf("Failed to allocate memory for group config");
return;
}
stream_kv = stream_kv ? stream_kv : COMPRESSED_OFFLOAD_PLAYBACK;
if (verbose)
printf("%s: entry\n", __func__);
file = fopen(name, "rb");
if (!file) {
fprintf(stderr, "Unable to open file '%s'\n", name);
exit(EXIT_FAILURE);
}
if (format == MP3_FORMAT) {
fread(&header, sizeof(header), 1, file);
if (parse_mp3_header(&header, &channels, &rate, &bits) == -1) {
fclose(file);
exit(EXIT_FAILURE);
}
codec.id = SND_AUDIOCODEC_MP3;
#ifdef SND_COMPRESS_DEC_HDR
} else if (format == AAC_ADTS_FORMAT) {
uint16_t protection_absent, crc;
fread(&adts_header, sizeof(adts_header), 1, file);
if (parse_aac_header(&adts_header, &channels, &rate, (unsigned int*)&protection_absent) == -1) {
fclose(file);
exit(EXIT_FAILURE);
}
if (!protection_absent) {
fread(&crc, 2, 1, file);
}
codec.id = SND_AUDIOCODEC_AAC;
codec.format = SND_AUDIOSTREAMFORMAT_MP4ADTS;
bits = 16;
codec.options.aac_dec.audio_obj_type = 29;
codec.options.aac_dec.pce_bits_size = 0;
#endif
} else {
printf("unknown format");
}
codec.ch_in = channels;
codec.ch_out = channels;
codec.sample_rate = rate;
if (!codec.sample_rate) {
fprintf(stderr, "invalid sample rate %d\n", rate);
fclose(file);
exit(EXIT_FAILURE);
}
codec.bit_rate = bits;
codec.rate_control = 0;
codec.profile = 0;
codec.level = 0;
codec.ch_mode = 0;
codec.format = 0;
if ((buffer_size != 0) && (frag != 0)) {
config.fragment_size = buffer_size/frag;
config.fragments = frag;
} else {
/* use driver defaults */
config.fragment_size = 0;
config.fragments = 0;
}
config.codec = &codec;
mixer = mixer_open(card);
if (!mixer) {
printf("Failed to open mixer\n");
goto FILE_EXIT;
}
for (index = 0; index < intf_num; index++) {
ret = get_device_media_config(BACKEND_CONF_FILE, intf_name[index], &dev_config[index]);
if (ret) {
printf("Invalid input, entry not found for : %s\n", intf_name[index]);
fclose(file);
}
printf("Backend %s rate ch bit : %d, %d, %d\n", intf_name[index],
dev_config[index].rate, dev_config[index].ch, dev_config[index].bits);
/* set device/audio_intf media config mixer control */
if (set_agm_device_media_config(mixer, intf_name[index], &dev_config[index])) {
printf("Failed to set device media config\n");
goto MIXER_EXIT;
}
/* set audio interface metadata mixer control */
if (set_agm_audio_intf_metadata(mixer, intf_name[index], device_kv[index], PLAYBACK,
dev_config[index].rate, dev_config[index].bits, stream_kv)) {
printf("Failed to set device metadata\n");
goto MIXER_EXIT;
}
}
/* set audio interface metadata mixer control */
if (set_agm_stream_metadata(mixer, device, stream_kv, PLAYBACK, STREAM_COMPRESS,
instance_kv)) {
printf("Failed to set stream metadata\n");
goto MIXER_EXIT;
}
/* Note: No common metadata as of now*/
for (index = 0; index < intf_num; index++) {
if (devicepp_kv[index] != 0) {
if (set_agm_streamdevice_metadata(mixer, device, stream_kv, PLAYBACK, STREAM_COMPRESS, intf_name[index],
devicepp_kv[index])) {
printf("Failed to set pcm metadata\n");
goto MIXER_EXIT;
}
}
/* connect stream to audio intf */
if (connect_agm_audio_intf_to_stream(mixer, device, intf_name[index], STREAM_COMPRESS, true)) {
printf("Failed to connect pcm to audio interface\n");
goto MIXER_EXIT;
}
ret = agm_mixer_get_miid (mixer, device, intf_name[index], STREAM_PCM, PER_STREAM_PER_DEVICE_MFC, &miid);
if (ret) {
printf("MFC not present for this graph\n");
} else {
if (configure_mfc(mixer, device, intf_name[index], PER_STREAM_PER_DEVICE_MFC,
STREAM_COMPRESS, dev_config[index].rate, dev_config[index].ch,
dev_config[index].bits, miid)) {
printf("Failed to configure pspd mfc\n");
goto MIXER_EXIT;
}
}
if (strstr(intf_name[index], "VIRT-")) {
if (get_group_device_info(BACKEND_CONF_FILE, intf_name[index], &grp_config[index]))
goto MIXER_EXIT;
if (set_agm_group_device_config(mixer, intf_name[index], &grp_config[index])) {
printf("Failed to set grp device config\n");
goto MIXER_EXIT;
}
}
}
compress = compress_open(card, device, COMPRESS_IN, &config);
if (!compress || !is_compress_ready(compress)) {
fprintf(stderr, "Unable to open Compress device %d:%d\n",
card, device);
fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
goto MIXER_EXIT;
};
if (verbose)
printf("%s: Opened compress device\n", __func__);
for (index = 0; index < intf_num; index++) {
if (strstr(intf_name[index], "VIRT-") || (device_kv[index] == SPEAKER) || (device_kv[index] == HANDSET)) {
if (set_agm_group_mux_config(mixer, device, &grp_config[index], intf_name[index], dev_config[index].ch)) {
printf("Failed to set grp device config\n");
}
}
}
size = config.fragment_size;
buffer = malloc(size * config.fragments);
if (!buffer) {
fprintf(stderr, "Unable to allocate %d bytes\n", size);
goto COMP_EXIT;
}
/* we will write frag fragment_size and then start */
num_read = fread(buffer, 1, size * config.fragments, file);
if (num_read > 0) {
if (verbose)
printf("%s: Doing first buffer write of %d\n", __func__, num_read);
wrote = compress_write(compress, buffer, num_read);
if (wrote < 0) {
fprintf(stderr, "Error %d playing sample\n", wrote);
fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
goto BUF_EXIT;
}
if (wrote != num_read) {
/* TODO: Buufer pointer needs to be set here */
fprintf(stderr, "We wrote %d, DSP accepted %d\n", num_read, wrote);
}
}
printf("Playing file %s On Card %u device %u, with buffer of %lu bytes\n",
name, card, device, buffer_size);
printf("Format %u Channels %u, %u Hz, Bit Rate %d\n",
SND_AUDIOCODEC_MP3, channels, rate, bits);
compress_start(compress);
if (verbose)
printf("%s: You should hear audio NOW!!!\n", __func__);
do {
num_read = fread(buffer, 1, size, file);
if (num_read > 0) {
wrote = compress_write(compress, buffer, num_read);
if (wrote < 0) {
fprintf(stderr, "Error playing sample\n");
fprintf(stderr, "ERR: %s\n", compress_get_error(compress));
goto BUF_EXIT;
}
if (wrote != num_read) {
/* TODO: Buffer pointer needs to be set here */
fprintf(stderr, "We wrote %d, DSP accepted %d\n", num_read, wrote);
}
if (verbose) {
print_time(compress);
printf("%s: wrote %d\n", __func__, wrote);
}
if (pause) {
printf("%s: pause \n", __func__);
compress_pause(compress);
sleep(2);
printf("%s: resume \n", __func__);
compress_resume(compress);
}
}
} while (num_read > 0);
if (verbose)
printf("%s: exit success\n", __func__);
/* issue drain if it supports */
compress_drain(compress);
/* disconnect stream to audio intf */
for(index = 0; index < intf_num; index++) {
connect_agm_audio_intf_to_stream(mixer, device, intf_name[index], STREAM_COMPRESS, false);
}
free(buffer);
fclose(file);
compress_close(compress);
return;
BUF_EXIT:
free(buffer);
COMP_EXIT:
compress_close(compress);
MIXER_EXIT:
if (dev_config)
free(dev_config);
if (grp_config)
free(grp_config);
mixer_close(mixer);
FILE_EXIT:
fclose(file);
if (verbose)
printf("%s: exit failure\n", __func__);
exit(EXIT_FAILURE);
}

View File

@@ -0,0 +1,392 @@
/*
** Copyright (c) 2019, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <tinyalsa/asoundlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <math.h>
#include <unistd.h>
#include <time.h>
#include "agmmixer.h"
static int close_f = 0;
void sigint_handler(int sig)
{
close_f = 1;
}
void play_loopback(unsigned int card, unsigned int p_device, unsigned int c_device, unsigned int channels,
unsigned int rate, enum pcm_format format, struct device_config *cap_config,
struct device_config *p_config, unsigned int period_size,
unsigned int period_count, unsigned int play_cap_time, char* capture_intf,
char* play_intf, unsigned int pdkv, unsigned int cdkv, unsigned int stream_kv,
unsigned int do_loopback);
void usage()
{
printf(" Usage: %s [-D card] [-P Hostless Playback device] [-C Hostless Capture device] [-p period_size]\n"
" [-n n_periods] [-c channels] [-r rate] [-b bits] [-T playback/capture time ]\n"
" [-i capture intf] [-o playback intf]\n"
" [-cdkv capture_device_kv] [-pdkv playback_device_kv] [-skv stream_kv]\n"
" Used to enable 'hostless' mode for audio devices with a DSP back-end.\n"
" Alternatively, specify '-l' for loopback mode: this program will read\n"
" from the capture device and write to the playback device.\n");
}
int main(int argc, char **argv)
{
unsigned int card = 100;
unsigned int p_device = 1;
unsigned int c_device = 0;
unsigned int period_size = 1024;
unsigned int period_count = 4;
unsigned int bits = 16;
unsigned int num_channels = 2;
unsigned int sample_rate = 48000;
unsigned int play_cap_time = 10;
char* c_intf_name = NULL;
char* p_intf_name = NULL;
unsigned int do_loopback = 0;
enum pcm_format format;
struct device_config capture_config;
struct device_config play_config;
unsigned int c_device_kv = 0;
unsigned int p_device_kv = 0;
unsigned int stream_kv = 0;
unsigned int ret = 0;
if (argc < 2) {
usage();
return 1;
}
/* parse command line arguments */
argv += 1;
while (*argv) {
if (strcmp(*argv, "-P") == 0) {
argv++;
if (*argv)
p_device = atoi(*argv);
} else if (strcmp(*argv, "-C") == 0) {
argv++;
if (*argv)
c_device = atoi(*argv);
} else if (strcmp(*argv, "-p") == 0) {
argv++;
if (*argv)
period_size = atoi(*argv);
} else if (strcmp(*argv, "-n") == 0) {
argv++;
if (*argv)
period_count = atoi(*argv);
} else if (strcmp(*argv, "-c") == 0) {
argv++;
if (*argv)
num_channels = atoi(*argv);
} else if (strcmp(*argv, "-r") == 0) {
argv++;
if (*argv)
sample_rate = atoi(*argv);
} else if (strcmp(*argv, "-b") == 0) {
argv++;
if (*argv)
bits = atoi(*argv);
} else if (strcmp(*argv, "-T") == 0) {
argv++;
if (*argv)
play_cap_time = atoi(*argv);
} else if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
} else if (strcmp(*argv, "-i") == 0) {
argv++;
if (*argv)
c_intf_name = *argv;
} else if (strcmp(*argv, "-o") == 0) {
argv++;
if (*argv)
p_intf_name = *argv;
} else if (strcmp(*argv, "-l") == 0) {
do_loopback = 1;
} else if (strcmp(*argv, "-skv") == 0) {
argv++;
if (*argv)
stream_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-cdkv") == 0) {
argv++;
if (*argv)
c_device_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-pdkv") == 0) {
argv++;
if (*argv)
p_device_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-help") == 0) {
usage();
}
if (*argv)
argv++;
}
ret = get_device_media_config(BACKEND_CONF_FILE, c_intf_name, &capture_config);
if (ret) {
printf("Invalid input, assigning default values for : %s\n", c_intf_name);
capture_config.rate = sample_rate;
capture_config.bits = bits;
capture_config.ch = num_channels;
}
ret = get_device_media_config(BACKEND_CONF_FILE, p_intf_name, &play_config);
if (ret) {
printf("Invalid input, assigning default values for : %s\n", p_intf_name);
play_config.rate = sample_rate;
play_config.bits = bits;
play_config.ch = num_channels;
}
switch (bits) {
case 32:
format = PCM_FORMAT_S32_LE;
break;
case 24:
format = PCM_FORMAT_S24_LE;
break;
case 16:
format = PCM_FORMAT_S16_LE;
break;
default:
printf("%u bits is not supported.\n", bits);
return 1;
}
play_loopback(card, p_device, c_device, num_channels, sample_rate, format, &capture_config, &play_config, period_size,
period_count, play_cap_time, c_intf_name, p_intf_name, p_device_kv,
c_device_kv, stream_kv, do_loopback);
return 0;
}
void play_loopback(unsigned int card, unsigned int p_device, unsigned int c_device, unsigned int channels,
unsigned int rate, enum pcm_format format, struct device_config *cap_config,
struct device_config *p_config, unsigned int period_size,
unsigned int period_count, unsigned int play_cap_time, char* capture_intf,
char* play_intf, unsigned int pdkv, unsigned int cdkv, unsigned int stream_kv,
unsigned int do_loopback)
{
struct pcm_config config;
struct pcm *p_pcm, *c_pcm;
struct mixer *mixer;
char *buffer = NULL;
char *p_intf_name = play_intf;
char *c_intf_name = capture_intf;
unsigned int size;
unsigned int bytes_read = 0;
unsigned int frames = 0, ret = 0, miid = 0;
struct timespec end;
struct timespec now;
struct group_config grp_config;
stream_kv = stream_kv ? stream_kv : PCM_RX_LOOPBACK;
if (!cap_config || !p_config || !capture_intf || !play_intf) {
printf("%s: %d: Invalid arguments.\n", __func__, __LINE__);
return;
}
memset(&config, 0, sizeof(config));
config.channels = channels;
config.rate = rate;
config.period_size = period_size;
config.period_count = period_count;
config.format = format;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;
mixer = mixer_open(card);
if (!mixer) {
printf("Failed to open mixer\n");
return;
}
/* set device/audio_intf media config mixer control */
if (set_agm_device_media_config(mixer, p_intf_name, p_config)) {
printf("Failed to set playback device media config\n");
goto err_close_mixer;
}
if (set_agm_device_media_config(mixer, c_intf_name, cap_config)) {
printf("Failed to set capture device media config\n");
goto err_close_mixer;
}
/* set audio interface metadata mixer control */
if (set_agm_audio_intf_metadata(mixer, c_intf_name, cdkv, CAPTURE, rate, cap_config->bits, stream_kv)) {
printf("Failed to set capture device metadata\n");
goto err_close_mixer;
}
if (set_agm_audio_intf_metadata(mixer, p_intf_name, pdkv, PLAYBACK, rate, p_config->bits, stream_kv)) {
printf("Failed to set playback device metadata\n");
goto err_close_mixer;
}
if (set_agm_stream_metadata(mixer, p_device, stream_kv, LOOPBACK, STREAM_PCM,
0)) {
printf("Failed to capture stream metadata\n");
goto err_close_mixer;
}
/* Note: No common metadata as of now*/
/* connect pcm stream to audio intf */
if (connect_agm_audio_intf_to_stream(mixer, p_device, p_intf_name, STREAM_PCM, true)) {
printf("Failed to connect playback pcm to audio interface\n");
goto err_close_mixer;
}
if (connect_agm_audio_intf_to_stream(mixer, c_device, c_intf_name, STREAM_PCM, true)) {
printf("Failed to connect capture pcm to audio interface\n");
connect_agm_audio_intf_to_stream(mixer, p_device, p_intf_name, STREAM_PCM, false);
goto err_close_mixer;
}
if (connect_play_pcm_to_cap_pcm(mixer, p_device, c_device)) {
printf("Failed to connect capture pcm to audio interface\n");
goto err_disconnect;
}
if (strstr(p_intf_name, "VIRT-")) {
if (get_group_device_info(BACKEND_CONF_FILE, p_intf_name, &grp_config))
goto err_disconnect;
if (set_agm_group_device_config(mixer, p_intf_name, &grp_config)) {
printf("Failed to set grp device config\n");
goto err_disconnect;
}
}
ret = agm_mixer_get_miid (mixer, p_device, p_intf_name, STREAM_PCM, PER_STREAM_PER_DEVICE_MFC, &miid);
if (ret) {
printf("MFC not present for this graph\n");
} else {
if (configure_mfc(mixer, p_device, p_intf_name, PER_STREAM_PER_DEVICE_MFC,
STREAM_PCM, p_config->rate, p_config->ch,
p_config->bits, miid)) {
printf("Failed to configure pspd mfc\n");
goto err_disconnect;
}
}
p_pcm = pcm_open(card, p_device, PCM_OUT, &config);
if (!p_pcm || !pcm_is_ready(p_pcm)) {
printf("Unable to open playback PCM device (%s)\n",
pcm_get_error(p_pcm));
goto err_disconnect;
}
if (strstr(p_intf_name, "VIRT-") || (pdkv == SPEAKER) || (pdkv == HANDSET)) {
if (set_agm_group_mux_config(mixer, p_device, &grp_config, p_intf_name, p_config->ch)) {
printf("Failed to set mux config\n");
goto err_close_p_pcm;
}
}
if (pcm_start(p_pcm) < 0) {
printf("start error");
goto err_close_p_pcm;
}
c_pcm = pcm_open(card, c_device, PCM_IN, &config);
if (!c_pcm || !pcm_is_ready(c_pcm)) {
printf("Unable to open playback PCM device (%s)\n",
pcm_get_error(c_pcm));
goto err_close_p_pcm;
}
if (pcm_start(c_pcm) < 0) {
printf("start error");
goto err_close_c_pcm;
}
if (do_loopback) {
size = pcm_frames_to_bytes(c_pcm, pcm_get_buffer_size(c_pcm));
buffer = malloc(size);
if (!buffer) {
printf("Unable to allocate %d bytes\n", size);
goto err_close_c_pcm;
}
}
clock_gettime(CLOCK_MONOTONIC, &end);
end.tv_sec += play_cap_time;
while (1) {
if (close_f)
break;
if (do_loopback) {
if (pcm_read(c_pcm, buffer, size)) {
printf("Unable to read from PCM capture device %u (%s)\n",
c_device, pcm_get_error(c_pcm));
break;
}
if (pcm_write(p_pcm, buffer, size)) {
printf("Unable to write to PCM playback device %u (%s)\n",
p_device, pcm_get_error(p_pcm));
break;
}
} else {
usleep(100000);
}
if (play_cap_time) {
clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec > end.tv_sec ||
(now.tv_sec == end.tv_sec && now.tv_nsec >= end.tv_nsec))
break;
}
}
connect_play_pcm_to_cap_pcm(mixer, -1, c_device);
err_close_c_pcm:
pcm_close(c_pcm);
err_close_p_pcm:
if (buffer)
free(buffer);
pcm_close(p_pcm);
err_disconnect:
connect_agm_audio_intf_to_stream(mixer, p_device, p_intf_name, STREAM_PCM, false);
connect_agm_audio_intf_to_stream(mixer, c_device, c_intf_name, STREAM_PCM, false);
err_close_mixer:
mixer_close(mixer);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
/*
** Copyright (c) 2019, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#ifndef __AGM_COMMON_H__
#define __AGM_COMMON_H__
#include <tinyalsa/asoundlib.h>
#include <kvh2xml.h>
enum usecase_type{
PLAYBACK,
CAPTURE,
LOOPBACK,
HAPTICS,
};
enum stream_type {
STREAM_PCM,
STREAM_COMPRESS,
};
struct device_config {
char name[80];
unsigned int rate;
unsigned int ch;
unsigned int bits;
enum pcm_format format;
};
struct usbAudioConfig {
uint32_t usb_token;
uint32_t svc_interval;
};
struct group_config {
char name[80];
unsigned int rate;
unsigned int ch;
unsigned int bits;
unsigned int slot_mask;
enum pcm_format format;
};
typedef enum {
SLOT_MASK1 = 1,
SLOT_MASK3 = 3,
SLOT_MASK7 = 7,
SLOT_MASK15 = 15,
}slot_mask_t;
#if defined(__cplusplus)
extern "C" {
#endif
int convert_char_to_hex(char *char_num);
int get_pcm_bit_width(enum pcm_format fmt_id);
int set_agm_device_media_config(struct mixer *mixer, char *intf_name, struct device_config *config);
int set_agm_device_custom_payload(struct mixer *mixer, char *intf_name, void *payload, size_t size);
void get_agm_usb_audio_config_payload(uint8_t** payload, size_t* size, uint32_t miid, struct usbAudioConfig *data);
int set_agm_group_device_config(struct mixer *mixer, char *intf_name, struct group_config *config);
int set_agm_group_mux_config(struct mixer *mixer, unsigned int device, struct group_config *config, char *intf_name, unsigned int channels);
int connect_play_pcm_to_cap_pcm(struct mixer *mixer, unsigned int p_device, unsigned int c_device);
int set_agm_audio_intf_metadata(struct mixer *mixer, char *intf_name, unsigned int dkv, enum usecase_type, int rate, int bitwidth, uint32_t val);
int set_agm_stream_metadata_type(struct mixer *mixer, int device, char *val, enum stream_type stype);
int set_agm_streamdevice_metadata(struct mixer *mixer, int device, uint32_t val, enum usecase_type usecase, enum stream_type stype,
char *intf_name, unsigned int devicepp_kv);
int set_agm_stream_metadata(struct mixer *mixer, int device, uint32_t val, enum usecase_type utype, enum stream_type stype,
unsigned int instance_kv);
int set_agm_capture_stream_metadata(struct mixer *mixer, int device, uint32_t val, enum usecase_type utype, enum stream_type stype,
unsigned int instance_kv);
int connect_agm_audio_intf_to_stream(struct mixer *mixer, unsigned int device,
char *intf_name, enum stream_type, bool connect);
int agm_mixer_register_event(struct mixer *mixer, int device, enum stream_type stype, uint32_t miid, uint8_t is_register);
int agm_mixer_get_miid(struct mixer *mixer, int device, char *intf_name, enum stream_type stype, int tag_id, uint32_t *miid);
int agm_mixer_set_param(struct mixer *mixer, int device,
enum stream_type stype, void *payload, int size);
int agm_mixer_set_param_with_file(struct mixer *mixer, int device,
enum stream_type stype, char *path);
int agm_mixer_set_ecref_path(struct mixer *mixer, unsigned int device, enum stream_type stype, char *intf_name);
int agm_mixer_get_event_param(struct mixer *mixer, int device, enum stream_type stype,uint32_t miid);
int agm_mixer_get_buf_tstamp(struct mixer *mixer, int device, enum stream_type stype, uint64_t *tstamp);
int get_device_media_config(char* filename, char *intf_name, struct device_config *config);
int get_group_device_info(char* filename, char *intf_name, struct group_config *config);
int configure_mfc(struct mixer *mixer, int device, char *intf_name, int tag, enum stream_type stype, unsigned int rate,
unsigned int channels, unsigned int bits, uint32_t miid);
#if defined(__cplusplus)
} /* extern "C" */
#endif
#endif

View File

@@ -0,0 +1,516 @@
/*
** Copyright (c) 2019, 2021, The Linux Foundation. All rights reserved.
**
** Copyright 2011, The Android Open Source Project
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of The Android Open Source Project nor the names of
** its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY The Android Open Source Project ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL The Android Open Source Project BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
** DAMAGE.
**
** Changes from Qualcomm Innovation Center, Inc. are provided under the following license:
** Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <errno.h>
#include <tinyalsa/asoundlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <signal.h>
#include <agm/agm_api.h>
#include "agmmixer.h"
#define ID_RIFF 0x46464952
#define ID_WAVE 0x45564157
#define ID_FMT 0x20746d66
#define ID_DATA 0x61746164
struct riff_wave_header {
uint32_t riff_id;
uint32_t riff_sz;
uint32_t wave_id;
};
struct chunk_header {
uint32_t id;
uint32_t sz;
};
struct chunk_fmt {
uint16_t audio_format;
uint16_t num_channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
};
static int close = 0;
void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int usb_device,
unsigned int channels, unsigned int rate, unsigned int bits,
unsigned int *device_kv, unsigned int stream_kv, unsigned int instance_kv,
unsigned int *devicepp_kv, struct chunk_fmt fmt, bool haptics, char **intf_name,
int intf_num, bool is_24_LE);
void stream_close(int sig)
{
/* allow the stream to be closed gracefully */
signal(sig, SIG_IGN);
close = 1;
}
static void usage(void)
{
printf(" Usage: %s file.wav [-help print usage] [-D card] [-d device]\n"
" [-c channels] [-r rate] [-b bits]\n"
" [-num_intf num of interfaces followed by interface name]\n"
" [-i intf_name] : Can be multiple if num_intf is more than 1\n"
" [-dkv device_kv] : Can be multiple if num_intf is more than 1\n"
" [-dppkv deviceppkv] : Assign 0 if no device pp in the graph\n"
" [-ikv instance_kv] : Assign 0 if no instance kv in the graph\n"
" [-skv stream_kv] [-h haptics usecase]\n"
" [is_24_LE] : [0-1] Only to be used if user wants to play S24_LE clip\n"
" [-usb_d usb device]\n"
" 0: If clip bps is 32, and format is S32_LE\n"
" 1: If clip bps is 24, and format is S24_LE\n");
}
int main(int argc, char **argv)
{
FILE *file;
struct riff_wave_header riff_wave_header;
struct chunk_header chunk_header;
struct chunk_fmt chunk_fmt;
unsigned int card = 100, device = 100, i=0;
unsigned int usb_device = 1;
unsigned int channels = 2;
unsigned int rate = 48000;
unsigned int bits = 16;
int intf_num = 1;
uint32_t dkv = SPEAKER;
uint32_t dppkv = DEVICEPP_RX_AUDIO_MBDRC;
unsigned int stream_kv = 0;
unsigned int instance_kv = INSTANCE_1;
bool haptics = false;
char **intf_name = NULL;
char *filename;
int more_chunks = 1, ret = 0;
bool is_24_LE = false;
unsigned int *devicepp_kv = (unsigned int *) malloc(intf_num * sizeof(unsigned int));
unsigned int *device_kv = (unsigned int *) malloc(intf_num * sizeof(unsigned int));
if (!device_kv || !devicepp_kv) {
printf(" insufficient memory\n");
return 1;
}
if (argc < 3) {
usage();
return 1;
}
device_kv[0] = dkv;
devicepp_kv[0] = dppkv;
filename = argv[1];
file = fopen(filename, "rb");
if (!file) {
printf("Unable to open file '%s'\n", filename);
return 1;
}
fread(&riff_wave_header, sizeof(riff_wave_header), 1, file);
if ((riff_wave_header.riff_id != ID_RIFF) ||
(riff_wave_header.wave_id != ID_WAVE)) {
printf("Error: '%s' is not a riff/wave file\n", filename);
fclose(file);
return 1;
}
do {
fread(&chunk_header, sizeof(chunk_header), 1, file);
switch (chunk_header.id) {
case ID_FMT:
fread(&chunk_fmt, sizeof(chunk_fmt), 1, file);
/* If the format header is larger, skip the rest */
if (chunk_header.sz > sizeof(chunk_fmt))
fseek(file, chunk_header.sz - sizeof(chunk_fmt), SEEK_CUR);
break;
case ID_DATA:
/* Stop looking for chunks */
more_chunks = 0;
break;
default:
/* Unknown chunk, skip bytes */
fseek(file, chunk_header.sz, SEEK_CUR);
}
} while (more_chunks);
/* parse command line arguments */
argv += 2;
while (*argv) {
if (strcmp(*argv, "-d") == 0) {
argv++;
if (*argv)
device = atoi(*argv);
} else if (strcmp(*argv, "-c") == 0) {
argv++;
if (*argv)
channels = atoi(*argv);
} else if (strcmp(*argv, "-r") == 0) {
argv++;
if (*argv)
rate = atoi(*argv);
} else if (strcmp(*argv, "-b") == 0) {
argv++;
if (*argv)
bits = atoi(*argv);
} else if (strcmp(*argv, "-D") == 0) {
argv++;
if (*argv)
card = atoi(*argv);
} else if (strcmp(*argv, "-num_intf") == 0) {
argv++;
if (*argv)
intf_num = atoi(*argv);
} else if (strcmp(*argv, "-i") == 0) {
intf_name = (char**) malloc(intf_num * sizeof(char*));
if (!intf_name) {
printf("insufficient memory\n");
return 1;
}
for (i = 0; i < intf_num ; i++){
argv++;
if (*argv)
intf_name[i] = *argv;
}
} else if (strcmp(*argv, "-h") == 0) {
argv++;
if (*argv)
haptics = *argv;
} else if (strcmp(*argv, "-dkv") == 0) {
device_kv = (unsigned int *) realloc(device_kv, intf_num * sizeof(unsigned int));
if (!device_kv) {
printf(" insufficient memory\n");
return 1;
}
for (i = 0; i < intf_num ; i++) {
argv++;
if (*argv) {
device_kv[i] = convert_char_to_hex(*argv);
}
}
} else if (strcmp(*argv, "-skv") == 0) {
argv++;
if (*argv)
stream_kv = convert_char_to_hex(*argv);
} else if (strcmp(*argv, "-ikv") == 0) {
argv++;
if (*argv) {
instance_kv = atoi(*argv);
}
} else if (strcmp(*argv, "-dppkv") == 0) {
devicepp_kv = (unsigned int *) realloc(devicepp_kv, intf_num * sizeof(unsigned int));
if (!devicepp_kv) {
printf(" insufficient memory\n");
return 1;
}
for (i = 0; i < intf_num ; i++) {
devicepp_kv[i] = DEVICEPP_RX_AUDIO_MBDRC;
}
for (i = 0; i < intf_num ; i++)
{
argv++;
if (*argv) {
devicepp_kv[i] = convert_char_to_hex(*argv);
}
}
} else if (strcmp(*argv, "-is_24_LE") == 0) {
argv++;
if (*argv) {
is_24_LE = atoi(*argv);
}
} else if (strcmp(*argv, "-usb_d") == 0) {
argv++;
if (*argv)
usb_device = atoi(*argv);
} else if (strcmp(*argv, "-help") == 0) {
usage();
}
if (*argv)
argv++;
}
if (intf_name == NULL)
return 1;
play_sample(file, card, device, usb_device, channels, rate, bits, device_kv, stream_kv,
instance_kv, devicepp_kv, chunk_fmt, haptics, intf_name, intf_num, is_24_LE);
fclose(file);
if (device_kv)
free(device_kv);
if (devicepp_kv)
free(devicepp_kv);
if (intf_name)
free(intf_name);
return 0;
}
void play_sample(FILE *file, unsigned int card, unsigned int device, unsigned int usb_device,
unsigned int channels, unsigned int rate, unsigned int bits,
unsigned int *device_kv, unsigned int stream_kv, unsigned int instance_kv,
unsigned int *devicepp_kv, struct chunk_fmt fmt, bool haptics, char **intf_name,
int intf_num, bool is_24_LE)
{
struct pcm_config config;
struct pcm *pcm;
struct mixer *mixer;
char *buffer;
int playback_path, playback_value;
int size, index, ret = 0;
int num_read;
struct group_config *grp_config = NULL;
struct device_config *dev_config = NULL;
uint32_t miid = 0;
struct usbAudioConfig cfg;
uint8_t* payload = NULL;
size_t payloadSize = 0;
grp_config = (struct group_config *) malloc(intf_num * sizeof(struct group_config));
if (!grp_config) {
printf("Failed to allocate memory for group config");
return;
}
dev_config = (struct device_config *) malloc(intf_num * sizeof(struct device_config));
if (!dev_config) {
printf("Failed to allocate memory for dev config");
return;
}
memset(&config, 0, sizeof(config));
config.channels = fmt.num_channels;
config.rate = fmt.sample_rate;
config.period_size = 1024;
config.period_count = 4;
if (fmt.bits_per_sample == 32) {
if (is_24_LE)
config.format = PCM_FORMAT_S24_LE;
else
config.format = PCM_FORMAT_S32_LE;
} else if (fmt.bits_per_sample == 24)
config.format = PCM_FORMAT_S24_3LE;
else if (fmt.bits_per_sample == 16)
config.format = PCM_FORMAT_S16_LE;
config.start_threshold = 0;
config.stop_threshold = 0;
config.silence_threshold = 0;
mixer = mixer_open(card);
if (!mixer) {
printf("Failed to open mixer\n");
return;
}
if (haptics) {
playback_path = HAPTICS;
stream_kv = stream_kv ? stream_kv : HAPTICS_PLAYBACK;
} else {
playback_path = PLAYBACK;
stream_kv = stream_kv ? stream_kv : PCM_LL_PLAYBACK;
}
for (index = 0; index < intf_num; index++) {
if(intf_name[index] != NULL && strcmp(intf_name[index], "USB_AUDIO-RX") == 0) {
dev_config[index].rate = rate;
dev_config[index].ch = channels;
dev_config[index].bits = bits;
dev_config[index].format = PCM_FORMAT_INVALID;
} else {
ret = get_device_media_config(BACKEND_CONF_FILE, intf_name[index], &dev_config[index]);
if (ret) {
printf("Invalid input, entry not found for : %s\n", intf_name[index]);
fclose(file);
}
if (dev_config[index].format != PCM_FORMAT_INVALID) {
printf("Valid format from backend_conf %d\n", dev_config[index].format);
/* Updating bitwitdh based on format to avoid mismatch between bitwidth
* and format, as device bw will be used to configure MFC.
*/
dev_config[index].bits = get_pcm_bit_width(dev_config[index].format);
}
}
printf("Backend %s rate ch bit fmt : %d, %d, %d %d\n", intf_name[index],
dev_config[index].rate, dev_config[index].ch, dev_config[index].bits,
dev_config[index].format);
/* set device/audio_intf media config mixer control */
if (set_agm_device_media_config(mixer, intf_name[index], &dev_config[index])) {
printf("Failed to set device media config\n");
goto err_close_mixer;
}
/* set audio interface metadata mixer control */
if (set_agm_audio_intf_metadata(mixer, intf_name[index], device_kv[index], playback_path,
dev_config[index].rate, dev_config[index].bits, stream_kv)) {
printf("Failed to set device metadata\n");
goto err_close_mixer;
}
}
/* set audio interface metadata mixer control */
if (set_agm_stream_metadata(mixer, device, stream_kv, PLAYBACK, STREAM_PCM,
instance_kv)) {
printf("Failed to set pcm metadata\n");
goto err_close_mixer;
}
/* Note: No common metadata as of now*/
for (index = 0; index < intf_num; index++) {
if (devicepp_kv[index] != 0) {
if (set_agm_streamdevice_metadata(mixer, device, stream_kv, PLAYBACK, STREAM_PCM, intf_name[index],
devicepp_kv[index])) {
printf("Failed to set pcm metadata\n");
goto err_close_mixer;
}
}
if (intf_name[index] != NULL && strcmp(intf_name[index], "USB_AUDIO-RX") == 0) {
ret = agm_mixer_get_miid (mixer, device, intf_name[index], STREAM_PCM, DEVICE_HW_ENDPOINT_RX, &miid);
if (ret == 0) {
cfg.usb_token = usb_device << 16;
cfg.svc_interval = 0;
get_agm_usb_audio_config_payload(&payload, &payloadSize, miid, &cfg);
if (payloadSize) {
ret = set_agm_device_custom_payload(mixer, intf_name[index], payload, payloadSize);
} else {
ret = -1;
printf("set_agm_device_custom_payload failed\n");
goto err_close_mixer;
}
} else {
printf("Failed to get miid for USB_AUDIO-TX\n");
goto err_close_mixer;
}
}
/* connect pcm stream to audio intf */
if (connect_agm_audio_intf_to_stream(mixer, device, intf_name[index], STREAM_PCM, true)) {
printf("Failed to connect pcm to audio interface\n");
goto err_close_mixer;
}
ret = agm_mixer_get_miid (mixer, device, intf_name[index], STREAM_PCM, PER_STREAM_PER_DEVICE_MFC, &miid);
if (ret) {
printf("MFC not present for this graph\n");
} else {
if (configure_mfc(mixer, device, intf_name[index], PER_STREAM_PER_DEVICE_MFC,
STREAM_PCM, dev_config[index].rate, dev_config[index].ch,
dev_config[index].bits, miid)) {
printf("Failed to configure pspd mfc\n");
goto err_close_mixer;
}
}
if (strstr(intf_name[index], "VIRT-")) {
if (get_group_device_info(BACKEND_CONF_FILE, intf_name[index], &grp_config[index]))
goto err_close_mixer;
if (set_agm_group_device_config(mixer, intf_name[index], &grp_config[index])) {
printf("Failed to set grp device config\n");
goto err_close_mixer;
}
}
}
pcm = pcm_open(card, device, PCM_OUT, &config);
if (!pcm || !pcm_is_ready(pcm)) {
printf("Unable to open PCM device %u (%s)\n",
device, pcm_get_error(pcm));
goto err_close_mixer;
}
for (index = 0; index < intf_num; index++) {
if (strstr(intf_name[index], "VIRT-") || (device_kv[index] == SPEAKER) || (device_kv[index] == HANDSET)) {
if (set_agm_group_mux_config(mixer, device, &grp_config[index], intf_name[index], dev_config[index].ch)) {
printf("Failed to set grp device config\n");
}
}
}
size = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
buffer = malloc(size);
if (!buffer) {
printf("Unable to allocate %d bytes\n", size);
goto err_close_pcm;
}
printf("Playing sample: %u ch, %u hz, %u bit\n", fmt.num_channels,
fmt.sample_rate, fmt.bits_per_sample);
if (pcm_start(pcm) < 0) {
printf("start error\n");
goto err_close_pcm;
}
/* catch ctrl-c to shutdown cleanly */
signal(SIGINT, stream_close);
do {
num_read = fread(buffer, 1, size, file);
if (num_read > 0) {
if (pcm_write(pcm, buffer, num_read)) {
printf("Error playing sample\n");
break;
}
}
} while (!close && num_read > 0);
pcm_stop(pcm);
/* disconnect pcm stream to audio intf */
for (index = 0; index < intf_num; index++) {
connect_agm_audio_intf_to_stream(mixer, device, intf_name[index], STREAM_PCM, false);
}
free(buffer);
err_close_pcm:
pcm_close(pcm);
err_close_mixer:
if (payload) {
free(payload);
}
if (dev_config)
free(dev_config);
if (grp_config)
free(grp_config);
mixer_close(mixer);
}

View File

@@ -0,0 +1,10 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: agmtest
Description: agm test tool
Version: @VERSION@
Libs: -L${libdir}
Cflags: -I${includedir}/agmtest

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. -->
<!-- -->
<!-- Redistribution and use in source and binary forms, with or without -->
<!-- modification, are permitted provided that the following conditions are -->
<!-- met: -->
<!-- * Redistributions of source code must retain the above copyright -->
<!-- notice, this list of conditions and the following disclaimer. -->
<!-- * Redistributions in binary form must reproduce the above -->
<!-- copyright notice, this list of conditions and the following -->
<!-- disclaimer in the documentation and/or other materials provided -->
<!-- with the distribution. -->
<!-- * Neither the name of The Linux Foundation nor the names of its -->
<!-- contributors may be used to endorse or promote products derived -->
<!-- from this software without specific prior written permission. -->
<!-- -->
<!-- THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED -->
<!-- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -->
<!-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT -->
<!-- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS -->
<!-- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -->
<!-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -->
<!-- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -->
<!-- BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -->
<!-- WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -->
<!-- OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -->
<!-- IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -->
<!-- -->
<!-- Changes from Qualcomm Innovation Center are provided under the following license: -->
<!-- Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. -->
<!-- SPDX-License-Identifier: BSD-3-Clause-Clear -->
<config>
<group_device name="TDM-LPAIF-RX-TERTIARY" rate="96000" ch="3" bits="16" slot_mask="7"/>
<device name="TDM-LPAIF-RX-TERTIARY-VIRT-0" rate="96000" ch="2" bits="16" />
<!-- Format support per backend is optional, we can provide pcm format to specify which format -->
<!-- BE should be configured to. If format is provided, and bitwidth does not match the format,-->
<!-- bitwidth will be overwritten based on format. -->
<device name="CODEC_DMA-LPAIF_WSA-RX-0" rate="48000" ch="1" bits="16" format="PCM_FORMAT_S16_LE"/>
<device name="CODEC_DMA-LPAIF_WSA-RX-1" rate="48000" ch="1" bits="16" />
<device name="CODEC_DMA-LPAIF_RXTX-RX-0" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_AXI-RX-PRIMARY" rate="48000" ch="2" bits="16" />
<device name="SLIM-DEV1-RX-0" rate="48000" ch="2" bits="16" />
<device name="DISPLAY_PORT-RX" rate="48000" ch="2" bits="16" />
<device name="USB_AUDIO-RX" rate="48000" ch="2" bits="16" />
<device name="CODEC_DMA-LPAIF_RXTX-TX-3" rate="48000" ch="1" bits="16" />
<device name="CODEC_DMA-LPAIF_VA-TX-0" rate="48000" ch="1" bits="16" />
<device name="CODEC_DMA-LPAIF_VA-TX-1" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_AXI-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="SLIM-DEV1-TX-0" rate="48000" ch="1" bits="16" />
<device name="USB_AUDIO-TX" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF-RX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_AUD-RX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_AUD-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF-RX-TERTIARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF-TX-TERTIARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF-RX-SECONDARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF-TX-SECONDARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_AUD-RX-SECONDARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_AUD-TX-SECONDARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_RXTX-RX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_RXTX-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_RXTX-RX-0" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_VA-RX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_VA-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_WSA-RX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="MI2S-LPAIF_WSA-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF-RX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF-RX-SECONDARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF-TX-SECONDARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_AUD-RX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_AUD-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_AUD-RX-SECONDARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_AUD-TX-SECONDARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF-RX-TERTIARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF-TX-TERTIARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_RXTX-RX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_RXTX-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_VA-RX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_VA-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_WSA-RX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_WSA-TX-PRIMARY" rate="48000" ch="1" bits="16" />
<device name="TDM-LPAIF_AXI-TX-PRIMARY" rate="48000" ch="2" bits="16" />
<device name="AUXPCM-LPAIF_AXI-TX-PRIMARY" rate="48000" ch="2" bits="16" />
<device name="MI2S-LPAIF_AXI-RX-PRIMARY" rate="48000" ch="2" bits="16" />
<device name="AUXPCM-LPAIF_AXI-RX-PRIMARY" rate="48000" ch="2" bits="16" />
</config>

View File

@@ -0,0 +1,53 @@
# Requires autoconf tool later than 2.61
AC_PREREQ(2.69)
# Initialize the agm plugin package version 1.0.0
AC_INIT([agmtest],1.0.0)
# Does not strictly follow GNU Coding standards
AM_INIT_AUTOMAKE([foreign subdir-objects])
# Disables auto rebuilding of configure, Makefile.ins
AM_MAINTAINER_MODE
# Verifies the --srcdir is correct by checking for the path
#AC_CONFIG_SRCDIR([test/agmplay.c])
# defines some macros variable to be included by source
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CXX
AC_PROG_LIBTOOL
AC_PROG_AWK
AC_PROG_CPP
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
PKG_PROG_PKG_CONFIG
AC_ARG_WITH([glib],
AC_HELP_STRING([--with-glib],
[enable glib, Build against glib. Use this when building for HLOS systems which use glib]))
if (test "x${with_glib}" = "xyes"); then
PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.16, dummy=yes,
AC_MSG_ERROR(GThread >= 2.16 is required))
PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, dummy=yes,
AC_MSG_ERROR(GLib >= 2.16 is required))
GLIB_CFLAGS="$GLIB_CFLAGS $GTHREAD_CFLAGS"
GLIB_LIBS="$GLIB_LIBS $GTHREAD_LIBS"
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
fi
AM_CONDITIONAL(USE_GLIB, test "x${with_glib}" = "xyes")
# Checks for libraries
PKG_CHECK_MODULES([MMHEADERS], [mm-audio-headers])
AC_SUBST([MMHEADERS_CFLAGS])
AC_ARG_WITH([openwrt],
AS_HELP_STRING([use openwrt (default is no)]),
[with_openwrt=$withval],
[with_openwrt=no])
AM_CONDITIONAL([BUILDSYSTEM_OPENWRT], [test "x${with_openwrt}" = "xyes"])
AC_CONFIG_FILES([ Makefile agmtest.pc ])
AC_OUTPUT

View File

@@ -0,0 +1,36 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := gtest_agm_test
LOCAL_MODULE_OWNER := qti
LOCAL_MODULE_TAGS := optional
LOCAL_VENDOR_MODULE := true
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-result
LOCAL_CFLAGS += -DBACKEND_CONF_FILE=\"/vendor/etc/backend_conf.xml\"
LOCAL_SRC_FILES := gtest_agm_test.cpp \
../AgmPlayer.cpp \
../RiffWaveParser.cpp \
../ChunkParser.cpp \
../PlaybackCommand.cpp \
../PlaybackCommandParser.cpp
LOCAL_HEADER_LIBRARIES := \
libagm_headers \
libacdb_headers
#if android version is R, refer to qtitinyxx otherwise use upstream ones
#This assumes we would be using AR code only for Android R and subsequent versions.
ifneq ($(filter 11 R, $(PLATFORM_VERSION)),)
LOCAL_SHARED_LIBRARIES += libqti-tinyalsa
else
LOCAL_SHARED_LIBRARIES += libtinyalsa
endif
LOCAL_SHARED_LIBRARIES += \
libagmmixer liblog libcutils libutils
LOCAL_STATIC_LIBRARIES := libgmock libgtest libgtest_main
include $(BUILD_EXECUTABLE)

View File

@@ -0,0 +1,669 @@
/*
** Copyright (c) 2024, The Linux Foundation. All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above
** copyright notice, this list of conditions and the following
** disclaimer in the documentation and/or other materials provided
** with the distribution.
** * Neither the name of The Linux Foundation nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
** Changes from Qualcomm Innovation Center are provided under the following license:
** Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
** SPDX-License-Identifier: BSD-3-Clause-Clear
*/
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <iostream>
#include "../AgmPlayer.h"
#include "../PlaybackCommandParser.h"
#include "../MockAgmMixerWrapper.h"
#include "../MockAgmPcmWrapper.h"
class ParserTest : public ::testing::Test {
protected:
void SetUp() override {
riffWaveParser = new RiffWaveParser();
chunkParser = new ChunkParser();
}
void TearDown() override {
delete riffWaveParser;
delete chunkParser;
}
std::ifstream file;
HeaderParser* riffWaveParser;
HeaderParser* chunkParser;
};
TEST_F(ParserTest, RiffWavParserIsValidFail) {
file.open("/sdcard/sample.m4a", std::ios::binary);
if (!file) {
std::cout << "please, push the sample.m4a file in /sdcard/ folder" << std::endl;
exit(1);
}
riffWaveParser->parseHeader(file);
EXPECT_FALSE(riffWaveParser->isValid());
file.close();
}
TEST_F(ParserTest, RiffWavParserIsValidSuccess) {
file.open("/sdcard/sample.wav", std::ios::binary);
if (!file) {
std::cout << "please, push the sample.wav file in /sdcard/ folder" << std::endl;
exit(1);
}
riffWaveParser->parseHeader(file);
EXPECT_TRUE(riffWaveParser->isValid());
file.close();
}
TEST_F(ParserTest, ChunkParserParseHeaderSuccess) {
ChunkFormat chunkFormat;
file.open("/sdcard/sample.wav", std::ios::binary);
if (!file) {
std::cout << "please, push the sample.wav file in /sdcard/ folder" << std::endl;
exit(1);
}
riffWaveParser->parseHeader(file);
EXPECT_EQ(true, riffWaveParser->isValid());
chunkParser->parseHeader(file);
chunkFormat = chunkParser->getFormat();
EXPECT_EQ(1, chunkFormat.audio_format);
EXPECT_EQ(2, chunkFormat.num_channels);
EXPECT_EQ(48000, chunkFormat.sample_rate);
EXPECT_EQ(192000, chunkFormat.byte_rate);
EXPECT_EQ(4, chunkFormat.block_align);
EXPECT_EQ(16, chunkFormat.bits_per_sample);
file.close();
}
class PlaybackCommandParserTest : public ::testing::Test {
protected:
void SetUp() override {
command = new char*[30];
}
void TearDown() override {
if (command) {
delete[] command;
command = nullptr;
}
}
void makeTestCommand() {
memset(command, 0, sizeof(command));
command[0] = "agmplay";
command[1] = "/sdcard/48.wav";
command[2] = "-d";
command[3] = "100";
command[4] = "-D";
command[5] = "100";
command[6] = "-num_intf";
command[7] = "1";
command[8] = "-i";
command[9] = "TDM-LPAIF-RX-PRIMARY";
command[10] = "-h";
command[11] = "false";
command[12] = "-dkv";
command[13] = "0xA2000001";
command[14] = "-skv";
command[15] = "0";
command[16] = "-ikv";
command[17] = "1";
command[18] = "-dppkv";
command[19] = "0xAC000002";
command[20] = "-is_24_LE";
command[21] = "false";
command[22] = "-c";
command[23] = "2";
command[24] = "-r";
command[25] = "48000";
command[26] = "-b";
command[27] = "16";
command[28] = "-usb_d";
command[29] = "1";
}
PlaybackCommandParser parser;
char** command = nullptr;
char **interfaceName = nullptr;
unsigned int *device_kv = nullptr;
unsigned int *devicepp_kv = nullptr;
};
TEST_F(PlaybackCommandParserTest, TestParseCommandLine) {
makeTestCommand();
parser.parseCommandLine(command);
device_kv = parser.getPlaybackCommand().getDeviceKeyVector();
devicepp_kv = parser.getPlaybackCommand().getDeviceppKeyVector();
interfaceName = parser.getPlaybackCommand().getInterfaceName();
EXPECT_EQ(100, parser.getPlaybackCommand().getCard());
EXPECT_EQ(100, parser.getPlaybackCommand().getDevice());
EXPECT_EQ(1, parser.getPlaybackCommand().getInterfaceNumber());
EXPECT_STREQ("TDM-LPAIF-RX-PRIMARY", interfaceName[0]);
EXPECT_FALSE(parser.getPlaybackCommand().getHaptics() );
EXPECT_EQ(0xA2000001, *device_kv);
EXPECT_EQ(0, parser.getPlaybackCommand().getStreamKeyVector());
EXPECT_EQ(1, parser.getPlaybackCommand().getInstanceKeyVector());
EXPECT_EQ(0xAC000002, *devicepp_kv);
EXPECT_FALSE(parser.getPlaybackCommand().is24LE());
EXPECT_EQ(2, parser.getPlaybackCommand().getChannel());
EXPECT_EQ(48000, parser.getPlaybackCommand().getSampleRate());
EXPECT_EQ(16, parser.getPlaybackCommand().getBitWidth());
EXPECT_EQ(1, parser.getPlaybackCommand().getUsbDevice());
}
class AgmPlayTestFixture : public ::testing::Test {
protected:
void SetUp() override {
riffWaveParser = new RiffWaveParser();
chunkParser = new ChunkParser();
command = new char*[30];
file.open("/sdcard/sample.wav", std::ios::binary);
if (!file) {
std::cout << "please, push the sample.wav file in /sdcard/ folder" << std::endl;
exit(1);
}
riffWaveParser->parseHeader(file);
if (!riffWaveParser->isValid()) {
std::cout << "It is not a riff/wave file" << std::endl;
file.close();
exit(1);
}
chunkParser->parseHeader(file);
makeTestCommand();
parser.parseCommandLine(command);
agmPlayer = new AgmPlayer(&mockAgmMixer, &mockAgmPcm);
}
void TearDown() override {
delete agmPlayer;
delete riffWaveParser;
delete chunkParser;
if (command) {
delete[] command;
command == nullptr;
}
file.close();
}
void makeTestCommand() {
memset(command, 0, sizeof(command));
command[0] = "agmplay";
command[1] = "/sdcard/48.wav";
command[2] = "-d";
command[3] = "100";
command[4] = "-D";
command[5] = "100";
command[6] = "-i";
command[7] = "TDM-LPAIF-RX-PRIMARY";
command[8] = "-dkv";
command[9] = "0xA2000001";
}
char** command = nullptr;
std::ifstream file;
HeaderParser* riffWaveParser;
HeaderParser* chunkParser;
PlaybackCommandParser parser;
AgmPlayer *agmPlayer;
MockAgmMixerWrapper mockAgmMixer;
MockAgmPcmWrapper mockAgmPcm;
};
TEST_F(AgmPlayTestFixture, openMixerFail) {
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(-1));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, setDeviceMediaConfigFail) {
struct device_config deviceConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, setAudioInterfaceMetadataFail) {
struct device_config deviceConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, setStreamConfigFail) {
struct device_config deviceConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamMetadata(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, setStreamDeviceMetadataFail) {
struct device_config deviceConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamMetadata(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamDeviceMetadata(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, connectAudioInterfaceToStreamFail) {
struct device_config deviceConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamMetadata(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamDeviceMetadata(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, connectAudioInterfaceToStream(testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, configureMFCFail) {
struct device_config deviceConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamMetadata(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamDeviceMetadata(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, connectAudioInterfaceToStream(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, configureMFC(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, setGroupConfigFail) {
struct device_config deviceConfig;
struct group_config groupConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamMetadata(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamDeviceMetadata(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, connectAudioInterfaceToStream(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, configureMFC(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getGroupConfig(testing::_))
.WillOnce(testing::Return(groupConfig));
EXPECT_CALL(mockAgmMixer, setGroupConfig(testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, pcmOpenFail) {
struct device_config deviceConfig;
struct group_config groupConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamMetadata(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamDeviceMetadata(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, connectAudioInterfaceToStream(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, configureMFC(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getGroupConfig(testing::_))
.WillOnce(testing::Return(groupConfig));
EXPECT_CALL(mockAgmMixer, setGroupConfig(testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmOpen(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, pcmStartFail) {
struct device_config deviceConfig;
struct group_config groupConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamMetadata(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamDeviceMetadata(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, connectAudioInterfaceToStream(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, configureMFC(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getGroupConfig(testing::_))
.WillOnce(testing::Return(groupConfig));
EXPECT_CALL(mockAgmMixer, setGroupConfig(testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmOpen(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmFramesToBytes())
.WillOnce(testing::Return(16384));
EXPECT_CALL(mockAgmPcm, pcmStart())
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, pcmWriteFail) {
struct device_config deviceConfig;
struct group_config groupConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamMetadata(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamDeviceMetadata(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, connectAudioInterfaceToStream(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, configureMFC(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getGroupConfig(testing::_))
.WillOnce(testing::Return(groupConfig));
EXPECT_CALL(mockAgmMixer, setGroupConfig(testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmOpen(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmFramesToBytes())
.WillOnce(testing::Return(16384));
EXPECT_CALL(mockAgmPcm, pcmStart())
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmWrite(testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayTestFixture, playSampleSuccess) {
struct device_config deviceConfig;
struct group_config groupConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(deviceConfig));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamMetadata(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamDeviceMetadata(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, connectAudioInterfaceToStream(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, configureMFC(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, getGroupConfig(testing::_))
.WillOnce(testing::Return(groupConfig));
EXPECT_CALL(mockAgmMixer, setGroupConfig(testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmOpen(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmFramesToBytes())
.WillOnce(testing::Return(16384));
EXPECT_CALL(mockAgmPcm, pcmStart())
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmWrite(testing::_, testing::_))
.WillRepeatedly(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmStop())
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, disconnectAudioInterfaceToStream(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmPcm, pcmClose())
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(0, agmPlayer->playSample(file, format, command));
}
class AgmPlayUSBTestFixture : public ::testing::Test {
protected:
void SetUp() override {
riffWaveParser = new RiffWaveParser();
chunkParser = new ChunkParser();
command = new char*[30];
file.open("/sdcard/sample.wav", std::ios::binary);
if (!file) {
std::cout << "please, push the sample.wav file in /sdcard/ folder" << std::endl;
exit(1);
}
riffWaveParser->parseHeader(file);
if (!riffWaveParser->isValid()) {
std::cout << "It is not a riff/wave file" << std::endl;
file.close();
exit(1);
}
chunkParser->parseHeader(file);
makeTestCommand();
parser.parseCommandLine(command);
agmPlayer = new AgmPlayer(&mockAgmMixer, &mockAgmPcm);
}
void TearDown() override {
delete agmPlayer;
delete riffWaveParser;
delete chunkParser;
if (command) {
delete[] command;
command == nullptr;
}
file.close();
}
void makeTestCommand() {
memset(command, 0, sizeof(command));
command[0] = "agmplay";
command[1] = "/sdcard/48.wav";
command[2] = "-d";
command[3] = "100";
command[4] = "-D";
command[5] = "100";
command[6] = "-i";
command[7] = "USB_AUDIO-RX";
command[8] = "-dkv";
command[9] = "0xA2000001";
}
char** command = nullptr;
std::ifstream file;
HeaderParser* riffWaveParser;
HeaderParser* chunkParser;
PlaybackCommandParser parser;
AgmPlayer *agmPlayer;
MockAgmMixerWrapper mockAgmMixer;
MockAgmPcmWrapper mockAgmPcm;
};
TEST_F(AgmPlayUSBTestFixture, setUSBDeviceMediaConfigFail) {
struct device_config deviceConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}
TEST_F(AgmPlayUSBTestFixture, setDeviceCustomPayloadFail) {
struct device_config deviceConfig;
ChunkFormat format = chunkParser->getFormat();
PlaybackCommand command = parser.getPlaybackCommand();
EXPECT_CALL(mockAgmMixer, mixerOpen(testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setDeviceMediaConfig(testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setAudioInterfaceMetadata(testing::_, testing::_, testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamMetadata(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setStreamDeviceMetadata(testing::_, testing::_, testing::_, testing::_))
.WillOnce(testing::Return(0));
EXPECT_CALL(mockAgmMixer, setDeviceCustomPayload(testing::_, testing::_, testing::_))
.WillOnce(testing::Return(-1));
EXPECT_CALL(mockAgmMixer, mixerClose())
.WillOnce(testing::Return(0));
EXPECT_EQ(-1 , agmPlayer->playSample(file, format, command));
}