插孔说明属性

在 Windows Vista 及更高版本中,KSPROPERTY_JACK_DESCRIPTION 属性用于描述音频适配器上的音频插孔或其他物理连接器。 属性值描述插孔的颜色、插孔的物理位置、连接器类型和其他插孔功能。 此信息的目的是帮助用户查找用于插入音频终结点设备(如麦克风、耳机或扬声器)的正确插孔。 有关详细信息,请参阅音频终结点设备

如果音频适配器上的 KS 筛选器支持 KSPROPERTY_JACK_DESCRIPTION 属性,则 Windows 多媒体控制面板 Mmsys.cpl 会显示筛选器上桥接引脚的插孔信息。 桥接引脚表示与音频终结点设备的连接(通常为插孔)。 虽然属性值包含有关引脚(或与引脚关联的一个或多个插孔)的信息,但该属性是筛选器的属性,而非引脚的属性。 有关桥接引脚的详细信息,请参阅音频筛选器图。 有关筛选器属性和引脚属性的详细信息,请参阅筛选器、引脚和节点属性

音频应用程序可以通过在 DeviceTopology API 中调用 IKsJackDescription::GetJackDescription 方法来获取音频终结点设备的 KSPROPERTY_JACK_DESCRIPTION 属性值。 例如,应用程序可以使用插孔信息来帮助用户区分插入绿色 XLR 插孔的麦克风与插入橙色 XLR 插孔的麦克风。 有关 DeviceTopology API 的详细信息,请参阅设备拓扑

Microsoft HD 音频类驱动程序根据它从 HD 音频编解码器中的引脚配置寄存器读取的数据自动构造 KSPROPERTY_JACK_DESCRIPTION 属性值。 但是,任何基于 KS 的音频驱动程序都可以在其筛选器自动化表中实现对此属性的支持。 有关 HD 音频类驱动程序的详细信息,请参阅 HD 音频和 UAA。 有关引脚配置寄存器的详细信息,请参阅高清音频设备引脚配置指南白皮书。

音频终结点设备可以通过一个或多个插孔连接到桥接引脚。 例如,一组(双声道)立体声扬声器需要一个插孔,但一组 5.1 环绕声扬声器需要三个插孔(假设每个插孔处理六个声道中的两个)。

每个插孔的说明包含在 KSJACK_DESCRIPTION 结构中。 例如,具有一个插孔的音频终结点设备的 KSPROPERTY_JACK_DESCRIPTION 属性值包含一个 KSJACK_DESCRIPTION 结构,但具有三个插孔的终结点设备的属性值包含三个 KSJACK_DESCRIPTION 结构。 在任一情况下,属性值中的一个或多个 KSJACK_DESCRIPTION 结构前面都是指定该属性值大小的 KSMULTIPLE_ITEM 结构。 有关详细信息,请参阅 KSPROPERTY_JACK_DESCRIPTION

插孔信息对于帮助用户区分连接到多声道扬声器配置的插孔特别有用。 下面的代码示例演示了音频驱动程序用来描述一组 5.1 环绕扬声器的三个插孔的 KSJACK_DESCRIPTION 结构数组:

KSJACK_DESCRIPTION ar_5dot1_Jacks[] =
{
    // Jack 1
    {
        (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT), // ChannelMapping (L,R)
        RGB(0,255,0),       // Color (green)
        eConnType3Point5mm, // ConnectionType
        eGeoLocRear,        // GeoLocation
        eGenLocPrimaryBox,  // GenLocation
        ePortConnJack,      // PortConnection
        TRUE                // IsConnected
    },
    // Jack 2
    {
        (SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY), // (C,Sub)
        RGB(0,0,255),       // (red)
        eConnType3Point5mm,
        eGeoLocRear,
        eGenLocPrimaryBox,
        ePortConnJack,
        TRUE
    },
    // Jack 3
    {
        (SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT),  // (SL,SR)
        RGB(0,255,255),     // (yellow)
        eConnType3Point5mm,
        eGeoLocRear,
        eGenLocPrimaryBox,
        ePortConnJack,
        TRUE
    }
};

如果音频硬件可以检测设备是否已插入,驱动程序会动态更新此成员的值,以指示设备当前是已插入 (TRUE) 还是已拔出 (FALSE)

在前面的代码示例中,每个数组元素中的 IsConnected 成员设置为 TRUE,以指示终结点设备已插入插孔。 但是,如果硬件缺少插孔状态检测,IsConnected 必须始终设置为 TRUE,无论是否有设备插入插孔中。 为消除 TRUE 返回值的这种双重含义产生的歧义,客户端应用程序可以调用 IKsJackDescription2::GetJackDescription2 来读取 KSJACK_DESCRIPTION2 结构的 JackCapabilities 标志。 如果此标志设置了 JACKDESC2_PRESENCE_DETECT_CAPABILITY 位,则表示终结点实际上支持插孔状态检测。 在这种情况下,IsConnected 成员的值可以解释为对插孔插入状态的准确反映。

上述结构中显示的 RGB 宏在 Windows SDK 的头文件 Wingdi.h 中定义。

此外,可以使用一组插孔说明来显示两个或更多个插孔在功能上彼此等效。 在下面的代码示例中,音频驱动程序将黄色 RCA 插孔的插孔说明和黑色数字光学插孔合并为一个数组,以向用户指示这两个插孔携带相同的信号:

KSJACK_DESCRIPTION ar_SPDIF_Jacks[] =
{
    // Jack 1
    {
        (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT), // ChannelMapping (L,R)
        RGB(0,255,255),         // Color (yellow)
        eConnTypeRCA,           // ConnectionType (RCA)
        eGeoLocRear,            // GeoLocation
 eGenLocPrimaryBox,   // GenLocation
        ePortConnJack,       // PortConnection
        TRUE                    // IsConnected
    },
    // Jack 2
    {
        (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT), // (L,R)
        RGB(0,0,0),             // (black)
        eConnTypeOptical,       // (optical)
        eGeoLocRear,
 eGenLocPrimaryBox,
        ePortConnJack,
        TRUE
    }
};

在前面的代码示例中,两个 KSJACK_DESCRIPTION 结构中 ChannelMapping 成员的值是相同的。

WDK 中的“简单”MSVAD 示例驱动程序(在示例目录 Src\Audio\Msvad\Simple 中)可以进行调整,以支持 KSPROPERTY_JACK_DESCRIPTION 属性。 此示例驱动程序便于演示该属性的使用,因为它不需要实际硬件。 因此,它可以安装在运行 Windows 的任何计算机上。 (但是,只有 Windows Vista 和更高版本的操作系统为 KSPROPERTY_JACK_DESCRIPTION 属性提供完全支持。)有关此示例的详细信息,请参阅 Windows 驱动程序工具包示例

简单 MSVAD 示例的拓扑筛选器定义了三个桥接引脚。 下表列出了这些引脚。

引脚 ID 说明

KSPIN_TOPO_SYNTHIN_SOURCE

MIDI 输入插孔

KSPIN_TOPO_MIC_SOURCE

麦克风输入插孔

KSPIN_TOPO_LINEOUT_DEST

立体声扬声器输出插孔

本主题的其余部分介绍如何修改简单 MSVAD 示例驱动程序,以提供三个桥接引脚的插孔信息。

首先,可以按如下所示指定这些引脚的插孔信息:

// Describe MIDI input jack (pin ID = KSPIN_TOPO_SYNTHIN_SOURCE).
static KSJACK_DESCRIPTION SynthIn_Jack[] =
{
    {
        0,                  // ChannelMapping
        RGB(255,255,0),    // Color (cyan)
 eConnType3Point5mm, // ConnectionType
        eGeoLocRear,        // GeoLocation
        eGenLocPrimaryBox,  // GenLocation
        ePortConnJack,      // PortConnection
        TRUE                // IsConnected
    }
};

// Describe microphone jack (pin ID = KSPIN_TOPO_MIC_SOURCE).
static KSJACK_DESCRIPTION MicIn_Jack[] =
{
    {
        0,
        RGB(0,128,255),   // (orange)
 eConnType3Point5mm,
        eGeoLocFront,
        eGenLocPrimaryBox,
        ePortConnJack,
        TRUE
    }
};

// Describe stereo speaker jack (pin ID = KSPIN_TOPO_LINEOUT_DEST).
static KSJACK_DESCRIPTION LineOut_Jack[] =
{
    {
        (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT), // ChannelMapping (L,R)
        RGB(0,255,0),       // (green)
 eConnType3Point5mm,
        eGeoLocRear,
        eGenLocPrimaryBox,
        ePortConnJack,
        TRUE
    }
};

前面的代码示例将两个捕获引脚的 ChannelMapping 成员设置为 0。 只有模拟呈现引脚应具有非零 ChannelMapping 值。

对简单 MSVAD 示例的主要修改是将以下属性处理程序添加到示例文件 Mintopo.cpp 中拓扑微型端口的实现:

#define ARRAY_LEN(a)  sizeof(a)/sizeof(a[0]);
#define MAXIMUM_VALID_PIN_ID  KSPIN_TOPO_WAVEIN_DEST

NTSTATUS
CMiniportTopology::PropertyHandlerJackDescription(
               IN PPCPROPERTY_REQUEST PropertyRequest)
{
    PAGED_CODE();

    ASSERT(PropertyRequest);

    DPF_ENTER(("[PropertyHandlerJackDescription]"));

    NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
    ULONG nPinId = (ULONG)-1;

    if (PropertyRequest->InstanceSize >= sizeof(ULONG))
    {
        nPinId = *((PULONG)(PropertyRequest->Instance));

        if (nPinId > MAXIMUM_VALID_PIN_ID)
        {
            ntStatus = STATUS_INVALID_PARAMETER;
        }
        else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
        {
            ntStatus = PropertyHandler_BasicSupport(
                            PropertyRequest,
                            KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET,
                            VT_ILLEGAL);
        }
        else
        {
            PKSJACK_DESCRIPTION pJack = NULL;
            ULONG cJacks = 0;

            switch (nPinId)
            {
            case KSPIN_TOPO_SYNTHIN_SOURCE:
                pJack = SynthIn_Jack;
                cJacks = ARRAY_LEN(SynthIn_Jack);
                break;
            case KSPIN_TOPO_MIC_SOURCE:
                pJack = MicIn_Jack;
                cJacks = ARRAY_LEN(MicIn_Jack);
                break;
            case KSPIN_TOPO_LINEOUT_DEST:
                pJack = LineOut_Jack;
                cJacks = ARRAY_LEN(LineOut_Jack);
                break;
            default:
                break;
            }

            ULONG cbNeeded = sizeof(KSMULTIPLE_ITEM) +
                             sizeof(KSJACK_DESCRIPTION) * cJacks;

            if (PropertyRequest->ValueSize == 0)
            {
                PropertyRequest->ValueSize = cbNeeded;
                ntStatus = STATUS_BUFFER_OVERFLOW;
            }
            else if (PropertyRequest->ValueSize < cbNeeded)
            {
                ntStatus = STATUS_BUFFER_TOO_SMALL;
            }
            else if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
            {
                PKSMULTIPLE_ITEM pMI = (PKSMULTIPLE_ITEM)PropertyRequest->Value;

                pMI->Size = cbNeeded;
                pMI->Count = cJacks;

                // Copy jack description structure into Value buffer.
                // RtlCopyMemory correctly handles the case Length=0.
                PKSJACK_DESCRIPTION pDesc = (PKSJACK_DESCRIPTION)(pMI + 1);

                RtlCopyMemory(pDesc, pJack, pMI->Size * pMI->Count);

                ntStatus = STATUS_SUCCESS;
            }
        }
    }

    return ntStatus;
}

NTSTATUS
PropertyHandler_TopoFilter(IN PPCPROPERTY_REQUEST PropertyRequest)
{
    PAGED_CODE();

    ASSERT(PropertyRequest);

    DPF_ENTER(("[PropertyHandler_TopoFilter]"));

    // PropertyRequest structure is filled by PortCls.
    // MajorTarget is a pointer to miniport object for miniports.
    //
    NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
    PCMiniportTopology pMiniport = (PCMiniportTopology)PropertyRequest->MajorTarget;

    if (IsEqualGUIDAligned(*PropertyRequest->PropertyItem->Set, KSPROPSETID_Jack) &&
        (PropertyRequest->PropertyItem->Id == KSPROPERTY_JACK_DESCRIPTION))
    {
        ntStatus = pMiniport->PropertyHandlerJackDescription(PropertyRequest);
    }

    return ntStatus;
}

前面的代码示例引用前面定义的三个 KSJACK_DESCRIPTION 变量(SynthIn_Jack、MicIn_Jack 和 LineOut_Jack)。 如果客户端在筛选器中查询有效引脚的插孔说明,但该引脚并非桥接引脚(因此没有插孔说明),则查询会成功(状态代码为 STATUS_SUCCESS),但属性处理程序会返回空插孔说明,其中包含 KSMULTIPLE_ITEM 结构(没有其他任何内容)。 如果客户端指定了无效的引脚 ID(用于标识不存在的引脚),处理程序将返回状态代码 STATUS_INVALID_PARAMETER。

需要对简单 MSVAD 示例进行另外两项修改才能支持 KSPROPERTY_JACK_DESCRIPTION 属性。 这些功能是:

  • 将上述代码示例中 PropertyHandlerJackDescription 方法的声明添加到头文件 Mintopo.h 中的 CMiniportTopology 类定义。

  • 为拓扑筛选器实现自动化表,并将此表的地址加载到头文件 Toptable.h 中 PCFILTER_DESCRIPTOR 结构的 AutomationTable 成员中。 此结构名为 MiniportFilterDescriptor

若要实现筛选器的自动化表,请将以下代码插入头文件 Toptable.h(在 MiniportFilterDescriptor 定义之前):

static PCPROPERTY_ITEM PropertiesTopoFilter[] =
{
    {
        &KSPROPSETID_Jack,
        KSPROPERTY_JACK_DESCRIPTION,
        KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_BASICSUPPORT,
        PropertyHandler_TopoFilter
    }
};

DEFINE_PCAUTOMATION_TABLE_PROP(AutomationTopoFilter, PropertiesTopoFilter);

在前面的代码示例中,PCPROPERTY_ITEM 结构的 Handler 成员包含一个函数指针,指向上一步中添加到 Mintopo.cpp 的属性处理程序。 若要使属性处理程序可从头文件进行访问,请在头文件开头插入 PropertyHandler_TopoFilter 的 extern 函数声明。

有关插孔说明属性的详细信息,请参阅针对动态音频子设备的插孔说明