实时播放麦克风录制的音频

5

我有一个函数,用于录制音频并将其存储到文件中。它看起来像这样:

private void startRecord(){

      File file = new File(Environment.getExternalStorageDirectory(), "test.pcm"); 

      int sampleFreq = (Integer)spFrequency.getSelectedItem();

      try {
       file.createNewFile();

       OutputStream outputStream = new FileOutputStream(file);
       BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
       DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream);

       int minBufferSize = AudioRecord.getMinBufferSize(sampleFreq, 
         AudioFormat.CHANNEL_CONFIGURATION_MONO, 
         AudioFormat.ENCODING_PCM_16BIT);

       short[] audioData = new short[minBufferSize];

       AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
         sampleFreq,
         AudioFormat.CHANNEL_CONFIGURATION_MONO,
         AudioFormat.ENCODING_PCM_16BIT,
         minBufferSize);

       audioRecord.startRecording();

       while(recording){
        int numberOfShort = audioRecord.read(audioData, 0, minBufferSize);
        for(int i = 0; i < numberOfShort; i++){
         dataOutputStream.writeShort(audioData[i]);
        }

       }

       audioRecord.stop();
       dataOutputStream.close();

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

     }

我有另一个函数,称为playrecord,用于播放这个记录的音频:
void playRecord(){

      File file = new File(Environment.getExternalStorageDirectory(), "test.pcm");

            int shortSizeInBytes = Short.SIZE/Byte.SIZE;

      int bufferSizeInBytes = (int)(file.length()/shortSizeInBytes);
      short[] audioData = new short[bufferSizeInBytes];

      try {
       InputStream inputStream = new FileInputStream(file);
       BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
       DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);

       int i = 0;
       while(dataInputStream.available() > 0){
        audioData[i] = dataInputStream.readShort();
        i++;
       }

       dataInputStream.close();

       int sampleFreq = (Integer)spFrequency.getSelectedItem();

       AudioTrack audioTrack = new AudioTrack(
         AudioManager.STREAM_MUSIC,
         sampleFreq,
         AudioFormat.CHANNEL_CONFIGURATION_MONO,
         AudioFormat.ENCODING_PCM_16BIT,
         bufferSizeInBytes,
         AudioTrack.MODE_STREAM);

       audioTrack.play();
       audioTrack.write(audioData, 0, bufferSizeInBytes);


      } catch (FileNotFoundException e) {
       e.printStackTrace();
      } catch (IOException e) {
       e.printStackTrace();
      }
     }

这两个函数单独使用时都能正常工作。如果我调用`startRecord()`,然后调用`playRecord()`,我就能听到录音。但是,我想要的是实时播放录音,也就是说,一旦我开始录音,就要立即播放音频。我该怎么做才能实现这个目标?
1个回答

14

这对我有效:

boolean isRecording = false;
AudioManager am = null;
AudioRecord record = null;
AudioTrack track = null;

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    setVolumeControlStream(AudioManager.MODE_IN_COMMUNICATION);

    initRecordAndTrack();

    am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
    am.setSpeakerphoneOn(true);

    (new Thread()
    {
        @Override
        public void run()
        {
            recordAndPlay();
        }
    }).start();

    Button startButton = (Button) findViewById(R.id.start_button);
    startButton.setOnClickListener(new OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            if (!isRecording)
            {
                startRecordAndPlay();
            }
        }
    });
    Button stopButton = (Button) findViewById(R.id.stop_button);
    stopButton.setOnClickListener(new OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            if (isRecording)
            {
                stopRecordAndPlay();
            }
        }
    });
}

private void initRecordAndTrack()
{
    int min = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
    record = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, 8000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
                             min);
    if (AcousticEchoCanceler.isAvailable())
    {
        AcousticEchoCanceler echoCancler = AcousticEchoCanceler.create(record.getAudioSessionId());
        echoCancler.setEnabled(true);
    }
    int maxJitter = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
    track = new AudioTrack(AudioManager.MODE_IN_COMMUNICATION, 8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, maxJitter,
                           AudioTrack.MODE_STREAM);
}

private void recordAndPlay()
{
    short[] lin = new short[1024];
    int num = 0;
    am.setMode(AudioManager.MODE_IN_COMMUNICATION);
    while (true)
    {
        if (isRecording)
        {
            num = record.read(lin, 0, 1024);
            track.write(lin, 0, num);
        }
    }
}

private void startRecordAndPlay()
{
    record.startRecording();
    track.play();
    isRecording = true;
}

private void stopRecordAndPlay()
{
    record.stop();
    track.pause();
    isRecording = false;
}

你的activity_main布局中还需要两个按钮,分别是start_buttonstop_button

这个示例还包含一个EchoCanceler!

祝你好运!


但延迟有多少? - harveyslash
它非常高效。当然会有一些延迟,但我想这是解决问题的最快速度和最佳方式。如果您需要更多信息,我建议您观看 https://www.youtube.com/watch?v=d3kfEeMZ65c。 - xry
1
我在以下代码行收到了一个 NullPointer 异常:AcousticEchoCanceler echoCancler = AcousticEchoCanceler.create(record.getAudioSessionId()); echoCancler.setEnabled(true); - AlinaBM
你可以尝试移除EchoCanceler的部分。 - xry
2
这个很好用,但值得注意的是,在Android 6.0及以上版本中,您需要使用ActivityCompat.requestPermissions()函数请求RECORD_AUDIO权限。 - James Heald

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