first commit

This commit is contained in:
m.cirafisi committed 2026-11-03 23:07:33 +01:00
commit 4b1fc0835a
14 files changed
+1133

No files matched your search

+111
View File
@@ -0,0 +1,111 @@
class_name SystemAudioAnalyzer
extends Node
## Emette il valore RMS continuo [0.0 - 1.0] esclusivo dell'audio di sistema/applicazione
signal volume_updated(rms_value: float)
## Emette il BPM rilevato in tempo reale dalla musica
signal bpm_updated(bpm: float)
## Emette la lista delle applicazioni audio attive (es. Spotify, Chrome, Discord)
signal applications_updated(apps: Array[Dictionary])
@export var udp_port: int = 4242
@export var cmd_port: int = 4243
@export_group("Gain & Sensitivity")
@export var system_audio_gain: float = 3.8
@export var system_noise_floor: float = 0.0003
var _udp_peer: PacketPeerUDP
var _udp_send: PacketPeerUDP
var _loopback_pid: int = -1
var _loopback_rms: float = 0.0
var _last_bpm: float = 0.0
var _watchdog_timer: float = 0.0
func _ready() -> void:
_init_native_wasapi_loopback()
func _init_native_wasapi_loopback() -> void:
_udp_peer = PacketPeerUDP.new()
var err = _udp_peer.bind(udp_port, "*")
if err != OK:
err = _udp_peer.bind(udp_port, "127.0.0.1")
_udp_send = PacketPeerUDP.new()
_udp_send.set_dest_address("127.0.0.1", cmd_port)
if err == OK:
var exe_path = ProjectSettings.globalize_path("res://bin/LoopbackCapture.exe")
if FileAccess.file_exists("res://bin/LoopbackCapture.exe"):
_loopback_pid = OS.create_process(exe_path, [str(udp_port)], false)
## Invia al bridge la richiesta di ascoltare SOLO l'applicazione con il PID specificato (0 = Tutto)
func select_application_pid(pid: int) -> void:
if _udp_send:
var msg = "PID:%d" % pid
_udp_send.put_packet(msg.to_utf8_buffer())
print("[SystemAudioAnalyzer] Applicazione audio selezionata PID: ", pid)
func _process(delta: float) -> void:
# Watchdog: se LoopbackCapture non è in esecuzione, riavvialo automaticamente
_watchdog_timer += delta
if _watchdog_timer >= 2.0:
_watchdog_timer = 0.0
if _loopback_pid <= 0 or not OS.is_process_running(_loopback_pid):
var exe_path = ProjectSettings.globalize_path("res://bin/LoopbackCapture.exe")
if FileAccess.file_exists("res://bin/LoopbackCapture.exe"):
_loopback_pid = OS.create_process(exe_path, [str(udp_port)], false)
# Lettura esclusiva dell'Audio di Sistema o dell'Applicazione selezionata (Zero Microfono)
if _udp_peer:
var packet_received: bool = false
while _udp_peer.get_available_packet_count() > 0:
var packet: PackedByteArray = _udp_peer.get_packet()
# Pacchetto con la lista delle applicazioni attive (APPS:...)
if packet.size() > 5 and packet[0] == 65 and packet[1] == 80 and packet[2] == 80 and packet[3] == 83 and packet[4] == 58:
var text: String = packet.get_string_from_utf8()
_parse_applications_list(text)
continue
# Pacchetto audio standard: 8 byte (4 byte RMS + 4 byte BPM)
if packet.size() >= 4:
_loopback_rms = packet.decode_float(0)
packet_received = true
if packet.size() >= 8:
var incoming_bpm: float = packet.decode_float(4)
if incoming_bpm > 50.0:
_last_bpm = incoming_bpm
bpm_updated.emit(incoming_bpm)
if not packet_received:
_loopback_rms = move_toward(_loopback_rms, 0.0, delta * 3.0)
# Calcolo Volume Percepito (Curva Loudness)
var processed_rms: float = 0.0
if _loopback_rms > system_noise_floor:
processed_rms = clampf(sqrt(_loopback_rms) * system_audio_gain, 0.0, 1.0)
volume_updated.emit(processed_rms)
func _parse_applications_list(raw_text: String) -> void:
var payload = raw_text.substr(5)
var tokens = payload.split(";")
var apps: Array[Dictionary] = []
for token in tokens:
var parts = token.split(":")
if parts.size() >= 2:
var pid = int(parts[0])
var name = parts[1]
apps.append({"pid": pid, "name": name})
if not apps.is_empty():
applications_updated.emit(apps)
func _exit_tree() -> void:
if _loopback_pid > 0:
OS.kill(_loopback_pid)
if _udp_peer:
_udp_peer.close()
if _udp_send:
_udp_send.close()