Jump to content

Reading a wav file and playing the raw PCM audio data


monoceres
 Share

Recommended Posts

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!

Link to comment
Share on other sites

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

Link to comment
Share on other sites

  • 2 years later...

$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! ....
Link to comment
Share on other sites

  • Moderators

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

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see 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

 

Link to comment
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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...