Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

My script uses the SoX package to play a (long) sound file.

exec 'play "/home/myname/soundfile.wav" &';

I need to be able to stop the sound before reaching the end of the sound file. The SoX documentation says:

...it can be terminated earlier by sending an interrupt signal to the process (usually by pressing the keyboard interrupt key which is normally Ctrl-C)...

I tried this code to generate a child process, play the sound in that process, and kill the child process after a few seconds:

my $pid = fork || exec 'play "/home/myname/soundfile.wav" &'; sleep 5; kill $pid;

It doesn't work, of course. What am I doing wrong?

Replies are listed 'Best First'.
Re: Killing a child process
by ikegami (Patriarch) on Oct 03, 2017 at 07:36 UTC

    You're not executing play; you're executing a shell that forks and executes play and immediately exits. You are sending the signal to the zombie shell instead of play.

    Replace

    exec 'play "/home/myname/soundfile.wav" &';

    with

    exec 'play', '/home/myname/soundfile.wav';

      Changing the code in that way also doesn't work.

      my $pid = fork || exec 'play', '/home/myname/soundfile.wav'; sleep 5; kill $pid;

        What's not working? What error did it return?

Re: Killing a child process
by haukex (Archbishop) on Oct 03, 2017 at 08:09 UTC
    I need to be able to stop the sound before reaching the end of the sound file.

    SoX also has the trim operation:

    $ play "/home/myname/soundfile.wav" trim 0 5

    Should play only the first 5 seconds of your file.

      I checked the SoX documentation for stuff like that, but I need to stop the sound playback 'on demand'; I don't know in advance when the playback will be stopped.

        Ah, I see. I like offloading things like this to modules, in this case I think IPC::Run would be appropriate, the following works for me. If you think the child might misbehave and not terminate on SIGINT, see the IPC::Run doc section "Timeouts and Timers" on how to time out the kill operation.

        use IPC::Run qw/start/; my $h = start ['play','-q','/home/myname/soundfile.wav']; sleep 5; $h->signal('INT'); $h->finish;
Re: Killing a child process
by Anonymous Monk on Oct 03, 2017 at 08:34 UTC

    The corrected code snippet, with the usual thanks to all commenters.

    #!/usr/bin/perl use strict; use diagnostics; use warnings; my $pid = fork || exec 'play', '/home/ra/Desktop/trek2.mp3'; print "My PID is *$pid*\n"; sleep 5; kill 'KILL', $pid;

      The approach recommended above is actually the less violent

      kill INT => $pid;