初版
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
#ifndef _ADC_FILE_H_
|
||||
#define _ADC_FILE_H_
|
||||
|
||||
#include "generic/typedef.h"
|
||||
#include "media/includes.h"
|
||||
#include "app_config.h"
|
||||
#include "audio_config.h"
|
||||
|
||||
|
||||
//stream.bin ADC参数文件解析
|
||||
struct adc_file_cfg {
|
||||
u32 mic_en_map; // BIT(ch) 1 为 ch 使能,0 为不使能
|
||||
struct adc_file_param param[AUDIO_ADC_MIC_MAX_NUM];
|
||||
} __attribute__((packed));
|
||||
|
||||
void audio_adc_file_init(void);
|
||||
void audio_all_adc_file_init(void);
|
||||
void audio_adc_file_set_gain(u8 mic_index, u8 mic_gain);
|
||||
u8 audio_adc_file_get_gain(u8 mic_index);
|
||||
u8 audio_adc_file_get_mic_mode(u8 mic_index);
|
||||
int adc_file_mic_open(struct adc_mic_ch *mic, int ch);
|
||||
struct adc_file_cfg *audio_adc_file_get_cfg(void);
|
||||
struct adc_platform_cfg *audio_adc_platform_get_cfg(void);
|
||||
u8 audio_adc_file_get_esco_mic_num(void);
|
||||
void audio_adc_fixed_digital_set_buffs(void);
|
||||
|
||||
u8 audio_adc_file_get_mic_en_map(void);
|
||||
|
||||
void audio_adc_file_set_mic_en_map(u8 mic_en_map);
|
||||
extern u32 pc_mic_get_fmt_sample_rate(void);
|
||||
/*根据mic通道值获取使用的第几个mic*/
|
||||
u8 audio_get_mic_index(u8 mic_ch);
|
||||
/*根据mic通道值获取使用了多少个mic*/
|
||||
u8 audio_get_mic_num(u32 mic_ch);
|
||||
|
||||
u8 audio_anc_adt_mic_ch_num_get();
|
||||
|
||||
#endif // #ifndef _ADC_FILE_H_
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef _LINEIN_FILE_H_
|
||||
#define _LINEIN_FILE_H_
|
||||
|
||||
#include "generic/typedef.h"
|
||||
#include "media/includes.h"
|
||||
#include "app_config.h"
|
||||
#include "audio_config.h"
|
||||
|
||||
struct linein_file_cfg {
|
||||
u32 mic_en_map;
|
||||
struct adc_file_param param[AUDIO_ADC_LINEIN_MAX_NUM];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct linein_file_hdl {
|
||||
void *source_node;
|
||||
u8 start;
|
||||
u8 dump_cnt;
|
||||
u8 ch_num;
|
||||
u8 mute_en;
|
||||
u16 sample_rate;
|
||||
u16 irq_points;
|
||||
struct adc_linein_ch linein_ch;
|
||||
struct audio_adc_output_hdl adc_output;
|
||||
s16 *adc_buf;
|
||||
u8 adc_seq;
|
||||
u16 output_fade_in_gain;
|
||||
u8 output_fade_in;
|
||||
};
|
||||
|
||||
void audio_linein_file_init();
|
||||
int adc_file_linein_open(struct adc_linein_ch *linein, int ch);
|
||||
|
||||
|
||||
#endif // #ifndef _ADC_FILE_H_
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef _VOLUME_NODE_H_
|
||||
#define _VOLUME_NODE_H_
|
||||
|
||||
#include "audio_config.h"
|
||||
|
||||
struct volume_cfg {
|
||||
u8 bypass; //是否bypass掉当前节点,复用高4bit用于传递 cmd(VOLUME_NODE_CMD_SET_VOL,VOLUME_NODE_CMD_SET_MUTE)
|
||||
u16 cfg_level_max; //最大音量等级
|
||||
s32 cfg_vol_min; //最小音量,dB
|
||||
u8 vol_table_custom; //是否自定义音量表
|
||||
s32 cfg_vol_max; //最大音量,dB
|
||||
s16 cur_vol; //当前音量
|
||||
#if VOL_TAB_CUSTOM_EN
|
||||
u16 tab_len; //音量表的字节长度
|
||||
#endif
|
||||
float vol_table[0]; //音量表
|
||||
} __attribute__((packed));
|
||||
|
||||
//Volume Node Command List
|
||||
#define VOLUME_NODE_CMD_SET_VOL (1<<4)
|
||||
#define VOLUME_NODE_CMD_SET_MUTE (1<<5)
|
||||
|
||||
#define VOLUME_TABLE_CUSTOM_EN 2
|
||||
|
||||
//初步判断是否为音量结构体参数的阈值,高8位是 volume_cfg 成员cfg_level_max的低8位,低8位是bypass的8位,cfg_level_max 最小值为01,bypass最小值为0,故此处阈值设为0x0100;
|
||||
#define VOL_CFG_THRESHOLD ((1<<8 |0 ) - 1 )
|
||||
|
||||
int volume_ioc_get_cfg(const char *name, struct volume_cfg *vol_cfg);//获取名字对应节点的音量配置
|
||||
|
||||
u16 volume_ioc_get_max_level(const char *name); //获取名字对应节点的最大音量
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,601 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".a2dp_tx_node.data.bss")
|
||||
#pragma data_seg(".a2dp_tx_node.data")
|
||||
#pragma const_seg(".a2dp_tx_node.text.const")
|
||||
#pragma code_seg(".a2dp_tx_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "btstack/avctp_user.h"
|
||||
#include "classic/hci_lmp.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "sync/audio_syncts.h"
|
||||
#include "codec/sbc_enc.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_A2DP_TX_NODE_ENABLE
|
||||
#define LOG_TAG "[STREAM]"
|
||||
/* #define LOG_TAG_CONST JLSTREAM */
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
#define BT_PROTOCOL_A2DP 0
|
||||
#define BT_PROTOCOL_BIS 1
|
||||
#define BT_PROTOCOL_CIS 2
|
||||
|
||||
#define AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST 1 // 使用蓝牙时钟校准
|
||||
#define AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME 1 // 使用系统时钟来做传输时间戳参考
|
||||
#define TIMESTAMP_USE_AUDIO_JIFFIES 0 // 是否用杰理时间戳
|
||||
|
||||
struct bt_tx_param {
|
||||
u8 protocol;
|
||||
u8 jl_timestamp;
|
||||
u8 frame_num;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct a2dp_tx_sync_node {
|
||||
u8 trigger;
|
||||
void *syncts;
|
||||
struct list_head entry;
|
||||
};
|
||||
|
||||
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
|
||||
struct audio_bt_emitter_timestamp {
|
||||
u16 id;
|
||||
u16 run_once;
|
||||
u32 run_points;
|
||||
#if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST
|
||||
u32 bt_time;
|
||||
int bt_cnt;
|
||||
#endif /* #if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST */
|
||||
};
|
||||
#endif
|
||||
|
||||
struct a2dp_tx_hdl {
|
||||
u8 start;
|
||||
u8 num;
|
||||
u8 reference_network;
|
||||
u8 first_timestamp;
|
||||
u8 bt_addr[6];
|
||||
u16 packet_size;
|
||||
u8 *packet;
|
||||
u32 timestamp;
|
||||
u32 start_timestamp;
|
||||
void *conn;
|
||||
int offset;
|
||||
u32 sample_rate;
|
||||
u32 sample_offset;
|
||||
u32 coding_type;
|
||||
u32 packet_sn;
|
||||
struct stream_node *node;
|
||||
struct bt_tx_param tx_param;
|
||||
u32 local_latch_time;
|
||||
struct list_head sync_list;
|
||||
struct stream_frame *frame;
|
||||
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
|
||||
u32 sbc_input_len;
|
||||
struct audio_bt_emitter_timestamp *ts;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern int bt_a2dp_get_packet_frame_num(void);
|
||||
extern void bt_edr_conn_system_clock_init(void *addr, u8 factor);
|
||||
extern u32 bt_edr_conn_master_to_local_time(void *addr, u32 usec);
|
||||
extern u32 bt_edr_conn_local_to_master_time(void *addr, u32 usec);
|
||||
extern void *a2dp_sbc_encoder_get_param(u8 *addr);
|
||||
extern int bt_source_a2dp_send_media_packet(void *priv, u8 *packet, int len, int frame_sum, u32 TS);
|
||||
extern int bt_get_source_send_a2dp_buf_size();
|
||||
extern u8 *get_cur_connect_emitter_mac_addr(void);
|
||||
extern void local_bt_us_time(u32 *_clkn, u32 *finecnt);
|
||||
|
||||
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
|
||||
static void audio_bt_emitter_time_func(void *priv)
|
||||
{
|
||||
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)priv;
|
||||
local_irq_disable();
|
||||
if (hdl->ts) {
|
||||
u32 run_once = hdl->ts->run_once;
|
||||
#if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST
|
||||
u32 _clkn, finecnt;
|
||||
local_bt_us_time(&_clkn, &finecnt);
|
||||
int offset_time = ((_clkn - hdl->ts->bt_time) & 0x7ffffff) * 625 / 1000;
|
||||
hdl->ts->bt_cnt += 10;
|
||||
if (offset_time >= (hdl->ts->bt_cnt + 10)) {
|
||||
hdl->ts->bt_time = _clkn;
|
||||
run_once += hdl->ts->run_once;
|
||||
hdl->ts->bt_cnt = (hdl->ts->bt_cnt + 10) - offset_time;
|
||||
} else if (offset_time <= (hdl->ts->bt_cnt - 10)) {
|
||||
hdl->ts->bt_time = _clkn;
|
||||
run_once -= hdl->ts->run_once;
|
||||
hdl->ts->bt_cnt = (hdl->ts->bt_cnt - 10) - offset_time;
|
||||
}
|
||||
/* printf("t:%d, %d, o:%d \n", hdl->ts->bt_cnt, offset_time, run_once); */
|
||||
#endif /* #if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST */
|
||||
hdl->ts->run_points += run_once;
|
||||
}
|
||||
local_irq_enable();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TIMESTAMP_USE_AUDIO_JIFFIES
|
||||
static int a2dp_tx_timestamp_init(struct a2dp_tx_hdl *hdl)
|
||||
{
|
||||
if (!hdl->tx_param.jl_timestamp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdl->local_latch_time = audio_jiffies_usec();
|
||||
hdl->start_timestamp = hdl->local_latch_time * TIMESTAMP_US_DENOMINATOR;
|
||||
/*r_printf("a2dp tx timestamp init : %u, %u\n", hdl->local_latch_time, hdl->start_timestamp);*/
|
||||
hdl->first_timestamp = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a2dp_tx_timestamp_handler(struct a2dp_tx_hdl *hdl)
|
||||
{
|
||||
int tx_pcm_frames = 0;
|
||||
struct a2dp_tx_sync_node *node;
|
||||
|
||||
if (!hdl->tx_param.jl_timestamp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hdl->coding_type == AUDIO_CODING_SBC) {
|
||||
tx_pcm_frames = hdl->num * 128;
|
||||
} else if (hdl->coding_type == AUDIO_CODING_AAC) {
|
||||
tx_pcm_frames = 1024;
|
||||
}
|
||||
|
||||
hdl->sample_offset += tx_pcm_frames;
|
||||
|
||||
if (hdl->sample_offset >= hdl->sample_rate) {
|
||||
hdl->sample_offset -= hdl->sample_rate;
|
||||
hdl->start_timestamp += PCM_SAMPLE_ONE_SECOND;
|
||||
hdl->local_latch_time += 1000000L;
|
||||
}
|
||||
|
||||
u32 time = hdl->local_latch_time + ((u64)hdl->sample_offset * 1000000) / hdl->sample_rate;
|
||||
|
||||
list_for_each_entry(node, &hdl->sync_list, entry) {
|
||||
if (!node->trigger) {
|
||||
node->trigger = 1;
|
||||
sound_pcm_syncts_latch_trigger(node->syncts);
|
||||
}
|
||||
sound_pcm_update_frame_num(node->syncts, tx_pcm_frames);
|
||||
if (audio_syncts_latch_enable(node->syncts)) {
|
||||
sound_pcm_update_frame_num_and_time(node->syncts, 0, time, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void a2dp_tx_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)iport->node->private_data;
|
||||
|
||||
while (1) {
|
||||
if (hdl->frame == NULL) {
|
||||
hdl->frame = jlstream_pull_frame(iport, note);
|
||||
}
|
||||
if (!hdl->frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
|
||||
if (hdl->ts->run_points < hdl->sbc_input_len) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (hdl->offset + hdl->frame->len > hdl->packet_size) {
|
||||
/* g_printf("send : %d, %d\n", hdl->num, hdl->offset); */
|
||||
hdl->packet_sn++;
|
||||
hdl->timestamp = hdl->packet_sn * (128 * hdl->num);
|
||||
bt_source_a2dp_send_media_packet(hdl->conn, hdl->packet, hdl->offset, hdl->num, hdl->timestamp);
|
||||
#if TIMESTAMP_USE_AUDIO_JIFFIES
|
||||
a2dp_tx_timestamp_handler(hdl);
|
||||
#endif
|
||||
hdl->offset = 0;
|
||||
hdl->num = 0;
|
||||
}
|
||||
|
||||
if (hdl->offset == 0) {
|
||||
if (hdl->tx_param.jl_timestamp == 1) {
|
||||
#if TIMESTAMP_USE_AUDIO_JIFFIES
|
||||
if (hdl->first_timestamp) {
|
||||
int time_diff = hdl->frame->timestamp - hdl->start_timestamp;
|
||||
time_diff /= TIMESTAMP_US_DENOMINATOR;
|
||||
hdl->local_latch_time += time_diff;
|
||||
|
||||
hdl->start_timestamp = bt_edr_conn_local_to_master_time(hdl->bt_addr, hdl->frame->timestamp);
|
||||
hdl->first_timestamp = 0;
|
||||
hdl->sample_offset = 0;
|
||||
/*r_printf("start : %u, %u, %d, %u\n", frame->timestamp, hdl->start_timestamp, time_diff, hdl->local_latch_time);*/
|
||||
} else {
|
||||
hdl->timestamp = hdl->start_timestamp + PCM_SAMPLE_TO_TIMESTAMP(hdl->sample_offset, hdl->sample_rate);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
hdl->timestamp = hdl->frame->timestamp;
|
||||
}
|
||||
}
|
||||
memcpy(hdl->packet + hdl->offset, hdl->frame->data, hdl->frame->len);
|
||||
hdl->num++;
|
||||
hdl->offset += hdl->frame->len;
|
||||
if (hdl->num >= hdl->tx_param.frame_num) {
|
||||
/* g_printf("send 2: %d, %d \n", hdl->num, hdl->offset); */
|
||||
hdl->packet_sn++;
|
||||
hdl->timestamp = hdl->packet_sn * (128 * hdl->num);
|
||||
bt_source_a2dp_send_media_packet(hdl->conn, hdl->packet, hdl->offset, hdl->num, hdl->timestamp);
|
||||
#if TIMESTAMP_USE_AUDIO_JIFFIES
|
||||
a2dp_tx_timestamp_handler(hdl);
|
||||
#endif
|
||||
hdl->offset = 0;
|
||||
hdl->num = 0;
|
||||
}
|
||||
|
||||
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
|
||||
local_irq_disable();
|
||||
if (hdl->ts->run_points >= hdl->sbc_input_len) {
|
||||
hdl->ts->run_points -= hdl->sbc_input_len;
|
||||
} else {
|
||||
ASSERT((hdl->ts->run_points >= hdl->sbc_input_len));
|
||||
}
|
||||
local_irq_enable();
|
||||
#endif
|
||||
jlstream_free_frame(hdl->frame);
|
||||
hdl->frame = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int a2dp_tx_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct a2dp_tx_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a2dp_tx_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)iport->node->private_data;
|
||||
iport->private_data = iport->node->private_data;
|
||||
iport->handle_frame = a2dp_tx_handle_frame;
|
||||
|
||||
INIT_LIST_HEAD(&hdl->sync_list);
|
||||
|
||||
int len = jlstream_read_node_data_new(NODE_UUID_A2DP_TX, hdl->node->subid, (void *)&hdl->tx_param, NULL);
|
||||
if (len < sizeof(hdl->tx_param)) {
|
||||
log_error("a2dp tx get params error\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void a2dp_tx_set_bt_addr(struct a2dp_tx_hdl *hdl, void *bt_addr)
|
||||
{
|
||||
memcpy(hdl->bt_addr, bt_addr, 6);
|
||||
|
||||
#if TIMESTAMP_USE_AUDIO_JIFFIES
|
||||
if (hdl->tx_param.jl_timestamp == 1) {
|
||||
bt_edr_conn_system_clock_init(hdl->bt_addr, TIMESTAMP_US_DENOMINATOR);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
u32 get_a2dp_tx_sr(u8 sbc_freqency)
|
||||
{
|
||||
/* #define SBC_FREQ_16000 0x00 */
|
||||
/* #define SBC_FREQ_32000 0x01 */
|
||||
/* #define SBC_FREQ_44100 0x02 */
|
||||
/* #define SBC_FREQ_48000 0x03 */
|
||||
|
||||
u32 sr[] = {16000, 32000, 44100, 48000};
|
||||
if (sbc_freqency >= ARRAY_SIZE(sr)) {
|
||||
log_error("a2dp sbc get sample_rate error %d\n", sbc_freqency);
|
||||
}
|
||||
return sr[sbc_freqency];
|
||||
}
|
||||
u8 get_a2dp_channel_mode(u8 mode)
|
||||
{
|
||||
/* [> channel mode <] */
|
||||
/* #define SBC_MODE_MONO 0x00 */
|
||||
/* #define SBC_MODE_DUAL_CHANNEL 0x01 */
|
||||
/* #define SBC_MODE_STEREO 0x02 */
|
||||
/* #define SBC_MODE_JOINT_STEREO 0x03 */
|
||||
|
||||
if (mode == SBC_MODE_MONO) {
|
||||
return AUDIO_CH_MIX;
|
||||
} else {
|
||||
return AUDIO_CH_LR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int a2dp_tx_ioc_fmt_nego(struct stream_iport *iport)
|
||||
{
|
||||
void *bt_addr = get_cur_connect_emitter_mac_addr();
|
||||
if (!bt_addr) {
|
||||
return 0; // 若无蓝牙发射连接,直接返回
|
||||
}
|
||||
|
||||
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)iport->node->private_data;
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
int ret = NEGO_STA_ACCPTED;
|
||||
|
||||
hdl->coding_type = in_fmt->coding_type;
|
||||
|
||||
if (hdl->coding_type == AUDIO_CODING_AAC) {
|
||||
/*TDDO : AAC格式发送*/
|
||||
}
|
||||
|
||||
u8 sbc_need_free = 0;
|
||||
sbc_t *sbc_param = a2dp_sbc_encoder_get_param(hdl->bt_addr);
|
||||
if (!sbc_param) {
|
||||
log_info("no sbc param, use initlized param\n");
|
||||
sbc_param = zalloc(sizeof(sbc_t));
|
||||
sbc_param->frequency = SBC_FREQ_44100; // 给sbc_param初值,防止未连接emitter的时候nego不过
|
||||
sbc_param->blocks = SBC_BLK_16;
|
||||
sbc_param->subbands = SBC_SB_8;
|
||||
sbc_param->mode = SBC_MODE_STEREO;
|
||||
sbc_param->allocation = 0;
|
||||
sbc_param->endian = SBC_LE;
|
||||
sbc_param->bitpool = 38;
|
||||
sbc_need_free = 1;
|
||||
}
|
||||
|
||||
u32 sample_rate = get_a2dp_tx_sr(sbc_param->frequency);
|
||||
if (in_fmt->sample_rate != sample_rate) {
|
||||
in_fmt->sample_rate = sample_rate;
|
||||
ret = NEGO_STA_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
u8 channel_mode = get_a2dp_channel_mode(sbc_param->mode);
|
||||
if (in_fmt->channel_mode != channel_mode) {
|
||||
in_fmt->channel_mode = channel_mode;
|
||||
ret = NEGO_STA_CONTINUE;
|
||||
}
|
||||
u32 bit_rate = ((sbc_param->bitpool) | (sbc_param->mode << 8) | (sbc_param->blocks << 16) | (sbc_param->subbands << 20) | (sbc_param->allocation << 24) | (sbc_param->endian << 28));
|
||||
if (in_fmt->bit_rate != bit_rate) {//sbc编码使用
|
||||
in_fmt->bit_rate = bit_rate;
|
||||
ret = NEGO_STA_CONTINUE;
|
||||
}
|
||||
|
||||
hdl->sample_rate = in_fmt->sample_rate;
|
||||
/* printf("====bit_rate %x\n", bit_rate); */
|
||||
/* printf("a2dp_tx frequency %d, sr %d, bitpool %d, mode %d, channle_mode %x,allocation %d, blocks %d, subbands %d, endian %d\n", sbc_param->frequency, sample_rate, sbc_param->bitpool, sbc_param->mode, channel_mode, sbc_param->allocation, sbc_param->blocks, sbc_param->subbands, sbc_param->endian); */
|
||||
|
||||
// 调整sbc编码参数与耳机sbc参数一致
|
||||
stream_node_ioctl(iport->prev->node, NODE_UUID_ENCODER, NODE_IOC_SET_PARAM, (int)sbc_param);
|
||||
|
||||
if (sbc_need_free) {
|
||||
free(sbc_param);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
__attribute__((weak))
|
||||
int bt_a2dp_get_packet_frame_num(void)
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
|
||||
static int a2dp_tx_start(struct a2dp_tx_hdl *hdl)
|
||||
{
|
||||
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
|
||||
sbc_t *sbc_param = a2dp_sbc_encoder_get_param(hdl->bt_addr);
|
||||
u8 subbands = 0;
|
||||
u8 blocks = 0;
|
||||
u8 channels = 0;
|
||||
if (sbc_param) {
|
||||
channels = sbc_param->mode == SBC_MODE_MONO ? 1 : 2;
|
||||
subbands = sbc_param->subbands ? 8 : 4;
|
||||
blocks = ((sbc_param->blocks) + 1) * 4;
|
||||
}
|
||||
hdl->sbc_input_len = subbands * blocks * channels * 2;
|
||||
|
||||
if (!hdl->ts) {
|
||||
hdl->ts = zalloc(sizeof(struct audio_bt_emitter_timestamp));
|
||||
}
|
||||
hdl->ts->run_points = 0;
|
||||
hdl->ts->run_once = (hdl->sample_rate * 10 / 1000) * channels * 2;
|
||||
#if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST
|
||||
u32 _clkn, finecnt;
|
||||
local_bt_us_time(&_clkn, &finecnt);
|
||||
hdl->ts->bt_time = _clkn;
|
||||
hdl->ts->bt_cnt = 0;
|
||||
#endif /* #if AUDIO_BT_EMITTER_USE_BT_TIME_ADJUST */
|
||||
hdl->ts->id = sys_s_hi_timer_add((void *)hdl, audio_bt_emitter_time_func, 10);
|
||||
#endif
|
||||
|
||||
hdl->start = 1;
|
||||
/*TODO */
|
||||
/*hdl->conn = a2dp_tx_open();*/
|
||||
|
||||
hdl->packet_size = bt_get_source_send_a2dp_buf_size();
|
||||
|
||||
//根据协商设置组帧数
|
||||
if (bt_a2dp_get_packet_frame_num()) {
|
||||
hdl->tx_param.frame_num = bt_a2dp_get_packet_frame_num();
|
||||
}
|
||||
|
||||
ASSERT(hdl->packet_size != 0);
|
||||
hdl->packet = malloc(hdl->packet_size);
|
||||
if (!hdl->packet) {
|
||||
log_error("a2dp tx packet buffer error.");
|
||||
}
|
||||
g_printf("a2dp tx packet size : %d\n", hdl->packet_size);
|
||||
#if TIMESTAMP_USE_AUDIO_JIFFIES
|
||||
a2dp_tx_timestamp_init(hdl);
|
||||
#endif
|
||||
hdl->packet_sn = 0;
|
||||
hdl->offset = 0;
|
||||
hdl->num = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a2dp_tx_stop(struct a2dp_tx_hdl *hdl)
|
||||
{
|
||||
hdl->start = 0;
|
||||
|
||||
if (hdl->packet) {
|
||||
free(hdl->packet);
|
||||
hdl->packet = NULL;
|
||||
}
|
||||
local_irq_disable();
|
||||
#if AUDIO_BT_EMITTER_TIMESTAMP_USE_SYS_TIME
|
||||
if (hdl->ts) {
|
||||
if (hdl->ts->id) {
|
||||
sys_s_hi_timer_del(hdl->ts->id);
|
||||
}
|
||||
free(hdl->ts);
|
||||
hdl->ts = NULL;
|
||||
}
|
||||
#endif
|
||||
local_irq_enable();
|
||||
if (hdl->frame) {
|
||||
jlstream_free_frame(hdl->frame);
|
||||
hdl->frame = NULL;
|
||||
}
|
||||
/*TODO */
|
||||
/*
|
||||
if (hdl->conn) {
|
||||
a2dp_tx_close(hdl->conn);
|
||||
}
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a2dp_tx_mount_syncts(struct a2dp_tx_hdl *hdl, void *syncts, u32 timestamp, u8 network)
|
||||
{
|
||||
struct a2dp_tx_sync_node *node = NULL;
|
||||
|
||||
#if TIMESTAMP_USE_AUDIO_JIFFIES
|
||||
if (!hdl->tx_param.jl_timestamp) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
list_for_each_entry(node, &hdl->sync_list, entry) {
|
||||
if ((u32)node->syncts == (u32)syncts) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
node = (struct a2dp_tx_sync_node *)zalloc(sizeof(struct a2dp_tx_sync_node));
|
||||
node->syncts = syncts;
|
||||
|
||||
/*g_printf("a2dp tx mount syncts : 0x%x, %u\n", (u32)syncts, timestamp);*/
|
||||
if (hdl->reference_network == 0xff) {
|
||||
hdl->reference_network = network;
|
||||
}
|
||||
list_add(&node->entry, &hdl->sync_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a2dp_tx_unmount_syncts(struct a2dp_tx_hdl *hdl, void *syncts)
|
||||
{
|
||||
struct a2dp_tx_sync_node *node;
|
||||
|
||||
#if TIMESTAMP_USE_AUDIO_JIFFIES
|
||||
if (!hdl->tx_param.jl_timestamp) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
list_for_each_entry(node, &hdl->sync_list, entry) {
|
||||
if (node->syncts == syncts) {
|
||||
goto unmount;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
unmount:
|
||||
/*g_printf("a2dp tx unmount syncts : 0x%x\n", syncts);*/
|
||||
list_del(&node->entry);
|
||||
free(node);
|
||||
}
|
||||
|
||||
static int a2dp_tx_syncts_handler(struct a2dp_tx_hdl *hdl, struct audio_syncts_ioc_params *params)
|
||||
{
|
||||
if (!params) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (params->cmd) {
|
||||
case AUDIO_SYNCTS_MOUNT_ON_SNDPCM:
|
||||
a2dp_tx_mount_syncts(hdl, (void *)params->data[0], params->data[1], params->data[2]);
|
||||
break;
|
||||
case AUDIO_SYNCTS_UMOUNT_ON_SNDPCM:
|
||||
a2dp_tx_unmount_syncts(hdl, (void *)params->data[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a2dp_tx_buffer_latency(struct a2dp_tx_hdl *hdl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a2dp_tx_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)iport->node->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
a2dp_tx_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_SET_BTADDR:
|
||||
a2dp_tx_set_bt_addr(hdl, (void *)arg);
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
a2dp_tx_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
a2dp_tx_stop(hdl);
|
||||
break;
|
||||
case NODE_IOC_SYNCTS:
|
||||
a2dp_tx_syncts_handler(hdl, (struct audio_syncts_ioc_params *)arg);
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= a2dp_tx_ioc_fmt_nego(iport);
|
||||
break;
|
||||
case NODE_IOC_GET_DELAY:
|
||||
return a2dp_tx_buffer_latency(hdl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a2dp_tx_release(struct stream_node *node)
|
||||
{
|
||||
struct a2dp_tx_hdl *hdl = (struct a2dp_tx_hdl *)node->private_data;
|
||||
|
||||
if (hdl) {
|
||||
free(hdl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
REGISTER_STREAM_NODE_ADAPTER(a2dp_tx_adapter) = {
|
||||
.name = "a2dp_tx",
|
||||
.uuid = NODE_UUID_A2DP_TX,
|
||||
.bind = a2dp_tx_bind,
|
||||
.ioctl = a2dp_tx_ioctl,
|
||||
.release = a2dp_tx_release,
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,214 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".agc_node.data.bss")
|
||||
#pragma data_seg(".agc_node.data")
|
||||
#pragma const_seg(".agc_node.text.const")
|
||||
#pragma code_seg(".agc_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "app_config.h"
|
||||
#include "audio_config.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "frame_length_adaptive.h"
|
||||
#include "audio_agc.h"
|
||||
|
||||
|
||||
#if 1
|
||||
#define agc_log printf
|
||||
#else
|
||||
#define agc_log(...)
|
||||
#endif/*log_en*/
|
||||
|
||||
#define AGC_FRAME_SIZE 128
|
||||
|
||||
#if TCFG_AGC_NODE_ENABLE
|
||||
|
||||
struct agc_cfg_t {
|
||||
float AGC_max_lvl; //最大幅度压制,range[0 : -90] dB
|
||||
float AGC_fade_in_step; //淡入步进,range[0.1 : 5] dB
|
||||
float AGC_fade_out_step; //淡出步进,range[0.1 : 5] dB
|
||||
float AGC_max_gain; //放大上限, range[-90 : 40] dB
|
||||
float AGC_min_gain; //放大下限, range[-90 : 40] dB
|
||||
float AGC_speech_thr; //放大阈值, range[-70 : -40] dB
|
||||
} __attribute__((packed));
|
||||
|
||||
struct agc_node_hdl {
|
||||
u16 sr;
|
||||
void *agc;
|
||||
struct stream_frame *out_frame;
|
||||
struct stream_node *node; //节点句柄
|
||||
agc_param_t parm;
|
||||
struct frame_length_adaptive_hdl *olen_adaptive;//输出长度适配
|
||||
};
|
||||
|
||||
int agc_param_cfg_read(struct stream_node *node)
|
||||
{
|
||||
struct agc_cfg_t config;
|
||||
struct agc_node_hdl *hdl = (struct agc_node_hdl *)node->private_data;
|
||||
if (!hdl) {
|
||||
return -1 ;
|
||||
}
|
||||
/*
|
||||
*获取配置文件内的参数,及名字
|
||||
* */
|
||||
if (!jlstream_read_node_data_new(NODE_UUID_AGC, node->subid, (void *)&config, NULL)) {
|
||||
printf("%s, read node data err\n", __FUNCTION__);
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
memcpy(&hdl->parm, &config, sizeof(config));
|
||||
|
||||
agc_log("AGC_max_lvl %d/10\n", (int)(hdl->parm.AGC_max_lvl * 10));
|
||||
agc_log("AGC_fade_in_step %d/10\n", (int)(hdl->parm.AGC_fade_in_step * 10));
|
||||
agc_log("AGC_fade_out_step %d/10\n", (int)(hdl->parm.AGC_fade_out_step * 10));
|
||||
agc_log("AGC_max_gain %d/10\n", (int)(hdl->parm.AGC_max_gain * 10));
|
||||
agc_log("AGC_min_gain %d/10\n", (int)(hdl->parm.AGC_min_gain * 10));
|
||||
agc_log("AGC_speech_thr %d/10\n", (int)(hdl->parm.AGC_speech_thr * 10));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void agc_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct agc_node_hdl *hdl = (struct agc_node_hdl *)iport->private_data;
|
||||
struct stream_node *node = iport->node;
|
||||
struct stream_frame *in_frame;
|
||||
int wlen;
|
||||
|
||||
while (1) {
|
||||
in_frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!in_frame) {
|
||||
break;
|
||||
}
|
||||
int out_frame_len = in_frame->len > (hdl->parm.AGC_frame_size << 1) ?
|
||||
in_frame->len : (hdl->parm.AGC_frame_size << 1);
|
||||
if (!hdl->out_frame) {
|
||||
|
||||
hdl->out_frame = jlstream_get_frame(node->oport, out_frame_len);
|
||||
if (!hdl->out_frame) {
|
||||
jlstream_return_frame(iport, in_frame);
|
||||
return;
|
||||
}
|
||||
}
|
||||
wlen = audio_agc_run(hdl->agc, (s16 *)in_frame->data,
|
||||
(s16 *)hdl->out_frame->data, in_frame->len);
|
||||
|
||||
/*保证节点输入输出长度一样*/
|
||||
if (!hdl->olen_adaptive) {
|
||||
hdl->olen_adaptive = frame_length_adaptive_open(in_frame->len, out_frame_len);
|
||||
}
|
||||
int len = frame_length_adaptive_run(hdl->olen_adaptive, (s16 *)hdl->out_frame->data, (s16 *)hdl->out_frame->data, wlen);
|
||||
if (len) {
|
||||
hdl->out_frame->len = len;
|
||||
jlstream_push_frame(node->oport, hdl->out_frame); //将数据推到oport
|
||||
hdl->out_frame = NULL;
|
||||
}
|
||||
jlstream_free_frame(in_frame); //释放iport资源
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int agc_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct agc_node_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
agc_param_cfg_read(node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void agc_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = agc_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int agc_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void agc_ioc_start(struct agc_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
hdl->parm.AGC_samplerate = fmt->sample_rate;
|
||||
hdl->parm.AGC_frame_size = AGC_FRAME_SIZE;
|
||||
agc_log("AGC_samplerate : %d", hdl->parm.AGC_samplerate);
|
||||
agc_log("AGC_frame_size : %d", hdl->parm.AGC_frame_size);
|
||||
hdl->agc = audio_agc_open(&hdl->parm);
|
||||
}
|
||||
|
||||
/*节点stop函数*/
|
||||
static void agc_ioc_stop(struct agc_node_hdl *hdl)
|
||||
{
|
||||
if (hdl->agc) {
|
||||
audio_agc_close(hdl->agc);
|
||||
hdl->agc = NULL;
|
||||
}
|
||||
if (hdl->olen_adaptive) {
|
||||
frame_length_adaptive_close(hdl->olen_adaptive);
|
||||
hdl->olen_adaptive = NULL;
|
||||
}
|
||||
if (hdl->out_frame) {
|
||||
jlstream_free_frame(hdl->out_frame);
|
||||
hdl->out_frame = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int agc_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct agc_node_hdl *hdl = (struct agc_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
agc_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= agc_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
agc_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
agc_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void agc_adapter_release(struct stream_node *node)
|
||||
{
|
||||
struct agc_node_hdl *hdl = (struct agc_node_hdl *)node->private_data;
|
||||
if (!hdl) {
|
||||
return;
|
||||
}
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(agc_node_adapter) = {
|
||||
.name = "agc",
|
||||
.uuid = NODE_UUID_AGC,
|
||||
.bind = agc_adapter_bind,
|
||||
.ioctl = agc_adapter_ioctl,
|
||||
.release = agc_adapter_release,
|
||||
};
|
||||
|
||||
//注册工具在线调试
|
||||
REGISTER_ONLINE_ADJUST_TARGET(agc_process) = {
|
||||
.uuid = NODE_UUID_AGC,
|
||||
};
|
||||
|
||||
#endif /*TCFG_AGC_NODE_ENABLE*/
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "source_node.h"
|
||||
#include "app_config.h"
|
||||
#include "reference_time.h"
|
||||
#include "ai_rx_player.h"
|
||||
/* #include "rcsp_translator.h" */
|
||||
|
||||
|
||||
#if TCFG_AI_RX_NODE_ENABLE
|
||||
|
||||
struct ai_rx_cb ai_rx_cb_t = {0};
|
||||
|
||||
extern const int OPUS_SRINDEX; //选择opus解码文件的帧大小,0代表一帧40字节,1代表一帧80字节,2代表一帧160字节
|
||||
static int ai_fseek(void *_hdl, u32 fpos)
|
||||
{
|
||||
struct ai_rx_file_handle *hdl = (struct ai_rx_file_handle *)_hdl;
|
||||
|
||||
if (!hdl->file) {
|
||||
return -1;
|
||||
}
|
||||
return hdl->file_ops->seek(hdl->file, fpos, SEEK_SET);
|
||||
}
|
||||
|
||||
static int ai_fread(void *_hdl, u8 *data, int size)
|
||||
{
|
||||
u32 rets_addr;
|
||||
__asm__ volatile("%0 = rets ;" : "=r"(rets_addr));
|
||||
struct ai_rx_file_handle *hdl = (struct ai_rx_file_handle *)_hdl;
|
||||
|
||||
if (!hdl->file) {
|
||||
return 0;
|
||||
}
|
||||
return hdl->file_ops->read(hdl->file, data, size);
|
||||
}
|
||||
static int ai_ioc_get_fmt(struct ai_rx_file_handle *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
return hdl->file_ops->get_fmt(hdl->file, fmt);
|
||||
}
|
||||
|
||||
|
||||
static void ai_ioc_set_file(struct ai_rx_file_handle *hdl, struct stream_file_info *info)
|
||||
{
|
||||
hdl->file = info->file;
|
||||
hdl->file_ops = info->ops;
|
||||
}
|
||||
|
||||
|
||||
extern u32 bt_audio_conn_clock_time(void *addr);
|
||||
static enum stream_node_state ai_rx_get_frame(void *file, struct stream_frame **pframe)
|
||||
{
|
||||
struct ai_rx_file_handle *hdl = (struct ai_rx_file_handle *)file;
|
||||
|
||||
if (ai_rx_cb_t.get_frame_event_cb != NULL) { // 用于ai应用的音频流播放
|
||||
return ai_rx_cb_t.get_frame_event_cb(hdl, pframe);
|
||||
}
|
||||
|
||||
struct stream_frame *frame;
|
||||
u8 fram_len = 0;
|
||||
|
||||
switch (OPUS_SRINDEX) {
|
||||
case FRAME_LEN_40:
|
||||
fram_len = 40;
|
||||
break;
|
||||
case FRAME_LEN_80:
|
||||
fram_len = 80;
|
||||
break;
|
||||
case FRAME_LEN_160:
|
||||
fram_len = 160;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
frame = jlstream_get_frame(hdl->node->oport, fram_len);
|
||||
if (frame == NULL) {
|
||||
*pframe = NULL;
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
int rlen = fread(frame->data, fram_len, 1, hdl->file);
|
||||
frame->len = rlen;
|
||||
*pframe = frame;
|
||||
return NODE_STA_RUN;
|
||||
|
||||
}
|
||||
|
||||
static void *ai_rx_file_init(void *priv, struct stream_node *node)
|
||||
{
|
||||
struct ai_rx_file_handle *hdl = (struct ai_rx_file_handle *)zalloc(sizeof(struct ai_rx_file_handle));
|
||||
|
||||
hdl->node = node;
|
||||
node->type |= NODE_TYPE_FLOW_CTRL;//NODE_TYPE_IRQ;
|
||||
return hdl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static int ai_rx_file_set_bt_addr(struct ai_rx_file_handle *hdl, void *bt_addr)
|
||||
{
|
||||
hdl->bt_addr = (void *)bt_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static void ai_rx_tick_handler(void *priv, u8 source)
|
||||
{
|
||||
struct ai_rx_file_handle *hdl = (struct ai_rx_file_handle *)priv;
|
||||
|
||||
if (hdl->start && (hdl->node->state & NODE_STA_SOURCE_NO_DATA)) {
|
||||
jlstream_wakeup_thread(NULL, hdl->node, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int ai_rx_file_get_fmt(struct ai_rx_file_handle *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
#if 0
|
||||
fmt->coding_type = AUDIO_CODING_OPUS;
|
||||
fmt->sample_rate = 16000;
|
||||
fmt->channel_mode = AUDIO_CH_MIX;
|
||||
#else
|
||||
|
||||
extern const int CONFIG_OGG_OPUS_DEC_SUPPORT;
|
||||
|
||||
#if TCFG_DEC_OGG_OPUS_ENABLE
|
||||
if (CONFIG_OGG_OPUS_DEC_SUPPORT) {
|
||||
fmt->sample_rate = 48000;
|
||||
}
|
||||
return hdl->file_ops->get_fmt(hdl->file, fmt);
|
||||
#else
|
||||
fmt->coding_type = hdl->param.coding_type;
|
||||
fmt->sample_rate = hdl->param.sample_rate;
|
||||
fmt->channel_mode = hdl->param.channel_mode;
|
||||
fmt->frame_dms = hdl->param.frame_dms;
|
||||
fmt->bit_rate = hdl->param.bit_rate;
|
||||
printf("%s 0x%x, %d, %d, %d, %d\n", __FUNCTION__, fmt->coding_type, fmt->sample_rate, fmt->channel_mode, fmt->frame_dms, fmt->bit_rate);
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ai_rx_file_start(struct ai_rx_file_handle *hdl)
|
||||
{
|
||||
if (hdl->start) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdl->start = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ai_rx_file_stop(struct ai_rx_file_handle *hdl)
|
||||
{
|
||||
if (!hdl->start) {
|
||||
return 0;
|
||||
}
|
||||
hdl->start = 0;
|
||||
if (hdl->reference) {
|
||||
audio_reference_clock_exit(hdl->reference);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ai_rx_file_ioctl(void *file, int cmd, int arg)
|
||||
{
|
||||
int err = 0;
|
||||
struct ai_rx_file_handle *hdl = (struct ai_rx_file_handle *)file;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_SET_BTADDR:
|
||||
ai_rx_file_set_bt_addr(hdl, (void *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
memcpy(&hdl->param, (struct ai_rx_player_param *)arg, sizeof(struct ai_rx_player_param));
|
||||
break;
|
||||
case NODE_IOC_GET_FMT:
|
||||
ai_rx_file_get_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_FILE:
|
||||
ai_ioc_set_file(hdl, (struct stream_file_info *)arg);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
ai_rx_file_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SET_PARAM:
|
||||
hdl->source = (u8)arg;
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
ai_rx_file_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ai_rx_file_release(void *file)
|
||||
{
|
||||
struct ai_rx_file_handle *hdl = (struct ai_rx_file_handle *)file;
|
||||
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
REGISTER_SOURCE_NODE_PLUG(AI_RX_file_plug) = {
|
||||
.uuid = NODE_UUID_AI_RX,
|
||||
.init = ai_rx_file_init,
|
||||
#if TCFG_DEC_OGG_OPUS_ENABLE
|
||||
.read = ai_fread,
|
||||
.seek = ai_fseek,
|
||||
#else
|
||||
.get_frame = ai_rx_get_frame,
|
||||
#endif
|
||||
.ioctl = ai_rx_file_ioctl,
|
||||
.release = ai_rx_file_release,
|
||||
};
|
||||
|
||||
|
||||
#endif /*TCFG_AI_RX_NODE_ENABLE*/
|
||||
@@ -0,0 +1,147 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".ai_tx_node.data.bss")
|
||||
#pragma data_seg(".ai_tx_node.data")
|
||||
#pragma const_seg(".ai_tx_node.text.const")
|
||||
#pragma code_seg(".ai_tx_node.text")
|
||||
#endif
|
||||
|
||||
#include "jlstream.h"
|
||||
/* #include "classic/hci_lmp.h" */
|
||||
#include "media/audio_base.h"
|
||||
#include "app_config.h"
|
||||
#include "codec/opus_codec_api.h"
|
||||
|
||||
#if TCFG_AI_TX_NODE_ENABLE
|
||||
|
||||
struct ai_tx_hdl {
|
||||
u8 start;
|
||||
struct stream_fmt fmt;
|
||||
OPUS_ENC_PARA param;
|
||||
int (*tx_func)(u8 *buf, u32 len);
|
||||
};
|
||||
|
||||
|
||||
extern int rec_enc_output(void *priv, void *buf, int len);
|
||||
static void ai_tx_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct ai_tx_hdl *hdl = (struct ai_tx_hdl *)iport->private_data;
|
||||
struct stream_frame *frame;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note);
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
#if (BT_MIC_EN)
|
||||
rec_enc_output(NULL, frame->data, frame->len);
|
||||
#endif
|
||||
if (hdl->tx_func) {
|
||||
hdl->tx_func(frame->data, frame->len);
|
||||
}
|
||||
jlstream_free_frame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
static int ai_tx_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ai_tx_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
struct ai_tx_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
|
||||
|
||||
iport->private_data = hdl;
|
||||
iport->handle_frame = ai_tx_handle_frame;
|
||||
}
|
||||
static int ai_tx_ioc_fmt_nego(struct ai_tx_hdl *hdl, struct stream_iport *iport)
|
||||
/* static int ai_tx_ioc_fmt_nego(struct stream_iport *iport) */
|
||||
{
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
#if 0
|
||||
in_fmt->coding_type = hdl->fmt.coding_type;
|
||||
in_fmt->sample_rate = 16000;
|
||||
in_fmt->channel_mode = AUDIO_CH_MIX;
|
||||
#else
|
||||
|
||||
|
||||
|
||||
if (in_fmt->coding_type != AUDIO_CODING_UNKNOW) {
|
||||
hdl->fmt.coding_type = in_fmt->coding_type;
|
||||
} else {
|
||||
in_fmt->coding_type = hdl->fmt.coding_type;
|
||||
}
|
||||
hdl->fmt.sample_rate = in_fmt->sample_rate;
|
||||
hdl->fmt.frame_dms = in_fmt->frame_dms;
|
||||
hdl->fmt.channel_mode = in_fmt->channel_mode;
|
||||
hdl->fmt.bit_rate = in_fmt->bit_rate;
|
||||
|
||||
/* printf("ai_tx: coding_type: %x", in_fmt->coding_type); */
|
||||
/* printf("ai_tx: sample_rate: %d", in_fmt->sample_rate); */
|
||||
/* printf("ai_tx: frame_dms: %d", in_fmt->frame_dms); */
|
||||
/* printf("ai_tx: channel_mode: %d", hdl->fmt.channel_mode); */
|
||||
/* printf("ai_tx: bit_rate: %d", hdl->fmt.bit_rate); */
|
||||
|
||||
if (in_fmt->coding_type == AUDIO_CODING_UNKNOW) {
|
||||
g_printf("ai_tx coding_type nego fail\n");
|
||||
return NEGO_STA_CONTINUE;
|
||||
}
|
||||
if (in_fmt->sample_rate == 0) {
|
||||
return NEGO_STA_CONTINUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return NEGO_STA_ACCPTED;
|
||||
}
|
||||
|
||||
static int ai_tx_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
struct ai_tx_hdl *hdl = (struct ai_tx_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
ai_tx_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
free(hdl);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
struct stream_fmt *fmt = (struct stream_fmt *)arg;
|
||||
hdl->fmt.coding_type = fmt->coding_type;
|
||||
hdl->fmt.sample_rate = fmt->sample_rate;
|
||||
hdl->fmt.bit_rate = fmt->sample_rate;
|
||||
hdl->fmt.channel_mode = fmt->channel_mode;
|
||||
/* hdl->fmt.frame_dms = fmt->frame_dms; */
|
||||
break;
|
||||
case NODE_IOC_SET_PRIV_FMT:
|
||||
hdl->tx_func = (int (*)(u8 *, u32))arg;
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= ai_tx_ioc_fmt_nego(hdl, iport);
|
||||
break;
|
||||
case NODE_IOC_GET_DELAY:
|
||||
break;
|
||||
/* return lmp_private_get_ai_tx_packet_num() * 75; */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ai_tx_release(struct stream_node *node)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
REGISTER_STREAM_NODE_ADAPTER(ai_tx_adapter) = {
|
||||
.name = "ai_tx",
|
||||
.uuid = NODE_UUID_AI_TX,
|
||||
.bind = ai_tx_bind,
|
||||
.ioctl = ai_tx_ioctl,
|
||||
.release = ai_tx_release,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
#include "system/includes.h"
|
||||
#include "source_node.h"
|
||||
#include "app_config.h"
|
||||
#include "reference_time.h"
|
||||
#include "avi_audio_player.h"
|
||||
/* #include "rcsp_translator.h" */
|
||||
|
||||
#if TCFG_VIDEO_DEC_NODE_ENABLE
|
||||
|
||||
extern u8 avi_audio_start;
|
||||
extern u8 get_avi_audio_status();
|
||||
|
||||
struct avi_audio_file_handle {
|
||||
void *file;
|
||||
void *bt_addr;
|
||||
u8 start;
|
||||
u8 source;
|
||||
u8 reference;
|
||||
struct stream_node *node;
|
||||
u32 play_latency; // us
|
||||
struct avi_audio_player_param param;
|
||||
const struct stream_file_ops *file_ops;
|
||||
};
|
||||
|
||||
extern const int OPUS_SRINDEX; // 选择opus解码文件的帧大小,0代表一帧40字节,1代表一帧80字节,2代表一帧160字节
|
||||
extern u32 bt_audio_conn_clock_time(void *addr);
|
||||
extern cbuffer_t avi_pcm_cbuf;
|
||||
extern OS_MUTEX avi_pcm_mutex;
|
||||
|
||||
static int avi_fseek(void *_hdl, u32 fpos)
|
||||
{
|
||||
struct avi_audio_file_handle *hdl = (struct avi_audio_file_handle *)_hdl;
|
||||
|
||||
if (!hdl->file) {
|
||||
return -1;
|
||||
}
|
||||
return hdl->file_ops->seek(hdl->file, fpos, SEEK_SET);
|
||||
}
|
||||
|
||||
static int avi_fread(void *_hdl, u8 *data, int size)
|
||||
{
|
||||
u32 rets_addr;
|
||||
__asm__ volatile("%0 = rets ;" : "=r"(rets_addr));
|
||||
struct avi_audio_file_handle *hdl = (struct avi_audio_file_handle *)_hdl;
|
||||
|
||||
if (!hdl->file) {
|
||||
return 0;
|
||||
}
|
||||
return hdl->file_ops->read(hdl->file, data, size);
|
||||
}
|
||||
static int avi_ioc_get_fmt(struct avi_audio_file_handle *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
return hdl->file_ops->get_fmt(hdl->file, fmt);
|
||||
}
|
||||
|
||||
static void avi_ioc_set_file(struct avi_audio_file_handle *hdl, struct stream_file_info *info)
|
||||
{
|
||||
hdl->file = info->file;
|
||||
hdl->file_ops = info->ops;
|
||||
}
|
||||
|
||||
|
||||
static enum stream_node_state avi_audio_get_frame(void *file, struct stream_frame **pframe)
|
||||
{
|
||||
struct avi_audio_file_handle *hdl = (struct avi_audio_file_handle *)file;
|
||||
struct stream_frame *frame;
|
||||
u16 fram_len = 512; // 帧长度
|
||||
|
||||
static int offset = 0;
|
||||
|
||||
frame = jlstream_get_frame(hdl->node->oport, fram_len);
|
||||
if (frame == NULL) {
|
||||
*pframe = NULL;
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
// int rlen = fread(frame->data, fram_len, 1, hdl->file);
|
||||
#if TCFG_VIDEO_DIAL_ENABLE // avi 音频pcm数据读取
|
||||
|
||||
if (get_avi_audio_status()) {
|
||||
os_mutex_pend(&avi_pcm_mutex, 0);
|
||||
u32 data_len = cbuf_get_data_size(&avi_pcm_cbuf);
|
||||
// printf("[cbuf_get_data_size] data_len:%d\n", data_len);
|
||||
if (data_len >= fram_len) {
|
||||
cbuf_read(&avi_pcm_cbuf, frame->data, fram_len);
|
||||
// printf("[cbuf_read] fram_len:%d\n", fram_len);
|
||||
os_mutex_post(&avi_pcm_mutex);
|
||||
frame->len = fram_len;
|
||||
// printf("[avi_audio_get_frame] frame->len:%d\n", frame->len);
|
||||
} else {
|
||||
os_mutex_post(&avi_pcm_mutex);
|
||||
// puts("silent audio data\n");
|
||||
memset(frame->data, 0, fram_len); // 数据不足时填零静音
|
||||
frame->len = 0;//fram_len;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
//如果没有开视屏表盘 用到该流程需要自行添加数据源。
|
||||
frame->len = 0;
|
||||
#endif
|
||||
*pframe = frame;
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
|
||||
static void *avi_audio_file_init(void *priv, struct stream_node *node)
|
||||
{
|
||||
struct avi_audio_file_handle *hdl = (struct avi_audio_file_handle *)zalloc(sizeof(struct avi_audio_file_handle));
|
||||
|
||||
hdl->node = node;
|
||||
node->type |= NODE_TYPE_FLOW_CTRL; // NODE_TYPE_IRQ;
|
||||
return hdl;
|
||||
}
|
||||
|
||||
static int avi_audio_file_set_bt_addr(struct avi_audio_file_handle *hdl, void *bt_addr)
|
||||
{
|
||||
hdl->bt_addr = (void *)bt_addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
static void avi_audio_tick_handler(void *priv, u8 source)
|
||||
{
|
||||
struct avi_audio_file_handle *hdl = (struct avi_audio_file_handle *)priv;
|
||||
|
||||
if (hdl->start && (hdl->node->state & NODE_STA_SOURCE_NO_DATA)) {
|
||||
jlstream_wakeup_thread(NULL, hdl->node, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static int avi_audio_file_get_fmt(struct avi_audio_file_handle *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
#if 0
|
||||
fmt->coding_type = AUDIO_CODING_OPUS;
|
||||
fmt->sample_rate = 16000;
|
||||
fmt->channel_mode = AUDIO_CH_MIX;
|
||||
#else
|
||||
|
||||
extern const int CONFIG_OGG_OPUS_DEC_SUPPORT;
|
||||
|
||||
#if TCFG_DEC_OGG_OPUS_ENABLE
|
||||
if (CONFIG_OGG_OPUS_DEC_SUPPORT) {
|
||||
fmt->sample_rate = 48000;
|
||||
}
|
||||
return hdl->file_ops->get_fmt(hdl->file, fmt);
|
||||
#else
|
||||
fmt->coding_type = hdl->param.coding_type;
|
||||
fmt->sample_rate = hdl->param.sample_rate;
|
||||
fmt->channel_mode = hdl->param.channel_mode;
|
||||
fmt->frame_dms = hdl->param.frame_dms;
|
||||
fmt->bit_rate = hdl->param.bit_rate;
|
||||
printf("%s 0x%x, %d, %d, %d, %d\n", __FUNCTION__, fmt->coding_type, fmt->sample_rate, fmt->channel_mode, fmt->frame_dms, fmt->bit_rate);
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avi_audio_file_start(struct avi_audio_file_handle *hdl)
|
||||
{
|
||||
if (hdl->start) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdl->start = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avi_audio_file_stop(struct avi_audio_file_handle *hdl)
|
||||
{
|
||||
if (!hdl->start) {
|
||||
return 0;
|
||||
}
|
||||
hdl->start = 0;
|
||||
if (hdl->reference) {
|
||||
audio_reference_clock_exit(hdl->reference);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avi_audio_file_ioctl(void *file, int cmd, int arg)
|
||||
{
|
||||
int err = 0;
|
||||
struct avi_audio_file_handle *hdl = (struct avi_audio_file_handle *)file;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_SET_BTADDR:
|
||||
avi_audio_file_set_bt_addr(hdl, (void *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
memcpy(&hdl->param, (struct avi_audio_player_param *)arg, sizeof(struct avi_audio_player_param));
|
||||
break;
|
||||
case NODE_IOC_GET_FMT:
|
||||
avi_audio_file_get_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_FILE:
|
||||
avi_ioc_set_file(hdl, (struct stream_file_info *)arg);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
avi_audio_file_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SET_PARAM:
|
||||
hdl->source = (u8)arg;
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
avi_audio_file_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void avi_audio_file_release(void *file)
|
||||
{
|
||||
struct avi_audio_file_handle *hdl = (struct avi_audio_file_handle *)file;
|
||||
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
REGISTER_SOURCE_NODE_PLUG(avi_audio_file_plug) = {
|
||||
.uuid = NODE_UUID_VIDEO_DEC,
|
||||
.init = avi_audio_file_init,
|
||||
#if TCFG_DEC_OGG_OPUS_ENABLE
|
||||
.read = avi_fread,
|
||||
.seek = avi_fseek,
|
||||
#else
|
||||
.get_frame = avi_audio_get_frame,
|
||||
#endif
|
||||
.ioctl = avi_audio_file_ioctl,
|
||||
.release = avi_audio_file_release,
|
||||
};
|
||||
|
||||
#endif /*TCFG_avi_audio_NODE_ENABLE*/
|
||||
@@ -0,0 +1,123 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".channle_swap_node.data.bss")
|
||||
#pragma data_seg(".channle_swap_node.data")
|
||||
#pragma const_seg(".channle_swap_node.text.const")
|
||||
#pragma code_seg(".channle_swap_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "overlay_code.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_CHANNEL_SWAP_NODE_ENABLE
|
||||
|
||||
static void channle_swap_node_run(s16 *ptr, int npoint)
|
||||
{
|
||||
int tmp32_1;
|
||||
asm volatile(
|
||||
" 1: \n\t"
|
||||
" rep %[npoint] { \n\t"
|
||||
" %[tmp32_1] = [%[ptr]] \n\t"
|
||||
" h[%[ptr]++=2] = %[tmp32_1].h \n\t"
|
||||
" h[%[ptr]++=2] = %[tmp32_1].l \n\t"
|
||||
" } \n\t"
|
||||
" if( %[npoint] != 0 ) goto 1b \n\t"
|
||||
:
|
||||
[ptr]"=&r"(ptr),
|
||||
[tmp32_1]"=&r"(tmp32_1),
|
||||
[npoint]"=&r"(npoint)
|
||||
:
|
||||
"0"(ptr),
|
||||
"1"(tmp32_1),
|
||||
"2"(npoint)
|
||||
);
|
||||
}
|
||||
|
||||
static void channle_swap_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
struct stream_node *node = iport->node;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note);
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
channle_swap_node_run((s16 *)frame->data, frame->len >> 2); //执行时间 46us/4096 byte
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, frame);
|
||||
} else {
|
||||
jlstream_free_frame(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int channle_swap_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void channle_swap_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = channle_swap_handle_frame;
|
||||
iport->private_data = iport->node->private_data;
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int channle_swap_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void channle_swap_ioc_start(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*节点stop函数*/
|
||||
static void channle_swap_ioc_stop(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int channle_swap_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
channle_swap_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= channle_swap_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
channle_swap_ioc_start();
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
channle_swap_ioc_stop();
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void channle_swap_adapter_release(struct stream_node *node)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
REGISTER_STREAM_NODE_ADAPTER(channle_swap_node_adapter) = {
|
||||
.name = "channle_swap",
|
||||
.uuid = NODE_UUID_CHANNLE_SWAP,
|
||||
.bind = channle_swap_adapter_bind,
|
||||
.ioctl = channle_swap_adapter_ioctl,
|
||||
.release = channle_swap_adapter_release,
|
||||
//固定要求输出为双声道
|
||||
.ability_channel_in = 2,
|
||||
.ability_channel_out = 2,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,655 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".cvp_3mic_node.data.bss")
|
||||
#pragma data_seg(".cvp_3mic_node.data")
|
||||
#pragma const_seg(".cvp_3mic_node.text.const")
|
||||
#pragma code_seg(".cvp_3mic_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "circular_buf.h"
|
||||
#include "cvp_node.h"
|
||||
#include "app_config.h"
|
||||
#include "cvp_tms.h"
|
||||
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
#include "audio_dut_control.h"
|
||||
#endif
|
||||
|
||||
/*(双MIC+ANS通话) || (双MIC+DNS通话)*/
|
||||
#if TCFG_AUDIO_CVP_3MIC_MODE
|
||||
|
||||
#define CVP_INPUT_SIZE 256*3 //CVP输入缓存,short
|
||||
|
||||
//------------------stream.bin CVP参数文件解析结构-START---------------//
|
||||
struct CVP_MIC_SEL_CONFIG {
|
||||
u8 talk_mic; //主MIC通道选择
|
||||
u8 talk_ref_mic; //副MIC通道选择
|
||||
u8 talk_fb_mic; //FBMIC通道选择
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_REF_MIC_CONFIG {
|
||||
u8 en; //ref 回采硬使能
|
||||
u8 ref_mic_ch; //ref 硬回采MIC通道选择
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_PRE_GAIN_CONFIG {
|
||||
u8 en;
|
||||
float talk_mic_gain; //主MIC前级数字增益,default:0dB(-90 ~ 40dB)
|
||||
float talk_ref_mic_gain; //副MIC前级数字增益,default:0dB(-90 ~ 40dB)
|
||||
float talk_fb_mic_gain; //FB MIC前级数字增益,default:0dB(-90 ~ 40dB)
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_AEC_CONFIG {
|
||||
u8 en;
|
||||
int aec_process_maxfrequency; //default:8000,range[3000:8000]
|
||||
int aec_process_minfrequency; //default:0,range[0:1000]
|
||||
int af_length; //default:128 range[128:256]
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_NLP_CONFIG {
|
||||
u8 en;
|
||||
int nlp_process_maxfrequency; //default:8000,range[3000:8000]
|
||||
int nlp_process_minfrequency; //default:0,range[0:1000]
|
||||
float overdrive; //default:1,range[0:30]
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_ENC_CONFIG {
|
||||
u8 en;
|
||||
int enc_process_maxfreq; //default:8000,range[3000:8000]
|
||||
int enc_process_minfreq; //default:0,range[0:1000]
|
||||
int sir_maxfreq; //default:3000,range[1000:8000]
|
||||
float mic_distance; //default:0.015,range[0.035:0.015]
|
||||
float target_signal_degradation;//default:1,range[0:1]
|
||||
float enc_aggressfactor; //default:4.f,range[0:4]
|
||||
float enc_minsuppress; //default:0.09f,range[0:0.1]
|
||||
float Tri_SnrThreshold0; //sir设定阈值
|
||||
float Tri_SnrThreshold1; //sir设定阈值
|
||||
float Tri_CompenDb; //mic增益补偿, dB
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_DNS_CONFIG {
|
||||
u8 en;
|
||||
float aggressfactor; //default:1.25,range[1:2]
|
||||
float minsuppress; //default:0.04,range[0.01:0.1]
|
||||
float init_noise_lvl; //default:-75dB,range[-100:-30]
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_AGC_CONFIG {
|
||||
u8 en;
|
||||
u8 agc_type;
|
||||
/*AGC*/
|
||||
float ndt_fade_in; //单端讲话淡入步进default: 1.3f(0.1 ~ 5 dB)
|
||||
float ndt_fade_out; //单端讲话淡出步进default: 0.7f(0.1 ~ 5 dB)
|
||||
float dt_fade_in; //双端讲话淡入步进default: 1.3f(0.1 ~ 5 dB)
|
||||
float dt_fade_out; //双端讲话淡出步进default: 0.7f(0.1 ~ 5 dB)
|
||||
float ndt_max_gain; //单端讲话放大上限,default: 12.f(0 ~ 24 dB)
|
||||
float ndt_min_gain; //单端讲话放大下限,default: 0.f(-20 ~ 24 dB)
|
||||
float ndt_speech_thr; //单端讲话放大阈值,default: -50.f(-70 ~ -40 dB)
|
||||
float dt_max_gain; //双端讲话放大上限,default: 12.f(0 ~ 24 dB)
|
||||
float dt_min_gain; //双端讲话放大下限,default: 0.f(-20 ~ 24 dB)
|
||||
float dt_speech_thr; //双端讲话放大阈值,default: -40.f(-70 ~ -40 dB)
|
||||
float echo_present_thr; //单端双端讲话阈值,default:-70.f(-70 ~ -40 dB)
|
||||
/*JLSP AGC*/
|
||||
int min_mag_db_level;
|
||||
int max_mag_db_level;
|
||||
int addition_mag_db_level;
|
||||
int clip_mag_db_level;
|
||||
int floor_mag_db_level;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_WNC_CONFIG {
|
||||
u8 en;
|
||||
float wn_msc_th; //双麦非相关性阈值,default:0.6f(0 ~ 1)
|
||||
float ms_th; //麦增益能量阈值, default:80.f(0-255)dB
|
||||
float wn_gain_offset;
|
||||
} __attribute__((packed));
|
||||
|
||||
/*MFDT Parameters*/
|
||||
struct CVP_MFDT_CONFIG {
|
||||
u8 en;
|
||||
float detect_time; // // 检测时间s,影响状态切换的速度
|
||||
float detect_eng_diff_thr; // 0~-90 dB 两个mic能量差异持续大于此阈值超过检测时间则会检测为故障
|
||||
float detect_eng_lowerbound; // 0~-90 dB 当处于故障状态时,正常的mic能量大于此阈值才会检测能量差异,避免安静环境下误判切回正常状态
|
||||
int MalfuncDet_MaxFrequency;// 检测信号的最大频率成分
|
||||
int MalfuncDet_MinFrequency;// 检测信号的最小频率成分
|
||||
int OnlyDetect;// 0 -> 故障切换到单mic模式, 1-> 只检测不切换
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_DEBUG_CONFIG {
|
||||
u8 output_sel; //输出数据选择
|
||||
} __attribute__((packed));
|
||||
|
||||
struct cvp_cfg_t {
|
||||
struct CVP_MIC_SEL_CONFIG mic_sel;
|
||||
struct CVP_REF_MIC_CONFIG ref_mic;
|
||||
struct CVP_PRE_GAIN_CONFIG pre_gain;
|
||||
struct CVP_AEC_CONFIG aec;
|
||||
struct CVP_NLP_CONFIG nlp;
|
||||
struct CVP_ENC_CONFIG enc;
|
||||
struct CVP_DNS_CONFIG dns;
|
||||
struct CVP_AGC_CONFIG agc;
|
||||
struct CVP_WNC_CONFIG wnc;
|
||||
struct CVP_MFDT_CONFIG mfdt;
|
||||
struct CVP_DEBUG_CONFIG debug;
|
||||
} __attribute__((packed));
|
||||
//------------------stream.bin CVP参数文件解析结构-END---------------//
|
||||
|
||||
struct cvp_node_hdl {
|
||||
char name[16];
|
||||
AEC_TMS_CONFIG online_cfg;
|
||||
struct stream_frame *frame[3]; //输入frame存储,算法输入缓存使用
|
||||
struct stream_node *node; //节点句柄
|
||||
u8 buf_cnt; //循环输入buffer位置
|
||||
s16 buf[CVP_INPUT_SIZE];
|
||||
s16 buf_1[CVP_INPUT_SIZE];
|
||||
s16 buf_2[CVP_INPUT_SIZE];
|
||||
s16 *buf_3;
|
||||
u32 ref_sr;
|
||||
u16 source_uuid; //源节点uuid
|
||||
struct CVP_MIC_SEL_CONFIG mic_sel;
|
||||
struct CVP_REF_MIC_CONFIG ref_mic;
|
||||
};
|
||||
|
||||
static struct cvp_node_hdl *g_cvp_hdl;
|
||||
|
||||
int cvp_node_output_handle(s16 *data, u16 len)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
frame = jlstream_get_frame(g_cvp_hdl->node->oport, len);
|
||||
if (!frame) {
|
||||
return 0;
|
||||
}
|
||||
frame->len = len;
|
||||
memcpy(frame->data, data, len);
|
||||
jlstream_push_frame(g_cvp_hdl->node->oport, frame);
|
||||
return len;
|
||||
}
|
||||
|
||||
extern float eq_db2mag(float x);
|
||||
void cvp_node_param_cfg_update(struct cvp_cfg_t *cfg, void *priv)
|
||||
{
|
||||
AEC_TMS_CONFIG *p = (AEC_TMS_CONFIG *)priv;
|
||||
|
||||
if (g_cvp_hdl) {
|
||||
g_cvp_hdl->mic_sel.talk_mic = cfg->mic_sel.talk_mic;
|
||||
g_cvp_hdl->mic_sel.talk_ref_mic = cfg->mic_sel.talk_ref_mic;
|
||||
g_cvp_hdl->mic_sel.talk_fb_mic = cfg->mic_sel.talk_fb_mic;
|
||||
|
||||
g_cvp_hdl->ref_mic.en = cfg->ref_mic.en;
|
||||
g_cvp_hdl->ref_mic.ref_mic_ch = cfg->ref_mic.ref_mic_ch;
|
||||
if (g_cvp_hdl->ref_mic.en && (g_cvp_hdl->buf_3 == NULL)) {
|
||||
g_cvp_hdl->buf_3 = zalloc(CVP_INPUT_SIZE * sizeof(short));
|
||||
}
|
||||
printf("talk_mic %d, talk_ref_mic %d, talk_fb_mic %d", g_cvp_hdl->mic_sel.talk_mic, g_cvp_hdl->mic_sel.talk_ref_mic, g_cvp_hdl->mic_sel.talk_fb_mic);
|
||||
printf("ref mic en %d, ref_mic_ch %d", g_cvp_hdl->ref_mic.en, g_cvp_hdl->ref_mic.ref_mic_ch);
|
||||
}
|
||||
|
||||
p->adc_ref_en = cfg->ref_mic.en;
|
||||
|
||||
//更新预处理参数
|
||||
struct audio_cvp_pre_param_t pre_cfg;
|
||||
pre_cfg.pre_gain_en = cfg->pre_gain.en;
|
||||
pre_cfg.talk_mic_gain = eq_db2mag(cfg->pre_gain.talk_mic_gain);
|
||||
pre_cfg.talk_ref_mic_gain = eq_db2mag(cfg->pre_gain.talk_ref_mic_gain);
|
||||
pre_cfg.talk_fb_mic_gain = eq_db2mag(cfg->pre_gain.talk_fb_mic_gain);
|
||||
audio_cvp_probe_param_update(&pre_cfg);
|
||||
|
||||
//更新算法参数
|
||||
p->enable_module = cfg->aec.en | (cfg->nlp.en << 1) | (cfg->dns.en << 2) | (cfg->enc.en << 3) | (cfg->agc.en << 4) | (cfg->wnc.en << 5) | (cfg->mfdt.en << 6);
|
||||
|
||||
p->aec_process_maxfrequency = cfg->aec.aec_process_maxfrequency;
|
||||
p->aec_process_minfrequency = cfg->aec.aec_process_minfrequency;
|
||||
p->af_length = cfg->aec.af_length;
|
||||
|
||||
p->nlp_process_maxfrequency = cfg->nlp.nlp_process_maxfrequency;
|
||||
p->nlp_process_minfrequency = cfg->nlp.nlp_process_minfrequency;
|
||||
p->overdrive = cfg->nlp.overdrive;
|
||||
|
||||
p->enc_process_maxfreq = cfg->enc.enc_process_maxfreq;
|
||||
p->enc_process_minfreq = cfg->enc.enc_process_minfreq;
|
||||
p->sir_maxfreq = cfg->enc.sir_maxfreq;
|
||||
p->mic_distance = cfg->enc.mic_distance / 1000.0f; //mm换算成m
|
||||
p->target_signal_degradation = eq_db2mag(cfg->enc.target_signal_degradation); //dB转浮点
|
||||
p->enc_aggressfactor = cfg->enc.enc_aggressfactor;
|
||||
p->enc_minsuppress = cfg->enc.enc_minsuppress;
|
||||
p->Tri_SnrThreshold0 = cfg->enc.Tri_SnrThreshold0;
|
||||
p->Tri_SnrThreshold1 = cfg->enc.Tri_SnrThreshold1;
|
||||
p->Tri_CompenDb = cfg->enc.Tri_CompenDb;
|
||||
|
||||
p->aggressfactor = cfg->dns.aggressfactor;
|
||||
p->minsuppress = cfg->dns.minsuppress;
|
||||
p->init_noise_lvl = cfg->dns.init_noise_lvl;
|
||||
|
||||
p->agc_type = cfg->agc.agc_type;
|
||||
if (p->agc_type == AGC_EXTERNAL) {
|
||||
p->agc.agc_ext.ndt_fade_in = cfg->agc.ndt_fade_in;
|
||||
p->agc.agc_ext.ndt_fade_out = cfg->agc.ndt_fade_out;
|
||||
p->agc.agc_ext.dt_fade_in = cfg->agc.dt_fade_in;
|
||||
p->agc.agc_ext.dt_fade_out = cfg->agc.dt_fade_out;
|
||||
p->agc.agc_ext.ndt_max_gain = cfg->agc.ndt_max_gain;
|
||||
p->agc.agc_ext.ndt_min_gain = cfg->agc.ndt_min_gain;
|
||||
p->agc.agc_ext.ndt_speech_thr = cfg->agc.ndt_speech_thr;
|
||||
p->agc.agc_ext.dt_max_gain = cfg->agc.dt_max_gain;
|
||||
p->agc.agc_ext.dt_min_gain = cfg->agc.dt_min_gain;
|
||||
p->agc.agc_ext.dt_speech_thr = cfg->agc.dt_speech_thr;
|
||||
p->agc.agc_ext.echo_present_thr = cfg->agc.echo_present_thr;
|
||||
} else {
|
||||
p->agc.agc_int.min_mag_db_level = cfg->agc.min_mag_db_level;
|
||||
p->agc.agc_int.max_mag_db_level = cfg->agc.max_mag_db_level;
|
||||
p->agc.agc_int.addition_mag_db_level = cfg->agc.addition_mag_db_level;
|
||||
p->agc.agc_int.clip_mag_db_level = cfg->agc.clip_mag_db_level;
|
||||
p->agc.agc_int.floor_mag_db_level = cfg->agc.floor_mag_db_level;
|
||||
}
|
||||
|
||||
p->wn_msc_th = cfg->wnc.wn_msc_th;
|
||||
p->ms_th = cfg->wnc.ms_th;
|
||||
p->wn_gain_offset = cfg->wnc.wn_gain_offset;
|
||||
|
||||
p->detect_time = cfg->mfdt.detect_time; // in second
|
||||
p->detect_eng_diff_thr = cfg->mfdt.detect_eng_diff_thr; // dB
|
||||
p->detect_eng_lowerbound = cfg->mfdt.detect_eng_lowerbound; // 0~-90 dB start detect when mic energy lower than this
|
||||
p->MalfuncDet_MaxFrequency = cfg->mfdt.MalfuncDet_MaxFrequency; //检测频率上限
|
||||
p->MalfuncDet_MinFrequency = cfg->mfdt.MalfuncDet_MinFrequency; //检测频率下限
|
||||
p->OnlyDetect = cfg->mfdt.OnlyDetect;// 0 -> 故障切换到单mic模式, 1-> 只检测不切换
|
||||
|
||||
p->output_sel = cfg->debug.output_sel;
|
||||
}
|
||||
|
||||
struct cvp_cfg_t global_cvp_cfg;
|
||||
int cvp_param_cfg_read(void)
|
||||
{
|
||||
u8 subid;
|
||||
if (g_cvp_hdl) {
|
||||
subid = g_cvp_hdl->node->subid;
|
||||
} else {
|
||||
subid = 0XFF;
|
||||
}
|
||||
/*
|
||||
*解析配置文件内效果配置
|
||||
* */
|
||||
int len = 0;
|
||||
struct node_param ncfg = {0};
|
||||
len = jlstream_read_node_data(NODE_UUID_CVP_3MIC, subid, (u8 *)&ncfg);
|
||||
if (len != sizeof(ncfg)) {
|
||||
printf("cvp_tms_name read ncfg err\n");
|
||||
return -2;
|
||||
}
|
||||
char mode_index = 0;
|
||||
char cfg_index = 0;//目标配置项序号
|
||||
struct cfg_info info = {0};
|
||||
if (!jlstream_read_form_node_info_base(mode_index, ncfg.name, cfg_index, &info)) {
|
||||
len = jlstream_read_form_cfg_data(&info, &global_cvp_cfg);
|
||||
}
|
||||
|
||||
printf(" %s len %d, sizeof(global_cvp_cfg) %d\n", __func__, len, (int)sizeof(global_cvp_cfg));
|
||||
if (len != sizeof(global_cvp_cfg)) {
|
||||
printf("cvp_dms_param read ncfg err\n");
|
||||
return -1 ;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 cvp_get_talk_mic_ch(void)
|
||||
{
|
||||
return global_cvp_cfg.mic_sel.talk_mic;
|
||||
}
|
||||
|
||||
u8 cvp_get_talk_ref_mic_ch(void)
|
||||
{
|
||||
return global_cvp_cfg.mic_sel.talk_ref_mic;
|
||||
}
|
||||
|
||||
u8 cvp_get_talk_fb_mic_ch(void)
|
||||
{
|
||||
return global_cvp_cfg.mic_sel.talk_fb_mic;
|
||||
}
|
||||
|
||||
int cvp_node_param_cfg_read(void *priv, u8 ignore_subid)
|
||||
{
|
||||
AEC_TMS_CONFIG *p = (AEC_TMS_CONFIG *)priv;
|
||||
struct cvp_cfg_t cfg;
|
||||
u8 subid;
|
||||
if (g_cvp_hdl) {
|
||||
subid = g_cvp_hdl->node->subid;
|
||||
} else {
|
||||
subid = 0XFF;
|
||||
}
|
||||
/*
|
||||
*解析配置文件内效果配置
|
||||
* */
|
||||
int len = 0;
|
||||
struct node_param ncfg = {0};
|
||||
len = jlstream_read_node_data(NODE_UUID_CVP_3MIC, subid, (u8 *)&ncfg);
|
||||
if (len != sizeof(ncfg)) {
|
||||
printf("cvp_tms_node read ncfg err\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char mode_index = 0;
|
||||
char cfg_index = 0;//目标配置项序号
|
||||
struct cfg_info info = {0};
|
||||
if (!jlstream_read_form_node_info_base(mode_index, ncfg.name, cfg_index, &info)) {
|
||||
len = jlstream_read_form_cfg_data(&info, &cfg);
|
||||
}
|
||||
printf(" %s len %d, sizeof(cfg) %d\n", __func__, len, (int)sizeof(cfg));
|
||||
if (len != sizeof(cfg)) {
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
/*
|
||||
*获取在线调试的临时参数
|
||||
* */
|
||||
if (g_cvp_hdl) {
|
||||
memcpy(g_cvp_hdl->name, ncfg.name, sizeof(ncfg.name));
|
||||
if (jlstream_read_effects_online_param(g_cvp_hdl->node->uuid, g_cvp_hdl->name, &cfg, sizeof(cfg))) {
|
||||
printf("get cvp online param\n");
|
||||
}
|
||||
}
|
||||
cvp_node_param_cfg_update(&cfg, p);
|
||||
|
||||
return sizeof(AEC_TMS_CONFIG);
|
||||
}
|
||||
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
__STREAM_CACHE_CODE
|
||||
static void cvp_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct cvp_node_hdl *hdl = (struct cvp_node_hdl *)iport->private_data;
|
||||
struct stream_node *node = iport->node;
|
||||
s16 *dat, *tbuf_0, *tbuf_1, *tbuf_2, *tbuf_3;
|
||||
int wlen;
|
||||
struct stream_frame *in_frame;
|
||||
u8 mic_ch = audio_adc_file_get_mic_en_map();
|
||||
|
||||
while (1) {
|
||||
in_frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!in_frame) {
|
||||
break;
|
||||
}
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
//产测bypass 模式 不经过算法
|
||||
if (cvp_dut_mode_get() == CVP_DUT_MODE_BYPASS) {
|
||||
jlstream_push_frame(node->oport, in_frame);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (hdl->ref_mic.en) { //参考数据硬回采
|
||||
wlen = in_frame->len / 4 / 2; //一个ADC的点数
|
||||
//模仿ADCbuff的存储方法
|
||||
tbuf_0 = hdl->buf + (wlen * hdl->buf_cnt);
|
||||
tbuf_1 = hdl->buf_1 + (wlen * hdl->buf_cnt);
|
||||
tbuf_2 = hdl->buf_2 + (wlen * hdl->buf_cnt);
|
||||
tbuf_3 = hdl->buf_3 + (wlen * hdl->buf_cnt);
|
||||
if (++hdl->buf_cnt > ((CVP_INPUT_SIZE / 256) - 1)) {
|
||||
hdl->buf_cnt = 0;
|
||||
}
|
||||
/*拆分mic数据*/
|
||||
dat = (s16 *)in_frame->data;
|
||||
for (int i = 0; i < wlen; i++) {
|
||||
tbuf_0[i] = dat[4 * i];
|
||||
tbuf_1[i] = dat[4 * i + 1];
|
||||
tbuf_2[i] = dat[4 * i + 2];
|
||||
tbuf_3[i] = dat[4 * i + 3];
|
||||
}
|
||||
u8 cnt = 0;
|
||||
u8 talk_data_num = 0;//记录通话MIC数据位置
|
||||
s16 *mic_data[4];
|
||||
mic_data[0] = tbuf_0;
|
||||
mic_data[1] = tbuf_1;
|
||||
mic_data[2] = tbuf_2;
|
||||
mic_data[3] = tbuf_3;
|
||||
for (int i = 0; i < AUDIO_ADC_MIC_MAX_NUM; i++) {
|
||||
if ((mic_ch & BIT(i)) == hdl->ref_mic.ref_mic_ch) {
|
||||
audio_aec_refbuf(mic_data[cnt++], NULL, wlen << 1);
|
||||
continue;
|
||||
}
|
||||
if ((mic_ch & BIT(i)) == hdl->mic_sel.talk_mic) {
|
||||
talk_data_num = cnt++;
|
||||
continue;
|
||||
}
|
||||
if ((mic_ch & BIT(i)) == hdl->mic_sel.talk_fb_mic) {
|
||||
audio_aec_inbuf_ref_1(mic_data[cnt++], wlen << 1);
|
||||
continue;
|
||||
}
|
||||
if ((mic_ch & BIT(i)) == hdl->mic_sel.talk_ref_mic) {
|
||||
audio_aec_inbuf_ref(mic_data[cnt++], wlen << 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/*通话MIC数据需要最后传进算法*/
|
||||
audio_aec_inbuf(mic_data[talk_data_num], wlen << 1);
|
||||
|
||||
} else {//参考数据软回采
|
||||
wlen = in_frame->len / 3 / 2; //一个ADC的点数
|
||||
//模仿ADCbuff的存储方法
|
||||
tbuf_0 = hdl->buf + (wlen * hdl->buf_cnt);
|
||||
tbuf_1 = hdl->buf_1 + (wlen * hdl->buf_cnt);
|
||||
tbuf_2 = hdl->buf_2 + (wlen * hdl->buf_cnt);
|
||||
if (++hdl->buf_cnt > ((CVP_INPUT_SIZE / 256) - 1)) {
|
||||
hdl->buf_cnt = 0;
|
||||
}
|
||||
/*拆分mic数据*/
|
||||
dat = (s16 *)in_frame->data;
|
||||
for (int i = 0; i < wlen; i++) {
|
||||
tbuf_0[i] = dat[3 * i];
|
||||
tbuf_1[i] = dat[3 * i + 1];
|
||||
tbuf_2[i] = dat[3 * i + 2];
|
||||
}
|
||||
u8 cnt = 0;
|
||||
u8 talk_data_num = 0;//记录通话MIC数据位置
|
||||
s16 *mic_data[3];
|
||||
mic_data[0] = tbuf_0;
|
||||
mic_data[1] = tbuf_1;
|
||||
mic_data[2] = tbuf_2;
|
||||
for (int i = 0; i < AUDIO_ADC_MIC_MAX_NUM; i++) {
|
||||
if ((mic_ch & BIT(i)) == hdl->mic_sel.talk_mic) {
|
||||
talk_data_num = cnt++;
|
||||
continue;
|
||||
}
|
||||
if ((mic_ch & BIT(i)) == hdl->mic_sel.talk_fb_mic) {
|
||||
audio_aec_inbuf_ref_1(mic_data[cnt++], wlen << 1);
|
||||
continue;
|
||||
}
|
||||
if ((mic_ch & BIT(i)) == hdl->mic_sel.talk_ref_mic) {
|
||||
audio_aec_inbuf_ref(mic_data[cnt++], wlen << 1);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/*通话MIC数据需要最后传进算法*/
|
||||
audio_aec_inbuf(mic_data[talk_data_num], wlen << 1);
|
||||
}
|
||||
jlstream_free_frame(in_frame); //释放iport资源
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int cvp_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct cvp_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
node->type = NODE_TYPE_ASYNC;
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
hdl->buf_cnt = 0;
|
||||
g_cvp_hdl = hdl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void cvp_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = cvp_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int cvp_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
struct stream_oport *oport = iport->node->oport;
|
||||
int ret = NEGO_STA_ACCPTED;
|
||||
int nb_sr, wb_sr, nego_sr;
|
||||
|
||||
#if (TCFG_AUDIO_CVP_BAND_WIDTH_CFG == CVP_WB_EN)
|
||||
nb_sr = 16000;
|
||||
wb_sr = 16000;
|
||||
nego_sr = 16000;
|
||||
#elif (TCFG_AUDIO_CVP_BAND_WIDTH_CFG == CVP_NB_EN)
|
||||
nb_sr = 8000;
|
||||
wb_sr = 8000;
|
||||
nego_sr = 8000;
|
||||
#else
|
||||
nb_sr = 8000;
|
||||
wb_sr = 16000;
|
||||
nego_sr = 16000;
|
||||
#endif
|
||||
//要求输入为8K或者16K
|
||||
if (in_fmt->sample_rate != nb_sr && in_fmt->sample_rate != wb_sr) {
|
||||
in_fmt->sample_rate = nego_sr;
|
||||
oport->fmt.sample_rate = in_fmt->sample_rate;
|
||||
ret = NEGO_STA_CONTINUE;
|
||||
}
|
||||
|
||||
//要求输入16bit位宽的数据
|
||||
if (in_fmt->bit_wide != DATA_BIT_WIDE_16BIT) {
|
||||
in_fmt->bit_wide = DATA_BIT_WIDE_16BIT;
|
||||
in_fmt->Qval = AUDIO_QVAL_16BIT;
|
||||
oport->fmt.bit_wide = in_fmt->bit_wide;
|
||||
oport->fmt.Qval = in_fmt->Qval;
|
||||
ret = NEGO_STA_CONTINUE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void cvp_ioc_start(struct cvp_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
struct audio_aec_init_param_t init_param;
|
||||
init_param.sample_rate = fmt->sample_rate;
|
||||
init_param.ref_sr = hdl->ref_sr;
|
||||
init_param.ref_channel = 1;
|
||||
u8 mic_num; //算法需要使用的MIC个数
|
||||
|
||||
audio_aec_init(&init_param);
|
||||
|
||||
if (hdl->source_uuid == NODE_UUID_ADC) {
|
||||
if (hdl->ref_mic.en) {
|
||||
/*硬回采需要开3个MIC*/
|
||||
mic_num = 4;
|
||||
} else {
|
||||
/*硬回采需要开2个MIC*/
|
||||
mic_num = 3;
|
||||
}
|
||||
|
||||
if (audio_adc_file_get_esco_mic_num() != mic_num) {
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
//使能产测时,只有算法模式才需判断
|
||||
if (cvp_dut_mode_get() == CVP_DUT_MODE_ALGORITHM) {
|
||||
ASSERT(0, "CVP_DMS, ESCO MIC num is %d != %d\n", audio_adc_file_get_esco_mic_num(), mic_num);
|
||||
}
|
||||
#else
|
||||
ASSERT(0, "CVP_DMS, ESCO MIC num is %d != %d\n", audio_adc_file_get_esco_mic_num(), mic_num);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*节点stop函数*/
|
||||
static void cvp_ioc_stop(struct cvp_node_hdl *hdl)
|
||||
{
|
||||
if (hdl) {
|
||||
audio_aec_close();
|
||||
}
|
||||
}
|
||||
|
||||
static int cvp_ioc_update_parm(struct cvp_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = false;
|
||||
struct cvp_cfg_t *cfg = (struct cvp_cfg_t *)parm;
|
||||
if (hdl) {
|
||||
cvp_node_param_cfg_update(cfg, &hdl->online_cfg);
|
||||
aec_tms_cfg_update(&hdl->online_cfg);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int cvp_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cvp_node_hdl *hdl = (struct cvp_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
cvp_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_OPEN_OPORT:
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= cvp_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
hdl->ref_sr = (u32)arg;
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
cvp_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
cvp_ioc_stop(hdl);
|
||||
break;
|
||||
case NODE_IOC_NAME_MATCH:
|
||||
if (!strcmp((const char *)arg, hdl->name)) {
|
||||
ret = 1;
|
||||
}
|
||||
break;
|
||||
case NODE_IOC_SET_PARAM:
|
||||
#if (TCFG_CFG_TOOL_ENABLE || TCFG_AEC_TOOL_ONLINE_ENABLE)
|
||||
ret = cvp_ioc_update_parm(hdl, arg);
|
||||
#endif
|
||||
break;
|
||||
case NODE_IOC_SET_PRIV_FMT:
|
||||
hdl->source_uuid = (u16)arg;
|
||||
printf("source_uuid %x", (int)hdl->source_uuid);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void cvp_adapter_release(struct stream_node *node)
|
||||
{
|
||||
if (g_cvp_hdl->buf_3) {
|
||||
free(g_cvp_hdl->buf_3);
|
||||
g_cvp_hdl->buf_3 = NULL;
|
||||
}
|
||||
free(g_cvp_hdl);
|
||||
g_cvp_hdl = NULL;
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(cvp_node_adapter) = {
|
||||
.name = "cvp_3mic",
|
||||
.uuid = NODE_UUID_CVP_3MIC,
|
||||
.bind = cvp_adapter_bind,
|
||||
.ioctl = cvp_adapter_ioctl,
|
||||
.release = cvp_adapter_release,
|
||||
};
|
||||
|
||||
//注册工具在线调试
|
||||
REGISTER_ONLINE_ADJUST_TARGET(cvp_dms) = {
|
||||
.uuid = NODE_UUID_CVP_3MIC,
|
||||
};
|
||||
|
||||
#endif/* TCFG_AUDIO_CVP_3MIC_MODE */
|
||||
|
||||
@@ -0,0 +1,289 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".cvp_develop_node.data.bss")
|
||||
#pragma data_seg(".cvp_develop_node.data")
|
||||
#pragma const_seg(".cvp_develop_node.text.const")
|
||||
#pragma code_seg(".cvp_develop_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "circular_buf.h"
|
||||
#include "cvp_node.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
#include "audio_dut_control.h"
|
||||
#endif
|
||||
|
||||
#if TCFG_AUDIO_CVP_DEVELOP_ENABLE
|
||||
#define CVP_INPUT_SIZE 256*3 //CVP输入缓存,short
|
||||
|
||||
struct cvp_cfg_t {
|
||||
u8 mic_num;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct cvp_node_hdl {
|
||||
struct stream_frame *frame[3]; //输入frame存储,算法输入缓存使用
|
||||
struct stream_node *node; //节点句柄
|
||||
struct cvp_cfg_t cfg;
|
||||
u8 buf_cnt; //循环输入buffer位置
|
||||
s16 *buf;
|
||||
s16 *buf_ref;
|
||||
s16 *buf_ref_1;
|
||||
u32 ref_sr;
|
||||
u16 source_uuid; //源节点uuid
|
||||
};
|
||||
|
||||
static struct cvp_node_hdl *g_cvp_hdl;
|
||||
|
||||
int cvp_node_output_handle(s16 *data, u16 len)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
frame = jlstream_get_frame(g_cvp_hdl->node->oport, len);
|
||||
if (!frame) {
|
||||
return 0;
|
||||
}
|
||||
frame->len = len;
|
||||
memcpy(frame->data, data, len);
|
||||
jlstream_push_frame(g_cvp_hdl->node->oport, frame);
|
||||
return len;
|
||||
}
|
||||
|
||||
int cvp_node_param_cfg_read(void *priv, u8 ignore_subid)
|
||||
{
|
||||
struct cvp_node_hdl *hdl = (struct cvp_node_hdl *)priv;
|
||||
int len = 0;
|
||||
u8 subid;
|
||||
if (g_cvp_hdl) {
|
||||
subid = g_cvp_hdl->node->subid;
|
||||
} else {
|
||||
subid = 0XFF;
|
||||
}
|
||||
|
||||
struct node_param ncfg = {0};
|
||||
len = jlstream_read_node_data(NODE_UUID_CVP_DEVELOP, subid, (u8 *)&ncfg);
|
||||
if (len != sizeof(ncfg)) {
|
||||
printf("cvp_dms_name read ncfg err\n");
|
||||
return -2;
|
||||
}
|
||||
char mode_index = 0;
|
||||
char cfg_index = 0;//目标配置项序号
|
||||
struct cfg_info info = {0};
|
||||
if (!jlstream_read_form_node_info_base(mode_index, ncfg.name, cfg_index, &info)) {
|
||||
len = jlstream_read_form_cfg_data(&info, &hdl->cfg);
|
||||
}
|
||||
printf(" %s len %d, sizeof(cfg) %d\n", __func__, len, (int)sizeof(struct cvp_cfg_t));
|
||||
if (len != sizeof(struct cvp_cfg_t)) {
|
||||
printf("cvp_develop_param read ncfg err\n");
|
||||
return -1 ;
|
||||
}
|
||||
g_printf("mic_num %d\n", hdl->cfg.mic_num);
|
||||
if (!hdl->cfg.mic_num) {
|
||||
hdl->cfg.mic_num = 1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void cvp_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct cvp_node_hdl *hdl = (struct cvp_node_hdl *)iport->private_data;
|
||||
s16 *dat, *tbuf, *tbuf_ref, *tbuf_ref_1;
|
||||
int wlen;
|
||||
struct stream_frame *in_frame;
|
||||
|
||||
while (1) {
|
||||
in_frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!in_frame) {
|
||||
break;
|
||||
}
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
//产测bypass 模式 不经过算法
|
||||
if (cvp_dut_mode_get() == CVP_DUT_MODE_BYPASS) {
|
||||
struct stream_node *node = iport->node;
|
||||
jlstream_push_frame(node->oport, in_frame);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
//模仿ADCbuff的存储方法
|
||||
if (hdl->cfg.mic_num == 1) { //单麦第三方算法
|
||||
dat = hdl->buf + (in_frame->len / 2 * hdl->buf_cnt);
|
||||
memcpy((u8 *)dat, in_frame->data, in_frame->len);
|
||||
audio_aec_inbuf(dat, in_frame->len);
|
||||
} else if (hdl->cfg.mic_num == 2) { //双麦第三方算法
|
||||
wlen = in_frame->len >> 2; //单个ADC的点数
|
||||
tbuf = hdl->buf + (wlen * hdl->buf_cnt);
|
||||
tbuf_ref = hdl->buf_ref + (wlen * hdl->buf_cnt);
|
||||
dat = (s16 *)in_frame->data;
|
||||
for (int i = 0; i < wlen; i++) {
|
||||
tbuf[i] = dat[2 * i];
|
||||
tbuf_ref[i] = dat[2 * i + 1];
|
||||
}
|
||||
#if TCFG_AUDIO_DMS_MIC_MANAGE == DMS_MASTER_MIC0
|
||||
audio_aec_inbuf_ref(tbuf_ref, wlen << 1);
|
||||
audio_aec_inbuf(tbuf, wlen << 1);
|
||||
#else
|
||||
audio_aec_inbuf_ref(tbuf, wlen << 1);
|
||||
audio_aec_inbuf(tbuf_ref, wlen << 1);
|
||||
#endif/*TCFG_AUDIO_DMS_MIC_MANAGE*/
|
||||
} else if (hdl->cfg.mic_num == 3) { //三麦第三方算法
|
||||
wlen = in_frame->len / 6; //单个ADC的点数
|
||||
tbuf = hdl->buf + (wlen * hdl->buf_cnt);
|
||||
tbuf_ref = hdl->buf_ref + (wlen * hdl->buf_cnt);
|
||||
tbuf_ref_1 = hdl->buf_ref_1 + (wlen * hdl->buf_cnt);
|
||||
dat = (s16 *)in_frame->data;
|
||||
for (int i = 0; i < wlen; i++) {
|
||||
tbuf[i] = dat[3 * i];
|
||||
tbuf_ref[i] = dat[3 * i + 1];
|
||||
tbuf_ref_1[i] = dat[3 * i + 2];
|
||||
}
|
||||
audio_aec_inbuf_ref(tbuf_ref, wlen << 1);
|
||||
audio_aec_inbuf_ref_1(tbuf_ref_1, wlen << 1);
|
||||
audio_aec_inbuf(tbuf, wlen << 1);
|
||||
}
|
||||
if (++hdl->buf_cnt > ((CVP_INPUT_SIZE / 256) - 1)) { //计算下一个ADCbuffer位置
|
||||
hdl->buf_cnt = 0;
|
||||
}
|
||||
jlstream_free_frame(in_frame); //释放iport资源
|
||||
}
|
||||
}
|
||||
|
||||
static int cvp_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct cvp_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
node->type = NODE_TYPE_ASYNC;
|
||||
hdl->node = node;
|
||||
node->private_data = hdl;
|
||||
hdl->buf_cnt = 0;
|
||||
cvp_node_param_cfg_read(hdl, 0);
|
||||
//根据算法单麦/双麦分配对应的空间
|
||||
hdl->buf = (s16 *)malloc(CVP_INPUT_SIZE << 1);
|
||||
if (hdl->cfg.mic_num == 2) {
|
||||
hdl->buf_ref = (s16 *)malloc(CVP_INPUT_SIZE << 1);
|
||||
} else if (hdl->cfg.mic_num == 3) {
|
||||
hdl->buf_ref = (s16 *)malloc(CVP_INPUT_SIZE << 1);
|
||||
hdl->buf_ref_1 = (s16 *)malloc(CVP_INPUT_SIZE << 1);
|
||||
}
|
||||
g_cvp_hdl = hdl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void cvp_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = cvp_handle_frame;
|
||||
iport->private_data = iport->node->private_data;
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int cvp_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void cvp_ioc_start(struct cvp_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
struct audio_aec_init_param_t init_param;
|
||||
init_param.sample_rate = fmt->sample_rate;
|
||||
init_param.ref_sr = hdl->ref_sr;
|
||||
init_param.mic_num = hdl->cfg.mic_num;
|
||||
|
||||
if (hdl->source_uuid == NODE_UUID_ADC) {
|
||||
if (audio_adc_file_get_esco_mic_num() != hdl->cfg.mic_num) {
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
//使能产测时,只有算法模式才需判断
|
||||
if (cvp_dut_mode_get() == CVP_DUT_MODE_ALGORITHM) {
|
||||
ASSERT(0, "CVP_develop ESCO MIC num is %d != %d\n", audio_adc_file_get_esco_mic_num(), hdl->cfg.mic_num);
|
||||
}
|
||||
#else
|
||||
ASSERT(0, "CVP_develop ESCO MIC num is %d != %d\n", audio_adc_file_get_esco_mic_num(), hdl->cfg.mic_num);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
audio_aec_init(&init_param);
|
||||
}
|
||||
|
||||
/*节点stop函数*/
|
||||
static void cvp_ioc_stop(struct cvp_node_hdl *hdl)
|
||||
{
|
||||
if (hdl) {
|
||||
/* for (int i = 0; i < 3; i++) { */
|
||||
/* if (hdl->frame[i] != NULL) { //检查是否存在未释放的iport缓存 */
|
||||
/* jlstream_free_frame(hdl->frame[i]); //释放iport缓存 */
|
||||
/* } */
|
||||
/* } */
|
||||
audio_aec_close();
|
||||
}
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int cvp_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cvp_node_hdl *hdl = (struct cvp_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
cvp_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_OPEN_OPORT:
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= cvp_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
hdl->ref_sr = (u32)arg;
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
cvp_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
cvp_ioc_stop(hdl);
|
||||
break;
|
||||
case NODE_IOC_SET_PRIV_FMT:
|
||||
hdl->source_uuid = (u16)arg;
|
||||
printf("source_uuid %x", (int)hdl->source_uuid);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void cvp_adapter_release(struct stream_node *node)
|
||||
{
|
||||
struct cvp_node_hdl *hdl = (struct cvp_node_hdl *)node->private_data;
|
||||
//根据算法单麦/双麦释放对应的空间
|
||||
free(hdl->buf);
|
||||
if (hdl->cfg.mic_num == 2) {
|
||||
free(hdl->buf_ref);
|
||||
hdl->buf_ref = NULL;
|
||||
} else if (hdl->cfg.mic_num == 3) {
|
||||
free(hdl->buf_ref);
|
||||
hdl->buf_ref = NULL;
|
||||
free(hdl->buf_ref_1);
|
||||
hdl->buf_ref_1 = NULL;
|
||||
}
|
||||
free(hdl);
|
||||
g_cvp_hdl = NULL;
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(cvp_node_adapter) = {
|
||||
.name = "cvp_develop",
|
||||
.uuid = NODE_UUID_CVP_DEVELOP,
|
||||
.bind = cvp_adapter_bind,
|
||||
.ioctl = cvp_adapter_ioctl,
|
||||
.release = cvp_adapter_release,
|
||||
};
|
||||
|
||||
#endif/*TCFG_CVP_DEVELOP_ENABLE*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,568 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".cvp_sms_node.data.bss")
|
||||
#pragma data_seg(".cvp_sms_node.data")
|
||||
#pragma const_seg(".cvp_sms_node.text.const")
|
||||
#pragma code_seg(".cvp_sms_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "circular_buf.h"
|
||||
#include "cvp_node.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
#include "audio_dut_control.h"
|
||||
#endif
|
||||
|
||||
/*(单MIC+ANS通话) || (单MIC+DNS通话)*/
|
||||
#if (TCFG_AUDIO_CVP_SMS_ANS_MODE) || (TCFG_AUDIO_CVP_SMS_DNS_MODE)
|
||||
|
||||
#define CVP_INPUT_SIZE 256*3 //CVP输入缓存,short
|
||||
|
||||
//------------------stream.bin CVP参数文件解析结构-START---------------//
|
||||
struct CVP_REF_MIC_CONFIG {
|
||||
u8 en; //ref 回采硬使能
|
||||
u8 ref_mic_ch; //ref 硬回采MIC通道选择
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_PRE_GAIN_CONFIG {
|
||||
u8 en;
|
||||
float talk_mic_gain; //主MIC前级数字增益,default:0dB(-90 ~ 40dB)
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_AEC_CONFIG {
|
||||
u8 en;
|
||||
float aec_dt_aggress; //原音回音追踪等级, default: 1.0f(1 ~ 5)
|
||||
float aec_refengthr; //进入回音消除参考值, default: -70.0f(-90 ~ -60 dB)
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_NLP_CONFIG {
|
||||
u8 en;
|
||||
float es_aggress_factor;//回音前级动态压制,越小越强,default: -3.0f(-1 ~ -5)
|
||||
float es_min_suppress; //回音后级静态压制,越大越强,default: 4.f(0 ~ 10)
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_NS_CONFIG {
|
||||
u8 en;
|
||||
float ns_aggress; //噪声前级动态压制,越大越强default: 1.25f(1 ~ 2)
|
||||
float ns_suppress; //噪声后级静态压制,越小越强default: 0.04f(0 ~ 1)
|
||||
float init_noise_lvl; //初始噪声水平,default:-75dB,range[-100:-30]
|
||||
} __attribute__((packed));
|
||||
|
||||
struct CVP_AGC_CONFIG {
|
||||
u8 en;
|
||||
float ndt_fade_in; //单端讲话淡入步进default: 1.3f(0.1 ~ 5 dB)
|
||||
float ndt_fade_out; //单端讲话淡出步进default: 0.7f(0.1 ~ 5 dB)
|
||||
float dt_fade_in; //双端讲话淡入步进default: 1.3f(0.1 ~ 5 dB)
|
||||
float dt_fade_out; //双端讲话淡出步进default: 0.7f(0.1 ~ 5 dB)
|
||||
float ndt_max_gain; //单端讲话放大上限,default: 12.f(0 ~ 24 dB)
|
||||
float ndt_min_gain; //单端讲话放大下限,default: 0.f(-20 ~ 24 dB)
|
||||
float ndt_speech_thr; //单端讲话放大阈值,default: -50.f(-70 ~ -40 dB)
|
||||
float dt_max_gain; //双端讲话放大上限,default: 12.f(0 ~ 24 dB)
|
||||
float dt_min_gain; //双端讲话放大下限,default: 0.f(-20 ~ 24 dB)
|
||||
float dt_speech_thr; //双端讲话放大阈值,default: -40.f(-70 ~ -40 dB)
|
||||
float echo_present_thr; //单端双端讲话阈值,default:-70.f(-70 ~ -40 dB)
|
||||
} __attribute__((packed));
|
||||
|
||||
struct cvp_cfg_t {
|
||||
struct CVP_REF_MIC_CONFIG ref_mic;
|
||||
struct CVP_PRE_GAIN_CONFIG pre_gain;
|
||||
struct CVP_AEC_CONFIG aec;
|
||||
struct CVP_NLP_CONFIG nlp;
|
||||
struct CVP_NS_CONFIG ns;
|
||||
struct CVP_AGC_CONFIG agc;
|
||||
} __attribute__((packed));
|
||||
//------------------stream.bin CVP参数文件解析结构-END---------------//
|
||||
|
||||
struct cvp_node_hdl {
|
||||
char name[16];
|
||||
enum stream_scene scene;
|
||||
AEC_CONFIG online_cfg;
|
||||
struct stream_frame *frame[3]; //输入frame存储,算法输入缓存使用
|
||||
struct stream_node *node; //节点句柄
|
||||
u8 buf_cnt; //循环输入buffer位置
|
||||
s16 buf[CVP_INPUT_SIZE];
|
||||
s16 *buf_1; //回采buf
|
||||
u32 ref_sr;
|
||||
u16 source_uuid; //源节点uuid
|
||||
u8 talk_mic_ch; //通话MIC
|
||||
struct CVP_REF_MIC_CONFIG ref_mic;
|
||||
u8 ref_mic_num; //回采mic个数
|
||||
u8 bypass;
|
||||
};
|
||||
|
||||
static struct cvp_node_hdl *g_cvp_hdl;
|
||||
|
||||
int cvp_node_output_handle(s16 *data, u16 len)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
frame = jlstream_get_frame(g_cvp_hdl->node->oport, len);
|
||||
if (!frame) {
|
||||
return 0;
|
||||
}
|
||||
frame->len = len;
|
||||
memcpy(frame->data, data, len);
|
||||
jlstream_push_frame(g_cvp_hdl->node->oport, frame);
|
||||
return len;
|
||||
}
|
||||
|
||||
extern float eq_db2mag(float x);
|
||||
void cvp_node_param_cfg_update(struct cvp_cfg_t *cfg, AEC_CONFIG *p)
|
||||
{
|
||||
if (cfg == NULL) {
|
||||
return;
|
||||
}
|
||||
if (g_cvp_hdl) {
|
||||
g_cvp_hdl->ref_mic.en = cfg->ref_mic.en;
|
||||
g_cvp_hdl->ref_mic.ref_mic_ch = cfg->ref_mic.ref_mic_ch;
|
||||
if (g_cvp_hdl->ref_mic.en && (g_cvp_hdl->buf_1 == NULL)) {
|
||||
/*计算回采mic个数*/
|
||||
g_cvp_hdl->ref_mic_num = audio_get_mic_num(g_cvp_hdl->ref_mic.ref_mic_ch);
|
||||
g_cvp_hdl->buf_1 = zalloc(CVP_INPUT_SIZE * sizeof(short) * g_cvp_hdl->ref_mic_num);
|
||||
}
|
||||
u8 mic_ch = audio_adc_file_get_mic_en_map();
|
||||
for (int i = 0; i < AUDIO_ADC_MIC_MAX_NUM; i ++) {
|
||||
/*如果是硬回采MIC,跳过本次判断*/
|
||||
if (BIT(i) & g_cvp_hdl->ref_mic.ref_mic_ch) {
|
||||
continue;
|
||||
}
|
||||
/*查找哪个是通话MIC*/
|
||||
if (BIT(i) & mic_ch) {
|
||||
g_cvp_hdl->talk_mic_ch = BIT(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("talk_mic %x, ref mic en %d, ref_mic_ch %x", g_cvp_hdl->talk_mic_ch, g_cvp_hdl->ref_mic.en, g_cvp_hdl->ref_mic.ref_mic_ch);
|
||||
}
|
||||
if (p) {
|
||||
p->adc_ref_en = cfg->ref_mic.en;
|
||||
|
||||
//更新预处理参数
|
||||
struct audio_cvp_pre_param_t pre_cfg;
|
||||
pre_cfg.pre_gain_en = cfg->pre_gain.en;
|
||||
pre_cfg.talk_mic_gain = eq_db2mag(cfg->pre_gain.talk_mic_gain);
|
||||
audio_cvp_probe_param_update(&pre_cfg);
|
||||
|
||||
//更新算法参数
|
||||
p->aec_mode = cfg->aec.en | (cfg->nlp.en << 1) | (cfg->ns.en << 2) | (cfg->agc.en << 4);
|
||||
p->ul_eq_en = 0;
|
||||
|
||||
p->ndt_fade_in = cfg->agc.ndt_fade_in;
|
||||
p->ndt_fade_out = cfg->agc.ndt_fade_out;
|
||||
p->dt_fade_in = cfg->agc.dt_fade_in;
|
||||
p->dt_fade_out = cfg->agc.dt_fade_out;
|
||||
p->ndt_max_gain = cfg->agc.ndt_max_gain;
|
||||
p->ndt_min_gain = cfg->agc.ndt_min_gain;
|
||||
p->ndt_speech_thr = cfg->agc.ndt_speech_thr;
|
||||
p->dt_max_gain = cfg->agc.dt_max_gain;
|
||||
p->dt_min_gain = cfg->agc.dt_min_gain;
|
||||
p->dt_speech_thr = cfg->agc.dt_speech_thr;
|
||||
p->echo_present_thr = cfg->agc.echo_present_thr;
|
||||
p->aec_dt_aggress = cfg->aec.aec_dt_aggress;
|
||||
p->aec_refengthr = cfg->aec.aec_refengthr;
|
||||
p->es_aggress_factor = cfg->nlp.es_aggress_factor;
|
||||
p->es_min_suppress = cfg->nlp.es_min_suppress;
|
||||
p->ans_aggress = cfg->ns.ns_aggress;
|
||||
p->ans_suppress = cfg->ns.ns_suppress;
|
||||
p->init_noise_lvl = cfg->ns.init_noise_lvl;
|
||||
}
|
||||
}
|
||||
|
||||
struct cvp_cfg_t global_cvp_cfg;
|
||||
int cvp_param_cfg_read(void)
|
||||
{
|
||||
u8 subid;
|
||||
if (g_cvp_hdl) {
|
||||
subid = g_cvp_hdl->node->subid;
|
||||
} else {
|
||||
subid = 0XFF;
|
||||
}
|
||||
/*
|
||||
*解析配置文件内效果配置
|
||||
* */
|
||||
int len = 0;
|
||||
struct node_param ncfg = {0};
|
||||
#if TCFG_AUDIO_CVP_SMS_ANS_MODE
|
||||
len = jlstream_read_node_data(NODE_UUID_CVP_SMS_ANS, subid, (u8 *)&ncfg);
|
||||
#else /*TCFG_AUDIO_CVP_SMS_DNS_MODE*/
|
||||
len = jlstream_read_node_data(NODE_UUID_CVP_SMS_DNS, subid, (u8 *)&ncfg);
|
||||
#endif
|
||||
if (len != sizeof(ncfg)) {
|
||||
printf("cvp_sms_node read ncfg err\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
char mode_index = 0;
|
||||
char cfg_index = 0;//目标配置项序号
|
||||
struct cfg_info info = {0};
|
||||
if (!jlstream_read_form_node_info_base(mode_index, ncfg.name, cfg_index, &info)) {
|
||||
len = jlstream_read_form_cfg_data(&info, &global_cvp_cfg);
|
||||
}
|
||||
|
||||
printf(" %s len %d, sizeof(global_cvp_cfg) %d\n", __func__, len, (int)sizeof(global_cvp_cfg));
|
||||
if (len != sizeof(global_cvp_cfg)) {
|
||||
printf("cvp_sms_param read ncfg err\n");
|
||||
return -1 ;
|
||||
}
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
u8 cvp_get_talk_mic_ch(void)
|
||||
{
|
||||
u8 talk_mic_ch = audio_adc_file_get_mic_en_map();
|
||||
|
||||
if (global_cvp_cfg.ref_mic.en) { //参考数据硬回采
|
||||
u8 ref_mic_ch = global_cvp_cfg.ref_mic.ref_mic_ch;
|
||||
u8 mic_ch = audio_adc_file_get_mic_en_map();
|
||||
for (int i = 0; i < AUDIO_ADC_MIC_MAX_NUM; i ++) {
|
||||
/*如果是硬回采MIC,跳过本次判断*/
|
||||
if (BIT(i) & ref_mic_ch) {
|
||||
continue;
|
||||
}
|
||||
/*查找哪个是通话MIC*/
|
||||
if (BIT(i) & mic_ch) {
|
||||
talk_mic_ch = BIT(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return talk_mic_ch;
|
||||
}
|
||||
|
||||
int cvp_node_param_cfg_read(void *priv, u8 ignore_subid)
|
||||
{
|
||||
AEC_CONFIG *p = (AEC_CONFIG *)priv;
|
||||
struct cvp_cfg_t cfg;
|
||||
u8 subid;
|
||||
if (g_cvp_hdl) {
|
||||
subid = g_cvp_hdl->node->subid;
|
||||
} else {
|
||||
subid = 0XFF;
|
||||
}
|
||||
/*
|
||||
*解析配置文件内效果配置
|
||||
* */
|
||||
int len = 0;
|
||||
struct node_param ncfg = {0};
|
||||
#if TCFG_AUDIO_CVP_SMS_ANS_MODE
|
||||
len = jlstream_read_node_data(NODE_UUID_CVP_SMS_ANS, subid, (u8 *)&ncfg);
|
||||
#else /*TCFG_AUDIO_CVP_SMS_DNS_MODE*/
|
||||
len = jlstream_read_node_data(NODE_UUID_CVP_SMS_DNS, subid, (u8 *)&ncfg);
|
||||
#endif
|
||||
if (len != sizeof(ncfg)) {
|
||||
printf("cvp_sms_node read ncfg err\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char mode_index = 0;
|
||||
char cfg_index = 0;//目标配置项序号
|
||||
struct cfg_info info = {0};
|
||||
if (!jlstream_read_form_node_info_base(mode_index, ncfg.name, cfg_index, &info)) {
|
||||
len = jlstream_read_form_cfg_data(&info, &cfg);
|
||||
}
|
||||
printf(" %s len %d, sizeof(cfg) %d\n", __func__, len, (int)sizeof(cfg));
|
||||
if (len != sizeof(cfg)) {
|
||||
return 0 ;
|
||||
}
|
||||
/*
|
||||
*获取在线调试的临时参数
|
||||
* */
|
||||
if (g_cvp_hdl) {
|
||||
memcpy(g_cvp_hdl->name, ncfg.name, sizeof(ncfg.name));
|
||||
if (jlstream_read_effects_online_param(g_cvp_hdl->node->uuid, g_cvp_hdl->name, &cfg, sizeof(cfg))) {
|
||||
printf("get cvp online param\n");
|
||||
}
|
||||
}
|
||||
cvp_node_param_cfg_update(&cfg, p);
|
||||
|
||||
return sizeof(AEC_CONFIG);
|
||||
}
|
||||
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void cvp_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct cvp_node_hdl *hdl = (struct cvp_node_hdl *)iport->private_data;
|
||||
s16 *dat, *mic_buf, *ref_buf;
|
||||
u8 mic_index = 0, ref_index[2] = {0};
|
||||
int wlen;
|
||||
struct stream_frame *in_frame;
|
||||
|
||||
while (1) {
|
||||
in_frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!in_frame) {
|
||||
break;
|
||||
}
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
//产测bypass 模式 不经过算法
|
||||
if (cvp_dut_mode_get() == CVP_DUT_MODE_BYPASS) {
|
||||
struct stream_node *node = iport->node;
|
||||
jlstream_push_frame(node->oport, in_frame);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (hdl->bypass) {
|
||||
jlstream_push_frame(hdl->node->oport, in_frame);
|
||||
break;
|
||||
}
|
||||
if (hdl->ref_mic.en) { //参考数据硬回采
|
||||
wlen = in_frame->len / ((1 + hdl->ref_mic_num) * 2); //一个ADC的点数
|
||||
//模仿ADCbuff的存储方法
|
||||
mic_buf = hdl->buf + (wlen * hdl->buf_cnt);
|
||||
ref_buf = hdl->buf_1 + (wlen * hdl->buf_cnt * hdl->ref_mic_num);
|
||||
if (++hdl->buf_cnt > ((CVP_INPUT_SIZE / 256) - 1)) {
|
||||
hdl->buf_cnt = 0;
|
||||
}
|
||||
|
||||
/*查找对应mic的数据index*/
|
||||
u8 mic_cnt = 0, ref_cnt = 0;
|
||||
for (int i = 0; i < AUDIO_ADC_MIC_MAX_NUM; i++) {
|
||||
if (hdl->ref_mic.ref_mic_ch & BIT(i)) {
|
||||
ref_index[ref_cnt++] = mic_cnt++;
|
||||
continue;
|
||||
}
|
||||
if (hdl->talk_mic_ch & BIT(i)) {
|
||||
mic_index = mic_cnt++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
dat = (s16 *)in_frame->data;
|
||||
if (hdl->ref_mic_num == 1) {
|
||||
/*分类2个mic的数据*/
|
||||
for (int i = 0; i < wlen; i++) {
|
||||
mic_buf[i] = dat[2 * i + mic_index];
|
||||
ref_buf[i] = dat[2 * i + ref_index[0]];
|
||||
}
|
||||
|
||||
} else if (hdl->ref_mic_num == 2) {
|
||||
/*分类3个mic的数据*/
|
||||
for (int i = 0; i < wlen; i++) {
|
||||
mic_buf[i] = dat[3 * i + mic_index];
|
||||
ref_buf[2 * i] = dat[3 * i + ref_index[0]];
|
||||
ref_buf[2 * i + 1] = dat[3 * i + ref_index[1]];
|
||||
}
|
||||
}
|
||||
audio_aec_refbuf(ref_buf, NULL, wlen << hdl->ref_mic_num);
|
||||
audio_aec_inbuf(mic_buf, wlen << 1);
|
||||
} else {//参考数据软回采
|
||||
dat = hdl->buf + (in_frame->len / 2 * hdl->buf_cnt);
|
||||
//模仿ADCbuff的存储方法
|
||||
memcpy((u8 *)dat, in_frame->data, in_frame->len);
|
||||
audio_aec_inbuf(dat, in_frame->len);
|
||||
if (++hdl->buf_cnt > ((CVP_INPUT_SIZE / 256) - 1)) {
|
||||
hdl->buf_cnt = 0;
|
||||
}
|
||||
}
|
||||
jlstream_free_frame(in_frame); //释放iport资源
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int cvp_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct cvp_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
node->type = NODE_TYPE_ASYNC;
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
hdl->buf_cnt = 0;
|
||||
g_cvp_hdl = hdl;
|
||||
/*先读取节点需要用的参数*/
|
||||
int mode = jlstream_event_notify(STREAM_EVENT_GET_CVP_MODE, 0);
|
||||
hdl->bypass = mode == 1 ? 1 : 0;
|
||||
|
||||
if (!hdl->bypass) {
|
||||
cvp_node_param_cfg_read(NULL, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void cvp_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = cvp_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int cvp_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
struct stream_oport *oport = iport->node->oport;
|
||||
int ret = NEGO_STA_ACCPTED;
|
||||
int nb_sr, wb_sr, nego_sr;
|
||||
|
||||
#if (TCFG_AUDIO_CVP_BAND_WIDTH_CFG == CVP_WB_EN)
|
||||
nb_sr = 16000;
|
||||
wb_sr = 16000;
|
||||
nego_sr = 16000;
|
||||
#elif (TCFG_AUDIO_CVP_BAND_WIDTH_CFG == CVP_NB_EN)
|
||||
nb_sr = 8000;
|
||||
wb_sr = 8000;
|
||||
nego_sr = 8000;
|
||||
#else
|
||||
nb_sr = 8000;
|
||||
wb_sr = 16000;
|
||||
nego_sr = 16000;
|
||||
#endif
|
||||
//要求输入为8K或者16K
|
||||
if (in_fmt->sample_rate != nb_sr && in_fmt->sample_rate != wb_sr) {
|
||||
in_fmt->sample_rate = nego_sr;
|
||||
oport->fmt.sample_rate = in_fmt->sample_rate;
|
||||
ret = NEGO_STA_CONTINUE;
|
||||
}
|
||||
|
||||
//要求输入16bit位宽的数据
|
||||
if (in_fmt->bit_wide != DATA_BIT_WIDE_16BIT) {
|
||||
in_fmt->bit_wide = DATA_BIT_WIDE_16BIT;
|
||||
in_fmt->Qval = AUDIO_QVAL_16BIT;
|
||||
oport->fmt.bit_wide = in_fmt->bit_wide;
|
||||
oport->fmt.Qval = in_fmt->Qval;
|
||||
ret = NEGO_STA_CONTINUE;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void cvp_ioc_start(struct cvp_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
struct audio_aec_init_param_t init_param;
|
||||
init_param.sample_rate = fmt->sample_rate;
|
||||
init_param.ref_sr = hdl->ref_sr;
|
||||
init_param.ref_channel = hdl->ref_mic_num;
|
||||
u8 mic_num; //算法需要使用的MIC个数
|
||||
|
||||
if (hdl->bypass) {
|
||||
return;
|
||||
}
|
||||
audio_aec_init(&init_param);
|
||||
|
||||
if (hdl->source_uuid == NODE_UUID_ADC) {
|
||||
if (hdl->ref_mic.en) {
|
||||
/*硬回采需要开2/3个MIC*/
|
||||
mic_num = 1 + hdl->ref_mic_num;
|
||||
} else {
|
||||
/*硬回采需要开1个MIC*/
|
||||
mic_num = 1;
|
||||
}
|
||||
if (audio_adc_file_get_esco_mic_num() != mic_num) {
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
//使能产测时,只有算法模式才需判断
|
||||
if (cvp_dut_mode_get() == CVP_DUT_MODE_ALGORITHM) {
|
||||
ASSERT(0, "CVP_SMS, ESCO MIC num is %d != %d\n", audio_adc_file_get_esco_mic_num(), mic_num);
|
||||
}
|
||||
#else
|
||||
ASSERT(0, "CVP_SMS, ESCO MIC num is %d != %d\n", audio_adc_file_get_esco_mic_num(), mic_num);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点stop函数*/
|
||||
static void cvp_ioc_stop(struct cvp_node_hdl *hdl)
|
||||
{
|
||||
if (hdl && !hdl->bypass) {
|
||||
audio_aec_close();
|
||||
}
|
||||
}
|
||||
|
||||
static int cvp_ioc_update_parm(struct cvp_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = false;
|
||||
struct cvp_cfg_t *cfg = (struct cvp_cfg_t *)parm;
|
||||
if (hdl && !hdl->bypass) {
|
||||
cvp_node_param_cfg_update(cfg, &hdl->online_cfg);
|
||||
aec_cfg_update(&hdl->online_cfg);
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int cvp_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct cvp_node_hdl *hdl = (struct cvp_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
cvp_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_OPEN_OPORT:
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= cvp_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
hdl->ref_sr = (u32)arg;
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
hdl->scene = arg;
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
cvp_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
cvp_ioc_stop(hdl);
|
||||
break;
|
||||
case NODE_IOC_NAME_MATCH:
|
||||
if (!strcmp((const char *)arg, hdl->name)) {
|
||||
ret = 1;
|
||||
}
|
||||
break;
|
||||
case NODE_IOC_SET_PARAM:
|
||||
#if (TCFG_CFG_TOOL_ENABLE || TCFG_AEC_TOOL_ONLINE_ENABLE)
|
||||
ret = cvp_ioc_update_parm(hdl, arg);
|
||||
#endif
|
||||
break;
|
||||
case NODE_IOC_SET_PRIV_FMT:
|
||||
hdl->source_uuid = (u16)arg;
|
||||
printf("source_uuid %x", (int)hdl->source_uuid);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void cvp_adapter_release(struct stream_node *node)
|
||||
{
|
||||
if (g_cvp_hdl->buf_1) {
|
||||
free(g_cvp_hdl->buf_1);
|
||||
g_cvp_hdl->buf_1 = NULL;
|
||||
}
|
||||
free(g_cvp_hdl);
|
||||
g_cvp_hdl = NULL;
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(cvp_node_adapter) = {
|
||||
#if TCFG_AUDIO_CVP_SMS_ANS_MODE
|
||||
.name = "cvp_sms_ans",
|
||||
.uuid = NODE_UUID_CVP_SMS_ANS,
|
||||
#else /*TCFG_AUDIO_CVP_SMS_DNS_MODE*/
|
||||
.name = "cvp_sms_dns",
|
||||
.uuid = NODE_UUID_CVP_SMS_DNS,
|
||||
#endif
|
||||
.bind = cvp_adapter_bind,
|
||||
.ioctl = cvp_adapter_ioctl,
|
||||
.release = cvp_adapter_release,
|
||||
};
|
||||
|
||||
//注册工具在线调试
|
||||
REGISTER_ONLINE_ADJUST_TARGET(cvp_sms) = {
|
||||
#if TCFG_AUDIO_CVP_SMS_ANS_MODE
|
||||
.uuid = NODE_UUID_CVP_SMS_ANS,
|
||||
#else /*TCFG_AUDIO_CVP_SMS_DNS_MODE*/
|
||||
.uuid = NODE_UUID_CVP_SMS_DNS,
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif/*TCFG_AUDIO_CVP_SMS_ANS_MODE || TCFG_AUDIO_CVP_SMS_DNS_MODE*/
|
||||
@@ -0,0 +1,237 @@
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "app_config.h"
|
||||
#include "aec_uart_debug.h"
|
||||
#include "uartPcmSender.h"
|
||||
|
||||
#if TCFG_DATA_EXPORT_NODE_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST EFFECTS
|
||||
#define LOG_TAG "[DATA_EXPORT-NODE]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_DUMP_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
struct data_export_cfg_t {
|
||||
u16 data_len;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct data_export_node_hdl {
|
||||
char name[16];
|
||||
struct stream_node *node; //节点句柄
|
||||
void *effect_dev1;
|
||||
u32 sample_rate;
|
||||
u8 ch_num;
|
||||
u8 ch_idx;
|
||||
struct data_export_cfg_t node_cfg;
|
||||
};
|
||||
|
||||
/*节点配置*/
|
||||
struct data_export_global_info {
|
||||
u8 node_cnt; //记录当前是第几个data export节点
|
||||
u8 node_num; //记录数据流里面,date export节点的个数
|
||||
int total_len; //记录通道数据总和
|
||||
};
|
||||
|
||||
/*全局变量*/
|
||||
struct data_export_global_info g_export_info = {
|
||||
.node_cnt = 0,
|
||||
.node_num = 0,
|
||||
.total_len = 0,
|
||||
};
|
||||
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void data_export_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
struct data_export_node_hdl *hdl = (struct data_export_node_hdl *)iport->private_data;
|
||||
struct stream_node *node = iport->node;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
aec_uart_fill_v2(hdl->ch_idx, (s16 *)frame->data, frame->len);
|
||||
aec_uart_write_v2();
|
||||
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, frame); //将数据推到oport
|
||||
} else {
|
||||
jlstream_free_frame(frame); //释放iport资源
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int data_export_param_cfg_read(struct data_export_node_hdl *hdl)
|
||||
{
|
||||
/*
|
||||
*解析配置文件内效果配置
|
||||
* */
|
||||
int len = 0;
|
||||
struct node_param ncfg = {0};
|
||||
len = jlstream_read_node_data(NODE_UUID_DATA_EXPORT, hdl->node->subid, (u8 *)&ncfg);
|
||||
|
||||
if (len != sizeof(ncfg)) {
|
||||
printf("data_export_node read ncfg err\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
char mode_index = 0;
|
||||
char cfg_index = 0;//目标配置项序号
|
||||
struct cfg_info info = {0};
|
||||
if (!jlstream_read_form_node_info_base(mode_index, ncfg.name, cfg_index, &info)) {
|
||||
len = jlstream_read_form_cfg_data(&info, &hdl->node_cfg);
|
||||
}
|
||||
|
||||
printf(" %s len %d, sizeof(struct data_export_cfg_t) %d\n", __func__, len, (int)sizeof(struct data_export_cfg_t));
|
||||
if (len != sizeof(struct data_export_cfg_t)) {
|
||||
printf("data_export_param read ncfg err\n");
|
||||
return -1 ;
|
||||
}
|
||||
return len ;
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int data_export_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
printf("data_export_adapter_bind");
|
||||
struct data_export_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
/*计算数据流data export节点的个数*/
|
||||
g_export_info.node_num++;
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void data_export_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
printf("data_export_ioc_open_iport");
|
||||
iport->handle_frame = data_export_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int data_export_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
/* struct stream_oport *oport = iport->node->oport;
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
struct data_export_node_hdl *hdl = (struct data_export_node_hdl *)iport->private_data; */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void data_export_ioc_start(struct data_export_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
/* struct jlstream *stream = jlstream_for_node(hdl->node); */
|
||||
|
||||
printf("data_export_ioc_start");
|
||||
/*读取节点参数*/
|
||||
data_export_param_cfg_read(hdl);
|
||||
|
||||
/*计算总的通道数据大小*/
|
||||
g_export_info.total_len += hdl->node_cfg.data_len;
|
||||
|
||||
hdl->ch_idx = g_export_info.node_cnt;
|
||||
printf("ch_idx : %d", hdl->ch_idx);
|
||||
|
||||
g_export_info.node_cnt++;
|
||||
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
hdl->ch_num = AUDIO_CH_NUM(fmt->channel_mode);
|
||||
|
||||
if (g_export_info.node_cnt >= g_export_info.node_num) {
|
||||
uartSendInit();
|
||||
aec_uart_open_v2(g_export_info.node_num, g_export_info.total_len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*节点stop函数*/
|
||||
static void data_export_ioc_stop(struct data_export_node_hdl *hdl)
|
||||
{
|
||||
printf("data_export_ioc_stop");
|
||||
g_export_info.node_num--;
|
||||
g_export_info.node_cnt--;
|
||||
if (g_export_info.node_num == 0) {
|
||||
g_export_info.total_len = 0;
|
||||
aec_uart_close_v2();
|
||||
}
|
||||
}
|
||||
|
||||
static int data_export_ioc_update_parm(struct data_export_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = false;
|
||||
return ret;
|
||||
}
|
||||
static int get_data_export_ioc_parm(struct data_export_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int data_export_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct data_export_node_hdl *hdl = (struct data_export_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
data_export_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_OPEN_OPORT:
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= data_export_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
data_export_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
data_export_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void data_export_adapter_release(struct stream_node *node)
|
||||
{
|
||||
printf("data_export_adapter_release");
|
||||
|
||||
free(node->private_data);
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(data_export_node_adapter) = {
|
||||
.name = "data_export",
|
||||
.uuid = NODE_UUID_DATA_EXPORT,
|
||||
.bind = data_export_adapter_bind,
|
||||
.ioctl = data_export_adapter_ioctl,
|
||||
.release = data_export_adapter_release,
|
||||
};
|
||||
|
||||
/* REGISTER_ONLINE_ADJUST_TARGET(data_export) = { */
|
||||
/* .uuid = NODE_UUID_DATA_EXPORT, */
|
||||
/* }; */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".dns_node.data.bss")
|
||||
#pragma data_seg(".dns_node.data")
|
||||
#pragma const_seg(".dns_node.text.const")
|
||||
#pragma code_seg(".dns_node.text")
|
||||
#endif
|
||||
|
||||
#include "jlstream.h"
|
||||
#include "audio_config.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "audio_ns.h"
|
||||
#include "btstack/avctp_user.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "frame_length_adaptive.h"
|
||||
#include "app_config.h"
|
||||
#include "overlay_code.h"
|
||||
|
||||
#if 1
|
||||
#define ns_log printf
|
||||
#else
|
||||
#define ns_log(...)
|
||||
#endif/*log_en*/
|
||||
|
||||
#if TCFG_DNS_NODE_ENABLE
|
||||
|
||||
extern void aec_code_movable_load(void);
|
||||
extern void aec_code_movable_unload(void);
|
||||
|
||||
enum {
|
||||
AUDIO_NS_TYPE_ESCO_DL = 1, //下行降噪类型
|
||||
AUDIO_NS_TYPE_GENERAL, //通用类型
|
||||
};
|
||||
|
||||
struct ns_cfg_t {
|
||||
u8 ns_type;//降噪类型选择,通用降噪/通话下行降噪
|
||||
u8 call_active_trigger;//接通电话触发标志, 只有通话下行降噪会使用
|
||||
float aggressfactor; //降噪强度(越大越强:1~2)
|
||||
float minsuppress; //降噪最小压制(越小越强:0~1)
|
||||
float noiselevel; //初始噪声水平(评估初始噪声,加快收敛)
|
||||
} __attribute__((packed));
|
||||
|
||||
struct dns_node_hdl {
|
||||
u16 sr;
|
||||
void *dns;
|
||||
struct stream_frame *out_frame;
|
||||
struct stream_node *node; //节点句柄
|
||||
struct ns_cfg_t cfg;
|
||||
struct frame_length_adaptive_hdl *olen_adaptive;//输出长度适配
|
||||
};
|
||||
|
||||
extern int db2mag(int db, int dbQ, int magDQ);//10^db/20
|
||||
int dns_param_cfg_read(struct stream_node *node)
|
||||
{
|
||||
struct ns_cfg_t config;
|
||||
struct dns_node_hdl *hdl = (struct dns_node_hdl *)node->private_data;
|
||||
int ret = 0;
|
||||
if (!hdl) {
|
||||
return -1 ;
|
||||
}
|
||||
/*
|
||||
*获取配置文件内的参数,及名字
|
||||
* */
|
||||
ret = jlstream_read_node_data_new(NODE_UUID_DNS_NOISE_SUPPRESSOR, node->subid, (void *)&config, NULL);
|
||||
if (ret != sizeof(config)) {
|
||||
printf("%s, read node data err %d = %d\n", __FUNCTION__, ret, (int)sizeof(config));
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
hdl->cfg = config;
|
||||
|
||||
ns_log("type %d\n", hdl->cfg.ns_type);
|
||||
ns_log("call_active_trigger %d\n", hdl->cfg.call_active_trigger);
|
||||
ns_log("aggressfactor %d/1000\n", (int)(hdl->cfg.aggressfactor * 1000.f));
|
||||
ns_log("minsuppress %d/1000\n", (int)(hdl->cfg.minsuppress * 1000.f));
|
||||
ns_log("noiselevel %d/1000\n", (int)(hdl->cfg.noiselevel * 1000.f));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void dns_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct dns_node_hdl *hdl = (struct dns_node_hdl *)iport->private_data;
|
||||
struct stream_node *node = iport->node;
|
||||
struct stream_frame *in_frame;
|
||||
int wlen;
|
||||
u8 trigger = 1;
|
||||
int out_frame_len = 0;
|
||||
|
||||
out_frame_len = DNS_FRAME_SIZE;
|
||||
|
||||
while (1) {
|
||||
in_frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!in_frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdl->cfg.ns_type == AUDIO_NS_TYPE_ESCO_DL) {
|
||||
if (hdl->cfg.call_active_trigger) {
|
||||
if (bt_get_call_status() != BT_CALL_ACTIVE) {
|
||||
trigger = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (trigger) {
|
||||
/*接通的时候再开始做降噪*/
|
||||
out_frame_len = in_frame->len > out_frame_len ? in_frame->len : out_frame_len;
|
||||
if (!hdl->out_frame) {
|
||||
hdl->out_frame = jlstream_get_frame(node->oport, out_frame_len);
|
||||
if (!hdl->out_frame) {
|
||||
jlstream_return_frame(iport, in_frame);
|
||||
return;
|
||||
}
|
||||
}
|
||||
wlen = audio_dns_run(hdl->dns, (s16 *)in_frame->data,
|
||||
(s16 *)hdl->out_frame->data, in_frame->len);
|
||||
|
||||
if (!hdl->olen_adaptive) {
|
||||
hdl->olen_adaptive = frame_length_adaptive_open(in_frame->len, out_frame_len);
|
||||
}
|
||||
int len = frame_length_adaptive_run(hdl->olen_adaptive, (s16 *)hdl->out_frame->data, (s16 *)hdl->out_frame->data, wlen);
|
||||
|
||||
if (len) {
|
||||
hdl->out_frame->len = len;
|
||||
jlstream_push_frame(node->oport, hdl->out_frame); //将数据推到oport
|
||||
hdl->out_frame = NULL;
|
||||
}
|
||||
jlstream_free_frame(in_frame); //释放iport资源
|
||||
} else {
|
||||
jlstream_push_frame(node->oport, in_frame); //将数据推到oport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int dns_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct dns_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
dns_param_cfg_read(node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void dns_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = dns_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int dns_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void dns_ioc_start(struct dns_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
printf("dns node start");
|
||||
|
||||
dns_param_t param = {
|
||||
.DNS_OverDrive = hdl->cfg.aggressfactor,
|
||||
.DNS_GainFloor = hdl->cfg.minsuppress,
|
||||
.DNS_NoiseLevel = hdl->cfg.noiselevel,
|
||||
.DNS_highGain = 2.5f,
|
||||
.DNS_rbRate = 0.3f,
|
||||
.sample_rate = fmt->sample_rate,
|
||||
};
|
||||
overlay_load_code(OVERLAY_AEC);
|
||||
aec_code_movable_load();
|
||||
/*打开算法*/
|
||||
hdl->dns = audio_dns_open(¶m);
|
||||
}
|
||||
|
||||
/*节点stop函数*/
|
||||
static void dns_ioc_stop(struct dns_node_hdl *hdl)
|
||||
{
|
||||
if (hdl->dns) {
|
||||
audio_dns_close(hdl->dns);
|
||||
hdl->dns = NULL;
|
||||
aec_code_movable_unload();
|
||||
}
|
||||
if (hdl->olen_adaptive) {
|
||||
frame_length_adaptive_close(hdl->olen_adaptive);
|
||||
hdl->olen_adaptive = NULL;
|
||||
}
|
||||
if (hdl->out_frame) {
|
||||
jlstream_free_frame(hdl->out_frame);
|
||||
hdl->out_frame = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int dns_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dns_node_hdl *hdl = (struct dns_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
dns_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= dns_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
dns_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
dns_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void dns_adapter_release(struct stream_node *node)
|
||||
{
|
||||
struct dns_node_hdl *hdl = (struct dns_node_hdl *)node->private_data;
|
||||
if (!hdl) {
|
||||
return;
|
||||
}
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(dns_node_adapter) = {
|
||||
.name = "dns",
|
||||
.uuid = NODE_UUID_DNS_NOISE_SUPPRESSOR,
|
||||
.bind = dns_adapter_bind,
|
||||
.ioctl = dns_adapter_ioctl,
|
||||
.release = dns_adapter_release,
|
||||
};
|
||||
|
||||
//注册工具在线调试
|
||||
REGISTER_ONLINE_ADJUST_TARGET(dns_noise_suppressor) = {
|
||||
.uuid = NODE_UUID_DNS_NOISE_SUPPRESSOR,
|
||||
};
|
||||
|
||||
#endif/* TCFG_DNS_NODE_ENABLE*/
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_EFFECT_DEV0_NODE_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST EFFECTS
|
||||
#define LOG_TAG "[EFFECT_DEV0-NODE]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_DUMP_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma code_seg(".effect_dev0.text")
|
||||
#pragma data_seg(".effect_dev0.data")
|
||||
#pragma const_seg(".effect_dev0.text.const")
|
||||
#endif
|
||||
|
||||
|
||||
struct effect_dev0_node_hdl {
|
||||
char name[16];
|
||||
struct stream_node *node; //节点句柄
|
||||
void *effect_dev1;
|
||||
u32 sample_rate;
|
||||
u8 ch_num;
|
||||
};
|
||||
|
||||
/* 自定义算法,初始化
|
||||
* sample_rate:采样率
|
||||
* ch_num:通道数,单声道 1,立体声 2, 四声道 4
|
||||
**/
|
||||
static void audio_effect_dev0_init(u32 sample_rate, u8 ch_num)
|
||||
{
|
||||
//do something
|
||||
}
|
||||
|
||||
/* 自定义算法,运行
|
||||
* sample_rate:采样率
|
||||
* ch_num:通道数,单声道 1,立体声 2, 四声道 4
|
||||
* *data:输入输出数据同地址,位宽16bit
|
||||
* data_len :输入数据长度,byte
|
||||
* */
|
||||
static void audio_effect_dev0_run(u32 sample_rate, u8 ch_num, s16 *data, u32 data_len)
|
||||
{
|
||||
//do something
|
||||
/* printf("effect dev0 do something here\n"); */
|
||||
}
|
||||
/* 自定义算法,关闭
|
||||
**/
|
||||
static void audio_effect_dev0_exit()
|
||||
{
|
||||
//do something
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void effect_dev0_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
struct effect_dev0_node_hdl *hdl = (struct effect_dev0_node_hdl *)iport->private_data;
|
||||
struct stream_node *node = iport->node;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
audio_effect_dev0_run(hdl->sample_rate, hdl->ch_num, (s16 *)frame->data, frame->len);
|
||||
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, frame); //将数据推到oport
|
||||
} else {
|
||||
jlstream_free_frame(frame); //释放iport资源
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int effect_dev0_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct effect_dev0_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void effect_dev0_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = effect_dev0_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int effect_dev0_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
/* struct stream_oport *oport = iport->node->oport;
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
struct effect_dev0_node_hdl *hdl = (struct effect_dev0_node_hdl *)iport->private_data; */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void effect_dev0_ioc_start(struct effect_dev0_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
/* struct jlstream *stream = jlstream_for_node(hdl->node); */
|
||||
|
||||
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
hdl->ch_num = AUDIO_CH_NUM(fmt->channel_mode);
|
||||
audio_effect_dev0_init(hdl->sample_rate, hdl->ch_num);
|
||||
}
|
||||
|
||||
|
||||
/*节点stop函数*/
|
||||
static void effect_dev0_ioc_stop(struct effect_dev0_node_hdl *hdl)
|
||||
{
|
||||
audio_effect_dev0_exit();
|
||||
}
|
||||
|
||||
static int effect_dev0_ioc_update_parm(struct effect_dev0_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = false;
|
||||
return ret;
|
||||
}
|
||||
static int get_effect_dev0_ioc_parm(struct effect_dev0_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int effect_dev0_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct effect_dev0_node_hdl *hdl = (struct effect_dev0_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
effect_dev0_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_OPEN_OPORT:
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= effect_dev0_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
effect_dev0_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
effect_dev0_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void effect_dev0_adapter_release(struct stream_node *node)
|
||||
{
|
||||
free(node->private_data);
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(effect_dev0_node_adapter) = {
|
||||
.name = "effect_dev0",
|
||||
.uuid = NODE_UUID_EFFECT_DEV0,
|
||||
.bind = effect_dev0_adapter_bind,
|
||||
.ioctl = effect_dev0_adapter_ioctl,
|
||||
.release = effect_dev0_adapter_release,
|
||||
};
|
||||
|
||||
/* REGISTER_ONLINE_ADJUST_TARGET(effect_dev0) = { */
|
||||
/* .uuid = NODE_UUID_effect_dev0, */
|
||||
/* }; */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_EFFECT_DEV1_NODE_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST EFFECTS
|
||||
#define LOG_TAG "[EFFECT_dev1-NODE]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_DUMP_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma code_seg(".effect_dev1.text")
|
||||
#pragma data_seg(".effect_dev1.data")
|
||||
#pragma const_seg(".effect_dev1.text.const")
|
||||
#endif
|
||||
|
||||
|
||||
struct effect_dev1_node_hdl {
|
||||
char name[16];
|
||||
struct stream_node *node; //节点句柄
|
||||
void *effect_dev1;
|
||||
u32 sample_rate;
|
||||
u8 ch_num;
|
||||
};
|
||||
|
||||
/* 自定义算法,初始化
|
||||
* sample_rate:采样率
|
||||
* ch_num:通道数,单声道 1,立体声 2, 四声道 4
|
||||
**/
|
||||
static void audio_effect_dev1_init(u32 sample_rate, u8 ch_num)
|
||||
{
|
||||
//do something
|
||||
}
|
||||
|
||||
/* 自定义算法,运行
|
||||
* sample_rate:采样率
|
||||
* ch_num:通道数,单声道 1,立体声 2, 四声道 4
|
||||
* *data:输入输出数据同地址,位宽16bit
|
||||
* data_len :输入数据长度,byte
|
||||
* */
|
||||
static void audio_effect_dev1_run(u32 sample_rate, u8 ch_num, s16 *data, u32 data_len)
|
||||
{
|
||||
//do something
|
||||
/* printf("effect dev1 do something here\n"); */
|
||||
}
|
||||
/* 自定义算法,关闭
|
||||
**/
|
||||
static void audio_effect_dev1_exit()
|
||||
{
|
||||
//do something
|
||||
|
||||
}
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void effect_dev1_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
struct effect_dev1_node_hdl *hdl = (struct effect_dev1_node_hdl *)iport->private_data;
|
||||
struct stream_node *node = iport->node;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
audio_effect_dev1_run(hdl->sample_rate, hdl->ch_num, (s16 *)frame->data, frame->len);
|
||||
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, frame); //将数据推到oport
|
||||
} else {
|
||||
jlstream_free_frame(frame); //释放iport资源
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int effect_dev1_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct effect_dev1_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void effect_dev1_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = effect_dev1_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int effect_dev1_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
/* struct stream_oport *oport = iport->node->oport;
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
struct effect_dev1_node_hdl *hdl = (struct effect_dev1_node_hdl *)iport->private_data; */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void effect_dev1_ioc_start(struct effect_dev1_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
/* struct jlstream *stream = jlstream_for_node(hdl->node); */
|
||||
|
||||
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
hdl->ch_num = AUDIO_CH_NUM(fmt->channel_mode);
|
||||
audio_effect_dev1_init(hdl->sample_rate, hdl->ch_num);
|
||||
}
|
||||
|
||||
|
||||
/*节点stop函数*/
|
||||
static void effect_dev1_ioc_stop(struct effect_dev1_node_hdl *hdl)
|
||||
{
|
||||
audio_effect_dev1_exit();
|
||||
}
|
||||
|
||||
static int effect_dev1_ioc_update_parm(struct effect_dev1_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = false;
|
||||
return ret;
|
||||
}
|
||||
static int get_effect_dev1_ioc_parm(struct effect_dev1_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int effect_dev1_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct effect_dev1_node_hdl *hdl = (struct effect_dev1_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
effect_dev1_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_OPEN_OPORT:
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= effect_dev1_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
effect_dev1_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
effect_dev1_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void effect_dev1_adapter_release(struct stream_node *node)
|
||||
{
|
||||
free(node->private_data);
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(effect_dev1_node_adapter) = {
|
||||
.name = "effect_dev1",
|
||||
.uuid = NODE_UUID_EFFECT_DEV1,
|
||||
.bind = effect_dev1_adapter_bind,
|
||||
.ioctl = effect_dev1_adapter_ioctl,
|
||||
.release = effect_dev1_adapter_release,
|
||||
};
|
||||
|
||||
/* REGISTER_ONLINE_ADJUST_TARGET(effect_dev1) = { */
|
||||
/* .uuid = NODE_UUID_effect_dev1, */
|
||||
/* }; */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "app_config.h"
|
||||
|
||||
|
||||
#if TCFG_EFFECT_DEV2_NODE_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST EFFECTS
|
||||
#define LOG_TAG "[EFFECT_dev2-NODE]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_DUMP_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma code_seg(".effect_dev2.text")
|
||||
#pragma data_seg(".effect_dev2.data")
|
||||
#pragma const_seg(".effect_dev2.text.const")
|
||||
#endif
|
||||
|
||||
|
||||
struct effect_dev2_node_hdl {
|
||||
char name[16];
|
||||
struct stream_node *node; //节点句柄
|
||||
void *effect_dev2;
|
||||
u32 sample_rate;
|
||||
u8 ch_num;
|
||||
};
|
||||
|
||||
/* 自定义算法,初始化
|
||||
* sample_rate:采样率
|
||||
* ch_num:通道数,单声道 1,立体声 2, 四声道 4
|
||||
**/
|
||||
static void audio_effect_dev2_init(u32 sample_rate, u8 ch_num)
|
||||
{
|
||||
//do something
|
||||
}
|
||||
|
||||
/* 自定义算法,运行
|
||||
* sample_rate:采样率
|
||||
* ch_num:通道数,单声道 1,立体声 2, 四声道 4
|
||||
* *data:输入输出数据同地址,位宽16bit
|
||||
* data_len :输入数据长度,byte
|
||||
* */
|
||||
static void audio_effect_dev2_run(u32 sample_rate, u8 ch_num, s16 *data, u32 data_len)
|
||||
{
|
||||
//do something
|
||||
/* printf("effect dev2 do something here\n"); */
|
||||
}
|
||||
/* 自定义算法,关闭
|
||||
**/
|
||||
static void audio_effect_dev2_exit()
|
||||
{
|
||||
//do something
|
||||
|
||||
}
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void effect_dev2_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
struct effect_dev2_node_hdl *hdl = (struct effect_dev2_node_hdl *)iport->private_data;
|
||||
struct stream_node *node = iport->node;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
audio_effect_dev2_run(hdl->sample_rate, hdl->ch_num, (s16 *)frame->data, frame->len);
|
||||
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, frame); //将数据推到oport
|
||||
} else {
|
||||
jlstream_free_frame(frame); //释放iport资源
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int effect_dev2_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct effect_dev2_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void effect_dev2_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = effect_dev2_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int effect_dev2_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
/* struct stream_oport *oport = iport->node->oport;
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
struct effect_dev2_node_hdl *hdl = (struct effect_dev2_node_hdl *)iport->private_data; */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void effect_dev2_ioc_start(struct effect_dev2_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
/* struct jlstream *stream = jlstream_for_node(hdl->node); */
|
||||
|
||||
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
hdl->ch_num = AUDIO_CH_NUM(fmt->channel_mode);
|
||||
audio_effect_dev2_init(hdl->sample_rate, hdl->ch_num);
|
||||
}
|
||||
|
||||
|
||||
/*节点stop函数*/
|
||||
static void effect_dev2_ioc_stop(struct effect_dev2_node_hdl *hdl)
|
||||
{
|
||||
audio_effect_dev2_exit();
|
||||
}
|
||||
|
||||
static int effect_dev2_ioc_update_parm(struct effect_dev2_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = false;
|
||||
return ret;
|
||||
}
|
||||
static int get_effect_dev2_ioc_parm(struct effect_dev2_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int effect_dev2_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct effect_dev2_node_hdl *hdl = (struct effect_dev2_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
effect_dev2_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_OPEN_OPORT:
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= effect_dev2_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
effect_dev2_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
effect_dev2_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void effect_dev2_adapter_release(struct stream_node *node)
|
||||
{
|
||||
free(node->private_data);
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(effect_dev2_node_adapter) = {
|
||||
.name = "effect_dev2",
|
||||
.uuid = NODE_UUID_EFFECT_DEV2,
|
||||
.bind = effect_dev2_adapter_bind,
|
||||
.ioctl = effect_dev2_adapter_ioctl,
|
||||
.release = effect_dev2_adapter_release,
|
||||
};
|
||||
|
||||
/* REGISTER_ONLINE_ADJUST_TARGET(effect_dev2) = { */
|
||||
/* .uuid = NODE_UUID_effect_dev2, */
|
||||
/* }; */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "app_config.h"
|
||||
|
||||
|
||||
#if TCFG_EFFECT_DEV3_NODE_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST EFFECTS
|
||||
#define LOG_TAG "[EFFECT_dev3-NODE]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_DUMP_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma code_seg(".effect_dev3.text")
|
||||
#pragma data_seg(".effect_dev3.data")
|
||||
#pragma const_seg(".effect_dev3.text.const")
|
||||
#endif
|
||||
|
||||
|
||||
struct effect_dev3_node_hdl {
|
||||
char name[16];
|
||||
struct stream_node *node; //节点句柄
|
||||
void *effect_dev3;
|
||||
u32 sample_rate;
|
||||
u8 ch_num;
|
||||
};
|
||||
|
||||
/* 自定义算法,初始化
|
||||
* sample_rate:采样率
|
||||
* ch_num:通道数,单声道 1,立体声 2, 四声道 4
|
||||
**/
|
||||
static void audio_effect_dev3_init(u32 sample_rate, u8 ch_num)
|
||||
{
|
||||
//do something
|
||||
}
|
||||
|
||||
/* 自定义算法,运行
|
||||
* sample_rate:采样率
|
||||
* ch_num:通道数,单声道 1,立体声 2, 四声道 4
|
||||
* *data:输入输出数据同地址,位宽16bit
|
||||
* data_len :输入数据长度,byte
|
||||
* */
|
||||
static void audio_effect_dev3_run(u32 sample_rate, u8 ch_num, s16 *data, u32 data_len)
|
||||
{
|
||||
//do something
|
||||
/* printf("effect dev3 do something here\n"); */
|
||||
}
|
||||
/* 自定义算法,关闭
|
||||
**/
|
||||
static void audio_effect_dev3_exit()
|
||||
{
|
||||
//do something
|
||||
|
||||
}
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void effect_dev3_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
struct effect_dev3_node_hdl *hdl = (struct effect_dev3_node_hdl *)iport->private_data;
|
||||
struct stream_node *node = iport->node;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
audio_effect_dev3_run(hdl->sample_rate, hdl->ch_num, (s16 *)frame->data, frame->len);
|
||||
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, frame); //将数据推到oport
|
||||
} else {
|
||||
jlstream_free_frame(frame); //释放iport资源
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int effect_dev3_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct effect_dev3_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void effect_dev3_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = effect_dev3_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int effect_dev3_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
/* struct stream_oport *oport = iport->node->oport;
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
struct effect_dev3_node_hdl *hdl = (struct effect_dev3_node_hdl *)iport->private_data; */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void effect_dev3_ioc_start(struct effect_dev3_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
/* struct jlstream *stream = jlstream_for_node(hdl->node); */
|
||||
|
||||
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
hdl->ch_num = AUDIO_CH_NUM(fmt->channel_mode);
|
||||
audio_effect_dev3_init(hdl->sample_rate, hdl->ch_num);
|
||||
}
|
||||
|
||||
|
||||
/*节点stop函数*/
|
||||
static void effect_dev3_ioc_stop(struct effect_dev3_node_hdl *hdl)
|
||||
{
|
||||
audio_effect_dev3_exit();
|
||||
}
|
||||
|
||||
static int effect_dev3_ioc_update_parm(struct effect_dev3_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = false;
|
||||
return ret;
|
||||
}
|
||||
static int get_effect_dev3_ioc_parm(struct effect_dev3_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int effect_dev3_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct effect_dev3_node_hdl *hdl = (struct effect_dev3_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
effect_dev3_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_OPEN_OPORT:
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= effect_dev3_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
effect_dev3_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
effect_dev3_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void effect_dev3_adapter_release(struct stream_node *node)
|
||||
{
|
||||
free(node->private_data);
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(effect_dev3_node_adapter) = {
|
||||
.name = "effect_dev3",
|
||||
.uuid = NODE_UUID_EFFECT_DEV3,
|
||||
.bind = effect_dev3_adapter_bind,
|
||||
.ioctl = effect_dev3_adapter_ioctl,
|
||||
.release = effect_dev3_adapter_release,
|
||||
};
|
||||
|
||||
/* REGISTER_ONLINE_ADJUST_TARGET(effect_dev3) = { */
|
||||
/* .uuid = NODE_UUID_effect_dev3, */
|
||||
/* }; */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "app_config.h"
|
||||
|
||||
|
||||
#if TCFG_EFFECT_DEV4_NODE_ENABLE
|
||||
|
||||
#define LOG_TAG_CONST EFFECTS
|
||||
#define LOG_TAG "[EFFECT_dev4-NODE]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#define LOG_DUMP_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma code_seg(".effect_dev4.text")
|
||||
#pragma data_seg(".effect_dev4.data")
|
||||
#pragma const_seg(".effect_dev4.text.const")
|
||||
#endif
|
||||
|
||||
|
||||
struct effect_dev4_node_hdl {
|
||||
char name[16];
|
||||
struct stream_node *node; //节点句柄
|
||||
void *effect_dev4;
|
||||
u32 sample_rate;
|
||||
u8 ch_num;
|
||||
};
|
||||
|
||||
/* 自定义算法,初始化
|
||||
* sample_rate:采样率
|
||||
* ch_num:通道数,单声道 1,立体声 2, 四声道 4
|
||||
**/
|
||||
static void audio_effect_dev4_init(u32 sample_rate, u8 ch_num)
|
||||
{
|
||||
//do something
|
||||
}
|
||||
|
||||
/* 自定义算法,运行
|
||||
* sample_rate:采样率
|
||||
* ch_num:通道数,单声道 1,立体声 2, 四声道 4
|
||||
* *data:输入输出数据同地址,位宽16bit
|
||||
* data_len :输入数据长度,byte
|
||||
* */
|
||||
static void audio_effect_dev4_run(u32 sample_rate, u8 ch_num, s16 *data, u32 data_len)
|
||||
{
|
||||
//do something
|
||||
/* printf("effect dev4 do something here\n"); */
|
||||
}
|
||||
/* 自定义算法,关闭
|
||||
**/
|
||||
static void audio_effect_dev4_exit()
|
||||
{
|
||||
//do something
|
||||
|
||||
}
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void effect_dev4_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
struct effect_dev4_node_hdl *hdl = (struct effect_dev4_node_hdl *)iport->private_data;
|
||||
struct stream_node *node = iport->node;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
audio_effect_dev4_run(hdl->sample_rate, hdl->ch_num, (s16 *)frame->data, frame->len);
|
||||
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, frame); //将数据推到oport
|
||||
} else {
|
||||
jlstream_free_frame(frame); //释放iport资源
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int effect_dev4_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct effect_dev4_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void effect_dev4_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = effect_dev4_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int effect_dev4_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
/* struct stream_oport *oport = iport->node->oport;
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
struct effect_dev4_node_hdl *hdl = (struct effect_dev4_node_hdl *)iport->private_data; */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void effect_dev4_ioc_start(struct effect_dev4_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
/* struct jlstream *stream = jlstream_for_node(hdl->node); */
|
||||
|
||||
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
hdl->ch_num = AUDIO_CH_NUM(fmt->channel_mode);
|
||||
audio_effect_dev4_init(hdl->sample_rate, hdl->ch_num);
|
||||
}
|
||||
|
||||
|
||||
/*节点stop函数*/
|
||||
static void effect_dev4_ioc_stop(struct effect_dev4_node_hdl *hdl)
|
||||
{
|
||||
audio_effect_dev4_exit();
|
||||
}
|
||||
|
||||
static int effect_dev4_ioc_update_parm(struct effect_dev4_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = false;
|
||||
return ret;
|
||||
}
|
||||
static int get_effect_dev4_ioc_parm(struct effect_dev4_node_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int effect_dev4_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct effect_dev4_node_hdl *hdl = (struct effect_dev4_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
effect_dev4_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_OPEN_OPORT:
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= effect_dev4_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
effect_dev4_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
effect_dev4_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void effect_dev4_adapter_release(struct stream_node *node)
|
||||
{
|
||||
free(node->private_data);
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(effect_dev4_node_adapter) = {
|
||||
.name = "effect_dev4",
|
||||
.uuid = NODE_UUID_EFFECT_DEV4,
|
||||
.bind = effect_dev4_adapter_bind,
|
||||
.ioctl = effect_dev4_adapter_ioctl,
|
||||
.release = effect_dev4_adapter_release,
|
||||
};
|
||||
|
||||
/* REGISTER_ONLINE_ADJUST_TARGET(effect_dev4) = { */
|
||||
/* .uuid = NODE_UUID_effect_dev4, */
|
||||
/* }; */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".esco_tx_node.data.bss")
|
||||
#pragma data_seg(".esco_tx_node.data")
|
||||
#pragma const_seg(".esco_tx_node.text.const")
|
||||
#pragma code_seg(".esco_tx_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "classic/hci_lmp.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "sync/audio_syncts.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_USER_BT_CLASSIC_ENABLE
|
||||
|
||||
struct esco_tx_sync_node {
|
||||
u8 trigger;
|
||||
void *syncts;
|
||||
struct list_head entry;
|
||||
};
|
||||
|
||||
struct esco_tx_hdl {
|
||||
u8 start;
|
||||
u8 reference_network;
|
||||
u8 tx_frames;
|
||||
u8 bt_addr[6];
|
||||
struct list_head sync_list;
|
||||
};
|
||||
|
||||
extern void bt_edr_conn_system_clock_init(void *addr, u8 factor);
|
||||
extern u32 bt_edr_conn_master_to_local_time(void *addr, u32 usec);
|
||||
|
||||
static void esco_tx_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
/* struct esco_tx_hdl *hdl = (struct esco_tx_hdl *)iport->private_data; */
|
||||
struct stream_frame *frame;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note);
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
/*printf("esco_tx: %d\n", frame->len);*/
|
||||
lmp_private_send_esco_packet(NULL, frame->data, frame->len);
|
||||
|
||||
jlstream_free_frame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void esco_tx_tick_handler(void *priv, u32 clkn)
|
||||
{
|
||||
struct esco_tx_hdl *hdl = (struct esco_tx_hdl *)priv;
|
||||
struct esco_tx_sync_node *node = NULL;
|
||||
|
||||
u32 time = bt_edr_conn_master_to_local_time(hdl->bt_addr, (clkn * 625));
|
||||
list_for_each_entry(node, &hdl->sync_list, entry) {
|
||||
if (!node->trigger) {
|
||||
node->trigger = 1;
|
||||
sound_pcm_syncts_latch_trigger(node->syncts);
|
||||
}
|
||||
sound_pcm_update_frame_num(node->syncts, hdl->tx_frames);
|
||||
sound_pcm_update_frame_num_and_time(node->syncts, 0, time, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int esco_tx_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void esco_tx_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
struct esco_tx_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
|
||||
iport->private_data = hdl;
|
||||
|
||||
INIT_LIST_HEAD(&hdl->sync_list);
|
||||
hdl->reference_network = 0xff;
|
||||
iport->handle_frame = esco_tx_handle_frame;
|
||||
}
|
||||
|
||||
static void esco_tx_set_bt_addr(struct esco_tx_hdl *hdl, void *bt_addr)
|
||||
{
|
||||
memcpy(hdl->bt_addr, bt_addr, 6);
|
||||
|
||||
|
||||
bt_edr_conn_system_clock_init(hdl->bt_addr, 1);
|
||||
}
|
||||
|
||||
static int esco_tx_ioc_fmt_nego(struct stream_iport *iport)
|
||||
{
|
||||
struct esco_tx_hdl *hdl = (struct esco_tx_hdl *)iport->private_data;
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
|
||||
int type = lmp_private_get_esco_packet_type();
|
||||
int frame_time = (lmp_private_get_esco_packet_type() >> 8) & 0xff;
|
||||
int media_type = type & 0xff;
|
||||
if (media_type == 0) {
|
||||
in_fmt->sample_rate = 8000;
|
||||
in_fmt->coding_type = AUDIO_CODING_CVSD;
|
||||
hdl->tx_frames = frame_time == 12 ? 60 : 30;
|
||||
} else {
|
||||
in_fmt->sample_rate = 16000;
|
||||
in_fmt->coding_type = AUDIO_CODING_MSBC;
|
||||
hdl->tx_frames = frame_time == 12 ? 120 : 60;
|
||||
}
|
||||
in_fmt->channel_mode = AUDIO_CH_MIX;
|
||||
|
||||
return NEGO_STA_ACCPTED;
|
||||
}
|
||||
|
||||
static int esco_tx_mount_syncts(struct esco_tx_hdl *hdl, void *syncts, u32 timestamp, u8 network)
|
||||
{
|
||||
struct esco_tx_sync_node *node = NULL;
|
||||
list_for_each_entry(node, &hdl->sync_list, entry) {
|
||||
if ((u32)node->syncts == (u32)syncts) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
node = (struct esco_tx_sync_node *)zalloc(sizeof(struct esco_tx_sync_node));
|
||||
node->syncts = syncts;
|
||||
|
||||
/*g_printf("esco tx mount syncts : 0x%x, %u\n", (u32)syncts, timestamp);*/
|
||||
if (hdl->reference_network == 0xff) {
|
||||
hdl->reference_network = network;
|
||||
}
|
||||
list_add(&node->entry, &hdl->sync_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void esco_tx_unmount_syncts(struct esco_tx_hdl *hdl, void *syncts)
|
||||
{
|
||||
struct esco_tx_sync_node *node;
|
||||
|
||||
list_for_each_entry(node, &hdl->sync_list, entry) {
|
||||
if (node->syncts == syncts) {
|
||||
goto unmount;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
unmount:
|
||||
/*g_printf("esco tx unmount syncts : 0x%x\n", syncts);*/
|
||||
list_del(&node->entry);
|
||||
free(node);
|
||||
}
|
||||
|
||||
static int esco_tx_syncts_handler(struct esco_tx_hdl *hdl, struct audio_syncts_ioc_params *params)
|
||||
{
|
||||
if (!params) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (params->cmd) {
|
||||
case AUDIO_SYNCTS_MOUNT_ON_SNDPCM:
|
||||
esco_tx_mount_syncts(hdl, (void *)params->data[0], params->data[1], params->data[2]);
|
||||
break;
|
||||
case AUDIO_SYNCTS_UMOUNT_ON_SNDPCM:
|
||||
esco_tx_unmount_syncts(hdl, (void *)params->data[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int esco_tx_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
struct esco_tx_hdl *hdl = (struct esco_tx_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_SET_BTADDR:
|
||||
esco_tx_set_bt_addr(hdl, (void *)arg);
|
||||
break;
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
esco_tx_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
lmp_esco_set_tx_notify(hdl->bt_addr, hdl, esco_tx_tick_handler);
|
||||
break;
|
||||
case NODE_IOC_STOP:
|
||||
lmp_esco_set_tx_notify(hdl->bt_addr, NULL, NULL);
|
||||
break;
|
||||
case NODE_IOC_CLOSE_IPORT:
|
||||
free(hdl);
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= esco_tx_ioc_fmt_nego(iport);
|
||||
break;
|
||||
case NODE_IOC_SYNCTS:
|
||||
esco_tx_syncts_handler(hdl, (struct audio_syncts_ioc_params *)arg);
|
||||
break;
|
||||
case NODE_IOC_GET_DELAY:
|
||||
return lmp_private_get_esco_tx_packet_num() * 75;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void esco_tx_release(struct stream_node *node)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
REGISTER_STREAM_NODE_ADAPTER(esco_tx_adapter) = {
|
||||
.name = "esco_tx",
|
||||
.uuid = NODE_UUID_ESCO_TX,
|
||||
.bind = esco_tx_bind,
|
||||
.ioctl = esco_tx_ioctl,
|
||||
.release = esco_tx_release,
|
||||
};
|
||||
|
||||
#endif /* #if TCFG_USER_BT_CLASSIC_ENABLE */
|
||||
@@ -0,0 +1,237 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".ns_node.data.bss")
|
||||
#pragma data_seg(".ns_node.data")
|
||||
#pragma const_seg(".ns_node.text.const")
|
||||
#pragma code_seg(".ns_node.text")
|
||||
#endif
|
||||
|
||||
#include "jlstream.h"
|
||||
#include "audio_config.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "audio_ns.h"
|
||||
#include "btstack/avctp_user.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "frame_length_adaptive.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if 1
|
||||
#define ns_log printf
|
||||
#else
|
||||
#define ns_log(...)
|
||||
#endif/*log_en*/
|
||||
|
||||
#if TCFG_NS_NODE_ENABLE
|
||||
|
||||
enum {
|
||||
AUDIO_NS_TYPE_ESCO_DL = 1, //下行降噪类型
|
||||
AUDIO_NS_TYPE_GENERAL, //通用类型
|
||||
};
|
||||
|
||||
struct ns_cfg_t {
|
||||
u8 ns_type;//降噪类型选择,通用降噪/通话下行降噪
|
||||
u8 call_active_trigger;//接通电话触发标志, 只有通话下行降噪会使用
|
||||
u8 mode; //ans降噪模式(0,1,2:越大越耗资源,效果越好),dns降噪时,用于选择dns算法
|
||||
float aggressfactor; //降噪强度(越大越强:1~2)
|
||||
float minsuppress; //降噪最小压制(越小越强:0~1)
|
||||
float noiselevel; //初始噪声水平(评估初始噪声,加快收敛)
|
||||
} __attribute__((packed));
|
||||
|
||||
struct ns_node_hdl {
|
||||
u16 sr;
|
||||
void *ns;
|
||||
struct stream_frame *out_frame;
|
||||
struct stream_node *node; //节点句柄
|
||||
struct ns_cfg_t cfg;
|
||||
struct frame_length_adaptive_hdl *olen_adaptive;//输出长度适配
|
||||
};
|
||||
|
||||
extern int db2mag(int db, int dbQ, int magDQ);//10^db/20
|
||||
int ns_param_cfg_read(struct stream_node *node)
|
||||
{
|
||||
struct ns_cfg_t config;
|
||||
struct ns_node_hdl *hdl = (struct ns_node_hdl *)node->private_data;
|
||||
int ret = 0;
|
||||
if (!hdl) {
|
||||
return -1 ;
|
||||
}
|
||||
/*
|
||||
*获取配置文件内的参数,及名字
|
||||
* */
|
||||
ret = jlstream_read_node_data_new(NODE_UUID_NOISE_SUPPRESSOR, node->subid, (void *)&config, NULL);
|
||||
if (ret != sizeof(config)) {
|
||||
printf("%s, read node data err %d = %d\n", __FUNCTION__, ret, (int)sizeof(config));
|
||||
return -1 ;
|
||||
}
|
||||
|
||||
hdl->cfg = config;
|
||||
|
||||
ns_log("type %d\n", hdl->cfg.ns_type);
|
||||
ns_log("call_active_trigger %d\n", hdl->cfg.call_active_trigger);
|
||||
ns_log("mode %d\n", hdl->cfg.mode);
|
||||
ns_log("aggressfactor %d/1000\n", (int)(hdl->cfg.aggressfactor * 1000.f));
|
||||
ns_log("minsuppress %d/1000\n", (int)(hdl->cfg.minsuppress * 1000.f));
|
||||
ns_log("noiselevel %d/1000\n", (int)(hdl->cfg.noiselevel * 1000.f));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点输出回调处理,可处理数据或post信号量*/
|
||||
static void ns_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct ns_node_hdl *hdl = (struct ns_node_hdl *)iport->private_data;
|
||||
struct stream_node *node = iport->node;
|
||||
struct stream_frame *in_frame;
|
||||
int wlen;
|
||||
u8 trigger = 1;
|
||||
int out_frame_len = 0;
|
||||
|
||||
out_frame_len = ANS_OUT_POINTS_MAX << 1;
|
||||
|
||||
while (1) {
|
||||
in_frame = jlstream_pull_frame(iport, note); //从iport读取数据
|
||||
if (!in_frame) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdl->cfg.ns_type == AUDIO_NS_TYPE_ESCO_DL) {
|
||||
if (hdl->cfg.call_active_trigger) {
|
||||
if (bt_get_call_status() != BT_CALL_ACTIVE) {
|
||||
trigger = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (trigger) {
|
||||
/*接通的时候再开始做降噪*/
|
||||
out_frame_len = in_frame->len > out_frame_len ? in_frame->len : out_frame_len;
|
||||
if (!hdl->out_frame) {
|
||||
hdl->out_frame = jlstream_get_frame(node->oport, out_frame_len);
|
||||
if (!hdl->out_frame) {
|
||||
jlstream_return_frame(iport, in_frame);
|
||||
return;
|
||||
}
|
||||
}
|
||||
wlen = audio_ns_run(hdl->ns, (s16 *)in_frame->data,
|
||||
(s16 *)hdl->out_frame->data, in_frame->len);
|
||||
|
||||
if (!hdl->olen_adaptive) {
|
||||
hdl->olen_adaptive = frame_length_adaptive_open(in_frame->len, out_frame_len);
|
||||
}
|
||||
int len = frame_length_adaptive_run(hdl->olen_adaptive, (s16 *)hdl->out_frame->data, (s16 *)hdl->out_frame->data, wlen);
|
||||
|
||||
if (len) {
|
||||
hdl->out_frame->len = len;
|
||||
jlstream_push_frame(node->oport, hdl->out_frame); //将数据推到oport
|
||||
hdl->out_frame = NULL;
|
||||
}
|
||||
jlstream_free_frame(in_frame); //释放iport资源
|
||||
} else {
|
||||
jlstream_push_frame(node->oport, in_frame); //将数据推到oport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int ns_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct ns_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
|
||||
hdl->node = node;
|
||||
node->private_data = hdl; //保存私有信息
|
||||
ns_param_cfg_read(node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void ns_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = ns_handle_frame; //注册输出回调
|
||||
iport->private_data = iport->node->private_data; //保存节点私有句柄
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int ns_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void ns_ioc_start(struct ns_node_hdl *hdl)
|
||||
{
|
||||
struct stream_fmt *fmt = &hdl->node->oport->fmt;
|
||||
|
||||
printf("ans node start");
|
||||
/*打开算法*/
|
||||
hdl->ns = audio_ns_open(fmt->sample_rate, hdl->cfg.mode, hdl->cfg.noiselevel, hdl->cfg.aggressfactor, hdl->cfg.minsuppress);
|
||||
}
|
||||
|
||||
/*节点stop函数*/
|
||||
static void ns_ioc_stop(struct ns_node_hdl *hdl)
|
||||
{
|
||||
if (hdl->ns) {
|
||||
audio_ns_close(hdl->ns);
|
||||
hdl->ns = NULL;
|
||||
}
|
||||
if (hdl->olen_adaptive) {
|
||||
frame_length_adaptive_close(hdl->olen_adaptive);
|
||||
hdl->olen_adaptive = NULL;
|
||||
}
|
||||
if (hdl->out_frame) {
|
||||
jlstream_free_frame(hdl->out_frame);
|
||||
hdl->out_frame = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int ns_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ns_node_hdl *hdl = (struct ns_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
ns_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= ns_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
ns_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
ns_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void ns_adapter_release(struct stream_node *node)
|
||||
{
|
||||
struct ns_node_hdl *hdl = (struct ns_node_hdl *)node->private_data;
|
||||
if (!hdl) {
|
||||
return;
|
||||
}
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(ns_node_adapter) = {
|
||||
.name = "ns",
|
||||
.uuid = NODE_UUID_NOISE_SUPPRESSOR,
|
||||
.bind = ns_adapter_bind,
|
||||
.ioctl = ns_adapter_ioctl,
|
||||
.release = ns_adapter_release,
|
||||
};
|
||||
|
||||
//注册工具在线调试
|
||||
REGISTER_ONLINE_ADJUST_TARGET(noise_suppressor) = {
|
||||
.uuid = NODE_UUID_NOISE_SUPPRESSOR,
|
||||
};
|
||||
|
||||
#endif/* TCFG_NS_NODE_ENABLE*/
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".pc_mic_node.data.bss")
|
||||
#pragma data_seg(".pc_mic_node.data")
|
||||
#pragma const_seg(".pc_mic_node.text.const")
|
||||
#pragma code_seg(".pc_mic_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "sync/audio_syncts.h"
|
||||
#include "circular_buf.h"
|
||||
#include "audio_splicing.h"
|
||||
#include "app_config.h"
|
||||
#include "gpio.h"
|
||||
#include "audio_cvp.h"
|
||||
#include "media/audio_general.h"
|
||||
#include "pc_mic_recoder.h"
|
||||
#include "uac_stream.h"
|
||||
#include "effects/convert_data.h"
|
||||
|
||||
#if TCFG_USB_SLAVE_AUDIO_MIC_ENABLE
|
||||
|
||||
#define PC_MIC_BUF_SIZE (1024 * 4)
|
||||
#define PC_MIC_SINGLE2DUAL_BUF_LEN (1024 * 2)
|
||||
#define PC_MIC_3BYTE_24BIT_BUF_LEN (1024 * 3) //单转双buf的3/2倍
|
||||
|
||||
static u8 pc_mic_node_get_data_flag = 0;
|
||||
|
||||
struct pc_mic_node_hdl {
|
||||
u8 start;
|
||||
u8 cache_flag;
|
||||
u8 iport_channel_mode; //保存输入节点的声道数
|
||||
u8 iport_bit_width; //保存输入节点的位宽
|
||||
u8 tmp_buf[PC_MIC_SINGLE2DUAL_BUF_LEN]; //单转双用到的数组
|
||||
u8 pcm_3byte_24bit_buf[PC_MIC_3BYTE_24BIT_BUF_LEN]; //16bit转3byte_24bit的buf
|
||||
u32 input_rate;
|
||||
u32 output_rate;
|
||||
struct stream_node *node;
|
||||
struct stream_frame *frame;
|
||||
enum stream_scene scene;
|
||||
cbuffer_t pc_mic_cache_cbuffer;
|
||||
u8 *cache_buf;
|
||||
};
|
||||
|
||||
struct pc_mic_fmt_t {
|
||||
u8 init;
|
||||
u8 channel;
|
||||
u8 bit;
|
||||
u32 sample_rate;
|
||||
};
|
||||
struct pc_mic_fmt_t pc_mic_fmt = {
|
||||
.init = 0,
|
||||
.channel = MIC_CHANNEL,
|
||||
.bit = MIC_AUDIO_RES,
|
||||
.sample_rate = MIC_AUDIO_RATE,
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(pc_mic_lock);
|
||||
|
||||
//pc mic 驱动拿数接口
|
||||
static int pc_mic_tx_handler(void *priv, void *buf, int len)
|
||||
{
|
||||
struct pc_mic_node_hdl *hdl = (struct pc_mic_node_hdl *)priv;
|
||||
int rlen = 0;
|
||||
/* spin_lock(&pc_mic_lock); */
|
||||
if (hdl) {
|
||||
if (hdl->start == 1 && hdl->cache_flag == 1 && hdl->cache_buf) {
|
||||
rlen = cbuf_read(&hdl->pc_mic_cache_cbuffer, buf, len);
|
||||
}
|
||||
}
|
||||
/* spin_unlock(&pc_mic_lock); */
|
||||
return rlen;
|
||||
}
|
||||
|
||||
// 数据流节点回调,做数据缓存
|
||||
__STREAM_CACHE_CODE
|
||||
static void pc_mic_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct pc_mic_node_hdl *hdl = (struct pc_mic_node_hdl *)iport->private_data;
|
||||
/* struct stream_frame *frame = NULL; */
|
||||
/* struct stream_node *node = iport->node; */
|
||||
|
||||
if (pc_mic_node_get_data_flag == 0) {
|
||||
pc_mic_node_get_data_flag = 1;
|
||||
}
|
||||
if (hdl->start == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (hdl->frame == NULL) {
|
||||
hdl->frame = jlstream_pull_frame(iport, NULL);
|
||||
if (!hdl->frame) {
|
||||
return;
|
||||
}
|
||||
hdl->frame->offset = 0;
|
||||
}
|
||||
s16 *data = (s16 *)(hdl->frame->data + hdl->frame->offset);
|
||||
u32 remain = hdl->frame->len - hdl->frame->offset;
|
||||
int wlen = 0;
|
||||
|
||||
spin_lock(&pc_mic_lock);
|
||||
|
||||
//声道处理
|
||||
#if (MIC_CHANNEL == 1) //mic的声道是单声道
|
||||
if (AUDIO_CH_NUM(hdl->iport_channel_mode) == 2) {
|
||||
//mic声道是单声道,输入声道是双声道,需要双转单
|
||||
if (hdl->iport_bit_width) {
|
||||
pcm_dual_to_single_32bit(data, data, remain);
|
||||
} else {
|
||||
pcm_dual_to_single(data, data, remain);
|
||||
}
|
||||
remain /= 2;
|
||||
}
|
||||
#elif (MIC_CHANNEL == 2) //mic的声道是双声道
|
||||
if (AUDIO_CH_NUM(hdl->iport_channel_mode) == 1) {
|
||||
//mic声道是双声道,输入声道是单声道,需要单转双
|
||||
s16 *src = data;
|
||||
s16 *dst = (s16 *)(hdl->tmp_buf);
|
||||
remain = (remain > PC_MIC_SINGLE2DUAL_BUF_LEN) ? PC_MIC_SINGLE2DUAL_BUF_LEN : remain;
|
||||
if (hdl->iport_bit_width) {
|
||||
pcm_single_to_dual_32bit(dst, src, remain);
|
||||
} else {
|
||||
pcm_single_to_dual(dst, src, remain);
|
||||
}
|
||||
remain = remain * 2;
|
||||
data = (s16 *)(hdl->tmp_buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
//位宽处理
|
||||
if (pc_mic_fmt.bit == 16) {
|
||||
if (hdl->iport_bit_width) {
|
||||
//输入位宽是4byte_24bit
|
||||
audio_convert_data_32bit_to_16bit_round((s32 *)data, (s16 *)data, remain / 4);
|
||||
remain /= 2;
|
||||
}
|
||||
} else if (pc_mic_fmt.bit == 24) {
|
||||
if (hdl->iport_bit_width) {
|
||||
//输入位宽是4byte_24bit
|
||||
audio_convert_data_4byte24bit_to_3byte24bit((s32 *)data, (s32 *)data, remain / 4);
|
||||
remain = remain * 3 / 4;
|
||||
} else {
|
||||
//输入位宽是16bit
|
||||
remain = (remain > PC_MIC_3BYTE_24BIT_BUF_LEN) ? PC_MIC_3BYTE_24BIT_BUF_LEN : remain;
|
||||
audio_convert_data_16bit_to_3byte24bit((s16 *)data, (s32 *)hdl->pcm_3byte_24bit_buf, remain / 2);
|
||||
remain = remain * 3 / 2;
|
||||
data = (s16 *)hdl->pcm_3byte_24bit_buf;
|
||||
}
|
||||
}
|
||||
|
||||
wlen = cbuf_write(&hdl->pc_mic_cache_cbuffer, data, remain);
|
||||
if (wlen != remain) {
|
||||
putchar('w');
|
||||
}
|
||||
|
||||
if (hdl->cache_flag == 0) {
|
||||
if (cbuf_get_data_len(&hdl->pc_mic_cache_cbuffer) >= PC_MIC_BUF_SIZE / 2) {
|
||||
hdl->cache_flag = 1;
|
||||
}
|
||||
}
|
||||
jlstream_free_frame(hdl->frame);
|
||||
hdl->frame = NULL;
|
||||
spin_unlock(&pc_mic_lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void pc_mic_ioc_start(struct pc_mic_node_hdl *hdl)
|
||||
{
|
||||
spin_lock(&pc_mic_lock);
|
||||
if (hdl->start == 0) {
|
||||
y_printf("## Enter Func:%s, Line:%d\n", __func__, __LINE__);
|
||||
if (!hdl->cache_buf) {
|
||||
hdl->cache_buf = malloc(PC_MIC_BUF_SIZE);
|
||||
if (!hdl->cache_buf) {
|
||||
r_printf("Error, Func:%s, Line:%d\n", __func__, __LINE__);
|
||||
spin_unlock(&pc_mic_lock);
|
||||
return;
|
||||
}
|
||||
cbuf_init(&hdl->pc_mic_cache_cbuffer, hdl->cache_buf, PC_MIC_BUF_SIZE);
|
||||
set_uac_mic_tx_handler((void *)hdl, pc_mic_tx_handler);
|
||||
}
|
||||
hdl->cache_flag = 0;
|
||||
hdl->start = 1;
|
||||
}
|
||||
spin_unlock(&pc_mic_lock);
|
||||
}
|
||||
|
||||
static void pc_mic_ioc_stop(struct pc_mic_node_hdl *hdl)
|
||||
{
|
||||
if (hdl->start == 1) {
|
||||
hdl->cache_flag = 0;
|
||||
hdl->start = 0;
|
||||
}
|
||||
pc_mic_node_get_data_flag = 0;
|
||||
}
|
||||
|
||||
|
||||
static void pc_mic_adapter_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = pc_mic_handle_frame;
|
||||
iport->private_data = iport->node->private_data;
|
||||
}
|
||||
|
||||
static int pc_mic_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
y_printf("## Enter Func : %s\n", __func__);
|
||||
int ret = NEGO_STA_ACCPTED;
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
struct pc_mic_node_hdl *hdl = (struct pc_mic_node_hdl *)iport->private_data;
|
||||
u32 coding_type = in_fmt->coding_type;
|
||||
u8 channel_mode = in_fmt->channel_mode;
|
||||
int output_rate = in_fmt->sample_rate;
|
||||
u8 bit_width = in_fmt->bit_wide;
|
||||
|
||||
if (hdl->scene != STREAM_SCENE_PC_MIC) {
|
||||
r_printf("## Error , Func:%s, Line:%d\n", __func__, __LINE__);
|
||||
ret = NEGO_STA_ABORT;
|
||||
}
|
||||
if (coding_type != AUDIO_CODING_PCM) {
|
||||
r_printf("## Error , Func:%s, Line:%d\n", __func__, __LINE__);
|
||||
ret = NEGO_STA_ABORT;
|
||||
}
|
||||
if (output_rate != pc_mic_get_fmt_sample_rate()) {
|
||||
in_fmt->sample_rate = pc_mic_get_fmt_sample_rate();
|
||||
r_printf("## Error , Func:%s, Line:%d\n", __func__, __LINE__);
|
||||
/* ret = NEGO_STA_ABORT; */
|
||||
ret = NEGO_STA_CONTINUE;
|
||||
}
|
||||
|
||||
hdl->iport_channel_mode = channel_mode;
|
||||
hdl->output_rate = output_rate;
|
||||
hdl->iport_bit_width = bit_width;
|
||||
g_printf("## Func:%s, negotiate_state : %d, output_rate:%d, channel_mode:%d\n", __func__, ret, output_rate, channel_mode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int pc_mic_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct pc_mic_node_hdl *hdl = (struct pc_mic_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
pc_mic_adapter_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= pc_mic_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
hdl->scene = arg;
|
||||
break;
|
||||
case NODE_IOC_GET_DELAY:
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
pc_mic_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
pc_mic_ioc_stop(hdl);
|
||||
break;
|
||||
case NODE_IOC_SYNCTS:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int pc_mic_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
spin_lock(&pc_mic_lock);
|
||||
struct pc_mic_node_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
ASSERT(hdl, "%s hdl = NULL!\n", __func__);
|
||||
hdl->node = node;
|
||||
node->private_data = hdl;
|
||||
spin_unlock(&pc_mic_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pc_mic_adapter_release(struct stream_node *node)
|
||||
{
|
||||
struct pc_mic_node_hdl *hdl = (struct pc_mic_node_hdl *)node->private_data;
|
||||
if (hdl) {
|
||||
spin_lock(&pc_mic_lock);
|
||||
pc_mic_ioc_stop(hdl);
|
||||
if (hdl->cache_buf) {
|
||||
free(hdl->cache_buf);
|
||||
hdl->cache_buf = NULL;
|
||||
}
|
||||
free(hdl);
|
||||
hdl = NULL;
|
||||
spin_unlock(&pc_mic_lock);
|
||||
}
|
||||
}
|
||||
|
||||
// 返回1代表数据流有跑起来
|
||||
u8 pc_mic_get_node_state(void)
|
||||
{
|
||||
return pc_mic_node_get_data_flag;
|
||||
}
|
||||
|
||||
// 设置pc mic 的数据格式,传入0不设置
|
||||
void pc_mic_set_fmt(u8 channel, u8 bit, u32 sample_rate)
|
||||
{
|
||||
y_printf("----------- Call %s, bit:%d, sr:%d\n", __func__, bit, sample_rate);
|
||||
pc_mic_fmt.init = 1;
|
||||
if (channel != 0) {
|
||||
pc_mic_fmt.channel = channel;
|
||||
}
|
||||
if (bit != 0) {
|
||||
pc_mic_fmt.bit = bit;
|
||||
}
|
||||
if (sample_rate != 0) {
|
||||
pc_mic_fmt.sample_rate = sample_rate;
|
||||
}
|
||||
}
|
||||
|
||||
u32 pc_mic_get_fmt_sample_rate(void)
|
||||
{
|
||||
y_printf("Mic Sr : %d\n", pc_mic_fmt.sample_rate);
|
||||
return pc_mic_fmt.sample_rate;
|
||||
}
|
||||
|
||||
|
||||
REGISTER_STREAM_NODE_ADAPTER(pc_mic_node_adapter) = {
|
||||
.name = "pc_mic",
|
||||
.uuid = NODE_UUID_PC_MIC,
|
||||
.bind = pc_mic_adapter_bind,
|
||||
.ioctl = pc_mic_adapter_ioctl,
|
||||
.release = pc_mic_adapter_release,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,283 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".plc_node.data.bss")
|
||||
#pragma data_seg(".plc_node.data")
|
||||
#pragma const_seg(".plc_node.text.const")
|
||||
#pragma code_seg(".plc_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "audio_plc.h"
|
||||
#include "tech_lib/LFaudio_plc_api.h"
|
||||
#include "app_config.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "media/audio_splicing.h"
|
||||
|
||||
#if TCFG_PLC_NODE_ENABLE
|
||||
|
||||
enum audio_plc_type {
|
||||
AUD_PLC_BYPASS = 0, //不做任何处理,但是仍然会有缓存;
|
||||
AUD_PLC_NORMAL, //仅修复
|
||||
AUD_PLC_WITH_FADE //修复的同时做淡入淡出的处理
|
||||
};
|
||||
|
||||
extern int tws_api_get_low_latency_state();
|
||||
struct plc_node_hdl {
|
||||
u8 start;
|
||||
u8 channel_num;
|
||||
enum stream_scene scene; //当前场景
|
||||
struct music_plc *plc;
|
||||
void *esco_plc;
|
||||
char *out_buf;
|
||||
u16 out_buf_len;
|
||||
struct stream_node *node; //节点句柄
|
||||
struct node_port_data_wide data_wide;
|
||||
u8 Qval;//数据的饱和位宽
|
||||
};
|
||||
struct music_plc {
|
||||
LFaudio_PLC_API *plc_ops;
|
||||
void *plc_mem;
|
||||
af_DataType datatype;
|
||||
};
|
||||
|
||||
struct music_plc *music_plc_open(struct plc_node_hdl *hdl, u32 sr, u8 ch_num)
|
||||
{
|
||||
|
||||
struct music_plc *plc = zalloc(sizeof(struct music_plc));
|
||||
if (plc) {
|
||||
plc->datatype.IndataBit = hdl->data_wide.iport_data_wide;
|
||||
plc->datatype.OutdataBit = hdl->data_wide.oport_data_wide;
|
||||
plc->datatype.IndataInc = (ch_num == 2) ? 2 : 1;
|
||||
plc->datatype.OutdataInc = (ch_num == 2) ? 2 : 1;
|
||||
plc->plc_ops = get_lfaudioPLC_api();
|
||||
int plc_mem_size = plc->plc_ops->need_buf(ch_num, &plc->datatype); // 3660bytes,请衡量是否使用该空间换取PLC处理
|
||||
plc->plc_mem = malloc(plc_mem_size);
|
||||
if (!plc->plc_mem) {
|
||||
plc->plc_ops = NULL;
|
||||
free(plc);
|
||||
return NULL;
|
||||
}
|
||||
#if TCFG_USER_TWS_ENABLE
|
||||
u8 mode = tws_api_get_low_latency_state() ? 4 : 0;
|
||||
#else
|
||||
u8 mode = 0;
|
||||
#endif
|
||||
plc->plc_ops->open(plc->plc_mem, ch_num, sr, mode, &plc->datatype); //4是延时最低 16个点
|
||||
}
|
||||
return plc;
|
||||
}
|
||||
|
||||
|
||||
void music_plc_run(struct music_plc *plc, s16 *data, u16 len, u8 repair)
|
||||
{
|
||||
if (plc && plc->plc_ops) {
|
||||
u16 point_offset = plc->datatype.IndataBit ? 2 : 1;
|
||||
u16 plc_type = repair ? AUD_PLC_WITH_FADE : AUD_PLC_BYPASS;
|
||||
plc->plc_ops->run(plc->plc_mem, data, data, len >> point_offset, plc_type);
|
||||
}
|
||||
}
|
||||
|
||||
void music_plc_set_bit_wide(struct music_plc *plc, struct node_port_data_wide data_wide)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
void music_plc_close(struct music_plc *plc)
|
||||
{
|
||||
if (plc) {
|
||||
if (plc->plc_mem) {
|
||||
free(plc->plc_mem);
|
||||
plc->plc_mem = NULL;
|
||||
}
|
||||
free(plc);
|
||||
}
|
||||
}
|
||||
/*
|
||||
*通话plc只支持单声道
|
||||
* */
|
||||
void *esco_plc_open(struct plc_node_hdl *hdl, u32 sr, u8 ch_num)
|
||||
{
|
||||
hdl->channel_num = ch_num;
|
||||
af_DataType dataTypeobj = {0};
|
||||
dataTypeobj.IndataBit = hdl->data_wide.iport_data_wide;
|
||||
dataTypeobj.OutdataBit = hdl->data_wide.oport_data_wide;
|
||||
dataTypeobj.IndataInc = 1;
|
||||
dataTypeobj.OutdataInc = 1;
|
||||
dataTypeobj.Qval = hdl->node->oport->fmt.Qval;
|
||||
void *esco_plc = audio_plc_open(sr, 1, &dataTypeobj);
|
||||
return esco_plc;
|
||||
}
|
||||
|
||||
void esco_plc_run(struct plc_node_hdl *hdl, s16 *data, u16 len, u8 repair_flag)
|
||||
{
|
||||
if (!hdl->esco_plc) {
|
||||
return;
|
||||
}
|
||||
u8 ch_num = hdl->channel_num;
|
||||
u8 bit_wide = hdl->data_wide.oport_data_wide;
|
||||
if (ch_num == 2) {
|
||||
if (hdl->out_buf_len < len / 2) {
|
||||
if (hdl->out_buf) {
|
||||
free(hdl->out_buf);
|
||||
hdl->out_buf = NULL;
|
||||
}
|
||||
}
|
||||
if (!hdl->out_buf) {
|
||||
hdl->out_buf = malloc(len / 2);
|
||||
hdl->out_buf_len = len / 2;
|
||||
}
|
||||
if (hdl->out_buf) {
|
||||
if (bit_wide) {
|
||||
if (config_media_24bit_enable) {
|
||||
pcm_dual_to_single_32bit(hdl->out_buf, data, len);
|
||||
audio_plc_run(hdl->esco_plc, (s16 *)hdl->out_buf, len / 2, repair_flag);
|
||||
pcm_single_to_dual_32bit(data, hdl->out_buf, len / 2);
|
||||
}
|
||||
} else {
|
||||
pcm_dual_to_single(hdl->out_buf, data, len);
|
||||
audio_plc_run(hdl->esco_plc, (s16 *)hdl->out_buf, len / 2, repair_flag);
|
||||
pcm_single_to_dual(data, hdl->out_buf, len / 2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
audio_plc_run(hdl->esco_plc, (s16 *)data, len, repair_flag);
|
||||
}
|
||||
}
|
||||
|
||||
void esco_plc_close(void *esco_plc)
|
||||
{
|
||||
if (!esco_plc) {
|
||||
return;
|
||||
}
|
||||
|
||||
audio_plc_close(esco_plc);
|
||||
}
|
||||
|
||||
static void plc_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
struct stream_node *node = iport->node;
|
||||
struct plc_node_hdl *hdl = (struct plc_node_hdl *)iport->private_data;
|
||||
u8 repair_flag = 0;
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note);
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
if ((frame->flags & (FRAME_FLAG_FILL_PACKET & ~FRAME_FLAG_FLUSH_OUT)) == (FRAME_FLAG_FILL_PACKET & ~FRAME_FLAG_FLUSH_OUT)) {
|
||||
repair_flag = 1;
|
||||
//putchar('Q');
|
||||
}
|
||||
if (hdl->scene == STREAM_SCENE_A2DP) {
|
||||
music_plc_run(hdl->plc, (s16 *)frame->data, frame->len, repair_flag);
|
||||
} else {
|
||||
esco_plc_run(hdl, (s16 *)frame->data, frame->len, repair_flag);
|
||||
}
|
||||
repair_flag = 0;
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, frame);
|
||||
} else {
|
||||
jlstream_free_frame(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int plc_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct plc_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
hdl->node = node;
|
||||
node->private_data = hdl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void plc_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = plc_handle_frame;
|
||||
iport->private_data = iport->node->private_data;
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int plc_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void plc_ioc_start(struct plc_node_hdl *hdl, u32 sr, u8 ch_num)
|
||||
{
|
||||
|
||||
hdl->data_wide.iport_data_wide = hdl->node->iport->prev->fmt.bit_wide;
|
||||
hdl->data_wide.oport_data_wide = hdl->node->oport->fmt.bit_wide;
|
||||
log_d("%s bit_wide, %d %d %d\n", __FUNCTION__, hdl->data_wide.iport_data_wide, hdl->data_wide.oport_data_wide, hdl->node->oport->fmt.Qval);
|
||||
if (hdl->scene == STREAM_SCENE_A2DP) {
|
||||
hdl->plc = music_plc_open(hdl, sr, ch_num);
|
||||
} else {
|
||||
hdl->esco_plc = esco_plc_open(hdl, sr, ch_num);
|
||||
}
|
||||
}
|
||||
|
||||
/*节点stop函数*/
|
||||
static void plc_ioc_stop(struct plc_node_hdl *hdl)
|
||||
{
|
||||
if (hdl->scene == STREAM_SCENE_A2DP) {
|
||||
if (hdl->plc) {
|
||||
music_plc_close(hdl->plc);
|
||||
hdl->plc = NULL;
|
||||
}
|
||||
} else {
|
||||
if (hdl->esco_plc) {
|
||||
esco_plc_close(hdl->esco_plc);
|
||||
hdl->esco_plc = NULL;
|
||||
}
|
||||
if (hdl->out_buf) {
|
||||
free(hdl->out_buf);
|
||||
hdl->out_buf = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int plc_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct plc_node_hdl *hdl = (struct plc_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
plc_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
hdl->scene = (enum stream_scene)arg;
|
||||
break;
|
||||
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= plc_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
plc_ioc_start(hdl, iport->prev->fmt.sample_rate, AUDIO_CH_NUM(iport->prev->fmt.channel_mode));
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
plc_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void plc_adapter_release(struct stream_node *node)
|
||||
{
|
||||
free(node->private_data);
|
||||
}
|
||||
|
||||
/*节点adapter 注意需要在sdk_used_list声明,否则会被优化*/
|
||||
REGISTER_STREAM_NODE_ADAPTER(plc_node_adapter) = {
|
||||
.name = "plc",
|
||||
.uuid = NODE_UUID_PLC,
|
||||
.bind = plc_adapter_bind,
|
||||
.ioctl = plc_adapter_ioctl,
|
||||
.release = plc_adapter_release,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".sink_dev0_node.data.bss")
|
||||
#pragma data_seg(".sink_dev0_node.data")
|
||||
#pragma const_seg(".sink_dev0_node.text.const")
|
||||
#pragma code_seg(".sink_dev0_node.text")
|
||||
#endif
|
||||
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#define SINK_DEV0_MSBC_TEST_ENABLE 0 //MSBC编码输出测试
|
||||
|
||||
#if TCFG_SINK_DEV0_NODE_ENABLE
|
||||
|
||||
struct sink_dev0_hdl {
|
||||
struct stream_fmt fmt; //节点参数
|
||||
struct stream_node *node; //节点句柄
|
||||
};
|
||||
|
||||
/*
|
||||
自定义输出节点 初始化
|
||||
hdl:节点私有句柄
|
||||
*/
|
||||
static int sink_dev0_init(struct sink_dev0_hdl *hdl)
|
||||
{
|
||||
u8 ch_num = AUDIO_CH_NUM(hdl->fmt.channel_mode); //声道数
|
||||
u32 sample_rate = hdl->fmt.sample_rate; //采样率
|
||||
u32 coding_type = hdl->fmt.coding_type; //数据格式
|
||||
|
||||
printf("sink_dev1_init,ch_num=%x,sr=%d,coding_type=%x\n", ch_num, sample_rate, coding_type);
|
||||
|
||||
//do something
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
自定义输出节点 关闭
|
||||
hdl:节点私有句柄
|
||||
*/
|
||||
static int sink_dev0_exit(struct sink_dev0_hdl *hdl)
|
||||
{
|
||||
//do something
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
自定义输出节点 运行
|
||||
hdl:节点私有句柄
|
||||
*data:输入数据地址,位宽16bit
|
||||
data_len :输入数据长度,byte
|
||||
*/
|
||||
static void sink_dev0_run(struct sink_dev0_hdl *hdl, void *data, int data_len)
|
||||
{
|
||||
//do something
|
||||
|
||||
}
|
||||
|
||||
static void sink_dev0_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct sink_dev0_hdl *hdl = (struct sink_dev0_hdl *)iport->private_data;
|
||||
struct stream_frame *frame;
|
||||
|
||||
while (1) {
|
||||
//获取上一个节点的输出
|
||||
frame = jlstream_pull_frame(iport, note);
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
sink_dev0_run(hdl, frame->data, frame->len);
|
||||
//释放资源
|
||||
jlstream_free_frame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
static int sink_dev0_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct sink_dev0_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
hdl->node = node;
|
||||
node->private_data = hdl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sink_dev0_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->private_data = iport->node->private_data;
|
||||
iport->handle_frame = sink_dev0_handle_frame;
|
||||
}
|
||||
|
||||
/*
|
||||
*********************************************************************
|
||||
* sink_dev0_ioc_fmt_nego
|
||||
* Description: sink_dev0 参数协商
|
||||
* Arguments : iport 输入端口句柄
|
||||
* Return : 节点参数协商结果
|
||||
* Note(s) : 目的在于检查与上一个节点的参数是否匹配,不匹配则重新协商;
|
||||
根据输出节点的参数特性,区分为
|
||||
1、固定参数, 或者通过NODE_IOC_SET_FMT获取fmt信息,
|
||||
协商过程会将此参数向前级节点传递, 直至协商成功;
|
||||
2、可变参数,继承上一节点的参数
|
||||
*********************************************************************
|
||||
*/
|
||||
static int sink_dev0_ioc_fmt_nego(struct stream_iport *iport)
|
||||
{
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt; //上一个节点的参数
|
||||
/* struct sink_dev0_hdl *hdl = (struct sink_dev0_hdl *)iport->private_data; */
|
||||
|
||||
//1、固定节点参数, 向前级节点传递
|
||||
#if SINK_DEV0_MSBC_TEST_ENABLE
|
||||
in_fmt->coding_type = AUDIO_CODING_MSBC; //数据格式
|
||||
in_fmt->sample_rate = 16000; //采样率
|
||||
in_fmt->channel_mode = AUDIO_CH_MIX; //数据通道
|
||||
#endif
|
||||
printf("sink_dev nego,type=%x,sr=%d,ch_mode=%x\n", in_fmt->coding_type, in_fmt->sample_rate, in_fmt->channel_mode);
|
||||
|
||||
//2、继承前一节点的参数
|
||||
/* hdl->fmt = &in_fmt; */
|
||||
return NEGO_STA_ACCPTED;
|
||||
}
|
||||
|
||||
static int sink_dev0_ioc_start(struct sink_dev0_hdl *hdl)
|
||||
{
|
||||
sink_dev0_init(hdl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sink_dev0_ioc_stop(struct sink_dev0_hdl *hdl)
|
||||
{
|
||||
sink_dev0_exit(hdl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sink_dev0_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
struct sink_dev0_hdl *hdl = (struct sink_dev0_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
sink_dev0_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
sink_dev0_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_STOP:
|
||||
sink_dev0_ioc_stop(hdl);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
//外部传参, 设置当前节点的参数
|
||||
/* struct stream_fmt *fmt = (struct stream_fmt *)arg; */
|
||||
/* hdl->fmt.coding_type = fmt->coding_type; */
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= sink_dev0_ioc_fmt_nego(iport);
|
||||
break;
|
||||
case NODE_IOC_GET_DELAY:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//释放当前节点资源
|
||||
static void sink_dev0_release(struct stream_node *node)
|
||||
{
|
||||
struct sink_dev0_hdl *hdl = (struct sink_dev0_hdl *)node->private_data;
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
REGISTER_STREAM_NODE_ADAPTER(sink_dev0_adapter) = {
|
||||
.name = "sink_dev0",
|
||||
.uuid = NODE_UUID_SINK_DEV0,
|
||||
.bind = sink_dev0_bind,
|
||||
.ioctl = sink_dev0_ioctl,
|
||||
.release = sink_dev0_release,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".sink_dev1_node.data.bss")
|
||||
#pragma data_seg(".sink_dev1_node.data")
|
||||
#pragma const_seg(".sink_dev1_node.text.const")
|
||||
#pragma code_seg(".sink_dev1_node.text")
|
||||
#endif
|
||||
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#define SINK_DEV1_MSBC_TEST_ENABLE 0 //MSBC编码输出测试
|
||||
|
||||
#if TCFG_SINK_DEV1_NODE_ENABLE
|
||||
|
||||
struct sink_dev1_hdl {
|
||||
struct stream_fmt fmt; //节点参数
|
||||
struct stream_node *node; //节点句柄
|
||||
};
|
||||
|
||||
/*
|
||||
自定义输出节点 初始化
|
||||
hdl:节点私有句柄
|
||||
*/
|
||||
static int sink_dev1_init(struct sink_dev1_hdl *hdl)
|
||||
{
|
||||
u8 ch_num = AUDIO_CH_NUM(hdl->fmt.channel_mode); //声道数
|
||||
u32 sample_rate = hdl->fmt.sample_rate; //采样率
|
||||
u32 coding_type = hdl->fmt.coding_type; //数据格式
|
||||
|
||||
printf("sink_dev1_init,ch_num=%x,sr=%d,coding_type=%x\n", ch_num, sample_rate, coding_type);
|
||||
|
||||
//do something
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
自定义输出节点 关闭
|
||||
hdl:节点私有句柄
|
||||
*/
|
||||
static int sink_dev1_exit(struct sink_dev1_hdl *hdl)
|
||||
{
|
||||
//do something
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
自定义输出节点 运行
|
||||
hdl:节点私有句柄
|
||||
*data:输入数据地址,位宽16bit
|
||||
data_len :输入数据长度,byte
|
||||
*/
|
||||
static void sink_dev1_run(struct sink_dev1_hdl *hdl, void *data, int data_len)
|
||||
{
|
||||
//do something
|
||||
|
||||
}
|
||||
|
||||
static void sink_dev1_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct sink_dev1_hdl *hdl = (struct sink_dev1_hdl *)iport->private_data;
|
||||
struct stream_frame *frame;
|
||||
|
||||
while (1) {
|
||||
//获取上一个节点的输出
|
||||
frame = jlstream_pull_frame(iport, note);
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
sink_dev1_run(hdl, frame->data, frame->len);
|
||||
//释放资源
|
||||
jlstream_free_frame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
static int sink_dev1_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct sink_dev1_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
hdl->node = node;
|
||||
node->private_data = hdl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sink_dev1_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->private_data = iport->node->private_data;
|
||||
iport->handle_frame = sink_dev1_handle_frame;
|
||||
}
|
||||
|
||||
/*
|
||||
*********************************************************************
|
||||
* sink_dev1_ioc_fmt_nego
|
||||
* Description: sink_dev1 参数协商
|
||||
* Arguments : iport 输入端口句柄
|
||||
* Return : 节点参数协商结果
|
||||
* Note(s) : 目的在于检查与上一个节点的参数是否匹配,不匹配则重新协商;
|
||||
根据输出节点的参数特性,区分为
|
||||
1、固定参数, 或者通过NODE_IOC_SET_FMT获取fmt信息,
|
||||
协商过程会将此参数向前级节点传递, 直至协商成功;
|
||||
2、可变参数,继承上一节点的参数
|
||||
*********************************************************************
|
||||
*/
|
||||
static int sink_dev1_ioc_fmt_nego(struct stream_iport *iport)
|
||||
{
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt; //上一个节点的参数
|
||||
/* struct sink_dev1_hdl *hdl = (struct sink_dev1_hdl *)iport->private_data; */
|
||||
|
||||
//1、固定节点参数, 向前级节点传递
|
||||
#if SINK_DEV1_MSBC_TEST_ENABLE
|
||||
in_fmt->coding_type = AUDIO_CODING_MSBC; //数据格式
|
||||
in_fmt->sample_rate = 16000; //采样率
|
||||
in_fmt->channel_mode = AUDIO_CH_MIX; //数据通道
|
||||
#endif
|
||||
printf("sink_dev nego,type=%x,sr=%d,ch_mode=%x\n", in_fmt->coding_type, in_fmt->sample_rate, in_fmt->channel_mode);
|
||||
|
||||
//2、继承前一节点的参数
|
||||
/* hdl->fmt = &in_fmt; */
|
||||
return NEGO_STA_ACCPTED;
|
||||
}
|
||||
|
||||
static int sink_dev1_ioc_start(struct sink_dev1_hdl *hdl)
|
||||
{
|
||||
sink_dev1_init(hdl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sink_dev1_ioc_stop(struct sink_dev1_hdl *hdl)
|
||||
{
|
||||
sink_dev1_exit(hdl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sink_dev1_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
struct sink_dev1_hdl *hdl = (struct sink_dev1_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
sink_dev1_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
sink_dev1_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_STOP:
|
||||
sink_dev1_ioc_stop(hdl);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
//外部传参, 设置当前节点的参数
|
||||
/* struct stream_fmt *fmt = (struct stream_fmt *)arg; */
|
||||
/* hdl->fmt.coding_type = fmt->coding_type; */
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= sink_dev1_ioc_fmt_nego(iport);
|
||||
break;
|
||||
case NODE_IOC_GET_DELAY:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//释放当前节点资源
|
||||
static void sink_dev1_release(struct stream_node *node)
|
||||
{
|
||||
struct sink_dev1_hdl *hdl = (struct sink_dev1_hdl *)node->private_data;
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
REGISTER_STREAM_NODE_ADAPTER(sink_dev1_adapter) = {
|
||||
.name = "sink_dev1",
|
||||
.uuid = NODE_UUID_SINK_DEV1,
|
||||
.bind = sink_dev1_bind,
|
||||
.ioctl = sink_dev1_ioctl,
|
||||
.release = sink_dev1_release,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".surround_demo_node.data.bss")
|
||||
#pragma data_seg(".surround_demo_node.data")
|
||||
#pragma const_seg(".surround_demo_node.text.const")
|
||||
#pragma code_seg(".surround_demo_node.text")
|
||||
#endif
|
||||
#include "app_config.h"
|
||||
#include "jlstream.h"
|
||||
#include "overlay_code.h"
|
||||
|
||||
#if TCFG_SURROUND_DEMO_NODE_ENABLE
|
||||
|
||||
struct surround_demo_node_hdl {
|
||||
u8 bypass;
|
||||
u8 out_channel;
|
||||
};
|
||||
|
||||
int surround_demo_data_run(void *priv, s16 *data, int len)
|
||||
{
|
||||
int wlen = len;
|
||||
/* struct surround_demo_node_hdl *hdl = (struct surround_demo_node_hdl *)priv; */
|
||||
|
||||
return wlen;
|
||||
}
|
||||
|
||||
static void surround_demo_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct stream_frame *in_frame;
|
||||
struct stream_frame *out_frame;
|
||||
struct stream_node *node = iport->node;
|
||||
struct surround_demo_node_hdl *hdl = (struct surround_demo_node_hdl *)iport->private_data;
|
||||
|
||||
while (1) {
|
||||
in_frame = jlstream_pull_frame(iport, note);
|
||||
if (!in_frame) {
|
||||
break;
|
||||
}
|
||||
if (hdl->bypass) {
|
||||
/*bypas时,输入输出通道一样,数据直接往下推*/
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, in_frame);
|
||||
} else {
|
||||
jlstream_free_frame(in_frame);
|
||||
}
|
||||
} else {
|
||||
/*输入数据是双声道*/
|
||||
out_frame = jlstream_get_frame(node->oport, in_frame->len);
|
||||
memcpy(out_frame->data, in_frame->data, in_frame->len);
|
||||
out_frame->len = in_frame->len;
|
||||
|
||||
/*音效处理*/
|
||||
surround_demo_data_run(hdl, (s16 *)out_frame->data, out_frame->len);
|
||||
|
||||
/*输入数据是双声道,根据输出声道做声道转换*/
|
||||
int pcm_frames = (out_frame->len >> 2);//点数
|
||||
s16 *pcm_buf = (s16 *)out_frame->data;
|
||||
int i = 0, tmp = 0;
|
||||
switch (hdl->out_channel) {
|
||||
case AUDIO_CH_L:
|
||||
for (i = 0; i < pcm_frames; i++) {
|
||||
pcm_buf[i] = pcm_buf[i * 2];
|
||||
}
|
||||
out_frame->len /= 2;
|
||||
break;
|
||||
case AUDIO_CH_R:
|
||||
for (i = 0; i < pcm_frames; i++) {
|
||||
pcm_buf[i] = pcm_buf[i * 2 + 1];
|
||||
}
|
||||
out_frame->len /= 2;
|
||||
break;
|
||||
case AUDIO_CH_MIX:
|
||||
for (i = 0; i < pcm_frames; i++) {
|
||||
tmp = pcm_buf[i * 2] + pcm_buf[i * 2 + 1];
|
||||
pcm_buf[i] = tmp / 2;
|
||||
}
|
||||
out_frame->len /= 2;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
jlstream_push_frame(node->oport, out_frame);
|
||||
jlstream_free_frame(in_frame);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*节点预处理-在ioctl之前*/
|
||||
static int surround_demo_adapter_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct surround_demo_node_hdl *hdl = malloc(sizeof(*hdl));
|
||||
memset(hdl, 0, sizeof(*hdl));
|
||||
node->private_data = hdl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*打开改节点输入接口*/
|
||||
static void surround_demo_ioc_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = surround_demo_handle_frame;
|
||||
iport->private_data = iport->node->private_data;
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int surround_demo_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
struct stream_oport *oport = iport->node->oport;
|
||||
struct stream_fmt *in_fmt = &iport->prev->fmt;
|
||||
struct surround_demo_node_hdl *hdl = (struct surround_demo_node_hdl *)iport->private_data;
|
||||
int ret = NEGO_STA_ACCPTED;
|
||||
|
||||
/*数据通道协商*/
|
||||
if (!hdl->bypass) {
|
||||
/*固定要求输入为双声道*/
|
||||
if (in_fmt->channel_mode != AUDIO_CH_LR) {
|
||||
in_fmt->channel_mode = AUDIO_CH_LR;
|
||||
ret = NEGO_STA_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*记录输出通道数*/
|
||||
hdl->out_channel = oport->fmt.channel_mode;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点start函数*/
|
||||
static void surround_demo_ioc_start(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*节点stop函数*/
|
||||
static void surround_demo_ioc_stop(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/*节点ioctl函数*/
|
||||
static int surround_demo_adapter_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct surround_demo_node_hdl *hdl = (struct surround_demo_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
surround_demo_ioc_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= surround_demo_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
surround_demo_ioc_start();
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
surround_demo_ioc_stop();
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*节点用完释放函数*/
|
||||
static void surround_demo_adapter_release(struct stream_node *node)
|
||||
{
|
||||
free(node->private_data);
|
||||
}
|
||||
|
||||
REGISTER_STREAM_NODE_ADAPTER(surround_demo_node_adapter) = {
|
||||
.name = "surround_demo",
|
||||
.uuid = NODE_UUID_SURROUND_DEMO,
|
||||
.bind = surround_demo_adapter_bind,
|
||||
.ioctl = surround_demo_adapter_ioctl,
|
||||
.release = surround_demo_adapter_release,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".uart_node.data.bss")
|
||||
#pragma data_seg(".uart_node.data")
|
||||
#pragma const_seg(".uart_node.text.const")
|
||||
#pragma code_seg(".uart_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "uart.h"
|
||||
#include "gpio_config.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_UART_NODE_ENABLE
|
||||
|
||||
struct uart_node_data {
|
||||
u16 port_uuid;
|
||||
u32 baudrate;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct uart_node_hdl {
|
||||
int uart;
|
||||
};
|
||||
|
||||
|
||||
static void uart_node_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct uart_node_hdl *hdl = (struct uart_node_hdl *)iport->private_data;
|
||||
struct stream_frame *frame;
|
||||
struct stream_node *node = iport->node;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note);
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
if (hdl->uart != -1) {
|
||||
uart_send_bytes(hdl->uart, frame->data, frame->len);
|
||||
}
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, frame);
|
||||
} else {
|
||||
jlstream_free_frame(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int uart_node_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct uart_node_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
node->type = NODE_TYPE_BYPASS;
|
||||
node->private_data = hdl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uart_node_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->private_data = iport->node->private_data;
|
||||
iport->handle_frame = uart_node_handle_frame;
|
||||
}
|
||||
|
||||
static void uart_ioc_start(struct uart_node_hdl *hdl, struct stream_node *node)
|
||||
{
|
||||
struct uart_node_data config;
|
||||
|
||||
/*
|
||||
*获取配置文件内的参数,及名字
|
||||
* */
|
||||
if (jlstream_read_node_data_new(node->uuid, node->subid, (void *)&config, NULL)) {
|
||||
printf("%s, read node data err\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 tx_pin = uuid2gpio(config.port_uuid);
|
||||
struct uart_config ut = {
|
||||
.baud_rate = config.baudrate,
|
||||
.tx_pin = tx_pin,
|
||||
.rx_pin = -1,
|
||||
};
|
||||
|
||||
hdl->uart = uart_init(-1, &ut);
|
||||
if (hdl->uart < 0) {
|
||||
printf("open uart dev err\n");
|
||||
hdl->uart = -1;
|
||||
}
|
||||
struct uart_dma_config dma_config = {
|
||||
.event_mask = UART_EVENT_TX_DONE,
|
||||
};
|
||||
uart_dma_init(hdl->uart, &dma_config);
|
||||
}
|
||||
|
||||
static int uart_node_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
struct uart_node_hdl *hdl = (struct uart_node_hdl *)iport->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
uart_node_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
uart_ioc_start(hdl, iport->node);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
if (hdl->uart != -1) {
|
||||
uart_deinit(hdl->uart);
|
||||
hdl->uart = -1 ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uart_node_release(struct stream_node *node)
|
||||
{
|
||||
free(node->private_data);
|
||||
}
|
||||
|
||||
|
||||
REGISTER_STREAM_NODE_ADAPTER(uart_node_adapter) = {
|
||||
.name = "uart_dump",
|
||||
.uuid = NODE_UUID_UART_DUMP,
|
||||
.bind = uart_node_bind,
|
||||
.ioctl = uart_node_ioctl,
|
||||
.release = uart_node_release,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,532 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".volume_node.data.bss")
|
||||
#pragma data_seg(".volume_node.data")
|
||||
#pragma const_seg(".volume_node.text.const")
|
||||
#pragma code_seg(".volume_node.text")
|
||||
#endif
|
||||
#include "jlstream.h"
|
||||
#include "classic/tws_api.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "app_main.h"
|
||||
#include "audio_config.h"
|
||||
#include "audio_dvol.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "volume_node.h"
|
||||
#include "scene_switch.h"
|
||||
#include "app_config.h"
|
||||
|
||||
struct volume_hdl {
|
||||
char name[16]; //节点名称
|
||||
enum stream_scene scene;
|
||||
dvol_handle *dvol_hdl; //音量操作句柄
|
||||
struct stream_node *node; //节点句柄
|
||||
struct node_port_data_wide data_wide;
|
||||
u8 Qval;//数据的饱和位宽
|
||||
u8 state;
|
||||
u8 bypass;//是否跳过节点处理
|
||||
struct volume_cfg *vol_cfg;
|
||||
u16 cfg_len;//配置数据实际大小
|
||||
u8 online_update_disable;//是否支持在线音量更新
|
||||
u16 target_dig_volume;//优化底噪时的固定数字音量
|
||||
};
|
||||
|
||||
//默认音量配置
|
||||
struct volume_cfg default_vol_cfg = {
|
||||
.bypass = 0,
|
||||
.cfg_level_max = 16,
|
||||
.cfg_vol_min = -45,
|
||||
.cfg_vol_max = 0,
|
||||
.vol_table_custom = 0,
|
||||
.cur_vol = 11,
|
||||
};
|
||||
|
||||
__STREAM_CACHE_CODE
|
||||
static void volume_handle_frame(struct stream_iport *iport, struct stream_note *note)
|
||||
{
|
||||
struct stream_frame *frame;
|
||||
struct stream_node *node = iport->node;
|
||||
struct volume_hdl *hdl = (struct volume_hdl *)iport->private_data;
|
||||
|
||||
while (1) {
|
||||
frame = jlstream_pull_frame(iport, note);
|
||||
if (!frame) {
|
||||
break;
|
||||
}
|
||||
if (!hdl->bypass) {
|
||||
audio_digital_vol_run(hdl->dvol_hdl, (s16 *)frame->data, frame->len);
|
||||
}
|
||||
if (node->oport) {
|
||||
jlstream_push_frame(node->oport, frame);
|
||||
} else {
|
||||
jlstream_free_frame(frame);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int volume_bind(struct stream_node *node, u16 uuid)
|
||||
{
|
||||
struct volume_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
|
||||
node->private_data = hdl;
|
||||
|
||||
hdl->node = node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void volume_open_iport(struct stream_iport *iport)
|
||||
{
|
||||
iport->handle_frame = volume_handle_frame;
|
||||
iport->private_data = iport->node->private_data;
|
||||
}
|
||||
|
||||
/*节点参数协商*/
|
||||
static int volume_ioc_negotiate(struct stream_iport *iport)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void audio_digital_vol_cfg_init(dvol_handle *dvol, struct volume_cfg *vol_cfg) //初始化配置
|
||||
{
|
||||
if (dvol) {
|
||||
dvol-> cfg_vol_max = vol_cfg->cfg_vol_max;
|
||||
dvol-> cfg_vol_min = vol_cfg->cfg_vol_min;
|
||||
dvol-> cfg_level_max = vol_cfg->cfg_level_max;
|
||||
dvol-> vol_table_custom = vol_cfg->vol_table_custom;
|
||||
#if VOL_TAB_CUSTOM_EN
|
||||
if (dvol->vol_table_custom == VOLUME_TABLE_CUSTOM_EN) {
|
||||
dvol->vol_table = vol_cfg->vol_table ;
|
||||
}
|
||||
#endif
|
||||
dvol->vol_limit = (dvol->vol_limit > dvol->cfg_level_max) ? dvol->cfg_level_max : dvol->vol_limit;
|
||||
u16 vol_level = dvol->vol * dvol->vol_limit / dvol->vol_max;
|
||||
if (vol_level == 0) {
|
||||
dvol->vol_target = 0;
|
||||
} else if (dvol->vol_table) {
|
||||
dvol->vol_target = dvol->vol_table[vol_level - 1];
|
||||
} else {
|
||||
extern float eq_db2mag(float x);
|
||||
u16 step = (dvol->cfg_level_max - 1 > 0) ? (dvol->cfg_level_max - 1) : 1;
|
||||
float step_db = (dvol->cfg_vol_max - dvol->cfg_vol_min) / (float)step;
|
||||
float dvol_db = dvol->cfg_vol_min + (vol_level - 1) * step_db;
|
||||
float dvol_gain = eq_db2mag(dvol_db);//dB转换倍数
|
||||
dvol->vol_target = (s16)(DVOL_MAX_FLOAT * dvol_gain + 0.5f);
|
||||
printf("vol param:%d,(%d/100)dB,cur:%d,max:%d,(%d/100)dB", dvol->vol_target, (int)step_db * 100, vol_level, vol_cfg->cfg_level_max, (int)dvol_db * 100);
|
||||
#if 0
|
||||
//打印音量表每一级的目标音量值和dB值,调试用
|
||||
printf("=========================================vol table=================================================================");
|
||||
int i = 1;
|
||||
for (i = 1; i < dvol->cfg_level_max + 1; i++) {
|
||||
float debug_db = dvol->cfg_vol_min + (i - 1) * step_db;
|
||||
float debug_gain = eq_db2mag(debug_db);//dB转换倍数
|
||||
int debug_target = (s16)(DVOL_MAX_FLOAT * debug_gain + 0.5);
|
||||
printf("dvol[%d] = %d,(%d / 100)dB", i, debug_target, (int)(debug_db * 100));
|
||||
}
|
||||
printf("====================================================================================================================");
|
||||
#endif
|
||||
}
|
||||
|
||||
dvol->vol_fade = 0; //从0开始淡入到目标音量
|
||||
if ((dvol->cfg_vol_min == -45) && (dvol->cfg_level_max == 31) && (dvol->cfg_vol_max == 0)) {
|
||||
dvol-> vol_table_default = 1; //使用默认的音量表
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void volume_ioc_start(struct volume_hdl *hdl)
|
||||
{
|
||||
/*
|
||||
*获取配置文件内的参数,及名字
|
||||
* */
|
||||
int len = 0;
|
||||
struct audio_vol_params params = {0};
|
||||
struct volume_cfg *vol_cfg = NULL;
|
||||
if (!hdl->vol_cfg) {
|
||||
struct cfg_info info = {0}; //节点配置相关信息(参数存储的目标地址、配置项大小)
|
||||
int ret = jlstream_read_node_info_data(hdl->node->uuid, hdl->node->subid, hdl->name, &info);
|
||||
/* printf("enter volume_node.c %d,%d,%d\n", __LINE__, ret, info.size); */
|
||||
if (ret) {
|
||||
hdl->cfg_len = info.size;
|
||||
vol_cfg = hdl->vol_cfg = zalloc(info.size);
|
||||
len = jlstream_read_form_cfg_data(&info, (void *)vol_cfg);
|
||||
if (info.size > sizeof(struct volume_cfg)) { //有自定义音量表,dB转成对应音量
|
||||
for (int i = 0; i < vol_cfg->cfg_level_max ; i++) {
|
||||
/* printf("custom dvol [%d] = %d / 100 dB", i, (int)(hdl->vol_cfg->vol_table[i] * 100)); */
|
||||
float dvol_gain = eq_db2mag(hdl->vol_cfg->vol_table[i]);//dB转换倍数
|
||||
hdl->vol_cfg->vol_table [i] = (s16)(DVOL_MAX_FLOAT * dvol_gain + 0.5f);
|
||||
/* printf("custom dvol[%d] = %d", i, (int)hdl->vol_cfg->vol_table[i]); */
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
len = hdl->cfg_len;
|
||||
vol_cfg = hdl->vol_cfg;
|
||||
}
|
||||
if (!len) {
|
||||
printf("%s, read node data err\n", __FUNCTION__);
|
||||
}
|
||||
|
||||
hdl->data_wide.iport_data_wide = hdl->node->iport->prev->fmt.bit_wide;
|
||||
hdl->data_wide.oport_data_wide = hdl->node->oport->fmt.bit_wide;
|
||||
log_d("%s bit_wide, %d %d %d\n", __FUNCTION__, hdl->data_wide.iport_data_wide, hdl->data_wide.oport_data_wide, hdl->node->oport->fmt.Qval);
|
||||
|
||||
printf("%s len %d, sizeof(cfg) %d\n", __func__, len, (int)sizeof(struct volume_cfg));
|
||||
if (len != hdl->cfg_len) {
|
||||
if (hdl->vol_cfg) {
|
||||
free(hdl->vol_cfg);
|
||||
hdl->vol_cfg = NULL;
|
||||
}
|
||||
vol_cfg = &default_vol_cfg;
|
||||
} else {
|
||||
printf("vol read config ok :%d,%d,%d,%d,%d\n", vol_cfg->cfg_level_max, (int)vol_cfg->cfg_vol_min, (int)vol_cfg->cfg_vol_max, vol_cfg->vol_table_custom, vol_cfg->bypass);
|
||||
}
|
||||
|
||||
/*
|
||||
*获取在线调试的临时参数
|
||||
* */
|
||||
u32 online_cfg_len = sizeof(struct volume_cfg) + DIGITAL_VOLUME_LEVEL_MAX * sizeof(float);
|
||||
struct volume_cfg *online_vol_cfg = zalloc(online_cfg_len);
|
||||
if (jlstream_read_effects_online_param(hdl->node->uuid, hdl->name, online_vol_cfg, online_cfg_len)) {
|
||||
/* printf("cfg_level_max = %d\n", online_vol_cfg->cfg_level_max); */
|
||||
/* printf("cfg_vol_min = %d\n", online_vol_cfg->cfg_vol_min); */
|
||||
/* printf("cfg_vol_max = %d\n", online_vol_cfg->cfg_vol_max); */
|
||||
/* printf("vol_table_custom = %d\n", online_vol_cfg->vol_table_custom); */
|
||||
/* printf("cur_vol = %d\n", online_vol_cfg->cur_vol); */
|
||||
/* printf("tab_len = %d\n", online_vol_cfg->tab_len); */
|
||||
hdl->vol_cfg-> cfg_vol_max = online_vol_cfg->cfg_vol_max;
|
||||
hdl->vol_cfg-> cfg_vol_min = online_vol_cfg->cfg_vol_min;
|
||||
hdl->vol_cfg-> bypass = online_vol_cfg->bypass;
|
||||
#if VOL_TAB_CUSTOM_EN
|
||||
if (hdl->vol_cfg->tab_len == online_vol_cfg->tab_len && hdl->vol_cfg->tab_len) {
|
||||
for (int i = 0; i < hdl->vol_cfg->cfg_level_max ; i++) {//重新计算音量表的值
|
||||
printf("custom dvol [%d] = %d / 100 dB", i, (int)(online_vol_cfg->vol_table[i] * 100));
|
||||
float dvol_gain = eq_db2mag(online_vol_cfg->vol_table[i]);//dB转换倍数
|
||||
hdl->vol_cfg->vol_table [i] = (s16)(DVOL_MAX_FLOAT * dvol_gain + 0.5f);
|
||||
printf("custom dvol[%d] = %d", i, (int)online_vol_cfg->vol_table[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
printf("get volume online param\n");
|
||||
}
|
||||
free(online_vol_cfg);
|
||||
hdl->bypass = vol_cfg->bypass;
|
||||
switch (hdl->scene) {
|
||||
case STREAM_SCENE_TONE:
|
||||
case STREAM_SCENE_RING:
|
||||
case STREAM_SCENE_KEY_TONE:
|
||||
/*puts("set_tone_volume\n");*/
|
||||
hdl->state = APP_AUDIO_STATE_WTONE;
|
||||
#if TONE_BGM_FADEOUT
|
||||
/*printf("tone_player.c %d tone play,BGM fade_out automatically\n", __LINE__);*/
|
||||
audio_digital_vol_bg_fade(1);
|
||||
#endif/*TONE_BGM_FADEOUT*/
|
||||
params.vol = app_audio_get_volume(APP_AUDIO_STATE_WTONE);
|
||||
params.vol_max = vol_cfg->cfg_level_max;
|
||||
params.fade_step = TONE_DVOL_FS;
|
||||
params.vol_limit = -1;
|
||||
break;
|
||||
case STREAM_SCENE_OPUS:
|
||||
case STREAM_SCENE_A2DP:
|
||||
case STREAM_SCENE_LINEIN:
|
||||
case STREAM_SCENE_IIS:
|
||||
case STREAM_SCENE_SPDIF:
|
||||
case STREAM_SCENE_PC_SPK:
|
||||
case STREAM_SCENE_PC_MIC:
|
||||
case STREAM_SCENE_MUSIC:
|
||||
case STREAM_SCENE_FM:
|
||||
case STREAM_SCENE_MIC_EFFECT:
|
||||
case STREAM_SCENE_HEARING_AID:
|
||||
puts("set_a2dp_volume\n");
|
||||
hdl->state = APP_AUDIO_STATE_MUSIC;
|
||||
params.vol = app_audio_get_volume(APP_AUDIO_STATE_MUSIC);
|
||||
params.vol_max = vol_cfg->cfg_level_max;
|
||||
params.fade_step = MUSIC_DVOL_FS;
|
||||
params.vol_limit = -1;
|
||||
break;
|
||||
case STREAM_SCENE_ESCO:
|
||||
puts("set_esco_volume\n");
|
||||
hdl->state = APP_AUDIO_STATE_CALL;
|
||||
params.vol = app_audio_get_volume(APP_AUDIO_STATE_CALL);
|
||||
params.vol_max = vol_cfg->cfg_level_max;
|
||||
params.fade_step = CALL_DVOL_FS;
|
||||
params.vol_limit = -1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
params.bit_wide = hdl->node->oport->fmt.bit_wide;
|
||||
if (!hdl->dvol_hdl) {
|
||||
hdl->dvol_hdl = audio_digital_vol_open(params);
|
||||
}
|
||||
audio_digital_vol_cfg_init(hdl->dvol_hdl, vol_cfg);
|
||||
|
||||
if (hdl->dvol_hdl) {
|
||||
hdl->dvol_hdl->mute_en = app_audio_get_mute_state(hdl->state);
|
||||
if (hdl->dvol_hdl->mute_en) {
|
||||
hdl->dvol_hdl->vol_fade = 0;
|
||||
}
|
||||
}
|
||||
if (hdl->scene == STREAM_SCENE_MIC_EFFECT) {
|
||||
return;
|
||||
}
|
||||
#ifdef DVOL_2P1_CH_DVOL_ADJUST_NODE
|
||||
#if (DVOL_2P1_CH_DVOL_ADJUST_NODE == DVOL_2P1_CH_DVOL_ADJUST_LR)
|
||||
char *substr = strstr(hdl->name, "Music");
|
||||
if (!strcmp(substr, "Music") || !strcmp(substr, "Music2")) { //找到默认初始化为最大音量的节点
|
||||
//printf("enter volume_node.c %d,%p,%s\n", __LINE__, hdl->dvol_hdl, substr);
|
||||
audio_digital_vol_set(hdl->dvol_hdl, hdl->dvol_hdl->vol_max);
|
||||
hdl->dvol_hdl->mute_en = 0;
|
||||
hdl->online_update_disable = 1;
|
||||
} else {
|
||||
//printf("enter volume_node.c %d,%p,%s\n", __LINE__, hdl->dvol_hdl, substr);
|
||||
app_audio_state_switch(hdl->state, vol_cfg->cfg_level_max, hdl->dvol_hdl);
|
||||
}
|
||||
#elif (DVOL_2P1_CH_DVOL_ADJUST_NODE == DVOL_2P1_CH_DVOL_ADJUST_SW)
|
||||
char *substr = strstr(hdl->name, "Music");
|
||||
if (!strcmp(substr, "Music") || !strcmp(substr, "Music1")) { //找到默认初始化为最大音量的节点
|
||||
// printf("enter volume_node.c %d,%p,%s\n", __LINE__, hdl->dvol_hdl, substr);
|
||||
audio_digital_vol_set(hdl->dvol_hdl, hdl->dvol_hdl->vol_max);
|
||||
hdl->dvol_hdl->mute_en = 0;
|
||||
hdl->online_update_disable = 1;
|
||||
} else {
|
||||
// printf("enter volume_node.c %d,%p,%s\n", __LINE__, hdl->dvol_hdl, substr);
|
||||
app_audio_state_switch(hdl->state, vol_cfg->cfg_level_max, hdl->dvol_hdl);
|
||||
}
|
||||
#else
|
||||
if (memchr(hdl->name, '1', 16) || memchr(hdl->name, '2', 16)) { //找到默认初始化为最大音量的节点
|
||||
//printf("enter volume_node.c %d,%p\n", __LINE__, hdl->dvol_hdl);
|
||||
audio_digital_vol_set(hdl->dvol_hdl, hdl->dvol_hdl->vol_max);
|
||||
hdl->online_update_disable = 1;
|
||||
hdl->dvol_hdl->mute_en = 0;
|
||||
} else {
|
||||
//printf("enter volume_node.c %d,%p\n", __LINE__, hdl->dvol_hdl);
|
||||
app_audio_state_switch(hdl->state, vol_cfg->cfg_level_max, hdl->dvol_hdl);
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
app_audio_state_switch(hdl->state, vol_cfg->cfg_level_max, hdl->dvol_hdl);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void volume_ioc_stop(struct volume_hdl *hdl)
|
||||
{
|
||||
if (hdl->scene != STREAM_SCENE_MIC_EFFECT) {
|
||||
app_audio_state_exit(hdl->state);
|
||||
}
|
||||
#if TONE_BGM_FADEOUT
|
||||
printf("tone_player.c %d tone play,BGM fade_out close\n", __LINE__);
|
||||
audio_digital_vol_bg_fade(0);
|
||||
#endif/*TONE_BGM_FADEOUT*/
|
||||
audio_digital_vol_close(hdl->dvol_hdl);
|
||||
hdl->dvol_hdl = NULL;
|
||||
}
|
||||
|
||||
int volume_ioc_get_cfg(const char *name, struct volume_cfg *vol_cfg)
|
||||
{
|
||||
char mode_index = get_current_scene();
|
||||
struct cfg_info info = {0}; //节点配置相关信息(参数存储的目标地址、配置项大小)
|
||||
int ret = jlstream_read_info_data(mode_index, name, 0, &info);
|
||||
if (!ret) {
|
||||
*vol_cfg = default_vol_cfg;
|
||||
} else {
|
||||
struct volume_cfg *temp_vol_cfg = zalloc(info.size);
|
||||
int len = jlstream_read_form_cfg_data(&info, (void *)temp_vol_cfg);
|
||||
if (len == info.size) {
|
||||
*vol_cfg = *temp_vol_cfg;
|
||||
} else {
|
||||
*vol_cfg = default_vol_cfg;
|
||||
}
|
||||
free(temp_vol_cfg); //赋值完结构体释放内存
|
||||
/* printf("volume node read cfg name %s", name); */
|
||||
/* printf("cfg_level_max = %d\n", vol_cfg->cfg_level_max); */
|
||||
/* printf("cfg_vol_min = %d\n", vol_cfg->cfg_vol_min); */
|
||||
/* printf("cfg_vol_max = %d\n", vol_cfg->cfg_vol_max); */
|
||||
/* printf("vol_table_custom = %d\n", vol_cfg->vol_table_custom); */
|
||||
/* printf("cur_vol = %d\n", vol_cfg->cur_vol); */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
u16 volume_ioc_get_max_level(const char *name)
|
||||
{
|
||||
struct volume_cfg vol_cfg;
|
||||
volume_ioc_get_cfg(name, &vol_cfg);
|
||||
return vol_cfg.cfg_level_max;
|
||||
}
|
||||
|
||||
|
||||
float volume_ioc_2_dB(struct volume_hdl *hdl, s16 volume)
|
||||
{
|
||||
if (hdl->dvol_hdl) {
|
||||
dvol_handle *dvol = hdl->dvol_hdl;
|
||||
u8 vol_level = volume * dvol->vol_limit / dvol->vol_max;
|
||||
u16 step = (dvol->cfg_level_max - 1 > 0) ? (dvol->cfg_level_max - 1) : 1;
|
||||
float step_db = (dvol->cfg_vol_max - dvol->cfg_vol_min) / (float)step;
|
||||
float dvol_db = dvol->cfg_vol_min + (vol_level - 1) * step_db;
|
||||
printf("vol_dB :%d,%d\n", __LINE__, (int)dvol_db);
|
||||
return (dvol_db);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int volume_ioc_update_parm(struct volume_hdl *hdl, int parm)
|
||||
{
|
||||
struct volume_cfg *vol_cfg = (struct volume_cfg *)parm;
|
||||
int ret = false;
|
||||
int cmd = (vol_cfg->bypass & 0xf0);
|
||||
int value = vol_cfg->cur_vol;
|
||||
|
||||
switch (cmd) {
|
||||
case VOLUME_NODE_CMD_SET_VOL:
|
||||
s16 volume = value & 0xffff;
|
||||
if (volume < 0) {
|
||||
volume = 0;
|
||||
}
|
||||
if (hdl && hdl->dvol_hdl) {
|
||||
#if defined(VOL_NOISE_OPTIMIZE) &&( VOL_NOISE_OPTIMIZE)
|
||||
if (volume) {
|
||||
if (volume_ioc_2_dB(hdl, volume) < TARGET_DIG_DB && hdl->state == APP_AUDIO_STATE_MUSIC) {
|
||||
if (!hdl->target_dig_volume) {
|
||||
s16 temp_volume = 0;
|
||||
for (temp_volume = hdl->dvol_hdl->vol_max; volume_ioc_2_dB(hdl, temp_volume) > TARGET_DIG_DB; temp_volume--) {};
|
||||
hdl->target_dig_volume = temp_volume + 1;
|
||||
}
|
||||
|
||||
//printf("enter volume_node.c %d,%d/100,%d/100,%d,%d,%d\n",__LINE__,(int)(volume_ioc_2_dB(hdl,volume) * 100),(int)(volume_ioc_2_dB(hdl,hdl->target_dig_volume)* 100), hdl->target_dig_volume,volume,hdl->dvol_hdl->vol);
|
||||
|
||||
float dac_dB = volume_ioc_2_dB(hdl, volume) - volume_ioc_2_dB(hdl, hdl->target_dig_volume) ;
|
||||
|
||||
app_audio_dac_set_dB(dac_dB);
|
||||
volume = hdl->target_dig_volume;
|
||||
} else { //把DAC 设回0dB
|
||||
app_audio_dac_set_dB(0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
audio_digital_vol_set(hdl->dvol_hdl, volume);
|
||||
printf("SET VOL volume update success : %d\n", volume);
|
||||
ret = true;
|
||||
}
|
||||
break;
|
||||
case VOLUME_NODE_CMD_SET_MUTE:
|
||||
s16 mute_en = value & 0xffff;
|
||||
if (hdl && hdl->dvol_hdl) {
|
||||
audio_digital_vol_mute_set(hdl->dvol_hdl, mute_en);
|
||||
printf("SET MUTE mute update success : %d\n", mute_en);
|
||||
ret = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (hdl && hdl->dvol_hdl) {
|
||||
if (!hdl->online_update_disable) {
|
||||
hdl->dvol_hdl-> cfg_vol_max = vol_cfg->cfg_vol_max;
|
||||
hdl->dvol_hdl-> cfg_vol_min = vol_cfg->cfg_vol_min;
|
||||
#if VOL_TAB_CUSTOM_EN
|
||||
if (hdl->vol_cfg->tab_len == vol_cfg->tab_len && hdl->vol_cfg->tab_len) {
|
||||
for (int i = 0; i < hdl->vol_cfg->cfg_level_max ; i++) {//重新计算音量表的值
|
||||
//printf("custom dvol [%d] = %d / 100 dB", i, (int)(vol_cfg->vol_table[i] * 100));
|
||||
float dvol_gain = eq_db2mag(vol_cfg->vol_table[i]);//dB转换倍数
|
||||
hdl->vol_cfg->vol_table [i] = (s16)(DVOL_MAX_FLOAT * dvol_gain + 0.5f);
|
||||
//printf("custom dvol[%d] = %d", i, (int)vol_cfg->vol_table[i]);
|
||||
}
|
||||
if (hdl->dvol_hdl->vol_table_custom == VOLUME_TABLE_CUSTOM_EN) {
|
||||
hdl->dvol_hdl->vol_table = hdl->vol_cfg->vol_table ;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
hdl->bypass = vol_cfg->bypass;
|
||||
audio_digital_vol_set(hdl->dvol_hdl, vol_cfg->cur_vol);
|
||||
if (hdl->scene != STREAM_SCENE_MIC_EFFECT) {//混响在线调试音量不更新音量状态的值
|
||||
app_audio_change_volume(app_audio_get_state(), vol_cfg->cur_vol);
|
||||
}
|
||||
}
|
||||
/* printf("cfg_level_max = %d\n", vol_cfg->cfg_level_max); */
|
||||
/* printf("cfg_vol_min = %d\n", vol_cfg->cfg_vol_min); */
|
||||
/* printf("cfg_vol_max = %d\n", vol_cfg->cfg_vol_max); */
|
||||
/* printf("vol_table_custom = %d\n", vol_cfg->vol_table_custom); */
|
||||
/* printf("cur_vol = %d\n", vol_cfg->cur_vol); */
|
||||
printf("volume update success : %d\n", vol_cfg->cur_vol);
|
||||
ret = true;
|
||||
return ret;
|
||||
}
|
||||
printf("parm update failed : %x\n", value);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_volume_ioc_parm(struct volume_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = 0;
|
||||
struct volume_cfg *cfg = (struct volume_cfg *)parm;
|
||||
if (hdl && hdl->dvol_hdl) {
|
||||
hdl->vol_cfg->cur_vol = hdl->dvol_hdl->vol;
|
||||
}
|
||||
memcpy(cfg, hdl->vol_cfg, sizeof(struct volume_cfg));
|
||||
ret = sizeof(struct volume_cfg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int volume_ioctl(struct stream_iport *iport, int cmd, int arg)
|
||||
{
|
||||
struct volume_hdl *hdl = (struct volume_hdl *)iport->private_data;
|
||||
|
||||
int ret = 0;
|
||||
switch (cmd) {
|
||||
case NODE_IOC_OPEN_IPORT:
|
||||
volume_open_iport(iport);
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
hdl->scene = (enum stream_scene)arg;
|
||||
break;
|
||||
case NODE_IOC_NEGOTIATE:
|
||||
*(int *)arg |= volume_ioc_negotiate(iport);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
volume_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_STOP:
|
||||
case NODE_IOC_SUSPEND:
|
||||
volume_ioc_stop(hdl);
|
||||
break;
|
||||
case NODE_IOC_NAME_MATCH:
|
||||
/* printf("volume_node name match :%s,%s\n", hdl->name, (const char *)arg); */
|
||||
if (!strcmp((const char *)arg, hdl->name)) {
|
||||
ret = 1;
|
||||
}
|
||||
break;
|
||||
case NODE_IOC_SET_PARAM:
|
||||
ret = volume_ioc_update_parm(hdl, arg);
|
||||
break;
|
||||
case NODE_IOC_GET_PARAM:
|
||||
ret = get_volume_ioc_parm(hdl, arg);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void volume_release(struct stream_node *node)
|
||||
{
|
||||
struct volume_hdl *hdl = (struct volume_hdl *)node->private_data;
|
||||
if (hdl->vol_cfg) {
|
||||
free(hdl->vol_cfg);
|
||||
hdl->vol_cfg = NULL;
|
||||
}
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
|
||||
REGISTER_STREAM_NODE_ADAPTER(volume_node_adapter) = {
|
||||
.name = "volume",
|
||||
.uuid = NODE_UUID_VOLUME_CTRLER,
|
||||
.bind = volume_bind,
|
||||
.ioctl = volume_ioctl,
|
||||
.release = volume_release,
|
||||
};
|
||||
|
||||
REGISTER_ONLINE_ADJUST_TARGET(volume) = {
|
||||
.uuid = NODE_UUID_VOLUME_CTRLER,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,820 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".a2dp_file.data.bss")
|
||||
#pragma data_seg(".a2dp_file.data")
|
||||
#pragma const_seg(".a2dp_file.text.const")
|
||||
#pragma code_seg(".a2dp_file.text")
|
||||
#endif
|
||||
#include "btstack/a2dp_media_codec.h"
|
||||
#include "btstack/avctp_user.h"
|
||||
#include "source_node.h"
|
||||
#include "classic/tws_api.h"
|
||||
#include "system/timer.h"
|
||||
#include "sync/audio_syncts.h"
|
||||
#include "media/bt_audio_timestamp.h"
|
||||
#include "os/os_api.h"
|
||||
#include "jiffies.h"
|
||||
#include "a2dp_streamctrl.h"
|
||||
#include "reference_time.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_USER_BT_CLASSIC_ENABLE
|
||||
|
||||
#define A2DP_TIMESTAMP_ENABLE 1
|
||||
|
||||
struct a2dp_file_params {
|
||||
u8 edr_to_local_time;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct a2dp_file_hdl {
|
||||
u8 start;
|
||||
/*u8 reassemble;*/
|
||||
u16 timer;
|
||||
u16 wake_up_timer;
|
||||
void *file;
|
||||
int media_type;
|
||||
struct stream_node *node;
|
||||
|
||||
void *ts_handle;
|
||||
u32 sample_rate;
|
||||
u16 codec_version;
|
||||
u8 channel_num;
|
||||
u16 seqn;
|
||||
u32 base_time;
|
||||
u32 timestamp;
|
||||
u32 ts_sample_rate;
|
||||
u32 dts;//total frams
|
||||
|
||||
u16 delay_time;
|
||||
u8 sync_step;
|
||||
u8 reference;
|
||||
struct a2dp_media_frame frame;
|
||||
int frame_len;
|
||||
void *stream_ctrl;
|
||||
u8 bt_addr[6];
|
||||
u8 tws_case;
|
||||
u8 handshake_state;
|
||||
u32 request_timeout;
|
||||
u32 handshake_timeout;
|
||||
/*struct stream_frame *reassembled_frame;*/
|
||||
|
||||
u8 link_jl_dongle; //连接jl_dongle
|
||||
u8 rtp_ts_en; //使用rtp的时间戳
|
||||
u16 jl_dongle_latency ;
|
||||
u8 edr_to_local_time;
|
||||
u8 timestamp_enable;
|
||||
u32 ts_align_time;//统计时间戳对齐动作的耗时
|
||||
};
|
||||
|
||||
extern const uint32_t CONFIG_A2DP_DELAY_TIME_AAC;
|
||||
extern const uint32_t CONFIG_A2DP_DELAY_TIME_SBC;
|
||||
extern const int CONFIG_JL_DONGLE_PLAYBACK_LATENCY;
|
||||
extern const int CONFIG_JL_DONGLE_PLAYBACK_DYNAMIC_LATENCY_ENABLE;
|
||||
extern const int CONFIG_A2DP_DELAY_TIME_LO;
|
||||
extern const int CONFIG_A2DP_SBC_DELAY_TIME_LO;
|
||||
extern const int CONFIG_BTCTLER_TWS_ENABLE;
|
||||
extern const int CONFIG_DONGLE_SPEAK_ENABLE;
|
||||
|
||||
extern void bt_audio_reference_clock_select(void *addr, u8 network);
|
||||
extern u32 bt_audio_reference_clock_time(u8 network);
|
||||
extern int a2dp_get_packet_pcm_frames(struct a2dp_file_hdl *hdl, u8 *data, int len);
|
||||
static int a2dp_stream_ts_enable_detect(struct a2dp_file_hdl *hdl, u8 *packet, int *drop);
|
||||
static void a2dp_frame_pack_timestamp(struct a2dp_file_hdl *hdl, struct stream_frame *frame, u8 *data, int pcm_frames);
|
||||
static void a2dp_file_timestamp_setup(struct a2dp_file_hdl *hdl);
|
||||
|
||||
extern void bt_edr_conn_system_clock_init(void *addr, u8 factor);
|
||||
extern u32 bt_edr_conn_master_to_local_time(void *addr, u32 usec);
|
||||
|
||||
static u8 a2dp_low_latency = 0;
|
||||
|
||||
#define msecs_to_bt_time(m) (((m + 1)* 1000) / 625)
|
||||
#define a2dp_seqn_before(a, b) ((a < b && (u16)(b - a) < 1000) || (a > b && (u16)(a - b) > 1000))
|
||||
#define RB16(b) (u16)(((u8 *)b)[0] << 8 | (((u8 *)b))[1])
|
||||
#define RB32(b) (u32)(((u8 *)b)[0] << 24 | (((u8 *)b))[1] << 16 | (((u8 *)b))[2] << 8 | (((u8 *)b))[3])
|
||||
|
||||
#include "a2dp_handshake.c"
|
||||
/*#include "a2dp_aac_demuxer.c"*/
|
||||
|
||||
void a2dp_file_low_latency_enable(u8 enable)
|
||||
{
|
||||
a2dp_low_latency = enable;
|
||||
}
|
||||
|
||||
static void abandon_a2dp_data(void *p)
|
||||
{
|
||||
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)p;
|
||||
struct a2dp_media_frame _frame;
|
||||
while (a2dp_media_try_get_packet(hdl->file, &_frame) > 0) {
|
||||
a2dp_media_free_packet(hdl->file, _frame.packet);
|
||||
}
|
||||
/*a2dp_media_clear_packet_before_seqn(hdl->file, 0);*/
|
||||
}
|
||||
|
||||
static void a2dp_file_start_abandon_data(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
int role = TWS_ROLE_MASTER;
|
||||
|
||||
/*if (CONFIG_BTCTLER_TWS_ENABLE) {
|
||||
role = tws_api_get_role();
|
||||
}*/
|
||||
if (role == TWS_ROLE_MASTER) {
|
||||
if (hdl->timer == 0) {
|
||||
hdl->timer = sys_timer_add(hdl, abandon_a2dp_data, 100);
|
||||
puts("start_abandon_a2dp_data\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void a2dp_file_stop_abandon_data(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
if (hdl->timer) {
|
||||
sys_timer_del(hdl->timer);
|
||||
hdl->timer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void a2dp_source_wake_jlstream_run(void *_hdl)
|
||||
{
|
||||
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
||||
|
||||
if (hdl->start && (hdl->node->state & NODE_STA_SOURCE_NO_DATA)) {
|
||||
jlstream_wakeup_thread(NULL, hdl->node, NULL);
|
||||
}
|
||||
}
|
||||
static void a2dp_source_direct_wake_jlstream_run(void *_hdl)
|
||||
{
|
||||
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
||||
if (hdl) {
|
||||
if (hdl->node) {
|
||||
jlstream_wakeup_thread(NULL, hdl->node, NULL);
|
||||
}
|
||||
hdl->ts_align_time += 4;
|
||||
}
|
||||
}
|
||||
|
||||
static enum stream_node_state a2dp_get_frame(void *_hdl, struct stream_frame **pframe)
|
||||
{
|
||||
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
||||
struct a2dp_media_frame _frame;
|
||||
int drop = 0;
|
||||
int stream_error = 0;
|
||||
|
||||
*pframe = NULL;
|
||||
|
||||
#if A2DP_TIMESTAMP_ENABLE
|
||||
if (!hdl->rtp_ts_en && !hdl->ts_handle) {
|
||||
int err = a2dp_tws_media_handshake(hdl);
|
||||
if (hdl->timestamp_enable && err) {
|
||||
if (!hdl->wake_up_timer) {//快速唤醒数据流,加速tws时间戳交互的过程
|
||||
hdl->ts_align_time = 0;
|
||||
hdl->wake_up_timer = sys_hi_timer_add((void *)hdl, a2dp_source_direct_wake_jlstream_run, 4);
|
||||
}
|
||||
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
||||
}
|
||||
log_d(">>>>handshake time %d ms<<<<\n", hdl->ts_align_time);
|
||||
if (hdl->wake_up_timer) {
|
||||
sys_hi_timer_del(hdl->wake_up_timer);
|
||||
hdl->wake_up_timer = 0;
|
||||
}
|
||||
a2dp_file_timestamp_setup(hdl);
|
||||
}
|
||||
#endif
|
||||
if ((!hdl->ts_handle /* || hdl->edr_to_local_time */) && hdl->start == 0) {
|
||||
int delay = a2dp_media_get_remain_play_time(hdl->file, 1);
|
||||
if (delay < (hdl->ts_handle ? hdl->delay_time : 300)) {
|
||||
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
||||
}
|
||||
hdl->start = 1;
|
||||
}
|
||||
|
||||
int len = hdl->frame_len;
|
||||
if (len == 0) {
|
||||
if (hdl->stream_ctrl) {
|
||||
stream_error = a2dp_stream_control_pull_frame(hdl->stream_ctrl, &_frame, &len);
|
||||
} else {
|
||||
len = a2dp_media_try_get_packet(hdl->file, &_frame);
|
||||
}
|
||||
if (len <= 0) {
|
||||
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
||||
}
|
||||
hdl->frame_len = len;
|
||||
memcpy(&hdl->frame, &_frame, sizeof(struct a2dp_media_frame));
|
||||
} else {
|
||||
memcpy(&_frame, &hdl->frame, sizeof(struct a2dp_media_frame));
|
||||
}
|
||||
|
||||
hdl->seqn = RB16((u8 *)_frame.packet + 2);
|
||||
int err = a2dp_stream_ts_enable_detect(hdl, _frame.packet, &drop);
|
||||
if (err) {
|
||||
if (drop) {
|
||||
if (hdl->stream_ctrl) {
|
||||
a2dp_stream_control_free_frame(hdl->stream_ctrl, &_frame);
|
||||
} else {
|
||||
a2dp_media_free_packet(hdl->file, _frame.packet);
|
||||
}
|
||||
hdl->frame_len = 0;
|
||||
}
|
||||
a2dp_tws_media_try_handshake_ack(0, hdl->seqn);
|
||||
if (!hdl->wake_up_timer) {//快速唤醒数据流,加速tws时间戳交互的过程
|
||||
hdl->ts_align_time = 0;
|
||||
hdl->wake_up_timer = sys_hi_timer_add((void *)hdl, a2dp_source_direct_wake_jlstream_run, 4);
|
||||
}
|
||||
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
||||
}
|
||||
if (hdl->wake_up_timer) {
|
||||
sys_hi_timer_del(hdl->wake_up_timer);
|
||||
hdl->wake_up_timer = 0;
|
||||
}
|
||||
|
||||
int head_len = a2dp_media_get_rtp_header_len(hdl->media_type, _frame.packet, len);
|
||||
|
||||
struct stream_frame *frame;
|
||||
int frame_len = len - head_len;
|
||||
frame = jlstream_get_frame(hdl->node->oport, frame_len);
|
||||
if (frame == NULL) {
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
frame->len = frame_len;
|
||||
frame->timestamp = _frame.clkn;
|
||||
frame->flags |= (stream_error);
|
||||
a2dp_frame_pack_timestamp(hdl, frame, _frame.packet + 4, //时间戳的地址
|
||||
a2dp_get_packet_pcm_frames(hdl,
|
||||
_frame.packet + head_len, frame_len));
|
||||
|
||||
a2dp_tws_media_try_handshake_ack(1, hdl->seqn);
|
||||
|
||||
memcpy(frame->data, _frame.packet + head_len, frame_len);
|
||||
|
||||
if (hdl->stream_ctrl) {
|
||||
a2dp_stream_control_free_frame(hdl->stream_ctrl, &_frame);
|
||||
} else {
|
||||
a2dp_media_free_packet(hdl->file, _frame.packet);
|
||||
}
|
||||
hdl->frame_len = 0;
|
||||
hdl->start = 1;
|
||||
|
||||
ASSERT(frame);
|
||||
*pframe = frame;
|
||||
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
|
||||
static void *a2dp_init(void *priv, struct stream_node *node)
|
||||
{
|
||||
struct a2dp_file_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
hdl->node = node;
|
||||
u16 plug_uuid = get_source_node_plug_uuid(priv);
|
||||
|
||||
struct a2dp_file_params params = {0};
|
||||
jlstream_read_node_data_by_cfg_index(plug_uuid, hdl->node->subid, 0, (void *)¶ms, NULL);
|
||||
hdl->edr_to_local_time = params.edr_to_local_time;
|
||||
return hdl;
|
||||
}
|
||||
|
||||
static const u32 aac_sample_rates[] = {
|
||||
96000, 88200, 64000, 48000, 44100, 32000,
|
||||
24000, 22050, 16000, 12000, 11025, 8000,
|
||||
};
|
||||
|
||||
static const u16 sbc_sample_rates[] = {16000, 32000, 44100, 48000};
|
||||
|
||||
static const u32 ldac_sample_rates[] = {44100, 48000, 88200, 96000};
|
||||
|
||||
static int a2dp_ioc_get_fmt(struct a2dp_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
struct a2dp_media_frame _frame;
|
||||
int type = a2dp_media_get_codec_type(hdl->file);
|
||||
char *code_type;
|
||||
switch (type) {
|
||||
case A2DP_CODEC_SBC:
|
||||
fmt->coding_type = AUDIO_CODING_SBC;
|
||||
code_type = "SBC";
|
||||
break;
|
||||
#if (defined(TCFG_BT_SUPPORT_AAC) && TCFG_BT_SUPPORT_AAC)
|
||||
case A2DP_CODEC_MPEG24:
|
||||
fmt->coding_type = AUDIO_CODING_AAC;
|
||||
code_type = "AAC";
|
||||
break;
|
||||
#endif
|
||||
#if (defined(TCFG_BT_SUPPORT_LDAC) && TCFG_BT_SUPPORT_LDAC)
|
||||
case A2DP_CODEC_LDAC:
|
||||
fmt->coding_type = AUDIO_CODING_LDAC;
|
||||
code_type = "LDAC";
|
||||
break;
|
||||
#endif
|
||||
#if (defined(TCFG_BT_SUPPORT_LHDC) && TCFG_BT_SUPPORT_LHDC)
|
||||
case A2DP_CODEC_LHDC: //LHDC 直接从蓝牙获取格式信息。
|
||||
fmt->coding_type = AUDIO_CODING_LHDC;
|
||||
fmt->sample_rate = a2dp_media_get_sample_rate(hdl->file);
|
||||
fmt->dec_bit_wide = a2dp_media_get_bit_wide(hdl->file);
|
||||
fmt->codec_version = a2dp_media_get_codec_version(hdl->file);
|
||||
fmt->channel_mode = AUDIO_CH_LR;
|
||||
hdl->media_type = type;
|
||||
hdl->codec_version = fmt->codec_version;
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
hdl->channel_num = (fmt->channel_mode == AUDIO_CH_LR) ? 2 : 1;
|
||||
|
||||
printf("a2dp format %s, sample_rate %d, bit_wide %d, codec_version %d\n",
|
||||
"lhdc", hdl->sample_rate, fmt->bit_wide, fmt->codec_version);
|
||||
return 0;
|
||||
#endif
|
||||
default:
|
||||
code_type = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
if (hdl->sample_rate) {
|
||||
fmt->sample_rate = hdl->sample_rate;
|
||||
fmt->channel_mode = hdl->channel_num == 2 ? AUDIO_CH_LR : AUDIO_CH_MIX;
|
||||
return 0;
|
||||
}
|
||||
hdl->media_type = type;
|
||||
|
||||
__again:
|
||||
int len = a2dp_media_try_get_packet(hdl->file, &_frame);
|
||||
if (len <= 0) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
u8 *packet = _frame.packet;
|
||||
|
||||
int head_len = a2dp_media_get_rtp_header_len(type, packet, len);
|
||||
if (head_len >= len) {
|
||||
a2dp_media_free_packet(hdl->file, packet);
|
||||
goto __again;
|
||||
}
|
||||
/*put_buf(packet, head_len + 8);*/
|
||||
u8 *frame = packet + head_len;
|
||||
if (frame[0] == 0x47) { //常见mux aac格式
|
||||
#if (defined(TCFG_BT_SUPPORT_AAC) && TCFG_BT_SUPPORT_AAC)
|
||||
u8 sr = (frame[5] & 0x3C) >> 2;
|
||||
/* u8 ch = ((frame[5] & 0x3) << 2) | ((frame[6] & 0xC0) >> 6); */
|
||||
fmt->channel_mode = AUDIO_CH_LR;
|
||||
fmt->sample_rate = aac_sample_rates[sr];
|
||||
} else if (frame[0] == 0x20) { //特殊LATM aac格式
|
||||
u8 sr = ((frame[2] & 0x7) << 1) | ((frame[3] & 0x80) >> 7);
|
||||
/* u8 ch = ((frame[3] & 0x78) >> 3) ; */
|
||||
fmt->channel_mode = AUDIO_CH_LR;
|
||||
fmt->sample_rate = aac_sample_rates[sr];
|
||||
#endif
|
||||
} else if (frame[0] == 0x9C) { //sbc 格式
|
||||
/*
|
||||
* 检查数据是否为AAC格式,
|
||||
* 可以切换AAC和SBC格式的手机可能切换后数据格式和type不对应
|
||||
*/
|
||||
head_len = a2dp_media_get_rtp_header_len(A2DP_CODEC_MPEG24, packet, len);
|
||||
if (head_len < len) {
|
||||
if (packet[head_len] == 0x47 || packet[head_len] == 0x20) {
|
||||
a2dp_media_free_packet(hdl->file, packet);
|
||||
goto __again;
|
||||
}
|
||||
}
|
||||
|
||||
u8 sr = (frame[1] >> 6) & 0x3;
|
||||
u8 ch = (frame[1] >> 2) & 0x3;
|
||||
if (ch == 0) {
|
||||
fmt->channel_mode = AUDIO_CH_MIX;
|
||||
} else {
|
||||
fmt->channel_mode = AUDIO_CH_LR;
|
||||
}
|
||||
fmt->sample_rate = sbc_sample_rates[sr];
|
||||
#if (defined(TCFG_BT_SUPPORT_LDAC) && TCFG_BT_SUPPORT_LDAC)
|
||||
} else if (frame[1] == 0xAA) {
|
||||
u8 sr = (frame[2] >> (8 - 3)) & 0x7;
|
||||
int chconfig_id = (frame[2] >> (8 - 5)) & 0x03;
|
||||
|
||||
fmt->channel_mode = AUDIO_CH_LR;
|
||||
fmt ->sample_rate = ldac_sample_rates[sr];
|
||||
fmt->chconfig_id = chconfig_id;
|
||||
//printf(" %x %x %x\n",frame[0],frame[1],frame[2]);
|
||||
//printf("sr:%d, sample_rate : %d chconfig_id : %d\n",sr,fmt->sample_rate,chconfig_id);
|
||||
#endif
|
||||
} else {
|
||||
/*
|
||||
* 小米8手机先播sbc,暂停后切成AAC格式点播放,有时第一包数据还是sbc格式
|
||||
* 导致这里获取头信息错误
|
||||
*/
|
||||
a2dp_media_free_packet(hdl->file, packet);
|
||||
goto __again;
|
||||
}
|
||||
a2dp_media_put_packet(hdl->file, packet);
|
||||
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
hdl->channel_num = (fmt->channel_mode == AUDIO_CH_LR) ? 2 : 1;
|
||||
printf("a2dp %d, format %s\n", hdl->sample_rate, code_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int a2dp_ioc_set_bt_addr(struct a2dp_file_hdl *hdl, u8 *bt_addr)
|
||||
{
|
||||
hdl->file = a2dp_open_media_file(bt_addr);
|
||||
if (!hdl->file) {
|
||||
printf("open_file_faild\n");
|
||||
put_buf(bt_addr, 6);
|
||||
return -EINVAL;
|
||||
}
|
||||
memcpy(hdl->bt_addr, bt_addr, 6);
|
||||
|
||||
if (CONFIG_DONGLE_SPEAK_ENABLE) {
|
||||
if (btstack_get_dev_type_for_addr(hdl->bt_addr) == REMOTE_DEV_DONGLE_SPEAK) {
|
||||
hdl->link_jl_dongle = 1;
|
||||
hdl->jl_dongle_latency = CONFIG_JL_DONGLE_PLAYBACK_LATENCY;
|
||||
if (!CONFIG_JL_DONGLE_PLAYBACK_DYNAMIC_LATENCY_ENABLE) {
|
||||
hdl->rtp_ts_en = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a2dp_ioc_stop(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
if (hdl->wake_up_timer) {
|
||||
sys_hi_timer_del(hdl->wake_up_timer);
|
||||
hdl->wake_up_timer = 0;
|
||||
}
|
||||
/*hdl->sample_rate = 0;*/
|
||||
if (hdl->frame_len) {
|
||||
if (hdl->stream_ctrl) {
|
||||
a2dp_stream_control_free_frame(hdl->stream_ctrl, &hdl->frame);
|
||||
} else {
|
||||
a2dp_media_free_packet(hdl->file, hdl->frame.packet);
|
||||
}
|
||||
hdl->frame_len = 0;
|
||||
}
|
||||
a2dp_file_stop_abandon_data(hdl);
|
||||
/*a2dp_media_clear_packet_before_seqn(hdl->file, 0);*/
|
||||
a2dp_media_stop_play(hdl->file);
|
||||
hdl->start = 0;
|
||||
}
|
||||
|
||||
static int sbc_get_packet_pcm_frames(u8 *data, int len)
|
||||
{
|
||||
data++;
|
||||
|
||||
u8 ch = (data[0] >> 2) & 0x3;
|
||||
u8 subbands = (data[0] & 0x01) ? 8 : 4;
|
||||
u8 blocks = (((data[0] >> 4) & 0x03) + 1) * 4;
|
||||
u8 channels = ch == 0x0 ? 1 : 2;
|
||||
u8 joint = ch == 0x3 ? 1 : 0;
|
||||
u8 bitpool = data[1] & 0xff;
|
||||
int frame_len = 4 + ((4 * subbands * channels) >> 3);
|
||||
if (ch >= 0x2) {
|
||||
frame_len += (((joint ? subbands : 0) + blocks * bitpool) + 7) >> 3;
|
||||
} else {
|
||||
frame_len += ((blocks * channels * bitpool) + 7) >> 3;
|
||||
}
|
||||
|
||||
return (len / frame_len) * (blocks * subbands);
|
||||
}
|
||||
|
||||
static int lhdc_get_packet_pcm_frames(void *_hdl, u8 *data, int len, int unit)
|
||||
{
|
||||
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
||||
int point = 0;
|
||||
|
||||
u8 frame_num = (data[0] & 0x3C) >> 2;
|
||||
|
||||
//目前测试LHDC 每帧输出点数是这样的。如果存在兼容性问题。这里需要同步兼容
|
||||
if (hdl->codec_version == 400) { //lhdc_v4
|
||||
point = 256;
|
||||
} else if (hdl->codec_version == 500) {
|
||||
/* if(hdl->sample_rate == 48000){ */
|
||||
/* point = 240 ; */
|
||||
/* }else if(hdl->sample_rate == 44100){ */
|
||||
/* point = 220; */
|
||||
/* } */
|
||||
point = hdl->sample_rate / 1000 * 5; //5ms 的数据量
|
||||
} else if (hdl->codec_version == 300) { //lhdc_v3
|
||||
point = 256;
|
||||
}
|
||||
|
||||
return frame_num * point * (unit);
|
||||
|
||||
}
|
||||
|
||||
|
||||
static int a2dp_get_packet_pcm_frames(struct a2dp_file_hdl *hdl, u8 *data, int len)
|
||||
{
|
||||
u8 unit = 1;
|
||||
u32 frames = 0;
|
||||
|
||||
switch (hdl->media_type) {
|
||||
case A2DP_CODEC_SBC:
|
||||
frames = sbc_get_packet_pcm_frames(data, len);//frame_num * 128 * (unit);
|
||||
break;
|
||||
case A2DP_CODEC_MPEG24:
|
||||
u32 audio_mux_element = 0xffffffff;
|
||||
memcpy(&audio_mux_element, data, len >= sizeof(audio_mux_element) ? sizeof(audio_mux_element) : len);
|
||||
if (audio_mux_element == 0xFC47 || audio_mux_element == 0x10120020) {
|
||||
frames = 1024 * (unit);
|
||||
}
|
||||
break;
|
||||
#if (defined(TCFG_BT_SUPPORT_LHDC) && TCFG_BT_SUPPORT_LHDC)
|
||||
case A2DP_CODEC_LHDC:
|
||||
frames = lhdc_get_packet_pcm_frames(hdl, data, len, unit);//frame_num * point * (unit);
|
||||
break;
|
||||
#endif
|
||||
#if (defined(TCFG_BT_SUPPORT_LDAC) && TCFG_BT_SUPPORT_LDAC)
|
||||
case A2DP_CODEC_LDAC:
|
||||
u8 frame_num = data[0] & 0xf;
|
||||
/* printf("ldac : frame_num = %d\n",frame_num); */
|
||||
if (hdl->sample_rate <= 48000) {
|
||||
frames = frame_num * 128 * (unit);//frame_num * point * (unit);
|
||||
} else {
|
||||
frames = frame_num * 256 * (unit);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
log_e("unsupport codec_type : 0x%x\n", hdl->media_type);
|
||||
break;
|
||||
}
|
||||
|
||||
/* printf("frames %d\n", frames); */
|
||||
return frames;
|
||||
}
|
||||
|
||||
|
||||
static void a2dp_stream_control_open(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
/*
|
||||
* 策略选择:
|
||||
* 1、是否低延时
|
||||
* 2、解码格式?
|
||||
* 3、策略方案 - 默认0,其他为定制方案
|
||||
*/
|
||||
if (hdl->stream_ctrl) {
|
||||
return;
|
||||
}
|
||||
if (hdl->link_jl_dongle) {
|
||||
hdl->stream_ctrl = a2dp_stream_control_plan_select(hdl->file, a2dp_low_latency, hdl->media_type, A2DP_STREAM_JL_DONGLE_CONTROL);
|
||||
|
||||
} else {
|
||||
hdl->stream_ctrl = a2dp_stream_control_plan_select(hdl->file, a2dp_low_latency, hdl->media_type, 0);
|
||||
}
|
||||
if (hdl->stream_ctrl) {
|
||||
hdl->delay_time = a2dp_stream_control_delay_time(hdl->stream_ctrl);
|
||||
a2dp_stream_control_set_underrun_callback(hdl->stream_ctrl, hdl, a2dp_source_wake_jlstream_run);
|
||||
}
|
||||
}
|
||||
|
||||
static void a2dp_stream_control_close(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
if (hdl->stream_ctrl) {
|
||||
a2dp_stream_control_free(hdl->stream_ctrl);
|
||||
hdl->stream_ctrl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 a2dp_stream_update_base_time(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
struct a2dp_media_frame frame;
|
||||
int distance_time = 0;
|
||||
int len = a2dp_media_try_get_packet(hdl->file, &frame);
|
||||
if (len > 0) {
|
||||
u32 base_clkn = frame.clkn;
|
||||
/* if (CONFIG_BTCTLER_TWS_ENABLE && a2dp_low_latency) { */
|
||||
/* base_clkn = bt_audio_reference_clock_time(0); */
|
||||
/* } */
|
||||
a2dp_media_put_packet(hdl->file, frame.packet);
|
||||
u32 base_time = base_clkn + msecs_to_bt_time((hdl->delay_time < 100 ? 100 : hdl->delay_time));
|
||||
if ((int)(base_time - bt_audio_reference_clock_time(0)) < msecs_to_bt_time(150)) {//启动过程耗时很长,此处为避免时间戳超时,加上150ms
|
||||
base_time = bt_audio_reference_clock_time(0) + msecs_to_bt_time(150);
|
||||
}
|
||||
return base_time;
|
||||
}
|
||||
distance_time = a2dp_low_latency ? hdl->delay_time : (hdl->delay_time - a2dp_media_get_remain_play_time(hdl->file, 1));
|
||||
if (!a2dp_low_latency) {
|
||||
distance_time = hdl->delay_time;
|
||||
} else if (distance_time < 100) {
|
||||
distance_time = 100;
|
||||
}
|
||||
/*printf("distance time : %d, %d, %d\n", hdl->delay_time, a2dp_media_get_remain_play_time(hdl->file, 1), distance_time);*/
|
||||
return bt_audio_reference_clock_time(0) + msecs_to_bt_time(distance_time);
|
||||
}
|
||||
|
||||
|
||||
void a2dp_ts_handle_create(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
if (!hdl || (hdl->rtp_ts_en)) {
|
||||
return;
|
||||
}
|
||||
if (hdl->ts_handle) {
|
||||
return;
|
||||
}
|
||||
#if A2DP_TIMESTAMP_ENABLE
|
||||
if (!hdl->timestamp_enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
hdl->base_time = a2dp_stream_update_base_time(hdl);
|
||||
int check_diff = hdl->base_time - bt_audio_reference_clock_time(0);
|
||||
if (check_diff < 0) {
|
||||
printf("a2dp base_time is outdated: %d ms\n", (int)(check_diff * 0.625));
|
||||
} else {
|
||||
printf("a2dp features play time: after %d ms\n", (int)(check_diff * 0.625));
|
||||
}
|
||||
printf("a2dp timestamp base time : %d, %d\n", hdl->base_time, bt_audio_reference_clock_time(0));
|
||||
hdl->ts_handle = a2dp_audio_timestamp_create(hdl->sample_rate, hdl->base_time, TIMESTAMP_US_DENOMINATOR);
|
||||
if (hdl->edr_to_local_time) {
|
||||
bt_edr_conn_system_clock_init(hdl->bt_addr, TIMESTAMP_US_DENOMINATOR);
|
||||
/*printf("--[%s - %d] bt edr system clock init : %u, %lu--\n", __FUNCTION__, __LINE__, hdl->base_time, jiffies_usec());*/
|
||||
}
|
||||
hdl->sync_step = 0;
|
||||
hdl->frame_len = 0;
|
||||
hdl->dts = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void a2dp_ts_handle_release(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
if (!hdl) {
|
||||
return;
|
||||
}
|
||||
#if A2DP_TIMESTAMP_ENABLE
|
||||
if (hdl->ts_handle) {
|
||||
a2dp_audio_timestamp_close(hdl->ts_handle);
|
||||
hdl->ts_handle = NULL;
|
||||
a2dp_tws_audio_conn_offline();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void a2dp_frame_pack_timestamp(struct a2dp_file_hdl *hdl, struct stream_frame *frame, u8 *data, int pcm_frames)
|
||||
{
|
||||
if (CONFIG_DONGLE_SPEAK_ENABLE) {
|
||||
if (hdl->link_jl_dongle && hdl->rtp_ts_en) {
|
||||
u32 ts = RB32(data);
|
||||
frame->timestamp = ts + hdl->jl_dongle_latency * 1000 * 32;
|
||||
frame->flags |= (FRAME_FLAG_TIMESTAMP_ENABLE | FRAME_FLAG_UPDATE_TIMESTAMP);
|
||||
/* printf("ts : %u, %u, %u\n",ts,frame->timestamp, bt_audio_reference_clock_time(0)); */
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!hdl->ts_handle || pcm_frames == 0) {
|
||||
frame->flags &= ~FRAME_FLAG_TIMESTAMP_ENABLE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (CONFIG_BTCTLER_TWS_ENABLE && (frame->flags & FRAME_FLAG_RESET_TIMESTAMP_BIT)) {
|
||||
/*printf("----stream error : resume----\n");*/
|
||||
tws_a2dp_share_timestamp(hdl->ts_handle);
|
||||
}
|
||||
u32 timestamp = a2dp_audio_update_timestamp(hdl->ts_handle, hdl->seqn, hdl->dts);
|
||||
int delay_time = hdl->stream_ctrl ? a2dp_stream_control_delay_time(hdl->stream_ctrl) : hdl->delay_time;
|
||||
int frame_delay = (timestamp - (frame->timestamp * 625 * TIME_US_FACTOR)) / 1000 / TIME_US_FACTOR;
|
||||
/*int distance_time = (int)(timestamp - (frame->timestamp * 625 * TIME_US_FACTOR)) / 1000 / TIME_US_FACTOR - delay_time;*/
|
||||
int distance_time = frame_delay - delay_time;
|
||||
a2dp_audio_delay_offset_update(hdl->ts_handle, distance_time);
|
||||
frame->flags |= (FRAME_FLAG_TIMESTAMP_ENABLE | FRAME_FLAG_UPDATE_TIMESTAMP | FRAME_FLAG_UPDATE_DRIFT_SAMPLE_RATE);
|
||||
if (hdl->edr_to_local_time) {
|
||||
timestamp = bt_edr_conn_master_to_local_time(hdl->bt_addr, timestamp);
|
||||
}
|
||||
frame->timestamp = timestamp;
|
||||
frame->d_sample_rate = a2dp_audio_sample_rate(hdl->ts_handle) - hdl->sample_rate;
|
||||
/*printf("drift : %d\n", frame->d_sample_rate);*/
|
||||
/*printf("-%u, %u, %u-\n", timestamp, bt_edr_conn_master_to_local_time(hdl->bt_addr, timestamp), local_time);*/
|
||||
hdl->dts += pcm_frames;
|
||||
a2dp_stream_mark_next_timestamp(hdl->stream_ctrl, timestamp + PCM_SAMPLE_TO_TIMESTAMP(pcm_frames, hdl->sample_rate));
|
||||
a2dp_stream_bandwidth_detect_handler(hdl->stream_ctrl, pcm_frames, hdl->sample_rate);
|
||||
}
|
||||
|
||||
static int a2dp_stream_ts_enable_detect(struct a2dp_file_hdl *hdl, u8 *packet, int *drop)
|
||||
{
|
||||
if (hdl->sync_step) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!drop) {
|
||||
printf("wrong argument 'drop'!\n");
|
||||
}
|
||||
if (CONFIG_BTCTLER_TWS_ENABLE && hdl->ts_handle) {
|
||||
if (hdl->tws_case != 1 && \
|
||||
!a2dp_audio_timestamp_is_available(hdl->ts_handle, hdl->seqn, 0, drop)) {
|
||||
if (*drop) {
|
||||
hdl->base_time = a2dp_stream_update_base_time(hdl);
|
||||
a2dp_audio_set_base_time(hdl->ts_handle, hdl->base_time);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
log_d(">>>>ts align time %d ms<<<<\n", hdl->ts_align_time);
|
||||
hdl->sync_step = 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a2dp_media_reference_time_setup(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
#if A2DP_TIMESTAMP_ENABLE
|
||||
int err = stream_node_ioctl(hdl->node, NODE_UUID_BT_AUDIO_SYNC, NODE_IOC_SYNCTS, 0);
|
||||
if (err) {
|
||||
err = stream_node_ioctl(hdl->node, NODE_UUID_CAPTURE_SYNC, NODE_IOC_SYNCTS, 0);
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
/* hdl->edr_to_local_time = 1; //由界面配置*/
|
||||
}
|
||||
if (CONFIG_BTCTLER_TWS_ENABLE) {
|
||||
a2dp_tws_audio_conn_delete();//处理两边交流时主机吴判断从机离线,结束对齐时间戳的情况
|
||||
}
|
||||
|
||||
hdl->timestamp_enable = 1;
|
||||
if (!hdl->edr_to_local_time) {
|
||||
hdl->reference = audio_reference_clock_select(hdl->bt_addr, 0);//0 - a2dp主机,1 - tws, 2 - BLE
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void a2dp_media_reference_time_close(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
#if A2DP_TIMESTAMP_ENABLE
|
||||
if (!hdl->edr_to_local_time) {
|
||||
audio_reference_clock_exit(hdl->reference);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void a2dp_file_timestamp_setup(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
a2dp_stream_control_open(hdl);
|
||||
a2dp_ts_handle_create(hdl);
|
||||
}
|
||||
|
||||
static void a2dp_file_timestamp_close(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
a2dp_tws_media_handshake_exit(hdl);
|
||||
a2dp_stream_control_close(hdl);
|
||||
a2dp_ts_handle_release(hdl);
|
||||
}
|
||||
|
||||
static int a2dp_ioctl(void *_hdl, int cmd, int arg)
|
||||
{
|
||||
int err = 0;
|
||||
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_SET_BTADDR:
|
||||
err = a2dp_ioc_set_bt_addr(hdl, (u8 *)arg);
|
||||
break;
|
||||
case NODE_IOC_GET_BTADDR:
|
||||
memcpy((u8 *)arg, hdl->bt_addr, 6);
|
||||
break;
|
||||
case NODE_IOC_GET_FMT:
|
||||
err = a2dp_ioc_get_fmt(hdl, (struct stream_fmt *)arg);
|
||||
stream_node_ioctl(hdl->node, NODE_UUID_BT_AUDIO_SYNC, NODE_IOC_SET_SYNC_NETWORK, hdl->edr_to_local_time ? AUDIO_NETWORK_LOCAL : AUDIO_NETWORK_BT2_1);
|
||||
break;
|
||||
case NODE_IOC_SET_PRIV_FMT:
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
a2dp_media_start_play(hdl->file);
|
||||
a2dp_media_set_rx_notify(hdl->file, hdl, a2dp_source_wake_jlstream_run);
|
||||
a2dp_file_stop_abandon_data(hdl);
|
||||
a2dp_media_reference_time_setup(hdl);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
/*hdl->sample_rate = 0;*/
|
||||
a2dp_media_set_rx_notify(hdl->file, NULL, NULL);
|
||||
a2dp_ioc_stop(hdl);
|
||||
a2dp_file_timestamp_close(hdl);
|
||||
a2dp_media_reference_time_close(hdl);
|
||||
a2dp_file_start_abandon_data(hdl);
|
||||
break;
|
||||
case NODE_IOC_STOP:
|
||||
a2dp_media_set_rx_notify(hdl->file, NULL, NULL);
|
||||
a2dp_ioc_stop(hdl);
|
||||
a2dp_file_timestamp_close(hdl);
|
||||
a2dp_media_reference_time_close(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void a2dp_release(void *_hdl)
|
||||
{
|
||||
struct a2dp_file_hdl *hdl = (struct a2dp_file_hdl *)_hdl;
|
||||
|
||||
a2dp_close_media_file(hdl->file);
|
||||
a2dp_file_stop_abandon_data(hdl);
|
||||
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
|
||||
REGISTER_SOURCE_NODE_PLUG(a2dp_file_plug) = {
|
||||
.uuid = NODE_UUID_A2DP_RX,
|
||||
.frame_size = 1024,
|
||||
.init = a2dp_init,
|
||||
.get_frame = a2dp_get_frame,
|
||||
.ioctl = a2dp_ioctl,
|
||||
.release = a2dp_release,
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* #if TCFG_USER_BT_CLASSIC_ENABLE */
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".a2dp_handshake.data.bss")
|
||||
#pragma data_seg(".a2dp_handshake.data")
|
||||
#pragma const_seg(".a2dp_handshake.text.const")
|
||||
#pragma code_seg(".a2dp_handshake.text")
|
||||
#endif
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file a2dp_handshake.c
|
||||
*
|
||||
* \brief
|
||||
*
|
||||
* Copyright (c) 2011-2022 ZhuHai Jieli Technology Co.,Ltd.
|
||||
*
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#if 1
|
||||
struct a2dp_handshake_data {
|
||||
u8 ack;
|
||||
u8 state;
|
||||
u16 seqn;
|
||||
u8 addr[6];
|
||||
};
|
||||
struct a2dp_tws_handshake {
|
||||
u8 state;
|
||||
u8 handshake_step;
|
||||
u8 rx_state;
|
||||
u8 need_ack;
|
||||
u32 rx_time;
|
||||
u16 rx_seqn;
|
||||
u16 run_seqn;
|
||||
u8 rx_addr[6];
|
||||
u8 run_addr[6];
|
||||
};
|
||||
|
||||
/*
|
||||
* handshake state
|
||||
* init / run / success / timeout
|
||||
* */
|
||||
#define A2DP_HANDSHAKE_STATE_INIT 0
|
||||
#define A2DP_HANDSHAKE_STATE_RUN 1
|
||||
#define A2DP_HANDSHAKE_STATE_SUCCESS 2
|
||||
#define A2DP_HANDSHAKE_STATE_TIMEOUT 3
|
||||
#define A2DP_HANDSHAKE_STATE_WAIT_MONITOR 4
|
||||
|
||||
/*
|
||||
* handshake step
|
||||
* init / request / wait ack / timeout / ack
|
||||
*
|
||||
*/
|
||||
#define A2DP_HANDSHAKE_STEP_INIT 0
|
||||
#define A2DP_HANDSHAKE_STEP_REQUEST 1
|
||||
#define A2DP_HANDSHAKE_STEP_WAIT_ACK 2
|
||||
#define A2DP_HANDSHAKE_STEP_TIMEOUT 3
|
||||
#define A2DP_HANDSHAKE_STEP_ACK 4
|
||||
|
||||
static struct a2dp_tws_handshake a2dp_tws = {0};
|
||||
|
||||
#define A2DP_TWS_HANDSHAKE \
|
||||
((int)((u8 )('A' + '2' + 'D' + 'P') << (3 * 8)) | \
|
||||
(int)(('T' + 'W' + 'S') << (2 * 8)) | \
|
||||
(int)(('H' + 'A' + 'N' + 'D') << (1 * 8)) | \
|
||||
(int)(('S' + 'H' + 'A' + 'K' + 'E') << (0 * 8)))
|
||||
|
||||
#define A2DP_TWS_FILE_EXIT 0
|
||||
#define A2DP_TWS_FILE_START 1
|
||||
#define A2DP_TWS_FILE_RUN 2
|
||||
|
||||
extern u32 bt_audio_conn_clock_time(void *addr);
|
||||
|
||||
static void a2dp_tws_handskake_handler(void *buf, u16 len, bool rx)
|
||||
{
|
||||
struct a2dp_handshake_data handshake_data;
|
||||
|
||||
if (!CONFIG_BTCTLER_TWS_ENABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (rx) {
|
||||
memcpy(&handshake_data, buf, len);
|
||||
/*putchar(handshake_data.ack ? '*' : '#');*/
|
||||
if (handshake_data.ack) {
|
||||
if (a2dp_tws.handshake_step != A2DP_HANDSHAKE_STEP_INIT && a2dp_tws.handshake_step != A2DP_HANDSHAKE_STEP_ACK) {
|
||||
a2dp_tws.handshake_step = A2DP_HANDSHAKE_STEP_ACK;
|
||||
a2dp_tws.rx_time = jiffies;
|
||||
a2dp_tws.rx_seqn = handshake_data.seqn;
|
||||
a2dp_tws.rx_state = handshake_data.state;
|
||||
memcpy(a2dp_tws.rx_addr, handshake_data.addr, sizeof(handshake_data.addr));
|
||||
}
|
||||
} else {
|
||||
a2dp_tws.need_ack = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_TWS_FUNC_STUB(a2dp_tws_handshake) = {
|
||||
.func_id = A2DP_TWS_HANDSHAKE,
|
||||
.func = a2dp_tws_handskake_handler,
|
||||
};
|
||||
|
||||
#define bt_clkn_after(a, b) (((a - b) & 0x7ffffff) >= 0)
|
||||
|
||||
static void a2dp_file_abandon_data_before_time(void *file, u32 time)
|
||||
{
|
||||
struct a2dp_media_frame frame;
|
||||
|
||||
while (1) {
|
||||
int len = a2dp_media_try_get_packet(file, &frame);
|
||||
if (len <= 0) {
|
||||
break;
|
||||
}
|
||||
if (bt_clkn_after(frame.clkn, time)) {
|
||||
a2dp_media_put_packet(file, frame.packet);
|
||||
break;
|
||||
}
|
||||
printf("drop clkn : %d\n", frame.clkn);
|
||||
a2dp_media_free_packet(file, frame.packet);
|
||||
}
|
||||
}
|
||||
|
||||
static void a2dp_file_abandon_data_before_seqn(void *file, u16 ref_seqn)
|
||||
{
|
||||
u16 seqn;
|
||||
struct a2dp_media_frame frame;
|
||||
|
||||
while (1) {
|
||||
int len = a2dp_media_try_get_packet(file, &frame);
|
||||
if (len <= 0) {
|
||||
break;
|
||||
}
|
||||
seqn = RB16((u8 *)frame.packet + 2);
|
||||
if (seqn == ref_seqn || seqn_after(seqn, ref_seqn)) {
|
||||
a2dp_media_put_packet(file, frame.packet);
|
||||
break;
|
||||
}
|
||||
printf("drop seqn : %d\n", seqn);
|
||||
a2dp_media_free_packet(file, frame.packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int a2dp_tws_media_pop_seqn(struct a2dp_file_hdl *hdl, u16 *seqn)
|
||||
{
|
||||
struct a2dp_media_frame frame;
|
||||
int error_num = 0;
|
||||
int len = 0;
|
||||
while (len <= 0) {
|
||||
if (++error_num > 3) {
|
||||
break;
|
||||
}
|
||||
len = a2dp_media_try_get_packet(hdl->file, &frame);
|
||||
}
|
||||
|
||||
if (len <= 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
*seqn = RB16(frame.packet + 2);
|
||||
|
||||
a2dp_media_put_packet(hdl->file, frame.packet);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void a2dp_tws_media_handshake_ack(u8 file_running)
|
||||
{
|
||||
if (!a2dp_tws.need_ack) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct a2dp_handshake_data handshake_data = {0};
|
||||
/*收到握手消息则ack当前信息*/
|
||||
handshake_data.ack = 1;
|
||||
handshake_data.state = file_running ? a2dp_tws.state : A2DP_TWS_FILE_START;
|
||||
handshake_data.seqn = a2dp_tws.run_seqn;
|
||||
memcpy(handshake_data.addr, a2dp_tws.run_addr, sizeof(a2dp_tws.run_addr));
|
||||
int err = tws_api_send_data_to_sibling(&handshake_data, sizeof(handshake_data), A2DP_TWS_HANDSHAKE);
|
||||
if (!err) {
|
||||
a2dp_tws.need_ack = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void a2dp_tws_media_try_handshake_ack(u8 file_running, u16 seqn)
|
||||
{
|
||||
if (!CONFIG_BTCTLER_TWS_ENABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(tws_api_get_tws_state() & TWS_STA_SIBLING_CONNECTED) || /*TWS未连接*/
|
||||
!(tws_api_get_lmp_state(a2dp_tws.run_addr) & TWS_STA_MONITOR_START)) { /*TWS播放另外一边还未开始监听*/
|
||||
return;
|
||||
}
|
||||
|
||||
a2dp_tws.run_seqn = seqn;
|
||||
a2dp_tws_media_handshake_ack(file_running);
|
||||
}
|
||||
|
||||
static int a2dp_tws_media_handshake_init(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_INIT) {
|
||||
u16 seqn = 0;
|
||||
a2dp_file_abandon_data_before_time(hdl->file, bt_audio_conn_clock_time(hdl->bt_addr) - 200);
|
||||
int err = a2dp_tws_media_pop_seqn(hdl, &seqn);
|
||||
if (err) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
local_irq_disable();
|
||||
memset(&a2dp_tws, 0x0, sizeof(a2dp_tws));
|
||||
a2dp_tws.state = A2DP_TWS_FILE_START;
|
||||
a2dp_tws.run_seqn = seqn;
|
||||
memcpy(a2dp_tws.run_addr, hdl->bt_addr, sizeof(a2dp_tws.run_addr));
|
||||
local_irq_enable();
|
||||
hdl->handshake_state = A2DP_HANDSHAKE_STATE_RUN;
|
||||
hdl->handshake_timeout = jiffies + msecs_to_jiffies(300);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a2dp_tws_conn_and_monitor_detect(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
if (!(tws_api_get_tws_state() & TWS_STA_SIBLING_CONNECTED)) {
|
||||
return TWS_STA_SIBLING_DISCONNECTED;
|
||||
}
|
||||
|
||||
if (!(tws_api_get_lmp_state(hdl->bt_addr) & TWS_STA_MONITOR_START)) { /*TWS播放另外一边还未开始监听*/
|
||||
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_INIT) {
|
||||
hdl->handshake_state = A2DP_HANDSHAKE_STATE_WAIT_MONITOR;
|
||||
hdl->handshake_timeout = jiffies + msecs_to_jiffies(200);
|
||||
return TWS_STA_MONITOR_ING;
|
||||
}
|
||||
|
||||
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_WAIT_MONITOR) {
|
||||
if (time_before(jiffies, hdl->handshake_timeout)) {
|
||||
return TWS_STA_MONITOR_ING;
|
||||
}
|
||||
}
|
||||
hdl->handshake_state = A2DP_HANDSHAKE_STATE_INIT;
|
||||
return TWS_STA_SIBLING_DISCONNECTED;
|
||||
}
|
||||
|
||||
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_WAIT_MONITOR) {
|
||||
hdl->handshake_state = A2DP_HANDSHAKE_STATE_INIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a2dp_tws_media_handshake(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!CONFIG_BTCTLER_TWS_ENABLE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = a2dp_tws_conn_and_monitor_detect(hdl);
|
||||
if (err) {
|
||||
if (err == TWS_STA_SIBLING_DISCONNECTED) {
|
||||
memset(&a2dp_tws, 0x0, sizeof(a2dp_tws));
|
||||
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_INIT) {
|
||||
memcpy(a2dp_tws.run_addr, hdl->bt_addr, sizeof(a2dp_tws.run_addr));
|
||||
}
|
||||
hdl->tws_case = 1;
|
||||
goto a2dp_file_run;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = a2dp_tws_media_handshake_init(hdl);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
struct a2dp_handshake_data handshake_data = {0};
|
||||
/*握手请求*/
|
||||
printf("[handshake] : %d, %d\n", hdl->handshake_state, a2dp_tws.handshake_step);
|
||||
switch (a2dp_tws.handshake_step) {
|
||||
case A2DP_HANDSHAKE_STEP_INIT:
|
||||
case A2DP_HANDSHAKE_STEP_REQUEST:
|
||||
local_irq_disable();
|
||||
hdl->request_timeout = jiffies + msecs_to_jiffies(60);
|
||||
err = tws_api_send_data_to_sibling(&handshake_data, sizeof(handshake_data.ack), A2DP_TWS_HANDSHAKE);
|
||||
a2dp_tws.handshake_step = err ? A2DP_HANDSHAKE_STEP_REQUEST : A2DP_HANDSHAKE_STEP_WAIT_ACK;
|
||||
local_irq_enable();
|
||||
break;
|
||||
case A2DP_HANDSHAKE_STEP_WAIT_ACK:
|
||||
local_irq_disable();
|
||||
if (time_after(jiffies, hdl->request_timeout)) {
|
||||
/*request后响应超时*/
|
||||
a2dp_tws.handshake_step = A2DP_HANDSHAKE_STEP_REQUEST;
|
||||
}
|
||||
local_irq_enable();
|
||||
break;
|
||||
case A2DP_HANDSHAKE_STEP_ACK:
|
||||
hdl->handshake_state = A2DP_HANDSHAKE_STATE_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
a2dp_tws_media_handshake_ack(0);
|
||||
|
||||
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_RUN) {
|
||||
if (time_before(jiffies, hdl->handshake_timeout)) { //等待握手未超时,以失败返回
|
||||
return -EINVAL;
|
||||
}
|
||||
hdl->handshake_state = A2DP_HANDSHAKE_STATE_TIMEOUT;
|
||||
}
|
||||
|
||||
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_TIMEOUT) {
|
||||
/*握手超时,这时候需要对超时数据进行处理*/
|
||||
printf("a2dp handshake timeout : %d\n", bt_audio_conn_clock_time(hdl->bt_addr));
|
||||
a2dp_file_abandon_data_before_time(hdl->file, bt_audio_conn_clock_time(hdl->bt_addr) - 200);
|
||||
hdl->tws_case = 1;
|
||||
goto a2dp_file_run;
|
||||
}
|
||||
|
||||
if (memcmp(a2dp_tws.rx_addr, hdl->bt_addr, 6) != 0) {
|
||||
printf("a2dp handshake bt addr error : \n");
|
||||
put_buf(a2dp_tws.rx_addr, 6);
|
||||
put_buf(hdl->bt_addr, 6);
|
||||
hdl->tws_case = 1;
|
||||
goto a2dp_file_run;
|
||||
}
|
||||
|
||||
if (__builtin_abs((int)(a2dp_tws.rx_seqn - a2dp_tws.run_seqn)) > 50) {
|
||||
printf("a2dp handshake seqn error : %d, %d, need check btstack.\n", a2dp_tws.rx_seqn, a2dp_tws.run_seqn);
|
||||
hdl->tws_case = 1;
|
||||
goto a2dp_file_run;
|
||||
}
|
||||
|
||||
/*将两边数据处理为一致的seqn*/
|
||||
a2dp_file_abandon_data_before_seqn(hdl->file, a2dp_tws.rx_seqn);
|
||||
|
||||
a2dp_file_run:
|
||||
local_irq_disable();
|
||||
if (hdl->handshake_state == A2DP_HANDSHAKE_STATE_SUCCESS && a2dp_tws.rx_state == A2DP_TWS_FILE_RUN) {
|
||||
printf("------>>> A2DP player join tws <<<--------\n");
|
||||
hdl->tws_case = 2;
|
||||
}
|
||||
a2dp_tws.state = A2DP_TWS_FILE_RUN;
|
||||
a2dp_tws.handshake_step = A2DP_HANDSHAKE_STATE_INIT;
|
||||
local_irq_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void a2dp_tws_media_handshake_exit(struct a2dp_file_hdl *hdl)
|
||||
{
|
||||
if (!CONFIG_BTCTLER_TWS_ENABLE) {
|
||||
return;
|
||||
}
|
||||
local_irq_disable();
|
||||
a2dp_tws.state = A2DP_TWS_FILE_EXIT;
|
||||
hdl->handshake_state = A2DP_HANDSHAKE_STATE_INIT;
|
||||
local_irq_enable();
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,537 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".a2dp_streamctrl.data.bss")
|
||||
#pragma data_seg(".a2dp_streamctrl.data")
|
||||
#pragma const_seg(".a2dp_streamctrl.text.const")
|
||||
#pragma code_seg(".a2dp_streamctrl.text")
|
||||
#endif
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file a2dp_streamctrl.c
|
||||
*
|
||||
* \brief a2dp plug stream control file.
|
||||
*
|
||||
* Copyright (c) 2011-2022 ZhuHai Jieli Technology Co.,Ltd.
|
||||
*
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#include "btstack/a2dp_media_codec.h"
|
||||
#include "sync/audio_syncts.h"
|
||||
#include "system/timer.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "source_node.h"
|
||||
#include "jiffies.h"
|
||||
#include "a2dp_streamctrl.h"
|
||||
|
||||
extern const int CONFIG_A2DP_DELAY_TIME_AAC;
|
||||
extern const int CONFIG_A2DP_DELAY_TIME_SBC;
|
||||
extern const int CONFIG_A2DP_ADAPTIVE_MAX_LATENCY;
|
||||
extern const int CONFIG_A2DP_DELAY_TIME_AAC_LO;
|
||||
extern const int CONFIG_A2DP_DELAY_TIME_SBC_LO;
|
||||
extern const int CONFIG_JL_DONGLE_PLAYBACK_LATENCY;
|
||||
extern const int CONFIG_DONGLE_SPEAK_ENABLE;
|
||||
extern const int CONFIG_A2DP_MAX_BUF_SIZE;
|
||||
extern u32 bt_audio_reference_clock_time(u8 network);
|
||||
|
||||
#define A2DP_FLUENT_DETECT_INTERVAL 90000//ms 流畅播放延时检测时长
|
||||
#define JL_DONGLE_FLUENT_DETECT_INTERVAL 30000//ms
|
||||
#define A2DP_ADAPTIVE_DELAY_ENABLE 1
|
||||
|
||||
#define A2DP_STREAM_NO_ERR 0
|
||||
#define A2DP_STREAM_UNDERRUN 1
|
||||
#define A2DP_STREAM_OVERRUN 2
|
||||
#define A2DP_STREAM_MISSED 3
|
||||
#define A2DP_STREAM_DECODE_ERR 4
|
||||
#define A2DP_STREAM_LOW_UNDERRUN 5
|
||||
|
||||
#define RB16(b) (u16)(((u8 *)b)[0] << 8 | (((u8 *)b))[1])
|
||||
#define bt_time_to_msecs(clk) (((clk) * 625) / 1000)
|
||||
|
||||
#define DELAY_INCREMENT 150
|
||||
#define LOW_LATENCY_DELAY_INCREMENT 50
|
||||
|
||||
#define DELAY_DECREMENT 50
|
||||
#define LOW_LATENCY_DELAY_DECREMENT 10
|
||||
|
||||
#define MAX_DELAY_INCREMENT 150
|
||||
struct a2dp_stream_control {
|
||||
u8 plan;
|
||||
u8 stream_error;
|
||||
u8 frame_free;
|
||||
u8 first_in;
|
||||
u8 low_latency;
|
||||
u8 jl_dongle;
|
||||
u16 timer;
|
||||
u16 seqn;
|
||||
u16 overrun_seqn;
|
||||
u16 missed_num;
|
||||
s16 repair_frames;
|
||||
s16 initial_latency;
|
||||
s16 adaptive_latency;
|
||||
s16 adaptive_max_latency;
|
||||
s16 max_rx_interval;
|
||||
u32 detect_timeout;
|
||||
u32 codec_type;
|
||||
u32 frame_time;
|
||||
struct a2dp_media_frame frame;
|
||||
int frame_len;
|
||||
void *stream;
|
||||
void *sample_detect;
|
||||
u32 next_timestamp;
|
||||
void *underrun_signal;
|
||||
void (*underrun_callback)(void *);
|
||||
};
|
||||
|
||||
|
||||
void a2dp_stream_mark_next_timestamp(void *_ctrl, u32 next_timestamp)
|
||||
{
|
||||
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
|
||||
if (ctrl) {
|
||||
ctrl->next_timestamp = next_timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
void a2dp_stream_bandwidth_detect_handler(void *_ctrl, int pcm_frames, int sample_rate)
|
||||
{
|
||||
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
|
||||
int max_latency = 0;
|
||||
|
||||
if (ctrl->low_latency) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrl->frame_len) {
|
||||
max_latency = (CONFIG_A2DP_MAX_BUF_SIZE * pcm_frames / ctrl->frame_len) * 1000 / sample_rate * 9 / 10;
|
||||
}
|
||||
|
||||
if (!max_latency) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (max_latency < ctrl->adaptive_max_latency) {
|
||||
ctrl->adaptive_max_latency = max_latency;
|
||||
}
|
||||
|
||||
if (ctrl->adaptive_latency > ctrl->adaptive_max_latency) {
|
||||
ctrl->adaptive_latency = ctrl->adaptive_max_latency;
|
||||
a2dp_media_update_delay_report_time(ctrl->stream, ctrl->adaptive_latency);
|
||||
}
|
||||
}
|
||||
|
||||
static void a2dp_stream_underrun_adaptive_handler(struct a2dp_stream_control *ctrl)
|
||||
{
|
||||
#if A2DP_ADAPTIVE_DELAY_ENABLE
|
||||
ctrl->detect_timeout = jiffies + msecs_to_jiffies(ctrl->jl_dongle ? JL_DONGLE_FLUENT_DETECT_INTERVAL : A2DP_FLUENT_DETECT_INTERVAL);
|
||||
|
||||
if (!ctrl->low_latency) {
|
||||
ctrl->adaptive_latency = ctrl->adaptive_max_latency;
|
||||
} else {
|
||||
ctrl->adaptive_latency += LOW_LATENCY_DELAY_INCREMENT;
|
||||
|
||||
if (ctrl->adaptive_latency < ctrl->max_rx_interval) {
|
||||
ctrl->adaptive_latency = ctrl->max_rx_interval;
|
||||
}
|
||||
|
||||
if (ctrl->adaptive_latency > ctrl->adaptive_max_latency) {
|
||||
ctrl->adaptive_latency = ctrl->adaptive_max_latency;
|
||||
}
|
||||
}
|
||||
a2dp_media_update_delay_report_time(ctrl->stream, ctrl->adaptive_latency);
|
||||
/*printf("---underrun, adaptive : %dms---\n", ctrl->adaptive_latency);*/
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* 自适应延时策略
|
||||
*/
|
||||
static void a2dp_stream_adaptive_detect_handler(struct a2dp_stream_control *ctrl, u8 new_frame, u32 new_frame_time)
|
||||
{
|
||||
#if A2DP_ADAPTIVE_DELAY_ENABLE
|
||||
if (ctrl->stream_error || !new_frame) {
|
||||
return;
|
||||
}
|
||||
int rx_interval = 0;
|
||||
if (ctrl->frame_time) {
|
||||
rx_interval = bt_time_to_msecs((new_frame_time - ctrl->frame_time) & 0x7ffffff) + 1;
|
||||
}
|
||||
ctrl->frame_time = new_frame_time;
|
||||
|
||||
if (rx_interval > ctrl->max_rx_interval) {
|
||||
if (CONFIG_DONGLE_SPEAK_ENABLE && ctrl->jl_dongle) {
|
||||
return;
|
||||
}
|
||||
ctrl->max_rx_interval = rx_interval;
|
||||
if (ctrl->max_rx_interval > ctrl->initial_latency + MAX_DELAY_INCREMENT) {
|
||||
ctrl->max_rx_interval = ctrl->initial_latency + MAX_DELAY_INCREMENT;
|
||||
}
|
||||
if (ctrl->adaptive_latency < ctrl->max_rx_interval) {
|
||||
ctrl->adaptive_latency = ctrl->max_rx_interval;
|
||||
a2dp_media_update_delay_report_time(ctrl->stream, ctrl->adaptive_latency);
|
||||
ctrl->detect_timeout = jiffies + msecs_to_jiffies(A2DP_FLUENT_DETECT_INTERVAL);
|
||||
ctrl->max_rx_interval = ctrl->initial_latency;
|
||||
return;
|
||||
}
|
||||
/*printf("---rx interval, adaptive : %dms, %dms---\n", ctrl->adaptive_latency, ctrl->max_rx_interval);*/
|
||||
}
|
||||
|
||||
if (time_after(jiffies, ctrl->detect_timeout)) {
|
||||
ctrl->adaptive_latency -= DELAY_DECREMENT;
|
||||
if (ctrl->adaptive_latency < ctrl->max_rx_interval) {
|
||||
ctrl->adaptive_latency = ctrl->max_rx_interval;
|
||||
}
|
||||
|
||||
if (ctrl->adaptive_latency < ctrl->initial_latency) {
|
||||
ctrl->adaptive_latency = ctrl->initial_latency;
|
||||
}
|
||||
/*printf("---adaptive detect : %dms, %dms---\n", ctrl->adaptive_latency, ctrl->max_rx_interval);*/
|
||||
if (ctrl->low_latency) {
|
||||
ctrl->max_rx_interval -= 10;//ctrl->initial_latency;
|
||||
} else {
|
||||
ctrl->max_rx_interval = ctrl->initial_latency;
|
||||
}
|
||||
ctrl->detect_timeout = jiffies + msecs_to_jiffies(A2DP_FLUENT_DETECT_INTERVAL);
|
||||
a2dp_media_update_delay_report_time(ctrl->stream, ctrl->adaptive_latency);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void *a2dp_stream_control_plan_select(void *stream, int low_latency, u32 codec_type, u8 plan)
|
||||
{
|
||||
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)zalloc(sizeof(struct a2dp_stream_control));
|
||||
|
||||
if (!ctrl) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (plan) {
|
||||
|
||||
case A2DP_STREAM_JL_DONGLE_CONTROL:
|
||||
if (CONFIG_DONGLE_SPEAK_ENABLE) {
|
||||
ctrl->initial_latency = CONFIG_JL_DONGLE_PLAYBACK_LATENCY;
|
||||
ctrl->adaptive_latency = ctrl->initial_latency;
|
||||
ctrl->max_rx_interval = ctrl->initial_latency;
|
||||
ctrl->adaptive_max_latency = low_latency ? (ctrl->initial_latency + MAX_DELAY_INCREMENT) : CONFIG_A2DP_ADAPTIVE_MAX_LATENCY;
|
||||
ctrl->detect_timeout = jiffies + msecs_to_jiffies(A2DP_FLUENT_DETECT_INTERVAL);
|
||||
ctrl->jl_dongle = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (low_latency) {
|
||||
ctrl->initial_latency = codec_type == A2DP_CODEC_MPEG24 ? CONFIG_A2DP_DELAY_TIME_AAC_LO : CONFIG_A2DP_DELAY_TIME_SBC_LO;
|
||||
} else {
|
||||
ctrl->initial_latency = codec_type == A2DP_CODEC_MPEG24 ? CONFIG_A2DP_DELAY_TIME_AAC : CONFIG_A2DP_DELAY_TIME_SBC;
|
||||
}
|
||||
ctrl->adaptive_max_latency = low_latency ? (ctrl->initial_latency + MAX_DELAY_INCREMENT) : CONFIG_A2DP_ADAPTIVE_MAX_LATENCY;
|
||||
ctrl->adaptive_latency = ctrl->initial_latency;
|
||||
ctrl->max_rx_interval = ctrl->initial_latency;
|
||||
ctrl->detect_timeout = jiffies + msecs_to_jiffies(A2DP_FLUENT_DETECT_INTERVAL);
|
||||
break;
|
||||
}
|
||||
|
||||
ctrl->low_latency = low_latency;
|
||||
ctrl->first_in = 1;
|
||||
ctrl->codec_type = codec_type;
|
||||
ctrl->plan = plan;
|
||||
ctrl->stream = stream;
|
||||
a2dp_media_update_delay_report_time(ctrl->stream, ctrl->adaptive_latency);
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
static void a2dp_stream_underrun_signal(void *arg)
|
||||
{
|
||||
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)arg;
|
||||
|
||||
local_irq_disable();
|
||||
if (ctrl->underrun_callback) {
|
||||
ctrl->underrun_callback(ctrl->underrun_signal);
|
||||
}
|
||||
ctrl->timer = 0;
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
static int a2dp_audio_is_underrun(struct a2dp_stream_control *ctrl)
|
||||
{
|
||||
int underrun_time = ctrl->low_latency ? 2 : 30;
|
||||
if (ctrl->next_timestamp) {
|
||||
u32 reference_clock = bt_audio_reference_clock_time(0);
|
||||
if (reference_clock == (u32) - 1) {
|
||||
return true;
|
||||
}
|
||||
u32 reference_time = reference_clock * 625 * TIMESTAMP_US_DENOMINATOR;
|
||||
int distance_time = ctrl->next_timestamp - reference_time;
|
||||
if (distance_time > 67108863L || distance_time < -67108863L) {
|
||||
if (ctrl->next_timestamp > reference_time) {
|
||||
distance_time = ctrl->next_timestamp - 0xffffffff - reference_time;
|
||||
} else {
|
||||
distance_time = 0xffffffff - reference_time + ctrl->next_timestamp;
|
||||
}
|
||||
}
|
||||
distance_time = distance_time / 1000 / TIMESTAMP_US_DENOMINATOR;
|
||||
/*printf("distance_time %d %u %u\n", distance_time, ctrl->next_timestamp, reference_time);*/
|
||||
if (distance_time < underrun_time) {
|
||||
return true;
|
||||
}
|
||||
|
||||
local_irq_disable();
|
||||
if (ctrl->timer) {
|
||||
sys_hi_timeout_del(ctrl->timer);
|
||||
ctrl->timer = 0;
|
||||
}
|
||||
ctrl->timer = sys_hi_timeout_add(ctrl, a2dp_stream_underrun_signal, distance_time - underrun_time);
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a2dp_stream_underrun_handler(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame)
|
||||
{
|
||||
if (!a2dp_audio_is_underrun(ctrl)) {
|
||||
/*putchar('x');*/
|
||||
return 0;
|
||||
}
|
||||
putchar('X');
|
||||
|
||||
a2dp_stream_underrun_adaptive_handler(ctrl);
|
||||
if (ctrl->stream_error != A2DP_STREAM_UNDERRUN) {
|
||||
if (!ctrl->stream_error) {
|
||||
|
||||
}
|
||||
ctrl->stream_error = A2DP_STREAM_UNDERRUN;
|
||||
}
|
||||
memcpy(frame, &ctrl->frame, sizeof(ctrl->frame));
|
||||
ctrl->repair_frames++;
|
||||
|
||||
return ctrl->frame_len;
|
||||
}
|
||||
|
||||
static void a2dp_stream_control_free_frames(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame)
|
||||
{
|
||||
if (frame && frame->packet) {
|
||||
a2dp_media_free_packet(ctrl->stream, frame->packet);
|
||||
if ((void *)frame->packet == (void *)ctrl->frame.packet) {
|
||||
ctrl->frame.packet = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (ctrl->frame.packet) {
|
||||
a2dp_media_free_packet(ctrl->stream, ctrl->frame.packet);
|
||||
ctrl->frame.packet = NULL;
|
||||
}
|
||||
ctrl->frame_len = 0;
|
||||
}
|
||||
|
||||
|
||||
static int a2dp_stream_overrun_handler(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame, int *len)
|
||||
{
|
||||
while (1) {
|
||||
if (1) {//!ctrl->low_latency) {
|
||||
int msecs = a2dp_media_get_remain_play_time(ctrl->stream, 1);
|
||||
if (msecs < ctrl->adaptive_latency) {
|
||||
/*printf("adaptive latency %d, msecs %d\n", ctrl->adaptive_latency, msecs);*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int rlen = a2dp_media_try_get_packet(ctrl->stream, frame);
|
||||
if (rlen <= 0) {
|
||||
break;
|
||||
}
|
||||
a2dp_stream_control_free_frames(ctrl, NULL);
|
||||
memcpy(&ctrl->frame, frame, sizeof(ctrl->frame));
|
||||
ctrl->frame_len = rlen;
|
||||
*len = rlen;
|
||||
putchar('n');
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(frame, &ctrl->frame, sizeof(ctrl->frame));
|
||||
*len = ctrl->frame_len;
|
||||
putchar('o');
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a2dp_stream_missed_handler(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame, int *len)
|
||||
{
|
||||
memcpy(frame, &ctrl->frame, sizeof(ctrl->frame));
|
||||
*len = ctrl->frame_len;
|
||||
if (--ctrl->missed_num == 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int a2dp_stream_error_filter(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame, int len)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
int header_len = a2dp_media_get_rtp_header_len(ctrl->codec_type, frame->packet, len);
|
||||
if (header_len >= len) {
|
||||
printf("##A2DP header error : %d\n", header_len);
|
||||
a2dp_stream_control_free_frames(ctrl, frame);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
u16 seqn = RB16(frame->packet + 2);
|
||||
if (ctrl->first_in) {
|
||||
ctrl->first_in = 0;
|
||||
goto __exit;
|
||||
}
|
||||
if (seqn != ctrl->seqn) {
|
||||
if (ctrl->stream_error == A2DP_STREAM_UNDERRUN) {
|
||||
int missed_frames = (u16)(seqn - ctrl->seqn) - 1;
|
||||
if (missed_frames > ctrl->repair_frames) {
|
||||
ctrl->stream_error = A2DP_STREAM_MISSED;
|
||||
ctrl->missed_num = 2;//missed_frames - ctrl->repair_frames + 1;
|
||||
/*printf("case 0 : %d, %d\n", missed_frames, ctrl->repair_frames);*/
|
||||
err = -EAGAIN;
|
||||
} else if (missed_frames < ctrl->repair_frames) {
|
||||
ctrl->stream_error = A2DP_STREAM_OVERRUN;
|
||||
ctrl->overrun_seqn = seqn + ctrl->repair_frames - missed_frames;
|
||||
/*printf("case 1 : %d, %d, seqn : %d, %d\n", missed_frames, ctrl->repair_frames, seqn, ctrl->overrun_seqn);*/
|
||||
err = -EAGAIN;
|
||||
}
|
||||
} else if (!ctrl->stream_error && (u16)(seqn - ctrl->seqn) > 1) {
|
||||
err = -EAGAIN;
|
||||
if ((u16)(seqn - ctrl->seqn) > 32768) {
|
||||
a2dp_stream_control_free_frames(ctrl, frame);
|
||||
return err;
|
||||
}
|
||||
ctrl->stream_error = A2DP_STREAM_MISSED;
|
||||
ctrl->missed_num = 2;//(u16)(seqn - ctrl->seqn);
|
||||
/*printf("case 2 : %d, %d, %d\n", seqn, ctrl->seqn, ctrl->missed_num); */
|
||||
}
|
||||
ctrl->repair_frames = 0;
|
||||
}
|
||||
|
||||
__exit:
|
||||
ctrl->seqn = seqn;
|
||||
memcpy(&ctrl->frame, frame, sizeof(ctrl->frame));
|
||||
ctrl->frame_len = len;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int a2dp_get_frame_and_check_errors(struct a2dp_stream_control *ctrl, struct a2dp_media_frame *frame, int *len)
|
||||
{
|
||||
int rlen = 0;
|
||||
int err = 0;
|
||||
u8 new_packet = 0;
|
||||
|
||||
try_again:
|
||||
switch (ctrl->stream_error) {
|
||||
case A2DP_STREAM_OVERRUN:
|
||||
new_packet = a2dp_stream_overrun_handler(ctrl, frame, len);
|
||||
break;
|
||||
case A2DP_STREAM_MISSED:
|
||||
new_packet = a2dp_stream_missed_handler(ctrl, frame, len);
|
||||
if (frame->packet) {
|
||||
break;
|
||||
}
|
||||
//注意:这里不break是因为很有可能由于位流的错误导致补包无法再正常补上
|
||||
default:
|
||||
rlen = a2dp_media_try_get_packet(ctrl->stream, frame);
|
||||
if (rlen <= 0) {
|
||||
rlen = a2dp_stream_underrun_handler(ctrl, frame);
|
||||
} else {
|
||||
if (ctrl->frame.packet) {
|
||||
a2dp_media_free_packet(ctrl->stream, ctrl->frame.packet);
|
||||
ctrl->frame.packet = NULL;
|
||||
}
|
||||
ctrl->frame_len = 0;
|
||||
new_packet = 1;
|
||||
}
|
||||
*len = rlen;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*len <= 0) {
|
||||
return 0;
|
||||
}
|
||||
err = a2dp_stream_error_filter(ctrl, frame, *len);
|
||||
if (err) {
|
||||
if (-err == EAGAIN) {
|
||||
goto try_again;
|
||||
}
|
||||
*len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
a2dp_stream_adaptive_detect_handler(ctrl, new_packet, frame->clkn);
|
||||
|
||||
if (ctrl->stream_error) {
|
||||
if (new_packet) {
|
||||
ctrl->stream_error = 0;
|
||||
return FRAME_FLAG_RESET_TIMESTAMP_BIT;
|
||||
}
|
||||
return FRAME_FLAG_FILL_PACKET;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int a2dp_stream_control_pull_frame(void *_ctrl, struct a2dp_media_frame *frame, int *len)
|
||||
{
|
||||
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
|
||||
|
||||
if (!ctrl) {
|
||||
*len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (ctrl->plan) {
|
||||
default:
|
||||
return a2dp_get_frame_and_check_errors(ctrl, frame, len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void a2dp_stream_control_free_frame(void *_ctrl, struct a2dp_media_frame *frame)
|
||||
{
|
||||
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
|
||||
|
||||
switch (ctrl->plan) {
|
||||
default:
|
||||
if (ctrl->frame.packet == frame->packet) {
|
||||
ctrl->frame_free = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void a2dp_stream_control_set_underrun_callback(void *_ctrl, void *priv, void (*callback)(void *priv))
|
||||
{
|
||||
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
|
||||
|
||||
ctrl->underrun_signal = priv;
|
||||
ctrl->underrun_callback = callback;
|
||||
}
|
||||
|
||||
int a2dp_stream_control_delay_time(void *_ctrl)
|
||||
{
|
||||
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
|
||||
|
||||
if (ctrl) {
|
||||
return ctrl->adaptive_latency;
|
||||
}
|
||||
|
||||
return CONFIG_A2DP_DELAY_TIME_AAC;
|
||||
}
|
||||
|
||||
void a2dp_stream_control_free(void *_ctrl)
|
||||
{
|
||||
struct a2dp_stream_control *ctrl = (struct a2dp_stream_control *)_ctrl;
|
||||
|
||||
if (!ctrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
a2dp_stream_control_free_frames(ctrl, NULL);
|
||||
|
||||
local_irq_disable();
|
||||
if (ctrl->timer) {
|
||||
sys_hi_timeout_del(ctrl->timer);
|
||||
ctrl->timer = 0;
|
||||
}
|
||||
local_irq_enable();
|
||||
free(ctrl);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*************************************************************************************************/
|
||||
/*!
|
||||
* \file audio/framework/plugs/source/a2dp_streamctrl.h
|
||||
*
|
||||
* \brief
|
||||
*
|
||||
* Copyright (c) 2011-2022 ZhuHai Jieli Technology Co.,Ltd.
|
||||
*
|
||||
*/
|
||||
/*************************************************************************************************/
|
||||
#ifndef _A2DP_STREAM_CTRL_H_
|
||||
#define _A2DP_STREAM_CTRL_H_
|
||||
|
||||
enum {
|
||||
A2DP_STREAM_DEFAULT_CONTROL = 0,
|
||||
A2DP_STREAM_JL_DONGLE_CONTROL = 1,
|
||||
};
|
||||
|
||||
void *a2dp_stream_control_plan_select(void *stream, int low_latency, u32 codec_type, u8 plan);
|
||||
|
||||
int a2dp_stream_control_pull_frame(void *_ctrl, struct a2dp_media_frame *frame, int *len);
|
||||
|
||||
void a2dp_stream_control_free_frame(void *_ctrl, struct a2dp_media_frame *frame);
|
||||
|
||||
int a2dp_stream_control_delay_time(void *_ctrl);
|
||||
|
||||
void a2dp_stream_control_free(void *_ctrl);
|
||||
|
||||
void a2dp_stream_mark_next_timestamp(void *_ctrl, u32 next_timestamp);
|
||||
|
||||
void a2dp_stream_bandwidth_detect_handler(void *_ctrl, int pcm_frames, int sample_rate);
|
||||
|
||||
void a2dp_stream_control_set_underrun_callback(void *_ctrl, void *priv, void (*callback)(void *priv));
|
||||
#endif
|
||||
@@ -0,0 +1,739 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".adc_file.data.bss")
|
||||
#pragma data_seg(".adc_file.data")
|
||||
#pragma const_seg(".adc_file.text.const")
|
||||
#pragma code_seg(".adc_file.text")
|
||||
#endif
|
||||
#include "source_node.h"
|
||||
#include "asm/audio_adc.h"
|
||||
#include "audio_config.h"
|
||||
#include "adc_file.h"
|
||||
#include "gpio_config.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "audio_cvp.h"
|
||||
#include "asm/audio_common.h"
|
||||
#if TCFG_AUDIO_ANC_ENABLE
|
||||
#include "audio_anc.h"
|
||||
#endif
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
#include "audio_dut_control.h"
|
||||
#endif
|
||||
|
||||
#if defined(TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN) && TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN
|
||||
#include "icsd_adt_app.h"
|
||||
#endif
|
||||
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
#include "audio_dut_control.h"
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
#define adc_file_log printf
|
||||
#else
|
||||
#define adc_file_log(...)
|
||||
#endif/*log_en*/
|
||||
|
||||
extern struct audio_adc_hdl adc_hdl;
|
||||
extern const struct adc_platform_cfg adc_platform_cfg_table[AUDIO_ADC_MAX_NUM];
|
||||
|
||||
#define ESCO_ADC_BUF_NUM 2 //mic_adc采样buf个数
|
||||
|
||||
struct adc_file_common ;
|
||||
struct adc_file_hdl {
|
||||
char name[16];
|
||||
void *source_node;
|
||||
struct stream_node *node;
|
||||
enum stream_scene scene;
|
||||
struct adc_mic_ch mic_ch;
|
||||
struct audio_adc_output_hdl adc_output;
|
||||
s16 *adc_buf;
|
||||
struct adc_file_common *adc_f;
|
||||
u16 sample_rate;
|
||||
u16 irq_points;
|
||||
u8 adc_seq;
|
||||
u8 Qval;
|
||||
u8 start;
|
||||
u8 dump_cnt;
|
||||
u8 ch_num;
|
||||
};
|
||||
|
||||
struct adc_file_common {
|
||||
u8 read_flag; //读取配置的标志
|
||||
struct adc_file_hdl *hdl; //当前ADC节点的句柄
|
||||
struct adc_file_cfg cfg; //ADC的参数信息
|
||||
struct adc_platform_cfg platform_cfg[AUDIO_ADC_MAX_NUM];
|
||||
};
|
||||
|
||||
struct adc_file_global {
|
||||
u8 fixed_ch_num;
|
||||
s16 *fixed_buf; //固定的ADCbuffer
|
||||
struct adc_file_cfg cfg; //ESCO ADC的参数信息
|
||||
};
|
||||
|
||||
static struct adc_file_common esco_adc_f = {0};
|
||||
static struct adc_file_global esco_adc_file_g = {0};
|
||||
|
||||
/*根据mic通道值获取使用的第几个mic*/
|
||||
u8 audio_get_mic_index(u8 mic_ch)
|
||||
{
|
||||
u8 i = 0;
|
||||
for (i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
if (mic_ch & 0x1) {
|
||||
return i;
|
||||
}
|
||||
mic_ch >>= 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*根据mic通道值获取使用了多少个mic*/
|
||||
u8 audio_get_mic_num(u32 mic_ch)
|
||||
{
|
||||
u8 mic_num = 0;
|
||||
for (int i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
if (mic_ch & BIT(i)) {
|
||||
mic_num++;
|
||||
}
|
||||
}
|
||||
return mic_num;
|
||||
}
|
||||
|
||||
u8 audio_anc_adt_mic_ch_num_get()
|
||||
{
|
||||
#if TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN
|
||||
return adc_hdl.anc_adt_mic_ch_num;
|
||||
#else
|
||||
return 0;
|
||||
#endif/*TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN*/
|
||||
}
|
||||
|
||||
struct adc_file_cfg *audio_adc_file_get_cfg(void)
|
||||
{
|
||||
return &esco_adc_f.cfg;
|
||||
}
|
||||
|
||||
struct adc_platform_cfg *audio_adc_platform_get_cfg(void)
|
||||
{
|
||||
return &esco_adc_f.platform_cfg[0];
|
||||
}
|
||||
|
||||
u8 audio_adc_file_get_esco_mic_num(void)
|
||||
{
|
||||
u8 mic_en = 0;
|
||||
for (int i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
if (esco_adc_f.cfg.mic_en_map & BIT(i)) {
|
||||
mic_en++;
|
||||
}
|
||||
}
|
||||
return mic_en;
|
||||
}
|
||||
|
||||
u8 audio_adc_file_get_mic_en_map(void)
|
||||
{
|
||||
return esco_adc_f.cfg.mic_en_map;
|
||||
}
|
||||
|
||||
void audio_adc_file_set_mic_en_map(u8 mic_en_map)
|
||||
{
|
||||
esco_adc_f.cfg.mic_en_map = mic_en_map;
|
||||
}
|
||||
|
||||
|
||||
void audio_adc_file_global_cfg_init(void)
|
||||
{
|
||||
memcpy(&esco_adc_file_g.cfg, &esco_adc_f.cfg, sizeof(struct adc_file_cfg));
|
||||
#if TCFG_AUDIO_ANC_ENABLE && (defined CONFIG_CPU_BR28)
|
||||
if (!esco_adc_file_g.fixed_buf) {
|
||||
//JL701 ANC开启ADC必须固定buffer /中断点数
|
||||
if (esco_adc_file_g.cfg.mic_en_map & AUDIO_ADC_MIC_0) {
|
||||
esco_adc_file_g.fixed_ch_num++;
|
||||
}
|
||||
if (esco_adc_file_g.cfg.mic_en_map & AUDIO_ADC_MIC_1) {
|
||||
esco_adc_file_g.fixed_ch_num++;
|
||||
}
|
||||
if (esco_adc_file_g.cfg.mic_en_map & AUDIO_ADC_MIC_2) {
|
||||
esco_adc_file_g.fixed_ch_num++;
|
||||
}
|
||||
if (esco_adc_file_g.cfg.mic_en_map & AUDIO_ADC_MIC_3) {
|
||||
esco_adc_file_g.fixed_ch_num++;
|
||||
}
|
||||
if (!adc_hdl.hw_buf) {
|
||||
u8 adc_num = adc_hdl.max_adc_num;
|
||||
#if defined(TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN) && TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN
|
||||
adc_num = adc_hdl.max_adc_num > get_icsd_adt_mic_num() ? adc_hdl.max_adc_num : get_icsd_adt_mic_num();
|
||||
#endif /*TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN*/
|
||||
esco_adc_file_g.fixed_buf = malloc(ESCO_ADC_BUF_NUM * 256 * ((adc_hdl.bit_width == ADC_BIT_WIDTH_16) ? 2 : 4) * adc_num);
|
||||
}
|
||||
}
|
||||
#endif/*TCFG_AUDIO_ANC_ENABLE && CONFIG_CPU_BR28*/
|
||||
}
|
||||
|
||||
void audio_adc_fixed_digital_set_buffs(void)
|
||||
{
|
||||
audio_adc_mic_set_buffs(NULL, esco_adc_file_g.fixed_buf, 256 * 2, ESCO_ADC_BUF_NUM);
|
||||
audio_adc_set_buf_fix(1, &adc_hdl);
|
||||
}
|
||||
|
||||
void audio_all_adc_file_init(void)
|
||||
{
|
||||
#if 0
|
||||
int subid = 0;
|
||||
char node_name[16];
|
||||
struct adc_file_cfg adc_cfg;
|
||||
for (subid = 0; subid < 0xff; subid++) { //遍历所有adc节点来获取累计开了多少路mic
|
||||
if (jlstream_read_node_data_new(NODE_UUID_ADC, subid, &adc_cfg, node_name)) {
|
||||
adc_file_log("read adc cfg :%d,%x,%s,%x\n", __LINE__, subid, node_name, adc_cfg.mic_en_map);
|
||||
for (int i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
if (adc_cfg.mic_en_map & BIT(i)) {
|
||||
audio_adc_add_ch(&adc_hdl, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < AUDIO_ADC_MAX_NUM; i++) { //默认按最大通道开ADC 数字
|
||||
audio_adc_add_ch(&adc_hdl, i);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void audio_adc_file_init(void) //通话的ADC节点配置
|
||||
{
|
||||
u32 i;
|
||||
if (!esco_adc_f.read_flag) {
|
||||
esco_adc_f.hdl = NULL;
|
||||
/*
|
||||
*解析配置文件内效果配置
|
||||
* */
|
||||
int len;
|
||||
char mode_index = 0;
|
||||
char cfg_index = 0;//目标配置项序号
|
||||
struct cfg_info info = {0};
|
||||
if (!jlstream_read_form_node_info_base(mode_index, "esco_adc", cfg_index, &info)) {
|
||||
len = jlstream_read_form_cfg_data(&info, &esco_adc_f.cfg);
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
|
||||
if (len != sizeof(struct adc_file_cfg)) {
|
||||
printf("esco_adc_file read cfg data err !!!\n");
|
||||
/* while (1); */
|
||||
}
|
||||
memcpy(&esco_adc_f.platform_cfg, adc_platform_cfg_table, sizeof(struct adc_platform_cfg) * AUDIO_ADC_MAX_NUM);
|
||||
|
||||
adc_file_log(" %s len %d, sizeof(cfg) %d\n", __func__, len, (int)sizeof(struct adc_file_cfg));
|
||||
|
||||
#if 0
|
||||
adc_file_log(" esco_adc_f.cfg.mic_en_map = %x\n", esco_adc_f.cfg.mic_en_map);
|
||||
for (i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
adc_file_log(" esco_adc_f.cfg.param[%d].mic_gain = %d\n", i, esco_adc_f.cfg.param[i].mic_gain);
|
||||
adc_file_log(" esco_adc_f.cfg.param[%d].mic_pre_gain = %d\n", i, esco_adc_f.cfg.param[i].mic_pre_gain);
|
||||
}
|
||||
#endif
|
||||
esco_adc_f.read_flag = 1;
|
||||
for (i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
if (esco_adc_f.cfg.mic_en_map & BIT(i)) {
|
||||
audio_adc_add_ch(&adc_hdl, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* audio_all_adc_file_init(); */
|
||||
audio_adc_file_global_cfg_init();
|
||||
}
|
||||
|
||||
void audio_adc_cfg_init(struct adc_file_common *adc_f) //通话外其他ADC节点读配置
|
||||
{
|
||||
if (!adc_f->read_flag) {
|
||||
/* adc_f->hdl = NULL; */
|
||||
/*
|
||||
*解析配置文件内效果配置
|
||||
* */
|
||||
int len = sizeof(adc_f->cfg);
|
||||
char mode_index = 0;
|
||||
char cfg_index = 0;//目标配置项序号
|
||||
struct cfg_info info = {0};
|
||||
if (!jlstream_read_form_node_info_base(mode_index, adc_f->hdl->name, cfg_index, &info)) {
|
||||
jlstream_read_form_cfg_data(&info, &adc_f->cfg);
|
||||
} else {
|
||||
len = 0;
|
||||
}
|
||||
if (len != sizeof(struct adc_file_cfg)) {
|
||||
printf("adc_file read cfg data err !!!\n");
|
||||
}
|
||||
memcpy(&adc_f->platform_cfg, adc_platform_cfg_table, sizeof(struct adc_platform_cfg) * AUDIO_ADC_MAX_NUM);
|
||||
adc_file_log(" %s len %d, sizeof(cfg) %d\n", __func__, len, (int)sizeof(struct adc_file_cfg));
|
||||
u32 i;
|
||||
#if 0
|
||||
adc_file_log(" adc_f->cfg.mic_en_map = %x\n", adc_f->cfg.mic_en_map);
|
||||
for (i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
adc_file_log(" adc_f->cfg.param[%d].mic_gain = %d\n", i, adc_f->cfg.param[i].mic_gain);
|
||||
adc_file_log(" adc_f->cfg.param[%d].mic_pre_gain = %d\n", i, adc_f->cfg.param[i].mic_pre_gain);
|
||||
}
|
||||
#endif
|
||||
|
||||
adc_f->hdl->ch_num = 0;
|
||||
adc_f->read_flag = 1;
|
||||
for (i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
if (adc_f->cfg.mic_en_map & BIT(i)) {
|
||||
audio_adc_add_ch(&adc_hdl, i);
|
||||
adc_f->hdl->ch_num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void audio_adc_file_set_gain(u8 mic_index, u8 mic_gain)
|
||||
{
|
||||
if (mic_index > AUDIO_ADC_MAX_NUM) {
|
||||
adc_file_log("mic_index[%d] err !!!", mic_index);
|
||||
}
|
||||
esco_adc_f.cfg.param[mic_index].mic_gain = mic_gain;
|
||||
if (esco_adc_f.hdl) {
|
||||
audio_adc_mic_set_gain(&esco_adc_f.hdl->mic_ch, BIT(mic_index), mic_gain);
|
||||
}
|
||||
}
|
||||
|
||||
u8 audio_adc_file_get_gain(u8 mic_index)
|
||||
{
|
||||
audio_adc_file_init(); //如果获取的时候没有初始化,则跑初始化流程
|
||||
|
||||
return esco_adc_f.cfg.param[mic_index].mic_gain;
|
||||
}
|
||||
|
||||
u8 audio_adc_file_get_mic_mode(u8 mic_index)
|
||||
{
|
||||
audio_adc_file_init(); //如果获取的时候没有初始化,则跑初始化流程
|
||||
return esco_adc_f.platform_cfg[mic_index].mic_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MIC 的中断回调函数
|
||||
*
|
||||
* @param _hdl MIC 节点的操作句柄
|
||||
* @param data MIC 中断采集到的数据地址
|
||||
* @param len MIC 单个通道中断采集到的数据字节长度
|
||||
*/
|
||||
static void adc_mic_output_handler(void *_hdl, s16 *data, int len)
|
||||
{
|
||||
struct adc_file_hdl *hdl = (struct adc_file_hdl *)_hdl;
|
||||
struct stream_frame *frame;
|
||||
/* printf(">> %d %d\n", data[0], data[1]); */
|
||||
|
||||
if (hdl->dump_cnt < 10) {
|
||||
hdl->dump_cnt++;
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN) && TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN
|
||||
u8 esco_mic_num = audio_adc_file_get_esco_mic_num();
|
||||
u8 adt_mic_num = audio_anc_adt_mic_ch_num_get();
|
||||
/* 1mic通话,2mic/3mic免摘
|
||||
* 2mic通话,3mic免摘*/
|
||||
if (esco_mic_num < adt_mic_num) {
|
||||
for (int i = 0; i < len / 2; i++) {
|
||||
memcpy(&data[esco_mic_num * i], &data[adt_mic_num * i], esco_mic_num * sizeof(short));
|
||||
}
|
||||
}
|
||||
#endif /*TCFG_AUDIO_ANC_ACOUSTIC_DETECTOR_EN*/
|
||||
|
||||
frame = source_plug_get_output_frame(hdl->source_node, (len * hdl->ch_num));
|
||||
if (hdl->scene == STREAM_SCENE_ESCO ||
|
||||
hdl->scene == STREAM_SCENE_PC_MIC ||
|
||||
(hdl->scene == STREAM_SCENE_AI_VOICE)) { //cvp读dac 参考数据
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
//打开产测功能,只有算法模式,才会读dac参考数据,避免 data full
|
||||
if (cvp_dut_mode_get() == CVP_DUT_MODE_ALGORITHM) {
|
||||
audio_cvp_phase_align();
|
||||
}
|
||||
#else
|
||||
audio_cvp_phase_align();
|
||||
#endif
|
||||
}
|
||||
if (frame) {
|
||||
#ifdef TCFG_AUDIO_ADC_ENABLE_ALL_DIGITAL_CH
|
||||
//ADC DIGITAL 按最大通道数开启时使用
|
||||
if (hdl->ch_num != adc_hdl.max_adc_num) {
|
||||
#else
|
||||
if (0) {
|
||||
#endif
|
||||
/* printf("l:%d,%d,%d\n",hdl->adc_seq,hdl->ch_num,adc_hdl.max_adc_num); */
|
||||
if (adc_hdl.bit_width != ADC_BIT_WIDTH_16) {
|
||||
s32 *s32_src = (s32 *)data;
|
||||
s32 *s32_dst = (s32 *)frame->data;
|
||||
for (int i = 0; i < len / 4; i++) {
|
||||
for (int j = 0; j < hdl->ch_num; j++) {
|
||||
s32_dst[hdl->ch_num * i + j] = s32_src[adc_hdl.max_adc_num * i + hdl->adc_seq + j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s16 *s16_src = data;
|
||||
s16 *s16_dst = (s16 *)frame->data;
|
||||
for (int i = 0; i < len / 2; i++) {
|
||||
for (int j = 0; j < hdl->ch_num; j++) {
|
||||
s16_dst[hdl->ch_num * i + j] = s16_src[adc_hdl.max_adc_num * i + hdl->adc_seq + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
memcpy(frame->data, data, (len * hdl->ch_num));
|
||||
}
|
||||
len *= hdl->ch_num;
|
||||
if (audio_common_mic_mute_en_get()) { //mute ADC
|
||||
memset((u8 *)frame->data, 0x0, len);
|
||||
}
|
||||
frame->len = len;
|
||||
frame->flags = FRAME_FLAG_TIMESTAMP_ENABLE | FRAME_FLAG_PERIOD_SAMPLE | FRAME_FLAG_UPDATE_TIMESTAMP;
|
||||
frame->timestamp = audio_jiffies_usec() * TIMESTAMP_US_DENOMINATOR;
|
||||
source_plug_put_output_frame(hdl->source_node, frame);
|
||||
}
|
||||
}
|
||||
|
||||
static void *adc_init(void *source_node, struct stream_node *node)
|
||||
{
|
||||
struct adc_file_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
hdl->source_node = source_node;
|
||||
hdl->node = node;
|
||||
node->type |= NODE_TYPE_IRQ;
|
||||
|
||||
|
||||
#if ((defined TCFG_CALL_KWS_SWITCH_ENABLE) && TCFG_CALL_KWS_SWITCH_ENABLE)
|
||||
extern void smart_voice_mcu_mic_suspend();
|
||||
smart_voice_mcu_mic_suspend();
|
||||
#endif
|
||||
|
||||
return hdl;
|
||||
}
|
||||
|
||||
static void adc_ioc_get_fmt(struct adc_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
fmt->coding_type = AUDIO_CODING_PCM;
|
||||
|
||||
switch (hdl->scene) {
|
||||
case STREAM_SCENE_ESCO:
|
||||
#if SUPPORT_CHAGE_AUDIO_CLK
|
||||
fmt->sample_rate = audio_common_clock_get() ? 17778 : 16000;
|
||||
#else
|
||||
fmt->sample_rate = 16000;
|
||||
#endif
|
||||
fmt->channel_mode = AUDIO_CH_MIX;
|
||||
break;
|
||||
#if TCFG_USB_SLAVE_AUDIO_MIC_ENABLE
|
||||
case STREAM_SCENE_PC_MIC:
|
||||
fmt->sample_rate = pc_mic_get_fmt_sample_rate();
|
||||
fmt->channel_mode = AUDIO_CH_MIX;
|
||||
break;
|
||||
#endif
|
||||
case STREAM_SCENE_HEARING_AID:
|
||||
#ifdef TCFG_AUDIO_HEARING_AID_SAMPLE_RATE
|
||||
fmt->sample_rate = TCFG_AUDIO_HEARING_AID_SAMPLE_RATE;
|
||||
#else
|
||||
fmt->sample_rate = 44100;
|
||||
#endif
|
||||
fmt->channel_mode = (hdl->ch_num == 2) ? AUDIO_CH_LR : AUDIO_CH_MIX ;
|
||||
break;
|
||||
default:
|
||||
#if SUPPORT_CHAGE_AUDIO_CLK
|
||||
fmt->sample_rate = audio_common_clock_get() ? 49000 : 44100;
|
||||
#else
|
||||
fmt->sample_rate = 44100;
|
||||
#endif
|
||||
fmt->channel_mode = (hdl->ch_num == 2) ? AUDIO_CH_LR : AUDIO_CH_MIX ;
|
||||
break;
|
||||
}
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
|
||||
if (adc_hdl.bit_width == ADC_BIT_WIDTH_24) {
|
||||
fmt->bit_wide = DATA_BIT_WIDE_24BIT;
|
||||
} else {
|
||||
fmt->bit_wide = DATA_BIT_WIDE_16BIT;
|
||||
}
|
||||
}
|
||||
|
||||
static int adc_ioc_set_fmt(struct adc_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_file_mic_open(struct adc_mic_ch *mic, int ch) //用于打开通话使用的mic
|
||||
{
|
||||
struct mic_open_param mic_param = {0};
|
||||
int mic_gain;
|
||||
int mic_pre_gain;
|
||||
int ch_index;
|
||||
|
||||
u32 i = 0;
|
||||
for (i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
if (ch & BIT(i)) {
|
||||
ch_index = i;
|
||||
|
||||
audio_adc_param_fill(&mic_param, &esco_adc_f.platform_cfg[ch_index]);
|
||||
mic_gain = esco_adc_f.cfg.param[ch_index].mic_gain;
|
||||
mic_pre_gain = esco_adc_f.cfg.param[ch_index].mic_pre_gain;
|
||||
|
||||
if ((mic_param.mic_bias_sel == 0) && (esco_adc_f.platform_cfg[ch_index].power_io != 0)) {
|
||||
u32 gpio = uuid2gpio(esco_adc_f.platform_cfg[ch_index].power_io);
|
||||
gpio_set_mode(IO_PORT_SPILT(gpio), PORT_OUTPUT_HIGH);
|
||||
}
|
||||
|
||||
audio_adc_mic_open(mic, AUDIO_ADC_MIC(ch_index), &adc_hdl, &mic_param);
|
||||
audio_adc_mic_set_gain(mic, AUDIO_ADC_MIC(ch_index), mic_gain);
|
||||
audio_adc_mic_gain_boost(AUDIO_ADC_MIC(ch_index), mic_pre_gain);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int adc_file_cfg_mic_open(struct adc_mic_ch *mic, int ch, struct adc_file_common *adc_f) //用于打开通话外其他mic
|
||||
{
|
||||
struct mic_open_param mic_param = {0};
|
||||
int mic_gain;
|
||||
int mic_pre_gain;
|
||||
int ch_index;
|
||||
|
||||
u32 i = 0;
|
||||
for (i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
if (ch & BIT(i)) {
|
||||
ch_index = i;
|
||||
|
||||
audio_adc_param_fill(&mic_param, &adc_f->platform_cfg[ch_index]);
|
||||
mic_gain = adc_f->cfg.param[ch_index].mic_gain;
|
||||
mic_pre_gain = adc_f->cfg.param[ch_index].mic_pre_gain;
|
||||
|
||||
if ((mic_param.mic_bias_sel == 0) && (adc_f->platform_cfg[ch_index].power_io != 0)) {
|
||||
u32 gpio = uuid2gpio(adc_f->platform_cfg[ch_index].power_io);
|
||||
gpio_set_mode(IO_PORT_SPILT(gpio), PORT_OUTPUT_HIGH);
|
||||
}
|
||||
|
||||
audio_adc_mic_open(mic, AUDIO_ADC_MIC(ch_index), &adc_hdl, &mic_param);
|
||||
audio_adc_mic_set_gain(mic, AUDIO_ADC_MIC(ch_index), mic_gain);
|
||||
audio_adc_mic_gain_boost(AUDIO_ADC_MIC(ch_index), mic_pre_gain);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 adc_mic_start = 0;
|
||||
|
||||
static void adc_open_task(void *_hdl)
|
||||
{
|
||||
struct adc_file_hdl *hdl = (struct adc_file_hdl *)_hdl;
|
||||
|
||||
/*y_printf("adc_mic_start\n");*/
|
||||
|
||||
audio_adc_mic_start(&hdl->mic_ch);
|
||||
adc_mic_start = 1;
|
||||
|
||||
while (1) {
|
||||
os_time_dly(0xffff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int adc_file_ioc_start(struct adc_file_hdl *hdl)
|
||||
{
|
||||
int ret = 0;
|
||||
/*
|
||||
*获取配置文件内的参数,及名字
|
||||
* */
|
||||
if (hdl->scene == STREAM_SCENE_ESCO) {
|
||||
hdl->adc_f = &esco_adc_f;
|
||||
} else if (!hdl->adc_f) {
|
||||
hdl->adc_f = zalloc(sizeof(struct adc_file_common));
|
||||
}
|
||||
hdl->adc_f->hdl = hdl;
|
||||
if (!jlstream_read_node_data_new(NODE_UUID_ADC, hdl->node->subid, (void *) & (hdl->adc_f->cfg), hdl->name)) {
|
||||
printf("%s, read node data err\n", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if TCFG_AUDIO_DUT_ENABLE
|
||||
//产测bypass 模式,MIC的使能位从产测命令读取
|
||||
if (cvp_dut_mode_get() == CVP_DUT_MODE_BYPASS) {
|
||||
hdl->adc_f->cfg.mic_en_map = cvp_dut_mic_ch_get();
|
||||
}
|
||||
#endif
|
||||
if (hdl->scene != STREAM_SCENE_ESCO) {
|
||||
audio_adc_cfg_init(hdl->adc_f);
|
||||
} else { //初始化通话通道数
|
||||
hdl->ch_num = 0;
|
||||
for (int i = 0; i < AUDIO_ADC_MIC_MAX_NUM; i++) {
|
||||
if (hdl->adc_f->cfg.mic_en_map & BIT(i)) {
|
||||
hdl->ch_num++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (jlstream_read_effects_online_param(NODE_UUID_ADC, hdl->name, &hdl->adc_f->cfg, sizeof(hdl->adc_f->cfg))) {
|
||||
adc_file_log("get adc online param\n");
|
||||
}
|
||||
if (hdl->start == 0) {
|
||||
hdl->start = 1;
|
||||
hdl->dump_cnt = 0;
|
||||
//不启动ANC动态MIC增益时,由用户自己保证ANC与通话复用的ADC增益一致
|
||||
#if TCFG_AUDIO_ANC_ENABLE && TCFG_AUDIO_DYNAMIC_ADC_GAIN
|
||||
#if (!TCFG_AUDIO_DUAL_MIC_ENABLE && (TCFG_AUDIO_ADC_MIC_CHA & AUDIO_ADC_MIC_1)) || \
|
||||
(TCFG_AUDIO_DUAL_MIC_ENABLE && (TCFG_AUDIO_DMS_MIC_MANAGE == DMS_MASTER_MIC0))
|
||||
anc_dynamic_micgain_start(hdl->adc_f->cfg.param[1].mic_gain);
|
||||
#else
|
||||
anc_dynamic_micgain_start(hdl->adc_f->cfg.param[0].mic_gain);
|
||||
#endif/*TCFG_AUDIO_DUAL_MIC_ENABLE*/
|
||||
#endif/*TCFG_AUDIO_ANC_ENABLE && TCFG_AUDIO_DYNAMIC_ADC_GAIN*/
|
||||
|
||||
//br50的LPADC需要根据采样率配置模拟部分的时钟分频,需要先设置采样率才能打开MIC
|
||||
audio_adc_mic_set_sample_rate(&hdl->mic_ch, hdl->sample_rate);
|
||||
adc_file_cfg_mic_open(&hdl->mic_ch, hdl->adc_f->cfg.mic_en_map, (void *)hdl->adc_f);
|
||||
|
||||
if (esco_adc_file_g.fixed_buf) {
|
||||
hdl->adc_buf = esco_adc_file_g.fixed_buf;
|
||||
audio_adc_set_buf_fix(1, &adc_hdl);
|
||||
}
|
||||
if (!hdl->adc_buf && !adc_hdl.hw_buf) { //避免没有设置ADC的中断点数,以及数据流stop之后重新start申请buffer
|
||||
if (!hdl->irq_points) {
|
||||
hdl->irq_points = 256;
|
||||
}
|
||||
hdl->adc_buf = zalloc(ESCO_ADC_BUF_NUM * hdl->irq_points * ((adc_hdl.bit_width == ADC_BIT_WIDTH_16) ? 2 : 4) * (adc_hdl.max_adc_num));
|
||||
if (!hdl->adc_buf) {
|
||||
ret = -1;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = audio_adc_mic_set_buffs(&hdl->mic_ch, hdl->adc_buf, hdl->irq_points * 2, ESCO_ADC_BUF_NUM);
|
||||
if (ret && hdl->adc_buf) {
|
||||
free(hdl->adc_buf);
|
||||
hdl->adc_buf = NULL;
|
||||
}
|
||||
|
||||
hdl->adc_output.priv = hdl;
|
||||
hdl->adc_output.handler = adc_mic_output_handler;
|
||||
audio_adc_add_output_handler(&adc_hdl, &hdl->adc_output);
|
||||
#ifdef CONFIG_CPU_BR36
|
||||
/*r_printf("-----create_adc_open_task\n");*/
|
||||
adc_mic_start = 0;
|
||||
int err = os_task_create(adc_open_task, hdl, 2, 512, 0, "adc_open");
|
||||
if (err) {
|
||||
adc_mic_start = 1;
|
||||
audio_adc_mic_start(&hdl->mic_ch);
|
||||
}
|
||||
#else
|
||||
audio_adc_mic_start(&hdl->mic_ch);
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc_file_ioc_stop(struct adc_file_hdl *hdl)
|
||||
{
|
||||
if (hdl->start) {
|
||||
hdl->start = 0;
|
||||
#if TCFG_AUDIO_ANC_ENABLE && TCFG_AUDIO_DYNAMIC_ADC_GAIN
|
||||
anc_dynamic_micgain_stop();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CPU_BR36
|
||||
while (adc_mic_start == 0) {
|
||||
os_time_dly(1);
|
||||
/*puts("--wait\n");*/
|
||||
}
|
||||
/*r_printf("-----del_adc_open_task\n");*/
|
||||
os_task_del("adc_open");
|
||||
#endif
|
||||
audio_adc_mic_close(&hdl->mic_ch);
|
||||
if (!esco_adc_file_g.fixed_buf) {
|
||||
hdl->adc_buf = NULL; //在adc 驱动中释放了这个buffer
|
||||
}
|
||||
audio_adc_del_output_handler(&adc_hdl, &hdl->adc_output);
|
||||
|
||||
for (u32 i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
if (hdl->adc_f->cfg.mic_en_map & BIT(i)) {
|
||||
if ((hdl->adc_f->platform_cfg[i].mic_bias_sel == 0) && (hdl->adc_f->platform_cfg[i].power_io != 0)) {
|
||||
if (!audio_adc_is_active()) {
|
||||
u32 gpio = uuid2gpio(hdl->adc_f->platform_cfg[i].power_io);
|
||||
gpio_set_mode(IO_PORT_SPILT(gpio), PORT_OUTPUT_LOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adc_file_ioc_update_parm(struct adc_file_hdl *hdl, int parm)
|
||||
{
|
||||
int i;
|
||||
int ret = false;
|
||||
struct adc_file_cfg *cfg = (struct adc_file_cfg *)parm;
|
||||
if (hdl) {
|
||||
for (i = 0; i < AUDIO_ADC_MAX_NUM; i++) {
|
||||
if (hdl->adc_f->cfg.mic_en_map & BIT(i)) {
|
||||
//目前仅支持更新增益
|
||||
audio_adc_mic_set_gain(&hdl->mic_ch, BIT(i), cfg->param[i].mic_gain);
|
||||
audio_adc_mic_gain_boost(BIT(i), cfg->param[i].mic_pre_gain);
|
||||
}
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc_ioctl(void *_hdl, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct adc_file_hdl *hdl = (struct adc_file_hdl *)_hdl;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_GET_FMT:
|
||||
adc_ioc_get_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
ret = adc_ioc_set_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
hdl->scene = arg;
|
||||
break;
|
||||
case NODE_IOC_SET_PRIV_FMT:
|
||||
hdl->irq_points = arg;
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
ret = adc_file_ioc_start(hdl);
|
||||
hdl->adc_seq = get_adc_seq(&adc_hdl, hdl->adc_f->cfg.mic_en_map); //查询模拟mic对应的ADC通道
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
adc_file_ioc_stop(hdl);
|
||||
break;
|
||||
case NODE_IOC_SET_PARAM:
|
||||
ret = adc_file_ioc_update_parm(hdl, arg);
|
||||
break;
|
||||
case NODE_IOC_NODE_CONFIG:
|
||||
hdl->ch_num = (u8)arg;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void adc_release(void *_hdl)
|
||||
{
|
||||
struct adc_file_hdl *hdl = (struct adc_file_hdl *)_hdl;
|
||||
if (hdl->adc_f && hdl->scene != STREAM_SCENE_ESCO) {
|
||||
free(hdl->adc_f);
|
||||
hdl->adc_f = NULL;
|
||||
}
|
||||
free(hdl);
|
||||
esco_adc_f.hdl = NULL;
|
||||
}
|
||||
|
||||
|
||||
REGISTER_SOURCE_NODE_PLUG(adc_file_plug) = {
|
||||
.uuid = NODE_UUID_ADC,
|
||||
.init = adc_init,
|
||||
.ioctl = adc_ioctl,
|
||||
.release = adc_release,
|
||||
};
|
||||
|
||||
REGISTER_ONLINE_ADJUST_TARGET(adc) = {
|
||||
.uuid = NODE_UUID_ADC,
|
||||
};
|
||||
@@ -0,0 +1,233 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".esco_file.data.bss")
|
||||
#pragma data_seg(".esco_file.data")
|
||||
#pragma const_seg(".esco_file.text.const")
|
||||
#pragma code_seg(".esco_file.text")
|
||||
#endif
|
||||
#include "classic/hci_lmp.h"
|
||||
#include "source_node.h"
|
||||
#include "sync/audio_syncts.h"
|
||||
#include "media/bt_audio_timestamp.h"
|
||||
#include "reference_time.h"
|
||||
#include "app_config.h"
|
||||
|
||||
#if TCFG_USER_BT_CLASSIC_ENABLE
|
||||
|
||||
struct esco_file_hdl {
|
||||
u8 start;
|
||||
u8 stop;
|
||||
u8 esco_len;
|
||||
u8 first_timestamp;
|
||||
u8 frame_time;
|
||||
u8 reference;
|
||||
u32 clkn;
|
||||
u32 coding_type;
|
||||
struct stream_node *node;
|
||||
void *ts_handle;
|
||||
u8 bt_addr[6];
|
||||
};
|
||||
|
||||
static void esco_file_timestamp_setup(struct esco_file_hdl *hdl);
|
||||
static void esco_frame_pack_timestamp(struct esco_file_hdl *hdl, struct stream_frame *frame, u32 clkn)
|
||||
{
|
||||
u32 frame_clkn = clkn;
|
||||
if (!hdl->ts_handle) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hdl->first_timestamp) {
|
||||
hdl->first_timestamp = 0;
|
||||
} else {
|
||||
if (((frame_clkn - hdl->clkn) & 0x7fffff) != hdl->frame_time) {
|
||||
printf("frame_clkn =%d ,clkn %d %d\n", frame_clkn, ((frame_clkn - hdl->clkn) & 0x7fffff), hdl->frame_time);
|
||||
frame_clkn = hdl->clkn + hdl->frame_time;
|
||||
}
|
||||
}
|
||||
frame->timestamp = esco_audio_timestamp_update(hdl->ts_handle, frame_clkn);
|
||||
frame->flags |= FRAME_FLAG_TIMESTAMP_ENABLE | FRAME_FLAG_UPDATE_TIMESTAMP;
|
||||
hdl->clkn = clkn;
|
||||
}
|
||||
|
||||
|
||||
static void esco_start_abandon_data(struct esco_file_hdl *hdl)
|
||||
{
|
||||
lmp_private_esco_suspend_resume(1);
|
||||
}
|
||||
|
||||
static void esco_stop_abandon_data(struct esco_file_hdl *hdl)
|
||||
{
|
||||
lmp_private_esco_suspend_resume(2);
|
||||
}
|
||||
|
||||
static void esco_packet_rx_notify(void *_hdl)
|
||||
{
|
||||
struct esco_file_hdl *hdl = (struct esco_file_hdl *)_hdl;
|
||||
jlstream_wakeup_thread(NULL, hdl->node, NULL);
|
||||
}
|
||||
|
||||
static enum stream_node_state esco_get_frame(void *_hdl, struct stream_frame **_frame)
|
||||
{
|
||||
int len = 0;
|
||||
u32 frame_clkn = 0;
|
||||
struct esco_file_hdl *hdl = (struct esco_file_hdl *)_hdl;
|
||||
struct stream_frame *frame;
|
||||
|
||||
u8 *packet = lmp_private_get_esco_packet(&len, &frame_clkn);
|
||||
if (!packet) {
|
||||
*_frame = NULL;
|
||||
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
||||
}
|
||||
frame = jlstream_get_frame(hdl->node->oport, len);
|
||||
frame->len = len;
|
||||
if (hdl->frame_time == 0xff) {
|
||||
/*对SCO链路获取不到T_sco的容错*/
|
||||
hdl->frame_time = len >= 60 ? 12 : 6;
|
||||
}
|
||||
memcpy(frame->data, packet, len);
|
||||
esco_frame_pack_timestamp(hdl, frame, frame_clkn);
|
||||
lmp_private_free_esco_packet(packet);
|
||||
|
||||
*_frame = frame;
|
||||
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
|
||||
static void *esco_init(void *priv, struct stream_node *node)
|
||||
{
|
||||
struct esco_file_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
hdl->node = node;
|
||||
node->type |= NODE_TYPE_IRQ;
|
||||
return hdl;
|
||||
}
|
||||
|
||||
static int esco_ioc_set_bt_addr(struct esco_file_hdl *hdl, u8 *bt_addr)
|
||||
{
|
||||
memcpy(hdl->bt_addr, bt_addr, 6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void esco_ioc_get_fmt(struct esco_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
int type = lmp_private_get_esco_packet_type();
|
||||
int media_type = type & 0xff;
|
||||
if (media_type == 0) {
|
||||
fmt->sample_rate = 8000;
|
||||
fmt->coding_type = AUDIO_CODING_CVSD;
|
||||
} else {
|
||||
fmt->sample_rate = 16000;
|
||||
fmt->coding_type = AUDIO_CODING_MSBC;
|
||||
}
|
||||
fmt->channel_mode = AUDIO_CH_MIX;
|
||||
hdl->coding_type = fmt->coding_type;
|
||||
}
|
||||
|
||||
static void esco_ioc_stop(struct esco_file_hdl *hdl)
|
||||
{
|
||||
hdl->stop = 1;
|
||||
lmp_esco_set_rx_notify(hdl->bt_addr, NULL, NULL);
|
||||
}
|
||||
|
||||
void esco_ts_handle_create(struct esco_file_hdl *hdl)
|
||||
{
|
||||
if (!hdl) {
|
||||
return;
|
||||
}
|
||||
#define ESCO_DELAY_TIME 60
|
||||
#define ESCO_RECOGNTION_TIME 220
|
||||
|
||||
int delay_time = ESCO_DELAY_TIME;
|
||||
/* if (get_sniff_out_status()) { */
|
||||
/* clear_sniff_out_status(); */
|
||||
/* if (ESCO_SIRI_WAKEUP()) { */
|
||||
/* [>fix : Siri出sniff蓝牙数据到音频通路延迟过长,容易引入同步的问题<] */
|
||||
/* delay_time = ESCO_RECOGNTION_TIME; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
if (!hdl->ts_handle) {
|
||||
hdl->reference = audio_reference_clock_select(hdl->bt_addr, 0);//0 - a2dp主机,1 - tws, 2 - BLE
|
||||
|
||||
hdl->frame_time = (lmp_private_get_esco_packet_type() >> 8) & 0xff;
|
||||
|
||||
hdl->ts_handle = esco_audio_timestamp_create(hdl->frame_time, delay_time, TIME_US_FACTOR);
|
||||
}
|
||||
hdl->first_timestamp = 1;
|
||||
}
|
||||
|
||||
void esco_ts_handle_release(struct esco_file_hdl *hdl)
|
||||
{
|
||||
if (!hdl) {
|
||||
return;
|
||||
}
|
||||
if (hdl->ts_handle) {
|
||||
esco_audio_timestamp_close(hdl->ts_handle);
|
||||
hdl->ts_handle = NULL;
|
||||
audio_reference_clock_exit(hdl->reference);
|
||||
}
|
||||
}
|
||||
|
||||
static void esco_file_timestamp_setup(struct esco_file_hdl *hdl)
|
||||
{
|
||||
int err = stream_node_ioctl(hdl->node, NODE_UUID_BT_AUDIO_SYNC, NODE_IOC_SYNCTS, 0);
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
esco_ts_handle_create(hdl);
|
||||
}
|
||||
|
||||
static void esco_file_timestamp_close(struct esco_file_hdl *hdl)
|
||||
{
|
||||
esco_ts_handle_release(hdl);
|
||||
}
|
||||
|
||||
|
||||
static int esco_ioctl(void *_hdl, int cmd, int arg)
|
||||
{
|
||||
struct esco_file_hdl *hdl = (struct esco_file_hdl *)_hdl;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_SET_BTADDR:
|
||||
esco_ioc_set_bt_addr(hdl, (u8 *)arg);
|
||||
break;
|
||||
case NODE_IOC_GET_FMT:
|
||||
esco_ioc_get_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
lmp_esco_set_rx_notify(hdl->bt_addr, NULL, NULL);
|
||||
esco_start_abandon_data(hdl);
|
||||
esco_file_timestamp_close(hdl);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
esco_stop_abandon_data(hdl);
|
||||
lmp_esco_set_rx_notify(hdl->bt_addr, hdl, esco_packet_rx_notify);
|
||||
esco_file_timestamp_setup(hdl);
|
||||
break;
|
||||
case NODE_IOC_STOP:
|
||||
esco_ioc_stop(hdl);
|
||||
esco_file_timestamp_close(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void esco_release(void *_hdl)
|
||||
{
|
||||
struct esco_file_hdl *hdl = (struct esco_file_hdl *)_hdl;
|
||||
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
|
||||
REGISTER_SOURCE_NODE_PLUG(esco_file_plug) = {
|
||||
.uuid = NODE_UUID_ESCO_RX,
|
||||
.init = esco_init,
|
||||
.get_frame = esco_get_frame,
|
||||
.ioctl = esco_ioctl,
|
||||
.release = esco_release,
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* #if TCFG_USER_BT_CLASSIC_ENABLE */
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".linein_file.data.bss")
|
||||
#pragma data_seg(".linein_file.data")
|
||||
#pragma const_seg(".linein_file.text.const")
|
||||
#pragma code_seg(".linein_file.text")
|
||||
#endif
|
||||
#include "source_node.h"
|
||||
#include "asm/audio_adc.h"
|
||||
#include "media/audio_splicing.h"
|
||||
#include "linein_file.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "sync/audio_clk_sync.h"
|
||||
|
||||
#if 1
|
||||
#define adc_file_log printf
|
||||
#else
|
||||
#define adc_file_log(...)
|
||||
#endif/*log_en*/
|
||||
|
||||
extern struct audio_adc_hdl adc_hdl;
|
||||
extern const struct adc_platform_cfg adc_platform_cfg_table[AUDIO_ADC_MAX_NUM];
|
||||
|
||||
#define LINEIN_ADC_BUF_NUM 2 //linein_adc采样buf个数
|
||||
|
||||
static struct linein_file_cfg linein_cfg_g;
|
||||
|
||||
int adc_file_linein_open(struct adc_linein_ch *linein, int ch)
|
||||
{
|
||||
int ch_index = 0;
|
||||
int linein_gain;
|
||||
int linein_pre_gain;
|
||||
const struct adc_platform_cfg *cfg_table = adc_platform_cfg_table;
|
||||
struct linein_open_param linein_param = {0};
|
||||
|
||||
for (int i = 0; i < AUDIO_ADC_LINEIN_MAX_NUM; i++) {
|
||||
if (ch == BIT(i)) {
|
||||
ch_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
audio_linein_param_fill(&linein_param, &cfg_table[ch_index]);
|
||||
linein_gain = linein_cfg_g.param[ch_index].mic_gain;
|
||||
linein_pre_gain = linein_cfg_g.param[ch_index].mic_pre_gain;
|
||||
printf(">> linein %d, linein_ain_sel %d, linein_mode %d, linein_dcc %d, linein_gain %d, linein_pre_gain %d", ch_index, linein_param.linein_ain_sel, linein_param.linein_mode, linein_param.linein_dcc, linein_gain, linein_pre_gain);
|
||||
audio_adc_linein_open(linein, ch, &adc_hdl, &linein_param);
|
||||
audio_adc_linein_set_gain(linein, ch, linein_gain);
|
||||
audio_adc_linein_gain_boost(ch, linein_pre_gain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if TCFG_AUDIO_LINEIN_ENABLE
|
||||
|
||||
|
||||
void audio_linein_file_init()
|
||||
{
|
||||
char mode_index = 0;
|
||||
char cfg_index = 0;//目标配置项序号
|
||||
int len = jlstream_read_form_data(mode_index, "linein_adc", cfg_index, &linein_cfg_g);
|
||||
if (len != sizeof(struct linein_file_cfg)) {
|
||||
printf("linein_file read cfg data err !!!\n");
|
||||
}
|
||||
adc_file_log(" %s len %d, sizeof(cfg) %d\n", __func__, len, (int)sizeof(struct linein_file_cfg));
|
||||
#if 1
|
||||
adc_file_log(" linein_cfg_g.mic_en_map = %x\n", linein_cfg_g.mic_en_map);
|
||||
for (int i = 0; i < AUDIO_ADC_LINEIN_MAX_NUM; i++) {
|
||||
adc_file_log(" linein_cfg_g.param[%d].mic_gain = %d\n", i, linein_cfg_g.param[i].mic_gain);
|
||||
adc_file_log(" linein_cfg_g.param[%d].mic_pre_gain = %d\n", i, linein_cfg_g.param[i].mic_pre_gain);
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < AUDIO_ADC_LINEIN_MAX_NUM; i++) {
|
||||
if (linein_cfg_g.mic_en_map & BIT(i)) {
|
||||
audio_adc_add_ch(&adc_hdl, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief LINEIN 的中断回调函数
|
||||
*
|
||||
* @param _hdl LINEIN 节点的操作句柄
|
||||
* @param data LINEIN 中断采集到的数据地址
|
||||
* @param len LINEIN 单个通道中断采集到的数据字节长度
|
||||
*/
|
||||
static void adc_linein_output_handler(void *_hdl, s16 *data, int len)
|
||||
{
|
||||
|
||||
struct linein_file_hdl *hdl = (struct linein_file_hdl *)_hdl;
|
||||
struct stream_frame *frame;
|
||||
|
||||
if (hdl->dump_cnt < 10) {
|
||||
hdl->dump_cnt++;
|
||||
return;
|
||||
}
|
||||
frame = source_plug_get_output_frame(hdl->source_node, (len * hdl->ch_num));
|
||||
if (frame) {
|
||||
if (hdl->ch_num != adc_hdl.max_adc_num) {
|
||||
//printf("l:%d,%d,%d\n",hdl->adc_seq,hdl->ch_num,adc_hdl.max_adc_num);
|
||||
if (adc_hdl.bit_width != ADC_BIT_WIDTH_16) {
|
||||
s32 *s32_src = (s32 *)data;
|
||||
s32 *s32_dst = (s32 *)frame->data;
|
||||
for (int i = 0; i < len / 4; i++) {
|
||||
for (int j = 0; j < hdl->ch_num; j++) {
|
||||
s32_dst[hdl->ch_num * i + j] = s32_src[adc_hdl.max_adc_num * i + hdl->adc_seq + j];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s16 *s16_src = data;
|
||||
s16 *s16_dst = (s16 *)frame->data;
|
||||
for (int i = 0; i < len / 2; i++) {
|
||||
for (int j = 0; j < hdl->ch_num; j++) {
|
||||
s16_dst[hdl->ch_num * i + j] = s16_src[adc_hdl.max_adc_num * i + hdl->adc_seq + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
memcpy(frame->data, data, (len * hdl->ch_num));
|
||||
}
|
||||
len *= hdl->ch_num;
|
||||
if (hdl->mute_en) { //mute ADC
|
||||
memset((u8 *)frame->data, 0x0, len);
|
||||
}
|
||||
if (hdl->output_fade_in) {
|
||||
if (adc_hdl.bit_width == ADC_BIT_WIDTH_16) {
|
||||
s16 tmp_data;
|
||||
s16 *tmp_data_ptr = (s16 *)frame->data;
|
||||
/* printf("fade:%d\n",hdl->output_fade_in_gain); */
|
||||
for (int i = 0; i < len / 2; i++) {
|
||||
tmp_data = tmp_data_ptr[i];
|
||||
tmp_data_ptr[i] = tmp_data * hdl->output_fade_in_gain >> 8;
|
||||
}
|
||||
hdl->output_fade_in_gain += 1;
|
||||
if (hdl->output_fade_in_gain >= 256) {
|
||||
hdl->output_fade_in = 0;
|
||||
}
|
||||
} else {
|
||||
s32 tmp_data;
|
||||
s32 *tmp_data_ptr = (s32 *)frame->data;
|
||||
/* printf("fade:%d\n",hdl->output_fade_in_gain); */
|
||||
for (int i = 0; i < len / 4; i++) {
|
||||
tmp_data = tmp_data_ptr[i];
|
||||
tmp_data_ptr[i] = tmp_data * hdl->output_fade_in_gain >> 8;
|
||||
}
|
||||
hdl->output_fade_in_gain += 1;
|
||||
if (hdl->output_fade_in_gain >= 256) {
|
||||
hdl->output_fade_in = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
frame->len = len;
|
||||
frame->flags = FRAME_FLAG_TIMESTAMP_ENABLE | FRAME_FLAG_PERIOD_SAMPLE | FRAME_FLAG_UPDATE_TIMESTAMP;
|
||||
frame->timestamp = audio_jiffies_usec() * TIMESTAMP_US_DENOMINATOR;
|
||||
source_plug_put_output_frame(hdl->source_node, frame);
|
||||
}
|
||||
}
|
||||
|
||||
static void *linein_init(void *source_node, struct stream_node *node)
|
||||
{
|
||||
struct linein_file_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
hdl->source_node = source_node;
|
||||
node->type |= NODE_TYPE_IRQ;
|
||||
|
||||
for (int i = 0; i < AUDIO_ADC_LINEIN_MAX_NUM; i++) {
|
||||
if (linein_cfg_g.mic_en_map & BIT(i)) {
|
||||
hdl->ch_num++;
|
||||
}
|
||||
}
|
||||
|
||||
adc_file_log("adc ch_num %d\n", hdl->ch_num);
|
||||
return hdl;
|
||||
}
|
||||
|
||||
static void adc_ioc_get_fmt(struct linein_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
fmt->coding_type = AUDIO_CODING_PCM;
|
||||
switch (hdl->ch_num) {
|
||||
case 2:
|
||||
fmt->sample_rate = 44100;
|
||||
fmt->channel_mode = AUDIO_CH_LR;
|
||||
break;
|
||||
default:
|
||||
fmt->sample_rate = 44100;
|
||||
fmt->channel_mode = AUDIO_CH_L;
|
||||
break;
|
||||
}
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
|
||||
if (adc_hdl.bit_width == ADC_BIT_WIDTH_24) {
|
||||
fmt->bit_wide = DATA_BIT_WIDE_24BIT;
|
||||
} else {
|
||||
fmt->bit_wide = DATA_BIT_WIDE_16BIT;
|
||||
}
|
||||
}
|
||||
|
||||
static int adc_ioc_set_fmt(struct linein_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
hdl->sample_rate = fmt->sample_rate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int linein_file_ioc_update_parm(struct linein_file_hdl *hdl, int parm)
|
||||
{
|
||||
int ret = false;
|
||||
struct linein_file_cfg *cfg = (struct linein_file_cfg *)parm;
|
||||
if (hdl) {
|
||||
for (int i = 0; i < AUDIO_ADC_LINEIN_MAX_NUM; i++) {
|
||||
if (cfg->mic_en_map & BIT(i)) {
|
||||
audio_adc_linein_set_gain(&hdl->linein_ch, BIT(i), cfg->param[i].mic_gain);
|
||||
audio_adc_linein_gain_boost(BIT(i), cfg->param[i].mic_pre_gain);
|
||||
}
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int linein_ioctl(void *_hdl, int cmd, int arg)
|
||||
{
|
||||
int ret = 0;
|
||||
struct linein_file_hdl *hdl = (struct linein_file_hdl *)_hdl;
|
||||
switch (cmd) {
|
||||
case NODE_IOC_GET_FMT:
|
||||
adc_ioc_get_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
ret = adc_ioc_set_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_PRIV_FMT:
|
||||
hdl->irq_points = arg;
|
||||
if (!adc_hdl.hw_buf) {
|
||||
hdl->adc_buf = zalloc(LINEIN_ADC_BUF_NUM * hdl->irq_points * ((adc_hdl.bit_width == ADC_BIT_WIDTH_16) ? 2 : 4) * adc_hdl.max_adc_num);
|
||||
}
|
||||
if (!hdl->adc_buf) {
|
||||
ret = -1;
|
||||
}
|
||||
adc_file_log("adc_buf points %d\n", hdl->irq_points);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
if (hdl->start == 0) {
|
||||
hdl->output_fade_in = 1;
|
||||
hdl->start = 1;
|
||||
hdl->dump_cnt = 0;
|
||||
int linein_en_map = 0;
|
||||
for (int i = 0; i < AUDIO_ADC_LINEIN_MAX_NUM; i++) {
|
||||
if (linein_cfg_g.mic_en_map & BIT(i)) {
|
||||
adc_file_linein_open(&hdl->linein_ch, BIT(i));
|
||||
linein_en_map |= BIT(i);
|
||||
}
|
||||
}
|
||||
hdl->adc_seq = get_adc_seq(&adc_hdl, linein_en_map); //查询模拟linein对应的ADC通道
|
||||
if (!hdl->adc_buf && !adc_hdl.hw_buf) { //避免没有设置ADC的中断点数,以及数据流stop之后重新start申请buffer
|
||||
if (!hdl->irq_points) {
|
||||
hdl->irq_points = 256;
|
||||
}
|
||||
hdl->adc_buf = zalloc(LINEIN_ADC_BUF_NUM * hdl->irq_points * ((adc_hdl.bit_width == ADC_BIT_WIDTH_16) ? 2 : 4) * (adc_hdl.max_adc_num));
|
||||
if (!hdl->adc_buf) {
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = audio_adc_linein_set_buffs(&hdl->linein_ch, hdl->adc_buf, hdl->irq_points * 2, LINEIN_ADC_BUF_NUM);
|
||||
if (ret && hdl->adc_buf) {
|
||||
free(hdl->adc_buf);
|
||||
hdl->adc_buf = NULL;
|
||||
}
|
||||
audio_adc_linein_set_sample_rate(&hdl->linein_ch, hdl->sample_rate);
|
||||
|
||||
hdl->adc_output.priv = hdl;
|
||||
hdl->adc_output.handler = adc_linein_output_handler;
|
||||
audio_adc_add_output_handler(&adc_hdl, &hdl->adc_output);
|
||||
|
||||
audio_adc_linein_start(&hdl->linein_ch);
|
||||
}
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
if (hdl->start) {
|
||||
hdl->start = 0;
|
||||
audio_adc_linein_close(&hdl->linein_ch);
|
||||
if (!adc_hdl.hw_buf) {
|
||||
hdl->adc_buf = NULL; //在adc 驱动中释放了这个buffer
|
||||
}
|
||||
audio_adc_del_output_handler(&adc_hdl, &hdl->adc_output);
|
||||
}
|
||||
break;
|
||||
case NODE_IOC_SET_PARAM:
|
||||
ret = linein_file_ioc_update_parm(hdl, arg);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void linein_release(void *_hdl)
|
||||
{
|
||||
struct linein_file_hdl *hdl = (struct linein_file_hdl *)_hdl;
|
||||
if (hdl->adc_buf) {
|
||||
/* free(hdl->adc_buf); */ //由adc 驱动释放buffer
|
||||
}
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
|
||||
REGISTER_SOURCE_NODE_PLUG(linein_file_plug) = {
|
||||
.uuid = NODE_UUID_LINEIN,
|
||||
.init = linein_init,
|
||||
.ioctl = linein_ioctl,
|
||||
.release = linein_release,
|
||||
};
|
||||
|
||||
REGISTER_ONLINE_ADJUST_TARGET(linein) = {
|
||||
.uuid = NODE_UUID_LINEIN,
|
||||
};
|
||||
#endif/*TCFG_AUDIO_LINEIN_ENABLE*/
|
||||
@@ -0,0 +1,377 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".pc_spk_file.data.bss")
|
||||
#pragma data_seg(".pc_spk_file.data")
|
||||
#pragma const_seg(".pc_spk_file.text.const")
|
||||
#pragma code_seg(".pc_spk_file.text")
|
||||
#endif
|
||||
#include "source_node.h"
|
||||
#include "media/audio_splicing.h"
|
||||
#include "audio_config.h"
|
||||
#include "jlstream.h"
|
||||
#include "pc_spk_file.h"
|
||||
#include "app_config.h"
|
||||
#include "effects/effects_adj.h"
|
||||
#include "gpio_config.h"
|
||||
#include "sync/audio_clk_sync.h"
|
||||
#include "clock_manager/clock_manager.h"
|
||||
#include "spinlock.h"
|
||||
#include "circular_buf.h"
|
||||
#include "pc_spk_player.h"
|
||||
#include "uac_stream.h"
|
||||
#if (LEA_BIG_CTRLER_TX_EN || LEA_BIG_CTRLER_RX_EN)
|
||||
#include "le_broadcast.h"
|
||||
#include "app_le_broadcast.h"
|
||||
#endif
|
||||
#if (TCFG_LE_AUDIO_APP_CONFIG & (LE_AUDIO_AURACAST_SOURCE_EN | LE_AUDIO_AURACAST_SINK_EN))
|
||||
#include "app_le_auracast.h"
|
||||
#endif
|
||||
|
||||
#define LOG_TAG_CONST USB
|
||||
#define LOG_TAG "[pcspk]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
/* #define LOG_INFO_ENABLE */
|
||||
/* #define LOG_DUMP_ENABLE */
|
||||
#define LOG_CLI_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
#if TCFG_USB_SLAVE_AUDIO_SPK_ENABLE
|
||||
|
||||
/* #define PC_SPK_CACHE_BUF_LEN (1024 * 2) */
|
||||
//足够缓存20ms的数据
|
||||
#define PC_SPK_CACHE_BUF_LEN ((SPK_AUDIO_RATE/1000) * 4 * 20)
|
||||
|
||||
/* PC SPK 在线检测 */
|
||||
#define PC_SPK_ONLINE_DET_EN 1
|
||||
#define PC_SPK_ONLINE_DET_TIME 3 //50->20
|
||||
#define SPK_PUSH_FRAME_NUM 5 //SPK一次push的帧数,单位:uac rx中断间隔
|
||||
|
||||
struct pc_spk_file_hdl {
|
||||
void *source_node;
|
||||
struct stream_node *node;
|
||||
struct stream_snode *snode;
|
||||
cbuffer_t spk_cache_cbuffer;
|
||||
#if PC_SPK_ONLINE_DET_EN
|
||||
u32 irq_cnt; //进中断++,用来给定时器判断是否中断没有起
|
||||
u16 det_timer_id;
|
||||
#endif
|
||||
int sr;
|
||||
u8 *cache_buf;
|
||||
u8 start;
|
||||
u8 data_run;
|
||||
u32 timestamp;
|
||||
};
|
||||
static struct pc_spk_file_hdl *pc_spk = NULL;
|
||||
|
||||
struct pc_spk_fmt_t {
|
||||
u8 init;
|
||||
u8 channel;
|
||||
u8 bit;
|
||||
u32 sample_rate;
|
||||
};
|
||||
struct pc_spk_fmt_t pc_spk_fmt = {
|
||||
.init = 0,
|
||||
.channel = SPK_CHANNEL,
|
||||
.bit = SPK_AUDIO_RES,
|
||||
.sample_rate = SPK_AUDIO_RATE,
|
||||
};
|
||||
|
||||
|
||||
static DEFINE_SPINLOCK(pc_spk_lock);
|
||||
|
||||
void pc_spk_data_isr_cb(void *buf, u32 len)
|
||||
{
|
||||
struct pc_spk_file_hdl *hdl = pc_spk;
|
||||
struct stream_frame *frame = NULL;
|
||||
|
||||
int wlen = 0;
|
||||
|
||||
if (!hdl) {
|
||||
#if TCFG_KBOX_1T3_MODE_EN
|
||||
if (pc_spk_player_runing() == 0) {
|
||||
//打开播放器
|
||||
pcspk_open_player_by_taskq();
|
||||
}
|
||||
#else
|
||||
#if (LEA_BIG_CTRLER_TX_EN || LEA_BIG_CTRLER_RX_EN)
|
||||
if (!get_broadcast_role()) {
|
||||
if (pc_spk_player_runing() == 0) {
|
||||
//打开播放器
|
||||
pcspk_open_player_by_taskq();
|
||||
}
|
||||
} else {
|
||||
pc_mode_broadcast_deal_by_taskq(LE_AUDIO_MUSIC_START);
|
||||
}
|
||||
#elif (TCFG_LE_AUDIO_APP_CONFIG & (LE_AUDIO_AURACAST_SOURCE_EN | LE_AUDIO_AURACAST_SINK_EN))
|
||||
if (!get_auracast_role()) {
|
||||
if (pc_spk_player_runing() == 0) {
|
||||
//打开播放器
|
||||
pcspk_open_player_by_taskq();
|
||||
}
|
||||
} else {
|
||||
pc_mode_broadcast_deal_by_taskq(LE_AUDIO_MUSIC_START);
|
||||
}
|
||||
#else
|
||||
if (pc_spk_player_runing() == 0) {
|
||||
//打开播放器
|
||||
pcspk_open_player_by_taskq();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hdl->start || !len) { //增加0长帧的过滤,避免引起后续节点的写异常
|
||||
return;
|
||||
}
|
||||
struct stream_node *source_node = hdl->source_node;
|
||||
if (!hdl->cache_buf) {
|
||||
int cache_buf_len = len * SPK_PUSH_FRAME_NUM * 4; //4块输出buf
|
||||
//申请cbuffer
|
||||
hdl->cache_buf = zalloc(cache_buf_len);
|
||||
if (hdl->cache_buf) {
|
||||
cbuf_init(&hdl->spk_cache_cbuffer, hdl->cache_buf, cache_buf_len);
|
||||
}
|
||||
}
|
||||
|
||||
if (cbuf_get_data_len(&hdl->spk_cache_cbuffer) == 0) {
|
||||
hdl->timestamp = audio_jiffies_usec();
|
||||
}
|
||||
|
||||
#if PC_SPK_ONLINE_DET_EN
|
||||
hdl->irq_cnt++;
|
||||
#endif
|
||||
|
||||
//1ms 起一次中断,一次长度192, 中断太快,需缓存
|
||||
wlen = cbuf_write(&hdl->spk_cache_cbuffer, buf, len);
|
||||
if (wlen != len) {
|
||||
/*putchar('W');*/
|
||||
}
|
||||
u32 cache_len = cbuf_get_data_len(&hdl->spk_cache_cbuffer);
|
||||
if (cache_len >= len * SPK_PUSH_FRAME_NUM) {
|
||||
frame = source_plug_get_output_frame(source_node, cache_len);
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
frame->len = cache_len;
|
||||
#if 1
|
||||
frame->flags = FRAME_FLAG_TIMESTAMP_ENABLE | FRAME_FLAG_PERIOD_SAMPLE | FRAME_FLAG_UPDATE_TIMESTAMP;
|
||||
frame->timestamp = hdl->timestamp * TIMESTAMP_US_DENOMINATOR;
|
||||
#else
|
||||
frame->flags = FRAME_FLAG_SYS_TIMESTAMP_ENABLE;
|
||||
frame->timestamp = hdl->timestamp;
|
||||
#endif
|
||||
cbuf_read(&hdl->spk_cache_cbuffer, frame->data, frame->len);
|
||||
source_plug_put_output_frame(source_node, frame);
|
||||
hdl->data_run = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* 定时器检测 pcspk 在线 */
|
||||
static void pcspk_det_timer_cb(void *priv)
|
||||
{
|
||||
struct pc_spk_file_hdl *hdl = (struct pc_spk_file_hdl *)priv;
|
||||
if (hdl) {
|
||||
if (hdl->start) {
|
||||
if (hdl->irq_cnt) {
|
||||
hdl->irq_cnt = 0;
|
||||
} else {
|
||||
if (hdl->data_run) {
|
||||
//已经往后面推数据突然中断没有起的情况
|
||||
hdl->data_run = 0;
|
||||
log_debug(">>>>>>> PCSPK LOST CONNECT <<<<<<<");
|
||||
#if (LEA_BIG_CTRLER_TX_EN || LEA_BIG_CTRLER_RX_EN)
|
||||
if (get_broadcast_role() == 1) {
|
||||
//广播(发送端)
|
||||
log_debug(">>[PC] spk lost audio stream, broadcast audio need suspend!\n");
|
||||
pc_mode_broadcast_deal_by_taskq(LE_AUDIO_MUSIC_STOP);
|
||||
} else {
|
||||
pcspk_close_player_by_taskq();
|
||||
}
|
||||
#elif (TCFG_LE_AUDIO_APP_CONFIG & (LE_AUDIO_AURACAST_SOURCE_EN | LE_AUDIO_AURACAST_SINK_EN))
|
||||
if (get_auracast_role() == 1) {
|
||||
//广播(发送端)
|
||||
log_debug(">>[PC] spk lost audio stream, broadcast audio need suspend!\n");
|
||||
pc_mode_broadcast_deal_by_taskq(LE_AUDIO_MUSIC_STOP);
|
||||
} else {
|
||||
pcspk_close_player_by_taskq();
|
||||
}
|
||||
#else
|
||||
pcspk_close_player_by_taskq();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 申请 pc_spk_file 结构体内存空间
|
||||
*/
|
||||
static void *pc_spk_file_init(void *source_node, struct stream_node *node)
|
||||
{
|
||||
struct pc_spk_file_hdl *hdl = NULL;
|
||||
if (pc_spk != NULL) {
|
||||
hdl = pc_spk;
|
||||
} else {
|
||||
hdl = zalloc(sizeof(*hdl));
|
||||
}
|
||||
if (!hdl) {
|
||||
log_error("%s, %d, alloc memory failed!\n", __func__, __LINE__);
|
||||
return NULL;
|
||||
}
|
||||
node->type |= NODE_TYPE_IRQ;
|
||||
hdl->source_node = source_node;
|
||||
hdl->node = node;
|
||||
pc_spk = hdl;
|
||||
return hdl;
|
||||
}
|
||||
|
||||
static int pc_spk_ioc_get_fmt(struct pc_spk_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
fmt->coding_type = AUDIO_CODING_PCM;
|
||||
if (pc_spk_fmt.channel == 2) {
|
||||
fmt->channel_mode = AUDIO_CH_LR;
|
||||
} else {
|
||||
fmt->channel_mode = AUDIO_CH_L;
|
||||
}
|
||||
fmt->sample_rate = pc_spk_fmt.sample_rate;
|
||||
fmt->bit_wide = (pc_spk_fmt.bit == 24) ? 1 : 0;
|
||||
fmt->pcm_24bit_type = (pc_spk_fmt.bit == 24) ? PCM_24BIT_DATA_3BYTE : PCM_24BIT_DATA_4BYTE;
|
||||
//log_debug(">>>>>>>>>>>>>>>>>>>>>>>>>>>>> fmt->bit_wide : %d\n", fmt->bit_wide);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pc_spk_ioc_set_fmt(struct pc_spk_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
//打开pcspk 在线检测定时器
|
||||
static void pcspk_open_det_timer(void)
|
||||
{
|
||||
#if PC_SPK_ONLINE_DET_EN
|
||||
struct pc_spk_file_hdl *hdl = pc_spk;
|
||||
//申请定时器
|
||||
if (hdl) {
|
||||
if (!hdl->det_timer_id) {
|
||||
hdl->det_timer_id = usr_timer_add(hdl, pcspk_det_timer_cb, PC_SPK_ONLINE_DET_TIME, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//关闭 pcspk 在线检测定时器
|
||||
static void pcspk_close_det_timer(void)
|
||||
{
|
||||
#if PC_SPK_ONLINE_DET_EN
|
||||
struct pc_spk_file_hdl *hdl = pc_spk;
|
||||
//申请定时器
|
||||
if (hdl) {
|
||||
if (hdl->det_timer_id) {
|
||||
usr_timer_del(hdl->det_timer_id);
|
||||
hdl->det_timer_id = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 1
|
||||
static int pc_spk_ioctl(void *_hdl, int cmd, int arg)
|
||||
{
|
||||
u32 i = 0;
|
||||
int ret = 0;
|
||||
struct pc_spk_file_hdl *hdl = (struct pc_spk_file_hdl *)_hdl;
|
||||
switch (cmd) {
|
||||
case NODE_IOC_GET_FMT:
|
||||
pc_spk_ioc_get_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_FMT:
|
||||
ret = pc_spk_ioc_set_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_PRIV_FMT:
|
||||
ret = pc_spk_ioc_set_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
|
||||
case NODE_IOC_START:
|
||||
if (hdl->start == 0) {
|
||||
pcspk_open_det_timer();
|
||||
hdl->data_run = 0;
|
||||
hdl->start = 1;
|
||||
}
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
case NODE_IOC_STOP:
|
||||
if (hdl->start) {
|
||||
hdl->start = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void pc_spk_release(void *_hdl)
|
||||
{
|
||||
struct pc_spk_file_hdl *hdl = (struct pc_spk_file_hdl *)_hdl;
|
||||
|
||||
spin_lock(&pc_spk_lock);
|
||||
if (!hdl) {
|
||||
hdl = pc_spk;
|
||||
if (!hdl) {
|
||||
spin_unlock(&pc_spk_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pcspk_close_det_timer();
|
||||
free(hdl->cache_buf);
|
||||
hdl->cache_buf = NULL;
|
||||
free(hdl);
|
||||
hdl = NULL;
|
||||
pc_spk = NULL;
|
||||
spin_unlock(&pc_spk_lock);
|
||||
}
|
||||
|
||||
u8 is_pc_spk_file_start(void)
|
||||
{
|
||||
struct pc_spk_file_hdl *hdl = pc_spk;
|
||||
if (!hdl) {
|
||||
return 0;
|
||||
}
|
||||
return (hdl->start);
|
||||
}
|
||||
|
||||
// 设置pc mic 的数据格式,传入0不设置
|
||||
void pc_spk_set_fmt(u8 channel, u8 bit, u32 sample_rate)
|
||||
{
|
||||
log_info("----------- Call %s, bit:%d, sr:%d\n", __func__, bit, sample_rate);
|
||||
pc_spk_fmt.init = 1;
|
||||
if (channel != 0) {
|
||||
pc_spk_fmt.channel = channel;
|
||||
}
|
||||
if (bit != 0) {
|
||||
pc_spk_fmt.bit = bit;
|
||||
}
|
||||
if (sample_rate != 0) {
|
||||
pc_spk_fmt.sample_rate = sample_rate;
|
||||
}
|
||||
}
|
||||
|
||||
u32 pc_spk_get_fmt_sample_rate(void)
|
||||
{
|
||||
return pc_spk_fmt.sample_rate;
|
||||
}
|
||||
|
||||
|
||||
REGISTER_SOURCE_NODE_PLUG(pc_spk_file_plug) = {
|
||||
.uuid = NODE_UUID_PC_SPK,
|
||||
.init = pc_spk_file_init,
|
||||
.ioctl = pc_spk_ioctl,
|
||||
.release = pc_spk_release,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef __PC_RX_FILE_H
|
||||
#define __PC_RX_FILE_H
|
||||
|
||||
#include "cpu.h"
|
||||
|
||||
|
||||
|
||||
u8 is_pc_spk_file_start(void);
|
||||
u8 is_pc_spk_file_run(void);
|
||||
u32 pc_spk_get_fmt_sample_rate(void);
|
||||
void pc_spk_set_fmt(u8 channel, u8 bit, u32 sample_rate);
|
||||
|
||||
void pc_spk_data_isr_cb(void *buf, u32 len); //spk 中断往数据流推数据函数
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".source_dev0_file.data.bss")
|
||||
#pragma data_seg(".source_dev0_file.data")
|
||||
#pragma const_seg(".source_dev0_file.text.const")
|
||||
#pragma code_seg(".source_dev0_file.text")
|
||||
#endif
|
||||
#include "classic/hci_lmp.h"
|
||||
#include "source_node.h"
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "app_config.h"
|
||||
|
||||
/*
|
||||
若源节点为中断节点,则需打开SOURCE_DEV0_IRQ_ENABLE
|
||||
并且在中断中调用source_dev0_packet_rx_notify, 用于中断收到数据时,主动唤醒数据流
|
||||
*/
|
||||
#define SOURCE_DEV0_IRQ_ENABLE 0 //中断节点使能
|
||||
|
||||
#define SOURCE_DEV0_MSBC_TEST_ENABLE 0 //MSBC解码测试, 使用MSBC固定数据测试
|
||||
|
||||
|
||||
#if TCFG_SOURCE_DEV0_NODE_ENABLE
|
||||
|
||||
struct source_dev0_file_hdl {
|
||||
u8 start; //启动标志
|
||||
struct stream_node *node; //节点句柄
|
||||
u8 bt_addr[6]; //蓝牙MAC地址
|
||||
};
|
||||
|
||||
#if SOURCE_DEV0_MSBC_TEST_ENABLE
|
||||
unsigned char source_test_data[60] = {
|
||||
0x01, 0x08, 0xAD, 0x00, 0x00, 0x35, 0xC3, 0x31, 0x01, 0x00, 0x7F, 0xEF, 0x76, 0xDF, 0xFB, 0x9D,
|
||||
0xB8, 0x05, 0x08, 0x6E, 0x04, 0x4E, 0x0B, 0x83, 0xF6, 0xBA, 0x62, 0xD0, 0x62, 0xB9, 0x1B, 0x27,
|
||||
0x6E, 0x5F, 0xB9, 0xDB, 0x9E, 0x2F, 0x76, 0xE9, 0x1B, 0xDD, 0xBA, 0xAA, 0xF7, 0x4E, 0xC3, 0xBD,
|
||||
0xDB, 0xB7, 0x2F, 0x74, 0xEF, 0x5B, 0xDD, 0x3C, 0x3A, 0xF7, 0x6C, 0x00
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
输入源节点数据
|
||||
hdl 节点私有句柄
|
||||
*len 源节点数据长度
|
||||
return 源节点数据指针
|
||||
*/
|
||||
static u8 *source_dev0_get_packet(struct source_dev0_file_hdl *hdl, u32 *len)
|
||||
{
|
||||
u8 *packet = NULL;
|
||||
u32 packet_len = 0;
|
||||
|
||||
//do something
|
||||
#if SOURCE_DEV0_MSBC_TEST_ENABLE
|
||||
u8 test_buf[4] = {0x08, 0x38, 0xc8, 0xf8};
|
||||
packet_len = sizeof(source_test_data);
|
||||
packet = source_test_data;
|
||||
static u8 i = 0;
|
||||
packet[1] = test_buf[i++];
|
||||
if (i > 3) {
|
||||
i = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
*len = packet_len;
|
||||
return packet;
|
||||
}
|
||||
|
||||
//释放源节点数据
|
||||
static int source_dev0_free_packet(struct source_dev0_file_hdl *hdl, u8 *packet)
|
||||
{
|
||||
/* free(packet); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
//自定义源节点挂起
|
||||
static void source_dev0_suspend(struct source_dev0_file_hdl *hdl)
|
||||
{
|
||||
/*
|
||||
do something
|
||||
1、启动丢数-避免suspend时 自定义源节点数据溢出
|
||||
2、挂起时,源节点需要的其他流程
|
||||
*/
|
||||
}
|
||||
|
||||
//自定义源节点 初始化
|
||||
static void source_dev0_open(struct source_dev0_file_hdl *hdl)
|
||||
{
|
||||
/*
|
||||
do something
|
||||
1、关闭丢数-用于suspend流程保护
|
||||
2、(中断节点需要)注册自定义源节点收包回调 source_dev0_packet_rx_notify
|
||||
3、自定义源节点启动流程
|
||||
*/
|
||||
}
|
||||
|
||||
//自定义源节点 停止
|
||||
static void source_dev0_close(struct source_dev0_file_hdl *hdl)
|
||||
{
|
||||
//do something
|
||||
}
|
||||
|
||||
/*
|
||||
自定义源节点 参数设置
|
||||
*fmt 目标参数,如采样率,数据类型,通道模式
|
||||
*/
|
||||
static void source_dev0_get_fmt(struct source_dev0_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
#if SOURCE_DEV0_MSBC_TEST_ENABLE
|
||||
fmt->sample_rate = 16000; //采样率
|
||||
fmt->coding_type = AUDIO_CODING_MSBC; //数据类型
|
||||
fmt->channel_mode = AUDIO_CH_LR; //通道模式
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
(当前节点为中断节点使用)
|
||||
自定义源节点收包回调
|
||||
- 用于自定义源节点收到数时, 唤醒数据流
|
||||
*/
|
||||
static void source_dev0_packet_rx_notify(void *_hdl)
|
||||
{
|
||||
struct source_dev0_file_hdl *hdl = (struct source_dev0_file_hdl *)_hdl;
|
||||
//启动状态才触发
|
||||
if (hdl->start) {
|
||||
jlstream_wakeup_thread(NULL, hdl->node, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static enum stream_node_state source_dev0_get_frame(void *_hdl, struct stream_frame **_frame)
|
||||
{
|
||||
u32 len = 0;
|
||||
struct source_dev0_file_hdl *hdl = (struct source_dev0_file_hdl *)_hdl;
|
||||
struct stream_frame *frame;
|
||||
|
||||
//1、获取当前节点数据
|
||||
u8 *packet = source_dev0_get_packet(hdl, &len);
|
||||
if (!packet) {
|
||||
*_frame = NULL;
|
||||
//表示源节点获取不到数据
|
||||
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
||||
}
|
||||
//2、申请数据流frame空间
|
||||
frame = jlstream_get_frame(hdl->node->oport, len);
|
||||
frame->len = len;
|
||||
|
||||
//3、将当前节点数据拷贝到frame
|
||||
memcpy(frame->data, packet, len);
|
||||
|
||||
//4、释放当前节点数据
|
||||
source_dev0_free_packet(hdl, packet);
|
||||
|
||||
*_frame = frame;
|
||||
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
|
||||
static void *source_dev0_init(void *priv, struct stream_node *node)
|
||||
{
|
||||
struct source_dev0_file_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
hdl->node = node;
|
||||
#if SOURCE_DEV0_IRQ_ENABLE
|
||||
node->type |= NODE_TYPE_IRQ;
|
||||
#endif/*SOURCE_DEV0_IRQ_ENABLE*/
|
||||
return hdl;
|
||||
}
|
||||
|
||||
//获取当前蓝牙地址
|
||||
static int source_dev0_ioc_set_bt_addr(struct source_dev0_file_hdl *hdl, u8 *bt_addr)
|
||||
{
|
||||
memcpy(hdl->bt_addr, bt_addr, 6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//获取当前节点数据参数
|
||||
static void source_dev0_ioc_get_fmt(struct source_dev0_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
source_dev0_get_fmt(hdl, fmt);
|
||||
}
|
||||
|
||||
static void source_dev0_ioc_suspend(struct source_dev0_file_hdl *hdl)
|
||||
{
|
||||
hdl->start = 0;
|
||||
source_dev0_suspend(hdl);
|
||||
}
|
||||
|
||||
static void source_dev0_ioc_start(struct source_dev0_file_hdl *hdl)
|
||||
{
|
||||
source_dev0_open(hdl);
|
||||
hdl->start = 1;
|
||||
}
|
||||
|
||||
static void source_dev0_ioc_stop(struct source_dev0_file_hdl *hdl)
|
||||
{
|
||||
hdl->start = 0;
|
||||
source_dev0_close(hdl);
|
||||
}
|
||||
|
||||
static int source_dev0_ioctl(void *_hdl, int cmd, int arg)
|
||||
{
|
||||
struct source_dev0_file_hdl *hdl = (struct source_dev0_file_hdl *)_hdl;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_SET_BTADDR:
|
||||
source_dev0_ioc_set_bt_addr(hdl, (u8 *)arg);
|
||||
break;
|
||||
case NODE_IOC_GET_FMT:
|
||||
source_dev0_ioc_get_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
source_dev0_ioc_suspend(hdl);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
source_dev0_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_STOP:
|
||||
source_dev0_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void source_dev0_release(void *_hdl)
|
||||
{
|
||||
struct source_dev0_file_hdl *hdl = (struct source_dev0_file_hdl *)_hdl;
|
||||
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
|
||||
REGISTER_SOURCE_NODE_PLUG(source_dev0_file_plug) = {
|
||||
.uuid = NODE_UUID_SOURCE_DEV0,
|
||||
.init = source_dev0_init,
|
||||
.get_frame = source_dev0_get_frame,
|
||||
.ioctl = source_dev0_ioctl,
|
||||
.release = source_dev0_release,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
#ifdef SUPPORT_MS_EXTENSIONS
|
||||
#pragma bss_seg(".source_dev1_file.data.bss")
|
||||
#pragma data_seg(".source_dev1_file.data")
|
||||
#pragma const_seg(".source_dev1_file.text.const")
|
||||
#pragma code_seg(".source_dev1_file.text")
|
||||
#endif
|
||||
#include "classic/hci_lmp.h"
|
||||
#include "source_node.h"
|
||||
#include "jlstream.h"
|
||||
#include "media/audio_base.h"
|
||||
#include "app_config.h"
|
||||
|
||||
/*
|
||||
若源节点为中断节点,则需打开SOURCE_DEV1_IRQ_ENABLE
|
||||
并且在中断中调用source_dev1_packet_rx_notify, 用于中断收到数据时,主动唤醒数据流
|
||||
*/
|
||||
#define SOURCE_DEV1_IRQ_ENABLE 0 //中断节点使能
|
||||
|
||||
#define SOURCE_DEV1_MSBC_TEST_ENABLE 0 //MSBC解码测试, 使用MSBC固定数据测试
|
||||
|
||||
|
||||
#if TCFG_SOURCE_DEV1_NODE_ENABLE
|
||||
|
||||
struct source_dev1_file_hdl {
|
||||
u8 start; //启动标志
|
||||
struct stream_node *node; //节点句柄
|
||||
u8 bt_addr[6]; //蓝牙MAC地址
|
||||
};
|
||||
|
||||
#if SOURCE_DEV1_MSBC_TEST_ENABLE
|
||||
unsigned char source_test_data[60] = {
|
||||
0x01, 0x08, 0xAD, 0x00, 0x00, 0x35, 0xC3, 0x31, 0x01, 0x00, 0x7F, 0xEF, 0x76, 0xDF, 0xFB, 0x9D,
|
||||
0xB8, 0x05, 0x08, 0x6E, 0x04, 0x4E, 0x0B, 0x83, 0xF6, 0xBA, 0x62, 0xD0, 0x62, 0xB9, 0x1B, 0x27,
|
||||
0x6E, 0x5F, 0xB9, 0xDB, 0x9E, 0x2F, 0x76, 0xE9, 0x1B, 0xDD, 0xBA, 0xAA, 0xF7, 0x4E, 0xC3, 0xBD,
|
||||
0xDB, 0xB7, 0x2F, 0x74, 0xEF, 0x5B, 0xDD, 0x3C, 0x3A, 0xF7, 0x6C, 0x00
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
输入源节点数据
|
||||
hdl 节点私有句柄
|
||||
*len 源节点数据长度
|
||||
return 源节点数据指针
|
||||
*/
|
||||
static u8 *source_dev1_get_packet(struct source_dev1_file_hdl *hdl, u32 *len)
|
||||
{
|
||||
u8 *packet = NULL;
|
||||
u32 packet_len = 0;
|
||||
|
||||
//do something
|
||||
#if SOURCE_DEV1_MSBC_TEST_ENABLE
|
||||
u8 test_buf[4] = {0x08, 0x38, 0xc8, 0xf8};
|
||||
packet_len = sizeof(source_test_data);
|
||||
packet = source_test_data;
|
||||
static u8 i = 0;
|
||||
packet[1] = test_buf[i++];
|
||||
if (i > 3) {
|
||||
i = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
*len = packet_len;
|
||||
return packet;
|
||||
}
|
||||
|
||||
//释放源节点数据
|
||||
static int source_dev1_free_packet(struct source_dev1_file_hdl *hdl, u8 *packet)
|
||||
{
|
||||
/* free(packet); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
//自定义源节点挂起
|
||||
static void source_dev1_suspend(struct source_dev1_file_hdl *hdl)
|
||||
{
|
||||
/*
|
||||
do something
|
||||
1、启动丢数-避免suspend时 自定义源节点数据溢出
|
||||
2、挂起时,源节点需要的其他流程
|
||||
*/
|
||||
}
|
||||
|
||||
//自定义源节点 初始化
|
||||
static void source_dev1_open(struct source_dev1_file_hdl *hdl)
|
||||
{
|
||||
/*
|
||||
do something
|
||||
1、关闭丢数-用于suspend流程保护
|
||||
2、(中断节点需要)注册自定义源节点收包回调 source_dev1_packet_rx_notify
|
||||
3、自定义源节点启动流程
|
||||
*/
|
||||
}
|
||||
|
||||
//自定义源节点 停止
|
||||
static void source_dev1_close(struct source_dev1_file_hdl *hdl)
|
||||
{
|
||||
//do something
|
||||
}
|
||||
|
||||
/*
|
||||
自定义源节点 参数设置
|
||||
*fmt 目标参数,如采样率,数据类型,通道模式
|
||||
*/
|
||||
static void source_dev1_get_fmt(struct source_dev1_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
#if SOURCE_DEV1_MSBC_TEST_ENABLE
|
||||
fmt->sample_rate = 16000; //采样率
|
||||
fmt->coding_type = AUDIO_CODING_MSBC; //数据类型
|
||||
fmt->channel_mode = AUDIO_CH_LR; //通道模式
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
(当前节点为中断节点使用)
|
||||
自定义源节点收包回调
|
||||
- 用于自定义源节点收到数时, 唤醒数据流
|
||||
*/
|
||||
static void source_dev1_packet_rx_notify(void *_hdl)
|
||||
{
|
||||
struct source_dev1_file_hdl *hdl = (struct source_dev1_file_hdl *)_hdl;
|
||||
//启动状态才触发
|
||||
if (hdl->start) {
|
||||
jlstream_wakeup_thread(NULL, hdl->node, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static enum stream_node_state source_dev1_get_frame(void *_hdl, struct stream_frame **_frame)
|
||||
{
|
||||
u32 len = 0;
|
||||
struct source_dev1_file_hdl *hdl = (struct source_dev1_file_hdl *)_hdl;
|
||||
struct stream_frame *frame;
|
||||
|
||||
//1、获取当前节点数据
|
||||
u8 *packet = source_dev1_get_packet(hdl, &len);
|
||||
if (!packet) {
|
||||
*_frame = NULL;
|
||||
//表示源节点获取不到数据
|
||||
return NODE_STA_RUN | NODE_STA_SOURCE_NO_DATA;
|
||||
}
|
||||
//2、申请数据流frame空间
|
||||
frame = jlstream_get_frame(hdl->node->oport, len);
|
||||
frame->len = len;
|
||||
|
||||
//3、将当前节点数据拷贝到frame
|
||||
memcpy(frame->data, packet, len);
|
||||
|
||||
//4、释放当前节点数据
|
||||
source_dev1_free_packet(hdl, packet);
|
||||
|
||||
*_frame = frame;
|
||||
|
||||
return NODE_STA_RUN;
|
||||
}
|
||||
|
||||
static void *source_dev1_init(void *priv, struct stream_node *node)
|
||||
{
|
||||
struct source_dev1_file_hdl *hdl = zalloc(sizeof(*hdl));
|
||||
hdl->node = node;
|
||||
#if SOURCE_DEV1_IRQ_ENABLE
|
||||
node->type |= NODE_TYPE_IRQ;
|
||||
#endif/*SOURCE_DEV1_IRQ_ENABLE*/
|
||||
return hdl;
|
||||
}
|
||||
|
||||
//获取当前蓝牙地址
|
||||
static int source_dev1_ioc_set_bt_addr(struct source_dev1_file_hdl *hdl, u8 *bt_addr)
|
||||
{
|
||||
memcpy(hdl->bt_addr, bt_addr, 6);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//获取当前节点数据参数
|
||||
static void source_dev1_ioc_get_fmt(struct source_dev1_file_hdl *hdl, struct stream_fmt *fmt)
|
||||
{
|
||||
source_dev1_get_fmt(hdl, fmt);
|
||||
}
|
||||
|
||||
static void source_dev1_ioc_suspend(struct source_dev1_file_hdl *hdl)
|
||||
{
|
||||
hdl->start = 0;
|
||||
source_dev1_suspend(hdl);
|
||||
}
|
||||
|
||||
static void source_dev1_ioc_start(struct source_dev1_file_hdl *hdl)
|
||||
{
|
||||
source_dev1_open(hdl);
|
||||
hdl->start = 1;
|
||||
}
|
||||
|
||||
static void source_dev1_ioc_stop(struct source_dev1_file_hdl *hdl)
|
||||
{
|
||||
hdl->start = 0;
|
||||
source_dev1_close(hdl);
|
||||
}
|
||||
|
||||
static int source_dev1_ioctl(void *_hdl, int cmd, int arg)
|
||||
{
|
||||
struct source_dev1_file_hdl *hdl = (struct source_dev1_file_hdl *)_hdl;
|
||||
|
||||
switch (cmd) {
|
||||
case NODE_IOC_SET_BTADDR:
|
||||
source_dev1_ioc_set_bt_addr(hdl, (u8 *)arg);
|
||||
break;
|
||||
case NODE_IOC_GET_FMT:
|
||||
source_dev1_ioc_get_fmt(hdl, (struct stream_fmt *)arg);
|
||||
break;
|
||||
case NODE_IOC_SET_SCENE:
|
||||
break;
|
||||
case NODE_IOC_SUSPEND:
|
||||
source_dev1_ioc_suspend(hdl);
|
||||
break;
|
||||
case NODE_IOC_START:
|
||||
source_dev1_ioc_start(hdl);
|
||||
break;
|
||||
case NODE_IOC_STOP:
|
||||
source_dev1_ioc_stop(hdl);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void source_dev1_release(void *_hdl)
|
||||
{
|
||||
struct source_dev1_file_hdl *hdl = (struct source_dev1_file_hdl *)_hdl;
|
||||
|
||||
free(hdl);
|
||||
}
|
||||
|
||||
|
||||
REGISTER_SOURCE_NODE_PLUG(source_dev1_file_plug) = {
|
||||
.uuid = NODE_UUID_SOURCE_DEV1,
|
||||
.init = source_dev1_init,
|
||||
.get_frame = source_dev1_get_frame,
|
||||
.ioctl = source_dev1_ioctl,
|
||||
.release = source_dev1_release,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user