Autor |
Beitrag |
Martok
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Fr 13.01.12 19:14
Hallo,
dann frag ich doch mal
Also: ein Progamm benutzt waveIn und waveOut. Dazu braucht man bekannterweise einen gewissen Buffer (hier: Ringpuffer), damit keine Aussetzer auftreten.
Bei meinen Testsystemen sind das unabhängig von der Samplingrate 42ms bei der Ausgabe und 23ms bei der Aufnahme. Das ist Zufall und natürlich Hardware- und Treiberabhängig.
Also: wie komm ich an das Minimum? Probiert hab ich von magic numbers, Statistik über Regler und Timing schon alles, eher durchwachsener Erfolg.
Wie könnte man das noch lösen? Mir gehen langsam die Ideen aus, und ich will eigentlich nicht was großes nehmen und dafür mit Latenz bezahlen.
Alternativ: wie bestimme ich, ob ein Buffer Underrun aufgetreten ist? Dnn könnte der sich auch selber kalibrieren.
Danke für alle Ideen,
Martok
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
delfiphan
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Fr 13.01.12 22:12
Martok hat folgendes geschrieben : | Hallo,
dann frag ich doch mal |
?
Wie auch immer: Wieso musst du so dicht am Limit sein? Du kannst nur schwer im Voraus wissen, was der User mit dem System macht. Möglicherweise trinkt er 10 Minuten Kaffee und plötzlich öffnet er ein CPU-Intensives Programm. Wie willst du das im Voraus kalibrieren?
Im Notfall kannst du die Datendurchsatzrate messen - wenn ein Buffer verloren geht, sollte dieser zu tief sein. Wie man das direkt rausfindet, weiss ich leider nicht.
|
|
Martok
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Fr 13.01.12 22:59
delfiphan hat folgendes geschrieben : | Martok hat folgendes geschrieben : | dann frag ich doch mal |
? |
An dem Problem arbeite ich (mit monatelangen Lücken) seit ein paar Jahren und seit dieser Woche zusammen mit FinnO. Und jetzt bin ich mit meinem Latein am Ende
delfiphan hat folgendes geschrieben : | Du kannst nur schwer im Voraus wissen, was der User mit dem System macht. Möglicherweise trinkt er 10 Minuten Kaffee und plötzlich öffnet er ein CPU-Intensives Programm. Wie willst du das im Voraus kalibrieren? |
Indem ich in die Readme schreibe, dass er das unterlassen soll. Und indem ich REALTIME_PRIORITY_CLASS verwende
delfiphan hat folgendes geschrieben : | Wie auch immer: Wieso musst du so dicht am Limit sein? |
Weil ich Latenzen minimieren muss. Und ja, ich weiß was Kernel Streaming ist, aber dazu gibt's nicht mal eine Header-Übersetzung und mir reichen 65ms (also 23+42) aus. Wenn ich sie denn bekomme
delfiphan hat folgendes geschrieben : | Im Notfall kannst du die Datendurchsatzrate messen - wenn ein Buffer verloren geht, sollte dieser zu tief sein. Wie man das direkt rausfindet, weiss ich leider nicht. |
Ja, das Gleiche misst man auch direkt am Timing (Buffergröße/Rundenzeit=Durchsatzrate)- nur: das schwankt extrem. Wenn man für 42ms Buffer hat (101x20 Samples bei 48kS) schwankt die "Durchlaufzeit" einmal durch den Ring zwischen 40 und 43ms, und das ohne jeden Aussetzer. Und wenn dann mal ein Aussetzer kommt, liegt der nicht außerhalb dieses bereichs, und es gab auch keinen Trend vorher.
Was ich vergessen hab zu erwähnen: ich habe einen separaten Thread, der sich nur um das Device kümmert. Also keine Callbacks, einfach nur eine Endlosschleife, die guckt ob Platz ist und sofort Daten abliefert.
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
delfiphan
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Sa 14.01.12 14:16
Kann dir wohl nicht helfen. Allenfalls ein Echtzeitsystem verwenden oder benutzerkonfigurierbar machen.
|
|
TomyN
Beiträge: 32
Erhaltene Danke: 2
Win10
D5 Std., Turbo-Delphi (w32), Delphi 2010
|
Verfasst: Di 17.01.12 10:19
Hi,
- warum nimmst du nicht ASIO?
- Wie viele Buffer hast du?
Verstehe ich das richtig, dass du einen Thread in RealTime laufen hast, der nur 'pollt' (in welchen Abständen?) und du hast bei 42ms Buffersize Aussetzer bei der Ausgabe?
Grüße
Tomy
|
|
Martok
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Di 17.01.12 16:21
Hallo,
TomyN hat folgendes geschrieben : | - warum nimmst du nicht ASIO? |
Im Grunde einfach nur weil die waveOut-API einfacher zu bedienen ist. ASIO ist, selbst mit den Komponenten dazu die ich sogar schonmal getestet hab, irgendwie seltsam.
TomyN hat folgendes geschrieben : | - Wie viele Buffer hast du? |
Das ist genau die Frage
Mit etwas experimentieren hab ich festgestellt, dass es bei gleicher Gesamtzeit stabiler ist, viele kleine als wenige große Buffer im Ring-Satz zu haben.
Aktuell sind das N * 20 Samples. N ist genau die gesuchte Größe.
TomyN hat folgendes geschrieben : | Verstehe ich das richtig, dass du einen Thread in RealTime laufen hast, der nur 'pollt' (in welchen Abständen?) und du hast bei 42ms Buffersize Aussetzer bei der Ausgabe? |
Im Grunde ja.
Pseudocode 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| InitBuffers; while true do begin Daten:= ReadFromPipe; Samples:= ConvertToFormat(Daten, OutputFormat);
while (not Buffer.Fertig) do Sleep(1); SchreibSamplesInBuffer; waveOutWrite(Buffer); Buffer:= Ringpuffer.Next; end; |
Realtime brauchts gar nicht zwingend, HIGH_PRIORITY_CLASS und THREAD_PRIORITY_HIGHEST reicht schon aus. Das dürfte IIRC eine BasePriority=15 ergeben, genauso wie auch die "andere Seite", also der Thread in wdmaud der das dann ausgibt (sagt der Process Explorer).
Dabei spielt es keine Rolle, ob ich ein CALLBACK_EVENT verwende und statt [1] zu nehme auf dieses WaitForSingleObject-e. Die Performance bzw. das Verhalten ist identisch.
Das funktioniert sehr flüssig, solange BUFFER_NUM * BUFFER_SAMPLES / SampleRate größer als 42ms ist. Drunter gibt's die typischen Buffer-Underrun-Aussetzer.
Bei 48kS sind das gut 2048 Samples insgesamt (103 * 20), aber die 42ms sind unabhängig von der Samplerate, gelten also sowohl für 48kS als auch z.B. für 22kS. Aber abhängig von der Hardware.
Für Recording hat sich das übrigens erledigt, wie ich festgestellt habe ist da die Latenz immer gleich, egal wie viel gebuffert wird (weil nämlich nur so viele Buffer gefüllt werden wie Daten kommen). Man kann also einfach "etwas großes" nehmen und nur sicherstellen dass keine Underruns auftreten.
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
TomyN
Beiträge: 32
Erhaltene Danke: 2
Win10
D5 Std., Turbo-Delphi (w32), Delphi 2010
|
Verfasst: Di 17.01.12 16:35
Hi,
ASIO ist eigentlich genauso schwierig, hat halt eine andere 'Denkweise'.
Ich bin auch immer auf der Suche nach gutem I/O Verhalten, da ich eine Audio-Messsoftware programmiere ( www.take-sat.de).
Im Moment bin ich eigentlich mit ASIO sehr zufrieden, das einzige Problem ist, dass viele ASIO Treiber sich nicht ganz genau an die Spezifikation halten.
Ich bin gerade dabei, die ersten vorsichtigen Schritte in Richtung DirectKS Audio zu machen, dabei ist mir ein Dokument in die Hände gefallen, in dem microsoft sagt, dass man durch den KsMixer bei der Audioausgabe mit zusätzlich ca. 30 ms rechnen muss. Daher auch der Unterschied in der nötigen Puffergröße für die Aufnahme und die Wiedergabe.
Tomy
|
|
Martok
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Di 17.01.12 16:55
Ja, das mit dem Mixer kann sein. Gelesen hatte ich das auch, aber an zwei verschiedenen Stellen die sich exakt widersprochen haben
Die Frage ist immer noch: wie kriege ich raus ob ein Underrun aufgetreten ist... es gibt kein Event, kein Callback, kein Flag, nix. Momentan hab ich das wirklich über Device Config Files gemacht, in denen der User manuell Werte einträgt, testet und irgendwann das passende hat. Aber schön ist was anderes...
Den Beispielcode in C++ im Abschnit "Kernel Streaming" kennst du dann ja vermutlich schon?
Audio auf Windows ist wirklich ein Krampf.
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
TomyN
Beiträge: 32
Erhaltene Danke: 2
Win10
D5 Std., Turbo-Delphi (w32), Delphi 2010
|
Verfasst: Di 17.01.12 17:06
Hi,
da bin ich grad dabei mich mit zu beschäftigen. Bei mir ist der Leidensdruck nicht so groß grad
Das Problem ist, dass es ja kein 'klassischer' Buffer_Under_Run ist, denn das passiert ja auch, wenn definitiv genug Puffer da sind (man z.B. 10 Puffer zur Verfügung stellt).
Das Audiosystem von Windows ist für den normalen Desktopeinsatz gedacht, da hat es viele Vorteile (Integrierter Mischer, d.h. Systemsounds und Sounds der Anwendung werden gleichzeitig wiedergeben, das Datenformat wird angepasst etc). Man muss sich erstmal 'um nix scheissen', solange man mit großen Puffern arbeitet.
Wenn es in Richtung 'realtime audio' geht, dann ist meiner Meinung nach im Moment ASIO der beste Weg, gerade weil ja mit ASIO4ALL ein Weg zur Verfügung steht, (fast) alle Soundkarten mit einem ASIO Treiber 'nachzurüsten'.
Tomy
Für diesen Beitrag haben gedankt: Martok
|
|
|