0

Used code example from How to check if the system master volume is mute or unmute? with some changes.

function GetChannelVolumeLevel(nChannel: Integer; out fLevelDB: double): HRESULT; stdcall;
function GetChannelVolumeLevelScalar(nChannel: Integer; out fLevel: double): HRESULT; stdcall;

fLevelDB from double to single. With double result always 0. And for

function SetMute(bMute: Boolean; pguidEventContext: PGUID): HRESULT; stdcall;
function GetMute(out bMute: Boolean): HRESULT; stdcall;

From Boolean to Integer (0-false 1-true)

Can't get SetChannelVolumeLevelScalar/SetChannelVolumeLevel to work and this function get me into this mess in a first place.

Demo code

unit MasterVolumeControl;
interface
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}';
  DEVICE_STATE_ACTIVE              = $00000001;
  DEVICE_STATE_UNPLUGGED           = $00000002;
  DEVICE_STATE_NOTPRESENT          = $00000004;
  DEVICE_STATEMASK_ALL             = $00000007;

type
  EDataFlow = TOleEnum;

const
  eRender                          = $00000000;
  eCapture                         = $00000001;
  eAll                             = $00000002;
  EDataFlow_enum_count             = $00000003;

type
  ERole = TOleEnum;

const
  eConsole                         = $00000000;
  eMultimedia                      = $00000001;
  eCommunications                  = $00000002;
  ERole_enum_count                 = $00000003;

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: uint; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
    function SetChannelVolumeLevelScalar(nChannel: uint; fLevelDB: double; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetChannelVolumeLevel(nChannel: Integer; out fLevelDB: double): HRESULT; stdcall;
    function GetChannelVolumeLevelScalar(nChannel: Integer; out fLevel: single): HRESULT; stdcall;
    function SetMute(bMute: Integer; pguidEventContext: PGUID): HRESULT; stdcall;
    function GetMute(out bMute: Integer): 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: EDataFlow; ER: SYSUINT; out Dev: IMMDevice): HRESULT; stdcall;
    function GetDevice(pwstrId: pointer; out Dev: IMMDevice): HRESULT; stdcall;
    function RegisterEndpointNotificationCallback(pClient: IMMNotificationClient): HRESULT; stdcall;
  end;

var
  pEndpointVolume  : IAudioEndpointVolume;
  LDeviceEnumerator: IMMDeviceEnumerator;
  Dev              : IMMDevice;
procedure SetMasterVolume(fLevelDB: single);
function GetMasterVolume(): single;
function GetNumberOfChannels: Integer;
function GetChannelVolume(Channel: DWORD): single;
procedure SetVolumeChannel(fLevelDB: single; Channel: uint);
procedure SetMute(mute: Boolean);
function GetMute(): Boolean;

implementation


procedure SetMasterVolume(fLevelDB: single);
begin
  if not Succeeded(pEndpointVolume.SetMasterVolumeLevelScalar(fLevelDB, @GUID_NULL)) then
    RaiseLastOSError;
end;

function GetMasterVolume(): single;
begin
  if not Succeeded(pEndpointVolume.GetMasterVolumeLevelScaler(result)) then
    RaiseLastOSError;
end;

function GetChannelVolume(Channel: DWORD): single;
begin
  if not Succeeded(pEndpointVolume.GetChannelVolumeLevelScalar(Channel, result)) then
    RaiseLastOSError;
end;

procedure SetVolumeChannel(fLevelDB: single; Channel: uint);
begin
  if not Succeeded(pEndpointVolume.SetChannelVolumeLevelScalar(Channel, fLevelDB, @GUID_NULL)) then
    RaiseLastOSError;
end;

procedure SetMute(mute: Boolean);
var
  iMute: Integer;
begin
  iMute := 0;
  if mute then
    iMute := 1;
  if not Succeeded(pEndpointVolume.SetMute(iMute, nil)) then
    RaiseLastOSError;
end;

function GetMute(): Boolean;
var
  mute: Integer;
begin
  if not Succeeded(pEndpointVolume.GetMute(mute)) then
    RaiseLastOSError;
  result := true;
  if mute = 0 then
    result := false;
end;

function GetNumberOfChannels: Integer;
begin
  if not Succeeded(pEndpointVolume.GetChannelCount(result)) then
    RaiseLastOSError;
end;

initialization

if not Succeeded(CoCreateInstance(CLASS_IMMDeviceEnumerator, nil, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, LDeviceEnumerator)) then
  RaiseLastOSError;
if not Succeeded(LDeviceEnumerator.GetDefaultAudioEndpoint(eRender, eConsole, Dev)) then
  RaiseLastOSError;
if not Succeeded(Dev.Activate(IID_IAudioEndpointVolume, CLSCTX_INPROC_SERVER, nil, pEndpointVolume)) then
  RaiseLastOSError;

end.

And form:

unit uVolumeControlExample;

interface

uses
  MasterVolumeControl,
  Vcl.ComCtrls,
  System.Classes,
  Vcl.Controls,
  Vcl.StdCtrls,
  Vcl.Forms,
  sysutils;

type
  TForm8 = class(TForm)
    chkMute: TCheckBox;
    masterVolume: TTrackBar;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure chkMuteClick(Sender: TObject);
    procedure FormResize(Sender: TObject);

    private
      noOfChannels: integer;
      channels    : TStringList;
      procedure TrackBarChange(Sender: TObject);
      procedure RefreshChannels;

    public
    { Public declarations }
  end;

var
  Form8: TForm8;

implementation

{$R *.dfm}

procedure TForm8.chkMuteClick(Sender: TObject);
begin
  SetMute(chkMute.Checked);
end;

procedure TForm8.FormCreate(Sender: TObject);
var
  I: integer;
begin
  channels := TStringList.Create;
  noOfChannels := GetNumberOfChannels;
  for I := 0 to noOfChannels - 1 do begin
    channels.AddObject('Channel_' + IntToStr(I), TTrackBar.Create(self));
    with TTrackBar(channels.Objects[I]) do begin
      Visible := false;
      Parent := self;
      Tag := I;
      Left := 8;
      Top := 8 + I * 45;
      Width := 345;
      Height := 45;
      OnChange := TrackBarChange;
      Max := 100;
      Name := 'Channel_' + IntToStr(I);
      Visible := true;
    end;
  end;
  with masterVolume do begin
    Visible := false;
    Tag := noOfChannels;
    Left := 8;
    Top := 8 + noOfChannels * 45;
    Width := 345;
    Height := 45;
    OnChange := TrackBarChange;
    Max := 100;
    Visible := true;
  end;
  RefreshChannels;
end;

procedure TForm8.FormDestroy(Sender: TObject);
begin
  channels.Free;
end;

procedure TForm8.FormResize(Sender: TObject);
begin
  RefreshChannels;
end;

procedure TForm8.RefreshChannels;
var
  I: integer;
begin
  for I := 0 to noOfChannels - 1 do begin
    with TTrackBar(channels.Objects[I]) do begin
      OnChange := nil;
      Position := round(GetChannelVolume(I) * 100);
      OnChange := TrackBarChange;
    end;
  end;
  if GetMute then
    chkMute.Checked := true
  else
    chkMute.Checked := false;
  masterVolume.OnChange := nil;
  masterVolume.Position := round(GetMasterVolume() * 100);
  masterVolume.OnChange := TrackBarChange;
end;

procedure TForm8.TrackBarChange(Sender: TObject);
begin
  if TTrackBar(Sender).Tag = noOfChannels then begin
    SetMasterVolume(TTrackBar(Sender).Position / 100);
    exit;
  end;
  SetVolumeChannel(TTrackBar(Sender).Position / 100, TTrackBar(Sender).Tag);
end;

end.
Community
  • 1
  • 1
Yuriy
  • 43
  • 1
  • 1
  • 7

1 Answers1

3

The obvious problem with your code is that you have used Double for the floating point arguments when in fact they are Single. You don't get to decide what types to use there. This is a binary interface and the decision has been made by the author of the interface. Change those argument types back to Single.

As for the GetMute method, its parameter should be BOOL rather than Boolean or Integer.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490