KT24-1110_65E-HA-651B/cpu/br25/audio_dec/audio_dec_midi_ctrl.c
2024-11-10 18:44:17 +08:00

617 lines
18 KiB
C
Raw Permalink 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 "audio_dec_midi_ctrl.h"
#if AUDIO_MIDI_CTRL_CONFIG
/*
*如遇到多按琴键同时按下或重复快速按某琴键出现卡顿的情况
*处理方法1、减少同时发声MIDI_KEY_NUM 个数
* 2、提高外挂flash的 波特率60M、90M,调整spi读线宽的为2线模式或4线
* 3、提高系统时钟
* */
extern struct audio_dac_hdl dac_hdl;
#define MIDI_KEY_NUM (18)//(支持多少个key同时发声(1~18) 越大,需要的时钟越大)
const u16 midi_samplerate_tab[9] = {
48000,
44100,
32000,
24000,
22050,
16000,
12000,
11025,
8000
};
struct _midi_obj {
u8 channel;
u32 sample_rate;
u32 id; // 唯一标识符,随机值
u32 start : 1; // 正在解码
char *path; //音色文件路径
struct audio_res_wait wait; // 资源等待句柄
struct midi_ctrl_decoder midi_ctrl_dec;
struct audio_mixer_ch mix_ch; // 叠加句柄
struct audio_stream *stream; // 音频流
};
struct _midi_obj *midi_ctrl_dec_hdl = NULL;
void midi_ctrl_ioctrl(u32 cmd, void *priv);
/*----------------------------------------------------------------------------*/
/**@brief midi ctrl解码释放
@param
@return
@note
*/
/*----------------------------------------------------------------------------*/
void midi_ctrl_dec_relaese()
{
if (midi_ctrl_dec_hdl) {
audio_decoder_task_del_wait(&decode_task, &midi_ctrl_dec_hdl->wait);
clock_remove(DEC_MIDI_CLK);
local_irq_disable();
free(midi_ctrl_dec_hdl);
midi_ctrl_dec_hdl = NULL;
local_irq_enable();
}
}
/*----------------------------------------------------------------------------*/
/**@brief midi音色文件获取
@param
@return
@note 内部调用
*/
/*----------------------------------------------------------------------------*/
static int midi_get_cfg_addr(u8 **addr)
{
if (!midi_ctrl_dec_hdl) {
return -1;
}
#ifndef CONFIG_MIDI_DEC_ADDR
//音色文件支持在外部存储卡或者外挂flash,sdk默认使用本方式
/* FILE *file = fopen("storage/sd0/C/MIDI.bin\0", "r"); */
FILE *file = fopen(midi_ctrl_dec_hdl->path, "r");
/* FILE *file = fopen(SDFILE_RES_ROOT_PATH"MIDI.bin\0", "r"); */
if (!file) {
log_e("MIDI.bin open err\n");
return -1;
}
*addr = (u8 *)file;
log_i("midi_file %x\n", file);
#else
//音色文件仅支持在内置flash
/* FILE *file = fopen(SDFILE_RES_ROOT_PATH"MIDI.bin\0", "r"); */
FILE *file = fopen(midi_ctrl_dec_hdl->path, "r");
if (!file) {
log_e("MIDI.bin open err\n");
return -1;
}
struct vfs_attr attr = {0};
fget_attrs(file, &attr);
*addr = (u8 *)attr.sclust;
fclose(file);
#endif
return 0;
}
/*----------------------------------------------------------------------------*/
/**@brief midi音色文件读
@param
@return
@note 内部调用
*/
/*----------------------------------------------------------------------------*/
static int midi_fread(void *file, void *buf, u32 len)
{
#ifndef CONFIG_MIDI_DEC_ADDR
FILE *hd = (FILE *)file;
if (hd) {
len = fread(hd, buf, len);
}
#endif
return len;
}
/*----------------------------------------------------------------------------*/
/**@brief midi音色文件seek
@param
@return
@note 内部调用
*/
/*----------------------------------------------------------------------------*/
static int midi_fseek(void *file, u32 offset, int seek_mode)
{
#ifndef CONFIG_MIDI_DEC_ADDR
FILE *hd = (FILE *)file;
if (hd) {
fseek(hd, offset, seek_mode);
}
#endif
return 0;
}
/*----------------------------------------------------------------------------*/
/**@brief midi ctrl初始化函数该函数由库调用
@param 参数返回地址
@return 0
@note 该函数是弱定义函数,不可修改定义
*/
/*----------------------------------------------------------------------------*/
int midi_ctrl_init(void *info)
{
if (!midi_ctrl_dec_hdl) {
return -1;
}
u8 *cache_addr;
if (midi_get_cfg_addr(&cache_addr)) {
return -1;
}
midi_ctrl_open_parm *parm = (midi_ctrl_open_parm *)info;
parm->ctrl_parm.tempo = 1024;//解码会改变播放速度,1024是正常值
parm->ctrl_parm.track_num = 1;//支持音轨的最大个数0~15
parm->sample_rate = midi_ctrl_dec_hdl->midi_ctrl_dec.sample_rate;//midi_samplerate_tab[5];
parm->cfg_parm.player_t = MIDI_KEY_NUM; //(支持多少个key同时发声,用户可修改)
parm->cfg_parm.spi_pos = (unsigned int)cache_addr;
parm->cfg_parm.fread = midi_fread;
parm->cfg_parm.fseek = midi_fseek;
for (int i = 0; i < ARRAY_SIZE(midi_samplerate_tab); i++) {
if (parm->sample_rate == midi_samplerate_tab[i]) {
parm->cfg_parm.sample_rate = i;
break;
}
}
return 0;
}
/*----------------------------------------------------------------------------*/
/**@brief midi ctrl音色文件关闭
@param
@return
@note 该函数在midi关闭时调用,该函数是弱定义函数,不可修改定义
*/
/*----------------------------------------------------------------------------*/
int midi_ctrl_uninit(void *priv)
{
#ifndef CONFIG_MIDI_DEC_ADDR
if (priv) {
fclose((FILE *)priv);
priv = NULL;
}
#endif
return 0;
}
/*----------------------------------------------------------------------------*/
/**@brief 乐器更新
@param prog:乐器号
@param trk_num :音轨 (0~15)
@return
@note
*/
/*----------------------------------------------------------------------------*/
void midi_ctrl_set_porg(u8 prog, u8 trk_num)
{
struct set_prog_parm parm = {0};
parm.prog = prog;
parm.trk_num = trk_num;
midi_ctrl_ioctrl(MIDI_CTRL_SET_PROG, &parm);
}
/*----------------------------------------------------------------------------*/
/**@brief 按键按下
@param nkey:按键序号0~127
@param nvel:按键力度0~127
@param chn :通道(0~15)
@return
@note
*/
/*----------------------------------------------------------------------------*/
void midi_ctrl_note_on(u8 nkey, u8 nvel, u8 chn)
{
struct note_on_parm parm = {0};
parm.nkey = nkey;
parm.nvel = nvel;
parm.chn = chn;
midi_ctrl_ioctrl(MIDI_CTRL_NOTE_ON, &parm);
}
/*----------------------------------------------------------------------------*/
/**@brief 按键松开
@param nkey:按键序号0~127
@param chn :通道(0~15)
@return
@note
*/
/*----------------------------------------------------------------------------*/
void midi_ctrl_note_off(u8 nkey, u8 chn)
{
struct note_off_parm parm = {0};
parm.nkey = nkey;
parm.chn = chn;
midi_ctrl_ioctrl(MIDI_CTRL_NOTE_OFF, &parm);
}
/*----------------------------------------------------------------------------*/
/**@brief midi 配置接口
@param cmd:命令
@param priv:对应cmd的结构体
@return
@note
*/
/*----------------------------------------------------------------------------*/
void midi_ctrl_confing(u32 cmd, void *priv)
{
midi_ctrl_ioctrl(cmd, priv);
}
/*----------------------------------------------------------------------------*/
/**@brief midi keyboard 设置按键按下音符发声的衰减系数
@param obj:控制句柄
@param samp:对应samplerate_tab坐标
@return
@note 低11bit为调节衰减系数值越小衰减越快 1024为默认值 范围0~1024
大于11bit为延时系数节尾音长度的值越大拉德越长0为默认值,31延长1s,范围0~31(延时系数无效)
@example midi_ctrl_confing_set_melody_decay((衰减系数&0x7fff) | (延时系数&0x1f <<11));
*/
/*----------------------------------------------------------------------------*/
void midi_ctrl_confing_set_melody_decay(u16 val)
{
u32 cmd = CMD_MIDI_CTRL_TEMPO;
MIDI_PLAY_CTRL_TEMPO tempo = {0};
tempo.decay_val = val;
tempo.tempo_val = 1024;//设置为固定1024即可
midi_ctrl_confing(cmd, (void *)&tempo);
}
/*----------------------------------------------------------------------------*/
/**@brief 弯音轮配置
@param pitch_val:弯音轮值,1 - 65535 256是正常值,对音高有作用
@param chn :通道(0~15)
@return
@note
*/
/*----------------------------------------------------------------------------*/
void midi_ctrl_pitch_bend(u16 pitch_val, u8 chn)
{
struct pitch_bend_parm parm = {0};
parm.pitch_val = pitch_val;
parm.chn = chn;
midi_ctrl_ioctrl(MIDI_CTRL_PITCH_BEND, &parm);
}
/*----------------------------------------------------------------------------*/
/**@brief midi ctrl解码数据流激活
@param *p: 私有句柄
@return
@note
*/
/*----------------------------------------------------------------------------*/
static void midi_ctrl_dec_out_stream_resume(void *p)
{
struct _midi_obj *dec = p;
#if FILE_DEC_USE_OUT_TASK
if (dec->midi_ctrl_dec.dec_no_out_sound == 0) {
audio_decoder_resume_out_task(&dec->midi_ctrl_dec.decoder);
return ;
}
#endif
audio_decoder_resume(&dec->midi_ctrl_dec.decoder);
}
/*----------------------------------------------------------------------------*/
/**@brief midi ctrl解码事件处理
@param *decoder: 解码器句柄
@param argc: 参数个数
@param *argv: 参数
@return
@note
*/
/*----------------------------------------------------------------------------*/
static void midi_ctrl_dec_event_handler(struct audio_decoder *decoder, int argc, int *argv)
{
switch (argv[0]) {
case AUDIO_DEC_EVENT_END:
if (!midi_ctrl_dec_hdl) {
log_i("midi_ctrl_dec_hdl handle err ");
break;
}
if (midi_ctrl_dec_hdl->id != argv[1]) {
log_w("midi_ctrl_dec_hdl id err : 0x%x, 0x%x \n", midi_ctrl_dec_hdl->id, argv[1]);
break;
}
midi_ctrl_dec_close();
//audio_decoder_resume_all(&decode_task);
break;
}
}
/*----------------------------------------------------------------------------*/
/**@brief midi ctrl解码开始
@param
@return 0成功
@return 非0失败
@note
*/
/*----------------------------------------------------------------------------*/
int midi_ctrl_dec_start()
{
int err;
struct _midi_obj *dec = midi_ctrl_dec_hdl;
struct audio_mixer *p_mixer = &mixer;
if (!dec) {
return -EINVAL;
}
log_i("midi_ctrl dec start: in\n");
// 打开midi ctrl解码器
err = midi_ctrl_decoder_open(&dec->midi_ctrl_dec, &decode_task);
if (err) {
goto __err1;
}
midi_ctrl_decoder_set_event_handler(&dec->midi_ctrl_dec, midi_ctrl_dec_event_handler, dec->id);
// 设置叠加功能
audio_mixer_ch_open_head(&dec->mix_ch, p_mixer);
audio_mixer_ch_set_src(&dec->mix_ch, 1, 0);
#if FILE_DEC_USE_OUT_TASK
if (dec->midi_ctrl_dec.dec_no_out_sound == 0) {
audio_decoder_out_task_ch_enable(&dec->midi_ctrl_dec.decoder);
}
#endif
// 数据流串联
struct audio_stream_entry *entries[8] = {NULL};
u8 entry_cnt = 0;
entries[entry_cnt++] = &dec->midi_ctrl_dec.decoder.entry;
entries[entry_cnt++] = &dec->mix_ch.entry;
// 创建数据流,把所有节点连接起来
dec->stream = audio_stream_open(dec, midi_ctrl_dec_out_stream_resume);
audio_stream_add_list(dec->stream, entries, entry_cnt);
// 设置音频输出音量
audio_output_set_start_volume(APP_AUDIO_STATE_MUSIC);
// 设置时钟
clock_set_cur();
// 开始解码
dec->midi_ctrl_dec.status = FILE_DEC_STATUS_PLAY;
err = audio_decoder_start(&dec->midi_ctrl_dec.decoder);
dec->start = 1;
if (err) {
goto __err3;
}
return 0;
__err3:
audio_mixer_ch_close(&dec->mix_ch);
midi_ctrl_decoder_close(&dec->midi_ctrl_dec);
if (dec->stream) {
audio_stream_close(dec->stream);
dec->stream = NULL;
}
__err1:
log_w(" start err close\n");
midi_ctrl_dec_relaese();
return -1;
}
/*----------------------------------------------------------------------------*/
/**@brief midi ctrl解码关闭
@param
@return
@note
*/
/*----------------------------------------------------------------------------*/
static void __midi_ctrl_dec_close(void)
{
if (midi_ctrl_dec_hdl && midi_ctrl_dec_hdl->start) {
midi_ctrl_dec_hdl->start = 0;
midi_ctrl_decoder_close(&midi_ctrl_dec_hdl->midi_ctrl_dec);
audio_mixer_ch_close(&midi_ctrl_dec_hdl->mix_ch);
// 先关闭各个节点最后才close数据流
if (midi_ctrl_dec_hdl->stream) {
audio_stream_close(midi_ctrl_dec_hdl->stream);
midi_ctrl_dec_hdl->stream = NULL;
}
}
}
/*----------------------------------------------------------------------------*/
/**@brief 关闭midi ctrl解码
@param
@return
@note
*/
/*----------------------------------------------------------------------------*/
void midi_ctrl_dec_close(void)
{
if (!midi_ctrl_dec_hdl) {
return;
}
__midi_ctrl_dec_close();
midi_ctrl_dec_relaese();
clock_set_cur();
log_i("midi ctrl dec close \n\n ");
}
/*----------------------------------------------------------------------------*/
/**@brief midi ctrl解码资源等待
@param *wait: 句柄
@param event: 事件
@return 0成功
@note 用于多解码打断处理
*/
/*----------------------------------------------------------------------------*/
static void __midi_ctrl_dec_close(void);
static int midi_ctrl_wait_res_handler(struct audio_res_wait *wait, int event)
{
int err = 0;
log_i("midi_ctrl_wait_res_handler, event:%d\n", event);
if (event == AUDIO_RES_GET) {
// 启动解码
err = midi_ctrl_dec_start();
} else if (event == AUDIO_RES_PUT) {
// 被打断
__midi_ctrl_dec_close();
}
return err;
}
/*----------------------------------------------------------------------------*/
/**@brief 打开midi ctrl解码
@param sample_rate: 采样率
@param *path:音色文件路径
@return 0成功
@return 非0失败
@note
*/
/*----------------------------------------------------------------------------*/
int midi_ctrl_dec_open(u32 sample_rate, char *path)
{
int err = 0;
int i = 0;
struct _midi_obj *dec;
dec = zalloc(sizeof(*dec));
if (!dec) {
return -ENOMEM;
}
midi_ctrl_dec_hdl = dec;
dec->id = rand32();
dec->path = path;
dec->midi_ctrl_dec.ch_num = 2;
dec->midi_ctrl_dec.output_ch_num = audio_output_channel_num();
dec->midi_ctrl_dec.output_ch_type = audio_output_channel_type();
#if TCFG_MIC_EFFECT_ENABLE
dec->midi_ctrl_dec.sample_rate = MIC_EFFECT_SAMPLERATE;
#else
for (i = 0; i < ARRAY_SIZE(midi_samplerate_tab); i++) {
if (sample_rate == midi_samplerate_tab[i]) {
dec->midi_ctrl_dec.sample_rate = midi_samplerate_tab[i];
break;
}
}
if (i >= ARRAY_SIZE(midi_samplerate_tab)) {
dec->midi_ctrl_dec.sample_rate = 16000;
log_e("midi sample_rate check err ,will set default 16000 Hz\n");
}
#endif
dec->wait.priority = 2;
dec->wait.preemption = 0;
dec->wait.snatch_same_prio = 1;
/* dec->wait.protect = 1; */
dec->wait.handler = midi_ctrl_wait_res_handler;
clock_add(DEC_MIDI_CLK);
err = audio_decoder_task_add_wait(&decode_task, &dec->wait);
return err;
}
/*----------------------------------------------------------------------------*/
/**@brief midi ctrl控制函数
@param cmd:
MIDI_CTRL_NOTE_ON, //按键按下参数结构对应struct note_on_parm
MIDI_CTRL_NOTE_OFF, //按键松开参数结构对应struct note_off_parm
MIDI_CTRL_SET_PROG, //更改乐器参数结构对应struct set_prog_parm
MIDI_CTRL_PITCH_BEND, //弯音轮参数结构对应struct pitch_bend_parm
CMD_MIDI_CTRL_TEMPO, //改变节奏,参数结构对应 MIDI_PLAY_CTRL_TEMPO
@param priv:对应cmd的参数地址
@return 0
@note midi解码控制例程
*/
/*----------------------------------------------------------------------------*/
void midi_ctrl_ioctrl(u32 cmd, void *priv)
{
struct _midi_obj *dec = midi_ctrl_dec_hdl;
if (!dec) {
log_e("midi ctrl dec NULL\n");
return ;
}
log_i("midi ctrl cmd %x", cmd);
audio_decoder_ioctrl(&dec->midi_ctrl_dec.decoder, cmd, priv);
}
#if 0
/*----------------------------------------------------------------------------*/
/**@brief midi key 测试样例
@param
@return
@note
*/
/*----------------------------------------------------------------------------*/
void midi_paly_test(u32 key)
{
static u8 open_close = 0;
static u8 change_prog = 0;
static u8 note_on_off = 0;
switch (key) {
case KEY_IR_NUM_0:
if (!open_close) {
/* midi_ctrl_dec_open(16000);//启动midi key */
/* midi_ctrl_dec_open(16000, "storage/sd0/C/MIDI.bin\0");//启动midi key */
midi_ctrl_dec_open(16000, SDFILE_RES_ROOT_PATH"MIDI.bin\0");//启动midi key
} else {
midi_ctrl_dec_close();//关闭midi key
}
open_close = !open_close;
break;
case KEY_IR_NUM_1:
if (!change_prog) {
midi_ctrl_set_porg(0, 0);//设置0号乐器音轨0
} else {
midi_ctrl_set_porg(22, 0);//设置22号乐器音轨0
}
change_prog = !change_prog;
break;
case KEY_IR_NUM_2:
if (!note_on_off) {
//模拟按键57、58、59、60、61、62,以力度127通道0按下测试
midi_ctrl_note_on(57, 127, 0);
midi_ctrl_note_on(58, 127, 0);
midi_ctrl_note_on(59, 127, 0);
midi_ctrl_note_on(60, 127, 0);
midi_ctrl_note_on(61, 127, 0);
midi_ctrl_note_on(62, 127, 0);
} else {
//模拟按键57、58、59、60、61、62松开测试
midi_ctrl_note_off(57, 0);
midi_ctrl_note_off(58, 0);
midi_ctrl_note_off(59, 0);
midi_ctrl_note_off(60, 0);
midi_ctrl_note_off(61, 0);
midi_ctrl_note_off(62, 0);
}
note_on_off = !note_on_off;
break;
default:
break;
}
}
#endif
#endif