Custom 7- segment display


import processing.sound.*; 

AudioIn mic;
Waveform waveform;
int numSamples = 1024; 
float sampleRate = 44100; 
float smoothedFreq = 0; 

boolean[][] segments = {
  {true,  true,  true,  true,  true,  true,  false}, 
  {false, true,  true,  false, false, false, false}, 
  {true,  true,  false, true,  true,  false, true }, 
  {true,  true,  true,  true,  false, false, true }, 
  {false, true,  true,  false, false, true,  true }, 
  {true,  false,  true,  true,  false, true,  true }, 
  {true,  false,  true,  true,  true,  true,  true }, 
  {true,  true,  true,  false, false, false, false}, 
  {true,  true,  true,  true,  true,  true,  true }, 
  {true,  true,  true,  true,  false, true,  true }  
};

void setup() {
  size(displayWidth, displayHeight);
  frameRate(30); 
  
  mic = new AudioIn(this, 0);
  mic.start();
  
  waveform = new Waveform(this, numSamples);
  waveform.input(mic);
}

void draw() {
  background(10, 20, 10); 
  
  waveform.analyze();
  
  float totalPeriodsSamples = 0;
  int crossingCount = 0;
  float lastCrossingTime = -1;
  float maxPeak = 0;
  
  for (int i = 1; i < numSamples; i++) {
    float prev = waveform.data[i - 1];
    float curr = waveform.data[i];
    
    if (abs(curr) > maxPeak) maxPeak = abs(curr);
    
    if ((prev >= 0 && curr < 0) || (prev < 0 && curr >= 0)) {
      float fraction = abs(prev) / (abs(prev) + abs(curr));
      float exactCrossingTime = (i - 1) + fraction;
      
      if (lastCrossingTime != -1) {
        totalPeriodsSamples += (exactCrossingTime - lastCrossingTime);
        crossingCount++;
      }
      lastCrossingTime = exactCrossingTime;
    }
  }
  
  float frequencyHz = 0;
  if (crossingCount > 0 && totalPeriodsSamples > 0) {
    float avgHalfPeriod = totalPeriodsSamples / crossingCount;
    frequencyHz = sampleRate / (2.0 * avgHalfPeriod);
  }
  
  if (frequencyHz > 20000) frequencyHz = 20000;
  if (maxPeak < 0.015 || crossingCount < 2) frequencyHz = 0;
  
  smoothedFreq = lerp(smoothedFreq, frequencyHz, 0.25);
  
  float boxW = width * 0.85;
  float boxH = height * 0.35;
  float boxX = (width - boxW) / 2;
  float boxY = (height - boxH) / 2;
  
  stroke(0, 120, 30);
  strokeWeight(5);
  fill(5, 15, 5);
  rect(boxX, boxY, boxW, boxH, 15);
  
  noStroke();
  if ((frameCount / 6) % 2 == 0 && maxPeak > 0.015) {
    fill(0, 255, 50); 
  } else {
    fill(5, 40, 10);  
  }
  ellipse(boxX + 40, boxY + 40, 15, 15);
  fill(0, 200, 40);
  textSize(24);
  text("GATE", boxX + 65, boxY + 48);
  
  float w = boxW * 0.11;    
  float h = w * 1.8; 
  float spacing = w * 0.22;  
  float totalDigitsW = (w * 5) + (spacing * 4);
  float startX = boxX + (boxW - totalDigitsW) / 2;
  float digitsY = boxY + (boxH - h) / 2 + 10;
  
  int displayVal = int(smoothedFreq);
  int d5 = (displayVal / 10000) % 10; 
  int d4 = (displayVal / 1000) % 10;  
  int d3 = (displayVal / 100) % 10;   
  int d2 = (displayVal / 10) % 10;    
  int d1 = displayVal % 10;           
  
  drawSevenSegment(startX, digitsY, w, h, d5);
  drawSevenSegment(startX + (w + spacing), digitsY, w, h, d4);
  
  drawDecimalDot(startX + (w + spacing) * 2 - (spacing / 2), digitsY + h, w);
  
  drawSevenSegment(startX + (w + spacing) * 2, digitsY, w, h, d3);
  drawSevenSegment(startX + (w + spacing) * 3, digitsY, w, h, d2);
  drawSevenSegment(startX + (w + spacing) * 4, digitsY, w, h, d1);
  
  fill(0, 255, 50);
  textSize(35);
  text("kHz", boxX + boxW - 80, boxY + boxH - 35);
  
  fill(0, 100, 25);
  textSize(22);
  text("SIGNAL LEVEL", boxX + 40, boxY + boxH - 35);
  
  float barMaxW = boxW * 0.45;
  float barW = map(maxPeak, 0, 0.5, 0, barMaxW);
  barW = constrain(barW, 0, barMaxW);
  
  stroke(0, 60, 15);
  strokeWeight(2);
  noFill();
  rect(boxX + 220, boxY + boxH - 52, barMaxW, 18, 3);
  
  noStroke();
  fill(0, 255, 50);
  rect(boxX + 222, boxY + boxH - 50, barW, 14, 2);
}

void drawSevenSegment(float x, float y, float w, float h, int num) {
  float t = w * 0.16; 
  float g = 1.5;      
  
  color onColor = color(0, 255, 50);  
  color offColor = color(5, 30, 10); 
  
  noStroke();
  pushMatrix();
  translate(x, y);
  shearX(radians(-7)); 
  
  fill(segments[num][0] ? onColor : offColor);
  beginShape(); vertex(t + g, 0); vertex(w - t - g, 0); vertex(w - (t/2) - g, t/2); vertex(w - t - g, t); vertex(t + g, t); vertex((t/2) + g, t/2); endShape(CLOSE);
  
  fill(segments[num][1] ? onColor : offColor);
  beginShape(); vertex(w, t + g); vertex(w, (h/2) - (t/2) - g); vertex(w - (t/2), (h/2) - g); vertex(w - t, (h/2) - (t/2) - g); vertex(w - t, t + g); vertex(w - (t/2), (t/2) + g); endShape(CLOSE);
  
  fill(segments[num][2] ? onColor : offColor);
  beginShape(); vertex(w, (h/2) + (t/2) + g); vertex(w, h - t - g); vertex(w - (t/2), h - (t/2) - g); vertex(w - t, h - t - g); vertex(w - t, (h/2) + (t/2) + g); vertex(w - (t/2), (h/2) + g); endShape(CLOSE);
  
  fill(segments[num][3] ? onColor : offColor);
  beginShape(); vertex(t + g, h - t); vertex(w - t - g, h - t); vertex(w - (t/2) - g, h - (t/2)); vertex(w - t - g, h); vertex(t + g, h); vertex((t/2) + g, h - (t/2)); endShape(CLOSE);
  
  fill(segments[num][4] ? onColor : offColor);
  beginShape(); vertex(t, (h/2) + (t/2) + g); vertex(t, h - t - g); vertex(t/2, h - (t/2) - g); vertex(0, h - t - g); vertex(0, (h/2) + (t/2) + g); vertex(t/2, (h/2) + g); endShape(CLOSE);
  
  fill(segments[num][5] ? onColor : offColor);
  beginShape(); vertex(t, t + g); vertex(t, (h/2) - (t/2) - g); vertex(t/2, (h/2) - g); vertex(0, (h/2) - (t/2) - g); vertex(0, t + g); vertex(t/2, (t/2) + g); endShape(CLOSE);
  
  fill(segments[num][6] ? onColor : offColor);
  beginShape(); vertex(t + g, (h/2) - (t/2)); vertex(w - t - g, (h/2) - (t/2)); vertex(w - (t/2) - g, h/2); vertex(w - t - g, (h/2) + (t/2)); vertex(t + g, (h/2) + (t/2)); vertex((t/2) + g, h/2); endShape(CLOSE);
  
  popMatrix();
}

void drawDecimalDot(float x, float y, float w) {
  float dotSize = w * 0.15;
  fill(0, 255, 50); 
  noStroke();
  rect(x, y - dotSize, dotSize, dotSize, 1);
}



I am unable to run the demo in Android mode with Processing 4.5.6 on MacOS (M4 Tahoe 26.5) and get this error message:

Build folder: /var/folders/x2/hwxvmv9s6t16_tkw9tgmbp040000gn/T/android737348970227068550sketch
Exception in thread "Thread-54" java.lang.NullPointerException: Cannot read the array length because "list" is null
	at processing.app.Library.wrapFiles(Library.java:429)
	at processing.app.Library.getApplicationExports(Library.java:439)
	at processing.mode.android.AndroidBuild.copyImportedLibs(AndroidBuild.java:839)
	at processing.mode.android.AndroidBuild.createAppModule(AndroidBuild.java:481)
	at processing.mode.android.AndroidBuild.createProject(AndroidBuild.java:262)
	at processing.mode.android.AndroidBuild.build(AndroidBuild.java:223)
	at processing.mode.android.AndroidMode.handleRunDevice(AndroidMode.java:294)
	at processing.mode.android.AndroidEditor$17.run(AndroidEditor.java:421)

I get the same error message if I run the following reference code in Android mode:

import processing.sound.*;

void setup() {
  Sound.list();
}

I am using a Samsung A8 tablet. There is no microphone connected to it. What type of device are you running the code on and does it have a microphone attached to it (or use an internal microphone)?

Addendum:

Runs without error in Java mode. Numbers change if I add a microphone.

Cool beans!

A440 (pitch standard) - Wikipedia

I got it to work on my Android with Processing 4.5.6 and Processing 4.3.4 with Sound Library 2.3.1 and applying necessary permissions for microphone.

:)

My tablet android 8 the sketch works without errors on APDE 5.1

@glv As always you are creative

I finally got it to run in Windows 11. Error messages from Processing 4.5.6 were helpful. I was referred to Examples/Libraries/Sound/IO/AudioInputAndroid. Based on these suggestions I added an initialize function and microphone permissions as shown below:

Modified source code:

import processing.sound.*; 

AudioIn mic;
Waveform waveform;

AudioIn input;
Amplitude loudness;

int numSamples = 1024; 
float sampleRate = 44100; 
float smoothedFreq = 0; 

boolean[][] segments = {
  {true,  true,  true,  true,  true,  true,  false}, 
  {false, true,  true,  false, false, false, false}, 
  {true,  true,  false, true,  true,  false, true }, 
  {true,  true,  true,  true,  false, false, true }, 
  {false, true,  true,  false, false, true,  true }, 
  {true,  false,  true,  true,  false, true,  true }, 
  {true,  false,  true,  true,  true,  true,  true }, 
  {true,  true,  true,  false, false, false, false}, 
  {true,  true,  true,  true,  true,  true,  true }, 
  {true,  true,  true,  true,  false, true,  true }  
};

void setup() {
  size(displayWidth, displayHeight);
  frameRate(30); 
    if (hasPermission("android.permission.RECORD_AUDIO")) {
    initialize(true);
  } else {
    requestPermission("android.permission.RECORD_AUDIO", "initialize");
  }
 // input = new AudioIn(this, 0);
 // input.start();
  
  waveform = new Waveform(this, numSamples);
  waveform.input(input);
}

void initialize(boolean granted) {
  if (!granted) {
    return;
  }

  // Create an Audio input and grab the 1st channel
  input = new AudioIn(this, 0);

  // Begin capturing the audio input
  input.start();
  // start() activates audio capture so that you can use it as
  // the input to live sound analysis, but it does NOT cause the
  // captured audio to be played back to you. if you also want the
  // microphone input to be played back to you, call
  //    input.play();
  // instead (be careful with your speaker volume, you might produce
  // painful audio feedback. best to first try it out wearing headphones!)

  // Create a new Amplitude analyzer
  loudness = new Amplitude(this);

  // Patch the input to the volume analyzer
  loudness.input(input);
}

void draw() {
  background(10, 20, 10); 
  
  waveform.analyze();
  
  float totalPeriodsSamples = 0;
  int crossingCount = 0;
  float lastCrossingTime = -1;
  float maxPeak = 0;
  
  for (int i = 1; i < numSamples; i++) {
    float prev = waveform.data[i - 1];
    float curr = waveform.data[i];
    
    if (abs(curr) > maxPeak) maxPeak = abs(curr);
    
    if ((prev >= 0 && curr < 0) || (prev < 0 && curr >= 0)) {
      float fraction = abs(prev) / (abs(prev) + abs(curr));
      float exactCrossingTime = (i - 1) + fraction;
      
      if (lastCrossingTime != -1) {
        totalPeriodsSamples += (exactCrossingTime - lastCrossingTime);
        crossingCount++;
      }
      lastCrossingTime = exactCrossingTime;
    }
  }
  
  float frequencyHz = 0;
  if (crossingCount > 0 && totalPeriodsSamples > 0) {
    float avgHalfPeriod = totalPeriodsSamples / crossingCount;
    frequencyHz = sampleRate / (2.0 * avgHalfPeriod);
  }
  
  if (frequencyHz > 20000) frequencyHz = 20000;
  if (maxPeak < 0.015 || crossingCount < 2) frequencyHz = 0;
  
  smoothedFreq = lerp(smoothedFreq, frequencyHz, 0.25);
  
  float boxW = width * 0.85;
  float boxH = height * 0.35;
  float boxX = (width - boxW) / 2;
  float boxY = (height - boxH) / 2;
  
  stroke(0, 120, 30);
  strokeWeight(5);
  fill(5, 15, 5);
  rect(boxX, boxY, boxW, boxH, 15);
  
  noStroke();
  if ((frameCount / 6) % 2 == 0 && maxPeak > 0.015) {
    fill(0, 255, 50); 
  } else {
    fill(5, 40, 10);  
  }
  ellipse(boxX + 40, boxY + 40, 15, 15);
  fill(0, 200, 40);
  textSize(24);
  text("GATE", boxX + 65, boxY + 48);
  
  float w = boxW * 0.11;    
  float h = w * 1.8; 
  float spacing = w * 0.22;  
  float totalDigitsW = (w * 5) + (spacing * 4);
  float startX = boxX + (boxW - totalDigitsW) / 2;
  float digitsY = boxY + (boxH - h) / 2 + 10;
  
  int displayVal = int(smoothedFreq);
  int d5 = (displayVal / 10000) % 10; 
  int d4 = (displayVal / 1000) % 10;  
  int d3 = (displayVal / 100) % 10;   
  int d2 = (displayVal / 10) % 10;    
  int d1 = displayVal % 10;           
  
  drawSevenSegment(startX, digitsY, w, h, d5);
  drawSevenSegment(startX + (w + spacing), digitsY, w, h, d4);
  
  drawDecimalDot(startX + (w + spacing) * 2 - (spacing / 2), digitsY + h, w);
  
  drawSevenSegment(startX + (w + spacing) * 2, digitsY, w, h, d3);
  drawSevenSegment(startX + (w + spacing) * 3, digitsY, w, h, d2);
  drawSevenSegment(startX + (w + spacing) * 4, digitsY, w, h, d1);
  
  fill(0, 255, 50);
  textSize(35);
  text("kHz", boxX + boxW - 80, boxY + boxH - 35);
  
  fill(0, 100, 25);
  textSize(22);
  text("SIGNAL LEVEL", boxX + 40, boxY + boxH - 35);
  
  float barMaxW = boxW * 0.45;
  float barW = map(maxPeak, 0, 0.5, 0, barMaxW);
  barW = constrain(barW, 0, barMaxW);
  
  stroke(0, 60, 15);
  strokeWeight(2);
  noFill();
  rect(boxX + 220, boxY + boxH - 52, barMaxW, 18, 3);
  
  noStroke();
  fill(0, 255, 50);
  rect(boxX + 222, boxY + boxH - 50, barW, 14, 2);
}

void drawSevenSegment(float x, float y, float w, float h, int num) {
  float t = w * 0.16; 
  float g = 1.5;      
  
  color onColor = color(0, 255, 50);  
  color offColor = color(5, 30, 10); 
  
  noStroke();
  pushMatrix();
  translate(x, y);
  shearX(radians(-7)); 
  
  fill(segments[num][0] ? onColor : offColor);
  beginShape(); vertex(t + g, 0); vertex(w - t - g, 0); vertex(w - (t/2) - g, t/2); vertex(w - t - g, t); vertex(t + g, t); vertex((t/2) + g, t/2); endShape(CLOSE);
  
  fill(segments[num][1] ? onColor : offColor);
  beginShape(); vertex(w, t + g); vertex(w, (h/2) - (t/2) - g); vertex(w - (t/2), (h/2) - g); vertex(w - t, (h/2) - (t/2) - g); vertex(w - t, t + g); vertex(w - (t/2), (t/2) + g); endShape(CLOSE);
  
  fill(segments[num][2] ? onColor : offColor);
  beginShape(); vertex(w, (h/2) + (t/2) + g); vertex(w, h - t - g); vertex(w - (t/2), h - (t/2) - g); vertex(w - t, h - t - g); vertex(w - t, (h/2) + (t/2) + g); vertex(w - (t/2), (h/2) + g); endShape(CLOSE);
  
  fill(segments[num][3] ? onColor : offColor);
  beginShape(); vertex(t + g, h - t); vertex(w - t - g, h - t); vertex(w - (t/2) - g, h - (t/2)); vertex(w - t - g, h); vertex(t + g, h); vertex((t/2) + g, h - (t/2)); endShape(CLOSE);
  
  fill(segments[num][4] ? onColor : offColor);
  beginShape(); vertex(t, (h/2) + (t/2) + g); vertex(t, h - t - g); vertex(t/2, h - (t/2) - g); vertex(0, h - t - g); vertex(0, (h/2) + (t/2) + g); vertex(t/2, (h/2) + g); endShape(CLOSE);
  
  fill(segments[num][5] ? onColor : offColor);
  beginShape(); vertex(t, t + g); vertex(t, (h/2) - (t/2) - g); vertex(t/2, (h/2) - g); vertex(0, (h/2) - (t/2) - g); vertex(0, t + g); vertex(t/2, (t/2) + g); endShape(CLOSE);
  
  fill(segments[num][6] ? onColor : offColor);
  beginShape(); vertex(t + g, (h/2) - (t/2)); vertex(w - t - g, (h/2) - (t/2)); vertex(w - (t/2) - g, h/2); vertex(w - t - g, (h/2) + (t/2)); vertex(t + g, (h/2) + (t/2)); vertex((t/2) + g, h/2); endShape(CLOSE);
  
  popMatrix();
}

void drawDecimalDot(float x, float y, float w) {
  float dotSize = w * 0.15;
  fill(0, 255, 50); 
  noStroke();
  rect(x, y - dotSize, dotSize, dotSize, 1);
}  

Sketch permissions added:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
    <uses-permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>

Excellent work. The idea is not about reading the frequency; the idea is to design a 7-segment display without using special files, font libraries, or character libraries.