android - 如何解决在 android 中合并 WAV 格式 2 录音文件时的失真噪声问题?
问题描述
我有一个可以以WAV 格式录制音频的应用程序。它具有编辑选项,例如:
- 在开头插入
- 在末尾插入
- 插在中间
- 覆盖
- 删除到最后
可以观察到,如果我们多次执行上述任何编辑操作,就会出现失真噪声。
以下是WAV 文件配置详细信息:
- ENCODING_PCM_16BIT
- CHANNEL_IN_MONO
- 8000(sample rate)
WAV 文件头:
fileWriter.setLength(0); // Set file length to 0, to prevent unexpected behavior in case the file already existed
fileWriter.writeBytes("RIFF");
fileWriter.writeInt(0); // Final file size not known yet, write 0
fileWriter.writeBytes("WAVE"); // format
fileWriter.writeBytes("fmt "); // subchunk 1 id
fileWriter.writeInt(Integer.reverseBytes(16)); // Sub-chunk size, 16 for PCM
fileWriter.writeShort(Short.reverseBytes((short) 1)); // AudioFormat, 1 for PCM
fileWriter.writeShort(Short.reverseBytes(samplingSpec.numberOfChannels()));// Number of channels, 1 for mono, 2 for stereo
fileWriter.writeInt(Integer.reverseBytes(samplingSpec.sampleRate())); // Sample rate
fileWriter.writeInt(Integer.reverseBytes(samplingSpec.sampleRate() * samplingSpec.sampleSizeInBytes() * samplingSpec.numberOfChannels())); // Byte rate, SampleRate*NumberOfChannels*BitsPerSample/8
fileWriter.writeShort(Short.reverseBytes((short) (samplingSpec.numberOfChannels() * samplingSpec.sampleSizeInBytes()))); // Block align, NumberOfChannels*BitsPerSample/8
fileWriter.writeShort(Short.reverseBytes(samplingSpec.sampleSizeInBits())); // Bits per sample
fileWriter.writeBytes("data");
fileWriter.writeInt(0); // Data chunk size not known yet, write 0
录制完成后,我们将更新WAV 文件头:
byte[] sizes = ByteBuffer
.allocate(8)
.order(ByteOrder.LITTLE_ENDIAN)
.putInt((int) (fileWriter.length() - 8)) // ChunkSize
.putInt((int) (fileWriter.length() - 44)) // Subchunk2Size
.array();
try {
fileWriter.seek(4);
fileWriter.write(sizes, 0, 4);
// Subchunk2Size
fileWriter.seek(40);
fileWriter.write(sizes, 4, 4);
} catch (IOException ex) {
// Rethrow but we still close accessWave in our finally
throw ex;
} finally {
if (fileWriter != null) {
try {
fileWriter.close();
} catch (IOException ex) {
//
}
}
}
设想:
我正在录制音频,然后我正在执行第一个编辑操作作为最后的插入。代码如下:
RandomAccessFile temp = null;
RandomAccessFile origin = null;
int length;
byte[] sizes = new byte[0];
isUrgent = getUrgentFl();
try {
try {
temp = new RandomAccessFile(tempFile, "rw");
origin = new RandomAccessFile(originalFile, "rw");
} catch (Exception e) {
Log.e("combineWaveFile1 : ", "combineWaveFile1_Exception" + e.getMessage());
}
byte[] buffer = new byte[1024]; // 8192
try {
try {
origin.seek(origin.length());
} catch (Exception e) {
Log.e("combineWaveFile1 : ", "combineWaveFile1_Exception" + e.getMessage());
}
try {
while ((length = temp.read(buffer)) > 0) {
origin.write(buffer, 0, length);
}
} catch (Exception e) {
Log.e("combineWaveFile2 : ", "combineWaveFile2_Exception" + e.getMessage());
}
//////////////////
try {
sizes = ByteBuffer
.allocate(8)
.order(ByteOrder.LITTLE_ENDIAN)
.putInt((int) (origin.length() - 8)) // ChunkSize
.putInt((int) (origin.length() - 44)) // Subchunk2Size
.array();
} catch (Exception e) {
Log.e("combineWaveFile3 : ", "combineWaveFile3_Exception" + e.getMessage());
}
//noinspection CaughtExceptionImmediatelyRethrown
try {
//accessWave = new RandomAccessFile(fileWriter, "rw");
// ChunkSize
//originFile.write(0);
origin.seek(4);
origin.write(sizes, 0, 4);
// Subchunk2Size
origin.seek(40);
origin.write(sizes, 4, 4);
//originFile.write(0);
} catch (Exception e) {
Log.e("combineWaveFile4 : ", "combineWaveFile4_Exception" + e.getMessage());
} finally {
if (tempFile != null) {
try {
temp.close();
File f = new File(tempFile);
if (f.exists()) {
f.delete();
}
} catch (Exception e) {
Log.e("combineWaveFile5 : ", "combineWaveFile5_Exception" + e.getMessage());
}
}
if (origin != null) {
try {
origin.close();
} catch (Exception e) {
Log.e("combineWaveFile6 : ", "combineWaveFile6_Exception" + e.getMessage());
}
}
}
在结束操作完成第一次插入后,我将在操作之间执行插入。它的代码如下:
RandomAccessFile temp = null;
RandomAccessFile origin = null;
RandomAccessFile newRecordFile = null;
int length;
long seekBytesLength = 0;
byte[] sizes = new byte[0];
try {
try {
temp = new RandomAccessFile(tempFile, "rw");
origin = new RandomAccessFile(originalFile, "rw");
} catch (Exception e) {
Log.e("insertInBetween1 : ", "insertInBetween1_Exception" + e.getMessage());
}
byte[] buffer = new byte[1024];
try {
try {
long totalLength = origin.length();
long oneByteOfLength = totalLength / 100;
seekBytesLength = oneByteOfLength * Constants.seekPosition;
//long skipBytesFromLength = totalLength - seekBytesLength;
} catch (Exception e) {
Log.e("insertInBetween2 : ", "insertInBetween2_Exception" + e.getMessage());
}
if (seekBytesLength != 0) {
try {
origin.seek(seekBytesLength);
} catch (Exception e) {
Log.e("insertInBetween3 : ", "insertInBetween3_Exception" + e.getMessage());
}
try {
temp.seek(temp.length());
while ((length = origin.read(buffer)) > 0) {
temp.write(buffer, 0, length);
}
} catch (Exception e) {
Log.e("insertInBetween4 : ", "insertInBetween4_Exception" + e.getMessage());
}
} else {
try {
origin.getChannel().position(0);
temp.seek(temp.length());
while ((length = origin.read(buffer)) > 0) {
temp.write(buffer, 0, length);
}
} catch (Exception e) {
Log.e("insertInBetween5 : ", "insertInBetween5_Exception" + e.getMessage());
}
try {
File originFile = new File(originalFile);
File tmpFile = new File(tempFile);
if (originFile.delete()) {
tmpFile.renameTo(originFile);
}
} catch (Exception e) {
Log.e("insertInBetween6 : ", "insertInBetween6_Exception" + e.getMessage());
}
}
/////////////////
try {
sizes = ByteBuffer
.allocate(8)
.order(ByteOrder.LITTLE_ENDIAN)
.putInt((int) (temp.length() - 8)) // ChunkSize
.putInt((int) (temp.length() - 44)) // Subchunk2Size
.array();
} catch (Exception e) {
Log.e("insertInBetween7 : ", "insertInBetween7_Exception" + e.getMessage());
}
//no inspection CaughtException Immediately Rethrown
try {
temp.seek(4);
temp.write(sizes, 0, 4);
// Subchunk2Size
temp.seek(40);
temp.write(sizes, 4, 4);
} catch (Exception e) {
Log.e("insertInBetween8 : ", "insertInBetween8_Exception" + e.getMessage());
}
/////////////////////
try {
if (seekBytesLength != 0) {
appendFiles(origin, temp, tempFile, seekBytesLength);
}
} catch (Exception e) {
Log.e("insertInBetween9 : ", "insertInBetween9_Exception" + e.getMessage());
}
} catch (Exception e) {
Log.e("insertInBetween10 : ", "insertInBetween10_Exception" + e.getMessage());
}
finally {
if (seekBytesLength == 0) {
File tmpFile = new File(tempFile);
if (tmpFile.exists()) {
tmpFile.delete();
}
}
}
} catch (Exception e) {
Log.e("insertInBetween11 : ", "insertInBetween11_Exception" + e.getMessage());
}
**// appendFiles method :**
int length;
byte[] sizes = new byte[0];
isUrgent = getUrgentFl();
try {
byte[] buffer = new byte[1024];
// byte[] buffer = new byte[BUFFER_SIZE];
try {
try {
originFile.seek(seekBytesLength);
} catch (Exception e) {
Log.e("appendFiles1 : ", "appendFiles1_Exception" + e.getMessage());
}
try {
while ((length = tempFile.read(buffer)) > 0) {
originFile.write(buffer, 0, length);
}
} catch (Exception e) {
Log.e("appendFiles2 : ", "appendFiles2_Exception" + e.getMessage());
}
//////////////////
try {
sizes = ByteBuffer
.allocate(8)
.order(ByteOrder.LITTLE_ENDIAN)
// There are probably a bunch of different/better ways to calculate
// these two given your circumstances. Cast should be safe since if the WAV is
// > 4 GB we've already made a terrible mistake.
.putInt((int) (originFile.length() - 8)) // ChunkSize
.putInt((int) (originFile.length() - 44)) // Subchunk2Size
.array();
} catch (Exception e) {
Log.e("appendFiles3 : ", "appendFiles3_Exception" + e.getMessage());
}
//noinspection CaughtExceptionImmediatelyRethrown
try {
originFile.seek(4);
originFile.write(sizes, 0, 4);
// Subchunk2Size
originFile.seek(40);
originFile.write(sizes, 4, 4);
//originFile.write(0);
} catch (Exception e) {
Log.e("appendFiles4 : ", "appendFiles4_Exception" + e.getMessage());
} finally {
if (originFile != null) {
try {
originFile.close();
} catch (Exception e) {
Log.e("appendFiles6 : ", "appendFiles6_Exception" + e.getMessage());
}
}
if (tempFile != null) {
try {
tempFile.close();
File f = new File(tempFileName);
if (f.exists()) {
f.delete();
}
} catch (Exception e) {
Log.e("appendFiles5 : ", "appendFiles5_Exception" + e.getMessage());
}
}
}
任何编辑操作都会随机出现失真问题。所以,请指导我,如何处理这个问题?
对不起,我的英语不好。
解决方案
不确定这会有多大帮助,但从音频工程师的角度来看,除非您在音频的末端和要添加的部分都搜索零交叉点,否则您会在编辑点处出现失真。
我不确定如何编写代码,但我找到了这个 GitHub 页面,这可能会有所帮助。 https://gist.github.com/endolith/129445
推荐阅读
- c# - 如何在 C# 中以相同的顺序从列表中向哈希表添加键
- ruby - 访问和抓取零星可用的 Wikipedia 部分
- firebase-analytics - 如果 Firebase Analytics 上的事件数量超过 500,会发生什么情况?
- typescript - 为什么 TypeScript 代码编译为 JavaScript 后不再兼容?
- python - MQTT msg.payload 有趣的字符
- jquery - 更改单击菜单栏上的字体真棒图标
- javascript - 使用组件而不是视图将 JSON 数据映射到 Angular 6 数组中
- fetch-api - 如何使用 bs-fetch 传递查询字符串参数?
- r - R中的XML到JSON转换
- python - 使用 tf.contrib.learn.preprocessing.VocabularyProcessor 后词汇量变小了