Switch Audio Source

From this source: http://apple.stackexchange.com/questions/142388/get-the-sound-output-device-name-with-applescript

Motivation: Quickly switch to headset, and back, so I can use handoff, and other VOIP applications.

3 Likes

An additional approach, if you don't want to install SwitchAudioSource, and can run JavaScript for Applications (Yosemite onwards) would be a macro like this:

(Edit the names of the two variables to strings which distinguish the two devices which you are toggling - in my case Internal and USB – there is no need to give the full name of the device)

Toggle two SOUND OUTPUT devices.kmmacros (20.0 KB)

(function (strDeviceA, strDeviceB) {
	'use strict';

	var e = Application("System Preferences"),
		b = !1,
		d;
	e.panes["com.apple.preference.sound"].anchors.output.reveal();
	var a = Application("System Events").processes["System Preferences"].windows[
			0].tabGroups[0].scrollAreas[0].tables[0].rows,
		c = a.where({
			selected: !0
		}),
		c = c.length ? c[0].textFields[0].value() : null;
	if (!c) return null;
	var f = -1 !== c.indexOf(strDeviceA) ? strDeviceB : strDeviceA;
	a().forEach(function (a) {
		b || (d = a.textFields[0].value(), -1 !== d.indexOf(f) &&
			(a.select(), b = !0));
	});
	a = Application.currentApplication();
	a.includeStandardAdditions = !0;
	a.activate();
	a.displayNotification(b ? "->  " + d :
		"Couldn't find a sound device containing this string: \"" + f + '"', {
			withTitle: "Sound Output Device " + (b ? "" : "NOT ") + "Changed"
		});
	e.quit();
	return b;


})(kmVar('soundDeviceONE').value(), kmVar('soundDeviceTWO').value());

function kmVar(k) {
	var kme = Application("Keyboard Maestro Engine"),
		vs = kme.variables,
		vks = vs.where({
			name: k
		});

	return vks.length ? vks[0] :
		vs.push(kme.Variable({
			'name': k
		})) && vs[k];
}
4 Likes

Hey Rob, just curious and trying to learn.
Why did you use !0 instead of true ?

Good question – I was originally using it in a shell script and pruned the code by passing it through https://closure-compiler.appspot.com/home

Closure goes for that abbreviation because in web contexts script download times are a bottle-neck, and it grabs every possible opportunity for abbreviation. In a KM script it really makes no sense – reduces readability, and just adds one additional operation to the evaluation.

( Closure does, however sometimes spot some useful optimisations - it’s an impressive piece of work )

(I don’t usually use it because it also strips out meaningful variable names, and replaces them with single lower case alphabetic characters)

1 Like

OK, thanks for the explanation.

It seems to me that Closure is over-optimizing the code.
I can't imagine that saving 2 char for the occasional use of true would really matter. And on top of that, aren't scripts normally cached by the browser, so they are only downloaded on the first occurrence?

In my System Preferences (System10.11.5), the normal device setting for my computer, an iMac, is “Internal Speakers”, type: “Built-in” (all without quote marks).

But the System Preferences automatically changes that to “Headphones”, type: “Headphone port” (all without quote marks) when I have my headphones plugged in, which is most of the time. So I’ve adjusted the first variable in my version of your macro to “Headphone port” to compensate.

I’d prefer not to keep track of whether the headphones are plugged in before running the macro due to lack of attentive brain. Is there any way to account within the macro for whether I have my headphones plugged in or not?

Or would I need two separate macros and use one or the other depending on whether I’ve got the headphones in (or not).

Thanks.

1 Like

Hey Peter,

Please be specific about what you’re switching between.

My impression is:

Toggle item A:

“Internal Speakers”/“Headphone port”

Toggle item B:

Something else.

If I’m right what’s the something else. If I’m wrong please explain.

-Chris

My impression is:

Toggle item A:

“Internal Speakers”/“Headphone port”

Toggle item B: ?

Thanks for asking, ccstone.

In your format, toggle item A can be one of either:...

“Internal Speakers" or "Headphones”

... in my System Preferences, depending on whether the headphones are physically plugged in or not. The System automatically detects whether they are plugged in, and changes its Preferences text accordingly, and...

Toggle item B: "C_Media USB Audio Device"

The wording of toggle item B doesn't change, unless the device itself isn't plugged into a USB port, in which case your code alerts me to the fact it's not there.

In your code, which works fine as is, if the headphones happen to be plugged in, then the Preferences wording changes and the code fails.

After reading your KM variable settings steps and your code script that follows (I know little about the coding), I was thinking what was needed was that KM, maybe, should do an IF-THEN-ELSE test before invoking your script, something like:

IF the System Preferences is set to "Internal Speakers" OR "Headphones" THEN switch to "C_Media USB Audio Device"

But I don't know how to query the System, by changing your KM steps or code, about the state of the Preferences's current setting, whether it's “Internal Speakers” or "Headphones". I'm guessing that KM should do the IF-Then test and branch accordingly so that it sets up your script properly. But maybe it works better if the script wording is adjusted in some way.

Hey Peter,

That's @ComplexPoint's code rather than mine.  :slight_smile:

I suggest you download and install this command-line tool:

AudioSwitcher.zip (107.1 KB)

Install it here:

/usr/local/bin/

Open a window in the Finder and type G

Paste the path string in the resulting dialog, and hit the GO button.

Drag the executable from the archive into that folder – you will be required to enter your password to do so.

Then you can run this macro and avoid having to mess with System Events.

Toggle Audio Output.kmmacros (2.5 KB)

Although the display in the Sound Preferences changes to reflect whether headphones are connected the actual underlying “Built-in Output” doesn't change, so the shell script doesn't need to check for headphones.

If the macro doesn't work out-of-the-box then the wording for C_Media USB Audio Device may be off.

You can check by switching to that output and then running this in the Terminal.app:

AudioSwitcher -c

The output should look something like this:

C_Media USB Audio Device (output)

If that differs from what I have in the shell script then you'll need to change it:

Remove (output) from the end of what you got from the terminal and replace the relevant string in the shell script in the macro.

All of this may seem a bit intimidating, but it's really simple to get done step-by-step.

Message me if you need help.

TESTED: on OSX 10.11.5 with Keyboard Maestro 7.2

-Chris

2 Likes

The link to AudioSwitcher.zip leads to : Oops! That page doesn’t exist or is private.

Hey Dimo,

That's odd. I'm looking into it.

In the meantime the direct link works – but ONLY if you paste it into your browser's address field – NOT if you click the link in the forum. (Tested only with Safari.)

Strange...

https://forum.keyboardmaestro.com/uploads/default/original/2X/3/3d208a21186eb96aefbc9f6c2ff093296c12722d.zip

-Chris

Another approach I found is based on the fact that if “Sound” appears in the System menu bar, you can hold the option key and click on the Sound icon. That displays all connected output options, including airplay speakers.

I created the following script - which would need to be adjusted based on where the icon appears on a specific monitor, and where the selected output item appears in the sub menu.

Holding the option key - Move and Click without dragging (click on Sound icon)
Duplicate the step and change the down position by a small number and test if you have hit the item. In my case, the number was 50 to hit the fourth item in the list.

Not overly elegant - but it works well. One point - if this is in the middle of other steps - put a Pause in front and after the two macro steps.

1 Like

Hey Folks,

I fixed the download link in post #11.

-Chris

Chris

Thanks for all of this - I followed your instructions and got it working fine. Can you, or someone, help me with 2 questions, as I’m not fluent completely at home with coding, even though the terms are explained in the AudioSwitch documentation.

Firstly, what would the code look like if I wanted it to alter the input device rather than the output device? The default is output, so that changed with F1, but my input isn’t changing. I would ultimately like to change both, so I’m assuming just make a second code and have it run too. I know that -t refers to type, and since the default is output, we don’t see it in the code. So how do you alter the code to work for input?

Secondly, I’d actually like to run these codes triggered by plugging in the usb device. I sort of got that working by changing the trigger, but it seems to go through three steps: Switched to USB, to internal, back to USB. I suspect that’s because the mac is responding anyway when I plug the device in, and that the way around it is to have the KM code simply always run in the one direction (IF) and not have an alternative (ELSE) letting KM do the work. Am I right? Or is this coming across as confusing?

Thanks for any help.

Phil

Hey Phil.

This will change the sound input to the source named in the variable “inputName”.

# REQUIRES: AudioSwitcher Unix Command Line Tool

inputName='Soundflower (2ch)'

export PATH=/opt/local/bin:/opt/local/sbin:/usr/local/bin:$PATH
currentInput=$(AudioSwitcher -t input -c)

if [ ! "$currentInput" = "$inputName" ]; then

   AudioSwitcher -t input -s "$inputName"
   
fi

You can get the available help for AudioSwitcher by inputting the following in the Terminal:

AudioSwitcher -h

This is one-way only.

As for the USB-trigger you’re on your own. I haven’t gone beyond briefly testing them.

-Chris

Chris. Thanks again. Brilliant and I seriously wouldn’t have been able to do it on my own. Because it’s a one-way script, I duplicated it and substituted output for input, so it works exactly as I hoped. Awesome.

1 Like

What would your AppleScript look like if I want it to select a specific audio output, not toggle between two different ones?

Thanks!

You have in mind the JavaScript for Automation in the post below (post 2 of this thread) ?

The variable f gets the name-string of the device to be chosen:

var f = -1 !== c.indexOf(strDeviceA) ? strDeviceB : strDeviceA;

So you should be able to comment out that line, and replace it with something on the pattern of:

var f = "nameStringOfMyPreferredDevice";

where "nameStringOfMyPreferredDevice" is replaced by the real (quoted) name of the device.

That is exactly what I meant! Thanks for a quick reply.

Could you please show me the full script?

I’m still new with programming :wink: