DirectSound Capture Question

Windows specific forum
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

DirectSound Capture Question

Post by chris319 »

Below is a simple program which records two seconds of sound from the default sound device using DirectX. On lines 45 - 46, the program works fine if wFormatTag = #WAVE_FORMAT_PCM, but I get an error if I set wFormatTag = $FFFE which is the same as #WAVE_FORMAT_EXTENSIBLE. I am trying to capture more than 2 audio channels with this and as I understand it, it is necessary for wFormatTag to equal $FFFE.

Code: Select all

;DIRECTSOUND RECORD PROGRAM 
;chris319 
;5/25/2008
;UPDATED ON 9/29/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 HERE: 

OpenConsole()

#CHANNELS               = 2
#SAMPLE_RATE            = 44100
#BIT_DEPTH              = 16 
#SECONDS = 2
#WAVE_FORMAT_PCM        = $0001 
#WAVE_FORMAT_EXTENSIBLE = $FFFE

#SPEAKER_FRONT_LEFT     = 1
#SPEAKER_FRONT_RIGHT    = 2
#SPEAKER_FRONT_CENTER   = 4
#SPEAKER_BACK_LEFT      = $10
#SPEAKER_BACK_RIGHT     = $20

KSDATAFORMAT_SUBTYPE_PCM$ = Chr($1)+Chr($0)+Chr($10)+Chr($80)+Chr($0)+Chr($0)+Chr($aa)+Chr($0)+Chr($38)+Chr($9b)+Chr($71)

#DS_OK = 0 
#LOOP_FLAG = 0 
#DSCBLOCK_ENTIREBUFFER = 1 

Structure WAVEFORMATEXTENSIBLE
  format.WAVEFORMATEX ; Capturing WaveFormatEx
  wValidBitsPerSample.w
  dwChannelMask.l
  SubFormat$; .w ;GUID
EndStructure

Global my_wfe.WAVEFORMATEXTENSIBLE ;THE FAMILIAR WAVEFORMATEX STRUCTURE 

Global *bufptr ;POINTER TO CAPTURE BUFFER 
Global bufsize.l ;SIZE OF CAPTURE BUFFER 


my_wfe\format\wFormatTag      = #WAVE_FORMAT_PCM 
;my_wfe\Format\wFormatTag = $FFFE ;#WAVE_FORMAT_EXTENSIBLE;

my_wfe\Format\nChannels = 2
my_wfe\Format\nSamplesPerSec = 44100
my_wfe\Format\nAvgBytesPerSec = 352800 / 2
;my_wfe\Format\nBlockAlign = 8 ;  /* Same as the usual */
;my_wfe\format\nAvgBytesPerSec = (my_wfe\format\nSamplesPerSec * my_wfe\format\nBlockAlign) 
my_wfe\Format\wBitsPerSample = 16
my_wfe\Format\cbSize = 22;  /* After this to GUID */
my_wfe\wValidBitsPerSample = 16;  /* All bits have data */
my_wfe\dwChannelMask = #SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_BACK_LEFT | #SPEAKER_BACK_RIGHT
                             ;// Quadraphonic = 0x00000033
my_wfe\SubFormat$ = KSDATAFORMAT_SUBTYPE_PCM$  ;Specify PCM

my_wfe\format\nBlockAlign     = (my_wfe\format\wBitsPerSample / 8 * my_wfe\format\nChannels)
my_wfe\format\nAvgBytesPerSec = (my_wfe\format\nSamplesPerSec * my_wfe\format\nBlockAlign)


;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 


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 

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 

Procedure Enumerate()
result.l = DirectSoundCaptureEnumerate_(0, 0) 
EndProcedure


;CREATE DIRECTSOUND CAPTURE OBJECT 
*DirectSound.IDirectSoundCapture 
result.l = DirectSoundCaptureCreate_(0, @*DirectSound, 0) 
If Result <> #DS_OK 
 Error_Msg("Can't do DirectSoundCaptureCreate : " + Str(Result.l)) 
EndIf 

;SET UP CAPTURE BUFFER 
dscbd.DSCBUFFERDESC                         ; Set up structure 
dscbd\dwSize        = SizeOf(DSCBUFFERDESC) ; 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 
dscbd\lpwfxFormat   = @my_WFE\format

  
result = *DirectSound\CreateCaptureBuffer(@dscbd, @*pDSCB.IDirectSoundCaptureBuffer, 0) 
If result <> #DS_OK 
 Error_Msg("Can't set up directsound buffer : " + Str(Result)) 
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
chris319
Enthusiast
Enthusiast
Posts: 782
Joined: Mon Oct 24, 2005 1:05 pm

Re: DirectSound Capture Question

Post by chris319 »

I got the code to work using PB's built-in GUID structure. Getting the GUID right was the tricky part.

The revised program has been posted in Tricks 'n Tips. CAUTION: the file save routine may yet have some bugs.
Post Reply