home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 5_2007-2008.ISO / data / Zips / DirectSoun2029361162006.psc / DirectSoundStream.cls < prev    next >
Text File  |  2006-11-05  |  14KB  |  450 lines

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "DirectSoundStream"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. Option Explicit
  15.  
  16. Private Declare Sub CpyMem Lib "kernel32" Alias "RtlMoveMemory" ( _
  17.     pDst As Any, pSrc As Any, ByVal cBytes As Long _
  18. )
  19.  
  20. Private Declare Sub FillMem Lib "kernel32" Alias "RtlFillMemory" ( _
  21.     pDst As Any, ByVal cBytes As Long, ByVal value As Byte _
  22. )
  23.  
  24. Public Enum PlaybackStatus
  25.     PlaybackStopped
  26.     PlaybackPlaying
  27.     PlaybackPausing
  28. End Enum
  29.  
  30. Private Type AudioBuffer
  31.     pData       As Long
  32.     DataLen     As Long
  33. End Type
  34.  
  35. Private Const TimerIntervalMs   As Long = 5
  36.  
  37. Private WithEvents m_clsTimer   As WinTimer
  38. Attribute m_clsTimer.VB_VarHelpID = -1
  39.  
  40. Private m_clsBufSec             As DirectSoundSecondaryBuffer8
  41. Private m_udeStatus             As PlaybackStatus
  42. Private m_udtFormat             As WAVEFORMATEX
  43. Private m_lngDSBufBytes         As Long
  44. Private m_lngDSBufMs            As Long
  45. Private m_lngWriteCursor        As Long
  46.  
  47. Private m_blnEndOfStream        As Boolean
  48. Private m_lngEOSPosition        As Long
  49. Private m_lngEOSDataLeft        As Long
  50. Private m_lngEOSDataLeftMarker  As Long
  51.  
  52. Private m_lngMSPlayed           As Long
  53. Private m_lngMSPlayedMarker     As Long
  54.  
  55. Private m_lngAudioQueueLengthMs As Long
  56.  
  57. Private m_clsBuffers            As DataQueue
  58. Private m_clsCurBuf             As DataQueueItem
  59.  
  60. Public Event StatusChanged(ByVal status As PlaybackStatus)
  61. Public Event BufferDone()
  62. Public Event NoDataLeft()
  63.  
  64.  
  65. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  66. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  67. ''' Various
  68.  
  69. Public Function BytesFromMs(ByVal ms As Long) As Long
  70.     BytesFromMs = MsToBytes(m_udtFormat, ms)
  71. End Function
  72.  
  73. ' data left to play in milliseconds
  74. Public Property Get AudioBufferQueueLength() As Long
  75.     Dim udtPos  As DSCURSORS
  76.     Dim lngLen  As Long
  77.     
  78.     On Error Resume Next
  79.         m_clsBufSec.GetCurrentPosition udtPos
  80.     On Error GoTo 0
  81.     
  82.     lngLen = RingDistance(udtPos.lPlay, m_lngWriteCursor)
  83.     lngLen = lngLen + m_lngAudioQueueLengthMs
  84.     lngLen = lngLen + BytesToMs(m_udtFormat, m_clsCurBuf.DataLeft)
  85.     
  86.     AudioBufferQueueLength = lngLen
  87. End Property
  88.  
  89. Public Property Get Elapsed() As Long
  90.     Elapsed = m_lngMSPlayed
  91. End Property
  92.  
  93. Public Property Let Elapsed(ByVal value As Long)
  94.     m_lngMSPlayed = value
  95. End Property
  96.  
  97. Public Property Get Volume() As Long
  98.     Volume = m_clsBufSec.GetVolume
  99. End Property
  100.  
  101. Public Property Let Volume(ByVal value As Long)
  102.     If value < -10000 Then value = -10000
  103.     If value > 0 Then value = 0
  104.  
  105.     m_clsBufSec.SetVolume value
  106. End Property
  107.  
  108. Public Property Get Balance() As Long
  109.     Balance = m_clsBufSec.GetPan
  110. End Property
  111.  
  112. Public Property Let Balance(ByVal value As Long)
  113.     If value < -10000 Then value = -10000
  114.     If value > 10000 Then value = 10000
  115.     
  116.     m_clsBufSec.SetPan value
  117. End Property
  118.  
  119.  
  120. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  121. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  122. ''' Playback Handling
  123.  
  124. Public Function PlaybackStop() As Boolean
  125.     If PlaybackStatus = PlaybackStopped Then
  126.         PlaybackStop = True
  127.     Else
  128.         m_clsTimer.Enabled = False
  129.         m_clsBufSec.Stop
  130.         m_clsBufSec.SetCurrentPosition 0
  131.         m_lngWriteCursor = 0
  132.         m_lngMSPlayed = 0
  133.         m_lngMSPlayedMarker = 0
  134.         m_blnEndOfStream = False
  135.         
  136.         m_clsCurBuf.Free
  137.         Set m_clsCurBuf = Nothing
  138.         m_clsBuffers.Clear
  139.         
  140.         PlaybackStop = True
  141.         SetPlaybackStatus PlaybackStopped
  142.     End If
  143. End Function
  144.  
  145. Public Function PlaybackPause() As Boolean
  146.     If PlaybackStatus = PlaybackPausing Then
  147.         PlaybackPause = True
  148.     Else
  149.         m_clsTimer.Enabled = False
  150.         m_clsBufSec.Stop
  151.         PlaybackPause = True
  152.         SetPlaybackStatus PlaybackPausing
  153.     End If
  154. End Function
  155.  
  156. Public Function PlaybackStart() As Boolean
  157.     If PlaybackStatus = PlaybackPlaying Then
  158.         PlaybackStart = True
  159.     Else
  160.         If m_clsBuffers.Count > 0 Then
  161.             If PlaybackStatus = PlaybackStopped Then
  162.                 m_clsBufSec.SetCurrentPosition 0
  163.                 m_lngWriteCursor = 0
  164.                 m_lngMSPlayedMarker = 0
  165.                 FillDSBuffer m_lngDSBufBytes
  166.             End If
  167.  
  168.             m_clsBufSec.Play DSBPLAY_LOOPING
  169.             m_clsTimer.Enabled = True
  170.         ElseIf PlaybackStatus = PlaybackPausing Then
  171.             m_clsBufSec.Play DSBPLAY_LOOPING
  172.             m_clsTimer.Enabled = True
  173.         End If
  174.         
  175.         SetPlaybackStatus PlaybackPlaying
  176.         PlaybackStart = True
  177.     End If
  178. End Function
  179.  
  180. Public Property Get PlaybackStatus() As PlaybackStatus
  181.     PlaybackStatus = m_udeStatus
  182. End Property
  183.  
  184.  
  185. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  186. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  187. ''' Audio Data Buffering
  188.  
  189. Private Sub m_clsTimer_Tick()
  190.     Dim udtCursors      As DSCURSORS
  191.     Dim lngDistance     As Long
  192.     Dim lngRead         As Long
  193.     
  194.     On Error GoTo ErrorHandler
  195.         m_clsBufSec.GetCurrentPosition udtCursors
  196.     On Error GoTo 0
  197.  
  198.     On Error Resume Next
  199.         Err.Clear
  200.         ' 25 days should fit in m_lngMSPlayed
  201.         m_lngMSPlayed = m_lngMSPlayed + BytesToMs(m_udtFormat, RingDistance(m_lngMSPlayedMarker, udtCursors.lPlay))
  202.         If Err.Number = 6 Then m_lngMSPlayed = 0    ' Overflow
  203.     On Error GoTo 0
  204.     
  205.     m_lngMSPlayedMarker = udtCursors.lPlay
  206.  
  207.     If m_blnEndOfStream Then
  208.         m_lngEOSDataLeft = m_lngEOSDataLeft - RingDistance(m_lngEOSDataLeftMarker, udtCursors.lPlay)
  209.         m_lngEOSDataLeftMarker = udtCursors.lPlay
  210.         
  211.         If m_lngEOSDataLeft <= 0 Then
  212.             m_clsTimer.Enabled = False
  213.             m_clsBufSec.Stop
  214.             RaiseEvent NoDataLeft
  215.         End If
  216.     Else
  217.         lngDistance = RingDistance(m_lngWriteCursor, udtCursors.lPlay)
  218.     
  219.         If lngDistance >= MsToBytes(m_udtFormat, 100) Then
  220.             lngRead = FillDSBuffer(lngDistance)
  221.             If lngRead < lngDistance Then
  222.                 If m_clsBuffers.Count = 0 Then
  223.                     m_blnEndOfStream = True
  224.                     m_lngEOSPosition = m_lngWriteCursor
  225.                     m_lngEOSDataLeft = RingDistance(udtCursors.lPlay, m_lngEOSPosition)
  226.                     m_lngEOSDataLeftMarker = udtCursors.lPlay
  227.                 End If
  228.             End If
  229.         End If
  230.     End If
  231.  
  232. ErrorHandler:
  233. End Sub
  234.  
  235. Public Property Get NoDataLeft() As Boolean
  236.     If m_blnEndOfStream Then
  237.         If m_lngEOSDataLeft <= 0 Then
  238.             NoDataLeft = True
  239.         End If
  240.     End If
  241. End Property
  242.  
  243. Public Function AudioBufferAdd(ByVal DataPointer As Long, ByVal DataSize As Long) As Boolean
  244.     Dim clsBufferItem   As New DataQueueItem
  245.  
  246.     clsBufferItem.Initialize DataPointer, DataSize
  247.     m_clsBuffers.Enqueue clsBufferItem
  248.  
  249.     m_lngAudioQueueLengthMs = m_lngAudioQueueLengthMs + BytesToMs(m_udtFormat, DataSize)
  250.  
  251.     ' if playback is running but there are no buffers,
  252.     ' the DirectSound buffer will be stopped.
  253.     ' When there is data, playback will be resumed
  254.     If m_blnEndOfStream Then
  255.         If m_clsBuffers.Count = 1 Then
  256.             m_clsBufSec.SetCurrentPosition 0
  257.             m_lngWriteCursor = 0
  258.             
  259.             ' idea: > PreBuffer MS?
  260.             If FillDSBuffer(m_lngDSBufBytes) > 0 Then
  261.                 m_blnEndOfStream = False
  262.                 
  263.                 On Error GoTo ErrorHandler
  264.                     m_clsBufSec.Play DSBPLAY_LOOPING
  265.                 On Error GoTo 0
  266.                 
  267.                 m_clsTimer.Enabled = True
  268.             End If
  269.             
  270.         End If
  271.     End If
  272.     
  273.     AudioBufferAdd = True
  274.     Exit Function
  275.     
  276. ErrorHandler:
  277.     AudioBufferAdd = False
  278. End Function
  279.  
  280. Public Sub AudioBuffersClear()
  281.     m_clsCurBuf.Free
  282.     m_clsCurBuf = Nothing
  283.     
  284.     m_clsBuffers.Clear
  285. End Sub
  286.  
  287. Public Property Get AudioBufferCount() As Long
  288.     AudioBufferCount = m_clsBuffers.Count
  289. End Property
  290.  
  291. Public Property Get BufferLengthMs() As Long
  292.     BufferLengthMs = m_lngDSBufMs
  293. End Property
  294.  
  295. ' returns bytes filled
  296. Private Function FillDSBuffer(ByVal bytes As Long) As Long
  297.     Dim btData()    As Byte
  298.     Dim lngRead     As Long
  299.  
  300.     lngRead = GetData(bytes, btData)
  301.  
  302.     m_clsBufSec.WriteBuffer m_lngWriteCursor, bytes, btData(0), DSBLOCK_DEFAULT
  303.  
  304.     m_lngWriteCursor = (m_lngWriteCursor + lngRead) Mod m_lngDSBufBytes
  305.     
  306.     FillDSBuffer = lngRead
  307. End Function
  308.  
  309. ' returns bytes read
  310. Private Function GetData(ByVal bytes As Long, ByRef btData() As Byte) As Long
  311.     ReDim btData(bytes - 1) As Byte
  312.     Dim lngRead             As Long
  313.     
  314.     If FormatBitsPerSample = 8 Then
  315.         ' fill complete buffer with silence
  316.         FillMem btData(0), bytes, &H80
  317.     End If
  318.     
  319.     ' check if there are audio buffers to read data from
  320.     If m_clsCurBuf Is Nothing Then
  321.         If m_clsBuffers.Count > 0 Then
  322.             Set m_clsCurBuf = m_clsBuffers.Dequeue()
  323.             If m_clsCurBuf Is Nothing Then
  324.                 GetData = 0
  325.                 Exit Function
  326.             Else
  327.                 m_lngAudioQueueLengthMs = m_lngAudioQueueLengthMs - BytesToMs(m_udtFormat, m_clsCurBuf.DataSize)
  328.             End If
  329.         Else
  330.             m_lngAudioQueueLengthMs = 0
  331.             GetData = 0
  332.             Exit Function
  333.         End If
  334.     End If
  335.  
  336.     Do While lngRead < bytes
  337.         If m_clsCurBuf.EndOfBuffer Then
  338.             m_clsCurBuf.Free
  339.             
  340.             RaiseEvent BufferDone
  341.             
  342.             ' current audio buffer was completly read, get the next one
  343.             If m_clsBuffers.Count > 0 Then
  344.                 Set m_clsCurBuf = m_clsBuffers.Dequeue
  345.                 If m_clsCurBuf Is Nothing Then
  346.                     Exit Do
  347.                 Else
  348.                     m_lngAudioQueueLengthMs = m_lngAudioQueueLengthMs - BytesToMs(m_udtFormat, m_clsCurBuf.DataSize)
  349.                 End If
  350.             Else
  351.                 m_lngAudioQueueLengthMs = 0
  352.                 Exit Do
  353.             End If
  354.         End If
  355.         
  356.         If lngRead + m_clsCurBuf.DataLeft > bytes Then
  357.             ' more data in the current buffer then we actually need
  358.             CpyMem btData(lngRead), ByVal m_clsCurBuf.DataPointer + m_clsCurBuf.DataPosition, bytes - lngRead
  359.             m_clsCurBuf.DataPosition = m_clsCurBuf.DataPosition + (bytes - lngRead)
  360.             lngRead = bytes
  361.         Else
  362.             ' whole audio buffer will fit in btData
  363.             CpyMem btData(lngRead), ByVal m_clsCurBuf.DataPointer + m_clsCurBuf.DataPosition, m_clsCurBuf.DataLeft
  364.             lngRead = lngRead + m_clsCurBuf.DataLeft
  365.             m_clsCurBuf.DataPosition = m_clsCurBuf.DataSize
  366.         End If
  367.     Loop
  368.     
  369.     GetData = lngRead
  370. End Function
  371.  
  372.  
  373. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  374. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  375. ''' Audio Format of Secondary Buffer
  376.  
  377. Public Property Get FormatSamplerate() As Long
  378.     FormatSamplerate = m_udtFormat.lSamplesPerSec
  379. End Property
  380.  
  381. Public Property Get FormatChannels() As Integer
  382.     FormatChannels = m_udtFormat.nChannels
  383. End Property
  384.  
  385. Public Property Get FormatBitsPerSample() As Integer
  386.     FormatBitsPerSample = m_udtFormat.nBitsPerSample
  387. End Property
  388.  
  389.  
  390. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  391. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  392. ''' Helpers
  393.  
  394. Private Function RingDistance(ByVal FromByte As Long, ByVal ToByte As Long) As Long
  395.     If ToByte < FromByte Then
  396.         RingDistance = ToByte + m_lngDSBufBytes - FromByte
  397.     Else
  398.         RingDistance = ToByte - FromByte
  399.     End If
  400. End Function
  401.  
  402. Private Sub SetPlaybackStatus(ByVal stat As PlaybackStatus, Optional ByVal silent As Boolean = False)
  403.     m_udeStatus = stat
  404.     If Not silent Then RaiseEvent StatusChanged(m_udeStatus)
  405. End Sub
  406.  
  407. Private Function MsToBytes(fmt As WAVEFORMATEX, ByVal ms As Long) As Long
  408.     MsToBytes = ms / 1000 * fmt.lAvgBytesPerSec
  409. End Function
  410.  
  411. Private Function BytesToMs(fmt As WAVEFORMATEX, ByVal bytes As Long) As Long
  412.     BytesToMs = bytes / fmt.lAvgBytesPerSec * 1000
  413. End Function
  414.  
  415.  
  416. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  417. ''''''''''''''''''''''''''''''''''''''''''''''''''''
  418. ''' Constructor(s)
  419.  
  420. Private Sub Class_Initialize()
  421.     Set m_clsBuffers = New DataQueue
  422.     Set m_clsTimer = New WinTimer
  423.  
  424.     m_clsTimer.Interval = TimerIntervalMs
  425.     m_udeStatus = PlaybackStopped
  426.     m_lngWriteCursor = 0
  427. End Sub
  428.  
  429. Private Sub Class_Terminate()
  430.     PlaybackStop
  431.     
  432.     Set m_clsCurBuf = Nothing
  433.     Set m_clsBuffers = Nothing
  434.     Set m_clsBufSec = Nothing
  435.     Set m_clsTimer = Nothing
  436. End Sub
  437.  
  438. ' Called by the class "DirectSound"
  439. ' Has to be the last member of this interface!
  440. Private Sub CustomConstructor(ByVal BufferSize As Long, dsound As DirectSoundSecondaryBuffer8)
  441.     Set m_clsBufSec = dsound
  442.     
  443.     If Not m_clsBufSec Is Nothing Then
  444.         m_clsBufSec.GetFormat m_udtFormat
  445.         
  446.         m_lngDSBufBytes = BufferSize
  447.         m_lngDSBufMs = BytesToMs(m_udtFormat, BufferSize)
  448.     End If
  449. End Sub
  450.