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 ...emitters.descriptor import ComplexDescriptorEmitter

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

# Create our emitters.
InterfaceAssociationDescriptorEmitter = emitter_for_format(InterfaceAssociationDescriptor)
StandardAudioControlInterfaceDescriptorEmitter = emitter_for_format(StandardAudioControlInterfaceDescriptor)
@@ -36,4 +38,4 @@ ExtendedTypeIIIFormatTypeDescriptorEmitter = emitt
ClassSpecificAudioStreamingIsochronousAudioDataEndpointDescriptorEmitter = emitter_for_format(ClassSpecificAudioStreamingIsochronousAudioDataEndpointDescriptor)
AudioControlInterruptEndpointDescriptorEmitter = emitter_for_format(AudioControlInterruptEndpointDescriptor)
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 """

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

import construct
from construct import this, Default

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

class AudioInterfaceClassCode(IntEnum):
AUDIO = 0x01
@@ -396,7 +393,7 @@ class EmbeddedFunctionTerminalTypes(IntEnum):
AudioControlInterruptEndpointDescriptor = DescriptorFormat(
"bLength" / construct.Const(7, construct.Int8ul),
"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),
"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")
@@ -405,7 +402,7 @@ AudioControlInterruptEndpointDescriptor = DescriptorFormat(
AudioStreamingIsochronousEndpointDescriptor = DescriptorFormat(
"bLength" / construct.Const(7, construct.Int8ul),
"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),
"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")
@@ -414,8 +411,117 @@ AudioStreamingIsochronousEndpointDescriptor = DescriptorFormat(
AudioStreamingIsochronousFeedbackEndpointDescriptor = DescriptorFormat(
"bLength" / construct.Const(7, construct.Int8ul),
"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),
"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")
)

###################### 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
"""

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
from construct import this, Default

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

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),
"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)
)
)

###################### 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