如何混合PCM音频源(Java)?

这就是我现在正在使用的内容:

for (int i = 0, numSamples = soundBytes.length / 2; i < numSamples; i += 2) { // Get the samples. int sample1 = ((soundBytes[i] & 0xFF) << 8) | (soundBytes[i + 1] & 0xFF); // Automatically converts to unsigned int 0...65535 int sample2 = ((outputBytes[i] & 0xFF) << 8) | (outputBytes[i + 1] & 0xFF); // Automatically converts to unsigned int 0...65535 // Normalize for simplicity. float normalizedSample1 = sample1 / 65535.0f; float normalizedSample2 = sample2 / 65535.0f; float normalizedMixedSample = 0.0f; // Apply the algorithm. if (normalizedSample1 < 0.5f && normalizedSample2 > 8) & 0xFF); soundBytes[i + 1] = (byte)(mixedSample & 0xFF); } 

据我所知,它是本页定义的算法的准确表示: http : //www.vttoth.com/CMS/index.php/technical-notes/68

然而,只是将声音与静音混合(全0)会产生非常明显听起来不正确的声音,也许最好将其描述为更高音和更响亮。

非常感谢帮助确定我是否正确地实现了算法,或者我是否只需要以不同的方式(不同的算法/方法)来实现它?

在链接的文章中,作者假设AB代表整个音频流。 更具体地, X表示流X中所有样品的最大abs值 – 其中XAB. 所以他的算法所做的是扫描两个流的整体以计算每个流的最大abs样本,然后对事物进行缩放,使得输出理论上达到峰值1.0。 您需要对数据进行多次传递才能实现此算法,如果您的数据是流式传输,则它将无法正常工作。

这是我认为算法如何工作的一个例子。 它假定样本已经转换为浮点到侧面步骤,导致转换代码出错。 我稍后会解释它有什么问题:

  double[] samplesA = ConvertToDoubles(samples1); double[] samplesB = ConvertToDoubles(samples2); double A = ComputeMax(samplesA); double B = ComputeMax(samplesB); // Z always equals 1 which is an un-useful bit of information. double Z = A+BA*B; // really need to find a value x such that xA+xB=1, which I think is: double x = 1 / (Math.sqrt(A) * Math.sqrt(B)); // Now mix and scale the samples double[] samples = MixAndScale(samplesA, samplesB, x); 

混合和缩放:

  double[] MixAndScale(double[] samplesA, double[] samplesB, double scalingFactor) { double[] result = new double[samplesA.length]; for (int i = 0; i < samplesA.length; i++) result[i] = scalingFactor * (samplesA[i] + samplesB[i]); } 

计算最大峰值:

 double ComputeMaxPeak(double[] samples) { double max = 0; for (int i = 0; i < samples.length; i++) { double x = Math.abs(samples[i]); if (x > max) max = x; } return max; } 

和转换。 注意我是如何使用short以便正确维护符号位:

 double[] ConvertToDouble(byte[] bytes) { double[] samples = new double[bytes.length/2]; for (int i = 0; i < samples.length; i++) { short tmp = ((short)bytes[i*2])<<8 + ((short)(bytes[i*2+1]); samples[i] = tmp / 32767.0; } return samples; }