Точно синхронизировать AudioRecord и AudioTrack в Android
Я пытаюсь
- Запишите данные PCM с помощью AudioRecord и сохраните их в файл.wav. (Готово)
- Позже запишите другой файл PCM во время воспроизведения ранее записанного файла
- Сохранить новую запись в другой файл
- Смешайте (наложите) первую и новую записи.
Моя проблема в том, что второй записанный файл (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, которые находятся перед вашими звуковыми данными.