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

¿Tiene alguna pregunta o comentario?

6 comments on “como detectar silbido (sonido) en android studio

Matia

Muchas gracias lo estaba buscando hace mucho tiempo 😀

0
Reply
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
Reply
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
Reply
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
Reply
loquito

no hay problema cambia el compile por implementation

0
Reply
Manuel Veyna Lamas

Hola
Tendrás un ejemplo de cómo se hace en Eclipse? o de las librerías que agregas cómo hacerlo en eclipse?
Saludos y están excelentes tus ejemplos.

0
Reply

Deja una respuesta

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