From 9170b04c203a845804e5cc94ac4057bf34589081 Mon Sep 17 00:00:00 2001 From: Charles Date: Mon, 19 Apr 2021 22:55:47 +0100 Subject: [PATCH 1/2] Pull request. --- .github/workflows/docker-build.yml | 29 ++++++ .gitignore | 153 ++++++++++++++++++++++++++++- README.md | 54 +++++++--- ap2-receiver.py | 18 +++- ap2/connections/audio.py | 45 ++++++--- ap2/connections/stream.py | 6 +- ap2/utils.py | 42 +++++--- docker/Dockerfile | 33 ++++--- docker/README.md | 3 - docker/start.sh | 14 ++- requirements.txt | 2 + 11 files changed, 330 insertions(+), 69 deletions(-) create mode 100644 .github/workflows/docker-build.yml delete mode 100644 docker/README.md diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..513b688 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,29 @@ +name: Build and Push Docker Image + +on: + push: + branches: master + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: ./ + file: ./docker/Dockerfile + platforms: linux/386,linux/amd64,linux/arm/v6,linux/arm64 + push: true + tags: charlesomer/airplay:latest \ No newline at end of file diff --git a/.gitignore b/.gitignore index b6e4761..d151ddb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,116 @@ +events.bin +proto +Features.xlsx +airplay-env + +# Created by https://www.toptal.com/developers/gitignore/api/python,vscode,macos,windows,linux,c +# Edit at https://www.toptal.com/developers/gitignore?templates=python,vscode,macos,windows,linux,c + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Python ### # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions -*.so # Distribution / packaging .Python @@ -50,6 +156,7 @@ coverage.xml *.py,cover .hypothesis/ .pytest_cache/ +pytestdebug.log # Translations *.mo @@ -70,6 +177,7 @@ instance/ # Sphinx documentation docs/_build/ +doc/_build/ # PyBuilder target/ @@ -109,6 +217,7 @@ venv/ ENV/ env.bak/ venv.bak/ +pythonenv* # Spyder project settings .spyderproject @@ -127,3 +236,45 @@ dmypy.json # Pyre type checker .pyre/ + +# pytype static type analyzer +.pytype/ + +# profiling data +.prof + +### vscode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/python,vscode,macos,windows,linux,c \ No newline at end of file diff --git a/README.md b/README.md index d715c19..51b7c5c 100644 --- a/README.md +++ b/README.md @@ -27,42 +27,64 @@ Next steps: - FairPlay v2 Support --- -## Raspberry Pi 4 +## Pre-Built Docker Image +This image is built directly from `master` so may break. Tested with Raspberry Pi. -Install docker and then build the image: +https://hub.docker.com/r/charlesomer/airplay +Example Docker Compose ```zsh -docker build -f docker/Dockerfile -t invano/ap2-receiver . +version: "3.8" +services: + airplay: + image: charlesomer/airplay:latest + restart: always + network_mode: host + environment: # All variables are optional. + # - AP2HOSTNAME=Airplay2Device + # - AP2IFACE=eth0 + # - AUDIO_DEVICE=default # For use with alsaaudio. + # - USE_PORTAUDIO=true # If this is set to true, volume management is also disabled + # - NO_VOLUME_MANAGEMENT=true + devices: + - "/dev/snd" ``` -To run the receiver: +## Raspberry Pi + +Install docker and then build the image: ```zsh -docker run -it --rm --device /dev/snd --net host invano/ap2-receiver +docker build -f docker/Dockerfile -t USERNAME/airplay . ``` -Default network device is wlan0, you can change this with AP2IFACE env variable: +To run the receiver: ```zsh -docker run -it --rm --device /dev/snd --env AP2IFACE=eth0 --net host invano/ap2-receiver +docker run -it --rm --device /dev/snd --net host USERNAME/airplay ``` -## macOS Catalina - -To run the receiver please use Python 3 and do the following: +## macOS -* Run the following commands +_macOS has shown issues when playing audio, if anyone is able to take a look at this to confirm/fix that would be great._ +Currently `portaudio` is required for MacOS. It can be installed via homebrew: ```zsh -brew install python3 brew install portaudio -virtualenv -p /usr/local/bin/python3 proto -source proto/bin/activate -pip install -r requirements.txt -pip install --global-option=build_ext --global-option="-I/usr/local/Cellar/portaudio/19.6.0/include" --global-option="-L/usr/local/Cellar/portaudio/19.6.0/lib" pyaudio +``` +Then, you may be able to use the docker image although this is untested. Add the `--use-portaudio` option. Alternatively, clone the repo and run via python virtualenv. + +```zsh +pip3 install virtualenv +virtualenv -p /usr/local/bin/python3 airplay-env +source airplay-env/bin/activate +pip3 install -r requirements.txt +# The following line may not be required. +# pip3 install --global-option=build_ext --global-option="-I/usr/local/Cellar/portaudio/19.6.0/include" --global-option="-L/usr/local/Cellar/portaudio/19.6.0/lib" pyaudio python ap2-receiver.py -m myap2 --netiface=en0 +# Allow incoming connections. ``` ## Windows diff --git a/ap2-receiver.py b/ap2-receiver.py index da839df..c273af7 100644 --- a/ap2-receiver.py +++ b/ap2-receiver.py @@ -67,6 +67,9 @@ HTTP_CT_IMAGE = "image/jpeg" HTTP_CT_DMAP = "application/x-dmap-tagged" +AUDIO_DEVICE = "default" +USE_PORTAUDIO = False + def setup_global_structs(args): global sonos_one_info global sonos_one_setup @@ -116,7 +119,7 @@ def setup_global_structs(args): if DISABLE_VM: volume = 0 else: - volume = get_volume() + volume = get_volume(AUDIO_DEVICE) second_stage_info = { "initialVolume": volume, } @@ -291,7 +294,7 @@ def do_SETUP(self): else: print("Sending CONTROL/DATA:") - stream = Stream(plist["streams"][0]) + stream = Stream(plist["streams"][0], AUDIO_DEVICE, USE_PORTAUDIO) self.server.streams.append(stream) sonos_one_setup_data["streams"][0]["controlPort"] = stream.control_port sonos_one_setup_data["streams"][0]["dataPort"] = stream.data_port @@ -327,7 +330,7 @@ def do_GET_PARAMETER(self): if p == b"volume": print("GET_PARAMETER: %s" % p) if not DISABLE_VM: - params_res[p] = str(get_volume()).encode() + params_res[p] = str(get_volume(AUDIO_DEVICE)).encode() else: print("Volume Management is disabled") else: @@ -342,7 +345,7 @@ def do_GET_PARAMETER(self): self.send_header("Server", self.version_string()) self.send_header("CSeq", self.headers["CSeq"]) self.end_headers() - hexdump(res); + hexdump(res) self.wfile.write(res) def do_SET_PARAMETER(self): @@ -361,7 +364,7 @@ def do_SET_PARAMETER(self): if pp[0] == b"volume": print("SET_PARAMETER: %s => %s" % (pp[0], pp[1])) if not DISABLE_VM: - set_volume(float(pp[1])) + set_volume(float(pp[1]), AUDIO_DEVICE) else: print("Volume Management is disabled") elif pp[0] == b"progress": @@ -711,6 +714,8 @@ def upgrade_to_encrypted(self, client_address, shared_key): parser.add_argument("-m", "--mdns", required=True, help="mDNS name to announce") parser.add_argument("-n", "--netiface", required=True, help="Network interface to bind to") parser.add_argument("-nv", "--no-volume-management", required=False, help="Disable volume management", action='store_true') + parser.add_argument("-d", "--audio-device", required=False, help="Specify output device (string).", default='default') + parser.add_argument("-po", "--use-portaudio", required=False, help="Use port audio (useful for Windows and MacOS).", default=False) parser.add_argument("-f", "--features", required=False, help="Features") args = parser.parse_args() @@ -719,6 +724,9 @@ def upgrade_to_encrypted(self, client_address, shared_key): IFEN = args.netiface ifen = ni.ifaddresses(IFEN) DISABLE_VM = args.no_volume_management + + AUDIO_DEVICE = args.audio_device + USE_PORTAUDIO = args.use_portaudio if args.features: try: FEATURES = int(args.features, 16) diff --git a/ap2/connections/audio.py b/ap2/connections/audio.py index a1f5f78..98d4a56 100644 --- a/ap2/connections/audio.py +++ b/ap2/connections/audio.py @@ -14,6 +14,13 @@ from ..utils import get_logger, get_free_tcp_socket, get_free_udp_socket +USE_PORTAUDIO = False + +try: + import alsaaudio +except ImportError: + USE_PORTAUDIO = True + print("Tried to import alsaaudio/pyalsaaudio. This may not be a problem depending on your setup.") class RTP: def __init__(self, data): @@ -197,20 +204,32 @@ class AudioFormat(enum.Enum): AAC_ELD_44100_1 = 1 << 31 AAC_ELD_48000_1 = 1 << 32 - def __init__(self, session_key, audio_format): + def __init__(self, session_key, audio_format, audio_device="default", use_portaudio=False): if audio_format != Audio.AudioFormat.ALAC_44100_16_2.value \ and audio_format != Audio.AudioFormat.AAC_LC_44100_2.value: raise Exception("Unsupported format: %s", Audio.AudioFormat(audio_format)).name self.audio_format = audio_format self.session_key = session_key self.rtp_buffer = RTPBuffer() + self.audio_device = audio_device + self.use_portaudio = use_portaudio def init_audio_sink(self): - self.pa = pyaudio.PyAudio() - self.sink = self.pa.open(format=self.pa.get_format_from_width(2), - channels=2, - rate=44100, - output=True) + if self.use_portaudio or USE_PORTAUDIO: + self.pa = pyaudio.PyAudio() + self.sink = self.pa.open( + format=self.pa.get_format_from_width(2), + channels=2, + rate=44100, + output=True) + else: + self.sink = alsaaudio.PCM( + device=self.audio_device, + channels=2, + rate=44100, + format=alsaaudio.PCM_FORMAT_S16_LE, + periodsize=1024) + codec = None extradata = None if self.audio_format == Audio.AudioFormat.ALAC_44100_16_2.value: @@ -268,8 +287,8 @@ def run(self, parent_reader_connection): player_thread.start() @classmethod - def spawn(cls, session_key, audio_format): - audio = cls(session_key, audio_format) + def spawn(cls, session_key, audio_format, audio_device, use_portaudio): + audio = cls(session_key, audio_format, audio_device, use_portaudio) # This pipe is reachable from receiver parent_reader_connection, audio.audio_connection = multiprocessing.Pipe() mainprocess = multiprocessing.Process(target=audio.run, args=(parent_reader_connection,)) @@ -279,8 +298,8 @@ def spawn(cls, session_key, audio_format): class AudioRealtime(Audio): - def __init__(self, session_key, audio_format): - super(AudioRealtime, self).__init__(session_key, audio_format) + def __init__(self, session_key, audio_format, audio_device, use_portaudio): + super(AudioRealtime, self).__init__(session_key, audio_format, audio_device, use_portaudio) self.socket = get_free_udp_socket() self.port = self.socket.getsockname()[1] @@ -302,7 +321,7 @@ def serve(self, playerconn): if data: rtp = RTP_REALTIME(data) self.handle(rtp) - audio = self.process(rtp) + audio = self.process(rtp) self.sink.write(audio) except KeyboardInterrupt: pass @@ -312,8 +331,8 @@ def serve(self, playerconn): class AudioBuffered(Audio): - def __init__(self, session_key, audio_format): - super(AudioBuffered, self).__init__(session_key, audio_format) + def __init__(self, session_key, audio_format, audio_device, use_portaudio): + super(AudioBuffered, self).__init__(session_key, audio_format, audio_device, use_portaudio) self.socket = get_free_tcp_socket() self.port = self.socket.getsockname()[1] self.anchorMonotonicTime = None diff --git a/ap2/connections/stream.py b/ap2/connections/stream.py index b80f66f..ee878a7 100644 --- a/ap2/connections/stream.py +++ b/ap2/connections/stream.py @@ -8,7 +8,7 @@ class Stream: REALTIME = 96 BUFFERED = 103 - def __init__(self, stream): + def __init__(self, stream, audio_device, use_portaudio): self.audio_format = stream["audioFormat"] self.compression = stream["ct"] self.session_key = stream["shk"] @@ -20,9 +20,9 @@ def __init__(self, stream): self.server_control = stream["controlPort"] self.latency_min = stream["latencyMin"] self.latency_max = stream["latencyMax"] - self.data_port, self.data_proc, audio_connection = AudioRealtime.spawn(self.session_key, self.audio_format) + self.data_port, self.data_proc, audio_connection = AudioRealtime.spawn(self.session_key, self.audio_format, audio_device, use_portaudio) elif self.type == Stream.BUFFERED: - self.data_port, self.data_proc, self.audio_connection = AudioBuffered.spawn(self.session_key, self.audio_format) + self.data_port, self.data_proc, self.audio_connection = AudioBuffered.spawn(self.session_key, self.audio_format, audio_device, use_portaudio) def teardown(self): self.data_proc.terminate() diff --git a/ap2/utils.py b/ap2/utils.py index 4301d04..941c598 100644 --- a/ap2/utils.py +++ b/ap2/utils.py @@ -4,6 +4,13 @@ import platform import subprocess +try: + import alsaaudio +except ImportError: + USE_PORTAUDIO = True + print("Tried to import alsaaudio/pyalsaaudio. This may not be a problem depending on your setup.") + + def get_logger(name, level="INFO"): logging.basicConfig(filename="%s.log" % name, filemode='a', @@ -39,20 +46,29 @@ def interpolate(value, from_min, from_max, to_min, to_max): return to_min + (value_scale * to_span) -def get_volume(): +def get_mixer(audio_device): + # Unfortunately getting the controller is not easy. Here we run the amixer command and extract from the result. + controller_string = subprocess.run(["amixer", "-D", audio_device, "scontrols"], capture_output=True, text=True).stdout + controller_extracted = re.findall("([^']*)", controller_string)[2] + print("Using controller: ", controller_extracted) + mixer = alsaaudio.Mixer(device=audio_device, control=controller_extracted) + + return mixer + +def get_volume(audio_device): subsys = platform.system() if subsys == "Darwin": pct = int(subprocess.check_output(["osascript", "-e", "output volume of (get volume settings)"]).rstrip()) vol = interpolate(pct, 0, 100, -30, 0) elif subsys == "Linux": - line_pct = subprocess.check_output(["amixer", "get", "PCM"]).splitlines()[-1] - m = re.search(b"\[([0-9]+)%\]", line_pct) - if m: - pct = int(m.group(1)) - if pct < 45: - pct = 45 - else: pct = 50 - vol = interpolate(pct, 45, 100, -30, 0) + mixer = get_mixer(audio_device) + current_volume_percentage = mixer.getvolume()[0] + + # Not sure why this limit is here. + if current_volume_percentage < 45: + current_volume_percentage = 45 + + vol = interpolate(current_volume_percentage, 45, 100, -30, 0) elif subsys == "Windows": # Volume get is not managed under windows, let's set to a default volume vol = 50; @@ -61,7 +77,7 @@ def get_volume(): return vol -def set_volume(vol): +def set_volume(vol, audio_device): if vol == -144: vol = -30 @@ -70,6 +86,8 @@ def set_volume(vol): pct = int(interpolate(vol, -30, 0, 0, 100)) subprocess.run(["osascript", "-e", "set volume output volume %d" % pct]) elif subsys == "Linux": - pct = int(interpolate(vol, -30, 0, 45, 100)) + volume_percentage = int(interpolate(vol, -30, 0, 45, 100)) - subprocess.run(["amixer", "set", "PCM", "%d%%" % pct]) + mixer = get_mixer(audio_device) + mixer.setvolume(volume_percentage) + print(mixer.getvolume()) \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index 79063d5..bf70a1a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:buster +FROM debian:stable-slim RUN apt-get update -yy && \ apt-get install -yy \ @@ -13,23 +13,26 @@ RUN apt-get update -yy && \ python3-pyaudio \ libffi-dev \ alsa-utils \ - libavformat-dev \ - libavcodec-dev \ - libavdevice-dev \ - libavutil-dev \ - libavfilter-dev \ - libswscale-dev \ - libswresample-dev + libavformat-dev \ + libavcodec-dev \ + libavdevice-dev \ + libavutil-dev \ + libavfilter-dev \ + libswscale-dev \ + libswresample-dev \ + libasound2-dev -COPY ap2-receiver.py /airplay2/ap2-receiver.py -COPY ap2 /airplay2/ap2 -COPY requirements.txt /airplay2/requirements.txt +WORKDIR /airplay2 -RUN pip3 install -r /airplay2/requirements.txt +COPY ./ap2-receiver.py ./ap2-receiver.py +COPY ./ap2 ./ap2 -COPY docker/avahi-daemon.conf /etc/avahi/avahi-daemon.conf -COPY docker/start.sh / +COPY ./requirements.txt ./requirements.txt +RUN pip3 install -r ./requirements.txt + +COPY ./docker/avahi-daemon.conf /etc/avahi/avahi-daemon.conf +COPY ./docker/start.sh / RUN chmod +x /start.sh -CMD ["/start.sh"] +CMD ["/start.sh"] \ No newline at end of file diff --git a/docker/README.md b/docker/README.md deleted file mode 100644 index 1b42574..0000000 --- a/docker/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Docker env for Raspberry Pi testing. - -*Tested on RPi 4* diff --git a/docker/start.sh b/docker/start.sh index 79caba0..0452041 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -6,6 +6,18 @@ fi if [ -z "${AP2IFACE}" ]; then export AP2IFACE='wlan0' fi +if [ -z "${AUDIO_DEVICE}" ]; then + export AUDIO_DEVICE='default' +fi +NO_VOLUME_MANAGEMENT_FLAG="" +if [ "${NO_VOLUME_MANAGEMENT}" = "true" ]; then + NO_VOLUME_MANAGEMENT_FLAG='--no-volume-management' +fi +USE_PORTAUDIO_FLAG="" +if [ "${USE_PORTAUDIO}" = "true" ]; then + USE_PORTAUDIO_FLAG='--use-portaudio' + NO_VOLUME_MANAGEMENT_FLAG='--no-volume-management' +fi # Swap hostname in the avahi config sed "s/\(host-name=\).*/\1${AP2HOSTNAME}/g" -i /etc/avahi/avahi-daemon.conf @@ -16,4 +28,4 @@ sed "s/\(host-name=\).*/\1${AP2HOSTNAME}/g" -i /etc/avahi/avahi-daemon.conf # Start AirPlay 2 service cd /airplay2 -exec python3 ap2-receiver.py -m ${AP2HOSTNAME} -n ${AP2IFACE} --no-volume-management +exec python3 ap2-receiver.py -m ${AP2HOSTNAME} -n ${AP2IFACE} --audio-device ${AUDIO_DEVICE} ${NO_VOLUME_MANAGEMENT_FLAG} ${USE_PORTAUDIO_FLAG} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index b192a7e..e8d6929 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,5 @@ cryptography requests av numpy +pyaudio +pyalsaaudio; sys_platform == 'linux' \ No newline at end of file From d0944df8092362f08fdad20ca75d360e9c69eaa1 Mon Sep 17 00:00:00 2001 From: Charles <32498151+charlesomer@users.noreply.github.com> Date: Thu, 8 Jul 2021 22:39:50 +0100 Subject: [PATCH 2/2] Quick updates. --- README.md | 2 +- ap2-receiver.py | 6 +++--- docker/start.sh | 9 ++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 51b7c5c..6acd17c 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ services: # - AP2HOSTNAME=Airplay2Device # - AP2IFACE=eth0 # - AUDIO_DEVICE=default # For use with alsaaudio. - # - USE_PORTAUDIO=true # If this is set to true, volume management is also disabled + # - DISABLE_PORTAUDIO=true # - NO_VOLUME_MANAGEMENT=true devices: - "/dev/snd" diff --git a/ap2-receiver.py b/ap2-receiver.py index c273af7..a839422 100644 --- a/ap2-receiver.py +++ b/ap2-receiver.py @@ -68,7 +68,7 @@ HTTP_CT_DMAP = "application/x-dmap-tagged" AUDIO_DEVICE = "default" -USE_PORTAUDIO = False +USE_PORTAUDIO = True def setup_global_structs(args): global sonos_one_info @@ -715,7 +715,7 @@ def upgrade_to_encrypted(self, client_address, shared_key): parser.add_argument("-n", "--netiface", required=True, help="Network interface to bind to") parser.add_argument("-nv", "--no-volume-management", required=False, help="Disable volume management", action='store_true') parser.add_argument("-d", "--audio-device", required=False, help="Specify output device (string).", default='default') - parser.add_argument("-po", "--use-portaudio", required=False, help="Use port audio (useful for Windows and MacOS).", default=False) + parser.add_argument("-dpo", "--disable-portaudio", required=False, help="Disable portaudio.", default=False) parser.add_argument("-f", "--features", required=False, help="Features") args = parser.parse_args() @@ -726,7 +726,7 @@ def upgrade_to_encrypted(self, client_address, shared_key): DISABLE_VM = args.no_volume_management AUDIO_DEVICE = args.audio_device - USE_PORTAUDIO = args.use_portaudio + USE_PORTAUDIO = not args.disable_portaudio if args.features: try: FEATURES = int(args.features, 16) diff --git a/docker/start.sh b/docker/start.sh index 0452041..d87f95b 100644 --- a/docker/start.sh +++ b/docker/start.sh @@ -13,10 +13,9 @@ NO_VOLUME_MANAGEMENT_FLAG="" if [ "${NO_VOLUME_MANAGEMENT}" = "true" ]; then NO_VOLUME_MANAGEMENT_FLAG='--no-volume-management' fi -USE_PORTAUDIO_FLAG="" -if [ "${USE_PORTAUDIO}" = "true" ]; then - USE_PORTAUDIO_FLAG='--use-portaudio' - NO_VOLUME_MANAGEMENT_FLAG='--no-volume-management' +DISABLE_PORTAUDIO_FLAG="" +if [ "${DISABLE_PORTAUDIO}" = "true" ]; then + DISABLE_PORTAUDIO_FLAG='--disable-portaudio' fi # Swap hostname in the avahi config @@ -28,4 +27,4 @@ sed "s/\(host-name=\).*/\1${AP2HOSTNAME}/g" -i /etc/avahi/avahi-daemon.conf # Start AirPlay 2 service cd /airplay2 -exec python3 ap2-receiver.py -m ${AP2HOSTNAME} -n ${AP2IFACE} --audio-device ${AUDIO_DEVICE} ${NO_VOLUME_MANAGEMENT_FLAG} ${USE_PORTAUDIO_FLAG} \ No newline at end of file +exec python3 ap2-receiver.py -m ${AP2HOSTNAME} -n ${AP2IFACE} --audio-device ${AUDIO_DEVICE} ${NO_VOLUME_MANAGEMENT_FLAG} ${DISABLE_PORTAUDIO_FLAG} \ No newline at end of file