Updated 2017-02-08 03:32:45 by bll

Windows: volume control edit

bll 2016-12-15 This Tcl stub might be useful to someone.

bll 2016-12-19 Updated so that it can be built as either a stand-alone program or as a .dll.
load [file join [pwd] winvolume64[info sharedlibextension]]
set vol [winvolume] ; # get the volume
incr vol -10
set newvol [winvolume $vol] ; # set the volume, always returns the current volume.

winmkvol.sh
#!/bin/bash

case $MSYSTEM in
  *32)
    g++ -m32 -static-libgcc -UWIN_TCL_INTERFACE -o winvolume32.exe \
        -I$HOME/local-32/include winvolume.cpp \
        -L$HOME/local-32/lib -lole32
    g++ -m32 -shared -static-libgcc -DWIN_TCL_INTERFACE -o winvolume32.dll \
        -I$HOME/local-32/include -DUSE_TCL_STUBS winvolume.cpp \
        -L$HOME/local-32/lib -ltclstub86 -lole32
    ;;
  *64)
    g++ -m64 -static-libgcc -UWIN_TCL_INTERFACE -o winvolume64.exe \
        -I$HOME/local-64/include winvolume.cpp \
        -L$HOME/local-64/lib -lole32
    g++ -m64 -shared -static-libgcc -DWIN_TCL_INTERFACE -o winvolume64.dll \
        -I$HOME/local-64/include -DUSE_TCL_STUBS winvolume.cpp \
        -L$HOME/local-64/lib -ltclstub86 -lole32
    ;;
esac

winvolume.cpp
/*
 * Copyright 2016 Brad Lanam Walnut Creek, CA US
 * This code is in the public domain.
 *
 * much of the original volume code from: https://gist.github.com/rdp/8363580
 */

#ifdef WIN_TCL_INTERFACE
# include <tcl.h>
#endif
#include <string.h>
#include <windows.h>
#include <commctrl.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <stdio.h>
#include <math.h>

#define EXIT_ON_ERROR(hr)  \
    if (FAILED(hr)) { printf ("error %d occurred\n", -hr); goto Exit; }

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

extern "C" {

int
process (int set, int vol)
{
  IAudioEndpointVolume  *g_pEndptVol = NULL;
  HRESULT               hr = S_OK;
  IMMDeviceEnumerator   *pEnumerator = NULL;
  IMMDevice             *pDevice = NULL;
  OSVERSIONINFO         VersionInfo;
  float                 currentVal;
  int                   rc;

  ZeroMemory(&VersionInfo, sizeof(OSVERSIONINFO));
  VersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  GetVersionEx(&VersionInfo);
  if (VersionInfo.dwMajorVersion <= 5) {
    return -1;
  }

  CoInitialize(NULL);

  // Get enumerator for audio endpoint devices.
  hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
      NULL, CLSCTX_INPROC_SERVER,
      __uuidof(IMMDeviceEnumerator),
      (void**)&pEnumerator);
  EXIT_ON_ERROR(hr)

  // Get default audio-rendering device.
  hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
  EXIT_ON_ERROR(hr)

  hr = pDevice->Activate(__uuidof(IAudioEndpointVolume),
      CLSCTX_ALL, NULL, (void**)&g_pEndptVol);
  EXIT_ON_ERROR(hr)

  if (set) {
    float got = (float) vol / 100.0; // needs to be within 1.0 to 0.0
    hr = g_pEndptVol->SetMasterVolumeLevelScalar (got, NULL);
    EXIT_ON_ERROR(hr)
  }
  hr = g_pEndptVol->GetMasterVolumeLevelScalar (&currentVal);
  EXIT_ON_ERROR(hr)
  vol = (int) round(100 * currentVal);

Exit:
  SAFE_RELEASE(pEnumerator)
  SAFE_RELEASE(pDevice)
  SAFE_RELEASE(g_pEndptVol)
  CoUninitialize();
  return vol;
}

#ifndef WIN_TCL_INTERFACE

int
main (int argc, char *argv[])
{
  int  set;
  int  vol;

  set = 0;
  if (argc > 1) {
    set = 1;
    vol = atoi (argv[1]);
  }
  vol = process (set, vol);
  printf ("%d\n", vol);
  fflush (stdout);
  return 0;
}

#endif

#ifdef WIN_TCL_INTERFACE

int winvolumeObjCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  int                   set;
  int                   vol;
  int                   rc;

  if (objc != 1 && objc != 2) {
    Tcl_WrongNumArgs (interp, 1, objv, "?volume?");
    return TCL_ERROR;
  }

  set = 0;
  vol = -1;
  if (objc == 2) {
    rc = Tcl_GetIntFromObj (interp, objv[1], &vol);
    if (rc != TCL_OK) {
      return TCL_ERROR;
    }
    set = 1;
  }

  vol = process (set, vol);
  if (vol < 0) {
    return TCL_ERROR;
  }

  Tcl_SetObjResult (interp, Tcl_NewIntObj (vol));
  return TCL_OK;
}

int
Winvolume_Init (Tcl_Interp *interp)
{
  Tcl_Encoding utf;
#ifdef USE_TCL_STUBS
  if (!Tcl_InitStubs(interp,"8.3",0)) {
    return TCL_ERROR;
  }
#else
  if (!Tcl_PkgRequire(interp,"Tcl","8.3",0)) {
    return TCL_ERROR;
  }
#endif
  Tcl_CreateObjCommand(interp,"winvolume", winvolumeObjCmd, (ClientData) NULL, NULL);
  Tcl_PkgProvide(interp,"winvolume","0.1");

  return TCL_OK;
}

#endif

} /* extern C */