简体   繁体   English

Android的AEC和​​Noise Suppressor在某些设备上不起作用

[英]Android's AEC and Noise Suppressor aren't working on some devices

In working on an Android Unity app with VoIP functionality, we've discovered that on some devices (The Galaxy S6 specifically) the microphone will pick up the sounds output by the speaker, leading to a lot of static, distortion, and echo. 在使用具有VoIP功能的Android Unity应用程序时,我们发现在某些设备(特别是Galaxy S6)上,麦克风会拾取扬声器输出的声音,从而导致大量的静态,失真和回声。 Because of this I've started working on a native solution to take advantage of some of the AEC and NS features of Android. 因此,我开始研究本机解决方案,以利用Android的某些AEC和NS功能。

I've written the following testbed, which works perfectly on some devices such as the Note 4. When the same app runs on the Galaxy S6, it's as if ARC and NS aren't being applied very well or at all. 我编写了以下测试平台,该测试平台在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"));
            }
        }
    }
}

I've tried a lot of things such as using VOICE_COMMUNICATION for the AudioRecord, using MIC and manually creating the NoiseSuppressor and AcousticEchoCanceller, disabling/enabling NuPlayer/AwesomePlayer, and more. 我尝试了很多事情,例如对音频记录使用VOICE_COMMUNICATION,使用MIC以及手动创建NoiseSuppressor和AcousticEchoCanceller,禁用/启用NuPlayer / AwesomePlayer等。 The devices show that AEC is supported and enabled, but it just doesn't seem to be happening. 这些设备表明已支持并启用了AEC,但似乎并没有发生。 The problem completely goes away when using headphones, or using the handset instead of the speaker phone (so the mic can't pick up speaker sounds). 使用耳机或使用听筒而不是扬声器电话时,问题完全消失了(因此麦克风无法拾取扬声器的声音)。 Am I overlooking something huge here, or is it possible that there's a bug to be reported? 我是在这里俯瞰巨大的东西,还是有可能报告一个错误?

I wouldn't be suspicious if AEC and NS weren't working perfectly on other devices. 如果AEC和NS在其他设备上运行不正常,我不会怀疑。

This is a known problem in Android. 这是Android中的已知问题。 Hardware AEC is not reliable and it is not implemented/supported by lots of hardware even if they report it otherwise. 硬件AEC不可靠,即使有其他硬件报告,它也不受许多硬件的实施/支持。 I would recommend to deploy also a software AEC alongside with the hardware AEC to make this feature more stable in your software. 我建议同时部署软件AEC和硬件AEC,以使此功能在软件中更稳定。 The only problem is that software AEC is not so easy to accomplish correctly, so be prepared for a lots of tests. 唯一的问题是软件AEC很难正确完成,因此要准备进行大量测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM