Teensy 4 Multi-pin TDM audio, Multi-channel USB, and variable sample rates


Active member
Hi all I just submitted the following pull requests and wanted to get your take on them.

These two pull requests do the following:
  • Allows for the TDM interface to use all 4 pins for either play or record. This means you can play or record up to 64 channels on 4 TDM busses (well, 4 pins of the same TDM bus)
  • Allows the USB interface to have a configurable number of channels. Yes, you can stream 32 channels of audio to or from your teensy.
  • Allows you to set the sample rate to 44100, 48000, 32000, 16000, 8000.

This does seem to work for my purposes, but needs review and further testing.

Thanks for the works !
Out of sheer curiosity, what is the maximum number of in/out did you manage to get working in your test
At a quick glance on the code the audio samples bit depth is still hardcoded in 16bits right ?
I ask because some codecs only handle 32bits samples which makes it incompatible with the TDM driver as it is.
(https://forum.pjrc.com/index.php?threads/tdm512.73977/ for reference)
Finally got round to trying the multi-channel USB, in the hope you found something @mcginty and I missed. No such luck, I'm afraid, I still get glitches:
2024-03-03 20_40_06-GoldWave.png

My test code is this:
#include <Audio.h>

#define AUDIO_kHz ((int) AUDIO_SAMPLE_RATE / 1000)

extern "C"
    struct usb_string_descriptor_struct
        uint8_t bLength;
        uint8_t bDescriptorType;
        uint16_t wString[6+1+1+2+1];
  usb_string_descriptor_struct usb_string_serial_number={
    {'A','u','d','i','o','-','0'+AUDIO_CHANNELS,'/','0'+(AUDIO_kHz / 10),'0' + (AUDIO_kHz % 10),'B'}

// GUItool: begin automatically generated code
AudioSynthWaveform       wav1; //xy=479,580
AudioSynthWaveform       wav2;      //xy=484,616
AudioSynthWaveform       wav3; //xy=486,654
AudioSynthWaveform       wav4; //xy=490,691
AudioSynthWaveform       wav5; //xy=494,729
AudioSynthWaveform       wav6; //xy=499,765
AudioSynthWaveform       wav7; //xy=503,802
AudioSynthWaveform       wav8; //xy=507,840
AudioInputUSB         usb_oct_in;       //xy=524,931
AudioMixer4              mixer1;         //xy=728,896
AudioMixer4              mixer2; //xy=733,968
AudioMixer4              mixer3; //xy=870,946
AudioOutputI2S           i2sOut;           //xy=939,684
AudioOutputUSB        usb_oct_out;       //xy=942,589

AudioConnection          patchCord1(wav1, 0, usb_oct_out, 0);
AudioConnection          patchCord2(wav1, 0, i2sOut, 0);
AudioConnection          patchCord3(wav2, 0, usb_oct_out, 1);
AudioConnection          patchCord4(wav3, 0, usb_oct_out, 2);
AudioConnection          patchCord5(wav4, 0, usb_oct_out, 3);
AudioConnection          patchCord6(wav5, 0, usb_oct_out, 4);
AudioConnection          patchCord7(wav6, 0, usb_oct_out, 5);
AudioConnection          patchCord8(wav7, 0, usb_oct_out, 6);
AudioConnection          patchCord9(wav8, 0, usb_oct_out, 7);
AudioConnection          patchCord10(usb_oct_in, 0, mixer1, 0);
AudioConnection          patchCord11(usb_oct_in, 1, mixer1, 1);
AudioConnection          patchCord12(usb_oct_in, 2, mixer1, 2);
AudioConnection          patchCord13(usb_oct_in, 3, mixer1, 3);
AudioConnection          patchCord14(usb_oct_in, 4, mixer2, 0);
AudioConnection          patchCord15(usb_oct_in, 5, mixer2, 1);
AudioConnection          patchCord16(usb_oct_in, 6, mixer2, 2);
AudioConnection          patchCord17(usb_oct_in, 7, mixer2, 3);
AudioConnection          patchCord18(mixer1, 0, mixer3, 0);
AudioConnection          patchCord19(mixer2, 0, mixer3, 1);
AudioConnection          patchCord20(mixer3, 0, i2sOut, 1);

AudioControlSGTL5000     sgtl5000;     //xy=946,745
// GUItool: end automatically generated code

AudioSynthWaveform* wavs[] = {

AudioMixer4* mixers[] = {&mixer1,&mixer2};

uint32_t ledOff;

void setup()

  while (!Serial)

  if (CrashReport)
  // sgtl5000.setAddress(HIGH);

  for (int i=0;i<8;i++)
    wavs[i]->begin(0.5f,220.0f + 110.0f*i,WAVEFORM_TRIANGLE);

  Serial.printf("Audio block size %d samples; sample rate %.2f; %d channels\n",AUDIO_BLOCK_SAMPLES,AUDIO_SAMPLE_RATE_EXACT,AUDIO_CHANNELS);

void loop()
  if (millis() > ledOff)
    digitalWrite(LED_BUILTIN,0); // ignore this, left from previous test!
Teensy 4.1 + audio adaptor (modified to alternate I²C address); Windows 10 x64; Arduino 1.8.19; Teensyduino 1.59 + your USB modifications; USB Type is set to Serial+MIDI+Audio.

To reproduce, I record 10s of the 8-channel output in Goldwave, then play it back through the Teensy. It can take a couple of go-rounds for the glitches to start happening in the recorded audio.
Just trying the multi-pin TDM, and it looks as if output_tdm.cpp only has some of the required modifications; the register bit-twiddling code is all in there, but the N-pin setup in begin() isn't, and neither are the changes in isr() or the associated interleaving function.

If you can sort that out, I have a Frankenaudio lash-up ready to test - one CS42448 and one PCM3168 for a first crack at 14i16o TDM magic!