Harold.DeArmas

Short and simple.

Is there a way to tell if an audio endpoint supports jack sensing.

I need to prompt the user if an active endpoint is selected for input or output but there is no jack sensing to verify something is plugged in.



Re: Vista Pro-Audio Application Development Detect jack sensing capability

WildDuck

I'm afraid I'm no help but I am simple although never short. I'm trying to work out why my, now 3, Vista notebook machines seem to differ in the way the HD audio sensing operates.

I understand that there can be a physical switch on a jack and that this is what most people would expect to trigger jack sensing, but it seems that some descriptions imply that the audio interface itself detects the signal level and switches, for example, from mono mic to line in.

Is this the case Is there any easily understandable documentation about this anywhere I have tried to understand the docs on the Realtek site, but with my usual lack of results.






Re: Vista Pro-Audio Application Development Detect jack sensing capability

Harold.DeArmas

From what I've seen, I agree with you when it comes to laptops. It's hit and miss. I think it is mostly because the laptops have attached speakers and microphone in most cases and jack detection doesn't make a large amount of sense, at least for an on-board audio solution. Then if notebook manufacturers decide to frankenstein an HD audio card on top of that, then you get into a whole new ballpark of headaches.

If you don't mind me asking, what docs did you look at on the Realtek site I've been looking and right now I'm trying to see if "Plug and Play Guidelines for High Definition Audio Devices" [http://www.microsoft.com/whdc/device/audio/HD-aud_PnP.mspx] turns up anything useful.





Re: Vista Pro-Audio Application Development Detect jack sensing capability

Harold.DeArmas

Okay, I think I'm really close, and I want to share to see if anyone can help.

I found a really neat doc from intel [http://download.intel.com/design/chipsets/manuals/30234903.pdf] and starting on page 155 they start talking about a configuration default data structure on the physical device. What is of particular interest to me is first: how much this seems to mirror the KSJACK_DESCRIPTION structure [http://msdn2.microsoft.com/en-us/library/ms679144.aspx] and second: how bit 0 of the 11:8 field is set to tell me jack sensing is not enabled on that pin.

I've never gotten this low in hardware from an OS before and if someone can help me connect the dots, I'd really appreciate it. I can't find anything in the Core Audio API about this.

Edit: More info:

http://download.microsoft.com/download/f/0/5/f05a42ce-575b-4c60-82d6-208d3754b2d6/HDaudio11.ppt

Here is a powerpoint that includes a misc field and alludes to it's inclusion in a topology parser.





Re: Vista Pro-Audio Application Development Detect jack sensing capability

Maurits

You can query the audio endpoint's state using IMMDevice::GetState and &-ing it with the DEVICE_STATE_UNPLUGGED constant:

IMMDevice::GetState http://msdn2.microsoft.com/en-us/library/ms679040.aspx

DEVICE_STATE_UNPLUGGED http://msdn2.microsoft.com/en-us/library/aa363230.aspx

If the DEVICE_STATE_UNPLUGGED flag is set, you can infer two things:
1) The device is unplugged
2) The device is smart enough to know when it is unplugged... that is, this is a jack-sensing endpoint.

Unfortunatly, if the flag isn't set, you can infer only that at least one of the following is true
EITHER
1) The device is plugged in
OR
2) The device doesn't know when it's plugged in or not... that is, this is not a jack-sensing endpoint.

AFAIK there is no way to distinguish between these two cases.

But there's more.

AFAIK, only HD Audio devices support jack sensing; and only HD Audio devices support KSJACK_DESCRIPTION. So if the device doesn't support KSJACK_DESCRIPTION, you can infer that this is not a jack sensing endpoint.

The Vista HD Audio class driver uses information in the codec registers and information in the pin configuration registers (stored in the BIOS) to decide whether an endpoint can support the DEVICE_STATE_UNPLUGGED flag. But these registers aren't bubbled up to a public API.

Pin configuration guidelines: http://www.microsoft.com/whdc/device/audio/PinConfig.mspx

HD Audio spec: http://www.intel.com/standards/hdaudio/

In order to qualify as a jack-sensing endpoint under the Vista HD Audio class driver, the following two things must hold true:

1) the "presence detect capable" bit in the "pin capabilities" register must be 1 (see 7.3.4.9 of the HD Audio spec)
AND
2) the "jack detect override" bit (first "misc" bit) in the "configuration default" register must be 0 (see 7.3.3.31 of the HD Audio spec)

The HD Audio spec allows for codecs to inform the driver of a plug/unplug event via an "unsolicited response" - this is also required for an HD Audio codec to get a logo. Audio widgets that support this have the "unsolicited response capable" bit set in the "audio widget capabilities" register (see 7.3.4.6 of the HD Audio spec)





Re: Vista Pro-Audio Application Development Detect jack sensing capability

Harold.DeArmas

Maurits, thanks again for a great response.

I had gotten past the first part, inferring using the unplugged flag, but this is already done in software and if I can detect it is unplugged, it's not active and I don't enumerate that endpoint for use. Its the second case that I'm having trouble with.
I want to handle the case where a device is active, but does not support jack sensing.


I don't know if it is true that only HDA devices support jack sensing. I've been pouring through docs this morning and I keep finding references to AC'97 2.3 introducing jack sensing as well.
I might have to rely on KSJack_Description being accessible as an additional check.


My curiosity gets to me though because it seems like the enumerations EPxcPortConnection, EPcxGeoLocation, EPcxConnectionType, and DWORD COLORREF all are exposed pretty much directly from the HD Audio spec, but the jack detect override is not.
Even if the registers aren't available in a public API, I wish the result (RO maybe) bool of whether an endpoint qualifies as a jack-sensing endpoint.


So given my "gung ho" spirit and my "if vista can do it, so can I" attitude, can you lead me on a direction to possibly check:
1) the "presence detect capable" bit in the "pin capabilities" register must be 1 (5.3.4.9 in my .pdf)
AND
2) the "jack detect override" bit (first "misc" bit) in the "configuration default" register must be 0 (5.3.3.31 in my .pdf)
AND possibly
3) Audio widgets that support this have the "unsolicited response capable" bit set in the "audio widget capabilities" register (5.3.4.6 in my .pdf)


I wouldn't think that these registers have security concerns. There are "Verb ID"s defined for all of these, with "get" results, which is what I want, I just don't know how to use. And it's okay if the code sucks, the code only has to be ugly once, then I can tuck it away.

Thanks again,




Re: Vista Pro-Audio Application Development Detect jack sensing capability

Maurits

Alas, those registers are only available to the driver... and the Vista HD Audio class driver doesn't expose them to areas higher up in the stack.

I'll forward your suggestion (that there be a public way to determine whether a given endpoint supports jack presence detection) to the audio developers.





Re: Vista Pro-Audio Application Development Detect jack sensing capability

Harold.DeArmas

Thanks for all the help Maurits. It may not be the answer I want, but definately the answer I was looking for. There is no nice way to do this (as of yet). I'll keep my eyes out for this in the future.





Re: Vista Pro-Audio Application Development Detect jack sensing capability

Victor Abrash

I have a further question about this, hopefully someone can answer me. I have a Vista laptop using Realtek HD audio which I think supports jack sensing, in the sense that if I plug a microphone into the jack, then the "Realtek High Definition Audio" device exists in the Windows multimedia control panel. If I unplug the microphone, then the device disappears (unless of course I select the Show Disabled Devices option).

Furthermore, using the program below, which I cobbled together from various MS documentation, I can enumerate the audio endpoint devices, and get and display the KSJACK_DESCRIPTION structure (assuming the mic is plugged in)...

From this, I would think that jack sensing is supported on this particular (Japanese) system.

However, when I unplug the microphone, and enumerate the audio endpoints, the input device state is 4 (DEVICE_STATE_NOTPRESENT), not 8 (DEVICE_STATE_UNPLUGGED). Because Windows thinks the device is not present, I can no longer use this code to get the jack description structure, because the

pConnFrom->GetConnectedTo(&pConnTo);

call doesn't suceed.

Am I missing something Has the device driver creator simply decided to mark the device as "not present" rather than "unplugged", so I can't get the information I want (which is to get the jack description and see that IsConnected=FALSE) Is this a "you can't do this in Vista" thing Or is my code just wrong

I would like to code up a routine which tries to detect that no microphone is plugged in, on an HD audio system.

Any help is appreciated,

Victor

#include <stdio.h>
#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <Devicetopology.h>
#include <functiondiscoverykeys.h>
#include <tchar.h>

#define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->Release(); (punk) = NULL; }

//-----------------------------------------------------------
// Print (raw) jack information
//-----------------------------------------------------------
void printJack(UINT index, KSJACK_DESCRIPTION *ksjack)
{
if (ksjack != NULL) {
printf("\n");
printf("\tJack[%d]->ChannelMapping = %d\n", index, ksjack->ChannelMapping);
printf("\tJack[%d]->Color = %d\n", index, ksjack->Color);
printf("\tJack[%d]->ConnectionType = %d\n", index, ksjack->ConnectionType);
printf("\tJack[%d]->GeoLocation = %d\n", index, ksjack->GeoLocation);
printf("\tJack[%d]->GenLocation = %d\n", index, ksjack->GenLocation);
printf("\tJack[%d]->PortConnection = %d\n", index, ksjack->PortConnection);
printf("\tJack[%d]->IsConnected = %d\n", index, ksjack->IsConnected);
}
}

//-----------------------------------------------------------
// Get the IKsJackDescription interface that describes the
// audio jack or jacks that the endpoint device plugs into.
//-----------------------------------------------------------
HRESULT GetJackInfo(IMMDevice *pDevice, IKsJackDescription **ppJackDesc)
{
HRESULT hr = S_OK;
IDeviceTopology *pDeviceTopology = NULL;
IConnector *pConnFrom = NULL;
IConnector *pConnTo = NULL;
IPart *pPart = NULL;
IKsJackDescription *pJackDesc = NULL;

if (NULL != ppJackDesc)
*ppJackDesc = NULL;

if (NULL == pDevice || NULL == ppJackDesc)
return E_POINTER;

// Get the endpoint device's IDeviceTopology interface.
printf("\tGetting device topology...\n");
hr = pDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL,
NULL, (void**)&pDeviceTopology);
if (hr != S_OK) {
SAFE_RELEASE(pDeviceTopology);
return hr;
}

// The device topology for an endpoint device always
// contains just one connector (connector number 0).
printf("\tGetting connnector...\n");
hr = pDeviceTopology->GetConnector(0, &pConnFrom);
if (hr != S_OK) {
SAFE_RELEASE(pDeviceTopology);
SAFE_RELEASE(pConnFrom);
return hr;
}

// Step across the connection to the jack on the adapter.
printf("\tGetting connectedTo...\n");
hr = pConnFrom->GetConnectedTo(&pConnTo);
if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
{
// The adapter device is not currently active.
printf("\tWarning: Couldn't get ConnectedTo, because adapter device isn't currently active, trying to continue!\n");
hr = E_NOINTERFACE;
//hr = S_OK;
}
if (hr != S_OK) {
SAFE_RELEASE(pDeviceTopology);
SAFE_RELEASE(pConnFrom);
SAFE_RELEASE(pConnTo);
return hr;
}

// Get the connector's IPart interface.
printf("\tGetting part...\n");
hr = pConnTo->QueryInterface(__uuidof(IPart), (void**)&pPart);
if (hr != S_OK) {
SAFE_RELEASE(pDeviceTopology);
SAFE_RELEASE(pConnFrom);
SAFE_RELEASE(pConnTo);
SAFE_RELEASE(pPart);
return hr;
}

// Activate the connector's IKsJackDescription interface.
printf("\tActivating jack description...\n");
hr = pPart->Activate(CLSCTX_INPROC_SERVER,
__uuidof(IKsJackDescription), (void**)&pJackDesc);
// printf("hr = %x (%d)\n", hr, (int)hr);
if (hr != S_OK) {
if (hr == E_INVALIDARG) {
printf("\tCouldn't activate IKsJackDescription because E_INVALIDARG\n");
} else if (hr == E_POINTER) {
printf("\tCouldn't activate IKsJackDescription because E_POINTER\n");
} else if (hr == E_NOINTERFACE) {
printf("\tCouldn't activate IKsJackDescription because E_NOINTERFACE\n");
} else {
printf("\tCouldn't activate IKsJackDescription because UNKNOWN ERROR\n");
}
}
/* printf("invalid = %d, pointer = %d, nointerface = %d\n",
(int)E_INVALIDARG, (int)E_POINTER, (int)E_NOINTERFACE);*/
if (hr != S_OK) {
SAFE_RELEASE(pDeviceTopology);
SAFE_RELEASE(pConnFrom);
SAFE_RELEASE(pConnTo);
SAFE_RELEASE(pPart);
return hr;
}

printf("\tGot jack description...\n");
if (NULL != ppJackDesc)
*ppJackDesc = pJackDesc;

SAFE_RELEASE(pDeviceTopology);
SAFE_RELEASE(pConnFrom);
SAFE_RELEASE(pConnTo);
SAFE_RELEASE(pPart);
return hr;
}

//-----------------------------------------------------------
// This function enumerates all active (plugged in) audio
// rendering endpoint devices. It prints the friendly name
// and endpoint ID string of each endpoint device.
//-----------------------------------------------------------

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);

void PrintEndpointNames(EDataFlow dataFlow, DWORD dwState)
{
HRESULT hr = S_OK;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDeviceCollection *pCollection = NULL;
IMMDevice *pEndpoint = NULL;
IPropertyStore *pProps = NULL;
LPWSTR pwszID = NULL;

hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator);
if (hr != S_OK) {
SAFE_RELEASE(pEnumerator);
return;
}

// DEVICE_STATE_ACTIVE, DEVICE_STATEMASK_ALL
hr = pEnumerator->EnumAudioEndpoints(dataFlow, dwState, &pCollection);
if (hr != S_OK) {
SAFE_RELEASE(pEnumerator);
SAFE_RELEASE(pCollection);
return;
}

UINT count;
hr = pCollection->GetCount(&count);
if (hr != S_OK) {
SAFE_RELEASE(pEnumerator);
SAFE_RELEASE(pCollection);
return;
}

if (count == 0)
printf("No endpoints found.\n");

// Each loop prints the name of an endpoint device.
for (ULONG i = 0; i < count; i++)
{
// Get pointer to endpoint number i.
hr = pCollection->Item(i, &pEndpoint);
if (hr != S_OK) {
SAFE_RELEASE(pEndpoint);
continue;
}

// Get the endpoint ID string.
hr = pEndpoint->GetId(&pwszID);
if (hr != S_OK) {
CoTaskMemFree(pwszID);
SAFE_RELEASE(pEndpoint);
continue;
}

hr = pEndpoint->OpenPropertyStore(STGM_READ, &pProps);
if (hr != S_OK) {
CoTaskMemFree(pwszID);
SAFE_RELEASE(pEndpoint);
SAFE_RELEASE(pProps);
continue;
}

PROPVARIANT varName;
// Initialize container for property value.
PropVariantInit(&varName);

// Get the endpoint's friendly-name property.
hr = pProps->GetValue(PKEY_Device_FriendlyName, &varName);
if (hr != S_OK) {
CoTaskMemFree(pwszID);
SAFE_RELEASE(pEndpoint);
SAFE_RELEASE(pProps);
continue;
}

// Print endpoint friendly name and endpoint ID.
printf("\nEndpoint %d: \"%S\"\n", i, varName.pwszVal);
printf("\tID = (%S)\n", pwszID);

CoTaskMemFree(pwszID);
pwszID = NULL;
PropVariantClear(&varName);
SAFE_RELEASE(pProps);

// Got an endpoint now, so do the following:
// 1. Get/print the state
// 2. Get/print the jack description

// now that I have the device, I can use a subset of the
// PrintEndpointNames() function below.... I can also call:
// Activate, GetId, GetState, and OpenPropertyStore. For
// examples of GetId and OpenPropertyStore, see
// PrintEndpointNames().

// printf("Getting input device state...\n");
DWORD deviceState = 0;
hr = pEndpoint->GetState(&deviceState);
if (hr != S_OK) {
SAFE_RELEASE(pEndpoint);
continue;
}

printf("\n\tInput device state = %x (%d), ",
deviceState, (int)deviceState);
if (deviceState & DEVICE_STATE_ACTIVE) { printf("Device is active!\n"); }
if (deviceState & DEVICE_STATE_DISABLED) { printf("Device is disabled!\n"); }
if (deviceState & DEVICE_STATE_NOTPRESENT) { printf("Device is not present!\n"); }
if (deviceState & DEVICE_STATE_UNPLUGGED) { printf("Device is unplugged!\n"); }

printf("\n\tGetting jack info...\n");
IKsJackDescription *pJackDesc = NULL;
hr = GetJackInfo(pEndpoint, &pJackDesc);
if (hr != S_OK) {
printf("\tCouldn't get Jack information!\n");
SAFE_RELEASE(pJackDesc);
SAFE_RELEASE(pEndpoint);
continue;
}

// now print the information in the KSJACK_DESCRIPTION,
// especially IsConnected().
UINT nJacks = 1;
hr = pJackDesc->GetJackCount(&nJacks);
if (hr != S_OK) {
SAFE_RELEASE(pJackDesc);
SAFE_RELEASE(pEndpoint);
continue;
}

printf("\tFound %d jacks\n", nJacks);
KSJACK_DESCRIPTION ksjack;
for (UINT i = 0; i < nJacks; i++) {
hr = pJackDesc->GetJackDescription(i, &ksjack);
if (hr != S_OK) {
SAFE_RELEASE(pEndpoint);
continue;
}
printJack(i, &ksjack);
}

SAFE_RELEASE(pJackDesc);
SAFE_RELEASE(pEndpoint);
}

SAFE_RELEASE(pEnumerator);
SAFE_RELEASE(pCollection);
return;
}

//-----------------------------------------------------------
//
//-----------------------------------------------------------

int main(int argc, char **argv)
{
HRESULT hr = S_OK;
CoInitialize(NULL);
// CoInitializeEx(NULL, COINIT_MULTITHREADED);

// try the print function
printf("Printing active eRender endpoints:\n\n");
PrintEndpointNames(eRender, DEVICE_STATE_ACTIVE);
printf("\nPrinting active eCapture endpoints:\n\n");
PrintEndpointNames(eCapture, DEVICE_STATE_ACTIVE);

printf("\n\nPrinting all eRender endpoints:\n\n");
PrintEndpointNames(eRender, DEVICE_STATEMASK_ALL);
printf("\nPrinting all eCapture endpoints:\n\n");
PrintEndpointNames(eCapture, DEVICE_STATEMASK_ALL);
printf("\n");

CoUninitialize();
return 0;
}





Re: Vista Pro-Audio Application Development Detect jack sensing capability

Maurits

Sounds like you're using the Realtek driver. If they're reporting the state of unplugged endpoints as DEVICE_STATE_NOTPRESENT, that is arguably a bug in their driver.

On the other hand, I've noticed that they allow for jack multitasking... where you plug something in to, say, the blue jack and they pop up a dialog saying "what did you plug in ". So there may be a defensible rationalization for making the microphone "really go away" (_NOTPRESENT) when it's unplugged, because perhaps next time you'll plug something different into that jack.