If the bundle ID passed to the function (a CACFString) was wrapping a
null CFStringRef, one of the comparison operator functions of CACFString
would pass null to CFStringCompare.
Similar to the one in macOS's Volume menu extra.
I'm mainly adding it so we can increase the output volume when the user
sets an app volume above 50%. Currently, setting an app volume above 50%
(the default) risks clipping, so it doesn't make sense to do so unless
your main output volume is at its max.
The volume slider added in this commit will make it clear to the user
that their main output volume is also increasing.
The other app volumes won't change, so in the ideal case the user
wouldn't need to be aware that their output device's volume is being
changed. But they might play audio to the device directly and would
expect it to play at the same volume as before they changed the app
volume.
Fix the workaround for apps whose bundle IDs don't match their CoreAudio
clients. BGMBackgroundMusicDevice::ResponsibleBundleIDsOf was always
returning an empty list.
Also fix over-releasing the app's bundle ID CFString in
BGMAVM_VolumeSlider::appVolumeChanged and
BGMAVM_PanSlider::appPanPositionChanged.
Both bugs were introduced two commits ago in
e05acde351.
There's still some code left that should be moved into the new class,
BGMBackgroundMusicDevice, but I think this is most of it.
This also helps reduce/contain the code that has to be aware of the
second instance of BGMDevice, which handles UI-related audio.
On macOS, apps are supposed to play UI-related sounds using the "system
default" device. This commit creates a new instance of BGM_Driver, which
BGMApp sets as the system default device. BGMApp ignores audio played to
that device when deciding whether to pause/unpause the user's music
player.
Since UI sounds are short, this helps avoid pausing the music player and
unpausing it shortly after.
Some apps have different bundle IDs to their CoreAudio clients, so
the bundle ID BGMApp sends with the app's volume doesn't match any
client in BGMDriver.
This change hardcodes the bundle IDs used by some popular apps and the
bundle IDs their clients use. We should be able to fix this for all
(almost all?) apps at some point.
It turns out that the HAL will sometimes call BGM_Driver::StartIO before
sending kAudioDevicePropertyDeviceIsRunning to BGMPlayThrough. In that
case, BGMApp would start playthrough and tell BGMDriver to return from
StartIO immediately, which meant we would drop the initial frames while
the output device started up.
So now BGMApp waits for the output device in that case as well.
Fixes#7.
The non-interactive version can be called by a Homebrew Cask formula.
Also, change some AppleScript to reference applications by their IDs
rather than their names, which should make them slightly more robust.
Also,
- add Background Music Device.driver to the array of bundles in
pkgbuild.plist, and
- don't warn about the permissions of the install dir for BGMXPCHelper
in build_and_install.sh if it's only building.
BGM_Device::Device_SetPropertyData was releasing the CFArray it gets
from the host (i.e. coreaudiod) when BGMApp sets
kAudioDeviceCustomPropertyEnabledOutputControls, which would deallocate
it, but coreaudiod also releases that CFArray.
Found with AddressSanitizer.
As a safety check, uninstall.sh refuses to delete a file if it's over a
certain size. With debug symbols, Background Music.app was just over the
previous 5MB limit.
BGMApp now disables BGMDevice's volume and/or mute controls if the
output device selected in BGMApp doesn't have matching controls. This
prevents the controls from being presented to the user when they don't
do anything.
In BGMPlayThrough, wait much longer for our IOProcs to stop themselves
before assuming something's gone wrong. In testing, rapidly changing
between output devices with and without controls while playing audio
would occasionally cause one of the IOProcs to take too long to stop
itself.
Also adds some basic scriptability, mainly so UI tests can use
AppleScript to check BGMApp's state that would be complicated to check
otherwise. (In this case, to check which output device is selected.)
Fixes#101.
If the Xcode command line tools were set to use a "command line tools
instance", which can be installed without having Xcode installed,
build_and_install.sh would fail. It prints an error message with a
command that can fix it if you do have Xcode installed, but the message
was kind of confusing and the command would fail if you didn't run it as
root.
build_and_install.sh now offers to run the command for you and then
continues the installation. I've also tried to make the message a bit
clearer and cleaned up some of the code.
Also fixes another bug that occurred with this configuration problem,
where the error from xcodebuild would be printed at an unintended (and
confusing) point in the script.
Fixes#108.
The Applescript that opened System Preferences at the end of the process
was failing to find the "Sound" pane. It might have been because I don't
have OS X set to English, but it's always worked for me in the past.
Either way, it's less fragile to use the ID and it fixes the problem (on
my machine, at least).
Also, added a short pause before restarting coreaudiod because the step
before that makes Finder to play a short sound. It probably wouldn't
cause any problems, but why risk it?
Skipping them by overriding runTest didn't work and this is the only other way
I can think of. xcodebuild's -skip-testing option would work, but only with
recent versions of Xcode.
The UI tests run with clean user defaults, but BGMDevice and Scripting Bridge
still need to be mocked/stubbed out. That also means that the UI tests can only
run if BGMDriver is installed and that changes to BGMDriver's state made during
the tests will persist.