Android-ljudstacken: Varför din musik samplas om
En djupdykning i hur Android hanterar ljud -- från app till högtalare. Lär dig varför AudioFlinger samplar om din musik och vad du kan göra åt det.
Hur Android spelar ljud
Varje ljud din Android-telefon producerar — notifikationer, telefonsamtal, speleffekter, musik — färdas genom samma ljudpipeline innan det når dina öron. Att förstå denna pipeline är nyckeln till att förstå varför din noggrant kurerade förlustfria musiksamling kanske inte låter riktigt så ren som du förväntar dig.
Den vanliga Android-ljudvägen ser ut så här:
App -> AudioTrack API -> AudioFlinger -> HAL (Hardware Abstraction Layer) -> Hårdvara (högtalare, DAC, Bluetooth-radio)
När en musikspelarapp vill producera ljud skapar den en AudioTrack (eller använder det nyare AAudio API:t, som fortfarande dirigeras genom samma system). Appen skriver PCM-ljudsamplingar till detta spår — avkodad ljuddata vid vilken samplingsfrekvens och bitdjup källfilen än innehåller.
Dessa samplingar går sedan in i AudioFlinger, Androids centrala ljudmixnings- och dirigeringstjänst. AudioFlinger är trafikledaren för Android-ljud. Den tar ljudströmmar från varje app i systemet, mixar dem tillsammans, applicerar systemnivåeffekter (som systemvolymen) och dirigerar resultatet till rätt utgångsenhet. Den körs som en inbyggd systemtjänst med förhöjd prioritet, och varje enskild ljudsampling på din enhet passerar genom den.
Under AudioFlinger sitter Hardware Abstraction Layer (HAL), ett enhetsspecifikt översättningslager skrivet av telefontillverkaren. HAL:en konverterar AudioFlingers utdata till det format den faktiska hårdvaran förväntar sig — oavsett om det är en I2S-ström för den interna DAC:en, USB-ljudpaket för en extern DAC eller kodat Bluetooth-ljud för trådlösa hörlurar.
Denna arkitektur är elegant ur ett systemdesignperspektiv. Vilken app som helst kan spela ljud utan att oroa sig för hårdvaruspecifika detaljer, flera ljudströmmar mixas sömlöst, och systemet behåller kontrollen över dirigering och volympolicy. Men för musikuppspelning introducerar det ett problem.
AudioFlinger-problemet
AudioFlinger arbetar med en fast samplingsfrekvens. På de flesta Android-enheter är denna frekvens antingen 44 100 Hz eller 48 000 Hz — enhetstillverkaren bestämmer när de konfigurerar HAL:en, och den kan vanligtvis inte ändras medan systemet körs. Den vanligaste standarden på moderna telefoner är 48 kHz.
Denna fasta frekvens existerar eftersom AudioFlinger i grunden är en mixer. Den behöver kombinera ljud från flera källor — din musik, en inkommande notifikation, navigationsanvisningar, ett telefonsamtal — till en enda utström. Att mixa ljud kräver att alla strömmar har samma samplingsfrekvens. Istället för att dynamiskt ändra mixerfrekvensen varje gång en ny ström startar (vilket skulle störa alla andra aktiva strömmar) väljer AudioFlinger en frekvens och samplar om allt för att matcha.
Så varje ljudström på enheten samplas om till mixerns fasta frekvens:
- Din 96 kHz hi-res FLAC? Nedsamplad till 48 kHz.
- Din 44,1 kHz CD-ripp på en 48 kHz-mixer? Uppsamplad till 48 kHz.
- Din 48 kHz-podcast på en 48 kHz-enhet? Passerar genom oförändrad — du hade tur.
- Din DSD64 konverterad till 176,4 kHz PCM? Nedsamplad till 48 kHz.
Google designade AudioFlinger för det allmänna fallet, och ärligt talat är det ett rimligt ingenjörsbeslut för en konsumenttelefon. En enhet som spelar notifikationsljud medan den strömmar Spotify och navigerar behöver en gemensam mixningsfrekvens. Men den designades aldrig för audiofil uppspelning, och omsamplingskväliteten, även om den är adekvat, är inte den du skulle välja om trohet vore det enda målet.
Omsamplaren inbyggd i AudioFlinger har förbättrats genom åren. Tidiga Android-versioner använde en lågkvalitets linjär interpolator. Moderna versioner (Android 5.0 och senare) använder en polyfas sinc-omsamplare som producerar rimliga resultat. Men “rimligt” och “transparent” är inte samma sak — varje omsamplingsoperation introducerar en viss nivå av kvantiseringsbrus och potentiella aliasingartefakter, hur små de än är.
Samplingsfrekvensförhandling
Situationen är inte helt hopplös. Android tillhandahåller faktiskt mekanismer för appar att påverka utsamplingsfrekvensen, även om resultaten är… inkonsekventa.
När en app skapar en ljudström genom AAudio API:t (Androids moderna inhemska ljudgränssnitt) kan den begära en specifik samplingsfrekvens. Android försöker tillmötesgå denna begäran, och appen kan sedan kontrollera vilken frekvens som faktiskt beviljades. Om enhetens hårdvara och HAL stöder den begärda frekvensen kan du få inbyggd uppspelning utan omsampling.
Android 12 och senare förbättrade detta med bättre stöd för inbyggd samplingsfrekvensväxling. På kompatibla enheter kan systemet ändra HAL:ens utfrekvens för att matcha vad appen begär, särskilt för USB-ljudenheter. En väldesignad app kan potentiellt få bit-perfekt utdata vid spårets ursprungliga frekvens.
Men “korrekt stöder” gör mycket tungt arbete i den meningen. Upplevelsen är galet inkonsekvent:
- Samsung låser samplingsfrekvensen till ett fast värde i sin HAL-implementation. Oavsett vad appen begär körs hårdvaran alltid på 48 kHz.
- Pixel-enheter har generellt varit mer tillåtande och låter dig förhandla. Men även här varierar beteendet mellan hårdvarugenerationer.
- OnePlus? Beror på firmwareversionen. Det här är verkligheten för Android-ljudutveckling.
- Vissa OEM-tillverkare tillåter dynamisk frekvensväxling men bara för vissa utgångsenheter (USB DAC:ar ja, hörlursuttag nej).
- Utvecklarinställningar på vissa enheter exponerar en “USB-ljuddirigering”-knapp eller samplingsfrekvensåsidosättning, men dessa är dolda för vanliga användare och inte standardiserade.
Det praktiska resultatet är att en apputvecklare inte kan förlita sig på att få någon specifik samplingsfrekvens. Appen måste undersöka enheten, begära en frekvens, kontrollera vad den faktiskt fick och anpassa sig därefter. Detta undersök-begär-verifiera-mönster är det enda pålitliga tillvägagångssättet.
USB DAC-vägen
USB audio class 1- och class 2-enheter erbjuder den mest lovande vägen till högkvalitativ ljudutgång på Android. När du ansluter en USB DAC skapar Androids USB-ljuddrivrutin en ny ljudutgångsenhet som AudioFlinger kan rikta mot. Eftersom USB DAC:ar vanligtvis stöder flera samplingsfrekvenser och rapporterar sina kapaciteter till värden finns det bättre chans att få uppspelning vid ursprunglig frekvens.
Ljudkedjan med en USB DAC ser ut så här:
App -> AAudio API -> AudioFlinger -> USB Audio HAL -> USB Audio Class-drivrutin -> USB DAC
Notera att AudioFlinger fortfarande är med i kedjan. Till skillnad från skrivbordsoperativsystem — där WASAPI Exclusive-läge i Windows eller hog mode i macOS CoreAudio kan kringgå systemmixern helt — erbjuder Android inte äkta exklusiv åtkomst till ljudhårdvara. AudioFlinger sitter alltid mellan appen och enheten. Alltid.
Men när stjärnorna ligger rätt kan AudioFlinger fungera som genomsläpp snarare än omsamplare:
- Appen begär spårets ursprungliga samplingsfrekvens via AAudio.
- AudioFlinger kontrollerar om USB audio HAL:en stöder den frekvensen.
- Om den stöds konfigurerar AudioFlinger sin utgång vid den frekvensen.
- Eftersom den enda aktiva ljudströmmen matchar mixerfrekvensen sker ingen omsampling.
- Samplingar passerar genom AudioFlinger oförändrade och når USB DAC:en vid den ursprungliga frekvensen.
Det här är så nära bit-perfekt som Android kommer. Samplingarna ändras inte matematiskt, även om de tekniskt passerar genom mixern. För att detta ska fungera behöver du också att inga systemljud eller andra ljudströmmar är aktiva samtidigt — allt annat som spelas skulle tvinga AudioFlinger tillbaka till mixning och potentiellt omsampling.
Appen måste hantera betydande komplexitet för att få detta att fungera: undersöka USB-enhetens stödda frekvenser, begära rätt frekvens, verifiera den beviljade frekvensen och graciöst hantera enhetskoppling. Appen behöver också hantera sin egen ljudbuffertstorlek, eftersom USB DAC:ar har andra latensegenskaper än telefonens inbyggda ljud.
Bluetooth: Ännu ett konverteringslager
Bluetooth-ljud lägger till ett helt separat konverteringssteg ovanpå AudioFlinger. Efter att ditt ljud passerat genom systemmixern går det in i Bluetooth-ljudstacken, där det kodas till en Bluetooth-ljudkodek innan trådlös överföring.
Kedjan blir:
App -> AudioFlinger -> Bluetooth-kodek-kodare -> Trådlös överföring -> Hörlurar/högtalare
Varje Bluetooth-kodek är förlustbringande. Inga undantag. Den tillgängliga bandbredden på en Bluetooth-länk räcker helt enkelt inte för okomprimerat ljud vid höga samplingsfrekvenser. Kodekarna skiljer sig åt i hur aggressivt de komprimerar och vilken kvalitet de uppnår inom den tillgängliga bandbredden.
SBC (Sub-Band Codec) är den universella baslinjen — varje Bluetooth-ljudenhet stöder den. Den toppar vid ungefär 345 kbps och 48 kHz. Den har förbättrats avsevärt med bättre kodarimplementationer, men den är fortfarande den lägsta gemensamma nämnaren.
LDAC, utvecklad av Sony och inkluderad i Android sedan version 8.0, erbjuder den högsta kvaliteten vid upp till 990 kbps och 96 kHz. Vid sin bästa inställning anses LDAC generellt vara transparent för det mesta innehållet — vilket innebär att tränade lyssnare har svårt att skilja den från den trådbundna originalet i kontrollerade tester. Men den är fortfarande förlustbringande komprimering.
För en detaljerad jämförelse av alla Bluetooth-kodekar — LDAC, aptX, aptX HD, aptX Adaptive, AAC, SBC och LC3 — se vår guide om Bluetooth-ljudkodekar.
Den viktiga poängen: även om du kringgår AudioFlingers omsampling (genom att få ursprunglig frekvens), komprimerar Bluetooth-kodningen om allt efteråt. Bit-perfekt uppspelning över Bluetooth är fysiskt omöjlig. Det bästa du kan göra är att mata Bluetooth-kodaren med den högsta kvalitetens källsignal och låta den göra bästa möjliga komprimering.
Hur Echobox navigerar Android-ljudstacken
Vi designade Echobox från grunden för att arbeta med (och runt) begränsningarna i Androids ljudsystem. Vår trelagersarkitektur — Flutter för gränssnittet, Rust för ljudorkestrering och Zig för realtidsutgång — ger oss ovanlig kontroll över vad som händer med ditt ljud i varje steg av pipelinen.
Arkitekturen
Flutter-lagret hanterar allt du ser och interagerar med — biblioteksbläddraren, nu-spelas-skärmen och inställningar. Det rör aldrig ljuddata direkt.
Rust-motorn sitter i mitten och gör det tunga arbetet: filavkodning (via Symphonia-biblioteket för format som FLAC, MP3, AAC och DSD), samplingsfrekvenskonvertering, formatnormalisering, ljudanalys och tillståndshantering. Vi valde Rust eftersom ljudbearbetning behöver vara både korrekt och snabb — dess kombination av minnessäkerhet och nollkostnadsabstraktioner innebär att vi inte behöver välja mellan de två.
Zig-lagret kör realtidsljud-callbacken — koden som aktiveras ungefär var tionde millisekund när operativsystemet begär nästa bit ljudsamplingar. Denna kod måste svara omedelbart med noll allokeringar och noll blockerande operationer. Vi använder Zig för callbacken eftersom det garanterar inget dolt kontrollflöde och ingen dold minnesallokering — du kan läsa koden och veta exakt vad den kommer att göra vid körning. Zig-callbacken läser föravkodade samplingar från en låsfri ringbuffert (fylld av Rust-motorn) och applicerar en sjustegs DSP-kedja: ReplayGain, förförstärkare, parametrisk EQ, crossfeed, volym, grafisk EQ och limiter.
Intelligent samplingsfrekvenshantering
Istället för att blint skicka ljud vid vilken frekvens som helst och hoppas på det bästa förhandlar vi aktivt med enheten:
- Undersök enhetens ursprungliga frekvens genom att öppna en tillfällig AAudio-ström och låta Android rapportera den optimala frekvensen för den aktuella utgångsenheten.
- Begär den frekvensen vid initiering av den riktiga uppspelningsströmmen.
- Verifiera den beviljade frekvensen genom att läsa tillbaka vad Android faktiskt tillhandahöll — eftersom den beviljade frekvensen kan skilja sig från vad som begärdes.
- Sampla om intelligent med en högkvalitets sinc-interpolationsomsamplare när spårets samplingsfrekvens skiljer sig från enhetsfrekvensen. Denna omsamplare använder ett 256-taps FIR-filter med BlackmanHarris-fönster, vilket är betydligt bättre än AudioFlingers inbyggda omsamplare.
Den kritiska fördelen här är att undvika dubbel omsampling. Om en naiv app avkodar en 44,1 kHz FLAC och ger ut den vid 44,1 kHz på en 48 kHz-enhet kommer AudioFlinger att sampla om den till 48 kHz med sin egen algoritm. Vi undviker detta genom att detektera att enheten körs vid 48 kHz och utföra en ren, högkvalitativ omsampling till 48 kHz själva — så AudioFlinger har inget kvar att konvertera.
Bit-perfekt-läge
För USB DAC-användare erbjuder Echobox ett bit-perfekt-läge som driver Androids kapaciteter så långt de kan gå:
- Begär spårets exakta ursprungliga samplingsfrekvens från DAC:en.
- Kringgår hela DSP-kedjan — ingen EQ, ingen volymjustering, ingen ReplayGain, ingen limiter. Avkodade samplingar passerar igenom omodifierade.
- Om DAC:en inte kan stödja den begärda frekvensen misslyckas uppspelningen med ett tydligt felmeddelande istället för att tyst sampla om. Detta är avsiktligt — om du har bett om bit-perfekt förtjänar du att veta när du inte får det.
Signalvägens transparens
Kanske viktigast av allt visar Echobox dig exakt vad som händer. Signalvägsdiagnostiken avslöjar hela ljudkedjan i realtid: källformat och samplingsfrekvens, om omsampling är aktiv och vid vilken kvalitet, vilka DSP-steg som är engagerade, vilken utfrekvens enheten faktiskt körs vid, och vilken Bluetooth-kodek som används om tillämpligt.
Denna nivå av transparens är sällsynt i musikspelarappar. De flesta spelare är en svart låda — du trycker play och litar på att rätt sak händer. Echobox låter dig verifiera. Om din 96 kHz FLAC samplas om till 48 kHz eftersom din telefons interna DAC inte stöder 96 kHz ser du det. Om din USB DAC accepterar 96 kHz och DSP-kedjan är helt kringgången ser du det också.
Ruttmedvetet beteende
Echobox klassificerar varje utgångsenhet efter ruttyp — lokal högtalare, USB DAC, Bluetooth eller nätverksrenderer — och anpassar sitt beteende därefter. När Bluetooth detekteras avaktiveras bit-perfekt-läge automatiskt (eftersom det är meningslöst över en förlustbringande trådlös kodek) och DSP-bearbetning förblir aktiv så du kan använda EQ och volymkontroll. När en USB DAC ansluts blir hela utbudet av samplingsfrekvensförhandling och bit-perfekt-alternativ tillgängliga.
Denna ruttmedvetna policy innebär att du inte behöver manuellt justera inställningar varje gång du byter mellan hörlurar och högtalare. Appen detekterar förändringen och gör intelligenta standardval för varje utgångstyp. För UPnP/DLNA-streaming till nätverkshögtalare lägger Echobox till ytterligare ett lager av intelligens — detekterar enhetskapaciteter och omkodar vid behov. Och om du utvärderar vad som gör en musikspelare genuint audiofil-klass utöver bara ljudstacken täcker vår guide om audiofila musikspelare hela bilden. Du kan också kolla färdplanen för plattformstillgänglighet utöver Android.
Verkligheten för Android-ljud
- AudioFlinger är flaskhalsen. Varje ljud på Android passerar genom denna systemmixer, som arbetar med en fast samplingsfrekvens (vanligtvis 48 kHz). Allt ljud samplas om till denna frekvens innan det når hårdvaran.
- Google designade AudioFlinger för allmänt bruk, inte audiofil uppspelning. Att mixa notifikationer, samtal och musik till en ström kräver en gemensam samplingsfrekvens, och omsampling är avvägningen. Det är ett rimligt designbeslut — bara inte ett som gjordes med oss i åtanke.
- USB DAC:ar erbjuder den bästa vägen till kvalitet på Android. De stöder flera samplingsfrekvenser, och när de adresseras korrekt kan AudioFlinger låta ljud passera igenom utan omsampling.
- Android erbjuder inte äkta exklusivt läge. Till skillnad från WASAPI Exclusive i Windows eller hog mode i macOS finns det inget sätt att helt kringgå AudioFlinger. Mixern är alltid i vägen, även om den fungerar som genomsläpp.
- Bluetooth lägger till ytterligare en förlustbringande konvertering ovanpå allt annat. Ingen Bluetooth-kodek är förlustfri, och bit-perfekt uppspelning är omöjlig över trådlöst.
- Echobox hanterar komplexiteten genom att undersöka enhetsfrekvenser, utföra högkvalitativ omsampling vid behov, erbjuda bit-perfekt USB DAC-utdata och visa dig exakt vad som händer via signalvägsdiagnostik. Vår Rust/Zig-arkitektur ger oss kontroll över ljudpipelinen som de flesta appar helt enkelt inte har.
- Den ärliga slutsatsen: på Android kräver att få din musik från app till öron utan oönskad bearbetning antingen en USB DAC med korrekt drivrutinsstöd eller acceptans av att systemet kommer att sampla om. Vi byggde Echobox för att ge dig verktygen och transparensen att navigera den verkligheten — och om du är en utvecklare som bygger något liknande hoppas vi att den här guiden sparar dig några av de månader vi lade på att lista ut det.