[*] 混音算法
This commit is contained in:
@@ -406,6 +406,7 @@ public final class MediaManager {
|
||||
// .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
// .build();
|
||||
final JavaAudioDeviceModule javaAudioDeviceModule = JavaAudioDeviceModule.builder(this.context)
|
||||
// 设置声音配置:采样播放(不会影响传输)
|
||||
// .setSampleRate(48000)
|
||||
// .setSampleRate(mediaAudioProperties.getSampleRate())
|
||||
// .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT)
|
||||
|
||||
@@ -105,6 +105,7 @@ public class MixerProcesser extends Thread implements JavaAudioDeviceModule.Samp
|
||||
this.audioSource = MediaRecorder.AudioSource.MIC;
|
||||
this.channelCount = channelCount;
|
||||
this.channelConfig = AudioFormat.CHANNEL_IN_MONO;
|
||||
// this.channelConfig = AudioFormat.CHANNEL_IN_STEREO;
|
||||
this.audioRecord = new AudioRecord.Builder()
|
||||
.setAudioFormat(
|
||||
new AudioFormat.Builder()
|
||||
@@ -147,8 +148,10 @@ public class MixerProcesser extends Thread implements JavaAudioDeviceModule.Samp
|
||||
byte[] localData = null;
|
||||
byte[] remoteData = null;
|
||||
byte[] recordData = null;
|
||||
int mixLocal, mixRemote, mixValue;
|
||||
JavaAudioDeviceModule.AudioSamples local = null;
|
||||
JavaAudioDeviceModule.AudioSamples remote = null;
|
||||
final ByteBuffer mixBuffer = ByteBuffer.allocateDirect(2);
|
||||
// 采集数据大小:采样频率 / (一千毫秒 / 回调频率毫秒) * 通道数量 * 采样数据大小
|
||||
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(this.sampleRate / (1000 / 10) * this.channelCount * 2);
|
||||
while(!this.close) {
|
||||
@@ -185,11 +188,36 @@ public class MixerProcesser extends Thread implements JavaAudioDeviceModule.Samp
|
||||
mixData = new byte[mixDataLength];
|
||||
}
|
||||
// 如果多路远程声音变小:(remote * 远程路数 + local) / (远程路数 + 1)
|
||||
for (int index = 0; index < mixDataLength; index++) {
|
||||
for (int index = 0; index < mixDataLength; index += 2) {
|
||||
// -0x8000 ~ 0x7FFF;
|
||||
mixData[index] = (byte) (((localData[index] + remoteData[index]) & 0x7FFF) / 2);
|
||||
// mixData[index] = (byte) (localData[index] + remoteData[index]);
|
||||
// mixData[index] = (byte) (((localData[index] + remoteData[index]) & 0x7FFF) / 2);
|
||||
// mixData[index] = (byte) (((localData[index] + remoteData[index]) & 0xFFFF) / 2);
|
||||
// mixData[index] = (byte) (((localData[index] + remoteData[index] * remoteCount) & 0xFFFF) / (1 + remoteCount));
|
||||
mixBuffer.clear();
|
||||
mixBuffer.put(localData[index + 1]);
|
||||
mixBuffer.put(localData[index]);
|
||||
mixBuffer.flip();
|
||||
mixLocal = mixBuffer.getShort();
|
||||
mixBuffer.flip();
|
||||
mixBuffer.put(remoteData[index + 1]);
|
||||
mixBuffer.put(remoteData[index]);
|
||||
mixBuffer.flip();
|
||||
mixRemote = mixBuffer.getShort();
|
||||
mixValue = mixLocal + mixRemote;
|
||||
if(mixValue > Short.MAX_VALUE) {
|
||||
mixValue = Short.MAX_VALUE;
|
||||
}
|
||||
if(mixValue < Short.MIN_VALUE) {
|
||||
mixValue = Short.MIN_VALUE;
|
||||
}
|
||||
// 不能使用下面这个
|
||||
// mixValue = mixValue & 0xFFFF;
|
||||
mixBuffer.flip();
|
||||
mixBuffer.putShort((short) mixValue);
|
||||
mixBuffer.flip();
|
||||
mixData[index + 1] = mixBuffer.get();
|
||||
mixData[index] = mixBuffer.get();
|
||||
}
|
||||
pts += mixData.length * (1_000_000 / local.getSampleRate() / 2);
|
||||
this.recordClient.onPcm(pts, mixData);
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.acgist.taoyao;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@@ -13,16 +14,40 @@ public class AudioMixerTest {
|
||||
public void testMixer() throws IOException {
|
||||
// ffmpeg -i audio.mp3 -f s32le audio.pcm
|
||||
// ffplay -i audio.pcm -f s32le -ar 48000 -ac 1
|
||||
final File fileA = new File("D:\\tmp\\mixer\\1.pcm");
|
||||
final File fileB = new File("D:\\tmp\\mixer\\2.pcm");
|
||||
final File fileA = new File("C:\\Users\\acgis\\桌面\\1.pcm");
|
||||
final File fileB = new File("C:\\Users\\acgis\\桌面\\2.pcm");
|
||||
final byte[] bytesA = Files.readAllBytes(fileA.toPath());
|
||||
final byte[] bytesB = Files.readAllBytes(fileB.toPath());
|
||||
final int length = Math.min(bytesA.length, bytesB.length);
|
||||
final byte[] target = new byte[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
target[i] = (byte) (((bytesA[i] + bytesB[i]) & 0xFFFF) / 2);
|
||||
final ByteBuffer buffer = ByteBuffer.allocateDirect(2);
|
||||
for (int i = 0; i < length; i += 2) {
|
||||
buffer.clear();
|
||||
buffer.put(bytesA[i + 1]);
|
||||
buffer.put(bytesA[i]);
|
||||
buffer.flip();
|
||||
int mixA = buffer.getShort();
|
||||
buffer.flip();
|
||||
buffer.put(bytesB[i + 1]);
|
||||
buffer.put(bytesB[i]);
|
||||
buffer.flip();
|
||||
int mixB = buffer.getShort();
|
||||
int mix = mixA + mixB;
|
||||
if(mix > Short.MAX_VALUE) {
|
||||
mix = Short.MAX_VALUE;
|
||||
}
|
||||
if(mix < Short.MIN_VALUE) {
|
||||
mix = Short.MIN_VALUE;
|
||||
}
|
||||
// 不能使用下面这个
|
||||
// mix = mix & 0xFFFF;
|
||||
buffer.flip();
|
||||
buffer.putShort((short) mix);
|
||||
buffer.flip();
|
||||
target[i + 1] = buffer.get();
|
||||
target[i] = buffer.get();
|
||||
}
|
||||
Files.write(Paths.get("D:\\tmp\\mixer\\3.pcm"), target);
|
||||
Files.write(Paths.get("C:\\Users\\acgis\\桌面\\3.pcm"), target);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user