/* * App_Auto.c * * Created on: Apr 16, 2026 * Author: monosail */ #include "autobandtop1.h" #include #include "output2.bin.h" #include "mymidififo1.h" #include #include "includes.h" MidiFifoSeq_t m_MidiSendFifo; #define PRESET_BUFFER_SIZE (1024*41) /* 2s.res compressed HKTK preset 39760 bytes */ static int m_presetCount = 0; static uint8_t pStoreBuffer[PRESET_BUFFER_SIZE]; //static uint8_t* pStoreBuffer; #define DEFAULT_TEMPO 100 /* ======== Bass/和弦分通道八度(无 AutoBand 源码时的输出侧映射) ======== * 代码通道(0 起,与 MIDI status 低 4 位一致):8=bass,9=鼓;其余=和弦。 * 日志打印同号(ch8=bass)。I/II(键1~6)原样;III+:bass -12 地板 E1; * 和弦 -12 夹在 E2~#G4,超上限就近和弦内音。移调≥#F(6) 时再 -12。 * 对照 Doc/吉他和弦表最终版.xlsx */ #define CHORD_REG_FIX_EN 1 #define BASS_CH 8 /* 日志 ch8 */ #define DRUM_CH 9 /* 日志 ch9 */ #define MIDI_E1 28 #define MIDI_E2 40 #define MIDI_GSHARP4 68 #define XPOSE_FS 6 /* C=0 时 #F=6 */ /* 映射策略指纹:变化时 CC123 清旧音,防挂音(不建大音符表) */ static uint8_t s_reg_fp = 0xFFu; /* 每个指纹周期内 NoteOn 映射日志限额(和弦与 bass 分开,避免和弦占满看不到 bass) */ static uint8_t s_pitch_log_left = 0; static uint8_t s_bass_log_left = 0; static uint16_t s_ch_ever_mask = 0; /* 会话内各通道是否出现过 NoteOn */ #define PITCH_LOG_PER_FP 24 #define BASS_LOG_PER_FP 16 static int App_Auto_ClampMidi(int n) { if (n < 0) return 0; if (n > 127) return 127; return n; } static int App_Auto_ChordDegree(void) { if (KEY_ID_1629 < 1 || KEY_ID_1629 > 21) return 0; return (int)((KEY_ID_1629 - 1) / 3) + 1; /* 1=I .. 7=VII */ } static int App_Auto_NeedRegisterFix(void) { /* 键 1~6 = I/II:不动;7~21 = III~VII */ return (KEY_ID_1629 >= 7 && KEY_ID_1629 <= 21) ? 1 : 0; } static int App_Auto_XposeExtra(void) { return (mGuiData[GUI_TRANSPOSE].Current >= XPOSE_FS) ? 1 : 0; } static uint8_t App_Auto_RegFingerprint(void) { /* bit0=need_fix, bit1=xpose_extra, bit2.. = 粗粒度级数 (key/3) */ uint8_t fp = 0; if (App_Auto_NeedRegisterFix()) fp |= 0x01u; if (App_Auto_XposeExtra()) fp |= 0x02u; if (KEY_ID_1629 >= 1 && KEY_ID_1629 <= 21) fp |= (uint8_t)(((KEY_ID_1629 - 1) / 3) << 2); return fp; } static void App_Auto_QueueAllNotesOffMelodic(void) { MidiFifoItem_t m; int ch; for (ch = 0; ch < 16; ch++) { if (ch == DRUM_CH) continue; m.msg[0] = (uint8_t)(0xB0 | ch); m.msg[1] = 123; /* All Notes Off */ m.msg[2] = 0; m.msglen = 3; mymidififo_InQueue(&m_MidiSendFifo, &m); } } /* 超出 #G4:在当前和弦内音(GetChordNotesByType,对照和弦表「和弦伴奏」列)中就近 */ static int App_Auto_NearestChordTone(int target) { uint8_t buff[4] = {0, 0, 0, 0}; uint8_t n; uint8_t pcs[4]; int i, oct, best = -1, best_dist = 9999; uint8_t type; if (KEY_ID_1629 < 1 || KEY_ID_1629 > 21) return App_Auto_ClampMidi(target); type = chord_type_index_map[KEY_ID_1629].type; n = GetChordNotesByType(KEY_ID_1629, type, buff); if (n == 0) return App_Auto_ClampMidi(target > MIDI_GSHARP4 ? MIDI_GSHARP4 : target); for (i = 0; i < (int)n; i++) pcs[i] = (uint8_t)(buff[i] % 12); for (i = 0; i < (int)n; i++) { for (oct = 0; oct < 11; oct++) { int cand = (int)pcs[i] + oct * 12; int dist; if (cand < MIDI_E2 || cand > MIDI_GSHARP4) continue; dist = cand - target; if (dist < 0) dist = -dist; /* 并列:优先较低者(不超过上限) */ if (dist < best_dist || (dist == best_dist && (best < 0 || cand < best))) { best_dist = dist; best = cand; } } } if (best < 0) return MIDI_GSHARP4; return best; } static int App_Auto_MapBassNote(int key, uint8_t *flags) { int out = key - 12; uint8_t f = 0x01u; /* bit0: -12 applied */ while (out < MIDI_E1) { out += 12; f |= 0x02u; /* bit1: floored up to E1 */ } if (App_Auto_XposeExtra()) { out -= 12; f |= 0x04u; /* bit2: xpose extra -12 */ while (out < MIDI_E1) { out += 12; f |= 0x02u; } } if (flags) *flags = f; return App_Auto_ClampMidi(out); } static int App_Auto_MapChordNote(int key, uint8_t *flags) { int out = key - 12; uint8_t f = 0x01u; /* bit0: -12 */ while (out < MIDI_E2) { out += 12; f |= 0x08u; /* bit3: raised to E2 */ } if (out > MIDI_GSHARP4) { out = App_Auto_NearestChordTone(out); f |= 0x10u; /* bit4: nearest chord tone */ } if (App_Auto_XposeExtra()) { out -= 12; f |= 0x04u; 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) { int out = key; if (flags) *flags = 0; #if CHORD_REG_FIX_EN if (channel == DRUM_CH || key < 0 || key > 127) return key; if (!App_Auto_NeedRegisterFix()) return key; if (channel == BASS_CH) out = App_Auto_MapBassNote(key, flags); else out = App_Auto_MapChordNote(key, flags); #else (void)channel; #endif return out; } static const char *App_Auto_RoleName(int channel) { if (channel == DRUM_CH) return "drum"; if (channel == BASS_CH) return "bass"; return "chord"; } static void App_Auto_LogPitch(int channel, int key, int out, uint8_t flags, int is_on) { const char *role; int deg; int fix; int xf; int pass; if (channel == DRUM_CH) return; if (channel == BASS_CH) { if (s_bass_log_left == 0) return; } else if (s_pitch_log_left == 0) { return; } role = App_Auto_RoleName(channel); deg = App_Auto_ChordDegree(); fix = App_Auto_NeedRegisterFix(); xf = App_Auto_XposeExtra(); pass = (!fix || 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", is_on ? "on" : "off", (unsigned)channel, role, /* 0 基,与 BASS_CH=8 一致 */ key, out, out - key, deg, (unsigned)KEY_ID_1629, (unsigned)mGuiData[GUI_TRANSPOSE].Current, xf, (unsigned)flags, pass ? "PASS " : "", (flags & 0x10u) ? "NEAR " : "", (flags & 0x02u) ? "E1UP " : "", (flags & 0x08u) ? "E2UP " : ""); if (channel == BASS_CH) { if (s_bass_log_left > 0) s_bass_log_left--; } else if (s_pitch_log_left > 0) { s_pitch_log_left--; } } /* NoteOn 路径:策略变化时清旧音;返回映射后音高 */ static int App_Auto_MapNoteOn(int channel, int key) { #if CHORD_REG_FIX_EN uint8_t fp = App_Auto_RegFingerprint(); uint8_t flags = 0; int out; if (fp != s_reg_fp) { if (s_reg_fp != 0xFFu) { LOG_I("REG", "fp %u->%u chord=%u deg=%d xp=%u xf=%d", (unsigned)s_reg_fp, (unsigned)fp, (unsigned)KEY_ID_1629, App_Auto_ChordDegree(), (unsigned)mGuiData[GUI_TRANSPOSE].Current, App_Auto_XposeExtra()); App_Auto_QueueAllNotesOffMelodic(); } else { LOG_I("REG", "fp init %u chord=%u deg=%d xp=%u", (unsigned)fp, (unsigned)KEY_ID_1629, App_Auto_ChordDegree(), (unsigned)mGuiData[GUI_TRANSPOSE].Current); } s_reg_fp = fp; s_pitch_log_left = PITCH_LOG_PER_FP; s_bass_log_left = BASS_LOG_PER_FP; s_ch_ever_mask = 0; /* 新策略周期重新报到通道 */ } out = App_Auto_MapNote(channel, key, &flags); if (channel >= 0 && channel < 16) { uint16_t bit = (uint16_t)(1u << channel); if ((s_ch_ever_mask & bit) == 0u) { s_ch_ever_mask |= bit; LOG_I("REG", "ch-seen ch%u role=%s key=%d", (unsigned)channel, App_Auto_RoleName(channel), key); } } App_Auto_LogPitch(channel, key, out, flags, 1); return out; #else (void)channel; return key; #endif } static void Func_CallBack_ProgramChange(int channel, int program) { MidiFifoItem_t midimsg; LOG_I("REG", "pc ch%u prog=%d", (unsigned)channel, program); midimsg.msg[0] = 0xc0 | (channel & 0x0F); midimsg.msg[1] = program; midimsg.msg[2] = 0; midimsg.msglen = 2; mymidififo_InQueue(&m_MidiSendFifo,&midimsg); } 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); midimsg.msg[0] = 0x80 | (channel & 0x0F); midimsg.msg[1] = (uint8_t)out_key; midimsg.msg[2] = 0; midimsg.msglen = 3; mymidififo_InQueue(&m_MidiSendFifo,&midimsg); } static void Func_CallBack_NoteOn(int channel,int key,int vel) { MidiFifoItem_t midimsg; int out_key; if (vel == 0) { uint8_t flags = 0; out_key = App_Auto_MapNote(channel, key, &flags); /* 视同 NoteOff */ } else out_key = App_Auto_MapNoteOn(channel, key); midimsg.msg[0] = 0x90 | (channel & 0x0F); midimsg.msg[1] = (uint8_t)out_key; midimsg.msg[2] = (uint8_t)vel; midimsg.msglen = 3; mymidififo_InQueue(&m_MidiSendFifo,&midimsg); } static void Func_CallBack_CC(int channel,int cc,int value) { if(cc == 7|| cc == 11) { return; } MidiFifoItem_t midimsg; midimsg.msg[0] = 0xb0 | channel; midimsg.msg[1] = cc; midimsg.msg[2] = value; midimsg.msglen = 3; mymidififo_InQueue(&m_MidiSendFifo,&midimsg); } static void Func_CallBack_PitchWheel(int channel,int cc,int value) { MidiFifoItem_t midimsg; midimsg.msg[0] = 0xe0 | channel; midimsg.msg[1] = cc; midimsg.msg[2] = value; midimsg.msglen = 3; mymidififo_InQueue(&m_MidiSendFifo,&midimsg); } /* Flash Map 0903:对照 ExtFlash_Tone_Addr.h / Doc/.../FLASH_MAP.txt */ static const char *ToneFlashMapBank(uint32_t abs) { if (abs >= EXTFLASH_BIN1_RHYTHM_ADDR && abs < EXTFLASH_BIN1_RHYTHM_ADDR + EXTFLASH_BIN1_RHYTHM_SIZE) return "BIN1_RHYTHM"; if (abs >= EXTFLASH_BIN2_SONG_HAITIAN_ADDR && abs < EXTFLASH_BIN2_SONG_HAITIAN_ADDR + EXTFLASH_BIN2_SONG_HAITIAN_SIZE) return "BIN2_SONG_HAITIAN"; if (abs >= EXTFLASH_BIN3_UNIVERSAL_ADDR && abs < EXTFLASH_BIN3_UNIVERSAL_ADDR + EXTFLASH_BIN3_UNIVERSAL_SIZE) return "BIN3_UNIVERSAL"; if (abs >= FLASH_ADDR_AUTOBAND_LEGACY && abs < FLASH_ADDR_AUTOBAND_LEGACY + 0x10000UL) return "LEGACY_AUTOBAND"; return "OUT_OF_MAP"; } static uint32_t s_flash_rd_last_base = 0xFFFFFFFFu; static uint16_t s_flash_rd_log_cnt = 0; static int Func_CallBack_ReadFlash(int address,int length,uint8_t* pOutput) { uint32_t base = (uint32_t)ADDRESS; uint32_t abs = base + (uint32_t)address; const char *bank; if (base != s_flash_rd_last_base) { s_flash_rd_last_base = base; s_flash_rd_log_cnt = 0; LOG_I("FLASH_RD", "bank base=0x%08X map=%s (BIN1=0x%08X BIN2=0x%08X BIN3=0x%08X)", (unsigned)base, ToneFlashMapBank(base), (unsigned)EXTFLASH_BIN1_RHYTHM_ADDR, (unsigned)EXTFLASH_BIN2_SONG_HAITIAN_ADDR, (unsigned)EXTFLASH_BIN3_UNIVERSAL_ADDR); } bank = ToneFlashMapBank(abs); /* 加载阶段打印前若干次 + 每次 offset==0;避免播放期刷屏 */ if (s_flash_rd_log_cnt < 16u || address == 0) { LOG_I("FLASH_RD", "abs=0x%08X off=%d len=%d map=%s", (unsigned)abs, address, length, bank); if (s_flash_rd_log_cnt < 0xFFFFu) s_flash_rd_log_cnt++; if (s_flash_rd_log_cnt == 16u) LOG_I("FLASH_RD", "further reads omitted (same base)"); } else if (bank[0] == 'O') { /* 读到 Map 外:始终告警 */ LOG_W("FLASH_RD", "OUT_OF_MAP abs=0x%08X off=%d len=%d base=0x%08X", (unsigned)abs, address, length, (unsigned)base); } W25Q128_Read(pOutput, abs, length); return 0; } // void App_Auto_Init(void) { LOG_I("REG", "ch map bass=%d drum=%d fix=%d E1=%d E2=%d Gs4=%d", BASS_CH, DRUM_CH, CHORD_REG_FIX_EN, MIDI_E1, MIDI_E2, MIDI_GSHARP4); LOG_I("REG", "PITCH log: chN(role) in->out d=delta deg=I..VII xp=xpose xf=#Fextra fl=flags"); AutoBandTop1_SetPresetStoreBuffer(pStoreBuffer,PRESET_BUFFER_SIZE); AutoBandTop1_Init(); AutoBandTop1_RegisterCallBack_NoteOn(Func_CallBack_NoteOn); AutoBandTop1_RegisterCallBack_NoteOff(Func_CallBack_NoteOff); AutoBandTop1_RegisterCallBack_ProgChange(Func_CallBack_ProgramChange); AutoBandTop1_RegisterCallBack_CC(Func_CallBack_CC); AutoBandTop1_RegisterCallBack_PitchWheel(Func_CallBack_PitchWheel); AutoBandTop1_RegisterReadFlashCallback(Func_CallBack_ReadFlash); m_presetCount = AutoBandTop1_GetPresetItemCount(); int ret = AutoBandTop1_LoadPresetItemFromFlash(0); if (ret != 0) { uint8_t hdr[16]; W25Q128_Read(hdr, (uint32_t)ADDRESS, 16); LOG_E("AUTO", "LoadPreset fail ret=%d addr=0x%08X map=%s magic=%02X%02X%02X%02X cnt=%lu", ret, (unsigned)ADDRESS, ToneFlashMapBank((uint32_t)ADDRESS), hdr[0], hdr[1], hdr[2], hdr[3], (unsigned long)(hdr[12] | (hdr[13] << 8) | (hdr[14] << 16) | (hdr[15] << 24))); } else { LOG_I("AUTO", "LoadPreset ok name=%s addr=0x%08X map=%s", AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?", (unsigned)ADDRESS, ToneFlashMapBank((uint32_t)ADDRESS)); AutoBandTop1_ChangeBPM(mGuiData[1].Current); } } //void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //{ // if (htim == (&htim6)) { // // 1ms timer // AutoBandTop1_Metronome_External_1msTick(); // } //} // 接下来的�??????题是如何测试�?????? // �??????以调�?????? //void AutoBandTop1_Start(void); //�??????动节�?????? //void AutoBandTop1_Stop(void); //停�??伴�?? //void AutoBandTop1_SyncStart(void);//同�?�启�?????? //void AutoBandTop1_SetRunVar(int varIndex);//设置变�?�(0-3�?????? //void AutoBandTop1_Preamble(int index);//设置前�?�(0-3�?????? //void AutoBandTop1_Postamble(int index);//位置尾�?�(0-3�?????? //void AutoBandTop1_Fillin(void);//触发一�??????插入 //如果�??????同�?�启�??????,需要再调用void AutoBandTop1_Note_On(int32_t key,int32_t vel);来输入一�??????音�?�(例�?�C3)来�??????定和弦根�?????? //在哪里调用这些函数?