Prima di iniziare faccio una critica a microsoft che nonostante abbia inventato un sistema eccellente per registrare i wav poteva automatizzare meglio e soprattutto degnarsi di realizzare un tutorial chiaro ed esaustivo. Ora iniziamo.
Per molte attività la possibilità di registrare sorgenti audio e video è fondamentale. DirectSound permette di registrare l'audio in formato wave tramite la scheda audio. Che il suono provenga da microfono o sia interna al sistema non fa differenza(ad esempio registrare un sorgente midi che sta girando nel pc). Il miglioramento rispetto al sistema presente in directX8 si nota soprattutto nel fatto che stavolta i dati vengono scritti durante la registrazione rendendo possibile registrare file wav di qualsiasi lunghezza e volendo registrare e sentire direttamente il suono (con appena un pò di differita). Nota negativa è che dovremo gestire il processo di salvataggio in modo diretto quindi un pò più complicato del solito.
dimensione campione: sono i bits usati per il suono, 8 o 16. Dim cBuff As CaptureBuffer 'buffer di registrazione
Questa variabile è il buffer di registrazione. Le altre variabili che ci serviranno sono
Dim th As New Threading.Thread(AddressOf checkD) 'thread che salva ad intervalli regolari i dati nel file
Public PositionNotify(16) As BufferPositionNotify 'posizioni di notifica
Public appNotify As Notify 'notificatore
Public NotificationEvent As AutoResetEvent = Nothing 'gestore delle attese nel thread
Dim captureBufferSize As Integer 'dimensione del buffer
Dim notifySize As Integer 'dimensione della notifica
Public NextCaptureOffset As Integer = 0 'prossimo intervallo di cattura
Private WaveFile As FileStream = Nothing 'file wave
Private Writer As BinaryWriter = Nothing 'scrittore del file
Private SampleCount As Integer = 0 'bit catturati
Private isRecording As Boolean = False
Const nNotification As Integer = 16
verranno spiegate man mano che vengono adoperate
Per avviare la registrazione occorre creare il buffer ed impostare le variabili che ci serviranno
'descrizione del capBuffer
Dim capD As CaptureBufferDescription
Dim captureF As New Capture(guida)
'formato
Dim format As WaveFormat
format.BitsPerSample = bitS
format.Channels = canali
format.SamplesPerSecond = frequenza
format.BlockAlign = CShort(format.Channels * (format.BitsPerSample / 8))
format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond
format.FormatTag = WaveFormatTag.Pcm
capD.Format = format
'creazione del fiff
CreateRIFF(FileName, format)
notifySize = IIf(1024 > format.AverageBytesPerSecond / 8, 1024, format.AverageBytesPerSecond / 8)
notifySize -= notifySize Mod format.BlockAlign
' imposta la dimensione del buffer
captureBufferSize = notifySize * nNotification
' Crea il capture buffer
capD.BufferBytes = captureBufferSize
cBuff = New CaptureBuffer(capD, captureF)
NotificationEvent = New AutoResetEvent(False)
'creazione notifica
Dim i As Integer
For i = 0 To nNotification - 1
PositionNotify(i).Offset = notifySize * i + notifySize - 1
PositionNotify(i).EventNotifyHandle = NotificationEvent.Handle
Next
appNotify = New Notify(cBuff)
'imposta il notificatore affinchè generi l'evento nel thread
appNotify.SetNotificationPositions(PositionNotify, nNotification)
impostato il formato creiamo l'intestazione del file RIFF (che spieghiamo qui sotto). Il notifysize è la dimensione del buffer di notifica per la cattura. In pratica se registriamo 200 mega di wave questi non saranno accumulati dal capture buffer ma scaricati piano piano nel file. Il notifysize è appunto il calcolo di ogni quanto scaricare i dati nel file. Guida è il numero univoco che descrive la scheda audio da usare (spesso c'è ne sono diverse). da leggere con un processo di enumerazione
Dim devices As New CaptureDevicesCollection
Dim info As DeviceInformation
For Each info In devices
info.DriverGuid
Next info
info contiene tutte le caratteristiche. DriverGuid è la guida da usare
Le altre caratteristiche vengono calcolate come mostrato nel codice
per avviare la registrazione si avvia il thread e il capture buffer
cBuff.Start(True)
th.Start() 'inizia il thread
Sub checkD()
'thread che scarica sul disco i dati
While isRecording
NotificationEvent.WaitOne(Timeout.Infinite, True)
saveRecord()
End While
End Sub
Questa sub chiamata nel thread crea un attesa nel notificationEvent (che è collegato al buffer di cattura). Quando il capturebuffer è pronto per scaricare un pò di dati sblocca l'evento e da li chiamo la routine di registrazione.
File Wave
Come tutti i file esistenti il file wave è formato da un certo numero di descrittori e dai dati. I descrittori si trovano in cima al file e devono venire scritti prima di iniziare il processo di scrittura. Questa ruotine presa dall'esempio microsoft e modificata da me per adattarla al programma imposta il filestream ed il binaryWriter per poter scrivere dati binari nel file "in corsa" (ossia dinamicamente).
Sub CreateRIFF(ByVal filename As String, ByVal inputFormat As WaveFormat)
'*************************************************************************
'
'
' Here is where the file will be created. A
'
' wave file is a RIFF file, which has chunks
'
' of data that describe what the file contains.
'
' A wave RIFF file is put together like this:
'
'
'
' The 12 byte RIFF chunk is constructed like this:
'
' Bytes(0 - 3) 'R' 'I' 'F' 'F'
'
' Bytes 4 - 7 : Length of file, minus the first 8 bytes of the RIFF description.
'
' (4 bytes for "WAVE" + 24 bytes for format chunk length +
'
' 8 bytes for data chunk description + actual sample data size.)
'
' Bytes(8 - 11) 'W' 'A' 'V' 'E'
'
'
'
' The 24 byte FORMAT chunk is constructed like this:
'
' Bytes(0 - 3) 'f' 'm' 't' ' '
'
' Bytes 4 - 7 : The format chunk length. This is always 16.
'
' Bytes 8 - 9 : File padding. Always 1.
'
' Bytes 10- 11: Number of channels. Either 1 for mono, or 2 for stereo.
'
' Bytes 12- 15: Sample rate.
'
' Bytes 16- 19: Number of bytes per second.
'
' Bytes 20- 21: Bytes per sample. 1 for 8 bit mono, 2 for 8 bit stereo or
'
' 16 bit mono, 4 for 16 bit stereo.
'
' Bytes 22- 23: Number of bits per sample.
'
'
'
' The DATA chunk is constructed like this:
'
' Bytes(0 - 3) 'd' 'a' 't' 'a'
'
' Bytes 4 - 7 : Length of data, in bytes.
'
' Bytes 8 -...: Actual sample data.
'
'
'
'**************************************************************************
' Open up the wave file for writing.
WaveFile = New FileStream(filename, FileMode.Create)
Writer = New BinaryWriter(WaveFile)
' Set up file with RIFF chunk info.
Dim ChunkRiff As Char() = {"R", "I", "F", "F"}
Dim ChunkType As Char() = {"W", "A", "V", "E"}
Dim ChunkFmt As Char() = {"f", "m", "t", " "}
Dim ChunkData As Char() = {"d", "a", "t", "a"}
Dim shPad As Short = 1 ' File padding
Dim nFormatChunkLength As Integer = &H10 ' Format chunk length.
Dim nLength As Integer = 0 ' File length, minus first 8 bytes of RIFF description. This will be filled in later.
Dim shBytesPerSample As Short = 0 ' Bytes per sample.
' Figure out how many bytes there will be per sample.
If 8 = inputFormat.BitsPerSample And 1 = inputFormat.Channels Then
shBytesPerSample = 1
ElseIf 8 = inputFormat.BitsPerSample And 2 = inputFormat.Channels Or (16 = inputFormat.BitsPerSample And 1 = inputFormat.Channels) Then
shBytesPerSample = 2
ElseIf 16 = inputFormat.BitsPerSample And 2 = inputFormat.Channels Then
shBytesPerSample = 4
End If
' Fill in the riff info for the wave file.
Writer.Write(ChunkRiff)
Writer.Write(nLength)
Writer.Write(ChunkType)
' Fill in the format info for the wave file.
Writer.Write(ChunkFmt)
Writer.Write(nFormatChunkLength)
Writer.Write(shPad)
Writer.Write(inputFormat.Channels)
Writer.Write(inputFormat.SamplesPerSecond)
Writer.Write(inputFormat.AverageBytesPerSecond)
Writer.Write(shBytesPerSample)
Writer.Write(inputFormat.BitsPerSample)
' Now fill in the data chunk.
Writer.Write(ChunkData)
Writer.Write(CInt(0)) ' The sample length will be written in later.
End Sub 'CreateRIFF
La descrizione di come è fatto un file wave può essere approfondita in numerosi siti ma in definitiva questo è tutto. Ora dobbiamo scrivere i dati.
Sub saveRecord()
Dim CaptureData As Byte() = Nothing
Dim ReadPos As Integer
Dim CapturePos As Integer
Dim LockSize As Integer
cBuff.GetCurrentPosition(CapturePos, ReadPos)
LockSize = ReadPos - NextCaptureOffset
If LockSize < 0 Then
LockSize += captureBufferSize
End If
LockSize -= LockSize Mod notifySize
If 0 = LockSize Then
Return
End If
' legge i dati
CaptureData = CType(cBuff.Read(NextCaptureOffset, GetType(Byte), LockFlag.None, LockSize), Byte())
'scrivi sul file
Writer.Write(CaptureData, 0, CaptureData.Length)
SampleCount += CaptureData.Length
NextCaptureOffset += CaptureData.Length
NextCaptureOffset = NextCaptureOffset Mod captureBufferSize
End Sub
Come vedete vengono letti i dati dal cBuff e scritti sul file. Le altre variabile spostano in avanti il prossimo intervallo di lettura.
Per fermare la registrazione
cBuff.Stop()
isRecording = False
saveRecord()
Writer.Seek(4, SeekOrigin.Begin) 'porta il writer al descrittore di lunghezza del file RIFF
Writer.Write(CInt(SampleCount + 36)) ' scrive la lunghezza del file meno i primi 8 bytes del descrittore RIFF
Writer.Seek(40, SeekOrigin.Begin) ' porta il writer al descrittore della lunghezza dei dati
Writer.Write(SampleCount) ' scrive la lunghezza dei dati
Writer.Close() ' chiude il writer e tutto il resto
Writer = Nothing
WaveFile = Nothing
NotificationEvent.Set() 'termina il processo di notifica
Il writer scrive gli ultimi dati e riempie alcune zone all'inizio del file lasciate in sospeso (ossia la lunghezza del file e dei dati). Il file è ora chiuso e pronto ad essere ascoltato.
Ultime note: