snmp-midi#
Polls server metrics via SNMP and plays them as MIDI notes. Each stat maps to an instrument on its own channel — pitch tracks the value, velocity tracks intensity. The result is an ambient, real-time sonic portrait of a running server.
Features#
- Maps CPU, memory, disk, network, and process stats to MIDI instruments
- Per-core CPU load and multi-timescale load averages (1min / 5min / 15min)
- Configurable scales, octaves, note durations, and velocity ranges
- Cascade stagger: voices roll in sequentially each poll cycle
- Optional threshold triggers for alert notes
- Works with hardware synths, virtual ports, or any MIDI-capable DAW
- Roland GS reset on startup for hardware synth initialization
Requirements#
- Python 3.8+
- SNMP-enabled server (
snmpdwith UCD-SNMP-MIB and HOST-RESOURCES-MIB) - MIDI output: hardware synth, software synth (FluidSynth), or DAW
Installation#
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
Quick Start#
# Copy example config and edit with your server details
cp config.example.yaml config.yaml
$EDITOR config.yaml
# List available MIDI ports
./main.py -l
# Run (port number from -l output)
./main.py -P 0
Usage#
./main.py [options]
-c FILE Config file (default: config.yaml)
-g Write default config to FILE and exit
-l List MIDI ports and exit
-H HOST SNMP host (overrides config)
-p PORT SNMP port (overrides config)
-C STR SNMP community string (overrides config)
-P NUM MIDI port by index number
-m NAME MIDI port by name
-v Use virtual MIDI port
-i SECS Poll interval in seconds (overrides config)
Configuration#
Copy config.example.yaml to config.yaml and edit. The config.yaml file is gitignored so credentials stay local.
Available stat types#
stat_name |
stat_key |
Description |
|---|---|---|
cpu_load |
— | 1-minute load average |
cpu_load_5min |
— | 5-minute load average |
cpu_load_15min |
— | 15-minute load average |
per_core_cpu_load |
core0, core1, … coreN |
Per-core CPU % (hrProcessorLoad) |
memory_usage |
percent, total, available, cached |
Memory stats (UCD-SNMP-MIB) |
disk_usage |
percent, size, used |
First mounted filesystem |
network_stats |
in_bytes, out_bytes |
Cumulative octets (IF-MIB) |
process_count |
— | hrSystemProcesses |
Scales#
major · minor · pentatonic_major · pentatonic_minor · blues · dorian · mixolydian · chromatic
Mapping fields#
mappings:
- stat_name: cpu_load # stat type (see table above)
stat_key: null # sub-key within stat, if applicable
channel: 0 # MIDI channel 0–15
program: 73 # GM program number 0–127
scale: pentatonic_major # musical scale
root: C # root note
octave: 5 # base octave
min_value: 0 # value mapped to lowest scale note
max_value: 10 # value mapped to highest scale note
note_duration: 1.0 # seconds (must be < poll_interval)
cc_number: null # also send value as CC, or null
enabled: true
Threshold triggers#
triggers:
- stat_name: cpu_load
threshold: 8.0
above: true
note: 72
velocity: 127
channel: 0
duration: 0.5
Timing#
note_duration must be less than poll_interval or note-off threads from earlier polls will cancel newer notes before slow-attack instruments sound. Use onboard reverb to blur the gaps between notes.
Setting Up snmpd#
sudo apt-get install snmpd snmp-mibs-downloader
/etc/snmp/snmpd.conf minimal additions:
rocommunity public 127.0.0.1
rocommunity public 192.168.0.0/16
sudo systemctl restart snmpd
# Test
snmpwalk -v2c -c public localhost 1.3.6.1.2.1.1.1.0
Connecting a Synthesizer#
Hardware synth: connect via USB-MIDI or DIN, then ./main.py -l to find the port index.
FluidSynth:
sudo apt-get install fluidsynth fluid-soundfont-gm
fluidsynth -a alsa -m alsa_seq /usr/share/sounds/sf2/FluidR3_GM.sf2 &
./main.py -l
./main.py -P <port number>
DAW: create a virtual MIDI port in your DAW, then ./main.py -m "port name".