配置和查询音频设备模块

本文介绍如何从 UWP 应用发送命令和接收来自音频设备模块的更改通知。 音频设备模块可以是硬件效果处理单元或音频驱动程序定义的任何其他音频配置模块。 此功能旨在使模块提供程序能够创建 UWP 应用,这些应用允许用户控制和获取 DSP 中运行的音频处理模块的状态信息。 若要使用本文中所示的音频设备模块 Api,必须在应用包清单中指定受限的 audioDeviceConfiguration 功能。

获取 AudioDeviceModulesManager 类的实例

本文中所示的所有音频设备模块操作都首先获取 AudioDeviceModulesManager的实例。 首先调用MediaDevice类的静态GetDefaultAudioRenderId方法来执行此操作。 这会返回默认音频呈现设备的 ID,该 ID 随后会传递到 AudioDeviceModulesManager 的构造函数中,以创建与音频设备关联的类的实例。

C#

var endpointId = MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default);
var audioModuleManager = new AudioDeviceModulesManager(endpointId);

查询安装的音频设备模块

通过调用AudioDeviceModulesManager类的FindAll ,查询所有已安装的音频设备模块。 通过调用 FindAllById 并传入请求的模块的 ID,查询特定的一组音频设备模块。 下面的示例定义一组模块的 ID,调用 FindAllById 来检索 AudioDeviceModule 对象的列表,然后将每个模块的详细信息输出到调试输出。

C#

public const string Contoso_AudioDeviceModuleId = "F72E09C3-FEBA-4C50-93BE-2CA56123AF09";

C#

var endpointId = MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default);
var audioModuleManager = new AudioDeviceModulesManager(endpointId);
var modules = audioModuleManager.FindAllById(Contoso_AudioDeviceModuleId);

foreach (var module in modules)
{
    var classId = module.ClassId;
    var name = module.DisplayName;
    var minorVersion = module.MinorVersion;
    var majorVersion = module.MajorVersion;
    var instanceId = module.InstanceId;

        Debug.WriteLine($"{classId} : {name} : {minorVersion} : {majorVersion} : {instanceId}");
}

向音频设备模块发送命令并接收结果数据

通过对AudioDeviceModule对象调用SendCommandAsync ,将命令发送到音频设备模块。 SendCommandAsync方法采用字节数组作为参数。 通常,此字节数组包含一个命令标识符,后跟与命令相关联的数据,但命令格式和值完全由供应商定义,并被系统视为透明。

SendCommandAsync方法返回一个异步操作,该操作在完成后返回表示命令结果的ModuleCommandResult对象。 Status属性包含一个枚举值,该值指示系统是否可以执行该命令。 这并不一定表示音频设备模块已成功执行命令。 Result属性包含一个字节数组,该数组由音频设备模块返回,用于指示命令的状态。 通常,这将是一个值,指示成功或失败后跟命令的数据结果。 与模块命令一样,模块响应格式和值都是由供应商定义的。

下面的示例调用 FindAllAsync 来检索一组音频设备模块。 DataWriter用于创建包含示例命令和数据的字节数组。 调用SendCommandAsync以发送命令缓冲区,并在异步操作完成后返回ModuleCommandResult 。 如果命令执行成功,则首先使用 DataReader 来读取从模块返回的整数状态值。 如果此值是供应商定义的成功值,则将读取并使用应用程序(如)更新 UI。

C#

public const byte Contoso_ReverbLevel_Command = 30; 
public const byte Contoso_SendCommand_Success = 99;

C#

var endpointId = MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default);
var audioModuleManager = new AudioDeviceModulesManager(endpointId);
var modules = audioModuleManager.FindAllById(Contoso_AudioDeviceModuleId);

foreach (var module in modules)
{
    var writer = new Windows.Storage.Streams.DataWriter();
    writer.WriteByte(Contoso_ReverbLevel_Command);
    writer.WriteByte(100);

    var command = writer.DetachBuffer();

    var result = await module.SendCommandAsync(command);

    if (result.Status == SendCommandStatus.Success)
    {
        using (DataReader reader = DataReader.FromBuffer(result.Result))
        {
            int bufferStatus = reader.ReadInt32();
            if (bufferStatus == Contoso_SendCommand_Success)
            {
                byte[] data = { 0, 0 };
                reader.ReadBytes(data);
                // Do something with returned data, such as update UI
            }
        }
    }
}

修改音频设备模块时接收通知

通过注册 ModuleNotificationReceived 事件来更新音频设备模块后,应用可以接收通知。

C#

var endpointId = MediaDevice.GetDefaultAudioRenderId(AudioDeviceRole.Default);
var audioModuleManager = new AudioDeviceModulesManager(endpointId);

audioModuleManager.ModuleNotificationReceived += AudioModuleManager_ModuleNotificationReceived;

当修改与当前音频设备关联的任何音频设备模块时,将引发ModuleNotificationReceived 。 若要确定事件是否与特定模块关联,请通过访问传递到事件处理程序中的AudioDeviceModuleNoticiationEventArgsmodule属性,然后检查标识该模块的ClassId属性来获取AudioDeviceModule的实例。 与事件关联的数据将作为存储在事件参数的 NotificationData 属性中的字节数组传递。 与命令和结果一样,返回的字节数组的格式为供应商定义的格式。 在以下示例中,如果通知数据的第一个字节包含模块的 "回响级别" 设置的示例值,则将读取并使用该数据来更新 UI。

C#

public const byte Contoso_ReverbLevel_Data = 25;

C#

private void AudioModuleManager_ModuleNotificationReceived(AudioDeviceModulesManager sender, AudioDeviceModuleNotificationEventArgs args)
{
    if (args.Module.ClassId == Contoso_AudioDeviceModuleId)
    {
        // Get the coefficient data from the reverb module.
        using (DataReader reader = DataReader.FromBuffer(args.NotificationData))
        {
            // read notification data.
            byte item = reader.ReadByte();

            // if reverb coefficient data are changed.
            if (item == Contoso_ReverbLevel_Data)
            {
                // read the new value
                byte[] data = { 0 };
                reader.ReadBytes(data);
                ReverbLevelSlider.Value = data[0];
            }
        }
    }
}