Delphi中的媒体播放器:歌曲停止通知

3

我需要在Delphi7应用程序中嵌入一个简单的MP3播放器。 我将简单地扫描一个目录并随机播放所有文件。

我找到了两个可能的解决方案:一个使用Delphi MediaPlayer,另一个使用PlaySound Windows API。

都不起作用。

问题似乎在于缺少“停止”通知。像这样使用PlaySound:

playsound(pchar(mp3[r].name), 0, SND_ASYNC or SND_FILENAME);

我找不到一种(礼貌的)方法来请求Windows在歌曲停止播放时通知我。使用Delphi MediaPlayer,互联网上充斥着各种建议,例如这里:

http://www.swissdelphicenter.ch/en/showcode.php?id=689

http://delphi.cjcsoft.net/viewthread.php?tid=44448

procedure TForm1.FormCreate(Sender: TObject);
begin
  MediaPlayer1.Notify   := True;
  MediaPlayer1.OnNotify := NotifyProc;
end;

procedure TForm1.NotifyProc(Sender: TObject);
begin
  with Sender as TMediaPlayer do 
  begin
    case Mode of
      mpStopped: {do something here};
    end;
    //must set to true to enable next-time notification
    Notify := True;
  end;
end;
{
  NOTE that the Notify property resets back to False when a
  notify event is triggered, so inorder for you to recieve
  further notify events, you have to set it back to True as in the code.
  for the MODES available, see the helpfile for MediaPlayer.Mode;
}

我的问题是,在一首歌曲结束时确实会得到NotifyValue == nvSuccessfull,但也会在开始播放歌曲时得到,因此我不能依赖它。 此外,无论如何我从未收到“mode”属性状态的更改,这应该根据我找到的所有示例变为mpStopped。
这里有一个类似的问题 如何重复播放一首歌? 但是它不起作用,因为如上所述,我会收到两次nvSuccessfull,而无法区分开始和结束。
最后但并非最不重要的,这个应用程序应该从XP到Win10都能工作,这就是我在WinXP上使用Delphi7进行开发的原因。
谢谢,对于这篇文章的长度,我很抱歉,但在寻求帮助之前,我真的尝试了很多解决方案。

最简单的方法可能是使用 mciSendString。您可以通过 AllocateHWnd 创建一个不可见的窗口,并 监听 MM_MCINOTIFY 消息。 - Victoria
谢谢。一般来说,使用Windows API直接进行编程不是我的“拿手好戏”,但是在MSDN上阅读关于MM_MCINOTIFY的内容,它看起来就像是Delphi映射到“NotifyValue”属性的东西,所以我想我需要更多的帮助来区分这两者 :) - ZioBit
@Victoria - 听起来是一个有趣的解决方案 - 但如果你只是使用API而不依赖于VCL,那么你不可以使用线程吗?我曾经遇到过同样的问题(不同的应用程序但是同样的问题)。早期版本的Windows(我指的是XP之前的版本)在通知模式下非常稳定。后来的版本则不是。所以我也很好奇有没有一个好的解决方案。 - Dsm
@Dsm,是的,使用线程和MCI_WAIT标志(而不是MCI_NOTIFY)是另一种方法。或者有一个线程并调用PlaySound而不使用SND_ASYNC标志。另一个相对简单的选择可能是DirectShow API。 - Victoria
我应该真正开始深入研究Direct*相关的东西了。我已经在使用它来访问网络摄像头,所以...是时候开始学习了 :) :) :) - ZioBit
1个回答

2
为了检测何时加载新的文件进行播放,您可以使用 TMediaPlayer(以下简称 MP)的 OnNotify 事件和 EndPos 和 Position 属性。
首先设置 MP 并选择一个 TimeFormat,例如:
MediaPlayer1.Wait := False;
MediaPlayer1.Notify := True;
MediaPlayer1.TimeFormat := tfFrames;
MediaPlayer1.OnNotify := NotifyProc;

当您加载一个文件进行播放时,请设置EndPos属性。
MediaPlayer1.FileName := OpenDialog1.Files[NextMedia];
MediaPlayer1.Open;
MediaPlayer1.EndPos := MediaPlayer1.Length;
MediaPlayer1.Play;

而且OnNotify()过程

procedure TForm1.NotifyProc(Sender: TObject);
var
  mp: TMediaPlayer;
begin
  mp:= Sender as TMediaPlayer;

  if not (mp.NotifyValue = TMPNotifyValues.nvSuccessful) then Exit;

  if mp.Position >= mp.EndPos then
  begin
    // Select next file to play
    NextMedia := (NextMedia + 1) mod OpenDialog1.Files.Count;
    mp.FileName := OpenDialog1.Files[NextMedia];
    mp.Open;
    mp.EndPos := mp.Length;
    mp.Position := 0;
    mp.Play;
    // Set Notify, important
    mp.Notify := True;
  end;
end;

最后,针对您尝试使用MP.Mode = mpStopped模式切换到新歌曲的评论。该模式在操作按钮时会更改,即当用户按下停止按钮时会更改为mpStopped。更改歌曲并开始播放可能不是用户所期望的。


非常感谢您提供的解决方案(我会在24小时内尝试),以及关于mpStopped行为的解释,我之前并不知道。实际上,我没有使用用户界面,我将控件隐藏起来,这只是播放mp3文件的简单方法。 - ZioBit
这对我没有用,由于某种原因mp.EndPos始终为0(零)。我正在播放一个mp3文件。 - delphirules
@delphirules 请更具体一些。mp.EndPos应该不为零的哪一行出现了零? - Tom Brunberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接