You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi中TTask.Run内for循环最后一个MIDI音符播放异常问题

MIDI播放最后一个音符时长异常的原因与解决

问题场景

我在Delphi中通过TTask.Run的匿名过程实现了一段MIDI音符播放代码,运行后发现最后一个音符的播放时长明显短于其他音符;当把循环内的Sleep(200)改为Sleep(100)时,最后一个音符甚至完全不发声。在关闭MIDI设备前添加Sleep(200)可以"修复"这个问题,但想知道背后的具体原因。

代码示例

type
  TMainForm = class(TForm)
    RandomBtn: TButton;
    procedure RandomBtnClick(Sender: TObject);
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

uses
  MMSystem, Threading;

function MIDIEncodeMessage(Msg, Param1, Param2: Integer): Integer;
begin
  Result := Msg + (Param1 shl 8) + (Param2 shl 16);
end;

procedure Music(Notes: TArray<Integer>);
const
  MIDI_DEVICE = 0;
  MIDI_NOTE_ON = $90;
  MIDI_NOTE_OFF = $80;
var
  MO: HMIDIOUT;
  n: Integer;
begin
  MidiOutOpen(@MO, MIDI_DEVICE, 0, 0, CALLBACK_NULL);

  for n in Notes do begin
    MidiOutShortMsg(MO, MIDIEncodeMessage(MIDI_NOTE_ON, n, 127));
    Sleep(200);
    MidiOutShortMsg(MO, MIDIEncodeMessage(MIDI_NOTE_OFF, n, 127));
  end;

  MidiOutClose(MO);
end;

procedure TMainForm.RandomBtnClick(Sender: TObject);
begin
  TTask.Run(
    procedure
    begin
      Music([64, 66, 67, 69, 71]);
    end);
end;

end.

原因分析

  • MIDI消息是异步处理的:调用MidiOutShortMsg只是把消息投递到MIDI设备的缓冲区,系统不会等待消息实际执行完成就返回。最后一个音符的NOTE_OFF发送后,程序立刻调用MidiOutClose关闭设备,此时设备可能还没完成这个音符的播放流程,甚至缓冲区里的消息都没来得及被处理,导致音符播放不完整或完全没声音。
  • 循环内Sleep的作用差异:前面的音符,Sleep(200)是在NOTE_ON和NOTE_OFF之间等待,确保音符能播放200毫秒后再触发关闭。但最后一个音符的NOTE_OFF发送后没有等待,直接关闭设备,设备没有足够时间完成播放。当把Sleep缩短到100毫秒时,这个时间差变得更小,设备还没响应NOTE_ON就被终止,所以最后一个音符完全听不到。

解决方案

简单可靠的临时修复

在最后一个NOTE_OFF发送后,添加一段等于音符播放时长的等待,确保设备处理完所有消息后再关闭:

for n in Notes do begin
  MidiOutShortMsg(MO, MIDIEncodeMessage(MIDI_NOTE_ON, n, 127));
  Sleep(200);
  MidiOutShortMsg(MO, MIDIEncodeMessage(MIDI_NOTE_OFF, n, 127));
end;
Sleep(200); // 等待最后一个音符的关闭消息处理完成
MidiOutClose(MO);

专业的回调方式(进阶)

使用MIDI的回调机制跟踪消息处理状态,当所有消息都执行完毕后再关闭设备。这种方式不需要依赖Sleep,精度和可靠性更高,适合复杂的MIDI播放场景。

内容的提问来源于stack exchange,提问作者new-to-so

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 02:12:33