home *** CD-ROM | disk | FTP | other *** search
/ Planet Source Code Jumbo …e CD Visual Basic 1 to 7 / 5_2007-2008.ISO / data / Zips / WaveOut_St20274210282006.psc / clsStreamWAV.cls < prev    next >
Text File  |  2006-10-28  |  29KB  |  1,080 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 = "StreamWAV"
  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. '**************************************'
  17. ' clsStreamWAV                         '
  18. '                                      '
  19. ' Reads WAVs, with ACM support         '
  20. ' for compressed formats               '
  21. '                                      '
  22. ' Will only return 16 bit samples!     '
  23. ' If a codec can't convert from 8 bit  '
  24. ' to 16 bit then conversion will fail! '
  25. '                                      '
  26. ' Supported Chunks: data, fmt          '
  27. '**************************************'
  28.  
  29.  
  30. Private Const Extensions    As String = "WAV"
  31. Private Const Description   As String = "Wave Audio"
  32.  
  33.  
  34. Private Declare Function CreateFile Lib "kernel32.dll" _
  35. Alias "CreateFileA" ( _
  36.     ByVal lpFileName As String, _
  37.     ByVal dwDesiredAccess As Long, _
  38.     ByVal dwShareMode As Long, _
  39.     lpSecurityAttributes As Any, _
  40.     ByVal dwCreationDisposition As Long, _
  41.     ByVal dwFlagsAndAttributes As Long, _
  42.     ByVal hTemplateFile As Long _
  43. ) As Long
  44.  
  45. Private Declare Function ReadFile Lib "kernel32" ( _
  46.     ByVal hFile As Long, _
  47.     lpBuffer As Any, _
  48.     ByVal nNumberOfBytesToRead As Long, _
  49.     lpNumberOfBytesRead As Long, _
  50.     ByVal lpOverlapped As Any _
  51. ) As Long
  52.  
  53. Private Declare Function WriteFile Lib "kernel32" ( _
  54.     ByVal hFile As Long, _
  55.     lpBuffer As Any, _
  56.     ByVal nNumberOfBytesToWrite As Long, _
  57.     lpNumberOfBytesWritten As Long, _
  58.     ByVal lpOverlapped As Any _
  59. ) As Long
  60.  
  61. Private Declare Function SetFilePointer Lib "kernel32" ( _
  62.     ByVal hFile As Long, _
  63.     ByVal lDistanceToMove As Long, _
  64.     ByVal lpDistanceToMoveHigh As Long, _
  65.     ByVal dwMoveMethod As Long _
  66. ) As Long
  67.  
  68. Private Declare Function GetFileSize Lib "kernel32" ( _
  69.     ByVal hFile As Long, _
  70.     ByVal lpFileSizeHigh As Long _
  71. ) As Long
  72.  
  73. Private Declare Function CloseHandle Lib "kernel32" ( _
  74.     ByVal hObject As Long _
  75. ) As Long
  76.  
  77. Private Declare Sub ZeroMem Lib "kernel32" _
  78. Alias "RtlZeroMemory" ( _
  79.     pDst As Any, _
  80.     ByVal dwLen As Long _
  81. )
  82.  
  83. Private Declare Sub CpyMem Lib "kernel32" _
  84. Alias "RtlMoveMemory" ( _
  85.     pDst As Any, _
  86.     pSrc As Any, _
  87.     ByVal cb As Long _
  88. )
  89.  
  90. Private Declare Function IsBadReadPtr Lib "kernel32" ( _
  91.     ptr As Any, _
  92.     ByVal ucb As Long _
  93. ) As Long
  94.  
  95. Private Declare Function IsBadWritePtr Lib "kernel32" ( _
  96.     ptr As Any, _
  97.     ByVal ucb As Long _
  98. ) As Long
  99.  
  100. Private Declare Function acmStreamPrepareHeader Lib "msacm32" ( _
  101.     ByVal has As Long, _
  102.     pash As ACMSTREAMHEADER, _
  103.     ByVal fdwPrepare As Long _
  104. ) As Long
  105.  
  106. Private Declare Function acmStreamUnprepareHeader Lib "msacm32" ( _
  107.     ByVal has As Long, _
  108.     pash As ACMSTREAMHEADER, _
  109.     ByVal fdwUnprepare As Long _
  110. ) As Long
  111.  
  112. Private Declare Function acmStreamOpen Lib "msacm32" ( _
  113.     phas As Long, _
  114.     ByVal had As Long, _
  115.     pwfxSrc As Any, _
  116.     pwfxDst As Any, _
  117.     ByVal pwfltr As Long, _
  118.     ByVal dwCallback As Long, _
  119.     ByVal dwInstance As Long, _
  120.     ByVal fdwOpen As Long _
  121. ) As Long
  122.  
  123. Private Declare Function acmStreamSize Lib "msacm32" ( _
  124.     ByVal has As Long, _
  125.     ByVal cbInput As Long, _
  126.     pdwOutputBytes As Long, _
  127.     ByVal fdwSize As Long _
  128. ) As Long
  129.  
  130. Private Declare Function acmStreamConvert Lib "msacm32" ( _
  131.     ByVal has As Long, _
  132.     pash As ACMSTREAMHEADER, _
  133.     ByVal fdwConvert As Long _
  134. ) As Long
  135.  
  136. Private Declare Function acmStreamReset Lib "msacm32" ( _
  137.     ByVal has As Long, _
  138.     ByVal fdwReset As Long _
  139. ) As Long
  140.  
  141. Private Declare Function acmStreamClose Lib "msacm32" ( _
  142.     ByVal has As Long, _
  143.     ByVal fdwClose As Long _
  144. ) As Long
  145.  
  146. Private Declare Function mmioClose Lib "winmm" ( _
  147.     ByVal hmmio As Long, _
  148.     ByVal uFlags As Long _
  149. ) As Long
  150.  
  151. Private Declare Function mmioDescend Lib "winmm" ( _
  152.     ByVal hmmio As Long, _
  153.     lpck As MMCKINFO, _
  154.     lpckParent As MMCKINFO, _
  155.     ByVal uFlags As Long _
  156. ) As Long
  157.  
  158. Private Declare Function mmioDescendParent Lib "winmm" _
  159. Alias "mmioDescend" ( _
  160.     ByVal hmmio As Long, _
  161.     lpck As MMCKINFO, _
  162.     ByVal x As Long, _
  163.     ByVal uFlags As Long _
  164. ) As Long
  165.  
  166. Private Declare Function mmioOpen Lib "winmm" _
  167. Alias "mmioOpenA" ( _
  168.     ByVal szFileName As String, _
  169.     lpmmioinfo As MMIOINFO, _
  170.     ByVal dwOpenFlags As Long _
  171. ) As Long
  172.  
  173. Private Declare Function mmioSeek Lib "winmm" ( _
  174.     ByVal hmmio As Long, _
  175.     ByVal lOffset As Long, _
  176.     ByVal iOrigin As Long _
  177. ) As Long
  178.  
  179. Private Declare Function mmioStringToFOURCC Lib "winmm" _
  180. Alias "mmioStringToFOURCCA" ( _
  181.     ByVal sz As String, _
  182.     ByVal uFlags As Long _
  183. ) As Long
  184.  
  185. Private Type ACMSTREAMHEADER
  186.     cbStruct                    As Long
  187.     fdwStatus                   As Long
  188.     dwUser                      As Long
  189.     pbSrc                       As Long
  190.     cbSrcLength                 As Long
  191.     cbSrcLengthUsed             As Long
  192.     dwSrcUser                   As Long
  193.     pbDst                       As Long
  194.     cbDstLength                 As Long
  195.     cbDstLengthUsed             As Long
  196.     dwDstUser                   As Long
  197.     dwReservedDriver(9)         As Long
  198. End Type
  199.  
  200. Private Type MMIOINFO
  201.    dwFlags                      As Long
  202.    fccIOProc                    As Long
  203.    pIOProc                      As Long
  204.    wErrorRet                    As Long
  205.    htask                        As Long
  206.    cchBuffer                    As Long
  207.    pchBuffer                    As String
  208.    pchNext                      As String
  209.    pchEndRead                   As String
  210.    pchEndWrite                  As String
  211.    lBufOffset                   As Long
  212.    lDiskOffset                  As Long
  213.    adwInfo(4)                   As Long
  214.    dwReserved1                  As Long
  215.    dwReserved2                  As Long
  216.    hmmio                        As Long
  217. End Type
  218.  
  219. Private Type WAVE_FORMAT
  220.     wFormatTag                  As Integer
  221.     wChannels                   As Integer
  222.     dwSampleRate                As Long
  223.     dwBytesPerSec               As Long
  224.     wBlockAlign                 As Integer
  225.     wBitsPerSample              As Integer
  226. End Type
  227.  
  228. Private Type MMCKINFO
  229.    ckid                         As Long
  230.    ckSize                       As Long
  231.    fccType                      As Long
  232.    dwDataOffset                 As Long
  233.    dwFlags                      As Long
  234. End Type
  235.  
  236. Private Type CHUNKINFO
  237.     Start                       As Long
  238.     Length                      As Long
  239. End Type
  240.  
  241. Private Type MMWAVEFORMATEX
  242.     wFormatTag                  As Integer
  243.     nChannels                   As Integer
  244.     nSamplesPerSec              As Long
  245.     nAvgBytesPerSec             As Long
  246.     nBlockAlign                 As Integer
  247.     wBitsPerSample              As Integer
  248.     cbSize                      As Integer
  249. End Type
  250.  
  251. Private Type hFile
  252.     handle                      As Long
  253.     path                        As String
  254. End Type
  255.  
  256. Private Enum HACMSTREAM
  257.     INVALID_STREAM_HANDLE = 0
  258. End Enum
  259.  
  260. Private Enum ACM_STREAMSIZEF
  261.     ACM_STREAMSIZEF_DESTINATION = &H1
  262.     ACM_STREAMSIZEF_SOURCE = &H0
  263.     ACM_STREAMSIZEF_QUERYMASK = &HF
  264. End Enum
  265.  
  266. Private Enum ACM_STREAMCONVERTF
  267.     ACM_STREAMCONVERTF_BLOCKALIGN = &H4
  268.     ACM_STREAMCONVERTF_START = &H10
  269.     ACM_STREAMCONVERTF_END = &H20
  270. End Enum
  271.  
  272. Private Enum FILE_OPEN_METHOD
  273.     CREATE_NEW = 1
  274.     CREATE_ALWAYS = 2
  275.     OPEN_EXISTING = 3
  276.     OPEN_ALWAYS = 4
  277. End Enum
  278.  
  279. Private Enum FILE_SHARE_RIGHTS
  280.     FILE_SHARE_READ = &H1
  281.     FILE_SHARE_WRITE = &H2
  282. End Enum
  283.  
  284. Private Enum FILE_ACCESS_RIGHTS
  285.     GENERIC_READ = &H80000000
  286.     GENERIC_WRITE = &H40000000
  287. End Enum
  288.  
  289. Private Enum SEEK_METHOD
  290.     FILE_BEGIN = 0
  291.     FILE_CURRENT = 1
  292.     FILE_END = 2
  293. End Enum
  294.  
  295. Public Enum SND_RESULT
  296.     SND_ERR_SUCCESS
  297.     SND_ERR_INVALID_SOURCE
  298.     SND_ERR_INVALID_OUTPUT
  299.     SND_ERR_INTERNAL
  300.     SND_ERR_OUT_OF_RANGE
  301.     SND_ERR_END_OF_STREAM
  302.     SND_ERR_INVALID_TAG
  303.     SND_ERR_INVALID_PARAM
  304.     SND_ERR_TOO_BIG
  305.     SND_ERR_NEED_MORE
  306.     SND_ERR_UNKNOWN
  307. End Enum
  308.  
  309. Public Enum SND_SEEK_MODE
  310.     SND_SEEK_PERCENT
  311.     SND_SEEK_SECONDS
  312. End Enum
  313.  
  314. Private Const MMIO_READ         As Long = &H0
  315. Private Const MMIO_FINDCHUNK    As Long = &H10
  316. Private Const MMIO_FINDRIFF     As Long = &H20
  317.  
  318. Private Const INVALID_HANDLE    As Long = -1
  319.  
  320. Private Const SEEK_CUR          As Long = 1
  321.  
  322. Private Const WAVE_FORMAT_PCM   As Long = 1
  323.  
  324.  
  325. ' Stream
  326.  
  327. Private hFWave                  As hFile
  328. Private cnkData                 As CHUNKINFO
  329. Private cnkInfo                 As CHUNKINFO
  330. Private udtWFXIn                As MMWAVEFORMATEX
  331. Private udtWFXOut               As MMWAVEFORMATEX
  332. Private btWFX()                 As Byte
  333.  
  334. ' ACM
  335.  
  336. Private Const OUTPUT_BUFFER_MS  As Long = 500
  337.  
  338. Private hStream                 As HACMSTREAM
  339.  
  340. Private btInput()               As Byte
  341. Private intOutput()             As Integer
  342.  
  343. Private lngInputLen             As Long
  344. Private lngOutputLen            As Long
  345.  
  346. Private lngPosInBuffer          As Long
  347. Private lngBufferData           As Long
  348.  
  349. Private lngFilePositionMS       As Long
  350.  
  351. Private blnEndOfStream          As Boolean
  352. Private blnFirst                As Boolean
  353.  
  354. Private lngKeepInBuffer         As Long
  355.  
  356. Private Sub Class_Initialize()
  357.     hStream = INVALID_STREAM_HANDLE
  358.     hFWave.handle = INVALID_HANDLE
  359. End Sub
  360.  
  361. Private Sub Class_Terminate()
  362.     StreamClose
  363. End Sub
  364.  
  365. Public Property Get StreamDescription( _
  366. ) As String
  367.  
  368.     StreamDescription = Description
  369. End Property
  370.  
  371. Public Property Get EndOfStream( _
  372. ) As Boolean
  373.  
  374.     If blnEndOfStream Then
  375.         If lngPosInBuffer = lngBufferData Then
  376.             EndOfStream = True
  377.         End If
  378.     End If
  379. End Property
  380.  
  381. Public Function StreamExtensions( _
  382. ) As String()
  383.  
  384.     StreamExtensions = Split(Extensions, ";")
  385. End Function
  386.  
  387. Public Function StreamOpen( _
  388.     ByVal Source As String _
  389. ) As SND_RESULT
  390.  
  391.     If Not IsValidFile(Source) Then
  392.         StreamOpen = SND_ERR_INVALID_SOURCE
  393.         Exit Function
  394.     End If
  395.  
  396.     StreamClose
  397.  
  398.     ' find WAV Chunks "data" and "fmt "
  399.     cnkData = GetWavChunkPos(Source, "data")
  400.     cnkInfo = GetWavChunkPos(Source, "fmt ")
  401.  
  402.     ' valid Chunks?
  403.     If cnkData.Start = 0 Then
  404.         StreamOpen = SND_ERR_INVALID_SOURCE
  405.         Exit Function
  406.     End If
  407.  
  408.     If cnkInfo.Start = 0 Then
  409.         StreamOpen = SND_ERR_INVALID_SOURCE
  410.         Exit Function
  411.     End If
  412.  
  413.     If cnkInfo.Length < 16 Then
  414.         StreamOpen = SND_ERR_INVALID_SOURCE
  415.         Exit Function
  416.     End If
  417.  
  418.     hFWave = FileOpen(Source, _
  419.                       GENERIC_READ, _
  420.                       FILE_SHARE_READ)
  421.  
  422.     If hFWave.handle = INVALID_HANDLE Then
  423.         StreamOpen = SND_ERR_INVALID_SOURCE
  424.         Exit Function
  425.     End If
  426.  
  427.     ' shrink data chunks with ilegal length to file length
  428.     If FileLength(hFWave) < (cnkData.Start + cnkData.Length) Then
  429.         cnkData.Length = FileLength(hFWave) - cnkData.Start
  430.     End If
  431.  
  432.     ' read info chunk
  433.     ReDim btWFX(cnkInfo.Length - 1) As Byte
  434.     FileSeek hFWave, cnkInfo.Start, FILE_BEGIN
  435.     FileRead hFWave, VarPtr(btWFX(0)), cnkInfo.Length
  436.  
  437.     CpyMem udtWFXIn, btWFX(0), Len(udtWFXIn)
  438.  
  439.     ' seek to the beginning of the audio data
  440.     FileSeek hFWave, cnkData.Start, FILE_BEGIN
  441.  
  442.     ' init the Audio Compression Manager
  443.     If Not InitConversion(True) Then
  444.         StreamOpen = SND_ERR_INTERNAL
  445.         StreamClose
  446.         Exit Function
  447.     End If
  448.  
  449.     StreamOpen = SND_ERR_SUCCESS
  450. End Function
  451.  
  452. Public Function StreamClose( _
  453. ) As SND_RESULT
  454.  
  455.     If hFWave.handle = INVALID_HANDLE Then
  456.         StreamClose = SND_ERR_INVALID_SOURCE
  457.     Else
  458.         CloseConverter
  459.         FileClose hFWave
  460.         lngFilePositionMS = 0
  461.         StreamClose = SND_ERR_SUCCESS
  462.     End If
  463. End Function
  464.  
  465. ' StreamRead returns exactly as many bytes as wanted,
  466. ' as long as the end of the stream isn't reached
  467. Public Function StreamRead( _
  468.     ByVal buffer_ptr As Long, _
  469.     ByVal buffer_len As Long, _
  470.     ByRef buffer_read As Long _
  471. ) As SND_RESULT
  472.  
  473.     StreamRead = SND_ERR_SUCCESS
  474.  
  475.     buffer_read = 0
  476.  
  477.     Do While buffer_read < buffer_len
  478.  
  479.         ' PCM buffer empty
  480.         If lngBufferData = 0 Then
  481.             If Not FillBuffer Then
  482.                 StreamRead = SND_ERR_END_OF_STREAM
  483.                 Exit Do
  484.             End If
  485.  
  486.         ' not enough data in the PCM buffer
  487.         ElseIf (lngBufferData - lngPosInBuffer) < (buffer_len - buffer_read) Then
  488.             If 0 < (lngBufferData - lngPosInBuffer) Then
  489.  
  490.                 If 0 = IsBadReadPtr(ByVal VarPtr(intOutput(0)) + lngPosInBuffer, _
  491.                                     lngBufferData - lngPosInBuffer) Then
  492.  
  493.                     If 0 = IsBadWritePtr(ByVal buffer_ptr + buffer_read, _
  494.                                          lngBufferData - lngPosInBuffer) Then
  495.  
  496.                         CpyMem ByVal buffer_ptr + buffer_read, _
  497.                                ByVal VarPtr(intOutput(0)) + lngPosInBuffer, _
  498.                                lngBufferData - lngPosInBuffer
  499.  
  500.                     End If
  501.  
  502.                 End If
  503.  
  504.                 buffer_read = buffer_read + (lngBufferData - lngPosInBuffer)
  505.             End If
  506.  
  507.             If Not FillBuffer Then
  508.                 StreamRead = SND_ERR_END_OF_STREAM
  509.                 Exit Do
  510.             End If
  511.  
  512.         ' enough data in the PCM buffer
  513.         Else
  514.             If 0 = IsBadReadPtr(ByVal VarPtr(intOutput(0)) + lngPosInBuffer, _
  515.                                 buffer_len - buffer_read) Then
  516.  
  517.                 If 0 = IsBadWritePtr(ByVal buffer_ptr + buffer_read, _
  518.                                      buffer_len - buffer_read) Then
  519.  
  520.                     CpyMem ByVal buffer_ptr + buffer_read, _
  521.                            ByVal VarPtr(intOutput(0)) + lngPosInBuffer, _
  522.                            buffer_len - buffer_read
  523.  
  524.                 End If
  525.  
  526.             End If
  527.  
  528.             lngPosInBuffer = lngPosInBuffer + (buffer_len - buffer_read)
  529.             buffer_read = buffer_read + (buffer_len - buffer_read)
  530.  
  531.         End If
  532.  
  533.     Loop
  534.     
  535.     lngFilePositionMS = lngFilePositionMS + (buffer_read / udtWFXOut.nAvgBytesPerSec * 1000)
  536. End Function
  537.  
  538. Public Function StreamSeek( _
  539.     ByVal value As Long, _
  540.     ByVal seek_mode As SND_SEEK_MODE _
  541. ) As SND_RESULT
  542.  
  543.     Dim lngBytes    As Long
  544.  
  545.     If hFWave.handle = INVALID_HANDLE Then
  546.         StreamSeek = SND_ERR_INVALID_SOURCE
  547.         Exit Function
  548.     End If
  549.  
  550.     Select Case seek_mode
  551.  
  552.         Case SND_SEEK_PERCENT
  553.             If value < 0 Or value > 99 Then
  554.                 StreamSeek = SND_ERR_OUT_OF_RANGE
  555.                 Exit Function
  556.             End If
  557.  
  558.             lngBytes = value / 100 * cnkData.Length
  559.  
  560.         Case SND_SEEK_SECONDS
  561.             If value < 0 Or value > (Duration / 1000) Then
  562.                 StreamSeek = SND_ERR_OUT_OF_RANGE
  563.                 Exit Function
  564.             End If
  565.  
  566.             lngBytes = udtWFXIn.nAvgBytesPerSec * value
  567.  
  568.     End Select
  569.  
  570.     If value = 0 Then
  571.         FileSeek hFWave, cnkData.Start, FILE_BEGIN
  572.     Else
  573.         lngBytes = AlignBytes(lngBytes) + cnkData.Start
  574.         FileSeek hFWave, lngBytes, FILE_BEGIN
  575.     End If
  576.  
  577.     ' reset ACM stream to clear buffers of codecs
  578.     lngFilePositionMS = (FilePosition(hFWave) - cnkData.Start) / udtWFXIn.nAvgBytesPerSec * 1000
  579.     ResetConverter
  580.     
  581.     StreamSeek = SND_ERR_SUCCESS
  582. End Function
  583.  
  584. '''''''''''''''''''''''''''''''''''''''''''''''''
  585. '''''''''''''''''''''''''''''''''''''''''''''''''
  586. '''''''''''''''''''''''''''''''''''''''''''''''''
  587.  
  588. Public Property Get Position( _
  589. ) As Long
  590.  
  591.     Position = lngFilePositionMS - OUTPUT_BUFFER_MS
  592. End Property
  593.  
  594. Public Property Get Duration( _
  595. ) As Long
  596.  
  597.     Duration = (cnkData.Length) / udtWFXIn.nAvgBytesPerSec * 1000
  598. End Property
  599.  
  600. Public Property Get BitsPerSample( _
  601. ) As Integer
  602.  
  603.     BitsPerSample = 16
  604. End Property
  605.  
  606. Public Property Get BitsPerSecond( _
  607. ) As Long
  608.  
  609.     BitsPerSecond = udtWFXIn.nAvgBytesPerSec * 8
  610. End Property
  611.  
  612. Public Property Get Channels( _
  613. ) As Integer
  614.  
  615.     Channels = udtWFXIn.nChannels
  616. End Property
  617.  
  618. Public Property Get SamplesPerSecond( _
  619. ) As Long
  620.  
  621.     SamplesPerSecond = udtWFXIn.nSamplesPerSec
  622. End Property
  623.  
  624. '''''''''''''''''''''''''''''''''''''''''''''''''
  625. '''''''''''''''''''''''''''''''''''''''''''''''''
  626. '''''''''''''''''''''''''''''''''''''''''''''''''
  627.  
  628. Private Function InitConversion( _
  629.     Optional Force16Bit As Boolean = False _
  630. ) As Boolean
  631.  
  632.     Dim mmr     As Long
  633.  
  634.     If hStream <> INVALID_STREAM_HANDLE Then
  635.         CloseConverter
  636.     End If
  637.  
  638.     udtWFXOut = udtWFXIn
  639.  
  640.     If udtWFXOut.wBitsPerSample < 8 Then
  641.         udtWFXOut.wBitsPerSample = 8
  642.     ElseIf udtWFXOut.wBitsPerSample > 8 Then
  643.         udtWFXOut.wBitsPerSample = 16
  644.     End If
  645.  
  646.     If Force16Bit Then
  647.         udtWFXOut.wBitsPerSample = 16
  648.     End If
  649.  
  650.     With udtWFXOut
  651.         udtWFXOut = CreateWFX(.nSamplesPerSec, _
  652.                               .nChannels, _
  653.                               .wBitsPerSample)
  654.     End With
  655.  
  656.     mmr = acmStreamOpen(hStream, 0, _
  657.                         btWFX(0), udtWFXOut, _
  658.                         0, 0, 0, 0)
  659.  
  660.     If mmr <> 0 Then
  661.         If Force16Bit Then Exit Function
  662.  
  663.         If udtWFXOut.wBitsPerSample = 16 Then
  664.             udtWFXOut.wBitsPerSample = 8
  665.         Else
  666.             udtWFXOut.wBitsPerSample = 16
  667.         End If
  668.  
  669.         mmr = acmStreamOpen(hStream, 0, _
  670.                             btWFX(0), udtWFXOut, _
  671.                             0, 0, 0, 0)
  672.  
  673.         If mmr <> 0 Then Exit Function
  674.     End If
  675.  
  676.     ' set size of output buffer
  677.     lngOutputLen = OUTPUT_BUFFER_MS / 1000 * udtWFXOut.nAvgBytesPerSec
  678.  
  679.     ' needed size of input buffer to fill the output buffer
  680.     mmr = acmStreamSize(hStream, _
  681.                         lngOutputLen, _
  682.                         lngInputLen, _
  683.                         ACM_STREAMSIZEF_DESTINATION)
  684.  
  685.     If mmr <> 0 Then
  686.         acmStreamClose hStream, 0
  687.         hStream = 0
  688.         Exit Function
  689.     End If
  690.  
  691.     ReDim intOutput(lngOutputLen / 2 - 1) As Integer
  692.     ReDim btInput(lngInputLen - 1) As Byte
  693.  
  694.     blnEndOfStream = False
  695.     blnFirst = True
  696.  
  697.     lngKeepInBuffer = 0
  698.  
  699.     InitConversion = True
  700. End Function
  701.  
  702. Private Function Convert( _
  703.     ByVal lngInLen As Long, _
  704.     ByVal lngOutLen As Long, _
  705.     lngInUsed As Long, _
  706.     lngOutUsed As Long, _
  707.     Optional ByVal LastConversion As Boolean = False _
  708. ) As Boolean
  709.  
  710.     Dim lngFlags    As Long
  711.     Dim udtHdr      As ACMSTREAMHEADER
  712.  
  713.     lngFlags = ACM_STREAMCONVERTF_BLOCKALIGN
  714.  
  715.     If blnFirst Then _
  716.         lngFlags = lngFlags Or ACM_STREAMCONVERTF_START
  717.     If LastConversion Then
  718.         lngFlags = lngFlags Or ACM_STREAMCONVERTF_END
  719.     End If
  720.  
  721.     With udtHdr
  722.         .cbStruct = Len(udtHdr)
  723.         .cbSrcLength = lngInLen
  724.         .cbDstLength = lngOutLen
  725.         .pbDst = VarPtr(intOutput(0))
  726.         .pbSrc = VarPtr(btInput(0))
  727.     End With
  728.  
  729.     acmStreamPrepareHeader hStream, udtHdr, 0
  730.  
  731.     blnFirst = False
  732.  
  733.     If 0 = acmStreamConvert(hStream, udtHdr, lngFlags) Then
  734.         With udtHdr
  735.             lngInUsed = .cbSrcLengthUsed
  736.             lngOutUsed = .cbDstLengthUsed
  737.  
  738.             lngKeepInBuffer = .cbSrcLength - .cbSrcLengthUsed
  739.         End With
  740.  
  741.         If lngKeepInBuffer > 0 Then
  742.             ' codec didn't use all the input bytes,
  743.             ' move them to the first index of the input buffer
  744.             ' to decode them with the next call to convert()
  745.             CpyMem btInput(0), _
  746.                    btInput(lngInLen - lngKeepInBuffer), _
  747.                    lngKeepInBuffer
  748.         End If
  749.  
  750.         Convert = True
  751.     End If
  752.  
  753.     acmStreamUnprepareHeader hStream, udtHdr, 0
  754. End Function
  755.  
  756. Private Function FillBuffer( _
  757. ) As Boolean
  758.  
  759.     Dim lngRead     As Long
  760.     Dim lngWritten  As Long
  761.     Dim udeRet      As SND_RESULT
  762.  
  763.     If blnEndOfStream Then
  764.         'If lngPosInBuffer >= lngBufferData Then
  765.             lngBufferData = 0
  766.             lngPosInBuffer = 0
  767.             ZeroMem intOutput(0), lngOutputLen
  768.             ZeroMem btInput(0), lngInputLen
  769.             Exit Function
  770.         'Else
  771.         '    FillBuffer = True
  772.         '    Exit Function
  773.         'End If
  774.     End If
  775.  
  776.     ' get data from WAV
  777.     udeRet = ReadWAVData(VarPtr(btInput(lngKeepInBuffer)), _
  778.                          lngInputLen - lngKeepInBuffer, _
  779.                          lngRead)
  780.  
  781.     If udeRet <> SND_ERR_SUCCESS Then
  782.         ' either read error or end of file
  783.         blnEndOfStream = True
  784.     End If
  785.  
  786.     Convert lngRead + lngKeepInBuffer, _
  787.             lngOutputLen, _
  788.             lngRead, lngWritten, _
  789.             blnEndOfStream
  790.  
  791.     lngPosInBuffer = 0
  792.     lngBufferData = lngWritten
  793.  
  794.     FillBuffer = True
  795. End Function
  796.  
  797. Private Sub ResetConverter()
  798.     If hStream = INVALID_STREAM_HANDLE Then
  799.         Exit Sub
  800.     End If
  801.  
  802.     CloseConverter
  803.  
  804.     acmStreamOpen hStream, 0, _
  805.                   btWFX(0), udtWFXOut, _
  806.                   0, 0, 0, 0
  807.  
  808.     lngOutputLen = OUTPUT_BUFFER_MS / 1000 * udtWFXOut.nAvgBytesPerSec
  809.  
  810.     acmStreamSize hStream, _
  811.                   lngOutputLen, _
  812.                   lngInputLen, _
  813.                   ACM_STREAMSIZEF_DESTINATION
  814.  
  815.     ReDim intOutput(lngOutputLen / 2 - 1) As Integer
  816.     ReDim btInput(lngInputLen - 1) As Byte
  817.  
  818.     blnEndOfStream = False
  819.     blnFirst = True
  820.  
  821.     lngKeepInBuffer = 0
  822. End Sub
  823.  
  824. Private Function CloseConverter( _
  825. ) As Boolean
  826.  
  827.     On Error Resume Next
  828.  
  829.     acmStreamClose hStream, 0
  830.     hStream = INVALID_STREAM_HANDLE
  831.  
  832.     ZeroMem btInput(0), lngInputLen
  833.     ZeroMem intOutput(0), lngOutputLen
  834.  
  835.     blnEndOfStream = False
  836.     lngBufferData = 0
  837.     lngPosInBuffer = 0
  838.     lngInputLen = 0
  839.     lngOutputLen = 0
  840.     lngKeepInBuffer = 0
  841.  
  842.     CloseConverter = True
  843. End Function
  844.  
  845. '''''''''''''''''''''''''''''''''''''''''''''''''
  846. '''''''''''''''''''''''''''''''''''''''''''''''''
  847. '''''''''''''''''''''''''''''''''''''''''''''''''
  848.  
  849. Private Function ReadWAVData( _
  850.     ByVal data_ptr As Long, _
  851.     ByVal data_len As Long, _
  852.     data_read As Long _
  853. ) As SND_RESULT
  854.  
  855.     ReadWAVData = SND_ERR_SUCCESS
  856.  
  857.     If hFWave.handle = INVALID_HANDLE Then
  858.         ReadWAVData = SND_ERR_INVALID_SOURCE
  859.         Exit Function
  860.     End If
  861.  
  862.     If FilePosition(hFWave) > (cnkData.Start + cnkData.Length) Then
  863.         ' end of file reached
  864.         ReadWAVData = SND_ERR_END_OF_STREAM
  865.         data_read = 0
  866.         Exit Function
  867.     End If
  868.  
  869.     If FilePosition(hFWave) + data_len > (cnkData.Start + cnkData.Length) Then
  870.         ' almost at the end of the file,
  871.         ' but reached after this read
  872.         data_len = (cnkData.Start + cnkData.Length) - FilePosition(hFWave)
  873.         ReadWAVData = SND_ERR_END_OF_STREAM
  874.     End If
  875.  
  876.     data_read = FileRead(hFWave, data_ptr, data_len)
  877. End Function
  878.  
  879. ' finds a chunk in a WAV container
  880. Private Function GetWavChunkPos( _
  881.     ByVal strFile As String, _
  882.     ByVal strChunk As String _
  883. ) As CHUNKINFO
  884.  
  885.     Dim hMmioIn             As Long
  886.     Dim lngRet              As Long
  887.     Dim mmckinfoParentIn    As MMCKINFO
  888.     Dim mmckinfoSubchunkIn  As MMCKINFO
  889.     Dim mmioinf             As MMIOINFO
  890.  
  891.     hMmioIn = mmioOpen(strFile, mmioinf, MMIO_READ)
  892.     If hMmioIn = 0 Then
  893.         Exit Function
  894.     End If
  895.  
  896.     mmckinfoParentIn.fccType = mmioStringToFOURCC("WAVE", 0)
  897.     lngRet = mmioDescendParent(hMmioIn, _
  898.                                mmckinfoParentIn, _
  899.                                0, _
  900.                                MMIO_FINDRIFF)
  901.  
  902.     If Not (lngRet = 0) Then
  903.         mmioClose hMmioIn, 0
  904.         Exit Function
  905.     End If
  906.  
  907.     mmckinfoSubchunkIn.ckid = mmioStringToFOURCC(strChunk, 0)
  908.     lngRet = mmioDescend(hMmioIn, _
  909.                          mmckinfoSubchunkIn, _
  910.                          mmckinfoParentIn, _
  911.                          MMIO_FINDCHUNK)
  912.  
  913.     If Not (lngRet = 0) Then
  914.         mmioClose hMmioIn, 0
  915.         Exit Function
  916.     End If
  917.  
  918.     GetWavChunkPos.Start = mmioSeek(hMmioIn, 0, SEEK_CUR)
  919.     GetWavChunkPos.Length = mmckinfoSubchunkIn.ckSize
  920.  
  921.     mmioClose hMmioIn, 0
  922. End Function
  923.  
  924. ' when seeking in WAV files you need to align
  925. ' the position seeked to on the Block Align of
  926. ' the audio data
  927. Private Function AlignBytes( _
  928.     ByVal bytes As Long _
  929. ) As Long
  930.  
  931.     AlignBytes = bytes - (bytes Mod udtWFXIn.nBlockAlign)
  932. End Function
  933.  
  934. Private Function CreateWFX( _
  935.     sr As Long, _
  936.     chs As Integer, _
  937.     bps As Integer _
  938. ) As MMWAVEFORMATEX
  939.  
  940.     With CreateWFX
  941.         .wFormatTag = WAVE_FORMAT_PCM
  942.         .nChannels = chs
  943.         .nSamplesPerSec = sr
  944.         .wBitsPerSample = bps
  945.         .nBlockAlign = chs * (bps / 8)
  946.         .nAvgBytesPerSec = sr * .nBlockAlign
  947.     End With
  948. End Function
  949.  
  950. '''''''''''''''''''''''''''''''''''''''''''''''
  951. '''''''''''''''''''''''''''''''''''''''''''''''
  952. '''''''''''''''''''''''''''''''''''''''''''''''
  953.  
  954. Private Function IsValidFile( _
  955.     ByVal strFile As String _
  956. ) As Boolean
  957.  
  958.     Dim hInp    As hFile
  959.  
  960.     hInp = FileOpen(strFile, _
  961.                     GENERIC_READ, _
  962.                     FILE_SHARE_READ)
  963.  
  964.     IsValidFile = hInp.handle <> INVALID_HANDLE
  965.     FileClose hInp
  966. End Function
  967.  
  968. Private Function FileOpen( _
  969.     ByVal strFile As String, _
  970.     Optional access As FILE_ACCESS_RIGHTS = GENERIC_READ Or GENERIC_WRITE, _
  971.     Optional share As FILE_SHARE_RIGHTS = FILE_SHARE_READ Or FILE_SHARE_WRITE, _
  972.     Optional method As FILE_OPEN_METHOD = OPEN_EXISTING _
  973. ) As hFile
  974.  
  975.     FileOpen.handle = CreateFile(strFile, _
  976.                                  access, _
  977.                                  share, _
  978.                                  ByVal 0&, _
  979.                                  method, _
  980.                                  0, 0)
  981.  
  982.     FileOpen.path = strFile
  983. End Function
  984.  
  985. Private Sub FileClose( _
  986.     filehandle As hFile _
  987. )
  988.  
  989.     CloseHandle filehandle.handle
  990.     filehandle.handle = INVALID_HANDLE
  991.     filehandle.path = vbNullString
  992. End Sub
  993.  
  994. Private Function FileRead( _
  995.     filehandle As hFile, _
  996.     ByVal ptr As Long, _
  997.     ByVal bytes As Long _
  998. ) As Long
  999.  
  1000.     Dim dwRead  As Long
  1001.     Dim lngRet  As Long
  1002.  
  1003.     If filehandle.handle = INVALID_HANDLE Then
  1004.         Exit Function
  1005.     End If
  1006.  
  1007.     lngRet = ReadFile(filehandle.handle, _
  1008.                       ByVal ptr, _
  1009.                       bytes, _
  1010.                       dwRead, _
  1011.                       0&)
  1012.  
  1013.     If lngRet = 1 Then
  1014.         FileRead = dwRead
  1015.     Else
  1016.         FileRead = -1
  1017.     End If
  1018. End Function
  1019.  
  1020. Private Function FileWrite( _
  1021.     filehandle As hFile, _
  1022.     ByVal ptr As Long, _
  1023.     ByVal bytes As Long _
  1024. ) As Long
  1025.  
  1026.     Dim dwWritten   As Long
  1027.     Dim lngRet      As Long
  1028.  
  1029.     If filehandle.handle = INVALID_HANDLE Then
  1030.         Exit Function
  1031.     End If
  1032.  
  1033.     lngRet = WriteFile(filehandle.handle, _
  1034.                        ByVal ptr, _
  1035.                        bytes, _
  1036.                        dwWritten, _
  1037.                        0&)
  1038.  
  1039.     If lngRet = 1 Then
  1040.         FileWrite = dwWritten
  1041.     Else
  1042.         FileWrite = -1
  1043.     End If
  1044. End Function
  1045.  
  1046. Private Function FileSeek( _
  1047.     filehandle As hFile, _
  1048.     ByVal bytes As Long, _
  1049.     ByVal method As SEEK_METHOD _
  1050. ) As Long
  1051.  
  1052.     FileSeek = SetFilePointer(filehandle.handle, _
  1053.                               bytes, _
  1054.                               0, _
  1055.                               method)
  1056. End Function
  1057.  
  1058. Private Function FilePosition( _
  1059.     filehandle As hFile _
  1060. ) As Long
  1061.  
  1062.     FilePosition = FileSeek(filehandle, _
  1063.                             0, _
  1064.                             FILE_CURRENT)
  1065. End Function
  1066.  
  1067. Private Function FileLength( _
  1068.     filehandle As hFile _
  1069. ) As Long
  1070.  
  1071.     FileLength = GetFileSize(filehandle.handle, 0)
  1072. End Function
  1073.  
  1074. Private Function FileEnd( _
  1075.     filehandle As hFile _
  1076. ) As Boolean
  1077.  
  1078.     FileEnd = FilePosition(filehandle) >= FileLength(filehandle)
  1079. End Function
  1080.