fix(band): soft-stop on stop key so immediate pick can start

Stop key no longer calls AutoBandTop1_Stop (library eats the next Start). Soft mute + tick gate runs on MainTask; cold start skips redundant Stop when already clean.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-09-22 17:42:03 +08:00
parent 4b8e676033
commit d00f570e2f
5 changed files with 272 additions and 33 deletions

View File

@ -315,8 +315,31 @@ uint8_t app_log_try_command(const char *cmd)
"FLASH_BIN3_GO", "FLASH_BIN3_OK\n"); "FLASH_BIN3_GO", "FLASH_BIN3_OK\n");
return 1U; return 1U;
} }
if (strncmp(cmd, "tone status", 11) == 0) { /* bp pick:经 MainTask 队列模拟真实拨片(与停止键串行,复现停后立即拨) */
uint8_t hdr[16]; if (strncmp(cmd, "bp pick", 7) == 0) {
MainTask_Sendmsg(MSG_ID_BP_KEY, 1 /*PICK_STATE_DOWN*/, 0, 0);
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "BP_PICK_OK\n");
return 1U;
}
/* band dirty:强制下次冷启动先 Stop(验证 Stop→Start 紧挨是否必被吞) */
if (strncmp(cmd, "band dirty", 10) == 0) {
Band_ForceStopBeforeStart();
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "BAND_DIRTY_OK\n");
return 1U;
}
/* band state:停/起时序诊断 — StartFlag/PlayTick/是否在播 */
if (strncmp(cmd, "band state", 10) == 0) {
char line[96];
snprintf(line, sizeof(line),
"BAND_STATE start=%u playing=%u playtick=%lu ms=%lu\n",
(unsigned)StartFlag,
(unsigned)Band_IsPlaying(),
(unsigned long)AutoBandTop1_GetCurrentPlayTick(),
(unsigned long)app_log_ms());
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
return 1U;
}
if (strncmp(cmd, "tone status", 11) == 0) {uint8_t hdr[16];
uint8_t name[40]; uint8_t name[40];
uint8_t cur_hdr[16]; uint8_t cur_hdr[16];
char *pname; char *pname;

View File

@ -66,6 +66,10 @@ Fader_t faders[4] = {
const Touch_AreaTypeDef Touch_Areas[] = const Touch_AreaTypeDef Touch_Areas[] =
{ {
/* 状态栏蓝牙:点击开关(热区略大于图标) */
{ UI0902_BT_HIT_X0, UI0902_BT_HIT_X1, UI0902_BT_HIT_Y0, UI0902_BT_HIT_Y1,
0, 0, GUI_BL_SW, NULL },
/* Xmin,Xmax,Ymin,Ymax, Flag, Value, ID, action — 与 UI_Label 网格同步 */ /* Xmin,Xmax,Ymin,Ymax, Flag, Value, ID, action — 与 UI_Label 网格同步 */
{ 12, 64, UI0902_CARD_Y, UI0902_CARD_Y + UI0902_CARD_H - 1, -1, 0, GUI_TRANSPOSE, NULL }, { 12, 64, UI0902_CARD_Y, UI0902_CARD_Y + UI0902_CARD_H - 1, -1, 0, GUI_TRANSPOSE, NULL },
{ 65, 116, UI0902_CARD_Y, UI0902_CARD_Y + UI0902_CARD_H - 1, 1, 0, GUI_TRANSPOSE, NULL }, { 65, 116, UI0902_CARD_Y, UI0902_CARD_Y + UI0902_CARD_H - 1, 1, 0, GUI_TRANSPOSE, NULL },
@ -107,6 +111,14 @@ bool PressFlag = 0;
void Return_Light_buff(uint8_t key_id_1629); void Return_Light_buff(uint8_t key_id_1629);
uint8_t Band_IsPlaying(void); uint8_t Band_IsPlaying(void);
void Band_AbsorbEatenStart(void);
void Band_MarkRealStop(void);
void Band_CancelStartFollowup(void);
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);
/* 1=喂 AutoBand 1ms 节拍;停止键软停时清 0,避免库继续出音且不必 Stop */
uint8_t Band_TickGate = 1;
uint8_t AutoCloseTime = 15; uint8_t AutoCloseTime = 15;
uint16_t mAutoPowerOffCount = 15 * 60; uint16_t mAutoPowerOffCount = 15 * 60;
@ -672,7 +684,7 @@ void AutoBand_StartOutro(void)
app_tm1629_all_on(LED_COLOR_G); app_tm1629_all_on(LED_COLOR_G);
/* 尾奏起奏前补发主音量,保证与图标一致 */ /* 尾奏起奏前补发主音量,保证与图标一致 */
MasterVolume_Reapply(); MasterVolume_Reapply();
AutoBandTop1_SyncStart(); Band_SyncStartRetry();
wk_delay_ms(50); wk_delay_ms(50);
note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff); note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff);
for(uint8_t i = 0;i < note_cnt;i++) for(uint8_t i = 0;i < note_cnt;i++)
@ -712,7 +724,7 @@ void ADC_IN1_KEY_Handle(uint8_t key,bool on)
/* 前奏/断奏起奏前补发主音量,保证与图标一致 */ /* 前奏/断奏起奏前补发主音量,保证与图标一致 */
MasterVolume_Reapply(); MasterVolume_Reapply();
//AutoBandTop1_Start(); //AutoBandTop1_Start();
AutoBandTop1_SyncStart(); Band_SyncStartRetry();
wk_delay_ms(50); wk_delay_ms(50);
note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff); note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff);
for(uint8_t i = 0;i < note_cnt;i++) for(uint8_t i = 0;i < note_cnt;i++)
@ -888,25 +900,62 @@ void FreeMode_PollEvenBeatStop(void)
if (tick_done || beat_done) if (tick_done || beat_done)
{ {
FreeMode_CancelEvenBeatStop(); FreeMode_CancelEvenBeatStop();
Stop_AutoBand(); Stop_AutoBand_Request();
LOG_I("pick", "free even-stop done beat=%d tick=%u beatDone=%u", LOG_I("pick", "free even-stop done beat=%d tick=%u beatDone=%u",
beat, (unsigned)tick_done, (unsigned)beat_done); beat, (unsigned)tick_done, (unsigned)beat_done);
} }
} }
static void Band_MarkCleanStopped(void);
static void Band_StopBeforeColdStart(void);
/* 扫描线程只投递:真正 Stop 在 MainTask,与拨片 Pick 同队列串行。
* 若在扫描线程同步 Stop,会与 MainTask 拨片并发:Stop 期间 StartFlag 仍为 1,
* 拨片误判 Band_IsPlaying → 只改和弦不起奏,需再等 ~0.5s 才能拨响。 */
void Stop_AutoBand_Request(void)
{
/* ID=1:停止键软停(静音+停节拍,不调库 Stop,否则随后 Start 被吞) */
MainTask_Sendmsg(MSG_ID_STOP_BAND, 1, 0, 0);
}
static void Band_AllNotesOff(void)
{
uint8_t i;
for (i = 0; i < 16; i++)
{
uint8_t AllNoteOff_buff[3] = {(uint8_t)(0xB0 | i), 0x7B, 0x00};
SendMidiDataToDreamDSP(AllNoteOff_buff, sizeof(AllNoteOff_buff));
uint8_t AllControlOff_buff[3] = {(uint8_t)(0xB0 | i), 0x79, 0x00};
SendMidiDataToDreamDSP(AllControlOff_buff, sizeof(AllControlOff_buff));
}
}
/* 停止键/偶拍收束:立刻静音并暂停库 1ms 节拍,不调用 AutoBandTop1_Stop。
* 库 Stop 之后第一个 Start 必被吞,立即拨片无声。 */
void Stop_AutoBand_Soft(void)
{
FreeMode_CancelEvenBeatStop();
Band_CancelStartFollowup();
Band_TickGate = 0;
StartFlag = 0;
KEY_ID_1629 = 0;
Band_MarkCleanStopped();
Band_AllNotesOff();
s_outro_led_all_green = 0;
for (uint8_t i = 0; i < 4; i++) key_toggle[i] = 0;
}
void Stop_AutoBand() void Stop_AutoBand()
{ {
FreeMode_CancelEvenBeatStop(); FreeMode_CancelEvenBeatStop();
AutoBandTop1_Stop(); /* 先清播放态,再 Stop/AllNoteOff,避免处理期间其它路径误判在播 */
for(uint8_t i = 0;i < 16;i++) Band_CancelStartFollowup();
{ Band_TickGate = 1;
uint8_t AllNoteOff_buff[3] = {(uint8_t)(0xB0 | i),0x7B,0x00};
SendMidiDataToDreamDSP(AllNoteOff_buff,sizeof(AllNoteOff_buff));
uint8_t AllControlOff_buff[3] = {(uint8_t)(0xB0 | i),0x79,0x00};
SendMidiDataToDreamDSP(AllControlOff_buff,sizeof(AllControlOff_buff));
}
StartFlag = 0; StartFlag = 0;
KEY_ID_1629 = 0; /* 停奏后再清,避免收束段音区映射突变 */ KEY_ID_1629 = 0;
Band_MarkCleanStopped();
AutoBandTop1_Stop();
Band_AllNotesOff();
s_outro_led_all_green = 0; s_outro_led_all_green = 0;
//tick_ever_positive = false; //tick_ever_positive = false;
for(uint8_t i = 0;i < 4;i++) key_toggle[i] = 0; for(uint8_t i = 0;i < 4;i++) key_toggle[i] = 0;
@ -950,6 +999,135 @@ uint8_t BP_Transpose(uint8_t key_idx)
* 供专业模式指板键区分「播放中换和弦」与「段落结束后续奏重启」。 */ * 供专业模式指板键区分「播放中换和弦」与「段落结束后续奏重启」。 */
static uint64_t s_play_tick_last = 0; static uint64_t s_play_tick_last = 0;
static uint32_t s_play_adv_tick = 0; /* 最近一次 PlayTick 前进的系统 tick */ 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;
}
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)
{
Band_TickGate = 1;
AutoBandTop1_StartWithNote(midi);
AutoBandTop1_StartWithNotes(notes, n);
}
static void Band_StartPieceRetry(uint8_t *notes, uint8_t n)
{
Band_TickGate = 1;
AutoBandTop1_StartWithNotes(notes, n);
}
static void Band_SyncStartRetry(void)
{
Band_TickGate = 1;
AutoBandTop1_SyncStart();
}
void Band_AbsorbEatenStart(void)
{
(void)0;
}
/* 冷启动前按需 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`) */
void Band_ForceStopBeforeStart(void)
{
s_band_need_stop_before_start = 1;
}
void Band_TrackPlayTick(void) void Band_TrackPlayTick(void)
{ {
@ -997,14 +1175,14 @@ static void Expert_StartWithChord(uint8_t chord_id, uint8_t *midi_note_buff,
if (mGuiData[GUI_AUTOBAND_SW].Current) if (mGuiData[GUI_AUTOBAND_SW].Current)
{ {
/* 会话内勿 Stop:段落圆键切变奏后指板续奏若 Stop 会直接关音; /* 会话内勿 Stop:段落圆键切变奏后指板续奏若 Stop 会直接关音;
* 与节奏类型一致,仅换和弦/重进当前段。冷启动仍 Stop 清残留。 */ * 与节奏类型一致,仅换和弦/重进当前段。
* 冷启动:仅当库可能仍有残留时才 Stop;停止键后已停干净则跳过。 */
if (!session) if (!session)
AutoBandTop1_Stop(); Band_StopBeforeColdStart();
AutoBandTop1_SetLoopMode(0, 1); AutoBandTop1_SetLoopMode(0, 1);
AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current); AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current);
midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current; midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current;
AutoBandTop1_StartWithNote(midi); Band_StartLocalRetry(midi, midi_note_buff, note_cnt);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
Band_NotifyPlayActivity(); Band_NotifyPlayActivity();
LOG_I("pick", "local/expert root=%u name=%s sw=%u tab=%u trigger=%s chord=%u", LOG_I("pick", "local/expert root=%u name=%s sw=%u tab=%u trigger=%s chord=%u",
midi, midi,
@ -1017,7 +1195,7 @@ static void Expert_StartWithChord(uint8_t chord_id, uint8_t *midi_note_buff,
else else
{ {
AutoBandTop1_SetPiecePlayMode(1, 0, 1920); AutoBandTop1_SetPiecePlayMode(1, 0, 1920);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt); Band_StartPieceRetry(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0, 1); AutoBandTop1_SetLoopMode(0, 1);
Band_NotifyPlayActivity(); Band_NotifyPlayActivity();
LOG_I("pick", "expert/rhythm piece name=%s trigger=%s chord=%u", LOG_I("pick", "expert/rhythm piece name=%s trigger=%s chord=%u",
@ -1037,7 +1215,7 @@ void TM1629_Handle(uint8_t key)
if (mGuiData[GUI_TAB_INDEX].Current == 0) if (mGuiData[GUI_TAB_INDEX].Current == 0)
{ {
if (key == 22) { Tap_to_Speed(); return; } if (key == 22) { Tap_to_Speed(); return; }
if (key == 23) { Stop_AutoBand(); return; } if (key == 23) { Stop_AutoBand_Request(); return; }
return; /* 忽略 0~21:不改 KEY_ID/USE_MIDI/PressFlag,不启停 OutTime_Stop */ return; /* 忽略 0~21:不改 KEY_ID/USE_MIDI/PressFlag,不启停 OutTime_Stop */
} }
@ -1066,7 +1244,7 @@ void TM1629_Handle(uint8_t key)
} }
break; break;
case 22:Tap_to_Speed(); break; case 22:Tap_to_Speed(); break;
case 23:Stop_AutoBand(); break; case 23:Stop_AutoBand_Request(); break;
default: // 1 - 21 default: // 1 - 21
if (key >= 1 && key <= 21) if (key >= 1 && key <= 21)
{ {
@ -1229,20 +1407,19 @@ void Pick_Handle()
StartFlag = 1; StartFlag = 1;
AutoBandTop1_SetLoopMode(0,1); AutoBandTop1_SetLoopMode(0,1);
midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current; midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current;
AutoBandTop1_StartWithNote(midi); Band_StartLocalRetry(midi, midi_note_buff, note_cnt);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
} }
break; break;
case 1: case 1:
StartFlag = 1; StartFlag = 1;
AutoBandTop1_SetPiecePlayMode(1,0,1920); AutoBandTop1_SetPiecePlayMode(1,0,1920);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt); Band_StartPieceRetry(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0,1); AutoBandTop1_SetLoopMode(0,1);
break; break;
case 0: case 0:
/* 设置页内沿用万能:与主路径一致,始终 loop */ /* 设置页内沿用万能:与主路径一致,始终 loop */
AutoBandTop1_SetPiecePlayMode(1, 0, 480); AutoBandTop1_SetPiecePlayMode(1, 0, 480);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt); Band_StartPieceRetry(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0, 1); AutoBandTop1_SetLoopMode(0, 1);
StartFlag = 1; StartFlag = 1;
break; break;
@ -1258,35 +1435,43 @@ void Pick_Handle()
(unsigned)PressFlag, (unsigned)chord_id); (unsigned)PressFlag, (unsigned)chord_id);
break; break;
} }
{
uint8_t session_was_on = StartFlag ? 1u : 0u;
StartFlag = 1; StartFlag = 1;
/* 普通:播放中指板 LED 全绿,关闭走向灯刷新 */ /* 普通:播放中指板 LED 全绿,关闭走向灯刷新 */
app_tm1629_all_on(LED_COLOR_G); app_tm1629_all_on(LED_COLOR_G);
if (mGuiData[GUI_AUTOBAND_SW].Current) if (mGuiData[GUI_AUTOBAND_SW].Current)
{ {
AutoBandTop1_Stop(); /* 播放中再拨:Stop+Start 重启段落;停止键后已停则勿二次 Stop。
* 此 Stop 若真实停播会武装库的吞噬器 → 紧随吸收 */
if (session_was_on)
{ Band_MarkRealStop(); AutoBandTop1_Stop(); }
else
Band_StopBeforeColdStart();
AutoBandTop1_SetLoopMode(0,1); AutoBandTop1_SetLoopMode(0,1);
AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current); AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current);
midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current; midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current;
AutoBandTop1_StartWithNote(midi); Band_StartLocalRetry(midi, midi_note_buff, note_cnt);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
LOG_I("pick", "local/normal root=%u name=%s", midi, LOG_I("pick", "local/normal root=%u name=%s", midi,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?"); AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?");
} }
else else
{ {
AutoBandTop1_SetPiecePlayMode(1, 0, 1920); AutoBandTop1_SetPiecePlayMode(1, 0, 1920);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt); Band_StartPieceRetry(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0, 1); AutoBandTop1_SetLoopMode(0, 1);
LOG_I("pick", "normal/rhythm piece name=%s notes=%u chord=%u", LOG_I("pick", "normal/rhythm piece name=%s notes=%u chord=%u",
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?", AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
(unsigned)note_cnt, (unsigned)chord_id); (unsigned)note_cnt, (unsigned)chord_id);
} }
}
break; break;
case 1: case 1:
/* 专业:播放中再拨片不 Stop+Start(库会把未消费的 Start 标志清掉 → 静音); /* 专业:播放中再拨片不 Stop+Start(库会把未消费的 Start 标志清掉 → 静音);
* 本地曲播放中仅换和弦音续播,节奏路径 piece 重启本就安全。 */ * 本地曲播放中仅换和弦音续播,节奏路径 piece 重启本就安全。 */
if (Band_IsPlaying() && mGuiData[GUI_AUTOBAND_SW].Current) if (!s_start_followup_force &&
Band_IsPlaying() && mGuiData[GUI_AUTOBAND_SW].Current)
{ {
Accomp_UpdatePlayingChord(); Accomp_UpdatePlayingChord();
LOG_I("pick", "expert/pick skip restart playing chord=%u", (unsigned)chord_id); LOG_I("pick", "expert/pick skip restart playing chord=%u", (unsigned)chord_id);
@ -1299,7 +1484,7 @@ void Pick_Handle()
StartFlag = 1; StartFlag = 1;
s_outro_led_all_green = 0; /* 拨片重开 piece,恢复走向灯 */ s_outro_led_all_green = 0; /* 拨片重开 piece,恢复走向灯 */
AutoBandTop1_SetPiecePlayMode(1, 0, 480); AutoBandTop1_SetPiecePlayMode(1, 0, 480);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt); Band_StartPieceRetry(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0, 1); AutoBandTop1_SetLoopMode(0, 1);
LOG_I("pick", "universal piece name=%s press=%u notes=%u chord=%u", LOG_I("pick", "universal piece name=%s press=%u notes=%u chord=%u",
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?", AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",

View File

@ -32,6 +32,8 @@
#define MSG_ID_BLE_UI_REFRESH 28 #define MSG_ID_BLE_UI_REFRESH 28
#define MSG_ID_BLE_STYLE_SET 29 #define MSG_ID_BLE_STYLE_SET 29
#define MSG_ID_LOW_BAT_TIP 30 #define MSG_ID_LOW_BAT_TIP 30
#define MSG_ID_STOP_BAND 31 /* 停止键/收束:MainTask 串行 Stop,避免与拨片起奏竞态 */
#define MSG_ID_START_FOLLOWUP 32 /* 停后首启被库吞:下一拍补发真正起奏 */
#define GPIO_PIN_RESET false #define GPIO_PIN_RESET false
#define GPIO_PIN_SET true #define GPIO_PIN_SET true
@ -210,7 +212,6 @@ extern GUI_SWITCH ParamGuiData[];
extern void (*CurrUIProcress)(DisplayTaskMessage_Type xEvent); extern void (*CurrUIProcress)(DisplayTaskMessage_Type xEvent);
void Stop_AutoBand();
/* 普通模式:松手后偶拍收束轮询(由 Read_Cur_Measure 调用) */ /* 普通模式:松手后偶拍收束轮询(由 Read_Cur_Measure 调用) */
void FreeMode_PollEvenBeatStop(void); void FreeMode_PollEvenBeatStop(void);
/* 当前调一级大和弦(把位1 + maj);开机/进模式/前奏/拨片兜底共用 */ /* 当前调一级大和弦(把位1 + maj);开机/进模式/前奏/拨片兜底共用 */
@ -238,6 +239,17 @@ void TM1617_Handle(uint8_t key);
void TM1629_Handle(uint8_t key); void TM1629_Handle(uint8_t key);
void Pick_Handle(); void Pick_Handle();
void Loop_Light_Handle(uint8_t measure); void Loop_Light_Handle(uint8_t measure);
void Stop_AutoBand(void);
void Stop_AutoBand_Soft(void);
extern uint8_t Band_TickGate;
void Stop_AutoBand_Request(void);
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 BL_Set_led(uint8_t Led, uint8_t chord_type); void BL_Set_led(uint8_t Led, uint8_t chord_type);

View File

@ -132,6 +132,7 @@ volatile uint32_t tmr6_ticks;
*/ */
__WEAK void wk_tmr6_1ms_tick(void) __WEAK void wk_tmr6_1ms_tick(void)
{ {
if (Band_TickGate)
AutoBandTop1_Metronome_External_1msTick(); AutoBandTop1_Metronome_External_1msTick();
} }

View File

@ -110,6 +110,24 @@ void TaskMainThread_entry(void* parameter)
if (!System_LowBat_IsActive()) if (!System_LowBat_IsActive())
Pick_Handle(); Pick_Handle();
break; break;
case MSG_ID_STOP_BAND:
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() && Band_TakeStartFollowup())
{
Band_BeginStartFollowup();
Pick_Handle();
Band_EndStartFollowup();
}
break;
case MSG_ID_LOOP_LIGHT: case MSG_ID_LOOP_LIGHT:
if (!System_LowBat_IsActive()) if (!System_LowBat_IsActive())
Loop_Light_Handle(xEvent.ID); Loop_Light_Handle(xEvent.ID);