356 lines
9.1 KiB
C
356 lines
9.1 KiB
C
#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;
|
||
}
|
||
|