views:

403

answers:

3

Part of my code goes like this:

while(1){
        my $winmm = new Win32::MediaPlayer;   
        $winmm->load('1.mp3'); $winmm->play; $winmm->volume(100);
        Do Some Stuff;
        last if some condition is met;
    }

Problem is: I want the music to be always on when I'm in the Do Some Stuff stage in the while loop. But the length of the music is so short that it will come to a full stop before I go to the next stage, so I want the music to repeat itself, but the Win32::Mediaplayer module does not seem to have a repeat mode, so I'm thinking of doing an infinite loop for the music playing part. Like this:

while(1){
 my $winmm = new Win32::MediaPlayer;   
 $winmm->load('1.mp3'); $winmm->play; $winmm->volume(100);
}
while(2){
Do some stuff;
last if some condition is met
}

But based on my current Perl knowledge if I'm in the while(1) part, I can never go to the while(2) part. Even if it comes to a nested loop, I have to do something to break out of the inside loop before going to the other part of the outside loop.

The answer to my question "Can we run two simultaneous non-nested loops in Perl?" may be a NO, but I assume there is some way of handling such situation. Correct me if I'm wrong.

Thanks as always for any comments/suggestions :)

UPDATE

I really appreciate the help from everyone. Thanks :) So the answer to my question is a YES, not a NO. I'm happy that I've learned how to use fork() and threads to solve a real problem :)

+6  A: 

You may try to fork, like this:

So you will create 2 threads, one of which will play your music and second one will do all your other stuff.

You should also think on terminating your music thread by some condition

    my @child_pids = ();
    my $pid = fork();
    if ($pid)
    {
        # parent
        push(@child_pids, $pid);
        Do some stuff;
        last if some condition is met
    }
    elsif ($pid == 0)
    {
        # child
        while(1){
         my $winmm = new Win32::MediaPlayer;   
         $winmm->load('1.mp3'); $winmm->play; $winmm->volume(100);
        }
        exit(0);
    }
    else
    {
        die "couldn’t fork: $!\n";
    }
Miollnyr
@user278023, thanks, thanks, this looks like a very good solution :) Thanks alot!
Mike
You are welcome.I have slightly updated code: now you can use saved pids to kill child process in parent when necessary.
Miollnyr
@user278023, Thanks to your answer, I'm glad that I've learned how to use fork() to solve a real problem. I had a little problem with this Win32::Mediaplayer module though. To make the music play repeatedly, I have to code in the while(1) loop like this: $winmm->load('1.mp3'); $winmm->play; $winmm->volume(100);sleep(4);The music lasts around 4 seconds in the test, without this sleep(4) line, the music simply refuses to play. Well, I'm glad that I've finally got the script working :) Thanks.
Mike
+10  A: 

You can run the two different processes in separate threads.

Something along the lines of:

use strict;
use warnings;
use threads;
use threads::shared;
use Win32::MediaPlayer;


my $killAudio :shared = undef;
my $audio = threads->create(\&playAudio);
my $condition = threads->create(\&doSomething,$audio);
$condition->join();

sub playAudio {

    my $winmm = new Win32::MediaPlayer;
    $winmm->load('1.mp3') or die 'Could not load file: $!';
    $winmm->volume(100);
    $winmm->play until $killAudio;
}

sub doSomething {
    my $thread = shift;
    my $conditionMet = undef;

    while (1) {
        ($conditionMet,$killAudio) = doSomeStuff();    # set doSomeStuff() to
                                                       # return only when 
                                                       # conditions are met

        $thread->join() if $killAudio;        # This line will terminate $audio
        last if $conditionMet;
    }
}

UPDATE

Based on Mike's comment below, the playAudio() subroutine can be rewritten as:

sub playAudio {
    my $winmm = new Win32::MediaPlayer;
    $winmm->load('1.mp3') or die 'Could not load file: $!';
    while (1) {
        $winmm->play;
        $winmm->volume(100);
        sleep($winmm->length/1000);
        last if $killAudio;
    }
}
Zaid
@Zaid, this also looks like a very promising solution to my problem. Thanks a lot. I'll try this threading thing too. Thanks :)
Mike
@Mike: Check out the revisions and code comments too.
Zaid
@Mike: Also, pay attention to the `playAudio()` sub, the creation of new `$winmm`s is unnecessary for every iteration of the while loop. I've updated the code to reflect that.
Zaid
@Zaid, thanks to your answer, I've now learned how to use threads to solve a real problem. Had some trouble making the playAudio sub working though. To make the music play, I changed the sub to this:sub playAudio { my $winmm = new Win32::MediaPlayer;$winmm->load('1.mp3') or die 'Could not load file: $!'; while(1){$winmm->play;$winmm->volume(100);sleep(4);last if $killAudio; }}In the test, the music lasts about 4 seconds. Otherwise, the music simply refuses to play. Anyway I'm happy that I've solved the problem.
Mike
@Mike: You're welcome! I've just taken a look at the `Win32::MediaPlayer` module documentation on CPAN. You could use `sleep($winmm->length/1000);` to allow the code to pause for long enough to play the entire audio file. (And `use Time::HiRes;` + `usleep($winmm->length);` would have the same result)
Zaid
@Zaid, sleep($winmm->length/1000) works fine :)
Mike
@Mike: Glad to know that!
Zaid
The combination of use Time::HiRes qw(usleep); and usleep($winmm->length); somehow does not work as expected.
Mike
Just checked perldoc. Looks like usleep($winmm->length);" works on microseconds.
Mike
And $length = $winmm->length; # Return the length in micro second integer. Weird. Why it isn't working
Mike
@Mike: Try `usleep` with a constant like 4000, to check it
Zaid
@Zaid, I've figured out why. 4000 doesn't work. 4,000,000 works.
Mike
@Mike: Ah yes, of course, μ is 1E-6, not 1E-3. So that should actually be `usleep(1000*$winmm->length);`, which isn't much better than the solution I posted. Anyway...
Zaid
@Zaid - +1 (not like this excellent answer needs another +1, but consider it a fee for prompting me to fix-up the Getopt::Long answer >=)
DVK
@DVK : Thanks a lot, this one's been aching for a 'Nice Answer' badge for quite some time now ;)
Zaid
@Zaid - heh... I must admit that consideration did indeed figure in my picking which of your Perl asnwers to upvote :)
DVK
+2  A: 

If your doSomething() stage fits naturally within a looping structure, you can simply check the status of the song periodically and seek back to the beginning if the song has ended -- admittedly a hack, but easy.

use strict;
use warnings;

use Win32::MediaPlayer;
my $winmm = Win32::MediaPlayer->new;

$winmm->load($ARGV[0]) or die $!;
$winmm->play;
my $end_of_song = $winmm->length;

# doSomething
for (1 .. 1000){
    # Perform difficult computations...
    sleep 2;

    # Has song ended?
    $winmm->seek(0) if $winmm->pos >= $end_of_song;
}
FM
+1 for a solution without threads, no need to add more complexity than is needed
Eric Strom
@FM, this also looks workable. Thanks :)
Mike
@FM, on second thoughts, no. it doesn't fit the bill. the song ends before difficult computations ends. I want the song to be on before difficult computations ends.
Mike