音频样本混合或改变音量会导致饱和和白噪声



我有一个多声道输入(我在mac上使用Soundflower 64ch(,我正在尝试将64个声道中的4个声道混合为立体声输出。

我正在做的是,读取1024帧的块,每帧有64个通道,然后将字节缓冲区转换为Short数组(值在-32768<->32767之间,因为样本是16位(。

通过这种方式,我添加了例如channel1[sample] + channel2[sample],并获得了两个通道的混合。但这里有一个问题,总和可能会溢出Short(16位(范围,从而在声音中引入饱和。所以我正在做的是(channel1[sample] + channel2[sample]) / 2,但当我除以2时,我会听到很多白色的声音。

此外,如果我试图通过channel1[sample] * 0.5来减少通道的体积,就会有很多饱和。为什么会发生这种情况?

这是我的完整代码,请注意,我正在将字节转换为短字节以更好地处理,然后我将转换回字节以将混合写入立体声输出:

public static void main(String[] args) throws LineUnavailableException {
int inputChannels = 64;
AudioFormat inputFormat = new AudioFormat(48000, 16, inputChannels, true, false);
AudioFormat outputFormat = new AudioFormat(48000, 16, 2, true, false);
TargetDataLine mic = AudioSystem.getTargetDataLine(inputFormat);
SourceDataLine speaker = AudioSystem.getSourceDataLine(outputFormat);
mic.open(inputFormat);
speaker.open(outputFormat);
mic.start();
speaker.start();

AudioInputStream audioInputStream = new AudioInputStream(mic);
int bytesPerFrame = audioInputStream.getFormat().getFrameSize();
// Set an arbitrary buffer size of 1024 frames.
int CHUNK = 1024 ;
int numBytes = CHUNK * bytesPerFrame;
byte[] audioBytes = new byte[numBytes];
try {
byte[][] frames = new byte[CHUNK][bytesPerFrame];
int i = 0, j = 0
;
while (true) {
// read to audioBytes.
audioInputStream.read(audioBytes);
// split audioBytes in _CHUNK_ frames (1024 frames)
for(j=0; j<CHUNK; j++) {
frames[j] = Arrays.copyOfRange(audioBytes, j * bytesPerFrame, j * bytesPerFrame + bytesPerFrame);
}
// convert bytearray to shortarray
short[][] shortFrames = new short[CHUNK][inputChannels];
for(i=0; i < frames.length; i++) {
ByteBuffer.wrap(frames[i]).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get(shortFrames[i]);
}
short[] leftOutput = new short[CHUNK*2];
short[] rightOutput = new short[CHUNK*2];
for (i=0; i<CHUNK; i++) {
short channel1 = shortFrames[i][0];
short channel2 = shortFrames[i][1];
short channel3 = shortFrames[i][2];
short channel4 = shortFrames[i][3];
leftOutput[i] = (short)(channel4);
rightOutput[i] = (short)(channel4);;
}

//convert shortarray in byte buffer
ByteBuffer byteBuf = ByteBuffer.allocate(CHUNK * 2 * 2); // 2 bytes * 2 output channels
for (i=0; i<CHUNK; i++) {
byteBuf.putShort(leftOutput[i]);
byteBuf.putShort(rightOutput[i]);
}
speaker.write(byteBuf.array(),0,byteBuf.array().length);
}
} catch (Exception ex) {
// Handle the error...
System.out.println("exception");
System.out.println(ex.toString());
}
}

IDK,如果问题是如何将字节转换为short和back,但由于您在评论中询问了这一点,我将发布它。假设buffer具有16位编码的连续小端字节。只需反转big-endian的字节索引即可。

pcmShort = ( buffer[i] & 0xff ) | ( buffer[i+1] << 8 );

我使用的pcm到字节的转换如下(对于小端序,反转大端序的索引(:

outBuffer[i] = (byte)pcmShort[0];
outBuffer[i+1] = (byte)((int)pcmShort[0] >> 8); 

也许您可以对相同的数据并行使用这两种方法(您尝试使用ByteBuffer和getShort,以及上面的方法(,并检查结果数组是否包含相同的值?

我想做的另一件事就是让一条赛道发挥作用。如果这听起来不错,那就检查一下混音。有点不可能信号如此之热以至于它们正在超越。所以可能发生了其他事情。

我应该自己尝试一下,我不确定什么时候能做到。这可能会比我一直在做的有所改进。