RPI4 Dac sycning, Tidal connect, white noise and volume tweaks

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