Page 1 of 1

Sound Effects with DirectX 8

Posted: Mon Aug 02, 2004 9:44 am
by zapman*
Code updated for 5.20+

Code: Select all

;
; Example of playing a sound buffer with DirectX 8
; (Danilo and www.MasterCreating.de)
; and apply DirectX effects on it.
; (Zapman)
;
#DD_OK = 0
#DS_OK = 0
#DSBPLAY_DEFAULT = $0
#DSBPLAY_LOOPING = $1
#DSBLOCK_ENTIREBUFFER    = 2

#DSBCAPS_LOCSOFTWARE   = $8
#DSBCAPS_CTRLFREQUENCY = $20
#DSBCAPS_CTRLVOLUME    = $80
#DSBCAPS_CTRLPAN       = $40
#DSFXECHO_WETDRYMIX_MIN =0
#DSFXECHO_WETDRYMIX_MAX =100
#DSFXECHO_FEEDBACK_MIN  =0
#DSFXECHO_FEEDBACK_MAX  =100
#DSFXECHO_LEFTDELAY_MIN =1
#DSFXECHO_LEFTDELAY_MAX =2000
#DSFXECHO_RIGHTDELAY_MIN=1
#DSFXECHO_RIGHTDELAY_MAX=2000
#DSFXECHO_PANDELAY_MIN  =0
#DSFXECHO_PANDELAY_MAX  =1
GUID_DSFX_STANDARD_ECHO.GUID
GUID_DSFX_STANDARD_ECHO\Data1    = $ef3e932c
GUID_DSFX_STANDARD_ECHO\Data2    = $d40b
GUID_DSFX_STANDARD_ECHO\Data3    = $4f51
GUID_DSFX_STANDARD_ECHO\Data4[0] = $8c
GUID_DSFX_STANDARD_ECHO\Data4[1] = $cf
GUID_DSFX_STANDARD_ECHO\Data4[2] = $3f
GUID_DSFX_STANDARD_ECHO\Data4[3] = $98
GUID_DSFX_STANDARD_ECHO\Data4[4] = $f1
GUID_DSFX_STANDARD_ECHO\Data4[5] = $b2
GUID_DSFX_STANDARD_ECHO\Data4[6] = $9d
GUID_DSFX_STANDARD_ECHO\Data4[7] = $5d
IID_IDirectSoundFXEcho.GUID
IID_IDirectSoundFXEcho\Data1   = $8bd28edf
IID_IDirectSoundFXEcho\Data2   = $50db
IID_IDirectSoundFXEcho\Data3   = $4e92
IID_IDirectSoundFXEcho\Data4[0]= $a2
IID_IDirectSoundFXEcho\Data4[1]= $bd
IID_IDirectSoundFXEcho\Data4[2]= $44
IID_IDirectSoundFXEcho\Data4[3]= $54
IID_IDirectSoundFXEcho\Data4[4]= $88
IID_IDirectSoundFXEcho\Data4[5]= $d1
IID_IDirectSoundFXEcho\Data4[6]= $ed
IID_IDirectSoundFXEcho\Data4[7]= $42
#DSBCAPS_CTRLFX = $00000200

Structure DSBUFFERDESC
  dwSize.l            ; Size of the Structure
  dwFlags.l           ; Flags specifying the capabilities of the buffer
  dwBufferBytes.l     ; Size of the new buffer, in bytes. This value must be 0 when creating a buffer with the DSBCAPS_PRIMARYBUFFER flag. For secondary buffers, the minimum and maximum sizes allowed are specified by DSBSIZE_MIN and DSBSIZE_MAX, defined in Dsound.h.
  dwReserved.l        ; Must be 0
  *lpwfxFormat        ; Address of a WAVEFORMATEX or WAVEFORMATEXTENSIBLE structure specifying the waveform format for the buffer.
  guid3DAlgorithm.GUID; Unique identifier of the two-speaker virtualization algorithm to be used by DirectSound3D hardware emulation. If DSBCAPS_CTRL3D is not set in dwFlags, this member must be GUID_NULL (DS3DALG_DEFAULT).
EndStructure

Structure DSEFFECTDESC
  dwSize.l            ; Size of the Structure
  dwFlags.l           ; Can be 0 or one of the following values: DSFX_LOCHARDWARE (Effect must be in hardware) DSFX_LOCSOFTWARE (Effect must be in software)
  guidDSFXClass.GUID  ; Class identifier of the effect. The following standard effect classes are defined :
                      ; GUID_DSFX_STANDARD_CHORUS
                      ; GUID_DSFX_STANDARD_COMPRESSOR
                      ; GUID_DSFX_STANDARD_DISTORTION
                      ; GUID_DSFX_STANDARD_ECHO
                      ; GUID_DSFX_STANDARD_FLANGER
                      ; GUID_DSFX_STANDARD_GARGLE
                      ; GUID_DSFX_STANDARD_I3DL2REVERB
                      ; GUID_DSFX_STANDARD_PARAMEQ
                      ; GUID_DSFX_STANDARD_WAVES_REVERB
  dwReserved1.i
  dwReserved2.i
EndStructure


Structure DSFXEcho
    fWetDryMix.f
    fFeedback.f
    fLeftDelay.f
    fRightDelay.f
    lPanDelay.l
EndStructure

Procedure Delete(*obj.IUnknown)
  ProcedureReturn *Obj\Release()
EndProcedure

Procedure.s DecodeError (errorNb )
; This fonction retrieve the text corresponding to errorNb
; Cette fonction récupère le text correspondant à ErrorNb
  If errorNb
    *memAdr=AllocateMemory (255)
    length = FormatMessage_ (#FORMAT_MESSAGE_FROM_SYSTEM, #Null, errorNb, 0, *memAdr, 255, #Null)
    If length > 1
        e$ = PeekS (*memAdr, length - 2)
    Else
      Select ErrorNb
       Case #E_INVALIDARG
        e$ = "INVALIDARG"
       Case #E_FAIL
        e$ = "FAIL"
       Case #DS_OK
        e$ = "OK"
       Case #E_OUTOFMEMORY
        e$ = "OUTOFMEMORY"
       Case #E_NOTIMPL
        e$ = "NOTIMPL"
       Case #E_NOINTERFACE
        e$ = "NOINTERFACE"
       Case $88780046 ; DSERR_PRIOLEVELNEEDED
        e$ = "PRIOLEVELNEEDED"
       Case $88780064 ; DSERR_BADFORMAT
        e$ = "BADFORMAT"
       Case $88780032 ; DSERR_INVALIDCALL
        e$ =  "INVALIDCALL"
       Case $8878001E ; DSERR_CONTROLUNAVAIL
        e$ = "CONTROLUNAVAIL"
       Case $8878000A ; DSERR_ALLOCATED
        e$ = "ALLOCATED"
       Case $88781161 ; DSERR_OBJECTNOTFOUND
        e$ = "OBJECTNOTFOUND"
       Default
        e$ = "Erreur N° / Error N# "+Str(errorNb)
      EndSelect
    EndIf
    FreeMemory (*memAdr)
    ProcedureReturn e$
  Else
    ProcedureReturn ""
  EndIf
EndProcedure

Procedure.s TextError ()
; When an error occure, this fonction retrieve the text of it
; Quand une erreur survient, cette fonction récupère le text correspondant
  errorNb = GetLastError_ ()
  ProcedureReturn DecodeError (errorNb)
EndProcedure

Procedure ShowError()
  Err$=TextError()
  If Err$<>""
    MessageRequester("Erreur / Error", Err$, 0)
  EndIf
EndProcedure

Procedure Error_Msg(String.s)
  MessageRequester("Error",String.s,0)
  End
EndProcedure

Procedure lpDSEnumCallback(lpGuid,lpcstrDescription.s,lpcstrModule.s,lpContext) 
  Debug lpcstrDescription
  Debug lpcstrModule
  ProcedureReturn #True
EndProcedure

DirectSoundCaptureEnumerate_(@lpDSEnumCallback(),0)



hwnd = OpenWindow(0,0,0,200,200,"Sound",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  TextGadget    (1,10,10,180,15,"Echo Wet/Dry: 100")
  TrackBarGadget(2,10,25,180,20,0,100)
  SetGadgetState(2,100)
  TextGadget    (3,10,50,180,15,"Pan: 0")
  TrackBarGadget(4,10,65,180,20,0,200)
  SetGadgetState(4,100)
  TextGadget    (5,10,90,180,15,"Volume: 10000")
  TrackBarGadget(6,10,105,180,20,0,10000)
  SetGadgetState(6,10000)


Result = OpenLibrary(0,"DSOUND.DLL")
If result = 0
  Error_Msg("Can't open Direct Sound Library : "+TextError())
EndIf

;*RA_DirectSound2.IDropSourceVtb
*RA_DirectSound.IDirectSound8

; create DS8 Sound Object
Result = CallFunction(0,"DirectSoundCreate8",0,@*RA_DirectSound,0)
If Result <> #DD_OK
  Error_Msg("Can't do DirectSoundCreate8 : " + DecodeError(Result))
EndIf

; Set Coop Level
Result = *RA_DirectSound\SetCooperativeLevel(hwnd,1)
If Result <> #DD_OK
  Error_Msg("Can't Set Coop Level : " + DecodeError(Result))
EndIf


; Setting up Primary Buffer
dsbd.DSBUFFERDESC                          ; Set up structure
dsbd\dwSize        = SizeOf(DSBUFFERDESC)  ; Save structure size
dsbd\dwFlags       = 1                     ; It is the primary Buffer (see DSound.h)
dsbd\dwBufferBytes = 0                     ; NULL ? Because primary Buffer must be Null
dsbd\lpwfxFormat   = 0                     ; NULL ? ? ? <- it's a Pointer

Result = *RA_DirectSound\CreateSoundBuffer(@dsbd,@*pDSBPrimary.IDirectSoundBuffer,0)
If Result <> #DD_OK
  Error_Msg("Can't Set up primary sound buffer : " + DecodeError(Result))
EndIf

#channels = 2

wfx.WAVEFORMATEX                ; Wave Format Structure
  RtlZeroMemory_(@wfx,SizeOf(WAVEFORMATEX));
  wfx\wFormatTag      = #WAVE_FORMAT_PCM;
  wfx\nChannels       = #channels ;dwPrimaryChannels;
  wfx\nSamplesPerSec  = 44100     ;dwPrimaryFreq;
  wfx\wBitsPerSample  = 16        ;dwPrimaryBitRate;
  wfx\nBlockAlign     = (wfx\wBitsPerSample / 8 * wfx\nChannels)
  wfx\nAvgBytesPerSec = (wfx\nSamplesPerSec * wfx\nBlockAlign)

; secondary Buffer (see DSound.h)
dsbd\dwFlags       = #DSBCAPS_LOCSOFTWARE|#DSBCAPS_CTRLVOLUME|#DSBCAPS_CTRLPAN |#DSBCAPS_CTRLFX ; |#DSBCAPS_CTRLFREQUENCY
dsbd\dwBufferBytes = 4 * wfx\nAvgBytesPerSec ; alloc 3 Seconds
dsbd\lpwfxFormat   = @wfx

; CREATE Secondary Buffer
Result = *RA_DirectSound\CreateSoundBuffer(@dsbd,@*pDSB.IDirectSoundBuffer,0)
If Result <> #DD_OK
  Error_Msg("Can't Set up secondary sound buffer : " + DecodeError(Result))
EndIf

; ASK for DirectSoundBuffer8 Interface
*DSB8.IDirectSoundBuffer8 = 0
*pDSB\QueryInterface(?IID_DirectSoundBuffer8,@*DSB8)

Delete(*pDSB)
If *DSB8 = 0
  Error_Msg("Can't get DirectSoundBuffer8 Interface")
EndIf

#DSBLOCK_ENTIREBUFFER = $2
If *DSB8\Lock(0,0,@lpvWrite,@dwLength,0,0,#DSBLOCK_ENTIREBUFFER) = #DS_OK

  ; OK, now copy data in buffer...

  Structure SOUND_BUFFER ; channels, each 16 bit
    channel.w[#channels]
  EndStructure

  *Buffer.SOUND_BUFFER = lpvWrite


  ; GENERATE SOUND DATA ;)
  l = dwLength/SizeOf(SOUND_BUFFER)
  s = 1
  s2 = 1
  s3.f = 200
        For a = 0 To l
            If b < -50000
              s = 1
            EndIf
            If b > 50000
              s = -1
            EndIf
            If c < -40000
              s2 = 1
            EndIf
            If c > 40000
              s2 = -1
            EndIf
            s3 +1/20
            If s3>4000
              s3 = 200
            EndIf
              b + s*s3
              c + (s2*500) + (s3/20)

            *Buffer\channel[0] = b
            *Buffer\channel[1] = c
            *Buffer + SizeOf(SOUND_BUFFER)
        Next a
  ; GENERATE SOUND END


  *DSB8\UnLock(lpvWrite,dwLength,0,0)

  ;- Echo
 
  Echo.DSFXEcho
  Echo\fWetDryMix = #DSFXECHO_WETDRYMIX_MAX
  Echo\fFeedback  = 40
  Echo\fLeftDelay = 333
  Echo\fRightDelay= 333
  Echo\lPanDelay  = 0
 
  CoInitialize_(0)
  pDSFXDesc.DSEFFECTDESC                            ; Set up structure
  pDSFXDesc\dwSize        = SizeOf(DSEFFECTDESC)    ; Save structure size
  pDSFXDesc\guidDSFXClass\Data1 = GUID_DSFX_STANDARD_ECHO\Data1 ; Set Echo Effect
  pDSFXDesc\guidDSFXClass\Data2 = GUID_DSFX_STANDARD_ECHO\Data2
  pDSFXDesc\guidDSFXClass\Data3 = GUID_DSFX_STANDARD_ECHO\Data3
  For i = 0 To 7
    pDSFXDesc\guidDSFXClass\Data4[i] = GUID_DSFX_STANDARD_ECHO\Data4[i]
  Next
   


  Result = *DSB8\SetFX(1, @pDSFXDesc ,@pdwResultCodes.l)
  If Result <> #DD_OK
    Error_Msg("Can't Set effect : " + DecodeError(Result))
  EndIf

  dwIndex.l = 0
  *ppObject.IDirectSoundFXEcho = 0
  Result  = *DSB8\GetObjectInPath(GUID_DSFX_STANDARD_ECHO,dwIndex, IID_IDirectSoundFXEcho,@*ppObject)
  If Result
    Error_Msg("Can't GetObjectInPath : "+DecodeError(Result))
  Else
    Result   = *ppObject\SetAllParameters(@Echo)
    If Result   
      Error_Msg("Can't SetAllParameters : "+DecodeError(Result))
    EndIf
  EndIf
 ; MemoryID = AllocateMemory(0,dwLength,0)

 
  *DSB8\Play(0,0,#DSBPLAY_LOOPING);#DSBPLAY_DEFAULT);

    mlpvWrite = lpvWrite
  result = *pDSBPrimary\Lock(0,0,@lpvWrite,@dwLength,0,0,#DSBLOCK_ENTIREBUFFER)
  If result = #DS_OK
    For i = 1 To 50
      a = PeekL(@mlpvWrite+i)-PeekL(@lpvWrite+i)
      Debug "*"+Str(PeekL(@mlpvWrite+i))+" - "+Str(PeekL(@lpvWrite+i))
      Debug a
    Next
    Debug "********************"

    *pDSBPrimary\UnLock(lpvWrite,dwLength,0,0)
  EndIf
   

EndIf

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Quit = 1
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 2 ; Frequency Control
          vEcho = GetGadgetState(2)
          SetGadgetText(1,"Echo Wet/Dry : "+Str(vEcho))
          ;*DSB8\SetFrequency(Frequ*100)
            Echo\fWetDryMix  = vEcho
            *ppObject\SetAllParameters(@Echo)
           
        Case 4 ; Pan: Left <> Right
          Pan = GetGadgetState(4)*100-10000
          SetGadgetText(3,"Pan: "+Str(Pan))
          *DSB8\SetPan(Pan)
        Case 6 ; Volume
          Vol = GetGadgetState(6)
          SetGadgetText(5,"Volume: "+Str(Vol))
          *DSB8\SetVolume(Vol-10000)
      EndSelect
  EndSelect
Until Quit

*DSB8\Stop()

; Release/Delete Objects
; (reversed order of creation)
Delete(*DSB8)
Delete(*pDSBPrimary)
Delete(*RA_DirectSound)

End

DataSection
  IID_DirectSoundBuffer8:  ; DSOUND.h
    Data.l $6825A449
    Data.w $7524,$4D82
    Data.b $92,$0F,$50,$E3,$6A,$B3,$AB,$1E
EndDataSection 

Posted: Thu Feb 17, 2005 11:46 pm
by kawasaki
Very nice :)