Initial Commit

This commit is contained in:
media
2026-06-14 14:16:10 -05:00
commit ab9824a71f
3 changed files with 146 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
import time
import numpy as np
import sounddevice as sd
import pyloudnorm as pyln
import paho.mqtt.client as mqtt
# MQTT
MQTT_HOST = "10.40.0.2"
MQTT_PORT = 1883
TOPIC_M = "stream/audio/lufs/momentary"
TOPIC_S = "stream/audio/lufs/shortterm"
TOPIC_I = "stream/audio/lufs/integrated"
DEVICE = "default"
# Audio
RATE = 48000
CHANNELS = 2
def find_scarlett():
for i, d in enumerate(sd.query_devices()):
name = d["name"].lower()
if "scarlett" in name and d["max_input_channels"] >= 2:
return i
raise RuntimeError("Scarlett stereo input not found")
# Windows
M_WINDOW = 0.4
S_WINDOW = 3.0
UPDATE = 0.25
meter = pyln.Meter(RATE)
integrated_buffer = np.array([], dtype=np.float32)
shortterm_buffer = np.array([], dtype=np.float32)
RESET_TOPIC = "stream/audio/lufs/reset"
reset_requested = False
def on_message(client, userdata, msg):
global reset_requested
if msg.topic == RESET_TOPIC:
print("Resetting integrated LUFS")
reset_requested = True
client = mqtt.Client()
client.on_message = on_message
client.connect(MQTT_HOST, MQTT_PORT)
client.subscribe(RESET_TOPIC)
client.loop_start()
print("LUFS → MQTT running")
while True:
try:
samples = sd.rec(
int(UPDATE * RATE),
samplerate=RATE,
channels=CHANNELS,
device=DEVICE,
dtype="float32",
)
sd.wait()
except Exception as e:
print(f"Audio route changed / device unavailable: {e}")
time.sleep(2)
continue
mono = samples.flatten()
if reset_requested:
integrated_buffer = np.array([], dtype=np.float32)
reset_requested = False
integrated_buffer = np.concatenate(
[integrated_buffer, mono]
)[-RATE * 60 * 120:]
shortterm_buffer = np.concatenate(
[shortterm_buffer, mono]
)[-int(S_WINDOW * RATE):]
try:
m_samples = shortterm_buffer[
-int(M_WINDOW * RATE):
]
momentary = meter.integrated_loudness(
m_samples
)
shortterm = meter.integrated_loudness(
shortterm_buffer
)
integrated = meter.integrated_loudness(
integrated_buffer
)
client.publish(
TOPIC_M,
f"{momentary:.1f}",
retain=True
)
client.publish(
TOPIC_S,
f"{shortterm:.1f}",
retain=True
)
client.publish(
TOPIC_I,
f"{integrated:.1f}",
retain=True
)
print(
f"\rM {momentary:.1f} "
f"S {shortterm:.1f} "
f"I {integrated:.1f}",
end=""
)
except Exception:
pass