DirectSound functions

Share your advanced PureBasic knowledge/code with the community.
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

DirectSound functions

Post 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 
Last edited by Hroudtwolf on Wed Mar 29, 2006 11:53 pm, edited 1 time in total.
User avatar
ts-soft
Always Here
Always Here
Posts: 5756
Joined: Thu Jun 24, 2004 2:44 pm
Location: Berlin - Germany

Post 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 ?
PureBasic 5.73 | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Old bugs good, new bugs bad! Updates are evil: might fix old bugs and introduce no new ones.
Image
inc.
Enthusiast
Enthusiast
Posts: 406
Joined: Thu May 06, 2004 4:28 pm
Location: Cologne/GER

Post 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.
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post 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.
va!n
Addict
Addict
Posts: 1104
Joined: Wed Apr 20, 2005 12:48 pm

Post by va!n »

nice work and thanks for sharing the source ;)
va!n aka Thorsten

Intel i7-980X Extreme Edition, 12 GB DDR3, Radeon 5870 2GB, Windows7 x64,
tomijan
Enthusiast
Enthusiast
Posts: 107
Joined: Sun Dec 11, 2005 1:32 pm

Post 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
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Post 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.
Good programmers don't comment their code. It was hard to write, should be hard to read.
tomijan
Enthusiast
Enthusiast
Posts: 107
Joined: Sun Dec 11, 2005 1:32 pm

Post 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
tomijan
Enthusiast
Enthusiast
Posts: 107
Joined: Sun Dec 11, 2005 1:32 pm

Post by tomijan »

In above post should be "not nothing more", sory for mistake :oops:
tom
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Post by traumatic »

Ok you got me wrong. Well...
Good programmers don't comment their code. It was hard to write, should be hard to read.
tomijan
Enthusiast
Enthusiast
Posts: 107
Joined: Sun Dec 11, 2005 1:32 pm

Post by tomijan »

traumatic wrote:
Ok you got me wrong. Well...
not true, I only try explain it in my rude english
:)
tom
traumatic
PureBasic Expert
PureBasic Expert
Posts: 1661
Joined: Sun Apr 27, 2003 4:41 pm
Location: Germany
Contact:

Post by traumatic »

It's my fault. I gave my opinion to something I wasn't asked for.
Good programmers don't comment their code. It was hard to write, should be hard to read.
inc.
Enthusiast
Enthusiast
Posts: 406
Joined: Thu May 06, 2004 4:28 pm
Location: Cologne/GER

Post 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.
inc.
Enthusiast
Enthusiast
Posts: 406
Joined: Thu May 06, 2004 4:28 pm
Location: Cologne/GER

Post 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
Check out OOP support for PB here!
ricardo
Addict
Addict
Posts: 2402
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post 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
Post Reply