{"id":6034,"date":"2025-06-04T16:10:33","date_gmt":"2025-06-04T08:10:33","guid":{"rendered":"https:\/\/blog.qdac.cc\/?p=6034"},"modified":"2025-06-04T16:10:34","modified_gmt":"2025-06-04T08:10:34","slug":"%e6%95%99%e7%a8%8b%e5%9c%a8-linux-%e4%b8%ad%e4%bc%98%e5%85%88%e4%bd%bf%e7%94%a8%e9%ab%98%e8%b4%a8%e9%87%8f%e7%bc%96%e7%a0%81%ef%bc%88ldac%e7%ad%89%ef%bc%89%e4%bb%a5%e8%a7%a3%e5%86%b3%e8%93%9d","status":"publish","type":"post","link":"https:\/\/blog.qdac.cc\/?p=6034","title":{"rendered":"[\u6559\u7a0b]\u5728 Linux \u4e2d\u4f18\u5148\u4f7f\u7528\u9ad8\u8d28\u91cf\u7f16\u7801\uff08LDAC\u7b49\uff09\u4ee5\u89e3\u51b3\u84dd\u7259\u97f3\u8d28\u95ee\u9898"},"content":{"rendered":"\n<p>\u7ed3\u5408 DeepSeek \u751f\u6210\u7684\u811a\u672c\uff0c\u81ea\u5df1\u505a\u4e86\u4e00\u4e9b\u6539\u52a8\u548c\u9519\u8bef\u4fee\u6b63\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n# \u84dd\u7259\u97f3\u9891\u4f18\u5316\u811a\u672c v2025.6\n# \u529f\u80fd\uff1a\u81ea\u52a8\u5207\u6362\u84dd\u7259\u8bbe\u5907\u5230\u9ad8\u8d28\u91cf\u97f3\u9891\u6a21\u5f0f\uff08A2DP Sink\uff09\uff0c\u6309\u914d\u7f6e\u6587\u4ef6\u7ed9\u51fa\u7684\u4f18\u5148\u7ea7\u6765\u6fc0\u6d3b\n\n# \u521d\u59cb\u5316\u8bbe\u7f6e\nDEBUG_MODE=false\nLOG_FILE=\"\/tmp\/bluetooth_audio_optimizer.log\"\nexport LC_ALL=C\n# \u8bbe\u7f6e\u6b63\u786e\u7684\u73af\u5883\u53d8\u91cf\nexport XDG_RUNTIME_DIR=\"\/run\/user\/$(id -u)\"\n\n# \u7b49\u5f85 PulseAudio\/Pipewire \u670d\u52a1\u542f\u52a8\nMAX_ATTEMPTS=10\nATTEMPT=0\nwhile &#91; $ATTEMPT -lt $MAX_ATTEMPTS ]; do\n    if pactl info &amp;>\/dev\/null; then\n        break\n    fi\n    sleep 1\n    ATTEMPT=$((ATTEMPT+1))\ndone\n\nif &#91; $ATTEMPT -ge $MAX_ATTEMPTS ]; then\n    echo \"$(date) - \u9519\u8bef\uff1a\u65e0\u6cd5\u8fde\u63a5\u5230 PulseAudio\/Pipewire \u670d\u52a1\" >&amp;2\n    exit 1\nfi\n# \u8bb0\u5f55\u65e5\u5fd7\u51fd\u6570\nlog() {\n    local msg=\"$1\"\n    if $DEBUG_MODE; then\n        echo -e \"$(date +'%F %T'): $msg\" | tee -a \"$LOG_FILE\"\n    else\n        echo -e \"$msg\"\n    fi\n}\n\n# === \u6838\u5fc3\u529f\u80fd\uff1a\u8bed\u8a00\u65e0\u5173\u7684\u8bbe\u5907\u8bc6\u522b ===\n\n# \u83b7\u53d6\u84dd\u7259MAC\u5730\u5740\uff08\u5b8c\u5168\u8bed\u8a00\u65e0\u5173\uff09\nget_bt_mac() {\n    pactl list sinks | grep -m1 -oP 'api\\.bluez5\\.address\\s*=\\s*\"\\K(&#91;0-9A-F:]{17})'\n}\n\n# \u83b7\u53d6\u84dd\u7259\u5361\u8def\u5f84\uff08\u517c\u5bb9\u6240\u6709\u8bed\u8a00\u73af\u5883\uff09\nget_bt_card_path() {\n    local mac\n    mac=$(pactl list sinks | grep -m1 -oP 'api\\.bluez5\\.address\\s*=\\s*\"\\K(&#91;0-9A-F:]{17})')\n    &#91; -n \"$mac\" ] &amp;&amp; echo \"bluez_card.${mac\/\/:\/_}\"\n}\n\n# === A2DP\/LDAC\u72b6\u6001\u68c0\u6d4b ===\n\n# \u68c0\u6d4b\u5f53\u524d\u914d\u7f6e\u6587\u4ef6\nget_active_profile() {\n    local card_path=\"$1\"\n    pactl list cards | awk -v card=\"$card_path\" '\n        $0 ~ \" \" card {\n            active = 0\n            while (getline) {\n                if (\/Active Profile:\/) {\n                    print $3\n                    exit\n                }\n                if (\/^$\/ || \/^Card\/) break\n            }\n        }\n    '\n}\n\n# \u68c0\u6d4b\u5f53\u524d\u7f16\u7801\u5668\nget_active_codec() {\n    pactl list sinks | awk '\n        \/api\\.bluez5\\.codec = \"\/ { \n            match($0, \/\"(&#91;^\"]+)\"\/, a); \n            if (a&#91;1] != \"\") print a&#91;1]\n        }' | head -1\n}\n\n# \u68c0\u6d4b\u652f\u6301\u7684\u7f16\u7801\u5217\u8868\nget_supported_codecs() {\n    local card_path=\"$1\"\n    pactl list cards | grep -A50 \" $card_path\" | \n        grep -oP 'bluez5\\.codecs = \\&#91;\\K&#91;^\\]]+' | \n        tr -d '\"' | tr ',' '\\n' | sed 's\/^ \/\/'\n}\n\n# === A2DP\u6a21\u5f0f\u5207\u6362\uff08\u517c\u5bb9\u591aProfile\uff09===\n\nswitch_to_a2dp() {\n    local card_path=\"$1\"\n    declare -a profile_entries\n    local profile_name=\"\" priority=\"\"\n\tdeclare -i -x retval=1\n    log \"\u68c0\u6d4b\u53ef\u7528\u7684A2DP\u914d\u7f6e\u5e76\u51c6\u5907\u6309\u4f18\u5148\u7ea7\u6392\u5e8f...\"\n    \n    # \u89e3\u6790\u6240\u6709\u53ef\u7528A2DP\u914d\u7f6e\n    pactl list cards | awk -v card=\"$card_path\" '\n        $0 ~ \" \" card {\n            in_card = 1\n            next\n        }\n        in_card &amp;&amp; \/Profiles:\/ {\n            in_profiles = 1\n            next\n        }\n        in_profiles &amp;&amp; \/^$\/ {\n            exit # \u9047\u5230\u7a7a\u884c\u7ed3\u675f\u89e3\u6790\n        }\n        in_profiles &amp;&amp; \/available:&#91;&#91;:space:]]*yes\/ &amp;&amp; \/a2dp-sink\/ {\n            # \u63d0\u53d6\u914d\u7f6e\u540d\n            profile_name = $0\n            sub(\/:.*\/, \"\", profile_name)\n            gsub(\/^&#91;&#91;:space:]]+\/, \"\", profile_name)\n            \n            # \u63d0\u53d6\u4f18\u5148\u7ea7\n            match($0, \/priority:&#91;&#91;:space:]]*(&#91;0-9]+)\/, m)\n            priority = m&#91;1]\n            \n            if (priority == \"\") priority = 0 # \u7f3a\u7701\u503c\n            \n            printf \"%d:%s\\n\", priority, profile_name\n        }\n    ' | sort -t: -k1,1nr | while IFS=: read -r priority profile; do\n        \n        # \u8bb0\u5f55\u53d1\u73b0\u7684\u914d\u7f6e\n        # profile_entries+=(\"$priority:$profile\")\n        if &#91;&#91; \"$retval\" == \"1\" ]]; then\t\t\n\t\t\tlog \"\u53d1\u73b0\u9ad8\u4f18\u5148\u7ea7\u914d\u7f6e &#91;\u4f18\u5148\u7ea7:${priority}]: ${profile}\"\t\t\n\t\t\tif pactl set-card-profile \"$card_path\" \"$profile\" 2>\/dev\/null; then\n\t            sleep 0.5 # \u7b49\u5f85\u534f\u8bae\u751f\u6548\n\t            log \"\u2713 \u6210\u529f\u6fc0\u6d3b\u914d\u7f6e: ${profile}\"\n\t            \n\t            # \u9a8c\u8bc1\u914d\u7f6e\u6fc0\u6d3b\n\t            local active_profile\n\t            active_profile=$(get_active_profile \"$card_path\")\n\t            \n\t            if &#91;&#91; \"$active_profile\" == \"$profile\" ]]; then\n\t                log \"\u2713 \u914d\u7f6e\u72b6\u6001\u5df2\u786e\u8ba4\"\n\t                retval=0\n\t            else\n\t                log \"&#x26a0;&#xfe0f; \u914d\u7f6e\u6fc0\u6d3b\u4f46\u72b6\u6001\u4e0d\u5339\u914d\uff0c\u5b9e\u9645\u914d\u7f6e: ${active_profile}\"\n\t            fi\n\t        else\n\t            log \"&#x26a0;&#xfe0f; \u914d\u7f6e\u6fc0\u6d3b\u5931\u8d25: ${profile}\"\n\t\t\tfi\n        fi\n    done\n    # \u6240\u6709\u914d\u7f6e\u5c1d\u8bd5\u5931\u8d25\n\tcurrent_profile=$(get_active_profile \"$card_path\")\n\tif &#91;&#91; ! \"$current_profile\" == *\"a2dp-sink\"* ]]; then\n\t    log \"${RED}\u9519\u8bef\uff1a\u6240\u6709\u53ef\u7528A2DP\u914d\u7f6e\u5c1d\u8bd5\u5747\u5931\u8d25${NC}\"\n    \t\tlog \"\u8bf7\u68c0\u67e5\u8bbe\u5907\u517c\u5bb9\u6027\uff1apactl list cards | grep -A30 ' $card_path'\"\n\t\treturn 1\n\tfi\n    return 0\n}\n# === \u8bbe\u5907\u91cd\u8fde\u8f85\u52a9\u51fd\u6570 ===\n\nreconnect_bluetooth_device() {\n    local mac=\"$1\"\n    log \"\u91cd\u7f6e\u84dd\u7259\u8fde\u63a5...\"\n    \n    (\n        echo \"disconnect $mac\"\n        sleep 3\n        echo \"connect $mac\"\n        sleep 5\n        echo \"quit\"\n    ) | bluetoothctl > \/dev\/null 2>&amp;1\n    \n    log \"\u8bbe\u5907\u91cd\u8fde\u5b8c\u6210\"\n}\n\n# === \u4e3b\u63a7\u5236\u6d41\u7a0b ===\n\nmain() {\n    log \"${BLUE}=== $(date -Iseconds) \u84dd\u7259\u97f3\u9891\u4f18\u5316\u5f00\u59cb ===\"\n    \n    # \u83b7\u53d6\u8bbe\u5907\u4fe1\u606f\n    local bt_mac\n    bt_mac=$(get_bt_mac)\n    if &#91; -z \"$bt_mac\" ]; then\n        log \"${RED}\u9519\u8bef\uff1a\u672a\u68c0\u6d4b\u5230\u5df2\u8fde\u63a5\u7684\u84dd\u7259\u8033\u673a\"\n        return 1\n    fi\n    log \"\u8bbe\u5907MAC\u5730\u5740: $bt_mac\"\n    \n    local card_path\n    card_path=$(get_bt_card_path \"$bt_mac\")\n    if &#91; -z \"$card_path\" ]; then\n        log \"${RED}\u9519\u8bef\uff1a\u65e0\u6cd5\u8bc6\u522b\u84dd\u7259\u8bbe\u5907\u8def\u5f84\"\n        return 1\n    fi\n    log \"\u8bbe\u5907\u8def\u5f84: $card_path\"\n    \n    # \u68c0\u67e5\u5f53\u524d\u914d\u7f6e\n    local current_profile\n    current_profile=$(get_active_profile \"$card_path\")\n    local current_codec\n    current_codec=$(get_active_codec \"$card_path\")\n    log \"\u5f53\u524d\u914d\u7f6e: ${current_profile:-\u672a\u77e5} | \u7f16\u7801: ${current_codec:-\u672a\u77e5}\"\n    \n    # \u81ea\u52a8\u5207\u6362\u5230\u9ad8\u8d28\u91cf\u6a21\u5f0f\n    if &#91;&#91; ! \"$current_profile\" == *\"a2dp-sink\"* ]]; then\n        log \"\u6b63\u5728\u5207\u6362\u5230\u9ad8\u8d28\u91cf\u97f3\u9891\u6a21\u5f0f...\"\n        if ! switch_to_a2dp \"$card_path\"; then\n            log \"${YELLOW}\u914d\u7f6e\u5207\u6362\u5931\u8d25\uff0c\u5c1d\u8bd5\u8bbe\u5907\u91cd\u8fde...\"\n            reconnect_bluetooth_device \"$bt_mac\"\n            sleep 3\n            switch_to_a2dp \"$card_path\" || {\n                log \"${RED}\u9519\u8bef\uff1a\u65e0\u6cd5\u5207\u6362\u5230A2DP\u6a21\u5f0f\"\n                return 1\n            }\n        fi\n        sleep 2 # \u7b49\u5f85\u914d\u7f6e\u7a33\u5b9a\n        log \"${GREEN}\u2713 \u5df2\u542f\u7528\u9ad8\u8d28\u91cf\u97f3\u9891\u914d\u7f6e\"\n    else\n        log \"${GREEN}\u2713 \u5df2\u5728\u9ad8\u8d28\u91cf\u97f3\u9891\u6a21\u5f0f\"\n    fi\n    \n    log \"${BLUE}=== \u4f18\u5316\u5b8c\u6210 ===\"\n}\n\n# === \u989c\u8272\u5b9a\u4e49 ===\nRED='\\033&#91;0;31m'\nGREEN='\\033&#91;0;32m'\nYELLOW='\\033&#91;1;33m'\nBLUE='\\033&#91;0;34m'\nNC='\\033&#91;0m'\n\n# \u6267\u884c\u4e3b\u51fd\u6570\nif &#91; \"$1\" = \"--debug\" ]; then\n    DEBUG_MODE=true\nfi\n\nmain<\/code><\/pre>\n\n\n\n<p>\u540e\u7eed\u6b65\u9aa4\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4fdd\u5b58\u811a\u672c\uff0c\u7136\u540e\u5c31\u662f\u52a0\u6267\u884c\u6743\u9650\u548c\u76ee\u5f55\u7684\u5199\u5165\u6743\u9650\u3002<\/li>\n\n\n\n<li>\u6267\u884c crontab -e \u5c06\u811a\u672c\u5199\u5165\u5230\u8ba1\u5212\u4efb\u52a1\u91cc\uff0c\u6bcf\u5206\u949f\u68c0\u67e5\u4e00\u6b21\u3002<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>* * * * * \/opt\/ldac\/ldac.sh > \/opt\/ldac\/ldac.log 2>&amp;1<\/code><\/pre>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u7ed3\u5408 DeepSeek \u751f\u6210\u7684\u811a\u672c\uff0c\u81ea\u5df1 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[840,603],"tags":[],"class_list":["post-6034","post","type-post","status-publish","format-standard","hentry","category-linux","category-603"],"views":1304,"_links":{"self":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts\/6034","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6034"}],"version-history":[{"count":1,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts\/6034\/revisions"}],"predecessor-version":[{"id":6035,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=\/wp\/v2\/posts\/6034\/revisions\/6035"}],"wp:attachment":[{"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6034"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6034"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.qdac.cc\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6034"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}