fix(chord): play sheet accompaniment sequences and chord tones
III-VII shapes use the C-major sequence column with the same fixed-key offset as the strings. Chord tones follow the decimal column. Bump firmware to 0.2.57. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
99c2d84465
commit
3c81607daf
|
|
@ -583,6 +583,38 @@ uint8_t app_log_try_command(const char *cmd)
|
|||
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "TONE_PICK_DONE\n");
|
||||
return 1U;
|
||||
}
|
||||
/* chord notes KEY [TYPE]:当前移调下 GetChordNotesByType 的和弦音 */
|
||||
if (strncmp(cmd, "chord notes ", 12) == 0 && cmd[12] != '\0') {
|
||||
char *endp = NULL;
|
||||
unsigned key = (unsigned)strtoul(cmd + 12, &endp, 10);
|
||||
unsigned type = 0;
|
||||
int has_type = 0;
|
||||
uint8_t buff[4] = {0, 0, 0, 0};
|
||||
uint8_t n, i;
|
||||
char line[64];
|
||||
int pos;
|
||||
if (endp != NULL && *endp == ' ') {
|
||||
type = (unsigned)strtoul(endp + 1, NULL, 10);
|
||||
has_type = 1;
|
||||
}
|
||||
if (key < 1U || key > 21U) {
|
||||
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, "CHORD_NOTES_BAD\n");
|
||||
return 1U;
|
||||
}
|
||||
if (!has_type)
|
||||
type = chord_type_index_map[key].type;
|
||||
n = GetChordNotesByType((uint8_t)key, (uint8_t)type, buff);
|
||||
pos = snprintf(line, sizeof(line), "CHORD_NOTES_OK key=%u type=%u n=%u",
|
||||
key, type, (unsigned)n);
|
||||
for (i = 0; i < n && pos > 0 && pos < (int)sizeof(line) - 5; i++)
|
||||
pos += snprintf(line + pos, sizeof(line) - (size_t)pos, " %u", (unsigned)buff[i]);
|
||||
if (pos > 0 && pos < (int)sizeof(line) - 2) {
|
||||
line[pos++] = '\n';
|
||||
line[pos] = '\0';
|
||||
}
|
||||
SEGGER_RTT_WriteString(APP_LOG_RTT_CHANNEL, line);
|
||||
return 1U;
|
||||
}
|
||||
/* 测试注入:chord key N(0释放 / 1~21和弦 / 22拍速 / 23停止) */
|
||||
if (strncmp(cmd, "chord key ", 10) == 0 && cmd[10] != '\0') {
|
||||
unsigned key = (unsigned)strtoul(cmd + 10, NULL, 10);
|
||||
|
|
|
|||
121
Global/Global.c
121
Global/Global.c
|
|
@ -88,6 +88,109 @@ void FretChord_FillUseMidi(uint8_t key)
|
|||
USE_MIDI.Midi_6 = (uint8_t)(ORGAN_MIDI[type].Midi_6 + add);
|
||||
}
|
||||
|
||||
/* C 大调「和弦伴奏发音顺序」,[源品位 I..VII][性质 0..8][8 音]。
|
||||
* 0 表示该格留空,改用标准型指法从六弦生成。AM7 格原文「#C4# G3」按同列写成
|
||||
* A2 #G3 #C4 #G3 E4 #G3 #C4 #G3。I/II 形状不走这张表(表上不需要改)。 */
|
||||
static const uint8_t ACCOMP_SEQ[7][9][8] =
|
||||
{
|
||||
{ /* I C */
|
||||
{48,55,60,55,64,55,60,55}, {48,55,60,55,63,55,60,55},
|
||||
{48,55,70,55,63,55,70,55}, {48,55,70,55,64,55,70,55},
|
||||
{48,55,71,55,64,55,71,55}, {0}, {0}, {0}, {0},
|
||||
},
|
||||
{ /* II D */
|
||||
{50,57,62,57,66,57,62,57}, {50,57,62,57,65,57,62,57},
|
||||
{50,57,60,57,65,57,62,57}, {50,57,60,57,66,57,60,57},
|
||||
{50,57,61,57,66,57,61,57}, {0}, {0}, {0}, {0},
|
||||
},
|
||||
{ /* III E */
|
||||
{40,56,59,56,64,56,59,56}, {40,55,59,55,64,55,59,55},
|
||||
{40,55,59,55,62,55,59,55}, {40,56,59,56,62,56,59,56},
|
||||
{40,56,59,56,63,56,59,56}, {0}, {0}, {0}, {0},
|
||||
},
|
||||
{ /* IV F */
|
||||
{41,57,60,57,65,57,60,57}, {41,56,60,56,65,56,60,56},
|
||||
{41,56,60,56,63,56,60,56}, {41,57,60,57,63,57,60,57},
|
||||
{41,57,60,57,64,57,60,57}, {0}, {0}, {0}, {0},
|
||||
},
|
||||
{ /* V G */
|
||||
{43,50,59,50,67,50,59,50}, {43,50,58,50,67,50,58,50},
|
||||
{43,50,58,50,65,50,58,50}, {43,50,59,50,65,50,59,50},
|
||||
{43,50,59,50,66,50,59,50}, {0}, {0}, {0}, {0},
|
||||
},
|
||||
{ /* VI A */
|
||||
{45,57,61,57,64,57,61,57}, {45,57,60,57,64,57,60,57},
|
||||
{45,55,60,55,64,55,60,55}, {45,55,61,55,64,55,61,55},
|
||||
{45,56,61,56,64,56,61,56}, {0}, {0}, {0}, {0},
|
||||
},
|
||||
{ /* VII B */
|
||||
{47,59,63,59,66,59,63,59}, {47,59,62,59,66,59,62,59},
|
||||
{47,54,57,54,62,54,57,54}, {47,54,57,54,63,54,57,54},
|
||||
{47,54,58,54,63,54,58,54}, {0}, {0}, {0}, {0},
|
||||
},
|
||||
};
|
||||
/* Sheet2 标准型指法,弦号 6=低音弦。只给发音顺序留空的性质用。 */
|
||||
static const uint8_t ACCOMP_FINGER[7][8] =
|
||||
{
|
||||
{5,3,2,3,1,3,2,3},
|
||||
{4,3,2,3,1,3,2,3},
|
||||
{6,3,2,3,1,3,2,3},
|
||||
{6,3,2,3,1,3,2,3},
|
||||
{6,3,2,3,1,3,2,3},
|
||||
{5,3,2,3,1,3,2,3},
|
||||
{5,3,2,3,1,3,2,3},
|
||||
};
|
||||
|
||||
static uint8_t Accomp_StringMidi(const STRING_MIDI *row, uint8_t string_no)
|
||||
{
|
||||
switch (string_no)
|
||||
{
|
||||
case 1: return row->Midi_1;
|
||||
case 2: return row->Midi_2;
|
||||
case 3: return row->Midi_3;
|
||||
case 4: return row->Midi_4;
|
||||
case 5: return row->Midi_5;
|
||||
default: return row->Midi_6;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t AccompPattern_Fill(uint8_t key, uint8_t out[8])
|
||||
{
|
||||
uint8_t group, src, add = 0, type, i;
|
||||
const uint8_t *seq = 0;
|
||||
|
||||
if (key < 1u || key > 21u)
|
||||
return 0;
|
||||
group = (uint8_t)((key - 1u) / 3u);
|
||||
src = FixedKey_SourceFret(group, &add);
|
||||
if (src < 2u)
|
||||
return 0;
|
||||
if (chord_type_index_map[key].PitchOffset == 2u)
|
||||
add = (uint8_t)(add + 1u);
|
||||
type = chord_type_index_map[key].type;
|
||||
if (type < 9u && ACCOMP_SEQ[src][type][0] != 0u)
|
||||
seq = ACCOMP_SEQ[src][type];
|
||||
for (i = 0u; i < 8u; i++)
|
||||
{
|
||||
uint8_t n;
|
||||
if (seq)
|
||||
n = seq[i];
|
||||
else
|
||||
{
|
||||
const STRING_MIDI *row = OrganChord_Midi_Table[src];
|
||||
uint8_t t = type;
|
||||
if (src < 6u && t > 8u)
|
||||
t = 0u;
|
||||
if (src == 6u && t > 9u)
|
||||
t = 0u;
|
||||
n = Accomp_StringMidi(&row[t], ACCOMP_FINGER[src][i]);
|
||||
}
|
||||
n = (uint8_t)(n + add);
|
||||
out[i] = (n > 127u) ? 127u : n;
|
||||
}
|
||||
return 1u;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const LEBEL_UI UI_Label[] = {
|
||||
|
|
@ -169,7 +272,7 @@ int16_t MIC_VOL_MAP[11]= {-9000,-7000,-5000,-3000,-1000,1000,3000,5000,7000,9000
|
|||
|
||||
uint8_t TOUCH_AREA_NUM = (sizeof(Touch_Areas)/sizeof(Touch_AreaTypeDef));
|
||||
|
||||
const uint8_t Version[3] = {0,2,56}; /* major.minor.patch — 0.2.56: 固定调和弦音高 */
|
||||
const uint8_t Version[3] = {0,2,57}; /* major.minor.patch — 0.2.57: 伴奏发音顺序与和弦音高 */
|
||||
uint8_t Led = 8;
|
||||
/* App 已下发琴颈引导灯:拨片勿再全绿盖掉 */
|
||||
static uint8_t s_app_neck_led = 0;
|
||||
|
|
@ -2222,14 +2325,14 @@ uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case 2: /* III:E 品位(整组低八度;M/m 三和弦带 E4) */
|
||||
case 2: /* III:E 品位(整组低八度;三和弦不再另加 E4) */
|
||||
switch(type)
|
||||
{
|
||||
case 0:
|
||||
buff[0] = 40;buff[1] = 44;buff[2] = 47;buff[3] = 64;
|
||||
buff[0] = 40;buff[1] = 44;buff[2] = 47;
|
||||
break;
|
||||
case 1:
|
||||
buff[0] = 40;buff[1] = 43;buff[2] = 47;buff[3] = 64;
|
||||
buff[0] = 40;buff[1] = 43;buff[2] = 47;
|
||||
break;
|
||||
case 2:
|
||||
buff[0] = 40;buff[1] = 43;buff[2] = 47;buff[3] = 50;
|
||||
|
|
@ -2350,26 +2453,26 @@ uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case 6: /* VII:B 品位(Bm/B7/BM7/Bad9/Bm7-5 按新表改) */
|
||||
case 6: /* VII:B 品位(五音为 #F;Bad9 最高音为 B) */
|
||||
switch(type)
|
||||
{
|
||||
case 0:
|
||||
buff[0] = 47;buff[1] = 51;buff[2] = 53;
|
||||
buff[0] = 47;buff[1] = 51;buff[2] = 54;
|
||||
break;
|
||||
case 1:
|
||||
buff[0] = 47;buff[1] = 50;buff[2] = 53;
|
||||
buff[0] = 47;buff[1] = 50;buff[2] = 54;
|
||||
break;
|
||||
case 2:
|
||||
buff[0] = 47;buff[1] = 50;buff[2] = 54;buff[3] = 57;
|
||||
break;
|
||||
case 3:
|
||||
buff[0] = 47;buff[1] = 51;buff[2] = 53;buff[3] = 57;
|
||||
buff[0] = 47;buff[1] = 51;buff[2] = 54;buff[3] = 57;
|
||||
break;
|
||||
case 4:
|
||||
buff[0] = 47;buff[1] = 51;buff[2] = 54;buff[3] = 58;
|
||||
break;
|
||||
case 5:
|
||||
buff[0] = 47;buff[1] = 51;buff[2] = 54;buff[3] = 60;
|
||||
buff[0] = 47;buff[1] = 51;buff[2] = 54;buff[3] = 59;
|
||||
break;
|
||||
case 6:
|
||||
buff[0] = 47;buff[1] = 50;buff[2] = 53;buff[3] = 57;
|
||||
|
|
|
|||
|
|
@ -277,6 +277,8 @@ uint8_t AppNeckLed_IsGuide(void);
|
|||
|
||||
uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff);
|
||||
void FretChord_FillUseMidi(uint8_t key);
|
||||
/* 源品位 III~VII:按 C 大调「和弦伴奏发音顺序」填 8 音(已含固定调半音)。I/II 返回 0 */
|
||||
uint8_t AccompPattern_Fill(uint8_t key, uint8_t out[8]);
|
||||
/* 伴奏已在播时仅更新和弦音,不 Restart(快转调) */
|
||||
void Accomp_UpdatePlayingChord(void);
|
||||
/* 切模式/停键前记住专业移调与最后指板和弦 */
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@ static uint8_t pStoreBuffer[PRESET_BUFFER_SIZE];
|
|||
* 代码通道(0 起,与 MIDI status 低 4 位一致):8=bass,9=鼓;其余=和弦。
|
||||
* 日志打印同号(ch8=bass)。
|
||||
* ① 指板 I/II(键1~6):原样;移调≥#F(6) 时表格「整体-12」(bass、和弦)。
|
||||
* ② 指板 III/IV(键7~12):bass -12 地板 E1;和弦 -12 夹在 E2~#G4;
|
||||
* 不再叠加转调 -12(避免 G 调 III 把 B3 压回 B2 后与根音重叠)。
|
||||
* ③ 指板 V~VII(键13~21):同②的把位 -12;移调≥#F 时再叠加 -12。
|
||||
* 对照 Doc/吉他和弦表最终版.xlsx / 转调说明表 */
|
||||
* ② 指板 III/IV(键7~12):bass -12 地板 E1;和弦通道改走发音顺序(见下)。
|
||||
* ③ 指板 V~VII(键13~21):bass 同②,移调≥#F 时 bass 再 -12。
|
||||
* 和弦通道:源品位是 III~VII 时,不再做 -12,按 C 大调「和弦伴奏发音顺序」
|
||||
* 逐音循环(固定调半音与六弦同一套映射)。I/II 形状仍走上面的八度规则。
|
||||
* 对照 自动伴奏新更改和弦规则.xlsx「C大调 / 和弦伴奏发音顺序」 */
|
||||
#define CHORD_REG_FIX_EN 1
|
||||
#define BASS_CH 8 /* 日志 ch8 */
|
||||
#define DRUM_CH 9 /* 日志 ch9 */
|
||||
|
|
@ -44,6 +45,116 @@ static uint16_t s_ch_ever_mask = 0; /* 会话内各通道是否出现过 NoteOn
|
|||
#define PITCH_LOG_PER_FP 24
|
||||
#define BASS_LOG_PER_FP 16
|
||||
|
||||
static void App_Auto_QueueAllNotesOffMelodic(void);
|
||||
|
||||
/* 和弦通道发音顺序:每通道自己的拍点,NoteOff 按进入音配对 */
|
||||
static uint8_t s_seq[8];
|
||||
static uint8_t s_seq_on;
|
||||
static uint8_t s_seq_ready;
|
||||
static uint8_t s_seq_key;
|
||||
static uint8_t s_seq_type;
|
||||
static uint8_t s_seq_xp;
|
||||
static uint8_t s_seq_po;
|
||||
#define SEQ_SLOTS 12
|
||||
static uint8_t s_seq_step[16];
|
||||
static struct { uint8_t ch, in, out, used; } s_seq_slot[SEQ_SLOTS];
|
||||
|
||||
static void App_Auto_SeqRefresh(void)
|
||||
{
|
||||
uint8_t key = KEY_ID_1629;
|
||||
uint8_t type = 0, po = 0;
|
||||
uint8_t xp = (uint8_t)mGuiData[GUI_TRANSPOSE].Current;
|
||||
uint8_t seq[8];
|
||||
uint8_t on;
|
||||
uint8_t i;
|
||||
|
||||
if (key >= 1u && key <= 21u)
|
||||
{
|
||||
type = chord_type_index_map[key].type;
|
||||
po = chord_type_index_map[key].PitchOffset;
|
||||
}
|
||||
if (s_seq_ready && key == s_seq_key && type == s_seq_type && xp == s_seq_xp && po == s_seq_po)
|
||||
return;
|
||||
on = AccompPattern_Fill(key, seq);
|
||||
s_seq_key = key;
|
||||
s_seq_type = type;
|
||||
s_seq_xp = xp;
|
||||
s_seq_po = po;
|
||||
s_seq_ready = 1u;
|
||||
if (!on)
|
||||
{
|
||||
if (s_seq_on)
|
||||
App_Auto_QueueAllNotesOffMelodic();
|
||||
s_seq_on = 0;
|
||||
for (i = 0u; i < SEQ_SLOTS; i++)
|
||||
s_seq_slot[i].used = 0;
|
||||
return;
|
||||
}
|
||||
if (s_seq_on)
|
||||
{
|
||||
uint8_t same = 1u;
|
||||
for (i = 0u; i < 8u; i++)
|
||||
{
|
||||
if (s_seq[i] != seq[i])
|
||||
same = 0u;
|
||||
}
|
||||
if (!same)
|
||||
App_Auto_QueueAllNotesOffMelodic();
|
||||
}
|
||||
for (i = 0u; i < 8u; i++)
|
||||
s_seq[i] = seq[i];
|
||||
for (i = 0u; i < 16u; i++)
|
||||
s_seq_step[i] = 0;
|
||||
for (i = 0u; i < SEQ_SLOTS; i++)
|
||||
s_seq_slot[i].used = 0;
|
||||
s_seq_on = 1u;
|
||||
}
|
||||
|
||||
/* 1 = 已用发音顺序处理(out 为最终音高) */
|
||||
static int App_Auto_MapSeq(int channel, int key, int is_on, int *out)
|
||||
{
|
||||
int ch;
|
||||
int i;
|
||||
|
||||
App_Auto_SeqRefresh();
|
||||
if (!s_seq_on || channel == BASS_CH || channel == DRUM_CH)
|
||||
return 0;
|
||||
ch = channel & 15;
|
||||
if (!is_on)
|
||||
{
|
||||
for (i = SEQ_SLOTS - 1; i >= 0; i--)
|
||||
{
|
||||
if (s_seq_slot[i].used && s_seq_slot[i].ch == (uint8_t)ch
|
||||
&& s_seq_slot[i].in == (uint8_t)key)
|
||||
{
|
||||
*out = s_seq_slot[i].out;
|
||||
s_seq_slot[i].used = 0;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
*out = key;
|
||||
return 1;
|
||||
}
|
||||
*out = s_seq[s_seq_step[ch] & 7u];
|
||||
s_seq_step[ch] = (uint8_t)((s_seq_step[ch] + 1u) & 7u);
|
||||
for (i = 0; i < SEQ_SLOTS; i++)
|
||||
{
|
||||
if (!s_seq_slot[i].used)
|
||||
{
|
||||
s_seq_slot[i].used = 1;
|
||||
s_seq_slot[i].ch = (uint8_t)ch;
|
||||
s_seq_slot[i].in = (uint8_t)key;
|
||||
s_seq_slot[i].out = (uint8_t)(*out);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
s_seq_slot[0].used = 1;
|
||||
s_seq_slot[0].ch = (uint8_t)ch;
|
||||
s_seq_slot[0].in = (uint8_t)key;
|
||||
s_seq_slot[0].out = (uint8_t)(*out);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int App_Auto_ClampMidi(int n)
|
||||
{
|
||||
if (n < 0) return 0;
|
||||
|
|
@ -229,7 +340,7 @@ static int App_Auto_MapXposeOverall(int channel, int key, uint8_t *flags)
|
|||
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 is_on)
|
||||
{
|
||||
int out = key;
|
||||
if (flags) *flags = 0;
|
||||
|
|
@ -238,6 +349,12 @@ static int App_Auto_MapNote(int channel, int key, uint8_t *flags)
|
|||
if (channel == DRUM_CH || key < 0 || key > 127)
|
||||
return key;
|
||||
|
||||
if (App_Auto_MapSeq(channel, key, is_on, &out))
|
||||
{
|
||||
if (flags) *flags = 0x20u; /* 发音顺序 */
|
||||
return out;
|
||||
}
|
||||
|
||||
if (App_Auto_NeedRegisterFix())
|
||||
{
|
||||
/* III+:把位音区修正;V~VII 且 ≥#F 时 Map* 内再叠转调 -12 */
|
||||
|
|
@ -258,6 +375,7 @@ static int App_Auto_MapNote(int channel, int key, uint8_t *flags)
|
|||
}
|
||||
#else
|
||||
(void)channel;
|
||||
(void)is_on;
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
|
@ -353,7 +471,7 @@ static int App_Auto_MapNoteOn(int channel, int key)
|
|||
s_ch_ever_mask = 0; /* 新策略周期重新报到通道 */
|
||||
}
|
||||
|
||||
out = App_Auto_MapNote(channel, key, &flags);
|
||||
out = App_Auto_MapNote(channel, key, &flags, 1);
|
||||
if (channel >= 0 && channel < 16)
|
||||
{
|
||||
uint16_t bit = (uint16_t)(1u << channel);
|
||||
|
|
@ -407,7 +525,7 @@ static void Func_CallBack_NoteOff(int channel,int key)
|
|||
{
|
||||
MidiFifoItem_t midimsg;
|
||||
uint8_t flags = 0;
|
||||
int out_key = App_Auto_MapNote(channel, key, &flags);
|
||||
int out_key = App_Auto_MapNote(channel, key, &flags, 0);
|
||||
midimsg.msg[0] = 0x80 | (channel & 0x0F);
|
||||
midimsg.msg[1] = (uint8_t)out_key;
|
||||
midimsg.msg[2] = 0;
|
||||
|
|
@ -424,7 +542,7 @@ static void Func_CallBack_NoteOn(int channel,int key,int vel)
|
|||
if (vel == 0)
|
||||
{
|
||||
uint8_t flags = 0;
|
||||
out_key = App_Auto_MapNote(channel, key, &flags); /* 视同 NoteOff */
|
||||
out_key = App_Auto_MapNote(channel, key, &flags, 0); /* 视同 NoteOff */
|
||||
}
|
||||
else
|
||||
out_key = App_Auto_MapNoteOn(channel, key);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ TONE_HDR = PROJ / "project" / "inc" / "ExtFlash_Tone_Addr.h"
|
|||
IAP_DIR = REPO / "升级" / "MCU主控升级"
|
||||
OUT_ROOT = REPO / "tools" / "out"
|
||||
|
||||
FW_VER = "0.2.56"
|
||||
FW_VER = "0.2.57"
|
||||
UI0902_RES_BASE = 0x00100000
|
||||
BOOT_FLASH_ADDR = 0x08000000
|
||||
APP_FLASH_ADDR = 0x08008000
|
||||
|
|
|
|||
Loading…
Reference in New Issue