Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d4e1bff10 | |||
| 1c743053c6 | |||
| de1a9fa413 | |||
| 12a7deb15f | |||
| e6375b0919 | |||
| 029cb2f5f8 | |||
| 2ac8a1afd1 | |||
| 784bb249a9 | |||
| 51e1266532 | |||
| 4b7caeeb54 | |||
| 7e592ebb2b | |||
| 0aea54f0c5 | |||
| 9407ee36d2 | |||
| 9058e3c556 | |||
| a8f4ace099 | |||
| 610f15c556 | |||
| 62174be95e | |||
| 48bdcd1a64 | |||
| cb6f38821e | |||
| b96fcb7a47 | |||
| ad14e1b0a4 | |||
| 61be8bead4 | |||
| 8b2bf56eca | |||
| e655d571e0 | |||
| 9257d50b15 | |||
| 3097f4b621 | |||
| 73f221e423 | |||
| 7d700b3de5 | |||
| 014a6eecc8 | |||
| 2cc3faaebb | |||
| 5000c64084 | |||
| 4fc776e4a6 | |||
| ca3c497940 | |||
| 08837fcdca | |||
| 076514c83a | |||
| 8090f12204 | |||
| bd38207146 | |||
| d92d9e6d29 | |||
| d048287140 | |||
| c89566d212 | |||
| cbb3e20e1c | |||
| 9f9c18748a | |||
| 3040a20766 | |||
| 30ec1c24b7 | |||
| cc2516a705 | |||
| 909615617a | |||
| 89eebb0e02 | |||
| 058af733be | |||
| ffd634245c | |||
| 7afb8a6c12 | |||
| c68e05244c | |||
| 22dea1ef5d | |||
| b2770f1793 | |||
| a730fb98f8 | |||
| 1fdf47cd12 | |||
| 4608f4bbac | |||
| 5f1641da8f | |||
| 2e1fb9db46 | |||
| fbf95c0605 | |||
| ada2615d88 | |||
| 71c11315e5 | |||
| 4e9d21d499 | |||
| da96132da0 | |||
| d84471c29e | |||
| 6ef13ed762 | |||
| 8ddfb81148 | |||
| f4ea06e3c3 | |||
| 01217d2d8c | |||
| 5a048431b2 | |||
| 333aaf93e2 | |||
| 9441a31d9d | |||
| f31aebcf51 | |||
| 3f4b119c86 | |||
| 3bfd68615f | |||
| 24b248e09d | |||
| 0d5965b266 | |||
| c84dd5754c | |||
| a5b7cd69e3 | |||
| fe786fb338 | |||
| 5a5f9d0a8d | |||
| 89186d526f | |||
| 69fb7f69e3 | |||
| b38f6ddcd6 | |||
| e9e13a3056 | |||
| c7df7506bc | |||
| 169ee92802 | |||
| 0a87596c1c | |||
| 3551e0f6e6 | |||
| 6d23685084 | |||
| a93ec5ee34 | |||
| 26d354866e | |||
| 06cf3d8777 | |||
| 0e0cc5fd05 | |||
| 213c339309 | |||
| c0a556be04 | |||
| a62b783e5f | |||
| 44e3e96986 | |||
| d9a22d1b62 | |||
| ce56a96829 | |||
| c10ea9d132 | |||
| 64003ca1d6 | |||
| 7df7f062e0 | |||
| 2090761ae5 | |||
| c303f60343 | |||
| 97d3645bd3 | |||
| 9765193c1f | |||
| 2ff4c08e75 | |||
| 3a529cc0fb | |||
| 0f653f90a8 | |||
| 4c40e0286e | |||
| bdebc07b38 | |||
| 65c2a34ce5 | |||
| 94459f7e8c | |||
| ef4bd54965 | |||
| c0ab98b98b | |||
| 043bfb3633 | |||
| 63393054ba | |||
| 6486c8ccf3 | |||
| c068810953 | |||
| dd05f60c1d | |||
| df02aae263 | |||
| f20dd578ab | |||
| 026558d974 | |||
| ae724fe7af | |||
| 42fbd7590e | |||
| ae564d761b | |||
| 6edf25bfbe | |||
| 694c8d612e | |||
| f24a6cb2f1 | |||
| 70a64de734 | |||
| 488ed16dfc | |||
| 8499b85736 | |||
| 1a2c880b32 | |||
| 4cfc8df48a | |||
| 4ff3db4238 | |||
| 23ecf7e46e | |||
| 63b458abdc |
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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
|
||||
-119
@@ -1,119 +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.
|
||||
- sudo installer -pkg Background-Music-*/BackgroundMusic-*.pkg -target / -verbose -dumplog
|
||||
- echo -en '/var/log/install.log\ntravis_fold:start:install.log\\r'
|
||||
- cat /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"
|
||||
# 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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
@@ -247,6 +249,7 @@
|
||||
/* Begin PBXFileReference section */
|
||||
19FE70CF6C93F5007940CE91 /* BGMMusic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMMusic.h; path = "Music Players/BGMMusic.h"; sourceTree = "<group>"; };
|
||||
19FE7179EBFA116F3861E79D /* BGMVolumeChangeListener.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMVolumeChangeListener.cpp; sourceTree = "<group>"; };
|
||||
19FE71BCD79E7246F7345C16 /* BGMThreadSafetyAnalysis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMThreadSafetyAnalysis.h; sourceTree = "<group>"; };
|
||||
19FE72A176FD500FB4C1F5C6 /* BGMPlayThroughRTLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMPlayThroughRTLogger.h; sourceTree = "<group>"; };
|
||||
19FE73389459BF65748F531F /* BGMDebugLogging.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = BGMDebugLogging.c; path = PublicUtility/BGMDebugLogging.c; sourceTree = "<group>"; };
|
||||
19FE73822ADD50BA9120AB05 /* BGMMusic.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BGMMusic.m; path = "Music Players/BGMMusic.m"; sourceTree = "<group>"; };
|
||||
@@ -256,6 +259,7 @@
|
||||
19FE799A86A285DD9423D164 /* BGMStatusBarItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMStatusBarItem.h; sourceTree = "<group>"; };
|
||||
19FE7DE5E3BA0046ED2BC3C6 /* BGMPlayThroughRTLogger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGMPlayThroughRTLogger.cpp; sourceTree = "<group>"; };
|
||||
19FE7FDAEBC3F0DB8C99823B /* BGMVolumeChangeListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGMVolumeChangeListener.h; sourceTree = "<group>"; };
|
||||
1C09150723F010FB001EB0E1 /* set-version.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "set-version.sh"; sourceTree = "<group>"; };
|
||||
1C0BD0A31BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMAutoPauseMusicPrefs.h; path = Preferences/BGMAutoPauseMusicPrefs.h; sourceTree = "<group>"; };
|
||||
1C0BD0A41BF1A8E6004F4CF5 /* BGMAutoPauseMusicPrefs.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BGMAutoPauseMusicPrefs.mm; path = Preferences/BGMAutoPauseMusicPrefs.mm; sourceTree = "<group>"; };
|
||||
1C0BD0A61BF1B029004F4CF5 /* BGMPreferencesMenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BGMPreferencesMenu.h; path = Preferences/BGMPreferencesMenu.h; sourceTree = "<group>"; };
|
||||
@@ -323,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>"; };
|
||||
@@ -431,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 */
|
||||
@@ -485,6 +491,15 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
1C09150623F010FB001EB0E1 /* Scripts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1C09150723F010FB001EB0E1 /* set-version.sh */,
|
||||
);
|
||||
name = Scripts;
|
||||
path = ../SharedSource/Scripts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1C0BD0A21BF1A827004F4CF5 /* Preferences Menu */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -538,6 +553,7 @@
|
||||
1C1962E31BC94E15008A4DF7 /* CARingBuffer.h */,
|
||||
19FE7908A33FA7BD97B432D9 /* BGMDebugLogging.h */,
|
||||
19FE73389459BF65748F531F /* BGMDebugLogging.c */,
|
||||
19FE71BCD79E7246F7345C16 /* BGMThreadSafetyAnalysis.h */,
|
||||
);
|
||||
name = PublicUtility;
|
||||
sourceTree = "<group>";
|
||||
@@ -550,6 +566,8 @@
|
||||
1C2FC31D1EC723A100A76592 /* BGMASOutputDevice.h */,
|
||||
1C2FC31A1EC7238A00A76592 /* BGMASOutputDevice.mm */,
|
||||
1C2FC2FF1EB4D6E700A76592 /* BGMApp.sdef */,
|
||||
9E129A3F2602AE620005851B /* BGMASApplication.h */,
|
||||
9E129A402602AE620005851B /* BGMASApplication.m */,
|
||||
);
|
||||
name = Scripting;
|
||||
sourceTree = "<group>";
|
||||
@@ -558,6 +576,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
27D643B41C9FABBD00737F6E /* BGM_Types.h */,
|
||||
1C09150623F010FB001EB0E1 /* Scripts */,
|
||||
2771700F1CA0C83B00AB34B4 /* BGM_Utils.h */,
|
||||
27FB8C2E1DE468320084DB9D /* BGM_Utils.cpp */,
|
||||
27D643C41C9FBE5600737F6E /* BGM_TestUtils.h */,
|
||||
@@ -751,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>";
|
||||
@@ -851,7 +870,7 @@
|
||||
1CB8B3321BBA75EF000E2DD1 /* Sources */,
|
||||
1CB8B3331BBA75EF000E2DD1 /* Frameworks */,
|
||||
1CB8B3341BBA75EF000E2DD1 /* Resources */,
|
||||
1CD440581E593DDD0064E0BC /* ShellScript */,
|
||||
1CD440581E593DDD0064E0BC /* Run Script - set-version.sh */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -887,7 +906,8 @@
|
||||
27379B8B1C7F57DA0084A24C /* Sources */,
|
||||
27379B8C1C7F57DA0084A24C /* Frameworks */,
|
||||
27379B8D1C7F57DA0084A24C /* Resources */,
|
||||
276972891CAFCE91007A2F7C /* ShellScript */,
|
||||
1C09150923F0208F001EB0E1 /* Run Script - set-version.sh */,
|
||||
276972891CAFCE91007A2F7C /* Run Script - post_install.sh */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -971,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,
|
||||
);
|
||||
@@ -1011,7 +1030,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1C62FE5823D4278300B9B68E /* travis-skip.py in Resources */,
|
||||
1C62FE5823D4278300B9B68E /* skip-ui-tests.py in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1042,26 +1061,46 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
1CD440581E593DDD0064E0BC /* ShellScript */ = {
|
||||
1C09150923F0208F001EB0E1 /* Run Script - set-version.sh */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script - set-version.sh";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# Append the git HEAD short ID to the build version for SNAPSHOT and DEBUG builds.\n\"$SRCROOT/../SharedSource/Scripts/set-version.sh\"\n";
|
||||
};
|
||||
1CD440581E593DDD0064E0BC /* Run Script - set-version.sh */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script - set-version.sh";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# Append the git HEAD short ID to the build version for snapshot builds. Thanks to\n# Václav Slavík for the initial version of this: http://stackoverflow.com/a/26354117/1091063\n# TODO: Update CFBundleVersion as well?\n\n# If HEAD isn't tagged, or has \"SNAPSHOT\" or \"DEBUG\" in the tag name, this is a snapshot build.\n# If HEAD is tagged more than once, use the most recent.\nTAG=$(/usr/bin/git tag --points-at HEAD --sort='-taggerdate' 2>/dev/null | head -n 1)\nif [[ $? -eq 0 ]] && ( [[ \"${TAG}\" == \"\" ]] || \\\n [[ \"${TAG}\" =~ .*SNAPSHOT.* ]] || \\\n [[ \"${TAG}\" =~ .*DEBUG.* ]] ); then\n HEAD=$(/usr/bin/git rev-list HEAD --max-count=1 --abbrev-commit)\n INFO_PLIST=\"${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}\"\n if [[ \"${CONFIGURATION}\" != \"Release\" ]]; then\n TYPE=\"DEBUG\"\n else\n TYPE=\"SNAPSHOT\"\n fi\n if [[ -f \"$INFO_PLIST\" ]]; then\n CURRENT_VERSION=$(/usr/libexec/PlistBuddy -c \"Print :CFBundleShortVersionString\" \"${INFO_PLIST}\")\n BASE_VERSION=$(/usr/libexec/PlistBuddy -c \"Print :BGMBundleVersionBase\" \"${INFO_PLIST}\" 2>/dev/null)\n if [[ $? -ne 0 ]] || [[ \"${BASE_VERSION}\" == \"\" ]]; then\n BASE_VERSION=\"${CURRENT_VERSION}\"\n /usr/libexec/PlistBuddy -c \"Add :BGMBundleVersionBase string ${BASE_VERSION}\" \"${INFO_PLIST}\"\n fi\n NEW_VERSION=\"${BASE_VERSION}-${TYPE}-${HEAD}\"\n if [[ \"${NEW_VERSION}\" != \"${CURRENT_VERSION}\" ]]; then # Only touch the file if we need to.\n /usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString ${NEW_VERSION}\" \"${INFO_PLIST}\"\n fi\n fi\nfi\n";
|
||||
shellScript = "# Append the git HEAD short ID to the build version for SNAPSHOT and DEBUG builds.\n\"$SRCROOT/../SharedSource/Scripts/set-version.sh\"\n";
|
||||
};
|
||||
276972891CAFCE91007A2F7C /* ShellScript */ = {
|
||||
276972891CAFCE91007A2F7C /* Run Script - post_install.sh */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 8;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script - post_install.sh";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
@@ -1118,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 */,
|
||||
@@ -1184,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 */,
|
||||
@@ -1358,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;
|
||||
@@ -1401,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";
|
||||
@@ -1416,6 +1458,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_USE_OPTIMIZATION_PROFILE = NO;
|
||||
CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "BGMApp/BGMApp-Debug.entitlements";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
@@ -1428,7 +1471,10 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.App;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRIP_STYLE = "non-global";
|
||||
WARNING_CFLAGS = "-Wpartial-availability";
|
||||
WARNING_CFLAGS = (
|
||||
"-Wpartial-availability",
|
||||
"-Wthread-safety",
|
||||
);
|
||||
};
|
||||
name = DebugOpt;
|
||||
};
|
||||
@@ -1463,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;
|
||||
@@ -1506,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";
|
||||
@@ -1547,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;
|
||||
@@ -1587,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;
|
||||
@@ -1601,6 +1649,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_USE_OPTIMIZATION_PROFILE = NO;
|
||||
CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "BGMApp/BGMApp-Debug.entitlements";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
@@ -1613,7 +1662,10 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.bearisdriving.BGM.App;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRIP_STYLE = "non-global";
|
||||
WARNING_CFLAGS = "-Wpartial-availability";
|
||||
WARNING_CFLAGS = (
|
||||
"-Wpartial-availability",
|
||||
"-Wthread-safety",
|
||||
);
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -1622,6 +1674,7 @@
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_USE_OPTIMIZATION_PROFILE = NO;
|
||||
CLANG_WARN_ATOMIC_IMPLICIT_SEQ_CST = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = BGMApp/BGMApp.entitlements;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
@@ -1647,6 +1700,7 @@
|
||||
WARNING_CFLAGS = (
|
||||
"-Wno-profile-instr-out-of-date",
|
||||
"-Wpartial-availability",
|
||||
"-Wthread-safety",
|
||||
);
|
||||
};
|
||||
name = Release;
|
||||
@@ -1661,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 = "";
|
||||
@@ -1679,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 = "";
|
||||
@@ -1698,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 = "";
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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)) ||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "BGM_Utils.h"
|
||||
|
||||
// PublicUtility Includes
|
||||
#include "CAAtomic.h"
|
||||
#include "CAHALAudioSystemObject.h"
|
||||
#include "CAPropertyAddress.h"
|
||||
|
||||
@@ -57,9 +56,20 @@ BGMPlayThrough::BGMPlayThrough(BGMAudioDevice inInputDevice, BGMAudioDevice inOu
|
||||
|
||||
BGMPlayThrough::~BGMPlayThrough()
|
||||
{
|
||||
CAMutex::Locker stateLocker(mStateMutex);
|
||||
|
||||
BGMLogAndSwallowExceptionsMsg("BGMPlayThrough::~BGMPlayThrough", "Deactivate", [&]() {
|
||||
Deactivate();
|
||||
});
|
||||
|
||||
// If one of the IOProcs failed to stop, CoreAudio could (at least in theory) still call it
|
||||
// after this point. This isn't a solution, but calling DeallocateBuffer instead of letting it
|
||||
// deallocate itself should at least make the error less likely to cause a segfault, since
|
||||
// DeallocateBuffer takes the buffer locks and sets mBuffer to null.
|
||||
//
|
||||
// TODO: It probably wouldn't be too hard to fix this properly by giving the IOProcs weak refs
|
||||
// to the BGMPlayThrough object instead of raw pointers.
|
||||
DeallocateBuffer();
|
||||
|
||||
if(mOutputDeviceIOProcSemaphore != SEMAPHORE_NULL)
|
||||
{
|
||||
@@ -97,7 +107,7 @@ void BGMPlayThrough::Init(BGMAudioDevice inInputDevice, BGMAudioDevice inOutp
|
||||
catch (...)
|
||||
{
|
||||
// Clean up.
|
||||
mBuffer.Deallocate();
|
||||
DeallocateBuffer();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -178,7 +188,7 @@ void BGMPlayThrough::Deactivate()
|
||||
DebugMsg("BGMPlayThrough::Deactivate: Deactivating playthrough");
|
||||
|
||||
bool inputDeviceIsBGMDevice = true;
|
||||
|
||||
|
||||
CATry
|
||||
inputDeviceIsBGMDevice = mInputDevice.IsBGMDeviceInstance();
|
||||
CACatch
|
||||
@@ -231,13 +241,29 @@ void BGMPlayThrough::AllocateBuffer()
|
||||
Throw(CAException(kAudioHardwareUnsupportedOperationError));
|
||||
}
|
||||
|
||||
// Need to lock the buffer mutexes to make sure the IOProcs aren't accessing it. The order is
|
||||
// important here. We always lock them in the same order to prevent deadlocks.
|
||||
CAMutex::Locker lockerInput(mBufferInputMutex);
|
||||
CAMutex::Locker lockerOutput(mBufferOutputMutex);
|
||||
|
||||
mBuffer = std::unique_ptr<CARingBuffer>(new CARingBuffer);
|
||||
|
||||
// The calculation for the size of the buffer is from Apple's CAPlayThrough.cpp sample code
|
||||
//
|
||||
// TODO: Test playthrough with hardware with more than 2 channels per frame, a sample (virtual) format other than
|
||||
// 32-bit floats and/or an IO buffer size other than 512 frames
|
||||
mBuffer.Allocate(outputFormat[0].mChannelsPerFrame,
|
||||
outputFormat[0].mBytesPerFrame,
|
||||
mOutputDevice.GetIOBufferSize() * 20);
|
||||
mBuffer->Allocate(outputFormat[0].mChannelsPerFrame,
|
||||
outputFormat[0].mBytesPerFrame,
|
||||
mOutputDevice.GetIOBufferSize() * 20);
|
||||
}
|
||||
|
||||
void BGMPlayThrough::DeallocateBuffer()
|
||||
{
|
||||
// Need to lock the buffer mutexes to make sure the IOProcs aren't accessing it. The order is
|
||||
// important here. We always lock them in the same order to prevent deadlocks.
|
||||
CAMutex::Locker lockerInput(mBufferInputMutex);
|
||||
CAMutex::Locker lockerOutput(mBufferOutputMutex);
|
||||
mBuffer = nullptr; // Note that the buffer's destructor will deallocate it.
|
||||
}
|
||||
|
||||
void BGMPlayThrough::CreateIOProcIDs()
|
||||
@@ -865,6 +891,10 @@ void BGMPlayThrough::HandleBGMDeviceIsRunning(BGMPlayThrough* refCon)
|
||||
isRunningSomewhereOtherThanBGMApp =
|
||||
IsRunningSomewhereOtherThanBGMApp(refCon->mInputDevice);
|
||||
});
|
||||
|
||||
DebugMsg("BGMPlayThrough::HandleBGMDeviceIsRunning: "
|
||||
"BGMDevice is %srunning somewhere other than BGMApp",
|
||||
isRunningSomewhereOtherThanBGMApp ? "" : "not ");
|
||||
|
||||
if(isRunningSomewhereOtherThanBGMApp)
|
||||
{
|
||||
@@ -903,15 +933,14 @@ void BGMPlayThrough::HandleBGMDeviceIsRunningSomewhereOtherThanBGMApp(BGMPlay
|
||||
// static
|
||||
bool BGMPlayThrough::IsRunningSomewhereOtherThanBGMApp(const BGMAudioDevice& inBGMDevice)
|
||||
{
|
||||
return CFBooleanGetValue(
|
||||
static_cast<CFBooleanRef>(
|
||||
inBGMDevice.GetPropertyData_CFType(kBGMRunningSomewhereOtherThanBGMAppAddress)));
|
||||
auto type = inBGMDevice.GetPropertyData_CFType(kBGMRunningSomewhereOtherThanBGMAppAddress);
|
||||
return type && CFBooleanGetValue(static_cast<CFBooleanRef>(type));
|
||||
}
|
||||
|
||||
#pragma mark IOProcs
|
||||
|
||||
// Note that the IOProcs will very likely not run on the same thread and that they intentionally don't
|
||||
// lock any mutexes.
|
||||
// Note that the IOProcs will very likely not run on the same thread and that they intentionally
|
||||
// only lock mutexes around their use of mBuffer.
|
||||
|
||||
// static
|
||||
OSStatus BGMPlayThrough::InputDeviceIOProc(AudioObjectID inDevice,
|
||||
@@ -950,15 +979,30 @@ OSStatus BGMPlayThrough::InputDeviceIOProc(AudioObjectID inDevice,
|
||||
|
||||
UInt32 framesToStore = inInputData->mBuffers[0].mDataByteSize / (SizeOf32(Float32) * 2);
|
||||
|
||||
CARingBufferError err =
|
||||
refCon->mBuffer.Store(inInputData,
|
||||
framesToStore,
|
||||
static_cast<CARingBuffer::SampleTime>(inInputTime->mSampleTime));
|
||||
refCon->mRTLogger.LogIfRingBufferError_Store(err);
|
||||
// See the comments in OutputDeviceIOProc where it locks mBufferOutputMutex.
|
||||
CAMutex::Tryer tryer(refCon->mBufferInputMutex);
|
||||
|
||||
// Disable a warning about accessing mBuffer without holding both mBufferInputMutex and
|
||||
// mBufferOutputMutex. Explained further in OutputDeviceIOProc.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wthread-safety"
|
||||
if(tryer.HasLock() && refCon->mBuffer)
|
||||
{
|
||||
CARingBufferError err =
|
||||
refCon->mBuffer->Store(inInputData,
|
||||
framesToStore,
|
||||
static_cast<CARingBuffer::SampleTime>(
|
||||
inInputTime->mSampleTime));
|
||||
#pragma clang diagnostic pop
|
||||
refCon->mRTLogger.LogIfRingBufferError_Store(err);
|
||||
|
||||
refCon->mLastInputSampleTime = inInputTime->mSampleTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
refCon->mRTLogger.LogRingBufferUnavailable("InputDeviceIOProc", tryer.HasLock());
|
||||
}
|
||||
|
||||
CAMemoryBarrier();
|
||||
refCon->mLastInputSampleTime = inInputTime->mSampleTime;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
@@ -987,6 +1031,7 @@ OSStatus BGMPlayThrough::OutputDeviceIOProc(AudioObjectID inDevice,
|
||||
if(state == IOState::Stopped || state == IOState::Stopping)
|
||||
{
|
||||
// Return early, since we just asked to stop. (Or something really weird is going on.)
|
||||
FillWithSilence(outOutputData);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
@@ -1006,8 +1051,7 @@ OSStatus BGMPlayThrough::OutputDeviceIOProc(AudioObjectID inDevice,
|
||||
if(refCon->mLastInputSampleTime == -1)
|
||||
{
|
||||
// Return early, since we don't have any data to output yet.
|
||||
//
|
||||
// TODO: Write silence to outOutputData here
|
||||
FillWithSilence(outOutputData);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
@@ -1028,47 +1072,90 @@ OSStatus BGMPlayThrough::OutputDeviceIOProc(AudioObjectID inDevice,
|
||||
static_cast<CARingBuffer::SampleTime>(refCon->mLastInputSampleTime);
|
||||
|
||||
UInt32 framesToOutput = outOutputData->mBuffers[0].mDataByteSize / (SizeOf32(Float32) * 2);
|
||||
|
||||
// Very occasionally (at least for me) our read head gets ahead of input, i.e. we haven't received any new input since
|
||||
// this IOProc was last called, and we have to recalculate its position. I figure this might be caused by clock drift
|
||||
// but I'm really not sure. It also happens if the input or output sample times are restarted from zero.
|
||||
//
|
||||
// We also recalculate the offset if the read head is outside of the ring buffer. This happens for example when you plug
|
||||
// in or unplug headphones, which causes the output sample times to be restarted from zero.
|
||||
//
|
||||
// The vast majority of the time, just using lastInputSampleTime as the read head time instead of the one we calculate
|
||||
// would work fine (and would also account for the above).
|
||||
SInt64 bufferStartTime, bufferEndTime;
|
||||
CARingBufferError err = refCon->mBuffer.GetTimeBounds(bufferStartTime, bufferEndTime);
|
||||
bool outOfBounds = false;
|
||||
if(err == kCARingBufferError_OK)
|
||||
{
|
||||
outOfBounds = (readHeadSampleTime < bufferStartTime) || (readHeadSampleTime - framesToOutput > bufferEndTime);
|
||||
}
|
||||
if(lastInputSampleTime < readHeadSampleTime || outOfBounds)
|
||||
{
|
||||
refCon->mRTLogger.LogNoSamplesReady(lastInputSampleTime,
|
||||
readHeadSampleTime,
|
||||
refCon->mInToOutSampleOffset);
|
||||
|
||||
// Recalculate the in-to-out offset and read head
|
||||
refCon->mInToOutSampleOffset = inOutputTime->mSampleTime - lastInputSampleTime;
|
||||
readHeadSampleTime = static_cast<CARingBuffer::SampleTime>(inOutputTime->mSampleTime - refCon->mInToOutSampleOffset);
|
||||
}
|
||||
// When the input and output devices are set, during start up or because the user changed the
|
||||
// output device, this class (re)allocates the ring buffer (mBuffer). We try to take this
|
||||
// lock before accessing the buffer to make sure it's allocated.
|
||||
//
|
||||
// If we don't get the lock, another thread must be allocating or deallocating it, so we just
|
||||
// give up. We can't avoid audio glitches while changing devices anyway. This class tries to
|
||||
// make sure the IOProcs aren't running when it allocates the buffer, but it can't guarantee
|
||||
// that.
|
||||
//
|
||||
// Note that this is only realtime safe because we only try to lock the mutex. If another
|
||||
// thread has the mutex, it will be a non-realtime thread, so we can't wait for it.
|
||||
CAMutex::Tryer tryer(refCon->mBufferOutputMutex);
|
||||
|
||||
// Copy the frames from the ring buffer
|
||||
err = refCon->mBuffer.Fetch(outOutputData, framesToOutput, readHeadSampleTime);
|
||||
// TODO: Not sure what else we should do to handle these errors, if anything. There's code in
|
||||
// Apple's CAPlayThrough.cpp sample code that handles them. (Look for
|
||||
// "kCARingBufferError_OK" around line 707.) Should be easy enough to use, but it's more
|
||||
// complicated that just directly copying it.
|
||||
refCon->mRTLogger.LogIfRingBufferError_Fetch(err);
|
||||
// Disable a warning about accessing mBuffer without holding both mBufferInputMutex and
|
||||
// mBufferOutputMutex. The input IOProc always writes ahead of where the output IOProc will read
|
||||
// in a given IO cycle, so it's safe for them to read and write at the same time.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wthread-safety"
|
||||
if(tryer.HasLock() && refCon->mBuffer)
|
||||
{
|
||||
// Very occasionally (at least for me) our read head gets ahead of input, i.e. we haven't
|
||||
// received any new input since this IOProc was last called, and we have to recalculate its
|
||||
// position. I figure this might be caused by clock drift but I'm really not sure. It also
|
||||
// happens if the input or output sample times are restarted from zero.
|
||||
//
|
||||
// We also recalculate the offset if the read head is outside of the ring buffer. This
|
||||
// happens for example when you plug in or unplug headphones, which causes the output sample
|
||||
// times to be restarted from zero.
|
||||
//
|
||||
// The vast majority of the time, just using lastInputSampleTime as the read head time
|
||||
// instead of the one we calculate would work fine (and would also account for the above).
|
||||
SInt64 bufferStartTime, bufferEndTime;
|
||||
CARingBufferError err = refCon->mBuffer->GetTimeBounds(bufferStartTime, bufferEndTime);
|
||||
bool outOfBounds = false;
|
||||
|
||||
if(err == kCARingBufferError_OK)
|
||||
{
|
||||
outOfBounds = (readHeadSampleTime < bufferStartTime)
|
||||
|| (readHeadSampleTime - framesToOutput > bufferEndTime);
|
||||
}
|
||||
|
||||
if(lastInputSampleTime < readHeadSampleTime || outOfBounds)
|
||||
{
|
||||
refCon->mRTLogger.LogNoSamplesReady(lastInputSampleTime,
|
||||
readHeadSampleTime,
|
||||
refCon->mInToOutSampleOffset);
|
||||
|
||||
// Recalculate the in-to-out offset and read head.
|
||||
refCon->mInToOutSampleOffset = inOutputTime->mSampleTime - lastInputSampleTime;
|
||||
readHeadSampleTime = static_cast<CARingBuffer::SampleTime>(
|
||||
inOutputTime->mSampleTime - refCon->mInToOutSampleOffset);
|
||||
}
|
||||
|
||||
// Copy the frames from the ring buffer.
|
||||
err = refCon->mBuffer->Fetch(outOutputData, framesToOutput, readHeadSampleTime);
|
||||
refCon->mRTLogger.LogIfRingBufferError_Fetch(err);
|
||||
|
||||
if(err != kCARingBufferError_OK)
|
||||
{
|
||||
FillWithSilence(outOutputData);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
refCon->mRTLogger.LogRingBufferUnavailable("OutputDeviceIOProc", tryer.HasLock());
|
||||
FillWithSilence(outOutputData);
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
refCon->mLastOutputSampleTime = inOutputTime->mSampleTime;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
// static
|
||||
inline void BGMPlayThrough::FillWithSilence(AudioBufferList* ioBuffer)
|
||||
{
|
||||
for(UInt32 i = 0; i < ioBuffer->mNumberBuffers; i++)
|
||||
{
|
||||
memset(ioBuffer->mBuffers[i].mData, 0, ioBuffer->mBuffers[i].mDataByteSize);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
bool BGMPlayThrough::UpdateIOProcState(const char* inCallerName,
|
||||
BGMPlayThroughRTLogger& inRTLogger,
|
||||
|
||||
@@ -46,10 +46,12 @@
|
||||
// PublicUtility Includes
|
||||
#include "CAMutex.h"
|
||||
#include "CARingBuffer.h"
|
||||
#include "BGMThreadSafetyAnalysis.h"
|
||||
|
||||
// STL Includes
|
||||
#include <atomic>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
// System Includes
|
||||
#include <mach/semaphore.h>
|
||||
@@ -79,7 +81,8 @@ public:
|
||||
|
||||
private:
|
||||
/*! @throws CAException */
|
||||
void Init(BGMAudioDevice inInputDevice, BGMAudioDevice inOutputDevice);
|
||||
void Init(BGMAudioDevice inInputDevice, BGMAudioDevice inOutputDevice)
|
||||
REQUIRES(mStateMutex);
|
||||
|
||||
public:
|
||||
/*! @throws CAException */
|
||||
@@ -88,7 +91,8 @@ public:
|
||||
void Deactivate();
|
||||
|
||||
private:
|
||||
void AllocateBuffer();
|
||||
void AllocateBuffer() REQUIRES(mStateMutex);
|
||||
void DeallocateBuffer();
|
||||
|
||||
/*! @throws CAException */
|
||||
void CreateIOProcIDs();
|
||||
@@ -98,7 +102,7 @@ private:
|
||||
@return True if both IOProcs are stopped.
|
||||
@nonthreadsafe
|
||||
*/
|
||||
bool CheckIOProcsAreStopped() const noexcept; // TODO: REQUIRES(mStateMutex);
|
||||
bool CheckIOProcsAreStopped() const noexcept REQUIRES(mStateMutex);
|
||||
|
||||
public:
|
||||
/*!
|
||||
@@ -148,7 +152,10 @@ private:
|
||||
AudioBufferList* outOutputData,
|
||||
const AudioTimeStamp* inOutputTime,
|
||||
void* __nullable inClientData);
|
||||
|
||||
|
||||
/*! Fills the given ABL with zeroes to make it silent. */
|
||||
static inline void FillWithSilence(AudioBufferList* ioBuffer);
|
||||
|
||||
// The state of an IOProc. Used by the IOProc to tell other threads when it's finished starting. Used by other
|
||||
// threads to tell the IOProc to stop itself. (Probably used for other things as well.)
|
||||
enum class IOState
|
||||
@@ -166,16 +173,37 @@ private:
|
||||
IOState& outNewState);
|
||||
|
||||
private:
|
||||
CARingBuffer mBuffer;
|
||||
std::unique_ptr<CARingBuffer> mBuffer PT_GUARDED_BY(mBufferInputMutex)
|
||||
PT_GUARDED_BY(mBufferOutputMutex) { nullptr };
|
||||
|
||||
AudioDeviceIOProcID __nullable mInputDeviceIOProcID { nullptr };
|
||||
AudioDeviceIOProcID __nullable mOutputDeviceIOProcID { nullptr };
|
||||
|
||||
BGMAudioDevice mInputDevice { kAudioObjectUnknown };
|
||||
BGMAudioDevice mOutputDevice { kAudioObjectUnknown };
|
||||
|
||||
CAMutex mStateMutex { "Playthrough state" };
|
||||
|
||||
|
||||
// mStateMutex is the general purpose mutex. mBufferInputMutex and mBufferOutputMutex are
|
||||
// just used to make sure mBuffer, the ring buffer, is allocated when the IOProcs access it. See
|
||||
// the comments in the IOProcs for details.
|
||||
//
|
||||
// If a thread might lock more than one of these mutexes, it *must* take them in this order:
|
||||
// 1. mStateMutex
|
||||
// 2. mBufferInputMutex
|
||||
// 3. mBufferOutputMutex
|
||||
//
|
||||
// The ACQUIRED_BEFORE annotations don't do anything yet. From clang's docs: "ACQUIRED_BEFORE(…)
|
||||
// and ACQUIRED_AFTER(…) are currently unimplemented. To be fixed in a future update." After
|
||||
// they've fixed that, the compiler will enforce the ordering statically.
|
||||
//
|
||||
// TODO: We can't use std::shared_lock because we're still on C++11, but we could use std::lock
|
||||
// to help ensure the locks are always taken in the right order.
|
||||
// TODO: It would be better to have a separate class for the buffer and its mutexes.
|
||||
CAMutex mStateMutex ACQUIRED_BEFORE(mBufferInputMutex)
|
||||
ACQUIRED_BEFORE(mBufferOutputMutex) { "Playthrough state" };
|
||||
CAMutex mBufferInputMutex ACQUIRED_BEFORE(mBufferOutputMutex)
|
||||
{ "Playthrough ring buffer input" };
|
||||
CAMutex mBufferOutputMutex { "Playthrough ring buffer output" };
|
||||
|
||||
// Signalled when the output IOProc runs. We use it to tell BGMDriver when the output device is ready to receive audio data.
|
||||
semaphore_t mOutputDeviceIOProcSemaphore { SEMAPHORE_NULL };
|
||||
|
||||
|
||||
@@ -214,6 +214,16 @@ void BGMPlayThroughRTLogger::LogUnexpectedIOStateAfterStopping(const char* inCal
|
||||
});
|
||||
}
|
||||
|
||||
void BGMPlayThroughRTLogger::LogRingBufferUnavailable(const char* inCallerName, bool inGotLock)
|
||||
{
|
||||
LogAsync(mRingBufferUnavailable, [&]()
|
||||
{
|
||||
// Store the data to include in the log message.
|
||||
mRingBufferUnavailable.callerName = inCallerName;
|
||||
mRingBufferUnavailable.gotLock = inGotLock;
|
||||
});
|
||||
}
|
||||
|
||||
void BGMPlayThroughRTLogger::LogIfRingBufferError(CARingBufferError inError,
|
||||
std::atomic<CARingBufferError>& outError)
|
||||
{
|
||||
@@ -276,7 +286,7 @@ void BGMPlayThroughRTLogger::LogSync_Warning(const char* inFormat, ...)
|
||||
mNumWarningMessagesLogged++;
|
||||
#endif
|
||||
|
||||
LogWarning(inFormat, args);
|
||||
vLogWarning(inFormat, args);
|
||||
|
||||
va_end(args);
|
||||
}
|
||||
@@ -291,10 +301,10 @@ void BGMPlayThroughRTLogger::LogSync_Error(const char* inFormat, ...)
|
||||
|
||||
if(!mContinueOnErrorLogged)
|
||||
{
|
||||
LogError(inFormat, args);
|
||||
vLogError(inFormat, args);
|
||||
}
|
||||
#else
|
||||
LogError(inFormat, args);
|
||||
vLogError(inFormat, args);
|
||||
#endif
|
||||
|
||||
va_end(args);
|
||||
@@ -321,6 +331,7 @@ void BGMPlayThroughRTLogger::LogMessages()
|
||||
LogSync_NoSamplesReady();
|
||||
LogSync_ExceptionStoppingIOProc();
|
||||
LogSync_UnexpectedIOStateAfterStopping();
|
||||
LogSync_RingBufferUnavailable();
|
||||
LogSync_RingBufferError(mRingBufferStoreError, "InputDeviceIOProc");
|
||||
LogSync_RingBufferError(mRingBufferFetchError, "OutputDeviceIOProc");
|
||||
}
|
||||
@@ -402,6 +413,19 @@ void BGMPlayThroughRTLogger::LogSync_UnexpectedIOStateAfterStopping()
|
||||
}
|
||||
}
|
||||
|
||||
void BGMPlayThroughRTLogger::LogSync_RingBufferUnavailable()
|
||||
{
|
||||
if(mRingBufferUnavailable.shouldLogMessage)
|
||||
{
|
||||
LogSync_Warning("BGMPlayThrough::%s: Ring buffer unavailable. %s",
|
||||
mRingBufferUnavailable.callerName,
|
||||
mRingBufferUnavailable.gotLock ?
|
||||
"No buffer currently allocated." :
|
||||
"Buffer locked for allocation/deallocation by another thread.");
|
||||
mRingBufferUnavailable.shouldLogMessage = false;
|
||||
}
|
||||
}
|
||||
|
||||
void BGMPlayThroughRTLogger::LogSync_RingBufferError(
|
||||
std::atomic<CARingBufferError>& ioRingBufferError,
|
||||
const char* inMethodName)
|
||||
@@ -471,6 +495,7 @@ bool BGMPlayThroughRTLogger::WaitUntilLoggerThreadIdle()
|
||||
mDroppedFrames.shouldLogMessage ||
|
||||
mNoSamplesReady.shouldLogMessage ||
|
||||
mUnexpectedIOStateAfterStopping.shouldLogMessage ||
|
||||
mRingBufferUnavailable.shouldLogMessage ||
|
||||
mExceptionStoppingIOProc.shouldLogMessage ||
|
||||
mRingBufferStoreError != kCARingBufferError_OK ||
|
||||
mRingBufferFetchError != kCARingBufferError_OK)
|
||||
|
||||
@@ -99,7 +99,8 @@ public:
|
||||
/*! For BGMPlayThrough::UpdateIOProcState. Not thread-safe. */
|
||||
void LogUnexpectedIOStateAfterStopping(const char* inCallerName,
|
||||
int inIOState);
|
||||
|
||||
/*! For BGMPlayThrough::InputDeviceIOProc and BGMPlayThrough::OutputDeviceIOProc. */
|
||||
void LogRingBufferUnavailable(const char* inCallerName, bool inGotLock);
|
||||
/*! For BGMPlayThrough::OutputDeviceIOProc. */
|
||||
void LogIfRingBufferError_Fetch(CARingBufferError inError)
|
||||
{
|
||||
@@ -134,6 +135,7 @@ private:
|
||||
void LogSync_NoSamplesReady();
|
||||
void LogSync_ExceptionStoppingIOProc();
|
||||
void LogSync_UnexpectedIOStateAfterStopping();
|
||||
void LogSync_RingBufferUnavailable();
|
||||
void LogSync_RingBufferError(
|
||||
std::atomic<CARingBufferError>& ioRingBufferError,
|
||||
const char* inMethodName);
|
||||
@@ -173,6 +175,12 @@ private:
|
||||
std::atomic<bool> shouldLogMessage { false };
|
||||
} mNoSamplesReady;
|
||||
|
||||
struct {
|
||||
const char* callerName;
|
||||
bool gotLock;
|
||||
std::atomic<bool> shouldLogMessage { false };
|
||||
} mRingBufferUnavailable;
|
||||
|
||||
// For BGMPlayThrough::UpdateIOProcState
|
||||
struct {
|
||||
const char* callerName;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
@@ -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([] {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -46,5 +46,11 @@
|
||||
</array>
|
||||
<key>OSAScriptingDefinition</key>
|
||||
<string>BGMApp.sdef</string>
|
||||
<!--
|
||||
Hopefully this will keep Background Music from using the discrete GPU on multi-GPU systems,
|
||||
which wastes energy. I don't have any hardware I could easily test this on, so it's untested.
|
||||
-->
|
||||
<key>NSSupportsAutomaticGraphicsSwitching</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -92,7 +92,11 @@
|
||||
|
||||
+ (NSArray<id<BGMMusicPlayer>>*) createInstancesWithDefaults:(BGMUserDefaults*)userDefaults {
|
||||
#pragma unused (userDefaults)
|
||||
// TODO: Fix this warning properly.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wobjc-literal-conversion"
|
||||
return @[ [self new] ];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
- (NSImage* __nullable) icon {
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
- (void) initSelectedMusicPlayerFromBGMDevice {
|
||||
// When the selected music player setting hasn't been stored in user defaults yet, we get the music player
|
||||
// bundle ID from the driver and look for the music player with that bundle ID. This is mainly done for
|
||||
// backwards compatability.
|
||||
// backwards compatibility.
|
||||
|
||||
NSString* __nullable bundleID =
|
||||
(__bridge_transfer NSString* __nullable)[audioDevices bgmDevice].GetMusicPlayerBundleID();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 */;
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
Binary file not shown.
Binary file not shown.
@@ -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
@@ -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.
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
+4
-13
@@ -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)
|
||||
|
||||
|
||||
@@ -98,6 +98,11 @@
|
||||
[self assertLoggedOneWarningMessage];
|
||||
}
|
||||
|
||||
- (void) testLogRingBufferUnavailable {
|
||||
logger->LogRingBufferUnavailable("OutputDeviceIOProc", false);
|
||||
[self assertLoggedOneWarningMessage];
|
||||
}
|
||||
|
||||
- (void) testLogIfRingBufferError_Fetch_noError {
|
||||
logger->LogIfRingBufferError_Fetch(kCARingBufferError_OK);
|
||||
[self assertLoggedNoMessages];
|
||||
|
||||
@@ -0,0 +1,383 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Original licence at the end of this file.
|
||||
|
||||
//
|
||||
// BGMThreadSafetyAnalysis.h
|
||||
// PublicUtility
|
||||
//
|
||||
// © Copyright 2007-2020, The Clang Team
|
||||
// Copyright © 2020 Kyle Neideck
|
||||
//
|
||||
// Macros that wrap Clang's attributes for statically checking concurrency properties. From
|
||||
// <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#mutexheader>.
|
||||
//
|
||||
|
||||
|
||||
#ifndef PublicUtility__BGMThreadSafetyAnalysis
|
||||
#define PublicUtility__BGMThreadSafetyAnalysis
|
||||
|
||||
// Enable thread safety attributes only with clang.
|
||||
// The attributes can be safely erased when compiling with other compilers.
|
||||
#if defined(__clang__) && (!defined(SWIG))
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
|
||||
#else
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
|
||||
#endif
|
||||
|
||||
#define CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(capability(x))
|
||||
|
||||
#define SCOPED_CAPABILITY \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
||||
|
||||
#define GUARDED_BY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
||||
|
||||
#define PT_GUARDED_BY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
|
||||
|
||||
#define ACQUIRED_BEFORE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRED_AFTER(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
|
||||
|
||||
#define REQUIRES(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))
|
||||
|
||||
#define REQUIRES_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))
|
||||
|
||||
#define ACQUIRE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define RELEASE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
|
||||
|
||||
#define RELEASE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define TRY_ACQUIRE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))
|
||||
|
||||
#define TRY_ACQUIRE_SHARED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))
|
||||
|
||||
#define EXCLUDES(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
|
||||
|
||||
#define ASSERT_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
|
||||
|
||||
#define ASSERT_SHARED_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
|
||||
|
||||
#define RETURN_CAPABILITY(x) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
||||
|
||||
#define NO_THREAD_SAFETY_ANALYSIS \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||
|
||||
#endif /* PublicUtility__BGMThreadSafetyAnalysis */
|
||||
|
||||
/*
|
||||
This file is derived from "mutex.h" from the Clang documentation at
|
||||
<https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>. This is the original license of "mutex.h".
|
||||
|
||||
==============================================================================
|
||||
The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
|
||||
==============================================================================
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
---- LLVM Exceptions to the Apache 2.0 License ----
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into an Object form of such source code, you
|
||||
may redistribute such embedded portions in such Object form without complying
|
||||
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
|
||||
|
||||
In addition, if you combine or link compiled forms of this Software with
|
||||
software that is licensed under the GPLv2 ("Combined Software") and if a
|
||||
court of competent jurisdiction determines that the patent provision (Section
|
||||
3), the indemnity provision (Section 9) or other Section of the License
|
||||
conflicts with the conditions of the GPLv2, you may retroactively and
|
||||
prospectively choose to deem waived or otherwise exclude such Section(s) of
|
||||
the License, but only in their entirety and only with respect to the Combined
|
||||
Software.
|
||||
|
||||
==============================================================================
|
||||
Software from third parties included in the LLVM Project:
|
||||
==============================================================================
|
||||
The LLVM Project contains third party software which is under different license
|
||||
terms. All such code will be identified clearly using at least one of two
|
||||
mechanisms:
|
||||
1) It will be in a separate directory tree with its own `LICENSE.txt` or
|
||||
`LICENSE` file at the top containing the specific license and restrictions
|
||||
which apply to that software, or
|
||||
2) It will contain specific license and restriction terms at the top of every
|
||||
file.
|
||||
|
||||
==============================================================================
|
||||
Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
|
||||
==============================================================================
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2007-2019 University of Illinois at Urbana-Champaign.
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
LLVM Team
|
||||
|
||||
University of Illinois at Urbana-Champaign
|
||||
|
||||
http://llvm.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal with
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the LLVM Team, University of Illinois at
|
||||
Urbana-Champaign, nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this Software without specific
|
||||
prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -1,3 +1,28 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// CADebugMacros.cpp
|
||||
// PublicUtility
|
||||
//
|
||||
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
// Copyright © 2016, 2017, 2020 Kyle Neideck
|
||||
//
|
||||
// Original license header follows.
|
||||
//
|
||||
|
||||
/*
|
||||
File: CADebugMacros.cpp
|
||||
Abstract: CADebugMacros.h
|
||||
@@ -46,7 +71,6 @@
|
||||
*/
|
||||
#include "CADebugMacros.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#if TARGET_API_MAC_OSX
|
||||
#include <syslog.h>
|
||||
#endif
|
||||
@@ -67,14 +91,12 @@ void LogError(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
// BGM edit: vprintf leaves args in an undefined state, which can cause a crash in
|
||||
// vsyslog. Also added CADebuggerStop(). Original code commented out below.
|
||||
//#if DEBUG
|
||||
// vprintf(fmt, args);
|
||||
//#endif
|
||||
//#if TARGET_API_MAC_OSX
|
||||
// vsyslog(LOG_ERR, fmt, args);
|
||||
//#endif
|
||||
vLogError(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void vLogError(const char *fmt, va_list args)
|
||||
{
|
||||
#if (DEBUG || !TARGET_API_MAC_OSX) && !CoreAudio_UseSysLog
|
||||
printf("[ERROR] ");
|
||||
vprintf(fmt, args);
|
||||
@@ -82,25 +104,22 @@ void LogError(const char *fmt, ...)
|
||||
#else
|
||||
vsyslog(LOG_ERR, fmt, args);
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
CADebuggerStop();
|
||||
#endif
|
||||
// BGM edit end
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void LogWarning(const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
// BGM edit: vprintf leaves args in an undefined state, which can cause a crash in
|
||||
// vsyslog. Also added CADebuggerStop(). Original code commented out below.
|
||||
//#if DEBUG
|
||||
// vprintf(fmt, args);
|
||||
//#endif
|
||||
//#if TARGET_API_MAC_OSX
|
||||
// vsyslog(LOG_WARNING, fmt, args);
|
||||
//#endif
|
||||
vLogWarning(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void vLogWarning(const char *fmt, va_list args)
|
||||
{
|
||||
#if (DEBUG || !TARGET_API_MAC_OSX) && !CoreAudio_UseSysLog
|
||||
printf("[WARNING] ");
|
||||
vprintf(fmt, args);
|
||||
@@ -108,9 +127,8 @@ void LogWarning(const char *fmt, ...)
|
||||
#else
|
||||
vsyslog(LOG_WARNING, fmt, args);
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
//CADebuggerStop(); // TODO: Add a toggle for this to the project file (under "Preprocessor Macros"). Default to off.
|
||||
#endif
|
||||
// BGM edit end
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,8 @@
|
||||
|
||||
#include "CADebugPrintf.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
//=============================================================================
|
||||
// CADebugMacros
|
||||
//=============================================================================
|
||||
@@ -220,9 +222,11 @@
|
||||
#define DebugMessageN8(msg, N1, N2, N3, N4, N5, N6, N7, N8) DebugMsg(msg, N1, N2, N3, N4, N5, N6, N7, N8)
|
||||
#define DebugMessageN9(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9) DebugMsg(msg, N1, N2, N3, N4, N5, N6, N7, N8, N9)
|
||||
|
||||
// BGM edit: Added __printflike.
|
||||
// BGM edit: Added __printflike and va_list versions.
|
||||
void LogError(const char *fmt, ...) __printflike(1, 2); // writes to syslog (and stderr if debugging)
|
||||
void vLogError(const char *fmt, va_list args);
|
||||
void LogWarning(const char *fmt, ...) __printflike(1, 2); // writes to syslog (and stderr if debugging)
|
||||
void vLogWarning(const char *fmt, va_list args);
|
||||
|
||||
#define NO_ACTION (void)0
|
||||
|
||||
|
||||
@@ -1,3 +1,28 @@
|
||||
// This file is part of Background Music.
|
||||
//
|
||||
// Background Music is free software: you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation, either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Background Music is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Background Music. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//
|
||||
// CAMutex.h
|
||||
// PublicUtility
|
||||
//
|
||||
// Copyright (C) 2014 Apple Inc. All Rights Reserved.
|
||||
// Copyright © 2020 Kyle Neideck
|
||||
//
|
||||
// Original license header follows.
|
||||
//
|
||||
|
||||
/*
|
||||
File: CAMutex.h
|
||||
Abstract: Part of CoreAudio Utility Classes
|
||||
@@ -51,6 +76,8 @@
|
||||
// Includes
|
||||
//==================================================================================================
|
||||
|
||||
#include "BGMThreadSafetyAnalysis.h"
|
||||
|
||||
// System Includes
|
||||
#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
@@ -70,7 +97,7 @@
|
||||
// A recursive mutex.
|
||||
//==================================================================================================
|
||||
|
||||
class CAMutex
|
||||
class CAPABILITY("mutex") CAMutex
|
||||
{
|
||||
// Construction/Destruction
|
||||
public:
|
||||
@@ -79,9 +106,9 @@ public:
|
||||
|
||||
// Actions
|
||||
public:
|
||||
virtual bool Lock();
|
||||
virtual void Unlock();
|
||||
virtual bool Try(bool& outWasLocked); // returns true if lock is free, false if not
|
||||
virtual bool Lock() ACQUIRE();
|
||||
virtual void Unlock() RELEASE();
|
||||
virtual bool Try(bool& outWasLocked) TRY_ACQUIRE(true); // returns true if lock is free, false if not
|
||||
|
||||
virtual bool IsFree() const;
|
||||
virtual bool IsOwnedByCurrentThread() const;
|
||||
@@ -99,15 +126,15 @@ protected:
|
||||
|
||||
// Helper class to manage taking and releasing recursively
|
||||
public:
|
||||
class Locker
|
||||
class SCOPED_CAPABILITY Locker
|
||||
{
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
Locker(CAMutex& inMutex) : mMutex(&inMutex), mNeedsRelease(false) { mNeedsRelease = mMutex->Lock(); }
|
||||
Locker(CAMutex* inMutex) : mMutex(inMutex), mNeedsRelease(false) { mNeedsRelease = (mMutex != NULL && mMutex->Lock()); }
|
||||
Locker(CAMutex& inMutex) ACQUIRE(inMutex) : mMutex(&inMutex), mNeedsRelease(false) { mNeedsRelease = mMutex->Lock(); }
|
||||
Locker(CAMutex* inMutex) ACQUIRE(inMutex) : mMutex(inMutex), mNeedsRelease(false) { mNeedsRelease = (mMutex != NULL && mMutex->Lock()); }
|
||||
// in this case the mutex can be null
|
||||
~Locker() { if(mNeedsRelease) { mMutex->Unlock(); } }
|
||||
~Locker() RELEASE() { if(mNeedsRelease) { mMutex->Unlock(); } }
|
||||
|
||||
|
||||
private:
|
||||
@@ -121,6 +148,8 @@ public:
|
||||
|
||||
};
|
||||
|
||||
// Clang's static analysis doesn't work for unlocker classes. See
|
||||
// <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html#no-alias-analysis>.
|
||||
// Unlocker
|
||||
class Unlocker
|
||||
{
|
||||
@@ -138,12 +167,12 @@ public:
|
||||
};
|
||||
|
||||
// you can use this with Try - if you take the lock in try, pass in the outWasLocked var
|
||||
class Tryer {
|
||||
class SCOPED_CAPABILITY Tryer {
|
||||
|
||||
// Construction/Destruction
|
||||
public:
|
||||
Tryer (CAMutex &mutex) : mMutex(mutex), mNeedsRelease(false), mHasLock(false) { mHasLock = mMutex.Try (mNeedsRelease); }
|
||||
~Tryer () { if (mNeedsRelease) mMutex.Unlock(); }
|
||||
Tryer (CAMutex &mutex) TRY_ACQUIRE(true, mutex) : mMutex(mutex), mNeedsRelease(false), mHasLock(false) { mHasLock = mMutex.Try (mNeedsRelease); }
|
||||
~Tryer () RELEASE() { if (mNeedsRelease) mMutex.Unlock(); }
|
||||
|
||||
bool HasLock () const { return mHasLock; }
|
||||
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
19FE7B8CE9148B3D8D7517C6 /* BGM_Control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BGM_Control.h; sourceTree = "<group>"; };
|
||||
19FE7BC3396C4E50D21E1BC8 /* BGM_Control.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_Control.cpp; sourceTree = "<group>"; };
|
||||
19FE7E6DC2A1B61211D74782 /* BGM_MuteControl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BGM_MuteControl.cpp; sourceTree = "<group>"; };
|
||||
1C09150523F010E8001EB0E1 /* set-version.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "set-version.sh"; sourceTree = "<group>"; };
|
||||
1C0CB6A61C4E06C00084C15A /* CAAtomicStack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAtomicStack.h; path = PublicUtility/CAAtomicStack.h; sourceTree = "<group>"; };
|
||||
1C0CB6A71C4E06F70084C15A /* CAAtomic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAtomic.h; path = PublicUtility/CAAtomic.h; sourceTree = "<group>"; };
|
||||
1C0CB6A91C50A3AF0084C15A /* CAAutoDisposer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CAAutoDisposer.h; path = PublicUtility/CAAutoDisposer.h; sourceTree = "<group>"; };
|
||||
@@ -203,6 +204,15 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
1C09150423F010E8001EB0E1 /* Scripts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
1C09150523F010E8001EB0E1 /* set-version.sh */,
|
||||
);
|
||||
name = Scripts;
|
||||
path = ../SharedSource/Scripts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1C0CB6AF1C642C600084C15A /* DeviceClients */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -352,6 +362,7 @@
|
||||
27D643B71C9FABF600737F6E /* BGM_Types.h */,
|
||||
2771700E1CA0C16200AB34B4 /* BGM_Utils.h */,
|
||||
275343BC1DE9B44900DF3858 /* BGM_Utils.cpp */,
|
||||
1C09150423F010E8001EB0E1 /* Scripts */,
|
||||
27D643C21C9FBC5800737F6E /* BGM_TestUtils.h */,
|
||||
27D643B81C9FABF600737F6E /* BGMXPCProtocols.h */,
|
||||
);
|
||||
@@ -396,6 +407,7 @@
|
||||
1CB8B3601BBBB78D000E2DD1 /* Sources */,
|
||||
1CB8B3611BBBB78D000E2DD1 /* Frameworks */,
|
||||
1CB8B3621BBBB78D000E2DD1 /* Resources */,
|
||||
1C09150823F01E6D001EB0E1 /* Run Script - set-version.sh */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -447,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 */;
|
||||
@@ -483,6 +495,27 @@
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
1C09150823F01E6D001EB0E1 /* Run Script - set-version.sh */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Run Script - set-version.sh";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "# Append the git HEAD short ID to the build version for SNAPSHOT and DEBUG builds.\n\"$SRCROOT/../SharedSource/Scripts/set-version.sh\"\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
1C8034D61BDD073B00668E00 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
@@ -639,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;
|
||||
@@ -678,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";
|
||||
@@ -706,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;
|
||||
@@ -745,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";
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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().
|
||||
|
||||
@@ -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 "
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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
@@ -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 |
+3
-2
@@ -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
@@ -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
|
||||
|
||||
@@ -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/>
|
||||
[Auto-pause music](#auto-pause-music)<br/>
|
||||
[Application volume](#application-volume)<br/>
|
||||
[Recording system audio](#recording-system-audio)<br/>
|
||||
[Download](#download)<br/>
|
||||
[Build and Install](#build-and-install)</br>
|
||||
[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
|
||||
|
||||
@@ -111,10 +114,10 @@ brew cask install background-music-pre
|
||||
```
|
||||
|
||||
<details><summary>More info...</summary>
|
||||
<p>
|
||||
|
||||
This command uses `/bin/bash` instead of `bash` in case someone has a nonstandard Bash in their `$PATH`. However, it doesn't do this for `tar` or `curl`. In addition, `build_and_install.sh` doesn't call programs by absolute paths. This command also uses `gzcat - | tar x` instead of `tar xz` because `gzcat` will also check the file's integrity (gzip files
|
||||
include a checksum), and will ensure that a half-downloaded copy of `build_and_install.sh` doesn't run.
|
||||
</p>
|
||||
|
||||
</details>
|
||||
|
||||
### Option 2
|
||||
@@ -144,11 +147,19 @@ 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
|
||||
system's default output device to something other than the **Background Music** device. If it already is, then
|
||||
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.
|
||||
|
||||
If this does not work, you might have to uninstall. Consider filing a bug report if you do.
|
||||
Make sure you allow "microphone access" when you first run Background Music. If you denied it, go to
|
||||
`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).)
|
||||
|
||||
If the volume slider for an app isn't working, try looking in `More Apps` for entries like `Some
|
||||
App (Helper)`. For some meeting or video chat apps, you may need to do this to change the current
|
||||
meeting volume.
|
||||
|
||||
## Known issues and solutions
|
||||
|
||||
@@ -156,16 +167,18 @@ If this does not work, you might have to uninstall. Consider filing a bug report
|
||||
|
||||
- Set your volume to its maximum level and lower the volumes of other applications.
|
||||
|
||||
- **VLC pauses iTunes or Spotify when playing, and stops Background Music from unpausing your music afterward.**
|
||||
- **Only 2-channel (stereo) audio devices are currently supported for output.**
|
||||
|
||||
- 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*.
|
||||
- **VLC pauses iTunes or Spotify when playing, and stops Background Music from unpausing your music afterward.**
|
||||
|
||||
- **Skype pauses iTunes during calls.**
|
||||
- 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.**
|
||||
|
||||
- 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.
|
||||
@@ -173,8 +186,8 @@ If this does not work, you might have to uninstall. Consider filing a bug report
|
||||
- **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.
|
||||
|
||||
### Other issues
|
||||
Some are in listed in [TODO.md](/TODO.md).
|
||||
Many other issues are listed in [TODO.md](/TODO.md) and in [GitHub
|
||||
Issues](https://github.com/kyleneideck/BackgroundMusic/issues).
|
||||
|
||||
# Related projects
|
||||
|
||||
@@ -188,24 +201,26 @@ Some are in listed in [TODO.md](/TODO.md).
|
||||
- [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
|
||||
Mac OS X"
|
||||
- [Zirkonium](https://code.google.com/archive/p/zirkonium) - "An infrastructure and application for multi-channel sound
|
||||
spatialization on MacOS X."
|
||||
- [BlackHole](https://github.com/ExistentialAudio/BlackHole) - "a modern macOS virtual audio driver that allows applications to pass audio to other applications with zero additional latency."
|
||||
|
||||
### 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:
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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/>.
|
||||
|
||||
#
|
||||
# set-version.sh
|
||||
# SharedSource
|
||||
#
|
||||
# Copyright © 2020 Kyle Neideck
|
||||
#
|
||||
# Append the git HEAD short ID to the build version for SNAPSHOT and DEBUG builds. For example,
|
||||
# this might change the version string from "0.4.0" to "0.4.0-SNAPSHOT-abc0123".
|
||||
#
|
||||
# Thanks to Václav Slavík for the initial version of this:
|
||||
# <http://stackoverflow.com/a/26354117/1091063>.
|
||||
#
|
||||
# TODO: Update CFBundleVersion as well?
|
||||
#
|
||||
|
||||
# If HEAD isn't tagged, or has "SNAPSHOT" or "DEBUG" in the tag name, this is a snapshot build.
|
||||
# If HEAD is tagged more than once, use the most recent.
|
||||
TAG=$(/usr/bin/git tag --points-at HEAD --sort='-taggerdate' 2>/dev/null | head -n 1)
|
||||
|
||||
if [[ $? -eq 0 ]] && ( [[ "${TAG}" == "" ]] || \
|
||||
[[ "${TAG}" =~ .*SNAPSHOT.* ]] || \
|
||||
[[ "${TAG}" =~ .*DEBUG.* ]] ); then
|
||||
head_short_id=$(/usr/bin/git rev-list HEAD --max-count=1 --abbrev-commit)
|
||||
info_plist="${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}"
|
||||
|
||||
if [[ "${CONFIGURATION}" != "Release" ]]; then
|
||||
build_type="DEBUG"
|
||||
else
|
||||
build_type="SNAPSHOT"
|
||||
fi
|
||||
|
||||
if [[ -f "$info_plist" ]]; then
|
||||
current_version=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "${info_plist}")
|
||||
base_version=$(/usr/libexec/PlistBuddy -c "Print :BGMBundleVersionBase" "${info_plist}" 2>/dev/null)
|
||||
|
||||
if [[ $? -ne 0 ]] || [[ "${base_version}" == "" ]]; then
|
||||
base_version="${current_version}"
|
||||
/usr/libexec/PlistBuddy -c "Add :BGMBundleVersionBase string ${base_version}" "${info_plist}"
|
||||
fi
|
||||
|
||||
new_version="${base_version}-${build_type}-${head_short_id}"
|
||||
|
||||
if [[ "${new_version}" != "${current_version}" ]]; then # Only touch the file if we need to.
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${new_version}" "${info_plist}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
+28
-5
@@ -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"
|
||||
@@ -662,9 +670,21 @@ else
|
||||
ENABLE_ASAN="${ENABLE_ASAN:-NO}"
|
||||
fi
|
||||
|
||||
enableUBSanArg() {
|
||||
if [[ "${ENABLE_UBSAN+}" != "" ]]; then
|
||||
echo "-enableUndefinedBehaviorSanitizer"
|
||||
echo "$ENABLE_UBSAN"
|
||||
fi
|
||||
}
|
||||
|
||||
# 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
|
||||
@@ -714,6 +734,7 @@ echo "[1/3] ${ACTIONING} the virtual audio device $(bold_face ${DRIVER_DIR}) to"
|
||||
${SUDO} "${XCODEBUILD}" -scheme "Background Music Device" \
|
||||
-configuration ${CONFIGURATION} \
|
||||
-enableAddressSanitizer ${ENABLE_ASAN} \
|
||||
$(enableUBSanArg) \
|
||||
$(archivePath BGMDriver) \
|
||||
BUILD_DIR=./build \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
@@ -743,6 +764,7 @@ xpcHelperInstallPathArg() {
|
||||
${SUDO} "${XCODEBUILD}" -scheme BGMXPCHelper \
|
||||
-configuration ${CONFIGURATION} \
|
||||
-enableAddressSanitizer ${ENABLE_ASAN} \
|
||||
$(enableUBSanArg) \
|
||||
$(archivePath BGMXPCHelper) \
|
||||
BUILD_DIR=./build \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
@@ -764,6 +786,7 @@ echo "[3/3] ${ACTIONING} $(bold_face ${APP_DIR}) to $(bold_face ${APP_PATH})" \
|
||||
${SUDO} "${XCODEBUILD}" -scheme "Background Music" \
|
||||
-configuration ${CONFIGURATION} \
|
||||
-enableAddressSanitizer ${ENABLE_ASAN} \
|
||||
$(enableUBSanArg) \
|
||||
$(archivePath BGMApp) \
|
||||
BUILD_DIR=./build \
|
||||
RUN_CLANG_STATIC_ANALYZER=0 \
|
||||
|
||||
+48
-23
@@ -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.
|
||||
@@ -34,6 +35,10 @@
|
||||
|
||||
# TODO: Code signing. See `man productbuild`.
|
||||
|
||||
set -o nounset
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
PATH="/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"; export PATH
|
||||
|
||||
# Sets all dirs in $1 to 755 (rwxr-xr-x) and all files in $1 to 644 (rw-r--r--).
|
||||
@@ -51,18 +56,12 @@ set_permissions() {
|
||||
|
||||
# Use the release configuration and archive by default.
|
||||
packaging_operation="make_release_package"
|
||||
bgmapp_build_output_path="archives/BGMApp.xcarchive/Products/Applications"
|
||||
bgmxpchelper_build_output_path="archives/BGMXPCHelper.xcarchive/Products/usr/local/libexec"
|
||||
bgmdriver_build_output_path="archives/BGMDriver.xcarchive/Products/Library/Audio/Plug-Ins/HAL"
|
||||
repackage_dir=""
|
||||
|
||||
# Handle the options passed to this script.
|
||||
while getopts ":dr:h" opt; do
|
||||
case $opt in
|
||||
d)
|
||||
packaging_operation="make_debug_package"
|
||||
bgmapp_build_output_path="BGMApp/build/Debug"
|
||||
bgmdriver_build_output_path="BGMDriver/build/Debug"
|
||||
;;
|
||||
r)
|
||||
packaging_operation="repackage"
|
||||
@@ -80,30 +79,40 @@ while getopts ":dr:h" opt; do
|
||||
esac
|
||||
done
|
||||
|
||||
bgmapp_path="${bgmapp_build_output_path}/Background Music.app"
|
||||
bgmdriver_path="${bgmdriver_build_output_path}/Background Music Device.driver"
|
||||
bgmxpchelper_path="${bgmxpchelper_build_output_path}/BGMXPCHelper.xpc"
|
||||
|
||||
# Build
|
||||
if [[ $packaging_operation == "repackage" ]]; then
|
||||
# Paths to the bundles in the expanded package that we're repackaging.
|
||||
# No need to build anything if we're repackaging.
|
||||
build_status=0
|
||||
|
||||
# Set the paths to the bundles in the expanded package that we're repackaging.
|
||||
bgmapp_path="${repackage_dir}/Installer.pkg/Payload/Applications/Background Music.app"
|
||||
bgmdriver_path="${repackage_dir}/Installer.pkg/Payload/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"
|
||||
bgmxpchelper_path="${repackage_dir}/Installer.pkg/Scripts/BGMXPCHelper.xpc"
|
||||
|
||||
# No need to build anything if we're repackaging.
|
||||
build_status=0
|
||||
elif [[ $packaging_operation == "make_debug_package" ]]; then
|
||||
# Disable AddressSanitizer so we can distribute debug packages to users reporting bugs without
|
||||
# worrying about loading the AddressSanitizer dylib in coreaudiod.
|
||||
# Build using the debug configuration.
|
||||
#
|
||||
# Disable AddressSanitizer and UndefinedBehaviorSanitizer so we can distribute debug packages to
|
||||
# users without worrying about loading the AddressSanitizer and UndefinedBehaviorSanitizer
|
||||
# dylibs in coreaudiod. We've also had issues loading those dylibs in the other binaries when
|
||||
# the binaries were built on other systems.
|
||||
#
|
||||
# TODO: Would debug packages be more useful if they were built with optimization (i.e. using the
|
||||
# DebugOpt configuration instead of Debug)?
|
||||
ENABLE_ASAN=NO bash build_and_install.sh -b -d
|
||||
ENABLE_ASAN=NO ENABLE_UBSAN=NO bash build_and_install.sh -b -d
|
||||
build_status=$?
|
||||
|
||||
# Set the paths to the build products (i.e. the bundles).
|
||||
bgmapp_path="BGMApp/build/Debug/Background Music.app"
|
||||
bgmdriver_path="BGMDriver/build/Debug/Background Music Device.driver"
|
||||
bgmxpchelper_path="BGMApp/build/Debug/BGMXPCHelper.xpc"
|
||||
else
|
||||
# Build and archive. (It uses the release config when archiving.)
|
||||
bash build_and_install.sh -a
|
||||
build_status=$?
|
||||
|
||||
# Set the paths to the build products (i.e. the bundles). Note that these are in the archives.
|
||||
bgmapp_path="archives/BGMApp.xcarchive/Products/Applications/Background Music.app"
|
||||
bgmdriver_path="archives/BGMDriver.xcarchive/Products/Library/Audio/Plug-Ins/HAL/Background Music Device.driver"
|
||||
bgmxpchelper_path="archives/BGMXPCHelper.xcarchive/Products/usr/local/libexec/BGMXPCHelper.xpc"
|
||||
fi
|
||||
|
||||
# Exit if the build failed.
|
||||
@@ -116,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"
|
||||
@@ -130,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"
|
||||
@@ -149,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"
|
||||
@@ -176,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
|
||||
|
||||
@@ -199,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
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
+97
-41
@@ -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,15 +32,39 @@ 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_path="$(bash safe_install_dir.sh -y)"
|
||||
xpc_helper_install_path="$(bash safe_install_dir.sh -y)"
|
||||
|
||||
log "Installing BGMXPCHelper to $xpc_helper_path"
|
||||
cp -Rf "BGMXPCHelper.xpc" "$xpc_helper_path"
|
||||
log "Installing BGMXPCHelper to $xpc_helper_install_path"
|
||||
|
||||
# Delete the old version first, if any, to work around a macOS bug where a codesigned Mach-O binary
|
||||
# can fail verification if you overwrite the old version instead of replacing it. Apparently the
|
||||
# kernel caches some codesigning info that doesn't get updated. See
|
||||
# <https://forums.developer.apple.com/thread/126187>.
|
||||
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_path" "BGMXPCHelper.xpc/Contents/MacOS/BGMXPCHelper" "."
|
||||
bash "post_install.sh" "$xpc_helper_install_path" "BGMXPCHelper.xpc/Contents/MacOS/BGMXPCHelper" "."
|
||||
|
||||
# TODO: Verify the installed files, their permissions, the _BGMXPCHelper user/group, etc.
|
||||
|
||||
@@ -47,19 +72,34 @@ bash "post_install.sh" "$xpc_helper_path" "BGMXPCHelper.xpc/Contents/MacOS/BGMXP
|
||||
# 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..."
|
||||
@@ -69,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.
|
||||
@@ -122,3 +177,4 @@ sleep 1
|
||||
exit 0
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user