Search code examples
androidaudionoise-reduction

How to mix two wav files without noise?


I am mixing two wav files. In output file, both files are mixed but there is a lot of noise. How to avoid that noise and get clear output? Are my headers corrupted or is it something else? Any suggestions/help will be appreciated.

Here is the code. (In MainActivity, just call mixSound(). rawToWave() function writes headers for the wav file, i am writing headers after creating mixed file).

private void mixSound() throws IOException {
    AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, 44100, AudioTrack.MODE_STREAM);

    InputStream in1 = getResources().openRawResource(R.raw.bird_caw1);
    InputStream in2 = getResources().openRawResource(R.raw.bird_caw2);

    byte[] arrayMusic1 = null;
    arrayMusic1 = new byte[in1.available()];
    arrayMusic1 = createMusicArray(in1);
    in1.close();

    byte[] arrayMusic2 = null;
    arrayMusic2 = new byte[in2.available()];
    arrayMusic2 = createMusicArray(in2);
    in2.close();


    byte[] output = new byte[arrayMusic1.length];

    audioTrack.play();

    for (int i = 0; i < output.length; i++) {
        float samplef1 = arrayMusic1[i] / 128.0f;
        float samplef2 = arrayMusic2[i] / 128.0f;
        float mixed = samplef1 + samplef2;

        // reduce the volume a bit:
        mixed *= 0.8;
        // hard clipping
        if (mixed > 1.0f) mixed = 1.0f;
        if (mixed < -1.0f) mixed = -1.0f;

        byte outputSample = (byte) (mixed * 128.0f);
        output[i] = outputSample;
    }

    audioTrack.write(output, 0, output.length);
    convertByteToFile(output);
    File file = new File(Environment.getExternalStorageDirectory().getPath() + "/mixed.wav");
    rawToWave(file, file);

}

private void writeInt(final DataOutputStream output, final int value) throws IOException {
    output.write(value >> 0);
    output.write(value >> 8);
    output.write(value >> 16);
    output.write(value >> 24);
}

private void writeShort(final DataOutputStream output, final short value) throws IOException {
    output.write(value >> 0);
    output.write(value >> 8);
}

private void writeString(final DataOutputStream output, final String value) throws IOException {
    for (int i = 0; i < value.length(); i++) {
        output.write(value.charAt(i));
    }
}

private void rawToWave(final File rawFile, final File waveFile) throws IOException {

    byte[] rawData = new byte[(int) rawFile.length()];
    DataInputStream input = null;
    try {
        input = new DataInputStream(new FileInputStream(rawFile));
        input.read(rawData);
    } finally {
        if (input != null) {
            input.close();
        }
    }

    DataOutputStream output = null;
    try {
        output = new DataOutputStream(new FileOutputStream(waveFile));
        // WAVE header
        // see http://ccrma.stanford.edu/courses/422/projects/WaveFormat/
        writeString(output, "RIFF"); // chunk id
        writeInt(output, 36 + rawData.length); // chunk size
        writeString(output, "WAVE"); // format
        writeString(output, "fmt "); // subchunk 1 id
        writeInt(output, 16); // subchunk 1 size
        writeShort(output, (short) 1); // audio format (1 = PCM)
        writeShort(output, (short) 1); // number of channels
        writeInt(output, 8000); // sample rate
        writeInt(output, 8000 * 2); // byte rate
        writeShort(output, (short) 2); // block align
        writeShort(output, (short) 16); // bits per sample
        writeString(output, "data"); // subchunk 2 id
        writeInt(output, rawData.length); // subchunk 2 size
        // Audio data (conversion big endian -> little endian)
        short[] shorts = new short[rawData.length / 2];
        ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts);
        ByteBuffer bytes = ByteBuffer.allocate(shorts.length * 2);
        for (short s : shorts) {
            bytes.putShort(s);
        }
        output.write(bytes.array());
    } finally {
        if (output != null) {
            output.close();
        }
    }
}

Solution

  • After spending few hours, i found the solution and this is working like a charm.

    Step 1: Import musicg-1.4.2.0.jar and compile

    compile files('libs/musicg-1.4.2.0.jar')
    

    Step 2: Create the wav file header

    public class MyWaveHeader {
    
    private static final String TAG = "WaveHeader";
    
    private static final int HEADER_LENGTH = 44;
    
    /** Indicates PCM format. */
    public static final short FORMAT_PCM = 1;
    /** Indicates ALAW format. */
    public static final short FORMAT_ALAW = 6;
    /** Indicates ULAW format. */
    public static final short FORMAT_ULAW = 7;
    
    private short mFormat;
    private short mNumChannels;
    private int mSampleRate;
    private short mBitsPerSample;
    private int mNumBytes;
    
    public MyWaveHeader(short format, short numChannels, int sampleRate, short bitsPerSample, int numBytes) {
        mFormat = format;
        mSampleRate = sampleRate;
        mNumChannels = numChannels;
        mBitsPerSample = bitsPerSample;
        mNumBytes = numBytes;
    }
    
    private static void readId(InputStream in, String id) throws IOException {
        for (int i = 0; i < id.length(); i++) {
            if (id.charAt(i) != in.read()) throw new IOException( id + " tag not present");
        }
    }
    
    private static int readInt(InputStream in) throws IOException {
        return in.read() | (in.read() << 8) | (in.read() << 16) | (in.read() << 24);
    }
    
    private static short readShort(InputStream in) throws IOException {
        return (short)(in.read() | (in.read() << 8));
    }
    
    public int write(OutputStream out) throws IOException {
        /* RIFF header */
        writeId(out, "RIFF");
        writeInt(out, 36 + mNumBytes);
        writeId(out, "WAVE");
    
        /* fmt chunk */
        writeId(out, "fmt ");
        writeInt(out, 16);
        writeShort(out, mFormat);
        writeShort(out, mNumChannels);
        writeInt(out, mSampleRate);
        writeInt(out, mNumChannels * mSampleRate * mBitsPerSample / 8);
        writeShort(out, (short)(mNumChannels * mBitsPerSample / 8));
        writeShort(out, mBitsPerSample);
    
        /* data chunk */
        writeId(out, "data");
        writeInt(out, mNumBytes);
    
        return HEADER_LENGTH;
    }
    
    private static void writeId(OutputStream out, String id) throws IOException {
        for (int i = 0; i < id.length(); i++) out.write(id.charAt(i));
    }
    
    private static void writeInt(OutputStream out, int val) throws IOException {
        out.write(val >> 0);
        out.write(val >> 8);
        out.write(val >> 16);
        out.write(val >> 24);
    }
    
    private static void writeShort(OutputStream out, short val) throws IOException {
        out.write(val >> 0);
        out.write(val >> 8);
    }
    
    @Override
    public String toString() {
        return String.format(
                "WaveHeader format=%d numChannels=%d sampleRate=%d bitsPerSample=%d numBytes=%d",
                mFormat, mNumChannels, mSampleRate, mBitsPerSample, mNumBytes);
    }}
    

    Step 3: Write the function to mix two wav sounds.

    private void mixSound() throws IOException {
    
        InputStream in1 = getResources().openRawResource(R.raw.music1_mono_low);
        InputStream in2 = getResources().openRawResource(R.raw.rec1_mono_low);
    
        Wave w1 = new Wave(in1);
        short[] music1 = w1.getSampleAmplitudes();
    
        in1.close();
    
    
        Wave w2 = new Wave(in2);
        short[] music2 = w2.getSampleAmplitudes();
        in2.close();
    
        byte[] output = new byte[(music1.length > music2.length) ? music2.length
                : music1.length];
    
        for (int i = 0; i < output.length; i++) {
    
            float samplef1 = (music1[i] / 128.0f)*1.2f; // 2^7=128
            float samplef2 = (music2[i] / 128.0f)*1.2f;
    
            float mixed = (samplef1 + samplef2)/2;
    
            byte outputSample = (byte) (mixed * 128.0f);
            output[i] = outputSample;
    
        } // for loop
    
    
        File f = new File("/mnt/sdcard/mixed_sound.wav");
        if (f.exists()) {
            f.delete();
        }
    
        FileOutputStream fo = new FileOutputStream(f);
    
        WaveHeader wh = w2.getWaveHeader();
        MyWaveHeader mwh = new MyWaveHeader((short) wh.getAudioFormat(),
                (short) wh.getChannels(), wh.getSampleRate(),
                (short) wh.getBitsPerSample(), output.length);
        mwh.write(fo);
    
        fo.write(output);
        fo.flush();
        fo.close();
    
        //For playing the sound immediately after mixing
         // MusicPlayer
        MediaPlayer mp = new MediaPlayer();
        // Listeners
        mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    
            @Override
            public void onCompletion(MediaPlayer mp) {
                // TODO Auto-generated method stub
    
            }
        }); // Important
    
        mp.reset();
    
        mp.setDataSource("/mnt/sdcard/mixed_sound.wav");
        mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
    
        mp.prepare();
        mp.start();
    }