Jump to content
Sign in to follow this  
monoceres

Reading a wav file and playing the raw PCM audio data

Recommended Posts

monoceres

Hi all!

Here's an example on how to read a wav file and playing the uncompressed PCM audio data inside it.

I did some heavy commenting so it should be easy to understand it.

#include <WINAPI.au3>

; Just a dummy variable I need for WINAPI_ReadFile
Global $NULL

; Path to file that is going to be read
Global $path = "C:\alive.wav"

; Just the three constants I'm using
Global Const $WAVE_MAPPER = 4294967295
Global Const $WAVE_FORMAT_PCM = 0x01
Global Const $WHDR_BEGINLOOP = 0x04

Global $winmm = DllOpen("winmm.dll")
Global $WaveDevice
Global $Data; A struct that will contain all the raw audio data

; WAVEFORMATEX
; Used to open a wavedevice
; http://msdn.microsoft.com/en-us/library/ms713497(VS.85).aspx
Global $WAVEFORMATEX = DllStructCreate("ushort wFormatTag;ushort nChannels;dword nSamplesPerSec;" & _
        "dword nAvgBytesPerSec;ushort nBlockAlign;ushort wBitsPerSample;" & _
        "ushort cbSize")

; WAVE_HEADER
; The header data that is read from the wav file
; Info found here: http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
Global $WAVE_HEADER = DllStructCreate("char ChunkID[4];int ChunkSize;char Format[4];char Subchunk1ID[4];" & _
        "int Subchunk1Size;short AudioFormat;short NumChannels;int SampleRate;" & _
        "int ByteRate;short BlockAlign;short BitsPerSample;char Subchunk2ID[4];" & _
        "int Subchunk2Size")

; WAVE_HDR
; Struct that contains the pointer to the raw audio data, which is sent to waveOutWrite
; http://msdn.microsoft.com/en-us/library/ms713724(VS.85).aspx
Global $WAVE_HDR = DllStructCreate("ptr lpData;dword dwBufferLength;dword dwBytesRecorded;uint dwUser;" & _
        "dword dwFlags;dword dwLoops;ptr lpNext;uint reserved")


; Open the file in read mode
$fhandle = _WinAPI_CreateFile($path, 2, 2, 2)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "ChunkID"), 4, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "ChunkSize"), 4, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "Format"), 4, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "Subchunk1ID"), 4, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "Subchunk1Size"), 4, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "AudioFormat"), 2, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "NumChannels"), 2, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "SampleRate"), 4, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "ByteRate"), 4, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "BlockAlign"), 2, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "BitsPerSample"), 2, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "Subchunk2ID"), 4, $NULL)
_WinAPI_ReadFile($fhandle, DllStructGetPtr($WAVE_HEADER, "Subchunk2Size"), 4, $NULL)

; Before we read the heavy stuff, lets check if the data is what we expect


If Not (DllStructGetData($WAVE_HEADER, "Format") == "WAVE") Then; All wav files should have the Format set to WAVE
    MsgBox(16, "Error", "This is not a valid wav file!" & @CRLF & "Script will terminate")
    Exit
ElseIf DllStructGetData($WAVE_HEADER, "AudioFormat") <> 1 Then; 1 means PCM (raw audio data) which is the only thing supported
    MsgBox(16, "Error", "This is wav file is compressed, only uncompressed files are supported" & @CRLF & "Script will terminate")
    Exit
ElseIf DllStructGetData($WAVE_HEADER, "BitsPerSample") <> 16 Then; THis script only deals with 16 bits, not difficult to fix though
    MsgBox(16, "Error", "This is wav file is has not a sample size of 16 bits, only 16 bits files are supported" & @CRLF & "Script will terminate")
    Exit
EndIf



; Create the buffer for the raw audio data
; Since this implemenetion is made for 16 bit wav files each sample is 16 bit (2 bytes) thus each element is a short.
; Amount of samples is calculated with the total size of the raw audio data divided by the size of each sample
$Data = DllStructCreate("ushort [" & DllStructGetData($WAVE_HEADER, "Subchunk2Size") / 2 & "]")
_WinAPI_ReadFile($fhandle, DllStructGetPtr($Data), DllStructGetData($WAVE_HEADER, "Subchunk2Size"), $NULL)

; No more reading, the entire file is in memory!
_WinAPI_CloseHandle($fhandle)


; Fill the WAVEFORMATEX structure, fortunetly the members are just like the ones we just read into memory
; Sometimes microsoft do make things easy;)
DllStructSetData($WAVEFORMATEX, "wFormatTag", $WAVE_FORMAT_PCM)
DllStructSetData($WAVEFORMATEX, "nChannels", DllStructGetData($WAVE_HEADER, "NumChannels"))
DllStructSetData($WAVEFORMATEX, "nSamplesPerSec", DllStructGetData($WAVE_HEADER, "SampleRate"))
DllStructSetData($WAVEFORMATEX, "nAvgBytesperSec", DllStructGetData($WAVE_HEADER, "ByteRate"))
DllStructSetData($WAVEFORMATEX, "nBlockAlign", DllStructGetData($WAVE_HEADER, "BlockAlign"))
DllStructSetData($WAVEFORMATEX, "wBitsPerSample", DllStructGetData($WAVE_HEADER, "BitsPerSample"))
DllStructSetData($WAVEFORMATEX, "cbSize", 0)

; Opens the default wave out device
; The ingored parameters are for callbacks
; http://msdn.microsoft.com/en-us/library/ms713754(VS.85).aspx
$call = DllCall($winmm, "int", "waveOutOpen", "ptr*", "", "uint", $WAVE_MAPPER, "ptr", DllStructGetPtr($WAVEFORMATEX), "ulong", 0, "ulong", 0, "dword", 0)
$WaveDevice = $call[1]

; Fill the WAVE_HDR structure with the pointer to the raw audio and some playback info
DllStructSetData($WAVE_HDR, "lpData", DllStructGetPtr($Data))
DllStructSetData($WAVE_HDR, "dwBufferLength", DllStructGetData($WAVE_HEADER, "Subchunk2Size"))
DllStructSetData($WAVE_HDR, "dwFlags", $WHDR_BEGINLOOP)
DllStructSetData($WAVE_HDR, "dwLoops", 1)

; Prepare the header for playback
; http://msdn.microsoft.com/en-us/library/ms713756(VS.85).aspx
$call = DllCall($winmm, "int", "waveOutPrepareHeader", "ptr", $WaveDevice, "ptr", DllStructGetPtr($WAVE_HDR), "uint", DllStructGetSize($WAVE_HDR))

; The moment has arrived, tme for p(l)ayback!
; This function is asynchronous so it returns as soon as playback has started
; http://msdn.microsoft.com/en-us/library/ms713764(VS.85).aspx
DllCall($winmm, "int", "waveOutWrite", "ptr", $WaveDevice, "ptr", DllStructGetPtr($WAVE_HDR), "uint", DllStructGetSize($WAVE_HDR))

MsgBox(64, "Info", "Wav file is playing!" & @CRLF & "Press OK when you wish to stop playback")

To get the playback position you need to use the waveOutGetPosition function, but it involves using a struct in a union which is in another struct and I didn't feel like going into that tonight.

However said autoit was easier than c++ was wrong :D

;)

Edited by monoceres

Broken link? PM me and I'll send you the file!

Share this post


Link to post
Share on other sites
ProgAndy

So, this is a func which creates a struct from a union ;) (AutoIt doesn't support unions, so you have to work-around... )

Global Const $TIME_BYTES = 0x4
Global Const $TIME_MIDI = 0x10
Global Const $TIME_MS = 0x1
Global Const $TIME_SAMPLES = 0x2
Global Const $TIME_SMPTE = 0x8
Global Const $TIME_TICKS = 0x20

; Prog@ndy
Func _Create_mmtime_tag_Ex($ptr)
    Local $type = DllStructCreate("uint",$ptr)
    Switch DllStructGetData($type,1)
        Case $TIME_MS
            Return DllStructCreate("UINT wType; DWORD ms",$ptr)
        Case $TIME_SAMPLES
            Return DllStructCreate("UINT wType; DWORD sample",$ptr)
        Case $TIME_BYTES
            Return DllStructCreate("UINT wType; DWORD cb",$ptr)
        Case $TIME_TICKS
            Return DllStructCreate("UINT wType; DWORD ticks",$ptr)
        Case $TIME_SMPTE
            Return DllStructCreate("UINT wType; BYTE hour; BYTE min; BYTE sec; BYTE frame; BYTE fps; BYTE dummy; BYTE pad[2]",$ptr)
        Case $TIME_MIDI
            Return DllStructCreate("UINT wType; DWORD midi",$ptr)
    EndSwitch
    Return SetError(1,0,0)
EndFunc

*GERMAN* [note: you are not allowed to remove author / modified info from my UDFs]My UDFs:[_SetImageBinaryToCtrl] [_TaskDialog] [AutoItObject] [Animated GIF (GDI+)] [ClipPut for Image] [FreeImage] [GDI32 UDFs] [GDIPlus Progressbar] [Hotkey-Selector] [Multiline Inputbox] [MySQL without ODBC] [RichEdit UDFs] [SpeechAPI Example] [WinHTTP]UDFs included in AutoIt: FTP_Ex (as FTPEx), _WinAPI_SetLayeredWindowAttributes

Share this post


Link to post
Share on other sites
Loz

$Data = DllStructCreate("ushort [" & DllStructGetData($WAVE_HEADER, "Subchunk2Size") / 2 & "]")

Throws an "Error Allocating Memory" popup on the above line. This is for a small 30K wave file.

DllStructGetData($WAVE_HEADER, "Subchunk2Size") returns 1992319348.

Something not right there.

ver 3.3.6.1 here

Any ideas?


.... GOTOs? We don't need no stinkin' GOTOs! ....

Share this post


Link to post
Share on other sites
Melba23

cits,

This is a very old topic. ;)

Take a look at the _Sound_* functions - they will play .wav files with a lot less fuss! :)

M23


Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind._______My UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.