0

I have a list of sound files stored in rows of a StringGrid that I want to play in sequence. That is, when one ends, the next line of the grid is read and that file played.

Bass has a BASS_SYNC_END event that allows for a callback procedure that I used successfully in the VCL version of my application. However, it does not in FMX.

The setup for the callback is this:

BASS_ChannelSetSync(chan1, BASS_SYNC_END, 0, @EndNormal, nil);

Which, at the end of playing the file, goes to:

procedure EndNormal(handle: HSYNC; channel, data: DWORD; user: DWORD); cdecl;
begin
  CPUnit1.NormPlaying := false;
  Form1.AdvTimer.Enabled := true;  // worked in VCL, ontimer proc gets file and plays
end;

In FMX, the variable will get changed, but the timer is not enabled since, I believe, it is part of the main GUI thread and not the BASS thread. Probably just lucky it worked in VCL, but that's history.

So, what is the technique to get the next file to play after one finishes?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Dgbaxter
  • 51
  • 1
  • 6
  • 1
    If EndNormal is called outside the main thread, you will need to use `TThread.Synchronize` (or `TThread.Queue`) inside of it, especially for enabling the timer. One way you can test whether or not it is in the main thread is with: `if TThread.CurrentThread.ThreadID = MainThreadID then` etc – Dave Nottage May 01 '23 at 08:04
  • 1
    According to [SYNCPROC callback](https://www.un4seen.com/doc/#bass/SYNCPROC.html) documentation the calback function are executed within one of the threads `BASS.DLL` creates so you will have to synchronize that with the main thread if you are directly accessing any GUI components. – SilverWarior May 01 '23 at 13:43
  • "*Probably just lucky it worked in VCL*" - It should not work in VCL, either. VCL's `TTimer` is a window-based timer. Its window is tied to the thread that creates it (ie, the thread that creates the `TTimer`), and only that thread can start the timer running (not sure if other threads can stop the timer). The resulting `WM_TIMER` messages are dispatched only to the thread that owns the window. – Remy Lebeau May 01 '23 at 18:07
  • I think it works because I don't use the regular TTimer, but TSVATimer, a class I found in my travels. You may know of it, Remy, as you are very well connected. – Dgbaxter May 01 '23 at 22:07
  • @Dgbaxter ah, in that case, `TSVATimer` is not a window-based timer, it is a thread-based timer. It creates a worker thread that in turn uses [`timeSetEvent()`](https://learn.microsoft.com/en-us/previous-versions/dd757634(v=vs.85)) to trigger a periodic callback from a multimedia thread provided by Windows, and then `Synchronize()`'s its `OnTimer` event to the main UI thread. – Remy Lebeau May 03 '23 at 15:23

1 Answers1

1

Try to synchronize it:

procedure EndNormal(handle: HSYNC; channel, data: DWORD;  user: DWORD); cdecl;
begin
  TThread.Synchronize(NIL, // or Queue
    procedure
    begin
      CPUnit1.NormPlaying := false;
      Form1.AdvTimer.Enabled := true;
    end);
end;
AWirthK2
  • 46
  • 2