Как создать цепочку аудиоустройств, которая была AEC, передискретизацией и фильтром нижних частот

#c# #ios #xamarin.ios #resampling #audiounit

#c# #iOS #xamarin.ios #передискретизация #audiounit

Вопрос:

Я использую аудиоустройство с типом VoiceProcessingIO для приема голоса без эха. В RenderCallback я получаю образцы звука, а затем устанавливаю все значения буфера равными нулю, чтобы не было воспроизведения. Теперь мне нужно изменить частоту дискретизации с 48000 до 16000 после получения звука, а затем пропустить полученный звук через фильтр нижних частот.
Я не могу понять, как настроить несколько аудиоустройств для подключения друг к другу и передачи данных.
Я знаю, что я должен использовать kAudioUnitSubType_AUConverter для конвертера и kAudioUnitSubType_LowPassFilter для фильтра.
Я уже отчаялся найти какую-либо помощь.

PS Я нашел это сообщение в блоге, есть аналогичная проблема, но автор так и не получил ответа на свой вопрос. Но я не понимаю, почему автор использует два конвертера. Я также обеспокоен тем, что он использует удаленный тип, и я не понимаю, почему он соединяет эти шины и в таком порядке.


 public static class SoundSettings 
{
    public static readonly int SampleRate = 16000;
    public static readonly int Channels = 1;
    public static readonly int BytesPerSample = 2;
    public static readonly int FramesPerPacket = 1;
}
private void SetupAudioSession() 
{
    AudioSession.Initialize();
    AudioSession.Category = AudioSessionCategory.PlayAndRecord;
    AudioSession.Mode = AudioSessionMode.GameChat;
    AudioSession.PreferredHardwareIOBufferDuration = 0.08f;
}

private void PrepareAudioUnit() 
{
    _srcFormat = new AudioStreamBasicDescription 
    {
        Format = AudioFormatType.LinearPCM,
        FormatFlags = AudioFormatFlags.LinearPCMIsSignedInteger | 
        AudioFormatFlags.LinearPCMIsPacked,
        SampleRate = AudioSession.CurrentHardwareSampleRate,
        FramesPerPacket = SoundSettings.FramesPerPacket,
        BytesPerFrame = SoundSettings.BytesPerSample * SoundSettings.Channels,
        BytesPerPacket = SoundSettings.FramesPerPacket *
            SoundSettings.BytesPerSample * 
            SoundSettings.Channels,
        BitsPerChannel = SoundSettings.BytesPerSample * 8,
        ChannelsPerFrame = SoundSettings.Channels,
        Reserved = 0
    };

    var audioComponent = AudioComponent.FindComponent(AudioTypeOutput.VoiceProcessingIO);

    _audioUnit = new AudioUnit.AudioUnit(audioComponent);

    _audioUnit.SetEnableIO(true, AudioUnitScopeType.Input, 1);
    _audioUnit.SetEnableIO(true, AudioUnitScopeType.Output, 0);

    _audioUnit.SetFormat(_srcFormat, AudioUnitScopeType.Input, 0);
    _audioUnit.SetFormat(_srcFormat, AudioUnitScopeType.Output, 1);

    _audioUnit.SetRenderCallback(this.RenderCallback, AudioUnitScopeType.Input, 0);
}

private AudioUnitStatus RenderCallback(
    AudioUnitRenderActionFlags actionFlags,
    AudioTimeStamp timeStamp, 
    uint busNumber,
    uint numberFrames, 
    AudioBuffers data)
{
    var status = _audioUnit.Render(ref actionFlags, timeStamp, 1, numberFrames, data);

    if (status != AudioUnitStatus.OK) 
    {
        return status;
    }

    var msgArray = new byte[dataByteSize];
    Marshal.Copy(data[0].Data, msgArray, 0, dataByteSize);

    var msg = _msgFactory.CreateAudioMsg(msgArray, msgArray.Length, (  _lastIndex));
    this.OnMsgReady(msg);

    // Disable playback IO
    var array = new byte[dataByteSize];
    Marshal.Copy(array, 0, data[0].Data, dataByteSize);

    return AudioUnitStatus.NoError;
}
  

Комментарии:

1. Похоже, у вас есть звуковая система, которая может обрабатывать голос или музыку. Во-первых, вам не нужно изменять частоту дискретизации, вы всегда можете добавить фильтрацию, чтобы получить более низкие частоты. Например, чтобы перейти от 48000 к 16000, вы можете просто взять каждую третью выборку. Данные, которые вы хотите получить от одного процесса и ввести во ввод второго процесса, и сделать это в режиме реального времени. Самый простой способ — создать FIFO и использовать таймеры.

2. Я реализовал «наивный» алгоритм для уменьшения частоты дискретизации. Я взял среднее значение из трех выборок. В принципе, я могу использовать любую интерполяцию, поскольку я не уверен, что входная частота дискретизации всегда будет равна 48000. Но, поскольку разница между 48000 и 16000 более чем в два раза больше, мне нужен фильтр, чтобы избежать искажения звука. В любом случае это то, что говорится в этой статье , в разделе «Передискретизация». В конце концов, использование возможностей системы предпочтительнее самодельных алгоритмов.

3. Вот список различных форматов: learn.microsoft.com/en-us/dotnet/api /…

Ответ №1:

Вот пример функции, которую вы можете использовать для подключения двух аудиоустройств (обратите внимание, что источник и назначение должны иметь одинаковый формат потока, прежде чем вы сможете их успешно подключить) :

 OSStatus connectAudioUnits(AudioUnit source, AudioUnit destination, AudioUnitElement sourceOutput, AudioUnitElement destinationInput) {
        
        AudioUnitConnection connection;
        connection.sourceAudioUnit    = source;
        connection.sourceOutputNumber = sourceOutput;
        connection.destInputNumber    = destinationInput;
        
        return AudioUnitSetProperty (
                                     destination,
                                     kAudioUnitProperty_MakeConnection,
                                     kAudioUnitScope_Input,
                                     destinationInput,
                                     amp;connection,
                                     sizeof(connection)
                                     );
    }