dormky 2 Posted January 10 (edited) 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 January 10 by dormky Share this post Link to post
Der schöne Günther 316 Posted January 10 (edited) 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 January 10 by Der schöne Günther 1 Share this post Link to post
DelphiUdIT 176 Posted January 10 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
dormky 2 Posted January 10 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
dormky 2 Posted January 10 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
Lajos Juhász 293 Posted January 10 (edited) 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 January 10 by Lajos Juhász Share this post Link to post
Der schöne Günther 316 Posted January 10 Sounds like you're trying to write an app such as this here: Share this post Link to post
dormky 2 Posted January 10 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
DelphiUdIT 176 Posted January 10 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
Rollo62 536 Posted January 10 (edited) 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 January 10 by Rollo62 Share this post Link to post
dormky 2 Posted January 10 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
David Heffernan 2345 Posted January 10 FYI this appears to be cross-posted here: playsound - How can I play quick sounds in Delphi? - Stack Overflow Share this post Link to post
DelphiUdIT 176 Posted January 10 (edited) 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 Edited January 10 by DelphiUdIT Share this post Link to post
Remy Lebeau 1397 Posted January 10 (edited) 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 January 10 by Remy Lebeau Share this post Link to post
Brian Evans 105 Posted January 10 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