Forum: Mikrocontroller und Digitale Elektronik USB Audio Sync Endpoint Implementierung


von Markus G. (usound)


Lesenswert?

Hallo zusammen,

in meinem aktuellen Projekt beschäftige ich mich mit der Umsetzung einer 
USB Audio Schnittstelle. Die Audiodaten werden dann entweder über I²S 
oder TDM, je nach benötigter Kanalanzahl ausgegeben. Momentan alles nur 
im Rahmen von UAC1.

Implementiert ist die Schnittstelle auf einem Atmel SAMD21, da ich den 
schon von anderen Projekten kenne. Die USB-Schnittstelle hatte ich 
vorher noch nie verwendet, deshalb hatte ich auch Interesse mal etwas in 
diese Richtung umzusetzen.
Die Basisentwickung habe ich auf dem Xplained Pro Eval Board gemacht und 
nun auf eine eigene Platine migriert.
Die Einarbeitungszeit war zwar enorm, aber die USB Audio Schnittstelle 
funktioniert nun soweit! Treiberseitig ist der USB Treiber aus dem ASF 
und die Audioklasse ist vom AVR32 portiert.
Das Device enumeriert unter Windows sauber und wird problemlos als 
Audiodevice erkannt. Das Abspielen funktioniert ebenfalls (im momentanen 
Testaufbau in Kombination mit einem DSP, der die DA-Wandlung übernimmt).

Das Device läuft im asynchronen Modus, womit wir zum aktuellen Problem 
kommen: die Synchronisation des USB- und des I²S-Clocks.
Beim Abspielen kommt es nach ca. 2-3 Minuten zu starken Verzerrungen. 
Entsprechend läuft der Buffer durch die nicht vorhandene Synchronisation 
über/unter.
Eine Hardware-Synchronisierung der Clocks oder den synchronen Modus will 
ich aus qualitativen Gründen zunächst nicht als mögliche Lösung 
berücksichtigen.

Entsprechend befasse ich mich gerade mit dem Thema Feedback/Sync 
Endpoint. Der SAMD21 unterstützt dieses Feature.
Nun habe ich mich schon durch die Tiefen der USB-Spezifikation 
gearbeitet und die nötigen Infos zum Vorhaben gefiltert.
Den Descriptor für den Sync Endpoint habe ich soweit implementiert und 
dieser wird auch unter Windows erkannt (ausgelesen mit USBlyzer).
Was mir noch nicht so recht klar ist, wie der Datentransfer aus dem Sync 
Endpoint abläuft. Muss ich den Feedback-Wert nur im Endpoint ablegen und 
der Host "holt" diesen dann ab oder brauche ich eine entsprechende 
Senderoutine, die das Datum im eingestellten Zeitintervall (2^P Frames, 
mit P=1..9) übermittelt?
Gibt es dazu eine Doku in der USB Spec die ich bloß noch nicht gefunden 
habe?


Die Recherche im Netz allgemein nach dem Thema und nach vorhandenem Code 
gestaltet sich äußerst mühsam.
Hat hier noch jemand nützlichen Input oder hat selbst so etwas mal 
implementiert?

Bin für konstruktive Kommentare dankbar.

Beste Grüße,
Markus

von Thomas (Gast)


Lesenswert?

Schwieriges Kapitel
Welche Usbaudio spec nutzt du unter welchem OS.

Welcher Treiber wird geladen? Beachte USB Audio 2.0 wird unter Win erst 
seit kurzem unterstützt.

Thomas

von Markus G. (usound)


Lesenswert?

Das Ganze läuft unter USB 2.0. Der SAMD21 kann maximal nur Fullspeed, 
was aber fürs erste auf jeden Fall ausreichend ist. Wie gesagt, ich 
beschränke mich zunächst auf USB Audio 1 (UAC1).
Betriebssystem ist Windows 10. Welcher Treiber genau geladen wird, habe 
ich mir noch nie angeschaut. Werde ich morgen mal schauen, wenn ich 
wieder im Bastelkeller bin.

von Thomas (Gast)


Lesenswert?

Hier ein paar Dinge zum lesen
https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers

USB Audio 1.0 macht nichts mit den Sync Endpoints. Es bleibt dir nichts 
anders übrig als die Synchronisation mit dem SOF selbst zu machen.

Been there done that.

Thomas

von Thomas (Gast)


Lesenswert?

Noch ein paar Hinweise

USB fullspeed reicht für 8 Kanäle @16 Bit 48k. Die SR wird mit einem 
Classrequest eingestellt. Wenn du vorhast Aufnahme und Wiedergabe 
gleichzeitig mit untersch.SRs zu verwenden vergiss es oder baue HW SR 
Konverter ein. Ansonsten wird immer der WIN eigene SR Konverter aktiv.

Das Thema ist kompliziert, schlecht dokumentiert und WIN unterstützt nur 
wenige Teile der Audiospec.

Falls du vor hast irgendwann USB Audio 2 zu unterstützen bleibt bis W7 
nur ein Fremdtreiber. Bei Interesse kann ich mehr dazu sagen.

Thomas

von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> Hier ein paar Dinge zum lesen
> 
https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers
>
> USB Audio 1.0 macht nichts mit den Sync Endpoints. Es bleibt dir nichts
> anders übrig als die Synchronisation mit dem SOF selbst zu machen.
>
> Been there done that.
>
> Thomas

Danke für die Infos. Mein Vorhaben funktioniert mit UAC1 also schonmal 
nicht.
Ich habe derweil keine vernünftige Möglichkeit gefunden, den SOF im uC 
auch für die Ausgabeseite zu verwenden. Atmel hat das wohl auf den 
asynchronen Modus ausgelegt. Synchron ginge zwar vermutlich, aber nur 
mit Kunstgriffen.

> USB fullspeed reicht für 8 Kanäle @16 Bit 48k.
Ist bekannt, hatte ich schonmal getestet, funktionierte auch.

> Wenn du vorhast Aufnahme und Wiedergabe gleichzeitig mit untersch.SRs zu 
verwenden
Nein das wird nicht vorkommen. Wenn das System als Zuspieler für einen 
DSP ohne ASRC verwendet wird, muss die Samplerate sowieso fix sein. Fürs 
erste ist für mich ohnehin nur die Abspielseite von Belangen.

> Falls du vor hast irgendwann USB Audio 2 zu unterstützen bleibt bis W7
> nur ein Fremdtreiber. Bei Interesse kann ich mehr dazu sagen.
Naja, alles vor Win7 ist auch nicht relevant. Wenn es Unterschiede 
zwischen Win7 und 10 gibt, würde ich mich ansich auf Win10 
konzentrieren.

Ich hatte mal spaßeshalber den Treiber meines MiniDSP Streamers (USB 
Audio 2) für mein Device installiert und wenn ich mich recht entsinne 
funktionierte das sogar!
Spricht prinzipiell etwas dagegen, das Projekt gleich auf USB Audio 2 
auszulegen?
Vermutlich höhere Komplexität, allerdings ist die Lösung der 
Synchronisation mit einem Feedback Endpoint vermutlich die 
professionellste.

von Thomas (Gast)


Lesenswert?

Markus G. schrieb:
> Spricht prinzipiell etwas dagegen, das Projekt gleich auf USB Audio 2
> auszulegen?
> Vermutlich höhere Komplexität, allerdings ist die Lösung der
> Synchronisation mit einem Feedback Endpoint vermutlich die
> professionellste.

Um ganz ehrlich zu sein ich bin mir nicht so sicher ob USB Audio 2 für 
ein Fullspeed Device überhaupt geladen wird, das musst du wohl einfach 
ausprobieren. Wenn ja wäre es wohl die Lösung.
Viele Hersteller benutzen unter W7 den Audio Class Treiber von Thesycon. 
Mir sind allerdings nur Highspeed devices bekannt die den Treiber 
benutzen.

MS hat bei USB Audio 1 seit 98SE nicht mehr viel verändert. Mit Me wurde 
Midi supportet dafür waren Selektoren kaput. 98Se hing bei Midi W2000 
machte einen Bluescreen. Ich hätte damals für XP entwickelt. Mein Device 
geht aber immer noch auch unter W10. Ich kenne andere die seit W7 nicht 
mehr gehen.

Thomas

von Thomas (Gast)


Lesenswert?

Thomas schrieb:
> USB Audio 1.0 macht nichts mit den Sync Endpoints.

Ich muss mich da korrigieren.
USB Audio 1.0 macht unter XP nichts mit den Sync Endpoints.
Ich glaube es zwar nicht aber vielleicht ist das ja unter W10 anders. 
Hab das nie überprüft.

Thomas

von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> Thomas schrieb:
>> USB Audio 1.0 macht nichts mit den Sync Endpoints.
>
> Ich muss mich da korrigieren.
> USB Audio 1.0 macht unter XP nichts mit den Sync Endpoints.
> Ich glaube es zwar nicht aber vielleicht ist das ja unter W10 anders.
> Hab das nie überprüft.
Wenn das mit UAC1 möglich ist, wäre mir das natürlich fürs erste am 
liebsten.
Nach folgendem Dokument müsste es seitens der USB-Schnittstelle möglich 
sein (Kapitel 4.6.2):
https://www.usb.org/sites/default/files/audio10.pdf
Wie es seitens des Treibers aussieht konnte ich derweil nicht eindeutig 
feststellen.

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Markus G. schrieb:
> Nach folgendem Dokument müsste es seitens der USB-Schnittstelle möglich
> sein (Kapitel 4.6.2):
> https://www.usb.org/sites/default/files/audio10.pdf
> Wie es seitens des Treibers aussieht konnte ich derweil nicht eindeutig
> feststellen.

Ja spezifiziert ist das schon. Die Frage ist halt ob MS das in 
usbaudio.sys implementiert hat. Ich kann das zumindest für XP verneinen.
Ich würde an deiner Stelle einfach mal simple UAC2 Deskriptoren 
implementieren und schauen ob für dein Device dann usbaudio2.sys geladen 
wird

Thomas

von Hannes M. (hannes_m)


Lesenswert?

Hallo Markus,

Ich kann mich im Punkt der "guten Dokumentation" zum Thema 
Synchronisierung meinen Vorrednern nur anschließen. Ich arbeite selbst 
gerade an einem ähnlichen Projekt (allerdings mit ARM Prozessor) wie du 
und habe es auch versucht mich in die USB Dokumentation einzulesen.

Mein Projekt ist zwar auch noch nicht wirklich lauffähig, aber ich kann 
dir zumindest bestätigen, dass (soweit ich herausgefunden habe) ab Win 
XP die Sync Endpoints funktionieren sollen (in XP wohl nur rudimentär, 
ab Win7 aber halbwegs brauchbar). Auf meinem Evaluation Board habe ich 
das ganze auch schon lauffähig hinbekommen und muss es nun noch auf 
meine eigene Platine übertragen.

Die Synchronisation läuft so ab, dass du dem Host über deinen Sync 
Endpoint eine Sendefrequenz vorgibst. (bspw. Wenn du 48kHz Samplerate 
nutzt und der PC zu langsam liefert, musst du beim PC eine höhere 
Samplerate anfordern bspw 48.01kHz, so dass du auf deinem Gerät passend 
deine 48kHz bekommst).

Die Sync Daten bestehen dabei aus 3 Bytes im 10.10 bzw. 10.14 format - 
heißt die oberen 10bits stellen den integer Part dar und die unteren 14 
Bits den gleitkommapart. Aufgebaut sind sie nach dem Muster: 10^9 10^8 
10^7 ... 10^0 10^-1 ... 10^-14

Ich hoffe ich konnte dir damit zumindest teilweise etwas helfen.

Gruß
Hannes

von Markus G. (usound)


Lesenswert?

Hallo Hannes,
das ermuntert natürlich schon mal, dass es noch Leidensgenossen bei dem 
Thema gibt :)
Ich arbeite auch auf einem ARM (SAMD21 = Cortex-M0). Implementierst du 
auch auf einem Atmel uC?
Die Info, dass es bei dir läuft und dass die Sync Endpoints mit Win7 
funktionieren müssten, hilft mir auf jeden Fall. Gibt es dazu eine 
Quelle, bzw. ist das irgendwo dokumentiert?
Bezieht sich die Aussage auf UAC1 oder 2?

Wie generierst du denn deinen Referenzwert für die aktuelle 
Übertragungsrate des USB?
Ich hatte mir überlegt, dass einfach mit einem Timer zu machen, sprich 
ich zähle die CPU Ticks, die zwischen einem/oder mehreren SOFs vergangen 
sind und kann so die reale Übertragungsrate ausrechnen.

Gruß
Markus

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Ich mach mal ein Beispiel für 44.1 weil das nicht so genau in 1ms SOFs 
aufgeteilt werden kann.

Bei 16 Bit muss der EP 44.1 * 32 Bit in einer ms übertragen (2 Kanäle = 
stereo)
Dein Device wird also 9mal 44*32 Bit senden und 1 mal 45*32 Bit.
Der SOF wird als konstant angenommen 1ms die spec schreibt das vor.
Du musst also den Korrektur Wert aus den den tatsächlich 
bereitgestellten Daten berechnen. Weicht der Wert nach 10ms ab musst du 
die Samplerate entsprechend verstellen. Damit das nicht so sehr sehr 
bemerkbar wird würde ich den Korrektur Wert langsam äendern aber die 
Geschwindigkeit mit der das passiert hängt von der Polling Rate ab.

Thomas

von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> Ich mach mal ein Beispiel für 44.1 weil das nicht so genau in 1ms
> SOFs
> aufgeteilt werden kann.
>
> Bei 16 Bit muss der EP 44.1 * 32 Bit in einer ms übertragen (2 Kanäle =
> stereo)
> Dein Device wird also 9mal 44*32 Bit senden und 1 mal 45*32 Bit.
> Der SOF wird als konstant angenommen 1ms die spec schreibt das vor.
> Du musst also den Korrektur Wert aus den den tatsächlich
> bereitgestellten Daten berechnen. Weicht der Wert nach 10ms ab musst du
> die Samplerate entsprechend verstellen. Damit das nicht so sehr sehr
> bemerkbar wird würde ich den Korrektur Wert langsam äendern aber die
> Geschwindigkeit mit der das passiert hängt von der Polling Rate ab.
>
> Thomas

Okay, das macht die Vorgehensweise schon klarer!

Ich bin momentan dabei, eine Testimplementierung zu machen: Den 
Descriptor habe ich soweit angepasst, dass der Feedback Endpoint richtig 
erkannt wird (zumindest kein Descriptor error).
Nun würde ich gerne als Feedback konstant 48 kHz ausgeben, um die 
allgemeine Funktion zu testen.
Das führt mich wieder zu einer meiner Eingangsfragen: wie läuft die 
Datenübertragung aus dem Feedback Endpoint heraus ab?
Muss ich den Wert im eingestellten Intervall (z.B. 8 oder 16 ms) mit 
einer Routine übermitteln oder holt sich der Host die Daten selbst aus 
dem Endpoint?

von Hannes M. (hannes_m)


Lesenswert?

Hallo Markus,

meine Aussagen beziehen sich komplett auf UAC1.
Mein Projekt basiert auf einem ARM Coartex M4 (EVAL Board) bzw ARM 
Coartex M7 (eigene Platine) beide aber nicht von Atmel, sondern von ST.

Aktuell habe ich die Synchronisierung noch nicht auf die Ticks des uC 
bezogen sondern erst einmal eine kleine Funktion gebastelt, die durch 
DMA Interrupts aufgerufen wird, und dort dann den Offset der Schreib zum 
Lesezeiger im Ringpuffer berechnet und Somit erkennt, ob ich vom PC zu 
viele oder zu wenig Samples bekommen habe. Wenn dann der Schreibzeiger 
von seiner Sollposition (1/2 Puffergrößenversatz) abweicht, dann 
aktualisiere ich die Samplerate über den Feedback Endpoint (bei 48KHz in 
meinem Bsp. fordere ich bei einem zu kleinem Versatz dann 48.010KHz 
Samplerate über den Feedback Endpoint an, bei einem zu großem 
48.010KHz).

Die Methode ist zwar Audiotechnisch sicher nicht perfekt, aber es 
funktioniert vorerst einmal und hörbar ist die geringe abweichung auch 
nicht.

Im Idealfall würde man die I2S Masterclock mit einem Timer zählen und 
diese dann über einen oder mehrere USB SOF's verrechnen. Damit ließe 
sich dann der Tatsächliche Versatz von PC Samplefrequenz zu I2S 
Samplefrequenz berechnen und man könnte über den Sync Endpoint die 
Datenraten Synchronisieren.
Entspricht ja auch fast deinem Ansatz und wäre sicher auch die sauberere 
Methode :).

Quellen habe ich leider gerade keine für dich zur Hand, die meisten 
Infos, die ich zu dem Thema gefunden habe, habe ich in diversen 
Russischen Foren gefunden (Google Translate sei Dank :)).

Gruß
Hannes

von Thomas (Gast)


Lesenswert?

Du must dich von der Vorstellung verabschieden dass dein Device irgend 
etwas selbstständig macht dem ist nicht so. Dein Device regiert nur auf 
Anfrage des Hosts. Du stellst deinen Wert bereit und der Host fragt 
diesen Wert ab. Wenn das geschehen ist bekommst du vermutlich einen 
Interrupt. Dann kannst du den nächsten Wert bereitstellen. Falls der 
Host deinen Endpoints nicht abfragt ist das eben so und deine Clocks 
sind free running.

Thomas

von Hannes M. (hannes_m)


Lesenswert?

Wenn ich die Definitionen korrekt verstanden habe, dann stellst du im 
Descriptor deine Aktualisierungsrate ein und der PC erwartet dann in 
diesem Intervall die neue Samplerate. Jedoch ist es so, dass wenn keine 
Änderung stattgefunden hat, auch keine neue Samplerate gemeldet werden 
muss - jedenfalls funktioniert es bei mir auf diese Art und Weise :).

EDIT: Die Ausführung von Thomas ist natürlich sehr viel besser als meine 
(ich bin ja auch noch ein wenig laienhaft unterwegs auf dem Gebiet). 
Jedoch hat bei mir das ganze nicht per Interrupt durch USB funktioniert 
(vielleicht mache ich hier auch etwas falsch) - Bei meiner Lösung stelle 
ich jedenfalls die Daten nicht in dem nach dem Endpoint definiertem 
Zeitintervall bereit.

Gruß

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> Du must dich von der Vorstellung verabschieden dass dein Device irgend
> etwas selbstständig macht dem ist nicht so. Dein Device regiert nur auf
> Anfrage des Hosts. Du stellst deinen Wert bereit und der Host fragt
> diesen Wert ab.
Das ist mir soweit schon klar.
Ich hänge momentan etwas bei der Implementierung selbst.
Den Descriptor für den FB Endpoint habe ich soweit zusammengebaut und er 
enumeriert fehlerfrei.

Der vereinfachte, momentane Ablauf (nur USB Audio Transfer):
- der Host schickt Audio samples (die MaxPacketSize steht auf 288 Byte, 
da 2 Kanäle, 48 Khz, 24 Bit)
- der Interrupt im Endpoint schiebt die Daten in den Buffer des 
DMA-Moduls
- der Interrupt im DMA-Modul schiebt die Daten ins I²S-Modul/auf den 
Serializer

Die Konvertierung ins 10.10 bzw. 10.14 Format habe ich soweit 
implementiert und überprüft.
So weit, so gut. Nur wo schiebe ich jetzt die Aktion des FB EPs da 
zwischen rein?
Mir scheint folgende Vorgehensweise am sinnvollsten:
Wenn ich Daten vom USB empfangen habe (Interrupt), sende ich meinen 
Feedback Wert in den Endpoint buffer. Dann inkrementiere ich einen 
Zähler (also in jedem Frame ein Mal). Sobald der Zähler bei dem 
eingestellten Intervall ist, sende ich wieder einen Feedback Wert und 
setze den Zähler zurück.
Sprich ich mache die Feedback Aktion immer nach dem Empfangen der Audio 
samples. Ist das so gangbar?

Hannes M. schrieb:
> Wenn ich die Definitionen korrekt verstanden habe, dann stellst du im
> Descriptor deine Aktualisierungsrate ein und der PC erwartet dann in
> diesem Intervall die neue Samplerate. Jedoch ist es so, dass wenn keine
> Änderung stattgefunden hat, auch keine neue Samplerate gemeldet werden
> muss - jedenfalls funktioniert es bei mir auf diese Art und Weise :).
Genau so ist es (beides).
- Der FB EP ist ein isochroner IN Endpoint mit festem Intervall (siehe 
USB Spec Rev. 2.0, Kapitel 5.12.4.1, Seite 72, Table 5.12).
- Das Feedback wird nur gebraucht, wenn sich etwas geändert hat (am 
Anfang reicht also theoretisch zum Testen eine Übertragung von Ff): "The 
endpoint may choose to report Ff only if the updated value has changed 
from the previous Ff value." (USB Spec Rev. 2.0, Seite 75).

: Bearbeitet durch User
von Hannes M. (hannes_m)


Lesenswert?

Meiner Meinung nach müsste es theoretisch so funktionieren. Du könntest 
aber alternativ auch den USB SOF Interrupt für das zählen benutzen und 
dann die Daten bereitstellen. So wärst du etwas unabhängig von dem Audio 
EP selbst.

von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> Mir scheint folgende Vorgehensweise am sinnvollsten:
> Wenn ich Daten vom USB empfangen habe (Interrupt), sende ich meinen
> Feedback Wert in den Endpoint buffer. Dann inkrementiere ich einen
> Zähler (also in jedem Frame ein Mal). Sobald der Zähler bei dem
> eingestellten Intervall ist, sende ich wieder einen Feedback Wert und
> setze den Zähler zurück.
> Sprich ich mache die Feedback Aktion immer nach dem Empfangen der Audio
> samples. Ist das so gangbar?

Ja, aber aufwändiger als nötig.

Der Feedback-Wert ist idempotent, d.h., es macht überhaupt nichts, wenn 
der selbe Wert mehrmals an den Host übermittelt wird.

Wenn du einen neuen Feedback-Wert hast, schreibst du ihn in den 
Endpoint-Buffer.

Unabhängig davon, wenn dir der Interrupt mitteilt, dass der 
Endpoint-Buffer vom Host gelesen wurde, sendest du ihn erneut. (Der Host 
kontrolliert selbst, wann er das Paket wirklich abfragt; in der Firmware 
musst du dich um das Intervall nicht kümmern.)

von Thomas (Gast)


Lesenswert?

Markus G. schrieb:
> - der Host schickt Audio samples (die MaxPacketSize steht auf 288 Byte,
> da 2 Kanäle, 48 Khz, 24 Bit)

das ist zu wenig probier mal 304 Bytes. Ev ist das sogar dein 
Hauptproblem.

Ich versuchs noch mal mit anderen Worten (Nur playback 48k/24 Bit)

Der Host stellt x Bytes in den ISO EP. Die Anzahl ist indirekt die 
Samplerate.
Bei genau genau 48k bekommst du 288 Bytes es kann aber auch mehr (zu 
langsam) oder weniger (zu schnell) sein.
Du kommulierst die tatsächlich empangenen Bytes, rechnest das um und 
stellst den Wert in den Sync EP. Nachdem der Polling Intervall 
abgelaufen ist wird der Host den Wert abholen. Was und ob etwas damit 
gemacht wird hast du nicht im Zugriff das ist dann Sache von 
usbaudio.sys bzw usbaudio2.sys.
Natürlich muss für den Sync EP das Toggle Bit korrekt gestellt sein 
falls deine HW das nicht schon automatisch macht. Ich habe damals den 
TUSB3200 verwendet und einfach mit dem SOF den DMA Clock verstellt ganz 
ohne Sync.

Das sollte es schon gewesen sein, komplizierter wird es erst wenn 
unterschiedliche SRs in Spiel kommen.

Thomas

von Markus G. (usound)


Lesenswert?

Clemens L. schrieb:
> Der Feedback-Wert ist idempotent, d.h., es macht überhaupt nichts, wenn
> der selbe Wert mehrmals an den Host übermittelt wird.
>
> Wenn du einen neuen Feedback-Wert hast, schreibst du ihn in den
> Endpoint-Buffer.
Das ist schonmal eine interessante Info.

Thomas schrieb:
> Markus G. schrieb:
>> - der Host schickt Audio samples (die MaxPacketSize steht auf 288 Byte,
>> da 2 Kanäle, 48 Khz, 24 Bit)
>
> das ist zu wenig probier mal 304 Bytes. Ev ist das sogar dein
> Hauptproblem.
>
> Ich versuchs noch mal mit anderen Worten (Nur playback 48k/24 Bit)

Den Buffer habe ich jetzt vergrößert. Macht natürlich auch Sinn Platz 
für eventuell zu viel übermittelte Samples zu haben.

Die Vorgehensweise ist mir ansich mittlerweile durchaus klar.
Momentan scheitert es wirklich eher an der reinen Implementierung. 
Sprich wie und mit welchen Routinen muss ich den IN Endpoint mit Daten 
bedienen. Da muss ich jetzt wieder etwas tiefer ins System eindringen. 
Die USB Audio Geschichte lag zwischendrin einige Monate brach.

Meine Endpoints habe ich so konfiguriert (ausgelesen mit USBlyzer):

OUT Endpoint:
Endpoint Descriptor 02 2 Out, Isochronous, 1 ms
Offset Field Size Value Description
0 bLength 1 09h
1 bDescriptorType 1 05h Endpoint
2 bEndpointAddress 1 02h 2 Out
3 bmAttributes 1 05h Isochronous, Asynchronous, Data
 1..0: Transfer Type  ......01  Isochronous
 3..2: Sync Type  ....01..  Asynchronous
 5..4: Usage Type  ..00....  Data
 7..6: Reserved  00......
4 wMaxPacketSize 2 0120h 288 bytes
6 bInterval 1 01h 1 ms
7 bRefresh 1 00h
8 bSynchAddress 1 82h

IN Endpoint (gekürzt):
2 bEndpointAddress 1 82h 2 Out
3 bmAttributes 1 05h Isochronous, Asynchronous, No Sync, Feedback
4 wMaxPacketSize 2 0003h 3 bytes
6 bInterval 1 04h 8 ms

Das müsste denke ich laut Spec so passen. Es wird zumindest in Windows 
korrekt erkannt, dass der IN Endpoint mit seiner Adresse als Sync 
Endpoint zum OUT Endpoint 2 gehört und bekommt entsprechend die gleiche 
Nummer.
Jetzt ist mir allerdings etwas aufgefallen in der USB Application Note 
zum uC:
http://ww1.microchip.com/downloads/en/AppNotes/Atmel-42261-SAM-D21-USB_Application-Note_AT06475.pdf
Auf Seite 6 wird das Thema Feedback Endpoint thematisiert. Dort steht 
allerdings der Endpoint als Interrupt Typ. Allerdings kann ich mir nicht 
vorstellen, dass der Feedback Endpoint aus Sicht des Controllers nur als 
Interrupt Typ funktionieren würde, da isochrone Data IN Endpoints (also 
Audio-Aufnahmeseite) ja auch funktionieren.

Den Feedback Wert sende ich im 10.14 Format. Die drei Bytes übergebe ich 
als LSB first an den Endpoint buffer. Müsste so passen oder?

Beste Grüße
Markus

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Hannes M. schrieb:
> Mein Projekt ist zwar auch noch nicht wirklich lauffähig, aber ich kann
> dir zumindest bestätigen, dass (soweit ich herausgefunden habe) ab Win
> XP die Sync Endpoints funktionieren sollen (in XP wohl nur rudimentär,
> ab Win7 aber halbwegs brauchbar). Auf meinem Evaluation Board habe ich
> das ganze auch schon lauffähig hinbekommen und muss es nun noch auf
> meine eigene Platine übertragen.

Hallo Hannes,

melde mich nochmals. Habe nun meine Routine, um den FB Endpoint zu 
bedienen implementiert. Die Funktion habe ich als Callback an das 
SOF-Event geheftet, im eingestellten Polling Intervall im Descriptor 
sende ich dann die FB Daten in den Endpoint. Ich toggle zusätzlich einen 
GPIO, mit dem Oszi kann ich dann nachvollziehen, ob das so läuft. Die 
Funktion ansich funktioniert, die toggles kommen im richtigen Intervall.
Der Controller hängt aber in der Routine fest, sobald ich Audio 
abspielen will.

Ich habe nur den MiniDSP Streamer als Vergleichsdevice für die 
Descriptors, dieser nutzt aber UAC2. Soweit ich weiß, sind die 
Deskriptoren von UAC1 zu UAC2 nicht übertragbar?!
Entsprechend würde ich gerne erst kontrollieren, ob die Deskriptoren für 
UAC1 passen.
Könntest du mal deine Konfiguration der Deskriptoren posten?

Beste Grüße
Markus

von Thomas (Gast)


Lesenswert?

Markus G. schrieb:
> . Die Funktion habe ich als Callback an das SOF-Event geheftet,

OK also 1mal pro ms wird deine Berechnung angeworfen....
Den startwert hast du beim Set SampleRate request eingestellt(deine 
288Bytes)?
Du stellst bei jedem SOF einen neuen passendenden Wert ein sobald dein 
alternate Setting eingestellt ist.
Das Schema in Samples ist etwa (alter Wert + neuer Wert ) <<1. Das 
Ergebnis musst du dann noch in das 10.14 Format konvertieren und in den 
Sync EP Puffer schreiben. Die Polling Rate interessiert dich nicht wenn 
der Host will wird er den EP abfragen und die richtige SR bekommen.

Nochmal
Solange dein alternate Setting auf 0 steht machst du gar nichts
Wenn den alternate auf fs48/24 gestellt wird wartet du auf den SR 
request
Im SR request gibst du deinen Startwert vor 288 Bytes.
Ab diesem Zeitpunkt in jedem SOF den Wert berechnen und Bereitstellen.

In keinem Fall sendest du was an den Host der holt es ab wenn er es 
braucht.

Thomas

von Thomas (Gast)


Lesenswert?

Vergessen zu sagen: Es ist sinnvoll anstelle des Orignialwerts das 
256fache für die Berechnung zu verwenden damit sich Rundungsfehler nicht 
so schnell im Ergebniss niederschlagen. Rechenleistung dürfte ja weniger 
ein Problem sein.
Als Buffersize würde ich 296 oder 302 Vorschlägen. Das reicht dann für 
49k bzw 50k.

Thomas

von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> Den startwert hast du beim Set SampleRate request eingestellt(deine
> 288Bytes)?
Gute Frage :)
Also das ganze Projekt ist im Prinzip aus drei Quellen zusammengenagelt:
USB Treiber aus dem ASF für den SAMD21, USB Audio Treiber vom SAM3X und 
USB Audio Class 1 vom AVR32. Das ließ sich alles noch "relativ" gut ohne 
größere Reibungsverluste adaptieren, weil der Code großteils portierbar 
ist.

> Du stellst bei jedem SOF einen neuen passenden Wert ein sobald dein
> alternate Setting eingestellt ist.

Momentan will ich noch nicht korrigieren. Ich will den Status Quo von 
"Audio funktioniert - ohne sync" zu "Audio funktioniert - ohne sync - 
mit Feedback Endpoint" bringen.
Sprich ein Testsetup, um sicherzustellen, dass der FB EP funktioniert.
Dazu will ich erstmal fix 48 kHz anfordern. Dazu habe ich meine Funktion 
an den SOF Interrupt gehängt und habe dort einen Zähler, der mir 
anzeigt, wann das polling interval rum ist und ich einen neuen Wert 
liefern muss. Das kann ich jetzt natürlich rausnehmen und den Wert in 
jedem Frame in den Buffer des FB EP schreiben.
Wie man sieht, das ist mein erstes Projekt mit USB und entsprechend 
fische ich noch bei Vielem im Trüben.

> Nochmal
> Solange dein alternate Setting auf 0 steht machst du gar nichts
> Wenn den alternate auf fs48/24 gestellt wird wartet du auf den SR
> request
> Im SR request gibst du deinen Startwert vor 288 Bytes.
> Ab diesem Zeitpunkt in jedem SOF den Wert berechnen und Bereitstellen.
Wo sehe ich das alternate Setting, bzw. was macht das Ding?

Gruß
Markus

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Markus G. schrieb:
> Wo sehe ich das alternate Setting, bzw. was macht das Ding?

Ok dann fehlen noch ein paar Grundlagen ....
Das Alternate Setting wird mit std request SetInterface vorgegeben
Solange keine Streaming pipe offen ist steht das auf 0. Für jede 
Samplerate die du unterstützt gibt es ein alternate Setting. Das 
alternate kommt dabei auf wValueLo.
Du bekommst also jedes mal wenn eine App was abspielen will den 
entsprechenden SetInterface request.
Die Alternate Setting sind Teil der Deskriptoren.

Vielleicht solltest du mal die Ausgaben von UsbView hier einstellen 
damit wir sehen können welche Deskriptoren du hast. Ich bin bisher davon 
ausgegangen dass du USB grunzätzlich klar ist und du nur der sync das 
Problem ist.

Thomas

von Thomas (Gast)


Angehängte Dateien:

Lesenswert?

im Anhang mal ein Satz Deskriptoren alle UAC1 allerdings ohne Sync EPs
Das ganze ist Teil eines MultiConfig Devices das heist ein Treiber (in 
diesem Fall ein Asio Treiber wählt genau eine Config.
Deshalb ist der Deskriptor Satz 4 mal da.

Vieleicht hilft das ja zum Verständniss. Ich bin gerne bereit Fragen zu 
den Deskriptoren zu beantworten werde aber nichts zum Device oder 
Treiber sagen. Einige hier werden sowieso wissen, um was für ein Device 
es sich handelt

Thomas

von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> Markus G. schrieb:
>> Wo sehe ich das alternate Setting, bzw. was macht das Ding?
>
> Ok dann fehlen noch ein paar Grundlagen ....
> Das Alternate Setting wird mit std request SetInterface vorgegeben
> Solange keine Streaming pipe offen ist steht das auf 0. Für jede
> Samplerate die du unterstützt gibt es ein alternate Setting. Das
> alternate kommt dabei auf wValueLo.
> Du bekommst also jedes mal wenn eine App was abspielen will den
> entsprechenden SetInterface request.
> Die Alternate Setting sind Teil der Deskriptoren.
Okay das kann ich soweit nachvollziehen. Da ich am Ausgang unter anderem 
einen DSP ohne ASRC anschließen will, muss die sample rate ohnehin immer 
fix auf 48 kHz liegen. Entsprechend habe ich auch im Deskriptor nur 48 
kHz bei 24 Bit zugelassen. Alles andere Material, das evtl. kommen 
könnte, muss dann seitens Windows erst durch den sample rate converter.

> Vielleicht solltest du mal die Ausgaben von UsbView hier einstellen
> damit wir sehen können welche Deskriptoren du hast. Ich bin bisher davon
> ausgegangen dass du USB grundsätzlich klar ist und du nur der sync das
> Problem ist.
Den Sync ansich habe ich soweit durchdrungen, es scheitert grade an der 
Umsetzung. Bei den Deskriptoren bin ich mir auch nicht sicher. Hier die 
Ausgabe von USBView (kann man hier eigentlich sinnvoll Code z.B. in 
einem Spoiler posten?):

[Port1]  :  USB-Verbundgerät

Is Port User Connectable:         yes
Is Port Debug Capable:            no
Companion Port Number:            11
Companion Hub Symbolic Link Name: 
USB#ROOT_HUB30#4&34571c9a&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
Protocols Supported:
 USB 1.1:                         yes
 USB 2.0:                         yes
 USB 3.0:                         no

Device Power State:               PowerDeviceD0

       ---===>Device Information<===---
English product name: "USB-I2S-Interface"

ConnectionStatus:
Current Config Value:              0x01  -> Device Bus Speed: Full (is 
not SuperSpeed or higher capable)
Device Address:                    0x16
Open Pipes:                           0
*!*ERROR:  No open pipes!

          ===>Device Descriptor<===
bLength:                           0x12
bDescriptorType:                   0x01
bcdUSB:                          0x0200
bDeviceClass:                      0x00  -> This is an Interface Class 
Defined Device
bDeviceSubClass:                   0x00
bDeviceProtocol:                   0x00
bMaxPacketSize0:                   0x40 = (64) Bytes
idVendor:                        0x03EB = Atmel Corporation
idProduct:                       0x2423
bcdDevice:                       0x0100
iManufacturer:                     0x01
     English (United States)  "Test_Manufacturer"
iProduct:                          0x02
     English (United States)  "USB-I2S-Interface"
iSerialNumber:                     0x03
     English (United States)  "v0.0.0.0.1"
bNumConfigurations:                0x01

       ---===>Full Configuration Descriptor<===---

          ===>Configuration Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x02
wTotalLength:                    0x006D
*!*ERROR: Invalid total configuration size 0x6D, should be 0x6B
bNumInterfaces:                    0x02
bConfigurationValue:               0x01
iConfiguration:                    0x00
bmAttributes:                      0x80  -> Bus Powered
MaxPower:                          0x32 = 100 mA

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x00
bAlternateSetting:                 0x00
bNumEndpoints:                     0x00
bInterfaceClass:                   0x01  -> Audio Interface Class
bInterfaceSubClass:                0x01  -> Audio Control Interface 
SubClass
bInterfaceProtocol:                0x00
iInterface:                        0x00

          ===>Audio Control Interface Header Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x01 (HEADER)
bcdADC:                          0x0100
wTotalLength:                    0x001E
bInCollection:                     0x01
baInterfaceNr[1]:                  0x01

          ===>Audio Control Input Terminal Descriptor<===
bLength:                           0x0C
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x02 (INPUT_TERMINAL)
bTerminalID:                       0x01
wTerminalType:                   0x0101 (USB streaming)
bAssocTerminal:                    0x00
bNrChannels:                       0x02
wChannelConfig:                  0x0003
                                 (Left Front (L))
                                 (Right Ront (R))
iChannelNames:                     0x00
iTerminal:                         0x00

          ===>Audio Control Output Terminal Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x03 (OUTPUT_TERMINAL)
bTerminalID:                       0x02
wTerminalType:                   0x0301 (Speaker)
bAssocTerminal:                    0x00
bSourceID:                         0x01
iTerminal:                         0x00

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x01
bAlternateSetting:                 0x00
bNumEndpoints:                     0x00
bInterfaceClass:                   0x01  -> Audio Interface Class
bInterfaceSubClass:                0x02  -> Audio Streaming Interface 
SubClass
bInterfaceProtocol:                0x00
iInterface:                        0x00

          ===>Interface Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x04
bInterfaceNumber:                  0x01
bAlternateSetting:                 0x01
bNumEndpoints:                     0x02
bInterfaceClass:                   0x01  -> Audio Interface Class
bInterfaceSubClass:                0x02  -> Audio Streaming Interface 
SubClass
bInterfaceProtocol:                0x00
iInterface:                        0x00

          ===>Audio Streaming Class Specific Interface Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x01 (AS_GENERAL)
bTerminalLink:                     0x01
bDelay:                            0x01
wFormatTag:                      0x0001 (PCM)

          ===>Audio Streaming Format Type Descriptor<===
bLength:                           0x0B
bDescriptorType:                   0x24 (CS_INTERFACE)
bDescriptorSubtype:                0x02 (FORMAT_TYPE)
bFormatType:                       0x01 (FORMAT_TYPE_I)
bNrChannels:                       0x02
bSubframeSize:                     0x03
bBitResolution:                    0x18 (24)
bSamFreqType:                      0x01 (Discrete)
tSamFreq[1]:                   0x00BB80 (48000 Hz)

          ===>Endpoint Descriptor<===
bLength:                           0x09
bDescriptorType:                   0x05
bEndpointAddress:                  0x02  -> Direction: OUT - EndpointID: 
2
bmAttributes:                      0x05  -> Isochronous Transfer Type, 
Synchronization Type = Asynchronous, Usage Type = Data Endpoint
wMaxPacketSize:                  0x0130 = 0x130 bytes
wInterval:                       0x0001
bSyncAddress:                      0x82

          ===>Audio Streaming Class Specific Audio Data Endpoint 
Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x25 (CS_ENDPOINT)
bDescriptorSubtype:                0x01 (EP_GENERAL)
bmAttributes:                      0x80
                                   (MaxPacketsOnly)
bLockDelayUnits:                   0x00 (Undefined)
wLockDelay:                      0x0000

          ===>Endpoint Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x82  -> Direction: IN - EndpointID: 
2
bmAttributes:                      0x11  -> Isochronous Transfer Type, 
Synchronization Type = No Synchronization, Usage Type = Feedback 
Endpoint
wMaxPacketSize:                  0x0003 = 0x03 bytes
bInterval:                         0x04

          ===>Descriptor Hex Dump<===
bLength:                           0x00
bDescriptorType:                   0x00

von Thomas (Gast)


Lesenswert?

Deine Deskriptoren sehen erst mal gut aus (2 Bytes zu lang aber das ist 
sicher nicht das Problem. Der As EP desc für den Strem EP sieht 
allerdings etwas seltsam aus. Meine sind da anders.

Thomas

von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> Deine Deskriptoren sehen erst mal gut aus (2 Bytes zu lang aber
> das ist
> sicher nicht das Problem. Der As EP desc für den Strem EP sieht
> allerdings etwas seltsam aus. Meine sind da anders.
>
> Thomas

Du meinst den vorletzten Endpoint?
Sprich:

===>Audio Streaming Class Specific Audio Data Endpoint
Descriptor<===
bLength:                           0x07
bDescriptorType:                   0x25 (CS_ENDPOINT)
bDescriptorSubtype:                0x01 (EP_GENERAL)
bmAttributes:                      0x80
                                   (MaxPacketsOnly)
bLockDelayUnits:                   0x00 (Undefined)
wLockDelay:                      0x0000

Ich tue mich ehrlich gesagt etwas schwer, bei dir das entsprechende 
Pendant zu finden.

Gruß
Markus

von Thomas (Gast)


Lesenswert?

Such einfach nach AS_GENERAL
bmAttribute steht bei mir auf 1 SR Request
bLockDelayUnits auf 1 ms
wLockdelay auf 0x01, 0x00

Das sind Unterschiede die mir aufgefallen sind. Attribute 0x80 kenn ich 
nicht was soll das sein? Mit 0x01 bekommst du auf jedenfall den SR 
Request
Thomas

von Hannes M. (hannes_m)


Lesenswert?

Hallo Markus,

ich sehe das genauso wie Thomas -> Attribute 0x80 finde ich auch seltsam

hier mein funktionierender Descriptor von meinem EVAL Board:

    ---------------------- Device Descriptor ----------------------
bLength                  : 0x12 (18 bytes)
bDescriptorType          : 0x01 (Device Descriptor)
bcdUSB                   : 0x200 (USB Version 2.00)
bDeviceClass             : 0x00 (defined by the interface descriptors)
bDeviceSubClass          : 0x00
bDeviceProtocol          : 0x00
bMaxPacketSize0          : 0x40 (64 bytes)
idVendor                 : 0x0483 (STMicroelectronics)
idProduct                : 0x5741
bcdDevice                : 0x0200
iManufacturer            : 0x01 (String Descriptor 1)
 Language 0x0409         : "STMicroelectronics"
iProduct                 : 0x02 (String Descriptor 2)
 Language 0x0409         : "STM32 Audio Class"
iSerialNumber            : 0x03 (String Descriptor 3)
 Language 0x0409         : "00000000001A"
bNumConfigurations       : 0x01 (1 Configuration)
Data (HexDump)           : 12 01 00 02 00 00 00 40 83 04 41 57 00 02 01 
02   .......@..AW....
                           03 01 
..

    ------------------ Configuration Descriptor -------------------
bLength                  : 0x09 (9 bytes)
bDescriptorType          : 0x02 (Configuration Descriptor)
wTotalLength             : 0x0076 (118 bytes)
bNumInterfaces           : 0x02 (2 Interfaces)
bConfigurationValue      : 0x01 (Configuration 1)
iConfiguration           : 0x00 (No String Descriptor)
bmAttributes             : 0xC0
 D7: Reserved, set 1     : 0x01
 D6: Self Powered        : 0x01 (yes)
 D5: Remote Wakeup       : 0x00 (no)
 D4..0: Reserved, set 0  : 0x00
MaxPower                 : 0x32 (100 mA)
Data (HexDump)           : 09 02 76 00 02 01 00 C0 32 09 04 00 00 00 01 
01   ..v.....2.......
                           00 00 09 24 01 00 01 27 00 01 01 0C 24 02 01 
01   ...$...'....$...
                           01 00 02 03 00 00 00 09 24 06 02 01 01 01 00 
00   ........$.......
                           09 24 03 03 01 03 00 02 00 09 04 01 00 00 01 
02   .$..............
                           00 00 09 04 01 01 02 01 02 00 00 07 24 01 01 
01   ............$...
                           01 00 0B 24 02 01 02 02 10 01 80 BB 00 09 05 
01   ...$............
                           05 C4 00 01 00 81 07 25 01 00 00 00 00 09 05 
81   .......%........
                           11 03 00 01 02 00 
......

        ---------------- Interface Descriptor -----------------
bLength                  : 0x09 (9 bytes)
bDescriptorType          : 0x04 (Interface Descriptor)
bInterfaceNumber         : 0x00
bAlternateSetting        : 0x00
bNumEndpoints            : 0x00 (Default Control Pipe only)
bInterfaceClass          : 0x01 (Audio)
bInterfaceSubClass       : 0x01 (Audio Control)
bInterfaceProtocol       : 0x00
iInterface               : 0x00 (No String Descriptor)
Data (HexDump)           : 09 04 00 00 00 01 01 00 00 
.........

        ------ Audio Control Interface Header Descriptor ------
bLength                  : 0x09 (9 bytes)
bDescriptorType          : 0x24 (Audio Interface Descriptor)
bDescriptorSubtype       : 0x01 (Header)
bcdADC                   : 0x0100
wTotalLength             : 0x0027 (39 bytes)
bInCollection            : 0x01
baInterfaceNr[1]         : 0x01
Data (HexDump)           : 09 24 01 00 01 27 00 01 01 
.$...'...

        ------- Audio Control Input Terminal Descriptor -------
bLength                  : 0x0C (12 bytes)
bDescriptorType          : 0x24 (Audio Interface Descriptor)
bDescriptorSubtype       : 0x02 (Input Terminal)
bTerminalID              : 0x01
wTerminalType            : 0x0101 (USB streaming)
bAssocTerminal           : 0x00
bNrChannels              : 0x02 (2 channels)
wChannelConfig           : 0x0003 (L, R)
iChannelNames            : 0x00 (No String Descriptor)
iTerminal                : 0x00 (No String Descriptor)
Data (HexDump)           : 0C 24 02 01 01 01 00 02 03 00 00 00 
.$..........

        -------- Audio Control Feature Unit Descriptor --------
bLength                  : 0x09 (9 bytes)
bDescriptorType          : 0x24 (Audio Interface Descriptor)
bDescriptorSubtype       : 0x06 (Feature Unit)
bUnitID                  : 0x02 (2)
bSourceID                : 0x01 (1)
bControlSize             : 0x01 (1 byte per control)
bmaControls[0]           : 0x01
 D0: Mute                : 1
 D1: Volume              : 0
 D2: Bass                : 0
 D3: Mid                 : 0
 D4: Treble              : 0
 D5: Graphic Equalizer   : 0
 D6: Automatic Gain      : 0
 D7: Delay               : 0
bmaControls[1]           : 0x00
 D0: Mute                : 0
 D1: Volume              : 0
 D2: Bass                : 0
 D3: Mid                 : 0
 D4: Treble              : 0
 D5: Graphic Equalizer   : 0
 D6: Automatic Gain      : 0
 D7: Delay               : 0
iFeature                 : 0x00 (No String Descriptor)
Data (HexDump)           : 09 24 06 02 01 01 01 00 00 
.$.......

        ------- Audio Control Output Terminal Descriptor ------
bLength                  : 0x09 (9 bytes)
bDescriptorType          : 0x24 (Audio Interface Descriptor)
bDescriptorSubtype       : 0x03 (Output Terminal)
bTerminalID              : 0x03
wTerminalType            : 0x0301 (Speaker)
bAssocTerminal           : 0x00 (0)
bSourceID                : 0x02 (2)
iTerminal                : 0x00 (No String Descriptor)
Data (HexDump)           : 09 24 03 03 01 03 00 02 00 
.$.......

        ---------------- Interface Descriptor -----------------
bLength                  : 0x09 (9 bytes)
bDescriptorType          : 0x04 (Interface Descriptor)
bInterfaceNumber         : 0x01
bAlternateSetting        : 0x00
bNumEndpoints            : 0x00 (Default Control Pipe only)
bInterfaceClass          : 0x01 (Audio)
bInterfaceSubClass       : 0x02 (Audio Streaming)
bInterfaceProtocol       : 0x00
iInterface               : 0x00 (No String Descriptor)
Data (HexDump)           : 09 04 01 00 00 01 02 00 00 
.........

        ---------------- Interface Descriptor -----------------
bLength                  : 0x09 (9 bytes)
bDescriptorType          : 0x04 (Interface Descriptor)
bInterfaceNumber         : 0x01
bAlternateSetting        : 0x01
bNumEndpoints            : 0x02 (2 Endpoints)
bInterfaceClass          : 0x01 (Audio)
bInterfaceSubClass       : 0x02 (Audio Streaming)
bInterfaceProtocol       : 0x00
iInterface               : 0x00 (No String Descriptor)
Data (HexDump)           : 09 04 01 01 02 01 02 00 00 
.........

        -------- Audio Streaming Interface Descriptor ---------
bLength                  : 0x07 (7 bytes)
bDescriptorType          : 0x24 (Audio Interface Descriptor)
bDescriptorSubtype       : 0x01
bTerminalLink            : 0x01
bDelay                   : 0x01
wFormatTag               : 0x0001 (PCM)
Data (HexDump)           : 07 24 01 01 01 01 00 
.$.....

        ------- Audio Streaming Format Type Descriptor --------
bLength                  : 0x0B (11 bytes)
bDescriptorType          : 0x24 (Audio Interface Descriptor)
bDescriptorSubtype       : 0x02 (Format Type)
bFormatType              : 0x01 (FORMAT_TYPE_I)
bNrChannels              : 0x02 (2 channels)
bSubframeSize            : 0x02 (2 bytes per subframe)
bBitResolution           : 0x10 (16 bits per sample)
bSamFreqType             : 0x01 (supports 1 sample frequence)
tSamFreq[1]              : 0x0BB80 (48000 Hz)
Data (HexDump)           : 0B 24 02 01 02 02 10 01 80 BB 00 
.$.........

        ----------------- Endpoint Descriptor -----------------
bLength                  : 0x09 (9 bytes)
bDescriptorType          : 0x05 (Endpoint Descriptor)
bEndpointAddress         : 0x01 (Direction=OUT EndpointID=1)
bmAttributes             : 0x05 (TransferType=Isochronous 
SyncType=Asynchronous  EndpointType=Data)
wMaxPacketSize           : 0x00C4 (196 bytes)
bInterval                : 0x01 (1 ms)
bRefresh                 : 0x00
bSynchAddress            : 0x81 (Direction=IN EndpointID=1)
Data (HexDump)           : 09 05 01 05 C4 00 01 00 81 
.........

        ----------- Audio Data Endpoint Descriptor ------------
bLength                  : 0x07 (7 bytes)
bDescriptorType          : 0x25 (Audio Endpoint Descriptor)
bDescriptorSubtype       : 0x01 (General)
bmAttributes             : 0x00
bLockDelayUnits          : 0x00
wLockDelay               : 0x0000
Data (HexDump)           : 07 25 01 00 00 00 00 
.%.....

        ----------------- Endpoint Descriptor -----------------
bLength                  : 0x09 (9 bytes)
bDescriptorType          : 0x05 (Endpoint Descriptor)
bEndpointAddress         : 0x81 (Direction=IN EndpointID=1)
bmAttributes             : 0x11 (TransferType=Isochronous  SyncType=None 
EndpointType=Feedback)
wMaxPacketSize           : 0x0003 (3 bytes)
bInterval                : 0x01 (1 ms)
bRefresh                 : 0x02 (4 ms)
bSynchAddress            : 0x00
Data (HexDump)           : 09 05 81 11 03 00 01 02 00 
.........

von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> Such einfach nach AS_GENERAL
> bmAttribute steht bei mir auf 1 SR Request
> bLockDelayUnits auf 1 ms
> wLockdelay auf 0x01, 0x00
>
> Das sind Unterschiede die mir aufgefallen sind. Attribute 0x80 kenn ich
> nicht was soll das sein? Mit 0x01 bekommst du auf jedenfall den SR
> Request
> Thomas

Das war mir noch nicht aufgefallen. Die Basiskonfiguration hatte ich von 
einem Example übernommen und nicht jeden Eintrag explizit geprüft 
nachdem das Audio Playback im Grunde funktionierte.
Habe das Thema jetzt mal in der Spec nachgelesen:
https://www.usb.org/sites/default/files/audio10.pdf
Auf Seite 62 ist der entsprechende Endpoint beschrieben.
0x80 (Bit 7 auf 1) entspricht also der Option wMaxPacketSize. Dabei 
werden dann kürzere Pakete immer auf wMaxPacketSize mit Nullen gepadded.

Das Thema Requests hatte ich bisweilen nur peripher auf dem Schirm, da 
ich ohnehin eine feste SR nutzen will. Deswegen bin ich davon 
ausgegangen, das bis auf weiteres außen vor lassen zu können.
Soweit ich das bisher verstanden habe, kann man mit den Requests im 
Betrieb Parameter ändern. Z.B. die SR kann so im Betrieb verstellt 
werden, sofern das Device das unterstützt (mit DACs die als Clock-Slave 
laufen in der Regel kein Problem). So muss dann Windows-seitig nicht 
interpoliert werden.

Mit Bit 0 kann ich wie du sagtest den Sample Rate Request einstellen.
Bei Hannes ist das ausgeschaltet (Bit 0 ist 0). So weit ich das verstehe 
müsste das bei fixer SR auch so passen. Oder?
Die Optionen bLockDelayUnits and wLockDelay müssen beim asynchronen 
Endpoint sowieso auf 0 stehen (siehe Absatz über Tabelle 4-21).


Entscheidend dürfte das in Hinblick auf den Feedback Endpoint aber alles 
nicht sein. Ich vermute die Deskriptoren passen jetzt. Dann liegt es 
noch an der reinen Implementierung die Daten in den Endpoint zu 
bekommen.

Gruß
Markus

: Bearbeitet durch User
von Markus G. (usound)


Angehängte Dateien:

Lesenswert?

Also vielen Dank erstmal für die tolle Unterstützung hier!

Durch die letzten Modifikationen an Deskriptoren und weiterem Code kann 
ich nun mit Feedback Endpoint, allerdings ohne Funktion, zumindest schon 
mal wieder kurz Audio abspielen.
Das ging vorher nicht. Die Wiedergabe ist gar nicht erst angelaufen, 
sondern hat sofort gestoppt.
Wenn ich meine Funktion, um den Endpoint zu bedienen aktiv habe, verhält 
es sich genau so wie ohne.
Beim Abspielen von Audio läuft es ca. 15 Sekunden normal und dann kommt 
ein Durchgangston oder starke Verzerrungen.

Ich gehe davon aus, dass entweder die Daten nicht korrekt im Endpoint 
ankommen oder nicht aus diesem geladen werden.
Ich bin mir gerade nicht sicher, wie ich den errechneten Wert korrekt in 
den Endpoint buffer schreiben muss.

Die Makros die ich verwende sehen folgendermaßen aus (siehe Anhang).

Das habe ich im Arduino (ESP32) kurz via Konsolenausgabe getestet.
Das resultiert wie erwartet (manuell berechnet) in:
0x0C 0x00 0x00
buf[2] - buf[1] - buf[0]
Das Datum ansich müsste korrekt sein. Ich bin gerade nur am Überlegen 
wie sich die Konstellation ARM=Little Endian und Senden via USB verhält. 
Sprich wie rum müssen die Daten in den Endpoint buffer?

Gruß
Markus

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

USB ist little endian kann man sehr schön an den Deskriptoren erkennen 
z.b bei den Samplerates. Ob dein Konvertiermakro so richtig ist kann und 
will ich nicht beurteilen. Ich bin aber der Meinung dass es nicht so 
einfach ist.

Meiner Meinung müsste das Makro aus 48.5k folgendes in den epbuffer 
schreiben:
Buffer[0] = 0x00
Buffer[1] = 0x80
Buffer[2] = 48

Thomas

von Hannes M. (hannes_m)


Lesenswert?

Hallo Markus,

wie du es geschrieben hast ist die Formatierung für 48KHz korrekt.
Buffer[0] = 0x00;
Buffer[1] = 0x00;
Buffer[2] = 0x0C;

Ich habe dann am Ende mit Wireshark den USB Traffic mitgeloggt um zu 
schauen, dass die Daten korrekt vom Host gelesen werden.

In diesem Beispiel sieht das ganze unter Wireshark bei mir so aus (siehe 
letzte Zeile):


Frame 2333: 54 bytes on wire (432 bits), 54 bytes captured (432 bits) on 
interface 0
USB URB
    [Source: 3.2.1]
    [Destination: host]
    USBPcap pseudoheader length: 51
    IRP ID: 0xfffffa8014b314a0
    IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
    URB Function: URB_FUNCTION_ISOCH_TRANSFER (0x000a)
    IRP information: 0x01, Direction: PDO -> FDO
    URB bus id: 3
    Device address: 2
    Endpoint: 0x81, Direction: IN
    URB transfer type: URB_ISOCHRONOUS (0x00)
    Packet Data Length: 3
    [Request in: 2331]
    [Time from request: 0.003001000 seconds]
    Isochronous transfer start frame: 3928
    Isochronous transfer number of packets: 1
    Isochronous transfer error count: 0
USB isochronous packet
    ISO Data offset: 0x00000000
    ISO Data length: 0x00000003 (relevant)
    ISO USBD status: USBD_STATUS_SUCCESS (0x00000000) (relevant)
    ISO Data: 00000c

Gruß
Hannes

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Hallo Hannes,

danke für den Tipp mit Wireshark. Die Frage nach einem Sniffer Program 
wäre meine nächste gewesen.

Momentan sieht das bei mir so aus:

HOST -> Device
--------------------

Frame 10989: 51 bytes on wire (408 bits), 51 bytes captured (408 bits) 
on interface 0
USB URB
    [Source: host]
    [Destination: 2.18.2]
    USBPcap pseudoheader length: 51
    IRP ID: 0xffffc10ceb593900
    IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
    URB Function: URB_FUNCTION_ISOCH_TRANSFER (0x000a)
    IRP information: 0x00, Direction: FDO -> PDO
    URB bus id: 2
    Device address: 18
    Endpoint: 0x82, Direction: IN
    URB transfer type: URB_ISOCHRONOUS (0x00)
    Packet Data Length: 0
    [Response in: 10990]
    Isochronous transfer start frame: 3304183
    Isochronous transfer number of packets: 1
    Isochronous transfer error count: 0
USB isochronous packet
    ISO Data offset: 0x00000000
    ISO Data length: 0x00000000 (irrelevant)
    ISO USBD status: USBD_STATUS_SUCCESS (0x00000000) (irrelevant)


Device -> HOST
--------------------

Frame 10990: 51 bytes on wire (408 bits), 51 bytes captured (408 bits) 
on interface 0
USB URB
    [Source: 2.18.2]
    [Destination: host]
    USBPcap pseudoheader length: 51
    IRP ID: 0xffffc10ceb593900
    IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000)
    URB Function: URB_FUNCTION_ISOCH_TRANSFER (0x000a)
    IRP information: 0x01, Direction: PDO -> FDO
    URB bus id: 2
    Device address: 18
    Endpoint: 0x82, Direction: IN
    URB transfer type: URB_ISOCHRONOUS (0x00)
    Packet Data Length: 0
    [Request in: 10989]
    [Time from request: 0.000953000 seconds]
    Isochronous transfer start frame: 3304183
    Isochronous transfer number of packets: 1
    Isochronous transfer error count: 0
USB isochronous packet
    ISO Data offset: 0x00000000
    ISO Data length: 0x00000000 (relevant)
    ISO USBD status: USBD_STATUS_SUCCESS (0x00000000) (relevant)


Die Packet Data Length steht allerdings auf 0, folglich kommen da auch 
keine Daten. Da ist also noch irgendwo der Wurm drin. Selbst wenn meine 
Daten nicht im Endpoint Buffer ankommen, müsste die Datenübertragung der 
3 Byte mit jeweils dem Inhalt 0 trotzdem funktionieren?!
Hat jemand einen Tipp was das sein könnte?

Gruß
Markus

von Thomas (Gast)


Lesenswert?

Auf den meisten USB cores gibt es sowas wie ArmEp(len) Das ist oft nur 
ein Makro.
Schau mal nach wie deine Firmware Deskriptoren bereitstellt. So ähnlich 
sollte das auf auf anderen EPs funktionieren.

Thomas

von Markus G. (usound)


Lesenswert?

Hallo nochmal,

ich habe mich bei der Implementierung ziemlich festgefahren. Sobald es 
keine fertige und dokumentierte Klasse für den Anwendungsfall (hier 
Audio) gibt, wird es vor allem bei Atmel mit dem ASF schwierig.

Eine funktionale Implementierung des FB EP als Teil des Interfaces für 
das Audio Streaming ist mir derweil nicht gelungen. Ich kann die 
Endpoints im gleichen Interface nicht voneinander separiert 
ansteuern/auslesen.
Was aber lauffähig ist, ist zusätzlich die IN streaming Richtung für 
Audio. Sprich voll-duplex mit jeweils 2 Kanälen Audio.

Nun war meine Idee, einfach das Audio IN Interface für den Feedback 
Endpoint zu missbrauchen (im Deskriptor entsprechend umkonfiguriert).
Normalerweise wird es ja so gehandhabt: 1 Interface mit 2 Endpoints, 
einer für Audio OUT und einer für FB IN.
Wäre es möglich, den FB Endpoint in ein eigenes Interface zu legen?
Also Interface 1 ist Audio OUT und Interface 2 ist FB IN. Die Verbindung 
könnte man mit der SynchAddress machen. Wenn das gehen würde wäre der 
restliche Aufwand vermutlich überschaubar, da der IN Endpoint für Audio 
schon funktioniert (und nicht gebraucht wird).
Ist das seitens der USB Spec überhaupt möglich?

Gruß
Markus

von TSE (Gast)


Lesenswert?

Hallo zusammen,

ich wundere mich gerade warum hier noch keiner LUFA vorgeschlagen hat:
http://www.fourwalledcubicle.com/LUFA.php

von Thomas (Gast)


Lesenswert?

Markus G. schrieb:
> ich habe mich bei der Implementierung ziemlich festgefahren. Sobald es
> keine fertige und dokumentierte Klasse für den Anwendungsfall (hier
> Audio) gibt, wird es vor allem bei Atmel mit dem ASF schwierig

Nun du hast dir mit Sicherheit kein einfaches Kapitel rausgesucht.
Die Audio spec gehört mit zu den komplexesten Specs unter USB. Du kannst 
also nicht erwarten dass Atmel das alles für dich macht zumal du noch 
nicht mal an der Oberfläche gekratzt hast. Der wirklich interessante 
Part kommt noch wenn z.B. OSX oder andere OS ins Spiel kommen.
Zusätzlich sind Sync EPs eine Sache für Pro Audio genauso wie TDM. Für 
das Wissen um diese Dinge lassen sich Entwickler gut bezahlen. Es ist 
also kein Wunder dass in dem Bereich nicht so wahnsinnig viele Quellen 
existieren.

Ein profundes Wissen wie USB funktioniert ist Voraussetzung. Dazu gehört 
beispielsweise wie man einen EP scharf schaltet. Das bekommt man raus 
wenn man sich mal anschaut wie die Kommunikation generell funktioniert.
Ob dein Device einen Deskriptor zurückmeldet oder auf einem anderen EP 
eine 3Byte SR ist abgesehen vom EP und der Länge erst mal das Gleiche.

Da musst du dich schon selber reinfrickeln. Ich kann dir nur soviel 
sagen:
Es gibt zig Audiodevices auch im Pro Segment die ohne Sync EPs 
auskommen.

Thomas

von Thomas (Gast)


Lesenswert?

Noch ein paar Hinweise:
selbst der steinalte UDA 1335 von Philips hat es asynchron vor über 
20Jahren hinbekommen, zumindest meistens ein 48k File abzuspielen. 
Meistens deshalb weil der nur mit Intel USB Chipsets richtig konnte und 
alle 5 sec ein kurzer Klick zu  hören war. TI, CMedia und einige andere 
beweisen das USB Audio funktioniert. Ich kenne allerdings kein Device 
mit Atmel.
Alle mir bekannten Implementierungen setzen auf dezidierte USB Audio 
chips oder verwenden potente Cores z.B Cypress ev. in Verbindung mit 
FPGA und manchmal noch mit DSPs.
Ich glaube auch nicht das dein Problem vom nicht funktionierenden Sync 
EP kommt. Alle Devices die ich bis jetzt gesehen habe unterstützten z.B 
den SR request. Ich bin mir sicher das ist bei deinem MiniDSP Streamer 
nicht anders ist.

Thomas

von Markus G. (usound)


Lesenswert?

LUFA ist doch nur für AVR?

Thomas schrieb:
> Nun du hast dir mit Sicherheit kein einfaches Kapitel rausgesucht.
> Die Audio spec gehört mit zu den komplexesten Specs unter USB.
Dass das Vorhaben ambitioniert ist, ist mir durchaus bewusst. Die USB 
Spec finde ich handlebar, die ist ansich sauber dokumentiert. Es hapert 
eher auf der Windows-Treiberseite und bei der uC Implementierung selbst. 
Wenn man da die Standardklassen verlässt, wird das Debugging mühsam.

Habe den FB EP jetzt aber zum Laufen gebracht! Es lag im Endeffekt an 
einer Eigenheit des Controllers. Durch eine Verschiebung von 2 Zeilen 
Code war das Problem behoben.

Jetzt kommen also schonmal Daten aus dem FB EP. Ich habe mich dann auch 
gleich dran gemacht, den Algorithmus zu implementieren, um die SR im 
Betrieb nachzustellen. Das funktioniert rudimentär, bedarf aber noch 
etwas Feintuning (man hört noch periodisch, dass wohl etwas verloren 
geht).
Ich habe mal einige Test über jeweils den Zeitraum von 1 Sekunde (am 
Anfang der Übertragung) gemacht: Ich bekomme da Samplerates im Bereich 
von ca. 47,35 bis 47,7 kHz. Ist das normal?

In der Spec steht, dass man mindestens 1 Sekunde lang messen sollte, um 
eine Präzision von 1 Hz oder besser zu erreichen. Allerdings ist doch am 
Anfang der Audioübertragung mit dem Initalwert 48 kHz die Abweichung 
tendenziell am größten. Wenn ich dann eine Sekunde warten muss, bis 
überhaupt begonnen wird nachzuregeln, habe ich am Anfang Verzerrungen.

Gruß
Markus

von Markus G. (usound)


Lesenswert?

Hallo,

bin gerade dabei auszutüfteln, wie man die Regelung der Samplerate am 
besten macht. Gibt es dafür einen Algorithmus der sich anbietet?

Ich hatte überlegt, die ersten Samples zur Generierung des Startwerts 
für die Sampleratekorrektur zu nehmen und dann nach Bedarf 
nachzustellen.
Also z.B. so:
Nach 1000 Samples messe ich 47,500 (mit 1000 Samples kann ich zumindest 
theoretisch eine Auflösung von 1 Hz bekommen) kHz.
Dann gebe ich als ersten FB Wert 48,500 kHz an. Wenn der Wert im 
darauffolgenden Messintervall von 48 kHz abweicht, stelle ich ausgehend 
von der momentanen Abweichung z.B. in 3 Stufen zu:
- Abweichung größer x: vorherigen FB-Wert um 100 Hz nachstellen
- Abweichung größer y & kleiner x: FB-Wert +- 10 Hz
- Abweichung kleiner y: FB-Wert +- 1 Hz

Das wäre natürlich ein sehr einfacher Algorithmus. Kann man bei USB von 
einer halbwegs konstanten Abweichung ausgehen? Wenn der Clock im Betrieb 
auch noch hin und her driftet, müsste evtl. ein Algorithmus her, der 
schneller nachregelt.
Gibt es einen Regelalgorithmus der sich dafür anbietet?

Gruß
Markus

von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> In der Spec steht, dass man mindestens 1 Sekunde lang messen sollte, um
> eine Präzision von 1 Hz oder besser zu erreichen.

Nicht zu schnell aufgeben. Weiter unten steht:
> A sink can determine F_f by counting cycles of the master clock F_m for
> a period of 2^(K-P) (micro)frames. The counter is read into F_f and reset
> every 2^(K-P) (micro)frames.

von Thomas (Gast)


Lesenswert?

ich habe viel weiter oben schon beschrieben wie ich das machen würde.
Beim SetAlternate oder falls implementiert SetSampleRate würde ich eine 
Var mit
256* 288 initialisieren. Wenn ein ISO Packet kommt die Anzahl der Bytes 
* 256 Zum ersten Wert dazu addieren und dann halbieren. Diese Zahl ist 
die Mittler SampleRate *256.
Beachte dabei das 288 der Normwert für 2* 3 * fs48 ist.
Den Wert in die ent. FP Zahl umrechnen und im Sync EP bereitstellen
Immer wenn ein neues Streampacket da ist einfach wie oben neu berechnen.
Der Host wird die Sync Daten abholen und vielleicht was damit machen.
Dieses Verfahren setzt voraus dass du irgendwie an die Anzahl Bytes 
kommst, die gestreamt werden

Du brauchst auch keine Sekunde warten das sollte sich automatisch 
einstellen und wenn du nach ein zwei Sekunden nachschaust sollte genau 
48k erscheinen.

Thomas

von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> 256* 288 initialisieren. Wenn ein ISO Packet kommt die Anzahl der Bytes
> * 256 Zum ersten Wert dazu addieren und dann halbieren. Diese Zahl ist
> die Mittler SampleRate *256.
Die empfangenen Bytes kenne ich, da die USB-Funktion die Anzahl an Bytes 
liefert.
Du würdest also die Samplerate über 256 Frames mitteln?

> Den Wert in die ent. FP Zahl umrechnen und im Sync EP bereitstellen
> Immer wenn ein neues Streampacket da ist einfach wie oben neu berechnen.
So wie ich dich verstehe, willst du den momentan tatsächlichen Wert der 
Samplerate (in der Regel weniger als 48 kHz) zurückgeben?

Die Spec verstehe ich aber so, dass der korrigierte Wert zurückgegeben 
werden muss:
"The information carried over the synch path consists of a 3-byte data 
packet. These three bytes contain the
Ff value in a 10.14 format as described in Section 5.10.4.2, “Feedback” 
of the USB Specification. Ff
represents the average number of samples the endpoint must produce or 
consume per frame to match the
desired sampling frequency Fs exactly."
(USB Audio Device Class Definition 1)
Und:
"An asynchronous sink must provide explicit feedback to the host by 
indicating accurately what its desired
data rate (Ff) is, relative to the USB (micro)frame frequency. This 
allows the host to continuously adjust the
number of samples sent to the sink so that neither underflow or overflow 
of the data buffer occurs."
(USB 2.0 Specification)

Es ist die Rede von "desired data rate". Das heißt für mich:
Wenn der Host nur mit z.B. 47,5 kHz schickt, brauche ich einen 
korrigierten Feedback-Wert, z.B. 48,5 kHz, damit am Ende genau 48 kHz 
resultiert!?

Gruß
Markus

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Markus G. schrieb:
> Du würdest also die Samplerate über 256 Frames mitteln?

Nein das ist ein willkürlicher Faktor damit nicht so schnell 
Rundungsfehler entstehen. Du bekommst vom Host ja 288 Bytes +- 6 Bytes 
oder ev +- 12 Bytes.
Bei 12Bytes würde die SR um max 2Hz schwanken. Es wird aber vermutlich 
nur um 1HZ schwanken. diese Zahlen must du irgendwie kommulierenen und 
dann als FP Zahl bereitstellen. Wie das genau auszusehen hat ist m.M. 
nicht 100% genau beschrieben.
Ich würde wahrscheinlich alle 16 ms einen neuen Wert bereitstellen.
Der Wert könnte meines Erachtens die Differenz oder auch die 
tatsächliche Rate sein. Das kommt auf einen Versuch an.
Wie gesagt das ist etwas spekulativ ich sowas nie gemacht.

Thomas

von Cghgf (Gast)


Lesenswert?

Ich hab mehrere Wochen  auf einem STM32 mit Host lib
Gebastelt und eine UVC class geschrieben.

Ende vom lied war das die CPU last zu hoch war und der Stack mit der 
gefrickelten RTOS Implementierung gekrankt hat.

Aber das nochmal neu zu bauen... War mir auch zu viel Schmerz...

Moral der Geschichte..


Die libs und stacks dieser Welt sind auch nicht immer Allheilmittel.
Und Versuch Mal was für USB Host mit UVC class zu finden..
Ist eben so ein Thema wie audio..

Irgendwie scheint es zu gehen, aber wenn man was detailliert möchte ... 
Sucht man sich einen Wolf

von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> Es ist die Rede von "desired data rate". Das heißt für mich:
> Wenn der Host nur mit z.B. 47,5 kHz schickt, brauche ich einen
> korrigierten Feedback-Wert, z.B. 48,5 kHz, damit am Ende genau 48 kHz
> resultiert!?

Nicht unbedingt.

Der primäre Zweck des Feedbacks ist es, die unterschiedlichen Frequenzen 
von Host und Device auszugleichen. Da beide getrennte Quarzkristalle 
verwenden, wird die I²S-Hardware im Gerät die Samples nicht mit der 
exakten Frequenz aus dem internen Buffer lesen, wie sie der Host dort 
hinein schreibt.

Die Aufgabe deiner Firmware ist es, zu messen, wie viele Samples deine 
Hardware in jedem USB-Frame verarbeitet; das hat erst mal nichts damit 
zu tun, wie viele Samples der Host wirklich sendet. (Wenn du dazu die 
Master Clock vor dem Herunterdividieren verwendest, geht das schneller 
und genauer; siehe oben.)

Falls der Host den Feedback-Wert zu stark rundet, oder nicht schnell 
genug verarbeitet, dann kann es passieren, dass sich Fehler aufaddieren, 
und dass die Host-Sample-Rate dauerhaft von deiner abweicht. In diesem 
Fall wird dein interner Buffer irgendwann zu wenig oder zu viele Samples 
enthalten, und du solltest den Feedback-Wert kurz anpassen. Das ist aber 
erst mal nicht so wichtig.

von Markus G. (usound)


Lesenswert?

Hallo zusammen,

habe am Wochenende noch diverse Änderungen in der Verarbeitung der 
Audiodaten vorgenommen. Ich musste nochmals zurück ans Reißbrett, um 
einen generellen Fehler in der Verarbeitung auszumerzen. Zudem habe ich 
die Größe meines FIFOs maximal groß gemacht.
Das läuft nun schon ohne FB Endpoint ca. 15 min ohne hörbare Probleme. 
Dann kommt der Under/Overrun. Die Abweichung scheint sich folglich in 
Grenzen zu halten.

Clemens L. schrieb:
> Der primäre Zweck des Feedbacks ist es, die unterschiedlichen Frequenzen
> von Host und Device auszugleichen. Da beide getrennte Quarzkristalle
> verwenden, wird die I²S-Hardware im Gerät die Samples nicht mit der
> exakten Frequenz aus dem internen Buffer lesen, wie sie der Host dort
> hinein schreibt.
>
> Die Aufgabe deiner Firmware ist es, zu messen, wie viele Samples deine
> Hardware in jedem USB-Frame verarbeitet; das hat erst mal nichts damit
> zu tun, wie viele Samples der Host wirklich sendet. (Wenn du dazu die
> Master Clock vor dem Herunterdividieren verwendest, geht das schneller
> und genauer; siehe oben.)
Mein DMA-Modul sendet einen Sample pro Beat. Danach kann ich einen 
Interrupt generieren lassen. Somit könnte ich die Anzahl an Samples, die 
gesendet wurden recht komfortabel messen.
Ich würde jetzt so vorgehen:
Das Reshesh-Intervall des FB EP würde ich auf 8 (= 128 ms) einstellen. 
Das ist dann auch mein Messintervall.
Dann zähle ich über das Intervall wie viele Samples gesendet wurden und 
zähle wie viele Bytes auf dem USB angekommen sind.
Dann erhalte ich einen Faktor, mit dem ich die Wunsch-Samplerate 
multipliziere.
That's it? Oder vergesse ich hierbei etwas?

Gruß
Markus

von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> Mein DMA-Modul sendet einen Sample pro Beat. Danach kann ich einen
> Interrupt generieren lassen. Somit könnte ich die Anzahl an Samples, die
> gesendet wurden recht komfortabel messen.

Was ist der Trigger für das DMA-Modul? Mit der Master-Clock wäre das 
genauer.

von Markus G. (usound)


Lesenswert?

Clemens L. schrieb:
> Markus G. schrieb:
>> Mein DMA-Modul sendet einen Sample pro Beat. Danach kann ich einen
>> Interrupt generieren lassen. Somit könnte ich die Anzahl an Samples, die
>> gesendet wurden recht komfortabel messen.
>
> Was ist der Trigger für das DMA-Modul? Mit der Master-Clock wäre das
> genauer.

Der Trigger ist das I2S-Modul. Das fordert im Prinzip die Samples vom 
DMA-Modul an. Das I2S-Modul hängt am 49,152 MHz Clock der DPLL.

Gruß
Markus

von Hannes M. (hannes_m)


Lesenswert?

Hallo Markus,

Ich bin auch gerade wieder dabei an meinem Audio Device 
weiterzuarbeiten. Bisher habe ich ja den Offset der Schreib/Lesezeiger 
benutzt und dann einfach die angeforderte Samplerate verändert.
Gestern habe ich erste Tests durchgeführt und Versuche gerade nach den 
aktuellen USB Specs vorzugehen - heißt ich habe die i2s master clock 
(welche bei mir 12,288 MHz beträgt an einen Timer gehängt und zähle 
diese immer zwischen den SOFs welche vom USB Host im 1ms Takt generiert 
werden. Somit erhalte ich jede ms einen zählwert der dann in meinen 
Fällen etwa bei 12290 liegt. Geteilt durch 256 erhalte ich somit die 
Sync rate, die ich dem PC mitteilen muss, damit dieser für meinen i2s 
Takt passend die Daten schickt. Das ganze scheint wunderbar zu 
funktionieren - jedoch muss man natürlich trotzdem noch seinen Versatz 
der Pufferzeiger im Auge behalten und die errechnete rate noch 
feinjustieren. Man kann natürlich auch über mehrere SOFs zählen und 
dementsprechend seinen Teiler verdoppeln/verdreifachen etc um hier mit 
einem besseren Mittelwert arbeiten zu können.

Ein zählen der Daten hingegen halte ich für nicht sonderlich sinnvoll, 
da man ja gar nicht weiß, wann der Host nach empfangen der neuen Sync 
rate seine rate tatsächlich anpasst. Dadurch kann schnell ein fehler bei 
dem bestimmen der neuen Sync rate entstehen und man muss über einen 
längeren Zeitraum messen um diesen Fehler möglichst klein zu bekommen.

Gruß
Hannes

Edit: bei meiner Vorgehensweise komme ich aktuell mit einem Buffer für 
ca 4ms Audiodaten aus ohne Buffer under/overruns in 30minuten Betrieb zu 
bekommen. Bei einem Buffer für 10ms kommt dies gar nicht mehr vor (so 
lang ich getestet habe).

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Hannes M. schrieb:
> heißt ich habe die i2s master clock
> (welche bei mir 12,288 MHz beträgt an einen Timer gehängt und zähle
> diese immer zwischen den SOFs welche vom USB Host im 1ms Takt generiert
> werden. Somit erhalte ich jede ms einen zählwert der dann in meinen
> Fällen etwa bei 12290 liegt. Geteilt durch 256 erhalte ich somit die
> Sync rate, die ich dem PC mitteilen muss, damit dieser für meinen i2s
> Takt passend die Daten schickt. Das ganze scheint wunderbar zu
> funktionieren - jedoch muss man natürlich trotzdem noch seinen Versatz
> der Pufferzeiger im Auge behalten und die errechnete rate noch
> feinjustieren. Man kann natürlich auch über mehrere SOFs zählen und
> dementsprechend seinen Teiler verdoppeln/verdreifachen etc um hier mit
> einem besseren Mittelwert arbeiten zu können.
Hallo Hannes,

das heißt du überträgst dann den gemessenen tatsächlichen Takt, mit dem 
die Samples auf dem I2S gesendet werden an den Host?!
In deinem Fall also: Fs * 12290/12288?

Werde ich nachher gleich mal testen.

Gruß
Markus

von Hannes M. (hannes_m)


Lesenswert?

Nein, viel einfacher - ich bestimme so die Anzahl der i2s Takte auf 
meinem Device für die Periode von 1ms des Hosts.

Heißt für mein Device wären 1ms = 12288 Takte = 48 kHz.

Wenn ich nun zwischen jedem SOF 12290 Takte zähle heißt dies, dass der 
Host langsamer sendet als ich über i2s (also ich bekomme zu wenig Daten 
über USB wenn der Host mit 48khz sendet).

Der gezählte wert ist natürlich abhängig vom jeweiligen Takt des Hosts 
(also es kann auch durchaus sein, dass wir nur 12285 Takte zählen, was 
bedeutet, dass der Host zu schnell Daten sendet - für ihn also 1ms 
schneller vergehen als für das Device)

Teilt man nun den gezählten wert durch 256 erhält man die sync rate, die 
man dem PC mitteilen muss :

In dem Beispiel 12290 / 256 = 48,0078125 kHz
Oder 12285 / 256 = 47,98828125 kHz

Dieser wer entspricht dann direkt der Sync rate und der PC sendet dann 
im ersten Fall mehr Daten oder im 2. Fall weniger, was dann der 
synchrongeschwindigkeit unseres Device entspricht.

Mit dem Verfahren lässt sich sehr schnell (in 1ms) eine Recht genaue 
Sync rate ermitteln und das ohne Bytes zählen zu müssen.

Wenn es noch genauer werden soll, kann man natürlich auch über mehrere 
SOFs zählen und erhält so einen genaueren Mittelwert.

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Hannes M. schrieb:
> Nein, viel einfacher - ich bestimme so die Anzahl der i2s Takte
> auf
> meinem Device für die Periode von 1ms des Hosts.

Hallo Hannes,

nun der Rechenweg ist im Endeffekt egal.
Habe es aber nun so wie du implementiert. Ich nehme einen 16 Bit Zähler, 
den klemme ich an den I2S Clock, teile per pre-divider auf 12,288 MHz 
runter und zähle über 4 SOFs. Das ergibt dann rechnerisch einen Wert von 
49152 des Zählers, passt also in 16 Bit.

Der Ablauf sieht so aus:
- Audio soll abgespielt werden (Alternate Setting wird gesetzt)
- im SOF Callback:
--> Clock-Zähler wird gestartet und Routine verlassen, zugehöriges Flag 
wird gesetzt, dass der Clock-Zähler läuft und nicht mehr gestartet 
werden muss
- in jedem SOF wird der Counter für die Mittelung erhöht, bis 4 erreicht 
wird. Der Clock-Zählerwert wird ausgelesen, die neue Samplerate 
berechnet und in das zugehörige Array geschrieben. Dann wird der 
Clock-Zähler und der Counter für die Mittelung zurückgesetzt und die 
SOF-Routine verlassen.

So weit so gut. Wenn ich mir nun im Debug-Modus die Werte anschaue, sehe 
ich, dass der erste Wert beim Auslesen des Zählers stimmt (ergibt z.B. 
48,00195 kHz). Die darauffolgenden Werte sind allerdings viel zu klein 
(Zählerwert in Größenordnung 15000-30000), obwohl der Zählerstand am 
Ende der Berechnungen zurückgesetzt wird.
Ich vermute einen Logikfehler, der evtl. einen Overflow oder dergleichen 
auslöst. Ich brüte nun schon 2 Tage darüber und komme einfach nicht 
drauf.
Hat jemand einen Tipp, habe vorher noch nie einen Timer/Counter (TC) 
gebraucht. Gibt es da bekannte Fallstricke?

Gruß
Markus

von Joe F. (easylife)


Lesenswert?

Hat dein Device nur Audio-Ausgänge oder auch Eingänge?
Mit Eingängen wird es wesentlich einfacher, dann kannst du "implicit 
feedback" nutzen, und auf den Feedback Endpoint ganz verzichten.
Der Host orientiert sich dann an der Datenmenge auf dem 
Audio-In-Endpoint.
Voraussetzung ist natürlich, dass hardwareseitig Inputs und Outputs 
synchron sind (gleiche Clockdomain).

: Bearbeitet durch User
von Hannes M. (hannes_m)


Lesenswert?

Hallo Joe,

soweit mir bekannt ist, unterstützt erst die UAC2 Claas implicit 
Feedback, daher ist dies in unseren Fällen leidrr nicht möglich, da es 
sich um UAC1 Geräte handelt.

von Markus G. (usound)


Lesenswert?

Laut UAC1 Spec müsste implicit Feedback für IN Endpoints schon gehen. 
Allerdings sind in meiner Anwendung keine Eingänge geplant, entsprechend 
muss ich mich auf das explizite Feedback konzentrieren.

Gruß
Markus

von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> So weit so gut. Wenn ich mir nun im Debug-Modus die Werte anschaue, sehe
> ich, dass der erste Wert beim Auslesen des Zählers stimmt (ergibt z.B.
> 48,00195 kHz). Die darauffolgenden Werte sind allerdings viel zu klein
> (Zählerwert in Größenordnung 15000-30000), obwohl der Zählerstand am
> Ende der Berechnungen zurückgesetzt wird.

Was genau wird zurückgesetzt? Ohne Code kann dir keiner helfen.

von guest (Gast)


Lesenswert?

Markus G. schrieb:
> Der Clock-Zählerwert wird ausgelesen, die neue Samplerate
> berechnet und in das zugehörige Array geschrieben. Dann wird der
> Clock-Zähler und der Counter für die Mittelung zurückgesetzt und die
> SOF-Routine verlassen.
>
> So weit so gut. Wenn ich mir nun im Debug-Modus die Werte anschaue, sehe
> ich, dass der erste Wert beim Auslesen des Zählers stimmt (ergibt z.B.
> 48,00195 kHz). Die darauffolgenden Werte sind allerdings viel zu klein
> (Zählerwert in Größenordnung 15000-30000), obwohl der Zählerstand am
> Ende der Berechnungen zurückgesetzt wird.

Was machst Du mit den I2S Ticks, die zwischen Auslesen und Zurücksetzen 
Deines Zählers auflaufen?
Laß Deinen Zähler frei laufen, also ohne Zurücksetzen. Den aktuellen 
Zählwert bekommts Du als Differenz zwischen aktuell ausgelesenem und 
beim vorhergenden Lauf ausgelesenem Wert (Überlauf beachten!).

von Markus G. (usound)


Angehängte Dateien:

Lesenswert?

Clemens L. schrieb:
> Was genau wird zurückgesetzt? Ohne Code kann dir keiner helfen.
Da hast du natürlich vollkommen recht :)

guest schrieb:
> Was machst Du mit den I2S Ticks, die zwischen Auslesen und Zurücksetzen
> Deines Zählers auflaufen?
Genau deswegen setze ich den Zähler erst nach den Berechnungen zurück.
> Laß Deinen Zähler frei laufen, also ohne Zurücksetzen. Den aktuellen
> Zählwert bekommts Du als Differenz zwischen aktuell ausgelesenem und
> beim vorhergenden Lauf ausgelesenem Wert (Überlauf beachten!).
Das habe ich nun implementiert. Die Problematik ist die selbe wie zuvor. 
Der erste Wert den ich ermittle stimmt, die darauffolgenden nicht mehr.

Anbei der relevante gekürzte Code. Kurz zur Erklärung, wenn das 
alternate Setting des Interfaces gesetzt wird, beginnt die Routine mit 
dem Streaming. In der Routine wird auf das Ankommen neuer Samples 
gepollt. Das Handling des Fb Werts mache ich im SOF callback.

Der Ablauf gestaltet sich so:
- Der Zähler läuft direkt beim Systemstart los
- Wiedergabe wird gestartet
- alternate Setting wird gesetzt, mein Flag für den ersten 
Zählerdurchlauf (FB_EP_start_counter) wird dann auf 1 gesetzt.
- sobald der nächste SOF kommt, wird der aktuelle Zählerwert 
abgespeichert (FB_EP_tc_last_val). Das flag wird zurückgesetzt und die 
SOF-Routine verlassen. Das ist dann mein Initialwert des Zählers.
- in den folgenden SOFs warte ich, bis 4 SOFs vergangen sind und hole 
dann den aktuellen Zählerwert ab.
Dann wird verglichen, ob es einen Overflow gab und entsprechend 
verrechnet. Dann durch 1024 geteilt (das ergäbe bei 12288 * 4 ms dann 
genau 48).
- Dann wird der Wert für den FB EP umgerechnet und in den Buffer gelegt.
- Am Ende wird FB_EP_tc_last_val neu gesetzt, um die vergangenen Ticks 
während der Berechnung zu eliminieren.

Im ersten Durchlauf passt der resultierende Wert. Danach nicht mehr. Ich 
tippe wie gesagt auf einen Logikfehler. Ansonsten müsste etwas 
vorsichgehen, was ich bislang noch nicht auf dem Schirm hatte.

Gruß
Markus

Edit:
Nunja, wie ich bereits erwartet habe, sobald ich hier das Problem 
genauer schildere, finde ich den Fehler :)
Am Ende der Routine habe ich den FB_EP_tc_last_val neu gesetzt und zwar 
mit dem aktuellen Zähler und nicht mit dem am Anfang ermittelten Wert. 
Dadurch ist der Zeitraum natürlich zu kurz.
Was mir aufgefallen ist: im Debugger kam der Wert trotzdem nicht 
korrekt. Ich denke im laufenden Betrieb mit USB und Timer kann man evtl. 
auch einfach nicht mehr sinnvoll debuggen.

Werde jetzt mal einige Zeit hören und schaunen wie es mit den Buffer 
Einstellungen klappt. Auf dem USB konnte ich mit dem Sniffer auf jeden 
Fall schonmal sinnvolle FB Werte sehen. Momentan ist mein buffer 30 SOFs 
groß. Sobald die ersten 10 im Speicher liegen, fange ich an mit dem 
senden via DMA. Da könnte ich auch noch optimieren. Bei der Mittelung 
könnte ich auch noch etwas feintunen.

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Vielen Dank an alle Beteiligten! Das waren wirklich konstruktive und 
produktive Beiträge. Für das Fein-Tuning werde ich aber womöglich 
nochmal auf euch zurückkommen.

@Hannes:
Um ein Monitoring des Buffer levels werde ich denke ich trotzdem nicht 
herum kommen. Hast du das schon implementiert?
Stellst du dabei pauschal um einen Wert zu?
Also z.B. zu wenig Daten im Speicher --> FB-Wert um 0,001 bzw 1 Hz 
erhöhen.
Eine elegantere Lösung fällt mir gerade nicht ein.

Gruß
Markus

von Thomas (Gast)


Lesenswert?

Markus G. schrieb:
> Vielen Dank an alle Beteiligten! Das waren wirklich konstruktive und
> produktive Beiträge

Ja solche Thread gibts hier immer noch. Wobei seltener werden. Leider 
ist es mittlerweile die Regel dass nach spätestens 40 Posts die Sache 
aus dem Ruder läuft. Liegt vermutlich daran dass bei dem Thema nicht so 
viele mitreden können/wollen.

Thomas

von Hannes M. (hannes_m)


Lesenswert?

Hallo Markus,

Genauso mache ich das - also ich habe bei mir hierzu aktuell eine 
Routine im i2s Interrupt definiert, die eine variable auf + bzw. - 
beispielsweise 1 setzt. Diese Variable addiere ich einfach auf meinen 
Zählwert und verändere so die Sync rate um einen minimalen wert um den 
Schreibzeiger so mittig auszuregeln.

Perfektioniert habe ich das ganze aber auch noch nicht. Wenn du hier 
eine bessere Idee hast wäre ich natürlich auch interessiert :).

Gruß
Hannes

von Markus G. (usound)


Lesenswert?

Hannes M. schrieb:
> Perfektioniert habe ich das ganze aber auch noch nicht. Wenn du hier
> eine bessere Idee hast wäre ich natürlich auch interessiert :).


Hallo Hannes,
eine bessere Idee hatte ich bisweilen auch noch nicht. Habe aber gerade 
einige Basisüberlegungen angestellt.

Messung auf der Device-Seite:
Teilt man für den Zähler den Masterclock des I2S-Moduls auf 12,288 MHz 
herunter, bekommt man 12288 Counts pro SOF.
Das führt zu einer Genauigkeit von 3,9 Hz (12289 Counts entsprechen 
48,0039 kHz).
Eine Mittelung über 4 SOFs liefert also rein rechnerisch eine 
Genauigkeit <1 Hz. Das ist schonmal in Ordnung.

Umsetzung auf der Host-Seite:
- Für eine Auflösung von 100 Hz braucht der Host 10 USB-Frames. 
Klassisches Beispiel sind die audiotypischen 44,1 kHz: 9 Frames kommen 
44 Samples, 1 Frame kommen 45 Samples.
- Für eine Auflösung von 10 Hz ergibt sich 100 Frames und für 1 Hz 1000 
Frames. 1000 Frames sind wiederum eine Sekunde, was auch zur Angabe in 
der Spec passt, man möge mindestens 1 Sekunde lang messen.

Was mich nun stutzig macht:
Angenommen wir arbeiten z.B. mit einem Polling-Intervall des FB 
Endpoints von 4 ms.
Wir mitteln über 4 Frames und bekommen eine Abweichung von einem Count. 
Das ist dann rund 1 Hz Abweichung. Der FB-Wert wird an den Host 
übermittelt. Der Host stellt die Samplerate entsprechend nach. Der Host 
benötigt aber 1 Sekunde Zeit, um die Auflösung überhaupt umsetzen zu 
können.
Nach 4 ms kommt allerdings schon der nächste Fb-Wert. Dieser ist vllt 
schon wieder in Ordnung (genau 48 kHz). Dann geht dem Host theoretisch 
dieser eine Sample, den er ausgehend vom vorherigen FB-Wert senden 
sollte verloren.
Über längere Zeit akkumuliert sich das Ganze und es kommt so ein Drift 
des Buffer-Levels zustande (zusätzlich zur ohnehin vorhandenen 
Abweichung der Clock-Domains, diese wird aber auf längere Zeit 
problemlos ausgeregelt).

Habe ich irgendwas nicht bedacht? Aus dieser Sicht kommt mir die ganze 
Umsetzung des FB-Mechanismus etwas kurios vor. Bin gespannt auf eure 
Anmerkungen.

Gruß
Markus

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Markus G. schrieb:
> Habe ich irgendwas nicht bedacht? Aus dieser Sicht kommt mir die ganze
> Umsetzung des FB-Mechanismus etwas kurios vor. Bin gespannt auf eure
> Anmerkungen.

Ja das sehe ich auch so und das ist vielleicht auch der Grund warum es 
nicht so viele Devices gibt die das implementieren. Ich persönlich kenne 
nur ein etwas seltsames Teil von Yamaha. Kommerzielle Devices die ich 
kenne regeln mit dem SOF eine PLL die dann wiederum fs256 oder fs512 
generiert.
Der SOF Takt wird per Definition als genau angesehen.

Thomas

von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> Kommerzielle Devices die ich
> kenne regeln mit dem SOF eine PLL die dann wiederum fs256 oder fs512
> generiert.
> Der SOF Takt wird per Definition als genau angesehen.
>
> Thomas

Hallo Thomas,
ich denke die Lösung mit FB-Endpoint ist schon mit Abstand die 
professionellste und hochqualitativste.
Bei der Implementierung kann man sich einfach nicht 100% an die USB Spec 
halten (die Dokumentation hätte anwendungsnäher ausfallen sollen). Man 
muss zwingend eine Überprüfung des Buffer-Levels implementieren und dann 
nach Bedarf manuell nachregeln. Dann scheint mir die Lösung keine 
Nachteile zu haben.

@Hannes:
Wenn ich einen neuen Buffer-Block anfange zu beschreiben schaue ich, wie 
viele Samples derweil auf dem I2S gesendet wurden.
Ich hab jetzt einfach mal folgendes implementiert:
Der Index darf +- 20% vom Idealwert abweichen. Innerhalb dieser Grenzen 
wird nichts korrigiert.
Ist die Abweichung größer, korrigiere ich um +- 1 Hz.
Die Werte sind allerdings völlig aus der Luft gegriffen. Der Test muss 
dann zeigen, ob das so praxisgerecht ist.

Gruß
Markus

von Thomas (Gast)


Lesenswert?

Markus G. schrieb:
> ich denke die Lösung mit FB-Endpoint ist schon mit Abstand die
> professionellste und hochqualitativste.

Das kann ich nicht beurteilen. Dazu muss mein eine Jitter Messung für 
beide Varianten machen. Dies wiederum würde bedeuten dass man beide 
Varianten fehlerfrei implementiert hat und eine entsprechende 
Messumgebung z.B. eine AP mit Digital Option vorhanden ist.

Thomas

von Hannes M. (hannes_m)


Lesenswert?

Hallo Markus,

ich habe heute auch wieder eine ganze Zeit lang an der Synchronisation 
gearbeitet. Ich verfahre mittlerweile so, dass ich eine Variable mit 
Startwert von 48KHz vorbelege, diese dann immer nach 5 SOFs mit meinem 
Zählwert verrechne (also die Differenz bilde). Diese Differenz rechne 
ich dann zu 1/100 zum Aktualwert dazu. Durch diese verrechnung ist mein 
Aktualwert sehr viel genauer als wenn ich nur über 5 SOFs zähle und der 
Wert schwankt auch nicht mehr so stark.

Zusätzlich benutze ich meinen Fehloffset vom Schreib zum Lesezeiger und 
nutze diesen direkt als Regelparameter mit einem Regelwert von 
0.01Hz/Byte.

Mit dieser Einstellung läuft mein Device gerade mit einem Jitter von 
etwa +/- 0.5Hz.

Gruß
Hannes

von Markus G. (usound)


Lesenswert?

Hallo,

bin wieder am Thema dran. Ich werde die Tage noch einige grundsätzliche 
Überlegungen darlegen, nachdem alles noch etwas gären konnte.

Bin momentan dabei, eine zweite Samplerate zu implementieren. Das Ganze 
soll dann darauf hinauslaufen, im Betrieb die SR wechseln zu können (mit 
einem Request).
Im ersten Schritt hatte ich ja alles fix auf 48 kHz SR ausgelegt. Nun 
bin ich dabei das auf 44,1 kHz anzupassen (ebenfalls erstmal fix 
implementiert).

Die nicht ganzzahlige SR (in kHz) bringt einige Fallstricke mit sich. 
Entsprechend tue ich mich momentan noch etwas schwer, das zum laufen zu 
bringen.

Die Clock-config des I2S-Moduls muss ich nur beim Basisclock anpassen. 
Sprich von 49,152 MHz habe ich auf 45,1584 MHz gewechselt. Die 
Teilerverhältnisse der restlichen Clocks bleiben natürlich gleich im 
Vergleich zu 48 kHz.
Den Output habe ich mittels Oszi gecheckt, das passt alles.

Im Deskriptor habe ich die MaxPaketSize geändert:
(45 Samples (aufgerundet) + 1 Sample Spielraum)   2 Kanäle  3 Byte = 
276 Byte
Den einen Sample dazu, um die SR im Betrieb via FB Endpoint stellen zu 
können, könnte ich mir wohl auch sparen, da ich ja sowieso auf 45 
aufrunden muss. Ist wohl aber nicht entscheidend.

Das Ganze enummeriert auch wie gewohnt. Allerdings die Audio-Wiedergabe 
funktioniert nicht.
Ich hatte erst meine Routinen auf dem uC in Verdacht, diese habe ich 
allerdings zu Begin gleich dynamisch, sprich für unterschiedliche SRs 
kompatibel ausgelegt.
Wenn ich mit Wireshark auf dem USB logge, dann kommen da erst garkeine 
Daten. Einen Fehler in Windows gibt es allerdings nicht.
Irgendwas scheine ich nicht bedacht zu haben.
Hat jemand einen Tipp?

@Hannes: hast du deine Software auch schon mal für 44,1 kHz, was für 
Audio (mp3) wesentlich verbreiteter ist, getestet?

Gruß
Markus

von Clemens L. (c_l)


Lesenswert?

Wenn Windows nicht will, dann probier mal mit den Pinguin ...

von Thomas (Gast)


Lesenswert?

Wie immer zeig die Deskriptoren....
Windows verhält sich ziemlich gutmütig beim Enum. Ein enumeriertes 
Device ist noch lange kein Audiodevice. Unter der Verraussetzung dass du 
ein korrektes Deskriptorset hast passieren beim Abspielen 2 Dinge:
1. SetInterface(alternate) std request um Bandbreite zu reservieren
2. SetSr(sr) Classrequest um die Samplerate einzustellen.

Beide Request müssen korrekt bearbeitet werde und sollten auch mit 
wireshark zu sehen sein. Falls nicht wird der Audiotreiber nichts 
streamen und du bekommst keine Daten.
Nun zum Classrequest:
Ist das korrekt implementiert? Hast du dafür einen Händler? Beachte das 
der SetSr ein Outrequest ist der eine OutDatastage hat. Ich hab schon 
genügend USB code gesehen der das nicht richtig macht. Das liegt u.A. 
auch daran das std requests keinen Outrequest mit OutDatastage stage 
definieren.
Thomas

von Markus G. (usound)


Lesenswert?

Thomas schrieb:
> Wie immer zeig die Deskriptoren....
> Windows verhält sich ziemlich gutmütig beim Enum. Ein enumeriertes
> Device ist noch lange kein Audiodevice. Unter der Verraussetzung dass du
> ein korrektes Deskriptorset hast passieren beim Abspielen 2 Dinge:
> 1. SetInterface(alternate) std request um Bandbreite zu reservieren
> 2. SetSr(sr) Classrequest um die Samplerate einzustellen.

Es geht mir noch garnicht um die Requests! Ich will erstmal nur von 
meiner festen Konfig. 48k - 24 Bit auf 44,1k - 16 Bit umstellen und 
testen, ob das funktioniert (sprich in eine andere Clockbasis wechseln).

Die Deskriptoren habe ich überprüft, sind komplett identisch bis auf die 
andere SR. Ich habe zwischenzeitlich das Problem verortet, warum Windows 
die Wiedergabe gesperrt hat:
Wenn das Gerät mit gleichem Namen und gleicher Seriennummer einmal mit 
48k - 24 Bit angeschlossen war, speichert Windows diese Konfig 
natürlich. Schließt man das vermeintlich gleiche Gerät dann mit einer 
anderen SR-Konfig (z.B. 48k- 16 Bit) an, sperrt Windows die 
Audioausgabe, weil gespeicherte Konfig und aktuell ausgelesene Konfig 
nicht mehr zusammenpassen.
Ich habe das im Code nun übergangsweise so gelöst, dass ich für jede 
Konfiguration eine eigene Seriennummer vergeben habe.
Also z.B. 48k - 24 Bit => 0.0.1
44,1k - 16 Bit => 0.0.2
Windows erkennt das dann alles als unterschiedliche Geräte mit gleichem 
Namen.
Soweit so gut.

: Bearbeitet durch User
von Thomas (Gast)


Lesenswert?

Du kannst es natürlich machen wie du willst, aber mit der SN bist du auf 
dem Holzweg. Usbaudio braucht keine SN das einzige was du damit 
verhindern kannst ist eine erneute Treiber Installation wenn das Device 
an einem anderen USB Port angesteckt wird. Anstelle einer neuen SN 
kannst du auch die entsprechende PNF Datei im Treiber Ordner löschen das 
hätte die gleiche Wirkung.

Usbaudio parst die Deskriptoren deines devices und baut daraus einen 
Deskriptor Satz für KS.sys. Das Streaming wird von KS.sys gemacht nicht 
von usbaudio.sys.
KS.sys ist ein sogenannter miniport driver. Wenn du also einen Fehler in 
den Deskriptoren hast wird ein falscher Satz KS Deskriptoren erzeugt und 
nichts geht.
Es gab / gibt ein tool von MS ksstudio oder so sollte immer noch in ddks 
zu finden sein. Ein anders Stichwort wäre graphview in alten ddks.

Wenn dein Device also mit anderen Deskriptoren auf einmal nicht mehr 
streamt hast du einen Fehler in den Deskriptoren. Beliebte Ursache ist 
eines der Längenfelder Z.B im AC control Header.

Thomas

von Markus G. (usound)


Lesenswert?

Thomas schrieb:

> Wenn dein Device also mit anderen Deskriptoren auf einmal nicht mehr
> streamt hast du einen Fehler in den Deskriptoren. Beliebte Ursache ist
> eines der Längenfelder Z.B im AC control Header.
Der Deskriptor war 100% identisch bis auf die andere Samplerate.
Der Trick mit der unterschiedlichen SN hat zunächst mal funktioniert und 
erlaubt mir weitere Tests.
44,1 kHz Playback funktioniert jetzt schonmal grundsätzlich.


Ich habe mich parallel dazu wieder mit dem Feedback-Thema beschäftigt. 
Hier einige Gedanken dazu:
- Übertragung des FB-Werts im Format 10.10 (mit 4 Nullen gepadded).
- Der kleinste Wert 0,0000 0000 01 (binär) entspricht dezimal 0,0009766
--> Man kann also schonmal nicht genau einen Sample oder ein Hz 
anfordern. Unschön. Weniger als einen Sample kann man folglich auch 
nicht anfordern, sprich genauer gehts also nicht. Insofern macht 
zumindest der angegebene Messzeitraum von min. 1 Sekunde in der USB-Spec 
Sinn,
da: 1 Sample pro Sekunde = 1 Hz, bzw. man braucht 1000 Frames, um 1 Hz 
auflösen/übermitteln zu können.
- Folgende Frage resultiert: Angenommen ich fordere 1 Sample mehr an, 
also 48,001 kHz. Innerhalb einer Sekunde kann der Host das also gar 
nicht auflösen, da er nur 0,976 Samples als FB-Wert erhält. Wie handhabt 
das Windows nun, wenn der Sample noch nicht gesendet wurde, aber schon 
ein neuer FB-Wert anliegt? Geht der zuvor angeforderte Sample dann 
verloren?


Man kommt zum Schluss, dass die USB-Audio Spec nicht so sonderlich 
durchdacht ist. Ohne ein Monitoring des Füllstands des FIFOs 
funktioniert das einfach nicht. Ich hatte gehofft mir das zusätzliche 
Monitoring des buffers sparen zu können, weil der uC schon ziemlich gut 
ausgelastet ist.
Nun muss ich schauen/testen, ob ich das Monitoring noch unterbringe.

Gruß
Markus

von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> Wie handhabt das Windows nun, wenn der Sample noch nicht gesendet wurde,
> aber schon ein neuer FB-Wert anliegt? Geht der zuvor angeforderte Sample
> dann verloren?

Der Host-Treiber hat einen Zähler, der aussagt, wie viele Samples das 
Device haben will. In jedem Frame wird der FB-Wert auf den Zähler 
addiert, und die Anzahl der wirklich gesendeten Samples abgezogen.

Beispiel (dezimal statt binär), mit Feedback = 44,099:
Frame  1: Zähler = 44,099 -> sende 44 Samples -> Zähler = 0,099
Frame  2: Zähler = 44,198 -> sende 44 Samples -> Zähler = 0,198
Frame  3: Zähler = 44,297 -> sende 44 Samples -> Zähler = 0,297
Frame  4: Zähler = 44,396 -> sende 44 Samples -> Zähler = 0,396
Frame  5: Zähler = 44,495 -> sende 44 Samples -> Zähler = 0,495
Frame  6: Zähler = 44,594 -> sende 44 Samples -> Zähler = 0,594
Frame  7: Zähler = 44,693 -> sende 44 Samples -> Zähler = 0,693
Frame  8: Zähler = 44,792 -> sende 44 Samples -> Zähler = 0,792
Frame  9: Zähler = 44,891 -> sende 44 Samples -> Zähler = 0,891
Frame 10: Zähler = 44,990 -> sende 44 Samples -> Zähler = 0,990
Frame 11: Zähler = 45,089 -> sende 45 Samples -> Zähler = 0,089
Frame 12: Zähler = 44,188 -> sende 44 Samples -> Zähler = 0,188
...

Dieses Verfahren funktioniert auch, wenn sich der Feedback-Wert zu jedem 
beliebigen Zeitpunkt ändert.

> - Der kleinste Wert 0,0000 0000 01 (binär) entspricht dezimal 0,0009766
> --> Man kann also schonmal nicht genau einen Sample oder ein Hz
> anfordern. Unschön.

In der Praxis ist der Unterschied zwischen Device- und Host-Clock eh 
kein exaktes Vielfaches von 1 Hz.

von Markus G. (usound)


Lesenswert?

Clemens L. schrieb:
> Der Host-Treiber hat einen Zähler, der aussagt, wie viele Samples das
> Device haben will. In jedem Frame wird der FB-Wert auf den Zähler
> addiert, und die Anzahl der wirklich gesendeten Samples abgezogen.
>
> Beispiel (dezimal statt binär), mit Feedback = 44,099:
> Frame  1: Zähler = 44,099 -> sende 44 Samples -> Zähler = 0,099
> Frame  2: Zähler = 44,198 -> sende 44 Samples -> Zähler = 0,198
> Frame  3: Zähler = 44,297 -> sende 44 Samples -> Zähler = 0,297
> Frame  4: Zähler = 44,396 -> sende 44 Samples -> Zähler = 0,396
> Frame  5: Zähler = 44,495 -> sende 44 Samples -> Zähler = 0,495
> Frame  6: Zähler = 44,594 -> sende 44 Samples -> Zähler = 0,594
> Frame  7: Zähler = 44,693 -> sende 44 Samples -> Zähler = 0,693
> Frame  8: Zähler = 44,792 -> sende 44 Samples -> Zähler = 0,792
> Frame  9: Zähler = 44,891 -> sende 44 Samples -> Zähler = 0,891
> Frame 10: Zähler = 44,990 -> sende 44 Samples -> Zähler = 0,990
> Frame 11: Zähler = 45,089 -> sende 45 Samples -> Zähler = 0,089
> Frame 12: Zähler = 44,188 -> sende 44 Samples -> Zähler = 0,188
> ...
>
> Dieses Verfahren funktioniert auch, wenn sich der Feedback-Wert zu jedem
> beliebigen Zeitpunkt ändert.
Hallo Clemens,
super Information, besten Dank. Das heißt, dass zumindest einmal 
angeforderte mehr/weniger-Samples nicht verloren gehen.
Ist diese Methode irgendwo offiziell dokumentiert? Würde gern noch etwas 
stöbern, tue mich bei der Suche aber etwas schwer. Vermutlich ermangelt 
es mir am richtigen Suchbegriff.

Momentan stocke ich etwas beim Zeitpunkt, an dem ich die Zeiger von 
Einlesen und Ausgeben aus dem Ringbuffer vergleiche. Evtl muss ich meine 
DMA-Konfiguration noch etwas anpassen.

Gruß
Markus

von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> Ist diese Methode irgendwo offiziell dokumentiert?

Nein, aber offensichtlich so vorgesehen, wenn man schon mal mit 
Festkommazahlen gearbeitet hat.

von Markus G. (usound)


Lesenswert?

Hallo nochmal,

ich hänge gerade beim Problem, dass ich an den Zeiger, der im DMA-Block 
auf den aktuellen Sample zeigt, nicht rankomme.

Mein DMA-Modul ist folgendermaßen konfiguriert:
- Blocklänge = 2 Frames
- 4 Blocks, dann wird von vorne begonnen (Ringbuffer mode)
- Beatsize = 3 Byte, also ein Sample eines Kanals
- Getriggert wird die DMA-Transaktion sample und kanalweise vom 
I2S-Modul (bei 48 kHz, 2 Kanäle also mit 96 kHz).

Das ist zwar ziemlich komfortabel durch den automatischen Ringbuffer 
mode und die Übertragung, die direkt vom I2S-Modul angestoßen wird. 
Allerdings habe ich das Problem, dass ich nicht weiß bei welchem Sample 
in einem Block das DMA-Modul momentan steht. Somit bekomme ich keine 
Info darüber, wo der Index momentan tatsächlich (!) steht. Ich kann 
lediglich am Ende eine Blocks einen Interrupt auslösen. Nach jedem 
Sample wäre zwar praktisch, da ich einen Zähler mitzählen lassen könnte. 
Das ist nun aber auch nicht Sinn und Zweck eines DMAC (unnötige 
Rechenleistung).
Damit meine ich, dass ich natürlich Samples oder Bits anhand des Clocks 
des I2S-Moduls zählen kann. Damit kann ich berechnen, wo der Index 
stehen müsste.
Nach einiger Zeit läuft das aber durch Rundungsungenauigkeit, 
Messungenauigkeit etc. von der tatsächlichen Position weg und der Buffer 
Over/Underflowed trotzdem irgendwann.

@Hannes:
Wie hast du das denn implementiert dass du an den Ausgabepointer ran 
kommst im Betrieb? Machst du die I2S-Ausgabe irgendwie manuell oder 
getriggert von einem Counter?

Gruß
Markus

von Hannes M. (hannes_m)


Lesenswert?

Hallo Markus,

Ich lasse bei meiner Lösung (genau wie du) DMA im Circular-Mode laufen 
und prüfe bei den DMA half/full Interrupts ob der Abstand vom Schreib- 
zum Lesezeiger im Toleranzbereich liegt. Driftet er hier zu stark weg 
verändere ich die Samplerate minimal +/- um ihn wieder in den 
Optimalbereich zu verschieben. Funktioniert bei mir als Lösung so ganz 
gut - lediglich ergeben sich hier ab und zu Ungenauigkeiten, da durch 
den Clock Offset zwischen PC (USB Frame) und i2s  Interrupt Abweichungen 
auftreten können, und der nächste bereits erwartete USB Frame beim I2S 
Interrupt Zeitpunkt noch nicht eingetroffen ist und somit hier Daten 
fehlen. Dadurch kommt es dazu, dass der Offset ab und zu etwas springt.

Ich denke statt den i2s Interrupts den USB SOF Interrupt zur 
Offsetberechnung zu nutzen wäre sicherlich besser - vielleicht versuche 
ich auch noch einmal diese Methode zu implementieren...

Gruß
Hannes

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Hannes M. schrieb:

> Ich lasse bei meiner Lösung (genau wie du) DMA im Circular-Mode laufen
> und prüfe bei den DMA half/full Interrupts ob der Abstand vom Schreib-
> zum Lesezeiger im Toleranzbereich liegt.
Hallo Hannes,
genau dort liegt meine Frage. Wie machst du das genau? Sprich wie kommst 
du an die Position des Zeigers, der für die Ausgabe verantwortlich ist 
(im DMA-Modul) ran?
Dieser Zeiger scheint mir nicht zugänglich zu sein. Ich sage dem DMAC 
was er machen soll wenn der Trigger kommt (einen Beat senden) und wie 
groß ein Block ist. Die Indizierung im Block macht der DMAC 
selbstständig.

Machst du das per Interrupt des I2S-Moduls nach jedem Sample und erhöhst 
dabei einen Zähler?

Gruß
Markus

von Hannes M. (hannes_m)


Lesenswert?

Hallo Markus,

ich nutze als Zeigerersatz einfach den Interrupt -> Wenn der DMA Full 
Interrupt aufgerufen wird, hat der Lesezeiger vom DMA-Modul ja das Ende 
des Puffers erreicht. Also prüfe ich in dem Interrupt, wie weit der 
Schreibzeiger (USB-Daten) hier von der Mitte des Ringbuffers abweicht 
und speichere den Offset für den USB Audio-Sync Routine zwischen.
Das ganze mache ich jeweils beim "DMA Full <-> USB bei Half" sowie bei 
"DMA Half <-> USB bei Full".

Ich hoffe das hilft dir weiter.

Gruß
Hannes

von Markus G. (usound)


Lesenswert?

Hannes M. schrieb:
> Das ganze mache ich jeweils beim "DMA Full <-> USB bei Half" sowie bei
> "DMA Half <-> USB bei Full".

Hallo Hannes,

danke für deine Rückmeldung. Wenn ich mir das recht überlege, hast du 
mit deiner Methode aber auch keinen sauberen Zeitbezug oder?

Ich mache die Generierung des FB Werts immer im SOF Interrupt. Das 
ergibt einen sicheren zeitlichen Bezug. Deshalb hatte ich das Problem, 
an den tatsächlichen Index im DMA-Block nicht heranzukommen.

Ich habe meine Herangehensweise nochmals überdacht und bin nun dabei 
diverses zu ändern.
Ich verfahre so:
- Ich zähle mit einem Counter die Bitclocks z.B. eine Sekunde lang mit. 
Somit weiß ich, wie viele Bits in den letzten 1000 SOFs auf dem I2S 
rausgetaktet wurden. Der Counter ist free-running.
- Diesen Bitcounter vergleiche ich dann mit dem Sollwert. So erhalte ich 
rechnerisch (!) die Abweichung meines read-Index des FIFO.
- Diese Abweichung summiere ich solang, bis ein ganzer Sample 
zusammengekommen ist (24 Bit * Anzahl Kanäle). Daraufhin kann ich dann 
entsprechend Samples mehr/weniger vom Host anfordern, um die Abweichung 
auszugleichen.

Bei der Übertragung des FB-Werts werde ich zusätzlich eine andere 
Herangehensweise implementieren. Bisher bin ich immer hergegangen und 
habe z.B. wenn ich 44,1 kHz haben will, eine Sekunde lang diesen Wert 
auf dem FB übertragen. Nun ist es so, dass man mit 10.10 Format 44,1 
genauso wenig präzise anfordern kann wie 1 Hz.

44,1 (dec)
101100.0001100110 (bin - truncated auf .10)
44.099609375 (dec)

Das gibt also von vornherein schon Abweichungen. Nicht tragisch, im 
Dauerbetrieb aber trotzdem unschön. Ich hatte das weiter oben im Thread 
schon thematisiert.
Die neue Herangehensweise ist nun folgende: anstatt ich eine Sekunde 
lang z.B. 48,001 kHz anfordere, um einen Sample mehr zu bekommen, was 
ich nicht genau auflösen kann, fordere ich 999 Mal 48 kHz/Samples an und 
1 Mal 49 kHz.
Die Integerzahl kann problemlos exakt übertragen werden. Das Ergebnis, 
also die übertragene Gesamtanzahl der Samples sollte identisch sein. 
Allerdings habe ich die Abweichung bei der Übertragung auf dem Feedback 
schonmal ausgemerzt. Ich denke mir im Nachhinein, dass es seitens der 
USB-Spec eventuell auch so gedacht war. Ansonsten wäre das gewählte 
Format mit 10.10 eigentlich völliger Blödsinn. Andererseits hätte man 
dann die Nachkommastellen gleich weglassen können.

Ich werde nun mal eine Routine bauen, die mir entsprechend dem 
ermittelten FB die passende Anzahl an Samples für eine Sekunde 
anfordert. Um den Rechen- und Übertragungsaufwand zu verringern, kann 
man auch z.B. warten, bis man 4 Samples für eine Pollingrate von 4 ms 
zusammen hat.

Gruß
Markus

: Bearbeitet durch User
von Hannes M. (hannes_m)


Lesenswert?

Hallo Markus,

das ist so nicht ganz korrekt -> Ich erzeuge meinen Feedback Wert 
natürlich auch im SOF Interrupt. Generell funktioniert das ja auf die 
Art und Weise, wie wir es ja weiter oben schon besprochen hatten.
Mal abgesehen davon, dass sich 44.1 nicht perfekt darstellen lässt, 
sowie von den Übertragungsabweichungen der Treiber mal abgesehen, würde 
das ganze so - Theoretisch - auch ohne die Zeigerkontrolle funktionieren 
und Schreib- sowie Lesezeiger hätten immer den gleichen Abstand.

Wie wir ja nun aber leider schmerzlich feststellen musste, ist das ganze 
ja nicht soo genau, wie wir es in der Theorie erwartet hätten und 
dadurch entsteht ein minimaler Drift zwischen Schreib- und Lesezeiger.

Ich behandle das ganze so, dass ich wie in meiner letzten Nachricht ja 
schon beschrieben, in den DMA Interrupts - also 2 mal pro 
Bufferdurchlauf - die Position von Schreib- zu Lesezeiger überprüfe. 
Stelle ich hier nun eine Abweichung größer X fest, dann lege ich - je 
nach Größe und Richtung der Abweichung - einen Offset Wert fest, den ich 
dann in den SOF-Interrupts mit auf den Feedback Wert aufrechne (ist nur 
ein minimaler Offset, der ja auch nur dafür gedacht ist, den Abstand von 
Schreib- zu Lesezeiger zu korrigieren).

Das ganze Funktioniert auch recht gut - bis auf die kleinen Probleme, 
die ich oben ja schon beschrieben hatte.

Ich verstehe allerdings nicht, warum die Übertragung im 10.10 Format 
keinen Sinn ergeben soll?! Gerade die Nachkommastellen bieten dir doch 
die Möglichkeit im Dauerbetrieb den korrekten Datenstrom von der Quelle 
zu erhalten?!

Gruß
Hannes

von Markus G. (usound)


Angehängte Dateien:

Lesenswert?

Hannes M. schrieb:
> Ich behandle das ganze so, dass ich wie in meiner letzten Nachricht ja
> schon beschrieben, in den DMA Interrupts - also 2 mal pro
> Bufferdurchlauf - die Position von Schreib- zu Lesezeiger überprüfe.
> Stelle ich hier nun eine Abweichung größer X fest
Hallo Hannes,
Bitte korrigiere mich, wenn ich falsch liege, aber der Schreibzeiger 
(der Index der Daten die in den FIFO geschrieben werden/vom USB kommen) 
ist nicht zeitlich an den Lesezeiger gekoppelt, da die Daten vom USB auf 
einen Schlag in den FIFO geschrieben werden und nicht sauber über einen 
Frame verteilt. Dementsprechend entsteht so kein vergleichbarer 
Zeitbezug von Lese- und Schreibzeiger.

> Ich verstehe allerdings nicht, warum die Übertragung im 10.10 Format
> keinen Sinn ergeben soll?! Gerade die Nachkommastellen bieten dir doch
> die Möglichkeit im Dauerbetrieb den korrekten Datenstrom von der Quelle
> zu erhalten?!
Näherungsweise stimmt das ja auch. Aber wenn ich einen Sample mehr 
anfordere mit 48,001 als Feedback Wert, dann bekomme ich nur 0,97 
Samples durch die Begrenzung der Nachkommastellen. Was ich sagen will 
ist, dass da unnötig eine Abweichung integriert wird. Klar, der Drift 
der darauf entsteht ist relativ gering, aber dennoch unschön.

Ich habe noch eine Frage zum Intervall/der Übertragung des FB-Werts. 
Wenn ich mir das mit Wireshark anschaue, stelle ich 2 Sachen fest (siehe 
angehängtes Bild):

1.) Frame 585: In Wireshark werden immer 10 Audioframes zusammengefasst 
dargestellt (10 * 288 Byte + Klimbim = 3039 Byte). Ich vermute das ist 
normal.

2.) Der Host erhält den FB-Wert immer abwechselnd einmal korrekt (Frame 
586) und einmal nicht (Frame 587-588). In Wireshark sieht man das an der 
übertragenen Datenmenge. Wenn der FB-Wert korrekt kommt, werden 3 Byte 
übertragen (exemplarisch 00000C, was korrekt ist). Die Gesamtdatenmenge 
beträgt dann 54 Byte. Die Anfrage des Host ist ebenfalls 51 Byte lang. 
Ich habe hier die Polling rate zum Test auf 1 ms gestellt, bei einer 
Erhöhung ist das Phänomen aber identisch.

@Hannes: Verhält sich das bei dir genauso? Könntest du mal einen 
Ausschnitt von Wireshark posten?

Ich vermute das könnte an der chronologischen Reihenfolge der 
Abarbeitung in meinem Code liegen, dass dann eine Übertragung des 
FB-Werts unter den Tisch fällt.

Gruß
Markus

von Markus G. (usound)


Lesenswert?

Hallo zusammen,

das Projekt ist noch aktiv und ich versuche es voranzutreiben.

In der Zwischenzeit habe ich eine Testoption implementiert, um die 
Genauigkeit des Zählers zu kontrollieren. Das ist im Grunde nur eine 
einfache Interpolation im FIFO. Sprich ich habe den Feedback Endpoint 
deaktiviert und mache eine rudimentäre Samplerate-Conversion.
Mit rudimentär meine ich:
zu viele Samples im Buffer -> Samples löschen,
zu wenig Samples im Buffer -> letzten Sample kopieren und so buffer 
auffüllen.

Das ist natürlich im Sinne der Audioqualität nicht zielführend, es dient 
aber nur dazu, um meinen Bitclock Zähler zu überprüfen.
Nachdem ich keinen Zugriff auf den tatsächlichen Wert des 
Read-Pointers/Ausgabepointers habe, muss ich mir den tatsächlichen 
Bitclock durch mitzählen ermitteln. Dafür habe ich einfach einen TC an 
den Bitclock gehängt und zähle da entsprechend mit.

Ergebnis des Tests war nun, dass der Buffer Over/Underrun trotzdem 
auftritt! Zwar nach ca. der dreifachen Zeit, wie ganz ohne Korrektur, 
aber die Verzerrungen treten trotzdem auf.
Ich habe dann verschiedenes optimiert: statt dem BCLK den MCLK (12,288 
MHz) gezählt, den Zählerwert nicht jeden Frame abgeholt und summiert, 
sondern eine ganze Sekunde einen 32 Bit Zähler laufen lassen und dann 
nur einmal den Wert abgeholt. Der Zähler ist zudem free-running, sprich 
ich prüfe, ob ein overrun aufgetreten ist und korrigiere entsprechend 
meinen Wert. Der Zählerwert wird immer konsistent am Anfang der 
SOF-Interrupt-Routine eingelesen.

Die Maßnahmen haben das Ergebnis aber nur marginal verbessert (ca. 30 
%).
Bei einer Buffergröße von 8 Frames tritt der Over/Underrun trotzdem noch 
nach ca 15 Min. auf.

Nun frage ich mich, wie kommt diese offensichtlich vorhandene 
Abweichung/der Drift zustande? Ist das Zählen per TC des I2S MCLK so 
unpräzise?

Gruß
Markus

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Hallo,

habe es nun hinbekommen auf den Read-Pointer Zugriff zu haben (Event des 
DMA via Eventsystem auf einen TC).
So funktioniert die einfache Interpolation schonmal (dauerhaft)!

Der Feedback hat als Basiswert den ermittelten tatsächlichen Clock des 
I2S-Moduls (als gleitender Mittelwert). Wenn die Abweichung des Buffer 
Level einen Schwellwert überschreitet, stelle ich mit einem zusätzlichen 
Offset nochmals eine Zeit lang nach (Ausgleich des Buffer Levels). 
Sprich ich mache das so wie Hannes es auch implementiert hat.
Das funktioniert soweit auch.

Ein Problem habe ich nun zusätzlich erkannt:
Der Feedback-Mechanismus funktioniert nach dem Hochfahren/Neustart des 
PCs wie gewünscht. Wenn man aber die Wiedergabe unterbricht, das 
Programm schließt etc. und dann später wieder neu startet funktioniert 
der Feedback-Mechanismus nicht mehr (sprich alt_setting geht auf 1, dann 
auf 0, dann wieder auf 1).
Ein Aus/Einschalten des uC bewirkt nichts. Nur beim Neustart des PCs 
wird der Feedback-Wert wieder angenommen.
Das äußert sich darin, dass immer nur die nominelle Anzahl an Samples 
gesendet wird.
Seitens des Controllers ist alles wie es sein sollte: Variablen werden 
zurückgesetzt, DMA wird neu gestartet, der Feedbackwert wird korrekt 
gesendet (getestet mit Wireshark), nur der Host scheint diesen 
Feedback-Wert zu ignorieren.
Woran kann das liegen?
Da scheint mir womöglich irgendeine Einstellung zu fehlen.

Gruß
Markus

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Hallo,

nachdem ich nun nochmal einen Schritt zurückgegangen bin, um die 
Besonderheiten der Videoklasse im Audiodevice ebenfalls zu 
berücksichtigen, läuft die USB-I2S-Bridge mit der einfachen 
Buffer-Interpolation nun stabil.

Jetzt werde ich mich wieder dem Feedback-Endpoint zuwenden. Ich habe 
zunächst noch diverse Bugs hinsichtlich der Grundfunktion ausgemerzt.
Nun bin ich an der Überarbeitung des Feedback-Mechanismus. Zunächst 
wollte ich wie einen Post zuvor beschrieben, den Basis-Offset anhand des 
Zählers auf dem MCLK machen und dann nur bei Bedarf etwas nachstellen.
Mir ist diese Methode eigentlich etwas zu aufwändig und unnötig 
unpräzise, zudem der Regelaufwand recht hoch.

Wie ich es eigentlich gerne angehen würde:
- nur die ausgegebenen Samples auf dem I2S über 10 Frames zählen (um 
44,1 kHz auch abdecken zu können)
- eingehende Samples vom USB über 10 Frames zählen

- einen Schwellwert/max. Bufferoffset definieren, momentan sind das 3 
Samples

- das Pollingintervall steht auf 2 ms (kleinst möglich)
- standardmäßig sende ich die nominelle Sampleanzahl pro Frame als 
Feedback (hier 48 für 48 kHz)

--> Wenn mein Bufferoffset größer/gleich dem max. Offset wird, fordere 
ich per Feedback einen Sample mehr oder weniger an (47 oder 49),
da das polling-Intervall auf 2 ms steht, bekomme ich somit entsprechend 
über 2 Frames einen Ausgleichssample, also 2 Samples.

Das scheint mir die einfachste und zielführendste Methode zu sein. Im 
Prinzip nach dem Motto "ich hab einen sample zu wenig, sende mir beim 
nächsten Frame einen mehr".
Einen ganzen Sample kann man zudem exakt per Feedback anfordern, eine 
fractional Samplerate hingegen nicht (bzw. die Abweichung liegt sowieso 
in der Größenordnung der Fb-Genauigkeit). Außerdem spare ich mir den 
ganzen float Rechenaufwand.

Ich habe das nun soweit implementiert. Es funktioniert aber eher mäßig 
(Over/Underflow kommt).
Die Frage, die sich mir stellt ist: wenn ich einen Feedback-Wert sende, 
nach wie vielen Frames wird dann tatsächlich dieser Wert berücksichtigt?
Irgendwo scheine ich bei dieser Methode einen Denkfehler zu haben, der 
die Regelung so mit dem Fb-Mechanismus von Windows nicht funktionieren 
lässt.

Gruß
Markus

: Bearbeitet durch User
von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> wenn ich einen Feedback-Wert sende, nach wie vielen Frames wird dann
> tatsächlich dieser Wert berücksichtigt?

Das ist nicht spezifiziert. Auch nicht, für wie viele der folgenden 
Frames er verwendet wird.

von Markus G. (usound)


Lesenswert?

Clemens L. schrieb:
> Markus G. schrieb:
>> wenn ich einen Feedback-Wert sende, nach wie vielen Frames wird dann
>> tatsächlich dieser Wert berücksichtigt?
>
> Das ist nicht spezifiziert. Auch nicht, für wie viele der folgenden
> Frames er verwendet wird.

Hallo Clemens,
Gibt es zu dem Feedback-Mechanismus keinerlei Dokumentation?
Wie machen das denn dann die professionellen Anbieter von Soundkarten?
Da werden die Ingenieure vermutlich auch nicht mit Try&Error auf gut 
Glück versuchen.

Ich war davon ausgegangen, dass das eingestellte Feedback-Intervall 
automatisch auch der Zeitraum der Gültigkeit eines übertragenen Wertes 
ist. Das würde zumindest Sinn machen.
Ggf. muss ich dann wirklich eine Regelung im klassischen Sinn 
implementieren (der Buffer-Level schwankt dann immer um den ideal 
Füllstand).

Meine bisherige Herangehensweise „es fehlt ein Sample im Buffer, sende 
mir im nächsten Frame einen mehr“ scheint nicht zu funktionieren. Es 
macht den Anschein, als würde der Host den mehr angeforderten Sample 
„vergessen“. Der geänderte Feedback-Wert wird übertragen, das prüfe ich 
explizit im Code ab.

Muss ich dann den Feedback-Wert anstatt nur ein Polling-Intervall lang, 
so lange senden, bis der Mehr/Weniger-sample tatsächlich übertragen 
wurde?
Evtl. wird der Feedback-Wert auf der Hostseite nochmal ausgemittelt?

Gruß
Markus

: Bearbeitet durch User
von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> Gibt es zu dem Feedback-Mechanismus keinerlei Dokumentation?

Es gibt die Spezifikation, die die Zusammenarbeit zwischen Host und 
Device regelt. Für beides gibt es viele Implementationen, die sich in 
Details unterscheiden.

> Ich war davon ausgegangen, dass das eingestellte Feedback-Intervall
> automatisch auch der Zeitraum der Gültigkeit eines übertragenen Wertes
> ist.

Der Feedback-Werte ist (theoretisch) unendlich lange gültig. Er ist 
deine beste Voraussage für die mittel- bis langfristige Datenrate, die 
du sehen willst.

> Ggf. muss ich dann wirklich eine Regelung im klassischen Sinn
> implementieren (der Buffer-Level schwankt dann immer um den ideal
> Füllstand).

Und Schwankungen (sowohl Buffer-Level als auch Feedback-Wert) sind kein 
Problem, solange Over-/Underflow vermeidet werden.

von Markus G. (usound)


Lesenswert?

Clemens L. schrieb:

> Der Feedback-Werte ist (theoretisch) unendlich lange gültig. Er ist
> deine beste Voraussage für die mittel- bis langfristige Datenrate, die
> du sehen willst.
>
> Und Schwankungen (sowohl Buffer-Level als auch Feedback-Wert) sind kein
> Problem, solange Over-/Underflow vermeidet werden.

Hallo Clemens,

okay, dann habe ich folgendes Vorgehen im Kopf:
- ich starte mit dem Feedback-Wert 48
- ich messe wie viele samples per I2S rausgesendet werden (das 
Messintervall von 10 auf 100 Frames/ms zu vergrößern, macht dann 
vermutlich Sinn?)
- entsprechend der Messung bilde ich einen gleitenden Mittelwert und 
sende den als Feedback
- ich definiere eine maximale Bufferabweichung: z.B. 10 Samples
- wenn die max. Abweichung erreicht ist, addiere ich zu meinem Feedback 
einen festen Offset (z.B. 0,00001 kHz = 0,01 Hz schneller (die gemessene 
mittlere Abweichung lag in der Größenordnung von 0,06 Hz)), das könnte 
man je nach Abweichung im Bufferlevel auch in 2 oder mehr Stufen tun
- wenn im darauffolgenden Frame die Bufferabweichung kleiner geworden 
ist, belasse ich den Wert wie er ist,
  ist die Abweichung hingegen größer geworden, addiere ich nochmals den 
Offset
- entsprechend wird auch in die andere Richtung verfahren

Macht das so Sinn?

Gruß
Markus

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Markus G. schrieb:
> Macht das so Sinn?

Nein.
Der Feedbackwert gibt die Anzahl der Samples pro Paket (mit 
Nachkommastellen) an. Das gilt für das gesamte Interval zwischen zwei 
Feedback Paketen.
Langfristig stimmt die Summe aus Feedbackwert multipliziert mit der 
Anzahl der Audio-Pakete zwischen zwei Feedback Paketen mit der Anzahl an 
Samples, die der Host erwartet und rausschickt genau überein.
Irgendwelche Mittelwertbildungen oder sonstige Tricksereien führen 
zwangsläufig zu Fehlern (Feedback-Summe stimmt nicht mit Sample-Summe 
überein).
Ein Missverständnis ist, dass der Host unmittelbar auf eine große 
Feedback-Änderung reagieren muss. Das passiert nicht. Damit die 
Audio-Geschwindigkeit nicht stark schwank, nehmen die Treiber natürlich 
auch eine sehr langsame Anpassung der Geschwindigkeit vor, stellen dabei 
aber trotzdem sicher, dass die Sampleanzahl langfristig ganz genau dem 
(langfristig gemittelten) Feedbackwert entspricht.
Es ist sehr schwierig, den Feedbackwert über Samples zu ermitteln, denn 
um eine hohe Genauigkeit zu erreichen müsste man ca. 1 Sekunde lang 
Samples zählen. Besser ist es die Masterclock oder Bitclock auszuwerten.

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

"Simple" Lösung: du stellst die Pollingrate für den Sync Endpoint auf 
die max. zulässige Zeit (512 ms) und summierst die Samples (für einen 
Audio-Kanal) über 512 SOFs in einem Counter auf.
Diesen Wert shiftest du 5 Bits nach links, das ist der Feedbackwert im 
Q10.14 Format. Dann Counter resetten und wieder die Samples über die 
nächsten 512 SOFs zählen usw...

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Hallo,
Joe F. schrieb:
> Ein Missverständnis ist, dass der Host unmittelbar auf eine große
> Feedback-Änderung reagieren muss. Das passiert nicht.

Ok dann lag dort mein Irrtum.

> Es ist sehr schwierig, den Feedbackwert über Samples zu ermitteln, denn
> um eine hohe Genauigkeit zu erreichen müsste man ca. 1 Sekunde lang
> Samples zählen. Besser ist es die Masterclock oder Bitclock auszuwerten.

Das macht natürlich Sinn. Einen Counter auf dem Bitclock hatte ich auch 
schon implementiert und so die "tatsächliche" Samplerate ermittelt und 
als Basiswert für den Feedback genommen.
Die ermittelte Abweichung lag im Bereich der max. Auflösung des 
Feedback-Werts. Dort entsteht dann hauptsächlich der Offset der 
Buffer-Indizes.
Die maximale Auflösung im 10.14 Format ist 0,061 Hz.

Also mache ich es vom Vorgehen nun so:
- Bitclock oder Masterclock wird gezählt und ein gleitender Mittelwert 
gebildet. Dieser dient als "Basis" für den Feedback-Wert.
- zusätzlich zähle ich die Samples die ankommen und die die 
rausgeschickt werden.
So ermittle ich einen langsamen Drift im Buffer (Schreib- und 
Lesezeiger).


Wie handhabe ich nun den Ausgleich eines Drifts im Buffer?
Ich muss dann ja einen fixen Offset auf den Feedback-Wert addieren, um 
den Buffer State wieder auszuregeln. In welcher Größenordnung macht 
dieser Offset Sinn?

Gruß
Markus

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Markus G. schrieb:
> Also mache ich es vom Vorgehen nun so:
> - Bitclock oder Masterclock wird gezählt und ein gleitender Mittelwert
> gebildet. Dieser dient als "Basis" für den Feedback-Wert.

Nein, kein gleitender Mittelwert. Nur zählen.
Das Mitteln geschieht automatisch, da eine ausreichend grosse Anzahl an 
Samples bzw. Clockticks innerhalb der Feedback-Intervalls aufsummiert 
wird.

> - zusätzlich zähle ich die Samples die ankommen und die die
> rausgeschickt werden.

Warum? Masterclock oder Bitclock reicht doch.

> So ermittle ich einen langsamen Drift im Buffer (Schreib- und
> Lesezeiger).

Wenn der Feedbackwert stimmt gibt es keinen Drift, denn der Host passt 
seine Samplerate genau an die des Gerätes an. Das ist ja der Sinn des 
ganzen.

> Wie handhabe ich nun den Ausgleich eines Drifts im Buffer?

Es wird keinen Drift geben.

> Ich muss dann ja einen fixen Offset auf den Feedback-Wert addieren, um
> den Buffer State wieder auszuregeln. In welcher Größenordnung macht
> dieser Offset Sinn?

Du hast es offenbar nicht verstanden.

Der Feedback-Wert ist stumpf die Anzahl an Samples, die pro Paket 
erwartet werden. Nicht mehr, nicht weniger.
Und zwar als Fixpoint-Zahl mit einer Genauigkeit von ca. +/-1 
Sample/Sekunde.
Um diese Anzahl zu ermitteln, musst du lediglich die USB SOFs als 
Zeitgeber nehmen und über eine (sinnvoll ausgewählte) Zeitspanne die 
Samples deines ADCs/DACs zählen. So einfach.
Die Regelung übernimmt komplett der Host.

Versuche es mal so, wie ich es dir beschrieben habe (ganze Samples über 
512 SOFs) und du wirst sehen, dass es funktioniert.

Wenn du das am Laufen hast, kannst du auf Zählen der Bitclock oder 
Masterclock "umbauen". Dabei verkürzt sich lediglich die Intervalllänge, 
die zum Ermitteln der genauen Samplerate nötig ist.

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Markus G. schrieb:
>> Also mache ich es vom Vorgehen nun so:
>> - Bitclock oder Masterclock wird gezählt und ein gleitender Mittelwert
>> gebildet. Dieser dient als "Basis" für den Feedback-Wert.
>
> Nein, kein gleitender Mittelwert. Nur zählen.

Wenn ich es mir recht überlege, spielt es im Grunde keine Rolle. Die 
entstehende Differenz muss ohnehin anderweitig ausgeregelt werden.

>> - zusätzlich zähle ich die Samples die ankommen und die die
>> rausgeschickt werden.
>
> Warum? Masterclock oder Bitclock reicht doch.

> Wenn der Feedbackwert stimmt gibt es keinen Drift, denn der Host passt
> seine Samplerate genau an die des Gerätes an. Das ist ja der Sinn des
> ganzen.
>
>> Wie handhabe ich nun den Ausgleich eines Drifts im Buffer?
>
> Es wird keinen Drift geben.

Da muss ich dir widersprechen. Es gibt allein durch die Ungenauigkeit 
des FB-Werts durch das 10.14 Format schon eine Abweichung.
Deine Aussage wäre korrekt, wenn man den Feedback unendlich genau 
übermitteln könnte!
Entsprechend laufen die Buffer-Indizes über einen längeren Zeitraum 
auseinander und es kommt zum Over/Underflow.

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Markus G. schrieb:
> Joe F. schrieb:

>> Es wird keinen Drift geben.
>
> Da muss ich dir widersprechen. Es gibt allein durch die Ungenauigkeit
> des FB-Werts durch das 10.14 Format schon eine Abweichung.
> Deine Aussage wäre korrekt, wenn man den Feedback unendlich genau
> übermitteln könnte!
> Entsprechend laufen die Buffer-Indizes über einen längeren Zeitraum
> auseinander und es kommt zum Over/Underflow.

Irrtum. Ein fehlendes Sample wirkt sich minimal auf den darauf folgenden 
Feedback-Wert aus, und so bleibt das auf alle Zeiten synchron. 10.14 
wurde nicht zufällig gewählt, es reicht aus um die Abweichung von nur 
einem einzelnen Sample zu repräsentieren.

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Vielleicht nochmal zum besseren Verständnis:
Die Feedback-Information ist im Grunde ein Sample-Counter.

Beim implicit feedback Mechanismus hat der Treiber die Information, wie 
viele Samples pro Paket vom Gerät kommen über den IN endpoint.
Er passt dann die Geschwindigkeit bzw. Datenrate auf dem OUT endpoint an 
genau diese Sample-Anzahl an.

Und ganz genau so funktioniert es mit dem SYNC endpoint, nur dass das 
Feedback-Paket eben nicht alle ms sondern seltener kommt, und 
übermittelt wird jeweils die Summe aller Samples im zurückliegenden 
Intervall.

Der Host bekommt also bei beiden Synchronisations-Verfahren die gleiche 
Information: wie viele Samples erwartet/sendet das Gerät pro Paket.

Da die Intervalllänge für SYNC unterschiedlich sein kann (2ms - 512ms), 
hat man sich entschlossen, diesen Counter-Wert zu normieren, nämlich auf 
Q14.10.
Das hat in der Regel zur Folge, dass die unteren Bits "0" bleiben, die 
Genauigkeit ist also höher als benötigt.
An meinem Vorschlag (über 512 SOFs zu summieren) siehst du auch, dass 
der Counter genau das 512-fache des Sample-pro-Paket Wertes erreicht, 
und man diesen Wert daher nochmal um ganze 5 Bits nach links shiften 
muss, um in ins Q10.14 Format zu bringen.
Wenn man die Bitclock zählen würde, ergäbe sich genau der 64-fache Wert 
ggü. der Zählung der Samples = 5 Bits mehr, kein Bitshift mehr nötig... 
so ein Zufall... ;-)

Das schöne an der ganzen Sache: das Device kann strohdumm sein (wichtig 
für günstige Hardware), es muss selbst nichts regeln oder rechnen, es 
muss nur richtig zählen können und die Regelung übernimmt komplett der 
Treiber. Er sorgt dafür, dass die Buffer im Gleichgewicht bleiben.

: Bearbeitet durch User
von Markus G. (usound)


Angehängte Dateien:

Lesenswert?

Hallo Joe,

danke für die ausführlichere Beschreibung der Thematik. Das hat nochmal 
etwas Licht ins Dunkel gebracht.

Habe das nun zum Test so implementiert:
Joe F. schrieb:
> "Simple" Lösung: du stellst die Pollingrate für den Sync Endpoint auf
> die max. zulässige Zeit (512 ms) und summierst die Samples (für einen
> Audio-Kanal) über 512 SOFs in einem Counter auf.
> Diesen Wert shiftest du 5 Bits nach links, das ist der Feedbackwert im
> Q10.14 Format. Dann Counter resetten und wieder die Samples über die
> nächsten 512 SOFs zählen usw...

Mein Zähler hängt am DMA-Event und zählt somit jeden einzelnen Sample 
(96 bei 2 Kanälen). Es ergibt sich folglich ein Count von 49152 über 512 
Frames. Ich muss also nur um 4 Bit schieben.

Ich habe nun auch die Berechnungsweise nochmal neu nachvollzogen und 
verstanden.
Durch das Schieben kann es auch nicht zu Werten kommen, die nicht exakt 
abgebildet werden können (zumindest wenn man immer über ein Vielfaches 
von 2 zählt).

49152 = 2^10 * 48
--> schieben um weitere 4 Stellen für 10.14 Format, die restlichen Bits 
sind Nullen.

Das Ganze liefert auch plausible Werte: im Bild im Anhang sind die 
ersten 25 Zählintervalle.
Ist der Zählerwert 49153 (ein "halber" Sample Abweichung bei 2 Kanälen), 
entspricht das
49153/1024 = 48,0009765625.
Der übertragene FB-Wert ist dann 10-00-0C (ausgelesen mit Wireshark) was 
48,0009765625 entspricht.
Vorgehensweise und Implementierung passen also.

Soweit zur Theorie. Buffer Over/Underflow kommt nach ca 4-5 min :D
Wo kommt nun dieser Drift her?

Gruß
Markus

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

2 Kanäle sind doch Stereo, wie kann da eine ungerade Sample-Anzahl (in 
Summe) zustande kommen?
Deine USB Pakete enthalten ja hoffentlich auch immer gleich viele 
Samples für L und R.

Und: wenn du einen Mac hast, und das Audiosystem mal mit einem Gerät 
verwirrt hast, das Bufferprobleme erzeugt, dann hilft meist nur ein 
Reboot um alles wieder ins Lot zu bekommen.

Und: wieviele Samples hast du als pre-buffering vorgesehen?

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> 2 Kanäle sind doch Stereo, wie kann da eine ungerade Sample-Anzahl
> (in Summe) zustande kommen?

Ich zähle jeden einzelnen DMA-Beat (geht nicht anders). Das ist jeweils 
ein Abtastwert, sprich ein Abtastwert eines Kanals.
Bei 48 kHz zähle ich folglich 96 Counts pro Frame bei stereo.

Getestet wird unter Windows.

Edit:
Over/Underflow kommt nach ca. 2,5 min.
Habe ich da noch einen neuen Denkfehler produziert?
Buffer ist für 4 Frames ausgelegt. Entsprechend buffere ich 2 Frames und 
starte dann im SOF vom dritten Frame die I2S-Ausgabe.

Gruß
Markus

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Achso, verstehe, dein DMA Handler wird 2x aufgerufen, einmal fürs linke 
einmal fürs rechte Sample.
Das ist gut, damit erhöhst du die Genauigkeit von 2 auf 1 
Sample/Sekunde.

Ein oft gemachter Fehler ist, den Counter z.B. in deiner DMA Routine zu 
erhöhen, und dann woanders (z.B. im SOF Interrupt) zu resetten. Das kann 
zu einer Racecondition führen.
Besser ist im DMA Handler den Counter zu erhöhen, und im SOF Interrupt 
die Differenz zu einem Sample-Count-Buffer zu nehmen anstatt den Counter 
zurückzusetzen.
1
uint32_t sample_cnt = 0;
2
uint32_t feedback_value = 0x000c0000; // good start value for 48 KHz
3
4
dma_interrupt()
5
{
6
  sample_cnt++;
7
}
8
9
sof_interrupt()
10
{
11
  uint32_t sample_cnt_delta;
12
  static uint32_t sample_cnt_buf = 0;
13
  static uint16_t sof_cnt = 0;
14
15
  sample_cnt_delta = sample_cnt - sample_cnt_buf;
16
  sof_cnt++;
17
  if (sof_cnt >= 512)  
18
  {
19
    feedback_value = sample_cnt_delta << 4;
20
    sample_cnt_buf = sample_cnt;
21
    sof_cnt = 0;
22
  }
23
}
24
25
sync_endpoint_handler() // make sure polling interval is set to 512 ms
26
{
27
  // send out current "feedback_value"
28
}

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Achso, verstehe, dein DMA Handler wird 2x aufgerufen, einmal fürs
> linke
> einmal fürs rechte Sample.

Genau so ist es.

> Ein oft gemachter Fehler ist, den Counter z.B. in deiner DMA Routine zu
> erhöhen, und dann woanders (z.B. im SOF Interrupt) zu resetten. Das kann
> zu einer Racecondition führen.
> Besser ist im DMA Handler den Counter zu erhöhen, und im SOF Interrupt
> die Differenz zu einem Sample-Count-Buffer zu nehmen anstatt den Counter
> zurückzusetzen.

Habe den Counter extra free-running implementiert.
Sprich wenn ich im SOF-Interrupt den Zähler auswerte, prüfe ich erst, ob 
ein Overflow auftreten ist und verrechne das dann entsprechend.

In meiner SOF-Routine mache ich das im Prinzip genau so wie in deinem 
Code.

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Markus G. schrieb:
> Sprich wenn ich im SOF-Interrupt den Zähler auswerte, prüfe ich erst, ob
> ein Overflow auftreten ist und verrechne das dann entsprechend.

Vielleicht liegt da ja der Fehler?
Bei der Differenzbildung zweier unsigned Werte muss keine 
Sonderbehandlung beim Overflow gemacht werden (vorausgesetzt es ist kein 
mehrfacher Overflow aufgetreten, was in diesem Fall nicht sein kann).

Generell: dein Feedback-Wert sieht plausibel aus.
Alles nahe 0x0c0000 ist korrekt.
Du kannst ja mal gucken, ob du irgendwo Aussreisser bekommst (deutlich 
zu groß, deutlich zu klein).

von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Vielleicht liegt da ja der Fehler?
> Bei der Differenzbildung zweier unsigned Werte muss keine
> Sonderbehandlung beim Overflow gemacht werden (vorausgesetzt es ist kein
> mehrfacher Overflow aufgetreten, was in diesem Fall nicht sein kann).

Da hatte ich mir noch garnicht genauer Gedanke zu gemacht, dass das bei 
unsigned keine Rolle spielt. Momentan ist das so implementiert, das 
müsste auch stimmen, da mein Testfall ohne Feedback, sondern mit 
einfacher Buffer-Interpolation funktioniert:
1
/* calc send samples over period of Tmeas from I2S sample counter */
2
if (tc_sample_count > tc_sample_count_last_frame)
3
{
4
  //counter hasn't overflowed
5
  as_buffer_level = tc_sample_count - tc_sample_count_last_frame;
6
}
7
else
8
{  //counter has overflowed
9
  as_buffer_level = tc_sample_count + 65536 - tc_sample_count_last_frame;
10
}

> Generell: dein Feedback-Wert sieht plausibel aus.
> Alles nahe 0x0c0000 ist korrekt.
> Du kannst ja mal gucken, ob du irgendwo Aussreisser bekommst (deutlich
> zu groß, deutlich zu klein).

Habe gerade noch auf Abweichungen größer 2 Counts geprüft, sprich 
counter > 49154 oder < 49150.
Dieser Fall tritt nicht auf.

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Markus G. schrieb:
> as_buffer_level = (tc_sample_count + 65536 -
> tc_sample_count_last_frame);

Von welchem Typ sind die Variablen jeweils?
Bei 32 Bit hättest du ein Problem... ;-)
Und bei 16 Bit sind die +65536 eh sinnlos, also bleibt:

as_buffer_level = tc_sample_count - tc_sample_count_last_frame;

Das funktioniert in beiden Fällen, egal ob overflow oder nicht.
Voraussetzung ist, dass alle 3 Variablen unsigned und gleich breit sind.

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Von welchem Typ sind die Variablen jeweils?
> Bei 32 Bit hättest du ein Problem... ;-)
> Und bei 16 Bit sind die +65536 eh sinnlos, also bleibt:

Die +65536 waren noch aus einer vorherigen Implementierung. 
Funktionierte zwar, habe das aber bei der Gelegenheit gleich auf 16 Bit 
umgebaut. Spart immerhin auch etwas Code.

Sieht nun in meinem SOF-Interrupt hauptsächlich so aus:
1
//count over T_MEAS frames
2
if (++as_sof_cnt == as_Tmeas)
3
{
4
  /* calc sent samples over period of Tmeas from I2S sample counter */
5
  fb_new_SR = tc_sample_count - tc_sample_count_last_frame;
6
7
  //save current sample count
8
  tc_sample_count_last_frame = tc_sample_count;
9
  
10
  //convert to 10.14 format (uint32)
11
  fb_new_SR_1014 = (fb_new_SR << 4);
12
  
13
  //put bytes in fb endpoint buffer
14
  PutBytesToBuf(fb_in_buf, fb_new_SR_1014);
15
  
16
  //reset frame counter
17
  as_sof_cnt = 0;
18
}

Ausreiser größer ein Samplepaar gibt es nicht, die ermittelten Werte 
sind auch plausibel (entweder 49152 oder 49153 in den ersten 100 
Messungen).
Der FB-Wert stimmt auch.
Trotzdem Buffer Over/Underflow nach ein paar Minuten.

Irgendwo muss nun im Betrieb noch ein Drift entstehen.

Gruß
Markus

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Tja. Jedenfalls hast du jetzt die Funktion des SYNC Endpoints (reiner 
Samplecounter) verstanden und lieferst korrekte Werte an den Treiber.

Es gibt noch eine Reihe weiterer Fehlerquellen, die in Betracht kommen, 
und dir wird wohl nichts anderes übrig bleiben als nicht nur die ersten 
100 Pakete, sondern den Traffic über mehrere Minuten intensiv zu 
analysieren.

Keine Ahnung welche Möglichkeiten dir Wireshark bietet, ich vermute mal 
man kann USB Pakete minutenlang loggen und dann offline auswerten.
Dann kannst du dich auf die Suche machen nach
- fehlenden Paketen (mehr als 1 SOF Abstand von Audio-Paket zu 
Audio-Paket)
- Audio-Pakete falscher Größe (zu viele, zu wenig Samples als erwartet)
- Doch irgendwo einen Ausreisser in der Feedback Information (nach 
mehreren Minuten)
- Falscher Abstand (!= 512 SOFs) von 2 Sync-Paketen
- Auch kannst du den Audiostream dazu verwenden die Bufferlevel zu 
debuggen (Bufferlevel als Audiosamples übertragen). Dann siehst du, ob 
es wirklich kontinuierlich drifted, oder eigentlich relativ lange stabil 
ist und irgendwo plötzlich ein Sprung auftritt.
- auch ist es möglich, dass der Wireshark Treiber Störungen 
verursacht...

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Dann kannst du dich auf die Suche machen nach
> - fehlenden Paketen (mehr als 1 SOF Abstand von Audio-Paket zu
> Audio-Paket)
> - Audio-Pakete falscher Größe (zu viele, zu wenig Samples als erwartet)
> - Doch irgendwo einen Ausreisser in der Feedback Information (nach
> mehreren Minuten)
> - Falscher Abstand (!= 512 SOFs) von 2 Sync-Paketen

Habe nun diverses Debugging gemacht. Das meiste lässt sich recht einfach 
im Debugger-Modus von Atmel Studio erledigen, bzw. entsprechende 
Haltepunkte setzen. Habe für die jeweiligen möglichen Problem if-cases 
eingebaut und dort dann einen Haltepunkt gesetzt, für den Fall, dass die 
Bedingung zutrifft. Das habe ich dann einfach laufen lassen bis der 
Over/Underrun kam.

Folgendes habe ich ausschließen können:
- ausbleibende Pakete
- Audio-Pakete falscher Größe
- Ausreißer in der Feedback Information
- Falscher Abstand

Was mir im Endeffekt aufgefallen ist, als ich nach falscher Größe der 
Audiopakete gesucht habe:
Der Host schickt immer nur max. 48 Samples. Eigentlich müsste in nahezu 
konstanten Abständen 49 Samples kommen.
Dem muss ich morgen nochmal genauer auf den Grund gehen. Im ersten 
Moment kann ich mir das allerdings nicht erklären. Der Endpoint 
(maxPacketSize) ist ausreichend groß, um auch mehr Samples übertragen zu 
können.

Gruß
Markus

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Häng mal deinen aktuellen Descriptor rein.

von Clemens L. (c_l)


Lesenswert?

Joe F. schrieb:
> Ein Missverständnis ist, dass der Host unmittelbar auf eine große
> Feedback-Änderung reagieren muss. Das passiert nicht. Damit die
> Audio-Geschwindigkeit nicht stark schwank, nehmen die Treiber natürlich
> auch eine sehr langsame Anpassung der Geschwindigkeit vor, stellen dabei
> aber trotzdem sicher, dass die Sampleanzahl langfristig ganz genau dem
> (langfristig gemittelten) Feedbackwert entspricht.

Das ist falsch.

Das Verhalten des Hosts ist in Abschnitt 5.12.4.2 der 
USB-2.0-Spezifikation  genau definiert:
> Each (micro)frame, an adaptive source adds F_f to any remaining
> fractional sample count from the previous (micro)frame, sources the
> number of samples in the integer part of the sum, and retains the
> fractional sample count for the next (micro)frame. The source can look
> at the behavior of F_f over many (micro)frames to determine an even
> more accurate rate, if it needs to.

Feedback-Werte mit großen Änderungen müssen also sofort angewendet 
werden.

Und die Hosts sind auch so implementiert; siehe z.B. 
snd_usb_handle_sync_urb() und snd_usb_endpoint_next_packet_size() in 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/sound/usb/endpoint.c

Das Gerät ist dafür verantwortlich, starke Schwankungen zu vermeiden:
> P should not be zero in order to keep the deviation in the number of
> samples sourced to less than 1 in the event of a lost F_f value.

... und Drift auszugleichen:
> It is possible that the source will deliver one too many or one too few
> samples over a long period due to errors or accumulated inaccuracies in
> measuring F_f . The sink must have sufficient buffer capability to
> accommodate this. When the sink recognizes this condition, it should
> adjust the reported F_f value to correct it. This may also be necessary
> to compensate for relative clock drifts. The implementation of this
> correction process is endpoint-specific and is not specified.

von Joe F. (easylife)


Lesenswert?

Clemens L. schrieb:
> Joe F. schrieb:
>> Ein Missverständnis ist, dass der Host unmittelbar auf eine große
>> Feedback-Änderung reagieren muss. (...)
>
> Das ist falsch.
>
> Das Verhalten des Hosts ist in Abschnitt 5.12.4.2 der
> USB-2.0-Spezifikation  genau definiert:
>> Each (micro)frame, an adaptive source adds F_f to any remaining
>> fractional sample count from the previous (micro)frame, sources the
>> number of samples in the integer part of the sum, and retains the
>> fractional sample count for the next (micro)frame.
>
> Feedback-Werte mit großen Änderungen müssen also sofort angewendet
> werden.

Ja, der geänderte Feedback-Wert geht natürlich sofort in die 
Berechnungen des Hosts ein. Was ich meinte ist, dass der Feedback-Wert 
keine "Anforderung einer bestimmten Paketgröße ist".

Beispiel

so sieht es nicht aus:
FB:  40 40 40 50 40 40 40 40 40 ...
OUT: 40 40 40 40 40 50 40 40 40 ...

sondern eher so:
FB:  40 40 40 50 40 40 40 40 40 ...
OUT: 40 40 40 40 40 41 41 41 41 ...
D.h. der Host reagiert auf einen geänderten FB-Wert sehr moderat, um 
hörbare Frequenzschwankungen zu verhindern.
Dass der Host die genaue Samplerate über einen längeren Zeitraum 
ermittelt ergibt sich auch aus:

>> The source can look
>> at the behavior of F_f over many (micro)frames to determine an even
>> more accurate rate, if it needs to.

> Das Gerät ist dafür verantwortlich, starke Schwankungen zu vermeiden:
>> P should not be zero in order to keep the deviation in the number of
>> samples sourced to less than 1 in the event of a lost F_f value.

P>0 meint, dass für die Berechnung des Feedback-Wertes möglichst eine 
Clock genommen werden soll, die ein Vielfaches der Samplerate ist, das 
wurde weiter oben ja schon festgestellt.
Da der DMA callback beim PO ja mit FS*2 aufgerufen wird, ist dieses 
Kriterium erfüllt, und die Abweichung kann auf 1 Sample/s genau 
berechnet werden.

> ... und Drift auszugleichen:
>> It is possible that the source will deliver one too many or one too few
>> samples over a long period due to errors or accumulated inaccuracies in
>> measuring F_f . The sink must have sufficient buffer capability to
>> accommodate this. When the sink recognizes this condition, it should
>> adjust the reported F_f value to correct it. This may also be necessary
>> to compensate for relative clock drifts. The implementation of this
>> correction process is endpoint-specific and is not specified.

Das sind 2 Dinge.
Warum man "relative clock drifts" noch zusätzlich kompensieren sollte 
erschließt sich mir nicht, denn genau dies geschieht bereits durch die 
Berechnung des FB Wertes über den Vergleich von SOF rate zu 
Masterclock/Bitclock rate.
Den 1. Punkt ("errors or accumulated inaccuracies") habe ich in der 
Praxis auch im Betrieb über 24h noch nie beobachtet, aber gut, man kann 
sich darauf vorbereiten.
Die Korrektur sollte aber dann eher im Bereich < 1 Sample/Sekunde 
geschehen (da dieser Fehler ja auch im Vergleich sehr, sehr klein ist).

Das Problem im Moment ist aber wohl, dass der Host beim PO nicht auf den 
SYNC endpoint reagiert.
Das muss natürlich zuerst mal gefixed werden.

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Häng mal deinen aktuellen Descriptor rein.
1
[Port2]  :  USB-Verbundgerät
2
3
4
Is Port User Connectable:         yes
5
Is Port Debug Capable:            no
6
Companion Port Number:            0
7
Companion Hub Symbolic Link Name: 
8
Protocols Supported:
9
 USB 1.1:                         yes
10
 USB 2.0:                         yes
11
 USB 3.0:                         no
12
13
Device Power State:               PowerDeviceD0
14
15
       ---===>Device Information<===---
16
English product name: "USB2I2S-Bridge"
17
18
ConnectionStatus:                  
19
Current Config Value:              0x01  -> Device Bus Speed: Full (is not SuperSpeed or higher capable)
20
Device Address:                    0x0A
21
Open Pipes:                           0
22
*!*ERROR:  No open pipes!
23
24
          ===>Device Descriptor<===
25
bLength:                           0x12
26
bDescriptorType:                   0x01
27
bcdUSB:                          0x0200
28
bDeviceClass:                      0x00  -> This is an Interface Class Defined Device
29
bDeviceSubClass:                   0x00
30
bDeviceProtocol:                   0x00
31
bMaxPacketSize0:                   0x40 = (64) Bytes
32
idVendor:                        0x03EB = Atmel Corporation
33
idProduct:                       0x2423
34
bcdDevice:                       0x0100
35
iManufacturer:                     0x01
36
     English (United States)  "Atmel"
37
iProduct:                          0x02
38
     English (United States)  "USB2I2S-Bridge"
39
iSerialNumber:                     0x03
40
     English (United States)  "v0.232"
41
bNumConfigurations:                0x01
42
43
       ---===>Full Configuration Descriptor<===---
44
45
          ===>Configuration Descriptor<===
46
bLength:                           0x09
47
bDescriptorType:                   0x02
48
wTotalLength:                    0x006D  -> Validated
49
bNumInterfaces:                    0x02
50
bConfigurationValue:               0x01
51
iConfiguration:                    0x00
52
bmAttributes:                      0xC0  -> Self Powered
53
MaxPower:                          0x00 =   0 mA
54
55
          ===>Interface Descriptor<===
56
bLength:                           0x09
57
bDescriptorType:                   0x04
58
bInterfaceNumber:                  0x00
59
bAlternateSetting:                 0x00
60
bNumEndpoints:                     0x00
61
bInterfaceClass:                   0x01  -> Audio Interface Class
62
bInterfaceSubClass:                0x01  -> Audio Control Interface SubClass
63
bInterfaceProtocol:                0x00
64
iInterface:                        0x00
65
66
          ===>Audio Control Interface Header Descriptor<===
67
bLength:                           0x09
68
bDescriptorType:                   0x24 (CS_INTERFACE)
69
bDescriptorSubtype:                0x01 (HEADER)
70
bcdADC:                          0x0100
71
wTotalLength:                    0x001E
72
bInCollection:                     0x01
73
baInterfaceNr[1]:                  0x01
74
75
          ===>Audio Control Input Terminal Descriptor<===
76
bLength:                           0x0C
77
bDescriptorType:                   0x24 (CS_INTERFACE)
78
bDescriptorSubtype:                0x02 (INPUT_TERMINAL)
79
bTerminalID:                       0x01
80
wTerminalType:                   0x0101 (USB streaming)
81
bAssocTerminal:                    0x00
82
bNrChannels:                       0x02
83
wChannelConfig:                  0x0003
84
                                 (Left Front (L))
85
                                 (Right Ront (R))
86
iChannelNames:                     0x00
87
iTerminal:                         0x00
88
89
          ===>Audio Control Output Terminal Descriptor<===
90
bLength:                           0x09
91
bDescriptorType:                   0x24 (CS_INTERFACE)
92
bDescriptorSubtype:                0x03 (OUTPUT_TERMINAL)
93
bTerminalID:                       0x02
94
wTerminalType:                   0x0301 (Speaker)
95
bAssocTerminal:                    0x00
96
bSourceID:                         0x01
97
iTerminal:                         0x00
98
99
          ===>Interface Descriptor<===
100
bLength:                           0x09
101
bDescriptorType:                   0x04
102
bInterfaceNumber:                  0x01
103
bAlternateSetting:                 0x00
104
bNumEndpoints:                     0x00
105
bInterfaceClass:                   0x01  -> Audio Interface Class
106
bInterfaceSubClass:                0x02  -> Audio Streaming Interface SubClass
107
bInterfaceProtocol:                0x00
108
iInterface:                        0x00
109
110
          ===>Interface Descriptor<===
111
bLength:                           0x09
112
bDescriptorType:                   0x04
113
bInterfaceNumber:                  0x01
114
bAlternateSetting:                 0x01
115
bNumEndpoints:                     0x02
116
bInterfaceClass:                   0x01  -> Audio Interface Class
117
bInterfaceSubClass:                0x02  -> Audio Streaming Interface SubClass
118
bInterfaceProtocol:                0x00
119
iInterface:                        0x00
120
121
          ===>Audio Streaming Class Specific Interface Descriptor<===
122
bLength:                           0x07
123
bDescriptorType:                   0x24 (CS_INTERFACE)
124
bDescriptorSubtype:                0x01 (AS_GENERAL)
125
bTerminalLink:                     0x01
126
bDelay:                            0x00
127
wFormatTag:                      0x0001 (PCM)
128
129
          ===>Audio Streaming Format Type Descriptor<===
130
bLength:                           0x0B
131
bDescriptorType:                   0x24 (CS_INTERFACE)
132
bDescriptorSubtype:                0x02 (FORMAT_TYPE)
133
bFormatType:                       0x01 (FORMAT_TYPE_I)
134
bNrChannels:                       0x02
135
bSubframeSize:                     0x03
136
bBitResolution:                    0x18 (24)
137
bSamFreqType:                      0x01 (Discrete)
138
tSamFreq[1]:                   0x00BB80 (48000 Hz)
139
140
          ===>Endpoint Descriptor<===
141
bLength:                           0x09
142
bDescriptorType:                   0x05
143
bEndpointAddress:                  0x02  -> Direction: OUT - EndpointID: 2
144
bmAttributes:                      0x05  -> Isochronous Transfer Type, Synchronization Type = Asynchronous, Usage Type = Data Endpoint
145
wMaxPacketSize:                  0x0138 = 0x138 bytes
146
wInterval:                       0x0001
147
bSyncAddress:                      0x82
148
149
          ===>Audio Streaming Class Specific Audio Data Endpoint Descriptor<===
150
bLength:                           0x07
151
bDescriptorType:                   0x25 (CS_ENDPOINT)
152
bDescriptorSubtype:                0x01 (EP_GENERAL)
153
bmAttributes:                      0x00
154
bLockDelayUnits:                   0x01 (Milliseconds)
155
wLockDelay:                      0x0000
156
157
          ===>Endpoint Descriptor<===
158
bLength:                           0x09
159
bDescriptorType:                   0x05
160
bEndpointAddress:                  0x82  -> Direction: IN - EndpointID: 2
161
bmAttributes:                      0x11  -> Isochronous Transfer Type, Synchronization Type = No Synchronization, Usage Type = Feedback Endpoint
162
wMaxPacketSize:                  0x0003 = 0x03 bytes
163
wInterval:                       0x0901
164
bSyncAddress:                      0x00

Müsste eigentlich soweit passen. Habe einfach mal pauschal 4 Samples pro 
Kanal extra Platz im Endpoint gelassen.

Gruß
Markus

von Clemens L. (c_l)


Lesenswert?

Joe F. schrieb:
> Was ich meinte ist, dass der Feedback-Wert
> keine "Anforderung einer bestimmten Paketgröße ist".

Doch. Wenn der Feedback-Wert xx.yy ist, dann ist die Paketgröße immer xx 
oder xx+1.

> Beispiel
>
> so sieht es nicht aus:
> FB:  40 40 40 50 40 40 40 40 40 ...
> OUT: 40 40 40 40 40 50 40 40 40 ...
>
> sondern eher so:
> FB:  40 40 40 50 40 40 40 40 40 ...
> OUT: 40 40 40 40 40 41 41 41 41 ...

Die Spezifikation verlangt zwingend das erste Verfahren, nämlich "adds 
F_f to any remaining fractional sample count ..., sources the number of 
samples in the integer part of the sum ...". Wie auch in 
Beitrag "Re: USB Audio Sync Endpoint Implementierung" beschrieben. Und 
genau so ist es auch in der Praxis implementiert.

> Dass der Host die genaue Samplerate über einen längeren Zeitraum
> ermittelt ergibt sich auch aus:
>
>>> The source can look at the behavior of F_f over many (micro)frames to
>>> determine an even more accurate rate, if it needs to.

Wenn der Feedback-Wert zwischen 40 und 41 pendelt, dann kann ein 
Durchschnitt helfen, um einen genaueren Wert zu ermitteln. Aber bei 
großen Sprüngen wie 40->50 helfen die vorherigen 4x-Werte nicht dabei, 
den genauen Wert zwischen 4F/50/51 zu bestimmen.

> Warum man "relative clock drifts" noch zusätzlich kompensieren sollte
> erschließt sich mir nicht

Es könnte sich auf verschiedene Clocks im Host beziehen. Auf jeden Fall 
kann man normalerweise nicht herausfinden, was genau die Fehlerquelle 
ist.

von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Das Problem im Moment ist aber wohl, dass der Host beim PO nicht auf den
> SYNC endpoint reagiert.
> Das muss natürlich zuerst mal gefixed werden.

Das ist fürs erste die Hauptbaustelle. Habe grade nochmals geprüft, aber 
das Device bekommt immer 288 Byte (48 Samples) pro Frame, obwohl ein 
anderer Feedback-Wert übertragen wird.

Ich habe nochmal die Spec hinsichtlich des Descriptors gecheckt, das 
müsste aber eigentlich alles passen.
Ich tue mich gerade etwas schwer, das Problem genauer einzukreisen. Habt 
ihr dahingehend Tipps?

Gruß
Markus

von Joe F. (easylife)


Lesenswert?

Spontan fallen mir nur 3 Dinge auf:

- die Endpoint-Nummern haben eine Lücke. 0x01 und 0x81 sind nicht 
benutzt, du solltest besser diese EP-Nummern nehmen statt 0x02 und 0x82

- bcdUSB: 0x0200
Ich bin mir nicht sicher, ob man für ein Audio 1.0 Gerät nicht auch hier 
0x0100 eintragen müsste, damit nicht der Audio 2.0 Treiber verwendet 
wird...?!

und

- bmAttributes: 0x11  -> Isochronous Transfer Type, Synchronization Type 
= No
das sollte glaube ich auf 0x01 stehen

D3..2: Synchronization type 00 = None
D1..0: Transfer type 01 = Isochronous

: Bearbeitet durch User
von Markus G. (usound)


Angehängte Dateien:

Lesenswert?

Joe F. schrieb:
> Spontan fallen mir nur 3 Dinge auf:
> - die Endpoint-Nummern haben eine Lücke. 0x01 und 0x81 sind nicht
> benutzt, du solltest besser diese EP-Nummern nehmen statt 0x02 und 0x82
Habe ich angepasst.

> - bcdUSB: 0x0200
> Ich bin mir nicht sicher, ob man für ein Audio 1.0 Gerät nicht auch hier
> 0x0100 eintragen müsste, damit nicht der Audio 2.0 Treiber verwendet
> wird...?!

Habe mal geschaut, welcher Treiber momentan geladen wird (siehe Bild). 
Es wird der UAC1 Treiber geladen.

> - bmAttributes: 0x11  -> Isochronous Transfer Type, Synchronization Type
> = No
> das sollte glaube ich auf 0x01 stehen
>
> D3..2: Synchronization type 00 = None
> D1..0: Transfer type 01 = Isochronous
Das stimmt. Die Bits 4 und 5 für den Usage Type gibt es erst in der 
UAC2.
Habe ich angepasst.

Die Implementierung wird richtiger, aber noch keine Verbesserung des 
Problems.

Gruß
Markus

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Ändere mal testweise den Seriennummernstring ("v0.232") auf irgendetwas 
anderes. Ich kann mich dunkel erinnern, dass Windows so ne Macke hat und 
sich die Konfiguration eines Gerätes "merkt" (zur Seriennummer), und 
wenn man dann an der Konfiguration etwas ändert hat man ein Problem. Und 
du hattest das Gerät ja schon mal ohne Sync-Endpoint am Rechner, 
vermutlich mit gleicher Seriennummer...

von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Ich kann mich dunkel erinnern, dass Windows so ne Macke hat und
> sich die Konfiguration eines Gerätes "merkt" (zur Seriennummer), und
> wenn man dann an der Konfiguration etwas ändert hat man ein Problem.

Ja dem ist so. Auf den Umstand bin ich bei Tests mit 44,1 kHz schon 
gestoßen.
Deswegen habe ich auch immer die Seriennummer und ggf. den Device-Name 
erhöht.

von Markus G. (usound)


Lesenswert?

Hallo,

bin derweil etwas festgefahren bei der Suche nach der Ursache, dass der 
FB-Wert nicht berücksichtigt wird.
Der Descriptor müsste stimmen, habe ich explizit nochmal geprüft.
Die FB-Werte werden vom Host auch korrekt angefordert und dann 
übertragen. Die Werte ansich sind auch plausibel (auch über längere 
Zeit).
Es kommen aber immer nur 48 samples pro Frame.

Hat jemand noch einen Tipp, woran das liegen könnte?

Gruß
Markus

von Markus G. (usound)


Lesenswert?

Bin derweil etwas weitergekommen.
Habe vom Zählen der Samples auf Zählen des Masterclock umgebaut. Das 
verbessert immerhin die Kontrolle über den Datenstrom, da öfter ein FB 
übertragen werden kann.

Nach der USB 2.0 Spec:
Tmeas = 2^K    mit K=10
--> 1024 Frames werden gemessen, um Genauigkeit 1 Sample zu erreichen.

F_mclk = Fs * 2^P
12288 kHz = 256 * 48 kHz = 2^8 * 48
--> P = 8

Tmeas = 2^(K-P) = 4 ms

Es muss also mindestens 4 Frames lang gemessen werden. Das passt bei 
12,288 MHz noch gut, da mit einem 16 Bit Counter machbar.
Habe das nun so implementiert. Am Descriptor habe ich nur von 
pollingrate 9 auf 2 umgestellt (von 512 auf 4 Frames). Sonst habe ich im 
Prinzip nichts geändert.

Auf wundersame Art und Weise liefert USB nun von 48 abweichende Werte 
(47 und 49 Samples pro Frame). Der Feedback scheint also nun vom Host 
auch angewendet zu werden. Die übertragenen Werte sind nach wie vor 
plausibel.
Die Laufzeit bis zum Over/Underflow hat sich ca. verdreifacht. Er kommt 
aber nach wie vor.

In der USB 2.0 Spec S. 75 steht dazu:
1
It is possible that the source will deliver one too many or one too few samples over a long period due to
2
errors or accumulated inaccuracies in measuring Ff. The sink must have sufficient buffer capability to
3
accommodate this. 
4
When the sink recognizes this condition, it should adjust the reported Ff value to correct
5
it. This may also be necessary to compensate for relative clock drifts. The implementation of this
6
correction process is endpoint-specific and is not specified.

Das heißt für mich:
Es gibt eine relativen Drift, Ungenauigkeiten bei der Messung etc.
Diesen muss man durch ein Tracking der Schreib/Lesepointer im Buffer 
manuell korrigieren. Sprich man muss einen Offset auf den FB-Wert 
addieren, um den Drift im Buffer auszugleichen.

Gruß
Markus

von Joe F. (easylife)


Lesenswert?

Markus G. schrieb:
> Auf wundersame Art und Weise liefert USB nun von 48 abweichende Werte
> (47 und 49 Samples pro Frame). Der Feedback scheint also nun vom Host
> auch angewendet zu werden. Die übertragenen Werte sind nach wie vor
> plausibel.

Das klingt gut.

Markus G. schrieb:
> Das heißt für mich:
> Es gibt eine relativen Drift, Ungenauigkeiten bei der Messung etc.
> Diesen muss man durch ein Tracking der Schreib/Lesepointer im Buffer
> manuell korrigieren. Sprich man muss einen Offset auf den FB-Wert
> addieren, um den Drift im Buffer auszugleichen.

Tja. Ich glaube da eher noch an einen Messfehler. Meine USB Audio 2.0 
Geräte machen keine Korrektur und laufen über 48h stabil durch, 
vermutlich auch länger, aber länger habe ich nie gemessen.
Der Buffer schwankt etwas, aber nie so weit, dass es zu Underruns oder 
Overflows kommt (allerdings mit Mac OSX).

Wenn du eine Korrektur einbauen willst ist das bestimmt keine schlechte 
Idee, nur muss die eben sehr minimal und langsam eingreifen. Also < 1 
Sample/s.

von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Tja. Ich glaube da eher noch an einen Messfehler. Meine USB Audio 2.0
> Geräte machen keine Korrektur und laufen über 48h stabil durch,
> vermutlich auch länger, aber länger habe ich nie gemessen.
> Der Buffer schwankt etwas, aber nie so weit, dass es zu Underruns oder
> Overflows kommt (allerdings mit Mac OSX).

Wo du das Thema Messfehler geschrieben hast, habe ich nochmal mit 
Wireshark die ankommenden Feedback Pakete beim Host geprüft.
Die Pakete sind zum Großteil plausibel:
Der Wert pendelt zwischen 00000c (48) und 10000c (48,000977), vereinzelt 
noch 20000c (48,001953). Das scheint in Ordnung.

Es gibt aber vereinzelt einen Ausreißer: f0ff0b, das entspricht 
46,999023.
Also eine Abweichung von 1 kHz. Und das auch noch in die 
entgegengesetzte Richtung wie die restlichen Werte.
Da scheint mir noch ein Problem zu liegen. Ich konnte aber noch nicht 
festmachen, woran das liegt. Die Zeitpunkte, zu denen dieser Wert 
auftaucht sind auch nicht äquidistant soweit ich das sehe.

Ich habe den Zähler momentan an den Clock der PLL gehängt. Die läuft mit 
49,152 MHz. Der Counter nutzt dann einen Teiler von 4. Kann dort evtl. 
eine Ungenauigkeit entstehen?

> Wenn du eine Korrektur einbauen willst ist das bestimmt keine schlechte
> Idee, nur muss die eben sehr minimal und langsam eingreifen. Also < 1
> Sample/s.

Das hätte immerhin den Vorteil, wenn der Zähler auf dem Mclk Unfug 
treibt, regelt es die Buffer-Korrektur mit aus.

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Markus G. schrieb:
> Es gibt aber vereinzelt einen Ausreißer: f0ff0b, das entspricht
> 46,999023

0x0bfff0 sind 47,999 KHz, passt also, kein Ausreisser.

von Markus G. (usound)


Angehängte Dateien:

Lesenswert?

Joe F. schrieb:
> Markus G. schrieb:
>> Es gibt aber vereinzelt einen Ausreißer: f0ff0b, das entspricht
>> 46,999023
>
> 0x0bfff0 sind 47,999 KHz, passt also, kein Ausreisser.

Du hast recht! Hatte einen Vorzeichenfehler in meinem 
Excel-Umrechnungstool in diesem Fall.
Anbei der Wertebereich des FB, der an den Host gesendet wird. Die Werte 
sind also plausibel, mit einer max. Abweichung von +- 3 Samples pro 
Sekunde.
Etwas genauer würde es wohl werden, wenn ich direkt den Clock der PLL 
zähle.

Dann werde ich jetzt noch den Samplezähler zusätzlich wieder 
implementieren, um ein Auge auf den Buffer-Level haben zu können.
Eine andere Fehlerquelle fällt mir momentan sonst nicht ein.

Gruß
Markus

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Etwas verwunderlich ist schon, warum der Wert um +/-3 schwankt. Ein 
Quarz kann nicht so ungenau sein, und der Wert sollte eigentlich immer 
zwischen nur 2 Werten hin- und her springen. Evtl. auch mal 3 Werte. 
Also +/-1.

von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Etwas verwunderlich ist schon, warum der Wert um +/-3 schwankt.
> Ein
> Quarz kann nicht so ungenau sein, und der Wert sollte eigentlich immer
> zwischen nur 2 Werten hin- und her springen. Evtl. auch mal 3 Werte.
> Also +/-1.

Habe nun direkt den PLL-clock von 49,152 MHz mit einem 32 Bit Counter 
gezählt.
Dabei sind 2 Erkenntnisse aufgetreten:
1. Die Laufzeit bis zum Bufferproblem ist jetzt ca. 2,5 mal so lang.
2. Die Schwankungen beim FB-Wert sind noch größer, teilweise bis knapp 
2,5 Hz.

Ich frage mich, ob der Controller damit klar kommt, wenn die PLL für I2S 
mit 49,152 MHz läuft, die CPU aber mit 48 MHz. Könnte mir vorstellen, 
dass es da zu Genauigkeitsproblemen bei der Auswertung des Zählers 
kommen kann.

Im Endeffekt scheint es so aber schon genauer zu sein.

Gruß
Markus

von Thomas Z. (usbman)


Lesenswert?

Braucht dein Codec fs512? Vielleicht kannst du die PLL auch auf fs256 
laufen lassen.
Dann hättest du mehr Abstand zu den 48MHz.

Thomas

von Markus G. (usound)


Lesenswert?

Thomas Z. schrieb:
> Braucht dein Codec fs512? Vielleicht kannst du die PLL auch auf
> fs256
> laufen lassen.
> Dann hättest du mehr Abstand zu den 48MHz.

Hallo,

bin nun bei der PLL-Frequenz von 1024fs auf 512fs runter gegangen 
(24,576 MHz).
Ich könnte auch auf 256*fs verringern. Ich denke das werde ich noch 
machen.

Mit der 512fs Einstellung lief das Device nun ca. 33 min bis zum 
Bufferproblem (ohne manuelle Korrektur).

Gruß
Markus

von Joe F. (easylife)


Lesenswert?

Wir nähern uns langsam dem Kern des Problems... ;-)

von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Wir nähern uns langsam dem Kern des Problems... ;-)

Sehe ich auch so. Ein Fortschritt ist immerhin spürbar :)

von Markus G. (usound)


Lesenswert?

Hallo,

ich befasse mich nun mit dem Thema Buffer-Level Korrektur. Überlege 
gerade, wie ich das am besten implementiere.
Ich denke, ab einem Schwellwert sollte die Korrektur eingreifen. Dies 
sollte sie aber auch abhängig von der Abweichung tun und nicht mit einem 
festen Wert.
Hannes hatte dazu hier schon was geschrieben:

Hannes M. schrieb:
> Zusätzlich benutze ich meinen Fehloffset vom Schreib zum Lesezeiger und
> nutze diesen direkt als Regelparameter mit einem Regelwert von
> 0.01Hz/Byte.
>
> Mit dieser Einstellung läuft mein Device gerade mit einem Jitter von
> etwa +/- 0.5Hz.

Wenn ich es mir recht überlege, muss man eigentlich hauptsächlich dafür 
sorgen, dass der Korrekturwert in jedem Fall größer ist, als der Drift 
(sonst driftet der Bufferlevel weiter), diesen dabei aber so klein wie 
möglich halten.
Dafür werde ich wohl mal einen Test machen müssen, mit welcher 
Geschwindigkeit der Bufferlevel wegläuft.

Gruß
Markus

von Markus G. (usound)


Lesenswert?

Hallo,

habe es nun so gemacht:
mit 10.14 Format ist minimal möglich:
0000 0000 00.0000 0000 0000 01
=
0,000 061035 Hz = 0,06 Hz

Wenn ich eine Bufferabweichung >= 5 Samples habe, addiere ich 1 für 
jeden Sample Abweichung zu meinem Feedback-Wert.
Der Startwert ist also bei 5 Samples = 5 * 0,06 Hz = 0,3 Hz.
Das ist dann ein sehr moderater Eingriff in den Feedbackwert.
Werde das nun so testen. Ggf. muss ich den Wert noch etwas skalieren, 
aber ich denke die Größenordnung passt.

Gruß
Markus

von Markus G. (usound)


Lesenswert?

Hallo zusammen,

die ersten Tests waren soweit erfolgreich. Ich habe den Schwellwert des 
Buffers zusätzlich mit LEDs visualisiert, um auch bei längeren 
Laufzeiten sehen zu können, ob der Bufferlevel wegläuft.
Bisher ist das Device sauber gelaufen. Längere Tests (> 4h) muss ich 
allerdings noch machen. Bin aber guter Dinge, dass es sich wenn dann nur 
noch um Feintuning handeln müsste.

An dieser Stelle besten Dank für die zahlreichen hilfreichen Antworten 
zum Thema!


Als nächstes, längerfristiges Ziel würde ich das Projekt dann gerne auf 
einen Atmel mit USB-Highspeed portieren. Habe aber noch keinen 
Kandidaten auserkoren.

Als erstes würde ich gerne UAC2 nutzen. Das hätte den Vorteil, dass ich 
32 Bit Audio übertragen könnte und somit bei der Verarbeitung der Daten 
nicht jeden 24 Bit Sample einzeln byteweise handhaben muss (24 Bit Daten 
kommen per USB an, der FIFO hat aber 32 Bit Speicherstellen). Das spart 
Code und Rechenleistung.
Was muss ich denn tun, um UAC2 zu verwenden? Reicht es dabei bcdUSB 
anzupassen, sodass automatisch der USB Audio 2 Treiber geladen wird oder 
gibt es Inkompatibilitäten zwischen den beiden USB Audio Klassen?

Beste Grüße
Markus

: Bearbeitet durch User
von Joe F. (easylife)


Lesenswert?

Deviceseitig gibt es durchaus Unterschiede.
z.B: 8000 statt 1000 Pakete/s und das Format auf dem Sync-Endpoint ist 
leicht verändert (4 Bytes statt 3).

von Thomas Z. (usbman)


Lesenswert?

nun ja ich habe zwar noch kein UAC2 Device gebaut aber so weit ich 
gelesen habe ist nun zwingend (wie für alle neuen Compound Devices) IAD 
erforderlich. Das ist jedenfalls so in USB Complete beschrieben. 
Zusätzlich gilt  natürlich sowieso dass UAC2 erst neuere Win 10 
Versionen Treiber für UAC2 eingebaut haben. W7 braucht custom Treiber. 
Die meisten Anbieter haben dazu die Treiber von Thesycon lizenziert.
Ich habe dazu ganz am Anfang des Threads auch was gepostet.

Die UAC2 Spec sollte Beispiel Deskriptoren haben.

Thomas

von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> gibt es Inkompatibilitäten zwischen den beiden USB Audio Klassen?

Die Deskriptoren sind ähnlich, aber natürlich nicht 100 % gleich.

Du solltest UAC2 nur verwenden, wenn du ein Feature wirklich benötigst. 
(Implicit Feedback wäre für dich interessant, wird aber von Windows 
nicht unterstützt.)

von Markus G. (usound)


Lesenswert?

Joe F. schrieb:
> Deviceseitig gibt es durchaus Unterschiede.
> z.B: 8000 statt 1000 Pakete/s und das Format auf dem Sync-Endpoint ist
> leicht verändert (4 Bytes statt 3).
Zunächst soll das noch mit USB Fullspeed laufen. Das unterschiedliche 
Feedbackformat ist mir bekannt. Wie weiter oben im Thread schon erkannt, 
kann bei UAC2 zudem der Endpoint des Feedback explizit als FB-Endpoint 
definiert werden.
Die Frage ist, mit welcher Einstellung im Deskriptor ich von UAC 1 auf 2 
„umstellen“ kann.

Thomas Z. schrieb:
> Zusätzlich gilt  natürlich sowieso dass UAC2 erst neuere Win 10
> Versionen Treiber für UAC2 eingebaut haben. W7 braucht custom Treiber.
Win7 ist für mich relativ unwichtig, habe nur noch Win10 Geräte. 
Alternativ wäre noch MacOS interessant. Somit dürfte das Treiberproblem 
auf Seite des Host umgangen werden, da Win10 und MacOS UAC2 
unterstützen. Oder?

Clemens L. schrieb:
> Markus G. schrieb:
>> gibt es Inkompatibilitäten zwischen den beiden USB Audio Klassen?
>
> Die Deskriptoren sind ähnlich, aber natürlich nicht 100 % gleich.
>
> Du solltest UAC2 nur verwenden, wenn du ein Feature wirklich benötigst.
> (Implicit Feedback wäre für dich interessant, wird aber von Windows
> nicht unterstützt.)
Ich meine gelesen zu haben, dass Windows auch implicit feedback 
unterstützt?!
Nunja, unbedingt brauchen momentan nicht wirklich. Auf längere Sicht 
wäre es natürlich interessant das Projekt auf USB Highspeed und UAC2 mit 
z.B. 8 Kanälen auszubauen (man muss sich ja auch irgendwie die Zeit 
vertreiben :D ). Ich dachte da wäre es am sinnigsten, erstmal auf der 
gleichen Hardware zu bleiben, auf UAC2 umzubauen und dann irgendwann das 
ganze Projekt/die Routinen im Ganzen auf die neue Hardware zu portieren.

Gruß
Markus

von Thomas Z. (usbman)


Lesenswert?

Ein paar Vorschläge:
Im Device descriptor wird class auf 0xFE gestellt subclass auf 0x02 und 
Protokoll auf 0x01 gestellt. (Compound Device IAD). Ich habe auf 
StackOverflow mal gelesen dass OSX das IAD noch nicht kann allerdings in 
anderem Zusammenhang.
Audio Class wird dann auf Interface Level definiert.
Der Config Descriptor bleibt gleich. Vor jedem Interface gibt es nun 
einen IAD Descriptor.
Interface0 beschreibt dein AC Control Interface mit der Topologie und 
controls die du exportierst. Dort ist der wesentliche Punkt dass Input 
und Output Pins nun einen Clock haben den du bedienen musst. (Set Get 
SampleRate)
Interface1 wird dein Streaming Interface.
Im Control Interface kann optional nun ein Interrupt EP eingebaut werden 
für Rückmeldungen  des devices.

Das wäre es auch schon im wesentlichen. Ich bin aber sicher dass ich 
noch ein paar Dinge vergessen habe. Ob so ein Device auch die usbaudio2 
Treiber unter fullspeed lädt musst du ausprobieren.

https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers#descriptors

Thomas

von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> Die Frage ist, mit welcher Einstellung im Deskriptor ich von UAC 1 auf 2
> „umstellen“ kann.

Keine; du musst alle Deskriptoren durch die entsprechenden 
UAC2-Deskriptoren ersetzen.

> Ich meine gelesen zu haben, dass Windows auch implicit feedback
> unterstützt?!

https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/usb-2-0-audio-drivers 
sagt:
| The driver does not support implicit feedback.

Dort steht auch, wie die Deskriptoren aussehen sollen.

von Thomas Z. (usbman)


Lesenswert?

noch eine Idee. Du hast doch ein UAC2 Gerät im Zugriff. Falls das Gerät 
keine Bendorf Implementation ist sollten da ein UAC2 kompatibler 
Deskriptor Satz eingebaut sein.

Ließ das mal aus und poste es hier. Das kannst du dann als Basis 
benutzen.

Thomas

von Markus G. (usound)


Lesenswert?

Thomas Z. schrieb:
> noch eine Idee. Du hast doch ein UAC2 Gerät im Zugriff. Falls das
> Gerät
> keine Bendorf Implementation ist sollten da ein UAC2 kompatibler
> Deskriptor Satz eingebaut sein.
>
> Ließ das mal aus und poste es hier. Das kannst du dann als Basis
> benutzen.

Ja, habe den miniDSP USBStreamer, den kann ich auslesen. Hier die 
Deskriptoren:
1
[Port1]  :  USB-Verbundgerät
2
3
Is Port User Connectable:         yes
4
Is Port Debug Capable:            no
5
Companion Port Number:            11
6
Companion Hub Symbolic Link Name: USB#ROOT_HUB30#4&34571c9a&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
7
Protocols Supported:
8
 USB 1.1:                         yes
9
 USB 2.0:                         yes
10
 USB 3.0:                         no
11
12
Device Power State:               PowerDeviceD0
13
14
       ---===>Device Information<===---
15
English product name: "USBStreamer"
16
17
ConnectionStatus:                  
18
Current Config Value:              0x01  -> Device Bus Speed: High (is not SuperSpeed or higher capable)
19
Device Address:                    0x0F
20
Open Pipes:                           0
21
*!*ERROR:  No open pipes!
22
23
          ===>Device Descriptor<===
24
bLength:                           0x12
25
bDescriptorType:                   0x01
26
bcdUSB:                          0x0200
27
bDeviceClass:                      0xEF  -> This is a Multi-interface Function Code Device
28
bDeviceSubClass:                   0x02  -> This is the Common Class Sub Class
29
bDeviceProtocol:                   0x01  -> This is the Interface Association Descriptor protocol
30
bMaxPacketSize0:                   0x40 = (64) Bytes
31
idVendor:                        0x2752 = miniDSP Ltd.
32
idProduct:                       0x0016
33
bcdDevice:                       0x06B3
34
iManufacturer:                     0x01
35
     English (United States)  "miniDSP"
36
iProduct:                          0x03
37
     English (United States)  "USBStreamer"
38
iSerialNumber:                     0x02
39
     English (United States)  "00001"
40
bNumConfigurations:                0x02
41
*!*CAUTION:    Most host controllers will only work with one configuration per speed
42
43
       ---===>Full Configuration Descriptor<===---
44
45
          ===>Configuration Descriptor<===
46
bLength:                           0x09
47
bDescriptorType:                   0x02
48
wTotalLength:                    0x017D  -> Validated
49
bNumInterfaces:                    0x04
50
bConfigurationValue:               0x01
51
iConfiguration:                    0x00
52
bmAttributes:                      0x80  -> Bus Powered
53
MaxPower:                          0xFA = 500 mA
54
55
          ===>IAD Descriptor<===
56
bLength:                           0x08
57
bDescriptorType:                   0x0B
58
bFirstInterface:                   0x00
59
bInterfaceCount:                   0x03
60
bFunctionClass:                    0x01  -> Audio Interface Class
61
bFunctionSubClass:                 0x00
62
*!*CAUTION:    This appears to be an invalid bFunctionSubClass
63
bFunctionProtocol:                 0x20
64
iFunction:                         0x00
65
66
          ===>Interface Descriptor<===
67
bLength:                           0x09
68
bDescriptorType:                   0x04
69
bInterfaceNumber:                  0x00
70
bAlternateSetting:                 0x00
71
bNumEndpoints:                     0x00
72
bInterfaceClass:                   0x01  -> Audio Interface Class
73
bInterfaceSubClass:                0x01  -> Audio Control Interface SubClass
74
bInterfaceProtocol:                0x20
75
*!*WARNING:  must be set to PC_PROTOCOL_UNDEFINED 0 for this class
76
iInterface:                        0x03
77
     English (United States)  "USBStreamer"
78
79
          ===>Audio Control Interface Header Descriptor<===
80
bLength:                           0x09
81
bDescriptorType:                   0x24 (CS_INTERFACE)
82
bDescriptorSubtype:                0x01 (HEADER)
83
bcdADC:                          0x0200
84
wTotalLength:                    0xA708
85
bInCollection:                     0x00
86
87
          ===>Audio Control Feature Unit Descriptor<===
88
bLength:                           0x2A
89
bDescriptorType:                   0x24 (CS_INTERFACE)
90
bDescriptorSubtype:                0x06 (FEATURE_UNIT)
91
bUnitID:                           0x0A
92
bSourceID:                         0x02
93
bControlSize:                      0x0F
94
*!*WARNING:    bLength is greater than number of bmaControls (bLength > ( 7 + (ch + 1) * n)
95
bmaControls[master]:               00 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 00 
96
bmaControls[channel 0]:            0F 00 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 
97
iFeature:                          0x00
98
99
          ===>Audio Control Feature Unit Descriptor<===
100
bLength:                           0x2A
101
bDescriptorType:                   0x24 (CS_INTERFACE)
102
bDescriptorSubtype:                0x06 (FEATURE_UNIT)
103
bUnitID:                           0x0B
104
bSourceID:                         0x01
105
bControlSize:                      0x0F
106
*!*WARNING:    bLength is greater than number of bmaControls (bLength > ( 7 + (ch + 1) * n)
107
bmaControls[master]:               00 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 00 
108
bmaControls[channel 0]:            0F 00 00 00 0F 00 00 00 0F 00 00 00 0F 00 00 
109
iFeature:                          0x00
110
111
          ===>Interface Descriptor<===
112
bLength:                           0x09
113
bDescriptorType:                   0x04
114
bInterfaceNumber:                  0x01
115
bAlternateSetting:                 0x00
116
bNumEndpoints:                     0x00
117
bInterfaceClass:                   0x01  -> Audio Interface Class
118
bInterfaceSubClass:                0x02  -> Audio Streaming Interface SubClass
119
bInterfaceProtocol:                0x20
120
*!*WARNING:  must be set to PC_PROTOCOL_UNDEFINED 0 for this class
121
iInterface:                        0x04
122
     English (United States)  "USBStreamer "
123
124
          ===>Interface Descriptor<===
125
bLength:                           0x09
126
bDescriptorType:                   0x04
127
bInterfaceNumber:                  0x01
128
bAlternateSetting:                 0x01
129
bNumEndpoints:                     0x02
130
bInterfaceClass:                   0x01  -> Audio Interface Class
131
bInterfaceSubClass:                0x02  -> Audio Streaming Interface SubClass
132
bInterfaceProtocol:                0x20
133
*!*WARNING:  must be set to PC_PROTOCOL_UNDEFINED 0 for this class
134
iInterface:                        0x04
135
     English (United States)  "USBStreamer "
136
137
          ===>Audio Streaming Format Type Descriptor<===
138
bLength:                           0x06
139
bDescriptorType:                   0x24 (CS_INTERFACE)
140
bDescriptorSubtype:                0x02 (FORMAT_TYPE)
141
bFormatType:                       0x01 (FORMAT_TYPE_I)
142
bNrChannels:                       0x04
143
bSubframeSize:                     0x18
144
bBitResolution:                    0x07 (7)
145
bSamFreqType:                      0x05 (Discrete)
146
tSamFreq[1]:                   0xA00501 (10487041 Hz)
147
tSamFreq[2]:                   0x080101 (524545 Hz)
148
tSamFreq[3]:                   0x000125 (293 Hz)
149
tSamFreq[4]:                   0x080200 (524800 Hz)
150
tSamFreq[5]:                   0x050700 (329472 Hz)
151
152
          ===>Endpoint Descriptor<===
153
bLength:                           0x07
154
bDescriptorType:                   0x05
155
bEndpointAddress:                  0x01  -> Direction: OUT - EndpointID: 1
156
bmAttributes:                      0x05  -> Isochronous Transfer Type, Synchronization Type = Asynchronous, Usage Type = Data Endpoint
157
wMaxPacketSize:                  0x01A0 = 1 transactions per microframe, 0x1A0 max bytes
158
bInterval:                         0x01
159
160
          ===>Endpoint Descriptor<===
161
bLength:                           0x07
162
bDescriptorType:                   0x05
163
bEndpointAddress:                  0x81  -> Direction: IN - EndpointID: 1
164
bmAttributes:                      0x11  -> Isochronous Transfer Type, Synchronization Type = No Synchronization, Usage Type = Feedback Endpoint
165
wMaxPacketSize:                  0x0004 = 1 transactions per microframe, 0x04 max bytes
166
bInterval:                         0x04
167
168
          ===>Interface Descriptor<===
169
bLength:                           0x09
170
bDescriptorType:                   0x04
171
bInterfaceNumber:                  0x01
172
bAlternateSetting:                 0x02
173
bNumEndpoints:                     0x02
174
bInterfaceClass:                   0x01  -> Audio Interface Class
175
bInterfaceSubClass:                0x02  -> Audio Streaming Interface SubClass
176
bInterfaceProtocol:                0x20
177
*!*WARNING:  must be set to PC_PROTOCOL_UNDEFINED 0 for this class
178
iInterface:                        0x04
179
     English (United States)  "USBStreamer "
180
181
          ===>Audio Streaming Format Type Descriptor<===
182
bLength:                           0x06
183
bDescriptorType:                   0x24 (CS_INTERFACE)
184
bDescriptorSubtype:                0x02 (FORMAT_TYPE)
185
bFormatType:                       0x01 (FORMAT_TYPE_I)
186
bNrChannels:                       0x02
187
bSubframeSize:                     0x10
188
bBitResolution:                    0x07 (7)
189
bSamFreqType:                      0x05 (Discrete)
190
tSamFreq[1]:                   0xD00501 (13632769 Hz)
191
tSamFreq[2]:                   0x080100 (524544 Hz)
192
tSamFreq[3]:                   0x000125 (293 Hz)
193
tSamFreq[4]:                   0x080200 (524800 Hz)
194
tSamFreq[5]:                   0x050700 (329472 Hz)
195
196
          ===>Endpoint Descriptor<===
197
bLength:                           0x07
198
bDescriptorType:                   0x05
199
bEndpointAddress:                  0x01  -> Direction: OUT - EndpointID: 1
200
bmAttributes:                      0x05  -> Isochronous Transfer Type, Synchronization Type = Asynchronous, Usage Type = Data Endpoint
201
wMaxPacketSize:                  0x00D0 = 1 transactions per microframe, 0xD0 max bytes
202
bInterval:                         0x01
203
204
          ===>Endpoint Descriptor<===
205
bLength:                           0x07
206
bDescriptorType:                   0x05
207
bEndpointAddress:                  0x81  -> Direction: IN - EndpointID: 1
208
bmAttributes:                      0x11  -> Isochronous Transfer Type, Synchronization Type = No Synchronization, Usage Type = Feedback Endpoint
209
wMaxPacketSize:                  0x0004 = 1 transactions per microframe, 0x04 max bytes
210
bInterval:                         0x04
211
212
          ===>Interface Descriptor<===
213
bLength:                           0x09
214
bDescriptorType:                   0x04
215
bInterfaceNumber:                  0x02
216
bAlternateSetting:                 0x00
217
bNumEndpoints:                     0x00
218
bInterfaceClass:                   0x01  -> Audio Interface Class
219
bInterfaceSubClass:                0x02  -> Audio Streaming Interface SubClass
220
bInterfaceProtocol:                0x20
221
*!*WARNING:  must be set to PC_PROTOCOL_UNDEFINED 0 for this class
222
iInterface:                        0x05
223
     English (United States)  "USBStreamer "
224
225
          ===>Interface Descriptor<===
226
bLength:                           0x09
227
bDescriptorType:                   0x04
228
bInterfaceNumber:                  0x02
229
bAlternateSetting:                 0x01
230
bNumEndpoints:                     0x01
231
bInterfaceClass:                   0x01  -> Audio Interface Class
232
bInterfaceSubClass:                0x02  -> Audio Streaming Interface SubClass
233
bInterfaceProtocol:                0x20
234
*!*WARNING:  must be set to PC_PROTOCOL_UNDEFINED 0 for this class
235
iInterface:                        0x05
236
     English (United States)  "USBStreamer "
237
238
          ===>Audio Streaming Format Type Descriptor<===
239
bLength:                           0x06
240
bDescriptorType:                   0x24 (CS_INTERFACE)
241
bDescriptorSubtype:                0x02 (FORMAT_TYPE)
242
bFormatType:                       0x01 (FORMAT_TYPE_I)
243
bNrChannels:                       0x04
244
bSubframeSize:                     0x18
245
bBitResolution:                    0x07 (7)
246
bSamFreqType:                      0x05 (Discrete)
247
tSamFreq[1]:                   0xA00582 (10487170 Hz)
248
tSamFreq[2]:                   0x080101 (524545 Hz)
249
tSamFreq[3]:                   0x000125 (293 Hz)
250
tSamFreq[4]:                   0x080200 (524800 Hz)
251
tSamFreq[5]:                   0x040900 (264448 Hz)
252
253
          ===>Endpoint Descriptor<===
254
bLength:                           0x07
255
bDescriptorType:                   0x05
256
bEndpointAddress:                  0x82  -> Direction: IN - EndpointID: 2
257
bmAttributes:                      0x05  -> Isochronous Transfer Type, Synchronization Type = Asynchronous, Usage Type = Data Endpoint
258
wMaxPacketSize:                  0x01A0 = 1 transactions per microframe, 0x1A0 max bytes
259
bInterval:                         0x01

Die Deskriptoren für DFU und HID habe ich der Übersicht wegen 
rausgeschmissen.
Das Ganze deckt sich aber schon recht gut mit dem was Thomas bereits 
geschrieben hatte:
Thomas Z. schrieb:
> Ein paar Vorschläge:
> Im Device descriptor wird class auf 0xFE gestellt subclass auf 0x02 und
> Protokoll auf 0x01 gestellt. (Compound Device IAD). Ich habe auf
> StackOverflow mal gelesen dass OSX das IAD noch nicht kann allerdings in
> anderem Zusammenhang.
> Audio Class wird dann auf Interface Level definiert.
> Der Config Descriptor bleibt gleich. Vor jedem Interface gibt es nun
> einen IAD Descriptor.
> Interface0 beschreibt dein AC Control Interface mit der Topologie und
> controls die du exportierst. Dort ist der wesentliche Punkt dass Input
> und Output Pins nun einen Clock haben den du bedienen musst. (Set Get
> SampleRate)
> Interface1 wird dein Streaming Interface.
> Im Control Interface kann optional nun ein Interrupt EP eingebaut werden
> für Rückmeldungen  des devices.

Gruß
Markus

: Bearbeitet durch User
von Thomas Z. (usbman)


Lesenswert?

naja die Deskriptoren sehen ziemlich kaput aus. Was für ein Treiber wird 
da geladen?
nach usbaudio2.sys sieht das nicht aus.
Ich vermisse die Topologie im AC Controlinterface, also sowas wie:
usboutpin -> Feature -> Digital Out
Der Deskriptor mit den SRs ist komplett im Off. wLength im AC Interface 
stimmt nicht. Da ist mindestens MSB und LSB vertauscht.
Schalte mal die Option ein dass auch Rohdaten ausgegeben werden.

Thomas

von Clemens L. (c_l)


Lesenswert?

Thomas Z. schrieb:
> die Deskriptoren sehen ziemlich kaput aus

Nur mit Software, die UAC2 nicht kennt.

von Markus G. (usound)


Lesenswert?

Hallo,

Clemens L. schrieb:
> Thomas Z. schrieb:
>> die Deskriptoren sehen ziemlich kaput aus
>
> Nur mit Software, die UAC2 nicht kennt.

Richtig, hatte grade nochmals die Möglichkeit an den miniDSP USB 
Streamer ranzukommen und auszulesen. USBView aus dem Windows SDK liefert 
bei UAC2 großteils Müll.
Habe es grade mit USBlyzer versucht, das ist zwar besser, aber auch 
beileibe nicht vollständig.

Weiß jemand ein USB-Tool, mit dem man auch UAC2 Deskriptoren auslesen 
kann?

Gruß
Markus

von Thomas Z. (usbman)


Lesenswert?

Naja die Bytes sind ja alle da wie wäre es denn wenn du das selbst über 
bLength formatierst? Ist ja nicht soviel Arbeit und du lernst auch was 
über die UAC Deskriptoren.

Thomas

von Joe F. (easylife)


Lesenswert?

Bin Mac User, könnte mir aber vorstellen, dass dieses Tool hier nützlich 
sein könnte:
https://www.thesycon.de/eng/usb_descriptordumper.shtml

von Markus G. (usound)


Angehängte Dateien:

Lesenswert?

Hallo,

natürlich könnte ich das selbst formatieren, die Zeitersparnis mit einem 
Tool wäre mir dann aber doch recht.

Der Tipp mit dem Tool von Thesycon war nicht schlecht. Das Auslesen hat 
dem Anschein nach ohne Probleme funktioniert. Habe den Log mal als File 
mit angehängt.

Dann muss ich also den UAC2 Deskriptor komplett neu aufbauen, wenn ich 
das richtig verstanden habe? Außer ich finde natürlich irgendwo ein 
Template, das man ggf. als Arbeitsgrundlage verwenden kann.
Dann würde ich also am besten so verfahren, mir in meinem uC Code einen 
zusätzlichen Deskriptor für UAC2 zu bauen. Am besten so, dass ich per 
define zwischen UAC1 und 2 umschalten kann.

Gruß
Markus

von Thomas Z. (usbman)


Lesenswert?

Ja für den Anfang würde ich es einfach mit einem 2. Deskriptorsatz mal 
austesten.
Ich bin mir immer noch nicht so ganz sicher ob Fullspeed überhaupt mit 
UAC2 umgehen kann.
Die Deskriptoren sehen schon mal gut aus, allerdings hab ich mir nur das 
Control Interface näher angesehen. Das kannst du prinzipiell übernehmen. 
Ich würde allerdings beim Feature Deskriptor die controls für mute und 
Volume auf 0 setzen. Die stehen im Moment auf rw. Es sei denn du hast 
die Möglichkeit z.b per DSP diese Sachen zu kontrollieren.
Beim Clock sind controls fcontrol rw und vality r eingebaut. Vality 
würde ich rausnehmen für fcontrol musst du die Control requests 
einbauen. Der clockselektor at nur einen Input Pin. Den kannst du 
wahrscheinlich rausschmeißen. Win10 hat sowieso keinen Support dafür. 
W10 fragt den nur einmal ab und setzt dann den Clock. Falls du den 
Sektor drin lässt müssen auchcdie Control requests eingebaut werden. 
Auch den Mic Part kannst du rausnehmen du willst ja nur abspielen.

Natürlich musst du die Kanalzahl erst mal an Fullspeed anpassen.

Noch ein Literatur Hinweis: USB Complete von Jan Axelson. Das ist auch 
in vielen Online Bibliotheken vorhanden. Das ist ganz hilfreich für die 
spätere Umstellung auf Highspeed (Otherspeed Descriptor....) Aktuell 
wäre die 4 Ausgabe mit IAD Beschreibungen.


Thomas

: Bearbeitet durch User
von Markus G. (usound)


Lesenswert?

Hallo zusammen,

war jetzt einige Zeit inaktiv bei der Erweiterung der Software. Die 
USB->I2S Brücke funktioniert seitdem tadellos. Höre immer noch fleißig 
damit.

Habe jetzt aber wieder etwas Lust an der Software zu basteln. Bevor ich 
nun weitere Baustellen aufmache, würde ich gerne als erstes eine 
Debug-Schnittstelle implementieren. Vom Arduino kenne ich das als 
einfache serielle Ausgabe. Das wäre eine charmante und einfache Lösung.
Habe nun etwas recherchiert und diverse Beispiel-Projekte getestet.
Mir scheint, als wäre die meisten USB-Projekte mit mehreren 
Schnittstellen mit IAD Deskriptoren programmiert.
Im Grunde hätte ich eigentlich nur gern mein Audio-Interface und 
unabhängig davon einen virtual COM-Port. Jede Teilfunktion hätte im 
Prinzip einen eigenen Treiber.
Kann ich die einfach in unterschiedliche Interfaces legen und die werden 
dann nur aktiviert, wenn sie auch gebraucht werden/Daten übertragen 
werden?
Wie läuft das denn treiberseitig? Sehe ich das richtig, wenn ich einen 
IAD Deskriptor verwende, dass ich dann einen speziellen Treiber 
bräuchte, der sowohl Audio als auch COM-Port abdeckt.
Der Einstieg in das Thema Composite Device ist etwas mühsam. Habt ihr 
dazu vielleicht eine Literaturquelle?
Das von Thomas empfohlene Buch von Jan Axelson bin ich gerade dabei zu 
beschaffen. Womöglich gibt es derweil aber schonmal eine Internetquelle.

Viele Grüße
Markus

: Bearbeitet durch User
von Clemens L. (c_l)


Lesenswert?

Markus G. schrieb:
> Mir scheint, als wäre die meisten USB-Projekte mit mehreren
> Schnittstellen mit IAD Deskriptoren programmiert.

Wozu hier IAD?

> Kann ich die einfach in unterschiedliche Interfaces legen

Ja.

> und die werden dann nur aktiviert, wenn sie auch gebraucht werden/Daten
> übertragen werden?

Das Gerät bemerkt das Aktivieren nur, wenn es Alternate Settings hat.

von Thomas Z. (usbman)


Lesenswert?

So wie ich das verstanden habe ist IAD immer notwendig wenn mehrere 
Interfaces von einem Treiber benutzt werden. Beispiel Audio wo es Audio 
Control, Audio Stream und Midi Interfaces gibt. Das gleiche bei CDC. 
Neuere Specs schreiben in diesen Fällen IAD vor. Ich habe das bisher nur 
bei CDC verwendet.

Ja so ein Debug Interface ist schon ne feine Sache, ich bin teilweise 
sogar den Weg gegangen mir ein Hid Interface dazuzubasteln um Variablen 
lesen und schreiben zu können.
Debuggen von USB Apps ist ja nicht so ganz trivial.

von Markus G. (usound)


Lesenswert?

Clemens L. schrieb:
> Wozu hier IAD?
>
>> Kann ich die einfach in unterschiedliche Interfaces legen
>
> Ja.
Habe das nun nochmal nachgelesen.
Grundsätzlich bräuchte ich den IAD hier nicht, weil ich jede 
Funktion/Interface getrennt voneinander nutzen will.
Es ist aber in der Tat so:
Thomas Z. schrieb:
> So wie ich das verstanden habe ist IAD immer notwendig wenn mehrere
> Interfaces von einem Treiber benutzt werden. Beispiel Audio wo es Audio
> Control, Audio Stream und Midi Interfaces gibt. Das gleiche bei CDC.
> Neuere Specs schreiben in diesen Fällen IAD vor. Ich habe das bisher nur
> bei CDC verwendet.
Um mal die Passage aus Jan Axelsons - USB Complete, Aufl. 5 zu zitieren:
"The Interface Association Descriptors ECN says that the descriptor must 
be
supported by future implementations of devices that use multiple
interfaces to manage a single device function. Devices that comply with
the video and audio-class 2.0 specications must use interface 
association
descriptors. Class specications that predate the IAD don’t require it. 
For
example, the audio 1.0 class specication denes a class-specic
descriptor to associate audio interfaces in a function."

Das heißt im Endeffekt, für UAC1 ist das Thema IAD nicht relevant. Da 
ich aber als nächsten Schritt gerne auf UAC2 aufbohren möchte, wäre das 
für dann doch ggf. relevant. Für das Update auf UAC2 will ich 
hauptsächlich davor die Implementierung des Debug-Interface machen.

Gibt es eigentlich ein Programm oder Webservice, über den ich Fehler in 
Deskriptoren auslesen kann?
Wenn am Deskriptor des Device etwas gröberes nicht passt, kommt unter 
Windows im Prinzip nur die Meldung "geht nicht". Damit ist es natürlich 
schwer die Ursache einzukreisen.

> Ja so ein Debug Interface ist schon ne feine Sache, ich bin teilweise
> sogar den Weg gegangen mir ein Hid Interface dazuzubasteln um Variablen
> lesen und schreiben zu können.
> Debuggen von USB Apps ist ja nicht so ganz trivial.
Das schmerzvolle Debugging hab ich ja auf meinem (Leidens)Weg der 
Programmierung kennengelernt. Vieles kann man zwar mit dem Debugger im 
Betrieb machen, man tut sich aber doch etwas leichter, wenn man eine 
stetige Ausgabe von Parametern einfach umsetzen kann.

Gruß
Markus

von Thomas Z. (usbman)


Lesenswert?

Markus G. schrieb:
> Gibt es eigentlich ein Programm oder Webservice, über den ich Fehler in
> Deskriptoren auslesen kann?
Ich kenn da leider auch nichts nur die üblichen USB View Geschichten.

> Wenn am Deskriptor des Device etwas gröberes nicht passt, kommt unter
> Windows im Prinzip nur die Meldung "geht nicht". Damit ist es natürlich
> schwer die Ursache einzukreisen
Am wichtigsten ist erst mal, dass im Config Deskriptor die wTotalLength 
korrekt ist. Falls das nicht stimmt gibt es alle möglichen lustigen 
Fehler. Ansonsten gibt UsbDevView ja schon Hinweise und Warnungen aus.
Wenn ein Device gar nicht gefunden wird weil es nicht durch die Enum 
kommt kann der Fehler nur daran liegen, dass mindestens einer der unten 
beschriebenen Requests nicht so funktioniert wie er sollte.

Damit die Enum funktioniert müssen folgende Requests funktionieren
1. Getdescriptor für Device und Config
2. SetAddress
3. SetConfig

Danach kann usbview auch was anzeigen.

Ein Getdescriptor muss auch mit seltsamen Angaben im wLength Feld 
umgehen können und der Host kann jederzeit einen Request abbrechen wenn 
er keine Lust mehr hat mehr Daten zu lesen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.