Jump to content


 

* * * * * 1 votes

Reading a wav file and playing the raw PCM audio data


3 replies to this topic

#1 monoceres

    Blue dream

  • AutoIt MVPs (MVP)
  • 3,609 posts
  • Gender:Male
  • Location:Linköping, Sweden

Posted 07 September 2008 - 05:35 PM

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.

[ code='text' ]    ( ExpandCollapse - Popup )
#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 ;)


:)

Edited by monoceres, 07 September 2008 - 05:36 PM.

Posted Image

Is the link in my post broken? I do not longer own my domain, all the files are moved to my new domain.
Example: http://monoceres.se/test.au3 -> http://andhen.mine.nu/monoceres.se/test.au3



#2 ProgAndy

    You need AutoItObject

  • AutoIt MVPs (MVP)
  • 2,414 posts
  • Gender:Male
  • Location:Germany

Posted 07 September 2008 - 05:52 PM

So, this is a func which creates a struct from a union :D (AutoIt doesn't support unions, so you have to work-around... )
[ autoIt ]    ( ExpandCollapse - Popup )
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* Posted Image [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

#3 cits

    Newbie

  • Full Members
  • 6 posts
  • Gender:Male

Posted 30 January 2011 - 09:04 AM

Quote


$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?

#4 Melba23

    Yes, me!

  • Moderators (Mod)
  • 11,317 posts
  • Gender:Not Telling
  • Location:Where never lark or even eagle flew

Posted 30 January 2011 - 03:47 PM

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
StringSize - Automatically size controls to fit text. - ExtMsgBox - A user customisable replacement for MsgBox. - Toast - Small GUIs which pop out of the Systray.
Marquee - Scrolling tickertape GUIs. - Scrollbars - Automatically sized scrollbars with a single command. - GUIFrame - Subdivide GUIs into many adjustable frames.
GUIExtender - Extend and retract multiple sections within a GUI. - .NoFocusLines - Remove the dotted focus lines from buttons, sliders, radios and checkboxes.
RecFileListToArray - An alternative to _FileListToArray with user-defined include/exclude masks, maximum recursion level, sorting and displayed path options
GUIListViewEx - Insert, delete, move, drag and sort ListView items. - ChooseFileFolder - Single and multiple selections from specified path tree structure.
Notify - Small notifications on the edge of the display





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users