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 "xua_hid_report.h"
|
||||||
#include "nau88c21.h"
|
#include "nau88c21.h"
|
||||||
#include "tile1_clk.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"
|
#include "dfu_upgrade.h"
|
||||||
#if MQA_EN
|
#if MQA_EN
|
||||||
#include "MQA_XMOS.h"
|
#include "MQA_XMOS.h"
|
||||||
@@ -219,6 +222,11 @@ void switch_mode_by_c1_mode(unsigned c1_mode, unsigned force_reboot)
|
|||||||
(void)force_reboot;
|
(void)force_reboot;
|
||||||
{
|
{
|
||||||
unsigned fps_val = (c1_mode == 4) ? 1u : 0u;
|
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);
|
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);
|
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;
|
extern unsigned g_adc_loop;
|
||||||
#if C1_DFS_EN
|
#if C1_DFS_EN
|
||||||
extern unsigned g_tile1_audio_ready; /* tile1_clk.xc: state-machine output */
|
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
|
#endif
|
||||||
#if UAC1
|
#if UAC1
|
||||||
#pragma unsafe arrays
|
#pragma unsafe arrays
|
||||||
@@ -74,6 +75,30 @@ void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudi
|
|||||||
buffer_exchange((chanend)uc_br_data, sampsFromUsbToAudio, sampsFromAudioToUsb, 2);
|
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)
|
if (dnr_enable)
|
||||||
{
|
{
|
||||||
dnr_exchange_buffer((int32_t *)&sampsFromAudioToUsb[0]);
|
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_buf_ready = 0;
|
||||||
unsigned fps_write_pos = 0;
|
unsigned fps_write_pos = 0;
|
||||||
unsigned fps_process_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 副本 */
|
/* 改动原因:tile0/tile1 内存不共享;game/level/使能/fps_eq 须经 channel sync 写入 tile1 副本 */
|
||||||
unsigned g_fps_game_select = 1;
|
unsigned g_fps_game_select = 1;
|
||||||
unsigned g_fps_level_select = 2;
|
unsigned g_fps_level_select = 2;
|
||||||
@@ -239,12 +243,36 @@ static void fps_xmos_modules_init(void)
|
|||||||
fps_apply_module_enable();
|
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()
|
void fps1_dsp_proc_task()
|
||||||
{
|
{
|
||||||
fps_xmos_modules_init();
|
fps_xmos_modules_init();
|
||||||
|
|
||||||
while (1)
|
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;
|
unsigned ready;
|
||||||
|
|
||||||
GET_SHARED_GLOBAL(ready, fps_buf_ready);
|
GET_SHARED_GLOBAL(ready, fps_buf_ready);
|
||||||
|
|||||||
@@ -55,6 +55,12 @@
|
|||||||
#define C1_TILE1_ACTIVE 0
|
#define C1_TILE1_ACTIVE 0
|
||||||
#define C1_TILE1_PENDING_SHUTDOWN 1
|
#define C1_TILE1_PENDING_SHUTDOWN 1
|
||||||
#define C1_TILE1_CLOCKED_DOWN 2
|
#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]. */
|
/* One-time enable of the tile[1] core divider output path. MUST run on tile[1]. */
|
||||||
void c1_tile1_clk_enable(void);
|
void c1_tile1_clk_enable(void);
|
||||||
|
|||||||
@@ -16,8 +16,11 @@
|
|||||||
unsigned g_audio_streaming = 0; /* 1 while USB audio is streaming */
|
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_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_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_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]) ---- */
|
/* ---- state (tile[0]) ---- */
|
||||||
static unsigned c1_tile1_state = C1_TILE1_ACTIVE;
|
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 */
|
c1_tile1_clock_up(); /* restore-before-send */
|
||||||
unsigned t; c1_tile1_tmr :> t;
|
unsigned t; c1_tile1_tmr :> t;
|
||||||
c1_tile1_tmr when timerafter(t + C1_TILE1_CLK_SETTLE_TICKS) :> void;
|
c1_tile1_tmr when timerafter(t + C1_TILE1_CLK_SETTLE_TICKS) :> void;
|
||||||
SET_SHARED_GLOBAL(g_tile1_audio_ready, 1);
|
/* Request FPS pipeline flush (clear stale buffer + algorithm)
|
||||||
c1_tile1_state = C1_TILE1_ACTIVE;
|
* 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
|
else
|
||||||
{
|
{
|
||||||
@@ -161,6 +166,20 @@ void c1_tile1_power_tick(void)
|
|||||||
c1_tile1_clock_down(g_tile1_lp_div);
|
c1_tile1_clock_down(g_tile1_lp_div);
|
||||||
}
|
}
|
||||||
break;
|
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