diff --git a/AudioLink/AudioLinkBeatDetector.cs b/AudioLink/AudioLinkBeatDetector.cs new file mode 100644 index 0000000..95085a0 --- /dev/null +++ b/AudioLink/AudioLinkBeatDetector.cs @@ -0,0 +1,64 @@ +using UdonSharp; +using UnityEngine; +using AudioLink; + +public class AudioLinkBeatDetector : UdonSharpBehaviour +{ + public AudioLink.AudioLink audioLinkInstance; + + [Header("Settings")] + [Range(0, 1)] public float threshold = 0.5f; + public float minBpm = 70f; + public float maxBpm = 210f; // Alles darüber wird als Fehler ignoriert + + [Header("Output")] + public float bpm = 128f; + public float instantBpm = 128f; + public bool isBeat; + + private float lastBeatTime; + + void Update() + { + if (audioLinkInstance == null || !audioLinkInstance.AudioDataIsAvailable()) return; + + Vector2 bassPos = AudioLink.AudioLink.GetALPassAudioBass(); + Vector4 data = audioLinkInstance.GetDataAtPixel((int)bassPos.x, (int)bassPos.y); + float currentLevel = data.x; + + // Prüfen auf Threshold + if (currentLevel > threshold) + { + float currentTime = Time.time; + float timeDiff = currentTime - lastBeatTime; + + // Der "Debounce" Check: + // 0.27s entspricht ca. 222 BPM. Alles was schneller kommt, ist Rauschen. + if (timeDiff > 0.27f) + { + if (lastBeatTime > 0) + { + float detected = 60f / timeDiff; + + // Nur plausible Werte übernehmen + if (detected >= minBpm && detected <= maxBpm) + { + instantBpm = detected; + // Glättung: 0.5 sorgt für schnelles Folgen, aber filtert Ausreißer + bpm = Mathf.Lerp(bpm, instantBpm, 0.5f); + } + } + lastBeatTime = currentTime; + isBeat = true; + } + else + { + isBeat = false; + } + } + else + { + isBeat = false; + } + } +} \ No newline at end of file diff --git a/AudioLink/BeatDetectorDisplay.cs b/AudioLink/BeatDetectorDisplay.cs new file mode 100644 index 0000000..8c0e1b6 --- /dev/null +++ b/AudioLink/BeatDetectorDisplay.cs @@ -0,0 +1,43 @@ +using UdonSharp; +using UnityEngine; +using UnityEngine.UI; +using TMPro; + +public class BeatDetectorDisplay : UdonSharpBehaviour +{ + public AudioLinkBeatDetector engine; + public TextMeshProUGUI bpmText; + public Image beatIndicator; + public Color activeColor = Color.cyan; + + private float flashTimer; + + void Update() + { + if (engine == null) return; + + // Wenn die Engine einen Beat erkennt... + if (engine.isBeat) + { + // ...aktualisieren wir SOFORT den Text mit der instantBpm + if (bpmText != null) + { + bpmText.text = engine.instantBpm.ToString("F0"); // "F0" für ganze Zahlen ohne Lag-Gefühl + } + + // Visueller Kick + flashTimer = 0.1f; + if (beatIndicator != null) beatIndicator.color = activeColor; + } + + // Timer für das Abklingen der LED + if (flashTimer > 0) + { + flashTimer -= Time.deltaTime; + if (flashTimer <= 0 && beatIndicator != null) + { + beatIndicator.color = new Color(0.1f, 0.1f, 0.1f, 1f); + } + } + } +} \ No newline at end of file