diff --git a/APP/app_log.c b/APP/app_log.c index f782d1e..12a56c3 100644 --- a/APP/app_log.c +++ b/APP/app_log.c @@ -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); diff --git a/Global/Global.c b/Global/Global.c index 27426af..5a25d0b 100644 --- a/Global/Global.c +++ b/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; diff --git a/Global/Global.h b/Global/Global.h index 5dda84a..161ecbc 100644 --- a/Global/Global.h +++ b/Global/Global.h @@ -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); /* 切模式/停键前记住专业移调与最后指板和弦 */ diff --git a/MyCode/App_Auto.c b/MyCode/App_Auto.c index be229fc..560ee09 100644 --- a/MyCode/App_Auto.c +++ b/MyCode/App_Auto.c @@ -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); diff --git a/tools/publish_k1_release.py b/tools/publish_k1_release.py index e4b5721..5bc7f67 100644 --- a/tools/publish_k1_release.py +++ b/tools/publish_k1_release.py @@ -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