|
|
发表于 2022-9-27 11:58:53
|
显示全部楼层
不流行,昨天晚上做的小玩具,叫Piano Executer的玩意,其工作流程如下图
简单来说,这是一个通过脚本语言来控制钢琴的演奏的一个程序
包含:
- 一个完整从词法分析到语法分析的编译型脚本编译器
- 一个脚本指令流执行的虚拟机
- 一个完全用数学物理方法建模的物理钢琴
- 一个音频流混音器
- 一个演奏用的脚本代码
这个程序可以让你使用算法编程方法来生成并演奏一个钢琴曲,例如,下面的代码演示的是逐步按下C4 D4 E4 F4 G4 A4 B4的音
#name "piano"#runtime thread 32#runtime stack 128host void Wait(int ms);//等待,毫秒为单位host void PianoKeyDown(string key);//钢琴键盘按下host void PianoKeyDownIndex(int index);//钢琴键盘按下,从左往右0-87一共88个键host void StartTrack(string trackFunction);//开始音轨函数export void track0(){ string note="CDEFGAB"; int i; for(i=0;i<7;i++) { string snote; snote[0]=note; snote[1]='4'; PianoKeyDown(snote); wait(800); }} export int main(){ StartTrack("track0");}
当然,它最大支持32个音轨函数的同时播放,例如,下面的是do rui mi fa so la xi和经典的4536251和弦的混合播放
#name "piano"#runtime thread 32#runtime stack 128host void Wait(int ms);host void PianoKeyDown(string key);host void PianoKeyDownIndex(int index);host void StartTrack(string trackFunction);export void track1(){ string note="CDEFGABBB"; int i; while(1) { for(i=0;i<7;i++) { string snote; snote[0]=note; snote[1]='4'; PianoKeyDown(snote); wait(800); } } }export void track0(){ int delay=800; while(1) { //4 PianoKeyDown("F3"); PianoKeyDown("F4"); PianoKeyDown("A4"); PianoKeyDown("C5"); wait(delay); PianoKeyDown("F4"); PianoKeyDown("A4"); PianoKeyDown("C5"); wait(delay); //5 PianoKeyDown("G3"); PianoKeyDown("G4"); PianoKeyDown("B4"); PianoKeyDown("D5"); wait(delay); PianoKeyDown("G4"); PianoKeyDown("B4"); PianoKeyDown("D5"); wait(delay); //3 PianoKeyDown("E3"); PianoKeyDown("E4"); PianoKeyDown("G4"); PianoKeyDown("B4"); wait(delay); PianoKeyDown("E4"); PianoKeyDown("G4"); PianoKeyDown("B4"); wait(delay); //6 PianoKeyDown("A3"); PianoKeyDown("A4"); PianoKeyDown("C5"); PianoKeyDown("E5"); wait(delay); PianoKeyDown("A4"); PianoKeyDown("C5"); PianoKeyDown("E5"); wait(delay); //2 PianoKeyDown("D3"); PianoKeyDown("D4"); PianoKeyDown("F4"); PianoKeyDown("A4"); wait(delay); PianoKeyDown("D4"); PianoKeyDown("F4"); PianoKeyDown("A4"); wait(delay); //5 PianoKeyDown("G3"); PianoKeyDown("G4"); PianoKeyDown("B4"); PianoKeyDown("D5"); wait(delay); PianoKeyDown("G4"); PianoKeyDown("B4"); PianoKeyDown("D5"); wait(delay); //1 PianoKeyDown("C3"); PianoKeyDown("C4"); PianoKeyDown("E4"); PianoKeyDown("G4"); wait(delay); PianoKeyDown("C4"); PianoKeyDown("E4"); PianoKeyDown("G4"); wait(delay); } } export int main(){ StartTrack("track0"); StartTrack("track1");}
当然,整个Piano Exectuer的代码如果除去PainterEngine部分的封装代码也比较简单
#include "windows.h"#include "stdio.h"#include "architecture/PainterEngine.h"#include "platform/modules/px_time.h"extern int PX_AudioInitializeHwnd(HWND hwnd);extern int PX_AudioInitialize(PX_SoundPlay* soundPlay);PX_SoundPlay soundPlay;px_char runtime_buffer[1024 * 1024 * 8] = {0};px_char script[1024 * 1024] = {0};PX_PianoModel pianoModel;//钢琴模型PX_Compiler compiler;//脚本编译器PX_VM vmInstance;//虚拟机//合成器合成px_void PX_PianoRead(px_void* userptr, px_byte* pBuffer, px_int readSize){ if (!readSize || readSize > 2048 * 2 * 2) { PX_memset(pBuffer, 0, readSize); } else { px_int oft = 0; px_int count = readSize / 4;//2bytes 2channels px_float out44100f[2048]; px_int i; px_short* PCM = (px_short*)pBuffer; PX_PianoModelGo(&pianoModel, out44100f, count); for (i = 0; i < count; i++) { PCM[oft++] = (px_short)(out44100f * 32768 * 8); PCM[oft++] = (px_short)(out44100f * 32768 * 8); } }}px_bool PX_VM_PianoKeyDown(PX_VM* Ins, px_void* ptr){ if (PX_VM_STACK(Ins, 0).type != PX_VM_VARIABLE_TYPE_STRING) { PX_VM_RET(Ins, PX_VM_Variable_int(0)); return PX_TRUE; } PX_PianoModelTriggerKey(&pianoModel, PX_VM_STACK(Ins, 0)._string.buffer); printf(PX_VM_STACK(Ins, 0)._string.buffer); printf("\n"); return PX_TRUE;}px_bool PX_VM_PianoKeyDownIndex(PX_VM* Ins, px_void* ptr){ if (PX_VM_STACK(Ins, 0).type != PX_VM_VARIABLE_TYPE_INT) { PX_VM_RET(Ins, PX_VM_Variable_int(0)); return PX_TRUE; } PX_PianoModelTriggerIndex(&pianoModel, PX_VM_STACK(Ins, 0)._int); return PX_TRUE;}px_bool PX_VM_Wait(PX_VM* Ins, px_void* ptr){ if (PX_VM_STACK(Ins, 0).type != PX_VM_VARIABLE_TYPE_INT) { PX_VM_RET(Ins, PX_VM_Variable_int(0)); return PX_TRUE; } Ins->pThread[Ins->T].sleep = PX_VM_STACK(Ins, 0)._int; PX_VM_RET(Ins, PX_VM_Variable_int(0)); return PX_TRUE;}px_bool PX_VM_CreateThread(PX_VM* Ins, px_void* userptr){ if (PX_VM_STACK(Ins, 0).type != PX_VM_VARIABLE_TYPE_STRING) { PX_VM_RET(Ins, PX_VM_Variable_int(0)); return PX_TRUE; } if (!PX_VMBeginThreadFunction(&vmInstance, PX_VMGetFreeThreadId(Ins), PX_VM_STACK(Ins, 0)._string.buffer, PX_NULL, 0)) { PX_VM_RET(Ins, PX_VM_Variable_int(0)); } return PX_TRUE;}int main(){ FILE* pf; px_memory payload; px_memorypool mp = MP_Create(runtime_buffer, sizeof(runtime_buffer)); px_dword now, lastUpdateTime; printf("PainterEngine Piano Executer\n"); printf("load player script\n"); fopen_s(&pf,"piano.txt", "rb"); if (!pf) { printf("player script not found\n"); return PX_FALSE; } fread(script, 1, sizeof(script), pf); fclose(pf); printf("compiling....\n"); PX_MemoryInitialize(&mp, &payload); if (!PX_CompilerInitialize(&mp, &compiler))return PX_FALSE; if (!PX_CompilerAddSource(&compiler, script)) { printf("compiler error:script too long.\n"); return PX_FALSE; } if (!PX_CompilerCompile(&compiler, &payload, "piano")) { printf("compiler error.check your script and try again\n"); return PX_FALSE; } printf("compile done\n"); if (!PX_VMInitialize(&vmInstance, &mp, payload.buffer, payload.usedsize)) { return PX_FALSE; } PX_VMRegistryHostFunction(&vmInstance, "PianoKeyDown", PX_VM_PianoKeyDown, PX_NULL); PX_VMRegistryHostFunction(&vmInstance, "PianoKeyDownIndex", PX_VM_PianoKeyDownIndex, PX_NULL); PX_VMRegistryHostFunction(&vmInstance, "Wait", PX_VM_Wait, PX_NULL); PX_VMRegistryHostFunction(&vmInstance, "StartTrack", PX_VM_CreateThread, PX_NULL); printf("piano model building...\n"); PX_PianoModelInitialize(&pianoModel, PX_NULL, PX_NULL); printf("build succeeded...\n"); if (!PX_SoundPlayInitialize(&mp, &soundPlay)) { return PX_FALSE; } if (!PX_AudioInitialize(&soundPlay)) { printf("sound device error.\n"); return PX_FALSE; } PX_AudioInitializeHwnd(GetConsoleWindow()); PX_SoundPlaySetUserRead(&soundPlay, PX_PianoRead, PX_NULL); printf("running...\n"); lastUpdateTime = PX_TimeGetTime(); PX_VMBeginThreadFunction(&vmInstance, 0, "main", 0, 0); while (PX_TRUE) { px_dword elapsed; now = PX_TimeGetTime(); elapsed = now - lastUpdateTime; lastUpdateTime = now; PX_VMRunTime(&vmInstance, -1, elapsed); if (!PX_VMIsRuning(&vmInstance)) { break; } }} |
|