add libxmos_ns

This commit is contained in:
Steven Dan
2026-06-10 17:25:27 +08:00
parent c5af9496c9
commit b5054852a7
10 changed files with 513 additions and 11 deletions

View File

@@ -101,6 +101,7 @@ set(APP_COMPILER_FLAGS_factory ${SW_FACT_AUDIO_FLAGS} -DI2S_CHANS_DA
-DMIN_FREQ=\(48000\)
-DDEFAULT_FREQ=\(48000\)
-DUART_DEBUG=0
-mcmodel=large
-DHID_CONTROLS=1
-DEQ_EN=1
-DHID_DFU_EN=1
@@ -134,17 +135,19 @@ set(APP_COMPILER_FLAGS_fps_uac1 ${SW_USB_AUDIO_FLAGS} -DXUA_SPDIF_RX
-DMIN_FREQ=\(48000\)
-DDEFAULT_FREQ=\(48000\)
-lxmos_fps
-lxmos_ns
-DHID_CONTROLS_UAC1=1
-DHID_DFU_EN=1
-DEQ_EN=1
-mcmodel=large
-DXMOS_FPS_EN=1
#-DXMOS_NS_EN=1
-DUAC1=1
-DWIN_OS_DETECTION=1
#-DLOW_POWER_EN=1
-DCHAN_BUFF_CTRL=1
-DXUD_PRIORITY_HIGH=1
-DHID_CONTROLS=1
-DDNR_50MS=1
#-DDNR_50MS=1
-ldnr_50ms
#-DDEBUG_MEMORY_LOG_ENABLED=1
-DNO_LOG_TIMESTAPS=0

View File

@@ -65,7 +65,22 @@ void UserBufferManagementInit(unsigned samFreq)
}
extern void buffer_exchange(chanend c_data, unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[], int num_chan);
#if XMOS_NS_EN
/* 改动原因tile0侧ADC->USB交换在.xc实现避免chanend与C侧chanend_t(unsigned)链接类型不匹配 */
void ns_exchange_buffer_data(chanend c_data, int32_t *data)
{
int sample;
sample = data[1];
c_data <: sample;
c_data :> sample;
data[1] = sample;
data[0] = data[1];
}
unsafe chanend uc_ns_data;
#else
void dnr_exchange_buffer(int32_t * unsafe data);
#endif
extern unsigned g_dnr_enable;
extern unsigned g_adc_loop;
#if UAC1
@@ -97,7 +112,12 @@ void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudi
if (dnr_enable)
{
#if XMOS_NS_EN
/* 改动原因XMOS_NS_EN时用tile1的NS库处理ADC->USB替代tile0旧DNR */
ns_exchange_buffer_data((chanend)uc_ns_data, (int32_t *)&sampsFromAudioToUsb[EXTRA_I2S_CHAN_INDEX_IN]);
#else
dnr_exchange_buffer((int32_t *)&sampsFromAudioToUsb[EXTRA_I2S_CHAN_INDEX_IN]);
#endif
}
else
{

View File

@@ -424,6 +424,14 @@ extern void fps_main(chanend c_data);
extern void fps1_dsp_proc_task();
#endif
#if XMOS_NS_EN
/* 改动原因NS集成放在main.xc而非user_main.h避免user_main.h内嵌#if破坏MQA_EN/UAC1预处理结构 */
extern void ns_main(chanend c_data);
extern void ns1_dsp_proc_task(void);
extern unsafe chanend uc_ns_data;
#endif /* XMOS_NS_EN */
/* Main for USB Audio Applications */
int main()
{
@@ -534,6 +542,19 @@ int main()
}
#endif
#endif
#if XMOS_NS_EN
/* 改动原因NS处理ADC->USB单声道数据运行在tile1与FPS(USB->DAC)方向相反 */
on tile[1]: {
set_core_high_priority_on();
ns_main(c_ns_transport);
}
on tile[1]: {
set_core_high_priority_on();
ns1_dsp_proc_task();
}
#endif
#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC && !XUA_USE_SW_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN))
on tile[PLL_REF_TILE]: PllRefPinTask(i_pll_ref, p_pll_ref);
#endif
@@ -552,6 +573,9 @@ int main()
{
#ifdef XUD_PRIORITY_HIGH
set_core_high_priority_on();
#endif
#if XMOS_NS_EN
unsafe { uc_ns_data = (chanend) c_ns_transport; }
#endif
/* Run UAC2.0 at high-speed, UAC1.0 at full-speed */
unsigned usbSpeed = (AUDIO_CLASS == 2) ? XUD_SPEED_HS : XUD_SPEED_FS;

View File

@@ -0,0 +1,37 @@
#ifndef __NS_API_H__
#define __NS_API_H__
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* 初始化 NS 处理链及默认运行状态。 */
void ns_xmos_xc_init(void);
/*
* 设置运行时两个子模块的开关。
* ns_enable: 0 表示旁路 NS 主处理,非 0 表示开启
* eq_enable: 0 表示在 NS 阶段旁路 EQ非 0 表示开启
*/
void ns_xmos_xc_set_module_enable(int ns_enable, int eq_enable);
/* 初始化运行,调节过程中不运行。 */
/* 设置 10 段 EQ 的全部增益,单位为 0.01 dB范围 -600 ~ 600。 */
void ns_xmos_xc_eq_set_all_gains(int16_t *gains);
/* 初始化不运行,调节过程中运行。 */
/* 设置单个 EQ 频段增益,单位为 0.01 dB范围 -600 ~ 600。 */
void ns_xmos_xc_eq_set_band_gain(int band, int16_t gain);
/*
* 单声道block长度768 Samples
*/
void ns_xmos_xc_process(int16_t *input, int16_t *output);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,167 @@
/* 改动原因NS ns_eq 五模式增益在tile0侧LittleFS持久化参照fps_eq_flash实现 */
#include <string.h>
#include <stdio.h>
#include "ns_eq_flash.h"
#include "lfs_io.h"
#include "debug_print.h"
#define NS_EQ_FLASH_PATH "ns_eq_store"
#define NS_EQ_FLASH_VERSION 0x01u
static void ns_eq_default_name(uint8_t mode, char out_name[NS_EQ_NAME_LEN])
{
/* 改动原因:按协议约定提供小写默认模式名 ns_eq_0 ~ ns_eq_4 */
snprintf(out_name, NS_EQ_NAME_LEN, "ns_eq_%u", (unsigned)mode);
}
/* 改动原因与fps_eq_flash相同逻辑判断Flash中是否已有有效模式名 */
static int ns_eq_name_has_saved_value(const char name[NS_EQ_NAME_LEN])
{
int i;
int all_zero = 1;
int all_ff = 1;
int has_terminator = 0;
unsigned char first = (unsigned char)name[0];
for (i = 0; i < NS_EQ_NAME_LEN; i++) {
unsigned char c = (unsigned char)name[i];
if (c != 0x00) {
all_zero = 0;
}
if (c != 0xFF) {
all_ff = 0;
}
if (c == '\0') {
has_terminator = 1;
break;
}
}
if (all_zero || all_ff) {
return 0;
}
if (first < 0x20) {
return 0;
}
if (!has_terminator) {
return 0;
}
return 1;
}
void ns_eq_flash_set_defaults(ns_eq_flash_store_t *store)
{
int m, b;
memset(store, 0, sizeof(*store));
store->magic = NS_EQ_FLASH_MAGIC;
store->version = NS_EQ_FLASH_VERSION;
store->current_mode = 0;
for (m = 0; m < NS_EQ_MODE_COUNT; m++) {
ns_eq_default_name((uint8_t)m, store->mode_names[m]);
for (b = 0; b < NS_EQ_BAND_COUNT; b++) {
store->gains[m][b] = NS_EQ_GAIN_DEFAULT;
}
}
}
int16_t ns_eq_clamp_gain(int16_t gain)
{
if (gain < NS_EQ_GAIN_MIN) {
return NS_EQ_GAIN_MIN;
}
if (gain > NS_EQ_GAIN_MAX) {
return NS_EQ_GAIN_MAX;
}
return gain;
}
int ns_eq_flash_load(ns_eq_flash_store_t *out)
{
ns_eq_flash_store_t tmp;
int m;
int need_repair = 0;
ns_eq_flash_set_defaults(out);
if (lfs_init() != 0) {
return -1;
}
lfs_read_config((unsigned char *)NS_EQ_FLASH_PATH,
(unsigned char *)&tmp,
sizeof(tmp));
lfs_deinit();
if (tmp.magic != NS_EQ_FLASH_MAGIC) {
debug_printf("ns_eq_flash: invalid magic, use defaults\n");
return -1;
}
if (tmp.version != NS_EQ_FLASH_VERSION) {
debug_printf("ns_eq_flash: version mismatch, reset defaults\n");
return -1;
}
if (tmp.current_mode >= NS_EQ_MODE_COUNT) {
tmp.current_mode = 0;
need_repair = 1;
}
for (m = 0; m < NS_EQ_MODE_COUNT; m++) {
tmp.mode_names[m][NS_EQ_NAME_LEN - 1] = '\0';
if (!ns_eq_name_has_saved_value(tmp.mode_names[m])) {
ns_eq_default_name((uint8_t)m, tmp.mode_names[m]);
need_repair = 1;
}
}
*out = tmp;
if (need_repair) {
(void)ns_eq_flash_save(out);
}
return 0;
}
int ns_eq_flash_save(const ns_eq_flash_store_t *store)
{
ns_eq_flash_store_t to_write = *store;
int m;
to_write.magic = NS_EQ_FLASH_MAGIC;
to_write.version = NS_EQ_FLASH_VERSION;
if (to_write.current_mode >= NS_EQ_MODE_COUNT) {
to_write.current_mode = 0;
}
for (m = 0; m < NS_EQ_MODE_COUNT; m++) {
to_write.mode_names[m][NS_EQ_NAME_LEN - 1] = '\0';
if (to_write.mode_names[m][0] == '\0') {
ns_eq_default_name((uint8_t)m, to_write.mode_names[m]);
}
}
if (lfs_init() != 0) {
return -1;
}
lfs_write_config((unsigned char *)NS_EQ_FLASH_PATH,
(unsigned char *)&to_write,
sizeof(to_write));
lfs_deinit();
return 0;
}
void ns_eq_name_get(const ns_eq_flash_store_t *store, uint8_t mode, char out_name[NS_EQ_NAME_LEN])
{
if (mode >= NS_EQ_MODE_COUNT) {
mode = 0;
}
memcpy(out_name, store->mode_names[mode], NS_EQ_NAME_LEN);
out_name[NS_EQ_NAME_LEN - 1] = '\0';
}
void ns_eq_name_set(ns_eq_flash_store_t *store, uint8_t mode, const char *name)
{
if (store == NULL || mode >= NS_EQ_MODE_COUNT || name == NULL) {
return;
}
snprintf(store->mode_names[mode], NS_EQ_NAME_LEN, "%s", name);
}

View File

@@ -0,0 +1,32 @@
#ifndef NS_EQ_FLASH_H
#define NS_EQ_FLASH_H
#include <stdint.h>
/* 改动原因NS库ns_eq五套预设(模式0-4)每套10段增益单位0.01dB存于tile0 Flash结构参照fps_eq */
#define NS_EQ_MODE_COUNT 5
#define NS_EQ_BAND_COUNT 10
#define NS_EQ_NAME_LEN 16
#define NS_EQ_GAIN_DEFAULT 0
#define NS_EQ_GAIN_MIN (-600)
#define NS_EQ_GAIN_MAX 600
#define NS_EQ_FLASH_MAGIC 0x4E534551u /* "NSEQ" */
typedef struct {
uint32_t magic;
uint8_t version;
uint8_t current_mode;
uint8_t reserved[2];
char mode_names[NS_EQ_MODE_COUNT][NS_EQ_NAME_LEN];
int16_t gains[NS_EQ_MODE_COUNT][NS_EQ_BAND_COUNT];
} ns_eq_flash_store_t;
void ns_eq_flash_set_defaults(ns_eq_flash_store_t *store);
int ns_eq_flash_load(ns_eq_flash_store_t *out);
int ns_eq_flash_save(const ns_eq_flash_store_t *store);
int16_t ns_eq_clamp_gain(int16_t gain);
void ns_eq_name_get(const ns_eq_flash_store_t *store, uint8_t mode, char out_name[NS_EQ_NAME_LEN]);
void ns_eq_name_set(ns_eq_flash_store_t *store, uint8_t mode, const char *name);
#endif

View File

@@ -0,0 +1,23 @@
#ifndef NS_EQ_SYNC_H
#define NS_EQ_SYNC_H
#include <stdint.h>
#include "ns_eq_flash.h"
/* 改动原因int16增益经5个int32共享字在tile0/tile1间传递(dp仅支持32位SHARED_GLOBAL)参照fps_eq_sync */
extern unsigned g_ns_eq_mode;
extern int32_t g_ns_eq_pack0;
extern int32_t g_ns_eq_pack1;
extern int32_t g_ns_eq_pack2;
extern int32_t g_ns_eq_pack3;
extern int32_t g_ns_eq_pack4;
void ns_eq_unpack_gains(int16_t out[NS_EQ_BAND_COUNT]);
void ns_eq_publish_gains_to_shared(const int16_t gains[NS_EQ_BAND_COUNT]);
void ns_eq_publish_mode_and_gains(unsigned mode, const int16_t gains[NS_EQ_BAND_COUNT]);
void ns_apply_eq_mode(void);
void ns_apply_eq_band_gain(unsigned band, int16_t gain);
void ns_apply_module_enable(void);
#endif

View File

@@ -0,0 +1,200 @@
// Copyright 2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <print.h>
#include <stdio.h>
#include <string.h>
#include <platform.h>
#include <xs1.h>
#include <xcore/chanend.h>
#include <xcore/channel.h>
#include "xc_ptr.h"
#include "xua_conf.h"
#if XMOS_NS_EN
/* 改动原因NS库单核xc接口使用dnr/ns_api.h中的ns_xmos_xc_*声明数据流为ADC->USB与FPS相反 */
#include "ns_api.h"
#include "ns_eq_sync.h"
#define NS_SLOT_NUM 3
#define NS_FRAME_SIZE 768
/* 改动原因ns_api.h约定单声道块长度768 sample */
short __attribute__((aligned (4))) dsp_ns_input_buf[NS_SLOT_NUM][NS_FRAME_SIZE];
short __attribute__((aligned (4))) dsp_ns_out_buf[NS_SLOT_NUM][NS_FRAME_SIZE];
unsigned ns_buf_ready = 0;
unsigned ns_write_pos = 0;
unsigned ns_process_pos = 0;
/* 改动原因ns主处理开关由tile0的g_dnr_enable(麦克风长按)控制与旧DNR用户操作一致 */
unsigned g_ns_eq_enable = 0;
/* 改动原因g_dnr_enable定义在audiohw.xc(tile0)NS主处理开关与其绑定 */
extern unsigned g_dnr_enable;
/* 改动原因ns_eq当前模式(0-4)与10段增益经5个pack字在tile0/tile1共享 */
unsigned g_ns_eq_mode = 0;
int32_t g_ns_eq_pack0 = 0;
int32_t g_ns_eq_pack1 = 0;
int32_t g_ns_eq_pack2 = 0;
int32_t g_ns_eq_pack3 = 0;
int32_t g_ns_eq_pack4 = 0;
static int32_t ns_eq_make_pack(int16_t lo, int16_t hi)
{
return ((int32_t)(uint16_t)lo) | (((int32_t)(uint16_t)hi) << 16);
}
static int16_t ns_eq_pack_lo(int32_t w)
{
return (int16_t)(w & 0xFFFF);
}
static int16_t ns_eq_pack_hi(int32_t w)
{
return (int16_t)((w >> 16) & 0xFFFF);
}
void ns_eq_unpack_gains(int16_t out[NS_EQ_BAND_COUNT])
{
int32_t p0, p1, p2, p3, p4;
GET_SHARED_GLOBAL(p0, g_ns_eq_pack0);
GET_SHARED_GLOBAL(p1, g_ns_eq_pack1);
GET_SHARED_GLOBAL(p2, g_ns_eq_pack2);
GET_SHARED_GLOBAL(p3, g_ns_eq_pack3);
GET_SHARED_GLOBAL(p4, g_ns_eq_pack4);
out[0] = ns_eq_pack_lo(p0);
out[1] = ns_eq_pack_hi(p0);
out[2] = ns_eq_pack_lo(p1);
out[3] = ns_eq_pack_hi(p1);
out[4] = ns_eq_pack_lo(p2);
out[5] = ns_eq_pack_hi(p2);
out[6] = ns_eq_pack_lo(p3);
out[7] = ns_eq_pack_hi(p3);
out[8] = ns_eq_pack_lo(p4);
out[9] = ns_eq_pack_hi(p4);
}
void ns_eq_publish_gains_to_shared(const int16_t gains[NS_EQ_BAND_COUNT])
{
SET_SHARED_GLOBAL(g_ns_eq_pack0, ns_eq_make_pack(gains[0], gains[1]));
SET_SHARED_GLOBAL(g_ns_eq_pack1, ns_eq_make_pack(gains[2], gains[3]));
SET_SHARED_GLOBAL(g_ns_eq_pack2, ns_eq_make_pack(gains[4], gains[5]));
SET_SHARED_GLOBAL(g_ns_eq_pack3, ns_eq_make_pack(gains[6], gains[7]));
SET_SHARED_GLOBAL(g_ns_eq_pack4, ns_eq_make_pack(gains[8], gains[9]));
}
void ns_eq_publish_mode_and_gains(unsigned mode, const int16_t gains[NS_EQ_BAND_COUNT])
{
SET_SHARED_GLOBAL(g_ns_eq_mode, mode);
ns_eq_publish_gains_to_shared(gains);
}
void ns_apply_eq_mode(void)
{
int16_t gains[NS_EQ_BAND_COUNT];
ns_eq_unpack_gains(gains);
/* 改动原因切换模式时用eq_set_all_gains一次性加载当前模式10段增益 */
ns_xmos_xc_eq_set_all_gains(gains);
}
void ns_apply_eq_band_gain(unsigned band, int16_t gain)
{
if (band >= NS_EQ_BAND_COUNT) {
return;
}
/* 改动原因运行中单段调节使用ns_xmos_xc_eq_set_band_gain */
ns_xmos_xc_eq_set_band_gain((int)band, gain);
}
/* 改动原因tile0侧ns_exchange_buffer_data已移至extra_i2s.xc(.xc用chanend)本文件仅保留tile1侧ns_main/DSP */
/* 改动原因tile1侧单声道帧收发累积768 sample后触发DSP输出为延迟后的处理结果 */
void ns_main(chanend_t c_data)
{
int input[1];
int output[1];
int count = 0;
unsigned write_pos = 0;
unsigned read_pos = 0;
while (1) {
chan_in_buf_word(c_data, input, 1);
chan_out_buf_word(c_data, output, 1);
if (count == 0) {
read_pos = (write_pos + 1 + NS_SLOT_NUM) % NS_SLOT_NUM;
}
dsp_ns_input_buf[write_pos][count] = (short)(input[0] >> 16);
output[0] = (int)(dsp_ns_out_buf[read_pos][count]) << 16;
if (count != (NS_FRAME_SIZE - 1)) {
count++;
} else {
count = 0;
SET_SHARED_GLOBAL(ns_process_pos, write_pos);
write_pos = (write_pos + 1) % NS_SLOT_NUM;
SET_SHARED_GLOBAL(ns_write_pos, write_pos);
SET_SHARED_GLOBAL(ns_buf_ready, 1);
}
}
}
/* 改动原因按g_dnr_enable(用户DNR开关)与g_ns_eq_enable同步NS主处理/ns_eq两模块开关到算法库 */
void ns_apply_module_enable(void)
{
unsigned ns_enable;
unsigned eq_enable;
GET_SHARED_GLOBAL(ns_enable, g_dnr_enable);
GET_SHARED_GLOBAL(eq_enable, g_ns_eq_enable);
ns_xmos_xc_set_module_enable(
(ns_enable != 0) ? 1 : 0,
(eq_enable != 0) ? 1 : 0);
}
/* 改动原因集中NS库初始化顺序——init→ns_eq全段增益→两模块使能 */
static void ns_xmos_modules_init(void)
{
int16_t gains[NS_EQ_BAND_COUNT];
ns_xmos_xc_init();
ns_eq_unpack_gains(gains);
ns_xmos_xc_eq_set_all_gains(gains);
ns_apply_module_enable();
}
#pragma stackfunction 4096
void ns1_dsp_proc_task(void)
{
ns_xmos_modules_init();
while (1) {
unsigned ready;
GET_SHARED_GLOBAL(ready, ns_buf_ready);
while (ready) {
unsigned cur_pos;
GET_SHARED_GLOBAL(cur_pos, ns_process_pos);
/* 改动原因每帧处理前刷新模块使能支持运行时切换g_dnr_enable */
ns_apply_module_enable();
ns_xmos_xc_process(dsp_ns_input_buf[cur_pos], dsp_ns_out_buf[cur_pos]);
ready = 0;
SET_SHARED_GLOBAL(ns_buf_ready, 0);
}
}
}
#endif /* XMOS_NS_EN */

View File

@@ -26,6 +26,7 @@ void i2s_driver(chanend c);
extern unsafe chanend uc_i2s;
extern unsafe chanend uc_br_data;
extern unsafe chanend uc_eq_data;
extern unsafe chanend uc_ns_data;
extern void UserBufferManagementSetChan(chanend c);
extern void dsp_main (chanend c_data , chanend cc_br_eof);
extern void br_dsp_proc_task(chanend c_validate, chanend cc_br_eof);
@@ -39,7 +40,7 @@ extern unsafe streaming chanend uc_dfu;
extern void fps1_dsp_proc_task(void);
extern void fps2_dsp_proc_task(void);
#define USER_MAIN_DECLARATIONS chan c_data_transport, cc_br_eof, c_validate; \
#define USER_MAIN_DECLARATIONS chan cc_br_eof, c_validate, c_ns_transport, c_data_transport; \
streaming chan c_audiohw; streaming chan c_dfu; interface c1_led_ctrl_if i_c1_led_ctrl; \
chan c_usb_to_io; chan c_io_to_usb; chan c_io_to_dspL; chan c_dspL_to_io; chan c_io_to_dspR; chan c_dspR_to_io; \
chan c_erase;
@@ -66,15 +67,10 @@ extern void fps2_dsp_proc_task(void);
#else
#define USER_MAIN_CORES on tile[0]: {\
dnr_dsp_proc_task();\
}\
on tile[0]: {\
unsafe { \
uc_audiohw = (chanend) c_audiohw;\
uc_br_data = (chanend) c_data_transport;\
uc_dfu = (chanend) c_dfu;\
} \
validate_algo(c_validate); \
}\
on tile[0]: {\
AudioHwRemote(c_audiohw, i_c1_led_ctrl, c_erase, c_dfu);\