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

Thread: Multiple Teensys, Distorted Audio

  1. #1
    Junior Member
    Join Date
    Jul 2015
    Posts
    16

    Multiple Teensys, Distorted Audio

    Hi all,
    I have a project that involves multiple (6, at the moment) Teensys with audio boards connected via USB to a single computer. Each Teensy is individually controlled by a python process, and is set up to stream audio from the python process (i.e., Teensy acts like a sound card). Teensys are in Serial + MIDI + Audio mode. Audio is being played at a sampling rate of 44.1 kHz.

    When I have several running at a time, often the device that was started most recently starts to produce very distorted audio: it's really choppy and sounds almost like the bit rate has fallen really low. Sometimes the control process even fails to start, and kicks back an error from the pyaudio module:

    Code:
    "2018-10-30 15:10:22,367",
    Unhandled exception: Traceback (most recent call last):
      [...]
      File "/home/user/Desktop/operant_Teensy/interfaces/pyaudio_.py", line 80, in _queue_wav
        self._get_stream(start=start, callback=callback)
      File "/home/user/Desktop/operant_Teensy/interfaces/pyaudio_.py", line 75, in _get_stream
        stream_callback=callback)
      File "/home/user/Desktop/operant_Teensy/core/venv/local/lib/python2.7/site-packages/pyaudio.py", line 750, in open
        stream = Stream(self, *args, **kwargs)
      File "/home/user/Desktop/operant_Teensy/core/venv/local/lib/python2.7/site-packages/pyaudio.py", line 441, in __init__
        self._stream = pa.open(**arguments)
    IOError: [Errno -9998] Invalid number of channels
    I've also noticed the distortion seems to get worse if I start additional Teensys on the same bus (attached to a powered hub). That led me to suspect some sort of problem with USB bandwidth and audio playback. I initially got around it by distributing the six different Teensys among the different buses, but it's recently started occurring again on one bus with only three Teensys attached.

    So I have a few questions.
    1. Does this issue look familiar to anyone?
    2. Is it possible to reduce the bandwidth that each Teensy uses? For example, I don't need audio in or a MIDI interface, and I'm only playing mono audio out, so I could conceivably create an entry in usb_desc.h that only creates interfaces for serial and mono audio out.
      • What files would I need to modify for that to work nicely? So far I've tried adding an entry to usb_desc.h and boards.txt but that doesn't seem to be enough - trying to upload to a Teensy with that causes it to disappear from the computer entirely.
    3. Alternatively, does the Audio interface allow serial communication with the Teensy? I've read that it provides an "emulation" but I don't know what that means in relation to actual serial communication.
    4. Or would I be best served by just getting a PCI card with an additional USB bus and trying to avoid the bandwidth issue entirely?

    For reference, here's the code uploaded to each Teensy:
    Code:
    #include <Audio.h>
    #include <Wire.h>
    #include <SPI.h>
    #include <SD.h>
    #include <SerialFlash.h>
    /*
     * Revision 1.1
     * 2/23/18 by AR
     * 
     */
    
    /*
    Pins used:
    Set by Teensyduino:
    //6 - Audio shield, MEMCS (NOT USED)
    7 - Audio shield, MOSI (SD, shared)
    9 - Audio shield, BCLK*
    10 - Audio shield, CS (SD)
    11 - Audio shield, MCLK*
    12 - Audio shield, MISO (SD, shared)
    13 - Audio shield, RX*
    14 - Audio shield, SCLK (SD)
    18 - Audio shield, SDA*
    19 - Audio shield, SCL*
    22 - Audio shield, TX*
    23 - Audio shield, LRCLK*
    
    Set by control program:
    1 - trial light
    2 - overhead light relay
    3 - response light
    16 - reinforcement relay
    37 - response sensor
    38 - start sensor
    
    */
    
    // -----
    
    static uint8_t myID[8]; //for getting unique Teensy ID
    unsigned long serialNum = 0; //for getting Teensy serial number - not sure which to use, serialNum or myID
    
    
    int inputValue = 0;
    int baudRate = 19200;
    char ioBytes[2];
    int ioPort = 0;
    
    // Audio setup
    // GUItool: begin automatically generated code
    AudioInputUSB            usb1;           //xy=200,69  (must set Tools > USB Type to Audio)
    AudioOutputI2S           i2s1;           //xy=365,94
    AudioConnection          patchCord1(usb1, 0, i2s1, 0);
    AudioConnection          patchCord2(usb1, 1, i2s1, 1);
    AudioControlSGTL5000     sgtl5000_1;     //xy=302,184
    // GUItool: end automatically generated code
    
    
    /*
    Code for getting Teensy unique ID # (so log files can accurately reflect which unit they came from)
    */
    void read_EE(uint8_t word, uint8_t *buf, uint8_t offset)  {
      noInterrupts();
      FTFL_FCCOB0 = 0x41;             // Selects the READONCE command
      FTFL_FCCOB1 = word;             // read the given word of read once area
    
      // launch command and wait until complete
      FTFL_FSTAT = FTFL_FSTAT_CCIF;
      while(!(FTFL_FSTAT & FTFL_FSTAT_CCIF))
        ;
      *(buf+offset+0) = FTFL_FCCOB4;
      *(buf+offset+1) = FTFL_FCCOB5;       
      *(buf+offset+2) = FTFL_FCCOB6;       
      *(buf+offset+3) = FTFL_FCCOB7;       
      interrupts();
    }
    
        
    void read_myID() {
      read_EE(0xe,myID,0); // should be 04 E9 E5 xx, this being PJRC's registered OUI
      read_EE(0xf,myID,4); // xx xx xx xx
      
      unsigned int ii;
      for (ii = 0; ii < sizeof(myID); ii++) {
        if ( ii > 3)
          serialNum = (serialNum << 8) + myID[ii];
      }
    }
    
    
    void setup(){
      Serial.begin(baudRate);
      while (!Serial) {
        ; // wait for serial port to connect
      }
      Serial.printf("Connected to Teensy");
      
      read_myID();
      Serial.printf("Teensy ID %d0 \n \n", serialNum);
      
      AudioMemory(12);
      sgtl5000_1.enable();
      sgtl5000_1.volume(0.4);
      
    }
    
    void loop() {
      // All serial communications should be two bytes long
      // The first byte specifies the port to act on
      // The second byte specifies the action to take
      // The actions are:
      // 0: Read the specified input
      // 1: Write the specified output to HIGH
      // 2: Write the specified output to LOW
      // 3: Set the specified pin to OUTPUT
      // 4: Set the specified pin to INPUT
      // 5: Set the specified pin to INPUT_PULLUP
      // 6: Return Teensy ID number (pin independent)
      // 99: Audio control (pin independent)
      // if we get a valid serial message, read the request:
      if (Serial.available() >= 2) {
        // get incoming three bytes:
        Serial.readBytes(ioBytes, 2);
    //    Serial.println("I received: ");
    //    Serial.println(ioBytes[0], DEC);
    //    Serial.println(ioBytes[1], DEC);
        // Extract the specified port
        ioPort = (int) ioBytes[0];
    //  if (ioPort == 99) {
    //    // 99 is for audio functions
    //    if (ioBytes[1] == 0) {
    //      //Stop playback
    //      playSdRaw1.stop();  
    //      break;
    //    }
    //    else if (ioBytes[1] == 1) {
    //        //Selected type is S+, choose file
    //        //randNumber = random(1,length(s+List))
    //        //const char *filename = S+filelist[filenumber];
    //    }
    //    else if (ioBytes[1] == 2) {
    //        //Selected type is S-, choose file
    //        //randNumber = random(1,length(s-List))
    //        //const char *filename = S-filelist[filenumber];
    //    }   
    //    //Could add additional types here
    //    
    //    playSdRaw1.play(filename);
    //    break;
    //  }
    //
    //  else
        // Specific pin actions 
        
        // Switch case on the specified action
        switch ((int) ioBytes[1]) {
          case 0: // Read an input
          inputValue = digitalRead(ioPort);
          Serial.write(inputValue);
          break;
          case 1: // Write an output to HIGH
          digitalWrite(ioPort, HIGH);       
          break;
          case 2: // Write an output to LOW
          digitalWrite(ioPort, LOW);        
          break;
          case 3: // Set a pin to OUTPUT
          pinMode(ioPort, OUTPUT);
          digitalWrite(ioPort, LOW);
          break;
          case 4: // Set a pin to INPUT
          pinMode(ioPort, INPUT);
          break;
          case 5: // Set a pin to INPUT_PULLUP
          pinMode(ioPort, INPUT_PULLUP);
          break;
          case 6: // Return Teensy ID
          Serial.write("Teensy ID: ");
          Serial.write(serialNum);
          break;
        //}
        }
      }
    }
    There's also a tiny file associated with the .ino to change the board name:
    Code:
    // To give your project a unique name, this code must be
    // placed into a .c file (its own tab).  It can not be in
    // a .cpp file or your main sketch (the .ino file).
    
    #include "usb_names.h"
    
    // Edit these lines to create your own name.  The length must
    // match the number of characters in your custom name.
    
    #define MIDI_NAME   {'B','o','a','r','d','0','1'}
    #define MIDI_NAME_LEN  7
    
    // Do not change this part.  This exact format is required by USB.
    
    struct usb_string_descriptor_struct usb_string_product_name = {
            2 + MIDI_NAME_LEN * 2,
            3,
            MIDI_NAME
    };

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,529
    Any chance you're using a Single-TT hub, or plugging them in directly?

    12 Mbit/sec devices will usually only share 480 Mbit/sec bandwidth properly if you use a Multi-TT hub.

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,529
    On Linux, you can check your hub type with "lsusb -v". It prints way too much info, so first run it without "-v" to find the vid:pid. Then run it with "-d" to look at only that device.

    For example, on my PC there are 2 hubs. Here's what a Single-TT looks like:

    Code:
    paul@preston:~ > lsusb -v -d 1a40:0101 | head
    Couldn't open device, some information will be missing
    
    Bus 008 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub
    Device Descriptor:
      bLength                18
      bDescriptorType         1
      bcdUSB               2.00
      bDeviceClass            9 Hub
      bDeviceSubClass         0 Unused
      bDeviceProtocol         1 Single TT
      bMaxPacketSize0        64
    Here's the output for a Mulit-TT hub.

    Code:
    paul@preston:~ > lsusb -v -d 05e3:0610 | head
    
    Bus 006 Device 002: ID 05e3:0610 Genesys Logic, Inc. 4-port hub
    Device Descriptor:
      bLength                18
      bDescriptorType         1
      bcdUSB               2.10
      bDeviceClass            9 Hub
      bDeviceSubClass         0 Unused
      bDeviceProtocol         2 TT per port
      bMaxPacketSize0        64
    The bDeviceProtocol line shows "2 TT per port" when you have a Multi-TT hub which is capable of allowing multiple 12 Mbit/sec devices to properly share bandwidth.

  4. #4
    Junior Member
    Join Date
    Jul 2015
    Posts
    16
    Hi Paul,
    At this point three are plugged directly into the computer, and three are plugged into the hub, and it's the ones on the hub that are acting up.
    It looks like the hub is in fact a single TT device - so that means replacing the USB hub with a multi TT device would solve those problems, then? That makes much more sense (considering this should be way below the theoretical max bandwidth of USB, haha).
    Thank you!
    (Just for reference, the USB hub I'm using is the Amazonbasics 7 port 3.0 hub, if anyone happens to come across this in the future.)

    Edit: It appears that this USB hub is multi-TT: https://www.amazon.com/gp/product/B0797NWDCB/
    I'll order it and test to confirm, but either way I'll update this thread with the results
    Last edited by qwazzerman; 10-30-2018 at 09:35 PM.

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,529
    Yup, this review looks encouraging.

    https://www.amazon.com/review/R12F7RYUKPCQX7/

    Sadly, hub manufacturers almost never label their products about which protocol their hub uses. Usually the only sure way to tell is to buy one and look at "lsusb -v" or the advanced properties in Windows 10's device manager.

  6. #6
    Junior Member
    Join Date
    Jul 2015
    Posts
    16
    So I received the hub today. I'm not actually sure what to make of the lsusb output, but it doesn't immediately read as a single TT device, which is hopeful. The value for bDeviceProtocol is actually 3, and I can't find what that means (but I'm assuming those values are independent settings rather than a scalar value, so it probably doesn't mean it's one more that multi-TT).
    Code:
    Bus 002 Device 005: ID 0bda:0411 Realtek Semiconductor Corp. 
    Couldn't open device, some information will be missing
    Device Descriptor:
      bLength                18
      bDescriptorType         1
      bcdUSB               3.10
      bDeviceClass            9 Hub
      bDeviceSubClass         0 Unused
      bDeviceProtocol         3 
      bMaxPacketSize0         9
      idVendor           0x0bda Realtek Semiconductor Corp.
      idProduct          0x0411 
      bcdDevice            1.36
      iManufacturer           1 
      iProduct                2 
      iSerial                 0 
      bNumConfigurations      1
      Configuration Descriptor:
        bLength                 9
        bDescriptorType         2
        wTotalLength           31
        bNumInterfaces          1
        bConfigurationValue     1
        iConfiguration          4 
        bmAttributes         0xe0
          Self Powered
          Remote Wakeup
        MaxPower                0mA
        Interface Descriptor:
          bLength                 9
          bDescriptorType         4
          bInterfaceNumber        0
          bAlternateSetting       0
          bNumEndpoints           1
          bInterfaceClass         9 Hub
          bInterfaceSubClass      0 Unused
          bInterfaceProtocol      0 Full speed (or root) hub
          iInterface              5 
          Endpoint Descriptor:
            bLength                 7
            bDescriptorType         5
            bEndpointAddress     0x81  EP 1 IN
            bmAttributes           19
              Transfer Type            Interrupt
              Synch Type               None
              Usage Type               Feedback
            wMaxPacketSize     0x0002  1x 2 bytes
            bInterval               8
            bMaxBurst               0
    
    Bus 002 Device 004: ID 0bda:0411 Realtek Semiconductor Corp. 
    Couldn't open device, some information will be missing
    Device Descriptor:
      bLength                18
      bDescriptorType         1
      bcdUSB               3.10
      bDeviceClass            9 Hub
      bDeviceSubClass         0 Unused
      bDeviceProtocol         3 
      bMaxPacketSize0         9
      idVendor           0x0bda Realtek Semiconductor Corp.
      idProduct          0x0411 
      bcdDevice            1.36
      iManufacturer           1 
      iProduct                2 
      iSerial                 0 
      bNumConfigurations      1
      Configuration Descriptor:
        bLength                 9
        bDescriptorType         2
        wTotalLength           31
        bNumInterfaces          1
        bConfigurationValue     1
        iConfiguration          4 
        bmAttributes         0xe0
          Self Powered
          Remote Wakeup
        MaxPower                0mA
        Interface Descriptor:
          bLength                 9
          bDescriptorType         4
          bInterfaceNumber        0
          bAlternateSetting       0
          bNumEndpoints           1
          bInterfaceClass         9 Hub
          bInterfaceSubClass      0 Unused
          bInterfaceProtocol      0 Full speed (or root) hub
          iInterface              5 
          Endpoint Descriptor:
            bLength                 7
            bDescriptorType         5
            bEndpointAddress     0x81  EP 1 IN
            bmAttributes           19
              Transfer Type            Interrupt
              Synch Type               None
              Usage Type               Feedback
            wMaxPacketSize     0x0002  1x 2 bytes
            bInterval               8
            bMaxBurst               0
    I'm going to move forward with the assumption that this hub is multi-TT, but I'll update this thread if I find otherwise.

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,529
    Maybe it's actually appearing as 2 hubs, one for USB3 and another for USB2 ?

Posting Permissions

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