How to make Teensy 4.0 appear as MIDI Device in windows

Status
Not open for further replies.

Jackbite

Member
I currently only have one selection for USB Type, (Serial), after selecting Board Type Teensy 4.0.

Seems like such a radical change from 3.6 so I'm guessing I've missed something simple.

Currently using 3.6 in "Serial - MIDI" and porting to 4.0.
 
Other USB modes for Teensy 4.0 are yet to be implemented, but when they are it will have the same functionality that the 3.X’s have.
 
Anyone have an idea when that might be implemented? That's a major chunk of functionality missing making the two 4.0s I just purchased super fast but useless to me.

Any ideas (or tricky stuff) on how to implement the midi interface in the interim? I currently receive midi information on usb host (usb 2) then send out a modified version of the messages (including sysEx) via usb midi (usb 1).

My project is an eight track midi looper and currently on 3.6, it stays musically precise through four to five simultaneously playing tracks so my hope was the 4.0 would put me in the eight to 10 track range.
 
Anyone have an idea when that might be implemented?

USB host MIDI already works on Teensy 4.0. So does regular serial MIDI.

Sometime in October is pretty likely for USB MIDI device. It will appear here (this forum) first as a beta test version, so keep an eye on the announcement section if you want to get it as early as possible.

Regarding higher performance, the very best thing you can do would be to create a test case that can be run on a single Teensy without any other hardware, or perhaps 2 programs for 2 Teensy boards connected together. Try to build in some sort of measurement of how well it's keeping up. Maybe use micros() or elapsedMicroseconds variables and occasionally Serial.print() that info about timing? Or if that changes the timing too much, maybe toggle pins with digitalWriteFast(), which can be measured with an oscilloscope or logic analyzer to check how well it's performing.

If you create something like that and I'm able to run it without any other MIDI hardware or special software on the PC side (but connecting an oscilloscope is perfectly fine), I'll give it a try here if you remind me when the early betas are published. I can't make any specific promises in advance, but that sort of performance test being available while the code is still under active development is the very best way to get me to optimize it well for your needs. ;)

On the USB support in general, over the last 4 weeks nearly all my engineering time has gone into speeding up the Arduino Serial Monitor. The sad reality is Teensy 4.0 with Serial.print() sends so fast that the Arduino IDE runs out of memory and locks up. That really had to be fixed. Fortunately, that work is pretty much completed and I wrote this article about the process.

https://www.pjrc.com/improving-arduino-serial-monitor-performance/

The next major step on USB device support is unfortunately not MIDI. We're still not fully USB standards compliant, mainly regarding bulk endpoint max packet size and the "other speed" descriptors. The code is also not yet well optimized, and of course I've been putting off more optimization work while the already less-than-optimal code could crash the Arduino software. Now that the serial monitor can handle sustained high speed data, I can start working on the Teensy side.

USB device MIDI and the other USB types are a high priority. They will be implemented. Unfortunately this stuff all takes time, almost always longer than originally anticipated, and a number of pretty important things need to happen to have a solid foundation to build all that USB functionality.

Getting the USB code well optimized for 480 Mbit/sec speed is pretty likely to bring you the much higher MIDI performance you're seeking. Today we're achieving only about 15% to 20% of the theoretical speed. I am going to get that up much higher. Even though it means delaying MIDI and other USB types a little longer, focusing on that first while the code is still simple (only USB serial) is the best path to ultimately achieving excellent performance for MIDI, Audio and all the other types.
 
@PaulStoffregen

Wow! Some article. Was just skimming through it and its a really thorough overview and explanation of what you had to do in order to get it operational. You don't always get that from the reading the info in the threads.
 
Thanks for the article Paul! That's awesome and thank you for offering to scope my code. I unfortunately do not own a scope but would love to know just how stable it is. I've included the Clock part of my code for now for scrutiny/review and maybe to throw on the scope to verify. I have a little clean up to do on the MIDI code but will post it prior to the start of USB MIDI support being addressed. Heck, someone might know how to do this in three lines of ninja code or have ideas for optimization so I'm extremely open to adaptation.

Code:
/*
 
  This code attempts to create a stable clock with granularity
  capable of recording/playing MIDI messages with timing accuracy
  perceived as "musical" to the human ear.  

  Highly trained musicians can detect as little as 5 milliseconds of 
  latency while average listeners will not be bothered by even 20-30 
  milliseconds of delay. This project provides a musical instrument 
  for recording and looping up to 8 tracks of MIDI music similtaneously.  
  
  The Clock portion of the code uses a zero priority IntervalTimer along 
  with a deprioritized Systick to create a stable Clock "Tick". It then 
  determines the average loop() iterations within one "Tick" and uses a 
  slightly lesser number of those iterations to process the programmer's 
  functional code or CODE BLOCK without any possibility of meaningful 
  code being interrupted. 
  
  This creates a unique mechanism.  Interval based processing capable of
  handling highly dynamic data flows and resultant processing where the 
  programmer can "tune" the processing window to handle a variety of real 
  world dynamic scenarios under complete control. (no inturrupt guesswork)  
    
*/

IntervalTimer CLK; 

const byte TICK_TIME = 40; // microseconds
const float PERCENT_OF_LOOPS_USED_FOR_PROCESSING = 0.80;
const float LOW_LOOP_COUNT_WARNING_PERCENTAGE = 0.10;
const byte MAX_CLOCK_SAMPLES = 10;

enum eClockState{
  Initializing,
  Sampling,
  Starting,
  Running
};


// Clock Vars
volatile byte clockState = 0;
volatile int loopIterationsAllowed = 0;
volatile int loopCount = 0;
volatile boolean clockTick = false;
volatile int clockErr = 0;

unsigned long pClockErrTime = micros();
byte clockErrCount = 0;


#define RESTART_ADDR 0xE000ED0C
#define READ_RESTART() (*(volatile uint32_t *)RESTART_ADDR)
#define WRITE_RESTART(val) ((*(volatile uint32_t *)RESTART_ADDR) = (val))


void setup() {
  Serial.begin(115200);
  delay(100);
    
  SCB_SHPR3 = 0x20200000;  // Systick = priority 32 (defaults to zero)
  
  CLK.priority(0);
  CLK.begin(handleClock, TICK_TIME);
  CLK.priority(0);

  clockState = Sampling;
}

void loop() {


//*********************************************************************
//****      PROCESS CLOCK ERRORS     **********************************
//*********************************************************************

  //Occasionally, Teensy will start up with unstable TimerInterval priority
  //and blow a bunch of errors so it automatially reboots itself until a 
  //stable clock is achieved.  It's rare, but it happens.
  
  if (clockErr){
    Serial.println("Clock Error !!");
    if (micros() - pClockErrTime < 8000){
      clockErrCount++;
      if (clockErrCount > 5){
        Serial.println("RESETTING CLOCK!!!!!!!!!!!");
        delay(100);
        WRITE_RESTART(0x5FA0004);           
      }
    }
    else if (micros() - pClockErrTime > 100000){
      clockErrCount = 0;
    }
    pClockErrTime = micros(); 
  }




//*********************************************************************
//****      PROCESS CLOCK INCREMENT     *******************************
//*********************************************************************

  if (clockState == Starting){
    
    //Initialization code goes here after stable clock is established 
    //and the appropriate loop() processing pool is determined.
    
    clockState = Running;
  }
  if (clockTick){
    
    clockTick = false;
    
    if (clockState == Running) {

      //Should loop() processing take long enough to cause a 10% drop
      //in actual loop() iterations (loopCount) below the 
      //pre-established allowable iterations (loopIterationsAllowed),
      //throw a warning.
      
      if (loopCount < loopIterationsAllowed * (1 - LOW_LOOP_COUNT_WARNING_PERCENTAGE)){
        Serial.print("LOW LOOP COUNT  ");
        Serial.println(loopCount);
      }

      //Set processing flags for Tick dependent functionality in CODE BLOCK. 

    }
    loopCount = 0;
  }
  loopCount++;


//*********************************************************************
//******      PROCESS CODE BLOCK      *********************************
//*********************************************************************

  if (clockState == Running){
    if (loopCount < loopIterationsAllowed){

      //CODE BLOCK goes here!  

    }
  }
}

void handleClock(void){
  
  static byte clockSample;
  static int loopCountAvg = 0;
  
  unsigned long t = micros();
  static unsigned long p = micros();

  if (clockState == Initializing){
    //Save the previous loop() iteration count for averaging during sampling time.
    loopCountAvg = loopCount;
  }
  else if (clockState == Sampling){
    clockSample++;
    if (clockSample < MAX_CLOCK_SAMPLES){
      loopCountAvg = (loopCountAvg + loopCount) / 2;    
      //Serial.println(loopCountAvg);
    }
    else if (clockSample == MAX_CLOCK_SAMPLES){
      clockState = Starting;
      loopIterationsAllowed = loopCountAvg * PERCENT_OF_LOOPS_USED_FOR_PROCESSING;
      Serial.print("Setting Allowable Loop Count: ");
      Serial.println(loopIterationsAllowed);
    }
  }
  else if (clockState == Running){
    if (t - p != TICK_TIME){
      clockErr = true;
    }
  }
  p = t;
  clockTick = true;
}


By tweaking TICK_TIME and PERCENT_OF_LOOPS_USED_FOR_PROCESSING, I've been able to achieve musical granularity with enough MIDI processing bandwidth to provide 3-4 tracks of simultaneous "real world" record/playback using 2 MIDI sources (e.g. e-drums, keyboards, etc.) and 2 musicians playing simultaneously. Interestingly enough, when running the code above on 3.6 clocked at 240khz optimized with "Fastest + pure-code with LTO", it carves out 41-44 loops for uninterrupted processing. On 4.0 optimized "Fastest", it yielded 200-210, WOW!! I'm 100% confident I'll get my targeted 8-10 tracks with room to lower that TICK_TIME for even more accurate MIDI timing.

Cant wait for this functionality in 4.0!! Keep up the great work!
 
Here are some pics of the project. You select your track, set the midi channel, set what instrument that that channel maps to in your DAW and hit the trigger pad to Record. Hit the pad again to Play then once more to stop the track. It has a usb port in the back that extends to a foot pedal for keyboard players to trigger Record/Play/Stop hands free and a 4 port powered usb hub on the side for instrument input.



frontAngledSmall.jpg sideSmall.jpg rearSmall.jpg
 
I think there should be something about this on the description page for the Teensy 4.0 or a big bold notice when ordering. I just bought two of them to use in Midi projects. If I'd known, I would just have ordered a couple of 3.2's
 
Are there any more details on timeline for implementing USB Types? Also, do we know what version of the arduino ide will include the fix provided by Paul in the article above??
 
Are there any more details on timeline for implementing USB Types? Also, do we know what version of the arduino ide will include the fix provided by Paul in the article above??

Bumping! I would be quite interested by a status update on this, and maybe pointers of how to help move the topic forward, maybe contributing (if it is possible? I am a pure beginner in the teensy world :p)
 
Hi Paul, any update on the USB type selection for Teensy 4? I got Teensy4 + Audio board to use for Line in to USB Audio and realized it doesn't work. Basically I need the USB > Audio. Any estimate on when this feature will be available? Thanks for your awesome boards!
 
I've committed USB MIDI code for Teensy 4.0 to github.

https://github.com/PaulStoffregen/cores

If you're feeling adventurous, edit your boards.txt file to enable MIDI in the USB Type menu, and update your {Arduino}/hardware/teensy/avr/cores folder with the latest code from github.

Soon I'll publish a 1.49-beta2 installer, so you can get this the easy way.
 
Teensyduino 1.49-beta2 is now available, with USB MIDI support for Teensy 4.0.

https://forum.pjrc.com/threads/58654-Teensyduino-1-49-Beta-2

Please give this a try and let me know how the USB MIDI works for you? I'm not a musician or DJ. I don't use software like Ableton, Garage Band or Logic. I mostly do rather synthetic tests and verify communication with a USB protocol analyzer, so I'm really depending on your feedback.
 
Teensyduino 1.49-beta2 is now available, with USB MIDI support for Teensy 4.0.

https://forum.pjrc.com/threads/58654-Teensyduino-1-49-Beta-2

Please give this a try and let me know how the USB MIDI works for you? I'm not a musician or DJ. I don't use software like Ableton, Garage Band or Logic. I mostly do rather synthetic tests and verify communication with a USB protocol analyzer, so I'm really depending on your feedback.

Thanks Paul, will check this against Ableton / Reaper / Cubase on windows - and custom stuff on Linux.

j.
 
Thanks Paul for the update. I installed and it shows all the USB types except AUDIO. Are you going to add Audio too?
What I want to do is send stereo audio through line in and pass it through USB as audio. I could do it on Teensy 3.2 but not in Teensy 4.0 since no USB Audio option available in the dropdown menu. Any suggestion?
 
Are you going to add Audio too?

Yes, but later.

Audio requires isochronous transfer, which is more complex and more difficult to test. We use it in asynchronous mode, which uses a slightly different rate feedback protocol at 12 vs 480 Mbit speed, so significant new code is needed.

I will do all that stuff, but it's going to take more time. I wanted to get these simpler, very widely used USB types published sooner, rather than hold up everything just for audio.
 
Wow, looks like a really interesting project. Hope you'll share some more info as it develops?

I'm really curious to hear if you see much of a difference in timing & latency with Teensy 4.0's USB MIDI, which is 480 MBit/sec. Even though MIDI uses only a small fraction of even 12 MBit/sec USB's bandwidth, when running at 480 MBit/sec the USB latency may be lower due to 125 us micro-frame timing vs 1ms frames, and the tighter timing specs used at 480 speed.

Here are some pics of the project. You select your track, set the midi channel, set what instrument that that channel maps to in your DAW and hit the trigger pad to Record.

View attachment 17639
 
One last followup to this old thread. Teensyduino 1.49 has been released. USB MIDI device mode is now fully supported on Teensy 4.0.
 
Status
Not open for further replies.
Back
Top