灵符lingfu
L

ling-sys

↓ 0 downloads

System 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).

Windows · x86-64macOS · Intel x86-64Linux · x86-64LingOS · x86-64LingOS · Pi 5 ARM64Web · WASM
lingfu install ling-sys
Authorsling-lin
READMECode / FilesVersionsDependency GraphDependentsSecurityBuildsSettings

Code / 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
}

← Back to index