Teensy 3.5 + Audio.h + ICS43432 for Audio Cue?

Status
Not open for further replies.
Hello,

I'll start by saying that I am not an audio engineer. I know my way around a mixing board but talk of codecs, effects, and i2s in general confuses me greatly.

That said, I am building a costume that has Teensy 3.5-controlled lights that I wish to sync to music. I've already worked out timing more or less (https://www.youtube.com/watch?v=cxKQS1fh3w8), but I want to hide ultrasonic trigger sounds (20K tone) in the beginning of the song to sync my animations up. Looking over the Arduino Whistle example, it looks like the way to do this is with an FFT process. However I'm having a hard time wrapping my head around the code to achieve this.

I don't have the audio shield, I prefer to wire my microphone (ICS43432 MEMS I2S microphone on a breakout board) directly to the Teensy (space is a concern on this project). Looking over the FFT1024 example, am I correct in assuming I can simply comment out the audio shield control code and more or less use it as is? Is there a better way to get a tone trigger? Normally I would be willing to simply play the audio myself and sync the lights to the start of playback but I intend to do this on stage where I won't have direct access to the sound system and will need to hand over an audio file for playback.

Code:
// FFT Test
//
// Compute a 1024 point Fast Fourier Transform (spectrum analysis)
// on audio connected to the Left Line-In pin.  By changing code,
// a synthetic sine wave can be input instead.
//
// The first 40 (of 512) frequency analysis bins are printed to
// the Arduino Serial Monitor.  Viewing the raw data can help you
// understand how the FFT works and what results to expect when
// using the data to control LEDs, motors, or other fun things!
//
// This example code is in the public domain.

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;

// Create the Audio components.  These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioInputI2S          audioInput;         // audio shield: mic or line-in
AudioSynthWaveformSine sinewave;
AudioAnalyzeFFT1024    myFFT;
AudioOutputI2S         audioOutput;        // audio shield: headphones & line-out

// Connect either the live input or synthesized sine wave
AudioConnection patchCord1(audioInput, 0, myFFT, 0);
//AudioConnection patchCord1(sinewave, 0, myFFT, 0);

AudioControlSGTL5000 audioShield;

void setup() {
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(12);

  // Enable the audio shield and set the output volume.
  // ------------------------------------------------------------------
  // Disabled because I'm not using the audio shield - Snow
  // audioShield.enable();
  // audioShield.inputSelect(myInput);
  // audioShield.volume(0.5);

  // Configure the window algorithm to use
  myFFT.windowFunction(AudioWindowHanning1024);
  //myFFT.windowFunction(NULL);

  // Create a synthetic sine wave, for testing
  // To use this, edit the connections above
  sinewave.amplitude(0.8);
  sinewave.frequency(1034.007);
}

void loop() {
  float n;
  int i;

  if (myFFT.available()) {
    // each time new FFT data is available
    // print it all to the Arduino Serial Monitor
    Serial.print("FFT: ");
    for (i=0; i<40; i++) {
      n = myFFT.read(i);
      if (n >= 0.01) {
        Serial.print(n);
        Serial.print(" ");
      } else {
        Serial.print("  -  "); // don't print "0.00"
      }
    }
    Serial.println();
  }
}
 
I don't have the audio shield, I prefer to wire my microphone (ICS43432 MEMS I2S microphone on a breakout board) directly to the Teensy (space is a concern on this project).

Make sure you're using the latest Teensyduino, version 1.39. Click Arduino's Help > About to check. The audio lib I2S was recently updated to improve compatibility with those I2S mics.

Looking over the FFT1024 example, am I correct in assuming I can simply comment out the audio shield control code and more or less use it as is?

I have not personally tested that mic, but from what I know of it, odds are very good that will "just work".

Before you dive into tone detection, I highly recommend adding a dac object to you design and simply route the I2S data to the DAC. Then use an amplified computer speaker to listen. Or maybe use a speaker with a headphone jack, so you an listen with headphones which won't tend to feed the sound right back into the mic.

The idea is to make sure the mic is actually working with the simplest possible test, before you add the extra complexity of analyzing the sound.

Is there a better way to get a tone trigger?

The audio library has a tone detector object.

https://www.pjrc.com/teensy/gui/?info=AudioAnalyzeToneDetect

This will probably work out to be about the same as using the FFT and only looking at a few specific bins where you expect to find your special tone.
 
Thanks for the quick reply!

I was planning to just look at the serial output at first, since the tones I'm using I can't hear (I actually can't hear anything above 14K, but everyone else at work sure can). I'm familiar with editing audio and looking at waveforms and spectrums so I should be able to get some idea if it's functioning just from the raw numbers.

Okay that's good to know, I'll take a closer look at it and see what I come up with.
 
Maybe you missed the point of a listening test. It's not to check the high frequency response, but rather to confirm the mic is working at all (eg, you got all the wires connected correctly). It's also a good way to estimate if the signal levels are ok.

Trust me, for your own sanity's sake, don't skip this important step.
 
Hmm, I'd prefer to rely on the serial info because I don't have any other audio hardware to hook up to it (and I've heard even with working setups people have trouble getting audacity to read serial data). I'll see what I can do.
 
After a few tests, I'm not getting any output from the microphone. Let me start with my wiring.
Mic GND and 3V3 to ground and 3.3v obviously.
Mic L/R to ground (shouldn't matter honestly, using it as mono audio)
Mic WS to Teensy pin 23
Mic CLK to Teensy pin 9
Mic SD to Teensy pin 13

With my oscilloscope I see the clock signals and word select signals from the Teensy clearly enough but the data output of the mic is not only noisy, I see this:
https://photos.app.goo.gl/T8LbrwV3cDV94lEy1

What the heck is causing that? It looks like capacitor discharge. Am I missing a pulldown/pullup resistor in my connections? The schematic shows the chip should have a pulldown resistor and no capacitor on the SD pin.

***EDIT***

Turns out I had a loose connection on the SD pin. When I fixed the connection I get data through. Nevermind ^.^
 
One final update here, everything is working perfectly. I'll begin my timing tests another day but I'm successfully able to detect a 20kHz tone.
Here's my hastily modified DTMF-to-20kHz example.

Code:
// Dial Tone (DTMF) decoding example.
//
// The audio with dial tones is connected to audio shield
// Left Line-In pin.  Dial tone output is produced on the
// Line-Out and headphones.
//
// Use the Arduino Serial Monitor to watch for incoming
// dial tones, and to send digits to be played as dial tones.
//
// This example code is in the public domain.


#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// Create the Audio components.  These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioInputI2S            audioIn;
AudioAnalyzeToneDetect   row1;     // 7 tone detectors are needed
/*AudioAnalyzeToneDetect   row2;     // to receive DTMF dial tones
AudioAnalyzeToneDetect   row3;
AudioAnalyzeToneDetect   row4;
AudioAnalyzeToneDetect   column1;
AudioAnalyzeToneDetect   column2;
AudioAnalyzeToneDetect   column3;
AudioSynthWaveformSine   sine1;    // 2 sine wave
AudioSynthWaveformSine   sine2;    // to create DTMF
AudioMixer4              mixer;
AudioOutputI2S           audioOut;*/

// Create Audio connections between the components
//
AudioConnection patchCord01(audioIn, 0, row1, 0);
/*AudioConnection patchCord02(audioIn, 0, row2, 0);
AudioConnection patchCord03(audioIn, 0, row3, 0);
AudioConnection patchCord04(audioIn, 0, row4, 0);
AudioConnection patchCord05(audioIn, 0, column1, 0);
AudioConnection patchCord06(audioIn, 0, column2, 0);
AudioConnection patchCord07(audioIn, 0, column3, 0);
AudioConnection patchCord10(sine1, 0, mixer, 0);
AudioConnection patchCord11(sine2, 0, mixer, 1);
AudioConnection patchCord12(mixer, 0, audioOut, 0);
AudioConnection patchCord13(mixer, 0, audioOut, 1);*/

// Create an object to control the audio shield.
// 
//AudioControlSGTL5000 audioShield;


void setup() {
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(12);

  // Enable the audio shield and set the output volume.
  //audioShield.enable();
  //audioShield.volume(0.5);
  
  while (!Serial) ;
  delay(100);
  
  // Configure the tone detectors with the frequency and number
  // of cycles to match.  These numbers were picked for match
  // times of approx 30 ms.  Longer times are more precise.
  row1.frequency(20000, 50);
  //row2.frequency(770, 23);
  //row3.frequency(852, 25);
  //row4.frequency(941, 28);
  //column1.frequency(1209, 36);
  //column2.frequency(1336, 40);
  //column3.frequency(1477, 44);
}

const float row_threshold = 0.2;
//const float column_threshold = 0.2;

void loop() {
  float r1, r2, r3, r4, c1, c2, c3;
  char digit=0;

  // read all seven tone detectors
  r1 = row1.read();
  /*r2 = row2.read();
  r3 = row3.read();
  r4 = row4.read();
  c1 = column1.read();
  c2 = column2.read();
  c3 = column3.read();*/

  // print the raw data, for troubleshooting
  //Serial.println(r1);
  r1 *= 100;
  for (int i = 0; i < r1; i++) {
    Serial.print('x');
  }
  Serial.println();

  // check all 12 combinations for key press
  /*if (r1 >= row_threshold) {
    if (c1 > column_threshold) {
      digit = '1';
    } else if (c2 > column_threshold) {
      digit = '2';
    } else if (c3 > column_threshold) {
      digit = '3';
    }
  } else if (r2 >= row_threshold) { 
    if (c1 > column_threshold) {
      digit = '4';
    } else if (c2 > column_threshold) {
      digit = '5';
    } else if (c3 > column_threshold) {
      digit = '6';
    }
  } else if (r3 >= row_threshold) { 
    if (c1 > column_threshold) {
      digit = '7';
    } else if (c2 > column_threshold) {
      digit = '8';
    } else if (c3 > column_threshold) {
      digit = '9';
    }
  } else if (r4 >= row_threshold) { 
    if (c1 > column_threshold) {
      digit = '*';
    } else if (c2 > column_threshold) {
      digit = '0';
    } else if (c3 > column_threshold) {
      digit = '#';
    }
  }*/

  // print the key, if any found
  /*if (digit > 0) {
    Serial.print("  --> Key: ");
    Serial.print(digit);
  }
  Serial.println();*/

  // uncomment these lines to see how much CPU time
  // the tone detectors and audio library are using
  //Serial.print("CPU=");
  //Serial.print(AudioProcessorUsage());
  //Serial.print("%, max=");
  //Serial.print(AudioProcessorUsageMax());
  //Serial.print("%   ");

  // check if any data has arrived from the serial monitor
  /*if (Serial.available()) {
    char key = Serial.read();
    int low=0;
    int high=0;
    if (key == '1') {
      low = 697;
      high = 1209;
    } else if (key == '2') {
      low = 697;
      high = 1336;
    } else if (key == '3') {
      low = 697;
      high = 1477;
    } else if (key == '4') {
      low = 770;
      high = 1209;
    } else if (key == '5') {
      low = 770;
      high = 1336;
    } else if (key == '6') {
      low = 770;
      high = 1477;
    } else if (key == '7') {
      low = 852;
      high = 1209;
    } else if (key == '8') {
      low = 852;
      high = 1336;
    } else if (key == '9') {
      low = 852;
      high = 1477;
    } else if (key == '*') {
      low = 941;
      high = 1209;
    } else if (key == '0') {
      low = 941;
      high = 1336;
    } else if (key == '#') {
      low = 941;
      high = 1477;
    }

    // play the DTMF tones, if characters send from the Arduino Serial Monitor
    if (low > 0 && high > 0) {
      Serial.print("Output sound for key ");
      Serial.print(key);
      Serial.print(", low freq=");
      Serial.print(low);
      Serial.print(", high freq=");
      Serial.print(high);
      Serial.println();
      AudioNoInterrupts();  // disable audio library momentarily
      sine1.frequency(low);
      sine1.amplitude(0.4);
      sine2.frequency(high);
      sine2.amplitude(0.45);
      AudioInterrupts();    // enable, both tones will start together
      delay(100);           // let the sound play for 0.1 second
      AudioNoInterrupts();
      sine1.amplitude(0);
      sine2.amplitude(0);
      AudioInterrupts();
      delay(50);            // make sure we have 0.05 second silence after
    }
  }*/

  delay(25);
}
 
Status
Not open for further replies.
Back
Top