como detectar silbido (sonido) en android studio

Hola a todos hoy veremos como detectar los silbidos en android estudio, hago este tutorial para atender a los bastantes correos y mensaje por los diferentes canales que tiene esta pagina, siempre que me han preguntado sobre el tema, yo he contestado que deberían utilizar la librería musicg e inclusive he fomentado a utilizarla por ser una de las pocas librerías free y de fácil entorno, pero lamentablemente al momento de utilizar cualquier librería de reconocimiento de audio es ponerse a programar en un nivel avanzado. Y mis leyentes novatos no han podido implementarla en ese sentido como que deseo echarles una mano, hoy haremos un ejercicio básico , sin mas rodeos empezamos con este tutorial.

 

 

 

como detectar silbido (sonido) en android studio

 

PASO 1: AGREGAR LIBRERIA

Debemos agregar esta librería en nuestro build.gradle

compile group: 'com.github.fracpete', name: 'musicg', version: '1.4.2.1'

Debería de quedar así

PASO 2: AGREGAR PERMISOS

 

Debemos ir a nuestro AndroidManifest y agregar los siguientes permisos

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />

 

PASO 3: AGREGANDO UTILIDADES

 

Primero debemos agregar dentro de la carpeta drawable la siguiente imagen

Ahora dentro de la carpeta res debemos crear una carpeta llamada raw , dentro de raw debemos pegar el siguiente archivo audio.

https://drive.google.com/file/d/1c7JDIPq5m4-4rrbV92dLoGRR_v1-ZdBJ/view?usp=sharing

 

PASO 4:CREANDO LAYOUT

Debemos crear los dos archivos layout como se muestran en la siguiente imagen

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff">

    <Button
        android:id="@+id/btnDetener"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="123dp"
        android:layout_marginEnd="57dp"
        android:layout_marginTop="8dp"
        android:text="DETENER"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/btnIniciar"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0" />

    <Button
        android:id="@+id/btnIniciar"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="123dp"
        android:layout_marginEnd="26dp"
        android:layout_marginStart="57dp"
        android:layout_marginTop="8dp"
        android:text="INICIAR"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/btnDetener"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="228dp"
        android:layout_height="0dp"
        android:layout_marginBottom="28dp"
        android:layout_marginTop="51dp"
        android:src="@drawable/silbido"
        app:layout_constraintBottom_toTopOf="@+id/btnIniciar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

 

alerta.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" 
    android:weightSum="100">

    <TextView
        android:id="@+id/tvAlert"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="80"
        android:gravity="center"
        android:text="Silbido Detectado"
        android:textSize="30dp" />
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/restartButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="20"
            android:text="Restart"
            android:textStyle="bold" />

        <Button
            android:id="@+id/quitButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_weight="20"
            android:text="Cancelar"
            android:textStyle="bold" />
    
    </LinearLayout>

</LinearLayout>

 

PASO 5 : CREANDO CLASES

Debemos crear las seis clases como se muestra en la siguiente imagen

 

MainActivity

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity{

    private Button start,stop;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 1000);
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 1000);
        }

        start = (Button) findViewById(R.id.btnIniciar);
        stop = (Button) findViewById(R.id.btnDetener);

        start.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                startService(new Intent(getBaseContext(),VocalServicio.class));
            }

        });

        stop.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                stopService(new Intent(getBaseContext(),VocalServicio.class));
            }

        });
    }
}

 

DetectarSonidoListener

 

public interface DetectarSonidoListener {
 public abstract void onWhistleDetected();
}

 

DetectorThread

 

import java.util.LinkedList;

import com.musicg.api.WhistleApi;
import com.musicg.wave.WaveHeader;

import android.media.AudioFormat;
import android.media.AudioRecord;

public class DetectorThread extends Thread{

 private RecorderThread grabadora;
 private WaveHeader waveHeader;
 private WhistleApi silbidoApi;
 private volatile Thread _thread;

 private LinkedList<Boolean> listaResultados = new LinkedList<Boolean>();
 private int numWhistles;
 private int whistleCheckLength = 3;
 private int whistlePassScore = 3;
 
 private DetectarSonidoListener detectarSonidoListener;
 
 public DetectorThread(RecorderThread grabadora){
  this.grabadora = grabadora;
  AudioRecord audioRecord = grabadora.getGrabadorAudio();
  
  int bitsPerSample = 0;
  if (audioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT){
   bitsPerSample = 16;
  }
  else if (audioRecord.getAudioFormat() == AudioFormat.ENCODING_PCM_8BIT){
   bitsPerSample = 8;
  }
  
  int channel = 0;
  // la detección de silbatos solo admite canales mono
  if (audioRecord.getChannelConfiguration() == AudioFormat.CHANNEL_IN_MONO){
   channel = 1;
  }

  waveHeader = new WaveHeader();
  waveHeader.setChannels(channel);
  waveHeader.setBitsPerSample(bitsPerSample);
  waveHeader.setSampleRate(audioRecord.getSampleRate());
  silbidoApi = new WhistleApi(waveHeader);
 }

 private void initBuffer() {
  numWhistles = 0;
  listaResultados.clear();

  for (int i = 0; i < whistleCheckLength; i++) {
   listaResultados.add(false);
  }
 }

 public void start() {
  _thread = new Thread(this);
        _thread.start();
    }
 
 public void stopDetection(){
  _thread = null;
 }
 
 public void run() {
  try {
   byte[] buffer;
   initBuffer();
   
   Thread thisThread = Thread.currentThread();
   while (_thread == thisThread) {
    // sonido detectado
    buffer = grabadora.getFrameBytes();

    // analisador de audio
    if (buffer != null) {
     // sonido detectado
     // detección de silbidos
     //System.out.println("*Whistle:");
     boolean isWhistle = silbidoApi.isWhistle(buffer);
     if (listaResultados.getFirst()) {
      numWhistles--;
     }
  
     listaResultados.removeFirst();
     listaResultados.add(isWhistle);
  
     if (isWhistle) {
      numWhistles++;
     }
  
     if (numWhistles >= whistlePassScore) {
      // Limpiar buffer
      initBuffer();
      onWhistleDetected();
     }
     // fin de detección de silbatos
    }
    else{
     // no sound detected
     if (listaResultados.getFirst()) {
      numWhistles--;
     }
     listaResultados.removeFirst();
     listaResultados.add(false);
    }
    // terminar el analista de audio
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 
 private void onWhistleDetected(){
  if (detectarSonidoListener != null){
   detectarSonidoListener.onWhistleDetected();
  }
 }
 
 public void setDetectarSonidoListener(DetectarSonidoListener listener){
  detectarSonidoListener = listener;
 }
}

 

RecorderThread

 

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;

public class RecorderThread extends Thread {
 
 private AudioRecord grabadorAudio;
 private boolean isRecording;
 private int configurarCanal = AudioFormat.CHANNEL_IN_MONO;
 private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
 private int sampleRate = 44100;
 private int frameByteSize = 2048; // for 1024 fft size (16bit sample size)
 byte[] buffer;
 
 public RecorderThread(){
  int recBufSize = AudioRecord.getMinBufferSize(sampleRate, configurarCanal, audioEncoding); // need to be larger than size of a frame
  grabadorAudio = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, configurarCanal, audioEncoding, recBufSize);
  buffer = new byte[frameByteSize];
 }
 
 public AudioRecord getGrabadorAudio(){
  return grabadorAudio;
 }
 
 public boolean isRecording(){
  return this.isAlive() && isRecording;
 }
 
 public void startRecording(){
  try{
   grabadorAudio.startRecording();
   isRecording = true;
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 
 public void stopRecording(){
  try{
   grabadorAudio.stop();
   grabadorAudio.release();
   isRecording = false;
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 
 public byte[] getFrameBytes(){
  grabadorAudio.read(buffer, 0, frameByteSize);
  
  // analizador de audio
  int totalAbsValue = 0;
        short sample = 0; 
        float averageAbsValue = 0.0f;
        
        for (int i = 0; i < frameByteSize; i += 2) {
            sample = (short)((buffer[i]) | buffer[i + 1] << 8);
            totalAbsValue += Math.abs(sample);
        }
        averageAbsValue = totalAbsValue / frameByteSize / 2;

        if (averageAbsValue < 30){
        	return null;
        }
        
  return buffer;
 }
 
 public void run() {
  startRecording();
 }
}

 

VocalAlerta

 

import android.app.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class VocalAlerta extends Activity implements View.OnClickListener
    ,OnCompletionListener{

 private MediaPlayer miSonido;
    private Button bReturn;
    private Button bRestart;
    
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  // TODO Auto-generated method stub
  super.onCreate(savedInstanceState);
     setContentView(R.layout.alerta);
     stopService(new Intent(getBaseContext(),VocalServicio.class));
  initialize();
  
  miSonido = MediaPlayer.create(this,R.raw.alarm);
  miSonido.setOnCompletionListener(this);
  miSonido.start();
  
 }

 private void initialize() {
  bReturn = (Button) findViewById(R.id.quitButton);
  bRestart = (Button) findViewById(R.id.restartButton);
  bReturn.setOnClickListener(this);
  bRestart.setOnClickListener(this);
 }

 @Override
 public void onClick(View v) {
  // TODO Auto-generated method stub
  switch(v.getId()){
  case R.id.restartButton :
       miSonido.release();
       Intent i = new Intent(getBaseContext(),VocalServicio.class);
       startService(i);
       finish();
      break;
  case R.id.quitButton:
   miSonido.release();
   finish();
   break;
  }
 }

 @Override
 public void onCompletion(MediaPlayer arg0) {
  // TODO Auto-generated method stub
  arg0.start();
 }
 
}

VocalServicio

 

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;

public class VocalServicio extends Service implements DetectarSonidoListener {

    private DetectorThread detectorThread;
    private RecorderThread recorderThread;
    private static final int NOTIFICATION_Id = 001;

    public static final int DETECT_NONE = 0;
    public static final int DETECT_WHISTLE = 1;
    public static int selectedDetection = DETECT_NONE;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        initNotification();
        startDetection();
        return START_STICKY;
    }

    public void initNotification(){
        NotificationCompat.Builder mBuilder =
                new NotificationCompat.Builder(this)
                        .setSmallIcon(R.drawable.silbido)
                        .setContentTitle("Detección de silbidos")
                        .setContentText("La detección de silbidos está activada.");

        Intent resultIntent = new Intent(this, MainActivity.class);
        PendingIntent resultPendingIntent = PendingIntent.getActivity( this,0,
                resultIntent,PendingIntent.FLAG_UPDATE_CURRENT);

        mBuilder.setContentIntent(resultPendingIntent);
        NotificationManager mNotifyMgr =
                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mNotifyMgr.notify(NOTIFICATION_Id, mBuilder.build());
    }

    public void startDetection(){
        selectedDetection = DETECT_WHISTLE;
        recorderThread = new RecorderThread();
        recorderThread.start();
        detectorThread = new DetectorThread(recorderThread);
        detectorThread.setDetectarSonidoListener(this);
        detectorThread.start();
        Toast.makeText(this, "Servicio iniciado", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        if (recorderThread != null) {
            recorderThread.stopRecording();
            recorderThread = null;
        }
        if (detectorThread != null) {
            detectorThread.stopDetection();
            detectorThread = null;
        }
        selectedDetection = DETECT_NONE;
        Toast.makeText(this, "Servicio detenido", Toast.LENGTH_LONG).show();
        stopNotification();
    }

    @Override
    public IBinder onBind(Intent arg0) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void onWhistleDetected() {
        Intent intent = new Intent(this,VocalAlerta.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
        Toast.makeText(this, "Silbido detectado", Toast.LENGTH_LONG).show();
        this.stopSelf();

    }

    public void stopNotification(){
        NotificationManager mNotifyMgr = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mNotifyMgr.cancel(NOTIFICATION_Id);
    }

}

 

 

PASO 6: LLAMAR NUESTRO SERVICIO 

 

Debemos verificar que nuestro androidManifest esta escrito de forma correcta, dentro debemos llamar a nuestro servicio VocalServicio” como se muestra en las lineas 35-38 y también a nuestra clase VocalAlerta como se muestra en las lineas del 24 al 33

AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.androfast.server.appsilbido">

    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.VIBRATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".VocalAlerta"
            android:label="@string/app_name"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.VOCALALERT" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

        <service
            android:name=".VocalServicio"
            android:enabled="true"
            android:exported="true" />
    </application>

</manifest>

Con esto terminamos el tutorial, espero les aya gustado y que les sirva para sus proyectos, si quieren leer mas de la librería les invito a visitar el siguiente repositorio : musicg    esta librería es muy utilizada en muchos ámbitos como el musical, científico y por supuesto el de programación, así que aprovechen que es gratuita y busque toda la info que necesiten en san google, hasta un próximo tutorial, se me cuidan!!!.

 

0

4 pensamientos sobre “como detectar silbido (sonido) en android studio

  1. Juan

    Excelente proyecto, pero seria genial si lo haces en vídeo explicando cada paso que das, no comprendo muchas cosas pero en si esta excelente esta app

    0
  2. Christopher

    Muy buen tutorial amigo, solo una pregunta, si yo quisiera que en lugar de un silbido sea cualquier palabra, tendria que cambiar la librería y las variables??
    gracias por tu aportación amigo muy buena la app

    0
  3. pedro

    hola muy buen tutorial pero resulta que ya no es posible agregar esto:

    compile group: ‘com.github.fracpete’, name: ‘musicg’, version: ‘1.4.2.1’

    Ya que ahora debe ser por complementación. Nos gustaría que hicieras de nuevo el vídeo con las nuevas implementaciones de android. Un cordial saludo y esperando impacientemente dicha explicación

    0

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *