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

Delphi 10.2中MCI播放MIDI文件重复播放失败问题求助

Delphi MCI MIDI 播放问题修复:重复选同一文件停止+MCI_CLOSE类型错误

问题根源

  1. 重复选文件失败:原代码仅在播放失败时关闭设备,正常播放完成依赖MM_MCINOTIFY消息关闭,但重复选同一文件时,旧设备实例可能未及时释放,导致MCI无法重新打开同一文件。
  2. MCI_CLOSE类型错误:Delphi中NULL是变体类型,而mciSendCommand第四个参数要求DWORD_PTR,直接传NULL会触发类型转换异常,需替换为0或合法指针。

修复步骤&代码

第一步:添加设备ID成员变量

在窗体类的private区域添加变量,跟踪当前打开的MIDI设备:

private
  FCurrentMidiDeviceID: Integer; // 保存当前播放的MIDI设备ID,初始化为0

第二步:修改PlayMidiFile函数

修复设备清理、参数初始化和类型错误:

uses
  MMSystem;

Function TForm1.PlayMidiFile(FileName: string): Word;
var
  wDeviceID: Integer;
  dwReturn : Word;
  mciOpen  : TMCI_Open_Parms;
  mciPlay  : TMCI_Play_Parms;
  mciStat  : TMCI_Status_Parms;
  mciStop  : TMCI_Generic_Parms;
begin
  // 先清理旧设备:停止播放并关闭
  if FCurrentMidiDeviceID <> 0 then
  begin
    mciStop.dwCallback := 0;
    mciSendCommand(FCurrentMidiDeviceID, MCI_STOP, 0, DWORD_PTR(@mciStop));
    mciSendCommand(FCurrentMidiDeviceID, MCI_CLOSE, 0, 0);
    FCurrentMidiDeviceID := 0;
  end;

  // 初始化参数结构体,避免残留旧数据
  ZeroMemory(@mciOpen, SizeOf(TMCI_Open_Parms));
  mciOpen.lpstrDeviceType := 'sequencer';
  mciOpen.lpstrElementName := PChar(FileName);

  // 打开MIDI设备
  dwReturn := mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE or MCI_OPEN_ELEMENT, DWORD_PTR(@mciOpen));
  if dwReturn <> 0 then
  begin
    Result := dwReturn;
    Exit;
  end;

  wDeviceID := mciOpen.wDeviceID;
  FCurrentMidiDeviceID := wDeviceID;

  // 检查MIDI映射器端口
  ZeroMemory(@mciStat, SizeOf(TMCI_Status_Parms));
  mciStat.dwItem := MCI_SEQ_STATUS_PORT;
  dwReturn := mciSendCommand(wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, DWORD_PTR(@mciStat));
  if dwReturn <> 0 then
  begin
    mciSendCommand(wDeviceID, MCI_CLOSE, 0, 0);
    FCurrentMidiDeviceID := 0;
    Result := dwReturn;
    Exit;
  end;

  // 启动播放,绑定窗体句柄接收完成通知
  ZeroMemory(@mciPlay, SizeOf(TMCI_Play_Parms));
  mciPlay.dwCallback := DWORD_PTR(Handle);
  dwReturn := mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, DWORD_PTR(@mciPlay));
  if dwReturn <> 0 then
  begin
    mciSendCommand(wDeviceID, MCI_CLOSE, 0, 0);
    FCurrentMidiDeviceID := 0;
    Result := dwReturn;
  end
  else
    Result := 0;
end;

第三步:添加MM_MCINOTIFY消息处理

播放完成后自动关闭设备,避免资源占用:

procedure TForm1.WndProc(var Message: TMessage);
begin
  inherited;
  if Message.Msg = MM_MCINOTIFY then
  begin
    // 播放成功完成时关闭设备
    if (Message.WParam = MCI_NOTIFY_SUCCESSFUL) and (FCurrentMidiDeviceID <> 0) then
    begin
      mciSendCommand(FCurrentMidiDeviceID, MCI_CLOSE, 0, 0);
      FCurrentMidiDeviceID := 0;
    end;
  end;
end;

关键修复说明

  • 强制清理旧设备:每次播放新文件前,先停止并关闭之前的设备,彻底释放文件占用,解决重复选同一文件的问题。
  • 替换NULL为0:将原代码中mciSendCommand(..., NULL)改为mciSendCommand(..., 0),适配Delphi类型系统,消除转换错误。
  • 初始化参数结构体:用ZeroMemory清空参数,避免结构体残留旧值导致MCI命令执行异常。
  • 跟踪设备ID:用成员变量记录当前设备,确保所有操作都能准确指向目标设备。

内容的提问来源于stack exchange,提问作者larshgf

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 20:24:23