Java:将4个单独的音频字节数组组合成单个wav音频文件 [英] Java: Combining 4 separate audio byte arrays into single wav audio file

查看:98
本文介绍了Java:将4个单独的音频字节数组组合成单个wav音频文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图将4个单独的字节数组组合到一个文件中,但是我只收到空指针异常,而且我不确定为什么.我的音频格式是16位PCM签名的,我知道我应该使用短而不是字节,但老实说,我在这一切中完全迷失了.

I've tried to combine 4 separate byte arrays into a single file but I'm only getting null pointer exceptions and I'm not sure why. My audio format is 16 bit PCM signed and I know I should be using short instead of bytes but honestly I'm quite lost in it all.

private short[] mixByteBuffers(byte[] bufferA, byte[] bufferB) {
    short[] first_array = new short[bufferA.length/2];
    short[] second_array = new short [bufferB.length/2];
    short[] final_array = null;

    if(first_array.length > second_array.length) {
        short[] temp_array = new short[bufferA.length];

        for (int i = 0; i < temp_array.length; i++) {
            int mixed=(int)first_array[i] + (int)second_array[i];
            if (mixed>32767) mixed=32767;
            if (mixed<-32768) mixed=-32768;
            temp_array[i] = (short)mixed;
            final_array = temp_array;
        }
    }
    else {
        short[] temp_array = new short[bufferB.length];

        for (int i = 0; i < temp_array.length; i++) {
            int mixed=(int)first_array[i] + (int)second_array[i];
            if (mixed>32767) mixed=32767;
            if (mixed<-32768) mixed=-32768;
            temp_array[i] = (short)mixed;
            final_array = temp_array;
        }        
    }
    return final_array;
}

这是我目前正在尝试的操作,但返回的行是 java.lang.ArrayIndexOutOfBoundsException:0

This is what I'm trying at the moment but it's returning with java.lang.ArrayIndexOutOfBoundsException: 0 at line

int mixed = (int)first_array[i] + (int)second_array[i];

我的数组长度不一样,这就是我调用函数的方式:

My arrays aren't all the same length, this is how I call the function:

public void combineAudio() {
    short[] combinationOne = mixByteBuffers(tempByteArray1, tempByteArray2);
    short[] combinationTwo = mixByteBuffers(tempByteArray3, tempByteArray4);
    short[] channelsCombinedAll = mixShortBuffers(combinationOne, combinationTwo);
    byte[] bytesCombined = new byte[channelsCombinedAll.length * 2];
    ByteBuffer.wrap(bytesCombined).order(ByteOrder.LITTLE_ENDIAN)
        .asShortBuffer().put(channelsCombinedAll);

    mixedByteArray = bytesCombined;
}

肯定有比我目前正在做的更好的方法,这让我非常疯狂.

There's got to be a better way than what I'm doing currently, it's driving me absolutely crazy.

推荐答案

要将两个 byte 数组与16位声音样本混合,应首先将这些数组转换为 int 数组,即基于样本的数组,然后将它们添加(混合),然后转换回字节数组.从 byte 数组转换为 int 数组时,需要确保使用正确的

To mix two byte arrays with 16 bit sound samples, you should first convert those arrays to int arrays, i.e., sample-based arrays, then add them (to mix) and then convert back to byte arrays. When converting from byte array to int array, you need to make sure you use the correct endianness (byte order).

这是一些使您可以混合两个数组的代码.最后,有一些示例代码(使用正弦波)演示了该方法.请注意,这可能不是编码的理想方法,而是一个演示该概念的可行示例.如 Phil建议的那样,使用流或线可能是更聪明的总体方法.

Here's some code that lets you mix two arrays. At the end there is some sample code (using sine waves) that demonstrates the approach. Note that this is may not be the ideal way of coding this, but a working example to demonstrate the concept. Using streams or lines, as Phil recommends is probably the smarter overall approach.

祝你好运!

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;

public class MixDemo {

    public static byte[] mix(final byte[] a, final byte[] b, final boolean bigEndian) {
        final byte[] aa;
        final byte[] bb;

        final int length = Math.max(a.length, b.length);
        // ensure same lengths
        if (a.length != b.length) {
            aa = new byte[length];
            bb = new byte[length];
            System.arraycopy(a, 0, aa, 0, a.length);
            System.arraycopy(b, 0, bb, 0, b.length);
        } else {
            aa = a;
            bb = b;
        }

        // convert to samples
        final int[] aSamples = toSamples(aa, bigEndian);
        final int[] bSamples = toSamples(bb, bigEndian);

        // mix by adding
        final int[] mix = new int[aSamples.length];
        for (int i=0; i<mix.length; i++) {
            mix[i] = aSamples[i] + bSamples[i];
            // enforce min and max (may introduce clipping)
            mix[i] = Math.min(Short.MAX_VALUE, mix[i]);
            mix[i] = Math.max(Short.MIN_VALUE, mix[i]);
        }

        // convert back to bytes
        return toBytes(mix, bigEndian);
    }

    private static int[] toSamples(final byte[] byteSamples, final boolean bigEndian) {
        final int bytesPerChannel = 2;
        final int length = byteSamples.length / bytesPerChannel;
        if ((length % 2) != 0) throw new IllegalArgumentException("For 16 bit audio, length must be even: " + length);
        final int[] samples = new int[length];
        for (int sampleNumber = 0; sampleNumber < length; sampleNumber++) {
            final int sampleOffset = sampleNumber * bytesPerChannel;
            final int sample = bigEndian
                    ? byteToIntBigEndian(byteSamples, sampleOffset, bytesPerChannel)
                    : byteToIntLittleEndian(byteSamples, sampleOffset, bytesPerChannel);
            samples[sampleNumber] = sample;
        }
        return samples;
    }

    private static byte[] toBytes(final int[] intSamples, final boolean bigEndian) {
        final int bytesPerChannel = 2;
        final int length = intSamples.length * bytesPerChannel;
        final byte[] bytes = new byte[length];
        for (int sampleNumber = 0; sampleNumber < intSamples.length; sampleNumber++) {
            final byte[] b = bigEndian
                    ? intToByteBigEndian(intSamples[sampleNumber], bytesPerChannel)
                    : intToByteLittleEndian(intSamples[sampleNumber], bytesPerChannel);
            System.arraycopy(b, 0, bytes, sampleNumber * bytesPerChannel, bytesPerChannel);
        }
        return bytes;
    }

    // from https://github.com/hendriks73/jipes/blob/master/src/main/java/com/tagtraum/jipes/audio/AudioSignalSource.java#L238
    private static int byteToIntLittleEndian(final byte[] buf, final int offset, final int bytesPerSample) {
        int sample = 0;
        for (int byteIndex = 0; byteIndex < bytesPerSample; byteIndex++) {
            final int aByte = buf[offset + byteIndex] & 0xff;
            sample += aByte << 8 * (byteIndex);
        }
        return (short)sample;
    }

    // from https://github.com/hendriks73/jipes/blob/master/src/main/java/com/tagtraum/jipes/audio/AudioSignalSource.java#L247
    private static int byteToIntBigEndian(final byte[] buf, final int offset, final int bytesPerSample) {
        int sample = 0;
        for (int byteIndex = 0; byteIndex < bytesPerSample; byteIndex++) {
            final int aByte = buf[offset + byteIndex] & 0xff;
            sample += aByte << (8 * (bytesPerSample - byteIndex - 1));
        }
        return (short)sample;
    }

    private static byte[] intToByteLittleEndian(final int sample, final int bytesPerSample) {
        byte[] buf = new byte[bytesPerSample];
        for (int byteIndex = 0; byteIndex < bytesPerSample; byteIndex++) {
            buf[byteIndex] = (byte)((sample >>> (8 * byteIndex)) & 0xFF);
        }
        return buf;
    }

    private static byte[] intToByteBigEndian(final int sample, final int bytesPerSample) {
        byte[] buf = new byte[bytesPerSample];
        for (int byteIndex = 0; byteIndex < bytesPerSample; byteIndex++) {
            buf[byteIndex] = (byte)((sample >>> (8 * (bytesPerSample - byteIndex - 1))) & 0xFF);
        }
        return buf;
    }

    public static void main(final String[] args) throws IOException {
        final int sampleRate = 44100;
        final boolean bigEndian = true;
        final int sampleSizeInBits = 16;
        final int channels = 1;
        final boolean signed = true;
        final AudioFormat targetAudioFormat = new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);

        final byte[] a = new byte[sampleRate * 10];
        final byte[] b = new byte[sampleRate * 5];

        // create sine waves
        for (int i=0; i<a.length/2; i++) {
            System.arraycopy(intToByteBigEndian((int)(30000*Math.sin(i*0.5)),2), 0, a, i*2, 2);
        }
        for (int i=0; i<b.length/2; i++) {
            System.arraycopy(intToByteBigEndian((int)(30000*Math.sin(i*0.1)),2), 0, b, i*2, 2);
        }

        final File aFile = new File("a.wav");
        AudioSystem.write(new AudioInputStream(new ByteArrayInputStream(a), targetAudioFormat, a.length),
                AudioFileFormat.Type.WAVE, aFile);
        final File bFile = new File("b.wav");
        AudioSystem.write(new AudioInputStream(new ByteArrayInputStream(b), targetAudioFormat, b.length),
                AudioFileFormat.Type.WAVE, bFile);

        // mix a and b
        final byte[] mixed = mix(a, b, bigEndian);
        final File outFile = new File("out.wav");
        AudioSystem.write(new AudioInputStream(new ByteArrayInputStream(mixed), targetAudioFormat, mixed.length),
                AudioFileFormat.Type.WAVE, outFile);
    }
}

这篇关于Java:将4个单独的音频字节数组组合成单个wav音频文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆