Hallo, ich hänge zwei Bluetooth Boxen an das Bluetooth device des Laptop und bespaße die mit Python. Mit sounddevice erstelle ich einen OutputStream und starte den. Die Daten werden in einer audio_callback routine als Rechtecksignal erzeugt. Das geht ganz gut, man hört am knackfreien Ton dass die callbacks immer rechtzeitig bedient werden. Jetzt mache ich mit threading das ganze zweimal parallel auf zwei Boxen. Dann kommt die Sache schon ins Schwimmen, manchmal knackt es und die Zähler beider threads laufen auseinander. Wie krieg ich hin die beiden Kanäle in Echtzeit störunsfrei auszugeben, ich möchte das auf insgesamt 12 Kanäle aufbohren? Wo ist der Falschenhals? Ist es der Interpreter, muss ich aus dem .py ein .exe machen? Der laptop ist ein thinkpad E15, könnte der verbaute Bluetooth chip zicken? Muss/kann ich die beiden callbacks hoch priorisieren? Vielen Dank Cheers Detlef
Detlef _. schrieb: > Wo ist der Falschenhals? Testen und rausfinden. > Ist es der Interpreter, muss ich aus dem .py ein .exe machen? Kann ich mir kaum vorstellen. > Der laptop ist ein thinkpad E15, könnte der verbaute Bluetooth chip > zicken? Möglich, aber bei nur zwei Audio-Streams unwahrscheinlich. Also: Schnapp dir einen möglichst ressourcenschonenden Musikplayer, und starte den zweimal für deine beiden Boxen. Schau ob der beide Musikstreams ruckelfrei rüberbringt. Dann teste mit mehr Boxen, bis zu deinen 12. Wenn das geht, ist zumindest nicht Bluetooth der Flaschenhals, und wir schauen weiter.
Detlef _. schrieb: > Wo ist der Falschenhals? Da hilft nur testen > Ist es der Interpreter, muss ich aus dem .py ein .exe machen? Das wird nicht die Lösung sein, auch wenn es vielleicht zufällig dein Problem unterdrückt. Wenn ich Spekulieren sollte, könnte das GIL ein teil deines Problems sein. Welche Python Version verwendest du? Ab 3.13 ist es deaktiviert. Generell globale Variablen sind böse und sollten vermieden werden, besonders in nebenläufigen Programmen. Globale Variablen sollte man auch in Python großschreiben. Wenn du das Gefühl hast, das du Variablen nummerieren musst, kann das ein Hinweis darauf sein das man eigentlich ein Array haben will. Deine Variable devid gefällt mir nicht. Wenn die nicht zufällig 5 oder 6 ist, hast du hochdrehenden Leerlauf, weil dann raus nichts macht. Außerdem sind die Berechnungen für cnt5 & cnt6 die gleichen, es hängt nur vom "index" in devid ab. Das solltest du also Umbauen. Denn ein Unterschied gibt es nicht. Die Globalen variablen sind* werden nicht verwendet. Übrigens while ist keine Funktion, sondern ein Keyword, die Klammern sind überflüssig zudem schreibt man in Python while True, statt while 1. Globale Variablen im Thread zu ändern und im Hauptprogramm zu lesen ist undefiniertes verhalten, hierfür solltest du eine Queue verwenden. Vielleicht ist auch ein Threadpool etwas für dich, mit seiner map Implementierung.
Hallo, thx, was ist GIL? Ich benutze 3.9 . Die Source ist nix Fertiges, die genannten Aspekte hab ich soweit aufm Schirm, ich bin wie zu sehen neu in Py :| Die Signale sind fest, die kann ich auch aus einer mp3 oder wav abspielen. Nur synchron müssen sie bleiben. Welcher Player kann denn Signale über 12 verschiedene Kanäle synchron abspielen? Nix gefunden. THX Cheers Detlef
Detlef _. schrieb: > thx, was ist GIL? GIL ~ Global Interpreter Lock. Hier ist ein schöner Artikel in Englisch der den/das GIL unter Python erklärt. https://realpython.com/python-gil/
Oliver schrieb: > Das wird nicht die Lösung sein, auch wenn es vielleicht zufällig dein > Problem unterdrückt. > > Wenn ich Spekulieren sollte, könnte das GIL ein teil deines Problems > sein. Welche Python Version verwendest du? Ab 3.13 ist es deaktiviert. Diese Vermutungen teile ich. Allerdings möchte ich darauf hinweisen, daß die Deaktivierung des GIL in Python 3.13 noch ein experimentelles Feature, und als solches nicht standardmäßig aktiviert ist. Die Version ohne GIL verwendet ein anderes Executable (python3.13t anstelle von python3.13), und sie muß bei der Kompilierung des Interpreters explizit aktiviert werden, siehe dazu auch [1]. [1] https://docs.python.org/3/whatsnew/3.13.html#free-threaded-cpython > Übrigens while ist keine Funktion, sondern ein Keyword, die Klammern > sind überflüssig zudem schreibt man in Python while True, statt while 1. > Globale Variablen im Thread zu ändern und im Hauptprogramm zu lesen ist > undefiniertes verhalten, hierfür solltest du eine Queue verwenden. > Vielleicht ist auch ein Threadpool etwas für dich, mit seiner map > Implementierung. Ich würde stattdessen eher empfehlen, das Modul "multiprocessing" [2] anstelle von "threading" zu verwenden. Die API ist jener von "threading" sehr ähnlich, allerdings werden dann Prozesse anstelle von Threads verwendet, das vermeidet einerseits das GIL und sorgt andererseits auch dafür, daß jeder Prozeß seine eigenen Variablen besitzt, die ohne Seiteneffekte überschrieben werden können. Darüber hinaus würde ich empfehlen, anstelle der diversen print()-Funktionen das Builtin-Modul "logging" zu verwenden. Neben Zeitstempeln und Loglevels bietet das Modul auch einige Vorzüge hinsichtlich der Frage, wohin geloggt wird: auf os.stdout, os.stderr wären beispielsweise auch eine Datei oder das systemweite Syslog möglich, wenn man in Produktion geht. Wenn Funktionen zuvor bekannte Variablen benötigen, verwende ich anstelle von functools.partial gerne die im Anhang gezeigte Technik mit der Magic-Methode "__call__()" an einer eigenen Klasse. Diese Möglichkeit ist (aus meiner Sicht) sehr viel flexibler und auch deutlich besser lesbar, allerdings kenne ich auch andere Entwickler, die das nicht so sehen. Außerdem ist es möglich, allerdings nicht sonderlich weit verbreitet, daß auch die Methode einer Instanz als Callback verwendet werden kann, also:
1 | class Dings: |
2 | def __init__(self, arg): |
3 | self.arg = arg |
4 | def methode(self, metharg): |
5 | print('do sth with self.arg and metharg') |
6 | d = Dings('foo') |
7 | p = Process(target=d.methode, args=('bar',)) # oha, d.methode! |
8 | # ... |
Das ist insbesondere im Zusammenhang mit dem Modul multiprocessing ein wenig tricky, denn dabei muß man sich in Erinnerung rufen, daß der UNIX-Systembefehl fork(2) (heute wird eher clone(2) genutzt, aber das funktioniert an dieser Stelle ganz ähnlich) eine haargenaue Kopie des aktuellen Prozesses mit einem neuen Speicherraum erzeugt. Dadurch hat man mit dieser Technik plötzlich je eine eigene Instanz pro Prozeß, die jedoch in jedem der Prozesse haargenau dieselbe Speicheradresse hat... aber das nur am Rande. [2] https://docs.python.org/3/library/multiprocessing.html Edit: Huch, Link vergessen.
:
Bearbeitet durch User
Hallo,
vielen Dank für den Rat.
Ich hab mal einfach
raus(5)
raus(6)
aufgerufen, also ohne threading. Das geht zwar, aber die Knacker
bleiben. Das liegt also sicher daran, dass die callbackroutinen nicht
rechtzeitig rankommen.
Ich habe auf das aktuelle PyCharm Community upgedatet. Das macht auch
nur Python 3.9. und mag Multiprocessing überhaupt nicht installieren.
Welche IDE würdet Ihr denn empfehlen?
>> Finger weg von globalen Variablen <<
In der callbackroutine benötige ich eine static Variable, die gibts
nicht also habe ich die global gemacht.
Wie wäre das denn in Python besser gemacht?
THX
Cheers
Detlef
Detlef _. schrieb: > Ich hab mal einfach > raus(5) > raus(6) > aufgerufen, also ohne threading. Das geht zwar, aber die Knacker > bleiben. Das liegt also sicher daran, dass die callbackroutinen nicht > rechtzeitig rankommen. Hmmm... ehrlich gesagt würde ich mir das gerne einmal genauer anschauen, allerdings habe ich leider nicht Dein Setup -- und vor allem nicht Deine Hardware. Könntest Du die vielleicht etwas genauer beschreiben? Eventuell könnte ich hier etwas nachstellen. Letzten Endes kann es nämlich auch sein, daß das Knacken von konkurrierenden Zugriffen auf Dein Bluetooth- oder Endgerät kommt... > Ich habe auf das aktuelle PyCharm Community upgedatet. Das macht auch > nur Python 3.9. und mag Multiprocessing überhaupt nicht installieren. > > Welche IDE würdet Ihr denn empfehlen? Ich persönlich verwende den GNU Emacs, aber... das ist eine sehr alte UNIX-Software und wird deutlich anders bedient als moderne Editoren bzw. IDEs. Im Grunde genommen brauchst Du keine IDE, sondern ein ordentlicher Editor wie Geany, Atom, Sublime, VSCode, Vi(m), UltraEdit, Programmers Notepad... ein sauberes Syntax-Highlighting für Python sollte er natürlich haben, aber den ganzen Rest (Debugger etc.) kann man auch in jeder anständigen Kommandozeile (zB. Bash oder Powershell) verwenden. > In der callbackroutine benötige ich eine static Variable, die gibts > nicht also habe ich die global gemacht. > > Wie wäre das denn in Python besser gemacht? Was genau meinst Du mit "static variable"? So etwas gibt es in Python nicht wirklich, jedenfalls nicht im Sinne dessen, was das Schlüsselwort "static" in C macht. Wenn Du eine Variable benötigst, die über mehrere Funktionsaufrufe hinweg ihren Wert behalten bzw. ändern soll, dann würde ich eine Klasse bzw. deren Instanz als eine Art "Container" für Deine Variable und eine Methode dieser Klasse für den Zugriff auf die Variable empfehlen, etwa so:
1 | class Foo: |
2 | def __init__(self, bar): |
3 | self.bar = bar |
4 | def increment(self): |
5 | self.bar += 1 |
6 | def get_bar(self): |
7 | return bar |
8 | |
9 | if __name__ == '__main__': |
10 | foo = Foo(0) |
11 | print(foo.get_bar()) # gibt "0" aus |
12 | foo.increment() |
13 | foo.increment() |
14 | foo.increment() |
15 | print(foo.get_bar()) # gibt "3" aus |
Hi, ok, Python mit einem Editor sollte gehen. >>> enn Du eine Variable benötigst, die über mehrere Funktionsaufrufe hinweg ihren Wert behalten bzw. ändern soll <<< Das ist Menge Holz für ne schlichte C static variable in der Routine. Na, mal sehen. >>> Könntest Du die vielleicht etwas genauer beschreiben? Eventuell könnte ich hier etwas nachstellen. <<<< Ich hab mir nen Dutzend von diesen Billigdingern https://www.amazon.de/LogiLink-SP0051-Bluetooth-Lautsprecher-MP3-Player-Schwarz/dp/B01L6PLKJM?th=1 geschossen, 2,38 das Stück :) . Das Problem tritt schon mit zweien auf. Ich habe auch keinerlei Schimmer wie der Bluetooth Chip im Lenovo E15 das Protokoll bedient, ob der eventuell zwei Kanäle nicht bedienen kann. Vllt. war das mit den Bluetooth Boxen doch nicht so eine gute Idee. Vielen Dank Cheers Detlef
Detlef _. schrieb: > Das ist Menge Holz für ne schlichte C static variable in der Routine. > Na, mal sehen. Eine sehr deutliche Nummer kleiner lässt sich so etwas auch mit einer einfachen ›closure‹ erreichen.
Detlef _. schrieb: >>>> enn Du eine Variable benötigst, die über mehrere > Funktionsaufrufe hinweg ihren Wert behalten bzw. ändern soll > <<< > > Das ist Menge Holz für ne schlichte C static variable in der Routine. > Na, mal sehen. Nicht wirklich. :-) >>>> > Könntest Du die vielleicht etwas genauer beschreiben? > Eventuell könnte ich hier etwas nachstellen. > <<<< > Ich hab mir nen Dutzend von diesen Billigdingern > > https://www.amazon.de/LogiLink-SP0051-Bluetooth-Lautsprecher-MP3-Player-Schwarz/dp/B01L6PLKJM?th=1 > geschossen, 2,38 das Stück :) . Das Problem tritt schon mit zweien auf. > Ich habe auch keinerlei Schimmer wie der Bluetooth Chip im Lenovo E15 > das Protokoll bedient, ob der eventuell zwei Kanäle nicht bedienen kann. Schade, nicht mehr verfügbar... sonst hätte ich glatt ein paar gekauft. > Vllt. war das mit den Bluetooth Boxen doch nicht so eine gute Idee. Möglicherweise... was ist denn das eigentliche Ziel Deines Projekts?
Norbert schrieb: > Eine sehr deutliche Nummer kleiner lässt sich so etwas auch mit einer > einfachen ›closure‹ erreichen. Eine "sehr deutliche Nummer kleiner" ist das nur dann, wenn man Klassen und ihre Instanzen als etwas Riesengroßes und Kompliziertes ansieht, was jedoch ziemlicher Humbug wäre. :-)
Ein T. schrieb: > Norbert schrieb: >> Eine sehr deutliche Nummer kleiner lässt sich so etwas auch mit einer >> einfachen ›closure‹ erreichen. > > Eine "sehr deutliche Nummer kleiner" ist das nur dann, wenn man Klassen > und ihre Instanzen als etwas Riesengroßes und Kompliziertes ansieht, was > jedoch ziemlicher Humbug wäre. :-) Dir ist aber schon klar, das auch eine ›closure‹ instanziiert werden muss. Nur mit deutlich weniger Geplänkel ringsherum.
1 | #!/usr/bin/python3
|
2 | # -*- coding: utf-8 -*-
|
3 | |
4 | def closure(): |
5 | def inner(): |
6 | nonlocal static_var |
7 | print(static_var) |
8 | static_var += 1 |
9 | static_var = 100 |
10 | return inner |
11 | |
12 | instance = closure() |
13 | instance() # 100 |
14 | instance() # 101 |
15 | instance() # 102 |
Norbert schrieb: > Dir ist aber schon klar, das auch eine ›closure‹ instanziiert werden > muss. Natürlich. Was möchtest Du mir sagen?
Hi, >>>>> Schade, nicht mehr verfügbar... sonst hätte ich glatt ein paar gekauft. <<<<<< Doch, die gibs noch, hab mir gerade 12 weitere bestellt https://www.lets-sell.de/smartphone-tablet/lautsprecher/34332/bluetooth-lautsprecher-schwarz-mp3-player-microsd-mikrofon-freisprechfunktion >>>>>> Möglicherweise... was ist denn das eigentliche Ziel Deines Projekts? <<<<<< Vielen Dank für die Frage. Ich hänge die Bluetooth Lautsprecher in die Ecken einer Turnhalle und lasse sie https://de.wikipedia.org/wiki/Pseudozufallsrauschen abstrahlen. Dann kann ich mit einem https://www.mikrocontroller.net/articles/MEMS-Mikrofone die Laufzeitdifferenzen des Schalls zu dem Mic bestimmen und daraus die Position des Mics im Raum. Das funktioniert mit Kabeln zu Lautsprechern schon gut, ohne Kabel ist natürlich schöner, deshalb Bluetooth. Das Rauschen geht vorher durch einen Hochpass sodass im Hörbereich nur ein Zischeln übrig blaibt. :)) Cheers Detlef
Detlef _. schrieb: > Ein T. schrieb: >> Schade, nicht mehr verfügbar... sonst hätte ich glatt ein paar gekauft. > > Doch, die gibs noch, hab mir gerade 12 weitere bestellt > > https://www.lets-sell.de/smartphone-tablet/lautsprecher/34332/bluetooth-lautsprecher-schwarz-mp3-player-microsd-mikrofon-freisprechfunktion Oh, prima, habe mir gerade vier bestellt... Tipp: wenn Du unter den Beiträgen den Link "Markierten Text zitieren" benutzt, werden Deine Zitate sehr viel lesbarer und enthalten obendrein auch noch einen Link zu dem Beitrag, den Du zitierst, so daß unsere Leser dort ganz einfach den genauen Kontext des von Dir Zitierten nachvollziehen können. Das ist für alle Beteiligten inklusive Deiner selbst wesentlich komfortabler als diese Mergekonflikte. > Vielen Dank für die Frage. Ich hänge die Bluetooth Lautsprecher in die > Ecken einer Turnhalle und lasse sie > https://de.wikipedia.org/wiki/Pseudozufallsrauschen abstrahlen. Dann > kann ich mit einem > https://www.mikrocontroller.net/articles/MEMS-Mikrofone die > Laufzeitdifferenzen des Schalls zu dem Mic bestimmen und daraus die > Position des Mics im Raum. Das funktioniert mit Kabeln zu Lautsprechern > schon gut, ohne Kabel ist natürlich schöner, deshalb Bluetooth. Ah, also Positionsbestimmung... würden dazu nicht drei oder vier Lautsprecher reichen, ähnlich wie die Satelliten bei GPS? Davon abgesehen bin ich mir nicht sicher, wie konstant die Latenzen bei BT sind.
Ja, vier Lautsprecher reichen. Das ist wie bei GPS, da reichen auch 4 Satelliten. Mit fünf ist es einfacher zu rechnen. Und je mehr man hat umso besser sieht man die. Die Latenz spielt keine Rolle, es kommt auf die Differenz der Ankunftszeiten an. Die wird für einen Bluetooth Chip nicht auseinanderlaufen, weil die Audiokanäle dann auch asynchron würden. Bei mehreren Chips mit unterschiedlichen clocks muss man schauen. Cheers Detlef
Detlef _. schrieb: > Die wird für einen Bluetooth Chip > nicht auseinanderlaufen, weil die Audiokanäle dann auch asynchron > würden. Find's grade nicht mehr, aber jemand hat da mal eine Messreihe gemacht. Lautsprecher und Mikrophon am PC, Sound ausgeben, Delay messen bis der am Mic ankommt, das als "0-Stellung" festhalten. Dann gleiches Mic, aber Soundausgabe über diverse Bluetooth-Kopfhörer und Boxen. Da war von ~100ms (Für aptX-LowLatency Codecs) bis fast 600ms alles dabei (SBC Codec) Hört sich jetzt schlimm an, aber, Lichtblick: Die Bluetooth-Geräte können dem Host per "Delay Report" mitteilen, wie weit sie hinterher-hängen. (AVDTP Delay Reporting, eigentlich für "Lip Sync" beim Videoschauen gedacht) Der Host kann dann die Audio-Streams zu den einzelnen Lautsprechern entsprechend verzögern. Schattenseite: Viele Geräte melden einfach statische 150ms auch wenn sie bei 140 oder 170ms liegen. Für Lip-Sync reicht das, bei dir liegst du halt mal 10 Meter daneben..
Das delay ist mir insoweit egal als es für alle Lautsprecher gleich sein muss. Ich berechne die Position nicht aus der Laufzeit sondern aus der Differenz der Laufzeiten. Cheers Detlef
Detlef _. schrieb: > die Laufzeitdifferenzen des Schalls zu dem Mic bestimmen und daraus die > Position des Mics im Raum. Sehr spannend :) Stören die Reflektionen der Wände nicht? Wer/Was trägt denn die Mikrofone? Also von was möchtest du die Position bestimmen?
900ss schrieb: > Detlef _. schrieb: >> die Laufzeitdifferenzen des Schalls zu dem Mic bestimmen und daraus die >> Position des Mics im Raum. > > Sehr spannend :) Stören die Reflektionen der Wände nicht? > > Wer/Was trägt denn die Mikrofone? Also von was möchtest du die Position > bestimmen? Ich bestimme mit dem Pseudorauschen den Frequenzgang und damit die Impulsantwort der Strecke zwischen Lautsprecher und Mic. Reflexionen sehe ich als verspätete Impulse und kann sie unterscheiden. Wenn ich einen Lautsprecher nur über eine Wandreflexion sehe ist das natürlich nutzlos für die Positionsbestimmung. Ich benötige freie Sicht auf die Lautsprecher, deswegen nehme ich viele. Die Mics haben einen clock Eingang ca. 3MHz und einen Ein-Bit data Ausgang. Die lassen sich direkt an einen SPI hängen, ich nehme ein Nucleo-eval board. Wenn man irgendwas mit freier SPI Schnittstelle hat kann man ein mic ranhängen und bekommt die Position im Raum. Cheers Detlef
Detlef _. schrieb: > Reflexionen sehe ich als verspätete Impulse und kann sie unterscheiden Interessant... Geht es jetzt erstmal "nur" um das Thema Positionsbestimmung oder gibt es schon ein weiteres Ziel von was die Position bestimmen werden soll?
900ss schrieb: > Detlef _. schrieb: >> Reflexionen sehe ich als verspätete Impulse und kann sie unterscheiden > > Interessant... > > Geht es jetzt erstmal "nur" um das Thema Positionsbestimmung oder gibt > es schon ein weiteres Ziel von was die Position bestimmen werden soll? Nein, ich hab nix von dem ich die Position bestimmen muss. "Und warum mach ich das? Weil ichs' kann." Alexander Gerst :) Cheers Detlef
Detlef _. schrieb: > "Und warum mach ich das? Weil ichs' kann." Alexander Gerst :) Reicht mir auch oft als Grund :)
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.