How to resolve distortion noise issue in merging WAV format 2 audio recording files in android?

Viewed 146

I have one application that can record audios in WAV format. It has editing options like:

  • insert at the start
  • insert at the end
  • insert in between
  • override
  • delete up to end

It is observed that distorted noise occurs if we perform any of above editing operations more than one time.

Below are the WAV file configuration details:

- ENCODING_PCM_16BIT
- CHANNEL_IN_MONO
- 8000(sample rate)

WAV file header:

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

Once recording completed, we are updating the WAV file header:

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) {
                    //
                }
            }
        }

Scenario:

I am recording Audio then I am performing first edit operation as insert at the end. The code is as below:

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());
                        }
                    }
                } 

After completing first insert at the end operation then I am performing insert in between operation. Code for it as below:

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());
                        }
                    }
                }
 

The distortion issue occurs here randomly for any edit operation. So, please guide me, how to handle this issue?

Sorry for my bad English.

1 Answers

Not sure how much help this will be but from an Audio Engineers perspective you will get distortion at edit points unless you are searching for a zero crossing point in the audio on both the end and the part you are adding.

I am not sure how to code that though, but I founf this GitHub page which might help. https://gist.github.com/endolith/129445

Related