diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/CMakeLists.txt b/sw_usb_audio/app_usb_aud_fosi_c1_v71/CMakeLists.txt new file mode 100644 index 0000000..e55d34e --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/CMakeLists.txt @@ -0,0 +1,163 @@ +cmake_minimum_required(VERSION 3.21) +include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/mode_cal.cmake) +project(phaten_module) + +set(APP_HW_TARGET xu316_qf60.xn) +include(${CMAKE_CURRENT_LIST_DIR}/../deps.cmake) +#set(APP_PCA_ENABLE ON) + +set(SW_USB_VERSION -DBCD_DEVICE_J=1 + -DBCD_DEVICE_M=0 + -DBCD_DEVICE_N=0) + +set(SW_FACT_VERSION -DBCD_DEVICE_J=5 + -DBCD_DEVICE_M=5 + -DBCD_DEVICE_N=7) + +set(SW_USB_AUDIO_FLAGS ${EXTRA_BUILD_FLAGS} ${SW_USB_VERSION} -O3 + -report + -lquadflash + -g + -lotp3 + -DUART_TX_VALUE=0x4 + -DUSB_TILE=tile[0] + -DADAT_TX_USE_SHARED_BUFF=1 + -DXUA_QUAD_SPI_FLASH=1) + + + +set(SW_FACT_AUDIO_FLAGS ${EXTRA_BUILD_FLAGS} ${SW_FACT_VERSION} + -O3 + -report + -lquadflash + -g + -lotp3 + -DUART_TX_VALUE=0x4 + -DUSB_TILE=tile[0] + -DADAT_TX_USE_SHARED_BUFF=1 + -DXUA_QUAD_SPI_FLASH=1) + + + +LINK_DIRECTORIES(${CMAKE_CURRENT_LIST_DIR}/../../lib_dnr/lib_dnr) + + + + + + + + + +set(APP_COMPILER_FLAGS_v71_uac2 ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2 + -DI2S_CHANS_ADC=2 + -DMIN_FREQ=48000 + -DMAX_FREQ=48000 + -DUSE_EX3D=1 + -DF3_F4_FPS_UAC2=1 + -DMIXER=0 + -DUAC2_MODE=1 + -ldnr_50ms + -llib_ex3d_all + -DEQ_EN=1 + -DDNR_ENABLE=1 + -DSTREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS=16 + -DSTREAM_FORMAT_INPUT_1_RESOLUTION_BITS=16 + -DINPUT_FORMAT_COUNT=1 + -DOUTPUT_FORMAT_COUNT=1 + -DEX3D_SF_NUM=3 + -DNUM_USB_CHAN_OUT=8 + -DNUM_USB_CHAN_IN=2 + -DNUM_EX3D_CHAN_OUT=2 + -DMIN_VOLUME=0xE000 + -DINPUT_VOLUME_CONTROL=1 + -DOUTPUT_VOLUME_CONTROL=1 + #-DDEBUG_MEMORY_LOG_ENABLED=1 + -DXUA_DFU_EN=1 + -DHID_DFU_EN=1 + -DIR_SWITCHING_MODE + -DHID_CONTROLS=1) + +set(APP_COMPILER_FLAGS_v71_uac1 ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2 + -DI2S_CHANS_ADC=2 + -DMIN_FREQ=48000 + -DAUDIO_CLASS=1 + -DMAX_FREQ=48000 + -DUSE_EX3D=1 + -DF3_F4_FPS_UAC2=1 + -DMIXER=0 + -DUAC2_MODE=1 + -ldnr_50ms + -llib_ex3d_all + -DEQ_EN=1 + -DDNR_ENABLE=1 + -DSTREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS=32 + -DSTREAM_FORMAT_INPUT_1_RESOLUTION_BITS=32 + -DINPUT_FORMAT_COUNT=1 + -DOUTPUT_FORMAT_COUNT=1 + -DEX3D_SF_NUM=3 + -DNUM_USB_CHAN_OUT=8 + -DNUM_USB_CHAN_IN=2 + -DNUM_EX3D_CHAN_OUT=2 + -DMIN_VOLUME=0xE000 + -DINPUT_VOLUME_CONTROL=1 + -DOUTPUT_VOLUME_CONTROL=1 + #-DDEBUG_MEMORY_LOG_ENABLED=1 + -DXUA_DFU_EN=1 + -DHID_DFU_EN=1 + -DIR_SWITCHING_MODE + -DHID_CONTROLS=1) + + +set(APP_INCLUDES src src/core src/extensions ../../lib_dnr/lib_dnr) +set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) + +XMOS_REGISTER_APP() + +###=========================================================================### +# Flash image generation +# Slot assignment (matches MODE_Fxx flag values in audiohw.xc): +# slot 1 = f3_f4_fps_uac2 (COAX_IN_FLAG = MODE_F3_F4_FPS_UAC2) +# slot 2 = f5_music_uac1 (UAC1_IN_FLAG = MODE_F5_MUSIC_UAC1) <- factory base +# slot 3 = f1_music_uac2 (OPT_IN_FLAG = MODE_F1_MUSIC_UAC2) +# slot 4 = f6_f7_fps_uac1 (USB_IN_FLAG = MODE_F6_F7_FPS_UAC1) +###=========================================================================### + +set(APP_BIN_DIR ${CMAKE_CURRENT_LIST_DIR}/bin) +set(APP_BASE ${PROJECT_NAME}) +set(XE_FACT ${APP_BIN_DIR}/fact/${APP_BASE}_fact.xe) +set(XE_F1 ${APP_BIN_DIR}/f1_music_uac2/${APP_BASE}_f1_music_uac2.xe) +set(XE_F3F4 ${APP_BIN_DIR}/f3_f4_fps_uac2/${APP_BASE}_f3_f4_fps_uac2.xe) +set(XE_F5 ${APP_BIN_DIR}/f5_music_uac1/${APP_BASE}_f5_music_uac1.xe) +set(XE_F6F7 ${APP_BIN_DIR}/f6_f7_fps_uac1/${APP_BASE}_f6_f7_fps_uac1.xe) +set(LOADER_OBJ ${CMAKE_CURRENT_LIST_DIR}/loader.o) +set(TARGET_XN ${CMAKE_CURRENT_LIST_DIR}/src/core/synido.xn) + +# factory__.bin — full factory image (base + 4 upgrade slots) +add_custom_target(factory_bin + COMMAND ${CMAKE_COMMAND} -E echo "xflash ${XE_FACT} --loader ${LOADER_OBJ} --upgrade 1 ${XE_F3F4} --upgrade 2 ${XE_F5} --upgrade 3 ${XE_F1} --upgrade 4 ${XE_F6F7} -o ${CMAKE_CURRENT_LIST_DIR}/factory_${APP_BASE}_${FW_VERSION}.bin" + COMMAND xflash ${XE_FACT} --loader ${LOADER_OBJ} + --upgrade 2 ${XE_F5} + --upgrade 3 ${XE_F1} + --upgrade 1 ${XE_F3F4} + --upgrade 4 ${XE_F6F7} + -o ${CMAKE_CURRENT_LIST_DIR}/factory_${APP_BASE}_${FW_VERSION}.bin + #DEPENDS f1_music_uac2 f3_f4_fps_uac2 f5_music_uac1 f6_f7_fps_uac1 + COMMENT "Generating factory image: factory_${APP_BASE}_${FW_VERSION}.bin" + VERBATIM +) + +# update__.bin — DFU upgrade package (upgrade slots only) +add_custom_target(update_bin + COMMAND ${CMAKE_COMMAND} -E echo "xflash --factory-version 15.2 --target-file ${TARGET_XN} --upgrade 1 ${XE_F3F4} --upgrade 2 ${XE_F5} --upgrade 3 ${XE_F1} --upgrade 4 ${XE_F6F7} -o ${CMAKE_CURRENT_LIST_DIR}/update_${APP_BASE}_${FW_VERSION}.bin" + COMMAND xflash --factory-version 15.2 --target-file ${TARGET_XN} + --upgrade 2 ${XE_F5} + --upgrade 3 ${XE_F1} + --upgrade 1 ${XE_F3F4} + --upgrade 4 ${XE_F6F7} + -o ${CMAKE_CURRENT_LIST_DIR}/update_${APP_BASE}_${FW_VERSION}.bin + #DEPENDS f1_music_uac2 f3_f4_fps_uac2 f5_music_uac1 f6_f7_fps_uac1 + COMMENT "Generating update image: update_${APP_BASE}_${FW_VERSION}.bin" + VERBATIM +) diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/core/PHATEN_GS.xn b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/PHATEN_GS.xn similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/core/PHATEN_GS.xn rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/PHATEN_GS.xn diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/xu316_qf60.xn b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/xu316_qf60.xn new file mode 100644 index 0000000..88013e0 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/xu316_qf60.xn @@ -0,0 +1,83 @@ + + + Board + XS3 MC Audio + + tileref tile[2] + tileref usb_tile + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/core/xua_conf.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/xua_conf.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/core/xua_conf.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/xua_conf.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/XUD_HAL.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/XUD_HAL.xc similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/XUD_HAL.xc rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/XUD_HAL.xc diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/agc_interface.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/agc_interface.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/agc_interface.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/agc_interface.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/aizip_dnr.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/aizip_dnr.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/aizip_dnr.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/aizip_dnr.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/audio_pipeline_dsp.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audio_pipeline_dsp.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/audio_pipeline_dsp.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audio_pipeline_dsp.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/audiohw.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiohw.xc similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/audiohw.xc rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiohw.xc diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiohw.xc.bak b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiohw.xc.bak new file mode 100644 index 0000000..bd5802d --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiohw.xc.bak @@ -0,0 +1,2534 @@ +#if UART_DEBUG || DEBUG_MEMORY_LOG_ENABLED +#define DEBUG_PRINT_ENABLE 1 +#else +#define DEBUG_PRINT_ENABLE 0 +#endif +#define DISABLE_REBOOT 1 + +#include +#include +#include +#include "xassert.h" +#include "i2c.h" +#include "xua.h" +#include +#include "xc_ptr.h" +#include "user_uart.h" +#include "debug_print.h" +#include "roleswitchflag.h" +#include "xua_hid_report.h" +#include "lfs_io.h" + +#if defined (CODEC_AIC3204) +#include "eq_flash_storage.h" +#include "codec_ti3204.h" +#else +#error "No codec selected" +#endif + +extern "C" { + #include "sw_pll.h" +} + +#include "xc_ptr.h" +unsigned g_ch0_level = 0; +unsigned g_ch1_level = 0; + +#define NAU88C25 0 + +#if NAU88C25 +#include "nau88c25.h" +#else +#include "nau88c21.h" +#endif + +#if NAU88C25 +#define NAU88_I2C_DEVICE_ADDR 0x15 +#else +#define NAU88_I2C_DEVICE_ADDR 0x1B +#endif + +#include "htr3236.h" +#include "led_control.h" +#include "xmath/scalar/f32.h" + +#include "dnr_dsp_buf.h" + +#include "xua_hid_report.h" + +#if (XUA_PCM_FORMAT == XUA_PCM_FORMAT_TDM) && (XUA_I2S_N_BITS != 32) +#warning ADC only supports TDM operation at 32 bits +#endif + +#ifndef I2S_LOOPBACK +#define I2S_LOOPBACK (0) +#endif + + +#if UAC1 +unsigned g_uac_mode = 1; // 2 for UAC2.0, 1 for UAC1.0 +#else +unsigned g_uac_mode = 2; // 2 for UAC2.0, 1 for UAC1.0 +#endif + +unsafe client interface i2c_master_if i_i2c_client; +audio_sampling g_new_playback_format, g_playback_format; +audio_type g_new_audio_type, g_audio_type = 0; +unsigned g_mute_enable = 0; +unsigned g_mute_state = 0; +unsigned g_mute_state_old = 1; +unsigned g_unmute_dac_state, g_unmute_time, g_format_time; +unsigned g_volume_level = 29, g_saved_volume_level = 29; +unsigned g_request_volume_set = 0; +unsigned g_init_saved_settings = 0; +unsigned g_host_volume = 0x0; +unsigned g_last_volume_level = 0xFF; // 上次已上报的音量级别,0xFF表示初始化未完成 +unsigned g_mic_volume_level = 37; // 麦克风PGA增益级别(0=mute, 1-37=0dB~36dB,HID可见范围) +unsigned g_request_mic_volume_set = 0; +unsigned g_last_mic_volume_level = 0xFF; // 上次已上报的麦克风增益级别,0xFF表示初始化未完成 +unsigned g_dnr_strength = 100; // AI降噪强度HID值(1-100=档位,步进1;100→-200dB最强;关断由g_dnr_on控制,此处不再用0表示强度) +// 改动原因:用户要求与强度分离的开关量;关时仍保留g_dnr_strength供0x86读取与再次开启时恢复 +unsigned g_dnr_on = 1; // AI降噪总开关(0=关仅停算法,1=开且用g_dnr_strength档位) +unsigned g_last_dnr_strength = 0xFF; // 上次已上报的降噪强度,0xFF表示首次未上报(触发开机上报) +unsigned g_last_game_mode = 0xFF; // 上次已上报的音效模式,0xFF表示首次未上报(触发开机上报) +unsigned g_request_dnr_strength_set = 0; // HID 0x85 SET_AI_NOISE_STRENGTH待处理标志 +unsigned g_monitor_switch = 0; // 耳返开关(0=关闭,1=开启),默认关闭 +unsigned g_request_monitor_switch_set = 0; // HID 0x87 SET_MONITOR_SWITCH待处理标志 +unsigned g_dac_m_gain = 0; +unsigned g_unmute_delay_time = 0; +unsigned g_format_delay_time = 0; +unsigned g_mclk_select = 0; +// 改动原因:添加LED开关全局变量,控制LED是否显示(0=关闭,1=开启) +unsigned g_led_enable = 1; // LED开关,默认开启 +// HID 0xB0 CMD_EXPAND_GAIN到达时由eq.c设置,button_task读取后更新footstep LED +// -1 (0xFFFFFFFF) 表示无待处理请求 +unsigned g_hid_expand_gain_request = (unsigned)-1; +// HID 0xB0 CMD_LMT_THRESHOLD到达时由eq.c设置,button_task读取后保存到flash +// 存储值为-threshold (0~35);-1 (0xFFFFFFFF) 表示无待处理请求 +unsigned g_hid_lmt_threshold_request = (unsigned)-1; +// HID 0xB0 CMD_ANGLE到达时由eq.c设置,button_task读取后保存到flash +// channel=0xFFFFFFFF 表示批量更新所有通道 +#define EX3D_ANGLE_CHANNELS 8 + +unsigned g_hid_angle_values[EX3D_ANGLE_CHANNELS] = {0}; +unsigned g_hid_angle_values_old[EX3D_ANGLE_CHANNELS] = { 315, 45, 0, 0, 225, 135, 270, 90 }; + +// HID 0x84 FACTORY_RESET命令到达时由eq.c设置,button_task轮询后执行重启 +unsigned g_request_factory_reset = 0; +uint32_t get_reference_time(); +unsigned g_disable_i2c = 0; +unsigned g_dac_mode = 10; +unsigned g_new_dac_mode = 0; +unsigned g_samfreq = 48000; +unsigned g_dsd_mode = 0; + +void save_value(unsigned char * unsafe path, unsigned char value); +unsigned char load_value(unsigned char * unsafe path); +void save_value32(unsigned char * unsafe path, unsigned value); +unsigned load_value32(unsigned char * unsafe path); + +// mic detect events: mute_handler (tile[1]) → button_task (tile[0]) +#define MIC_DET_MUTE 1 // mic插入或全拔出,立即mute mic +#define MIC_DET_UNMUTE 2 // 插入1s后恢复mic音量 +#define MIC_DET_DAC_MUTE 3 // mic插入时mute DAC防pop +#define MIC_DET_DAC_UNMUTE 4 // 1s后恢复DAC音量 + +unsafe chanend uc_audiohw; // tile[1] end: AudioHwConfig → button_task (tile[0]) +#if HID_DFU_EN +unsafe streaming chanend uc_dfu; // tile[1] send end: hid_button_task → button_task (tile[0]) +#endif + +static void ex3d_angle_load_defaults(unsigned angles[EX3D_ANGLE_CHANNELS]) +{ + angles[0] = 315; + angles[1] = 45; + angles[2] = 0; + angles[3] = 0; + angles[4] = 225; + angles[5] = 135; + angles[6] = 270; + angles[7] = 90; +} + +static void ex3d_angle_make_path(unsigned channel, unsigned char path[6]) +{ + path[0] = 'x'; + path[1] = '3'; + path[2] = 'd'; + path[3] = 'a'; + path[4] = (unsigned char)('1' + channel); + path[5] = 0; +} + +static void ex3d_angle_save_channel(unsigned channel, unsigned angle) +{ + unsigned char path[6]; + debug_printf("ex3d_angle_save_channel: %d, %d\n", channel, angle); + // 改动原因:角度值改为单key一次写入32位,减少4次拆分写入的flash调用,提升效率并简化流程。 + ex3d_angle_make_path(channel, path); + save_value32(path, angle); +} + +static unsigned ex3d_angle_load_channel(unsigned channel, unsigned default_angle) +{ + unsigned char path[6]; + ex3d_angle_make_path(channel, path); + unsigned angle = load_value32(path); + + if (angle == 0xFFFFFFFF) { + // 改动原因:按用户要求去掉legacy迁移与归一化,未初始化时直接使用并保存默认值,流程最简。 + ex3d_angle_save_channel(channel, default_angle); + return default_angle; + } + + // debug_printf("ex3d_angle_load_channel: %d, %d\n", channel, angle); + // 改动原因:按用户要求去掉normalize,读取后直接返回原始32位角度值。 + return angle; +} +// 改动原因:添加增益模式和滤波器模式的请求变量,用于HID命令设置模式 +unsigned g_request_game_mode = -1; + +#if EQ_EN +extern unsigned g_request_eq_mode, g_new_eq_mode; +#endif + +// tile 1, 3d_on_off +#define LED_ON 0 +#define LED_OFF 1 +#define S3D_ON LED_ON +#define S3D_OFF LED_OFF + +unsigned g_led_color = LED_OFF; +unsigned g_led_blink_count = 0; +unsigned g_led_blink_is_white = 0; + +#define LED_OFF 0x7F // 修改:清除最高位,只保留低7位 +#define LED_RED 0b01011111 // 修改:清除最高位,只保留低7位 +#define LED_GREEN 0b01101111 // 修改:清除最高位,只保留低7am +#define LED_BLUE 0b00111111 // 修改:清除最高位,只保留低7位 +#define LED_YELLOW (LED_RED & LED_GREEN) +#define LED_CYAN (LED_GREEN & LED_BLUE) +#define LED_PURPLE (LED_RED & LED_BLUE) +#define LED_WHITE (LED_RED & LED_GREEN & LED_BLUE) +enum { OS_WIN = 1, OS_OTHERS = 2 }; +// 改动原因:定义模式切换标志值,用于在SPATIAL_GAME、STEREO_8K、STEREO_2K、UAC1之间循环切换 +#define MODE_F3_F4_FPS_UAC2 COAX_IN_FLAG //1 SPATIAL_GAME模式标志 +#define MODE_F5_MUSIC_UAC1 USB_IN_FLAG // 2 F5 MUSIC +#define MODE_F1_MUSIC_UAC2 OPT_IN_FLAG // 3 F1 MUSIC +#define MODE_F6_F7_FPS_UAC1 UAC1_IN_FLAG // 4 STEREO_2K模式标志 + +/* All on tile[0] */ +port p_scl = PORT_I2C_SCL; +port p_sda = PORT_I2C_SDA; + +//out port p_htr3235_sdb = PORT_HTR3236_SDB; /* 连接到HTR3236的SDB引脚,用于控制其电源状态 */ + +out port p_ctl_mute = PORT_CTL_MUTE; +port p_ctl_det = PORT_CTL_DET; + + +/* Board setup for XU316 MC Audio (1v1) */ +void board_setup() +{ + +} + +// tile 0 +// called on tile 0 - start +#define NAU88L21_PGA_GAIN_REG_MIN_USED_VALUE 0x0 // 0x1=1, 0dB (0x0, -1dB which is not used in this design) +#define NAU88L21_PGA_GAIN_REG_MAX_VALUE 0x25+1 // 0x25=37, 36dB. +1 is for extra 1.5dB digital gain +#define NAU88L21_PGA_GAIN_REG_DEFAULT_VALUE (NAU88L21_PGA_GAIN_REG_MAX_VALUE) + +// DAC 监听音量控制范围: 共 30 级 (0~29) +// 0 = mute (全灭, reg 0x0034=0x0000), 1~29 = -28dB ~ 0dB,1dB/步 +#define DAC_LEVEL_MIN 0 // mute,0 LEDs +#define DAC_LEVEL_MAX 29 // 0dB,15 LEDs +#define DAC_LEVEL_DEFAULT DAC_LEVEL_MAX + +static inline void NAU88C22_REGREAD(unsigned reg, unsigned &val, client interface i2c_master_if i2c) +{ + i2c_regop_res_t result; + val = i2c.read_reg16(NAU88_I2C_DEVICE_ADDR, reg, result); +} + +static inline i2c_regop_res_t NAU88C22_REGWRITE(unsigned reg, unsigned val, client interface i2c_master_if i2c) +{ + i2c_regop_res_t result = i2c.write_reg16(NAU88_I2C_DEVICE_ADDR, reg, val); + + if(result != I2C_REGOP_SUCCESS) + { + debug_printf("I2C write failed: reg="); + printhex(reg); + debug_printf(", val="); + printhex(val); + debug_printf("\n"); + } + else + { + //debug_printf("I2C write success: reg="); + //printhex(reg); + //debug_printf(", val="); + //printhex(val); + //debug_printf("\n"); + //debug_printf("."); + } + + return result; +} + +void dac_volume(signed level) +{ + // 1dB/步: level 范围 -28 ~ 0,对应寄存器 0xcf-28=0xb3 ~ 0xcf + unsigned tmp = 0xcf + level; + unsafe {NAU88C22_REGWRITE(0x0034, (tmp<<8|tmp), (client interface i2c_master_if)i_i2c_client);} +} + +void mic_volume(unsigned level) +{ + if (level == 0) + { + // mute: 关闭PGA和输出放大器 + unsafe {NAU88C22_REGWRITE(0x007E, 0, (client interface i2c_master_if)i_i2c_client);} + unsafe {NAU88C22_REGWRITE(0x0035, 0, (client interface i2c_master_if)i_i2c_client);} + } + else if(level == NAU88L21_PGA_GAIN_REG_MAX_VALUE) + { + // 内部最大值 (reg=38, +1.5dB数字增益,仅firmware使用) + // PGA设到最大(37=0x25),数字增益额外+1.5dB + unsafe {NAU88C22_REGWRITE(0x007E, (NAU88L21_PGA_GAIN_REG_MAX_VALUE-1) << 8, (client interface i2c_master_if)i_i2c_client);} + unsafe {NAU88C22_REGWRITE(0x0035, (0xd2d2), (client interface i2c_master_if)i_i2c_client);} + } + else + { + // 1-37: 0dB ~ 36dB,1dB/步 + debug_printf("mic_volume write reg=%d\n", level); + unsafe {NAU88C22_REGWRITE(0x007E, (level << 8), (client interface i2c_master_if)i_i2c_client);} + unsafe {NAU88C22_REGWRITE(0x0035, (0xcfcf), (client interface i2c_master_if)i_i2c_client);} + } +} + +void codec_init(void) +{ + /* + * Setup CODEC + */ + uint16_t addr = 0, val; + for (int i = 0; i < sizeof(nau88c22_registers) / sizeof(nau88c22_registers[0]); i++) { + addr = nau88c22_registers[i][1]; + val = (nau88c22_registers[i][2] << 8) | nau88c22_registers[i][3]; + unsafe {NAU88C22_REGWRITE(addr, val, (client interface i2c_master_if)i_i2c_client);} + } + + // ADCL PGA default setting + if(NAU88L21_PGA_GAIN_REG_DEFAULT_VALUE == NAU88L21_PGA_GAIN_REG_MAX_VALUE) + unsafe {NAU88C22_REGWRITE(0x007E, (NAU88L21_PGA_GAIN_REG_MAX_VALUE-1) << 8, (client interface i2c_master_if)i_i2c_client);} + else + unsafe {NAU88C22_REGWRITE(0x007E, NAU88L21_PGA_GAIN_REG_DEFAULT_VALUE << 8, (client interface i2c_master_if)i_i2c_client);} + + //debug_printfln("Codec init finished"); +} + +in port p_mic_gain_encoder1 = PORT_MIC_GAIN_ENCODER1; // 4F2 +in port p_mic_gain_encoder2 = PORT_MIC_GAIN_ENCODER2; // 4E2 + +in port p_hp_gain_encoder = PORT_HP_GAIN_ENCODER; // 8D4, 8D5 + +in port p_button_music_mode = PORT_BUTTON_MUSIC_MODE; // 1A +in port p_button_game_mode = PORT_BUTTON_GAME_MODE; // 1L +in port p_button_ai71_onoff = PORT_BUTTON_AI71_ONOFF; // 1M + +// +// +// +// +// +// +// +// +// + +#define KEY_POLLING_INTERVAL 10000000L // 100ms polling interval +#define TIMER_PERIOD (100000) +#define FACTORY_RESET_HOLD_TICKS 3000 // 3000 × 1ms = 3秒 +#define FACTORY_RESET_BLINK_TICKS 400 // 每相400ms +#define MUTE_BLINK_HALF_PERIOD 500 // mute LED blink half period 250ms +#define BTN_COMBO_DELAY_TICKS 40 // 单键去抖/组合检测窗口(50ms) + +unsigned g_windows_detect_done = 0; + +#define MUTE_ON 1 +#define MUTE_OFF 0 + +extern unsigned g_host_os; // 1 -> Windows, 2 -> Others +extern unsigned g_mute_on_off_t0; +extern unsigned g_uac_vol; +#define DNR_STRENGTH_off 10 +extern void dnr_set_mode(unsigned char mode); +extern void dnr_set_strength_level(unsigned char strength); + +extern void device_reboot(void); + + +enum {IR_OFF=0, IR_GAME=1, IR_MUSIC=2, IR_MOVIE=3, IR_7_1_GAME=4, IR_7_1_MUSIC=5, IR_7_1_MOVIE=6}; + +#ifndef HID_MAX_DATA_BYTES +#define HID_MAX_DATA_BYTES ( 64 ) +#endif +extern unsigned hidSendData[]; + +#if HID_DFU_EN +#include "dfu_upgrade.h" +/* 将HID收到的63字节固件升级START命令打包为16个unsigned word, + * 通过uc_dfu streaming channel发送到button_task (tile[0])执行。 + * 从eq.c的process_send_params中对0xA7命令调用此函数。 */ +void dfu_chan_forward(unsigned char data[], unsigned len) +{ + unsafe + { + for (int i = 0; i < 16; i++) { + unsigned word = 0; + for (int j = 0; j < 4; j++) { + int idx = i * 4 + j; + if (idx < 63) { + word |= ((unsigned)data[idx]) << (j * 8); + } + } + uc_dfu <: word; + } + } +} +#endif + +void button_task(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx +#if HID_DFU_EN + , streaming chanend c_dfu_rx +#endif + , chanend c_mic_det + ) +{ + //hwtimer_t timer = hwtimer_alloc(); + unsigned time = 0; + timer tmr; + + static int uac_vol=0, last_uac_vol=0; +#if EQ_EN + + static unsigned eq_sync_counter = 0; // EQ参数存储计数器(每100ms递增,5次后保存) +#endif + // port_t p_ctrl_keys = XS1_PORT_8C; + + uint32_t port_ctrl_keys = 0, curr_ctrl_keys = 0, last_ctrl_keys = 0, keys_changed = 0; + uint32_t port_ctrl_keys2 = 0, curr_ctrl_keys2 = 0, last_ctrl_keys2 = 0, keys_changed2 = 0; + + int debounce_cnt = 0; + static unsigned isMute = 0; + uint8_t tmp = 0; + + // port_t p_button_music_mode = PORT_BUTTON_MUSIC_MODE; + // port_t p_button_game_mode = PORT_BUTTON_GAME_MODE; + // port_t p_button_ai71_onoff = PORT_BUTTON_AI71_ONOFF; + + //port_enable(p_button_music_mode); + + // 出厂默认:麦克风21dB (reg=22), 监听-14dB (level=15) + int codec_adc_pga_gain_reg_value = 22; // 21dB + + int dac_level = 15; // -14dB (DAC_LEVEL_MAX - 14 = 29 - 14 = 15) + + // mic endcoder 变量 + uint8_t prev_encode_input1 = 0; + uint8_t rotation_active = 0; + uint8_t sample_count = 0; + uint8_t last_valid_direction = 0; + uint8_t fast_rotation_mode = 0; // 快速旋转模式 + uint8_t consecutive_same_dir = 0; // 连续相同方向计数 + + // hp endcoder 变量 + uint8_t hp_prev_encode_input1 = 0; + uint8_t hp_rotation_active = 0; + uint8_t hp_sample_count = 0; + uint8_t hp_last_valid_direction = 0; + uint8_t hp_fast_rotation_mode = 0; // 快速旋转模式 + uint8_t hp_consecutive_same_dir = 0; // 连续相同方向计数 + + + + // active_mode: 0=off, 1=music (LED_MUSIC, no algorithm), + // 2=game (LED_GAME_MODE, IR_GAME), 3=AI7.1 (LED_AI7_1, IR_7_1_GAME) + unsigned active_mode = 0; + // 脚步增强3档状态: 0=关(0dB), 1=中亮/6dB, 2=高亮/12dB;出厂默认开启(12dB) + unsigned flag_footsteps_enhancement = 2; + // 出厂默认:AI降噪开启 + unsigned flag_aidenoise_onoff = 1; + unsigned dnr_strength_saved = 100; // AI降噪重新开启时恢复的强度(0x85可更新) + unsigned last_footstep_expand = 0xFF; // 上次已上报的脚步增强增益值,0xFF表示初始化未完成 + + // Buttons state + unsigned push_button_music_mode_state_old = 1; // Active low + unsigned push_button_ai71_onoff_state_old = 1; // Active low + unsigned push_button_game_mode_state_old = 1; // Active low + unsigned push_button_footsteps_enhancement_state_old = 0; // Active low + unsigned push_button_aidenoise_onoff_state_old = 1; // Active low + unsigned char saved_footstep = 0; + + + + unsigned flag_mic_mute = 0; + unsigned flag_hp_mute = 0; + unsigned mic_det_muted = 0; // mic检测导致的mute(不影响LED和g_mic_volume_level) + unsigned dac_det_muted = 0; // mic插入时临时mute DAC防pop(不影响LED) + + unsigned push_button_mic_mute_state_old = 1; // Active low + unsigned push_button_hp_mute_state_old = 1; // Active low + + // 出厂恢复长按状态机:音乐+游戏同时长按3秒 + unsigned factory_reset_hold_count = 0; // 两键同时按住计数(×1ms) + unsigned factory_reset_blink_state = 0; // 0=空闲,1-6=闪烁相位 + unsigned factory_reset_blink_tick = 0; // 当前相位内计数 + unsigned mic_mute_blink_tick = 0; + unsigned hp_mute_blink_tick = 0; + // 组合键去抖:单键动作延迟 BTN_COMBO_DELAY_TICKS 后触发,期间若另一键也按下则切换为组合模式 + unsigned btn_music_hold_ticks = 0; // music键连续按住计数 + unsigned btn_game_hold_ticks = 0; // game键连续按住计数 + unsigned btn_combo_active = 0; // 1=处于组合键模式(单键动作已抑制) + unsigned fr_inhibit = 0; // 1=出厂恢复重启后抑制状态,必须松键后才允许重新触发 + + int current_mic_led_pos = 0; + + htr3236_t htr3236_dev; + + // LED控制上下文 + led_control_t led_ctx; + + + // saved_mode / active_mode: 0=off, 1=music, 2=game (IR_GAME), 3=AI7.1 (IR_7_1_GAME) + unsigned /* char */ saved_mode; + unsigned char saved_uac1_mode = 0; + unsigned char path[] = "game_mode"; + unsigned char mic_vol_path[] = "mic_vol"; + unsigned char dac_vol_path[] = "dac_vol"; + unsigned char mic_mute_path[] = "mic_mute"; + unsigned char hp_mute_path[] = "hp_mute"; + unsigned char monitor_sw_path[] = "monitor_sw"; + unsigned char dnr_strength_path[] = "dnr_strength"; + // 改动原因:断电记忆AI降噪开关与强度分离存储(旧固件仅用dnr_strength=0表示关,启动时迁移) + unsigned char dnr_on_path[] = "dnr_on"; + unsigned host_os = OS_OTHERS; +#if DNR_ENABLE + unsigned dnr_init_flag = 0; + while (dnr_init_flag == 0) + { + GET_SHARED_GLOBAL(dnr_init_flag, g_dnr_init_flag); + asm("nop"); + } +#endif +#if USE_EX3D == 1 + unsigned ex3d_key_verified = 0; + while (ex3d_key_verified == 0) + { + GET_SHARED_GLOBAL(ex3d_key_verified, g_ex3d_key_verified); + asm("nop"); + } +#endif + + delay_milliseconds(10); + saved_mode = load_value(path); + debug_printf("Loaded game_mode from flash: %d\n", saved_mode); + +#if USE_EX3D == 1 + // 加载脚步增强状态(保存值为expand_gain: 0/6/12) + { + unsigned char footstep_path[] = "footstep"; + saved_footstep = load_value(footstep_path); + debug_printf("Loaded footstep gain from flash: %d\n", saved_footstep); + if (saved_footstep == 0) { + flag_footsteps_enhancement = 0; + } else if (saved_footstep <= 6) { + flag_footsteps_enhancement = 1; + } else if (saved_footstep <= 20) { + flag_footsteps_enhancement = 2; + } else { + // 未初始化(255)或异常值:出厂默认脚步增强开启(12dB),写入flash + saved_footstep = 12; + flag_footsteps_enhancement = 2; + save_value(footstep_path, (unsigned char)12); + debug_printf("Saved footstep gain to flash: %d\n", saved_footstep); + } + debug_printf("Loaded footstep gain from flash: %d, state=%d\n", saved_footstep, flag_footsteps_enhancement); + } + + // 加载EX3D角度,未初始化时写入默认值 + { + unsigned ex3d_angles[EX3D_ANGLE_CHANNELS]; + ex3d_angle_load_defaults(ex3d_angles); + for (int i = 0; i < EX3D_ANGLE_CHANNELS; ++i) { + g_hid_angle_values[i] = ex3d_angle_load_channel(i, ex3d_angles[i]); + // 改动原因: + // - g_hid_angle_values_old 用于后续“变更检测后再写flash”; + // - 开机从flash加载角度后,旧值必须与当前值对齐,否则首次保存请求会把未变化通道也误判为变化。 + g_hid_angle_values_old[i] = g_hid_angle_values[i]; + } + } +#endif + +#if defined(UAC1_MODE) + for(int i = 0; i < 500; i++) + { + GET_SHARED_GLOBAL(host_os, g_host_os); + if (host_os == OS_WIN) { + break; + } + delay_milliseconds(1); + } + + if (host_os == OS_WIN) { + debug_printf("Detected Windows OS (OS_WIN) saved_mode: %d\n", saved_mode); + unsigned flag = (saved_mode <= 1) ? MODE_F1_MUSIC_UAC2 : MODE_F3_F4_FPS_UAC2; + SetRoleSwitchFlag(flag); +#ifndef DISABLE_REBOOT + device_reboot(); + while (1); +#endif + } + else + { +#if F5_MUSIC_UAC1 + if (saved_mode >= 2) { + SetRoleSwitchFlag(MODE_F6_F7_FPS_UAC1); +#ifndef DISABLE_REBOOT + device_reboot(); + while (1); +#endif + } +#elif F6_F7_FPS_UAC1 + if (saved_mode <= 1) { + SetRoleSwitchFlag(MODE_F5_MUSIC_UAC1); +#ifndef DISABLE_REBOOT + device_reboot(); + while (1); +#endif + } +#endif + } +#endif + + +#if defined(UAC1_MODE) + if (saved_mode == 255) { +#if defined(F5_MUSIC_UAC1) + saved_mode = 1; // f5: default music mode +#elif defined(F6_F7_FPS_UAC1) + saved_mode = 2; // f6/f7: default game mode +#else + saved_mode = 1; // default off +#endif + save_value(path, saved_mode); + debug_printf("Saved game_mode to flash: %d\n", saved_mode); + } +#else + if (saved_mode == 255) { + // 出厂默认:AI7.1开启(mode=3) + saved_mode = 3; + save_value(path, saved_mode); + debug_printf("Saved game_mode to flash: %d\n", saved_mode); + } +#endif + + + active_mode = (saved_mode <= 3) ? saved_mode : 0; + { + SET_SHARED_GLOBAL(g_game_mode, active_mode); + debug_printf("active_mode=%d\n", active_mode); + } + + + + // 初始化硬件 + htr3236_init(&htr3236_dev, HTR3236_ADDR_GND); + htr3236_hw_enable(&htr3236_dev); + //p_htr3235_sdb <: 1; + + unsafe + { + while(htr3236_software_wake(&htr3236_dev, (client interface i2c_master_if)i_i2c_client) != 0) { + // 错误处理 + debug_printf("HTR3236 LED driver wake failed\n"); + //return; + } + } + debug_printf("HTR3236 LED driver wake success 9\n"); + + // 设置PWM频率为22kHz (避免可闻噪声) + //htr3236_set_frequency(i2c, HTR3236_FREQ_22KHZ); + unsafe + { + while(htr3236_set_frequency(&htr3236_dev, (client interface i2c_master_if)i_i2c_client, HTR3236_FREQ_22KHZ) != 0) + { + debug_printf("HTR3236 set frequency failed\n"); + } + } + debug_printf("HTR3236 set frequency success 9\n"); + + // 初始化LED控制 + unsafe{led_control_init(&led_ctx, &htr3236_dev, (client interface i2c_master_if)i_i2c_client);} + + // led_on(&led_ctx, led_l_physical_map[0]); + // delay_milliseconds(100); + // led_update_all(&led_ctx); + + // led_on(&led_ctx, led_l_physical_map[1]); + // delay_milliseconds(100); + // led_update_all(&led_ctx); + + // led_on(&led_ctx, led_l_physical_map[2]); + // delay_milliseconds(100); + // led_update_all(&led_ctx); + + //led_waterfall(&led_ctx, 50); + + + // 从 Flash 恢复 mic/dac 音量 + { + unsigned char saved_mic = load_value(mic_vol_path); + if(saved_mic >= NAU88L21_PGA_GAIN_REG_MIN_USED_VALUE && saved_mic <= NAU88L21_PGA_GAIN_REG_MAX_VALUE) + codec_adc_pga_gain_reg_value = saved_mic; + else + save_value(mic_vol_path, (unsigned char)codec_adc_pga_gain_reg_value); // 出厂默认22(21dB) + + unsigned char saved_dac = load_value(dac_vol_path); + if(saved_dac <= DAC_LEVEL_MAX) + dac_level = saved_dac; + else + save_value(dac_vol_path, (unsigned char)dac_level); // 出厂默认15(-14dB) + + // 从Flash恢复耳返开关(默认关闭) + unsigned char saved_monitor_sw = load_value(monitor_sw_path); + if(saved_monitor_sw == 1) + g_monitor_switch = 1; + else if(saved_monitor_sw == 255) { + // 未初始化:出厂默认关闭 + g_monitor_switch = 0; + save_value(monitor_sw_path, (unsigned char)0); + } else { + g_monitor_switch = 0; + } + debug_printf("Loaded monitor_switch from flash: %d\n", g_monitor_switch); + + { + unsigned char saved_dnr = load_value(dnr_strength_path); + unsigned char saved_on = load_value(dnr_on_path); + // 改动原因:强度文件只存2–100偶数档位;开关存dnr_on;兼容旧数据saved_dnr==0表示关且未单独存开关 + if (saved_dnr == 255) { + g_dnr_strength = 100; + dnr_strength_saved = 100; + g_dnr_on = 1; + flag_aidenoise_onoff = 1; + save_value(dnr_strength_path, (unsigned char)100); + if (saved_on == 255) + save_value(dnr_on_path, (unsigned char)1); + } else if (saved_dnr == 0) { + g_dnr_strength = 100; + dnr_strength_saved = 100; + g_dnr_on = 0; + flag_aidenoise_onoff = 0; + save_value(dnr_strength_path, (unsigned char)100); + save_value(dnr_on_path, (unsigned char)0); + } else if (saved_dnr <= 100) { + g_dnr_strength = saved_dnr; + dnr_strength_saved = saved_dnr; + if (saved_on == 0) { + g_dnr_on = 0; + flag_aidenoise_onoff = 0; + } else { + g_dnr_on = 1; + flag_aidenoise_onoff = 1; + if (saved_on == 255) + save_value(dnr_on_path, (unsigned char)1); + } + } else { + g_dnr_strength = 100; + dnr_strength_saved = 100; + g_dnr_on = 1; + flag_aidenoise_onoff = 1; + save_value(dnr_strength_path, (unsigned char)100); + save_value(dnr_on_path, (unsigned char)1); + } + debug_printf("Loaded dnr from flash: strength=%d saved=%d on=%d key_onoff=%d\n", + g_dnr_strength, dnr_strength_saved, g_dnr_on, flag_aidenoise_onoff); + } + + // 从Flash恢复mic_mute状态(默认关闭=未静音) + unsigned char saved_mic_mute = load_value(mic_mute_path); + if(saved_mic_mute == 1) + flag_mic_mute = 1; + else { + flag_mic_mute = 0; + if(saved_mic_mute == 255) + save_value(mic_mute_path, (unsigned char)0); + } + debug_printf("Loaded mic_mute from flash: %d\n", flag_mic_mute); + + // 从Flash恢复hp_mute状态(默认关闭=未静音) + unsigned char saved_hp_mute = load_value(hp_mute_path); + if(saved_hp_mute == 1) + flag_hp_mute = 1; + else { + flag_hp_mute = 0; + if(saved_hp_mute == 255) + save_value(hp_mute_path, (unsigned char)0); + } + debug_printf("Loaded hp_mute from flash: %d\n", flag_hp_mute); + } + + codec_init(); + // 同步全局音量变量与从Flash恢复的dac_level / codec_adc_pga_gain_reg_value + g_volume_level = dac_level; + // 麦克风HID范围上限37 (register=38的+1.5dB仅firmware内部使用) + g_mic_volume_level = (codec_adc_pga_gain_reg_value <= 37) ? codec_adc_pga_gain_reg_value : 37; + + // ADCL PGA default setting(mic_volume内部已根据g_monitor_switch决定是否启用耳返通路) + // 若mic_mute已从flash恢复为1,则此处先以正常gain调用,再用mute覆盖 + mic_volume(codec_adc_pga_gain_reg_value); + { + int mic_led_count = flag_mic_mute ? 15 : mic_gain_to_led[codec_adc_pga_gain_reg_value]; + for(int i = 0; i < 15; i++) + { + if (i < mic_led_count) + led_on(&led_ctx, led_l_physical_map[i]); + else + led_off(&led_ctx, led_l_physical_map[i]); + } + } + // 应用从flash恢复的mic_mute状态 + if(flag_mic_mute) { + mic_volume(0); + g_mic_volume_level = 0; + mic_mute_blink_tick = 1; + } + + // 硬件耳返通路已禁用,改为数字监听(在tile1 UserBufferManagement中实现) + // 初始化同步在上方已通过 cc_mic_level <: 0xFE 完成 + + // DAC 默认音量(mute 时写 0x0000) + if(dac_level == DAC_LEVEL_MIN) + unsafe { NAU88C22_REGWRITE(0x0034, 0x0000, (client interface i2c_master_if)i_i2c_client); } + else + dac_volume(dac_level - DAC_LEVEL_MAX); + { + int dac_led_count = flag_hp_mute ? 15 : dac_gain_to_led[dac_level]; + for(int i = 0; i < 15; i++) + { + if (i < dac_led_count) + led_on(&led_ctx, led_d_physical_map[i]); + else + led_off(&led_ctx, led_d_physical_map[i]); + } + } + // 应用从flash恢复的hp_mute状态 + if(flag_hp_mute) { + unsafe { NAU88C22_REGWRITE(0x0034, 0x0000, (client interface i2c_master_if)i_i2c_client); } + g_volume_level = 0; + hp_mute_blink_tick = 1; + } + // Initialize mode button LEDs: at most one may be lit at a time + led_off(&led_ctx, LED_MUSIC); + led_off(&led_ctx, LED_GAME_MODE); + led_off(&led_ctx, LED_AI7_1); + switch (active_mode) { + case 1: led_on(&led_ctx, LED_MUSIC); break; + case 2: led_on(&led_ctx, LED_GAME_MODE); break; + case 3: led_on(&led_ctx, LED_AI7_1); break; + default: break; + } + led_update_all(&led_ctx); + + + unsigned hidData0; + delay_milliseconds(200); + + debug_printf("button task start\n"); + + + { + cc_mic_level <: 0xFC; + cc_mic_level <: active_mode; + debug_printf("Sent sound_effect_mode %d to hid_button_task\n", active_mode); + } + +#if USE_EX3D == 1 + // 发送初始脚步增强expand_gain到tile1 + { + cc_mic_level <: 0xFD; + cc_mic_level <: (unsigned) saved_footstep; + debug_printf("set init expand_gain %d to hid_button_task\n", saved_footstep); + } + + // 加载枪声阈值并同步到tile1 + // 存储格式: -threshold (0~35, 0=threshold=0dB), 255=未初始化 + { + unsigned char lmt_thresh_path[] = "lmt_thresh"; + unsigned char raw = load_value(lmt_thresh_path); + int init_threshold; + if (raw == 255) { + // 未初始化,使用默认值-15 + init_threshold = -15; + save_value(lmt_thresh_path, (unsigned char)15); + } else if (raw <= 36) { + // raw=0~35 全部有效: 0=threshold=0dB(不限幅), 35=threshold=-35dB(最大限幅) + init_threshold = -(int)(raw - 1); + } else { + init_threshold = -15; + } + cc_mic_level <: 0xFA; + cc_mic_level <: (unsigned)init_threshold; + debug_printf("Loaded lmt_threshold=%d from flash (raw=%d), sent to tile1\n", init_threshold, raw); + } + + // 同步EX3D角度到tile1 + { + cc_mic_level <: 0xF9; + for (int i = 0; i < EX3D_ANGLE_CHANNELS; ++i) { + cc_mic_level <: g_hid_angle_values[i]; + debug_printf("angle[%d] = %d\n", i, g_hid_angle_values[i]); + } + } + + // 恢复脚步增强LED初始状态 + if (flag_footsteps_enhancement == 1) { + led_on(&led_ctx, LED_FOOTSTEP_MODE); + led_update_all(&led_ctx); + } else if (flag_footsteps_enhancement == 2) { + led_set_brightness(&led_ctx, LED_FOOTSTEP_MODE, 128); + led_update_all(&led_ctx); + } +#endif + + // 初始化数字监听开关同步到tile1 + cc_mic_level <: 0xFE; + cc_mic_level <: (unsigned)g_monitor_switch; + debug_printf("Init monitor sync: sw=%d\n", g_monitor_switch); + + // 出厂默认:AI降噪由g_dnr_on决定,关时强度变量仍保留供再次开启 +#if DNR_ENABLE == 1 + if (g_dnr_on) { + led_on(&led_ctx, LED_ANC); + led_update_all(&led_ctx); + dnr_set_mode(1); + dnr_set_strength_level((unsigned char)g_dnr_strength); + } else { + led_off(&led_ctx, LED_ANC); + led_update_all(&led_ctx); + dnr_set_mode(1); + dnr_set_strength_level(DNR_STRENGTH_off); + } +#endif + + // port_enable(p_ctrl_keys); + port_ctrl_keys = 0;//port_in(p_ctrl_keys); + if ((port_ctrl_keys & KEY_MUTE) == 0) { + // mute switch off (unmuted) + debug_printf("unmuted\n"); + isMute = 0; + SET_SHARED_GLOBAL(g_mute_on_off_t0, MUTE_OFF); // 初始化tile0静音状态 + } else { + // mute switch on (muted) + debug_printf("muted\n"); + isMute = 1; + SET_SHARED_GLOBAL(g_mute_on_off_t0, MUTE_ON); // 初始化tile0静音状态 + } + + last_ctrl_keys = ((port_ctrl_keys) & KEY_BITS); + last_ctrl_keys ^= KEY_MUTE; + + + // 启动时若两键同时按住,设置抑制标志,必须松开后才允许触发出厂恢复 + { + unsigned music_init, game_init; + p_button_music_mode :> music_init; + p_button_game_mode :> game_init; + if (music_init == 0 && game_init == 0) { + fr_inhibit = 1; + btn_combo_active = 1; + debug_printf("Boot: both keys held, FR inhibited until release\n"); + } + } + + //hwtimer_set_trigger_time(timer, hwtimer_get_time(timer) + KEY_POLLING_INTERVAL); + tmr :> time; /* Input the time */ + time += TIMER_PERIOD; /* Increment the time */ + + g_windows_detect_done = 1; + while (1) { + select + { + case c_hidSendData :> hidData0: + { + if (hidData0 == 0xffffffff) { + debug_printf("receive end data\n"); + + // 注意:UAC1模式下不会收到此命令(hid_button_task不发送) + // 读取当前g_game_mode值 + unsigned current_mode;// = chan_in_word(c_hidSendData); + c_hidSendData :> current_mode; + + + // 循环切换模式:0(STEREO_2K) -> 1(SPATIAL_GAME) -> 2(SPATIAL_MOVIE) -> 3(无音效) -> 0 + unsigned new_mode; + unsigned mode_flag; + debug_printf("current_mode: %d\n", current_mode); + if (current_mode == 0) { + // 当前是STEREO_2K,切换到STEREO_2K + new_mode = 1; +#if IR_SWITCHING_MODE + mode_flag = IR_GAME; +#else + // mode_flag = MODE_STEREO_2K; +#endif + debug_printf("Switching from STEREO_2K (0) to STEREO_2K (1)\n"); + } else if (current_mode == 1) { + // 当前是STEREO_2K,切换到SPATIAL_GAME + new_mode = 2; +#if IR_SWITCHING_MODE + mode_flag = IR_7_1_GAME; +#else + // mode_flag = MODE_SPATIAL_GAME; +#endif + debug_printf("Switching from STEREO_2K (1) to SPATIAL_GAME (2)\n"); + } else if (current_mode == 2) { + // 当前是SPATIAL_GAME,切换到SPATIAL_MOVIE + new_mode = 3; +#if IR_SWITCHING_MODE + mode_flag = IR_7_1_MOVIE; +#else + // mode_flag = MODE_SPATIAL_MOVIE; // SPATIAL_MOVIE模式使用SPATIAL_MOVIE固件 +#endif + debug_printf("Switching from SPATIAL_GAME (2) to SPATIAL_MOVIE (3)\n"); + } // 更新g_game_mode + else if(current_mode == 3) { + new_mode = 0; +#if IR_SWITCHING_MODE + mode_flag = IR_OFF; +#else + // mode_flag = MODE_STEREO_2K; +#endif + debug_printf("Switching from SPATIAL_MOVIE (3) to STEREO_2K (0)\n"); + } + + + unsigned char path[] = "game_mode"; + save_value(path, (unsigned char)new_mode); + debug_printf("Saved game_mode %d to flash\n", new_mode); + delay_milliseconds(20); + + + // 只有在需要切换固件时才reboot,同一固件内的模式切换(如0<->1)只更新模式和算法状态 +#if !UAC1_MODE + +#if IR_SWITCHING_MODE + debug_printf("Same firmware mode (0x%04X), updating mode and algorithm state without reboot\n", mode_flag); + SET_SHARED_GLOBAL(g_game_mode, new_mode); + //chan_out_byte(cc_mic_level, 0xFC); // 音频模式传输命令 + //chan_out_byte(cc_mic_level, new_mode); // 音频模式值 + cc_mic_level <: 0xFC; + cc_mic_level <: new_mode; + debug_printf("Sent audio_mode %d to hid_button_task via cc_mic_level channel\n", new_mode); +#else + if (new_mode != 1) { + + debug_printf("Set role switch flag: 0x%04X, switching firmware, rebooting...\n", mode_flag); + SetRoleSwitchFlag(mode_flag); + delay_milliseconds(20); +#ifndef DISABLE_REBOOT + device_reboot(); + while (1); +#endif + } else { + + debug_printf("Same firmware mode (0x%04X), updating mode and algorithm state without reboot\n", mode_flag); + SET_SHARED_GLOBAL(g_game_mode, new_mode); + + // chan_out_byte(cc_mic_level, 0xFC); // 音频模式传输命令 + // chan_out_byte(cc_mic_level, new_mode); // 音频模式值 + cc_mic_level <: 0xFC; + cc_mic_level <: new_mode; + debug_printf("Sent audio_mode %d to hid_button_task via cc_mic_level channel\n", new_mode); + } +#endif +#endif + break; + } + +#if HID_DFU_EN + if (!g_in_fw_upgrade) +#endif + { +#if (HID_CONTROLS == 1) + unsigned *reportData = hidSendData; + reportData[0] = hidData0; + for (int i=1; i<(HID_MAX_DATA_BYTES/4); i++) { + c_hidSendData :> reportData[i]; + } + hidSetChangePending(1); +#endif + } + break; + } + + case c_audiohw_rx :> unsigned new_samfreq: + { + // 采样率变化通知,来自AudioHwConfig (tile[1]),通过uc_audiohw专用通道传递 + unsigned new_dsd_mode; + c_audiohw_rx :> new_dsd_mode; + debug_printf("SampRate->tile0: %uHz dsd=%u\n", new_samfreq, new_dsd_mode); + + unsafe { +#if !UAC1_MODE + switch (new_samfreq) { + case 192000: + case 176400: + NAU88C22_REGWRITE(0x0003, 0x00D0, (client interface i2c_master_if)i_i2c_client); + NAU88C22_REGWRITE(0x002B, 0x4000, (client interface i2c_master_if)i_i2c_client); + + NAU88C22_REGWRITE(0x002C, 0x0082, (client interface i2c_master_if)i_i2c_client); + break; + case 96000: + case 88200: + NAU88C22_REGWRITE(0x0003, 0x0092, (client interface i2c_master_if)i_i2c_client); + NAU88C22_REGWRITE(0x002B, 0x4001, (client interface i2c_master_if)i_i2c_client); + NAU88C22_REGWRITE(0x002C, 0x0082, (client interface i2c_master_if)i_i2c_client); + break; + case 44100: + case 48000: + default: + NAU88C22_REGWRITE(0x0003, 0x0053, (client interface i2c_master_if)i_i2c_client); + NAU88C22_REGWRITE(0x002B, 0x4002, (client interface i2c_master_if)i_i2c_client); + NAU88C22_REGWRITE(0x002C, 0x0082, (client interface i2c_master_if)i_i2c_client); + break; + } +#endif +#if 0 + unsigned val; + + NAU88C22_REGREAD(0x0003, val, (client interface i2c_master_if)i_i2c_client); + debug_printf("NAU88C22_REGREAD(0x0003): 0x%08x\n", val); + NAU88C22_REGREAD(0x002B, val, (client interface i2c_master_if)i_i2c_client); + debug_printf("NAU88C22_REGREAD(0x002B): 0x%08x\n", val); + NAU88C22_REGREAD(0x002C, val, (client interface i2c_master_if)i_i2c_client); + debug_printf("NAU88C22_REGREAD(0x002C): 0x%08x\n", val); +#endif + } + c_audiohw_rx <: 0xff; + +#if HID_CONTROLS > 0 +#if HID_DFU_EN + if (!g_in_fw_upgrade) +#endif + { + unsafe { + unsigned char * unsafe ptr = (unsigned char * unsafe)hidSendData; + ptr[0] = 1; + ptr[1] = 0x77; + ptr[2] = 0x9F; + ptr[3] = (unsigned char)(new_samfreq & 0xFF); + ptr[4] = (unsigned char)((new_samfreq >> 8) & 0xFF); + ptr[5] = (unsigned char)((new_samfreq >> 16) & 0xFF); + ptr[6] = (unsigned char)((new_samfreq >> 24) & 0xFF); + ptr[7] = (unsigned char)new_dsd_mode; + for (int i = 8; i < HID_MAX_DATA_BYTES; i++) + ptr[i] = 0x00; + } + hidSetChangePending(0x1); + } +#endif + break; + } + +#if HID_DFU_EN + case c_dfu_rx :> unsigned dfu_first_word: + { + // 接收来自tile[1] hid_button_task转发的固件升级START命令(16个word) + unsigned dfu_words[16]; + dfu_words[0] = dfu_first_word; + for (int i = 1; i < 16; i++) + c_dfu_rx :> dfu_words[i]; + unsigned char cmd_buf[63]; + for (int i = 0; i < 63; i++) + cmd_buf[i] = (dfu_words[i / 4] >> ((i % 4) * 8)) & 0xFF; + if (cmd_buf[1] == FIRMWARE_UPGRADE_START) + handle_firmware_upgrade_start(cmd_buf, 63); + break; + } +#endif + + case c_mic_det :> unsigned mic_det_cmd: + { + if (mic_det_cmd == MIC_DET_MUTE) { + mic_det_muted = 1; + // 仅mute硬件,不改变g_mic_volume_level和LED + mic_volume(0); + debug_printf("mic_det: mute (det)\n"); + } else if (mic_det_cmd == MIC_DET_UNMUTE) { + mic_det_muted = 0; + // 恢复音量,但如果用户手动mute了则不恢复 + if (!flag_mic_mute) { + mic_volume(codec_adc_pga_gain_reg_value); + debug_printf("mic_det: unmute -> pga=%d\n", codec_adc_pga_gain_reg_value); + } + } else if (mic_det_cmd == MIC_DET_DAC_MUTE) { + dac_det_muted = 1; + // mute DAC硬件,不改变dac_level/g_volume_level和LED + unsafe { NAU88C22_REGWRITE(0x0034, 0x0000, (client interface i2c_master_if)i_i2c_client); } + debug_printf("mic_det: dac mute\n"); + } else if (mic_det_cmd == MIC_DET_DAC_UNMUTE) { + dac_det_muted = 0; + // 恢复DAC音量,但如果用户手动hp_mute或dac_level==0则不恢复 + if (!flag_hp_mute && dac_level > DAC_LEVEL_MIN) { + dac_volume(dac_level - DAC_LEVEL_MAX); + debug_printf("mic_det: dac unmute -> level=%d\n", dac_level); + } + } + break; + } + + case tmr when timerafter(time) :> void : + { + //hwtimer_change_trigger_time(timer, hwtimer_get_time(timer) + KEY_POLLING_INTERVAL); + time += TIMER_PERIOD; + + + //hidData0 = 0; + +// port_t p_button_music_mode = PORT_BUTTON_MUSIC_MODE; +// port_t p_button_game_mode = PORT_BUTTON_GAME_MODE; +// port_t p_button_ai71_onoff = PORT_BUTTON_AI71_ONOFF; + +//p_button_music_mode :> button_music_mode; // 1A + + unsigned mic_encoder1, mic_encoder2, hp_encoder; + unsigned button_mic_mute, button_hp_mute; + unsigned button_music_mode, button_ai71_onoff, button_game_mode, button_footsteps_enhancement, button_aidenoise_onoff; + unsigned mode_change = 0; + + //button_music_mode = port_in(p_button_music_mode); + p_button_music_mode :> button_music_mode; // 1A + p_button_game_mode :> button_game_mode; // 1L + p_button_ai71_onoff :> button_ai71_onoff; // 1M + + p_mic_gain_encoder1 :> mic_encoder1; // 4F2 + p_mic_gain_encoder2 :> mic_encoder2; // 4E2 + + button_mic_mute = ((mic_encoder1 & 0b1000) != 0); // 4F3 + //button_hp_mute = ((hp_encoder & 0b01000000) != 0); // 8D6 + + button_footsteps_enhancement = ((mic_encoder1 & 0b0010) != 0); // 4F1 + button_aidenoise_onoff = ((mic_encoder2 & 0b1000) != 0); // 4E3 + + // --- Mode button handling --- + // active_mode: 0=off, 1=music, 2=game (IR_GAME), 3=AI7.1 (IR_7_1_GAME) + // + // Boot chain: power-on → F5 → OS detect → route to F1/F3/F4/F5/F6/F7 + // + // Music-only firmware (F1, F5): USE_EX3D not set + // Music btn: toggle LED locally (no reboot) + // Game/AI7.1 btn: save mode + reboot (F5 will route to FPS firmware) + // + // FPS firmware (F3/F4, F6/F7): USE_EX3D set + // Music btn: save mode=1 + reboot (F5 will route to music firmware) + // Game/AI7.1 btn: switch IR algorithm locally (no reboot) + + unsigned mode_btn_change = 0; + unsigned need_reboot = 0; + + // --- Music + Game 按键:组合检测 + 去抖 --- + // 两键同时按住3秒 → 出厂恢复;单键需持续 BTN_COMBO_DELAY_TICKS 后生效 + // (50ms 窗口内若另一键也按下则进入组合模式,单键动作取消) + { + int music_is_down = (button_music_mode == 0); + int game_is_down = (button_game_mode == 0); + int factory_combo_is_down = ( music_is_down && game_is_down); + + // 更新各键连续按住计数(断开则清零,用于去抖和窗口判断) + if (music_is_down && !factory_combo_is_down) + { + if (btn_music_hold_ticks < 0xFFFFu) btn_music_hold_ticks++; + debug_printf("btn_music_hold_ticks=%d\n", btn_music_hold_ticks); + } + else + { + btn_music_hold_ticks = 0; + } + if (game_is_down && !factory_combo_is_down) + { + if (btn_game_hold_ticks < 0xFFFFu) + { + btn_game_hold_ticks++; + } + debug_printf("btn_game_hold_ticks=%d\n", btn_game_hold_ticks); + } + else + { + btn_game_hold_ticks = 0; + } + + // 场景1:两键同时按住(参照 fosi_ds1 else-if 结构) + if (factory_combo_is_down) { + btn_combo_active = 1; // 粘性锁定 + // fr_inhibit=1 时(出厂恢复重启后):不计时,必须松键后重新按下才触发 + if (factory_reset_blink_state == 0 && !fr_inhibit) { + factory_reset_hold_count++; + if (factory_reset_hold_count >= FACTORY_RESET_HOLD_TICKS) { + factory_reset_hold_count = 0; + factory_reset_blink_state = 1; + factory_reset_blink_tick = 0; + // 清除所有Flash设置并重启 + debug_printf("Factory reset: clearing flash " + "and rebooting\n"); + unsafe { + lfs_format_all(); + } + + led_on(&led_ctx, LED_MUSIC); + led_on(&led_ctx, LED_ANC); + led_on(&led_ctx, LED_FOOTSTEP_MODE); + led_on(&led_ctx, LED_AI7_1); + led_on(&led_ctx, LED_GAME_MODE); + led_update_all(&led_ctx); + debug_printf("Factory reset: 3s hold detected, starting blink\n"); + } + } + } + // 场景2:仅 Music 按住 + else if (music_is_down) { + factory_reset_hold_count = 0; + if (!btn_combo_active) { + if (btn_music_hold_ticks == BTN_COMBO_DELAY_TICKS) { + debug_printf("Music button pressed for %d ticks\n", BTN_COMBO_DELAY_TICKS); +#if (F3_F4_FPS_UAC2 == 1) + active_mode = 1; + need_reboot = 1; + SetRoleSwitchFlag(MODE_F1_MUSIC_UAC2); +#elif (F6_F7_FPS_UAC1 == 1) + // UAC1 FPS firmware: music needs F5, reboot if not already music + if (active_mode != 1) { + active_mode = 1; + need_reboot = 1; + SetRoleSwitchFlag(MODE_F5_MUSIC_UAC1); + } +#elif (F5_MUSIC_UAC1 == 1) + // UAC1 Music firmware: already on music FW, toggle locally + if (active_mode != 1) { + active_mode = 1; + mode_btn_change = 1; + } +#else + if (active_mode != 1) { + active_mode = 1; + mode_btn_change = 1; + } +#endif + } + } + } + // 场景3:仅 Game 按住 + else if (game_is_down) { + factory_reset_hold_count = 0; + if (!btn_combo_active) { + if (btn_game_hold_ticks == BTN_COMBO_DELAY_TICKS) { + debug_printf("Game button pressed for %d ticks\n", BTN_COMBO_DELAY_TICKS); +#if F1_MUSIC_UAC2 == 1 + active_mode = 2; + need_reboot = 1; + SetRoleSwitchFlag(MODE_F3_F4_FPS_UAC2); +#elif (F5_MUSIC_UAC1 == 1) + // UAC1 Music firmware: game needs F6/F7, reboot if not already game + if (active_mode != 2) { + active_mode = 2; + need_reboot = 1; + SetRoleSwitchFlag(MODE_F6_F7_FPS_UAC1); + } +#elif (F6_F7_FPS_UAC1 == 1) + // UAC1 FPS firmware: already on FPS FW, switch locally (IR_OFF) + if (active_mode != 2) { + active_mode = 2; + mode_btn_change = 1; + } +#else + if (active_mode != 2) { + active_mode = 2; + mode_btn_change = 1; + } +#endif + } + } + } + // 场景4:两键全松开 → 解除组合锁定及抑制标志 + else { + factory_reset_hold_count = 0; + btn_combo_active = 0; + fr_inhibit = 0; // 松键后允许下次同时按下重新触发出厂恢复 + } + } + + if (button_ai71_onoff == 0) { + if (push_button_ai71_onoff_state_old == 1) { +#if (F1_MUSIC_UAC2 == 1) + // Music-only firmware: AI7.1 needs FPS firmware + active_mode = 3; + need_reboot = 1; + SetRoleSwitchFlag(MODE_F3_F4_FPS_UAC2); +#elif (F5_MUSIC_UAC1 == 1) + // UAC1 Music firmware: AI71 needs F6/F7 FPS, reboot if not already AI71 + if (active_mode != 3) { + active_mode = 3; + need_reboot = 1; + SetRoleSwitchFlag(MODE_F6_F7_FPS_UAC1); + } +#elif (F6_F7_FPS_UAC1 == 1) + // UAC1 FPS firmware: already on FPS FW, switch locally (IR_GAME) + if (active_mode != 3) { + active_mode = 3; + mode_btn_change = 1; + } +#else + // FPS firmware: switch algorithm locally (activate if not already) + if (active_mode != 3) { + active_mode = 3; + mode_btn_change = 1; + } +#endif + } + } + push_button_ai71_onoff_state_old = button_ai71_onoff; + + // 处理HID SET_SOUND_EFFECT_MODE (0xA4) 请求,等同于按键效果 + if (!mode_btn_change && !need_reboot) { + unsigned hid_req_mode; + GET_SHARED_GLOBAL(hid_req_mode, g_request_game_mode); + if (hid_req_mode != (unsigned)-1) { + SET_SHARED_GLOBAL(g_request_game_mode, (unsigned)-1); + unsigned target = (hid_req_mode <= 3) ? hid_req_mode : 0; +#if (F3_F4_FPS_UAC2 == 1) + if (target == 1) { + active_mode = target; + need_reboot = 1; + SetRoleSwitchFlag(MODE_F1_MUSIC_UAC2); + } else { + active_mode = target; + mode_btn_change = 1; + } +#elif (F1_MUSIC_UAC2 == 1) + if (target >= 2) { + active_mode = target; + need_reboot = 1; + SetRoleSwitchFlag(MODE_F3_F4_FPS_UAC2); + } else { + active_mode = target; + mode_btn_change = 1; + } +#elif (F5_MUSIC_UAC1 == 1) + // UAC1 Music FW: mode 1 local, mode 2/3 need F6/F7 reboot + if (target >= 2) { + active_mode = target; + need_reboot = 1; + SetRoleSwitchFlag(MODE_F6_F7_FPS_UAC1); + } else if (target != active_mode) { + active_mode = target; + mode_btn_change = 1; + } +#elif (F6_F7_FPS_UAC1 == 1) + // UAC1 FPS FW: mode 2/3 local, mode 1 needs F5 reboot + if (target <= 1) { + active_mode = target; + need_reboot = 1; + SetRoleSwitchFlag(MODE_F5_MUSIC_UAC1); + } else if (target != active_mode) { + active_mode = target; + mode_btn_change = 1; + } +#else + active_mode = target; + mode_btn_change = 1; +#endif + } + } + + if (mode_btn_change || need_reboot) { + // Always persist mode to flash so boot chain restores it + { + unsigned char save_path[] = "game_mode"; + save_value(save_path, (unsigned char)active_mode); + } + + if (need_reboot) { + // Reboot: F5 will re-detect OS and route to correct firmware + debug_printf("Mode %d requires firmware switch, rebooting\n", active_mode); + delay_milliseconds(200); +#ifndef DISABLE_REBOOT + device_reboot(); + while (1); +#endif + } + + // Local update path (no reboot): update LEDs and DSP algorithm + led_off(&led_ctx, LED_MUSIC); + led_off(&led_ctx, LED_GAME_MODE); + led_off(&led_ctx, LED_AI7_1); + switch (active_mode) { + case 1: led_on(&led_ctx, LED_MUSIC); break; + case 2: led_on(&led_ctx, LED_GAME_MODE); break; + case 3: led_on(&led_ctx, LED_AI7_1); break; + default: break; + } + led_update_all(&led_ctx); + + // Always update g_game_mode so 0xA5 reads correctly + SET_SHARED_GLOBAL(g_game_mode, active_mode); +#if USE_EX3D == 1 + { + // sound_effect_mode: 0=无音效, 1=音乐, 2=游戏, 3=AI7.1 + cc_mic_level <: 0xFC; + cc_mic_level <: active_mode; + debug_printf("Mode changed: active_mode=%d\n", active_mode); + } +#endif + } + + // 出厂恢复闪烁状态机:5个按键LED闪3次后清除Flash并重启 + if (factory_reset_blink_state > 0) { + factory_reset_blink_tick++; + if (factory_reset_blink_tick >= FACTORY_RESET_BLINK_TICKS) { + factory_reset_blink_tick = 0; + factory_reset_blink_state++; + if (factory_reset_blink_state > 6) { +#if 1 + // 3次闪烁完成:恢复原始LED显示 + factory_reset_blink_state = 0; + led_off(&led_ctx, LED_MUSIC); + led_off(&led_ctx, LED_ANC); + led_off(&led_ctx, LED_FOOTSTEP_MODE); + led_off(&led_ctx, LED_AI7_1); + led_off(&led_ctx, LED_GAME_MODE); + switch (active_mode) { + case 1: led_on(&led_ctx, LED_MUSIC); break; + case 2: led_on(&led_ctx, LED_GAME_MODE); break; + case 3: led_on(&led_ctx, LED_AI7_1); break; + default: break; + } + if (flag_footsteps_enhancement == 1) led_on(&led_ctx, LED_FOOTSTEP_MODE); + else if (flag_footsteps_enhancement == 2) led_set_brightness(&led_ctx, LED_FOOTSTEP_MODE, 128); + if (flag_aidenoise_onoff) led_on(&led_ctx, LED_ANC); + led_update_all(&led_ctx); + // 等待松键,防止重启后再次触发出厂恢复循环 + { + unsigned mw, gw; + do { + delay_milliseconds(10); + p_button_music_mode :> mw; + p_button_game_mode :> gw; + } while (mw == 0 || gw == 0); + delay_milliseconds(100); + } +#endif + // 出厂恢复默认AI7.1模式(mode=3),需要F3/F4固件 + SetRoleSwitchFlag(MODE_F3_F4_FPS_UAC2); + delay_milliseconds(20); +#ifndef DISABLE_REBOOT + device_reboot(); + while (1); +#endif + } else if (factory_reset_blink_state % 2 == 0) { + // 偶数相:全灭 + led_off(&led_ctx, LED_MUSIC); + led_off(&led_ctx, LED_ANC); + led_off(&led_ctx, LED_FOOTSTEP_MODE); + led_off(&led_ctx, LED_AI7_1); + led_off(&led_ctx, LED_GAME_MODE); + led_update_all(&led_ctx); + delay_milliseconds(200); + } else { + // 奇数相:全亮 + led_on(&led_ctx, LED_MUSIC); + led_on(&led_ctx, LED_ANC); + led_on(&led_ctx, LED_FOOTSTEP_MODE); + led_on(&led_ctx, LED_AI7_1); + led_on(&led_ctx, LED_GAME_MODE); + led_update_all(&led_ctx); + delay_milliseconds(200); + } + } + } + +#if USE_EX3D == 1 + mode_change = 0; + if(button_footsteps_enhancement == 0) + { + if(push_button_footsteps_enhancement_state_old == 1) + { + // 3档循环: 0(关/0dB) → 1(中亮/6dB) → 2(高亮/12dB) → 0 + flag_footsteps_enhancement = (flag_footsteps_enhancement + 1) % 3; + mode_change = 1; + debug_printf("Footstep mode changed: %d -> %d\n", flag_footsteps_enhancement, flag_footsteps_enhancement + 1); + } + } + push_button_footsteps_enhancement_state_old = button_footsteps_enhancement; + if(mode_change) + { + if(flag_footsteps_enhancement == 0) + { + saved_footstep = 0; + led_off(&led_ctx, LED_FOOTSTEP_MODE); + } + else if(flag_footsteps_enhancement == 1) + { + saved_footstep = 6; + led_on(&led_ctx, LED_FOOTSTEP_MODE); + } + else + { + saved_footstep = 12; + led_set_brightness(&led_ctx, LED_FOOTSTEP_MODE, 128); + } + led_update_all(&led_ctx); + // 发送expand_gain到tile1执行 + cc_mic_level <: 0xFD; + cc_mic_level <: (unsigned)saved_footstep; + // 掉电保存 + unsigned char footstep_path[] = "footstep"; + save_value(footstep_path, (unsigned char)saved_footstep); + debug_printf("Footstep state=%d, expand_gain=%d\n", flag_footsteps_enhancement, saved_footstep); + } + + // HID 0xB0 CMD_EXPAND_GAIN请求:同步footstep LED状态 + { + unsigned hid_gain_req; + GET_SHARED_GLOBAL(hid_gain_req, g_hid_expand_gain_request); + if (hid_gain_req != (unsigned)-1) { + SET_SHARED_GLOBAL(g_hid_expand_gain_request, (unsigned)-1); + // 将增益值映射到footstep档位 + unsigned new_state; + if (hid_gain_req == 0) { + new_state = 0; + } else if (hid_gain_req <= 6) { + new_state = 1; + } else { + new_state = 2; + } + if (new_state != (unsigned)flag_footsteps_enhancement) { + flag_footsteps_enhancement = (int)new_state; + if (flag_footsteps_enhancement == 0) { + led_off(&led_ctx, LED_FOOTSTEP_MODE); + } else if (flag_footsteps_enhancement == 1) { + led_on(&led_ctx, LED_FOOTSTEP_MODE); + } else { + led_set_brightness(&led_ctx, LED_FOOTSTEP_MODE, 128); + } + led_update_all(&led_ctx); + } + + unsigned char fp[] = "footstep"; + save_value(fp, (unsigned char)hid_gain_req); + saved_footstep = hid_gain_req; + debug_printf("HID set footstep gain=%d, state=%d\n", hid_gain_req, flag_footsteps_enhancement); + } + } + + // HID 0xB0 CMD_LMT_THRESHOLD请求:保存阈值到flash + // eq.c以(-threshold+1)编码存入shared global,范围1~36;此处解码后存flash (0~35) + { + unsigned hid_thresh_req; + GET_SHARED_GLOBAL(hid_thresh_req, g_hid_lmt_threshold_request); + if (hid_thresh_req != (unsigned)-1) { + SET_SHARED_GLOBAL(g_hid_lmt_threshold_request, (unsigned)-1); + if (hid_thresh_req >= 1 && hid_thresh_req <= 36) { + unsigned char lmt_thresh_path[] = "lmt_thresh"; + unsigned char flash_raw = (unsigned char)(hid_thresh_req - 1); + save_value(lmt_thresh_path, flash_raw); + debug_printf("HID set lmt_threshold=-%d, saved to flash\n", (int)(hid_thresh_req - 1)); + } + } + } + + // HID 0xB0 CMD_ANGLE请求:保存角度到flash + { + for (int i = 0; i < EX3D_ANGLE_CHANNELS; ++i) + { + // 改动原因:按用户要求去掉normalize,直接比较并保存原始角度值,减少计算分支。 + unsigned angle = g_hid_angle_values[i]; + if (angle != g_hid_angle_values_old[i]) + { + ex3d_angle_save_channel(i, angle); + g_hid_angle_values_old[i] = angle; + } + } + } +#endif + + mode_change = 0; + if(button_aidenoise_onoff == 0) + { + if(push_button_aidenoise_onoff_state_old == 1) + { + flag_aidenoise_onoff ^= 1; + mode_change = 1; + } + } + push_button_aidenoise_onoff_state_old = button_aidenoise_onoff; +#if DNR_ENABLE == 1 + if(mode_change) { + if(flag_aidenoise_onoff) + { + // 改动原因:开时应用当前保存的档位g_dnr_strength,不把强度全局量写成0 + unsigned gs_on; + GET_SHARED_GLOBAL(gs_on, g_dnr_strength); + led_on(&led_ctx, LED_ANC); + dnr_set_strength_level((unsigned char)gs_on); + SET_SHARED_GLOBAL(g_dnr_on, 1); + dnr_strength_saved = gs_on; + } + else + { + // 改动原因:关时只清开关与算法档位,保留g_dnr_strength供HID 0x86与再次开启 + led_off(&led_ctx, LED_ANC); + dnr_set_strength_level(DNR_STRENGTH_off); + SET_SHARED_GLOBAL(g_dnr_on, 0); + } + save_value(dnr_on_path, (unsigned char)flag_aidenoise_onoff); + { + unsigned gs_sv; + GET_SHARED_GLOBAL(gs_sv, g_dnr_strength); + save_value(dnr_strength_path, (unsigned char)gs_sv); + } + g_dnr_on = flag_aidenoise_onoff; + led_update_all(&led_ctx); + } +#endif + + // 检查HID 0x84 FACTORY_RESET请求 + { + unsigned factory_reset_req; + GET_SHARED_GLOBAL(factory_reset_req, g_request_factory_reset); + if (factory_reset_req) { + SET_SHARED_GLOBAL(g_request_factory_reset, 0); + debug_printf("Factory reset: rebooting...\n"); + unsafe { + lfs_format_all(); + } + delay_milliseconds(500); +#ifndef DISABLE_REBOOT + device_reboot(); + while (1); +#endif + } + } + + mode_change = 0; + if(button_mic_mute == 0) + { + if(push_button_mic_mute_state_old == 1) + { + flag_mic_mute ^= 1; + mode_change = 1; + } + } + push_button_mic_mute_state_old = button_mic_mute; + if(mode_change) + { + save_value(mic_mute_path, (unsigned char)flag_mic_mute); + debug_printf("mic_mute changed: %d, saved to flash\n", flag_mic_mute); + } + if(mode_change) + if(flag_mic_mute) + { + mic_volume(0); + g_mic_volume_level = 0; + for(int i = 0; i < 15; i++) + led_on(&led_ctx, led_l_physical_map[i]); + mic_mute_blink_tick = 1; + } + else + { + if (!mic_det_muted) + mic_volume(codec_adc_pga_gain_reg_value); + g_mic_volume_level = (codec_adc_pga_gain_reg_value <= 37) ? codec_adc_pga_gain_reg_value : 37; + mic_mute_blink_tick = 0; + for(int i = 0; i < 15; i++) + led_off(&led_ctx, led_l_physical_map[i]); + current_mic_led_pos = mic_gain_to_led[codec_adc_pga_gain_reg_value] - 1; + for(int i = 0; i <= current_mic_led_pos; i++) + { + if ((i < 15) && (i >= 0)) + led_on(&led_ctx, led_l_physical_map[i]); + } + } + uint8_t rotating = (mic_encoder2 & 0b0100) == 0; // 4E2 + uint8_t encode_input1 = ((mic_encoder1 & 0b0100) != 0); // 4F2 + + if(rotating) { + if(!rotation_active) { + // 旋转开始 + prev_encode_input1 = encode_input1; + sample_count = 1; + rotation_active = 1; + + // 根据上次方向预判 + if(fast_rotation_mode) { + // 快速模式只需要2次采样 + sample_count = 2; + } + } else { + sample_count++; + + // 动态调整采样次数:快速模式用2次,普通模式用3次 + uint8_t required_samples = fast_rotation_mode ? 2 : 3; + + if(sample_count >= required_samples) { + if(encode_input1 != prev_encode_input1) { + uint8_t current_dir = encode_input1 ? 1 : 2; + + // 执行音量调节 + if(current_dir == 1) { // 顺时针 + if((codec_adc_pga_gain_reg_value < NAU88L21_PGA_GAIN_REG_MAX_VALUE) && (flag_mic_mute == 0)) { + uint8_t old_led_count = mic_gain_to_led[codec_adc_pga_gain_reg_value]; + + ++codec_adc_pga_gain_reg_value; + + if (!mic_det_muted) + mic_volume(codec_adc_pga_gain_reg_value); + g_mic_volume_level = (codec_adc_pga_gain_reg_value <= 37) ? codec_adc_pga_gain_reg_value : 37; + debug_printf("volume up %d\n", codec_adc_pga_gain_reg_value); + save_value(mic_vol_path, codec_adc_pga_gain_reg_value); + uint8_t new_led_count = mic_gain_to_led[codec_adc_pga_gain_reg_value]; + + // 如果LED数增加,点亮新增的那个LED + if(new_led_count > old_led_count) { + // 新点亮的LED索引 = new_led_count - 1 + led_on(&led_ctx, led_l_physical_map[new_led_count - 1]); + led_update_all(&led_ctx); + } + } + } else { // 逆时针 + if((codec_adc_pga_gain_reg_value > 0) && (flag_mic_mute == 0)) { + uint8_t old_led_count = mic_gain_to_led[codec_adc_pga_gain_reg_value]; + + --codec_adc_pga_gain_reg_value; + + if (!mic_det_muted) { + if (codec_adc_pga_gain_reg_value == 0) + mic_volume(0); + else + mic_volume(codec_adc_pga_gain_reg_value); + } + g_mic_volume_level = (codec_adc_pga_gain_reg_value <= 37) ? codec_adc_pga_gain_reg_value : 37; + debug_printf("volume down %d\n", codec_adc_pga_gain_reg_value); + save_value(mic_vol_path, codec_adc_pga_gain_reg_value); + + uint8_t new_led_count = mic_gain_to_led[codec_adc_pga_gain_reg_value]; + + // 如果LED数减少,关闭减少的那个LED + if(new_led_count < old_led_count) { + // 关闭的LED索引 = old_led_count - 1 + led_off(&led_ctx, led_l_physical_map[old_led_count - 1]); + led_update_all(&led_ctx); + } + } + } + + // 检测是否快速旋转 + if(current_dir == last_valid_direction) { + consecutive_same_dir++; + if(consecutive_same_dir >= 3) { + fast_rotation_mode = 1; // 进入快速模式 + } + } else { + consecutive_same_dir = 0; + fast_rotation_mode = 0; // 方向变化,退出快速模式 + } + + last_valid_direction = current_dir; + rotation_active = 0; + sample_count = 0; + } + } + } + } else { + rotation_active = 0; + sample_count = 0; + // 停止旋转一段时间后退出快速模式 + static uint16_t idle_count = 0; + idle_count++; + if(idle_count > 50) { // 50ms无旋转 + fast_rotation_mode = 0; + consecutive_same_dir = 0; + idle_count = 0; + } + } + +#if 1 + // HP Encoder + p_hp_gain_encoder :> hp_encoder; // 8D4, 8D5 + + uint8_t hp_rotating = ((hp_encoder & 0b00100000) == 0); // 8D5 + uint8_t hp_encode_input1 = ((hp_encoder & 0b00010000) != 0); // 8D4 + + if(hp_rotating) { + if(!hp_rotation_active) { + // 旋转开始 + hp_prev_encode_input1 = hp_encode_input1; + hp_sample_count = 1; + hp_rotation_active = 1; + + // 根据上次方向预判 + if(hp_fast_rotation_mode) { + // 快速模式只需要2次采样 + hp_sample_count = 2; + } + } else { + hp_sample_count++; + + // 动态调整采样次数:快速模式用2次,普通模式用3次 + uint8_t hp_required_samples = hp_fast_rotation_mode ? 2 : 3; + + if(hp_sample_count >= hp_required_samples) { + if(hp_encode_input1 != hp_prev_encode_input1) { + uint8_t hp_current_dir = hp_encode_input1 ? 1 : 2; + + // 执行音量调节 + if(hp_current_dir == 1) { // 顺时针 + if(dac_level < DAC_LEVEL_MAX && flag_hp_mute == 0) { + uint8_t old_led_count = dac_gain_to_led[dac_level]; + + // 增加音量 + ++dac_level; + g_volume_level = dac_level; + if (!dac_det_muted) + dac_volume(dac_level - DAC_LEVEL_MAX); + save_value(dac_vol_path, dac_level); + + uint8_t new_led_count = dac_gain_to_led[dac_level]; + + // 如果LED数增加,点亮新增的那个LED + if(new_led_count > old_led_count) { + // 新点亮的LED索引 = new_led_count - 1 + led_on(&led_ctx, led_d_physical_map[new_led_count - 1]); + led_update_all(&led_ctx); + } + } + } else { // 逆时针 + if(dac_level > DAC_LEVEL_MIN && flag_hp_mute == 0) { + uint8_t old_led_count = dac_gain_to_led[dac_level]; + + // 减小音量 + --dac_level; + g_volume_level = dac_level; + if (!dac_det_muted) { + if(dac_level == DAC_LEVEL_MIN) + unsafe { NAU88C22_REGWRITE(0x0034, 0x0000, (client interface i2c_master_if)i_i2c_client); } + else + dac_volume(dac_level - DAC_LEVEL_MAX); + } + save_value(dac_vol_path, dac_level); + + uint8_t new_led_count = dac_gain_to_led[dac_level]; + + // 如果LED数减少,关闭减少的那个LED + if(new_led_count < old_led_count) { + // 关闭的LED索引 = old_led_count - 1 + led_off(&led_ctx, led_d_physical_map[old_led_count - 1]); + led_update_all(&led_ctx); + } + } + } + + // 检测是否快速旋转 + if(hp_current_dir == hp_last_valid_direction) { + hp_consecutive_same_dir++; + if(hp_consecutive_same_dir >= 3) { + hp_fast_rotation_mode = 1; // 进入快速模式 + } + } else { + hp_consecutive_same_dir = 0; + hp_fast_rotation_mode = 0; // 方向变化,退出快速模式 + } + + hp_last_valid_direction = hp_current_dir; + hp_rotation_active = 0; + hp_sample_count = 0; + } + } + } + } else { + hp_rotation_active = 0; + hp_sample_count = 0; + // 停止旋转一段时间后退出快速模式 + static uint16_t hp_idle_count = 0; + hp_idle_count++; + if(hp_idle_count > 50) { // 50ms无旋转 + hp_fast_rotation_mode = 0; + hp_consecutive_same_dir = 0; + hp_idle_count = 0; + } + } + + button_hp_mute = ((hp_encoder & 0b01000000) != 0); // 8D6 + + mode_change = 0; + if(button_hp_mute == 0) + { + if(push_button_hp_mute_state_old == 1) + { + flag_hp_mute ^= 1; + mode_change = 1; + } + } + push_button_hp_mute_state_old = button_hp_mute; + if(mode_change) + { + save_value(hp_mute_path, (unsigned char)flag_hp_mute); + debug_printf("hp_mute changed: %d, saved to flash\n", flag_hp_mute); + } + if(mode_change) + if(flag_hp_mute) + { + unsafe { NAU88C22_REGWRITE(0x0034, 0x0000, (client interface i2c_master_if)i_i2c_client); } + g_volume_level = 0; + for(int i = 0; i < 15; i++) + led_on(&led_ctx, led_d_physical_map[i]); + hp_mute_blink_tick = 1; + } + else + { + if (!dac_det_muted) { + if(dac_level == DAC_LEVEL_MIN) + unsafe { NAU88C22_REGWRITE(0x0034, 0x0000, (client interface i2c_master_if)i_i2c_client); } + else + dac_volume(dac_level - DAC_LEVEL_MAX); + } + g_volume_level = dac_level; + hp_mute_blink_tick = 0; + for(int i = 0; i < 15; i++) + led_off(&led_ctx, led_d_physical_map[i]); + current_mic_led_pos = dac_gain_to_led[dac_level] - 1; + for(int i = 0; i <= current_mic_led_pos; i++) + { + if ((i < 15) && (i >= 0)) + led_on(&led_ctx, led_d_physical_map[i]); + } + } + + // mute blink: 250ms on / 250ms off + if(mic_mute_blink_tick > 0) + { + mic_mute_blink_tick++; + if(mic_mute_blink_tick >= MUTE_BLINK_HALF_PERIOD * 2) + mic_mute_blink_tick = 1; + if(mic_mute_blink_tick == 1) + for(int i = 0; i < 15; i++) led_on(&led_ctx, led_l_physical_map[i]); + else if(mic_mute_blink_tick == MUTE_BLINK_HALF_PERIOD + 1) + for(int i = 0; i < 15; i++) led_off(&led_ctx, led_l_physical_map[i]); + } + if(hp_mute_blink_tick > 0) + { + hp_mute_blink_tick++; + if(hp_mute_blink_tick >= MUTE_BLINK_HALF_PERIOD * 2) + hp_mute_blink_tick = 1; + if(hp_mute_blink_tick == 1) + for(int i = 0; i < 15; i++) led_on(&led_ctx, led_d_physical_map[i]); + else if(hp_mute_blink_tick == MUTE_BLINK_HALF_PERIOD + 1) + for(int i = 0; i < 15; i++) led_off(&led_ctx, led_d_physical_map[i]); + } + led_update_all(&led_ctx); +#endif + + GET_SHARED_GLOBAL(uac_vol, g_uac_vol); + if (uac_vol != last_uac_vol) { + unsigned conv_vol = (0xffffffff - uac_vol + 1) >> 8; + //debug_printf("vol: - %d\n", conv_vol); + //chan_out_word(c_uac_vol, conv_vol); + c_uac_vol <: conv_vol; + last_uac_vol = uac_vol; + } + + // 处理HID SET_VOLUME (0x93) 请求:应用新音量到DAC和LED + if (g_request_volume_set) { + g_request_volume_set = 0; + unsigned new_level = g_volume_level; + if (new_level <= DAC_LEVEL_MAX) { + uint8_t new_led_count; + dac_level = new_level; + if (!dac_det_muted) { + if (dac_level == DAC_LEVEL_MIN) + unsafe { NAU88C22_REGWRITE(0x0034, 0x0000, (client interface i2c_master_if)i_i2c_client); } + else + dac_volume(dac_level - DAC_LEVEL_MAX); + } + save_value(dac_vol_path, (unsigned char)dac_level); + // 更新DAC音量指示LED + new_led_count = dac_gain_to_led[dac_level]; + for (int i = 0; i < 15; i++) { + if (i < new_led_count) + led_on(&led_ctx, led_d_physical_map[i]); + else + led_off(&led_ctx, led_d_physical_map[i]); + } + led_update_all(&led_ctx); + debug_printf("HID SET_VOLUME: dac_level=%d\n", dac_level); + } + } + + // 处理HID SET_MIC_VOLUME (0x82) 请求:应用新麦克风增益到硬件和LED + if (g_request_mic_volume_set) { + g_request_mic_volume_set = 0; + unsigned new_mic_level = g_mic_volume_level; + if (new_mic_level <= 37) { // 0=mute, 1-37=0dB~36dB + uint8_t new_led_count; + if (new_mic_level == 0) { + // mute: 静音硬件,codec_adc_pga_gain_reg_value设为0(与编码器静音一致) + mic_volume(0); + codec_adc_pga_gain_reg_value = 0; + new_led_count = 0; + } else { + codec_adc_pga_gain_reg_value = new_mic_level; + if (!mic_det_muted) + mic_volume(codec_adc_pga_gain_reg_value); + new_led_count = mic_gain_to_led[codec_adc_pga_gain_reg_value]; + } + save_value(mic_vol_path, (unsigned char)codec_adc_pga_gain_reg_value); + // 更新麦克风增益指示LED(L系列) + for (int i = 0; i < 15; i++) { + if (i < new_led_count) + led_on(&led_ctx, led_l_physical_map[i]); + else + led_off(&led_ctx, led_l_physical_map[i]); + } + led_update_all(&led_ctx); + debug_printf("HID SET_MIC_VOLUME: level=%d pga=%d\n", new_mic_level, codec_adc_pga_gain_reg_value); + } + } + + // 处理HID SET_MONITOR_SWITCH (0x87) 请求:设置耳返开关 + if (g_request_monitor_switch_set) { + g_request_monitor_switch_set = 0; + unsigned new_mon = g_monitor_switch; + // 同步数字监听开关到tile1 + cc_mic_level <: 0xFE; + cc_mic_level <: (unsigned)new_mon; + debug_printf("HID monitor sync: sw=%d\n", new_mon); + save_value(monitor_sw_path, (unsigned char)new_mon); + debug_printf("HID SET_MONITOR_SWITCH: %d\n", new_mon); + } + + // 处理HID SET_AI_NOISE_STRENGTH (0x85) 请求 + // 改动原因:0x85的0仅关开关与灯并停算法,g_dnr_strength由eq.c保持原档位;非0更新强度并打开 + { + unsigned dnr_req; + GET_SHARED_GLOBAL(dnr_req, g_request_dnr_strength_set); + if (dnr_req) { + SET_SHARED_GLOBAL(g_request_dnr_strength_set, 0); + unsigned new_strength; + unsigned dnr_on_sw; + GET_SHARED_GLOBAL(new_strength, g_dnr_strength); + GET_SHARED_GLOBAL(dnr_on_sw, g_dnr_on); +#if DNR_ENABLE == 1 + if (dnr_on_sw) { + dnr_set_strength_level((unsigned char)new_strength); + flag_aidenoise_onoff = 1; + dnr_strength_saved = new_strength; + led_on(&led_ctx, LED_ANC); + } else { + dnr_set_strength_level(DNR_STRENGTH_off); + flag_aidenoise_onoff = 0; + led_off(&led_ctx, LED_ANC); + } +#endif + save_value(dnr_on_path, (unsigned char)dnr_on_sw); + save_value(dnr_strength_path, (unsigned char)new_strength); + g_dnr_on = dnr_on_sw; + led_update_all(&led_ctx); + debug_printf("HID SET_AI_NOISE_STRENGTH: strength=%d on=%d flash saved\n", + new_strength, dnr_on_sw); + } + } + +#if HID_CONTROLS == 1 + // HID监听音量变化主动上报(编码器旋转或HID命令导致的音量变化) + { + unsigned current_volume_level = g_volume_level; +#if HID_DFU_EN + if (!g_in_fw_upgrade) +#endif + if (g_last_volume_level != current_volume_level && g_last_volume_level != 0xFF) { + unsafe { + unsigned char * unsafe ptr = (unsigned char * unsafe)hidSendData; + ptr[0] = 1; + ptr[1] = 0x77; + ptr[2] = 0x94; + ptr[3] = (unsigned char)current_volume_level; + for (int i = 4; i < HID_MAX_DATA_BYTES; i++) + ptr[i] = 0x00; + } + hidSetChangePending(0x1); + debug_printf("Volume changed: %d -> %d, HID report sent\n", + g_last_volume_level, current_volume_level); + } + g_last_volume_level = current_volume_level; + } + + // HID麦克风增益变化主动上报 + { + unsigned current_mic_level = g_mic_volume_level; +#if HID_DFU_EN + if (!g_in_fw_upgrade) +#endif + if (g_last_mic_volume_level != current_mic_level && g_last_mic_volume_level != 0xFF) { + unsafe { + unsigned char * unsafe ptr = (unsigned char * unsafe)hidSendData; + ptr[0] = 1; + ptr[1] = 0x77; + ptr[2] = 0x83; + ptr[3] = (unsigned char)current_mic_level; + for (int i = 4; i < HID_MAX_DATA_BYTES; i++) + ptr[i] = 0x00; + } + hidSetChangePending(0x1); + debug_printf("Mic volume changed: %d -> %d, HID report sent\n", + g_last_mic_volume_level, current_mic_level); + } + g_last_mic_volume_level = current_mic_level; + } + + // HID AI降噪强度变化主动上报(含首次开机上报:g_last_dnr_strength初始为0xFF) + // 改动原因:按键关闭AI降噪时,协议要求上报0;开启时上报当前g_dnr_strength +#if DNR_ENABLE == 1 + { + unsigned current_dnr_strength; + unsigned current_dnr_on; + GET_SHARED_GLOBAL(current_dnr_on, g_dnr_on); + GET_SHARED_GLOBAL(current_dnr_strength, g_dnr_strength); + if (!current_dnr_on) { + current_dnr_strength = 0; + } +#if HID_DFU_EN + if (!g_in_fw_upgrade) +#endif + if (g_last_dnr_strength != current_dnr_strength) { + unsafe { + unsigned char * unsafe ptr = (unsigned char * unsafe)hidSendData; + ptr[0] = 1; + ptr[1] = 0x77; + ptr[2] = 0x86; + ptr[3] = (unsigned char)current_dnr_strength; + for (int i = 4; i < HID_MAX_DATA_BYTES; i++) + ptr[i] = 0x00; + } + hidSetChangePending(0x1); + debug_printf("DNR strength changed: %d -> %d, HID report sent\n", + g_last_dnr_strength, current_dnr_strength); + } + g_last_dnr_strength = current_dnr_strength; + } +#endif + + // HID音效模式变化主动上报(按键切换或0xA4命令导致的模式变化,含首次开机上报) + { + unsigned current_game_mode; + GET_SHARED_GLOBAL(current_game_mode, g_game_mode); +#if HID_DFU_EN + if (!g_in_fw_upgrade) +#endif + if (g_last_game_mode != current_game_mode) { + unsafe { + unsigned char * unsafe ptr = (unsigned char * unsafe)hidSendData; + ptr[0] = 1; + ptr[1] = 0x77; + ptr[2] = 0xA5; + ptr[3] = (unsigned char)current_game_mode; + for (int i = 4; i < HID_MAX_DATA_BYTES; i++) + ptr[i] = 0x00; + } + hidSetChangePending(0x1); + debug_printf("Game mode changed: %d -> %d, HID report sent\n", + g_last_game_mode, current_game_mode); + } + g_last_game_mode = current_game_mode; + } + +#if USE_EX3D == 1 + // HID脚步增强状态变化主动上报(按键切换或HID 0xB0命令导致的变化,含首次开机上报) + { +#if HID_DFU_EN + if (!g_in_fw_upgrade) +#endif + if (last_footstep_expand != saved_footstep ) { + unsafe { + unsigned char * unsafe ptr = (unsigned char * unsafe)hidSendData; + ptr[0] = 1; + ptr[1] = 0x77; + ptr[2] = 0xB1; + // EX3D GET CMD_EXPAND_GAIN = 0x193 (little-endian) + ptr[3] = 0x93; ptr[4] = 0x01; ptr[5] = 0x00; ptr[6] = 0x00; + // 返回值: expand_gain (uint32, little-endian) + ptr[7] = (unsigned char)saved_footstep; + ptr[8] = 0x00; ptr[9] = 0x00; ptr[10] = 0x00; + for (int i = 11; i < HID_MAX_DATA_BYTES; i++) ptr[i] = 0x00; + } + hidSetChangePending(0x1); + debug_printf("Footstep expand_gain changed: %d -> %d, HID 0xB1 report sent\n", + last_footstep_expand, saved_footstep); + } + last_footstep_expand = saved_footstep; + } +#endif +#endif + +#if EQ_EN + + { + unsigned request_eq_mode, new_eq_mode; + unsigned force_request_mode_change; + GET_SHARED_GLOBAL(request_eq_mode, g_request_eq_mode); + GET_SHARED_GLOBAL(new_eq_mode, g_new_eq_mode); + GET_SHARED_GLOBAL(force_request_mode_change, g_force_request_eq_mode_change); + if (new_eq_mode != request_eq_mode) + { + // debug_printf("new eq mode != request eq mode\n"); + SET_SHARED_GLOBAL(g_new_eq_mode, request_eq_mode); + } + + if (force_request_mode_change == 1) + { + debug_printf("force request mode change\n"); + SET_SHARED_GLOBAL(g_force_eq_mode_change, 1); + SET_SHARED_GLOBAL(g_force_request_eq_mode_change, 0); + } + } + + + eq_sync_counter++; + if (eq_sync_counter >= 5) { // 5 * 100ms = 500ms + eq_sync_counter = 0; + + // 使用单参数存储系统保存脏参数(独立保存EQ参数) + if (eq_save_dirty_params() == 0) { + //debug_printf("Single EQ parameters synced to Flash successfully\n"); + } else { + //debug_printf("Failed to sync single EQ parameters to Flash\n"); + } + + // 独立保存脏的增益 + if (eq_save_dirty_gain() == 0) { + // debug_printf("Gain synced to Flash successfully\n"); + } else { + //debug_printf("Failed to sync gain to Flash\n"); + } + + // 独立保存脏的模式名称 + if (eq_save_dirty_names() == 0) { + // debug_printf("Mode names synced to Flash successfully\n"); + } else { + //debug_printf("Failed to sync mode names to Flash\n"); + } + } +#endif + +#if UAC1_MODE == 1 + GET_SHARED_GLOBAL(host_os, g_host_os); + if (host_os == OS_WIN) { + unsigned flag = (saved_mode <= 1) ? MODE_F1_MUSIC_UAC2 : MODE_F3_F4_FPS_UAC2; + SetRoleSwitchFlag(flag); +#ifndef DISABLE_REBOOT + device_reboot(); + while (1); +#endif + } +#endif + break; + } + } + } +} + +void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx +#if HID_DFU_EN + , streaming chanend c_dfu_rx +#endif + , chanend c_mic_det + ) +{ + i2c_master_if i2c[1]; + board_setup(); + par { + i2c_master(i2c, 1, p_scl, p_sda, 300); + { + unsafe {i_i2c_client = i2c[0];} + button_task(c_hidSendData, cc_mic_level, c_uac_vol, c_audiohw_rx +#if HID_DFU_EN + , c_dfu_rx +#endif + , c_mic_det + ); + } + } +} + + +/* Configures the external audio hardware at startup */ +void AudioHwInit() +{ + // p_ctl_mute <: 0; + int result=0; + + sw_pll_fixed_clock(MCLK_48); + + + + // Wait for power supply to come up. + delay_milliseconds(200); +} + +/* Configures the external audio hardware for the required sample frequency */ +void AudioHwConfig(unsigned samFreq, unsigned mClk, unsigned dsdMode, unsigned sampRes_DAC, unsigned sampRes_ADC) +{ +#if UAC1_MODE == 1 + static unsigned count = 1; +#else + static unsigned count = 0; +#endif + // p_ctl_mute <: 0; + g_samfreq = samFreq; + delay_milliseconds(2); + { + sw_pll_fixed_clock(mClk); + } + // 通知tile0 (button_task) 采样率已变化,用于配置NAU88C22寄存器 + unsafe {uc_audiohw <: samFreq; } + unsafe {uc_audiohw <: dsdMode; } + + if (count++ > 0) + { + // p_ctl_mute <: 1; + } + else + { + debug_printf("don't unmute at boot\n"); + } + + unsafe {uc_audiohw :> dsdMode; } +} + + +void mute_handler(chanend c_mic_det) +{ + // 开机mute sequence + p_ctl_mute <: 0; +#if UAC1_MODE == 1 + delay_milliseconds(3000); +#else + delay_milliseconds(1000); +#endif + p_ctl_mute <: 0xf; + + // mic detect: bit1=mic1 (插入=高, 拔出=低), bit2=mic2 (插入=低, 拔出=高) + c_mic_det <: (unsigned)MIC_DET_MUTE; + unsigned det_val; + p_ctl_det :> det_val; + unsigned mic1_in = (det_val >> 1) & 1; // bit1: 1=inserted + unsigned mic2_in = ((det_val >> 2) & 1) ^ 1; // bit2: 0=inserted, invert + unsigned phone_in = ((det_val >> 3) & 1) ^ 1; // bit3: 0=inserted, invert + unsigned any_mic_in = mic1_in || mic2_in; + unsigned prev_mic1_in = mic1_in; + unsigned prev_mic2_in = mic2_in; + unsigned prev_any_mic_in = any_mic_in; + unsigned prev_phone_in = phone_in; + + // 如果开机时全拔出,发送mute + + if (((det_val & 0b1110) == 0b1110) || ((det_val & 0b1110) == 0b1010) || ((det_val & 0b1110) == 0b0010)) { + // 有mic插入,恢复mic音量状态 + c_mic_det <: (unsigned)MIC_DET_UNMUTE; + debug_printf("mic stable -> unmute\n"); + } + + timer tmr; + unsigned time; + tmr :> time; + #define MIC_DET_POLL_MS 10 + time += MIC_DET_POLL_MS * 100000; // 10ms in 100MHz ticks + + while (1) { + select { + case tmr when timerafter(time) :> void: + { + time += MIC_DET_POLL_MS * 100000; + + p_ctl_det :> det_val; + mic1_in = (det_val >> 1) & 1; + mic2_in = ((det_val >> 2) & 1) ^ 1; + phone_in = ((det_val >> 3) & 1) ^ 1; + any_mic_in = mic1_in || mic2_in; + + // 检测单个mic插拔事件 + unsigned mic1_changed = (mic1_in != prev_mic1_in); + unsigned mic2_changed = (mic2_in != prev_mic2_in); + unsigned any_changed = mic1_changed || mic2_changed; + unsigned phone_changed = (phone_in != prev_phone_in); + + if (any_changed || phone_changed) { + // 状态变化:立即mute mic和DAC防止pop音 + p_ctl_mute <: 0; + c_mic_det <: (unsigned)MIC_DET_MUTE; + // c_mic_det <: (unsigned)MIC_DET_DAC_MUTE; + + // 配置ADC + //c_mic_det <: (unsigned)MIC_DET_REINIT_CODEC; + p_ctl_det <: 0; + delay_milliseconds(100); + + //debug_printf("mic det change: mic1=%d mic2=%d -> mute all & reconfig\n", mic1_in, mic2_in); + p_ctl_det :> det_val; + + // 等1s让插拔稳定 + delay_milliseconds(900); + p_ctl_mute <: 0xf; + + // 重新读取稳定状态 + p_ctl_det :> det_val; + mic1_in = (det_val >> 1) & 1; + mic2_in = ((det_val >> 2) & 1) ^ 1; + phone_in = ((det_val >> 3) & 1) ^ 1; + any_mic_in = mic1_in || mic2_in; + + // 恢复DAC状态 + c_mic_det <: (unsigned)MIC_DET_DAC_UNMUTE; + +#if 0 + if ((any_mic_in && !phone_in) || (phone_in && mic2_in)) { + // 有mic插入,恢复mic音量状态 + c_mic_det <: (unsigned)MIC_DET_UNMUTE; + debug_printf("mic stable -> unmute\n"); + } + // 全拔出则保持mic mute + + delay_milliseconds(200); + +#endif + + if (((det_val & 0b1110) == 0b1110) || ((det_val & 0b1110) == 0b1010) || ((det_val & 0b1110) == 0b0010)) { + // 有mic插入,恢复mic音量状态 + c_mic_det <: (unsigned)MIC_DET_UNMUTE; + debug_printf("mic stable -> unmute\n"); + } + // 全拔出则保持mic mute + + prev_mic1_in = mic1_in; + prev_mic2_in = mic2_in; + prev_any_mic_in = any_mic_in; + prev_phone_in = phone_in; + // 重新同步timer + tmr :> time; + time += MIC_DET_POLL_MS * 100000; + } + break; + } + } + } +} diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/audiostream.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiostream.xc similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/audiostream.xc rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiostream.xc diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/biquad_standalone.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/biquad_standalone.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/biquad_standalone.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/biquad_standalone.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/biquad_standalone.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/biquad_standalone.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/biquad_standalone.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/biquad_standalone.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/br_wrapper.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/br_wrapper.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/br_wrapper.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/br_wrapper.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/c_dsp.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/c_dsp.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/c_dsp.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/c_dsp.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/codec_ti3204.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/codec_ti3204.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/codec_ti3204.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/codec_ti3204.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dfu_flash_interface.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dfu_flash_interface.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dfu_flash_interface.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flash_interface.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dfu_flash_interface.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flashlib_user.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dfu_flashlib_user.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_flashlib_user.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dfu_flashlib_user.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dfu_upgrade.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dfu_upgrade.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dfu_upgrade.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dfu_upgrade.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dfu_upgrade.h diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dist/eq_designer_new.exe b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dist/eq_designer_new.exe new file mode 100644 index 0000000..75da70f Binary files /dev/null and b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dist/eq_designer_new.exe differ diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/aizip_dnr.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/aizip_dnr.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/aizip_dnr.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/aizip_dnr.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/dnr_dsp.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/dnr_dsp.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/dnr_dsp.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/dnr_dsp.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/dnr_dsp_buf.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/dnr_dsp_buf.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/dnr_dsp_buf.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/dnr_dsp_buf.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/dnr_dsp_buf.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/dnr_dsp_buf.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/dnr_dsp_buf.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/dnr_dsp_buf.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/flash.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/flash.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/flash.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/flash.h diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/noise_gate_simple.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/noise_gate_simple.c new file mode 100644 index 0000000..1576cf5 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/noise_gate_simple.c @@ -0,0 +1,32 @@ +// Copyright 2026 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#include "noise_gate_simple.h" + +#define GATE_THRESHOLD_DB_MIN (-80) +#define GATE_THRESHOLD_DB_MAX (0) + +// 10^(db/20) scaled to Q1.31, 4 dB resolution. Input dB is rounded to nearest +// 4 dB on lookup. 21 entries = 84 bytes of ROM. +static const int32_t k_db_to_q31_step4[21] = { + /* -80 */ 214748, 340354, 539423, 855025, + /* -64 */ 1355287, 2147484, 3403542, 5394226, + /* -48 */ 8550255, 13552872, 21474836, 34035417, + /* -32 */ 53942258, 85502547, 135528724, 214748365, + /* -16 */ 340354167, 539422577, 855025466, 1355287238, + /* 0 */ 2147483647 +}; + +void gate_reset(simple_gate_t *g) { + g->env = 0; + g->gain = NOISE_GATE_Q31_ONE; +} + +void gate_set_threshold_db(simple_gate_t *g, int8_t db) { + if (db > GATE_THRESHOLD_DB_MAX) db = GATE_THRESHOLD_DB_MAX; + if (db < GATE_THRESHOLD_DB_MIN) db = GATE_THRESHOLD_DB_MIN; + int idx = (db - GATE_THRESHOLD_DB_MIN + 2) >> 2; // round to nearest 4 dB + if (idx < 0) idx = 0; + if (idx > 20) idx = 20; + g->threshold_q31 = k_db_to_q31_step4[idx]; +} diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/noise_gate_simple.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/noise_gate_simple.h new file mode 100644 index 0000000..8bd9cba --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/noise_gate_simple.h @@ -0,0 +1,76 @@ +// Copyright 2026 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +// +// Minimal, zero-latency Q1.31 noise gate. Coefficients are hard-coded for +// fs = 48 kHz; the struct only carries per-instance state. +// +// Algorithm: +// 1) Peak envelope tracker: single-pole EMA on |x|, separate rise/decay +// coefficients. +// 2) Hard-knee decision: target_gain = (env < threshold) ? 0 : 1.0. +// 3) Gain smoothing: EMA toward target_gain; fast when opening +// (gain rising), slow when closing (gain falling). +// 4) Output = x * gain (Q31 * Q31 -> Q31). +// Group delay: 0 samples. +// +// gate_process() lives in this header as static inline so the single call +// site in dnr_dsp.c inlines without function-call overhead — halves the +// tile-0 code footprint vs. an out-of-line implementation. + +#ifndef NOISE_GATE_SIMPLE_H +#define NOISE_GATE_SIMPLE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NOISE_GATE_Q31_ONE 0x7FFFFFFF + +// Alphas = 1 - exp(-1 / (tau * fs)) in Q1.31 at fs = 48 kHz. +#define NOISE_GATE_ENV_ATTACK_A 44291727 // 1 ms +#define NOISE_GATE_ENV_RELEASE_A 4469637 // 10 ms +#define NOISE_GATE_OPEN_A 22253774 // 2 ms (gain rising) +#define NOISE_GATE_CLOSE_A 558561 // 80 ms (gain falling) + +typedef struct { + int32_t env; // Envelope state, Q1.31 unsigned magnitude. + int32_t gain; // Smoothed gain, Q1.31 (0..INT32_MAX). + int32_t threshold_q31; // Linear threshold, Q1.31 unsigned magnitude. +} simple_gate_t; + +// Reset envelope and gain. Leaves threshold unchanged. +void gate_reset(simple_gate_t *g); + +// Set threshold in integer dBFS, clamped to [-80, 0]. Quantised to 4 dB. +void gate_set_threshold_db(simple_gate_t *g, int8_t db); + +// Single-sample gate. Inlined at the call site. +static inline int32_t gate_process(simple_gate_t *g, int32_t x) { + int32_t ax; + if (x == (int32_t)0x80000000) ax = NOISE_GATE_Q31_ONE; + else if (x < 0) ax = -x; + else ax = x; + + int32_t edelta = ax - g->env; + int32_t env_a = (edelta > 0) ? NOISE_GATE_ENV_ATTACK_A : NOISE_GATE_ENV_RELEASE_A; + int64_t estep = (int64_t)env_a * (int64_t)edelta; + g->env += (int32_t)(estep >> 31); + + int32_t target = (g->env < g->threshold_q31) ? 0 : NOISE_GATE_Q31_ONE; + + int32_t gain_a = (target > g->gain) ? NOISE_GATE_OPEN_A : NOISE_GATE_CLOSE_A; + int32_t gdelta = target - g->gain; + int64_t gstep = (int64_t)gain_a * (int64_t)gdelta; + g->gain += (int32_t)(gstep >> 31); + + int64_t y = (int64_t)x * (int64_t)g->gain; + return (int32_t)(y >> 31); +} + +#ifdef __cplusplus +} +#endif + +#endif // NOISE_GATE_SIMPLE_H diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/ringbuf.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/ringbuf.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr/ringbuf.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr/ringbuf.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr_dsp_buf.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr_dsp_buf.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dnr_dsp_buf.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dnr_dsp_buf.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dsp.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dsp.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dsp.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dsp.c diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dsp.c.bak b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dsp.c.bak new file mode 100644 index 0000000..161d2d6 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dsp.c.bak @@ -0,0 +1,1474 @@ +#define DEBUG_PRINT_ENABLE 0 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dsp_user_impl.h" +#include "DSBuild.h" +#include "filter.h" + +#include "xua_conf.h" +#include +#include "audio_ex3d_control.h" +#include "c_dsp.h" +#include +#include "xua_hid_report.h" +#include "xc_ptr.h" +#include "dnr_dsp_buf.h" +#include "roleswitchflag.h" +#include "debug_print.h" +#include "swlock.h" + +// Global flash lock defined in lfs_services.c — protects all flash hardware access +extern swlock_t flash_lock; +// 改动原因:添加user_func.h头文件,用于调用save_value和load_value函数保存/加载模式到/从flash +extern void save_value(unsigned char *path, unsigned char value); +extern unsigned char load_value(unsigned char *path); +#if EQ_EN + +#include "eq_flash_storage.h" +extern unsigned g_request_eq_mode, g_new_eq_mode; +extern unsigned g_force_request_eq_mode_change, g_force_eq_mode_change; +#endif + +extern void device_reboot(void); +unsigned g_ex3d_key_verified = 0; +int32_t sys_vol = 0; +unsigned g_mic_vol_cmd_pending = 0; +// 0xFA boot sync收到后置1;ex3d_task用此标志决定是否保留boot sync的阈值 +static volatile int g_boot_lmt_threshold_loaded = 0; +static volatile int g_boot_footstep_expand_gain_loaded = 0; +static volatile int g_boot_angle_loaded = 0; +// 改动原因:修复“主机已通过CMD_ANGLE写入,但随后启动阶段0xF9(来自flash旧值)又覆盖回默认角度”的竞态。 +// 该标志在收到任意CMD_ANGLE写入后置1;若置1,则忽略后续0xF9覆盖,确保“最后一次主机写入”为准。 +static volatile int g_runtime_angle_written = 0; +// 改动原因:修复0xF9角度同步与ex3d_task初始化的竞态。 +// 当0xF9在ex3d_task初始化完成后才到达时,旧逻辑只缓存不应用,导致GET CMD_ANGLE仍返回默认值。 +// 通过该标志判断EX3D是否已完成初始化,若已完成则在收到0xF9时立即应用角度。 +static volatile int g_ex3d_init_done = 0; +// audio_ex3d_init会覆盖Ex3dExpandGain为库默认值;用此变量保存0xFD boot sync收到的值 +static volatile int32_t g_boot_footstep_expand_gain_value = 12; +static volatile int32_t g_boot_lmt_threshold_value = -5; +static volatile uint32_t g_boot_angle_values[8] = {315, 45, 0, 0, 225, 135, 270, 90}; + +chanend_t uc_ex3d_to_ubm, uc_eq_data; +static unsigned ubm_sample_freq = 0; + +#if (STEREO_8K == 1) || (STEREO_2K == 1) +#define UBM_TO_EX3D_CHANS (2) +#define EX3D_TO_UBM_CHANS (2) +#else +#define UBM_TO_EX3D_CHANS (NUM_USB_CHAN_OUT) +#define EX3D_TO_UBM_CHANS (2) +#endif + +// tile 1, mic level +unsigned g_mic_level_t1; + + +#if defined(IR_SWITCHING_MODE) +enum {IR_OFF=0, IR_GAME=1, IR_MUSIC=2, IR_MOVIE=3, IR_7_1_GAME=4, IR_7_1_MUSIC=5, IR_7_1_MOVIE=6}; +unsigned g_3d_on_off_t1 = IR_OFF; // Default 3D off +#else +enum {A3D_OFF=0, A3D_VON=1, A3D_ON=2}; +unsigned g_3d_on_off_t1 = A3D_OFF; // Default 3D off +#endif +#define DNR_ON 1 +#define DNR_OFF 0 +#define MUTE_ON 1 +#define MUTE_OFF 0 +unsigned g_mute_on_off_t1 = MUTE_ON; + +// 数字监听 tile1 共享变量(由 tile0 通过 cc_mic_level 通道同步) +unsigned g_monitor_switch_t1 = 0; // 耳返开关 tile1 副本(0=关闭,1=开启) + +unsigned g_mute_on_off_t0 = MUTE_OFF; + +unsigned g_game_mode = 0; + +#if USE_EX3D == 1 +static void ex3d_apply_boot_angles(void) +{ + int apply_count = (NUM_USB_CHAN_OUT < 8) ? NUM_USB_CHAN_OUT : 8; + for (int i = 0; i < apply_count; ++i) { + uint32_t angle = g_boot_angle_values[i]; + EX3D_VAngle[i] = (angle >> 16) & 0xFFFF; + EX3D_HAngle[i] = angle & 0xFFFF; + EX3DAudio_SetAngle(i + 1, EX3D_VAngle[i], EX3D_HAngle[i], (i == (apply_count - 1))); + debug_printf("ex3d_apply_boot_angles: %d, %d\n", EX3D_VAngle[i], EX3D_HAngle[i]); + } +} +#endif + +// On UserBufferManagement Tile (1) +static unsigned frame_index = 0; +//static unsigned dnr_frame_idx = 0; +static int32_t ubm_egress[UBM_TO_EX3D_CHANS][DSP_BLOCK_LENGTH]; //leaving ubm +static int32_t ubm_ingress[EX3D_TO_UBM_CHANS][DSP_BLOCK_LENGTH]; //entering ubm +#if AIZIP_DNR == 1 +static int32_t ubm_micIn[DSP_CH_NUM][DNR_DSP_FRAME_SIZE]; //leaving ubm +static int32_t ubm_micOut[DSP_CH_NUM][DNR_DSP_FRAME_SIZE]; //entering ubm +#endif +chanend_t uc_dsp_to_dnr_t1; +//chanend_t uc_dsp_to_dnr_t0; +chanend_t uc_key_to_ubm_t0; +volatile EXTERN DS_BOOL m_hThread; +extern unsigned g_dnr_init_flag; +void SetEqDataChan (chanend_t c) { + uc_eq_data = c; +} +void key_receiver(chanend_t c) +{ + debug_printf("===> key_receiver\n"); +#if DNR_ENABLE + unsigned dnr_init_flag = 0; + while (dnr_init_flag == 0) { + GET_SHARED_GLOBAL(dnr_init_flag, g_dnr_init_flag); + asm("nop"); + } + +#endif + // tile0 + // load the license key + // This function must be called before audio_ex3d_activate_key. + swlock_acquire(&flash_lock); + audio_ex3d_load_key(c); + swlock_release(&flash_lock); + SET_SHARED_GLOBAL(g_ex3d_key_verified, 1); +} + +void key_sender(chanend_t c) +{ + debug_printf("===> key_sender\n"); + + // tile1 + // activate the license key + // if the keys match, the function returns 0. + int ret = audio_ex3d_activate_key(c); + if (ret == NO_ERR) { + debug_printf("License keys match.\n"); + } else { + debug_printf("License keys do not match. Error code: %d\n", ret); + } +} + +void UserBufferManagementInit(unsigned sampFreq) +{ + ubm_sample_freq = sampFreq; +#if USE_EX3D == 1 + memset(ubm_ingress, 0, sizeof(ubm_ingress)); + memset(ubm_egress, 0, sizeof(ubm_egress)); + frame_index = 0; + + assert(uc_ex3d_to_ubm); + while( !m_hThread ){} + debug_printf("[UserBufferManagementInit] samfreq:%d\n\r", sampFreq); + int ret = audio_ex3d_change_parameter(NUM_USB_CHAN_OUT, NUM_USB_CHAN_OUT * FRAME_SIZE * sizeof(AUDIO_T), sampFreq, sizeof(AUDIO_T)); + if(ret != NO_ERR) { + debug_printf("audio_ex3d_change_parameter() error:%d\n\r", ret); + } else { + debug_printf("audio_ex3d_change_parameter() success\n\r"); + } + // 改动原因:修复“上电后GET CMD_ANGLE仍返回315”的问题。 + // audio_ex3d_change_parameter 内部会把 EX3D_VAngle/EX3D_HAngle 重置为库默认值(315,45,...), + // 若 0xF9 已同步了 flash 中的角度(或主机期间已通过 CMD_ANGLE 改过),必须在此重新应用, + // 否则采样率切换/流启动时刚建立的角度会被静默覆盖,导致 GET 依旧读到默认值315。 + if (g_boot_angle_loaded || g_runtime_angle_written) { + ex3d_apply_boot_angles(); + debug_printf("UserBufferManagementInit: reapplied ex3d angles after change_parameter\n"); + } + // ② 预热:推一个零帧穿过 ex3d,触发所有 worker 加载 + 首次同步 + chan_out_buf_word(uc_ex3d_to_ubm, (const uint32_t *)ubm_egress, + UBM_TO_EX3D_CHANS * DSP_BLOCK_LENGTH); + chan_in_buf_word (uc_ex3d_to_ubm, (uint32_t *)ubm_ingress, + EX3D_TO_UBM_CHANS * DSP_BLOCK_LENGTH); + memset(ubm_ingress, 0, sizeof(ubm_ingress)); // 清掉哑帧输出 +#endif +} +float fLevel[NUM_USB_CHAN_OUT] = {0,}; +enum {UBM_A3D_OFF=0, UBM_A3D_VON=1, UBM_A3D_ON=2}; +extern void buffer_exchange(chanend_t c_data, unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[], unsigned sample_freq); +extern unsigned int is_eq_disabled(void); +extern unsigned int g_eq_enable; + +void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]) +{ + uint32_t is_3d; + unsigned is_monitor; + +#if USE_EX3D == 1 +#if defined(SPATIAL_DRAMA) || defined(SPATIAL_GAME) || defined(SPATIAL_MOVIE) || defined(SPATIAL_MUSIC) + // 6ch version + unsigned tmp[8]={0,0,0,0,0,0,0,0}, downmix[8]={0,0,0,0,0,0,0,0}; +#endif + + GET_SHARED_GLOBAL(is_3d, g_3d_on_off_t1); +#if (HID_CONTROLS > 0) + AUDIO_T absVal, maxVal; +#endif + //Frame the samples going from the usb to the DAC into frames of 8 + for(int ch=0;ch 0) + maxVal = 0; + absVal = ubm_egress[ch][frame_index]; + if(absVal < 0) absVal = -absVal; + if(maxVal < absVal) maxVal = absVal; +#if defined(AUDIO_T_16) + fLevel[ch] = (float)maxVal / 32768; +#else + fLevel[ch] = (float)maxVal / 2147483648; +#endif +#endif + } + +#if defined(SPATIAL_DRAMA) || defined(SPATIAL_GAME) || defined(SPATIAL_MOVIE) || defined(SPATIAL_MUSIC) || defined(IR_SWITCHING_MODE) + // 6ch version + // C and LFE -3dB mixed to L and R. + // L = L + C*-3dB + LFE*-3dB + // R = R + C*-3dB + LFE*-3dB + + int32_t input_L = (int32_t)sampsFromUsbToAudio[0]; + int32_t input_R = (int32_t)sampsFromUsbToAudio[1]; + int32_t input_C = (int32_t)sampsFromUsbToAudio[2]; + int32_t input_LFE = (int32_t)sampsFromUsbToAudio[3]; + + // -3dB Gain = 10^(-3/20) ~= 0.707946 + // Using 0.70710678 (1/sqrt(2)) approximation for -3dB + // Q31 factor: 0.70710678 * 2^31 = 1518500250 + const int32_t gain_minus_3db = 1518500250; + + int32_t C_weighted = (int32_t)(((int64_t)input_C * gain_minus_3db) >> 31); + int32_t LFE_weighted = (int32_t)(((int64_t)input_LFE * gain_minus_3db) >> 31); + + int64_t sum_L = (int64_t)input_L + C_weighted + LFE_weighted; + int64_t sum_R = (int64_t)input_R + C_weighted + LFE_weighted; + + // Saturation logic + if (sum_L > 2147483647) sum_L = 2147483647; + else if (sum_L < -2147483648) sum_L = -2147483648; + + if (sum_R > 2147483647) sum_R = 2147483647; + else if (sum_R < -2147483648) sum_R = -2147483648; + + // only for Stereo game, Stereo music and Stereo movie mode + if ((is_3d > IR_OFF) && (is_3d < IR_7_1_GAME)) { + // stereo mode + ubm_egress[0][frame_index] = 0; + ubm_egress[1][frame_index] = 0; + ubm_egress[2][frame_index] = 0; + ubm_egress[3][frame_index] = 0; + ubm_egress[4][frame_index] = 0; + ubm_egress[5][frame_index] = 0; + ubm_egress[6][frame_index] = (int32_t)sum_L; //SL + ubm_egress[7][frame_index] = (int32_t)sum_R; //SR + } else { + ubm_egress[0][frame_index] = (int32_t)sum_L; + ubm_egress[1][frame_index] = (int32_t)sum_R; + } + + // Zero out C (2) and LFE (3) since they are mixed into L/R + ubm_egress[2][frame_index] = 0; + ubm_egress[3][frame_index] = 0; +#endif + + for(int ch=0;ch> 16) % 181; + EX3D_HAngle[ChNum] = (angle & 0xffff) % 360; + EX3DAudio_SetAngle(ChNum+1, EX3D_VAngle[ChNum], EX3D_HAngle[ChNum], true); + // 改动原因:记录“运行期角度已被主机改写”,并同步缓存,防止后续0xF9旧值覆盖。 + g_boot_angle_values[ChNum] = ((uint32_t)EX3D_VAngle[ChNum] << 16) | (uint32_t)EX3D_HAngle[ChNum]; + g_boot_angle_loaded = 1; + g_runtime_angle_written = 1; + debug_printf("[%s] Set angle ch%d-%d,%d\n",__FUNCTION__, ChNum+1, EX3D_VAngle[ChNum], EX3D_HAngle[ChNum]); + } else { + uint8_t bApply = false; + debug_printf("[%s] Set angle(V,H):",__FUNCTION__); + ChNum = NUM_USB_CHAN_OUT; + for(int i=0; i> 16) % 181; + EX3D_HAngle[i] = (angle & 0xffff) % 360; + if(i == (ChNum - 1)) bApply = true; + EX3DAudio_SetAngle(i+1, EX3D_VAngle[i], EX3D_HAngle[i], bApply); + // 改动原因:批量写入时同样更新本地启动角度缓存,避免0xF9旧值回灌。 + g_boot_angle_values[i] = ((uint32_t)EX3D_VAngle[i] << 16) | (uint32_t)EX3D_HAngle[i]; + debug_printf(" ch%d-%d,%d,", i+1, EX3D_VAngle[i], EX3D_HAngle[i]); + } + // 改动原因:批量写入成功后置位,后续0xF9不再覆盖主机刚写的角度。 + g_boot_angle_loaded = 1; + g_runtime_angle_written = 1; + debug_printf("\n"); + } + debug_printf("Set CMD_ANGLE : %u\r\n", pRcvBuf[1]); + } else { + debug_printf("Get CMD_ANGLE : %u\r\n", pRcvBuf[1]); + + uint32_t ChNum = pRcvBuf[1]; + if (ChNum < NUM_USB_CHAN_OUT) { + angle = EX3D_VAngle[ChNum] << 16; + angle |= EX3D_HAngle[ChNum]; + pSendBuf[idx] = angle; + debug_printf("Get CMD_ANGLE : %u, %d, %d\n", ChNum, EX3D_VAngle[ChNum], EX3D_HAngle[ChNum]); + } else { + ChNum = NUM_USB_CHAN_OUT; + for (int i = 0; i < ChNum; i++) { + angle = EX3D_VAngle[i] << 16; + angle |= EX3D_HAngle[i]; + debug_printf("%08x, ", angle); + pSendBuf[idx++] = angle; + } + } + debug_printf("\r\n"); + } + break; + + case CMD_MUTE: + if (CMD_SET(CMD_MUTE) == command_code) { + uint32_t ChNum = pRcvBuf[1]; + if (ChNum < NUM_USB_CHAN_OUT) { + EX3D_Mute[ChNum] = pRcvBuf[2]; + EX3DAudio_SetMute(ChNum+1, EX3D_Mute[ChNum]); + debug_printf("[%s] Set mute ch%d-%d,%d\n",__FUNCTION__, ChNum+1, EX3D_Mute[ChNum]); + } else { + ChNum = NUM_USB_CHAN_OUT; + debug_printf("[%s] Set mute:",__FUNCTION__); + for(int i=0; i IR_GAME/IR_7_1_GAME/IR_7_1_MOVIE + const char * sfIdxName = ""; + switch (sfIdx) { + case 0: sfIdxName = "STEREO GAME"; break; + case 1: sfIdxName = "SPATIAL GAME"; break; + case 2: sfIdxName = "SPATIAL MOVIE"; break; + default: break; + } +// if (cur_mode == IR_GAME) { +// sfIdxName = spatial_game; +// } else { +// if (cur_mode == IR_MUSIC) { +// sfIdxName = spatial_music; +// } else { +// sfIdxName = spatial_movie; +// } +// } +#else + const char * sfIdxName = "EXTREME"; +#endif + int32_t NameLen = strlen(sfIdxName); + pSendBuf[idx++] = NameLen; + memcpy(&pSendBuf[idx], sfIdxName, NameLen); + //debug_printf("%s\r\n", sfDispName[sfIdx]); + } else + pSendBuf[idx] = 0xFFFFFFFF; + } + break; + + case CMD_SOUND_FIELD: + if (CMD_SET(CMD_SOUND_FIELD) == command_code) { + int32_t sfIdx = pRcvBuf[1]; + debug_printf("Set CMD_SOUND_FIELD : "); + // 改动原因: + // - host UI 现在只暴露3个“带算法声场”(GAME/MUSIC/MOVIE 三选一)。 + // - 算法开关通过 CMD_ONOFF/其它接口单独控制;CMD_SOUND_FIELD 不能隐式把算法打开/关闭。 + // 因此:CMD_SOUND_FIELD 只接受 0~2(对应 GAME/MUSIC/MOVIE),并且在算法OFF(IR_OFF)时只缓存选择、不改共享变量。 + if (sfIdx >= 0 && sfIdx < 3) { + unsigned cur_mode; + GET_SHARED_GLOBAL(cur_mode, g_3d_on_off_t1); + + last_sf_idx_0_2 = (unsigned)sfIdx; + + if (cur_mode == IR_OFF) { + // 改动原因:算法已关闭时不写 g_3d_on_off_t1,避免 event_polling 误判“主机改变声场”而自动打开算法 + debug_printf("algorithm OFF, cache sound field idx=%d only (no implicit on/off)\n", sfIdx); + } else { + // 改动原因:算法已开启时才更新声场;因EX3D_SF_NUM=3不再区分6个族 + unsigned new_mode; + if (sfIdx == 1) new_mode = IR_7_1_GAME; + else if (sfIdx == 2) new_mode = IR_7_1_MOVIE; + else new_mode = IR_GAME; + SET_SHARED_GLOBAL(g_3d_on_off_t1, new_mode); + debug_printf("set sound field idx=%d -> g_3d_on_off_t1=%d (no implicit on/off)\n", sfIdx, new_mode); + } + } else { + // 改动原因:EX3D_SF_NUM=3后只支持0~2,超过范围返回错误 + pSendBuf[idx] = 0xFFFFFFFF; + } + } else { // Get + // 改动原因:返回当前声场索引(0~2: GAME/MUSIC/MOVIE);若算法OFF则返回缓存值,避免依赖 g_3d_on_off_t1=IR_OFF + unsigned cur_mode; + GET_SHARED_GLOBAL(cur_mode, g_3d_on_off_t1); + if (cur_mode == IR_OFF) { + pSendBuf[idx] = last_sf_idx_0_2; + } else if (cur_mode == IR_7_1_MOVIE) { + pSendBuf[idx] = 2; + } else if (cur_mode == IR_7_1_GAME) { + pSendBuf[idx] = 1; + } else { + pSendBuf[idx] = 0; + } + } + break; + + case CMD_LEVEL: + { +#if 1 //TODO LISTS + uint32_t ChNum = pRcvBuf[1]; + uint32_t *pLevel = (uint32_t *)fLevel; + + if(ChNum > NUM_USB_CHAN_OUT) ChNum = NUM_USB_CHAN_OUT; + + // debug_printf("Get CMD_LEVEL : %d, %d\r\n", ChNum, NUM_USB_CHAN_OUT); + for (int i = 0; i < ChNum; i++) { + pSendBuf[idx++] = pLevel[i]; + // debug_printf("%08x, ", pLevel[i] ); + } + // debug_printf("\r\n"); +#endif + } + break; + + case CMD_ON_GAIN: + if (CMD_SET(CMD_ON_GAIN) == command_code) { + int32_t idBGain; + memcpy(&idBGain, &pRcvBuf[1], 4); + debug_printf("Set CMD_ON_GAIN : %d\r\n", idBGain); + if((-100 <= idBGain) && (idBGain <= 0)) { + Ex3dOnGain = idBGain; + EX3DAudio_SetOnGain(Ex3dOnGain); + } else { + pSendBuf[idx] = 0xFFFFFFFF; + } + } else { // Get + debug_printf("Get CMD_ON_GAIN : %d\r\n", Ex3dOnGain); + memcpy(&pSendBuf[idx], &Ex3dOnGain, 4); + } + break; + + case CMD_OFF_GAIN: + if (CMD_SET(CMD_OFF_GAIN) == command_code) { + int32_t idBGain; + memcpy(&idBGain, &pRcvBuf[1], 4); + debug_printf("Set CMD_OFF_GAIN : %d\r\n", idBGain); + if((-100 <= idBGain) && (idBGain <= 0)) { + Ex3dOffGain = idBGain; + EX3DAudio_SetOffGain(Ex3dOffGain); + } else { + pSendBuf[idx] = 0xFFFFFFFF; + } + } else { // Get + debug_printf("Get CMD_OFF_GAIN : %d\r\n", Ex3dOffGain); + memcpy(&pSendBuf[idx], &Ex3dOffGain, 4); + } + break; + + case CMD_LFE: + if (CMD_SET(CMD_LFE) == command_code) { + int32_t idBGain; + memcpy(&idBGain, &pRcvBuf[1], 4); + debug_printf("Set CMD_LFE : %d\r\n", idBGain); + if((-100 <= idBGain) && (idBGain <= 0)) { + Ex3dLfeGain = idBGain; + EX3DAudio_SetLFE(true, Ex3dLfeGain); + } else { + pSendBuf[idx] = 0xFFFFFFFF; + } + } else { // Get + debug_printf("Get CMD_LFE : %d\r\n", Ex3dLfeGain); + memcpy(&pSendBuf[idx], &Ex3dLfeGain, 4); + } + break; + + case CMD_LMT_THRESHOLD: + if (CMD_SET(CMD_LMT_THRESHOLD) == command_code) { + int32_t threshold; + memcpy(&threshold, &pRcvBuf[1], 4); + debug_printf("Set CMD_LMT_THRESHOLD : %d\r\n", threshold); + if((-35 <= threshold) && (threshold <= 0)) { + Ex3dLimiterThreshold = threshold; + } else { + pSendBuf[idx] = 0xFFFFFFFF; + } + + threshold = Ex3dLimiterThreshold + (sys_vol + Ex3dOnGain + Ex3dExpandGain); + if (threshold > 0) threshold = 0; + if (threshold < -35) threshold = -35; + EX3DAudio_SetLimiterThreshold(threshold); + + } else { // Get + debug_printf("Get CMD_LMT_THRESHOLD : %d\r\n", Ex3dLimiterThreshold); + memcpy(&pSendBuf[idx], &Ex3dLimiterThreshold, 4); + } + break; + + case CMD_LMT_ATTACKK_TIME: + if (CMD_SET(CMD_LMT_ATTACKK_TIME) == command_code) { + unsigned AttackTime; + memcpy(&AttackTime, &pRcvBuf[1], 4); + debug_printf("Set CMD_LMT_ATTACKK_TIME : %d\r\n", AttackTime); + if(AttackTime <= 10) { + Ex3dLimiterAttackTime = AttackTime; + EX3DAudio_SetLimiterAttackTime(Ex3dLimiterAttackTime); + } else { + pSendBuf[idx] = 0xFFFFFFFF; + } + } else { // Get + debug_printf("Get CMD_LMT_ATTACKK_TIME : %d\r\n", Ex3dLimiterAttackTime); + memcpy(&pSendBuf[idx], &Ex3dLimiterAttackTime, 4); + } + break; + + case CMD_LMT_RELEASE_TIME: + if (CMD_SET(CMD_LMT_RELEASE_TIME) == command_code) { + unsigned ReleaseTime; + memcpy(&ReleaseTime, &pRcvBuf[1], 4); + debug_printf("Set CMD_LMT_RELEASE_TIME : %d\r\n", ReleaseTime); + if(ReleaseTime <= 100) { + Ex3dLimiterReleaseTime = ReleaseTime; + EX3DAudio_SetLimiterReleaseTime(Ex3dLimiterReleaseTime); + } else { + pSendBuf[idx] = 0xFFFFFFFF; + } + } else { // Get + debug_printf("Get CMD_LMT_RELEASE_TIME : %d\r\n", Ex3dLimiterReleaseTime); + memcpy(&pSendBuf[idx], &Ex3dLimiterReleaseTime, 4); + } + break; + + // dhlee++ + + case CMD_EXPAND_GAIN: + if (CMD_SET(CMD_EXPAND_GAIN) == command_code) { + int32_t idBGain; + memcpy(&idBGain, &pRcvBuf[1], 4); + debug_printf("Set CMD_EXPAND_GAIN : %d\r\n", idBGain); + if((0 <= idBGain) && (idBGain <= 20)) { + Ex3dExpandGain = idBGain; + EX3DAudio_SetExpandGain(Ex3dExpandGain); + } else { + pSendBuf[idx] = 0xFFFFFFFF; + } + } else { // Get + debug_printf("Get CMD_EXPAND_GAIN : %d\r\n", Ex3dExpandGain); + memcpy(&pSendBuf[idx], &Ex3dExpandGain, 4); + } + break; + + case CMD_REDUCE_GAIN: + if (CMD_SET(CMD_REDUCE_GAIN) == command_code) { + int32_t idBGain; + memcpy(&idBGain, &pRcvBuf[1], 4); + debug_printf("Set CMD_REDUCE_GAIN : %d\r\n", idBGain); + if((-20 <= idBGain) && (idBGain <= 0)) { + Ex3dReduceGain = idBGain; + EX3DAudio_SetReduceGain(Ex3dReduceGain); + } else { + pSendBuf[idx] = 0xFFFFFFFF; + } + } else { // Get + debug_printf("Get CMD_REDUCE_GAIN : %d\r\n", Ex3dReduceGain); + memcpy(&pSendBuf[idx], &Ex3dReduceGain, 4); + } + break; + + + default: + break; +#endif + } +} + +void LevelMeter(AUDIO_T AudioData[NUM_USB_CHAN_OUT][FRAME_SIZE]) +{ + unsigned ch; + AUDIO_T absVal, maxVal; + #if defined(DETECT_CHANNEL) + AUDIO_T DetectAudioData; + unsigned MaskAudioData = 0; + unsigned Mask = 0x01; + static unsigned Mode = 0; + #endif + + for(ch=0; ch 3) current_mode_local = 0; + debug_printf("hid_button_task received audio_mode: %d\n", current_mode_local); +#if USE_EX3D == 1 + if ((current_mode_local == 0) || (current_mode_local == 1)) { + // 无音效模式:关闭EX3D和EQ算法 + audio_ex3d_set_onoff(0); + is_3d_on = IR_OFF; + debug_printf("Mode 0: EX3D+EQ OFF\n"); + } else if (current_mode_local == 2) { + // 音乐模式:EX3D关闭,EQ开启 +#if (MODE_F3_F4_FPS_UAC2 == 1) + audio_ex3d_set_onoff(1); + is_3d_on = IR_7_1_GAME; +#endif +#if (MODE_F6_F7_FPS_UAC1 == 1) + audio_ex3d_set_onoff(0); + is_3d_on = IR_OFF; +#endif + } else if (current_mode_local == 3) { +#if (MODE_F3_F4_FPS_UAC2 == 1) + audio_ex3d_set_onoff(1); + is_3d_on = IR_7_1_MOVIE; +#endif +#if (MODE_F6_F7_FPS_UAC1 == 1) + audio_ex3d_set_onoff(1); + is_3d_on = IR_MOVIE; +#endif + } +#endif + + } else if (tmp == 0xFD) { + // 脚步增强按键发来的 expand gain 命令 + // 必须先读取payload,无论是否启用EX3D + int32_t gain = (int32_t)chan_in_word(cc_mic_level); +#if USE_EX3D == 1 + if ((0 <= gain) && (gain <= 20)) { + Ex3dExpandGain = gain; + EX3DAudio_SetExpandGain(Ex3dExpandGain); + g_boot_footstep_expand_gain_value = gain; + g_boot_footstep_expand_gain_loaded = 1; + debug_printf("Button set CMD_EXPAND_GAIN: %d\n", gain); + } +#endif + } else if (tmp == 0xFA) { + // 开机同步枪声阈值:从tile0 flash加载的Ex3dLimiterThreshold + // 必须先读取payload,无论是否启用EX3D + int32_t threshold = (int32_t)chan_in_word(cc_mic_level); +#if USE_EX3D == 1 + if ((-35 <= threshold) && (threshold <= 0)) { + Ex3dLimiterThreshold = threshold; + g_boot_lmt_threshold_loaded = 1; + g_boot_lmt_threshold_value = threshold; + EX3DAudio_SetLimiterThreshold(threshold); + // audio_ex3d_init尚未运行(T≈200ms),ex3d_task将在T=500ms后用正确的OnGain重新应用 + debug_printf("Boot sync lmt_threshold=%d stored, will apply after ex3d init\n", threshold); + } +#endif + } else if (tmp == 0xFE) { + // 数字监听开关同步命令:来自 tile0(所有模式都需要处理) + unsigned mon_sw = chan_in_word(cc_mic_level); + SET_SHARED_GLOBAL(g_monitor_switch_t1, mon_sw); + debug_printf("Monitor sync: sw=%d\n", mon_sw); + } else if (tmp == 0xF9) { + uint32_t incoming_angles[8]; + for (int i = 0; i < 8; ++i) { + incoming_angles[i] = chan_in_word(cc_mic_level); + } + +#if USE_EX3D == 1 + if (g_runtime_angle_written) { + // 改动原因:主机运行期已写入角度时,0xF9可能携带启动早期flash旧值;此处只消费通道数据但不覆盖当前角度。 + debug_printf("Boot sync ex3d angles ignored (runtime angle already written)\n"); + } else { + for (int i = 0; i < 8; ++i) { + g_boot_angle_values[i] = incoming_angles[i]; + } + g_boot_angle_loaded = 1; + // 改动原因:若EX3D已初始化完成,0xF9角度同步需要立刻生效, + // 否则仅缓存会导致HID GET仍读到初始化时的默认角度。 + if (g_ex3d_init_done) { + ex3d_apply_boot_angles(); + debug_printf("Boot sync ex3d angles applied immediately (post-init)\n"); + } + debug_printf("Boot sync ex3d angles loaded\n"); + } +#endif + } + } + continue; + + event_hid: + { + unsigned length = chan_in_byte(c_hidRcvData); + for(int i=0; i= 3 && RcvData[1] == 0x77 && + (RcvData[2] == 0xB0 || RcvData[2] == 0xB1)) { + // 丢弃,由event_ex3d_hid处理 + } else { + hid_receive_task_in_c(RcvData, SendData); + for(int i=0; i<(HID_MAX_DATA_BYTES / 4); i++) { + chan_out_word(c_hidSendData, SendData[i]); + } + } +#endif + } + } + continue; + event_ex3d_hid: + { +#if (HID_CONTROLS == 1) + // 收到process_send_params发来的0xB0/0xB1 EX3D命令 + // 协议: cmd(1 byte) + ex3d_cmd_code(1 word) + params_len(1 byte) + params(N bytes) + uint8_t hid_cmd = chan_in_byte(c_ex3d_hid_cmd); + uint32_t ex3d_cmd_code = chan_in_word(c_ex3d_hid_cmd); + uint8_t params_len = chan_in_byte(c_ex3d_hid_cmd); + + // 构建EX3D原生格式缓冲区: [0x01, 0, 0, 0, cmd_code[4], params...] + unsigned char ex3d_rcv[64]; + memset(ex3d_rcv, 0, 64); + ex3d_rcv[0] = 0x01; + ex3d_rcv[4] = (uint8_t)(ex3d_cmd_code & 0xFF); + ex3d_rcv[5] = (uint8_t)((ex3d_cmd_code >> 8) & 0xFF); + ex3d_rcv[6] = (uint8_t)((ex3d_cmd_code >> 16) & 0xFF); + ex3d_rcv[7] = (uint8_t)((ex3d_cmd_code >> 24) & 0xFF); + for (int i = 0; i < params_len && i < 56; i++) { + ex3d_rcv[8 + i] = chan_in_byte(c_ex3d_hid_cmd); + } + + // 在tile1执行EX3D命令 + hid_receive_task_in_c(ex3d_rcv, SendData); + + // 将结果回传给process_send_params(16个word) + for (int i = 0; i < (HID_MAX_DATA_BYTES / 4); i++) { + chan_out_word(c_ex3d_hid_cmd, SendData[i]); + } +#endif + } + continue; + + event_uac_vol: + { + sys_vol = chan_in_word(c_uac_vol); +#if USE_EX3D == 1 + sys_vol = sys_vol * (-1); + int32_t threshold = Ex3dLimiterThreshold + (sys_vol + Ex3dOnGain + Ex3dExpandGain); + if (threshold > 0) threshold = 0; + if (threshold < -35) threshold = -35; + EX3DAudio_SetLimiterThreshold(threshold); + debug_printf("Set CMD_LMT_THRESHOLD : %d\r\n", threshold); + debug_printf("sys_vol:%d\n", sys_vol); +#endif + } + continue; + + event_polling: + { + hwtimer_set_trigger_time(timer, hwtimer_get_time(timer) + KEY_POLLING_INTERVAL); + +#if USE_EX3D == 1 + // 检查 host app 是否通过 HID 改变了音场状态 + uint32_t host_app_sf; + GET_SHARED_GLOBAL(host_app_sf, g_3d_on_off_t1); + if (host_app_sf != is_3d_on) { + is_3d_on = host_app_sf; + + // Select SF and set on/off together + if (is_3d_on == IR_OFF) { + audio_ex3d_set_onoff(0); + current_mode_local = 0; + debug_printf("3d off\n"); + } else if (is_3d_on == IR_GAME) { + audio_ex3d_set_sf(0); + audio_ex3d_set_onoff(1); + current_mode_local = 1; + debug_printf("Stereo Game on (sf=0)\n"); + } else if (is_3d_on == IR_7_1_GAME) { + audio_ex3d_set_sf(1); + audio_ex3d_set_onoff(1); + current_mode_local = 2; + debug_printf("3d Game on (sf=1)\n"); + } else if (is_3d_on == IR_7_1_MOVIE) { + audio_ex3d_set_sf(2); + audio_ex3d_set_onoff(1); + current_mode_local = 3; + debug_printf("3d Movie on (sf=2)\n"); + } else { + audio_ex3d_set_onoff(is_3d_on != IR_OFF); + } + SET_SHARED_GLOBAL(g_3d_on_off_t1, is_3d_on); + } +#endif + } + continue; + } + } + // port_disable(p_keys); + // port_disable(p_leds); + // port_disable(p_leds2); + hwtimer_free(timer); +} + + +chanend_t uc_dsp_to_ex3d[DSP_WORKER_COUNT]; + +extern int OnProcessing(); +void ex3d_task(){ + channel_t c = chan_alloc(); + chanend_t c_ex3d_to_ubm = c.end_a; + uc_ex3d_to_ubm = c.end_b; + + // set_core_high_priority_on(); + delay_milliseconds(500); + int ret = audio_ex3d_init(NUM_USB_CHAN_OUT, NUM_USB_CHAN_OUT * FRAME_SIZE * sizeof(AUDIO_T), 48000, sizeof(AUDIO_T)); + if(ret != 0) { + debug_printf("audio_ex3d_init() error:%d\n\r", ret); + } + +#if !UAC1_MODE + Ex3dOffGain = -5; + Ex3dOnGain = -5; + Ex3dLfeGain = -5; + // 若boot sync (0xFA) 已到达,保留flash加载的阈值;否则使用默认值-15 + if (!g_boot_lmt_threshold_loaded) { + Ex3dLimiterThreshold = -15; + } + + // audio_ex3d_init内部会将Ex3dExpandGain重置为库默认值(6),这里始终用保存值恢复 + Ex3dExpandGain = g_boot_footstep_expand_gain_loaded ? g_boot_footstep_expand_gain_value : 12; + Ex3dLimiterThreshold = g_boot_lmt_threshold_loaded ? g_boot_lmt_threshold_value : -15; + + EX3DAudio_SetOnGain(Ex3dOnGain); + EX3DAudio_SetOffGain(Ex3dOffGain); + // audio_ex3d_init完成后用正确的增益链重新计算并应用阈值 + { + int32_t init_effective = Ex3dLimiterThreshold + (sys_vol + Ex3dOnGain + Ex3dExpandGain); + if (init_effective > 0) init_effective = 0; + if (init_effective < -35) init_effective = -35; + EX3DAudio_SetLimiterThreshold(init_effective); + debug_printf("ex3d_task init: lmt_threshold=%d effective=%d expand=%d\n", + Ex3dLimiterThreshold, init_effective, Ex3dExpandGain); + } + // 0xFD boot sync在init前调用了EX3DAudio_SetExpandGain,init后重新应用 + EX3DAudio_SetExpandGain(Ex3dExpandGain); + + ex3d_apply_boot_angles(); + debug_printf("===ex3d_apply_boot_angles after init\n"); + + g_ex3d_init_done = 1; + debug_printf("ex3d_task init: expand_gain=%d\n", Ex3dExpandGain); + +#endif + + while (1) { + OnProcessing(c_ex3d_to_ubm, uc_dsp_to_ex3d); + } +} + +#ifdef USE_EX3D == 1 +#if defined(SPATIAL_DRAMA) +#define TD_DATA_BUFFER_ELEMENTS SPATIAL_71_DRAMA_v090h225_left_DATA_BUFFER_ELEMENTS +#elif defined(SPATIAL_GAME) +#define TD_DATA_BUFFER_ELEMENTS SPATIAL_71_GAME_v090h225_left_DATA_BUFFER_ELEMENTS +#elif defined(SPATIAL_MOVIE) +#define TD_DATA_BUFFER_ELEMENTS SPATIAL_71_MOVIE_v090h225_left_DATA_BUFFER_ELEMENTS +#elif defined(SPATIAL_MUSIC) +#define TD_DATA_BUFFER_ELEMENTS SPATIAL_71_MUSIC_v090h225_left_DATA_BUFFER_ELEMENTS +#elif defined(STEREO_2K) +#define TD_DATA_BUFFER_ELEMENTS SPATIAL_STEREO_2K_v090h090_left_DATA_BUFFER_ELEMENTS +#elif defined(STEREO_8K) +#define TD_DATA_BUFFER_ELEMENTS SPATIAL_STEREO_8K_v090h090_left_DATA_BUFFER_ELEMENTS +#elif defined(IR_SWITCHING_MODE) +#define TD_DATA_BUFFER_ELEMENTS SPATIAL_71_GAME_v090h225_left_DATA_BUFFER_ELEMENTS +#else +#error you need to define SPATIAL_DRAMA or STEREO_8K.... +#endif + +#if defined (IR_SWITCHING_MODE) +int dsp_worker(chanend_t c_dsp_to_ex3d, + td_block_fir_filter_t * filters[DSP_WORKER_FILTER_PER_DATA_CH*DSP_WORKER_DATA_CH_COUNT*4]){ +#else +int dsp_worker(chanend_t c_dsp_to_ex3d, + td_block_fir_filter_t * filters[DSP_WORKER_FILTER_PER_DATA_CH*DSP_WORKER_DATA_CH_COUNT]){ +#endif + int32_t sample_buffer[DSP_WORKER_DATA_CH_COUNT][TD_DATA_BUFFER_ELEMENTS]; + td_block_fir_data_t fir_data[DSP_WORKER_DATA_CH_COUNT]; +#if defined (IR_SWITCHING_MODE) + static uint32_t ir_mode; + GET_SHARED_GLOBAL(ir_mode, g_3d_on_off_t1); +#endif + + for(int i=0;i 3) { + ir_mode -= 4; + } else { + ir_mode -= 1; + } + } + for(int ch=0;ch +#include +#include +#include +#include "xmath/filter.h" +#include "stdio.h" +#include "xc_ptr.h" +#include "eq.h" +#include "share_buffer.h" +#include "debug_print.h" +#include "eq_flash_storage.h" + +#if 0 +#define DPRINTF(...) printf(__VA_ARGS__) +#else +#define DPRINTF(...) +#endif + +#define LEN_FADE_ (2024) + + // EQ Flash存储初始化标志 +int eq_flash_initialized = 0; +extern unsigned int is_mode_changed(void); +extern void change_eq_mode(unsigned sample_freq); +extern void clear_eq_status(unsigned sample_freq, unsigned ch_no); +extern unsigned int g_request_eq_mode; +extern unsigned int g_eq_enable; // 改动原因:添加EQ使能开关外部声明,用于初始化时检查EQ状态 +extern unsigned int g_saved_eq_mode; // 改动原因:添加保存模式外部声明,用于初始化时设置保存的模式 +extern void debug_print_eq_params(unsigned sample_freq); +extern uint32_t get_reference_time(void); +extern void update_eq_post_gain(void); +extern unsigned int g_eq_sample_rate; +extern unsigned g_windows_detect_done; +extern unsigned g_dnr_init_flag; +extern unsigned g_ex3d_key_verified; + +#pragma unsafe arrays +void dsp_core0(void) +{ + unsigned t0,t1; + static int audio_in_samples[2]; // idx 0: input_samples + static int audio_out_samples[2]; // idx 0: input_samples + static unsigned int usb_to_dsp_ratio = 1; + static int buffer_in_active = 0; + static int buffer_out_active = 0; + static unsigned int sample_freq = 48000 ; + unsigned b_fade_in = 0; + unsigned u_in_step; + unsigned b_fade_out = 0; + unsigned u_out_step; + + // EQ Flash存储初始化标志 + + delay_milliseconds(300); + unsigned windows_detect_done, dnr_init_flag, ex3d_key_verified; + GET_SHARED_GLOBAL(windows_detect_done, g_windows_detect_done); +#if DNR_ENABLE + GET_SHARED_GLOBAL(dnr_init_flag, g_dnr_init_flag); +#endif +#if USE_EX3D + GET_SHARED_GLOBAL(ex3d_key_verified, g_ex3d_key_verified); +#endif + while ((windows_detect_done == 0) +#if DNR_ENABLE + || (dnr_init_flag == 0) +#endif +#if USE_EX3D + || (ex3d_key_verified == 0) +#endif + ) { + asm("nop"); + GET_SHARED_GLOBAL(windows_detect_done, g_windows_detect_done); +#if DNR_ENABLE + GET_SHARED_GLOBAL(dnr_init_flag, g_dnr_init_flag); +#endif +#if USE_EX3D + GET_SHARED_GLOBAL(ex3d_key_verified, g_ex3d_key_verified); +#endif + + } + +#if EQ_EN + + uint32_t time = get_reference_time(), time2; + // EQ Flash存储初始化 - 只在第一次运行时执行 + if (eq_flash_initialized == 0) { + // 延迟更长时间,确保QSPI Flash完全初始化 + debug_printf("Initializing EQ Flash storage system\n"); + + // 初始化EQ Flash存储系统 + if (eq_flash_init() == 0) { + debug_printf("EQ Flash storage initialized successfully\n"); + init_mode_info(); + eq_load_gain_and_names(); + // 检查Flash中是否有EQ参数数据 + debug_printf("Loading EQ parameters from Flash using simplified storage\n"); + // 简化加载逻辑:每个模式只加载一次,会自动应用到所有采样率和通道 + int loaded_modes = 0; + + for (int mode = EQ_USER_MODE_MIN; mode <= EQ_USER_MODE_MAX; mode++) { + if (eq_load_all_params_and_calculate_coefficients(44100, mode) == 0) { + loaded_modes++; + debug_printf("Loaded mode %d parameters from Flash (applied to all sample rates and channels)\n", mode); + } + } + debug_printf("Loaded %d EQ modes from Flash successfully\n", loaded_modes); + + // 从mode info里获取gain值,并相应的赋值到eq数组的post gain + update_eq_post_gain(); + // 从Flash加载并设置当前EQ模式和使能状态 + // 改动原因:修复EQ开关逻辑问题,确保从Flash加载模式时正确设置g_saved_eq_mode和g_eq_enable + // eq_flash_load_current_mode()会同时加载模式并设置g_eq_enable + int saved_mode_result = eq_flash_load_current_mode(); + + // 改动原因:确保g_eq_enable被正确加载,无论模式加载是否成功 + // eq_flash_load_current_mode()已经设置了g_eq_enable,这里读取确认 + unsigned int eq_enable = g_eq_enable; + + if (saved_mode_result >= 0 && saved_mode_result <= 9) { + uint8_t saved_mode = (uint8_t)saved_mode_result; + debug_printf("Loaded current EQ mode %d and enable %d from Flash\n", saved_mode, eq_enable); + + // 改动原因:将eq_enable独立为全局开关,所有模式共用一个开关 + // 无论EQ是否启用,都设置到Flash中保存的模式,eq_enable只控制EQ处理开关 + g_saved_eq_mode = saved_mode; + SET_SHARED_GLOBAL(g_request_eq_mode, saved_mode); + debug_printf("EQ initialization: Set mode to %d from Flash, EQ enable: %d (global switch, independent of mode)\n", + saved_mode, eq_enable); + } else { + // Flash加载失败时,使用默认模式0,EQ默认禁用 + // 改动原因:如果模式加载失败,确保g_eq_enable也被设置为默认值0 + g_saved_eq_mode = 0; + SET_SHARED_GLOBAL(g_request_eq_mode, 0); + g_eq_enable = 0; + eq_enable = 0; // 同步本地变量 + debug_printf("Failed to load EQ mode from Flash, using default mode 0, EQ disabled (enable: %d)\n", eq_enable); + } + + } else { + debug_printf("Failed to initialize EQ Flash storage system, continuing without Flash storage\n"); + // 即使Flash初始化失败,也标记为已初始化,避免重复尝试 + // 系统将继续运行,只是不使用Flash存储功能 + } + + eq_single_param_database_init(); + time2 = get_reference_time(); + debug_printf("EQ Flash storage initialized time %lu\n", time2 - time); + eq_flash_initialized = 1; + } + + while (1) + { + if(is_ring_buffer_empty(0) && is_ring_buffer_empty(1)) + { + // delay_microseconds(1); + } + else + { + GET_SHARED_GLOBAL(sample_freq, g_eq_sample_rate); + if (sample_freq == 0) sample_freq = 48000; + audio_in_samples[0] = read_from_ring_buffer(0); + audio_in_samples[1] = read_from_ring_buffer(1); + + if (is_mode_changed() && b_fade_out == 0 && b_fade_in == 0) + { + + debug_printf("mode changed ====================== \n"); + //debug_print_eq_params(sample_freq); + b_fade_out=1; + u_out_step=1; + } + + + audio_out_samples[0] = handler_eq_filter(sample_freq, 0, audio_in_samples[0]); + audio_out_samples[1] = handler_eq_filter(sample_freq, 1, audio_in_samples[1]); + + + if(b_fade_out) + { + audio_out_samples[0] /= LEN_FADE_; + audio_out_samples[0] *= (LEN_FADE_-u_out_step) ; + + audio_out_samples[1] /= LEN_FADE_; + audio_out_samples[1] *= (LEN_FADE_-u_out_step) ; + + ++u_out_step; + } + + if(b_fade_out == 1 && u_out_step == LEN_FADE_){ + b_fade_out = 0; + change_eq_mode(sample_freq); + clear_eq_status(sample_freq, 0); + clear_eq_status(sample_freq, 1); + clear_ring_buffer(0); + clear_ring_buffer(1); + b_fade_in=1; + u_in_step=1; + } + + + if (b_fade_in) + { + audio_out_samples[0] /= LEN_FADE_; + audio_out_samples[0] *= (u_in_step) ; + + audio_out_samples[1] /= LEN_FADE_; + audio_out_samples[1] *= (u_in_step); + + if(++u_in_step >= LEN_FADE_){ + b_fade_in = 0; + } + } + + write_to_ring_buffer(2, audio_out_samples[0]); + write_to_ring_buffer(3, audio_out_samples[1]); + } + } +#endif +} diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dsp_core0.xc_tile1 b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dsp_core0.xc_tile1 new file mode 100644 index 0000000..134bc64 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dsp_core0.xc_tile1 @@ -0,0 +1,421 @@ +#if DEBUG_MEMORY_LOG_ENABLED +#define DEBUG_PRINT_ENABLE 1 +#else +#define DEBUG_PRINT_ENABLE 0 +#endif +#include +#include +#include +#include +#include "xmath/filter.h" +#include "stdio.h" +#include "xc_ptr.h" +#include "eq.h" +#include "share_buffer.h" +#include "debug_print.h" +#include "eq_flash_storage.h" +#include "eq_channel_protocol.h" + +#if 0 +#define DPRINTF(...) printf(__VA_ARGS__) +#else +#define DPRINTF(...) +#endif + +#define LEN_FADE_ (2024) + +extern unsigned int is_mode_changed(void); +extern void change_eq_mode(unsigned sample_freq); +extern void clear_eq_status(unsigned sample_freq, unsigned ch_no); +extern unsigned int g_request_eq_mode; +extern unsigned int g_eq_enable; +extern unsigned int g_saved_eq_mode; +extern void debug_print_eq_params(unsigned sample_freq); +extern uint32_t get_reference_time(void); +extern void update_eq_post_gain(void); +extern unsigned int g_eq_sample_rate; +extern unsigned g_windows_detect_done; +extern unsigned g_dnr_init_flag; +extern unsigned g_ex3d_key_verified; +extern unsigned int g_new_eq_mode; +extern unsigned int g_force_eq_mode_change; + +/* ---------------------------------------------------------------- + * eq_flash_init_task — 运行在 tile[0] + * + * 负责: + * 1) 等待系统就绪 (windows_detect / dnr / ex3d) + * 2) 从 QSPI Flash 加载 EQ 预设参数 + * 3) 通过 c_eq_params channel 将加载的参数发送到 tile[1] 的 dsp_core0 + * 4) 完成后退出 (不占用线程) + * ---------------------------------------------------------------- */ +void eq_flash_init_task(chanend c_eq_params) +{ +#if EQ_EN + delay_milliseconds(300); + + /* ---- 等待系统就绪 ---- */ + unsigned windows_detect_done, dnr_init_flag, ex3d_key_verified; + GET_SHARED_GLOBAL(windows_detect_done, g_windows_detect_done); +#if DNR_ENABLE + GET_SHARED_GLOBAL(dnr_init_flag, g_dnr_init_flag); +#endif +#if USE_EX3D + GET_SHARED_GLOBAL(ex3d_key_verified, g_ex3d_key_verified); +#endif + while ((windows_detect_done == 0) +#if DNR_ENABLE + || (dnr_init_flag == 0) +#endif +#if USE_EX3D + || (ex3d_key_verified == 0) +#endif + ) { + asm("nop"); + GET_SHARED_GLOBAL(windows_detect_done, g_windows_detect_done); +#if DNR_ENABLE + GET_SHARED_GLOBAL(dnr_init_flag, g_dnr_init_flag); +#endif +#if USE_EX3D + GET_SHARED_GLOBAL(ex3d_key_verified, g_ex3d_key_verified); +#endif + } + + uint32_t time = get_reference_time(), time2; + + /* ---- Flash 初始化 & 加载 ---- */ + debug_printf("Initializing EQ Flash storage system\n"); + + unsigned int saved_mode = 0; + unsigned int eq_enable = 0; + + if (eq_flash_init() == 0) { + debug_printf("EQ Flash storage initialized successfully\n"); + init_mode_info(); + eq_load_gain_and_names(); + + debug_printf("Loading EQ parameters from Flash using simplified storage\n"); + int loaded_modes = 0; + for (int mode = EQ_USER_MODE_MIN; mode <= EQ_USER_MODE_MAX; mode++) { + if (eq_load_all_params_and_calculate_coefficients(44100, mode) == 0) { + loaded_modes++; + debug_printf("Loaded mode %d parameters from Flash\n", mode); + } + } + debug_printf("Loaded %d EQ modes from Flash successfully\n", loaded_modes); + + update_eq_post_gain(); + + int saved_mode_result = eq_flash_load_current_mode(); + eq_enable = g_eq_enable; + + if (saved_mode_result >= 0 && saved_mode_result <= 9) { + saved_mode = (unsigned int)saved_mode_result; + debug_printf("Loaded current EQ mode %d and enable %d from Flash\n", saved_mode, eq_enable); + g_saved_eq_mode = saved_mode; + SET_SHARED_GLOBAL(g_request_eq_mode, saved_mode); + } else { + saved_mode = 0; + g_saved_eq_mode = 0; + SET_SHARED_GLOBAL(g_request_eq_mode, 0); + g_eq_enable = 0; + eq_enable = 0; + } + } else { + debug_printf("Failed to initialize EQ Flash storage system\n"); + } + + eq_single_param_database_init(); + time2 = get_reference_time(); + debug_printf("EQ Flash storage initialized time %lu\n", time2 - time); + + /* ---- 通过 channel 将加载的参数发送到 tile[1] ---- */ + /* 发送每个 mode / channel 的完整 band 参数 */ + unsafe { + extern eq_mode_data_t sEQ_data_44100HZ[NUM_EQ_MODES][NUM_EQ_CHANS]; + for (int mode = 0; mode < NUM_EQ_MODES; mode++) { + for (int ch = 0; ch < NUM_EQ_CHANS; ch++) { + c_eq_params <: (unsigned)EQ_CMD_FLASH_LOAD_MODE; + c_eq_params <: (unsigned)mode; + c_eq_params <: (unsigned)ch; + c_eq_params <: (unsigned)sEQ_data_44100HZ[mode][ch].post_gain_db; + for (int b = 0; b < MAX_EQ_BANDS; b++) { + filter_params_t * unsafe bp = &sEQ_data_44100HZ[mode][ch].bands[b]; + c_eq_params <: (unsigned)bp->type; + unsigned fc_u, q_u, bw_u, gain_u; + memcpy(&fc_u, &bp->fc, 4); + memcpy(&q_u, &bp->q, 4); + memcpy(&bw_u, &bp->bw, 4); + memcpy(&gain_u, &bp->gain, 4); + c_eq_params <: fc_u; + c_eq_params <: q_u; + c_eq_params <: bw_u; + c_eq_params <: gain_u; + c_eq_params <: (unsigned)bp->index; + } + } + } + + /* 发送初始化完成信号 + 当前模式/使能 */ + c_eq_params <: (unsigned)EQ_CMD_FLASH_INIT_COMPLETE; + c_eq_params <: (unsigned)saved_mode; + c_eq_params <: (unsigned)eq_enable; + } + + debug_printf("EQ Flash init task: all params sent to tile[1], task exiting\n"); +#endif /* EQ_EN */ +} + + +/* ---------------------------------------------------------------- + * 接收 channel 参数更新的内部处理函数 (tile[1]) + * ---------------------------------------------------------------- */ +#if EQ_EN +static void handle_eq_channel_cmd(chanend c_eq_params, unsigned cmd, unsigned sample_freq) +{ + extern eq_mode_data_t sEQ_data_44100HZ[NUM_EQ_MODES][NUM_EQ_CHANS]; + + switch (cmd) { + case EQ_CMD_SET_MODE: + { + unsigned mode; + c_eq_params :> mode; + g_new_eq_mode = mode; + debug_printf("EQ channel: SET_MODE %d\n", mode); + break; + } + case EQ_CMD_SET_ENABLE: + { + unsigned enable; + c_eq_params :> enable; + g_eq_enable = enable; + debug_printf("EQ channel: SET_ENABLE %d\n", enable); + break; + } + case EQ_CMD_UPDATE_BAND: + { + unsigned mode, band_index, filter_type; + unsigned fc_u, q_u, bw_u, gain_u; + c_eq_params :> mode; + c_eq_params :> band_index; + c_eq_params :> filter_type; + c_eq_params :> fc_u; + c_eq_params :> q_u; + c_eq_params :> bw_u; + c_eq_params :> gain_u; + + if (mode < NUM_EQ_MODES && band_index < MAX_EQ_BANDS) { + float fc, q, bw, gain; + memcpy(&fc, &fc_u, 4); + memcpy(&q, &q_u, 4); + memcpy(&bw, &bw_u, 4); + memcpy(&gain, &gain_u, 4); + + for (int ch = 0; ch < NUM_EQ_CHANS; ch++) { + filter_params_t *bp = &sEQ_data_44100HZ[mode][ch].bands[band_index]; + bp->type = (filter_type_t)filter_type; + bp->fc = fc; + bp->q = q; + bp->bw = bw; + bp->gain = gain; + bp->index = band_index; + } + /* 重算系数 */ + calculate_current_mode_coefficients(sample_freq, mode); + debug_printf("EQ channel: UPDATE_BAND mode=%d band=%d\n", mode, band_index); + } + break; + } + case EQ_CMD_SET_GAIN: + { + unsigned mode; + unsigned gain_val; + c_eq_params :> mode; + c_eq_params :> gain_val; + if (mode < NUM_EQ_MODES) { + int32_t gain_db = (int32_t)gain_val; + sEQ_data_44100HZ[mode][0].post_gain_db = gain_db; + sEQ_data_44100HZ[mode][1].post_gain_db = gain_db; + debug_printf("EQ channel: SET_GAIN mode=%d gain=%d\n", mode, gain_db); + } + break; + } + case EQ_CMD_FORCE_REFRESH: + { + g_force_eq_mode_change = 1; + debug_printf("EQ channel: FORCE_REFRESH\n"); + break; + } + case EQ_CMD_FLASH_LOAD_MODE: + { + unsigned mode, ch, post_gain; + c_eq_params :> mode; + c_eq_params :> ch; + c_eq_params :> post_gain; + if (mode < NUM_EQ_MODES && ch < NUM_EQ_CHANS) { + sEQ_data_44100HZ[mode][ch].post_gain_db = (int32_t)post_gain; + for (int b = 0; b < MAX_EQ_BANDS; b++) { + unsigned type_u, fc_u, q_u, bw_u, gain_u, idx_u; + c_eq_params :> type_u; + c_eq_params :> fc_u; + c_eq_params :> q_u; + c_eq_params :> bw_u; + c_eq_params :> gain_u; + c_eq_params :> idx_u; + filter_params_t *bp = &sEQ_data_44100HZ[mode][ch].bands[b]; + bp->type = (filter_type_t)type_u; + memcpy(&bp->fc, &fc_u, 4); + memcpy(&bp->q, &q_u, 4); + memcpy(&bp->bw, &bw_u, 4); + memcpy(&bp->gain, &gain_u, 4); + bp->index = (uint8_t)idx_u; + } + } else { + /* mode/ch 越界, 仍然要读空 channel 数据 */ + for (int b = 0; b < MAX_EQ_BANDS; b++) { + unsigned dummy; + c_eq_params :> dummy; c_eq_params :> dummy; + c_eq_params :> dummy; c_eq_params :> dummy; + c_eq_params :> dummy; c_eq_params :> dummy; + } + } + break; + } + case EQ_CMD_FLASH_INIT_COMPLETE: + { + unsigned saved_mode, eq_en; + c_eq_params :> saved_mode; + c_eq_params :> eq_en; + g_saved_eq_mode = saved_mode; + g_new_eq_mode = saved_mode; + SET_SHARED_GLOBAL(g_request_eq_mode, saved_mode); + g_eq_enable = eq_en; + /* 为所有模式计算初始系数 */ + init_eq_data(48000); + debug_printf("EQ channel: FLASH_INIT_COMPLETE mode=%d enable=%d\n", saved_mode, eq_en); + break; + } + default: + debug_printf("EQ channel: unknown cmd 0x%x\n", cmd); + break; + } +} +#endif /* EQ_EN */ + + +/* ---------------------------------------------------------------- + * dsp_core0 — 运行在 tile[1] + * + * EQ 滤波主循环 + 通过 channel 接收参数更新 + * ---------------------------------------------------------------- */ +#pragma unsafe arrays +void dsp_core0(chanend c_eq_params) +{ + unsigned t0,t1; + static int audio_in_samples[2]; + static int audio_out_samples[2]; + static unsigned int usb_to_dsp_ratio = 1; + static int buffer_in_active = 0; + static int buffer_out_active = 0; + static unsigned int sample_freq = 48000; + unsigned b_fade_in = 0; + unsigned u_in_step; + unsigned b_fade_out = 0; + unsigned u_out_step; + +#if EQ_EN + + /* 等待 Flash 初始化数据从 tile[0] 到达 */ + { + unsigned flash_init_done = 0; + while (!flash_init_done) { + unsigned cmd; + c_eq_params :> cmd; + if (cmd == EQ_CMD_FLASH_INIT_COMPLETE) { + handle_eq_channel_cmd(c_eq_params, cmd, sample_freq); + flash_init_done = 1; + } else { + handle_eq_channel_cmd(c_eq_params, cmd, sample_freq); + } + } + } + + clear_ring_buffer(0); + clear_ring_buffer(1); + clear_ring_buffer(2); + clear_ring_buffer(3); + + while (1) + { + /* ---- 优先检查 channel 上的参数更新 (non-blocking) ---- */ + select { + case c_eq_params :> unsigned cmd: + handle_eq_channel_cmd(c_eq_params, cmd, sample_freq); + break; + default: + break; + } + + /* ---- 音频处理 ---- */ + if(is_ring_buffer_empty(0) && is_ring_buffer_empty(1)) + { + // 无数据, 继续轮询 + } + else + { + GET_SHARED_GLOBAL(sample_freq, g_eq_sample_rate); + if (sample_freq == 0) sample_freq = 48000; + audio_in_samples[0] = read_from_ring_buffer(0); + audio_in_samples[1] = read_from_ring_buffer(1); + + if (is_mode_changed() && b_fade_out == 0 && b_fade_in == 0) + { + debug_printf("mode changed ====================== \n"); + b_fade_out=1; + u_out_step=1; + } + + audio_out_samples[0] = handler_eq_filter(sample_freq, 0, audio_in_samples[0]); + audio_out_samples[1] = handler_eq_filter(sample_freq, 1, audio_in_samples[1]); + + if(b_fade_out) + { + audio_out_samples[0] /= LEN_FADE_; + audio_out_samples[0] *= (LEN_FADE_-u_out_step) ; + + audio_out_samples[1] /= LEN_FADE_; + audio_out_samples[1] *= (LEN_FADE_-u_out_step) ; + + ++u_out_step; + } + + if(b_fade_out == 1 && u_out_step == LEN_FADE_){ + b_fade_out = 0; + change_eq_mode(sample_freq); + clear_eq_status(sample_freq, 0); + clear_eq_status(sample_freq, 1); + clear_ring_buffer(0); + clear_ring_buffer(1); + b_fade_in=1; + u_in_step=1; + } + + if (b_fade_in) + { + audio_out_samples[0] /= LEN_FADE_; + audio_out_samples[0] *= (u_in_step) ; + + audio_out_samples[1] /= LEN_FADE_; + audio_out_samples[1] *= (u_in_step); + + if(++u_in_step >= LEN_FADE_){ + b_fade_in = 0; + } + } + + write_to_ring_buffer(2, audio_out_samples[0]); + write_to_ring_buffer(3, audio_out_samples[1]); + } + } +#endif +} diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dsp_user_impl.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dsp_user_impl.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/dsp_user_impl.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/dsp_user_impl.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq.c diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq.c.bak b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq.c.bak new file mode 100644 index 0000000..f6cb676 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq.c.bak @@ -0,0 +1,1330 @@ +#if DEBUG_MEMORY_LOG_ENABLED +#define DEBUG_PRINT_ENABLE 1 +#else +#define DEBUG_PRINT_ENABLE 0 +#endif +#include "debug_print.h" +#include "xc_ptr.h" +#include // 添加数学函数支持 + +extern void device_reboot(void); + +#if HID_DFU_EN +#include "dfu_upgrade.h" +#endif + +// 常量定义 +#define EQ_DISABLED_MODE 10 // 禁用EQ的模式编号 +unsigned g_log_switch = 0; + +#include "xmath/filter.h" +#include "eq.h" +#include +#include +#include +#include "eq_params_44100.h" + +#define RSP_HDR(r, cmd) do { (r)[0] = 0x77; (r)[1] = (uint8_t)(cmd); } while(0) + +#include "eq_coefficient_calculator.h" +#include "biquad_standalone.h" +#include "eq_flash_storage.h" +#include "user_func.h" +#if (USE_EX3D == 1) && (HID_CONTROLS == 1) +#include +#endif + +static struct { + uint8_t pending_cmd; // 0xB0/0xB1 or 0 + uint8_t ex3d_params_len; + uint32_t ex3d_command_code; + uint8_t ex3d_params[56]; +} ex3d_request = {0}; + +#if EQ_EN + +unsigned int g_current_eq_mode = 0; +unsigned int g_request_eq_mode = 0; +unsigned int g_new_eq_mode = 9; +unsigned int g_eq_sample_rate = 0; +unsigned int g_force_eq_mode_change = 0; +unsigned int g_force_request_eq_mode_change = 0; +unsigned int g_eq_enable = 0; +unsigned int g_saved_eq_mode = 9; + +// 全局模式信息存储 - 只存储NUM_EQ_MODES条信息 +eq_mode_info_t g_mode_info = {0}; + +static struct { + uint8_t pending_cmd; // HID command code (0 = none pending) + uint8_t mode; + uint8_t eq_index; + uint8_t read_mode_request_mode; +} read_request = {0}; + +// Optimized: 51 entries for integer dB values 0 to -50 (was 183 entries at 0.5dB steps) +// 改动原因:log打印开关全局变量,0=关闭 1=开启;由0x70 SET_LOG_SWITCH 设置,debug_printf 宏在输出前检查该变量 +static const int32_t attenuation_table[51] = { + 0x01000000, // 0dB + 0x00E42905, // -1dB + 0x00CB5918, // -2dB + 0x00B53BEF, // -3dB + 0x00A1866B, // -4dB + 0x008FF599, // -5dB + 0x00804DCE, // -6dB + 0x007259DB, // -7dB + 0x0065EA59, // -8dB + 0x005AD50C, // -9dB + 0x0050F44D, // -10dB + 0x0048268D, // -11dB + 0x00404DE6, // -12dB + 0x00394FAE, // -13dB + 0x00331426, // -14dB + 0x002D8621, // -15dB + 0x002892C1, // -16dB + 0x00242934, // -17dB + 0x00203A7E, // -18dB + 0x001CB942, // -19dB + 0x00199999, // -20dB + 0x0016D0E6, // -21dB + 0x001455B5, // -22dB + 0x00121F97, // -23dB + 0x0010270A, // -24dB + 0x000E655C, // -25dB + 0x000CD494, // -26dB + 0x000B6F62, // -27dB + 0x000A3108, // -28dB + 0x0009154E, // -29dB + 0x0008186E, // -30dB + 0x0007370E, // -31dB + 0x00066E30, // -32dB + 0x0005BB2B, // -33dB + 0x00051B9D, // -34dB + 0x00048D69, // -35dB + 0x00040EAC, // -36dB + 0x00039DB8, // -37dB + 0x0003390C, // -38dB + 0x0002DF53, // -39dB + 0x00028F5C, // -40dB + 0x00024817, // -41dB + 0x00020892, // -42dB + 0x0001CFF5, // -43dB + 0x00019D81, // -44dB + 0x00017089, // -45dB + 0x00014875, // -46dB + 0x000124BD, // -47dB + 0x000104E7, // -48dB + 0x0000E887, // -49dB + 0x0000CF3E, // -50dB +}; + +static inline int get_gain_q24(int input, unsigned multipler){ + int result; + long long intermediate; + intermediate = (long long) input * (long long) multipler; + intermediate = intermediate >> 24; + + if (intermediate > INT32_MAX) { + intermediate = INT32_MAX; + } + else if (intermediate < INT32_MIN) { + intermediate = INT32_MIN; + } + + result = (int)(intermediate); + return result; +} + +static inline int32_t clamp_post_gain(int32_t gain) { + // 限制gain范围:0 >= gain >= -50 + if (gain > 0) { + return 0; + } else if (gain < -50) { + return -50; + } + return gain; +} + +// apply_gain: apply attenuation (Q24 format) +// gain_db is always in range [-50, 0] +static inline int32_t apply_gain(int32_t sample, int32_t gain_db) { + int32_t idx = -gain_db; + if (idx > 50) idx = 50; + return get_gain_q24(sample, attenuation_table[idx]); +} + +// Helper: check if name is empty (all NUL or all spaces) +static bool is_name_empty(const char *name, int len) { + if (name[0] == '\0') return true; + for (int i = 0; i < len && name[i] != '\0'; i++) { + if (name[i] != ' ') return false; + } + return true; +} + +// copy_params_from_44100: set sample rate flag for specified mode +static int copy_params_from_44100(uint32_t sample_rate, uint8_t mode) { + if (mode >= NUM_EQ_MODES) { + return -1; + } + + for (int ch = 0; ch < NUM_EQ_CHANS; ch++) { + sEQ_data_44100HZ[mode][ch].sample_rate = sample_rate; + sEQ_data_44100HZ[mode][ch].total_bshift = 0; + } + + return 0; +} + +// calculate_current_mode_coefficients: dynamically compute coefficients for all bands of a mode +int calculate_current_mode_coefficients(uint32_t sample_rate, uint8_t mode) { + copy_params_from_44100(sample_rate, mode); + + if (mode >= NUM_EQ_MODES) { + return -1; + } + + eq_mode_data_t *eq_data = &sEQ_data_44100HZ[mode][0]; + eq_mode_data_t *eq_data_2 = &sEQ_data_44100HZ[mode][1]; + + // 重置total_bshift + int total_bshift = 0; + + // 遍历所有band,计算系数 + for (int band_index = 0; band_index < MAX_EQ_BANDS; band_index++) { + filter_params_t *band = &eq_data->bands[band_index]; + + // 检查参数有效性 + if (isnan(band->fc) || isnan(band->q) || isnan(band->bw) || isnan(band->gain)) { + band->type = FILTER_TYPE_BYPASS; + band->fc = 1000.0f; + band->q = 0.707f; + band->bw = 1.0f; + band->gain = 0.0f; + } + + q2_30 coeffs[5]; + int calculated_bshift = 0; + + // 根据滤波器类型计算系数 + if (band->type == FILTER_TYPE_BYPASS) { + // bypass类型:直接设置bypass系数,不累加bshift + coeffs[0] = 1 << 30; // b0 = 1.0 in Q30 + coeffs[1] = 0; // b1 = 0 + coeffs[2] = 0; // b2 = 0 + coeffs[3] = 0; // a1 = 0 + coeffs[4] = 0; // a2 = 0 + calculated_bshift = 0; + } else { + // 非bypass类型:调用系数计算函数 + if (eq_calculate_coefficients_from_params(band, sample_rate, coeffs, &calculated_bshift) != 0) { + // 计算失败,使用bypass + coeffs[0] = 1 << 30; + coeffs[1] = 0; + coeffs[2] = 0; + coeffs[3] = 0; + coeffs[4] = 0; + calculated_bshift = 0; + } + // 累加非bypass滤波器的bshift + total_bshift += calculated_bshift; + } + + // 存储计算出的系数到两个通道 + for (int i = 0; i < 5; i++) { + eq_data->filter.coef[i][band_index] = coeffs[i]; + eq_data_2->filter.coef[i][band_index] = coeffs[i]; + } + // 同步band参数到第二个通道 + memcpy(&eq_data_2->bands[band_index], band, sizeof(filter_params_t)); + } + + // 更新total_bshift + eq_data->total_bshift = total_bshift; + eq_data_2->total_bshift = total_bshift; + + return 0; +} + +// Initialize EQ data for the specified sample rate +uint32_t init_eq_data(unsigned sample_freq) { + g_eq_sample_rate = sample_freq; + + // Clear EQ status for all sample rates, modes, and channels + for (int ch = 0; ch < NUM_EQ_CHANS; ch++) { + // Clear status for various sample rates + clear_eq_status(sample_freq, ch); + } + + // 为所有模式计算系数 (calculate_current_mode_coefficients already calls copy_params_from_44100) + for (int mode = 0; mode < NUM_EQ_MODES; mode++) { + calculate_current_mode_coefficients(sample_freq, mode); + } + + return 1; +} + +void update_eq_post_gain(void) { + for (int mode = EQ_USER_MODE_MIN; mode <= EQ_USER_MODE_MAX; mode++) { + int32_t gain = clamp_post_gain(g_mode_info.mode_gains[mode]); + if (gain != g_mode_info.mode_gains[mode]) { + g_mode_info.mode_gains[mode] = gain; + } + + if (sEQ_data_44100HZ[mode][0].post_gain_db != gain) { + sEQ_data_44100HZ[mode][0].post_gain_db = gain; + sEQ_data_44100HZ[mode][1].post_gain_db = gain; + } + } +} + +unsigned int is_mode_changed(void) +{ + if ((g_current_eq_mode != g_new_eq_mode) || (g_force_eq_mode_change == 1)) + { + g_force_eq_mode_change = 0; + return 1; + } + + return 0; +} + +void change_eq_mode(unsigned sample_freq) +{ + calculate_current_mode_coefficients(sample_freq, g_new_eq_mode); + g_current_eq_mode = g_new_eq_mode; +} + +unsigned int is_eq_disabled(void) +{ + return (g_eq_enable == 0); +} + +void clear_eq_status(unsigned sample_freq, unsigned ch_no) { + if(is_eq_disabled()) + { + return; + } + + // Clear status for the specified sample rate, mode, and channel + memset(sEQ_data_44100HZ[g_current_eq_mode][ch_no].filter.state, 0, + sizeof(sEQ_data_44100HZ[g_current_eq_mode][ch_no].filter.state)); +} + +// reset_eq_params: restore default EQ parameters for given mode +uint8_t reset_eq_params(uint8_t mode) { + // 检查模式号有效性 + if (mode != 0xFF && mode >= NUM_EQ_MODES) { + return 0x01; // 失败 + } + + // 删除Flash中的EQ参数文件 + delete_flash_eq_params(mode); + + // 删除Flash中的增益和模式名称文件(只删除相应模式的数据) + delete_flash_gain_and_names(mode); + + // 恢复头文件预设参数 + restore_default_eq_params(mode); + + // 恢复默认模式信息 + init_mode_info(); + + return 0x00; // 成功 +} + +int32_t handler_eq_filter(unsigned sample_freq, uint32_t ch, int32_t new_sample) { + int32_t temp_sample = new_sample; + + + if(is_eq_disabled()) + { + return temp_sample; + } + + + if (ch < NUM_EQ_CHANS) { + eq_mode_data_t *eq = &sEQ_data_44100HZ[g_current_eq_mode][ch]; + + // 应用增益或衰减 (Q24格式) + int32_t post_gain = eq->post_gain_db; + if (post_gain <= 0 && post_gain >= -50) { + temp_sample = apply_gain(temp_sample, post_gain); + } + + temp_sample = temp_sample >> 1; //Q30 + + // 应用均衡器滤波 + temp_sample = filter_biquads_s32(&eq->filter, 1, temp_sample); + + // 应用bshift补偿 + unsigned int bshift = eq->total_bshift; + + // Q2.30 -> Q31,溢出保护 + if (temp_sample > (INT32_MAX >> (bshift + 1))) { + temp_sample = INT32_MAX; + } else if (temp_sample < (INT32_MIN >> (bshift + 1))) { + temp_sample = INT32_MIN; + } else { + temp_sample = temp_sample << (bshift + 1); + } + } + + return temp_sample; +} + + + + +#endif + + +unsigned key_program = 0; + +extern unsigned short XUA_Endpoint0_getProductId(); +extern unsigned short XUA_Endpoint0_getVendorId(); +extern char* XUA_Endpoint0_getProductStr(); +extern char* XUA_Endpoint0_getVendorStr(); +extern char* XUA_Endpoint0_getSerialStr(); +extern unsigned g_request_volume_set; + +void get_device_info(unsigned char *response) { + unsigned short pid = XUA_Endpoint0_getProductId(); + unsigned short vid = XUA_Endpoint0_getVendorId(); + + // 产品ID (1字节,小端序,位置3-4) + response[2] = (uint8_t)(pid & 0xFF); + response[3] = (uint8_t)((pid >> 8) & 0xFF); + + // 厂商ID (1字节,小端序,位置5-6) + response[4] = (uint8_t)(vid & 0xFF); + response[5] = (uint8_t)((vid >> 8) & 0xFF); + + // 产品字符串 (15字节,UTF-8编码,位置7-22) + memcpy(&response[6], XUA_Endpoint0_getProductStr(), 16); + + // 厂商字符串 (15字节,UTF-8编码,位置23-38) + memcpy(&response[22], XUA_Endpoint0_getVendorStr(), 16); + + // 序列号字符串 (15字节,UTF-8编码,位置39-54) + memcpy(&response[38], XUA_Endpoint0_getSerialStr(), 16); +} + + + + +#if EQ_EN +// Shared EQ band param update logic — called from 0x8D HID handler and UART handler +static bool apply_eq_band_params(uint8_t mode, uint8_t band_index, uint8_t filter_type, + float fc, float q, float bw, float gain) +{ + if (isnan(fc) || isnan(q) || isnan(bw) || isnan(gain) || fc == 0) { + return false; + } + + filter_params_t new_params; + memset(&new_params, 0, sizeof(filter_params_t)); + new_params.index = band_index; + new_params.type = (filter_type_t)filter_type; + new_params.fc = fc; + new_params.q = q; + new_params.bw = bw; + new_params.gain = gain; + + filter_params_t *band_44100 = &sEQ_data_44100HZ[mode][0].bands[band_index]; + + if (eq_single_params_different(band_44100, &new_params)) { + *band_44100 = new_params; + memcpy(&sEQ_data_44100HZ[mode][1].bands[band_index], band_44100, sizeof(filter_params_t)); + + if (g_eq_enable == 1) { + SET_SHARED_GLOBAL(g_force_request_eq_mode_change, 1); + } + + uint32_t all_sample_rates[] = {48000, 88200, 96000, 176400, 192000}; + for (int i = 0; i < (int)(sizeof(all_sample_rates)/sizeof(all_sample_rates[0])); i++) { + calculate_current_mode_coefficients(all_sample_rates[i], mode); + } + calculate_current_mode_coefficients(44100, mode); + + eq_process_single_param_change(44100, mode, 0, band_index, band_44100); + eq_flash_mark_dirty(mode); + } + + return true; +} +#endif + +void program_key(unsigned char *, signed int); +#if (USE_EX3D == 1) && (HID_CONTROLS == 1) +extern void hid_receive_task_in_c(unsigned char * RcvData, unsigned * SendData); +#ifndef HID_MAX_DATA_BYTES +#define HID_MAX_DATA_BYTES (64) +#endif +static chanend_t g_ex3d_hid_chanend = 0; +static unsigned ex3d_b0b1_result[HID_MAX_DATA_BYTES / 4] = {0}; +extern unsigned g_hid_expand_gain_request; +extern unsigned g_hid_lmt_threshold_request; +void SetEx3dHidChan(chanend_t c) { g_ex3d_hid_chanend = c; } +#endif + +// 0x84 FACTORY_RESET: 删除Flash参数后触发重启(由button_task轮询执行) +extern unsigned g_request_factory_reset; +extern int lfs_remove_file(const char *path); +unsigned char process_send_params(uint8_t data[], uint16_t len) { + if (len < 60) { + return false; + } + + if (data[0] == 0x77 && data[1] == 0x5B && key_program == 0) + { + key_program = 1; + program_key(data, len); + } + +#if EQ_EN + // Check sync header + if (data[0] != 0x77) { + return false; + } + + // Process read request (0x8E) - GET_EQ_PARAMS + if (data[1] == 0x8E) { + // Get mode value + read_request.mode = data[2]; + // Get EQ index + read_request.eq_index = data[3]; + read_request.pending_cmd = 0x8E; + } + + if (data[1] == 0x8a) + { + if (data[2] > EQ_MODE_MAX ) { + return false; + } + SET_SHARED_GLOBAL(g_request_eq_mode, data[2]); + return true; + } + + // 处理读取EQ模式信息命令 (0x8B) - GET_EQ_MODE + if (data[1] == 0x8b) { + uint8_t mode = data[2]; + + // 验证mode参数:0xFF表示获取当前模式,0-9表示获取指定模式 + if (mode == 0xFF) { + read_request.pending_cmd = 0x8B; + read_request.read_mode_request_mode = 0xFF; + } else if (mode <= EQ_MODE_MAX) { + // 获取指定模式信息 + read_request.pending_cmd = 0x8B; + read_request.read_mode_request_mode = mode; + } else { + return false; + } + return true; + } + // 处理设置EQ整体增益和名称命令 (0x8C) - SET_MODE_GAIN_AND_NAME + if (data[1] == 0x8c) { + // 获取模式值 + uint8_t mode = data[2]; + + if (mode < EQ_USER_MODE_MIN || mode > EQ_USER_MODE_MAX ) { + return false; + } + // 获取增益值 (4字节,范围0到-50dB) + uint32_t gain = (uint32_t)data[3] | ((uint32_t)data[4] << 8) | ((uint32_t)data[5] << 16) | ((uint32_t)data[6] << 24); + + int32_t new_gain = clamp_post_gain((int32_t)gain); + + // 获取模式名称 (16字节,UTF-8编码) + char mode_name[17] = {0}; // 16字节 + 1个null终止符 + memcpy(mode_name, &data[7], 16); + mode_name[16] = '\0'; // 确保null终止 + + // 获取44100采样率下该模式的当前gain值,判断是否有变化 + int32_t current_gain_44100 = sEQ_data_44100HZ[mode][0].post_gain_db; + bool gain_changed = (current_gain_44100 != new_gain); + + // 获取当前模式名称,判断是否有变化 + char current_name[16] = {0}; + strncpy(current_name, g_mode_info.mode_names[mode], 15); + current_name[15] = '\0'; + + // 比较名称是否有变化 + bool name_changed = (strncmp(current_name, mode_name, 15) != 0); + + if (!is_name_empty(mode_name, 16) && name_changed) { + set_mode_name(mode, mode_name); + eq_mark_name_dirty(); + } + + if (gain_changed) { + if (mode < NUM_EQ_MODES) { + sEQ_data_44100HZ[mode][0].post_gain_db = new_gain; + sEQ_data_44100HZ[mode][1].post_gain_db = new_gain; + } + + set_mode_gain(mode, new_gain); + eq_mark_gain_dirty(); + } + + return true; + } + + // 处理获取设备信息命令 (0x8F) - GET_DEVICE_INFO + if (data[1] == 0x8f) { + read_request.pending_cmd = 0x8F; + return true; + } + + // 处理复位EQ参数命令 (0x90) - RESET_EQ_PARAMS + if (data[1] == 0x90) { + // 获取模式号 + uint8_t mode = data[2]; + + reset_eq_params(mode); + + // 设置读取请求标志,读取时只返回保存的状态 + read_request.pending_cmd = 0x90; + read_request.mode = mode; + + device_reboot(); + while(1); // 防止编译器警告 + + return true; + } + + if (data[1] == 0x91) + { + // 设置读取请求,返回EQ模式总数 + read_request.pending_cmd = 0x91; + return true; + } + + // 处理设置并保存EQ模式命令 (0x92) - SET_AND_SAVE_EQ_MODE + if (data[1] == 0x92) { + read_request.pending_cmd = 0x92; + + // 获取模式值 + uint8_t mode = data[2]; + + // 验证模式值范围 + if (mode > EQ_MODE_MAX ) { + read_request.mode = 0xFF; // 使用0xFF表示失败 + return true; + } + + // 设置当前模式 + SET_SHARED_GLOBAL(g_request_eq_mode, mode); + + // 更新保存的模式(用于0x9D启用时恢复) + g_saved_eq_mode = mode; + + // 保存到Flash(只保存模式,不影响使能状态) + eq_flash_save_current_mode(mode); + read_request.mode = mode; + + return true; + } + + // 处理设置麦克风增益命令 (0x82) - SET_MIC_VOLUME + // 范围: 0-37 (0=mute, 1-37=0dB~36dB, 1dB/步; register=38的+1.5dB仅firmware内部) + if (data[1] == 0x82) { + uint8_t mic_level = data[2]; + + // 参数验证: 0-37 + if (mic_level > 37) { + return false; + } + + extern unsigned g_mic_volume_level; + extern unsigned g_request_mic_volume_set; + g_mic_volume_level = mic_level; + g_request_mic_volume_set = 1; + read_request.pending_cmd = 0x83; // 设置后用0x83格式回报当前增益 + + return true; + } + + // 处理读取麦克风增益命令 (0x83) - GET_MIC_VOLUME + if (data[1] == 0x83) { + read_request.pending_cmd = 0x83; + return true; + } + + // 处理设置AI降噪强度命令 (0x85) - SET_AI_NOISE_STRENGTH + // 有效值: 0(关闭), 2,4,...,100(步进2); 强度映射: strength*2 → dB (100→-200dB) + if (data[1] == 0x85) { + uint8_t strength = data[2]; + if (strength > 100 || (strength != 0 && strength % 2 != 0)) { + return false; + } + extern unsigned g_dnr_strength; + extern unsigned g_request_dnr_strength_set; + g_dnr_strength = strength; + g_request_dnr_strength_set = 1; + read_request.pending_cmd = 0x86; // 设置后用0x86格式回报当前强度 + return true; + } + + // 处理读取AI降噪强度命令 (0x86) - GET_AI_NOISE_STRENGTH + if (data[1] == 0x86) { + read_request.pending_cmd = 0x86; + return true; + } + + // 处理设置耳返开关命令 (0x87) - SET_MONITOR_SWITCH + // 0=关闭耳返(ADC不路由到耳机输出),1=开启耳返 + if (data[1] == 0x87) { + uint8_t mon_sw = data[2]; + if (mon_sw > 1) { + return false; + } + extern unsigned g_monitor_switch; + extern unsigned g_request_monitor_switch_set; + g_monitor_switch = mon_sw; + g_request_monitor_switch_set = 1; + read_request.pending_cmd = 0x88; // 设置后用0x88格式回报当前状态 + return true; + } + + // 处理读取耳返开关命令 (0x88) - GET_MONITOR_SWITCH + if (data[1] == 0x88) { + read_request.pending_cmd = 0x88; + return true; + } + + // 处理设置音量命令 (0x93) - SET_VOLUME + // 范围: 0-15 (0=静音, 1-15=-28dB~0dB, 2dB/步) + if (data[1] == 0x93) { + // 获取音量级别 + uint8_t volume_level = data[2]; + + // 参数验证: 0-29 (0=mute, 1-29=-28dB~0dB, 1dB/步) + if (volume_level > 29) { + return false; + } + + // 设置全局变量(直接赋值,因为在C文件中) + extern unsigned g_volume_level; + g_volume_level = volume_level; + g_request_volume_set = 1; + + return true; + } + + // 处理读取音量命令 (0x94) - GET_VOLUME + if (data[1] == 0x94) { + read_request.pending_cmd = 0x94; + return true; + } + + // 处理设置EQ使能开关命令 (0x9D) - SET_EQ_ENABLE + if (data[1] == 0x9D) { + uint8_t enable = data[2]; // 0=OFF禁用, 1=ON启用 + + if (enable > 1) { + return false; + } + + g_eq_enable = enable; + + // 只保存EQ使能状态到Flash,不影响已保存的模式(0x92保存的模式) + eq_flash_save_eq_enable(enable); + + read_request.pending_cmd = 0x9D; + return true; + } + + // 处理获取EQ使能开关命令 (0x9E) - GET_EQ_ENABLE + if (data[1] == 0x9E) { + read_request.pending_cmd = 0x9E; + return true; + } + + // 处理获取采样率和格式命令 (0x9F) - GET_SAMPLE_FORMAT + if (data[1] == 0x9F) { + read_request.pending_cmd = 0x9F; + return true; + } + + // 处理设置音效模式命令 (0xA4) - SET_SOUND_EFFECT_MODE + // 0=无音效(EQ/EX3D全关), 1=音乐模式, 2=游戏模式, 3=AI7.1模式 + if (data[1] == 0xA4) { + uint8_t game_mode = data[2]; + + // 参数验证:0-3 + if (game_mode > 3) { + return false; + } + + extern unsigned g_request_game_mode; + SET_SHARED_GLOBAL(g_request_game_mode, game_mode); + + return true; + } + + // 处理获取音效模式命令 (0xA5) - GET_SOUND_EFFECT_MODE + if (data[1] == 0xA5) { + read_request.pending_cmd = 0xA5; + return true; + } + + // 处理获取固件版本命令 (0xA6) - GET_FIRMWARE_VERSION + if (data[1] == 0xA6) { + read_request.pending_cmd = 0xA6; + return true; + } + + // 处理设置log打印开关命令 (0x70) - SET_LOG_SWITCH + // 改动原因:添加0x70命令,用全局变量g_log_switch记录开关状态(0=关闭 1=开启),debug_printf 据此决定是否输出 + if (data[1] == 0x70) { + uint8_t on = data[2]; + if (on > 1) { + debug_printf("Error: Invalid log switch %d (must be 0 or 1)\n", on); + return false; + } + g_log_switch = on; + debug_printf("Log switch set to %d (0=off, 1=on)\n", on); + return true; + } + + + + // 处理恢复出厂默认命令 (0x84) - FACTORY_RESET + if (data[1] == 0x84) { + // 通知button_task在合适时机删除其他文件并重启 + SET_SHARED_GLOBAL(g_request_factory_reset, 1); + read_request.pending_cmd = 0x84; + return true; + } + + // 处理发送/读取EX3D命令 (0xB0/0xB1) - SET_EX3D_CMD / GET_EX3D_CMD + // 数据通过 g_ex3d_hid_chanend 直接传到tile1的hid_button_task执行,结果同步返回 + if (data[1] == 0xB0 || data[1] == 0xB1) { +#if (USE_EX3D == 1) && (HID_CONTROLS == 1) + if (!g_ex3d_hid_chanend) return false; + + // 提取EX3D命令码(4字节,小端序) + uint32_t ex3d_cmd = (uint32_t)data[2] | ((uint32_t)data[3] << 8) | + ((uint32_t)data[4] << 16) | ((uint32_t)data[5] << 24); + + uint8_t params_len = (uint8_t)((len > 62) ? 56 : (len > 6 ? len - 6 : 0)); + + // 存储到ex3d_request供process_read_params构建响应 + ex3d_request.ex3d_command_code = ex3d_cmd; + ex3d_request.ex3d_params_len = params_len; + if (params_len > 0) memcpy(ex3d_request.ex3d_params, &data[6], params_len); + ex3d_request.pending_cmd = data[1]; + + // 若是SET CMD_EXPAND_GAIN (0x93),同步更新footstep LED状态 + if (data[1] == 0xB0 && ex3d_cmd == 0x93 && params_len >= 4) { + uint32_t gain_val = (uint32_t)data[6] | ((uint32_t)data[7] << 8) | + ((uint32_t)data[8] << 16) | ((uint32_t)data[9] << 24); + SET_SHARED_GLOBAL(g_hid_expand_gain_request, gain_val); + } + + // 若是SET CMD_LMT_THRESHOLD (0x87),通知button_task保存阈值到flash + // 存储编码: -threshold (0~35),避免与哨兵值0xFFFFFFFF冲突 + if (data[1] == 0xB0 && ex3d_cmd == 0x87 && params_len >= 4) { + int32_t threshold_val = (int32_t)((uint32_t)data[6] | ((uint32_t)data[7] << 8) | + ((uint32_t)data[8] << 16) | ((uint32_t)data[9] << 24)); + if (threshold_val >= -35 && threshold_val <= 0) { + // Encode as (-threshold_val + 1) so range is 1~36. + // Avoids save_code=0 (flash "uninitialized" sentinel) when threshold_val=0. + uint32_t save_code = (uint32_t)(-threshold_val) + 1; + SET_SHARED_GLOBAL(g_hid_lmt_threshold_request, save_code); + } + } + + // 发送命令到tile1并同步等待结果 + // 协议: cmd(1 byte) + ex3d_cmd_code(4 bytes) + params_len(1 byte) + params(N bytes) + chan_out_byte(g_ex3d_hid_chanend, data[1]); + chan_out_word(g_ex3d_hid_chanend, ex3d_cmd); + chan_out_byte(g_ex3d_hid_chanend, params_len); + for (int i = 0; i < params_len; i++) { + chan_out_byte(g_ex3d_hid_chanend, data[6 + i]); + } + // 接收tile1返回的结果(16个word) + for (int i = 0; i < (HID_MAX_DATA_BYTES / 4); i++) { + ex3d_b0b1_result[i] = chan_in_word(g_ex3d_hid_chanend); + } + return true; +#else + return false; +#endif + } + + if (data[1] == 0x8d) + { + uint8_t mode = data[2]; + if (mode < EQ_USER_MODE_MIN || mode > EQ_USER_MODE_MAX) { + return false; + } + uint8_t band_index = data[3]; + if (band_index >= MAX_EQ_BANDS) { + return false; + } + // 解析浮点参数(小端序) + float fc, q, bw, gain; + uint32_t v; + v = data[5]|(data[6]<<8)|(data[7]<<16)|(data[8]<<24); memcpy(&fc, &v, 4); + v = data[9]|(data[10]<<8)|(data[11]<<16)|(data[12]<<24); memcpy(&q, &v, 4); + v = data[13]|(data[14]<<8)|(data[15]<<16)|(data[16]<<24);memcpy(&bw, &v, 4); + v = data[17]|(data[18]<<8)|(data[19]<<16)|(data[20]<<24);memcpy(&gain, &v, 4); + return apply_eq_band_params(mode, band_index, data[4], fc, q, bw, gain); + } + +#endif // #if EQ_EN (for process_send_params EQ commands section starting at line 1228) + +#if HID_DFU_EN + // 0xA7 START: 转发到button_task (tile[0])执行,因为flash访问需要在tile[0]进行 + if (data[1] == FIRMWARE_UPGRADE_START) { + dfu_chan_forward(data, len); + return true; + } + // 0xA8 DATA / 0xA9 END / 0xAA STATUS / 0xAB ABORT / 0xAC ERASE: 直接处理 + if (data[1] >= FIRMWARE_UPGRADE_DATA && data[1] <= FIRMWARE_UPGRADE_ERASE) { + unsigned char result; + if (data[1] == FIRMWARE_UPGRADE_DATA) + result = handle_firmware_upgrade_data(data, len); + else if (data[1] == FIRMWARE_UPGRADE_END) + result = handle_firmware_upgrade_end(data, len); + else if (data[1] == FIRMWARE_UPGRADE_STATUS) + result = handle_firmware_upgrade_status(data, len); + else if (data[1] == FIRMWARE_UPGRADE_ABORT) + result = handle_firmware_upgrade_abort(data, len); + else + result = handle_firmware_upgrade_erase(data, len); + (void)result; + return true; + } +#endif + + // 0xAE DEVICE_REBOOT (通用命令,不仅限于固件升级) + if (data[1] == 0xAE) { + debug_printf("Received device REBOOT command (0xAE)\n"); + device_reboot(); + while(1); + return true; + } + + return true; +} + +#if EQ_EN + +// 标记增益和模式名称为脏(延迟保存) +extern void eq_mark_gain_dirty(void); +extern void eq_mark_name_dirty(void); + +// set_mode_gain: set gain for specified mode +void set_mode_gain(uint8_t mode, int32_t gain) { + if (mode >= NUM_EQ_MODES) { + return; + } + + int32_t clamped_gain = clamp_post_gain(gain); + + // 设置模式增益 + g_mode_info.mode_gains[mode] = clamped_gain; + + // 注意:不在这里自动标记脏标志,由调用者根据实际情况决定是否标记 +} + +// set_mode_name: set name for specified mode +void set_mode_name(uint8_t mode, const char* name) { + if (mode >= NUM_EQ_MODES || name == NULL) { + return; + } + + // 设置模式名称 + strncpy(g_mode_info.mode_names[mode], name, 15); + g_mode_info.mode_names[mode][15] = '\0'; // 确保null终止 + + // 注意:不在这里自动标记脏标志,由调用者根据实际情况决定是否标记 +} + +// get_mode_gain: retrieve gain for specified mode +void get_mode_gain(uint8_t mode, int32_t* gain) { + if (mode >= NUM_EQ_MODES || gain == NULL) { + return; + } + + *gain = g_mode_info.mode_gains[mode]; +} + +// get_mode_name: retrieve name for specified mode +void get_mode_name(uint8_t mode, char* name) { + if (mode >= NUM_EQ_MODES || name == NULL) { + return; + } + + strncpy(name, g_mode_info.mode_names[mode], 15); + name[15] = '\0'; // 确保null终止 +} + +// init_mode_info: initialize mode info with default names and gains +void init_mode_info(void) { + // 初始化默认模式名称 + const char* default_names[NUM_EQ_MODES] = { + "JAZZ", "POP", "ROCK", "CLASIC", "R&B", + "3A Game", "FPS", "HI-FI", "VOCAL", "DANCE", + "user 1", "user 2" + }; + + // 初始化默认增益(0dB) + for (int i = 0; i < NUM_EQ_MODES; i++) { + g_mode_info.mode_gains[i] = 0; + strncpy(g_mode_info.mode_names[i], default_names[i], 15); + g_mode_info.mode_names[i][15] = '\0'; + } +} + +#endif + +void get_key_ret(uint8_t *buffer); + +// process_read_params: build HID response from pending request state +unsigned char process_read_params(uint8_t response[]) { + memset(response, 0, 63); + +#if EQ_EN + // 处理读取当前EQ模式的请求 (0x8B) - GET_EQ_MODE + if (read_request.pending_cmd == 0x8B) { + RSP_HDR(response, 0x8B); + + // 确定要返回的模式值 + uint8_t target_mode; + if (read_request.read_mode_request_mode == 0xFF) { + // 获取当前模式(eq_enable是全局开关,不影响模式返回) + unsigned int current_mode; + GET_SHARED_GLOBAL(current_mode, g_request_eq_mode); + target_mode = current_mode; + } else { + // 获取指定模式 + target_mode = read_request.read_mode_request_mode; + } + + // 模式值 + response[2] = target_mode; + + if (target_mode >= EQ_DISABLED_MODE || target_mode > EQ_MODE_MAX) { + // 返回全0 + memset(&response[3], 0, 20); // 整体增益(4字节) + 模式名称(16字节) = 20字节 + } else { + // 获取指定模式的整体增益 (从当前采样率获取) + int32_t target_post_gain = 0; + if (target_mode < NUM_EQ_MODES) { + target_post_gain = sEQ_data_44100HZ[target_mode][0].post_gain_db; + } + + // 整体增益 (4字节,位置3-6) + memcpy(&response[3], &target_post_gain, 4); + + // 模式名称 (16字节,UTF-8编码,位置7-22) + if (target_mode < NUM_EQ_MODES) { + memcpy(&response[7], g_mode_info.mode_names[target_mode], 16); + } else { + // 如果模式超出范围,填充0 + memset(&response[7], 0, 16); + } + } + + read_request.pending_cmd = 0; // Clear read request flag + return true; + } + + // 处理获取设备信息的请求 (0x8F) - GET_DEVICE_INFO + if (read_request.pending_cmd == 0x8F) { + RSP_HDR(response, 0x8F); + + get_device_info(response); + + read_request.pending_cmd = 0; // Clear read request flag + return true; + } + + // 处理复位EQ参数的请求 (0x90) - RESET_EQ_PARAMS + if (read_request.pending_cmd == 0x90) { + RSP_HDR(response, 0x90); + + if (read_request.mode != 0xFF && read_request.mode >= EQ_PRESET_MODE_MIN && read_request.mode <= EQ_USER_MODE_MAX) { + response[3] = 0x01; // 失败 + } else { + response[3] = 0x00; // 成功 + } + + read_request.pending_cmd = 0; // Clear read request flag + return true; + } + + // 处理获取EQ模式总数的请求 (0x91) - GET_EQ_MODE_COUNT + if (read_request.pending_cmd == 0x91) { + RSP_HDR(response, 0x91); + + // 模式总数:预定义模式(0-9) + 用户模式(10-11) = 12,不包含禁用模式(12) + uint8_t mode_count = NUM_EQ_MODES; // 固定返回12 + response[2] = mode_count; + + // 预定义模式数量:0-9共10个预设模式 + uint8_t preset_mode_count = EQ_PRESET_MODE_MAX + 1; + response[3] = preset_mode_count; + + read_request.pending_cmd = 0; // Clear read request flag + return true; + } + + // 处理设置并保存EQ模式的请求 (0x92) - SET_AND_SAVE_EQ_MODE + if (read_request.pending_cmd == 0x92) { + RSP_HDR(response, 0x92); + + // 状态码:0x00=成功, 0x01=失败 + uint8_t status = (read_request.mode == 0xFF) ? 0x01 : 0x00; + response[2] = status; + + read_request.pending_cmd = 0; // Clear read request flag + return true; + } + // 处理读取麦克风增益请求 (0x83) - GET_MIC_VOLUME + if (read_request.pending_cmd == 0x83) { + RSP_HDR(response, 0x83); + + extern unsigned g_mic_volume_level; + response[2] = (uint8_t)g_mic_volume_level; + + read_request.pending_cmd = 0; + return true; + } + + // 处理AI降噪强度请求/响应 (0x86) - GET_AI_NOISE_STRENGTH + if (read_request.pending_cmd == 0x86) { + RSP_HDR(response, 0x86); + + extern unsigned g_dnr_strength; + response[2] = (uint8_t)g_dnr_strength; + + read_request.pending_cmd = 0; + return true; + } + + // 处理耳返开关请求/响应 (0x88) - GET_MONITOR_SWITCH + if (read_request.pending_cmd == 0x88) { + RSP_HDR(response, 0x88); + + extern unsigned g_monitor_switch; + response[2] = (uint8_t)g_monitor_switch; + + read_request.pending_cmd = 0; + return true; + } + + // 处理读取音量请求 (0x94) - GET_VOLUME + if (read_request.pending_cmd == 0x94) { + RSP_HDR(response, 0x94); + + // 读取全局变量(直接读取,因为在C文件中) + extern unsigned g_volume_level; + unsigned volume_level = g_volume_level; + + // 当前音量级别 (1字节,位置2) + response[2] = (uint8_t)volume_level; + + read_request.pending_cmd = 0; // Clear read request flag + return true; + } + + // 处理设置EQ使能开关请求 (0x9D) - SET_EQ_ENABLE + if (read_request.pending_cmd == 0x9D) { + RSP_HDR(response, 0x9D); + + // 状态码:0x00=成功, 0x01=失败 + response[2] = 0x00; // 成功 + + // 当前EQ使能状态(全局开关,所有模式共用) + response[3] = g_eq_enable; + + read_request.pending_cmd = 0; // Clear request flag + return true; + } + + // 处理获取EQ使能开关请求 (0x9E) - GET_EQ_ENABLE + if (read_request.pending_cmd == 0x9E) { + RSP_HDR(response, 0x9E); + + // EQ使能开关状态(全局开关,所有模式共用) + response[2] = g_eq_enable; + + read_request.pending_cmd = 0; // Clear request flag + return true; + } + + // 处理获取采样率和格式请求 (0x9F) - GET_SAMPLE_FORMAT + if (read_request.pending_cmd == 0x9F) { + RSP_HDR(response, 0x9F); + + // 读取全局变量(使用SHARED_GLOBAL宏) + extern unsigned g_samfreq, g_dsd_mode; + unsigned samfreq, dsd_mode; + GET_SHARED_GLOBAL(samfreq, g_samfreq); + GET_SHARED_GLOBAL(dsd_mode, g_dsd_mode); + + // 采样率(32位,小端序,位置2-5) + memcpy(&response[2], &samfreq, 4); + + // DSD模式(1字节,位置6) + response[6] = (uint8_t)dsd_mode; + + read_request.pending_cmd = 0; // Clear request flag + return true; + } + + // 处理获取游戏模式请求 (0xA5) - GET_GAME_MODE + if (read_request.pending_cmd == 0xA5) { + RSP_HDR(response, 0xA5); + + // 读取全局变量(使用SHARED_GLOBAL宏) + extern unsigned g_game_mode; + unsigned game_mode; + GET_SHARED_GLOBAL(game_mode, g_game_mode); + + // 当前游戏模式 (1字节,位置2) + response[2] = (uint8_t)game_mode; + + read_request.pending_cmd = 0; // Clear read request flag + return true; + } + + // 处理获取固件版本请求 (0xA6) - GET_FIRMWARE_VERSION + if (read_request.pending_cmd == 0xA6) { + RSP_HDR(response, 0xA6); + + // 读取固件版本号(BCD格式) + response[2] = (uint8_t)BCD_DEVICE_J; // 主版本号 + response[3] = (uint8_t)BCD_DEVICE_M; // 次版本号 + response[4] = (uint8_t)BCD_DEVICE_N; // 修订版本号 + + read_request.pending_cmd = 0; // Clear read request flag + return true; + } + + // 处理恢复出厂默认响应 (0x84) - FACTORY_RESET + if (read_request.pending_cmd == 0x84) { + RSP_HDR(response, 0x84); + response[2] = 0x00; // 成功 + read_request.pending_cmd = 0; + return true; + } + + // 处理发送EX3D命令响应 (0xB0) - SET_EX3D_CMD + // 结果已在process_send_params中同步从tile1获取,存于ex3d_b0b1_result + if (ex3d_request.pending_cmd == 0xB0) { + RSP_HDR(response, 0xB0); + +#if (USE_EX3D == 1) && (HID_CONTROLS == 1) + unsigned *send_data = ex3d_b0b1_result; + + // 状态码:检查send_data[1]是否有错误返回值(0xFFFFFFFF表示参数错误) + uint8_t status = (send_data[1] == 0xFFFFFFFF) ? 0x01 : 0x00; + response[2] = status; + + // 回显命令码(4字节) + response[3] = (uint8_t)(ex3d_request.ex3d_command_code & 0xFF); + response[4] = (uint8_t)((ex3d_request.ex3d_command_code >> 8) & 0xFF); + response[5] = (uint8_t)((ex3d_request.ex3d_command_code >> 16) & 0xFF); + response[6] = (uint8_t)((ex3d_request.ex3d_command_code >> 24) & 0xFF); + + // 返回send_data[1]中的返回值 + response[7] = (uint8_t)(send_data[1] & 0xFF); + response[8] = (uint8_t)((send_data[1] >> 8) & 0xFF); + response[9] = (uint8_t)((send_data[1] >> 16) & 0xFF); + response[10] = (uint8_t)((send_data[1] >> 24) & 0xFF); +#else + response[2] = 0x01; // 失败:EX3D支持未启用 +#endif + + ex3d_request.pending_cmd = 0; // Clear request flag + return true; + } + + // 处理读取EX3D命令响应 (0xB1) - GET_EX3D_CMD + // 结果已在process_send_params中同步从tile1获取,存于ex3d_b0b1_result + if (ex3d_request.pending_cmd == 0xB1) { + RSP_HDR(response, 0xB1); + +#if (USE_EX3D == 1) && (HID_CONTROLS == 1) + unsigned *send_data = ex3d_b0b1_result; + + // 回显命令码(4字节) + response[2] = (uint8_t)(ex3d_request.ex3d_command_code & 0xFF); + response[3] = (uint8_t)((ex3d_request.ex3d_command_code >> 8) & 0xFF); + response[4] = (uint8_t)((ex3d_request.ex3d_command_code >> 16) & 0xFF); + response[5] = (uint8_t)((ex3d_request.ex3d_command_code >> 24) & 0xFF); + + // 复制返回值(从send_data[1]开始,最多14个uint32值) + int response_idx = 6; + for (int i = 1; i < 16 && response_idx < 63; i++) { + if (response_idx + 3 < 63) { + response[response_idx++] = (uint8_t)(send_data[i] & 0xFF); + response[response_idx++] = (uint8_t)((send_data[i] >> 8) & 0xFF); + response[response_idx++] = (uint8_t)((send_data[i] >> 16) & 0xFF); + response[response_idx++] = (uint8_t)((send_data[i] >> 24) & 0xFF); + } + } +#else + // EX3D支持未启用,返回错误 + response[2] = 0xFF; + response[3] = 0xFF; + response[4] = 0xFF; + response[5] = 0xFF; +#endif + + ex3d_request.pending_cmd = 0; // Clear request flag + return true; + } + + if (read_request.pending_cmd == 0x8E) { + + // 检查EQ索引范围 + if (read_request.eq_index >= MAX_EQ_BANDS) { + return false; + } + + // Build response data + RSP_HDR(response, 0x8E); + + response[2] = read_request.mode; // Mode + response[3] = read_request.eq_index & 0xFF; // EQ index + + if (read_request.mode >= EQ_DISABLED_MODE || read_request.mode > EQ_MODE_MAX) { + // 返回全0 + response[4] = 0; // Filter type + memset(&response[5], 0, 16); // 所有参数(fc, q, bw, gain)都设为0 + } else { + // 优先使用44100Hz数据,因为串口设置参数时主要存储到44100Hz + filter_params_t *band = &sEQ_data_44100HZ[read_request.mode][0].bands[read_request.eq_index]; + response[4] = band->type; + + // Copy parameters - 直接写入float值 (位置调整) + memcpy(&response[5], &band->fc, 4); + memcpy(&response[9], &band->q, 4); + memcpy(&response[13], &band->bw, 4); + memcpy(&response[17], &band->gain, 4); + } + + // Copy coefficients + read_request.pending_cmd = 0; // Clear read request flag + + return true; + } +#endif +#if 1 + if (key_program == 1) + { + key_program = 0; + get_key_ret(response); + return true; + } +#endif + return false; +} diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq.h diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_channel_protocol.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_channel_protocol.h new file mode 100644 index 0000000..4a2706c --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_channel_protocol.h @@ -0,0 +1,67 @@ +#ifndef __EQ_CHANNEL_PROTOCOL_H__ +#define __EQ_CHANNEL_PROTOCOL_H__ + +/** + * @file eq_channel_protocol.h + * @brief EQ 参数跨 tile 通道通信协议 + * + * 当 dsp_core0 运行在 tile[1] 时, process_send_params (tile[0]) 和 + * AudioHwRemote (tile[0]) 通过 chanend c_eq_params 将 EQ 参数变更 + * 发送到 tile[1] 的 dsp_core0. + * + * 通道数据格式: [cmd:32] [payload...] + * - 发送端先发 cmd word, 再发各命令对应的 payload words + * - 接收端在 dsp_core0 主循环中 non-blocking select 接收 + */ + +/*--- 命令码定义 ---*/ + +/** 切换EQ模式 (对应 HID 0x8A / 0x92) + * payload: [mode:32] + */ +#define EQ_CMD_SET_MODE 0x01 + +/** EQ使能开关 (对应 HID 0x9D) + * payload: [enable:32] (0=OFF, 1=ON) + */ +#define EQ_CMD_SET_ENABLE 0x02 + +/** 更新单个 band 参数 (对应 HID 0x8D) + * payload: [mode:32] [band_index:32] [filter_type:32] + * [fc_bits:32] [q_bits:32] [bw_bits:32] [gain_bits:32] + * float 值以 memcpy 方式打包到 uint32 + */ +#define EQ_CMD_UPDATE_BAND 0x03 + +/** 设置模式增益 (对应 HID 0x8C 的增益部分) + * payload: [mode:32] [gain_db:32] (int32_t, 0 ~ -50) + */ +#define EQ_CMD_SET_GAIN 0x05 + +/** 强制刷新系数 (对应 apply_eq_band_params 中的 force_mode_change) + * payload: 无 + */ +#define EQ_CMD_FORCE_REFRESH 0x06 + +/** Flash 初始化完成, 传输单个 mode 的完整参数 + * payload: [mode:32] [ch:32] [post_gain_db:32] + * 每个 band (共 MAX_EQ_BANDS 个): + * [type:32] [fc_bits:32] [q_bits:32] [bw_bits:32] [gain_bits:32] [index:32] + * 发送完所有 mode/ch 后发 EQ_CMD_FLASH_INIT_COMPLETE + */ +#define EQ_CMD_FLASH_LOAD_MODE 0x10 + +/** Flash 初始化全部完成, 开始正常运行 + * payload: [saved_mode:32] [eq_enable:32] + */ +#define EQ_CMD_FLASH_INIT_COMPLETE 0x11 + +/*--- 辅助宏 ---*/ + +/** 将 float 打包为 uint32_t 以便通过 channel 传输 */ +#define FLOAT_TO_U32(f) ({ float _f = (f); uint32_t _u; memcpy(&_u, &_f, 4); _u; }) + +/** 将 uint32_t 解包为 float */ +#define U32_TO_FLOAT(u) ({ uint32_t _u = (u); float _f; memcpy(&_f, &_u, 4); _f; }) + +#endif /* __EQ_CHANNEL_PROTOCOL_H__ */ diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_coefficient_calculator.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_coefficient_calculator.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_coefficient_calculator.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_coefficient_calculator.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_coefficient_calculator.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_coefficient_calculator.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_coefficient_calculator.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_coefficient_calculator.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_designer_new.py b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_designer_new.py similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_designer_new.py rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_designer_new.py diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_designer_new.spec b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_designer_new.spec similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_designer_new.spec rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_designer_new.spec diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_flash_storage.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_flash_storage.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_flash_storage.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_flash_storage.c diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_flash_storage.c.bak b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_flash_storage.c.bak new file mode 100644 index 0000000..5501452 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_flash_storage.c.bak @@ -0,0 +1,2016 @@ +#define DEBUG_PRINT_ENABLE 0 +#include +#include +#include +#include +#include +#include "xmath/xmath.h" +#include "eq.h" +#include "eq_flash_storage.h" +#include "lfs_io.h" +#include "lfs.h" +#include "eq_coefficient_calculator.h" +#include "debug_print.h" + +#if 0 // DEBUG_PRINT_ENABLE +#define DPRINTF(...) printf(__VA_ARGS__) +#else +#define DPRINTF(...) +#endif +// 外部函数声明 +extern uint32_t get_reference_time(void); +extern lfs_t lfs; +void delay_milliseconds(unsigned int); +#define FILE_PATH_MAX_LENGTH 30 + +// EQ Flash存储目录和文件名全局变量定义(默认值) +// 改动原因:统一管理所有EQ相关文件的存储路径,便于支持不同产品的参数存储 +#if UAC2 == 1 +char *g_eq_flash_base_dir = "uac2"; // 基础目录名 +#elif FPS == 1 +char *g_eq_flash_base_dir = "fps"; // 基础目录名 +#elif SPATIAL_MODE == 1 +char *g_eq_flash_base_dir = "spa"; // 基础目录名 +#else +char *g_eq_flash_base_dir = "default"; // 基础目录名 +#endif +char *g_eq_flash_gains_file = "gains"; // 增益文件名 +char *g_eq_flash_mode_file = "cur_mode"; // 当前模式文件名 +char *g_eq_flash_enable_file = "eq_enable"; // EQ使能开关文件名(独立存储) +char *g_eq_flash_name_prefix = "name_"; // 模式名称文件前缀 + +// 外部EQ数组声明 +// 改动原因:进一步优化EQ参数存储策略,所有采样率共用44100Hz数组 +extern eq_mode_data_t sEQ_data_44100HZ[NUM_EQ_MODES][NUM_EQ_CHANS]; +extern void debug_print_eq_mode(eq_mode_data_t *eq_data); +extern unsigned int g_eq_sample_rate; + +// 支持的采样率数组 +static const uint32_t supported_sample_rates[EQ_FLASH_MAX_SAMPLE_RATES] = EQ_SAMPLE_RATES; + +// 存储状态 +static eq_storage_status_t storage_status = {0}; + +// 脏标志数组,用于跟踪哪些模式需要同步到Flash (简化版本,不按采样率分组) +static int dirty_flags[EQ_FLASH_MAX_MODES] = {0}; + +// 增益和模式名称脏标志(分别独立) +static int gain_dirty = 0; +static int name_dirty = 0; + +// 单参数存储数据库 +static eq_single_param_database_t single_param_database = {0}; + +// 内部函数声明 +static int get_sample_rate_index(uint32_t sample_rate); +static int create_eq_directory_structure(void); +// 元数据相关函数已移除,简化存储系统 +static int verify_file_header(const eq_file_header_t *header, uint8_t mode, uint8_t channel); +static eq_mode_data_t (*get_eq_array_ptr(uint32_t sample_rate))[NUM_EQ_MODES][NUM_EQ_CHANS]; + +/** + * @brief 获取采样率在数组中的索引 + * @param sample_rate 采样率 + * @return 索引,-1表示不支持 + */ +static int get_sample_rate_index(uint32_t sample_rate) { + for (int i = 0; i < EQ_FLASH_MAX_SAMPLE_RATES; i++) { + if (supported_sample_rates[i] == sample_rate) { + return i; + } + } + return -1; +} + +/** + * @brief 获取对应采样率的EQ数组指针 + * @param sample_rate 采样率 + * @return EQ数组指针,NULL表示不支持的采样率 + */ +static eq_mode_data_t (*get_eq_array_ptr(uint32_t sample_rate))[NUM_EQ_MODES][NUM_EQ_CHANS] { + // 改动原因:所有采样率共用44100Hz数组 + return &sEQ_data_44100HZ; +} + +/** + * @brief 验证文件头 (简化版本,去除采样率字段) + * @param header 文件头指针 + * @param mode 期望的模式 + * @param channel 期望的通道 + * @return 1: 有效, 0: 无效 + */ +static int verify_file_header(const eq_file_header_t *header, uint8_t mode, uint8_t channel) { + if (!header) return 0; + + return (header->magic == EQ_FLASH_MAGIC && + header->version == EQ_FLASH_VERSION && + header->mode == mode && + header->channel == channel && + header->data_size == sizeof(eq_mode_data_t)); +} + +/** + * @brief 创建EQ目录结构(按需创建) + * @return 0: 成功, -1: 失败 + */ +static int create_eq_directory_structure(void) { + char dir_path[64]; + + DPRINTF("Creating EQ directory structure (on-demand)\n"); + + // 检查主目录是否存在,不存在才创建 + // 改动原因:使用全局变量代替硬编码目录名,支持不同产品 + if (!lfs_file_exists(g_eq_flash_base_dir)) { + lfs_mkdir(&lfs, g_eq_flash_base_dir); + DPRINTF("Created %s directory\n", g_eq_flash_base_dir); + } else { + DPRINTF("%s directory already exists\n", g_eq_flash_base_dir); + } + + // 不再为每个采样率创建子目录,因为同一模式的参数是相同的 + DPRINTF("EQ parameters will be stored without sample rate grouping\n"); + + DPRINTF("EQ directory structure creation completed\n"); + return 0; +} + +// 元数据保存函数已移除,简化存储系统 + +// 元数据加载函数已移除,简化存储系统 + +// 元数据迁移函数已移除,简化存储系统 + +/** + * @brief 初始化EQ Flash存储系统 + * @return 0: 成功, -1: 失败 + */ +int eq_flash_init(void) { + DPRINTF("Initializing EQ Flash storage system\n"); + + // 初始化存储状态 + memset(&storage_status, 0, sizeof(storage_status)); + memset(dirty_flags, 0, sizeof(dirty_flags)); + + // 延迟一段时间,确保QSPI Flash已经初始化完成 + delay_milliseconds(100); + + // 尝试初始化LFS,最多重试3次 + int retry_count = 0; + int lfs_init_result = -1; + + while (retry_count < 3 && lfs_init_result != 0) { + lfs_init_result = lfs_init(); + if (lfs_init_result != 0) { + retry_count++; + DPRINTF("Failed to initialize LFS, retry %d/3\n", retry_count); + if (retry_count < 3) { + delay_milliseconds(200 * retry_count); // 递增延迟 + } + } + } + + if (lfs_init_result != 0) { + DPRINTF("Failed to initialize LFS after %d retries, continuing without Flash storage\n", retry_count); + // 即使LFS初始化失败,也标记为已初始化,系统继续运行 + storage_status.is_initialized = 1; + storage_status.has_flash_data = 0; + return 0; + } + + DPRINTF("LFS initialized successfully\n"); + + // 创建目录结构(按需创建) + create_eq_directory_structure(); + + // 检查是否有现有数据(通过检查目录是否存在) + // 改动原因:使用全局变量代替硬编码目录名 + storage_status.has_flash_data = lfs_file_exists(g_eq_flash_base_dir) ? 1 : 0; + storage_status.is_initialized = 1; + + DPRINTF("EQ Flash storage initialized, has_data: %d\n", storage_status.has_flash_data); + + lfs_deinit(); + return 0; +} + +/** + * @brief 反初始化EQ Flash存储系统 + */ +void eq_flash_deinit(void) { + DPRINTF("Deinitializing EQ Flash storage system\n"); + storage_status.is_initialized = 0; + storage_status.has_flash_data = 0; + memset(dirty_flags, 0, sizeof(dirty_flags)); +} + +// 注意:eq_flash_load_all 函数已移除,现在使用动态单参数存储系统 + +// 注意:eq_flash_save_all 函数已移除,现在使用动态单参数存储系统 + +/** + * @brief 从Flash加载指定模式的EQ参数 + * @param mode EQ模式 + * @return 0: 成功, -1: 失败 + */ +/** + * @brief Initialize eq_mode_data_t with default bypass values + * @param eq_data Target data to initialize + * @param sample_rate Sample rate to set + */ +static void init_eq_data_defaults(eq_mode_data_t *eq_data, uint32_t sample_rate) { + memset(eq_data, 0, sizeof(eq_mode_data_t)); + eq_data->sample_rate = sample_rate; + eq_data->total_bshift = 0; + eq_data->post_gain_db = 0; + for (int i = 0; i < MAX_EQ_BANDS; i++) { + eq_data->bands[i].index = i; + eq_data->bands[i].type = FILTER_TYPE_BYPASS; + eq_data->bands[i].fc = 1000.0f; + eq_data->bands[i].q = 0.707f; + eq_data->bands[i].bw = 1.0f; + eq_data->bands[i].gain = 0.0f; + } + for (int i = 0; i < 8; i++) { + eq_data->filter.coef[0][i] = 1 << 30; + eq_data->filter.coef[1][i] = 0; + eq_data->filter.coef[2][i] = 0; + eq_data->filter.coef[3][i] = 0; + eq_data->filter.coef[4][i] = 0; + } +} + +int eq_flash_load_mode(uint8_t mode) { + //DPRINTF("Loading EQ mode %d from Flash\n", mode); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: EQ Flash storage not initialized\n"); + return -1; + } + + if (mode >= NUM_EQ_MODES) { + DPRINTF(" Error: Invalid mode %d (max: %d)\n", mode, NUM_EQ_MODES - 1); + return -1; + } + + // 获取当前采样率的EQ数组指针 + eq_mode_data_t (*eq_array)[NUM_EQ_MODES][NUM_EQ_CHANS] = get_eq_array_ptr(g_eq_sample_rate); + if (!eq_array) { + DPRINTF(" Error: Failed to get EQ array for current sample rate %lu\n", (unsigned long)g_eq_sample_rate); + return -1; + } + + // 加载左右两个声道的数据 + for (int ch = 0; ch < NUM_EQ_CHANS; ch++) { + // 构建文件路径 + // 改动原因:使用全局变量代替硬编码目录名,统一文件路径管理 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/mode%d_ch%d.bin", g_eq_flash_base_dir, mode, ch); + DPRINTF(" File path (channel %d): %s\n", ch, file_path); + + // 检查文件是否存在 + if (!lfs_file_exists(file_path)) { + DPRINTF(" ****************** file is not exist (ch %d), initializing with default values\n", ch); + init_eq_data_defaults(&(*eq_array)[mode][ch], g_eq_sample_rate); + DPRINTF(" Initialized with default values for mode %d, channel %d\n", mode, ch); + continue; // 继续处理下一个通道 + } + + // 准备读取缓冲区 + eq_file_header_t header; + eq_flash_data_t flash_data; + uint8_t file_data[sizeof(header) + sizeof(flash_data)]; + + // 使用lfs_read_config读取文件 + DPRINTF(" Read file data, size: %zu bytes\n", sizeof(file_data)); + lfs_read_config(file_path, file_data, sizeof(file_data)); + + // 解析文件数据 + memcpy(&header, file_data, sizeof(header)); + memcpy(&flash_data, file_data + sizeof(header), sizeof(flash_data)); + + // 检查文件头基本信息 + DPRINTF(" File header: magic=0x%08X, version=%u, mode=%u\n", + header.magic, header.version, header.mode); + + // 验证文件头 + DPRINTF(" Verifying file header:\n"); + DPRINTF(" Expected: magic=0x%08X, version=%u, mode=%u, channel=%d, data_size=%zu\n", + EQ_FLASH_MAGIC, EQ_FLASH_VERSION, mode, ch, sizeof(eq_flash_data_t)); + DPRINTF(" Actual: magic=0x%08X, version=%u, mode=%u, channel=%u, data_size=%u\n", + header.magic, header.version, header.mode, header.channel, header.data_size); + + if (!verify_file_header(&header, mode, ch)) { + DPRINTF(" Invalid file header for mode %d, channel %d\n", mode, ch); + init_eq_data_defaults(&(*eq_array)[mode][ch], g_eq_sample_rate); + DPRINTF(" Initialized with default values for mode %d, channel %d\n", mode, ch); + continue; // 继续处理下一个通道 + } + + // 验证CRC32 + uint32_t calculated_crc = eq_flash_calculate_crc32((uint8_t*)&flash_data, sizeof(flash_data)); + if (calculated_crc != header.crc32) { + DPRINTF(" CRC32 mismatch for mode %d, sample rate %lu, channel %d\n", mode, (unsigned long)g_eq_sample_rate, ch); + init_eq_data_defaults(&(*eq_array)[mode][ch], g_eq_sample_rate); + DPRINTF(" Initialized with default values due to CRC32 mismatch for channel %d\n", ch); + continue; // 继续处理下一个通道 + } + + // 将Flash数据加载到EQ数组 + eq_mode_data_t *eq_data = &(*eq_array)[mode][ch]; + + // 检查Flash数据的完整性 + DPRINTF(" Flash data before restore (ch %d): active_bands_count=%d\n", + ch, flash_data.active_bands_count); + + DPRINTF(" EQ array after copy (ch %d): sample_rate=%u, post_gain_db=%ld\n", + ch, eq_data->sample_rate, (long)eq_data->post_gain_db); + DPRINTF(" EQ array total_bshift=%ld\n", + (long)eq_data->total_bshift); + + + // 检查内存地址是否有效 + if (eq_data == NULL) { + DPRINTF(" ERROR: eq_data is NULL\n"); + continue; // 继续处理下一个通道 + } + + DPRINTF(" eq_data address: %p\n", (void*)eq_data); + DPRINTF(" eq_data size: %zu bytes\n", sizeof(eq_mode_data_t)); + + // 检查bands数组地址是否有效 + DPRINTF(" bands array address: %p\n", (void*)&eq_data->bands); + DPRINTF(" bands array size: %zu bytes\n", sizeof(eq_data->bands)); + + // 检查bands数组偏移量 + DPRINTF(" bands offset in struct: %zu bytes\n", (size_t)&eq_data->bands - (size_t)eq_data); + + // 检查第一个band的地址 + DPRINTF(" bands[0] address: %p\n", (void*)&eq_data->bands[0]); + DPRINTF(" bands[0] size: %zu bytes\n", sizeof(eq_data->bands[0])); + + // 检查内存对齐 + DPRINTF(" eq_data alignment: %zu\n", (size_t)eq_data % 4); + DPRINTF(" bands alignment: %zu\n", (size_t)&eq_data->bands % 4); + + // 尝试安全地访问第一个band + DPRINTF(" Attempting to access bands[0]...\n"); + + // 先检查bands[0]的地址是否在合理范围内 + if ((size_t)&eq_data->bands[0] < (size_t)eq_data || + (size_t)&eq_data->bands[0] > (size_t)eq_data + sizeof(eq_mode_data_t)) { + DPRINTF(" ERROR: bands[0] address out of range\n"); + continue; // 继续处理下一个通道 + } + + // 暂时注释掉有问题的调试代码 + DPRINTF(" Accessing bands[0].index...\n"); + DPRINTF(" bands[0].index = %d\n", eq_data->bands[0].index); + + DPRINTF(" Accessing bands[0].type...\n"); + DPRINTF(" bands[0].type = %d\n", eq_data->bands[0].type); + + DPRINTF(" Accessing bands[0].fc...\n"); + DPRINTF(" bands[0].fc = %d.%02d Hz\n", (int)eq_data->bands[0].fc, (int)((eq_data->bands[0].fc - (int)eq_data->bands[0].fc) * 100)); + + DPRINTF(" Accessing bands[0].q...\n"); + DPRINTF(" bands[0].q = %d.%04d\n", (int)eq_data->bands[0].q, (int)((eq_data->bands[0].q - (int)eq_data->bands[0].q) * 10000)); + + DPRINTF(" Successfully loaded EQ parameters for mode %d, channel %d\n", mode, ch); + } + + return 0; +} + +/** + * @brief 检查滤波器是否为默认值(bypass) + * @param band 滤波器参数 + * @return 1: 是默认值, 0: 不是默认值 + */ +int is_filter_bypass(const filter_params_t *band) { + if (band == NULL) return 1; + + // 检查是否为bypass类型 + if (band->type != FILTER_TYPE_BYPASS) return 0; + + // 检查其他参数是否为默认值 + if (band->fc != 1000.0f) return 0; + if (band->q != 0.707f) return 0; + if (band->bw != 1.0f) return 0; + if (band->gain != 0.0f) return 0; + + return 1; +} + +/** + * @brief 从EQ数据中提取非默认滤波器到Flash存储格式 + * @param eq_data 源EQ数据 + * @param flash_data 目标Flash存储数据 + * @return 0: 成功, -1: 失败 + */ +int extract_active_filters(const eq_mode_data_t *eq_data, eq_flash_data_t *flash_data) { + if (eq_data == NULL || flash_data == NULL) return -1; + + flash_data->active_bands_count = 0; + + // 遍历所有滤波器,只保存非bypass的 + for (int i = 0; i < MAX_EQ_BANDS; i++) { + if (!is_filter_bypass(&eq_data->bands[i])) { + if (flash_data->active_bands_count >= MAX_EQ_BANDS) { + DPRINTF(" Warning: Too many active filters, truncating\n"); + break; + } + + // 复制非默认滤波器 + flash_data->active_bands[flash_data->active_bands_count] = eq_data->bands[i]; + flash_data->active_bands[flash_data->active_bands_count].index = flash_data->active_bands_count; + flash_data->active_bands_count++; + } + } + + DPRINTF(" Extracted %d active filters from %d total filters\n", + flash_data->active_bands_count, MAX_EQ_BANDS); + + return 0; +} + +/** + * @brief 从Flash存储格式恢复EQ数据 + * @param flash_data 源Flash存储数据 + * @param eq_data 目标EQ数据 + * @return 0: 成功, -1: 失败 + */ +int restore_eq_from_flash(const eq_flash_data_t *flash_data, eq_mode_data_t *eq_data) { + if (flash_data == NULL || eq_data == NULL) return -1; + + // 初始化所有滤波器为bypass + for (int i = 0; i < MAX_EQ_BANDS; i++) { + eq_data->bands[i].index = i; + eq_data->bands[i].type = FILTER_TYPE_BYPASS; + eq_data->bands[i].fc = 1000.0f; + eq_data->bands[i].q = 0.707f; + eq_data->bands[i].bw = 1.0f; + eq_data->bands[i].gain = 0.0f; + } + + // 恢复非默认滤波器 + for (int i = 0; i < flash_data->active_bands_count && i < MAX_EQ_BANDS; i++) { + int target_index = flash_data->active_bands[i].index; + if (target_index >= 0 && target_index < MAX_EQ_BANDS) { + eq_data->bands[target_index] = flash_data->active_bands[i]; + } + } + + DPRINTF(" Restored %d active filters to EQ data\n", flash_data->active_bands_count); + + return 0; +} + +// ==================== 单参数动态存储系统实现 ==================== + +/** + * @brief 初始化单参数存储数据库 + * @return 0: 成功, -1: 失败 + */ +int eq_single_param_database_init(void) { + DPRINTF("Initializing single parameter storage database\n"); + + memset(&single_param_database, 0, sizeof(single_param_database)); + single_param_database.max_params = MAX_EQ_BANDS * 8 * 2; // 最多8个模式×2通道的所有参数 + single_param_database.param_count = 0; + single_param_database.last_save_time = 0; + + DPRINTF(" Single parameter database initialized: max_params=%d\n", single_param_database.max_params); + return 0; +} + +/** + * @brief 处理0x8c/0x8d命令的单参数变化 + * @param data 接收到的数据 + * @param len 数据长度 + * @return 0: 成功, -1: 失败 + */ +int eq_process_single_param_change(uint32_t sample_rate, uint8_t mode, uint8_t channel, uint8_t band_index, filter_params_t *band) { + // 添加到单参数数据库 + if (eq_add_single_param(sample_rate, mode, channel, band_index, band) == 0) { + // 标记为脏数据 + dirty_flags[mode] = 1; + + return 0; + } else { + return -1; + } +} + +/** + * @brief 比较单个参数是否不同 + * @param old_params 旧参数 + * @param new_params 新参数 + * @return 1: 不同, 0: 相同 + */ +int eq_single_params_different(const filter_params_t *old_params, const filter_params_t *new_params) { + if (old_params == NULL || new_params == NULL) return 1; + + // 比较各个字段 + if (old_params->type != new_params->type) return 1; + if (fabsf(old_params->fc - new_params->fc) > 0.01f) return 1; + if (fabsf(old_params->q - new_params->q) > 0.0001f) return 1; + if (fabsf(old_params->bw - new_params->bw) > 0.01f) return 1; + if (fabsf(old_params->gain - new_params->gain) > 0.01f) return 1; + + return 0; +} + +/** + * @brief 添加单个参数变化到数据库 + * @param sample_rate 采样率 + * @param mode 模式 + * @param channel 通道 + * @param band_index 滤波器索引 + * @param new_params 新参数 + * @return 0: 成功, -1: 失败 + */ +int eq_add_single_param(uint32_t sample_rate, uint8_t mode, uint8_t channel, + uint8_t band_index, const filter_params_t *new_params) { + if (new_params == NULL) return -1; + + // 检查是否已存在相同的参数记录 + for (int i = 0; i < single_param_database.param_count; i++) { + eq_single_param_t *param = &single_param_database.params[i]; + if (param->sample_rate == sample_rate && + param->mode == mode && + param->channel == channel && + param->band_index == band_index) { + // 更新现有记录 + param->params = *new_params; + param->timestamp = get_reference_time(); + param->is_dirty = 1; + DPRINTF(" Updated existing single parameter: rate=%u, mode=%d, ch=%d, band=%d\n", + sample_rate, mode, channel, band_index); + return 0; + } + } + + // 检查数据库是否已满 + if (single_param_database.param_count >= single_param_database.max_params) { + DPRINTF(" Warning: Single parameter database full, overwriting oldest entry\n"); + // 移动所有条目向前一位,覆盖最旧的 + memmove(&single_param_database.params[0], &single_param_database.params[1], + (single_param_database.max_params - 1) * sizeof(eq_single_param_t)); + single_param_database.param_count--; + } + + // 添加新参数 + eq_single_param_t *param = &single_param_database.params[single_param_database.param_count]; + param->sample_rate = sample_rate; + param->mode = mode; + param->channel = channel; + param->band_index = band_index; + param->params = *new_params; + param->timestamp = get_reference_time(); + param->is_dirty = 1; + + single_param_database.param_count++; + + DPRINTF(" Added single parameter: rate=%u, mode=%d, ch=%d, band=%d, count=%d\n", + sample_rate, mode, channel, band_index, single_param_database.param_count); + + return 0; +} + +/** + * @brief 保存单个参数到Flash + * @param sample_rate 采样率 + * @param mode 模式 + * @param channel 通道 + * @param band_index 滤波器索引 + * @return 0: 成功, -1: 失败 + */ +int eq_save_single_param(uint32_t sample_rate, uint8_t mode, uint8_t channel, uint8_t band_index) { + if (mode < EQ_USER_MODE_MIN || mode > EQ_USER_MODE_MAX ) { + return -1; + } + DPRINTF("Saving single parameter to Flash: mode=%d, ch=%d, band=%d (applying to all sample rates)\n", + mode, channel, band_index); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 构建单个参数文件路径 - 简化格式:只存储mode和band信息 + // 改动原因:使用全局变量代替硬编码目录名,统一文件路径管理 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/mode%d_band%d.bin", + g_eq_flash_base_dir, mode, band_index); + + // 改动原因:优化EQ参数存储策略,统一参数源为44100Hz + // 保存参数时,总是从44100Hz读取参数,确保参数一致性 + eq_mode_data_t (*eq_array)[NUM_EQ_MODES][NUM_EQ_CHANS] = get_eq_array_ptr(44100); + if (!eq_array) { + DPRINTF(" Error: Failed to get EQ array for sample rate 44100 (unified source)\n"); + return -1; + } + + // 从44100Hz统一参数源读取参数 + filter_params_t *param = &(*eq_array)[mode][channel].bands[band_index]; + + // 创建文件头 + eq_file_header_t header; + header.magic = EQ_FLASH_MAGIC; + header.version = EQ_FLASH_VERSION; + header.mode = mode; + header.channel = 0; // 固定为channel 0,因为只存储channel 0的参数 + header.reserved = band_index; // 使用reserved字段存储band_index + header.data_size = sizeof(filter_params_t); + header.crc32 = eq_flash_calculate_crc32((uint8_t*)param, sizeof(filter_params_t)); + + // 创建文件数据 + uint8_t file_data[sizeof(header) + sizeof(filter_params_t)]; + memcpy(file_data, &header, sizeof(header)); + memcpy(file_data + sizeof(header), param, sizeof(filter_params_t)); + + // 写入文件 + lfs_write_config(file_path, file_data, sizeof(file_data)); + + DPRINTF(" Successfully saved single parameter to %s\n", file_path); + + // 改动原因:优化EQ参数存储策略,参数保存后需要同步到44100Hz并更新所有采样率 + // 确保44100Hz作为统一参数源,然后通过copy_params_from_44100函数同步到其他采样率 + // 注意:这里只保存参数,系数会在切换采样率时动态计算 + // 参数已经保存到Flash,加载时会自动应用到所有采样率 + + return 0; +} + +/** + * @brief 从Flash加载单个参数 + * @param sample_rate 采样率 + * @param mode 模式 + * @param channel 通道 + * @param band_index 滤波器索引 + * @return 0: 成功, -1: 失败 + */ +int eq_load_single_param(uint32_t sample_rate, uint8_t mode, uint8_t channel, uint8_t band_index) { + if (mode < EQ_USER_MODE_MIN || mode > EQ_USER_MODE_MAX ) { + return -1; + } + DPRINTF("Loading single parameter from Flash: mode=%d, ch=%d, band=%d (applying to all sample rates)\n", + mode, channel, band_index); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 构建单个参数文件路径 - 简化格式:只存储mode和band信息 + // 改动原因:使用全局变量代替硬编码目录名,统一文件路径管理 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/mode%d_band%d.bin", + g_eq_flash_base_dir, mode, band_index); + + // 检查文件是否存在 + if (!lfs_file_exists(file_path)) { + DPRINTF(" Single parameter file does not exist: %s\n", file_path); + return -1; + } + + // 准备读取缓冲区 + eq_file_header_t header; + filter_params_t param; + uint8_t file_data[sizeof(header) + sizeof(filter_params_t)]; + + // 读取文件 + lfs_read_config(file_path, file_data, sizeof(file_data)); + + // 解析文件数据 + memcpy(&header, file_data, sizeof(header)); + memcpy(¶m, file_data + sizeof(header), sizeof(filter_params_t)); + + // 验证文件头 + if (header.magic != EQ_FLASH_MAGIC || + header.version != EQ_FLASH_VERSION || + header.mode != mode || + header.channel != 0 || // 固定验证channel为0 + header.reserved != band_index) { + DPRINTF(" Error: Invalid single parameter file header\n"); + return -1; + } + + // 验证CRC32 + uint32_t calculated_crc = eq_flash_calculate_crc32((uint8_t*)¶m, sizeof(filter_params_t)); + if (calculated_crc != header.crc32) { + DPRINTF(" Error: CRC32 mismatch for single parameter\n"); + return -1; + } + + // 为所有采样率应用参数到两个通道并计算系数 + for (int i = 0; i < EQ_FLASH_MAX_SAMPLE_RATES; i++) { + uint32_t fs = supported_sample_rates[i]; + eq_mode_data_t (*eq_array)[NUM_EQ_MODES][NUM_EQ_CHANS] = get_eq_array_ptr(fs); + if (eq_array) { + // 应用到两个通道 + (*eq_array)[mode][0].bands[band_index] = param; + (*eq_array)[mode][1].bands[band_index] = param; + } + } + + return 0; +} + +/** + * @brief 保存所有脏参数到Flash + * @return 0: 成功, -1: 失败 + */ +int eq_save_dirty_params(void) { + //DPRINTF("Saving dirty single parameters to Flash\n"); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + //DPRINTF(" Single param database count: %d\n", single_param_database.param_count); + + int saved_count = 0; + + // 遍历所有脏参数,但只保存一次(不按采样率分组) + bool saved_params[EQ_FLASH_MAX_MODES][NUM_EQ_CHANS][MAX_EQ_BANDS] = {0}; // 记录已保存的参数 + + for (int i = 0; i < single_param_database.param_count; i++) { + eq_single_param_t *param = &single_param_database.params[i]; + + DPRINTF(" Checking param %d: mode=%d, ch=%d, band=%d, dirty=%d\n", + i, param->mode, param->channel, param->band_index, param->is_dirty); + + if (param->is_dirty && !saved_params[param->mode][param->channel][param->band_index]) { + DPRINTF(" Saving dirty single parameter: mode=%d, ch=%d, band=%d (applying to all sample rates)\n", + param->mode, param->channel, param->band_index); + if (param->mode < EQ_USER_MODE_MIN || param->mode > EQ_USER_MODE_MAX ) { + DPRINTF(" Invalid mode %d, only user modes (%d-%d) can be saved\n", param->mode, EQ_USER_MODE_MIN, EQ_USER_MODE_MAX); + continue; + } + if (eq_save_single_param(param->sample_rate, param->mode, param->channel, param->band_index) == 0) { + // 标记所有相同模式的参数为已保存 + for (int j = 0; j < single_param_database.param_count; j++) { + eq_single_param_t *p = &single_param_database.params[j]; + if (p->mode == param->mode && p->channel == param->channel && p->band_index == param->band_index) { + p->is_dirty = 0; + } + } + saved_params[param->mode][param->channel][param->band_index] = true; + saved_count++; + DPRINTF(" Successfully saved single parameter\n"); + } else { + DPRINTF(" Error: Failed to save single parameter\n"); + } + } + } + + if (saved_count > 0) { + DPRINTF(" Saved %d dirty single parameters\n", saved_count); + } + return 0; +} +/** + * @brief 保存脏的增益到Flash + * @return 0: 成功, -1: 失败 + */ +int eq_save_dirty_gain(void) { + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 检查是否有脏的增益 + if (!gain_dirty) { + DPRINTF(" No dirty gain to save\n"); + return 0; + } + + DPRINTF(" Saving dirty gain\n"); + // 使用新的独立保存函数 + if (eq_save_gain() == 0) { + gain_dirty = 0; // 只清除增益脏标志 + DPRINTF(" Successfully saved gain\n"); + return 0; + } else { + DPRINTF(" Error: Failed to save gain\n"); + return -1; + } +} +/** + * @brief 保存脏的模式名称到Flash + * @return 0: 成功, -1: 失败 + */ +int eq_save_dirty_names(void) { + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 检查是否有脏的模式名称 + if (!name_dirty) { + DPRINTF(" No dirty mode names to save\n"); + return 0; + } + + DPRINTF(" Saving dirty mode names\n"); + // 使用新的独立保存函数,逐个保存每个用户模式的name + int saved_count = 0; + int skipped_count = 0; + + for (int i = EQ_USER_MODE_MIN; i <= EQ_USER_MODE_MAX; i++) { + int result = eq_save_name(i); + if (result == 0) { + saved_count++; + } else if (result == 1) { + skipped_count++; // 跳过(与初始值相同或为空) + } + // result == -1 表示错误,但继续处理其他模式 + } + + name_dirty = 0; // 清除名称脏标志(无论是否保存成功) + + if (saved_count > 0) { + DPRINTF(" Successfully saved %d mode name(s), skipped %d\n", saved_count, skipped_count); + return 0; + } else if (skipped_count > 0) { + DPRINTF(" All mode names skipped (same as default or empty), %d skipped\n", skipped_count); + return 0; // 跳过是正常的,不算错误 + } else { + DPRINTF(" Error: Failed to save mode names\n"); + return -1; + } +} + +/** + * @brief 保存用户模式的增益到Flash(只保存用户模式6-8) + * @return 0: 成功, -1: 失败 + */ +int eq_save_gain(void) { + DPRINTF("Saving user mode gains to Flash\n"); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 构建文件路径 + // 改动原因:使用全局变量代替硬编码文件名,统一文件路径管理,将文件存储到基础目录下 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/%s", g_eq_flash_base_dir, g_eq_flash_gains_file); + + // 准备数据(只存储用户模式6-8的gain) + eq_gain_data_t data = {0}; + + // 从全局模式信息中获取数据 + extern eq_mode_info_t g_mode_info; + + // 只复制用户模式(6-8)的增益数据 + for (int i = EQ_USER_MODE_MIN; i <= EQ_USER_MODE_MAX; i++) { + int idx = i - EQ_USER_MODE_MIN; // 转换为数组索引 0, 1, 2 + data.mode_gains[idx] = g_mode_info.mode_gains[i]; + debug_printf(" Saving user mode %d: gain=%d\n", i, data.mode_gains[idx]); + } + + // 创建文件头 + eq_gain_header_t header; + header.magic = 0x4551474E; // "EQGN" + header.version = EQ_FLASH_VERSION; + header.data_size = sizeof(eq_gain_data_t); + header.crc32 = eq_flash_calculate_crc32((uint8_t*)&data, sizeof(eq_gain_data_t)); + + // 创建文件数据 + uint8_t file_data[sizeof(header) + sizeof(eq_gain_data_t)]; + memcpy(file_data, &header, sizeof(header)); + memcpy(file_data + sizeof(header), &data, sizeof(eq_gain_data_t)); + + // 写入文件 + lfs_write_config(file_path, file_data, sizeof(file_data)); + + DPRINTF(" Successfully saved gains to %s\n", file_path); + return 0; +} + +/** + * @brief 保存单个用户模式的名称到Flash(只有与初始值不同且非空才保存) + * @param mode 模式号(必须是用户模式6-8) + * @return 0: 成功, -1: 失败, 1: 跳过(与初始值相同或为空) + */ +int eq_save_name(uint8_t mode) { + if (mode < EQ_USER_MODE_MIN || mode > EQ_USER_MODE_MAX) { + DPRINTF(" Error: Invalid mode %d, only user modes (%d-%d) can be saved\n", + mode, EQ_USER_MODE_MIN, EQ_USER_MODE_MAX); + return -1; + } + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 从全局模式信息中获取当前名称 + extern eq_mode_info_t g_mode_info; + const char* current_name = g_mode_info.mode_names[mode]; + + // 获取初始默认名称 + const char* default_names[] = {"user 1", "user 2", "user 3"}; + int idx = mode - EQ_USER_MODE_MIN; // 转换为数组索引 0, 1, 2 + const char* default_name = default_names[idx]; + + // 检查是否与初始值相同 + if (strncmp(current_name, default_name, 15) == 0) { + DPRINTF(" Mode %d name '%s' is same as default, skip saving\n", mode, current_name); + return 1; // 跳过保存 + } + + // 检查是否为空(全0或全空格) + bool name_is_empty = true; + if (current_name[0] != '\0') { + for (int i = 0; i < 16 && current_name[i] != '\0'; i++) { + if (current_name[i] != ' ') { + name_is_empty = false; + break; + } + } + } + + if (name_is_empty) { + DPRINTF(" Mode %d name is empty, skip saving\n", mode); + // 如果文件存在,删除它 + // 改动原因:使用全局变量代替硬编码文件名,统一文件路径管理,将文件存储到基础目录下 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/%s%d", g_eq_flash_base_dir, g_eq_flash_name_prefix, mode); + if (lfs_file_exists(file_path)) { + // 删除文件(通过写入空文件或使用删除函数) + DPRINTF(" Deleting empty name file: %s\n", file_path); + // 注意:LittleFS可能没有直接的删除函数,可以通过写入空文件或标记删除 + } + return 1; // 跳过保存 + } + + DPRINTF("Saving user mode %d name to Flash: '%s'\n", mode, current_name); + + // 构建文件路径 + // 改动原因:使用全局变量代替硬编码文件名,统一文件路径管理,将文件存储到基础目录下 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/%s%d", g_eq_flash_base_dir, g_eq_flash_name_prefix, mode); + + // 准备数据 + eq_name_data_t data = {0}; + strncpy(data.mode_name, current_name, 15); + data.mode_name[15] = '\0'; // 确保字符串终止 + + // 创建文件头 + eq_name_header_t header; + header.magic = 0x45514E4D; // "EQNM" + header.version = EQ_FLASH_VERSION; + header.mode = mode; + header.reserved[0] = 0; + header.reserved[1] = 0; + header.reserved[2] = 0; + header.data_size = sizeof(eq_name_data_t); + header.crc32 = eq_flash_calculate_crc32((uint8_t*)&data, sizeof(eq_name_data_t)); + + // 创建文件数据 + uint8_t file_data[sizeof(header) + sizeof(eq_name_data_t)]; + memcpy(file_data, &header, sizeof(header)); + memcpy(file_data + sizeof(header), &data, sizeof(eq_name_data_t)); + + // 写入文件 + lfs_write_config(file_path, file_data, sizeof(file_data)); + + DPRINTF(" Successfully saved mode %d name to %s\n", mode, file_path); + return 0; +} + +/** + * @brief 保存增益和模式名称到Flash(兼容旧接口,内部调用新函数) + * @return 0: 成功, -1: 失败 + */ +int eq_save_gain_and_names(void) { + int ret = 0; + + // 保存gain + if (eq_save_gain() != 0) { + ret = -1; + } + + // 保存每个用户模式的name(如果与初始值不同且非空) + for (int i = EQ_USER_MODE_MIN; i <= EQ_USER_MODE_MAX; i++) { + int result = eq_save_name(i); + if (result == -1) { + ret = -1; // 错误 + } + // result == 1 表示跳过,这是正常的 + } + + return ret; +} + +/** + * @brief 标记增益为脏(需要保存) + */ +void eq_mark_gain_dirty(void) { + gain_dirty = 1; + DPRINTF("Marked gain as dirty for Flash sync\n"); +} + +/** + * @brief 标记模式名称为脏(需要保存) + */ +void eq_mark_name_dirty(void) { + name_dirty = 1; + DPRINTF("Marked mode names as dirty for Flash sync\n"); +} + +/** + * @brief 从Flash加载用户模式的增益 + * @return 0: 成功, -1: 失败 + */ +int eq_load_gain(void) { + DPRINTF("Loading user mode gains from Flash\n"); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 构建文件路径 + // 改动原因:使用全局变量代替硬编码文件名,统一文件路径管理,将文件存储到基础目录下 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/%s", g_eq_flash_base_dir, g_eq_flash_gains_file); + + // 检查文件是否存在 + if (!lfs_file_exists(file_path)) { + debug_printf(" Gains file does not exist: %s, using defaults\n", file_path); + return -1; // 文件不存在,使用默认值 + } + + // 准备读取缓冲区 + eq_gain_header_t header; + eq_gain_data_t data; + uint8_t file_data[sizeof(header) + sizeof(eq_gain_data_t)]; + + // 读取文件 + lfs_read_config(file_path, file_data, sizeof(file_data)); + + // 解析文件数据 + memcpy(&header, file_data, sizeof(header)); + memcpy(&data, file_data + sizeof(header), sizeof(eq_gain_data_t)); + + // 验证文件头 + if (header.magic != 0x4551474E) { // "EQGN" + DPRINTF(" Error: Invalid gains file magic\n"); + return -1; + } + + if (header.version != EQ_FLASH_VERSION) { + DPRINTF(" Error: Unsupported gains file version\n"); + return -1; + } + + // 验证CRC32 + uint32_t calculated_crc = eq_flash_calculate_crc32((uint8_t*)&data, sizeof(eq_gain_data_t)); + if (calculated_crc != header.crc32) { + DPRINTF(" Error: CRC32 mismatch for gains data\n"); + return -1; + } + + // 更新全局模式信息(只加载用户模式) + extern eq_mode_info_t g_mode_info; + + // 只加载用户模式(6-8)的增益 + for (int i = EQ_USER_MODE_MIN; i <= EQ_USER_MODE_MAX; i++) { + int idx = i - EQ_USER_MODE_MIN; // 转换为数组索引 0, 1, 2 + g_mode_info.mode_gains[i] = data.mode_gains[idx]; + debug_printf(" Loaded user mode %d: gain=%d\n", i, g_mode_info.mode_gains[i]); + } + + DPRINTF(" Successfully loaded gains\n"); + return 0; +} + +/** + * @brief 从Flash加载单个用户模式的名称 + * @param mode 模式号(必须是用户模式6-8) + * @return 0: 成功, -1: 失败(文件不存在或错误) + */ +int eq_load_name(uint8_t mode) { + if (mode < EQ_USER_MODE_MIN || mode > EQ_USER_MODE_MAX) { + DPRINTF(" Error: Invalid mode %d, only user modes (%d-%d) can be loaded\n", + mode, EQ_USER_MODE_MIN, EQ_USER_MODE_MAX); + return -1; + } + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 构建文件路径 + // 改动原因:使用全局变量代替硬编码文件名,统一文件路径管理,将文件存储到基础目录下 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/%s%d", g_eq_flash_base_dir, g_eq_flash_name_prefix, mode); + + // 检查文件是否存在 + if (!lfs_file_exists(file_path)) { + DPRINTF(" Mode %d name file does not exist: %s, using default\n", mode, file_path); + return -1; // 文件不存在,使用默认值 + } + + // 准备读取缓冲区 + eq_name_header_t header; + eq_name_data_t data; + uint8_t file_data[sizeof(header) + sizeof(eq_name_data_t)]; + + // 读取文件 + lfs_read_config(file_path, file_data, sizeof(file_data)); + + // 解析文件数据 + memcpy(&header, file_data, sizeof(header)); + memcpy(&data, file_data + sizeof(header), sizeof(eq_name_data_t)); + + // 验证文件头 + if (header.magic != 0x45514E4D) { // "EQNM" + DPRINTF(" Error: Invalid name file magic for mode %d\n", mode); + return -1; + } + + if (header.mode != mode) { + DPRINTF(" Error: Mode mismatch in name file (expected %d, got %d)\n", mode, header.mode); + return -1; + } + + if (header.version != EQ_FLASH_VERSION) { + DPRINTF(" Error: Unsupported name file version for mode %d\n", mode); + return -1; + } + + // 验证CRC32 + uint32_t calculated_crc = eq_flash_calculate_crc32((uint8_t*)&data, sizeof(eq_name_data_t)); + if (calculated_crc != header.crc32) { + DPRINTF(" Error: CRC32 mismatch for mode %d name data\n", mode); + return -1; + } + + // 更新全局模式信息 + extern eq_mode_info_t g_mode_info; + strncpy(g_mode_info.mode_names[mode], data.mode_name, 15); + g_mode_info.mode_names[mode][15] = '\0'; // 确保字符串终止 + + DPRINTF(" Loaded user mode %d name: '%s'\n", mode, g_mode_info.mode_names[mode]); + return 0; +} + +/** + * @brief 从Flash加载增益和模式名称(兼容旧接口,内部调用新函数) + * @return 0: 成功, -1: 失败 + */ +int eq_load_gain_and_names(void) { + int ret = 0; + + // 加载gain + if (eq_load_gain() != 0) { + ret = -1; // 文件不存在是正常的,使用默认值 + } + + // 加载每个用户模式的name + for (int i = EQ_USER_MODE_MIN; i <= EQ_USER_MODE_MAX; i++) { + if (eq_load_name(i) != 0) { + // 文件不存在是正常的,使用默认值 + } + } + + DPRINTF("Successfully loaded gain and mode names\n"); + return ret; +} + +/** + * @brief 系统开机时加载所有参数并计算系数 + * @param sample_rate 采样率 + * @param mode 模式 + * @return 0: 成功, -1: 失败 + */ +int eq_load_all_params_and_calculate_coefficients(uint32_t sample_rate, uint8_t mode) { + //DPRINTF("Loading all parameters and calculating coefficients for rate=%u, mode=%d\n", + // sample_rate, mode); + + if (mode < EQ_USER_MODE_MIN || mode > EQ_USER_MODE_MAX ) { + return -1; + } + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 获取EQ数组指针 + eq_mode_data_t (*eq_array)[NUM_EQ_MODES][NUM_EQ_CHANS] = get_eq_array_ptr(sample_rate); + if (!eq_array) { + DPRINTF(" Error: Failed to get EQ array for sample rate %u\n", sample_rate); + return -1; + } + + int loaded_count = 0; + + // 简化加载逻辑:只从44100Hz channel 0加载参数,然后应用到所有采样率和通道 + for (int band = 0; band < MAX_EQ_BANDS; band++) { + if (eq_load_single_param(44100, mode, 0, band) == 0) { + loaded_count++; + DPRINTF(" Loaded parameter for band %d from 44100Hz channel 0\n", band); + } + } + + //DPRINTF(" Loaded %d single parameters and calculated coefficients for mode %d\n", + // loaded_count, mode); + + return 0; +} + +/** + * @brief 获取单参数存储统计 + * @param stats 输出统计信息 + * @return 0: 成功, -1: 失败 + */ +int eq_get_single_param_stats(eq_single_param_database_t *stats) { + if (stats == NULL) return -1; + + *stats = single_param_database; + return 0; +} + +/** + * @brief 清空单参数存储数据库 + * @return 0: 成功, -1: 失败 + */ +int eq_clear_single_param_database(void) { + DPRINTF("Clearing single parameter database\n"); + + memset(&single_param_database, 0, sizeof(single_param_database)); + single_param_database.max_params = MAX_EQ_BANDS * 8 * 2; + + DPRINTF(" Single parameter database cleared\n"); + return 0; +} + +/** + * @brief 保存指定模式的EQ参数到Flash + * @param mode EQ模式 + * @return 0: 成功, -1: 失败 + */ +int eq_flash_save_mode(uint8_t mode) { + DPRINTF("Saving EQ mode %d to Flash\n", mode); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: EQ Flash storage not initialized\n"); + return -1; + } + + if (mode >= NUM_EQ_MODES) { + DPRINTF(" Error: Invalid mode %d (max: %d)\n", mode, NUM_EQ_MODES - 1); + return -1; + } + + // 获取当前采样率的EQ数组指针 + eq_mode_data_t (*eq_array)[NUM_EQ_MODES][NUM_EQ_CHANS] = get_eq_array_ptr(g_eq_sample_rate); + if (!eq_array) { + DPRINTF(" Error: Failed to get EQ array for current sample rate %lu\n", (unsigned long)g_eq_sample_rate); + return -1; + } + + // 保存左右两个声道的数据 + for (int ch = 0; ch < NUM_EQ_CHANS; ch++) { + // 构建文件路径 + // 改动原因:使用全局变量代替硬编码目录名,统一文件路径管理 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/mode%d_ch%d.bin", g_eq_flash_base_dir, mode, ch); + DPRINTF(" File path (channel %d): %s\n", ch, file_path); + + // 从EQ数组读取数据 + eq_mode_data_t *eq_data = &(*eq_array)[mode][ch]; + DPRINTF(" Reading EQ data from array (ch %d): sample_rate=%u, post_gain_db=%ld\n", + ch, eq_data->sample_rate, (long)eq_data->post_gain_db); + + // 提取非默认滤波器到优化存储格式 + eq_flash_data_t flash_data; + if (extract_active_filters(eq_data, &flash_data) != 0) { + DPRINTF(" Error: Failed to extract active filters\n"); + continue; + } + + // 准备文件头 + eq_file_header_t header; + header.magic = EQ_FLASH_MAGIC; + header.version = EQ_FLASH_VERSION; + header.mode = mode; + header.channel = ch; // 使用正确的通道号 + header.reserved = 0; + header.data_size = sizeof(eq_flash_data_t); + header.crc32 = eq_flash_calculate_crc32((uint8_t*)&flash_data, sizeof(eq_flash_data_t)); + + // 创建完整的数据包(文件头 + 优化EQ数据) + uint8_t file_data[sizeof(header) + sizeof(eq_flash_data_t)]; + uint8_t file_data2[sizeof(header) + sizeof(eq_flash_data_t)]; + memcpy(file_data, &header, sizeof(header)); + memcpy(file_data + sizeof(header), &flash_data, sizeof(eq_flash_data_t)); + + DPRINTF(" +++++++++++++++ data before save (ch %d) ++++++++++++++ \n", ch); + // debug_print_eq_mode(eq_data); + DPRINTF(" +++++++++++++++ data before save (ch %d) ++++++++++++++ \n", ch); + + // 使用lfs_write_config写入文件 + lfs_write_config(file_path, file_data, sizeof(file_data)); +#if 0 + lfs_read_config(file_path, file_data2, sizeof(file_data2)); + + if( memcmp(file_data, file_data2, sizeof(file_data)) != 0) + { + DPRINTF(" *********** ** \n %s, file_data is not equal to file_data2\n", file_path); + return -1; + } + #endif + DPRINTF(" Successfully saved EQ parameters for mode %d, channel %d\n", mode, ch); + } + + return 0; +} + +/** + * @brief 标记指定模式为脏(需要同步) + * @param mode EQ模式 + */ +void eq_flash_mark_dirty(uint8_t mode) { + if ((mode >= EQ_USER_MODE_MIN) && (mode <= EQ_USER_MODE_MAX )) { + dirty_flags[mode] = 1; + DPRINTF("Marked mode %d as dirty\n", mode); + } +} + +/** + * @brief 同步指定模式参数到Flash(延迟写入) + * @param mode 模式号 + * @return 0: 成功, -1: 失败 + */ +int eq_flash_sync_mode(uint8_t mode) { + DPRINTF("Syncing EQ mode %d to Flash\n", mode); + + if (!storage_status.is_initialized) { + DPRINTF("EQ Flash storage not initialized\n"); + return -1; + } + + if (mode >= NUM_EQ_MODES) { + DPRINTF("Invalid mode %d\n", mode); + return -1; + } + + // 保存指定模式到Flash + if (eq_flash_save_mode(mode) != 0) { + DPRINTF("Failed to save mode %d to Flash\n", mode); + return -1; + } + + // 清除脏标志 + dirty_flags[mode] = 0; + DPRINTF("EQ mode %d synced to Flash\n", mode); + return 0; +} + +/** + * @brief 同步所有脏数据到Flash + * @return 0: 成功, -1: 失败 + */ +int eq_flash_sync_all(void) { + DPRINTF("Syncing dirty EQ parameters to Flash\n"); + + if (!storage_status.is_initialized) { + DPRINTF("EQ Flash storage not initialized\n"); + return -1; + } + + // 简化实现 + memset(dirty_flags, 0, sizeof(dirty_flags)); + DPRINTF("EQ parameters synced to Flash (simplified)\n"); + return 0; +} + +/** + * @brief 检查Flash中是否有EQ数据 + * @return 1: 有数据, 0: 无数据 + */ +int eq_flash_has_data(void) { + return storage_status.has_flash_data; +} + +/** + * @brief 获取存储状态 + * @param status 输出存储状态结构 + * @return 0: 成功, -1: 失败 + */ +int eq_flash_get_status(eq_storage_status_t *status) { + if (!status) { + return -1; + } + *status = storage_status; + return 0; +} + +/** + * @brief 清除所有Flash数据 + * @return 0: 成功, -1: 失败 + */ +int eq_flash_clear_all(void) { + DPRINTF("Clearing all EQ Flash data\n"); + + if (!storage_status.is_initialized) { + DPRINTF("EQ Flash storage not initialized\n"); + return -1; + } + + // 简化实现 + storage_status.has_flash_data = 0; + memset(dirty_flags, 0, sizeof(dirty_flags)); + DPRINTF("All EQ Flash data cleared\n"); + return 0; +} + +/** + * @brief 验证指定模式的完整性 + * @param mode EQ模式 + * @return 1: 有效, 0: 无效 + */ +int eq_flash_verify_mode(uint8_t mode) { + DPRINTF("Verifying EQ mode %d\n", mode); + + if (!storage_status.is_initialized) { + return 0; + } + + // 简化实现 + return 1; +} + +/** + * @brief 获取文件路径 + * @param mode EQ模式 + * @param channel 通道 + * @param file_path 输出文件路径 + * @param max_len 最大长度 + * @return 0: 成功, -1: 失败 + */ +int eq_flash_get_file_path(uint8_t mode, uint8_t channel, char *file_path, size_t max_len) { + if (!file_path || max_len == 0) { + return -1; + } + // 改动原因:使用全局变量代替硬编码目录名,统一文件路径管理 + snprintf(file_path, max_len, "%s/mode%d_ch%d.bin", g_eq_flash_base_dir, mode, channel); + return 0; +} + +/** + * @brief 计算CRC32校验和 + * @param data 数据指针 + * @param length 数据长度 + * @return CRC32值 + */ +uint32_t eq_flash_calculate_crc32(const uint8_t *data, size_t length) { + // 简化的CRC32计算(实际应该使用标准CRC32算法) + uint32_t crc = 0xFFFFFFFF; + for (size_t i = 0; i < length; i++) { + crc ^= data[i]; + for (int j = 0; j < 8; j++) { + if (crc & 1) { + crc = (crc >> 1) ^ 0xEDB88320; + } else { + crc >>= 1; + } + } + } + return ~crc; +} + +/** + * @brief 检查是否有需要同步的数据 + * @return 1: 需要同步, 0: 不需要同步 + */ +int eq_flash_needs_sync(void) { + for (int j = 0; j < NUM_EQ_MODES; j++) { + if (dirty_flags[j]) { + return 1; + } + } + return 0; +} + +/** + * @brief 删除Flash中的EQ参数文件 + * @param mode 模式号 (0-9, 0xFF表示所有模式) + * @return 0: 成功, -1: 失败 + */ +int delete_flash_eq_params(uint8_t mode) { + DPRINTF("Deleting Flash EQ params for mode %d\n", mode); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + int deleted_count = 0; + + if (mode == 0xFF) { + // 删除所有模式的参数文件 + DPRINTF(" Deleting all mode parameters\n"); + for (uint8_t m = 0; m < NUM_EQ_MODES; m++) { + for (uint8_t band = 0; band < MAX_EQ_BANDS; band++) { + // 改动原因:使用全局变量代替硬编码目录名,统一文件路径管理 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/mode%d_band%d.bin", g_eq_flash_base_dir, m, band); + + if (lfs_file_exists(file_path)) { + if (lfs_remove_file(file_path) == 0) { + deleted_count++; + DPRINTF(" Deleted: %s\n", file_path); + } else { + DPRINTF(" Failed to delete: %s\n", file_path); + } + } + } + } + } else { + // 删除指定模式的参数文件 + DPRINTF(" Deleting mode %d parameters\n", mode); + for (uint8_t band = 0; band < MAX_EQ_BANDS; band++) { + // 改动原因:使用全局变量代替硬编码目录名,统一文件路径管理 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/mode%d_band%d.bin", g_eq_flash_base_dir, mode, band); + + if (lfs_file_exists(file_path)) { + if (lfs_remove_file(file_path) == 0) { + deleted_count++; + DPRINTF(" Deleted: %s\n", file_path); + } else { + DPRINTF(" Failed to delete: %s\n", file_path); + } + } + } + } + + DPRINTF(" Deleted %d parameter files\n", deleted_count); + return 0; +} + +/** + * @brief 删除Flash中的增益和模式名称文件 + * @param mode 模式号 (0-9, 0xFF表示所有模式) + * @return 0: 成功, -1: 失败 + * + * 改动原因:修改为按模式删除,只删除指定模式的增益和名称,而不是删除所有文件 + * - 如果mode == 0xFF,删除所有用户模式的增益和名称文件 + * - 如果mode是用户模式(6-8),只删除该模式的名称文件,并将该模式的增益恢复为默认值(0) + * - 如果mode是预设模式(0-5),不需要删除(预设模式的增益和名称不存储在Flash中) + */ +int delete_flash_gain_and_names(uint8_t mode) { + DPRINTF("Deleting Flash gain and names for mode %d\n", mode); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 处理删除所有模式的情况 + if (mode == 0xFF) { + DPRINTF(" Deleting all user mode gains and names\n"); + + // 删除gains文件 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/%s", g_eq_flash_base_dir, g_eq_flash_gains_file); + if (lfs_file_exists(file_path)) { + if (lfs_remove_file(file_path) == 0) { + DPRINTF(" Successfully deleted gains file: %s\n", file_path); + } else { + DPRINTF(" Failed to delete gains file: %s\n", file_path); + } + } + + // 删除所有用户模式的名称文件 + for (int i = EQ_USER_MODE_MIN; i <= EQ_USER_MODE_MAX; i++) { + snprintf(file_path, sizeof(file_path), "%s/%s%d", g_eq_flash_base_dir, g_eq_flash_name_prefix, i); + if (lfs_file_exists(file_path)) { + if (lfs_remove_file(file_path) == 0) { + DPRINTF(" Successfully deleted name file for mode %d: %s\n", i, file_path); + } else { + DPRINTF(" Failed to delete name file for mode %d: %s\n", i, file_path); + } + } + } + + return 0; + } + + // 检查模式有效性 + if (mode >= NUM_EQ_MODES) { + DPRINTF(" Error: Invalid mode %d\n", mode); + return -1; + } + + // 预设模式(0-5)的增益和名称不存储在Flash中,不需要删除 + if (mode < EQ_USER_MODE_MIN) { + DPRINTF(" Mode %d is a preset mode, no Flash data to delete\n", mode); + return 0; + } + + // 只处理用户模式(6-8) + if (mode >= EQ_USER_MODE_MIN && mode <= EQ_USER_MODE_MAX) { + // 删除该模式的名称文件 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s/%s%d", g_eq_flash_base_dir, g_eq_flash_name_prefix, mode); + if (lfs_file_exists(file_path)) { + if (lfs_remove_file(file_path) == 0) { + DPRINTF(" Successfully deleted name file for mode %d: %s\n", mode, file_path); + } else { + DPRINTF(" Failed to delete name file for mode %d: %s\n", mode, file_path); + } + } else { + DPRINTF(" Name file for mode %d does not exist: %s\n", mode, file_path); + } + + // 从gains文件中移除该模式的增益(恢复为默认值0) + // 改动原因:gains文件存储了所有用户模式的增益,需要读取、修改、写回 + snprintf(file_path, sizeof(file_path), "%s/%s", g_eq_flash_base_dir, g_eq_flash_gains_file); + if (lfs_file_exists(file_path)) { + // 读取gains文件 + // 改动原因:lfs_read_config返回void,不能检查返回值,需要通过验证文件头来判断读取是否成功 + eq_gain_header_t header; + eq_gain_data_t data; + uint8_t file_data[sizeof(header) + sizeof(eq_gain_data_t)]; + + // 直接调用lfs_read_config(返回void,不能检查返回值) + lfs_read_config(file_path, file_data, sizeof(file_data)); + + // 解析文件数据 + memcpy(&header, file_data, sizeof(header)); + memcpy(&data, file_data + sizeof(header), sizeof(eq_gain_data_t)); + + // 验证文件头来判断读取是否成功 + if (header.magic == 0x4551474E && header.version == EQ_FLASH_VERSION) { + // 将对应模式的增益恢复为默认值(0) + int idx = mode - EQ_USER_MODE_MIN; // 转换为数组索引 0, 1, 2 + data.mode_gains[idx] = 0; + DPRINTF(" Reset gain for mode %d to default (0)\n", mode); + + // 重新计算CRC32 + header.crc32 = eq_flash_calculate_crc32((uint8_t*)&data, sizeof(eq_gain_data_t)); + + // 写回文件 + memcpy(file_data, &header, sizeof(header)); + memcpy(file_data + sizeof(header), &data, sizeof(eq_gain_data_t)); + lfs_write_config(file_path, file_data, sizeof(file_data)); + + DPRINTF(" Successfully updated gains file for mode %d\n", mode); + } else { + DPRINTF(" Error: Invalid gains file format or read failed\n"); + } + } else { + DPRINTF(" Gains file does not exist: %s\n", file_path); + } + + return 0; + } + + // 其他模式(如模式9)不需要处理 + DPRINTF(" Mode %d does not require Flash data deletion\n", mode); + return 0; +} + +/** + * @brief 恢复指定模式在指定采样率下的默认参数 + * @param mode 模式号 + * @param sample_rate 采样率 + */ +void restore_mode_default_params(uint8_t mode, uint32_t sample_rate) { + if (mode >= NUM_EQ_MODES) return; + + // 改动原因:所有采样率共用44100Hz数组 + eq_mode_data_t (*eq_array)[NUM_EQ_MODES][NUM_EQ_CHANS] = &sEQ_data_44100HZ; + + if (eq_array == NULL) return; + + // 恢复左右通道的默认参数 + for (int ch = 0; ch < NUM_EQ_CHANS; ch++) { + eq_mode_data_t *eq_data = &(*eq_array)[mode][ch]; + + // 恢复基本参数 + eq_data->sample_rate = sample_rate; + eq_data->total_bshift = 0; + eq_data->post_gain_db = 0; + + // 恢复所有滤波器的默认参数 + for (int i = 0; i < MAX_EQ_BANDS; i++) { + filter_params_t *band = &eq_data->bands[i]; + band->index = i; + band->type = FILTER_TYPE_BYPASS; + band->fc = 1000.0f; + band->q = 0.707f; + band->bw = 1.0f; + band->gain = 0.0f; + + // 恢复默认系数 (bypass滤波器) + eq_data->filter.coef[0][i] = 1 << 30; // b0 = 1.0 in Q30 + eq_data->filter.coef[1][i] = 0; // b1 = 0 + eq_data->filter.coef[2][i] = 0; // b2 = 0 + eq_data->filter.coef[3][i] = 0; // a1 = 0 + eq_data->filter.coef[4][i] = 0; // a2 = 0 + } + + eq_data->filter.biquad_count = MAX_EQ_BANDS; + memset(eq_data->filter.state, 0, sizeof(eq_data->filter.state)); + } + + DPRINTF(" Restored mode %d for sample rate %u Hz\n", mode, sample_rate); +} +/** + * @brief 恢复默认EQ参数 + * @param mode 模式号 (0-9, 0xFF表示所有模式) + * @return 0: 成功, -1: 失败 + */ +int restore_default_eq_params(uint8_t mode) { + DPRINTF("Restoring default EQ params for mode %d\n", mode); + + // 改动原因:所有采样率共用44100Hz数组,只需要恢复44100Hz数组 + if (mode == 0xFF) { + // 恢复所有模式的默认参数 + DPRINTF(" Restoring all modes to default parameters\n"); + for (uint8_t m = 0; m < NUM_EQ_MODES; m++) { + restore_mode_default_params(m, 44100); // 恢复44100Hz数组 + } + } else { + // 恢复指定模式的默认参数 + DPRINTF(" Restoring mode %d to default parameters\n", mode); + restore_mode_default_params(mode, 44100); // 恢复44100Hz数组 + } + + DPRINTF(" Default parameters restored successfully\n"); + return 0; +} + +/** + * @brief 保存EQ使能状态到Flash(独立文件存储,与模式存储完全分离) + * @param enable EQ使能状态 (0=禁用, 1=启用) + * @return 0: 成功, -1: 失败 + * + * 改动原因:将eq_enable存储独立为单独文件,与模式存储完全分离,互不影响 + */ +int eq_flash_save_eq_enable(uint8_t enable) +{ + DPRINTF("Saving EQ enable state %d to Flash (independent file)\n", enable); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 验证使能值范围 + if (enable > 1) { + DPRINTF(" Error: Invalid EQ enable value %d (valid range: 0-1)\n", enable); + return -1; + } + + // 构建独立文件路径 + // 改动原因:使用独立的文件名存储eq_enable,与模式存储完全分离 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s", g_eq_flash_enable_file); + + // 创建文件头 + eq_file_header_t header = {0}; + header.magic = EQ_FLASH_MAGIC; + header.version = EQ_FLASH_VERSION; + header.mode = 0; // 不使用模式字段 + header.channel = 0; // 不使用通道字段 + header.data_size = 1; // 数据大小:只有使能状态(1字节) + + // 准备数据(只有使能状态) + uint8_t enable_data = enable; + + header.crc32 = eq_flash_calculate_crc32(&enable_data, 1); + + // 准备文件数据 + uint8_t file_data[sizeof(header) + 1]; + memcpy(file_data, &header, sizeof(header)); + file_data[sizeof(header)] = enable; + + // 写入文件 + lfs_write_config(file_path, file_data, sizeof(file_data)); + DPRINTF(" Successfully saved EQ enable %d to Flash (independent file: %s)\n", enable, file_path); + return 0; +} + +/** + * @brief 保存当前EQ模式到Flash(独立文件存储,与使能状态存储完全分离) + * @param mode 模式值 (0-9) + * @return 0: 成功, -1: 失败 + * + * 改动原因:将模式存储独立为单独文件,与eq_enable存储完全分离,互不影响 + */ +int eq_flash_save_current_mode(uint8_t mode) +{ + DPRINTF("Saving current EQ mode %d to Flash (independent file)\n", mode); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 验证模式值范围 + if (mode > 9) { + DPRINTF(" Error: Invalid mode %d (valid range: 0-9)\n", mode); + return -1; + } + + // 构建独立文件路径 + // 改动原因:使用独立的文件名存储模式,与eq_enable存储完全分离 + char file_path[FILE_PATH_MAX_LENGTH]; + snprintf(file_path, sizeof(file_path), "%s", g_eq_flash_mode_file); + + // 创建文件头 + eq_file_header_t header = {0}; + header.magic = EQ_FLASH_MAGIC; + header.version = EQ_FLASH_VERSION; + header.mode = mode; // 模式值 + header.channel = 0; // 不使用通道字段 + header.data_size = 1; // 数据大小:只有模式值(1字节) + + // 准备数据(只有模式值) + uint8_t mode_data = mode; + + header.crc32 = eq_flash_calculate_crc32(&mode_data, 1); + + // 准备文件数据 + uint8_t file_data[sizeof(header) + 1]; + memcpy(file_data, &header, sizeof(header)); + file_data[sizeof(header)] = mode; + + // 写入文件 + lfs_write_config(file_path, file_data, sizeof(file_data)); + DPRINTF(" Successfully saved EQ mode %d to Flash (independent file: %s)\n", mode, file_path); + return 0; +} + +/** + * @brief 从Flash加载当前EQ模式和使能状态(从两个独立文件分别加载) + * @return 模式值: 成功, -1: 失败 + * + * 改动原因:从两个独立文件分别加载模式和使能状态,完全独立,互不影响 + */ +int eq_flash_load_current_mode(void) +{ + uint8_t mode = 0; + uint8_t eq_enable = 0; + DPRINTF("Loading current EQ mode and enable state from Flash (independent files)\n"); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 加载模式(从独立文件) + char mode_file_path[FILE_PATH_MAX_LENGTH]; + snprintf(mode_file_path, sizeof(mode_file_path), "%s", g_eq_flash_mode_file); + + if (lfs_file_exists(mode_file_path)) { + eq_file_header_t header; + uint8_t file_header_data[sizeof(header) + 1]; + lfs_read_config(mode_file_path, file_header_data, sizeof(file_header_data)); + memcpy(&header, file_header_data, sizeof(header)); + + if (header.magic == EQ_FLASH_MAGIC && header.version == EQ_FLASH_VERSION) { + mode = file_header_data[sizeof(header)]; + + // 验证模式值范围 + if (mode > 9) { + DPRINTF(" Error: Invalid mode value %d in file\n", mode); + mode = 0; // 使用默认值 + } else { + // 验证CRC32 + uint8_t mode_data = mode; + uint32_t calculated_crc = eq_flash_calculate_crc32(&mode_data, 1); + if (calculated_crc == header.crc32) { + DPRINTF(" Successfully loaded mode %d from Flash\n", mode); + } else { + DPRINTF(" Error: CRC32 mismatch for mode file, using default mode 0\n"); + mode = 0; + } + } + } else { + DPRINTF(" Error: Invalid mode file header, using default mode 0\n"); + mode = 0; + } + } else { + DPRINTF(" Mode file does not exist, using default mode 0\n"); + mode = 0; + } + + // 加载使能状态(从独立文件) + char enable_file_path[FILE_PATH_MAX_LENGTH]; + snprintf(enable_file_path, sizeof(enable_file_path), "%s", g_eq_flash_enable_file); + + if (lfs_file_exists(enable_file_path)) { + eq_file_header_t header; + uint8_t file_header_data[sizeof(header) + 1]; + lfs_read_config(enable_file_path, file_header_data, sizeof(file_header_data)); + memcpy(&header, file_header_data, sizeof(header)); + + if (header.magic == EQ_FLASH_MAGIC && header.version == EQ_FLASH_VERSION) { + eq_enable = file_header_data[sizeof(header)]; + + // 验证使能值范围 + if (eq_enable > 1) { + DPRINTF(" Warning: Invalid EQ enable value %d in file, defaulting to 0\n", eq_enable); + eq_enable = 0; + } else { + // 验证CRC32 + uint8_t enable_data = eq_enable; + uint32_t calculated_crc = eq_flash_calculate_crc32(&enable_data, 1); + if (calculated_crc == header.crc32) { + DPRINTF(" Successfully loaded EQ enable %d from Flash\n", eq_enable); + } else { + DPRINTF(" Error: CRC32 mismatch for enable file, using default enable 0\n"); + eq_enable = 0; + } + } + } else { + DPRINTF(" Error: Invalid enable file header, using default enable 0\n"); + eq_enable = 0; + } + } else { + DPRINTF(" Enable file does not exist, using default enable 0\n"); + eq_enable = 0; + } + + // 设置全局变量g_eq_enable + extern unsigned int g_eq_enable; + g_eq_enable = eq_enable; + + DPRINTF(" Successfully loaded mode %d and EQ enable %d from Flash (independent files)\n", mode, eq_enable); + return mode; +} + +/** + * @brief 从Flash加载EQ使能状态(独立文件加载,与模式加载完全分离) + * @return 使能状态值: 成功(0或1), -1: 失败 + * + * 改动原因:从独立文件(eq_enable)加载eq_enable,与模式加载完全分离,互不影响 + */ +int eq_flash_load_eq_enable(void) +{ + uint8_t eq_enable = 0; + DPRINTF("Loading EQ enable state from Flash (independent file)\n"); + + if (!storage_status.is_initialized) { + DPRINTF(" Error: Flash storage not initialized\n"); + return -1; + } + + // 加载使能状态(从独立文件) + char enable_file_path[FILE_PATH_MAX_LENGTH]; + snprintf(enable_file_path, sizeof(enable_file_path), "%s", g_eq_flash_enable_file); + + if (lfs_file_exists(enable_file_path)) { + eq_file_header_t header; + uint8_t file_header_data[sizeof(header) + 1]; + lfs_read_config(enable_file_path, file_header_data, sizeof(file_header_data)); + memcpy(&header, file_header_data, sizeof(header)); + + if (header.magic == EQ_FLASH_MAGIC && header.version == EQ_FLASH_VERSION) { + eq_enable = file_header_data[sizeof(header)]; + + // 验证使能值范围 + if (eq_enable > 1) { + DPRINTF(" Warning: Invalid EQ enable value %d in file, defaulting to 0\n", eq_enable); + eq_enable = 0; + } else { + // 验证CRC32 + uint8_t enable_data = eq_enable; + uint32_t calculated_crc = eq_flash_calculate_crc32(&enable_data, 1); + if (calculated_crc == header.crc32) { + DPRINTF(" Successfully loaded EQ enable %d from Flash\n", eq_enable); + } else { + DPRINTF(" Error: CRC32 mismatch for enable file, using default enable 0\n"); + eq_enable = 0; + } + } + } else { + DPRINTF(" Error: Invalid enable file header, using default enable 0\n"); + eq_enable = 0; + } + } else { + DPRINTF(" Enable file does not exist, using default enable 0\n"); + eq_enable = 0; + } + + // 设置全局变量g_eq_enable + extern unsigned int g_eq_enable; + g_eq_enable = eq_enable; + + DPRINTF(" Successfully loaded EQ enable %d from Flash (independent file)\n", eq_enable); + return eq_enable; +} diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_flash_storage.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_flash_storage.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_flash_storage.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_flash_storage.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_hid_protocol.md b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_hid_protocol.md similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_hid_protocol.md rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_hid_protocol.md diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_params_44100.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_params_44100.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/eq_params_44100.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_params_44100.h diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_params_44100_tile1.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_params_44100_tile1.h new file mode 100644 index 0000000..2ac2097 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/eq_params_44100_tile1.h @@ -0,0 +1,1754 @@ +// EQ参数头文件 - 44100Hz +// 自动生成,请勿手动修改 + +#include "eq.h" + +// Directly declare sEQ_data_44100HZ, including coefficients +eq_mode_data_t sEQ_data_44100HZ_tile1[NUM_EQ_MODES][NUM_EQ_CHANS] = { + [0][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = -6, // -6 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 32.00f, + .gain = 0.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 64.00f, + .gain = 4.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 2.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 3.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40ea71da, 0x41c96a66, 0x436723df, 0x40000000, 0x449012f8, 0x4a4d3a1a, 0x4d0a569a}, + {-0x7e6501c8, -0x7cca0ea9, -0x7994c1a0, -0x732f5c01, -0x668a08e1, -0x4e189e14, -0x209c225f, 0x364b4fdd}, + {0x3e6a239c, 0x3bf3e36e, 0x381a7c14, 0x30f60f23, 0x2ad90a30, 0x18317347, 0x03c161bb, 0x065e7484}, + {0x7e6501c8, 0x7cca0ea9, 0x7994c1a0, 0x732f5c01, 0x668a08e1, 0x4e189e14, 0x209c225f, -0x364b4fdd}, + {-0x3e6a239c, -0x3cde5548, -0x39e3e67a, -0x345d3303, -0x2ad90a30, -0x1cc1863f, -0x0e0e9bd5, -0x1368cb1f}, + }, + } + }, + [0][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = -6, // -6 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 32.00f, + .gain = 0.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 64.00f, + .gain = 4.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 2.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 3.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40ea71da, 0x41c96a66, 0x436723df, 0x40000000, 0x449012f8, 0x4a4d3a1a, 0x4d0a569a}, + {-0x7e6501c8, -0x7cca0ea9, -0x7994c1a0, -0x732f5c01, -0x668a08e1, -0x4e189e14, -0x209c225f, 0x364b4fdd}, + {0x3e6a239c, 0x3bf3e36e, 0x381a7c14, 0x30f60f23, 0x2ad90a30, 0x18317347, 0x03c161bb, 0x065e7484}, + {0x7e6501c8, 0x7cca0ea9, 0x7994c1a0, 0x732f5c01, 0x668a08e1, 0x4e189e14, 0x209c225f, -0x364b4fdd}, + {-0x3e6a239c, -0x3cde5548, -0x39e3e67a, -0x345d3303, -0x2ad90a30, -0x1cc1863f, -0x0e0e9bd5, -0x1368cb1f}, + }, + } + }, + [1][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = -3, // -3 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 32.00f, + .gain = 3.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 64.00f, + .gain = 1.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = -1.50f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = -3.50f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 3.50f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 1.50f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 1.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 2.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x4015a1bd, 0x400cc15e, 0x3fb4c9f2, 0x3e84e60a, 0x42e317f3, 0x41fe5db4, 0x430c06de, 0x45c5d7f1}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7c37687b, -0x76c49365, -0x732f5c01, -0x668a08e1, -0x209c225f, 0x364b4fdd}, + {0x3f817f0c, 0x3f222c0f, 0x3c96cd54, 0x388cfd67, 0x317a1b0f, 0x28daac7c, 0x0b0294f6, 0x0da2f32e}, + {0x7f96c9e2, 0x7f2d92eb, 0x7c37687b, 0x76c49365, 0x732f5c01, 0x668a08e1, 0x209c225f, -0x364b4fdd}, + {-0x3f9720ca, -0x3f2eed6e, -0x3c4b9747, -0x3711e371, -0x345d3303, -0x2ad90a30, -0x0e0e9bd5, -0x1368cb1f}, + }, + } + }, + [1][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = -3, // -3 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 32.00f, + .gain = 3.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 64.00f, + .gain = 1.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = -1.50f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = -3.50f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 3.50f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 1.50f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 1.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 2.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x4015a1bd, 0x400cc15e, 0x3fb4c9f2, 0x3e84e60a, 0x42e317f3, 0x41fe5db4, 0x430c06de, 0x45c5d7f1}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7c37687b, -0x76c49365, -0x732f5c01, -0x668a08e1, -0x209c225f, 0x364b4fdd}, + {0x3f817f0c, 0x3f222c0f, 0x3c96cd54, 0x388cfd67, 0x317a1b0f, 0x28daac7c, 0x0b0294f6, 0x0da2f32e}, + {0x7f96c9e2, 0x7f2d92eb, 0x7c37687b, 0x76c49365, 0x732f5c01, 0x668a08e1, 0x209c225f, -0x364b4fdd}, + {-0x3f9720ca, -0x3f2eed6e, -0x3c4b9747, -0x3711e371, -0x345d3303, -0x2ad90a30, -0x0e0e9bd5, -0x1368cb1f}, + }, + } + }, + [2][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = -5, // -5 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 32.00f, + .gain = 1.50f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 64.00f, + .gain = 2.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = -1.50f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 1.50f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x4009e261, 0x40348b39, 0x40ea71da, 0x3f6de57e, 0x4118c15a, 0x3fffffff, 0x4e9b0b4d, 0x4d0a569a}, + {-0x7f96c9e2, -0x7e6501c8, -0x7cca0ea9, -0x787f33eb, -0x732f5c01, -0x4e189e14, -0x209c225f, 0x364b4fdd}, + {0x3f8d3e69, 0x3e359862, 0x3bf3e36e, 0x395fbe9b, 0x334471a9, 0x1cc1863f, -0x008c6f78, 0x065e7484}, + {0x7f96c9e2, 0x7e6501c8, 0x7cca0ea9, 0x787f33eb, 0x732f5c01, 0x4e189e14, 0x209c225f, -0x364b4fdd}, + {-0x3f9720ca, -0x3e6a239c, -0x3cde5548, -0x38cda419, -0x345d3303, -0x1cc1863f, -0x0e0e9bd5, -0x1368cb1f}, + }, + } + }, + [2][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = -5, // -5 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 32.00f, + .gain = 1.50f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 64.00f, + .gain = 2.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = -1.50f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 1.50f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x4009e261, 0x40348b39, 0x40ea71da, 0x3f6de57e, 0x4118c15a, 0x3fffffff, 0x4e9b0b4d, 0x4d0a569a}, + {-0x7f96c9e2, -0x7e6501c8, -0x7cca0ea9, -0x787f33eb, -0x732f5c01, -0x4e189e14, -0x209c225f, 0x364b4fdd}, + {0x3f8d3e69, 0x3e359862, 0x3bf3e36e, 0x395fbe9b, 0x334471a9, 0x1cc1863f, -0x008c6f78, 0x065e7484}, + {0x7f96c9e2, 0x7e6501c8, 0x7cca0ea9, 0x787f33eb, 0x732f5c01, 0x4e189e14, 0x209c225f, -0x364b4fdd}, + {-0x3f9720ca, -0x3e6a239c, -0x3cde5548, -0x38cda419, -0x345d3303, -0x1cc1863f, -0x0e0e9bd5, -0x1368cb1f}, + }, + } + }, + [3][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = -11, // -11 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 7.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 7.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 2.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = -2.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40817dc4, 0x40fb5fcd, 0x40ea71da, 0x40000000, 0x3fffffff, 0x46773b39, 0x3ab46df2}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7e6501c8, -0x7cca0ea9, -0x7994c1a0, -0x4e189e14, -0x209c225f, 0x31cd5177}, + {0x3f9720ca, 0x3ead6fa9, 0x3d6ec3ce, 0x3bf3e36e, 0x39e3e67a, 0x1cc1863f, 0x0797609c, 0x11cdb028}, + {0x7f96c9e2, 0x7f2d92eb, 0x7e6501c8, 0x7cca0ea9, 0x7994c1a0, 0x4e189e14, 0x209c225f, -0x31cd5177}, + {-0x3f9720ca, -0x3f2eed6e, -0x3e6a239c, -0x3cde5548, -0x39e3e67a, -0x1cc1863f, -0x0e0e9bd5, -0x0c821e1b}, + }, + } + }, + [3][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = -11, // -11 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 7.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 7.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 2.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = -2.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40817dc4, 0x40fb5fcd, 0x40ea71da, 0x40000000, 0x3fffffff, 0x46773b39, 0x3ab46df2}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7e6501c8, -0x7cca0ea9, -0x7994c1a0, -0x4e189e14, -0x209c225f, 0x31cd5177}, + {0x3f9720ca, 0x3ead6fa9, 0x3d6ec3ce, 0x3bf3e36e, 0x39e3e67a, 0x1cc1863f, 0x0797609c, 0x11cdb028}, + {0x7f96c9e2, 0x7f2d92eb, 0x7e6501c8, 0x7cca0ea9, 0x7994c1a0, 0x4e189e14, 0x209c225f, -0x31cd5177}, + {-0x3f9720ca, -0x3f2eed6e, -0x3e6a239c, -0x3cde5548, -0x39e3e67a, -0x1cc1863f, -0x0e0e9bd5, -0x0c821e1b}, + }, + } + }, + [4][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = -10, // -10 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 3.80f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 6.30f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.80f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 1.70f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 3.20f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.70f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.30f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 3.20f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x401cc718, 0x406f5ef0, 0x4095b8e5, 0x40a91177, 0x4297702b, 0x4797b900, 0x4b49da50, 0x4b1f8f95}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7e6501c8, -0x7994c1a0, -0x732f5c01, -0x668a08e1, -0x4e189e14, -0x209c225f}, + {0x3f7a59b1, 0x3ebf8e7d, 0x3dd46ab6, 0x393ad503, 0x31c5c2d8, 0x2341512f, 0x1177abee, 0x02ef0c40}, + {0x7f96c9e2, 0x7f2d92eb, 0x7e6501c8, 0x7994c1a0, 0x732f5c01, 0x668a08e1, 0x4e189e14, 0x209c225f}, + {-0x3f9720ca, -0x3f2eed6e, -0x3e6a239c, -0x39e3e67a, -0x345d3303, -0x2ad90a30, -0x1cc1863f, -0x0e0e9bd5}, + }, + } + }, + [4][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = -10, // -10 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 3.80f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 6.30f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.80f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 1.70f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 3.20f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.70f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 4.30f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 3.20f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x401cc718, 0x406f5ef0, 0x4095b8e5, 0x40a91177, 0x4297702b, 0x4797b900, 0x4b49da50, 0x4b1f8f95}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7e6501c8, -0x7994c1a0, -0x732f5c01, -0x668a08e1, -0x4e189e14, -0x209c225f}, + {0x3f7a59b1, 0x3ebf8e7d, 0x3dd46ab6, 0x393ad503, 0x31c5c2d8, 0x2341512f, 0x1177abee, 0x02ef0c40}, + {0x7f96c9e2, 0x7f2d92eb, 0x7e6501c8, 0x7994c1a0, 0x732f5c01, 0x668a08e1, 0x4e189e14, 0x209c225f}, + {-0x3f9720ca, -0x3f2eed6e, -0x3e6a239c, -0x39e3e67a, -0x345d3303, -0x2ad90a30, -0x1cc1863f, -0x0e0e9bd5}, + }, + } + }, + [5][0] = { + .sample_rate = 44100, + .total_bshift = 1, + .post_gain_db = 0, // 0 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_LOWSHELF, + .fc = 64.00f, + .q = 1.2000f, + .bw = 53.00f, + .gain = 6.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 4.5000f, + .bw = 27.00f, + .gain = 4.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 6.7200f, + .bw = 37.00f, + .gain = 2.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7200f, + .bw = 1388.00f, + .gain = -3.50f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 2.5000f, + .bw = 800.00f, + .gain = -1.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 1.5000f, + .bw = 2666.00f, + .gain = 2.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_HIGHSHELF, + .fc = 8000.00f, + .q = 1.1500f, + .bw = 6956.00f, + .gain = 10.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.4500f, + .bw = 35555.00f, + .gain = 9.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x402ba321, 0x4012ecaa, 0x400b35f5, 0x3d455e1b, 0x3f9645d9, 0x4286aa03, 0x479b029d, 0x753f706d}, + {-0x7f2c0ce7, -0x7fba1a56, -0x7f94ad0b, -0x6e69ac2c, -0x738c9f3f, -0x5b57939c, -0x4c01bc94, 0x2d3271dc}, + {0x3f031d23, 0x3fac5d59, 0x3f9e31d1, 0x3245a3aa, 0x38d14d8b, 0x29f65a1f, 0x1b16b101, -0x2fd05d25}, + {0x7f2cb956, 0x7fba1a56, 0x7f94ad0b, 0x6e69ac2c, 0x738c9f3f, 0x5b57939c, 0x208ed3f6, -0x2d3271dc}, + {-0x3f2e13d7, -0x3fbf4a04, -0x3fa967c7, -0x2f8b01c6, -0x38679364, -0x2c7d0423, -0x0deec20a, -0x056f1347}, + }, + } + }, + [5][1] = { + .sample_rate = 44100, + .total_bshift = 1, + .post_gain_db = 0, // 0 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_LOWSHELF, + .fc = 64.00f, + .q = 1.2000f, + .bw = 53.00f, + .gain = 6.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 4.5000f, + .bw = 27.00f, + .gain = 4.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 6.7200f, + .bw = 37.00f, + .gain = 2.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7200f, + .bw = 1388.00f, + .gain = -3.50f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 2.5000f, + .bw = 800.00f, + .gain = -1.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 1.5000f, + .bw = 2666.00f, + .gain = 2.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_HIGHSHELF, + .fc = 8000.00f, + .q = 1.1500f, + .bw = 6956.00f, + .gain = 10.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.4500f, + .bw = 35555.00f, + .gain = 9.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x402ba321, 0x4012ecaa, 0x400b35f5, 0x3d455e1b, 0x3f9645d9, 0x4286aa03, 0x479b029d, 0x753f706d}, + {-0x7f2c0ce7, -0x7fba1a56, -0x7f94ad0b, -0x6e69ac2c, -0x738c9f3f, -0x5b57939c, -0x4c01bc94, 0x2d3271dc}, + {0x3f031d23, 0x3fac5d59, 0x3f9e31d1, 0x3245a3aa, 0x38d14d8b, 0x29f65a1f, 0x1b16b101, -0x2fd05d25}, + {0x7f2cb956, 0x7fba1a56, 0x7f94ad0b, 0x6e69ac2c, 0x738c9f3f, 0x5b57939c, 0x208ed3f6, -0x2d3271dc}, + {-0x3f2e13d7, -0x3fbf4a04, -0x3fa967c7, -0x2f8b01c6, -0x38679364, -0x2c7d0423, -0x0deec20a, -0x056f1347}, + }, + } + }, + [6][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, // 0 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7000f, + .bw = 45.00f, + .gain = -2.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 176.00f, + .gain = -1.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 707.00f, + .gain = 1.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1414.00f, + .gain = 3.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 1.0000f, + .bw = 2000.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 5633.00f, + .gain = 4.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 11267.00f, + .gain = 2.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.7100f, + .bw = 22535.00f, + .gain = -2.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x3ff23dc1, 0x3fe746ae, 0x405f6cac, 0x42666efe, 0x40000000, 0x4a4e979f, 0x46773b39, 0x3ab46df2}, + {-0x7f79de0d, -0x7e342deb, -0x7994c1a0, -0x732f5c01, -0x6bb3556c, -0x4e189e14, -0x209c225f, 0x31cd5177}, + {0x3f87f720, 0x3e520715, 0x398479ce, 0x31f6c405, 0x3039dbd4, 0x1272eea0, 0x0797609c, 0x11cdb028}, + {0x7f79de0d, 0x7e342deb, 0x7994c1a0, 0x732f5c01, 0x6bb3556c, 0x4e189e14, 0x209c225f, -0x31cd5177}, + {-0x3f7a34e2, -0x3e394dc3, -0x39e3e67a, -0x345d3303, -0x3039dbd4, -0x1cc1863f, -0x0e0e9bd5, -0x0c821e1b}, + }, + } + }, + [6][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, // 0 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7000f, + .bw = 45.00f, + .gain = -2.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 176.00f, + .gain = -1.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 707.00f, + .gain = 1.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1414.00f, + .gain = 3.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 1.0000f, + .bw = 2000.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 5633.00f, + .gain = 4.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 8000.00f, + .q = 0.7100f, + .bw = 11267.00f, + .gain = 2.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 16000.00f, + .q = 0.7100f, + .bw = 22535.00f, + .gain = -2.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x3ff23dc1, 0x3fe746ae, 0x405f6cac, 0x42666efe, 0x40000000, 0x4a4e979f, 0x46773b39, 0x3ab46df2}, + {-0x7f79de0d, -0x7e342deb, -0x7994c1a0, -0x732f5c01, -0x6bb3556c, -0x4e189e14, -0x209c225f, 0x31cd5177}, + {0x3f87f720, 0x3e520715, 0x398479ce, 0x31f6c405, 0x3039dbd4, 0x1272eea0, 0x0797609c, 0x11cdb028}, + {0x7f79de0d, 0x7e342deb, 0x7994c1a0, 0x732f5c01, 0x6bb3556c, 0x4e189e14, 0x209c225f, -0x31cd5177}, + {-0x3f7a34e2, -0x3e394dc3, -0x39e3e67a, -0x345d3303, -0x3039dbd4, -0x1cc1863f, -0x0e0e9bd5, -0x0c821e1b}, + }, + } + }, + [7][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, // 0 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1414.00f, + .gain = 0.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x3fffffff}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7e6501c8, -0x7cca0ea9, -0x7994c1a0, -0x732f5c01, -0x668a08e1, -0x4e189e14}, + {0x3f9720ca, 0x3f2eed6e, 0x3e6a239c, 0x3cde5548, 0x39e3e67a, 0x345d3303, 0x2ad90a30, 0x1cc1863f}, + {0x7f96c9e2, 0x7f2d92eb, 0x7e6501c8, 0x7cca0ea9, 0x7994c1a0, 0x732f5c01, 0x668a08e1, 0x4e189e14}, + {-0x3f9720ca, -0x3f2eed6e, -0x3e6a239c, -0x3cde5548, -0x39e3e67a, -0x345d3303, -0x2ad90a30, -0x1cc1863f}, + }, + } + }, + [7][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, // 0 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1414.00f, + .gain = 0.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x3fffffff}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7e6501c8, -0x7cca0ea9, -0x7994c1a0, -0x732f5c01, -0x668a08e1, -0x4e189e14}, + {0x3f9720ca, 0x3f2eed6e, 0x3e6a239c, 0x3cde5548, 0x39e3e67a, 0x345d3303, 0x2ad90a30, 0x1cc1863f}, + {0x7f96c9e2, 0x7f2d92eb, 0x7e6501c8, 0x7cca0ea9, 0x7994c1a0, 0x732f5c01, 0x668a08e1, 0x4e189e14}, + {-0x3f9720ca, -0x3f2eed6e, -0x3e6a239c, -0x3cde5548, -0x39e3e67a, -0x345d3303, -0x2ad90a30, -0x1cc1863f}, + }, + } + }, + [8][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, // 0 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1414.00f, + .gain = 0.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x3fffffff}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7e6501c8, -0x7cca0ea9, -0x7994c1a0, -0x732f5c01, -0x668a08e1, -0x4e189e14}, + {0x3f9720ca, 0x3f2eed6e, 0x3e6a239c, 0x3cde5548, 0x39e3e67a, 0x345d3303, 0x2ad90a30, 0x1cc1863f}, + {0x7f96c9e2, 0x7f2d92eb, 0x7e6501c8, 0x7cca0ea9, 0x7994c1a0, 0x732f5c01, 0x668a08e1, 0x4e189e14}, + {-0x3f9720ca, -0x3f2eed6e, -0x3e6a239c, -0x3cde5548, -0x39e3e67a, -0x345d3303, -0x2ad90a30, -0x1cc1863f}, + }, + } + }, + [8][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, // 0 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1414.00f, + .gain = 0.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x3fffffff}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7e6501c8, -0x7cca0ea9, -0x7994c1a0, -0x732f5c01, -0x668a08e1, -0x4e189e14}, + {0x3f9720ca, 0x3f2eed6e, 0x3e6a239c, 0x3cde5548, 0x39e3e67a, 0x345d3303, 0x2ad90a30, 0x1cc1863f}, + {0x7f96c9e2, 0x7f2d92eb, 0x7e6501c8, 0x7cca0ea9, 0x7994c1a0, 0x732f5c01, 0x668a08e1, 0x4e189e14}, + {-0x3f9720ca, -0x3f2eed6e, -0x3e6a239c, -0x3cde5548, -0x39e3e67a, -0x345d3303, -0x2ad90a30, -0x1cc1863f}, + }, + } + }, + [9][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, // 0 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1414.00f, + .gain = 0.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x3fffffff}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7e6501c8, -0x7cca0ea9, -0x7994c1a0, -0x732f5c01, -0x668a08e1, -0x4e189e14}, + {0x3f9720ca, 0x3f2eed6e, 0x3e6a239c, 0x3cde5548, 0x39e3e67a, 0x345d3303, 0x2ad90a30, 0x1cc1863f}, + {0x7f96c9e2, 0x7f2d92eb, 0x7e6501c8, 0x7cca0ea9, 0x7994c1a0, 0x732f5c01, 0x668a08e1, 0x4e189e14}, + {-0x3f9720ca, -0x3f2eed6e, -0x3e6a239c, -0x3cde5548, -0x39e3e67a, -0x345d3303, -0x2ad90a30, -0x1cc1863f}, + }, + } + }, + [9][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, // 0 dB + .bands = { + { + .index = 0, + .type = FILTER_TYPE_PEAKING, + .fc = 32.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 1, + .type = FILTER_TYPE_PEAKING, + .fc = 64.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 2, + .type = FILTER_TYPE_PEAKING, + .fc = 125.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 3, + .type = FILTER_TYPE_PEAKING, + .fc = 250.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 4, + .type = FILTER_TYPE_PEAKING, + .fc = 500.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 5, + .type = FILTER_TYPE_PEAKING, + .fc = 1000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 6, + .type = FILTER_TYPE_PEAKING, + .fc = 2000.00f, + .q = 0.7100f, + .bw = 1.00f, + .gain = 0.00f, + }, + { + .index = 7, + .type = FILTER_TYPE_PEAKING, + .fc = 4000.00f, + .q = 0.7100f, + .bw = 1414.00f, + .gain = 0.00f, + }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x3fffffff}, + {-0x7f96c9e2, -0x7f2d92eb, -0x7e6501c8, -0x7cca0ea9, -0x7994c1a0, -0x732f5c01, -0x668a08e1, -0x4e189e14}, + {0x3f9720ca, 0x3f2eed6e, 0x3e6a239c, 0x3cde5548, 0x39e3e67a, 0x345d3303, 0x2ad90a30, 0x1cc1863f}, + {0x7f96c9e2, 0x7f2d92eb, 0x7e6501c8, 0x7cca0ea9, 0x7994c1a0, 0x732f5c01, 0x668a08e1, 0x4e189e14}, + {-0x3f9720ca, -0x3f2eed6e, -0x3e6a239c, -0x3cde5548, -0x39e3e67a, -0x345d3303, -0x2ad90a30, -0x1cc1863f}, + }, + } + }, + // 用户模式 10 (user 1) - 平坦初始值,用户可自定义 + [10][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, + .bands = { + { .index = 0, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 1, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 2, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 3, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 4, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 5, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 6, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 7, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + }, + } + }, + [10][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, + .bands = { + { .index = 0, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 1, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 2, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 3, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 4, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 5, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 6, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 7, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + }, + } + }, + // 用户模式 11 (user 2) - 平坦初始值,用户可自定义 + [11][0] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, + .bands = { + { .index = 0, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 1, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 2, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 3, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 4, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 5, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 6, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 7, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + }, + } + }, + [11][1] = { + .sample_rate = 44100, + .total_bshift = 0, + .post_gain_db = 0, + .bands = { + { .index = 0, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 1, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 2, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 3, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 4, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 5, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 6, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + { .index = 7, .type = FILTER_TYPE_BYPASS, .fc = 1000.00f, .q = 0.7100f, .bw = 1.00f, .gain = 0.00f }, + }, + .filter = { + .biquad_count = MAX_EQ_BANDS, + .state = {{0}}, + .coef = { + {0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000, 0x40000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + }, + } + }, +}; diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/ex3d_0.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/ex3d_0.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/ex3d_0.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/ex3d_0.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/ex3d_protocol.md b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/ex3d_protocol.md similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/ex3d_protocol.md rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/ex3d_protocol.md diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/filter_utils.py b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/filter_utils.py similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/filter_utils.py rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/filter_utils.py diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/hid_report_descriptor.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/hid_report_descriptor.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/hid_report_descriptor.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/hid_report_descriptor.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/hidbuttons.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/hidbuttons.xc similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/hidbuttons.xc rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/hidbuttons.xc diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/host_os_detect.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/host_os_detect.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/host_os_detect.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/host_os_detect.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/hostactive.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/hostactive.xc similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/hostactive.xc rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/hostactive.xc diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/htr3236.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/htr3236.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/htr3236.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.xc similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/htr3236.xc rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.xc diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.xc.bak b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.xc.bak new file mode 100644 index 0000000..0ad1bd5 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.xc.bak @@ -0,0 +1,255 @@ +/** + * @file htr3236.c + * @brief HTR3236 36-Channel LED PWM Driver Implementation for XMOS + * @version 1.1 + */ + +#include "htr3236.h" +#include + +#include + +out port p_htr3235_sdb = PORT_HTR3236_SDB; /* 连接到HTR3236的SDB引脚,用于控制其电源状态 */ + +/*========================================================================= + Gamma 校正查找表 + -----------------------------------------------------------------------*/ + +static const uint8_t gamma_table_32[32] = { + 0, 1, 2, 4, 6, 10, 13, 18, + 22, 28, 33, 39, 46, 53, 61, 69, + 78, 86, 96, 106, 116, 126, 138, 149, + 161, 173, 186, 199, 212, 226, 240, 255 +}; + +static const uint8_t gamma_table_64[64] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 10, 12, 14, 16, 18, 20, 22, + 24, 26, 29, 32, 35, 38, 41, 44, + 47, 50, 53, 57, 61, 65, 69, 73, + 77, 81, 85, 89, 94, 99, 104, 109, + 114, 119, 124, 129, 134, 140, 146, 152, + 158, 164, 170, 176, 182, 188, 195, 202, + 209, 216, 223, 230, 237, 244, 251, 255 +}; + +/*========================================================================= + 内部函数 + -----------------------------------------------------------------------*/ + +/** + * @brief 写单字节到寄存器 + */ +static int write_reg(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t reg, uint8_t data) +{ +#if 0 + uint8_t buf[2] = {reg, data}; + size_t n; + + i2c.write(dev->i2c_addr, buf, 2, n, 1); + + if (n == 0) + { + return I2C_REGOP_DEVICE_NACK; + } + if (n < 2) + { + return I2C_REGOP_INCOMPLETE; + } + +#endif + return I2C_REGOP_SUCCESS; +} + +/** + * @brief 写多字节到寄存器 (支持自动地址递增) + */ +static int write_reg_bulk(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t start_reg, + const uint8_t *data, uint8_t len) +{ + uint8_t buf[36 + 1]; + size_t n; +#if 0 + buf[0] = start_reg; + + for (int i = 0; i < len; i++) { + buf[i + 1] = data[i]; + } + + i2c.write(dev->i2c_addr, buf, len + 1, n, 1); + + if (n == 0) + { + return I2C_REGOP_DEVICE_NACK; + } + if (n < 2) + { + return I2C_REGOP_INCOMPLETE; + } +#endif + + return I2C_REGOP_SUCCESS; +} + +/** + * @brief 读取寄存器值 + */ +static int read_reg(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t reg, i2c_regop_res_t &result) +{ + uint8_t a_reg[1] = {reg}; + uint8_t data[1] = {0}; + size_t n; + i2c_res_t res; + + unsafe + { + res = i2c.write(dev->i2c_addr, a_reg, 1, n, 0); + + if (n != 1) + { + result = I2C_REGOP_DEVICE_NACK; + i2c.send_stop_bit(); + return 0; + } + + res = i2c.read(dev->i2c_addr, data, 1, 1); + } + + if (res == I2C_ACK) + { + result = I2C_REGOP_SUCCESS; + } + else + { + result = I2C_REGOP_DEVICE_NACK; + } + return data[0]; +} + + + +/*========================================================================= + API 函数实现 + -----------------------------------------------------------------------*/ + +void htr3236_init(htr3236_t *dev, uint8_t addr) +{ + dev->i2c_addr = addr; + + // 默认 SDB 拉高,使能芯片 + //htr3236_hw_enable(dev); +} + +void htr3236_hw_enable(htr3236_t *dev) +{ + p_htr3235_sdb <: 1; +} + +void htr3236_hw_disable(htr3236_t *dev) +{ + p_htr3235_sdb <: 0; +} + +int htr3236_software_wake(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c)) +{ + return write_reg(dev, i2c, HTR3236_REG_SHUTDOWN, HTR3236_NORMAL_OP); +} + +int htr3236_software_shutdown(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c)) +{ + return write_reg(dev, i2c, HTR3236_REG_SHUTDOWN, HTR3236_SHUTDOWN_SOFT); +} + +int htr3236_software_reset(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c)) +{ + return write_reg(dev, i2c, HTR3236_REG_RESET, 0x00); +} + +int htr3236_set_pwm(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t channel, uint8_t brightness) +{ + if (channel < 1 || channel > 36) { + return -1; + } + + uint8_t reg = HTR3236_REG_PWM_START + (channel - 1); + return write_reg(dev, i2c, reg, brightness); +} + +int htr3236_set_pwm_bulk(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t start_ch, + const uint8_t *values, uint8_t len) +{ + if (start_ch < 1 || start_ch > 36 || len == 0) { + return -1; + } + + // 检查是否会超出寄存器范围 + if (start_ch + len - 1 > 36) { + return -1; + } + + uint8_t start_reg = HTR3236_REG_PWM_START + (start_ch - 1); + return write_reg_bulk(dev, i2c, start_reg, values, len); +} + +int htr3236_set_led_config(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t channel, + htr3236_current_t current, uint8_t enable) +{ + if (channel < 1 || channel > 36) { + return -1; + } + + uint8_t reg = HTR3236_REG_LED_CTRL_START + (channel - 1); + uint8_t data = (enable ? 1 : 0) | (current << 1); + + return write_reg(dev, i2c, reg, data); +} + +int htr3236_set_led_config_bulk(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t start_ch, + const uint8_t *configs, uint8_t len) +{ + if (start_ch < 1 || start_ch > 36 || len == 0) { + return -1; + } + + // 检查是否会超出寄存器范围 + if (start_ch + len - 1 > 36) { + return -1; + } + + uint8_t start_reg = HTR3236_REG_LED_CTRL_START + (start_ch - 1); + return write_reg_bulk(dev, i2c, start_reg, configs, len); +} + +int htr3236_update(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c)) +{ + // 写入任意值到更新寄存器使配置生效 + return write_reg(dev, i2c, HTR3236_REG_PWM_UPDATE, 0x00); +} + +int htr3236_global_enable(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t enable) +{ + uint8_t data = enable ? 1 : 0; + return write_reg(dev, i2c, HTR3236_REG_GLOBAL_CTRL, data); +} + +int htr3236_set_frequency(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), htr3236_freq_t freq) +{ + uint8_t data = (freq == HTR3236_FREQ_22KHZ) ? 1 : 0; + return write_reg(dev, i2c, HTR3236_REG_FREQ_SET, data); +} + +uint8_t htr3236_gamma_32(uint8_t index) +{ + if (index >= 32) { + index = 31; + } + return gamma_table_32[index]; +} + +uint8_t htr3236_gamma_64(uint8_t index) +{ + if (index >= 64) { + index = 63; + } + return gamma_table_64[index]; +} \ No newline at end of file diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/keys.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/keys.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/keys.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/keys.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/led_control.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/led_control.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/led_control.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/led_control.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/led_control.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/led_control.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/led_control.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/led_control.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/lfs_io.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/lfs_io.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/lfs_io.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/lfs_io.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/lfs_services.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/lfs_services.c similarity index 99% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/lfs_services.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/lfs_services.c index 9560ab0..0df1818 100644 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/lfs_services.c +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/lfs_services.c @@ -1,5 +1,5 @@ #if UART_DEBUG || DEBUG_MEMORY_LOG_ENABLED -#define DEBUG_PRINT_ENABLE 1 +#define DEBUG_PRINT_ENABLE 0 #else #define DEBUG_PRINT_ENABLE 0 #endif diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/lfs_services.c.bak b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/lfs_services.c.bak new file mode 100644 index 0000000..62c87ba --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/lfs_services.c.bak @@ -0,0 +1,233 @@ +#include +#include +#include +#include "lfs.h" +#include "rtos_qspi_flash.h" +#include "swlock.h" +#include "debug_print.h" +// variables used by the filesystem +lfs_t lfs; +lfs_file_t file; +swlock_t lfs_lock = SWLOCK_INITIAL_VALUE; + +static rtos_qspi_flash_t qspi_flash_ctx_s; +#define FLASH_CLKBLK XS1_CLKBLK_3 +#ifndef FS_BASE_ADDR +#define FS_BASE_ADDR 0x1da000 +#endif +#define SECTOR_SIZE 4096 +rtos_qspi_flash_t *qspi_flash_ctx = &qspi_flash_ctx_s; + +__attribute__((fptrgroup(" local_block_device_read_fptr_grp"))) +int local_block_device_read(const struct lfs_config *c, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size) +{ + unsigned address = (FS_BASE_ADDR + block * SECTOR_SIZE + off); + qspi_flash_ctx->read(qspi_flash_ctx, buffer, address, size); + return 0; +} + +__attribute__((fptrgroup(" local_block_device_prog_fptr_grp"))) +int local_block_device_prog(const struct lfs_config *c, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size) +{ + unsigned address = (FS_BASE_ADDR + block * SECTOR_SIZE + off); + qspi_flash_ctx->write(qspi_flash_ctx, buffer, address, size); + return 0; +} + +__attribute__((fptrgroup(" local_block_device_erase_fptr_grp"))) +int local_block_device_erase(const struct lfs_config *c, lfs_block_t block) +{ + unsigned address = (FS_BASE_ADDR + block * SECTOR_SIZE); + qspi_flash_ctx->erase(qspi_flash_ctx, address, SECTOR_SIZE); + return 0; +} + +__attribute__((fptrgroup(" local_block_device_sync_fptr_grp"))) +int local_block_device_sync(const struct lfs_config *c) +{ + return 0; +} + +// configuration of the filesystem is provided by this struct +const struct lfs_config cfg = { + // block device operations + .read = local_block_device_read, + .prog = local_block_device_prog, + .erase = local_block_device_erase, + .sync = local_block_device_sync, + + // block device configuration + .read_size = 16, + .prog_size = 16, + .block_size = 4096, + .block_count = 128, + .cache_size = 16, + .lookahead_size = 16, + .block_cycles = 500, +}; + +int lfs_init(void) { + swlock_acquire(&lfs_lock); + rtos_qspi_flash_init( + qspi_flash_ctx, + FLASH_CLKBLK, + XS1_PORT_1B, + XS1_PORT_1C, + XS1_PORT_4B, + NULL); + + + // mount the filesystem + int err = lfs_mount(&lfs, &cfg); + + // reformat if we can't mount the filesystem + // this should only happen on the first boot + if (err) { + debug_printf("no lfs partiton is found, formating ...\n"); + lfs_format(&lfs, &cfg); + lfs_mount(&lfs, &cfg); + swlock_release(&lfs_lock); + return -1; + } + swlock_release(&lfs_lock); + return 0; + +} + +void lfs_deinit(void) { + swlock_acquire(&lfs_lock); + lfs_unmount(&lfs); + swlock_release(&lfs_lock); +} + +#pragma stackfunction 1300 +void lfs_read_config(unsigned char * config, unsigned char * buffer, unsigned size) +{ + swlock_acquire(&lfs_lock); + debug_printf("lfs_read_config: %s, size: %d\n", config, size); + int result = lfs_file_open(&lfs, &file, config, LFS_O_RDWR | LFS_O_CREAT); + debug_printf("lfs_read_config: %s, result: %d\n", config, result); + if (result != 0) { + debug_printf("lfs_read_config: %s, open file failed\n", config); + swlock_release(&lfs_lock); + return; + } + debug_printf("lfs_read_config: %s, file opened\n", config); + result = lfs_file_read(&lfs, &file, buffer, size); + debug_printf("lfs_read_config: %s, result: %d\n", config, result); + if (result < 0) { + debug_printf("lfs_read_config: %s, read file failed, error: %d\n", config, result); + // Update: Added lfs_file_close to ensure file is closed even on error. + // Missing this caused subsequent open calls to assert/crash. + lfs_file_close(&lfs, &file); + swlock_release(&lfs_lock); + return; + } + lfs_file_close(&lfs, &file); + swlock_release(&lfs_lock); +} + + +#pragma stackfunction 1300 +void lfs_write_config(unsigned char * config, unsigned char * buffer, unsigned size) +{ + swlock_acquire(&lfs_lock); + debug_printf("lfs_write_config: %s, size: %d\n", config, size); + int result = lfs_file_open(&lfs, &file, config, LFS_O_RDWR | LFS_O_CREAT); + if (result != 0) { + debug_printf("lfs_write_config: open file failed\n"); + swlock_release(&lfs_lock); + return; + } + result = lfs_file_rewind(&lfs, &file); + if (result != 0) { + debug_printf("lfs_write_config: rewind file failed\n"); + // Update: Added lfs_file_close to prevent file remaining open if rewind fails. + lfs_file_close(&lfs, &file); + swlock_release(&lfs_lock); + return; + } + lfs_file_write(&lfs, &file, buffer, size); + lfs_file_close(&lfs, &file); + swlock_release(&lfs_lock); +} + + +// 检查文件是否存在 +int lfs_file_exists(const char * file_path) +{ + swlock_acquire(&lfs_lock); + lfs_file_t file; + int result = lfs_file_open(&lfs, &file, file_path, LFS_O_RDONLY); + if (result == 0) { + lfs_file_close(&lfs, &file); + swlock_release(&lfs_lock); + return 1; // 文件存在 + } + swlock_release(&lfs_lock); + return 0; // 文件不存在 +} + +// EQ参数专用读写函数 +void lfs_read_eq_config(const char * file_path, unsigned char * buffer, unsigned size) +{ + lfs_file_open(&lfs, &file, file_path, LFS_O_RDONLY); + lfs_file_read(&lfs, &file, buffer, size); + lfs_file_close(&lfs, &file); +} + +void lfs_write_eq_config(const char * file_path, unsigned char * buffer, unsigned size) +{ + lfs_file_open(&lfs, &file, file_path, LFS_O_RDWR | LFS_O_CREAT); + lfs_file_rewind(&lfs, &file); + lfs_file_write(&lfs, &file, buffer, size); + lfs_file_close(&lfs, &file); +} + + +// 删除文件 +int lfs_remove_file(const char * file_path) +{ + return lfs_remove(&lfs, file_path); +} + +// 创建目录(通过创建临时文件然后删除) +int lfs_create_directory(const char * dir_path) +{ + char temp_file[256]; + snprintf(temp_file, sizeof(temp_file), "%s/.dir", dir_path); + + lfs_file_t file; + int result = lfs_file_open(&lfs, &file, temp_file, LFS_O_RDWR | LFS_O_CREAT); + if (result == 0) { + lfs_file_close(&lfs, &file); + lfs_remove(&lfs, temp_file); + return 0; // 成功 + } + return -1; // 失败 +} + +#if 0 + // read current count + uint32_t boot_count = 0; + lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT); + lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count)); + + // update boot count + boot_count += 1; + lfs_file_rewind(&lfs, &file); + for (int i = 0; i < 100; i++) + lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count)); + + // remember the storage is not updated until the file is closed successfully + lfs_file_close(&lfs, &file); + + // release any resources we were using + lfs_unmount(&lfs); + + // print the boot count + printf("boot_count: %d\n", boot_count); + +#endif diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/log.txt b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/log.txt new file mode 100644 index 0000000..5c39e5d --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/log.txt @@ -0,0 +1 @@ +ninja: Entering directory `build' diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/main.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/main.xc rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc.bak b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc.bak new file mode 100644 index 0000000..50cf2a5 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc.bak @@ -0,0 +1,836 @@ +// Copyright 2012-2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#include "xua.h" /* Device specific defines */ +#ifdef EXCLUDE_USB_AUDIO_MAIN + +/** + * @file main.xc + * @brief Top level for XMOS USB 2.0 Audio 2.0 Reference Designs. + * @author Ross Owen, XMOS Semiconductor Ltd + */ +#include +#include +#include +#include +#include +#ifdef XSCOPE +#include +#endif + +#if XUA_USB_EN +#include "xud_device.h" /* XMOS USB Device Layer defines and functions */ +#include "xua_endpoint0.h" +#endif + +#include "uac_hwresources.h" + +#ifdef IAP +#include "i2c_shared.h" +#include "iap.h" +#endif + +#if (XUA_SPDIF_RX_EN || XUA_SPDIF_TX_EN) +#include "spdif.h" /* From lib_spdif */ +#endif + +#if (XUA_ADAT_RX_EN) +#include "adat_rx.h" +#endif + +#if (XUA_NUM_PDM_MICS > 0) +#include "xua_pdm_mic.h" +#endif + +#if (XUA_DFU_EN == 1) +[[distributable]] +void DFUHandler(server interface i_dfu i, chanend ?c_user_cmd); +#endif + +/* Audio I/O - Port declarations */ +#if I2S_WIRES_DAC > 0 +on tile[AUDIO_IO_TILE] : buffered out port:32 p_i2s_dac[I2S_WIRES_DAC] = + {PORT_I2S_DAC0, +#endif +#if I2S_WIRES_DAC > 1 + PORT_I2S_DAC1, +#endif +#if I2S_WIRES_DAC > 2 + PORT_I2S_DAC2, +#endif +#if I2S_WIRES_DAC > 3 + PORT_I2S_DAC3, +#endif +#if I2S_WIRES_DAC > 4 + PORT_I2S_DAC4, +#endif +#if I2S_WIRES_DAC > 5 + PORT_I2S_DAC5, +#endif +#if I2S_WIRES_DAC > 6 + PORT_I2S_DAC6, +#endif +#if I2S_WIRES_DAC > 7 +#error I2S_WIRES_DAC value is too large! +#endif +#if I2S_WIRES_DAC > 0 + }; +#else + #define p_i2s_dac null +#endif + +#if I2S_WIRES_ADC > 0 +on tile[AUDIO_IO_TILE] : buffered in port:32 p_i2s_adc[I2S_WIRES_ADC] = + {PORT_I2S_ADC0, +#endif +#if I2S_WIRES_ADC > 1 + PORT_I2S_ADC1, +#endif +#if I2S_WIRES_ADC > 2 + PORT_I2S_ADC2, +#endif +#if I2S_WIRES_ADC > 3 + PORT_I2S_ADC3, +#endif +#if I2S_WIRES_ADC > 4 + PORT_I2S_ADC4, +#endif +#if I2S_WIRES_ADC > 5 + PORT_I2S_ADC5, +#endif +#if I2S_WIRES_ADC > 6 + PORT_I2S_ADC6, +#endif +#if I2S_WIRES_ADC > 7 +#error I2S_WIRES_ADC value is too large! +#endif +#if I2S_WIRES_ADC > 0 + }; +#else + #define p_i2s_adc null +#endif + + +#if CODEC_MASTER +on tile[AUDIO_IO_TILE] : buffered in port:32 p_lrclk = PORT_I2S_LRCLK; +on tile[AUDIO_IO_TILE] : buffered in port:32 p_bclk = PORT_I2S_BCLK; +#else +on tile[AUDIO_IO_TILE] : buffered out port:32 p_lrclk = PORT_I2S_LRCLK; +on tile[AUDIO_IO_TILE] : buffered out port:32 p_bclk = PORT_I2S_BCLK; +#endif + +#if (!CODEC_MASTER) || XUA_SPDIF_TX_EN || XUA_ADAT_TX_EN || ((AUDIO_IO_TILE == XUD_TILE) && XUA_USB_EN) +/* Audio master clock input */ +on tile[AUDIO_IO_TILE] : in port p_mclk_in = PORT_MCLK_IN; +#else +#define p_mclk_in null +#endif + +#if (AUDIO_IO_TILE != XUD_TILE) && XUA_USB_EN +/* If audio I/O and USB running on different tiles we need a separate port for + * the master clock input (to use for USB async feedback calculation) */ +on tile[XUD_TILE] : in port p_mclk_in_usb = PORT_MCLK_IN_USB; +#endif + +#if XUA_USB_EN +on tile[XUD_TILE] : in port p_for_mclk_count = PORT_MCLK_COUNT; +#endif + +#if (XUA_SPDIF_TX_EN) +on tile[SPDIF_TX_TILE] : buffered out port:32 p_spdif_tx = PORT_SPDIF_OUT; +#endif + +#if (XUA_ADAT_TX_EN) +on stdcore[AUDIO_IO_TILE] : buffered out port:32 p_adat_tx = PORT_ADAT_OUT; +#endif + +#if (XUA_ADAT_RX_EN) +on stdcore[XUD_TILE] : buffered in port:32 p_adat_rx = PORT_ADAT_IN; +#endif + +#if (XUA_SPDIF_RX_EN) +on tile[XUD_TILE] : in port p_spdif_rx = PORT_SPDIF_IN; +#endif + +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) || (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) +/* Reference to external clock multiplier */ +on tile[PLL_REF_TILE] : out port p_pll_ref = PORT_PLL_REF; +#ifdef __XS3A__ +on tile[AUDIO_IO_TILE] : port p_for_mclk_count_audio = PORT_MCLK_COUNT_2; +#else /* __XS3A__ */ +#define p_for_mclk_count_audio null +#endif /* __XS3A__ */ +#endif + +#ifdef MIDI +on tile[MIDI_TILE] : port p_midi_tx = PORT_MIDI_OUT; + +#if(MIDI_RX_PORT_WIDTH == 4) +on tile[MIDI_TILE] : buffered in port:4 p_midi_rx = PORT_MIDI_IN; +#elif(MIDI_RX_PORT_WIDTH == 1) +on tile[MIDI_TILE] : buffered in port:1 p_midi_rx = PORT_MIDI_IN; +#endif +#endif + + +#ifdef MIDI +on tile[MIDI_TILE] : clock clk_midi = CLKBLK_MIDI; +#endif + +#if (XUA_SPDIF_TX_EN || XUA_ADAT_TX_EN) +on tile[SPDIF_TX_TILE] : clock clk_mst_spd = CLKBLK_SPDIF_TX; +#endif + +#if (XUA_SPDIF_RX_EN) +on tile[XUD_TILE] : clock clk_spd_rx = CLKBLK_SPDIF_RX; +#endif + +on tile[AUDIO_IO_TILE] : clock clk_audio_mclk = CLKBLK_MCLK; /* Master clock */ + +#if (AUDIO_IO_TILE != XUD_TILE) && XUA_USB_EN +/* Separate clock/port for USB feedback calculation */ +on tile[XUD_TILE] : clock clk_audio_mclk_usb = CLKBLK_MCLK; /* Master clock */ +#endif + +on tile[AUDIO_IO_TILE] : clock clk_audio_bclk = CLKBLK_I2S_BIT; /* Bit clock */ + +#ifdef IAP +/* I2C ports - in a struct for use with module_i2c_shared & module_i2c_simple/module_i2c_single_port */ +#ifdef PORT_I2C +on tile [IAP_TILE] : struct r_i2c r_i2c = {PORT_I2C}; +#else +on tile [IAP_TILE] : struct r_i2c r_i2c = {PORT_I2C_SCL, PORT_I2C_SDA}; +#endif +#endif + +#if XUA_USB_EN +/* Endpoint type tables for XUD */ +XUD_EpType epTypeTableOut[ENDPOINT_COUNT_OUT] = { XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, +#if (NUM_USB_CHAN_IN > 0) + XUD_EPTYPE_ISO, /* Audio */ +#endif +#ifdef MIDI + XUD_EPTYPE_BUL, /* MIDI */ +#endif +#if HID_OUT_REQUIRED + XUD_EPTYPE_INT, +#endif +#ifdef IAP + XUD_EPTYPE_BUL, /* iAP */ +#ifdef IAP_EA_NATIVE_TRANS + XUD_EPTYPE_BUL, /* EA Native Transport */ +#endif +#endif + }; + +XUD_EpType epTypeTableIn[ENDPOINT_COUNT_IN] = { XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, +#if (NUM_USB_CHAN_IN > 0) + XUD_EPTYPE_ISO, +#endif +#if (NUM_USB_CHAN_OUT > 0) && ((NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)) + XUD_EPTYPE_ISO, /* Async feedback endpoint */ +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + XUD_EPTYPE_INT, +#endif +#ifdef MIDI + XUD_EPTYPE_BUL, +#endif +#if XUA_OR_STATIC_HID_ENABLED + XUD_EPTYPE_INT, +#endif +#ifdef IAP + XUD_EPTYPE_BUL | XUD_STATUS_ENABLE, +#ifdef IAP_INT_EP + XUD_EPTYPE_BUL | XUD_STATUS_ENABLE, +#endif +#ifdef IAP_EA_NATIVE_TRANS + XUD_EPTYPE_BUL | XUD_STATUS_ENABLE, +#endif +#endif + }; +#endif /* XUA_USB_EN */ + +void thread_speed() +{ +#ifdef FAST_MODE +#warning Building with fast mode enabled + set_thread_fast_mode_on(); +#else + set_thread_fast_mode_off(); +#endif +} + +#ifdef XSCOPE +void xscope_user_init() +{ + xscope_register(0, 0, "", 0, ""); + + xscope_config_io(XSCOPE_IO_BASIC); +} +#endif + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) +void SpdifTxWrapper(chanend c_spdif_tx) +{ + unsigned portId; + //configure_clock_src(clk, p_mclk); + + // TODO could share clock block here.. + // NOTE, Assuming SPDIF tile == USB tile here.. + asm("ldw %0, dp[p_mclk_in_usb]":"=r"(portId)); + asm("setclk res[%0], %1"::"r"(clk_mst_spd), "r"(portId)); + configure_out_port_no_ready(p_spdif_tx, clk_mst_spd, 0); + set_clock_fall_delay(clk_mst_spd, 7); + start_clock(clk_mst_spd); + + while(1) + { + spdif_tx(p_spdif_tx, c_spdif_tx); + } +} +#endif + +void usb_audio_io(chanend ?c_aud_in, +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + chanend c_spdif_tx, +#endif +#if (MIXER) + chanend c_mix_ctl, +#endif + streaming chanend ?c_spdif_rx, + streaming chanend ?c_adat_rx, + chanend ?c_clk_ctl, + chanend ?c_clk_int +#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) + , server interface i_dfu ?dfuInterface +#endif +#if (XUA_NUM_PDM_MICS > 0) + , chanend c_pdm_pcm +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , client interface pll_ref_if i_pll_ref +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , chanend c_audio_rate_change +#endif +#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + , port p_for_mclk_count_aud + , chanend c_sw_pll +#endif +) +{ +#if (MIXER) + chan c_mix_out; +#endif + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + chan c_dig_rx; + chan c_audio_rate_change; /* Notification of new mclk freq to clockgen and synch */ +#if XUA_USE_SW_PLL + /* Connect p_for_mclk_count_aud to clk_audio_mclk so we can count mclks/timestamp in digital rx*/ + unsigned x = 0; + asm("ldw %0, dp[clk_audio_mclk]":"=r"(x)); + asm("setclk res[%0], %1"::"r"(p_for_mclk_count_aud), "r"(x)); +#endif /* XUA_USE_SW_PLL */ +#endif /* (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) */ + + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE == AUDIO_IO_TILE) + chan c_spdif_tx; + + /* Setup S/PDIF tx port - note this is done before par since sharing clock-block/port */ + spdif_tx_port_config(p_spdif_tx, clk_audio_mclk, p_mclk_in, 7); +#endif + + par + { +#if (MIXER && XUA_USB_EN) + /* Mixer cores(s) */ + { + thread_speed(); + mixer(c_aud_in, c_mix_out, c_mix_ctl); + } +#endif + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE == AUDIO_IO_TILE) + while(1) + { + spdif_tx(p_spdif_tx, c_spdif_tx); + } +#endif + + /* Audio I/O core (pars additional S/PDIF TX Core) */ + { + thread_speed(); +#if (MIXER) +#define AUDIO_CHANNEL c_mix_out +#else +#define AUDIO_CHANNEL c_aud_in +#endif + XUA_AudioHub(AUDIO_CHANNEL, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc +#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE) + , c_spdif_tx +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , c_dig_rx +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , c_audio_rate_change +#endif +#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) + , dfuInterface +#endif +#if (XUA_NUM_PDM_MICS > 0) + , c_pdm_pcm +#endif + ); + } + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + { + /* ClockGen must currently run on same tile as AudioHub due to shared memory buffer + * However, due to the use of an interface the pll reference signal port can be on another tile + */ + thread_speed(); + clockGen( c_spdif_rx, + c_adat_rx, + i_pll_ref, + c_dig_rx, + c_clk_ctl, + c_clk_int, + c_audio_rate_change +#if XUA_USE_SW_PLL + , p_for_mclk_count_aud + , c_sw_pll +#endif + ); + } +#endif + + } // par +} + +#ifndef USER_MAIN_DECLARATIONS +#define USER_MAIN_DECLARATIONS +#endif + +#ifndef USER_MAIN_CORES +#define USER_MAIN_CORES +#endif + +//extern unsafe client interface i2c_master_if i_i2c_client; +//extern unsafe client interface i2c_master_if i_i2c_client_t0; +extern void dsp_core0(void); +extern void board_setup(); +extern void dsp_main (chanend c_data); +extern void SetEqDataChan (chanend c); +extern void SetEx3dHidChan (chanend c); + +extern int dsp_worker_tile(chanend c_dsp_to_ex3d, int worker_id); +//extern int dsp_worker_tile_1(chanend c_dsp_to_ex3d, int worker_id); +extern void ex3d_task(); +extern void hid_button_task(chanend cc_mic_level, chanend c_hid, chanend c_hidSendData, chanend c_uac_vol, chanend c_ex3d_hid_cmd); +#if HID_DFU_EN +extern void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx, streaming chanend c_dfu_rx); +#else +extern void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx); +#endif +extern void dnr_dsp_proc_task(void); + +extern unsafe chanend uc_dsp_to_ex3d[DSP_WORKER_COUNT]; +extern unsafe chanend uc_dsp_to_dnr_t1; +extern unsafe chanend uc_key_to_ubm_t0; +extern unsafe chanend uc_audiohw; +#if HID_DFU_EN +extern unsafe streaming chanend uc_dfu; +#endif +extern void key_sender(chanend c_key); +extern void key_receiver(chanend c_key); +extern void mute_handler(); + + +/* Main for USB Audio Applications */ +int main() +{ +#if !XUA_USB_EN + #define c_mix_out null +#else + chan c_mix_out; +#endif + +#ifdef MIDI + chan c_midi; +#endif +#ifdef IAP + chan c_iap; +#ifdef IAP_EA_NATIVE_TRANS + chan c_ea_data; +#endif +#endif + +#if (MIXER) + chan c_mix_ctl; +#endif + +#if (XUA_SPDIF_RX_EN) + streaming chan c_spdif_rx; +#else +#define c_spdif_rx null +#endif + +#if (XUA_ADAT_RX_EN) + streaming chan c_adat_rx; +#else +#define c_adat_rx null +#endif + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + chan c_spdif_tx; +#endif + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + chan c_clk_ctl; + chan c_clk_int; +#else +#define c_clk_int null +#define c_clk_ctl null +#endif + +#if (XUA_DFU_EN == 1) + interface i_dfu dfuInterface; +#else + #define dfuInterface null +#endif + +#if (XUA_NUM_PDM_MICS > 0) + chan c_pdm_pcm; +#endif + +#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC && !XUA_USE_SW_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) ) + interface pll_ref_if i_pll_ref; +#endif + +#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + chan c_sw_pll; +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + chan c_audio_rate_change; /* Notification of new mclk freq to ep_buffer */ +#endif + chan c_sof; + chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */ + chan c_xud_in[ENDPOINT_COUNT_IN]; + + /* Used to communicate controls/setting from XUA_Endpoint0() to the Audio/Buffering sub-system */ + chan c_aud_ctl; + +#if (!MIXER) +#define c_mix_ctl null +#endif + +#ifdef IAP_EA_NATIVE_TRANS + chan c_EANativeTransport_ctrl; +#else +#define c_EANativeTransport_ctrl null +#endif + +//#if (HID_CONTROLS > 0) +// chan c_hid; +//#endif + + USER_MAIN_DECLARATIONS + + chan c_dsp_to_ex3d[DSP_WORKER_COUNT]; + chan cc_mic_level; + chan c_audiohw; +#if HID_DFU_EN + streaming chan c_dfu; +#endif + chan c_key; chan c_hidSendData; + chan c_hidRcvData; + chan c_eq_data; + chan c_uac_vol; + chan c_ex3d_hid_cmd; + + par + { + USER_MAIN_CORES + on tile[1] : { + par { + unsafe { + uc_audiohw = (chanend)c_audiohw; +#if EQ_EN == 1 && USE_EX3D == 0 + SetEqDataChan(c_eq_data); +#endif + hid_button_task(cc_mic_level, c_hidRcvData, c_hidSendData, c_uac_vol, c_ex3d_hid_cmd); + } +#if USE_EX3D == 1 + par(int i = 0; i < DSP_WORKER_COUNT; i++) + dsp_worker_tile(c_dsp_to_ex3d[i], i); + + unsafe { + SetEqDataChan(c_eq_data); + delay_milliseconds(200); + key_sender(c_key); + for (int i = 0; i < DSP_WORKER_COUNT; i++) + uc_dsp_to_ex3d[i] = (chanend)c_dsp_to_ex3d[i]; + ex3d_task(); + } +#endif + + } + } + on tile[1]: mute_handler(); + + on tile[0] : { + par { + { +#if USE_EX3D == 1 + unsafe { key_receiver(c_key); } +#endif + AudioHwRemote(c_hidSendData, cc_mic_level, c_uac_vol, c_audiohw +#if HID_DFU_EN + , c_dfu +#endif + ); + } + } + } +#if EQ_EN == 1 + on tile[0] : { + { + +#if HID_DFU_EN + unsafe { + uc_dfu = (streaming chanend)c_dfu; + } +#endif + + dsp_core0(); + } + } + on tile[0] : { dsp_main(c_eq_data); } +#endif + +#if DNR_ENABLE == 1 + on tile[0] : { dnr_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 + on tile[XUD_TILE]: + par + { +#if XUA_USB_EN +#if ((XUD_TILE == 0) && (XUA_DFU_EN == 1)) + /* Check if USB is on the flash tile (tile 0) */ + /* Expect to be distrbuted into XUA_Endpoint0() */ + [[distribute]] + DFUHandler(dfuInterface, null); +#endif + + /* Core USB task, buffering, USB etc */ + { +#ifdef XUD_PRIORITY_HIGH + set_core_high_priority_on(); +#endif + /* Run UAC2.0 at high-speed, UAC1.0 at full-speed */ + unsigned usbSpeed = (AUDIO_CLASS == 2) ? XUD_SPEED_HS : XUD_SPEED_FS; + + unsigned xudPwrCfg = (XUA_POWERMODE == XUA_POWERMODE_SELF) ? XUD_PWR_SELF : XUD_PWR_BUS; + + /* USB interface core */ + XUD_Main(c_xud_out, ENDPOINT_COUNT_OUT, c_xud_in, ENDPOINT_COUNT_IN, + c_sof, epTypeTableOut, epTypeTableIn, usbSpeed, xudPwrCfg); + } + +#if (NUM_USB_CHAN_OUT > 0) || (NUM_USB_CHAN_IN > 0) || XUA_HID_ENABLED || defined(MIDI) + /* Core USB audio task, buffering, USB etc */ + { + unsigned x; + thread_speed(); + + /* Attach mclk count port to mclk clock-block (for feedback) */ + //set_port_clock(p_for_mclk_count, clk_audio_mclk); +#if(AUDIO_IO_TILE != XUD_TILE) + set_clock_src(clk_audio_mclk_usb, p_mclk_in_usb); + set_port_clock(p_for_mclk_count, clk_audio_mclk_usb); + start_clock(clk_audio_mclk_usb); +#else + /* Clock port from same clock-block as I2S */ + /* TODO remove asm() */ + asm("ldw %0, dp[clk_audio_mclk]":"=r"(x)); + asm("setclk res[%0], %1"::"r"(p_for_mclk_count), "r"(x)); +#endif + /* Endpoint & audio buffering cores - buffers all EP's other than 0 */ + XUA_Buffer( +#if (NUM_USB_CHAN_OUT > 0) + c_xud_out[ENDPOINT_NUMBER_OUT_AUDIO], /* Audio Out*/ +#endif +#if (NUM_USB_CHAN_IN > 0) + c_xud_in[ENDPOINT_NUMBER_IN_AUDIO], /* Audio In */ +#endif +#if (NUM_USB_CHAN_OUT > 0) && ((NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)) + c_xud_in[ENDPOINT_NUMBER_IN_FEEDBACK], /* Audio FB */ +#endif +#ifdef MIDI + c_xud_out[ENDPOINT_NUMBER_OUT_MIDI], /* MIDI Out */ // 2 + c_xud_in[ENDPOINT_NUMBER_IN_MIDI], /* MIDI In */ // 4 + c_midi, +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + /* Audio Interrupt - only used for interrupts on external clock change */ + c_xud_in[ENDPOINT_NUMBER_IN_INTERRUPT], + c_clk_int, +#endif + c_sof, c_aud_ctl, p_for_mclk_count +#if (XUA_HID_ENABLED) + , c_xud_in[ENDPOINT_NUMBER_IN_HID] +#endif + , c_mix_out +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , c_audio_rate_change + #if (!XUA_USE_SW_PLL) + , i_pll_ref + #else + , c_sw_pll + #endif +#endif + ); + //: + } +#endif + + /* Endpoint 0 Core */ + { + thread_speed(); +#if (USE_EX3D == 1) && (HID_CONTROLS > 0) + SetEx3dHidChan(c_ex3d_hid_cmd); + XUA_Endpoint0( c_xud_out[0], c_xud_in[0], c_hidRcvData, c_aud_ctl, c_mix_ctl, c_clk_ctl, c_EANativeTransport_ctrl, dfuInterface VENDOR_REQUESTS_PARAMS_); +#else + XUA_Endpoint0( c_xud_out[0], c_xud_in[0], c_aud_ctl, c_mix_ctl, c_clk_ctl, c_EANativeTransport_ctrl, dfuInterface VENDOR_REQUESTS_PARAMS_); +#endif + } + +#endif /* XUA_USB_EN */ + } + +#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + on tile[AUDIO_IO_TILE]: sw_pll_task(c_sw_pll); +#endif + + on tile[AUDIO_IO_TILE]: + { + /* Audio I/O task, includes mixing etc */ + usb_audio_io( +#if (NUM_USB_CHAN_OUT > 0) || (NUM_USB_CHAN_IN > 0) + /* Connect audio system to XUA_Buffer(); */ + c_mix_out +#else + /* Connect to XUA_Endpoint0() */ + c_aud_ctl +#endif +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + , c_spdif_tx +#endif +#if (MIXER) + , c_mix_ctl +#endif + , c_spdif_rx, c_adat_rx, c_clk_ctl, c_clk_int +#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) + , dfuInterface +#endif +#if (XUA_NUM_PDM_MICS > 0) + , c_pdm_pcm +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , i_pll_ref +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , c_audio_rate_change +#endif +#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + , p_for_mclk_count_audio + , c_sw_pll +#endif + ); + } + //: + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + on tile[SPDIF_TX_TILE]: + { + thread_speed(); + SpdifTxWrapper(c_spdif_tx); + } +#endif + +#if defined(MIDI) && defined(IAP) && (IAP_TILE == MIDI_TILE) + /* MIDI and IAP share a core */ + on tile[IAP_TILE]: + { + thread_speed(); + usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0, c_iap, null, null, null); + } +#else +#if defined(MIDI) + /* MIDI core */ + on tile[MIDI_TILE]: + { + thread_speed(); + usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0); + } +#endif +#if defined(IAP) + on tile[IAP_TILE]: + { + thread_speed(); + iAP(c_iap, null, null, null); + } +#endif +#endif + +#if (XUA_SPDIF_RX_EN) + on tile[XUD_TILE]: + { + thread_speed(); + spdif_rx(c_spdif_rx, p_spdif_rx, clk_spd_rx, 192000); + } +#endif + +#if (XUA_ADAT_RX_EN) + on stdcore[XUD_TILE] : + { + set_thread_fast_mode_on(); + + while (1) + { + adatReceiver48000(p_adat_rx, c_adat_rx); + adatReceiver44100(p_adat_rx, c_adat_rx); + } + } +#endif + + +#if XUA_USB_EN +#if (XUD_TILE != 0) && (AUDIO_IO_TILE != 0) && (XUA_DFU_EN == 1) + /* Run flash code on its own - hope it gets combined */ + //#warning Running DFU flash code on its own + on stdcore[0]: DFUHandler(dfuInterface, null); +#endif +#endif + +#if (XUA_NUM_PDM_MICS > 0) + /* PDM Mics running on a separate to AudioHub */ + on stdcore[PDM_TILE]: + { + mic_array_task(c_mic_pcm); + } +#endif /*XUA_NUM_PDM_MICS > 0*/ + } + + return 0; +} + +#endif diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc.gemini b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc.gemini new file mode 100644 index 0000000..d1d2184 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc.gemini @@ -0,0 +1,844 @@ +// Copyright 2012-2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#include "xua.h" /* Device specific defines */ +#ifdef EXCLUDE_USB_AUDIO_MAIN + +/** + * @file main.xc + * @brief Top level for XMOS USB 2.0 Audio 2.0 Reference Designs. + * @author Ross Owen, XMOS Semiconductor Ltd + */ +#include +#include +#include +#include +#include +#ifdef XSCOPE +#include +#endif + +#if XUA_USB_EN +#include "xud_device.h" /* XMOS USB Device Layer defines and functions */ +#include "xua_endpoint0.h" +#endif + +#include "uac_hwresources.h" + +#ifdef IAP +#include "i2c_shared.h" +#include "iap.h" +#endif + +#if (XUA_SPDIF_RX_EN || XUA_SPDIF_TX_EN) +#include "spdif.h" /* From lib_spdif */ +#endif + +#if (XUA_ADAT_RX_EN) +#include "adat_rx.h" +#endif + +#if (XUA_NUM_PDM_MICS > 0) +#include "xua_pdm_mic.h" +#endif + +#if (XUA_DFU_EN == 1) +[[distributable]] +void DFUHandler(server interface i_dfu i, chanend ?c_user_cmd); +#endif + +/* Audio I/O - Port declarations */ +#if I2S_WIRES_DAC > 0 +on tile[AUDIO_IO_TILE] : buffered out port:32 p_i2s_dac[I2S_WIRES_DAC] = + {PORT_I2S_DAC0, +#endif +#if I2S_WIRES_DAC > 1 + PORT_I2S_DAC1, +#endif +#if I2S_WIRES_DAC > 2 + PORT_I2S_DAC2, +#endif +#if I2S_WIRES_DAC > 3 + PORT_I2S_DAC3, +#endif +#if I2S_WIRES_DAC > 4 + PORT_I2S_DAC4, +#endif +#if I2S_WIRES_DAC > 5 + PORT_I2S_DAC5, +#endif +#if I2S_WIRES_DAC > 6 + PORT_I2S_DAC6, +#endif +#if I2S_WIRES_DAC > 7 +#error I2S_WIRES_DAC value is too large! +#endif +#if I2S_WIRES_DAC > 0 + }; +#else + #define p_i2s_dac null +#endif + +#if I2S_WIRES_ADC > 0 +on tile[AUDIO_IO_TILE] : buffered in port:32 p_i2s_adc[I2S_WIRES_ADC] = + {PORT_I2S_ADC0, +#endif +#if I2S_WIRES_ADC > 1 + PORT_I2S_ADC1, +#endif +#if I2S_WIRES_ADC > 2 + PORT_I2S_ADC2, +#endif +#if I2S_WIRES_ADC > 3 + PORT_I2S_ADC3, +#endif +#if I2S_WIRES_ADC > 4 + PORT_I2S_ADC4, +#endif +#if I2S_WIRES_ADC > 5 + PORT_I2S_ADC5, +#endif +#if I2S_WIRES_ADC > 6 + PORT_I2S_ADC6, +#endif +#if I2S_WIRES_ADC > 7 +#error I2S_WIRES_ADC value is too large! +#endif +#if I2S_WIRES_ADC > 0 + }; +#else + #define p_i2s_adc null +#endif + + +#if CODEC_MASTER +on tile[AUDIO_IO_TILE] : buffered in port:32 p_lrclk = PORT_I2S_LRCLK; +on tile[AUDIO_IO_TILE] : buffered in port:32 p_bclk = PORT_I2S_BCLK; +#else +on tile[AUDIO_IO_TILE] : buffered out port:32 p_lrclk = PORT_I2S_LRCLK; +on tile[AUDIO_IO_TILE] : buffered out port:32 p_bclk = PORT_I2S_BCLK; +#endif + +#if (!CODEC_MASTER) || XUA_SPDIF_TX_EN || XUA_ADAT_TX_EN || ((AUDIO_IO_TILE == XUD_TILE) && XUA_USB_EN) +/* Audio master clock input */ +on tile[AUDIO_IO_TILE] : in port p_mclk_in = PORT_MCLK_IN; +#else +#define p_mclk_in null +#endif + +#if (AUDIO_IO_TILE != XUD_TILE) && XUA_USB_EN +/* If audio I/O and USB running on different tiles we need a separate port for + * the master clock input (to use for USB async feedback calculation) */ +on tile[XUD_TILE] : in port p_mclk_in_usb = PORT_MCLK_IN_USB; +#endif + +#if XUA_USB_EN +on tile[XUD_TILE] : in port p_for_mclk_count = PORT_MCLK_COUNT; +#endif + +#if (XUA_SPDIF_TX_EN) +on tile[SPDIF_TX_TILE] : buffered out port:32 p_spdif_tx = PORT_SPDIF_OUT; +#endif + +#if (XUA_ADAT_TX_EN) +on stdcore[AUDIO_IO_TILE] : buffered out port:32 p_adat_tx = PORT_ADAT_OUT; +#endif + +#if (XUA_ADAT_RX_EN) +on stdcore[XUD_TILE] : buffered in port:32 p_adat_rx = PORT_ADAT_IN; +#endif + +#if (XUA_SPDIF_RX_EN) +on tile[XUD_TILE] : in port p_spdif_rx = PORT_SPDIF_IN; +#endif + +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) || (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) +/* Reference to external clock multiplier */ +on tile[PLL_REF_TILE] : out port p_pll_ref = PORT_PLL_REF; +#ifdef __XS3A__ +on tile[AUDIO_IO_TILE] : port p_for_mclk_count_audio = PORT_MCLK_COUNT_2; +#else /* __XS3A__ */ +#define p_for_mclk_count_audio null +#endif /* __XS3A__ */ +#endif + +#ifdef MIDI +on tile[MIDI_TILE] : port p_midi_tx = PORT_MIDI_OUT; + +#if(MIDI_RX_PORT_WIDTH == 4) +on tile[MIDI_TILE] : buffered in port:4 p_midi_rx = PORT_MIDI_IN; +#elif(MIDI_RX_PORT_WIDTH == 1) +on tile[MIDI_TILE] : buffered in port:1 p_midi_rx = PORT_MIDI_IN; +#endif +#endif + + +#ifdef MIDI +on tile[MIDI_TILE] : clock clk_midi = CLKBLK_MIDI; +#endif + +#if (XUA_SPDIF_TX_EN || XUA_ADAT_TX_EN) +on tile[SPDIF_TX_TILE] : clock clk_mst_spd = CLKBLK_SPDIF_TX; +#endif + +#if (XUA_SPDIF_RX_EN) +on tile[XUD_TILE] : clock clk_spd_rx = CLKBLK_SPDIF_RX; +#endif + +on tile[AUDIO_IO_TILE] : clock clk_audio_mclk = CLKBLK_MCLK; /* Master clock */ + +#if (AUDIO_IO_TILE != XUD_TILE) && XUA_USB_EN +/* Separate clock/port for USB feedback calculation */ +on tile[XUD_TILE] : clock clk_audio_mclk_usb = CLKBLK_MCLK; /* Master clock */ +#endif + +on tile[AUDIO_IO_TILE] : clock clk_audio_bclk = CLKBLK_I2S_BIT; /* Bit clock */ + +#ifdef IAP +/* I2C ports - in a struct for use with module_i2c_shared & module_i2c_simple/module_i2c_single_port */ +#ifdef PORT_I2C +on tile [IAP_TILE] : struct r_i2c r_i2c = {PORT_I2C}; +#else +on tile [IAP_TILE] : struct r_i2c r_i2c = {PORT_I2C_SCL, PORT_I2C_SDA}; +#endif +#endif + +#if XUA_USB_EN +/* Endpoint type tables for XUD */ +XUD_EpType epTypeTableOut[ENDPOINT_COUNT_OUT] = { XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, +#if (NUM_USB_CHAN_IN > 0) + XUD_EPTYPE_ISO, /* Audio */ +#endif +#ifdef MIDI + XUD_EPTYPE_BUL, /* MIDI */ +#endif +#if HID_OUT_REQUIRED + XUD_EPTYPE_INT, +#endif +#ifdef IAP + XUD_EPTYPE_BUL, /* iAP */ +#ifdef IAP_EA_NATIVE_TRANS + XUD_EPTYPE_BUL, /* EA Native Transport */ +#endif +#endif + }; + +XUD_EpType epTypeTableIn[ENDPOINT_COUNT_IN] = { XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, +#if (NUM_USB_CHAN_IN > 0) + XUD_EPTYPE_ISO, +#endif +#if (NUM_USB_CHAN_OUT > 0) && ((NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)) + XUD_EPTYPE_ISO, /* Async feedback endpoint */ +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + XUD_EPTYPE_INT, +#endif +#ifdef MIDI + XUD_EPTYPE_BUL, +#endif +#if XUA_OR_STATIC_HID_ENABLED + XUD_EPTYPE_INT, +#endif +#ifdef IAP + XUD_EPTYPE_BUL | XUD_STATUS_ENABLE, +#ifdef IAP_INT_EP + XUD_EPTYPE_BUL | XUD_STATUS_ENABLE, +#endif +#ifdef IAP_EA_NATIVE_TRANS + XUD_EPTYPE_BUL | XUD_STATUS_ENABLE, +#endif +#endif + }; +#endif /* XUA_USB_EN */ + +void thread_speed() +{ +#ifdef FAST_MODE +#warning Building with fast mode enabled + set_thread_fast_mode_on(); +#else + set_thread_fast_mode_off(); +#endif +} + +#ifdef XSCOPE +void xscope_user_init() +{ + xscope_register(0, 0, "", 0, ""); + + xscope_config_io(XSCOPE_IO_BASIC); +} +#endif + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) +void SpdifTxWrapper(chanend c_spdif_tx) +{ + unsigned portId; + //configure_clock_src(clk, p_mclk); + + // TODO could share clock block here.. + // NOTE, Assuming SPDIF tile == USB tile here.. + asm("ldw %0, dp[p_mclk_in_usb]":"=r"(portId)); + asm("setclk res[%0], %1"::"r"(clk_mst_spd), "r"(portId)); + configure_out_port_no_ready(p_spdif_tx, clk_mst_spd, 0); + set_clock_fall_delay(clk_mst_spd, 7); + start_clock(clk_mst_spd); + + while(1) + { + spdif_tx(p_spdif_tx, c_spdif_tx); + } +} +#endif + +void usb_audio_io(chanend ?c_aud_in, +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + chanend c_spdif_tx, +#endif +#if (MIXER) + chanend c_mix_ctl, +#endif + streaming chanend ?c_spdif_rx, + streaming chanend ?c_adat_rx, + chanend ?c_clk_ctl, + chanend ?c_clk_int +#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) + , server interface i_dfu ?dfuInterface +#endif +#if (XUA_NUM_PDM_MICS > 0) + , chanend c_pdm_pcm +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , client interface pll_ref_if i_pll_ref +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , chanend c_audio_rate_change +#endif +#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + , port p_for_mclk_count_aud + , chanend c_sw_pll +#endif +) +{ +#if (MIXER) + chan c_mix_out; +#endif + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + chan c_dig_rx; + chan c_audio_rate_change; /* Notification of new mclk freq to clockgen and synch */ +#if XUA_USE_SW_PLL + /* Connect p_for_mclk_count_aud to clk_audio_mclk so we can count mclks/timestamp in digital rx*/ + unsigned x = 0; + asm("ldw %0, dp[clk_audio_mclk]":"=r"(x)); + asm("setclk res[%0], %1"::"r"(p_for_mclk_count_aud), "r"(x)); +#endif /* XUA_USE_SW_PLL */ +#endif /* (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) */ + + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE == AUDIO_IO_TILE) + chan c_spdif_tx; + + /* Setup S/PDIF tx port - note this is done before par since sharing clock-block/port */ + spdif_tx_port_config(p_spdif_tx, clk_audio_mclk, p_mclk_in, 7); +#endif + + par + { +#if (MIXER && XUA_USB_EN) + /* Mixer cores(s) */ + { + thread_speed(); + mixer(c_aud_in, c_mix_out, c_mix_ctl); + } +#endif + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE == AUDIO_IO_TILE) + while(1) + { + spdif_tx(p_spdif_tx, c_spdif_tx); + } +#endif + + /* Audio I/O core (pars additional S/PDIF TX Core) */ + { + thread_speed(); +#if (MIXER) +#define AUDIO_CHANNEL c_mix_out +#else +#define AUDIO_CHANNEL c_aud_in +#endif + XUA_AudioHub(AUDIO_CHANNEL, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc +#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE) + , c_spdif_tx +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , c_dig_rx +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , c_audio_rate_change +#endif +#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) + , dfuInterface +#endif +#if (XUA_NUM_PDM_MICS > 0) + , c_pdm_pcm +#endif + ); + } + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + { + /* ClockGen must currently run on same tile as AudioHub due to shared memory buffer + * However, due to the use of an interface the pll reference signal port can be on another tile + */ + thread_speed(); + clockGen( c_spdif_rx, + c_adat_rx, + i_pll_ref, + c_dig_rx, + c_clk_ctl, + c_clk_int, + c_audio_rate_change +#if XUA_USE_SW_PLL + , p_for_mclk_count_aud + , c_sw_pll +#endif + ); + } +#endif + + } // par +} + +#ifndef USER_MAIN_DECLARATIONS +#define USER_MAIN_DECLARATIONS +#endif + +#ifndef USER_MAIN_CORES +#define USER_MAIN_CORES +#endif + +//extern unsafe client interface i2c_master_if i_i2c_client; +//extern unsafe client interface i2c_master_if i_i2c_client_t0; +extern void dsp_core0(void); +extern void board_setup(); +extern void dsp_main (streaming chanend c_data); +extern void SetEqDataChan (streaming chanend c); +extern void SetUbmInitChan (streaming chanend c); +extern void UserBufferForwardingTask(streaming chanend c_dsp, streaming chanend c_init); +extern void SetEx3dHidChan (chanend c); + +extern int dsp_worker_tile(chanend c_dsp_to_ex3d, int worker_id); +//extern int dsp_worker_tile_1(chanend c_dsp_to_ex3d, int worker_id); +extern void ex3d_task(); +extern void hid_button_task(chanend cc_mic_level, chanend c_hid, chanend c_hidSendData, chanend c_uac_vol, chanend c_ex3d_hid_cmd); +#if HID_DFU_EN +extern void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx, streaming chanend c_dfu_rx, chanend c_mic_det); +#else +extern void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx, chanend c_mic_det); +#endif +extern void dnr_dsp_proc_task(void); + +extern unsafe chanend uc_dsp_to_ex3d[DSP_WORKER_COUNT]; +extern unsafe chanend uc_dsp_to_dnr_t1; +extern unsafe chanend uc_key_to_ubm_t0; +extern unsafe chanend uc_audiohw; +#if HID_DFU_EN +extern unsafe streaming chanend uc_dfu; +#endif +extern void key_sender(chanend c_key); +extern void key_receiver(chanend c_key); +extern void mute_handler(chanend c_mic_det); + + +/* Main for USB Audio Applications */ +int main() +{ +#if !XUA_USB_EN + #define c_mix_out null +#else + chan c_mix_out; +#endif + +#ifdef MIDI + chan c_midi; +#endif +#ifdef IAP + chan c_iap; +#ifdef IAP_EA_NATIVE_TRANS + chan c_ea_data; +#endif +#endif + +#if (MIXER) + chan c_mix_ctl; +#endif + +#if (XUA_SPDIF_RX_EN) + streaming chan c_spdif_rx; +#else +#define c_spdif_rx null +#endif + +#if (XUA_ADAT_RX_EN) + streaming chan c_adat_rx; +#else +#define c_adat_rx null +#endif + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + chan c_spdif_tx; +#endif + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + chan c_clk_ctl; + chan c_clk_int; +#else +#define c_clk_int null +#define c_clk_ctl null +#endif + +#if (XUA_DFU_EN == 1) + interface i_dfu dfuInterface; +#else + #define dfuInterface null +#endif + +#if (XUA_NUM_PDM_MICS > 0) + chan c_pdm_pcm; +#endif + +#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC && !XUA_USE_SW_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) ) + interface pll_ref_if i_pll_ref; +#endif + +#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + chan c_sw_pll; +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + chan c_audio_rate_change; /* Notification of new mclk freq to ep_buffer */ +#endif + chan c_sof; + chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */ + chan c_xud_in[ENDPOINT_COUNT_IN]; + + /* Used to communicate controls/setting from XUA_Endpoint0() to the Audio/Buffering sub-system */ + chan c_aud_ctl; + +#if (!MIXER) +#define c_mix_ctl null +#endif + +#ifdef IAP_EA_NATIVE_TRANS + chan c_EANativeTransport_ctrl; +#else +#define c_EANativeTransport_ctrl null +#endif + +//#if (HID_CONTROLS > 0) +// chan c_hid; +//#endif + + USER_MAIN_DECLARATIONS + + chan c_dsp_to_ex3d[DSP_WORKER_COUNT]; + chan cc_mic_level; + chan c_audiohw; +#if HID_DFU_EN + streaming chan c_dfu; +#endif + chan c_key; chan c_hidSendData; + chan c_hidRcvData; + streaming chan c_eq_data; + streaming chan c_ubm_init; + chan c_uac_vol; + chan c_ex3d_hid_cmd; + chan c_mic_det; + + par + { + USER_MAIN_CORES + on tile[1] : { + par { + // Forwarder Task on its own core + UserBufferForwardingTask(c_eq_data, c_ubm_init); + + // HID & Control sequence on its own core + unsafe { + uc_audiohw = (chanend)c_audiohw; + SetUbmInitChan(c_ubm_init); + hid_button_task(cc_mic_level, c_hidRcvData, c_hidSendData, c_uac_vol, c_ex3d_hid_cmd); + } + +#if USE_EX3D == 1 + // Workers on their own cores + par(int i = 0; i < DSP_WORKER_COUNT; i++) + dsp_worker_tile(c_dsp_to_ex3d[i], i); + + // EX3D Task sequence on its own core + unsafe { + delay_milliseconds(200); + key_sender(c_key); + for (int i = 0; i < DSP_WORKER_COUNT; i++) + uc_dsp_to_ex3d[i] = (chanend)c_dsp_to_ex3d[i]; + ex3d_task(); + } +#endif + } + } + on tile[1]: mute_handler(c_mic_det); + + on tile[0] : { + par { + { +#if USE_EX3D == 1 + unsafe { key_receiver(c_key); } +#endif + AudioHwRemote(c_hidSendData, cc_mic_level, c_uac_vol, c_audiohw +#if HID_DFU_EN + , c_dfu +#endif + , c_mic_det + ); + } + } + } +#if EQ_EN == 1 + on tile[0] : { + { + +#if HID_DFU_EN + unsafe { + uc_dfu = (streaming chanend)c_dfu; + } +#endif + + dsp_core0(); + } + } + on tile[0] : { dsp_main(c_eq_data); } +#endif + +#if DNR_ENABLE == 1 + on tile[0] : { dnr_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 + on tile[XUD_TILE]: + par + { +#if XUA_USB_EN +#if ((XUD_TILE == 0) && (XUA_DFU_EN == 1)) + /* Check if USB is on the flash tile (tile 0) */ + /* Expect to be distrbuted into XUA_Endpoint0() */ + [[distribute]] + DFUHandler(dfuInterface, null); +#endif + + /* Core USB task, buffering, USB etc */ + { +#ifdef XUD_PRIORITY_HIGH + set_core_high_priority_on(); +#endif + /* Run UAC2.0 at high-speed, UAC1.0 at full-speed */ + unsigned usbSpeed = (AUDIO_CLASS == 2) ? XUD_SPEED_HS : XUD_SPEED_FS; + + unsigned xudPwrCfg = (XUA_POWERMODE == XUA_POWERMODE_SELF) ? XUD_PWR_SELF : XUD_PWR_BUS; + + /* USB interface core */ + XUD_Main(c_xud_out, ENDPOINT_COUNT_OUT, c_xud_in, ENDPOINT_COUNT_IN, + c_sof, epTypeTableOut, epTypeTableIn, usbSpeed, xudPwrCfg); + } + +#if (NUM_USB_CHAN_OUT > 0) || (NUM_USB_CHAN_IN > 0) || XUA_HID_ENABLED || defined(MIDI) + /* Core USB audio task, buffering, USB etc */ + { + unsigned x; + thread_speed(); + + /* Attach mclk count port to mclk clock-block (for feedback) */ + //set_port_clock(p_for_mclk_count, clk_audio_mclk); +#if(AUDIO_IO_TILE != XUD_TILE) + set_clock_src(clk_audio_mclk_usb, p_mclk_in_usb); + set_port_clock(p_for_mclk_count, clk_audio_mclk_usb); + start_clock(clk_audio_mclk_usb); +#else + /* Clock port from same clock-block as I2S */ + /* TODO remove asm() */ + asm("ldw %0, dp[clk_audio_mclk]":"=r"(x)); + asm("setclk res[%0], %1"::"r"(p_for_mclk_count), "r"(x)); +#endif + /* Endpoint & audio buffering cores - buffers all EP's other than 0 */ + XUA_Buffer( +#if (NUM_USB_CHAN_OUT > 0) + c_xud_out[ENDPOINT_NUMBER_OUT_AUDIO], /* Audio Out*/ +#endif +#if (NUM_USB_CHAN_IN > 0) + c_xud_in[ENDPOINT_NUMBER_IN_AUDIO], /* Audio In */ +#endif +#if (NUM_USB_CHAN_OUT > 0) && ((NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)) + c_xud_in[ENDPOINT_NUMBER_IN_FEEDBACK], /* Audio FB */ +#endif +#ifdef MIDI + c_xud_out[ENDPOINT_NUMBER_OUT_MIDI], /* MIDI Out */ // 2 + c_xud_in[ENDPOINT_NUMBER_IN_MIDI], /* MIDI In */ // 4 + c_midi, +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + /* Audio Interrupt - only used for interrupts on external clock change */ + c_xud_in[ENDPOINT_NUMBER_IN_INTERRUPT], + c_clk_int, +#endif + c_sof, c_aud_ctl, p_for_mclk_count +#if (XUA_HID_ENABLED) + , c_xud_in[ENDPOINT_NUMBER_IN_HID] +#endif + , c_mix_out +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , c_audio_rate_change + #if (!XUA_USE_SW_PLL) + , i_pll_ref + #else + , c_sw_pll + #endif +#endif + ); + //: + } +#endif + + /* Endpoint 0 Core */ + { + thread_speed(); +#if (USE_EX3D == 1) && (HID_CONTROLS > 0) + SetEx3dHidChan(c_ex3d_hid_cmd); + XUA_Endpoint0( c_xud_out[0], c_xud_in[0], c_hidRcvData, c_aud_ctl, c_mix_ctl, c_clk_ctl, c_EANativeTransport_ctrl, dfuInterface VENDOR_REQUESTS_PARAMS_); +#else + XUA_Endpoint0( c_xud_out[0], c_xud_in[0], c_aud_ctl, c_mix_ctl, c_clk_ctl, c_EANativeTransport_ctrl, dfuInterface VENDOR_REQUESTS_PARAMS_); +#endif + } + +#endif /* XUA_USB_EN */ + } + +#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + on tile[AUDIO_IO_TILE]: sw_pll_task(c_sw_pll); +#endif + + on tile[AUDIO_IO_TILE]: + { + set_core_high_priority_on(); + /* Audio I/O task, includes mixing etc */ + usb_audio_io( +#if (NUM_USB_CHAN_OUT > 0) || (NUM_USB_CHAN_IN > 0) + /* Connect audio system to XUA_Buffer(); */ + c_mix_out +#else + /* Connect to XUA_Endpoint0() */ + c_aud_ctl +#endif +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + , c_spdif_tx +#endif +#if (MIXER) + , c_mix_ctl +#endif + , c_spdif_rx, c_adat_rx, c_clk_ctl, c_clk_int +#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) + , dfuInterface +#endif +#if (XUA_NUM_PDM_MICS > 0) + , c_pdm_pcm +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , i_pll_ref +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , c_audio_rate_change +#endif +#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + , p_for_mclk_count_audio + , c_sw_pll +#endif + ); + } + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + on tile[SPDIF_TX_TILE]: + { + thread_speed(); + SpdifTxWrapper(c_spdif_tx); + } +#endif + +#if defined(MIDI) && defined(IAP) && (IAP_TILE == MIDI_TILE) + /* MIDI and IAP share a core */ + on tile[IAP_TILE]: + { + thread_speed(); + usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0, c_iap, null, null, null); + } +#else +#if defined(MIDI) + /* MIDI core */ + on tile[MIDI_TILE]: + { + thread_speed(); + usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0); + } +#endif +#if defined(IAP) + on tile[IAP_TILE]: + { + thread_speed(); + iAP(c_iap, null, null, null); + } +#endif +#endif + +#if (XUA_SPDIF_RX_EN) + on tile[XUD_TILE]: + { + thread_speed(); + spdif_rx(c_spdif_rx, p_spdif_rx, clk_spd_rx, 192000); + } +#endif + +#if (XUA_ADAT_RX_EN) + on stdcore[XUD_TILE] : + { + set_thread_fast_mode_on(); + + while (1) + { + adatReceiver48000(p_adat_rx, c_adat_rx); + adatReceiver44100(p_adat_rx, c_adat_rx); + } + } +#endif + + +#if XUA_USB_EN +#if (XUD_TILE != 0) && (AUDIO_IO_TILE != 0) && (XUA_DFU_EN == 1) + /* Run flash code on its own - hope it gets combined */ + //#warning Running DFU flash code on its own + on stdcore[0]: DFUHandler(dfuInterface, null); +#endif +#endif + +#if (XUA_NUM_PDM_MICS > 0) + /* PDM Mics running on a separate to AudioHub */ + on stdcore[PDM_TILE]: + { + mic_array_task(c_mic_pcm); + } +#endif /*XUA_NUM_PDM_MICS > 0*/ + } + + return 0; +} + +#endif diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc.gemini1 b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc.gemini1 new file mode 100644 index 0000000..c5d664e --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/main.xc.gemini1 @@ -0,0 +1,846 @@ +// Copyright 2012-2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. + +#include "xua.h" /* Device specific defines */ +#ifdef EXCLUDE_USB_AUDIO_MAIN + +/** + * @file main.xc + * @brief Top level for XMOS USB 2.0 Audio 2.0 Reference Designs. + * @author Ross Owen, XMOS Semiconductor Ltd + */ +#include +#include +#include +#include +#include +#ifdef XSCOPE +#include +#endif + +#if XUA_USB_EN +#include "xud_device.h" /* XMOS USB Device Layer defines and functions */ +#include "xua_endpoint0.h" +#endif + +#include "uac_hwresources.h" + +#ifdef IAP +#include "i2c_shared.h" +#include "iap.h" +#endif + +#if (XUA_SPDIF_RX_EN || XUA_SPDIF_TX_EN) +#include "spdif.h" /* From lib_spdif */ +#endif + +#if (XUA_ADAT_RX_EN) +#include "adat_rx.h" +#endif + +#if (XUA_NUM_PDM_MICS > 0) +#include "xua_pdm_mic.h" +#endif + +#if (XUA_DFU_EN == 1) +[[distributable]] +void DFUHandler(server interface i_dfu i, chanend ?c_user_cmd); +#endif + +/* Audio I/O - Port declarations */ +#if I2S_WIRES_DAC > 0 +on tile[AUDIO_IO_TILE] : buffered out port:32 p_i2s_dac[I2S_WIRES_DAC] = + {PORT_I2S_DAC0, +#endif +#if I2S_WIRES_DAC > 1 + PORT_I2S_DAC1, +#endif +#if I2S_WIRES_DAC > 2 + PORT_I2S_DAC2, +#endif +#if I2S_WIRES_DAC > 3 + PORT_I2S_DAC3, +#endif +#if I2S_WIRES_DAC > 4 + PORT_I2S_DAC4, +#endif +#if I2S_WIRES_DAC > 5 + PORT_I2S_DAC5, +#endif +#if I2S_WIRES_DAC > 6 + PORT_I2S_DAC6, +#endif +#if I2S_WIRES_DAC > 7 +#error I2S_WIRES_DAC value is too large! +#endif +#if I2S_WIRES_DAC > 0 + }; +#else + #define p_i2s_dac null +#endif + +#if I2S_WIRES_ADC > 0 +on tile[AUDIO_IO_TILE] : buffered in port:32 p_i2s_adc[I2S_WIRES_ADC] = + {PORT_I2S_ADC0, +#endif +#if I2S_WIRES_ADC > 1 + PORT_I2S_ADC1, +#endif +#if I2S_WIRES_ADC > 2 + PORT_I2S_ADC2, +#endif +#if I2S_WIRES_ADC > 3 + PORT_I2S_ADC3, +#endif +#if I2S_WIRES_ADC > 4 + PORT_I2S_ADC4, +#endif +#if I2S_WIRES_ADC > 5 + PORT_I2S_ADC5, +#endif +#if I2S_WIRES_ADC > 6 + PORT_I2S_ADC6, +#endif +#if I2S_WIRES_ADC > 7 +#error I2S_WIRES_ADC value is too large! +#endif +#if I2S_WIRES_ADC > 0 + }; +#else + #define p_i2s_adc null +#endif + + +#if CODEC_MASTER +on tile[AUDIO_IO_TILE] : buffered in port:32 p_lrclk = PORT_I2S_LRCLK; +on tile[AUDIO_IO_TILE] : buffered in port:32 p_bclk = PORT_I2S_BCLK; +#else +on tile[AUDIO_IO_TILE] : buffered out port:32 p_lrclk = PORT_I2S_LRCLK; +on tile[AUDIO_IO_TILE] : buffered out port:32 p_bclk = PORT_I2S_BCLK; +#endif + +#if (!CODEC_MASTER) || XUA_SPDIF_TX_EN || XUA_ADAT_TX_EN || ((AUDIO_IO_TILE == XUD_TILE) && XUA_USB_EN) +/* Audio master clock input */ +on tile[AUDIO_IO_TILE] : in port p_mclk_in = PORT_MCLK_IN; +#else +#define p_mclk_in null +#endif + +#if (AUDIO_IO_TILE != XUD_TILE) && XUA_USB_EN +/* If audio I/O and USB running on different tiles we need a separate port for + * the master clock input (to use for USB async feedback calculation) */ +on tile[XUD_TILE] : in port p_mclk_in_usb = PORT_MCLK_IN_USB; +#endif + +#if XUA_USB_EN +on tile[XUD_TILE] : in port p_for_mclk_count = PORT_MCLK_COUNT; +#endif + +#if (XUA_SPDIF_TX_EN) +on tile[SPDIF_TX_TILE] : buffered out port:32 p_spdif_tx = PORT_SPDIF_OUT; +#endif + +#if (XUA_ADAT_TX_EN) +on stdcore[AUDIO_IO_TILE] : buffered out port:32 p_adat_tx = PORT_ADAT_OUT; +#endif + +#if (XUA_ADAT_RX_EN) +on stdcore[XUD_TILE] : buffered in port:32 p_adat_rx = PORT_ADAT_IN; +#endif + +#if (XUA_SPDIF_RX_EN) +on tile[XUD_TILE] : in port p_spdif_rx = PORT_SPDIF_IN; +#endif + +#if (XUA_SPDIF_RX_EN) || (XUA_ADAT_RX_EN) || (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) +/* Reference to external clock multiplier */ +on tile[PLL_REF_TILE] : out port p_pll_ref = PORT_PLL_REF; +#ifdef __XS3A__ +on tile[AUDIO_IO_TILE] : port p_for_mclk_count_audio = PORT_MCLK_COUNT_2; +#else /* __XS3A__ */ +#define p_for_mclk_count_audio null +#endif /* __XS3A__ */ +#endif + +#ifdef MIDI +on tile[MIDI_TILE] : port p_midi_tx = PORT_MIDI_OUT; + +#if(MIDI_RX_PORT_WIDTH == 4) +on tile[MIDI_TILE] : buffered in port:4 p_midi_rx = PORT_MIDI_IN; +#elif(MIDI_RX_PORT_WIDTH == 1) +on tile[MIDI_TILE] : buffered in port:1 p_midi_rx = PORT_MIDI_IN; +#endif +#endif + + +#ifdef MIDI +on tile[MIDI_TILE] : clock clk_midi = CLKBLK_MIDI; +#endif + +#if (XUA_SPDIF_TX_EN || XUA_ADAT_TX_EN) +on tile[SPDIF_TX_TILE] : clock clk_mst_spd = CLKBLK_SPDIF_TX; +#endif + +#if (XUA_SPDIF_RX_EN) +on tile[XUD_TILE] : clock clk_spd_rx = CLKBLK_SPDIF_RX; +#endif + +on tile[AUDIO_IO_TILE] : clock clk_audio_mclk = CLKBLK_MCLK; /* Master clock */ + +#if (AUDIO_IO_TILE != XUD_TILE) && XUA_USB_EN +/* Separate clock/port for USB feedback calculation */ +on tile[XUD_TILE] : clock clk_audio_mclk_usb = CLKBLK_MCLK; /* Master clock */ +#endif + +on tile[AUDIO_IO_TILE] : clock clk_audio_bclk = CLKBLK_I2S_BIT; /* Bit clock */ + +#ifdef IAP +/* I2C ports - in a struct for use with module_i2c_shared & module_i2c_simple/module_i2c_single_port */ +#ifdef PORT_I2C +on tile [IAP_TILE] : struct r_i2c r_i2c = {PORT_I2C}; +#else +on tile [IAP_TILE] : struct r_i2c r_i2c = {PORT_I2C_SCL, PORT_I2C_SDA}; +#endif +#endif + +#if XUA_USB_EN +/* Endpoint type tables for XUD */ +XUD_EpType epTypeTableOut[ENDPOINT_COUNT_OUT] = { XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, +#if (NUM_USB_CHAN_IN > 0) + XUD_EPTYPE_ISO, /* Audio */ +#endif +#ifdef MIDI + XUD_EPTYPE_BUL, /* MIDI */ +#endif +#if HID_OUT_REQUIRED + XUD_EPTYPE_INT, +#endif +#ifdef IAP + XUD_EPTYPE_BUL, /* iAP */ +#ifdef IAP_EA_NATIVE_TRANS + XUD_EPTYPE_BUL, /* EA Native Transport */ +#endif +#endif + }; + +XUD_EpType epTypeTableIn[ENDPOINT_COUNT_IN] = { XUD_EPTYPE_CTL | XUD_STATUS_ENABLE, +#if (NUM_USB_CHAN_IN > 0) + XUD_EPTYPE_ISO, +#endif +#if (NUM_USB_CHAN_OUT > 0) && ((NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)) + XUD_EPTYPE_ISO, /* Async feedback endpoint */ +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + XUD_EPTYPE_INT, +#endif +#ifdef MIDI + XUD_EPTYPE_BUL, +#endif +#if XUA_OR_STATIC_HID_ENABLED + XUD_EPTYPE_INT, +#endif +#ifdef IAP + XUD_EPTYPE_BUL | XUD_STATUS_ENABLE, +#ifdef IAP_INT_EP + XUD_EPTYPE_BUL | XUD_STATUS_ENABLE, +#endif +#ifdef IAP_EA_NATIVE_TRANS + XUD_EPTYPE_BUL | XUD_STATUS_ENABLE, +#endif +#endif + }; +#endif /* XUA_USB_EN */ + +void thread_speed() +{ +#ifdef FAST_MODE +#warning Building with fast mode enabled + set_thread_fast_mode_on(); +#else + set_thread_fast_mode_off(); +#endif +} + +#ifdef XSCOPE +void xscope_user_init() +{ + xscope_register(0, 0, "", 0, ""); + + xscope_config_io(XSCOPE_IO_BASIC); +} +#endif + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) +void SpdifTxWrapper(chanend c_spdif_tx) +{ + unsigned portId; + //configure_clock_src(clk, p_mclk); + + // TODO could share clock block here.. + // NOTE, Assuming SPDIF tile == USB tile here.. + asm("ldw %0, dp[p_mclk_in_usb]":"=r"(portId)); + asm("setclk res[%0], %1"::"r"(clk_mst_spd), "r"(portId)); + configure_out_port_no_ready(p_spdif_tx, clk_mst_spd, 0); + set_clock_fall_delay(clk_mst_spd, 7); + start_clock(clk_mst_spd); + + while(1) + { + spdif_tx(p_spdif_tx, c_spdif_tx); + } +} +#endif + +void usb_audio_io(chanend ?c_aud_in, +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + chanend c_spdif_tx, +#endif +#if (MIXER) + chanend c_mix_ctl, +#endif + streaming chanend ?c_spdif_rx, + streaming chanend ?c_adat_rx, + chanend ?c_clk_ctl, + chanend ?c_clk_int +#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) + , server interface i_dfu ?dfuInterface +#endif +#if (XUA_NUM_PDM_MICS > 0) + , chanend c_pdm_pcm +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , client interface pll_ref_if i_pll_ref +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , chanend c_audio_rate_change +#endif +#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + , port p_for_mclk_count_aud + , chanend c_sw_pll +#endif +) +{ +#if (MIXER) + chan c_mix_out; +#endif + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + chan c_dig_rx; + chan c_audio_rate_change; /* Notification of new mclk freq to clockgen and synch */ +#if XUA_USE_SW_PLL + /* Connect p_for_mclk_count_aud to clk_audio_mclk so we can count mclks/timestamp in digital rx*/ + unsigned x = 0; + asm("ldw %0, dp[clk_audio_mclk]":"=r"(x)); + asm("setclk res[%0], %1"::"r"(p_for_mclk_count_aud), "r"(x)); +#endif /* XUA_USE_SW_PLL */ +#endif /* (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) */ + + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE == AUDIO_IO_TILE) + chan c_spdif_tx; + + /* Setup S/PDIF tx port - note this is done before par since sharing clock-block/port */ + spdif_tx_port_config(p_spdif_tx, clk_audio_mclk, p_mclk_in, 7); +#endif + + par + { +#if (MIXER && XUA_USB_EN) + /* Mixer cores(s) */ + { + thread_speed(); + mixer(c_aud_in, c_mix_out, c_mix_ctl); + } +#endif + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE == AUDIO_IO_TILE) + while(1) + { + spdif_tx(p_spdif_tx, c_spdif_tx); + } +#endif + + /* Audio I/O core (pars additional S/PDIF TX Core) */ + { + thread_speed(); +#if (MIXER) +#define AUDIO_CHANNEL c_mix_out +#else +#define AUDIO_CHANNEL c_aud_in +#endif + XUA_AudioHub(AUDIO_CHANNEL, clk_audio_mclk, clk_audio_bclk, p_mclk_in, p_lrclk, p_bclk, p_i2s_dac, p_i2s_adc +#if (XUA_SPDIF_TX_EN) //&& (SPDIF_TX_TILE != AUDIO_IO_TILE) + , c_spdif_tx +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , c_dig_rx +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , c_audio_rate_change +#endif +#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) + , dfuInterface +#endif +#if (XUA_NUM_PDM_MICS > 0) + , c_pdm_pcm +#endif + ); + } + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + { + /* ClockGen must currently run on same tile as AudioHub due to shared memory buffer + * However, due to the use of an interface the pll reference signal port can be on another tile + */ + thread_speed(); + clockGen( c_spdif_rx, + c_adat_rx, + i_pll_ref, + c_dig_rx, + c_clk_ctl, + c_clk_int, + c_audio_rate_change +#if XUA_USE_SW_PLL + , p_for_mclk_count_aud + , c_sw_pll +#endif + ); + } +#endif + + } // par +} + +#ifndef USER_MAIN_DECLARATIONS +#define USER_MAIN_DECLARATIONS +#endif + +#ifndef USER_MAIN_CORES +#define USER_MAIN_CORES +#endif + +//extern unsafe client interface i2c_master_if i_i2c_client; +//extern unsafe client interface i2c_master_if i_i2c_client_t0; +extern void dsp_core0(void); +extern void board_setup(); +extern void dsp_main (streaming chanend c_data); +extern void UserBufferForwardingTask_XC(unsigned c_dsp, unsigned c_init); +extern void SetEx3dHidChan (chanend c); + +extern int dsp_worker_tile(chanend c_dsp_to_ex3d, int worker_id); +//extern int dsp_worker_tile_1(chanend c_dsp_to_ex3d, int worker_id); +extern void ex3d_task(); +extern void hid_button_task(chanend cc_mic_level, chanend c_hid, chanend c_hidSendData, chanend c_uac_vol, chanend c_ex3d_hid_cmd); +#if HID_DFU_EN +extern void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx, streaming chanend c_dfu_rx, chanend c_mic_det); +#else +extern void AudioHwRemote(chanend c_hidSendData, chanend cc_mic_level, chanend c_uac_vol, chanend c_audiohw_rx, chanend c_mic_det); +#endif +extern void dnr_dsp_proc_task(void); + +extern unsafe chanend uc_dsp_to_ex3d[DSP_WORKER_COUNT]; +extern unsafe chanend uc_dsp_to_dnr_t1; +extern unsafe chanend uc_key_to_ubm_t0; +extern unsafe chanend uc_audiohw; +#if HID_DFU_EN +extern unsafe streaming chanend uc_dfu; +#endif +extern void key_sender(chanend c_key); +extern void key_receiver(chanend c_key); +extern void mute_handler(chanend c_mic_det); + + +/* Main for USB Audio Applications */ +int main() +{ +#if !XUA_USB_EN + #define c_mix_out null +#else + chan c_mix_out; +#endif + +#ifdef MIDI + chan c_midi; +#endif +#ifdef IAP + chan c_iap; +#ifdef IAP_EA_NATIVE_TRANS + chan c_ea_data; +#endif +#endif + +#if (MIXER) + chan c_mix_ctl; +#endif + +#if (XUA_SPDIF_RX_EN) + streaming chan c_spdif_rx; +#else +#define c_spdif_rx null +#endif + +#if (XUA_ADAT_RX_EN) + streaming chan c_adat_rx; +#else +#define c_adat_rx null +#endif + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + chan c_spdif_tx; +#endif + +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + chan c_clk_ctl; + chan c_clk_int; +#else +#define c_clk_int null +#define c_clk_ctl null +#endif + +#if (XUA_DFU_EN == 1) + interface i_dfu dfuInterface; +#else + #define dfuInterface null +#endif + +#if (XUA_NUM_PDM_MICS > 0) + chan c_pdm_pcm; +#endif + +#if (((XUA_SYNCMODE == XUA_SYNCMODE_SYNC && !XUA_USE_SW_PLL) || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) ) + interface pll_ref_if i_pll_ref; +#endif + +#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + chan c_sw_pll; +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + chan c_audio_rate_change; /* Notification of new mclk freq to ep_buffer */ +#endif + chan c_sof; + chan c_xud_out[ENDPOINT_COUNT_OUT]; /* Endpoint channels for XUD */ + chan c_xud_in[ENDPOINT_COUNT_IN]; + + /* Used to communicate controls/setting from XUA_Endpoint0() to the Audio/Buffering sub-system */ + chan c_aud_ctl; + +#if (!MIXER) +#define c_mix_ctl null +#endif + +#ifdef IAP_EA_NATIVE_TRANS + chan c_EANativeTransport_ctrl; +#else +#define c_EANativeTransport_ctrl null +#endif + +//#if (HID_CONTROLS > 0) +// chan c_hid; +//#endif + + USER_MAIN_DECLARATIONS + + chan c_dsp_to_ex3d[DSP_WORKER_COUNT]; + chan cc_mic_level; + chan c_audiohw; +#if HID_DFU_EN + streaming chan c_dfu; +#endif + chan c_key; chan c_hidSendData; + chan c_hidRcvData; + streaming chan c_eq_data; + streaming chan c_ubm_init; + chan c_uac_vol; + chan c_ex3d_hid_cmd; + chan c_mic_det; + + par + { + USER_MAIN_CORES + on tile[1] : { + par { + // Forwarder Task on its own core + { + unsigned u_dsp, u_init; + asm("mov %0, %1" : "=r"(u_dsp) : "r"(c_eq_data)); + asm("mov %0, %1" : "=r"(u_init) : "r"(c_ubm_init)); + UserBufferForwardingTask_C(u_dsp, u_init); + } + + // HID & Control sequence on its own core + unsafe { + uc_audiohw = (chanend)c_audiohw; + hid_button_task(cc_mic_level, c_hidRcvData, c_hidSendData, c_uac_vol, c_ex3d_hid_cmd); + } + +#if USE_EX3D == 1 + // Workers on their own cores + par(int i = 0; i < DSP_WORKER_COUNT; i++) + dsp_worker_tile(c_dsp_to_ex3d[i], i); + + // EX3D Task sequence on its own core + unsafe { + delay_milliseconds(200); + key_sender(c_key); + for (int i = 0; i < DSP_WORKER_COUNT; i++) + uc_dsp_to_ex3d[i] = (chanend)c_dsp_to_ex3d[i]; + ex3d_task(); + } +#endif + } + } + on tile[1]: mute_handler(c_mic_det); + + on tile[0] : { + par { + { +#if USE_EX3D == 1 + unsafe { key_receiver(c_key); } +#endif + AudioHwRemote(c_hidSendData, cc_mic_level, c_uac_vol, c_audiohw +#if HID_DFU_EN + , c_dfu +#endif + , c_mic_det + ); + } + } + } +#if EQ_EN == 1 + on tile[0] : { + { + +#if HID_DFU_EN + unsafe { + uc_dfu = (streaming chanend)c_dfu; + } +#endif + + dsp_core0(); + } + } + on tile[0] : { dsp_main(c_eq_data); } +#endif + +#if DNR_ENABLE == 1 + on tile[0] : { dnr_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 + on tile[XUD_TILE]: + par + { +#if XUA_USB_EN +#if ((XUD_TILE == 0) && (XUA_DFU_EN == 1)) + /* Check if USB is on the flash tile (tile 0) */ + /* Expect to be distrbuted into XUA_Endpoint0() */ + [[distribute]] + DFUHandler(dfuInterface, null); +#endif + + /* Core USB task, buffering, USB etc */ + { +#ifdef XUD_PRIORITY_HIGH + set_core_high_priority_on(); +#endif + /* Run UAC2.0 at high-speed, UAC1.0 at full-speed */ + unsigned usbSpeed = (AUDIO_CLASS == 2) ? XUD_SPEED_HS : XUD_SPEED_FS; + + unsigned xudPwrCfg = (XUA_POWERMODE == XUA_POWERMODE_SELF) ? XUD_PWR_SELF : XUD_PWR_BUS; + + /* USB interface core */ + XUD_Main(c_xud_out, ENDPOINT_COUNT_OUT, c_xud_in, ENDPOINT_COUNT_IN, + c_sof, epTypeTableOut, epTypeTableIn, usbSpeed, xudPwrCfg); + } + +#if (NUM_USB_CHAN_OUT > 0) || (NUM_USB_CHAN_IN > 0) || XUA_HID_ENABLED || defined(MIDI) + /* Core USB audio task, buffering, USB etc */ + { + unsigned x; + thread_speed(); + + /* Attach mclk count port to mclk clock-block (for feedback) */ + //set_port_clock(p_for_mclk_count, clk_audio_mclk); +#if(AUDIO_IO_TILE != XUD_TILE) + set_clock_src(clk_audio_mclk_usb, p_mclk_in_usb); + set_port_clock(p_for_mclk_count, clk_audio_mclk_usb); + start_clock(clk_audio_mclk_usb); +#else + /* Clock port from same clock-block as I2S */ + /* TODO remove asm() */ + asm("ldw %0, dp[clk_audio_mclk]":"=r"(x)); + asm("setclk res[%0], %1"::"r"(p_for_mclk_count), "r"(x)); +#endif + /* Endpoint & audio buffering cores - buffers all EP's other than 0 */ + XUA_Buffer( +#if (NUM_USB_CHAN_OUT > 0) + c_xud_out[ENDPOINT_NUMBER_OUT_AUDIO], /* Audio Out*/ +#endif +#if (NUM_USB_CHAN_IN > 0) + c_xud_in[ENDPOINT_NUMBER_IN_AUDIO], /* Audio In */ +#endif +#if (NUM_USB_CHAN_OUT > 0) && ((NUM_USB_CHAN_IN == 0) || defined(UAC_FORCE_FEEDBACK_EP)) + c_xud_in[ENDPOINT_NUMBER_IN_FEEDBACK], /* Audio FB */ +#endif +#ifdef MIDI + c_xud_out[ENDPOINT_NUMBER_OUT_MIDI], /* MIDI Out */ // 2 + c_xud_in[ENDPOINT_NUMBER_IN_MIDI], /* MIDI In */ // 4 + c_midi, +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + /* Audio Interrupt - only used for interrupts on external clock change */ + c_xud_in[ENDPOINT_NUMBER_IN_INTERRUPT], + c_clk_int, +#endif + c_sof, c_aud_ctl, p_for_mclk_count +#if (XUA_HID_ENABLED) + , c_xud_in[ENDPOINT_NUMBER_IN_HID] +#endif + , c_mix_out +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , c_audio_rate_change + #if (!XUA_USE_SW_PLL) + , i_pll_ref + #else + , c_sw_pll + #endif +#endif + ); + //: + } +#endif + + /* Endpoint 0 Core */ + { + thread_speed(); +#if (USE_EX3D == 1) && (HID_CONTROLS > 0) + SetEx3dHidChan(c_ex3d_hid_cmd); + XUA_Endpoint0( c_xud_out[0], c_xud_in[0], c_hidRcvData, c_aud_ctl, c_mix_ctl, c_clk_ctl, c_EANativeTransport_ctrl, dfuInterface VENDOR_REQUESTS_PARAMS_); +#else + XUA_Endpoint0( c_xud_out[0], c_xud_in[0], c_aud_ctl, c_mix_ctl, c_clk_ctl, c_EANativeTransport_ctrl, dfuInterface VENDOR_REQUESTS_PARAMS_); +#endif + } + +#endif /* XUA_USB_EN */ + } + +#if ((XUA_SYNCMODE == XUA_SYNCMODE_SYNC || XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + on tile[AUDIO_IO_TILE]: sw_pll_task(c_sw_pll); +#endif + + on tile[AUDIO_IO_TILE]: + { + set_core_high_priority_on(); + /* Audio I/O task, includes mixing etc */ + usb_audio_io( +#if (NUM_USB_CHAN_OUT > 0) || (NUM_USB_CHAN_IN > 0) + /* Connect audio system to XUA_Buffer(); */ + c_mix_out +#else + /* Connect to XUA_Endpoint0() */ + c_aud_ctl +#endif +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + , c_spdif_tx +#endif +#if (MIXER) + , c_mix_ctl +#endif + , c_spdif_rx, c_adat_rx, c_clk_ctl, c_clk_int +#if (XUD_TILE != 0) && (AUDIO_IO_TILE == 0) && (XUA_DFU_EN == 1) + , dfuInterface +#endif +#if (XUA_NUM_PDM_MICS > 0) + , c_pdm_pcm +#endif +#if (XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) + , i_pll_ref +#endif +#if (XUA_SYNCMODE == XUA_SYNCMODE_SYNC) + , c_audio_rate_change +#endif +#if ((XUA_SPDIF_RX_EN || XUA_ADAT_RX_EN) && XUA_USE_SW_PLL) + , p_for_mclk_count_audio + , c_sw_pll +#endif + ); + } + +#if (XUA_SPDIF_TX_EN) && (SPDIF_TX_TILE != AUDIO_IO_TILE) + on tile[SPDIF_TX_TILE]: + { + thread_speed(); + SpdifTxWrapper(c_spdif_tx); + } +#endif + +#if defined(MIDI) && defined(IAP) && (IAP_TILE == MIDI_TILE) + /* MIDI and IAP share a core */ + on tile[IAP_TILE]: + { + thread_speed(); + usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0, c_iap, null, null, null); + } +#else +#if defined(MIDI) + /* MIDI core */ + on tile[MIDI_TILE]: + { + thread_speed(); + usb_midi(p_midi_rx, p_midi_tx, clk_midi, c_midi, 0); + } +#endif +#if defined(IAP) + on tile[IAP_TILE]: + { + thread_speed(); + iAP(c_iap, null, null, null); + } +#endif +#endif + +#if (XUA_SPDIF_RX_EN) + on tile[XUD_TILE]: + { + thread_speed(); + spdif_rx(c_spdif_rx, p_spdif_rx, clk_spd_rx, 192000); + } +#endif + +#if (XUA_ADAT_RX_EN) + on stdcore[XUD_TILE] : + { + set_thread_fast_mode_on(); + + while (1) + { + adatReceiver48000(p_adat_rx, c_adat_rx); + adatReceiver44100(p_adat_rx, c_adat_rx); + } + } +#endif + + +#if XUA_USB_EN +#if (XUD_TILE != 0) && (AUDIO_IO_TILE != 0) && (XUA_DFU_EN == 1) + /* Run flash code on its own - hope it gets combined */ + //#warning Running DFU flash code on its own + on stdcore[0]: DFUHandler(dfuInterface, null); +#endif +#endif + +#if (XUA_NUM_PDM_MICS > 0) + /* PDM Mics running on a separate to AudioHub */ + on stdcore[PDM_TILE]: + { + mic_array_task(c_mic_pcm); + } +#endif /*XUA_NUM_PDM_MICS > 0*/ + } + + return 0; +} + +#endif diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/nau88c21.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/nau88c21.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/nau88c21.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/nau88c21.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/program_key.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/program_key.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/program_key.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/program_key.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/share_buffer.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/share_buffer.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/share_buffer.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/share_buffer.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/share_buffer.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/share_buffer.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/share_buffer.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/share_buffer.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/sy102_hid_protocol.md b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/sy102_hid_protocol.md similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/sy102_hid_protocol.md rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/sy102_hid_protocol.md diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.bak b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.bak new file mode 100644 index 0000000..077b7f1 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.bak @@ -0,0 +1,128 @@ +// Copyright 2021-2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#define DEBUG_PRINT_ENABLE 1 +#include +#include +#include +#include +#include +#include "xc_ptr.h" +#include "share_buffer.h" +#include "debug_print.h" + +unsafe streaming chanend uc_eq_data; +unsigned g_current_sample_freq = 0; +extern void UserBufferManagement_ex3d(unsigned sampsFromUsbToAudio[]); +extern void UserBufferManagementInit_ex3d(unsigned sampFreq); +extern uint32_t init_eq_data(unsigned sample_freq); +extern unsigned g_monitor_switch_t1; + +unsigned dnr_exchange_buffer(unsigned data); + +void SetEqDataChan (streaming chanend c) { +unsafe { + uc_eq_data = c; + } +} + +void dsp_main (streaming chanend c_data) { + int play_input[NUM_USB_CHAN_OUT]; + int play_output[I2S_CHANS_DAC]; + unsigned mic_input[I2S_CHANS_ADC]; + int mic_output[I2S_CHANS_ADC]; + unsigned sample_freq_old = 0; + + play_output[0] = 0; + play_output[1] = 0; + while (1) { + // 用 stestct 区分控制令牌(采样率变更)和数据令牌(音频样本) + if (stestct(c_data)) { + // 控制令牌:采样率变更,执行初始化 + sinct(c_data); + unsigned sample_freq; + c_data :> sample_freq; + if ( sample_freq != sample_freq_old) + { + init_eq_data(sample_freq); + clear_ring_buffer(0); + clear_ring_buffer(1); + clear_ring_buffer(2); + clear_ring_buffer(3); + play_output[0] = 0; + play_output[1] = 0; + sample_freq_old = sample_freq; + } + // 回送控制令牌作为确认 + soutct(c_data, 0x06); + } else { + // 数据令牌:正常音频处理路径 + c_data :> play_input[0]; + c_data :> play_input[1]; + + c_data <: play_output[0]; + c_data <: play_output[1]; + +#if DNR_ENABLE == 1 + c_data :> mic_input[1]; + c_data <: mic_output[1]; +#endif + write_to_ring_buffer(0, play_input[0]); + write_to_ring_buffer(1, play_input[1]); + play_output[0] = read_from_ring_buffer(2); + play_output[1] = read_from_ring_buffer(3); +#if DNR_ENABLE == 1 + unsafe { + mic_output[1] = dnr_exchange_buffer(mic_input[1]); + } +#endif + } + } +} + + +#pragma unsafe arrays +void UserBufferManagementInit(unsigned sampFreq) +{ + SET_SHARED_GLOBAL(g_current_sample_freq, sampFreq); + UserBufferManagementInit_ex3d(sampFreq); +unsafe { + // 发送控制令牌 + 采样率给 dsp_main,触发初始化 + soutct((streaming chanend)uc_eq_data, 0x06); + uc_eq_data <: sampFreq; + // 等待 dsp_main 确认:排空残留数据令牌,等控制令牌回来 + int trashbin; + for (;;) { + if (stestct((streaming chanend)uc_eq_data)) { + sinct((streaming chanend)uc_eq_data); + break; + } else { + uc_eq_data :> trashbin; + } + } +} +} +// out port freq_port = PORT_X1D26_27_32_33; +#pragma unsafe arrays +void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]) +{ + unsigned is_monitor; + UserBufferManagement_ex3d(sampsFromUsbToAudio); +unsafe { + // 数据路径:只传输音频样本,不传采样率(采样率变更由 UserBufferManagementInit 通过控制令牌处理) + uc_eq_data <: sampsFromUsbToAudio[0]; + uc_eq_data <: sampsFromUsbToAudio[1]; + uc_eq_data :> sampsFromUsbToAudio[0]; + uc_eq_data :> sampsFromUsbToAudio[1]; +#if DNR_ENABLE == 1 + uc_eq_data <: sampsFromAudioToUsb[1]; + uc_eq_data :> sampsFromAudioToUsb[1]; + sampsFromAudioToUsb[0] = sampsFromAudioToUsb[1]; +#endif + } + GET_SHARED_GLOBAL(is_monitor, g_monitor_switch_t1); + if (is_monitor) { + sampsFromUsbToAudio[0] += (sampsFromAudioToUsb[0]); + sampsFromUsbToAudio[1] += (sampsFromAudioToUsb[1]); + } +} + diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.gemini b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.gemini new file mode 100644 index 0000000..bb681a7 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.gemini @@ -0,0 +1,207 @@ +// Copyright 2021-2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#define DEBUG_PRINT_ENABLE 1 +#include +#include +#include +#include +#include +#include "xc_ptr.h" +#include "share_buffer.h" +#include "debug_print.h" +#include "ubm_memory.h" + +unsafe streaming chanend uc_eq_data; +unsafe streaming chanend uc_ubm_init; +unsigned g_current_sample_freq = 0; +extern void UserBufferManagement_ex3d(unsigned sampsFromUsbToAudio[]); +extern void UserBufferManagementInit_ex3d(unsigned sampFreq); +extern uint32_t init_eq_data(unsigned sample_freq); +extern unsigned g_monitor_switch_t1; + +unsigned dnr_exchange_buffer(unsigned data); + +void SetEqDataChan (streaming chanend c) { +unsafe { + uc_eq_data = c; + } +} + +void SetUbmInitChan (streaming chanend c) { +unsafe { + uc_ubm_init = c; + } +} + +void UserBufferForwardingTask(streaming chanend c_dsp, streaming chanend c_init) { + unsigned sample_freq = 0; + unsafe { + ubm_shared_t * unsafe p = (ubm_shared_t * unsafe)get_ubm_shared_ptr(); + while(1) { + select { + // 1. Handle Init Request via Channel + case c_init :> sample_freq: + soutct(c_dsp, 0x06); + c_dsp <: sample_freq; + // wait for ack from dsp_main (Tile 0) + int trashbin; + for (;;) { + if (stestct(c_dsp)) { + sinct(c_dsp); + break; + } else { + c_dsp :> trashbin; + } + } + c_init <: 1; // Send ack back + break; + + // 2. Forward samples to dsp_main on Tile 0 + default: + if (p->head != p->tail) { + unsigned t = p->tail; + unsigned p_in[2]; +#if DNR_ENABLE == 1 + unsigned dnr_in; +#endif + unsigned p_out[2]; +#if DNR_ENABLE == 1 + unsigned dnr_out; +#endif + + p_in[0] = p->usb_to_dsp[0][t]; + p_in[1] = p->usb_to_dsp[1][t]; +#if DNR_ENABLE == 1 + dnr_in = p->dnr_to_dsp[t]; +#endif + p->tail = (t + 1) & 3; + + // Talk to dsp_main (Tile 0) + c_dsp <: p_in[0]; + c_dsp <: p_in[1]; + c_dsp :> p_out[0]; + c_dsp :> p_out[1]; +#if DNR_ENABLE == 1 + c_dsp <: dnr_in; + c_dsp :> dnr_out; +#endif + + // Put processed sample back + unsigned fh = p->f_head; + p->usb_from_dsp[0][fh] = p_out[0]; + p->usb_from_dsp[1][fh] = p_out[1]; +#if DNR_ENABLE == 1 + p->dnr_from_dsp[fh] = dnr_out; +#endif + p->f_head = (fh + 1) & 3; + } + break; + } + } + } +} + +void dsp_main (streaming chanend c_data) { + int play_input[NUM_USB_CHAN_OUT]; + int play_output[I2S_CHANS_DAC]; + unsigned sample_freq_old = 0; +#if DNR_ENABLE == 1 + static int mic_output_dnr = 0; // 必须为 static 以保持采样间状态并回传上一结果 +#endif + + play_output[0] = 0; + play_output[1] = 0; + while (1) { + if (stestct(c_data)) { + sinct(c_data); + unsigned sample_freq; + c_data :> sample_freq; + if ( sample_freq != sample_freq_old) + { + init_eq_data(sample_freq); + clear_ring_buffer(0); + clear_ring_buffer(1); + clear_ring_buffer(2); + clear_ring_buffer(3); + play_output[0] = 0; + play_output[1] = 0; + sample_freq_old = sample_freq; + } + soutct(c_data, 0x06); + } else { + c_data :> play_input[0]; + c_data :> play_input[1]; + + c_data <: play_output[0]; + c_data <: play_output[1]; + +#if DNR_ENABLE == 1 + unsigned mic_input_dnr; + c_data :> mic_input_dnr; + c_data <: mic_output_dnr; // 发送上一次的处理结果,避免阻塞 + + // 在 Tile 0 上执行 DNR 处理(与 dnr_dsp_proc_task 共享内存) + // 注意:即便 DNR level 为 0,也必须调用 dnr_exchange_buffer 以维持缓冲区轮转和数据直通 + unsafe { + mic_output_dnr = (int)dnr_exchange_buffer(mic_input_dnr); + } +#endif + write_to_ring_buffer(0, play_input[0]); + write_to_ring_buffer(1, play_input[1]); + play_output[0] = read_from_ring_buffer(2); + play_output[1] = read_from_ring_buffer(3); + } + } +} + + +#pragma unsafe arrays +void UserBufferManagementInit(unsigned sampFreq) +{ + SET_SHARED_GLOBAL(g_current_sample_freq, sampFreq); + UserBufferManagementInit_ex3d(sampFreq); + +unsafe { + uc_ubm_init <: sampFreq; + int ack; + uc_ubm_init :> ack; +} +} + +// out port freq_port = PORT_X1D26_27_32_33; +#pragma unsafe arrays +void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]) +{ + unsigned is_monitor; + UserBufferManagement_ex3d(sampsFromUsbToAudio); + +unsafe { + ubm_shared_t * unsafe p = (ubm_shared_t * unsafe)get_ubm_shared_ptr(); + // 1. Push new samples into ring buffer + unsigned h = p->head; + p->usb_to_dsp[0][h] = sampsFromUsbToAudio[0]; + p->usb_to_dsp[1][h] = sampsFromUsbToAudio[1]; +#if DNR_ENABLE == 1 + p->dnr_to_dsp[h] = sampsFromAudioToUsb[1]; +#endif + p->head = (h + 1) & 3; + + // 2. Pull processed samples from ring buffer + if (p->f_head != p->f_tail) { + unsigned ft = p->f_tail; + sampsFromUsbToAudio[0] = p->usb_from_dsp[0][ft]; + sampsFromUsbToAudio[1] = p->usb_from_dsp[1][ft]; +#if DNR_ENABLE == 1 + sampsFromAudioToUsb[1] = p->dnr_from_dsp[ft]; + sampsFromAudioToUsb[0] = sampsFromAudioToUsb[1]; // 拷贝处理后的单声道到双声道 +#endif + p->f_tail = (ft + 1) & 3; + } +} + + GET_SHARED_GLOBAL(is_monitor, g_monitor_switch_t1); + if (is_monitor) { + sampsFromUsbToAudio[0] += (sampsFromAudioToUsb[0]); + sampsFromUsbToAudio[1] += (sampsFromAudioToUsb[1]); + } +} diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.gemini1 b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.gemini1 new file mode 100644 index 0000000..c85424b --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.gemini1 @@ -0,0 +1,200 @@ +// Copyright 2021-2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#define DEBUG_PRINT_ENABLE 1 +#include +#include +#include +#include +#include +#include "xc_ptr.h" +#include "share_buffer.h" +#include "debug_print.h" +#include "ubm_memory.h" + +unsigned g_current_sample_freq = 0; +extern void UserBufferManagement_ex3d(unsigned sampsFromUsbToAudio[]); +extern void UserBufferManagementInit_ex3d(unsigned sampFreq); +extern uint32_t init_eq_data(unsigned sample_freq); +extern unsigned g_monitor_switch_t1; + +unsigned dnr_exchange_buffer(unsigned data); + +// 保持定义以兼容 main.xc +void SetEqDataChan (chanend c) {} +void SetUbmInitChan (chanend c) {} + +// XC 中实现转发任务。使用 unsigned 参数避开 XC 编译器的并行资源分析 Bug。 +void UserBufferForwardingTask_XC(unsigned c_dsp_val, unsigned c_init_val) { + unsigned sample_freq = 0; + // 设置 Init 通道供回调使用 + set_ubm_init_chan_c(c_init_val); + + unsafe { + ubm_shared_t * unsafe p = (ubm_shared_t * unsafe)get_ubm_shared_ptr(); + while(1) { + // 1. 处理初始化请求 + int is_ct; + asm("testct %0, res[%1]" : "=r"(is_ct) : "r"(c_init_val)); + if (is_ct) { + asm("in %0, res[%1]" : "=r"(sample_freq) : "r"(c_init_val)); + asm("outct res[%0], %1" :: "r"(c_dsp_val), "r"(0x06)); + asm("out res[%0], %1" :: "r"(c_dsp_val), "r"(sample_freq)); + + // 等待确认 + while (1) { + int dsp_ct; + asm("testct %0, res[%1]" : "=r"(dsp_ct) : "r"(c_dsp_val)); + if (dsp_ct) { + unsigned tmp; + asm("inct %0, res[%1]" : "=r"(tmp) : "r"(c_dsp_val)); + break; + } else { + unsigned trash; + asm("in %0, res[%1]" : "=r"(trash) : "r"(c_dsp_val)); + } + } + asm("out res[%0], %1" :: "r"(c_init_val), "r"(1)); + } + + // 2. 处理音频转发 + if (p->head != p->tail) { + unsigned t = p->tail; + unsigned p_in[NUM_USB_CHAN_OUT]; + unsigned p_out[2]; +#if DNR_ENABLE == 1 + unsigned dnr_in = p->dnr_to_dsp[t]; + unsigned dnr_out; +#endif + for(int i=0; iusb_to_dsp[i][t]; + } + p->tail = (t + 1) & 3; + + // 在此处调用算法,确保 XC 宏定义完全有效 + UserBufferManagement_ex3d(p_in); + + // 发送给 Tile 0 (EQ 处理) + asm("out res[%0], %1" :: "r"(c_dsp_val), "r"(p_in[0])); + asm("out res[%0], %1" :: "r"(c_dsp_val), "r"(p_in[1])); + asm("in %0, res[%1]" : "=r"(p_out[0]) : "r"(c_dsp_val)); + asm("in %0, res[%1]" : "=r"(p_out[1]) : "r"(c_dsp_val)); + +#if DNR_ENABLE == 1 + // 发送给 Tile 0 (DNR 处理) + asm("out res[%0], %1" :: "r"(c_dsp_val), "r"(dnr_in)); + asm("in %0, res[%1]" : "=r"(dnr_out) : "r"(c_dsp_val)); +#endif + + // 写回结果 + unsigned fh = p->f_head; + p->usb_from_dsp[0][fh] = p_out[0]; + p->usb_from_dsp[1][fh] = p_out[1]; +#if DNR_ENABLE == 1 + p->dnr_from_dsp[fh] = dnr_out; +#endif + p->f_head = (fh + 1) & 3; + } + } + } +} + +void dsp_main (streaming chanend c_data) { + int play_input[2]; + int play_output[2]; + unsigned sample_freq_old = 0; +#if DNR_ENABLE == 1 + static int mic_output_dnr = 0; +#endif + + play_output[0] = 0; + play_output[1] = 0; + while (1) { + if (stestct(c_data)) { + sinct(c_data); + unsigned sample_freq; + c_data :> sample_freq; + if ( sample_freq != sample_freq_old) + { + init_eq_data(sample_freq); + clear_ring_buffer(0); + clear_ring_buffer(1); + clear_ring_buffer(2); + clear_ring_buffer(3); + play_output[0] = 0; + play_output[1] = 0; + sample_freq_old = sample_freq; + } + soutct(c_data, 0x06); + } else { + c_data :> play_input[0]; + c_data :> play_input[1]; + + c_data <: play_output[0]; + c_data <: play_output[1]; + +#if DNR_ENABLE == 1 + unsigned mic_input_dnr; + c_data :> mic_input_dnr; + c_data <: mic_output_dnr; + + unsafe { + mic_output_dnr = (int)dnr_exchange_buffer(mic_input_dnr); + } +#endif + write_to_ring_buffer(0, play_input[0]); + write_to_ring_buffer(1, play_input[1]); + play_output[0] = read_from_ring_buffer(2); + play_output[1] = read_from_ring_buffer(3); + } + } +} + +#pragma unsafe arrays +void UserBufferManagementInit(unsigned sampFreq) +{ + SET_SHARED_GLOBAL(g_current_sample_freq, sampFreq); + UserBufferManagementInit_ex3d(sampFreq); + + unsigned val = get_ubm_init_chan_c(); + if (val != 0) { + unsafe { + asm("out res[%0], %1" :: "r"(val), "r"(sampFreq)); + unsigned ack; + asm("in %0, res[%1]" : "=r"(ack) : "r"(val)); + } + } +} + +#pragma unsafe arrays +void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]) +{ + unsigned is_monitor; +unsafe { + ubm_shared_t * unsafe p = (ubm_shared_t * unsafe)get_ubm_shared_ptr(); + unsigned h = p->head; + for(int i=0; iusb_to_dsp[i][h] = sampsFromUsbToAudio[i]; + } +#if DNR_ENABLE == 1 + p->dnr_to_dsp[h] = sampsFromAudioToUsb[1]; +#endif + p->head = (h + 1) & 3; + + if (p->f_head != p->f_tail) { + unsigned ft = p->f_tail; + sampsFromUsbToAudio[0] = p->usb_from_dsp[0][ft]; + sampsFromUsbToAudio[1] = p->usb_from_dsp[1][ft]; +#if DNR_ENABLE == 1 + sampsFromAudioToUsb[1] = p->dnr_from_dsp[ft]; + sampsFromAudioToUsb[0] = sampsFromAudioToUsb[1]; +#endif + p->f_tail = (ft + 1) & 3; + } +} + + GET_SHARED_GLOBAL(is_monitor, g_monitor_switch_t1); + if (is_monitor) { + sampsFromUsbToAudio[0] += (sampsFromAudioToUsb[0]); + sampsFromUsbToAudio[1] += (sampsFromAudioToUsb[1]); + } +} diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.new b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.new new file mode 100644 index 0000000..9a1770e --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_buffer.xc.new @@ -0,0 +1,208 @@ +// Copyright 2021-2024 XMOS LIMITED. +// This Software is subject to the terms of the XMOS Public Licence: Version 1. +#define DEBUG_PRINT_ENABLE 1 +#include +#include +#include +#include +#include +#include "xc_ptr.h" +#include "share_buffer.h" +#include "debug_print.h" +#include "ubm_memory.h" + +unsafe streaming chanend uc_eq_data; +unsafe streaming chanend uc_ubm_init; +unsigned g_current_sample_freq = 0; +extern void UserBufferManagement_ex3d(unsigned sampsFromUsbToAudio[]); +extern void UserBufferManagementInit_ex3d(unsigned sampFreq); +extern uint32_t init_eq_data(unsigned sample_freq); +extern unsigned g_monitor_switch_t1; + +unsigned dnr_exchange_buffer(unsigned data); + +void SetEqDataChan (streaming chanend c) { +unsafe { + uc_eq_data = c; + } +} + +void SetUbmInitChan (streaming chanend c) { +unsafe { + uc_ubm_init = c; + } +} + +void UserBufferForwardingTask(streaming chanend c_dsp, streaming chanend c_init) { + unsigned sample_freq = 0; + unsafe { + ubm_shared_t * unsafe p = (ubm_shared_t * unsafe)get_ubm_shared_ptr(); + while(1) { + select { + // 1. Handle Init Request via Channel + case c_init :> sample_freq: + soutct(c_dsp, 0x06); + c_dsp <: sample_freq; + // wait for ack from dsp_main (Tile 0) + int trashbin; + for (;;) { + if (stestct(c_dsp)) { + sinct(c_dsp); + break; + } else { + c_dsp :> trashbin; + } + } + c_init <: 1; // Send ack back + break; + + // 2. Forward samples to dsp_main on Tile 0 + default: + if (p->head != p->tail) { + unsigned t = p->tail; + unsigned p_in[NUM_USB_CHAN_OUT]; +#if DNR_ENABLE == 1 + unsigned dnr_in; +#endif + unsigned p_out[2]; +#if DNR_ENABLE == 1 + unsigned dnr_out; +#endif + + for(int i=0; iusb_to_dsp[i][t]; + } +#if DNR_ENABLE == 1 + dnr_in = p->dnr_to_dsp[t]; +#endif + p->tail = (t + 1) & 3; + // 在此处调用算法,确保 XC 宏定义完全有效 + UserBufferManagement_ex3d(p_in); + // Talk to dsp_main (Tile 0) + c_dsp <: p_in[0]; + c_dsp <: p_in[1]; + c_dsp :> p_out[0]; + c_dsp :> p_out[1]; +#if DNR_ENABLE == 1 + c_dsp <: dnr_in; + c_dsp :> dnr_out; +#endif + + // Put processed sample back + unsigned fh = p->f_head; + p->usb_from_dsp[0][fh] = p_out[0]; + p->usb_from_dsp[1][fh] = p_out[1]; +#if DNR_ENABLE == 1 + p->dnr_from_dsp[fh] = dnr_out; +#endif + p->f_head = (fh + 1) & 3; + } + break; + } + } + } +} + +void dsp_main (streaming chanend c_data) { + int play_input[NUM_USB_CHAN_OUT]; + int play_output[I2S_CHANS_DAC]; + unsigned sample_freq_old = 0; +#if DNR_ENABLE == 1 + static int mic_output_dnr = 0; // 必须为 static 以保持采样间状态并回传上一结果 +#endif + + play_output[0] = 0; + play_output[1] = 0; + while (1) { + if (stestct(c_data)) { + sinct(c_data); + unsigned sample_freq; + c_data :> sample_freq; + if ( sample_freq != sample_freq_old) + { + init_eq_data(sample_freq); + clear_ring_buffer(0); + clear_ring_buffer(1); + clear_ring_buffer(2); + clear_ring_buffer(3); + play_output[0] = 0; + play_output[1] = 0; + sample_freq_old = sample_freq; + } + soutct(c_data, 0x06); + } else { + c_data :> play_input[0]; + c_data :> play_input[1]; + + c_data <: play_output[0]; + c_data <: play_output[1]; + +#if DNR_ENABLE == 1 + unsigned mic_input_dnr; + c_data :> mic_input_dnr; + c_data <: mic_output_dnr; // 发送上一次的处理结果,避免阻塞 + + // 在 Tile 0 上执行 DNR 处理(与 dnr_dsp_proc_task 共享内存) + // 注意:即便 DNR level 为 0,也必须调用 dnr_exchange_buffer 以维持缓冲区轮转和数据直通 + unsafe { + mic_output_dnr = (int)dnr_exchange_buffer(mic_input_dnr); + } +#endif + write_to_ring_buffer(0, play_input[0]); + write_to_ring_buffer(1, play_input[1]); + play_output[0] = read_from_ring_buffer(2); + play_output[1] = read_from_ring_buffer(3); + } + } +} + + +#pragma unsafe arrays +void UserBufferManagementInit(unsigned sampFreq) +{ + SET_SHARED_GLOBAL(g_current_sample_freq, sampFreq); + UserBufferManagementInit_ex3d(sampFreq); + +unsafe { + uc_ubm_init <: sampFreq; + int ack; + uc_ubm_init :> ack; +} +} + +// out port freq_port = PORT_X1D26_27_32_33; +#pragma unsafe arrays +void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[]) +{ + unsigned is_monitor; +unsafe { + ubm_shared_t * unsafe p = (ubm_shared_t * unsafe)get_ubm_shared_ptr(); + // 1. Push new samples into ring buffer + unsigned h = p->head; + for(int i=0; iusb_to_dsp[i][h] = sampsFromUsbToAudio[i]; + } +#if DNR_ENABLE == 1 + p->dnr_to_dsp[h] = sampsFromAudioToUsb[1]; +#endif + p->head = (h + 1) & 3; + + // 2. Pull processed samples from ring buffer + if (p->f_head != p->f_tail) { + unsigned ft = p->f_tail; + sampsFromUsbToAudio[0] = p->usb_from_dsp[0][ft]; + sampsFromUsbToAudio[1] = p->usb_from_dsp[1][ft]; +#if DNR_ENABLE == 1 + sampsFromAudioToUsb[1] = p->dnr_from_dsp[ft]; + sampsFromAudioToUsb[0] = sampsFromAudioToUsb[1]; // 拷贝处理后的单声道到双声道 +#endif + p->f_tail = (ft + 1) & 3; + } +} + + GET_SHARED_GLOBAL(is_monitor, g_monitor_switch_t1); + if (is_monitor) { + sampsFromUsbToAudio[0] += (sampsFromAudioToUsb[0]); + sampsFromUsbToAudio[1] += (sampsFromAudioToUsb[1]); + } +} diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/user_func.c b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_func.c similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/user_func.c rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_func.c diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/user_func.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_func.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/user_func.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_func.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/user_main.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_main.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/user_main.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_main.h diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/user_uart.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_uart.h similarity index 100% rename from sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/extensions/user_uart.h rename to sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/user_uart.h diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/xscope.xscope b/sw_usb_audio/app_usb_aud_fosi_c1_v71/xscope.xscope new file mode 100644 index 0000000..f7ddfd0 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/xscope.xscope @@ -0,0 +1,3 @@ + + + diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/CMakeLists.txt b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/CMakeLists.txt deleted file mode 100644 index 286ca07..0000000 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/CMakeLists.txt +++ /dev/null @@ -1,283 +0,0 @@ -cmake_minimum_required(VERSION 3.21) -include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake) -project(app_usb_aud_sy102) - -set(APP_HW_TARGET synido.xn) -include(${CMAKE_CURRENT_LIST_DIR}/../deps.cmake) -set(APP_PCA_ENABLE ON) - -###=========================================================================### -set(TARGET_HW_PHATEN_GSV2 ON) -set(CODEC_IS_AIC3204 ON) - -# Configure host os detection here... (Windows or Non-Windows) -#-----------------------------------------------------------------------------# -# Host OS detection, set HOST_OS_DETECTION to ON - -set(HOST_OS_DETECTION ON) -###=========================================================================### -# Customer -if (TARGET_HW_PHATEN_GSV2) - message("-- Building for PHATEN_GSv2 ---") - set(TARGET_BOARD PHATEN_GSV2) - set(APP_HW_TARGET synido.xn) -endif() - -# CODEC IC -if (CODEC_IS_AIC3204) - message("-- DAC: AIC3204 ---") - set(CODEC_IC CODEC_AIC3204) -endif() - -# Host OS detection -if (HOST_OS_DETECTION) - message("-- Host OS detection (Windows/Non-Windows) enabled ---") - set(EXTRA_BUILD_FLAGS ${EXTRA_BUILD_FLAGS} -DHOST_OS_DETECTION) -endif() - -# Firmware version (maps to BCD_DEVICE_J.M.N in USB descriptor) -set(FW_VER_J 1) -set(FW_VER_M 1) -set(FW_VER_N 15) -set(FW_VERSION "v${FW_VER_J}.${FW_VER_M}.${FW_VER_N}") - -set(SW_USB_AUDIO_FLAGS ${EXTRA_BUILD_FLAGS} -Os - -report - -L${CMAKE_CURRENT_LIST_DIR}/../../lib_ex3d/lib_ex3d/lib - -lquadflash - -g - #-fxscope - -DUSB_TILE=tile[0] - -DXUA_QUAD_SPI_FLASH=1 - -D${TARGET_BOARD} - -DBCD_DEVICE_J=${FW_VER_J} - -DBCD_DEVICE_M=${FW_VER_M} - -DBCD_DEVICE_N=${FW_VER_N} - -D${CODEC_IC} - -DWINDOWS_OS_DESCRIPTOR_SUPPORT) - -set(SW_USB_FACT_FLAGS ${EXTRA_BUILD_FLAGS} -Os - -report - -L${CMAKE_CURRENT_LIST_DIR}/../../lib_ex3d/lib_ex3d/lib - -lquadflash - -g - #-fxscope - -DUSB_TILE=tile[0] - -DXUA_QUAD_SPI_FLASH=1 - -D${TARGET_BOARD} - -DBCD_DEVICE_J=5 - -DBCD_DEVICE_M=5 - -DBCD_DEVICE_N=7 - -D${CODEC_IC} - -DWINDOWS_OS_DESCRIPTOR_SUPPORT) - - - -LINK_DIRECTORIES(${CMAKE_CURRENT_LIST_DIR}/../../lib_dnr/lib_dnr) - -set(APP_COMPILER_FLAGS_f5_music_uac1 ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2 - -DI2S_CHANS_ADC=2 - -DAUDIO_CLASS=1 - -DMIN_FREQ=48000 - -DMAX_FREQ=48000 - -DMCLK_441=512*44100 - -DMCLK_48=512*48000 - -DUAC1_MODE=1 - -DF5_MUSIC_UAC1=1 - #-DUSE_EX3D - -DMIXER=0 - #-ldnr_11ms - #-DDNR_ENABLE=1 - #-llib_ex3d_all - -DEQ_EN=1 - -DEX3D_SF_NUM=3 - -DNUM_USB_CHAN_OUT=8 - -DNUM_USB_CHAN_OUT_FS=2 - -DNUM_USB_CHAN_IN=2 - -DSTREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS=16 - -DSTREAM_FORMAT_INPUT_1_RESOLUTION_BITS=16 - -DINPUT_FORMAT_COUNT=1 - -DOUTPUT_FORMAT_COUNT=1 - #-DNUM_EX3D_CHAN_OUT=2 - -DMIN_VOLUME=0xE000 - -DINPUT_VOLUME_CONTROL=0 - -DOUTPUT_VOLUME_CONTROL=0 - #-DDEBUG_MEMORY_LOG_ENABLED=1 - -DXUA_DFU_EN=0 - -DHID_DFU_EN=1 - -DHID_CONTROLS_UAC1=1 - #-DIR_SWITCHING_MODE - -DHID_CONTROLS=1) - - - -set(APP_COMPILER_FLAGS_fact ${SW_USB_FACT_FLAGS} -DI2S_CHANS_DAC=2 - -DI2S_CHANS_ADC=0 - -DMIN_FREQ=48000 - -DMAX_FREQ=48000 - -DFACT=1 - #-DUSE_EX3D - -DMIXER=0 - -DUAC2_MODE=1 - #-ldnr_50ms - #-llib_ex3d_all - #-DEQ_EN=1 - #-DEX3D_SF_NUM=3 - -DNUM_USB_CHAN_OUT=2 - -DNUM_USB_CHAN_IN=0 - #-DNUM_EX3D_CHAN_OUT=2 - -DMIN_VOLUME=0xE000 - -DINPUT_VOLUME_CONTROL=0 - -DOUTPUT_VOLUME_CONTROL=0 - -DDEBUG_MEMORY_LOG_ENABLED=1 - -DHID_DFU_EN=1 - -DXUA_DFU_EN=1 - #-DIR_SWITCHING_MODE - -DHID_CONTROLS=1) - - - -set(APP_COMPILER_FLAGS_f1_music_uac2 ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2 - -DI2S_CHANS_ADC=2 - -DMIN_FREQ=44100 - -DMAX_FREQ=192000 - -DF1_MUSIC_UAC2=1 - -DSTREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS=24 - -DSTREAM_FORMAT_OUTPUT_2_RESOLUTION_BITS=24 - -DSTREAM_FORMAT_INPUT_1_RESOLUTION_BITS=24 - -DSTREAM_FORMAT_INPUT_2_RESOLUTION_BITS=24 - -DINPUT_FORMAT_COUNT=1 - -DOUTPUT_FORMAT_COUNT=1 - #-DUSE_EX3D - -DMIXER=0 - -DUAC2_MODE=1 - #-ldnr_50ms - #-llib_ex3d_all - -DEQ_EN=1 - #-DEX3D_SF_NUM=3 - -DNUM_USB_CHAN_OUT=2 - -DNUM_USB_CHAN_IN=2 - #-DNUM_EX3D_CHAN_OUT=2 - -DMIN_VOLUME=0xE000 - -DINPUT_VOLUME_CONTROL=1 - -DOUTPUT_VOLUME_CONTROL=1 - #-DDEBUG_MEMORY_LOG_ENABLED=1 - -DXUA_DFU_EN=1 - -DHID_DFU_EN=1 - #-DIR_SWITCHING_MODE - -DHID_CONTROLS=1) - - -set(APP_COMPILER_FLAGS_f3_f4_fps_uac2 ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2 - -DI2S_CHANS_ADC=2 - -DMIN_FREQ=48000 - -DMAX_FREQ=48000 - -DUSE_EX3D=1 - -DF3_F4_FPS_UAC2=1 - -DMIXER=0 - -DUAC2_MODE=1 - -ldnr_50ms - -llib_ex3d_all - -DEQ_EN=1 - -DDNR_ENABLE=1 - -DSTREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS=16 - -DSTREAM_FORMAT_INPUT_1_RESOLUTION_BITS=16 - -DINPUT_FORMAT_COUNT=1 - -DOUTPUT_FORMAT_COUNT=1 - -DEX3D_SF_NUM=3 - -DNUM_USB_CHAN_OUT=8 - -DNUM_USB_CHAN_IN=2 - -DNUM_EX3D_CHAN_OUT=2 - -DMIN_VOLUME=0xE000 - -DINPUT_VOLUME_CONTROL=1 - -DOUTPUT_VOLUME_CONTROL=1 - #-DDEBUG_MEMORY_LOG_ENABLED=1 - -DXUA_DFU_EN=1 - -DHID_DFU_EN=1 - -DIR_SWITCHING_MODE - -DHID_CONTROLS=1) - -set(APP_COMPILER_FLAGS_f6_f7_fps_uac1 ${SW_USB_AUDIO_FLAGS} -DI2S_CHANS_DAC=2 - -DI2S_CHANS_ADC=2 - -DAUDIO_CLASS=1 - -DMIN_FREQ=48000 - -DMAX_FREQ=48000 - -DMCLK_441=512*44100 - -DMCLK_48=512*48000 - -DSTREAM_FORMAT_OUTPUT_1_RESOLUTION_BITS=16 - -DSTREAM_FORMAT_INPUT_1_RESOLUTION_BITS=16 - -DINPUT_FORMAT_COUNT=1 - -DOUTPUT_FORMAT_COUNT=1 - -DUAC1_MODE=1 - -DUSE_EX3D=1 - -DF6_F7_FPS_UAC1=1 - -DMIXER=0 - -ldnr_50ms - -DDNR_ENABLE=1 - -llib_ex3d_all - -DEQ_EN=1 - -DEX3D_SF_NUM=3 - -DNUM_USB_CHAN_OUT=8 - -DNUM_USB_CHAN_IN=2 - -DNUM_EX3D_CHAN_OUT=2 - -DMIN_VOLUME=0xE000 - -DINPUT_VOLUME_CONTROL=1 - -DOUTPUT_VOLUME_CONTROL=1 - #-DDEBUG_MEMORY_LOG_ENABLED=1 - -DXUA_DFU_EN=1 - -DHID_DFU_EN=1 - -DHID_CONTROLS_UAC1=1 - -DIR_SWITCHING_MODE - -DHID_CONTROLS=1) - -set(APP_INCLUDES src src/core src/extensions ../../lib_dnr/lib_dnr) -set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) - -XMOS_REGISTER_APP() - -###=========================================================================### -# Flash image generation -# Slot assignment (matches MODE_Fxx flag values in audiohw.xc): -# slot 1 = f3_f4_fps_uac2 (COAX_IN_FLAG = MODE_F3_F4_FPS_UAC2) -# slot 2 = f5_music_uac1 (UAC1_IN_FLAG = MODE_F5_MUSIC_UAC1) <- factory base -# slot 3 = f1_music_uac2 (OPT_IN_FLAG = MODE_F1_MUSIC_UAC2) -# slot 4 = f6_f7_fps_uac1 (USB_IN_FLAG = MODE_F6_F7_FPS_UAC1) -###=========================================================================### - -set(APP_BIN_DIR ${CMAKE_CURRENT_LIST_DIR}/bin) -set(APP_BASE ${PROJECT_NAME}) -set(XE_FACT ${APP_BIN_DIR}/fact/${APP_BASE}_fact.xe) -set(XE_F1 ${APP_BIN_DIR}/f1_music_uac2/${APP_BASE}_f1_music_uac2.xe) -set(XE_F3F4 ${APP_BIN_DIR}/f3_f4_fps_uac2/${APP_BASE}_f3_f4_fps_uac2.xe) -set(XE_F5 ${APP_BIN_DIR}/f5_music_uac1/${APP_BASE}_f5_music_uac1.xe) -set(XE_F6F7 ${APP_BIN_DIR}/f6_f7_fps_uac1/${APP_BASE}_f6_f7_fps_uac1.xe) -set(LOADER_OBJ ${CMAKE_CURRENT_LIST_DIR}/loader.o) -set(TARGET_XN ${CMAKE_CURRENT_LIST_DIR}/src/core/synido.xn) - -# factory__.bin — full factory image (base + 4 upgrade slots) -add_custom_target(factory_bin - COMMAND ${CMAKE_COMMAND} -E echo "xflash ${XE_FACT} --loader ${LOADER_OBJ} --upgrade 1 ${XE_F3F4} --upgrade 2 ${XE_F5} --upgrade 3 ${XE_F1} --upgrade 4 ${XE_F6F7} -o ${CMAKE_CURRENT_LIST_DIR}/factory_${APP_BASE}_${FW_VERSION}.bin" - COMMAND xflash ${XE_FACT} --loader ${LOADER_OBJ} - --upgrade 2 ${XE_F5} - --upgrade 3 ${XE_F1} - --upgrade 1 ${XE_F3F4} - --upgrade 4 ${XE_F6F7} - -o ${CMAKE_CURRENT_LIST_DIR}/factory_${APP_BASE}_${FW_VERSION}.bin - #DEPENDS f1_music_uac2 f3_f4_fps_uac2 f5_music_uac1 f6_f7_fps_uac1 - COMMENT "Generating factory image: factory_${APP_BASE}_${FW_VERSION}.bin" - VERBATIM -) - -# update__.bin — DFU upgrade package (upgrade slots only) -add_custom_target(update_bin - COMMAND ${CMAKE_COMMAND} -E echo "xflash --factory-version 15.2 --target-file ${TARGET_XN} --upgrade 1 ${XE_F3F4} --upgrade 2 ${XE_F5} --upgrade 3 ${XE_F1} --upgrade 4 ${XE_F6F7} -o ${CMAKE_CURRENT_LIST_DIR}/update_${APP_BASE}_${FW_VERSION}.bin" - COMMAND xflash --factory-version 15.2 --target-file ${TARGET_XN} - --upgrade 2 ${XE_F5} - --upgrade 3 ${XE_F1} - --upgrade 1 ${XE_F3F4} - --upgrade 4 ${XE_F6F7} - -o ${CMAKE_CURRENT_LIST_DIR}/update_${APP_BASE}_${FW_VERSION}.bin - #DEPENDS f1_music_uac2 f3_f4_fps_uac2 f5_music_uac1 f6_f7_fps_uac1 - COMMENT "Generating update image: update_${APP_BASE}_${FW_VERSION}.bin" - VERBATIM -) diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/gen_factory.bat b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/gen_factory.bat deleted file mode 100644 index 22327bb..0000000 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/gen_factory.bat +++ /dev/null @@ -1,3 +0,0 @@ -xflash bin/fact/app_usb_aud_sy102_fact.xe --loader loader.o --upgrade 2 bin/f5_music_uac1/app_usb_aud_sy102_f5_music_uac1.xe --upgrade 3 bin/f1_music_uac2/app_usb_aud_sy102_f1_music_uac2.xe --upgrade 1 bin/f3_f4_fps_uac2/app_usb_aud_sy102_f3_f4_fps_uac2.xe --upgrade 4 bin/f6_f7_fps_uac1/app_usb_aud_sy102_f6_f7_fps_uac1.xe -o %1 - -rem --upgrade 4 bin/f6_f7_fps_uac1/app_usb_aud_sy102_f6_f7_fps_uac1.xe diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/gen_update.bat b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/gen_update.bat deleted file mode 100644 index 707adfd..0000000 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/gen_update.bat +++ /dev/null @@ -1 +0,0 @@ -xflash --factory-version 15.3 --target-file src/core/synido.xn --upgrade 2 bin/f5_music_uac1/app_usb_aud_sy102_f5_music_uac1.xe --upgrade 3 bin/f1_music_uac2/app_usb_aud_sy102_f1_music_uac2.xe --upgrade 1 bin/f3_f4_fps_uac2/app_usb_aud_sy102_f3_f4_fps_uac2.xe --upgrade 4 bin/f6_f7_fps_uac1/app_usb_aud_sy102_f6_f7_fps_uac1.xe -o %1 diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/loader.o b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/loader.o deleted file mode 100644 index b014a4b..0000000 Binary files a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/loader.o and /dev/null differ diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/.DS_Store b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/.DS_Store deleted file mode 100644 index 0bf1edb..0000000 Binary files a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/.DS_Store and /dev/null differ diff --git a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/core/synido.xn b/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/core/synido.xn deleted file mode 100644 index aa57bfc..0000000 --- a/sw_usb_audio/app_usb_aud_phaten_golden_6ch/src/core/synido.xn +++ /dev/null @@ -1,89 +0,0 @@ - - - Board - xcore.ai MC Audio Board - - - tileref tile[2] - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -