GFWaveReadWrite/GFWaveReadWritecls.cls
VERSION 1.0 CLASS
BEGIN
MultiUse = ‑1 'True
END
Attribute VB_Name = "GFWaveReadWritecls"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
'(c)2003 by Louis. Functions to read and write wave files.
'Code partially taken from GFWaveSnipper ((c)2001 by Louis.).
'‑Note that the bit rate, sample rate and channel number must not
' be changed between reading and writing;
'‑Use one class instance per file to read/write;
'‑Not finished yet, data length must be constant.
'
'GetWAVEFORMATEX_EXFromWaveName
'Private Declare Sub DLLGetWaveDataArrayFromWaveFileArray Lib "Wave Snipper.dll" Alias "GetWaveDataArrayFromWaveFileArray" (ByRef WaveDataArray As Integer, ByVal WaveDataArrayIndexMax As Long, ByRef WaveFileArray As Byte, ByVal WaveFileArrayIndexMax As Long, ByVal WaveBitsPerSample As Integer, ByVal WaveChannelNumber As Integer) 'now in VB
'Private Declare Sub DLLGetWaveFileArrayFromWaveDataArray Lib "Wave Snipper.dll" Alias "GetWaveFileArrayFromWaveDataArray" (ByRef WaveFileArray As Byte, ByVal WaveFileArrayIndexMax As Long, ByRef WaveDataArray As Integer, ByVal WaveDataArrayIndexMax As Long, ByVal WaveBitsPerSample As Integer, ByVal WaveChannelNumber As Integer) 'now in VB
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal dwLength As Long)
'GetWAVEFORMATEX_EX
Private Type FileHeader
lRiff As Long
lFileSize As Long
lWave As Long
lFormat As Long
lFormatLength As Long
End Type
'GetWAVEFORMATEX_EXFromWaveName
Private Type WaveFormat
wFormatTag As Integer
nChannels As Integer
nSamplesPerSec As Long
nAvgBytesPerSec As Long
nBlockAlign As Integer
wBitsPerSample As Integer
End Type
'GetWAVEFORMATEX_EXFromWaveName
Private Type ChunkHeader
lType As Long
lLen As Long
End Type
'GetWAVEFORMATEX_EXFromWaveName
Private Type WAVEFORMATEX_EX 'extension of WAVEFORMATEX to be used by WaveSnipper
lAvgBytesPerSec As Long
lExtra As Long
lSamplesPerSec As Long
nBitsPerSample As Integer
nBlockAlign As Integer
nChannels As Integer
nFormatTag As Integer
nSize As Integer
WaveName As String 'name of file wave file data was read out of
WaveDataStartPos As Long 'start pos of WaveData in wave file
WaveDataChunkHeaderStartPos As Long 'start pos of data chunk containing wave file data size information
WaveDataChunkHeaderVar As ChunkHeader 'data chunk containing wave file data size
WaveNameLength As Long 'size of related file (including header)
WaveNameFileHeaderVar As FileHeader 'file header containing file information (e.g. file size)
End Type
'other
Dim WAVEFORMATEX_EXVar As WAVEFORMATEX_EX
Dim WaveDataStartPosGlobal As Long 'offset of wave file data in wave file (after header)
'***INTERFACE FUNCTIONS***
Public Function GFWaveReadWrite_GetWaveDataArray(ByRef WaveDataArray() As Integer, ByRef WaveDataNumber As Long, ByVal WaveName As String) As Boolean
'on error resume next 'returns True for success or False for error
Dim WaveFileArray() As Byte 'source array
Dim WaveFileArrayIndexMax As Long
'Dim WaveDataArray() As Integer 'target array
'Dim WaveDataArrayIndexMax As Long
'verify
If (Dir(WaveName) = "") Or (Right$(WaveName, 1) = "\") Or (Len(WaveName) = 0) Then
GFWaveReadWrite_GetWaveDataArray = False 'error
Exit Function
End If
'begin
WAVEFORMATEX_EXVar = GetWAVEFORMATEX_EXFromWaveName(WaveName)
Call WaveFileArray_Read(WaveFileArray(), WaveFileArrayIndexMax, WaveName, WAVEFORMATEX_EXVar.WaveDataStartPos, 1, WAVEFORMATEX_EXVar.WaveNameLength ‑ WAVEFORMATEX_EXVar.WaveDataStartPos + 1)
Call GetWaveDataArrayFromWaveFileArray(WaveDataArray(), WaveDataNumber, WaveFileArray(), WaveFileArrayIndexMax, WAVEFORMATEX_EXVar)
GFWaveReadWrite_GetWaveDataArray = True 'ok
Exit Function
End Function
Public Property Set CurrentBitRate() As Integer
'on error resume next
CurrentBitRate = WAVEFORMATEX_EXVar.nBitsPerSample
End Property
Public Property Set CurrentSampleRate() As Long
'on error resume next
CurrentSampleRate = WAVEFORMATEX_EXVar.lSamplesPerSec
End Property
Public Function GFWaveReadWrite_SetWaveDataArray(ByRef WaveDataArray() As Integer, ByVal WaveDataNumber As Long, ByVal WaveName As String) As Boolean
'on error resume next 'returns True for success or False for error
Dim WaveFileArray() As Byte 'source array
Dim WaveFileArrayIndexMax As Long
'Dim WaveDataArray() As Integer 'target array
'Dim WaveDataArrayIndexMax As Long
'verify
If (Dir(WaveName) = "") Or (Right$(WaveName, 1) = "\") Or (Len(WaveName) = 0) Then
GFWaveReadWrite_SetWaveDataArray = False 'error
Exit Function
End If
'begin
Call GetWaveFileArrayFromWaveDataArray(WaveFileArray(), WaveFileArrayIndexMax, WaveDataArray(), WaveDataNumber, WAVEFORMATEX_EXVar)
Call WaveFileArray_Write(WaveFileArray(), WaveFileArrayIndexMax, WaveName, WAVEFORMATEX_EXVar.WaveDataStartPos, 1)
GFWaveReadWrite_SetWaveDataArray = True 'ok
Exit Function
End Function
'***END OF INTERFACE FUNCTIONS***
'*************************************WAVEFILEARRAY*************************************
Private Sub WaveFileArray_Read(ByRef WaveFileArray() As Byte, ByRef WaveFileArrayIndexMax As Long, ByVal WaveName As String, ByVal WaveDataStartPos As Long, ByVal BlockReadStartPos As Long, ByVal BlockReadByteNumber As Long)
'on errorr resume next 'reads wave file data from a wave file
Dim WaveNameFileNumber As Integer
'
'NOTE: BlockReadStartPos = 1 means read from beginning of wave file data (after header),
'to read x seconds wave file data, use BlockReadByteNumber = #Channels * #Samples * (#Bits/8) * x.
'
'preset
WaveNameFileNumber = FreeFile(0)
If WaveNameFileNumber = 0 Then GoTo Error:
'verify
If (Dir(WaveName) = "") Or (Right$(WaveName, 1) = "\") Or (WaveName = "") Then 'verify
MsgBox "internal error in WaveFileArray_Read(): file not found !", vbOKOnly + vbExclamation
GoTo Error:
End If
'begin
Open WaveName For Binary As #WaveNameFileNumber
'preset (within Open...Close)
WaveDataStartPos = WaveDataStartPos + (BlockReadStartPos ‑ 1) 'where reading starts
WaveFileArrayIndexMax = BlockReadByteNumber
'verify
If (WaveDataStartPos < 1) Or (WaveDataStartPos > LOF(WaveNameFileNumber)) Then 'verify
MsgBox "internal error in WaveFileArray_Read(): wave file data offset invalid !", vbOKOnly + vbExclamation
Exit Sub 'error
End If
If (WaveFileArrayIndexMax < 1) Or (WaveFileArrayIndexMax > (LOF(WaveNameFileNumber) ‑ WaveDataStartPos + 1)) Then
MsgBox "internal error in WaveFileArray_Read(): wave file data read amount invalid !", vbOKOnly + vbExclamation
Exit Sub 'error
End If
'begin
ReDim WaveFileArray(1 To WaveFileArrayIndexMax) As Byte
Get #1, WaveDataStartPos, WaveFileArray()
Close #WaveNameFileNumber
Exit Sub
Error:
Close #WaveNameFileNumber 'make sure file is closed
WaveFileArrayIndexMax = 0 'reset (error)
ReDim WaveFileArray(1 To 1) As Byte 'reset
Exit Sub
End Sub
Private Sub WaveFileArray_Write(ByRef WaveFileArray() As Byte, ByRef WaveFileArrayIndexMax As Long, ByVal WaveName As String, ByVal WaveDataStartPos As Long, ByVal BlockWriteStartPos As Long)
'on error resume next 'writes wave file array to a file
Dim WaveNameFileNumber As Integer
'
'NOTE: set BlockWriteStartPos to 1 to write wave file data at beginning of file
'(after header).
'
'preset
WaveNameFileNumber = FreeFile(0)
If WaveNameFileNumber = 0 Then GoTo Error:
'verify
If (Right$(WaveName, 1) = "\") Or (WaveName = "") Then
MsgBox "internal error in WaveFileArray_Write(): file not found !", vbOKOnly + vbExclamation
Exit Sub 'error
End If
'begin
Open WaveName For Binary As #WaveNameFileNumber
'preset (within Open...Close)
WaveDataStartPos = WaveDataStartPos + BlockWriteStartPos ‑ 1 'where writing starts
'verify
If (WaveDataStartPos < 1) Or (WaveDataStartPos > LOF(WaveNameFileNumber)) Then
GoTo Error:
End If
'begin
Put #WaveNameFileNumber, WaveDataStartPos, WaveFileArray()
Close #WaveNameFileNumber
Exit Sub
Error:
Close #WaveNameFileNumber 'make sure file is closed
MsgBox "internal error in WaveFileArray_Write() !", vbOKOnly + vbExclamation
Exit Sub
End Sub
'*********************************END OF WAVEFILEARRAY**********************************
'********************************TRANSFORMING FUNCTIONS*********************************
'NOTE: Use the transforming functions to convert WaveFileArray data into
'WaveDataArray data (and vice versa). See also annotations in WSE dll.
Private Sub GetWaveDataArrayFromWaveFileArray(ByRef WaveDataArray() As Integer, ByRef WaveDataArrayIndexMax As Long, ByRef WaveFileArray() As Byte, ByVal WaveFileArrayIndexMax As Long, ByRef WAVEFORMATEX_EXVar As WAVEFORMATEX_EX)
'on error resume next
Dim TempWDA() As Integer
Dim TempWDAIndexMax As Long
'
'NOTE: this sub uses the VC function DLLGetWaveDataArrayFromWaveFileArray
'to convert the WaveFileArray() into the WaveDataArray().
'Note that resizing the WaveDataArray() must be done by this sub.
'
'resize array
'NOTE: WaveFileArray() and WaveFileArrayIndexMax are defined,
'WaveDataArray() and WaveDataArrayIndexMax are to be defined.
'
Select Case WAVEFORMATEX_EXVar.nBitsPerSample
Case 8
Select Case WAVEFORMATEX_EXVar.nChannels
Case 1
WaveDataArrayIndexMax = WaveFileArrayIndexMax
ReDim WaveDataArray(1 To WaveDataArrayIndexMax) As Integer
Case 2
WaveDataArrayIndexMax = WaveFileArrayIndexMax
ReDim WaveDataArray(1 To WaveDataArrayIndexMax) As Integer
End Select
Case 16
Select Case WAVEFORMATEX_EXVar.nChannels
Case 1
WaveDataArrayIndexMax = WaveFileArrayIndexMax / 2 'write amount is half of the read amount
ReDim WaveDataArray(1 To WaveDataArrayIndexMax) As Integer
Case 2
WaveDataArrayIndexMax = WaveFileArrayIndexMax / 2
ReDim WaveDataArray(1 To WaveDataArrayIndexMax) As Integer
End Select
End Select
'convert array
TempWDAIndexMax = WaveDataArrayIndexMax
ReDim TempWDA(1 To TempWDAIndexMax) As Integer
Call DLLGetWaveDataArrayFromWaveFileArray(TempWDA(), TempWDAIndexMax, WaveFileArray(), WaveFileArrayIndexMax, WAVEFORMATEX_EXVar.nBitsPerSample, WAVEFORMATEX_EXVar.nChannels)
Call CopyMemory(WaveDataArray(1), TempWDA(1), TempWDAIndexMax * Len(TempWDA(1)))
'end of converting array
Exit Sub
Error:
MsgBox "internal error in GeWaveDataArrayFromWaveFileArray() !", vbOKOnly + vbExclamation
Exit Sub
End Sub
Private Sub GetWaveFileArrayFromWaveDataArray(ByRef WaveFileArray() As Byte, ByRef WaveFileArrayIndexMax As Long, ByRef WaveDataArray() As Integer, ByVal WaveDataArrayIndexMax As Long, ByRef WAVEFORMATEX_EXVar As WAVEFORMATEX_EX)
'on error resume next
'
'NOTE: WaveFileArray() and WaveFileArrayIndexMax must be defined.
'
Select Case WAVEFORMATEX_EXVar.nBitsPerSample
Case 8
Select Case WAVEFORMATEX_EXVar.nChannels
Case 1
WaveFileArrayIndexMax = WaveDataArrayIndexMax
ReDim WaveFileArray(1 To WaveFileArrayIndexMax) As Byte
Case 2
WaveFileArrayIndexMax = WaveDataArrayIndexMax
ReDim WaveFileArray(1 To WaveFileArrayIndexMax) As Byte
End Select
Case 16
Select Case WAVEFORMATEX_EXVar.nChannels
Case 1
WaveFileArrayIndexMax = WaveDataArrayIndexMax * 2 'write amount is twice the read amount
ReDim WaveFileArray(1 To WaveFileArrayIndexMax) As Byte
Case 2
WaveFileArrayIndexMax = WaveDataArrayIndexMax * 2
ReDim WaveFileArray(1 To WaveFileArrayIndexMax) As Byte
End Select
End Select
'convert array
Call DLLGetWaveFileArrayFromWaveDataArray(WaveFileArray(), WaveFileArrayIndexMax, WaveDataArray(), WaveDataArrayIndexMax, WAVEFORMATEX_EXVar.nBitsPerSample, WAVEFORMATEX_EXVar.nChannels)
'end of converting array
End Sub
'*****************************END OF TRANSFORMING FUNCTIONS*****************************
'*********************************CONVERSION FUNCTIONS**********************************
'NOTE: use the conversion functions to convert wave file data into
'wave data used by WS (and reverse).
'NOTE: stereo data is saved in WDA in the following way:
'
'channel1 channel2
'xxxxxxxxxxyyyyyyyyyy
'|‑‑‑‑‑‑‑‑||‑‑‑‑‑‑‑‑|
'^ ^^ ^
'| || WaveDataArrayIndexMax
'| |WaveDataArrayIndexMax / 2
'| WaveDataArrayIndexMax / 2 ‑ 1
'0
'
Private Sub DLLGetWaveDataArrayFromWaveFileArray(ByRef WaveDataArray() As Integer, ByVal WaveDataArrayIndexMax As Long, ByRef WaveFileArray() As Byte, ByVal WaveFileArrayIndexMax As Long, ByVal WaveBitsPerSample As Integer, ByVal WaveChannelNumber As Integer)
'on error resume next
Dim WaveDataArrayWritePos As Long
Dim WaveFileArrayReadPos As Long 'use if Temp does not run up to WaveFileArrayIndexMax
Dim Temp As Long
Dim TempInt As Integer
'begin; 8 bit mono
If (WaveBitsPerSample = 8) And (WaveChannelNumber = 1) Then
For Temp = 1 To WaveFileArrayIndexMax
WaveDataArrayWritePos = WaveDataArrayWritePos + 1&
WaveDataArray(Temp) = (WaveFileArray(Temp) ‑ 128) * 256 'WSE uses 127, but then overflow
Next Temp
End If
'8 bit stereo
If (WaveBitsPerSample = 8) And (WaveChannelNumber = 2) Then
For Temp = 1 To (WaveFileArrayIndexMax / 2)
WaveDataArrayWritePos = WaveDataArrayWritePos + 1&
'left channel
WaveFileArrayReadPos = WaveFileArrayReadPos + 1&
WaveDataArray(WaveDataArrayWritePos) = (WaveFileArray(WaveFileArrayReadPos) ‑ 128) * 256
'right channel
WaveFileArrayReadPos = WaveFileArrayReadPos + 1&
WaveDataArray((WaveFileArrayIndexMax / 2) + (WaveDataArrayWritePos)) = (WaveFileArray(WaveFileArrayReadPos) ‑ 128) * 256
Next Temp
End If
'16 bit mono
If (WaveBitsPerSample = 16) And (WaveChannelNumber = 1) Then
For Temp = 1 To (WaveFileArrayIndexMax / 2)
WaveDataArrayWritePos = WaveDataArrayWritePos + 1&
'high order bit
WaveFileArrayReadPos = WaveFileArrayReadPos + 1&
Call CopyMemory(ByVal (VarPtr(TempInt)), WaveFileArray(WaveFileArrayReadPos), 1&)
'low order bit
WaveFileArrayReadPos = WaveFileArrayReadPos + 1&
Call CopyMemory(ByVal (VarPtr(TempInt) + 1&), WaveFileArray(WaveFileArrayReadPos), 1&)
'transfer Integer value
WaveDataArray(WaveDataArrayWritePos) = TempInt
Next Temp
End If
'16 bit stereo
'[not supported yet]
End Sub
Private Sub DLLGetWaveFileArrayFromWaveDataArray(ByRef WaveFileArray() As Byte, ByVal WaveFileArrayIndexMax As Long, ByRef WaveDataArray() As Integer, ByVal WaveDataArrayIndexMax As Long, ByVal WaveBitsPerSample As Integer, ByVal WaveChannelNumber As Integer)
'on error resume next
Dim Temp As Long
Dim OutputArrayWritePos As Long
'begin; 8 bit mono
If (WaveBitsPerSample = 8) And (WaveChannelNumber = 1) Then
For Temp = 1 To WaveDataArrayIndexMax
OutputArrayWritePos = OutputArrayWritePos + 1&
WaveFileArray(OutputArrayWritePos) = Int(WaveDataArray(Temp) / 256) + 128 'using Int() is important
Next Temp
End If
'8 bit stereo
If (WaveBitsPerSample = 8) And (WaveChannelNumber = 2) Then
For Temp = 1 To (WaveDataArrayIndexMax / 2)
OutputArrayWritePos = OutputArrayWritePos + 1&
WaveFileArray(OutputArrayWritePos) = Int(WaveDataArray(Temp) / 256) + 128
OutputArrayWritePos = OutputArrayWritePos + 1&
WaveFileArray(OutputArrayWritePos) = Int(WaveDataArray((WaveDataArrayIndexMax / 2) + Temp) / 256) + 128
Next Temp
End If
'16 bit mono
If (WaveBitsPerSample = 16) And (WaveChannelNumber = 1) Then
For Temp = 1 To WaveDataArrayIndexMax 'already half of original value
OutputArrayWritePos = OutputArrayWritePos + 1&
Call CopyMemory(WaveFileArray(OutputArrayWritePos), ByVal (VarPtr(WaveDataArray(Temp))), 1&)
OutputArrayWritePos = OutputArrayWritePos + 1&
Call CopyMemory(WaveFileArray(OutputArrayWritePos), ByVal (VarPtr(WaveDataArray(Temp)) + 1&), 1&)
Next Temp
End If
End Sub
'******************************END OF CONVERSION FUNCTIONS******************************
'***OTHER***
Private Function GetWAVEFORMATEX_EXFromWaveName(ByVal WaveName As String) As WAVEFORMATEX_EX
'on error resume next 'allocated wave file header
Dim FileHeaderVar As FileHeader
Dim WaveFormatVar As WaveFormat
Dim ChunkHeaderVar As ChunkHeader
Dim WaveDataChunkHeaderStartPos As Long
Dim WaveNameFileNumber As Integer
Dim WaveLoop As Integer
Dim Temp As Long
Dim TempByte As Byte
'
'NOTE: this sub was copied from the 'WaveTest' project (by Louis).
'For further information view html help in related test program directory
'or search www.microsoft.com for 'WAVEFORMATEX Visual Basic'.
'NOTE: view F:\DataEx\Coding (Other)\Format Information\Wave.htm
'(03‑13‑01) for chunk information.
'NOTE: the wave file consists of such‑called 'chunks' which all have
'a type and a length property. The data chunk is the one that contains
'the wave data, if the file size is changed the data chunk header must
'be changed, too.
'
'preset
WaveNameFileNumber = FreeFile(0)
If WaveNameFileNumber = 0 Then GoTo Error: 'verify
If (Dir(WaveName) = "") Or (Right$(WaveName, 1) = "\") Or (WaveName = "") Then GoTo Error: 'verify
'begin
Open WaveName For Binary As #WaveNameFileNumber
'read RIFF WAVE chunk
Get #WaveNameFileNumber, , FileHeaderVar 'will be used later
If Not (FileHeaderVar.lRiff = &H46464952) Then GoTo Error: 'verify 'RIFF' was found (Long value created out of 4 byte String)
If Not (FileHeaderVar.lWave = &H45564157) Then GoTo Error: 'verify 'WAVE' was found
If (FileHeaderVar.lFormatLength < 16) Then GoTo Error: 'verify wave file has PCM format
'read fmt (format) chunk
Get #WaveNameFileNumber, , WaveFormatVar
'search and read data chunk
WaveLoop = 0
For Temp = 1 To (FileHeaderVar.lFormatLength ‑ 16)
Get #WaveNameFileNumber, , TempByte
Next Temp
'read a chunk header
Get #WaveNameFileNumber, , ChunkHeaderVar
'check if chunk header is data chunk header
Do While Not ((ChunkHeaderVar.lType = &H61746164) Or (WaveLoop = 32767)) 'avoid endless loop
For Temp = 1 To ChunkHeaderVar.lLen
Get #WaveNameFileNumber, , TempByte
Next Temp
'read a chunk header
Get #WaveNameFileNumber, , ChunkHeaderVar
WaveLoop = WaveLoop + 1
Loop
'
'NOTE: the data chunk contains the type information &H61746164 and
'the length of the wave file data.
'
WaveDataChunkHeaderStartPos = (Seek(WaveNameFileNumber) ‑ Len(ChunkHeaderVar))
'all important chunks read
'
With GetWAVEFORMATEX_EXFromWaveName 'ok
.lAvgBytesPerSec = WaveFormatVar.nAvgBytesPerSec
.lExtra = 0
.lSamplesPerSec = WaveFormatVar.nSamplesPerSec
.nBitsPerSample = WaveFormatVar.wBitsPerSample
.nBlockAlign = WaveFormatVar.nBlockAlign
.nChannels = WaveFormatVar.nChannels
.nFormatTag = WaveFormatVar.wFormatTag
.WaveDataStartPos = Seek(WaveNameFileNumber)
.WaveDataChunkHeaderStartPos = WaveDataChunkHeaderStartPos
.WaveName = WaveName
.WaveNameLength = LOF(WaveNameFileNumber)
Call CopyMemory(.WaveDataChunkHeaderVar, ChunkHeaderVar, Len(ChunkHeaderVar))
Call CopyMemory(.WaveNameFileHeaderVar, FileHeaderVar, Len(FileHeaderVar))
End With
Close #WaveNameFileNumber
Exit Function
Error:
Close #WaveNameFileNumber 'make sure file is closed
'
With GetWAVEFORMATEX_EXFromWaveName 'error
.lAvgBytesPerSec = 0 'reset (error)
.lExtra = 0 'reset (error)
.lSamplesPerSec = 0 'reset (error)
.nBitsPerSample = 0 'reset (error)
.nBlockAlign = 0 'reset (error)
.nChannels = 0 'reset (error)
.nFormatTag = 0 'reset (error)
.WaveDataStartPos = 0 'reset (error)
.WaveDataChunkHeaderStartPos = 0 'reset (error)
.WaveName = "" 'reset (error)
.WaveNameLength = 0 'reset (error)
End With
'
MsgBox "internal error in GetWAVEFORMATEX_EXFromWaveName() !", vbOKOnly + vbExclamation
Exit Function
End Function
'***END OF MODULE***
[END OF FILE]