Theremin, filtering Time of Flight sensor

emmanuel63

Well-known member
Hello,
I would like to build a simple theremin using time of flight sensor. I simply measure the distance between my hand and the sensor and map the result to an oscillator frequency. But the update rate of the sensor is slow (20hz), and you can hear the steps. I need to interpolate the frequency between every reading, like a small portamento effect. How can I do it?
Thank you,
Emmanuel
 
The simplest way using the already available blocks in the audio library would be to take a AudioSynthWaveformDc, plug it into AudioSynthWaveformModulated as the oscillator and update the frequency by setting the DC value + ramp time.
 
Another way would be a simple lowpass:
C++:
float alpha = 0.2; // choose that you like it
// frequency_new = ... map your sensor data
frequency = frequency + (alpha * (frequency_new - frequency));
 
If you want to lose the 20Hz pulsing something more like a brickwall low pass would be good - the problem is getting good enough speed of response without having 20Hz artifacts. However low-latency brickwall filters have a lot of ringing which isn't desirable either.
Here we know the unwanted frequency, so could tailor a minimum-phase filter with zeroes at 20Hz and its multiples to help reduce the ringing. In fact a filter with just zeroes might be possible. There is still an issue with phase distortion I suspect, given minimum phase (i.e. minimum delay) is important.
There are other approaches which interpolate using a predictive model - we are trying to create a curve from a set of points after all.
 
I tried the low pass filter. It works pretty well.
I do the filtering and the frequency oscillator updating every millisecond. I think I should do it only when the audio objects are updated. Is there a way to know when audio update occurs ?

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

// GUItool: begin automatically generated code
AudioSynthWaveformModulated waveformMod1;   //xy=656,394
AudioAmplifier           amp1;           //xy=910,408
AudioOutputI2S           i2s1;           //xy=1057,407
AudioConnection          patchCord1(waveformMod1, amp1);
AudioConnection          patchCord2(amp1, 0, i2s1, 0);
AudioConnection          patchCord3(amp1, 0, i2s1, 1);
// GUItool: end automatically generated code


#include <VL53L1X.h>
VL53L1X sensor;

bool sensorIsRanging;
float alpha = 0.10;
float freq;
float new_freq;
elapsedMillis update_audio;

void setup()
{
  Serial.begin(9600);
  AudioMemory(20);
  waveformMod1.begin(1, 200, WAVEFORM_SINE);
  amp1.gain(0.01);

  Wire.begin();
  delay(100);
  Wire.setClock(400000);
  delay(100);
  sensor.init();

  sensor.setDistanceMode(VL53L1X::Short);
  sensor.setMeasurementTimingBudget(20000);
}

void loop() {

  if (sensor.dataReady() == 0) {
    if (sensorIsRanging == 0) {
      sensor.readSingle(false);
      sensorIsRanging = 1;
    }
  } else {
    float distance = sensor.read(false);
    new_freq = map(distance, 100, 700, 100, 2000);
    Serial.println(freq);
    sensorIsRanging = 0;
  }


  if (update_audio > 1) {
    update_audio = 0;
    freq = freq + (alpha * (new_freq - freq));
    waveformMod1.frequency(freq);
  }


}
 
The default is 128 samples/block, at 44100Hz is about 3ms per block and thus per update.
 
Is there a way to know when audio update occurs ?
I use an AudioRecordQueue, wired to something guaranteed to produce data, and whenever a new block is available, read and discard it, then do any required actions. Late in the UK, can post code tomorrow if needed…
 
I use an AudioRecordQueue, wired to something guaranteed to produce data, and whenever a new block is available, read and discard it, then do any required actions. Late in the UK, can post code tomorrow if needed…
I do something similar using the peak object with isAvailable() method.
 
Back
Top