K1Guitar/MyCode/App_Auto.c

261 lines
9.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* App_Auto.c
*
* Created on: Apr 16, 2026
* Author: monosail
*/
#include "autobandtop1.h"
#include <stdint.h>
#include "output2.bin.h"
#include "mymidififo1.h"
#include <stdlib.h>
#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();
// }
//}
// 接下来的<E69DA5>??????题是如何测试<E6B58B>??????
// <20>??????以调<E4BBA5>??????
//void AutoBandTop1_Start(void); //<2F>??????动节<E58AA8>??????
//void AutoBandTop1_Stop(void); //停<>??伴<>??
//void AutoBandTop1_SyncStart(void);//同<>?<3F><EFBFBD>??????
//void AutoBandTop1_SetRunVar(int varIndex);//设置变<E7BDAE>?<3F>0-3<>??????
//void AutoBandTop1_Preamble(int index);//设置前<E7BDAE>?<3F>0-3<>??????
//void AutoBandTop1_Postamble(int index);//位置尾<E7BDAE>?<3F>0-3<>??????
//void AutoBandTop1_Fillin(void);//触发一<E58F91>??????插入
//如果<E5A682>??????同<>?<3F><EFBFBD>??????需要再调用void AutoBandTop1_Note_On(int32_t key,int32_t vel);来输入一<E585A5>??????音<>?<3F><EFBC88>?<3F>C3<EFBC89>??????定和弦根<E5BCA6>??????
//在哪里调用这些函数?