Is an Audio In OLED oscilloscope possible?

Here’s the final mark 1 build!

Surprised myself by figuring out how to implement horizontal scaling, other knob is vertical scaling.

There’s one last thing that I can’t figure out though. If there isn’t a stereo plug plugged into the output, and if it doesn’t then lead to a stereo input, then the pots behave as if there’s grounding issues. AGND and DGND are bridged, and the audio jacks and OLED screen work fine regardless. I’d like it to be fully functioning even without needing to use the output.

Here’s the code:

#include <CircularBuffer.h>

#include <AudioClass.h>
#include <DaisyDSP.h>
#include <DaisyDuino.h>
#include <hal_conf_extra.h>

#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 32
#define CHUNK 6

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ SCL, /* data=*/ SDA);   // pin remapping with ESP8266 HW I2C

DaisyHardware hw;
size_t num_channels;

const unsigned long period = 16777; //period of time to perform an OLED update, in microseconds
unsigned long previousTime, currentTime;
uint64_t currentCount, previousCount;
uint8_t hKnob, vKnob;
float addIn, avg; //average of samples
int lastSmpl; //used in OLED draw
int avgPeriod = 6; //number of 6 sample 'chunks' to average, larger period means slower horizontal scaling
int vScale = 2; //vertical scaling amount
int previousValue;
CircularBuffer<float, DISPLAY_WIDTH> buf;
bool draw;

void AudioCallback(float **in, float **out, size_t size) {
  for (size_t i = 0; i < size; i++) {
    for (size_t chn = 0; chn < num_channels; chn++) {
      out[chn][i] = in[chn][i]; //bypass audio
      addIn += in[chn][i]; //continuously add up incoming values
    }
  }
  currentCount++;
  if (currentCount - previousCount >= avgPeriod) {
    avg += addIn;
    avg /= CHUNK * avgPeriod;
    buf.push(avg * vScale);
    if (buf.isFull()) {
      buf.shift();
    }
    avg = 0;
    addIn = 0;
    draw = true;
    previousCount = currentCount;
  }

}

void setup(void) {
  float samplerate;
//  Serial.begin();
  hw = DAISY.init(DAISY_SEED, AUDIO_SR_48K);
  num_channels = hw.num_channels;
  samplerate = DAISY.get_samplerate();
  lastSmpl = 16;
  draw = false;
  hKnob = A2;
  vKnob = A0;
  pinMode(hKnob, INPUT);
  pinMode(vKnob, INPUT);
  u8g2.begin();
  DAISY.SetAudioBlockSize(CHUNK);
  DAISY.begin(AudioCallback);
}

void loop(void) {
  unsigned long currentTime = micros();
  if (draw) {
    if (currentTime - previousTime >= period) {
      u8g2.clearBuffer();
      for (int i = DISPLAY_WIDTH; i > 0; i--) {
        float y = buf[i];
        int x = i;
        updateOLED(x, y);
      }
      u8g2.sendBuffer();
      draw = false;
      previousTime = currentTime;
    }
  }

  //Stabilize potentiometers, assign values to horizontal and vertical scaling
  int h = smooth(hKnob);
  avgPeriod = map(h, 0, 1023, 1, 256); //
  int v = smooth(vKnob);
  vScale = map(v, 0, 1023, 2, 32);
}

void updateOLED(int x, float val) {
  float y = (val + 1) / 2 * DISPLAY_HEIGHT; //map the incoming averaged value from -1, 1 to 0, displayHeight
  float currentSmpl = constrain(y, 0, DISPLAY_HEIGHT - 1); //create currentSmpl value from y and constrain to displayHeight
  u8g2.drawLine(x, lastSmpl, x + 1, currentSmpl); //draw a line from last sample to current sample
  lastSmpl = currentSmpl; //reassign last sample as the current one
}

int smooth(uint8_t pot) {
  int result;
  int newValue = 0;
  const int numReadings = 48;
  for (int i = 0; i < numReadings; i++) {
    newValue += analogRead(pot);
  }
  newValue /= numReadings;

  //additional stabilization? I don't know if it's doing much
  if (abs(previousValue - newValue) > 2) {
    result = newValue;
    previousValue = newValue;
  } else {
    result = previousValue;
  }
  return result;
}
5 Likes