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|内存池|
|pPiano|Piano的指针|
|返回值|返回PX_TRUE表示模型初始化成功,否者表示初始化失败|
|
| 函数名称 | 功能 |
|:---|:---|
|PX_PianoInitializeEx| 使用钢琴模型参数初始化物理建模钢琴|
|参数说明
|mp|内存池|
|pPiano|Piano的指针|
|keyparam|钢琴88键的声学模型参数,你可以给出一个PX_NULL或0表示默认参数|
|soundboardparam|钢琴声板的参数(滤波器参数),你可以给出一个PX_NULL或0表示默认参数|
|返回值|返回PX_TRUE表示模型初始化成功,否者表示初始化失败|
|
| 函数名称 | 功能 |
|:---|:---|
|PX_PianoIndexToKey| 将钢琴按键索引转换为键名|
|参数说明
|index|按键索引|
|keyName|输出的键名|
|例|PX_PianoIndexToKey(51,name)中的name为C4|
|
| 函数名称 | 功能 |
|:---|:---|
|PX_PianoTriggerKey| 按下钢琴的一个键|
|参数说明
|pPiano|Piano的指针|
|index|按键索引|
|v|按键的力度|
|
| 函数名称 | 功能 |
|:---|:---|
|PX_PianoGo|从物理钢琴模型中读取一段混音后的PCM流,这个数据流是一个归一化后的float类型|
|参数说明
|pPiano|pPiano的指针|
|out|输出PCM流的指针|
|samples|读取流的个数|
|返回值|-|
|
| 函数名称 | 功能 |
|:---|:---|
|PX_PianoFree|释放Paino的资源|
|参数说明
|pPiano|Piano的指针|
|返回值|-|
|
其中,PX_PianoModel公开API定义如下:
| 函数名称 | 功能 |
|:---|:---|
|PX_PianoModelInitialize| 初始化PianoModel(这是一个耗时较长的函数,你可以通过访问该结构体的initialize_process成员来获取其初始化进度,其范围是0-1|
|参数说明
|pModel|PianoModel的指针|
|keyparam|钢琴88键的声学模型参数,你可以给出一个PX_NULL或0表示默认参数|
|soundboardparam|钢琴声板的参数(滤波器参数),你可以给出一个PX_NULL或0表示默认参数|
|返回值|返回PX_TRUE表示模型初始化成功,否者表示初始化失败|
|
| 函数名称 | 功能 |
|:---|:---|
|PX_PianoModelGo|从物理钢琴模型中读取一段混音后的PCM流,这个数据流是一个归一化后的float类型|
|参数说明
|pModel|PianoModel的指针|
|out|输出PCM流的指针|
|count|读取流的个数|
|返回值|-|
|
| 函数名称 | 功能 |
|:---|:---|
|PX_PianoModelTriggerKey|按下钢琴下的一个按键|
|参数说明
|pModel|PianoModel的指针|
|keyName|按键的名称,例如A1,#A1,C4,#C4...|
|返回值|-|
|
| 函数名称 | 功能 |
|:---|:---|
|PX_PianoModelTriggerIndex或PX_PianoModelTrigger|按下钢琴下的一个按键|
|参数说明
|pModel|PianoModel的指针|
|index|按键的索引,索引范围是0-87,分别对应钢琴88键盘从最左(第0键)到最右(第87键)|
|返回值|-|
|
| 函数名称 | 功能 |
|:---|:---|
|PX_PianoModelFree|释放PainoModel的资源|
|参数说明
|pModel|PianoModel的指针|
|返回值|-|
|
示范代码
以下示范代码以播放魔女之旅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);
}