• WANTED: Happy members who like to discuss audio and other topics related to our interest. Desire to learn and share knowledge of science required. There are many reviews of audio hardware and expert members to help answer your questions. Click here to have your audio equipment measured for free!

Any software or electrical engineers on the forum familiar with the USB spec?

JimmyKing

Member
Joined
Jan 17, 2022
Messages
16
Likes
27
I recently got a Moondrop FreeDSP which is an IEM cable with a USB-C termination, comprising both a balanced DAC and a DSP. It's plug and play and the last used DSP profile is automatically carried forward & applied without the need of any software. However, the software you need to configure the DSP in the first place is hot garbage for so many reasons; least of all, being mobile only. This got me thinking whether the DSP profile could be read & subsequently adapted using a PC.

I do a little coding now & then and found the opensource library 'libusb' which looked promising. Better still, I found its Python wrapper 'pyusb' which promises easy USB access. Now, I've never worked with USB from a dev/engineering perspective, so I didn't find it very user friendly at all. Anyhow, after a couple of hours I could find the device, it's endpoints & read their data. I was hoping that'd be a springboard moment enabling me to do something useful. Alas, I can't see anything meaningful in the data & I suspect that I'm missing something fundamental.

My code:
Python:
#!/usr/bin/python
import sys
import usb.core
import usb.util

# hexidecimal vendor and product values
device = usb.core.find(idVendor=0x35d8, idProduct=0x1496)

# was it found?
if device is None:
  raise ValueError('FreeDSP not found')
  sys.exit(1)
else: print(device)

# endpoints
interface = 2
alternate = 1
endpoint = device[0][(interface,alternate)][0]
print('-------------')
print('OUR ENDPOINT:')
print('-------------')
print(endpoint) # this is the only one that gives us anything.

# free resources
for cfg in device:
  for intf in cfg:
    if device.is_kernel_driver_active(intf.bInterfaceNumber):
      try:
        device.detach_kernel_driver(intf.bInterfaceNumber)
      except usb.core.USBError as e:
        sys.exit("Could not detach kernel driver from interface({0}): {1}".format(intf.bInterfaceNumber, str(e)))

# check/set configuration
try:
  cfg = device.get_active_configuration()
except usb.core.USBError:
  cfg = None
if cfg is None :
  device.set_configuration()

# set alts & claim interface.
device.set_interface_altsetting(interface, alternate)
usb.util.claim_interface(device, interface)

# read 512 bytes from endpoint
data = device.read(endpoint.bEndpointAddress,512)
print('---------')
print('OUR DATA:')
print('---------')
print(data) # can't see any relevance in this.... :'(

# release the device
usb.util.release_interface(device, interface)
# Dispose the device resource
usb.util.dispose_resources(device)
# reattach the device to the OS kernel
# 0 is the audio device, including mic.
device.attach_kernel_driver(0)
# 3 (the HID) is the remote control
device.attach_kernel_driver(3)
# That leaves interfaces 1 & 2 for DSP?

print('done')

And it's output:
Code:
DEVICE ID 35d8:1496 on Bus 003 Address 008 =================
 bLength                :   0x12 (18 bytes)
 bDescriptorType        :    0x1 Device
 bcdUSB                 :  0x200 USB 2.0
 bDeviceClass           :   0xef Miscellaneous
 bDeviceSubClass        :    0x2
 bDeviceProtocol        :    0x1
 bMaxPacketSize0        :   0x40 (64 bytes)
 idVendor               : 0x35d8
 idProduct              : 0x1496
 bcdDevice              :    0x9 Device 0.09
 iManufacturer          :    0x1 MOONDROP
 iProduct               :    0x2 FreeDSP
 iSerialNumber          :    0x0
 bNumConfigurations     :    0x1
  CONFIGURATION 1: 100 mA ==================================
   bLength              :    0x9 (9 bytes)
   bDescriptorType      :    0x2 Configuration
   wTotalLength         :  0x1a6 (422 bytes)
   bNumInterfaces       :    0x4
   bConfigurationValue  :    0x1
   iConfiguration       :    0x4 Headset
   bmAttributes         :   0xa0 Bus Powered, Remote Wakeup
   bMaxPower            :   0x32 (100 mA)
    INTERFACE 0: Audio =====================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x0
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x0
     bInterfaceClass    :    0x1 Audio
     bInterfaceSubClass :    0x1
     bInterfaceProtocol :   0x20
     iInterface         :    0x0
    INTERFACE 1: Audio =====================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x1
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x0
     bInterfaceClass    :    0x1 Audio
     bInterfaceSubClass :    0x2
     bInterfaceProtocol :   0x20
     iInterface         :    0x0
    INTERFACE 1, 1: Audio ==================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x1
     bAlternateSetting  :    0x1
     bNumEndpoints      :    0x1
     bInterfaceClass    :    0x1 Audio
     bInterfaceSubClass :    0x2
     bInterfaceProtocol :   0x20
     iInterface         :    0x0
      ENDPOINT 0x1: Isochronous OUT ========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x1 OUT
       bmAttributes     :    0xd Isochronous
       wMaxPacketSize   :   0xc0 (192 bytes)
       bInterval        :    0x1
    INTERFACE 1, 2: Audio ==================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x1
     bAlternateSetting  :    0x2
     bNumEndpoints      :    0x1
     bInterfaceClass    :    0x1 Audio
     bInterfaceSubClass :    0x2
     bInterfaceProtocol :   0x20
     iInterface         :    0x0
      ENDPOINT 0x1: Isochronous OUT ========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x1 OUT
       bmAttributes     :    0xd Isochronous
       wMaxPacketSize   :  0x120 (288 bytes)
       bInterval        :    0x1
    INTERFACE 1, 3: Audio ==================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x1
     bAlternateSetting  :    0x3
     bNumEndpoints      :    0x1
     bInterfaceClass    :    0x1 Audio
     bInterfaceSubClass :    0x2
     bInterfaceProtocol :   0x20
     iInterface         :    0x0
      ENDPOINT 0x1: Isochronous OUT ========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :    0x1 OUT
       bmAttributes     :    0xd Isochronous
       wMaxPacketSize   :  0x180 (384 bytes)
       bInterval        :    0x1
    INTERFACE 2: Audio =====================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x2
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x0
     bInterfaceClass    :    0x1 Audio
     bInterfaceSubClass :    0x2
     bInterfaceProtocol :   0x20
     iInterface         :    0x0
    INTERFACE 2, 1: Audio ==================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x2
     bAlternateSetting  :    0x1
     bNumEndpoints      :    0x1
     bInterfaceClass    :    0x1 Audio
     bInterfaceSubClass :    0x2
     bInterfaceProtocol :   0x20
     iInterface         :    0x0
      ENDPOINT 0x81: Isochronous IN ========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0xd Isochronous
       wMaxPacketSize   :    0xc (12 bytes)
       bInterval        :    0x1
    INTERFACE 2, 2: Audio ==================================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x2
     bAlternateSetting  :    0x2
     bNumEndpoints      :    0x1
     bInterfaceClass    :    0x1 Audio
     bInterfaceSubClass :    0x2
     bInterfaceProtocol :   0x20
     iInterface         :    0x0
      ENDPOINT 0x81: Isochronous IN ========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0xd Isochronous
       wMaxPacketSize   :   0x12 (18 bytes)
       bInterval        :    0x1
    INTERFACE 3: Human Interface Device ====================
     bLength            :    0x9 (9 bytes)
     bDescriptorType    :    0x4 Interface
     bInterfaceNumber   :    0x3
     bAlternateSetting  :    0x0
     bNumEndpoints      :    0x1
     bInterfaceClass    :    0x3 Human Interface Device
     bInterfaceSubClass :    0x0
     bInterfaceProtocol :    0x0
     iInterface         :    0x0
      ENDPOINT 0x83: Interrupt IN ==========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x83 IN
       bmAttributes     :    0x3 Interrupt
       wMaxPacketSize   :   0x40 (64 bytes)
       bInterval        :    0x7
-------------
OUR ENDPOINT:
-------------
      ENDPOINT 0x81: Isochronous IN ========================
       bLength          :    0x7 (7 bytes)
       bDescriptorType  :    0x5 Endpoint
       bEndpointAddress :   0x81 IN
       bmAttributes     :    0xd Isochronous
       wMaxPacketSize   :    0xc (12 bytes)
       bInterval        :    0x1
---------
OUR DATA:
---------
array('B', [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 0, 0, 255, 255, 1, 0, 253, 255, 5, 0, 4, 0, 251, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
done

That endpoint is the only one which returns anything but zeros and I've ruled out interfaces 0 & 3 as being the audio device and inline remote respectively. Furthermore, if I disconnect, reconnect & try again, it often gives a different response. :facepalm:

Any help would be appreciated. Even if it's to say that this isn't how it's done - it'd at least save me the headache...
 
  • Like
Reactions: MCH

IAtaman

Major Contributor
Forum Donor
Joined
Mar 29, 2021
Messages
2,409
Likes
4,166
I have limited experience and am not an expert either, but if I were you, I'd check if you can read anything from Isochronous OUT endpoints.
 

phofman

Addicted to Fun and Learning
Joined
Apr 13, 2021
Messages
503
Likes
327
If you find out how to read the bytes of the DSP profile from your device, will you know how to decode the stream to get some meaning and modify it? IOW do you have programming documentation for the DSP in your device?
 

-Matt-

Addicted to Fun and Learning
Joined
Nov 21, 2021
Messages
680
Likes
570
You don't really need an electrical engineer; you need a software person, ideally one who is familiar with the way Moondrop have implemented their DSP and USB comms.

A quick search of Github turned this up, which sounds like it might have added some support for this device:


Updates​

4.1.2​

  • Added Moondrop Free DSP PEQ optimizer config

Edit: Reading a bit more, I think this app only generates eq settings for you to type in manually (or copy paste). I don't think it attempts to send the settings via USB.

(There is already a thread on this site about AutoEQ and the author @jaakkopasanen posts here):

Searching Github for existing projects should usually be your first port of call, though I admit I'm not seeing much else other than AutoEQ. If Moondrop don't publish their protocols anywhere then it would likely be a very hard job to reverse engineer them.

Think of libusb as giving you a telephone line to mars. Just having the telephone line doesn't allow you to speak and understand martian!
 
Last edited:
Joined
Nov 23, 2022
Messages
11
Likes
19
@JimmyKing
I've reverse engineered the regular moondrop dawn controls (i.e. the moondrop link app, see here).
The communication is done over the control endpoint (0).

If the free dsp is supported by moondrop link, you can reverse engineer it via jadx and build your own software based on it like i did.

Edit: Just checked, the dsp is supported by link, so your starting point should be to reverse eng the link 2.0 app as per above.
 
Top Bottom