Delphi 中的跨平台音频支持
Crossplatform audio support in Delphi
我在 运行 Delphi 东京,我正在寻找一种在 Windows 和 Android 上播放音频的方法(也许在某个时候 iOS).
在 Windows 上,我可以使用 PlaySound(PChar(ResourceName), 0, SND_RESOURCE or SND_ASYNC)
之类的东西,但我卡在了 Android 上。我试过 TMediaPlayer,但它需要大约一秒钟才能开始播放,这对于鼠标点击或屏幕点击来说太长了。
基本上我已经构建了一个扫雷克隆,我正在寻找声音支持(如果你想知道背景)。
建议?
有一些带有音频的街机游戏演示 class 您可以使用。参见 https://github.com/Embarcadero/DelphiArcadeGames
您还可以查看 Is there an alternative to TMediaPlayer for multi platform rapid sound effects?,了解在这些演示中使用音频管理 class 在 Android 上遇到的一些问题的描述。
使用游戏示例中提供的最新版本的音频管理器,开发人员只需删除所有 notifications/checking 音频文件实际加载并准备播放。我个人不喜欢简单地期待音频准备好播放的想法。
我的音频管理 classes 有点太复杂了,不能简单地 post,但是如果你需要这个功能,希望这段伪代码可以提供一些线索,让我知道我是如何解决这些缺点的游戏演示中提供的AudioManager
。
我的想法是在我的主应用程序中创建一个回调,当音频文件准备好播放时调用该回调。通过一些网络搜索,我发现以下链接对我的实施很有帮助:
https://developer.android.com/reference/android/media/SoundPool
https://www.101apps.co.za/articles/using-android-s-soundpool-class-a-tutorial.html
1 - 定义一个通知类型,该类型将提供有关准备播放的音频文件的足够信息。我的看起来像这样:
TSoundLoadedEvent = procedure(Sender: TObject; ASoundID: integer; AStatus: Integer) of object;
2 - 根据文档,定义一个 class 来处理 JSoundPool_OnLoadCompleteListener
。请注意,class 使用我们定义为 TSoundLoadedEvent
的自定义事件,这意味着 AudioManager
将必须实现此回调:
TMyAudioLoadedListener = class(TJavaLocal, JSoundPool_OnLoadCompleteListener)
private
FSoundPool : JSoundPool;
FOnJLoadCompleted : TSoundLoadedEvent;
public
procedure onLoadComplete(soundPool: JSoundPool; sampleId,status: Integer); cdecl;
property OnLoadCompleted: TSoundLoadedEvent read FOnJLoadCompleted write FOnJLoadCompleted;
property SoundPool: JSoundPool read FSoundPool;
end;
...
procedure TMyAudioLoadedListener.onLoadComplete(soundPool: JSoundPool; sampleId, status: Integer);
begin
FSoundPool := soundPool;
if Assigned(FOnJLoadCompleted) then
FOnJLoadCompleted(Self, sampleID, status);
end;
3 - 修改音频管理器class以实现监听器:
TAudioManager = Class
Private
fAudioMgr : JAudioManager;
fSoundPool : JSoundPool;
fmyAudioLoadedListener : TMyAudioLoadedListener;
fOnPlatformLoadComplete : TSoundLoadedEvent;
Public
Constructor Create; override;
...
procedure DoOnLoadComplete(Sender: TObject; sampleId: Integer; status: Integer);
...
property OnLoadComplete: TSoundLoadedEvent read fOnPlatformLoadComplete write fOnPlatformLoadComplete;
4 - 实现 JSoundPool
侦听器并将侦听器的回调连接到我们的 AudioManager
:
constructor TAudioManger.Create;
begin
...
//create our listener
fmyAudioLoadedListener := TMyAudioLoadedListener.Create;
// set the listener callback
fmyAudioLoadedListener.OnLoadCompleted := DoOnLoadComplete;
// inform JSoundPool that we have a listener
fSoundPool.setOnLoadCompleteListener( fmyAudioLoadedListener );
...
和
procedure TAudioManager.DoOnLoadComplete(Sender: TObject; sampleId: Integer; status: Integer);
begin
if Succeeded(status) then //remove this if you want all notifications
begin
if Assigned(Self.fOnPlatformLoadComplete) then
fOnPlatformLoadComplete( self, sampleID, status );
end;
end;
5 - 最后一件事是在主应用程序中实现回调:
TMainForm = class(TForm)
...
fAudioMgr : TAudioManager;
...
procedure OnSoundLoaded(Sender: TObject; ASoundID: integer; AStatus: integer);
然后,在您创建 AudioManager
的地方,将新的 TSoundLoadedEvent
分配给我调用的本地过程 OnSoundLoaded
:
procedure TMainForm.FormCreate(Sender: TObject);
...
begin
...
fAudioMgr := TAudioManager.Create;
fAudioMgr.OnSoundLoaded := OnSoundLoaded;
现在,当音频文件准备好播放时,您应该会收到通知:
procedure TMainForm.OnSoundLoaded(Sender: TObject; ASoundID: integer; AStatus: integer);
begin
// track IDs when loading sounds to identify which one is ready
// check status to confirm that the audio was loadded successfully
end;
这绝对只是点点滴滴,但希望能有所帮助。
我在 运行 Delphi 东京,我正在寻找一种在 Windows 和 Android 上播放音频的方法(也许在某个时候 iOS).
在 Windows 上,我可以使用 PlaySound(PChar(ResourceName), 0, SND_RESOURCE or SND_ASYNC)
之类的东西,但我卡在了 Android 上。我试过 TMediaPlayer,但它需要大约一秒钟才能开始播放,这对于鼠标点击或屏幕点击来说太长了。
基本上我已经构建了一个扫雷克隆,我正在寻找声音支持(如果你想知道背景)。
建议?
有一些带有音频的街机游戏演示 class 您可以使用。参见 https://github.com/Embarcadero/DelphiArcadeGames
您还可以查看 Is there an alternative to TMediaPlayer for multi platform rapid sound effects?,了解在这些演示中使用音频管理 class 在 Android 上遇到的一些问题的描述。
使用游戏示例中提供的最新版本的音频管理器,开发人员只需删除所有 notifications/checking 音频文件实际加载并准备播放。我个人不喜欢简单地期待音频准备好播放的想法。
我的音频管理 classes 有点太复杂了,不能简单地 post,但是如果你需要这个功能,希望这段伪代码可以提供一些线索,让我知道我是如何解决这些缺点的游戏演示中提供的AudioManager
。
我的想法是在我的主应用程序中创建一个回调,当音频文件准备好播放时调用该回调。通过一些网络搜索,我发现以下链接对我的实施很有帮助:
https://developer.android.com/reference/android/media/SoundPool
https://www.101apps.co.za/articles/using-android-s-soundpool-class-a-tutorial.html
1 - 定义一个通知类型,该类型将提供有关准备播放的音频文件的足够信息。我的看起来像这样:
TSoundLoadedEvent = procedure(Sender: TObject; ASoundID: integer; AStatus: Integer) of object;
2 - 根据文档,定义一个 class 来处理 JSoundPool_OnLoadCompleteListener
。请注意,class 使用我们定义为 TSoundLoadedEvent
的自定义事件,这意味着 AudioManager
将必须实现此回调:
TMyAudioLoadedListener = class(TJavaLocal, JSoundPool_OnLoadCompleteListener)
private
FSoundPool : JSoundPool;
FOnJLoadCompleted : TSoundLoadedEvent;
public
procedure onLoadComplete(soundPool: JSoundPool; sampleId,status: Integer); cdecl;
property OnLoadCompleted: TSoundLoadedEvent read FOnJLoadCompleted write FOnJLoadCompleted;
property SoundPool: JSoundPool read FSoundPool;
end;
...
procedure TMyAudioLoadedListener.onLoadComplete(soundPool: JSoundPool; sampleId, status: Integer);
begin
FSoundPool := soundPool;
if Assigned(FOnJLoadCompleted) then
FOnJLoadCompleted(Self, sampleID, status);
end;
3 - 修改音频管理器class以实现监听器:
TAudioManager = Class
Private
fAudioMgr : JAudioManager;
fSoundPool : JSoundPool;
fmyAudioLoadedListener : TMyAudioLoadedListener;
fOnPlatformLoadComplete : TSoundLoadedEvent;
Public
Constructor Create; override;
...
procedure DoOnLoadComplete(Sender: TObject; sampleId: Integer; status: Integer);
...
property OnLoadComplete: TSoundLoadedEvent read fOnPlatformLoadComplete write fOnPlatformLoadComplete;
4 - 实现 JSoundPool
侦听器并将侦听器的回调连接到我们的 AudioManager
:
constructor TAudioManger.Create;
begin
...
//create our listener
fmyAudioLoadedListener := TMyAudioLoadedListener.Create;
// set the listener callback
fmyAudioLoadedListener.OnLoadCompleted := DoOnLoadComplete;
// inform JSoundPool that we have a listener
fSoundPool.setOnLoadCompleteListener( fmyAudioLoadedListener );
...
和
procedure TAudioManager.DoOnLoadComplete(Sender: TObject; sampleId: Integer; status: Integer);
begin
if Succeeded(status) then //remove this if you want all notifications
begin
if Assigned(Self.fOnPlatformLoadComplete) then
fOnPlatformLoadComplete( self, sampleID, status );
end;
end;
5 - 最后一件事是在主应用程序中实现回调:
TMainForm = class(TForm)
...
fAudioMgr : TAudioManager;
...
procedure OnSoundLoaded(Sender: TObject; ASoundID: integer; AStatus: integer);
然后,在您创建 AudioManager
的地方,将新的 TSoundLoadedEvent
分配给我调用的本地过程 OnSoundLoaded
:
procedure TMainForm.FormCreate(Sender: TObject);
...
begin
...
fAudioMgr := TAudioManager.Create;
fAudioMgr.OnSoundLoaded := OnSoundLoaded;
现在,当音频文件准备好播放时,您应该会收到通知:
procedure TMainForm.OnSoundLoaded(Sender: TObject; ASoundID: integer; AStatus: integer);
begin
// track IDs when loading sounds to identify which one is ready
// check status to confirm that the audio was loadded successfully
end;
这绝对只是点点滴滴,但希望能有所帮助。