Issue Subtracting Left Line-In from Right Line-in on Audio Shield to Noise Cancel

Status
Not open for further replies.

enphoti

Member
Hey All,

I am been searching the forums and did not see this issue discussed relating to my project. Maybe I missed it, but your help would be appreciated.

My project is to remove ambient noise from a mic input, so that the output is *nearly* voice only. I have two mics, one that is for my voice and one is for background noise. I am using a custom preamp board to bring the sound into Line-in Left and Right on the Audio Shield from the mics. I have a Teensy 4.0 performing the math to subtract the ambient noise mic (Line-in Left) from the voice mic (Line-in Right) and my output is through the headphone jack on the Audio Shield. See block diagram below for set up. Also picture below for actual board setup. Code is also below.

My issue:
I cannot seem to get Line-in Left (background noise) to subtract from Line-in Right (voice + background noise) to produce my voice only output.

What I have verified:
  1. I verified receiving sound through both mics independently to my headphones using Audio Shield and Teensy.
  2. I verified reducing output to zero volume (verified by rms1) via subtraction if: (1) I use just one channel (e.g. ch0 of i2s1) as the source to both inputs to Mixer 1 (0 and 1).

#2 tells me that what I am trying to do is possible. So that is encouraging.

What I am having trouble with:
  1. See above - Pass
  2. See above - Pass
  3. Using the same sound through both mics to reduce output to zero volume via subtraction - Fail
  4. Using one mic for voice + background sound and the other for background sound to output voice only via subtraction
  5. Testing outside in real world

I would appreciate any direction or help in this manner to solve issue #3. I will do the homework to figure this out, just need to know where to look.

Thanks.

Block diagram
BackgroundNoiseRemovalSetup.jpg

Actual board setup
actualsetup2.jpg

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

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           
AudioAmplifier           amp1;           
AudioMixer4              mixer1;         
AudioOutputI2S           i2s2;
AudioAnalyzeRMS          rms1;
AudioAnalyzePeak         peak1;
AudioConnection          patchCord1(i2s1, 0, amp1, 0);
//AudioConnection          patchCord2(i2s1, 0, mixer1, 1);   //same Channel, test cancelation
AudioConnection          patchCord2(i2s1, 1, mixer1, 1); //Two channels
AudioConnection          patchCord3(amp1, 0, mixer1, 0);
AudioConnection          patchCord4(mixer1, 0, i2s2, 0);
AudioConnection          patchCord5(mixer1, 0, i2s2, 1);
AudioConnection          patchCord6(mixer1, rms1);
AudioConnection          patchCord7(mixer1, peak1);
AudioControlSGTL5000     sgtl5000_1; 
// GUItool: end automatically generated code

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

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

  //Amplify channel 0
  amp1.gain(-0.5);

  //Mixer setting
  mixer1.gain(0, 0.5);
  mixer1.gain(1, 0.5);
  
}

elapsedMillis volmsec=0;
String amp_text;
float amp_value;

float rms_amp;
float peak_amp;

void loop() {
  // Adjust amp of background noise coming in
  // Keyboard input for amp value
  // Run every 50 msecs
  if (volmsec > 50) 
  {
    if (Serial.available())
    {
        amp_text = Serial.readStringUntil('\n');
        Serial.print(amp_text + " ");
        amp_value = amp_text.toFloat();
        Serial.println(-amp_value);
        amp1.gain(-amp_value);
    }
    
    // Result - verify cancelation of sound
    rms_amp = rms1.read() * 30.0;
    Serial.print(rms_amp);
    Serial.print(" ");
    peak_amp = peak1.read() * 30.0;
    Serial.println(peak_amp);
    volmsec = 0;
  }
}
 
Last edited:
Hello I've no experience here, but the first thing that comes to my mind is - is there a time difference between left and right signals? This could be introduced any where in the signal path including processing on the Teensy. How about amp1? Could this introduce a delay or are the sample blocks invariant at this point, after i2s1, and dealt with together? Can you analyse the signals just before they go into the audio board? Is the background noise going into both mics the same? A 1m position difference is 3ms at the speed of sound. I think what you're doing may be quite hard to achieve physically rather than electronically. Noise-cancelling headphones have mics and transducers right next to each other, your mics presumably aren't.
 
Last edited:
amp1 reduces the amplitude of the inverted signal by 0.5 but the non-inverted signal isn't attenuated at all.
Try adding an amp in the non-inverted path and set it to 0.5. This should also ensure that there's no processing delay between the two paths.

Pete
 
Hello I've no experience here, but the first thing that comes to my mind is - is there a time difference between left and right signals?

UHF, this is what I am thinking also, I think the sampling of two channels happens sequentially, resulting in a large enough time delay where the same point is not being canceled. However, I should experience some cancellation, even with milliseconds of delay...I need to measure things more closely.

This could be introduced any where in the signal path including processing on the Teensy. How about amp1? Could this introduce a delay or are the sample blocks invariant at this point, after i2s1, and dealt with together?

I don't think that there is delay after i2s1 because of what I experienced in #2. Seems like once the signal passes through i2s1, the math is pretty quick, resulting in no practical time delay.

Can you analyse the signals just before they go into the audio board? Is the background noise going into both mics the same? A 1m position difference is 3ms at the speed of sound. I think what you're doing may be quite hard to achieve physically rather than electronically. Noise-cancelling headphones have mics and transducers right next to each other, your mics presumably aren't.

What you say is very interesting. There will be a delay if the mics are far apart. In my testing of #3 though, I actually have a speaker playing music with both mics inches apart, so I don't think the problem is mic distance at this point. I think I need to measure both channels separately and then measure my output to see if I can measure the time delay between the left and the right channels. Maybe it is as easy as adding a delay in the math.
 
Pete, I don't think amp is causing the delay, as #2 indicates. When the same signal goes through both paths, I can achieve cancellation. But I will give it a try to see if I am missing something.
 
Update #1

I did find two issues, but was only able to resolve Issue #1. Any thoughts on Issue #2?

Issue #1. There was not a delay in i2s1, but looking at RMS graph (shown below), my mics were not equal. I added a 1.6 amp to make the amplitudes equal before the mixer. (solved)
Issue #2. Mixer1 is not performing the math correctly. See graph below. I expected a cancellation to happen (mixer_out on graph), but rms5 (mixer output) equals rms2 (channel 1 input). very confusing. Any help here? (Technically RMS3 reads positive, but I assume that the inversion makes the signal negative, as shown in the graph. I could also be wrong here).

Graph
graph.png

Audio System set up
audio system design setup.png

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

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=232.74998474121094,244
AudioAnalyzeRMS          rms1;           //xy=414.1999969482422,111.19999694824219
AudioAmplifier           amp1;           //xy=416.5000305175781,199
AudioAmplifier           amp2;           //xy=422.20001220703125,314.20001220703125
AudioAnalyzeRMS          rms2;           //xy=425.20001220703125,404.20001220703125
AudioAnalyzePeak         peak1;          //xy=442.1999969482422,52.19999694824219
AudioAnalyzePeak         peak2;          //xy=455.1999969482422,468.1999969482422
AudioMixer4              mixer1;         //xy=591.5000610351562,300.25
AudioAnalyzeRMS          rms3;           //xy=645.1999969482422,118.19999694824219
AudioAnalyzeRMS          rms4;           //xy=660.1999969482422,417.1999969482422
AudioOutputI2S           i2s2;           //xy=801.4999961853027,356.49999618530273
AudioAnalyzePeak         peak3;          //xy=830.1999969482422,168.1999969482422
AudioAnalyzeRMS          rms5;           //xy=831.1999969482422,230.1999969482422
AudioConnection          patchCord1(i2s1, 0, amp1, 0);
AudioConnection          patchCord2(i2s1, 0, rms1, 0);
AudioConnection          patchCord3(i2s1, 0, peak1, 0);
AudioConnection          patchCord4(i2s1, 1, amp2, 0);
AudioConnection          patchCord5(i2s1, 1, rms2, 0);
AudioConnection          patchCord6(i2s1, 1, peak2, 0);
AudioConnection          patchCord7(amp1, 0, mixer1, 0);
AudioConnection          patchCord8(amp1, rms3);
AudioConnection          patchCord9(amp2, 0, mixer1, 1);
AudioConnection          patchCord10(amp2, rms4);
AudioConnection          patchCord11(mixer1, 0, i2s2, 0);
AudioConnection          patchCord12(mixer1, 0, i2s2, 1);
AudioConnection          patchCord13(mixer1, rms5);
AudioConnection          patchCord14(mixer1, peak3);
AudioControlSGTL5000     sgtl5000_1;     //xy=354.5,533.25
// GUItool: end automatically generated code



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

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

  //Set amplification
  amp1.gain(-1.0); //Inversion
  amp2.gain(1.6); //Amplification per mics

  //Mixer setting to invert signal
  mixer1.gain(0, 0.3);
  mixer1.gain(1, 0.3);
}

elapsedMillis volmsec=0;
String amp_text;
float amp_value;

float out_peak1;
float out_peak2;
float out_peak3;

float out_rms1;
float out_rms2;
float out_rms3;
float out_rms4;
float out_rms5;
float outrmsvar;


void loop() {
  // Adjust amp of background noise coming in
  // Keyboard input for amp value
  // Run every 10 msecs
  if (volmsec > 10)
  {
    if (Serial.available())
    {
        amp_text = Serial.readStringUntil('\n');
        Serial.print(amp_text + " ");
        amp_value = amp_text.toFloat();
        Serial.println(-amp_value);
        amp1.gain(-amp_value);
    }
  
    //Adjust Signal 0 to be identical in amplitude to signal 1
    out_rms1 = rms1.read() * 30.0; // Signal 0
    out_rms2 = rms2.read() * 30.0; // Signal 1
    out_rms3 = rms3.read() * 30.0; // Signal 1
    out_rms4 = rms4.read() * 30.0; // Signal 1
    out_rms5 = rms5.read() * 30.0; // Signal 1

    Serial.print(out_rms1);
    Serial.print(" ");
    Serial.print(out_rms2);
    Serial.print(" ");
    Serial.print(out_rms3);
    Serial.print(" ");
    Serial.print(out_rms4);
    Serial.print(" ");
    Serial.print(out_rms5);
    Serial.print(" ");

    if(out_rms1>(out_rms2+.1) || out_rms1<(out_rms2-.1)) {
      outrmsvar = out_rms1/out_rms2;
      Serial.print(outrmsvar);
      Serial.print(" ");
    }
  
    // Result - verify
    out_peak1 = peak1.read() * 30.0; // Signal 0
    out_peak2 = peak2.read() * 30.0; // Signal 1
    out_peak3 = peak3.read() * 30.0; // Output
    Serial.print(out_peak1);
    Serial.print(" ");
    Serial.print(out_peak2);
    Serial.print(" ");
    Serial.println(out_peak3);
    volmsec = 0;
  }
}

Raw Data
Time rms1 rms2 rms3 -rms3 rms4 rms5 Mixer_out
7.5 1.95 1.07 1.95 -1.95 1.71 1.09 -0.072
7.51 1.65 0.92 1.65 -1.65 1.46 0.93 -0.057
7.52 1.68 0.92 1.68 -1.68 1.47 0.94 -0.063
7.53 1.77 0.96 1.77 -1.77 1.54 0.98 -0.069
7.54 1.4 0.76 1.4 -1.4 1.22 0.78 -0.054
7.55 1.32 0.71 1.32 -1.32 1.14 0.73 -0.054
7.56 1.15 0.65 1.15 -1.15 1.04 0.65 -0.033
7.57 0.98 0.57 0.98 -0.98 0.91 0.56 -0.021
7.58 1.02 0.58 1.02 -1.02 0.93 0.58 -0.027
7.59 1.16 0.65 1.16 -1.16 1.04 0.66 -0.036
7.6 1.1 0.61 1.1 -1.1 0.97 0.62 -0.039
7.61 1.04 0.64 1.04 -1.04 1.02 0.61 -0.006
7.62 1.19 0.65 1.19 -1.19 1.05 0.67 -0.042
7.63 1.2 0.63 1.2 -1.2 1.01 0.66 -0.057
7.64 1.95 1.1 1.95 -1.95 1.76 1.11 -0.057
7.65 2.17 1.23 2.17 -2.17 1.97 1.23 -0.06
7.66 2.5 1.37 2.5 -2.5 2.18 1.39 -0.096
7.67 2.18 1.16 2.18 -2.18 1.85 1.2 -0.099
7.68 2.1 1.18 2.1 -2.1 1.88 1.19 -0.066
7.69 1.76 1 1.76 -1.76 1.6 0.99 -0.048
7.7 1.82 1.11 1.82 -1.82 1.77 1.07 -0.015
7.71 2.1 1.24 2.1 -2.1 1.98 1.22 -0.036
7.72 2.04 1.24 2.04 -2.04 1.98 1.2 -0.018
7.73 1.81 1.1 1.81 -1.81 1.76 1.06 -0.015
7.74 2.09 1.23 2.09 -2.09 1.96 1.2 -0.039
7.75 1.96 1.09 1.96 -1.96 1.74 1.1 -0.066
7.76 1.79 1.05 1.79 -1.79 1.67 1.03 -0.036
7.77 1.44 0.83 1.44 -1.44 1.33 0.82 -0.033
7.78 1.55 0.94 1.55 -1.55 1.51 0.91 -0.012
7.79 1.64 0.99 1.64 -1.64 1.58 0.96 -0.018
7.8 1.58 0.93 1.58 -1.58 1.49 0.92 -0.027
7.81 1.65 1 1.65 -1.65 1.6 0.97 -0.015
7.82 1.79 1.03 1.79 -1.79 1.64 1.02 -0.045
7.83 1.87 1.05 1.87 -1.87 1.69 1.06 -0.054
7.84 1.95 1.1 1.95 -1.95 1.76 1.11 -0.057
7.85 1.77 1.04 1.77 -1.77 1.66 1.02 -0.033
7.86 1.8 1.06 1.8 -1.8 1.69 1.04 -0.033
7.87 1.66 0.99 1.66 -1.66 1.59 0.97 -0.021
7.88 1.82 1.08 1.82 -1.82 1.72 1.06 -0.03
7.89 1.85 1.08 1.85 -1.85 1.72 1.06 -0.039
7.9 1.55 0.85 1.55 -1.55 1.37 0.86 -0.054
7.91 1.35 0.67 1.35 -1.35 1.08 0.71 -0.081
7.92 1.24 0.64 1.24 -1.24 1.02 0.66 -0.066
7.93 1.19 0.65 1.19 -1.19 1.04 0.65 -0.045
7.94 0.94 0.49 0.94 -0.94 0.78 0.5 -0.048
7.95 0.87 0.42 0.87 -0.87 0.68 0.44 -0.057
7.96 0.75 0.36 0.75 -0.75 0.57 0.37 -0.054
7.97 1.18 0.64 1.18 -1.18 1.02 0.65 -0.048
7.98 1.47 0.8 1.47 -1.47 1.28 0.82 -0.057
7.99 1.71 0.98 1.71 -1.71 1.57 0.98 -0.042
8 1.71 0.96 1.71 -1.71 1.53 0.97 -0.054
 
Last edited:
Hi Fred_france,

Well, I confirmed that cancellation was possible by putting the same audio signal into both left and right sides and saw that it became canceled. But I ran into a software limitation that became more time that I wanted to invest. Basically, the code currently processes the data in blocks of samples (1 sample being ~1/44000 of a second) and for the canceling feature to work, the blocks of samples need to be aligned when doing the calculation to get zero if you will. It worked when the same data was pumped into both left and right block, but when I used two mic separately, one into the left and one into the right, the current software did not allow the fine tuning enough to achieve a cancellation.

The hardware is capable of doing the echo cancellation, but in the software, the blocks of audio data need to be able to be aligned with each other using a time stamp and that involves custom code. I would probably recommend decreasing the size of the blocks of samples and write a new mixer code that can combine the blocks based on a time stamp, rather than when the computation is done. You could try to manually align the blocks, but I would recommend having a feedback system which moves the align the two inputs automatically to get a cancellation. Maybe first align the averages, then align down to the blocks to achieve near cancellation. Perfect cancellation won't be possible I think.

Having the microphones too far apart will also be a challenge as the audio source you want to cancel will hit the microphones at different times and if you have two audio sources you want to cancel that move in opposite directions, it may be very hard to cancel both because they hit each microphone at different times, making the resulting wave different between the right and left inputs.

Let me know if you get your project to work.
 
thank you for your reply

my experiences (see my thread) show that there is indeed a synchronization to be done.
Indeed, when I go through "amp1" and "amp2", the sync seems to be made between the two outputs L and R.
On the other hand, when I compare the input (low frequency generator sound source) with the output, there is a delay on the output. This delay changes depending on the frequency.

My goal is to completely cancel the noises picked up by a microphone by reproducing an inverted sound, but this delay will be problematic.

I also have another project which is very close to yours (cancel noise to let only voice through) but I see the issues you have been facing.
A solution could be to memorize for a few seconds the annoying noise (assuming that it is stable like a breath) on the SD card, and to use it in a second step as reference to cancel when the voice passes into the microphone.

Your project dates from a year ago, have you found another solution since?
 
Status
Not open for further replies.
Back
Top