261 lines
9.2 KiB
C
261 lines
9.2 KiB
C
/*
|
||
* 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>??????
|
||
//在哪里调用这些函数?
|
||
|