Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 20 of 20

Thread: Audio library, USB audio interface, volume setting

  1. #1
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    294

    Audio library, USB audio interface, volume setting

    Hooked up a PCM5102 board to a Teensy 3.1 to form a USB audio interface.

    Click image for larger version. 

Name:	PCM5102 DAC board.jpg 
Views:	49 
Size:	111.3 KB 
ID:	16337

    With the code below, my windows PC sees a USB Teensy Audio device and outputs audio via the PCM5102 board as expected.
    Code:
    // PCM5102 bd   Teensy
    // VCC          5V
    // GND          GND
    // LRCK         23
    // DATA         22
    // BCK          9
    
    // set Tools > USB Type to Audio (+ Serial if needed)
    
    #include <Audio.h>
    
    AudioInputUSB            usb1;
    AudioAmplifier           amp2;
    AudioAmplifier           amp1;
    AudioOutputI2S           i2s1;
    AudioConnection          patchCord1(usb1, 0, amp1, 0);
    AudioConnection          patchCord2(usb1, 1, amp2, 0);
    AudioConnection          patchCord3(amp2, 0, i2s1, 1);
    AudioConnection          patchCord4(amp1, 0, i2s1, 0);
    
    void setup()
    {
      Serial.begin(9600);
      AudioMemory(12);
      pinMode(LED_BUILTIN, OUTPUT);
      digitalWrite(LED_BUILTIN, HIGH);
    }
    
    void loop()
    {
      float vol = usb1.volume();  // read PC volume setting
      amp1.gain(vol);             // set gain according to PC volume
      amp2.gain(vol);
      delay(100);
      Serial.println(vol);
    }
    To control the volume of the audio I use the windows volume slider:

    Click image for larger version. 

Name:	Windows audio volume 50%.png 
Views:	27 
Size:	8.5 KB 
ID:	16338

    Now the interesting part is that when the slider is at 50%, the value of variable "vol" = 0.63. I expected 0.50.
    When I checked other slider settings, I see the following values for slider percentages:

    Click image for larger version. 

Name:	Slider vs volume value.PNG 
Views:	35 
Size:	16.5 KB 
ID:	16339

    My questions now are: where [and how] is this conversion done? And is there a reason it isn't linear?
    I have been digging through the audio library sources but could not find it.

    Any help and/or explanation is appreciated!

    Thanks,
    Paul

  2. #2
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    294
    After further digging I found a number of references to USB volume in C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3\us b_audio.h & usb_audio.c.
    But I'm still in the dark where the non-linear conversion takes place.

    Is it perhaps in C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\Audio\ mixer.h and mixer.cpp? If I understand these files correctly, I see a conversion from float to int32_t. Could that be the issue?

    Regards,
    Paul

  3. #3
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    294

    Questions answered

    While printing member "volume" of structure "usb_audio_features_struct" [in C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3\us b_audio.h] to the serial monitor, I noticed the same non-linearity between Windows slider percentage and the value of "volume".

    Since member "volume" is directly obtained from the USB audio interface Volume feature descriptor, I can only conclude that Windows does the non-linear conversion from slider percentage to USB volume. Apparently Windows 7 tries to make a kind of logarithmic scaled volume.

    Regards,
    Paul

  4. #4
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    Hi Paul,


    Yes logarithmic volume sliders are better than linear. Interesting that windows does this that way, but it's a good thing for audio. Our ears don't hear linearly so they setup the volume sliders in the same way.

    It would be nice if the teensy audio library mixers were also logarithmic...

    Thanks for the code, I was looking for this. Next step for me is to figure out the USB volume descriptor for the microphones. I'll want control over them via the windows mixer.

    Jay

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,663
    I haven't used the USB volume parameter. It was a contribution I merged without testing, since it seemed like a low risk. Looks like it just delivers whatever number the USB host sends.

    If there's a problem in need of fixing (in the Teensy core lib), please let me know?

  6. #6
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    Quote Originally Posted by PaulStoffregen View Post
    I haven't used the USB volume parameter. It was a contribution I merged without testing, since it seemed like a low risk. Looks like it just delivers whatever number the USB host sends.

    If there's a problem in need of fixing (in the Teensy core lib), please let me know?
    No it appears that Paul found out the USB volume is logarithmic... Your library is fine, anybody's application can control your library in a logarithmic way. The volumes on the library could have the feature, but it isn't broken.

  7. #7
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    294
    @PaulStoffregen: Indeed, nothing to fix here.
    Out of curiosity I checked the slider versus volume values on Windows 10.
    Microsoft updated the conversion. It now looks more like the correct logarithmic behaviour:

    Click image for larger version. 

Name:	Capture.PNG 
Views:	12 
Size:	20.4 KB 
ID:	20906

    Paul

  8. #8
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    Hello,

    I'm looking to control the input volume similarly to the way the output volume is being controlled by the usb1.volume(); funtion. Here is an image showing the volume slider I'm looking to monitor in my application.

    Click image for larger version. 

Name:	usb-audio-input-volume.png 
Views:	230 
Size:	35.0 KB 
ID:	20907


    I was looking over the USB Audio Spec to try and figure it out. It looks like it's a descriptor called "Input Terminal Audio Class Descriptor". Any quick hints on how I could add functionality to the library to get a usb1.inputVolume(); function?

    Any help is appreciated. :-)

    Jay

  9. #9
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    294
    I wonder if it is possible at all to tell the source to attenuate the output signal before sending out.
    In your case you would like to send out a control signal to the Teensy to attenuate the mic output. But I think the actual attenuation is always happening at the receiver side [in your case the host PC]. I know that for analog signals the volume attenuation ["levels" setting] is done at the receiver side.
    Similarly, in case of the usb1.volume control in my sketch on top, the receiving Teensy attenuates the full-scale signal from the source [the host PC] before sending it further out to the DAC.

    Paul

  10. #10
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    To your point... It might be class compliant to send the full un-attenuated input from the input into the USB1 object. That can be arranged in the Audio Design Tool project. But what I'm looking to control is the "listening volume" that goes through the "DSP" and out the speakers...

    Just to be clear, I'm not looking for the windows "listen to this device" feature. That feature will rout the USB input back out the output, and introduce lag.

  11. #11
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    Many people may not know... Motherboard sound cards have an analog audio path from the input to the output. So when you turn down the volume on that slider (the one here), the motherboard attenuates the analog volume of the input and sends it directly out of the speakers. This is the functionality that I'm trying to replicate, inside the Teensy. This is why any regular USB Audio device doesn't work for my use-case. We need the DSP, to have low-latency input to output because we are in the digital domain. The reason it works on motherboards is because of that analog signal path.... In comparison to the Windows "listen to this device" which will take the USB input and push it back down the USB to the output, introducing lag.
    Last edited by JayShoe; 07-09-2020 at 09:02 PM.

  12. #12
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    Here's another example, from a different computer. This one has specified a "microphone boost" parameter (aka "gain"). I also want to see about getting access to that slider. We probably have to define it somewhere so when the device is installed windows knows what options to display.

    Click image for larger version. 

Name:	usb-audio-input-volume2.png 
Views:	11 
Size:	31.3 KB 
ID:	20911

    But this slider definitely controls the input "listen" level.


    We probably also can define multiple inputs (although I'm aware Teensy is only currently 2i2o). This soundcard has two mics...

    Click image for larger version. 

Name:	usb-audio-input-volume3.png 
Views:	7 
Size:	71.1 KB 
ID:	20912

    Anyway, just defining my situation while researching.

  13. #13
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    How can I return the current features.volume?

    I have an AudioInputUSB object called usb1 and an AudioOutputUSB object called usb2.

    So this code works for the "output" AudioInputUSB object.

    float vol = usb1.volume();
    Can I get this to work for the AudioOutputUSB object too?

    float inputVol = usb2.volume();
    I'm trying to figure it out in the library code if is there. The usb_audio.cpp file has this code. Can I get the usb2 reading from that?

    Code:
    int usb_audio_get_feature(void *stp, uint8_t *data, uint32_t *datalen)
    {
    	struct setup_struct setup = *((struct setup_struct *)stp);
    	if (setup.bmRequestType==0xA1) { // should check bRequest, bChannel, and UnitID
    			if (setup.bCS==0x01) { // mute
    				data[0] = AudioInputUSB::features.mute;  // 1=mute, 0=unmute
    				*datalen = 1;
    				return 1;
    			}
    			else if (setup.bCS==0x02) { // volume
    				if (setup.bRequest==0x81) { // GET_CURR
    					data[0] = AudioInputUSB::features.volume & 0xFF;
    					data[1] = (AudioInputUSB::features.volume>>8) & 0xFF;
    				}
    				else if (setup.bRequest==0x82) { // GET_MIN
    					//serial_print("vol get_min\n");
    					data[0] = 0;     // min level is 0
    					data[1] = 0;
    				}
    				else if (setup.bRequest==0x83) { // GET_MAX
    					data[0] = FEATURE_MAX_VOLUME & 0xFF;  // max level, for range of 0 to MAX
    					data[1] = (FEATURE_MAX_VOLUME>>8) & 0x0F;
    				}
    				else if (setup.bRequest==0x84) { // GET_RES
    					data[0] = 1; // increment vol by by 1
    					data[1] = 0;
    				}
    				else { // pass over SET_MEM, etc.
    					return 0;
    				}
    				*datalen = 2;
    				return 1;
    			}
    	}
    	return 0;
    }
    Am I looking in the right place? I don't notice an "AudioOutputUSB", I wonder where I could add it to get that value?

    Jay

  14. #14
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    294
    Hi Jay,
    I've been digging further on your wanted USB input volume control.
    What you are looking for is the "Feature Unit Descriptor" [para 4.3.2.5 of the USB spec for Audio Devices].
    I googled for "Feature Unit Descriptor" and mostly you see this Feature Unit Descriptor mentioned for a USB Output Terminal, not for the Input Terminal that you want.
    For example see para "4.1. Descriptors" of this document or para "2.3.3.4 Feature Unit Descriptor" of this document.
    However, this document talks about a "Microphone - Feature Unit Descriptor" [page 30/31].
    So theoretically it looks like there is a Feature Unit Descriptor for an Input Terminal but I'm not sure how often this is actually implemented [either by Windows and/or a recording capable USB audio device].
    Perhaps Rev 2.0 of the Universal Serial Bus Device Class Definition for Audio Devices is more informative. Figure 3-2 shows a Feature Unit Descriptor for an analog input.

    Cheers,
    Paul

  15. #15
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    Broken link for Rev 2. Posting correct link below.
    http://dl.project-voodoo.org/usb-aud...20%20final.pdf

  16. #16
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    Thanks, I think you are right about this version 2.0 spec. It's the Input Terminal Descriptor in section 4.7.2.4



    4.7.2.4 Input Terminal Descriptor
    The Input Terminal descriptor (ITD) provides information to the Host that is related to the functional
    aspects of the Input Terminal.
    The Input Terminal is uniquely identified by the value in the bTerminalID field. No other Unit or
    Terminal within the AudioControl interface may have the same ID. This value must be passed in the Entity
    ID field (part of the wIndex field) of each request that is directed to the Terminal.
    The wTerminalType field provides pertinent information about the physical entity that the Input Terminal
    represents. This could be a USB OUT endpoint, an external Line In connection, a microphone, etc. A
    complete list of Terminal Type codes is provided in a separate document, USB Audio Terminal Types that
    is considered part of this specification.
    The bAssocTerminal field is used to associate an Output Terminal to this Input Terminal, effectively
    implementing a bi-directional Terminal pair. If no association exists, the bAssocTerminal field must be set
    to zero.
    The Host software can treat the associated Terminals as being physically related. In many cases, one
    Terminal cannot exist without the other. A typical example of such a Terminal pair is an Input Terminal,
    which represents the microphone, and an Output Terminal, which represents the earpiece of a headset.
    The bCSourceID contains a constant indicating to which Clock Entity the Clock Input Pin of this Input
    Terminal is connected.
    The bNrChannels, bmChannelConfig and iChannelNames fields together constitute the cluster
    descriptor. They characterize the cluster that leaves the Input Terminal over the single Output Pin
    (‘downstream’ connection). For a detailed description of the cluster descriptor, see Section 4.1, “Audio
    Channel Cluster Descriptor”.
    The bmControls field contains a set of bit pairs, indicating which Controls are present and what their
    capabilities are. If a Control is present, it must be Host readable. If a certain Control is not present then the
    bit pair must be set to 0b00. If a Control is present but read-only, the bit pair must be set to 0b01. If a
    Control is also Host programmable, the bit pair must be set to 0b11. The value 0b10 is not allowed.
    An index to a string descriptor is provided to further describe the Input Terminal.

    A table of the elements in the descriptor.

    Click image for larger version. 

Name:	usb_Input_terminal_descriptor.png 
Views:	8 
Size:	200.3 KB 
ID:	21116

    And a list of Terminal Types here https://www.usb.org/sites/default/files/termt10.pdf

    Click image for larger version. 

Name:	usb_Input_terminal_types.png 
Views:	8 
Size:	146.0 KB 
ID:	21117

  17. #17
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    Input Terminal (IT) = Receptacle for audio information flowing into the audio function.

    Feature Unit (FU) = Provides basic audio manipulation on the incoming logical audio channels.


    Attachment 21119

    It appears that one would outline the full functionality of the audio device to the host, although the processing is done within the audio device. This allows the host to control the device. This chart reminds me of the Teensy Audio Design Tool.

    Figure 3-2: Inside the Audio Function

    Attachment 21118

    As an example, consider a Volume Control inside a Feature Unit. By issuing the appropriate Get requests, the Host software can obtain values for the Volume Control’s attributes and, for instance, use them to correctly display the Control on the screen. Setting the Volume Control’s current attribute allows the Host software to change the volume setting of the Volume Control.

    After the Teensy Processes the input signal, it will send it to an "output terminal" may would go back up the USB into the host PC, or possibly out of some speaker or line output. So the Teensy can process things but the processing can be manipulated by the user via the host.

    Interesting stuff, now I guess I need to figure out how to write and get values from the feature unit. Where is the Input Feature Unit described? Is it described? It shows up in the control panel as an input...

    Last edited by JayShoe; 07-25-2020 at 03:15 AM.

  18. #18
    Senior Member PaulS's Avatar
    Join Date
    Apr 2015
    Location
    Netherlands
    Posts
    294
    Hi Jay,
    Both your attachments return this when clicked on:
    Click image for larger version. 

Name:	Capture.PNG 
Views:	4 
Size:	4.1 KB 
ID:	21123

    On topic: I think we are getting closer. What you want seems theoretically possible, but the question is how to realize it.
    I don't know yet but will keep thinking about it.
    Thanks for correcting the link to the Rev 2 spec.

    Paul

  19. #19
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    Quote Originally Posted by PaulS View Post
    Hi Jay,
    Both your attachments return this when clicked on:
    Click image for larger version. 

Name:	Capture.PNG 
Views:	4 
Size:	4.1 KB 
ID:	21123

    Ah, that was the meat and potatoes! In the spec there is this chart which shows how the descriptors relate to eachother. You were asking originally about why that volume control is useful, suggesting that input volume is adjusted on the host, not on the USB device. This chart shows why I'm seeking access to those descriptors.

    Click image for larger version. 

Name:	usb_descriptor_flow_chart.jpg 
Views:	11 
Size:	78.6 KB 
ID:	21124

    My custom ADC module has 4 microphones, and each mic has a gain setting. So I believe that's what I'm going to attempt to define in the descriptors. The result will be the ability to control the volume and the gain of each microphone from within windows, and within windows applications that understand what we are giving them control over.

    Click image for larger version. 

Name:	usb_feature_unit.png 
Views:	9 
Size:	267.3 KB 
ID:	21125

    Quote Originally Posted by PaulS View Post
    On topic: I think we are getting closer. What you want seems theoretically possible, but the question is how to realize it.
    I don't know yet but will keep thinking about it.
    Thanks for correcting the link to the Rev 2 spec.

    Paul
    Yes thanks for your correction as well. I'm looking deeper to see how the current implementation is being described, and how I can modify those descriptors to include my input controls. I will report with any progress. Thanks!

  20. #20
    Senior Member
    Join Date
    Jun 2018
    Posts
    127
    Hello,

    @PaulStoffregen, I'm wondering if you could help and/or take a bounty for this. I have some notes above, but long story short is that I would like to monitor the "usb1.inputVolume();" in the same way that we monitor "usb1.volume();".

    Sure, it would be nice to also get access to a) define more inputs (even though they wouldn't be actual "USB" inputs just inputs on the device, and b) define other features such as tone, gain, eq, etc. However, this is not ultimately that important to me at this point. The only thing I really want to do at this point is monitor this level. This level is clearly already defined in the code somewhere, because it exists, the computer knows about it. But it's not clear to me how I can monitor its value.




    I've spoken with the audio focused users on this forum (Chip, Pauls, Bob, etc) nobody can help. Those who are interested in the possibility of helping aren't able due to contractual obligations. I've tried posting on other forums, freelancers, etc, but this is a really high-level black hole for most people. So do you have any advice for me (us) as to where I can dive into the code to monitor this level?

    Thanks for whatever it is you can offer, if anything! :-)

    Jay

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •