2018-5-18 : Now available on SourceForge: https://sourceforge.net/projects/volume-controls-cmdline/files/
bll 2016-12-19 Finding the code to handle Windows XP volume was a bit difficult. Builds a stand-alone executable or loadable Tcl stub.
load [file join [pwd] winxpvolume[info sharedlibextension]] set vol [winxpvolume] ; # get the volume incr vol -10 set newvol [winxpvolume $vol] ; # set the volume, always returns the current volume.
winxpmkvol.sh
#!/bin/bash gcc -m32 -static-libgcc -UWINXP_TCL_INTERFACE -o winxpvolume.exe \ -I$HOME/local-32/include -DUSE_TCL_STUBS winxpvolume.c \ -L$HOME/local-32/lib -lwinmm gcc -m32 -shared -static-libgcc -DWINXP_TCL_INTERFACE -o winxpvolume.dll \ -I$HOME/local-32/include -DUSE_TCL_STUBS winxpvolume.c \ -L$HOME/local-32/lib -ltclstub86 -lwinmm
winxpvolume.c
/* * Copyright 2016 Brad Lanam Walnut Creek CA US * MIT License * * Reference: * https://www.codeproject.com/kb/audio-video/mixersetcontroldetails.aspx?display=printall&fid=16481&df=90&mpp=25&noise=3&sort=position&view=quick&select=2765918 */ #ifdef WINXP_TCL_INTERFACE # include <tcl.h> #endif #include <stdio.h> #include <stdlib.h> #include <math.h> #include <windows.h> int process (int set, int vol) { MMRESULT result; HMIXER hMixer; result = mixerOpen(&hMixer, MIXER_OBJECTF_MIXER, 0, 0, 0); MIXERLINE ml = {0}; ml.cbStruct = sizeof(MIXERLINE); ml.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS; result = mixerGetLineInfo((HMIXEROBJ) hMixer, &ml, MIXER_GETLINEINFOF_COMPONENTTYPE); MIXERLINECONTROLS mlc = {0}; MIXERCONTROL mc = {0}; mlc.cbStruct = sizeof(MIXERLINECONTROLS); mlc.dwLineID = ml.dwLineID; mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; mlc.cControls = 1; mlc.pamxctrl = &mc; mlc.cbmxctrl = sizeof(MIXERCONTROL); result = mixerGetLineControls((HMIXEROBJ) hMixer, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE); if (set) { vol = round ((double) vol / 100.0 * 65535.0); MIXERCONTROLDETAILS mcd = {0}; MIXERCONTROLDETAILS_UNSIGNED mcdu = {0}; mcdu.dwValue = vol; mcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mcd.hwndOwner = 0; mcd.dwControlID = mc.dwControlID; mcd.paDetails = &mcdu; mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); mcd.cChannels = 1; result = mixerSetControlDetails((HMIXEROBJ) hMixer, &mcd, MIXER_SETCONTROLDETAILSF_VALUE); } MIXERCONTROLDETAILS mcd = {0}; MIXERCONTROLDETAILS_UNSIGNED mcdu = {0}; mcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mcd.hwndOwner = 0; mcd.dwControlID = mc.dwControlID; mcd.paDetails = &mcdu; mcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); mcd.cChannels = 1; result = mixerGetControlDetails((HMIXEROBJ) hMixer, &mcd, MIXER_SETCONTROLDETAILSF_VALUE); vol = (int) round ((double) mcdu.dwValue / 65535.0 * 100.0); return vol; } #ifndef WINXP_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 WINXP_TCL_INTERFACE int winxpvolumeObjCmd ( 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, "?vol?"); return TCL_ERROR; } set = 0; if (objc == 2) { rc = Tcl_GetIntFromObj(interp, objv[1], &vol); if (rc != TCL_OK) { return TCL_ERROR; } set = 1; } vol = process (set, vol); Tcl_SetObjResult (interp, Tcl_NewIntObj (vol)); return TCL_OK; } int Winxpvolume_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,"winxpvolume", winxpvolumeObjCmd, (ClientData) NULL, NULL); Tcl_PkgProvide(interp,"winxpvolume","0.1"); return TCL_OK; } #endif /* WINXP_TCL_INTERFACE */