L
ling-sys
↓ 0 downloadsSystem activity and performance monitor in the Ling Design System — dials, meshes, drums and crosshatch for CPU, GPU, memory, disk, network, board, processes and audio, with every subsystem given a voice. A LingOS app for rpi5: live board/CPU/memory/process stats from the kernel (stats-lingos.ling).
lingfu install ling-sysCode / Files (13)
sound.ling
# ============================================================
# sound.ling — the machine's voice. Each subsystem hums on its
# own tone slot; discrete activity becomes ticks, pings, wooshes
# and dings. Everything scales with the panel's volume steps and
# falls silent when sound is off.
# slot 0 cpu hum · 1 mem drone · 2 gpu whine · 3 net air ·
# slot 4 board drone
# ============================================================
fn snd_init() {
audio_listener(1.0, 0.0, 1.0, 0.0)
audio_volume(0.5)
return 0
}
fn snd_gain() {
if st_get("p_sound", 1.0) < 1.0 { return 0.0 }
return st_get("p_vol", 2.0) / 4.0
}
# The active tab's voice comes forward; the rest step back.
# kinds: 0 cpu · 1 mem · 2 gpu · 3 net · 4 board · 5 disk · 6 proc
fn snd_emph(kind) {
bind tab = st_get("tab", 0.0)
if tab == 0.0 { return 1.0 }
if tab == 8.0 { return 1.15 }
bind focus = 0.0 - 1.0
if tab == 1.0 { bind focus = 0.0 }
if tab == 2.0 { bind focus = 2.0 }
if tab == 3.0 { bind focus = 1.0 }
if tab == 4.0 { bind focus = 5.0 }
if tab == 5.0 { bind focus = 3.0 }
if tab == 6.0 { bind focus = 4.0 }
if tab == 7.0 { bind focus = 6.0 }
if focus == kind { return 1.9 }
return 0.7
}
# ---------- the two platforms' sound engines ----------
# Windows has audio_blip (one-shots) and a tone mixer scaled for these amplitudes.
# LingOS has audio_tone only, and its mixer scales a voice by amp * 0.37 * master,
# which makes the Windows amplitudes roughly 10 dB too quiet there; its own
# ui_sound uses amp 0.35. So on LingOS: a gain boost, hum pitches lifted out of
# the bass (small TV / HDMI speakers do not reproduce 40-70 Hz), and one-shots
# played on the three spare tone voices (5..7) and switched off by a timer.
# (The kernel's audio_sfx would synthesize and keep a new sample on every call:
# at this tick rate that is a memory leak.)
fn sclamp(x, lo, hi) {
if x < lo { return lo }
if x > hi { return hi }
return x
}
fn snd_boost() {
if st_get("os_win", 1.0) > 0.5 { return 1.0 }
return 14.0
}
fn snd_tone(slot, freq, amp, lr, ld) {
bind k = "ta_" + num_str(slot)
bind cur = st_get(k, 0.0)
bind cur = cur + (amp - cur) * 0.12
st_set(k, cur)
bind f = freq
bind a = cur
if st_get("os_win", 1.0) < 0.5 {
if f < 200.0 { bind f = f * 4.0 }
bind a = sclamp(cur * snd_boost(), 0.0, 0.4)
}
audio_tone(slot, 0.0, 0.0, 1.0, 8.0, f, a, lr, ld)
return 0
}
# a short sound: freq Hz for dur seconds, wave "sine" | "square" | "saw" | "tri" | "noise"
fn snd_blip(freq, dur, wave, amp) {
if st_get("os_win", 1.0) > 0.5 {
audio_blip(freq, dur, wave, amp)
return 0
}
bind slot = 5.0 + st_get("blip_rr", 0.0)
st_set("blip_rr", (st_get("blip_rr", 0.0) + 1.0) % 3.0)
# one-shots are brief and must cut through the hum bed: a larger boost than the hums get
audio_tone(slot, 0.0, 0.0, 1.0, 8.0, freq, sclamp(amp * snd_boost() * 2.2, 0.0, 0.6), 0.0, 0.0, wave)
st_set("blip_off_" + num_str(slot), time_now() + dur)
return 0
}
# switch finished one-shots off (called every frame on LingOS)
fn snd_blip_update(t) {
bind slot = 5.0
while slot < 8.0 {
bind k = "blip_off_" + num_str(slot)
bind off = st_get(k, 0.0)
if off > 0.0 {
if t >= off {
audio_tone(slot, 0.0, 0.0, 1.0, 8.0, 440.0, 0.0, 0.0, 0.0, "sine")
st_set(k, 0.0)
}
}
bind slot = slot + 1.0
}
return 0
}
fn snd_update(t, dt) {
bind g = snd_gain()
audio_volume(0.8 * g)
if st_get("os_win", 1.0) < 0.5 { snd_blip_update(t) }
bind cpu = st_get("d_cpu", 0.0)
bind mpct = st_get("d_mem", 0.0)
bind gpu = st_get("d_gpu", 0.0)
bind fan = st_get("s_gpu_fan", 30.0)
bind rx = st_get("s_net_rx", 0.0)
bind tx = st_get("s_net_tx", 0.0)
bind rd = st_get("s_disk_read", 0.0)
bind wr = st_get("s_disk_write", 0.0)
# ---- hums ----
bind ga = 1.0
if g <= 0.0 { bind ga = 0.0 }
snd_tone(0, 72.0 + cpu * 1.9, ga * snd_emph(0.0) * (0.012 + cpu * 0.00055), 0.3 + cpu * 0.05, 0.012)
snd_tone(1, 46.0 + mpct * 0.5, ga * snd_emph(1.0) * 0.026, 0.11, 0.02)
snd_tone(2, 150.0 + gpu * 5.5, ga * snd_emph(2.0) * (0.006 + gpu * 0.00045), 0.2 + fan * 0.06, 0.006)
bind tp = log10(1.0 + rx + tx)
bind na = tp * 0.006 - 0.012
if na < 0.0 { bind na = 0.0 }
if na > 0.04 { bind na = 0.04 }
snd_tone(3, 380.0 + tp * 55.0, ga * snd_emph(3.0) * na, 2.8, 0.018)
snd_tone(4, 38.0, ga * snd_emph(4.0) * 0.014, 0.07, 0.03)
# ---- cpu tick-tock: the clock speeds up with load ----
bind rate = 0.7 + cpu * 0.062
bind acc = st_get("acc_tick", 0.0) + dt * rate
if acc >= 1.0 {
bind acc = acc - floor(acc)
bind alt = st_get("tick_alt", 0.0)
if g > 0.0 {
bind e = snd_emph(0.0)
if alt < 0.5 {
snd_blip(1180.0, 0.028, "square", 0.022 * g * e)
} else {
snd_blip(760.0, 0.034, "square", 0.019 * g * e)
}
}
st_set("tick_alt", 1.0 - alt)
st_set("ev_tick", 1.0)
st_set("bl_tick", t)
}
st_set("acc_tick", acc)
# ---- disk scratches ----
bind rrate = log10(1.0 + rd) - 4.0
if rrate > 0.0 {
bind racc = st_get("acc_rd", 0.0) + dt * rrate * 1.4
if racc >= 1.0 {
bind racc = racc - floor(racc)
if g > 0.0 { snd_blip(2400.0, 0.022, "noise", 0.016 * g * snd_emph(5.0)) }
st_set("bl_rd", t)
}
st_set("acc_rd", racc)
}
bind wrate = log10(1.0 + wr) - 4.0
if wrate > 0.0 {
bind wacc = st_get("acc_wr", 0.0) + dt * wrate * 1.1
if wacc >= 1.0 {
bind wacc = wacc - floor(wacc)
if g > 0.0 { snd_blip(900.0, 0.035, "noise", 0.014 * g * snd_emph(5.0)) }
st_set("bl_wr", t)
}
st_set("acc_wr", wacc)
}
# ---- net pings in, wooshes out ----
bind rrx = log10(1.0 + rx) - 2.2
if rrx > 0.0 {
bind xacc = st_get("acc_rx", 0.0) + dt * rrx * 0.75
if xacc >= 1.0 {
bind xacc = xacc - floor(xacc)
if g > 0.0 {
bind f = 1540.0 + 260.0 * sin(t * 1.7)
snd_blip(f, 0.05, "sine", 0.026 * g * snd_emph(3.0))
}
st_set("bl_rx", t)
}
st_set("acc_rx", xacc)
}
bind rtx = log10(1.0 + tx) - 2.2
if rtx > 0.0 {
bind yacc = st_get("acc_tx", 0.0) + dt * rtx * 0.6
if yacc >= 1.0 {
bind yacc = yacc - floor(yacc)
if g > 0.0 { snd_blip(320.0, 0.2, "noise", 0.02 * g * snd_emph(3.0)) }
st_set("bl_tx", t)
}
st_set("acc_tx", yacc)
}
# ---- a ding when the process population changes ----
if st_get("ev_proc", 0.0) > 0.0 {
if g > 0.0 {
bind e = snd_emph(6.0)
snd_blip(1318.5, 0.07, "tri", 0.03 * g * e)
snd_blip(1760.0, 0.16, "sine", 0.024 * g * e)
}
st_set("ev_proc", 0.0)
st_set("bl_ding", t)
}
# memory moved (LingOS: free memory changed by 4 MiB or more)
if st_get("ev_mem", 0.0) > 0.0 {
if g > 0.0 { snd_blip(330.0, 0.06, "tri", 0.02 * g * snd_emph(1.0)) }
st_set("ev_mem", 0.0)
}
# page-fault crackle, foregrounded on the MEM tab
if st_get("tab", 0.0) == 3.0 {
bind fr = log10(1.0 + st_get("s_mem_faults", 0.0)) - 2.5
if fr > 0.0 {
bind facc = st_get("acc_flt", 0.0) + dt * fr * 1.6
if facc >= 1.0 {
bind facc = facc - floor(facc)
if g > 0.0 { snd_blip(3100.0, 0.012, "noise", 0.012 * g) }
}
st_set("acc_flt", facc)
}
}
return 0
}
# a soft tup when a packet reaches the hub; idx varies the pitch by key
fn snd_packet(idx) {
bind g = snd_gain()
if g > 0.0 {
snd_blip(236.0 + idx * 64.0, 0.04, "sine", 0.015 * g)
}
return 0
}