fix(expert): 专业模式仅按指板即可续奏下一和弦,无需再拨片
App 连机专业模式下,起奏后按亮灯指板即可继续播放: - 本地曲目(App 和弦曲目)/段落停止时:指板键投递 MSG_ID_BP_KEY 给主任务, 走与「指板+拨片」完全相同的 Pick_Handle 起奏路径(扫描线程内直接 Stop+Start 会无声,主任务上下文正常),Pick_Handle 内 Return_Light_buff 通知 App 亮下一节 - 节奏库播放中:保持说明书行为,仅 Note_On 换和弦音 + Return_Light_buff - 去掉 Last_Tm1926D_Key 去重:相邻两节可能是同一和弦键,松手再按必须能续奏 - 新增 Band_TrackPlayTick/Band_IsPlaying:8ms 周期跟踪 AutoBand PlayTick 判定伴奏是否进行中 - 抽出 Expert_StartWithChord 公共起奏函数,拨片/指板共用 - tools/_test_expert_fret_restart.py: 指板续奏 RTT 回归测试 HIL 验证: pitch reg OK=41 FAIL=0; packs 9/9 PASS Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
2d9f234a7d
commit
5f41af27e7
125
Global/Global.c
125
Global/Global.c
|
|
@ -106,6 +106,9 @@ const uint8_t Version[3] = {0,2,12}; /* major.minor.patch — 0.2.12: autoclose
|
||||||
uint8_t Led = 8;
|
uint8_t Led = 8;
|
||||||
bool PressFlag = 0;
|
bool PressFlag = 0;
|
||||||
|
|
||||||
|
void Return_Light_buff(uint8_t key_id_1629);
|
||||||
|
uint8_t Band_IsPlaying(void);
|
||||||
|
|
||||||
uint8_t AutoCloseTime = 15;
|
uint8_t AutoCloseTime = 15;
|
||||||
uint16_t mAutoPowerOffCount = 15 * 60;
|
uint16_t mAutoPowerOffCount = 15 * 60;
|
||||||
bool AutoCloseFlag = true;
|
bool AutoCloseFlag = true;
|
||||||
|
|
@ -774,11 +777,77 @@ uint8_t BP_Transpose(uint8_t key_idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 伴奏活动跟踪:AutoBand PlayTick 前进即视为播放中。
|
||||||
|
* 供专业模式指板键区分「播放中换和弦」与「段落结束后续奏重启」。 */
|
||||||
|
static uint64_t s_play_tick_last = 0;
|
||||||
|
static uint32_t s_play_adv_tick = 0; /* 最近一次 PlayTick 前进的系统 tick */
|
||||||
|
|
||||||
|
void Band_TrackPlayTick(void)
|
||||||
|
{
|
||||||
|
uint64_t pt;
|
||||||
|
|
||||||
|
if (!StartFlag)
|
||||||
|
{
|
||||||
|
s_play_tick_last = 0;
|
||||||
|
s_play_adv_tick = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pt = AutoBandTop1_GetCurrentPlayTick();
|
||||||
|
if (pt != s_play_tick_last)
|
||||||
|
{
|
||||||
|
s_play_tick_last = pt;
|
||||||
|
s_play_adv_tick = rt_tick_get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t Band_IsPlaying(void)
|
||||||
|
{
|
||||||
|
if (!StartFlag || s_play_adv_tick == 0)
|
||||||
|
return 0;
|
||||||
|
return (rt_tick_get() - s_play_adv_tick) < (RT_TICK_PER_SECOND / 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 专业模式公共起奏:拨片首次起奏与起奏后的指板续奏共用。
|
||||||
|
* 节奏库走 piece 循环;本地曲目按当前段落走 StartWithNote。 */
|
||||||
|
static void Expert_StartWithChord(uint8_t chord_id, uint8_t *midi_note_buff,
|
||||||
|
uint8_t note_cnt, const char *trigger)
|
||||||
|
{
|
||||||
|
uint8_t midi = 0;
|
||||||
|
|
||||||
|
StartFlag = 1;
|
||||||
|
if (mGuiData[GUI_AUTOBAND_SW].Current)
|
||||||
|
{
|
||||||
|
AutoBandTop1_Stop();
|
||||||
|
AutoBandTop1_SetLoopMode(0, 1);
|
||||||
|
AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current);
|
||||||
|
midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current;
|
||||||
|
AutoBandTop1_StartWithNote(midi);
|
||||||
|
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
|
||||||
|
LOG_I("pick", "local/expert root=%u name=%s sw=%u tab=%u trigger=%s chord=%u",
|
||||||
|
midi,
|
||||||
|
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
|
||||||
|
(unsigned)mGuiData[GUI_AUTOBAND_SW].Current,
|
||||||
|
(unsigned)mGuiData[GUI_TAB_INDEX].Current,
|
||||||
|
trigger,
|
||||||
|
(unsigned)chord_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AutoBandTop1_SetPiecePlayMode(1, 0, 1920);
|
||||||
|
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
|
||||||
|
AutoBandTop1_SetLoopMode(0, 1);
|
||||||
|
LOG_I("pick", "expert/rhythm piece name=%s trigger=%s chord=%u",
|
||||||
|
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
|
||||||
|
trigger,
|
||||||
|
(unsigned)chord_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void TM1629_Handle(uint8_t key)
|
void TM1629_Handle(uint8_t key)
|
||||||
{
|
{
|
||||||
STRING_MIDI *MIDI;
|
STRING_MIDI *MIDI;
|
||||||
const STRING_MIDI *ORGAN_MIDI;
|
const STRING_MIDI *ORGAN_MIDI;
|
||||||
static uint8_t Last_Tm1926D_Key = 0;
|
|
||||||
|
|
||||||
/* 万能:指板不改和弦、不启停伴奏;保留拍速/停止 */
|
/* 万能:指板不改和弦、不启停伴奏;保留拍速/停止 */
|
||||||
if (mGuiData[GUI_TAB_INDEX].Current == 0)
|
if (mGuiData[GUI_TAB_INDEX].Current == 0)
|
||||||
|
|
@ -857,19 +926,31 @@ void TM1629_Handle(uint8_t key)
|
||||||
USE_MIDI.Midi_5 += mGuiData[GUI_TRANSPOSE].Current;
|
USE_MIDI.Midi_5 += mGuiData[GUI_TRANSPOSE].Current;
|
||||||
USE_MIDI.Midi_6 += mGuiData[GUI_TRANSPOSE].Current;
|
USE_MIDI.Midi_6 += mGuiData[GUI_TRANSPOSE].Current;
|
||||||
|
|
||||||
if(mGuiData[GUI_TAB_INDEX].Current == 1 && StartFlag && Last_Tm1926D_Key != KEY_ID_1629)
|
if(mGuiData[GUI_TAB_INDEX].Current == 1 && StartFlag)
|
||||||
{
|
{
|
||||||
|
/* 不按 Last_Tm1926D_Key 去重:App 曲目相邻两节可能是同一和弦键,
|
||||||
|
* 松手再按同一键也必须能续奏(与「指板+拨片」一致,拨片本无去重) */
|
||||||
|
if (mGuiData[GUI_AUTOBAND_SW].Current || !Band_IsPlaying())
|
||||||
|
{
|
||||||
|
/* 本地曲目(App 和弦曲目):段落结束/循环点处仅按指板即续奏下一节。
|
||||||
|
* 转主任务走 Pick_Handle,与「指板+拨片」完全等价——
|
||||||
|
* 扫描线程内直接 Stop+Start 重启会无声,主任务上下文则正常。
|
||||||
|
* Pick_Handle 内会调 Return_Light_buff 通知 App 亮下一节。 */
|
||||||
|
MainTask_Sendmsg(MSG_ID_BP_KEY, 0, 0, 0);
|
||||||
|
LOG_I("pick", "expert/fret continue chord=%u", (unsigned)KEY_ID_1629);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* 节奏库播放中:仅换和弦音,不重启段落(说明书:左手切换和弦) */
|
||||||
uint8_t midi_note_buff[4] = {0,};
|
uint8_t midi_note_buff[4] = {0,};
|
||||||
uint8_t note_cnt = 0;
|
uint8_t note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff);
|
||||||
Last_Tm1926D_Key = KEY_ID_1629;
|
|
||||||
//AutoBandTop1_SetPiecePlayMode(1,0,480);
|
|
||||||
//AutoBandTop1_StartWithNotes(midi_note_buff,GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff));
|
|
||||||
//AutoBandTop1_SetLoopMode(0,1);
|
|
||||||
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++)
|
||||||
{
|
{
|
||||||
AutoBandTop1_Note_On(midi_note_buff[i],0x50);
|
AutoBandTop1_Note_On(midi_note_buff[i],0x50);
|
||||||
}
|
}
|
||||||
|
Return_Light_buff(KEY_ID_1629);
|
||||||
|
LOG_I("pick", "expert/fret change chord=%u", (unsigned)KEY_ID_1629);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1002,30 +1083,8 @@ void Pick_Handle()
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
StartFlag = 1;
|
/* 首次拨片起奏与后续指板续奏共用同一专业起奏路径 */
|
||||||
if (mGuiData[GUI_AUTOBAND_SW].Current)
|
Expert_StartWithChord(chord_id, midi_note_buff, note_cnt, "pick");
|
||||||
{
|
|
||||||
/* 本地曲目:与普通模式同一套起奏(0x3C 根音 + 和弦音) */
|
|
||||||
AutoBandTop1_Stop();
|
|
||||||
AutoBandTop1_SetLoopMode(0,1);
|
|
||||||
AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current);
|
|
||||||
midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current;
|
|
||||||
AutoBandTop1_StartWithNote(midi);
|
|
||||||
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
|
|
||||||
LOG_I("pick", "local/expert root=%u name=%s sw=%u tab=%u",
|
|
||||||
midi,
|
|
||||||
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
|
|
||||||
(unsigned)mGuiData[GUI_AUTOBAND_SW].Current,
|
|
||||||
(unsigned)mGuiData[GUI_TAB_INDEX].Current);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AutoBandTop1_SetPiecePlayMode(1,0,1920);
|
|
||||||
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
|
|
||||||
AutoBandTop1_SetLoopMode(0,1);
|
|
||||||
LOG_I("pick", "expert/rhythm piece name=%s",
|
|
||||||
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
/* 万能:和弦走向用 piece 模式;须 SetLoopMode,否则无循环时常听不到伴奏 */
|
/* 万能:和弦走向用 piece 模式;须 SetLoopMode,否则无循环时常听不到伴奏 */
|
||||||
|
|
@ -1509,6 +1568,8 @@ void Read_Cur_Measure()
|
||||||
(tab == 0 || tab == 2) ||
|
(tab == 0 || tab == 2) ||
|
||||||
(tab == 3 && (last == 0 || last == 2));
|
(tab == 3 && (last == 0 || last == 2));
|
||||||
|
|
||||||
|
Band_TrackPlayTick();
|
||||||
|
|
||||||
if (loop_light_ok && StartFlag)
|
if (loop_light_ok && StartFlag)
|
||||||
{
|
{
|
||||||
if(now - last_tick_i >= 10)
|
if(now - last_tick_i >= 10)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
"""One-off: expert rhythm (1.bin) piece mode ends after the piece window;
|
||||||
|
press fretboard only (no pick) after it stops -> must restart (trigger=fret + PITCH)."""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
import rtt_pitch_reg_test as R
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
R.import_deps()
|
||||||
|
jlink = R.connect_jlink("AT32F403AC") # same default as rtt_pitch_reg_test
|
||||||
|
try:
|
||||||
|
cb = R.find_rtt_control_block(jlink)
|
||||||
|
if cb is None:
|
||||||
|
print("FAIL: RTT CB not found")
|
||||||
|
return 1
|
||||||
|
print(f"RTT CB @ 0x{cb:08X}", flush=True)
|
||||||
|
jlink.rtt_start(cb)
|
||||||
|
R.wait_rtt_ready(jlink)
|
||||||
|
sess = R.RttSession(jlink)
|
||||||
|
sess.pump(0.3)
|
||||||
|
|
||||||
|
sess.cmd("log clear", 0.3)
|
||||||
|
sess.cmd("tone bin1 0", 1.2) # expert + rhythm 1.bin (piece 0..1920ms)
|
||||||
|
sess.cmd("tone start", 0.8) # pick start, piece plays then stops
|
||||||
|
print(">> playing window (piece ~2s) ...", flush=True)
|
||||||
|
sess.pump(4.0) # wait until piece ended, PlayTick frozen
|
||||||
|
|
||||||
|
base = len(sess.lines)
|
||||||
|
sess.cmd("chord key 0", 0.4)
|
||||||
|
sess.cmd("chord key 8", 0.2) # fretboard only, no pick
|
||||||
|
sess.pump(3.0) # capture restart
|
||||||
|
|
||||||
|
new = sess.lines[base:]
|
||||||
|
joined = "\n".join(new)
|
||||||
|
trig = [l for l in new if "trigger=fret" in l]
|
||||||
|
change = [l for l in new if "expert/fret change" in l]
|
||||||
|
pitch = [l for l in new if "[PITCH] on" in l]
|
||||||
|
print("---- full session ----", flush=True)
|
||||||
|
for l in sess.lines:
|
||||||
|
print(l, flush=True)
|
||||||
|
print("---- captured after fret-only press ----", flush=True)
|
||||||
|
for l in new:
|
||||||
|
print(l, flush=True)
|
||||||
|
print("---- verdict ----", flush=True)
|
||||||
|
handled = trig or change
|
||||||
|
if handled and pitch:
|
||||||
|
path = "restart" if trig else "note_on-change"
|
||||||
|
print(f"PASS fret-only continue: path={path}, PITCH on x{len(pitch)}")
|
||||||
|
return 0
|
||||||
|
print("FAIL: no handler log or no pitch")
|
||||||
|
return 1
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
sess.cmd("chord key 23", 0.3) # stop band, leave device quiet
|
||||||
|
sess.pump(0.3)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
jlink.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
|
|
||||||
Loading…
Reference in New Issue