Audio Whine when Recording with Teensy Audioboard MIC/GND Pins

QLeitz

New member
Hello all,

I'm new to the Teensy ecosystem and like many others I am trying to get the audio guestbook project off the ground. I've got demos of many of the other basic building blocks of the project working, and am now working on a recording demo using the MIC/GND pins on my audio board. However, when I listen back to the recording I hear a high-pitched whine overlaid on the entire thing. I've attempted a few things to get rid of this behavior, including:
  • Powering the Teensy using my 5V battery pack
  • Moving the Teensy away from my computer/other sources of electrical noise
  • Purchasing a USB-A to Micro USB cable with a ferrite bead to capture interference
  • Moving the microphone wires as far away as possible from the Teensy/power cord
  • Purchasing and re-soldering a completely new Teensy 4.0 & audioboard, as I thought my soldering job the first time might have been the issue.

Components
  • Teensy 4.0 - TEENSY40_PINS - Link
  • Teensy 4.0 Audio Board - TEENSY4_AUDIO - Link
  • SD Card - SanDisk 256GB Extreme microSDXC - Link
  • USB 5V Battery - Anker PowerCore 5000 Portable Charger - Link
  • USB Cable - 6-Feet USB 2.0 A Male to Micro 5pin Male 28/24AWG Cable with Ferrite Core - Link
  • Microphone - Cylewet 10Pcs Cylindrical Electret Condenser Microphone - Link
  • Dupont Wires - ELEGOO 120pcs Multicolored Dupont Wire - Link

Software
For the IDE, I am using Arduino 1.8.19. The board is configured as Teensy 4.0. To process audio files, I am using Audacity v3.2.5. The files are saved with the name .WAV, but are not WAV files. They can be imported into Audacity using File > Import > Raw Data and settings as shown in the following image:

Audacity Import Settings.png

Hardware Setup
I have the Teensy 4.0 board soldered to the Teensy audio board. To the Audio board MIC pin, I have a white wire which goes to the positive side of my microphone. From the negative side of my microphone, I have a black wire which goes back to the Teensy audio board GND pin. Connected to the Audio board SD card slot is my Sandisk SD card, and I am receiving power to the Teensy micro USB port using my ferrite bead cable to my 5V battery pack.

Test.png

Teensy Side View.png

Teensy Bottom View.png

Microphone Pin Soldering.png

Microphone Front.png

Microphone Back.png

Full Setup.png

Result
I've uploaded a test recording to the following Google Drive Link. Note that it will not play on Google; it needs to be downloaded and imported into Audacity using the procedure above in Software.

If there is a better way to upload content like this, please let me know and I will revise.

Code
This is a slimmed down version of the audio guestbook code, which includes only one button. On powerup, the device begins recording. To get it to save the recording, connect the GND pin to pin 0. I have been using a screwdriver to do this for my test setup. After a second remove the screwdriver, remove power to the device, remove the SD card, and inspect the audio file.

Code:
// Record sound as raw data to an SD card.
//
// Requires the audio shield:
//   http://www.pjrc.com/store/teensy3_audio.html
//
// One pushbutton need to be connected:
//   Record Button: pin 0 to GND

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

// GUItool: begin automatically generated code
AudioInputI2S            i2s2;           //xy=105,63
AudioAnalyzePeak         peak1;          //xy=278,108
AudioRecordQueue         queue1;         //xy=281,63
AudioPlaySdRaw           playRaw1;       //xy=302,157
AudioOutputI2S           i2s1;           //xy=470,120
AudioConnection          patchCord1(i2s2, 0, queue1, 0);
AudioConnection          patchCord2(i2s2, 0, peak1, 0);
AudioConnection          patchCord3(playRaw1, 0, i2s1, 0);
AudioConnection          patchCord4(playRaw1, 0, i2s1, 1);
AudioControlSGTL5000     sgtl5000_1;     //xy=265,212

// Filename to save audio recording on SD card
char filename[15];

// Bounce objects to easily and reliably read the buttons
#define HOOK_PIN 0
Bounce buttonRecord = Bounce(HOOK_PIN, 100);

// Specify input on the audio shield
const int myInput = AUDIO_INPUT_MIC;

// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording

// The file where data is recorded
File frec;

void setup() {
  // Configure the pushbutton pins
  pinMode(0, INPUT_PULLUP);

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(60);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(myInput);
  sgtl5000_1.volume(0.5);

  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
}


void loop() {
  // First, read the buttons
  buttonRecord.update();

  // Respond to button presses
  if (buttonRecord.risingEdge()) {
    Serial.println("Command: Start Record");
    if (mode == 0) startRecording();
  }

  if (buttonRecord.fallingEdge()) {
    Serial.println("Command: Stop Record");
    if (mode == 1) stopRecording();
  }

  // If we're playing or recording, carry on...
  if (mode == 1) {
    continueRecording();
  }
}


void startRecording() {
  Serial.println("Starting recording...");
  for (uint16_t i=0; i<9999; i++) {   
    // Format the counter as a five-digit number with leading zeroes, followed by file extension
    snprintf(filename, 11, " %05d.wav", i);
    // Create if does not exist, do not open existing, write, sync after write
    if (!SD.exists(filename)) {
      break;
    }
  }
  
  if (SD.exists(filename)) {
    // The SD library writes new data to the end of the
    // file, so to start a new recording, the old file
    // must be deleted before new data is written.
    SD.remove(filename);
  }
  frec = SD.open(filename, FILE_WRITE);
  if (frec) {
    queue1.begin();
    mode = 1;
  }
}

void continueRecording() {
  //Serial.println("Continuing recording...");
  if (queue1.available() >= 2) {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer+256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // write all 512 bytes to the SD card
    //elapsedMicros usec = 0;
    frec.write(buffer, 512);
    // Uncomment these lines to see how long SD writes
    // are taking.  A pair of audio blocks arrives every
    // 5802 microseconds, so hopefully most of the writes
    // take well under 5802 us.  Some will take more, as
    // the SD library also must write to the FAT tables
    // and the SD card controller manages media erase and
    // wear leveling.  The queue1 object can buffer
    // approximately 301700 us of audio, to allow time
    // for occasional high SD card latency, as long as
    // the average write time is under 5802 us.
    //Serial.print("SD write, us=");
    //Serial.println(usec);
  }
}

void stopRecording() {
  Serial.println("Stopping recording...");
  queue1.end();
  if (mode == 1) {
    while (queue1.available() > 0) {
      frec.write((byte*)queue1.readBuffer(), 256);
      queue1.freeBuffer();
    }
    frec.close();
  }
  mode = 0;
}

Thank you in advance for any support! If anything needs clarification, please let me know!

Quentin
 

MarkT,

Thanks for the response. I've selected a sample of the background noise in my recording in Audacity, and run the Frequency Analysis tool. I'm by no means an audio expert, so if there's a better way to do this I'm all ears. But it does appear that there is a peak between 16kHz and 20kHz. The peak appears to be right at 18kHz, which is pretty darn close to the 17.5kHz you mentioned. Any idea what could be causing the whine?

See the below photo:

Frequency Analysis Whitenoise Only.jpg

Thanks,

Quentin
 
Use more FFT points, and set linear frequency scale, then you might be able to see better. The frequency of the spur may be dependent on system clock frequency and differ from Teensy to Teensy perhaps. Alas Audacity has a very poor choice of FFT windows, normally you'd use a flattop window for diagnosing spurs and peaks, but try Blackman-Harris.
 
Use more FFT points, and set linear frequency scale, then you might be able to see better. The frequency of the spur may be dependent on system clock frequency and differ from Teensy to Teensy perhaps. Alas Audacity has a very poor choice of FFT windows, normally you'd use a flattop window for diagnosing spurs and peaks, but try Blackman-Harris.

MarkT,

I've re-run the analysis using the Blackman-Harris window, size 2048, and Linear Frequency axis. If you'd like to take a look at the audio and do any analysis with your preferred tools, I've uploaded the file to the following Google Drive Link.

See the below photo for my analysis:

Frequency Analysis Whitenoise Only - Blackman-Harris Linear.jpg

Thanks,

Quentin
 
This looks like it might be the same thing I found recently - SD card writes draw a large current, and poor supply routing (and / or decoupling) results in power rail sagging which couples into the mic signal. See https://forum.pjrc.com/threads/70553-Teensy-4-0-based-Audio-Guestbook?p=325149&viewfull=1#post325149 and the following posts. In post #311 there's an image of where I added thicker wires to "star" the power directly to the SD card. I used 0.5mm (0.020", 25swg) tinned wire, it'd be better to use insulated. Note the wire connecting the two pin grounds and the mic ground, too: not sure it's necessary, but probably doesn't hurt. That was the first thing I tried, by itself it made a small improvement and was easier to do than the power wires which require identifying the SD card power traces on the PCB and carefully scraping the solder resist off.

If you look closely at the file you uploaded, you can easily see the SD card write spikes every 5.8ms, which is a fundamental of ~172Hz. Not quite what I'd call high pitched, but there you go...

Do let us know how you get on.

EDIT: here's my FFT from Audacity - I just looked at the middle section of audio, where you weren't talking.
2023-05-03 08_39_06-TestRecording.png
 
MarkT,

I've re-run the analysis using the Blackman-Harris window, size 2048, and Linear Frequency axis. If you'd like to take a look at the audio and do any analysis with your preferred tools, I've uploaded the file to the following Google Drive Link.

See the below photo for my analysis:

View attachment 31047

Thanks,

Quentin

The problem is a spike of rogue values every 256 samples:
Screen Shot 2023-05-03 at 19.12.51.jpg
- that's just the first bit of the file. I suspect somethings overwritting the buffer[256] somehow?
[ Actually no, thinking about it its clearly that you've passed an evanescent pointer (i.e. buffer) to frec.write, but you have to pass a pointer to statically allocated memory to frec.write() as its non-blocking and interrupt driven. By the time the interrupt gets to use the buffer that stack frame is long gone and overwritten at the start, hence the spike. ]
 
The problem is a spike of rogue values every 256 samples:
View attachment 31053
- that's just the first bit of the file. I suspect somethings overwritting the buffer[256] somehow?
[ Actually no, thinking about it its clearly that you've passed an evanescent pointer (i.e. buffer) to frec.write, but you have to pass a pointer to statically allocated memory to frec.write() as its non-blocking and interrupt driven. By the time the interrupt gets to use the buffer that stack frame is long gone and overwritten at the start, hence the spike. ]
Just tested that hypothesis, and no dice even with the buffer statically allocated. (As a matter of fact I don't think the SD filesystem is interrupt-driven, but blocks the sketch during reads, writes etc.)

Corroborative evidence to support a hardware issue, as suggested in my post #6 above:
  • with multi-sector writes, the fine detail of the rogue spikes shows a "spikelet" for each sector written
  • close observation of the leading and trailing edges always shows an intermediate-level sample; you'd expect a totally clean edge if it were buffer corruption
  • buffer corruption would have much more random values than those observed, which are just offset from the main waveform, and always in the same direction
 
Well it was a good theory (easily testable!) Sigma-delta DACs have complex reconstruction filters, don't expect the output voltage to be exactly like the digital samples (those leading and trailing edges). For that you need a different kind of DAC and no analog reconstruction filter after it.
 
Back
Top