K1Guitar/Global/Global.c

1655 lines
55 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.

#include "includes.h"
rt_mutex_t TM1629_Mutex = RT_NULL;
rt_mutex_t TM1617_Mutex = RT_NULL;
rt_mq_t MainTask_msg = RT_NULL;
rt_mq_t UITask_msg = RT_NULL;
rt_mq_t BLTask_msg = RT_NULL;
rt_sem_t UART4_sem = RT_NULL;
// 定义定时器ID
rt_timer_t OutTime_Stop_timer = RT_NULL;
const Touch_AreaTypeDef *pCurrentTouch_Area = Touch_ModeSelect_Areas;
GUI_SWITCH mGuiData[] = {
{ 0, 0, 11, GUI_TRANSPOSE}, // 0
{ 78, 40, 260, GUI_SPEED}, // 1
{ 0, 0, 1, GUI_MODE_PARAM}, // 2 ?? <20>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD>࿪<EFBFBD><E0BFAA> 0=<3D><> 1=<3D><>
{ 0, 0, 4, GUI_TIMBRE_SELECT}, // 3
{ 0, 0, 3, GUI_PLAY_SECTION}, // 4
{ 0, 0, 3, GUI_TAB_INDEX}, // 5
{ 0, 0, 1, GUI_AUTOBAND_SW}, // 6 ?? <20>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD>࿪<EFBFBD><E0BFAA> 0=<3D><> 1=<3D><>
{ 1, 0, 1, GUI_BL_SW},
{ 0, 0, 3, GUI_TAB_LAST_INDEX},
};
GUI_SWITCH ParamGuiData[] =
{
{ 0, 0, 30, SONG_MODE_PARAM}, // 0 节奏类型(1.bin,约 31 首)
{ 0, 0, 0, EXPRESS_MODE_PARAM}, // 1 本地曲目(2.bin;Max 由 UI_ReloadTonePreset←LOCAL_SONG_COUNT)
{ 0, 0, 2, ALL_MODE_PARAM}, // 2
};
const LEBEL_UI UI_Label[] = {
/* 0819:移调/速度 标签11、主值16 */
{UI0902_MARGIN_X, UI0902_CARD_W, UI0902_CARD_Y, UI0902_CARD_H,
(const uint8_t*)"\xD2\xC6\xB5\xF7", LCD_ALIGN_CENTER, WHITE,BLACK,UI0902_FONT_LABEL,
LCD_ALIGN_CENTER,WHITE,BLACK,UI0902_FONT_HERO, GUI_TRANSPOSE},
{UI0902_MARGIN_X + UI0902_CARD_W + UI0902_CARD_GAP, UI0902_CARD_W, UI0902_CARD_Y, UI0902_CARD_H,
(const uint8_t*)"\xCB\xD9\xB6\xC8", LCD_ALIGN_CENTER, WHITE,BLACK,UI0902_FONT_LABEL,
LCD_ALIGN_CENTER,WHITE,BLACK,UI0902_FONT_HERO, GUI_SPEED},
/* 0819:TAB/行标题11、曲目正文13 */
{UI0902_MARGIN_X, UI0902_CONTENT_W, UI0902_MODE_Y, UI0902_MODE_H,
(const uint8_t*)"\xB8\xE8\xC7\xFA\xD1\xA1\xD4\xF1", LCD_ALIGN_LEFT,WHITE,BLACK,UI0902_FONT_LABEL,
LCD_ALIGN_CENTER,WHITE,BLACK,UI0902_FONT_BODY,GUI_MODE_PARAM},
{UI0902_MARGIN_X, UI0902_CONTENT_W, UI0902_TIMBRE_Y, UI0902_TIMBRE_H,
(const uint8_t*)"\xCF\xD2\xD2\xF4\xC9\xAB\xD1\xA1\xD4\xF1",LCD_ALIGN_LEFT,WHITE,BLACK,UI0902_FONT_LABEL,
LCD_ALIGN_CENTER,WHITE,BLACK,UI0902_FONT_BODY,GUI_TIMBRE_SELECT},
/* 0819:演奏段落 字号13 */
{UI0902_MARGIN_X, UI0902_CONTENT_W, UI0902_SECTION_Y, UI0902_SECTION_H,
(const uint8_t*)"\xD1\xDD\xD7\xE0\xB6\xCE\xC2\xE4",LCD_ALIGN_LEFT,WHITE,BLACK,UI0902_FONT_BODY,
LCD_ALIGN_CENTER,WHITE,BLACK,UI0902_FONT_BODY,GUI_PLAY_SECTION},
};
/* 0904 / 0902-06:左→右 伴奏|弦|麦|麦混响,轨宽31 顶≈58 */
Fader_t faders[4] = {
{ 27, 58, 31, 215, 7, 0, 0, "\xB0\xE9\xD7\xE0" }, /* 0: 伴奏 */
{ 78, 58, 31, 215, 7, 1, 0, "\xCF\xD2" }, /* 1: 弦 */
{ 130, 58, 31, 215, 7, 2, 0, "\xC2\xF3" }, /* 2: 麦 */
{ 182, 58, 31, 215, 7, 3, 0, "\xC2\xF3\xBB\xEC\xCF\xEC" } /* 3: 麦混响 */
};
const Touch_AreaTypeDef Touch_Areas[] =
{
/* Xmin,Xmax,Ymin,Ymax, Flag, Value, ID, action — 与 UI_Label 网格同步 */
{ 12, 64, UI0902_CARD_Y, UI0902_CARD_Y + UI0902_CARD_H - 1, -1, 0, GUI_TRANSPOSE, NULL },
{ 65, 116, UI0902_CARD_Y, UI0902_CARD_Y + UI0902_CARD_H - 1, 1, 0, GUI_TRANSPOSE, NULL },
{ 125, 176, UI0902_CARD_Y, UI0902_CARD_Y + UI0902_CARD_H - 1, -1, 0, GUI_SPEED, NULL },
{ 177, 228, UI0902_CARD_Y, UI0902_CARD_Y + UI0902_CARD_H - 1, 1, 0, GUI_SPEED, NULL },
/* 节奏类型 / 本地曲目 TAB(0902:12-66 / 72-127) */
{ 12, 66, UI0902_MODE_Y, UI0902_MODE_Y + UI0902_TAB_H - 1, 0, 0, GUI_AUTOBAND_SW, NULL },
{ 72, 127, UI0902_MODE_Y, UI0902_MODE_Y + UI0902_TAB_H - 1, 0, 1, GUI_AUTOBAND_SW, NULL },
{ 13, 120, UI0902_MODE_Y + UI0902_TAB_H + 4, UI0902_MODE_Y + UI0902_MODE_H - 1, -1, 0, GUI_MODE_PARAM, NULL },
{ 121, 227, UI0902_MODE_Y + UI0902_TAB_H + 4, UI0902_MODE_Y + UI0902_MODE_H - 1, 1, 0, GUI_MODE_PARAM, NULL },
{ 13, 120, UI0902_TIMBRE_Y, UI0902_TIMBRE_Y + UI0902_TIMBRE_H - 1, -1, 0, GUI_TIMBRE_SELECT, NULL },
{ 121, 227, UI0902_TIMBRE_Y, UI0902_TIMBRE_Y + UI0902_TIMBRE_H - 1, 1, 0, GUI_TIMBRE_SELECT, NULL },
/* 演奏段落 1~4(0902-04:12/69/125/182,宽 47,步进 57) */
{ 12, 58, UI0902_SECTION_Y, UI0902_SECTION_Y + UI0902_SECTION_BTN_H - 1, 0, 0, GUI_PLAY_SECTION, NULL },
{ 69, 115, UI0902_SECTION_Y, UI0902_SECTION_Y + UI0902_SECTION_BTN_H - 1, 0, 1, GUI_PLAY_SECTION, NULL },
{ 126, 172, UI0902_SECTION_Y, UI0902_SECTION_Y + UI0902_SECTION_BTN_H - 1, 0, 2, GUI_PLAY_SECTION, NULL },
{ 183, 229, UI0902_SECTION_Y, UI0902_SECTION_Y + UI0902_SECTION_BTN_H - 1, 0, 3, GUI_PLAY_SECTION, NULL },
/* 底栏四键:万能 | 普通 | 专业 | 设置(60×55,y265–319) */
{ UI0902_NAV_X0(0), UI0902_NAV_X1(0), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_UNIVERSAL, GUI_NAV_BAR, NULL },
{ UI0902_NAV_X0(1), UI0902_NAV_X1(1), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_NORMAL, GUI_NAV_BAR, NULL },
{ UI0902_NAV_X0(2), UI0902_NAV_X1(2), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_EXPERT, GUI_NAV_BAR, NULL },
{ UI0902_NAV_X0(3), UI0902_NAV_X1(3), UI0902_NAV_Y0, UI0902_NAV_Y1, 0, UI0902_NAV_SETTING, GUI_NAV_BAR, NULL },
};
/* 推子 0~10 共 11 档;档10 取 AW816 上限附近 */
int16_t MIC_VOL_MAP[11]= {-9000,-7000,-5000,-3000,-1000,1000,3000,5000,7000,9000,1200};
uint8_t TOUCH_AREA_NUM = (sizeof(Touch_Areas)/sizeof(Touch_AreaTypeDef));
const uint8_t Version[3] = {0,2,17}; /* major.minor.patch — 0.2.17: Type-C charge battery icon; BLE timbre/style UI sync via UI task; RTT midi/string inject */
uint8_t Led = 8;
bool PressFlag = 0;
void Return_Light_buff(uint8_t key_id_1629);
uint8_t Band_IsPlaying(void);
uint8_t AutoCloseTime = 15;
uint16_t mAutoPowerOffCount = 15 * 60;
bool AutoCloseFlag = true;
void MainTask_Sendmsg(uint16_t ID, uint16_t ID2, uint16_t HiByte, uint16_t LoByte)
{
DisplayTaskMessage_Type msg;
rt_err_t ret;
msg.MessageType = ID; /* 业务消息 ID(MSG_ID_xxx) */
msg.ID = ID2;
msg.HiByte = HiByte;
msg.LoByte = LoByte;
ret = rt_mq_send(MainTask_msg, &msg, sizeof(DisplayTaskMessage_Type));
if (ret != RT_EOK) {
LOG_E("MSG", "send fail id=%u ret=%d", (unsigned)ID, (int)ret);
} else if (ID == MSG_ID_TOUCH || ID == MSG_ID_TOUCH_LONG_REPEAT ||
ID == MSG_ID_POWER_ON || ID == MSG_ID_POWER_OFF ||
ID == MSG_ID_BP_KEY || ID == MSG_ID_TAP_SPEED ||
ID == MSG_ID_BP_TRANSPOSE) {
LOG_I("MSG", "id=%u hi=%u lo=%u", (unsigned)ID, (unsigned)HiByte, (unsigned)LoByte);
}
}
void UITask_Sendmsg(uint16_t ID, uint16_t ID2, uint16_t HiByte, uint16_t LoByte)
{
DisplayTaskMessage_Type msg;
msg.MessageType = ID; /* 业务消息 ID(MSG_ID_xxx) */
msg.ID = ID2;
msg.HiByte = HiByte;
msg.LoByte = LoByte;
rt_mq_send(UITask_msg, &msg, sizeof(DisplayTaskMessage_Type));
}
void BLTask_Sendmsg(uint16_t ID, uint16_t ID2, uint16_t HiByte, uint16_t LoByte)
{
DisplayTaskMessage_Type msg;
msg.MessageType = ID; /* 业务消息 ID(MSG_ID_xxx) */
msg.ID = ID2;
msg.HiByte = HiByte;
msg.LoByte = LoByte;
rt_mq_send(BLTask_msg, &msg, sizeof(DisplayTaskMessage_Type));
}
#define KEY_PRESS_LEVEL RESET
#define PWR_ON_HOLD_MS 3000 /* 关机态长按 3s → 开机 */
#define PWR_OFF_HOLD_MS 2000 /* 开机态长按 2s → 关机 */
#define PWR_OFF_GRACE_MS 1500 /* 请求优雅关机后,超时仍未切轨则紧急断电+复位 */
typedef enum
{
KEY_STATE_IDLE,
KEY_STATE_PRESS,
KEY_STATE_NONE,
}PwrKeyState;
static PwrKeyState pwr_key_state = KEY_STATE_IDLE;
static uint32_t pwr_press_tick = 0;
/* 当前开关机状态(UI_Idle.c 等 extern 引用) */
bool powon = false;
/* 电源轨是否已切断(优雅关机与紧急关机共用;ISR 依此判断是否还需强切) */
static volatile uint8_t s_pwr_rails_cut = 0;
/* TMR7:关机长按已触发请求后的紧急倒计时(ms) */
static volatile uint16_t s_pwr_off_grace_ms = 0;
/* ==================== 开机/关机动作 ==================== */
/* 仅 GPIO/背光:可在 ISR / HardFault 中调用,禁止 RTOS/Flash/日志 */
void PowerOff_CutRails(void)
{
s_pwr_rails_cut = 1u;
powon = false;
LCD_BLK_Clr();
BSP_MainPowerEnable(0);
BSP_DreamCorePowerEnable(0);
BSP_HT7178PowerEnable(0);
BSP_BlueToothPowerEnable(0);
}
static void PowerOff_EmergencyReset(void)
{
PowerOff_CutRails();
/* 主循环已死时无法画充电页:复位后按 USB 状态进充电/待机 */
nvic_system_reset();
}
void System_PowerOn(void)
{
LOG_I("PWR", "power_on begin");
/* 软关机不复位 MCU:清掉上次会话演奏状态,默认当前调一级大和弦(C 调=do mi sol) */
StartFlag = 0;
Accomp_ApplyDefaultIChord();
app_tm1629_reset_key_state();
s_pwr_rails_cut = 0u;
s_pwr_off_grace_ms = 0u;
/* 本次长按已用于开机:先消耗,再置 powon,防止 TMR7 ISR 把同一按压力即判为关机 */
Power_Key_ConsumePress();
/* 控制电源硬件;蓝牙按 NVM/系统设置开关恢复,勿强制常开 */
BSP_MainPowerEnable(1);
BSP_DreamCorePowerEnable(1);
BSP_HT7178PowerEnable(1);
/* 蓝牙按 NVM/系统设置开关恢复,勿强制常开 */
BSP_BlueToothPowerEnable((uint8_t)(mGuiData[GUI_BL_SW].Current ? 1 : 0));
powon = true; /* 更新开机标志 */
/* 先通知 UI 亮屏,避免外设初始化阻塞导致长时间黑屏 */
LOG_I("PWR", "send POWER_ON");
MainTask_Sendmsg(MSG_ID_POWER_ON, 0, 0, 0);
AD85020B_Init();
TM1629D_Key_LED_Init();
app_tm1617_init();
XPT2046_Init();
App_Auto_Init();
/* App 协议走 UART4:须在开机后即解析,不能等到触摸选模式才 StartTask */
StartTask();
LOG_I("PWR", "periph ready + BT UART recv started");
}
static void PowerOn(void)
{
System_PowerOn();
}
static void PowerOff(void)
{
uint8_t nvm_ret;
LOG_I("PWR", "power_off begin");
powon = false; /* 更新关机标志 */
s_pwr_off_grace_ms = 0u; /* 主路径已接管,取消紧急倒计时 */
StopFullTask();
/* 关机前把当前调音台推子与蓝牙开关写入 NVM,保证再次开机保持 */
{
NvmParam_Type *nvm = drv_nvm_param_ptr();
nvm->param.BandVol = faders[0].val;
nvm->param.StringVol = faders[1].val;
nvm->param.MIcVol = faders[2].val;
nvm->param.MicRev = faders[3].val;
nvm->param.BluetoothSW = (uint8_t)(mGuiData[GUI_BL_SW].Current ? 1 : 0);
}
nvm_ret = drv_nvm_save_to_flash();
LOG_I("PWR", "nvm_save=%u", (unsigned)nvm_ret);
LCD_FillByColor(0, 0, 240, 320, BLACK);
LCD_BLK_Clr();
LCD_WR_REG(0x28); /* Display OFF */
app_tm1629_all_off();
app_tm1617_off();
/* BT 先留窗口转发已排队 ACK,再切全部电源轨 */
rt_thread_mdelay(30);
PowerOff_CutRails();
CurrUIProcress = IdleProcess;
MainTask_Sendmsg(MSG_ID_POWER_OFF, 0, 0, 0);
LOG_I("PWR", "power_off done (soft-off)");
/* Type-C 有供电:维持现有逻辑,显示 Charging Image(POWOFF_CHARG) */
{
uint8_t i;
for (i = 0; i < 4; i++)
{
app_adc_in1_scan_part(4);
if (usb_charging_state || app_adc_usb_is_plugged())
break;
rt_thread_mdelay(5);
}
if (usb_charging_state || app_adc_usb_is_plugged())
{
usb_charging_state = 1;
usb_charging_laststate = 1;
LOG_I("PWR", "soft-off with Type-C → charge UI");
MainTask_Sendmsg(MSG_ID_POWOFF_CHARG, percentage, 1, 0);
}
}
}
/* App remote power-off request (BLE cmd 05 00): executed in main-loop context,
so StopFullTask() never detaches the calling BT thread itself. */
static volatile uint8_t s_sys_poweroff_req = 0;
void System_RequestPowerOff(void)
{
s_sys_poweroff_req = 1;
/* 主循环若已卡死:TMR7 紧急倒计时仍会强切电源 */
if (s_pwr_off_grace_ms == 0u && !s_pwr_rails_cut)
s_pwr_off_grace_ms = PWR_OFF_GRACE_MS;
}
void System_PowerOff_Poll(void)
{
if (s_sys_poweroff_req)
{
s_sys_poweroff_req = 0;
if (powon)
PowerOff();
}
}
/*
* TMR7 1ms(优先级高于伴奏 TMR6):与主循环解耦的关机键扫描。
* 正常:请求优雅关机;超时未切轨 → 紧急断电并复位。
*/
static uint16_t s_isr_hold_ms = 0;
static uint8_t s_isr_fired = 0;
/* 开机时消耗本次按键:同一次长按已用于开机,须松开后重新长按 2s 才关机。
* 须在 powon 置 true 前调用,避免 ISR 在开机瞬间把本次长按误判为关机。 */
void Power_Key_ConsumePress(void)
{
s_isr_fired = 1;
}
void Power_Key_IsrTick(void)
{
flag_status st;
/* RTT/USB 烧 ExtFlash 期间禁止软关机,避免长按/粘键打断 FLASH_ACK */
if (app_log_flash_busy())
return;
st = BSP_GetPowerKey();
if (st == KEY_PRESS_LEVEL)
{
if (s_isr_hold_ms < 60000u)
s_isr_hold_ms++;
}
else
{
s_isr_hold_ms = 0;
s_isr_fired = 0;
}
/* 仅处理「开机态关机」;开机仍由主循环负责(需完整外设初始化) */
if (powon && !s_isr_fired && s_isr_hold_ms >= PWR_OFF_HOLD_MS)
{
s_isr_fired = 1;
System_RequestPowerOff();
}
if (s_pwr_off_grace_ms > 0u)
{
s_pwr_off_grace_ms--;
if (s_pwr_off_grace_ms == 0u && !s_pwr_rails_cut)
PowerOff_EmergencyReset();
}
}
/* HardFault / MemManage / BusFault / UsageFault:死循环中仍可长按关机 */
void Power_Key_FaultLoop(void)
{
uint32_t hold = 0;
for (;;)
{
if (BSP_GetPowerKey() == KEY_PRESS_LEVEL)
{
hold++;
if (hold >= PWR_OFF_HOLD_MS)
{
PowerOff_EmergencyReset();
}
}
else
{
hold = 0;
}
Delay_us(1000);
}
}
static rt_err_t Power_HardFault_Hook(void *context)
{
(void)context;
Power_Key_FaultLoop();
return -RT_ERROR; /* 不可达 */
}
void Power_Key_WatchdogInit(void)
{
rt_hw_exception_install(Power_HardFault_Hook);
wk_tmr7_pwrkey_init();
}
void Power_Key_Scan()
{
flag_status status = BSP_GetPowerKey();
uint32_t now = rt_tick_get();
uint32_t holdtick = 0;
/* 复位/烧录后:无 USB 则自动开机一次;有 Type-C 则进关机充电画面(勿进 Logo) */
if (powon == false && pwr_key_state == KEY_STATE_IDLE && pwr_press_tick == 0)
{
static uint8_t s_auto_poweron_pending = 1;
if (s_auto_poweron_pending)
{
uint8_t i;
s_auto_poweron_pending = 0;
/* 多采几次 USB 检测,避免上电瞬间误判 */
for (i = 0; i < 8; i++)
{
app_adc_in1_scan_part(4);
if (usb_charging_state || app_adc_usb_is_plugged())
break;
rt_thread_mdelay(5);
}
if (usb_charging_state || app_adc_usb_is_plugged())
{
usb_charging_state = 1;
usb_charging_laststate = 1;
LOG_I("PWR", "boot with USB → charge UI (no logo)");
CurrUIProcress = IdleProcess;
MainTask_Sendmsg(MSG_ID_POWOFF_CHARG, percentage, 1, 0);
pwr_key_state = KEY_STATE_NONE;
return;
}
LOG_I("PWR", "auto power_on");
PowerOn();
pwr_key_state = KEY_STATE_NONE; /* 忽略开机瞬间可能残留的按键电平 */
return;
}
}
switch(pwr_key_state)
{
case KEY_STATE_IDLE:
if(status == KEY_PRESS_LEVEL)
{
pwr_press_tick = now;
pwr_key_state = KEY_STATE_PRESS;
}
break;
case KEY_STATE_PRESS:
if(status != KEY_PRESS_LEVEL) /* 提前松开,放弃 */
{
pwr_press_tick = 0;
pwr_key_state = KEY_STATE_IDLE;
break;
}
holdtick = now - pwr_press_tick;
/* 关机长按 3s 开机;开机态关机改由 TMR7 ISR(主循环卡死仍可关) */
if(powon == false && holdtick >= PWR_ON_HOLD_MS)
{
LOG_I("PWR", "key power_on hold=%u", (unsigned)holdtick);
PowerOn();
pwr_key_state = KEY_STATE_NONE;
}
break;
case KEY_STATE_NONE:
if(status != KEY_PRESS_LEVEL) /* 松开后复位,允许下次触发 */
{
pwr_key_state = KEY_STATE_IDLE;
pwr_press_tick = 0;
}
break;
}
}
/* ============================================================
* 缺失符号补定义(AutoBandTop1 模块源码缺失,这些是它依赖的变量)
* ============================================================ */
/* UI 当前处理函数指针(UI_global.c extern 引用) */
void (*CurrUIProcress)(DisplayTaskMessage_Type xEvent);
/* UI 公共全局变量 */
volatile uint32_t ADDRESS = FLASH_ADDR_MODE_UNIVERSAL;
uint8_t KEY_ID_1629 = 0;
bool StartFlag = false;
bool InModeFlag = false;
uint8_t key_toggle[4] = {0};
/* ============================================================
* UI 公共函数补定义
* ============================================================ */
/* 自动关机:有操作时复位秒计数;主循环每秒递减,到 0 关机 */
void ResetAutoPowerCount(void)
{
if (!AutoCloseFlag || AutoCloseTime == 0)
return;
mAutoPowerOffCount = (uint16_t)(AutoCloseTime * 60);
}
void AutoPowerOff_Scan(void)
{
static uint32_t s_last_sec_tick = 0;
uint32_t now;
if (!powon || !AutoCloseFlag || AutoCloseTime == 0)
return;
now = rt_tick_get();
if (s_last_sec_tick == 0)
{
s_last_sec_tick = now;
return;
}
if ((now - s_last_sec_tick) < RT_TICK_PER_SECOND)
return;
s_last_sec_tick = now;
if (mAutoPowerOffCount > 0)
mAutoPowerOffCount--;
if (mAutoPowerOffCount == 0)
{
LOG_I("PWR", "auto power_off idle=%umin", (unsigned)AutoCloseTime);
PowerOff();
}
}
/* GUI 数值调整(简单实现:更新当前值) */
uint8_t GUI_Item_AjustValue(GUI_SWITCH * Group, int8_t Dir)
{
uint8_t ret= 0;
if (Dir > 0) {
if (Group->Current < Group->Max) {
Group->Current++;
ret = 1;
}
} else if (Dir < 0) {
if (Group->Current > Group->Min) {
Group->Current--;
ret = 1;
}
}
return ret;
}
void GUI_Item_AjustLoopValue(GUI_SWITCH * Group, int8_t Dir)
{
if (Dir > 0) {
if (Group->Current < Group->Max) {
Group->Current++;
} else {
Group->Current = Group->Min;
}
} else if (Dir < 0) {
if (Group->Current > Group->Min) {
Group->Current--;
} else {
Group->Current = Group->Max;
}
}
}
void Send_volume(uint8_t vol)
{
uint8_t mMasterSysExVolume[12] = { 0xF0, 0x41, 0x00, 0x42, 0x12, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00,0xF7};
memset(mMasterSysExVolume+8,vol,1);
SendMidiDataToDreamDSP(mMasterSysExVolume,12);
}
/* 重发当前主音量:开机补发 / 进模式主页 / 开始播放前调用。
* Dream 上电早期可能丢 SysEx,丢失后旋钮不动就不再重发,
* 会出现"图标低音量、实际音量大";在关键时机重发可自愈 */
void MasterVolume_Reapply(void)
{
uint8_t vol = app_adc_get_volume();
app_adc_sync_volume(vol); /* 兜底刷新 level,保证图标与下发值一致 */
Send_volume(vol);
}
/* 当前调一级大和弦:把位1 + maj + 自然音(再经 GUI_TRANSPOSE 移调 → C调即 do mi sol) */
void Accomp_ApplyDefaultIChord(void)
{
KEY_ID_1629 = 1;
chord_type_index_map[1].type = 0; /* maj */
chord_type_index_map[1].PitchOffset = 1; /* 自然,非 # */
PressFlag = 0;
}
void Accomp_UpdatePlayingChord(void)
{
uint8_t midi_note_buff[4] = {0,};
uint8_t note_cnt = 0;
uint8_t i;
if (!StartFlag)
return;
if (KEY_ID_1629 == 0 || KEY_ID_1629 == 22 || KEY_ID_1629 == 23)
Accomp_ApplyDefaultIChord();
note_cnt = GetChordNotesByType(KEY_ID_1629,
chord_type_index_map[KEY_ID_1629].type,
midi_note_buff);
for (i = 0; i < note_cnt; i++)
AutoBandTop1_Note_On(midi_note_buff[i], 0x50);
}
/* 尾奏期间冻结走向灯:保持全绿,避免 LOOP_LIGHT 按小节刷红 */
static uint8_t s_outro_led_all_green = 0;
/* 尾奏起奏:Postamble(0) + 同步起奏 + 当前和弦 Note_On。
* 供 ADC 独立尾奏键与万能模式第4段落键共用。
* 万能走 piece 循环,须先退出 piece,否则 Postamble 舞台切不过去;
* 尾奏 MIDI 须由音师写入 3.bin(见 Doc/音师需求_万能3.bin尾奏_20260914.md)。 */
void AutoBand_StartOutro(void)
{
uint8_t midi_note_buff[4] = {0,};
uint8_t note_cnt = 0;
if (KEY_ID_1629 == 0 || KEY_ID_1629 == 22 || KEY_ID_1629 == 23)
Accomp_ApplyDefaultIChord();
/* 退出和弦走向 piece,再进风格尾奏舞台 */
AutoBandTop1_SetPiecePlayMode(0, 0, 0);
AutoBandTop1_Postamble(0);
/* 尾奏:指板 LED 全绿(停走 LOOP 走向红灯) */
s_outro_led_all_green = 1;
app_tm1629_all_on(LED_COLOR_G);
/* 尾奏起奏前补发主音量,保证与图标一致 */
MasterVolume_Reapply();
AutoBandTop1_SyncStart();
wk_delay_ms(50);
note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff);
for(uint8_t i = 0;i < note_cnt;i++)
{
AutoBandTop1_Note_On(midi_note_buff[i],0x50);
}
}
void ADC_IN1_KEY_Handle(uint8_t key,bool on)
{
uint8_t midi_note_buff[4] = {0,};
uint8_t note_cnt = 0;
uint8_t is_preamble = 0;
key_toggle[key] = !key_toggle[key];
if(key_toggle[key] == 1)
{
//ResetAutoPowerCount();
if(key == 1)
{
/* 尾奏:与万能模式第4键共用同一起奏路径 */
AutoBand_StartOutro();
}
else
{
s_outro_led_all_green = 0; /* 前奏/断奏:恢复走向灯 */
switch(key)
{
case 3: AutoBandTop1_Preamble(0); is_preamble = 1; break;
case 0: AutoBandTop1_Preamble(1); is_preamble = 1; break;
case 2: AutoBandTop1_Postamble(1); break;
}
/* 前奏:须在 SyncStart 前落到当前调一级,否则前 50ms 伴奏按旧把位进 */
if (is_preamble)
Accomp_ApplyDefaultIChord();
else if (KEY_ID_1629 == 0 || KEY_ID_1629 == 22 || KEY_ID_1629 == 23)
Accomp_ApplyDefaultIChord();
/* 前奏/断奏起奏前补发主音量,保证与图标一致 */
MasterVolume_Reapply();
//AutoBandTop1_Start();
AutoBandTop1_SyncStart();
wk_delay_ms(50);
note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff);
for(uint8_t i = 0;i < note_cnt;i++)
{
AutoBandTop1_Note_On(midi_note_buff[i],0x50);
}
if (is_preamble)
StartFlag = 1;
}
}
else
{
Stop_AutoBand();
//StartFlag = 0;
//tick_ever_positive = false;
for(uint8_t i = 0;i < 4;i++) key_toggle[i] = 0;
}
}
void TM1617_Handle(uint8_t key)
{
if(mGuiData[GUI_TAB_INDEX].Current == 3)
{
uint8_t value = 0;
switch(key)
{
case 0: value = 0; break;
case 1: value = 1; break;
case 2: value = 2; break;
case 3: value = 3; break;
}
mGuiData[GUI_PLAY_SECTION].Current = value;
//AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current);
//Refresh_SectionSelect(last_value, &UI_Label[GUI_PLAY_SECTION], &mGuiData[GUI_PLAY_SECTION]);
//Draw_Tab_Section_Menu(&UI_Label[GUI_PLAY_SECTION], mGuiData[GUI_PLAY_SECTION].Current);
}else
{
switch(key)
{
case 0: MainTask_Sendmsg(MSG_ID_TOUCH, 1, 40, 255); break;
case 1: MainTask_Sendmsg(MSG_ID_TOUCH, 1, 90, 255); break;
case 2: MainTask_Sendmsg(MSG_ID_TOUCH, 1, 140, 255); break;
case 3: MainTask_Sendmsg(MSG_ID_TOUCH, 1, 200, 255); break;
}
}
}
void Tap_to_Speed()
{
#define TAP_MIN_BPM 40 // 鏈€灏忛€熷害
#define TAP_MAX_BPM 260 // 鏈€澶ч€熷害
#define TAP_TIMEOUT_MS 1000 // 2绉掓病鏁插氨澶嶄綅
static int32_t tap_last_time = 0; // 涓婃鎸夐敭鏃堕棿
static uint8_t tap_state = 0; // 0=绌洪棽 1=宸叉暡涓€娆?
static uint16_t ui_tap_tempo = 120; // 鏈€缁堥€熷害 40~260
uint32_t now = rt_tick_get();
if (now - tap_last_time > TAP_TIMEOUT_MS) {
tap_state = 0;
}
if (tap_state == 0) {
tap_last_time = now;
tap_state = 1;
uint8_t AllNoteOff_buff[3] = {0x99,0x3C,0x50};
SendMidiDataToDreamDSP(AllNoteOff_buff,sizeof(AllNoteOff_buff));
}
else {
uint32_t delta = now - tap_last_time;
if (delta >= 200 && delta <= 1500)
{
ui_tap_tempo = 60000 / delta;
if (ui_tap_tempo < TAP_MIN_BPM) ui_tap_tempo = TAP_MIN_BPM;
if (ui_tap_tempo > TAP_MAX_BPM) ui_tap_tempo = TAP_MAX_BPM;
MainTask_Sendmsg(MSG_ID_TAP_SPEED, ui_tap_tempo, 0, 0);
uint8_t AllNoteOff_buff[3] = {0x99,0x3C,0x50};
SendMidiDataToDreamDSP(AllNoteOff_buff,sizeof(AllNoteOff_buff));
}
tap_state = 0;
}
}
void Stop_AutoBand()
{
AutoBandTop1_Stop();
for(uint8_t i = 0;i < 16;i++)
{
uint8_t AllNoteOff_buff[3] = {(uint8_t)(0xB0 | i),0x7B,0x00};
SendMidiDataToDreamDSP(AllNoteOff_buff,sizeof(AllNoteOff_buff));
uint8_t AllControlOff_buff[3] = {(uint8_t)(0xB0 | i),0x79,0x00};
SendMidiDataToDreamDSP(AllControlOff_buff,sizeof(AllControlOff_buff));
}
StartFlag = 0;
s_outro_led_all_green = 0;
//tick_ever_positive = false;
for(uint8_t i = 0;i < 4;i++) key_toggle[i] = 0;
}
uint8_t BP_Transpose(uint8_t key_idx)
{
if(pick_hold_up)
{
/* 快速移调模式:按键只用于选择移调值,不切换和弦指型 */
uint8_t new_trans = 15;
switch(key_idx)
{
case 2: new_trans = 0; break;
case 3: new_trans = 1; break;
case 5: new_trans = 2; break;
case 6: new_trans = 3; break;
case 8: new_trans = 4; break;
case 9: new_trans = 5; break;
case 11: new_trans = 5; break;
case 12: new_trans = 6; break;
case 14: new_trans = 7; break;
case 15: new_trans = 8; break;
case 17: new_trans = 9; break;
case 18: new_trans = 10; break;
case 20: new_trans = 11; break;
}
if(new_trans != 15)
{
MainTask_Sendmsg(MSG_ID_BP_TRANSPOSE, new_trans, 0, 0);
return 1;
}
}
return 0;
}
/* 伴奏活动跟踪:AutoBand PlayTick 前进即视为播放中。
* 供专业模式指板键区分「播放中换和弦」与「段落结束后续奏重启」。 */
static uint64_t s_play_tick_last = 0;
static uint32_t s_play_adv_tick = 0; /* 最近一次 PlayTick 前进的系统 tick */
void Band_TrackPlayTick(void)
{
uint64_t pt;
if (!StartFlag)
{
s_play_tick_last = 0;
s_play_adv_tick = 0;
return;
}
pt = AutoBandTop1_GetCurrentPlayTick();
if (pt != s_play_tick_last)
{
s_play_tick_last = pt;
s_play_adv_tick = rt_tick_get();
}
}
/* 段落键 SetRunVar 后 PlayTick 可能短暂不变;主动刷新以免误判为已停。 */
void Band_NotifyPlayActivity(void)
{
if (!StartFlag)
return;
s_play_tick_last = AutoBandTop1_GetCurrentPlayTick();
s_play_adv_tick = rt_tick_get();
}
uint8_t Band_IsPlaying(void)
{
if (!StartFlag || s_play_adv_tick == 0)
return 0;
return (rt_tick_get() - s_play_adv_tick) < (RT_TICK_PER_SECOND / 4);
}
/* 专业模式公共起奏:拨片首次起奏与起奏后的指板续奏共用。
* 节奏库走 piece 循环;本地曲目按当前段落走 StartWithNote。 */
static void Expert_StartWithChord(uint8_t chord_id, uint8_t *midi_note_buff,
uint8_t note_cnt, const char *trigger)
{
uint8_t midi = 0;
uint8_t session = StartFlag ? 1u : 0u;
StartFlag = 1;
if (mGuiData[GUI_AUTOBAND_SW].Current)
{
/* 会话内勿 Stop:段落圆键切变奏后指板续奏若 Stop 会直接关音;
* 与节奏类型一致,仅换和弦/重进当前段。冷启动仍 Stop 清残留。 */
if (!session)
AutoBandTop1_Stop();
AutoBandTop1_SetLoopMode(0, 1);
AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current);
midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current;
AutoBandTop1_StartWithNote(midi);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
Band_NotifyPlayActivity();
LOG_I("pick", "local/expert root=%u name=%s sw=%u tab=%u trigger=%s chord=%u",
midi,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
(unsigned)mGuiData[GUI_AUTOBAND_SW].Current,
(unsigned)mGuiData[GUI_TAB_INDEX].Current,
trigger,
(unsigned)chord_id);
}
else
{
AutoBandTop1_SetPiecePlayMode(1, 0, 1920);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0, 1);
Band_NotifyPlayActivity();
LOG_I("pick", "expert/rhythm piece name=%s trigger=%s chord=%u",
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
trigger,
(unsigned)chord_id);
}
}
void TM1629_Handle(uint8_t key)
{
STRING_MIDI *MIDI;
const STRING_MIDI *ORGAN_MIDI;
/* 万能:指板不改和弦、不启停伴奏;保留拍速/停止 */
if (mGuiData[GUI_TAB_INDEX].Current == 0)
{
if (key == 22) { Tap_to_Speed(); return; }
if (key == 23) { Stop_AutoBand(); return; }
return; /* 忽略 0~21:不改 KEY_ID/USE_MIDI/PressFlag,不启停 OutTime_Stop */
}
if(BP_Transpose(key)) return;
switch(key)
{
case 0:
PressFlag = 0;
if(mGuiData[GUI_TAB_INDEX].Current == 2)
{
/* 普通:松手立即结束当前和弦伴奏 */
KEY_ID_1629 = 0;
if (StartFlag)
Stop_AutoBand();
}
else if(mGuiData[GUI_TAB_INDEX].Current == 0)
{
//StartFlag = 0;
//tick_ever_positive = false;
//osTimerStart(Delay_stop_timer,1000);
rt_timer_start(OutTime_Stop_timer);
}
break;
case 22:Tap_to_Speed(); break;
case 23:Stop_AutoBand(); break;
default: // 1 - 21
if (key >= 1 && key <= 21)
{
if(mGuiData[GUI_TAB_INDEX].Current == 0)
{
PressFlag = 1;
rt_timer_stop(OutTime_Stop_timer);
//osTimerStop(Delay_stop_timer);
}
else if(mGuiData[GUI_TAB_INDEX].Current == 2)
{
/* 普通:按住指板,等待拨片起奏当前和弦 */
PressFlag = 1;
}
uint8_t group = (key - 1) / 3; /* 1~3→0, 4~6→1, ..., 19~21→6 */
KEY_ID_1629 = key;
MIDI = (STRING_MIDI *)Chord_Midi_Table[group];
ORGAN_MIDI = OrganChord_Midi_Table[group];
uint8_t type = chord_type_index_map[key].type;
switch(chord_type_index_map[key].PitchOffset)
{
case 1:
USE_MIDI.Midi_1 = ORGAN_MIDI[type].Midi_1;
USE_MIDI.Midi_2 = ORGAN_MIDI[type].Midi_2;
USE_MIDI.Midi_3 = ORGAN_MIDI[type].Midi_3;
USE_MIDI.Midi_4 = ORGAN_MIDI[type].Midi_4;
USE_MIDI.Midi_5 = ORGAN_MIDI[type].Midi_5;
USE_MIDI.Midi_6 = ORGAN_MIDI[type].Midi_6;
break;
case 2:
USE_MIDI.Midi_1 = MIDI[type].Midi_1;
USE_MIDI.Midi_2 = MIDI[type].Midi_2;
USE_MIDI.Midi_3 = MIDI[type].Midi_3;
USE_MIDI.Midi_4 = MIDI[type].Midi_4;
USE_MIDI.Midi_5 = MIDI[type].Midi_5;
USE_MIDI.Midi_6 = MIDI[type].Midi_6;
break;
}
USE_MIDI.Midi_1 += mGuiData[GUI_TRANSPOSE].Current;
USE_MIDI.Midi_2 += mGuiData[GUI_TRANSPOSE].Current;
USE_MIDI.Midi_3 += mGuiData[GUI_TRANSPOSE].Current;
USE_MIDI.Midi_4 += mGuiData[GUI_TRANSPOSE].Current;
USE_MIDI.Midi_5 += mGuiData[GUI_TRANSPOSE].Current;
USE_MIDI.Midi_6 += mGuiData[GUI_TRANSPOSE].Current;
if(mGuiData[GUI_TAB_INDEX].Current == 1 && StartFlag)
{
/* 不按 Last_Tm1926D_Key 去重:App 曲目相邻两节可能是同一和弦键,
* 松手再按同一键也必须能续奏(与「指板+拨片」一致,拨片本无去重) */
if (!Band_IsPlaying())
{
/* 节奏:段落自然停后指板续奏 → piece 重启。
* 本地曲:圆键切段后 PlayTick 常短暂停滞,若走 Stop+Start 会关音;
* 与节奏「播放中换把」一致,仅换和弦音持续播放。段落后续奏用拨片/再按圆键。 */
if (mGuiData[GUI_AUTOBAND_SW].Current)
{
uint8_t midi_note_buff[4] = {0,};
uint8_t note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff);
for(uint8_t i = 0;i < note_cnt;i++)
AutoBandTop1_Note_On(midi_note_buff[i],0x50);
Return_Light_buff(KEY_ID_1629);
Band_NotifyPlayActivity();
LOG_I("pick", "expert/fret change(local) chord=%u", (unsigned)KEY_ID_1629);
}
else
{
MainTask_Sendmsg(MSG_ID_BP_KEY, 0, 0, 0);
LOG_I("pick", "expert/fret continue chord=%u", (unsigned)KEY_ID_1629);
}
}
else
{
/* 播放中(节奏库/本地曲):仅换和弦音,不重启段落 */
uint8_t midi_note_buff[4] = {0,};
uint8_t note_cnt = GetChordNotesByType(KEY_ID_1629,chord_type_index_map[KEY_ID_1629].type,midi_note_buff);
for(uint8_t i = 0;i < note_cnt;i++)
{
AutoBandTop1_Note_On(midi_note_buff[i],0x50);
}
Return_Light_buff(KEY_ID_1629);
LOG_I("pick", "expert/fret change chord=%u", (unsigned)KEY_ID_1629);
}
}
}
break;
}
}
void Return_Light_buff(uint8_t key_id_1629)
{
uint8_t buff[7] = {0xF0, 0x51, 0x04, 0x01, 0x01, 0x08, 0xF7};
switch(key_id_1629)
{
case 1: case 2: case 3: buff[3] = 0x00;break;
case 4: case 5: case 6: buff[3] = 0x01;break;
case 7: case 8: case 9: buff[3] = 0x02;break;
case 10:case 11:case 12: buff[3] = 0x03;break;
case 13:case 14:case 15: buff[3] = 0x04;break;
case 16:case 17:case 18: buff[3] = 0x05;break;
case 19:case 20:case 21: buff[3] = 0x06;break;
}
switch(chord_type_index_map[key_id_1629].type)
{
case 0:buff[5] = 0; break;
case 1:buff[5] = 8; break;
case 2:buff[5] = 2; break;
case 3:buff[5] = 13;break;
case 4:buff[5] = 10;break;
case 5:buff[5] = 31;break;
case 6:buff[5] = 30;break;
case 7:buff[5] = 4; break;
case 8:buff[5] = 11;break;
}
if(Led == buff[3])
USART4_SendData(buff,sizeof(buff));
}
void Pick_Handle()
{
uint8_t midi = 0;
uint8_t midi_note_buff[4] = {0,};
uint8_t note_cnt = 0;
uint8_t chord_id = KEY_ID_1629;
/* 空库/加载失败时禁止起奏:否则 TMR6 ISR 内 AutoBand 可能 HardFault,
* LCD 卡死且主循环 Power_Key_Scan 不再跑 → 只能 RESET */
if (!App_Auto_IsPresetReady())
{
LOG_E("pick", "abort: preset not ready tab=%u addr=0x%08X",
(unsigned)mGuiData[GUI_TAB_INDEX].Current, (unsigned)ADDRESS);
return;
}
/* 左手未触发和弦板(或落在拍速/停止键)时:拨片默认发当前调一级大和弦伴奏;
* 普通模式除外——必须左手按住指板再拨,不做一级兜底(说明书需求) */
if (mGuiData[GUI_TAB_INDEX].Current != 2 &&
(chord_id == 0 || chord_id == 22 || chord_id == 23))
{
Accomp_ApplyDefaultIChord();
chord_id = KEY_ID_1629;
}
note_cnt = GetChordNotesByType(chord_id, chord_type_index_map[chord_id].type, midi_note_buff);
Return_Light_buff(KEY_ID_1629);
/* 首次起奏前补发主音量:防开机 SysEx 丢失后"图标低、实际大" */
if (StartFlag == 0)
MasterVolume_Reapply();
switch(mGuiData[GUI_TAB_INDEX].Current)
{
case 3:
switch(mGuiData[GUI_TAB_LAST_INDEX].Current)
{
case 2:
if(StartFlag == 0)//当播放了之后,拨片不再起作用
{
StartFlag = 1;
AutoBandTop1_SetLoopMode(0,1);
midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current;
AutoBandTop1_StartWithNote(midi);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
}
break;
case 1:
StartFlag = 1;
AutoBandTop1_SetPiecePlayMode(1,0,1920);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0,1);
break;
case 0:
/* 设置页内沿用万能:与主路径一致,始终 loop */
AutoBandTop1_SetPiecePlayMode(1, 0, 480);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0, 1);
StartFlag = 1;
break;
}
break;
case 2:
/* 普通:左手按住指板 + 拨片起奏;松手由 TM1629 停。
* 节奏用 piece;本地曲目用 StartWithNote */
if (PressFlag == 0 || chord_id == 0 || chord_id > 21)
{
LOG_I("pick", "normal skip press=%u chord=%u (need fretboard)",
(unsigned)PressFlag, (unsigned)chord_id);
break;
}
StartFlag = 1;
if (mGuiData[GUI_AUTOBAND_SW].Current)
{
AutoBandTop1_Stop();
AutoBandTop1_SetLoopMode(0,1);
AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current);
midi = 0x3C + mGuiData[GUI_TRANSPOSE].Current;
AutoBandTop1_StartWithNote(midi);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
LOG_I("pick", "local/normal root=%u name=%s", midi,
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?");
}
else
{
AutoBandTop1_SetPiecePlayMode(1, 0, 1920);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0, 1);
LOG_I("pick", "normal/rhythm piece name=%s notes=%u chord=%u",
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
(unsigned)note_cnt, (unsigned)chord_id);
}
break;
case 1:
/* 专业:播放中再拨片不 Stop+Start(库会把未消费的 Start 标志清掉 → 静音);
* 本地曲播放中仅换和弦音续播,节奏路径 piece 重启本就安全。 */
if (Band_IsPlaying() && mGuiData[GUI_AUTOBAND_SW].Current)
{
Accomp_UpdatePlayingChord();
LOG_I("pick", "expert/pick skip restart playing chord=%u", (unsigned)chord_id);
break;
}
Expert_StartWithChord(chord_id, midi_note_buff, note_cnt, "pick");
break;
case 0:
/* 万能:和弦走向用 piece 模式;须 SetLoopMode,否则无循环时常听不到伴奏 */
StartFlag = 1;
s_outro_led_all_green = 0; /* 拨片重开 piece,恢复走向灯 */
AutoBandTop1_SetPiecePlayMode(1, 0, 480);
AutoBandTop1_StartWithNotes(midi_note_buff, note_cnt);
AutoBandTop1_SetLoopMode(0, 1);
LOG_I("pick", "universal piece name=%s press=%u notes=%u chord=%u",
AutoBandTop1_GetPresetName() ? AutoBandTop1_GetPresetName() : "?",
(unsigned)PressFlag, (unsigned)note_cnt, (unsigned)chord_id);
break;
}
}
void Loop_Light_Handle(uint8_t measure)
{
switch(ParamGuiData[ALL_MODE_PARAM].Current)
{
case 0:
switch(measure)
{
case 1:app_tm1629_set_led(LED_L1, LED_COLOR_R);break;
case 2:app_tm1629_set_led(LED_L6, LED_COLOR_R);break;
case 3:app_tm1629_set_led(LED_L4, LED_COLOR_R);break;
case 4:app_tm1629_set_led(LED_L5, LED_COLOR_R);break;
}
break;
case 1:
switch(measure)
{
case 1:app_tm1629_set_led(LED_L1, LED_COLOR_R);break;
case 2:app_tm1629_set_led(LED_L5, LED_COLOR_R);break;
case 3:app_tm1629_set_led(LED_L6, LED_COLOR_R);break;
case 4:app_tm1629_set_led(LED_L4, LED_COLOR_R);break;
}
break;
case 2:
switch(measure)
{
case 1:app_tm1629_set_led(LED_L1, LED_COLOR_R);break;
case 2:app_tm1629_set_led(LED_L3, LED_COLOR_R);break;
case 3:app_tm1629_set_led(LED_L6, LED_COLOR_R);break;
case 4:app_tm1629_set_led(LED_L4, LED_COLOR_R);break;
}
break;
}
}
void BL_Set_led(uint8_t led, uint8_t chord_type)
{
uint8_t chord_offset;
Led = led;
switch(chord_type){
case 0x00:
chord_offset = 0;
break;
case 0x08:
chord_offset = 1;
break;
case 0x02:
chord_offset = 4;
break;
case 0x13:
chord_offset = 3;
break;
case 0x0A: //A
chord_offset = 2;
break;
case 0x1F: //1F
chord_offset = 7;
break;
case 0x1E: //1E
chord_offset = 8;
break;
case 0x04:
chord_offset = 5;
break;
case 0x0B: //B
chord_offset = 6;
break;
}
switch(led){
case LED_L1:
app_tm1629_set_led(LED_L1, LED_COLOR_R);
chord_type_index_map[2].type = chord_offset;
break;
case LED_L2:
app_tm1629_set_led(LED_L2, LED_COLOR_G);
chord_type_index_map[5].type = chord_offset;
break;
case LED_L3:
app_tm1629_set_led(LED_L3, LED_COLOR_B);
chord_type_index_map[8].type = chord_offset;
break;
case LED_L4:
app_tm1629_set_led(LED_L4, LED_COLOR_YELL);
chord_type_index_map[11].type = chord_offset;
break;
case LED_L5:
app_tm1629_set_led(LED_L5, LED_COLOR_PURP);
chord_type_index_map[14].type = chord_offset;
break;
case LED_L6:
app_tm1629_set_led(LED_L6, LED_COLOR_CYA);
chord_type_index_map[17].type = chord_offset;
break;
case LED_L7:
app_tm1629_set_led(LED_L7, LED_COLOR_WHITE);
chord_type_index_map[20].type = chord_offset;
break;
default:
// 鏈煡鐏彿锛屽叏閮ㄥ叧闂?
app_tm1629_all_off();
break;
}
}
void BT_Pitch_offset_map(uint8_t pos_offset,uint8_t pitch_offset)
{
if (pos_offset < 1u || pos_offset > 21u)
return;
chord_type_index_map[pos_offset].PitchOffset = pitch_offset;
}
void BT_Chord_offset_map(uint8_t pos_offset,uint8_t chord_offset)
{
//char Info[20];
STRING_MIDI *MIDI;
const STRING_MIDI *ORGAN_MIDI;
uint8_t group;
uint8_t chord_APP_Local_offset = 0;
if (pos_offset < 1u || pos_offset > 21u)
return;
group = (pos_offset - 1) / 3; /* 1~3→0, 4~6→1, ..., 19~21→6 */
switch(chord_offset){
case 0x00:
chord_APP_Local_offset = 0;
break;
case 0x08:
chord_APP_Local_offset = 1;
break;
case 0x02:
chord_APP_Local_offset = 4;
break;
case 0x13:
chord_APP_Local_offset = 3;
break;
case 0x0A: //A
chord_APP_Local_offset = 2;
break;
case 0x1F: //1F
chord_APP_Local_offset = 7;
break;
case 0x1E: //1E
chord_APP_Local_offset = 8;
break;
case 0x04:
chord_APP_Local_offset = 5;
break;
case 0x0B: //B
chord_APP_Local_offset = 6;
break;
}
//sprintf (Info, "%d %d %d %d", chord_offset,chord_APP_Local_offset,pos_offset,chord_type_index_map[pos_offset].PitchOffset);
//LCD_ShowString(2, 176+16, (const uint8_t*)Info, RED, WHITE, 16, 0);
chord_type_index_map[pos_offset].type = chord_APP_Local_offset;
MIDI = (STRING_MIDI *)Chord_Midi_Table[group];
ORGAN_MIDI = OrganChord_Midi_Table[group];
switch(chord_type_index_map[pos_offset].PitchOffset){
case 1:
MIDI->Midi_1 = ORGAN_MIDI->Midi_1;
MIDI->Midi_2 = ORGAN_MIDI->Midi_2;
MIDI->Midi_3 = ORGAN_MIDI->Midi_3;
MIDI->Midi_4 = ORGAN_MIDI->Midi_4;
MIDI->Midi_5 = ORGAN_MIDI->Midi_5;
MIDI->Midi_6 = ORGAN_MIDI->Midi_6;
break;
case 2:
MIDI->Midi_1 = ORGAN_MIDI->Midi_1 + 1;
MIDI->Midi_2 = ORGAN_MIDI->Midi_2 + 1;
MIDI->Midi_3 = ORGAN_MIDI->Midi_3 + 1;
MIDI->Midi_4 = ORGAN_MIDI->Midi_4 + 1;
MIDI->Midi_5 = ORGAN_MIDI->Midi_5 + 1;
MIDI->Midi_6 = ORGAN_MIDI->Midi_6 + 1;
break;
}
}
uint8_t GetChordNotesByType(uint8_t MIDI_Index,uint8_t type,uint8_t *buff)
{
uint8_t n = 0;
uint8_t i;
uint8_t root;
buff[0] = buff[1] = buff[2] = buff[3] = 0;
switch(MIDI_Index)
{
case 1:case 2:case 3:
switch(type)
{
case 0:
buff[0] = 48;buff[1] = 52;buff[2] = 55;
break;
case 1:
buff[0] = 48;buff[1] = 51;buff[2] = 55;
break;
case 2:
buff[0] = 48;buff[1] = 51;buff[2] = 55;buff[3] = 58;
break;
case 3:
buff[0] = 48;buff[1] = 52;buff[2] = 55;buff[3] = 58;
break;
case 4:
buff[0] = 48;buff[1] = 52;buff[2] = 55;buff[3] = 59;
break;
case 5:
buff[0] = 48;buff[1] = 52;buff[2] = 55;buff[3] = 62;
break;
case 6:
buff[0] = 48;buff[1] = 51;buff[2] = 54;buff[3] = 58;
break;
case 7:
buff[0] = 48;buff[1] = 50;buff[2] = 55;
break;
case 8:
buff[0] = 48;buff[1] = 53;buff[2] = 55;
break;
}
break;
case 4:case 5:case 6:
switch(type)
{
case 0:
buff[0] = 50;buff[1] = 54;buff[2] = 57;
break;
case 1:
buff[0] = 50;buff[1] = 53;buff[2] = 57;
break;
case 2:
buff[0] = 50;buff[1] = 53;buff[2] = 57;buff[3] = 60;
break;
case 3:
buff[0] = 50;buff[1] = 54;buff[2] = 57;buff[3] = 60;
break;
case 4:
buff[0] = 50;buff[1] = 54;buff[2] = 57;buff[3] = 61;
break;
case 5:
buff[0] = 50;buff[1] = 54;buff[2] = 57;buff[3] = 64;
break;
case 6:
buff[0] = 50;buff[1] = 53;buff[2] = 56;buff[3] = 60;
break;
case 7:
buff[0] = 50;buff[1] = 52;buff[2] = 57;
break;
case 8:
buff[0] = 50;buff[1] = 55;buff[2] = 57;
break;
}
break;
case 7:case 8:case 9:
switch(type)
{
case 0:
buff[0] = 52;buff[1] = 56;buff[2] = 59;
break;
case 1:
buff[0] = 52;buff[1] = 55;buff[2] = 59;
break;
case 2:
buff[0] = 52;buff[1] = 55;buff[2] = 59;buff[3] = 62;
break;
case 3:
buff[0] = 52;buff[1] = 56;buff[2] = 59;buff[3] = 62;
break;
case 4:
buff[0] = 52;buff[1] = 56;buff[2] = 59;buff[3] = 63;
break;
case 5:
buff[0] = 52;buff[1] = 56;buff[2] = 59;buff[3] = 66;
break;
case 6:
buff[0] = 52;buff[1] = 55;buff[2] = 58;buff[3] = 62;
break;
case 7:
buff[0] = 52;buff[1] = 54;buff[2] = 59;
break;
case 8:
buff[0] = 52;buff[1] = 57;buff[2] = 59;
break;
}
break;
case 10:case 11:case 12:
switch(type)
{
case 0:
buff[0] = 53;buff[1] = 57;buff[2] = 60;
break;
case 1:
buff[0] = 53;buff[1] = 56;buff[2] = 60;
break;
case 2:
buff[0] = 53;buff[1] = 56;buff[2] = 60;buff[3] = 63;
break;
case 3:
buff[0] = 53;buff[1] = 57;buff[2] = 60;buff[3] = 63;
break;
case 4:
buff[0] = 53;buff[1] = 57;buff[2] = 60;buff[3] = 64;
break;
case 5:
buff[0] = 53;buff[1] = 57;buff[2] = 60;buff[3] = 67;
break;
case 6:
buff[0] = 53;buff[1] = 56;buff[2] = 59;buff[3] = 63;
break;
case 7:
buff[0] = 53;buff[1] = 55;buff[2] = 60;
break;
case 8:
buff[0] = 53;buff[1] = 58;buff[2] = 60;
break;
}
break;
case 13:case 14:case 15:
switch(type)
{
case 0:
buff[0] = 43;buff[1] = 47;buff[2] = 50;
break;
case 1:
buff[0] = 43;buff[1] = 46;buff[2] = 50;
break;
case 2:
buff[0] = 43;buff[1] = 46;buff[2] = 50;buff[3] = 53;
break;
case 3:
buff[0] = 43;buff[1] = 47;buff[2] = 50;buff[3] = 53;
break;
case 4:
buff[0] = 43;buff[1] = 47;buff[2] = 50;buff[3] = 54;
break;
case 5:
buff[0] = 43;buff[1] = 47;buff[2] = 50;buff[3] = 57;
break;
case 6:
buff[0] = 43;buff[1] = 46;buff[2] = 49;buff[3] = 53;
break;
case 7:
buff[0] = 43;buff[1] = 45;buff[2] = 50;
break;
case 8:
buff[0] = 43;buff[1] = 48;buff[2] = 50;
break;
}
break;
case 16:case 17:case 18:
switch(type)
{
case 0:
buff[0] = 45;buff[1] = 49;buff[2] = 52;
break;
case 1:
buff[0] = 45;buff[1] = 48;buff[2] = 52;
break;
case 2:
buff[0] = 45;buff[1] = 48;buff[2] = 52;buff[3] = 55;
break;
case 3:
buff[0] = 45;buff[1] = 49;buff[2] = 52;buff[3] = 55;
break;
case 4:
buff[0] = 45;buff[1] = 49;buff[2] = 52;buff[3] = 56;
break;
case 5:
buff[0] = 45;buff[1] = 49;buff[2] = 52;buff[3] = 59;
break;
case 6:
buff[0] = 45;buff[1] = 48;buff[2] = 51;buff[3] = 55;
break;
case 7:
buff[0] = 45;buff[1] = 47;buff[2] = 52;
break;
case 8:
buff[0] = 45;buff[1] = 50;buff[2] = 52;
break;
}
break;
case 19:case 20:case 21:
switch(type)
{
case 0:
buff[0] = 47;buff[1] = 51;buff[2] = 53;
break;
case 1:
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] = 50;buff[2] = 53;buff[3] = 57;
break;
case 4:
buff[0] = 47;buff[1] = 51;buff[2] = 53;buff[3] = 57;
break;
case 5:
buff[0] = 47;buff[1] = 51;buff[2] = 54;buff[3] = 58;
break;
case 6:
buff[0] = 47;buff[1] = 51;buff[2] = 54;buff[3] = 60;
break;
case 7:
buff[0] = 47;buff[1] = 49;buff[2] = 54;
break;
case 8:
buff[0] = 47;buff[1] = 52;buff[2] = 54;
break;
case 9:
buff[0] = 47;buff[1] = 50;buff[2] = 53;
break;
}
break;
default:
return 0;
}
n = (buff[3] != 0) ? 4 : 3;
switch (chord_type_index_map[MIDI_Index].PitchOffset)
{
case 2: /* # 升:整体 +1 半音 */
for (i = 0; i < n; i++)
buff[i] += 1;
break;
default:
break;
}
for (i = 0; i < n; i++)
buff[i] += mGuiData[GUI_TRANSPOSE].Current;
/* 音师:仅所有调的 #四级(键 10~12 + 升)— 整组低八度,音域压在一个八度内
* 最低音 = #IV,最高音 ≤ 自然 IV。勿对六级等其它升号和弦套用。 */
if (MIDI_Index >= 10 && MIDI_Index <= 12 &&
chord_type_index_map[MIDI_Index].PitchOffset == 2)
{
for (i = 0; i < n; i++)
{
if (buff[i] >= 12)
buff[i] -= 12;
}
root = buff[0];
for (i = 1; i < n; i++)
{
uint8_t guard;
for (guard = 0; guard < 2 && buff[i] >= (uint8_t)(root + 12); guard++)
buff[i] -= 12;
for (guard = 0; guard < 2 && buff[i] < root; guard++)
buff[i] += 12;
}
}
return n;
}
void Read_Cur_Measure()
{
uint32_t now = rt_tick_get();
static uint32_t last_tick_i = 0;
uint8_t tab = (uint8_t)mGuiData[GUI_TAB_INDEX].Current;
uint8_t last = (uint8_t)mGuiData[GUI_TAB_LAST_INDEX].Current;
/* 普通(2)与万能(0)均按「和弦走向」同步亮灯;设置页(3)沿用进入前模式 */
uint8_t loop_light_ok =
(tab == 0 || tab == 2) ||
(tab == 3 && (last == 0 || last == 2));
Band_TrackPlayTick();
/* 尾奏中保持全绿,不刷新和弦走向红灯 */
if (loop_light_ok && StartFlag && !s_outro_led_all_green)
{
if(now - last_tick_i >= 10)
{
last_tick_i = now;
uint8_t Measure = AutoBandTop1_GetCurrentRunMeasure();
MainTask_Sendmsg(MSG_ID_LOOP_LIGHT, Measure, 0, 0);
}
}
}