I had some issues with clock sync, tidal freezing if the dac was power cycled and some other minor issues using Volumio on RPI4 via USB to my tonewinner ad1-pre+ dac.
My previous solution was using a SMSL PO100 to reclock and convert to SPDIF.
This weekend I sat down with the help of chatGPT and Gemini to create a script.
In addition to te script I enabled fusionDSP with no EQ set. This sorted out the occasional ‘white noise’ when
Hopefulling this may help someone:
Below is a log of a powercycle so you can se what I was trying to acheive:
Feb 22 14:24:54 volumio dac-guard[697]: DAC lost — debouncing
Feb 22 14:24:59 volumio dac-guard[697]: Stopping TIDAL Connect…
Feb 22 14:24:59 volumio dac-guard[697]: Stopping MPD…
Feb 22 14:24:59 volumio dac-guard[697]: Clearing MPD state…
Feb 22 14:24:59 volumio dac-guard[697]: Attempting dynamic USB reset…
Feb 22 14:24:59 volumio dac-guard[697]: DAC path not found. Performing controlled hub reset (usb1)
Feb 22 14:25:04 volumio dac-guard[697]: Recovery reset complete
Feb 22 14:25:12 volumio dac-guard[697]: DAC detected — debouncing
Feb 22 14:25:17 volumio dac-guard[697]: DAC detected — handshake delay 12 s
Feb 22 14:25:29 volumio dac-guard[697]: Initializing ALSA
Feb 22 14:25:29 volumio dac-guard[697]: Forcing DAC Clock Sync…
Feb 22 14:25:32 volumio dac-guard[697]: Starting MPD
Feb 22 14:25:32 volumio dac-guard[697]: Restarting Volumio discovery
Feb 22 14:25:37 volumio dac-guard[697]: Soft unmute sequence for PCM indices 0 and 1
Feb 22 14:25:38 volumio dac-guard[697]: PCM channels set to 100%
Feb 22 14:25:38 volumio dac-guard[697]: Audio stack ready
Feb 22 14:25:38 volumio dac-guard[697]: Tidal Connect is active and ready for use
dac-guard script:
#!/bin/bash
# -------------------------------
# DAC Guard for Volumio + Tonewinner
# VERSION: v4.1 (Positive Logging + Card 5 Hardware-Aware)
# -------------------------------
DAC_NAME="PRMA"
DAC_VENDOR="2fc6"
CHECK_INTERVAL=2
DEBOUNCE=5
HANDSHAKE_DELAY=12
SETTLE_DELAY=5
LOG_TAG="dac-guard"
log() {
# Systemd/Journald adds the timestamp naturally.
echo "$1"
}
dac_present() {
grep -qi "$DAC_NAME" /proc/asound/cards
}
vtcs_running() {
# 1. HARDWARE CHECK (Priority 1)
# Check card 5 specifically (verified Tonewinner path)
if grep -q "RUNNING" /proc/asound/card5/pcm0p/sub0/status 2>/dev/null; then
return 0
fi
# 2. SERVICE CHECK (Priority 2)
if systemctl is-active --quiet vtcs.service; then
return 0
fi
# 3. PROCESS CHECK (Fallback)
if pgrep -f "/usr/bin/vtcs" >/dev/null; then
return 0
fi
return 1
}
kill_vtcs() {
log "Stopping TIDAL Connect..."
curl -s --max-time 2 http://localhost:3000/api/v1/commands/?cmd=clearQueue >/dev/null
systemctl stop vtcs.service
pkill -9 vtcs 2>/dev/null
}
stop_mpd() {
log "Stopping MPD..."
systemctl stop mpd
}
clear_mpd() {
log "Clearing MPD state..."
mpc stop >/dev/null 2>&1
mpc clear >/dev/null 2>&1
rm -f /var/lib/mpd/state 2>/dev/null
}
find_dac_usb_path() {
for dev in /sys/bus/usb/devices/*; do
if [ -f "$dev/idVendor" ]; then
if grep -qi "$DAC_VENDOR" "$dev/idVendor"; then
basename "$dev"
return
fi
fi
done
}
reset_usb() {
log "Attempting dynamic USB reset..."
TARGET_DEV=$(find_dac_usb_path)
if [ -n "$TARGET_DEV" ] && [ -f "/sys/bus/usb/devices/$TARGET_DEV/authorized" ]; then
log "Resetting DAC at $TARGET_DEV"
echo 0 > /sys/bus/usb/devices/"$TARGET_DEV"/authorized
sleep 2
echo 1 > /sys/bus/usb/devices/"$TARGET_DEV"/authorized
else
log "DAC path not found. Performing controlled hub reset (usb1)"
if [ -f "/sys/bus/usb/devices/usb1/authorized" ]; then
echo 0 > /sys/bus/usb/devices/usb1/authorized
sleep 2
echo 1 > /sys/bus/usb/devices/usb1/authorized
else
log "Hub reset failed — usb1 not present"
fi
fi
echo 1 > /sys/bus/pci/rescan 2>/dev/null
sleep 3
}
start_audio_stack() {
TRIES=0
MAX_TRIES=12
while ! dac_present && [ $TRIES -lt $MAX_TRIES ]; do
log "Waiting for DAC enumeration..."
sleep 1
TRIES=$((TRIES+1))
done
if ! dac_present; then
log "DAC still not detected — aborting start"
return
fi
log "DAC detected — handshake delay $HANDSHAKE_DELAY s"
sleep $HANDSHAKE_DELAY
log "Initializing ALSA"
alsactl init "$DAC_NAME" >/dev/null 2>&1
log "Forcing DAC Clock Sync..."
timeout 1s aplay -D "plughw:CARD=$DAC_NAME" -f S24_LE -r 44100 /dev/zero >/dev/null 2>&1
sleep 2
log "Starting MPD"
systemctl start mpd
log "Restarting Volumio discovery"
volumio vrestart >/dev/null 2>&1
sleep $SETTLE_DELAY
if amixer -c "$DAC_NAME" scontrols | grep -q "'PCM'"; then
log "Soft unmute sequence for PCM indices 0 and 1"
for i in 0 1; do
amixer -c "$DAC_NAME" set 'PCM',$i 0% unmute >/dev/null 2>&1
sleep 0.2
amixer -c "$DAC_NAME" set 'PCM',$i 50% unmute >/dev/null 2>&1
sleep 0.2
amixer -c "$DAC_NAME" set 'PCM',$i 100% unmute >/dev/null 2>&1
done
log "PCM channels set to 100%"
fi
log "Audio stack ready"
log "Tidal Connect is active and ready for use"
TIDAL_READY="yes"
sleep 45
}
# --- Initial State ---
STATE="unknown"
TIDAL_READY="no"
if dac_present; then
STATE="present"
else
STATE="absent"
fi
log "DAC Guard started. Initial state: $STATE"
if [ "$STATE" == "absent" ]; then
reset_usb
fi
# --- Main Loop ---
while true; do
if dac_present; then
# 1. MONITOR TIDAL (Only if DAC is stable)
if [ "$STATE" == "present" ]; then
if ! vtcs_running; then
log "TIDAL Connect missing - verifying 15s"
TIDAL_READY="no"
sleep 15
if ! vtcs_running; then
log "Restarting audio stack"
start_audio_stack
continue
fi
else
# Positive reinforcement log
if [ "$TIDAL_READY" != "yes" ]; then
log "Tidal Connect is active"
TIDAL_READY="yes"
fi
fi
# 2. RECOVER DAC (If it just came back)
else
log "DAC detected — debouncing"
sleep $DEBOUNCE
if dac_present; then
start_audio_stack
STATE="present"
TIDAL_READY="no" # Reset so next loop logs the "active" message
fi
fi
else
# 3. DAC IS LOST
if [ "$STATE" != "absent" ]; then
log "DAC lost — debouncing"
TIDAL_READY="no"
sleep $DEBOUNCE
if ! dac_present; then
kill_vtcs
stop_mpd
clear_mpd
reset_usb
STATE="absent"
log "Recovery reset complete"
fi
fi
fi
sleep $CHECK_INTERVAL
done
Service:
[Unit]
Description=Tonewinner DAC Monitor
After=network.target sound.target volumiocore.service
[Service]
ExecStart=/bin/bash /usr/local/bin/dac-guard.sh
Restart=always
RestartSec=5
Type=simple
TimeoutStartSec=0
StandardOutput=journal
StandardError=journal
SyslogIdentifier=dac-guard
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
[Install]
WantedBy=multi-user.target