Crackling Loud Noise when changing volume and pitch

I’ve built a theremin like instrument using a daisyseed where I’m changing the pitch and volume using two ultrasonic distance sensors. Everything works fine but whenever I’m changing the pitch or volume there is this incredible loud crackle. Below is the code, a few pictures and a link to the video.

//THEREMIN VARIATON
//DENIS FLUERARU

//NEOPIXELS

#include <Adafruit_NeoPixel.h>
#define PIN      D0
#define NUMPIXELS 48

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
#define DELAYVAL 30

//DISTANCE SENSORS
#define trigPin1 D2
#define echoPin1 D3
long duration1;
float distance1;
long counterzero1;

#define trigPin2 D5
#define echoPin2 D6
long duration2;
float distance2;
long counterzero2;

int midiSignal;
int MIDItoFQ;
float sonarAmplitude;
float ampSignal;

int lightState = 0;
int timer = -1;

//DAISY
#include "DaisyDuino.h"
DaisyHardware hw;
size_t num_channels;
static Oscillator osc;




void MyCallback(float **in, float **out, size_t size) {
  osc.SetFreq(mtof(midiSignal));
  osc.SetAmp(ampSignal);
  osc.SetWaveform(osc.WAVE_TRI);
  for (size_t i = 0; i < size; i++) {
    float sig = osc.Process();
    for (size_t chn = 0; chn < num_channels; chn++) {
      out[chn][i] = sig;
    }
  }
}

void setup() {

  float sample_rate;

  // Initialize for Daisy pod at 48kHz
  hw = DAISY.init(DAISY_SEED, AUDIO_SR_48K);
  num_channels = hw.num_channels;
  sample_rate = DAISY.get_samplerate();
  osc.Init(sample_rate);
  osc.SetWaveform(osc.WAVE_TRI);
  DAISY.begin(MyCallback);

  pinMode(trigPin1, OUTPUT); // Sets the trigPin as an OUTPUT
  pinMode(echoPin1, INPUT);
  pinMode(trigPin2, OUTPUT); // Sets the trigPin as an OUTPUT
  pinMode(echoPin2, INPUT);

  counterzero1 = 0;
  counterzero2 = 0;


  pixels.begin();
  setPixels(255, 255, 255);
  pixels.setBrightness(255);

}

void loop() {


  frequency();
  amplitude();
  states();

}


void frequency() {
  digitalWrite(trigPin1, LOW);
  delayMicroseconds(2);
  // Sets the trigPin HIGH (ACTIVE) for 10 microseconds
  digitalWrite(trigPin1, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin1, LOW);
  // Reads the echoPin, returns the sound wave travel time in microseconds
  duration1 = pulseIn(echoPin1, HIGH);
  // Calculating the distance
  distance1 = (duration1 - 10) * 0.034 / 2; // Speed of sound wave divided by 2 (go and back)
  distance1 = constrain(distance1, 20, 100);
  MIDItoFQ = (distance1 - 20) / 5 + 48;
  if (MIDItoFQ >= 64) {
    MIDItoFQ = 0;
  }
  if (MIDItoFQ == 0) {
    counterzero1++;
  } else {
    counterzero1 = 0;
  }
  if (counterzero1 >= 5 || counterzero1 == 0) {
    midiSignal = MIDItoFQ;
  }
}

void amplitude() {
  digitalWrite(trigPin2, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin2, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin2, LOW);
  duration2 = pulseIn(echoPin2, HIGH);
  // Calculating the distance
  distance2 = (duration2 - 10) * 0.034 / 2; // Speed of sound wave divided by 2 (go and back)
  distance2 = constrain(distance2, 20, 50);
  sonarAmplitude = (distance2 - 20) * 0.033;
  if (sonarAmplitude >= 0.95) {
    sonarAmplitude = 0;
  }
  if (sonarAmplitude == 0) {
    counterzero2++;
  } else {
    counterzero2 = 0;
  }
  if (counterzero2 >= 200 || counterzero2 == 0) {
    ampSignal = sonarAmplitude;
    //delay(50);

  }

}

void setPixels(int r, int g, int b) {
  for (int i = 0; i < NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(r, g, b));
  }
  pixels.show();
}

void states() {


  if (midiSignal == 48 && ampSignal == 0.00 && lightState != 2) {
    if (timer == -1) {
      lightState = 1;
      timer = 0;
      setPixels(255, 0, 0);
    }
    timer++;
    if (timer >= 2500) {
      lightState = 2;
    }
    return;
  }
  if (lightState == 2) {
    setPixels(0, 255, 0);

  }

  if (counterzero1 >= 15) {
    //  if (midiSignal <= 0 && ampSignal <= 0.00 && lightState != 0) {
    timer = -1;
    lightState = 0;
    setPixels(255,255, 255);

  }

  if (lightState == 0 || lightState == 1) {
    midiSignal = 0;
    ampSignal = 0.00;
  }
}
![IMG_0337|375x500](upload://k8d8NGB18ZksGCQF8hrOM3yE9g4.jpeg)
![IMG_0340|666x500](upload://5Ue8dUUWcGUOrCuCY4W1gQhat98.jpeg)
![IMG_0341|375x500](upload://4XeXADANdxMZmDudBX6NU025pHr.jpeg)

Salut Denis!

It’s hard to spot exact problem from a quick glance over your code. But at the very least you need to use an LPF over amplitude to have smooth changes in amplitude. The way it works now causes volume to jump to different value every 24 ms or so. And I’m sure that the sensors are quite noisy themselves. You actually need a smooth transition and it must be applied at audio rate (that is, for every sample).

Also, there’s an obvious optimization to reduce that update delay - you could refactor amplitude() and frequency() to be performed in a single function that shares 10ms and 2ms delays, this way you’ll get updates every 12ms rather than every 24ms.

PS - nice dress btw, is that a daisy print on it?

1 Like

You need to smooth your parameter changes over time.
What is happening is when you jump from one value to another instantly there is a hard edge on the the waveform = distortion. For example, if you generate a sine wave and then instantly increase it’s volume by 10db, you have created “a piece of a square wave” interjected into the sine and the D->A converter WILL make a click. Do this multiple times a second and it will sound like noise/distortion. Soooooooo it is best to perform parameter changes (especially with volume) over several samples. 480 samples is 1ms at 48k and is more than enough, although that “could” generate some momentary low frequency artifacts. There are multiple solutions, code and math-wise, for this issue. The simplest ways are typically ramps generated from previous value to new value.