MIDI播放器尽可能精确地播放音符非常重要。我从来没有成功,总是责怪计时器(参见前一个帖子:How to prevent hints interrupting a timer)。最近我收购了ProDelphi并开始测量那段时间到底消耗了多少。结果非常令人惊讶,请参阅下面的示例代码。
procedure TClip_View.doMove (Sender: TObject; note, time, dure, max_time: Int32);
var x: Int32;
begin
{$IFDEF PROFILE}Profint.ProfStop; Try; Profint.ProfEnter(@self,1572 or $58B20000); {$ENDIF}
Image.Picture.Bitmap.Canvas.Pen.Mode := pmNot;
Image.Picture.Bitmap.Canvas.MoveTo (FPPos, 0);
Image.Picture.Bitmap.Canvas.LineTo (FPPos, Image.Height);
x := time * GPF.PpM div MIDI_Resolution;
Image.Picture.Bitmap.Canvas.Pen.Mode := pmNot;
Image.Picture.Bitmap.Canvas.MoveTo (x, 0);
Image.Picture.Bitmap.Canvas.LineTo (x, Image.Height);
FPPos := x;
// Bevel.Left := time * GPF.PpM div MIDI_Resolution;
{$IFDEF PROFILE}finally; Profint.ProfExit(1572); end;{$ENDIF}
end; // doMove //
测量结果(在Intel i7-920上没有调试代码,2,7Ghz):
Bevel.Left :=
)之外,x := time * GPF.PpM div MIDI_Resolution;
只需移动Bevel就可以花费60倍于绘制Canvas的CPU。这让我感到惊讶。测量1的结果非常可听(除此之外还有更多),但不是2和3。我需要某种形式的反馈给用户,因为玩家现在正在处理,钢琴卷上的某种线是可接受的方式。在我永远不断寻求减少定时事件循环中的CPU周期时,我有一些问题:
答案 0 :(得分:10)
您将无法改变世界,也无法改变VCL或Windows。我怀疑你要求那些人......
恕我直言,你应该更好地改变你的架构:
也就是说,音序器不会刷新UI,但UI会定期询问音序器的当前状态。这将是恕我直言更顺利。
回答您的确切问题:
DoubleBuffered := true
,或者使用一个专用组件(TPaintBox
),其中包含整个组件内容的全局位图,类似于消息WM_ERASEBKGND
一些代码:
procedure TMyPaintBox.WMEraseBkgnd(var Message: TWmEraseBkgnd);
begin
Message.Result := 1; // no erasing is necessary after this method call
end;
答案 1 :(得分:3)
我觉得你的位图缓冲是错误的。移动剪辑时,根本不需要重新绘制剪辑。您可以尝试使用此剪辑组件结构:
TMidiClip = Class(TControl)
Private
FBuffer: TBitmap;
FGridPos: TPoint;
FHasToRepaint: Boolean;
Public
Procedure Paint; Override; // you only draw the bitmap on the control canvas
Procedure Refresh; // you recompute the FBuffer.canvas
End;
更改某些属性(如“剪辑刻度长度”)时,将“FHasToRepaint”设置为true,但不更改“FGridPos”(网格上的位置)时。所以大多数时候,在你的Paint事件中,你只有你的FBuffer的副本。
实际上,这非常依赖于网格及其子项(剪辑)的设计。 我可能错了,但似乎你的设计在控件中分解不够:主网格应该是TControl,剪辑应该是TControl,甚至剪辑上的事件应该是一些TControls ......你只能定义一个通过这种方式强烈优化位图缓冲系统(又名“双缓冲”)。
关于定时器:你应该使用一个处理每个音频样本的音乐时钟,否则你的分辨率不够好。这样的时钟可以使用“Windows音频驱动程序”(mmsystem.pas)或“Asio”驱动程序(例如,在BASS,Delphi Asio Vst项目中有接口)来实现。
答案 2 :(得分:2)
到目前为止,解决此问题的最佳方法是阻止GUI消息队列干扰您的MIDI播放器。将MIDI播放器放在后台线程上,以便它可以在不中断主线程的情况下完成其工作。当然,这依赖于您在具有多个处理器的计算机上运行,但现在将其视为理所当然并非不合理。
根据您的评论判断,您的音频处理似乎被UI线程阻止。不要让这种情况发生,你的音频问题就会消失。如果您使用TThread.Synchronize
之类的东西从音频线程触发VCL事件,那么将阻止UI线程。改为使用异步通信方法。
你建议的加速VCL的替代方案并不可行。您无法修改VCL,即使可以,也可能很容易成为底层Windows代码的瓶颈。