【问题标题】:Play Back audio from mic in real Time实时播放来自麦克风的音频
【发布时间】:2014-11-21 17:45:54
【问题描述】:

我有一个函数,它记录音频并将其存储到文件中。看起来是这样的:

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,我就能听到音频。 但我想做的是实时播放声音,即,一旦我开始录制,我就希望播放音频。我应该怎么做才能做到这一点?

【问题讨论】:

    标签: android audio buffer


    【解决方案1】:

    这对我有用:

    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 布局中还需要两个按钮,ID 为start_buttonstop_button

    此示例还包含一个 EchoCanceler!

    祝你好运!

    【讨论】:

    • 但是延迟是多少?
    • 真的是高性能。当然会有一些延迟,但我想这是解决问题的最佳最快方法。如果您需要更多信息,我可以建议您观看youtube.com/watch?v=d3kfEeMZ65c
    • 我在这行遇到了 NullPointer 异常:AcousticEchoCanceler echoCancler = AcousticEchoCanceler.create(record.getAudioSessionId()); echoCancler.setEnabled(true);
    • 您可以尝试使用 EchoCanceler 移除该部分。
    • 这很好用,但值得注意的是,在 Android 6.0 及更高版本中,您需要使用 ActivityCompat.requestPermissions() 函数请求 RECORD_AUDIO 的权限。
    猜你喜欢
    • 1970-01-01
    • 2011-08-09
    • 2018-06-18
    • 2018-02-14
    • 2023-02-12
    • 2010-11-26
    • 2021-09-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多