Vlastní zvukové efekty

Tento článek popisuje, jak vytvořit prostředí Windows Runtime komponentu, která implementuje rozhraní IBasicAudioEffect k vytvoření vlastních efektů pro zvukové streamy. Vlastní efekty můžete použít s AudioGraphem, MediaCapture a MediaComposition.

Note

Rozhraní IBasicAudioEffect je rozhraní API systému prostředí Windows Runtime v oboru názvů Windows.Media.Effects. Tato rozhraní API fungují stejným způsobem v desktopových aplikacích WinUI 3 jako v UPW. Hlavní rozdíl spočívá v tom, jak nastavíte projekt a odkazujete na komponentu efektu.

Přidání komponenty prostředí Windows Runtime pro zvukový efekt

V Visual Studio přidejte do řešení WinUI 3 projekt komponenty prostředí Windows Runtime (C#):

  1. V Průzkumník řešení klikněte pravým tlačítkem na řešení a vyberte Přidat>nový Project.
  2. Vyberte šablonu projektu komponenty prostředí Windows Runtime.
  3. Pojmenujte projekt AudioEffectComponent.
  4. Do tohoto projektu komponenty přidejte odkaz na projekt vaší hlavní aplikace WinUI 3.
  5. Přejmenujte výchozí soubor třídy na ExampleAudioEffect.cs.

Implementace rozhraní IBasicAudioEffect

Váš zvukový efekt musí implementovat všechny metody a vlastnosti IBasicAudioEffect rozhraní.

using System.Collections.Generic;
using Windows.Media;
using Windows.Media.Effects;
using Windows.Media.MediaProperties;
using Windows.Foundation.Collections;

namespace AudioEffectComponent;

public sealed class ExampleAudioEffect : IBasicAudioEffect
{
    private AudioEncodingProperties _currentEncodingProperties;
    private IPropertySet _propertySet;
    private float _echoDelay = 0.5f; // delay in seconds
    private float _echoMix = 0.5f;   // mix level (0-1)

    public bool UseInputFrameForOutput => true;

    public IReadOnlyList<AudioEncodingProperties> SupportedEncodingProperties
    {
        get
        {
            var supportedProperties = new List<AudioEncodingProperties>();

            AudioEncodingProperties encodingProps1 =
                AudioEncodingProperties.CreatePcm(44100, 1, 32);
            encodingProps1.Subtype = MediaEncodingSubtypes.Float;
            supportedProperties.Add(encodingProps1);

            AudioEncodingProperties encodingProps2 =
                AudioEncodingProperties.CreatePcm(48000, 1, 32);
            encodingProps2.Subtype = MediaEncodingSubtypes.Float;
            supportedProperties.Add(encodingProps2);

            return supportedProperties;
        }
    }

    public void SetEncodingProperties(
        AudioEncodingProperties encodingProperties)
    {
        _currentEncodingProperties = encodingProperties;
    }

    public void SetProperties(IPropertySet configuration)
    {
        _propertySet = configuration;

        // Read custom properties if provided
        if (configuration != null &&
            configuration.TryGetValue("EchoDelay", out object delay))
        {
            _echoDelay = (float)delay;
        }

        if (configuration != null &&
            configuration.TryGetValue("EchoMix", out object mix))
        {
            _echoMix = (float)mix;
        }
    }

    public void ProcessFrame(ProcessAudioFrameContext context)
    {
        // Audio processing logic goes here.
        // See the echo effect example below.
    }

    public void Close(MediaEffectClosedReason reason)
    {
        // Clean up resources
    }

    public void DiscardQueuedFrames()
    {
        // Discard any cached frames
    }
}

Vytvoření efektu ozvěny

Následující příklad ukazuje implementaci ProcessFrame , která vytvoří efekt echo. Efekt ukládá předchozí zvuková data do vyrovnávací paměti a kombinuje je s aktuálními zvukovými daty po konfigurovatelném zpoždění.

Pro přístup k nezpracovanému zvukovému bufferu k tomu potřebujete rozhraní COM IMemoryBufferByteAccess. Do oboru názvů efektu přidejte následující definici:

using System.Runtime.InteropServices;

[ComImport]
[System.Runtime.InteropServices.Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
unsafe interface IMemoryBufferByteAccess
{
    void GetBuffer(out byte* buffer, out uint capacity);
}

Important

Tato technika umožňuje přístup k nativní nespravované vyrovnávací paměti zvuku. Do souboru .csproj vašeho projektu musíte přidat <AllowUnsafeBlocks>true</AllowUnsafeBlocks>, aby bylo možné povolit unsafe kód.

private float[] _echoBuffer;
private int _echoBufferPosition;
private float _echoDelay = 0.5f; // delay in seconds
private float _echoMix = 0.5f;   // mix level (0-1)
private Windows.Media.MediaProperties.AudioEncodingProperties _currentEncodingProperties;

public unsafe void ProcessFrame(ProcessAudioFrameContext context)
{
    AudioFrame inputFrame = context.InputFrame;

    using (AudioBuffer inputBuffer =
        inputFrame.LockBuffer(AudioBufferAccessMode.ReadWrite))
    using (IMemoryBufferReference inputReference =
        inputBuffer.CreateReference())
    {
        byte* dataInBytes;
        uint capacityInBytes;

        ((IMemoryBufferByteAccess)inputReference).GetBuffer(
            out dataInBytes, out capacityInBytes);

        float* dataInFloat = (float*)dataInBytes;
        int dataInFloatLength =
            (int)capacityInBytes / sizeof(float);

        // Initialize echo buffer on first call
        if (_echoBuffer == null)
        {
            int delaySamples = (int)(
                _currentEncodingProperties.SampleRate * _echoDelay);
            _echoBuffer = new float[delaySamples];
        }

        for (int i = 0; i < dataInFloatLength; i++)
        {
            float input = dataInFloat[i];
            float echo = _echoBuffer[_echoBufferPosition];

            // Mix the original signal with the echo
            dataInFloat[i] = input + (echo * _echoMix);

            // Store the current sample in the echo buffer
            _echoBuffer[_echoBufferPosition] = input;
            _echoBufferPosition =
                (_echoBufferPosition + 1) % _echoBuffer.Length;
        }
    }
}

Přidání efektu do zvukového grafu

Pokud chcete použít vlastní efekt s AudioGraphem, vytvořte instanci definice efektu a přidejte ho do uzlu:

using Windows.Media.Audio;
using Windows.Media.Effects;

var effectDefinition = new AudioEffectDefinition(
    typeof(AudioEffectComponent.ExampleAudioEffect).FullName);

audioFileInputNode.EffectDefinitions.Add(effectDefinition);

Předání konfigurace efektu

Hodnoty konfigurace můžete do efektu předat pomocí PropertySet:

var properties = new PropertySet();
properties["EchoDelay"] = 0.3f;
properties["EchoMix"] = 0.6f;

var effectDefinition = new AudioEffectDefinition(
    typeof(AudioEffectComponent.ExampleAudioEffect).FullName,
    properties);

V metodě efektu SetProperties (uvedené dříve) načtěte tyto hodnoty ze sady vlastností ke konfiguraci efektu za běhu.

Přidání efektu do MediaCapture

Vlastní zvukový efekt můžete také přidat do zvukového streamu MediaCapture :

var effectDefinition = new AudioEffectDefinition(
    typeof(AudioEffectComponent.ExampleAudioEffect).FullName);

await _mediaCapture.AddAudioEffectAsync(effectDefinition);