fix(reg): apply overall -12 for transpose >= #F on I/II too
Match 转调说明 table: C~F keep normal register; #F~B lower bass/chord by one octave for all fret degrees. III+ register fix unchanged. RTT pitch test covers I/II+#F. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
ada2031e38
commit
feb084699c
|
|
@ -21,9 +21,11 @@ static uint8_t pStoreBuffer[PRESET_BUFFER_SIZE];
|
||||||
|
|
||||||
/* ======== Bass/和弦分通道八度(无 AutoBand 源码时的输出侧映射) ========
|
/* ======== Bass/和弦分通道八度(无 AutoBand 源码时的输出侧映射) ========
|
||||||
* 代码通道(0 起,与 MIDI status 低 4 位一致):8=bass,9=鼓;其余=和弦。
|
* 代码通道(0 起,与 MIDI status 低 4 位一致):8=bass,9=鼓;其余=和弦。
|
||||||
* 日志打印同号(ch8=bass)。I/II(键1~6)原样;III+:bass -12 地板 E1;
|
* 日志打印同号(ch8=bass)。
|
||||||
* 和弦 -12 夹在 E2~#G4,超上限就近和弦内音。移调≥#F(6) 时再 -12。
|
* ① 指板 I/II(键1~6):原样;移调≥#F(6) 时表格「整体-12」(bass、和弦)。
|
||||||
* 对照 Doc/吉他和弦表最终版.xlsx */
|
* ② 指板 III+(键7~21):bass -12 地板 E1;和弦 -12 夹在 E2~#G4;
|
||||||
|
* 移调≥#F 时再叠加 -12(与①同一转调表)。
|
||||||
|
* 对照 Doc/吉他和弦表最终版.xlsx / 转调说明表 */
|
||||||
#define CHORD_REG_FIX_EN 1
|
#define CHORD_REG_FIX_EN 1
|
||||||
#define BASS_CH 8 /* 日志 ch8 */
|
#define BASS_CH 8 /* 日志 ch8 */
|
||||||
#define DRUM_CH 9 /* 日志 ch9 */
|
#define DRUM_CH 9 /* 日志 ch9 */
|
||||||
|
|
@ -195,6 +197,37 @@ static int App_Auto_MapChordNote(int key, uint8_t *flags)
|
||||||
return App_Auto_ClampMidi(out);
|
return App_Auto_ClampMidi(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 转调说明:移调≥#F 时 I/II 也「整体-12」(bass、和弦);不含 III+ 把位音区修正 */
|
||||||
|
static int App_Auto_MapXposeOverall(int channel, int key, uint8_t *flags)
|
||||||
|
{
|
||||||
|
int out = key - 12;
|
||||||
|
uint8_t f = 0x04u; /* bit2: xpose overall -12 */
|
||||||
|
|
||||||
|
if (channel == BASS_CH)
|
||||||
|
{
|
||||||
|
while (out < MIDI_E1)
|
||||||
|
{
|
||||||
|
out += 12;
|
||||||
|
f |= 0x02u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (out < MIDI_E2)
|
||||||
|
{
|
||||||
|
out += 12;
|
||||||
|
f |= 0x08u;
|
||||||
|
}
|
||||||
|
if (out > MIDI_GSHARP4)
|
||||||
|
{
|
||||||
|
out = App_Auto_NearestChordTone(out);
|
||||||
|
f |= 0x10u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flags) *flags = f;
|
||||||
|
return App_Auto_ClampMidi(out);
|
||||||
|
}
|
||||||
|
|
||||||
static int App_Auto_MapNote(int channel, int key, uint8_t *flags)
|
static int App_Auto_MapNote(int channel, int key, uint8_t *flags)
|
||||||
{
|
{
|
||||||
int out = key;
|
int out = key;
|
||||||
|
|
@ -204,13 +237,24 @@ static int App_Auto_MapNote(int channel, int key, uint8_t *flags)
|
||||||
if (channel == DRUM_CH || key < 0 || key > 127)
|
if (channel == DRUM_CH || key < 0 || key > 127)
|
||||||
return key;
|
return key;
|
||||||
|
|
||||||
if (!App_Auto_NeedRegisterFix())
|
if (App_Auto_NeedRegisterFix())
|
||||||
return key;
|
{
|
||||||
|
/* III+:既有把位音区修正;≥#F 时 Map* 内再叠转调 -12 */
|
||||||
if (channel == BASS_CH)
|
if (channel == BASS_CH)
|
||||||
out = App_Auto_MapBassNote(key, flags);
|
out = App_Auto_MapBassNote(key, flags);
|
||||||
|
else
|
||||||
|
out = App_Auto_MapChordNote(key, flags);
|
||||||
|
}
|
||||||
|
else if (App_Auto_XposeExtra())
|
||||||
|
{
|
||||||
|
/* I/II + 移调≥#F:表格「整体-12 (bass、和弦)」 */
|
||||||
|
out = App_Auto_MapXposeOverall(channel, key, flags);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
out = App_Auto_MapChordNote(key, flags);
|
{
|
||||||
|
/* I/II + 移调 C~F:正常和弦逻辑,不改八度 */
|
||||||
|
return key;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
(void)channel;
|
(void)channel;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -249,7 +293,8 @@ static void App_Auto_LogPitch(int channel, int key, int out, uint8_t flags, int
|
||||||
deg = App_Auto_ChordDegree();
|
deg = App_Auto_ChordDegree();
|
||||||
fix = App_Auto_NeedRegisterFix();
|
fix = App_Auto_NeedRegisterFix();
|
||||||
xf = App_Auto_XposeExtra();
|
xf = App_Auto_XposeExtra();
|
||||||
pass = (!fix || channel == DRUM_CH) ? 1 : 0;
|
/* PASS = 未做任何八度映射(I/II 且移调 C~F) */
|
||||||
|
pass = ((!fix && !xf) || channel == DRUM_CH) ? 1 : 0;
|
||||||
|
|
||||||
LOG_I("PITCH", "%s ch%u(%s) %d->%d d=%d deg=%d chord=%u xp=%u xf=%d fl=0x%02X %s%s%s%s",
|
LOG_I("PITCH", "%s ch%u(%s) %d->%d d=%d deg=%d chord=%u xp=%u xf=%d fl=0x%02X %s%s%s%s",
|
||||||
is_on ? "on" : "off",
|
is_on ? "on" : "off",
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,30 @@ def map_chord(key: int, xp: int) -> tuple[int, int, bool]:
|
||||||
return max(0, min(127, out)), flags, need_near
|
return max(0, min(127, out)), flags, need_near
|
||||||
|
|
||||||
|
|
||||||
|
def map_xpose_overall_bass(key: int) -> tuple[int, int]:
|
||||||
|
"""I/II + xp>=#F: overall -12 only (mirrors App_Auto_MapXposeOverall bass)."""
|
||||||
|
out = key - 12
|
||||||
|
flags = 0x04
|
||||||
|
while out < MIDI_E1:
|
||||||
|
out += 12
|
||||||
|
flags |= 0x02
|
||||||
|
return max(0, min(127, out)), flags
|
||||||
|
|
||||||
|
|
||||||
|
def map_xpose_overall_chord(key: int) -> tuple[int, int, bool]:
|
||||||
|
"""I/II + xp>=#F: overall -12 only (mirrors App_Auto_MapXposeOverall chord)."""
|
||||||
|
out = key - 12
|
||||||
|
flags = 0x04
|
||||||
|
while out < MIDI_E2:
|
||||||
|
out += 12
|
||||||
|
flags |= 0x08
|
||||||
|
need_near = out > MIDI_GSHARP4
|
||||||
|
if need_near:
|
||||||
|
flags |= 0x10
|
||||||
|
out = MIDI_GSHARP4
|
||||||
|
return max(0, min(127, out)), flags, need_near
|
||||||
|
|
||||||
|
|
||||||
def expect_for_event(ev: PitchEvent) -> tuple[str, bool]:
|
def expect_for_event(ev: PitchEvent) -> tuple[str, bool]:
|
||||||
"""Return (detail, ok)."""
|
"""Return (detail, ok)."""
|
||||||
if not ev.is_on:
|
if not ev.is_on:
|
||||||
|
|
@ -268,6 +292,23 @@ def expect_for_event(ev: PitchEvent) -> tuple[str, bool]:
|
||||||
|
|
||||||
fix = need_register_fix(ev.chord)
|
fix = need_register_fix(ev.chord)
|
||||||
if not fix:
|
if not fix:
|
||||||
|
# I/II: C~F 原样;≥#F 表格整体-12
|
||||||
|
if ev.xp >= XPOSE_FS:
|
||||||
|
if ev.ch == BASS_CH_DISP or ev.role == "bass":
|
||||||
|
exp, exp_fl = map_xpose_overall_bass(ev.key_in)
|
||||||
|
if ev.key_out != exp:
|
||||||
|
return f"I/II xpose bass expect {ev.key_in}->{exp}, got {ev.key_out}", False
|
||||||
|
if not (ev.flags & 0x04):
|
||||||
|
return f"I/II xpose bass missing xf flag fl=0x{ev.flags:02X}", False
|
||||||
|
return f"I/II xpose bass {ev.key_in}->{ev.key_out}", True
|
||||||
|
exp, exp_fl, need_near = map_xpose_overall_chord(ev.key_in)
|
||||||
|
if not (MIDI_E2 <= ev.key_out <= MIDI_GSHARP4):
|
||||||
|
return f"I/II xpose chord out {ev.key_out} not in range", False
|
||||||
|
if need_near:
|
||||||
|
return f"I/II xpose chord NEAR {ev.key_in}->{ev.key_out}", True
|
||||||
|
if ev.key_out != exp:
|
||||||
|
return f"I/II xpose chord expect {ev.key_in}->{exp}, got {ev.key_out}", False
|
||||||
|
return f"I/II xpose chord {ev.key_in}->{ev.key_out}", True
|
||||||
if ev.key_out != ev.key_in or ev.delta != 0:
|
if ev.key_out != ev.key_in or ev.delta != 0:
|
||||||
return f"I/II should PASS {ev.key_in}->{ev.key_in}, got {ev.key_out}", False
|
return f"I/II should PASS {ev.key_in}->{ev.key_in}, got {ev.key_out}", False
|
||||||
if "PASS" not in ev.tags and ev.flags != 0:
|
if "PASS" not in ev.tags and ev.flags != 0:
|
||||||
|
|
@ -476,6 +517,8 @@ def run_auto(device: str, hold_s: float, report: str | None, log_path: str | Non
|
||||||
sess.pump(hold_s)
|
sess.pump(hold_s)
|
||||||
|
|
||||||
sess.phase_switch("I/II key2 PASS", key=2, xpose=None, hold_s=hold_s)
|
sess.phase_switch("I/II key2 PASS", key=2, xpose=None, hold_s=hold_s)
|
||||||
|
sess.phase_switch("I/II key2 xpose=#F overall-12", key=2, xpose=6, hold_s=hold_s)
|
||||||
|
sess.phase_switch("I/II key2 xpose=F restore", key=2, xpose=2, hold_s=hold_s)
|
||||||
sess.phase_switch("III key8 -12", key=8, xpose=None, hold_s=hold_s)
|
sess.phase_switch("III key8 -12", key=8, xpose=None, hold_s=hold_s)
|
||||||
sess.phase_switch("VII key20 -12", key=20, xpose=None, hold_s=hold_s)
|
sess.phase_switch("VII key20 -12", key=20, xpose=None, hold_s=hold_s)
|
||||||
sess.phase_switch("VII key20 xpose=#F", key=20, xpose=6, hold_s=hold_s)
|
sess.phase_switch("VII key20 xpose=#F", key=20, xpose=6, hold_s=hold_s)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue