[PYTHON] Make an audio interface controller with pyusb (2)

It's been a while, but it's a continuation of Last time. This time

These two. However, since Focusrite software does not support Linux, you need to do the initialization sequence that Windows did for you. First of all, from the analysis.

Analysis of Scarlett initialization sequence

First, start Ratatoskr and then turn on / off the power of Scarlett 18i20.

VirtualBox_Windows 10_07_01_2020_00_40_15.png

A considerable amount of communication is being performed, but the point is to reset the serial number, so pay attention to the position of the serial number and make a hit. The position of the serial number is like 15..16 bytes, so if you think about it normally, the serial number is 0 or 1, and it seems that the feature appears in the message type part of 9..14 bytes. (* In the image, various filters are used to suppress.)

As a result, the following two control transfers were hit as it was.

SEND 21 02 00 00 03 00 10 00 00 00 00 00 00 00 01 00
SEND 21 02 00 00 03 00 10 00 02 00 00 00 00 00 01 00

I have sent the message with serial number 1 twice, but it seems that the first one is the serial number reset process, and the second one normally sends the message with serial number 1. First, write the code exactly as it is, and try to operate the previously written device status acquisition code with the serial number after reset. (The code below is an excerpt. See the previous code for details.)

reset_seq.py


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

By the way, it seems that the value of the serial number part of the serial number reset command can be anything.

Works on Linux

When looking at communication with USB devices on Linux, I think the method using wireshark is quite major, but I cat under / sys / kernel / debug / usbmon and play unnecessary output with grep -v. (In any case, it is necessary to install the usbmon module.)

Also, Linux seems to have a slightly different initialization process from Windows, for example, if you call set_configuration poorly, an error will occur.

analogstat4linux.py


import usb.core
import usb.backend.libusb1
from ctypes import c_void_p, c_int
backend = usb.backend.libusb1.get_backend()

from usb.util import CTRL_IN, CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE, build_request_type
from usb.control import get_status

VENDOR_ID = 0x1235
PRODUCT_ID = 0x8215
device = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID, backend=backend)
device.ctrl_transfer(0x21, 0x02, 0x00, 0x03,
    [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
device.ctrl_transfer(0xA1, 0x03, 0x00, 0x03, 0x0010)
device.ctrl_transfer(0x21, 0x02, 0x00, 0x03,
    [0x00, 0x00, 0x80, 0x00, 0x08, 0x00, 0x01, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x7C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00])
device.read(0x83, 8, 100)
ret = bytearray(device.ctrl_transfer(0xA1, 0x03, 0x00, 0x03, 0x0028))
print(' '.join(map(lambda x: '{0:0{1}x}'.format(x, 2), ret)))
$ sudo cat /sys/kernel/debug/usbmon/3t > dump.txt
* Script execution in another terminal
Ctrl-C
$ grep -v 'Z[io]:' dump.txt 
ffff95a78595f500 560234266 S Co:013:00 s 21 02 0000 0003 0010 16 = 00000000 00000100 00000000 00000000
ffff95a78595f500 560234401 C Co:013:00 0 16 >
ffff95a78595f500 560234429 S Ci:013:00 s a1 03 0000 0003 0010 16 <
ffff95a78595f500 560234646 C Ci:013:00 0 16 = 00000000 00000000 00000000 00000000
ffff95a78595f500 560234667 S Co:013:00 s 21 02 0000 0003 0018 24 = 00008000 08000100 00000000 00000000 7c000000 18000000
ffff95a78595f500 560234893 C Co:013:00 0 24 >
ffff95a78595f500 560235404 S Ii:013:03 -115 8 <
ffff95a78595f500 560235649 C Ii:013:03 0 8 = 01000000 00000000
ffff95a78595f500 560235693 S Ci:013:00 s a1 03 0000 0003 0028 40 <
ffff95a78595f500 560235776 C Ci:013:00 0 40 = 00008000 18000100 00000000 00000000 00000000 00000000 00000000 00000000

Apparently, this is also the expected result. (* There is a part that does not match the result of the Windows environment because some byte order conversion is included)

While writing various codes and experimenting, there were times when the device hung due to Operation Timeout, but at present, no recovery method has been found other than turning off the hardware. This is a future issue.

Change device state with Python code

Finally the main subject. Among the functions of Scarlett 18i20, the functions that cannot be supported by the switch of the main unit and cannot be operated by the general-purpose driver of Linux are as follows.

The latter is rather complicated or troublesome, but it is not so troublesome for me to use [^ 2], so in addition to the former, LINE / INST IMPEDANCE [^ 3] switching and input PAD [^ 4] I will try to correspond.

Analysis such as AIR switching

The procedure is almost the same as before.

As a result, the following communication seems to be AIR switching.

Sequence (1)
SEND: 21 02 00 00 00 03 00 19 01 00 80 00 09 00 01 00 00 00 00 00 00 00 00 00 8c 00 00 00 01 00 00 00 01
SEND: 03 00 00 00 03 00 10 
RECV: 01 00 80 00 00 00 01 00 00 00 00 00 00 00 00 00

Sequence (2)
SEND: 21 02 00 00 00 03 00 14 02 00 80 00 04 00 02 00 00 00 00 00 00 00 00 00 08 00 00 00
SEND: 03 00 00 00 03 00 10 
RECV: 01 00 80 00 00 00 02 00 00 00 00 00 00 00 00 00

As a result of switching IMPEDANCE etc., the output control transfer in the analog input state change sequence (1) seems to be in the following format.

The details of the 25th byte are as follows.

Also, I was wondering what the output control transfer of sequence (2) meant, but it seems that this is a display switching command of the indicator on the main unit. ** If you do not send this message, the INST / PAD / AIR indicator on the main unit will not switch. ** **

Summary code so far

The following is the code that switches the AIR state of analog input 1 each time it is executed.

switch_air.py


import usb.core
import usb.backend.libusb1
from ctypes import c_void_p, c_int
backend = usb.backend.libusb1.get_backend()

from usb.util import CTRL_IN, CTRL_OUT, CTRL_TYPE_CLASS, CTRL_RECIPIENT_INTERFACE, build_request_type
from usb.control import get_status

VENDOR_ID = 0x1235
PRODUCT_ID = 0x8215
device = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID, backend=backend)
device.ctrl_transfer(0x21, 0x02, 0x00, 0x03,
    [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])
device.ctrl_transfer(0xA1, 0x03, 0x00, 0x03, 0x0010)

#Status inquiry
device.ctrl_transfer(0x21, 0x02, 0x00, 0x03,
    [0x00, 0x00, 0x80, 0x00, 0x08, 0x00, 0x01, 0x00,
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x7C, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00])
device.read(0x83, 8, 100)
ret = bytearray(device.ctrl_transfer(0xA1, 0x03, 0x00, 0x03, 0x0028))
stat = ret[32] #AIR with analog input 1

device.ctrl_transfer(0x21, 0x02, 0x00, 0x03, [0x01, 0x00, 0x80, 0x00, 0x09, 0x00, 0x02, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x8C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
    stat ^1]) #Switch state with XOR
device.ctrl_transfer(0xA1, 0x03, 0x00, 0x03, 0x0010)

#Switching indicators
device.ctrl_transfer(0x21, 0x02, 0x00, 0x03, [0x02, 0x00, 0x80, 0x00, 0x04, 0x00, 0x03, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00])
device.ctrl_transfer(0xA1, 0x03, 0x00, 0x03, 0x0010)



The code I am actually developing is available below.

I'm so busy that I'm completely out of hand

If you fall under any of the above, please cooperate. [^ 6]

[^ 1]: A function that changes the sound quality during microphone recording to an emulation of the company's best-selling microphone preamplifier. If you can't use this, it's not worth buying for 56,000 yen. [^ 2]: During recording work, while recording the input signal on a PC, it is sometimes connected directly to the output for monitoring without latency, so it is best to play with the built-in mixer. [^ 3]: Since the impedance of the signal flowing differs depending on the equipment, switch it. [^ 4]: Decrease the gain for excessive input. [^ 5]: It is necessary to devise according to the situation, such as clearing the log once with F4 after the startup sequence is completed, narrowing down with a filter as mentioned last time. Is it important not to perform extra operations? [^ 6]: I don't know how many people are in Japan. I found two people doing similar things overseas.

Recommended Posts

Make an audio interface controller with pyusb (2)
Make an audio interface controller with pyusb (1)
Make Scrapy an exe with Pyinstaller
[Python] Make a game with Pyxel-Use an editor-
Make a monitoring device with an infrared sensor
Reinforcement learning 37 Make an automatic start with Atari's wrapper
How to make an HTTPS server with Go / Gin
Make an umbrella reminder with Raspberry Pi Zero W
I tried to make an OCR application with PySimpleGUI
Make an application using tkinter an executable file with cx_freeze
Make Lambda Layers with Lambda
Make Yubaba with Discord.py
Make slides with iPython
Make an air conditioner integrated PC "airpi" with Raspberry Pi 3!