|
物理钢琴合成《魔女之旅》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 | 内存池 | 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, &#34;D:\\test.mid&#34;);
//设置音频回调函数
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);
} |
|