Mixing two16-bit encoded stereo PCM samples causing noise and distortion in the resulting audio(混合两个16位编码的立体声PCM样本,导致所产生的音频中的噪声和失真)
本文介绍了混合两个16位编码的立体声PCM样本,导致所产生的音频中的噪声和失真的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我从两个来源获得两个不同的音频样本。
麦克风声音:
audioRecord = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, 44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, (AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT)*5));
对于内音:
audioRecord = new AudioRecord.Builder() .setAudioPlaybackCaptureConfig(config) .setAudioFormat(new AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_PCM_16BIT) .setSampleRate(44100) .setChannelMask(AudioFormat.CHANNEL_IN_STEREO) .build()) .setBufferSizeInBytes((AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT)*5)) .build();
为了读取AudioRecord对象,我们创建了单独的Frame对象(称为Frame的自定义对象)-
private ByteBuffer pcmBuffer = ByteBuffer.allocateDirect(4096);
private Frame read() {
pcmBuffer.rewind();
int size = audioRecord.read(pcmBuffer, pcmBuffer.remaining());
if (size <= 0) {
return null;
}
return new Frame(pcmBuffer.array(),
pcmBuffer.arrayOffset(), size);
}
我们创建了两个单独的LL(链表),用于添加从Read函数获得的这些帧。
私有链接列表内部AudioQueue=new LinkedList<;>;(); Private LinkedList麦克风AudioQueue=new LinkedList<;>;();
public void onFrameReceived(Frame frame, boolean isInternalAudio) {
if (isInternalAudio) {
internalAudioQueue.add(frame);
} else {
microphoneAudioQueue.add(frame);
}
checkAndPoll();
}
每次在相应的L1中添加帧时,我们都会调用下面的check AndPoll()函数,并根据具体情况将帧传递给音频编码器。
public void checkAndPoll() {
Frame frame1 = internalAudioQueue.poll();
Frame frame2 = microphoneAudioQueue.poll();
if (frame1 == null && frame2 != null) {
audioEncoder.inputPCMData(frame2);
} else if (frame1 != null && frame2 == null) {
audioEncoder.inputPCMData(frame1);
} else if (frame1 != null && frame2 != null) {
Frame frame = new Frame(PCMUtil.mix(frame1.getBuffer(), frame2.getBuffer(), frame1.getSize(), frame2.getSize(), false), frame1.getOrientation(), frame1.getSize());
audioEncoder.inputPCMData(frame);
}
}
现在,在Hendrik的帮助下,我们将两个来源的音频样本以ByteBuffer的形式混合在一起link。
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;
}
我得到的混合样本既有失真又有噪音。不知道需要做些什么才能移除它。感谢您在这里提供的任何帮助。 提前感谢!
推荐答案
我认为如果要混合,则应取两者的(加权)平均值。
如果您的样本是128和128,结果仍然是128,而不是256,这可能超出范围。
所以只需将您的代码更改为:
// mix by adding
final int[] mix = new int[aSamples.length];
for (int i=0; i<mix.length; i++) {
// calculating the average
mix[i] = (aSamples[i] + bSamples[i]) >> 1;
}
这对您有效吗?
这篇关于混合两个16位编码的立体声PCM样本,导致所产生的音频中的噪声和失真的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
沃梦达教程
本文标题为:混合两个16位编码的立体声PCM样本,导致所产生的音频中的噪声和失真
基础教程推荐
猜你喜欢
- 如何让对象对 Cocos2D 中的触摸做出反应? 2022-01-01
- 如何在 UIImageView 中异步加载图像? 2022-01-01
- 如何在 iPhone 上显示来自 API 的 HTML 文本? 2022-01-01
- Android:对话框关闭而不调用关闭 2022-01-01
- 如何在没有IB的情况下将2个按钮添加到右侧的UINavigationbar? 2022-01-01
- android 应用程序已发布,但在 google play 中找不到 2022-01-01
- Kivy Buildozer 无法构建 apk,命令失败:./distribute.sh -m “kivy"d 2022-01-01
- 在 gmail 中为 ios 应用程序检索朋友的朋友 2022-01-01
- 当从同一个组件调用时,两个 IBAction 触发的顺序是什么? 2022-01-01
- UIWebView 委托方法 shouldStartLoadWithRequest:在 WKWebView 中等效? 2022-01-01