/* * 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 /* ======== 路线A:#IV 降八度拦截(音师库支持八度前的临时方案) ======== * AutoBand 库对输入音符只做音级识别,输出音区由其内部 octaveBase 决定, * 固件侧在 GetChordNotesByType 里的低八度设计不可听。故在引擎输出回调处 * 按当前和弦状态对旋律通道做条件性八度偏移;音师新库到位后整体移除。 */ #define CHORD_OCTAVE_INTERCEPT_EN 1 #define DRUM_CHANNEL 9 /* 鼓通道不移调 */ /* 当前生效的八度偏移(0 或 -12)。RAM 余量极小,不用按音符表: * 状态翻转瞬间先向旋律通道补 CC123(All Notes Off) 清掉旧音区的音, * 之后 NoteOff 统一按当前偏移还原即可,不会挂音。 */ static int8_t s_chord_octave_shift = 0; static int8_t App_Auto_TargetChordOctaveShift(void) { #if CHORD_OCTAVE_INTERCEPT_EN /* 与 GetChordNotesByType 的根音八度设计对齐(整段伴奏压一个八度): * V/VI/VII(键 13~21)根音本就在低八度(43/45/47); * #IV(键 10~12 + Pitch 升)整组低八度,为最低音。 * 引擎只认音级不认八度,故在输出侧统一补 -12。 */ if (KEY_ID_1629 >= 13 && KEY_ID_1629 <= 21) return -12; if (KEY_ID_1629 >= 10 && KEY_ID_1629 <= 12 && chord_type_index_map[KEY_ID_1629].PitchOffset == 2) return -12; #endif return 0; } static void App_Auto_QueueAllNotesOff(void) { MidiFifoItem_t m; for (int ch = 0; ch < 16; ch++) { if (ch == DRUM_CHANNEL) continue; m.msg[0] = 0xB0 | ch; m.msg[1] = 123; /* CC123 All Notes Off */ m.msg[2] = 0; m.msglen = 3; mymidififo_InQueue(&m_MidiSendFifo,&m); } } /* NoteOn 路径调用:偏移状态翻转时先清旧音,再返回新偏移 */ static int8_t App_Auto_ShiftForNoteOn(void) { int8_t target = App_Auto_TargetChordOctaveShift(); if (target != s_chord_octave_shift) { LOG_I("OCT", "shift %d->%d chord=%u pitch=%u", (int)s_chord_octave_shift, (int)target, (unsigned)KEY_ID_1629, (unsigned)chord_type_index_map[KEY_ID_1629].PitchOffset); App_Auto_QueueAllNotesOff(); s_chord_octave_shift = target; } return s_chord_octave_shift; } static void Func_CallBack_ProgramChange(int channel, int program) { MidiFifoItem_t midimsg; midimsg.msg[0] = 0xc0 | channel; 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; int out_key = key; if (channel != DRUM_CHANNEL && key >= 0 && key < 128) { /* 按当前偏移还原;偏移翻转瞬间的旧音已由 CC123 清掉 */ out_key = key + s_chord_octave_shift; if (out_key < 0 || out_key > 127) out_key = key; } 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 = key; if (channel != DRUM_CHANNEL && key >= 0 && key < 128) { int8_t shift = (vel == 0) ? s_chord_octave_shift /* vel=0 视同 NoteOff */ : App_Auto_ShiftForNoteOn(); out_key = key + shift; if (out_key < 0 || out_key > 127) out_key = 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("OCT", "intercept build 20260910 en=%d", (int)CHORD_OCTAVE_INTERCEPT_EN); 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)来�??????定和弦根�?????? //在哪里调用这些函数?