Skip to content

Instantly share code, notes, and snippets.

@adaskar
Created December 20, 2020 15:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adaskar/a3e0d891e5dbe76d57d04952d72bfb4d to your computer and use it in GitHub Desktop.
Save adaskar/a3e0d891e5dbe76d57d04952d72bfb4d to your computer and use it in GitHub Desktop.
//
// main.swift
// AggregateAudioVolumeControl
//
// Created by Gurhan Polat on 20.12.2020.
//
import Foundation
import Cocoa
import AVFoundation
// @mode = 0 get
// @mode = 1 set n
var mode:Int = -1
var volumeToBeSet:Float = 0.0
// get current volume
if (CommandLine.arguments.count == 2 &&
CommandLine.arguments[1] == "get") {
mode = 0
}
// set current volume
else if (CommandLine.arguments.count == 3 &&
CommandLine.arguments[1] == "set" &&
Int32(CommandLine.arguments[2]) != nil &&
Int32(CommandLine.arguments[2])! >= 0 && Int32(CommandLine.arguments[2])! <= 100) {
mode = 1
volumeToBeSet = Float(Int32(CommandLine.arguments[2])!) / Float(100)
}
else {
print("usage: AggregateAudioVolumeControl <get | set n>")
exit(-1)
}
// get default audio output device
var address:AudioObjectPropertyAddress = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioHardwarePropertyDefaultOutputDevice),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var audioDeviceID:AudioDeviceID = 0
var propsize:UInt32 = UInt32(MemoryLayout<AudioDeviceID>.size)
var result = AudioObjectGetPropertyData(AudioDeviceID(kAudioObjectSystemObject), &address, 0, nil, &propsize, &audioDeviceID)
if (result != 0) {
print("kAudioHardwarePropertyDefaultOutputDevice")
exit(-1)
}
// get default audio output device uid
address = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyDeviceUID),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var audioDeviceUID:CFString? = nil
propsize = UInt32(MemoryLayout<CFString?>.size)
result = AudioObjectGetPropertyData(audioDeviceID, &address, 0, nil, &propsize, &audioDeviceUID)
if (result != 0) {
print("kAudioDevicePropertyDeviceUID")
exit(-1)
}
// get default audio output device transport type
address = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyTransportType),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var transportType:UInt32 = 0
propsize = UInt32(MemoryLayout<UInt32>.size)
result = AudioObjectGetPropertyData(audioDeviceID, &address, 0, nil, &propsize, &transportType)
if (result != 0) {
print("kAudioDevicePropertyTransportType")
exit(-1)
}
// if transportType is not Aggregate then exit the tool
if (transportType != kAudioDeviceTransportTypeAggregate) {
print("audioDeviceID: \(audioDeviceID) uid: \(audioDeviceUID as String? ?? "") transportType: \(transportType == kAudioDeviceTransportTypeAggregate)")
print("this tool only works with a kAudioDeviceTransportTypeAggregate")
exit(1)
}
// get the sublist of the Aggregate Audio Device
address = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioAggregateDevicePropertyActiveSubDeviceList),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var subDevicesID = [AudioDeviceID]()
for _ in 0..<32 {
subDevicesID.append(AudioDeviceID())
}
propsize = UInt32(MemoryLayout<AudioDeviceID>.size * 32)
result = AudioObjectGetPropertyData(audioDeviceID, &address, 0, nil, &propsize, &subDevicesID)
if (result != 0) {
print("kAudioAggregateDevicePropertyActiveSubDeviceList")
exit(-1)
}
var volAvg: Float = 0.0
var subDeviceCount:Int = Int((propsize / UInt32(MemoryLayout<AudioDeviceID>.size)))
// for eah sub device
// we either set the volume or just get it according to mode argument
print("audioDeviceUID: \(audioDeviceUID as String? ?? "") subDeviceCount: \(subDeviceCount)")
for i in 0..<subDeviceCount {
let subDevice:AudioDeviceID = subDevicesID[i]
var volLeft:Float = 0.0
var volRight:Float = 0.0
address = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyDeviceUID),
mScope:AudioObjectPropertyScope(kAudioObjectPropertyScopeGlobal),
mElement:AudioObjectPropertyElement(kAudioObjectPropertyElementMaster))
var subDeviceUID:CFString? = nil
propsize = UInt32(MemoryLayout<CFString?>.size)
result = AudioObjectGetPropertyData(subDevice, &address, 0, nil, &propsize, &subDeviceUID)
if (result != 0) {
print("kAudioDevicePropertyDeviceUID subDevice")
exit(-1)
}
address = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyVolumeScalar),
mScope:AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput),
mElement:1)
propsize = UInt32(MemoryLayout<Float>.size)
// if mode is get, get the volume of left channel otherwise set it to parameter n
if (mode == 0) {
result = AudioObjectGetPropertyData(subDevice, &address, 0, nil, &propsize, &volLeft)
}
else {
result = AudioObjectSetPropertyData(subDevice, &address, 0, nil, propsize, &volumeToBeSet)
volLeft = volumeToBeSet
}
if (result != 0) {
print("kAudioDevicePropertyVolumeScalar volLeft")
exit(-1)
}
address = AudioObjectPropertyAddress(
mSelector:AudioObjectPropertySelector(kAudioDevicePropertyVolumeScalar),
mScope:AudioObjectPropertyScope(kAudioDevicePropertyScopeOutput),
mElement:2)
propsize = UInt32(MemoryLayout<Float>.size)
// if mode is get, get the volume of right channel otherwise set it to parameter n
if (mode == 0) {
result = AudioObjectGetPropertyData(subDevice, &address, 0, nil, &propsize, &volRight)
}
else {
result = AudioObjectSetPropertyData(subDevice, &address, 0, nil, propsize, &volumeToBeSet)
volRight = volumeToBeSet
}
if (result != 0) {
print("kAudioDevicePropertyVolumeScalar volRight")
exit(-1)
}
volAvg += (volLeft + volRight) / 2
print("subDevice: \(subDeviceUID as String? ?? "") volLeft: \(volLeft) volLeft: \(volRight)")
}
volAvg = volAvg / Float(subDeviceCount)
print("volAvg: \(volAvg)")
// we always return current volume of the sound if no error
// if any error occurs we then return (-1):255
exit(Int32(volAvg * 100))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment