Skip to content

Instantly share code, notes, and snippets.

@benagricola
Last active June 8, 2024 16:16
Show Gist options
  • Save benagricola/ce97696df9377a9f9c38045b89769a5b to your computer and use it in GitHub Desktop.
Save benagricola/ce97696df9377a9f9c38045b89769a5b to your computer and use it in GitHub Desktop.
Using MicroPython's new pure-python USB driver to send audio data to the host.
import array
import math
import time
import os
import usb.device
from uac2 import AudioInterface
print("Okay, configuring the audio interface now...")
# Constants
sample_rate = 48000
frequency = 440 # 440Hz
num_samples = 10 # number of samples to fit in 60 bytes
# Generate the sine wave
wave = [0.5 * math.sin(2 * math.pi * frequency * (i / sample_rate)) for i in range(num_samples)]
# Convert to 24-bit PCM
wave_pcm = [int(sample * (2**23 - 1)) for sample in wave]
# Convert to bytes and interleave left and right channels (they are the same because it's mono)
byte_array = bytearray()
for sample in wave_pcm:
# Convert sample to 24-bit little-endian
sample_bytes = sample.to_bytes(3, 'little')
# Add sample to left channel
byte_array.extend(sample_bytes)
# Add sample to right channel
byte_array.extend(sample_bytes)
print("Sample Len: {}".format(len(byte_array)))
class AudioTest(AudioInterface):
def __init__(self, sample):
self.sample = sample
super().__init__()
def send_sample(self):
self._tx.write(self.sample)
# Create an AudioInterface instance
print("Creating AudioTest instance...")
# Replace 'AudioInterface' with your actual AudioInterface class
m = AudioTest(sample=byte_array)
print("Initializing USB device...")
usb.device.get().init(m, builtin_driver=True)
while not m.is_open():
print("Waiting for USB host to configure the interface...")
time.sleep_ms(100)
print("USB host configured the interface")
while m.is_open():
m.send_sample()
m._tx_xfer()
print("Finished")
# MicroPython USB AUDIO INPUT module
# MIT license; Copyright (c) 2023 Paul Hamshere, 2023-2024 Angus Gratton
from micropython import const
import struct, time
from usb.device.core import Interface, Buffer
_EP_IN_FLAG = const(1 << 7)
_INTERFACE_CLASS_AUDIO = const(0x01)
_INTERFACE_SUBCLASS_AUDIO_CONTROL = const(0x01)
_INTERFACE_SUBCLASS_AUDIO_STREAMING = const(0x02)
class AudioInterface(Interface):
# Base class to implement a USB Audio input device in Python.
def __init__(self, txlen=30):
# Arguments are size of transmit and receive buffers in bytes.
super().__init__()
self.ep_in = None # TX direction (device to host)
self._tx = Buffer(txlen)
def _tx_xfer(self):
# Keep an active IN transfer to send data to the host, whenever
# there is data to send.
if self.is_open() and not self.xfer_pending(self.ep_in) and self._tx.readable():
self.submit_xfer(self.ep_in, self._tx.pend_read(), self._tx_cb)
def _tx_cb(self, ep, res, num_bytes):
if res == 0:
self._tx.finish_read(num_bytes)
def desc_cfg(self, desc, itf_num, ep_num, strs):
# By this point, standard device and configuration descriptors have been added.
# Append the standard AudioControl interface descriptor
desc.interface(itf_num, 0, _INTERFACE_CLASS_AUDIO, _INTERFACE_SUBCLASS_AUDIO_CONTROL)
# Append the class-specific AC interface header descriptor
desc.pack(
"<BBBHHBB",
9, # bLength
0x24, # bDescriptorType CS_INTERFACE
0x01, # bDescriptorSubtype MS_HEADER
0x0100, # BcdADC
0x0009, # wTotalLength
0x01, # bInCollection,
itf_num + 1, # baInterfaceNr - points to the Audio Streaming interface
)
# Append the input terminal descriptor
# Append input terminal name
idx = len(strs)+1
strs.append("MicroPython Audio Input")
desc.pack(
"<BBBBHBBHBB",
12, # bLength
0x24, # bDescriptorType CS_INTERFACE
0x02, # bDescriptorSubtype INPUT_TERMINAL
0x01, # bTerminalID
0x0603,# wTerminalType - USB streaming
0x00, # bAssocTerminal - no association
0x02, # bNrChannels - 2 channels
0x0003, # wChannelConfig - left and right front speakers
0x00, # iChannelNames - no string
idx, # iTerminal - no string
)
# Next add the Audio Streaming interface descriptor
desc.interface(
itf_num + 1, 1, _INTERFACE_CLASS_AUDIO, _INTERFACE_SUBCLASS_AUDIO_STREAMING
)
# Append the class-specific AS interface descriptor
desc.pack(
"<BBBBBH",
7, # bLength
0x24, # bDescriptorType CS_INTERFACE
0x01, # bDescriptorSubtype AS_GENERAL
0x01, # bTerminalLink - points to the Terminal ID of the Terminal to which the endpoint of this interface is connected
0x01, # bDelay - delay introduced by the data path
0x0001,# wFormatTag - PCM
)
# Append the Type I format type descriptor
desc.pack(
"<BBBBBBBB3B",
11, # bLength
0x24, # bDescriptorType CS_INTERFACE
0x02, # bDescriptorSubtype FORMAT_TYPE
0x01, # bFormatType - Type I
2, # bNrChannels - 2 channels
3, # bSubframeSize - 3 bytes per audio subframe
24, # bBitResolution - 24 bits per sample
1, # bSamFreqType - 1 discrete sampling frequencies
0x80, 0xBB, 0x00, # tSamFreq - 48000 Hz
)
self.ep_in = ep_num | _EP_IN_FLAG
# # rx side, USB "in" endpoint
_audio_endpoint(desc, self.ep_in)
def num_itfs(self):
return 2
def num_eps(self):
return 1
def on_open(self):
super().on_open()
# kick off any transfers that may have queued while the device was not open
self._tx_xfer()
def _audio_endpoint(desc, bEndpointAddress):
# Append the standard AS Isochronous Audio Data Endpoint descriptor
desc.pack(
"<BBBBHB",
7, # bLength
0x05, # bDescriptorType ENDPOINT
bEndpointAddress, # bEndpointAddress
0x01, # bmAttributes - Isochronous
0x0120, # wMaxPacketSize
0x01, # bInterval
)
# Append the class-specific AS Isochronous Audio Data Endpoint descriptor
desc.pack(
"<BBBBBH",
7, # bLength
0x25, # bDescriptorType CS_ENDPOINT
0x01, # bDescriptorSubtype EP_GENERAL
0x00, # bmAttributes
0x00, # bLockDelayUnits
0x0000, # wLockDelay
)
@benagricola
Copy link
Author

Currently having trouble with transmitting the actual samples:

[133467.137960] usb 3-1: Open EP 0x83, iface=3:0, idx=0
[133467.137964] usb 3-1:   channels=2, rate=48000, format=S24_3LE, period_bytes=7200, periods=5, implicit_fb=0
[133467.137969] usb 3-1: Setting params for data EP 0x83, pipe 0x1b980
[133467.137972] usb 3-1: Set up 12 URBS, ret=0
[133467.137996] usb 3-1: 3:0 Set sample rate 48000, clock 0
[133467.188291] usb 3-1: Starting data EP 0x83 (running 0)
[133467.188326] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x55d
[133467.188347] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x55d
[133467.188352] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x55e
[133467.188356] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x55f
[133467.188361] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x560
[133467.188365] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x561
[133467.188370] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x562
[133467.188374] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x563
[133467.188379] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x564
[133467.188383] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x565
[133467.188388] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x566
[133467.188392] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ad9 start_frame_id 0x55c, end_frame_id 0xda, start_frame 0x567
[133467.188394] usb 3-1: 12 URBs submitted for EP 0x83
[133467.188397] usb 3-1: 3:0 Start Capture PCM
[133467.190185] xhci_hcd 0000:00:14.0: Transfer error for slot 7 ep 6 on endpoint
[133467.190229] retire_capture_urb: 4989 callbacks suppressed
[133467.190232] usb 3-1: frame 0 active: -71
[133467.190250] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2ae8 start_frame_id 0x55e, end_frame_id 0xdc, start_frame 0x568
[133467.191229] xhci_hcd 0000:00:14.0: Miss service interval error for slot 7 ep 6, set skip flag
[133467.191232] xhci_hcd 0000:00:14.0: Transfer error for slot 7 ep 6 on endpoint
[133467.191234] xhci_hcd 0000:00:14.0: Found td. Clear skip flag for slot 7 ep 6.
[133467.191261] usb 3-1: frame 0 active: -18
[133467.191285] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2af1 start_frame_id 0x55f, end_frame_id 0xdd, start_frame 0x569
[133467.191310] usb 3-1: frame 0 active: -71
[133467.191329] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2af1 start_frame_id 0x55f, end_frame_id 0xdd, start_frame 0x56a
[133467.192270] xhci_hcd 0000:00:14.0: Transfer error for slot 7 ep 6 on endpoint
[133467.192300] usb 3-1: frame 0 active: -71
[133467.192326] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2af9 start_frame_id 0x560, end_frame_id 0xde, start_frame 0x56b
[133467.193201] xhci_hcd 0000:00:14.0: Transfer error for slot 7 ep 6 on endpoint
[133467.193233] usb 3-1: frame 0 active: -71
[133467.193259] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2b00 start_frame_id 0x561, end_frame_id 0xdf, start_frame 0x56c
[133467.194168] xhci_hcd 0000:00:14.0: Transfer error for slot 7 ep 6 on endpoint
[133467.194198] usb 3-1: frame 0 active: -71
[133467.194209] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2b08 start_frame_id 0x562, end_frame_id 0xe0, start_frame 0x56d
[133467.195190] xhci_hcd 0000:00:14.0: Transfer error for slot 7 ep 6 on endpoint
[133467.195215] usb 3-1: frame 0 active: -71
[133467.195235] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2b10 start_frame_id 0x563, end_frame_id 0xe1, start_frame 0x56e
[133467.196172] xhci_hcd 0000:00:14.0: Transfer error for slot 7 ep 6 on endpoint
[133467.196195] usb 3-1: frame 0 active: -71
[133467.196215] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2b18 start_frame_id 0x564, end_frame_id 0xe2, start_frame 0x56f
[133467.197168] xhci_hcd 0000:00:14.0: Transfer error for slot 7 ep 6 on endpoint
[133467.197182] usb 3-1: frame 0 active: -71
[133467.197194] xhci_hcd 0000:00:14.0: xhci_get_isoc_frame_id: index 0, reg 0x2b20 start_frame_id 0x565, end_frame_id 0xe3, start_frame 0x570
[133467.198160] xhci_hcd 0000:00:14.0: Transfer error for slot 7 ep 6 on endpoint
[133467.198182] usb 3-1: frame 0 active: -71

@benagricola
Copy link
Author

OK the updated code now is able to send audio data. It's not clean, there's clearly an issue with the sample rate vs buffer size vs transmit rate in USB but... AUDIO!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment