doubao_chat/apps/common/audio_music/pcm_play_api.c

356 lines
9.1 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "server/audio_server.h"
#include "server/server_core.h"
#include "generic/circular_buf.h"
#include "os/os_api.h"
#include "app_config.h"
struct audio_pcm_play_t {
void *dec_server;
void *cache_buf;
cbuffer_t cbuf;
OS_SEM w_sem;
OS_SEM r_sem;
u8 block;
u8 run_flag;
u8 channel;
u8 force_double_channel;
#if defined CONFIG_AUDIO_MIX_ENABLE
u8 inc_step;
u8 dec_step;
s16 adjust_step;
s16 max_step;
u32 begin_size;
u32 top_size;
u32 bottom_size;
int drop_points;
#endif
};
static void __single_data_copy(s16 *data, int len, u8 ch_num)
{
s16 *start = (s16 *)((u8 *)data + len / ch_num);
s16 *end = (s16 *)((u8 *)data + len);
if (ch_num == 2) {
for (u32 i = 0; i < len / (ch_num * 2); ++i) {
*--end = *--start;
*--end = *start;
}
} else if (ch_num == 3) {
for (u32 i = 0; i < len / (ch_num * 2); ++i) {
*--end = *--start;
*--end = *start;
*--end = *start;
}
} else if (ch_num == 4) {
for (u32 i = 0; i < len / (ch_num * 2); ++i) {
*--end = *--start;
*--end = *start;
*--end = *start;
*--end = *start;
}
}
}
//解码器读取PCM数据
static int audio_pcm_play_vfs_fread(void *priv, void *data, u32 len)
{
struct audio_pcm_play_t *hdl = (struct audio_pcm_play_t *)priv;
u32 rlen;
if (hdl->force_double_channel && hdl->channel == 1) {
len >>= 1;
}
do {
rlen = cbuf_get_data_size(&hdl->cbuf);
rlen = rlen > len ? len : rlen;
if (cbuf_read(&hdl->cbuf, data, rlen) > 0) {
if (hdl->block) {
os_sem_set(&hdl->w_sem, 0);
os_sem_post(&hdl->w_sem);
}
break;
}
if (!hdl->block) {
return -2; //非堵塞
}
//此处等待信号量是为了防止解码器因为读不到数而一直空转
os_sem_pend(&hdl->r_sem, 0);
} while (hdl->run_flag);
#if defined CONFIG_AUDIO_MIX_ENABLE
if (hdl->drop_points > 0) {
hdl->drop_points -= rlen / 2;
memset(data, 0, rlen);
}
#endif
if (hdl->force_double_channel && hdl->channel == 1) {
rlen <<= 1;
__single_data_copy((s16 *)data, rlen, 2);
}
//返回成功读取的字节数
return rlen;
}
static const struct audio_vfs_ops pcm_vfs_ops = {
.fread = audio_pcm_play_vfs_fread,
};
#if defined CONFIG_AUDIO_MIX_ENABLE
static int pcm_sync(void *priv, u32 data_size, u16 *in_rate, u16 *out_rate)
{
struct audio_pcm_play_t *hdl = (struct audio_pcm_play_t *)priv;
data_size = cbuf_get_data_size(&hdl->cbuf);
if (data_size < hdl->bottom_size) {
/* putchar('<'); */
hdl->adjust_step += hdl->inc_step;
if (hdl->adjust_step < 0) {
hdl->adjust_step += hdl->inc_step * 2;
}
} else if (data_size > hdl->top_size) {
/* putchar('>'); */
hdl->adjust_step -= hdl->dec_step;
if (hdl->adjust_step > 0) {
hdl->adjust_step -= hdl->dec_step * 2;
}
} else {
/* putchar('='); */
if (hdl->adjust_step > 0) {
hdl->adjust_step -= (hdl->adjust_step * hdl->inc_step) / hdl->max_step;
if (hdl->adjust_step > 0) {
hdl->adjust_step--;
}
} else if (hdl->adjust_step < 0) {
hdl->adjust_step += ((0 - hdl->adjust_step) * hdl->dec_step) / hdl->max_step;
if (hdl->adjust_step < 0) {
hdl->adjust_step++;
}
}
}
if (hdl->adjust_step < -hdl->max_step) {
hdl->adjust_step = -hdl->max_step;
putchar(',');
} else if (hdl->adjust_step > hdl->max_step) {
hdl->adjust_step = hdl->max_step;
putchar('.');
}
*out_rate += hdl->adjust_step;
return 0;
}
#endif
void *audio_pcm_play_open(int sample_rate, u32 frame_size, u32 drop_points, u8 channel, u8 volume, u8 block)
{
union audio_req req = {0};
int err;
u32 cache_buf_size = frame_size ? frame_size * 4 * (sample_rate / 11025 > 0 ? sample_rate / 11025 : 1) : sample_rate * channel; //上层缓冲buf缓冲0.5秒的数据,缓冲太大听感上会有延迟
struct audio_pcm_play_t *hdl = (struct audio_pcm_play_t *)zalloc(sizeof(struct audio_pcm_play_t));
if (hdl == NULL) {
return NULL;
}
hdl->cache_buf = malloc(cache_buf_size);
if (hdl->cache_buf == NULL) {
free(hdl);
return NULL;
}
cbuf_init(&hdl->cbuf, hdl->cache_buf, cache_buf_size);
os_sem_create(&hdl->w_sem, 0);
os_sem_create(&hdl->r_sem, 0);
hdl->block = block;
hdl->run_flag = 1;
hdl->dec_server = server_open("audio_server", "dec");
if (hdl->dec_server == NULL) {
goto __err;
}
/****************打开解码DAC器*******************/
hdl->force_double_channel = 1;
hdl->channel = channel;
if (hdl->force_double_channel && channel == 1) {
req.dec.channel = 2;
req.dec.output_buf_len = cache_buf_size;
} else {
req.dec.channel = channel;
req.dec.output_buf_len = cache_buf_size / 2;
}
req.dec.cmd = AUDIO_DEC_OPEN;
req.dec.volume = volume;
req.dec.sample_rate = sample_rate;
req.dec.vfs_ops = &pcm_vfs_ops;
req.dec.dec_type = "pcm";
#if defined CONFIG_AUDIO_DEC_PLAY_SOURCE
req.dec.sample_source = CONFIG_AUDIO_DEC_PLAY_SOURCE;
#else
req.dec.sample_source = "dac";
#endif
req.dec.file = (FILE *)hdl;
/* req.dec.attr = AUDIO_ATTR_LR_ADD; */ //左右声道数据合在一起
#if defined CONFIG_AUDIO_MIX_ENABLE
req.dec.attr = AUDIO_ATTR_REAL_TIME;
req.dec.dec_sync = pcm_sync;
req.dec.sync_priv = hdl;
hdl->drop_points = drop_points * channel;
hdl->begin_size = cache_buf_size * 6 / 10;
hdl->top_size = cache_buf_size / 2;
hdl->bottom_size = cache_buf_size * 3 / 10;
hdl->max_step = sample_rate / 5;
if (sample_rate > 8000) {
hdl->inc_step = 10 * sample_rate / 11025;
hdl->dec_step = 10 * sample_rate / 11025;
} else {
hdl->inc_step = 10;
hdl->dec_step = 10;
}
#endif
err = server_request(hdl->dec_server, AUDIO_REQ_DEC, &req);
if (err) {
goto __err;
}
return hdl;
__err:
if (hdl->dec_server) {
server_close(hdl->dec_server);
hdl->dec_server = NULL;
}
if (hdl->cache_buf) {
free(hdl->cache_buf);
hdl->cache_buf = NULL;
}
free(hdl);
return NULL;
}
int audio_pcm_play_start(void *priv)
{
struct audio_pcm_play_t *hdl = (struct audio_pcm_play_t *)priv;
union audio_req req = {0};
if (hdl == NULL || hdl->dec_server == NULL) {
return -1;
}
req.dec.cmd = AUDIO_DEC_START;
return server_request(hdl->dec_server, AUDIO_REQ_DEC, &req);
}
int audio_pcm_play_pause(void *priv, int clear_cache)
{
struct audio_pcm_play_t *hdl = (struct audio_pcm_play_t *)priv;
union audio_req req = {0};
int err;
if (hdl == NULL || hdl->dec_server == NULL) {
return -1;
}
req.dec.cmd = AUDIO_DEC_PAUSE;
err = server_request(hdl->dec_server, AUDIO_REQ_DEC, &req);
if (hdl->cache_buf && clear_cache) {
cbuf_clear(&hdl->cbuf);
}
return err;
}
int audio_pcm_play_set_volume(void *priv, u8 volume)
{
struct audio_pcm_play_t *hdl = (struct audio_pcm_play_t *)priv;
union audio_req req = {0};
int err;
if (hdl == NULL || hdl->dec_server == NULL) {
return -1;
}
req.dec.cmd = AUDIO_DEC_SET_VOLUME;
req.dec.volume = volume;
return server_request(hdl->dec_server, AUDIO_REQ_DEC, &req);
}
int audio_pcm_play_stop(void *priv)
{
struct audio_pcm_play_t *hdl = (struct audio_pcm_play_t *)priv;
union audio_req req = {0};
if (hdl == NULL || hdl->dec_server == NULL) {
return -1;
}
hdl->run_flag = 0;
if (hdl->block) {
os_sem_post(&hdl->w_sem);
os_sem_post(&hdl->r_sem);
}
if (hdl->dec_server) {
req.dec.cmd = AUDIO_DEC_STOP;
server_request(hdl->dec_server, AUDIO_REQ_DEC, &req);
server_close(hdl->dec_server);
hdl->dec_server = NULL;
}
os_sem_del(&hdl->w_sem, OS_DEL_ALWAYS);
os_sem_del(&hdl->r_sem, OS_DEL_ALWAYS);
if (hdl->cache_buf) {
free(hdl->cache_buf);
hdl->cache_buf = NULL;
}
free(hdl);
return 0;
}
int audio_pcm_play_data_write(void *priv, void *data, u32 size)
{
struct audio_pcm_play_t *hdl = (struct audio_pcm_play_t *)priv;
u32 wlen;
if (hdl == NULL || hdl->cache_buf == NULL) {
return -1;
}
do {
wlen = cbuf_write(&hdl->cbuf, data, size);
if (hdl->block) {
os_sem_set(&hdl->r_sem, 0);
os_sem_post(&hdl->r_sem);
}
if (wlen == size) {
break;
}
if (!hdl->block) {
//上层buf写不进去时清空一下,避免出现声音滞后的情况
cbuf_clear(&hdl->cbuf);
return 0;
}
os_sem_pend(&hdl->w_sem, 0);
} while (hdl->run_flag);
return size;
}