/* * Driver for USB Audio Device Class devices. * Copyright (c) 2009-13 S.Zharski * Distributed under the tems of the MIT license. * */ #include "AudioControlInterface.h" #include #include "Device.h" #include "Driver.h" #include "Settings.h" // control id is encoded in following way // CS CN ID IF where: // CS - control selector // CN - channel // ID - id of this feature unit // IF - interface #define CTL_ID(_CS, _CN, _ID, _IF) \ (((_CS) << 24) | ((_CN) << 16) | ((_ID) << 8) | (_IF)) #define CS_FROM_CTLID(_ID) (0xff & ((_ID) >> 24)) #define CN_FROM_CTLID(_ID) (0xff & ((_ID) >> 16)) #define ID_FROM_CTLID(_ID) (0xff & ((_ID) >> 8)) #define REQ_VALUE(_ID) (0xffff & ((_ID) >> 16)) #define REQ_INDEX(_ID) (0xffff & (_ID)) struct _Designation { uint32 ch; uint32 bus; } gDesignations[AudioControlInterface::kChannels] = { { B_CHANNEL_LEFT, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_RIGHT, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_CENTER, B_CHANNEL_SURROUND_BUS }, { B_CHANNEL_SUB, B_CHANNEL_SURROUND_BUS }, { B_CHANNEL_REARLEFT, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_REARRIGHT, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_FRONT_LEFT_CENTER, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_FRONT_RIGHT_CENTER, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_BACK_CENTER, B_CHANNEL_SURROUND_BUS }, { B_CHANNEL_SIDE_LEFT, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_SIDE_RIGHT, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_TOP_CENTER, B_CHANNEL_SURROUND_BUS }, { B_CHANNEL_TOP_FRONT_LEFT, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_TOP_FRONT_CENTER, B_CHANNEL_SURROUND_BUS }, { B_CHANNEL_TOP_FRONT_RIGHT, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_TOP_BACK_LEFT, B_CHANNEL_STEREO_BUS }, { B_CHANNEL_TOP_BACK_CENTER, B_CHANNEL_SURROUND_BUS }, { B_CHANNEL_TOP_BACK_RIGHT, B_CHANNEL_STEREO_BUS } }; struct _MixPageCollector : public Vector { _MixPageCollector(const char* pageName) { multi_mix_control page; memset(&page, 0, sizeof(multi_mix_control)); page.flags = B_MULTI_MIX_GROUP; strlcpy(page.name, pageName, sizeof(page.name)); PushBack(page); } }; _AudioControl::_AudioControl(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : fStatus(B_NO_INIT), fInterface(interface), fSubType(Header->descriptor_subtype), fID(0), fSourceID(0), fStringIndex(0) { } _AudioControl::~_AudioControl() { } AudioChannelCluster* _AudioControl::OutCluster() { if (SourceID() == 0 || fInterface == NULL) return NULL; _AudioControl* control = fInterface->Find(SourceID()); if (control == NULL) return NULL; return control->OutCluster(); } AudioChannelCluster::AudioChannelCluster() : fOutChannelsNumber(0), fChannelsConfig(0), fChannelNames(0) { } AudioChannelCluster::~AudioChannelCluster() { } bool AudioChannelCluster::HasChannel(uint32 location) { return (fChannelsConfig & location) == location; } _Terminal::_Terminal(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioControl(interface, Header), fTerminalType(0), fAssociatedTerminal(0), fClockSourceId(0), fControlsBitmap(0) { } _Terminal::~_Terminal() { } const char* _Terminal::Name() { return _GetTerminalDescription(fTerminalType); } bool _Terminal::IsUSBIO() { return (fTerminalType & 0xff00) == USB_AUDIO_UNDEFINED_USB_IO; } const char* _Terminal::_GetTerminalDescription(uint16 TerminalType) { static struct _pair { uint16 type; const char* description; } termInfoPairs[] = { // USB Terminal Types { USB_AUDIO_UNDEFINED_USB_IO, "USB I/O" }, { USB_AUDIO_STREAMING_USB_IO, "USB I/O" }, { USB_AUDIO_VENDOR_USB_IO, "Vendor USB I/O" }, // Input Terminal Types { USB_AUDIO_UNDEFINED_IN, "Undefined Input" }, { USB_AUDIO_MICROPHONE_IN, "Microphone" }, { USB_AUDIO_DESKTOPMIC_IN, "Desktop Microphone" }, { USB_AUDIO_PERSONALMIC_IN, "Personal Microphone" }, { USB_AUDIO_OMNI_MIC_IN, "Omni-directional Mic" }, { USB_AUDIO_MICS_ARRAY_IN, "Microphone Array" }, { USB_AUDIO_PROC_MICS_ARRAY_IN, "Processing Mic Array" }, // Output Terminal Types { USB_AUDIO_UNDEFINED_OUT, "Undefined Output" }, { USB_AUDIO_SPEAKER_OUT, "Speaker" }, { USB_AUDIO_HEAD_PHONES_OUT, "Headphones" }, { USB_AUDIO_HMD_AUDIO_OUT, "Head Mounted Disp.Audio" }, { USB_AUDIO_DESKTOP_SPEAKER, "Desktop Speaker" }, { USB_AUDIO_ROOM_SPEAKER, "Room Speaker" }, { USB_AUDIO_COMM_SPEAKER, "Communication Speaker" }, { USB_AUDIO_LFE_SPEAKER, "LFE Speaker" }, // Bi-directional Terminal Types { USB_AUDIO_UNDEFINED_IO, "Undefined I/O" }, { USB_AUDIO_HANDSET_IO, "Handset" }, { USB_AUDIO_HEADSET_IO, "Headset" }, { USB_AUDIO_SPEAKER_PHONE_IO, "Speakerphone" }, { USB_AUDIO_SPEAKER_PHONEES_IO, "Echo-supp Speakerphone" }, { USB_AUDIO_SPEAKER_PHONEEC_IO, "Echo-cancel Speakerphone" }, // Telephony Terminal Types { USB_AUDIO_UNDEF_TELEPHONY_IO, "Undefined Telephony" }, { USB_AUDIO_PHONE_LINE_IO, "Phone Line" }, { USB_AUDIO_TELEPHONE_IO, "Telephone" }, { USB_AUDIO_DOWNLINE_PHONE_IO, "Down Line Phone" }, // External Terminal Types { USB_AUDIO_UNDEFINEDEXT_IO, "Undefined External I/O" }, { USB_AUDIO_ANALOG_CONNECTOR_IO, "Analog Connector" }, { USB_AUDIO_DAINTERFACE_IO, "Digital Audio Interface" }, { USB_AUDIO_LINE_CONNECTOR_IO, "Line Connector" }, { USB_AUDIO_LEGACY_CONNECTOR_IO, "LegacyAudioConnector" }, { USB_AUDIO_SPDIF_INTERFACE_IO, "S/PDIF Interface" }, { USB_AUDIO_DA1394_STREAM_IO, "1394 DA Stream" }, { USB_AUDIO_DV1394_STREAMSOUND_IO, "1394 DV Stream Soundtrack" }, { USB_AUDIO_ADAT_LIGHTPIPE_IO, "Alesis DAT Stream" }, { USB_AUDIO_TDIF_IO, "Tascam Digital Interface" }, { USB_AUDIO_MADI_IO, "AES Multi-channel interface" }, // Embedded Terminal Types { USB_AUDIO_UNDEF_EMBEDDED_IO, "Undefined Embedded I/O" }, { USB_AUDIO_LC_NOISE_SOURCE_OUT, "Level Calibration Noise Source" }, { USB_AUDIO_EQUALIZATION_NOISE_OUT, "Equalization Noise" }, { USB_AUDIO_CDPLAYER_IN, "CD Player" }, { USB_AUDIO_DAT_IO, "DAT" }, { USB_AUDIO_DCC_IO, "DCC" }, { USB_AUDIO_MINI_DISK_IO, "Mini Disk" }, { USB_AUDIO_ANALOG_TAPE_IO, "Analog Tape" }, { USB_AUDIO_PHONOGRAPH_IN, "Phonograph" }, { USB_AUDIO_VCR_AUDIO_IN, "VCR Audio" }, { USB_AUDIO_VIDEO_DISC_AUDIO_IN, "Video Disc Audio" }, { USB_AUDIO_DVD_AUDIO_IN, "DVD Audio" }, { USB_AUDIO_TV_TUNER_AUDIO_IN, "TV Tuner Audio" }, { USB_AUDIO_SAT_RECEIVER_AUDIO_IN, "Satellite Receiver Audio" }, { USB_AUDIO_CABLE_TUNER_AUDIO_IN, "Cable Tuner Audio" }, { USB_AUDIO_DSS_AUDIO_IN, "DSS Audio" }, { USB_AUDIO_RADIO_RECEIVER_IN, "Radio Receiver" }, { USB_AUDIO_RADIO_TRANSMITTER_IN, "Radio Transmitter" }, { USB_AUDIO_MULTI_TRACK_RECORDER_IO,"Multi-track Recorder" }, { USB_AUDIO_SYNTHESIZER_IO, "Synthesizer" }, { USB_AUDIO_PIANO_IO, "Piano" }, { USB_AUDIO_GUITAR_IO, "Guitar" }, { USB_AUDIO_DRUMS_IO, "Percussion Instrument" }, { USB_AUDIO_INSTRUMENT_IO, "Musical Instrument" } }; for (size_t i = 0; i < B_COUNT_OF(termInfoPairs); i++) if (termInfoPairs[i].type == TerminalType) return termInfoPairs[i].description; TRACE(ERR, "Unknown Terminal Type: %#06x", TerminalType); return "Unknown"; } InputTerminal::InputTerminal(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioChannelCluster<_Terminal>(interface, Header) { usb_audio_input_terminal_descriptor* Terminal = (usb_audio_input_terminal_descriptor*) Header; fID = Terminal->terminal_id; fTerminalType = Terminal->terminal_type; fAssociatedTerminal = Terminal->assoc_terminal; TRACE(UAC, "Input Terminal ID:%d >>>\n", fID); TRACE(UAC, "Terminal type:%s (%#06x)\n", _GetTerminalDescription(fTerminalType), fTerminalType); TRACE(UAC, "Assoc.terminal:%d\n", fAssociatedTerminal); if (fInterface->SpecReleaseNumber() < 0x200) { fOutChannelsNumber = Terminal->r1.num_channels; fChannelsConfig = Terminal->r1.channel_config; fChannelNames = Terminal->r1.channel_names; fStringIndex = Terminal->r1.terminal; } else { fClockSourceId = Terminal->r2.clock_source_id; fOutChannelsNumber = Terminal->r2.num_channels; fChannelsConfig = Terminal->r2.channel_config; fChannelNames = Terminal->r2.channel_names; fControlsBitmap = Terminal->r2.bm_controls; fStringIndex = Terminal->r2.terminal; TRACE(UAC, "Clock Source ID:%d\n", fClockSourceId); TRACE(UAC, "Controls Bitmap:%#04x\n", fControlsBitmap); } TRACE(UAC, "Out.channels num:%d\n", fOutChannelsNumber); TRACE(UAC, "Channels config:%#06x\n", fChannelsConfig); TRACE(UAC, "Channels names:%d\n", fChannelNames); TRACE(UAC, "StringIndex:%d\n", fStringIndex); fStatus = B_OK; } InputTerminal::~InputTerminal() { } const char* InputTerminal::Name() { if (fTerminalType == USB_AUDIO_STREAMING_USB_IO) return "USB Input"; return _Terminal::Name(); } OutputTerminal::OutputTerminal(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _Terminal(interface, Header) { usb_audio_output_terminal_descriptor* Terminal = (usb_audio_output_terminal_descriptor*) Header; fID = Terminal->terminal_id; fTerminalType = Terminal->terminal_type; fAssociatedTerminal = Terminal->assoc_terminal; fSourceID = Terminal->source_id; TRACE(UAC, "Output Terminal ID:%d >>>\n", fID); TRACE(UAC, "Terminal type:%s (%#06x)\n", _GetTerminalDescription(fTerminalType), fTerminalType); TRACE(UAC, "Assoc.terminal:%d\n", fAssociatedTerminal); TRACE(UAC, "Source ID:%d\n", fSourceID); if (fInterface->SpecReleaseNumber() < 0x200) { fStringIndex = Terminal->r1.terminal; } else { fClockSourceId = Terminal->r2.clock_source_id; fControlsBitmap = Terminal->r2.bm_controls; fStringIndex = Terminal->r2.terminal; TRACE(UAC, "Clock Source ID:%d\n", fClockSourceId); TRACE(UAC, "Controls Bitmap:%#04x\n", fControlsBitmap); } TRACE(UAC, "StringIndex:%d\n", fStringIndex); fStatus = B_OK; } OutputTerminal::~OutputTerminal() { } const char* OutputTerminal::Name() { if (fTerminalType == USB_AUDIO_STREAMING_USB_IO) return "USB Output"; return _Terminal::Name(); } MixerUnit::MixerUnit(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioChannelCluster<_AudioControl>(interface, Header), fBmControlsR2(0) { usb_audio_mixer_unit_descriptor* Mixer = (usb_audio_mixer_unit_descriptor*) Header; fID = Mixer->unit_id; TRACE(UAC, "Mixer ID:%d >>>\n", fID); TRACE(UAC, "Number of input pins:%d\n", Mixer->num_input_pins); for (size_t i = 0; i < Mixer->num_input_pins; i++) { fInputPins.PushBack(Mixer->input_pins[i]); TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]); } uint8* mixerControlsData = NULL; uint8 mixerControlsSize = 0; if (fInterface->SpecReleaseNumber() < 0x200) { usb_audio_output_channels_descriptor_r1* OutChannels = (usb_audio_output_channels_descriptor_r1*) &Mixer->input_pins[Mixer->num_input_pins]; fOutChannelsNumber = OutChannels->num_output_pins; fChannelsConfig = OutChannels->channel_config; fChannelNames = OutChannels->channel_names; mixerControlsData = (uint8*) ++OutChannels; mixerControlsSize = Mixer->length - 10 - Mixer->num_input_pins; fStringIndex = *(mixerControlsData + mixerControlsSize); #if 0 // TEST if (fOutChannelsNumber > 2) { // fOutChannelsNumber = 2; // fChannelsConfig = 0x03; mixerControlsData[0] = 0x80; mixerControlsData[1] = 0x40; mixerControlsData[2] = 0x20; mixerControlsData[3] = 0x10; mixerControlsData[4] = 0x08; mixerControlsData[5] = 0x04; mixerControlsData[6] = 0x02; mixerControlsData[7] = 0x01; mixerControlsData[8] = 0x80; mixerControlsData[9] = 0x40; mixerControlsData[10] = 0x02; mixerControlsData[11] = 0x01; } #endif } else { usb_audio_output_channels_descriptor* OutChannels = (usb_audio_output_channels_descriptor*) &Mixer->input_pins[Mixer->num_input_pins]; fOutChannelsNumber = OutChannels->num_output_pins; fChannelsConfig = OutChannels->channel_config; fChannelNames = OutChannels->channel_names; mixerControlsData = (uint8*) ++OutChannels; mixerControlsSize = Mixer->length - 13 - Mixer->num_input_pins; fBmControlsR2 = *(mixerControlsData + mixerControlsSize); fStringIndex = *(mixerControlsData + mixerControlsSize + 1); TRACE(UAC, "Control Bitmap:%#04x\n", fBmControlsR2); } TRACE(UAC, "Out channels number:%d\n", fOutChannelsNumber); TRACE(UAC, "Out channels config:%#06x\n", fChannelsConfig); TRACE(UAC, "Out channels names:%d\n", fChannelNames); TRACE(UAC, "Controls Size:%d\n", mixerControlsSize); for (size_t i = 0; i < mixerControlsSize; i++) { fControlsBitmap.PushBack(mixerControlsData[i]); TRACE(UAC, "Controls Data[%d]:%#x\n", i, fControlsBitmap[i]); } TRACE(UAC, "StringIndex:%d\n", fStringIndex); fStatus = B_OK; } MixerUnit::~MixerUnit() { } bool MixerUnit::HasProgrammableControls() { for (int i = 0; i < fControlsBitmap.Count(); i++) if (fControlsBitmap[i] != 0) return true; return false; } bool MixerUnit::IsControlProgrammable(int inChannel, int outChannel) { AudioChannelCluster* outCluster = OutCluster(); if (outCluster == NULL) { TRACE(ERR, "Output cluster is not valid.\n"); return false; } bool result = false; if (outChannel < outCluster->ChannelsCount()) { int index = inChannel * outCluster->ChannelsCount()+ outChannel; result = (fControlsBitmap[index >> 3] & (0x80 >> (index & 7))) != 0; } // TRACE(UAC, "in:%d out:%d is %s\n", // inChannel, outChannel, result ? "on" : "off"); return result; } SelectorUnit::SelectorUnit(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioControl(interface, Header), fControlsBitmap(0) { usb_audio_selector_unit_descriptor* Selector = (usb_audio_selector_unit_descriptor*) Header; fID = Selector->unit_id; TRACE(UAC, "Selector ID:%d >>>\n", fID); TRACE(UAC, "Number of input pins:%d\n", Selector->num_input_pins); for (size_t i = 0; i < Selector->num_input_pins; i++) { fInputPins.PushBack(Selector->input_pins[i]); TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]); } if (fInterface->SpecReleaseNumber() < 0x200) { fStringIndex = Selector->input_pins[Selector->num_input_pins]; } else { fControlsBitmap = Selector->input_pins[Selector->num_input_pins]; fStringIndex = Selector->input_pins[Selector->num_input_pins + 1]; TRACE(UAC, "Controls Bitmap:%d\n", fControlsBitmap); } TRACE(UAC, "StringIndex:%d\n", fStringIndex); fStatus = B_OK; } SelectorUnit::~SelectorUnit() { } AudioChannelCluster* SelectorUnit::OutCluster() { if (fInterface == NULL) return NULL; for (int i = 0; i < fInputPins.Count(); i++) { _AudioControl* control = fInterface->Find(fInputPins[i]); if (control == NULL) continue; // selector has the same channels number in the // out cluster as anyone of his inputs if (control->OutCluster() != NULL) return control->OutCluster(); } return NULL; } FeatureUnit::FeatureUnit(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioControl(interface, Header) { usb_audio_feature_unit_descriptor* Feature = (usb_audio_feature_unit_descriptor*) Header; fID = Feature->unit_id; TRACE(UAC, "Feature ID:%d >>>\n", fID); fSourceID = Feature->source_id; TRACE(UAC, "Source ID:%d\n", fSourceID); uint8 controlSize = 4; uint8 channelsCount = (Feature->length - 6) / controlSize; uint8* ControlsBitmapPointer = (uint8*)&Feature->r2.bma_controls[0]; if (fInterface->SpecReleaseNumber() < 0x200) { controlSize = Feature->r1.control_size; channelsCount = (Feature->length - 7) / Feature->r1.control_size; ControlsBitmapPointer = &Feature->r1.bma_controls[0]; } TRACE(UAC, "Channel bitmap size:%d\n", controlSize); TRACE(UAC, "Channels number:%d\n", channelsCount - 1); // not add master! for (size_t i = 0; i < channelsCount; i++) { uint8* controlPointer = &ControlsBitmapPointer[i* controlSize]; switch(controlSize) { case 1: fControlBitmaps.PushBack(*controlPointer); break; case 2: fControlBitmaps.PushBack(*(uint16*)controlPointer); break; case 4: fControlBitmaps.PushBack(*(uint32*)controlPointer); break; default: TRACE(ERR, "Feature control of unsupported size %d ignored\n", controlSize); continue; } NormalizeAndTraceChannel(i); } fStringIndex = ControlsBitmapPointer[channelsCount* controlSize]; TRACE(UAC, "StringIndex:%d\n", fStringIndex); fStatus = B_OK; } FeatureUnit::~FeatureUnit() { } const char* FeatureUnit::Name() { // first check if source of this FU is an input terminal _AudioControl* control = fInterface->Find(fSourceID); while (control != NULL) { if (control->SubType() != USB_AUDIO_AC_INPUT_TERMINAL) break; // USB I/O terminal is a not good candidate to use it's name if (static_cast<_Terminal*>(control)->IsUSBIO()) break; // use the name of source input terminal as name of this FU return control->Name(); } // check if output of this FU is connected to output terminal control = fInterface->FindOutputTerminal(fID); while (control != NULL) { if (control->SubType() != USB_AUDIO_AC_OUTPUT_TERMINAL) break; // USB I/O terminal is a not good candidate to use it's name if (static_cast<_Terminal*>(control)->IsUSBIO()) break; // use the name of this output terminal as name of this FU return control->Name(); } // otherwise get the generic name of this FU's source control = fInterface->Find(fSourceID); if (control != NULL && control->Name() != NULL && strlen(control->Name()) > 0) return control->Name(); // I have no more ideas, have you one? return "Unknown"; } bool FeatureUnit::HasControl(int32 Channel, uint32 Control) { if (Channel >= fControlBitmaps.Count()) { TRACE(ERR, "Out of limits error of retrieving control %#010x " "for channel %d\n", Control, Channel); return false; } return (Control & fControlBitmaps[Channel]) != 0; } void FeatureUnit::NormalizeAndTraceChannel(int32 Channel) { if (Channel >= fControlBitmaps.Count()) { TRACE(ERR, "Out of limits error of tracing channel %d\n", Channel); return; } struct _RemapInfo { uint32 rev1Bits; uint32 rev2Bits; const char* name; } remapInfos[] = { { BMA_CTL_MUTE_R1, BMA_CTL_MUTE, "Mute" }, { BMA_CTL_VOLUME_R1, BMA_CTL_VOLUME, "Volume" }, { BMA_CTL_BASS_R1, BMA_CTL_BASS, "Bass" }, { BMA_CTL_MID_R1, BMA_CTL_MID, "Mid" }, { BMA_CTL_TREBLE_R1, BMA_CTL_TREBLE, "Treble" }, { BMA_CTL_GRAPHEQ_R1, BMA_CTL_GRAPHEQ, "Graphic Equalizer" }, { BMA_CTL_AUTOGAIN_R1, BMA_CTL_AUTOGAIN, "Automatic Gain"}, { BMA_CTL_DELAY_R1, BMA_CTL_DELAY, "Delay" }, { BMA_CTL_BASSBOOST_R1, BMA_CTL_BASSBOOST, "Bass Boost" }, { BMA_CTL_LOUDNESS_R1, BMA_CTL_LOUDNESS, "Loudness" }, { 0, BMA_CTL_INPUTGAIN, "InputGain" }, { 0, BMA_CTL_INPUTGAINPAD, "InputGainPad" }, { 0, BMA_CTL_PHASEINVERTER, "PhaseInverter" }, { 0, BMA_CTL_UNDERFLOW, "Underflow" }, { 0, BMA_CTL_OVERFLOW, "Overflow" } }; if (Channel == 0) TRACE(UAC, "Master channel bitmap:%#x\n", fControlBitmaps[Channel]); else TRACE(UAC, "Channel %d bitmap:%#x\n", Channel, fControlBitmaps[Channel]); bool isRev1 = (fInterface->SpecReleaseNumber() < 0x200); uint32 remappedBitmap = 0; for (size_t i = 0; i < B_COUNT_OF(remapInfos); i++) { uint32 bits = isRev1 ? remapInfos[i].rev1Bits : remapInfos[i].rev2Bits; if ((fControlBitmaps[Channel] & bits) > 0) { if (isRev1) remappedBitmap |= remapInfos[i].rev2Bits; TRACE(UAC, "\t%s\n", remapInfos[i].name); } } if (isRev1) { TRACE(UAC, "\t%#08x -> %#08x.\n", fControlBitmaps[Channel], remappedBitmap); fControlBitmaps[Channel] = remappedBitmap; } } EffectUnit::EffectUnit(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioControl(interface, Header) { usb_audio_input_terminal_descriptor* descriptor = (usb_audio_input_terminal_descriptor*) Header; TRACE(UAC, "Effect Unit:%d >>>\n", descriptor->terminal_id); } EffectUnit::~EffectUnit() { } ProcessingUnit::ProcessingUnit(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioChannelCluster<_AudioControl>(interface, Header), fProcessType(0), fControlsBitmap(0) { usb_audio_processing_unit_descriptor* Processing = (usb_audio_processing_unit_descriptor*) Header; fID = Processing->unit_id; TRACE(UAC, "Processing ID:%d >>>\n", fID); fProcessType = Processing->process_type; TRACE(UAC, "Processing Type:%d\n", fProcessType); TRACE(UAC, "Number of input pins:%d\n", Processing->num_input_pins); for (size_t i = 0; i < Processing->num_input_pins; i++) { fInputPins.PushBack(Processing->input_pins[i]); TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]); } if (fInterface->SpecReleaseNumber() < 0x200) { usb_audio_output_channels_descriptor_r1* OutChannels = (usb_audio_output_channels_descriptor_r1*) &Processing->input_pins[Processing->num_input_pins]; fOutChannelsNumber = OutChannels->num_output_pins; fChannelsConfig = OutChannels->channel_config; fChannelNames = OutChannels->channel_names; } else { usb_audio_output_channels_descriptor* OutChannels = (usb_audio_output_channels_descriptor*) &Processing->input_pins[Processing->num_input_pins]; fOutChannelsNumber = OutChannels->num_output_pins; fChannelsConfig = OutChannels->channel_config; fChannelNames = OutChannels->channel_names; } TRACE(UAC, "Out channels number:%d\n", fOutChannelsNumber); TRACE(UAC, "Out channels config:%#06x\n", fChannelsConfig); TRACE(UAC, "Out channels names:%d\n", fChannelNames); /* uint8 controlsSize = Processing->length - 10 - Processing->num_input_pins; TRACE(UAC, "Controls Size:%d\n", controlsSize); uint8* controlsData = (uint8*) ++OutChannels; for (size_t i = 0; i < controlsSize; i++) { fProgrammableControls.PushBack(controlsData[i]); TRACE(UAC, "Controls Data[%d]:%#x\n", i, controlsData[i]); } fStringIndex = *(controlsData + controlsSize); TRACE(UAC, "StringIndex:%d\n", fStringIndex); */ fStatus = B_OK; } ProcessingUnit::~ProcessingUnit() { } ExtensionUnit::ExtensionUnit(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioChannelCluster<_AudioControl>(interface, Header), fExtensionCode(0), fControlsBitmap(0) { usb_audio_extension_unit_descriptor* Extension = (usb_audio_extension_unit_descriptor*) Header; fID = Extension->unit_id; TRACE(UAC, "Extension ID:%d >>>\n", fID); fExtensionCode = Extension->extension_code; TRACE(UAC, "Extension Type:%d\n", fExtensionCode); TRACE(UAC, "Number of input pins:%d\n", Extension->num_input_pins); for (size_t i = 0; i < Extension->num_input_pins; i++) { fInputPins.PushBack(Extension->input_pins[i]); TRACE(UAC, "Input pin #%d:%d\n", i, fInputPins[i]); } if (fInterface->SpecReleaseNumber() < 0x200) { usb_audio_output_channels_descriptor_r1* OutChannels = (usb_audio_output_channels_descriptor_r1*) &Extension->input_pins[Extension->num_input_pins]; fOutChannelsNumber = OutChannels->num_output_pins; fChannelsConfig = OutChannels->channel_config; fChannelNames = OutChannels->channel_names; } else { usb_audio_output_channels_descriptor* OutChannels = (usb_audio_output_channels_descriptor*) &Extension->input_pins[Extension->num_input_pins]; fOutChannelsNumber = OutChannels->num_output_pins; fChannelsConfig = OutChannels->channel_config; fChannelNames = OutChannels->channel_names; } TRACE(UAC, "Out channels number:%d\n", fOutChannelsNumber); TRACE(UAC, "Out channels config:%#06x\n", fChannelsConfig); TRACE(UAC, "Out channels names:%d\n", fChannelNames); /* uint8 controlsSize = Processing->length - 10 - Processing->num_input_pins; TRACE(UAC, "Controls Size:%d\n", controlsSize); uint8* controlsData = (uint8*) ++OutChannels; for (size_t i = 0; i < controlsSize; i++) { fProgrammableControls.PushBack(controlsData[i]); TRACE(UAC, "Controls Data[%d]:%#x\n", i, controlsData[i]); } fStringIndex = *(controlsData + controlsSize); TRACE(UAC, "StringIndex:%d\n", fStringIndex); */ fStatus = B_OK; } ExtensionUnit::~ExtensionUnit() { } ClockSource::ClockSource(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioControl(interface, Header) { usb_audio_input_terminal_descriptor* descriptor = (usb_audio_input_terminal_descriptor*) Header; TRACE(UAC, "Clock Source:%d >>>\n", descriptor->terminal_id); } ClockSource::~ClockSource() { } ClockSelector::ClockSelector(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioControl(interface, Header) { usb_audio_input_terminal_descriptor* descriptor = (usb_audio_input_terminal_descriptor*) Header; TRACE(UAC, "Clock Selector:%d >>>\n", descriptor->terminal_id); } ClockSelector::~ClockSelector() { } ClockMultiplier::ClockMultiplier(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioControl(interface, Header) { usb_audio_input_terminal_descriptor* descriptor = (usb_audio_input_terminal_descriptor*) Header; TRACE(UAC, "Clock Multiplier:%d >>>\n", descriptor->terminal_id); } ClockMultiplier::~ClockMultiplier() { } SampleRateConverter::SampleRateConverter(AudioControlInterface* interface, usb_audiocontrol_header_descriptor* Header) : _AudioControl(interface, Header) { usb_audio_input_terminal_descriptor* descriptor = (usb_audio_input_terminal_descriptor*) Header; TRACE(UAC, "Sample Rate Converter:%d >>>\n", descriptor->terminal_id); } SampleRateConverter::~SampleRateConverter() { } AudioControlInterface::AudioControlInterface(Device* device) : fInterface(0), fStatus(B_NO_INIT), fADCSpecification(0), fFunctionCategory(0), fControlsBitmap(0), fDevice(device) { } AudioControlInterface::~AudioControlInterface() { for (AudioControlsIterator I = fAudioControls.Begin(); I != fAudioControls.End(); I++) delete I->Value(); fAudioControls.MakeEmpty(); // object already freed. just purge the map fOutputTerminals.MakeEmpty(); // object already freed. just purge the map fInputTerminals.MakeEmpty(); } status_t AudioControlInterface::Init(size_t interface, usb_interface_info* Interface) { for (size_t i = 0; i < Interface->generic_count; i++) { usb_audiocontrol_header_descriptor* Header = (usb_audiocontrol_header_descriptor* )Interface->generic[i]; if (Header->descriptor_type != USB_AUDIO_CS_INTERFACE) { TRACE(ERR, "Ignore Audio Control of " "unknown descriptor type %#04x.\n", Header->descriptor_type); continue; } _AudioControl* control = NULL; switch(Header->descriptor_subtype) { default: TRACE(ERR, "Ignore Audio Control of unknown " "descriptor sub-type %#04x\n", Header->descriptor_subtype); break; case USB_AUDIO_AC_DESCRIPTOR_UNDEFINED: TRACE(ERR, "Ignore Audio Control of undefined sub-type\n"); break; case USB_AUDIO_AC_HEADER: InitACHeader(interface, Header); break; case USB_AUDIO_AC_INPUT_TERMINAL: control = new(std::nothrow) InputTerminal(this, Header); break; case USB_AUDIO_AC_OUTPUT_TERMINAL: control = new(std::nothrow) OutputTerminal(this, Header); break; case USB_AUDIO_AC_MIXER_UNIT: control = new(std::nothrow) MixerUnit(this, Header); break; case USB_AUDIO_AC_SELECTOR_UNIT: control = new(std::nothrow) SelectorUnit(this, Header); break; case USB_AUDIO_AC_FEATURE_UNIT: control = new(std::nothrow) FeatureUnit(this, Header); break; case USB_AUDIO_AC_PROCESSING_UNIT: if (SpecReleaseNumber() < 200) control = new(std::nothrow) ProcessingUnit(this, Header); else control = new(std::nothrow) EffectUnit(this, Header); break; case USB_AUDIO_AC_EXTENSION_UNIT: if (SpecReleaseNumber() < 200) control = new(std::nothrow) ExtensionUnit(this, Header); else control = new(std::nothrow) ProcessingUnit(this, Header); break; case USB_AUDIO_AC_EXTENSION_UNIT_R2: control = new(std::nothrow) ExtensionUnit(this, Header); break; case USB_AUDIO_AC_CLOCK_SOURCE_R2: control = new(std::nothrow) ClockSource(this, Header); break; case USB_AUDIO_AC_CLOCK_SELECTOR_R2: control = new(std::nothrow) ClockSelector(this, Header); break; case USB_AUDIO_AC_CLOCK_MULTIPLIER_R2: control = new(std::nothrow) ClockMultiplier(this, Header); break; case USB_AUDIO_AC_SAMPLE_RATE_CONVERTER_R2: control = new(std::nothrow) SampleRateConverter(this, Header); break; } if (control != 0 && control->InitCheck() == B_OK) { switch(control->SubType()) { case USB_AUDIO_AC_OUTPUT_TERMINAL: fOutputTerminals.Put(control->SourceID(), control); break; case USB_AUDIO_AC_INPUT_TERMINAL: fInputTerminals.Put(control->ID(), control); break; } fAudioControls.Put(control->ID(), control); } else delete control; } return fStatus = B_OK; } _AudioControl* AudioControlInterface::Find(uint8 id) { return fAudioControls.Get(id); } _AudioControl* AudioControlInterface::FindOutputTerminal(uint8 id) { return fOutputTerminals.Get(id); } status_t AudioControlInterface::InitACHeader(size_t interface, usb_audiocontrol_header_descriptor* Header) { if (Header == NULL) return fStatus = B_NO_INIT; fInterface = interface; fADCSpecification = Header->bcd_release_no; TRACE(UAC, "ADCSpecification:%#06x\n", fADCSpecification); if (fADCSpecification < 0x200) { TRACE(UAC, "InterfacesCount:%d\n", Header->r1.in_collection); for (size_t i = 0; i < Header->r1.in_collection; i++) { fStreams.PushBack(Header->r1.interface_numbers[i]); TRACE(UAC, "Interface[%d] number is %d\n", i, fStreams[i]); } } else { fFunctionCategory = Header->r2.function_category; fControlsBitmap = Header->r2.bm_controls; TRACE(UAC, "Function Category:%#04x\n", fFunctionCategory); TRACE(UAC, "Controls Bitmap:%#04x\n", fControlsBitmap); } return B_OK; } uint32 AudioControlInterface::GetChannelsDescription( Vector& Channels, multi_description* Description, Vector<_AudioControl*>& Terminals, bool isForInput) { uint32 addedChannels = 0; for (int32 i = 0; i < Terminals.Count(); i++) { AudioChannelCluster* cluster = Terminals[i]->OutCluster(); if (cluster == NULL || cluster->ChannelsCount() <= 0) { TRACE(ERR, "Terminal #%d ignored due null " "channels cluster (%08x)\n", Terminals[i]->ID(), cluster); continue; } uint32 channels = GetTerminalChannels(Channels, cluster, isForInput ? B_MULTI_INPUT_CHANNEL : B_MULTI_OUTPUT_CHANNEL); if (isForInput) Description->input_channel_count += channels; else Description->output_channel_count += channels; addedChannels += channels; } return addedChannels; } uint32 AudioControlInterface::GetTerminalChannels(Vector& Channels, AudioChannelCluster* cluster, channel_kind kind, uint32 connectors) { if (cluster->ChannelsCount() < 2) { // mono channel multi_channel_info info; info.channel_id = Channels.Count(); info.kind = kind; info.designations= B_CHANNEL_MONO_BUS; info.connectors = connectors; Channels.PushBack(info); return 1; } uint32 startCount = Channels.Count(); // Haiku multi-aduio designations have the same bits // as USB Audio 2.0 cluster spatial locations :-) for (size_t i = 0; i < kChannels; i++) { uint32 designation = 1 << i; if ((cluster->ChannelsConfig() & designation) == designation) { multi_channel_info info; info.channel_id = Channels.Count(); info.kind = kind; info.designations= gDesignations[i].ch | gDesignations[i].bus; info.connectors = connectors; Channels.PushBack(info); } } return Channels.Count() - startCount; } uint32 AudioControlInterface::GetBusChannelsDescription( Vector& Channels, multi_description* Description) { uint32 addedChannels = 0; // first iterate output channels for (AudioControlsIterator I = fOutputTerminals.Begin(); I != fOutputTerminals.End(); I++) { _AudioControl* control = I->Value(); if (static_cast<_Terminal*>(control)->IsUSBIO()) continue; AudioChannelCluster* cluster = control->OutCluster(); if (cluster == 0 || cluster->ChannelsCount() <= 0) { TRACE(ERR, "Terminal #%d ignored due null " "channels cluster (%08x)\n", control->ID(), cluster); continue; } uint32 channels = GetTerminalChannels(Channels, cluster, B_MULTI_OUTPUT_BUS); Description->output_bus_channel_count += channels; addedChannels += channels; } // input channels should follow too for (AudioControlsIterator I = fInputTerminals.Begin(); I != fInputTerminals.End(); I++) { _AudioControl* control = I->Value(); if (static_cast<_Terminal*>(control)->IsUSBIO()) continue; AudioChannelCluster* cluster = control->OutCluster(); if (cluster == NULL || cluster->ChannelsCount() <= 0) { TRACE(ERR, "Terminal #%d ignored due null " "channels cluster (%08x)\n", control->ID(), cluster); continue; } uint32 channels = GetTerminalChannels(Channels, cluster, B_MULTI_INPUT_BUS); Description->input_bus_channel_count += channels; addedChannels += channels; } return addedChannels; } void AudioControlInterface::_HarvestRecordFeatureUnits(_AudioControl* rootControl, AudioControlsMap& Map) { if (rootControl == 0) { TRACE(ERR, "Not processing due NULL root control.\n"); return; } switch(rootControl->SubType()) { case USB_AUDIO_AC_SELECTOR_UNIT: { SelectorUnit* unit = static_cast(rootControl); for (int i = 0; i < unit->fInputPins.Count(); i++) _HarvestRecordFeatureUnits(Find(unit->fInputPins[i]), Map); Map.Put(rootControl->ID(), rootControl); } break; case USB_AUDIO_AC_FEATURE_UNIT: Map.Put(rootControl->ID(), rootControl); break; } } void AudioControlInterface::_HarvestOutputFeatureUnits(_AudioControl* rootControl, AudioControlsMap& Map) { if (rootControl == 0) { TRACE(ERR, "Not processing due NULL root control.\n"); return; } switch(rootControl->SubType()) { case USB_AUDIO_AC_MIXER_UNIT: { MixerUnit* unit = static_cast(rootControl); for (int i = 0; i < unit->fInputPins.Count(); i++) _HarvestOutputFeatureUnits(Find(unit->fInputPins[i]), Map); Map.Put(rootControl->ID(), rootControl); } break; case USB_AUDIO_AC_FEATURE_UNIT: Map.Put(rootControl->ID(), rootControl); break; } } bool AudioControlInterface::_InitGainLimits(multi_mix_control& Control) { bool canControl = false; float current = 0.; struct _GainInfo { uint8 request; int16 data; float& value; } gainInfos[] = { { USB_AUDIO_GET_CUR, 0, current }, { USB_AUDIO_GET_MIN, 0, Control.gain.min_gain }, { USB_AUDIO_GET_MAX, 0, Control.gain.max_gain }, { USB_AUDIO_GET_RES, 0, Control.gain.granularity } }; Control.gain.min_gain = 0.; Control.gain.max_gain = 100.; Control.gain.granularity = 1.; size_t actualLength = 0; for (size_t i = 0; i < B_COUNT_OF(gainInfos); i++) { status_t status = gUSBModule->send_request(fDevice->USBDevice(), USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS, gainInfos[i].request, REQ_VALUE(Control.id), REQ_INDEX(Control.id), sizeof(gainInfos[i].data), &gainInfos[i].data, &actualLength); if (status != B_OK || actualLength != sizeof(gainInfos[i].data)) { TRACE(ERR, "Request %d (%04x:%04x) fail:%#08x; received %d of %d\n", i, REQ_VALUE(Control.id), REQ_INDEX(Control.id), status, actualLength, sizeof(gainInfos[i].data)); continue; } if (i == 0) canControl = true; gainInfos[i].value = static_cast(gainInfos[i].data) / 256.; } TRACE(ERR, "Control %s: %f dB, from %f to %f dB, step %f dB.\n", Control.name, current, Control.gain.min_gain, Control.gain.max_gain, Control.gain.granularity); return canControl; } uint32 AudioControlInterface::_ListFeatureUnitOption(uint32 controlType, int32& index, int32 parentIndex, multi_mix_control_info* Info, FeatureUnit* unit, uint32 channel, uint32 channels) { int32 startIndex = index; uint32 id = 0; uint32 flags = 0; strind_id string = S_null; const char* name = NULL; bool initGainLimits = false; switch(controlType) { case BMA_CTL_MUTE: id = USB_AUDIO_MUTE_CONTROL; flags = B_MULTI_MIX_ENABLE; string = S_MUTE; break; case BMA_CTL_VOLUME: id = USB_AUDIO_VOLUME_CONTROL; flags = B_MULTI_MIX_GAIN; string = S_GAIN; initGainLimits = true; break; case BMA_CTL_AUTOGAIN: id = USB_AUDIO_AUTOMATIC_GAIN_CONTROL; flags = B_MULTI_MIX_ENABLE; name = "Auto Gain"; break; default: TRACE(ERR, "Unsupported type %#08x ignored.\n", controlType); return 0; } multi_mix_control* Controls = Info->controls; if (unit->HasControl(channel, controlType)) { uint32 masterIndex = CTL_ID(id, channel, unit->ID(), fInterface); Controls[index].id = masterIndex; Controls[index].flags = flags; Controls[index].parent = parentIndex; Controls[index].string = string; if (name != NULL) strlcpy(Controls[index].name, name, sizeof(Controls[index].name)); if (initGainLimits) _InitGainLimits(Controls[index]); index++; if (channels == 2) { Controls[index].id = CTL_ID(id, channel + 1, unit->ID(), fInterface); Controls[index].flags = flags; Controls[index].parent = parentIndex; Controls[index].master = masterIndex; Controls[index].string = string; if (name != NULL) strlcpy(Controls[index].name, name, sizeof(Controls[index].name)); if (initGainLimits) _InitGainLimits(Controls[index]); index++; } } return index - startIndex; } int32 AudioControlInterface::_ListFeatureUnitControl(int32& index, int32 parentIndex, multi_mix_control_info* Info, _AudioControl* control) { FeatureUnit* unit = static_cast(control); if (unit == 0) { TRACE(ERR, "Feature Unit for null control ignored.\n"); return 0; } if (index + 4 > Info->control_count) { TRACE(ERR, "Could not list feature control group." " Limit %d of %d has been reached.\n", index, Info->control_count); return 0; } AudioChannelCluster* cluster = unit->OutCluster(); if (cluster == 0) { TRACE(ERR, "Control %s with null cluster ignored.\n", unit->Name()); return 0; } struct _ChannelInfo { const char* Name; uint8 channels; uint32 Mask; } channelInfos[] = { { "", 1, 0 }, // Master channel entry - no bitmask { "", 2, B_CHANNEL_LEFT | B_CHANNEL_RIGHT }, { "Left", 1, B_CHANNEL_LEFT }, { "Right", 1, B_CHANNEL_RIGHT }, { "Center", 1, B_CHANNEL_CENTER }, { "L.F.E.", 1, B_CHANNEL_SUB }, { "Back", 2, B_CHANNEL_REARLEFT | B_CHANNEL_REARRIGHT }, { "Back Left", 1, B_CHANNEL_REARLEFT }, { "Back Right", 1, B_CHANNEL_REARRIGHT }, { "Front of Center", 2, B_CHANNEL_FRONT_LEFT_CENTER | B_CHANNEL_FRONT_RIGHT_CENTER }, { "Front Left of Center", 1, B_CHANNEL_FRONT_LEFT_CENTER }, { "Front Right of Center", 1, B_CHANNEL_FRONT_RIGHT_CENTER }, { "Back Center", 1, B_CHANNEL_BACK_CENTER }, { "Side", 2, B_CHANNEL_SIDE_LEFT | B_CHANNEL_SIDE_RIGHT }, { "Side Left", 1, B_CHANNEL_SIDE_LEFT }, { "Side Right", 1, B_CHANNEL_SIDE_RIGHT }, { "Top Center", 1, B_CHANNEL_TOP_CENTER }, { "Top Front Left", 1, B_CHANNEL_TOP_FRONT_LEFT }, { "Top Front Center", 1, B_CHANNEL_TOP_FRONT_CENTER }, { "Top Front Right", 1, B_CHANNEL_TOP_FRONT_RIGHT }, { "Top Back Left", 1, B_CHANNEL_TOP_BACK_LEFT }, { "Top Back Center", 1, B_CHANNEL_TOP_BACK_CENTER }, { "Top Back Right", 1, B_CHANNEL_TOP_BACK_RIGHT } }; multi_mix_control* Controls = Info->controls; uint32 channelsConfig = cluster->ChannelsConfig(); int32 groupIndex = 0; int32 channel = 0; int32 masterIndex = 0; // in case master channel has no volume // control - add following "L+R" channels into it for (size_t i = 0; i < B_COUNT_OF(channelInfos); i++) { if ((channelsConfig & channelInfos[i].Mask) != channelInfos[i].Mask) { // ignore non-listed and possibly non-paired stereo channels. // note that master channel with zero mask pass this check! ;-) continue; } if (masterIndex == 0) { groupIndex = index; Controls[index].id = groupIndex; Controls[index].flags = B_MULTI_MIX_GROUP; Controls[index].parent = parentIndex; snprintf(Controls[index].name, sizeof(Controls[index].name), "%s %s", unit->Name(), channelInfos[i].Name); index++; } else { groupIndex = masterIndex; masterIndex = 0; } // First list possible Mute controls _ListFeatureUnitOption(BMA_CTL_MUTE, index, groupIndex, Info, unit, channel, channelInfos[i].channels); // Gain controls may be usefull too if (_ListFeatureUnitOption(BMA_CTL_VOLUME, index, groupIndex, Info, unit, channel, channelInfos[i].channels) == 0) { masterIndex = (i == 0) ? groupIndex : 0 ; TRACE(UAC, "channel:%d set master index to %d\n", channel, masterIndex); } // Auto Gain checkbox will be listed too _ListFeatureUnitOption(BMA_CTL_AUTOGAIN, index, groupIndex, Info, unit, channel, channelInfos[i].channels); // Now check if the group filled with something usefull. // In case no controls were added into it - "remove" it if (Controls[index - 1].flags == B_MULTI_MIX_GROUP) { Controls[index - 1].id = 0; index--; masterIndex = 0; } channel += channelInfos[i].channels; // remove bits for already processed channels - this prevent from // duplication of the stereo channels as "orphaned" ones and optimize // exit from this iterations after all channels are processed. channelsConfig &= ~channelInfos[i].Mask; if (0 == channelsConfig) break; } if (channelsConfig > 0) TRACE(ERR, "Following channels were not processed: %#08x.\n", channelsConfig); // return last group index to stick possible selector unit to it. ;-) return groupIndex; } void AudioControlInterface::_ListSelectorUnitControl(int32& index, int32 parentGroup, multi_mix_control_info* Info, _AudioControl* control) { SelectorUnit* selector = static_cast(control); if (selector == 0 || selector->SubType() != USB_AUDIO_AC_SELECTOR_UNIT) return; if ((index + 1 + selector->fInputPins.Count()) > Info->control_count) { TRACE(ERR, "Could not list selector control." " Limit %d of %d has been reached.\n", index, Info->control_count); return; } multi_mix_control* Controls = Info->controls; int32 recordMUX = CTL_ID(0, 0, selector->ID(), fInterface); Controls[index].id = recordMUX; Controls[index].flags = B_MULTI_MIX_MUX; Controls[index].parent = parentGroup; Controls[index].string = S_null; strlcpy(Controls[index].name, "Source", sizeof(Controls[index].name)); index++; for (int i = 0; i < selector->fInputPins.Count(); i++) { Controls[index].id = CTL_ID(0, 1, selector->ID(), fInterface); Controls[index].flags = B_MULTI_MIX_MUX_VALUE; Controls[index].master = 0; Controls[index].string = S_null; Controls[index].parent = recordMUX; _AudioControl* control = Find(selector->fInputPins[i]); if (control != NULL) strlcpy(Controls[index].name, control->Name(), sizeof(Controls[index].name)); else snprintf(Controls[index].name, sizeof(Controls[index].name), "Input #%d", i + 1); index++; } } size_t AudioControlInterface::_CollectMixerUnitControls( const uint32 controlIds[kChannels][kChannels], size_t inLeft, size_t outLeft, size_t inRight, size_t outRight, const char* inputName, const char* name, Vector& Controls) { size_t count = 0; uint32 leftId = controlIds[inLeft][outLeft]; uint32 rightId = controlIds[inRight][outRight]; // TRACE(UAC, "left:%d %d: %08x; right:%d %d: %08x\n", // inLeft, outLeft, leftId, inRight, outRight, rightId); multi_mix_control control; memset(&control, 0, sizeof(multi_mix_control)); snprintf(control.name, sizeof(control.name), "%s %s", inputName, name); for (size_t i = 0; i < 2; i++) { if (leftId != 0 || rightId != 0) { control.flags = B_MULTI_MIX_GROUP; control.string = S_null; Controls.PushBack(control); int gainControls = 0; if (leftId != 0) { control.id = leftId; control.flags = B_MULTI_MIX_GAIN; control.string = S_GAIN; if (_InitGainLimits(control)) { gainControls++; Controls.PushBack(control); } } if (rightId != 0) { control.id = rightId; control.flags = B_MULTI_MIX_GAIN; control.string = S_GAIN; control.master = leftId; if (_InitGainLimits(control)) { gainControls++; Controls.PushBack(control); } } // remove empty mix group if (gainControls == 0) Controls.PopBack(); else count++; } // take care about surround bus if (inLeft == inRight) break; // handle possible reverse controls leftId = controlIds[inLeft][outRight]; rightId = controlIds[inRight][outLeft]; snprintf(control.name, sizeof(control.name), "%s %s (Reverse)", inputName, name); } return count; } void AudioControlInterface::_ListMixerUnitControls(int32& index, multi_mix_control_info* Info, Vector& controls) { multi_mix_control* Controls = Info->controls; uint32 groupParent = 0; uint32 gainParent = 0; for (Vector::Iterator I = controls.Begin(); I != controls.End() && index < Info->control_count; I++) { memcpy(Controls + index, &*I, sizeof(multi_mix_control)); switch (I->flags) { case B_MULTI_MIX_GROUP: Controls[index].id = index; Controls[index].parent = groupParent; if (groupParent == 0) { Controls[index].id |= 0x10000; groupParent = Controls[index].id; } gainParent = Controls[index].id; break; case B_MULTI_MIX_GAIN: Controls[index].parent = gainParent; break; default: TRACE(ERR, "Control type %d ignored\n", I->flags); continue; } index++; } if (index == Info->control_count) TRACE(ERR, "Control count limit %d has been reached.\n", index); } void AudioControlInterface::_ListMixControlsForMixerUnit(int32& index, multi_mix_control_info* Info, _AudioControl* control) { MixerUnit* mixer = static_cast(control); if (mixer == 0 || mixer->SubType() != USB_AUDIO_AC_MIXER_UNIT) return; struct _ChannelPair { size_t inLeft; size_t inRight; const char* name; } channelPairs[] = { { 0, 1, "" }, { 2, 2, "Center" }, { 3, 3, "L.F.E" }, { 4, 5, "Back" }, { 6, 7, "Front of Center" }, { 8, 8, "Back Center" }, { 9, 10, "Side" }, { 11, 11, "Top Center" }, { 12, 14, "Top Front" }, { 13, 13, "Top Front Center" }, { 15, 17, "Top Back" }, { 16, 16, "Top Back Center" } }; Vector<_MixPageCollector*> mixControls; _MixPageCollector* genericPage = new(std::nothrow) _MixPageCollector("Mixer"); mixControls.PushBack(genericPage); // page for extended in (>2) and out (>2) mixer controls size_t controlsOnExMixerPage = 0; _MixPageCollector* exMixerPage = new(std::nothrow) _MixPageCollector("Mixer"); AudioChannelCluster* outCluster = mixer->OutCluster(); int inOffset = 0; for (int iPin = 0; iPin < mixer->fInputPins.Count(); iPin++) { _AudioControl* control = Find(mixer->fInputPins[iPin]); AudioChannelCluster* inCluster = NULL; if (control != NULL) inCluster = control->OutCluster(); if (inCluster == NULL) { TRACE(ERR, "control %p cluster %p failed!\n", control, inCluster); break; } // at first - collect programmable control ids uint32 controlIds[kChannels][kChannels] = { { 0 } }; int inChannel = 0; for (size_t in = 0; in < kChannels && inChannel < inCluster->ChannelsCount(); in++) { if ((inCluster->ChannelsConfig() & (1 << in)) == 0) continue; for (size_t out = 0, outChannel = 0; out < kChannels && outChannel < outCluster->ChannelsCount(); out++) { if ((outCluster->ChannelsConfig() & (1 << out)) == 0) continue; if (mixer->IsControlProgrammable( inOffset + inChannel, outChannel)) { if (SpecReleaseNumber() < 0x200) // USB Audio 1.0 uses ICN/OCN for request controlIds[in][out] = CTL_ID(inOffset + inChannel + 1, outChannel + 1, mixer->ID(), fInterface); else // USB Audio 2.0 uses CS/MCN for request controlIds[in][out] = CTL_ID(USB_AUDIO_MIXER_CONTROL, (inOffset + inChannel) * outCluster->ChannelsCount() + outChannel, mixer->ID(), fInterface); } outChannel++; } inChannel++; } inOffset += inChannel; for (size_t in = 0; in < kChannels; in++) for (size_t out = 0; out < kChannels; out++) if (controlIds[in][out] != 0) TRACE(UAC, "ctrl:%08x for in %d; out %d;\n", controlIds[in][out], in, out); // second step - distribute controls on // mixer pages in logical groups uint32 exChannelsMask = ~(B_CHANNEL_LEFT | B_CHANNEL_RIGHT); bool inIsEx = (inCluster->ChannelsConfig() & exChannelsMask) != 0; bool outIsEx = (outCluster->ChannelsConfig() & exChannelsMask) != 0; if (!inIsEx && !outIsEx) { // heap up all mono and stereo controls into single "Mixer" page for (size_t i = 0; i < 2; i++) _CollectMixerUnitControls(controlIds, kLeftChannel, channelPairs[i].inLeft, kRightChannel, channelPairs[i].inRight, control->Name(), channelPairs[i].name, *mixControls[0]); continue; // go next input cluster } if (!outIsEx) { // special case - extended (>2 channels) input cluster // connected to 2-channels output - add into generic "Mixer" page for (size_t i = 0; i < B_COUNT_OF(channelPairs); i++) _CollectMixerUnitControls(controlIds, channelPairs[i].inLeft, kLeftChannel, channelPairs[i].inRight, kRightChannel, control->Name(), channelPairs[i].name, *mixControls[0]); continue; // go next input cluster } // make separate mixer pages for set of extended (>2) input // channels connected to extended (>2 channels) output for (size_t in = 0; in < B_COUNT_OF(channelPairs); in++) { for (size_t out = 0; out < B_COUNT_OF(channelPairs); out++) { char outName[sizeof(Info->controls->name)] = { 0 }; if (in == out) strlcpy(outName, channelPairs[out].name, sizeof(outName)); else snprintf(outName, sizeof(outName), "%s to %s", channelPairs[in].name, channelPairs[out].name); controlsOnExMixerPage += _CollectMixerUnitControls(controlIds, channelPairs[in].inLeft, channelPairs[out].inLeft, channelPairs[in].inRight, channelPairs[out].inRight, control->Name(), outName, *exMixerPage); } if (controlsOnExMixerPage >= 6) { mixControls.PushBack(exMixerPage); exMixerPage = new(std::nothrow) _MixPageCollector("Mixer"); controlsOnExMixerPage = 0; } } } if (exMixerPage->Count() > 1) mixControls.PushBack(exMixerPage); else delete exMixerPage; // final step - fill multiaudio controls info with // already structured pages/controls info arrays for (Vector<_MixPageCollector*>::Iterator I = mixControls.Begin(); I != mixControls.End(); I++) { Vector* controls = *I; TRACE(UAC, "controls count: %d\n", controls->Count()); if (controls->Count() > 1) _ListMixerUnitControls(index, Info, *controls); delete controls; } } void AudioControlInterface::_ListMixControlsPage(int32& index, multi_mix_control_info* Info, AudioControlsMap& Map, const char* Name) { multi_mix_control* Controls = Info->controls; int32 groupIndex = index | 0x10000; Controls[index].id = groupIndex; Controls[index].flags = B_MULTI_MIX_GROUP; Controls[index].parent = 0; strlcpy(Controls[index].name, Name, sizeof(Controls[index].name)); index++; int32 group = groupIndex; for (AudioControlsIterator I = Map.Begin(); I != Map.End(); I++) { TRACE(UAC, "%s control %d listed.\n", Name, I->Value()->ID()); switch(I->Value()->SubType()) { case USB_AUDIO_AC_FEATURE_UNIT: group = _ListFeatureUnitControl(index, groupIndex, Info, I->Value()); break; case USB_AUDIO_AC_SELECTOR_UNIT: _ListSelectorUnitControl(index, group, Info, I->Value()); break; default: break; } } } status_t AudioControlInterface::ListMixControls(multi_mix_control_info* Info) { // first harvest feature units that assigned to output terminal(s) AudioControlsMap RecordControlsMap; AudioControlsMap OutputControlsMap; for (AudioControlsIterator I = fOutputTerminals.Begin(); I != fOutputTerminals.End(); I++) { _Terminal* terminal = static_cast<_Terminal*>(I->Value()); if (terminal->IsUSBIO()) _HarvestRecordFeatureUnits(terminal, RecordControlsMap); else _HarvestOutputFeatureUnits(terminal, OutputControlsMap); } // separate input and output Feature units // and collect mixer units that can be controlled AudioControlsMap InputControlsMap; AudioControlsMap MixerControlsMap; for (AudioControlsIterator I = fAudioControls.Begin(); I != fAudioControls.End(); I++) { _AudioControl* control = I->Value(); if (control->SubType() == USB_AUDIO_AC_MIXER_UNIT) { MixerUnit* mixerControl = static_cast(control); if (mixerControl->HasProgrammableControls()) MixerControlsMap.Put(control->ID(), control); continue; } // filter out feature units if (control->SubType() != USB_AUDIO_AC_FEATURE_UNIT) continue; // ignore controls that are already in the output controls maps if (RecordControlsMap.Find(control->ID()) != RecordControlsMap.End() || OutputControlsMap.Find(control->ID()) != OutputControlsMap.End()) continue; _AudioControl* sourceControl = Find(control->SourceID()); if (sourceControl != 0 && sourceControl->SubType() == USB_AUDIO_AC_INPUT_TERMINAL) InputControlsMap.Put(control->ID(), control); else OutputControlsMap.Put(control->ID(), control); } int32 index = 0; if (InputControlsMap.Count() > 0) _ListMixControlsPage(index, Info, InputControlsMap, "Input"); if (OutputControlsMap.Count() > 0) _ListMixControlsPage(index, Info, OutputControlsMap, "Output"); if (RecordControlsMap.Count() > 0) _ListMixControlsPage(index, Info, RecordControlsMap, "Record"); for (AudioControlsIterator I = MixerControlsMap.Begin(); I != MixerControlsMap.End(); I++) _ListMixControlsForMixerUnit(index, Info, I->Value()); Info->control_count = index; return B_OK; } status_t AudioControlInterface::GetMix(multi_mix_value_info* Info) { for (int32 i = 0; i < Info->item_count; i++) { uint16 length = 0; int16 data = 0; _AudioControl* control = Find(ID_FROM_CTLID(Info->values[i].id)); if (control == NULL) { TRACE(ERR, "No control found for unit id %#02x. Ignore it.\n", ID_FROM_CTLID(Info->values[i].id)); continue; } switch (control->SubType()) { case USB_AUDIO_AC_FEATURE_UNIT: switch(CS_FROM_CTLID(Info->values[i].id)) { case USB_AUDIO_VOLUME_CONTROL: length = 2; break; case USB_AUDIO_MUTE_CONTROL: case USB_AUDIO_AUTOMATIC_GAIN_CONTROL: length = 1; break; default: TRACE(ERR, "Unsupported control id:%08x of type %#02x " "ignored.\n", ID_FROM_CTLID(Info->values[i].id), CS_FROM_CTLID(Info->values[i].id)); continue; } break; case USB_AUDIO_AC_SELECTOR_UNIT: length = 1; break; case USB_AUDIO_AC_MIXER_UNIT: length = 2; break; default: TRACE(ERR, "Control id:%08x of type %d is not supported\n", ID_FROM_CTLID(Info->values[i].id), control->SubType()); continue; } size_t actualLength = 0; status_t status = gUSBModule->send_request(fDevice->USBDevice(), USB_REQTYPE_INTERFACE_IN | USB_REQTYPE_CLASS, USB_AUDIO_GET_CUR, REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id), length, &data, &actualLength); if (status != B_OK || actualLength != length) { TRACE(ERR, "Request (%04x:%04x) failed:%#08x; received %d of %d\n", REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id), status, actualLength, length); continue; } switch (control->SubType()) { case USB_AUDIO_AC_FEATURE_UNIT: switch(CS_FROM_CTLID(Info->values[i].id)) { case USB_AUDIO_VOLUME_CONTROL: Info->values[i].gain = static_cast(data) / 256.; TRACE(MIX, "Gain control %d; channel: %d; is %f dB.\n", ID_FROM_CTLID(Info->values[i].id), CN_FROM_CTLID(Info->values[i].id), Info->values[i].gain); break; case USB_AUDIO_MUTE_CONTROL: Info->values[i].enable = data > 0; TRACE(MIX, "Mute control %d; channel: %d; is %d.\n", ID_FROM_CTLID(Info->values[i].id), CN_FROM_CTLID(Info->values[i].id), Info->values[i].enable); break; case USB_AUDIO_AUTOMATIC_GAIN_CONTROL: Info->values[i].enable = data > 0; TRACE(MIX, "AGain control %d; channel: %d; is %d.\n", ID_FROM_CTLID(Info->values[i].id), CN_FROM_CTLID(Info->values[i].id), Info->values[i].enable); break; default: break; } break; case USB_AUDIO_AC_SELECTOR_UNIT: Info->values[i].mux = data - 1; TRACE(MIX, "Selector control %d; is %d.\n", ID_FROM_CTLID(Info->values[i].id), Info->values[i].mux); break; case USB_AUDIO_AC_MIXER_UNIT: Info->values[i].gain = static_cast(data) / 256.; TRACE(MIX, "Mixer #%d channels in: %d; out: %d; is %f dB.\n", ID_FROM_CTLID(Info->values[i].id), CS_FROM_CTLID(Info->values[i].id), CN_FROM_CTLID(Info->values[i].id), Info->values[i].gain); break; } } return B_OK; } status_t AudioControlInterface::SetMix(multi_mix_value_info* Info) { for (int32 i = 0; i < Info->item_count; i++) { uint16 length = 0; int16 data = 0; _AudioControl* control = Find(ID_FROM_CTLID(Info->values[i].id)); if (control == NULL) { TRACE(ERR, "No control found for unit id %#02x. Ignore it.\n", ID_FROM_CTLID(Info->values[i].id)); continue; } switch (control->SubType()) { case USB_AUDIO_AC_FEATURE_UNIT: switch(CS_FROM_CTLID(Info->values[i].id)) { case USB_AUDIO_VOLUME_CONTROL: data = static_cast(Info->values[i].gain * 256.); length = 2; TRACE(MIX, "Gain control %d; channel: %d; " "about to set to %f dB.\n", ID_FROM_CTLID(Info->values[i].id), CN_FROM_CTLID(Info->values[i].id), Info->values[i].gain); break; case USB_AUDIO_MUTE_CONTROL: data = (Info->values[i].enable ? 1 : 0); length = 1; TRACE(MIX, "Mute control %d; channel: %d; " "about to set to %d.\n", ID_FROM_CTLID(Info->values[i].id), CN_FROM_CTLID(Info->values[i].id), Info->values[i].enable); break; case USB_AUDIO_AUTOMATIC_GAIN_CONTROL: data = (Info->values[i].enable ? 1 : 0); length = 1; TRACE(MIX, "AGain control %d; channel: %d; " "about to set to %d.\n", ID_FROM_CTLID(Info->values[i].id), CN_FROM_CTLID(Info->values[i].id), Info->values[i].enable); break; default: TRACE(ERR, "Unsupported control id:%08x of type %#02x " "ignored.\n", ID_FROM_CTLID(Info->values[i].id), CS_FROM_CTLID(Info->values[i].id)); continue; } break; case USB_AUDIO_AC_SELECTOR_UNIT: data = Info->values[i].mux + 1; length = 1; TRACE(MIX, "Selector Control %d about to set to %d.\n", ID_FROM_CTLID(Info->values[i].id), Info->values[i].mux); break; case USB_AUDIO_AC_MIXER_UNIT: data = static_cast(Info->values[i].gain * 256.); length = 2; TRACE(MIX, "Mixer %d channels in: %d; out: %d; " "about to set to %f dB.\n", ID_FROM_CTLID(Info->values[i].id), CS_FROM_CTLID(Info->values[i].id), CN_FROM_CTLID(Info->values[i].id), Info->values[i].gain); break; default: TRACE(ERR, "Control id:%08x of type %d is not supported\n", Info->values[i].id, control->SubType()); continue; } size_t actualLength = 0; status_t status = gUSBModule->send_request(fDevice->USBDevice(), USB_REQTYPE_INTERFACE_OUT | USB_REQTYPE_CLASS, USB_AUDIO_SET_CUR, REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id), length, &data, &actualLength); if (status != B_OK || actualLength != length) { TRACE(ERR, "Request (%04x:%04x) failed:%#08x; send %d of %d\n", REQ_VALUE(Info->values[i].id), REQ_INDEX(Info->values[i].id), status, actualLength, length); continue; } TRACE(MIX, "Value set OK\n"); } return B_OK; }