fix(chord): apply fixed-key voicings from the new chord sheet

Stop adding transpose to every string and AutoBand tone. Each degree now uses the C-major source fret from the sheet, with +1 only where the sheet marks it.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
yuquanjun 2026-10-15 14:38:23 +08:00
parent e49f5a06fc
commit 605af8f045
6 changed files with 244 additions and 99 deletions

View File

@ -30,6 +30,64 @@ GUI_SWITCH ParamGuiData[] =
{ 0, 0, 2, ALL_MODE_PARAM}, // 2
};
/* ======== 固定调(自动伴奏新更改和弦规则.xlsx「和弦固定调逻辑」) ========
* 目标调各级不再整体加 GUI_TRANSPOSE,而是取 C 调「源品位」同一行;
* 表头标 +1 的级整行再升半音(发音位置不变)。升号调(C#/Eb/F#/G#/A#)
* 用低半音自然调的映射再全体 +1。 */
static const int8_t FIXED_KEY_SRC[7][7] = /* [C D E F G A B][级 I..VII] → C 调源品位 0~6 */
{
{0, 1, 2, 3, 4, 5, 6}, /* C */
{1, 2, 3, 4, 5, 6, 0}, /* D */
{2, 3, 4, 5, 6, 0, 1}, /* E */
{3, 4, 5, 5, 0, 1, 2}, /* F */
{4, 5, 6, 0, 1, 2, 3}, /* G */
{5, 6, 0, 1, 2, 3, 4}, /* A */
{6, 0, 1, 2, 3, 4, 5}, /* B */
};
static const uint8_t FIXED_KEY_SHARP[7][7] = /* 该行相对源品位再 +1(表头「源 品 X +1」) */
{
{0, 0, 0, 0, 0, 0, 0}, /* C */
{0, 0, 1, 0, 0, 0, 1}, /* D */
{0, 1, 1, 0, 0, 1, 1}, /* E */
{0, 0, 0, 1, 0, 0, 0}, /* F */
{0, 0, 0, 0, 0, 0, 1}, /* G */
{0, 0, 1, 0, 0, 1, 1}, /* A */
{0, 1, 1, 0, 1, 1, 1}, /* B */
};
static const uint8_t FIXED_KEY_ROW[12] = {0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6}; /* 移调 → 自然调行 */
static const uint8_t FIXED_KEY_HALF[12] = {0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0}; /* 升号调:低半音映射 +1 */
/* 返回 C 调源品位 0~6;*add = 需叠加半音(调内 +1 行 + 升号调 +1,取值 0~2) */
static uint8_t FixedKey_SourceFret(uint8_t group, uint8_t *add)
{
uint8_t t = (uint8_t)mGuiData[GUI_TRANSPOSE].Current % 12u;
uint8_t row = FIXED_KEY_ROW[t];
*add = (uint8_t)(FIXED_KEY_SHARP[row][group] + FIXED_KEY_HALF[t]);
return (uint8_t)FIXED_KEY_SRC[row][group];
}
/* 固定调填 6 弦 USE_MIDI:源品位行 + add;指板单独升号(PitchOffset==2)再 +1 */
void FretChord_FillUseMidi(uint8_t key)
{
uint8_t group, type, add = 0, src;
const STRING_MIDI *ORGAN_MIDI;
if (key < 1u || key > 21u)
return;
group = (uint8_t)((key - 1u) / 3u);
src = FixedKey_SourceFret(group, &add);
if (chord_type_index_map[key].PitchOffset == 2u)
add = (uint8_t)(add + 1u);
ORGAN_MIDI = OrganChord_Midi_Table[src];
type = chord_type_index_map[key].type;
USE_MIDI.Midi_1 = (uint8_t)(ORGAN_MIDI[type].Midi_1 + add);
USE_MIDI.Midi_2 = (uint8_t)(ORGAN_MIDI[type].Midi_2 + add);
USE_MIDI.Midi_3 = (uint8_t)(ORGAN_MIDI[type].Midi_3 + add);
USE_MIDI.Midi_4 = (uint8_t)(ORGAN_MIDI[type].Midi_4 + add);
USE_MIDI.Midi_5 = (uint8_t)(ORGAN_MIDI[type].Midi_5 + add);
USE_MIDI.Midi_6 = (uint8_t)(ORGAN_MIDI[type].Midi_6 + add);
}
const LEBEL_UI UI_Label[] = {
@ -854,7 +912,7 @@ void MasterVolume_Reapply(void)
Send_volume(vol);
}
/* 当前调一级大和弦:把位1 + maj + 自然音(再经 GUI_TRANSPOSE 移调 → C调即 do mi sol) */
/* 当前调一级大和弦:把位1 + maj + 自然音(固定调映射 → C调即 do mi sol) */
void Accomp_ApplyDefaultIChord(void)
{
KEY_ID_1629 = 1;
@ -1627,9 +1685,6 @@ static void Expert_StartWithChord(uint8_t chord_id, uint8_t *midi_note_buff,
void TM1629_Handle(uint8_t key)
{
STRING_MIDI *MIDI;
const STRING_MIDI *ORGAN_MIDI;
/* 指板/拍速/停止键均为用户操作 */
ResetAutoPowerCount();
@ -1684,7 +1739,6 @@ void TM1629_Handle(uint8_t key)
FreeMode_ResumePieceLoop();
PressFlag = 1;
}
uint8_t group = (key - 1) / 3; /* 1~3→0, 4~6→1, ..., 19~21→6 */
KEY_ID_1629 = key;
/* 专业/普通(及对应设置页):记下最后指板和弦;专业同时记移调 */
{
@ -1698,35 +1752,8 @@ void TM1629_Handle(uint8_t key)
else if (tab == 2 || (tab == 3 && last == 2))
Accomp_RememberLastFretChord(key);
}
MIDI = (STRING_MIDI *)Chord_Midi_Table[group];
ORGAN_MIDI = OrganChord_Midi_Table[group];
uint8_t type = chord_type_index_map[key].type;
switch(chord_type_index_map[key].PitchOffset)
{
case 1:
USE_MIDI.Midi_1 = ORGAN_MIDI[type].Midi_1;
USE_MIDI.Midi_2 = ORGAN_MIDI[type].Midi_2;
USE_MIDI.Midi_3 = ORGAN_MIDI[type].Midi_3;
USE_MIDI.Midi_4 = ORGAN_MIDI[type].Midi_4;
USE_MIDI.Midi_5 = ORGAN_MIDI[type].Midi_5;
USE_MIDI.Midi_6 = ORGAN_MIDI[type].Midi_6;
break;
case 2:
USE_MIDI.Midi_1 = MIDI[type].Midi_1;
USE_MIDI.Midi_2 = MIDI[type].Midi_2;
USE_MIDI.Midi_3 = MIDI[type].Midi_3;
USE_MIDI.Midi_4 = MIDI[type].Midi_4;
USE_MIDI.Midi_5 = MIDI[type].Midi_5;
USE_MIDI.Midi_6 = MIDI[type].Midi_6;
break;
}
USE_MIDI.Midi_1 += mGuiData[GUI_TRANSPOSE].Current;
USE_MIDI.Midi_2 += mGuiData[GUI_TRANSPOSE].Current;
USE_MIDI.Midi_3 += mGuiData[GUI_TRANSPOSE].Current;
USE_MIDI.Midi_4 += mGuiData[GUI_TRANSPOSE].Current;
USE_MIDI.Midi_5 += mGuiData[GUI_TRANSPOSE].Current;
USE_MIDI.Midi_6 += mGuiData[GUI_TRANSPOSE].Current;
/* 固定调:按当前移调取 C 调源品位六弦,不再整体加 GUI_TRANSPOSE */
FretChord_FillUseMidi(key);
if(mGuiData[GUI_TAB_INDEX].Current == 1 && StartFlag)
{
@ -2118,11 +2145,20 @@ uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
uint8_t n = 0;
uint8_t i;
uint8_t root;
uint8_t add = 0;
uint8_t src;
buff[0] = buff[1] = buff[2] = buff[3] = 0;
switch(MIDI_Index)
if (MIDI_Index < 1 || MIDI_Index > 21)
return 0;
/* 固定调:按当前移调把该级映射到 C 调源品位(发音位置不变,音高随表),
* 不再对结果整体加 GUI_TRANSPOSE。各 case 数值 = 新表 C 大调「和弦音高」列。 */
src = FixedKey_SourceFret((uint8_t)((MIDI_Index - 1) / 3), &add);
switch(src)
{
case 1:case 2:case 3:
case 0: /* I:C 品位 */
switch(type)
{
case 0:
@ -2154,7 +2190,7 @@ uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
break;
}
break;
case 4:case 5:case 6:
case 1: /* II:D 品位 */
switch(type)
{
case 0:
@ -2186,71 +2222,71 @@ uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
break;
}
break;
case 7:case 8:case 9:
case 2: /* III:E 品位(整组低八度;M/m 三和弦带 E4) */
switch(type)
{
case 0:
buff[0] = 52;buff[1] = 56;buff[2] = 59;
buff[0] = 40;buff[1] = 44;buff[2] = 47;buff[3] = 64;
break;
case 1:
buff[0] = 52;buff[1] = 55;buff[2] = 59;
buff[0] = 40;buff[1] = 43;buff[2] = 47;buff[3] = 64;
break;
case 2:
buff[0] = 52;buff[1] = 55;buff[2] = 59;buff[3] = 62;
buff[0] = 40;buff[1] = 43;buff[2] = 47;buff[3] = 50;
break;
case 3:
buff[0] = 52;buff[1] = 56;buff[2] = 59;buff[3] = 62;
buff[0] = 40;buff[1] = 44;buff[2] = 47;buff[3] = 50;
break;
case 4:
buff[0] = 52;buff[1] = 56;buff[2] = 59;buff[3] = 63;
buff[0] = 40;buff[1] = 44;buff[2] = 47;buff[3] = 51;
break;
case 5:
buff[0] = 52;buff[1] = 56;buff[2] = 59;buff[3] = 66;
buff[0] = 40;buff[1] = 44;buff[2] = 47;buff[3] = 54;
break;
case 6:
buff[0] = 52;buff[1] = 55;buff[2] = 58;buff[3] = 62;
buff[0] = 40;buff[1] = 43;buff[2] = 46;buff[3] = 50;
break;
case 7:
buff[0] = 52;buff[1] = 54;buff[2] = 59;
buff[0] = 40;buff[1] = 42;buff[2] = 47;
break;
case 8:
buff[0] = 52;buff[1] = 57;buff[2] = 59;
buff[0] = 40;buff[1] = 45;buff[2] = 47;
break;
}
break;
case 10:case 11:case 12:
case 3: /* IV:F 品位(整组低八度) */
switch(type)
{
case 0:
buff[0] = 53;buff[1] = 57;buff[2] = 60;
buff[0] = 41;buff[1] = 45;buff[2] = 48;
break;
case 1:
buff[0] = 53;buff[1] = 56;buff[2] = 60;
buff[0] = 41;buff[1] = 44;buff[2] = 48;
break;
case 2:
buff[0] = 53;buff[1] = 56;buff[2] = 60;buff[3] = 63;
buff[0] = 41;buff[1] = 44;buff[2] = 48;buff[3] = 51;
break;
case 3:
buff[0] = 53;buff[1] = 57;buff[2] = 60;buff[3] = 63;
buff[0] = 41;buff[1] = 45;buff[2] = 48;buff[3] = 51;
break;
case 4:
buff[0] = 53;buff[1] = 57;buff[2] = 60;buff[3] = 64;
buff[0] = 41;buff[1] = 45;buff[2] = 48;buff[3] = 52;
break;
case 5:
buff[0] = 53;buff[1] = 57;buff[2] = 60;buff[3] = 67;
buff[0] = 41;buff[1] = 45;buff[2] = 48;buff[3] = 55;
break;
case 6:
buff[0] = 53;buff[1] = 56;buff[2] = 59;buff[3] = 63;
buff[0] = 41;buff[1] = 44;buff[2] = 47;buff[3] = 51;
break;
case 7:
buff[0] = 53;buff[1] = 55;buff[2] = 60;
buff[0] = 41;buff[1] = 43;buff[2] = 48;
break;
case 8:
buff[0] = 53;buff[1] = 58;buff[2] = 60;
buff[0] = 41;buff[1] = 46;buff[2] = 48;
break;
}
break;
case 13:case 14:case 15:
case 4: /* V:G 品位 */
switch(type)
{
case 0:
@ -2282,7 +2318,7 @@ uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
break;
}
break;
case 16:case 17:case 18:
case 5: /* VI:A 品位 */
switch(type)
{
case 0:
@ -2314,30 +2350,30 @@ uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
break;
}
break;
case 19:case 20:case 21:
case 6: /* VII:B 品位(Bm/B7/BM7/Bad9/Bm7-5 按新表改) */
switch(type)
{
case 0:
buff[0] = 47;buff[1] = 51;buff[2] = 53;
break;
case 1:
buff[0] = 47;buff[1] = 50;buff[2] = 54;
buff[0] = 47;buff[1] = 50;buff[2] = 53;
break;
case 2:
buff[0] = 47;buff[1] = 50;buff[2] = 54;buff[3] = 57;
break;
case 3:
buff[0] = 47;buff[1] = 50;buff[2] = 53;buff[3] = 57;
break;
case 4:
buff[0] = 47;buff[1] = 51;buff[2] = 53;buff[3] = 57;
break;
case 5:
case 4:
buff[0] = 47;buff[1] = 51;buff[2] = 54;buff[3] = 58;
break;
case 6:
case 5:
buff[0] = 47;buff[1] = 51;buff[2] = 54;buff[3] = 60;
break;
case 6:
buff[0] = 47;buff[1] = 50;buff[2] = 53;buff[3] = 57;
break;
case 7:
buff[0] = 47;buff[1] = 49;buff[2] = 54;
break;
@ -2354,6 +2390,10 @@ uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
}
n = (buff[3] != 0) ? 4 : 3;
/* 固定调:调内 +1 行 / 升号调全体 +1(替代旧的整体加 GUI_TRANSPOSE) */
for (i = 0; i < n; i++)
buff[i] += add;
switch (chord_type_index_map[MIDI_Index].PitchOffset)
{
case 2: /* # 升:整体 +1 半音 */
@ -2363,8 +2403,6 @@ uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
default:
break;
}
for (i = 0; i < n; i++)
buff[i] += mGuiData[GUI_TRANSPOSE].Current;
/* 音师:仅所有调的 #四级(键 10~12 + 升)— 整组低八度,音域压在一个八度内
* 最低音 = #IV,最高音 ≤ 自然 IV。勿对六级等其它升号和弦套用。 */

View File

@ -276,6 +276,7 @@ void AppNeckLed_ClearGuide(void);
uint8_t AppNeckLed_IsGuide(void);
uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff);
void FretChord_FillUseMidi(uint8_t key);
/* 伴奏已在播时仅更新和弦音,不 Restart(快转调) */
void Accomp_UpdatePlayingChord(void);
/* 切模式/停键前记住专业移调与最后指板和弦 */

View File

@ -111,12 +111,20 @@ void Touch_Action(int8_t FLAG, uint8_t ID, uint8_t areaIndex,uint8_t value)
int8_t new_trans = mGuiData[GUI_TRANSPOSE].Current;
delta = new_trans - last_value;
USE_MIDI.Midi_1 += delta;
USE_MIDI.Midi_2 += delta;
USE_MIDI.Midi_3 += delta;
USE_MIDI.Midi_4 += delta;
USE_MIDI.Midi_5 += delta;
USE_MIDI.Midi_6 += delta;
/* 固定调:换调后按新映射重填 6 弦;未按指板时维持旧平移 */
if (KEY_ID_1629 >= 1 && KEY_ID_1629 <= 21)
{
FretChord_FillUseMidi(KEY_ID_1629);
}
else
{
USE_MIDI.Midi_1 += delta;
USE_MIDI.Midi_2 += delta;
USE_MIDI.Midi_3 += delta;
USE_MIDI.Midi_4 += delta;
USE_MIDI.Midi_5 += delta;
USE_MIDI.Midi_6 += delta;
}
if(StartFlag)
{
@ -416,12 +424,20 @@ void UI_ApplyModeKeysOnSettings(DisplayTaskMessage_Type msg)
USART4_SendData(transpose, 5);
new_trans = (int8_t)mGuiData[GUI_TRANSPOSE].Current;
delta = (int8_t)(new_trans - (int8_t)lastvalue);
USE_MIDI.Midi_1 += delta;
USE_MIDI.Midi_2 += delta;
USE_MIDI.Midi_3 += delta;
USE_MIDI.Midi_4 += delta;
USE_MIDI.Midi_5 += delta;
USE_MIDI.Midi_6 += delta;
/* 固定调:换调后按新映射重填 6 弦;未按指板时维持旧平移 */
if (KEY_ID_1629 >= 1 && KEY_ID_1629 <= 21)
{
FretChord_FillUseMidi(KEY_ID_1629);
}
else
{
USE_MIDI.Midi_1 += delta;
USE_MIDI.Midi_2 += delta;
USE_MIDI.Midi_3 += delta;
USE_MIDI.Midi_4 += delta;
USE_MIDI.Midi_5 += delta;
USE_MIDI.Midi_6 += delta;
}
if (StartFlag && KEY_ID_1629 != 22 && KEY_ID_1629 != 23)
Accomp_UpdatePlayingChord();
}

View File

@ -24,9 +24,9 @@ CHORD_TYPE_INDEX chord_type_index_map[22] =
{1, 0, 1, 0}, // index 16
{1, 0, 1, 0}, // index 17
{1, 0, 1, 0}, // index 18
{0, 0, 1, 0}, // index 19
{0, 0, 1, 0}, // index 20
{0, 0, 1, 0}, // index 21
{1, 0, 1, 0}, // index 19 (VII 出厂小三,对照新表 Bm)
{1, 0, 1, 0}, // index 20
{1, 0, 1, 0}, // index 21
};
STRING_MIDI MIDI_1[] =
@ -58,7 +58,7 @@ STRING_MIDI MIDI_2[] =
STRING_MIDI MIDI_3[] =
{
{0x28,0x2F,0x34,0x38,0x3B,0x40,"E"},
{0x28,0x37,0x3B,0x40,0x3B,0x40,"Em"},
{0x28,0x2F,0x34,0x37,0x3B,0x40,"Em"},
{0x28,0x2F,0x34,0x37,0x3B,0x3E,"Em7"},
{0x28,0x2F,0x34,0x38,0x3B,0x3E,"E7"},
{0x28,0x2F,0x34,0x38,0x3B,0x3F,"EM7"},
@ -70,7 +70,7 @@ STRING_MIDI MIDI_3[] =
STRING_MIDI MIDI_4[] =
{
{0x29,0x39,0x3C,0x41,0x3C,0x41,"F"},
{0x29,0x30,0x35,0x39,0x3C,0x41,"F"},
{0x29,0x30,0x35,0x38,0x3C,0x41,"Fm"},
{0x29,0x30,0x35,0x38,0x3C,0x3F,"Fm7"},
{0x29,0x30,0x35,0x39,0x3C,0x3F,"F7"},
@ -83,7 +83,7 @@ STRING_MIDI MIDI_4[] =
STRING_MIDI MIDI_5[] =
{
{0x2B,0x2F,0x37,0x3B,0x43,0x43,"G"},
{0x2B,0x2F,0x32,0x37,0x3B,0x43,"G"},
{0x2B,0x2E,0x32,0x37,0x3A,0x43,"Gm"},
{0x2B,0x2E,0x32,0x37,0x3A,0x41,"Gm7"},
{0x2B,0x2F,0x32,0x37,0x3B,0x41,"G7"},
@ -97,7 +97,7 @@ STRING_MIDI MIDI_5[] =
STRING_MIDI MIDI_6[] =
{
{0x28,0x2D,0x34,0x39,0x3D,0x40,"A"},
{0x28,0x2D,0x39,0x3C,0x40,0x40,"Am"},
{0x28,0x2D,0x34,0x39,0x3C,0x40,"Am"},
{0x28,0x2D,0x34,0x37,0x3C,0x40,"Am7"},
{0x28,0x2D,0x34,0x37,0x3D,0x40,"A7"},
{0x28,0x2D,0x34,0x38,0x3D,0x40,"AM7"},
@ -118,7 +118,7 @@ STRING_MIDI MIDI_7[] =
{0x29,0x2F,0x32,0x35,0x39,0x3E,"Bm7-5"},
{0x29,0x2F,0x31,0x36,0x3B,0x3D,"Bsus2"},
{0x29,0x2F,0x34,0x36,0x3B,0x40,"Bsus4"},
{0x29,0x2F,0x32,0x35,0x3B,0x3E,"Bdim"},
{0x29,0x2F,0x32,0x35,0x3B,0x3E,"Bm-5"},
};
const STRING_MIDI OrganMIDI_1[] =
@ -150,7 +150,7 @@ const STRING_MIDI OrganMIDI_2[] =
const STRING_MIDI OrganMIDI_3[] =
{
{0x28,0x2F,0x34,0x38,0x3B,0x40,"E"},
{0x28,0x37,0x3B,0x40,0x3B,0x40,"Em"},
{0x28,0x2F,0x34,0x37,0x3B,0x40,"Em"},
{0x28,0x2F,0x34,0x37,0x3B,0x3E,"Em7"},
{0x28,0x2F,0x34,0x38,0x3B,0x3E,"E7"},
{0x28,0x2F,0x34,0x38,0x3B,0x3F,"EM7"},
@ -162,7 +162,7 @@ const STRING_MIDI OrganMIDI_3[] =
const STRING_MIDI OrganMIDI_4[] =
{
{0x29,0x39,0x3C,0x41,0x3C,0x41,"F"},
{0x29,0x30,0x35,0x39,0x3C,0x41,"F"},
{0x29,0x30,0x35,0x38,0x3C,0x41,"Fm"},
{0x29,0x30,0x35,0x38,0x3C,0x3F,"Fm7"},
{0x29,0x30,0x35,0x39,0x3C,0x3F,"F7"},
@ -175,7 +175,7 @@ const STRING_MIDI OrganMIDI_4[] =
const STRING_MIDI OrganMIDI_5[] =
{
{0x2B,0x2F,0x37,0x3B,0x43,0x43,"G"},
{0x2B,0x2F,0x32,0x37,0x3B,0x43,"G"},
{0x2B,0x2E,0x32,0x37,0x3A,0x43,"Gm"},
{0x2B,0x2E,0x32,0x37,0x3A,0x41,"Gm7"},
{0x2B,0x2F,0x32,0x37,0x3B,0x41,"G7"},
@ -189,7 +189,7 @@ const STRING_MIDI OrganMIDI_5[] =
const STRING_MIDI OrganMIDI_6[] =
{
{0x28,0x2D,0x34,0x39,0x3D,0x40,"A"},
{0x28,0x2D,0x39,0x3C,0x40,0x40,"Am"},
{0x28,0x2D,0x34,0x39,0x3C,0x40,"Am"},
{0x28,0x2D,0x34,0x37,0x3C,0x40,"Am7"},
{0x28,0x2D,0x34,0x37,0x3D,0x40,"A7"},
{0x28,0x2D,0x34,0x38,0x3D,0x40,"AM7"},
@ -210,7 +210,7 @@ const STRING_MIDI OrganMIDI_7[] =
{0x29,0x2F,0x32,0x35,0x39,0x3E,"Bm7-5"},
{0x29,0x2F,0x31,0x36,0x3B,0x3D,"Bsus2"},
{0x29,0x2F,0x34,0x36,0x3B,0x40,"Bsus4"},
{0x29,0x2F,0x32,0x35,0x3B,0x3E,"Bdim"},
{0x29,0x2F,0x32,0x35,0x3B,0x3E,"Bm-5"},
};
/* 和弦根音表索引 */

View File

@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
"""固定调和弦六弦音高:RTT STRING_OK 对照《自动伴奏新更改和弦规则.xlsx》。
普通模式按下指板键后读 USE_MIDI(六弦→一弦)。先 chord xpose 再 chord key,
因为换调命令本身不重填六弦。
python tools/test_fixed_key_voicing.py
"""
from __future__ import annotations
import os
import re
import sys
import time
TOOLS = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, TOOLS)
from k1_harness.transports.rtt_session import open_session # noqa: E402
NOTE_RE = re.compile(r"STRING_OK s=(\d+) note=(\d+)")
# (调名, GUI_TRANSPOSE, 键, 和弦名, 六弦→一弦 MIDI)
# 默认品质:I/IV/V 大三,II/III/VI/VII 小三。数值来自各调 sheet 对应行。
CASES = [
("C", 0, 1, "C", (43, 48, 52, 55, 60, 64)),
("C", 0, 4, "Dm", (41, 45, 50, 57, 62, 65)),
("C", 0, 7, "Em", (40, 47, 52, 55, 59, 64)),
("C", 0, 10, "F", (41, 48, 53, 57, 60, 65)),
("C", 0, 13, "G", (43, 47, 50, 55, 59, 67)),
("C", 0, 16, "Am", (40, 45, 52, 57, 60, 64)),
("C", 0, 19, "Bm", (42, 47, 50, 54, 59, 62)),
# G:源品位 V/VI/VII/I/II/III/IV+1
("G", 7, 1, "G", (43, 47, 50, 55, 59, 67)),
("G", 7, 4, "Am", (40, 45, 52, 57, 60, 64)),
("G", 7, 7, "Bm", (42, 47, 50, 54, 59, 62)),
("G", 7, 10, "C", (43, 48, 52, 55, 60, 64)),
("G", 7, 13, "D", (42, 45, 50, 57, 62, 66)),
("G", 7, 16, "Em", (40, 47, 52, 55, 59, 64)),
("G", 7, 19, "#Fm", (42, 49, 54, 58, 61, 66)),
# D I = C 调 II 行;F# I = F 调 I 行全体 +1
("D", 2, 1, "D", (42, 45, 50, 57, 62, 66)),
("F#", 6, 1, "F#", (42, 49, 54, 58, 61, 66)),
]
def last_string_note(text: str, string_n: int) -> int | None:
found = None
for m in NOTE_RE.finditer(text or ""):
if int(m.group(1)) == string_n:
found = int(m.group(2))
return found
def main() -> int:
fails = 0
print("=== 固定调六弦音高 RTT ===", flush=True)
with open_session(reset=False) as sess:
sess.cmd("tone pick f", settle=1.0)
sess.cmd("chord key 23", settle=0.3)
sess.cmd("chord key 0", settle=0.2)
cur_xp = None
for key_name, xp, key, chord, notes in CASES:
if xp != cur_xp:
sess.cmd_ack(f"chord xpose {xp}", "CHORD_XPOSE_OK", timeout_s=2.0, retries=4)
cur_xp = xp
sess.cmd_ack(f"chord key {key}", "CHORD_KEY_OK", timeout_s=3.0, retries=4)
time.sleep(0.15)
print(f"\n-- {key_name} {chord} key={key} xp={xp}", flush=True)
for s in range(6, 0, -1):
expect = int(notes[6 - s])
n0 = len(sess.lines)
sess.cmd(f"string {s}", settle=0.12)
sess.cmd(f"string {s} off", settle=0.05)
got = last_string_note("\n".join(sess.lines[n0:]), s)
ok = got == expect
if not ok:
fails += 1
print(
f" str{s} expect={expect} got={got} {'PASS' if ok else 'FAIL'}",
flush=True,
)
sess.cmd_ack("chord xpose 0", "CHORD_XPOSE_OK", timeout_s=2.0, retries=2)
sess.cmd("chord key 0", settle=0.2)
print(f"\nFAIL={fails} / {len(CASES) * 6}", flush=True)
return 1 if fails else 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -1,8 +1,8 @@
# -*- coding: utf-8 -*-
"""音色表六弦音高校对:RTT string 回报 + 麦克风基频。
对照 Doc/Bug/音色改善.png(C 调、移调 0)。
I C / II Dm 保持原把位;III Em / IV F / V G / VI Am 为上移后的把位。
对照 Doc/音色文件/自动伴奏新更改和弦规则.xlsx「C 大调」弦音高列(C 调、移调 0)。
I C / II Dm 与旧表一致;III Em / IV F / V G / VI Am 按新表改为开放把位音高。
只走 `string N`,不再额外 midi on 期望音,避免麦测被兜底音骗过。
"""
from __future__ import annotations
@ -38,10 +38,10 @@ except ImportError as exc:
CASES = [
("C", 1, (43, 48, 52, 55, 60, 64)),
("Dm", 4, (41, 45, 50, 57, 62, 65)),
("Em", 7, (40, 55, 59, 64, 59, 64)),
("F", 10, (41, 57, 60, 65, 60, 65)),
("G", 13, (43, 47, 55, 59, 67, 67)),
("Am", 16, (40, 45, 57, 60, 64, 64)),
("Em", 7, (40, 47, 52, 55, 59, 64)),
("F", 10, (41, 48, 53, 57, 60, 65)),
("G", 13, (43, 47, 50, 55, 59, 67)),
("Am", 16, (40, 45, 52, 57, 60, 64)),
]
SR = 48000