Version 1 of Windows: volume control

Updated 2016-12-16 03:31:55 by bll

Windows: volume control

bll 2016-12-15 This Tcl stub might be useful to someone. I was using a stand-alone program (and still need to for 32-bit and Windows XP). The 32-bit .dll is not linking properly, don't know what is going on with that.

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
# 
# Run within Msys2

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

winvolume.cpp

/* much of the original volume code from: https://gist.github.com/rdp/8363580
 */

#include <tcl.h>
#include <string.h>
#include <windows.h>
#include <commctrl.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <stdio.h>
#include <math.h>       /* log */

#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 winvolumeObjCmd (
  ClientData cd,
  Tcl_Interp* interp,
  int objc,
  Tcl_Obj * const objv[]
  )
{
  IAudioEndpointVolume  *g_pEndptVol = NULL;
  HRESULT               hr = S_OK;
  IMMDeviceEnumerator   *pEnumerator = NULL;
  IMMDevice             *pDevice = NULL;
  OSVERSIONINFO         VersionInfo;
  float                 currentVal;
  int                   vol;
  int                   rc;

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

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

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

  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 (objc == 2 && vol != -1) {
    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)
  Tcl_SetObjResult (interp, Tcl_NewIntObj ((int) round(100 * currentVal)));

Exit:
  SAFE_RELEASE(pEnumerator)
  SAFE_RELEASE(pDevice)
  SAFE_RELEASE(g_pEndptVol)
  CoUninitialize();
  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;
}

} /* extern C */