Jump to content
dormky

How can I make TTimer run in the main tread ?

Recommended Posts

I need to make a metronome. For this purpose I've been trying to use TTimer, but it seems that for every call, it spawns a new thread. How can I prevent this and make it run in the program's main thread ? The problem is that I'm calling a windows API asynchronously, which means the timer's thread ends before the Windows call completes.

Edited by dormky

Share this post


Link to post
6 minutes ago, dormky said:

I've been trying to use TTimer, but it seems that for every call, it spawns a new thread.

No, it doesn't. What makes you think so?

Edited by Der schöne Günther
  • Like 1

Share this post


Link to post

Like most events, the OnTimer event executes on the main thread. It might be what you do in the OnTimer procedure that is "asynchronous" and the procedure "OnTimer" exits before the async operations complete.

Share this post


Link to post
40 minutes ago, Der schöne Günther said:

No, it doesn't. What makes you think so?

Every call prints "Thread ID start" and "Thread ID Exit" to the debug log.

Share this post


Link to post

Ah, nevermind ! It seems that it's the Windows API call making this thread. Now why does calling PlaySound not work when it's called from a TTimer callback, I have no idea.

Share this post


Link to post

I've tested:

 

uses
  MMSystem;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  sndPlaySound('C:\Windows\Media\Tada.wav',
    SND_NODEFAULT Or SND_ASYNC);
end;

no problems.

Edited by Lajos Juhász

Share this post


Link to post
23 minutes ago, Lajos Juhász said:

I've tested:

 


uses
  MMSystem;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  sndPlaySound('C:\Windows\Media\Tada.wav',
    SND_NODEFAULT Or SND_ASYNC);
end;

no problems.

It breaks down when you go above 1000ms interval, try it with 500ms. You can even see the "Thread start" logs still going after stopping the timer.

Share this post


Link to post
Quote

You can use the PlaySound function to play waveform audio if the sound fits into available memory. (The sndPlaySound function offers a subset of the capabilities of PlaySound. To maximize the portability of your Win32-based application, use PlaySound, not sndPlaySound.)

I use PlaySound in async way (and with more then 10 seconds) in all my applications without any issue.

I use it with RESOURCE identifier, but with old app I used also with media file.

PlaySound('Alarm_Sound', HINSTANCE, SND_RESOURCE OR SND_ASYNC);

 

Share this post


Link to post
3 hours ago, dormky said:

It breaks down when you go above 1000ms interval, try it with 500ms. You can even see the "Thread start" logs still going after stopping the timer.

Perhaps a case of flooding, I would protect this like that:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  sndPlaySound('C:\Windows\Media\Tada.wav', SND_NODEFAULT Or SND_ASYNC);
  Timer1.Enabled := True;
end;

But I think you can run several sounds anyway in parallel, which may of course run in a new thread each.
The end of a sound can also be delayed, so that maybe the audio is not yet cleared, before the next requests starts.

Edit: Sorry - Below was FMX related, not VCL
Have you tried with MediaPlayer instead?
https://docwiki.embarcadero.com/RADStudio/Athens/en/Playing_Audio_Files
For this I'm pretty sure that this will play several parallel sounds in the background, without problems.

--------------------------------------------------------
But there were options for VCL as well
https://docwiki.embarcadero.com/RADStudio/Sydney/de/Einer_Anwendung_Audio-_oder_Videoclips_hinzufügen
 

Edited by Rollo62

Share this post


Link to post
1 minute ago, Rollo62 said:

Perhaps a case of flooding, I would protect this like that:


procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;
  sndPlaySound('C:\Windows\Media\Tada.wav', SND_NODEFAULT Or SND_ASYNC);
  Timer1.Enabled := True;
end;

But I think you can run several sounds anyway in parallel, which may of course run in a new thread each.
The end of a sound can also be delayed, so that maybe the audio is not yet cleared, before the next requests starts.

Have you tried with MediaPlayer instead?
https://docwiki.embarcadero.com/RADStudio/Athens/en/Playing_Audio_Files
For this I'm pretty sure that this will play several parallel sounds in the background, without problems.
 

This is the VCL subforum 😕 And this is async, so disabling the timer in-between would have no effect. And this is a metronome, so I need to be as exact as possible.

Share this post


Link to post
1 hour ago, dormky said:

This is the VCL subforum 😕 And this is async, so disabling the timer in-between would have no effect. And this is a metronome, so I need to be as exact as possible.

To be more exact as possible you can use MMSystem:

 

timeBeginPeriod(1);

timeEndPeriod(1);

These affect the timer resolution in Windows ... in this example the resolution is set to milliseconds between timeBeginPeriod and timeEndPeriod. You can find more information on: https://learn.microsoft.com/en-us/windows/win32/api/timeapi/nf-timeapi-timebeginperiod.

You can set (timebegin) at the start of application and reset (timeend) at the end.

 

P.S.: normally this is the accuracy ... thanks to Mark: https://learn.microsoft.com/it-it/sysinternals/downloads/clockres

 

image.thumb.png.e2759a0f1a83e03ab4240fdbe565cde9.png

Edited by DelphiUdIT

Share this post


Link to post
4 hours ago, dormky said:

And this is a metronome, so I need to be as exact as possible.

Then TTimer is not a good choice, as it is not a real-time timer, it is a message-based timer, and so is subject to the speed of the message queue. And also, the WM_TIMER message is low-priority and only generated when there are no other messages pending.  You should use an actual multimedia timer instead.  And don't use PlaySound() when high performance is needed.  Pre-prepare the audio sample ahead of time and use a multimedia API to play the sample when needed.  Such as waveOutWrite(), or more modern audio APIs like WASAPI, DirectSound/XAudio2, etc.

Edited by Remy Lebeau

Share this post


Link to post

Libraries that support editing/sequencing and playing MIDI and/or MOD/Tracker files while overkill for a metronome have all the parts to play sounds periodically with looping. Most have callbacks so a visual of playback can be maintained. Bit more work to get started but might be worth it to hand off the lower-level stuff needed for accurate playback.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×