Java 分层音频文件时的峰值剪辑

Java 分层音频文件时的峰值剪辑,java,audio,signal-processing,javasound,Java,Audio,Signal Processing,Javasound,因此,作为我正在进行的一个项目的一部分,我正在尝试将多个音频片段相互层叠,以创建人群的声音,并将其写入一个新的.WAV文件 首先,我创建一个文件的byte[]表示形式(一个16位PCM.WAV文件),它似乎不会引起任何问题 public byte[] toByteArray(File file) { try { AudioInputStream in = AudioSystem.getAudioInputStream(file); byte[]

因此,作为我正在进行的一个项目的一部分,我正在尝试将多个音频片段相互层叠,以创建人群的声音,并将其写入一个新的.WAV文件

首先,我创建一个文件的byte[]表示形式(一个16位PCM.WAV文件),它似乎不会引起任何问题

public byte[] toByteArray(File file)
{
    try
    {
        AudioInputStream in = AudioSystem.getAudioInputStream(file);

        byte[] byteArray = new byte[(int) file.length()];//make sure the size is correct

        while (in.read(byteArray) != -1) ;//read in byte by byte until end of audio input stream reached

        return byteArray;//return the new byte array
    }
然后,我创建一个缓冲区(一个整数数组,以便在添加字节时防止字节溢出),并尝试在我的文件的字节数组版本中分层

 int[] buffer = new int[bufferLength];//buffer of appropriate length
        int offset = 0;//no offset for the very first file

        while(!convertedFiles.isEmpty())//until every sample has been added
        {
            byte[] curr = convertedFiles.pop();//get a sample from list

            if(curr.length+offset < bufferLength)
            {
                for (int i =0; i < curr.length; i++)
                {
                    buffer[i] += curr[i];
                }
            }

           offset = randomiseOffset();//next sample placed in a random location in the buffer
        }
我意识到我需要小心避免溢出,所以我尝试使用整数缓冲区而不是字节缓冲区

但我不明白的是,为什么当我引入补偿时,它才会中断

我没有发布实际使用AudioSystem对象创建新文件的代码,因为这两种方式似乎都没有效果

这是我第一次使用音频编程,非常感谢您的帮助

编辑:

Hendrik的回答解决了我的问题,但我只需要稍微更改建议的代码(一些类型转换问题):

private static short byteToShortLittleEndian(最终字节[]buf,最终整数偏移量)
{
int sample=(buf[offset]&0xff)+(buf[offset+1]&0xff)>8)和0xff);
返回buf;
}

您的
randomiseOffset()
方法看起来像什么?它是否考虑到每个音频样本有两个字节长?如果
randomiseOffset()
提供奇数偏移量,则最终会将一个样本的低字节与另一个样本的高字节混合在一起,这听起来像(通常很可怕)噪音。也许这就是你认为是剪辑的声音

 buffer[i+offset] += curr[i];
要做到这一点,您需要首先解码音频,即考虑采样长度(2字节)和通道计数(?),进行操作,然后再次将音频编码为字节流

假设只有一个通道,字节顺序为。然后将两个字节解码为如下示例值:

private static int byteToShortLittleEndian(final byte[] buf, final int offset) {
    int sample = (buf[offset] & 0xff) + ((buf[offset+1] & 0xff) << 8);
    return (short)sample;
}
private static byte[] shortToByteLittleEndian(final int[] samples, final int offset) {
    byte[] buf = new byte[2];
    int sample = samples[offset];
    buf[0] = sample & 0xFF;
    buf[1] = (sample >> 8) & 0xFF;
    return buf;
}
以下是在您的案例中如何使用这两种方法:

byte[] byteArray = ...;  // your array
// DECODE: convert to sample values
int[] samples = byteArray.length / 2;
for (int i=0; i<samples.length; i++) {
    samples[i] = byteToShortLittleEndian(byteArray, i*2);
}
// now do your manipulation on the samples array
[...]
// ENCODE: convert back to byte values
byte[] byteOut = new byte[byteArray.length];
for (int i=0; i<samples.length; i++) {
    byte[] b = shortToByteLittleEndian(samples, i);
    byteOut[2*i] = b[0];
    byteOut[2*i+1] = b[1];
}
// do something with byteOut ...
byte[]byteArray=…;//你的阵列
//解码:转换为样本值
int[]samples=byteArray.length/2;

对于(int i=0;我非常感谢你的帮助,Hendrik。你对randomiseOffset方法过于简单的怀疑是正确的,但这是一个足够简单的修复方法,不会返回奇数值。当谈论批量解码/编码以提高效率时,你是指简单地循环编码过程,还是更复杂lex?非常感谢您提供有关此主题的任何资源。感谢againI,这意味着要重写这些编码/解码方法,以便它们一次对多个样本进行解码/编码,而不仅仅是一个样本。顺便说一句:仅将随机化方法更改为仅返回偶数值不是正确的解决方案。
byte[] byteArray = ...;  // your array
// DECODE: convert to sample values
int[] samples = byteArray.length / 2;
for (int i=0; i<samples.length; i++) {
    samples[i] = byteToShortLittleEndian(byteArray, i*2);
}
// now do your manipulation on the samples array
[...]
// ENCODE: convert back to byte values
byte[] byteOut = new byte[byteArray.length];
for (int i=0; i<samples.length; i++) {
    byte[] b = shortToByteLittleEndian(samples, i);
    byteOut[2*i] = b[0];
    byteOut[2*i+1] = b[1];
}
// do something with byteOut ...