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

Thread: Bug report: Teensy 4.0 and AudioPlayQueue

  1. #1
    Junior Member
    Join Date
    Apr 2020
    Posts
    8

    Bug report: Teensy 4.0 and AudioPlayQueue

    When AudioPlayQueue.getBuffer() and AudioPlayQueue.playBuffer() are used in an audio rate ISR, they slowly consume AudioMemory buffers and T4.0 crashes when all buffers are used (default 80 for IMXRT1062). The same code works flawlessly on Teensy 3.6 for hours, on 4.0 it cracks up in ~570s. I tried everything I could to bypass this issue, but no luck so far.

    Here's a code to test/reproduce this issue. I would be thankful if someone could run this on T4.0 and report back. If you reduce to AudioMemory(16) in Setup, it will freeze in ~100s.
    Code:
    #include <Audio.h>
    #include <SerialFlash.h>
    
    AudioPlayQueue           audio;
    AudioPlayQueue           audio2;
    AudioOutputI2S           i2s1;
    AudioConnection          patchCord1(audio, 0, i2s1, 0);
    AudioConnection          patchCord2(audio2, 0, i2s1, 1);
    
    uint8_t cycle=0;
    elapsedMillis tme;
    
    void audioISR()    //dummy ISR routine to test getBuffer()/playBuffer()
    {
      if((cycle % 128) == 127){     //for 128 int16 array
        int16_t *p1 = audio.getBuffer();
        p1 = p1; // to avoid unused variable compiler warning
        audio.playBuffer();
      }
      cycle++;
    }
    
    void setup()
    {
      AudioNoInterrupts();
      noInterrupts();
      AudioMemory(512);
      Serial.begin(9600);
      IntervalTimer *t1 = new IntervalTimer();
      t1->begin(audioISR, 1000000.0f / 44100.0f);
      interrupts();
      AudioInterrupts();
    }
    
    void loop()
    {
      if (tme>10000)
      {
        tme=0;
        Serial.printf("Run time: %i s \n", (millis()/1000));
        Serial.printf("CPU: %.2f (%.2f),  MEM: %i (%i)\n",
        AudioProcessorUsage(), AudioProcessorUsageMax(),
        AudioMemoryUsage(), AudioMemoryUsageMax());
        AudioProcessorUsageMaxReset();
        AudioMemoryUsageMaxReset();
      }
    }
    Reports from Serial Monitor:

    Teensy 4.0, no AudioPlayBuffer

    Run time: 10s
    CPU: 0.51 (0.54), MEM: 2 (2)

    Run time: 200s
    CPU: 0.51 (0.54), MEM: 2 (2)

    Run time: 2340s
    CPU: 0.51 (0.54), MEM: 2 (2)
    Teensy 4.0, one AudioPlayBuffer

    Run time: 10s
    CPU: 0.52 (0.57), MEM: 3 (4)

    Run time: 20s
    CPU: 0.52 (0.56), MEM: 4 (5)

    Run time: 30s
    CPU: 0.52 (0.57), MEM: 6 (7)

    Run time: 200s
    CPU: 0.52 (0.57), MEM: 29 (30)

    Run time: 560s
    CPU: 0.52 (0.57), MEM: 79 (80)

    Run time: 570s
    CPU: 0.52 (0.57), MEM: 80 (81)

    ======= CRASH =======
    Teensy 4.0, two AudioPlayBuffers

    Run time: 10s
    CPU: 0.52 (0.55), MEM: 4 (6)

    Run time: 20s
    CPU: 0.52 (0.55), MEM: 6 (8)

    Run time: 30s
    CPU: 0.52 (0.55), MEM: 10 (12)

    Run time: 200s
    CPU: 0.52 (0.55), MEM: 56 (58)

    Run time: 560s
    CPU: 0.52 (0.56), MEM: 156 (158)

    Run time: 570s
    CPU: 0.52 (0.56), MEM: 158 (160)

    ======= CRASH =======
    Teensy 3.6, two AudioPlayBuffers

    Run time: 10s
    CPU: 0.89 (0.96), MEM: 4 (4)

    Run time: 200s
    CPU: 0.89 (0.97), MEM: 2 (4)

    Run time: 2340s
    CPU: 0.89 (0.95), MEM: 2 (4)
    Thanks in advance for your kind answers!

  2. #2
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,366
    It is not safe to use getBuffer or playBuffer in an interrupt routine. The getBuffer function can call yield() and playBuffer has a while loop which won't terminate unless something external (in an interrupt) happens.
    I don't know why it doesn't crash in a T3.6, perhaps you need to let it run much longer.

    Pete

  3. #3
    Junior Member
    Join Date
    Apr 2020
    Posts
    8
    Like I said, it was tested on T3.6 for hours and it works very well. I'm aware it's potentially unsafe, but:
    a) It doesn't crash just like that, I specified exactly what's going on. I know why it happens and when it happens, I just have no idea why or how to prevent/solve it
    b) It's AFAIK the only way to communicate data from Arduino sketch to the AudioLibrary. That's a bummer
    c) How else are you supposed to use getBuffer and playBuffer with just 128 words of data? It gets exhausted in 0.003s, certainly calling it in Void Loop won't do the trick

    If anybody knows a way to use Teensy 4 I2S/TDM without audio library, suggestions are welcome.

  4. #4
    Senior Member
    Join Date
    Nov 2012
    Posts
    1,366
    certainly calling it in Void Loop won't do the trick
    Why not? The T4 chugs along at 600MHz. It can do a lot in 3 milliseconds.

    Try this:
    Code:
    #include <Audio.h>
    #include <SerialFlash.h>
    
    AudioPlayQueue           audio;
    AudioPlayQueue           audio2;
    AudioOutputI2S           i2s1;
    AudioConnection          patchCord1(audio, 0, i2s1, 0);
    AudioConnection          patchCord2(audio2, 0, i2s1, 1);
    
    uint8_t cycle=0;
    elapsedMillis tme;
    
    // set by the ISR
    volatile uint8_t int_flag = 0;
    
    void audioISR()    //dummy ISR routine to test getBuffer()/playBuffer()
    {
      int_flag = 1;
    }
    
    void setup()
    {
      AudioNoInterrupts();
      noInterrupts();
      AudioMemory(512);
      Serial.begin(9600);
      IntervalTimer *t1 = new IntervalTimer();
      t1->begin(audioISR, 1000000.0f / 44100.0f);
      interrupts();
      AudioInterrupts();
    }
    
    void loop()
    {
      if( int_flag) {
        int_flag = 0;
        if((cycle % 128) == 127) {    //for 128 int16 array
          int16_t *p1 = audio.getBuffer();
          p1 = p1; // to avoid unused variable compiler warning
          audio.playBuffer();
        }
        cycle++;
      }
      if (tme>10000) {
        tme=0;
        Serial.printf("Run time: %i s \n", (millis()/1000));
        Serial.printf("CPU: %.2f (%.2f),  MEM: %i (%i)\n",
              AudioProcessorUsage(), AudioProcessorUsageMax(),
              AudioMemoryUsage(), AudioMemoryUsageMax());
        AudioProcessorUsageMaxReset();
        AudioMemoryUsageMaxReset();
      }
    }
    Pete

  5. #5
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    618
    I am a little puzzled why you use TWO interrupt routines, the queue in the audio lib and an additional one!? But maybe I am misunderstanding your approach.
    I use the record and the play queue with great success with both, the T3.6 and the T4.0 without problems.
    But I do NOT use an IntervalTimer, but I only use the audio lib with its built-in interrupt.
    Maybe also you have to use FreeBuffer in order to release audio blocks?

  6. #6
    Junior Member
    Join Date
    Apr 2020
    Posts
    8
    Quote Originally Posted by el_supremo View Post
    Why not? The T4 chugs along at 600MHz. It can do a lot in 3 milliseconds.
    Well, because I'm doing some other not-so-timing-sensitive things in Void Loop
    Reading inputs, calculating huge amounts of side data and drawing a meaningful representation of all that to SPI LCD takes exactly 7ms per one loop (and I had to write my own specialized routine to drive the LCD, otherwise with the generic library it took 36ms ). You can imagine what it sounds like with 7ms delay...

    Quote Originally Posted by el_supremo View Post
    Try this:
    // set by the ISR
    volatile uint8_t int_flag = 0;

    void audioISR()
    {
    int_flag = 1;
    }
    Thanks for your sugestion Pete, that's exactly where I was heading! In fact I'm trying to do just that right now, even called the variable int_flag (but declared volatile bool instead of uint8) I'll try to prioritize playBuffer() routine and use the remaining time for everything else, hopefully that will not screw up the rest of things going on in the loop.

  7. #7
    Junior Member
    Join Date
    Apr 2020
    Posts
    8
    Quote Originally Posted by DD4WH View Post
    I am a little puzzled why you use TWO interrupt routines, the queue in the audio lib and an additional one!? But maybe I am misunderstanding your approach.
    I'm actually using three interrupt routines in my code, not counting the Audio Lib (which I don't have any control over) One fast at audio rate (44.100Hz) to produce the complex waveforms, one medium at 8.000Hz to crunch and prepare the math calculations for the filters and one slow at 1.000Hz to process the inputs

    Quote Originally Posted by DD4WH View Post
    I use the record and the play queue with great success with both, the T3.6 and the T4.0 without problems. But I do NOT use an IntervalTimer, but I only use the audio lib with its built-in interrupt.
    That's exactly the problem: Audio Lib just doesn't do what I'm trying to accomplish, and the methods to communicate between Arduino sketch and the library are severely limited. So I'm doing everything that's needed in my own routines, and then just serve them to the Audio Lib playBuffer() for output.

    Quote Originally Posted by DD4WH View Post
    Maybe also you have to use FreeBuffer in order to release audio blocks?
    I was under impression that's done automatically by the Audio Lib when all audio blocks are used? At least it works that way on T3.6, beats me why the things would be any different on T4.0. But I'll certainly try if nothing else works, thanks for your suggestion.

  8. #8
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,949
    Warning, I more or less never use Audio library... Not because anything is wrong with it, but more because most Audio I typically need is a series of Beeps...

    But if it were me and I needed (or really wanted to control this with ISR doing this.)

    The things I would experiment with include:

    a) Don't call getBuffer from ISR as this can call yield... Instead call available and bail if it returns false. Or better yet remember it failed and try again on your next ISR.

    b) See if one could add a new member function which tries to queue the buffer, but fails if queue is full, and again remember this and try to queue it on the next ISR.
    This function would be simple... Not sure what @PaulStoffregen would think of it worked and you did a PR... But something like:
    Code:
    bool AudioPlayQueue::queueBufferNoWait(void)
    {
    	uint32_t h;
    
    	if (!userblock) return false;
    	h = head + 1;
    	if (h >= max_buffers) h = 0;
    	if (tail == h) return false;
    	queue[h] = userblock;
    	head = h;
    	userblock = NULL;
    	return true;
    }
    But again I don't know if this will fix your issue or not. Hard to know exactly where you are, what crashed...

  9. #9
    Junior Member
    Join Date
    Apr 2020
    Posts
    8
    Quote Originally Posted by KurtE View Post
    Warning, I more or less never use Audio library... Not because anything is wrong with it, but more because most Audio I typically need is a series of Beeps...
    It's OK, we all have our thing and I really want to hear all sugestions and/or feedback

    Quote Originally Posted by KurtE View Post
    But if it were me and I needed (or really wanted to control this with ISR doing this.)

    The things I would experiment with include:

    a) Don't call getBuffer from ISR as this can call yield... Instead call available and bail if it returns false. Or better yet remember it failed and try again on your next ISR.
    Ugh. I've tried to send a flag from ISR and use getBuffer() and playBuffer() in the loop() - been testing this for a few days - but it's extremely unstable. I have to put all blocking stuff in the loop, so there's a 7ms delay between cycles... Even if I comment out all blocking routines, the results are unpredictable - ISRs suck the most of available cycles and it produces all kinds of noises and bumps. All this must be pretty much confusing for the guys who use Teensy to do simpler stuff, but I'm seriously thinking about installing a cooler and bumping it up to 1GHz
    TL/DR: loop() is not usable.

    Quote Originally Posted by KurtE View Post
    b) See if one could add a new member function which tries to queue the buffer, but fails if queue is full, and again remember this and try to queue it on the next ISR.
    This function would be simple... Not sure what @PaulStoffregen would think of it worked and you did a PR...
    Thanks for your suggestion Kurt, I'll try this for sure!

    Quote Originally Posted by KurtE View Post
    But again I don't know if this will fix your issue or not. Hard to know exactly where you are, what crashed...
    We'll see, I've been testing this for a long time. Lowering the proc speed doesn't help, if the ISR is slowed down to 22.050Hz everything works, if I skip every second cycle everything works, if I use T3.6 everything works, so obviously the ISR on T4.0 is interrupting the Audio Lib before it can release memory. I've described the problem in details, and would really like to hear from @PaulStoffregen about this

Posting Permissions

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