You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
461 lines
12 KiB
461 lines
12 KiB
/*
|
|
* Copyright (c) Hisilicon Technologies Co., Ltd. 2019-2019. All rights reserved.
|
|
* Description: mix engine sample
|
|
* Author: audio
|
|
* Create: 2019-09-17
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <pthread.h>
|
|
|
|
#include "uapi_sound.h"
|
|
#include "uapi_system.h"
|
|
#include "securec.h"
|
|
|
|
#define MAX_MIXER_NUM 8
|
|
#define MIXER_FRAME_TIME 10 /* 10ms */
|
|
#define MIX_THREAD_SLEEP_TIME (MIXER_FRAME_TIME * 1000) /* 1000 s to ms */
|
|
#define INPUT_CMD_LENGTH 32
|
|
|
|
typedef struct {
|
|
td_char *in_stream;
|
|
td_char *volume;
|
|
td_char *sample_rate;
|
|
td_char *channels;
|
|
} input_file;
|
|
|
|
typedef struct {
|
|
td_char *sample;
|
|
input_file file[MAX_MIXER_NUM];
|
|
} input_arg;
|
|
|
|
typedef struct {
|
|
td_u32 id;
|
|
td_char *file_name;
|
|
FILE *in_stream;
|
|
td_u32 volume;
|
|
td_u32 sample_rate;
|
|
td_u32 channels;
|
|
|
|
td_u32 pcm_samples;
|
|
td_u32 frame_size;
|
|
|
|
td_bool stop;
|
|
pthread_t thread;
|
|
|
|
td_handle track;
|
|
} sample_chan_ctx;
|
|
|
|
typedef struct {
|
|
uapi_snd snd;
|
|
td_u32 chan_num;
|
|
sample_chan_ctx mix_chan[MAX_MIXER_NUM];
|
|
} sample_mixengine_ctx;
|
|
|
|
static td_s32 snd_init(sample_mixengine_ctx *mixengine_ctx)
|
|
{
|
|
td_s32 ret;
|
|
uapi_snd_attr snd_attr;
|
|
|
|
ret = uapi_snd_init();
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call uapi_snd_init failed(0x%x)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = uapi_snd_get_default_open_attr(mixengine_ctx->snd, &snd_attr);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call uapi_snd_get_default_open_attr failed(0x%x)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = uapi_snd_open(mixengine_ctx->snd, &snd_attr);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call uapi_snd_open failed(0x%x)\n", ret);
|
|
(td_void)uapi_snd_deinit();
|
|
return ret;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_void snd_deinit(sample_mixengine_ctx *mixengine_ctx)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ret = uapi_snd_close(mixengine_ctx->snd);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call uapi_snd_close failed(0x%x)\n", ret);
|
|
return;
|
|
}
|
|
|
|
ret = uapi_snd_deinit();
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call uapi_snd_deinit failed(0x%x).\n", ret);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static td_void audio_frame_init(const sample_chan_ctx *mixengine_ctx, uapi_audio_frame *frame)
|
|
{
|
|
td_s32 ret;
|
|
|
|
ret = memset_s(frame, sizeof(*frame), 0, sizeof(uapi_audio_frame));
|
|
if (ret != EOK) {
|
|
printf("call memset_s failed(0x%x)\n", ret);
|
|
return;
|
|
}
|
|
|
|
frame->pts = TD_INVALID_PTS;
|
|
frame->bit_depth = UAPI_AUDIO_BIT_DEPTH_16;
|
|
frame->interleaved = TD_TRUE;
|
|
frame->channels = mixengine_ctx->channels;
|
|
frame->sample_rate = mixengine_ctx->sample_rate;
|
|
frame->pcm_samples = mixengine_ctx->pcm_samples;
|
|
}
|
|
|
|
static td_void *mix_thread(td_void *args)
|
|
{
|
|
td_s32 ret;
|
|
td_void *pcm_buf = TD_NULL;
|
|
sample_chan_ctx *mixengine_ctx = (sample_chan_ctx *)args;
|
|
uapi_audio_frame ao_frame;
|
|
td_bool send_pending = TD_FALSE;
|
|
td_u32 read_len;
|
|
|
|
if (mixengine_ctx->frame_size > (UAPI_AUDIO_SAMPLE_RATE_192K * UAPI_AUDIO_CHANNEL_16 * sizeof(td_s16))) {
|
|
return TD_NULL;
|
|
}
|
|
|
|
pcm_buf = (td_void *)malloc(mixengine_ctx->frame_size);
|
|
if (pcm_buf == TD_NULL) {
|
|
printf("malloc pcm buffer failed!\n");
|
|
return TD_NULL;
|
|
}
|
|
|
|
audio_frame_init(mixengine_ctx, &ao_frame);
|
|
ao_frame.pcm_buffer = (td_s32 *)(pcm_buf);
|
|
|
|
printf("mix %u start, file: %s\n", mixengine_ctx->id, mixengine_ctx->file_name);
|
|
|
|
while (mixengine_ctx->stop != TD_TRUE) {
|
|
if (send_pending == TD_FALSE) {
|
|
read_len = (td_u32)fread(pcm_buf, 1, mixengine_ctx->frame_size, mixengine_ctx->in_stream);
|
|
if (read_len != mixengine_ctx->frame_size) {
|
|
printf("rewind mix(%u), file: %s\n", mixengine_ctx->id, mixengine_ctx->file_name);
|
|
rewind(mixengine_ctx->in_stream);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ret = uapi_snd_send_track_data(mixengine_ctx->track, &ao_frame);
|
|
if (ret == TD_SUCCESS) {
|
|
send_pending = TD_FALSE;
|
|
continue;
|
|
} else if (ret == TD_FAILURE) {
|
|
printf("uapi_snd_send_track_data failed(0x%x)!\n", ret);
|
|
break;
|
|
} else {
|
|
usleep(MIX_THREAD_SLEEP_TIME);
|
|
send_pending = TD_TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
free(pcm_buf);
|
|
printf("mix %u stop, file: %s\n", mixengine_ctx->id, mixengine_ctx->file_name);
|
|
return TD_NULL;
|
|
}
|
|
|
|
static td_void track_deinit_one_chan(sample_chan_ctx *mixengine_ctx)
|
|
{
|
|
td_s32 ret;
|
|
|
|
if (mixengine_ctx->track == TD_INVALID_HANDLE) {
|
|
return;
|
|
}
|
|
|
|
ret = uapi_snd_destroy_track(mixengine_ctx->track);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call uapi_snd_destroy_track failed(0x%x)\n", ret);
|
|
return;
|
|
}
|
|
|
|
mixengine_ctx->track = TD_INVALID_HANDLE;
|
|
}
|
|
|
|
static td_void track_deinit(sample_mixengine_ctx *mixengine_ctx)
|
|
{
|
|
td_u32 i;
|
|
|
|
for (i = 0; i < mixengine_ctx->chan_num; i++) {
|
|
track_deinit_one_chan(&mixengine_ctx->mix_chan[i]);
|
|
}
|
|
}
|
|
|
|
static td_s32 track_init_one_chan(uapi_snd snd, sample_chan_ctx *mixengine_ctx)
|
|
{
|
|
td_s32 ret;
|
|
uapi_audio_track_attr track_attr;
|
|
uapi_snd_gain gain = {
|
|
.linear_mode = TD_TRUE,
|
|
.gain = mixengine_ctx->volume,
|
|
};
|
|
|
|
ret = uapi_snd_get_default_track_attr(UAPI_SND_TRACK_TYPE_SLAVE, &track_attr);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("uapi_snd_get_default_track_attr failed(0x%x)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = uapi_snd_create_track(snd, &track_attr, &mixengine_ctx->track);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("uapi_snd_create_track failed(0x%x)!\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = uapi_snd_set_track_weight(mixengine_ctx->track, &gain);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("uapi_snd_set_track_weight failed(0x%x)!\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
out:
|
|
track_deinit_one_chan(mixengine_ctx);
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 track_init(sample_mixengine_ctx *mixengine_ctx)
|
|
{
|
|
td_u32 i;
|
|
td_s32 ret;
|
|
|
|
for (i = 0; i < mixengine_ctx->chan_num; i++) {
|
|
ret = track_init_one_chan(mixengine_ctx->snd, &mixengine_ctx->mix_chan[i]);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("track_init_one_chan failed(0x%x)!\n", ret);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
out:
|
|
track_deinit(mixengine_ctx);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
static td_void sample_stop(sample_mixengine_ctx *mixengine_ctx)
|
|
{
|
|
td_u32 i;
|
|
|
|
for (i = 0; i < mixengine_ctx->chan_num; i++) {
|
|
mixengine_ctx->mix_chan[i].stop = TD_TRUE;
|
|
pthread_join(mixengine_ctx->mix_chan[i].thread, TD_NULL);
|
|
}
|
|
}
|
|
|
|
static td_s32 sample_start(sample_mixengine_ctx *mixengine_ctx)
|
|
{
|
|
td_u32 i;
|
|
|
|
for (i = 0; i < mixengine_ctx->chan_num; i++) {
|
|
mixengine_ctx->mix_chan[i].stop = TD_FALSE;
|
|
pthread_create(&mixengine_ctx->mix_chan[i].thread, TD_NULL,
|
|
mix_thread, &mixengine_ctx->mix_chan[i]);
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static const struct {
|
|
td_s32 (*init)(sample_mixengine_ctx *mixengine_ctx);
|
|
td_void (*deinit)(sample_mixengine_ctx *mixengine_ctx);
|
|
} g_sample_module[] = {
|
|
{snd_init, snd_deinit},
|
|
{track_init, track_deinit},
|
|
{sample_start, sample_stop},
|
|
};
|
|
|
|
static td_void sample_deinit(sample_mixengine_ctx *mixengine_ctx)
|
|
{
|
|
td_s32 ret;
|
|
td_s32 module;
|
|
const td_s32 num = sizeof(g_sample_module) / sizeof(g_sample_module[0]);
|
|
|
|
for (module = num - 1; module >= 0; module--) {
|
|
g_sample_module[module].deinit(mixengine_ctx);
|
|
}
|
|
|
|
ret = uapi_sys_deinit();
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call uapi_sys_deinit failed(0x%x)\n", ret);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static td_s32 sample_init(sample_mixengine_ctx *mixengine_ctx)
|
|
{
|
|
td_s32 ret;
|
|
td_s32 module;
|
|
const td_s32 mixengine_num = sizeof(g_sample_module) / sizeof(g_sample_module[0]);
|
|
|
|
ret = uapi_sys_init();
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call uapi_sys_init failed(0x%x)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
for (module = 0; module < mixengine_num; module++) {
|
|
ret = g_sample_module[module].init(mixengine_ctx);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call init failed(0x%x)\n", ret);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
out:
|
|
module--;
|
|
for (; module >= 0; module--) {
|
|
g_sample_module[module].deinit(mixengine_ctx);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static td_s32 mix_init_chan(const input_file *file, sample_chan_ctx *mixengine_ctx)
|
|
{
|
|
mixengine_ctx->file_name = file->in_stream;
|
|
mixengine_ctx->in_stream = fopen(file->in_stream, "rb");
|
|
if (mixengine_ctx->in_stream == TD_NULL) {
|
|
printf("open file %s failed\n", file->in_stream);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
mixengine_ctx->volume = (td_u32)strtol(file->volume, NULL, 10); /* 10 is Dec */
|
|
mixengine_ctx->sample_rate = (td_u32)strtol(file->sample_rate, NULL, 10); /* 10 is Dec */
|
|
mixengine_ctx->channels = (td_u32)strtol(file->channels, NULL, 10); /* 10 is Dec */
|
|
|
|
if ((mixengine_ctx->sample_rate > UAPI_AUDIO_SAMPLE_RATE_192K) ||
|
|
(mixengine_ctx->channels > UAPI_AUDIO_CHANNEL_16)) {
|
|
printf("invalid samplerate(%u) or channel(%u).\n", mixengine_ctx->sample_rate, mixengine_ctx->channels);
|
|
(td_void)fclose(mixengine_ctx->in_stream);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
mixengine_ctx->pcm_samples = mixengine_ctx->sample_rate * MIXER_FRAME_TIME / 1000; /* 1000 s to ms */
|
|
mixengine_ctx->frame_size = mixengine_ctx->pcm_samples * mixengine_ctx->channels * sizeof(td_s16);
|
|
return TD_SUCCESS;
|
|
}
|
|
|
|
static td_void sample_ctx_deinit(sample_mixengine_ctx *mixengine_ctx)
|
|
{
|
|
td_u32 i;
|
|
|
|
for (i = 0; i < mixengine_ctx->chan_num; i++) {
|
|
if (mixengine_ctx->mix_chan[i].in_stream == TD_NULL) {
|
|
continue;
|
|
}
|
|
|
|
(td_void)fclose(mixengine_ctx->mix_chan[i].in_stream);
|
|
mixengine_ctx->mix_chan[i].in_stream = TD_NULL;
|
|
mixengine_ctx->mix_chan[i].file_name = TD_NULL;
|
|
}
|
|
}
|
|
|
|
static td_void usage(const input_arg *arg)
|
|
{
|
|
printf("usage: %s "
|
|
"file0 volume samplerate channels "
|
|
"file1 volume samplerate channels "
|
|
"file2 volume samplerate channels ...\n",
|
|
arg->sample);
|
|
|
|
printf("examples:\n");
|
|
printf("%s file0 100 48000 2 file1 100 44100 1\n", arg->sample);
|
|
}
|
|
|
|
static td_s32 sample_ctx_init(td_s32 argc, td_char *argv[], sample_mixengine_ctx *mixengine_ctx)
|
|
{
|
|
td_u32 i;
|
|
td_s32 ret;
|
|
input_arg *arg = (input_arg *)argv;
|
|
const td_s32 chan_arg_num = sizeof(input_file) / sizeof(td_char *);
|
|
|
|
if (argc <= chan_arg_num) {
|
|
usage(arg);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
mixengine_ctx->chan_num = (td_u32)((argc - 1) / chan_arg_num);
|
|
|
|
if ((mixengine_ctx->chan_num <= 0) || (mixengine_ctx->chan_num > MAX_MIXER_NUM)) {
|
|
usage(arg);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
for (i = 0; i < MAX_MIXER_NUM; i++) {
|
|
mixengine_ctx->mix_chan[i].id = i;
|
|
mixengine_ctx->mix_chan[i].track = TD_INVALID_HANDLE;
|
|
}
|
|
|
|
for (i = 0; i < mixengine_ctx->chan_num; i++) {
|
|
ret = mix_init_chan(&arg->file[i], &mixengine_ctx->mix_chan[i]);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call mix_init_chan failed(0x%x)\n", ret);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
return TD_SUCCESS;
|
|
|
|
out:
|
|
sample_ctx_deinit(mixengine_ctx);
|
|
return TD_FAILURE;
|
|
}
|
|
|
|
td_s32 main(int argc, char *argv[])
|
|
{
|
|
td_s32 ret;
|
|
sample_mixengine_ctx mixengine_ctx;
|
|
td_char input_cmd[INPUT_CMD_LENGTH];
|
|
|
|
ret = memset_s(&mixengine_ctx, sizeof(mixengine_ctx), 0, sizeof(sample_mixengine_ctx));
|
|
if (ret != EOK) {
|
|
printf("call memset_s failed(0x%x)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
mixengine_ctx.snd = UAPI_SND_0;
|
|
|
|
ret = sample_ctx_init(argc, argv, &mixengine_ctx);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call sample_ctx_init failed(0x%x)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = sample_init(&mixengine_ctx);
|
|
if (ret != TD_SUCCESS) {
|
|
printf("call sample_init failed(0x%x)\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
while (1) {
|
|
printf("please input the q to quit!\n");
|
|
(td_void)fgets((char *)(input_cmd), (sizeof(input_cmd) - 1), stdin);
|
|
if (input_cmd[0] == 'q') {
|
|
printf("prepare to quit!\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
sample_deinit(&mixengine_ctx);
|
|
|
|
out:
|
|
sample_ctx_deinit(&mixengine_ctx);
|
|
return ret;
|
|
}
|
|
|