This is an old version of the documentation. View the latest version here.
Control Surface  1.0.0
MIDI Control Surface library for Arduino
2.VU-Meter-OLED-USB-DAC.ino

2.VU-Meter-OLED-USB-DAC

This example shows the usage of the AudioVU and AnalogVUDisplay classes of the Control Surface library.

It displays two analog-style VU meters on two 128×64 OLED displays.
The VU meters imitate the inertia and ballistics of real analog VU meters.

It acts as a USB Audio DAC (Digital-to-Analog Converter).

Boards:
Teensy 3.x

Connections

Add a capacitor between the reset pin of the displays and ground, and a resistor from reset to 3.3V. The values are not critical, 0.1µF and 10kΩ work fine.
You do need some way to reset the displays, without it, it won't work.
Alternatively, you could use an IO pin from the Teensy to reset the displays, but this just "wastes" a pin.

Behavior

Upload the sketch, and select the Control Surface as the audio output of your computer. Connect the output of the DAC to a pair of headphones or powered speakers, and play some music.
You should see the VU meters come to life and jump around to the music.

You can now adjust the volume using the potentiometer on pin A0, and the gain/sensitivity of the meters using the potentiometer on pin A1.

Mapping

None.

Demo

Todo:
Add a demo video.

Written by PieterP, 2019-08-09
https://github.com/tttapa/Control-Surface

#include <Control_Surface.h> // Include the Control Surface library
// Include the display interface you'd like to use
// #define DIGITAL_VU
// ----------------------------- Display setup ------------------------------ //
// ========================================================================== //
constexpr uint8_t SCREEN_WIDTH = 128;
constexpr uint8_t SCREEN_HEIGHT = 64;
constexpr int8_t OLED_DC = 17; // Data/Command pin of the display
constexpr int8_t OLED_reset = -1; // Use the external RC circuit for reset
constexpr int8_t OLED_CS_L = 10; // Chip Select pin of the left display
constexpr int8_t OLED_CS_R = 18; // Chip Select pin of the right display
constexpr uint32_t SPI_Frequency = SPI_MAX_SPEED;
// Instantiate the displays
Adafruit_SSD1306 ssd1306Display_L = {
SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, OLED_DC,
OLED_reset, OLED_CS_L, SPI_Frequency,
};
// Instantiate the displays
Adafruit_SSD1306 ssd1306Display_R = {
SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, OLED_DC,
OLED_reset, OLED_CS_R, SPI_Frequency,
};
// --------------------------- Display interface ---------------------------- //
// ========================================================================== //
// Implement the display interface, specifically, the begin and drawBackground
// methods.
// If you want, you can draw a background image of a VU meter first, and then
// draw the needle on top.
class MySSD1306_DisplayInterface : public SSD1306_DisplayInterface {
public:
MySSD1306_DisplayInterface(Adafruit_SSD1306 &display)
void begin() override {
// Initialize the Adafruit_SSD1306 display
if (!disp.begin())
FATAL_ERROR(F("SSD1306 allocation failed."), 0x1306);
#ifdef DIGITAL_VU
disp.setRotation(1); // Rotate the display in portrait mode
#endif
// If you override the begin method, remember to call the super class method
}
void drawBackground() override {}
} display_L = ssd1306Display_L, display_R = ssd1306Display_R;
// --------------------------- Audio connections ---------------------------- //
// ========================================================================== //
AudioInputUSB audioInputUSB;
AudioMixer4 mixer_L;
AudioAnalyzeRMS rms_L;
AudioMixer4 mixer_R;
AudioAnalyzeRMS rms_R;
AudioOutputI2S audioOutputI2S;
// Connect the input to the mixers (volume control), the input to the RMS
// meters, and the mixers to the output
AudioConnection patchCord1(audioInputUSB, 0, mixer_L, 0);
AudioConnection patchCord2(audioInputUSB, 0, rms_L, 0);
AudioConnection patchCord3(audioInputUSB, 1, mixer_R, 0);
AudioConnection patchCord4(audioInputUSB, 1, rms_R, 0);
AudioConnection patchCord5(mixer_L, 0, audioOutputI2S, 0);
AudioConnection patchCord6(mixer_R, 0, audioOutputI2S, 1);
// ----------------------------- Volume control ----------------------------- //
// ========================================================================== //
VolumeControl<2> volume = {{&mixer_L, &mixer_R}, A0, 1.0};
// ------------------------------- VU Meters -------------------------------- //
// ========================================================================== //
#ifdef DIGITAL_VU
AudioVU vu_L = {rms_L, MovingCoilBallistics::noOvershoot(), 1, 25};
AudioVU vu_R = {rms_R, MovingCoilBallistics::noOvershoot(), 1, 25};
MCU::VUDisplay vu_display_L = {display_L, vu_L, {0, 127}, 64, 4, 1, WHITE};
MCU::VUDisplay vu_display_R = {display_R, vu_R, {0, 127}, 64, 4, 1, WHITE};
#else
MCU::AnalogVUDisplay vu_display_L = {
display_L, // Display to display on
vu_L, // VU meter to display
{63, 63}, // Location of the needle pivot
63, // Length of the needle
-140 * PI / 180, // Minimum angle (radians)
100 * PI / 180, // Total angle range (radians)
WHITE, // Color
};
// Note that the y axis points downwards (as is common in computer graphics).
// This means that a positive angle is clockwise, and -140° lies in the top left
// quadrant
MCU::AnalogVUDisplay vu_display_R = {
display_R, vu_R, {63, 63}, 63, -140 * PI / 180, 100 * PI / 180, WHITE,
};
#endif
// ------------------------------- Gain knob -------------------------------- //
// ========================================================================== //
constexpr float maxGain = 5;
FilteredAnalog<> gainKnob = A1;
// --------------------------------- Setup ---------------------------------- //
// ========================================================================== //
void setup() {
AudioMemory(8);
// The default SPI MOSI pin (11) is used for I²S, so we need to use the
// alternative MOSI pin (7)
SPI.setMOSI(7);
display_L.begin();
display_R.begin();
}
// ---------------------------------- Loop ---------------------------------- //
// ========================================================================== //
void loop() {
const unsigned long frametime = 1000000 / MAX_FPS;
static unsigned long previousFrameTime = micros();
if (micros() - previousFrameTime >= frametime) {
previousFrameTime += frametime;
Updatable<AudioVU>::updateAll(); // Update VU meters
Control_Surface.updateDisplays(); // Update displays
Updatable<Potentiometer>::updateAll(); // Update volume controls
}
if (gainKnob.update()) {
float gain = gainKnob.getFloatValue() * maxGain;
vu_L.setGain(gain);
vu_R.setGain(gain);
}
}
FilteredAnalog
A class that reads and filters an analog input.
Definition: FilteredAnalog.hpp:47
MovingCoilBallistics::responsiveVU
static MovingCoilBallistics responsiveVU(float Tsfactor=2.0)
Definition: MovingCoilBallistics.hpp:40
MovingCoilBallistics::noOvershoot
static MovingCoilBallistics noOvershoot(float Tsfactor=1.0)
Definition: MovingCoilBallistics.hpp:44
FilteredAnalog::getFloatValue
float getFloatValue() const
Get the filtered value of the analog input with the mapping function applied as a floating point numb...
Definition: FilteredAnalog.hpp:122
FilteredAnalog::update
bool update()
Read the analog input value, apply the mapping function, and update the average.
Definition: FilteredAnalog.hpp:97
AudioVU
A VU meter that reads from an Audio stream using the Analyzer class.
Definition: AudioVU.hpp:17
Updatable::updateAll
static void updateAll()
Update all enabled instances of this class.
Definition: Updatable.hpp:75
Control_Surface.h
The main header file that includes all Control-Surface header files.
MCU::AnalogVUDisplay
Definition: VUDisplay.hpp:93
SSD1306_DisplayInterface
This class creates a mapping between the Adafruit_SSD1306 display driver and the general display inte...
Definition: DisplayInterfaceSSD1306.hpp:13
Control_Surface
Control_Surface_ & Control_Surface
A predefined instance of the Control Surface to use in the Arduino sketches.
Definition: Control_Surface_Class.cpp:171
SSD1306_DisplayInterface::drawBackground
void drawBackground() override=0
Draw a custom background.
SPI_MAX_SPEED
constexpr static Frequency SPI_MAX_SPEED
Definition: Settings.hpp:113
FATAL_ERROR
#define FATAL_ERROR(x, e)
Definition: Error.hpp:35
MAX_FPS
constexpr uint8_t MAX_FPS
The maximum frame rate of the displays.
Definition: Settings.hpp:111
DisplayInterfaceSSD1306.hpp
MCU::VUDisplay
Definition: VUDisplay.hpp:10
Control_Surface_::updateDisplays
void updateDisplays()
Clear, draw and display all displays.
Definition: Control_Surface_Class.cpp:155
VolumeControl
A class for controlling the volume of AudioMixer4 objects using a potentiometer.
Definition: VolumeControl.hpp:23
AudioVU::setGain
void setGain(float gain)
Set the gain for the VU meter.
Definition: AudioVU.hpp:91
DisplayInterface::begin
virtual void begin()
Initialize the display.
Definition: DisplayInterface.cpp:7
FilteredAnalog::setupADC
static void setupADC()
Definition: FilteredAnalog.hpp:135
MIDI_Notes::F
constexpr int8_t F
Definition: Notes.hpp:23