在安卓设备上检测“口哨”声

4
我希望能够检测“口哨”声音。为此,我已经实现了http://code.google.com/p/musicg/
源代码本身存在问题。当您启动应用程序时,它准备好进行监听,但当您返回并再次启动检测器线程时,它不会触发口哨检测。

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;
}

}

使用方法

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

它第一次检测到哨声直到您停止服务。但是在停止并再次启动后,它不会检测(不会调用监听器)。我只是无法追踪,可能出了什么问题?

录音有问题吗?


@ClassStacker 我追踪了所有变量。但是如果你想要追踪 music cg 库的变量,那么怎么做呢? - Bhavesh Hirpara
难道你不应该首先检查为什么哨声检测没有被「触发」吗?也就是说,从你的代码来看,是否已经「触发」了呢? - class stacker
@ClassStacker 当初始化时调用 Debug.e("", "onWhistle()");,但当我重新启动服务时它不会被调用。我尝试重置所有对象,但仍然无效。我认为获取 gramebytes 未正确返回 byte[]。我还尝试更改输入通道。 - Bhavesh Hirpara
我看到了... 你能提供一些关于如何停止和启动的更多信息吗?例如来自Activity的代码片段。这样我们可以从另一个角度看代码? - class stacker
@BhaveshHirpara,你能否请发给我musicg的源代码或任何可用的工作示例。Github上的代码对我来说无法运行。 - Sagar Nayak
你好。我在MainActivity中使用了上述代码并启动了服务。但是在RecorderThread类的public byte[] getFrameBytes()方法中,我总是得到averageAbsValue:0.0的值......以及@Override public void onWhistle() { } - KAMAL VERMA
2个回答

7

我花费了6个小时:D,令人难以置信的是,音频记录器在停止时未被释放。我只是在停止后才释放记录器。

源代码存在一些小错误。它没有释放记录器。

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

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

这段代码很棒,您能否建议我如何使用它?我无法完全理解,已经查看了服务文档,但在录制和检测方面我仍然一无所获。如果能给出使用此代码的提示将非常有帮助。非常感谢! - Pending Intent
你好,Bhavesh Hirpara。我使用了上面的代码并在MainActivity中启动了服务。但是在RecorderThread类的public byte[] getFrameBytes()方法中,我总是得到averageAbsValue:0.0......而且@Override public void onWhistle() {}从未被触发。 - KAMAL VERMA

0

这段代码对我来说没问题

    if (detectorThread != null) {
            detectorThread.stopDetection();
            recorderThread.stopRecording();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接