fix(c1): flush FPS pipeline + mute output to remove FPS on/off pop
FPS buffer (dsp_fps_out_buf) and algorithm delay-line state held stale samples across FPS-off periods, causing an occasional pop on re-enable. On CLOCKED_DOWN->ACTIVE, request fps1 to flush (zero buffers, reset positions, re-init library) before g_tile1_audio_ready=1, via a new FLUSHING state with req/ack handshake. Also arm a ~30ms proactive output mute in UserBufferManagement on every FPS toggle (g_fps_switch_mute) to mask the path-switch discontinuity in both directions. All C1_DFS_EN-guarded; factory unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user