[PYTHON] Créer un contrôleur d'interface audio avec pyusb (1)

Je fais des activités musicales en tant que passe-temps [^ 1], mais depuis que j'ai commencé à faire de la musique sur la machine Linux que j'utilisais au travail quand je jouais avec en privé et à la maison, Linux est toujours C'est un environnement de production de. Et bien qu'il y ait plusieurs obstacles à faire de la musique sous Linux [^ 2], l'un des problèmes majeurs est qu'il y a aussi des ** options d'interface audio dans le titre **. Non, comme je l'expliquerai plus tard, cela s'est considérablement amélioré récemment, donc cela devient moins un problème.

Pour faire simple, une interface audio est un appareil qui peut enregistrer / lire avec un faible bruit en connectant divers appareils, et il est presque indispensable pour la production musicale, mais bien sûr, la taille du marché Linux dans la production musicale est difficile. C'est loin de l'échelle qui motive les fabricants de vêtements à écrire des pilotes pour Linux, nous ne pouvons donc pas nous attendre à cela.

Cependant, au cours des dernières années, le nombre d'équipements qui revendiquent ** USB Class Compliant ** conforme à USB Audio Class [^ 3] a augmenté, et comme ils peuvent utiliser des fonctions de base avec des pilotes à usage général, ils peuvent également être utilisés sous Linux. C'est devenu comme. [^ 4] D'un autre côté, il y a naturellement des limites aux pilotes à usage général [^ 5], et les fonctions propres à chaque périphérique ne peuvent pas du tout être utilisées. J'ai utilisé jusqu'à présent Native Instrument Komplete Audio 6, et ce n'était pas gênant même avec un pilote à usage général, mais l'interface audio Focusrite Scarlett 18i20 récemment achetée a de nombreuses fonctions que le pilote à usage général ne peut pas prendre en charge. Peut être contrôlé uniquement via le logiciel fourni par Focusrite (Focusrite Control).

Lorsqu'on lui a demandé "Pourquoi avez-vous acheté une telle chose?" ** "Parce que je prévoyais de faire un contrôleur pour Linux depuis le début" **, mais cela semble être juste une source d'articles techniques, donc je le ferai plusieurs fois à partir de maintenant. J'écrirai la création d'un contrôleur d'interface audio avec pyusb séparément.

Bibliothèque et logiciels à utiliser

Comme mentionné ci-dessus, il n'y a pas de version Linux du pilote pour utiliser la fonction d'origine, nous allons donc capturer et analyser ici l'interaction entre Scarlett 18i 20 et Focusrite Control sous Windows.

Au début, je pensais que libusb seul ferait l'affaire, mais un rapide coup d'œil à la source du backend WinUSB de libusb semble être incompatible avec certains appareils, et la Scarlett 18i 20 semble être incompatible. Il est. (Si vous exécutez le code ci-dessous sans utiliser usbdk, une erreur se produira.) Si vous utilisez le backend usbdk, vous pouvez fonctionner avec pyusb, mais dans certains cas ** cela peut impliquer le système d'exploitation et tomber ** Donc, si vous voulez imiter ce que vous faites dans l'article, vous êtes à vos risques et périls.

Procédure d'analyse du protocole

Si vous l'écrivez, c'est juste ceci, mais c'est assez gênant. Bien sûr, l'analyse du protocole est plus ou moins gênante, mais par exemple, même si rien n'est fait, une grande quantité de journaux de données sera crachée comme le montre l'image ci-dessous.

scarlet1-1.png

Quoi qu'il en soit, la communication se fait à une fréquence énorme, et même si vous utilisez quelque chose avec Focusrite Control, le journal coulera immédiatement. Cependant, si vous regardez cette grande quantité de communication, vous pouvez voir que c'est un bon indice.

Tout d'abord, il y a une partie qui ressemble à un numéro de série, il est donc probable que Scarlett juge si le logiciel du contrôleur est dans un état cohérent par le numéro de série. Et je crains que le transfert de contrôle de 272 octets Scarlett soit presque rempli de 0. Donc, après avoir appliqué les filtres suivants, essayez diverses opérations.

DataSize == 272 && HexString != /^011000000001[0-9A-Z]{4}0{264}/

À la suite d'essais et d'erreurs, il semble que cette grande quantité de journaux concerne les données audio, probablement pour l'affichage du moniteur de Focusrite Control. Cela sera utilisé plus tard si vous souhaitez créer une fonction de moniteur similaire, mais comme ce n'est pas nécessaire récemment, elle sera supprimée avec le filtre suivant.

DataSize != 272 && HexString != /^2102000003001800011000000/ && DataSize != 0 && HexString != "A103000003001001" && HexString != "0100000000000000"

Lorsque ce filtre est appliqué, le transfert d'interruption initialement requis est supprimé, mais il est nécessaire de retirer une partie du filtre et de le vérifier.

Immédiatement après la mise sous tension de Scarlett ou lors de l'exécution du script décrit plus tard, une grande quantité de communication autre que celle ci-dessus peut être confirmée. En ce qui concerne le vidage du contenu de la communication, il s'agit probablement d'une séquence de configuration qui demande l'état. De plus, si vous exécutez un programme qui utilise usbdk, vous pouvez vérifier la même communication, donc

Peut être deviné.

Demande d'état simple

Maintenant que vous avez une idée générale du flux d'analyse, commencez par analyser la requête d'état Scarlett et écrivez le processus de requête d'état en Python. Le but initial est de fonctionner sous Linux, mais comme j'écrirai le code par essais et erreurs tout en analysant le contenu de communication régulier par Focusrite Control, j'écrirai le code une fois dans l'environnement Windows.

Analyse du traitement des requêtes

Tout d'abord, nous devons découvrir comment Focusrite Control reçoit l'état Scarlett. Si vous pensez simplement, vous devriez être en mesure de détecter le changement d'état lorsque vous appuyez sur le bouton de commutation d'état de l'unité principale par interruption du transfert.

Si vous appuyez réellement sur INST ou PAD sur l'unité principale Scarlett, vous pouvez envoyer et recevoir les données suivantes. RECV correspond aux données reçues de Scarlett et SEND correspond aux données transmises à Scarlett.

RECV Interrupt 00 00 80 00 00 00 00 00
SEND Control   21 02 00 00 03 00 18 00 00 00 80 00 08 00 EE 99
               00 00 00 00 00 00 00 00 7C 00 00 00 18 00 00 00
RECV Control
SEND Interrupt 
RECV Interrupt 01 00 00 00 00 00 00 00
SEND Control   A1 03 00 00 03 00 28 00
RECV Control   00 00 80 00 18 00 EE 99 00 00 00 00 00 00 00 00 
               01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
               00 00 00 00 00 00 00 00

C'est le résultat lorsque le mode microphone de l'entrée analogique 1 de la Scarlett 18i20 est changé en INST, mais en fait, même si d'autres boutons sont enfoncés, l'échange autre que les dernières données n'a pas changé à l'exception du numéro de série. En d'autres termes

Cela semble être le flux. Le format final des données reçues est

Il est devenu.

Code de demande

Maintenant que j'en suis conscient, j'écrirai un code qui demande en fait l'état de l'entrée analogique.

analogstat.py


import usb.core
import usb.backend.libusb1
from ctypes import c_void_p, c_int
backend = usb.backend.libusb1.get_backend(find_library=lambda x: "libusb-1.0.dll")
backend.lib.libusb_set_option.argtypes = [c_void_p, c_int]
backend.lib.libusb_set_debug(backend.ctx, 5)
#Voici les options pour utiliser usbdk
backend.lib.libusb_set_option(backend.ctx, 1)
#Identifiant du fournisseur et identifiant du produit
VENDOR_ID = 0x1235
PRODUCT_ID = 0x8215

device = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID, backend=backend)
device.set_configuration()
#La séquence de configuration s'exécute toute seule, probablement à cause de usbdk, et le numéro de série suivant est généralement 0x77.
device.ctrl_transfer(0x21, 0x02, 0x00, 0x03,
    [0x00, 0x00, 0x80, 0x00, 0x08, 0x00, 0x77, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x7C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00])
#Lecture de transfert d'interruption.
#Lire à partir du point de terminaison de l'appareil 3=83, 8 octets, délai d'expiration 100 ms
ret = device.read(0x83, 8, 100)
print(' '.join(map(lambda x: '{0:0{1}x}'.format(x, 2), ret)))
ret = bytearray(device.ctrl_transfer(0xA1, 0x03, 0x00, 0x03, 0x0028))
print(' '.join(map(lambda x: '{0:0{1}x}'.format(x, 2), ret)))

La sortie finale du résultat de l'exécution du code ci-dessus avec INST, PAD et AIR de l'entrée 1, AIR de l'entrée 4, PAD de l'entrée 5 et AIR de l'entrée 8 activés est

RECV Control 00 00 80 00 18 00 77 00 00 00 00 00 00 00 00 00 
             01 00 00 00 00 00 00 00 01 00 00 00 01 00 00 00
             01 00 00 01 00 00 00 01

bingo. Essayons une communication étrange telle que le changement du numéro de série lorsque le fonctionnement normal est confirmé.

device.ctrl_transfer(0x21, 0x02, 0x00, 0x03,
    [0x00, 0x00, 0x80, 0x00, 0x08, 0x00, 0xFF, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x7C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00])

Envoyer le transfert de contrôle de la demande d'état de l'entrée analogique avec le numéro de série avancé.

RECV Control 00 00 80 00 00 00 77 00 03 00 00 00 00 00 00 00

Apparemment, l'état d'erreur est entré dans les 8 derniers octets. Bien que j'aie demandé d'envoyer 40 octets de données, seuls 16 octets ont été envoyés, mais c'est certainement le comportement reconnu par la norme USB. NG uniquement si la longueur des données dépasse la taille requise.


Ça fait longtemps, donc cette fois c'est ici la prochaine fois

Essayons ces deux.

[^ 1]: https://return0.info/ Même si on dit que c'est un hobby exagéré ... [^ 2]: Nous ne le recommandons pas car il y a des problèmes tels que le manque de sources sonores logicielles ainsi que des équipements compatibles. Cependant, j'utilise un plug-in qui n'a qu'un binaire pour Linux, et en premier lieu j'enregistre principalement des performances sans utiliser beaucoup de sources sonores logicielles, donc je peux faire des œuvres de qualité qui proposent des étiquettes d'angle d'outre-mer. Pourtant. [^ 3]: Documents standard est assez énorme, et la rétro-ingénierie appropriée comme cette fois Pour être honnête, il est gênant de l'utiliser comme référence. [^ 4]: Derrière cela se cache la demande croissante de production musicale sur les appareils mobiles / tablettes équipés d'iOS et d'Android, et il semble qu'il y ait un aspect fort de la prise en charge des appareils pour lesquels il est difficile d'installer un pilote dédié. [^ 5]: correspond à l'enregistrement, la lecture, la commutation de la source d'horloge de l'équipement numérique, la commutation de la fréquence d'échantillonnage. De plus, dans le cas d'un pilote à usage général, il peut y avoir un inconvénient en termes de latence ainsi qu'en termes de fonctionnalités. Cependant, si la latence aller-retour comprenant tous les traitements de l'entrée à la sortie est d'environ 20 ms, cela ne semble pas être un problème. Le Pro Tools HDX utilisé dans les studios professionnels semble être dans le monde de 0,7 ms.

Recommended Posts

Créer un contrôleur d'interface audio avec pyusb (2)
Créer un contrôleur d'interface audio avec pyusb (1)
Créer un exe Scrapy avec Pyinstaller
Fabriquer un appareil de surveillance avec un capteur infrarouge
Apprentissage par renforcement 37 Démarrez automatiquement avec l'enrubanneuse Atari
Comment créer un serveur HTTPS avec Go / Gin
Faire un rappel de parapluie avec Raspberry Pi Zero W
J'ai essayé de créer une application OCR avec PySimpleGUI
Faire une application utilisant tkinter un fichier exécutable avec cx_freeze
Créer des couches Lambda avec Lambda
Créez un Yuma avec Discord.py
Créer des diapositives avec iPython
Fabriquez un climatiseur intégré à un ordinateur personnel "airpi" avec Raspberry Pi 3!