返回列表 发新帖

使用PainterEngine物理模型钢琴播放midi音乐

[复制链接]

1417

主题

2957

帖子

5794

积分

论坛元老

Rank: 8Rank: 8

积分
5794
发表于 2022-7-28 11:32:58 | 显示全部楼层 | 阅读模式 IP:香港

物理钢琴合成《魔女之旅》op
https://www.pianoworld.cn/video/1528363889401618432
PainterEngine中集成了一个物理建模钢琴,你可以在PX_Piano.h PX_Piano.c中查阅到它的完整相关源代码,这篇文章是该物理建模钢琴的API使用教程。
PainterEngine为此提供了2个简化API接口,一个是PX_Piano,一个是PX_PianoModel,它们提供的接口函数几乎是一样的,不同的是:
PX_Piano是实时生成模型,它依据给入参数实时合成音频的PCM流,它需要更少的运行内存空间,但这也这意味着它将耗费更多的CPU运行资源。
PX_PianoModel是静态合成模型,它使用给出的钢琴参数预生成静态的击键PCM音频流,之后使用该音频流进行混音,它只需要很少的CPU运行资源,但是它的初始化将需要较长的时间,同时将占用更多的运行时内存空间。
其中,PX_Piano公开API定义如下:

函数名称功能
PX_PianoInitialize使用默认参数初始化物理建模钢琴
参数说明
mp内存池
pPianoPiano的指针
返回值返回PX_TRUE表示模型初始化成功,否者表示初始化失败
函数名称功能
PX_PianoInitializeEx使用钢琴模型参数初始化物理建模钢琴
参数说明
mp内存池
pPianoPiano的指针
keyparam钢琴88键的声学模型参数,你可以给出一个PX_NULL或0表示默认参数
soundboardparam钢琴声板的参数(滤波器参数),你可以给出一个PX_NULL或0表示默认参数
返回值返回PX_TRUE表示模型初始化成功,否者表示初始化失败
函数名称功能
PX_PianoIndexToKey将钢琴按键索引转换为键名
参数说明
index按键索引
keyName输出的键名
PX_PianoIndexToKey(51,name)中的name为C4
函数名称功能
PX_PianoTriggerKey按下钢琴的一个键
参数说明
pPianoPiano的指针
index按键索引
v按键的力度
函数名称功能
PX_PianoGo从物理钢琴模型中读取一段混音后的PCM流,这个数据流是一个归一化后的float类型
参数说明
pPianopPiano的指针
out输出PCM流的指针
samples读取流的个数
返回值-
函数名称功能
PX_PianoFree释放Paino的资源
参数说明
pPianoPiano的指针
返回值-
其中,PX_PianoModel公开API定义如下:

函数名称功能
PX_PianoModelInitialize初始化PianoModel(这是一个耗时较长的函数,你可以通过访问该结构体的initialize_process成员来获取其初始化进度,其范围是0-1
参数说明
pModelPianoModel的指针
keyparam钢琴88键的声学模型参数,你可以给出一个PX_NULL或0表示默认参数
soundboardparam钢琴声板的参数(滤波器参数),你可以给出一个PX_NULL或0表示默认参数
返回值返回PX_TRUE表示模型初始化成功,否者表示初始化失败
函数名称功能
PX_PianoModelGo从物理钢琴模型中读取一段混音后的PCM流,这个数据流是一个归一化后的float类型
参数说明
pModelPianoModel的指针
out输出PCM流的指针
count读取流的个数
返回值-
函数名称功能
PX_PianoModelTriggerKey按下钢琴下的一个按键
参数说明
pModelPianoModel的指针
keyName按键的名称,例如A1,#A1,C4,#C4...
返回值-
函数名称功能
PX_PianoModelTriggerIndex或PX_PianoModelTrigger按下钢琴下的一个按键
参数说明
pModelPianoModel的指针
index按键的索引,索引范围是0-87,分别对应钢琴88键盘从最左(第0键)到最右(第87键)
返回值-
函数名称功能
PX_PianoModelFree释放PainoModel的资源
参数说明
pModelPianoModel的指针
返回值-
示范代码





魔女之旅

以下示范代码以播放魔女之旅Op《リテラチュア》为例(白毛女孩纸谁不爱呢?),创建一个PainterEngine工程,在PX_Application.c中输入以下代码:
#include "PainterEngine_Application.h"
PX_Application App;

PX_PianoModel pianomodel;//钢琴模型
PX_Midi midi;//midi解析器

//sound buffer回调函数,将合成PCM流输入到音频设备
px_void PX_ApplicationSoundPlay(px_void* userptr, px_byte* buffer, px_int size)
{
    px_int i;
    px_int count = size / 4;//计算需要的采样数量
    px_short* pPCM = (px_short*)buffer;
    for (i = 0; i < count; i++)
    {
        px_float out = 0;
        PX_PianoModelGo(&pianomodel, &out, 1);//取得1个采样点
        pPCM[0] = (px_int16)(out * 32768 * 3);//将归一化数据转换为PCM16(int16)数据输出到声道0,*3表示放大音量
        pPCM[1] = (px_int16)(out * 32768 * 3);//将归一化数据转换为PCM16(int16)数据输出到声道1,*3表示放大音量
        pPCM += 2;
    }
}

//midi解析器的回调函数
px_void PX_Application_MidiCallback(struct _PX_Midi* midi, px_byte opcode, px_byte  track, px_byte  instrument, px_byte Note, px_byte velocity, px_void* userptr)
{
    //如果是midi按键按下的消息
    if (opcode== PX_MIDI_OPCODE_MASK_NOTEON)
    {
        //判断按键力度(速度)
        if (velocity)
        {
            //在钢琴模型中按下对应按键
            PX_PianoModelTrigger(&pianomodel, PX_MidiNoteToPianoKey(Note));
        }
    }

}
px_bool PX_ApplicationInitialize(PX_Application *pApp,px_int screen_width,px_int screen_height)
{
    PX_ApplicationInitializeDefault(&pApp->runtime, screen_width, screen_height);
    //初始化混音器
    PX_SoundPlayInitialize(&pApp->runtime.mp_game, &pApp->runtime.soundplay);
    PX_AudioInitialize(&pApp->runtime.soundplay);
    //初始化钢琴模型
    PX_PianoModelInitialize(&pianomodel, 0, 0);
    //初始化midi解析器
    PX_MidiInitialize(&midi, &pApp->runtime.mp_game, PX_Application_MidiCallback, pApp);
    //加载一个midi文件
    PX_LoadMidiFromFile(&midi, "D:\\test.mid");
    //设置音频回调函数
    PX_SoundPlaySetUserRead(&pApp->runtime.soundplay, PX_ApplicationSoundPlay, 0);
    //解析器开始解析midi
    PX_MidiPlay(&midi);
    //设置1个四分音符的时间间隔
    PX_MidiSetQuarterNoteDuration(&midi, 480);
    return PX_TRUE;
}

px_void PX_ApplicationUpdate(PX_Application *pApp,px_dword elapsed)
{
    //更新midi消息以触发回调
    PX_MidiUpdate(&midi, elapsed);
}

px_void PX_ApplicationRender(PX_Application *pApp,px_dword elapsed)
{
    px_surface *pRenderSurface=&pApp->runtime.RenderSurface;
    PX_RuntimeRenderClear(&pApp->runtime,PX_OBJECT_UI_DEFAULT_BACKGROUNDCOLOR);

}

px_void PX_ApplicationPostEvent(PX_Application *pApp,PX_Object_Event e)
{
    PX_ApplicationEventDefault(&pApp->runtime, e);
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册   手机动态码快速登录

x
打赏鼓励一下!

发表回复

您需要登录后才可以回帖 登录 | 立即注册   手机动态码快速登录

本版积分规则

 
 
点击这里给我发消息
点击这里给我发消息
官方微信

招募城市商务合作 电话/微信 18702940294
 
快速回复 返回顶部 返回列表