none
How to enable the speaker from a C/C++ program?

    Question

  • Please, is there some way to write a C/C++ program to enable (or disable) the speaker in Windows Vista and 7?

    I have written an application which uses the computer speaker to play songs (with the words displayed in slides on the screen) and it runs on a PC which is shared by many people within our voluntary organisation. 

    This particular application is operated by retired volunteers who have had 50-year productive careers as housewives, welders and carpenters but have never before used computers.  They use this application via a remote control by pressing the forward and back buttons to change slides, and pressing the forward button twice to start the music, which they can do successfully even if they didn't bring their reading glasses.  They really enjoy doing this task and our organisation is most appreciative of their contributions.

    My problem is that some other computer-literate volunteers (or their teenage children) who use the PC for other purposes mess around with the Control Panel and audio devices and are liable to disable the speaker.  It's a huge ask for our retired volunteers to know that if no sound comes out of the speakers, they should go into the Control Panel, select Sound and Playback, and then re-enable the speaker, especially if they didn't bring their reading glasses with them.  Our retired volunteers are not regular customers of the Digital Guru bookshop!

    I'm quite happy for my application to put things back the way they were before it exits, although the IMMDevice::GetState method doesn't help by reporting a disabled speaker as not present (rather than as disabled).

    Any suggestions would be most appreciated, thank you.

    Tuesday, July 06, 2010 10:06 AM

Answers

  • I should have added to my previous post that if you are running a 32-bit program on a 64-bit operating system, you need to be be aware that the registry key used is in the 64-bit tree, so the RegOpenKeyEx fourth parameter needs to be something like

    KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_64KEY

    as otherwise a 32-bit program will look in the 32-bit tree and not find it.

     

    David Bellair

    • Marked as answer by DavidB3084 Wednesday, July 14, 2010 6:10 AM
    Tuesday, July 13, 2010 8:35 AM

All replies

  • That's a tricky one.

    If it was a question of muting/unmuting the speakers, no problem; IAudioEndpointVolume::GetMute / SetMute would work.

    But there's intentionally no programmatic way to re-enable an audio device that the user has disabled via the Sound control panel.  The most your app can do is guide the user to re-enable the device themselves.

    > IMMDevice::GetState [reports] a disabled speaker as not present

    mmdeviceapi.h:

    #define DEVICE_STATE_ACTIVE      0x00000001
    #define DEVICE_STATE_DISABLED    0x00000002
    #define DEVICE_STATE_NOTPRESENT  0x00000004
    #define DEVICE_STATE_UNPLUGGED   0x00000008
    #define DEVICE_STATEMASK_ALL     0x0000000f

    Are you saying that IMMDevice::GetState() on a disabled endpoint is giving back DEVICE_STATE_NOTPRESENT?  I'm not seeing that on my Windows 7 box.


    Matthew van Eerde
    Tuesday, July 06, 2010 3:49 PM
    Moderator
  • Hi Matthew

    Many thanks for your reply.  As you say, there is no problem with the mute (or volume control also).

    If Microsoft has a policy of not allowing applications to change the endpoint device state, all I can ask is that it please reconsiders that policy in light of my own situation.  It really is unreasonable to ask our volunteers to use the Control Panel to re-enable the speakers (especially when they have forgotten their reading glasses).

    Could Microsoft provide a "sandbox" in which changes made to endpoint device states are automatically undone when the application terminates?

    Thanks for your advice that IMMDevice::GetSpace does return the correct state for a disabled endpoint under Windows 7.  My comment applied to Windows Vista (when I supplied the statemask DEVICE_STATEMASK_ALL | 0x10000000 to the IMMDeviceEnumerator::EnumAudioEndpoints call in order for it to return disabled endpoints).

    I'm currently exploring a couple of other approaches which I won't mention on the forum, but thank you for at least understanding my issue.

    David Bellair

     

    Tuesday, July 06, 2010 11:46 PM
  • I could not agree more. I beg you at MS to expose an API so we are able to embed (some) of the "Audio Devices" functionality inside the application we write. The sandbox idea would be great of course.

    David Bellair has given an extreme example of this need, but many other users, semi computer-literate, will be happy to avoid this complexity.

    Wednesday, July 07, 2010 5:30 AM
  • For the record, I've found a way for a program to enable/disable the speaker in Windows Vista and 7, which might be useful to other developers.

    Windows Vista and 7 store data on the state and properties of the speakers in the registry, using the key

    HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Render\{guid for speaker}

    and the speaker state is stored as a 32-bit number in its value named DeviceState.  The GUID is (I think) created when the sound device driver is installed so it should always be obtained using the function IMMDeviceEnumerator::EnumAudioEndpoints in case a new driver has been installed.  Note that for Windows Vista the device statemask parameter must include the bit 0x10000000 (otherwise it does not return endpoint devices which are disabled).  These registry values are not the same as the values returned by the function IMMDevice::GetState, and it appears that the following values are used:

    Conditions                         Registry value                    Vista GetState        Windows 7 GetState

    Sound driver disabled
    and speaker enabled          0x00000004                               4                              4

    Sound driver disabled
    and speaker disabled          0x10000004                              4                              4

    Speaker unplugged
    and speaker enabled           0x00000008                             8                               8

    Speaker unplugged
    and speaker disabled           0x10000008                             4                              2

    Speaker plugged in
    and speaker enabled            0x00000001                            1                              1

    Speaker plugged in
    and speaker disabled            0x10000001                           4                              2

    Note that "Sound driver disabled" means that the Sound Controller in Device Manager has been disabled on its Driver tab, whereas "speaker enabled/disabled" means enabling/disabling the speakers on the  Playback tab of Sound on the Control Panel.

    Reading this registry value avoids the Windows Vista error where GetState returns 4 when it should return 2.

    Also, writing the 0x10000000 bit to 0 or 1 forces enabling or disabling the speakers.

    I know this is a dirty solution, but as Microsoft appears to have a deliberate policy of not providing a clean solution, I have no choice.  It's even dirtier considering the steps one must go through to create a new user group which can write this registry key, adding users to that group, and then changing the registry permission to allow that user group to write to just that key.  It also greatly complicates the installation of my application, which currently just involves copying files without touching the registry.

    One clean solution would be to provide new functions IMMDevice::GetEnabled and IMMDevice::SetEnabled to get reliable enabled/disabled values, and to set the enabled/disabled state.  Considering that the Core Audio API already has functions to mute the speakers and to set their volume to minimum, it doesn't seem any more dangerous to enable/disable the speakers.  I'm just an unwilling newbie to all this audio stuff, but I'm sure Microsoft could think of something which provided the functionality and also complied with all their standards and conventions.

    I'm sorry not to go into all the gory details of the registry stuff, but I don't think this is the place.

    David Bellair

     

     

     

    Tuesday, July 13, 2010 8:27 AM
  • I should have added to my previous post that if you are running a 32-bit program on a 64-bit operating system, you need to be be aware that the registry key used is in the 64-bit tree, so the RegOpenKeyEx fourth parameter needs to be something like

    KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_WOW64_64KEY

    as otherwise a 32-bit program will look in the 32-bit tree and not find it.

     

    David Bellair

    • Marked as answer by DavidB3084 Wednesday, July 14, 2010 6:10 AM
    Tuesday, July 13, 2010 8:35 AM