ZigiZ
ZigiZ

Reputation: 2520

How to check if the system master volume is mute or unmute?

I'm using this code to mute/unmute system master volume:

const
  APPCOMMAND_VOLUME_MUTE = $80000;
  WM_APPCOMMAND = $319;

procedure TForm1.Button1Click(Sender: TObject);
begin
  // toggle mute/unmute
  SendMessageW(Handle, WM_APPCOMMAND, Handle, APPCOMMAND_VOLUME_MUTE);
end;

(Got the code from https://stackoverflow.com/a/154128/1140885)

It works fine on XP (Didn't test it on Win7 yet).
I need a method to check (get) what is the current "mute" state? Is it mute or not.
Any ideas?


Update: For XP I ended up using the code from here: How to get the master volume in windows xp? (Thanks to @Sertac Akyuz)

I had to change only a single line:

mxlc.dwControlType := MIXERCONTROL_CONTROLTYPE_VOLUME;

to:

mxlc.dwControlType := MIXERCONTROL_CONTROLTYPE_MUTE;

return value is either 0 (not mute) or 1 (mute).

Upvotes: 6

Views: 5056

Answers (3)

Pasha.A
Pasha.A

Reputation: 73

Use this snipped, I've tested it and works for me.
This will check and set master volume. (Copied from http://www.swissdelphicenter.ch/torry/showcode.php?id=1630)
I hope this helps:

 uses
      MMSystem;

    function GetMasterMute(
      Mixer: hMixerObj;
      var Control: TMixerControl): MMResult;
      // Returns True on success
    var
      Line: TMixerLine;
      Controls: TMixerLineControls;
    begin
      ZeroMemory(@Line, SizeOf(Line));
      Line.cbStruct := SizeOf(Line);
      Line.dwComponentType := MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
      Result := mixerGetLineInfo(Mixer, @Line,
        MIXER_GETLINEINFOF_COMPONENTTYPE);
      if Result = MMSYSERR_NOERROR then
      begin
        ZeroMemory(@Controls, SizeOf(Controls));
        Controls.cbStruct := SizeOf(Controls);
        Controls.dwLineID := Line.dwLineID;
        Controls.cControls := 1;
        Controls.dwControlType := MIXERCONTROL_CONTROLTYPE_MUTE;
        Controls.cbmxctrl := SizeOf(Control);
        Controls.pamxctrl := @Control;
        Result := mixerGetLineControls(Mixer, @Controls,
          MIXER_GETLINECONTROLSF_ONEBYTYPE);
      end;
    end;

    procedure SetMasterMuteValue(
      Mixer: hMixerObj;
      Value: Boolean);
    var
      MasterMute: TMixerControl;
      Details: TMixerControlDetails;
      BoolDetails: TMixerControlDetailsBoolean;
      Code: MMResult;
    begin
      Code := GetMasterMute(0, MasterMute);
      if Code = MMSYSERR_NOERROR then
      begin
        with Details do
        begin
          cbStruct := SizeOf(Details);
          dwControlID := MasterMute.dwControlID;
          cChannels := 1;
          cMultipleItems := 0;
          cbDetails := SizeOf(BoolDetails);
          paDetails := @BoolDetails;
        end;
        LongBool(BoolDetails.fValue) := Value;
        Code := mixerSetControlDetails(0, @Details,
    MIXER_SETCONTROLDETAILSF_VALUE);
      end;
      if Code <> MMSYSERR_NOERROR then
        raise Exception.CreateFmt('SetMasterMuteValue failure, '+
          'multimedia system error #%d', [Code]);
    end;

    // Example:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      SetMasterMuteValue(0, CheckBox1.Checked); // Mixer device #0 mute on/off
    end;

Upvotes: 2

ToKa
ToKa

Reputation: 98

The GetMute method's parameter should be BOOL rather than Boolean. Likewise for SetMute. –

Well, that is to say.. Yes and no.. Yes A Delphi BOOL (actually a LongBool) can store a C-BOOL. No, because it can't be used to write to a C-BOOL property. You will get a 0x80070057 = "Wrong Parameter" result.

The simple reason is that In Delphi True means "everything but 0" and represents -1. A C-BOOL True, however represents 1 and only 1.

So, using a LongBool doesn't work and you should use a workaround using an INT, LongInt, Integer or your own proper defined "BOOL" to avoid "Wrong Parameter" results.

Here an example (that works in Delphi XE7 and tested with SDK version 10.0.10586.15:

// Workaround for BOOL
type TcBOOL = (cFalse = Integer(0),
               cTrue = Integer(1));

// IAudioEndpointVolume
function SetMute(bMute: BOOL; pguidEventContext: PGUID): HRESULT; stdcall;
function GetMute(out pbMute: BOOL): HRESULT; stdcall;


// Client functions
function TMfpMixer.GetMute(): TcBOOL;
var
  hr: HResult;
  Res: BOOL;

begin
  hr:= FAudioEndpoint.GetMute(Res);

  if FAILED(hr) then
    OleCheck(hr);

  Result:= TcBOOL(Res);
end;

//
procedure TMfpMixer.SetMute(Value: TcBOOL);
var
  hr: HResult;

begin
  // This is a workaround on the Delphi BOOL issue. 
  hr:= FAudioEndpoint.SetMute(BOOL(Value),
                              Nil);
  OleCheck(hr);
end;

Upvotes: 1

RRUZ
RRUZ

Reputation: 136451

Starting with Windows Vista you must use the Core Audio SDK to control the Windows audio. To check if the master volume is muted you must use the IAudioEndpointVolume.GetMute method.

Try this sample code

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  ActiveX,
  ComObj;

const
  CLASS_IMMDeviceEnumerator : TGUID = '{BCDE0395-E52F-467C-8E3D-C4579291692E}';
  IID_IMMDeviceEnumerator : TGUID = '{A95664D2-9614-4F35-A746-DE8DB63617E6}';
  IID_IAudioEndpointVolume : TGUID = '{5CDF2C82-841E-4546-9722-0CF74078229A}';

type
  IAudioEndpointVolumeCallback = interface(IUnknown)
  ['{657804FA-D6AD-4496-8A60-352752AF4F89}']
  end;

  IAudioEndpointVolume = interface(IUnknown)
    ['{5CDF2C82-841E-4546-9722-0CF74078229A}']
    function RegisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
    function UnregisterControlChangeNotify(AudioEndPtVol: IAudioEndpointVolumeCallback): HRESULT; stdcall;
    function GetChannelCount(out PInteger): HRESULT; stdcall;
    function SetMasterVolumeLevel(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
    function SetMasterVolumeLevelScalar(fLevelDB: single; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetMasterVolumeLevel(out fLevelDB: single): HRESULT; stdcall;
    function GetMasterVolumeLevelScaler(out fLevelDB: single): HRESULT; stdcall;
    function SetChannelVolumeLevel(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
    function SetChannelVolumeLevelScalar(nChannel: Integer; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetChannelVolumeLevel(nChannel: Integer; out fLevelDB: double): HRESULT; stdcall;
    function GetChannelVolumeLevelScalar(nChannel: Integer; out fLevel: double): HRESULT; stdcall;
    function SetMute(bMute: Boolean; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetMute(out bMute: Boolean): HRESULT; stdcall;
    function GetVolumeStepInfo(pnStep: Integer; out pnStepCount: Integer): HRESULT; stdcall;
    function VolumeStepUp(pguidEventContext: PGUID): HRESULT; stdcall;
    function VolumeStepDown(pguidEventContext: PGUID): HRESULT; stdcall;
    function QueryHardwareSupport(out pdwHardwareSupportMask): HRESULT; stdcall;
    function GetVolumeRange(out pflVolumeMindB: double; out pflVolumeMaxdB: double; out pflVolumeIncrementdB: double): HRESULT; stdcall;
  end;

  IAudioMeterInformation = interface(IUnknown)
  ['{C02216F6-8C67-4B5B-9D00-D008E73E0064}']
  end;

  IPropertyStore = interface(IUnknown)
  end;

  IMMDevice = interface(IUnknown)
  ['{D666063F-1587-4E43-81F1-B948E807363F}']
    function Activate(const refId: TGUID; dwClsCtx: DWORD;  pActivationParams: PInteger; out pEndpointVolume: IAudioEndpointVolume): HRESULT; stdCall;
    function OpenPropertyStore(stgmAccess: DWORD; out ppProperties: IPropertyStore): HRESULT; stdcall;
    function GetId(out ppstrId: PLPWSTR): HRESULT; stdcall;
    function GetState(out State: Integer): HRESULT; stdcall;
  end;


  IMMDeviceCollection = interface(IUnknown)
  ['{0BD7A1BE-7A1A-44DB-8397-CC5392387B5E}']
  end;

  IMMNotificationClient = interface(IUnknown)
  ['{7991EEC9-7E89-4D85-8390-6C703CEC60C0}']
  end;

  IMMDeviceEnumerator = interface(IUnknown)
  ['{A95664D2-9614-4F35-A746-DE8DB63617E6}']
    function EnumAudioEndpoints(dataFlow: TOleEnum; deviceState: SYSUINT; DevCollection: IMMDeviceCollection): HRESULT; stdcall;
    function GetDefaultAudioEndpoint(EDF: SYSUINT; ER: SYSUINT; out Dev :IMMDevice ): HRESULT; stdcall;
    function GetDevice(pwstrId: pointer; out Dev: IMMDevice): HRESULT; stdcall;
    function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
  end;

function IsMasterVolumeMute : Boolean;
var
  pEndpointVolume: IAudioEndpointVolume;
  LDeviceEnumerator: IMMDeviceEnumerator;
  Dev: IMMDevice;
  bMute: Boolean;
begin
  if not Succeeded(CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, LDeviceEnumerator)) then
   RaiseLastOSError;
  if not Succeeded(LDeviceEnumerator.GetDefaultAudioEndpoint($00000000, $00000000, Dev)) then
   RaiseLastOSError;

  if not Succeeded( Dev.Activate(IID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, nil, pEndpointVolume)) then
   RaiseLastOSError;

  if not Succeeded(pEndpointVolume.GetMute(bMute)) then
   RaiseLastOSError
  else
  Result:=bMute;
end;


begin
 try
    CoInitialize(nil);
    try
      Writeln(Format('Master Volume is Mute ? : %s',[BoolToStr(IsMasterVolumeMute, True)]));
    finally
      CoUninitialize;
    end;
 except
    on E:Exception do
        Writeln(E.Classname, ':', E.Message);
 end;
 Writeln('Press Enter to exit');
 Readln;
end.

Upvotes: 13

Related Questions