#!/usr/bin/env bash
# Remove driver-inserted silence from a capture, exactly.
#
#   tools/port/strip-padding in.wav out.wav
#
# WHEN THIS IS VALID, AND WHEN IT IS VANDALISM. The distinction is the whole
# tool and getting it backwards destroys the artefact:
#
#   * PulseAudio's monitor SUBSTITUTES silence. It advances on a wall clock and
#     replaces audio that existed when the producer was late. Information is
#     gone; deleting the holes compresses time unevenly and repairs nothing.
#     DO NOT RUN THIS ON A MONITOR CAPTURE.
#   * Xenia's ALSA writer PADS. It inserts silence between samples the guest
#     emitted when its ring is empty (`alsa_audio_driver.cc:359`). Nothing is
#     lost and nothing is overwritten, so removing the padding is EXACT -- it
#     hands back the contiguous stream the guest produced.
#
# CONTROLLED, not argued. A real music+SFX bed (137.37 s, with 454 zero runs of
# its own) had 1 149 holes inserted at 8.37/s to +9.9 % length, matching the
# observed ALSA profile, then was stripped:
#
#   original vs itself          r 1.000  lag  0.0 s  margin +0.141   [ceiling]
#   PADDED   vs original        r 0.436  lag -12.2 s margin +0.006   [destroyed]
#   STRIPPED vs original        r 1.000  lag  0.0 s  margin +0.142   [recovered]
#
# Frame counts: original 6 593 984, stripped 6 559 880, and the original stripped
# of its own genuine zero runs 6 560 044 -- a difference of 164 frames, 3.4 ms in
# 137 s, from inserted holes abutting genuine ones and merging.
#
# ⚠️ It removes GENUINE silence too, and cannot tell the two apart -- that is why
# the reference above is the unstripped original: recovery does not depend on
# stripping both sides. On this material the genuine runs total 0.71 s in 137 s
# and cost nothing measurable. On material that is mostly silence they would.
set -euo pipefail
in="${1:?usage: strip-padding IN.wav OUT.wav}"; out="${2:?usage: strip-padding IN.wav OUT.wav}"
python3 - "$in" "$out" <<'PYEOF'
import array, struct, sys, wave
src, dst = sys.argv[1], sys.argv[2]
w = wave.open(src); ch = w.getnchannels(); rate = w.getframerate()
if w.getsampwidth() != 2:
    print("strip-padding: 16-bit PCM only (got %d-bit)" % (w.getsampwidth()*8)); raise SystemExit(2)
n = w.getnframes(); a = array.array('h'); a.frombytes(w.readframes(n)); w.close()
MIN = max(1, rate // 1000)          # a gap is a run, not a sample
sil = bytearray(n)
for f in range(n):
    b = f * ch
    if not any(a[b+c] for c in range(ch)): sil[f] = 1
keep = array.array('h'); f = 0; removed = 0; holes = 0
while f < n:
    s = f
    if sil[f]:
        while f < n and sil[f]: f += 1
        if f - s < MIN: keep.extend(a[s*ch:f*ch])
        else: removed += f - s; holes += 1
    else:
        while f < n and not sil[f]: f += 1
        keep.extend(a[s*ch:f*ch])
k = len(keep) // ch
o = wave.open(dst + ".partial", "wb")   # temp name, renamed on completion
o.setnchannels(ch); o.setsampwidth(2); o.setframerate(rate)
o.writeframes(keep.tobytes()); o.close()
import os; os.replace(dst + ".partial", dst)
print("%s: %d frames (%.3f s) -> %s: %d frames (%.3f s)"
      % (src, n, n/rate, dst, k, k/rate))
print("  removed %d run(s) totalling %.3f s (%.2f %% of the input)"
      % (holes, removed/rate, 100.0*removed/n))
PYEOF
