BGMApp has to set BGMDevice, and often also the Null Device for a short
time, as the systemwide default audio device, which makes CoreAudio put
them in the preferred devices list in its Plist file. And since the list
is limited to three devices, it only gives us one or two usable ones.
Ideally, CoreAudio just wouldn't add our devices to its list, but I
don't think we can prevent that.
As a partial workaround, we now store our own copy of the preferred
devices list without our devices, which BGMApp can use to figure out
which devices were pushed out of CoreAudio's list by our devices.
This doesn't fix the problem entirely because our devices still take up
room in CoreAudio's list when BGMApp is closed, but I think that would
be harder to solve.
See #167.
Also:
- Handle setting the initial output device in BGMPreferredOutputDevices
instead of BGMAudioDeviceManager.
- Fix a crash in BGMOutputVolumeMenuItem::dealloc caused by using
dispatch_sync to dispatch to the main queue while running on the main
queue.
- Fix a crash in BGMPreferredOutputDevices if
/Library/Preferences/Audio/com.apple.audio.SystemSettings.plist
doesn't exist.
- Add Swinsian to the list of music players in the README. (I must have
forgotten to do that when I added support for it.)
When the user chooses a different output device in BGMApp, the new
device is now added to the front of the list of preferred devices. This
stops BGMPreferredOutputDevices changing the output device back shortly
afterward when it gets a device connection/disconnection notification,
which is sent because BGMDriver's Null Device is enabled and then
disabled as part of changing the output device.
It also means BGMApp will now account for the times the output device
has been changed since BGMApp started when deciding whether to change to
a newly connected device and deciding which device to change to when the
current output device is removed.
BGMDeviceControlsList: Set some members to null before they've been
lazily initialised.
BGM_TaskQueue: Fix the destructor possibly throwing.
BGM_Device and BGM_NullDevice: Fix integer division when calculating the
host clock frequency.
BGM_Utils: Fix the C++ utility function used to explicitly cast
__nullable values to __nonnull. (Was previously unused.)
System sounds are UI-related sounds like mail notifications or terminal
bells.
Xcode 9.2 doesn't support saving .xib files in Xcode 7 format any more,
so building Background Music now requires Xcode 8 or above.
Also, fix some of the tooltips that would only work if BGMApp was the
foreground app, which it shouldn't be.
This is mostly so BGMApp won't leave BGMDevice as the default if BGMApp
crashes, which would stop audio from playing until the user changed the
default device themselves. Also handles SIGINT, SIGTERM and SIGQUIT.
For crashes where the BGMApp process may be in an unknown state, e.g.
segfaults, BGMXPCHelper handles changing the default device.
Should fix the Travis Xcode 9 build, which is currently failing because
the AppleScript we use to quit BGMApp in .travis.yml gets "user
cancelled" for some reason.
Also makes some minor improvements to the reports generated by
CrashReporter. The way CrashReporter works with Background Music should
otherwise be unchanged.
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.
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.
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.
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.
Also add centre tick marks and "L"/"R" (left/right) labels to them.
The idea is to eventually include extra controls like an equalizer, recording
apps, hiding/ignoring apps, routing apps, etc.
Also, remove the left margin from the App Volumes menu items. Even macOS isn't
consistent about including that margin, as far as I can tell.
BGM_Device::StartIO blocks on
BGMAudioDeviceManager::waitForOutputDeviceToStart, which could be blocked by
HAL requests that the HAL wouldn't return until BGM_Device::StartIO returned.
Also:
- Replace BGMPlayThrough's move constructor with a SetDevices function for
simplicity.
- Pause/abort debug builds if an error is logged.
Also, default to only aborting debug builds when they log and swallow an
exception if the exception was unexpected. That is, the developer didn't
realise the code could throw.
The names of the data source(s) for a device are generally the names
intended to be shown to the user, since the OS X volume menu, System
Preferences, etc. use them.
A menu item is now added for each data source of each output device,
rather than one per device.
Also adds some macros/functions for casting values to __nonnull.
Resolves#59.
And other reliability improvements. Mostly in BGMPlayThrough and the
classes that use it. Trying to catch C++ exceptions as early as possible
in the Objective-C++ code and, if necessary, convert them to NSErrors.
More errors are logged in release builds now, which will hopefully help
with debugging issues the developers can't reproduce themselves.
It seems that BGMDriver was failing to compile on case-sensitive file
systems because BGM_Types.h included "AudioServerPlugin.h" instead of
"AudioServerPlugIn.h". (Lowercase "i".)
I tried building with the project and Xcode on a case-sensitive disk
image and it would fail without this patch. So I figure it should at
least build now. I haven't had time to test Background Music on a system
running on a case-insensitive file system yet, so I added a TODO about
it in TODO.md.
Also, some unrelated tidying up.
The BGMApp project now builds an XPC service bundle called BGMXPCHelper,
which vends a Mach service that BGMApp and BGMDriver can use to
communicate. This will hopefully be useful for some of the tasks HAL
notifications aren't suited to.
In this commit, BGMDriver uses the XPC helper when starting IO, to wait
until BGMApp is ready for playthrough. BGMApp can only start playthrough
when the output hardware is ready for IO. BGMDriver can now tell the HAL
when we're ready for IO, which means we don't have to keep the output
hardware running all the time (or drop frames or increase latency).
The end result is that playthrough doesn't waste CPU time while idle any
more. This also means that now playthrough won't prevent the system from
sleeping when idle.