Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

native: cleaner PCM scan — find-then-open, two-pass, safe fallback

Earlier smart-PCM commit had a lifecycle bug: on HiFi match it set
err>=0 but didn't break, so the next loop iteration tried another
snd_pcm_open() on the same state. On the G7 this wrote 15KB of binary
garbage to ac-native-stderr.log and produced zero sdmode toggles,
meaning ac-native crashed before audio init completed.

Split into enumerate-then-open: first pass prefers "Speaker", second
pass falls back to "Jack"/"HiFi" (SOF combined FE name). Remember the
winning device, open exactly once. If that open fails drop through to
the legacy hardcoded-list. HDA-direct paths untouched because their
/proc/asound names don't match any token.

+51 -37
+51 -37
fedac/native/src/audio.c
··· 1757 1757 int card_idx = 0; 1758 1758 1759 1759 // Smart PCM selection — on SOF topologies (Chromebooks), card0 typically 1760 - // exposes multiple playback PCMs with distinct names: "Headset (*)", 1761 - // "Speakers (*)", "HDMI1 (*)"… hw:0,0 blindly is fine on HDA-direct 1762 - // codecs but opens the wrong endpoint on SOF. Scan /proc/asound and 1763 - // prefer the PCM whose name contains "Speaker" (fall back to anything 1764 - // containing "HiFi" which is the common SOF FE name for both-path). 1765 - // HDMI PCMs are explicitly skipped — they'd play on monitor, not speakers. 1766 - for (int c = 0; c < 4 && err < 0; c++) { 1767 - for (int d = 0; d < 8 && err < 0; d++) { 1768 - char info_path[64]; 1769 - snprintf(info_path, sizeof(info_path), "/proc/asound/card%d/pcm%dp/info", c, d); 1770 - FILE *ip = fopen(info_path, "r"); 1771 - if (!ip) continue; 1772 - char line[256], id_str[96] = "", name_str[96] = ""; 1773 - while (fgets(line, sizeof(line), ip)) { 1774 - if (!strncmp(line, "id: ", 4)) snprintf(id_str, sizeof(id_str), "%s", line + 4); 1775 - if (!strncmp(line, "name: ", 6)) snprintf(name_str, sizeof(name_str), "%s", line + 6); 1776 - } 1777 - fclose(ip); 1778 - // Skip HDMI — those route to monitor audio, not internal speaker. 1779 - if (strstr(id_str, "HDMI") || strstr(name_str, "HDMI")) continue; 1780 - // Prefer Speaker name; accept HiFi (SOF combined FE) as fallback. 1781 - int is_speaker = (strstr(id_str, "Speaker") || strstr(name_str, "Speaker")); 1782 - int is_combined = (strstr(id_str, "HiFi") || strstr(name_str, "HiFi") || 1783 - strstr(id_str, "Jack") || strstr(name_str, "Jack")); 1784 - if (!is_speaker && !is_combined) continue; 1785 - char dev_str[16]; 1786 - snprintf(dev_str, sizeof(dev_str), "hw:%d,%d", c, d); 1787 - err = snd_pcm_open(&pcm, dev_str, SND_PCM_STREAM_PLAYBACK, 0); 1788 - if (err >= 0) { 1789 - fprintf(stderr, "[audio] SOF PCM match: %s (id=%.*s)\n", 1790 - dev_str, (int)strcspn(id_str, "\n"), id_str); 1791 - snprintf(audio->audio_device, sizeof(audio->audio_device), "%s", dev_str); 1792 - card_idx = c; 1793 - // Prefer speaker name over combined — if we found a Speaker, 1794 - // stop here; otherwise keep scanning in case a later PCM is 1795 - // the speaker (cheap sort: Speaker beats HiFi by continuing). 1796 - if (is_speaker) break; 1760 + // exposes multiple playback PCMs: pcm0p "Speakers", pcm1p "Headset", 1761 + // pcm2p "HDMI1". hw:0,0 blindly is fine on HDA-direct codecs but breaks 1762 + // on SOF. Two-pass scan: first pass finds a PCM whose id contains 1763 + // "Speaker"; second pass falls back to "HiFi"/"Jack" (SOF combined FE). 1764 + // HDMI PCMs are skipped. If we find one, open it ONCE and stop cleanly. 1765 + const char *prefer_tokens[2] = {"Speaker", "Jack"}; 1766 + int prefer_hifi[2] = {0, 1}; /* pass 1 also accepts HiFi as a fallback */ 1767 + char found_dev[16] = ""; 1768 + char found_id[96] = ""; 1769 + int found_card = 0; 1770 + for (int pass = 0; pass < 2 && !found_dev[0]; pass++) { 1771 + for (int c = 0; c < 4 && !found_dev[0]; c++) { 1772 + for (int d = 0; d < 8 && !found_dev[0]; d++) { 1773 + char info_path[64]; 1774 + snprintf(info_path, sizeof(info_path), 1775 + "/proc/asound/card%d/pcm%dp/info", c, d); 1776 + FILE *ip = fopen(info_path, "r"); 1777 + if (!ip) continue; 1778 + char line[256], id_str[96] = "", name_str[96] = ""; 1779 + while (fgets(line, sizeof(line), ip)) { 1780 + if (!strncmp(line, "id: ", 4)) 1781 + snprintf(id_str, sizeof(id_str), "%s", line + 4); 1782 + if (!strncmp(line, "name: ", 6)) 1783 + snprintf(name_str, sizeof(name_str), "%s", line + 6); 1784 + } 1785 + fclose(ip); 1786 + if (strstr(id_str, "HDMI") || strstr(name_str, "HDMI")) continue; 1787 + int match = (strstr(id_str, prefer_tokens[pass]) || 1788 + strstr(name_str, prefer_tokens[pass])); 1789 + if (!match && prefer_hifi[pass]) { 1790 + match = (strstr(id_str, "HiFi") || strstr(name_str, "HiFi")); 1791 + } 1792 + if (!match) continue; 1793 + snprintf(found_dev, sizeof(found_dev), "hw:%d,%d", c, d); 1794 + snprintf(found_id, sizeof(found_id), "%.*s", 1795 + (int)strcspn(id_str, "\n"), id_str); 1796 + found_card = c; 1797 1797 } 1798 + } 1799 + } 1800 + if (found_dev[0]) { 1801 + err = snd_pcm_open(&pcm, found_dev, SND_PCM_STREAM_PLAYBACK, 0); 1802 + if (err >= 0) { 1803 + fprintf(stderr, "[audio] SOF PCM match: %s (id=%s)\n", 1804 + found_dev, found_id); 1805 + snprintf(audio->audio_device, sizeof(audio->audio_device), 1806 + "%s", found_dev); 1807 + card_idx = found_card; 1808 + } else { 1809 + fprintf(stderr, "[audio] SOF PCM %s open failed: %s\n", 1810 + found_dev, snd_strerror(err)); 1811 + /* fall through to legacy list */ 1798 1812 } 1799 1813 } 1800 1814