Точно синхронизировать AudioRecord и AudioTrack в Android

Я пытаюсь

    1. Запишите данные PCM с помощью AudioRecord и сохраните их в файл.wav. (Готово)
    1. Позже запишите другой файл PCM во время воспроизведения ранее записанного файла
    1. Сохранить новую запись в другой файл
    1. Смешайте (наложите) первую и новую записи.

Моя проблема в том, что второй записанный файл (2.) и первый записанный файл (1.) не являются синхронными. Как только я смешал их вместе, я услышал задержку, которую я не записал. Чтобы проверить мое приложение, я сказал "Тест 1 2 3" в микрофон. Во второй записи я сказал "Тест 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: true, если первая запись уже сделана и доступен wav
  • в петле:
    • Прочитайте аудио данные рекордера и запишите их в выходной поток
    • Прочитайте часть ранее записанного wav и запишите его в плеер

(- Когда больше нечего играть, PlayRead равен -1)

После этого я пытаюсь свести мои записи. Однако моя вторая запись имеет задержку, которую я не записал.

Что я делаю неправильно? Как мне (почти) полностью правильно синхронизировать AudioRecord и AudioTrack, чтобы, когда я что-то говорил, оно воспроизводилось в положении фоновой записи, как это было при записи?

1 ответ

Решение

Наконец, я нашел очень удачное решение и хотел бы поделиться им со всеми, кто столкнется с этой проблемой в будущем. Я распечатал каждый short Значение данных PCM я записывал. Это выглядело так:

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, которые находятся перед вашими звуковыми данными.

Другие вопросы по тегам