如何在Delphi FMX应用中使用bass.dll按顺序播放音频文件列表?
FMX中使用BASS实现音频顺序播放的正确方法
你遇到的问题本质是FMX的GUI控件只能在主线程中操作,而BASS的BASS_SYNC_END回调是在后台线程执行的,直接在回调里启用定时器(GUI控件)会违反线程安全规则,导致操作失效。VCL中能生效是因为旧版VCL的部分控件对跨线程操作有一定兼容性,但FMX严格遵循单线程GUI模型。
核心思路:将GUI/播放逻辑切换到主线程执行
使用TThread.Queue(异步)或TThread.Synchronize(同步)将需要主线程处理的逻辑放到主线程消息队列中执行,确保线程安全。
方案1:修复定时器启用逻辑
修改你的回调函数,把启用定时器的操作通过TThread.Queue投递到主线程:
procedure EndNormal(handle: HSYNC; channel, data: DWORD; user: DWORD); cdecl; begin CPUnit1.NormPlaying := false; // 将GUI操作投递到主线程执行 TThread.Queue(nil, procedure begin Form1.AdvTimer.Enabled := true; end); end;
方案2:直接在回调中触发下一首播放(更简洁高效)
省去定时器,直接在回调中通过主线程触发下一首音频的播放,逻辑更直接:
- 先在Form类中定义记录当前播放行的变量和播放方法:
private CurrentPlayRow: Integer; // 当前播放的行索引 procedure PlayNextAudio; // 播放下一首的方法
- 实现
PlayNextAudio方法:
procedure TForm1.PlayNextAudio; var AudioFilePath: string; chan1: HSTREAM; begin // 检查是否还有未播放的行 if CurrentPlayRow < StringGrid1.RowCount then begin // 从StringGrid中获取音频文件路径(假设路径在第0列) AudioFilePath := StringGrid1.Cells[0, CurrentPlayRow]; // 创建音频流并播放 chan1 := BASS_StreamCreateFile(False, PChar(AudioFilePath), 0, 0, 0); if chan1 <> 0 then begin BASS_ChannelPlay(chan1, False); // 设置播放结束回调 BASS_ChannelSetSync(chan1, BASS_SYNC_END, 0, @EndNormal, nil); Inc(CurrentPlayRow); // 移动到下一行 end; end; end;
- 修改回调函数,释放资源并触发下一首播放:
procedure EndNormal(handle: HSYNC; channel, data: DWORD; user: DWORD); cdecl; begin CPUnit1.NormPlaying := false; BASS_StreamFree(channel); // 释放当前音频流资源 // 投递播放下一首的逻辑到主线程 TThread.Queue(nil, Form1.PlayNextAudio); end;
- 初始化播放(比如在按钮点击事件中):
procedure TForm1.btnStartPlayClick(Sender: TObject); begin CurrentPlayRow := 0; // 从第0行开始播放 PlayNextAudio; end;
注意事项
- 确保使用的BASS库与FMX平台匹配(Windows/Android/iOS等平台需要对应版本的BASS库)
- 涉及多线程访问的变量(如
CurrentPlayRow),如果有其他线程修改,需要用临界区(TCriticalSection)保证线程安全 - 播放结束后一定要调用
BASS_StreamFree释放音频资源,避免内存泄漏
内容的提问来源于stack exchange,提问作者Dgbaxter
相关产品推荐
相关产品推荐

