Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Playing wav files

by merrymonk (Hermit)
on Feb 08, 2019 at 12:39 UTC ( [id://1229609]=perlquestion: print w/replies, xml ) Need Help??

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

I want to play a sound wav file. I have tried the following but all I get is the same (possibly error) sound from my PC for whatever .wav file I use. Is there a simple way of doing this?
use Win32::Sound; Win32::Sound::Play($wav_file);

Replies are listed 'Best First'.
Re: Playing wav files
by haukex (Archbishop) on Feb 08, 2019 at 14:57 UTC

    You might be interested in the thread Playing sounds, maybe one of the methods described there helps.

Re: Playing wav files
by pryrt (Abbot) on Feb 08, 2019 at 15:20 UTC

    Win32::Sound has direct access to the various Windows sounds: to test that everything's working okay,

    perl -MWin32::Sound -le "print($_), Win32::Sound::Play($_) for qw/Syst +emDefault SystemAsterisk SystemExclamation SystemExit SystemHand Syst +emQuestion SystemStart/"
    ... when I did that, all but SystemHand were the same -- probably because I don't have a lot of those sounds defined on my machine (I like a quiet machine).

    If you go to cpanm --look Win32::Sound, which (if you have cpanminus (*)) will put you into the distro-directory for Win32::Sound, then cd into the samples directory, you should be able to run sinus.pl and hear a tone (the note A). Then perl -MWin32::Sound -le "Win32::Sound::Play('welcome.wav')" should play a small sound. (*: if you don't have cpanminus, download the Win32::Sound tarball and untar it, then go into the samples subdirectory.)

    My guess is what's happening in your example is that it's not finding $wav_file. In the same directory as welcome.wav above, run

    perl -MWin32::Sound -le "for(qw/welcome.wav DNE.wav/) { print qq(With +Default: $_); Win32::Sound::Play($_); sleep(1); print qq(No Default: +$_); Win32::Sound::Play($_,SND_NODEFAULT); sleep(1); }"
    . This should play the welcome sound twice, then play the system default (because it didn't find DNE.wav), then play nothing (because it didn't find DNE.wav and was set to no default)

Re: Playing wav files
by harangzsolt33 (Chaplain) on Feb 08, 2019 at 14:41 UTC
    I noticed that someone asked a very similar question on StackOverflow earlier in 2012, but I won't post a link to that page. I don't like that site and its rules. It's just a mess. So, let me copy and paste the whole page here:

    Playing Sound in Perl script

    I'm trying to add sound to a Perl script to alert the user that the transaction was OK (user may not be looking at the screen all the time while working). I'd like to stay as portable as possible, as the script runs on Windows and Linux stations.

    I can

    use Win32::Sound; Win32::Sound::Play('SystemDefault',SND_ASYNC);

    for Windows. But I'm not sure how to call a generic sound on Linux (Gnome). So far, I've come up with

    system('paplay /usr/share/sounds/gnome/default/alert/sonar.ogg');

    But I'm not sure if I can count on that path being available. So, three questions:

    Is there a better way to call a default sound in Gnome Is that path pretty universal (at least among Debain/Ubuntu flavors) paplay takes a while to exit after playing a sound, is there a better way to call it? I'd rather stay away from beeping the system speaker, it sounds awful (this is going to get played a lot) and Ubuntu blacklists the PC Speaker anyway. Thanks!

    asked Oct 10 '12 at 19:11

    1 Answer

    A more portable way to get the path to paplay (assuming it's there) might be to use File::Which. Then you could get the path like:

    use File::Which; my $paplay_path = which 'paplay';

    And to play the sound asynchronously, you can fork a subprocess:

    my $pid = fork; if ( !$pid ) { # in the child process system $paplay_path, '/usr/share/sounds/gnome/default/alert/sonar. +ogg'; } # parent proc continues here

    Notice also that I've used the multi-argument form of system; doing so avoids the shell and runs the requested program directly. This avoids dangerous bugs (and is more efficient.)

    answered Oct 10 '12 at 20:27

    Also, I'm writing this in Perl/Tk. fork has to explicitly call CORE::exit() or POSIX::_exit() or horrible things happen. – charlesbridge Oct 24 '12 at 11:23

    --- END OF PAGE ---

      Thank you. Something very similar works on my PC for system sounds. This is shown in the Perl below. I get all the same sound except for SystemHand. ..it is other wav files, which I know give different sounds (I can hear these in another application) which seem to be the problem.
      my (@wsn, $js, @wavf); $wsn[0] = 'SystemDefault'; $wsn[1] = 'SystemAsterisk'; $wsn[2] = 'SystemExclamation'; $wsn[3] = 'SystemExit'; $wsn[4] = 'SystemHand'; $wsn[5] = 'SystemQuestion'; $wsn[6] = 'SystemStart'; for($js = 0; $js <= 6; $js++) { print "before play $wsn[$js]\n"; Win32::Sound::Play($wsn[$js]); print "after play\n"; }

        That seems more like a reply to mine rather than the one it actually replied to. if you agree, I will consider it for reparenting. If you really meant it where you put it, sorry for the misunderstanding.

        The goal of my listing of the various System sounds was to ensure that your soundcard and Win32::Sound installation were correct. Which seems to be the case based on this code.

        My other examples -- running sinus.pl, or directly accessing the welcome.wav, while in the Win32::Sound samples/ directory -- was an attempt to make sure that you're really in the directory you think you are, and to make sure you can really get to a local wav file. If you don't want to download the whole tarball, you could run the code from the EXAMPLE portion of Win32::Sound, which will create sinus.wav in your local directory; you can then run perl -MWin32::Sound -e "Win32::Sound::Play('sinus.wav', SND_NODEFAULT)" and it should play, then perl -MWin32::Sound -e "Win32::Sound::Play('sinus.wavx', SND_NODEFAULT)" and it should do no sound at all, because 'sinus.wavx' doesn't exist.

Re: Playing wav files
by harangzsolt33 (Chaplain) on Feb 08, 2019 at 17:16 UTC
    Also related: If you just want your computer to read something to you out loud, you can do that too. This code works on Windows XP and higher. This code creates a VBScript file on the hard drive and executes it, so it's not the most ideal solution, but the job gets done! Lol

    If you're wondering why this script creates a TEMP folder, it's because Windows 8 and higher will give an Access Denied error if you try to create a file in the root folder. So, we can't create a temp file like C:\TEMP.TXT or anything like that. Windows XP would handle it all right, but Windows 10 requires you to have admin privilege for that. But you don't need admin privilege to create a folder and put a file within that folder.)

    #!/usr/bin/perl -w use strict; use warnings; SAY("Hello World!!!"); exit; # Usage: STATUS = SAY(TEXT, VOLUME) - Reads a text in computer voice o +n Windows computers (tested on Windows XP) Returns 1 if something was + read or 0 if nothing was read sub SAY { @_ or return 0; my $TEXT = shift; defined $TEXT or return 0; length($TEXT) or return 0; my $VOLUME = @_ ? shift : 100; defined $VOLUME or $VOLUME = 100; length($VOLUME) or $VOLUME = 100; $TEXT =~ tr|\"| |; # Remove all quotes print "\n\n$TEXT\n\n"; # For those who are deaf, it's important to a +lso print the same message. my $TEMP_VBS_FILE = 'C:\\TEMP\\SAY.VBS'; my $TEMPDIR = substr($TEMP_VBS_FILE, 0, rindex($TEMP_VBS_FILE, "\\") + + 1); my $VB_SCRIPT = "IF Wscript.Arguments.length > 1 THEN\nSET VOICE = C +reateObject(\"SAPI.SpVoice\")\nVOICE.Volume = Wscript.Arguments(0)\nV +OICE.Speak Wscript.Arguments(1)\nELSE\nWscript.echo \"Usage: say.vbs +<VOLUME> <TEXT>\"\nEND IF\n"; unless (-f $TEMP_VBS_FILE && -s $TEMP_VBS_FILE == length($VB_SCRIPT) +) { mkdir $TEMPDIR; open(SCRIPTFILE, "> $TEMP_VBS_FILE") or return 0; print SCRIPTFILE $VB_SCRIPT; close SCRIPTFILE; } `$TEMP_VBS_FILE $VOLUME \"$TEXT\"`; #unlink $TEMP_VBS_FILE; #rmdir $TEMPDIR; return 1; }

      FWIW, this is an option on OS X–

      moo@cowi>perl -e 'system say => qw/ Ohai der /' moo@cow>which say /usr/bin/say # say - Convert text to audible speech

      I use it for short scripts or command line stuff since the visual bell isn't always enough. Stuff like–

      run-my-thing.pl || say "Epic fail, hire a real programmer" # or echo "Long running script." && say "Done. Finally. Jeeze."

        Computer says no

      Oh you are opening up new scenarios for me! Can you point me to some pages describing the VB commands (I have no clue about VB, but I guess it should be easy to adapt), for example I guess there are more languages installed, how to select the one desired?

        Well, the good thing about VBScript is that it's included in Windows XP and all versions of Windows after that. So, if you create a file with .vbs extension and execute it, it will run. There's no need to install anything.

        The same thing is true about JavaScript and VBScript. Files that end with .js .vbs can be executed just like any binary. You can either double-click on them or invoke them from perl.

        But this probably doesn't work the same way on Mac or Android or Linux, so this solution is for Windows only. I am pretty sure there's a way to make Linux read something out loud, but I don't know how to do that. Oh wait, maybe it's through the PerlSpeak module.

        VBScript is kind of like the BASIC language. It's very similar. But if you are familiar with JavaScript, then that works as well. JavaScript's syntax is more like Perl's or C syntax. There are tons of VBScript example programs online and manuals and references. The above code I wrote that makes the computer talk I did it by looking up various things online. I mean I myself couldn't write it from memory, but if I have to write something, I can look it up online and put the program together using online references and articles. I rarely use VBScript, but it's a cool feature in Windows.

Re: Playing wav files
by merrymonk (Hermit) on Feb 08, 2019 at 21:58 UTC
    This is an update to my question. One of the suggestions was that it could not find the .wav file. Therefore I created a directory which had both the Perl to play the sound and the .wav files as well. This worked. As in the case where it did not work, I gave the full path to the .wav file (therefore this was the same in both cases). Therefore the suggestion that it could not find the file was correct but why it works when the wav files are in the same directory is a mystery to me. It would be nice to know.

      Making progress. I just ran the following on my computer: It played the wave once when it created it, once each for 'sinus.wav', './sinus.wav', and for the tempdir version, then finally played the WindowsDefault / error chime when it could not find 'DoesNotExist'. So, that means as long as the file exists and is readable (and is a valid wave file, presumably), Win32::Sound::Play() does what I expect.

      Please add a fourth and a fifth entry to the for-loop: once for a local copy of your .wav, and once for the copy that's in some other directory. Then run the following script, and report the results.

      use warnings; use strict; use Win32::Sound; # Create the object my $WAV = new Win32::Sound::WaveOut(44100, 8, 2); my $data = ""; my $counter = 0; my $increment = 440/44100; # Generate 44100 samples ( = 1 second) for my $i (1..44100) { # Calculate the pitch # (range 0..255 for 8 bits) my $v = sin($counter*2*3.14) * 127 + 128; # "pack" it twice for left and right $data .= pack("CC", $v, $v); $counter += $increment; } $WAV->Load($data); # get it $WAV->Write(); # hear it 1 until $WAV->Status(); # wait for completion $WAV->Save("sinus.wav"); # write to disk my $otherFile = $ENV{TEMP} . '/sinus.wav'; $WAV->Save($otherFile); # write to disk elsewhere $WAV->Unload(); # drop it ############ $|++; for('sinus.wav', './sinus.wav', $otherFile, 'DoesNotExist') { sleep(1); printf "'%s' does%s exist and is%s readable\n", $_, (-f $_ ? '' : +' not'), (-r $_ ? '' : ' not'); Win32::Sound::Play($_); (my $f = $_) =~ s{/}{\\}; print qx(cmd.exe /c dir /N "$f"); print "\n" x 4; } __END__ 'sinus.wav' does exist and is readable Volume in drive C is Windows Volume Serial Number is XXXX-XXXX Directory of C:\usr\local\share\PassThru\perl\perlmonks 02/08/2019 03:08 PM 96,278 sinus.wav 1 File(s) 96,278 bytes 0 Dir(s) 113,186,381,824 bytes free './sinus.wav' does exist and is readable Volume in drive C is Windows Volume Serial Number is XXXX-XXXX Directory of C:\usr\local\share\PassThru\perl\perlmonks 02/08/2019 03:08 PM 96,278 sinus.wav 1 File(s) 96,278 bytes 0 Dir(s) 113,186,381,824 bytes free 'C:\Users\pryrt\AppData\Local\Temp/sinus.wav' does exist and is readab +le Volume in drive C is Windows Volume Serial Number is XXXX-XXXX Directory of C:\Users\pryrt\AppData\Local\Temp 02/08/2019 03:08 PM 96,278 sinus.wav 1 File(s) 96,278 bytes 0 Dir(s) 113,186,381,824 bytes free 'DoesNotExist' does not exist and is not readable File Not Found Volume in drive C is Windows Volume Serial Number is XXXX-XXXX Directory of C:\usr\local\share\PassThru\perl\perlmonks

      As you might be able to guess, since I am able to access a .wav in my local directory and in a different directory, I see no reason why it should not work for you; thus, I added the file-exists / file-readable test and dir output to give some clue as to what's going wrong for you.

        I tried the Perl but found a problem which causes a message saying "perl.exe has stopped working".
        I am using Perl version 5.028000
        The area where it failed is
        $WAV->Load($data); # get it $WAV->Write(); # hear it 1 until $WAV->Status(); # wait for completion
        Specifically the line ...Load was ok
        ..but I did not get a print statement working after the row ...Write
        I did try removing the '1 until' because it did not look like proper Perl
        Any clues about how to make this work would be appreciated.
Re: Playing wav files
by karlgoethebier (Abbot) on Feb 08, 2019 at 20:18 UTC

    May i kindly ask why you need to accomplish this mission? Just because i gave it up with my multimedia activities from the commandline decades ago for some good reasons. Best regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

    perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1229609]
Approved by haukex
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (6)
As of 2024-03-28 22:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found