fix(band): revert soft-stop; ignore round/side keys on mode select

Restore hard AutoBandTop1_Stop and drop eaten-start/followup tick-gate.
Keep API stubs for callers. Block TM1617 round keys and reinforce side-key/BLE gates while InModeFlag is false.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-09-24 20:08:27 +08:00
parent 18394d0b9b
commit 67a5fe5d60
4 changed files with 49 additions and 135 deletions

View File

@ -79,6 +79,8 @@ uint8_t app_tm1617_scan_key(void)
{
key_last = key;
LOG_I("KEY", "tm1617 key=%u", (unsigned)key);
/* ????????????????/?????? */
if (InModeFlag)
TM1617_Handle(key);
return key;
}

View File

@ -119,7 +119,7 @@ static void Band_StartLocalRetry(uint8_t midi, uint8_t *notes, uint8_t n);
static void Band_StartPieceRetry(uint8_t *notes, uint8_t n);
static void Band_SyncStartRetry(void);
static void Band_StopBeforeColdStart(void);
/* 1=喂 AutoBand 1ms 节拍;停止键软停时清 0,避免库继续出音且不必 Stop */
/* 1=喂 AutoBand 1ms 节拍(软停曾清 0;已回退硬停,保持为 1) */
uint8_t Band_TickGate = 1;
uint8_t AutoCloseTime = 15;
@ -795,10 +795,7 @@ static void Univ_StartWithPreamble(uint8_t *notes, uint8_t n)
mGuiData[GUI_PLAY_SECTION].Current = 0; /* 段落后续固定进段落1 */
if (session_was_on)
{
Band_MarkRealStop();
AutoBandTop1_Stop();
}
else
Band_StopBeforeColdStart();
@ -971,6 +968,9 @@ void ADC_IN1_KEY_Handle(uint8_t key,bool on)
void TM1617_Handle(uint8_t key)
{
/* 模式选择页:圆形段落键不得假触摸点选模式行,也不得改段落 */
if (!InModeFlag)
return;
/* 调音台:段落数字键不得注入假触摸(会打在推子底部,把数值打到 1) */
if (CurrUIProcress == UI_Mixer_Process)
return;
@ -1140,13 +1140,12 @@ void FreeMode_PollEvenBeatStop(void)
static void Band_MarkCleanStopped(void);
static void Band_StopBeforeColdStart(void);
/* 扫描线程只投递:真正 Stop 在 MainTask,与拨片 Pick 同队列串行。
* 若在扫描线程同步 Stop,会与 MainTask 拨片并发:Stop 期间 StartFlag 仍为 1,
* 拨片误判 Band_IsPlaying → 只改和弦不起奏,需再等 ~0.5s 才能拨响。 */
/* 扫描线程只投递:真正 Stop 在 MainTask,与拨片 Pick 同队列串行,
* 避免扫描线程同步 Stop 与 MainTask 拨片并发。 */
void Stop_AutoBand_Request(void)
{
/* ID=1:停止键软停(静音+停节拍,不调库 Stop,否则随后 Start 被吞) */
MainTask_Sendmsg(MSG_ID_STOP_BAND, 1, 0, 0);
/* ID=0:硬停(AutoBandTop1_Stop)。软停已回退:库态与 UI 态必须一致。 */
MainTask_Sendmsg(MSG_ID_STOP_BAND, 0, 0, 0);
}
static void Band_AllNotesOff(void)
@ -1161,30 +1160,18 @@ static void Band_AllNotesOff(void)
}
}
/* 停止键/偶拍收束:立刻静音并暂停库 1ms 节拍,不调用 AutoBandTop1_Stop。
* 库 Stop 之后第一个 Start 必被吞,立即拨片无声。 */
/* 兼容旧入口:一律硬停,不再做「只静音不停库」的软停。 */
void Stop_AutoBand_Soft(void)
{
Expert_RememberStopContext();
FreeMode_CancelEvenBeatStop();
Band_CancelStartFollowup();
Band_TickGate = 0;
StartFlag = 0;
KEY_ID_1629 = 0;
Band_MarkCleanStopped();
Band_AllNotesOff();
s_outro_led_all_green = 0;
s_univ_preamble_flags = 0;
for (uint8_t i = 0; i < 4; i++) key_toggle[i] = 0;
Stop_AutoBand();
}
void Stop_AutoBand()
void Stop_AutoBand(void)
{
Expert_RememberStopContext();
FreeMode_CancelEvenBeatStop();
/* 先清播放态,再 Stop/AllNoteOff,避免处理期间其它路径误判在播 */
Band_CancelStartFollowup();
Band_TickGate = 1;
Band_TickGate = 1; /* 始终喂库节拍;软停曾清 0,已撤销 */
StartFlag = 0;
KEY_ID_1629 = 0;
Band_MarkCleanStopped();
@ -1192,7 +1179,6 @@ void Stop_AutoBand()
Band_AllNotesOff();
s_outro_led_all_green = 0;
s_univ_preamble_flags = 0;
//tick_ever_positive = false;
for (uint8_t i = 0; i < 4; i++) key_toggle[i] = 0;
}
@ -1235,93 +1221,33 @@ uint8_t BP_Transpose(uint8_t key_idx)
* 供专业模式指板键区分「播放中换和弦」与「段落结束后续奏重启」。 */
static uint64_t s_play_tick_last = 0;
static uint32_t s_play_adv_tick = 0; /* 最近一次 PlayTick 前进的系统 tick */
/* 1=下次冷启动前需先 Stop 清库残留;0=Stop_AutoBand 已停干净,勿再 Stop。 */
static uint8_t s_band_need_stop_before_start = 1;
/* 库特性(RTT 实测):真实停播后第一个完整 Start 被吞
* (StartFlag=1 但 PlayTick 归 0 且不前进);第二次完整 Start 才起奏。
* 哑 Start 不能用来吸收,会占住会话把真正起奏也吃掉。 */
static uint8_t s_band_eater_armed = 0;
static void Band_MarkCleanStopped(void)
{
s_play_tick_last = 0;
s_play_adv_tick = 0;
s_band_need_stop_before_start = 0;
}
/* 一切可能真实停播的 Stop 路径调用:武装「停后首启被吞」标记。
* 宁可误武装(哑 Start 未被吞也只会被紧随的真实 Start 立刻重触发,无感),
* 不可漏武装(漏了用户就要拨两次)。 */
/* 软停/吞噬器/补发起奏已撤销:保留空实现,避免其它调用点改动。 */
void Band_MarkRealStop(void)
{
s_band_eater_armed = 1;
}
/* 停后首个完整 Start 会被库吞掉(PlayTick 归 0 且不前进)。
* 同一次调用里连发两次会被合成一次,仍然被吞;必须隔一拍再 Start。
* 停后再起:先发本次 Start(可能被吞),再 20ms 后由 MainTask 补发一次。 */
static uint8_t s_start_followup_pending;
static uint8_t s_start_followup_force; /* 1=补发中,禁止走「在播只换和弦」 */
static rt_timer_t s_start_followup_timer;
static uint8_t Band_TakeEaterArmed(void)
{
uint8_t armed = s_band_eater_armed;
s_band_eater_armed = 0;
return armed;
}
static void Band_StartFollowupTimerCb(void *parameter)
{
(void)parameter;
MainTask_Sendmsg(MSG_ID_START_FOLLOWUP, 0, 0, 0);
}
void Band_CancelStartFollowup(void)
{
s_start_followup_pending = 0;
s_start_followup_force = 0;
if (s_start_followup_timer != RT_NULL)
rt_timer_stop(s_start_followup_timer);
}
uint8_t Band_TakeStartFollowup(void)
{
uint8_t pending = s_start_followup_pending;
s_start_followup_pending = 0;
return pending;
return 0;
}
void Band_BeginStartFollowup(void)
{
s_start_followup_force = 1;
}
void Band_EndStartFollowup(void)
{
s_start_followup_force = 0;
}
static void Band_PostStartFollowup(void)
{
if (s_start_followup_pending)
return;
s_start_followup_pending = 1;
if (s_start_followup_timer == RT_NULL)
{
s_start_followup_timer = rt_timer_create("bandFu",
Band_StartFollowupTimerCb,
RT_NULL,
500, /* 库吞掉停后首启;约 0.5s 后再 Start 才生效 */
RT_TIMER_FLAG_ONE_SHOT);
}
if (s_start_followup_timer != RT_NULL)
{
rt_timer_stop(s_start_followup_timer);
rt_timer_start(s_start_followup_timer);
}
else
MainTask_Sendmsg(MSG_ID_START_FOLLOWUP, 0, 0, 0);
}
static void Band_StartLocalRetry(uint8_t midi, uint8_t *notes, uint8_t n)
@ -1345,24 +1271,17 @@ static void Band_SyncStartRetry(void)
void Band_AbsorbEatenStart(void)
{
(void)0;
}
/* 冷启动前按需 Stop:已由停止键停干净则跳过 */
/* 冷启动前一律 Stop 清库残留(与软停前回退一致:不再「已软停则跳过」)。 */
static void Band_StopBeforeColdStart(void)
{
if (s_band_need_stop_before_start)
{
Band_MarkRealStop(); /* 若库仍在播,此 Stop 会武装吞噬器,由紧随的吸收处理 */
AutoBandTop1_Stop();
}
s_band_need_stop_before_start = 1;
}
/* 诊断钩子:强制下次冷启动前先 Stop(RTT `band dirty`) */
/* 诊断钩子:兼容 RTT `band dirty`(现冷启动本就会 Stop,保留空操作)。 */
void Band_ForceStopBeforeStart(void)
{
s_band_need_stop_before_start = 1;
}
void Band_TrackPlayTick(void)
@ -1411,8 +1330,7 @@ static void Expert_StartWithChord(uint8_t chord_id, uint8_t *midi_note_buff,
if (mGuiData[GUI_AUTOBAND_SW].Current)
{
/* 会话内勿 Stop:段落圆键切变奏后指板续奏若 Stop 会直接关音;
* 与节奏类型一致,仅换和弦/重进当前段。
* 冷启动:仅当库可能仍有残留时才 Stop;停止键后已停干净则跳过。 */
* 与节奏类型一致,仅换和弦/重进当前段。冷启动先 Stop 清残留。 */
if (!session)
Band_StopBeforeColdStart();
AutoBandTop1_SetLoopMode(0, 1);
@ -1704,10 +1622,9 @@ void Pick_Handle()
app_tm1629_all_on(LED_COLOR_G);
if (mGuiData[GUI_AUTOBAND_SW].Current)
{
/* 播放中再拨:Stop+Start 重启段落;停止键后已停则勿二次 Stop。
* 此 Stop 若真实停播会武装库的吞噬器 → 紧随吸收 */
/* 播放中再拨:Stop+Start 重启段落;冷启动前也先 Stop 清残留 */
if (session_was_on)
{ Band_MarkRealStop(); AutoBandTop1_Stop(); }
AutoBandTop1_Stop();
else
Band_StopBeforeColdStart();
AutoBandTop1_SetLoopMode(0,1);
@ -1738,8 +1655,7 @@ void Pick_Handle()
case 1:
/* 专业:播放中再拨片不 Stop+Start(库会把未消费的 Start 标志清掉 → 静音);
* 本地曲播放中仅换和弦音续播,节奏路径 piece 重启本就安全。 */
if (!s_start_followup_force &&
Band_IsPlaying() && mGuiData[GUI_AUTOBAND_SW].Current)
if (Band_IsPlaying() && mGuiData[GUI_AUTOBAND_SW].Current)
{
Accomp_UpdatePlayingChord();
LOG_I("pick", "expert/pick skip restart playing chord=%u", (unsigned)chord_id);

View File

@ -32,8 +32,8 @@
#define MSG_ID_BLE_UI_REFRESH 28
#define MSG_ID_BLE_STYLE_SET 29
#define MSG_ID_LOW_BAT_TIP 30
#define MSG_ID_STOP_BAND 31 /* 停止键/收束:MainTask 串行 Stop,避免与拨片起奏竞态 */
#define MSG_ID_START_FOLLOWUP 32 /* 停后首启被库吞:下一拍补发真正起奏 */
#define MSG_ID_STOP_BAND 31 /* 停止键/收束:MainTask 串行硬停 */
#define MSG_ID_START_FOLLOWUP 32 /* 保留:软停补发已撤销,不再使用 */
#define GPIO_PIN_RESET false
#define GPIO_PIN_SET true
@ -241,16 +241,16 @@ void TM1629_Handle(uint8_t key);
void Pick_Handle();
void Loop_Light_Handle(uint8_t measure);
void Stop_AutoBand(void);
void Stop_AutoBand_Soft(void);
void Stop_AutoBand_Soft(void); /* 兼容:等同 Stop_AutoBand */
extern uint8_t Band_TickGate;
void Stop_AutoBand_Request(void);
void Stop_AutoBand_Request(void); /* 投递 MSG_ID_STOP_BAND → 硬停 */
uint8_t Band_IsPlaying(void);
void Band_ForceStopBeforeStart(void);
void Band_AbsorbEatenStart(void);
void Band_MarkRealStop(void);
uint8_t Band_TakeStartFollowup(void);
void Band_BeginStartFollowup(void);
void Band_EndStartFollowup(void);
void Band_ForceStopBeforeStart(void); /* 兼容空操作 */
void Band_AbsorbEatenStart(void); /* 兼容空操作 */
void Band_MarkRealStop(void); /* 兼容空操作 */
uint8_t Band_TakeStartFollowup(void); /* 兼容:恒返回 0 */
void Band_BeginStartFollowup(void); /* 兼容空操作 */
void Band_EndStartFollowup(void); /* 兼容空操作 */
void BL_Set_led(uint8_t Led, uint8_t chord_type);

View File

@ -111,22 +111,12 @@ void TaskMainThread_entry(void* parameter)
Pick_Handle();
break;
case MSG_ID_STOP_BAND:
/* 停止键/偶拍收束:MainTask 串行硬停(已撤销软停) */
if (!System_LowBat_IsActive())
{
if (xEvent.ID)
Stop_AutoBand_Soft();
else
Stop_AutoBand();
}
break;
case MSG_ID_START_FOLLOWUP:
/* 停后补发:强制再 Start,勿因 NotifyPlayActivity 误判「在播」只换和弦 */
if (!System_LowBat_IsActive() && InModeFlag && Band_TakeStartFollowup())
{
Band_BeginStartFollowup();
Pick_Handle();
Band_EndStartFollowup();
}
/* 软停补发路径已撤销;保留 case 以免旧固件消息号撞车 */
break;
case MSG_ID_LOOP_LIGHT:
if (!System_LowBat_IsActive())
@ -179,8 +169,14 @@ void TaskBTHandleThread_entry(void* parameter)
case MSG_ID_PITCH_OFFSET : BT_Pitch_offset_map(xEvent.ID,xEvent.HiByte); break;
case MSG_ID_CHORD_OFFSET : BT_Chord_offset_map(xEvent.ID,xEvent.HiByte); break;
/* BLE section / intro-outro-end commands (06 xx, 04 07) */
case MSG_ID_ADCIN1KEY : ADC_IN1_KEY_Handle((uint8_t)xEvent.ID, true); break;
case MSG_ID_KEY_1617 : TM1617_Handle((uint8_t)xEvent.ID); break;
case MSG_ID_ADCIN1KEY:
if (InModeFlag)
ADC_IN1_KEY_Handle((uint8_t)xEvent.ID, true);
break;
case MSG_ID_KEY_1617:
if (InModeFlag)
TM1617_Handle((uint8_t)xEvent.ID);
break;
}
}
}