qwazzerman
Member
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:
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.
There's also a tiny file associated with the .ino to change the board name:
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.
- Does this issue look familiar to anyone?
- 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.
- 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.
- 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?
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;
//}
}
}
}
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
};