severino

Hello,

we're developing a .NET component that should be able to change/retrieve volume levels available for the channles available inside a certain sound card installed in Vista.

I'm actually performing some test on a PC which comes with an onboard "High definition audio device" and with a "Creative Audigy 2 NX" USB card.

For the "High definition audio device" the displayed levels is only the one related to the speakers as seen on the screenshot below:

http://www.multimediasoft.com/temp/onboard.jpg

Differently, for the "Creative Audigy 2 NX" card the displayed levels are the following:

http://www.multimediasoft.com/temp/audigy.jpg

The questions are:

- how can I enumerate the other available levels for the Creative card

- once enumerated, how can I access them in order to change these volume levels and mute status

Thanks in advance and Kind Regards

Severino Delaurenti



Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

AntonX

You need to use the IDeviceTopology interface to traverse the topology tree for the rendering endpoint. All these controls (MIDI, CD Audio, etc.) are connected to one side (opposite to the main volume) of the Sum part and have IAudioVolumeLevel and IAudioMute interfaces available. Here is some reading: http://msdn2.microsoft.com/en-us/library/ms678713.aspx





Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

severino

Hello,

I've read the mentioned document and also tried to figure out the relationship between the various interfaces but I must say that I cannot actually understand how to do the mentioned enumeration: I was only able to get information about the IKsJackDescription interface in order to enumerate the available jacks and their descriptions but nothing more so I'm completely lost: any further information would be more than welcome.

Kind regards

Severino





Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

severino

Wow, a so difficult question to answer Anyone from MS audio development team All this is really frustrating...





Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

Zach Nelson

I'm in the same boat as you. I need to enumerate the outputs of a multi-channel card and play specific wavs to each one. Still no luck (or help) ..



Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

Maurits

Severino, could you tell me a little more about what you tried along IDeviceTopology lines

In principle there should be an IAudioVolumeLevel and an IAudioMute node for each of those sliders that you want to be able to control.





Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

severino

Hello,

well, what I have tried to do was trying to understand what was written inside the documentation but the result was a big confusion. I was able to get the IDeviceTopology for a certain device using the code below but, after getting the IPart interface, I wasn't able to go ahead:

IDeviceTopology *pDevTopoEndpt = NULL;
hr = pDevice->Activate(
__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&pDevTopoEndpt);

// The device topology for an endpoint device always
// contains just one connector (connector number 0).
IConnector *pConnEndpt = NULL;
hr = pDevTopoEndpt->GetConnector(0, &pConnEndpt);

IConnector *pConnHWDev = NULL;
hr = pConnEndpt->GetConnectedTo(&pConnHWDev);

// Query the connector in the audio hardware device for
// its IPart interface.
IPart *pPartConn = NULL;
hr = pConnHWDev->QueryInterface(__uuidof(IPart), (void**)&pPartConn);

Kind Regards

Severino





Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

Maurits

You're crossing the line between "audio device" and "audio endpoint" here. As you note, the High Definition Audio device and its driver do not allow you to monitor your capture endpoints via the speaker. On the other hand, the Creative card and its driver do allow you to monitor your capture endpoints on your Creative render endpoint.

Device Topology is a map of the audio device... it shows how all the various sound-processing nodes on the card are connected to each other, to the endpoints, and to the internal "system" interface.

The audio endpoint is a leaf node that has a single connector. You'll need to go further than that - you'll need to wander the graph until you find the volume nodes and mute nodes that interest you, and then do with them what you will.





Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

severino

Maurits wrote:
You'll need to go further than that - you'll need to wander the graph until you find the volume nodes and mute nodes that interest you, and then do with them what you will.

Hello Maurits,

I hope that you were not trying to joke with me because telling that I should "go further than that" without giving a possible solution in terms of code (and interfaces to call) really seems to be an attempt to joke: in cases like this, if you don't have a valid solution, it would be better to avoid posting. I have no doubt that I should go further than that or I wouldn't need to post my questions in this, till now, useless forum: the problem is the fact that Microsoft's documentation on this topic sucks and what could be done easily in Windows XP and past versions is now not possible because someone woke up a morning and decided to broke compatibility with the past and to use a different terminology just to better confuse ideas to other developers.

Severino





Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

Maurits

To walk the tree, use the methods exposed by the IPart interface:
http://msdn2.microsoft.com/en-us/library/ms679050.aspx

You have the part that the audio endpoint connects to. You can call pPart->EnumPartsIncoming((void**)&pPartsList) to get the list of parts that connect in to that endpoint (EnumPartsOutgoing is not of use to you because you're walking the tree backwards from the output end.)

For each part, you can QI to IAudioVolumeLevel and IAudioMute to see if that part is a volume node or a mute node. If it is, you can call the IAudioVolumeLevel or IAudioMute methods accordingly on that QI. You can also call EnumPartsIncoming on each of these parts to walk your way backwards along the audio path.

IAudioVolume interface is a thin layer over the IPerChannelDbLevel interface:
http://msdn2.microsoft.com/en-us/library/ms679067.aspx

IAudioMute interface:
http://msdn2.microsoft.com/en-us/library/ms678773.aspx

EDIT: fixed IAudioMute link

EDIT2: I'm wrong about QI'ing to IAudioVolumeLevel or IAudio Mute. There's an IPart::Activate method that you pass a IID_IAudioMute REFID parameter - the Activate call will only succeed if this is a mute node.





Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

Maurits

See Scotty's post on this page:

http://forums.microsoft.com/MSDN/ShowPost.aspx PostID=960616&SiteID=1

-- quote --
Wow. I was able to find the controls, but they were really buried. The Speakers endpoint does have a single connector. I obtain the connector and call GetConnectedTo to obtain a second connector. From this connector, I call QueryInterface to get the part -- the resulting IPart has a name of "Speakers". This is the part you say "should then have connectors to the various volume control parts". IPart doesn't seem to have functions to retrieve associated connectors. I can call EnumPartsIncoming, which yields a single IPart with the name "Swap Center/Subwoofer Controls". Calling EnumPartsIncoming on the "Swap Center/Subwoofer Controls" IPart yields one IPart named "Master Mute", and calling EnumPartsIncoming on that yields another IPart named "Speakers". If I continue calling EnumPartsIncoming this way, I get an IPart named "Stereo Mix" that has 4 incoming parts. Three of these are named "Mute" and if I call Activate on it with an IAudioMute refiid, I can actually toggle the mute states of Line In, Mic, and CD Player as they heard through the speakers. The only way I could find to identify which "Mute" IPart went to which input was to call EnumPartsIncoming again on the respective "Mute" IParts. That yields IParts named "Line In", "Microphone", and "Mute", which can also be Activated with a refiid of IAudioVolumeLevel to set the respective volumes. Thank you so much for all of your help.
-- end quote --





Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

severino

Maurits wrote:
See Scotty's post on this page:

http://forums.microsoft.com/MSDN/ShowPost.aspx PostID=960616&SiteID=1

cut...

Thanks for your efforts Maurits but all of this continue sounding very similar to arabic to my hears: ok, I will give up and will wait that someone will have the time to write a book with real-word source code samples about this topic. In the meantime I will continue suggesting our customers to avoid upgrading to Vista if they want to deal with working audio.





Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

Maurits

It sounds like you're trying to programmatically control the volume level/mute status of capture "monitoring"... that is, you want to be able to listen (on your speakers) to what you're capturing (via Line In). Is that accurate

As you note in your original post, some card/driver combinations do not expose these "monitoring" volume sliders/mute controls, and some do. This is unfortunate.

There are simpler ways to control capture volume on the capture endpoint... see Larry's blog post on IAudioEndpointVolume, for example. His example deals with render endpoints, but the same code works for capture endpoints if you use eCapture instead of eRender.
http://blogs.msdn.com/larryosterman/archive/2007/03/06/how-do-i-change-the-master-volume-in-windows-vista.aspx

It is also possible - though admittedly, not easy - to control the capture hardware volume controls you see on the "Speakers" page of the control panel.

I have sample code that walks the device graph, finding all the hardware nodes... I don't (yet) have sample code that actually does anything with the nodes.

Here it is:
#include <windows.h>
#include <stdio.h>

#include <mmdeviceapi.h>
#include <devicetopology.h>

HRESULT WalkTreeBackwardsFromPart(IPart *pPart, int iTabLevel = 0);
void Tab(int iTabLevel);

int __cdecl main(void) {
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) {
printf("Failed CoInitializeEx: hr = 0x%08x\n", hr);
return __LINE__;
}

// get default render endpoint
IMMDeviceEnumerator *pEnum = NULL;
hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
(void**)&pEnum
);
if (FAILED(hr)) {
printf("Couldn't get device enumerator: hr = 0x%08x\n", hr);
CoUninitialize();
return __LINE__;
}
IMMDevice *pDevice = NULL;
hr = pEnum->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
if (FAILED(hr)) {
printf("Couldn't get default render device: hr = 0x%08x\n", hr);
pEnum->Release();
CoUninitialize();
return __LINE__;
}
pEnum->Release();

// get device topology object for that endpoint
IDeviceTopology *pDT = NULL;
hr = pDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&pDT);
if (FAILED(hr)) {
printf("Couldn't get device topology object: hr = 0x%08x\n", hr);
pDevice->Release();
CoUninitialize();
return __LINE__;
}
pDevice->Release();

// get the single connector for that endpoint
IConnector *pConnEndpoint = NULL;
hr = pDT->GetConnector(0, &pConnEndpoint);
if (FAILED(hr)) {
printf("Couldn't get the connector on the endpoint: hr = 0x%08x\n", hr);
pDT->Release();
CoUninitialize();
return __LINE__;
}
pDT->Release();

// get the connector on the device that is
// connected to
// the connector on the endpoint
IConnector *pConnDevice = NULL;
hr = pConnEndpoint->GetConnectedTo(&pConnDevice);
if (FAILED(hr)) {
printf("Couldn't get the connector on the device: hr = 0x%08x\n", hr);
pConnEndpoint->Release();
CoUninitialize();
return __LINE__;
}
pConnEndpoint->Release();

// QI on the device's connector for IPart
IPart *pPart = NULL;
hr = pConnDevice->QueryInterface(__uuidof(IPart), (void**)&pPart);
if (FAILED(hr)) {
printf("Couldn't get the part: hr = 0x%08x\n", hr);
pConnDevice->Release();
CoUninitialize();
return __LINE__;
}
pConnDevice->Release();

// all the real work is done in this function
hr = WalkTreeBackwardsFromPart(pPart);
if (FAILED(hr)) {
printf("Couldn't walk the tree: hr = 0x%08x\n", hr);
pPart->Release();
CoUninitialize();
return __LINE__;
}
pPart->Release();

CoUninitialize();

return 0;
}

HRESULT WalkTreeBackwardsFromPart(IPart *pPart, int iTabLevel /* = 0 */) {
HRESULT hr = S_OK;

Tab(iTabLevel);
printf("Walking tree for %p\n", pPart);

// You could put code here to activate the IAudioMute and IAudioVolumeLevel
// controls on the pPart IPart interface

// get the list of incoming parts
IPartsList *pIncomingParts = NULL;
hr = pPart->EnumPartsIncoming(&pIncomingParts);
if (E_NOTFOUND == hr) {
// not an error... we've just reached the end of the path
Tab(iTabLevel);
printf("No incoming parts at this part\n");
return S_OK;
}
if (FAILED(hr)) {
printf("Couldn't enum incoming parts: hr = 0x%08x\n", hr);
return hr;
}
UINT nParts = 0;
hr = pIncomingParts->GetCount(&nParts);
if (FAILED(hr)) {
printf("Couldn't get count of incoming parts: hr = 0x%08x\n", hr);
pIncomingParts->Release();
return hr;
}

// walk the tree on each incoming part recursively
for (UINT n = 0; n < nParts; n++) {
IPart *pIncomingPart = NULL;
hr = pIncomingParts->GetPart(n, &pIncomingPart);
if (FAILED(hr)) {
printf("Couldn't get part #%u (0-based) of %u (1-basedSmile hr = 0x%08x\n", n, nParts, hr);
pIncomingParts->Release();
return hr;
}

hr = WalkTreeBackwardsFromPart(pIncomingPart, iTabLevel + 1);
if (FAILED(hr)) {
printf("Couldn't walk tree on part #%u (0-based) of %u (1-basedSmile hr = 0x%08x\n", n, nParts, hr);
pIncomingPart->Release();
pIncomingParts->Release();
return hr;
}
pIncomingPart->Release();
}

pIncomingParts->Release();

return S_OK;
}

void Tab(int iTabLevel) {
if (0 >= iTabLevel) { return; }
printf("\t");
Tab(iTabLevel - 1);
}






Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

Maurits

Here's a version that actually does something useful. It queries the volume and mute nodes it finds, and prints out their current settings. I also print out the parts' friendly names.

First, here's the output I get on a couple of machines:

HD Audio output:
Part name: Speakers
Part name: Master Mute
Mute node: NOT MUTED
Part name: Speakers
Channel 0 volume is -8.000 dB (range is -64.000 dB to 0.000 dB in increments of 1.000 dB)
Channel 1 volume is -8.000 dB (range is -64.000 dB to 0.000 dB in increments of 1.000 dB)
Part name: (Unnamed)
No incoming parts at this part

AC'97 output:
Part name: Volume Control
Part name: Mute
Mute node: NOT MUTED
Part name: Speakers
Channel 0 volume is -0.750 dB (range is -46.500 dB to 0.000 dB in increments of 1.500 dB)
Channel 1 volume is -0.750 dB (range is -46.500 dB to 0.000 dB in increments of 1.500 dB)
Part name: Wave
Part name: (Unnamed)
No incoming parts at this part
Part name: Mute
Mute node: NOT MUTED
Part name: Phone Line
Channel 0 volume is 11.250 dB (range is -34.500 dB to 12.000 dB in increments of 1.500 dB)
Part name: Phone Line
No incoming parts at this part
Part name: Mute
Mute node: MUTED
Part name: Line Volume
Channel 0 volume is 11.250 dB (range is -34.500 dB to 12.000 dB in increments of 1.500 dB)
Channel 1 volume is 11.250 dB (range is -34.500 dB to 12.000 dB in increments of 1.500 dB)
Part name: Line In
No incoming parts at this part
Part name: Mute
Mute node: MUTED
Part name: Aux Volume
Channel 0 volume is 11.250 dB (range is -34.500 dB to 12.000 dB in increments of 1.500 dB)
Channel 1 volume is 11.250 dB (range is -34.500 dB to 12.000 dB in increments of 1.500 dB)
Part name: Aux
No incoming parts at this part
Part name: Mute
Mute node: MUTED
Part name: Mic Volume
Channel 0 volume is 2.250 dB (range is -34.500 dB to 3.000 dB in increments of 1.500 dB)
Part name: MIC Boost
Part name: Microphone
No incoming parts at this part
Part name: Mute
Mute node: NOT MUTED
Part name: CD Volume
Channel 0 volume is 5.250 dB (range is -34.500 dB to 12.000 dB in increments of 1.500 dB)
Channel 1 volume is 5.250 dB (range is -34.500 dB to 12.000 dB in increments of 1.500 dB)
Part name: CD Player
No incoming parts at this part
Part name: Mute
Part name: Mute
Part name: (Unnamed)
No incoming parts at this part
Part name: Bass Boost Mute
Part name: Bass Boost Level
Part name: Bass Boost
No incoming parts at this part

And here's the updated code listing:

#include <windows.h>
#include <stdio.h>

#include <mmdeviceapi.h>
#include <devicetopology.h>

HRESULT WalkTreeBackwardsFromPart(IPart *pPart, int iTabLevel = 0);
HRESULT DisplayVolume(IAudioVolumeLevel *pVolume, int iTabLevel);
HRESULT DisplayMute(IAudioMute *pMute, int iTabLevel);
void Tab(int iTabLevel);

int __cdecl main(void) {
HRESULT hr = CoInitialize(NULL);
if (FAILED(hr)) {
printf("Failed CoInitializeEx: hr = 0x%08x\n", hr);
return __LINE__;
}

// get default render endpoint
IMMDeviceEnumerator *pEnum = NULL;
hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator),
(void**)&pEnum
);
if (FAILED(hr)) {
printf("Couldn't get device enumerator: hr = 0x%08x\n", hr);
CoUninitialize();
return __LINE__;
}
IMMDevice *pDevice = NULL;
hr = pEnum->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
if (FAILED(hr)) {
printf("Couldn't get default render device: hr = 0x%08x\n", hr);
pEnum->Release();
CoUninitialize();
return __LINE__;
}
pEnum->Release();

// get device topology object for that endpoint
IDeviceTopology *pDT = NULL;
hr = pDevice->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&pDT);
if (FAILED(hr)) {
printf("Couldn't get device topology object: hr = 0x%08x\n", hr);
pDevice->Release();
CoUninitialize();
return __LINE__;
}
pDevice->Release();

// get the single connector for that endpoint
IConnector *pConnEndpoint = NULL;
hr = pDT->GetConnector(0, &pConnEndpoint);
if (FAILED(hr)) {
printf("Couldn't get the connector on the endpoint: hr = 0x%08x\n", hr);
pDT->Release();
CoUninitialize();
return __LINE__;
}
pDT->Release();

// get the connector on the device that is
// connected to
// the connector on the endpoint
IConnector *pConnDevice = NULL;
hr = pConnEndpoint->GetConnectedTo(&pConnDevice);
if (FAILED(hr)) {
printf("Couldn't get the connector on the device: hr = 0x%08x\n", hr);
pConnEndpoint->Release();
CoUninitialize();
return __LINE__;
}
pConnEndpoint->Release();

// QI on the device's connector for IPart
IPart *pPart = NULL;
hr = pConnDevice->QueryInterface(__uuidof(IPart), (void**)&pPart);
if (FAILED(hr)) {
printf("Couldn't get the part: hr = 0x%08x\n", hr);
pConnDevice->Release();
CoUninitialize();
return __LINE__;
}
pConnDevice->Release();

// all the real work is done in this function
hr = WalkTreeBackwardsFromPart(pPart);
if (FAILED(hr)) {
printf("Couldn't walk the tree: hr = 0x%08x\n", hr);
pPart->Release();
CoUninitialize();
return __LINE__;
}
pPart->Release();

CoUninitialize();

return 0;
}

HRESULT WalkTreeBackwardsFromPart(IPart *pPart, int iTabLevel /* = 0 */) {
HRESULT hr = S_OK;

Tab(iTabLevel);
LPWSTR pwszPartName = NULL;
hr = pPart->GetName(&pwszPartName);
if (FAILED(hr)) {
printf("Could not get part name: hr = 0x%08x", hr);
return hr;
}
printf("Part name: %ws\n", *pwszPartName pwszPartName : L"(Unnamed)");
CoTaskMemFree(pwszPartName);

// see if this is a volume node part
IAudioVolumeLevel *pVolume = NULL;
hr = pPart->Activate(CLSCTX_ALL, __uuidof(IAudioVolumeLevel), (void**)&pVolume);
if (E_NOINTERFACE == hr) {
// not a volume node
} else if (FAILED(hr)) {
printf("Unexpected failure trying to activate IAudioVolumeLevel: hr = 0x%08x\n", hr);
return hr;
} else {
// it's a volume node...
hr = DisplayVolume(pVolume, iTabLevel);
if (FAILED(hr)) {
printf("DisplayVolume failed: hr = 0x%08x", hr);
pVolume->Release();
return hr;
}

pVolume->Release();
}

// see if this is a mute node part
IAudioMute *pMute = NULL;
hr = pPart->Activate(CLSCTX_ALL, __uuidof(IAudioMute), (void**)&pMute);
if (E_NOINTERFACE == hr) {
// not a mute node
} else if (FAILED(hr)) {
printf("Unexpected failure trying to activate IAudioMute: hr = 0x%08x\n", hr);
return hr;
} else {
// it's a mute node...
hr = DisplayMute(pMute, iTabLevel);
if (FAILED(hr)) {
printf("DisplayMute failed: hr = 0x%08x", hr);
pMute->Release();
return hr;
}

pMute->Release();
}

// get the list of incoming parts
IPartsList *pIncomingParts = NULL;
hr = pPart->EnumPartsIncoming(&pIncomingParts);
if (E_NOTFOUND == hr) {
// not an error... we've just reached the end of the path
Tab(iTabLevel);
printf("No incoming parts at this part\n");
return S_OK;
}
if (FAILED(hr)) {
printf("Couldn't enum incoming parts: hr = 0x%08x\n", hr);
return hr;
}
UINT nParts = 0;
hr = pIncomingParts->GetCount(&nParts);
if (FAILED(hr)) {
printf("Couldn't get count of incoming parts: hr = 0x%08x\n", hr);
pIncomingParts->Release();
return hr;
}

// walk the tree on each incoming part recursively
for (UINT n = 0; n < nParts; n++) {
IPart *pIncomingPart = NULL;
hr = pIncomingParts->GetPart(n, &pIncomingPart);
if (FAILED(hr)) {
printf("Couldn't get part #%u (0-based) of %u (1-basedSmile hr = 0x%08x\n", n, nParts, hr);
pIncomingParts->Release();
return hr;
}

hr = WalkTreeBackwardsFromPart(pIncomingPart, iTabLevel + 1);
if (FAILED(hr)) {
printf("Couldn't walk tree on part #%u (0-based) of %u (1-basedSmile hr = 0x%08x\n", n, nParts, hr);
pIncomingPart->Release();
pIncomingParts->Release();
return hr;
}
pIncomingPart->Release();
}

pIncomingParts->Release();

return S_OK;
}

HRESULT DisplayVolume(IAudioVolumeLevel *pVolume, int iTabLevel) {
HRESULT hr = S_OK;
UINT nChannels = 0;

hr = pVolume->GetChannelCount(&nChannels);

if (FAILED(hr)) {
printf("GetChannelCount failed: hr = %08x\n", hr);
return hr;
}

for (UINT n = 0; n < nChannels; n++) {
float fMinLevelDB, fMaxLevelDB, fStepping, fLevelDB;

hr = pVolume->GetLevelRange(n, &fMinLevelDB, &fMaxLevelDB, &fStepping);
if (FAILED(hr)) {
printf("GetLevelRange failed: hr = 0x%08x\n", hr);
return hr;
}

hr = pVolume->GetLevel(n, &fLevelDB);
if (FAILED(hr)) {
printf("GetLevel failed: hr = 0x%08x\n", hr);
return hr;
}

Tab(iTabLevel);
printf(
"Channel %u volume is %.3f dB (range is %.3f dB to %.3f dB in increments of %.3f dB)\n",
n, fLevelDB, fMinLevelDB, fMaxLevelDB, fStepping
);
}

return S_OK;
}

HRESULT DisplayMute(IAudioMute *pMute, int iTabLevel) {
HRESULT hr = S_OK;
BOOL bMuted = FALSE;

hr = pMute->GetMute(&bMuted);

if (FAILED(hr)) {
printf("GetMute failed: hr = 0x%08x\n", hr);
return hr;
}

Tab(iTabLevel);
printf("Mute node: %s\n", bMuted "MUTED" : "NOT MUTED");

return S_OK;
}

void Tab(int iTabLevel) {
if (0 >= iTabLevel) { return; }
printf("\t");
Tab(iTabLevel - 1);
}






Re: Vista Pro-Audio Application Development Enumeration of available Levels for a specific installed sound card

Maurits

Another option is to set your app to run in XP compat mode... right-click the .exe, Properties, Compatibility, check "Run this program in compatibility mode for:", choose "Windows XP (Service Pack 2)

Then all the hardware-mixer stuff works as it did in XP.