首页 > 解决方案 > 如何解决在 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());
                        }
                    }
                }
 

任何编辑操作都会随机出现失真问题。所以,请指导我,如何处理这个问题?

对不起,我的英语不好。

标签: androidaudiowavrecording

解决方案


不确定这会有多大帮助,但从音频工程师的角度来看,除非您在音频的末端和要添加的部分都搜索零交叉点,否则您会在编辑点处出现失真。

我不确定如何编写代码,但我找到了这个 GitHub 页面,这可能会有所帮助。 https://gist.github.com/endolith/129445


推荐阅读