Recent

Author Topic: [SOLUTION] Thread based Sound Player, with Resume - Suspend  (Read 11241 times)

Shebuka

  • Sr. Member
  • ****
  • Posts: 427
Hi, I'm trying to create for my application a sound player TProcess on thread because:
1. if i waitforexit the gui will be frozzed untill end of sound
2. if i not waitforexit it will leave zombies

i created this class (attached) based on thread in witch i also try to not simply put the Execute part to sleep when there is nothing to play, but to suspend it, but after first play of sound it never resumes again...

Code: [Select]
procedure TPlaySoundAlert.Execute;
begin
  if FileExists(FSoundFile) then
  begin
    EnterCriticalSection(CritSection);    // ACQUIRE

    if not FToPlayAlert then
    begin
      LeaveCriticalSection(CritSection);  // RELEASE
      Self.Suspend;
      Exit;
    end;

    FToPlayAlert := False;
    LeaveCriticalSection(CritSection);    // RELEASE

    FPlayer.CommandLine := FSoundPlayer + ' ' + FSoundFile;
    FPlayer.Options := FPlayer.Options + [poUsePipes, poWaitOnExit];
    FPlayer.Execute;
    FPlayer.Terminate(0);

    Self.Suspend;
  end
  else
    Self.Suspend;
end;

procedure TPlaySoundAlert.PlayAlert();
begin
  EnterCriticalSection(CritSection);    // ACQUIRE

  FToPlayAlert := True;

  LeaveCriticalSection(CritSection);    // RELEASE

  Self.Resume;
end;
(see attached file for full code)

why?
« Last Edit: July 21, 2011, 10:14:45 am by Shebuka »

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: Thread based Sound Player, Resume - Suspend - Resume (nothing...)
« Reply #1 on: July 20, 2011, 10:44:28 pm »
If the Execute method ends, the thread ends.
IOW: implement a loop of some sort in the Execute method.
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

Shebuka

  • Sr. Member
  • ****
  • Posts: 427
Re: Thread based Sound Player, Resume - Suspend - Resume (nothing...)
« Reply #2 on: July 21, 2011, 10:08:21 am »
If the Execute method ends, the thread ends.
IOW: implement a loop of some sort in the Execute method.
Simply adding while not Terminated solved the thing ;)

Code: [Select]
procedure TPlaySoundAlert.Execute;
begin
  while not Terminated do
  begin
    if FileExists(FSoundFile) then
    begin
      EnterCriticalSection(CritSection);    // ACQUIRE

      if not FToPlayAlert then
      begin
        LeaveCriticalSection(CritSection);  // RELEASE
        Self.Suspend;
        Exit;
      end;

      FToPlayAlert := False;
      LeaveCriticalSection(CritSection);    // RELEASE

      FPlayer.CommandLine := FSoundPlayer + ' ' + FSoundFile;
      FPlayer.Options := FPlayer.Options + [poUsePipes, poWaitOnExit];
      FPlayer.Execute;
      FPlayer.Terminate(0);

      Self.Suspend;
    end
    else
      Self.Suspend;
  end;
end;

BTW the whole code is right enough? i'm still feel not much secure with threads.
« Last Edit: July 21, 2011, 10:13:44 am by Shebuka »

evoshroom

  • Full Member
  • ***
  • Posts: 157
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #3 on: June 06, 2012, 11:59:38 am »
Thanks for this code.  I was already using afplay from another tutorial, but the tutorial gave this code:

SoundProcess := TProcess.Create(nil);
SoundProcess.CommandLine := 'afplay ' + Filename;
SoundProcess.Execute;
SoundProcess.Free;

However, there is a problem with that code I discovered later.  You will eventually get Failed to Fork Process errors when using it extensively.

I was already looking into multithreading and third party sound libraries when I found your snippet here.  Your code works great!

However, I wanted to add a tip for others because your code only works great to play a single sound file.  If your application has the potential for overlapping sounds you need to add one or two small things.  First I added to the PlaySoundAlert.pas:

private
Finished: Boolean;

Then:

property IsTerminated: Boolean read Finished write Finished;

Then:

Finished := true; (in OnCreate at the start)

And:

Finished := false; (at the start of the while not Terminated do loop)
Finished := true; (and the end(s) of that section of code)

Then I created an array of TPlaySoundAlert in OnCreate of my application and SetLength() to 10.

Finally to play the sounds:

for i := 0 to 9 do
  if (SoundThread.IsTerminated) then
  begin
  SoundThread.SoundFile := FileName;
  SoundThread.PlayAlert();
  break;
  end;

So it looks for which of the 10 threads is not currently playing and picks one of those to play the sounds, letting you play up to 10 sounds at the same time without the stupid rare Failed to Fork errors, zombie processes or waiting.
« Last Edit: June 06, 2012, 12:31:19 pm by evoshroom »

evoshroom

  • Full Member
  • ***
  • Posts: 157
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #4 on: June 06, 2012, 12:37:42 pm »
Also, a question for you.  Do you have a similar fuss-less solution for Windows? PlaySound(PChar(FSoundFile), 0, SND_ASYNC); will work, but even if you thread it it will only play one sound file at a time it seems and if they overlap it gets all quiet.

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #5 on: June 06, 2012, 12:48:14 pm »
A couple of points here ->

Code: [Select]
  EnterCriticalSection(CritSection);    // ACQUIRE
  FToPlayAlert := True;
  LeaveCriticalSection(CritSection);    // RELEASE

The Critical sections here are doing nothing,  assigning a boolean field is Atomic.

Code: [Select]
Self.Suspend;

Suspend is marked deprecated, you might be better using Thread Events to get the same effect, look at RTLEventCreate.

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #6 on: June 06, 2012, 12:54:22 pm »
Quote
Do you have a similar fuss-less solution for Windows?

Your best option is to use some sort of sound library.  eg. BASS,FpSound etc, another advantage here it's then cross platform.

evoshroom

  • Full Member
  • ***
  • Posts: 157
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #7 on: June 06, 2012, 02:23:53 pm »
I downloaded the BASS .pas from their site, but it was just a .pas to load a DLL with no examples about how to do anything once the DLL is loaded.  Know of any Lazarus example code to just play a simple WAV with it?

I found the wiki for the FPSound library, but there is no download link.  Where can I get it?

evoshroom

  • Full Member
  • ***
  • Posts: 157
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #8 on: June 06, 2012, 02:38:26 pm »
Found FPSound finally.  Had the smart idea for Googling for FPSound.pas.  There is another thread with a guy who is looking for it too I'm going to go post on now.  It is at:

http://lazarus-ccr.svn.sourceforge.net/viewvc/lazarus-ccr/components/fpsound/

TheBlackSheep

  • Jr. Member
  • **
  • Posts: 93
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #9 on: June 06, 2012, 02:44:51 pm »
there's some stuff for various sound libraries in the Afterwarp game library site (BASS included) - it might need porting from Delphi (suggests it works on Win32 and MacOSX but not necessarily Linux);

http://www.afterwarp.net/resources/soundlib

if you follow the link to the forum there look to be some examples (if they need translating from German then Google is your friend).

TheBlackSheep


BigChimp

  • Hero Member
  • *****
  • Posts: 5740
  • Add to the wiki - it's free ;)
    • FPCUp, PaperTiger scanning and other open source projects
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #10 on: June 06, 2012, 03:00:55 pm »
Found FPSound finally.  Had the smart idea for Googling for FPSound.pas.  There is another thread with a guy who is looking for it too I'm going to go post on now.  It is at:

http://lazarus-ccr.svn.sourceforge.net/viewvc/lazarus-ccr/components/fpsound/
Perhaps a good idea to update the wiki then as fpsound is already documented/descirbed there;)

I'll do it if nobody's already done it.
Want quicker answers to your questions? Read http://wiki.lazarus.freepascal.org/Lazarus_Faq#What_is_the_correct_way_to_ask_questions_in_the_forum.3F

Open source including papertiger OCR/PDF scanning:
https://bitbucket.org/reiniero

Lazarus trunk+FPC trunk x86, Windows x64 unless otherwise specified

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #11 on: June 06, 2012, 03:04:12 pm »
Quote
Found FPSound finally.

You will probably want to go here too -> http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx

Shebuka

  • Sr. Member
  • ****
  • Posts: 427
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #12 on: June 06, 2012, 03:05:01 pm »
Thanks for this code.
You are welcome :D

Quote
However, I wanted to add a tip for others because your code only works great to play a single sound file.  If your application has the potential for overlapping sounds you need to add one or two small things.  First I added to the PlaySoundAlert.pas:

private
Finished: Boolean;

Then:

property IsTerminated: Boolean read Finished write Finished;

Then:

Finished := true; (in OnCreate at the start)

And:

Finished := false; (at the start of the while not Terminated do loop)
Finished := true; (and the end(s) of that section of code)

Then I created an array of TPlaySoundAlert in OnCreate of my application and SetLength() to 10.

Finally to play the sounds:

for i := 0 to 9 do
  if (SoundThread.IsTerminated) then
  begin
  SoundThread.SoundFile := FileName;
  SoundThread.PlayAlert();
  break;
  end;

So it looks for which of the 10 threads is not currently playing and picks one of those to play the sounds, letting you play up to 10 sounds at the same time without the stupid rare Failed to Fork errors, zombie processes or waiting.
Good improvements ;) i'v created it like that to play a single sound a time to not bore my users with too much spammed sound alerts.

A couple of points here ->

Code: [Select]
  EnterCriticalSection(CritSection);    // ACQUIRE
  FToPlayAlert := True;
  LeaveCriticalSection(CritSection);    // RELEASE

The Critical sections here are doing nothing,  assigning a boolean field is Atomic.
Good point :) it was placed there to be like a placeholder for future improvements with more that one assignment.

Quote
Code: [Select]
Self.Suspend;

Suspend is marked deprecated, you might be better using Thread Events to get the same effect, look at RTLEventCreate.
In actual code, i'm using Self.Running := False, will see for RTLEventCreate thanks ;)

evoshroom

  • Full Member
  • ***
  • Posts: 157
Re: [SOLUTION] Thread based Sound Player, with Resume - Suspend
« Reply #13 on: June 06, 2012, 04:18:29 pm »
So I downloaded and installed FPSound.  This is the nicest looking and most Pascal-like library I've seen yet.  However, it may not be cross-platform yet I don't think or at least it didn't compile for Mac without modification and the library dependency $IFDEF's at the start were for windows and linux only (though strangely the file structure in the sample was for Windows and Mac only).  It might have worked if I fiddled with it, but I was more concerned at getting it working on Windows anyway.

I compiled it on Windows and tested out the example which worked great...or so it seemed.

However, when I went to implement it in my project it behaves strangely.  I tried using the SoundPlayer var or creating TSoundDocuments, but it is all the same.  The first sound plays fine.  The second time it is called it plays the first sound twice (no matter what I play).  The the third time it is called it plays the first sound three times (no matter what I play).  There isn't any errors in the code I have and replacing the FPSound four-or-so example lines with PlaySound(PChar(FSoundFile), 0, SND_ASYNC); results in normal behavior again.

Does FPSound even work?  Has anyone else used it successfully?  If you have, how do you get it to work and is it possible to get it to play more than one sound at a time?

 

TinyPortal © 2005-2018