This commit is contained in:
huxi
2025-12-03 11:12:34 +08:00
parent c23ae4f24c
commit bc195654bf
8163 changed files with 3799544 additions and 92 deletions
+38
View File
@@ -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_
+34
View File
@@ -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_
+33
View File
@@ -0,0 +1,33 @@
#ifndef _VOLUME_NODE_H_
#define _VOLUME_NODE_H_
#include "audio_config.h"
struct volume_cfg {
u8 bypass; //是否bypass掉当前节点,复用高4bit用于传递 cmdVOLUME_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
+601
View File
@@ -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
+214
View File
@@ -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*/
+216
View File
@@ -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*/
+147
View File
@@ -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
+231
View File
@@ -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
+655
View File
@@ -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
+568
View File
@@ -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
+250
View File
@@ -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(&param);
}
/*节点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
+216
View File
@@ -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 */
+237
View File
@@ -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*/
+341
View File
@@ -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
+283
View File
@@ -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
+178
View File
@@ -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
+178
View File
@@ -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
+129
View File
@@ -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
+532
View File
@@ -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 *)&params, 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
+739
View File
@@ -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