Compare commits

...

121 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
75 changed files with 1358 additions and 5412 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
-126
View File
@@ -1,126 +0,0 @@
language: objective-c
matrix:
include:
- os: osx
osx_image: xcode11.3
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.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
# 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.
# TODO: Debug packages are failing to install on Travis, but I can't reproduce the problem locally,
# so temporarily allow debug builds to pass even if the install fails.
- sudo installer -pkg Background-Music-*/BackgroundMusic-*.pkg -target / -verbose -dumplog || [[ "$TRAVIS_TAG" =~ .*DEBUG.* ]]
# Print the installer logs. This only prints the current day's logs.
- echo -en '/var/log/install.log\ntravis_fold:start:install.log\\r'
- grep -A 9999 $(date +%Y-%m-%d) /var/log/install.log
- echo -en 'travis_fold:end:install.log\\r'
# 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"
# Also deploy (i.e. attach to the GitHub release) the log file from the build package.sh did. If we
# just printed it, we'd run out of space and the build would be killed. The release is created as a
# draft initially, so we can remove the log file before making the release public.
- cp build_and_install.log Background-Music-*
# Post on IRC when Travis builds finish.
notifications:
irc: "irc.freenode.org#backgroundmusic"
# Upload the .pkg and archives 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
draft: true
on:
repo: kyleneideck/BackgroundMusic
tags: true
# TODO: Use "condition" to build master and tags?
condition: $DEPLOY = true
+19 -12
View File
@@ -72,7 +72,7 @@
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 /* travis-skip.py in Resources */ = {isa = PBXBuildFile; fileRef = 1C62FE5623D4278300B9B68E /* travis-skip.py */; };
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 */; };
@@ -225,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 */
@@ -325,7 +327,7 @@
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 /* travis-skip.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; name = "travis-skip.py"; path = "UITests/travis-skip.py"; sourceTree = "<group>"; };
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>"; };
@@ -433,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 */
@@ -562,6 +566,8 @@
1C2FC31D1EC723A100A76592 /* BGMASOutputDevice.h */,
1C2FC31A1EC7238A00A76592 /* BGMASOutputDevice.mm */,
1C2FC2FF1EB4D6E700A76592 /* BGMApp.sdef */,
9E129A3F2602AE620005851B /* BGMASApplication.h */,
9E129A402602AE620005851B /* BGMASApplication.m */,
);
name = Scripting;
sourceTree = "<group>";
@@ -764,7 +770,7 @@
children = (
1CCC4F5F1E5840EF008053E4 /* BGMAppUITests-Info.plist */,
1CCC4F491E581C0D008053E4 /* BGMAppUnitTests-Info.plist */,
1C62FE5623D4278300B9B68E /* travis-skip.py */,
1C62FE5623D4278300B9B68E /* skip-ui-tests.py */,
);
name = "Supporting Files";
sourceTree = "<group>";
@@ -985,10 +991,9 @@
};
buildConfigurationList = 1CB8B3311BBA75EF000E2DD1 /* Build configuration list for PBXProject "BGMApp" */;
compatibilityVersion = "Xcode 6.3";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
@@ -1025,7 +1030,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1C62FE5823D4278300B9B68E /* travis-skip.py in Resources */,
1C62FE5823D4278300B9B68E /* skip-ui-tests.py in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1152,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 */,
@@ -1218,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 */,
@@ -1392,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;
@@ -1435,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";
@@ -1501,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;
@@ -1544,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";
@@ -1585,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;
@@ -1625,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;
@@ -1705,7 +1715,6 @@
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 = "";
@@ -1723,7 +1732,6 @@
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 = "";
@@ -1742,7 +1750,6 @@
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 = "";
+4 -3
View File
@@ -18,16 +18,16 @@
// BGMApp
//
// 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;
@@ -53,6 +53,7 @@ static NSInteger const kSeparatorBelowVolumesMenuItemTag = 4;
@property (weak) IBOutlet NSMenuItem* debugLoggingMenuItemUnwrapped;
@property (readonly) BGMAudioDeviceManager* audioDevices;
@property BGMAppVolumesController* appVolumes;
@end
+118 -52
View File
@@ -17,7 +17,8 @@
// BGMAppDelegate.mm
// BGMApp
//
// Copyright © 2016-2020 Kyle Neideck
// Copyright © 2016-2022 Kyle Neideck
// Copyright © 2021 Marcus Wu
//
// Self Include
@@ -25,6 +26,7 @@
// Local Includes
#import "BGM_Utils.h"
#import "BGMAppVolumes.h"
#import "BGMAppVolumesController.h"
#import "BGMAutoPauseMusic.h"
#import "BGMAutoPauseMenuItem.h"
@@ -64,7 +66,6 @@ static NSString* const kOptShowDockIcon = @"--show-dock-icon";
BGMAutoPauseMenuItem* autoPauseMenuItem;
BGMMusicPlayers* musicPlayers;
BGMSystemSoundsVolume* systemSoundsVolume;
BGMAppVolumesController* appVolumes;
BGMOutputDeviceMenuSection* outputDeviceMenuSection;
BGMPreferencesMenu* prefsMenu;
BGMDebugLoggingMenuItem* debugLoggingMenuItem;
@@ -73,6 +74,7 @@ static NSString* const kOptShowDockIcon = @"--show-dock-icon";
}
@synthesize audioDevices = audioDevices;
@synthesize appVolumes = appVolumes;
- (void) awakeFromNib {
[super awakeFromNib];
@@ -117,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;
@@ -134,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.
@@ -181,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;
}
}
}
}
+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
+89 -4
View File
@@ -19,6 +19,8 @@
//
// 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];
}
}
@@ -206,11 +281,22 @@ static NSString* const kMoreAppsMenuTitle = @"More Apps";
[((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);
@@ -242,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.
@@ -447,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++) {
+10 -5
View File
@@ -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)) ||
+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
+5 -1
View File
@@ -241,7 +241,11 @@ BGMBackgroundMusicDevice::ResponsibleBundleIDsOf(CACFString inParentBundleID)
// Skype
{ "com.skype.skype", { "com.skype.skype.Helper" } },
// Google Chrome
{ "com.google.Chrome", { "com.google.Chrome.helper" } }
{ "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
+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;
}
}
}
+3 -4
View File
@@ -894,7 +894,7 @@ void BGMPlayThrough::HandleBGMDeviceIsRunning(BGMPlayThrough* refCon)
DebugMsg("BGMPlayThrough::HandleBGMDeviceIsRunning: "
"BGMDevice is %srunning somewhere other than BGMApp",
isRunningSomewhereOtherThanBGMApp ? "" : " not");
isRunningSomewhereOtherThanBGMApp ? "" : "not ");
if(isRunningSomewhereOtherThanBGMApp)
{
@@ -933,9 +933,8 @@ 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
+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.
+11 -4
View File
@@ -125,10 +125,17 @@ static CGFloat const kVolumeIconAdditionalVerticalPadding = 0.075;
- (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;
+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([] {
+82 -72
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15705" 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"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -85,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">
@@ -102,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">
@@ -111,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>
@@ -127,7 +127,7 @@
<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">
@@ -136,7 +136,7 @@
<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">
@@ -148,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">
@@ -166,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.4.0" 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">
@@ -188,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">
@@ -216,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"/>
@@ -243,7 +243,7 @@
<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">
@@ -252,20 +252,29 @@
<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-2020 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">
@@ -284,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">
@@ -306,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">
@@ -333,53 +342,54 @@
<image name="buttonCell:IXo-C7-3uE:image" width="1" height="1">
<mutableData key="keyedArchiveRepresentation">
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCgpMDM2PD9VJG51bGzWDQ4PEBESExQVFhcYVk5T
S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T
U2l6ZV5OU1Jlc2l6aW5nTW9kZVYkY2xhc3NcTlNJbWFnZUZsYWdzVk5TUmVwc1dOU0NvbG9ygAIQAIAN
EiDDAACAA4ALVnsxLCAxfdIbDxweWk5TLm9iamVjdHOhHYAEgArSGw8gI6IhIoAFgAaACdIlDyYnXxAU
TlNUSUZGUmVwcmVzZW50YXRpb26AB4AITxEIxE1NACoAAAAKAAAAEAEAAAMAAAABAAEAAAEBAAMAAAAB
AAEAAAECAAMAAAACAAgACAEDAAMAAAABAAEAAAEGAAMAAAABAAEAAAEKAAMAAAABAAEAAAERAAQAAAAB
AAAACAESAAMAAAABAAEAAAEVAAMAAAABAAIAAAEWAAMAAAABAAEAAAEXAAQAAAABAAAAAgEcAAMAAAAB
AAEAAAEoAAMAAAABAAIAAAFSAAMAAAABAAEAAAFTAAMAAAACAAEAAYdzAAcAAAf0AAAA0AAAAAAAAAf0
YXBwbAIgAABtbnRyR1JBWVhZWiAH0AACAA4ADAAAAABhY3NwQVBQTAAAAABub25lAAAAAAAAAAAAAAAA
AAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAVkZXNjAAAAwAAAAG9kc2NtAAABMAAABmZjcHJ0AAAHmAAAADh3dHB0AAAH0AAAABRrVFJD
AAAH5AAAAA5kZXNjAAAAAAAAABVHZW5lcmljIEdyYXkgUHJvZmlsZQAAAAAAAAAAAAAAFUdlbmVyaWMg
R3JheSBQcm9maWxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
bWx1YwAAAAAAAAAfAAAADHNrU0sAAAAqAAABhGVuVVMAAAAoAAABrmNhRVMAAAAsAAAB1nZpVk4AAAAs
AAACAnB0QlIAAAAqAAACLnVrVUEAAAAsAAACWGZyRlUAAAAqAAAChGh1SFUAAAAuAAACrnpoVFcAAAAQ
AAAC3G5iTk8AAAAsAAAC7GtvS1IAAAAYAAADGGNzQ1oAAAAkAAADMGhlSUwAAAAgAAADVHJvUk8AAAAk
AAADdGRlREUAAAA6AAADmGl0SVQAAAAuAAAD0nN2U0UAAAAuAAAEAHpoQ04AAAAQAAAELmphSlAAAAAW
AAAEPmVsR1IAAAAkAAAEVHB0UE8AAAA4AAAEeG5sTkwAAAAqAAAEsGVzRVMAAAAoAAAE2nRoVEgAAAAk
AAAFAnRyVFIAAAAiAAAFJmZpRkkAAAAsAAAFSGhySFIAAAA6AAAFdHBsUEwAAAA2AAAFrnJ1UlUAAAAm
AAAF5GFyRUcAAAAoAAAGCmRhREsAAAA0AAAGMgBWAWEAZQBvAGIAZQBjAG4A/QAgAHMAaQB2AP0AIABw
AHIAbwBmAGkAbABHAGUAbgBlAHIAaQBjACAARwByAGEAeQAgAFAAcgBvAGYAaQBsAGUAUABlAHIAZgBp
AGwAIABkAGUAIABnAHIAaQBzACAAZwBlAG4A6AByAGkAYwBDHqUAdQAgAGgA7ABuAGgAIABNAOAAdQAg
AHgA4QBtACAAQwBoAHUAbgBnAFAAZQByAGYAaQBsACAAQwBpAG4AegBhACAARwBlAG4A6QByAGkAYwBv
BBcEMAQzBDAEOwRMBD0EOAQ5ACAEPwRABD4ERAQwBDkEOwAgAEcAcgBhAHkAUAByAG8AZgBpAGwAIABn
AOkAbgDpAHIAaQBxAHUAZQAgAGcAcgBpAHMAwQBsAHQAYQBsAOEAbgBvAHMAIABzAHoA/AByAGsAZQAg
AHAAcgBvAGYAaQBskBp1KHBwlo6Ccl9pY8+P8ABHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QB0AG8AbgBl
AHAAcgBvAGYAaQBsx3y8GAAgAEcAcgBhAHkAINUEuFzTDMd8AE8AYgBlAGMAbgD9ACABYQBlAGQA/QAg
AHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAARwByAGEAeQAgBdsF3AXcBdkAUAByAG8AZgBpAGwAIABn
AHIAaQAgAGcAZQBuAGUAcgBpAGMAQQBsAGwAZwBlAG0AZQBpAG4AZQBzACAARwByAGEAdQBzAHQAdQBm
AGUAbgAtAFAAcgBvAGYAaQBsAFAAcgBvAGYAaQBsAG8AIABnAHIAaQBnAGkAbwAgAGcAZQBuAGUAcgBp
AGMAbwBHAGUAbgBlAHIAaQBzAGsAIABnAHIA5QBzAGsAYQBsAGUAcAByAG8AZgBpAGxmbpAacHBepmPP
j/Blh072TgCCLDCwMOwwpDDXMO0w1TChMKQw6wOTA7UDvQO5A7oDzAAgA8ADwQO/A8YDrwO7ACADswO6
A8EDuQBQAGUAcgBmAGkAbAAgAGcAZQBuAOkAcgBpAGMAbwAgAGQAZQAgAGMAaQBuAHoAZQBuAHQAbwBz
AEEAbABnAGUAbQBlAGUAbgAgAGcAcgBpAGoAcwBwAHIAbwBmAGkAZQBsAFAAZQByAGYAaQBsACAAZwBy
AGkAcwAgAGcAZQBuAOkAcgBpAGMAbw5CDhsOIw5EDh8OJQ5MDioONQ5ADhcOMg4XDjEOSA4nDkQOGwBH
AGUAbgBlAGwAIABHAHIAaQAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUAbgAgAGgAYQByAG0AYQBh
AHAAcgBvAGYAaQBpAGwAaQBHAGUAbgBlAHIAaQENAGsAaQAgAHAAcgBvAGYAaQBsACAAcwBpAHYAaQBo
ACAAdABvAG4AbwB2AGEAVQBuAGkAdwBlAHIAcwBhAGwAbgB5ACAAcAByAG8AZgBpAGwAIABzAHoAYQBy
AG8BWwBjAGkEHgQxBEkEOAQ5ACAEQQQ1BEAESwQ5ACAEPwRABD4ERAQ4BDsETAZFBkQGQQAgBioGOQYx
BkoGQQAgAEcAcgBhAHkAIAYnBkQGOQYnBkUARwBlAG4AZQByAGUAbAAgAGcAcgDlAHQAbwBuAGUAYgBl
AHMAawByAGkAdgBlAGwAcwBlAAB0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCBy
aWdodHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUQABAAAAARbMY3VydgAAAAAAAAABAc0AANIqKywtWiRj
bGFzc25hbWVYJGNsYXNzZXNfEBBOU0JpdG1hcEltYWdlUmVwoywuL1pOU0ltYWdlUmVwWE5TT2JqZWN0
0iorMTJXTlNBcnJheaIxL9IqKzQ1Xk5TTXV0YWJsZUFycmF5ozQxL9M3OA85OjtXTlNXaGl0ZVxOU0Nv
bG9yU3BhY2VEMCAwABADgAzSKis9PldOU0NvbG9yoj0v0iorQEFXTlNJbWFnZaJALwAIABEAGgAkACkA
MgA3AEkATABRAFMAYgBoAHUAfACLAJIAnwCmAK4AsACyALQAuQC7AL0AxADJANQA1gDYANoA3wDiAOQA
5gDoAO0BBAEGAQgJ0AnVCeAJ6Qn8CgAKCwoUChkKIQokCikKOAo8CkMKSwpYCl0KXwphCmYKbgpxCnYK
fgAAAAAAAAIBAAAAAAAAAEIAAAAAAAAAAAAAAAAAAAqBA
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>
+2 -2
View File
@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.4.0</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-2020 Background Music contributors</string>
<string>Copyright © 2016-2024 Background Music contributors</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSMicrophoneUsageDescription</key>
@@ -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
@@ -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
+7 -6
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}")
@@ -139,20 +140,20 @@ sleep 2
# 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 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.
+42 -12
View File
@@ -17,7 +17,7 @@
// BGMAppUITests.mm
// BGMAppUITests
//
// Copyright © 2017, 2018, 2020 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.
//
@@ -34,7 +34,6 @@
#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
@@ -75,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 {
@@ -102,9 +134,7 @@
[menuItems[@"Quit Background Music"] click];
// BGMApp should quit.
for (NSRunningApplication* runningApp : [[NSWorkspace sharedWorkspace] runningApplications]) {
XCTAssertFalse([[runningApp bundleIdentifier] isEqualToString:@kBGMAppBundleID]);
}
XCTAssertTrue([app waitForState:XCUIApplicationStateNotRunning timeout:10.0]);
[super tearDown];
}
@@ -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)
+2 -2
View File
@@ -17,13 +17,13 @@
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>0.4.0</string>
<string>0.4.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016-2020 Background Music contributors</string>
<string>Copyright © 2016-2024 Background Music contributors</string>
<key>XPCService</key>
<dict>
<key>ServiceType</key>
+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");
}
+2 -2
View File
@@ -17,7 +17,7 @@
// BGMDebugLogging.c
// PublicUtility
//
// Copyright © 2020 Kyle Neideck
// Copyright © 2020, 2024 Kyle Neideck
//
// Self Include
@@ -36,7 +36,7 @@
// 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()
int BGMDebugLoggingIsEnabled(void)
{
return gDebugLoggingIsEnabled;
}
+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__
+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(); }
@@ -459,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 */;
@@ -672,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;
@@ -711,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";
@@ -739,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;
@@ -778,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";
};
+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
+12 -3
View File
@@ -20,6 +20,7 @@
// Copyright © 2016, 2017, 2019 Kyle Neideck
// Copyright © 2017 Andrew Tonner
// 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
@@ -1206,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)
@@ -1217,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.
@@ -1227,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));
}
}
}
+2 -2
View File
@@ -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
+5 -5
View File
@@ -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().
+1 -1
View File
@@ -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 "
+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.
@@ -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
@@ -132,7 +132,7 @@ private:
// as one at a later time.
pid_t mMusicPlayerProcessIDProperty = 0;
// The value of the kAudioDeviceCustomPropertyMusicPlayerBundleID property, or the empty string it it's
// The value of the kAudioDeviceCustomPropertyMusicPlayerBundleID property, or the empty string if it's
// unset/null. UTF8 encoding.
//
// As with mMusicPlayerProcessID, we keep a copy of the bundle ID the user sets for the music player
+2 -2
View File
@@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>0.4.0</string>
<string>0.4.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
@@ -37,7 +37,7 @@
</array>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2016-2020 Background Music contributors</string>
<string>Copyright © 2016-2024 Background Music contributors</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
+13 -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
@@ -195,6 +197,7 @@ inline bool CAAtomicTestAndSetBarrier(int bitToSet, void* theAddress)
return OSAtomicTestAndSetBarrier(bitToSet, (volatile 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
@@ -243,6 +246,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 )
{
@@ -258,6 +264,7 @@ inline bool CAAtomicCompareAndSwapPtrBarrier(void *__oldValue, void *__newValue,
return CAAtomicCompareAndSwap32Barrier((int32_t)__oldValue, (int32_t)__newValue, (int32_t *)__theValue);
#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
@@ -273,6 +280,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 +311,7 @@ inline bool CASpinLockTry( volatile CASpinLock *__lock )
#endif
}
#pragma clang diagnostic pop
#endif // __CAAtomic_h__
+3
View File
@@ -160,6 +160,8 @@ public:
static bool compare_and_swap(T *oldvalue, T *newvalue, T **pvalue)
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
#if TARGET_OS_MAC
#if __LP64__
return ::OSAtomicCompareAndSwap64Barrier(int64_t(oldvalue), int64_t(newvalue), (int64_t *)pvalue);
@@ -172,6 +174,7 @@ public:
//return ::CompareAndSwap(UInt32(oldvalue), UInt32(newvalue), (UInt32 *)pvalue);
return CAAtomicCompareAndSwap32Barrier(SInt32(oldvalue), SInt32(newvalue), (SInt32*)pvalue);
#endif
#pragma clang diagnostic pop
}
protected:
@@ -101,6 +101,8 @@ public:
// STL Helpers
public:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
struct EqualTo : public std::binary_function<AudioObjectPropertyAddress, AudioObjectPropertyAddress, bool>
{
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsSameAddress(inAddress1, inAddress2); }
@@ -120,6 +122,7 @@ public:
{
bool operator()(const AudioObjectPropertyAddress& inAddress1, const AudioObjectPropertyAddress& inAddress2) const { return IsCongruentLessThanAddress(inAddress1, inAddress2); }
};
#pragma clang diagnostic pop
};
+1 -1
View File
@@ -13,7 +13,7 @@ the virtual device to the real output device and a few other things. The virtual
## Summary
From the user's perspective, BGMDevice appears as one input device and one output device, both named "Background Music".
They're shown in `System Preferences > Sound` along with the real audio devices.
They're shown in `System Settings > Sound` along with the real audio devices.
When you start BGMApp, it sets BGMDevice as your system's default output device so the system (i.e. Core Audio) will
start sending all<sup id="a2">[2](#f2)</sup> your audio data to BGMDriver. BGMDriver plays that audio on BGMDevice's
Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 157 KiB

BIN
View File
Binary file not shown.
+3 -2
View File
@@ -38,12 +38,13 @@
(Audio will stop working until the next step, so you might want to pause any running audio apps.)
```shell
sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod
sudo killall coreaudiod
```
or, if that fails
```shell
sudo killall coreaudiod
sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod
```
- Run `Background Music.app`.
+21 -4
View File
@@ -9,22 +9,39 @@
<sup>(Open `/Applications/Utilities/Terminal.app` and paste the following at the prompt.)</sup>
```shell
sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod
sudo killall coreaudiod
```
or, if that fails
```shell
sudo killall coreaudiod
sudo launchctl kickstart -kp system/com.apple.audio.coreaudiod
```
- Go to the Sound section in System Preferences and change your default output device at least once. (If you only have
- Go to the Sound section in System Settings and change your default output device at least once. (If you only have
one device now, either use `Audio MIDI Setup.app` to create a temporary aggregate device, restart any audio apps that
have stopped working or just restart your system.)
## Troubleshooting
If you still have the Background Music audio device, try using `Terminal.app` to make sure you've deleted its files:
```shell
sudo ls /Library/Audio/Plug-Ins/HAL
```
If you see `Background Music Device.driver` in the output of that command, use this command to actually delete it:
```shell
sudo rm -rf "/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"
```
Then restart `coreaudiod` again. If that still doesn't work, restart your computer. If that doesn't work, feel free to
open an issue. Include the output of `sudo ls /Library/Audio/Plug-Ins/HAL`.
## Optional
- Delete `BGMXPCHelper.xpc` from `/usr/local/libexec` or possibly `/Library/Application Support/Background Music`.
- Unregister BGMXPCHelper.
- If you're using OS X 10.11:
- If you're using OS X 10.11 or later:
```shell
sudo launchctl bootout system /Library/LaunchDaemons/com.bearisdriving.BGM.XPCHelper.plist
+41 -37
View File
@@ -5,18 +5,19 @@
# Background Music
##### macOS audio utility
<img src="Images/README/Screenshot.png" width="340" height="342" />
<img src="Images/README/Screenshot.png" width="340" height="443" />
[Overview](#overview)<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Auto-pause music](#auto-pause-music)<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Application volume](#application-volume)<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Recording system audio](#recording-system-audio)<br/>
[Download](#download)<br/>
[Build and Install](#build-and-install)</br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[Recording system audio](#recording-system-audio)<br/>
[Download](#download)<br/>
[Run / Configure](#run--configure)<br/>
[Build and Install](#installing-from-source-code)</br>
[Uninstall](#uninstall)<br/>
[Troubleshooting](#troubleshooting)<br/>
[Related Projects](#related-projects)<br/>
[License](#license)<br/>
[Troubleshooting](#troubleshooting)<br/>
[Related Projects](#related-projects)<br/>
[License](#license)<br/>
# Overview
@@ -33,18 +34,18 @@
The auto-pause feature currently supports following music players:
+ iTunes
+ [iTunes](https://www.apple.com/itunes/)
+ [Spotify](https://www.spotify.com)
+ [VLC](https://www.videolan.org/vlc/)
+ [VOX](https://vox.rocks/mac-music-player)
+ [Decibel](https://sbooth.org/Decibel/)
+ [Hermes](http://hermesapp.org/)
+ [Swinsian](https://swinsian.com/)
+ [GPMDP](https://www.googleplaymusicdesktopplayer.com/)
+ [GPMDP](https://www.googleplaymusicdesktopplayer.com/)
Adding support for a new music player is usually straightforward.<sup id="a1">[1](#f1)</sup> If you don't know how to program, or just don't feel
like it, feel free to [create an issue](https://github.com/kyleneideck/BackgroundMusic/issues/new). Otherwise, see
[BGMMusicPlayer.h](BGMApp/BGMApp/Music%20Players/BGMMusicPlayer.h).
[BGMMusicPlayer.h](BGMApp/BGMApp/Music%20Players/BGMMusicPlayer.h).
## Application volume
@@ -61,22 +62,22 @@ the **Background Music** device. You can create the aggregate device using the *
# Download
**Requires macOS 10.10+**.
**Requires macOS 10.13+**.
You can download the current version of **Background Music** using the following options. We also have [snapshot builds](https://github.com/kyleneideck/BackgroundMusic/releases).
### Option 1
Download **version 0.3.2**:
Download **version 0.4.3**:
<a href="https://github.com/kyleneideck/BackgroundMusic/releases/download/v0.3.2/BackgroundMusic-0.3.2.pkg"><img
<a href="https://github.com/kyleneideck/BackgroundMusic/releases/download/v0.4.3/BackgroundMusic-0.4.3.pkg"><img
src="Images/README/pkg-icon.png" width="32" height="32" align="absmiddle" />
BackgroundMusic-0.3.2.pkg</a> (571 KB)
BackgroundMusic-0.4.3.pkg</a> (771 KB)
> <sub>MD5: 7f34d9e6595566f3ba14e7afc89c86a2</sub><br/>
> <sub>SHA256: 0cd7b488b5ab97a1ecb496e484a6c209c29f35ab503e6f73b45e56719a7aba18</sub><br/>
> <sub>MD5: 8c3bfe26c9cdf27365b9843f719ef188</sub><br/>
> <sub>SHA256: c1c48a37c83af44ce50bee68879856c96b2f6c97360ce461b1c7d653515be7fd</sub><br/>
> <sub>PGP:
> [sig](https://github.com/kyleneideck/BackgroundMusic/releases/download/v0.3.2/BackgroundMusic-0.3.2.pkg.asc),
> [sig](https://github.com/kyleneideck/BackgroundMusic/releases/download/v0.4.3/BackgroundMusic-0.4.3.pkg.asc),
> [key (0595DF814E41A6F69334C5E2CAA8D9B8E39EC18C)](https://bearisdriving.com/kyle-neideck.gpg)</sub>
### Option 2
@@ -84,20 +85,22 @@ BackgroundMusic-0.3.2.pkg</a> (571 KB)
Install using [Homebrew](https://brew.sh/) by running the following command in **Terminal**:
```bash
brew cask install background-music
brew install --cask background-music
```
If you want the snapshot version, run:
# Run / Configure
```bash
brew tap homebrew/cask-versions
brew cask install background-music-pre
```
Just run `Applications > Background Music.app`! **Background Music** sets itself as your default output device under
`System Settings > Sound` when it starts up (and sets it back on Quit).
### Launch at Startup (Optional)
Add **Background Music** to `System Settings > General > Login Items`.
# Installing from Source Code
**Background Music** usually takes less than a minute to build. You need [Xcode](https://developer.apple.com/xcode/download/) version
8 or higher.
**Background Music** usually takes less than a minute to build. You need [Xcode](https://developer.apple.com/xcode/download/) version
10 or higher.
### Option 1
@@ -144,12 +147,12 @@ To manually uninstall, see [MANUAL_UNINSTALL.md](https://github.com/kyleneideck/
# Troubleshooting
If Background Music crashes and your audio stops working, open `System Preferences > Sound` and change your
If Background Music crashes and your audio stops working, open `System Settings > Sound` and change your
system's default output device to something other than the **Background Music device**. If it already is, then
change the default device and then change it back again.
Make sure you allow "microphone access" when you first run Background Music. If you denied it, go to
`System Preferences > Security & Privacy > Privacy > Microphone`, find Background Music in the list
`System Settings > Security & Privacy > Privacy > Microphone`, find Background Music in the list
and check the box next to it. Background Music doesn't actually listen to your microphone. It needs
the permission because it gets your system audio from its virtual input device, which macOS counts
as a microphone. (We're working on it in [#177](/../../issues/177).)
@@ -166,16 +169,16 @@ meeting volume.
- **Only 2-channel (stereo) audio devices are currently supported for output.**
- **VLC pauses iTunes or Spotify when playing, and stops Background Music from unpausing your music afterward.**
- **VLC pauses iTunes or Spotify when playing, and stops Background Music from unpausing your music afterward.**
- Under VLC's preferences, select **Show All**. Navigate to **Interface > Main interfaces > macosx** and change *Control external music players* to either *Do nothing* or *Pause and resume iTunes/Spotify*.
- Under VLC's preferences, select **Show All**. Navigate to **Interface > Main interfaces > macosx** and change *Control external music players* to either *Do nothing* or *Pause and resume iTunes/Spotify*.
- **Skype pauses iTunes during calls.**
- **Skype pauses iTunes during calls.**
- To disable this, uncheck *Pause iTunes during calls* on the **General** tab of **Skype**'s preferences.
- **Plugging in or unplugging headphones when Background Music isn't running causes silence in the system audio.**
- Navigate to **System Preferences > Sound**. Click the **Output** tab and change your default output device to something other than the **Background Music** device. Alternatively, press **Option + Click** on the sound icon within the menu bar to select a different output device. This happens when macOS remembers that the **Background Music** device was your default audio device the last time you used (or didn't use) headphones.
- **Plugging in or unplugging headphones when Background Music isn't running causes silence in the system audio.**
- Navigate to **System Settings > Sound**. Click the **Output** tab and change your default output device to something other than the **Background Music** device. Alternatively, press **Option + Click** on the sound icon within the menu bar to select a different output device. This happens when macOS remembers that the **Background Music** device was your default audio device the last time you used (or didn't use) headphones.
- **[A Chrome bug](https://bugs.chromium.org/p/chromium/issues/detail?id=557620) stops Chrome from switching to the Background Music device after you open Background Music.**
- Chrome's audio will still play, but **Background Music** won't be aware of it.
@@ -183,7 +186,7 @@ meeting volume.
- **Some applications play notification sounds that are only just long enough to trigger an auto-pause.**
- Increase the `kPauseDelayNSec` constant in [BGMAutoPauseMusic.mm](/BGMApp/BGMApp/BGMAutoPauseMusic.mm). It will increase your music's overlap time over other audio, so don't increase it too much. See [#5](https://github.com/kyleneideck/BackgroundMusic/issues/5) for details.
Many other issues are in listed in [TODO.md](/TODO.md) and in [GitHub
Many other issues are listed in [TODO.md](/TODO.md) and in [GitHub
Issues](https://github.com/kyleneideck/BackgroundMusic/issues).
# Related projects
@@ -198,7 +201,7 @@ Issues](https://github.com/kyleneideck/BackgroundMusic/issues).
- [llaudio](https://github.com/mountainstorm/llaudio) - "An old piece of work to reverse engineer the Mac OSX
user/kernel audio interface. Shows how to read audio straight out of the kernel as you would on Darwin (where most the
OSX goodness is missing)"
- [mute.fm](http://www.mute.fm), [GitHub](https://github.com/jaredsohn/mutefm) (Windows) - Auto-pause music
- [mute.fm](http://www.mutefm.com), [GitHub](https://github.com/jaredsohn/mutefm) (Windows) - Auto-pause music
- [Jack OS X](http://www.jackosx.com) - "A Jack audio connection kit implementation for Mac OS X"
- [PulseAudio OS X](https://github.com/zonque/PulseAudioOSX) - "PulseAudio for Mac OS X"
- [Sound Pusher](https://github.com/q-p/SoundPusher) - "Virtual audio device, real-time encoder and SPDIF forwarder for
@@ -209,14 +212,15 @@ Issues](https://github.com/kyleneideck/BackgroundMusic/issues).
### Non-free
- [Audio Hijack](https://rogueamoeba.com/audiohijack/) - "Capture Audio From Anywhere on Your Mac"
- [Audio Hijack](https://rogueamoeba.com/audiohijack/), [SoundSource](https://rogueamoeba.com/soundsource/) - "Capture
Audio From Anywhere on Your Mac", "Get truly powerful control over all the audio on your Mac!"
- [Sound Siphon](https://staticz.com/soundsiphon/), [Sound Control](https://staticz.com/soundcontrol/) - System/app audio recording, per-app volumes, system audio equaliser
- [SoundBunny](https://www.prosofteng.com/soundbunny-mac-volume-control/) - "Control application volume independently."
- [Boom 2](http://www.globaldelight.com/boom/index.php) - "The Best Volume Booster & Equalizer For Mac"
- [Boom 2](https://www.globaldelight.com/boom/) - "The Best Volume Booster & Equalizer For Mac"
## License
Copyright © 2016-2020 [Background Music contributors](https://github.com/kyleneideck/BackgroundMusic/graphs/contributors).
Copyright © 2016-2024 [Background Music contributors](https://github.com/kyleneideck/BackgroundMusic/graphs/contributors).
Licensed under [GPLv2](https://www.gnu.org/licenses/gpl-2.0.html), or any later version.
**Background Music** includes code from:
+2 -1
View File
@@ -17,7 +17,7 @@
// BGM_TestUtils.h
// SharedSource
//
// Copyright © 2016 Kyle Neideck
// Copyright © 2016, 2021 Kyle Neideck
//
#ifndef __SharedSource__BGM_TestUtils__
@@ -37,6 +37,7 @@
template<typename ExpectedException>
void BGMShouldThrow(XCTestCase* self, const std::function<void()>& f)
{
#pragma unused (self)
try
{
f();
+3 -1
View File
@@ -17,7 +17,7 @@
// BGM_Types.h
// SharedSource
//
// Copyright © 2016, 2017, 2019 Kyle Neideck
// Copyright © 2016, 2017, 2019, 2024 Kyle Neideck
//
#ifndef SharedSource__BGM_Types
@@ -36,6 +36,7 @@
static const char* const kBGMProjectURL = "https://github.com/kyleneideck/BackgroundMusic";
static const char* const kBGMIssueTrackerURL = "https://github.com/kyleneideck/BackgroundMusic/issues";
static const char* const kBGMContributorsURL = "https://github.com/kyleneideck/BackgroundMusic/graphs/contributors";
#pragma mark IDs
@@ -163,6 +164,7 @@ enum BGMDeviceAudibleState : SInt32
#define kAppPanLeftRawValue -100
#define kAppPanCenterRawValue 0
#define kAppPanRightRawValue 100
#define kAppPanNoValue INT_MIN
// kAudioDeviceCustomPropertyEnabledOutputControls indices
enum
+19 -6
View File
@@ -19,7 +19,7 @@
#
# build_and_install.sh
#
# Copyright © 2016-2020 Kyle Neideck
# Copyright © 2016-2022, 2024 Kyle Neideck
# Copyright © 2016 Nick Jacques
#
# Builds and installs BGMApp, BGMDriver and BGMXPCHelper. Requires xcodebuild and Xcode.
@@ -136,7 +136,7 @@ BUILD_FAILED_ERROR_MSG="A build command failed. Probably a compilation error."
BGMAPP_FAILED_TO_START_ERROR_MSG="Background Music (${APP_PATH}/${APP_DIR}) didn't seem to start \
up. It might just be taking a while.
If it didn't install correctly, you'll need to open the Sound control panel in System Preferences \
If it didn't install correctly, you'll need to open the Sound control panel in System Settings \
and change your output device at least once. Your sound probably won't work until you do. (Or you \
restart your computer.)
@@ -519,7 +519,6 @@ log_debug_info() {
uname -mrsv >> ${LOG_FILE} 2>&1
/bin/bash --version >> ${LOG_FILE} 2>&1
/usr/bin/env python --version >> ${LOG_FILE} 2>&1
echo "On git branch: $(git rev-parse --abbrev-ref HEAD 2>&1)" >> ${LOG_FILE}
echo "Most recent commit: $(git rev-parse HEAD 2>&1)" \
@@ -618,8 +617,7 @@ fi
# Update the user's sudo timestamp if we're going to need to sudo at some point. This prompts the
# user for their password.
if [[ "${XCODEBUILD_ACTION}" == "install" ]]; then
# Don't call sudo -v if this is a Travis CI build.
if ([[ -z ${TRAVIS:-} ]] || [[ "${TRAVIS}" != true ]]) && ! sudo -v; then
if ! sudo -v; then
echo "$(tput setaf 9)ERROR$(tput sgr0): This script must be run by a user with" \
"administrator (sudo) privileges." >&2
exit 1
@@ -643,6 +641,16 @@ if [[ "${XCODEBUILD_ACTION}" == "install" ]]; then
SUDO="sudo"
ACTIONING="Installing"
DSTROOT_ARG="DSTROOT=/"
# Work around an Xcode (15.2) bug where xcodebuild incorrectly detects a dependency cycle if
# DSTROOT is set to /.
for v in /Volumes/*; do
if [[ "$(realpath "$v")" == "/" ]]; then
DSTROOT_ARG="DSTROOT=$v"
echo "Set DSTROOT_ARG to ${DSTROOT_ARG} to work around an Xcode bug." >> ${LOG_FILE}
break
fi
done
echo "DSTROOT_ARG: ${DSTROOT_ARG}." >> ${LOG_FILE}
elif [[ "${XCODEBUILD_ACTION}" == "archive" ]]; then
SUDO=""
ACTIONING="Building and archiving"
@@ -663,7 +671,7 @@ else
fi
enableUBSanArg() {
if [[ -n "$ENABLE_UBSAN" ]]; then
if [[ "${ENABLE_UBSAN+}" != "" ]]; then
echo "-enableUndefinedBehaviorSanitizer"
echo "$ENABLE_UBSAN"
fi
@@ -672,6 +680,11 @@ enableUBSanArg() {
# Clean all projects. Done separately to workaround what I think is a bug in Xcode 10.0. If you just
# add "clean" to the other xcodebuild commands, they seem to fail because of the DSTROOT="/" arg.
if [[ "${CLEAN}" != "" ]]; then
if [[ "${XCODEBUILD_ACTION}" == "archive" ]]; then
# Delete any previous archives to force Xcode to rebuild them.
/bin/rm -rf "${ARCHIVES_DIR}" >> ${LOG_FILE} 2>&1
fi
# Disable the -e shell option and error trap for build commands so we can handle errors
# differently.
(disable_error_handling
+22 -5
View File
@@ -19,8 +19,9 @@
#
# package.sh
#
# Copyright © 2017-2020 Kyle Neideck
# Copyright © 2017-2022 Kyle Neideck
# Copyright © 2016, 2017 Takayama Fumihiko
# Copyright © 2023 modue sp. z o.o.
#
# Builds Background Music and packages it into a .pkg file. Call this script with -d to use the
# debug build configuration.
@@ -124,7 +125,7 @@ version="$(/usr/libexec/PlistBuddy \
-c "Print CFBundleShortVersionString" \
"${bgmapp_path}/Contents/Info.plist")"
# Everything in out_dir at the end of this script will be released in the Travis CI builds.
# Everything in out_dir at the end of this script will be released in the CI builds.
out_dir="Background-Music-$version"
rm -rf "$out_dir"
mkdir "$out_dir"
@@ -138,6 +139,19 @@ fi
# --------------------------------------------------
echo "Compiling ListInputDevices"
if ! [[ $packaging_operation == "repackage" ]]; then
echo "Compiling ListInputDevices"
swiftc pkg/ListInputDevices.swift -o pkg/ListInputDevices-x86_64 -target x86_64-apple-macos13.0
swiftc pkg/ListInputDevices.swift -o pkg/ListInputDevices-arm64 -target arm64-apple-macos13.0
# Combine the x86_64 and arm64 binaries into a universal binary.
lipo -create pkg/ListInputDevices-x86_64 pkg/ListInputDevices-arm64 -output pkg/ListInputDevices
rm pkg/ListInputDevices-x86_64 pkg/ListInputDevices-arm64
fi
# --------------------------------------------------
echo "Copying Files"
rm -rf "pkgroot"
@@ -157,12 +171,14 @@ if [[ $packaging_operation == "repackage" ]]; then
repackage_scripts_dir="${repackage_dir}/Installer.pkg/Scripts"
cp "${repackage_scripts_dir}/preinstall" "$scripts_dir"
cp "${repackage_scripts_dir}/postinstall" "$scripts_dir"
cp "${repackage_scripts_dir}/ListInputDevices" "$scripts_dir"
cp "${repackage_scripts_dir}/com.bearisdriving.BGM.XPCHelper.plist.template" "$scripts_dir"
cp "${repackage_scripts_dir}/safe_install_dir.sh" "$scripts_dir"
cp "${repackage_scripts_dir}/post_install.sh" "$scripts_dir"
else
cp "pkg/preinstall" "$scripts_dir"
cp "pkg/postinstall" "$scripts_dir"
mv "pkg/ListInputDevices" "$scripts_dir"
cp "BGMApp/BGMXPCHelper/com.bearisdriving.BGM.XPCHelper.plist.template" "$scripts_dir"
cp "BGMApp/BGMXPCHelper/safe_install_dir.sh" "$scripts_dir"
cp "BGMApp/BGMXPCHelper/post_install.sh" "$scripts_dir"
@@ -184,6 +200,7 @@ if [[ $packaging_operation != "repackage" ]]; then
set_permissions "$scripts_dir"
chmod 755 "$scripts_dir/preinstall"
chmod 755 "$scripts_dir/postinstall"
chmod 755 "$scripts_dir/ListInputDevices"
chmod 755 "$scripts_dir/BGMXPCHelper.xpc/Contents/MacOS/BGMXPCHelper"
fi
@@ -207,9 +224,9 @@ if [[ $packaging_operation == "repackage" ]]; then
pkg="$out_dir/BackgroundMusic-$version.repackaged.pkg"
else
# As a security check for releases, we manually build the same package locally, compare it to
# the release built by Travis and then code sign it. (And then remove the code signature on a
# different computer and check that it still matches the one from Travis.) So we include
# "unsigned" in the name to differentiate the two versions.
# the release built in CI and then code sign it. (And then remove the code signature on a
# different computer and check that it still matches the one from CI.) So we include "unsigned"
# in the name to differentiate the two versions.
pkg="$out_dir/BackgroundMusic-$version.unsigned.pkg"
fi
+2 -2
View File
@@ -5,7 +5,7 @@
<volume-check>
<allowed-os-versions>
<!-- TODO: Get this from the Xcode project files instead of hardcoding it. -->
<os-version min="10.9" />
<os-version min="10.13" />
</allowed-os-versions>
</volume-check>
@@ -17,7 +17,7 @@
<pkg-ref id="com.bearisdriving.BGM"/>
<options customize="never" require-scripts="false" />
<options customize="never" require-scripts="false" hostArchitectures="arm64,x86_64" />
<choices-outline>
<line choice="default">
+41
View File
@@ -0,0 +1,41 @@
// 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/>.
//
// ListInputDevices.swift
//
// Copyright © 2022 Kyle Neideck
//
// The postinstall script uses this to check that BGMDevice was installed successfully.
//
import AVFoundation
var devices: Array<AVCaptureDevice>
if #available(macOS 10.15, *) {
devices = AVCaptureDevice.DiscoverySession(
deviceTypes: [ .builtInMicrophone, .externalUnknown ],
mediaType: .audio,
position: .unspecified
).devices
} else {
devices = AVCaptureDevice.devices(for: .audio)
}
print(devices.map {
(device: AVCaptureDevice) -> Array<String> in
[device.uniqueID, device.modelID, device.localizedName]
})
Executable → Regular
+87 -37
View File
@@ -19,7 +19,8 @@
#
# postinstall
#
# Copyright © 2017-2020 Kyle Neideck
# Copyright © 2017-2022, 2024 Kyle Neideck
# Copyright © 2023 modue sp. z o.o.
#
# Make sure we use the built-in versions of programs, for consistency. Probably not necessary
@@ -31,6 +32,13 @@ function log {
echo "$@"
}
function log_output {
while read line
do
log "$line"
done < /dev/stdin
}
coreaudiod_plist="/System/Library/LaunchDaemons/com.apple.audio.coreaudiod.plist"
dest_volume="$3"
xpc_helper_install_path="$(bash safe_install_dir.sh -y)"
@@ -44,6 +52,17 @@ log "Installing BGMXPCHelper to $xpc_helper_install_path"
rm -rf "${xpc_helper_install_path}/BGMXPCHelper.xpc"
cp -Rf "BGMXPCHelper.xpc" "$xpc_helper_install_path"
# Remove the quarantine attribute from BGMXPCHelper. Since macOS 14.0, this is needed to keep
# Gatekeeper from trying to show a permission prompt when BGMXPCHelper is launched. That would
# fail because BGMXPCHelper is a daemon, and Gatekeeper would prevent it from launching.
#
# TODO: I think, ideally, we should use the new Service Management API to install BGMXPCHelper,
# which would probably avoid this issue. See
# <https://developer.apple.com/documentation/servicemanagement/updating_your_app_package_installer_to_use_the_new_service_management_api>.
log "Removing com.apple.quarantine attribute from BGMXPCHelper."
sudo xattr -dr com.apple.quarantine "${xpc_helper_install_path}/BGMXPCHelper.xpc" 2>&1 | log_output
ls -lah@ "${xpc_helper_install_path}/BGMXPCHelper.xpc" 2>&1 | log_output
# TODO: Fail the install and show an error message if this fails.
bash "post_install.sh" "$xpc_helper_install_path" "BGMXPCHelper.xpc/Contents/MacOS/BGMXPCHelper" "."
@@ -53,19 +72,34 @@ bash "post_install.sh" "$xpc_helper_install_path" "BGMXPCHelper.xpc/Contents/Mac
# 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 (which should all work).
(sudo launchctl kickstart -k system/com.apple.audio.coreaudiod &>/dev/null || \
launchctl kill SIGTERM system/com.apple.audio.coreaudiod &>/dev/null || \
launchctl kill TERM system/com.apple.audio.coreaudiod &>/dev/null || \
launchctl kill 15 system/com.apple.audio.coreaudiod &>/dev/null || \
launchctl kill -15 system/com.apple.audio.coreaudiod &>/dev/null || \
(launchctl unload "$coreaudiod_plist" &>/dev/null && \
launchctl load "$coreaudiod_plist" &>/dev/null) || \
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 killall coreaudiod &>/dev/null || \
(sudo launchctl unload "$coreaudiod_plist" &>/dev/null && \
sudo launchctl load "$coreaudiod_plist" &>/dev/null)
) && \
sleep 2
# Wait until coreaudiod has restarted and BGMDevice is ready to use.
retries=5
while [[ $retries -gt 0 ]]; do
if ! system_profiler SPAudioDataType | grep "Background Music" >/dev/null 2>&1; then
device_list="$(system_profiler SPAudioDataType)"
# The system_profiler command above doesn't work on GitHub Actions, so also try this
# alternative, which does. I haven't looked into the root cause, so this may be necessary for
# other systems as well.
input_device_list="$(./ListInputDevices)"
# "BGMDevice" is the UID, which is a stable identifier. device_list doesn't include the UIDs, so
# we have to check for BGMDevice's name.
if [[ "$device_list" =~ "Background Music" ]] || \
[[ "$input_device_list" =~ "BGMDevice" ]]; then
log "Background Music device is installed and available."
# Break out of the loop.
retries=0
else
retries=$((retries - 1))
if [[ $retries -gt 0 ]]; then
log "Background Music device not found. Trying again in 3 seconds..."
@@ -75,51 +109,66 @@ while [[ $retries -gt 0 ]]; do
# installer GUI instead of just logging them. See
# <https://developer.apple.com/library/archive/documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html#//apple_ref/doc/uid/TP40005370-CH100-SW12>.
log "Background Music device not found. Installation failed."
log "Audio devices:"
log "$device_list"
log "Audio input devices:"
log "$input_device_list"
exit 1
fi
else
# BGMDevice is installed and available, so we can continue the script.
retries=0
fi
done
# Try opening BGMApp using its bundle ID first so the installer can declare it as "relocatable".
# That way, if the user moves BGMApp and then installs a newer version of Background Music, the
# installer will try to find the old version of BGMApp and put the new one in the same place.
# Launch BGMApp.
#
# Use launchctl to make sure we don't run BGMApp as the installer user.
#
# If we can't open BGMApp, it's very likely it didn't install properly, so we fail the install.
# Allow a few retries because it seems to sometimes fail intermittently in CI builds. For example,
# <https://travis-ci.org/github/kyleneideck/BackgroundMusic/jobs/764126689>. From the error
# messages, it looks like BGMApp isn't always registered with Launch Services by this point.
logged_in_user_id="$(id -u "${USER}")"
did_open_bgmapp=false
retries=5
# TODO: If they have multiple copies of BGMApp, this might open one of the old ones.
log "Opening Background Music.app by bundle ID"
if launchctl asuser "${logged_in_user_id}" \
open -b com.bearisdriving.BGM.App; then
did_open_bgmapp=true
fi
while [[ $did_open_bgmapp != "true" ]] && [[ $retries -gt 0 ]]; do
retries=$((retries - 1))
if [[ $did_open_bgmapp != "true" ]]; then
dest_volume_no_trailing_slash="${dest_volume/%\//}"
log "Opening ${dest_volume_no_trailing_slash}/Applications/Background Music.app"
# Try opening BGMApp using its bundle ID first so the installer can declare it as "relocatable".
# That way, if the user moves BGMApp and then installs a newer version of Background Music, the
# installer will try to find the old version of BGMApp and put the new one in the same place.
#
# Use launchctl to make sure we don't run BGMApp as the installer user.
#
# TODO: If they have multiple copies of BGMApp, this might open one of the old ones.
log "Opening Background Music.app by bundle ID"
if launchctl asuser "${logged_in_user_id}" \
open "${dest_volume_no_trailing_slash}/Applications/Background Music.app"; then
open -b com.bearisdriving.BGM.App; then
did_open_bgmapp=true
fi
fi
if [[ $did_open_bgmapp != "true" ]]; then
log "Opening Background Music.app using AppleScript"
if osascript -e 'tell application "Background Music" to activate'; then
did_open_bgmapp=true
if [[ $did_open_bgmapp != "true" ]]; then
dest_volume_no_trailing_slash="${dest_volume/%\//}"
log "Opening ${dest_volume_no_trailing_slash}/Applications/Background Music.app"
if launchctl asuser "${logged_in_user_id}" \
open "${dest_volume_no_trailing_slash}/Applications/Background Music.app"; then
did_open_bgmapp=true
fi
fi
fi
if [[ $did_open_bgmapp != "true" ]]; then
log "Opening Background Music.app using AppleScript"
if osascript -e 'tell application "Background Music" to activate'; then
did_open_bgmapp=true
fi
fi
if [[ $did_open_bgmapp != "true" ]]; then
log "Failed to open Background Music.app. Will retry shortly."
sleep 1
fi
done
# If we couldn't open BGMApp, it probably didn't install properly, but it's not certain, so don't
# fail the install.
if [[ $did_open_bgmapp != "true" ]]; then
log "Failed to open Background Music.app"
# Fail the install.
exit 1
log "Failed to open Background Music.app. Giving up."
fi
# The installer plays a sound when it finishes, so give BGMApp a second to launch.
@@ -128,3 +177,4 @@ sleep 1
exit 0
+1 -1
View File
@@ -63,7 +63,7 @@ if [ "$user_prompt" == "y" ] || [ "$user_prompt" == "Y" ]; then
# Invalidate sudo ticket
sudo -k
# Open System Preferences and go to Sound > Output.
# Open System Settings and go to Sound > Output.
osascript -e 'tell application id "com.apple.systempreferences"
activate
reveal anchor "output" of pane id "com.apple.preference.sound"