DirectSound Recording with WAVE FORMAT EXTENSIBLE
Posted: Fri Oct 01, 2010 4:58 pm
Below is a demo program which makes a wave recording using DirectSound and WAVE FORMAT EXTENSIBLE. CAUTION: there may yet be some bugs in the file save part. It should do multi-channel (more than two channel) recording.
Code: Select all
;DIRECTSOUND RECORD PROGRAM
;chris319
;UPDATED ON 10/1/2010
;THIS PROGRAM RECORDS 2 SECONDS OF AUDIO
;FROM THE DEFAULT RECORDING DEVICE, THEN SAVES THE
;AUDIO AS A WAV FILE NAMED "test.wav". YOU CAN FOOL
;AROUND WITH CHANNELS, SAMPLE RATE AND BIT DEPTH.
OpenConsole()
#CHANNELS = 4
#SAMPLE_RATE = 44100
#BIT_DEPTH = 16
#SECONDS = 2
#WAVE_FORMAT_PCM = 1
#WAVE_FORMAT_EXTENSIBLE = $FFFE
#SPEAKER_DIRECTOUT = 0
#SPEAKER_FRONT_LEFT = ($1)
#SPEAKER_FRONT_RIGHT = ($2)
#SPEAKER_FRONT_CENTER = ($4)
#SPEAKER_LOW_FREQUENCY = ($8)
#SPEAKER_BACK_LEFT = ($10)
#SPEAKER_BACK_RIGHT = ($20)
#SPEAKER_FRONT_LEFT_OF_CENTER = ($40)
#SPEAKER_FRONT_RIGHT_OF_CENTER = ($80)
#SPEAKER_BACK_CENTER = ($100)
#SPEAKER_SIDE_LEFT = ($200)
#SPEAKER_SIDE_RIGHT = ($400)
#SPEAKER_TOP_CENTER = ($800)
#SPEAKER_TOP_FRONT_LEFT = ($1000)
#SPEAKER_TOP_FRONT_CENTER = ($2000)
#SPEAKER_TOP_FRONT_RIGHT = ($4000)
#SPEAKER_TOP_BACK_LEFT = ($8000)
#SPEAKER_TOP_BACK_CENTER = ($10000)
#SPEAKER_TOP_BACK_RIGHT = ($20000)
#SPEAKER_MONO = (#SPEAKER_FRONT_CENTER)
#SPEAKER_STEREO = (#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT)
#SPEAKER_QUAD = (#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_BACK_LEFT | #SPEAKER_BACK_RIGHT)
#SPEAKER_SURROUND = (#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_FRONT_CENTER | #SPEAKER_BACK_CENTER)
#SPEAKER_5POINT1 = (#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_FRONT_CENTER | #SPEAKER_LOW_FREQUENCY | #SPEAKER_BACK_LEFT | #SPEAKER_BACK_RIGHT)
#SPEAKER_7POINT1 = (#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_FRONT_CENTER | #SPEAKER_LOW_FREQUENCY | #SPEAKER_BACK_LEFT | #SPEAKER_BACK_RIGHT | #SPEAKER_FRONT_LEFT_OF_CENTER | #SPEAKER_FRONT_RIGHT_OF_CENTER)
#SPEAKER_5POINT1_SURROUND = (#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_FRONT_CENTER | #SPEAKER_LOW_FREQUENCY | #SPEAKER_SIDE_LEFT | #SPEAKER_SIDE_RIGHT)
#SPEAKER_7POINT1_SURROUND = (#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_FRONT_CENTER | #SPEAKER_LOW_FREQUENCY | #SPEAKER_BACK_LEFT | #SPEAKER_BACK_RIGHT | #SPEAKER_SIDE_LEFT | #SPEAKER_SIDE_RIGHT)
#DS_OK = 0
#LOOP_FLAG = 0
#DSCBLOCK_ENTIREBUFFER = 1
Structure WAVEFORMATEXTENSIBLE
format.WAVEFORMATEX ;THE FAMILIAR WAVEFORMATEX STRUCTURE
wValidBitsPerSample.w
dwChannelMask.l
SubFormat.GUID
EndStructure
Global my_wfe.WAVEFORMATEXTENSIBLE
If #CHANNELS > 2
my_wfe\Format\wFormatTag = #WAVE_FORMAT_EXTENSIBLE
Else
my_wfe\Format\wFormatTag = #WAVE_FORMAT_PCM
EndIf
Global *bufptr ;POINTER TO CAPTURE BUFFER
Global bufsize.l ;SIZE OF CAPTURE BUFFER
; Base part:
my_wfe\Format\nChannels = #CHANNELS
my_wfe\Format\wBitsPerSample = #BIT_DEPTH
my_wfe\Format\nSamplesPerSec = #SAMPLE_RATE
my_wfe\Format\nBlockAlign = my_wfe\Format\nChannels * (my_wfe\Format\wBitsPerSample / 8)
my_wfe\Format\nAvgBytesPerSec = my_wfe\Format\nSamplesPerSec * my_wfe\Format\nBlockAlign
; Extensible part:
my_wfe\wValidBitsPerSample = #BIT_DEPTH ;All bits have Data
my_wfe\Format\cbSize = 22 ;Size of Extensible part in bytes( SubFormat + wValidBitsPerSample + dwChannelMask)
; Choose channel mask:
Select #CHANNELS ;my_wfe\Format\nChannels
Case 0
my_wfe\dwChannelMask = #SPEAKER_DIRECTOUT
Case 1
my_wfe\dwChannelMask = #SPEAKER_MONO
Case 2
my_wfe\dwChannelMask = #SPEAKER_STEREO
Case 3
my_wfe\dwChannelMask = #SPEAKER_STEREO | #SPEAKER_FRONT_CENTER
Case 4
my_wfe\dwChannelMask = #SPEAKER_QUAD
Case 5
my_wfe\dwChannelMask = #SPEAKER_QUAD | #SPEAKER_FRONT_CENTER
Case 6
my_wfe\dwChannelMask = #SPEAKER_5POINT1_SURROUND
Case 7
my_wfe\dwChannelMask = #SPEAKER_5POINT1_SURROUND | #SPEAKER_BACK_CENTER
Case 8
my_wfe\dwChannelMask = #SPEAKER_7POINT1_SURROUND
EndSelect
;GUID
my_wfe\SubFormat\Data1 = #WAVE_FORMAT_PCM
my_wfe\SubFormat\Data2 = $00
my_wfe\SubFormat\Data3 = $10
my_wfe\SubFormat\Data4[0] = $80
my_wfe\SubFormat\Data4[1] = $00
my_wfe\SubFormat\Data4[2] = $00
my_wfe\SubFormat\Data4[3] = $aa
my_wfe\SubFormat\Data4[4] = $00
my_wfe\SubFormat\Data4[5] = $38
my_wfe\SubFormat\Data4[6] = $9b
my_wfe\SubFormat\Data4[7] = $71
;DIRECTSOUND BUFFER
Structure DSCBUFFERDESC
dwSize.l ; Size of the structure, in bytes. This member must be initialized before the structure is used.
dwFlags.l ; Flags specifying the capabilities of the buffer
dwBufferBytes.l ; Size of capture buffer to create, in bytes.
dwReserved.l ; Must be 0
*lpwfxFormat ; Address of a WAVEFORMATEX or WAVEFORMATEXTENSIBLE structure specifying the waveform format for the buffer.
EndStructure
;SET UP CAPTURE BUFFER
dscbd.DSCBUFFERDESC
;dscbd\dwSize = SizeOf(DSCBUFFERDESC) ; Save structure size
dscbd\dwSize = SizeOf(dscbd) ; Save structure size
dscbd\dwFlags = 0 ; It is the primary Buffer (see DSound.h)
dscbd\dwBufferBytes = my_wfe\format\nAvgBytesPerSec * #SECONDS
dscbd\dwReserved = 0
dscbd\lpwfxFormat = @my_WFE
Procedure Delete(*obj.IUnknown)
ProcedureReturn *Obj\Release()
EndProcedure
Procedure Error_Msg(String.s)
MessageRequester("Error",String.s,0)
End
EndProcedure
Procedure File_Save()
;SAVE BUFFER AS A WAV FILE
;CAUTION: THERE MAY BE SOME BUGS IN THIS PART
If CreateFile(1, "audio\test.wav") = 0
MessageRequester("Error", "Unable to create file.", #MB_ICONERROR)
End
ProcedureReturn
EndIf
subchunk1size.l = SizeOf(WAVEFORMATEX)
subchunk2size.l = bufsize
chunksize = 4 + (8 + SizeOf(WAVEFORMATEX)) + (8 + subchunk2size)
samprate.l = my_wfe\format\nSamplesPerSec
byterate.l = my_wfe\format\nSamplesPerSec * my_wfe\format\nAvgBytesPerSec ;IS THIS RIGHT?
blockalign.w = my_wfe\format\nChannels * (my_wfe\format\wBitsPerSample / 8)
bitspersample.w = my_wfe\format\wBitsPerSample
;my_wfe\format\cbSize = 0
chunksize = chunksize + my_wfe\format\cbSize
;my_wfe\format\wFormatTag = #WAVE_FORMAT_PCM
;WRITE WAV HEADER
WriteString(1, "RIFF") ; 4 bytes
WriteLong(1, chunksize) ; 4 bytes
WriteString(1, "WAVE") ; 4 bytes
WriteString(1, "fmt ") ; 4 bytes
WriteLong(1, subchunk1size) ; 4 bytes
WriteData(1, my_WFE, SizeOf(WAVEFORMATEX))
;END OF WAVEFORMATEX STRUCTURE
WriteString(1, "data", #PB_Ascii) ; 4 bytes
WriteLong(1, subchunk2size) ; 4 bytes
;END OF FILE HEADER
;WRITE AUDIO DATA AFTER WAV HEADER
WriteData(1, *bufptr, bufsize)
CloseFile(1)
EndProcedure
;CREATE DIRECTSOUND CAPTURE OBJECT
*DirectSound.IDirectSoundCapture
result = DirectSoundCaptureCreate_(0, @*DirectSound, 0)
If Result <> #DS_OK
Error_Msg("Can't do DirectSoundCaptureCreate: " + Right(Hex(Result), 8))
EndIf
result = *DirectSound\CreateCaptureBuffer(@dscbd, @*pDSCB.IDirectSoundCaptureBuffer, 0)
If result <> #DS_OK
Error_Msg("Can't set up DirectSound buffer: " + Right(Hex(Result), 8))
EndIf
;START RECORDING
result = *pDSCB.IDirectSoundCaptureBuffer\Start(#LOOP_FLAG)
If result <> #DS_OK
Error_Msg("Can't start : " + Str(Result))
Else
PrintN("Recording started")
EndIf
;RECORD FOR SPECIFIED NUMBER OF SECONDS
Delay(#SECONDS * 1000)
;STOP RECORDING
result = *pDSCB.IDirectSoundCaptureBuffer\Stop()
If result <> #DS_OK
Error_Msg("Can't stop : " + Str(Result))
Else
PrintN("Recording stopped")
EndIf
;LOCK THE BUFFER BEFORE SAVING
result = *pDSCB.IDirectSoundCaptureBuffer\Lock(0, my_wfe\format\nAvgBytesPerSec * #SECONDS, @*bufptr, @bufsize, 0, 0, #DSCBLOCK_ENTIREBUFFER)
If result <> #DS_OK
Error_Msg("Can't lock : " + Str(Result))
EndIf
;SAVE BUFFER CONTENTS AS A WAV FILE
PrintN("Saving")
File_Save()
;UNLOCK THE BUFFER
result = *pDSCB.IDirectSoundCaptureBuffer\Unlock(*bufptr, bufsize, 0, 0)
If result <> #DS_OK
Error_Msg("Can't unlock : " + Str(Result))
EndIf
;PrintN("Normal program termination")
CloseConsole()
End