Mac OS X: volume control edit
2018-5-18 : Now available on SourceForge: https://sourceforge.net/projects/volume-controls-cmdline/files/
bll 2017-2-16: The Swift version does not work with tclkits due to incorrect linkage with the tclStub library.Example Usage
load [file join [pwd] macvolume[info sharedlibextension]] set vol [macvolume] ; # get the volume incr vol -10 set newvol [macvolume $vol] ; # set the volume, always returns the current volume.
Objective-C Version
mkvol.sh#!/bin/bash
set -x
lpath=/Users/bll/tcl/build/tcl/Deployment
ipath=/Users/bll/tcl/build/tcl/Tcl.framework/Versions/8.6/Headers
tcllib=libtclstub8.6.a
tcllibnm=tclstub8.6
clang \
-mmacosx-version-min=10.9 \
-framework Cocoa \
-framework AudioToolbox \
-o macvolume \
main.m volume.m
clang \
-mmacosx-version-min=10.9 \
-framework Cocoa \
-framework AudioToolbox \
-shared -fPIC \
-o macvolume.dylib \
-DUSE_TCL_STUBS \
-I$ipath \
volume.m tclmacvol.m \
-L$lpath -l$tcllibnmvolume.m#import "AudioToolbox/AudioServices.h"
#import "Foundation/NSObject.h"
#include <stdio.h>
#include <stdlib.h>
#include <MacTypes.h>
/*
* has objective-c code to get all output devices...
* http://stackoverflow.com/questions/11347989/get-built-in-output-from-core-audio
*/
int
macvolume_cmd (int set, int vol)
{
AudioDeviceID outputDeviceID;
UInt32 outputDeviceIDSize = sizeof (outputDeviceID);
OSStatus status;
AudioObjectPropertyAddress propertyAOPA;
Float32 volume;
UInt32 volumeSize = sizeof (volume);
int ivol;
propertyAOPA.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
propertyAOPA.mScope = kAudioObjectPropertyScopeGlobal;
propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
status = AudioHardwareServiceGetPropertyData(
kAudioObjectSystemObject,
&propertyAOPA,
0,
NULL,
&outputDeviceIDSize,
&outputDeviceID);
if (outputDeviceID == kAudioObjectUnknown) {
return 0;
}
propertyAOPA.mSelector = kAudioHardwareServiceDeviceProperty_VirtualMasterVolume;
propertyAOPA.mScope = kAudioDevicePropertyScopeOutput;
propertyAOPA.mElement = kAudioObjectPropertyElementMaster;
if (set == 1) {
volume = (Float32) vol / 100.0;
AudioHardwareServiceSetPropertyData(
outputDeviceID,
&propertyAOPA,
0,
NULL,
volumeSize,
&volume);
}
AudioHardwareServiceGetPropertyData(
outputDeviceID,
&propertyAOPA,
0,
NULL,
&volumeSize,
&volume);
ivol = (int) round(volume*100.0);
return ivol;
}main.m#include <stdio.h>
#include <stdlib.h>
extern int macvolume_cmd (int, int);
int
main (int argc, const char * argv[])
{
int set = 0;
int vol = 0;
if (argc > 1) {
set = 1;
vol = atoi (argv[1]);
}
printf ("%d\n", macvolume_cmd(set, vol));
}tclmacvol.m#include <stdlib.h>
#include <tcl.h>
extern int macvolume_cmd (int, int);
int
Macvolume_Cmd (
ClientData cd,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[] )
{
int vol = 0;
int rvol;
int rc = 0;
int set = 0;
if (objc != 1 && objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, NULL);
return TCL_ERROR;
}
if (objc == 2) {
rc = Tcl_GetIntFromObj (interp, objv[1], &vol);
if (rc != TCL_OK) {
return TCL_ERROR;
}
set = 1;
}
rvol = macvolume_cmd (set, vol);
Tcl_SetObjResult(interp, Tcl_NewIntObj(rvol));
return TCL_OK;
}
int
Macvolume_Init (Tcl_Interp *interp)
{
if (! Tcl_InitStubs(interp, TCL_VERSION, 0)) {
return TCL_ERROR;
}
Tcl_CreateObjCommand (interp, "macvolume", Macvolume_Cmd, NULL, NULL);
Tcl_PkgProvide (interp, "macvolume", "0.1");
return TCL_OK;
}Swift Version
This version does not work with a tclkit due to incorrect linkage with the stub library.mkvol.sh#!/bin/bash
set -x
lpath=/Users/bll/tcl/build/tcl/Deployment
ipath=/Users/bll/tcl/build/tcl/Tcl.framework/Versions/8.6/Headers
tcllib=libtclstub8.6.a
tcllibnm=tclstub8.6
swiftc -O -o macvolume volume.swift main.swift
swiftc -O -emit-library \
-Xcc -DUSE_TCL_STUBS \
-I$ipath -import-objc-header tclbridge.h \
volume.swift tclmacvol.swift \
-Xlinker -undefined -Xlinker dynamic_lookup \
-Xlinker -L$lpath \
-l$tcllibnm \
-o macvolume.dylibvolume.swiftimport AudioToolbox
func macvolume_cmd (set: Int32, vol: Int32) -> Int32
{
var defaultOutputDeviceID = AudioDeviceID(0)
var defaultOutputDeviceIDSize = UInt32(MemoryLayout.size(ofValue:defaultOutputDeviceID))
var getDefaultOutputDevicePropertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice),
mScope: AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
AudioObjectGetPropertyData(
AudioObjectID(kAudioObjectSystemObject),
&getDefaultOutputDevicePropertyAddress,
0,
nil,
&defaultOutputDeviceIDSize,
&defaultOutputDeviceID)
var volume = Float32()
var volumeSize = UInt32(MemoryLayout.size(ofValue:volume))
var volumePropertyAddress = AudioObjectPropertyAddress(
mSelector: AudioObjectPropertySelector(kAudioHardwareServiceDeviceProperty_VirtualMasterVolume),
mScope: AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput),
mElement: AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
if (set == 1) {
volume = Float(vol) / 100.0
AudioHardwareServiceSetPropertyData(
defaultOutputDeviceID,
&volumePropertyAddress,
0,
nil,
volumeSize,
&volume)
}
AudioHardwareServiceGetPropertyData(
defaultOutputDeviceID,
&volumePropertyAddress,
0,
nil,
&volumeSize,
&volume)
let ivol = Int32(round(volume*100.0))
return ivol
}main.swiftimport Foundation
var vol = Int32(0)
var set = Int32(0)
if (CommandLine.argc > 1) {
vol = Int32((CommandLine.arguments[1] as NSString).integerValue)
set = 1
}
var rvol = Int32()
rvol = macvolume_cmd (set:set, vol:vol)
print ("\(rvol)");tclbridge.h#include "tcl.h"tclmacvol.swift
func Macvolume_Cmd(cdata: ClientData?,
interp: UnsafeMutablePointer<Tcl_Interp>?,
objc: Int32,
objv: UnsafePointer<UnsafeMutablePointer<Tcl_Obj>?>?) -> Int32
{
var vol = Int32(0)
var rc = Int32(0)
var set = Int32(0)
if (objc != 1 && objc != 2) {
Tcl_WrongNumArgs(interp, 1, objv, nil)
return TCL_ERROR
}
if (objc == 2) {
rc = Tcl_GetIntFromObj (interp, objv![1], &vol);
if (rc == 0) { } // clear compiler warning
set = 1
}
var rvol = Int32()link problems.
rvol = macvolume_cmd (set:set, vol:vol)
Tcl_SetObjResult(interp, Tcl_NewIntObj(rvol))
return TCL_OK
}
@_cdecl("Macvolume_Init")
public func Macvolume_Init(
interp: UnsafeMutablePointer<Tcl_Interp>) -> Int32
{
if Tcl_InitStubs(interp, TCL_VERSION, 0) == nil {
return TCL_ERROR;
}
Tcl_CreateObjCommand(interp, "::macvolume", Macvolume_Cmd, nil, nil);
Tcl_PkgProvide(interp, "macvolume", "0.1");
return TCL_OK
}

