Usar la interfaz IKsControl para acceder a las propiedades de audio

En raras ocasiones, es posible que una aplicación de audio especializada tenga que usar la interfaz IKsControl para acceder a determinadas funcionalidades de hardware de un adaptador de audio que no está expuesto por la API DeviceTopology o la API MMDevice. La interfaz IKsControl hace que las propiedades, eventos y métodos de los dispositivos de streaming de kernel (KS) estén disponibles para las aplicaciones en modo de usuario. De interés principal para las aplicaciones de audio, son propiedades KS. La interfaz IKsControl se puede usar junto con la API DeviceTopology y la API MMDevice para acceder a las propiedades KS de los adaptadores de audio.

La interfaz IKsControl está pensada principalmente para que la usen los proveedores de hardware que escriben aplicaciones de panel de control para administrar su hardware de audio. IKsControl es menos útil para aplicaciones de audio de uso general que no están vinculadas a dispositivos de hardware concretos. La razón es que los proveedores de hardware suelen implementar mecanismos propietarios para acceder a las propiedades de audio de sus dispositivos. A diferencia de la API DeviceTopology, que oculta las peculiaridades del controlador específicos del hardware y proporciona una interfaz relativamente uniforme para acceder a las propiedades de audio, las aplicaciones usan IKsControl para comunicarse directamente con los controladores. Para obtener más información sobre IKsControl, consulte la documentación de Windows DDK.

Como se describe en Topologías de dispositivo, una subunidad en la topología de un dispositivo adaptador podría admitir una o varias de las interfaces de control específicas de la función que se muestran en la columna izquierda de la tabla siguiente. Cada entrada de la columna derecha de la tabla es la propiedad KS que corresponde a la interfaz de control de la izquierda. La interfaz de control proporciona un acceso cómodo a la propiedad . La mayoría de los controladores de audio usan propiedades KS para representar las funcionalidades de procesamiento específicas de función de las subunidades (también denominadas nodos KS) en las topologías de sus adaptadores de audio. Para obtener más información sobre las propiedades de KS y los nodos KS, consulte la documentación de Windows DDK.

Interfaz de control KS (propiedad)
IAudioAutoGainControl KSPROPERTY_AUDIO_AGC
IAudioBass KSPROPERTY_AUDIO_BASS
IAudioChannelConfig KSPROPERTY_AUDIO_CHANNEL_CONFIG
IAudioInputSelector KSPROPERTY_AUDIO_MUX_SOURCE
IAudioLoudness KSPROPERTY_AUDIO_LOUDNESS
IAudioMidrange KSPROPERTY_AUDIO_MID
IAudioMute KSPROPERTY_AUDIO_MUTE
IAudioOutputSelector KSPROPERTY_AUDIO_DEMUX_DEST
IAudioPeakMeter KSPROPERTY_AUDIO_PEAKMETER
IAudioTreble KSPROPERTY_AUDIO_TREBLE
IAudioVolumeLevel KSPROPERTY_AUDIO_VOLUMELEVEL
IDeviceSpecificProperty KSPROPERTY_AUDIO_DEV_SPECIFIC

 

Las topologías de algunos adaptadores de audio pueden contener subunidades que tienen propiedades KS que no aparecen en la tabla anterior. Por ejemplo, supongamos que una llamada al método IPart::GetSubType para una subunidad determinada recupera el valor GUID KSNODETYPE_TONE. Este GUID de subtipo indica que la subunidad admite una o varias de las siguientes propiedades de KS:

  • KSPROPERTY_AUDIO_BASS
  • KSPROPERTY_AUDIO_MID
  • KSPROPERTY_AUDIO_TREBLE
  • KSPROPERTY_AUDIO_BASS_BOOST

Se puede tener acceso a las tres primeras propiedades de esta lista a través de las interfaces de control que aparecen en la tabla anterior, pero la propiedad KSPROPERTY_AUDIO_BASS_BOOST no tiene ninguna interfaz de control correspondiente en la API DeviceTopology. Sin embargo, una aplicación puede usar la interfaz IKsControl para acceder a esta propiedad, si la subunidad admite la propiedad .

Los pasos siguientes son necesarios para acceder a la propiedad KSPROPERTY_AUDIO_BASS_BOOST de una subunidad de subtipo KSNODETYPE_TONE:

  1. Llame al método IConnector::GetDeviceIdConnectedTo o IDeviceTopology::GetDeviceId para obtener la cadena de identificador de dispositivo que identifica el dispositivo del adaptador. Esta cadena es similar a una cadena de identificador de punto de conexión, salvo que identifica un dispositivo adaptador en lugar de un dispositivo de punto de conexión. Para obtener más información sobre la diferencia entre un dispositivo de adaptador y un dispositivo de punto de conexión, consulte Dispositivos de punto de conexión de audio.
  2. Obtenga la interfaz IMMDevice del dispositivo del adaptador llamando al método IMMDeviceEnumerator::GetDevice con la cadena de id. de dispositivo. Esta interfaz IMMDevice es la misma que la que se describe en IMMDevice Interface, pero representa un dispositivo de adaptador en lugar de un dispositivo de punto de conexión.
  3. Obtenga la interfaz IKsControl en la subunidad llamando al método IMMDevice::Activate con el parámetro iid establecido en REFIID IID_IKsControl. Tenga en cuenta que las interfaces compatibles con este método Activate , que es para un dispositivo adaptador, son diferentes de las interfaces admitidas por el método Activate para un dispositivo de punto de conexión. En concreto, el método Activate para un dispositivo adaptador admite IKsControl.
  4. Llame al método IPart::GetLocalId para obtener el identificador local de la subunidad.
  5. Construya una solicitud de propiedad KS. El identificador de nodo KS necesario para la solicitud se encuentra en los 16 bits menos significativos del identificador local obtenido en el paso anterior.
  6. Envíe la solicitud de propiedad KS al controlador de audio llamando al método IKsControl::KsProperty . Para obtener más información sobre este método, consulte la documentación de Windows DDK.

En el ejemplo de código siguiente se recupera el valor de la propiedad KSPROPERTY_AUDIO_BASS_BOOST de una subunidad de subtipo KSNODETYPE_TONE:

//-----------------------------------------------------------
// This function calls the IKsControl::Property method to get
// the value of the KSPROPERTY_AUDIO_BASS_BOOST property of
// a subunit. Parameter pPart should point to a part that is
// a subunit with a subtype GUID value of KSNODETYPE_TONE.
//-----------------------------------------------------------
#define PARTID_MASK 0x0000ffff
#define EXIT_ON_ERROR(hres)  \
              if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk)  \
              if ((punk) != NULL)  \
                { (punk)->Release(); (punk) = NULL; }

const IID IID_IKsControl = __uuidof(IKsControl);

HRESULT GetBassBoost(IMMDeviceEnumerator *pEnumerator,
                     IPart *pPart, BOOL *pbValue)
{
    HRESULT hr;
    IDeviceTopology *pTopology = NULL;
    IMMDevice *pPnpDevice = NULL;
    IKsControl *pKsControl = NULL;
    LPWSTR pwszDeviceId = NULL;

    if (pEnumerator == NULL || pPart == NULL || pbValue == NULL)
    {
        return E_INVALIDARG;
    }

    // Get the topology object for the adapter device that contains
    // the subunit represented by the IPart interface.
    hr = pPart->GetTopologyObject(&pTopology);
    EXIT_ON_ERROR(hr)

    // Get the device ID string that identifies the adapter device.
    hr = pTopology->GetDeviceId(&pwszDeviceId);
    EXIT_ON_ERROR(hr)

    // Get the IMMDevice interface of the adapter device object.
    hr = pEnumerator->GetDevice(pwszDeviceId, &pPnpDevice);
    EXIT_ON_ERROR(hr)

    // Activate an IKsControl interface on the adapter device object.
    hr = pPnpDevice->Activate(IID_IKsControl, CLSCTX_ALL, NULL, (void**)&pKsControl);
    EXIT_ON_ERROR(hr)

    // Get the local ID of the subunit (contains the KS node ID).
    UINT localId = 0;
    hr = pPart->GetLocalId(&localId);
    EXIT_ON_ERROR(hr)

    KSNODEPROPERTY_AUDIO_CHANNEL ksprop;
    ZeroMemory(&ksprop, sizeof(ksprop));
    ksprop.NodeProperty.Property.Set = KSPROPSETID_Audio;
    ksprop.NodeProperty.Property.Id = KSPROPERTY_AUDIO_BASS_BOOST;
    ksprop.NodeProperty.Property.Flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY;
    ksprop.NodeProperty.NodeId = localId & PARTID_MASK;
    ksprop.Channel = 0;

    // Send the property request.to the device driver.
    BOOL bValue = FALSE;
    ULONG valueSize;
    hr = pKsControl->KsProperty(
                         &ksprop.NodeProperty.Property, sizeof(ksprop),
                         &bValue, sizeof(bValue), &valueSize);
    EXIT_ON_ERROR(hr)

    *pbValue = bValue;

Exit:
    SAFE_RELEASE(pTopology)
    SAFE_RELEASE(pPnpDevice)
    SAFE_RELEASE(pKsControl)
    CoTaskMemFree(pwszDeviceId);
    return hr;
}

En el ejemplo de código anterior, la función GetBassBoost toma los tres parámetros siguientes:

  • pEnumerator apunta a la interfaz IMMDeviceEnumerator de un enumerador de puntos de conexión de audio.
  • pPart apunta a la interfaz IPart de una subunidad que tiene un subtipo de KSNODETYPE_TONE.
  • pbValue apunta a una variable BOOL en la que la función escribe el valor de propiedad.

Un programa llama a GetBassBoost solo después de llamar a IPart::GetSubType y determina que el subtipo de la subunidad es KSNODETYPE_TONE. El valor de la propiedad es TRUE si la potenciación de bajo está habilitada. Es FALSE si el aumento de graves está deshabilitado.

Al principio de la función GetBassBoost, la llamada al método IPart::GetTopologyObject obtiene la interfaz IDeviceTopology del dispositivo adaptador que contiene la subunidad KSNODETYPE_TONE. La llamada al método IDeviceTopology::GetDeviceId recupera la cadena de identificador de dispositivo que identifica el dispositivo del adaptador. La llamada al método IMMDeviceEnumerator::GetDevice toma la cadena de identificador de dispositivo como parámetro de entrada y recupera la interfaz IMMDevice del dispositivo adaptador. A continuación, la llamada al método IMMDevice::Activate recupera la interfaz IKsControl de la subunidad. Para obtener más información sobre la interfaz IKsControl , consulta la documentación de Windows DDK.

A continuación, el ejemplo de código anterior crea una estructura KSNODEPROPERTY_AUDIO_CHANNEL que describe la propiedad bass-boost. El ejemplo de código pasa un puntero a la estructura al método IKsControl::KsProperty , que usa la información de la estructura para recuperar el valor de la propiedad. Para obtener más información sobre la estructura de KSNODEPROPERTY_AUDIO_CHANNEL y el método IKsControl::KsProperty , consulte la documentación de Windows DDK.

Por lo general, el hardware de audio asigna un estado de aumento de bajo independiente a cada canal de una secuencia de audio. En principio, el aumento de graves se puede habilitar para algunos canales y deshabilitarse para otros. La estructura KSNODEPROPERTY_AUDIO_CHANNEL contiene un miembro Channel que especifica el número de canal. Si una secuencia contiene N canales, los canales se numeran de 0 a N: 1. El ejemplo de código anterior obtiene el valor de la propiedad bass-boost solo para el canal 0. Esta implementación supone implícitamente que las propiedades de aumento de bajo para todos los canales se establecen uniformemente en el mismo estado. Por lo tanto, leer la propiedad bass-boost para el canal 0 es suficiente para determinar el valor de la propiedad bass-boost para la secuencia. Para ser coherente con esta suposición, una función correspondiente para establecer la propiedad bass-boost establecería todos los canales en el mismo valor de la propiedad bass-boost. Estas son convenciones razonables, pero no todos los proveedores de hardware necesariamente los siguen. Por ejemplo, un proveedor podría proporcionar una aplicación de panel de control que permite el aumento de graves solo para los canales que controlan altavoces de gama completa. (Un altavoz de gama completa es capaz de reproducir sonidos en la gama completa de bajo a treble). En ese caso, el valor de propiedad recuperado por el ejemplo de código anterior podría no representar con precisión el estado de aumento de bajo de la secuencia.

Los clientes que usan las interfaces de control enumeradas en la tabla anterior pueden llamar al método IPart::RegisterControlChangeCallback para registrarse para recibir notificaciones cuando cambia el valor de un parámetro de control. Tenga en cuenta que la interfaz IKsControl no proporciona un medio similar para que los clientes se registren para recibir notificaciones cuando cambia un valor de propiedad. Si IKsControl admitía notificaciones de cambio de propiedad, podría informar a una aplicación cuando otra aplicación cambió un valor de propiedad. Sin embargo, a diferencia de los controles más usados que se supervisan a través de notificaciones de IPart , las propiedades administradas por IKsControl están pensadas principalmente para su uso por parte de los proveedores de hardware y es probable que esas propiedades solo las cambien las aplicaciones del panel de control escritas por los proveedores. Si una aplicación debe detectar los cambios de propiedad realizados por otra aplicación, puede detectar los cambios mediante el sondeo periódico del valor de la propiedad a través de IKsControl.

Guía de programación