diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiohw.xc b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiohw.xc index f6610ae..127c344 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiohw.xc +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/audiohw.xc @@ -21,6 +21,9 @@ #include "xua_hid_report.h" #include "nau88c21.h" #include "tile1_clk.h" +#if C1_DFS_EN +extern unsigned g_fps_switch_mute; /* tile1_clk.xc: proactive mute on FPS toggle */ +#endif #include "dfu_upgrade.h" #if MQA_EN #include "MQA_XMOS.h" @@ -219,6 +222,11 @@ void switch_mode_by_c1_mode(unsigned c1_mode, unsigned force_reboot) (void)force_reboot; { unsigned fps_val = (c1_mode == 4) ? 1u : 0u; + /* Mute the output around the path switch so neither the stale FPS + * buffer/algorithm tail (off->on) nor the FPS<->bypass discontinuity + * (on<->off) is audible. UserBufferManagement clears the mute after + * ~C1_SWITCH_MUTE_SAMPLES. */ + SET_SHARED_GLOBAL(g_fps_switch_mute, 1); SET_SHARED_GLOBAL(g_3d_fps, fps_val); debug_printf("switch_mode_by_c1_mode: c1_mode=%d -> g_3d_fps=%d (no reboot)\n", c1_mode, fps_val); } diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/extra_i2s.xc b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/extra_i2s.xc index d76fa90..2cf7f48 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/extra_i2s.xc +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/extra_i2s.xc @@ -44,6 +44,7 @@ extern unsigned g_dnr_enable; extern unsigned g_adc_loop; #if C1_DFS_EN extern unsigned g_tile1_audio_ready; /* tile1_clk.xc: state-machine output */ +extern unsigned g_fps_switch_mute; /* tile1_clk.xc: proactive mute on FPS toggle */ #endif #if UAC1 #pragma unsafe arrays @@ -74,6 +75,30 @@ void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudi buffer_exchange((chanend)uc_br_data, sampsFromUsbToAudio, sampsFromAudioToUsb, 2); } +#if C1_DFS_EN + /* Proactive output mute around FPS on/off: zeroes the playback path for + * ~C1_SWITCH_MUTE_SAMPLES after each toggle, masking the path-switch pop + * and the stale FPS data tail until the flushed pipeline refills. */ + { + static unsigned sw_mute_samps = 0; + static unsigned prev_sw_mute = 0; + unsigned sw_mute; + GET_SHARED_GLOBAL(sw_mute, g_fps_switch_mute); + if (sw_mute) + { + if (!prev_sw_mute) sw_mute_samps = 0; /* re-armed: restart count */ + sampsFromUsbToAudio[0] = 0; + sampsFromUsbToAudio[1] = 0; + if (++sw_mute_samps >= C1_SWITCH_MUTE_SAMPLES) + { + SET_SHARED_GLOBAL(g_fps_switch_mute, 0); + sw_mute_samps = 0; + } + } + prev_sw_mute = sw_mute; + } +#endif + if (dnr_enable) { dnr_exchange_buffer((int32_t *)&sampsFromAudioToUsb[0]); diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/fps_wrapper.c b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/fps_wrapper.c index c82ef23..7c0c530 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/fps_wrapper.c +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/fps_wrapper.c @@ -29,6 +29,10 @@ short __attribute__((aligned (4))) dsp_fps_out_buf[FPS_SLOT_NUM][FPS_PLANAR_SAMP unsigned fps_buf_ready = 0; unsigned fps_write_pos = 0; unsigned fps_process_pos = 0; +/* Flush handshake (tile0 -> tile1): tile0 requests a flush before resuming audio + * after FPS re-enable; fps1 clears stale buffer + algorithm state, then acks. */ +unsigned g_fps_flush_req = 0; +unsigned g_fps_flush_ack = 0; /* 改动原因:tile0/tile1 内存不共享;game/level/使能/fps_eq 须经 channel sync 写入 tile1 副本 */ unsigned g_fps_game_select = 1; unsigned g_fps_level_select = 2; @@ -239,12 +243,36 @@ static void fps_xmos_modules_init(void) fps_apply_module_enable(); } +/* Clear stale FPS pipeline state (zero the triple-buffer, reset positions, and + * re-initialise the algorithm library to flush its delay-line state). Called by + * fps1 on a flush request from tile0 so old samples/state are not played out. */ +static void fps_flush_pipeline(void) +{ + memset(dsp_fps_input_buf, 0, sizeof(dsp_fps_input_buf)); + memset(dsp_fps_out_buf, 0, sizeof(dsp_fps_out_buf)); + SET_SHARED_GLOBAL(fps_write_pos, 0); + SET_SHARED_GLOBAL(fps_process_pos, 0); + SET_SHARED_GLOBAL(fps_buf_ready, 0); + fps_xmos_modules_init(); /* reset algorithm library (delay lines etc.) */ +} + void fps1_dsp_proc_task() { fps_xmos_modules_init(); while (1) { + /* Service a flush request from tile0 (FPS re-enable): clear stale data + * before audio resumes, so old samples/state are not played out. */ + unsigned flush_req; + GET_SHARED_GLOBAL(flush_req, g_fps_flush_req); + if (flush_req) + { + fps_flush_pipeline(); + SET_SHARED_GLOBAL(g_fps_flush_req, 0); + SET_SHARED_GLOBAL(g_fps_flush_ack, 1); + } + unsigned ready; GET_SHARED_GLOBAL(ready, fps_buf_ready); diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.h b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.h index 47424e0..781810c 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.h +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.h @@ -55,6 +55,12 @@ #define C1_TILE1_ACTIVE 0 #define C1_TILE1_PENDING_SHUTDOWN 1 #define C1_TILE1_CLOCKED_DOWN 2 +#define C1_TILE1_FLUSHING 3 /* waiting for FPS pipeline flush before audio resume */ + +/* Duration of the proactive output mute around an FPS on/off toggle, in audio + * samples (@ 48 kHz). Masks the path-switch discontinuity and covers the FPS + * pipeline flush + 1-frame refill. 2880 ~= 60 ms. */ +#define C1_SWITCH_MUTE_SAMPLES 2880 /* One-time enable of the tile[1] core divider output path. MUST run on tile[1]. */ void c1_tile1_clk_enable(void); diff --git a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.xc b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.xc index 82b6372..20d29de 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.xc +++ b/sw_usb_audio/app_usb_aud_fosi_c1/src/extensions/tile1_clk.xc @@ -16,8 +16,11 @@ unsigned g_audio_streaming = 0; /* 1 while USB audio is streaming */ unsigned g_tile1_audio_ready = 0; /* state-machine output: 1 => tile[0] may send to tile[1] */ unsigned g_tile1_lp_div = C1_TILE1_LP_XCORE_DIV_DEFAULT; +unsigned g_fps_switch_mute = 0; /* set on FPS toggle: UserBufferManagement zeroes output for C1_SWITCH_MUTE_SAMPLES */ extern unsigned g_3d_fps; /* defined in extra_i2s.xc: 0=bypass, 1=FPS */ +extern unsigned g_fps_flush_req; /* defined in fps_wrapper.c: flush FPS pipeline before audio resume */ +extern unsigned g_fps_flush_ack; /* defined in fps_wrapper.c: ack flush complete */ /* ---- state (tile[0]) ---- */ static unsigned c1_tile1_state = C1_TILE1_ACTIVE; @@ -152,8 +155,10 @@ void c1_tile1_power_tick(void) c1_tile1_clock_up(); /* restore-before-send */ unsigned t; c1_tile1_tmr :> t; c1_tile1_tmr when timerafter(t + C1_TILE1_CLK_SETTLE_TICKS) :> void; - SET_SHARED_GLOBAL(g_tile1_audio_ready, 1); - c1_tile1_state = C1_TILE1_ACTIVE; + /* Request FPS pipeline flush (clear stale buffer + algorithm) + * BEFORE audio resumes, so old samples are not played out. */ + SET_SHARED_GLOBAL(g_fps_flush_req, 1); + c1_tile1_state = C1_TILE1_FLUSHING; } else { @@ -161,6 +166,20 @@ void c1_tile1_power_tick(void) c1_tile1_clock_down(g_tile1_lp_div); } break; + + case C1_TILE1_FLUSHING: + { + /* Wait for fps1 to finish flushing the FPS pipeline, then resume audio. */ + unsigned ack; + GET_SHARED_GLOBAL(ack, g_fps_flush_ack); + if (ack) + { + SET_SHARED_GLOBAL(g_fps_flush_ack, 0); + SET_SHARED_GLOBAL(g_tile1_audio_ready, 1); + c1_tile1_state = C1_TILE1_ACTIVE; + } + } + break; } }