Devo fare un applicazione (activeX visual basic, pilotato poi da una pagina asp) che deve riprodurre, mixare, attaccare dei file wave, poi convertirli in mp3.
Per la conversione da wave a mp3 non ci son problemi, nemmeno per il mixaggio di file wave, ho trovato già activeX che fanno al caso mio.
Per la riproduzione uso le directX 8, più precisamente le directSound, e anche qua non ci sono problemi.
I problemi li ho nell'attaccare due file wave. Si presuppone che i due file abbiano le stesse caratteristiche, bit rate, numero canali, frequenza, ecc...
Voglio creare una procedura che dati in ingresso i due file wave, mi crei un terzo file con i due file attacccati, uno dietro l'altro, senza pause in mezzo e nessun effetto sonoro.
Ho trovato due vie, una usando i buffer di directSound, e un'altra a più basso livello che crea il file wave byte per byte. In entrambi i casi uso una procedura che mi estrae l'header del file wave (una struttura di alcuni byte contente le informazioni sul file) e la parte puramente di dati, così da avere una variabile con l'header del file (che è più o meno lo stesso per i due file wave, se hanno le stesse caratteristiche) e un array di byte che contiene la parte dati del file wave (in pratica le note da suonare). A questo punto non mi resta che attaccare le due parti dati dei due file wave assieme, e poi riassemblare il tutto con l'header (contente le informazioni sul bit rate, numero canali, frequenza,...) opportunamente modificato (occorre cambiare il campo che indica la lunghezza della parte dati). Per fare ciò ho provato con due diverse strade:
- directSound (attaccaFile). Creo un buffer audio vuoto con le caratteristiche del file wave (che ricavo dall'header) e ci scrivo dentro la parte dati completa dei due blocchi di dati wave. Poi con un metodo del buffer salvo il tutto su un file su disco.
- file binario (attaccaFile2). Stando a basso livello, tratto il tutto come file binario. Mi riscrivo da zero l'header del file wave, inserendovi le caratteristiche ricavate in precedenza, poi inserisco la parte di dati del primo file, poi inserisco la parte di dati del secondo file. Ho così creato il file wave. A differenza del metodo di prima, che mi scrive l'header in automatico, qua mi creo il file wave direttamente da zero.
In via teorica mi sembra tutto corretto, in pratica invece mi funziona solo se uso dei file wave 8bit mono, cioè coi parametri del waveFormat
- nChannels = 1 (mono)
- nAvgBytesPerSec = 8 (8 bit)
Con qualsiasi altro tipo di campionamento del file wave, 16 bit, stereo, ecc... non mi funziona come dovrebbe. Mi riproduce correttamente solo la prima metà del file (cioè il primo file) e l'altro si sentono dei fruscii, stridolii, e del casino.
CODICE VB:
Private Type FileHeader
lRiff As Long
lFileSize As Long
lWave As Long
lFormat As Long
lFormatLength As Long
End Type
Private Type waveFormat
wFormatTag As Integer
nChannels As Integer
nSamplesPerSec As Long
nAvgBytesPerSec As Long
nBlockAlign As Integer
wBitsPerSample As Integer
End Type
Private Type ChunkHeader
lType As Long
lLen As Long
End Type
Private Type WAVETYPE
strHead As String * 12
strFormatID As String * 4
lngChunkSize As Long
intFormat As Integer
intChannels As Integer
lngSamplesPerSec As Long
lngAvgBytesPerSec As Long
intBlockAlign As Integer
intBitsPerSample As Integer
End Type
Global gudtHeader As WAVETYPE 'header del file wave
Global glngChunkSize1 As Long 'dimensione primo blocco dati wav
Global gbytData1() As Byte 'primo blocco dati wav
Global glngChunkSize2 As Long 'dimensione secondo blocco dati wav
Global gbytData2() As Byte 'secondo blocco dati wav
'DirectX Variable
Dim mobjDX As DirectX8
Dim mobjDS As DirectSound8
Global objBuffer As DirectSoundSecondaryBuffer8
Public Sub Initialize(frmInit As Form)
'Initialize DirectSound
Set mobjDX = New DirectX8
Set mobjDS = mobjDX.DirectSoundCreate("")
'Set the DirectSound object's cooperative level (Priority gives us sole control)
mobjDS.SetCooperativeLevel frmInit.hWnd, DSSCL_PRIORITY
End Sub
Sub ExtractWaveData1(strFileName As String, lngOffset As Long)
Dim intWAVFile As Integer
Dim i As Long
Dim strTemp As String * 4
Dim blnFound As Boolean
'Open the wave
intWAVFile = FreeFile()
Open strFileName For Binary Access Read Lock Write As intWAVFile
'Get the header info
Get intWAVFile, lngOffset, gudtHeader
'Find the "data" portion of the file
For i = lngOffset To LOF(intWAVFile)
Get intWAVFile, i, strTemp
If strTemp = "data" Then
blnFound = True
Exit For
End If
Next i
'Ensure this is a wave file
If blnFound = False Then
MsgBox "Invalid wave data.", vbCritical, "Invalid Wave"
Close intWAVFile
Exit Sub
End If
'Get the data information
Get intWAVFile, , glngChunkSize1
ReDim gbytData1(glngChunkSize1)
Get intWAVFile, , gbytData1
Close intWAVFile
End Sub
Sub ExtractWaveData2(strFileName As String, lngOffset As Long)
Dim intWAVFile As Integer
Dim i As Long
Dim strTemp As String * 4
Dim blnFound As Boolean
'Open the wave
intWAVFile = FreeFile()
Open strFileName For Binary Access Read Lock Write As intWAVFile
'Get the header info
Get intWAVFile, lngOffset, gudtHeader
'Find the "data" portion of the file
For i = lngOffset To LOF(intWAVFile)
Get intWAVFile, i, strTemp
If strTemp = "data" Then
blnFound = True
Exit For
End If
Next i
'Ensure this is a wave file
If blnFound = False Then
MsgBox "Invalid wave data.", vbCritical, "Invalid Wave"
Close intWAVFile
Exit Sub
End If
'Get the data information
Get intWAVFile, , glngChunkSize2
ReDim gbytData2(glngChunkSize2)
Get intWAVFile, , gbytData2
Close intWAVFile
End Sub
Public Sub AttaccaFile(file1 As String, file2 As String, fileSomma As String)
'gli vengono passati i nomi dei file da attaccare (file1 e file2) e il file creato su cui attaccarli (filesomma)
Dim udtBufferDesc As DSBUFFERDESC
Dim intBinaryFile As Integer
Dim gbytDataTot() As Byte
'Extract the wave info
ExtractWaveData1 file1, 1
ExtractWaveData2 file2, 1
'ora ho in variabili globali la dimensione e i blocchi di dati wav, e in gutHeader l'header del file
'(si presuppone che i due file da attaccare abbiano le stesse caratteristiche)
'Set the Wave Format
With udtBufferDesc.fxFormat
.nFormatTag = gudtHeader.intFormat
.nChannels = gudtHeader.intChannels 'funziona solo se questo è = 1
.lSamplesPerSec = gudtHeader.lngSamplesPerSec
.nBitsPerSample = gudtHeader.intBitsPerSample 'funziona solo se questo è = 8
.nBlockAlign = gudtHeader.intBlockAlign
.lAvgBytesPerSec = gudtHeader.lngAvgBytesPerSec
End With
udtBufferDesc.lBufferBytes = glngChunkSize1 + glngChunkSize2
'apro un file binario per scriverci i due blocchi di dati wav
intBinaryFile = FreeFile
Open App.Path & "\BINARY.DAT" For Binary Access Write Lock Write As intBinaryFile
'Store the data in the file
Put intBinaryFile, 1, gbytData1
Put intBinaryFile, , gbytData2
'Close the file
Close intBinaryFile
ReDim gbytDataTot(glngChunkSize1 + glngChunkSize2)
intBinaryFile = FreeFile
Open App.Path & "\BINARY.DAT" For Binary Access Read Lock Write As intBinaryFile
'metto in gbytDataTot i due blocchi di wav attaccati
Get intBinaryFile, 1, gbytDataTot
'Close the file
Close intBinaryFile
'ora in gbytDataTot ho la parte dati dei due file wave attaccati, non mi resta che attaccarci l'header con directSound
'Create the buffer
Set objBuffer = Nothing
Set objBuffer = mobjDS.CreateSoundBuffer(udtBufferDesc)
'Load the buffer with data
objBuffer.WriteBuffer 0, glngChunkSize1 + glngChunkSize2, gbytDataTot(0), DSBLOCK_ENTIREBUFFER
'play
objBuffer.Play DSBPLAY_DEFAULT
'salvo il risultato sul file fileSomma
objBuffer.SaveToFile fileSomma
End Sub
Public Sub AttaccaFile2(file1 As String, file2 As String, fileSomma As String)
'gli vengono passati i nomi dei file da attaccare (file1 e file2) e il file creato su cui attaccarli (filesomma)
Dim fh As FileHeader
Dim wf As waveFormat
Dim ch As ChunkHeader
Dim dsBuf As DSBUFFERDESC
Dim intBinaryFile As Integer
Dim gbytDataTot() As Byte
Dim fsize As Long
'Extract the wave info
ExtractWaveData1 file1, 1
ExtractWaveData2 file2, 1
'ora ho in variabili globali la dimensione e i blocchi di dati wav, e in gutHeader l'header del file
'(si presuppone che i due file da attaccare abbiano le stesse caratteristiche)
'creo il file che conterrà i due wave attaccati
intBinaryFile = FreeFile
Open fileSomma For Binary Access Write As intBinaryFile
With fh
.lRiff = &H46464952 ' <RIFF> chunk tag
.lFileSize = 0 ' Will get later
.lWave = &H45564157 ' <WAVE> chunk tag
.lFormat = &H20746D66 ' <fmt > chunk tag
.lFormatLength = Len(wf)
End With
Put #1, , fh
With wf
.wFormatTag = WAVE_FORMAT_PCM
.nChannels = gudtHeader.intChannels 'funziona solo se questo è = 1
.nSamplesPerSec = gudtHeader.lngSamplesPerSec
.wBitsPerSample = gudtHeader.intBitsPerSample 'funziona solo se questo è = 8
.nBlockAlign = gudtHeader.intBlockAlign
.nAvgBytesPerSec = gudtHeader.lngAvgBytesPerSec
End With
Put #1, , wf
ch.lType = &H61746164 ' <data> chunk tag
Put #1, , ch
' Write file size.
fsize = Len(fh) + Len(wf) + Len(ch) + glngChunkSize1 + glngChunkSize2
Put #1, 5, fsize
' Rewrite data chunk header with size.
ch.lLen = glngChunkSize1 + glngChunkSize2
Put #1, Len(fh) + Len(wf) + 1, ch
'Store the data in the file
Put intBinaryFile, Len(fh) + Len(wf) + Len(ch) + 1, gbytData1
Put intBinaryFile, , gbytData2
'Close the file
Close intBinaryFile
'Create the buffer to play file
dsBuf.lFlags = DSBCAPS_CTRLFREQUENCY Or DSBCAPS_CTRLPAN Or DSBCAPS_CTRLVOLUME
Set objBuffer = Nothing
Set objBuffer = mobjDS.CreateSoundBufferFromFile(fileSomma, dsBuf)
'play
objBuffer.Play DSBPLAY_DEFAULT
End Sub
Public Sub Terminate()
'Terminate all
Set mobjDS = Nothing
Set mobjDX = Nothing
Set objBuffer = Nothing
End Sub