Multiple Teensys, Distorted Audio

Status
Not open for further replies.
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
};
 
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.
 
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.
 
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:
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.
 
Status
Not open for further replies.
Back
Top