2013-10-02 116 views
6

我發現這個android代碼,當他/她開始說話時記錄用戶聲音,當他/她停止時停止記錄。但問題在於錄製速度非常快。如果有人想說兩個字,它只是記錄第一個字。Android音頻捕獲沉默檢測

如何更改以下代碼以使錄音過程對瞬間靜音不那麼敏感?

public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    final int RECORDER_BPP = 16; 
    int RECORDER_SAMPLERATE = 8000; 
    int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO; 
    int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT; 

    // Get the minimum buffer size required for the successful creation of 
    // an AudioRecord object. 
    int bufferSizeInBytes = AudioRecord 
      .getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS, 
        RECORDER_AUDIO_ENCODING); 
    // Initialize Audio Recorder. 
    AudioRecord audioRecorder = new AudioRecord(
      MediaRecorder.AudioSource.MIC, RECORDER_SAMPLERATE, 
      RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING, bufferSizeInBytes); 
    // Start Recording. 
    audioRecorder.startRecording(); 

    int numberOfReadBytes = 0; 
    byte audioBuffer[] = new byte[bufferSizeInBytes]; 
    boolean recording = false; 
    float tempFloatBuffer[] = new float[3]; 
    int tempIndex = 0; 
    int totalReadBytes = 0; 
    byte totalByteBuffer[] = new byte[60 * 44100 * 2]; 

    // While data come from microphone. 
    while (true) { 
     float totalAbsValue = 0.0f; 
     short sample = 0; 

     numberOfReadBytes = audioRecorder.read(audioBuffer, 0, 
       bufferSizeInBytes); 

     // Analyze Sound. 
     for (int i = 0; i < bufferSizeInBytes; i += 2) { 
      sample = (short) ((audioBuffer[i]) | audioBuffer[i + 1] << 8); 
      totalAbsValue += Math.abs(sample)/(numberOfReadBytes/2); 
     } 

     // Analyze temp buffer. 
     tempFloatBuffer[tempIndex % 3] = totalAbsValue; 
     float temp = 0.0f; 
     for (int i = 0; i < 3; ++i) 
      temp += tempFloatBuffer[i]; 

     if ((temp >= 0 && temp <= 350) && recording == false) { 
      Log.i("TAG", "1"); 
      tempIndex++; 
      continue; 
     } 

     if (temp > 350 && recording == false) { 
      Log.i("TAG", "2"); 
      recording = true; 
     } 

     if ((temp >= 0 && temp <= 350) && recording == true) { 
      Log.i("TAG", "Save audio to file."); 

      // Save audio to file. 
      String filepath = Environment.getExternalStorageDirectory() 
        .getPath(); 
      File file = new File(filepath, "AudioRecorder"); 
      if (!file.exists()) 
       file.mkdirs(); 

      String fn = file.getAbsolutePath() + "/" 
        + System.currentTimeMillis() + ".wav"; 

      long totalAudioLen = 0; 
      long totalDataLen = totalAudioLen + 36; 
      long longSampleRate = RECORDER_SAMPLERATE; 
      int channels = 1; 
      long byteRate = RECORDER_BPP * RECORDER_SAMPLERATE * channels 
        /8; 
      totalAudioLen = totalReadBytes; 
      totalDataLen = totalAudioLen + 36; 
      byte finalBuffer[] = new byte[totalReadBytes + 44]; 

      finalBuffer[0] = 'R'; // RIFF/WAVE header 
      finalBuffer[1] = 'I'; 
      finalBuffer[2] = 'F'; 
      finalBuffer[3] = 'F'; 
      finalBuffer[4] = (byte) (totalDataLen & 0xff); 
      finalBuffer[5] = (byte) ((totalDataLen >> 8) & 0xff); 
      finalBuffer[6] = (byte) ((totalDataLen >> 16) & 0xff); 
      finalBuffer[7] = (byte) ((totalDataLen >> 24) & 0xff); 
      finalBuffer[8] = 'W'; 
      finalBuffer[9] = 'A'; 
      finalBuffer[10] = 'V'; 
      finalBuffer[11] = 'E'; 
      finalBuffer[12] = 'f'; // 'fmt ' chunk 
      finalBuffer[13] = 'm'; 
      finalBuffer[14] = 't'; 
      finalBuffer[15] = ' '; 
      finalBuffer[16] = 16; // 4 bytes: size of 'fmt ' chunk 
      finalBuffer[17] = 0; 
      finalBuffer[18] = 0; 
      finalBuffer[19] = 0; 
      finalBuffer[20] = 1; // format = 1 
      finalBuffer[21] = 0; 
      finalBuffer[22] = (byte) channels; 
      finalBuffer[23] = 0; 
      finalBuffer[24] = (byte) (longSampleRate & 0xff); 
      finalBuffer[25] = (byte) ((longSampleRate >> 8) & 0xff); 
      finalBuffer[26] = (byte) ((longSampleRate >> 16) & 0xff); 
      finalBuffer[27] = (byte) ((longSampleRate >> 24) & 0xff); 
      finalBuffer[28] = (byte) (byteRate & 0xff); 
      finalBuffer[29] = (byte) ((byteRate >> 8) & 0xff); 
      finalBuffer[30] = (byte) ((byteRate >> 16) & 0xff); 
      finalBuffer[31] = (byte) ((byteRate >> 24) & 0xff); 
      finalBuffer[32] = (byte) (2 * 16/8); // block align 
      finalBuffer[33] = 0; 
      finalBuffer[34] = RECORDER_BPP; // bits per sample 
      finalBuffer[35] = 0; 
      finalBuffer[36] = 'd'; 
      finalBuffer[37] = 'a'; 
      finalBuffer[38] = 't'; 
      finalBuffer[39] = 'a'; 
      finalBuffer[40] = (byte) (totalAudioLen & 0xff); 
      finalBuffer[41] = (byte) ((totalAudioLen >> 8) & 0xff); 
      finalBuffer[42] = (byte) ((totalAudioLen >> 16) & 0xff); 
      finalBuffer[43] = (byte) ((totalAudioLen >> 24) & 0xff); 

      for (int i = 0; i < totalReadBytes; ++i) 
       finalBuffer[44 + i] = totalByteBuffer[i]; 

      FileOutputStream out; 
      try { 
       out = new FileOutputStream(fn); 
       try { 
        out.write(finalBuffer); 
        out.close(); 
       } catch (IOException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 

      } catch (FileNotFoundException e1) { 
       // TODO Auto-generated catch block 
       e1.printStackTrace(); 
      } 

      // */ 
      tempIndex++; 
      break; 
     } 

     // -> Recording sound here. 
     Log.i("TAG", "Recording Sound."); 
     for (int i = 0; i < numberOfReadBytes; i++) 
      totalByteBuffer[totalReadBytes + i] = audioBuffer[i]; 
     totalReadBytes += numberOfReadBytes; 
     // */ 

     tempIndex++; 

    } 
} 

回答

-6

都要經過CAPTURE

這個文件,然後按照這個幾個環節,這可能有助於找出你的錯誤。

http://www.devlper.com/2010/12/android-audio-recording-part-1/

http://www.devlper.com/2010/12/android-audio-recording-part-2/

也終於確保你給你的應用程序將需要的權限。至少它需要RECORD_AUDIOWRITE_EXTERNAL_STORAGE

+0

我有記錄沒有問題,我的問題是我要停止錄音時,用戶停止說話,但沒有這麼快,例如,如果麥克風不發出任何聲音1然後錄製停止 –

19

我不喜歡你寫代碼的方式。 使用我的。 您可以根據您的音量更改閾值變量。 在這個示例應用程序中沒有按鈕。當您啓動應用程序時,它會開始聆聽,當您按下後退按鈕時,停止採集,並在每次運行文件時將文件存儲在手機存儲器文件夾AudioRecorder中。 甚至閱讀評論和評論代碼。 注意:每次語音超過閾值時,語音都會附加到臨時文件。 當您通過按回來手動停止錄製(wav文件是從臨時文件創建的,具有新的唯一名稱)時,會添加wav標題。 如果您需要在每次顯示語音時創建一個新文件,您可以根據自己的需要輕鬆修改它,但您應該始終通過臨時文件保存。 如果您需要某種延遲,爲了記錄更多,即使沒有語音(在語音之後),只需在延遲過後繼續保存數據即可。您可以創建延遲,計算自上次找到峯值(表示語音存在)以來的經過時間(System.nanotime)。

不要忘記標記接受的解決方案(我測試過)。

package com.example.testaudiocapturewiththreshold; 

import android.os.Bundle; 
import android.app.Activity; 
import android.view.Menu; 


import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 

import android.media.AudioFormat; 
import android.media.AudioRecord; 
import android.media.MediaRecorder; 
import android.os.AsyncTask; 
import android.os.Environment; 
import android.os.Handler; 
import android.util.Log; 

public class TestAudioCaptureWithThreshold extends Activity { 

private static final String TAG = TestAudioCaptureWithThreshold.class.getSimpleName(); 
private static final int RECORDER_BPP = 16; 
private static final String AUDIO_RECORDER_FILE_EXT_WAV = ".wav"; 
private static final String AUDIO_RECORDER_FOLDER = "AudioRecorder"; 
private static final String AUDIO_RECORDER_TEMP_FILE = "record_temp.raw"; 

FileOutputStream os = null; 

int bufferSize ; 
int frequency = 44100; //8000; 
int channelConfiguration = AudioFormat.CHANNEL_IN_MONO; 
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; 
boolean started = false; 
    RecordAudio recordTask; 

short threshold=15000; 

boolean debug=false; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    Log.w(TAG, "onCreate"); 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_test_audio_capture_with_threshold); 

    startAquisition(); 
} 


@Override 
protected void onResume() { 
    Log.w(TAG, "onResume"); 
    super.onResume(); 


} 

@Override 
protected void onDestroy() { 
    Log.w(TAG, "onDestroy"); 
    stopAquisition(); 
    super.onDestroy(); 

} 

public class RecordAudio extends AsyncTask<Void, Double, Void> { 

    @Override 
    protected Void doInBackground(Void... arg0) { 
     Log.w(TAG, "doInBackground"); 
     try { 

       String filename = getTempFilename(); 

      try { 
         os = new FileOutputStream(filename); 
      } catch (FileNotFoundException e) { 
         e.printStackTrace(); 
      } 


      bufferSize = AudioRecord.getMinBufferSize(frequency, 
      channelConfiguration, audioEncoding); 

      AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequency, 
        channelConfiguration, audioEncoding, bufferSize); 

      short[] buffer = new short[bufferSize]; 

      audioRecord.startRecording(); 

      while (started) { 
       int bufferReadResult = audioRecord.read(buffer, 0,bufferSize); 
       if(AudioRecord.ERROR_INVALID_OPERATION != bufferReadResult){ 
         //check signal 
        //put a threshold 
         int foundPeak=searchThreshold(buffer,threshold); 
         if (foundPeak>-1){ //found signal 
               //record signal 
          byte[] byteBuffer =ShortToByte(buffer,bufferReadResult); 
         try { 
           os.write(byteBuffer); 
         } catch (IOException e) { 
           e.printStackTrace(); 
         } 
         }else{//count the time 
          //don't save signal 
         } 


           //show results 
         //here, with publichProgress function, if you calculate the total saved samples, 
         //you can optionally show the recorded file length in seconds:  publishProgress(elsapsedTime,0); 


       } 
      } 

      audioRecord.stop(); 


      //close file 
       try { 
        os.close(); 
       } catch (IOException e) { 
        e.printStackTrace(); 
       } 

       copyWaveFile(getTempFilename(),getFilename()); 
       deleteTempFile(); 


     } catch (Throwable t) { 
      t.printStackTrace(); 
      Log.e("AudioRecord", "Recording Failed"); 
     } 
     return null; 

    } //fine di doInBackground 

     byte [] ShortToByte(short [] input, int elements) { 
     int short_index, byte_index; 
     int iterations = elements; //input.length; 
     byte [] buffer = new byte[iterations * 2]; 

     short_index = byte_index = 0; 

     for(/*NOP*/; short_index != iterations; /*NOP*/) 
     { 
     buffer[byte_index]  = (byte) (input[short_index] & 0x00FF); 
     buffer[byte_index + 1] = (byte) ((input[short_index] & 0xFF00) >> 8); 

     ++short_index; byte_index += 2; 
     } 

     return buffer; 
    } 


    int searchThreshold(short[]arr,short thr){ 
     int peakIndex; 
     int arrLen=arr.length; 
     for (peakIndex=0;peakIndex<arrLen;peakIndex++){ 
      if ((arr[peakIndex]>=thr) || (arr[peakIndex]<=-thr)){ 
       //se supera la soglia, esci e ritorna peakindex-mezzo kernel. 

       return peakIndex; 
      } 
     } 
     return -1; //not found 
    } 

    /* 
    @Override 
    protected void onProgressUpdate(Double... values) { 
     DecimalFormat sf = new DecimalFormat("000.0000");   
     elapsedTimeTxt.setText(sf.format(values[0])); 

    } 
    */ 

    private String getFilename(){ 
     String filepath = Environment.getExternalStorageDirectory().getPath(); 
     File file = new File(filepath,AUDIO_RECORDER_FOLDER); 

     if(!file.exists()){ 
       file.mkdirs(); 
     } 

     return (file.getAbsolutePath() + "/" + System.currentTimeMillis() + AUDIO_RECORDER_FILE_EXT_WAV); 
    } 


    private String getTempFilename(){ 
     String filepath = Environment.getExternalStorageDirectory().getPath(); 
     File file = new File(filepath,AUDIO_RECORDER_FOLDER); 

     if(!file.exists()){ 
       file.mkdirs(); 
     } 

     File tempFile = new File(filepath,AUDIO_RECORDER_TEMP_FILE); 

     if(tempFile.exists()) 
       tempFile.delete(); 

     return (file.getAbsolutePath() + "/" + AUDIO_RECORDER_TEMP_FILE); 
    } 





    private void deleteTempFile() { 
      File file = new File(getTempFilename()); 

      file.delete(); 
    } 

    private void copyWaveFile(String inFilename,String outFilename){ 
     FileInputStream in = null; 
     FileOutputStream out = null; 
     long totalAudioLen = 0; 
     long totalDataLen = totalAudioLen + 36; 
     long longSampleRate = frequency; 
     int channels = 1; 
     long byteRate = RECORDER_BPP * frequency * channels/8; 

     byte[] data = new byte[bufferSize]; 

     try { 
       in = new FileInputStream(inFilename); 
       out = new FileOutputStream(outFilename); 
       totalAudioLen = in.getChannel().size(); 
       totalDataLen = totalAudioLen + 36; 


       WriteWaveFileHeader(out, totalAudioLen, totalDataLen, 
           longSampleRate, channels, byteRate); 

       while(in.read(data) != -1){ 
         out.write(data); 
       } 

       in.close(); 
       out.close(); 
     } catch (FileNotFoundException e) { 
       e.printStackTrace(); 
     } catch (IOException e) { 
       e.printStackTrace(); 
     } 
    } 

    private void WriteWaveFileHeader(
        FileOutputStream out, long totalAudioLen, 
        long totalDataLen, long longSampleRate, int channels, 
        long byteRate) throws IOException { 

      byte[] header = new byte[44]; 

      header[0] = 'R'; // RIFF/WAVE header 
      header[1] = 'I'; 
      header[2] = 'F'; 
      header[3] = 'F'; 
      header[4] = (byte) (totalDataLen & 0xff); 
      header[5] = (byte) ((totalDataLen >> 8) & 0xff); 
      header[6] = (byte) ((totalDataLen >> 16) & 0xff); 
      header[7] = (byte) ((totalDataLen >> 24) & 0xff); 
      header[8] = 'W'; 
      header[9] = 'A'; 
      header[10] = 'V'; 
      header[11] = 'E'; 
      header[12] = 'f'; // 'fmt ' chunk 
      header[13] = 'm'; 
      header[14] = 't'; 
      header[15] = ' '; 
      header[16] = 16; // 4 bytes: size of 'fmt ' chunk 
      header[17] = 0; 
      header[18] = 0; 
      header[19] = 0; 
      header[20] = 1; // format = 1 
      header[21] = 0; 
      header[22] = (byte) channels; 
      header[23] = 0; 
      header[24] = (byte) (longSampleRate & 0xff); 
      header[25] = (byte) ((longSampleRate >> 8) & 0xff); 
      header[26] = (byte) ((longSampleRate >> 16) & 0xff); 
      header[27] = (byte) ((longSampleRate >> 24) & 0xff); 
      header[28] = (byte) (byteRate & 0xff); 
      header[29] = (byte) ((byteRate >> 8) & 0xff); 
      header[30] = (byte) ((byteRate >> 16) & 0xff); 
      header[31] = (byte) ((byteRate >> 24) & 0xff); 
      header[32] = (byte) (channels * 16/8); // block align 
      header[33] = 0; 
      header[34] = RECORDER_BPP; // bits per sample 
      header[35] = 0; 
      header[36] = 'd'; 
      header[37] = 'a'; 
      header[38] = 't'; 
      header[39] = 'a'; 
      header[40] = (byte) (totalAudioLen & 0xff); 
      header[41] = (byte) ((totalAudioLen >> 8) & 0xff); 
      header[42] = (byte) ((totalAudioLen >> 16) & 0xff); 
      header[43] = (byte) ((totalAudioLen >> 24) & 0xff); 

      out.write(header, 0, 44); 
    } 

} //Fine Classe RecordAudio (AsyncTask) 

@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
    getMenuInflater().inflate(R.menu.test_audio_capture_with_threshold, 
      menu); 
    return true; 

} 


public void resetAquisition() { 
    Log.w(TAG, "resetAquisition"); 
    stopAquisition(); 
    //startButton.setText("WAIT"); 
    startAquisition(); 
} 

public void stopAquisition() { 
    Log.w(TAG, "stopAquisition"); 
    if (started) { 
     started = false; 
     recordTask.cancel(true); 
    } 
} 

public void startAquisition(){ 
    Log.w(TAG, "startAquisition"); 
    Handler handler = new Handler(); 
    handler.postDelayed(new Runnable() { 
     public void run() { 

      //elapsedTime=0; 
      started = true; 
      recordTask = new RecordAudio(); 
      recordTask.execute(); 
      //startButton.setText("RESET"); 
     } 
    }, 500); 
} 


} 

不要忘記添加權限清單文件:

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
package="com.example.testaudiocapturewiththreshold" 
android:versionCode="1" 
android:versionName="1.0" > 
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> 
<uses-permission android:name="android.permission.RECORD_AUDIO" /> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 

<uses-sdk 
    android:minSdkVersion="8" 
    android:targetSdkVersion="17" /> 

<application 
    android:allowBackup="true" 
    android:icon="@drawable/ic_launcher" 
    android:label="@string/app_name" 
    android:theme="@style/AppTheme" > 
    <activity 
     android:name="com.example.testaudiocapturewiththreshold.TestAudioCaptureWithThreshold" 
     android:label="@string/app_name" > 
     <intent-filter> 
      <action android:name="android.intent.action.MAIN" /> 

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

</manifest> 
+0

謝謝!我已經改變了一些代碼以適合我的用例。我檢測到沉默,如果X保持沉默,我停止錄音。 – SjoerdvGestel

+0

@Gaucho我使用了你的代碼,但是如果沒有聲音,你能告訴我如何停止錄音和播放聲音嗎? – Sunny

+0

@Gaucho:感謝真棒代碼..我有一個需求在我的應用程序..我需要檢測用戶的語音,如果沒有用戶輸入的X號。的秒數比它應該提供一個錯誤給用戶..我需要做什麼改變? –

1

對於那些誰沒有找到一個答案yet.It可以檢測沉默,停止記錄,當用戶不是招幾秒鐘不說話。

爲了計算用戶是否已經停止說話,我們可以從錄音的最後一秒開始讀取數據,將其映射到一個數字並將此數字與之前獲得的數字進行比較。將語音輸入中出現較長暫停的置信度得分(0-INF)。

按照此鏈接一切都在那裏解釋。 https://github.com/Kaljurand/speechutils/blob/master/app/src/main/java/ee/ioc/phon/android/speechutils/AbstractAudioRecorder.java感謝Kaljurand和他的精彩代碼。

您可以在此鏈接中找到整個項目。 https://github.com/Kaljurand/K6nele

1

這個post的代碼正在工作。但有一些問題。事實是,這個記錄突然中斷得太厲害了。 聲音通過口吃進行。爲了解決我創建了conter silenceDegree。

見我科特林代碼:

var threshold: Short = 5000 
    val SILENCE_DEGREE = 15 

     //buffer size - need be fixed, established value for IOS compatibility 
       val buffer = ShortArray(MIN_SIZE) 
       var silenceDegree = 0 

       while (record) { 
        val bytesRead = audioRecord?.read(buffer, 0, MIN_SIZE) 

        if (bytesRead != null) { 
         if (bytesRead > 0) { 

           val foundPeak = searchThreshold(buffer, threshold) 

          if (foundPeak == -1) { 
           if (silenceDegree <= SILENCE_DEGREE) { 
            silenceDegree++ 
           } 

          } else { 
           silenceDegree = 0 
          } 

          //stoping to send, only when counter became equals SILENCE_DEGREE 
          if (silenceDegree < SILENCE_DEGREE) { 
          //SEND USEFUL DATA 

         handler.sendDataOnRecord(INSTANCE.shorts2Bytes(buffer)) 
          } 

         } else { 
          if (bytesRead == AudioRecord.ERROR_INVALID_OPERATION) { 
           // This can happen if there is already an active 
           // AudioRecord (e.g. in another tab). 
           record = false; 
          } 
         } 
        }