Software-Experimente mit Linux
Verfasst: 25.10.2013, 00:51
Hallo Forenten,
in einem anderen Thread (http://aktives-hoeren.de/viewtopic.php?f=30&t=4510) habe ich von einem Hardware-Projekt berichtet, das einen sehr guten Audio-Rechner hervorgebracht hat, der meinen USB DAC beliefert (asynchron, USB 2.0). Hier möchte ich kurz ein paar Ideen beschreiben, mit denen ich die Wiedergabe durch Software weiter verbessern konnte; einiges davon ist vielleicht auch bei anderem Hardware-Setup interessant. Alle Experimente wurden ausschließlich auf Linux-Rechnern gemacht.
Beim Abspielen eines digitalen Audiosignals gibt es diverse "Jitter"-Probleme, was bedeutet, dass das digitale Signal nicht zum optimalen Zeitpunkt beim DAC ankommt. Die digitale Information wird ja mittels analoger Signale übertragen. Der Vorteil ist, dass es eine große Bandbreite gibt, wie dieses analoge Signal aussehen kann, um die digitale Information noch "bit-genau" rekonstruieren zu können. Aber "bit-genau" ist im Hinblick auf Klangqualität nicht gut genug.
Vielleicht kann man zwei Hauptprobleme so beschreiben:
(a) Durch diverse Hardware-Komponenten eines Rechners, Kabel, und nicht-optimale Stromversorgung wird das digitale Signal durch zufällige Störungen überlagert.
(b) Es gibt zeitliche Variationen im Digitalsignal, die eher systematisch beim Abspielen entstehen durch die Abspielsoftware.
Das Problem (a) konnte ich durch das oben zitierte Hardware-Projekt ganz erfolgreich verringern.
Hier wollte ich ein paar Experimente aufzählen, mit denen ich versucht habe Problem (b) zu verringern, teilweise auch mit klar hörbarem Erfolg.
Bei mir sind beim Musikabspielen immer mindestens 2 Rechner beteiligt, ein Zuspielrechner und der Audio-Rechner, an dem der USB DAC hängt. Beim Zuspielrechner gibt es die Variante, bei dem die originalen Musikdateien beim Abspielen noch durch einen Raum-/Systemkorrektur- und andere Filter geschickt werden. Oder es gibt die Variante eines schwächeren Rechners, der bereits vorgefilterte Daten zum Audio-Rechner schickt.
(1) Es ist unter Linux sehr leicht, alle nicht benötigten Prozesse abzuschalten und Kernel-Module zu entfernen (Kommandos "service ... stop", "pkill ..." und "rmmod ..."). Damit wird die Chance erhöht, dass mein eigentliches Abspielprogramm auch "dran" ist, wenn es soll.
Zum Beispiel laufen auf meinem Audio-Rechner neben meinem eigentlichen Abspielprogramm nur noch etwa 25 Kernel-Prozesse für den Zugriff auf die benötigte Hardware und das Prozessmanagement. Nur 5 davon haben in den 3 Monaten seit dem letzten Booten mehr als 1 Sekunde Rechenzeit benötigt.
Dies alleine bringt keine dramatische Klangverbesserung - aber es ist immer gut, potenzielle Störquellen ganz auszuschalten.
(2) Man kann unter Linux leicht wichtigen Prozessen eine hohe Priorität im "Realtime Scheduler" geben (Kommando 'chrt'). Das ist gut für das eigentliche Abspielprogramm, das die Daten zu DAC/Soundkarte oder ins Netzwerk schickt.
(3) Wenn in Linux Daten durch mehrere Programme geschickt werden (Pipes) oder Daten vom Netzwerk oder aus einer Datei gelesen werden, dann legt das Betriebssytem Datenpuffer an, verwaltet diese und sorgt dafür, dass Prozesse angehalten werden, solange keine Daten vorliegen. Die Details sind hier nicht so leicht zu verstehen und zu beeinflussen. Man kann aber mit Buffergrößen experimentieren, die sich mit dem Programm 'stdbuf' beeinflussen lassen. Zum Beispiel kann es (kleine) klangliche Vorteile geben, wenn die Programme, die Daten ins Netzwerk schreiben oder von dort lesen, einen größeren Buffer bekommen.
(4) Ich habe etwas mit dem Abspielen aus dem Arbeitsspeicher (RAM) experimentiert. Zum Beispiel auf dem Audio-Rechner direkt eine im RAM gespeicherte Datei spielen statt einen Stream vom Netzwerk. Oder vorgefilterte Daten auf dem Zuspielrechner aus dem RAM statt von Festplatte lesen. In beiden Fällen konnte ich (allerdings eher geringfügige) Verbesserungen feststellen. Meine genannten Rechner haben aber nur 256 MB RAM, so dass Abspielen aus dem RAM nur für kurze Teststücke praktikabel ist.
Für eine praktikable Variante habe ich zwei kleine (C-)Programme geschrieben, die einen Datenstrom zyklisch in mehrere Dateien schreiben, beziehungsweise diese Dateien lesen und wieder als Datenstrom ausgeben. Wenn ich dies für die Datenpufferung im RAM verwende und die Dateien nicht zu klein wähle (etwa ein paar MB), dann ist diese Variante genauso gut, wie das Abspielen einer vorher in den RAM kopierten Datei (auf dem Audio-Rechner und auch auf dem Zuspielrechner bevor der Datenstrom ins Netzwerk geschrieben wird).
Eine deutliche Verbesserung ergibt sich, wenn die Hilfsdateien für die Datenpufferung so klein gewählt werden, dass sie in den Prozessorcache passen (etwa nur ein paar kB). (256 MB sind also unnötig viel Arbeitsspeicher!)
(5) Mit den in (1)-(4) genannten Ideen konnte ich bereits eine gute Verbesserung der Wiedergabe erreichen. Der wirklich große Schritt vorwärts wurde dann durch folgende Idee erreicht: Man kann den Linux Kernel mit "High Resolution Timer" kompilieren, was ich für meine Rechner getan habe. Die GNU C-library hat "realtime"-Funktionalität, die durch Funktionen 'clock_getres', 'clock_nanosleep' und ein paar mehr verfügbar ist. Hiermit kann man einen Prozess bis zu einem vorgegebenen Zeitpunkt anhalten (mit 'clock_nanosleep' im Modus 'TIMER_ABSTIME', siehe UNIX man-pages für weitere Details).
Dies benutze ich nun, um Datenströme mit sehr genauem Timing zu erzeugen. Ich habe einige kleine (C-)Programme geschrieben, die etwa so funktionieren: in einer Schleife (die zum Beispiel 2000 Mal pro Sekunde durchlaufen wird) werden Datenblöcke (aus einer Datei, von einer Pipe oder aus dem Netzwerk) gelesen und in einen Puffer geschrieben, die als nächstes herauszuschreibenden Daten werden vorbereitet, dann wird der Prozess bis zu einem vorgegebenen Zeitpunkt schlafen gelegt, und nach dem Aufwachen wird als allererstes ein Datenblock herausgeschrieben.
Zum Beispiel läuft jetzt auf meinem Audio-Rechner ein solches Programm. Es füllt einen Puffer, indem es Daten vom Netzwerk liest, legt sich schlafen und schreibt nach dem Aufwachen einen Datenblock direkt in den Puffer des USB-Audio Treibers, usw. Das ist so präzise, dass ich den Puffer des Treibers sehr klein wählen kann (8kB) und die Daten im non-blocking Modus dort hineinschreiben kann, ohne dass es in 80 Minuten (Daten im 192/32 Format, mehr als 7GB) zu einen Überlauf oder Unterlauf dieses Puffers kommt. Auch hat der Interrupt-Prozess des Kernels nichts mehr zu tun, weil ich auch auf dem Zuspielrechner mit einem kleinen Programm dafür sorge, dass der Audio-Rechner nur genau so viel Daten über das Netzwerk bekommt, wie er braucht; auf diese Weise werden die Puffer für die Netzwerkverbindung nie voll (oder leer).
(6) ... ich habe noch eine Reihe von Ideen, alle paar Wochen probiere ich mal was Neues und wenn ich meine, dass eine Änderung den Klang verbessert hat, teste ich das wieder für ein paar Wochen. Im Moment habe ich einen Raspberry Pi als Netzwerk-Zwischenpuffer zwischen meinem Zuspiel- und dem Audiorechner laufen; da ist fast kein Unterschied mehr zwischen Daten on-the-fly filtern und dem Abspielen von vorgefilterten Daten.
----
Klangbeschreibungen lasse ich jetzt mal. Ich könnte einige der begeisterten Beschreibungen aus den Windows-Optimierung Threads in diesem Forum hierher kopieren. Ich habe selbst nie ein optimiertes Windows Setup gehört, kann also nichts über einen Vergleich sagen (der sicher interessant wäre).
Falls jemand hierdurch angeregt wurde, selbst in dieser Richtung zu experimentieren, kann ich gerne weitere Details erklären. Umgekehrt sind natürlich auch Tipps für weitere Verbesserungen willkommen.
Viele Grüße,
Frank
PS: Gar nicht geredet habe ich hier über die Filter, durch die meine Musikdaten vor dem Abspielen geschickt werden, das ist eine andere Geschichte.
in einem anderen Thread (http://aktives-hoeren.de/viewtopic.php?f=30&t=4510) habe ich von einem Hardware-Projekt berichtet, das einen sehr guten Audio-Rechner hervorgebracht hat, der meinen USB DAC beliefert (asynchron, USB 2.0). Hier möchte ich kurz ein paar Ideen beschreiben, mit denen ich die Wiedergabe durch Software weiter verbessern konnte; einiges davon ist vielleicht auch bei anderem Hardware-Setup interessant. Alle Experimente wurden ausschließlich auf Linux-Rechnern gemacht.
Beim Abspielen eines digitalen Audiosignals gibt es diverse "Jitter"-Probleme, was bedeutet, dass das digitale Signal nicht zum optimalen Zeitpunkt beim DAC ankommt. Die digitale Information wird ja mittels analoger Signale übertragen. Der Vorteil ist, dass es eine große Bandbreite gibt, wie dieses analoge Signal aussehen kann, um die digitale Information noch "bit-genau" rekonstruieren zu können. Aber "bit-genau" ist im Hinblick auf Klangqualität nicht gut genug.
Vielleicht kann man zwei Hauptprobleme so beschreiben:
(a) Durch diverse Hardware-Komponenten eines Rechners, Kabel, und nicht-optimale Stromversorgung wird das digitale Signal durch zufällige Störungen überlagert.
(b) Es gibt zeitliche Variationen im Digitalsignal, die eher systematisch beim Abspielen entstehen durch die Abspielsoftware.
Das Problem (a) konnte ich durch das oben zitierte Hardware-Projekt ganz erfolgreich verringern.
Hier wollte ich ein paar Experimente aufzählen, mit denen ich versucht habe Problem (b) zu verringern, teilweise auch mit klar hörbarem Erfolg.
Bei mir sind beim Musikabspielen immer mindestens 2 Rechner beteiligt, ein Zuspielrechner und der Audio-Rechner, an dem der USB DAC hängt. Beim Zuspielrechner gibt es die Variante, bei dem die originalen Musikdateien beim Abspielen noch durch einen Raum-/Systemkorrektur- und andere Filter geschickt werden. Oder es gibt die Variante eines schwächeren Rechners, der bereits vorgefilterte Daten zum Audio-Rechner schickt.
(1) Es ist unter Linux sehr leicht, alle nicht benötigten Prozesse abzuschalten und Kernel-Module zu entfernen (Kommandos "service ... stop", "pkill ..." und "rmmod ..."). Damit wird die Chance erhöht, dass mein eigentliches Abspielprogramm auch "dran" ist, wenn es soll.
Zum Beispiel laufen auf meinem Audio-Rechner neben meinem eigentlichen Abspielprogramm nur noch etwa 25 Kernel-Prozesse für den Zugriff auf die benötigte Hardware und das Prozessmanagement. Nur 5 davon haben in den 3 Monaten seit dem letzten Booten mehr als 1 Sekunde Rechenzeit benötigt.
Dies alleine bringt keine dramatische Klangverbesserung - aber es ist immer gut, potenzielle Störquellen ganz auszuschalten.
(2) Man kann unter Linux leicht wichtigen Prozessen eine hohe Priorität im "Realtime Scheduler" geben (Kommando 'chrt'). Das ist gut für das eigentliche Abspielprogramm, das die Daten zu DAC/Soundkarte oder ins Netzwerk schickt.
(3) Wenn in Linux Daten durch mehrere Programme geschickt werden (Pipes) oder Daten vom Netzwerk oder aus einer Datei gelesen werden, dann legt das Betriebssytem Datenpuffer an, verwaltet diese und sorgt dafür, dass Prozesse angehalten werden, solange keine Daten vorliegen. Die Details sind hier nicht so leicht zu verstehen und zu beeinflussen. Man kann aber mit Buffergrößen experimentieren, die sich mit dem Programm 'stdbuf' beeinflussen lassen. Zum Beispiel kann es (kleine) klangliche Vorteile geben, wenn die Programme, die Daten ins Netzwerk schreiben oder von dort lesen, einen größeren Buffer bekommen.
(4) Ich habe etwas mit dem Abspielen aus dem Arbeitsspeicher (RAM) experimentiert. Zum Beispiel auf dem Audio-Rechner direkt eine im RAM gespeicherte Datei spielen statt einen Stream vom Netzwerk. Oder vorgefilterte Daten auf dem Zuspielrechner aus dem RAM statt von Festplatte lesen. In beiden Fällen konnte ich (allerdings eher geringfügige) Verbesserungen feststellen. Meine genannten Rechner haben aber nur 256 MB RAM, so dass Abspielen aus dem RAM nur für kurze Teststücke praktikabel ist.
Für eine praktikable Variante habe ich zwei kleine (C-)Programme geschrieben, die einen Datenstrom zyklisch in mehrere Dateien schreiben, beziehungsweise diese Dateien lesen und wieder als Datenstrom ausgeben. Wenn ich dies für die Datenpufferung im RAM verwende und die Dateien nicht zu klein wähle (etwa ein paar MB), dann ist diese Variante genauso gut, wie das Abspielen einer vorher in den RAM kopierten Datei (auf dem Audio-Rechner und auch auf dem Zuspielrechner bevor der Datenstrom ins Netzwerk geschrieben wird).
Eine deutliche Verbesserung ergibt sich, wenn die Hilfsdateien für die Datenpufferung so klein gewählt werden, dass sie in den Prozessorcache passen (etwa nur ein paar kB). (256 MB sind also unnötig viel Arbeitsspeicher!)
(5) Mit den in (1)-(4) genannten Ideen konnte ich bereits eine gute Verbesserung der Wiedergabe erreichen. Der wirklich große Schritt vorwärts wurde dann durch folgende Idee erreicht: Man kann den Linux Kernel mit "High Resolution Timer" kompilieren, was ich für meine Rechner getan habe. Die GNU C-library hat "realtime"-Funktionalität, die durch Funktionen 'clock_getres', 'clock_nanosleep' und ein paar mehr verfügbar ist. Hiermit kann man einen Prozess bis zu einem vorgegebenen Zeitpunkt anhalten (mit 'clock_nanosleep' im Modus 'TIMER_ABSTIME', siehe UNIX man-pages für weitere Details).
Dies benutze ich nun, um Datenströme mit sehr genauem Timing zu erzeugen. Ich habe einige kleine (C-)Programme geschrieben, die etwa so funktionieren: in einer Schleife (die zum Beispiel 2000 Mal pro Sekunde durchlaufen wird) werden Datenblöcke (aus einer Datei, von einer Pipe oder aus dem Netzwerk) gelesen und in einen Puffer geschrieben, die als nächstes herauszuschreibenden Daten werden vorbereitet, dann wird der Prozess bis zu einem vorgegebenen Zeitpunkt schlafen gelegt, und nach dem Aufwachen wird als allererstes ein Datenblock herausgeschrieben.
Zum Beispiel läuft jetzt auf meinem Audio-Rechner ein solches Programm. Es füllt einen Puffer, indem es Daten vom Netzwerk liest, legt sich schlafen und schreibt nach dem Aufwachen einen Datenblock direkt in den Puffer des USB-Audio Treibers, usw. Das ist so präzise, dass ich den Puffer des Treibers sehr klein wählen kann (8kB) und die Daten im non-blocking Modus dort hineinschreiben kann, ohne dass es in 80 Minuten (Daten im 192/32 Format, mehr als 7GB) zu einen Überlauf oder Unterlauf dieses Puffers kommt. Auch hat der Interrupt-Prozess des Kernels nichts mehr zu tun, weil ich auch auf dem Zuspielrechner mit einem kleinen Programm dafür sorge, dass der Audio-Rechner nur genau so viel Daten über das Netzwerk bekommt, wie er braucht; auf diese Weise werden die Puffer für die Netzwerkverbindung nie voll (oder leer).
(6) ... ich habe noch eine Reihe von Ideen, alle paar Wochen probiere ich mal was Neues und wenn ich meine, dass eine Änderung den Klang verbessert hat, teste ich das wieder für ein paar Wochen. Im Moment habe ich einen Raspberry Pi als Netzwerk-Zwischenpuffer zwischen meinem Zuspiel- und dem Audiorechner laufen; da ist fast kein Unterschied mehr zwischen Daten on-the-fly filtern und dem Abspielen von vorgefilterten Daten.
----
Klangbeschreibungen lasse ich jetzt mal. Ich könnte einige der begeisterten Beschreibungen aus den Windows-Optimierung Threads in diesem Forum hierher kopieren. Ich habe selbst nie ein optimiertes Windows Setup gehört, kann also nichts über einen Vergleich sagen (der sicher interessant wäre).
Falls jemand hierdurch angeregt wurde, selbst in dieser Richtung zu experimentieren, kann ich gerne weitere Details erklären. Umgekehrt sind natürlich auch Tipps für weitere Verbesserungen willkommen.
Viele Grüße,
Frank
PS: Gar nicht geredet habe ich hier über die Filter, durch die meine Musikdaten vor dem Abspielen geschickt werden, das ist eine andere Geschichte.