add onnx and metadata
This commit is contained in:
1 parent
98d9fa24cf
commit
5f3f915258
18 files changed
+2229
-78
No files matched your search
@@ -29,6 +29,8 @@ var _silence_timer: float = 0.0
|
||||
var _current_rms: float = 0.0
|
||||
var _current_genre: String = ""
|
||||
var _current_genre_conf: float = 0.0
|
||||
var _current_genre_source: String = "rules"
|
||||
var _current_genre_detail: String = ""
|
||||
|
||||
# BPM dinamico: parte da 0 finché la canzone non viene agganciata
|
||||
var _current_bpm: float = 0.0
|
||||
@@ -59,9 +61,11 @@ func _ready() -> void:
|
||||
|
||||
_set_state(State.IDLE)
|
||||
|
||||
func _on_genre_updated(genre: String, confidence: float, _probs: Dictionary) -> void:
|
||||
func _on_genre_updated(genre: String, confidence: float, _probs: Dictionary, source: String, detail: String) -> void:
|
||||
_current_genre = genre
|
||||
_current_genre_conf = confidence
|
||||
_current_genre_source = source
|
||||
_current_genre_detail = detail
|
||||
|
||||
func _on_applications_updated(apps: Array[Dictionary]) -> void:
|
||||
if not app_selector:
|
||||
@@ -216,7 +220,11 @@ func _update_debug_ui() -> void:
|
||||
|
||||
var genre_str: String = "Genere: --"
|
||||
if not _current_genre.is_empty() and _current_genre != "unknown":
|
||||
genre_str = "%s (%d%%)" % [_current_genre, roundi(_current_genre_conf * 100.0)]
|
||||
# Con i metadati mostriamo il genere specifico del catalogo ("Hard Rock"),
|
||||
# che e' piu' informativo della classe grossolana usata dal gameplay.
|
||||
var shown: String = _current_genre_detail if not _current_genre_detail.is_empty() else _current_genre
|
||||
var source_label: String = "meta" if _current_genre_source == "metadata" else _current_genre_source
|
||||
genre_str = "%s (%d%%) [%s]" % [shown, roundi(_current_genre_conf * 100.0), source_label]
|
||||
debug_label.text = "Stato: %s | Volume: %.2f | %s | %s" % [
|
||||
State.keys()[current_state],
|
||||
_current_rms,
|
||||
|
||||
@@ -7,13 +7,14 @@ signal volume_updated(rms_value: float)
|
||||
signal bpm_updated(bpm: float)
|
||||
## Emette la lista delle applicazioni audio attive (es. Spotify, Chrome, Discord)
|
||||
signal applications_updated(apps: Array[Dictionary])
|
||||
signal genre_updated(genre: String, confidence: float, probs: Dictionary)
|
||||
signal genre_updated(genre: String, confidence: float, probs: Dictionary, source: String, detail: String)
|
||||
|
||||
@export var udp_port: int = 4242
|
||||
@export var cmd_port: int = 4243
|
||||
@export var heartbeat_timeout_ms: int = 1500
|
||||
@export var lease_enabled: bool = true
|
||||
@export var genre_enabled: bool = true
|
||||
@export var diag_enabled: bool = true
|
||||
|
||||
const LEASE_MS: int = 10000
|
||||
const GENRE_LABELS = ["latin", "electronic", "hiphop", "rock", "classical"]
|
||||
@@ -44,6 +45,9 @@ var _last_ping_msec: int = 0
|
||||
var _genre_probs: Dictionary = {}
|
||||
var _genre_argmax: String = ""
|
||||
var _genre_last_emit_msec: int = -5000
|
||||
var _genre_source: String = ""
|
||||
var _genre_last_packet_msec: int = -1
|
||||
var _diag_last_status: String = ""
|
||||
|
||||
func _ready() -> void:
|
||||
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
@@ -70,6 +74,10 @@ func _spawn_bridge() -> void:
|
||||
while _udp_peer.get_available_packet_count() > 0:
|
||||
_udp_peer.get_packet()
|
||||
_bridge_protocol = 0
|
||||
if _genre_source == "metadata":
|
||||
genre_updated.emit("unknown", 0.0, {}, "rules", "")
|
||||
_genre_source = ""
|
||||
_genre_last_packet_msec = -1
|
||||
_genre_probs.clear()
|
||||
_genre_argmax = ""
|
||||
_genre_last_emit_msec = -5000
|
||||
@@ -180,6 +188,12 @@ func _process(delta: float) -> void:
|
||||
_parse_genre(packet.get_string_from_utf8())
|
||||
continue
|
||||
|
||||
# Pacchetto diagnostico per-sorgente (DIAG:...). Riconosciuto PRIMA del ramo
|
||||
# binario e ignorato ai fini del genere.
|
||||
if packet.size() >= 5 and packet[0] == 68 and packet[1] == 73 and packet[2] == 65 and packet[3] == 71 and packet[4] == 58:
|
||||
_parse_diag(packet.get_string_from_utf8())
|
||||
continue
|
||||
|
||||
# Convenzione: le future estensioni testuali non devono MAI essere lunghe
|
||||
# esattamente 8 byte (messaggio completo, non solo prefisso).
|
||||
# HELLO:/HB:/APPS:/GENR: sono riconosciuti sopra; PING: e i futuri STATUS:/SILENT:
|
||||
@@ -202,6 +216,14 @@ func _process(delta: float) -> void:
|
||||
if not packet_received:
|
||||
_loopback_rms = move_toward(_loopback_rms, 0.0, delta * 3.0)
|
||||
|
||||
# A stalled metadata stream must not leave a permanent 100% label on screen.
|
||||
if _genre_source == "metadata" and _genre_last_packet_msec >= 0 and Time.get_ticks_msec() - _genre_last_packet_msec > 10000:
|
||||
_genre_probs.clear()
|
||||
_genre_argmax = ""
|
||||
_genre_source = "rules"
|
||||
_genre_last_packet_msec = -1
|
||||
genre_updated.emit("unknown", 0.0, {}, "rules", "")
|
||||
|
||||
# Read queued heartbeats before checking the watchdog to avoid false timeouts.
|
||||
_watchdog_timer += delta
|
||||
if _watchdog_timer >= 0.5:
|
||||
@@ -223,13 +245,31 @@ func _parse_genre(raw_text: String) -> void:
|
||||
var fields: PackedStringArray = raw_text.substr(5).split(";")
|
||||
if fields.size() < 8:
|
||||
return
|
||||
# Version, raw label and raw confidence precede the five probabilities.
|
||||
# The displayed label/confidence are recomputed from the smoothed distribution.
|
||||
var source: String = "rules"
|
||||
var first_probability: int = 3
|
||||
if fields[0] == "2":
|
||||
if fields.size() < 9 or not ["metadata", "onnx", "rules"].has(fields[3]):
|
||||
return
|
||||
source = fields[3]
|
||||
first_probability = 4
|
||||
elif fields[0] != "1":
|
||||
return
|
||||
if source == "metadata" and (not GENRE_LABELS.has(fields[1]) or not fields[2].is_valid_float() or fields[2].to_float() != 1.0):
|
||||
return
|
||||
var incoming: Dictionary = {}
|
||||
var total: float = 0.0
|
||||
for i in range(3, fields.size()):
|
||||
var pair: PackedStringArray = fields[i].split("=")
|
||||
if pair.size() != 2 or not GENRE_LABELS.has(pair[0]):
|
||||
var detail: String = ""
|
||||
for i in range(first_probability, fields.size()):
|
||||
# maxsplit=1: un valore puo' contenere '=' senza rompere il parsing.
|
||||
var pair: PackedStringArray = fields[i].split("=", true, 1)
|
||||
if pair.size() != 2:
|
||||
continue
|
||||
# Genere specifico dal catalogo ("Hard Rock", "Hip-Hop/Rap"): solo descrittivo,
|
||||
# non partecipa alle probabilita' e non entra nella media esponenziale.
|
||||
if pair[0] == "detail":
|
||||
detail = pair[1].strip_edges()
|
||||
continue
|
||||
if not GENRE_LABELS.has(pair[0]):
|
||||
continue
|
||||
if incoming.has(pair[0]) or not pair[1].is_valid_float():
|
||||
return
|
||||
@@ -240,7 +280,13 @@ func _parse_genre(raw_text: String) -> void:
|
||||
total += probability
|
||||
if incoming.size() != GENRE_LABELS.size() or total <= 0.0:
|
||||
return
|
||||
var first: bool = _genre_probs.is_empty()
|
||||
if source == "metadata":
|
||||
# Discrete lookup: require a coherent one-hot distribution, never EMA-blend it.
|
||||
for genre in GENRE_LABELS:
|
||||
if float(incoming[genre]) != (1.0 if genre == fields[1] else 0.0):
|
||||
return
|
||||
var source_changed: bool = source != _genre_source
|
||||
var first: bool = _genre_probs.is_empty() or source_changed or source == "metadata"
|
||||
var best: String = ""
|
||||
var confidence: float = -1.0
|
||||
for genre in GENRE_LABELS:
|
||||
@@ -253,12 +299,42 @@ func _parse_genre(raw_text: String) -> void:
|
||||
confidence = float(_genre_probs[genre])
|
||||
best = genre
|
||||
var label: String = best if confidence >= GENRE_MIN_CONFIDENCE else "unknown"
|
||||
if source != "metadata":
|
||||
detail = ""
|
||||
var now: int = Time.get_ticks_msec()
|
||||
if best != _genre_argmax or now - _genre_last_emit_msec >= 5000:
|
||||
_genre_last_packet_msec = now
|
||||
_genre_source = source
|
||||
if source_changed or best != _genre_argmax or now - _genre_last_emit_msec >= 5000:
|
||||
_genre_argmax = best
|
||||
_genre_last_emit_msec = now
|
||||
print("[SystemAudioAnalyzer] Genere: %s (%.0f%%)" % [label, confidence * 100.0])
|
||||
genre_updated.emit(label, confidence, _genre_probs.duplicate())
|
||||
var shown: String = detail if not detail.is_empty() else label
|
||||
print("[SystemAudioAnalyzer] Genere: %s (%.0f%%) [%s]" % [shown, confidence * 100.0, source])
|
||||
genre_updated.emit(label, confidence, _genre_probs.duplicate(), source, detail)
|
||||
|
||||
## Stampa una riga compatta dello stato per-sorgente solo quando uno dei campi di
|
||||
## stato cambia (non a ogni pacchetto). Il contenuto DIAG non influenza il genere.
|
||||
func _parse_diag(raw_text: String) -> void:
|
||||
if not diag_enabled:
|
||||
return
|
||||
var fields: PackedStringArray = raw_text.substr(5).split(";")
|
||||
if fields.size() < 2:
|
||||
return
|
||||
var diag: Dictionary = {}
|
||||
for i in range(1, fields.size()):
|
||||
var pair: PackedStringArray = fields[i].split("=", true, 1)
|
||||
if pair.size() != 2:
|
||||
continue
|
||||
diag[pair[0]] = pair[1]
|
||||
var meta: String = diag.get("meta", "")
|
||||
var onnx: String = diag.get("onnx", "")
|
||||
var itunes: String = diag.get("itunes", "")
|
||||
var deezer: String = diag.get("deezer", "")
|
||||
var bpm: String = diag.get("bpm", "0.0")
|
||||
var anchor: String = diag.get("anchor", "0.0")
|
||||
var status_key: String = "%s|%s|%s|%s" % [meta, onnx, itunes, deezer]
|
||||
if status_key != _diag_last_status:
|
||||
_diag_last_status = status_key
|
||||
print("[DIAG] meta=%s onnx=%s itunes=%s deezer=%s bpm=%s anchor=%s" % [meta, onnx, itunes, deezer, bpm, anchor])
|
||||
|
||||
func _parse_applications_list(raw_text: String) -> void:
|
||||
var payload = raw_text.substr(5)
|
||||
|
||||
Reference in new issue
Block a user