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 Imports System.Drawing 2 3 Imports System.Windows.Forms 4 5 Namespace detector_de_sonido 6 Class AudioFrame 7 8 Private _canvasTimeDomain As Bitmap 9 10 Private _canvasFrequencyDomain As Bitmap 11 12 Private _waveLeft As Double() 13 14 Private _waveRight As Double() 15 Private _fftLeft As Double() 16 17 Private _fftRight As Double() 18 19 Private _signalGenerator As SignalGenerator 20 21 Private _isTest As Boolean = False 22 23 Public IsEventActive As Boolean = False 24 25 Public IsDetectingEvents As Boolean = False 26 Public AmplitudeThreshold As Integer 27 28 'valor original de la amplitud=16384 29 30 Public Sub New(ByVal isTest As Boolean) 31 32 _isTest = isTest 33 34 End Sub 35 36 Public Sub Process(ByRef wave As Byte()) 37 38 IsEventActive = False 39 _waveLeft = New Double(wave.Length \ 4 - 1) {} 40 41 _waveRight = New Double(wave.Length \ 4 - 1) {} 42 43 If _isTest = False Then 44 45 ' Split out channels from sample 46 47 Dim h As Integer = 0 48 For i As Integer = 0 To wave.Length - 1 Step 4 49 50 _waveLeft(h) = CDbl(BitConverter.ToInt16(wave, i)) 51 52 _waveRight(h) = CDbl(BitConverter.ToInt16(wave, i + 2)) 53 54 If IsDetectingEvents = True Then 55 56 If _waveLeft(h) > AmplitudeThreshold OrElse _waveLeft(h) < -AmplitudeThreshold Then 57 58 IsEventActive = True 59 End If 60 61 End If 62 63 _waveRight(h) = CDbl(BitConverter.ToInt16(wave, i + 2)) 64 65 If IsDetectingEvents = True Then 66 67 If _waveRight(h) > AmplitudeThreshold OrElse _waveRight(h) < -AmplitudeThreshold The 68 IsEventActive = True 69 70 End If 71 72 End If 73 74 h += 1 75 76 Next 77 78 End If 79 End Sub 80 End Class 81 82 End Namespace 83 84 85 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 'lleva el nombre de acuerdo al espacio de 13Imports System.Threading 14 15'----------- 16 17Imports System.Diagnostics.Process 18 19Imports System.IO.Path 20 21
Luego declaremos las siguientes variables como globales:
1 2 Private _recorder As WaveInRecorder 3 4 Private _recorderBuffer As Byte() 5 6 Private _player As WaveOutPlayer 7 8 Private _playerBuffer As Byte() 9 10 Private _stream As FifoStream 11 Private _waveFormat As WaveFormat 12 13 Private _audioFrame As AudioFrame 14 15 Private _audioSamplesPerSecond As Integer = 44100 16 17 Private _audioFrameSize As Integer = 16384 18 19 Private _audioBitsPerSample As Byte = 16 20 21 Private _audioChannels As Byte = 2 22 Private _isPlayer As Boolean = False 23 24 Private _isTest As Boolean = False 25 26 Dim contador_eventos As Integer 27 28 Dim indicador, i, sensibilidad, cant_proces As Integer 29 30 ' Creamos una variable del tipo Thread 31 Private hebra As Thread 32 33 Dim pList1() As Process 34 35
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 Sub Stop_sonido() 1 2 If _recorder IsNot Nothing Then 3 4 Try 5 6 _recorder.Dispose() 7 8 Finally 9 _recorder = Nothing 10 11 End Try 12 13 End If 14 15 If _isPlayer = True Then 16 17 If _player IsNot Nothing Then 18 Try 19 20 _player.Dispose() 21 22 Finally 23 24 _player = Nothing 25 26 End Try 27 28 End If 29 ' clear all pending data 30 31 _stream.Flush() 32 33 End If 34 35 End Sub 36 37 38 39
Ahora, en el evento load del formulario colocamos lo siguiente:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Hand 1 2 sensibilidad = 16384 3 4 CheckForIllegalCrossThreadCalls = False 'Desactiva error por subproceso 5 6 contador_eventos = 0 7 8 Me.Text_num_eventos.Text = 0 9 indicador = 0 10 11 i = 0 12 13 If WaveNative.waveInGetNumDevs() = 0 Then 14 15 TextBox1.AppendText(DateTime.Now.ToString() & " : El Dispositivo de Audio no se Encu 16 Else 17 18 If _isPlayer = True Then 19 20 _stream = New FifoStream() 21 22 End If 23 24 _audioFrame = New AudioFrame(_isTest) 25 _audioFrame.AmplitudeThreshold = sensibilidad 26 27 Start() 28 29 End If 30 31 pList1 = Process.GetProcesses() 32 33 cant_proces = pList1.Count 34 35 End Sub 36 37 38 39
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 Private Sub Filler(ByVal data As IntPtr, ByVal size As Integer) 2 3 If _isPlayer = True Then 4 5 If _playerBuffer Is Nothing OrElse _playerBuffer.Length < size Then 6 7 _playerBuffer = New Byte(size - 1) {} 8 End If 9 10 If _stream.Length >= size Then 11 12 _stream.Read(_playerBuffer, 0, size) 13 14 Else 15 16 For i As Integer = 0 To _playerBuffer.Length - 1 17 _playerBuffer(i) = 0 18 19 20 Next 21 22 End If 23 System.Runtime.InteropServices.Marshal.Copy(_playerBuffer, 0, data, size) 24 25 End If 26 27 End Sub 28 29
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 Private Sub DataArrived(ByVal data As IntPtr, ByVal size As Integer) 2 3 indicador = 0 4 5 Me.Button_sdetec.Text = "Sonido No Detectado" 6 Me.Button_sdetec.ForeColor = Color.Black 7 8 Me.Button_sdetec.BackColor = Color.LightGray 9 10 If _recorderBuffer Is Nothing OrElse _recorderBuffer.Length < size Then 11 12 _recorderBuffer = New Byte(size - 1) {} 13 14 End If 15 If _recorderBuffer IsNot Nothing Then 16 17 System.Runtime.InteropServices.Marshal.Copy(data, _recorderBuffer, 0, size) 18 19 If _isPlayer = True Then 20 21 _stream.Write(_recorderBuffer, 0, _recorderBuffer.Length) 22 23 End If 24 25 _audioFrame.Process(_recorderBuffer) 26 _audioFrame.RenderTimeDomain(pictureBox1) 27 28 '------------------------------------------- 29 If (_audioFrame.IsEventActive = True) Then 30 31 contador_eventos += 1 32 33 Me.Text_num_eventos.Text = contador_eventos 34 35 indicador = 1 36 37 Me.Button_sdetec.Text = "Sonido Detectado" 38 Me.Button_sdetec.ForeColor = Color.White 39 40 Me.Button_sdetec.BackColor = Color.Red 41 42 End If 43 44 End If 45 46 End Sub 47 48 49 50
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: Sub cerrar_ventana() 1 2 Dim j, posi, encontro As Integer 3 4 Dim nomp As String 5 6 Dim vect() As String 7 8 encontro = 0 9 For j = 0 To cant_proces - 1 10 11 Try 12 13 nomp = pList1(j).ProcessName.ToString 14 15 vect = Split(nomp, ".") 16 17 If (vect(0) = "Deteccion de Sonido") Then 18 19 posi = j 20 encontro = 1 21 22 End If 23 24 Catch ex As Exception 25 26 End Try 27 28 Next 29 hebra.Abort() 30 31 If (encontro = 1) Then 32 33 pList1(posi).Kill() 34 35 End If 36 End Sub 37 38 39 40 41
Luego, en el boton salir colocamos lo siguiente para el correcto
cierre de la aplicacion: 1 Private Sub Button_salir_Click(ByVal sender As System.Object, ByVal e As System.EventArg 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 12 End Sub 13
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