在开发具有 VoIP 功能的 Android Unity 应用程序时,我们发现在某些设备(特别是 Galaxy S6)上,麦克风会拾取扬声器输出的声音,从而导致大量的静电、失真和回声。正因为如此,我开始着手开发一个原生解决方案,以利用 Android 的一些 AEC 和 NS 功能。
我编写了以下测试平台,它可以在某些设备上完美运行,例如 Note 4。当相同的应用程序在 Galaxy S6 上运行时,就好像 ARC 和 NS 没有很好地应用或根本没有应用。
public class MainActivity extends Activity
{
private AudioRecord mAudioRecorder = null;
private AudioTrack mStreamingPlayer = null;
private Thread playbackThread = null;
private boolean useVoip = true;
int bufferSize;
int chunkSize;
private AutomaticGainControl agc;
private NoiseSuppressor suppressor;
private AcousticEchoCanceler aec;
private boolean manuallyAttachEffects = false;
private void StartRecordingAudio()
{
int sampleRate = 44100;
int channelConfig = AudioFormat.CHANNEL_IN_MONO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
chunkSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);
bufferSize = chunkSize * 10;
int audioSource = MediaRecorder.AudioSource.MIC;
if ( useVoip && !manuallyAttachEffects )
{
audioSource = MediaRecorder.AudioSource.VOICE_COMMUNICATION;
}
mAudioRecorder = new AudioRecord( audioSource,
sampleRate,
channelConfig,
audioFormat,
bufferSize );
if ( useVoip && manuallyAttachEffects )
{
SetupVoipEffects( mAudioRecorder.getAudioSessionId() );
}
mAudioRecorder.startRecording();
mStreamingPlayer = new AudioTrack( AudioManager.STREAM_MUSIC,
sampleRate,
AudioFormat.CHANNEL_OUT_MONO,
audioFormat,
bufferSize,
AudioTrack.MODE_STREAM );
mStreamingPlayer.play();
playbackThread = new Thread(new Runnable()
{
@Override
public void run() {
readAudioData();
}
});
playbackThread.start();
}
private void readAudioData()
{
Thread thisThread = Thread.currentThread();
byte[] buffer = new byte[chunkSize];
while ( !thisThread.isInterrupted() )
{
if ( mAudioRecorder == null )
break;
int bytesRead = mAudioRecorder.read(buffer, 0, chunkSize);
if ( bytesRead > 0 )
{
if ( mStreamingPlayer == null )
break;
mStreamingPlayer.write(buffer, 0, chunkSize);
}
}
}
private void SetupVoipEffects( int sessionId )
{
if(AutomaticGainControl.isAvailable())
{
agc = AutomaticGainControl.create( sessionId );
Log.d(LOG_TAG, "AGC is " + (agc.getEnabled()?"enabled":"disabled"));
if ( !agc.getEnabled() )
{
agc.setEnabled(true);
Log.d(LOG_TAG, "AGC is " + (agc.getEnabled()?"enabled":"disabled" +" after trying to enable"));
}
}
if(NoiseSuppressor.isAvailable())
{
suppressor = NoiseSuppressor.create( sessionId );
Log.d(LOG_TAG, "NS is " + (suppressor.getEnabled()?"enabled":"disabled"));
if ( !suppressor.getEnabled() )
{
suppressor.setEnabled(true);
Log.d(LOG_TAG, "NS is " + (suppressor.getEnabled()?"enabled":"disabled" +" after trying to disable"));
}
}
if(AcousticEchoCanceler.isAvailable())
{
aec = AcousticEchoCanceler.create( sessionId );
Log.d(LOG_TAG, "AEC is " + (aec.getEnabled()?"enabled":"disabled"));
if ( !aec.getEnabled() )
{
aec.setEnabled(true);
Log.d(LOG_TAG, "AEC is " + (aec.getEnabled()?"enabled":"disabled" +" after trying to disable"));
}
}
}
}
我已经尝试了很多方法,例如将 VOICE_COMMUNICATION 用于 AudioRecord、使用 MIC 并手动创建 NoiseSuppressor 和 AcousticEchoCanceller、禁用/启用 NuPlayer/AwesomePlayer 等等。这些设备显示支持并启用了 AEC,但它似乎并没有发生。使用耳机或使用听筒代替免提电话时,问题完全消失(因此麦克风无法拾取扬声器声音)。我是否在这里忽略了一些巨大的东西,或者是否有可能要报告一个错误?
如果 AEC 和 NS 在其他设备上不能完美运行,我不会怀疑。