Hack zur Unterstützung von Windows Android-Headset-Tasten

Fast jeden Tag höre ich Musik auf meinem Smartphone und benutze die Steuertasten am Headset. Aber eines hat mir immer nicht gefallen. Ich komme nach Hause, höre weiter zu, das Headset wird an meinen Heim-PC angeschlossen - und plötzlich funktionieren die Tasten nicht mehr.

Natürlich habe ich die Lösung für dieses Problem gegoogelt. Leider wird diese wunderbare Funktion unter Windows nicht allzu unterstützt. Ein paar Minuten Suche gaben auf Stack Overflow nur schlammige Erwähnungen über Soundkarten und Nachrichten von einigen Leuten, dass auf ihren Laptops alles gut funktionierte.

Das hat mich nicht erschreckt - und ich habe beschlossen, das Problem als interessante Herausforderung anzunehmen: Ist es möglich, ein Programm zum Aktivieren der Steuertasten zu erstellen, wenn überhaupt keine Hardware-Unterstützung für sie vorhanden ist? Die Antwort lautet: Ja, das können Sie. Und so geht's in einer halben Stunde.

Funktionsweise der Android-Headset-Tasten


Als Erstes müssen Sie verstehen, wie die Headset-Tasten funktionieren. Eine schnelle Suche im Internet ergab diese Spezifikation aus der Android-Dokumentation. Dort gibt es eine Tabelle.



Wie Sie verstehen können, schließt der Stromkreis eines der Widerstände, wenn Sie eine Taste am Headset drücken. Besonders hervorzuheben ist Taste A (Play / Pause / Hook) mit einem Widerstand von 0 Ohm, dh ein Mikrofonkurzschluss. Wenn wir einen Kurzschluss im Mikrofon erkennen können, können wir das Drücken der Play / Pause-Taste bestimmen.

Hypothesentest


Bevor ich mit der Programmierung beginne, möchte ich die Angemessenheit unserer Argumentation im Prinzip überprüfen. Das heißt, die Tatsache, dass das Signal vom Mikrofon durch Drücken der Play / Pause-Taste bestimmt werden kann. Glücklicherweise reicht es aus, einfach den Ton auf dem Computer aufzunehmen und das Ergebnis anzusehen. Ich habe Audacity gestartet, während der Aufnahme die Play / Pause-Taste gedrückt - und ein solches Signal empfangen.


Bingo

Wie Sie sehen können, spiegelt sich das Drücken der Taste offensichtlich in der Wellenform wider: ein plötzlicher Abfall auf -1, gefolgt von einem plötzlichen Übergang auf 1 und einem allmählichen Abfall auf 0. Intuitiv würde ich aufgrund der Spezifikation annehmen, dass das Signal auf 1 springt und dort bleibt, bis die Taste losgelassen wird, aber in Wirklichkeit sieht es anders aus. Trotzdem ist ein solches Bild immer noch leicht zu erkennen, wenn Sie den Audiostream vom Mikrofon aufnehmen.

Tonaufnahme mit Python


Wenn Sie wissen, wie Tastendrücke am Headset erkannt werden, können Sie über das Hauptziel nachdenken: Wie Sie den Player auf dem Desktop mithilfe der Headset-Tasten steuern.

Der erste Schritt besteht darin, einen Klick auf eine Schaltfläche zu erkennen. Dazu müssen Sie den Audiostream vom Mikrofon abrufen und die eindeutige Signatur ermitteln, die wir zuvor gesehen haben. Der Einfachheit halber implementieren wir die Lösung in Python. Nach einer weiteren kleinen Suche im Internet habe ich ein Paket namens sounddevice gefunden, mit dem Sie den schwierigsten Teil - die echte Audioaufnahme von einem Mikrofon - abstrahieren können.

Ein bisschen Codierung gibt uns folgendes:

import sounddevice as sd SAMPLE_RATE = 1000 # Sample rate for our input stream BLOCK_SIZE = 100 # Number of samples before we trigger a processing callback class HeadsetButtonController: def process_frames(self, indata, frames, time, status): mean = sum([y for x in indata[:] for y in x])/len(indata[:]) print(mean) def __init__(self): self.stream = sd.InputStream( samplerate=SAMPLE_RATE, blocksize=BLOCK_SIZE, channels=1, callback=self.process_frames ) self.stream.start() if __name__ == '__main__': controller = HeadsetButtonController() while True: pass 

Ein solcher Code erzeugt kontinuierlich den Durchschnittswert jeder Probencharge. Wir stellen die Abtastrate auf 1000 ein, was für die Tonverarbeitung furchtbar klein ist (normalerweise wird 44100 verwendet), aber wir brauchen wirklich nicht viel Genauigkeit. Die Blockgröße bestimmt, wie viele Samples im Puffer einen Rückruf auslösen. Auch hier setzen wir sehr niedrige Werte. Eine Blockgröße von 100 und eine Abtastrate von 1000 bedeuten tatsächlich das Auslösen von 10 Mal pro Sekunde, wobei bei jedem Aufruf nur 100 Abtastwerte verarbeitet werden.

Knopfklickerkennung: wahrscheinlich zu einfach


Jetzt erfassen wir den Audiostream und können einen echten Mechanismus zum Erkennen von Tastendrücken implementieren. Denken Sie daran, dass das Signal bei jedem Drücken auf 1 springt. Dies legt den einfachsten Weg nahe, um zu erkennen: Wenn N aufeinanderfolgende Blöcke einen Signalwert über 0,9 haben, dh einen Klick.

Wir implementieren den Algorithmus in unserer Funktion:

 import sounddevice as sd SAMPLE_RATE = 1000 # Sample rate for our input stream BLOCK_SIZE = 100 # Number of samples before we trigger a processing callback PRESS_SECONDS = 0.2 # Number of seconds button should be held to register press PRESS_SAMPLE_THRESHOLD = 0.9 # Signal amplitude to register as a button press BLOCKS_TO_PRESS = (SAMPLE_RATE/BLOCK_SIZE) * PRESS_SECONDS ... def process_frames(self, indata, frames, time, status): mean = sum([y for x in indata[:] for y in x])/len(indata[:]) if mean < PRESS_SAMPLE_THRESHOLD: self.times_pressed += 1 if self.times_pressed > BLOCKS_TO_PRESS and not self.is_held: # The button was pressed! self.is_held = True else: self.is_held = False self.times_pressed = 0 ... 

Tatsächlich haben wir einen internen Zähler gestartet, der angibt, wie viele verarbeitete Blöcke die Schwellenwertanforderung erfüllen, die einfach auf 0,9 festgelegt wird, um das unvermeidliche Rauschen der Probe zu gewährleisten. Wenn der Block die Anforderung nicht erfüllt, wird der Zähler zurückgesetzt - und wir beginnen erneut. Die Variable is_held überwacht Trigger, um sie nicht wiederholt zu registrieren, wenn die Taste nicht losgelassen wird.

Windows-Wiedergabesteuerung


Jetzt bleibt nur noch der Kommentar "Die Taste wurde gedrückt!" In echtem Code zu ersetzen zur Steuerung der Audiowiedergabe unter Windows. Google noch einmal, um herauszufinden, wie das geht: Es stellt sich heraus, dass Sie die Wiedergabe steuern können, indem Sie Tastenanschläge mit den entsprechenden virtuellen Schlüsselcodes simulieren.

Es stellte sich heraus, dass das Simulieren von Tastenanschlägen mit dem pywin32- Paket, das nur eine Python-Shell für die Windows-API ist, sehr einfach ist. Wenn wir alles zusammenfügen, können wir die folgende Funktion erstellen:

 import win32api import win32con VK_MEDIA_PLAY_PAUSE = 0xB3 def toggle_play(): win32api.keybd_event(VK_MEDIA_PLAY_PAUSE, 0, 0, 0) 

Und wir haben es geschafft! toggle_play Funktion toggle_play anstelle des Codes, in dem der Kommentar "Die Taste wurde gedrückt!" Mit dieser Option können Sie jeden Media Player in Windows über die Tasten des Android-Headsets steuern.

Tests haben gezeigt, dass der Code überraschend gut funktioniert. Der einzige Unterschied zwischen der Funktionalität unter Android und Windows ist eine leichte Verzögerung beim Drücken der Taste, aber Sie können damit leben.


Und was ist passiert?

Das Python-Skript besteht aus 51 Zeilen, die die Schaltflächen des Android-Headsets unter Windows aktivieren. Der endgültige Quellcode für dieses Projekt befindet sich auf Github .

Warten Sie, das ist noch nicht alles!


Nachdem ich das Programm einige Stunden lang glücklich benutzt hatte, bemerkte ich ein ernstes Problem:



Das Programm verbraucht fast 30% der CPU! Offensichtlich ist dies bei langer Arbeit nicht akzeptabel, es muss etwas getan werden. Beim Betrachten des Codes wurde mir klar, dass sich der Hauptthread in der Hauptschleife im Ruhezustand befindet, obwohl dort nichts passiert. Die logischste Lösung besteht darin, den Thread einfach für immer einzuschläfern: Da der Rückruf automatisch aufgerufen wird, benötigen wir immer noch keine Schleife.

 from time import sleep if __name__ == '__main__': controller = HeadsetButtonController() while True: sleep(10) 



Ich wollte das Python-Skript auch nicht nach jedem Computer-Start manuell ausführen. Glücklicherweise enthält Python für Windows ein nützliches Dienstprogramm namens pythonw.exe, mit dem der Dämonprozess ohne angeschlossenes Terminal gestartet wird. Wir platzieren eine Verknüpfung zu diesem Prozess im Verzeichnis Microsoft \ Windows \ Startmenü \ Programme \ Startup und geben unser Skript als erstes Argument an. Anschließend wird die Anwendung automatisch gestartet und im Hintergrund leise ausgeführt.

Source: https://habr.com/ru/post/de417303/


All Articles