├── userconfig ├── .placeholder ├── peachtree-dacit.asound.conf ├── topping-d10.asound.conf ├── hdmi-rpi.asound.conf ├── hdmi0-rpi.asound.conf ├── minix-neo-u1.coreelec.asound.conf ├── hdmi-rpi-44.asound.conf ├── hdmi-rpi-48.asound.conf ├── hdmi0-rpi-44.asound.conf ├── hdmi0-rpi-48.asound.conf ├── ifi-zen-dac-v2-softvol.asound.conf ├── smsl-a8-softvol.asound.conf ├── peachtree-dacit-softvol.asound.conf ├── dac-softvol.asound.conf ├── iqaudio-dac.asound.conf ├── xmos-dac-softvol.asound.conf ├── topping-d10-softvol.asound.conf ├── hifiberry-digi-plus-pro.asound.conf ├── aune-s6-softvol.asound.conf ├── dac-softvol-s16.asound.conf ├── dac-softvol-s32.asound.conf ├── hifiberry-dac-plus.asound.conf ├── xmos-dac-softvol-s16.asound.conf ├── xmos-dac-softvol-s32.asound.conf ├── applejack.asound.conf ├── pi-headphones.asound.conf ├── minix-neo-u1.armbian.asound.conf └── README.md ├── logs.sh ├── samples ├── smsl-a8.env ├── fiio-k11.env ├── xmos-dac.env ├── iqaudio-dac.env ├── yulong-d200.env ├── chord-qutest.env ├── ifi-zen-dac-v2.env ├── aune-s6.env ├── hifiberry-dac-plus.env ├── hifiberry-digi-plus-pro.env ├── topping-d10.env ├── xmos-dac-s32.env ├── peachtree-dac-it.env ├── fosi-ds1-no-softvol.env ├── fosi-ds1-softvol.env ├── minix-neo-u1.coreelec.env ├── minix-neo-u1.armbian.env └── README.md ├── restart.sh ├── .gitignore ├── assets ├── _custom │ └── .gitignore ├── audio │ ├── short-low-tone-44k.wav │ └── short-low-tone-48k.wav └── known-devices.md ├── restart-watch.sh ├── LICENSE ├── docker-compose.yaml ├── configure.sh ├── bin ├── entrypoint.sh └── common.sh └── README.md /userconfig/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker-compose logs -f 4 | -------------------------------------------------------------------------------- /samples/smsl-a8.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=SMSL A8 2 | CARD_NAME=v12 3 | -------------------------------------------------------------------------------- /samples/fiio-k11.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=FiiO K11 2 | CARD_NAME=K11 3 | -------------------------------------------------------------------------------- /samples/xmos-dac.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=XMOS DAC 2 | CARD_NAME=DAC 3 | -------------------------------------------------------------------------------- /restart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker-compose up -d --force-recreate 4 | -------------------------------------------------------------------------------- /samples/iqaudio-dac.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=IQAudio DAC 2 | CARD_NAME=IQaudIODAC -------------------------------------------------------------------------------- /samples/yulong-d200.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Yulong D200 2 | CARD_NAME=DAC 3 | -------------------------------------------------------------------------------- /samples/chord-qutest.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Chord Qutest 2 | CARD_NAME=Qutest 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | **.env 3 | userconfig/asound.conf 4 | .vscode/settings.json 5 | -------------------------------------------------------------------------------- /samples/ifi-zen-dac-v2.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Ifi Zen DAC v2 2 | CARD_NAME=Audio 3 | -------------------------------------------------------------------------------- /samples/aune-s6.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Aune S6 2 | CARD_NAME=DAC 3 | CARD_FORMAT=S32_LE 4 | -------------------------------------------------------------------------------- /assets/_custom/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | certificate 3 | lib 4 | lib-arm-linux-gnueabihf 5 | 6 | -------------------------------------------------------------------------------- /samples/hifiberry-dac-plus.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Hifiberry DAC 2 | CARD_NAME=snd_rpi_hifiberry_dacplus -------------------------------------------------------------------------------- /samples/hifiberry-digi-plus-pro.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Hifiberry Digi 2 | CARD_NAME=sndrpihifiberry 3 | -------------------------------------------------------------------------------- /samples/topping-d10.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Topping D10 2 | CARD_NAME=D10 3 | CARD_FORMAT=S32_LE 4 | -------------------------------------------------------------------------------- /samples/xmos-dac-s32.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=XMOS DAC S32 2 | CARD_NAME=DAC 3 | CARD_FORMAT=S32_LE 4 | -------------------------------------------------------------------------------- /restart-watch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker-compose up -d --force-recreate 4 | docker-compose logs -f -------------------------------------------------------------------------------- /samples/peachtree-dac-it.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Peachtree DacIt 2 | CARD_NAME=DAC 3 | CARD_DEVICE=0 4 | 5 | -------------------------------------------------------------------------------- /samples/fosi-ds1-no-softvol.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Fosi Audio DS1 2 | CARD_NAME=DS1 3 | ENABLE_SOFTVOLUME=no 4 | -------------------------------------------------------------------------------- /samples/fosi-ds1-softvol.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Fosi Audio DS1 2 | CARD_NAME=DS1 3 | ENABLE_SOFTVOLUME=yes 4 | -------------------------------------------------------------------------------- /assets/audio/short-low-tone-44k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GioF71/tidal-connect/HEAD/assets/audio/short-low-tone-44k.wav -------------------------------------------------------------------------------- /assets/audio/short-low-tone-48k.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GioF71/tidal-connect/HEAD/assets/audio/short-low-tone-48k.wav -------------------------------------------------------------------------------- /samples/minix-neo-u1.coreelec.env: -------------------------------------------------------------------------------- 1 | FRIENDLY_NAME=Minix Neo U1 2 | CARD_NAME=AMLM8AUDIO 3 | CARD_DEVICE=1 4 | CARD_FORMAT=S32_LE 5 | -------------------------------------------------------------------------------- /userconfig/peachtree-dacit.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.custom { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card DAC 6 | device 0 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /userconfig/topping-d10.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.custom { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card D10 6 | device 0 7 | format S32_LE 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /userconfig/hdmi-rpi.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.custom { 2 | type plug 3 | slave.pcm "hdmihw" 4 | type "iec958" 5 | slave.format "IEC958_SUBFRAME_LE" 6 | } 7 | 8 | pcm.hdmihw { 9 | type hw 10 | card "vc4hdmi" 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /userconfig/hdmi0-rpi.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.custom { 2 | type plug 3 | slave.pcm "hdmihw" 4 | type "iec958" 5 | slave.format "IEC958_SUBFRAME_LE" 6 | } 7 | 8 | pcm.hdmihw { 9 | type hw 10 | card "vc4hdmi0" 11 | } 12 | 13 | 14 | -------------------------------------------------------------------------------- /userconfig/minix-neo-u1.coreelec.asound.conf: -------------------------------------------------------------------------------- 1 | ## Requires FORCE_PLAYBACK_DEVICE=default in .env file 2 | 3 | pcm.!default { 4 | type plug 5 | slave.pcm { 6 | type hw 7 | card "AMLM8AUDIO" 8 | device 1 9 | format S32_LE 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /userconfig/hdmi-rpi-44.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.custom { 2 | type plug 3 | slave.pcm "hdmihw" 4 | type "iec958" 5 | slave.format "IEC958_SUBFRAME_LE" 6 | } 7 | 8 | pcm.hdmihw { 9 | type hw 10 | card "vc4hdmi" 11 | rate 44100 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /userconfig/hdmi-rpi-48.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.custom { 2 | type plug 3 | slave.pcm "hdmihw" 4 | type "iec958" 5 | slave.format "IEC958_SUBFRAME_LE" 6 | } 7 | 8 | pcm.hdmihw { 9 | type hw 10 | card "vc4hdmi" 11 | rate 48000 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /userconfig/hdmi0-rpi-44.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.custom { 2 | type plug 3 | slave.pcm "hdmihw" 4 | type "iec958" 5 | slave.format "IEC958_SUBFRAME_LE" 6 | } 7 | 8 | pcm.hdmihw { 9 | type hw 10 | card "vc4hdmi0" 11 | rate 44100 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /userconfig/hdmi0-rpi-48.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.custom { 2 | type plug 3 | slave.pcm "hdmihw" 4 | type "iec958" 5 | slave.format "IEC958_SUBFRAME_LE" 6 | } 7 | 8 | pcm.hdmihw { 9 | type hw 10 | card "vc4hdmi0" 11 | rate 48000 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /userconfig/ifi-zen-dac-v2-softvol.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card Audio 6 | } 7 | } 8 | 9 | pcm.custom { 10 | type softvol 11 | slave { 12 | pcm "tidal-device" 13 | } 14 | control { 15 | name "Master" 16 | card 0 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /userconfig/smsl-a8-softvol.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-v12 { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card v12 6 | device 0 7 | } 8 | } 9 | 10 | pcm.custom { 11 | type softvol 12 | slave { 13 | pcm "tidal-v12" 14 | } 15 | control { 16 | name "Master" 17 | card 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /userconfig/peachtree-dacit-softvol.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-dacit { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card DAC 6 | device 0 7 | } 8 | } 9 | 10 | pcm.custom { 11 | type softvol 12 | slave { 13 | pcm "tidal-dacit" 14 | } 15 | control { 16 | name "Master" 17 | card 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /userconfig/dac-softvol.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-audio-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card DAC 6 | device 0 7 | } 8 | } 9 | 10 | pcm.custom { 11 | type softvol 12 | slave { 13 | pcm "tidal-audio-device" 14 | } 15 | control { 16 | name "Master" 17 | card 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /userconfig/iqaudio-dac.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-audio-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card IQaudIODAC 6 | device 0 7 | } 8 | } 9 | 10 | pcm.custom { 11 | type softvol 12 | slave { 13 | pcm "tidal-audio-device" 14 | } 15 | control { 16 | name "Master" 17 | card 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /userconfig/xmos-dac-softvol.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-audio-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card DAC 6 | device 0 7 | } 8 | } 9 | 10 | pcm.custom { 11 | type softvol 12 | slave { 13 | pcm "tidal-audio-device" 14 | } 15 | control { 16 | name "Master" 17 | card 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /userconfig/topping-d10-softvol.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-d10 { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card D10 6 | device 0 7 | format S32_LE 8 | } 9 | } 10 | 11 | pcm.custom { 12 | type softvol 13 | slave { 14 | pcm "tidal-d10" 15 | } 16 | control { 17 | name "Master" 18 | card 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /userconfig/hifiberry-digi-plus-pro.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-audio-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card sndrpihifiberry 6 | device 0 7 | } 8 | } 9 | 10 | pcm.custom { 11 | type softvol 12 | slave { 13 | pcm "tidal-audio-device" 14 | } 15 | control { 16 | name "Master" 17 | card 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /userconfig/aune-s6-softvol.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-audio-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card DAC 6 | device 0 7 | format S32_LE 8 | } 9 | } 10 | 11 | pcm.custom { 12 | type softvol 13 | slave { 14 | pcm "tidal-audio-device" 15 | } 16 | control { 17 | name "Master" 18 | card 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /userconfig/dac-softvol-s16.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-audio-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card DAC 6 | device 0 7 | format S16_LE 8 | } 9 | } 10 | 11 | pcm.custom { 12 | type softvol 13 | slave { 14 | pcm "tidal-audio-device" 15 | } 16 | control { 17 | name "Master" 18 | card 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /userconfig/dac-softvol-s32.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-audio-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card DAC 6 | device 0 7 | format S32_LE 8 | } 9 | } 10 | 11 | pcm.custom { 12 | type softvol 13 | slave { 14 | pcm "tidal-audio-device" 15 | } 16 | control { 17 | name "Master" 18 | card 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /userconfig/hifiberry-dac-plus.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-audio-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card snd_rpi_hifiberry_dacplus 6 | device 0 7 | } 8 | } 9 | 10 | pcm.custom { 11 | type softvol 12 | slave { 13 | pcm "tidal-audio-device" 14 | } 15 | control { 16 | name "Master" 17 | card 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /userconfig/xmos-dac-softvol-s16.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-audio-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card DAC 6 | device 0 7 | format S16_LE 8 | } 9 | } 10 | 11 | pcm.custom { 12 | type softvol 13 | slave { 14 | pcm "tidal-audio-device" 15 | } 16 | control { 17 | name "Master" 18 | card 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /userconfig/xmos-dac-softvol-s32.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.tidal-audio-device { 2 | type plug 3 | slave.pcm { 4 | type hw 5 | card DAC 6 | device 0 7 | format S32_LE 8 | } 9 | } 10 | 11 | pcm.custom { 12 | type softvol 13 | slave { 14 | pcm "tidal-audio-device" 15 | } 16 | control { 17 | name "Master" 18 | card 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /userconfig/applejack.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.softvol { 2 | type softvol 3 | slave { 4 | pcm "hw:CARD=A,DEV=0" 5 | } 6 | control { 7 | name "SoftMaster" 8 | card "A" 9 | } 10 | } 11 | 12 | pcm.custom { 13 | type plug 14 | slave { 15 | pcm "softvol" 16 | } 17 | } 18 | 19 | ctl.custom { 20 | type hw 21 | card "A" 22 | } 23 | -------------------------------------------------------------------------------- /userconfig/pi-headphones.asound.conf: -------------------------------------------------------------------------------- 1 | pcm.softvol { 2 | type softvol 3 | slave { 4 | pcm "hw:CARD=Headphones,DEV=0" 5 | } 6 | control { 7 | name "SoftMaster" 8 | card "Headphones" 9 | } 10 | } 11 | 12 | pcm.custom { 13 | type plug 14 | slave { 15 | pcm "softvol" 16 | } 17 | } 18 | 19 | ctl.custom { 20 | type hw 21 | card "Headphones" 22 | } -------------------------------------------------------------------------------- /samples/minix-neo-u1.armbian.env: -------------------------------------------------------------------------------- 1 | ### Minix Neo U1 onboard audio via Optical SPDIF 2 | ### Operating System: Armbian Bookworm 64bit 3 | ### Official docker version 25.0.1 4 | 5 | ### I also had to: 6 | ### add `abi.cp15_barrier=2` to `/etc/sysctl.conf` in order to suppress lots of warnings 7 | ### install avahi-daemon using `sudo apt-get install avahi-daemon` 8 | 9 | ### Performance is poor due to emulation issues of 32bit -> audio quality is also really bad for this reason 10 | ### With other audio containers, there are no issues as long as this tidal-connect is turned off 11 | 12 | FRIENDLY_NAME=Minix Neo U1 13 | CARD_NAME=VEGAS95 14 | CARD_DEVICE=1 15 | -------------------------------------------------------------------------------- /userconfig/minix-neo-u1.armbian.asound.conf: -------------------------------------------------------------------------------- 1 | ### Minix Neo U1 onboard audio via Optical SPDIF 2 | ### Operating System: Armbian Bookworm 64bit 3 | ### Official docker version 25.0.1 4 | 5 | ### I also had to: 6 | ### add `abi.cp15_barrier=2` to `/etc/sysctl.conf` in order to suppress lots of warnings 7 | ### install avahi-daemon using `sudo apt-get install avahi-daemon` 8 | 9 | ### Performance is poor due to emulation issues of 32bit -> audio quality is also really bad for this reason 10 | ### With other audio containers, there are no issues as long as this tidal-connect is turned off 11 | 12 | pcm.custom { 13 | type plug 14 | slave.pcm { 15 | type hw 16 | card VEGAS95 17 | device 1 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Giovanni Fulco 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /userconfig/README.md: -------------------------------------------------------------------------------- 1 | # Samples 2 | 3 | The files you find here are sample `asound.conf` configuration files with the hardware configurations I have run tidal-connect on, or that user have tested successfully. 4 | Choose one that matches your hardware (if any), or create a new one. Follow the naming style .asound.conf, then set ASOUND_FILE_PREFIX to the relevant prefix. 5 | Example, in order to select a file name `topping-d10-softvol.asound.conf`, set ASOUND_FILE_PREFIX to `topping-d10-softvol`. 6 | 7 | ## HDMI output on Raspberry Pi 8 | 9 | I have added three presets, `hdmi-rpi`, `hdmi-rpi-44` and `hdmi-rpi-48`. 10 | The `hdmi-rpi` and `hdmi-rpi-44` worked for me on my Sony tv, however I had to limit audio quality to 16bit/44.1kHz, otherwise this would fail when trying to stream hi-res content. 11 | Enable one of these presets by setting (e.g. for the 44.1kHz variant): 12 | 13 | `ASOUND_FILE_PREFIX=hdmi-rpi-44` 14 | 15 | to your `.env` file. Do not set `CARD_NAME` or `CARD_INDEX` when specifying an asound file. 16 | There is also another set of presets, named `hdmi0-rpi`, `hdmi0-rpi-44` and `hdmi0-rpi-48` which should work with Raspberry Pi 4, which has two hdmi outputs, thus different card names (vc4hdmi0 and vc4hdmi1 instead of only vc4hdmi). 17 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | 4 | services: 5 | tidal-connect: 6 | image: ${TIDAL_CONNECT_IMAGE:-edgecrush3r/tidal-connect:latest} 7 | container_name: tidal-connect 8 | network_mode: host 9 | devices: 10 | - /dev/snd 11 | environment: 12 | - CARD_NAME=${CARD_NAME:-} 13 | - CARD_INDEX=${CARD_INDEX:-} 14 | - CARD_DEVICE=${CARD_DEVICE:-} 15 | - CARD_FORMAT=${CARD_FORMAT:-} 16 | - CERTIFICATE_PATH=${CERTIFICATE_PATH:-} 17 | - CLIENT_ID=${CLIENT_ID:-} 18 | - CREATED_ASOUND_CARD_NAME=${CREATED_ASOUND_CARD_NAME:-} 19 | - ENABLE_SOFTVOLUME=${ENABLE_SOFTVOLUME:-yes} 20 | - ENABLE_GENERATED_TONE=${ENABLE_GENERATED_TONE:-yes} 21 | - ASOUND_FILE_PREFIX=${ASOUND_FILE_PREFIX:-} 22 | - FRIENDLY_NAME=${FRIENDLY_NAME:-TidalConnect} 23 | - MODEL_NAME=${MODEL_NAME:-Audio Streamer} 24 | - MQA_CODEC=${MQA_CODEC:-false} 25 | - MQA_PASSTHROUGH=${MQA_PASSTHROUGH:-false} 26 | - FORCE_PLAYBACK_DEVICE=${FORCE_PLAYBACK_DEVICE:-} 27 | - SLEEP_TIME_SEC=${SLEEP_TIME:-3} 28 | - RESTART_ON_FAIL=${RESTART_ON_FAIL:-1} 29 | - RESTART_WAIT_SEC=${RESTART_WAIT_SEC:-10} 30 | - LOG_LEVEL=${LOG_LEVEL:-3} 31 | - DISABLE_CONTROL_APP=${DISABLE_CONTROL_APP:-0} 32 | - DISABLE_APP_SECURITY=${DISABLE_APP_SECURITY:-} 33 | - DISABLE_WEB_SECURITY=${DISABLE_WEB_SECURITY:-} 34 | volumes: 35 | - ./assets:/assets:ro 36 | - ./userconfig:/userconfig:ro 37 | - ./bin/common.sh:/common.sh 38 | - ./bin/entrypoint.sh:/entrypoint.sh 39 | - /var/run/dbus:/var/run/dbus 40 | dns: 41 | - ${DNS_SERVER_LIST:-8.8.8.8} 42 | restart: unless-stopped 43 | -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # Example `.env` files 2 | 3 | The files you find in this directory represent working configurations, related to specific devices that have been used to run this implementation of tidal-connect. 4 | Of course, this is open to user contribution. The list already includes some user contributions, which of course are very welcome. 5 | Use these information to complete your `.env` file. 6 | 7 | ## The files 8 | 9 | FILE|DEVICE|SOFTVOL|NOTES 10 | :---|:---|:---|:--- 11 | aune-s6.env|Aune S6|yes|OK. One of my DACs 12 | fiio-k11.env|FiiO K11|yes|OK. See [#202](https://github.com/GioF71/tidal-connect/issues/202) 13 | fosi-ds1-softvol.env|Fosi Audio DS1|yes|OK. One of my DACs 14 | fosi-ds1-no-softvol.env|Fosi Audio DS1|no|OK. One of my DACs 15 | hifiberry-dac-plus.env|Hifiberry DAC Plus|yes|OK. One of my pi hats 16 | hifiberry-digi-plus-pro.env|Hifiberry Digi+ Pro|yes|OK. One of my pi hats 17 | ifi-zen-dac-v2|Ifi Zen DAC V2|yes|OK. See [#136](https://github.com/GioF71/tidal-connect/issues/136) 18 | minix-neo-u1.armbian.env|Minix Neo U1 optical output with Armbian OS|yes|Owned device. Performance issues, see file 19 | minix-neo-u1.coreelec.env|Minix Neo U1 optical output with CoreElec|yes|Owned device. OK with CoreElec 20 | peachtree-dac-it.env|Peachtree DAC*It|yes|OK. One of my DACs 21 | smsl-a8.env|SMSL A8|yes|OK. See [#103](https://github.com/GioF71/tidal-connect/issues/103) 22 | topping-d10.env|Topping D10|yes|OK. One of my DACs 23 | xmos-dac.env|Generic XMOS "DAC"|yes|OK. One of my DACs 24 | xmos-dac-s32.env|Generic XMOS "DAC", S32 format|yes|OK. One of my DACs 25 | yulong-d200.env|Yulong D200|yes|OK. One of my DACs 26 | 27 | ## Cannot find your Audio device? 28 | 29 | Open an issue and we will try to add support for your audio device, and document the results. 30 | -------------------------------------------------------------------------------- /assets/known-devices.md: -------------------------------------------------------------------------------- 1 | # Known devices 2 | 3 | ## Preface 4 | 5 | This is a list of devices that are known to be working with this application, and can be easily configure by just using the variables CARD_NAME, CARD_FORMAT and, in a very limited set of cases, CARD_DEVICE. 6 | Other audio devices can be used with the provided presets via ASOUND_FILE_PREFIX (the available configurations are in the userconfig folder of the repository). 7 | For devices known through user issues, I assume that software volume works unless we know that it doesn't. Feel free to open issues in order to correct/enrich the following table with even more devices. 8 | 9 | ## Devices 10 | 11 | DEVICE|CARD NAME|CARD FORMAT|CARD DEVICE|ASOUND PREFIX|NOTES|SOFTWARE VOLUME|ISSUE REFERENCE 12 | :---|:---|:---|:---|:---|:---|:---|:--- 13 | Apple USB Dongle||||applejack||no (?)|#187 14 | Aune S6|DAC|S32_LE|||Owned device|yes|None 15 | Chord Qutest|Qutest|||||yes|#186 16 | FiiO K11|K11|||||yes|#202 17 | Fosi DS1|DS1||||Owned device|yes|None 18 | Hifiberry DAC Plus|snd_rpi_hifiberry_dacplus||||Owned device|yes|None 19 | Hifiberry Digi Plus Pro|sndrpihifiberry||||Owned device|yes|None 20 | Ifi Zen DAC V2|Audio|||||yes|#136 21 | IQAudio DAC|IQaudIODAC|||||yes|#237 22 | Minix Neo U1 (Armbian, optical)|VEGAS95||1||Owned device|yes|None 23 | Minix Neo U1 (CoreElec, optical)|AMLM8AUDIO|S32_LE|1||Owned device|yes|None 24 | Peachtree DAC It|DAC||||Owned device|yes|None 25 | SMSL A8|v12|||||yes|#216 26 | Topping D10|D10|S32_LE|||Owned device|yes|None 27 | RPI3 HDMI Out||||hdmi-rpi|Owned device||None 28 | RPI3 HDMI Out 44.1 kHz||||hdmi-rpi-44|Owned device||None 29 | RPI3 HDMI Out 48 kHz||||hdmi-rpi-48|Owned device||None 30 | RPI4 HDMI 0 Out||||hdmi0-rpi|Owned device||None 31 | RPI4 HDMI 0 Out 44.1 kHz||||hdmi0-rpi-44|Owned device||None 32 | RPI4 HDMI 0 Out 48 kHz||||hdmi0-rpi-48|Owned device||None 33 | Yulong D200|DAC||||Owned device|yes|None 34 | -------------------------------------------------------------------------------- /configure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # error codes 4 | # 1 cannot specify both index and name 5 | 6 | set -e 7 | 8 | ENV_FILE=.env 9 | chmod 755 bin/entrypoint.sh 10 | 11 | while getopts n:i:d:s:l:r:f:m:c:p:o:a:g:w:t:e:b:h:j:v:k:q:u: flag 12 | do 13 | case "${flag}" in 14 | n) card_name=${OPTARG};; 15 | i) card_index=${OPTARG};; 16 | d) card_device=${OPTARG};; 17 | s) card_format=${OPTARG};; 18 | l) enable_soft_volume=${OPTARG};; 19 | r) asound_file_prefix=${OPTARG};; 20 | f) friendly_name=${OPTARG};; 21 | m) model_name=${OPTARG};; 22 | c) mqa_codec=${OPTARG};; 23 | p) mqa_passthrough=${OPTARG};; 24 | o) force_playback_device=${OPTARG};; 25 | a) created_asound_card_name=${OPTARG};; 26 | g) enable_generated_tone=${OPTARG};; 27 | w) restart_wait_sec=${OPTARG};; 28 | t) sleep_time_sec=${OPTARG};; 29 | e) client_id=${OPTARG};; 30 | b) log_level=${OPTARG};; 31 | h) certificate_path=${OPTARG};; 32 | j) disable_control_app=${OPTARG};; 33 | v) dns_server_list=${OPTARG};; 34 | k) disable_app_security=${OPTARG};; 35 | q) disable_web_security=${OPTARG};; 36 | u) docker_image=${OPTARG};; 37 | esac 38 | done 39 | 40 | if [[ -n "${card_index}" && -n "${card_name}" ]]; then 41 | echo "Cannot specify both index and name for audio card" 42 | exit 1 43 | fi 44 | 45 | echo "card_name=[$card_name]" 46 | echo "card_index=[$card_index]" 47 | echo "card_device=[$card_device]" 48 | echo "card_format=[$card_format]" 49 | echo "enable_soft_volume=[$enable_soft_volume]" 50 | echo "asound_file_prefix=[$asound_file_prefix]" 51 | echo "friendly_name=[$friendly_name]" 52 | echo "model_name=[$model_name]" 53 | echo "mqa_codec=[$mqa_codec]" 54 | echo "mqa_passthrough=[$mqa_passthrough]" 55 | echo "force_playback_device=[$force_playback_device]" 56 | echo "created_asound_card_name=[$created_asound_card_name]" 57 | echo "enable_generated_tone=[$enable_generated_tone]" 58 | echo "restart_wait_sec=[$restart_wait_sec]" 59 | echo "sleep_time_sec=[$sleep_time_sec]" 60 | echo "client_id=[$client_id]" 61 | echo "log_level=[$log_level]" 62 | echo "certificate_path=[$certificate_path]" 63 | echo "disable_control_app=[$disable_control_app]" 64 | echo "dns_server_list=[$dns_server_list]" 65 | echo "disable_app_security=[$disable_app_security]" 66 | echo "disable_web_security=[$disable_web_security]" 67 | echo "docker_image=[$docker_image]" 68 | 69 | if test -f $ENV_FILE; then 70 | truncate -s 0 $ENV_FILE 71 | fi 72 | 73 | if test -f $ENV_FILE; then 74 | truncate -s 0 $ENV_FILE 75 | fi 76 | 77 | if [[ -n ${friendly_name} ]]; then 78 | echo "Setting FRIENDLY_NAME to [$friendly_name]" 79 | echo "FRIENDLY_NAME=${friendly_name}" >> $ENV_FILE 80 | else 81 | echo "FRIENDLY_NAME not specified" 82 | fi 83 | 84 | if [[ -n ${model_name} ]]; then 85 | echo "Setting MODEL_NAME to [$model_name]" 86 | echo "MODEL_NAME=${model_name}" >> $ENV_FILE 87 | else 88 | echo "MODEL_NAME not specified" 89 | fi 90 | 91 | if [[ -n ${mqa_codec} ]]; then 92 | echo "Setting MQA_CODEC to [$mqa_codec]" 93 | echo "MQA_CODEC=${mqa_codec}" >> $ENV_FILE 94 | else 95 | echo "MQA_CODEC not specified" 96 | fi 97 | 98 | if [[ -n ${mqa_passthrough} ]]; then 99 | echo "Setting MQA_PASSTHROUGH to [$mqa_passthrough]" 100 | echo "MQA_PASSTHROUGH=${mqa_passthrough}" >> $ENV_FILE 101 | else 102 | echo "MQA_PASSTHROUGH not specified" 103 | fi 104 | 105 | if [[ -n ${restart_wait_sec} ]]; then 106 | echo "Setting RESTART_ON_FAIL to [$restart_wait_sec]" 107 | echo "RESTART_ON_FAIL=${restart_wait_sec}" >> $ENV_FILE 108 | else 109 | echo "RESTART_ON_FAIL not specified" 110 | fi 111 | 112 | if [[ -n ${sleep_time_sec} ]]; then 113 | echo "Setting SLEEP_TIME_SEC to [$sleep_time_sec]" 114 | echo "SLEEP_TIME_SEC=${sleep_time_sec}" >> $ENV_FILE 115 | else 116 | echo "SLEEP_TIME_SEC not specified" 117 | fi 118 | 119 | if [[ -n ${dns_server_list} ]]; then 120 | echo "Setting DNS_SERVER_LIST to [$sleep_time_sec]" 121 | echo "DNS_SERVER_LIST=${dns_server_list}" >> $ENV_FILE 122 | else 123 | echo "DNS_SERVER_LIST not specified" 124 | fi 125 | 126 | if [[ -n "${card_name}" ]]; then 127 | echo "Setting CARD_NAME to [$card_name]" 128 | echo "CARD_NAME=${card_name}" >> $ENV_FILE 129 | elif [[ -n "${card_index}" ]]; then 130 | echo "Setting CARD_INDEX to [$card_index]" 131 | echo "CARD_INDEX=${card_index}" >> $ENV_FILE 132 | echo "CARD_NAME=NOT_SET" >> $ENV_FILE 133 | fi 134 | 135 | if [[ -n "${card_device}" ]]; then 136 | echo "Setting CARD_DEVICE to [$card_device]" 137 | echo "CARD_DEVICE=${card_device}" >> $ENV_FILE 138 | fi 139 | 140 | if [[ -n "${card_format}" ]]; then 141 | echo "Setting CARD_FORMAT to [$card_format]" 142 | echo "CARD_FORMAT=${card_format}" >> $ENV_FILE 143 | fi 144 | 145 | if [[ -n "${enable_soft_volume}" ]]; then 146 | echo "Setting ENABLE_SOFTVOLUME to [$enable_soft_volume]" 147 | echo "ENABLE_SOFTVOLUME=${enable_soft_volume}" >> $ENV_FILE 148 | fi 149 | 150 | if [[ -n "${asound_file_prefix}" ]]; then 151 | echo "Setting ASOUND_FILE_PREFIX to [$asound_file_prefix]" 152 | echo "ASOUND_FILE_PREFIX=${asound_file_prefix}" >> $ENV_FILE 153 | fi 154 | 155 | if [[ -n "${force_playback_device}" ]]; then 156 | echo "Setting FORCE_PLAYBACK_DEVICE to [$force_playback_device]" 157 | echo "FORCE_PLAYBACK_DEVICE=${force_playback_device}" >> $ENV_FILE 158 | fi 159 | 160 | if [[ -n "${created_asound_card_name}" ]]; then 161 | echo "Setting CREATED_ASOUND_CARD_NAME to [$created_asound_card_name]" 162 | echo "CREATED_ASOUND_CARD_NAME=${created_asound_card_name}" >> $ENV_FILE 163 | fi 164 | 165 | if [[ -n "${client_it}" ]]; then 166 | echo "Setting CLIENT_ID to [$client_id]" 167 | echo "CLIENT_ID=${client_id}" >> $ENV_FILE 168 | fi 169 | 170 | if [[ -n "${log_level}" ]]; then 171 | echo "Setting LOG_LEVEL to [$log_level]" 172 | echo "LOG_LEVEL=${log_level}" >> $ENV_FILE 173 | fi 174 | 175 | if [[ -n "${certificate_path}" ]]; then 176 | echo "Setting CERTIFICATE_PATH to [$certificate_path]" 177 | echo "CERTIFICATE_PATH=${certificate_path}" >> $ENV_FILE 178 | fi 179 | 180 | if [[ -n "${disable_control_app}" ]]; then 181 | echo "Setting DISABLE_CONTROL_APP to [$disable_control_app]" 182 | echo "DISABLE_CONTROL_APP=${disable_control_app}" >> $ENV_FILE 183 | fi 184 | 185 | if [[ -n "${enable_generated_tone}" ]]; then 186 | echo "Setting ENABLE_GENERATED_TONE to [$enable_generated_tone]" 187 | echo "ENABLE_GENERATED_TONE=${enable_generated_tone}" >> $ENV_FILE 188 | fi 189 | 190 | if [[ -n "${disable_app_security}" ]]; then 191 | if [[ "${disable_app_security}" == "false" ]] || [[ "${disable_app_security}" == "true" ]]; then 192 | echo "Setting DISABLE_APP_SECURITY to [$disable_app_security]" 193 | echo "DISABLE_APP_SECURITY=${disable_app_security}" >> $ENV_FILE 194 | else 195 | echo "Invalid value for DISABLE_APP_SECURITY=[${disable_app_security}]" 196 | fi 197 | fi 198 | 199 | if [[ -n "${disable_web_security}" ]]; then 200 | if [[ "${disable_web_security}" == "false" ]] || [[ "${disable_web_security}" == "true" ]]; then 201 | echo "Setting DISABLE_WEB_SECURITY to [$disable_web_security]" 202 | echo "DISABLE_WEB_SECURITY=${disable_web_security}" >> $ENV_FILE 203 | else 204 | echo "Invalid value for DISABLE_WEB_SECURITY=[${DISABLE_WEB_SECURITY}]" 205 | fi 206 | fi 207 | 208 | if [[ -n "${docker_image}" ]]; then 209 | echo "Setting TIDAL_CONNECT_IMAGE to [$docker_image]" 210 | echo "TIDAL_CONNECT_IMAGE=${docker_image}" >> $ENV_FILE 211 | fi 212 | 213 | echo -e "\nFinal .env file:\n" 214 | cat .env 215 | 216 | -------------------------------------------------------------------------------- /bin/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Tidal Connect - https://github.com/GioF71/tidal-connect.git - entrypoint.sh version 0.2.0" 4 | 5 | mkdir -p /config 6 | 7 | source /common.sh 8 | 9 | # run configuration 10 | configure 11 | 12 | PLAYBACK_DEVICE=`get_playback_device` 13 | echo "PLAYBACK_DEVICE=[${PLAYBACK_DEVICE}]" 14 | 15 | friendly_name=`load_key_value $KEY_FRIENDLY_NAME` 16 | model_name=`load_key_value $KEY_MODEL_NAME` 17 | mqa_codec=`load_key_value $KEY_MQA_CODEC` 18 | mqa_passthrough=`load_key_value $KEY_MQA_PASSTHROUGH` 19 | 20 | if [[ -z "${DISABLE_CONTROL_APP}" ]] || [[ $DISABLE_CONTROL_APP -eq 0 ]]; then 21 | tmux_available=0 22 | control_app_available=0 23 | if [ -f /usr/bin/tmux ]; then 24 | tmux_available=1 25 | fi 26 | if [ -f /app/ifi-tidal-release/bin/speaker_controller_application ]; then 27 | control_app_available=1 28 | fi 29 | 30 | if [ $tmux_available -eq 1 ] && [ $control_app_available -eq 1 ]; then 31 | echo "Starting Speaker Application in Background (TMUX)" 32 | /usr/bin/tmux new-session -d -s speaker_controller_application '/app/ifi-tidal-release/bin/speaker_controller_application' 33 | echo "Sleeping for a while ($SLEEP_TIME_SEC seconds) ..." 34 | sleep $SLEEP_TIME_SEC 35 | else 36 | if [ $tmux_available -eq 0 ]; then 37 | echo "The tmux binary is not available." 38 | fi 39 | if [ $control_app_available -eq 0 ]; then 40 | echo "The Control application is not available." 41 | fi 42 | echo "The Control application or the tmux binary are not available, so we cannot start the control application." 43 | fi 44 | fi 45 | 46 | echo "ENABLE_GENERATED_TONE=[${ENABLE_GENERATED_TONE}]" 47 | tone_enabled=1 48 | if [[ -n "${ENABLE_GENERATED_TONE}" ]] && [[ "${ENABLE_GENERATED_TONE^^}" == "NO" || "${ENABLE_GENERATED_TONE^^}" == "N" ]]; then 49 | tone_enabled=0 50 | elif [[ -n "${ENABLE_GENERATED_TONE}" ]] && [[ "${ENABLE_GENERATED_TONE^^}" != "YES" && "${ENABLE_GENERATED_TONE^^}" != "Y" ]]; then 51 | echo "Invalid ENABLE_GENERATED_TONE=[$ENABLE_GENERATED_TONE]" 52 | exit 1 53 | else 54 | echo "Generated tone is enabled" 55 | fi 56 | 57 | executable_path=/app/ifi-tidal-release/bin/tidal_connect_application 58 | certificate_path=/app/ifi-tidal-release/id_certificate/IfiAudio_ZenStream.dat 59 | 60 | if [ -f "/assets/custom/bin/tidal_connect" ]; then 61 | echo "User provided tidal_connect app found." 62 | # copy 63 | mkdir -p /app/bin 64 | cp /assets/custom/bin/tidal_connect /app/bin 65 | # change permissions 66 | chmod +x /app/bin/tidal_connect 67 | # adopt this version 68 | executable_path=/app/bin/tidal_connect 69 | if [ -f "/assets/custom/bin/tidal_connect.dat" ]; then 70 | echo "User provided tidal_connect.dat file found." 71 | mkdir -p /app/bin 72 | cp /assets/custom/bin/tidal_connect.dat /app/bin 73 | certificate_path=/app/bin/tidal_connect.dat 74 | else 75 | echo "An user provided tidal_connect.dat file was not found." 76 | fi 77 | else 78 | echo "An user provided tidal_connect app was not found." 79 | # is there the fallback application in the image? 80 | if [ ! -f "$executable_path" ]; then 81 | echo "Cannot find fallback executable [$executable_path], this is a fatal error!" 82 | exit 1 83 | fi 84 | fi 85 | 86 | # is there a custom certificate? 87 | if [ -f "/assets/custom/certificate/tcon.crt" ]; then 88 | echo "User provided tcon.crt found." 89 | mkdir -p /app/cert 90 | cp /assets/custom/certificate/tcon.crt /app/cert 91 | chown root:root /app/cert/tcon.crt 92 | certificate_path=/app/cert/tcon.crt 93 | else 94 | echo "An user provided tcon.crt was not found." 95 | fi 96 | 97 | # has the certificate path been explicitly set? if so, we use that value 98 | if [[ -n "${CERTIFICATE_PATH}" ]]; then 99 | certificate_path=${CERTIFICATE_PATH} 100 | fi 101 | echo "certificate_path=[${certificate_path}]" 102 | 103 | if test -d /assets/custom/lib; then 104 | lib_cnt=`find /assets/custom/lib -type f | grep -v ".placeholder" | wc -l` 105 | echo "Lib count in /assets/custom/lib: $lib_cnt" 106 | if [ $lib_cnt -eq 0 ]; then 107 | echo "No custom libraries to inject to /usr/lib/ ..." 108 | else 109 | echo "Some custom libraries to inject to /usr/lib/ ..." 110 | for lib_name in /assets/custom/lib/*; do 111 | echo "Found library path [$lib_name], injecting to /usr/lib/ ..." 112 | cp $lib_name /usr/lib/ 113 | done 114 | fi 115 | else 116 | echo "Custom lib directory not found." 117 | fi 118 | 119 | if test -d /assets/custom/lib-arm-linux-gnueabihf; then 120 | lib_cnt=`find /assets/custom/lib-arm-linux-gnueabihf -type f | grep -v ".placeholder" | wc -l` 121 | echo "Lib count in /assets/custom/lib-arm-linux-gnueabihf: $lib_cnt" 122 | if [ $lib_cnt -eq 0 ]; then 123 | echo "No custom libraries to inject to /lib/arm-linux-gnueabihf/ ..." 124 | else 125 | echo "Some custom libraries to inject to /lib/arm-linux-gnueabihf/ ..." 126 | for lib_name in /assets/custom/lib-arm-linux-gnueabihf/*; do 127 | echo "Found library [$lib_name], injecting to /lib/arm-linux-gnueabihf/..." 128 | cp $lib_name /lib/arm-linux-gnueabihf/ 129 | done 130 | fi 131 | else 132 | echo "Custom arm-linux-gnueabihf directory not found." 133 | fi 134 | 135 | disable_app_security=false 136 | disable_web_security=true 137 | 138 | if [[ -n "${DISABLE_APP_SECURITY}" ]]; then 139 | if [[ "${DISABLE_APP_SECURITY}" == "false" ]] || [[ "${DISABLE_APP_SECURITY}" == "true" ]]; then 140 | disable_app_security=${DISABLE_APP_SECURITY} 141 | echo "DISABLE_APP_SECURITY=[${DISABLE_APP_SECURITY}]" 142 | else 143 | echo "Invalid value for DISABLE_APP_SECURITY=[${DISABLE_APP_SECURITY}]" 144 | exit 1 145 | fi 146 | fi 147 | 148 | if [[ -n "${DISABLE_WEB_SECURITY}" ]]; then 149 | if [[ "${DISABLE_WEB_SECURITY}" == "false" ]] || [[ "${DISABLE_WEB_SECURITY}" == "true" ]]; then 150 | disable_web_security=${DISABLE_WEB_SECURITY} 151 | echo "DISABLE_WEB_SECURITY=[${DISABLE_WEB_SECURITY}]" 152 | else 153 | echo "Invalid value for DISABLE_WEB_SECURITY=[${DISABLE_WEB_SECURITY}]" 154 | exit 1 155 | fi 156 | fi 157 | 158 | COMMAND_LINE="${executable_path} \ 159 | --tc-certificate-path ${certificate_path} \ 160 | --playback-device ${PLAYBACK_DEVICE} \ 161 | -f \"${friendly_name}\" \ 162 | --model-name \"${model_name}\" \ 163 | --codec-mpegh true \ 164 | --codec-mqa ${mqa_codec} \ 165 | --disable-app-security ${disable_app_security} \ 166 | --disable-web-security ${disable_web_security} \ 167 | --enable-mqa-passthrough ${mqa_passthrough} \ 168 | --log-level ${LOG_LEVEL} \ 169 | --enable-websocket-log \"0\"" 170 | 171 | if [[ -n "${CLIENT_ID}" ]]; then 172 | echo "Using user provided CLIENT_ID [${CLIENT_ID}]" 173 | COMMAND_LINE="${COMMAND_LINE} --clientid \"${CLIENT_ID}\"" 174 | else 175 | echo "User did not provide a CLIENT_ID." 176 | fi 177 | 178 | echo "COMMAND_LINE=${COMMAND_LINE}" 179 | 180 | while true 181 | do 182 | tone_skipped=0 183 | if [[ $tone_enabled -eq 1 ]]; then 184 | echo "Trying a short tone @ 48kHz ..." 185 | tone_played=0 186 | if aplay -D $PLAYBACK_DEVICE /assets/audio/short-low-tone-48k.wav; then 187 | echo "Success with 48 kHz tone." 188 | tone_played=1 189 | else 190 | echo "Failed with 48 kHz tone, trying 44.1 kHz ..." 191 | if aplay -D $PLAYBACK_DEVICE /assets/audio/short-low-tone-44k.wav; then 192 | echo "Success with 44.1 kHz tone." 193 | tone_played=1 194 | fi 195 | fi 196 | echo "tone_played=[$tone_played]" 197 | else 198 | tone_skipped=1 199 | echo "tone_skipped=[$tone_skipped]" 200 | fi 201 | if [[ $tone_played -eq 1 || $tone_skipped -eq 1 ]]; then 202 | echo "Starting TIDAL Connect ..." 203 | eval "${COMMAND_LINE}" 204 | echo "TIDAL Connect Container Stopped." 205 | else 206 | echo "Device locked/invalid, won't start the application ..." 207 | fi 208 | if [ $RESTART_ON_FAIL -eq 1 ]; then 209 | echo "Sleeping $RESTART_WAIT_SEC seconds before restarting ..." 210 | sleep $RESTART_WAIT_SEC 211 | else 212 | echo "RESTART_ON_FAIL=$RESTART_ON_FAIL, exiting." 213 | break 214 | fi 215 | done -------------------------------------------------------------------------------- /bin/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Tidal Connect - https://github.com/GioF71/tidal-connect.git - common.sh version 0.1.7" 4 | 5 | # some constants 6 | ASOUND_CONF_SIMPLE_FILE=asound.conf 7 | ASOUND_CONF_FILE=/etc/$ASOUND_CONF_SIMPLE_FILE 8 | USER_CONFIG_DIR=/userconfig 9 | 10 | KEY_PLAYBACK_DEVICE=playback_device 11 | KEY_FORCE_PLAYBACK_DEVICE=force_playback_device 12 | KEY_ASOUND_CONF_EXISTS=asound_conf_exists 13 | KEY_ASOUND_CONF_WRITABLE=asound_conf_writable 14 | 15 | KEY_FRIENDLY_NAME=friendly_name 16 | KEY_MODEL_NAME=model_name 17 | KEY_MQA_CODEC=mqa_codec 18 | KEY_MQA_PASSTHROUGH=mqa_passthrough 19 | 20 | save_key_value() { 21 | V_KEY=$1 22 | V_VALUE=$2 23 | echo "${V_VALUE}" > /config/$V_KEY 24 | } 25 | 26 | load_key_value() { 27 | V_KEY=$1 28 | if [ -f /config/$V_KEY ]; then 29 | cat /config/$V_KEY 30 | fi 31 | } 32 | 33 | save_playback_device() { 34 | save_key_value $KEY_PLAYBACK_DEVICE $1 35 | } 36 | 37 | get_playback_device() { 38 | load_key_value $KEY_PLAYBACK_DEVICE 39 | } 40 | 41 | save_asound_conf_exists() { 42 | save_key_value $KEY_ASOUND_CONF_EXISTS $1 43 | } 44 | 45 | get_asound_conf_exists() { 46 | load_key_value $KEY_ASOUND_CONF_EXISTS 47 | } 48 | 49 | save_asound_conf_writable() { 50 | save_key_value $KEY_ASOUND_CONF_WRITABLE $1 51 | } 52 | 53 | get_asound_conf_writable() { 54 | load_key_value $KEY_ASOUND_CONF_WRITABLE 55 | } 56 | 57 | dump_asound() { 58 | # dump current asound.conf if it exists 59 | asound_conf_exists=`get_asound_conf_exists` 60 | if [ $asound_conf_exists -eq 1 ]; then 61 | echo "Current $ASOUND_CONF_FILE:" 62 | cat $ASOUND_CONF_FILE 63 | fi 64 | } 65 | 66 | dump_final_asound() { 67 | if [[ -f "$ASOUND_CONF_FILE" ]]; then 68 | cat $ASOUND_CONF_FILE 69 | else 70 | echo "File [$ASOUND_CONF_FILE] not found." 71 | fi 72 | } 73 | 74 | display_variables() { 75 | echo "FRIENDLY_NAME=$FRIENDLY_NAME" 76 | echo "MODEL_NAME=$MODEL_NAME" 77 | echo "MQA_CODEC=$MQA_CODEC" 78 | echo "MQA_PASSTHROUGH=$MQA_PASSTHROUGH" 79 | echo "CARD_NAME=$CARD_NAME" 80 | echo "CARD_INDEX=$CARD_INDEX" 81 | echo "CARD_DEVICE=$CARD_DEVICE" 82 | echo "CARD_FORMAT=$CARD_FORMAT" 83 | echo "CREATED_ASOUND_CARD_NAME=$CREATED_ASOUND_CARD_NAME" 84 | echo "ENABLE_SOFTVOLUME=$ENABLE_SOFTVOLUME" 85 | echo "ENABLE_GENERATED_TONE=$ENABLE_GENERATED_TONE" 86 | echo "ASOUND_FILE_PREFIX=$ASOUND_FILE_PREFIX" 87 | echo "FORCE_PLAYBACK_DEVICE=$FORCE_PLAYBACK_DEVICE" 88 | echo "SLEEP_TIME_SEC=$SLEEP_TIME_SEC" 89 | echo "RESTART_ON_FAIL=$RESTART_ON_FAIL" 90 | echo "RESTART_WAIT_SEC=$RESTART_WAIT_SEC" 91 | echo "CLIENT_ID=$CLIENT_ID" 92 | echo "LOG_LEVEL=$LOG_LEVEL" 93 | } 94 | 95 | set_defaults() { 96 | # we initially assume that asound.conf does not exist 97 | save_asound_conf_exists 0 98 | # we initially assume that asound.conf is writable 99 | save_asound_conf_writable 1 100 | DEFAULT_FRIENDLY_NAME="Tidal connect" 101 | DEFAULT_MODE_NAME="Audio Streamer" 102 | DEFAULT_MQA_CODEC="false" 103 | DEFAULT_MQA_PASSTHROUGH="false" 104 | if [[ -z "${FRIENDLY_NAME}" ]]; then 105 | FRIENDLY_NAME="${DEFAULT_FRIENDLY_NAME}" 106 | fi 107 | save_key_value $KEY_FRIENDLY_NAME "${FRIENDLY_NAME}" 108 | if [[ -z "${MODEL_NAME}" ]]; then 109 | MODEL_NAME="${DEFAULT_MODE_NAME}" 110 | fi 111 | save_key_value $KEY_MODEL_NAME "${MODEL_NAME}" 112 | if [[ -z "${MQA_CODEC}" ]]; then 113 | MQA_CODEC="${DEFAULT_MQA_CODEC}" 114 | fi 115 | save_key_value $KEY_MQA_CODEC $MQA_CODEC 116 | if [[ -z "${MQA_PASSTHROUGH}" ]]; then 117 | MQA_PASSTHROUGH="${DEFAULT_MQA_PASSTHROUGH}" 118 | fi 119 | save_key_value $KEY_MQA_PASSTHROUGH $MQA_PASSTHROUGH 120 | PLAYBACK_DEVICE=default 121 | save_playback_device $PLAYBACK_DEVICE 122 | if [[ -n "${FORCE_PLAYBACK_DEVICE}" ]]; then 123 | save_key_value $KEY_FORCE_PLAYBACK_DEVICE $FORCE_PLAYBACK_DEVICE 124 | fi 125 | } 126 | 127 | write_audio_config() { 128 | card_index=$1 129 | echo "Entering write_audio_config with card_index=[$card_index] ..." 130 | asound_conf_exists=`get_asound_conf_exists` 131 | asound_conf_writable=`get_asound_conf_writable` 132 | if [[ $asound_conf_exists -eq 0 ]] || [[ $asound_conf_writable -eq 1 ]]; then 133 | if test -f "$ASOUND_CONF_FILE"; then 134 | truncate -s 0 "$ASOUND_CONF_FILE" 135 | fi 136 | echo "Creating sound configuration file (card_index=[$card_index], softvol=[$ENABLE_SOFTVOLUME]) ..." 137 | enable_soft_volume=0 138 | softvolume_card_name=Master 139 | if [[ "${ENABLE_SOFTVOLUME^^}" == "YES" || "${ENABLE_SOFTVOLUME^^}" == "Y" ]]; then 140 | # check there is no Master already 141 | check_master=`amixer -c $card_index controls | grep \'${softvolume_card_name}\'` 142 | if [[ -z "${check_master}" ]]; then 143 | echo "Ok to enable softvolume, as no '${softvolume_card_name}' control exists for the device at index [$card_index]" 144 | enable_soft_volume=1 145 | else 146 | echo "check_master=[${check_master}]" 147 | echo "A control named [${softvolume_card_name}] already exists for the device at index [$card_index]" 148 | softvolume_card_name="SoftMaster" 149 | enable_soft_volume=1 150 | if [ $enable_soft_volume -eq 1 ]; then 151 | echo "A softvolume control will be created for the device at index [$card_index] using name [${softvolume_card_name}]" 152 | echo "*WARNING* Tidal volume slider might act on the hardware volume control" 153 | echo "*WARNING* If you don't want this, consider disabling software volume" 154 | fi 155 | fi 156 | elif [[ -n "${ENABLE_SOFTVOLUME}" ]] && [[ "${ENABLE_SOFTVOLUME^^}" != "NO" && "${ENABLE_SOFTVOLUME^^}" != "N" ]]; then 157 | echo "Invalid ENABLE_SOFTVOLUME=[${ENABLE_SOFTVOLUME}]" 158 | exit 1 159 | fi 160 | if [[ $enable_soft_volume -eq 0 ]]; then 161 | echo "Building asound.conf without softvolume ..." 162 | if [[ -n "${CREATED_ASOUND_CARD_NAME}" ]]; then 163 | echo "pcm.$CREATED_ASOUND_CARD_NAME {" >> /etc/asound.conf 164 | save_playback_device $CREATED_ASOUND_CARD_NAME 165 | else 166 | echo "pcm.!default {" >> /etc/asound.conf 167 | save_playback_device default 168 | fi 169 | echo " type plug" >> /etc/asound.conf 170 | echo " slave.pcm {" >> /etc/asound.conf 171 | echo " type hw" >> /etc/asound.conf 172 | echo " card ${card_index}" >> /etc/asound.conf 173 | if [[ -n "${CARD_DEVICE}" ]]; then 174 | echo " device $CARD_DEVICE" >> /etc/asound.conf 175 | fi 176 | if [[ -n "${CARD_FORMAT}" ]]; then 177 | echo " format $CARD_FORMAT" >> /etc/asound.conf 178 | fi 179 | echo " }" >> /etc/asound.conf 180 | echo "}" >> /etc/asound.conf 181 | elif [[ $enable_soft_volume -eq 1 ]]; then 182 | echo "Building asound.conf with softvolume ..." 183 | # create plug device first 184 | echo "pcm.tidal-audio-device {" >> /etc/asound.conf 185 | echo " type plug" >> /etc/asound.conf 186 | echo " slave.pcm {" >> /etc/asound.conf 187 | echo " type hw" >> /etc/asound.conf 188 | echo " card ${card_index}" >> /etc/asound.conf 189 | if [[ -n "${CARD_DEVICE}" ]]; then 190 | echo " device $CARD_DEVICE" >> /etc/asound.conf 191 | fi 192 | if [[ -n "${CARD_FORMAT}" ]]; then 193 | echo " format $CARD_FORMAT" >> /etc/asound.conf 194 | fi 195 | echo " }" >> /etc/asound.conf 196 | echo "}" >> /etc/asound.conf 197 | # add softvol device 198 | echo "pcm.tidal-softvol {" >> /etc/asound.conf 199 | echo " type softvol" >> /etc/asound.conf 200 | echo " slave {" >> /etc/asound.conf 201 | echo " pcm \"tidal-audio-device\"" >> /etc/asound.conf 202 | echo " }" >> /etc/asound.conf 203 | echo " control {" >> /etc/asound.conf 204 | echo " name \"${softvolume_card_name}\"" >> /etc/asound.conf 205 | echo " card 0" >> /etc/asound.conf 206 | echo " }" >> /etc/asound.conf 207 | echo "}" >> /etc/asound.conf 208 | save_playback_device tidal-softvol 209 | echo "Setting PLAYBACK_DEVICE=[tidal-softvol]" 210 | else 211 | echo "Invalid ENABLE_SOFTVOLUME=${ENABLE_SOFTVOLUME}" 212 | fi 213 | echo "Sound configuration file created" 214 | else 215 | echo "Cannot create file [$ASOUND_CONF_FILE]: Exists [$asound_conf_exists] Writable [$asound_conf_writable]" 216 | fi 217 | echo "Completed write_audio_config" 218 | } 219 | 220 | check_provided_asound() { 221 | ## see if there is a user-provided asound.conf file 222 | select_asound_file="$USER_CONFIG_DIR/$ASOUND_CONF_SIMPLE_FILE" 223 | if [[ -n "${ASOUND_FILE_PREFIX}" ]]; then 224 | select_asound_file="$USER_CONFIG_DIR/$ASOUND_FILE_PREFIX.$ASOUND_CONF_SIMPLE_FILE" 225 | fi 226 | if [ -f "$select_asound_file" ]; then 227 | echo "File [$select_asound_file] has been provided, copying to [$ASOUND_CONF_FILE] ..." 228 | cp $select_asound_file /etc/asound.conf 229 | # make it read-only 230 | chmod -w $ASOUND_CONF_FILE 231 | save_asound_conf_exists 1 232 | save_asound_conf_writable 0 233 | # set PLAYBACK_DEVICE to [custom] if not set 234 | force_playback_device=`load_key_value $KEY_FORCE_PLAYBACK_DEVICE` 235 | if [[ -z "${force_playback_device}" ]]; then 236 | echo "FORCE_PLAYBACK_DEVICE empty, setting to [custom]" 237 | save_key_value $KEY_FORCE_PLAYBACK_DEVICE custom 238 | else 239 | echo "FORCE_PLAYBACK_DEVICE is already set to [$force_playback_device], leaving as-is" 240 | fi 241 | else 242 | echo "File [$ASOUND_CONF_SIMPLE_FILE] has not been provided" 243 | if [ -f "$ASOUND_CONF_FILE" ]; then 244 | echo "File $ASOUND_CONF_FILE exists." 245 | #ASOUND_CONF_EXISTS=1 246 | save_asound_conf_exists 1 247 | else 248 | echo "File $ASOUND_CONF_FILE does not exist." 249 | fi 250 | asound_conf_exists=`get_asound_conf_exists` 251 | if [ $asound_conf_exists -eq 1 ]; then 252 | # check if file is writable 253 | if [ -w "$ASOUND_CONF_FILE" ]; then 254 | echo "File $ASOUND_CONF_FILE is writable" 255 | save_asound_conf_writable 1 256 | else 257 | echo "File $ASOUND_CONF_FILE is NOT writable" 258 | save_asound_conf_writable 0 259 | fi 260 | fi 261 | fi 262 | } 263 | 264 | write_asound_if_needed() { 265 | echo "Entering write_asound_if_needed ..." 266 | card_index=$CARD_INDEX 267 | card_name=$CARD_NAME 268 | asound_conf_exists=`get_asound_conf_exists` 269 | asound_conf_writable=`get_asound_conf_writable` 270 | if [[ $asound_conf_exists -eq 0 ]] || [[ $asound_conf_writable -eq 1 ]]; then 271 | if [[ -z "${card_index}" || "${card_index}" == "-1" ]] && [[ -n "${card_name}" ]]; then 272 | # card name is set 273 | echo "Specified CARD_NAME=[$card_name]" 274 | aplay -l | sed 1d | \ 275 | while read i 276 | do 277 | first_word=`echo $i | cut -d " " -f 1` 278 | if [[ "${first_word}" == "card" ]]; then 279 | second_word=`echo $i | cut -d ":" -f 1` 280 | third_word=`echo $i | cut -d " " -f 3` 281 | curr_ndx=`echo $second_word | cut -d " " -f 2` 282 | if [[ "${third_word}" == "${CARD_NAME}" ]]; then 283 | echo "Found audio device [${CARD_NAME}] as index [$curr_ndx]" 284 | CARD_INDEX=$curr_ndx 285 | write_audio_config $curr_ndx 286 | break 287 | else 288 | echo "Skipping audio device [${third_word}] at index [$curr_ndx]" 289 | fi 290 | fi 291 | done 292 | elif [[ -n "${card_index}" && ! "${card_index}" == "-1" ]]; then 293 | # card index is set 294 | echo "Specified CARD_INDEX=[$card_index]" 295 | echo "Set card_index=[$card_index]" 296 | write_audio_config $card_index 297 | else 298 | echo "using sysdefault ..." 299 | save_playback_device sysdefault 300 | fi 301 | else 302 | echo "File [$ASOUND_CONF_FILE] cannot be modified." 303 | fi 304 | echo "Completed write_asound_if_needed." 305 | } 306 | 307 | enforce_playback_device_if_requested() { 308 | #echo "Entering enforce_playback_device_if_requested ..." 309 | force_playback_device=`load_key_value $KEY_FORCE_PLAYBACK_DEVICE` 310 | if [[ -n "${force_playback_device}" ]]; then 311 | echo "Setting playback device to [$force_playback_device] ..." 312 | save_playback_device $force_playback_device 313 | fi 314 | #echo "Completed enforce_playback_device_if_requested, PLAYBACK_DEVICE=[$pd_enf_ref]" 315 | } 316 | 317 | configure() { 318 | set_defaults 319 | display_variables 320 | check_provided_asound 321 | dump_asound 322 | write_asound_if_needed 323 | dump_final_asound 324 | enforce_playback_device_if_requested 325 | } 326 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tidal-connect 2 | 3 | A simple script used to configure a docker-compose stack for Tidal Connect, using an existing docker image. 4 | This repository does not contain any tidal-connect binary. 5 | 6 | ## Support 7 | 8 | [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/H2H7UIN5D) 9 | Please see the [Goal](https://ko-fi.com/giof71/goal?g=0) 10 | Please note that support goal is limited to cover running costs for subscriptions to music services. 11 | 12 | ## Known devices 13 | 14 | See the new document [known devices](assets/known-devices.md) for a list of known working devices. 15 | 16 | ## Avahi and friendly names 17 | 18 | Some users experienced issues using a friendly name which is composed of multiple words. See [this post](https://github.com/GioF71/tidal-connect/issues/216#issuecomment-2684176691). 19 | I have changed the default for `FRIENDLY_NAME` accordingly. You might want to avoid using friendly names with multiple words if you experience similar issues. 20 | 21 | ## Issue on the Raspberry Pi 5 22 | 23 | If you use a Raspberry Pi 5, you might encounter an issue that shows with the following content in the container logs: 24 | 25 | `/app/ifi-tidal-release/bin/tidal_connect_application: error while loading shared libraries: libsystemd.so.0: ELF load command alignment not page-aligned` 26 | 27 | This issue can be solved by editing the file `/boot/firmware/config.txt` using the following command: 28 | 29 | `sudo nano /boot/firmware/config.txt` 30 | 31 | Add the following line in the `all` section: 32 | 33 | `kernel=kernel8.img` 34 | 35 | You will need to reboot, then restart the container. 36 | 37 | ## News 38 | 39 | ### Support for building your own image 40 | 41 | You can now build a custom image, which will be based on [debian:bookworm-slim](https://hub.docker.com/_/debian/tags?name=bookworm-slim). 42 | In order to build the image, change the current directory to `build`, then execute: 43 | 44 | ```text 45 | docker build . -t my/tidal-connect 46 | ``` 47 | 48 | or execute the provided `local-build.sh` using 49 | 50 | ```text 51 | ./local-build.sh 52 | ``` 53 | 54 | The resulting image will be tagged as `my/tidal-connect`. You will have to set the variable `TIDAL_CONNECT_IMAGE` to this image name if you want to use it instead of the default docker image. 55 | Note that the image you build this way will not contain the tidal binaries. Refer to the following readme files in the [bin](https://github.com/GioF71/tidal-connect/blob/main/assets/custom/bin/README.md), [certificate](https://github.com/GioF71/tidal-connect/blob/main/assets/custom/certificate/README.md), [lib](https://github.com/GioF71/tidal-connect/blob/main/assets/custom/lib/README.md) and [lib-arm-linux-gnueabihf](https://github.com/GioF71/tidal-connect/blob/main/assets/custom/lib-arm-linux-gnueabihf/README.md). 56 | 57 | ### MQA content is gone 58 | 59 | At the end of July 2024, Tidal has removed all the MQA content. This particular implementation of Tidal Connect could play hi-res files only up to 24/48 and MQA content: in the latter case, an unfolding to 24/88 or 24/96 was implemented by the application itself, while the further processing was delegated to a MQA capable DAC. 60 | As far as I know, content above 16/44 available as HI_RES quality (so 24/44 or 24/48) is currently inexistent on Tidal, so I guess we are now stuck to standard redbook resolution (16/44). 61 | I don't consider this limitation a show-stopper. Some people might miss MQA, and that is totally fine for me. On the bright side, at least from now on, we don't have to worry about this controversial format to be detrimental to the audio quality, as some people said. 62 | The hires content from Tidal is not unavailable to some legacy streamers, including this (now quite outdated) implementation of Tidal Connect. Some of the [alternatives mentioned below](#alternatives) currently support Tidal HiRes streaming and will be able to play content up to 24/192. 63 | 64 | ### Presets 65 | 66 | I am trying to document all the configurations I have encountered with my own dacs and through your tickets. Look [here](https://github.com/GioF71/tidal-connect/blob/main/userconfig/README.md) and [here](https://github.com/GioF71/tidal-connect/blob/main/samples/README.md). 67 | Look at the [samples](https://github.com/GioF71/tidal-connect/tree/main/samples) and [userconfig](https://github.com/GioF71/tidal-connect/tree/main/userconfig) directories. They might contain a good file for your dac. It something for you isn't already there, open an issue, and we will create a sample file for your dac, which will also help other users. 68 | 69 | ### A nice alternative 70 | 71 | Also, consider the new [mopidy-tidal](https://github.com/tehkillerbee/mopidy-tidal) alternative. Some things are not on par with the full-fledged web player or mobile app, but the developer is excellent, and I believe things are going to improve. 72 | Visit [this page](https://github.com/GioF71/audio-tools/tree/main/players/mopidy-tidal) for an example configuration using [my docker image for mopidy](https://github.com/GioF71/mopidy-docker). You will also find the references to the underlying projects. 73 | 74 | ## Disclaimers 75 | 76 | ### Contents 77 | 78 | This repository does not contain the Tidal Connect software. There is only a docker-compose.yaml file, a configurator script, and a modified entrypoint which is just used for selecting the desired audio output more easily and reliably. 79 | 80 | ### Credit 81 | 82 | All the hard work has been done by the owner of the repository mentioned in the [References](#references) section and in the other repositories from which his one has been forked. I am just trying to provide a way to run their container more easily in certain environments (where the index of your audio device is not the same on every restart). 83 | 84 | ### No support from Tidal 85 | 86 | This solution is not and will probably never be supported by Tidal. It might stop working at any time. 87 | 88 | #### Alternatives 89 | 90 | If you need to have a supported solution, look at Tidal Connect enabled products. A cheap one is represented by the ubiquitous Wiim Mini/Pro/Pro+/Amp/AmpPro/Ultra devices. Another option is represented by Google Chromecast/Chromecast Audio devices. Also, if you use Apple devices, you can already stream to AirPlay-enabled devices. 91 | Even your current TV might already be used as an endpoint for Tidal via the embedded Chromecast functionality, or via AirPlay. 92 | 93 | ##### UPnP control points with Tidal support 94 | 95 | Alternatively, if you are not scared of some DIY, you might want to create an UPnP/DLNA renderer, maybe with upmpdcli (you might use my [docker image for upmpdcli](https://github.com/GioF71/upmpdcli-docker)) and mpd (you might use my [docker image for mpd](https://github.com/GioF71/mpd-alsa-docker)), and then use some Android app like BubbleUPnP or mConnect Lite (this one is also on iOS/iPadOS), connect those apps to Tidal and then stream to your UPnP/DLNA renderer. 96 | Starting late November 2023, BubbleUPnP supports hires flac, see [this post on Reddit](https://www.reddit.com/r/TIdaL/comments/184gcsq/bubbleupnp_android_app_now_supports_hires_flac/). This is something that, from what I know, is not supported by this Tidal Connect application, and will probably never be. 97 | 98 | ##### Tidal Plugin for upmpdcli 99 | 100 | Another solution might be my [Tidal Plugin for Upmpdcli](https://github.com/GioF71/upmpdcli-docker/discussions/281) but again, there will be no support from Tidal. This solution works very well with Tidal LOSSLESS and HI_RES_LOSSLESS. 101 | This solution supports native HiRes flac playback (HI_RES_LOSSLESS so up to 24/192) to a few well-known player types. However, it will work in LOSSLESS quality (16/44) on pretty much any UPnP renderer. 102 | When configured for HI_RES_LOSSLESS audio quality, only a few (whitelisted) players will be able to actally play HI_RES_LOSSLESS streams (so, better than 16/44, up to 24/192): 103 | 104 | - Music Player Daemon + upmpdcli 105 | - gmrender-resurrect 106 | - some WiiM devices, for sure the WiiM Pro (which I own), most likely the WiiM Pro Plus, possibly others. 107 | 108 | Other players will be served with 16/44 streams for compatibility. 109 | But more UPnP/DLNA streamers might work properly in HI_RES_LOSSLESS mode, as well. If owners of commercial streamer with DLNA support are willing to test the solution with whitelisting disabled, we might extend the whitelist. 110 | Eversolo maybe? Cambridge Audio? Lumin? Owners of any streamer are welcome to try and report their experience. 111 | See [here](https://github.com/GioF71/audio-tools/tree/main/media-servers/tidal-hires) for a configuration for the HI_RES_LOSSLESS enabled media server, as well as details for running the server without the whitelist. 112 | As a final note: why a Tidal Plugin for upmpdcli? Aren't BubbleUPnP and mConnect available already, and don't they support Tidal? Yes they do, but this solution covers a niche use-case, the desktop user with a streamer on its working desk. 113 | With this plugin, you will be able to play Tidal to your streamer using [upplay](https://www.lesbonscomptes.com/upplay/), a control point which runs on Windows, MacOS and Linux, without having to use the phone or tablet in order to stream from Tidal. 114 | 115 | ##### Logitech Media Server 116 | 117 | Another alternative is using [Lyrion Music Server](https://lyrion.org/) and Squeezelite players, for which you might use my [docker image for squeezelite](https://github.com/GioF71/squeezelite-docker). 118 | In February 2024 the `mysqueezebox` online services have been [shut down](https://forums.lyrion.org/forum/user-forums/mysqueezebox-com/1668754-squeezebox-display-please-follow-the-qr-code-logitech-migration-announcement), there is a new, very nice plugin [here](https://github.com/michaelherger/lms-plugin-tidal) by community heroes [michaelherger](https://github.com/michaelherger) and [philippe44](https://github.com/philippe44). This solution does not currently support Tidal HiRes while it supports standard LOSSLESS streams (so, again, up to 16/44). 119 | The solution used to support MQA encoded streams (hence you might find a setting to enable HI_RES), but that kind content is now gone, so (I believe) that the setting is currently not too relevant anymore. YMMV. 120 | 121 | ##### Mopidy-Tidal 122 | 123 | There is a new plugin for [Mopidy](https://mopidy.com/) called [Mopidy-Tidal](https://github.com/tehkillerbee/mopidy-tidal), which enabled Mopidy to stream for Tidal with full support for HiRes streams. 124 | I have prepared a docker container image for this solution [here](https://github.com/GioF71/mopidy-docker) with a strong focus on this plugin especially. 125 | A simple configuration can be found [here](https://github.com/GioF71/audio-tools/tree/main/players/mopidy-tidal). 126 | 127 | ##### Music Assistant 128 | 129 | [Music Assistant](https://www.music-assistant.io/) is an open source solution which support playback from Tidal, even in HiRes mode, to a variety of devices, notably Squeezelite, AirPlay, Chromecast, UPnP, see the [Player Providers](https://www.music-assistant.io/player-support/) section. 130 | Of course it can manage your local library as well. See the [Music Providers](https://www.music-assistant.io/music-providers/) section. 131 | You can run it as a standalone application or within [Home Assistant](https://www.home-assistant.io/). 132 | I am starting using this solution, and I must say, I am impressed. 133 | 134 | ##### Audirvana 135 | 136 | A commercial solution, [Audirvana Studio](https://audirvana.com/), allows you to stream from Tidal to UPnP renderers. 137 | I maintain docker images for [Audirvana Studio](https://github.com/GioF71/audirvana-docker) and [here](https://github.com/GioF71/audio-tools/tree/main/players/audirvana-upnp) you can find a simple configuration for using mpd and upmpdcli as a player for Audirvana. 138 | 139 | ##### Roon 140 | 141 | [Roon](https://roon.app/) is another commercial solution which can allow you to stream from Tidal. 142 | You can build an endpoint using [Roon Bridge](https://help.roonlabs.com/portal/en/kb/articles/roonbridge). 143 | A dockerized solution is [here](https://github.com/GioF71/roon-bridge-docker). 144 | 145 | ## References 146 | 147 | The contents of this repository entirely rely on [this](https://github.com/TonyTromp/tidal-connect-docker) repository from [GitHub user TonyTrump](https://github.com/TonyTromp). A big thank you to the author for the great work. 148 | It will also use [his docker image](https://hub.docker.com/r/edgecrush3r/tidal-connect). 149 | 150 | ## Why 151 | 152 | I created this repository because it was very inconvenient for me to figure out the string to put for the PLAYBACK_DEVICE variable. I even failed for one of my DAC, I don't know honestly why, but as far as I understand, the `ifi-pa-devs-get` for some reason refuses to see the device. The most relevant issue I found about this issue is [this](https://github.com/TonyTromp/tidal-connect-docker/issues/23), but it does not seems to have an easy solution. 153 | Additionally, even if you can specify the correct string for your DAC, I found that the resulting configuration would be error-prone as the string reports both the device name and the device index. AFAIK the index can change across restarts, so outside of a known and controlled setup (which is probably represented by the I*i devices) this situation can an will lead to errors or unwanted configurations. 154 | Keep in mind that the audio device index can also change because one time the (usb) device is already powered on during boot, and some other time it isn't. 155 | This is my experience, unless I am missing something obvious. If so, I will be glad to be corrected. 156 | The work in this repository consists in slightly altering the container startup phase (the `entrypoint.sh` file), in such a way that a custom `/etc/asound.conf` is created with (hopefully) the correct device index, regardless of the order of the audio devices, which can vary across restarts. The underlying application then always uses the `default` audio device. 157 | 158 | ## Requirements 159 | 160 | You will need a single-board computer (or anyway, a computer) with an armhf architecture (arm64 should work as well), running `docker` and `docker-compose`. 161 | A Raspberry Pi 3/4 will work. If you plan to use a usb dac and hi-res audio, consider at least using a Pi 3b+ or, even better, a Pi 4b. 162 | I am also running this on as Asus Tinkerboard. With this hardware, my suggestion is to not allow it to scale down the cpu frequency too much, or you might experience every kind of crackling noises along with what will remain of your music. In my experience, I am having good result if I set the minimum frequency at least about 600MHz, but, of course, YMMV. 163 | 164 | ## Usage 165 | 166 | ### Install Docker 167 | 168 | Docker is a prerequisite. On debian and derived distributions (this includes Raspberry Pi OS, DietPi, Moode Audio, Volumio), we can install the necessary packages using the following commands: 169 | 170 | ```text 171 | sudo apt-get update 172 | sudo apt-get install docker.io docker-compose 173 | sudo usermod -a -G docker $USER 174 | newgrp docker 175 | ``` 176 | 177 | The third line command adds the current user to the docker group. This is not mandatory; if you choose to skip this step, you might need to execute docker-compose commands by prepending `sudo`. 178 | The last line will login to the group named `docker`. The previous command (`usermod`) is of course required in order for the `newgrp` instruction to be successful. 179 | 180 | ### Clone the repository 181 | 182 | You need to clone the repository. Make sure that `git` is installed using the following command on debian and derived distributions (again, this includes Raspberry Pi OS, DietPi, Moode Audio, Volumio): 183 | 184 | ```code 185 | sudo apt-get update 186 | sudo apt-get install -y git 187 | ``` 188 | 189 | Move to the your home directory and clone the repository using the commands: 190 | 191 | ```code 192 | cd 193 | git clone https://github.com/GioF71/tidal-connect.git 194 | ``` 195 | 196 | ### Update the repository 197 | 198 | If you just downloaded the repository, you can skip this step. 199 | If you previously cloned the repository, it might have been updated in the meantime. Move to the directory and pull the changes: 200 | 201 | ```code 202 | cd $HOME/tidal-connect 203 | git config pull.rebase false 204 | git pull 205 | ``` 206 | 207 | ### Configure 208 | 209 | From the repository directory, just run the `configure.sh` bash script, specifying the following parameters: 210 | 211 | PARAM|DESCRIPTION|VARIABLE 212 | :---|:---|:--- 213 | -n|Sound card name (e.g. DAC), if not specified and also card index isn't, `sysdefault` is used|CARD_NAME 214 | -i|Sound card index, not recommended: if not specified and also card name isn't, `sysdefault` is used|CARD_INDEX 215 | -d|Sound card device, optional|CARD_DEVICE 216 | -s|Card format, optional (`S32_LE`, `S16_LE`, etc)|CARD_FORMAT 217 | -l|Enables softvolume, defaults to `yes`|ENABLE_SOFTVOLUME 218 | -g|Enables generated tone, defaults to `yes`|ENABLE_GENERATED_TONE 219 | -f|Friendly name, defaults to `TidalConnect`|FRIENDLY_NAME 220 | -m|Model name, defaults to `Audio Streamer`|MODEL_NAME 221 | -c|MQA Codec, defaults to `false`|MQA_CODEC 222 | -p|MQA Passthrough, defaults to `false`|MQA_PASSTHROUGH 223 | -r|Asound file prefix|ASOUND_FILE_PREFIX 224 | -o|Force playback device|FORCE_PLAYBACK_DEVICE 225 | -a|Name of the virtual sound card in the generated asound.conf file|CREATED_ASOUND_CARD_NAME 226 | -t|Sleep time in seconds be, defaults to `3`|SLEEP_TIME_SEC 227 | -w|Restart wait time in seconds, defaults to `10`|RESTART_WAIT_SEC 228 | -e|Custom client id, defaults to empty string|CLIENT_ID 229 | -b|log level, defaults to `3`|LOG_LEVEL 230 | -h|override default certificate path|CERTIFICATE_PATH 231 | -j|Disable control app if set to `1`, defaults to `0`|DISABLE_CONTROL_APP 232 | -v|DNS Server list, defaults to `8.8.8.8 8.8.4.4` (Google's DNS servers)|DNS_SERVER_LIST 233 | -k|Disable app security, defaults for false|DISABLE_APP_SECURITY 234 | -q|Disable web security, defaults to true|DISABLE_WEB_SECURITY 235 | 236 | I recommend to use the `-n` parameter instead of `-i`, because the index of the devices might change across restarts. 237 | If you already used the `configure.sh` command and you are experiencing issues (because of the card has changed its index), you can run the command again. In the latest version, the card index is calculated during the container startup phase and hopefully there will not be any need to use `configure.sh` again unless you change the audio device you want to use. 238 | 239 | #### Example 240 | 241 | Configure for sound card named "DAC", using friendly name "Aune S6 USB DAC" and model name "Asus Tinkerboard": 242 | 243 | ```text 244 | cd $HOME/tidal-connect 245 | bash configure.sh -n DAC -f "Aune S6 USB DAC" -m "Asus Tinkerboard" 246 | ``` 247 | 248 | If no error is reported, you will find a new (or updated) `.env` file. 249 | If you find a spurious `.asound.conf` file there, it probably was generated by a previous version of the `configure.sh` script, and you can safely delete it. 250 | So now you can run the `docker-compose.yaml` as usual: 251 | 252 | ```text 253 | cd $HOME/tidal-connect 254 | docker-compose up -d 255 | ``` 256 | 257 | #### Some known devices 258 | 259 | Please refer to the [known devices](assets/known-devices.md) document for a list of known working devices. 260 | 261 | ## Environment Variables 262 | 263 | The container can be entirely configured using the environment variables listed on the following table: 264 | 265 | VARIABLE|DESCRIPTION 266 | :---|:--- 267 | CARD_NAME|Alsa name of the audio card. Example for xmos dac might be `DAC` while e.g. it is `D10` for a Topping D10 268 | CARD_INDEX|Alsa index of the audio card 269 | CARD_DEVICE|Audio device, optional 270 | CARD_FORMAT|Audio format, optional (`S32_LE`, `S16_LE`, etc) 271 | FORCE_PLAYBACK_DEVICE|If set and if there is an `asound.conf` provided or selected via a prefix in `userconfig`, this will be the playback device 272 | FRIENDLY_NAME|Friendly name of the device, will be shown on Tidal Apps. Defaults to `TidalConnect`. You might [prefer to use a single word](https://github.com/GioF71/tidal-connect/issues/216#issuecomment-2684176691). 273 | ASOUND_FILE_PREFIX|Search asound.conf with this prefix, a `.` is used as separator 274 | CREATED_ASOUND_CARD_NAME|When creating asound.conf, use this as the declared device name 275 | ENABLE_SOFTVOLUME|Generate a configuration with softvolume if set to `yes`, defaults to `no` 276 | ENABLE_GENERATED_TONE|Generates a tone before starting the app, defaults to `yes` 277 | MODEL_NAME|Model name of the device. Defaults to `Audio Streamer`. 278 | MQA_CODEC|Can't comment a lot on this, defaults to `false`. 279 | MQA_PASSTHROUGH|Can't comment a lot on this, defaults to `false`. 280 | SLEEP_TIME_SEC|Sleep time before starting the real app, after starting tmux. Defaults to `3`. 281 | RESTART_ON_FAIL|Enables auto restart (see issue [#16](https://github.com/GioF71/tidal-connect/issues/16)), defaults to `1` (which means restart is enabled). 282 | RESTART_WAIT_SEC|Wait time in seconds before trying restart (see RESTART_ON_FAIL), defaults to 10. 283 | CLIENT_ID|Set custom client id, defaults to an empty string 284 | LOG_LEVEL|Application log level, defaults to `3` 285 | CERTIFICATE_PATH|Override default certificate path 286 | DISABLE_CONTROL_APP|Disable control app if set to `1`, defaults to `0` 287 | DISABLE_APP_SECURITY|Defaults to false 288 | DISABLE_WEB_SECURITY|Defaults to true 289 | DNS_SERVER_LIST|The DNS serves to be used, defaults to `8.8.8.8 8.8.4.4` (Google's DNS servers). 290 | TIDAL_CONNECT_IMAGE|Alternative image 291 | 292 | Please note that if both CARD_NAME and CARD_INDEX are specified, only CARD_NAME will be considered. 293 | Also, if both CARD_NAME and CARD_INDEX are not specified, `sysdefault` (the system default audio device) will be used. 294 | 295 | ## Volumes 296 | 297 | Here is the list of volumes: 298 | 299 | VOLUME|DESCRIPTION 300 | :---|:--- 301 | /userconfig|Might contain user-provided configurations. 302 | 303 | ### User-provided configurations 304 | 305 | #### Custom asound.conf 306 | 307 | If you put an `asound.conf` file in the userconfig directory of the repository, this file will be copied to `/etc/asound.conf`, so you can implement your custom configuration. You might need to set the variable `FORCE_PLAYBACK_DEVICE` according to the contents of the provided file, unless the device you want to play to is named `custom`. 308 | 309 | ## Installation on Moode Audio or Volumio 310 | 311 | It is possible to use this solution for easy installation of Tidal Connect on [Moode Audio](https://moodeaudio.org/) and [Volumio](https://volumio.com/). 312 | It is required to have a ssh connection to the Moode/Volumio audio box. In order to enable ssh on Volumio, refer to [this](https://developers.volumio.com/SSH%20Connection) page. 313 | Those two platforms do not ship docker out of the box (unsurprisingly), so docker installation is required. See [Docker Installation](#install-docker) earlier in this page. 314 | 315 | ### Configure Audio 316 | 317 | If you have just installed docker with the previous commands, it is probably a good idea to logoff your current ssh session, then log back in. Otherwise, just open a ssh connection to your box. 318 | We need to configure the audio output you want to use for Tidal Connect. 319 | If your device only has one output, or if that output is also configured as the default output, no configuration might be needed other than the Friendly and Model name. 320 | 321 | #### Single audio device 322 | 323 | On one of my boxes, I have a Hifiberry Dac+ Pro Hat, so when I use the command: 324 | 325 | ```text 326 | cat /proc/asound/cards 327 | ``` 328 | 329 | I get: 330 | 331 | ```text 332 | pi@moode-living:~/git/tidal-connect $ cat /proc/asound/cards 333 | 0 [sndrpihifiberry]: HifiberryDacp - snd_rpi_hifiberry_dacplus 334 | snd_rpi_hifiberry_dacplus 335 | ``` 336 | 337 | Great, the operating system has just disabled the onboard audio and set the Hifiberry HAT as the default card. 338 | So let's configure Tidal Connect: 339 | 340 | ```text 341 | cd $HOME/tidal-connect 342 | ./configure.sh -f "Living Aux1" -m "Raspberry Pi" 343 | ``` 344 | 345 | We are not specifying anything (not the card index and neither the name) because there is only one output available. 346 | Replace the first and second strings to your liking. Once configured, start the service as usual: 347 | 348 | ```text 349 | cd $HOME/tidal-connect 350 | docker-compose up -d 351 | ``` 352 | 353 | #### Multiple audio devices 354 | 355 | On another one of my boxes, I have an usb dac connected, so when I use the command: 356 | 357 | ```text 358 | cat /proc/asound/cards 359 | ``` 360 | 361 | I get: 362 | 363 | ```text 364 | moode@moode:~ $ cat /proc/asound/cards 365 | 0 [b1 ]: bcm2835_hdmi - bcm2835 HDMI 1 366 | bcm2835 HDMI 1 367 | 1 [Headphones ]: bcm2835_headpho - bcm2835 Headphones 368 | bcm2835 Headphones 369 | 2 [X20 ]: USB-Audio - XMOS USB Audio 2.0 370 | XMOS XMOS USB Audio 2.0 at usb-0000:01:00.0-1.2, high speed 371 | ``` 372 | 373 | So in this setup, the operating system has not disabled the onboard audio. Even if you have configured Moode so that is will use the USB DAC, this might not be enough for Tidal Connect to automatically select that card. 374 | The safest way (at least IMO) is to use the string that identifies the dac as card name: 375 | So let's configure Tidal Connect: 376 | 377 | ```text 378 | cd $HOME/tidal-connect 379 | ./configure.sh -n "X20" -f "Desktop" -m "Raspberry Pi" 380 | ``` 381 | 382 | Replace the second and third strings to your liking. Once configured, start the service as usual: 383 | 384 | ```text 385 | cd $HOME/tidal-connect 386 | docker-compose up -d 387 | ``` 388 | 389 | ### Caveat 390 | 391 | #### Audio device locking 392 | 393 | Tidal Connect will access exclusively your audio device if you select it in your desktop/mobile Tidal App. 394 | If you want to play to the selected audio device from other sources, you will need to disconnect from the Tidal app you are using. 395 | Similarly, you won't be able to play to a device if this device is already playing something from another source. 396 | In order to check if a device is playing, follow the instructions in the next paragraphs. 397 | 398 | ##### Check Audio device is playing, by index 399 | 400 | Say you have selected your card by its index (not recommended), and that the index is `1`, you can execute the following command: 401 | 402 | `watch cat /proc/asound/card1/pcm0p/sub0/hw_params` 403 | 404 | If this does not say "closed", it means that the audio device is being currently used. 405 | 406 | ##### Check Audio device is playing, by name 407 | 408 | Say you have selected your card by its name, and that the name is `D10`, you can execute the following command: 409 | 410 | `watch cat /proc/asound/D10/pcm0p/sub0/hw_params` 411 | 412 | If this does not say "closed", it means that the audio device is being currently used. 413 | 414 | #### Software volume 415 | 416 | This solution will try to configure a software volume for your Tidal Connect device, so that you should be able to control the software volume from the Tidal App on any platform, without touching the hardware volume, which is something you might find useful if you have other players running on the same audio device, e.g. mpd, librespot, squeezelite, etc. 417 | However, software volume will be enabled only if your audio device does not already have a control named `Master`. You can verify if your device already has such control using the following command, for the audio device at index 0: 418 | 419 | ```text 420 | amixer -c 0 | grep \'Master\' 421 | ``` 422 | 423 | The desired output is no output, as this would mean that there is no `Master` control already. 424 | If, instead, you get a line like this one, you are (almost) out of luck: 425 | 426 | ```text 427 | Simple mixer control 'Master',0 428 | ``` 429 | 430 | Even under this condition, the solution will create the SoftVolume control naming it `SoftVolume`. This will allow the Tidal Apps (at least the current Android version) to control the *hardware* volume using its slider. This means that changing the volume from the Tidal App will affect all the other players running on the same audio card. 431 | If you are in this situation (so if you already have a `Master` control defined in your audio device) and you don't want to use hardware volume, consider disabling software volume by setting `ENABLE_SOFTVOLUME` to `no` in your `.env` file, or acting on the correspondent switch of the `configure.sh` script. 432 | See [this issue](https://github.com/GioF71/tidal-connect/issues/187) for some context information. 433 | Special "Thank You" to [zamiere](https://github.com/zamiere) for providing useful information. 434 | 435 | #### Hardware changes 436 | 437 | Remember that, should you change something to your Moode/Volumio setup, maybe replacing the audio-hat with an USB DAC, you will most likely need to reconfigure Tidal Connect accordingly. 438 | 439 | #### Volumio integration 440 | 441 | Please be aware that this solution will not be completely equivalent to the built-in premium feature of Volumio. That solution (probably) allows the attached (touch) display to show the currently playing song, while this solution for sure does not allow that or any other related features. 442 | 443 | #### Mandatory IPV6 support 444 | 445 | Tidal connect won't work if your system does not support ipv6. See [this](https://github.com/GioF71/tidal-connect/issues/21) issue. 446 | Afaik, there is no solution or workaround available other than, somehow, enabling ipv6. 447 | 448 | #### DietPi 449 | 450 | On DietPi (which I am running on my Asus Tinkerboard), you might need to enable avahi-daemon, if this is not enabled yet. 451 | You might find the following on the logs: 452 | 453 | ```text 454 | [tisoc] [error] [avahiImpl.cpp:358] avahi_client_new() FAILED: Daemon not running 455 | ``` 456 | 457 | This can be fixed by installing the avahi-daemon. It is not installed by default on DietPi, so we can installing it with this command: 458 | 459 | ```text 460 | sudo apt-get install avahi-daemon 461 | ``` 462 | 463 | An already started tidal-connect container should start working immediately, at least that is what happened with my setup. 464 | 465 | ## Change History 466 | 467 | Date|Comment 468 | :---|:--- 469 | 2025-09-01|Add [known devices document](assets/known-devices.md) 470 | 2025-09-01|Add sample and asound.conf for IQAudio Board (see issue [#237](https://github.com/GioF71/tidal-connect/issues/237)) 471 | 2024-11-20|Add support for own docker image 472 | 2024-10-15|Add `newgrp` instruction so a logoff/logon is not strictly required. 473 | 2024-09-23|Add sample for FiiO K11 (see issue [#202](https://github.com/GioF71/tidal-connect/issues/202)) 474 | 2024-09-23|Corrected markdown table in `userconfig/README.md` 475 | 2024-08-06|Add sample for Chord Qutest (see issue [#186](https://github.com/GioF71/tidal-connect/issues/186)) 476 | 2024-08-02|Add alternatives: Mopidy-Tidal, Audirvana (paid) and Roon (paid) 477 | 2024-07-17|Add documentation about software volume 478 | 2024-07-17|Create SoftMaster control when a Master control already exists 479 | 2024-07-08|Add sample env files for Fosi Audio DS1 headphone amp 480 | 2024-07-08|Fixed bug which would cause ENABLE_SOFTVOLUME to not be honored when set to e.g. "no" 481 | 2024-07-08|Add pi-headphones.asound.conf for RPI Headphone jack 482 | 2024-07-01|Add asound.conf for Apple USB dongle, see #187 483 | 2024-07-01|Add support for 48kHz tone, see #187 484 | 2024-06-17|Add presets for hdmi on raspberry pi 485 | 2024-04-08|Add sample config file for hifiberry dac plus 486 | 2024-04-06|Reverted removal of optional `version` property in docker-compose file (#160) 487 | 2024-04-02|Remove optional `version` property in docker-compose file 488 | 2024-04-02|Add sample config file for Yulong D200 USB DAC 489 | 2024-03-27|Add sample config file for hifiberry digi+ pro 490 | 2024-03-13|Support for disabling control app 491 | 2024-03-13|Support for overriding certificate path 492 | 2024-03-13|Support for log level 493 | 2024-03-13|Support for custom clientid 494 | 2024-03-04|Lowered default for RESTART_WAIT_SEC to 10 495 | 2024-03-04|Add logs.sh, restart.sh and restart-watch.sh scripts 496 | 2024-03-04|Add -w for RESTART_WAIT_SEC to configure.sh 497 | 2024-03-04|Corrected configure.sh (sequence of opts) 498 | 2024-03-04|Add aune-s6 dac configuration with softvolume 499 | 2024-03-03|Fix software volume, avoid to give up for a device which only contains Master (see #136) 500 | 2024-02-22|Add support for software volume 501 | 2024-02-22|Add support for configuration self-test using a generated tone 502 | 2024-02-17|Add support for newer variables in configure.sh, see [#108](https://github.com/GioF71/tidal-connect/issues/108) 503 | 2024-01-30|Add support for ASOUND_FILE_PREFIX, see [#101](https://github.com/GioF71/tidal-connect/issues/101) 504 | 2024-01-26|Assume `custom` playback device when asound.conf is provided, see [#90](https://github.com/GioF71/tidal-connect/issues/90) 505 | 2024-01-25|Support custom asound.conf, support forced PLAYBACK device, see [#80](https://github.com/GioF71/tidal-connect/issues/80) 506 | 2024-01-25|Revert latest change, see ([#78](https://github.com/GioF71/tidal-connect/issues/78)) 507 | 2024-01-24|Always create sysdefault in asound.conf and log device names, see [#76](https://github.com/GioF71/tidal-connect/issues/76) 508 | 2024-01-23|Add support for optional card device (`CARD_DEVICE`) and format (`CARD_FORMAT`), see [#72](https://github.com/GioF71/tidal-connect/issues/72) 509 | 2023-09-12|Clarify how to install on Volumio, see issue [#29](https://github.com/GioF71/tidal-connect/issues/29) 510 | 2023-09-04|Allow default audio card selection, see issue [#22](https://github.com/GioF71/tidal-connect/issues/22) 511 | 2023-07-18|Allow user-specified dns server(s), see issue [#13](https://github.com/GioF71/tidal-connect/issues/13) 512 | 2023-07-07|Fixed asound.conf generation from card index, see issue [#2](https://github.com/GioF71/tidal-connect/issues/2) 513 | 2023-06-02|First unfolding seems to be working 514 | 2023-06-02|Some effort to avoid resampling 515 | 2023-06-02|MQA passthrough defaults to `false` 516 | 2023-06-01|Using hardware mode 517 | 2023-06-01|Resolve device name at container startup 518 | 2023-05-29|First working version 519 | --------------------------------------------------------------------------------