Presque tous les jours, j'écoute de la musique sur mon smartphone et j'utilise les boutons de commande du casque. Mais je n'ai toujours pas aimé une chose. Je rentre chez moi, en continuant d'écouter, le casque se connecte à mon ordinateur personnel - et soudain, les boutons cessent de fonctionner.
Bien sûr, j'ai googlé la solution à ce problème. Malheureusement, sous Windows, cette merveilleuse fonctionnalité n'est pas trop prise en charge. Quelques minutes de recherche n'ont donné que des mentions boueuses sur Stack Overflow sur les cartes son et les messages de certaines personnes que tout fonctionnait bien sur leurs ordinateurs portables.
Cela ne m'a pas fait peur - et j'ai décidé d'accepter le problème comme un défi intéressant: est-il possible de créer une sorte de programme pour activer les boutons de commande, s'il n'y a aucun support matériel pour eux? La réponse est oui, vous le pouvez. Et voici comment le faire en une demi-heure.
Fonctionnement des boutons du casque Android
La première chose à comprendre est le fonctionnement des boutons du casque. Une recherche rapide sur Internet a trouvé
cette spécification dans la documentation Android. Il y a un tableau ici.

Comme vous pouvez le comprendre, lorsque vous appuyez sur un bouton du casque, le circuit de l'une des résistances se ferme. Il convient de noter en particulier le bouton A (lecture / pause / crochet) avec une résistance de 0 ohms, c'est-à-dire un court-circuit du microphone. Si nous sommes capables de détecter un court-circuit dans le microphone, alors nous pouvons déterminer la pression sur le bouton Play / Pause.
Test d'hypothèse
Avant de commencer la programmation, je voudrais vérifier le caractère raisonnable de notre raisonnement en principe. Autrement dit, le fait que le signal provenant du microphone peut être déterminé en appuyant sur le bouton Lecture / Pause. Heureusement, pour cela, il suffit d'enregistrer simplement le son sur l'ordinateur et de regarder le résultat. J'ai lancé Audacity, j'ai appuyé sur le bouton Lecture / Pause pendant l'enregistrement - et j'ai reçu un tel signal.
BingoComme vous pouvez le voir, appuyer sur le bouton se reflète évidemment dans la forme d'onde: une chute soudaine à −1 suivie d'une transition soudaine à 1 et une diminution progressive à 0. Intuitivement par spécification, je suppose que le signal passe à 1 et y reste jusqu'à ce que le bouton soit relâché, mais en réalité, cela semble différent. Néanmoins, une telle image est toujours facile à détecter si vous capturez le flux audio à partir du microphone.
Capture sonore avec Python
Connaissant la façon de détecter les pressions sur les boutons du casque, vous pouvez penser à l'objectif principal: comment contrôler le lecteur sur le bureau à l'aide des boutons du casque.
La première étape consiste à détecter un clic de bouton. Pour ce faire, vous devez saisir le flux audio du microphone et découvrir la signature distincte que nous avons vue plus tôt. Pour plus de simplicité, nous implémentons la solution en Python. Après une autre petite recherche sur Internet, j'ai trouvé un package appelé sounddevice, qui vous permet d'abstraire de la partie la plus difficile - la capture audio réelle à partir d'un microphone.
Un peu de codage nous donne ce qui suit:
import sounddevice as sd SAMPLE_RATE = 1000
Un tel code produit en continu la valeur moyenne de chaque lot d'échantillons. Nous avons réglé le taux d'échantillonnage à 1000, ce qui est terriblement petit pour le traitement du son (44100 est généralement utilisé), mais nous n'avons vraiment pas besoin de beaucoup de précision. La taille de bloc détermine le nombre d'échantillons dans le tampon qui déclenchent un rappel. Encore une fois, nous fixons des valeurs très faibles. Une taille de bloc de 100 et un taux d'échantillonnage de 1000 signifient en fait un déclenchement 10 fois par seconde, où seulement 100 échantillons sont traités à chaque appel.
Détection de clic de bouton: probablement trop facile
Maintenant, nous capturons le flux audio et nous pouvons implémenter un véritable mécanisme de détection des pressions sur les boutons. Rappelez-vous que le signal passe à 1 chaque fois que vous appuyez sur. Cela suggère la manière la plus simple de détecter: si
N blocs consécutifs ont une valeur de signal supérieure à 0,9, c'est-à-dire un clic.
Nous implémentons l'algorithme dans notre fonction:
import sounddevice as sd SAMPLE_RATE = 1000
En fait, nous avons lancé un compteur interne, combien de blocs traités répondent à l'exigence de seuil, qui est simplement définie à 0,9, prévoyant le bruit inévitable de l'échantillon. Si le bloc ne satisfait pas à l'exigence, le compteur est réinitialisé - et nous recommençons. La variable
is_held
surveille les déclencheurs afin de ne pas les enregistrer à plusieurs reprises si le bouton n'est pas relâché.
Contrôle de lecture Windows
Il ne reste plus qu'à remplacer le commentaire
"Le bouton a été enfoncé!" En vrai code pour contrôler la lecture audio dans Windows. Google à nouveau pour comprendre comment faire: il s'avère que vous pouvez contrôler la lecture en simulant les frappes avec les
codes de touches virtuelles correspondantes.
Il s'est avéré que la simulation des frappes est très facile avec le paquet
pywin32 , qui n'est qu'un shell Python pour l'API Windows. En mettant tout cela ensemble, nous pouvons créer la fonction suivante:
import win32api import win32con VK_MEDIA_PLAY_PAUSE = 0xB3 def toggle_play(): win32api.keybd_event(VK_MEDIA_PLAY_PAUSE, 0, 0, 0)
Et nous l'avons fait!
toggle_play
fonction
toggle_play
à la place du code où le commentaire
"Le bouton a été enfoncé!" , vous permet de contrôler n'importe quel lecteur multimédia sous Windows à l'aide des boutons du casque Android.
Des tests ont montré que le code fonctionne étonnamment bien. La seule différence entre les fonctionnalités sur Android et Windows est un léger retard lorsque vous appuyez sur le bouton, mais vous pouvez vivre avec.
Et donc qu'est-il arrivéLe script Python se compose de 51 lignes qui activent les boutons du casque Android sous Windows. Le code source final de ce projet est
sur Github .
Attendez, ce n'est pas tout!
Après avoir utilisé le programme avec bonheur pendant plusieurs heures, j'ai remarqué un problème grave:

Le programme utilise près de 30% du CPU! De toute évidence, cela est inacceptable pendant un long travail, quelque chose doit être fait. En regardant le code, j'ai réalisé que le thread principal est à l'état inactif dans la boucle principale, bien que rien ne s'y passe. La solution la plus logique consiste à simplement euthanasier le thread pour toujours: puisque le rappel est appelé automatiquement, nous n'avons toujours pas besoin d'une boucle.
from time import sleep if __name__ == '__main__': controller = HeadsetButtonController() while True: sleep(10)

Je ne voulais pas non plus exécuter le script Python manuellement après chaque démarrage de l'ordinateur. Heureusement, Python pour Windows est livré avec un utilitaire utile appelé pythonw.exe qui démarre le processus démon sans qu'un terminal soit connecté. Nous plaçons un raccourci vers ce processus dans le répertoire
Microsoft \ Windows \ Start Menu \ Programs \ Startup , en spécifiant notre script comme premier argument - puis l'application démarre automatiquement et s'exécute silencieusement en arrière-plan.