Joukko .WAV äänitiedoston käsittelyyn tarkoitettuja rutiineja VB6 kielellä.
- WAV tagien luku/kirjoitus
- äänen sävyn korjaus
- äänen normalisointi
- yms.
'=================================================== ' WaveFileModule: Procedures for handling wave files ' Created by Tapio Nordbo, 2001, Freeware '=================================================== Option Explicit Private Type bufferi bu(0 To 9999) As Integer ' buffer type 9000 * 16 bit End Type Private fil As Integer ' #file number Private i As Integer Private b As Byte ' to read one byte Private SamplesPerSec As Long ' these are the wave file header data Private FormatTag As Integer Private Channels As Integer Private AvgBytesPerSec As Long Private BlockAlign As Integer Private BitsPerSample As Integer Private Datasize As Long Private RIFF As String ' for chunk name Private buff As bufferi ' read buffer Private buff2 As bufferi ' write buffer Function EqualizeWave(sFil As String, sFil2 As String, g50 As Single, g200 As Single, g800 As Single, g3200 As Single, g12800 As Single, txtObj As Object, Modify_original As Boolean) As Boolean ' Enchance bass & treble, normalize volume to 16 bit max ' sFil: path to the file to be modified ' sFil2: path to the modified file ' g50 ... g12800 : gain of bandpass filters 50 Hz ... 12800 Hz ' txtObj: textfield for progress text (set txtOBJ = Form1.text2) ' Modify_original: True = original file will be modified, FALSE = a new result file Dim fil As Integer, fil2 As Integer '#filenumbers Dim Dataa As Long ' for datasize Dim k As Long Dim L As Single, R As Single ' left and right sample data Dim maxvolume As Single ' max sample volume found 'Static buff As bufferi ' read buffer 'Static buff2 As bufferi ' write buffer Dim i As Integer Dim datacount As Long ' counter for data Dim k2 As Long Dim fs, F ' filesystem, file Dim pit As Integer ' position in string Dim sfilname As String ' string for filename Dim a1 As Single, b1 As Single, LB As Single, RB As Single Dim a2 As Single, b2 As Single, LT As Single Dim a3 As Single, b3 As Single Dim a4 As Single, b4 As Single Dim a5 As Single, b5 As Single Dim LTT As Single, RT As Single, RTT As Single Dim sR() As String ' for split return array Dim L50 As Single, L200 As Single, L800 As Single, L3200 As Single, L12800 As Single Dim R50 As Single, R200 As Single, R800 As Single, R3200 As Single, R12800 As Single Dim L200H As Single, L800H As Single, L3200H As Single, L12800H As Single Dim R200H As Single, R800H As Single, R3200H As Single, R12800H As Single ' count filter coeff a1 = Exp(-2 * 3.14 * 50 / 44100) ' 50 Hz cut frequency b1 = 1 - a1 a2 = Exp(-2 * 3.14 * 200 / 44100) ' 200 Hz cut frequency b2 = 1 - a2 a3 = Exp(-2 * 3.14 * 800 / 44100) ' 800 Hz cut frequency b3 = 1 - a3 a4 = Exp(-2 * 3.14 * 3200 / 44100) ' 3200 Hz cut frequency b4 = 1 - a4 a5 = Exp(-2 * 3.14 * 12800 / 44100) ' 12800 Hz cut frequency b5 = 1 - a5 On Local Error Resume Next ' in case there is no sFil2 file Kill (sFil2) On Error GoTo ErrHand ' start error handler 'Err.Raise 6 ' for testing error ' shorten the file name, for lack of space If Len(sFil) Then sR() = Split(sFil, "\") sfilname = sR(UBound(sR)) End If 'initialize maxvolume = 0 fil = FreeFile Dataa = GetDataSize(sFil) ' music byte size ' copy headers to the new file Open sFil For Binary As #fil ' open original file at binary mode fil2 = FreeFile Open sFil2 For Binary As #fil2 For k = 1 To 44 Step 2 ' 44 byte header Get #fil, k, L Put #fil2, k, L Next k k = 45 ' music starts at byte 45 datacount = 0 ' pass one, find out the max volume after enchancements Do While datacount < Dataa txtObj.Text = Str(Int(datacount / Dataa * 50)) + " % of " + sfilname 'progress indication DoEvents ' give time for the main form to show the progress Get #fil, k, buff ' read 10000 integers = 5000 samples * 2 channels * 16 bits For i = 0 To 9998 Step 2 ' go thru the buffer L = CSng(buff.bu(i)) ' left channel value R = CSng(buff.bu(i + 1)) ' next is the right channel value L50 = a1 * L50 + b1 * L R50 = a1 * R50 + b1 * R L200H = a2 * L200H + b2 * L R200H = a2 * R200H + b2 * R L200 = a2 * L200 + b2 * (L - L200H) R200 = a2 * R200 + b2 * (R - R200H) L800H = a3 * L800H + b3 * L R800H = a3 * R800H + b3 * R L800 = a3 * L800 + b3 * (L - L800H) R800 = a3 * R800 + b3 * (R - R800H) L3200H = a4 * L3200H + b4 * L R3200H = a4 * R3200H + b4 * R L3200 = a4 * L3200 + b4 * (L - L3200H) R3200 = a4 * R3200 + b4 * (R - R3200H) L12800H = a5 * L12800H + b5 * L R12800H = a5 * R12800H + b5 * R L12800 = L - L12800H R12800 = R - R12800H L = g50 * L50 + g200 * L200 + g800 * L800 + g3200 * L3200 + g12800 * L12800 R = g50 * R50 + g200 * R200 + g800 * R800 + g3200 * R3200 + g12800 * R12800 datacount = datacount + 4 ' for bytes handled at a loop k = k + 4 If Abs(L) > maxvolume Then maxvolume = Abs(L) ' get the max of the samples after modifications If Abs(R) > maxvolume Then maxvolume = Abs(R) Next i Loop 'pass two, like pass one but now the max volume is limited and results go to the buff2 k = 45 datacount = 0 Do While datacount < Dataa txtObj.Text = Str(Int(50 + datacount / Dataa * 50)) + " % of " + sfilname 'can be commented out DoEvents Get #fil, k, buff k2 = k For i = 0 To 9998 Step 2 L = (CSng(buff.bu(i)) * 32000#) / (maxvolume + 1#) R = (CSng(buff.bu(i + 1)) * 32000#) / (maxvolume + 1#) L50 = a1 * L50 + b1 * L R50 = a1 * R50 + b1 * R L200H = a2 * L200H + b2 * L R200H = a2 * R200H + b2 * R L200 = a2 * L200 + b2 * (L - L200H) R200 = a2 * R200 + b2 * (R - R200H) L800H = a3 * L800H + b3 * L R800H = a3 * R800H + b3 * R L800 = a3 * L800 + b3 * (L - L800H) R800 = a3 * R800 + b3 * (R - R800H) L3200H = a4 * L3200H + b4 * L R3200H = a4 * R3200H + b4 * R L3200 = a4 * L3200 + b4 * (L - L3200H) R3200 = a4 * R3200 + b4 * (R - R3200H) L12800H = a5 * L12800H + b5 * L R12800H = a5 * R12800H + b5 * R L12800 = L - L12800H R12800 = R - R12800H L = (g50 * L50 + g200 * L200 + g800 * L800 + g3200 * L3200 + g12800 * L12800) R = (g50 * R50 + g200 * R200 + g800 * R800 + g3200 * R3200 + g12800 * R12800) If datacount < Dataa Then buff2.bu(i) = CInt(L) ' from buff to buff2 buff2.bu(i + 1) = CInt(R) Else buff2.bu(i) = 0 ' rest is zero silence buff2.bu(i + 1) = 0 End If datacount = datacount + 4 k = k + 4 Next i Put #fil2, k2, buff2 ' write buff2 to the file2 Loop Close #fil Close #fil2 CorrectAvgBytesPerSec (sFil2) 'Check and correct ' copy sfil2 to sfil and delete sfil2 Set fs = CreateObject("Scripting.FileSystemObject") Set F = fs.GetFile(sFil2) If Modify_original Then F.Copy sFil Set F = Nothing Set fs = Nothing If Modify_original Then Kill (sFil2) EqualizeWave = True ' return value of the function Exit Function ErrHand: ErrSub ' error handling sub rutine Resume Next End Function Function NormaliseWaveVolume(sFil As String, sFil2 As String, Megabass As Single, Treble As Single, txtObj As Object, Modify_original As Boolean) As Boolean ' Enchance bass & treble, normalize volume to 16 bit max ' sFil: path to the file to be modified ' sFil2: path to the modified file ' Megabass : multiplier for bass, 10 = 20 dB ' Treble : multiplier for treble, 10 = 20 dB ' txtObj: textfield for progress text (set txtOBJ = Form1.text2) ' Modify_original: True = original file will be modified, FALSE = a new result file Dim fil As Integer, fil2 As Integer '#filenumbers Dim Dataa As Long ' for datasize Dim k As Long Dim L As Single, R As Single ' left and right sample data Dim maxvolume As Single ' max sample volume found 'Static buff As bufferi ' read buffer 'Static buff2 As bufferi ' write buffer Dim i As Integer Dim datacount As Long ' counter for data Dim k2 As Long Dim fs, F ' filesystem, file Dim pit As Integer ' position in string Dim sfilname As String ' string for filename Dim a1 As Single, b1 As Single, LB As Single, RB As Single ' for bass filter Dim a2 As Single, b2 As Single, LT As Single ' for treble filter Dim LTT As Single, RT As Single, RTT As Single Dim sR() As String ' for split return array ' count filter coeff a1 = Exp(-2 * 3.14 * 50 / 44100) ' 50 Hz cut frequency b1 = 1 - a1 a2 = Exp(-2 * 3.14 * 5000 / 44100) ' 5000 Hz cut frequency b2 = 1 - a2 On Local Error Resume Next ' in case there is no sFil2 file Kill (sFil2) On Error GoTo ErrHand ' start error handler 'Err.Raise 6 ' for testing error ' shorten the file name, for lack of space If Len(sFil) Then sR() = Split(sFil, "\") sfilname = sR(UBound(sR)) End If 'initialize maxvolume = 0 fil = FreeFile Dataa = GetDataSize(sFil) ' music byte size ' copy headers to the new file Open sFil For Binary As #fil ' open original file at binary mode fil2 = FreeFile Open sFil2 For Binary As #fil2 For k = 1 To 44 Step 2 ' 44 byte header Get #fil, k, L Put #fil2, k, L Next k k = 45 ' music starts at byte 45 datacount = 0 ' pass one, find out the max volume after enchancements Do While datacount < Dataa txtObj.Text = Str(Int(datacount / Dataa * 50)) + " % of " + sfilname 'progress indication DoEvents ' give time for the main form to show the progress Get #fil, k, buff ' read 10000 integers = 5000 samples * 2 channels * 16 bits For i = 0 To 9998 Step 2 ' go thru the buffer If (Megabass > 0) Or (Treble > 0) Then L = CSng(buff.bu(i)) ' left channel value R = CSng(buff.bu(i + 1)) ' next is the right channel value LB = a1 * LB + b1 * L ' this is the bass filter (1st order low pass filter at 50 Hz) RB = a1 * RB + b1 * R L = Megabass * LB + L R = Megabass * RB + R LT = a2 * LT + b2 * L ' this is the treble filter (1st order high pass filter at 5000 Hz) RT = a2 * RT + b2 * R LTT = L - LT RTT = R - RT L = Treble * LTT + L R = Treble * RTT + R Else L = CSng(buff.bu(i)) ' case no filter R = CSng(buff.bu(i + 1)) End If datacount = datacount + 4 ' for bytes handled at a loop k = k + 4 If Abs(L) > maxvolume Then maxvolume = Abs(L) ' get the max of the samples after modifications If Abs(R) > maxvolume Then maxvolume = Abs(R) Next i Loop 'pass two, like pass one but now the max volume is limited and results go to the buff2 k = 45 datacount = 0 Do While datacount < Dataa txtObj.Text = Str(Int(50 + datacount / Dataa * 50)) + " % of " + sfilname 'can be commented out DoEvents Get #fil, k, buff k2 = k For i = 0 To 9998 Step 2 If (Megabass > 0) Or (Treble > 0) Then L = (CSng(buff.bu(i)) * 32000#) / (maxvolume + 1#) R = (CSng(buff.bu(i + 1)) * 32000#) / (maxvolume + 1#) LB = a1 * LB + b1 * L RB = a1 * RB + b1 * R L = Megabass * LB + L ' add megabass to left channel R = Megabass * RB + R ' add megabass to right channel LT = a2 * LT + b2 * L RT = a2 * RT + b2 * R LTT = L - LT RTT = R - RT L = Treble * LTT + L R = Treble * RTT + R Else L = (CSng(buff.bu(i)) * 32000#) / (maxvolume + 1#) R = (CSng(buff.bu(i + 1)) * 32000#) / (maxvolume + 1#) End If If datacount < Dataa Then buff2.bu(i) = CInt(L) ' from buff to buff2 buff2.bu(i + 1) = CInt(R) Else buff2.bu(i) = 0 ' rest is zero silence buff2.bu(i + 1) = 0 End If datacount = datacount + 4 k = k + 4 Next i Put #fil2, k2, buff2 ' write buff2 to the file2 Loop Close #fil Close #fil2 CorrectAvgBytesPerSec (sFil2) 'Check and correct ' copy sfil2 to sfil and delete sfil2 Set fs = CreateObject("Scripting.FileSystemObject") Set F = fs.GetFile(sFil2) If Modify_original Then F.Copy sFil Set F = Nothing Set fs = Nothing If Modify_original Then Kill (sFil2) NormaliseWaveVolume = True ' return value of the function Exit Function ErrHand: ErrSub ' error handling sub rutine Resume Next End Function Function CorrectAvgBytesPerSec(sFil As String) As Long ' corrects the AverageBytesPerSec value at wav file Dim X As Long Dim Y As Long Dim Z As Long On Error GoTo ErrHand If sFil = "" Then CorrectAvgBytesPerSec = 0 Exit Function End If If InStr(sFil, "*") Then CorrectAvgBytesPerSec = 0 Exit Function End If If Not CheckIfWaveFile(sFil) Then CorrectAvgBytesPerSec = 0 Exit Function End If X = GetNumberOfChannels(sFil) Y = GetSamplesPerSec(sFil) Z = GetBitsPerSample(sFil) If Z > 8 Then AvgBytesPerSec = X * 2 * Y ' 16 bit samples Else AvgBytesPerSec = X * Y ' 8 bit samples End If Open sFil For Binary As #fil Put #fil, 29, AvgBytesPerSec ' put to file, byte place 29 Get #fil, 29, AvgBytesPerSec CorrectAvgBytesPerSec = AvgBytesPerSec Close #fil Exit Function ErrHand: ErrSub Resume Next End Function Function GetBitsPerSample(sFil As String) As Integer ' returns the Bits per sample value of the wav file On Error GoTo ErrHand If sFil = "" Then GetBitsPerSample = 0 Exit Function End If If InStr(sFil, "*") Then GetBitsPerSample = 0 Exit Function End If If Not CheckIfWaveFile(sFil) Then GetBitsPerSample = 0 Exit Function End If fil = FreeFile Open sFil For Binary As #fil Get #fil, 35, BitsPerSample GetBitsPerSample = BitsPerSample Close #fil Exit Function ErrHand: ErrSub Resume Next End Function Function GetNumberOfChannels(sFil As String) As Integer 'returns the number of channels (mono=1 , stereo=2) On Error GoTo ErrHand If sFil = "" Then GetNumberOfChannels = 0 Exit Function End If If InStr(sFil, "*") Then GetNumberOfChannels = 0 Exit Function End If If Not CheckIfWaveFile(sFil) Then GetNumberOfChannels = 0 Exit Function End If fil = FreeFile Open sFil For Binary As #fil Get #fil, 23, Channels GetNumberOfChannels = Channels Close #fil Exit Function ErrHand: ErrSub Resume Next End Function Function GetAvgBytesPerSec(sFil As String) As Long ' returns the Average bytes per sec On Error GoTo ErrHand If sFil = "" Then GetAvgBytesPerSec = 0 Exit Function End If If InStr(sFil, "*") Then GetAvgBytesPerSec = 0 Exit Function End If If Not CheckIfWaveFile(sFil) Then GetAvgBytesPerSec = 0 Exit Function End If fil = FreeFile Open sFil For Binary As #fil Get #fil, 29, AvgBytesPerSec GetAvgBytesPerSec = AvgBytesPerSec Close #fil Exit Function ErrHand: ErrSub Resume Next End Function Function GetDataSize(sFil As String) As Long ' returns the data size, lenght of the sound data On Error GoTo ErrHand If sFil = "" Then GetDataSize = 0 Exit Function End If If InStr(sFil, "*") Then GetDataSize = 0 Exit Function End If If Not CheckIfWaveFile(sFil) Then GetDataSize = 0 Exit Function End If fil = FreeFile Open sFil For Binary As #fil Get #fil, 41, Datasize GetDataSize = Datasize Close #fil Exit Function ErrHand: ErrSub Resume Next End Function Function GetBlockAlign(sFil As String) As Integer On Error GoTo ErrHand If sFil = "" Then GetBlockAlign = 0 Exit Function End If If InStr(sFil, "*") Then GetBlockAlign = 0 Exit Function End If If Not CheckIfWaveFile(sFil) Then GetBlockAlign = 0 Exit Function End If fil = FreeFile Open sFil For Binary As #fil Get #fil, 33, BlockAlign GetBlockAlign = BlockAlign Close #fil Exit Function ErrHand: ErrSub Resume Next End Function Function GetSamplesPerSec(sFil As String) As Long 'returns the Samples per sec value On Error GoTo ErrHand If sFil = "" Then GetSamplesPerSec = 0 Exit Function End If If InStr(sFil, "*") Then GetSamplesPerSec = 0 Exit Function End If If Not CheckIfWaveFile(sFil) Then GetSamplesPerSec = 0 Exit Function End If fil = FreeFile Open sFil For Binary As #fil Get #fil, 25, SamplesPerSec GetSamplesPerSec = SamplesPerSec Close #fil Exit Function ErrHand: ErrSub Resume Next End Function Function CheckIfWaveFile(sFil As String) As Boolean On Error GoTo ErrHand If sFil = "" Then CheckIfWaveFile = False Exit Function End If If InStr(sFil, "*") Then CheckIfWaveFile = False Exit Function End If fil = FreeFile Open sFil For Binary As #fil 'Check if a RIFF file RIFF = "" For i = 1 To 4 Get #fil, i, b RIFF = RIFF + Chr(CLng(b)) Next i If RIFF <> "RIFF" Then CheckIfWaveFile = False Close #fil Exit Function End If 'Check if a Wave file RIFF = "" For i = 9 To 12 Get #fil, i, b RIFF = RIFF + Chr(CLng(b)) Next i If RIFF <> "WAVE" Then CheckIfWaveFile = False Close #fil Exit Function End If 'Check if a fmt chunk RIFF = "" For i = 13 To 16 Get #fil, i, b RIFF = RIFF + Chr(CLng(b)) Next i If RIFF <> "fmt " Then CheckIfWaveFile = False Close #fil Exit Function End If 'Check format tag Get #fil, 21, FormatTag If FormatTag <> 1 Then CheckIfWaveFile = False Close #fil Exit Function End If 'Check if a data chunk RIFF = "" For i = 37 To 40 Get #fil, i, b RIFF = RIFF + Chr(CLng(b)) Next i If RIFF <> "data" Then CheckIfWaveFile = False Close #fil Exit Function End If ' Is MS wave with data CheckIfWaveFile = True Close #fil Exit Function ErrHand: ErrSub Resume Next End Function Private Sub ErrSub() Dim msg As String ' Check for error, then show message. If Err.Number <> 0 Then msg = "Error # " & Str(Err.Number) & " was generated by " _ & Err.Source & " / WaveFileModule " & Chr(13) & Err.Description MsgBox msg, , "Error", Err.HelpFile, Err.HelpContext End If End Sub
Entä QB:llä
En usko että onnistuu kovin helpolla. Joidenkin lisäkirjastoje avulla sujuu...
Hieno!
Wow! Aika kiva koodi
Hyvältä vaikuttaa (en pääse kokeilemaan VBn puuttumisen takia, enkä sitä aio hankkia) Onko binääriä?
Kyllä tuo QB:lläkin melko varmasti onnistuu, eipä tuossa taida sinänsä mitään ihmekikkailuja olla (datan käsittelyä, tiedoston lukua/kirjoitusta jne). Kivat filtterit :-)
Aihe on jo aika vanha, joten et voi enää vastata siihen.