【问题标题】:Immediate Audio Input & Output Android即时音频输入和输出 Android
【发布时间】:2016-04-02 01:20:37
【问题描述】:

在我的 Android 应用程序中,我想从智能手机的麦克风中获取一些音频并立即播放,就像麦克风一样,没有延迟。我目前正在考虑使用 AudioRecordAudioTrack 类(根据我的阅读),但我不太确定如何继续。

我在 Stack Overflow 上查看了其他一些问题,但它们并没有完全回答我想做的事情。大多数是 2012 年的。

那么如何使用这些类同时输入和输出音频呢?

还:我看过MediaRecorder API,但从我读到的内容来看,这需要您将音频保存到一个文件中,而我不想这样做。可以tweeked 来满足我的要求吗?还是我最好只使用AudioRecord

谢谢

编辑:

这是@Pradip Pramanick 建议的我更新的代码:

final Thread record = new Thread(new Runnable() {
        @Override
        public void run() {
            while (!Thread.interrupted()) {
                MediaRecorder microphone = new MediaRecorder();
                microphone.setAudioSource(MediaRecorder.AudioSource.MIC);
                    microphone.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
                microphone.setOutputFile(filename);
                microphone.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
                try {
                    microphone.prepare();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                microphone.start();
            }
        }
    });

    final Thread play = new Thread(new Runnable() {
        @Override
        public void run() {
            while (!Thread.interrupted()) {
                player = new MediaPlayer();
                try {
                    player.setDataSource(filename);
                    player.prepare();
                    player.start();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    });

我收到了Illegal State Exception | Start failed: -38。但我在microphone.prepare 之后打电话给microphone.start... 似乎是什么问题?我搜索了其他线程,这些线程说可能有其他后台应用程序使用麦克风。我搜索了我的设备:Moto X Play(第 3 代),但没有找到。 (我什至关闭了“Ok Google”语音识别,但错误不断出现)。

错误日志:

这里是显示最近错误的 log-cat:

01-31 09:37:21.064 344-3339/? E/MediaPlayerService: offset error
01-31 09:37:21.065 1835-1922/com.synerflow.testapp E/MediaPlayer: Unable to create media player

01-31 09:37:21.065 1835-1922/com.synerflow.testapp I/Player: player.prepare() has failed
01-31 09:37:21.065 1835-1922/com.synerflow.testapp W/System.err: java.io.IOException: setDataSourceFD failed.: status=0x80000000

IO Exception 似乎在player.setDataSource(filename),文件名变量是一个字符串:Environment.getExternalStorageDirectory().getAbsolutePath() + "\voice.3gp"

【问题讨论】:

  • 应该只能够传输类似stackoverflow.com/questions/5381969/… 的流。也许也有一个小缓冲区。只是想知道为什么真的 - 如果你这样做了,你是否会遭受非常严重的音频反馈。
  • 是的,问题是由于播放器和录音机都试图读取和写入同一个文件。使用可以容纳 1ms 音频的小缓冲区。然后使用两个同步线程:记录器线程将数据放入缓冲区并设置一个标志。检查标志的播放器线程开始播放。参考经典的生产者-消费者问题

标签: android audio microphone audiorecord audiotrack


【解决方案1】:

据我所知,它可以通过一种非常简单的方式完成。我没试过,你试试。我认为它会起作用:

创建两个线程,一个用于录制另一个用于播放。 假设线程是 TRecord 和 TPlay。

在 TRecord 的 run 方法中这样做:

public void run(){
        MediaRecorder mRecorder = null;
        mRecorder = new MediaRecorder();
        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        mRecorder.setOutputFile(mFileName);
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

        try {
            mRecorder.prepare();
        } catch (IOException e) {
            //todo
        }

        mRecorder.start();
}

TPlay 的 run 方法会这样做:

public void run() {
MediaPlayer   mPlayer = null;
mPlayer = new MediaPlayer();
        try {
            mPlayer.setDataSource(mFileName);
            mPlayer.prepare();
            mPlayer.start();
        } catch (IOException e) {
            //todo
        }
}

现在在 mainactivity 上简单地创建两个线程。首先启动 TRecord 线程,然后启动 Tplay 。试试看。

这是文件扩展名的代码:

mFileName = Environment.getExternalStorageDirectory().getAbsolutePath();
 mFileName += "/audiorecordtest.3gp";

【讨论】:

  • 嗨,在这种情况下,mFileName 是什么?它是预定义的文件吗?另外,我想在我说完之后摆脱文件。如何做到这一点?
  • 另外,在这种情况下文件的扩展名应该是什么?您能否包含设置输出文件/数据源的代码?
  • 嗨,这给了我一个错误:Start Failed: -38 | IllegalStateException。但我在准备后打电话给开始。似乎是什么问题?
  • 原来你不能使用“MediaRecorder”,当其他一些服务使用麦克风时不能使用。查看this。检查是否发生任何类似的事情。否则使用其他一些类,例如 AudioRecord。
  • 您也可以尝试使用 AsynkTask 执行任一任务(录制/播放)并检查它是否满足您的延迟要求
【解决方案2】:

这在 Android 上实际上非常棘手。谷歌自己有一个很好的(但有点长)video 解释这些问题。

他们还为各种设备提供a page explaining their latency testing methods and benchmarks

基本上,普通的 Android 无法实现零延迟音频,但没有什么能阻止硬件合作伙伴添加所需的硬件和平台扩展来做到这一点。

【讨论】:

    【解决方案3】:

    你可以试试谷歌双簧管。

    Oboe 是一个 C++ 库,可让您轻松在 Android 上构建高性能音频应用程序。

    Oboe 已经有一个“即时音频输入和输出 Android”的示例

    LiveEffect 示例

    此示例只是将音频从输入流循环到输出流以 演示 2 个流接口的用法。

    https://github.com/google/oboe/tree/master/samples/LiveEffect

    【讨论】:

      【解决方案4】:

      试试这个。我没有运行这个。

            recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
            RECORDER_SAMPLERATE, RECORDER_CHANNELS,
            RECORDER_AUDIO_ENCODING, bufferSize * BytesPerElement);
      
          recorder.startRecording();
      
          isRecording = true;
      
          recordingThread = new Thread(new Runnable() {
      
           public void run() {
      
            try {
                int intSize = android.media.AudioTrack.getMinBufferSize(RECORDER_SAMPLERATE,AudioFormat.CHANNEL_OUT_MONO , RECORDER_AUDIO_ENCODING);
                byte[] sData = new byte[bufferSize];
                AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, RECORDER_SAMPLERATE, AudioFormat.CHANNEL_OUT_MONO, RECORDER_AUDIO_ENCODING, intSize, AudioTrack.MODE_STREAM);
                while(isRecording){
                recorder.read(sData, 0, bufferSize);  //isRecording = false; onStop button
      
                if (at!=null) { 
                    at.play();
                    // Write the byte array to the track
                    at.write(sData, 0, sData.length); 
                    at.stop();
                    at.release();
                }
                }
          } catch (IOException e) {
              e.printStackTrace();
          }
       }
        }, "AudioRecorder Thread");
          recordingThread.start();
      

      【讨论】:

      • 它是我之前的一个项目的一部分,并且工作正常。您需要正确组装它并初始化采样率 44100 等变量。:)
      • 所以,它有效。但声音非常沙哑,非常奇怪。为什么会发生这种情况?还有,BytesPerElement多少钱?
      • 尝试采样率 44100 和 RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT 。我希望您已经考虑过 buffersize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE, RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING) 如果可行,请不要忘记标记为答案。 :)
      • 嗨,我有这些参数作为采样率和编码。它仍然发出噼里啪啦的声音。 BytesPerElement 是什么,值是什么?我的频道也是 CHANNEL_IN_MONO。对吗?
      • bytesperelement=2 频道正确。您可以参考开发者 android 网站了解更多选项
      猜你喜欢
      • 2012-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-26
      • 2013-07-14
      • 2011-12-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多