Android audioarchitectuur Android audio stack AudioFlinger resamplen

De Android Audio Stack: Waarom Je Muziek Wordt Geresampeld

Een diepgaande blik op hoe Android audio verwerkt — van app tot luidspreker. Leer waarom AudioFlinger je muziek resamplet en wat je eraan kunt doen.

· 12 min leestijd

Hoe Android audio afspeelt

Elk geluid dat je Android-telefoon maakt — meldingen, telefoongesprekken, game-effecten, muziek — reist door dezelfde audiopijplijn voordat het je oren bereikt. Het begrijpen van deze pijplijn is de sleutel tot het begrijpen waarom je zorgvuldig samengestelde lossless muziekcollectie misschien niet helemaal zo ongerept klinkt als je zou verwachten.

Het standaard Android-audiopad ziet er zo uit:

App -> AudioTrack API -> AudioFlinger -> HAL (Hardware Abstraction Layer) -> Hardware (luidspreker, DAC, Bluetooth-radio)

Wanneer een muziekspeler-app geluid wil produceren, maakt het een AudioTrack aan (of gebruikt de nieuwere AAudio API, die nog steeds door hetzelfde systeem wordt gerouteerd). De app schrijft PCM-audiosamples naar deze track — gedecodeerde audiodata op welke samplerate en bitdiepte het bronbestand ook bevat.

Die samples komen dan in AudioFlinger terecht, Android’s centrale audiomix- en routeringsdienst. AudioFlinger is de verkeersregelaar van Android-audio. Het neemt audiostreams van elke app op het systeem, mixt ze samen, past systeemniveau-effecten toe (zoals het systeemvolume) en routeert het resultaat naar het juiste uitvoerapparaat. Het draait als een native systeemdienst met verhoogde prioriteit, en elk audiosamples op je apparaat gaat erdoorheen.

Onder AudioFlinger zit de Hardware Abstraction Layer (HAL), een apparaat-specifieke vertaallaag geschreven door de telefoonfabrikant. De HAL converteert de uitvoer van AudioFlinger naar welk formaat de daadwerkelijke hardware ook verwacht — of dat nu een I2S-stream is voor de interne DAC, USB-audiopakketten voor een externe DAC, of gecodeerde Bluetooth-audio voor draadloze koptelefoons.

Deze architectuur is elegant vanuit een systeemontwerpperspectief. Elke app kan audio afspelen zonder zich zorgen te maken over hardwarespecificaties, meerdere audiostreams mixen naadloos, en het systeem behoudt controle over routering en volumebeleid. Maar voor muziekweergave introduceert het een probleem.

Het AudioFlinger-probleem

AudioFlinger werkt op een vaste samplerate. Op de meeste Android-apparaten is deze snelheid ofwel 44.100 Hz of 48.000 Hz — de apparaatfabrikant beslist wanneer ze de HAL configureren, en het kan doorgaans niet veranderen terwijl het systeem draait. De meest voorkomende standaard op moderne telefoons is 48 kHz.

Deze vaste snelheid bestaat omdat AudioFlinger in de kern een mixer is. Het moet audio van meerdere bronnen combineren — je muziek, een inkomende melding, navigatie-aanwijzingen, een telefoongesprek — tot een enkele uitvoerstroom. Het mixen van audio vereist dat alle streams op dezelfde samplerate staan. In plaats van de mixersnelheid dynamisch te wijzigen elke keer dat een nieuwe stream begint (wat alle andere actieve streams zou verstoren), kiest AudioFlinger één snelheid en resamplet alles om overeen te komen.

Dus elke audiostream op het apparaat wordt geresampeld naar de vaste snelheid van de mixer:

  • Je 96 kHz hi-res FLAC? Gedownsampeld naar 48 kHz.
  • Je 44,1 kHz cd-rip op een 48 kHz-mixer? Opgesampeld naar 48 kHz.
  • Je 48 kHz podcast op een 48 kHz-apparaat? Gaat ongewijzigd door — geluk gehad.
  • Je DSD64 omgezet naar 176,4 kHz PCM? Gedownsampeld naar 48 kHz.

Google heeft AudioFlinger ontworpen voor het algemene geval, en eerlijk gezegd is het een redelijke engineeringbeslissing voor een consumententelefoon. Een apparaat dat meldingsgeluiden afspeelt terwijl het Spotify streamt en navigeert, heeft een gemeenschappelijke mixsnelheid nodig. Maar het was nooit ontworpen voor audiofiele weergave, en de resamplingkwaliteit is, hoewel adequaat, niet wat je zou kiezen als geluidskwaliteit het enige doel was.

De resampler die in AudioFlinger is ingebouwd, is door de jaren heen verbeterd. Vroege Android-versies gebruikten een lineaire interpolator van lage kwaliteit. Moderne versies (Android 5.0 en later) gebruiken een polyfase sinc-resampler die redelijke resultaten produceert. Maar “redelijk” en “transparant” zijn niet hetzelfde — elke resamplingoperatie introduceert enige mate van kwantisatieruis en potentiële aliasing-artefacten, hoe klein ook.

Samplerate-onderhandeling

De situatie is niet volledig hopeloos. Android biedt mechanismen waarmee apps de uitvoer-samplerate kunnen beïnvloeden, hoewel de resultaten… inconsistent zijn.

Wanneer een app een audiostream maakt via de AAudio API (Android’s moderne native audio-interface), kan het een specifieke samplerate aanvragen. Android zal proberen dit verzoek te honoreren, en de app kan dan controleren welke snelheid daadwerkelijk is toegekend. Als de apparaathardware en HAL de gevraagde snelheid ondersteunen, krijg je mogelijk native weergave zonder resamplen.

Android 12 en later verbeterde dit met betere ondersteuning voor native samplerate-schakeling. Op compatibele apparaten kan het systeem de HAL-uitvoersnelheid wijzigen om overeen te komen met wat de app vraagt, met name voor USB-audioapparaten. Een goed ontworpen app kan mogelijk bit-perfecte uitvoer krijgen op de native snelheid van het nummer.

Maar “correct ondersteunt” doet veel werk in die zin. De ervaring is maddening inconsistent:

  • Samsung vergrendelt de samplerate op een vaste waarde in hun HAL-implementatie. Ongeacht wat de app vraagt, draait de hardware altijd op 48 kHz.
  • Pixel-apparaten zijn over het algemeen meer meegaand en laten je onderhandelen. Maar zelfs hier varieert het gedrag tussen hardwaregeneraties.
  • OnePlus? Hangt af van de firmware-versie. Dit is de realiteit van Android-audio-ontwikkeling.
  • Sommige OEM’s staan dynamische snelheidswisseling toe maar alleen voor bepaalde uitvoerapparaten (USB DAC’s ja, koptelefoonaansluiting nee).
  • Ontwikkelaarsinstellingen op sommige apparaten tonen een “USB-audioroutering”-schakelaar of samplerate-overschrijving, maar deze zijn verborgen voor typische gebruikers en niet gestandaardiseerd.

Het praktische resultaat is dat een app-ontwikkelaar niet kan vertrouwen op het krijgen van enige specifieke samplerate. De app moet het apparaat peilen, een snelheid aanvragen, controleren wat het daadwerkelijk heeft ontvangen en zich dienovereenkomstig aanpassen. Dit peil-verzoek-verificeer patroon is de enige betrouwbare aanpak.

De USB DAC-bypass

USB audio class 1 en class 2 apparaten bieden het meest veelbelovende pad naar hoogwaardige audio-uitvoer op Android. Wanneer je een USB DAC aansluit, creëert Android’s USB-audiodriver een nieuw audio-uitvoerapparaat waar AudioFlinger naartoe kan richten. Omdat USB DAC’s doorgaans meerdere samplerates ondersteunen en hun mogelijkheden aan de host rapporteren, is er een betere kans op native snelheidsweergave.

De audioketen met een USB DAC ziet er zo uit:

App -> AAudio API -> AudioFlinger -> USB Audio HAL -> USB Audio Class Driver -> USB DAC

Merk op dat AudioFlinger nog steeds in het pad zit. In tegenstelling tot desktopbesturingssystemen — waar WASAPI Exclusive mode op Windows of hog mode in macOS CoreAudio de systeemmixer volledig kunnen omzeilen — biedt Android geen echte exclusieve toegang tot audiohardware. AudioFlinger zit altijd tussen de app en het apparaat. Altijd.

Echter, wanneer de sterren op één lijn staan, kan AudioFlinger optreden als doorgifte in plaats van resampler:

  1. De app vraagt de native samplerate van het nummer aan via AAudio.
  2. AudioFlinger controleert of de USB audio HAL die snelheid ondersteunt.
  3. Indien ondersteund, configureert AudioFlinger zijn uitvoer op die snelheid.
  4. Omdat de enige actieve audiostream overeenkomt met de mixersnelheid, vindt er geen resamplen plaats.
  5. Samples gaan ongewijzigd door AudioFlinger en bereiken de USB DAC op de originele snelheid.

Dit is zo dicht bij bit-perfect als Android komt. De samples worden wiskundig niet gewijzigd, ook al gaan ze technisch door de mixer. Hiervoor heb je ook geen systeemgeluiden of andere audiostreams nodig die tegelijkertijd actief zijn — al het andere dat speelt zou AudioFlinger terugdwingen naar mixen en mogelijk resamplen.

De app moet aanzienlijke complexiteit afhandelen om dit te laten werken: het peilen van de ondersteunde snelheden van het USB-apparaat, het aanvragen van de juiste snelheid, het verifiëren van de toegekende snelheid, en het netjes afhandelen van apparaatontkoppeling. De app moet ook zijn eigen audiobuffergrootte beheren, omdat USB DAC’s andere latentiekenmerken hebben dan de ingebouwde audio van de telefoon.

Bluetooth: Nog een conversielaag

Bluetooth-audio voegt een volledig aparte conversiestap toe bovenop AudioFlinger. Nadat je audio door de systeemmixer is gegaan, komt het in de Bluetooth-audiostack terecht, waar het wordt gecodeerd in een Bluetooth-audiocodec voor draadloze overdracht.

De keten wordt:

App -> AudioFlinger -> Bluetooth-codecencoder -> Draadloze overdracht -> Koptelefoon/luidspreker

Elke Bluetooth-codec is lossy. Geen uitzonderingen. De beschikbare bandbreedte op een Bluetooth-verbinding is simpelweg niet genoeg voor ongecomprimeerde audio op hoge samplerates. De codecs verschillen in hoe agressief ze comprimeren en welke kwaliteit ze bereiken binnen de beschikbare bandbreedte.

SBC (Sub-Band Codec) is de universele basislijn — elk Bluetooth-audioapparaat ondersteunt het. Het piekt op ongeveer 345 kbps en 48 kHz. Het is aanzienlijk verbeterd met betere encoder-implementaties, maar het is nog steeds de laagste gemene deler.

LDAC, ontwikkeld door Sony en opgenomen in Android sinds versie 8.0, biedt de hoogste kwaliteit op tot 990 kbps en 96 kHz. Op zijn beste instelling wordt LDAC over het algemeen als transparant beschouwd voor de meeste inhoud — wat betekent dat getrainde luisteraars moeite hebben om het van het bedrade origineel te onderscheiden in gecontroleerde tests. Maar het is nog steeds lossy compressie.

Voor een gedetailleerde vergelijking van alle Bluetooth-codecs — LDAC, aptX, aptX HD, aptX Adaptive, AAC, SBC en LC3 — zie onze gids over Bluetooth-audiocodecs.

Het belangrijke punt: zelfs als je het resamplen van AudioFlinger omzeilt (door een native snelheidsuitvoer te krijgen), hercomprimeert Bluetooth-codering alles daarna. Bit-perfecte weergave over Bluetooth is fysiek onmogelijk. Het beste wat je kunt doen is de Bluetooth-encoder het hoogst mogelijke kwaliteitsbronignaal voeden en het de best mogelijke compressie laten doen.

Hoe Echobox de Android Audio Stack navigeert

We hebben Echobox van de grond af ontworpen om te werken met (en rond) de beperkingen van Android’s audiosysteem. Onze drielaags-architectuur — Flutter voor de UI, Rust voor audio-orkestratie, en Zig voor realtime uitvoer — geeft ons ongewone controle over wat er met je audio gebeurt in elke fase van de pijplijn.

De architectuur

De Flutter-laag verwerkt alles wat je ziet en waarmee je interageert — de bibliothekbrowser, het nu-afspelendscherm en instellingen. Het raakt audiodata nooit rechtstreeks aan.

De Rust-engine zit in het midden en doet het zware werk: bestandsdecodering (via de Symphonia-bibliotheek voor formaten als FLAC, MP3, AAC en DSD), samplerate-conversie, formaatsnormalisatie, audioanalyse en statusbeheer. We kozen Rust omdat audioverwerking zowel correct als snel moet zijn — de combinatie van geheugenveiligheid en zero-cost abstracties betekent dat we niet hoeven te kiezen tussen de twee.

De Zig-laag draait de realtime audiocallback — de code die elke ~10 milliseconden afgaat wanneer het besturingssysteem het volgende blok audiosamples opvraagt. Deze code moet onmiddellijk reageren met nul allocaties en nul blokkerende bewerkingen. We gebruiken Zig voor de callback omdat het geen verborgen controlestroom en geen verborgen geheugenallocatie garandeert — je kunt de code lezen en precies weten wat het tijdens runtime zal doen. De Zig-callback leest vooraf gedecodeerde samples uit een lockvrije ringbuffer (gevuld door de Rust-engine) en past een zeven-fasen DSP-keten toe: ReplayGain, voorversterker, parametrische EQ, crossfeed, volume, grafische EQ en limiter.

Intelligente samplerate-afhandeling

In plaats van blindelings audio te versturen op welke snelheid dan ook en te hopen op het beste, onderhandelen we actief met het apparaat:

  1. Peil de native snelheid van het apparaat door een tijdelijke AAudio-stream te openen en Android de optimale snelheid te laten rapporteren voor het huidige uitvoerapparaat.
  2. Vraag die snelheid aan bij het initialiseren van de echte afspelstream.
  3. Verifieer de toegekende snelheid door terug te lezen wat Android daadwerkelijk heeft geleverd — omdat de toegekende snelheid kan verschillen van wat is aangevraagd.
  4. Resampel intelligent met een hoogwaardige sinc-interpolatieresampler wanneer de samplerate van het nummer verschilt van de apparaatsnelheid. Deze resampler gebruikt een 256-tap FIR-filter met een BlackmanHarris-venster, wat aanzienlijk beter is dan de ingebouwde resampler van AudioFlinger.

Het cruciale voordeel hier is het vermijden van dubbel resamplen. Als een naïeve app een 44,1 kHz FLAC decodeert en het uitvoert op 44,1 kHz op een 48 kHz-apparaat, zal AudioFlinger het resamplen naar 48 kHz met zijn eigen algoritme. Wij vermijden dit door te detecteren dat het apparaat op 48 kHz draait en één schone, hoogwaardige resample naar 48 kHz zelf uit te voeren — zodat AudioFlinger niets meer hoeft te converteren.

Bit-perfecte modus

Voor USB DAC-gebruikers biedt Echobox een bit-perfecte modus die de mogelijkheden van Android zo ver mogelijk duwt:

  • Vraagt de exacte native samplerate van het nummer aan bij de DAC.
  • Slaat de volledige DSP-keten over — geen EQ, geen volumeaanpassing, geen ReplayGain, geen limiter. Gedecodeerde samples gaan ongewijzigd door.
  • Als de DAC de gevraagde snelheid niet kan ondersteunen, mislukt de weergave met een duidelijke fout in plaats van stilletjes te resamplen. Dit is opzettelijk — als je om bit-perfect hebt gevraagd, verdien je het te weten wanneer je het niet krijgt.

Signaalpadtransparantie

Misschien het belangrijkst: Echobox toont je precies wat er gebeurt. De signaalpaddiagnostiekweergave onthult de volledige audioketen in realtime: bronformaat en samplerate, of resamplen actief is en op welke kwaliteit, welke DSP-fasen zijn ingeschakeld, op welke uitvoersnelheid het apparaat daadwerkelijk draait, en welke Bluetooth-codec in gebruik is indien van toepassing.

Dit niveau van transparantie is zeldzaam in muziekspeler-apps. De meeste spelers zijn een zwarte doos — je drukt op afspelen en vertrouwt erop dat het juiste gebeurt. Echobox laat je verifiëren. Als je 96 kHz FLAC wordt geresampeld naar 48 kHz omdat de interne DAC van je telefoon geen 96 kHz ondersteunt, zie je dat. Als je USB DAC 96 kHz accepteert en de DSP-keten volledig is overgeslagen, zie je dat ook.

Route-bewust gedrag

Echobox classificeert elk uitvoerapparaat op routetype — lokale luidspreker, USB DAC, Bluetooth of netwerkrenderer — en past zijn gedrag dienovereenkomstig aan. Wanneer Bluetooth wordt gedetecteerd, wordt de bit-perfecte modus automatisch uitgeschakeld (omdat het zinloos is over een lossy draadloze codec) en blijft DSP-verwerking actief zodat je EQ en volumeregeling kunt gebruiken. Wanneer een USB DAC is aangesloten, wordt het volledige scala aan samplerate-onderhandeling en bit-perfecte opties beschikbaar.

Dit route-bewuste beleid betekent dat je niet handmatig instellingen hoeft aan te passen elke keer dat je wisselt tussen koptelefoons en luidsprekers. De app detecteert de verandering en maakt intelligente standaardwaarden voor elk uitvoertype. Voor UPnP/DLNA-streaming naar netwerkluidsprekers voegt Echobox nog een intelligentielaag toe — apparaatmogelijkheden detecteren en transcoderen wanneer nodig. En als je evalueert wat een muziekspeler echt audiofiel maakt naast alleen de audiostack, behandelt onze gids over audiofiele muziekspelers het volledige plaatje. Je kunt ook de roadmap bekijken voor platformbeschikbaarheid naast Android.

De realiteit van Android-audio

  • AudioFlinger is het knelpunt. Elk geluid op Android gaat door deze systeemmixer, die op een vaste samplerate werkt (meestal 48 kHz). Alle audio wordt geresampeld naar deze snelheid voordat het de hardware bereikt.
  • Google heeft AudioFlinger ontworpen voor algemeen gebruik, niet voor audiofiele weergave. Het mixen van meldingen, gesprekken en muziek in één stream vereist een gemeenschappelijke samplerate, en resamplen is de afweging. Het is een redelijke ontwerpbeslissing — alleen niet een die met ons in gedachten is gemaakt.
  • USB DAC’s bieden het beste pad naar kwaliteit op Android. Ze ondersteunen meerdere samplerates, en wanneer ze correct worden aangestuurd, kan AudioFlinger audio doorlaten zonder resamplen.
  • Android biedt geen echte exclusieve modus. In tegenstelling tot WASAPI Exclusive op Windows of hog mode op macOS is er geen manier om AudioFlinger volledig te omzeilen. De mixer zit altijd in het pad, zelfs als het als doorgifte fungeert.
  • Bluetooth voegt nog een lossy conversie toe bovenop al het andere. Geen enkele Bluetooth-codec is lossless, en bit-perfecte weergave is onmogelijk via draadloos.
  • Echobox handelt de complexiteit af door apparaatsnelheden te peilen, hoogwaardig resamplen uit te voeren wanneer nodig, bit-perfecte USB DAC-uitvoer te bieden en je precies te tonen wat er gebeurt via signaalpaddiagnostiek. Onze Rust/Zig-architectuur geeft ons controle over de audiopijplijn die de meeste apps simpelweg niet hebben.
  • De eerlijke conclusie: op Android vereist het krijgen van je muziek van app tot oren zonder ongewenste verwerking ofwel een USB DAC met juiste driverondersteuning of acceptatie dat het systeem zal resamplen. We hebben Echobox gebouwd om je de tools en transparantie te geven om die realiteit te navigeren — en als je een ontwikkelaar bent die iets vergelijkbaars bouwt, hopen we dat deze gids je een deel van de maanden bespaart die wij hebben besteed aan het uitzoeken ervan.

Gerelateerde gidsen


Probeer Echobox

Ervaar wat deze gidsen beschrijven — precisieweergave op Android.

Eén e-mail per mijlpaal. Geen ruis.