Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
638 views
in Technique[技术] by (71.8m points)

audio - Detect 'Whistle' sound in android

I want to detect 'Whistle' sound. For that I have implemented http://code.google.com/p/musicg/

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.

DetectorThread.java

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<Boolean> whistleResultList = new LinkedList<Boolean>();
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 < 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 >= 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;
}
}

RecorderThread.java

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 < frameByteSize; i += 2) {
        sample = (short) ((buffer[i]) | buffer[i + 1] << 8);
        totalAbsValue += Math.abs(sample);
    }
    averageAbsValue = totalAbsValue / frameByteSize / 2;

    Debug.e("", "averageAbsValue : " + averageAbsValue);

    // no input
    if (averageAbsValue < 30) {
       return null;
    }

    return buffer;
}

}

Usage

   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 && 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()");
}

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?

Is there any issue with recording?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I invested 6 hours, :D Unbelievable, audio recorder is not released when it is stopped. I just released recorder after stopping.

Source code is having minor silly mistake. It is not releasing recorder.

public void stopRecording() {
    try {
        audioRecord.stop();
        audioRecord.release();

    } catch (Exception e) {
        e.printStackTrace();
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...