How do I run Teensy with USB audio output on Linux?

Status
Not open for further replies.

MichaelMeissner

Senior Member+
I decided to add to my series of tests to do simple audio output to different devices (I already have DAC, DACS, Audio Shield, I2S, I2S2, and Prop Shield). So I cloned my source that does ToneSweep on both the right and left channels to target USB output. But when I start the program, it just hangs there. I suspect I need something on the Linux side that takes the sound and sends it to the speakers. What do I need to hear the sounds from the Teensy on the Linux side? I am running Fedora 33 at the moment.

Here is the program. I've tried it on a Teensy 3.2, specifying the USB type to be either Audio or Serial+Midi+Audio:

Code:
/*
  Demo of the audio sweep function.
  The user specifies the amplitude,
  start and end frequencies (which can sweep up or down)
  and the length of time of the sweep.

  Modified to eliminate the audio shield, and use the Teensy to send output via
  USB to the PC.  */

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>

// GUItool: begin automatically generated code (edited by meissner afterwards).
AudioSynthToneSweep	tonesweep;		//xy=99,198
AudioMixer4		mixer2;			//xy=280,253
AudioMixer4		mixer1;			//xy=280,175
AudioOutputUSB		usb1;			//xy=452,189

AudioConnection		patchCord1 (tonesweep, 0, mixer1, 0);
AudioConnection		patchCord2 (tonesweep, 0, mixer2, 0);
AudioConnection		patchCord3 (mixer2,    0, usb1,   1);
AudioConnection		patchCord4 (mixer1,    0, usb1,   0);
// GUItool: end automatically generated code

const float	t_ampx	= 0.8;
const int	t_lox	= 10;
const int	t_hix	= 22000;
const float	t_timex	= 10;		// Length of time for the sweep in seconds

// Do a sweep in both directions, enabling or disabling the left/right speakers
void do_sweep (int i)
{
  int do_left  = (i & 1) != 0;
  int do_right = (i & 2) != 0;
  float gain   = (do_left && do_right) ? 0.5f : 1.0f;

  Serial.printf ("Sweep up,   left = %c, right = %c\n",
		 (do_left)  ? 'Y' : 'N',
		 (do_right) ? 'Y' : 'N');

  mixer1.gain (0, do_left  ? gain : 0.0f);
  mixer2.gain (0, do_right ? gain : 0.0f);

  if (!tonesweep.play (t_ampx, t_lox, t_hix, t_timex)) {
    Serial.println ("ToneSweep - play failed");
    while (1)
      ;
  }

  // wait for the sweep to end
  while (tonesweep.isPlaying ())
    ;

  // and now reverse the sweep
  Serial.printf ("Sweep down, left = %c, right = %c\n",
		 (do_left)  ? 'Y' : 'N',
		 (do_right) ? 'Y' : 'N');

  if (!tonesweep.play (t_ampx, t_hix, t_lox, t_timex)) {
    Serial.println("ToneSweep - play failed");
    while (1)
      ;
  }

  // wait for the sweep to end
  while (tonesweep.isPlaying ())
    ;

  Serial.println ("Sweep done");
}

void setup(void)
{
  // Wait for at least 3 seconds for the USB serial connection
  Serial.begin (9600);
  while (!Serial && millis () < 3000)
    ;

  AudioMemory (8);
  Serial.println ("setup done");

  for (int i = 1; i <= 3; i++)
    do_sweep (i);

  Serial.println ("Done");
}

void loop (void)
{
}
 
Last edited:
I thought having Tonesweep would be enough to pull in the Audio USB to the loop. But I put in a dummy I2S input -> mixer -> I2S output just to be sure.

If I open up the Sound launcher in my menubar, and go to input, and select the Teensy input, I can see a bar graph as tonesweep is running. What I suspect I am missing is a Linux app that reads the sound from the USB stream and puts it on the speakers/headphones.

Code:
/*
  Demo of the audio sweep function.
  The user specifies the amplitude,
  start and end frequencies (which can sweep up or down)
  and the length of time of the sweep.

  Modified to eliminate the audio shield, and use the Teensy to send output via
  USB to the PC.  */

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code (edited by meissner afterwards).
// I2S1/I2S2 are needed to make USB output work (you need at least one
// definition of a non-USB audio event to trigger the main loop).
AudioSynthToneSweep      tonesweep1;     //xy=151.90362548828125,302.89676666259766
AudioInputI2S            i2s1;           //xy=151.90363311767578,541.8967809677124
AudioMixer4              mixer1;         //xy=361.9036407470703,318.89673614501953
AudioMixer4              mixer2;         //xy=368.9036636352539,435.8967514038086
AudioMixer4              mixer3;         //xy=373.90362548828125,535.8967666625977
AudioOutputUSB           usb1;           //xy=536.9036159515381,300.89677143096924
AudioOutputI2S           i2s2;           //xy=543.9036254882812,525.8967666625977
AudioConnection          patchCord1(tonesweep1, 0, mixer1, 0);
AudioConnection          patchCord2(tonesweep1, 0, mixer2, 0);
AudioConnection          patchCord3(i2s1, 0, mixer3, 0);
AudioConnection          patchCord4(mixer1, 0, usb1, 0);
AudioConnection          patchCord5(mixer2, 0, usb1, 1);
AudioConnection          patchCord6(mixer3, 0, i2s2, 0);
// GUItool: end automatically generated code

const float	t_ampx	= 0.8;
const int	t_lox	= 10;
const int	t_hix	= 22000;
const float	t_timex	= 10;		// Length of time for the sweep in seconds

// Do a sweep in both directions, enabling or disabling the left/right speakers
void do_sweep (int i)
{
  int do_left  = (i & 1) != 0;
  int do_right = (i & 2) != 0;
  float gain   = (do_left && do_right) ? 0.5f : 1.0f;

  Serial.printf ("Sweep up,   left = %c, right = %c\n",
		 (do_left)  ? 'Y' : 'N',
		 (do_right) ? 'Y' : 'N');

  mixer1.gain (0, do_left  ? gain : 0.0f);
  mixer2.gain (0, do_right ? gain : 0.0f);

  // I2S fake input/output
  mixer3.gain (0, 0.0f);

  if (!tonesweep1.play (t_ampx, t_lox, t_hix, t_timex)) {
    Serial.println ("ToneSweep - play failed");
    while (1)
      ;
  }

  // wait for the sweep to end
  while (tonesweep1.isPlaying ())
    ;

  // and now reverse the sweep
  Serial.printf ("Sweep down, left = %c, right = %c\n",
		 (do_left)  ? 'Y' : 'N',
		 (do_right) ? 'Y' : 'N');

  if (!tonesweep1.play (t_ampx, t_hix, t_lox, t_timex)) {
    Serial.println("ToneSweep - play failed");
    while (1)
      ;
  }

  // wait for the sweep to end
  while (tonesweep1.isPlaying ())
    ;

  Serial.println ("Sweep done");
}

void setup(void)
{
  // Wait for at least 3 seconds for the USB serial connection
  Serial.begin (9600);
  while (!Serial && millis () < 3000)
    ;

  AudioMemory (8);
  Serial.println ("setup done");

  for (int i = 1; i <= 3; i++)
    do_sweep (i);

  Serial.println ("Done");
}

void loop (void)
{
}
 
something like alsa mixer (was years since since I played with Linux audio)

That jogged my memory. I wasn't able to get alsa mixer to play (I could use F6 to change the stream to the Teensy, but I didn't hear a sound).

But if I go into the sound preferences app from my menubar, I can change the default input to be the Teensy. Then if I use the command line program:
  • play -d

It plays the sounds fine.

And FWIW, Tonesweep is considered an audio output generator, so I didn't need to declare an I2S input device, but I left it in the program.

Here is the final sketch:

Code:
// Demo of the audio sweep function.  The user specifies the amplitude, start
// and end frequencies (which can sweep up or down) and the length of time of
// the sweep.
//
// Modified to eliminate the audio shield, and use the Teensy to send output
// via USB to the PC.
//
// To play this on a Linux system, after starting the application the first time
// go into the sound preferences application on the menubar, and select the
// Teensy as the default input device.  Then from the command line issue:
//
//	play -d
//
// When building this sketch, you need to set the USB type to either:
//	Audio			(or)
//	Serial + MIDI + Audio

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code (edited by meissner afterwards).
// I2S1/I2S2 are needed to make USB output work (you need at least one
// definition of a non-USB audio event to trigger the main loop).
AudioSynthToneSweep      tonesweep1;     //xy=151.90362548828125,302.89676666259766
AudioInputI2S            i2s1;           //xy=151.90363311767578,541.8967809677124
AudioMixer4              mixer1;         //xy=361.9036407470703,318.89673614501953
AudioMixer4              mixer2;         //xy=368.9036636352539,435.8967514038086
AudioMixer4              mixer3;         //xy=373.90362548828125,535.8967666625977
AudioOutputUSB           usb1;           //xy=536.9036159515381,300.89677143096924
AudioOutputI2S           i2s2;           //xy=543.9036254882812,525.8967666625977
AudioConnection          patchCord1(tonesweep1, 0, mixer1, 0);
AudioConnection          patchCord2(tonesweep1, 0, mixer2, 0);
AudioConnection          patchCord4(mixer1, 0, usb1, 0);
AudioConnection          patchCord5(mixer2, 0, usb1, 1);
AudioConnection          patchCord6(mixer3, 0, i2s2, 0);
// GUItool: end automatically generated code

const float	t_ampx	= 0.8;
const int	t_lox	= 10;
const int	t_hix	= 22000;
const float	t_timex	= 10;		// Length of time for the sweep in seconds

// Do a sweep in both directions, enabling or disabling the left/right speakers
void do_sweep (int i)
{
  int do_left  = (i & 1) != 0;
  int do_right = (i & 2) != 0;
  float gain   = (do_left && do_right) ? 0.5f : 1.0f;

  Serial.printf ("Sweep up,   left = %c, right = %c\n",
		 (do_left)  ? 'Y' : 'N',
		 (do_right) ? 'Y' : 'N');

  mixer1.gain (0, do_left  ? gain : 0.0f);
  mixer2.gain (0, do_right ? gain : 0.0f);

  if (!tonesweep1.play (t_ampx, t_lox, t_hix, t_timex)) {
    Serial.println ("ToneSweep - play failed");
    while (1)
      ;
  }

  // wait for the sweep to end
  while (tonesweep1.isPlaying ())
    ;

  // and now reverse the sweep
  Serial.printf ("Sweep down, left = %c, right = %c\n",
		 (do_left)  ? 'Y' : 'N',
		 (do_right) ? 'Y' : 'N');

  if (!tonesweep1.play (t_ampx, t_hix, t_lox, t_timex)) {
    Serial.println("ToneSweep - play failed");
    while (1)
      ;
  }

  // wait for the sweep to end
  while (tonesweep1.isPlaying ())
    ;

  Serial.println ("Sweep done");
}

void setup(void)
{
  // Wait for at least 3 seconds for the USB serial connection
  Serial.begin (9600);
  while (!Serial && millis () < 3000)
    ;

  AudioMemory (8);
  Serial.println ("setup done");

  for (int i = 1; i <= 3; i++)
    do_sweep (i);

  Serial.println ("Done");
}

void loop (void)
{
}
 
That jogged my memory. I wasn't able to get alsa mixer to play (I could use F6 to change the stream to the Teensy, but I didn't hear a sound).

But if I go into the sound preferences app from my menubar, I can change the default input to be the Teensy. Then if I use the command line program:

play -d
Ah, many thanks. For the last few years I've wondered why I couldn't get teensy-USB audio to play on my Ubuntu boxes. Now all is good. :D
 
Status
Not open for further replies.
Back
Top