Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Win32::Sound 16 bit example

by true (Pilgrim)
on Jul 20, 2005 at 22:23 UTC ( [id://476642]=perlquestion: print w/replies, xml ) Need Help??

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

Aldo wrote a great module Win32::Sound. I have been struggling with making an example using 16bit sound though and i would dig some help from any monk willing. Here's the example included with Win32::Sound distro. It generates a 8 bit sound in stereo.
use Win32::Sound; # Create the object $WAV = new Win32::Sound::WaveOut(44100, 8, 2); $data = ""; $counter = 0; $increment = 440/44100; # Generate 44100 samples ( = 1 second) for $i (1..44100) { # Calculate the pitch # (range 0..255 for 8 bits) $v = sin($counter/2*3.14) * 128 + 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 $WAV->Unload(); # drop it
Above example works great. My questoin is how to do this with 16bit sound. 16 bit sound has a range of 65335 values. It is stored different from 8bit as a signed integer value -32768 <-> 32767. Zero (no sound is 0). My best guess is to use pack i but i know it is not correct. The below example tries to make a 16 bit mono wav file the same way Aldo's 8bit example works. One 16bit sound is stored in 2 bytes while 8bit sound is stored in one. The pack i line is the line that needs correcting. WAV files are stored in little endian order.
use Win32::Sound; # Create the object $WAV = new Win32::Sound::WaveOut(44100, 16, 1); $data = ""; $counter = 0; $increment = 440/44100; # Generate 44100 samples ( = 1 second) for $i (1..44100) { # Calculate the pitch # (range 0..65335 for 16 bits) $v = int(sin($counter/2*3.14) * (65335/2)); #signed integer (v) range -32768 <-> 32767 $data .= pack("i", $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 $WAV->Unload(); # drop it
Thanks for reading.
jtrue

Replies are listed 'Best First'.
Re: Win32::Sound 16 bit example
by BrowserUk (Patriarch) on Jul 20, 2005 at 22:56 UTC

    If your only problem is how to pack a 16-bit unsigned value in le order?, then (from the pack entry in perlfunc):

    v An unsigned short in "VAX" (little-endian) order.

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.
      By george i think that's the answer. Sound generated with your fix is the exact same tone as the 8bit one!!! Thanks so much for your help BrowserUK! I tried to do this over a year ago and was stuck then too. I have read the pack docs so many times its crazy but their brevity times my ignorance about value types still confuses me. Here is a working example with your fix for a 16bit mono sound wave...
      use Win32::Sound; # Create the object $WAV = new Win32::Sound::WaveOut(44100, 16, 1); $data = ""; $counter = 0; $increment = 440/44100; # Generate 44100 samples ( = 1 second) for $i (1..44100) { # Calculate the pitch # (range 0..65335 for 16 bits) $v = int(sin($counter/2*3.14) * (65335/2)); #signed integer (v) range -32768 <-> 32767 $data .= pack("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 $WAV->Unload(); # drop it
      thanks again, hopefully someone else can use this too. BTW if anyone wants to do this in stereo here's the code for 16bit stereo sound too. Just as above, this generates a 1 second sinusoidal wave at 440Hz but in both channels.
      use Win32::Sound; # Create the object $WAV = new Win32::Sound::WaveOut(44100, 16, 2); $data = ""; $counter = 0; $increment = 440/44100; # Generate 44100 samples ( = 1 second) for $i (1..44100) { # Calculate the pitch # (range 0..65335 for 16 bits) $v = int(sin($counter/2*3.14) * (65335/2)); #signed integer (v) range -32768 <-> 32767 $data .= pack("v", $v); $data .= pack("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 $WAV->Unload(); # drop it exit;
      Thanks again all!
      jtrue
Unpacking 16bit sound
by true (Pilgrim) on Jul 22, 2005 at 19:44 UTC
    Yikes, When i try and unpack the 16bit sound value right after i pack it, i get a value which is incorrect if the initial value is less than zero. Basically, the unpack("v",...) is not working correctly. Any advice would be appreciated. I want to unpack the value so i can use it when i read in sound. I use this code only to illustrate. This program outputs two different numbers if the initial value of v is below zero. I need it to output the same numbers so i know i am reading the sound file correctly.
    use Win32::Sound; # Create the object my $WAV = new Win32::Sound::WaveOut(44100, 16, 1); my $data = ""; my $counter = 0; my $increment = 440/44100; # Generate 44100 samples ( = 1 second) for my $i (1..44100) { # Calculate the pitch # (range 0..65335 for 16 bits) $v = int(sin($counter/2*3.14) * (65335/2)); #signed integer (v) range -32768 <-> 32767 print "$v = ".unpack("v",pack("v", $v))."\n"; $data .= pack("v", $v); $counter += $increment; } $WAV->Load($data); # get it $WAV->Write(); # hear it 1 until $WAV->Status(); # wait for completion $WAV->Save("16.wav"); # write to disk $WAV->Unload(); # drop it exit;
    thanks to all for help and for reading.
    jtrue

      The 'v' is unsigned 16-bit little-endian. It works fine for packing signed 16-bit values to little-endian, but not for unpacking them.

      To recover the signed value use:

      print unpack 's', pack 'v', -32767;; -32767

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      The "good enough" maybe good enough for the now, and perfection maybe unobtainable, but that should not preclude us from striving for perfection, when time, circumstance or desire allow.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-04-19 23:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found