【问题标题】:Exactly synchronize AudioRecord and AudioTrack in Android在Android中精确同步AudioRecord和AudioTrack
【发布时间】:2017-12-12 16:53:08
【问题描述】:

我正在努力

    1. 使用 AudioRecord 录制 PCM 数据并将它们保存到 .wav 文件中。 (完成)
    1. 稍后在播放之前录制的文件时录制另一个 PCM 文件
    1. 将新录音保存到另一个文件
    1. 混合(叠加)第一个录音和新录音。

我的问题是第二个录制文件(2.)和第一个录制文件(1.)不同步。一旦我将它们混合在一起,我就会听到我没有记录的延迟。为了测试我的应用程序,我对着麦克风说“测试 1 2 3”。在第二个录音中,我同时说“Test 1 2 3”。但是,在混合(叠加)我的 2 个文件后,我得到了延迟。

我做错了什么?

final Thread recordingThread = new Thread(new Runnable() {
                final int SAMPLING_RATE = 44100;
                final int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;
                final int CHANNEL_IN_CONFIG = AudioFormat.CHANNEL_IN_MONO;
                final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
                final int BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLING_RATE, CHANNEL_IN_CONFIG, AUDIO_FORMAT);
                final String AUDIO_RECORDING_FILE_NAME = project.getPath()+"/track"+String.valueOf(project.getTrackNumber())+".raw";
                final int playbackBufferSize = AudioTrack.getMinBufferSize(SAMPLING_RATE, CHANNEL_IN_CONFIG, AUDIO_FORMAT);


                @Override
                public void run() {
                    android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
                    Log.d("Record", "[Starting recording]");

                    byte audioData[] = new byte[BUFFER_SIZE];

                    boolean playSound = true;

                    ByteArrayOutputStream playbackOutput = new ByteArrayOutputStream();
                    BufferedInputStream in = null;
                    try {
                        in = new BufferedInputStream(new FileInputStream(project.getPath()+"/output.wav"));
                    } catch (FileNotFoundException e) {
                        playSound = false;
                    }

                    int playbackRead = 1;
                    byte[] playbackBuffer = new byte[BUFFER_SIZE];

                    AudioRecord recorder = new AudioRecord(AUDIO_SOURCE,
                            SAMPLING_RATE, CHANNEL_IN_CONFIG,
                            AUDIO_FORMAT, BUFFER_SIZE);

                    AudioTrack player = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLING_RATE, AudioFormat.CHANNEL_OUT_MONO, AUDIO_FORMAT, playbackBufferSize, AudioTrack.MODE_STREAM);
                    player.play();

                    recorder.startRecording();
                    String filePath = AUDIO_RECORDING_FILE_NAME;
                    BufferedOutputStream os = null;
                    try {
                        os = new BufferedOutputStream(new FileOutputStream(filePath));
                    } catch (FileNotFoundException e) {
                        Log.e("Record", "File not found for recording ", e);
                    }

                    while (!mStop) {

                        int status = recorder.read(audioData, 0, audioData.length);

                        if (status == AudioRecord.ERROR_INVALID_OPERATION ||
                                status == AudioRecord.ERROR_BAD_VALUE) {
                            Log.e("Record", "Error reading audio data!");
                            return;
                        }

                        try {


                            os.write(audioData, 0, audioData.length);
                            if (playSound) {
                                try {
                                    if (playbackRead > 0) {
                                        playbackRead = in.read(playbackBuffer);
                                    }
                                    if (playbackRead > 0){
                                        player.write(playbackBuffer, 0, playbackBuffer.length);
                                    }
                                } catch (IOException e) {
                                    Toast.makeText(MainActivity.this, "ERROR!", Toast.LENGTH_SHORT).show();
                                    return;
                                }
                            }
                        } catch (IOException e) {
                            Log.e("Record", "Error saving recording ", e);
                            return;
                        }
                    }

                    try {
                        os.close();

                        recorder.stop();
                        recorder.release();
                        player.stop();
                        player.release();

                        Log.v("Record", "Recording done…");
                        mStop = false;
                        File out = new File(project.getPath()+"/output.wav");
                        if (!out.exists()) {
                            out.createNewFile();
                            SoundUtils.rawToWave(new File(AUDIO_RECORDING_FILE_NAME), out, SAMPLING_RATE);
                        } else {
                            mixSound(project.getPath()+"/track1.raw", project.getPath()+"/track2.raw", project.getPath()+"/track3.raw", project.getPath()+"/track4.raw");
                        }

                    } catch (IOException e) {
                        Log.e("Record", "Error when releasing", e);
                    }
                }
            });

解释:

  • 在线程中运行
  • 使用相同的设置创建 AudioTrack 和 AudioRecord
  • boolean playSound:如果第一次录制已经完成并且 wav 可用,则为 true
  • 在循环中:
    • 读取录音机音频数据并将其写入输出流
    • 读取部分之前录制的 wav 并将其写入播放器

(- 一旦没有东西可以播放,playbackRead 为 -1)

之后,我尝试混合我的录音。但是,我的第二次录制有延迟,我没​​有录制。

我做错了什么?我如何(几乎)完全正确地同步 AudioRecord 和 AudioTrack,以便当我说某事时,它会在我录制时的背景录制位置播放?

【问题讨论】:

  • 真的有可能吗?
  • 在一段时间内发出一个频率,在后处理的时候检测录音的延迟呢?

标签: java android audio audio-recording java-audio


【解决方案1】:

最后我找到了一个非常完美的解决方案,我想和以后遇到这个问题的每个人分享。我打印出我正在记录的 pcm 数据的每个 short 值。它看起来像这样:

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ..., 0.0025, 0.0026, 0.0163, 0.0123, ...

如您所见,录音开头有很多零。但是在正常安静的环境中,背景中总会有(非常安静的)噪音。它不能正好是 0。

我想,也许这个范围,所有的 0 都是从录音开始到第一个声音真正接收到文件的时间。

=> 这是延迟

在第二个录音中(将前一个录音作为背景声音播放时)我从整个数据中删除了开头的 0,以便声音立即开始。然后我将两个录音混合在一起,它们非常精确地同步。

我不知道这是否是一种在所有情况下都可以消除延迟的方法,但我在几部手机上进行了尝试,效果很好。我很高兴能够解决我的问题。

长话短说:要消除延迟,请删除声音数据前面的所有 0。

【讨论】:

  • 你是如何从字节缓冲区数组中打印出短值
  • 那是很久以前的事了,我已经不记得了。但我认为在 os.write(audioData...) 行之后,您可以使用 Log.d("YourTagNameHere", audioData) 。希望对您有所帮助。
猜你喜欢
  • 2011-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-19
  • 2013-02-14
  • 1970-01-01
  • 2020-02-15
  • 1970-01-01
相关资源
最近更新 更多