配置和查询音频设备模块

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

获取 AudioDeviceModulesManager 类的实例

本文中显示的所有音频设备模块操作首先获取 AudioDeviceModulesManager 的实例。 为此,首先调用 MediaDevice类的静态 GetDefaultAudioRenderId 方法。 这会返回默认音频呈现设备的 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 属性来获取 AudioDeviceModule 的实例,然后检查标识该模块的 ClassId 属性。 与事件关联的数据作为存储在事件参数的 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];
            }
        }
    }
}