【问题标题】:Distorted microphone audio when the loudspeaker is enabled (Xamarin.iOS)启用扬声器时麦克风音频失真 (Xamarin.iOS)
【发布时间】:2021-06-10 06:48:36
【问题描述】:

我正在维护一键通 VoIP 应用程序。 当 PTT 呼叫运行时,应用创建音频会话

m_AudioSession = AVAudioSession.SharedInstance();

NSError error;
if (!m_AudioSession.SetCategory(AVAudioSession.CategoryPlayAndRecord, AVAudioSessionCategoryOptions.DefaultToSpeaker | AVAudioSessionCategoryOptions.AllowBluetooth, out error))
{
    IOSErrorLogger.Log(DammLoggerLevel.Error, TAG, error, "Error setting the category");
}

if (!m_AudioSession.SetMode(AVAudioSession.ModeVoiceChat, out error))
{
    IOSErrorLogger.Log(DammLoggerLevel.Error, TAG, error, "Error setting the mode");
}

if (!m_AudioSession.OverrideOutputAudioPort(AVAudioSessionPortOverride.Speaker, out error))
{
    IOSErrorLogger.Log(DammLoggerLevel.Error, TAG, error, "Error redirecting the audio to the loudspeaker");
}

if (!m_AudioSession.SetPreferredIOBufferDuration(0.06, out error)) // 60 milli seconds
{
    IOSErrorLogger.Log(DammLoggerLevel.Error, TAG, error, "Error setting the preferred buffer duration");
}

if (!m_AudioSession.SetPreferredSampleRate(8000, out error)) // kHz
{
    IOSErrorLogger.Log(DammLoggerLevel.Error, TAG, error, "Error setting the preferred sample rate");
}

if (!m_AudioSession.SetActive(true, out error))
{
    IOSErrorLogger.Log(DammLoggerLevel.Error, TAG, error, "Error activating the audio session");
}

使用 OutputAudioQueue 播放接收到的音频,并使用语音处理 I/O 单元捕获麦克风音频(如 Apple 文档中所述:https://developer.apple.com/documentation/avfaudio/avaudiosession/mode/1616455-voicechat)。 Voice-Processing I/O Unit的初始化代码为:

            AudioStreamBasicDescription audioFormat = new AudioStreamBasicDescription()
            {
                SampleRate = SAMPLERATE_8000,
                Format = AudioFormatType.LinearPCM,
                FormatFlags = AudioFormatFlags.LinearPCMIsSignedInteger | AudioFormatFlags.LinearPCMIsPacked,
                FramesPerPacket = 1,
                ChannelsPerFrame = CHANNELS,
                BitsPerChannel = BITS_X_SAMPLE,
                BytesPerPacket = BYTES_X_SAMPLE,
                BytesPerFrame = BYTES_X_FRAME,
                Reserved = 0
            };

            AudioComponent audioComp = AudioComponent.FindComponent(AudioTypeOutput.VoiceProcessingIO);
            AudioUnit.AudioUnit voiceProcessing = new AudioUnit.AudioUnit(audioComp);

            AudioUnitStatus unitStatus = AudioUnitStatus.NoError;

            unitStatus = voiceProcessing.SetEnableIO(true, AudioUnitScopeType.Input, ELEM_Mic);
            if (unitStatus != AudioUnitStatus.NoError)
            {
                DammLogger.Log(DammLoggerLevel.Warn, TAG, "Audio Unit SetEnableIO(true, AudioUnitScopeType.Input, ELEM_Mic) returned: {0}", unitStatus);
            }

            unitStatus = voiceProcessing.SetEnableIO(true, AudioUnitScopeType.Output, ELEM_Speaker);
            if (unitStatus != AudioUnitStatus.NoError)
            {
                DammLogger.Log(DammLoggerLevel.Warn, TAG, "Audio Unit SetEnableIO(false, AudioUnitScopeType.Output, ELEM_Speaker) returned: {0}", unitStatus);
            }


            unitStatus = voiceProcessing.SetFormat(audioFormat, AudioUnitScopeType.Output, ELEM_Mic);
            if (unitStatus != AudioUnitStatus.NoError)
            {
                DammLogger.Log(DammLoggerLevel.Warn, TAG, "Audio Unit SetFormat (MIC-OUTPUT) returned: {0}", unitStatus);
            }

            unitStatus = voiceProcessing.SetFormat(audioFormat, AudioUnitScopeType.Input, ELEM_Speaker);
            if (unitStatus != AudioUnitStatus.NoError)
            {
                DammLogger.Log(DammLoggerLevel.Warn, TAG, "Audio Unit SetFormat (ELEM 0-INPUT) returned: {0}", unitStatus);
            }

            unitStatus = voiceProcessing.SetRenderCallback(AudioUnit_RenderCallback, AudioUnitScopeType.Input, ELEM_Speaker);
            if (unitStatus != AudioUnitStatus.NoError)
            {
                DammLogger.Log(DammLoggerLevel.Warn, TAG, "Audio Unit SetRenderCallback returned: {0}", unitStatus);
            }
            
            ...
            
            voiceProcessing.Initialize();
            voiceProcessing.Start();

                
                

而RenderCallback函数是:

private AudioUnitStatus AudioUnit_RenderCallback(AudioUnitRenderActionFlags actionFlags, AudioTimeStamp timeStamp, uint busNumber, uint numberFrames, AudioBuffers data)
{
    AudioUnit.AudioUnit voiceProcessing = m_VoiceProcessing;
    if (voiceProcessing != null)
    {
        // getting microphone input signal
        var status = voiceProcessing.Render(ref actionFlags, timeStamp, ELEM_Mic, numberFrames, data);
        if (status != AudioUnitStatus.OK)
        {
            return status;
        }

        if (data.Count > 0)
        {
            unsafe
            {
                short* samples = (short*)data[0].Data.ToPointer();

                for (uint idxSrcFrame = 0; idxSrcFrame < numberFrames; idxSrcFrame++)
                {
                   ... send the collected microphone audio (samples[idxSrcFrame])
                }
            }
        }
    }

    return AudioUnitStatus.NoError;
}

    
    

如果启用扬声器,我面临的问题是:m_AudioSession.OverrideOutputAudioPort(AVAudioSessionPortOverride.Speaker, out error) 然后麦克风音频损坏(有时无法理解语音)。 如果未启用扬声器(未设置 AVAudioSessionPortOverride.Speaker),则音频非常好。

我已经验证了 Render 函数返回的 AudioBuffer 中的 NumberChannels 为 1(单声道音频)。

非常感谢任何帮助解决问题的方法。谢谢

更新: AudioUnit_RenderCallback 方法每 32 毫秒调用一次。当扬声器被禁用时,接收到的帧数是准确的 256(采样率为 8000)。启用扬声器后,接收的帧数为 85。 在这两种情况下,GetAudioFormat 都会返回预期值:BitsPerChannel=16, BytesPerFrame=2, FramesPerPacket=1, ChannelsPerFrame=1, SampleRate=8000

更新: 我最终使用硬件的采样率并执行下采样自我。必须理解音频单元应该能够执行下采样https://developer.apple.com/library/archive/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/AudioUnitHostingFundamentals/AudioUnitHostingFundamentals.html#//apple_ref/doc/uid/TP40009492-CH3-SW11)),但我无法在启用扬声器时使其工作。

【问题讨论】:

    标签: audio xamarin.ios audiounit


    【解决方案1】:

    我希望您在实际设备上而不是模拟器上进行测试。

    在代码中,你有没有尝试过使用这个:

    sampleRate = AudioSession.CurrentHardwareSampleRate;
    

    最好从硬件检查采样率,而不是强制采样率。可能是在扬声器使用过程中,它会改变采样率,从而产生问题。

    我建议根据上述更改进行录制,看看音频是否有所改善,然后尝试其他标志。

    标准录制模式https://docs.microsoft.com/en-us/dotnet/api/audiotoolbox.audiostreambasicdescription?view=xamarin-ios-sdk-12#remarks

    【讨论】:

    • 我尝试使用 AudioSession.SampleRate(CurrentHardwareSampleRate 已被 Apple 弃用),但这无助于解决问题。在测试设备上,当扬声器打开并使用此值时,AudioSession 采样率为 48000,然后有时我完全错过了来自 AudioUnit 的回调。不管怎样,Apple 文档说:“音频单元提供硬件音频格式和您的应用程序音频格式之间的格式转换”(“使用特定音频单元”)。 Audio 格式应用于 AudioUnit 的麦克风输出范围。
    • 当我在 ios xamarin 表单中播放视频背景声音时,我也必须面对类似的问题,一旦在网络服务器中被压缩 stackoverflow.com/questions/68284286/…
    猜你喜欢
    • 1970-01-01
    • 2015-10-31
    • 1970-01-01
    • 2014-10-31
    • 2012-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-22
    相关资源
    最近更新 更多