Hallo zusammen, ich werkle gerade an einer USB-zu-I2S-Brücke auf einem Atmel SAMD21. Die Audioausgabe an sich funktioniert auch schon ohne Probleme (mit UAC1). Ich teste momentan mit einer einfachen asynchronen Übertragung, der Endpoint hat genau die benötigte Größe (48 Samples x 2 Kanäle x 24 Bit = 288 Byte). Mit einem Counter zähle ich die ausgegebenen Samples auf dem I2S. Habe ich nun zu wenig oder zu viele Samples im FIFO, mache ich eine einfache SRC indem ich einen Sample verwerfe oder einen kopiere. Der FIFO ist seitens der Größe variabel zwischen 4 und 32 Frames Speichergröße. Die kleinste Größe ist natürlich seitens der Latenz am wünschenswertesten. Das ist zunächst nur ein einfacher Testcase. Die Synchronisation funktioniert so im Grunde auch schon. Ich stelle nun aber fest, dass es durch das Pre-buffering bei nicht kontinuierlichen Streams (Youtube-Videos, MP3s in iTunes etc.) nach einigen Videos oder MP3s (jedesmal wird bei einem neuen Video/File vom Windows-Treiber geprebuffered) zu einem Overflow/Underrun meines FIFOs kommt. Das äußert sich dann in einer stark verzerrten Wiedergabe. Im Schnitt ist das alle 8 bis 10 Lieder bei Buffersize = 4 Frames der Fall (die Wiedergabe wird gestartet, wenn der FIFO halb gefüllt ist, Schreib- und Lesezeiger haben also den maximalen Abstand). Zu viele Daten können nicht gesendet werden, da die maxPacketSize des Endpoints nur die 48 Samples aufnehmen kann. Nichts desto trotz verursacht das Prebuffering in Windows, dass der Lesezeiger im Vergleich zum Schreibzeiger meines FIFOs beginnt auseinander zu driften. Wie kann das zu Stande kommen? Kennt jemand das Problem und weiß ggf. wie man dem Beikommen kann? Wie läuft der Prozess dieses Prebufferings in Windows ab? Was passiert auf Seiten der USB-Datenausgabe? Jegliche Infos/Links/Stichwörter zum Prozess des Prebufferings in Windows würden mir aber auch schon helfen, mich seitens der Recherche zu dem Thema in die richtige Richtung zu orientieren. Wäre super, wenn mir jemand einen Tipp geben könnte. Beste Grüße, Markus
:
Bearbeitet durch User
Wenn ein neuer Stream startet oder stopt bekommst du jedes mal einen SetInterface(Alternate). Im Handler für diesen Requests initialisierst du dann deine Counter und alles was sonst noch notwendig ist um deine Ausgabe betriebsbereit zu machen. Ich gehe davon aus, dass du noch keine SetSampleRate() implementiert hast. Das wäre der eigentliche Platz für den InitCode. Was und wie Windows das macht ist eine Blackbox. Wenn's dich interessiert kannst du aber im DDK nachsehen Stichwort dort Audio Minidriver bzw AC97 Sample. Das ist aber sehr harte Kost. Usbaudio baut für KS Audio einen Graph zusammen der die Features deiner Descriptoren abbildet. Es gibt auch ein Tool im DDK graphview oder so. Thomas
Thomas Z. schrieb: > Wenn ein neuer Stream startet oder stoppt bekommst du jedes mal > einen SetInterface(Alternate). > Im Handler für diesen Requests initialisierst du dann deine Counter und > alles was sonst noch notwendig ist um deine Ausgabe betriebsbereit zu > machen. Hallo Thomas, genau so mache ich das auch. Wenn das alternate setting des Interface gesetzt wird, initialisiere ich alle benötigten Instanzen und Variablen neu. Die Ausgabe funktioniert auch, ebenso wie das Neustarten nach einem Stopp. Dort liegt aber auch gar nicht das Problem. Angenommen ich stoppe die Audioausgabe (drücke in der Wiedergabe-Applikation auf Stopp), dann bleibt das Interface zunächst ca. 15 Sekunden im Abspielmodus. Auf dem USB werden dann lediglich Samples mit Nullen gesendet! Ist ein Lied oder Video zu Ende, so wird das alt_setting garnicht zurückgesetzt. Am Anfang des neuen Lieds/Videos wird aber wieder zunächst der Pre-buffer gefüllt. Zumindest glaube ich, dass dies der Ursprung des Problems ist, da der Buffer Over/Underflow bzw. die Verzerrungen immer am Anfang eines neuen Lieds/Videos auftreten. Da muss folglich noch irgendwas anderes passieren. > Ich gehe davon aus, dass du noch keine SetSampleRate() implementiert > hast. Das wäre der eigentliche Platz für den InitCode. Nein habe ich momentan noch nicht implementiert, ich arbeite mit einer fixen Samplerate von 48 kHz. Es steht auch nur diese im Descriptor. > Was und wie Windows das macht ist eine Blackbox. Wenn's dich > interessiert kannst du aber im DDK nachsehen Stichwort dort Audio > Minidriver bzw AC97 Sample. Das ist aber sehr harte Kost. Wenn sich das umgehen ließe, würde ich mir das natürlich gerne ersparen. Die Umstände, die das Problem verursachen kann ich momentan aber nicht anderweitig greifen, deswegen dachte ich, dass es womöglich sinnvoll ist, sich mit der Thematik tiefergehend zu beschäftigen. Gruß Markus
Markus G. schrieb: > Angenommen ich stoppe die Audioausgabe (drücke in der > Wiedergabe-Applikation auf Stopp), dann bleibt das Interface zunächst > ca. 15 Sekunden im Abspielmodus. Auf dem USB werden dann lediglich > Samples mit Nullen gesendet! mm das ist seltsam. Ich habe zwar keine Erfahrung mit W10 aber bei allen anderen Windows Versionen kommt bei einem Stop ein SetInterface() mit einem Alternate==0. Das das Interface noch 15sec offen bleibt hab ich noch nie gesehen. Bitte prüfe das mal mit usbview. Dieses Verhalten kenn ich nicht und hab ich auch noch nie gesehen. Wenn möglich benutze das Original usbview aus dem DDK. Das ist zwar buggy ohne Ende aber zeigt offene EPs sehr schnell an. Ich vermute ein Bug bei deinem SetInterface(). Auch die 15sec ergeben für mich keinen Sinn. ksaudio hat meiner Erfahrung nach nur eine Latenz von etwa 10..50 ms. Thomas
ich hab mal das original usbview aus dem w7 ddk angehängt. Das ist vermutlich aber noch das gleiche wie das aus dem w98 ddk. Noch eine Idee zu deinen 15 sec: Das dürfte ein Timeout sein weil der SetInterface() request nicht korrekt ist. Ein USB Request besteht aus SetupStage, DataStage, und AckStage. DataStage ist in diesem Fall 0 (wLength == 0). Dein Handler muss alles abschliesen bevor das NAK weggenommen wird und mit ACK der request bestätigt wird. Ich hoffe deine Lib macht das korrekt. Ich schreib das deshalb weil ich schon zuviel USB code gesehen habe der das nicht korekt macht. Thomas
Thomas Z. schrieb: > mm das ist seltsam. Ich habe zwar keine Erfahrung mit W10 aber bei allen > anderen Windows Versionen kommt bei einem Stop ein SetInterface() mit > einem Alternate==0. Das das Interface noch 15sec offen bleibt hab ich > noch nie gesehen. USB-View habe ich bereits für die Überprüfung der Enumeration verwendet. Das Alternate setting des Interface habe ich zusätzlich auf eine LED geführt. Ich sehe also, sobald das alt_setting von 1 auf 0 geht. Ich habe den Sachverhalt gerade mit Wireshark geprüft. Sowohl meine Implementierung, als auch eine USB-Soundkarte von Roland legen das gleiche Verhalten an den Tag! Das alt_setting bleibt ca. 15-20 s auf 1 (aktiv), solange kommen auf dem USB Samples mit Nullen. Dann kommt ein set Interface und das alt_setting wird auf 0 gesetzt. Rein aus einer logischen Überlegung heraus würde es doch auch keinen Sinn machen, dass das alt_setting sofort bei jeder Unterbrechung des Streams (anderes Video anklicken / nächstes Lied in der Abspielfolge eines MediaPlayers) zurückgesetzt wird. Das wäre ja unnötiger Bustransfer ohne Ende. Gruß Markus
Thomas Z. schrieb: > Noch eine Idee zu deinen 15 sec: Dein Handler muss alles > abschliesen bevor das NAK weggenommen wird und mit ACK der request > bestätigt wird. Ich hoffe deine Lib macht das korrekt. > Ich schreib das deshalb weil ich schon zuviel USB code gesehen habe der > das nicht korekt macht. Hier der Log von Wireshark des Set Interface Requests: Host -> Device: Frame 4397: 36 bytes on wire (288 bits), 36 bytes captured (288 bits) on interface 0 USB URB [Source: host] [Destination: 2.18.0] USBPcap pseudoheader length: 28 IRP ID: 0xffff960152c918a0 IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000) URB Function: URB_FUNCTION_SELECT_INTERFACE (0x0001) IRP information: 0x00, Direction: FDO -> PDO URB bus id: 2 Device address: 18 Endpoint: 0x80, Direction: IN URB transfer type: URB_CONTROL (0x02) Packet Data Length: 8 [Response in: 4398] Control transfer stage: Setup (0) URB setup bmRequestType: 0x00 bRequest: SET INTERFACE (11) bAlternateSetting: 0 wInterface: 1 wLength: 0 Device -> Host: Frame 4398: 28 bytes on wire (224 bits), 28 bytes captured (224 bits) on interface 0 USB URB [Source: 2.18.0] [Destination: host] USBPcap pseudoheader length: 28 IRP ID: 0xffff960152c918a0 IRP USBD_STATUS: USBD_STATUS_SUCCESS (0x00000000) URB Function: URB_FUNCTION_SELECT_INTERFACE (0x0001) IRP information: 0x01, Direction: PDO -> FDO URB bus id: 2 Device address: 18 Endpoint: 0x80, Direction: IN URB transfer type: URB_CONTROL (0x02) Packet Data Length: 0 [Request in: 4397] [Time from request: 0.000960000 seconds] Control transfer stage: Status (2) Das sieht meinem Dafürhalten nach auf den ersten Blick in Ordnung aus. Bei der vorhandenen USB-Soundkarte sieht es genau identisch aus. Ich vermute an der Verarbeitung des Requests kann es somit nicht liegen. Gruß Markus
Thomas Z. schrieb: > Markus G. schrieb: >> Angenommen ich stoppe die Audioausgabe (drücke in der >> Wiedergabe-Applikation auf Stopp), dann bleibt das Interface zunächst >> ca. 15 Sekunden im Abspielmodus. Auf dem USB werden dann lediglich >> Samples mit Nullen gesendet! > > mm das ist seltsam. Ich habe zwar keine Erfahrung mit W10 aber bei allen > anderen Windows Versionen kommt bei einem Stop ein SetInterface() mit > einem Alternate==0. Das das Interface noch 15sec offen bleibt hab ich > noch nie gesehen. > Bitte prüfe das mal mit usbview. Dieses Verhalten kenn ich nicht und hab > ich auch noch nie gesehen. Wenn möglich benutze das Original usbview aus > dem DDK. Das ist zwar buggy ohne Ende aber zeigt offene EPs sehr schnell > an. > Ich vermute ein Bug bei deinem SetInterface(). > Auch die 15sec ergeben für mich keinen Sinn. ksaudio hat meiner > Erfahrung nach nur eine Latenz von etwa 10..50 ms. Hallo, habe das Thema nun nochmal etwas genauer unter die Lupe genommen. Die Verwirrung entstand durch eine unpräzise Formulierung meinerseits. Ich habe meist mit Youtube-Videos oder in Itunes getestet. Dort habe ich in diesem Sinne nicht auf Stop gedrückt, sondern auf Pause! Drückt man Pause, bleibt das Interface noch für ca. 18 Sekunden im Abspielmodus und wird dann erst abgeschaltet. Drückt man Stop oder schließt den Tab im Browser oder wählt das nächste Lied, wird das Interface sofort in alt_setting = 0 gesetzt. Das erklärt auch, warum ich beim Pausieren eines Streams keine Probleme mit dem Buffer auf meinem Device bekomme, da das Device diesen Umstand garnicht mitbekommt. Es kommen dann einfach nur 0-Samples. Nun scheint es so zu sein, dass beim Wiederstart nach einem Stop Frames verloren gehen und somit meine Bufferindizes auseinander driften. Kann das sein? Beste Grüße, Markus
Ich würde einfach mal versuchen mit wireshark nach class requests zu suchen. Ich vermute ja ganz stark dass dort SetSampleRate Requests auftauchen auch wenn du das bisher in den Descriptoren ausgeschlossen hast. Dann wäre es interessant ob du diese mit STALL bedienst. Beachte auch das SetSampleRate im Gegensatz zu den meisten anderen Requests ein Requests mit Datastage ist (wlength≠0) Manche Libs beherrschen das nicht. Thomas
Thomas Z. schrieb: > Ich würde einfach mal versuchen mit wireshark nach class requests > zu > suchen. > Ich vermute ja ganz stark dass dort SetSampleRate Requests auftauchen > auch wenn du das bisher in den Descriptoren ausgeschlossen hast. Dann > wäre es interessant ob du diese mit STALL bedienst. Beachte auch das > SetSampleRate im Gegensatz zu den meisten anderen Requests ein Requests > mit Datastage ist (wlength≠0) Manche Libs beherrschen das nicht. > > Thomas Hallo Thomas, habe gerade nochmals mit Wireshark geprüft, was passiert wenn ich den Stream stoppe. Das verhält sich soweit wie erwartet, es kommt im Prinzip sofort ein SetInterface Request und das altSetting wird auf 0 gesetzt (siehe angehängtes Bild). Gruß Markus
Markus G. schrieb: > habe gerade nochmals mit Wireshark geprüft, was passiert wenn ich den > Stream stoppe. Das verhält sich soweit wie erwartet, es kommt im Prinzip > sofort ein SetInterface Request und das altSetting wird auf 0 gesetzt > (siehe angehängtes Bild). Das war aber gar nicht die Frage. Wenn ich dich richtig verstehe bekommst du immer dann ein Problem wenn nach dem 2. SetAlternate(0) ein SetAlternate(1) kommt. Der erste SetAlternate(0) kommt dabei während der Enumeration. Vielleicht ist es einfach so dass du deinen EP beim SetInterface (1) nicht richtig einstellst und das beim ersten Streamstart nur zufällig richtig eingestellt ist. Thomas
Thomas Z. schrieb: > Markus G. schrieb: >> habe gerade nochmals mit Wireshark geprüft, was passiert wenn ich den >> Stream stoppe. Das verhält sich soweit wie erwartet, es kommt im Prinzip >> sofort ein SetInterface Request und das altSetting wird auf 0 gesetzt >> (siehe angehängtes Bild). > > Das war aber gar nicht die Frage. Wenn ich dich richtig verstehe > bekommst du immer dann ein Problem wenn nach dem 2. SetAlternate(0) ein > SetAlternate(1) kommt. > Der erste SetAlternate(0) kommt dabei während der Enumeration. > Vielleicht ist es einfach so dass du deinen EP beim SetInterface (1) > nicht richtig einstellst und das beim ersten Streamstart nur zufällig > richtig eingestellt ist. > > Thomas Hallo, Um das Ganze nochmals etwas zu präzisieren: Z.B. wenn ein Lied gewechselt wird oder das nächste Youtube-Video kommt, geht das Interface von 1 -> 0 und dann sobald die Daten vom neuen Video gebuffert sind, direkt wieder von 0 -> 1 (soweit die Theorie, siehe weiter unten). Nach ca. 10-15 Mal, bekomme ich einen Buffer Over/Underrun auf meinem Device, was sich durch Verzerrungen bei der Wiedergabe äußert. Beim Übergang 1 - 0 - 1 des altSettings scheint irgendwas zu passieren, das ich mir noch nicht so recht erklären kann. Auf meinem Device zähle ich die gesendeten Samples auf dem I2S mit (immer im Start-of-Frame Interrupt des USB-Moduls wird dieser ausgewertet). Die eingehenden Daten via USB sind konstant auf 48 Samples pro Frame fixiert. Damit meine Bufferpointer auseinander/aufeinander zu laufen, müsste folgendes passieren: - es werden zu viel/zu wenig Daten über USB in einem Frame gesendet: diesen Fall habe ich mittels der EndpointSize und der Option MaxpacketsOnly ausgemerzt. - es kommen ganze Datenframes nicht an - es werden SOFs nicht erkannt Habe nun noch Tests gemacht und so wie mir scheint das Problem noch etwas eingegrenzt. Ich habe mir eine LED auf den SetInterface-Request gelegt. Diese ist an, wenn altSetting=1 und aus, wenn das altSetting zurückgesetzt wird. Zudem habe ich eine LED die mit 20 Hz blinkt und im Data-received Interrupt des USBs verarbeitet wird. Diese hört also sofort auf, wenn der Datenstrom unterbrochen wird. Nun beobachte ich drei verschiedene Sachen (geprüft mit Wireshark): 1. Ich teste in Audacity: ich lasse etwas abspielen und sobald ich auf Stopp drücke wird der Datenstrom unterbrochen und das Interface zurückgesetzt (sehr kleine Latenz). Soweit wie erwartet. 2. Lasse ich nun ein Video abspielen und schließe das Browserfenster oder den Tab, dann wird zunächst der Datenstrom unterbrochen (mit etwas größerer Verzögerung als in Testfall 1, denke das ist aber durch die Vorverarbeitung/Buffering des Videos zu erklären). Das Interface bleibt dann aber noch ca. 4 Sekunden aktiv und wird dann erst mit einem SetInterface zurückgesetzt! 3. Ich lasse ein Video abspielen und klicke dann im Betrieb ein anderes an. Dabei kommt das Interface gar nicht dazu zurückgesetzt zu werden! Hier liegt also vermutlich das Problem. Der Datenstrom reist ab, das Interface ist aber noch im Abspielmodus. Der Neustart des Datenstroms kommt dann so schnell, dass das Device von der zwischenzeitlichen Unterbrechung nichts mitbekommt. Entsprechend werden die Buffer und zugehörige Pointer nicht zurückgesetzt. Dadurch akkumuliert sich ein Offset bei jedem Auftreten. Nun frage ich mich, ob das am Device liegen kann. Ansich ist das doch eine Treibersache auf der Hostseite. Das Device kann ja nur auf Aktionen reagieren, die übermittelt werden. Kann es sein, dass man zwingend ein zusätzliches HID-Interface braucht, über das die Play/Pause/Stopp-Aktionen auf der Hostseite übertragen werden? Ich vermute, dann wäre das Device immer im Bilde, was gerade auf dem Host passiert. Bin für jeden Hinweis dankbar. Gruß Markus
:
Bearbeitet durch User
Markus G. schrieb: > Kann es sein, dass man zwingend ein zusätzliches HID-Interface braucht, > über das die Play/Pause/Stopp-Aktionen auf der Hostseite übertragen > werden? > Ich vermute, dann wäre das Device immer im Bilde, was gerade auf dem > Host passiert. Nein HID ist nicht notwendig. Keines meiner Devices implementiert HID. Die funktionieren seit w98se problemlos. Kannst du mit wireshark irgendwelche anderen Requests zusätzlich zu SetInterface () erkennen? Versuch mal beim Setup eine Error Led einzuschalten die bei jedem unbekannten Requests angeht (ev Timeout gesteuert) 5ms reichen dann siehst du die jedesmal aufblitzen wenn ein Request auftaucht den dein Device nicht beherrscht. Das kannst du dann verfeinern indem du das z.B. nur für std requests oder class requests machst. Ich hatte damals die Möglichkeit die CPU jederzeit anzuhalten da die Streams per DMA ausgegeben wurden. Falls SOFs fehlen wird der Framecounter nicht stetig sein auch das kann man auslesen und auswerten. Thomas
Markus G. schrieb: > - es werden zu viel/zu wenig Daten über USB in einem Frame gesendet: > diesen Fall habe ich mittels der EndpointSize und der Option > MaxpacketsOnly ausgemerzt. Damit hast du nichts "ausgemerzt" sondern schaffst dir Probleme. Dein Endpoint muss in der Lage sein 49 Samples entgegenzunehmen, und auch 47. Das ist ein ganz normaler Fall, auch wenn beide Clocks scheinbar gleich schnell sind. Genau gleich schnell sind sie eben nicht, und daher braucht man immer die Möglichkeit mal ein Sample mehr oder weniger übertragen zu können. Mit deinem "Fix" schmeisst du regelmäßig gültige Datenpakete weg, und dein Buffer läuft langfristig zwangsläufig leer. Eine zweite Fehlerquelle kann sein, dass nach einem Underrun (zB. durch einen gestoppten Stream) nicht gewartet wird, bis der FIFO wieder genügend voll ist. Wenn das (pre-buffering nach underrun) nicht geschieht, läuft der FIFO ab diesem Moment in kurzen Abständen hörbar leer.
:
Bearbeitet durch User
Hallo zusammen, danke für eure Anmerkungen. Thomas Z. schrieb: > Kannst du mit wireshark > irgendwelche anderen Requests zusätzlich zu SetInterface () erkennen? Soweit ich bisher gesehen habe nicht. Werde morgen früh aber nochmal genauer schauen. > Versuch mal beim Setup eine Error Led einzuschalten die bei jedem > unbekannten Requests angeht (ev Timeout gesteuert) 5ms reichen dann > siehst du die jedesmal aufblitzen wenn ein Request auftaucht den dein > Device nicht beherrscht. > Das kannst du dann verfeinern indem du das z.B. nur für std requests > oder class requests machst. Ich hatte damals die Möglichkeit die CPU > jederzeit anzuhalten da die Streams per DMA ausgegeben wurden. > Falls SOFs fehlen wird der Framecounter nicht stetig sein auch das kann > man auslesen und auswerten. Werde ich mir auch morgen anschauen, aber ansich brauche ich doch nach erfolgreicher Enumeration nur noch den SetInterface Request für die Audiofunktion oder? Joe F. schrieb: > Damit hast du nichts "ausgemerzt" sondern schaffst dir Probleme. > Dein Endpoint muss in der Lage sein 49 Samples entgegenzunehmen, und > auch 47. > Das ist ein ganz normaler Fall, auch wenn beide Clocks scheinbar gleich > schnell sind. Genau gleich schnell sind sie eben nicht, und daher > braucht man immer die Möglichkeit mal ein Sample mehr oder weniger > übertragen zu können. > Mit deinem "Fix" schmeisst du regelmäßig gültige Datenpakete weg, und > dein Buffer läuft langfristig zwangsläufig leer. Der Endpoint läuft momentan einfach im synchronen Modus. Auf dem Device mache ich im Prinzip eine rudimentäre Sample Rate Conversion. Habe ich zu wenig Samples im FIFO, kopiere ich einen, habe ich zu viel, lösche ich einen. Das funktioniert (bei kontinuierlichen Streams) soweit auch. Den asynchronen Endpoint mit Feedback-Pfad wollte ich erst umsetzen, wenn die "Audio-Grundfunktion" korrekt läuft. Oder brauche ich für den synchronen Endpoint auch die Möglichkeit 47 bzw. 49 Samples zu übertragen? > Eine zweite Fehlerquelle kann sein, dass nach einem Underrun (zB. durch > einen gestoppten Stream) nicht gewartet wird, bis der FIFO wieder > genügend voll ist. > Wenn das (pre-buffering nach underrun) nicht geschieht, läuft der FIFO > ab diesem Moment in kurzen Abständen hörbar leer. Wenn der Stream gestoppt wird (altSetting geht auf 0), werden alle Buffer und Indizes zurückgesetzt. Beim Streamingbegin wird immer gewartet bis der Buffer halb voll ist, bis mit der Ausgabe per I2S begonnen wird. Gruß Markus
Markus G. schrieb: > Der Endpoint läuft momentan einfach im synchronen Modus. Auf dem Device > mache ich im Prinzip eine rudimentäre Sample Rate Conversion. Habe ich > zu wenig Samples im FIFO, kopiere ich einen, habe ich zu viel, lösche > ich einen. Das heisst aber nicht, dass der Host immer genau 48 Samples pro Paket sendet. Die Audio-Clock im Host kann durchaus von der USB-Clock abweichen. In der Regel ist das zwar minimal, führt aber zu der Notwendigkeit ab und zu ein "Ausgleichspaket" zu übertragen. Da du in diesem Fall immer gleich das gesamte Paket nicht akzeptierst, läufst du recht schnell in einen Underrun. > Das funktioniert (bei kontinuierlichen Streams) soweit auch. > Den asynchronen Endpoint mit Feedback-Pfad wollte ich erst umsetzen, > wenn die "Audio-Grundfunktion" korrekt läuft. > Oder brauche ich für den synchronen Endpoint auch die Möglichkeit 47 > bzw. 49 Samples zu übertragen? Ja. Das ist ja auch kein großes Problem. Mache den Endpoint etwas größer und fülle in den FIFO was vom Host ankommt.
:
Bearbeitet durch User
Joe F. schrieb: > Die Audio-Clock im Host kann durchaus von der USB-Clock abweichen. > In der Regel ist das zwar minimal, führt aber zu der Notwendigkeit ab > und zu ein "Ausgleichspaket" zu übertragen. Da du in diesem Fall immer > gleich das gesamte Paket nicht akzeptierst, läufst du recht schnell in > einen Underrun. > Ja. Das ist ja auch kein großes Problem. Mache den Endpoint etwas größer > und fülle in den FIFO was vom Host ankommt. Hallo, habe nun die Endpointsize angepasst:
1 | #define SPEAKER_NOM_SAMPLES_PER_SOF ((uint16_t)(SYSTEM_SAMPLE_RATE/1000) * SPEAKER_NB_CHANNELS)
|
2 | #define UDI_AUDIO_SPEAKER_EP_SIZE ((SPEAKER_NOM_SAMPLES_PER_SOF + SPEAKER_NB_CHANNELS) * SPEAKER_SAMPLE_LEN)
|
Somit habe ich immer einen Sample pro Kanal mehr Platz im Endpoint (in meinem Fall nun 294 Byte). Nebenfrage: Wie verhält sich das eigentlich bei nicht ganzzahligen Samplezahlen pro Frame? Z.B. bei 44,1 kHz, ist da die Basisrate 44 kHz und ich brauche dann Platz für 45 Samples oder mehr? Das war auf jeden Fall eine sinnvolle Korrektur für die Langzeitstabilität. Meine Problematik, die z.B. beim direkten Umschalten eines Videos auftritt, löst das aber auch nicht. Das betrifft im Grunde nur den beschriebenen Spezialfall: Stream wird gestoppt, Interface aber nicht zurückgesetzt, Stream direkt wieder gestartet. Sobald das Interface zurückgesetzt wird, kann kein Problem entstehen, da ich wieder einen sauber konsistenten Zustand meines FIFOs und der zugehörigen Variablen habe (alles auf Reset/Ausgangszustand beim Start). Werde nochmals versuchen etwas genauer zu lokalisieren wo das Problem entsteht. Nochmals zur Frage von oben: Brauche ich nach erfolgreicher Enumeration nur noch den SetInterface Request für die Audiofunktion oder noch andere (wenn die Samplerate immer konstant ist)? Gruß Markus
Markus G. schrieb: > Werde ich mir auch morgen anschauen, aber ansich brauche ich doch nach > erfolgreicher Enumeration nur noch den SetInterface Request für die > Audiofunktion oder? Ja das ist die Theorie. Da du aber nicht sagen kannst wie usbaudio.sys deine Dekriptoren parsed und was für ein Feature Baum daraus für ksaudio gebaut wird, musst du da auch mit Fehlern rechnen. Es ist also durchaus möglich dass class requests auftauchen die nicht korrekt bearbeitet werden. Stellt dein Code sicher dass jeder unbekannte Request auf STALL gesetzt wird? Generell liegt der Fehler an deinem Device weil USB Audio devices seit vielen Jahren zuverlässig funktionieren. Noch eine Frage bleibt die Ausgabe denn gestört oder gibt sich das wieder von alleine? Sprich braucht du ein Disconnect um ein funktionierendes Device zu bekommen? Thomas
Markus G. schrieb: > Das war auf jeden Fall eine sinnvolle Korrektur für die > Langzeitstabilität. Naja das mit der Buffer Größe ist aber nicht neu das hatte ich ja schon im alten Thread vorgeschlagen. Bei 44.1 k ist das genauso. Das spielt aber keine Rolle da du ja sowieso nur 48k machst und keine SR Umschaltung implementiert hast. Du streamst einfach was da so kommt! Ich hab allerdings das Gefühl dass du dich im Kreis drehst. Du baust ein Audiodevice machst aber Tests mit Video? Video muss zwar auch gehen, aber erst mal ist es ein Audio Device. Ist dir überhaupt klar wie viele Layer bei Video noch hinzukommen und ob die nicht zusätzliche Anforderungen an dein Device haben? Seit Monaten sage ich dir schon dass du den SetSampleRate Request einbauen sollst den alle mir bekannten USB Audiodevice unterstützen. Du frikelst das ohne hin. Immerhin hast du ja inzwischen bemerkt daß es besser ist erst mal ohne Feedback zu arbeiten. Du suchst Fehler in usbadio.sys bzw ks.sys weil dein Gerät ja alles richtig macht. Nochmal: usbaudio.sys ist mindestens seit XP stabil und weitgehend fehlerfrei. Das gleiche gilt für das Windows Audio Subsystem. Also schau dir deinen Code an. Thomas
Thomas Z. schrieb: > Ja das ist die Theorie. Da du aber nicht sagen kannst wie usbaudio.sys > deine Dekriptoren parsed und was für ein Feature Baum daraus für ksaudio > gebaut wird, musst du da auch mit Fehlern rechnen. Es ist also durchaus > möglich dass class requests auftauchen die nicht korrekt bearbeitet > werden. > Stellt dein Code sicher dass jeder unbekannte Request auf STALL gesetzt > wird? Das ist eine ziemlich gute Frage. Im Grunde habe ich einfach eine Standard-Struktur vom ASF-Treiber vorgegeben, der unabhängig von der genutzten USB-Funktion ist (Audio, HID etc.). Das Ding sieht so aus:
1 | UDC_DESC_STORAGE udi_api_t udi_api_audio_out_stream = |
2 | {
|
3 | .enable = udi_audio_stream_out_enable, |
4 | .disable = udi_audio_stream_out_disable, |
5 | .setup = udi_audio_stream_out_setup, |
6 | .getsetting = udi_audio_stream_out_getsetting, |
7 | .sof_notify = udi_audio_stream_out_SOF_routine, |
8 | };
|
Das Ding habe ich einmal für das Control-Interface und einmal für das Audio-Interface. Der enable-Callback wird immer dann aufgerufen, wenn sich an der Config des Interface etwas ändert (z.B. das altSetting). Da war die Denke natürlich folgende: Das Control-Int bekommt den Set Interface Request und macht damit irgendwas. Das resultiert darin, dass das alt Setting des Audio-Interface geändert wird. Man denkt, super, die Callback-Funktion wird aufgerufen, dann brauche ich dort ja nur meinen Initialisierungscode unterbringen und die Routine zum Empfangen von Audiodaten aufrufen. > Generell liegt der Fehler an deinem Device weil USB Audio devices seit > vielen Jahren zuverlässig funktionieren. > Noch eine Frage bleibt die Ausgabe denn gestört oder gibt sich das > wieder von alleine? Sprich braucht du ein Disconnect um ein > funktionierendes Device zu bekommen? Dass der Fehler an meinem Device liegt, ist mir durchaus klar. Die Herangehensweise war nur die, zu versuchen zu verstehen was auf der Hostseite passiert, um die Auswirkungen im Device nachvollziehen zu können. Disconnecten muss ich nicht zwingend, ein Stopp der Wiedergabe reicht (nicht Pause). Dann wird das altSetting zurückgesetzt und der Controller ist im Startzustand. > Ich hab allerdings das Gefühl dass du dich im Kreis drehst. Absolut korrekt. Im anderen Thread ging es allerdings konkret um die Implementierung des Feedback-Pfads. > Du baust ein Audiodevice machst aber Tests mit Video? Video muss zwar > auch gehen, aber erst mal ist es ein Audio Device. Ist dir überhaupt > klar wie viele Layer bei Video noch hinzukommen und ob die nicht > zusätzliche Anforderungen an dein Device haben? > Seit Monaten sage ich dir schon dass du den SetSampleRate Request > einbauen sollst den alle mir bekannten USB Audiodevice unterstützen. Du > frikelst das ohne hin. Ich teste mit allem möglichen: Audacity, Itunes und Youtube. Warum? Weil sich alle Abspielapplikationen anders zu verhalten scheinen. Dass Videos als Test nicht ideal sind, leuchtet mir ein. Sieht man diesen lustigen SetSampleRate Request in Wireshark? Vermutlich ja. Meine 2 bisher getesteten USB-Soundkarten machen das jedenfalls nicht! Testdevices sind eine einfache Behringer UCA und eine ältere Roland aus XP-Zeiten. Da gibt es für jede Samplerate ein eigenes Interface. Das wird beim Abspielen aktiviert. Das wars. Habe das jeweils mit Wireshark geprüft, die beiden Soundkarten verhalten sich auf dem Log genau identisch wie meine implementierte USB-I2S-Bridge (sowohl beim Ein- als uach beim Ausschalten). Deswegen wundere ich mich auch, dass da was nicht richtig funktioniert. > Immerhin hast du ja inzwischen bemerkt daß es > besser ist erst mal ohne Feedback zu arbeiten. Du suchst Fehler in > usbadio.sys bzw ks.sys weil dein Gerät ja alles richtig macht. Habe ich in keinster Weise behauptet. Wie gesagt, es ging mir nur darum, die Zusammenhänge zu verstehen. Gruß Markus
:
Bearbeitet durch User
Zur Überprüfung ob die Requests korrekt bearbeitet werden habe ich früher immer den Thesycon usbio Treiber benutzt dort gibts eine App mit der man jeden beliebigen Request absetzen kann. Gut geeignet um das ACK NACK STALL verhalten zu überprüfen. Der Nachteil ist, dass man dann immer den Treiber deaktivieren muss um wieder auf Audio zu kommen. Ein 2. Rechner ist also hilfreich oder man muss deren Wizard bemühen. hier ein Ausschnitt aus meiner USB Dogging Station
1 | ---------------- Interface Descriptor ----------------- |
2 | bLength : 0x09 (9 bytes) |
3 | bDescriptorType : 0x04 (Interface Descriptor) |
4 | bInterfaceNumber : 0x01 |
5 | bAlternateSetting : 0x01 |
6 | bNumEndpoints : 0x01 (1 Endpoint) |
7 | bInterfaceClass : 0x01 (Audio) |
8 | bInterfaceSubClass : 0x02 (Audio Streaming) |
9 | bInterfaceProtocol : 0x00 |
10 | iInterface : 0x00 (No String Descriptor) |
11 | |
12 | -------- Audio Streaming Interface Descriptor --------- |
13 | bLength : 0x07 (7 bytes) |
14 | bDescriptorType : 0x24 (Audio Interface Descriptor) |
15 | bDescriptorSubtype : 0x01 |
16 | bTerminalLink : 0x01 |
17 | bDelay : 0x01 |
18 | wFormatTag : 0x0001 (PCM) |
19 | |
20 | ------- Audio Streaming Format Type Descriptor -------- |
21 | bLength : 0x14 (20 bytes) |
22 | bDescriptorType : 0x24 (Audio Interface Descriptor) |
23 | bDescriptorSubtype : 0x02 (Format Type) |
24 | bFormatType : 0x01 (FORMAT_TYPE_I) |
25 | bNrChannels : 0x02 (2 channels) |
26 | bSubframeSize : 0x02 (2 bytes per subframe) |
27 | bBitResolution : 0x10 (16 bits per sample) |
28 | bSamFreqType : 0x04 (supports 4 sample frequencies) |
29 | tSamFreq[1] : 0x01F40 (8000 Hz) |
30 | tSamFreq[2] : 0x03E80 (16000 Hz) |
31 | tSamFreq[3] : 0x0AC44 (44100 Hz) |
32 | tSamFreq[4] : 0x0BB80 (48000 Hz) |
33 | |
34 | ----------------- Endpoint Descriptor ----------------- |
35 | bLength : 0x09 (9 bytes) |
36 | bDescriptorType : 0x05 (Endpoint Descriptor) |
37 | bEndpointAddress : 0x01 (Direction=OUT EndpointID=1) |
38 | bmAttributes : 0x09 (TransferType=Isochronous SyncType=Adaptive EndpointType=Data) |
39 | wMaxPacketSize : 0x00C8 (200 bytes) |
40 | bInterval : 0x01 (1 ms) |
41 | bRefresh : 0x00 |
42 | bSynchAddress : 0x00 |
43 | |
44 | ----------- Audio Data Endpoint Descriptor ------------ |
45 | bLength : 0x07 (7 bytes) |
46 | bDescriptorType : 0x25 (Audio Endpoint Descriptor) |
47 | bDescriptorSubtype : 0x01 (General) |
48 | bmAttributes : 0x01 |
49 | bLockDelayUnits : 0x01 |
50 | wLockDelay : 0x0001 |
Ich hab das auch noch nie anders gesehen ich hab (bei allen Chips) sowas drin:
1 | /********************************************************************
|
2 | * Declaration : UINT8 DecodeRequest (void) *
|
3 | * Description : Verteiler fuer die USB Requests *
|
4 | * Parameter : bmRequestType *
|
5 | * Return : REQUEST_VALID or REQUEST_ERROR *
|
6 | ********************************************************************/
|
7 | UINT8 DecodeRequest (void) |
8 | {
|
9 | switch (RequestBuf.bmRequestType & REQUESTMASK) |
10 | {
|
11 | case _STANDART: |
12 | return GetStandartRequest(); |
13 | //#ifdef VENDOR_REQUESTS_ON
|
14 | case _VENDOR: |
15 | return GetVendorRequest(); |
16 | //#endif
|
17 | case _CLASS: |
18 | return AudioClassRequests(); |
19 | }
|
20 | return REQUEST_ERROR; |
21 | }
|
22 | |
23 | /********************************************************************
|
24 | * Declaration : UINT8 AudioClassRequests(void) *
|
25 | * Description : Funktion zum bearbeiten der Audio Class Requests *
|
26 | * Parameter : -- *
|
27 | * Return : REQUEST_VALID oder REQUEST_ERROR *
|
28 | ********************************************************************/
|
29 | UINT8 AudioClassRequests(void) |
30 | {
|
31 | UINT8 result=REQUEST_ERROR; |
32 | switch (RequestBuf.bmRequestType) |
33 | {
|
34 | case _DEVICE | _CLASS | _INTERFACE: //0x21 |
35 | result=AudioSetInterfaceRequest(); |
36 | break; |
37 | case _HOST | _CLASS | _INTERFACE: //0xA1 |
38 | result=AudioGetInterfaceRequest(); |
39 | break; |
40 | case _DEVICE | _CLASS | _ENDPOINT: //0x22 |
41 | result=AudioSetEndpointRequest(); |
42 | break; |
43 | case _HOST | _CLASS | _ENDPOINT: //0xA2 |
44 | result=AudioGetEndpointRequest(); |
45 | break; |
46 | }
|
47 | if (result==REQUEST_VALID) |
48 | {
|
49 | if (RequestBuf.bmRequestType & _HOST) // GetRequest ? |
50 | SaveDataStagePointer(RequestBuf.wLength,&StageBuff[0]); |
51 | }
|
52 | return result; |
53 | }
|
Sowas sollte sich ja auch irgendwo in deinem ASF code finden lassen. Thomas
Hallo Thomas, Thomas Z. schrieb: > hier ein Ausschnitt aus meiner USB Dogging Station > Ich hab das auch noch nie anders gesehen Descriptor-mäßig sieht das im Grunde identisch aus wie bei mir. > ich hab (bei allen Chips) sowas drin: > Sowas sollte sich ja auch irgendwo in deinem ASF code finden lassen. Habe heute mal etwas tiefer gegraben, um zu verstehen wie der Code eigentlich funktioniert, den ich da benutze. Es gestaltet sich in der Tat so, dass der Treiber aus dem ASF die Standard-Requests schon auf niedriger Ebene abfängt und bearbeitet. Das ist die Beschreibung der Routine, die als erstes seitens des USB-Moduls aufgerufen wird:
1 | * brief Main routine to manage the USB SETUP request. |
2 | *
|
3 | * This function parses a USB SETUP request and submits an appropriate |
4 | * response back to the host or, in the case of SETUP OUT requests |
5 | * with data, sets up a buffer for receiving the data payload. |
6 | *
|
7 | * The main standard requests defined by the USB 2.0 standard are handled |
8 | * internally. The interface requests are sent to UDI, and the specific request |
9 | * sent to a specific application callback. |
10 | * return true if the request is supported, else the request is stalled by UDD |
Das erklärt, warum der SetInterface Request überhaupt ohne mein Zutun bearbeitet wird (vermeintlich korrekt, jedenfalls identisch wie bei anderen USB-Interfaces die ich getestet habe). Nicht Standard-Requests (z.B. SetSamplerate) landen dann in den Setup-Callbacks entweder des Control-Interface oder des Endpoints. Diese kann ich dann dort verarbeiten. Für die Sample Frequency Control habe ich sogar in meinem portierten Code vom AVR32 schon eine fertige Infrastruktur. Diese war bislang nur nicht aktiv. Ich habe es nun so implementiert, dass ich dies per define einschalten kann. Der USB-Deskriptor ändert sich dann gleich mit. Nur ob das sinnvoll ist, finde ich eher fraglich, da ich mir dort zunächst eine weitere Fehlerquelle ins System hole, die es eigentlich zur Validierung der Grundfunktion nicht braucht. Ich habe zum Thema SampleRateRequest nochmals in der UAC1 Spec recherchiert. "5.2.3.2.3.1 Sampling Frequency Control The Sampling Frequency Control is used to set the initial sampling frequency for an isochronous audio data endpoint. This allows the endpoints’ clock recovery system to lock onto the incoming clock much faster. Adaptive endpoints can benefit from this. ... If the endpoint operates at a fixed sampling frequency, setting this Control has no effect." Das heißt, dass man den SetSampleRate Request theoretisch nicht braucht, wenn das Device ohnehin nur eine SR unterstützt. Durch meine LEDs im Data Received Callback (blinken 20 Hz) und im Interface changed Callback (wird aufgerufen wenn das alt Setting geändert wird) sehe ich nun woher das Problem zu kommen scheint. Beende ich einen Stream (am einfachsten ist das Schließen eines Videos, auch wenn der Testcase nicht ideal ist), dann kommt auf dem Datenendpoint nichts mehr. Erst nach ca. 3-4 Sekunden wird das Interface zurückgesetzt. Dabei werden natürlich noch Daten ausgegeben und der Buffer läuft leer. Wenn der Stream dann gleich wieder neustartet (springen von Video zu Video/Lied zu Lied), wird das altSetting nicht zurückgesetzt und der vorher implizierte Offset der Bufferpointer tritt zum Vorschein. Gibt es eine Einstellung, die speziell mit dem Testcase Video zusammenhängt und das verzögerte Verhalten verursacht? Ansonsten müsste ich im Code implementieren, dass immer geschaut wird, wenn ein paar Frames ohne Daten übermittelt wurden und dann das Interface und die Audioverarbeitung zu reseten. Gruß Markus
Markus G. schrieb: > This allows the endpoints’ clock recovery system to lock onto the > incoming clock much faster. Das ist doch aber in meinen Augen genau der der springende Punkt. Du bekommst einen Request mit deiner SR und stellst dann alles was notwendig ist ein. Danach ist dein Stream wieder synchron. Ich finde das sinnvoll auch wenn nur eine SR im Deskriptor ist. Thomas
Noch was zum SetInterface. Ich mach bei diesem Request intern eigentlich gar nichts. Ich speichere nur das Alternate in meiner Device Struktur damit ich das bei einem GetInterface verfügbar habe. Bei mir ist die ganze Funktionalität in Get/SetSamplerate eingebaut. Thomas
Markus G. schrieb: > Dabei werden natürlich noch Daten ausgegeben und der Buffer läuft leer. > Wenn der Stream dann gleich wieder neustartet (springen von Video zu > Video/Lied zu Lied), wird das altSetting nicht zurückgesetzt und der > vorher implizierte Offset der Bufferpointer tritt zum Vorschein. Das darf aber keine Rolle spielen. Nehmen wir mal an, dein FIFO wäre so groß, dass er 5 USB Pakete fassen kann. Dann wäre der ideale Füllstand bei 3 Paketen. So groß sollte auch das Pre-Buffering sein. Ab dem Moment hast du nach "oben" und nach "unten" 2 USB Pakete Platz zur Jitter-Kompensierung. Wenn jetzt von deinem Player nichts mehr kommt, geht dein FIFO bereits nach 3 ausbleibenden Paketen in den Underrun, und du startest dein Pre-Buffering neu. Sobald wieder Daten kommen, werden erstmal 3 Pakete gebuffert, bevor du hinten was rausschickst, und alles läuft ganz normal weiter. Dass das Setting mit Verzögerung gewechselt wird, hat folgenden Grund: um Strom zu sparen möchten manche Geräte gerne den Verstärker ganz abschalten, wenn kein Audio mehr erwartet wird (z.B. Headsets). Da beim Wiedereinschalten evtl. eine kleine Verzögerung entsteht oder ein Knackser hörbar ist, versucht man dieses Abschalten bei kürzeren Audio-Pausen zu vermeiden.
:
Bearbeitet durch User
Joe F. schrieb: > Das darf aber keine Rolle spielen. (...) > Sobald wieder Daten kommen, werden erstmal 3 Pakete > gebuffert, bevor du hinten was rausschickst, und alles läuft ganz normal > weiter. Hallo, habe das nun so implementiert, dass dieser Fall abgefangen wird. Nun läuft das Ganze einwandfrei :) War dann wohl ein spezieller Fall, bzw. eine Besonderheit der Video-Klasse. Jetzt mache ich mit dem SetSamplerate weiter, um im Betrieb die SR wechseln zu können. Die Request-Infrastruktur steht schon und funktioniert auch. Eine grundsätzliche Frage dazu: Kommt der Setsamplerate nur einmal am Anfang nach der Enumeration und wenn ich im Windows-Sound-Fenster eine andere Standard-SR auswähle oder kann das auch im Betrieb gewechselt werden (z.B. erst wird ein File mit 48 kHz und dann eins mit 44,1 kHz abgespielt)? Oder dient diese Funktion nur dazu, eine feste Standard-SR auswählen zu können? Beste Grüße Markus
Markus G. schrieb: > Kommt der Setsamplerate nur einmal am Anfang nach der Enumeration und > wenn ich im Windows-Sound-Fenster eine andere Standard-SR auswähle oder > kann das auch im Betrieb gewechselt werden (z.B. erst wird ein File mit > 48 kHz und dann eins mit 44,1 kHz abgespielt)? Hallo Markus, Set SampleRate kommt immer wenn ein Sound startet also beispielsweise auch wenn ein SystemSound abgespielt wird. Wenn ich mich richtig erinnere kannst du auch Stall zurück geben wenn du beispielsweise gerade einen Stream abspielst. In diesen Fällen wird kmixer dann eine SR Konvertierung vornehmen damit beide Streams gemixt werden können. Du musst must SetCur und GetCur implementieren Windows versucht bei ruhendem Interface immer die SR der Quelle zu setzen. Wenn diese nicht unterstützt wird wird auf die höchste SR konvertiert. Da du nur abspielst braucht das nicht viel Logik. Thomas
Thomas Z. schrieb: > Hallo Markus, > Set SampleRate kommt immer wenn ein Sound startet also beispielsweise > auch wenn ein SystemSound abgespielt wird. Wenn ich mich richtig > erinnere kannst du auch Stall zurück geben wenn du beispielsweise gerade > einen Stream abspielst. > In diesen Fällen wird kmixer dann eine SR Konvertierung vornehmen damit > beide Streams gemixt werden können. Du musst must SetCur und GetCur > implementieren Hallo Thomas, danke für die Infos. Die Grundfunktion habe ich soweit implementiert. Wie du sagtest, die Logik hält sich in Grenzen. Werde mich nun aber zunächst um den Feedback-Endpoint kümmern. Wenn das läuft, kümmere ich mich nochmals intensiver um den Nebenschauplatz des SetSamplerate. Gruß Markus
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.