Compare commits

...

176 Commits

Author SHA1 Message Date
Kyle Neideck 3d4e1bff10 Merge pull request #785 from khipp/update-homebrew
Update Homebrew installation instructions
2024-12-23 00:11:29 +11:00
Klaus Hipp 1c743053c6 Update Homebrew installation instructions 2024-12-22 14:04:59 +01:00
Kyle Neideck de1a9fa413 Merge pull request #781 from johzzy/master
feat: BGMAppDelegate.h forward declaring @class BGMAudioDeviceManager and @class BGMAppVolumesController
2024-12-19 20:56:34 +11:00
Johnny 12a7deb15f feat: BGMAppDelegate.h forward declaring @class BGMAudioDeviceManager and @class BGMAppVolumesController 2024-12-18 18:18:20 +08:00
Kyle Neideck e6375b0919 Merge pull request #779 from johzzy/master
update c++11 lambda for CAPropertyAddressList
2024-12-08 20:10:46 +11:00
Johnny 029cb2f5f8 remove part of #pragma clang diagnostic ignored "-Wdeprecated". 2024-12-08 15:20:40 +08:00
Johnny 2ac8a1afd1 update c++11 lambda for CAPropertyAddressList 2024-12-07 09:45:23 +08:00
Kyle Neideck 784bb249a9 Merge pull request #778 from johzzy/master
fix crash
2024-12-07 09:47:09 +11:00
Johnny 51e1266532 fix crash 2024-12-06 18:30:51 +08:00
Kyle Neideck 4b7caeeb54 Merge pull request #777 from johzzy/master
fix crash in BGMPlayThrough::IsRunningSomewhereOtherThanBGMApp
2024-12-06 21:07:47 +11:00
Johnny 7e592ebb2b fix crash in BGMPlayThrough::IsRunningSomewhereOtherThanBGMApp 2024-12-06 11:32:25 +08:00
Kyle Neideck 0aea54f0c5 Merge pull request #776 from johzzy/master
format log about "BGMDevice is not running somewhere other than BGMApp"
2024-12-06 14:30:31 +11:00
Johnny 9407ee36d2 update format log about "BGMDevice is not running somewhere other than BGMApp" 2024-12-06 10:43:40 +08:00
Kyle Neideck 9058e3c556 Merge pull request #767 from 7Backwards/Fix-volume-icon-on-menu-bar-not-centered
Fix volume icon on menu bar not centered
2024-09-09 19:57:41 +10:00
Gonçalo Neves a8f4ace099 Fix volume icon on menu bar not centered 2024-09-07 21:39:42 +00:00
Kyle Neideck 610f15c556 Merge pull request #741 from manzick/arc-support
Add arc browser support
2024-05-27 01:05:31 +10:00
manzick 62174be95e Add arc browser support 2024-05-26 20:48:17 +06:00
Kyle Neideck 48bdcd1a64 Allow installation when Rosetta 2 isn't installed.
If you didn't have Rosetta 2 installed, the Background Music installer
would tell you that it's required and prompt you to install it.
Background Music doesn't actually need Rosetta 2; macOS just assumes
that it does unless told otherwise.
2024-05-13 13:36:51 +10:00
Kyle Neideck cb6f38821e README.md: Make v0.4.3 the current version. 2024-04-26 15:49:28 +10:00
Kyle Neideck b96fcb7a47 Fix BGMXPCHelper not launching due to Gatekeeper in macOS 14.5.
The previous fix was removing the quarantine attribute from the
BGMXPCHelper.xpc dir, but in 14.5 its contents also get the attribute.
(I haven't tested in earlier versions.) This new fix removes the
attribute from the contents as well.
2024-04-26 09:50:08 +10:00
Kyle Neideck ad14e1b0a4 Fix BGMDevice not immediately removed after uninstall on macOS 14.4.
The uninstall script was failing to restart `coreaudiod`.

`launchctl kickstart -k` is no longer permitted for `coreaudiod`. See
<https://developer.apple.com/documentation/macos-release-notes/macos-14_4-release-notes>
(thanks to @gchilds) and commit
3097f4b621.

Fixes #731.
2024-04-25 09:16:29 +10:00
Kyle Neideck 61be8bead4 Bump the patch version number again.
Also, update the min macOS version in the installer to the min version
actually supported (10.13).
2024-04-25 09:05:03 +10:00
Kyle Neideck 8b2bf56eca Fix Gatekeeper stopping BGMXPCHelper from launching on macOS 14.
Also, build the ListInputDevices tool as a universal binary.

Contributed by modue sp. z o.o..
2024-04-25 08:46:54 +10:00
Kyle Neideck e655d571e0 Fix the About panel not always opening on macOS 14.4+. 2024-04-24 17:55:46 +10:00
Kyle Neideck 9257d50b15 In the About panel, link to the contributors page on GitHub.
Also, bump the patch version number and copyright years. (Should have
done that before tagging v0.4.1, so jump to v0.4.2.)
2024-04-24 13:50:53 +10:00
Kyle Neideck 3097f4b621 Fix installer failing in macOS 14.5.
`launchctl kickstart -k` is no longer permitted for `coreaudiod`. See
<https://developer.apple.com/documentation/macos-release-notes/macos-14_4-release-notes>.
(Thanks to @gchilds.)

As explained in those release notes, `kill` still works. `postinstall`
was using `killall coreaudiod` if `launchctl kickstart -k` failed, but
that was failing as well because it had to be `sudo killall coreaudiod`.

I haven't been able to reproduce this on macOS 14.4, only 14.5. Not sure
why.

See #705.
2024-04-23 15:49:13 +10:00
Kyle Neideck 73f221e423 skip-ui-tests.py: Update the path to Python as macOS dropped Python 2. 2024-03-07 08:42:01 +11:00
Kyle Neideck 7d700b3de5 build_and_install.sh: Work around build failures caused by an Xcode bug.
Fixes #712.
2024-03-07 08:42:00 +11:00
Kyle Neideck 014a6eecc8 Merge pull request #721 from mrbaloghakos/master
Fix Login Items path in README
2024-02-20 22:11:54 +11:00
Ákos Balogh 2cc3faaebb Fix Login Items path in README 2024-02-20 11:08:02 +01:00
Kyle Neideck 5000c64084 Fix build errors in XCode 15.2.
Ignore deprecation warnings in PublicUtility and drop support for macOS
10.12 and earlier.

See #712.
2024-01-23 23:44:31 +11:00
Kyle Neideck 4fc776e4a6 Merge pull request #638 from LawrenceWarren/master
Update references to `System Preferences`
2022-11-27 16:01:20 +11:00
Lawrence Warren ca3c497940 feat(docs): Update references to System Preferences
As of MacOS 13, System Preferences is known as System Settings. This
commit updates all user facing references to System Preferences, using
the new nomenclature.
2022-11-26 12:52:44 +11:00
Kyle Neideck 08837fcdca Merge pull request #622 from dnicolson/fix-menu-offset-big-sur
Fix menu alignment on macOS Big Sur and later
2022-08-31 02:07:09 +10:00
Dave Nicolson 076514c83a Fix menu alignment on macOS Big Sur and later 2022-08-29 06:42:49 +02:00
Kyle Neideck 8090f12204 Merge pull request #614 from Kache/master
Add Run / Configure instructions to README
2022-07-28 13:42:39 +10:00
Kevin C bd38207146 Add Run / Configure instructions to README
And fix whitespace

Fixes: #613
2022-07-27 01:59:19 -07:00
Kyle Neideck d92d9e6d29 README.md: Make v0.4.0 the current version. 2022-06-15 21:25:05 +10:00
Kyle Neideck d048287140 Fix slider knob sizes.
Also,
 - Update copyright years.
 - Drop support for macOS 10.9.
2022-06-12 14:36:59 +10:00
Kyle Neideck c89566d212 Merge branch 'gh-actions' 2022-06-12 13:44:12 +10:00
Kyle Neideck cbb3e20e1c GH Actions: Reenable the release job. 2022-06-11 23:37:15 +10:00
Kyle Neideck 9f9c18748a GH Actions: Re-disable the UI tests. 2022-06-11 20:42:12 +10:00
Kyle Neideck 3040a20766 GH Actions: Try to grant BGMApp mic authorization by modifying TCC.db. 2022-05-19 17:24:33 +10:00
Kyle Neideck 30ec1c24b7 GH Actions: Try to fix mic authorization dialog not closing. 2022-05-15 16:04:31 +10:00
Kyle Neideck cc2516a705 GH Actions: Fix application log gathering command. 2022-05-15 15:08:51 +10:00
Kyle Neideck 909615617a GH Actions: More logging to help diagnose UI test failures. 2022-05-15 14:49:08 +10:00
Kyle Neideck 89eebb0e02 Show an error message if the user denies input device permissions. 2022-05-15 02:20:58 +10:00
Kyle Neideck 058af733be Automatically accept the input device permission dialog in UI tests. 2022-05-15 02:06:35 +10:00
Kyle Neideck ffd634245c GH Actions: Upload the test results. 2022-05-13 21:35:24 +10:00
Kyle Neideck 7afb8a6c12 GH Actions: Re-enable the UI tests.
GitHub Action's macOS image has been updates and UI tests might work
now: <https://github.com/actions/virtual-environments/pull/5417>.

Also, remove some supporting files/code for CI builds that we don't need
anymore.
2022-05-13 20:53:12 +10:00
Kyle Neideck c68e05244c Merge branch 'gh-actions' 2022-05-13 17:20:31 +10:00
Kyle Neideck 22dea1ef5d GH Actions: Fix checking the BGMApp bundle was installed. 2022-05-13 16:50:36 +10:00
Kyle Neideck b2770f1793 Installer: Fix post-install check for BGMDriver failing unnecessarily. 2022-05-13 15:46:53 +10:00
Kyle Neideck a730fb98f8 Installer: Log more audio device info if BGMDevice doesn't appear. 2022-05-13 12:03:33 +10:00
Kyle Neideck 1fdf47cd12 Merge pull request #597 from adrenalin8231/474-edge-helper
add Microsoft Edge to `ResponsibleBundleIDsOf`
2022-04-18 18:33:44 +10:00
adrenalin8231 4608f4bbac add Microsoft Edge to ResponsibleBundleIDsOf 2022-04-18 14:24:29 +09:00
Kyle Neideck 5f1641da8f GH Actions: Skip the UI tests. 2022-04-03 18:00:47 +10:00
Kyle Neideck 2e1fb9db46 GH Actions: Fix the test and package commands. 2022-04-03 17:09:08 +10:00
Kyle Neideck fbf95c0605 GH Actions: Workaround for https://github.com/actions/runner/issues/884 2022-04-02 15:05:33 +11:00
Kyle Neideck ada2615d88 GH Actions: Add a checkout step for the release job.
Also, use `sudo` to run scripts that `sudo` to hopefully keep those
`sudo` commands from failing. They're failing with "sudo: a terminal is
required to read the password[...]".
2022-04-02 13:06:43 +11:00
Kyle Neideck 71c11315e5 GH Actions: Split build/test and release into separate jobs. 2022-04-01 16:36:34 +11:00
Kyle Neideck 4e9d21d499 Fix more errors in the GitHub Actions file. 2022-04-01 12:53:47 +11:00
Kyle Neideck da96132da0 Fix YAML syntax in the GitHub Actions file. 2022-04-01 12:43:02 +11:00
Kyle Neideck d84471c29e Migrate from Travis CI to GitHub Actions. 2022-04-01 12:33:51 +11:00
Kyle Neideck 6ef13ed762 Fix an infinite loop in safe_install_dir.sh.
`pwd -P` was returning `//some/path` and the condition
`[[ "${PWD}" == / ]]` would end up as `[[ // == / ]]` and get stuck.
2022-03-31 15:33:43 +11:00
Kyle Neideck 8ddfb81148 Fix compiler warnings in Xcode 13.2. 2022-03-31 15:29:25 +11:00
Kyle Neideck f4ea06e3c3 Merge pull request #593 from muneebmahmed/master
Remove python 2 usage
2022-03-31 15:20:21 +11:00
Muneeb Ahmed 01217d2d8c Remove python 2 usage 2022-03-30 19:06:35 -07:00
Kyle Neideck 5a048431b2 Merge pull request #588 from Arsenic-ATG/license_patch
Update LICENSE
2022-03-15 13:19:15 +11:00
Arsenic 333aaf93e2 Update LICENSE
Duplicate license file for better discoverability on GitHub and similar sites.
2022-03-13 18:16:58 +05:30
Kyle Neideck 9441a31d9d Merge pull request #579 from Coeur/spelling
spelling
2022-01-23 16:16:12 +11:00
Antoine Cœur f31aebcf51 spelling 2022-01-21 09:35:16 +08:00
Kyle Neideck 3f4b119c86 Merge pull request #578 from JTEgan/master
Fix for issue #475 dropdown arrows move instead of doing something
2022-01-20 20:21:15 +11:00
Jon Egan 3bfd68615f added copyright 2022-01-19 07:16:17 -05:00
Jon Egan 24b248e09d Fix for issue #475 dropdown arrows move instead of doing something 2022-01-19 07:01:13 -05:00
Kyle Neideck 0d5965b266 Merge pull request #485 from BossElijah/fix-grammar-error
Fix grammar error in README.md
2021-05-06 19:27:36 +10:00
BossElijah c84dd5754c Fix grammar error 2021-05-06 11:08:34 +02:00
Kyle Neideck a5b7cd69e3 Update the link to the latest SNAPSHOT in the README.
Also, update the copyright year in the UI and in the bundles.
2021-04-03 18:28:01 +11:00
Kyle Neideck fe786fb338 Merge pull request #464 from marcuswu/fix/issue_457
Fix/issue 459
2021-04-03 00:04:12 +11:00
Marcus Wu 5a5f9d0a8d Use kAppPanNoValue in place of INT_MIN 2021-03-31 14:53:30 -04:00
Marcus Wu 89186d526f Fix comment referencing INT_MAX instead of INT_MIN 2021-03-31 14:45:39 -04:00
Marcus Wu 69fb7f69e3 Fix syntax error 2021-03-31 14:42:42 -04:00
Kyle Neideck b38f6ddcd6 Reduce noise in the CI logs.
CI builds that use Xcode 12.2 are going over Travis CI's log length
limit and getting killed.
2021-03-31 14:31:08 +11:00
Marcus Wu e9e13a3056 Fix problem setting pan to -1 2021-03-30 09:42:50 -04:00
Marcus Wu c7df7506bc Merge pull request #1 from kyleneideck/master
Update from source
2021-03-30 09:36:38 -04:00
Kyle Neideck 169ee92802 Fix test compilation error with Xcode 12.5. 2021-03-30 23:47:57 +11:00
Kyle Neideck 0a87596c1c Merge branch 'master' of https://github.com/kyleneideck/BackgroundMusic 2021-03-30 20:35:41 +11:00
Kyle Neideck 3551e0f6e6 Fix uninstall failing when BGMApp has debug symbols.
If you install with `build_and_install.sh`, it leaves debug symbols in
the `Background Music.app` bundle, which makes it a lot larger. That
would trigger a sanity check in the uninstaller and it would refuse to
delete the bundle.
2021-03-30 20:34:51 +11:00
Kyle Neideck 6d23685084 Merge pull request #463 from thelukasprobst/master
Update the screenshot in the README and make it transparent
2021-03-30 20:22:52 +11:00
Lukas Probst a93ec5ee34 Adjust height of Screenshot.png 2021-03-30 11:09:51 +02:00
Lukas Probst 26d354866e Update the screenshot in the README and make it transparent
In the last screenshot, the part of the menu that lets you change the output device was not visible. I've also added transparent background because the white background didn't look great when you're using GitHub's dark mode. (See Issue #461)
2021-03-30 10:53:24 +02:00
Kyle Neideck 06cf3d8777 Update the macOS versions used in CI. 2021-03-30 19:34:59 +11:00
Kyle Neideck 0e0cc5fd05 Installer: Retry if opening BGMApp fails. 2021-03-30 19:03:09 +11:00
Kyle Neideck 213c339309 Add a bundle ID property for apps in the AppleScript API. 2021-03-25 01:19:46 +11:00
Kyle Neideck c0a556be04 Merge pull request #457 from marcuswu/AppleScript
Apple script additions
2021-03-24 13:08:57 +11:00
Marcus Wu a62b783e5f Fix removed synonym 2021-03-23 13:16:12 -04:00
Marcus Wu 44e3e96986 Update comment based on suggestion
Co-authored-by: Kyle Neideck <kyle@bearisdriving.com>
2021-03-23 12:09:42 -04:00
Marcus Wu d9a22d1b62 Update comment based on suggestion
Co-authored-by: Kyle Neideck <kyle@bearisdriving.com>
2021-03-23 12:09:29 -04:00
Marcus Wu ce56a96829 Updated according to suggestions
Co-authored-by: Kyle Neideck <kyle@bearisdriving.com>
2021-03-22 20:48:41 -04:00
Marcus Wu c10ea9d132 Updated according to suggestions
Co-authored-by: Kyle Neideck <kyle@bearisdriving.com>
2021-03-22 20:48:20 -04:00
Marcus Wu 64003ca1d6 Updated according to suggestions
Co-authored-by: Kyle Neideck <kyle@bearisdriving.com>
2021-03-22 20:47:57 -04:00
Marcus Wu 7df7f062e0 Updated according to suggestions
Co-authored-by: Kyle Neideck <kyle@bearisdriving.com>
2021-03-22 20:47:41 -04:00
Marcus Wu 2090761ae5 Add BGMASApplication to test target 2021-03-19 21:00:39 -04:00
Marcus Wu c303f60343 Fix licensing text in new files 2021-03-19 20:52:04 -04:00
Marcus Wu 97d3645bd3 Fix main volume changes not showing on slider 2021-03-19 20:44:49 -04:00
Marcus Wu 9765193c1f Added app volume and pan setting to AppleScript 2021-03-19 20:29:36 -04:00
Kyle Neideck 2ff4c08e75 Add GitHub issue templates. 2021-02-21 01:02:07 +11:00
Kyle Neideck 3a529cc0fb Update the copyright year in the README. 2021-02-21 00:21:15 +11:00
Kyle Neideck 0f653f90a8 Merge pull request #430 from findtravishere/master
Update README.md for option 2 snapshot version regarding homebrew
2021-02-11 22:02:58 +11:00
findtravishere 4c40e0286e Update README.md for option 2 snapshot version regarding homebrew 2021-02-08 11:07:17 +08:00
Kyle Neideck bdebc07b38 MANUAL-UNINSTALL.md: Add a troubleshooting section 2021-01-26 10:20:04 +11:00
Kyle Neideck 65c2a34ce5 Merge pull request #400 from rstad/master
updates brew cask instructions in README
2020-12-11 12:16:35 +11:00
Russell Stadler 94459f7e8c updates brew cask instructions in README
`brew cask install` appears to be deprecated, and using it gets this warning:

```Warning: Calling brew cask install is deprecated! Use brew install
[--cask] instead.```

So this is a super small change in README to reflect that.
2020-12-10 12:25:52 -08:00
Kyle Neideck ef4bd54965 Add a warning about Big Sur compatibility to the README. 2020-12-03 23:05:25 +11:00
Kyle Neideck c0ab98b98b Workaround deadlocks when starting IO on macOS Big Sur.
This works similarly to the workaround in the previous commit, but
avoids the short wait while XPC times out. It still allows some initial
frames to be dropped when IO starts.

See #328, #388 and PR #390.
2020-11-26 00:49:34 +11:00
Aleksey Yurkevich 043bfb3633 Big Sur fix 2020-11-21 16:27:59 +03:00
Kyle Neideck 63393054ba Merge pull request #349 from stenalpjolly/patch-1
Update README.md
2020-08-10 18:48:53 +10:00
Stenal P Jolly 6486c8ccf3 Update README.md 2020-08-10 14:10:37 +05:30
Kyle Neideck c068810953 Merge pull request #344 from YohanTz/readme-update
Updated README.md
2020-08-07 15:21:06 +10:00
Yohan Tancrez dd05f60c1d Updated README 2020-08-07 02:32:11 +02:00
Kyle Neideck df02aae263 Fix some spurious error messages in build_and_install.sh.
Also, update the Xcode versions for CI builds.
2020-07-11 17:30:02 +10:00
Kyle Neideck f20dd578ab Temporarily allow debug builds to pass even if the install fails.
The debug build packages are still failing to install on Travis and it's
not clear why.
2020-06-22 11:31:00 +10:00
Kyle Neideck 026558d974 Disable UndefinedBehaviorSanitizer in debug build packages.
Debug builds from CI have been crashing because they can't find the
UBSan dylib (even though it was in the bundles).
2020-06-22 01:36:56 +10:00
Kyle Neideck ae724fe7af Reduce logging in CI builds so Travis doesn't kill them.
Upload the build_and_install.log from the packaging step instead of
printing it.
2020-06-21 00:12:16 +10:00
Kyle Neideck 42fbd7590e Temporarily allow debug builds to pass even if the install fails.
Debug packages are failing to install on Travis CI, but I can't
reproduce the problem locally and there's not much to go on in the
installer logs.
2020-06-20 23:25:19 +10:00
Kyle Neideck ae564d761b Fix BGMXPCHelper not being included in debug packages.
The path to the built BGMXPCHelper bundle was missing from package.sh,
so it wasn't including that bundle in the .pkg it created. Then the .pkg
would fail when you tried to install it.
2020-06-20 20:51:26 +10:00
Kyle Neideck 6edf25bfbe Merge pull request #300 from qaisjp/patch-1
Fix details box markdown
2020-05-13 22:37:21 +10:00
Qais Patankar 694c8d612e Fix details box markdown 2020-05-13 13:34:38 +01:00
Kyle Neideck f24a6cb2f1 Try to avoid using the discrete GPU on multi-GPU systems to save power.
Add NSSupportsAutomaticGraphicsSwitching to BGMApp's Info.plist in the
hopes that it will use the integrated GPU when possible. BGMApp doesn't
need to use the fastest GPU, so it's better to save battery power.

Untested because I don't have the right hardware. (I actually have an
iMac with a discrete GPU, but the integrated GPU seems to be disabled.)

See #284 and
<https://developer.apple.com/library/archive/qa/qa1734/_index.html>.
2020-04-21 14:11:42 +10:00
Kyle Neideck 70a64de734 Fix apps failing to start audio after upgrading Background Music.
A macOS bug was stopping BGMXPCHelper from running, which would stop
BGMDevice from starting IO. It would wait in StartIO until timing out.

I'm not sure why that didn't cause BGMDriver's XPC connection fail. If
it had, BGMDevice would have just started IO without waiting for BGMApp
to be ready. The it would have just dropped some initial frames and
logged an error instead of freezing for 30 seconds.

The fix/workaround is to delete the old version of BGMXPCHelper before
installing the new one.
2020-04-19 16:43:17 +10:00
Kyle Neideck 488ed16dfc Hopefully fix a segfault caused by BGM_PlayThrough.
Issue #276 reported a segfault that occurred while BGMApp was launching.
It was caused by BGM_PlayThrough's output IOProc when it tried to read
from the ring buffer. I haven't been able to reproduce the problem, so I
don't know whether this commit will actually fix it. Hopefully it will
at least make it easier to diagnose.

This commit adds locking around accesses to the buffer so that the
IOProcs will skip the current IO cycle if it isn't safe to use the ring
buffer. That should only happen during launch or when changing the
output device. (And only if something else has gone wrong.)
2020-04-18 22:00:49 +10:00
Kyle Neideck 8499b85736 Add known issue to the README: only stereo devices currently work.
Also:
 - Explain why Background Music needs "microphone access".
 - Add BlackHole to the related projects.
2020-04-10 22:21:32 +10:00
Kyle Neideck 1a2c880b32 Merge pull request #279 from Anaxilaus/master
Add troubleshooting tips
2020-04-09 19:35:08 +10:00
Mert Dede 4cfc8df48a Update README.md
Add tip to troubleshooting for meeting softwares.
2020-04-09 12:27:25 +03:00
Mert Dede 4ff3db4238 Update README.md
Add troubleshooting warning for experienced OS issues.
2020-04-09 12:19:18 +03:00
Kyle Neideck 23ecf7e46e Fix BGMDriver and BGMXPCHelper's version strings for SNAPSHOT builds.
And DEBUG builds. The "-SNAPSHOT-abc0123" suffix was only being added to
the version string for BGMApp.
2020-02-09 22:18:52 +11:00
Kyle Neideck 63b458abdc Limit the installer logs copied into the Travis logs. 2020-02-09 21:31:26 +11:00
Kyle Neideck d95b0d8b1a Add a repackage option (-r) to package.sh.
It takes an expanded Background Music .pkg installer and repackages it
into a new .pkg installer. It's mainly useful for code signing, since
you have to expand the unsigned package, sign each of the bundles,
repackage it and then sign the new package.

Also, fix build_and_install.sh putting the BGMXPCHelper dSYMs in the
wrong archive.
2020-02-09 20:19:41 +11:00
Kyle Neideck 8de821d4aa Add an archive option (-a) to build_and_install.sh.
Archiving is the standard way to build a release for distribution using
Xcode. package.sh now uses this option when it creates a release
package.

This change is mainly to keep the Run settings in the Xcode schemes from
affecting the release builds, which means we can commit the schemes with
more sensible settings.

Also, disable AddressSanitizer in the BGMDevice scheme, which was
stopping coreaudiod from launching because its sandbox won't allow the
process to load the AddressSanitizer dylib.
2020-02-05 00:09:14 +11:00
Kyle Neideck 08d428c948 Merge branch 'CodeNameFox06-patch-1' 2020-01-25 19:17:59 +11:00
Kyle Neideck 1710b93333 Merge branch 'patch-1' of https://github.com/CodeNameFox06/BackgroundMusic into CodeNameFox06-patch-1 2020-01-25 19:17:02 +11:00
Kyle Neideck 2c1677305d Add optional debug logging in release builds.
Clicking the status bar icon with the option key held now reveals a
setting that enables debug logging in BGMApp. It's enabled by default in
debug builds.

It doesn't enable debug logging in BGMDriver or BGMXPCHelper yet.

This will hopefully make it easier for people to include logs when they
report bugs that don't occur with most hardware or are otherwise hard to
reproduce.

Enabling debug logging should be unlikely to cause audio glitches, but I
haven't tried to make it completely safe.

Also,
 - add some basic unit tests for BGMPlayThrough and expand the mocks for
   the CoreAudio HAL API,
 - fix the UI tests so you can run them without code signing them, and
 - update copyright years.
2020-01-24 08:07:34 +11:00
Kyle Neideck 8ca17bb5f8 Fix occasional IO deadline misses in BGM_Device.
Seems to have been introduced by my previous commit.
2019-12-01 14:04:57 +11:00
Kyle Neideck 120606b95e Fix the BGMDriver tests not compiling. 2019-11-30 22:36:17 +11:00
Kyle Neideck 5fc615bdb5 Add a copy of CARingBuffer to the BGMDriver project.
It was using the copy in the BGMApp project, but for historical reasons
they both have a separate copy of the PublicUtility classes. We haven't
gotten around to combining them yet, so this commit is to keep them
consistent until we do.

Also, some minor clean up in BGMDriver related to #218.
2019-11-30 21:04:21 +11:00
Gordon Childs 40f0128dcd first attempt at using CARingBuffer 2019-11-30 21:04:21 +11:00
Kyle Neideck 76e63965db Update the download link in the README for v0.3.2. 2019-11-18 20:13:22 +11:00
Kyle Neideck 151c41599e Update the command to restart coreaudiod in MANUAL-INSTALL.md. 2019-11-17 16:17:16 +11:00
Kyle Neideck bab1ec9e96 build_and_install.sh: Update the Xcode Command Line Tools check.
The current method of checking the CLI Tools are installed doesn't seem
to work with newer versions of Xcode.
2019-10-17 12:41:47 +11:00
Kyle Neideck 0913671557 Update a command MANUAL-UNINSTALL.md to work on macOS Catalina. 2019-10-17 12:40:26 +11:00
Kyle Neideck 3221888a96 Update the Xcode version for Travis builds and make releases drafts.
That way I can code sign the releases before making them available and
never publish the unsigned releases.
2019-10-17 12:37:05 +11:00
CodeNameFox06 c9f2270b31 Update README.md 2019-10-12 15:09:47 +05:30
Kyle Neideck c024116cd4 Merge branch 'master' of https://github.com/kyleneideck/BackgroundMusic 2019-09-16 01:40:13 +10:00
Kyle Neideck 35d1b17c39 Merge pull request #238 from AshleyEM/master
Updated README.md (WIP)
2019-08-31 14:31:43 +10:00
AshleyEM e8e11bae32 Update README.md 2019-08-30 10:59:17 -07:00
AshleyEM be2e9b46db Fixes
- Formatting
- Wording
- Moved information
- Removed manual install/uninstall
2019-08-26 19:34:29 -07:00
AshleyEM 2e8caeed48 Fixed wording
Changed "another audio source is playing" to "other audio sources are playing".
2019-08-25 10:30:18 -07:00
AshleyEM 8f04d9afc1 Update README.md 2019-08-23 15:53:27 -07:00
AshleyEM ad13915d70 Update README.md 2019-08-23 15:52:52 -07:00
AshleyEM 2b71621c33 Update README.md 2019-08-23 15:44:15 -07:00
AshleyEM 884dd8fd0a Update README.md 2019-08-23 15:19:57 -07:00
AshleyEM 904cb9b345 Update README.md 2019-08-23 14:44:52 -07:00
AshleyEM 85d2df909e Update README.md 2019-08-23 14:18:26 -07:00
AshleyEM 966987702c Update README.md 2019-08-23 14:13:34 -07:00
AshleyEM 5c0d806e57 Update README.md 2019-08-23 14:10:22 -07:00
AshleyEM fc55ce1c54 Update README.md 2019-08-21 16:31:45 -07:00
AshleyEM d7cb6e9318 Update README.md 2019-08-21 15:27:23 -07:00
AshleyEM e5043e3fe0 Update README.md 2019-08-19 15:44:58 -07:00
AshleyEM c652955a04 Update README.md 2019-08-19 14:25:12 -07:00
AshleyEM f3ef314cd9 Update README.md 2019-08-19 14:16:32 -07:00
Kyle Neideck f61f998c51 BGMDriver: Copy settings when a new client matches an existing one.
If the new client has the same bundle ID as an existing one, we now copy
its settings (volume, etc.) to the new client. This is to fix app
volumes not being applied consistently to apps that play audio through
multiple subprocesses (specifically Google Chrome). See #61.
2019-08-17 16:16:17 +10:00
Kyle Neideck dfad77dc35 Add app volume workaround for Google Chrome. See #61. 2019-08-17 15:28:16 +10:00
Kyle Neideck 4c7eba30af Make sure the installer launches BGMApp as the logged-in user. 2019-08-17 15:20:47 +10:00
Kyle Neideck e6e784b013 Add an additional method for restarting coreaudiod.
The install and uninstall scripts try to restart coreaudiod in a number
of ways until one works. Despite that, it still wasn't always successful
when I tested it on macOS Catalina. This commit will hopefully fix that.
2019-08-17 15:17:51 +10:00
Kyle Neideck 8ed95eb83b Make sure Homebrew Travis CI builds won't show a password prompt.
`sudo` is usually passwordless in Travis CI builds, but for some reason
`sudo -v` still causes a password prompt in macOS Travis builds.

_uninstall-non-interactive.sh already included a workaround for this,
but it doesn't work when the Travis build for the Homebrew Cask calls
`brew uninstall background-music`, which then calls
_uninstall-non-interactive.sh. The old workaround used the `TRAVIS` env
var, but Homebrew seems to run the script in an environment without it.

See <https://github.com/Homebrew/homebrew-cask/pull/67524>.
2019-08-14 21:58:10 +10:00
Kyle Neideck 7f6c0e9d4f Update the README to link to v0.3.1.
Also, bump the minor version number.
2019-08-13 18:46:25 +10:00
126 changed files with 6492 additions and 6121 deletions
+49
View File
@@ -0,0 +1,49 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
## Example bug report template
> Don't worry if you have trouble getting some of this info. Just leave it out.
**Description of the bug**
> Please don't just say it's "not working".
**Steps to reproduce**
> Steps to reproduce the bug. This usually doesn't need to be super detailed.
1. Go to '...'
2. Click on '...'
3. See error message '...'
**Versions**
> Please complete the following information.
- Background Music: [e.g. "0.4.3" or "0.4.0-SNAPSHOT-c0ab98b". `Preferences > About Background Music`]
- macOS: [e.g. "11.3 Beta (20E5172i)" or "Big Sur". ` > About This Mac`]
**Hardware**
> Delete this part if you think it's probably not necessary.
- Computer: [e.g. "MacBook Pro (13-inch, 2016, Four Thunderbolt 3 Ports)". ` > About This Mac`]
- Audio Device: [e.g. "Built-in Output. Manufacturer: Apple Inc. Output Channels: 2 [...]". `System Information app > Hardware > Audio`]
**Debug logs**
> If you think the developers might not be able to reproduce the bug on their computers, e.g. because an important feature is completely broken and they would have noticed, it can help to include [debug logs](https://github.com/kyleneideck/BackgroundMusic/wiki/Getting-Debug-Logs). This takes a little effort, so feel free to leave it out at first.
[Debug logs attached here](https://github.com/example/background-music-debug-logs.txt)
**Other info**
> Anything else you want to add?
---
> Tips
> (Delete this section before posting.)
> - https://github.com/kyleneideck/BackgroundMusic#troubleshooting
> - Try the latest SNAPSHOT version from https://github.com/kyleneideck/BackgroundMusic/releases (if it's newer than the latest non-SNAPSHOT release).
> - If your bug is one of these common issues, consider leaving a comment or a +1 (👍) on an existing issue:
> - Background Music currently only supports audio devices with two channels. Bluetooth devices often only have one.
> - Volumes having no effect for certain apps: Microsoft Teams ([workaround](https://github.com/kyleneideck/BackgroundMusic/issues/268#issuecomment-604977210)), Zoom ([workaround](https://github.com/kyleneideck/BackgroundMusic/issues/396#issuecomment-741992157)), Discord ([workaround](https://github.com/kyleneideck/BackgroundMusic/issues/210#issuecomment-507048957), [see also](https://github.com/kyleneideck/BackgroundMusic/issues/267#issuecomment-617327850)), Chrome (sometimes)
+10
View File
@@ -0,0 +1,10 @@
---
name: Other
about: Feature request, question, support request or anything else
title: ''
labels: ''
assignees: ''
---
> There's no template for this issue type. I just wanted to make it clear that it's OK to submit other types of issues.
+216
View File
@@ -0,0 +1,216 @@
# TODO: Split this into multiple .yml files? Multiple jobs?
name: Build, Test and Release
on:
push:
branches:
- '*'
tags:
- '*'
pull_request:
branches:
- '*'
jobs:
# Build and test in the same job because the UI tests expect BGMDriver to be installed.
build-and-test:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
matrix:
# TODO: Add older macOS versions.
os:
- macos-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Work in a case-sensitive disk image.
# This lets us catch failures that only happen on case-sensitive filesystems.
run: |
hdiutil create \
-type SPARSEBUNDLE \
-fs 'Case-sensitive Journaled HFS+' \
-volname bgmbuild \
-nospotlight \
-verbose \
-attach \
-size 100m \
bgmbuild.dmg
sudo cp -r . /Volumes/bgmbuild
cd /Volumes/bgmbuild
- name: Install coreutils for actions/runner/issues/884 workaround.
# See https://github.com/actions/runner/issues/884#issuecomment-1018851327
run: brew install coreutils
- name: Build and install Background Music.
run: |
# `sudo` and `tput` expect this to be set.
export TERM=xterm-256color
genv --default-signal=PIPE yes | sudo ./build_and_install.sh
- name: Print the log file.
if: always()
run: cat build_and_install.log
- name: Log some checksums.
run: 'find */build/Release/*/ -type f -exec md5 {} \;'
- name: Log the installed audio devices and their IDs.
run: |
system_profiler SPAudioDataType
say -a '?'
- name: Check the BGM dirs and files were installed.
run: |
# These commands fail if the dir/file isn't found.
ls -la "/Applications/Background Music.app"
ls -la "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"
ls -la "/usr/local/libexec/BGMXPCHelper.xpc" \
|| ls -la "/Library/Application Support/Background Music/BGMXPCHelper.xpc"
ls -la "/Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"
- name: Close BGMApp (which the install script opened).
run: >-
osascript -e 'tell application "Background Music" to quit'
|| killall "Background Music"
- name: Skip the UI tests. (They don't work on GitHub Actions yet.)
run: BGMApp/BGMAppTests/UITests/skip-ui-tests.py
- name: Run the tests.
run: |
echo '::group::BGMDriver Tests'
xcodebuild \
-quiet \
-workspace BGM.xcworkspace \
-scheme 'Background Music Device' \
test
echo '::endgroup::'
echo '::group::BGMXPCHelper Tests'
xcodebuild \
-quiet \
-workspace BGM.xcworkspace \
-scheme 'BGMXPCHelper' \
test
echo '::endgroup::'
# Grant BGMApp authorization to use input devices.
# This is necessary for the UI tests because accepting the "Background Music would like to
# use the microphone" dialog programmatically isn't reliable.
# TODO: Commented out because we would need to generate the csreq (codesign signature)
# value to match the BGMApp bundle the tests will run against.
# dbPath="$HOME/Library/Application Support/com.apple.TCC/TCC.db"
# values="'kTCCServiceMicrophone','com.bearisdriving.BGM.App',0,2,2,1,X'FADE0C000000004800000001000000070000000800000014545ABE68FAF437700B14984BB24117EDDA1BBF2C0000000800000014386FB63B9CD6BA6E83CEDEAF4EDEE177C1FAEA92',NULL,NULL,'UNUSED',NULL,0,1652845317"
# sqlQuery="INSERT OR IGNORE INTO access VALUES($values);"
# sqlite3 "$dbPath" "$sqlQuery" || (echo "Failed to modify $dbPath"; exit 1)
# # Log the added TCC.db entry.
# sqlite3 "$dbPath" "select * from access where client like '%BGM%';"
echo '::group::BGMApp Tests'
# TODO: Commented out in case it uses too much CPU.
# log stream --info \
# --predicate 'process == "coreaudiod" or
# process == "Background Music" or
# process == "BGMXPCHelper" or
# composedMessage contains[cd] "Background Music" or
# composedMessage contains "BGM"' > app.log &
xcodebuild \
-quiet \
-workspace BGM.xcworkspace \
-scheme 'Background Music' \
test
echo '::endgroup::'
- name: Upload the test results.
if: always()
uses: actions/upload-artifact@v3
with:
name: bgm-test-results
path: |
/Users/runner/Library/Developer/Xcode/DerivedData/*/Logs/Test/*.xcresult
app.log
/Users/runner/Library/Logs/CrashReporter/*
/Users/runner/Library/Logs/DiagnosticReports/*
- name: Uninstall Background Music.
run: |
# `tput` expects this to be set.
export TERM=xterm-256color
genv --default-signal=PIPE yes | sudo ./uninstall.sh
- name: Check the BGM dirs and files were removed.
run: |
if ls -la "/Applications/Background Music.app"; then exit 1; fi
if ls -la "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"; then exit 1; fi
if ls -la "/usr/local/libexec/BGMXPCHelper.xpc"; then exit 1; fi
if ls -la "/Library/Application Support/Background Music/BGMXPCHelper.xpc"; then
exit 1
fi
if ls -la "/Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"; then exit 1; fi
release:
runs-on: macos-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Build the .pkg installer.
run: |
# `sudo` and `tput` expect this to be set.
export TERM=xterm-256color
# If this build is for a tag with "DEBUG" in its name, build a debug package. (More
# detailed logging, no optimization, etc.)
if [[ "$GITHUB_REF" =~ .*DEBUG.* ]]; then
sudo ./package.sh -d
else
sudo ./package.sh
fi
- name: Install the .pkg.
# Delete archives/ first because it contains a copy of Background Music.app.
# Background Music.app is "relocatable", which means that if the user moves it and then
# installs a new version, macOS will put the new version in the same place. This makes sure
# the installer puts Background Music.app in /Applications so the build won't fail when we
# check that later.
#
# package.sh puts the archives in a zipfile next to the .pkg, so we can still upload them
# after deleting the directory here.
#
# TODO: On TravisCI, this was failing for debug builds. We couldn't figure out why, so we
# might have to ignore that with
# || [[ "$GITHUB_REF" =~ .*DEBUG.* ]]
run: |
sudo rm -rf archives
sudo installer \
-pkg Background-Music-*/BackgroundMusic-*.pkg \
-target / \
-verbose \
-dumplog
- name: Print the installer logs.
if: always()
# This trims the start of the log to save space.
run: grep -E -A 9999 -B 20 'Background.?Music' /var/log/install.log
- name: Check the BGM dirs and files were installed.
if: always()
run: |
ls -la "/Applications/Background Music.app"
ls -la "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"
ls -la "/usr/local/libexec/BGMXPCHelper.xpc" \
|| ls -la "/Library/Application Support/Background Music/BGMXPCHelper.xpc"
ls -la "/Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"
- name: Upload the .pkg installer and archives.
if: always()
uses: actions/upload-artifact@v3
with:
name: pkg-installer
path: Background-Music-*
- name: Upload the log file from the package.sh build.
if: always()
uses: actions/upload-artifact@v3
with:
name: build-and-install-log-for-pkg
path: build_and_install.log
# TODO: Create a GitHub release. This is the Travis YAML that was handling it:
# deploy:
# provider: releases
# api_key:
# secure: j5Gd[...]
# file_glob: true
# file: Background-Music-*/*
# skip_cleanup: true
# name: $TRAVIS_TAG
# prerelease: true
# draft: true
# on:
# repo: kyleneideck/BackgroundMusic
# tags: true
# # TODO: Use "condition" to build master and tags?
# condition: $DEPLOY = true
+1
View File
@@ -9,6 +9,7 @@ cmake-build-debug/
BGM.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
Images/*.aux
Images/*.log
/archives/
# Everything below is from https://github.com/github/gitignore/blob/master/Objective-C.gitignore
-124
View File
@@ -1,124 +0,0 @@
language: objective-c
matrix:
include:
- os: osx
osx_image: xcode11
xcode_sdk: macosx10.14
sudo: required
env: DEPLOY=true
- os: osx
osx_image: xcode10.1
xcode_sdk: macosx10.14
sudo: required
- os: osx
osx_image: xcode10
xcode_sdk: macosx10.14
sudo: required
- os: osx
osx_image: xcode9.4
xcode_sdk: macosx10.13
sudo: required
- os: osx
osx_image: xcode9.3
xcode_sdk: macosx10.13
sudo: required
- os: osx
osx_image: xcode9.2
xcode_sdk: macosx10.13
sudo: required
# Fails to compile in 8.3, but it isn't clear from the logs why it fails.
# - os: osx
# osx_image: xcode8.3
# xcode_sdk: macosx10.12
# sudo: required
- os: osx
osx_image: xcode7.3
xcode_sdk: macosx10.11
sudo: required
env: PACKAGE=false
# branches:
# only:
# - master
install:
# Install Apple's NullAudio device. Travis' VMs don't have any audio devices installed.
- sudo xcodebuild -project BGMApp/BGMAppTests/NullAudio/AudioDriverExamples.xcodeproj -target NullAudio DSTROOT="/" install
- sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod || sudo killall coreaudiod
script:
# Build in a case-sensitive disk image to catch failures that only happen on case-sensitive filesystems.
- hdiutil create -type SPARSEBUNDLE -fs 'Case-sensitive Journaled HFS+' -volname bgmbuild -nospotlight -verbose -attach -size 100m bgmbuild.dmg
- sudo cp -r . /Volumes/bgmbuild
- cd /Volumes/bgmbuild
# Install Background Music.
- yes | ./build_and_install.sh
# Print the log file, but put it in a fold because it's so long.
- echo -en 'build_and_install.log\ntravis_fold:start:build.log\\r'
- cat build_and_install.log
- echo -en 'travis_fold:end:build.log\\r'
- find */build/Release/*/ -type f -exec md5 {} \;
# Log the installed audio devices...
- system_profiler SPAudioDataType
# ...and their IDs.
- say -a '?'
# Check the BGM dirs and files were installed. (These fail if the dir/file isn't found.)
- ls -la "/Applications/Background Music.app"
- ls -la "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"
- ls -la "/usr/local/libexec/BGMXPCHelper.xpc" || ls -la "/Library/Application Support/Background Music/BGMXPCHelper.xpc"
- ls -la "/Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"
# Close BGMApp (which the install script opened).
#
# The killall fallback command is necessary because the AppleScript gets "user canceled" on Travis'
# Xcode 9 images for some reason.
- osascript -e 'tell application "Background Music" to quit' || killall "Background Music"
# Skip the UI tests until Travis has support for them.
- BGMApp/BGMAppTests/UITests/travis-skip.py
# Run the tests.
# The echo commands put the output into a fold in the Travis logs.
- echo -en 'Unit Tests\ntravis_fold:start:tests\\r'
- xcodebuild -workspace BGM.xcworkspace -scheme 'Background Music Device' test
- xcodebuild -workspace BGM.xcworkspace -scheme 'Background Music' test
- xcodebuild -workspace BGM.xcworkspace -scheme 'BGMXPCHelper' test
- echo -en 'travis_fold:end:tests\\r'
# Uninstall Background Music.
- yes | ./uninstall.sh
# Check the BGM dirs and files were removed.
- if ls -la "/Applications/Background Music.app"; then false; fi
- if ls -la "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"; then false; fi
- if ls -la "/usr/local/libexec/BGMXPCHelper.xpc"; then false; fi
- if ls -la "/Library/Application Support/Background Music/BGMXPCHelper.xpc"; then false; fi
- if ls -la "/Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"; then false; fi
# Return early if we're not testing packaging on this OS X version.
- if [[ "$PACKAGE" == "false" ]]; then exit 0; fi
# Build the .pkg installer. Print the build logs if it fails. If this build is for a tag with
# "DEBUG" in its name, build a debug package. (More detailed logging, no optimization, etc.)
- if [[ "$TRAVIS_TAG" =~ .*DEBUG.* ]]; then
./package.sh -d || (cat build_and_install.log && travis_terminate 1);
else
./package.sh || (cat build_and_install.log && travis_terminate 1);
fi
# Install the .pkg.
- sudo installer -pkg Background-Music-*/BackgroundMusic-*.pkg -target / -verbose -dumplog
# Check the BGM dirs and files were installed again.
- ls -la "/Applications/Background Music.app"
- ls -la "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"
- ls -la "/usr/local/libexec/BGMXPCHelper.xpc" || ls -la "/Library/Application Support/Background Music/BGMXPCHelper.xpc"
- ls -la "/Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist"
# Post on IRC when Travis builds finish.
notifications:
irc: "irc.freenode.org#backgroundmusic"
# Upload the .pkg and dSYM zip to GitHub.
deploy:
provider: releases
api_key:
secure: j5GdMTkJI/9lfGMcAW4dnBnfNSW0EUGSuaKSXw49FfjfcshLL2RFxIbQkyA7QqjoJm6ohstU3tOCo7c9FrqIWjE/+5itGJpq7NXDRxFtd2qzcli1u+1IRvQUZJ4VYC9982pSS0IUynK9/f0rhbdkWsCuXWIjoClYPBRscc8soDBJvkDbfilPFfFgkc8TuSmtGDCdu9coGVi6b9HuTLNQU0g5DZkjmv71Vj3SwJ2CmvOk3GFfV1SjvG2SRgBDwyP1g9MRGRiNYkmK9lJRgsq2KLluzb04lt22x8RIcZ+kZYOQVmgDlCeWlOcXi0iz1wU/QzdoYFEAnJdG4q0hqKeqIi+p8Tc31nHPuc1ZlYpifzMQ6KuOoOP19eceJwriAT133t2RSB3Rl3nxh9bymNPNyQ2dJwGNFtO68f3aZsuE5L92lVgW/ipZ6e5Sw1ovXldR04mxNtyY4WvFXFlkn/776tKV0vgAubsHfceGM/aRoBj+E2gDvqkFqIR8wrZAZEeSM2reMHPMx5ICFppIZ8dCIVjF5bsxZQsbojY+LXV8BUU5kLAou0yD7Q+lHi9r3HYdN90+cC02HKGFYzsIiMAyf4IAngnLhwmmrLOwr3wWdACjYTJhznAZGNJh4lCeB4dx85iyj3EexJ6J/DL1k2+ZNKyMN3+i/215t+AvSsXuw5U=
file_glob: true
file: Background-Music-*/*
skip_cleanup: true
name: $TRAVIS_TAG
prerelease: true
on:
repo: kyleneideck/BackgroundMusic
tags: true
# TODO: Use "condition" to build master and tags?
condition: $DEPLOY = true
+227 -35
View File
@@ -8,12 +8,21 @@
/* Begin PBXBuildFile section */
19FE7071FF5280BC38F35E1D /* BGMVolumeChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19FE7179EBFA116F3861E79D /* BGMVolumeChangeListener.cpp */; };
19FE70F73D26D54450779A22 /* BGMPlayThroughRTLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19FE7DE5E3BA0046ED2BC3C6 /* BGMPlayThroughRTLogger.cpp */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMPlayThroughRTLogger.cpp"; }; };
19FE715E7338035C7BCD24E7 /* BGMPlayThroughRTLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19FE7DE5E3BA0046ED2BC3C6 /* BGMPlayThroughRTLogger.cpp */; };
19FE719951725A698A419CBA /* BGMVolumeChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19FE7179EBFA116F3861E79D /* BGMVolumeChangeListener.cpp */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMVolumeChangeListener.cpp"; }; };
19FE72566BCEB11BD1F3D487 /* BGMMusic.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FE73822ADD50BA9120AB05 /* BGMMusic.m */; };
19FE72566BCEB11BD1F3D487 /* BGMMusic.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FE73822ADD50BA9120AB05 /* BGMMusic.m */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMMusic.m"; }; };
19FE72D66CBC5C39F86333DE /* BGMPlayThroughRTLogger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19FE7DE5E3BA0046ED2BC3C6 /* BGMPlayThroughRTLogger.cpp */; };
19FE734C861E0370C21E4E94 /* BGMDebugLogging.c in Sources */ = {isa = PBXBuildFile; fileRef = 19FE73389459BF65748F531F /* BGMDebugLogging.c */; };
19FE7590D7565E7677D84C55 /* BGMDebugLogging.c in Sources */ = {isa = PBXBuildFile; fileRef = 19FE73389459BF65748F531F /* BGMDebugLogging.c */; };
19FE76F614F260F3F65AF550 /* BGMMusic.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FE73822ADD50BA9120AB05 /* BGMMusic.m */; };
19FE77608F6C80D0B1F595A7 /* BGMStatusBarItem.mm in Sources */ = {isa = PBXBuildFile; fileRef = 19FE774DD758EC163EF4F28C /* BGMStatusBarItem.mm */; };
19FE78EEC6D3C3B19D1FBD64 /* BGMDebugLogging.c in Sources */ = {isa = PBXBuildFile; fileRef = 19FE73389459BF65748F531F /* BGMDebugLogging.c */; };
19FE7921FD1B6C037429ECA4 /* BGMStatusBarItem.mm in Sources */ = {isa = PBXBuildFile; fileRef = 19FE774DD758EC163EF4F28C /* BGMStatusBarItem.mm */; };
19FE7B32E1214BA0E8166A9E /* BGMMusic.m in Sources */ = {isa = PBXBuildFile; fileRef = 19FE73822ADD50BA9120AB05 /* BGMMusic.m */; };
19FE7B7BDF0C683288654F90 /* BGMDebugLogging.c in Sources */ = {isa = PBXBuildFile; fileRef = 19FE73389459BF65748F531F /* BGMDebugLogging.c */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMDebugLogging.c"; }; };
19FE7BD48C0CA2CAF16C9ACE /* BGMPlayThroughTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 19FE761D0371DEF9FDF053D6 /* BGMPlayThroughTests.mm */; };
19FE7C144C12607D947EB030 /* BGMDebugLogging.c in Sources */ = {isa = PBXBuildFile; fileRef = 19FE73389459BF65748F531F /* BGMDebugLogging.c */; };
19FE7DFF63F69E77C53BF95E /* BGMVolumeChangeListener.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 19FE7179EBFA116F3861E79D /* BGMVolumeChangeListener.cpp */; };
19FE7F77376562C179449013 /* BGMStatusBarItem.mm in Sources */ = {isa = PBXBuildFile; fileRef = 19FE774DD758EC163EF4F28C /* BGMStatusBarItem.mm */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMStatusBarItem.mm"; }; };
1C0BD0A51BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C0BD0A41BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMAutoPauseMusicPrefs.mm"; }; };
@@ -56,6 +65,15 @@
1C533C7B1EED2F6200270802 /* safe_install_dir.sh in Resources */ = {isa = PBXBuildFile; fileRef = 276972901CB16008007A2F7C /* safe_install_dir.sh */; };
1C533C7C1EED2F8A00270802 /* com.bearisdriving.BGM.XPCHelper.plist.template in Resources */ = {isa = PBXBuildFile; fileRef = 2769728D1CAFCEFD007A2F7C /* com.bearisdriving.BGM.XPCHelper.plist.template */; };
1C533C801EF532CA00270802 /* _uninstall-non-interactive.sh in Resources */ = {isa = PBXBuildFile; fileRef = 1C533C7F1EF532CA00270802 /* _uninstall-non-interactive.sh */; };
1C62FE4E23D3EB2E00B9B68E /* MockAudioObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C62FE4523D3EB2D00B9B68E /* MockAudioObject.cpp */; };
1C62FE4F23D3EB2E00B9B68E /* Mock_CAHALAudioObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C62FE4623D3EB2D00B9B68E /* Mock_CAHALAudioObject.cpp */; };
1C62FE5023D3EB2E00B9B68E /* MockAudioObjects.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C62FE4723D3EB2D00B9B68E /* MockAudioObjects.cpp */; };
1C62FE5123D3EB2E00B9B68E /* Mock_CAHALAudioSystemObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C62FE4823D3EB2D00B9B68E /* Mock_CAHALAudioSystemObject.cpp */; };
1C62FE5223D3EB2E00B9B68E /* Mock_CAHALAudioDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C62FE4A23D3EB2E00B9B68E /* Mock_CAHALAudioDevice.cpp */; };
1C62FE5323D3EB2E00B9B68E /* MockAudioDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C62FE4B23D3EB2E00B9B68E /* MockAudioDevice.cpp */; };
1C62FE5523D423D700B9B68E /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C62FE5423D423D700B9B68E /* XCTest.framework */; };
1C62FE5823D4278300B9B68E /* skip-ui-tests.py in Resources */ = {isa = PBXBuildFile; fileRef = 1C62FE5623D4278300B9B68E /* skip-ui-tests.py */; };
1C687A6B23B889E000834B75 /* BGMPlayThroughRTLoggerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C687A6A23B889E000834B75 /* BGMPlayThroughRTLoggerTests.mm */; };
1C780FF21FEF6C3B00497FAD /* BGMSystemSoundsVolume.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C780FF11FEF6C3B00497FAD /* BGMSystemSoundsVolume.mm */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMSystemSoundsVolume.mm"; }; };
1C780FF31FEF6C3B00497FAD /* BGMSystemSoundsVolume.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C780FF11FEF6C3B00497FAD /* BGMSystemSoundsVolume.mm */; };
1C8034D520B0347A004BC50C /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C8034D420B0347A004BC50C /* Security.framework */; };
@@ -93,7 +111,6 @@
1CC6593E1F91DEB400B0CCDC /* BGMTermination.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CC6593A1F91DEB400B0CCDC /* BGMTermination.mm */; };
1CCC4F3E1E58196C008053E4 /* BGMXPCHelperTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CCC4F3C1E58196C008053E4 /* BGMXPCHelperTests.m */; };
1CCC4F4D1E581C40008053E4 /* BGMMusicPlayersUnitTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CCC4F4B1E581C40008053E4 /* BGMMusicPlayersUnitTests.mm */; };
1CCC4F4E1E581C40008053E4 /* Mock_CAHALAudioObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CCC4F4C1E581C40008053E4 /* Mock_CAHALAudioObject.cpp */; };
1CCC4F621E584100008053E4 /* BGMAppUITests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CCC4F611E584100008053E4 /* BGMAppUITests.mm */; };
1CD1FD301BDDEAF2004F7E1B /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD1FD2F1BDDEAF2004F7E1B /* AudioToolbox.framework */; };
1CD410D41F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMAppVolumesController.mm"; }; };
@@ -138,9 +155,10 @@
1CD989581ECFFD250014BBBF /* CAMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1963041BCAF468008A4DF7 /* CAMutex.cpp */; };
1CD989591ECFFD250014BBBF /* CAPThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C8034C21BDAFD5700668E00 /* CAPThread.cpp */; };
1CD9895A1ECFFD250014BBBF /* CARingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962E21BC94E15008A4DF7 /* CARingBuffer.cpp */; };
1CE03A57239B56740036908D /* BGMDebugLoggingMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CE03A56239B56740036908D /* BGMDebugLoggingMenuItem.m */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMDebugLoggingMenuItem.m"; }; };
1CE03A58239B56740036908D /* BGMDebugLoggingMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CE03A56239B56740036908D /* BGMDebugLoggingMenuItem.m */; };
1CE03A59239B56740036908D /* BGMDebugLoggingMenuItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 1CE03A56239B56740036908D /* BGMDebugLoggingMenuItem.m */; };
1CE7064C1BF1EC0600BFC06D /* BGMOutputDeviceMenuSection.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CE7064B1BF1EC0600BFC06D /* BGMOutputDeviceMenuSection.mm */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMOutputDeviceMenuSection.mm"; }; };
1CEACF4D1F34793700FEC143 /* CAHALAudioDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962EB1BCABFC5008A4DF7 /* CAHALAudioDevice.cpp */; };
1CEACF4F1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CEACF4E1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp */; };
1CED61691C3081C2002CAFCF /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 1CED61681C3081C2002CAFCF /* LICENSE */; };
1CED616C1C316E1A002CAFCF /* BGMAudioDeviceManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMAudioDeviceManager.mm"; }; };
1CF2D58F1F944773008B6E35 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1C1963021BCAC160008A4DF7 /* CoreAudio.framework */; };
@@ -207,6 +225,8 @@
27FB8C2F1DE468320084DB9D /* BGM_Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27FB8C2E1DE468320084DB9D /* BGM_Utils.cpp */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGM_Utils.cpp"; }; };
27FB8C301DE4758A0084DB9D /* BGMPlayThrough.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C1962E51BC94E91008A4DF7 /* BGMPlayThrough.cpp */; };
27FB8C311DE4758A0084DB9D /* BGM_Utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 27FB8C2E1DE468320084DB9D /* BGM_Utils.cpp */; };
9E129A412602AE620005851B /* BGMASApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E129A402602AE620005851B /* BGMASApplication.m */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMApp-BGMASApplication.m"; }; };
9E542C7026057FBA0016C0B5 /* BGMASApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E129A402602AE620005851B /* BGMASApplication.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -229,10 +249,17 @@
/* Begin PBXFileReference section */
19FE70CF6C93F5007940CE91 /* BGMMusic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMMusic.h; path = "Music Players/BGMMusic.h"; sourceTree = "<group>"; };
19FE7179EBFA116F3861E79D /* BGMVolumeChangeListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMVolumeChangeListener.cpp; sourceTree = "<group>"; };
19FE71BCD79E7246F7345C16 /* BGMThreadSafetyAnalysis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMThreadSafetyAnalysis.h; sourceTree = "<group>"; };
19FE72A176FD500FB4C1F5C6 /* BGMPlayThroughRTLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMPlayThroughRTLogger.h; sourceTree = "<group>"; };
19FE73389459BF65748F531F /* BGMDebugLogging.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = BGMDebugLogging.c; path = PublicUtility/BGMDebugLogging.c; sourceTree = "<group>"; };
19FE73822ADD50BA9120AB05 /* BGMMusic.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMMusic.m; path = "Music Players/BGMMusic.m"; sourceTree = "<group>"; };
19FE761D0371DEF9FDF053D6 /* BGMPlayThroughTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMPlayThroughTests.mm; path = UnitTests/BGMPlayThroughTests.mm; sourceTree = "<group>"; };
19FE774DD758EC163EF4F28C /* BGMStatusBarItem.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMStatusBarItem.mm; sourceTree = "<group>"; };
19FE7908A33FA7BD97B432D9 /* BGMDebugLogging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMDebugLogging.h; path = PublicUtility/BGMDebugLogging.h; sourceTree = "<group>"; };
19FE799A86A285DD9423D164 /* BGMStatusBarItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMStatusBarItem.h; sourceTree = "<group>"; };
19FE7DE5E3BA0046ED2BC3C6 /* BGMPlayThroughRTLogger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMPlayThroughRTLogger.cpp; sourceTree = "<group>"; };
19FE7FDAEBC3F0DB8C99823B /* BGMVolumeChangeListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMVolumeChangeListener.h; sourceTree = "<group>"; };
1C09150723F010FB001EB0E1 /* set-version.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "set-version.sh"; sourceTree = "<group>"; };
1C0BD0A31BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMAutoPauseMusicPrefs.h; path = Preferences/BGMAutoPauseMusicPrefs.h; sourceTree = "<group>"; };
1C0BD0A41BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMAutoPauseMusicPrefs.mm; path = Preferences/BGMAutoPauseMusicPrefs.mm; sourceTree = "<group>"; };
1C0BD0A61BF1B029004F4CF5 /* BGMPreferencesMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMPreferencesMenu.h; path = Preferences/BGMPreferencesMenu.h; sourceTree = "<group>"; };
@@ -282,7 +309,7 @@
1C3D36711ED90E8600F98E66 /* BGMDeviceControlsList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMDeviceControlsList.h; sourceTree = "<group>"; };
1C3DB4881BE0885A00EC8160 /* BGMAppVolumes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BGMAppVolumes.m; sourceTree = "<group>"; };
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMAppVolumes.h; sourceTree = "<group>"; };
1C43DABE22F582780004AF35 /* Background Music.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Background Music.entitlements"; sourceTree = "<group>"; };
1C43DABE22F582780004AF35 /* BGMApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = BGMApp.entitlements; sourceTree = "<group>"; };
1C4699461BD5C0E400F78043 /* BGMiTunes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMiTunes.m; path = "Music Players/BGMiTunes.m"; sourceTree = "<group>"; };
1C46994C1BD7694C00F78043 /* BGMDeviceControlSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMDeviceControlSync.cpp; sourceTree = "<group>"; };
1C46994D1BD7694C00F78043 /* BGMDeviceControlSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMDeviceControlSync.h; sourceTree = "<group>"; };
@@ -290,6 +317,19 @@
1C4D1A1C217C7D6400A1ACD0 /* BGMPreferredOutputDevices.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMPreferredOutputDevices.mm; sourceTree = "<group>"; };
1C533C791EED28B700270802 /* uninstall.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = uninstall.sh; path = ../../uninstall.sh; sourceTree = "<group>"; };
1C533C7F1EF532CA00270802 /* _uninstall-non-interactive.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "_uninstall-non-interactive.sh"; sourceTree = "<group>"; };
1C62FE4523D3EB2D00B9B68E /* MockAudioObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MockAudioObject.cpp; path = BGMAppTests/UnitTests/Mocks/MockAudioObject.cpp; sourceTree = SOURCE_ROOT; };
1C62FE4623D3EB2D00B9B68E /* Mock_CAHALAudioObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mock_CAHALAudioObject.cpp; path = BGMAppTests/UnitTests/Mocks/Mock_CAHALAudioObject.cpp; sourceTree = SOURCE_ROOT; };
1C62FE4723D3EB2D00B9B68E /* MockAudioObjects.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MockAudioObjects.cpp; path = BGMAppTests/UnitTests/Mocks/MockAudioObjects.cpp; sourceTree = SOURCE_ROOT; };
1C62FE4823D3EB2D00B9B68E /* Mock_CAHALAudioSystemObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mock_CAHALAudioSystemObject.cpp; path = BGMAppTests/UnitTests/Mocks/Mock_CAHALAudioSystemObject.cpp; sourceTree = SOURCE_ROOT; };
1C62FE4923D3EB2E00B9B68E /* MockAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MockAudioDevice.h; path = BGMAppTests/UnitTests/Mocks/MockAudioDevice.h; sourceTree = SOURCE_ROOT; };
1C62FE4A23D3EB2E00B9B68E /* Mock_CAHALAudioDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mock_CAHALAudioDevice.cpp; path = BGMAppTests/UnitTests/Mocks/Mock_CAHALAudioDevice.cpp; sourceTree = SOURCE_ROOT; };
1C62FE4B23D3EB2E00B9B68E /* MockAudioDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MockAudioDevice.cpp; path = BGMAppTests/UnitTests/Mocks/MockAudioDevice.cpp; sourceTree = SOURCE_ROOT; };
1C62FE4C23D3EB2E00B9B68E /* MockAudioObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MockAudioObject.h; path = BGMAppTests/UnitTests/Mocks/MockAudioObject.h; sourceTree = SOURCE_ROOT; };
1C62FE4D23D3EB2E00B9B68E /* MockAudioObjects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MockAudioObjects.h; path = BGMAppTests/UnitTests/Mocks/MockAudioObjects.h; sourceTree = SOURCE_ROOT; };
1C62FE5423D423D700B9B68E /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
1C62FE5623D4278300B9B68E /* skip-ui-tests.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; name = "skip-ui-tests.py"; path = "UITests/skip-ui-tests.py"; sourceTree = "<group>"; };
1C62FE5923D44FC000B9B68E /* BGMApp-Debug.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "BGMApp-Debug.entitlements"; sourceTree = "<group>"; };
1C687A6A23B889E000834B75 /* BGMPlayThroughRTLoggerTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMPlayThroughRTLoggerTests.mm; path = UnitTests/BGMPlayThroughRTLoggerTests.mm; sourceTree = "<group>"; };
1C780FF01FEF6C3B00497FAD /* BGMSystemSoundsVolume.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BGMSystemSoundsVolume.h; sourceTree = "<group>"; };
1C780FF11FEF6C3B00497FAD /* BGMSystemSoundsVolume.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMSystemSoundsVolume.mm; sourceTree = "<group>"; };
1C8034C21BDAFD5700668E00 /* CAPThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAPThread.cpp; path = PublicUtility/CAPThread.cpp; sourceTree = "<group>"; };
@@ -329,7 +369,6 @@
1CCC4F3C1E58196C008053E4 /* BGMXPCHelperTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMXPCHelperTests.m; path = BGMXPCHelperTests/BGMXPCHelperTests.m; sourceTree = SOURCE_ROOT; };
1CCC4F491E581C0D008053E4 /* BGMAppUnitTests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "BGMAppUnitTests-Info.plist"; path = "UnitTests/BGMAppUnitTests-Info.plist"; sourceTree = "<group>"; };
1CCC4F4B1E581C40008053E4 /* BGMMusicPlayersUnitTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMMusicPlayersUnitTests.mm; path = UnitTests/BGMMusicPlayersUnitTests.mm; sourceTree = "<group>"; };
1CCC4F4C1E581C40008053E4 /* Mock_CAHALAudioObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mock_CAHALAudioObject.cpp; path = UnitTests/Mock_CAHALAudioObject.cpp; sourceTree = "<group>"; };
1CCC4F541E584081008053E4 /* BGMAppUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BGMAppUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
1CCC4F5F1E5840EF008053E4 /* BGMAppUITests-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "BGMAppUITests-Info.plist"; path = "UITests/BGMAppUITests-Info.plist"; sourceTree = "<group>"; };
1CCC4F611E584100008053E4 /* BGMAppUITests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMAppUITests.mm; path = BGMAppTests/UITests/BGMAppUITests.mm; sourceTree = SOURCE_ROOT; };
@@ -337,9 +376,10 @@
1CD410D21F9EDDAD0070A094 /* BGMAppVolumesController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BGMAppVolumesController.h; sourceTree = "<group>"; };
1CD410D31F9EDDAD0070A094 /* BGMAppVolumesController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMAppVolumesController.mm; sourceTree = "<group>"; };
1CDE224022CBB95B0008E3AC /* Music.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Music.h; path = "Music Players/Music.h"; sourceTree = "<group>"; };
1CE03A55239B56740036908D /* BGMDebugLoggingMenuItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BGMDebugLoggingMenuItem.h; sourceTree = "<group>"; };
1CE03A56239B56740036908D /* BGMDebugLoggingMenuItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BGMDebugLoggingMenuItem.m; sourceTree = "<group>"; };
1CE7064A1BF1EC0600BFC06D /* BGMOutputDeviceMenuSection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMOutputDeviceMenuSection.h; sourceTree = "<group>"; };
1CE7064B1BF1EC0600BFC06D /* BGMOutputDeviceMenuSection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMOutputDeviceMenuSection.mm; sourceTree = "<group>"; };
1CEACF4E1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Mock_CAHALAudioSystemObject.cpp; path = UnitTests/Mock_CAHALAudioSystemObject.cpp; sourceTree = "<group>"; };
1CED61681C3081C2002CAFCF /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
1CED616A1C316E1A002CAFCF /* BGMAudioDeviceManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMAudioDeviceManager.h; sourceTree = "<group>"; };
1CED616B1C316E1A002CAFCF /* BGMAudioDeviceManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGMAudioDeviceManager.mm; sourceTree = "<group>"; };
@@ -395,6 +435,8 @@
27F7D48F1D2483B100821C4B /* BGMDecibel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMDecibel.m; path = "Music Players/BGMDecibel.m"; sourceTree = "<group>"; };
27F7D4911D2484A300821C4B /* Decibel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Decibel.h; path = "Music Players/Decibel.h"; sourceTree = "<group>"; };
27FB8C2E1DE468320084DB9D /* BGM_Utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BGM_Utils.cpp; path = ../SharedSource/BGM_Utils.cpp; sourceTree = "<group>"; };
9E129A3F2602AE620005851B /* BGMASApplication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BGMASApplication.h; path = Scripting/BGMASApplication.h; sourceTree = "<group>"; };
9E129A402602AE620005851B /* BGMASApplication.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = BGMASApplication.m; path = Scripting/BGMASApplication.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -414,6 +456,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
1C62FE5523D423D700B9B68E /* XCTest.framework in Frameworks */,
1C8B0C6B21645355008C5679 /* AVFoundation.framework in Frameworks */,
1CD989401ECFFCC50014BBBF /* AudioToolbox.framework in Frameworks */,
);
@@ -448,6 +491,15 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
1C09150623F010FB001EB0E1 /* Scripts */ = {
isa = PBXGroup;
children = (
1C09150723F010FB001EB0E1 /* set-version.sh */,
);
name = Scripts;
path = ../SharedSource/Scripts;
sourceTree = "<group>";
};
1C0BD0A21BF1A827004F4CF5 /* Preferences Menu */ = {
isa = PBXGroup;
children = (
@@ -499,6 +551,9 @@
1C8034C31BDAFD5700668E00 /* CAPThread.h */,
1C1962E21BC94E15008A4DF7 /* CARingBuffer.cpp */,
1C1962E31BC94E15008A4DF7 /* CARingBuffer.h */,
19FE7908A33FA7BD97B432D9 /* BGMDebugLogging.h */,
19FE73389459BF65748F531F /* BGMDebugLogging.c */,
19FE71BCD79E7246F7345C16 /* BGMThreadSafetyAnalysis.h */,
);
name = PublicUtility;
sourceTree = "<group>";
@@ -511,6 +566,8 @@
1C2FC31D1EC723A100A76592 /* BGMASOutputDevice.h */,
1C2FC31A1EC7238A00A76592 /* BGMASOutputDevice.mm */,
1C2FC2FF1EB4D6E700A76592 /* BGMApp.sdef */,
9E129A3F2602AE620005851B /* BGMASApplication.h */,
9E129A402602AE620005851B /* BGMASApplication.m */,
);
name = Scripting;
sourceTree = "<group>";
@@ -519,6 +576,7 @@
isa = PBXGroup;
children = (
27D643B41C9FABBD00737F6E /* BGM_Types.h */,
1C09150623F010FB001EB0E1 /* Scripts */,
2771700F1CA0C83B00AB34B4 /* BGM_Utils.h */,
27FB8C2E1DE468320084DB9D /* BGM_Utils.cpp */,
27D643C41C9FBE5600737F6E /* BGM_TestUtils.h */,
@@ -561,10 +619,25 @@
name = "Music Players";
sourceTree = "<group>";
};
1C62FE4423D3EAC500B9B68E /* Mocks */ = {
isa = PBXGroup;
children = (
1C62FE4823D3EB2D00B9B68E /* Mock_CAHALAudioSystemObject.cpp */,
1C62FE4623D3EB2D00B9B68E /* Mock_CAHALAudioObject.cpp */,
1C62FE4A23D3EB2E00B9B68E /* Mock_CAHALAudioDevice.cpp */,
1C62FE4D23D3EB2E00B9B68E /* MockAudioObjects.h */,
1C62FE4723D3EB2D00B9B68E /* MockAudioObjects.cpp */,
1C62FE4C23D3EB2E00B9B68E /* MockAudioObject.h */,
1C62FE4523D3EB2D00B9B68E /* MockAudioObject.cpp */,
1C62FE4923D3EB2E00B9B68E /* MockAudioDevice.h */,
1C62FE4B23D3EB2E00B9B68E /* MockAudioDevice.cpp */,
);
path = Mocks;
sourceTree = "<group>";
};
1CB8B32D1BBA75EF000E2DD1 = {
isa = PBXGroup;
children = (
1C43DABE22F582780004AF35 /* Background Music.entitlements */,
1CB8B3381BBA75EF000E2DD1 /* BGMApp */,
1CB8B34C1BBA75F0000E2DD1 /* BGMApp Tests */,
27379B901C7F57DB0084A24C /* BGMXPCHelper */,
@@ -598,6 +671,8 @@
1C80DED220A6718600045BBE /* BGMAppWatcher.m */,
1C837DD61F6AA1F2004B1E60 /* BGMOutputVolumeMenuItem.h */,
1C837DD71F6AA1F2004B1E60 /* BGMOutputVolumeMenuItem.mm */,
1CE03A55239B56740036908D /* BGMDebugLoggingMenuItem.h */,
1CE03A56239B56740036908D /* BGMDebugLoggingMenuItem.m */,
1C780FF01FEF6C3B00497FAD /* BGMSystemSoundsVolume.h */,
1C780FF11FEF6C3B00497FAD /* BGMSystemSoundsVolume.mm */,
1C3DB48A1BE0888500EC8160 /* BGMAppVolumes.h */,
@@ -626,6 +701,8 @@
1C3D36701ED90E8600F98E66 /* BGMDeviceControlsList.cpp */,
1C1962E61BC94E91008A4DF7 /* BGMPlayThrough.h */,
1C1962E51BC94E91008A4DF7 /* BGMPlayThrough.cpp */,
19FE72A176FD500FB4C1F5C6 /* BGMPlayThroughRTLogger.h */,
19FE7DE5E3BA0046ED2BC3C6 /* BGMPlayThroughRTLogger.cpp */,
19FE799A86A285DD9423D164 /* BGMStatusBarItem.h */,
19FE774DD758EC163EF4F28C /* BGMStatusBarItem.mm */,
1CC6593B1F91DEB400B0CCDC /* BGMTermination.h */,
@@ -646,6 +723,8 @@
1CB8B3391BBA75EF000E2DD1 /* Supporting Files */ = {
isa = PBXGroup;
children = (
1C43DABE22F582780004AF35 /* BGMApp.entitlements */,
1C62FE5923D44FC000B9B68E /* BGMApp-Debug.entitlements */,
275343BF1DFD01BC00DF3858 /* SystemPreferences.h */,
1CED61681C3081C2002CAFCF /* LICENSE */,
1CC1DF951BE8607700FB8FE4 /* Images.xcassets */,
@@ -691,6 +770,7 @@
children = (
1CCC4F5F1E5840EF008053E4 /* BGMAppUITests-Info.plist */,
1CCC4F491E581C0D008053E4 /* BGMAppUnitTests-Info.plist */,
1C62FE5623D4278300B9B68E /* skip-ui-tests.py */,
);
name = "Supporting Files";
sourceTree = "<group>";
@@ -699,8 +779,9 @@
isa = PBXGroup;
children = (
1CCC4F4B1E581C40008053E4 /* BGMMusicPlayersUnitTests.mm */,
1CCC4F4C1E581C40008053E4 /* Mock_CAHALAudioObject.cpp */,
1CEACF4E1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp */,
19FE761D0371DEF9FDF053D6 /* BGMPlayThroughTests.mm */,
1C687A6A23B889E000834B75 /* BGMPlayThroughRTLoggerTests.mm */,
1C62FE4423D3EAC500B9B68E /* Mocks */,
);
name = "Unit Tests";
sourceTree = "<group>";
@@ -756,6 +837,7 @@
2743CA1B1D86DA9B0089613B /* Frameworks */ = {
isa = PBXGroup;
children = (
1C62FE5423D423D700B9B68E /* XCTest.framework */,
1C8034D420B0347A004BC50C /* Security.framework */,
1C8B0C69216205BF008C5679 /* AVFoundation.framework */,
270A84501E0044EE00F13C99 /* ScriptingBridge.framework */,
@@ -788,7 +870,7 @@
1CB8B3321BBA75EF000E2DD1 /* Sources */,
1CB8B3331BBA75EF000E2DD1 /* Frameworks */,
1CB8B3341BBA75EF000E2DD1 /* Resources */,
1CD440581E593DDD0064E0BC /* ShellScript */,
1CD440581E593DDD0064E0BC /* Run Script - set-version.sh */,
);
buildRules = (
);
@@ -824,7 +906,8 @@
27379B8B1C7F57DA0084A24C /* Sources */,
27379B8C1C7F57DA0084A24C /* Frameworks */,
27379B8D1C7F57DA0084A24C /* Resources */,
276972891CAFCE91007A2F7C /* ShellScript */,
1C09150923F0208F001EB0E1 /* Run Script - set-version.sh */,
276972891CAFCE91007A2F7C /* Run Script - post_install.sh */,
);
buildRules = (
);
@@ -902,16 +985,15 @@
};
2743C9F51D86CFF90089613B = {
CreatedOnToolsVersion = 8.0;
ProvisioningStyle = Automatic;
ProvisioningStyle = Manual;
};
};
};
buildConfigurationList = 1CB8B3311BBA75EF000E2DD1 /* Build configuration list for PBXProject "BGMApp" */;
compatibilityVersion = "Xcode 6.3";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
@@ -948,6 +1030,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1C62FE5823D4278300B9B68E /* skip-ui-tests.py in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -978,26 +1061,46 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
1CD440581E593DDD0064E0BC /* ShellScript */ = {
1C09150923F0208F001EB0E1 /* Run Script - set-version.sh */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Run Script - set-version.sh";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Append the git HEAD short ID to the build version for SNAPSHOT and DEBUG builds.\n\"$SRCROOT/../SharedSource/Scripts/set-version.sh\"\n";
};
1CD440581E593DDD0064E0BC /* Run Script - set-version.sh */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script - set-version.sh";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Append the git HEAD short ID to the build version for snapshot builds. Thanks to\n# Václav Slavík for the initial version of this: http://stackoverflow.com/a/26354117/1091063\n# TODO: Update CFBundleVersion as well?\n\n# If HEAD isn't tagged, or has \"SNAPSHOT\" or \"DEBUG\" in the tag name, this is a snapshot build.\n# If HEAD is tagged more than once, use the most recent.\nTAG=$(/usr/bin/git tag --points-at HEAD --sort='-taggerdate' 2>/dev/null | head -n 1)\nif [[ $? -eq 0 ]] && ( [[ \"${TAG}\" == \"\" ]] || \\\n [[ \"${TAG}\" =~ .*SNAPSHOT.* ]] || \\\n [[ \"${TAG}\" =~ .*DEBUG.* ]] ); then\n HEAD=$(/usr/bin/git rev-list HEAD --max-count=1 --abbrev-commit)\n INFO_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n if [[ \"${CONFIGURATION}\" != \"Release\" ]]; then\n TYPE=\"DEBUG\"\n else\n TYPE=\"SNAPSHOT\"\n fi\n if [[ -f \"$INFO_PLIST\" ]]; then\n CURRENT_VERSION=$(/usr/libexec/PlistBuddy -c \"Print :CFBundleShortVersionString\" \"${INFO_PLIST}\")\n BASE_VERSION=$(/usr/libexec/PlistBuddy -c \"Print :BGMBundleVersionBase\" \"${INFO_PLIST}\" 2>/dev/null)\n if [[ $? -ne 0 ]] || [[ \"${BASE_VERSION}\" == \"\" ]]; then\n BASE_VERSION=\"${CURRENT_VERSION}\"\n /usr/libexec/PlistBuddy -c \"Add :BGMBundleVersionBase string ${BASE_VERSION}\" \"${INFO_PLIST}\"\n fi\n NEW_VERSION=\"${BASE_VERSION}-${TYPE}-${HEAD}\"\n if [[ \"${NEW_VERSION}\" != \"${CURRENT_VERSION}\" ]]; then # Only touch the file if we need to.\n /usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString ${NEW_VERSION}\" \"${INFO_PLIST}\"\n fi\n fi\nfi\n";
shellScript = "# Append the git HEAD short ID to the build version for SNAPSHOT and DEBUG builds.\n\"$SRCROOT/../SharedSource/Scripts/set-version.sh\"\n";
};
276972891CAFCE91007A2F7C /* ShellScript */ = {
276972891CAFCE91007A2F7C /* Run Script - post_install.sh */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 8;
files = (
);
inputPaths = (
);
name = "Run Script - post_install.sh";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 1;
@@ -1013,6 +1116,7 @@
files = (
1C86DA6A1F91EE3B000C8CCF /* CAPThread.cpp in Sources */,
1C780FF21FEF6C3B00497FAD /* BGMSystemSoundsVolume.mm in Sources */,
1CE03A57239B56740036908D /* BGMDebugLoggingMenuItem.m in Sources */,
1C4699471BD5C0E400F78043 /* BGMiTunes.m in Sources */,
1CD410D41F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */,
1C1962E41BC94E15008A4DF7 /* CARingBuffer.cpp in Sources */,
@@ -1053,6 +1157,7 @@
1C837DD81F6AA1F2004B1E60 /* BGMOutputVolumeMenuItem.mm in Sources */,
1C9258472090287F00B8D3A6 /* BGMGooglePlayMusicDesktopPlayerConnection.m in Sources */,
1C1963011BCAC0F6008A4DF7 /* CACFString.cpp in Sources */,
9E129A412602AE620005851B /* BGMASApplication.m in Sources */,
1C1962E71BC94E91008A4DF7 /* BGMPlayThrough.cpp in Sources */,
1C8D8304204238DB00A838F2 /* BGMSwinsian.m in Sources */,
1C1962FA1BCAC061008A4DF7 /* CADebugMacros.cpp in Sources */,
@@ -1067,6 +1172,8 @@
19FE7F77376562C179449013 /* BGMStatusBarItem.mm in Sources */,
19FE719951725A698A419CBA /* BGMVolumeChangeListener.cpp in Sources */,
19FE72566BCEB11BD1F3D487 /* BGMMusic.m in Sources */,
19FE70F73D26D54450779A22 /* BGMPlayThroughRTLogger.cpp in Sources */,
19FE7B7BDF0C683288654F90 /* BGMDebugLogging.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1076,6 +1183,7 @@
files = (
1C8104B022AD082E00B35517 /* BGMGooglePlayMusicDesktopPlayer.m in Sources */,
1C8D830520423E1C00A838F2 /* BGMSwinsian.m in Sources */,
1CE03A58239B56740036908D /* BGMDebugLoggingMenuItem.m in Sources */,
1CACCF3A1F334447007F86CA /* BGMBackgroundMusicDevice.cpp in Sources */,
1C780FF31FEF6C3B00497FAD /* BGMSystemSoundsVolume.mm in Sources */,
1CC6593D1F91DEB400B0CCDC /* BGMTermination.mm in Sources */,
@@ -1116,6 +1224,7 @@
1CD989361ECFFC9E0014BBBF /* CACFDictionary.cpp in Sources */,
1CD989371ECFFC9E0014BBBF /* CACFNumber.cpp in Sources */,
1CD989381ECFFC9E0014BBBF /* CACFString.cpp in Sources */,
9E542C7026057FBA0016C0B5 /* BGMASApplication.m in Sources */,
1CD989391ECFFC9E0014BBBF /* CADebugger.cpp in Sources */,
1CD9893A1ECFFC9E0014BBBF /* CADebugMacros.cpp in Sources */,
1CD9893B1ECFFC9E0014BBBF /* CADebugPrintf.cpp in Sources */,
@@ -1130,6 +1239,8 @@
19FE7921FD1B6C037429ECA4 /* BGMStatusBarItem.mm in Sources */,
19FE7DFF63F69E77C53BF95E /* BGMVolumeChangeListener.cpp in Sources */,
19FE7B32E1214BA0E8166A9E /* BGMMusic.m in Sources */,
19FE72D66CBC5C39F86333DE /* BGMPlayThroughRTLogger.cpp in Sources */,
19FE734C861E0370C21E4E94 /* BGMDebugLogging.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1158,6 +1269,7 @@
27D643C01C9FB99200737F6E /* BGMXPCHelperService.mm in Sources */,
27D643C11C9FB99200737F6E /* main.m in Sources */,
277170161CA24D7C00AB34B4 /* BGMXPCListenerDelegate.m in Sources */,
19FE7590D7565E7677D84C55 /* BGMDebugLogging.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1165,10 +1277,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1C62FE5123D3EB2E00B9B68E /* Mock_CAHALAudioSystemObject.cpp in Sources */,
1C8104AF22AD07E200B35517 /* BGMAppWatcher.m in Sources */,
1C8D830620423E2400A838F2 /* BGMSwinsian.m in Sources */,
1C227C0B1FA4C48200A95B6D /* BGMAppVolumes.m in Sources */,
1CEACF4D1F34793700FEC143 /* CAHALAudioDevice.cpp in Sources */,
1CACCF3B1F334450007F86CA /* BGMBackgroundMusicDevice.cpp in Sources */,
1C8D830C2042DE9600A838F2 /* BGMGooglePlayMusicDesktopPlayer.m in Sources */,
1C3D36741ED90E8600F98E66 /* BGMDeviceControlsList.cpp in Sources */,
@@ -1176,14 +1288,16 @@
27FB8C311DE4758A0084DB9D /* BGM_Utils.cpp in Sources */,
27FB8C071DD75D0A0084DB9D /* BGMHermes.m in Sources */,
2743CA211D86DE780089613B /* BGMDeviceControlSync.cpp in Sources */,
1CE03A59239B56740036908D /* BGMDebugLoggingMenuItem.m in Sources */,
2743CA0C1D86D7FA0089613B /* CACFArray.cpp in Sources */,
1C837DDA1F6AA1F2004B1E60 /* BGMOutputVolumeMenuItem.mm in Sources */,
2743CA0D1D86D7FA0089613B /* CACFDictionary.cpp in Sources */,
2743CA0E1D86D7FA0089613B /* CACFNumber.cpp in Sources */,
2743CA0F1D86D7FA0089613B /* CACFString.cpp in Sources */,
1CCC4F4E1E581C40008053E4 /* Mock_CAHALAudioObject.cpp in Sources */,
2743CA101D86D7FA0089613B /* CADebugger.cpp in Sources */,
1C687A6B23B889E000834B75 /* BGMPlayThroughRTLoggerTests.mm in Sources */,
2743CA111D86D7FA0089613B /* CADebugMacros.cpp in Sources */,
1C62FE4F23D3EB2E00B9B68E /* Mock_CAHALAudioObject.cpp in Sources */,
2743CA121D86D7FA0089613B /* CADebugPrintf.cpp in Sources */,
1CCC4F4D1E581C40008053E4 /* BGMMusicPlayersUnitTests.mm in Sources */,
2743CA141D86D7FA0089613B /* CAHALAudioStream.cpp in Sources */,
@@ -1193,7 +1307,6 @@
2743CA191D86D7FA0089613B /* CARingBuffer.cpp in Sources */,
2743CA0A1D86D52D0089613B /* BGMAudioDeviceManager.mm in Sources */,
2743CA031D86D41C0089613B /* BGMScriptingBridge.m in Sources */,
1CEACF4F1F34A30000FEC143 /* Mock_CAHALAudioSystemObject.cpp in Sources */,
2743CA041D86D41C0089613B /* BGMMusicPlayer.m in Sources */,
1CD410D61F9EDDAD0070A094 /* BGMAppVolumesController.mm in Sources */,
2743CA051D86D41C0089613B /* BGMDecibel.m in Sources */,
@@ -1201,14 +1314,21 @@
2743CA071D86D41C0089613B /* BGMVLC.m in Sources */,
1CF5423D1EAAEE4300445AD8 /* BGMAudioDevice.cpp in Sources */,
2743CA081D86D41C0089613B /* BGMVOX.m in Sources */,
1C62FE5223D3EB2E00B9B68E /* Mock_CAHALAudioDevice.cpp in Sources */,
1C62FE5323D3EB2E00B9B68E /* MockAudioDevice.cpp in Sources */,
2743CA091D86D41C0089613B /* BGMUserDefaults.m in Sources */,
1C62FE5023D3EB2E00B9B68E /* MockAudioObjects.cpp in Sources */,
1CC6593E1F91DEB400B0CCDC /* BGMTermination.mm in Sources */,
2743CA011D86D3CB0089613B /* BGMMusicPlayers.mm in Sources */,
1C62FE4E23D3EB2E00B9B68E /* MockAudioObject.cpp in Sources */,
2743CA021D86D3CB0089613B /* BGMiTunes.m in Sources */,
19FE77608F6C80D0B1F595A7 /* BGMStatusBarItem.mm in Sources */,
19FE7071FF5280BC38F35E1D /* BGMVolumeChangeListener.cpp in Sources */,
1C9258492090287F00B8D3A6 /* BGMGooglePlayMusicDesktopPlayerConnection.m in Sources */,
19FE76F614F260F3F65AF550 /* BGMMusic.m in Sources */,
19FE715E7338035C7BCD24E7 /* BGMPlayThroughRTLogger.cpp in Sources */,
19FE78EEC6D3C3B19D1FBD64 /* BGMDebugLogging.c in Sources */,
19FE7BD48C0CA2CAF16C9ACE /* BGMPlayThroughTests.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1217,6 +1337,7 @@
buildActionMask = 2147483647;
files = (
1CCC4F3E1E58196C008053E4 /* BGMXPCHelperTests.m in Sources */,
19FE7C144C12607D947EB030 /* BGMDebugLogging.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1278,6 +1399,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -1321,7 +1443,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "-fno-omit-frame-pointer";
@@ -1336,20 +1458,23 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_USE_OPTIMIZATION_PROFILE = NO;
CODE_SIGN_ENTITLEMENTS = "Background Music.entitlements";
CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES;
CODE_SIGN_ENTITLEMENTS = "BGMApp/BGMApp-Debug.entitlements";
COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES;
DEPLOYMENT_POSTPROCESSING = NO;
DWARF_DSYM_FILE_NAME = "$(EXECUTABLE_NAME).dSYM";
DWARF_DSYM_FOLDER_PATH = "$(CONFIGURATION_BUILD_DIR)/$(EXECUTABLE_FOLDER_PATH)";
ENABLE_HARDENED_RUNTIME = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
INFOPLIST_FILE = BGMApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.App;
PRODUCT_NAME = "$(TARGET_NAME)";
STRIP_STYLE = "non-global";
WARNING_CFLAGS = "-Wpartial-availability";
WARNING_CFLAGS = (
"-Wpartial-availability",
"-Wthread-safety",
);
};
name = DebugOpt;
};
@@ -1384,6 +1509,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -1427,7 +1553,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
OTHER_CFLAGS = "-fno-omit-frame-pointer";
@@ -1468,6 +1594,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = NO;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -1508,7 +1635,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = NO;
OTHER_CFLAGS = "";
RUN_CLANG_STATIC_ANALYZER = YES;
@@ -1522,20 +1649,23 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_USE_OPTIMIZATION_PROFILE = NO;
CODE_SIGN_ENTITLEMENTS = "Background Music.entitlements";
CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES;
CODE_SIGN_ENTITLEMENTS = "BGMApp/BGMApp-Debug.entitlements";
COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES;
DEPLOYMENT_POSTPROCESSING = NO;
DWARF_DSYM_FILE_NAME = "$(EXECUTABLE_NAME).dSYM";
DWARF_DSYM_FOLDER_PATH = "$(CONFIGURATION_BUILD_DIR)/$(EXECUTABLE_FOLDER_PATH)";
ENABLE_HARDENED_RUNTIME = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
INFOPLIST_FILE = BGMApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.App;
PRODUCT_NAME = "$(TARGET_NAME)";
STRIP_STYLE = "non-global";
WARNING_CFLAGS = "-Wpartial-availability";
WARNING_CFLAGS = (
"-Wpartial-availability",
"-Wthread-safety",
);
};
name = Debug;
};
@@ -1544,13 +1674,22 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_USE_OPTIMIZATION_PROFILE = NO;
CODE_SIGN_ENTITLEMENTS = "Background Music.entitlements";
CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES;
CODE_SIGN_ENTITLEMENTS = BGMApp/BGMApp.entitlements;
COMBINE_HIDPI_IMAGES = YES;
DEAD_CODE_STRIPPING = YES;
DEPLOYMENT_POSTPROCESSING = YES;
DWARF_DSYM_FILE_NAME = "$(EXECUTABLE_NAME).dSYM";
DWARF_DSYM_FOLDER_PATH = "$(CONFIGURATION_BUILD_DIR)/$(EXECUTABLE_FOLDER_PATH)";
ENABLE_HARDENED_RUNTIME = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=0",
"CoreAudio_Debug=0",
"CoreAudio_StopOnAssert=0",
"BGM_StopDebuggerOnLoggedExceptions=$(BGM_STOP_DEBUGGER_ON_LOGGED_EXCEPTIONS)",
"BGM_StopDebuggerOnLoggedUnexpectedExceptions=$(BGM_STOP_DEBUGGER_ON_LOGGED_UNEXPECTED_EXCEPTIONS)",
"CoreAudio_StopOnThrow=0",
"CoreAudio_UseSysLog=1",
);
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
INFOPLIST_FILE = BGMApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
@@ -1561,6 +1700,7 @@
WARNING_CFLAGS = (
"-Wno-profile-instr-out-of-date",
"-Wpartial-availability",
"-Wthread-safety",
);
};
name = Release;
@@ -1568,14 +1708,16 @@
1CCC4F5C1E584081008053E4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = "BGMApp/BGMApp-Debug.entitlements";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "BGMAppTests/UITests/BGMAppUITests-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.AppUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
TEST_TARGET_NAME = "Background Music";
WARNING_CFLAGS = "";
};
@@ -1584,13 +1726,15 @@
1CCC4F5D1E584081008053E4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = "BGMApp/BGMApp-Debug.entitlements";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "BGMAppTests/UITests/BGMAppUITests-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.AppUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
TEST_TARGET_NAME = "Background Music";
WARNING_CFLAGS = "";
};
@@ -1599,14 +1743,16 @@
1CCC4F5E1E584081008053E4 /* DebugOpt */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = "BGMApp/BGMApp-Debug.entitlements";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "BGMAppTests/UITests/BGMAppUITests-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.11;
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.AppUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
TEST_TARGET_NAME = "Background Music";
WARNING_CFLAGS = "";
};
@@ -1615,6 +1761,7 @@
27379B991C7F57DB0084A24C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COPY_PHASE_STRIP = NO;
DEPLOYMENT_POSTPROCESSING = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -1628,12 +1775,14 @@
INSTALL_PATH = /usr/local/libexec;
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.XPCHelper;
PRODUCT_NAME = BGMXPCHelper;
STRIP_INSTALLED_PRODUCT = NO;
};
name = Debug;
};
27379B9A1C7F57DB0084A24C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COPY_PHASE_STRIP = NO;
DEPLOYMENT_POSTPROCESSING = YES;
INFOPLIST_FILE = BGMXPCHelper/Info.plist;
INSTALL_GROUP = wheel;
@@ -1642,12 +1791,14 @@
LLVM_LTO = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.XPCHelper;
PRODUCT_NAME = BGMXPCHelper;
STRIP_INSTALLED_PRODUCT = NO;
};
name = Release;
};
27379B9B1C7F57DB0084A24C /* DebugOpt */ = {
isa = XCBuildConfiguration;
buildSettings = {
COPY_PHASE_STRIP = NO;
DEPLOYMENT_POSTPROCESSING = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -1661,6 +1812,7 @@
INSTALL_PATH = /usr/local/libexec;
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.XPCHelper;
PRODUCT_NAME = BGMXPCHelper;
STRIP_INSTALLED_PRODUCT = NO;
};
name = DebugOpt;
};
@@ -1672,11 +1824,25 @@
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = NO;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"CoreAudio_Debug=1",
"CoreAudio_UseSysLog=1",
"CoreAudio_StopOnAssert=1",
"CoreAudio_ThreadStampMessages=0",
"BGM_StopDebuggerOnLoggedExceptions=$(BGM_STOP_DEBUGGER_ON_LOGGED_EXCEPTIONS)",
"BGM_StopDebuggerOnLoggedUnexpectedExceptions=$(BGM_STOP_DEBUGGER_ON_LOGGED_UNEXPECTED_EXCEPTIONS)",
"CoreAudio_StopOnThrow=0",
"BGM_UnitTest=1",
);
INFOPLIST_FILE = "BGMAppTests/UnitTests/BGMAppUnitTests-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.AppUnitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
WARNING_CFLAGS = "";
};
name = Debug;
@@ -1689,11 +1855,23 @@
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = NO;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=0",
"CoreAudio_Debug=0",
"CoreAudio_StopOnAssert=0",
"BGM_StopDebuggerOnLoggedExceptions=$(BGM_STOP_DEBUGGER_ON_LOGGED_EXCEPTIONS)",
"BGM_StopDebuggerOnLoggedUnexpectedExceptions=$(BGM_STOP_DEBUGGER_ON_LOGGED_UNEXPECTED_EXCEPTIONS)",
"CoreAudio_StopOnThrow=0",
"BGM_UnitTest=1",
);
INFOPLIST_FILE = "BGMAppTests/UnitTests/BGMAppUnitTests-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.AppUnitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
WARNING_CFLAGS = "";
};
name = Release;
@@ -1706,11 +1884,25 @@
CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = NO;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
DEVELOPMENT_TEAM = "";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"CoreAudio_Debug=1",
"CoreAudio_UseSysLog=1",
"CoreAudio_StopOnAssert=1",
"CoreAudio_ThreadStampMessages=0",
"BGM_StopDebuggerOnLoggedExceptions=$(BGM_STOP_DEBUGGER_ON_LOGGED_EXCEPTIONS)",
"BGM_StopDebuggerOnLoggedUnexpectedExceptions=$(BGM_STOP_DEBUGGER_ON_LOGGED_UNEXPECTED_EXCEPTIONS)",
"CoreAudio_StopOnThrow=0",
"BGM_UnitTest=1",
);
INFOPLIST_FILE = "BGMAppTests/UnitTests/BGMAppUnitTests-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.AppUnitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
WARNING_CFLAGS = "";
};
name = DebugOpt;
@@ -26,9 +26,42 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
enableAddressSanitizer = "YES"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
enableASanStackUseAfterReturn = "YES"
enableUBSanitizer = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "27379B8E1C7F57DA0084A24C"
BuildableName = "BGMXPCHelper.xpc"
BlueprintName = "BGMXPCHelper"
ReferencedContainer = "container:BGMApp.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocScribble"
value = ""
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "27379B8E1C7F57DA0084A24C"
BuildableName = "BGMXPCHelper.xpc"
BlueprintName = "BGMXPCHelper"
ReferencedContainer = "container:BGMApp.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
@@ -41,24 +74,14 @@
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "27379B8E1C7F57DA0084A24C"
BuildableName = "BGMXPCHelper.xpc"
BlueprintName = "BGMXPCHelper"
ReferencedContainer = "container:BGMApp.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableAddressSanitizer = "YES"
language = ""
enableASanStackUseAfterReturn = "YES"
enableUBSanitizer = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@@ -82,6 +105,16 @@
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocScribble"
value = ""
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
@@ -26,11 +26,47 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
enableAddressSanitizer = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
enableASanStackUseAfterReturn = "YES"
enableUBSanitizer = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1CB8B3351BBA75EF000E2DD1"
BuildableName = "Background Music.app"
BlueprintName = "Background Music"
ReferencedContainer = "container:BGMApp.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocScribble"
value = ""
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1CB8B3351BBA75EF000E2DD1"
BuildableName = "Background Music.app"
BlueprintName = "Background Music"
ReferencedContainer = "container:BGMApp.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
skipped = "NO"
parallelizable = "YES"
testExecutionOrdering = "random">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2743C9F51D86CFF90089613B"
@@ -50,23 +86,14 @@
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1CB8B3351BBA75EF000E2DD1"
BuildableName = "Background Music.app"
BlueprintName = "Background Music"
ReferencedContainer = "container:BGMApp.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableAddressSanitizer = "YES"
enableASanStackUseAfterReturn = "YES"
enableUBSanitizer = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@@ -101,6 +128,16 @@
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocScribble"
value = ""
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<!--
Without this key, AddressSanitizer and the UI tests would only work when BGMApp is code signed.
-->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
</dict>
</plist>
+6 -4
View File
@@ -17,17 +17,17 @@
// BGMAppDelegate.h
// BGMApp
//
// Copyright © 2016, 2017 Kyle Neideck
// Copyright © 2016, 2017, 2020 Kyle Neideck
// Copyright © 2021 Marcus Wu
//
// Sets up and tears down the app.
//
// Local Includes
#import "BGMAudioDeviceManager.h"
// System Includes
#import <Cocoa/Cocoa.h>
@class BGMAudioDeviceManager;
@class BGMAppVolumesController;
// Tags for UI elements in MainMenu.xib
static NSInteger const kVolumesHeadingMenuItemTag = 3;
@@ -50,8 +50,10 @@ static NSInteger const kSeparatorBelowVolumesMenuItemTag = 4;
@property (unsafe_unretained) IBOutlet NSTextView* aboutPanelLicenseView;
@property (weak) IBOutlet NSMenuItem* autoPauseMenuItemUnwrapped;
@property (weak) IBOutlet NSMenuItem* debugLoggingMenuItemUnwrapped;
@property (readonly) BGMAudioDeviceManager* audioDevices;
@property BGMAppVolumesController* appVolumes;
@end
+125 -52
View File
@@ -17,7 +17,8 @@
// BGMAppDelegate.mm
// BGMApp
//
// Copyright © 2016-2019 Kyle Neideck
// Copyright © 2016-2022 Kyle Neideck
// Copyright © 2021 Marcus Wu
//
// Self Include
@@ -25,9 +26,11 @@
// Local Includes
#import "BGM_Utils.h"
#import "BGMAppVolumes.h"
#import "BGMAppVolumesController.h"
#import "BGMAutoPauseMusic.h"
#import "BGMAutoPauseMenuItem.h"
#import "BGMDebugLoggingMenuItem.h"
#import "BGMMusicPlayers.h"
#import "BGMOutputDeviceMenuSection.h"
#import "BGMOutputVolumeMenuItem.h"
@@ -63,14 +66,15 @@ static NSString* const kOptShowDockIcon = @"--show-dock-icon";
BGMAutoPauseMenuItem* autoPauseMenuItem;
BGMMusicPlayers* musicPlayers;
BGMSystemSoundsVolume* systemSoundsVolume;
BGMAppVolumesController* appVolumes;
BGMOutputDeviceMenuSection* outputDeviceMenuSection;
BGMPreferencesMenu* prefsMenu;
BGMDebugLoggingMenuItem* debugLoggingMenuItem;
BGMXPCListener* xpcListener;
BGMPreferredOutputDevices* preferredOutputDevices;
}
@synthesize audioDevices = audioDevices;
@synthesize appVolumes = appVolumes;
- (void) awakeFromNib {
[super awakeFromNib];
@@ -115,6 +119,48 @@ static NSString* const kOptShowDockIcon = @"--show-dock-icon";
preferredOutputDevices =
[[BGMPreferredOutputDevices alloc] initWithDevices:audioDevices userDefaults:userDefaults];
// Skip this if we're compiling on a version of macOS before 10.14 as won't compile and it
// isn't needed.
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 // MAC_OS_X_VERSION_10_14
if (@available(macOS 10.14, *)) {
// On macOS 10.14+ we need to get the user's permission to use input devices before we can
// use BGMDevice for playthrough (see BGMPlayThrough), so we wait until they've given it
// before making BGMDevice the default device. This way, if the user is playing audio when
// they open Background Music, we won't interrupt it while we're waiting for them to click
// OK.
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted) {
dispatch_async(dispatch_get_main_queue(), ^{
if (granted) {
DebugMsg("BGMAppDelegate::applicationDidFinishLaunching: Permission granted");
[self continueLaunchAfterInputDevicePermissionGranted];
} else {
NSLog(@"BGMAppDelegate::applicationDidFinishLaunching: Permission denied");
// If they don't accept, Background Music won't work at all and the only way to
// fix it is in System Preferences, so show an error dialog with instructions.
//
// TODO: It would be nice if this dialog had a shortcut to open the System
// Preferences panel. See showSetDeviceAsDefaultError.
[self showErrorMessage:@"Background Music needs permission to use microphones."
informativeText:@"It uses a virtual microphone to access your system's "
"audio.\n\nYou can grant the permission by going to "
"System Preferences > Security and Privacy > "
"Microphone and checking the box for Background Music."
exitAfterMessageDismissed:YES];
}
});
}];
}
else
#endif
{
// We can change the device immediately on older versions of macOS because they don't
// require user permission for input devices.
[self continueLaunchAfterInputDevicePermissionGranted];
}
}
- (void) continueLaunchAfterInputDevicePermissionGranted {
// Choose an output device for BGMApp to use to play audio.
if (![self setInitialOutputDevice]) {
return;
@@ -132,16 +178,16 @@ static NSString* const kOptShowDockIcon = @"--show-dock-icon";
autoPauseMusic = [[BGMAutoPauseMusic alloc] initWithAudioDevices:audioDevices
musicPlayers:musicPlayers];
[self setUpMainMenu];
xpcListener = [[BGMXPCListener alloc] initWithAudioDevices:audioDevices
helperConnectionErrorHandler:^(NSError* error) {
NSLog(@"BGMAppDelegate::applicationDidFinishLaunching: (helperConnectionErrorHandler) "
"BGMXPCHelper connection error: %@",
error);
[self showXPCHelperErrorMessage:error];
}];
NSLog(@"BGMAppDelegate::continueLaunchAfterInputDevicePermissionGranted: "
"(helperConnectionErrorHandler) BGMXPCHelper connection error: %@",
error);
[self showXPCHelperErrorMessage:error];
}];
}
// Returns NO if (and only if) BGMApp is about to terminate because of a fatal error.
@@ -179,51 +225,73 @@ static NSString* const kOptShowDockIcon = @"--show-dock-icon";
// Sets the "Background Music" virtual audio device (BGMDevice) as the user's default audio device.
- (void) setBGMDeviceAsDefault {
void (^setDefaultDevice)() = ^{
NSError* error = [audioDevices setBGMDeviceAsOSDefault];
NSError* error = [audioDevices setBGMDeviceAsOSDefault];
if (error) {
[self showSetDeviceAsDefaultError:error
message:@"Could not set the Background Music device as your"
"default audio device."
informativeText:@"You might be able to change it yourself."];
}
};
// Skip this if we're compiling on a version of macOS before 10.14 as won't compile and it
// isn't needed.
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 // MAC_OS_X_VERSION_10_14
if (@available(macOS 10.14, *)) {
// On macOS 10.14+ we need to get the user's permission to use input devices before we can
// use BGMDevice for playthrough (see BGMPlayThrough), so we wait until they've given it
// before making BGMDevice the default device. This way, if the user is playing audio when
// they open Background Music, we won't interrupt it while we're waiting for them to click
// OK.
//
// TODO: This isn't a perfect solution because, if the user takes too long to accept,
// BGMPlayThrough will try to use BGMDevice again and log some errors.
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio
completionHandler:^(BOOL granted) {
if (granted) {
DebugMsg("BGMAppDelegate::setBGMDeviceAsDefault: "
"Permission granted");
setDefaultDevice();
} else {
NSLog(@"BGMAppDelegate::setBGMDeviceAsDefault: "
"Permission denied");
// TODO: If they don't accept, Background Music won't work
// at all and the only way to fix it is in System
// Preferences, so we should show an error dialog
// with instructions.
}
}];
if (error) {
[self showSetDeviceAsDefaultError:error
message:@"Could not set the Background Music device as your"
"default audio device."
informativeText:@"You might be able to change it yourself."];
}
else
#endif
{
// We can change the device immediately on older versions of macOS because they don't
// require user permission for input devices.
setDefaultDevice();
}
- (void) menuWillOpen:(NSMenu*)menu {
if (@available(macOS 10.16, *)) {
// Set menu offset and check for any active menu items
float menuOffset = 12.0;
for (NSMenuItem* menuItem in self.bgmMenu.itemArray) {
if (menuItem.state == NSControlStateValueOn && menuItem.indentationLevel == 0) {
menuOffset += 10;
break;
}
}
// Align volume output device and slider
for (NSView* subview in self.outputVolumeView.subviews) {
CGRect newSubview = subview.frame;
newSubview.origin.x = menuOffset;
subview.frame = newSubview;
}
// Align system sounds and app volumes
double appIconTitleOffset = 0;
for (NSMenuItem* menuItem in self.bgmMenu.itemArray) {
if (menuItem.view.subviews.count == 7 || menuItem.view.subviews.count == 3) {
NSTextField* appTitle;
NSImageView* appIcon;
for (NSView* subview in menuItem.view.subviews) {
if (menuItem.view.subviews.count == 3) {
// System sounds
if ([subview isKindOfClass:[NSTextField class]]) {
appTitle = (NSTextField*)subview;
}
if ([subview isKindOfClass:[NSImageView class]]) {
appIcon = (NSImageView*)subview;
}
} else if (menuItem.view.subviews.count == 7) {
// App volumes
if ([subview isKindOfClass:[BGMAVM_AppNameLabel class]]) {
appTitle = (NSTextField*)subview;
}
if ([subview isKindOfClass:[BGMAVM_AppIcon class]]) {
appIcon = (NSImageView*)subview;
}
}
}
if (appIconTitleOffset == 0) {
appIconTitleOffset = appTitle.frame.origin.x - appIcon.frame.origin.x;
}
CGRect newAppIcon = appIcon.frame;
newAppIcon.origin.x = menuOffset;
appIcon.frame = newAppIcon;
CGRect newAppTitle = appTitle.frame;
newAppTitle.origin.x = menuOffset + appIconTitleOffset;
appTitle.frame = newAppTitle;
}
}
}
}
@@ -251,6 +319,11 @@ static NSString* const kOptShowDockIcon = @"--show-dock-icon";
aboutPanel:self.aboutPanel
aboutPanelLicenseView:self.aboutPanelLicenseView];
// Enable/disable debug logging. Hidden unless you option-click the status bar icon.
debugLoggingMenuItem =
[[BGMDebugLoggingMenuItem alloc] initWithMenuItem:self.debugLoggingMenuItemUnwrapped];
[statusBarItem setDebugLoggingMenuItem:debugLoggingMenuItem];
// Handle events about the main menu. (See the NSMenuDelegate methods below.)
self.bgmMenu.delegate = self;
}
+5 -1
View File
@@ -18,6 +18,7 @@
// BGMApp
//
// Copyright © 2016, 2017 Kyle Neideck
// Copyright © 2021 Marcus Wu
//
// Local Includes
@@ -35,7 +36,7 @@
bgmMenu:(NSMenu*)inMenu
appVolumeView:(NSView*)inView;
// Pass -1 for initialVolume or initialPan to leave the volume/pan at its default level.
// Pass -1 for initialVolume or kAppPanNoValue for initialPan to leave the volume/pan at its default level.
- (void) insertMenuItemForApp:(NSRunningApplication*)app
initialVolume:(int)volume
initialPan:(int)pan;
@@ -44,6 +45,9 @@
- (void) removeAllAppVolumeMenuItems;
- (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication*)app;
- (void) setVolumeAndPan:(BGMAppVolumeAndPan)volumeAndPan forApp:(NSRunningApplication*)app;
@end
// Protocol for the UI custom classes
+95 -9
View File
@@ -17,8 +17,10 @@
// BGMAppVolumes.m
// BGMApp
//
// Copyright © 2016-2018 Kyle Neideck
// Copyright © 2016-2020 Kyle Neideck
// Copyright © 2017 Andrew Tonner
// Copyright © 2021 Marcus Wu
// Copyright © 2022 Jon Egan
//
// Self Include
@@ -124,6 +126,79 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
}
}
- (NSMenuItem*) getMenuItemForApp:(NSRunningApplication*)app {
NSInteger lastAppVolumeMenuItemIndex = [self lastMenuItemIndex] - 2;
for (NSInteger i = [self firstMenuItemIndex]; i <= lastAppVolumeMenuItemIndex; i++) {
NSMenuItem* item = [bgmMenu itemAtIndex:i];
NSRunningApplication* itemApp = item.representedObject;
BGMAssert(itemApp, "!itemApp for %s", item.title.UTF8String);
if ([itemApp isEqual:app]) {
return item;
}
}
for (NSInteger i = 0; i < [moreAppsMenu numberOfItems]; i++) {
NSMenuItem* item = [moreAppsMenu itemAtIndex:i];
NSRunningApplication* itemApp = item.representedObject;
BGMAssert(itemApp, "!itemApp for %s", item.title.UTF8String);
if ([itemApp isEqual:app]) {
return item;
}
}
return nil;
}
- (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication*)app {
BGMAppVolumeAndPan result = {
.volume = -1,
.pan = kAppPanNoValue
};
NSMenuItem *item = [self getMenuItemForApp:app];
if (item == nil) {
return result;
}
for (NSView* subview in item.view.subviews) {
// Get the volume.
if ([subview isKindOfClass:[BGMAVM_VolumeSlider class]]) {
result.volume = [(BGMAVM_VolumeSlider*)subview intValue];
}
// Get the pan position.
if ([subview isKindOfClass:[BGMAVM_PanSlider class]]) {
result.pan = [(BGMAVM_PanSlider*)subview intValue];
}
}
return result;
}
- (void) setVolumeAndPan:(BGMAppVolumeAndPan)volumeAndPan forApp:(NSRunningApplication*)app {
NSMenuItem *item = [self getMenuItemForApp:app];
if (item == nil) {
return;
}
for (NSView* subview in item.view.subviews) {
// Set the volume.
if (volumeAndPan.volume != -1 && [subview isKindOfClass:[BGMAVM_VolumeSlider class]]) {
[(BGMAVM_VolumeSlider*)subview setRelativeVolume:volumeAndPan.volume];
}
// Set the pan position.
if (volumeAndPan.pan != kAppPanNoValue && [subview isKindOfClass:[BGMAVM_PanSlider class]]) {
[(BGMAVM_PanSlider*)subview setPanPosition:volumeAndPan.pan];
}
}
}
// Create a blank menu item to copy as a template.
- (NSMenuItem*) createBlankAppVolumeMenuItem {
NSMenuItem* menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
@@ -143,7 +218,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
}
// Set the pan position.
if (pan != -1 && [subview isKindOfClass:[BGMAVM_PanSlider class]]) {
if (pan != kAppPanNoValue && [subview isKindOfClass:[BGMAVM_PanSlider class]]) {
[(BGMAVM_PanSlider*)subview setPanPosition:pan];
}
}
@@ -196,20 +271,32 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
BGMAssert(button, "!button");
BGMAssert(menuItem, "!menuItem");
CGFloat width = menuItem.view.frame.size.width;
CGFloat height = menuItem.view.frame.size.height;
#if DEBUG
const char* appName = [((NSRunningApplication*)menuItem.representedObject).localizedName UTF8String];
CGFloat height = menuItem.view.frame.size.height;
#endif
const char* appName =
[((NSRunningApplication*)menuItem.representedObject).localizedName UTF8String];
// Using this function (instead of just ==) shouldn't be necessary, but just in case.
#if DEBUG
BOOL(^nearEnough)(CGFloat x, CGFloat y) = ^BOOL(CGFloat x, CGFloat y) {
return fabs(x - y) < 0.01; // We don't need much precision.
};
#endif
if (nearEnough(button.frameCenterRotation, 0.0)) {
bool allSubviewsShowing = true;
for (NSView* subview in menuItem.view.subviews) {
if (subview.hidden) {
allSubviewsShowing = false;
break;
}
//DebugMsg("BGMAppVolumes:: subview hash / hidden: (%lu) / (%hhd)", (unsigned long)subview.hash, subview.hidden);
}
if (allSubviewsShowing) {
// Hide extra controls
DebugMsg("BGMAppVolumes::showHideExtraControls: Hiding extra controls (%s)", appName);
@@ -241,7 +328,7 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
menuItem.view.frameSize = NSMakeSize(width, appVolumeViewFullHeight);
// Turn the button rightside up so the arrowhead points up.
button.frameCenterRotation = 0.0;
// Move the button down slightly, back to it's original position.
// Move the button down slightly, back to its original position.
[button setFrameOrigin:NSMakePoint(button.frame.origin.x, button.frame.origin.y + 1)];
// Set all of the UI elements in the menu item to "not hidden" for accessibility clients.
@@ -446,4 +533,3 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
}
@end
+9
View File
@@ -18,6 +18,7 @@
// BGMApp
//
// Copyright © 2017 Kyle Neideck
// Copyright © 2021 Marcus Wu
//
// Local Includes
@@ -29,6 +30,11 @@
#pragma clang assume_nonnull begin
typedef struct BGMAppVolumeAndPan {
int volume;
int pan;
} BGMAppVolumeAndPan;
@interface BGMAppVolumesController : NSObject
- (id) initWithMenu:(NSMenu*)menu
@@ -45,6 +51,9 @@ forAppWithProcessID:(pid_t)processID
forAppWithProcessID:(pid_t)processID
bundleID:(NSString* __nullable)bundleID;
- (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication *)app;
- (void) setVolumeAndPan:(BGMAppVolumeAndPan)volumeAndPan forApp:(NSRunningApplication*)app;
@end
#pragma clang assume_nonnull end
+16 -6
View File
@@ -19,6 +19,7 @@
//
// Copyright © 2017, 2018 Kyle Neideck
// Copyright © 2017 Andrew Tonner
// Copyright © 2021 Marcus Wu
//
// Self Include
@@ -40,11 +41,6 @@
#pragma clang assume_nonnull begin
typedef struct BGMAppVolumeAndPan {
int volume;
int pan;
} BGMAppVolumeAndPan;
@implementation BGMAppVolumesController {
// The App Volumes UI.
BGMAppVolumes* appVolumes;
@@ -104,11 +100,25 @@ typedef struct BGMAppVolumeAndPan {
}
}
- (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication *)app {
return [appVolumes getVolumeAndPanForApp:app];
}
- (void) setVolumeAndPan:(BGMAppVolumeAndPan)volumeAndPan forApp:(NSRunningApplication*)app {
[appVolumes setVolumeAndPan:volumeAndPan forApp:app];
if (volumeAndPan.volume != -1) {
[self setVolume:volumeAndPan.volume forAppWithProcessID:app.processIdentifier bundleID:app.bundleIdentifier];
}
if (volumeAndPan.pan != kAppPanNoValue) {
[self setPanPosition:volumeAndPan.pan forAppWithProcessID:app.processIdentifier bundleID:app.bundleIdentifier];
}
}
- (BGMAppVolumeAndPan) getVolumeAndPanForApp:(NSRunningApplication*)app
fromVolumes:(const CACFArray&)volumes {
BGMAppVolumeAndPan volumeAndPan = {
.volume = -1,
.pan = -1
.pan = kAppPanNoValue
};
for (UInt32 i = 0; i < volumes.GetNumberItems(); i++) {
+11 -6
View File
@@ -48,7 +48,7 @@ BGMAudioDevice::BGMAudioDevice(const CAHALAudioDevice& inDevice)
:
BGMAudioDevice(inDevice.GetObjectID())
{
};
}
BGMAudioDevice::~BGMAudioDevice()
{
@@ -57,6 +57,8 @@ BGMAudioDevice::~BGMAudioDevice()
bool BGMAudioDevice::CanBeOutputDeviceInBGMApp() const
{
CFStringRef uid = CopyDeviceUID();
assert(uid != nullptr);
bool isNullDevice = CFEqual(uid, CFSTR(kBGMNullDeviceUID));
CFRelease(uid);
@@ -81,7 +83,7 @@ bool BGMAudioDevice::HasSettableMasterVolume(AudioObjectPropertyScope inScope
bool BGMAudioDevice::HasSettableVirtualMasterVolume(AudioObjectPropertyScope inScope) const
{
AudioObjectPropertyAddress virtualMasterVolumeAddress = {
kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
kAudioHardwareServiceDeviceProperty_VirtualMainVolume,
inScope,
kAudioObjectPropertyElementMaster
};
@@ -220,7 +222,7 @@ bool BGMAudioDevice::GetVirtualMasterVolumeScalar(AudioObjectPropertyScope in
Float32& outVirtualMasterVolume) const
{
AudioObjectPropertyAddress virtualMasterVolumeAddress = {
kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
kAudioHardwareServiceDeviceProperty_VirtualMainVolume,
inScope,
kAudioObjectPropertyElementMaster
};
@@ -265,7 +267,7 @@ bool BGMAudioDevice::SetVirtualMasterVolumeScalar(AudioObjectPropertyScope in
bool didGetVirtualMasterBalance = GetVirtualMasterBalance(inScope, virtualMasterBalance);
AudioObjectPropertyAddress virtualMasterVolumeAddress = {
kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
kAudioHardwareServiceDeviceProperty_VirtualMainVolume,
inScope,
kAudioObjectPropertyElementMaster
};
@@ -277,7 +279,7 @@ bool BGMAudioDevice::SetVirtualMasterVolumeScalar(AudioObjectPropertyScope in
// Reset the balance
AudioObjectPropertyAddress virtualMasterBalanceAddress = {
kAudioHardwareServiceDeviceProperty_VirtualMasterBalance,
kAudioHardwareServiceDeviceProperty_VirtualMainBalance,
inScope,
kAudioObjectPropertyElementMaster
};
@@ -310,7 +312,7 @@ bool BGMAudioDevice::GetVirtualMasterBalance(AudioObjectPropertyScope inScope
Float32& outVirtualMasterBalance) const
{
AudioObjectPropertyAddress virtualMasterBalanceAddress = {
kAudioHardwareServiceDeviceProperty_VirtualMasterBalance,
kAudioHardwareServiceDeviceProperty_VirtualMainBalance,
inScope,
kAudioObjectPropertyElementMaster
};
@@ -340,6 +342,9 @@ bool BGMAudioDevice::IsBGMDevice(bool inIncludeUISoundsInstance) const
{
// Check the device's UID to see whether it's BGMDevice.
CFStringRef uid = CopyDeviceUID();
if (uid == nullptr) {
return isBGMDevice;
}
isBGMDevice =
CFEqual(uid, CFSTR(kBGMDeviceUID)) ||
+3 -2
View File
@@ -16,7 +16,7 @@
// BGMAudioDevice.h
// BGMApp
//
// Copyright © 2017 Kyle Neideck
// Copyright © 2017, 2020 Kyle Neideck
//
// A HAL audio device. Note that this class's only state is the AudioObjectID of the device.
//
@@ -59,7 +59,8 @@ public:
operator AudioObjectID() const { return GetObjectID(); }
/*!
@return True if this device is BGMDevice. (Specifically, the main instance of BGMDevice.)
@return True if this device is BGMDevice. (Specifically, the main instance of BGMDevice, not
the instance used for UI sounds.)
@throws CAException If the HAL returns an error when queried.
*/
bool IsBGMDevice() const { return IsBGMDevice(false); };
+8 -2
View File
@@ -378,8 +378,14 @@
@try {
gotLock = [stateLock tryLock];
if (gotLock) {
BOOL isBigSur = NO;
if (@available(macOS 11.0, *)) {
isBigSur = YES;
}
// Always start playthrough asynchronously. Temp workaround for deadlock on Big Sur.
if (!isBigSur && gotLock) {
BGMPlayThrough& pt = (forUISoundsDevice ? playThrough_UISounds : playThrough);
// Playthrough might not have been notified that BGMDevice is starting yet, so make sure
+8 -2
View File
@@ -17,7 +17,7 @@
// BGMBackgroundMusicDevice.cpp
// BGMApp
//
// Copyright © 2016, 2017 Kyle Neideck
// Copyright © 2016-2019 Kyle Neideck
// Copyright © 2017 Andrew Tonner
//
@@ -239,7 +239,13 @@ BGMBackgroundMusicDevice::ResponsibleBundleIDsOf(CACFString inParentBundleID)
// Discord
{ "com.hnc.Discord", { "com.hnc.Discord.helper" } },
// Skype
{ "com.skype.skype", { "com.skype.skype.Helper" } }
{ "com.skype.skype", { "com.skype.skype.Helper" } },
// Google Chrome
{ "com.google.Chrome", { "com.google.Chrome.helper" } },
// Microsoft Edge
{ "com.microsoft.edgemac", { "com.microsoft.edgemac.helper" } },
// Arc
{ "company.thebrowser.Browser", { "company.thebrowser.browser.helper" } }
};
// Parallels' VM "dock helper" apps have bundle IDs like
+48
View File
@@ -0,0 +1,48 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMDebugLoggingMenuItem.h
// BGMApp
//
// Copyright © 2020 Kyle Neideck
//
// A menu item in the main menu that enables/disables debug logging. Only visible if you hold the
// option down when you click the status bar icon to reveal the main menu.
//
// TODO: It would be better to have this menu item in the Preferences menu (maybe in an Advanced
// section) and always visible, but first we'd need to add something that tells the user how
// to view the log messages. Or better yet, something that automatically opens them.
//
// System Includes
#import <Cocoa/Cocoa.h>
#pragma clang assume_nonnull begin
@interface BGMDebugLoggingMenuItem : NSObject
- (instancetype) initWithMenuItem:(NSMenuItem*)menuItem;
// True if the main menu is showing hidden items/options because the user held the option key when
// they clicked the icon. This class makes the debug logging menu item visible if this property has
// been set true or if debug logging is enabled.
@property (nonatomic) BOOL menuShowingExtraOptions;
@end
#pragma clang assume_nonnull end
+73
View File
@@ -0,0 +1,73 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMDebugLoggingMenuItem.m
// BGMApp
//
// Copyright © 2020 Kyle Neideck
//
// Self Include
#import "BGMDebugLoggingMenuItem.h"
// PublicUtility Includes
#import "BGMDebugLogging.h"
#import "CADebugMacros.h"
#pragma clang assume_nonnull begin
@implementation BGMDebugLoggingMenuItem {
NSMenuItem* _menuItem;
BOOL _menuShowingExtraOptions;
}
- (instancetype) initWithMenuItem:(NSMenuItem*)menuItem {
if ((self = [super init])) {
_menuItem = menuItem;
_menuItem.state =
BGMDebugLoggingIsEnabled() ? NSControlStateValueOn : NSControlStateValueOff;
[self setMenuShowingExtraOptions:NO];
// Enable/disable debug logging when the menu item is clicked.
menuItem.target = self;
menuItem.action = @selector(toggleDebugLogging);
}
return self;
}
- (void) setMenuShowingExtraOptions:(BOOL)showingExtra {
_menuShowingExtraOptions = showingExtra;
_menuItem.hidden = !BGMDebugLoggingIsEnabled() && !showingExtra;
DebugMsg("BGMDebugLoggingMenuItem::menuShowingExtraOptions: %s the menu item",
_menuItem.hidden ? "Hiding" : "Showing");
}
- (void) toggleDebugLogging {
BGMSetDebugLoggingEnabled(!BGMDebugLoggingIsEnabled());
_menuItem.state = BGMDebugLoggingIsEnabled() ? NSControlStateValueOn : NSControlStateValueOff;
DebugMsg("BGMDebugLoggingMenuItem::toggleDebugLogging: Debug logging %s",
BGMDebugLoggingIsEnabled() ? "enabled" : "disabled");
}
@end
#pragma clang assume_nonnull end
+45 -28
View File
@@ -211,36 +211,53 @@ NSString* const __nonnull kGenericOutputDeviceName = @"Output Device";
// datasource, the device's name is set as this menu item's tooltip. Falls back to a generic name if
// the device returns an error when queried.
- (void) updateLabelAndToolTip {
BOOL didSetLabel = NO;
try {
if (outputDevice.HasDataSourceControl(kScope, kMasterChannel)) {
// The device has datasources, so use the current datasource's name like macOS does.
UInt32 dataSourceID = outputDevice.GetCurrentDataSourceID(kScope, kMasterChannel);
deviceLabel.stringValue =
(__bridge_transfer NSString*)outputDevice.CopyDataSourceNameForID(kScope,
kMasterChannel,
dataSourceID);
didSetLabel = YES; // So we know not to change the text if setting the tooltip fails.
// Set the tooltip of the menu item (the container) rather than the label because menu
// items' tooltips will still appear when a different app is focused and, as far as I
// know, BGMApp should never be the foreground app.
self.toolTip = (__bridge_transfer NSString*)outputDevice.CopyName();
} else {
deviceLabel.stringValue = (__bridge_transfer NSString*)outputDevice.CopyName();
self.toolTip = nil;
}
} catch (const CAException& e) {
BGMLogException(e);
// The device returned an error, so set the label to a generic device name, since we don't
// want to leave it set to the previous device's name.
if (outputDevice.GetObjectID() == kAudioObjectUnknown) {
DebugMsg("BGMOutputVolumeMenuItem::updateLabelAndToolTip: Output device unknown. Using the "
"generic label.");
self.toolTip = nil;
deviceLabel.stringValue = kGenericOutputDeviceName;
} else {
BOOL didSetLabel = NO;
if (!didSetLabel) {
deviceLabel.stringValue = kGenericOutputDeviceName;
DebugMsg("BGMOutputVolumeMenuItem::updateLabelAndToolTip: Output device: %u",
outputDevice.GetObjectID());
try {
if (outputDevice.HasDataSourceControl(kScope, kMasterChannel)) {
DebugMsg("BGMOutputVolumeMenuItem::updateLabelAndToolTip: Getting data source ID");
// The device has datasources, so use the current datasource's name like macOS does.
UInt32 dataSourceID = outputDevice.GetCurrentDataSourceID(kScope, kMasterChannel);
DebugMsg("BGMOutputVolumeMenuItem::updateLabelAndToolTip: "
"Getting name for data source %u",
dataSourceID);
deviceLabel.stringValue =
(__bridge_transfer NSString*)outputDevice.CopyDataSourceNameForID(
kScope, kMasterChannel, dataSourceID);
// So we know not to change the text if setting the tooltip fails.
didSetLabel = YES;
DebugMsg("BGMOutputVolumeMenuItem::updateLabelAndToolTip: Getting device name");
// Set the tooltip of the menu item (the container) rather than the label because
// menu items' tooltips will still appear when a different app is focused and, as
// far as I know, BGMApp should never be the foreground app.
self.toolTip = (__bridge_transfer NSString*)outputDevice.CopyName();
} else {
DebugMsg("BGMOutputVolumeMenuItem::updateLabelAndToolTip: Getting device name");
deviceLabel.stringValue = (__bridge_transfer NSString*)outputDevice.CopyName();
self.toolTip = nil;
}
} catch (const CAException& e) {
BGMLogException(e);
// The device returned an error, so set the label to a generic device name, since we
// don't want to leave it set to the previous device's name.
self.toolTip = nil;
if (!didSetLabel) {
deviceLabel.stringValue = kGenericOutputDeviceName;
}
}
}
+190 -124
View File
@@ -17,7 +17,7 @@
// BGMPlayThrough.cpp
// BGMApp
//
// Copyright © 2016, 2017 Kyle Neideck
// Copyright © 2016, 2017, 2020 Kyle Neideck
//
// Self Include
@@ -28,7 +28,6 @@
#include "BGM_Utils.h"
// PublicUtility Includes
#include "CAAtomic.h"
#include "CAHALAudioSystemObject.h"
#include "CAPropertyAddress.h"
@@ -57,9 +56,20 @@ BGMPlayThrough::BGMPlayThrough(BGMAudioDevice inInputDevice, BGMAudioDevice inOu
BGMPlayThrough::~BGMPlayThrough()
{
CAMutex::Locker stateLocker(mStateMutex);
BGMLogAndSwallowExceptionsMsg("BGMPlayThrough::~BGMPlayThrough", "Deactivate", [&]() {
Deactivate();
});
// If one of the IOProcs failed to stop, CoreAudio could (at least in theory) still call it
// after this point. This isn't a solution, but calling DeallocateBuffer instead of letting it
// deallocate itself should at least make the error less likely to cause a segfault, since
// DeallocateBuffer takes the buffer locks and sets mBuffer to null.
//
// TODO: It probably wouldn't be too hard to fix this properly by giving the IOProcs weak refs
// to the BGMPlayThrough object instead of raw pointers.
DeallocateBuffer();
if(mOutputDeviceIOProcSemaphore != SEMAPHORE_NULL)
{
@@ -97,7 +107,7 @@ void BGMPlayThrough::Init(BGMAudioDevice inInputDevice, BGMAudioDevice inOutp
catch (...)
{
// Clean up.
mBuffer.Deallocate();
DeallocateBuffer();
throw;
}
}
@@ -178,7 +188,7 @@ void BGMPlayThrough::Deactivate()
DebugMsg("BGMPlayThrough::Deactivate: Deactivating playthrough");
bool inputDeviceIsBGMDevice = true;
CATry
inputDeviceIsBGMDevice = mInputDevice.IsBGMDeviceInstance();
CACatch
@@ -231,13 +241,29 @@ void BGMPlayThrough::AllocateBuffer()
Throw(CAException(kAudioHardwareUnsupportedOperationError));
}
// Need to lock the buffer mutexes to make sure the IOProcs aren't accessing it. The order is
// important here. We always lock them in the same order to prevent deadlocks.
CAMutex::Locker lockerInput(mBufferInputMutex);
CAMutex::Locker lockerOutput(mBufferOutputMutex);
mBuffer = std::unique_ptr<CARingBuffer>(new CARingBuffer);
// The calculation for the size of the buffer is from Apple's CAPlayThrough.cpp sample code
//
// TODO: Test playthrough with hardware with more than 2 channels per frame, a sample (virtual) format other than
// 32-bit floats and/or an IO buffer size other than 512 frames
mBuffer.Allocate(outputFormat[0].mChannelsPerFrame,
outputFormat[0].mBytesPerFrame,
mOutputDevice.GetIOBufferSize() * 20);
mBuffer->Allocate(outputFormat[0].mChannelsPerFrame,
outputFormat[0].mBytesPerFrame,
mOutputDevice.GetIOBufferSize() * 20);
}
void BGMPlayThrough::DeallocateBuffer()
{
// Need to lock the buffer mutexes to make sure the IOProcs aren't accessing it. The order is
// important here. We always lock them in the same order to prevent deadlocks.
CAMutex::Locker lockerInput(mBufferInputMutex);
CAMutex::Locker lockerOutput(mBufferOutputMutex);
mBuffer = nullptr; // Note that the buffer's destructor will deallocate it.
}
void BGMPlayThrough::CreateIOProcIDs()
@@ -569,20 +595,21 @@ OSStatus BGMPlayThrough::WaitForOutputDeviceToStart() noexcept
while((theError != KERN_SUCCESS) && // Signalled from the IOProc.
(state == IOState::Starting) && // IO state changed.
(waitedNsec < kStartIOTimeoutNsec)); // Timed out.
#if DEBUG
UInt64 startedBy = mach_absolute_time();
struct mach_timebase_info baseInfo = { 0, 0 };
mach_timebase_info(&baseInfo);
UInt64 base = baseInfo.numer / baseInfo.denom;
DebugMsg("BGMPlayThrough::WaitForOutputDeviceToStart: Started %f ms after notification, %f ms "
"after entering WaitForOutputDeviceToStart.",
static_cast<Float64>(startedBy - mToldOutputDeviceToStartAt) * base / NSEC_PER_MSEC,
static_cast<Float64>(startedBy - startedAt) * base / NSEC_PER_MSEC);
#endif
if(BGMDebugLoggingIsEnabled())
{
UInt64 startedBy = mach_absolute_time();
struct mach_timebase_info baseInfo = { 0, 0 };
mach_timebase_info(&baseInfo);
UInt64 base = baseInfo.numer / baseInfo.denom;
DebugMsg("BGMPlayThrough::WaitForOutputDeviceToStart: Started %f ms after notification, %f "
"ms after entering WaitForOutputDeviceToStart.",
static_cast<Float64>(startedBy - mToldOutputDeviceToStartAt) * base / NSEC_PER_MSEC,
static_cast<Float64>(startedBy - startedAt) * base / NSEC_PER_MSEC);
}
// Figure out which error code to return.
switch (theError)
{
@@ -604,7 +631,7 @@ OSStatus BGMPlayThrough::WaitForOutputDeviceToStart() noexcept
// Release any threads waiting for the output device to start. This function doesn't take mStateMutex
// because it gets called on the IO thread, which is realtime priority.
void BGMPlayThrough::ReleaseThreadsWaitingForOutputToStart() const
void BGMPlayThrough::ReleaseThreadsWaitingForOutputToStart()
{
if(mActive)
{
@@ -612,13 +639,10 @@ void BGMPlayThrough::ReleaseThreadsWaitingForOutputToStart() const
if(semaphore != SEMAPHORE_NULL)
{
DebugMsg("BGMPlayThrough::ReleaseThreadsWaitingForOutputToStart: Releasing waiting threads");
mRTLogger.LogReleasingWaitingThreads();
kern_return_t theError = semaphore_signal_all(semaphore);
// TODO: Tell another thread to log this error, since we might be on a realtime thread.
BGM_Utils::LogIfMachError("BGMPlayThrough::ReleaseThreadsWaitingForOutputToStart",
"semaphore_signal_all",
theError);
mRTLogger.LogIfMachError_ReleaseWaitingThreadsSignal(theError);
}
}
}
@@ -867,12 +891,15 @@ void BGMPlayThrough::HandleBGMDeviceIsRunning(BGMPlayThrough* refCon)
isRunningSomewhereOtherThanBGMApp =
IsRunningSomewhereOtherThanBGMApp(refCon->mInputDevice);
});
DebugMsg("BGMPlayThrough::HandleBGMDeviceIsRunning: "
"BGMDevice is %srunning somewhere other than BGMApp",
isRunningSomewhereOtherThanBGMApp ? "" : "not ");
if(isRunningSomewhereOtherThanBGMApp)
{
#if DEBUG
refCon->mToldOutputDeviceToStartAt = mach_absolute_time();
#endif
// TODO: Handle expected exceptions (mostly CAExceptions from PublicUtility classes) in Start.
// For any that can't be handled sensibly in Start, catch them here and retry a few
// times (with a very short delay) before handling them by showing an unobtrusive error
@@ -906,15 +933,14 @@ void BGMPlayThrough::HandleBGMDeviceIsRunningSomewhereOtherThanBGMApp(BGMPlay
// static
bool BGMPlayThrough::IsRunningSomewhereOtherThanBGMApp(const BGMAudioDevice& inBGMDevice)
{
return CFBooleanGetValue(
static_cast<CFBooleanRef>(
inBGMDevice.GetPropertyData_CFType(kBGMRunningSomewhereOtherThanBGMAppAddress)));
auto type = inBGMDevice.GetPropertyData_CFType(kBGMRunningSomewhereOtherThanBGMAppAddress);
return type && CFBooleanGetValue(static_cast<CFBooleanRef>(type));
}
#pragma mark IOProcs
// Note that the IOProcs will very likely not run on the same thread and that they intentionally don't
// lock any mutexes.
// Note that the IOProcs will very likely not run on the same thread and that they intentionally
// only lock mutexes around their use of mBuffer.
// static
OSStatus BGMPlayThrough::InputDeviceIOProc(AudioObjectID inDevice,
@@ -932,6 +958,7 @@ OSStatus BGMPlayThrough::InputDeviceIOProc(AudioObjectID inDevice,
IOState state;
UpdateIOProcState("InputDeviceIOProc",
refCon->mRTLogger,
refCon->mInputDeviceIOProcState,
refCon->mInputDeviceIOProcID,
refCon->mInputDevice,
@@ -952,16 +979,30 @@ OSStatus BGMPlayThrough::InputDeviceIOProc(AudioObjectID inDevice,
UInt32 framesToStore = inInputData->mBuffers[0].mDataByteSize / (SizeOf32(Float32) * 2);
CARingBufferError err =
refCon->mBuffer.Store(inInputData,
framesToStore,
static_cast<CARingBuffer::SampleTime>(inInputTime->mSampleTime));
HandleRingBufferError(err, "InputDeviceIOProc", "mBuffer.Store");
CAMemoryBarrier();
refCon->mLastInputSampleTime = inInputTime->mSampleTime;
// See the comments in OutputDeviceIOProc where it locks mBufferOutputMutex.
CAMutex::Tryer tryer(refCon->mBufferInputMutex);
// Disable a warning about accessing mBuffer without holding both mBufferInputMutex and
// mBufferOutputMutex. Explained further in OutputDeviceIOProc.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wthread-safety"
if(tryer.HasLock() && refCon->mBuffer)
{
CARingBufferError err =
refCon->mBuffer->Store(inInputData,
framesToStore,
static_cast<CARingBuffer::SampleTime>(
inInputTime->mSampleTime));
#pragma clang diagnostic pop
refCon->mRTLogger.LogIfRingBufferError_Store(err);
refCon->mLastInputSampleTime = inInputTime->mSampleTime;
}
else
{
refCon->mRTLogger.LogRingBufferUnavailable("InputDeviceIOProc", tryer.HasLock());
}
return noErr;
}
@@ -974,13 +1015,14 @@ OSStatus BGMPlayThrough::OutputDeviceIOProc(AudioObjectID inDevice,
const AudioTimeStamp* inOutputTime,
void* __nullable inClientData)
{
#pragma unused (inDevice, inNow, inInputData, inInputTime, inOutputTime)
#pragma unused (inDevice, inNow, inInputData, inInputTime)
// refCon (reference context) is the instance that created the IOProc
BGMPlayThrough* const refCon = static_cast<BGMPlayThrough*>(inClientData);
IOState state;
const bool didChangeState = UpdateIOProcState("OutputDeviceIOProc",
refCon->mRTLogger,
refCon->mOutputDeviceIOProcState,
refCon->mOutputDeviceIOProcID,
refCon->mOutputDevice,
@@ -989,6 +1031,7 @@ OSStatus BGMPlayThrough::OutputDeviceIOProc(AudioObjectID inDevice,
if(state == IOState::Stopped || state == IOState::Stopping)
{
// Return early, since we just asked to stop. (Or something really weird is going on.)
FillWithSilence(outOutputData);
return noErr;
}
@@ -1008,8 +1051,7 @@ OSStatus BGMPlayThrough::OutputDeviceIOProc(AudioObjectID inDevice,
if(refCon->mLastInputSampleTime == -1)
{
// Return early, since we don't have any data to output yet.
//
// TODO: Write silence to outOutputData here
FillWithSilence(outOutputData);
return noErr;
}
@@ -1020,13 +1062,8 @@ OSStatus BGMPlayThrough::OutputDeviceIOProc(AudioObjectID inDevice,
refCon->mInToOutSampleOffset = inOutputTime->mSampleTime - refCon->mLastInputSampleTime;
// Log if we dropped frames
if(refCon->mFirstInputSampleTime != refCon->mLastInputSampleTime)
{
DebugMsg("BGMPlayThrough::OutputDeviceIOProc: Dropped %f frames before output started. %s%f %s%f",
(refCon->mLastInputSampleTime - refCon->mFirstInputSampleTime),
"mFirstInputSampleTime=", refCon->mFirstInputSampleTime,
"mLastInputSampleTime=", refCon->mLastInputSampleTime);
}
refCon->mRTLogger.LogIfDroppedFrames(refCon->mFirstInputSampleTime,
refCon->mLastInputSampleTime);
}
CARingBuffer::SampleTime readHeadSampleTime =
@@ -1035,47 +1072,93 @@ OSStatus BGMPlayThrough::OutputDeviceIOProc(AudioObjectID inDevice,
static_cast<CARingBuffer::SampleTime>(refCon->mLastInputSampleTime);
UInt32 framesToOutput = outOutputData->mBuffers[0].mDataByteSize / (SizeOf32(Float32) * 2);
// Very occasionally (at least for me) our read head gets ahead of input, i.e. we haven't received any new input since
// this IOProc was last called, and we have to recalculate its position. I figure this might be caused by clock drift
// but I'm really not sure. It also happens if the input or output sample times are restarted from zero.
//
// We also recalculate the offset if the read head is outside of the ring buffer. This happens for example when you plug
// in or unplug headphones, which causes the output sample times to be restarted from zero.
//
// The vast majority of the time, just using lastInputSampleTime as the read head time instead of the one we calculate
// would work fine (and would also account for the above).
SInt64 bufferStartTime, bufferEndTime;
CARingBufferError err = refCon->mBuffer.GetTimeBounds(bufferStartTime, bufferEndTime);
bool outOfBounds = false;
if(err == kCARingBufferError_OK)
{
outOfBounds = (readHeadSampleTime < bufferStartTime) || (readHeadSampleTime - framesToOutput > bufferEndTime);
}
if(lastInputSampleTime < readHeadSampleTime || outOfBounds)
{
DebugMsg("BGMPlayThrough::OutputDeviceIOProc: No input samples ready at output sample time. %s%lld %s%lld %s%f",
"lastInputSampleTime=", lastInputSampleTime,
"readHeadSampleTime=", readHeadSampleTime,
"mInToOutSampleOffset=", refCon->mInToOutSampleOffset);
// Recalculate the in-to-out offset and read head
refCon->mInToOutSampleOffset = inOutputTime->mSampleTime - lastInputSampleTime;
readHeadSampleTime = static_cast<CARingBuffer::SampleTime>(inOutputTime->mSampleTime - refCon->mInToOutSampleOffset);
}
// Copy the frames from the ring buffer
err = refCon->mBuffer.Fetch(outOutputData, framesToOutput, readHeadSampleTime);
HandleRingBufferError(err, "OutputDeviceIOProc", "mBuffer.Fetch");
// When the input and output devices are set, during start up or because the user changed the
// output device, this class (re)allocates the ring buffer (mBuffer). We try to take this
// lock before accessing the buffer to make sure it's allocated.
//
// If we don't get the lock, another thread must be allocating or deallocating it, so we just
// give up. We can't avoid audio glitches while changing devices anyway. This class tries to
// make sure the IOProcs aren't running when it allocates the buffer, but it can't guarantee
// that.
//
// Note that this is only realtime safe because we only try to lock the mutex. If another
// thread has the mutex, it will be a non-realtime thread, so we can't wait for it.
CAMutex::Tryer tryer(refCon->mBufferOutputMutex);
// Disable a warning about accessing mBuffer without holding both mBufferInputMutex and
// mBufferOutputMutex. The input IOProc always writes ahead of where the output IOProc will read
// in a given IO cycle, so it's safe for them to read and write at the same time.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wthread-safety"
if(tryer.HasLock() && refCon->mBuffer)
{
// Very occasionally (at least for me) our read head gets ahead of input, i.e. we haven't
// received any new input since this IOProc was last called, and we have to recalculate its
// position. I figure this might be caused by clock drift but I'm really not sure. It also
// happens if the input or output sample times are restarted from zero.
//
// We also recalculate the offset if the read head is outside of the ring buffer. This
// happens for example when you plug in or unplug headphones, which causes the output sample
// times to be restarted from zero.
//
// The vast majority of the time, just using lastInputSampleTime as the read head time
// instead of the one we calculate would work fine (and would also account for the above).
SInt64 bufferStartTime, bufferEndTime;
CARingBufferError err = refCon->mBuffer->GetTimeBounds(bufferStartTime, bufferEndTime);
bool outOfBounds = false;
if(err == kCARingBufferError_OK)
{
outOfBounds = (readHeadSampleTime < bufferStartTime)
|| (readHeadSampleTime - framesToOutput > bufferEndTime);
}
if(lastInputSampleTime < readHeadSampleTime || outOfBounds)
{
refCon->mRTLogger.LogNoSamplesReady(lastInputSampleTime,
readHeadSampleTime,
refCon->mInToOutSampleOffset);
// Recalculate the in-to-out offset and read head.
refCon->mInToOutSampleOffset = inOutputTime->mSampleTime - lastInputSampleTime;
readHeadSampleTime = static_cast<CARingBuffer::SampleTime>(
inOutputTime->mSampleTime - refCon->mInToOutSampleOffset);
}
// Copy the frames from the ring buffer.
err = refCon->mBuffer->Fetch(outOutputData, framesToOutput, readHeadSampleTime);
refCon->mRTLogger.LogIfRingBufferError_Fetch(err);
if(err != kCARingBufferError_OK)
{
FillWithSilence(outOutputData);
}
}
else
{
refCon->mRTLogger.LogRingBufferUnavailable("OutputDeviceIOProc", tryer.HasLock());
FillWithSilence(outOutputData);
}
#pragma clang diagnostic pop
refCon->mLastOutputSampleTime = inOutputTime->mSampleTime;
return noErr;
}
// static
bool BGMPlayThrough::UpdateIOProcState(const char* __nullable callerName,
inline void BGMPlayThrough::FillWithSilence(AudioBufferList* ioBuffer)
{
for(UInt32 i = 0; i < ioBuffer->mNumberBuffers; i++)
{
memset(ioBuffer->mBuffers[i].mData, 0, ioBuffer->mBuffers[i].mDataByteSize);
}
}
// static
bool BGMPlayThrough::UpdateIOProcState(const char* inCallerName,
BGMPlayThroughRTLogger& inRTLogger,
std::atomic<IOState>& inState,
AudioDeviceIOProcID __nullable inIOProcID,
BGMAudioDevice& inDevice,
@@ -1088,6 +1171,9 @@ bool BGMPlayThrough::UpdateIOProcState(const char* __nullable callerName,
//
// compare_exchange_strong will return true iff it changed inState from Starting to Running.
// Otherwise it will set prevState to the current value of inState.
//
// TODO: We probably don't actually need memory_order_seq_cst (the default). Would it be worth
// changing? Might be worth checking for the other atomics/barriers in this class, too.
IOState prevState = IOState::Starting;
bool didChangeState = inState.compare_exchange_strong(prevState, IOState::Running);
@@ -1105,21 +1191,28 @@ bool BGMPlayThrough::UpdateIOProcState(const char* __nullable callerName,
{
// The IOProc isn't Starting or Running, so it must be Stopping. That is, it's been
// told to stop itself.
BGMAssert(outNewState == IOState::Stopping,
"BGMPlayThrough::UpdateIOProcState: Unexpected state: %d",
outNewState);
bool stoppedSuccessfully = false;
BGMLogAndSwallowExceptionsMsg("BGMPlayThrough::UpdateIOProcState", callerName, [&]() {
// TODO: If this throws, tell another thread to log the exception rather than
// logging it from a real-time thread.
try
{
inDevice.StopIOProc(inIOProcID);
// StopIOProc didn't throw, so the IOProc won't be called again until the next
// time playthrough is started.
stoppedSuccessfully = true;
});
}
catch(CAException e)
{
inRTLogger.LogExceptionStoppingIOProc(inCallerName, e.GetError());
}
catch(...)
{
inRTLogger.LogExceptionStoppingIOProc(inCallerName);
}
if(stoppedSuccessfully)
{
@@ -1132,7 +1225,7 @@ bool BGMPlayThrough::UpdateIOProcState(const char* __nullable callerName,
//
// Stop won't return until the IOProc has changed inState to Stopped, unless it
// times out, so Stop should still be waiting. And since Start and Stop are
// mutually exclusive, so this should be safe.
// mutually exclusive, this should be safe.
//
// But if Stop has timed out and inState has changed, we leave it in its new
// state (unless there's some ABA problem thing happening), which I suspect is
@@ -1145,8 +1238,8 @@ bool BGMPlayThrough::UpdateIOProcState(const char* __nullable callerName,
}
else
{
DebugMsg("BGMPlayThrough::UpdateIOProcState: inState changed since last read "
"outNewState = %d", outNewState);
inRTLogger.LogUnexpectedIOStateAfterStopping(inCallerName,
static_cast<int>(outNewState));
}
}
}
@@ -1155,30 +1248,3 @@ bool BGMPlayThrough::UpdateIOProcState(const char* __nullable callerName,
return didChangeState;
}
// static
void BGMPlayThrough::HandleRingBufferError(CARingBufferError inErr,
const char* inMethodName,
const char* inCallReturningErr)
{
#if DEBUG
if(inErr != kCARingBufferError_OK)
{
const char* errStr = (inErr == kCARingBufferError_TooMuch ? "kCARingBufferError_TooMuch" :
(inErr == kCARingBufferError_CPUOverload ? "kCARingBufferError_CPUOverload" : "unknown error"));
DebugMsg("BGMPlayThrough::%s: %s returned %s (%d)", inMethodName, inCallReturningErr, errStr, inErr);
// kCARingBufferError_CPUOverload wouldn't mean we have a bug, but I think kCARingBufferError_TooMuch would
if(inErr != kCARingBufferError_CPUOverload)
{
Throw(CAException(inErr));
}
}
#else
// Not sure what we should do to handle these errors in release builds, if anything.
// TODO: There's code in Apple's CAPlayThrough.cpp sample code that handles them. (Look for "kCARingBufferError_OK"
// around line 707.) Should be easy enough to use, but it's more complicated that just directly copying it.
#pragma unused (inErr, inMethodName, inCallReturningErr)
#endif
}
+46 -17
View File
@@ -17,7 +17,7 @@
// BGMPlayThrough.h
// BGMApp
//
// Copyright © 2016, 2017 Kyle Neideck
// Copyright © 2016, 2017, 2020 Kyle Neideck
//
// Reads audio from an input device and immediately writes it to an output device. We currently use this class with the input
// device always set to BGMDevice and the output device set to the one selected in the preferences menu.
@@ -41,14 +41,17 @@
// Local Includes
#include "BGMAudioDevice.h"
#include "BGMPlayThroughRTLogger.h"
// PublicUtility Includes
#include "CARingBuffer.h"
#include "CAMutex.h"
#include "CARingBuffer.h"
#include "BGMThreadSafetyAnalysis.h"
// STL Includes
#include <atomic>
#include <algorithm>
#include <memory>
// System Includes
#include <mach/semaphore.h>
@@ -78,7 +81,8 @@ public:
private:
/*! @throws CAException */
void Init(BGMAudioDevice inInputDevice, BGMAudioDevice inOutputDevice);
void Init(BGMAudioDevice inInputDevice, BGMAudioDevice inOutputDevice)
REQUIRES(mStateMutex);
public:
/*! @throws CAException */
@@ -87,7 +91,8 @@ public:
void Deactivate();
private:
void AllocateBuffer();
void AllocateBuffer() REQUIRES(mStateMutex);
void DeallocateBuffer();
/*! @throws CAException */
void CreateIOProcIDs();
@@ -97,7 +102,7 @@ private:
@return True if both IOProcs are stopped.
@nonthreadsafe
*/
bool CheckIOProcsAreStopped() const noexcept; // TODO: REQUIRES(mStateMutex);
bool CheckIOProcsAreStopped() const noexcept REQUIRES(mStateMutex);
public:
/*!
@@ -115,7 +120,8 @@ public:
OSStatus WaitForOutputDeviceToStart() noexcept;
private:
void ReleaseThreadsWaitingForOutputToStart() const;
/*! Real-time safe. */
void ReleaseThreadsWaitingForOutputToStart();
public:
OSStatus Stop();
@@ -146,7 +152,10 @@ private:
AudioBufferList* outOutputData,
const AudioTimeStamp* inOutputTime,
void* __nullable inClientData);
/*! Fills the given ABL with zeroes to make it silent. */
static inline void FillWithSilence(AudioBufferList* ioBuffer);
// The state of an IOProc. Used by the IOProc to tell other threads when it's finished starting. Used by other
// threads to tell the IOProc to stop itself. (Probably used for other things as well.)
enum class IOState
@@ -156,27 +165,45 @@ private:
// The IOProcs call this to update their IOState member. Also stops the IOProc if its state has been set to Stopping.
// Returns true if it changes the state.
static bool UpdateIOProcState(const char* __nullable callerName,
static bool UpdateIOProcState(const char* inCallerName,
BGMPlayThroughRTLogger& inRTLogger,
std::atomic<IOState>& inState,
AudioDeviceIOProcID __nullable inIOProcID,
BGMAudioDevice& inDevice,
IOState& outNewState);
static void HandleRingBufferError(CARingBufferError err,
const char* methodName,
const char* callReturningErr);
private:
CARingBuffer mBuffer;
std::unique_ptr<CARingBuffer> mBuffer PT_GUARDED_BY(mBufferInputMutex)
PT_GUARDED_BY(mBufferOutputMutex) { nullptr };
AudioDeviceIOProcID __nullable mInputDeviceIOProcID { nullptr };
AudioDeviceIOProcID __nullable mOutputDeviceIOProcID { nullptr };
BGMAudioDevice mInputDevice { kAudioObjectUnknown };
BGMAudioDevice mOutputDevice { kAudioObjectUnknown };
CAMutex mStateMutex { "Playthrough state" };
// mStateMutex is the general purpose mutex. mBufferInputMutex and mBufferOutputMutex are
// just used to make sure mBuffer, the ring buffer, is allocated when the IOProcs access it. See
// the comments in the IOProcs for details.
//
// If a thread might lock more than one of these mutexes, it *must* take them in this order:
// 1. mStateMutex
// 2. mBufferInputMutex
// 3. mBufferOutputMutex
//
// The ACQUIRED_BEFORE annotations don't do anything yet. From clang's docs: "ACQUIRED_BEFORE(…)
// and ACQUIRED_AFTER(…) are currently unimplemented. To be fixed in a future update." After
// they've fixed that, the compiler will enforce the ordering statically.
//
// TODO: We can't use std::shared_lock because we're still on C++11, but we could use std::lock
// to help ensure the locks are always taken in the right order.
// TODO: It would be better to have a separate class for the buffer and its mutexes.
CAMutex mStateMutex ACQUIRED_BEFORE(mBufferInputMutex)
ACQUIRED_BEFORE(mBufferOutputMutex) { "Playthrough state" };
CAMutex mBufferInputMutex ACQUIRED_BEFORE(mBufferOutputMutex)
{ "Playthrough ring buffer input" };
CAMutex mBufferOutputMutex { "Playthrough ring buffer output" };
// Signalled when the output IOProc runs. We use it to tell BGMDriver when the output device is ready to receive audio data.
semaphore_t mOutputDeviceIOProcSemaphore { SEMAPHORE_NULL };
@@ -200,7 +227,9 @@ private:
// Subtract this from the output time to get the input time.
Float64 mInToOutSampleOffset { 0.0 };
BGMPlayThroughRTLogger mRTLogger;
};
#pragma clang assume_nonnull end
+521
View File
@@ -0,0 +1,521 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMPlayThroughRTLogger.cpp
// BGMApp
//
// Copyright © 2020 Kyle Neideck
//
// Self Include
#include "BGMPlayThroughRTLogger.h"
// Local Includes
#include "BGM_Utils.h"
// PublicUtility Includes
#include "CADebugMacros.h"
// STL Includes
#include <atomic>
// System Includes
#include <CoreAudio/CoreAudio.h>
#include <mach/mach_init.h>
#include <mach/task.h>
#include <unistd.h>
#pragma clang assume_nonnull begin
// Track the number of messages logged when built for the unit tests.
#if BGM_UnitTest
#define LogSync_Debug(inFormat, ...) do { \
mNumDebugMessagesLogged++; \
DebugMsg(inFormat, ## __VA_ARGS__); \
} while (0)
#else
#define LogSync_Debug(inFormat, ...) DebugMsg(inFormat, ## __VA_ARGS__)
#endif
#pragma mark Construction/Destruction
BGMPlayThroughRTLogger::BGMPlayThroughRTLogger()
{
// Create the semaphore we use to wake up the logging thread when it has messages to log.
mWakeUpLoggingThreadSemaphore = CreateSemaphore();
// Create the logging thread last because it starts immediately and expects the other member
// variables to be initialised.
mLoggingThread = std::thread(&BGMPlayThroughRTLogger::LoggingThreadEntry, this);
}
// static
semaphore_t BGMPlayThroughRTLogger::CreateSemaphore()
{
// TODO: Make a BGMMachSemaphore class to reduce some of this repetitive semaphore code.
// Create the semaphore.
semaphore_t semaphore;
kern_return_t error =
semaphore_create(mach_task_self(), &semaphore, SYNC_POLICY_FIFO, 0);
// Check the error code.
BGM_Utils::ThrowIfMachError("BGMPlayThroughRTLogger::CreateSemaphore",
"semaphore_create",
error);
ThrowIf(semaphore == SEMAPHORE_NULL,
CAException(kAudioHardwareUnspecifiedError),
"BGMPlayThroughRTLogger::CreateSemaphore: Failed to create semaphore");
return semaphore;
}
BGMPlayThroughRTLogger::~BGMPlayThroughRTLogger()
{
// Stop the logging thread.
mLoggingThreadShouldExit = true;
kern_return_t error = semaphore_signal(mWakeUpLoggingThreadSemaphore);
BGM_Utils::LogIfMachError("BGMPlayThroughRTLogger::~BGMPlayThroughRTLogger",
"semaphore_signal",
error);
if(error == KERN_SUCCESS)
{
// Wait for it to stop.
mLoggingThread.join();
// Destroy the semaphore.
error = semaphore_destroy(mach_task_self(), mWakeUpLoggingThreadSemaphore);
BGM_Utils::LogIfMachError("BGMPlayThroughRTLogger::~BGMPlayThroughRTLogger",
"semaphore_destroy",
error);
}
else
{
// If we couldn't tell it to wake up, it's not safe to wait for it to stop or to destroy the
// semaphore. We have to detach it so its destructor doesn't cause a crash.
mLoggingThread.detach();
}
}
#pragma mark Log Messages
void BGMPlayThroughRTLogger::LogReleasingWaitingThreads()
{
if(!BGMDebugLoggingIsEnabled())
{
return;
}
if(!mLogReleasingWaitingThreadsMsg.is_lock_free())
{
// Modifying mLogReleasingWaitingThreadsMsg might cause the thread to lock a mutex that
// isn't safe to lock on a realtime thread, so just give up.
return;
}
// Set the flag that tells the logging thread to log the message.
mLogReleasingWaitingThreadsMsg = true;
// Wake the logging thread so it can log the message.
WakeLoggingThread();
}
void BGMPlayThroughRTLogger::LogIfMachError_ReleaseWaitingThreadsSignal(mach_error_t inError)
{
if(inError == KERN_SUCCESS)
{
// No error.
return;
}
if(!mReleaseWaitingThreadsSignalError.is_lock_free())
{
// Modifying mReleaseWaitingThreadsSignalError might cause the thread to lock a mutex that
// isn't safe to lock on a realtime thread, so just give up.
return;
}
mReleaseWaitingThreadsSignalError = inError;
WakeLoggingThread();
}
void BGMPlayThroughRTLogger::LogIfDroppedFrames(Float64 inFirstInputSampleTime,
Float64 inLastInputSampleTime)
{
if(inFirstInputSampleTime == inLastInputSampleTime || !BGMDebugLoggingIsEnabled())
{
// Either we didn't drop any initial frames or we don't need to log a message about it.
return;
}
LogAsync(mDroppedFrames, [&]()
{
// Store the data to include in the log message.
mDroppedFrames.firstInputSampleTime = inFirstInputSampleTime;
mDroppedFrames.lastInputSampleTime = inLastInputSampleTime;
});
}
void BGMPlayThroughRTLogger::LogNoSamplesReady(CARingBuffer::SampleTime inLastInputSampleTime,
CARingBuffer::SampleTime inReadHeadSampleTime,
Float64 inInToOutSampleOffset)
{
if(!BGMDebugLoggingIsEnabled())
{
return;
}
LogAsync(mNoSamplesReady, [&]()
{
// Store the data to include in the log message.
mNoSamplesReady.lastInputSampleTime = inLastInputSampleTime;
mNoSamplesReady.readHeadSampleTime = inReadHeadSampleTime;
mNoSamplesReady.inToOutSampleOffset = inInToOutSampleOffset;
});
}
void BGMPlayThroughRTLogger::LogExceptionStoppingIOProc(const char* inCallerName,
OSStatus inError,
bool inErrorKnown)
{
LogAsync(mExceptionStoppingIOProc, [&]()
{
// Store the data to include in the log message.
mExceptionStoppingIOProc.callerName = inCallerName;
mExceptionStoppingIOProc.error = inError;
mExceptionStoppingIOProc.errorKnown = inErrorKnown;
});
}
void BGMPlayThroughRTLogger::LogUnexpectedIOStateAfterStopping(const char* inCallerName,
int inIOState)
{
LogAsync(mUnexpectedIOStateAfterStopping, [&]()
{
// Store the data to include in the log message.
mUnexpectedIOStateAfterStopping.callerName = inCallerName;
mUnexpectedIOStateAfterStopping.ioState = inIOState;
});
}
void BGMPlayThroughRTLogger::LogRingBufferUnavailable(const char* inCallerName, bool inGotLock)
{
LogAsync(mRingBufferUnavailable, [&]()
{
// Store the data to include in the log message.
mRingBufferUnavailable.callerName = inCallerName;
mRingBufferUnavailable.gotLock = inGotLock;
});
}
void BGMPlayThroughRTLogger::LogIfRingBufferError(CARingBufferError inError,
std::atomic<CARingBufferError>& outError)
{
if(inError == kCARingBufferError_OK)
{
// No error.
return;
}
if(!outError.is_lock_free())
{
// Modifying outError might cause the thread to lock a mutex that isn't safe to lock on
// a realtime thread, so just give up.
return;
}
// Store the error.
outError = inError;
// Wake the logging thread so it can log the error.
WakeLoggingThread();
}
template <typename T, typename F>
void BGMPlayThroughRTLogger::LogAsync(T& inMessageData, F&& inStoreMessageData)
{
if(!inMessageData.shouldLogMessage.is_lock_free())
{
// Modifying shouldLogMessage might cause the thread to lock a mutex that isn't safe to
// lock on a realtime thread, so just give up.
return;
}
if(inMessageData.shouldLogMessage)
{
// The logging thread could be reading inMessageData.
return;
}
// Store the data to include in the log message.
//
// std::forward lets the compiler treat inStoreMessageData as an rvalue if the caller gave it as
// an rvalue. No idea if that actually does anything.
std::forward<F>(inStoreMessageData)();
// shouldLogMessage is a std::atomic, so this store also makes sure that the non-atomic stores
// in inStoreMessageData will be visible to the logger thread (since the default memory order is
// memory_order_seq_cst).
inMessageData.shouldLogMessage = true;
WakeLoggingThread();
}
void BGMPlayThroughRTLogger::LogSync_Warning(const char* inFormat, ...)
{
va_list args;
va_start(args, inFormat);
#if BGM_UnitTest
mNumWarningMessagesLogged++;
#endif
vLogWarning(inFormat, args);
va_end(args);
}
void BGMPlayThroughRTLogger::LogSync_Error(const char* inFormat, ...)
{
va_list args;
va_start(args, inFormat);
#if BGM_UnitTest
mNumErrorMessagesLogged++;
if(!mContinueOnErrorLogged)
{
vLogError(inFormat, args);
}
#else
vLogError(inFormat, args);
#endif
va_end(args);
}
#pragma mark Logging Thread
void BGMPlayThroughRTLogger::WakeLoggingThread()
{
kern_return_t error = semaphore_signal(mWakeUpLoggingThreadSemaphore);
BGMAssert(error == KERN_SUCCESS, "semaphore_signal (%d)", error);
// We can't do anything useful with the error in release builds. At least, not easily.
(void)error;
}
void BGMPlayThroughRTLogger::LogMessages()
{
// Log the messages/errors from the realtime threads (if any).
LogSync_ReleasingWaitingThreads();
LogSync_ReleaseWaitingThreadsSignalError();
LogSync_DroppedFrames();
LogSync_NoSamplesReady();
LogSync_ExceptionStoppingIOProc();
LogSync_UnexpectedIOStateAfterStopping();
LogSync_RingBufferUnavailable();
LogSync_RingBufferError(mRingBufferStoreError, "InputDeviceIOProc");
LogSync_RingBufferError(mRingBufferFetchError, "OutputDeviceIOProc");
}
void BGMPlayThroughRTLogger::LogSync_ReleasingWaitingThreads()
{
if(mLogReleasingWaitingThreadsMsg)
{
LogSync_Debug("BGMPlayThrough::ReleaseThreadsWaitingForOutputToStart: "
"Releasing waiting threads");
// Reset it.
mLogReleasingWaitingThreadsMsg = false;
}
}
void BGMPlayThroughRTLogger::LogSync_ReleaseWaitingThreadsSignalError()
{
if(mReleaseWaitingThreadsSignalError != KERN_SUCCESS)
{
BGM_Utils::LogIfMachError("BGMPlayThrough::ReleaseThreadsWaitingForOutputToStart",
"semaphore_signal_all",
mReleaseWaitingThreadsSignalError);
// Reset it.
mReleaseWaitingThreadsSignalError = KERN_SUCCESS;
}
}
void BGMPlayThroughRTLogger::LogSync_DroppedFrames()
{
if(mDroppedFrames.shouldLogMessage)
{
LogSync_Debug("BGMPlayThrough::OutputDeviceIOProc: "
"Dropped %f frames before output started. %s%f %s%f",
(mDroppedFrames.lastInputSampleTime - mDroppedFrames.firstInputSampleTime),
"mFirstInputSampleTime=",
mDroppedFrames.firstInputSampleTime,
"mLastInputSampleTime=",
mDroppedFrames.lastInputSampleTime);
mDroppedFrames.shouldLogMessage = false;
}
}
void BGMPlayThroughRTLogger::LogSync_NoSamplesReady()
{
if(mNoSamplesReady.shouldLogMessage)
{
LogSync_Debug("BGMPlayThrough::OutputDeviceIOProc: "
"No input samples ready at output sample time. %s%lld %s%lld %s%f",
"lastInputSampleTime=", mNoSamplesReady.lastInputSampleTime,
"readHeadSampleTime=", mNoSamplesReady.readHeadSampleTime,
"mInToOutSampleOffset=", mNoSamplesReady.inToOutSampleOffset);
mNoSamplesReady.shouldLogMessage = false;
}
}
void BGMPlayThroughRTLogger::LogSync_ExceptionStoppingIOProc()
{
if(mExceptionStoppingIOProc.shouldLogMessage)
{
const char error4CC[5] = CA4CCToCString(mExceptionStoppingIOProc.error);
LogSync_Error("BGMPlayThrough::UpdateIOProcState: "
"Exception while stopping IOProc %s: %s (%d)",
mExceptionStoppingIOProc.callerName,
mExceptionStoppingIOProc.errorKnown ? error4CC : "unknown",
mExceptionStoppingIOProc.error);
mExceptionStoppingIOProc.shouldLogMessage = false;
}
}
void BGMPlayThroughRTLogger::LogSync_UnexpectedIOStateAfterStopping()
{
if(mUnexpectedIOStateAfterStopping.shouldLogMessage)
{
LogSync_Warning("BGMPlayThrough::UpdateIOProcState: "
"%s IO state changed since last read. state = %d",
mUnexpectedIOStateAfterStopping.callerName,
mUnexpectedIOStateAfterStopping.ioState);
mUnexpectedIOStateAfterStopping.shouldLogMessage = false;
}
}
void BGMPlayThroughRTLogger::LogSync_RingBufferUnavailable()
{
if(mRingBufferUnavailable.shouldLogMessage)
{
LogSync_Warning("BGMPlayThrough::%s: Ring buffer unavailable. %s",
mRingBufferUnavailable.callerName,
mRingBufferUnavailable.gotLock ?
"No buffer currently allocated." :
"Buffer locked for allocation/deallocation by another thread.");
mRingBufferUnavailable.shouldLogMessage = false;
}
}
void BGMPlayThroughRTLogger::LogSync_RingBufferError(
std::atomic<CARingBufferError>& ioRingBufferError,
const char* inMethodName)
{
CARingBufferError error = ioRingBufferError;
switch(error)
{
case kCARingBufferError_OK:
// No error.
return;
case kCARingBufferError_CPUOverload:
// kCARingBufferError_CPUOverload might not be our fault, so just log a warning.
LogSync_Warning("BGMPlayThrough::%s: Ring buffer error: "
"kCARingBufferError_CPUOverload (%d)",
inMethodName,
error);
break;
default:
// Other types of CARingBuffer errors should never occur. This will crash debug builds.
LogSync_Error("BGMPlayThrough::%s: Ring buffer error: %s (%d)",
inMethodName,
(error == kCARingBufferError_TooMuch ?
"kCARingBufferError_TooMuch" :
"unknown error"),
error);
break;
};
// Reset it.
ioRingBufferError = kCARingBufferError_OK;
}
// static
void* __nullable BGMPlayThroughRTLogger::LoggingThreadEntry(BGMPlayThroughRTLogger* inRefCon)
{
DebugMsg("BGMPlayThroughRTLogger::IOProcLoggingThreadEntry: "
"Starting the IOProc logging thread");
while(!inRefCon->mLoggingThreadShouldExit)
{
// Log the messages, if there are any to log.
inRefCon->LogMessages();
// Wait until woken up.
kern_return_t error = semaphore_wait(inRefCon->mWakeUpLoggingThreadSemaphore);
BGM_Utils::LogIfMachError("BGMPlayThroughRTLogger::IOProcLoggingThreadEntry",
"semaphore_wait",
error);
}
DebugMsg("BGMPlayThroughRTLogger::IOProcLoggingThreadEntry: IOProc logging thread exiting");
return nullptr;
}
#if BGM_UnitTest
#pragma mark Test Helpers
bool BGMPlayThroughRTLogger::WaitUntilLoggerThreadIdle()
{
int msWaited = 0;
while(mLogReleasingWaitingThreadsMsg ||
mReleaseWaitingThreadsSignalError != KERN_SUCCESS ||
mDroppedFrames.shouldLogMessage ||
mNoSamplesReady.shouldLogMessage ||
mUnexpectedIOStateAfterStopping.shouldLogMessage ||
mRingBufferUnavailable.shouldLogMessage ||
mExceptionStoppingIOProc.shouldLogMessage ||
mRingBufferStoreError != kCARingBufferError_OK ||
mRingBufferFetchError != kCARingBufferError_OK)
{
// Poll until the logger thread has nothing left to log. (Ideally we'd use a semaphore
// instead of polling, but it isn't worth the effort at this point.)
usleep(10 * 1000);
msWaited += 10;
// Time out after 5 seconds.
if(msWaited > 5000)
{
return false;
}
}
return true;
}
#endif /* BGM_UnitTest */
#pragma clang assume_nonnull end
+227
View File
@@ -0,0 +1,227 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMPlayThroughRTLogger.h
// BGMApp
//
// Copyright © 2020 Kyle Neideck
//
// A real-time safe logger for BGMPlayThrough. The messages are logged asynchronously by a
// non-realtime thread.
//
// For the sake of simplicity, this class is very closely coupled with BGMPlayThrough and its
// methods make assumptions about where they will be called. Also, if the same logging method is
// called multiple times before the logging thread next checks for messages, it will only log the
// message for one of those calls and ignore the others.
//
// This class's methods are real-time safe in that they return in a bounded amount of time and we
// think they're probably fast enough that the callers won't miss their deadlines, but we don't try
// to guarantee it. Some of them should only be called in unusual cases where it's worth increasing
// the risk of a thread missing its deadline.
//
#ifndef BGMApp__BGMPlayThroughRTLogger
#define BGMApp__BGMPlayThroughRTLogger
// PublicUtility Includes
#include "CARingBuffer.h"
// STL Includes
#include <thread>
// System Includes
#include <mach/error.h>
#include <mach/semaphore.h>
#pragma clang assume_nonnull begin
class BGMPlayThroughRTLogger
{
#pragma mark Construction/Destruction
public:
BGMPlayThroughRTLogger();
~BGMPlayThroughRTLogger();
BGMPlayThroughRTLogger(const BGMPlayThroughRTLogger&) = delete;
BGMPlayThroughRTLogger& operator=(
const BGMPlayThroughRTLogger&) = delete;
private:
static semaphore_t CreateSemaphore();
#pragma mark Log Messages
public:
/*! For BGMPlayThrough::ReleaseThreadsWaitingForOutputToStart. */
void LogReleasingWaitingThreads();
/*! For BGMPlayThrough::ReleaseThreadsWaitingForOutputToStart. */
void LogIfMachError_ReleaseWaitingThreadsSignal(mach_error_t inError);
/*! For BGMPlayThrough::OutputDeviceIOProc. Not thread-safe. */
void LogIfDroppedFrames(Float64 inFirstInputSampleTime,
Float64 inLastInputSampleTime);
/*! For BGMPlayThrough::OutputDeviceIOProc. Not thread-safe. */
void LogNoSamplesReady(CARingBuffer::SampleTime inLastInputSampleTime,
CARingBuffer::SampleTime inReadHeadSampleTime,
Float64 inInToOutSampleOffset);
/*! For BGMPlayThrough::UpdateIOProcState. Not thread-safe. */
void LogExceptionStoppingIOProc(const char* inCallerName)
{
LogExceptionStoppingIOProc(inCallerName, noErr, false);
}
/*! For BGMPlayThrough::UpdateIOProcState. Not thread-safe. */
void LogExceptionStoppingIOProc(const char* inCallerName, OSStatus inError)
{
LogExceptionStoppingIOProc(inCallerName, inError, true);
}
private:
void LogExceptionStoppingIOProc(const char* inCallerName,
OSStatus inError,
bool inErrorKnown);
public:
/*! For BGMPlayThrough::UpdateIOProcState. Not thread-safe. */
void LogUnexpectedIOStateAfterStopping(const char* inCallerName,
int inIOState);
/*! For BGMPlayThrough::InputDeviceIOProc and BGMPlayThrough::OutputDeviceIOProc. */
void LogRingBufferUnavailable(const char* inCallerName, bool inGotLock);
/*! For BGMPlayThrough::OutputDeviceIOProc. */
void LogIfRingBufferError_Fetch(CARingBufferError inError)
{
LogIfRingBufferError(inError, mRingBufferFetchError);
}
/*! For BGMPlayThrough::InputDeviceIOProc. */
void LogIfRingBufferError_Store(CARingBufferError inError)
{
LogIfRingBufferError(inError, mRingBufferStoreError);
}
private:
void LogIfRingBufferError(CARingBufferError inError,
std::atomic<CARingBufferError>& outError);
template <typename T, typename F>
void LogAsync(T& inMessageData, F&& inStoreMessageData);
// Wrapper methods used to mock out the logging for unit tests.
void LogSync_Warning(const char* inFormat, ...) __printflike(2, 3);
void LogSync_Error(const char* inFormat, ...) __printflike(2, 3);
#pragma mark Logging Thread
private:
void WakeLoggingThread();
void LogMessages();
void LogSync_ReleasingWaitingThreads();
void LogSync_ReleaseWaitingThreadsSignalError();
void LogSync_DroppedFrames();
void LogSync_NoSamplesReady();
void LogSync_ExceptionStoppingIOProc();
void LogSync_UnexpectedIOStateAfterStopping();
void LogSync_RingBufferUnavailable();
void LogSync_RingBufferError(
std::atomic<CARingBufferError>& ioRingBufferError,
const char* inMethodName);
// The entry point of the logging thread (mLoggingThread).
static void* __nullable LoggingThreadEntry(BGMPlayThroughRTLogger* inRefCon);
#if BGM_UnitTest
#pragma mark Test Helpers
public:
/*!
* @return True if the logger thread finished logging the requested messages. False if it still
* had messages to log after 5 seconds.
*/
bool WaitUntilLoggerThreadIdle();
#endif /* BGM_UnitTest */
private:
// For BGMPlayThrough::ReleaseThreadsWaitingForOutputToStart
std::atomic<bool> mLogReleasingWaitingThreadsMsg { false };
std::atomic<kern_return_t> mReleaseWaitingThreadsSignalError { KERN_SUCCESS };
// For BGMPlayThrough::InputDeviceIOProc and BGMPlayThrough::OutputDeviceIOProc
struct {
Float64 firstInputSampleTime;
Float64 lastInputSampleTime;
std::atomic<bool> shouldLogMessage { false };
} mDroppedFrames;
struct {
CARingBuffer::SampleTime lastInputSampleTime;
CARingBuffer::SampleTime readHeadSampleTime;
Float64 inToOutSampleOffset;
std::atomic<bool> shouldLogMessage { false };
} mNoSamplesReady;
struct {
const char* callerName;
bool gotLock;
std::atomic<bool> shouldLogMessage { false };
} mRingBufferUnavailable;
// For BGMPlayThrough::UpdateIOProcState
struct {
const char* callerName;
int ioState;
std::atomic<bool> shouldLogMessage { false };
} mUnexpectedIOStateAfterStopping;
struct {
const char* callerName;
OSStatus error;
bool errorKnown; // If false, we didn't get an error code from the exception.
std::atomic<bool> shouldLogMessage { false };
} mExceptionStoppingIOProc;
// For BGMPlayThrough::OutputDeviceIOProc
std::atomic<CARingBufferError> mRingBufferStoreError { kCARingBufferError_OK };
// For BGMPlayThrough::InputDeviceIOProc.
std::atomic<CARingBufferError> mRingBufferFetchError { kCARingBufferError_OK };
// Signalled to wake up the mLoggingThread when it has messages to log.
semaphore_t mWakeUpLoggingThreadSemaphore;
std::atomic<bool> mLoggingThreadShouldExit { false };
// The thread that actually logs the messages.
std::thread mLoggingThread;
#if BGM_UnitTest
public:
// Tests normally crash (abort) if LogError is called. This flag lets us test the code that
// would otherwise call LogError.
bool mContinueOnErrorLogged { false };
int mNumDebugMessagesLogged { 0 };
int mNumWarningMessagesLogged { 0 };
int mNumErrorMessagesLogged { 0 };
#endif /* BGM_UnitTest */
};
#pragma clang assume_nonnull end
#endif /* BGMApp__BGMPlayThroughRTLogger */
+1 -1
View File
@@ -113,7 +113,7 @@ NSString* const kAudioSystemSettingsPlist =
// BGMApp's stored preferred devices to fill in the rest optimistically. This doesn't help us
// tell when to switch to a newly connected device, but it should improve our chances of
// switching to the best device if the current output device is disconnected.
NSArray<NSDictionary*>* preferredOutputDeviceInfos = @[];
NSArray<NSDictionary*>* _Nonnull preferredOutputDeviceInfos = @[];
// If we can't read the Plist, we only know that the current systemwide default device is the
// most-preferred device that's currently connected.
+6 -1
View File
@@ -17,7 +17,7 @@
// BGMStatusBarItem.h
// BGMApp
//
// Copyright © 2019 Kyle Neideck
// Copyright © 2019, 2020 Kyle Neideck
//
// The button in the system status bar (the bar with volume, battery, clock, etc.) to show the main
// menu for the app. These are called "menu bar extras" in the Human Interface Guidelines.
@@ -25,6 +25,7 @@
// Local Includes
#import "BGMAudioDeviceManager.h"
#import "BGMDebugLoggingMenuItem.h"
// System Includes
#import <Cocoa/Cocoa.h>
@@ -57,6 +58,10 @@ static BGMStatusBarIcon const kBGMStatusBarIconDefaultValue = BGMFermataStatusBa
// same as the icon for the macOS volume status bar item.
@property BGMStatusBarIcon icon;
// If the user holds down the option key when they click the status bar icon, this menu item will be
// shown in the main menu.
- (void) setDebugLoggingMenuItem:(BGMDebugLoggingMenuItem*)menuItem;
@end
#pragma clang assume_nonnull end
+57 -5
View File
@@ -17,7 +17,7 @@
// BGMStatusBarItem.m
// BGMApp
//
// Copyright © 2019 Kyle Neideck
// Copyright © 2019, 2020 Kyle Neideck
//
// Self Include
@@ -48,11 +48,16 @@ static CGFloat const kVolumeIconAdditionalVerticalPadding = 0.075;
NSImage* volumeIcon3SoundWaves;
NSStatusItem* statusBarItem;
BGMDebugLoggingMenuItem* debugLoggingMenuItem;
BGMVolumeChangeListener* volumeChangeListener;
id __nullable clickEventHandler;
BGMStatusBarIcon _icon;
}
#pragma mark Initialisation
- (instancetype) initWithMenu:(NSMenu*)bgmMenu
audioDevices:(BGMAudioDeviceManager*)devices
userDefaults:(BGMUserDefaults*)defaults {
@@ -72,6 +77,10 @@ static CGFloat const kVolumeIconAdditionalVerticalPadding = 0.075;
// Set the menu item to open the main menu.
statusBarItem.menu = bgmMenu;
// Monitor click events so we can show extra options in the menu if the user was holding the
// option key.
clickEventHandler = [self addClickMonitor];
// Set the accessibility label to "Background Music". (We intentionally don't set a title or
// a tooltip.)
if ([BGMStatusBarItem buttonAvailable]) {
@@ -92,17 +101,41 @@ static CGFloat const kVolumeIconAdditionalVerticalPadding = 0.075;
return self;
}
- (id __nullable) addClickMonitor {
NSEvent* __nullable (^handlerBlock)(NSEvent*) =
^NSEvent* __nullable (NSEvent* event) {
[self statusBarItemWasClicked:event];
return event;
};
// TODO: I doubt this works well with VoiceOver.
return [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown
handler:handlerBlock];
}
- (void) dealloc {
delete volumeChangeListener;
if (clickEventHandler) {
[NSEvent removeMonitor:(id)clickEventHandler];
clickEventHandler = nil;
}
}
- (void) initIcons {
// Load the icons.
fermataIcon = [NSImage imageNamed:@"FermataIcon"];
volumeIcon0SoundWaves = [NSImage imageNamed:@"Volume0"];
volumeIcon1SoundWave = [NSImage imageNamed:@"Volume1"];
volumeIcon2SoundWaves = [NSImage imageNamed:@"Volume2"];
volumeIcon3SoundWaves = [NSImage imageNamed:@"Volume3"];
if (@available(macOS 11.0, *)) {
volumeIcon0SoundWaves = [NSImage imageWithSystemSymbolName:@"speaker.fill" accessibilityDescription:nil];
volumeIcon1SoundWave = [NSImage imageWithSystemSymbolName:@"speaker.wave.1.fill" accessibilityDescription:nil];
volumeIcon2SoundWaves = [NSImage imageWithSystemSymbolName:@"speaker.wave.2.fill" accessibilityDescription:nil];
volumeIcon3SoundWaves = [NSImage imageWithSystemSymbolName:@"speaker.wave.3.fill" accessibilityDescription:nil];
} else {
volumeIcon0SoundWaves = [NSImage imageNamed:@"Volume0"];
volumeIcon1SoundWave = [NSImage imageNamed:@"Volume1"];
volumeIcon2SoundWaves = [NSImage imageNamed:@"Volume2"];
volumeIcon3SoundWaves = [NSImage imageNamed:@"Volume3"];
}
// Set the icons' sizes.
NSRect statusBarItemFrame;
@@ -142,6 +175,8 @@ static CGFloat const kVolumeIconAdditionalVerticalPadding = 0.075;
[volumeIcon3SoundWaves setTemplate:YES];
}
#pragma mark Accessors
+ (BOOL) buttonAvailable {
// NSStatusItem doesn't have the "button" property on OS X 10.9.
return (floor(NSAppKitVersionNumber) >= NSAppKitVersionNumber10_10);
@@ -188,6 +223,8 @@ static CGFloat const kVolumeIconAdditionalVerticalPadding = 0.075;
});
}
#pragma mark Volume Icon
- (void) bgmDeviceVolumeDidChange {
if (self.icon == BGMVolumeStatusBarIcon) {
[self updateVolumeStatusBarIcon];
@@ -251,6 +288,21 @@ static CGFloat const kVolumeIconAdditionalVerticalPadding = 0.075;
statusBarItem.image.name.UTF8String);
}
#pragma mark Debug Logging Menu Item
- (void) statusBarItemWasClicked:(NSEvent* __nonnull)event {
if ((event.modifierFlags & NSEventModifierFlagOption) != 0) {
DebugMsg("BGMStatusBarItem::statusBarItemWasClicked: Option key held");
[debugLoggingMenuItem setMenuShowingExtraOptions:YES];
} else {
[debugLoggingMenuItem setMenuShowingExtraOptions:NO];
}
}
- (void) setDebugLoggingMenuItem:(BGMDebugLoggingMenuItem*)menuItem {
debugLoggingMenuItem = menuItem;
}
@end
#pragma clang assume_nonnull end
+1 -1
View File
@@ -19,7 +19,7 @@
//
// Copyright © 2017 Kyle Neideck
//
// Cleans up if BGMApp crashes because of an uncaught C++ or Objective C exception, or is sent
// Cleans up if BGMApp crashes because of an uncaught C++ or Objective-C exception, or is sent
// SIGINT/SIGTERM/SIGQUIT. Currently, it just changes the default output device from BGMDevice to
// the real output device and records debug info for some types of crashes.
//
+1 -1
View File
@@ -83,7 +83,7 @@ void BGMTermination::SetUpTerminationCleanUp(BGMAudioDeviceManager* inAudioDevic
StartExitSignalsThread();
// Wrap the default handler for std::terminate, which is called if BGMApp crashes because of an
// uncaught C++ or Objective C exception, so we can clean up first.
// uncaught C++ or Objective-C exception, so we can clean up first.
sOriginalTerminateHandler = std::get_terminate();
std::set_terminate([] {
+91 -78
View File
@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14835.7" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<development version="8000" identifier="xcode"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14835.7"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -21,6 +20,7 @@
<outlet property="appVolumeView" destination="MWB-XH-kFI" id="eFA-RN-VMC"/>
<outlet property="autoPauseMenuItemUnwrapped" destination="nHv-T8-1nb" id="Lie-Cx-jw6"/>
<outlet property="bgmMenu" destination="8AN-nh-rEe" id="UWn-BX-eLy"/>
<outlet property="debugLoggingMenuItemUnwrapped" destination="sc9-vO-KyP" id="Zyd-0v-0RN"/>
<outlet property="outputVolumeLabel" destination="wfC-C6-SLv" id="Nuf-mo-osG"/>
<outlet property="outputVolumeSlider" destination="9Ru-Sc-dqC" id="wv0-Md-BwF"/>
<outlet property="outputVolumeView" destination="JOz-H1-mj9" id="xeJ-fk-NMI"/>
@@ -58,7 +58,7 @@
<menuItem title="Background Music Logo" state="on" tag="2" id="9VF-qy-6fh">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Volume Icon" tag="3" toolTip="todo" id="B47-O2-wd0">
<menuItem title="Volume Icon" tag="3" id="B47-O2-wd0">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="pYP-Fy-nKA"/>
@@ -68,6 +68,9 @@
</items>
</menu>
</menuItem>
<menuItem title="Debug Logging" hidden="YES" toolTip="Log detailed messages to help diagnose bugs. Search for &quot;bgm&quot; or &quot;background music&quot; in Console.app." id="sc9-vO-KyP">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
<menuItem title="Quit Background Music" id="Nj2-gJ-DhW">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
@@ -82,7 +85,7 @@
<rect key="frame" x="0.0" y="0.0" width="269" height="47"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField identifier="AppName" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Xmd-bg-huG" customClass="BGMAVM_AppNameLabel">
<textField identifier="AppName" focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Xmd-bg-huG" customClass="BGMAVM_AppNameLabel">
<rect key="frame" x="42" y="28" width="115" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="left" title="App name here" usesSingleLineMode="YES" id="ZHF-ZW-Oqg">
@@ -99,7 +102,7 @@
<slider verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="I1l-Ci-4md" customClass="BGMAVM_VolumeSlider">
<rect key="frame" x="163" y="27" width="74" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<sliderCell key="cell" controlSize="small" continuous="YES" state="on" alignment="left" maxValue="100" doubleValue="50" tickMarkPosition="above" sliderType="linear" id="Jmg-df-9Xl"/>
<sliderCell key="cell" controlSize="mini" continuous="YES" state="on" alignment="left" maxValue="100" doubleValue="50" tickMarkPosition="above" sliderType="linear" id="Jmg-df-9Xl"/>
<accessibility description="Volume"/>
</slider>
<slider toolTip="Pan" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2mh-uO-kOV" customClass="BGMAVM_PanSlider">
@@ -108,7 +111,7 @@
<sliderCell key="cell" controlSize="mini" continuous="YES" state="on" alignment="left" minValue="-100" maxValue="100" tickMarkPosition="below" numberOfTickMarks="1" sliderType="linear" id="ccM-Mt-93g"/>
<accessibility description="Pan"/>
</slider>
<button verticalHuggingPriority="750" fixedFrame="YES" tag="1" springLoaded="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vTG-n6-GxY" customClass="BGMAVM_ShowMoreControlsButton">
<button tag="1" springLoaded="YES" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vTG-n6-GxY" customClass="BGMAVM_ShowMoreControlsButton">
<rect key="frame" x="243" y="27" width="16" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<contentFilters>
@@ -124,20 +127,20 @@
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<textField identifier="PanLeft" toolTip="Pan" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9jc-9i-jw2">
<textField identifier="PanLeft" toolTip="Pan" focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9jc-9i-jw2">
<rect key="frame" x="162" y="-1" width="12" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="L" id="hgE-7A-bez">
<font key="font" metaFont="miniSystem"/>
<font key="font" metaFont="menu" size="9"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField identifier="PanRight" toolTip="Pan" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1lZ-hX-6Kl">
<textField identifier="PanRight" toolTip="Pan" focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1lZ-hX-6Kl">
<rect key="frame" x="228" y="-1" width="12" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="R" id="lzr-NO-0Na">
<font key="font" metaFont="miniSystem"/>
<font key="font" metaFont="menu" size="9"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@@ -145,16 +148,16 @@
</subviews>
<point key="canvasLocation" x="117" y="-45"/>
</customView>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" visibleAtLaunch="NO" animationBehavior="default" id="Cf4-3V-gl1" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES"/>
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" hidesOnDeactivate="YES" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="Cf4-3V-gl1" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" utility="YES" nonactivatingPanel="YES"/>
<windowPositionMask key="initialPositionMask" topStrut="YES"/>
<rect key="contentRect" x="248" y="350" width="1002" height="335"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<rect key="screenRect" x="0.0" y="0.0" width="1512" height="920"/>
<view key="contentView" id="HlB-hX-Y0Y">
<rect key="frame" x="0.0" y="0.0" width="1002" height="335"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r51-dd-LGP">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="r51-dd-LGP">
<rect key="frame" x="71" y="125" width="240" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Background Music" id="Dw2-nu-eBQ">
@@ -163,16 +166,16 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" tag="1" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ekc-h0-I43">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" tag="1" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ekc-h0-I43">
<rect key="frame" x="71" y="100" width="240" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Version 0.3.1" id="FDH-7l-wFf">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Version 0.4.3" id="FDH-7l-wFf">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L5P-Lw-aCd">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L5P-Lw-aCd">
<rect key="frame" x="413" y="298" width="270" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="Licensed under GPL v2 or any later version." id="ETh-En-bzX">
@@ -185,7 +188,7 @@
<rect key="frame" x="383" y="93" width="5" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</box>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" tag="3" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nx6-kQ-N8Z" customClass="BGMLinkField">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" tag="3" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="nx6-kQ-N8Z" customClass="BGMLinkField">
<rect key="frame" x="36" y="50" width="310" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" tag="3" title="https://github.com/kyleneideck/BackgroundMusic" placeholderString="" id="VOb-5X-o3R">
@@ -213,11 +216,11 @@
<scrollView fixedFrame="YES" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eqz-ap-PAC">
<rect key="frame" x="415" y="45" width="567" height="245"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" ambiguous="YES" drawsBackground="NO" id="Cdb-RA-YK0">
<clipView key="contentView" drawsBackground="NO" id="Cdb-RA-YK0">
<rect key="frame" x="1" y="1" width="565" height="243"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textView ambiguous="YES" editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" id="LSG-PF-cl8">
<textView editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" id="LSG-PF-cl8">
<rect key="frame" x="-6" y="0.0" width="577" height="243"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
@@ -240,29 +243,38 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6qu-yI-r00">
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6qu-yI-r00">
<rect key="frame" x="413" y="20" width="203" height="11"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="The AirPlay Logo is a trademark of Apple Inc." id="lx7-k3-q16">
<font key="font" metaFont="miniSystem"/>
<font key="font" metaFont="menu" size="9"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" tag="2" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vy4-dv-jQB">
<rect key="frame" x="18" y="75" width="346" height="17"/>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" tag="2" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vy4-dv-jQB">
<rect key="frame" x="18" y="75" width="155" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Copyright © 2016-2019 Background Music contributors" placeholderString="" id="ctF-95-uVu">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Copyright © 2016-2024" placeholderString="" id="ctF-95-uVu">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" tag="4" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tRj-QC-GuQ" customClass="BGMLinkField">
<rect key="frame" x="168" y="75" width="194" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" tag="3" title="Background Music contributors" placeholderString="" allowsEditingTextAttributes="YES" usesSingleLineMode="YES" id="UC2-MX-fML">
<font key="font" metaFont="system"/>
<color key="textColor" red="0.20000000000000001" green="0.40000000000000002" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
</view>
<point key="canvasLocation" x="-200" y="232.5"/>
</window>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" allowsCharacterPickerTouchBarItem="YES" id="IoN-sN-cCx">
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" setsMaxLayoutWidthAtFirstLayout="YES" allowsCharacterPickerTouchBarItem="YES" id="IoN-sN-cCx">
<rect key="frame" x="0.0" y="0.0" width="471" height="180"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" controlSize="mini" sendsActionOnEndEditing="YES" drawsBackground="YES" id="Ay8-8n-FHi">
@@ -281,10 +293,10 @@
<slider identifier="Output Volume" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9Ru-Sc-dqC" userLabel="Output Volume Slider">
<rect key="frame" x="20" y="4" width="220" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<sliderCell key="cell" continuous="YES" state="on" alignment="left" maxValue="1" tickMarkPosition="above" sliderType="linear" id="MzM-fe-nKb"/>
<sliderCell key="cell" controlSize="small" continuous="YES" state="on" alignment="left" maxValue="1" tickMarkPosition="above" sliderType="linear" id="MzM-fe-nKb"/>
<accessibility description="Output Volume" help="Sets the volume of your audio output device." identifier="Output Volume"/>
</slider>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wfC-C6-SLv">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wfC-C6-SLv">
<rect key="frame" x="20" y="25" width="226" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Volume" id="60O-ju-B5C">
@@ -303,10 +315,10 @@
<slider identifier="System Sounds Volume" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gyd-WV-2ju" userLabel="Output Volume Slider">
<rect key="frame" x="163" y="0.0" width="74" height="15"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<sliderCell key="cell" controlSize="small" continuous="YES" state="on" alignment="left" maxValue="1" doubleValue="1" tickMarkPosition="above" sliderType="linear" id="VDn-d8-XK3"/>
<sliderCell key="cell" controlSize="mini" continuous="YES" state="on" alignment="left" maxValue="1" doubleValue="1" tickMarkPosition="above" sliderType="linear" id="VDn-d8-XK3"/>
<accessibility description="System Sounds Volume" help="Volume of alerts, notification sounds, etc. Usually short. Can be played by any app."/>
</slider>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iKs-df-Hp6">
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iKs-df-Hp6">
<rect key="frame" x="42" y="1" width="86" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="System Sounds" id="ATK-L8-s8z">
@@ -330,53 +342,54 @@
<image name="buttonCell:IXo-C7-3uE:image" width="1" height="1">
<mutableData key="keyedArchiveRepresentation">
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwXGB0iIycoLzI1Oz5VJG51bGzVDQ4PEBESExQVFlZOU1Np
emVWJGNsYXNzXE5TSW1hZ2VGbGFnc1ZOU1JlcHNXTlNDb2xvcoACgA0SIMMAAIADgAtWezEsIDF90hkO
GhxaTlMub2JqZWN0c6EbgASACtIZDh4hoh8ggAWABoAJEADSJA4lJl8QFE5TVElGRlJlcHJlc2VudGF0
aW9ugAeACE8RCMRNTQAqAAAACgAAABABAAADAAAAAQABAAABAQADAAAAAQABAAABAgADAAAAAgAIAAgB
AwADAAAAAQABAAABBgADAAAAAQABAAABCgADAAAAAQABAAABEQAEAAAAAQAAAAgBEgADAAAAAQABAAAB
FQADAAAAAQACAAABFgADAAAAAQABAAABFwAEAAAAAQAAAAIBHAADAAAAAQABAAABKAADAAAAAQACAAAB
UgADAAAAAQABAAABUwADAAAAAgABAAGHcwAHAAAH9AAAANAAAAAAAAAH9GFwcGwCIAAAbW50ckdSQVlY
WVogB9AAAgAOAAwAAAAAYWNzcEFQUEwAAAAAbm9uZQAAAAAAAAAAAAAAAAAAAAAAAPbWAAEAAAAA0y1h
cHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFZGVzYwAAAMAA
AABvZHNjbQAAATAAAAZmY3BydAAAB5gAAAA4d3RwdAAAB9AAAAAUa1RSQwAAB+QAAAAOZGVzYwAAAAAA
AAAVR2VuZXJpYyBHcmF5IFByb2ZpbGUAAAAAAAAAAAAAABVHZW5lcmljIEdyYXkgUHJvZmlsZQAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1sdWMAAAAAAAAAHwAAAAxz
a1NLAAAAKgAAAYRlblVTAAAAKAAAAa5jYUVTAAAALAAAAdZ2aVZOAAAALAAAAgJwdEJSAAAAKgAAAi51
a1VBAAAALAAAAlhmckZVAAAAKgAAAoRodUhVAAAALgAAAq56aFRXAAAAEAAAAtxuYk5PAAAALAAAAuxr
b0tSAAAAGAAAAxhjc0NaAAAAJAAAAzBoZUlMAAAAIAAAA1Ryb1JPAAAAJAAAA3RkZURFAAAAOgAAA5hp
dElUAAAALgAAA9JzdlNFAAAALgAABAB6aENOAAAAEAAABC5qYUpQAAAAFgAABD5lbEdSAAAAJAAABFRw
dFBPAAAAOAAABHhubE5MAAAAKgAABLBlc0VTAAAAKAAABNp0aFRIAAAAJAAABQJ0clRSAAAAIgAABSZm
aUZJAAAALAAABUhockhSAAAAOgAABXRwbFBMAAAANgAABa5ydVJVAAAAJgAABeRhckVHAAAAKAAABgpk
YURLAAAANAAABjIAVgFhAGUAbwBiAGUAYwBuAP0AIABzAGkAdgD9ACAAcAByAG8AZgBpAGwARwBlAG4A
ZQByAGkAYwAgAEcAcgBhAHkAIABQAHIAbwBmAGkAbABlAFAAZQByAGYAaQBsACAAZABlACAAZwByAGkA
cwAgAGcAZQBuAOgAcgBpAGMAQx6lAHUAIABoAOwAbgBoACAATQDgAHUAIAB4AOEAbQAgAEMAaAB1AG4A
ZwBQAGUAcgBmAGkAbAAgAEMAaQBuAHoAYQAgAEcAZQBuAOkAcgBpAGMAbwQXBDAEMwQwBDsETAQ9BDgE
OQAgBD8EQAQ+BEQEMAQ5BDsAIABHAHIAYQB5AFAAcgBvAGYAaQBsACAAZwDpAG4A6QByAGkAcQB1AGUA
IABnAHIAaQBzAMEAbAB0AGEAbADhAG4AbwBzACAAcwB6APwAcgBrAGUAIABwAHIAbwBmAGkAbJAadShw
cJaOgnJfaWPPj/AARwBlAG4AZQByAGkAcwBrACAAZwByAOUAdABvAG4AZQBwAHIAbwBmAGkAbMd8vBgA
IABHAHIAYQB5ACDVBLhc0wzHfABPAGIAZQBjAG4A/QAgAWEAZQBkAP0AIABwAHIAbwBmAGkAbAXkBegF
1QXkBdkF3AAgAEcAcgBhAHkAIAXbBdwF3AXZAFAAcgBvAGYAaQBsACAAZwByAGkAIABnAGUAbgBlAHIA
aQBjAEEAbABsAGcAZQBtAGUAaQBuAGUAcwAgAEcAcgBhAHUAcwB0AHUAZgBlAG4ALQBQAHIAbwBmAGkA
bABQAHIAbwBmAGkAbABvACAAZwByAGkAZwBpAG8AIABnAGUAbgBlAHIAaQBjAG8ARwBlAG4AZQByAGkA
cwBrACAAZwByAOUAcwBrAGEAbABlAHAAcgBvAGYAaQBsZm6QGnBwXqZjz4/wZYdO9k4AgiwwsDDsMKQw
1zDtMNUwoTCkMOsDkwO1A70DuQO6A8wAIAPAA8EDvwPGA68DuwAgA7MDugPBA7kAUABlAHIAZgBpAGwA
IABnAGUAbgDpAHIAaQBjAG8AIABkAGUAIABjAGkAbgB6AGUAbgB0AG8AcwBBAGwAZwBlAG0AZQBlAG4A
IABnAHIAaQBqAHMAcAByAG8AZgBpAGUAbABQAGUAcgBmAGkAbAAgAGcAcgBpAHMAIABnAGUAbgDpAHIA
aQBjAG8OQg4bDiMORA4fDiUOTA4qDjUOQA4XDjIOFw4xDkgOJw5EDhsARwBlAG4AZQBsACAARwByAGkA
IABQAHIAbwBmAGkAbABpAFkAbABlAGkAbgBlAG4AIABoAGEAcgBtAGEAYQBwAHIAbwBmAGkAaQBsAGkA
RwBlAG4AZQByAGkBDQBrAGkAIABwAHIAbwBmAGkAbAAgAHMAaQB2AGkAaAAgAHQAbwBuAG8AdgBhAFUA
bgBpAHcAZQByAHMAYQBsAG4AeQAgAHAAcgBvAGYAaQBsACAAcwB6AGEAcgBvAVsAYwBpBB4EMQRJBDgE
OQAgBEEENQRABEsEOQAgBD8EQAQ+BEQEOAQ7BEwGRQZEBkEAIAYqBjkGMQZKBkEAIABHAHIAYQB5ACAG
JwZEBjkGJwZFAEcAZQBuAGUAcgBlAGwAIABnAHIA5QB0AG8AbgBlAGIAZQBzAGsAcgBpAHYAZQBsAHMA
ZQAAdGV4dAAAAABDb3B5cmlnaHQgMjAwNyBBcHBsZSBJbmMuLCBhbGwgcmlnaHRzIHJlc2VydmVkLgBY
WVogAAAAAAAA81EAAQAAAAEWzGN1cnYAAAAAAAAAAQHNAADSKSorLFokY2xhc3NuYW1lWCRjbGFzc2Vz
XxAQTlNCaXRtYXBJbWFnZVJlcKMrLS5aTlNJbWFnZVJlcFhOU09iamVjdNIpKjAxV05TQXJyYXmiMC7S
KSozNF5OU011dGFibGVBcnJheaMzMC7TNjcOODk6V05TV2hpdGVcTlNDb2xvclNwYWNlRDAgMAAQA4AM
0ikqPD1XTlNDb2xvcqI8LtIpKj9AV05TSW1hZ2WiPy4ACAARABoAJAApADIANwBJAEwAUQBTAGIAaABz
AHoAgQCOAJUAnQCfAKEApgCoAKoAsQC2AMEAwwDFAMcAzADPANEA0wDVANcA3ADzAPUA9wm/CcQJzwnY
CesJ7wn6CgMKCAoQChMKGAonCisKMgo6CkcKTApOClAKVQpdCmAKZQptAAAAAAAAAgEAAAAAAAAAQQAA
AAAAAAAAAAAAAAAACnA
S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T
U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN
EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdMPJSYnKBRf
EBROU1RJRkZSZXByZXNlbnRhdGlvbl8QGU5TSW50ZXJuYWxMYXlvdXREaXJlY3Rpb26ACIAHTxEIxE1N
ACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAABAAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEG
AAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAABAAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEW
AAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAABAAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFT
AAMAAAACAAEAAYdzAAcAAAf0AAAA0AAAAAAAAAf0YXBwbAIgAABtbnRyR1JBWVhZWiAH0AACAA4ADAAA
AABhY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkZXNjAAAAwAAAAG9kc2NtAAABMAAA
BmZjcHJ0AAAHmAAAADh3dHB0AAAH0AAAABRrVFJDAAAH5AAAAA5kZXNjAAAAAAAAABVHZW5lcmljIEdy
YXkgUHJvZmlsZQAAAAAAAAAAAAAAFUdlbmVyaWMgR3JheSBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAfAAAADHNrU0sAAAAqAAABhGVu
VVMAAAAoAAABrmNhRVMAAAAsAAAB1nZpVk4AAAAsAAACAnB0QlIAAAAqAAACLnVrVUEAAAAsAAACWGZy
RlUAAAAqAAAChGh1SFUAAAAuAAACrnpoVFcAAAAQAAAC3G5iTk8AAAAsAAAC7GtvS1IAAAAYAAADGGNz
Q1oAAAAkAAADMGhlSUwAAAAgAAADVHJvUk8AAAAkAAADdGRlREUAAAA6AAADmGl0SVQAAAAuAAAD0nN2
U0UAAAAuAAAEAHpoQ04AAAAQAAAELmphSlAAAAAWAAAEPmVsR1IAAAAkAAAEVHB0UE8AAAA4AAAEeG5s
TkwAAAAqAAAEsGVzRVMAAAAoAAAE2nRoVEgAAAAkAAAFAnRyVFIAAAAiAAAFJmZpRkkAAAAsAAAFSGhy
SFIAAAA6AAAFdHBsUEwAAAA2AAAFrnJ1UlUAAAAmAAAF5GFyRUcAAAAoAAAGCmRhREsAAAA0AAAGMgBW
AWEAZQBvAGIAZQBjAG4A/QAgAHMAaQB2AP0AIABwAHIAbwBmAGkAbABHAGUAbgBlAHIAaQBjACAARwBy
AGEAeQAgAFAAcgBvAGYAaQBsAGUAUABlAHIAZgBpAGwAIABkAGUAIABnAHIAaQBzACAAZwBlAG4A6ABy
AGkAYwBDHqUAdQAgAGgA7ABuAGgAIABNAOAAdQAgAHgA4QBtACAAQwBoAHUAbgBnAFAAZQByAGYAaQBs
ACAAQwBpAG4AegBhACAARwBlAG4A6QByAGkAYwBvBBcEMAQzBDAEOwRMBD0EOAQ5ACAEPwRABD4ERAQw
BDkEOwAgAEcAcgBhAHkAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAwQBs
AHQAYQBsAOEAbgBvAHMAIABzAHoA/AByAGsAZQAgAHAAcgBvAGYAaQBskBp1KHBwlo6Ccl9pY8+P8ABH
AGUAbgBlAHIAaQBzAGsAIABnAHIA5QB0AG8AbgBlAHAAcgBvAGYAaQBsx3y8GAAgAEcAcgBhAHkAINUE
uFzTDMd8AE8AYgBlAGMAbgD9ACABYQBlAGQA/QAgAHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAARwBy
AGEAeQAgBdsF3AXcBdkAUAByAG8AZgBpAGwAIABnAHIAaQAgAGcAZQBuAGUAcgBpAGMAQQBsAGwAZwBl
AG0AZQBpAG4AZQBzACAARwByAGEAdQBzAHQAdQBmAGUAbgAtAFAAcgBvAGYAaQBsAFAAcgBvAGYAaQBs
AG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBpAGMAbwBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QBz
AGsAYQBsAGUAcAByAG8AZgBpAGxmbpAacHBepmPPj/Blh072TgCCLDCwMOwwpDDXMO0w1TChMKQw6wOT
A7UDvQO5A7oDzAAgA8ADwQO/A8YDrwO7ACADswO6A8EDuQBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBp
AGMAbwAgAGQAZQAgAGMAaQBuAHoAZQBuAHQAbwBzAEEAbABnAGUAbQBlAGUAbgAgAGcAcgBpAGoAcwBw
AHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwByAGkAcwAgAGcAZQBuAOkAcgBpAGMAbw5CDhsOIw5E
Dh8OJQ5MDioONQ5ADhcOMg4XDjEOSA4nDkQOGwBHAGUAbgBlAGwAIABHAHIAaQAgAFAAcgBvAGYAaQBs
AGkAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBhAHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQEN
AGsAaQAgAHAAcgBvAGYAaQBsACAAcwBpAHYAaQBoACAAdABvAG4AbwB2AGEAVQBuAGkAdwBlAHIAcwBh
AGwAbgB5ACAAcAByAG8AZgBpAGwAIABzAHoAYQByAG8BWwBjAGkEHgQxBEkEOAQ5ACAEQQQ1BEAESwQ5
ACAEPwRABD4ERAQ4BDsETAZFBkQGQQAgBioGOQYxBkoGQQAgAEcAcgBhAHkAIAYnBkQGOQYnBkUARwBl
AG4AZQByAGUAbAAgAGcAcgDlAHQAbwBuAGUAYgBlAHMAawByAGkAdgBlAGwAcwBlAAB0ZXh0AAAAAENv
cHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdodHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUQAB
AAAAARbMY3VydgAAAAAAAAABAc0AANIrLC0uWiRjbGFzc25hbWVYJGNsYXNzZXNfEBBOU0JpdG1hcElt
YWdlUmVwoy0vMFpOU0ltYWdlUmVwWE5TT2JqZWN00issMjNXTlNBcnJheaIyMNIrLDU2Xk5TTXV0YWJs
ZUFycmF5ozUyMNM4OQ86OzxXTlNXaGl0ZVxOU0NvbG9yU3BhY2VEMCAwABADgAzSKyw+P1dOU0NvbG9y
oj4w0issQUJXTlNJbWFnZaJBMAAIABEAGgAkACkAMgA3AEkATABRAFMAYgBoAHUAfACLAJIAnwCmAK4A
sACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA5gDoAO8BBgEiASQBJgnuCfMJ/goHChoKHgopCjIK
Nwo/CkIKRwpWCloKYQppCnYKewp9Cn8KhAqMCo8KlAqcAAAAAAAAAgEAAAAAAAAAQwAAAAAAAAAAAAAA
AAAACp8
</mutableData>
</image>
</resources>
+8 -2
View File
@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.3.1</string>
<string>0.4.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -33,7 +33,7 @@
<key>NSAppleScriptEnabled</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016-2019 Background Music contributors</string>
<string>Copyright © 2016-2024 Background Music contributors</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSMicrophoneUsageDescription</key>
@@ -46,5 +46,11 @@
</array>
<key>OSAScriptingDefinition</key>
<string>BGMApp.sdef</string>
<!--
Hopefully this will keep Background Music from using the discrete GPU on multi-GPU systems,
which wastes energy. I don't have any hardware I could easily test this on, so it's untested.
-->
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>
@@ -92,7 +92,11 @@
+ (NSArray<id<BGMMusicPlayer>>*) createInstancesWithDefaults:(BGMUserDefaults*)userDefaults {
#pragma unused (userDefaults)
// TODO: Fix this warning properly.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-literal-conversion"
return @[ [self new] ];
#pragma clang diagnostic pop
}
- (NSImage* __nullable) icon {
@@ -146,7 +146,7 @@
- (void) initSelectedMusicPlayerFromBGMDevice {
// When the selected music player setting hasn't been stored in user defaults yet, we get the music player
// bundle ID from the driver and look for the music player with that bundle ID. This is mainly done for
// backwards compatability.
// backwards compatibility.
NSString* __nullable bundleID =
(__bridge_transfer NSString* __nullable)[audioDevices bgmDevice].GetMusicPlayerBundleID();
+1 -1
View File
@@ -18,7 +18,7 @@
// BGMApp
//
// Copyright © 2016-2018 Kyle Neideck
// Portions copyright (C) 2012 Peter Ljunglöf. All rights reserved.
// Copyright (C) 2012 Peter Ljunglöf. All rights reserved.
//
// Self Include
+1 -1
View File
@@ -221,7 +221,7 @@ typedef enum VLCEnum VLCEnum;
@property NSInteger audioVolume; // The volume of the current playlist item from 0 to 4, where 4 is 400%
@property NSInteger currentTime; // The current time of the current playlist item in seconds.
@property (readonly) NSInteger durationOfCurrentItem; // The duration of the current playlist item in seconds.
@property BOOL fullscreenMode; // indicates wheter fullscreen is enabled or not
@property BOOL fullscreenMode; // indicates whether fullscreen is enabled or not
@property (readonly) BOOL muted; // Is VLC currently muted?
@property (copy, readonly) NSString *nameOfCurrentItem; // Name of the current playlist item.
@property (copy, readonly) NSString *pathOfCurrentItem; // Path to the current playlist item.
+2 -2
View File
@@ -37,7 +37,7 @@
- (void) rewindForwardFast; // Rewind current track forward fast.
- (void) rewindBackward; // Rewind current track backward.
- (void) rewindBackwardFast; // Rewind current track backward fast.
- (void) increasVolume; // Increas volume.
- (void) increasVolume; // Increase volume.
- (void) decreaseVolume; // Decrease volume.
- (void) showHidePlaylist; // Show/Hide playlist.
@@ -62,7 +62,7 @@
@property (copy, readonly) NSString *album; // Current track album.
@property (copy, readonly) NSString *uniqueID; // Unique identifier for the current track.
@property double currentTime; // The current playback position.
@property (readonly) double totalTime; // The total time of the currenty playing track.
@property (readonly) double totalTime; // The total time of the currently playing track.
@property double playerVolume; // Player volume (0.0 to 1.0)
@property NSInteger repeatState; // Player repeat state (none = 0, repeat one = 1, repeat all = 2)
+37 -3
View File
@@ -17,7 +17,7 @@
// BGMAboutPanel.m
// BGMApp
//
// Copyright © 2016 Kyle Neideck
// Copyright © 2016, 2024 Kyle Neideck
//
// Self Include
@@ -35,6 +35,7 @@ NS_ASSUME_NONNULL_BEGIN
static NSInteger const kVersionLabelTag = 1;
static NSInteger const kCopyrightLabelTag = 2;
static NSInteger const kProjectWebsiteLabelTag = 3;
static NSInteger const kContributorsLabelTag = 4;
@implementation BGMAboutPanel {
NSPanel* aboutPanel;
@@ -42,6 +43,7 @@ static NSInteger const kProjectWebsiteLabelTag = 3;
NSTextField* versionLabel;
NSTextField* copyrightLabel;
NSTextField* websiteLabel;
NSTextField* contributorsLabel;
NSTextView* licenseView;
}
@@ -53,6 +55,7 @@ static NSInteger const kProjectWebsiteLabelTag = 3;
versionLabel = [[aboutPanel contentView] viewWithTag:kVersionLabelTag];
copyrightLabel = [[aboutPanel contentView] viewWithTag:kCopyrightLabelTag];
websiteLabel = [[aboutPanel contentView] viewWithTag:kProjectWebsiteLabelTag];
contributorsLabel = [[aboutPanel contentView] viewWithTag:kContributorsLabelTag];
licenseView = inLicenseView;
@@ -83,7 +86,10 @@ static NSInteger const kProjectWebsiteLabelTag = 3;
[[bundle infoDictionary] objectForKey:@"NSHumanReadableCopyright"];
if (copyrightNotice) {
copyrightLabel.stringValue = (NSString*)copyrightNotice;
// Remove the part that we replace with a link.
copyrightLabel.stringValue =
[((NSString*)copyrightNotice) stringByReplacingOccurrencesOfString:contributorsLabel.stringValue
withString:@""];
}
// Project website link label
@@ -97,6 +103,18 @@ static NSInteger const kProjectWebsiteLabelTag = 3;
attributes:@{ NSLinkAttributeName: projectURL,
NSFontAttributeName: linkFont }];
// Contributors link label
// TODO: Proper credits (i.e. in the app instead of just a link)
contributorsLabel.selectable = YES;
contributorsLabel.allowsEditingTextAttributes = YES;
NSString* contributorsURL = [NSString stringWithUTF8String:kBGMContributorsURL];
NSFont* cLinkFont = contributorsLabel.font ? contributorsLabel.font : [NSFont labelFontOfSize:0.0];
contributorsLabel.attributedStringValue =
[[NSAttributedString alloc] initWithString:contributorsLabel.stringValue
attributes:@{ NSLinkAttributeName: contributorsURL,
NSFontAttributeName: cLinkFont }];
// Load the text of the license into the text view
NSString* __nullable licensePath = [bundle pathForResource:@"LICENSE" ofType:nil];
@@ -122,9 +140,25 @@ static NSInteger const kProjectWebsiteLabelTag = 3;
- (void) show {
DebugMsg("BGMAboutPanel::showAboutPanel: Opening \"About Background Music\" panel");
[NSApp activateIgnoringOtherApps:YES];
// We have to make aboutPanel visible before calling [NSApp activateIgnoringOtherApps:YES]
// or the app won't be activated the first time (not sure why it only happens the first
// time) and aboutPanel won't open. WindowServer logs this explanation:
// 0[SetFrontProcessWithInfo]: CPS: Rejecting the request for pid 1234 due to the activation count being 0; launch ts=19302059379458, current time=19314267188375, window count = 0.
[aboutPanel setIsVisible:YES];
[aboutPanel makeKeyAndOrderFront:self];
// On macOS 14.4, aboutPanel needs "Release When Closed" unchecked in MainMenu.xib. Otherwise,
// aboutPanel will never open again if you click the close button.
// This is deprecated for NSApplication.activate, but that stops aboutPanel from ever being shown.
[NSApp activateIgnoringOtherApps:YES];
DebugMsg("BGMAboutPanel::showAboutPanel: Finished opening panel. "
"aboutPanel.isVisible %d, aboutPanel.isKeyWindow %d, NSApp.isActive %d",
aboutPanel.isVisible,
aboutPanel.isKeyWindow,
NSApp.isActive);
}
@end
@@ -0,0 +1,50 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMASApplication.h
// BGMApp
//
// Copyright © 2021 Marcus Wu
// Copyright © 2021 Kyle Neideck
//
// An AppleScript class for volume and pan settings for running applications.
//
// Local Includes
#import "BGMAppVolumesController.h"
// System Includes
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
@interface BGMASApplication : NSObject
- (instancetype) initWithApplication:(NSRunningApplication*)app
volumeController:(BGMAppVolumesController*)volumeController
parentSpecifier:(NSScriptObjectSpecifier* __nullable)parentSpecifier
index:(int)i;
@property (readonly) NSString* name;
@property (readonly) NSString* bundleID;
@property int volume;
@property int pan;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,91 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMASApplication.m
// BGMApp
//
// Copyright © 2021 Marcus Wu
// Copyright © 2021 Kyle Neideck
//
// Self Include
#import "BGMASApplication.h"
// Local Includes
#import "BGM_Types.h"
@implementation BGMASApplication {
NSScriptObjectSpecifier* parentSpecifier;
NSRunningApplication *application;
BGMAppVolumesController* appVolumesController;
int index;
}
- (instancetype) initWithApplication:(NSRunningApplication*)app
volumeController:(BGMAppVolumesController*)volumeController
parentSpecifier:(NSScriptObjectSpecifier* __nullable)parent
index:(int)i {
if ((self = [super init])) {
parentSpecifier = parent;
application = app;
appVolumesController = volumeController;
index = i;
}
return self;
}
- (NSString*) name {
return [NSString stringWithFormat:@"%@", [application localizedName]];
}
- (NSString*) bundleID {
return [NSString stringWithFormat:@"%@", [application bundleIdentifier]];
}
- (int) volume {
return [appVolumesController getVolumeAndPanForApp:application].volume;
}
- (void) setVolume:(int)vol {
BGMAppVolumeAndPan volume = {
.volume = vol,
.pan = kAppPanNoValue
};
[appVolumesController setVolumeAndPan:volume forApp:application];
}
- (int) pan {
return [appVolumesController getVolumeAndPanForApp:application].pan;
}
- (void) setPan:(int)pan {
BGMAppVolumeAndPan thePan = {
.volume = -1,
.pan = pan
};
[appVolumesController setVolumeAndPan:thePan forApp:application];
}
- (NSScriptObjectSpecifier* __nullable) objectSpecifier {
NSScriptClassDescription* parentClassDescription = [parentSpecifier keyClassDescription];
return [[NSNameSpecifier alloc] initWithContainerClassDescription:parentClassDescription
containerSpecifier:parentSpecifier
key:@"applications"
name:self.name];
}
@end
+59 -4
View File
@@ -9,8 +9,7 @@
<class name="output device"
code="aDev"
description="A hardware device that can play audio"
plural="output devices"
inherits="item">
plural="output devices">
<synonym name="audio device"/>
<cocoa class="BGMASOutputDevice"/>
@@ -19,14 +18,58 @@
code="pnam"
description="The name of the output device."
type="text"
access="r"/>
access="r">
<cocoa key="name"/>
</property>
<property name="selected"
code="Slcd"
type="boolean"
access="rw"
description="Is this the device to be used for audio output?">
<synonym name="default"/>
<synonym name="default"/>
<cocoa key="selected"/>
</property>
</class>
<class name="audio application"
code="aApp"
description="An application that can play audio"
plural="audio applications">
<synonym name="audio app"/>
<cocoa class="BGMASApplication"/>
<property name="name"
code="pnam"
description="The name of the application."
type="text"
access="r">
<cocoa key="name"/>
</property>
<property name="bundleID"
code="bdid"
description="The bundle ID of the application, e.g. 'com.somecompany.coolapp'."
type="text"
access="r">
<cocoa key="bundleID"/>
</property>
<property name="vol"
code="pVol"
type="integer"
access="rw"
description="The volume setting of the application">
<cocoa key="volume"/>
</property>
<property name="pan"
code="pPan"
type="integer"
access="rw"
description="The pan setting of the application">
<cocoa key="pan"/>
</property>
</class>
@@ -48,11 +91,23 @@
<cocoa key="selectedOutputDevice"/>
</property>
<property name="output volume"
type="real"
code="oVol"
access="rw"
description="The main output volume">
<synonym name="main volume"/>
<cocoa key="mainVolume"/>
</property>
<!-- Unintuitively, this is for the array of output devices. -->
<element type="output device" access="r">
<cocoa key="outputDevices"/>
</element>
<element type="audio application" access="r">
<cocoa key="applications"/>
</element>
</class>
</suite>
</dictionary>
@@ -18,12 +18,18 @@
// BGMApp
//
// Copyright © 2017 Kyle Neideck
// Copyright © 2021 Marcus Wu
//
#import "BGMAppDelegate.h"
// Local Includes
#import "BGMAudioDeviceManager.h"
#import "BGMAppVolumesController.h"
// Local Includes
#import "BGMASOutputDevice.h"
#import "BGMASApplication.h"
// System Includes
#import <Foundation/Foundation.h>
@@ -37,6 +43,8 @@
@property BGMASOutputDevice* selectedOutputDevice;
@property (readonly) NSArray<BGMASOutputDevice*>* outputDevices;
@property double mainVolume;
@property (readonly) NSArray<BGMASApplication*>* applications;
@end
@@ -18,6 +18,7 @@
// BGMApp
//
// Copyright © 2017 Kyle Neideck
// Copyright © 2021 Marcus Wu
//
// Self Include
@@ -30,6 +31,7 @@
#import "CAHALAudioSystemObject.h"
#import "CAAutoDisposer.h"
const AudioObjectPropertyScope kScope = kAudioDevicePropertyScopeOutput;
#pragma clang assume_nonnull begin
@@ -43,7 +45,7 @@
[key UTF8String]);
}
return [@[@"selectedOutputDevice", @"outputDevices"] containsObject:key];
return [@[@"selectedOutputDevice", @"outputDevices", @"mainVolume", @"applications"] containsObject:key];
}
- (BGMASOutputDevice*) selectedOutputDevice {
@@ -83,6 +85,30 @@
return outputDevices;
}
- (double) mainVolume {
BGMAudioDevice bgmDevice = [self.audioDevices bgmDevice];
return bgmDevice.GetVolumeControlScalarValue(kScope, kMasterChannel);
}
- (void) setMainVolume:(double)mainVolume {
BGMAudioDevice bgmDevice = [self.audioDevices bgmDevice];
bgmDevice.SetMasterVolumeScalar(kScope, (Float32)mainVolume);
[self.outputVolumeSlider setFloatValue:(float)mainVolume];
}
- (NSArray<BGMASApplication*>*) applications {
NSArray<NSRunningApplication*>* apps = [[NSWorkspace sharedWorkspace] runningApplications];
NSMutableArray<BGMASApplication*>* applications = [NSMutableArray arrayWithCapacity:[apps count]];
for (UInt32 i = 0; i < [apps count]; i++) {
BGMASApplication *app = [[BGMASApplication alloc] initWithApplication:apps[i] volumeController:self.appVolumes parentSpecifier:[self objectSpecifier] index:i];
[applications addObject:app];
}
return applications;
}
@end
#pragma clang assume_nonnull end
+12 -9
View File
@@ -20,7 +20,7 @@
# _uninstall-non-interactive.sh
#
# Copyright © 2016 Nick Jacques
# Copyright © 2016, 2017 Kyle Neideck
# Copyright © 2016, 2017, 2021 Kyle Neideck
#
# Removes BGMApp, BGMDriver and BGMXPCHelper from the system immediately. Run by uninstall.sh and the Homebrew formula.
#
@@ -43,8 +43,9 @@ driver_path="/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"
xpc_path1="/usr/local/libexec/BGMXPCHelper.xpc"
xpc_path2="/Library/Application Support/Background Music/BGMXPCHelper.xpc"
# Check that files/directories are at most this big before we delete them, just to be safe.
max_size_mb_for_rm=15
# Check that files/directories are at most this big before we delete them, just to be safe. Note that the bundles can
# include debug symbols, e.g. if you use build_and_install.sh, which makes them a lot bigger.
max_size_mb_for_rm=30
file_paths=("${app_path}" "${driver_path}" "${xpc_path1}" "${xpc_path2}")
@@ -68,8 +69,9 @@ function size_check {
[[ "${size}" =~ ^[0-9]+$ ]] && [[ "${size}" -le ${max_size_mb_for_rm} ]]
}
# Ensure that the user can use sudo. (But not if this is a Travis CI build, because then it would fail.)
if ([[ -z ${TRAVIS:-} ]] || [[ "${TRAVIS}" != true ]]) && ! sudo -v; then
# Ensure that the user can use sudo. (Use `sudo true` instead of `sudo -v` because that causes a
# password prompt in Travis CI builds for some reason.)
if ! sudo true; then
echo "ERROR: This script must be run by a user with administrator (sudo) privileges." >&2
exit 1
fi
@@ -137,20 +139,21 @@ sleep 2
# The extra or-clauses are fallback versions of the command that restarts coreaudiod. Apparently some of these commands
# don't work with older versions of launchctl, so I figure there's no harm in trying a bunch of different ways until
# one works.
(sudo launchctl kill SIGTERM system/com.apple.audio.coreaudiod &>/dev/null || \
(sudo launchctl kickstart -k system/com.apple.audio.coreaudiod &>/dev/null || \
sudo killall coreaudiod &>/dev/null || \
sudo launchctl kill SIGTERM system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill TERM system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill 15 system/com.apple.audio.coreaudiod &>/dev/null || \
sudo launchctl kill -15 system/com.apple.audio.coreaudiod &>/dev/null || \
(sudo launchctl unload "${coreaudiod_plist}" &>/dev/null && \
sudo launchctl load "${coreaudiod_plist}" &>/dev/null) || \
sudo killall coreaudiod &>/dev/null)
sudo launchctl load "${coreaudiod_plist}" &>/dev/null))
echo "..."
sleep 3
# TODO: What if they only have one audio device?
echo ""
echo "${bold}Done! Toggle your audio output device in the Sound section of System Preferences to finish" \
echo "${bold}Done! Toggle your audio output device in the Sound section of System Settings to finish" \
"uninstalling. (Or just restart your computer.)${normal}"
@@ -1,754 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
2D46CA7B17D6ADCA00049D4A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2D46CA7817D6AD8A00049D4A /* Localizable.strings */; };
2D46CA7D17D6AF4200049D4A /* DeviceIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 2D46CA7C17D6AF4200049D4A /* DeviceIcon.icns */; };
2D47CAA415FEC82B002AAFB5 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 2D47CAA215FEC82B002AAFB5 /* Localizable.strings */; };
2D4DE41415EDF8D500E96F0D /* CAVolumeCurve.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D4DE41215EDF8D500E96F0D /* CAVolumeCurve.cpp */; };
2D4DE41515EDF8D500E96F0D /* CAVolumeCurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D4DE41315EDF8D500E96F0D /* CAVolumeCurve.h */; };
2D616EF415B8C82500D598BD /* NullAudio.c in Sources */ = {isa = PBXBuildFile; fileRef = 2D616EF215B8C82500D598BD /* NullAudio.c */; };
2D7477AD1578168D00412279 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D7477AC1578168D00412279 /* CoreFoundation.framework */; };
2D76D96115E48B2000FF0F33 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D7477AC1578168D00412279 /* CoreFoundation.framework */; };
2D76D97615E48B6400FF0F33 /* SA_PlugIn.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D76D97415E48B6400FF0F33 /* SA_PlugIn.cpp */; };
2D76D97715E48B6400FF0F33 /* SA_PlugIn.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D76D97515E48B6400FF0F33 /* SA_PlugIn.h */; };
2D76D97A15E498EB00FF0F33 /* SA_Object.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2D76D97815E498EB00FF0F33 /* SA_Object.cpp */; };
2D76D97B15E498EB00FF0F33 /* SA_Object.h in Headers */ = {isa = PBXBuildFile; fileRef = 2D76D97915E498EB00FF0F33 /* SA_Object.h */; };
2DD7AA0A15EACDE000C67AE1 /* SA_IOKit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA0815EACDDF00C67AE1 /* SA_IOKit.cpp */; };
2DD7AA0B15EACDE000C67AE1 /* SA_IOKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA0915EACDDF00C67AE1 /* SA_IOKit.h */; };
2DD7AA2315EAFD5100C67AE1 /* CACFArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA0E15EAFD3300C67AE1 /* CACFArray.cpp */; };
2DD7AA2415EAFD5100C67AE1 /* CACFArray.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA0F15EAFD3300C67AE1 /* CACFArray.h */; };
2DD7AA2515EAFD5100C67AE1 /* CACFDictionary.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1015EAFD3300C67AE1 /* CACFDictionary.cpp */; };
2DD7AA2615EAFD5100C67AE1 /* CACFDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1115EAFD3300C67AE1 /* CACFDictionary.h */; };
2DD7AA2715EAFD5100C67AE1 /* CACFNumber.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1215EAFD3300C67AE1 /* CACFNumber.cpp */; };
2DD7AA2815EAFD5100C67AE1 /* CACFNumber.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1315EAFD3300C67AE1 /* CACFNumber.h */; };
2DD7AA2915EAFD5100C67AE1 /* CACFString.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1415EAFD3300C67AE1 /* CACFString.cpp */; };
2DD7AA2A15EAFD5100C67AE1 /* CACFString.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1515EAFD3300C67AE1 /* CACFString.h */; };
2DD7AA2B15EAFD5100C67AE1 /* CADebugger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1615EAFD3300C67AE1 /* CADebugger.cpp */; };
2DD7AA2C15EAFD5100C67AE1 /* CADebugger.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1715EAFD3300C67AE1 /* CADebugger.h */; };
2DD7AA2D15EAFD5100C67AE1 /* CADebugMacros.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1815EAFD3300C67AE1 /* CADebugMacros.cpp */; };
2DD7AA2E15EAFD5100C67AE1 /* CADebugMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1915EAFD3300C67AE1 /* CADebugMacros.h */; };
2DD7AA2F15EAFD5100C67AE1 /* CADebugPrintf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1A15EAFD3300C67AE1 /* CADebugPrintf.cpp */; };
2DD7AA3015EAFD5100C67AE1 /* CADebugPrintf.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1B15EAFD3300C67AE1 /* CADebugPrintf.h */; };
2DD7AA3115EAFD5100C67AE1 /* CAException.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1C15EAFD3300C67AE1 /* CAException.h */; };
2DD7AA3215EAFD5100C67AE1 /* CAGuard.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1D15EAFD3300C67AE1 /* CAGuard.cpp */; };
2DD7AA3315EAFD5100C67AE1 /* CAGuard.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA1E15EAFD3300C67AE1 /* CAGuard.h */; };
2DD7AA3415EAFD5100C67AE1 /* CAHostTimeBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA1F15EAFD3300C67AE1 /* CAHostTimeBase.cpp */; };
2DD7AA3515EAFD5100C67AE1 /* CAHostTimeBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA2015EAFD3300C67AE1 /* CAHostTimeBase.h */; };
2DD7AA3615EAFD5100C67AE1 /* CAMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA2115EAFD3300C67AE1 /* CAMutex.cpp */; };
2DD7AA3715EAFD5100C67AE1 /* CAMutex.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA2215EAFD3300C67AE1 /* CAMutex.h */; };
2DD7AA7D15EC20FD00C67AE1 /* CADispatchQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA7B15EC20FD00C67AE1 /* CADispatchQueue.cpp */; };
2DD7AA7E15EC20FD00C67AE1 /* CADispatchQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA7C15EC20FD00C67AE1 /* CADispatchQueue.h */; };
2DD7AA8015EC3DB800C67AE1 /* CACFObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA7F15EC3DB800C67AE1 /* CACFObject.h */; };
2DD7AA9715EC551600C67AE1 /* SA_Device.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DD7AA9515EC551500C67AE1 /* SA_Device.cpp */; };
2DD7AA9815EC551600C67AE1 /* SA_Device.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DD7AA9615EC551600C67AE1 /* SA_Device.h */; };
2DD7AA9A15EC572000C67AE1 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DD7AA9915EC572000C67AE1 /* IOKit.framework */; };
2DED184915C359AC0091BE97 /* SimpleAudioDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DED182515C356BA0091BE97 /* SimpleAudioDriver.cpp */; };
2DED184A15C359AC0091BE97 /* SimpleAudioDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DED182615C356BA0091BE97 /* SimpleAudioDriver.h */; };
2DED184B15C359AC0091BE97 /* SimpleAudioDriverUserClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2DED182915C356BA0091BE97 /* SimpleAudioDriverUserClient.cpp */; };
2DED184C15C359AC0091BE97 /* SimpleAudioDriverUserClient.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DED182A15C356BA0091BE97 /* SimpleAudioDriverUserClient.h */; };
2DED184D15C359AC0091BE97 /* SimpleAudioDriverTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DED182B15C356BA0091BE97 /* SimpleAudioDriverTypes.h */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
2D46CA7917D6AD8A00049D4A /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/Localizable.strings; sourceTree = "<group>"; };
2D46CA7C17D6AF4200049D4A /* DeviceIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = DeviceIcon.icns; sourceTree = "<group>"; };
2D47CAA315FEC82B002AAFB5 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/Localizable.strings; sourceTree = "<group>"; };
2D4DE41215EDF8D500E96F0D /* CAVolumeCurve.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CAVolumeCurve.cpp; sourceTree = "<group>"; };
2D4DE41315EDF8D500E96F0D /* CAVolumeCurve.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CAVolumeCurve.h; sourceTree = "<group>"; };
2D616EF115B8C82500D598BD /* NullAudio-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "NullAudio-Info.plist"; sourceTree = "<group>"; };
2D616EF215B8C82500D598BD /* NullAudio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = NullAudio.c; sourceTree = "<group>"; };
2D7477A91578168D00412279 /* NullAudio.driver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NullAudio.driver; sourceTree = BUILT_PRODUCTS_DIR; };
2D7477AC1578168D00412279 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
2D7477EC157823CF00412279 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
2D76D96015E48B2000FF0F33 /* SimpleAudioPlugIn.driver */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimpleAudioPlugIn.driver; sourceTree = BUILT_PRODUCTS_DIR; };
2D76D97415E48B6400FF0F33 /* SA_PlugIn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SA_PlugIn.cpp; sourceTree = "<group>"; };
2D76D97515E48B6400FF0F33 /* SA_PlugIn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SA_PlugIn.h; sourceTree = "<group>"; };
2D76D97815E498EB00FF0F33 /* SA_Object.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SA_Object.cpp; sourceTree = "<group>"; };
2D76D97915E498EB00FF0F33 /* SA_Object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SA_Object.h; sourceTree = "<group>"; };
2D76D98B15E56E4E00FF0F33 /* SA_PlugIn-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SA_PlugIn-Info.plist"; sourceTree = "<group>"; };
2DA8FA1515FEAAB000F04B50 /* ReadMe.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = ReadMe.txt; sourceTree = "<group>"; };
2DD7AA0815EACDDF00C67AE1 /* SA_IOKit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SA_IOKit.cpp; sourceTree = "<group>"; };
2DD7AA0915EACDDF00C67AE1 /* SA_IOKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SA_IOKit.h; sourceTree = "<group>"; };
2DD7AA0E15EAFD3300C67AE1 /* CACFArray.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CACFArray.cpp; sourceTree = "<group>"; };
2DD7AA0F15EAFD3300C67AE1 /* CACFArray.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CACFArray.h; sourceTree = "<group>"; };
2DD7AA1015EAFD3300C67AE1 /* CACFDictionary.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CACFDictionary.cpp; sourceTree = "<group>"; };
2DD7AA1115EAFD3300C67AE1 /* CACFDictionary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CACFDictionary.h; sourceTree = "<group>"; };
2DD7AA1215EAFD3300C67AE1 /* CACFNumber.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CACFNumber.cpp; sourceTree = "<group>"; };
2DD7AA1315EAFD3300C67AE1 /* CACFNumber.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CACFNumber.h; sourceTree = "<group>"; };
2DD7AA1415EAFD3300C67AE1 /* CACFString.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CACFString.cpp; sourceTree = "<group>"; };
2DD7AA1515EAFD3300C67AE1 /* CACFString.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CACFString.h; sourceTree = "<group>"; };
2DD7AA1615EAFD3300C67AE1 /* CADebugger.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CADebugger.cpp; sourceTree = "<group>"; };
2DD7AA1715EAFD3300C67AE1 /* CADebugger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CADebugger.h; sourceTree = "<group>"; };
2DD7AA1815EAFD3300C67AE1 /* CADebugMacros.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CADebugMacros.cpp; sourceTree = "<group>"; };
2DD7AA1915EAFD3300C67AE1 /* CADebugMacros.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CADebugMacros.h; sourceTree = "<group>"; };
2DD7AA1A15EAFD3300C67AE1 /* CADebugPrintf.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CADebugPrintf.cpp; sourceTree = "<group>"; };
2DD7AA1B15EAFD3300C67AE1 /* CADebugPrintf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CADebugPrintf.h; sourceTree = "<group>"; };
2DD7AA1C15EAFD3300C67AE1 /* CAException.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAException.h; sourceTree = "<group>"; };
2DD7AA1D15EAFD3300C67AE1 /* CAGuard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CAGuard.cpp; sourceTree = "<group>"; };
2DD7AA1E15EAFD3300C67AE1 /* CAGuard.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAGuard.h; sourceTree = "<group>"; };
2DD7AA1F15EAFD3300C67AE1 /* CAHostTimeBase.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CAHostTimeBase.cpp; sourceTree = "<group>"; };
2DD7AA2015EAFD3300C67AE1 /* CAHostTimeBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAHostTimeBase.h; sourceTree = "<group>"; };
2DD7AA2115EAFD3300C67AE1 /* CAMutex.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CAMutex.cpp; sourceTree = "<group>"; };
2DD7AA2215EAFD3300C67AE1 /* CAMutex.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CAMutex.h; sourceTree = "<group>"; };
2DD7AA7B15EC20FD00C67AE1 /* CADispatchQueue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CADispatchQueue.cpp; sourceTree = "<group>"; };
2DD7AA7C15EC20FD00C67AE1 /* CADispatchQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CADispatchQueue.h; sourceTree = "<group>"; };
2DD7AA7F15EC3DB800C67AE1 /* CACFObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CACFObject.h; sourceTree = "<group>"; };
2DD7AA9515EC551500C67AE1 /* SA_Device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SA_Device.cpp; sourceTree = "<group>"; };
2DD7AA9615EC551600C67AE1 /* SA_Device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SA_Device.h; sourceTree = "<group>"; };
2DD7AA9915EC572000C67AE1 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
2DED182415C356BA0091BE97 /* SimpleAudioDriver-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SimpleAudioDriver-Info.plist"; sourceTree = "<group>"; };
2DED182515C356BA0091BE97 /* SimpleAudioDriver.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SimpleAudioDriver.cpp; sourceTree = "<group>"; };
2DED182615C356BA0091BE97 /* SimpleAudioDriver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleAudioDriver.h; sourceTree = "<group>"; };
2DED182915C356BA0091BE97 /* SimpleAudioDriverUserClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SimpleAudioDriverUserClient.cpp; sourceTree = "<group>"; };
2DED182A15C356BA0091BE97 /* SimpleAudioDriverUserClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleAudioDriverUserClient.h; sourceTree = "<group>"; };
2DED182B15C356BA0091BE97 /* SimpleAudioDriverTypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SimpleAudioDriverTypes.h; sourceTree = "<group>"; };
2DED183815C357180091BE97 /* SimpleAudioDriver.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SimpleAudioDriver.kext; sourceTree = BUILT_PRODUCTS_DIR; };
2DED183A15C357180091BE97 /* Kernel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Kernel.framework; path = System/Library/Frameworks/Kernel.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
2D7477A61578168D00412279 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2D7477AD1578168D00412279 /* CoreFoundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D76D95D15E48B2000FF0F33 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2D76D96115E48B2000FF0F33 /* CoreFoundation.framework in Frameworks */,
2DD7AA9A15EC572000C67AE1 /* IOKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2DED183315C357180091BE97 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2D616EF015B8C82500D598BD /* NullAudio */ = {
isa = PBXGroup;
children = (
2D46CA7C17D6AF4200049D4A /* DeviceIcon.icns */,
2D46CA7817D6AD8A00049D4A /* Localizable.strings */,
2D616EF115B8C82500D598BD /* NullAudio-Info.plist */,
2D616EF215B8C82500D598BD /* NullAudio.c */,
);
path = NullAudio;
sourceTree = "<group>";
};
2D74779B1578162B00412279 = {
isa = PBXGroup;
children = (
2DA8FA1515FEAAB000F04B50 /* ReadMe.txt */,
2D616EF015B8C82500D598BD /* NullAudio */,
2DED182215C356BA0091BE97 /* SimpleAudio */,
2DD7AA0D15EAFD3300C67AE1 /* PublicUtility */,
2D7477AB1578168D00412279 /* Frameworks */,
2D7477AA1578168D00412279 /* Products */,
);
sourceTree = "<group>";
};
2D7477AA1578168D00412279 /* Products */ = {
isa = PBXGroup;
children = (
2D7477A91578168D00412279 /* NullAudio.driver */,
2DED183815C357180091BE97 /* SimpleAudioDriver.kext */,
2D76D96015E48B2000FF0F33 /* SimpleAudioPlugIn.driver */,
);
name = Products;
sourceTree = "<group>";
};
2D7477AB1578168D00412279 /* Frameworks */ = {
isa = PBXGroup;
children = (
2D7477EC157823CF00412279 /* CoreAudio.framework */,
2D7477AC1578168D00412279 /* CoreFoundation.framework */,
2DD7AA9915EC572000C67AE1 /* IOKit.framework */,
2DED183A15C357180091BE97 /* Kernel.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
2DA8FA7E15FEC6B500F04B50 /* Resources */ = {
isa = PBXGroup;
children = (
2D47CAA215FEC82B002AAFB5 /* Localizable.strings */,
);
path = Resources;
sourceTree = "<group>";
};
2DD7AA0D15EAFD3300C67AE1 /* PublicUtility */ = {
isa = PBXGroup;
children = (
2DD7AA0E15EAFD3300C67AE1 /* CACFArray.cpp */,
2DD7AA0F15EAFD3300C67AE1 /* CACFArray.h */,
2DD7AA1015EAFD3300C67AE1 /* CACFDictionary.cpp */,
2DD7AA1115EAFD3300C67AE1 /* CACFDictionary.h */,
2DD7AA1215EAFD3300C67AE1 /* CACFNumber.cpp */,
2DD7AA1315EAFD3300C67AE1 /* CACFNumber.h */,
2DD7AA7F15EC3DB800C67AE1 /* CACFObject.h */,
2DD7AA1415EAFD3300C67AE1 /* CACFString.cpp */,
2DD7AA1515EAFD3300C67AE1 /* CACFString.h */,
2DD7AA1615EAFD3300C67AE1 /* CADebugger.cpp */,
2DD7AA1715EAFD3300C67AE1 /* CADebugger.h */,
2DD7AA1815EAFD3300C67AE1 /* CADebugMacros.cpp */,
2DD7AA1915EAFD3300C67AE1 /* CADebugMacros.h */,
2DD7AA1A15EAFD3300C67AE1 /* CADebugPrintf.cpp */,
2DD7AA1B15EAFD3300C67AE1 /* CADebugPrintf.h */,
2DD7AA7B15EC20FD00C67AE1 /* CADispatchQueue.cpp */,
2DD7AA7C15EC20FD00C67AE1 /* CADispatchQueue.h */,
2DD7AA1C15EAFD3300C67AE1 /* CAException.h */,
2DD7AA1D15EAFD3300C67AE1 /* CAGuard.cpp */,
2DD7AA1E15EAFD3300C67AE1 /* CAGuard.h */,
2DD7AA1F15EAFD3300C67AE1 /* CAHostTimeBase.cpp */,
2DD7AA2015EAFD3300C67AE1 /* CAHostTimeBase.h */,
2DD7AA2115EAFD3300C67AE1 /* CAMutex.cpp */,
2DD7AA2215EAFD3300C67AE1 /* CAMutex.h */,
2D4DE41215EDF8D500E96F0D /* CAVolumeCurve.cpp */,
2D4DE41315EDF8D500E96F0D /* CAVolumeCurve.h */,
);
path = PublicUtility;
sourceTree = "<group>";
};
2DED182215C356BA0091BE97 /* SimpleAudio */ = {
isa = PBXGroup;
children = (
2DED182315C356BA0091BE97 /* Driver */,
2DED182C15C356BA0091BE97 /* Plug-In */,
);
path = SimpleAudio;
sourceTree = "<group>";
};
2DED182315C356BA0091BE97 /* Driver */ = {
isa = PBXGroup;
children = (
2DED182415C356BA0091BE97 /* SimpleAudioDriver-Info.plist */,
2DED182515C356BA0091BE97 /* SimpleAudioDriver.cpp */,
2DED182615C356BA0091BE97 /* SimpleAudioDriver.h */,
2DED182915C356BA0091BE97 /* SimpleAudioDriverUserClient.cpp */,
2DED182A15C356BA0091BE97 /* SimpleAudioDriverUserClient.h */,
2DED182B15C356BA0091BE97 /* SimpleAudioDriverTypes.h */,
);
path = Driver;
sourceTree = "<group>";
};
2DED182C15C356BA0091BE97 /* Plug-In */ = {
isa = PBXGroup;
children = (
2DD7AA9515EC551500C67AE1 /* SA_Device.cpp */,
2DD7AA9615EC551600C67AE1 /* SA_Device.h */,
2DD7AA0815EACDDF00C67AE1 /* SA_IOKit.cpp */,
2DD7AA0915EACDDF00C67AE1 /* SA_IOKit.h */,
2D76D97815E498EB00FF0F33 /* SA_Object.cpp */,
2D76D97915E498EB00FF0F33 /* SA_Object.h */,
2D76D98B15E56E4E00FF0F33 /* SA_PlugIn-Info.plist */,
2D76D97415E48B6400FF0F33 /* SA_PlugIn.cpp */,
2D76D97515E48B6400FF0F33 /* SA_PlugIn.h */,
2DA8FA7E15FEC6B500F04B50 /* Resources */,
);
path = "Plug-In";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
2D76D95E15E48B2000FF0F33 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
2D76D97715E48B6400FF0F33 /* SA_PlugIn.h in Headers */,
2D76D97B15E498EB00FF0F33 /* SA_Object.h in Headers */,
2DD7AA0B15EACDE000C67AE1 /* SA_IOKit.h in Headers */,
2DD7AA2415EAFD5100C67AE1 /* CACFArray.h in Headers */,
2DD7AA2615EAFD5100C67AE1 /* CACFDictionary.h in Headers */,
2DD7AA2815EAFD5100C67AE1 /* CACFNumber.h in Headers */,
2DD7AA2A15EAFD5100C67AE1 /* CACFString.h in Headers */,
2DD7AA2C15EAFD5100C67AE1 /* CADebugger.h in Headers */,
2DD7AA2E15EAFD5100C67AE1 /* CADebugMacros.h in Headers */,
2DD7AA3015EAFD5100C67AE1 /* CADebugPrintf.h in Headers */,
2DD7AA3115EAFD5100C67AE1 /* CAException.h in Headers */,
2DD7AA3315EAFD5100C67AE1 /* CAGuard.h in Headers */,
2DD7AA3515EAFD5100C67AE1 /* CAHostTimeBase.h in Headers */,
2DD7AA3715EAFD5100C67AE1 /* CAMutex.h in Headers */,
2DD7AA7E15EC20FD00C67AE1 /* CADispatchQueue.h in Headers */,
2DD7AA8015EC3DB800C67AE1 /* CACFObject.h in Headers */,
2DD7AA9815EC551600C67AE1 /* SA_Device.h in Headers */,
2D4DE41515EDF8D500E96F0D /* CAVolumeCurve.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2DED183415C357180091BE97 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
2DED184A15C359AC0091BE97 /* SimpleAudioDriver.h in Headers */,
2DED184C15C359AC0091BE97 /* SimpleAudioDriverUserClient.h in Headers */,
2DED184D15C359AC0091BE97 /* SimpleAudioDriverTypes.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
2D7477A81578168D00412279 /* NullAudio */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2D7477B51578168D00412279 /* Build configuration list for PBXNativeTarget "NullAudio" */;
buildPhases = (
2D7477A51578168D00412279 /* Sources */,
2D7477A61578168D00412279 /* Frameworks */,
2D46CA7A17D6ADC500049D4A /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = NullAudio;
productName = AudioNULLDriver;
productReference = 2D7477A91578168D00412279 /* NullAudio.driver */;
productType = "com.apple.product-type.bundle";
};
2D76D95F15E48B2000FF0F33 /* SimpleAudioPlugIn */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2D76D97215E48B2000FF0F33 /* Build configuration list for PBXNativeTarget "SimpleAudioPlugIn" */;
buildPhases = (
2D76D95E15E48B2000FF0F33 /* Headers */,
2D76D95C15E48B2000FF0F33 /* Sources */,
2D76D95D15E48B2000FF0F33 /* Frameworks */,
2DA8FA4B15FEABE000F04B50 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SimpleAudioPlugIn;
productName = SimpleAudioPlugIn;
productReference = 2D76D96015E48B2000FF0F33 /* SimpleAudioPlugIn.driver */;
productType = "com.apple.product-type.bundle";
};
2DED183715C357180091BE97 /* SimpleAudioDriver */ = {
isa = PBXNativeTarget;
buildConfigurationList = 2DED184515C357180091BE97 /* Build configuration list for PBXNativeTarget "SimpleAudioDriver" */;
buildPhases = (
2DED183415C357180091BE97 /* Headers */,
2DED183215C357180091BE97 /* Sources */,
2DED183315C357180091BE97 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = SimpleAudioDriver;
productName = SimpleAudioDriver;
productReference = 2DED183815C357180091BE97 /* SimpleAudioDriver.kext */;
productType = "com.apple.product-type.kernel-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
2D74779D1578162B00412279 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0500;
};
buildConfigurationList = 2D7477A01578162B00412279 /* Build configuration list for PBXProject "AudioDriverExamples" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
English,
);
mainGroup = 2D74779B1578162B00412279;
productRefGroup = 2D7477AA1578168D00412279 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
2D7477A81578168D00412279 /* NullAudio */,
2DED183715C357180091BE97 /* SimpleAudioDriver */,
2D76D95F15E48B2000FF0F33 /* SimpleAudioPlugIn */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
2D46CA7A17D6ADC500049D4A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2D46CA7B17D6ADCA00049D4A /* Localizable.strings in Resources */,
2D46CA7D17D6AF4200049D4A /* DeviceIcon.icns in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2DA8FA4B15FEABE000F04B50 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2D47CAA415FEC82B002AAFB5 /* Localizable.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
2D7477A51578168D00412279 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2D616EF415B8C82500D598BD /* NullAudio.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2D76D95C15E48B2000FF0F33 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2D76D97615E48B6400FF0F33 /* SA_PlugIn.cpp in Sources */,
2D76D97A15E498EB00FF0F33 /* SA_Object.cpp in Sources */,
2DD7AA0A15EACDE000C67AE1 /* SA_IOKit.cpp in Sources */,
2DD7AA2315EAFD5100C67AE1 /* CACFArray.cpp in Sources */,
2DD7AA2515EAFD5100C67AE1 /* CACFDictionary.cpp in Sources */,
2DD7AA2715EAFD5100C67AE1 /* CACFNumber.cpp in Sources */,
2DD7AA2915EAFD5100C67AE1 /* CACFString.cpp in Sources */,
2DD7AA2B15EAFD5100C67AE1 /* CADebugger.cpp in Sources */,
2DD7AA2D15EAFD5100C67AE1 /* CADebugMacros.cpp in Sources */,
2DD7AA2F15EAFD5100C67AE1 /* CADebugPrintf.cpp in Sources */,
2DD7AA3215EAFD5100C67AE1 /* CAGuard.cpp in Sources */,
2DD7AA3415EAFD5100C67AE1 /* CAHostTimeBase.cpp in Sources */,
2DD7AA3615EAFD5100C67AE1 /* CAMutex.cpp in Sources */,
2DD7AA7D15EC20FD00C67AE1 /* CADispatchQueue.cpp in Sources */,
2DD7AA9715EC551600C67AE1 /* SA_Device.cpp in Sources */,
2D4DE41415EDF8D500E96F0D /* CAVolumeCurve.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
2DED183215C357180091BE97 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
2DED184915C359AC0091BE97 /* SimpleAudioDriver.cpp in Sources */,
2DED184B15C359AC0091BE97 /* SimpleAudioDriverUserClient.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
2D46CA7817D6AD8A00049D4A /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
2D46CA7917D6AD8A00049D4A /* English */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
2D47CAA215FEC82B002AAFB5 /* Localizable.strings */ = {
isa = PBXVariantGroup;
children = (
2D47CAA315FEC82B002AAFB5 /* English */,
);
name = Localizable.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
2D7477A21578162B00412279 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_OPTIMIZATION_LEVEL = s;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
SDKROOT = macosx;
};
name = Release;
};
2D7477A31578162B00412279 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
SDKROOT = macosx;
};
name = Debug;
};
2D7477A41578164E00412279 /* Debug-Opt */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__EXIT_TIME_DESTRUCTORS = YES;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_OPTIMIZATION_LEVEL = s;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_HIDDEN_VIRTUAL_FUNCTIONS = YES;
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
GCC_WARN_NON_VIRTUAL_DESTRUCTOR = YES;
GCC_WARN_SHADOW = YES;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNKNOWN_PRAGMAS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
SDKROOT = macosx;
};
name = "Debug-Opt";
};
2D7477B61578168D00412279 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=0",
);
INFOPLIST_FILE = "NullAudio/NullAudio-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
};
name = Release;
};
2D7477B71578168D00412279 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=1",
);
INFOPLIST_FILE = "NullAudio/NullAudio-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
};
name = Debug;
};
2D7477B81578168D00412279 /* Debug-Opt */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"DEBUG=1",
);
INFOPLIST_FILE = "NullAudio/NullAudio-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
};
name = "Debug-Opt";
};
2D76D96F15E48B2000FF0F33 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_ENABLE_CPP_RTTI = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"CoreAudio_Debug=0",
"CoreAudio_UseSysLog=1",
);
INFOPLIST_FILE = "SimpleAudio/Plug-In/SA_PlugIn-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
};
name = Release;
};
2D76D97015E48B2000FF0F33 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_ENABLE_CPP_RTTI = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"CoreAudio_Debug=1",
"CoreAudio_UseSysLog=1",
);
INFOPLIST_FILE = "SimpleAudio/Plug-In/SA_PlugIn-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
};
name = Debug;
};
2D76D97115E48B2000FF0F33 /* Debug-Opt */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_ENABLE_CPP_RTTI = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"CoreAudio_Debug=1",
"CoreAudio_UseSysLog=1",
);
INFOPLIST_FILE = "SimpleAudio/Plug-In/SA_PlugIn-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = driver;
};
name = "Debug-Opt";
};
2DED184615C357180091BE97 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN__EXIT_TIME_DESTRUCTORS = NO;
GCC_PREPROCESSOR_DEFINITIONS = "";
INFOPLIST_FILE = "SimpleAudio/Driver/SimpleAudioDriver-Info.plist";
INSTALL_PATH = "$(SYSTEM_LIBRARY_DIR)/Extensions";
MODULE_NAME = com.apple.audio.SimpleAudioDriver;
MODULE_VERSION = 1.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = kext;
};
name = Release;
};
2DED184715C357180091BE97 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN__EXIT_TIME_DESTRUCTORS = NO;
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
INFOPLIST_FILE = "SimpleAudio/Driver/SimpleAudioDriver-Info.plist";
INSTALL_PATH = "$(SYSTEM_LIBRARY_DIR)/Extensions";
MODULE_NAME = com.apple.audio.SimpleAudioDriver;
MODULE_VERSION = 1.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = kext;
};
name = Debug;
};
2DED184815C357180091BE97 /* Debug-Opt */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN__EXIT_TIME_DESTRUCTORS = NO;
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
INFOPLIST_FILE = "SimpleAudio/Driver/SimpleAudioDriver-Info.plist";
INSTALL_PATH = "$(SYSTEM_LIBRARY_DIR)/Extensions";
MODULE_NAME = com.apple.audio.SimpleAudioDriver;
MODULE_VERSION = 1.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = kext;
};
name = "Debug-Opt";
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
2D7477A01578162B00412279 /* Build configuration list for PBXProject "AudioDriverExamples" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2D7477A21578162B00412279 /* Release */,
2D7477A31578162B00412279 /* Debug */,
2D7477A41578164E00412279 /* Debug-Opt */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
2D7477B51578168D00412279 /* Build configuration list for PBXNativeTarget "NullAudio" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2D7477B61578168D00412279 /* Release */,
2D7477B71578168D00412279 /* Debug */,
2D7477B81578168D00412279 /* Debug-Opt */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
2D76D97215E48B2000FF0F33 /* Build configuration list for PBXNativeTarget "SimpleAudioPlugIn" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2D76D96F15E48B2000FF0F33 /* Release */,
2D76D97015E48B2000FF0F33 /* Debug */,
2D76D97115E48B2000FF0F33 /* Debug-Opt */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
2DED184515C357180091BE97 /* Build configuration list for PBXNativeTarget "SimpleAudioDriver" */ = {
isa = XCConfigurationList;
buildConfigurations = (
2DED184615C357180091BE97 /* Release */,
2DED184715C357180091BE97 /* Debug */,
2DED184815C357180091BE97 /* Debug-Opt */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 2D74779D1578162B00412279 /* Project object */;
}
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -1,38 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.apple.audio.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFPlugInFactories</key>
<dict>
<key>99A15A8B-DA3C-42C3-BD5D-D035A0C2377A</key>
<string>NullAudio_Create</string>
</dict>
<key>CFPlugInTypes</key>
<dict>
<key>443ABAB8-E7B3-491A-B985-BEB9187030DB</key>
<array>
<string>99A15A8B-DA3C-42C3-BD5D-D035A0C2377A</string>
</array>
</dict>
</dict>
</plist>
File diff suppressed because it is too large Load Diff
-6
View File
@@ -1,6 +0,0 @@
TravisCI's OS X VMs don't have any audio devices installed by default, so we
install Apple's NullAudio sample driver before the tests run. Otherwise the
tests would fail because BGMApp currently crashes on launch if you don't have
any audio devices.
+46 -11
View File
@@ -17,7 +17,7 @@
// BGMAppUITests.mm
// BGMAppUITests
//
// Copyright © 2017, 2018 Kyle Neideck
// Copyright © 2017, 2018, 2020, 2022 Kyle Neideck
//
// You might want to use Xcode's UI test recording feature if you add new tests.
//
@@ -30,8 +30,10 @@
// Scripting Bridge Includes
#import "BGMApp.h"
// System Includes
#import <XCTest/XCTest.h>
// TODO: Skip these tests if macOS SDK 10.11 or higher isn't available.
// TODO: Mock BGMDevice and music players.
#if __clang_major__ >= 9
@@ -72,22 +74,55 @@
// would fail to start because of a bug in Xcode.
app.launchArguments = @[ @"--no-persistent-data", @"--show-dock-icon" ];
// Make the "Background Music wants to use the microphone" dialog appear every time so the test
// doesn't need logic to handle both cases.
// TODO: Commented out until acceptMicrophoneAuthorizationDialog work again. See below.
// if (@available(macOS 10.15.4, *)) {
// [app resetAuthorizationStatusForResource:XCUIProtectedResourceMicrophone];
// }
// Launch BGMApp.
[app launch];
if (![icon waitForExistenceWithTimeout:1.0]) {
// TODO: This doesn't seem to be working on macOS 12.4 (21F79). You can click OK manually for
// now.
// [self acceptMicrophoneAuthorizationDialog];
if (![icon waitForExistenceWithTimeout:20.0]) {
// The status bar icon/button has this type when using older versions of XCTest, so try
// both. (Actually, it might depend on the macOS or Xcode version. I'm not sure.)
XCUIElement* iconOldType =
[app.menuBars childrenMatchingType:XCUIElementTypeMenuBarItem].element;
if (![iconOldType waitForExistenceWithTimeout:5.0]) {
icon = iconOldType;
}
[app.menuBars childrenMatchingType:XCUIElementTypeMenuBarItem].element;
if ([iconOldType waitForExistenceWithTimeout:20.0]) {
NSLog(@"icon = iconOldType");
icon = iconOldType;
}
}
// Wait for the initial elements.
XCTAssert([app waitForExistenceWithTimeout:10.0]);
XCTAssert([icon waitForExistenceWithTimeout:10.0]);
XCTAssert([app waitForExistenceWithTimeout:20.0]);
XCTAssert([icon waitForExistenceWithTimeout:20.0]);
}
// Clicks the OK button in the "Background Music wants to use the microphone" dialog.
- (void) acceptMicrophoneAuthorizationDialog {
XCUIApplication* unc =
[[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.UserNotificationCenter"];
NSLog(@"UserNotificationCenter: %@", unc);
XCUIElement* okButton = unc.dialogs.buttons[@"OK"];
XCTAssert([okButton waitForExistenceWithTimeout:20.0]);
// This click is failing on GH Actions. No idea why, so try a sleep.
(void)[XCTWaiter waitForExpectations:@[[XCTestExpectation new]] timeout:5.0];
[okButton click];
int retries = 10;
while (retries > 0 && [okButton waitForExistenceWithTimeout:3.0]) {
NSLog(@"Microphone authorization dialog is still open. Trying to click OK again.");
[okButton click];
retries--;
}
}
- (void) tearDown {
@@ -99,13 +134,13 @@
[menuItems[@"Quit Background Music"] click];
// BGMApp should quit.
XCTAssert(!app.exists);
XCTAssertTrue([app waitForState:XCUIApplicationStateNotRunning timeout:10.0]);
[super tearDown];
}
- (void) testCycleOutputDevices {
const int NUM_CYCLES = 2;
const int NUM_CYCLES = 1;
// sbApp lets us use AppleScript to query BGMApp and check the test has made the changes to its
// settings we expect.
@@ -1,4 +1,4 @@
#!/usr/bin/python2.7
#!/usr/bin/env python3
# This file is part of Background Music.
#
@@ -16,20 +16,12 @@
# along with Background Music. If not, see <http://www.gnu.org/licenses/>.
#
# travis-skip.py
# skip-ui-tests.py
# BGMAppUITests
#
# Copyright (c) 2017 Kyle Neideck
# Copyright (c) 2017, 2024 Kyle Neideck
#
# Skip the UI tests in Travis builds because they aren't supported.
#
# We can't run the tests on Travis because Xcode needs permission to use the Accessibility API
# to control BGMApp. There's no way to set that up programmatically without disabling SIP and
# Travis doesn't support that.
#
# See https://github.com/travis-ci/travis-ci/issues/5819
#
# TODO: Figure out a better way to do this.
# Disables the UI tests. This is mainly useful on CI systems that don't support UI tests.
#
import xml.etree.ElementTree as ET
@@ -46,4 +38,3 @@ tree.getroot().findall(UI_REF_XPATH)[0].set("skipped", "YES")
# Save the scheme.
tree.write(SCHEME_FILE)
@@ -17,24 +17,28 @@
// BGMMusicPlayersUnitTests.mm
// BGMAppUnitTests
//
// Copyright © 2016-2018 Kyle Neideck
// Copyright © 2016-2020 Kyle Neideck
//
// Unit include
#import "BGMMusicPlayers.h"
// Local includes
#import "BGM_TestUtils.h"
// BGM includes
#import "BGM_Types.h"
#import "BGMAudioDeviceManager.h"
#import "BGMiTunes.h"
#import "BGMVLC.h"
#import "BGMDecibel.h"
#import "BGMSpotify.h"
#import "BGMVLC.h"
// Local includes
#import "BGM_TestUtils.h"
#import "MockAudioObject.h"
#import "MockAudioObjects.h"
// System includes
#import <Foundation/Foundation.h>
#import <XCTest/XCTest.h>
// Note that the PublicUtility classes that we use to communicate with the HAL, CAHALAudioObject and
@@ -72,32 +76,6 @@
// -------------------------------------------------------------------------------------------------
class BGMMockBackgroundMusicDevice
:
public BGMBackgroundMusicDevice
{
public:
CFStringRef GetMusicPlayerBundleID() const;
void SetMusicPlayerBundleID(CFStringRef inBundleID);
private:
CFStringRef mMusicPlayerBundleID = CFSTR("");
};
CFStringRef BGMMockBackgroundMusicDevice::GetMusicPlayerBundleID() const
{
return mMusicPlayerBundleID;
}
void BGMMockBackgroundMusicDevice::SetMusicPlayerBundleID(CFStringRef inBundleID)
{
mMusicPlayerBundleID = inBundleID;
}
// -------------------------------------------------------------------------------------------------
@interface BGMMockAudioDeviceManager : BGMAudioDeviceManager
@end
@@ -127,6 +105,10 @@ void BGMMockBackgroundMusicDevice::SetMusicPlayerBundleID(CFStringRef inBundleID
- (void) setUp {
[super setUp];
// Mock BGMDevice.
MockAudioObjects::CreateMockDevice(kBGMDeviceUID);
MockAudioObjects::CreateMockDevice(kBGMDeviceUID_UISounds);
devices = [BGMMockAudioDeviceManager new];
defaults = [BGMMockUserDefaults new];
@@ -137,9 +119,10 @@ void BGMMockBackgroundMusicDevice::SetMusicPlayerBundleID(CFStringRef inBundleID
- (void) tearDown {
[super tearDown];
MockAudioObjects::DestroyMocks();
}
- (void) testNoSelectedMusicPlayerStored {
- (void) testNoSelectedMusicPlayerStored_iTunesDefault {
// Test the case where the user has never changed the music player preference.
// Test with iTunes as the default.
@@ -156,16 +139,20 @@ void BGMMockBackgroundMusicDevice::SetMusicPlayerBundleID(CFStringRef inBundleID
XCTAssertEqualObjects(players.selectedMusicPlayer.musicPlayerID, [BGMiTunes sharedMusicPlayerID]);
XCTAssertEqualObjects(players.selectedMusicPlayer.name, @"iTunes");
[self resetDevice];
}
- (void) testNoSelectedMusicPlayerStored_vlcDefault {
// Test the case where the user has never changed the music player preference.
// Test with VLC as the default.
players = [[BGMMusicPlayers alloc] initWithAudioDevices:devices
defaultMusicPlayerID:vlcID
musicPlayerClasses:@[ BGMiTunes.class,
BGMVLC.class,
BGMDecibel.class ]
userDefaults:defaults];
BGMMusicPlayers* players =
[[BGMMusicPlayers alloc] initWithAudioDevices:devices
defaultMusicPlayerID:vlcID
musicPlayerClasses:@[ BGMiTunes.class,
BGMVLC.class,
BGMDecibel.class ]
userDefaults:defaults];
XCTAssertEqual(players.musicPlayers.count, 3);
@@ -193,15 +180,15 @@ void BGMMockBackgroundMusicDevice::SetMusicPlayerBundleID(CFStringRef inBundleID
XCTAssertEqualObjects(players.selectedMusicPlayer.musicPlayerID, spotifyID);
XCTAssertEqualObjects(players.selectedMusicPlayer.name, @"Spotify");
[self resetDevice];
}
- (void) testUnrecognizedSelectedMusicPlayerInUserDefaults {
// If there's an unrecognized ID in user defaults, the default music player should be selected.
defaults.selectedPlayerID = [[NSUUID alloc] initWithUUIDString:@"11111111-1111-1111-0000-000000000000"];
// This initializer sets iTunes as the default music player and adds all the other music players.
players = [[BGMMusicPlayers alloc] initWithAudioDevices:devices
userDefaults:defaults];
BGMMusicPlayers* players = [[BGMMusicPlayers alloc] initWithAudioDevices:devices
userDefaults:defaults];
XCTAssert(players.musicPlayers.count >= 6);
@@ -226,10 +213,5 @@ void BGMMockBackgroundMusicDevice::SetMusicPlayerBundleID(CFStringRef inBundleID
// TODO: Test setting the selectedMusicPlayer property
- (void) resetDevice {
// Reset the mock BGMDevice.
[devices bgmDevice].SetMusicPlayerBundleID(CFSTR(""));
}
@end
@@ -0,0 +1,179 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMPlayThroughRTLoggerTests.mm
// BGMAppUnitTests
//
// Copyright © 2020 Kyle Neideck
//
// Unit Include
#import "BGMPlayThroughRTLogger.h"
// PublicUtility Includes
#import "CARingBuffer.h"
// System Includes
#import <CoreAudio/CoreAudio.h>
#import <XCTest/XCTest.h>
@interface BGMPlayThroughRTLoggerTests : XCTestCase
@end
@implementation BGMPlayThroughRTLoggerTests
{
BGMPlayThroughRTLogger* logger;
}
- (void) setUp {
[super setUp];
logger = new BGMPlayThroughRTLogger;
}
- (void) tearDown {
[super tearDown];
delete logger;
}
- (void) testLogReleasingWaitingThreads {
logger->LogReleasingWaitingThreads();
[self assertLoggedOneDebugMessage];
}
- (void) testLogIfMachError_ReleaseWaitingThreadsSignal {
logger->LogIfMachError_ReleaseWaitingThreadsSignal(KERN_SUCCESS);
[self assertLoggedNoMessages];
}
- (void) testLogIfDroppedFrames_didNotDropFrames {
logger->LogIfDroppedFrames(11256.0, 11256.0);
[self assertLoggedNoMessages];
}
- (void) testLogIfDroppedFrames_didDropFrames {
logger->LogIfDroppedFrames(11256.0, 11768.0);
[self assertLoggedOneDebugMessage];
}
- (void) testLogNoSamplesReady {
logger->LogNoSamplesReady(512, 1024, 512.0);
[self assertLoggedOneDebugMessage];
}
- (void) testLogExceptionStoppingIOProc_withoutErrorCode {
// Set a test-only flag that keeps it from calling abort() after logging the error when the
// tests are compiled with the debug configuration.
logger->mContinueOnErrorLogged = true;
logger->LogExceptionStoppingIOProc("InputDeviceIOProc");
[self assertLoggedOneErrorMessage];
}
- (void) testLogExceptionStoppingIOProc_withErrorCode {
// Set a test-only flag that keeps it from calling abort() after logging the error when the
// tests are compiled with the debug configuration.
logger->mContinueOnErrorLogged = true;
logger->LogExceptionStoppingIOProc("OutputDeviceIOProc", kAudioHardwareUnknownPropertyError);
[self assertLoggedOneErrorMessage];
}
- (void) testLogUnexpectedIOStateAfterStopping {
logger->LogUnexpectedIOStateAfterStopping("OutputDeviceIOProc", 1);
[self assertLoggedOneWarningMessage];
}
- (void) testLogRingBufferUnavailable {
logger->LogRingBufferUnavailable("OutputDeviceIOProc", false);
[self assertLoggedOneWarningMessage];
}
- (void) testLogIfRingBufferError_Fetch_noError {
logger->LogIfRingBufferError_Fetch(kCARingBufferError_OK);
[self assertLoggedNoMessages];
}
- (void) testLogIfRingBufferError_Fetch_errorCPUOverload {
logger->LogIfRingBufferError_Fetch(kCARingBufferError_CPUOverload);
[self assertLoggedOneWarningMessage];
}
- (void) testLogIfRingBufferError_Fetch_errorTooMuch {
// Set a test-only flag that keeps it from calling abort() after logging the error when the
// tests are compiled with the debug configuration.
logger->mContinueOnErrorLogged = true;
logger->LogIfRingBufferError_Fetch(kCARingBufferError_TooMuch);
[self assertLoggedOneErrorMessage];
}
- (void) testLogIfRingBufferError_Store_noError {
logger->LogIfRingBufferError_Store(kCARingBufferError_OK);
[self assertLoggedNoMessages];
}
- (void) testLogIfRingBufferError_Store_errorCPUOverload {
logger->LogIfRingBufferError_Store(kCARingBufferError_CPUOverload);
[self assertLoggedOneWarningMessage];
}
- (void) testLogIfRingBufferError_Store_errorTooMuch {
// Set a test-only flag that keeps it from calling abort() after logging the error when the
// tests are compiled with the debug configuration.
logger->mContinueOnErrorLogged = true;
logger->LogIfRingBufferError_Store(kCARingBufferError_TooMuch);
[self assertLoggedOneErrorMessage];
}
- (void) waitForLoggingThread {
// Wait for it to finish logging the messages.
bool noMessagesLeft = logger->WaitUntilLoggerThreadIdle();
XCTAssert(noMessagesLeft);
}
- (void) assertLoggedNoMessages {
[self waitForLoggingThread];
XCTAssertEqual(0, logger->mNumDebugMessagesLogged);
XCTAssertEqual(0, logger->mNumWarningMessagesLogged);
XCTAssertEqual(0, logger->mNumErrorMessagesLogged);
}
- (void) assertLoggedOneDebugMessage {
[self waitForLoggingThread];
XCTAssertEqual(1, logger->mNumDebugMessagesLogged);
XCTAssertEqual(0, logger->mNumWarningMessagesLogged);
XCTAssertEqual(0, logger->mNumErrorMessagesLogged);
}
- (void) assertLoggedOneWarningMessage {
[self waitForLoggingThread];
XCTAssertEqual(0, logger->mNumDebugMessagesLogged);
XCTAssertEqual(1, logger->mNumWarningMessagesLogged);
XCTAssertEqual(0, logger->mNumErrorMessagesLogged);
}
- (void) assertLoggedOneErrorMessage {
[self waitForLoggingThread];
XCTAssertEqual(0, logger->mNumDebugMessagesLogged);
XCTAssertEqual(0, logger->mNumWarningMessagesLogged);
XCTAssertEqual(1, logger->mNumErrorMessagesLogged);
}
@end
@@ -0,0 +1,106 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMPlayThroughTests.mm
// BGMAppUnitTests
//
// Copyright © 2020 Kyle Neideck
//
// Unit Include
#import "BGMPlayThrough.h"
// Local Includes
#import "MockAudioDevice.h"
#import "MockAudioObjects.h"
// BGM Includes
#import "BGM_Types.h"
#import "BGMAudioDevice.h"
// STL Includes
#import <memory>
// System Includes
#import <XCTest/XCTest.h>
@interface BGMPlayThroughTests : XCTestCase
@end
@implementation BGMPlayThroughTests {
BGMAudioDevice inputDevice;
BGMAudioDevice outputDevice;
// The unit tests use mock implementations of CAHALAudioObject and CAHALAudioDevice, which are
// the superclasses of BGMAudioDevice. When BGMPlayThrough calls methods on inputDevice or
// outputDevice, some of them will update these objects.
std::shared_ptr<MockAudioDevice> mockInputDevice;
std::shared_ptr<MockAudioDevice> mockOutputDevice;
}
- (void) setUp {
[super setUp];
// Set up the mocks.
mockInputDevice = MockAudioObjects::CreateMockDevice(kBGMDeviceUID);
mockOutputDevice = MockAudioObjects::CreateMockDevice("Mock Output Device");
inputDevice = BGMAudioDevice(mockInputDevice->GetObjectID());
outputDevice = BGMAudioDevice(mockOutputDevice->GetObjectID());
}
- (void) tearDown {
[super tearDown];
MockAudioObjects::DestroyMocks();
}
- (void) testActivate {
// Set the mock output device's sample rate and IO buffer size.
outputDevice.SetNominalSampleRate(12345.0);
outputDevice.SetIOBufferSize(123);
// Create an instance and activate it.
BGMPlayThrough playThrough(inputDevice, outputDevice);
playThrough.Activate();
// It should set the input device's sample rate and IO buffer size to match the output device.
XCTAssertEqual(12345.0, inputDevice.GetNominalSampleRate());
XCTAssertEqual(123, inputDevice.GetIOBufferSize());
// It should add the property listeners it needs.
std::set<AudioObjectPropertySelector> expectedProperties {
kAudioDevicePropertyDeviceIsRunning,
kAudioDeviceProcessorOverload,
kAudioDeviceCustomPropertyDeviceIsRunningSomewhereOtherThanBGMApp
};
XCTAssertEqual(expectedProperties, mockInputDevice->mPropertiesWithListeners);
}
- (void) testDeactivate {
BGMPlayThrough playThrough(inputDevice, outputDevice);
playThrough.Activate();
playThrough.Deactivate();
// It should remove the property listeners added by Activate.
XCTAssert(mockInputDevice->mPropertiesWithListeners.empty());
}
@end
@@ -0,0 +1,61 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// MockAudioDevice.cpp
// BGMAppUnitTests
//
// Copyright © 2020 Kyle Neideck
//
// Self Include
#include "MockAudioDevice.h"
// BGM Includes
#include "BGM_Types.h"
// STL Includes
#include <functional>
MockAudioDevice::MockAudioDevice(const std::string& inUID)
:
mUID(inUID),
mNominalSampleRate(44100.0),
mIOBufferSize(512),
MockAudioObject(static_cast<AudioObjectID>(std::hash<std::string>{}(inUID)))
{
}
CACFString MockAudioDevice::GetPlayerBundleID() const
{
if(mUID != kBGMDeviceUID)
{
throw "Only BGMDevice has kAudioDeviceCustomPropertyMusicPlayerBundleID";
}
return mPlayerBundleID;
}
void MockAudioDevice::SetPlayerBundleID(const CACFString& inPlayerBundleID)
{
if(mUID != kBGMDeviceUID)
{
throw "Only BGMDevice has kAudioDeviceCustomPropertyMusicPlayerBundleID";
}
mPlayerBundleID = inPlayerBundleID;
}
@@ -0,0 +1,73 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// MockAudioObject.h
// BGMAppUnitTests
//
// Copyright © 2020 Kyle Neideck
//
#ifndef BGMAppUnitTests__MockAudioDevice
#define BGMAppUnitTests__MockAudioDevice
// Superclass Includes
#include "MockAudioObject.h"
// STL Includes
#include <string>
/*!
* A mock audio device in our mock CoreAudio HAL. In the HAL's API class hierarchy, the base class
* for audio devices, kAudioDeviceClassID, is the audio objects class, kAudioObjectClassID.
*
* The unit tests generally use instances of this class to verify the HAL is being queried correctly
* and to control the responses that the code they're testing will receive from the mock HAL.
*/
class MockAudioDevice
:
public MockAudioObject
{
public:
MockAudioDevice(const std::string& inUID);
/*!
* @return This device's music player bundle ID property.
* @throws If this device isn't a mock of BGMDevice.
*/
CACFString GetPlayerBundleID() const;
/*!
* Set this device's music player bundle ID property.
* @throws If this device isn't a mock of BGMDevice.
*/
void SetPlayerBundleID(const CACFString& inPlayerBundleID);
/*!
* The device's UID. The UID is a persistent token used to identify a particular audio device
* across boot sessions.
*/
const std::string mUID;
Float64 mNominalSampleRate;
UInt32 mIOBufferSize;
private:
CACFString mPlayerBundleID { "" };
};
#endif /* BGMAppUnitTests__MockAudioDevice */
@@ -0,0 +1,37 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// MockAudioObject.cpp
// BGMAppUnitTests
//
// Copyright © 2020 Kyle Neideck
//
// Self Include
#include "MockAudioObject.h"
MockAudioObject::MockAudioObject(AudioObjectID inAudioObjectID)
:
mAudioObjectID(inAudioObjectID)
{
}
AudioObjectID MockAudioObject::GetObjectID() const
{
return mAudioObjectID;
}
@@ -0,0 +1,61 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// MockAudioObject.h
// BGMAppUnitTests
//
// Copyright © 2020 Kyle Neideck
//
#ifndef BGMAppUnitTests__MockAudioObject
#define BGMAppUnitTests__MockAudioObject
// PublicUtility Includes
#include "CACFString.h"
// STL Includes
#include <set>
// System Includes
#include <CoreAudio/CoreAudio.h>
/*!
* The base class for mock audio objects in our mock CoreAudio HAL. Maps to kAudioObjectClassID
* (AudioHardwareBase.h) in the HAL's API class hierarchy.
*/
class MockAudioObject
{
public:
MockAudioObject(AudioObjectID inAudioObjectID);
virtual ~MockAudioObject() = default;
AudioObjectID GetObjectID() const;
/*!
* The properties that callers have added listeners for (and haven't since removed). See
* CAHALAudioObject::AddPropertyListener and CAHALAudioObject::RemovePropertyListener.
*/
std::set<AudioObjectPropertySelector> mPropertiesWithListeners;
private:
AudioObjectID mAudioObjectID;
};
#endif /* BGMAppUnitTests__MockAudioObject */
@@ -0,0 +1,123 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// MockAudioObjects.cpp
// BGMAppUnitTests
//
// Copyright © 2020 Kyle Neideck
//
// Self Include
#include "MockAudioObjects.h"
// PublicUtility Includes
#include "CACFString.h"
// static
MockAudioObjects::MockDeviceMap MockAudioObjects::sDevices;
// static
MockAudioObjects::MockDeviceMapByUID MockAudioObjects::sDevicesByUID;
// static
std::shared_ptr<MockAudioDevice> MockAudioObjects::CreateMockDevice(const std::string& inUID)
{
std::shared_ptr<MockAudioDevice> mockDevice = std::make_shared<MockAudioDevice>(inUID);
sDevices.insert(MockDeviceMap::value_type(mockDevice->GetObjectID(), mockDevice));
sDevicesByUID.insert(MockDeviceMapByUID::value_type(inUID, mockDevice));
return mockDevice;
}
// static
void MockAudioObjects::DestroyMocks()
{
sDevices.clear();
}
// static
std::shared_ptr<MockAudioObject> MockAudioObjects::GetAudioObject(AudioObjectID inAudioObjectID)
{
auto device = GetAudioDeviceOrNull(inAudioObjectID);
if(device)
{
return device;
}
// Devices are the only audio objects we currently mock.
// Tests have to create mocks for all of the audio objects they expect the code they test to
// access. They should fail if it accesses any others.
throw "Mock audio object not found.";
}
// static
std::shared_ptr<MockAudioDevice> MockAudioObjects::GetAudioDevice(AudioObjectID inAudioObjectID)
{
auto device = GetAudioDeviceOrNull(inAudioObjectID);
if(device)
{
return device;
}
// Tests have to create mocks for all of the audio devices they expect the code they test to
// access. They should fail if it accesses any others.
throw "Mock audio device not found.";
}
// static
std::shared_ptr<MockAudioDevice> MockAudioObjects::GetAudioDevice(CFStringRef inUID)
{
// Convert inUID to a std::string.
UInt32 uidCStringLen = CACFString::GetStringByteLength(inUID) + 1;
char uidCString[uidCStringLen];
CACFString::GetCString(inUID, uidCString, uidCStringLen);
std::string uid = std::string(uidCString);
return GetAudioDevice(uid);
}
// static
std::shared_ptr<MockAudioDevice> MockAudioObjects::GetAudioDevice(const std::string& inUID)
{
auto device = sDevicesByUID.find(inUID);
if(device != sDevicesByUID.end())
{
return device->second;
}
return nullptr;
}
// static
std::shared_ptr<MockAudioDevice>
MockAudioObjects::GetAudioDeviceOrNull(AudioObjectID inAudioObjectID)
{
auto device = sDevices.find(inAudioObjectID);
if(device != sDevices.end())
{
return device->second;
}
return nullptr;
}
@@ -0,0 +1,90 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// MockAudioObjects.h
// BGMAppUnitTests
//
// Copyright © 2020 Kyle Neideck
//
#ifndef BGMAppUnitTests__MockAudioObjects
#define BGMAppUnitTests__MockAudioObjects
// Local Includes
#include "MockAudioObject.h"
#include "MockAudioDevice.h"
// STL Includes
#include <map>
#include <memory>
#include <string>
// System Includes
#include <CoreAudio/CoreAudio.h>
class MockAudioObjects
{
public:
/*!
* Create a mock audio device in the mock CoreAudio HAL.
*
* The mock device will then be accessible using GetAudioObject and GetAudioDevice. The
* Mock_CAHAL* implementations will access the mock device when they query the mock HAL.
*
* Unit tests can check the mock device to verify the code they're testing has called the mocked
* CAHAL classes correctly. They can also modify the mock device to control the Mock_CAHAL*
* implementations, e.g. to have CAHALAudioDevice::IsAlive return false so the test can cover
* the case where a device is being removed from the system.
*
* @param inUID The UID string to give the device. The UID is a persistent token used to
* identify a particular audio device across boot sessions.
* @return The mock device.
*/
static std::shared_ptr<MockAudioDevice> CreateMockDevice(const std::string& inUID);
/*!
* Remove all mock audio objects from the mock HAL. (Currently, mock devices are the only mock
* objects that can be created.)
*/
static void DestroyMocks();
/*! Get a mock audio object by its ID. */
static std::shared_ptr<MockAudioObject> GetAudioObject(AudioObjectID inAudioObjectID);
/*! Get a mock audio device by its ID. */
static std::shared_ptr<MockAudioDevice> GetAudioDevice(AudioObjectID inAudioDeviceID);
/*! Get a mock audio device by its UID. */
static std::shared_ptr<MockAudioDevice> GetAudioDevice(const std::string& inUID);
/*! Get a mock audio device by its UID. */
static std::shared_ptr<MockAudioDevice> GetAudioDevice(CFStringRef inUID);
private:
typedef std::map<AudioObjectID, std::shared_ptr<MockAudioDevice>> MockDeviceMap;
typedef std::map<std::string, std::shared_ptr<MockAudioDevice>> MockDeviceMapByUID;
static std::shared_ptr<MockAudioDevice> GetAudioDeviceOrNull(AudioObjectID inAudioDeviceID);
/*! Maps IDs to mocked audio devices. */
static MockDeviceMap sDevices;
/*! Maps UIDs (ID strings) to mocked audio devices. */
static MockDeviceMapByUID sDevicesByUID;
};
#endif /* BGMAppUnitTests__MockAudioObjects */
@@ -0,0 +1,691 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// Mock_CAHALAudioDevice.cpp
// BGMAppUnitTests
//
// Copyright © 2020 Kyle Neideck
//
// Self Include
#include "CAHALAudioDevice.h"
// Local Includes
#include "MockAudioDevice.h"
#include "MockAudioObjects.h"
// BGM Includes
#include "BGM_Types.h"
// PublicUtility Includes
#include "CACFString.h"
#include "CAHALAudioSystemObject.h"
#include "CAPropertyAddress.h"
#pragma clang diagnostic ignored "-Wunused-parameter"
CAHALAudioDevice::CAHALAudioDevice(AudioObjectID inObjectID)
:
CAHALAudioObject(inObjectID)
{
}
CAHALAudioDevice::CAHALAudioDevice(CFStringRef inUID)
:
CAHALAudioObject(CAHALAudioSystemObject().GetAudioDeviceForUID(inUID))
{
}
CAHALAudioDevice::~CAHALAudioDevice()
{
}
void CAHALAudioDevice::GetCurrentVirtualFormats(bool inIsInput, UInt32& ioNumberStreams, AudioStreamBasicDescription* outFormats) const
{
ioNumberStreams = 1;
CAPropertyAddress theAddress(kAudioStreamPropertyVirtualFormat);
UInt32 theSize = sizeof(AudioStreamBasicDescription);
GetPropertyData(theAddress, 0, NULL, theSize, outFormats);
}
UInt32 CAHALAudioDevice::GetIOBufferSize() const
{
return MockAudioObjects::GetAudioDevice(GetObjectID())->mIOBufferSize;
}
void CAHALAudioDevice::SetIOBufferSize(UInt32 inBufferSize)
{
MockAudioObjects::GetAudioDevice(GetObjectID())->mIOBufferSize = inBufferSize;
}
bool CAHALAudioDevice::IsAlive() const
{
return true;
}
AudioDeviceIOProcID CAHALAudioDevice::CreateIOProcID(AudioDeviceIOProc inIOProc, void* inClientData)
{
return reinterpret_cast<AudioDeviceIOProcID>(0x99990000);
}
void CAHALAudioDevice::DestroyIOProcID(AudioDeviceIOProcID inIOProcID)
{
}
Float64 CAHALAudioDevice::GetNominalSampleRate() const
{
return MockAudioObjects::GetAudioDevice(GetObjectID())->mNominalSampleRate;
}
void CAHALAudioDevice::SetNominalSampleRate(Float64 inSampleRate)
{
MockAudioObjects::GetAudioDevice(GetObjectID())->mNominalSampleRate = inSampleRate;
}
CFStringRef CAHALAudioDevice::CopyDeviceUID() const
{
std::string uid = MockAudioObjects::GetAudioDevice(GetObjectID())->mUID;
return CACFString(uid.c_str()).CopyCFString();
}
#pragma mark Unimplemented Methods
bool CAHALAudioDevice::HasModelUID() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
CFStringRef CAHALAudioDevice::CopyModelUID() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
CFStringRef CAHALAudioDevice::CopyConfigurationApplicationBundleID() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
CFURLRef CAHALAudioDevice::CopyIconLocation() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetTransportType() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::CanBeDefaultDevice(bool inIsInput, bool inIsSystem) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasDevicePlugInStatus() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
OSStatus CAHALAudioDevice::GetDevicePlugInStatus() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::IsHidden() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
pid_t CAHALAudioDevice::GetHogModeOwner() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::IsHogModeSettable() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::TakeHogMode()
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::ReleaseHogMode()
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasPreferredStereoChannels(bool inIsInput) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetPreferredStereoChannels(bool inIsInput, UInt32& outLeft, UInt32& outRight) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetPreferredStereoChannels(bool inIsInput, UInt32 inLeft, UInt32 inRight)
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasPreferredChannelLayout(bool inIsInput) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetPreferredChannelLayout(bool inIsInput, AudioChannelLayout& outChannelLayout) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetPreferredStereoChannels(bool inIsInput, AudioChannelLayout& inChannelLayout)
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetNumberRelatedAudioDevices() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetRelatedAudioDevices(UInt32& ioNumberRelatedDevices, AudioObjectID* outRelatedDevices) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
AudioObjectID CAHALAudioDevice::GetRelatedAudioDeviceByIndex(UInt32 inIndex) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetNumberStreams(bool inIsInput) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetStreams(bool inIsInput, UInt32& ioNumberStreams, AudioObjectID* outStreamList) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
AudioObjectID CAHALAudioDevice::GetStreamByIndex(bool inIsInput, UInt32 inIndex) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetTotalNumberChannels(bool inIsInput) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetCurrentPhysicalFormats(bool inIsInput, UInt32& ioNumberStreams, AudioStreamBasicDescription* outFormats) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::IsRunning() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::IsRunningSomewhere() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetLatency(bool inIsInput) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetSafetyOffset(bool inIsInput) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasClockDomain() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetClockDomain() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float64 CAHALAudioDevice::GetActualSampleRate() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetNumberAvailableNominalSampleRateRanges() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetAvailableNominalSampleRateRanges(UInt32& ioNumberRanges, AudioValueRange* outRanges) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetAvailableNominalSampleRateRangeByIndex(UInt32 inIndex, Float64& outMinimum, Float64& outMaximum) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::IsValidNominalSampleRate(Float64 inSampleRate) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::IsIOBufferSizeSettable() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::UsesVariableIOBufferSizes() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetMaximumVariableIOBufferSize() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasIOBufferSizeRange() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetIOBufferSizeRange(UInt32& outMinimum, UInt32& outMaximum) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::StartIOProc(AudioDeviceIOProcID inIOProcID)
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::StartIOProcAtTime(AudioDeviceIOProcID inIOProcID, AudioTimeStamp& ioStartTime, bool inIsInput, bool inIgnoreHardware)
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::StopIOProc(AudioDeviceIOProcID inIOProcID)
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetIOProcStreamUsage(AudioDeviceIOProcID inIOProcID, bool inIsInput, bool* outStreamUsage) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetIOProcStreamUsage(AudioDeviceIOProcID inIOProcID, bool inIsInput, const bool* inStreamUsage)
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float32 CAHALAudioDevice::GetIOCycleUsage() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetIOCycleUsage(Float32 inValue)
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetCurrentTime(AudioTimeStamp& outTime)
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::TranslateTime(const AudioTimeStamp& inTime, AudioTimeStamp& outTime)
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetNearestStartTime(AudioTimeStamp& ioTime, bool inIsInput, bool inIgnoreHardware)
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasVolumeControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::VolumeControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float32 CAHALAudioDevice::GetVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float32 CAHALAudioDevice::GetVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue)
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue)
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float32 CAHALAudioDevice::GetVolumeControlScalarForDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float32 CAHALAudioDevice::GetVolumeControlDecibelForScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasSubVolumeControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::SubVolumeControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float32 CAHALAudioDevice::GetSubVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float32 CAHALAudioDevice::GetSubVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetSubVolumeControlScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue)
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetSubVolumeControlDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue)
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float32 CAHALAudioDevice::GetSubVolumeControlScalarForDecibelValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float32 CAHALAudioDevice::GetSubVolumeControlDecibelForScalarValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasMuteControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::MuteControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::GetMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue)
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasSoloControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::SoloControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::GetSoloControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetSoloControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue)
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasStereoPanControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::StereoPanControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
Float32 CAHALAudioDevice::GetStereoPanControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetStereoPanControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, Float32 inValue)
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetStereoPanControlChannels(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32& outLeftChannel, UInt32& outRightChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasJackControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::GetJackControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasSubMuteControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::SubMuteControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::GetSubMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetSubMuteControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue)
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasiSubOwnerControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::iSubOwnerControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::GetiSubOwnerControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetiSubOwnerControlValue(AudioObjectPropertyScope inScope, UInt32 inChannel, bool inValue)
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasDataSourceControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::DataSourceControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetCurrentDataSourceID(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetCurrentDataSourceByID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID)
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetNumberAvailableDataSources(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetAvailableDataSources(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32& ioNumberSources, UInt32* outSources) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetAvailableDataSourceByIndex(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inIndex) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
CFStringRef CAHALAudioDevice::CopyDataSourceNameForID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasDataDestinationControl(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::DataDestinationControlIsSettable(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetCurrentDataDestinationID(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetCurrentDataDestinationByID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID)
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetNumberAvailableDataDestinations(AudioObjectPropertyScope inScope, UInt32 inChannel) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetAvailableDataDestinations(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32& ioNumberDestinations, UInt32* outDestinations) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetAvailableDataDestinationByIndex(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inIndex) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
CFStringRef CAHALAudioDevice::CopyDataDestinationNameForID(AudioObjectPropertyScope inScope, UInt32 inChannel, UInt32 inID) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::HasClockSourceControl() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
bool CAHALAudioDevice::ClockSourceControlIsSettable() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetCurrentClockSourceID() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::SetCurrentClockSourceByID(UInt32 inID)
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetNumberAvailableClockSources() const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioDevice::GetAvailableClockSources(UInt32& ioNumberSources, UInt32* outSources) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetAvailableClockSourceByIndex(UInt32 inIndex) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
CFStringRef CAHALAudioDevice::CopyClockSourceNameForID(UInt32 inID) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioDevice::GetClockSourceKindForID(UInt32 inID) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
@@ -17,25 +17,24 @@
// Mock_CAHALAudioObject.cpp
// BGMAppUnitTests
//
// Copyright © 2016 Kyle Neideck
// Copyright © 2016, 2020 Kyle Neideck
//
// Self include
// Self Include
#include "CAHALAudioObject.h"
// Local Includes
#include "MockAudioObjects.h"
// BGM Includes
#include "BGM_Types.h"
// System includes
#include <CoreAudio/AudioHardware.h>
// PublicUtility Includes
#include "CACFString.h"
#pragma clang diagnostic ignored "-Wunused-parameter"
// The value of the music player bundle ID property. Tests should set this back to "" when they finish. (Has
// to be static because we can't add to the real class's interface.)
static CFStringRef playerBundleID = CFSTR("");
CAHALAudioObject::CAHALAudioObject(AudioObjectID inObjectID)
:
mObjectID(inObjectID)
@@ -53,21 +52,89 @@ AudioObjectID CAHALAudioObject::GetObjectID() const
void CAHALAudioObject::GetPropertyData(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32& ioDataSize, void* outData) const
{
if(inAddress.mSelector == kAudioDeviceCustomPropertyMusicPlayerBundleID)
switch(inAddress.mSelector)
{
*reinterpret_cast<CFStringRef*>(outData) = playerBundleID;
case kAudioDeviceCustomPropertyMusicPlayerBundleID:
*reinterpret_cast<CFStringRef*>(outData) =
MockAudioObjects::GetAudioDevice(GetObjectID())->
GetPlayerBundleID().CopyCFString();
break;
case kAudioDevicePropertyStreams:
reinterpret_cast<AudioObjectID*>(outData)[0] = 1;
if(inAddress.mScope == kAudioObjectPropertyScopeGlobal)
{
reinterpret_cast<AudioObjectID*>(outData)[1] = 2;
}
break;
case kAudioDevicePropertyBufferFrameSize:
*reinterpret_cast<UInt32*>(outData) = 512;
break;
case kAudioDevicePropertyDeviceIsAlive:
*reinterpret_cast<UInt32*>(outData) = 1;
break;
case kAudioStreamPropertyVirtualFormat:
{
AudioStreamBasicDescription* outASBD =
reinterpret_cast<AudioStreamBasicDescription*>(outData);
outASBD->mSampleRate = 44100.0;
outASBD->mFormatID = kAudioFormatLinearPCM;
outASBD->mFormatFlags =
kAudioFormatFlagIsFloat | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked;
outASBD->mBytesPerPacket = 8;
outASBD->mFramesPerPacket = 1;
outASBD->mBytesPerFrame = 8;
outASBD->mChannelsPerFrame = 2;
outASBD->mBitsPerChannel = 32;
break;
}
default:
Throw(new CAException(kAudio_UnimplementedError));
}
}
void CAHALAudioObject::SetPropertyData(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData)
{
if(inAddress.mSelector == kAudioDeviceCustomPropertyMusicPlayerBundleID)
switch(inAddress.mSelector)
{
playerBundleID = *reinterpret_cast<const CFStringRef*>(inData);
case kAudioDeviceCustomPropertyMusicPlayerBundleID:
MockAudioObjects::GetAudioDevice(GetObjectID())->SetPlayerBundleID(
CACFString(*reinterpret_cast<const CFStringRef*>(inData), false));
break;
default:
break;
}
}
#pragma mark Unimplemented methods
UInt32 CAHALAudioObject::GetPropertyDataSize(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const
{
switch(inAddress.mSelector)
{
case kAudioDevicePropertyStreams:
return (inAddress.mScope == kAudioObjectPropertyScopeGlobal ? 2 : 1) *
sizeof(AudioObjectID);
default:
Throw(new CAException(kAudio_UnimplementedError));
}
}
void CAHALAudioObject::AddPropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData)
{
MockAudioObjects::GetAudioObject(GetObjectID())->
mPropertiesWithListeners.insert(inAddress.mSelector);
}
void CAHALAudioObject::RemovePropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData)
{
MockAudioObjects::GetAudioObject(GetObjectID())->
mPropertiesWithListeners.erase(inAddress.mSelector);
}
#pragma mark Unimplemented Methods
void CAHALAudioObject::SetObjectID(AudioObjectID inObjectID)
{
@@ -144,18 +211,3 @@ bool CAHALAudioObject::IsPropertySettable(const AudioObjectPropertyAddress& inAd
Throw(new CAException(kAudio_UnimplementedError));
}
UInt32 CAHALAudioObject::GetPropertyDataSize(const AudioObjectPropertyAddress& inAddress, UInt32 inQualifierDataSize, const void* inQualifierData) const
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioObject::AddPropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData)
{
Throw(new CAException(kAudio_UnimplementedError));
}
void CAHALAudioObject::RemovePropertyListener(const AudioObjectPropertyAddress& inAddress, AudioObjectPropertyListenerProc inListenerProc, void* inClientData)
{
Throw(new CAException(kAudio_UnimplementedError));
}
@@ -17,12 +17,18 @@
// Mock_CAHALAudioSystemObject.cpp
// BGMAppUnitTests
//
// Copyright © 2017 Kyle Neideck
// Copyright © 2017, 2020 Kyle Neideck
//
// Self include
#include "CAHALAudioSystemObject.h"
// BGM Includes
#include "BGM_Types.h"
// Local Includes
#include "MockAudioObjects.h"
CAHALAudioSystemObject::CAHALAudioSystemObject()
:
@@ -36,19 +42,17 @@ CAHALAudioSystemObject::~CAHALAudioSystemObject()
AudioObjectID CAHALAudioSystemObject::GetAudioDeviceForUID(CFStringRef inUID) const
{
AudioObjectID id = kAudioObjectUnknown;
auto device = MockAudioObjects::GetAudioDevice(inUID);
// Generate a deterministic and random-ish ID from the UID string. Ideally we would ensure the
// IDs are unique, but this is probably fine.
for(int i = 0; i < CFStringGetLength(inUID); i++)
if(device)
{
id += 37 * CFStringGetCharacterAtIndex(inUID, i);
return device->GetObjectID();
}
return id;
return kAudioObjectUnknown;
}
#pragma mark Unimplemented methods
#pragma mark Unimplemented Methods
#pragma clang diagnostic ignored "-Wunused-parameter"
+383
View File
@@ -0,0 +1,383 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
// Original licence at the end of this file.
//
// BGMThreadSafetyAnalysis.h
// PublicUtility
//
// © Copyright 2007-2020, The Clang Team
// Copyright © 2020 Kyle Neideck
//
// Macros that wrap Clang's attributes for statically checking concurrency properties. From
// <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader>.
//
#ifndef PublicUtility__BGMThreadSafetyAnalysis
#define PublicUtility__BGMThreadSafetyAnalysis
// Enable thread safety attributes only with clang.
// The attributes can be safely erased when compiling with other compilers.
#if defined(__clang__) && (!defined(SWIG))
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif
#define CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
#define SCOPED_CAPABILITY \
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
#define GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
#define PT_GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
#define ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
#define ACQUIRED_AFTER(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
#define REQUIRES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
#define REQUIRES_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
#define ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
#define ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
#define RELEASE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
#define RELEASE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
#define TRY_ACQUIRE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
#define TRY_ACQUIRE_SHARED(...) \
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
#define EXCLUDES(...) \
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
#define ASSERT_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
#define ASSERT_SHARED_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
#define RETURN_CAPABILITY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
#endif /* PublicUtility__BGMThreadSafetyAnalysis */
/*
This file is derived from "mutex.h" from the Clang documentation at
<https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>. This is the original license of "mutex.h".
==============================================================================
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
==============================================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
---- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.
==============================================================================
Software from third parties included in the LLVM Project:
==============================================================================
The LLVM Project contains third party software which is under different license
terms. All such code will be identified clearly using at least one of two
mechanisms:
1) It will be in a separate directory tree with its own `LICENSE.txt` or
`LICENSE` file at the top containing the specific license and restrictions
which apply to that software, or
2) It will contain specific license and restriction terms at the top of every
file.
==============================================================================
Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2007-2019 University of Illinois at Urbana-Champaign.
All rights reserved.
Developed by:
LLVM Team
University of Illinois at Urbana-Champaign
http://llvm.org
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the LLVM Team, University of Illinois at
Urbana-Champaign, nor the names of its contributors may be used to
endorse or promote products derived from this Software without specific
prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
*/
+2 -2
View File
@@ -17,13 +17,13 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>0.3.1</string>
<string>0.4.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016-2019 Background Music contributors</string>
<string>Copyright © 2016-2024 Background Music contributors</string>
<key>XPCService</key>
<dict>
<key>ServiceType</key>
+8 -2
View File
@@ -20,7 +20,7 @@
# post_install.sh
# BGMXPCHelper
#
# Copyright © 2016-2018 Kyle Neideck
# Copyright © 2016-2020 Kyle Neideck
#
# Installs BGMXPCHelper's launchd plist file and "bootstraps" (registers/enables) it with launchd.
#
@@ -68,7 +68,13 @@ fi
# If DEPLOYMENT_POSTPROCESSING is true, xcodebuild calls this script even if you're just building
# (and not also installing). I'm not sure why, as we have the "run script only when installing"
# option enabled.
if ! [[ -z ${ACTION} ]] && [[ "${ACTION}" != "install" ]]; then
#
# REAL_ACTION is a workaround for xcodebuild setting ACTION to "install" even if we're actually
# making an archive.
#
# TODO: Archiving BGMXPCHelper from Xcode instead of using build_and_install.sh still fails.
if ( ! [[ -z ${ACTION} ]] && [[ "${ACTION}" != "install" ]] ) || \
( ! [[ -z ${REAL_ACTION} ]] && [[ "${REAL_ACTION}" == "archive" ]] ); then
echo "$0 should only be called during an install. Exiting."
exit 0
fi
+2 -2
View File
@@ -71,7 +71,7 @@ check_dir() {
pushd . > /dev/null
# Normalize the path and follow symlinks.
REAL_PATH=$(python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$1")
REAL_PATH=$(cd "$1" && pwd -P)
cd "${REAL_PATH}"
DIR_IS_SAFE=0
@@ -84,7 +84,7 @@ check_dir() {
[[ $((0$(stat -f '%Lp' .) & 0022)) -eq 0 ]]; do
# ...go upwards until we reach the root directory.
cd ..
if [[ "${PWD}" == / ]]; then
if [[ "${PWD}" -ef / ]]; then
DIR_IS_SAFE=1
break
fi
+1 -1
View File
@@ -69,7 +69,7 @@
dispatch_semaphore_signal(replySemaphore);
} forUISoundsDevice:NO];
// Very long timeout to make it less likely to fail on Travis CI when there's high contention.
// Very long timeout to make it less likely to fail in CI builds when there's high contention.
if (0 != dispatch_semaphore_wait(replySemaphore, dispatch_time(DISPATCH_TIME_NOW, 5 * 60 * NSEC_PER_SEC))) {
XCTFail(@"Timed out waiting for BGMXPCHelper");
}
+50
View File
@@ -0,0 +1,50 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMDebugLogging.c
// PublicUtility
//
// Copyright © 2020, 2024 Kyle Neideck
//
// Self Include
#include "BGMDebugLogging.h"
#pragma clang assume_nonnull begin
// It's probably not ideal to use a global variable for this, but it's a lot easier.
#if DEBUG || CoreAudio_Debug
// Enable debug logging by default in debug builds.
int gDebugLoggingIsEnabled = 1;
#else
int gDebugLoggingIsEnabled = 0;
#endif
// We don't bother synchronising accesses of gDebugLoggingIsEnabled because it isn't really
// necessary and would complicate code that accesses it on realtime threads.
int BGMDebugLoggingIsEnabled(void)
{
return gDebugLoggingIsEnabled;
}
void BGMSetDebugLoggingEnabled(int inEnabled)
{
gDebugLoggingIsEnabled = inEnabled;
}
#pragma clang assume_nonnull end
+63
View File
@@ -0,0 +1,63 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGMDebugLogging.h
// PublicUtility
//
// Copyright © 2020 Kyle Neideck
//
// Functions to globally enable/disable debug logging, i.e. more detailed logging to help diagnose
// bugs. If debug logging is enabled, the DebugMsg macro from CADebugMacros.h (and possibly others)
// will log messages. If not, it won't do anything.
//
// If the preprocessor macro CoreAudio_UseSysLog is true, which is currently the case for all build
// variants (see BGMApp/BGMApp.xcodeproj/project.pbxproj and
// BGMDriver/BGMDriver.xcodeproj/project.pbxproj), those messages will be logged using syslog and
// can be read using Console.app. Try searching for "background music", "bgm" or "coreaudiod".
//
// Debug logging is enabled by default in debug builds, but in release builds you have to enable it
// by option-clicking the status bar icon and then checking the Debug Logging menu item. Enabling
// debug logging probably won't cause glitches, but we don't try to guarantee that and it's not
// well tested.
//
#ifndef PublicUtility__BGMDebugLogging
#define PublicUtility__BGMDebugLogging
#pragma clang assume_nonnull begin
/*!
* @return Non-zero if debug logging is globally enabled. (Probably -- it's not synchronised.)
* Real-time safe.
*/
#if defined(__cplusplus)
extern "C"
#endif
int BGMDebugLoggingIsEnabled(void);
/*!
* @param inEnabled Non-zero to globally enable debug logging, zero to disable it. The change might
* not be visible to other threads immediately.
*/
#if defined(__cplusplus)
extern "C"
#endif
void BGMSetDebugLoggingEnabled(int inEnabled);
#pragma clang assume_nonnull end
#endif /* PublicUtility__BGMDebugLogging */
+14 -1
View File
@@ -59,13 +59,15 @@ full barrier.
#if TARGET_OS_WIN32
#include <windows.h>
#include <intrin.h>
#pragma intrinsic(_InterlockedOr)
#pragma intrinsic(_InterlockedOr)
#pragma intrinsic(_InterlockedAnd)
#else
#include <CoreFoundation/CFBase.h>
#include <libkern/OSAtomic.h>
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
inline void CAMemoryBarrier()
{
#if TARGET_OS_WIN32
@@ -196,6 +198,8 @@ inline bool CAAtomicTestAndSetBarrier(int bitToSet, void* theAddress)
#endif
}
#pragma clang diagnostic pop
// int32_t flavors -- for C++ only since we can't overload in C
// CFBase.h defines SInt32 as signed int which is similar to int32_t. If CFBase.h is included, then
// this will generate redefinition error. But on Mac, CFBase.h, still includes MacTypes.h where
@@ -243,6 +247,9 @@ inline int32_t CAAtomicDecrement32Barrier(volatile int32_t* theValue)
}
#endif // __cplusplus && !__LP64__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
#if __LP64__
inline bool CAAtomicCompareAndSwap64Barrier( int64_t __oldValue, int64_t __newValue, volatile int64_t *__theValue )
{
@@ -259,6 +266,8 @@ inline bool CAAtomicCompareAndSwapPtrBarrier(void *__oldValue, void *__newValue,
#endif
}
#pragma clang diagnostic pop
/* Spinlocks. These use memory barriers as required to synchronize access to shared
* memory protected by the lock. The lock operation spins, but employs various strategies
* to back off if the lock is held, making it immune to most priority-inversion livelocks.
@@ -273,6 +282,9 @@ bool CASpinLockTry( volatile CASpinLock *__lock );
void CASpinLockLock( volatile CASpinLock *__lock );
void CASpinLockUnlock( volatile CASpinLock *__lock );
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
inline void CASpinLockLock( volatile CASpinLock *__lock )
{
#if TARGET_OS_MAC
@@ -301,5 +313,6 @@ inline bool CASpinLockTry( volatile CASpinLock *__lock )
#endif
}
#pragma clang diagnostic pop
#endif // __CAAtomic_h__
+39 -21
View File
@@ -1,3 +1,28 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// CADebugMacros.cpp
// PublicUtility
//
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
// Copyright © 2016, 2017, 2020 Kyle Neideck
//
// Original license header follows.
//
/*
File: CADebugMacros.cpp
Abstract: CADebugMacros.h
@@ -46,7 +71,6 @@
*/
#include "CADebugMacros.h"
#include <stdio.h>
#include <stdarg.h>
#if TARGET_API_MAC_OSX
#include <syslog.h>
#endif
@@ -67,14 +91,12 @@ void LogError(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
// BGM edit: vprintf leaves args in an undefined state, which can cause a crash in
// vsyslog. Also added CADebuggerStop(). Original code commented out below.
//#if DEBUG
// vprintf(fmt, args);
//#endif
//#if TARGET_API_MAC_OSX
// vsyslog(LOG_ERR, fmt, args);
//#endif
vLogError(fmt, args);
va_end(args);
}
void vLogError(const char *fmt, va_list args)
{
#if (DEBUG || !TARGET_API_MAC_OSX) && !CoreAudio_UseSysLog
printf("[ERROR] ");
vprintf(fmt, args);
@@ -82,25 +104,22 @@ void LogError(const char *fmt, ...)
#else
vsyslog(LOG_ERR, fmt, args);
#endif
#if DEBUG
CADebuggerStop();
#endif
// BGM edit end
va_end(args);
}
void LogWarning(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
// BGM edit: vprintf leaves args in an undefined state, which can cause a crash in
// vsyslog. Also added CADebuggerStop(). Original code commented out below.
//#if DEBUG
// vprintf(fmt, args);
//#endif
//#if TARGET_API_MAC_OSX
// vsyslog(LOG_WARNING, fmt, args);
//#endif
vLogWarning(fmt, args);
va_end(args);
}
void vLogWarning(const char *fmt, va_list args)
{
#if (DEBUG || !TARGET_API_MAC_OSX) && !CoreAudio_UseSysLog
printf("[WARNING] ");
vprintf(fmt, args);
@@ -108,9 +127,8 @@ void LogWarning(const char *fmt, ...)
#else
vsyslog(LOG_WARNING, fmt, args);
#endif
#if DEBUG
//CADebuggerStop(); // TODO: Add a toggle for this to the project file (under "Preprocessor Macros"). Default to off.
#endif
// BGM edit end
va_end(args);
}
+65 -35
View File
@@ -1,3 +1,28 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// CADebugMacros.h
// PublicUtility
//
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
// Copyright © 2016, 2020 Kyle Neideck
//
// Original license header follows.
//
/*
File: CADebugMacros.h
Abstract: Part of CoreAudio Utility Classes
@@ -57,6 +82,10 @@
#include "CoreAudioTypes.h"
#endif
#include "CADebugPrintf.h"
#include <stdarg.h>
//=============================================================================
// CADebugMacros
//=============================================================================
@@ -92,42 +121,41 @@
#pragma mark Basic Definitions
// basic debugging print routines
#if TARGET_OS_MAC && !TARGET_API_MAC_CARBON
extern void DebugStr(const unsigned char* debuggerMsg);
#define DebugMessage(msg) DebugStr("\p"msg)
#define DebugMessageN1(msg, N1)
#define DebugMessageN2(msg, N1, N2)
#define DebugMessageN3(msg, N1, N2, N3)
#else
#if (CoreAudio_FlushDebugMessages && !CoreAudio_UseSysLog) || defined(CoreAudio_UseSideFile)
#define FlushRtn ,fflush(DebugPrintfFile)
#else
#define FlushRtn
#endif
#if CoreAudio_ThreadStampMessages
#include <pthread.h>
#include "CAHostTimeBase.h"
#if TARGET_RT_64_BIT
#define DebugPrintfThreadIDFormat "%16p"
#else
#define DebugPrintfThreadIDFormat "%8p"
#endif
#define DebugMsg(inFormat, ...) DebugPrintf("%17qd: " DebugPrintfThreadIDFormat " " inFormat, CAHostTimeBase::GetCurrentTimeInNanos(), pthread_self(), ## __VA_ARGS__) FlushRtn
#elif CoreAudio_TimeStampMessages
#include "CAHostTimeBase.h"
#define DebugMsg(inFormat, ...) DebugPrintf("%17qd: " inFormat, CAHostTimeBase::GetCurrentTimeInNanos(), ## __VA_ARGS__) FlushRtn
#else
#define DebugMsg(inFormat, ...) DebugPrintf(inFormat, ## __VA_ARGS__) FlushRtn
#endif
#endif
#if DEBUG || CoreAudio_Debug
// can be used to break into debugger immediately, also see CADebugger
#define BusError() { long* p=NULL; *p=0; }
// basic debugging print routines
#if TARGET_OS_MAC && !TARGET_API_MAC_CARBON
extern void DebugStr(const unsigned char* debuggerMsg);
#define DebugMessage(msg) DebugStr("\p"msg)
#define DebugMessageN1(msg, N1)
#define DebugMessageN2(msg, N1, N2)
#define DebugMessageN3(msg, N1, N2, N3)
#else
#include "CADebugPrintf.h"
#if (CoreAudio_FlushDebugMessages && !CoreAudio_UseSysLog) || defined(CoreAudio_UseSideFile)
#define FlushRtn ,fflush(DebugPrintfFile)
#else
#define FlushRtn
#endif
#if CoreAudio_ThreadStampMessages
#include <pthread.h>
#include "CAHostTimeBase.h"
#if TARGET_RT_64_BIT
#define DebugPrintfThreadIDFormat "%16p"
#else
#define DebugPrintfThreadIDFormat "%8p"
#endif
#define DebugMsg(inFormat, ...) DebugPrintf("%17qd: " DebugPrintfThreadIDFormat " " inFormat, CAHostTimeBase::GetCurrentTimeInNanos(), pthread_self(), ## __VA_ARGS__) FlushRtn
#elif CoreAudio_TimeStampMessages
#include "CAHostTimeBase.h"
#define DebugMsg(inFormat, ...) DebugPrintf("%17qd: " inFormat, CAHostTimeBase::GetCurrentTimeInNanos(), ## __VA_ARGS__) FlushRtn
#else
#define DebugMsg(inFormat, ...) DebugPrintf(inFormat, ## __VA_ARGS__) FlushRtn
#endif
#endif
void DebugPrint(const char *fmt, ...); // can be used like printf
#ifndef DEBUGPRINT
#define DEBUGPRINT(msg) DebugPrint msg // have to double-parenthesize arglist (see Debugging.h)
@@ -172,7 +200,7 @@
#endif
#else
#define DebugMsg(inFormat, ...)
#ifndef DEBUGPRINT
#define DEBUGPRINT(msg)
#endif
@@ -194,9 +222,11 @@
#define DebugMessageN8(msg, N1, N2, N3, N4, N5, N6, N7, N8) DebugMsg(msg, N1, N2, N3, N4, N5, N6, N7, N8)
#define DebugMessageN9(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9) DebugMsg(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9)
// BGM edit: Added __printflike.
// BGM edit: Added __printflike and va_list versions.
void LogError(const char *fmt, ...) __printflike(1, 2); // writes to syslog (and stderr if debugging)
void vLogError(const char *fmt, va_list args);
void LogWarning(const char *fmt, ...) __printflike(1, 2); // writes to syslog (and stderr if debugging)
void vLogWarning(const char *fmt, va_list args);
#define NO_ACTION (void)0
+52 -27
View File
@@ -1,3 +1,28 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// CADebugPrintf.cpp
// PublicUtility
//
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
// Copyright © 2020 Kyle Neideck
//
// Original license header follows.
//
/*
File: CADebugPrintf.cpp
Abstract: CADebugPrintf.h
@@ -51,39 +76,39 @@
// Self Include
#include "CADebugPrintf.h"
#if DEBUG || CoreAudio_Debug
#if TARGET_OS_WIN32
#include <stdarg.h>
#include <stdio.h>
#include <Windows.h>
extern "C"
int CAWin32DebugPrintf(char* inFormat, ...)
{
#if TARGET_OS_WIN32
#include <stdarg.h>
#include <stdio.h>
#include <Windows.h>
extern "C"
int CAWin32DebugPrintf(char* inFormat, ...)
{
if (BGMDebugLoggingIsEnabled()) {
char theMessage[1024];
va_list theArguments;
va_start(theArguments, inFormat);
_vsnprintf(theMessage, 1024, inFormat, theArguments);
va_end(theArguments);
OutputDebugString(theMessage);
return 0;
}
#endif
#if defined(CoreAudio_UseSideFile)
#include <unistd.h>
FILE* sDebugPrintfSideFile = NULL;
extern "C"
void OpenDebugPrintfSideFile()
{
if(sDebugPrintfSideFile == NULL)
{
char theFileName[1024];
snprintf(theFileName, sizeof(theFileName), CoreAudio_UseSideFile, getpid());
sDebugPrintfSideFile = fopen(theFileName, "a+");
DebugPrintfRtn(DebugPrintfFileComma "\n------------------------------\n");
}
}
#endif
return 0;
}
#endif
#if defined(CoreAudio_UseSideFile)
#include <unistd.h>
FILE* sDebugPrintfSideFile = NULL;
extern "C"
void OpenDebugPrintfSideFile()
{
if(sDebugPrintfSideFile == NULL)
{
char theFileName[1024];
snprintf(theFileName, sizeof(theFileName), CoreAudio_UseSideFile, getpid());
sDebugPrintfSideFile = fopen(theFileName, "a+");
DebugPrintfRtn(DebugPrintfFileComma "\n------------------------------\n");
}
}
#endif
+37 -39
View File
@@ -57,6 +57,8 @@
#include "CoreAudioTypes.h"
#endif
#include "BGMDebugLogging.h"
//=============================================================================
// Macros to redirect debugging output to various logging services
//=============================================================================
@@ -64,52 +66,48 @@
//#define CoreAudio_UseSysLog 1
//#define CoreAudio_UseSideFile "/CoreAudio-%d.txt"
#if DEBUG || CoreAudio_Debug
#if TARGET_OS_WIN32
#if TARGET_OS_WIN32
#if defined(__cplusplus)
extern "C"
#endif
extern int CAWin32DebugPrintf(char* inFormat, ...);
#define DebugPrintfRtn CAWin32DebugPrintf
#define DebugPrintfFile
#define DebugPrintfLineEnding "\n"
#define DebugPrintfFileComma
#else
#if CoreAudio_UseSysLog
#include <sys/syslog.h>
#define DebugPrintfRtn syslog
#define DebugPrintfFile LOG_NOTICE
#define DebugPrintfLineEnding ""
#define DebugPrintfFileComma DebugPrintfFile,
#elif defined(CoreAudio_UseSideFile)
#include <stdio.h>
#if defined(__cplusplus)
extern "C"
#endif
extern int CAWin32DebugPrintf(char* inFormat, ...);
#define DebugPrintfRtn CAWin32DebugPrintf
#define DebugPrintfFile
void OpenDebugPrintfSideFile();
extern FILE* sDebugPrintfSideFile;
#define DebugPrintfRtn fprintf
#define DebugPrintfFile ((sDebugPrintfSideFile != NULL) ? sDebugPrintfSideFile : stderr)
#define DebugPrintfLineEnding "\n"
#define DebugPrintfFileComma
#define DebugPrintfFileComma DebugPrintfFile,
#else
#if CoreAudio_UseSysLog
#include <sys/syslog.h>
#define DebugPrintfRtn syslog
#define DebugPrintfFile LOG_NOTICE
#define DebugPrintfLineEnding ""
#define DebugPrintfFileComma DebugPrintfFile,
#elif defined(CoreAudio_UseSideFile)
#include <stdio.h>
#if defined(__cplusplus)
extern "C"
#endif
void OpenDebugPrintfSideFile();
extern FILE* sDebugPrintfSideFile;
#define DebugPrintfRtn fprintf
#define DebugPrintfFile ((sDebugPrintfSideFile != NULL) ? sDebugPrintfSideFile : stderr)
#define DebugPrintfLineEnding "\n"
#define DebugPrintfFileComma DebugPrintfFile,
#else
#include <stdio.h>
#define DebugPrintfRtn fprintf
#define DebugPrintfFile stderr
#define DebugPrintfLineEnding "\n"
#define DebugPrintfFileComma DebugPrintfFile,
#endif
#include <stdio.h>
#define DebugPrintfRtn fprintf
#define DebugPrintfFile stderr
#define DebugPrintfLineEnding "\n"
#define DebugPrintfFileComma DebugPrintfFile,
#endif
#define DebugPrintf(inFormat, ...) DebugPrintfRtn(DebugPrintfFileComma inFormat DebugPrintfLineEnding, ## __VA_ARGS__)
#else
#define DebugPrintfRtn
#define DebugPrintfFile
#define DebugPrintfLineEnding
#define DebugPrintfFileComma
#define DebugPrintf(inFormat, ...)
#endif
#define DebugPrintf(inFormat, ...) \
do { \
if (BGMDebugLoggingIsEnabled()) { \
DebugPrintfRtn(DebugPrintfFileComma inFormat DebugPrintfLineEnding, ## __VA_ARGS__); \
} \
} while (0)
#endif
+40 -11
View File
@@ -1,3 +1,28 @@
// This file is part of Background Music.
//
// Background Music is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 2 of the
// License, or (at your option) any later version.
//
// Background Music is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// CAMutex.h
// PublicUtility
//
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
// Copyright © 2020 Kyle Neideck
//
// Original license header follows.
//
/*
File: CAMutex.h
Abstract: Part of CoreAudio Utility Classes
@@ -51,6 +76,8 @@
// Includes
//==================================================================================================
#include "BGMThreadSafetyAnalysis.h"
// System Includes
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
#include <CoreAudio/CoreAudioTypes.h>
@@ -70,7 +97,7 @@
// A recursive mutex.
//==================================================================================================
class CAMutex
class CAPABILITY("mutex") CAMutex
{
// Construction/Destruction
public:
@@ -79,9 +106,9 @@ public:
// Actions
public:
virtual bool Lock();
virtual void Unlock();
virtual bool Try(bool& outWasLocked); // returns true if lock is free, false if not
virtual bool Lock() ACQUIRE();
virtual void Unlock() RELEASE();
virtual bool Try(bool& outWasLocked) TRY_ACQUIRE(true); // returns true if lock is free, false if not
virtual bool IsFree() const;
virtual bool IsOwnedByCurrentThread() const;
@@ -99,15 +126,15 @@ protected:
// Helper class to manage taking and releasing recursively
public:
class Locker
class SCOPED_CAPABILITY Locker
{
// Construction/Destruction
public:
Locker(CAMutex& inMutex) : mMutex(&inMutex), mNeedsRelease(false) { mNeedsRelease = mMutex->Lock(); }
Locker(CAMutex* inMutex) : mMutex(inMutex), mNeedsRelease(false) { mNeedsRelease = (mMutex != NULL && mMutex->Lock()); }
Locker(CAMutex& inMutex) ACQUIRE(inMutex) : mMutex(&inMutex), mNeedsRelease(false) { mNeedsRelease = mMutex->Lock(); }
Locker(CAMutex* inMutex) ACQUIRE(inMutex) : mMutex(inMutex), mNeedsRelease(false) { mNeedsRelease = (mMutex != NULL && mMutex->Lock()); }
// in this case the mutex can be null
~Locker() { if(mNeedsRelease) { mMutex->Unlock(); } }
~Locker() RELEASE() { if(mNeedsRelease) { mMutex->Unlock(); } }
private:
@@ -121,6 +148,8 @@ public:
};
// Clang's static analysis doesn't work for unlocker classes. See
// <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#no-alias-analysis>.
// Unlocker
class Unlocker
{
@@ -138,12 +167,12 @@ public:
};
// you can use this with Try - if you take the lock in try, pass in the outWasLocked var
class Tryer {
class SCOPED_CAPABILITY Tryer {
// Construction/Destruction
public:
Tryer (CAMutex &mutex) : mMutex(mutex), mNeedsRelease(false), mHasLock(false) { mHasLock = mMutex.Try (mNeedsRelease); }
~Tryer () { if (mNeedsRelease) mMutex.Unlock(); }
Tryer (CAMutex &mutex) TRY_ACQUIRE(true, mutex) : mMutex(mutex), mNeedsRelease(false), mHasLock(false) { mHasLock = mMutex.Try (mNeedsRelease); }
~Tryer () RELEASE() { if (mNeedsRelease) mMutex.Unlock(); }
bool HasLock () const { return mHasLock; }
+3 -29
View File
@@ -98,29 +98,6 @@ public:
static bool IsCongruentElement(AudioObjectPropertyElement inElement1, AudioObjectPropertyElement inElement2) { return (inElement1 == inElement2) || (inElement1 == kAudioObjectPropertyElementWildcard) || (inElement2 == kAudioObjectPropertyElementWildcard); }
static bool IsCongruentAddress(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) { return IsCongruentScope(inAddress1.mScope, inAddress2.mScope) && IsCongruentSelector(inAddress1.mSelector, inAddress2.mSelector) && IsCongruentElement(inAddress1.mElement, inAddress2.mElement); }
static bool IsCongruentLessThanAddress(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) { bool theAnswer = false; if(!IsCongruentScope(inAddress1.mScope, inAddress2.mScope)) { theAnswer = inAddress1.mScope < inAddress2.mScope; } else if(!IsCongruentSelector(inAddress1.mSelector, inAddress2.mSelector)) { theAnswer = inAddress1.mSelector < inAddress2.mSelector; } else if(!IsCongruentElement(inAddress1.mElement, inAddress2.mElement)) { theAnswer = inAddress1.mElement < inAddress2.mElement; } return theAnswer; }
// STL Helpers
public:
struct EqualTo : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
{
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsSameAddress(inAddress1, inAddress2); }
};
struct LessThan : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
{
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsLessThanAddress(inAddress1, inAddress2); }
};
struct CongruentEqualTo : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
{
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsCongruentAddress(inAddress1, inAddress2); }
};
struct CongruentLessThan : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
{
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsCongruentLessThanAddress(inAddress1, inAddress2); }
};
};
//==================================================================================================
@@ -157,11 +134,8 @@ public:
const AudioObjectPropertyAddress* GetItems() const { return &(*mAddressList.begin()); }
AudioObjectPropertyAddress* GetItems() { return &(*mAddressList.begin()); }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
bool HasItem(const AudioObjectPropertyAddress& inAddress) const { AddressList::const_iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), std::bind1st(CAPropertyAddress::CongruentEqualTo(), inAddress)); return theIterator != mAddressList.end(); }
bool HasExactItem(const AudioObjectPropertyAddress& inAddress) const { AddressList::const_iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), std::bind1st(CAPropertyAddress::EqualTo(), inAddress)); return theIterator != mAddressList.end(); }
#pragma clang diagnostic pop
bool HasItem(const AudioObjectPropertyAddress& inAddress) const { AddressList::const_iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), [&inAddress](const CAPropertyAddress& addr) { return CAPropertyAddress::IsCongruentAddress(addr, inAddress); }); return theIterator != mAddressList.end(); }
bool HasExactItem(const AudioObjectPropertyAddress& inAddress) const { AddressList::const_iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), [&inAddress](const CAPropertyAddress& addr) { return CAPropertyAddress::IsSameAddress(addr, inAddress); }); return theIterator != mAddressList.end(); }
void AppendItem(const AudioObjectPropertyAddress& inAddress) { mAddressList.push_back(inAddress); }
void AppendUniqueItem(const AudioObjectPropertyAddress& inAddress) { if(!HasItem(inAddress)) { mAddressList.push_back(inAddress); } }
@@ -169,7 +143,7 @@ public:
void InsertItemAtIndex(UInt32 inIndex, const AudioObjectPropertyAddress& inAddress) { if(inIndex < mAddressList.size()) { AddressList::iterator theIterator = mAddressList.begin(); std::advance(theIterator, static_cast<int>(inIndex)); mAddressList.insert(theIterator, inAddress); } else { mAddressList.push_back(inAddress); } }
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
void EraseExactItem(const AudioObjectPropertyAddress& inAddress) { AddressList::iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), std::bind1st(CAPropertyAddress::EqualTo(), inAddress)); if(theIterator != mAddressList.end()) { mAddressList.erase(theIterator); } }
void EraseExactItem(const AudioObjectPropertyAddress& inAddress) { AddressList::iterator theIterator = std::find_if(mAddressList.begin(), mAddressList.end(), [&inAddress](const CAPropertyAddress& addr) { return CAPropertyAddress::IsSameAddress(addr, inAddress); }); if(theIterator != mAddressList.end()) { mAddressList.erase(theIterator); } }
#pragma clang diagnostic pop
void EraseItemAtIndex(UInt32 inIndex) { if(inIndex < mAddressList.size()) { AddressList::iterator theIterator = mAddressList.begin(); std::advance(theIterator, static_cast<int>(inIndex)); mAddressList.erase(theIterator); } }
void EraseAllItems() { mAddressList.clear(); }
+2 -2
View File
@@ -87,13 +87,13 @@ public:
// Return false for failure (buffer not large enough).
CARingBufferError Fetch(AudioBufferList *abl, UInt32 nFrames, SampleTime frameNumber);
// will alter mNumDataBytes of the buffers
// will alter mDataByteSize of the buffers
CARingBufferError GetTimeBounds(SampleTime &startTime, SampleTime &endTime);
protected:
int FrameOffset(SampleTime frameNumber) { return (frameNumber & mCapacityFramesMask) * mBytesPerFrame; }
UInt32 FrameOffset(SampleTime frameNumber) { return (frameNumber & mCapacityFramesMask) * mBytesPerFrame; }
CARingBufferError ClipTimeBounds(SampleTime& startRead, SampleTime& endRead);
+57 -6
View File
@@ -17,6 +17,7 @@
1C30A69F1C1E98F000C05AA5 /* CAMutex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3841BBCEFE8000E2DD1 /* CAMutex.cpp */; };
1C38210E1C4A163A00A0C8C6 /* BGM_TaskQueue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C38210C1C4A163A00A0C8C6 /* BGM_TaskQueue.cpp */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMDriver-BGM_TaskQueue.cpp"; }; };
1C3DB4871BE063C500EC8160 /* BGM_DeviceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1C3DB4861BE063C500EC8160 /* BGM_DeviceTests.mm */; };
1C6181A72388FC8A0068C4D3 /* CARingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C6181A52388FC8A0068C4D3 /* CARingBuffer.cpp */; };
1C7010751F05ED5100D8CCDC /* BGM_AudibleState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C7010731F05ED5100D8CCDC /* BGM_AudibleState.cpp */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMDriver-BGM_AudibleState.cpp"; }; };
1C7010761F05ED5100D8CCDC /* BGM_AudibleState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C7010731F05ED5100D8CCDC /* BGM_AudibleState.cpp */; };
1C7010791F07A0BA00D8CCDC /* BGM_VolumeControl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C7010771F07A0BA00D8CCDC /* BGM_VolumeControl.cpp */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMDriver-BGM_VolumeControl.cpp"; }; };
@@ -43,6 +44,8 @@
1CD95B141E93AA5200EB8EF0 /* BGM_Stream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CA2A9E01E8D1D08007A76A4 /* BGM_Stream.cpp */; };
1CDF3ABC1E863B980001E9B7 /* BGM_NullDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CDF3ABA1E863B980001E9B7 /* BGM_NullDevice.cpp */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMDriver-BGM_NullDevice.cpp"; }; };
1CDF3ABF1E8644C20001E9B7 /* BGM_AbstractDevice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CDF3ABD1E8644C20001E9B7 /* BGM_AbstractDevice.cpp */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMDriver-BGM_AbstractDevice.cpp"; }; };
1CE03A4B238A5BF40036908D /* CABitOperations.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CE03A4A238A5BF40036908D /* CABitOperations.h */; };
1CE03A4C23928B370036908D /* CARingBuffer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C6181A52388FC8A0068C4D3 /* CARingBuffer.cpp */; };
27379B821C76D62D0084A24C /* CADebugMacros.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3701BBBD8A4000E2DD1 /* CADebugMacros.cpp */; };
27379B831C76D62D0084A24C /* CADebugPrintf.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB8B3781BBBDFA2000E2DD1 /* CADebugPrintf.cpp */; };
27381A161C8EF50F00DF167C /* BGM_XPCHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 27381A141C8EF50F00DF167C /* BGM_XPCHelper.m */; settings = {COMPILER_FLAGS = "-frandom-seed=BGMDriver-BGM_XPCHelper.m"; }; };
@@ -85,6 +88,7 @@
19FE7B8CE9148B3D8D7517C6 /* BGM_Control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_Control.h; sourceTree = "<group>"; };
19FE7BC3396C4E50D21E1BC8 /* BGM_Control.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_Control.cpp; sourceTree = "<group>"; };
19FE7E6DC2A1B61211D74782 /* BGM_MuteControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_MuteControl.cpp; sourceTree = "<group>"; };
1C09150523F010E8001EB0E1 /* set-version.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "set-version.sh"; sourceTree = "<group>"; };
1C0CB6A61C4E06C00084C15A /* CAAtomicStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAtomicStack.h; path = PublicUtility/CAAtomicStack.h; sourceTree = "<group>"; };
1C0CB6A71C4E06F70084C15A /* CAAtomic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAtomic.h; path = PublicUtility/CAAtomic.h; sourceTree = "<group>"; };
1C0CB6A91C50A3AF0084C15A /* CAAutoDisposer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAutoDisposer.h; path = PublicUtility/CAAutoDisposer.h; sourceTree = "<group>"; };
@@ -103,6 +107,8 @@
1C38210F1C4A18DE00A0C8C6 /* CAPThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CAPThread.cpp; path = PublicUtility/CAPThread.cpp; sourceTree = "<group>"; };
1C3821101C4A18DE00A0C8C6 /* CAPThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAPThread.h; path = PublicUtility/CAPThread.h; sourceTree = "<group>"; };
1C3DB4861BE063C500EC8160 /* BGM_DeviceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BGM_DeviceTests.mm; sourceTree = "<group>"; };
1C6181A42388FC8A0068C4D3 /* CARingBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CARingBuffer.h; path = PublicUtility/CARingBuffer.h; sourceTree = "<group>"; };
1C6181A52388FC8A0068C4D3 /* CARingBuffer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CARingBuffer.cpp; path = PublicUtility/CARingBuffer.cpp; sourceTree = "<group>"; };
1C7010731F05ED5100D8CCDC /* BGM_AudibleState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_AudibleState.cpp; sourceTree = "<group>"; };
1C7010741F05ED5100D8CCDC /* BGM_AudibleState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_AudibleState.h; sourceTree = "<group>"; };
1C7010771F07A0BA00D8CCDC /* BGM_VolumeControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_VolumeControl.cpp; sourceTree = "<group>"; };
@@ -149,6 +155,7 @@
1CDF3ABB1E863B980001E9B7 /* BGM_NullDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_NullDevice.h; sourceTree = "<group>"; };
1CDF3ABD1E8644C20001E9B7 /* BGM_AbstractDevice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_AbstractDevice.cpp; sourceTree = "<group>"; };
1CDF3ABE1E8644C20001E9B7 /* BGM_AbstractDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_AbstractDevice.h; sourceTree = "<group>"; };
1CE03A4A238A5BF40036908D /* CABitOperations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CABitOperations.h; path = PublicUtility/CABitOperations.h; sourceTree = "<group>"; };
1CE3E68C1BE263CA00167F5D /* CACFDictionary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CACFDictionary.cpp; path = PublicUtility/CACFDictionary.cpp; sourceTree = "<group>"; };
1CE3E68D1BE263CA00167F5D /* CACFDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CACFDictionary.h; path = PublicUtility/CACFDictionary.h; sourceTree = "<group>"; };
1CE3E68F1BE2683900167F5D /* CACFArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CACFArray.cpp; path = PublicUtility/CACFArray.cpp; sourceTree = "<group>"; };
@@ -197,6 +204,15 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
1C09150423F010E8001EB0E1 /* Scripts */ = {
isa = PBXGroup;
children = (
1C09150523F010E8001EB0E1 /* set-version.sh */,
);
name = Scripts;
path = ../SharedSource/Scripts;
sourceTree = "<group>";
};
1C0CB6AF1C642C600084C15A /* DeviceClients */ = {
isa = PBXGroup;
children = (
@@ -297,6 +313,7 @@
1C37B3681E9B8D3C000DF98F /* CAPropertyAddress.h */,
1C0CB6A61C4E06C00084C15A /* CAAtomicStack.h */,
1C0CB6A91C50A3AF0084C15A /* CAAutoDisposer.h */,
1CE03A4A238A5BF40036908D /* CABitOperations.h */,
1CE3E68F1BE2683900167F5D /* CACFArray.cpp */,
1CE3E6901BE2683900167F5D /* CACFArray.h */,
1CE3E68C1BE263CA00167F5D /* CACFDictionary.cpp */,
@@ -320,6 +337,8 @@
1CB8B3851BBCEFE8000E2DD1 /* CAMutex.h */,
1C38210F1C4A18DE00A0C8C6 /* CAPThread.cpp */,
1C3821101C4A18DE00A0C8C6 /* CAPThread.h */,
1C6181A42388FC8A0068C4D3 /* CARingBuffer.h */,
1C6181A52388FC8A0068C4D3 /* CARingBuffer.cpp */,
1CB8B38C1BBCF4A9000E2DD1 /* CAVolumeCurve.cpp */,
1CB8B38D1BBCF4A9000E2DD1 /* CAVolumeCurve.h */,
);
@@ -343,6 +362,7 @@
27D643B71C9FABF600737F6E /* BGM_Types.h */,
2771700E1CA0C16200AB34B4 /* BGM_Utils.h */,
275343BC1DE9B44900DF3858 /* BGM_Utils.cpp */,
1C09150423F010E8001EB0E1 /* Scripts */,
27D643C21C9FBC5800737F6E /* BGM_TestUtils.h */,
27D643B81C9FABF600737F6E /* BGMXPCProtocols.h */,
);
@@ -356,6 +376,7 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
1CE03A4B238A5BF40036908D /* CABitOperations.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -386,6 +407,7 @@
1CB8B3601BBBB78D000E2DD1 /* Sources */,
1CB8B3611BBBB78D000E2DD1 /* Frameworks */,
1CB8B3621BBBB78D000E2DD1 /* Resources */,
1C09150823F01E6D001EB0E1 /* Run Script - set-version.sh */,
);
buildRules = (
);
@@ -437,11 +459,11 @@
};
buildConfigurationList = 1CB8B35D1BBBB69C000E2DD1 /* Build configuration list for PBXProject "BGMDriver" */;
compatibilityVersion = "Xcode 6.3";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
mainGroup = 1CB8B3591BBBB69C000E2DD1;
productRefGroup = 1CB8B3651BBBB78D000E2DD1 /* Products */;
@@ -473,11 +495,33 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
1C09150823F01E6D001EB0E1 /* Run Script - set-version.sh */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Run Script - set-version.sh";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "# Append the git HEAD short ID to the build version for SNAPSHOT and DEBUG builds.\n\"$SRCROOT/../SharedSource/Scripts/set-version.sh\"\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
1C8034D61BDD073B00668E00 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1CE03A4C23928B370036908D /* CARingBuffer.cpp in Sources */,
1CD95B121E93AA5200EB8EF0 /* BGM_AbstractDevice.cpp in Sources */,
1CD95B131E93AA5200EB8EF0 /* BGM_NullDevice.cpp in Sources */,
1CD95B141E93AA5200EB8EF0 /* BGM_Stream.cpp in Sources */,
@@ -546,6 +590,7 @@
2743C9CD1D7EF8760089613B /* CACFArray.cpp in Sources */,
2743C9CF1D7EF8760089613B /* CACFDictionary.cpp in Sources */,
2743C9D11D7EF8760089613B /* CACFNumber.cpp in Sources */,
1C6181A72388FC8A0068C4D3 /* CARingBuffer.cpp in Sources */,
2743C9D31D7EF8760089613B /* CACFString.cpp in Sources */,
2743C9D51D7EF8760089613B /* CADebugger.cpp in Sources */,
2743C9D71D7EF8760089613B /* CADebugMacros.cpp in Sources */,
@@ -627,6 +672,7 @@
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -666,7 +712,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.13;
ONLY_ACTIVE_ARCH = YES;
RUN_CLANG_STATIC_ANALYZER = YES;
WARNING_CFLAGS = "-Wpartial-availability";
@@ -694,6 +740,7 @@
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
@@ -733,7 +780,7 @@
GCC_WARN_UNUSED_LABEL = YES;
GCC_WARN_UNUSED_PARAMETER = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.13;
RUN_CLANG_STATIC_ANALYZER = YES;
WARNING_CFLAGS = "-Wpartial-availability";
};
@@ -762,13 +809,14 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = BGMDriver/Info.plist;
INSTALL_GROUP = wheel;
INSTALL_OWNER = root;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.Driver;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = NO;
WRAPPER_EXTENSION = driver;
};
name = Debug;
@@ -797,12 +845,13 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = BGMDriver/Info.plist;
INSTALL_GROUP = wheel;
INSTALL_OWNER = root;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Audio/Plug-Ins/HAL";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.Driver;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = NO;
WRAPPER_EXTENSION = driver;
};
name = Release;
@@ -833,6 +882,7 @@
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
};
name = Debug;
};
@@ -857,6 +907,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
};
name = Release;
};
@@ -26,21 +26,12 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
enableAddressSanitizer = "YES"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1C8034D91BDD073B00668E00"
BuildableName = "BGMDriverTests.xctest"
BlueprintName = "BGMDriverTests"
ReferencedContainer = "container:BGMDriver.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
enableASanStackUseAfterReturn = "YES"
enableUBSanitizer = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@@ -50,15 +41,37 @@
ReferencedContainer = "container:BGMDriver.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1CB8B3631BBBB78D000E2DD1"
BuildableName = "Background Music Device.driver"
BlueprintName = "Background Music Device"
ReferencedContainer = "container:BGMDriver.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES"
testExecutionOrdering = "random">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "1C8034D91BDD073B00668E00"
BuildableName = "BGMDriverTests.xctest"
BlueprintName = "BGMDriverTests"
ReferencedContainer = "container:BGMDriver.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
debugAsWhichUser = "root"
language = ""
enableASanStackUseAfterReturn = "YES"
disableMainThreadChecker = "YES"
launchStyle = "1"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@@ -85,8 +98,6 @@
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
@@ -26,19 +26,20 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
enableAddressSanitizer = "YES"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
enableASanStackUseAfterReturn = "YES"
enableUBSanitizer = "YES"
disableMainThreadChecker = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
enableASanStackUseAfterReturn = "YES"
disableMainThreadChecker = "YES"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
@@ -61,8 +62,6 @@
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
+1 -1
View File
@@ -18,7 +18,7 @@
// BGMDriver
//
// Copyright © 2017 Kyle Neideck
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Self Include
+1 -1
View File
@@ -175,7 +175,7 @@ bool BGM_AudibleState::BufferIsAudible(UInt32 inIOBufferFrameSize, const Floa
// Check each frame to see if any are audible. This could be much more accurate, but seems to
// work well enough for now.
//
// The trade off here is between pausing the music player at the wrong time and unpausing it at
// The trade-off here is between pausing the music player at the wrong time and unpausing it at
// the wrong time. If a short sound (e.g. a UI alert) plays but has a long, barely-audible tail,
// we might not detect the silence quickly enough and pause the music player. Similarly, if
// we've paused the music player and there's a period of near-silence in the new audio, we might
+1 -1
View File
@@ -18,7 +18,7 @@
// BGMDriver
//
// Copyright © 2017 Kyle Neideck
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Self Include
+99 -58
View File
@@ -17,9 +17,11 @@
// BGM_Device.cpp
// BGMDriver
//
// Copyright © 2016, 2017 Kyle Neideck
// Copyright © 2016, 2017, 2019 Kyle Neideck
// Copyright © 2017 Andrew Tonner
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright © 2019 Gordon Childs
// Copyright © 2020 Aleksey Yurkevich
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Based largely on SA_Device.cpp from Apple's SimpleAudioDriver Plug-In sample code. Also uses a few sections from Apple's
// NullAudio.c sample code (found in the same sample project).
@@ -199,9 +201,11 @@ void BGM_Device::InitLoopback()
// Calculate the number of host clock ticks per frame for our loopback clock.
mLoopbackTime.hostTicksPerFrame = CAHostTimeBase::GetFrequency() / mLoopbackSampleRate;
// Zero-out the loopback buffer
// Allocate (or re-allocate) the loopback buffer.
// 2 channels * 32-bit float = bytes in each frame
memset(mLoopbackRingBuffer, 0, sizeof(Float32) * 2 * kLoopbackRingBufferFrameSize);
// Pass 1 for nChannels because it's going to be storing interleaved audio, which means we
// don't need a separate buffer for each channel.
mLoopbackRingBuffer.Allocate(1, 2 * sizeof(Float32), kLoopbackRingBufferFrameSize);
}
#pragma mark Property Operations
@@ -1203,6 +1207,7 @@ void BGM_Device::StartIO(UInt32 inClientID)
// frames or increase latency.
if(!clientIsBGMApp && bgmAppHasClientRegistered)
{
DebugMsg("BGM_Device::StartIO: StartBGMAppPlayThroughSync.");
UInt64 theXPCError = StartBGMAppPlayThroughSync(GetObjectID() == kObjectID_Device_UI_Sounds);
switch(theXPCError)
@@ -1214,9 +1219,15 @@ void BGM_Device::StartIO(UInt32 inClientID)
case kBGMXPC_MessageFailure:
// This most likely means BGMXPCHelper isn't installed or has crashed. IO will probably still work,
// but we may drop frames while the audio hardware starts up.
LogError("BGM_Device::StartIO: Couldn't reach BGMApp via XPC. Attempting to start IO anyway.");
LogWarning("BGM_Device::StartIO: Couldn't reach BGMApp via XPC. Attempting to start IO anyway.");
break;
case kBGMXPC_Timeout:
// XPC timeout. IO will probably still work,
// but we may drop frames while the audio hardware starts up.
LogWarning("BGM_Device::StartIO: Couldn't reach BGMApp via XPC (timeout). Attempting to start IO anyway.");
break;
case kBGMXPC_ReturningEarlyError:
// This can (and might always) happen when the user changes output device in BGMApp while IO is running.
// See BGMAudioDeviceManager::startPlayThroughSync and BGMPlayThrough::WaitForOutputDeviceToStart.
@@ -1224,8 +1235,9 @@ void BGM_Device::StartIO(UInt32 inClientID)
break;
default:
DebugMsg("BGM_Device::StartIO: BGMApp failed to start the output device. theXPCError=%llu", theXPCError);
LogError("BGM_Device::StartIO: BGMApp failed to start the output device. theXPCError=%llu", theXPCError);
Throw(CAException(kAudioHardwareNotRunningError));
//Throw(CAException(kAudioHardwareNotRunningError));
}
}
}
@@ -1345,7 +1357,19 @@ void BGM_Device::DoIOOperation(AudioObjectID inStreamObjectID, UInt32 inClientID
case kAudioServerPlugInIOOperationReadInput:
{
CAMutex::Locker theIOLocker(mIOMutex);
ReadInputData(inIOBufferFrameSize, inIOCycleInfo.mInputTime.mSampleTime, ioMainBuffer);
// Copy the audio data out of our ring buffer.
//
// Take the IO mutex because, in testing, not taking it seemed to make this function
// occasionally miss its deadline and cause an audio glitch. It's hard to be sure
// that was actually the cause, but it's probably not worth the risk anyway.
//
// If an IO operation misses its deadline, the host will log this message:
// Audio IO Overload inputs: '<private>' outputs: '<private>' cause: 'Unknown'
// prewarming: no recovering: no
ReadInputData(inIOBufferFrameSize,
inIOCycleInfo.mInputTime.mSampleTime,
ioMainBuffer);
}
break;
@@ -1384,21 +1408,23 @@ void BGM_Device::DoIOOperation(AudioObjectID inStreamObjectID, UInt32 inClientID
{
CAMutex::Locker theIOLocker(mIOMutex);
bool didChangeState =
mAudibleState.UpdateWithMixedIO(inIOBufferFrameSize,
inIOCycleInfo.mOutputTime.mSampleTime,
reinterpret_cast<const Float32*>(ioMainBuffer));
bool didChangeState =
mAudibleState.UpdateWithMixedIO(
inIOBufferFrameSize,
inIOCycleInfo.mOutputTime.mSampleTime,
reinterpret_cast<const Float32*>(ioMainBuffer));
if(didChangeState)
{
// Send notifications. I'm pretty sure we don't have to use
// RequestDeviceConfigurationChange for this property, but the docs seemed a bit
// unclear to me.
// Send notifications.
mTaskQueue.QueueAsync_SendPropertyNotification(
kAudioDeviceCustomPropertyDeviceAudibleState, GetObjectID());
}
WriteOutputData(inIOBufferFrameSize, inIOCycleInfo.mOutputTime.mSampleTime, ioMainBuffer);
// Copy the audio data into our ring buffer.
WriteOutputData(inIOBufferFrameSize,
inIOCycleInfo.mOutputTime.mSampleTime,
ioMainBuffer);
}
break;
@@ -1425,54 +1451,69 @@ void BGM_Device::EndIOOperation(UInt32 inOperationID, UInt32 inIOBufferFrameSize
void BGM_Device::ReadInputData(UInt32 inIOBufferFrameSize, Float64 inSampleTime, void* outBuffer)
{
// figure out where we are starting
UInt64 theSampleTime = static_cast<UInt64>(inSampleTime);
UInt32 theStartFrameOffset = theSampleTime % kLoopbackRingBufferFrameSize;
// figure out how many frames we need to copy
UInt32 theNumberFramesToCopy1 = inIOBufferFrameSize;
UInt32 theNumberFramesToCopy2 = 0;
if((theStartFrameOffset + theNumberFramesToCopy1) > kLoopbackRingBufferFrameSize)
{
theNumberFramesToCopy1 = kLoopbackRingBufferFrameSize - theStartFrameOffset;
theNumberFramesToCopy2 = inIOBufferFrameSize - theNumberFramesToCopy1;
}
// do the copying (the byte sizes here assume a 32 bit stereo sample format)
Float32* theDestination = reinterpret_cast<Float32*>(outBuffer);
memcpy(theDestination, mLoopbackRingBuffer + (theStartFrameOffset * 2), theNumberFramesToCopy1 * 8);
if(theNumberFramesToCopy2 > 0)
{
memcpy(theDestination + (theNumberFramesToCopy1 * 2), mLoopbackRingBuffer, theNumberFramesToCopy2 * 8);
// Wrap the provided buffer in an AudioBufferList.
AudioBufferList abl = {
.mNumberBuffers = 1,
.mBuffers[0] = {
.mNumberChannels = 2,
// Each frame is 2 Float32 samples (one per channel). The number of frames * the number
// of bytes per frame = the size of outBuffer in bytes.
.mDataByteSize = static_cast<UInt32>(inIOBufferFrameSize * sizeof(Float32) * 2),
.mData = outBuffer
}
};
// Copy the audio data from our ring buffer into the provided buffer.
CARingBufferError err =
mLoopbackRingBuffer.Fetch(&abl,
inIOBufferFrameSize,
static_cast<CARingBuffer::SampleTime>(inSampleTime));
// Handle errors.
switch (err)
{
case kCARingBufferError_CPUOverload:
// Write silence to the buffer.
memset(outBuffer, 0, abl.mBuffers[0].mDataByteSize);
break;
case kCARingBufferError_TooMuch:
// Should be impossible, but handle it just in case. Write silence to the buffer and
// return an error code.
memset(outBuffer, 0, abl.mBuffers[0].mDataByteSize);
Throw(CAException(kAudioHardwareIllegalOperationError));
case kCARingBufferError_OK:
break;
default:
throw CAException(kAudioHardwareUnspecifiedError);
}
//DebugMsg("BGM_Device::ReadInputData: Reading. theSampleTime=%llu theStartFrameOffset=%u theNumberFramesToCopy1=%u theNumberFramesToCopy2=%u", theSampleTime, theStartFrameOffset, theNumberFramesToCopy1, theNumberFramesToCopy2);
}
void BGM_Device::WriteOutputData(UInt32 inIOBufferFrameSize, Float64 inSampleTime, const void* inBuffer)
{
// figure out where we are starting
UInt64 theSampleTime = static_cast<UInt64>(inSampleTime);
UInt32 theStartFrameOffset = theSampleTime % kLoopbackRingBufferFrameSize;
// figure out how many frames we need to copy
UInt32 theNumberFramesToCopy1 = inIOBufferFrameSize;
UInt32 theNumberFramesToCopy2 = 0;
if((theStartFrameOffset + theNumberFramesToCopy1) > kLoopbackRingBufferFrameSize)
{
theNumberFramesToCopy1 = kLoopbackRingBufferFrameSize - theStartFrameOffset;
theNumberFramesToCopy2 = inIOBufferFrameSize - theNumberFramesToCopy1;
}
// do the copying (the byte sizes here assume a 32 bit stereo sample format)
const Float32* theSource = reinterpret_cast<const Float32*>(inBuffer);
memcpy(mLoopbackRingBuffer + (theStartFrameOffset * 2), theSource, theNumberFramesToCopy1 * 8);
if(theNumberFramesToCopy2 > 0)
{
memcpy(mLoopbackRingBuffer, theSource + (theNumberFramesToCopy1 * 2), theNumberFramesToCopy2 * 8);
// Wrap the provided buffer in an AudioBufferList.
AudioBufferList abl = {
.mNumberBuffers = 1,
.mBuffers[0] = {
.mNumberChannels = 2,
// Each frame is 2 Float32 samples (one per channel). The number of frames * the number
// of bytes per frame = the size of inBuffer in bytes.
.mDataByteSize = static_cast<UInt32>(inIOBufferFrameSize * sizeof(Float32) * 2),
.mData = const_cast<void *>(inBuffer)
}
};
// Copy the audio data from the provided buffer into our ring buffer.
CARingBufferError err =
mLoopbackRingBuffer.Store(&abl,
inIOBufferFrameSize,
static_cast<CARingBuffer::SampleTime>(inSampleTime));
// Return an error code if we failed to store the data. (But ignore CPU overload, which would be
// temporary.)
if (err != kCARingBufferError_OK && err != kCARingBufferError_CPUOverload)
{
Throw(CAException(err));
}
//DebugMsg("BGM_Device::WriteOutputData: Writing. theSampleTime=%llu theStartFrameOffset=%u theNumberFramesToCopy1=%u theNumberFramesToCopy2=%u", theSampleTime, theStartFrameOffset, theNumberFramesToCopy1, theNumberFramesToCopy2);
}
void BGM_Device::ApplyClientRelativeVolume(UInt32 inClientID, UInt32 inIOBufferFrameSize, void* ioBuffer) const
+6 -3
View File
@@ -17,8 +17,9 @@
// BGM_Device.h
// BGMDriver
//
// Copyright © 2016, 2017 Kyle Neideck
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright © 2016, 2017, 2019 Kyle Neideck
// Copyright © 2019 Gordon Childs
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Based largely on SA_Device.h from Apple's SimpleAudioDriver Plug-In sample code.
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
@@ -43,6 +44,7 @@
// PublicUtility Includes
#include "CAMutex.h"
#include "CAVolumeCurve.h"
#include "CARingBuffer.h"
// System Includes
#include <CoreFoundation/CoreFoundation.h>
@@ -237,7 +239,8 @@ private:
#define kLoopbackRingBufferFrameSize 16384
Float64 mLoopbackSampleRate;
Float32 mLoopbackRingBuffer[kLoopbackRingBufferFrameSize * 2];
CARingBuffer mLoopbackRingBuffer;
// TODO: a comment explaining why we need a clock for loopback-only mode
struct {
Float64 hostTicksPerFrame = 0.0;
+1 -1
View File
@@ -18,7 +18,7 @@
// BGMDriver
//
// Copyright © 2016 Kyle Neideck
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Based largely on SA_Object.cpp from Apple's SimpleAudioDriver Plug-In sample code.
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
+4 -4
View File
@@ -18,7 +18,7 @@
// BGMDriver
//
// Copyright © 2016 Kyle Neideck
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Based largely on SA_Object.h from Apple's SimpleAudioDriver Plug-In sample code.
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
@@ -46,7 +46,7 @@
//
// This is the base class for objects managed by BGM_ObjectMap. It's only job is to ensure that
// objects of this type have the proper external semantics for a reference counted object. This
// means that the desctructor is protected so that these objects cannot be deleted directly. Also,
// means that the destructor is protected so that these objects cannot be deleted directly. Also,
// these objects many not make a copy of another object or be assigned from another object. Note
// that the reference count of the object is tracked and owned by the BGM_ObjectMap.
//
@@ -68,8 +68,8 @@
// not need to worry about being thread safe while Activate() is in progress.
//
// Subclasses of this class must also implement Deactivate(). This method is called when the object
// is at the end of it's lifecycle. Once Deactivate() has been called, the object may no longer
// perform active opertions, including Deactivating other objects. This is based on the notion that
// is at the end of its lifecycle. Once Deactivate() has been called, the object may no longer
// perform active operations, including Deactivating other objects. This is based on the notion that
// all the objects have a definite point at which they are considered dead to the outside world.
// For example, an AudioDevice object is dead if it's hardware is unplugged. The point of death is
// the notification the owner of the device gets to signal that it has been unplugged. Note that it
+1 -1
View File
@@ -18,7 +18,7 @@
// BGMDriver
//
// Copyright © 2016, 2017 Kyle Neideck
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Based largely on SA_PlugIn.cpp from Apple's SimpleAudioDriver Plug-In sample code.
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
+1 -1
View File
@@ -18,7 +18,7 @@
// BGMDriver
//
// Copyright © 2016 Kyle Neideck
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Based largely on SA_PlugIn.h from Apple's SimpleAudioDriver Plug-In sample code.
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
+6 -6
View File
@@ -18,7 +18,7 @@
// BGMDriver
//
// Copyright © 2016, 2017 Kyle Neideck
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Based largely on SA_PlugIn.cpp from Apple's SimpleAudioDriver Plug-In sample code.
// https://developer.apple.com/library/mac/samplecode/AudioDriverExamples
@@ -153,11 +153,11 @@ void* BGM_Create(CFAllocatorRef inAllocator, CFUUIDRef inRequestedTypeUUID)
{
// This is the CFPlugIn factory function. Its job is to create the implementation for the given
// type provided that the type is supported. Because this driver is simple and all its
// initialization is handled via static initalization when the bundle is loaded, all that
// initialization is handled via static initialization when the bundle is loaded, all that
// needs to be done is to return the AudioServerPlugInDriverRef that points to the driver's
// interface. A more complicated driver would create any base line objects it needs to satisfy
// the IUnknown methods that are used to discover that actual interface to talk to the driver.
// The majority of the driver's initilization should be handled in the Initialize() method of
// The majority of the driver's initialization should be handled in the Initialize() method of
// the driver's AudioServerPlugInDriverInterface.
#pragma unused(inAllocator)
@@ -196,7 +196,7 @@ static HRESULT BGM_QueryInterface(void* inDriver, REFIID inUUID, LPVOID* outInte
ThrowIf(theRequestedUUID == NULL, CAException(kAudioHardwareIllegalOperationError), "BGM_QueryInterface: failed to create the CFUUIDRef");
// AudioServerPlugIns only support two interfaces, IUnknown (which has to be supported by all
// CFPlugIns and AudioServerPlugInDriverInterface (which is the actual interface the HAL will
// CFPlugIns) and AudioServerPlugInDriverInterface (which is the actual interface the HAL will
// use).
ThrowIf(!CFEqual(theRequestedUUID, IUnknownUUID) && !CFEqual(theRequestedUUID, kAudioServerPlugInDriverInterfaceUUID), CAException(E_NOINTERFACE), "BGM_QueryInterface: requested interface is unsupported");
ThrowIf(gAudioServerPlugInDriverRefCount == UINT32_MAX, CAException(E_NOINTERFACE), "BGM_QueryInterface: the ref count is maxxed out");
@@ -268,7 +268,7 @@ static OSStatus BGM_Initialize(AudioServerPlugInDriverRef inDriver, AudioServerP
// The job of this method is, as the name implies, to get the driver initialized. One specific
// thing that needs to be done is to store the AudioServerPlugInHostRef so that it can be used
// later. Note that when this call returns, the HAL will scan the various lists the driver
// maintains (such as the device list) to get the inital set of objects the driver is
// maintains (such as the device list) to get the initial set of objects the driver is
// publishing. So, there is no need to notifiy the HAL about any objects created as part of the
// execution of this method.
@@ -397,7 +397,7 @@ static OSStatus BGM_RemoveDeviceClient(AudioServerPlugInDriverRef inDriver, Audi
static OSStatus BGM_PerformDeviceConfigurationChange(AudioServerPlugInDriverRef inDriver, AudioObjectID inDeviceObjectID, UInt64 inChangeAction, void* inChangeInfo)
{
// This method is called to tell the device that it can perform the configuation change that it
// This method is called to tell the device that it can perform the configuration change that it
// had requested via a call to the host method, RequestDeviceConfigurationChange(). The
// arguments, inChangeAction and inChangeInfo are the same as what was passed to
// RequestDeviceConfigurationChange().
+2 -2
View File
@@ -17,7 +17,7 @@
// BGMDriver
//
// Copyright © 2017 Kyle Neideck
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Self Include
@@ -287,7 +287,7 @@ void BGM_Stream::GetPropertyData(AudioObjectID inObjectID,
break;
case kAudioStreamPropertyLatency:
// This property returns any additonal presentation latency the stream has.
// This property returns any additional presentation latency the stream has.
ThrowIf(inDataSize < sizeof(UInt32),
CAException(kAudioHardwareBadPropertySizeError),
"BGM_Stream::GetPropertyData: not enough space for the return "
+1 -1
View File
@@ -18,7 +18,7 @@
// BGMDriver
//
// Copyright © 2016, 2017 Kyle Neideck
// Portions copyright (C) 2013 Apple Inc. All Rights Reserved.
// Copyright (C) 2013 Apple Inc. All Rights Reserved.
//
// Self Include
+4 -3
View File
@@ -14,10 +14,11 @@
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
//
// BGM_XPCHelper.cpp
// BGM_XPCHelper.m
// BGMDriver
//
// Copyright © 2016, 2017 Kyle Neideck
// Copyright © 2016, 2017, 2020, 2024 Kyle Neideck
// Copyright © 2020 Aleksey Yurkevich
//
// Self Include
@@ -37,7 +38,7 @@
static const UInt64 REMOTE_CALL_DEFAULT_TIMEOUT_SECS = 30;
static NSXPCConnection* CreateXPCHelperConnection()
static NSXPCConnection* CreateXPCHelperConnection(void)
{
// Create a connection to BGMXPCHelper's Mach service. If it isn't already running, launchd will start BGMXPCHelper when we send
// a message to this connection.
@@ -17,7 +17,7 @@
// BGM_ClientMap.cpp
// BGMDriver
//
// Copyright © 2016 Kyle Neideck
// Copyright © 2016, 2017, 2019 Kyle Neideck
// Copyright © 2017 Andrew Tonner
//
@@ -59,11 +59,13 @@ void BGM_ClientMap::AddClient(BGM_Client inClient)
// The shadow maps (which were the main maps until we swapped them) are now missing the new client. Add it again to
// keep the sets of maps identical.
AddClientToShadowMaps(inClient);
// Remove the client from the past clients map (if it was in there)
// Insert the client into the past clients map. We do this here rather than in RemoveClient
// because some apps add multiple clients with the same bundle ID and we want to give them all
// the same settings (volume, etc.).
if(inClient.mBundleID.IsValid())
{
mPastClientMap.erase(inClient.mBundleID);
mPastClientMap[inClient.mBundleID] = inClient;
}
}
@@ -102,12 +104,6 @@ BGM_Client BGM_ClientMap::RemoveClient(UInt32 inClientID)
BGM_Client theClient = theClientItr->second;
// Insert the client into the past clients map
if(theClient.mBundleID.IsValid())
{
mPastClientMap[theClient.mBundleID] = theClient;
}
// Remove the client from the shadow maps
mClientMapShadow.erase(theClientItr);
mClientMapByPIDShadow.erase(theClient.mProcessID);
@@ -53,7 +53,7 @@ class BGM_ClientTasks;
// removed by the HAL we add it to a map of past clients to keep track of settings specific to that
// client. (Currently only the client's volume.)
//
// Since the maps are read from during IO, this class has to to be real-time safe when accessing
// Since the maps are read from during IO, this class has to be real-time safe when accessing
// them. So each map has an identical "shadow" map, which we use to buffer updates.
//
// To update the clients we lock the shadow maps, modify them, have BGM_TaskQueue's real-time
@@ -182,8 +182,8 @@ private:
std::map<CACFString, BGM_ClientPtrList> mClientMapByBundleID;
std::map<CACFString, BGM_ClientPtrList> mClientMapByBundleIDShadow;
// Clients are added to mPastClientMap after they're removed so we can restore settings specific to them
// if they get added again.
// Clients are added to mPastClientMap so we can restore settings specific to them if they get
// added again.
std::map<CACFString, BGM_Client> mPastClientMap;
};

Some files were not shown because too many files have changed in this diff Show More