title: Detect 'Whistle' sound in android
body: <p>I want to detect 'Whistle' sound. For that I have implemented <a href="http://code.google.com/p/musicg/" rel="nofollow noreferrer">http://code.google.com/p/musicg/</a></p>
  
  <p>Source code itself having issue. When you start app it is ready for listen but when you go back and again restart detector thread it does not trigger whistle detection.</p>
  
  <p>DetectorThread.java</p>
  
  <pre><code>package weetech.wallpaper.services;
  
  import java.util.LinkedList;
  
  import weetech.wallpaper.utils.Debug;
  import android.media.AudioFormat;
  import android.media.AudioRecord;
  
  import com.musicg.api.WhistleApi;
  import com.musicg.wave.WaveHeader;
  
  public class DetectorThread extends Thread {
  
  private RecorderThread recorder;
  private WaveHeader waveHeader;
  private WhistleApi whistleApi;
  private Thread _thread;
  
  private LinkedList&lt;Boolean&gt; whistleResultList = new LinkedList&lt;Boolean&gt;();
  private int numWhistles;
  private int totalWhistlesDetected = 0;
  private int whistleCheckLength = 3;
  private int whistlePassScore = 3;
  
  public DetectorThread(RecorderThread recorder) {
      this.recorder = recorder;
      AudioRecord audioRecord = recorder.getAudioRecord();
  
      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;
      // whistle detection only supports mono channel
      if (audioRecord.getChannelConfiguration() == AudioFormat.CHANNEL_IN_MONO) {
          channel = 1;
      }
  
      waveHeader = new WaveHeader();
      waveHeader.setChannels(channel);
      waveHeader.setBitsPerSample(bitsPerSample);
      waveHeader.setSampleRate(audioRecord.getSampleRate());
      whistleApi = new WhistleApi(waveHeader);
  }
  
  private void initBuffer() {
      numWhistles = 0;
      whistleResultList.clear();
  
      // init the first frames
      for (int i = 0; i &lt; whistleCheckLength; i++) {
          whistleResultList.add(false);
      }
      // end init the first frames
  }
  
  public void start() {
      _thread = new Thread(this);
      _thread.start();
  }
  
  public void stopDetection() {
      _thread = null;
  }
  
  @Override
  public void run() {
      Debug.e("", "DetectorThread started...");
  
      try {
          byte[] buffer;
          initBuffer();
  
          Thread thisThread = Thread.currentThread();
          while (_thread == thisThread) {
              // detect sound
              buffer = recorder.getFrameBytes();
  
              // audio analyst
              if (buffer != null) {
                  // sound detected
                  // MainActivity.whistleValue = numWhistles;
  
                  // whistle detection
                  // System.out.println("*Whistle:");
  
                  try {
                      boolean isWhistle = whistleApi.isWhistle(buffer);
                      Debug.e("", "isWhistle : " + isWhistle + " "
                              + buffer.length);
  
                      if (whistleResultList.getFirst()) {
                          numWhistles--;
                      }
  
                      whistleResultList.removeFirst();
                      whistleResultList.add(isWhistle);
  
                      if (isWhistle) {
                          numWhistles++;
                      }
  
                      // Debug.e("", "numWhistles : " + numWhistles);
  
                      if (numWhistles &gt;= whistlePassScore) {
                          // clear buffer
                          initBuffer();
                          totalWhistlesDetected++;
  
                          Debug.e("", "totalWhistlesDetected : "
                                  + totalWhistlesDetected);
  
                          if (onWhistleListener != null) {
                              onWhistleListener.onWhistle();
                          }
                      }
                  } catch (Exception e) {
                      Debug.w("", "" + e.getCause());
                  }
                  // end whistle detection
              } else {
                  // Debug.e("", "no sound detected");
                  // no sound detected
                  if (whistleResultList.getFirst()) {
                      numWhistles--;
                  }
                  whistleResultList.removeFirst();
                  whistleResultList.add(false);
  
                  // MainActivity.whistleValue = numWhistles;
              }
              // end audio analyst
          }
  
          Debug.e("", "Terminating detector thread...");
  
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
  
  private OnWhistleListener onWhistleListener;
  
  public void setOnWhistleListener(OnWhistleListener onWhistleListener) {
      this.onWhistleListener = onWhistleListener;
  }
  
  public interface OnWhistleListener {
      void onWhistle();
  }
  
  public int getTotalWhistlesDetected() {
      return totalWhistlesDetected;
  }
  }
  </code></pre>
  
  <p>RecorderThread.java</p>
  
  <pre><code>public class RecorderThread {
  
  private AudioRecord audioRecord;
  private int channelConfiguration;
  private int audioEncoding;
  private int sampleRate;
  private int frameByteSize; // for 1024 fft size (16bit sample size)
  byte[] buffer;
  
  public RecorderThread() {
      sampleRate = 44100;
      frameByteSize = 1024 * 2;
  
      channelConfiguration = AudioFormat.CHANNEL_IN_MONO;
      audioEncoding = AudioFormat.ENCODING_PCM_16BIT;
  
      int recBufSize = AudioRecord.getMinBufferSize(sampleRate,
              channelConfiguration, audioEncoding); // need to be larger than
                                                      // size of a frame
      audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
              sampleRate, channelConfiguration, audioEncoding, recBufSize);
      buffer = new byte[frameByteSize];
  }
  
  public AudioRecord getAudioRecord() {
      return audioRecord;
  }
  
  public boolean isRecording() {
      if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
          return true;
      }
  
      return false;
  }
  
  public void startRecording() {
      try {
          audioRecord.startRecording();
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
  
  public void stopRecording() {
      try {
          audioRecord.stop();
      } catch (Exception e) {
          e.printStackTrace();
      }
  }
  
  public byte[] getFrameBytes() {
      audioRecord.read(buffer, 0, frameByteSize);
  
      // analyze sound
      int totalAbsValue = 0;
      short sample = 0;
      float averageAbsValue = 0.0f;
  
      for (int i = 0; i &lt; frameByteSize; i += 2) {
          sample = (short) ((buffer[i]) | buffer[i + 1] &lt;&lt; 8);
          totalAbsValue += Math.abs(sample);
      }
      averageAbsValue = totalAbsValue / frameByteSize / 2;
  
      Debug.e("", "averageAbsValue : " + averageAbsValue);
  
      // no input
      if (averageAbsValue &lt; 30) {
         return null;
      }
  
      return buffer;
  }
  
  }
  </code></pre>
  
  <p>Usage</p>
  
  <pre><code>   public class DetectionService extends Service implements
      OnWhistleListener {
  
  Handler handler;
  private DetectorThread detectorThread;
  private RecorderThread recorderThread;
  
  @Override
  public void onCreate() {
      super.onCreate();
      handler = new Handler();
  
  }
  
  @Override
  public IBinder onBind(Intent intent) {
      return null;
  }
  
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
  
      try {
          if (intent != null &amp;&amp; intent.getExtras() != null) {
  
              if (intent.getExtras().containsKey("action")) {
                  Debug.e("", "action : " + intent.getStringExtra("action"));
  
                  if (intent.getStringExtra("action").equals("start")) {
                      startWhistleDetection();
                  }
  
                  if (intent.getStringExtra("action").equals("stop")) {
                      stopWhistleDetection();
                      stopSelf();
                  }
              }
          } else {
  
              startWhistleDetection();
              Debug.e("", "intent is null OR intent.getExtras() is null");
          }
      } catch (Exception e) {
          e.printStackTrace();
      }
  
      return super.onStartCommand(intent, flags, startId);
  }
  
  private void startWhistleDetection() {
  
      try {
          stopWhistleDetection();
      } catch (Exception e) {
          e.printStackTrace();
      }
  
      recorderThread = new RecorderThread();
      recorderThread.startRecording();
      detectorThread = new DetectorThread(recorderThread);
      detectorThread.setOnWhistleListener(this);
      detectorThread.start();
  
  }
  
  private void stopWhistleDetection() {
      if (detectorThread != null) {
          detectorThread.stopDetection();
          detectorThread.setOnWhistleListener(null);
          detectorThread = null;
      }
  
      if (recorderThread != null) {
          recorderThread.stopRecording();
          recorderThread = null;
      }
  
  }
  
  @Override
  public void onDestroy() {
      super.onDestroy();
  }
  
  @Override
  public void onWhistle() {
      Debug.e("", "onWhistle()");
  }
  </code></pre>
  
  <p>It detects whistle first time until you don't stop service. But after stopping and again starting It does not detect (does not call listener). I just failed to trace, what can be the issue?</p>
  
  <p>Is there any issue with recording?</p>