From d16a454ec1bb511e9f12fe300bc2807e79e0b05f Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Fri, 12 Jun 2026 16:07:57 +0200 Subject: [PATCH 1/2] Add AudioClipDump debugging utility (dump audio buffers to WAV) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extracted from the microphone investigation: snapshots an AudioClip's raw contents to a 16-bit PCM WAV (plus a generic float[]-to-WAV writer and a delayed-dump coroutine helper) so buffer contents can be inspected offline. A raw dump like this was what cracked the macOS + Bluetooth HFP capture bug: it revealed FMOD writing valid 320-sample fragments at a 1024-sample stride with exact-zero padding, while GetPosition advanced 3.2x faster than the data rate — something no amount of reasoning about API values could show. Kept as a permanent internal tool for future audio debugging. Co-Authored-By: Claude Fable 5 --- Runtime/Scripts/Internal/AudioClipDump.cs | 78 +++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 Runtime/Scripts/Internal/AudioClipDump.cs diff --git a/Runtime/Scripts/Internal/AudioClipDump.cs b/Runtime/Scripts/Internal/AudioClipDump.cs new file mode 100644 index 00000000..626bbce8 --- /dev/null +++ b/Runtime/Scripts/Internal/AudioClipDump.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using System.IO; +using UnityEngine; + +namespace LiveKit.Internal +{ + /// + /// Debugging utility for dumping audio buffers to WAV files so they can be inspected offline + /// (in an audio editor, or analyzed programmatically). + /// + /// + /// This proved decisive when diagnosing microphone capture issues: a raw dump of the mic clip + /// on macOS with a Bluetooth HFP headset revealed that FMOD writes valid 320-sample audio + /// fragments at a 1024-sample stride with exact-zero padding between them, while + /// Microphone.GetPosition advances ~3.2x faster than the data rate. Inspecting the actual + /// buffer contents settles questions that API values (clip.frequency, GetPosition) cannot. + /// + internal static class AudioClipDump + { + /// + /// Snapshots the full contents of a clip to a 16-bit PCM WAV file and returns the path. + /// For looping microphone clips, call a few seconds after capture started so the ring + /// buffer contains audio, and produce sound continuously while it fills. + /// + public static string DumpClip(AudioClip clip, string fileName = "lk_clip_dump.wav") + { + var data = new float[clip.samples * clip.channels]; + clip.GetData(data, 0); + var path = Path.Combine(Application.temporaryCachePath, fileName); + WriteWav(path, data, clip.channels, clip.frequency); + Utils.Info($"AudioClipDump: wrote {path} ({clip.samples} frames @ {clip.frequency}Hz/{clip.channels}ch)"); + return path; + } + + /// + /// Coroutine that waits, then dumps the clip. Convenient to start alongside capture: + /// MonoBehaviourContext.RunCoroutine(AudioClipDump.DumpClipAfter(clip, 4f)); + /// + public static IEnumerator DumpClipAfter(AudioClip clip, float delaySeconds, string fileName = "lk_clip_dump.wav") + { + yield return new WaitForSeconds(delaySeconds); + if (clip == null) yield break; + try + { + DumpClip(clip, fileName); + } + catch (Exception e) + { + Utils.Warning($"AudioClipDump: dump failed: {e.Message}"); + } + } + + /// + /// Writes interleaved float samples as a 16-bit PCM WAV file. + /// + public static void WriteWav(string path, float[] samples, int channels, int sampleRate) + { + using var fs = new FileStream(path, FileMode.Create); + using var w = new BinaryWriter(fs); + int dataBytes = samples.Length * 2; + w.Write(System.Text.Encoding.ASCII.GetBytes("RIFF")); + w.Write(36 + dataBytes); + w.Write(System.Text.Encoding.ASCII.GetBytes("WAVEfmt ")); + w.Write(16); + w.Write((short)1); // PCM + w.Write((short)channels); + w.Write(sampleRate); + w.Write(sampleRate * channels * 2); + w.Write((short)(channels * 2)); // block align + w.Write((short)16); // bits per sample + w.Write(System.Text.Encoding.ASCII.GetBytes("data")); + w.Write(dataBytes); + foreach (var s in samples) + w.Write((short)(Mathf.Clamp(s, -1f, 1f) * 32767f)); + } + } +} From 17d2aa205e55788f5a810b042eb9974cb5e2913b Mon Sep 17 00:00:00 2001 From: Max Heimbrock <43608204+MaxHeimbrock@users.noreply.github.com> Date: Fri, 12 Jun 2026 17:23:09 +0200 Subject: [PATCH 2/2] Add Unity meta file for AudioClipDump Required for stable GUIDs when the package is imported. Co-Authored-By: Claude Fable 5 --- Runtime/Scripts/Internal/AudioClipDump.cs.meta | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Runtime/Scripts/Internal/AudioClipDump.cs.meta diff --git a/Runtime/Scripts/Internal/AudioClipDump.cs.meta b/Runtime/Scripts/Internal/AudioClipDump.cs.meta new file mode 100644 index 00000000..26a9ff0c --- /dev/null +++ b/Runtime/Scripts/Internal/AudioClipDump.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 366ae3d162f2460fa2de6a859cafc508 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: