Page 1 of 2

DirectSound functions

Posted: Wed Mar 29, 2006 10:41 pm
by Hroudtwolf
A few of DirectSound functions.
I wrote it while my DSound practice.

Have fun with it. :)

Code: Select all

; 2006 Hroudtwolf
; PureBasic-Lounge.de
; PlayDXSound
; tesed with PB 4.00

Procedure InitDXSound ()
  Shared IS_DXSound_Init.l
  If IS_DXSound_Init.l=0
     IS_DXSound_Init.l=#True
     Structure DSBCAPS
       dwSize.l
       dwFlags.l
       dwBufferBytes.l
       dwUnlockTransferRate.l
       dwPlayCpuOverhead.l
     EndStructure 
     If InitSound ()=0
        ProcedureReturn #False
     EndIf 
     ProcedureReturn #True
     Else
     ProcedureReturn #False
  EndIf 
EndProcedure 




Procedure PlayDXSound (SoundID.l,Volume.l,LoopFlag.l)
  Protected PlayState.l,*SoundObject,Samplefreq.l,OrigFreqency.l
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    *DirectSoundBuffer\GetStatus(@PlayState)
    If PlayState.l=1 Or PlayState.l=5
       *DirectSoundBuffer\Stop()
       *DirectSoundBuffer\SetCurrentPosition(0)
    EndIf
    *DirectSoundBuffer\SetVolume (-1000+(Volume.l*10))
    *DirectSoundBuffer\Play (0,0,LoopFlag.l)
  ProcedureReturn #True
  Else
  ProcedureReturn #False
  EndIf
EndProcedure




Procedure StopDXSound (SoundID.l)
  Protected *SoundObject,PlayState.l
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    *DirectSoundBuffer\GetStatus(@PlayState)
    If PlayState.l=1 Or PlayState.l=5
       *DirectSoundBuffer\Stop()
       *DirectSoundBuffer\SetCurrentPosition(0)
       ProcedureReturn #True
       Else
       ProcedureReturn #False
    EndIf  
  EndIf
EndProcedure 




Procedure GetDXSoundPosition(SoundID.l)
  Protected *SoundObject,DXSndPosition.l
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    *DirectSoundBuffer\GetCurrentPosition(@DXSndPosition,0)
    ProcedureReturn DXSndPosition.l
    Else
    ProcedureReturn -1
  EndIf 
EndProcedure




Procedure SetDXSoundPosition(SoundID.l,DXSndPosition.l)
  Protected *SoundObject
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    *DirectSoundBuffer\SetCurrentPosition(DXSndPosition.l)
    ProcedureReturn #True
    Else
    ProcedureReturn #False
  EndIf 
EndProcedure 




Procedure GetDXSoundFrequency(SoundID.l)
  Protected *SoundObject,Frequency.l
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    *DirectSoundBuffer\GetFrequency(@Frequency)
    ProcedureReturn Frequency.l
    Else
    ProcedureReturn -1
  EndIf 
EndProcedure 




Procedure GetDXSoundBufferSize(SoundID.l)
  Protected *SoundObject
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    DXSBCaps.DSBCAPS\dwSize=SizeOf(DSBCAPS)
    *DirectSoundBuffer\GetCaps(@DXSBCaps)
    ProcedureReturn DXSBCaps\dwBufferBytes
    Else 
    ProcedureReturn -1
  EndIf
EndProcedure




Procedure GetDXSoundState (SoundID.l)
  Protected PlayState.l,*SoundObject
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    *DirectSoundBuffer\GetStatus(@PlayState)
    If PlayState.l=1 Or PlayState.l=5
       ProcedureReturn #True
    Else
       ProcedureReturn #False
    EndIf 
    Else
    ProcedureReturn #False
  EndIf
EndProcedure




Procedure SetDXSoundVolume (SoundID.l,Volume.l)
  Protected *SoundObject,CurrentVolume.l
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    *DirectSoundBuffer\SetVolume (-1000+(Volume.l*10))
    *DirectSoundBuffer\GetVolume (@CurrentVolume)
    ProcedureReturn CurrentVolume.l
    Else
    ProcedureReturn -1
  EndIf 
EndProcedure 




Procedure GetDXSoundVolume (SoundID.l)
  Protected *SoundObject,CurrentVolume.l
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    *DirectSoundBuffer\GetVolume (@CurrentVolume)
    ProcedureReturn CurrentVolume.l
    Else
    ProcedureReturn -1
  EndIf 
EndProcedure 




Procedure GetDXSoundPan (SoundID.l)
  Protected *SoundObject,CurrentPan.l
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    *DirectSoundBuffer\GetPan (@CurrentPan)
    ProcedureReturn CurrentPan.l
    Else
    ProcedureReturn -1
  EndIf 
EndProcedure 




Procedure SetDXSoundPan (SoundID.l,SoundPan.l)
  Protected *SoundObject,CurrentPan.l
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
     *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
     *DirectSoundBuffer\SetPan (SoundPan.l)
     *DirectSoundBuffer\GetPan (@CurrentPan)
    ProcedureReturn CurrentPan.l
    Else
    ProcedureReturn -1
  EndIf 
EndProcedure 



Procedure LoadDXSound (SoundID.l,FileName.s)
  FileID.l=ReadFile (#PB_Any,FileName.s)
  If FileID.l And SoundID.l
    *PreBuffer=AllocateMemory (Lof(FileID.l))
    ReadData(FileID.l,*PreBuffer,Lof(FileID.l))
    CatchSound (SoundID.l,*PreBuffer,Lof(FileID.l))
    CloseFile (FileID.l)
    FreeMemory (*PreBuffer)
    ProcedureReturn #True
    Else
    ProcedureReturn #False
  EndIf 
EndProcedure 

Posted: Wed Mar 29, 2006 11:04 pm
by ts-soft
this can't correct :wink:

Code: Select all

Procedure GetDXSoundPosition(SoundID.l)
  Protected *SoundObject,DXSndPosition.l
  *SoundObject=IsSound (SoundID.l)
  If *SoundObject
    *DirectSoundBuffer.IDirectSoundBuffer=PeekL(*SoundObject)
    *DirectSoundBuffer\GetCurrentPosition(@Position,0)
    ProcedureReturn DXSndPosition.l
    Else
    ProcedureReturn -1
  EndIf
EndProcedure
ProcedureReturn Position ?

Posted: Wed Mar 29, 2006 11:24 pm
by inc.
Is Streaming audio also possible? Means that not the whole wave data is read into memory at once, which could be a lot in case of a Movie Audiotrack of i.E. 2Gb.

Posted: Wed Mar 29, 2006 11:52 pm
by Hroudtwolf
@TS-Soft

Thanks.
This was the remnant of my first uncleaned try.

@Inc

Sorry, I don't know.
Thats my first steps with DirectSound.

But I think, you could copy a big databuffer partial to a soundbuffer.

Posted: Thu Mar 30, 2006 1:48 pm
by va!n
nice work and thanks for sharing the source ;)

Posted: Thu Mar 30, 2006 8:55 pm
by tomijan
Hi all,
about streaming play with DirectSound look bellow, please:
this demo play few seconds of WAVE file

Code: Select all

;--------------------------------------------------------------
;-  DirectSound Streaming Play
;   Tomijan 2006 
;   PB4 
;
;  SORRY - no comments, no cleanup, no other DXSound functions
;  This piece of code  IS NOT REALLY PROGRAM
;  This is DEMO OF STREAMING - LOOP ONLY !!!
;  and should be Inspiration to serious program (I hope)
;--------------------------------------------------------------
;
#NUM_PLAY_NOTIFICATIONS  = 2
#Duration   = 1 ;sek
;
#DSBCAPS_LOCSOFTWARE   = $8 
#DSBCAPS_CTRLFREQUENCY = $20 
#DSBCAPS_CTRLVOLUME    = $80 
#DSBCAPS_CTRLPAN       = $40
#DSBCAPS_CTRLPOSITIONNOTIFY = $00000100
#DSBCAPS_GETCURRENTPOSITION2 =$00010000
#DSBCAPS_GLOBALFOCUS = $00008000
;
#DSSCL_EXCLUSIVE = 3  
#DSSCL_NORMAL = 1     
#DSSCL_PRIORITY = 2   
#DSSCL_WRITEPRIMARY = 4 

#DD_OK = 0 
#DS_OK = 0 
;
#DSBPLAY_LOOPING         = $1
#DSBLOCK_FROMWRITECURSOR = $1
#DSBLOCK_ENTIREBUFFER    = $2
;
Structure WAVEFORMATEX Extends WAVEFORMAT
  cbSize.w   
EndStructure 

Structure DSBUFFERDESC 
  dwSize.l            
  dwFlags.l           
  dwBufferBytes.l     
  dwReserved.l        
  *lpwfxFormat        
EndStructure 
;
Structure DSBPOSITIONNOTIFY
  dwOffset.l;
  hEventNotify.l;
EndStructure 
;
Structure DIRECTSOUND
  RA_DirectSound.l
  Sound.l
  Notify.l
  secBuflen.l
  nChannels.l
EndStructure
;
;
;- Globals 
Global hWndM, hEvent
Global *mem
 
Global Dim Notifications.DSBPOSITIONNOTIFY(#NUM_PLAY_NOTIFICATIONS-1) 
Global DX8Sound.DIRECTSOUND

Procedure Delete(*obj.IUnknown) 
  ProcedureReturn *Obj\Release() 
EndProcedure 
;
Procedure PrepareDX8Sound (*pWfx.WAVEFORMATEX)
  If OpenLibrary(0,"DSOUND.DLL")
  result.l = CallFunction(0,"DirectSoundCreate8",0,@*RA_DirectSound.IDirectSound8,0)
  EndIf
  If result = #Null
     result = *RA_DirectSound\SetCooperativeLevel(hWndM,#DSSCL_NORMAL) ; hwnd is the WindowID
      If result.l <> #DD_OK 
          Delete(*RA_DirectSound)
        Else 
    
        dsbd.DSBUFFERDESC
        dsbd\dwSize        = SizeOf(DSBUFFERDESC)
    
        dsbd\dwFlags       = #DSBCAPS_LOCSOFTWARE|#DSBCAPS_CTRLVOLUME|#DSBCAPS_CTRLPOSITIONNOTIFY|#DSBCAPS_GLOBALFOCUS | #DSBCAPS_GETCURRENTPOSITION2 
        dsbd\dwBufferBytes = *pWfx\nAvgBytesPerSec * #Duration 
        dsbd\lpwfxFormat   = *pWfx 
        ;
        nBlockAlign    = *pWfx\nBlockAlign;
        nSamplesPerSec = *pWfx\nSamplesPerSec;
        dwNotifySize   = nSamplesPerSec * #Duration * nBlockAlign / #NUM_PLAY_NOTIFICATIONS;
        dwNotifySize   = dwNotifySize - (dwNotifySize % nBlockAlign)
    
        hEvent = CreateEvent_(0,#True,0,0)
;           ......        
            For i = 0 To #NUM_PLAY_NOTIFICATIONS-1    
              Notifications(i)\dwOffset = dwNotifySize*(i)
              Notifications(i)\hEventNotify = hEvent
            Next
        
        result.l = *RA_DirectSound\CreateSoundBuffer(@dsbd,@*pDSB.IDirectSoundBuffer,0) 
        If result.l <> #DD_OK
          Delete(*RA_DirectSound)
          Fresult = #Null
        Else 
        ;
            *DSB8.IDirectSoundBuffer8 = 0 
            *pDSB\QueryInterface(?IID_DirectSoundBuffer8,@*DSB8) 
            
            *DSN8.IDirectSoundNotify = 0 
            *pDSB\QueryInterface(?IID_DirectSoundNotify8,@*DSN8)     
            Delete(*pDSB) 
    
              If *DSB8 = 0 
                Delete(*RA_DirectSound)
              Else 
                ;          
                NotifyRes = *DSN8\SetNotificationPositions(#NUM_PLAY_NOTIFICATIONS,@notifications(0))
                ;
                DX8Sound\secBuflen   = #Duration * *pWfx\nAvgBytesPerSec
                DX8Sound\Sound       = *DSB8
                DX8Sound\Notify      = *DSN8
                DX8Sound\RA_DirectSound = *RA_DirectSound
                DX8Sound\nChannels = *pWfx\nChannels
                FResult = *DSB8
              EndIf
      EndIf
    EndIf
 Else
   MessageRequester("Error","Please install DirectX v.8 or above",0)
   End
 EndIf
  If IsLibrary(0)
    CloseLibrary(0)
  EndIf
 ProcedureReturn FResult
EndProcedure
;
Procedure DXSound() ; ******************************************
Protected *pWfx.WAVEFORMATEX
   *pWfx = *mem+20
   result = PrepareDX8Sound(*pWfx)
   If result
       dwLength.l    = DX8Sound\secBuflen/#NUM_PLAY_NOTIFICATIONS
       dwLengthAll.l = DX8Sound\secBuflen
      *Sound.IDirectSoundBuffer8 = DX8Sound\Sound  
      *Sound\SetCurrentPosition(0)
      *Sound\Play(0,0,#DSBPLAY_LOOPING)
      loop = 0
      While loop<50 
          wfso = WaitForSingleObject_(hEvent,#INFINITE)
          wres = *Sound\Lock(0,dwLength,@Write1,@L1,@Write2,@L2, #DSBLOCK_FROMWRITECURSOR )
          If wres = #DS_OK
              CopyMemory_(Write1,*mem,L1)
              *mem+L1
            If L2
              CopyMemory_(Write2,*mem,L2)
            EndIf
              *mem+L2
            lres = *Sound\Unlock(Write1,L1,Write2,L2)
          EndIf
          ResetEvent_(hEvent)
          loop+1
      Wend
   EndIf
EndProcedure
;
;

f=ReadFile(0,"H:\TBII-SVHS.wav")
If f
    *mem = AllocateMemory(Lof(0))
    ReadData(0,*mem,Lof(0))
    wave.WAVEFORMATEX
    FileSeek(0,20)
    ReadData(0,@wave,SizeOf(WAVEFORMATEX))
    CloseFile(0)
    hWndM = OpenWindow(0,0,0,20,20,"Sound",#PB_Window_ScreenCentered)
    DXSound()
    CloseWindow(0)
    FreeMemory(-1)
EndIf
End
;
DataSection 
  IID_DirectSoundBuffer8:  ; DSOUND.h 
    Data.l $6825A449 
    Data.w $7524,$4D82 
    Data.b $92,$0F,$50,$E3,$6A,$B3,$AB,$1E 
  
  IID_DirectSoundNotify8:
    Data.l $b0210783
    Data.w $89cd, $11d0
    Data.b $af,$08,$00,$a0,$c9,$25,$cd,$16
    
EndDataSection
Tom

Posted: Thu Mar 30, 2006 10:20 pm
by traumatic
Tomijan, the following is meant as constructive criticism, I hope you'll
get it right:

It doesn't make any sense to allocate as much memory as the WAV file
needs as it makes streaming completely gratuitous. The key to streaming
lies in using a small buffer that gets played inside a thread, being feeded
continuously with new chunks of data (aka "ringbuffer").

DSound provides notification mechanism (you're using them already) to
tell you when it's time to re-fill the buffer.

Posted: Fri Mar 31, 2006 7:36 am
by tomijan
@traumatic, please read comments in header carefully!
Streaming is not only way to sparing memory, evei is needed if you need insert in streaming loop real-time filter !
Above example demonstrate use of streaming buffer and events to creating streaming loop, not any more.
This is the first example on this forum about this solution.

tom

Posted: Fri Mar 31, 2006 7:53 am
by tomijan
In above post should be "not nothing more", sory for mistake :oops:
tom

Posted: Fri Mar 31, 2006 8:19 am
by traumatic
Ok you got me wrong. Well...

Posted: Fri Mar 31, 2006 8:43 am
by tomijan
traumatic wrote:
Ok you got me wrong. Well...
not true, I only try explain it in my rude english
:)
tom

Posted: Fri Mar 31, 2006 8:49 am
by traumatic
It's my fault. I gave my opinion to something I wasn't asked for.

Posted: Fri Mar 31, 2006 9:23 am
by inc.
http://msdn.microsoft.com/library/defau ... lecode.asp

It uses Directsound for streamrendering but the Wavout engine for playback.
The bufferrefiiling here is beeing done via a WindowCallback.

Posted: Mon Jul 24, 2006 9:39 pm
by inc.
tomijan wrote:In above post should be "not nothing more", sory for mistake :oops:
tom
After investigating some time in Dshow etc. I had again a look at your code example.
The problem was that you did fill a memory buffer using the whole soundfile data where the streaming process itself just reads from that "big" memoryamount the chunks for buffering and serving to the DSoundBuffer. Honestly by this you did left the sense of a true soundfile streaming approach.

So the simple fix is just to read the chunks into the soundbuffer within the refilling loop DIRECTLY from the Soundfile and not from a previous generated full-soundfile-size temp. memoryspace.

Here's the corrected code :)

Edit: And again the problem of the forum engine when posting codes :( .... parts will be just cut off or mixed. This code still is the same size as the one above :?

Heres the code as file: StreamingViaDirectSound.pb

Posted: Mon Jan 15, 2007 8:59 pm
by ricardo
tomijan wrote:Hi all,
about streaming play with DirectSound look bellow, please:
this demo play few seconds of WAVE file

Code: Select all

;--------------------------------------------------------------
;-  DirectSound Streaming Play
;   Tomijan 2006 
;   PB4 
;
;  SORRY - no comments, no cleanup, no other DXSound functions
;  This piece of code  IS NOT REALLY PROGRAM
;  This is DEMO OF STREAMING - LOOP ONLY !!!
;  and should be Inspiration to serious program (I hope)
;--------------------------------------------------------------
[/quote]

Im playing with this example, very nice.

One question:

What do i need to release when closing program? Otheriwse i get an exception because trying to access some memory (i guess by DirectX) but since program is closed...

Thanks