Deteccion de Sonido Con Vb.Net abril 16, 2012 ingsistele Programacion en .Net
Un detector de sonido es una practica aplicacion que se puede realizar implementando un microfono para determinar cualquier tipo de ruido que se produzca y posteriormente realizar cualquier actividad especifica al escuchar dicho sonido, existen dispositivos electronicos especializados para realizar dicha labor, sin embargo, en este tutorial se va a explicar una forma alternativa de llevarlo acabo con nuestro computador. para poder realizar esta aplicacion, vamos a hacer uso de la transformada rapida de fourier y de varias clases que nos permiten capturar la entrada de audio, generar la onda y determinar el umbral de aproximacion de la onda a partir del cual se decidira si existe sonido o no. La aplicacion tendra la siguiente interfaz:
Bien amigos, para comenzar debo aclara que para la realizacion de este software me he basado en varias aplicaciones que he conseguido util, como son: Grabador y activador de sonido con c# y Visualizador de sonido en c# ambos alojados en la pagina web CodeProject. Ahora elaboramos una presentacion como la siguiente en vb:
aqui, agregamos 1 picturebox, 2 botones, 1 TrackBar para graduar la sensibilidad de la captura de audio, 1 groupbox, 2 textbox y varios labels, se puede personalizar al gusto con otros picturebox tal y como se ve en la anterior imagen. Bien, acontinuacion es necesario crear las siguientes clases: * WaveOut.vb para procesar y presentar la salida del audio capturado. * WaveNative.vb Para realizar operaciones en el core con la tarjeta de audio. * WaveIn.vb Encargada de procesar el audio que esta entrando por el microfono. * SignalGenerator.vb Para crear la onda y si se desea se puede variar la forma de la onda por sinusoidal, triangular, cuadrada, etc. * FourierTransform.vb que es la que recibe la onda auditiva y la transforma en una descomposición en distintas frecuencias, * FifoStream.vb Para procesar el flujo de entrada y salida,
* AudioFrame.vb para realizar gran parte de la deteccion del audio y personalizar las graficas. Sabiendo ya las clases que vamos a necesitar, podemos proceder a codificar nuestro proyecto, en esta ocasion, solo voy escribir un fragmento de la clase AudioFrame ya que es la que vamos a utilizar en gran manera para interactuar con los elementos del formulario y es la que se encarga de determinar si hay sonido o no. En Dicho fragmento se estudiara solamente la subrutina proccess, tal y como se ve a continuacion: Clase AudioFrame.vb: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
Imports System.Drawing Imports System.Windows.Forms Namespace detector_de_sonido Class AudioFrame Private _canvasTimeDomain As Bitmap Private _canvasFrequencyDomain As Bitmap Private _waveLeft As Double() Private _waveRight As Double() Private _fftLeft As Double() Private _fftRight As Double() Private _signalGenerator As SignalGenerator Private _isTest As Boolean = False Public IsEventActive As Boolean = False Public IsDetectingEvents As Boolean = False Public AmplitudeThreshold As Integer 'valor original de la amplitud=16384 Public Sub New(ByVal isTest As Boolean) _isTest = isTest End Sub
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
Public Sub Process(ByRef wave As Byte()) IsEventActive = False _waveLeft = New Double(wave.Length \ 4 - 1) {} _waveRight = New Double(wave.Length \ 4 - 1) {} If _isTest = False Then ' Split out channels from sample Dim h As Integer = 0 For i As Integer = 0 To wave.Length - 1 Step 4 _waveLeft(h) = CDbl(BitConverter.ToInt16(wave, i)) _waveRight(h) = CDbl(BitConverter.ToInt16(wave, i + 2)) If IsDetectingEvents = True Then If _waveLeft(h) > AmplitudeThreshold OrElse _waveLeft(h) < -AmplitudeThreshold Then IsEventActive = True End If End If _waveRight(h) = CDbl(BitConverter.ToInt16(wave, i + 2)) If IsDetectingEvents = True Then
If _waveRight(h) > AmplitudeThreshold OrElse _waveRight(h) < -AmplitudeThreshold The IsEventActive = True End If End If h += 1 Next End If End Sub End Class End Namespace
86 87 88 89 90
las variables IsEventActive, e IsDetectingEvents son las encargadas de controlar las interrupciones que determinan si existe algun sonido, ademas la variable AmplitudeThreshold indica el umbral o nivel maximo de la amplitud de la onda para determinar que ha ocurrido algun evento o ha entrado algun sonido. Como la entrada de un microfono es una señal estero en la mayoria de los casos se realiza una comparacion con la onda izquierda que representa la señal con la variable que establece el umbral y se hace la misma comparacion nuevamente pero con la onda de la entrada derecha, si la señal de entrada supera al umbral establecido automaticamente se coloca la variable IsEventActive en true o activa tal y como se puede observar en el fragmento de codigo de arriba dentro del ciclo for. Bien para poder continuar es necesario descargar todas las clases que conforman el proyecto, por ello les coloco el siguiente link para que encuentren la informacion completa de las clases: Bajar Clases del Proyecto. a continuacion pasemos a programar dentro del formulario: Inicialmente importemos las siguientes librerias, 1 Imports System.Collections.Generic 2 Imports System.ComponentModel 3 4 Imports System.Data 5 6 Imports System.Drawing 7 8 Imports System.Text 9 10Imports System.Windows.Forms 11 12Imports Deteccion_de_Sonido.detector_de_sonido 13Imports System.Threading 14 15'-----------
'lleva el nombre de acuerdo al espacio de
16 17Imports System.Diagnostics.Process 18 19Imports System.IO.Path 20 21
Luego declaremos las siguientes variables como globales: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
Private _recorder As WaveInRecorder Private _recorderBuffer As Byte() Private _player As WaveOutPlayer Private _playerBuffer As Byte() Private _stream As FifoStream Private _waveFormat As WaveFormat Private _audioFrame As AudioFrame Private _audioSamplesPerSecond As Integer = 44100 Private _audioFrameSize As Integer = 16384 Private _audioBitsPerSample As Byte = 16 Private _audioChannels As Byte = 2 Private _isPlayer As Boolean = False Private _isTest As Boolean = False Dim contador_eventos As Integer Dim indicador, i, sensibilidad, cant_proces As Integer ' Creamos una variable del tipo Thread Private hebra As Thread Dim pList1() As Process
Ahora vamos a crear la funcion Start que es la encargada de iniciar la entrada de audio por el microfono, hay que recordar que aqui en esta funcion vamos a activar o colocar en true la variable IsDetectingEvents para que se comience a comparar la señal de
audio que entra con el umbral establecido para la amplitud. la funcion o porcedimiento start se observa a continuacion:
1 2 Private Sub Start() 3 4 Stop_sonido() 5 6 Try 7 8 _waveFormat = New WaveFormat(_audioFrameSize, _audioBitsPerSample, _audioChannels) 9 10_recorder = New WaveInRecorder(0, _waveFormat, _audioFrameSize * 2, 3, New BufferDoneEven 11 12If _isPlayer = True Then 13 _player = New WaveOutPlayer(-1, _waveFormat, _audioFrameSize * 2, 3, New BufferFillEventH 14 15End If 16 17textBox1.AppendText(DateTime.Now.ToString() & " : Dispositivo de Audio Inicializado" & vb 18 19textBox1.AppendText(DateTime.Now.ToString() & " : Dispositivo de Audio en Escucha" & vbCr 20 21textBox1.AppendText(DateTime.Now & " : Muestras Por Segundo = " & _audioSamplesPerSecond. 22 23textBox1.AppendText(DateTime.Now & " : Tamaño de la Trama = " & _audioFrameSize.ToString( 24textBox1.AppendText(DateTime.Now & " : Bits por Muestra = " & _audioBitsPerSample.ToStrin 25 26textBox1.AppendText(DateTime.Now & " : Canales = " & _audioChannels.ToString() & vbCr & v 27 28_audioFrame.IsDetectingEvents = True 29 30Catch ex As Exception 31 32textBox1.AppendText(DateTime.Now & " : Ha Ocurrido una excepcion con el Audio" & vbCr & v 33 End Try 34 35End Sub 36 37
Luego pasamos a crear la funcion stop_sonido que detiene la entrada de audio y libera de la memoria el player o el procesamiento de la onda 1 2 3 4
Sub Stop_sonido() If _recorder IsNot Nothing Then Try
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
_recorder.Dispose() Finally _recorder = Nothing End Try End If If _isPlayer = True Then If _player IsNot Nothing Then Try _player.Dispose() Finally _player = Nothing End Try End If ' clear all pending data _stream.Flush() End If End Sub
Ahora, en el evento load del formulario colocamos lo siguiente: 1 2 3 4 5 6 7 8 9 10 11 12
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Hand sensibilidad = 16384 CheckForIllegalCrossThreadCalls = False 'Desactiva error por subproceso contador_eventos = 0 Me.Text_num_eventos.Text = 0 indicador = 0 i = 0
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
If WaveNative.waveInGetNumDevs() = 0 Then
TextBox1.AppendText(DateTime.Now.ToString() & " : El Dispositivo de Audio no se Encu Else If _isPlayer = True Then _stream = New FifoStream() End If _audioFrame = New AudioFrame(_isTest) _audioFrame.AmplitudeThreshold = sensibilidad Start() End If pList1 = Process.GetProcesses() cant_proces = pList1.Count End Sub
La funcion Filler es la encargada de manejar el flujo de datos capturados por el microfono y pasarla al player o visualizadores de onda: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Private Sub Filler(ByVal data As IntPtr, ByVal size As Integer) If _isPlayer = True Then If _playerBuffer Is Nothing OrElse _playerBuffer.Length < size Then _playerBuffer = New Byte(size - 1) {} End If If _stream.Length >= size Then _stream.Read(_playerBuffer, 0, size) Else For i As Integer = 0 To _playerBuffer.Length - 1 _playerBuffer(i) = 0
19 20 21 22 23 24 25 26 27 28 29
Next End If System.Runtime.InteropServices.Marshal.Copy(_playerBuffer, 0, data, size) End If End Sub
Posteriormente pasamos a elaborar el procedimiento DataArrived que es el que se encarga de procesar los datos que van de llegando o saliento despues de haber evaluado la señal de la onda: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
Private Sub DataArrived(ByVal data As IntPtr, ByVal size As Integer) indicador = 0 Me.Button_sdetec.Text = "Sonido No Detectado" Me.Button_sdetec.ForeColor = Color.Black Me.Button_sdetec.BackColor = Color.LightGray If _recorderBuffer Is Nothing OrElse _recorderBuffer.Length < size Then _recorderBuffer = New Byte(size - 1) {} End If If _recorderBuffer IsNot Nothing Then System.Runtime.InteropServices.Marshal.Copy(data, _recorderBuffer, 0, size) If _isPlayer = True Then _stream.Write(_recorderBuffer, 0, _recorderBuffer.Length) End If _audioFrame.Process(_recorderBuffer) _audioFrame.RenderTimeDomain(pictureBox1) '------------------------------------------If (_audioFrame.IsEventActive = True) Then contador_eventos += 1 Me.Text_num_eventos.Text = contador_eventos
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
indicador = 1 Me.Button_sdetec.Text = "Sonido Detectado" Me.Button_sdetec.ForeColor = Color.White Me.Button_sdetec.BackColor = Color.Red End If End If End Sub
Esta funcion es muy importante ya que aqui se determina si hay algun sonido o no, la comparacion que establece el if _audioFrame.IsEventActive = True permite realizar las operaciones a desencadenar si se detecto algun sonido, como se puede ver alli, se incrementa la variable contador, la variable indicador se coloca en 1 y el texto y color del boton se cambian. para ir variando la sensibilidad de nuestro detector de sonido solo basta con cambiar el valor del umbral de la amplitud, dicho proceso se lleva a cabo cuando variamos la barra de desplazamiento del trackbar:
1 Private Sub TrackBar_sensibilidad_Scroll(ByVal sender As System.Object, ByVal e As System.E 2 3 If (Me.TrackBar_sensibilidad.Value = 0) Then 4 sensibilidad = 16384 5 6 _audioFrame.AmplitudeThreshold = sensibilidad 7 8 End If 9 10If (Me.TrackBar_sensibilidad.Value = 1) Then 11 12sensibilidad = 15500 13 14_audioFrame.AmplitudeThreshold = sensibilidad 15End If 16 17If (Me.TrackBar_sensibilidad.Value = 2) Then 18 19sensibilidad = 9900
20 21_audioFrame.AmplitudeThreshold = sensibilidad 22 23End If End Sub 24 25 26
por ultimo elaboramos el procedimiento cerrar_ventana que permitira cerrar el formulario de manera segura, ya que el proceimiento stop_sonido solo detiene la captura de audio y sin embargo existe una saturacion de flujo de datos ya que el microfono permanecio todo el tiempo en escucha y eso genera cierto bloqueo al programa, por tal motivo se crea una funcion aparte para cerrar la ventana, tal y como se ve a continuacion: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
Sub cerrar_ventana() Dim j, posi, encontro As Integer Dim nomp As String Dim vect() As String encontro = 0 For j = 0 To cant_proces - 1 Try nomp = pList1(j).ProcessName.ToString vect = Split(nomp, ".") If (vect(0) = "Deteccion de Sonido") Then posi = j encontro = 1 End If Catch ex As Exception End Try Next hebra.Abort() If (encontro = 1) Then
33 34 35 36 37 38 39 40 41
pList1(posi).Kill() End If End Sub
Luego, en el boton salir colocamos lo siguiente para el correcto cierre de la aplicacion: 1 2 3 4 5 6 7 8 9 10 11 12 13
Private Sub Button_salir_Click(ByVal sender As System.Object, ByVal e As System.EventArg 'crear objeto y asignarlo al sub que queremos ejecutar hebra = New Thread(AddressOf Stop_sonido) 'ponemos al hilo en marcha hebra.Start() cerrar_ventana() End Sub
Asi como en el evento formclosing de la aplicacion:
1 Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormC 2 3 'crear objeto y asignarlo al sub que queremos ejecutar 4 5 hebra = New Thread(AddressOf Stop_sonido) 6 7 'ponemos al hilo en marcha 8 9 hebra.Start() 10 cerrar_ventana() 11 12End Sub 13
Bien por ultimo solo resta hacerle una prueba a la aplicacion, se ejecuta y empezamos a hacer ruidos, nos podemos dar cuenta como se observa la señal que esta entrando por el microfono y si ocurre algun evento, inmediantamente el contador comienza a avanzar y el botn que indica el sonido se coloca de color rojo tal y como se ve en
la
siguiente
imagen:
Bueno eso es todo amigos, espero y este tutorial les resulte de utilidad, si desean descargar la aplicacion pueden hacer click sobre el siguiente link: Descargar Aplicacion