T4.0 ILI9341_t3. DemoSauce WavFilePlayer mashup causes crashes

bicycleguy

Well-known member
As title suggests playing wave files while displaying DemoSauce causes a crash when the '_twistyText' animation runs.
On Mac, I'm using Arduino 1.8.13, TL1.54-beta7 with PJRC T4, 320x240 display and a T3 audio board painfully converted some year ago to work on T4.

The example program 'DemoSauce' for the T4.0 has had only the .ino file modified as below to match my setup and merge WavFilePlayer so it will play the animations with music except, skipping the first occurrence of twistyText to avoid an immediate crash. The interesting spot is at //mdr309 where if you un-comment the line, sound playing is turned off for twisty and the animations will run for many minutes to hours depending on who knows what. It will eventually crash in the '_checkerboard' animation however. Longer run times with no arduino serial monitor and longer still if wall wort.

The changes from the original are commented with //mdr You should start with the T4, example DemoSauce. Then substitute the below for the DemoSauce.ino

Removed the microphone stuff and random animation selection to simplify and help debug.
Added the XPT2046_Touchscreen stuff because if I don't have its .begin() I get noise in the audio. (I don't want to unhook the touchscreen)

Code:
/***************************************************
                 **  DemoSauce!  **
  State-of-the-art graphics for your beautiful TFT display.

  Greetz to the Portland Dorkbot Crew!!!!

         Programmed by Zach Archer (@zkarcher)  ::  http://controlzinc.com/
  Using hardware by Thomas Hudson (@hydronics)  ::  http://thomashudson.org/

  Usage:
    * Connect a microphone to MIC_PIN.
    * Connect TFT backlight to BACKLIGHT_PIN.

  MIT license, all text above must be included in any redistribution
 ****************************************************/

// https://github.com/zkarcher/demosauce

#include "SPI.h"
#include "ILI9341_t3.h"
#include "font_Arial.h"
#include <XPT2046_Touchscreen.h>    //mdr
#include <Audio.h>    //mdr

#include "FrameParams.h"

// Animations
#include "Checkerboard.h"
#include "Cube3D.h"
#include "Leaves.h"
#include "MagentaSquares.h"
//#include "MicCheck.h"
#include "PlasmaCloud.h"
#include "PlasmaYellow.h"
#include "Sphere3D.h"
#include "TriangleWeb.h"
#include "TwistyText.h"
#include "Waveform.h"

// Transitions
#include "TransitionDither.h"
#include "TransitionHalftone.h"
#include "TransitionScroll.h"
#include "TransitionSquares.h"

const boolean DO_BENCHMARKS = true;
const uint32_t SERIAL_BAUD_RATE = 9600;

const boolean DEBUG_ANIM = false; // dev: for hacking on one animation.
const uint_fast8_t DEBUG_ANIM_INDEX = 0;

const boolean DEBUG_TRANSITION = false;  // dev: set to true for short animation durations
const int_fast8_t DEBUG_TRANSITION_INDEX = -1;  // Supports -1: chooses a transition at random

const int_fast16_t DEFAULT_ANIM_TIME = 7.0f * 1000.0f;  // ms mdr changed to 7 seconds from 20

// TFT pins
const uint8_t TFT_DC = 5;    //mdr yours might be different
const uint8_t TFT_CS = 9;    //mdr yours might be different
const uint8_t MIC_PIN = 14;
const uint8_t BACKLIGHT_PIN = 23;

// Use these with the Teensy Audio Shield and Teesny 4.0  mdr20190823
#define SDCARD_CS_PIN    10    //mdr yours might be different

// Pin assignment for T4 XPT2046_Touchscreen
#define T_CS_PIN  4    //mdr yours might be different
#define TIRQ_PIN  3    //mdr yours might be different

// Use hardware SPI (#13, #12, #11) and the above for CS/DC
ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);
XPT2046_Touchscreen ts(T_CS_PIN, TIRQ_PIN);  // mdr get buzzing if connected and not inialized
AudioPlaySdWav           playWav1;    //mdr
// Use one of these 3 output types: Digital I2S, Digital S/PDIF, or Analog DAC
AudioOutputI2S           audioOutput;    //mdr
AudioConnection          patchCord1(playWav1, 0, audioOutput, 0);    //mdr
AudioConnection          patchCord2(playWav1, 1, audioOutput, 1);    //mdr
AudioControlSGTL5000     sgtl5000_1;    //mdr

FrameParams frameParams;
long previousMillis = 0;

Checkerboard * _checkerboard       = new Checkerboard();
Cube3D * _cube3D                   = new Cube3D();
Leaves * _leaves                   = new Leaves();
MagentaSquares * _magentaSquares   = new MagentaSquares();
PlasmaCloud * _plasmaCloud         = new PlasmaCloud();
PlasmaYellow * _plasmaYellow       = new PlasmaYellow();
Sphere3D * _sphere3D               = new Sphere3D();
TriangleWeb * _triangleWeb         = new TriangleWeb();
TwistyText * _twistyText           = new TwistyText();
Waveform * _waveform               = new Waveform();

TransitionDither * _transDither  = new TransitionDither();
TransitionHalftone * _transHalftone  = new TransitionHalftone();
TransitionScroll * _transScroll  = new TransitionScroll();
TransitionSquares * _transSquares  = new TransitionSquares();

BaseAnimation **anims; // Array of pointers to BaseAnimation's. Initialized in setup() below.
int_fast8_t animCount;

BaseAnimation *activeAnim = 0;
int_fast16_t animTimeLeft = 0;
BaseAnimation *nextAnim;

BaseTransition **transitions;
int_fast8_t transCount;
boolean isTransition = true;
BaseTransition *activeTransition = 0;

// Benchmarks
uint32_t frameCount;

// Search the anims[] aray for the activeAnim pointer. If found, return the array index.
int_fast8_t getActiveAnimIndex() {
  for( int_fast8_t i=0; i<animCount; i++ ) {
    if( anims[i] == activeAnim ) return i;
  }
  return -1;  // not found
}

void setup() {
  // Backlight
  //pinMode( BACKLIGHT_PIN, OUTPUT );    //mdr not using
  //analogWrite( BACKLIGHT_PIN, 1023 );

  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(5);    //mdr  max seems to be 4

  // Comment these out if not using the audio adaptor board.
  // This may wait forever if the SDA & SCL pins lack
  // pullup resistors
  sgtl5000_1.enable();    //mdr
  sgtl5000_1.volume(0.25);    //mdr

  // Microphone
  pinMode( MIC_PIN, INPUT );    //mdr not using

  tft.begin();
  tft.setRotation( 1 );
  tft.fillScreen(ILI9341_BLACK);

  ts.begin();  // mdr get buzzing if connected and not inialized
  ts.setRotation(2);    //mdr
  
  Serial.begin( SERIAL_BAUD_RATE );
  while (!Serial && millis() < 2000)
    ;

  // Serial
  if( DO_BENCHMARKS ) {
    tft.setTextColor(ILI9341_YELLOW);
    tft.setFont(Arial_18);
    tft.setCursor(10, 30);
    tft.print("myT4_demoSauce_Audio");    //mdr changed displayed text on no serial
    tft.setCursor(80, 90);
    tft.print("waiting for");
    tft.setCursor(60, 120);
    tft.print("Serial Monitor");
    tft.setTextColor(ILI9341_GREEN);
    tft.setFont(Arial_18);
    while (!Serial && millis() < 8000) { // wait for Arduino Serial Monitor
      tft.fillRect(118, 182, 42, 18, ILI9341_BLACK);
      tft.setCursor(118, 182);
      tft.print((8000.0 - (float)millis()) / 1000.0, 1);
      tft.print(" sec");
      delay(100);
    }
  }
  previousMillis = millis();

  // Clear
  uint16_t w = tft.width();
  uint16_t h = tft.height();
  tft.fillRect( 0, 0, w, h, 0x0 );

  tft.setScroll( 0 );

  // Populate anims in the order you want them to display.
  BaseAnimation* ANIMS_TEMP[] = {
    _twistyText,
    _plasmaCloud,
    _waveform,
    _magentaSquares,
    _sphere3D,
    _checkerboard,
    _leaves,
    _cube3D,
    _plasmaYellow,
    _triangleWeb
  };
  animCount = sizeof( ANIMS_TEMP ) / sizeof( BaseAnimation* );

  // Retain ANIMS_TEMP objects permanently
  anims = (BaseAnimation**)malloc( animCount * sizeof(BaseAnimation*) );
  for( int_fast8_t i=0; i<animCount; i++ ) {
    anims[i] = ANIMS_TEMP[i];
    anims[i]->init( tft );      // Initalize all animations
  }

  BaseTransition* TRANS_TEMP[] = {
    _transDither,
    _transHalftone,
    _transScroll,
    _transSquares
  };
  transCount = sizeof( TRANS_TEMP ) / sizeof( BaseTransition* );

  // Retain TRANS_TEMP objects permanently
  transitions = (BaseTransition**)malloc( transCount * sizeof(BaseTransition*) );
  for( int_fast8_t i=0; i<transCount; i++ ) {
    transitions[i] = TRANS_TEMP[i];
    transitions[i]->init( tft );
  }

  // Start!
  if( !activeAnim ) {
    if( DEBUG_ANIM ) {
      startAnimation( anims[DEBUG_ANIM_INDEX] );
    } else {
      startAnimation( anims[0] );
    }
  }

  if (!(SD.begin(SDCARD_CS_PIN))) {    //mdr
    // stop here, but print a message repetitively
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
}

void startAnimation( BaseAnimation *newAnim ) {
  isTransition = false;

  activeAnim = newAnim;
  tft.fillScreen( activeAnim->bgColor() );
  tft.setScroll( 0 );
  activeAnim->reset( tft );

  animTimeLeft = DEFAULT_ANIM_TIME;

  if( DEBUG_TRANSITION ) animTimeLeft = 2000;

  if( DO_BENCHMARKS ) {
    Serial.println("---");
    Serial.print( activeAnim->title() );

    if( activeAnim->willForceTransition() ) {
      // TwistyText does not obey DEFAULT_ANIM_TIME
      Serial.println("");

    } else {
      Serial.print("  [");
      Serial.print( (uint8_t)(animTimeLeft / 1000.0f) );
      Serial.print(" secs]  AudioMemoryUsageMax ");    //mdr added
      Serial.println(AudioMemoryUsageMax());
    }

    frameCount = 0;
  }
}

void loop() {
  // Frame multiplier
  long newMillis = millis();
  uint_fast8_t elapsed = newMillis - previousMillis;
  previousMillis = newMillis;
  frameParams.timeMult = elapsed * (60.0f / 1000);  // 1.0==exactly 60fps. 4.0==15fps, 4x slower

  // Get some audio input
  const uint_fast8_t SAMPLES_PER_FRAME = 1;
  frameParams.audioPeak = 0;
  uint_fast16_t sum = 0;

  for( uint_fast8_t s=0; s<SAMPLES_PER_FRAME; s++ ) {
    uint_fast16_t sample = abs( random(0,511) );    //mdr nice effects with no mic
    frameParams.audioPeak = max( frameParams.audioPeak, sample );
    sum += sample;
  }
  frameParams.audioMean = sum * (1.0 / (512*SAMPLES_PER_FRAME));  // Range: 0..1

  frameParams.audioPeak = min( (uint_fast16_t)frameParams.audioPeak, (uint_fast16_t)511 );

  if( !isTransition ) {
    activeAnim->perFrame( tft, frameParams );
    animTimeLeft -= elapsed;

    if( DO_BENCHMARKS ) frameCount++;
  }

  // Has this animation expired?
  boolean willForceTransition = activeAnim->willForceTransition();
  boolean forceTransitionNow = activeAnim->forceTransitionNow();

  // Debugging transitions: Ignore animations hogging the screen
  if( DEBUG_TRANSITION ) willForceTransition = false;

  if( !DEBUG_ANIM ) {
    if( (!willForceTransition && (animTimeLeft <= 0)) || forceTransitionNow ) {

      // If the transition has not started yet, then start it.
      if( !isTransition ) {
        isTransition = true;

        nextAnim = anims[ (getActiveAnimIndex() + 1) % animCount ];
        if(_twistyText==nextAnim){    //mdr309 don't play if twistyText or crash
            //playWav1.stop();    //mdr un-comment this to prevent crash on twistyText
        }else if(!playWav1.isPlaying()){
            playWav1.play("SDTEST1.WAV"); //start playing
        }

        // Choose a random transition
        activeTransition = transitions[ random(transCount) ];
        if( DEBUG_TRANSITION && (DEBUG_TRANSITION_INDEX >= 0) ) {
          activeTransition = transitions[ DEBUG_TRANSITION_INDEX ];
        }

        activeTransition->restart( tft, nextAnim->bgColor() );

        // Benchmark: show how many frames the animation completed while alive.
        if( DO_BENCHMARKS ) {
          Serial.print("Frame count (more is better):  ");
          Serial.print( frameCount );

          if( !activeAnim->willForceTransition() ) {
            Serial.print( "  (" );
            Serial.print( (float)frameCount / (DEFAULT_ANIM_TIME / 1000.0f) );
            Serial.println(" FPS)");
          } else {
            Serial.println("");
          }
        }
      }

      // After the transition ends, advance to the next animation
      activeTransition->perFrame( tft, frameParams );
      if( activeTransition->isComplete() ) {
        startAnimation( nextAnim );
      }

    }
  }

}

I'm hoping someone can try this on an T4 version(Rev D) audio board to confirm the issue.
 
Last edited:
Forgot to mention, you don't need to be attached to a display to see the problem, although it's not very interesting. You can hear the music in headphones. It will crash when it gets to twistyText as shown in the terminal. If you un-comment the line after //mdr309 when it gets there the sound will stop and then start again in ~20sec when the next animation starts.
 
Back
Top