在 Android 中准确同步 AudioRecord 和 AudioTrack
Exactly synchronize AudioRecord and AudioTrack in Android
我正在尝试
- 使用 AudioRecord 录制 PCM 数据并将其保存为 .wav 文件。 (完成)
- 稍后在播放之前录制的文件时录制另一个 PCM 文件
- 将新录音保存到另一个文件
- 混合(叠加)第一个录音和新录音。
我的问题是第二个录制文件(2.) 和第一个录制文件(1.) 不同步。一旦我将它们混合在一起,我就会听到我没有记录的延迟。为了测试我的应用程序,我对着麦克风说 'Test 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,以便当我说某事时,它会在后台录音的位置播放,就像我录音时一样?
终于找到了一个非常完美的解决方案,分享给以后遇到这个问题的大家。我打印出我正在记录的 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。
我正在尝试
- 使用 AudioRecord 录制 PCM 数据并将其保存为 .wav 文件。 (完成)
- 稍后在播放之前录制的文件时录制另一个 PCM 文件
- 将新录音保存到另一个文件
- 混合(叠加)第一个录音和新录音。
我的问题是第二个录制文件(2.) 和第一个录制文件(1.) 不同步。一旦我将它们混合在一起,我就会听到我没有记录的延迟。为了测试我的应用程序,我对着麦克风说 'Test 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,以便当我说某事时,它会在后台录音的位置播放,就像我录音时一样?
终于找到了一个非常完美的解决方案,分享给以后遇到这个问题的大家。我打印出我正在记录的 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。