Browse Source

add MIDI Streaming Descriptors

main
Hans Baier 3 years ago
parent
commit
5ad0368097
4 changed files with 228 additions and 18 deletions
  1. +67
    -0
      usb_protocol/emitters/descriptors/uac.py
  2. +3
    -1
      usb_protocol/emitters/descriptors/uac2.py
  3. +115
    -9
      usb_protocol/types/descriptors/uac.py
  4. +43
    -8
      usb_protocol/types/descriptors/uac2.py

+ 67
- 0
usb_protocol/emitters/descriptors/uac.py View File

@@ -0,0 +1,67 @@
#
# This file is part of usb_protocol.
#
""" Convenience emitters for USB Audio Class 2 descriptors. """

from contextlib import contextmanager

from .. import emitter_for_format
from ...types.descriptors.uac import *
from ...types.descriptors.uac2 import *
from ...emitters.descriptor import ComplexDescriptorEmitter

###################### MIDI #########################

StandardMidiStreamingInterfaceDescriptorEmitter = emitter_for_format(StandardMidiStreamingInterfaceDescriptor)
ClassSpecificMidiStreamingInterfaceHeaderDescriptorEmitter = emitter_for_format(ClassSpecificMidiStreamingInterfaceHeaderDescriptor)
StandardMidiStreamingDataEndpointDescriptorEmitter = emitter_for_format(StandardMidiStreamingDataEndpointDescriptor)
StandardMidiStreamingBulkDataEndpointDescriptorEmitter = emitter_for_format(StandardMidiStreamingBulkDataEndpointDescriptor)
MidiInJackDescriptorEmitter = emitter_for_format(MidiInJackDescriptor)
MidiOutJackDescriptorElementEmitter = emitter_for_format(MidiOutJackDescriptorElement)
MidiOutJackDescriptorFootEmitter = emitter_for_format(MidiOutJackDescriptorFoot)
ClassSpecificMidiStreamingBulkDataEndpointDescriptorHeadEmitter = emitter_for_format(ClassSpecificMidiStreamingBulkDataEndpointDescriptorHead)
ClassSpecificMidiStreamingBulkDataEndpointDescriptorElementEmitter = emitter_for_format(ClassSpecificMidiStreamingBulkDataEndpointDescriptorElement)

class ClassSpecificMidiStreamingInterfaceDescriptorEmitter(ComplexDescriptorEmitter):
DESCRIPTOR_FORMAT = ClassSpecificMidiStreamingInterfaceHeaderDescriptor

def _pre_emit(self):
# Figure out the total length of our descriptor, including subordinates.
subordinate_length = sum(len(sub) for sub in self._subordinates)
self.wTotalLength = subordinate_length + self.DESCRIPTOR_FORMAT.sizeof()

class MidiOutJackDescriptorEmitter(ComplexDescriptorEmitter):
DESCRIPTOR_FORMAT = MidiOutJackDescriptorHead

def add_subordinate_descriptor(self, subordinate):
subordinate = subordinate.emit()
self._subordinates.append(subordinate)

def add_source(self, sourceId, sourcePin=1):
sourceDescriptor = MidiOutJackDescriptorElementEmitter()
sourceDescriptor.baSourceID = sourceId
sourceDescriptor.BaSourcePin = sourcePin
self.add_subordinate_descriptor(sourceDescriptor)

def _pre_emit(self):
self.add_subordinate_descriptor(MidiOutJackDescriptorFootEmitter())
# Figure out the total length of our descriptor, including subordinates.
subordinate_length = sum(len(sub) for sub in self._subordinates)
self.bLength = subordinate_length + self.DESCRIPTOR_FORMAT.sizeof()

class ClassSpecificMidiStreamingBulkDataEndpointDescriptorEmitter(ComplexDescriptorEmitter):
DESCRIPTOR_FORMAT = ClassSpecificMidiStreamingBulkDataEndpointDescriptorHead

def add_subordinate_descriptor(self, subordinate):
subordinate = subordinate.emit()
self._subordinates.append(subordinate)

def add_associated_jack(self, jackID):
jackDescriptor = ClassSpecificMidiStreamingBulkDataEndpointDescriptorElementEmitter()
jackDescriptor.baAssocJackID = jackID
self.add_subordinate_descriptor(jackDescriptor)

def _pre_emit(self):
# Figure out the total length of our descriptor, including subordinates.
subordinate_length = sum(len(sub) for sub in self._subordinates)
self.bLength = subordinate_length + self.DESCRIPTOR_FORMAT.sizeof()

+ 3
- 1
usb_protocol/emitters/descriptors/uac2.py View File

@@ -10,6 +10,8 @@ from ...types.descriptors.uac import *
from ...types.descriptors.uac2 import * from ...types.descriptors.uac2 import *
from ...emitters.descriptor import ComplexDescriptorEmitter from ...emitters.descriptor import ComplexDescriptorEmitter


###################### Audio #########################

# Create our emitters. # Create our emitters.
InterfaceAssociationDescriptorEmitter = emitter_for_format(InterfaceAssociationDescriptor) InterfaceAssociationDescriptorEmitter = emitter_for_format(InterfaceAssociationDescriptor)
StandardAudioControlInterfaceDescriptorEmitter = emitter_for_format(StandardAudioControlInterfaceDescriptor) StandardAudioControlInterfaceDescriptorEmitter = emitter_for_format(StandardAudioControlInterfaceDescriptor)
@@ -36,4 +38,4 @@ ExtendedTypeIIIFormatTypeDescriptorEmitter = emitt
ClassSpecificAudioStreamingIsochronousAudioDataEndpointDescriptorEmitter = emitter_for_format(ClassSpecificAudioStreamingIsochronousAudioDataEndpointDescriptor) ClassSpecificAudioStreamingIsochronousAudioDataEndpointDescriptorEmitter = emitter_for_format(ClassSpecificAudioStreamingIsochronousAudioDataEndpointDescriptor)
AudioControlInterruptEndpointDescriptorEmitter = emitter_for_format(AudioControlInterruptEndpointDescriptor) AudioControlInterruptEndpointDescriptorEmitter = emitter_for_format(AudioControlInterruptEndpointDescriptor)
AudioStreamingIsochronousEndpointDescriptorEmitter = emitter_for_format(AudioStreamingIsochronousEndpointDescriptor) AudioStreamingIsochronousEndpointDescriptorEmitter = emitter_for_format(AudioStreamingIsochronousEndpointDescriptor)
AudioStreamingIsochronousFeedbackEndpointDescriptorEmitter = emitter_for_format(AudioStreamingIsochronousFeedbackEndpointDescriptor)
AudioStreamingIsochronousFeedbackEndpointDescriptorEmitter = emitter_for_format(AudioStreamingIsochronousFeedbackEndpointDescriptor)

+ 115
- 9
usb_protocol/types/descriptors/uac.py View File

@@ -3,17 +3,14 @@
# #
""" common USB audio enums and descriptors """ """ common USB audio enums and descriptors """


from build.lib.usb_protocol.emitters import descriptor
import unittest
from usb_protocol.types import USBSynchronizationType, USBUsageType
from enum import IntEnum from enum import IntEnum


import construct import construct
from construct import this, Default


from .. import LanguageIDs
from .. import USBTransferType
from ..descriptor import \ from ..descriptor import \
DescriptorField, DescriptorNumber, DescriptorFormat, \
BCDFieldAdapter, DescriptorLength
DescriptorField, DescriptorNumber, DescriptorFormat


class AudioInterfaceClassCode(IntEnum): class AudioInterfaceClassCode(IntEnum):
AUDIO = 0x01 AUDIO = 0x01
@@ -396,7 +393,7 @@ class EmbeddedFunctionTerminalTypes(IntEnum):
AudioControlInterruptEndpointDescriptor = DescriptorFormat( AudioControlInterruptEndpointDescriptor = DescriptorFormat(
"bLength" / construct.Const(7, construct.Int8ul), "bLength" / construct.Const(7, construct.Int8ul),
"bDescriptorType" / DescriptorNumber(AudioClassSpecificDescriptorTypes.CS_ENDPOINT), "bDescriptorType" / DescriptorNumber(AudioClassSpecificDescriptorTypes.CS_ENDPOINT),
"bEndpointAddress" / DescriptorField(description="The address of the endpoint: D7: Direction (1 = IN); D6..4: Reserved; D3..0: endpoint number"),
"bEndpointAddress" / DescriptorField(description="The address of the endpoint, use USBDirection.*.from_endpoint_address()"),
"bmAttributes" / DescriptorField(description="D1..0: Transfer type (0b11 = Interrupt)", default=0b11), "bmAttributes" / DescriptorField(description="D1..0: Transfer type (0b11 = Interrupt)", default=0b11),
"wMaxPacketSize" / DescriptorField(description="Maximum packet size this endpoint is capable of. Used here to pass 6-byte interrupt information.", default=6), "wMaxPacketSize" / DescriptorField(description="Maximum packet size this endpoint is capable of. Used here to pass 6-byte interrupt information.", default=6),
"bInterval" / DescriptorField(description="Interval for polling the Interrupt endpoint") "bInterval" / DescriptorField(description="Interval for polling the Interrupt endpoint")
@@ -405,7 +402,7 @@ AudioControlInterruptEndpointDescriptor = DescriptorFormat(
AudioStreamingIsochronousEndpointDescriptor = DescriptorFormat( AudioStreamingIsochronousEndpointDescriptor = DescriptorFormat(
"bLength" / construct.Const(7, construct.Int8ul), "bLength" / construct.Const(7, construct.Int8ul),
"bDescriptorType" / DescriptorNumber(DescriptorTypes.ENDPOINT), "bDescriptorType" / DescriptorNumber(DescriptorTypes.ENDPOINT),
"bEndpointAddress" / DescriptorField(description="The address of the endpoint: D3..0: endpoint number; D6..4: Reserved; D7: direction (0=OUT / 1=IN)"),
"bEndpointAddress" / DescriptorField(description="The address of the endpoint, use USBDirection.*.from_endpoint_address()"),
"bmAttributes" / DescriptorField(description="D1..0: transfer type (01=isochronous); D3..2: synchronization type (01=asynchronous/10=adaptive/11=synchronous); D5..4: usage (00=data/10=feedback)", default=0b000101), "bmAttributes" / DescriptorField(description="D1..0: transfer type (01=isochronous); D3..2: synchronization type (01=asynchronous/10=adaptive/11=synchronous); D5..4: usage (00=data/10=feedback)", default=0b000101),
"wMaxPacketSize" / DescriptorField(description="Maximum packet size this endpoint is capable of. Used here to pass 6-byte interrupt information.", default=6), "wMaxPacketSize" / DescriptorField(description="Maximum packet size this endpoint is capable of. Used here to pass 6-byte interrupt information.", default=6),
"bInterval" / DescriptorField(description="Interval for polling the Interrupt endpoint") "bInterval" / DescriptorField(description="Interval for polling the Interrupt endpoint")
@@ -414,8 +411,117 @@ AudioStreamingIsochronousEndpointDescriptor = DescriptorFormat(
AudioStreamingIsochronousFeedbackEndpointDescriptor = DescriptorFormat( AudioStreamingIsochronousFeedbackEndpointDescriptor = DescriptorFormat(
"bLength" / construct.Const(7, construct.Int8ul), "bLength" / construct.Const(7, construct.Int8ul),
"bDescriptorType" / DescriptorNumber(DescriptorTypes.ENDPOINT), "bDescriptorType" / DescriptorNumber(DescriptorTypes.ENDPOINT),
"bEndpointAddress" / DescriptorField(description="The address of the endpoint: D3..0: endpoint number; D6..4: Reserved; D7: direction (0=OUT / 1=IN)"),
"bEndpointAddress" / DescriptorField(description="The address of the endpoint, use USBDirection.*.from_endpoint_address()"),
"bmAttributes" / DescriptorField(description="D1..0: transfer type (01=isochronous); D3..2: synchronization type (00=no sync); D5..4: usage (10=feedback)", default=0b00100001), "bmAttributes" / DescriptorField(description="D1..0: transfer type (01=isochronous); D3..2: synchronization type (00=no sync); D5..4: usage (10=feedback)", default=0b00100001),
"wMaxPacketSize" / DescriptorField(description="Maximum packet size this endpoint is capable of. Used here to pass 6-byte interrupt information.", default=6), "wMaxPacketSize" / DescriptorField(description="Maximum packet size this endpoint is capable of. Used here to pass 6-byte interrupt information.", default=6),
"bInterval" / DescriptorField(description="Interval for polling the Interrupt endpoint") "bInterval" / DescriptorField(description="Interval for polling the Interrupt endpoint")
) )

###################### MIDI #########################
class MidiStreamingInterfaceDescriptorTypes(IntEnum):
CS_UNDEFINED = 0x20
CS_DEVICE = 0x21
CS_CONFIGURATION = 0x22
CS_STRING = 0x23
CS_INTERFACE = 0x24
CS_ENDPOINT = 0x25
CS_GR_TRM_BLOCK = 0x26

class MidiStreamingInterfaceDescriptorSubtypes(IntEnum):
MS_DESCRIPTOR_UNDEFINED = 0x00
MS_HEADER = 0x01
MIDI_IN_JACK = 0x02
MIDI_OUT_JACK = 0x03
ELEMENT = 0x04

class MidiStreamingEndpointDescriptorSubtypes(IntEnum):
DESCRIPTOR_UNDEFINED = 0x00
MS_GENERAL = 0x01
MS_GENERAL_2_0 = 0x02

class MidiStreamingInterfaceHeaderClassRevision(IntEnum):
MS_MIDI_1_0 = 0x0100
MS_MIDI_2_0 = 0x0200

class MidiStreamingJackTypes(IntEnum):
JACK_TYPE_UNDEFINED = 0x00
EMBEDDED = 0x01
EXTERNAL = 0x02

StandardMidiStreamingInterfaceDescriptor = DescriptorFormat(
"bLength" / construct.Const(9, construct.Int8ul),
"bDescriptorType" / DescriptorNumber(DescriptorTypes.INTERFACE),
"bInterfaceNumber" / DescriptorField(description="ID of the streaming interface"),
"bAlternateSetting" / DescriptorField(description="alternate setting number for the interface", default=0),
"bNumEndpoints" / DescriptorField(description="Number of data endpoints used (excluding endpoint 0). Can be: 0 (no data endpoint); 1 (data endpoint); 2 (data + explicit feedback endpoint)", default=0),
"bInterfaceClass" / DescriptorNumber(AudioInterfaceClassCode.AUDIO),
"bInterfaceSubClass" / DescriptorNumber(AudioInterfaceSubclassCodes.MIDI_STREAMING),
"bInterfaceProtocol" / DescriptorNumber(0),
"iInterface" / DescriptorField(description="index of a string descriptor describing this interface (0 = unused)", default=0)
)

ClassSpecificMidiStreamingInterfaceHeaderDescriptor = DescriptorFormat(
"bLength" / construct.Const(7, construct.Int8ul),
"bDescriptorType" / DescriptorNumber(AudioClassSpecificDescriptorTypes.CS_INTERFACE),
"bDescriptorSubtype" / DescriptorNumber(AudioClassSpecificACInterfaceDescriptorSubtypes.HEADER),
"bcdADC" / DescriptorField(description="Midi Streaming Class specification release version", default=1.0),
"wTotalLength" / DescriptorField(description="Total number of bytes of the class-specific MIDIStreaming interface descriptor. Includes the combined length of this descriptor header and all Jack and Element descriptors."),
)

StandardMidiStreamingDataEndpointDescriptor = DescriptorFormat(
"bLength" / construct.Const(7, construct.Int8ul),
"bDescriptorType" / DescriptorNumber(AudioClassSpecificDescriptorTypes.CS_ENDPOINT),
"bEndpointAddress" / DescriptorField(description="endpoint address, use USBDirection.*.from_endpoint_address()"),
"bmAttributes" / DescriptorField(description="endpoint type, see USBTransferType (only NONE, BULK or INTERRUPT allowed)", default=USBTransferType.BULK),
"wMaxPacketSize" / DescriptorField(description="Maximum packet size this endpoint is capable of sending or receiving"),
"bInterval" / DescriptorField(description="Interval for polling endpoint for Interrupt data transfers. For bulk endpoints this field is ignored and must be reset to 0", default=0)
)

MidiInJackDescriptor = DescriptorFormat(
"bLength" / construct.Const(6, construct.Int8ul),
"bDescriptorType" / DescriptorNumber(AudioClassSpecificDescriptorTypes.CS_INTERFACE),
"bDescriptorSubtype" / DescriptorNumber(MidiStreamingInterfaceDescriptorSubtypes.MIDI_IN_JACK),
"bJackType" / DescriptorField(description="see MidiStreamingJackTypes"),
"bJackID" / DescriptorField(description="Constant uniquely identifying the MIDI IN Jack within the USB-MIDI function"),
"iJack" / DescriptorField(description="index of a string descriptor describing this jack (0 = unused)", default=0)
)

MidiOutJackDescriptorHead = DescriptorFormat(
"bLength" / DescriptorField(description="Size of this descriptor, in bytes: 6+2*p"),
"bDescriptorType" / DescriptorNumber(AudioClassSpecificDescriptorTypes.CS_INTERFACE),
"bDescriptorSubtype" / DescriptorNumber(MidiStreamingInterfaceDescriptorSubtypes.MIDI_OUT_JACK),
"bJackType" / DescriptorField(description="see MidiStreamingJackTypes"),
"bJackID" / DescriptorField(description="Constant uniquely identifying the MIDI IN Jack within the USB-MIDI function"),
"bNrInputPins" / DescriptorField(description="Number of Input Pins of this MIDI OUT Jack: p", default=1)
)

MidiOutJackDescriptorElement = DescriptorFormat(
"baSourceID" / construct.Int8ul, # ID of the Entity to which the first Input Pin of this MIDI OUT Jack is connected
"BaSourcePin" / construct.Int8ul, #Output Pin number of the Entity to which the first Input Pin of this MIDI OUT Jack is connected
)

MidiOutJackDescriptorFoot = DescriptorFormat(
"iJack" / DescriptorField(description="index of a string descriptor describing this jack (0 = unused)", default=0)
)

StandardMidiStreamingBulkDataEndpointDescriptor = DescriptorFormat(
"bLength" / construct.Const(9, construct.Int8ul),
"bDescriptorType" / DescriptorNumber(DescriptorTypes.ENDPOINT),
"bEndpointAddress" / DescriptorField(description="The address of the endpoint, use USBDirection.*.from_endpoint_address()"),
"bmAttributes" / DescriptorField(description="D1..0: transfer type (10=bulk), D3..2: synchronization type (00=no sync);", default=USBTransferType.BULK | USBSynchronizationType.NONE | USBUsageType.DATA),
"wMaxPacketSize" / DescriptorField(description="Maximum packet size this endpoint is capable of", default=512),
"bInterval" / DescriptorField(description="Interval for polling endpoint for data transfers expressed in milliseconds. This field is ignored for bulk endpoints. Must be set to 0", default=0),
"bRefresh" / DescriptorField(description="must be set to 0", default=0),
"bSynchAddress" / DescriptorField(description="The address of the endpoint used to communicate synchronization information if required by this endpoint. Must be set to 0", default=0)
)

ClassSpecificMidiStreamingBulkDataEndpointDescriptorHead = DescriptorFormat(
"bLength" / DescriptorField(description="Size of this descriptor, in bytes: 4+n"),
"bDescriptorType" / DescriptorNumber(AudioClassSpecificDescriptorTypes.CS_ENDPOINT),
"bDescriptorSubtype" / DescriptorField(description="see MidiStreamingEndpointDescriptorSubtypes", default=MidiStreamingEndpointDescriptorSubtypes.MS_GENERAL),
"bNumEmbMIDIJack" / DescriptorField(description="Number of Embedded MIDI Jacks: n", default=1)
)

ClassSpecificMidiStreamingBulkDataEndpointDescriptorElement = DescriptorFormat(
"baAssocJackID" / construct.Int8ul # ID of the embedded eack that is associated with this endpoint
)

+ 43
- 8
usb_protocol/types/descriptors/uac2.py View File

@@ -5,17 +5,13 @@
NOTE: This is not complete yet and will be extended as needed NOTE: This is not complete yet and will be extended as needed
""" """


from build.lib.usb_protocol.emitters import descriptor
import unittest
from enum import IntEnum
from usb_protocol.emitters import descriptor
from enum import IntEnum


import construct import construct
from construct import this, Default


from .. import LanguageIDs
from ..descriptor import \ from ..descriptor import \
DescriptorField, DescriptorNumber, DescriptorFormat, \
BCDFieldAdapter, DescriptorLength
DescriptorField, DescriptorNumber, DescriptorFormat


from .uac import * from .uac import *


@@ -303,4 +299,43 @@ ClassSpecificAudioStreamingIsochronousAudioDataEndpointDescriptor = DescriptorFo
"bmControls" / DescriptorField(description="D1..0: pitch control D3..2: data overrun control; D5..4: data underrun control;", default=0), "bmControls" / DescriptorField(description="D1..0: pitch control D3..2: data overrun control; D5..4: data underrun control;", default=0),
"bLockDelayUnits" / DescriptorField(description="wLockDelay unit: 0: undefined; 1: milliseconds; 2: decoded PCM samples;", default=0), "bLockDelayUnits" / DescriptorField(description="wLockDelay unit: 0: undefined; 1: milliseconds; 2: decoded PCM samples;", default=0),
"wLockDelay" / DescriptorField(description="the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units see bLockDelayUnits", default=0) "wLockDelay" / DescriptorField(description="the time it takes this endpoint to reliably lock its internal clock recovery circuitry. Units see bLockDelayUnits", default=0)
)
)

###################### MIDI #########################

class MidiStreamingGroupTerminalBlockDescriptorSubtypes(IntEnum):
GR_TRM_BLOCK_UNDEFINED = 0x00
GR_TRM_BLOCK_HEADER = 0x01
GR_TRM_BLOCK = 0x02

class GroupTerminalBlockType(IntEnum):
BIDIRECTIONAL = 0x00
INPUT_ONLY = 0x01
OUTPUT_ONLY = 0x02

class GroupTerminalDefaultMidiProtocol(IntEnum):
USE_MIDI_CI = 0x00
MIDI_1_0_UP_TO_64_BITS = 0x01
MIDI_1_0_UP_TO_64_BITS_AND_JRTS = 0x02
MIDI_1_0_UP_TO_128_BITS = 0x03
MIDI_1_0_UP_TO_128_BITS_AND_JRTS = 0x04
MIDI_2_0 = 0x11
MIDI_2_0_AND_JRTS = 0x12

class GroupTerminalNumber(IntEnum):
GROUP_1 = 0x00
GROUP_2 = 0x01
GROUP_3 = 0x02
GROUP_4 = 0x03
GROUP_5 = 0x04
GROUP_6 = 0x05
GROUP_7 = 0x06
GROUP_8 = 0x07
GROUP_9 = 0x08
GROUP_10 = 0x09
GROUP_11 = 0x0A
GROUP_12 = 0x0B
GROUP_13 = 0x0C
GROUP_14 = 0x0D
GROUP_15 = 0x0E
GROUP_16 = 0x0F

Loading…
Cancel
Save