Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 8 of 8

Thread: How to make Teensy 4.0 appear as MIDI Device in windows

  1. #1
    Junior Member
    Join Date
    Sep 2019
    Posts
    4

    How to make Teensy 4.0 appear as MIDI Device in windows

    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.

  2. #2
    Senior Member vjmuzik's Avatar
    Join Date
    Apr 2017
    Location
    Florida
    Posts
    347
    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.

  3. #3
    Junior Member
    Join Date
    Sep 2019
    Posts
    4
    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.

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,579
    Quote Originally Posted by Jackbite View Post
    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-ardui...r-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.

  5. #5
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    4,105
    @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.

  6. #6
    Junior Member
    Join Date
    Sep 2019
    Posts
    4
    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!

  7. #7
    Junior Member
    Join Date
    Sep 2019
    Posts
    4
    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.



    Click image for larger version. 

Name:	frontAngledSmall.jpg 
Views:	15 
Size:	121.9 KB 
ID:	17639 Click image for larger version. 

Name:	sideSmall.jpg 
Views:	8 
Size:	110.4 KB 
ID:	17640 Click image for larger version. 

Name:	rearSmall.jpg 
Views:	7 
Size:	98.2 KB 
ID:	17641

  8. #8
    Junior Member
    Join Date
    Sep 2019
    Posts
    1
    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

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •