Files
StefanandClaude Opus 5 2d3c2fbc6d Vorabprüfung: proot-distro und Rootfs vor jedem Beweis
Aus dem Gerätelauf: S2 lief bis Stufe 1 durch und scheiterte dann an
'proot-distro: cannot execute: required file not found'. Danach meldete die
Auswertung folgerichtig 'S2 gescheitert' — was den Beweis inhaltlich belastet,
obwohl nur die Grundlage fehlte. Genau diese Verwechslung soll der Spike nicht
produzieren.

Alle Beweise prüfen jetzt vorab, ob proot-distro ausführbar ist und das Rootfs
steht, und brechen sonst mit klarer Ansage ab statt mitten im Test aufzulaufen.

Die Meldung selbst ist irreführend: 'required file not found' heißt nicht, dass
proot-distro fehlt — bash hat es gefunden und nennt den vollen Pfad. Es fehlt der
Interpreter aus dessen Shebang-Zeile. Die Prüfung sagt das ausdrücklich und nennt
Datei und Shebang, statt den Suchenden an der falschen Stelle graben zu lassen.
00-bootstrap versucht in dem Fall selbsttätig eine Neuinstallation.

Dazu die Berechtigungsprüfung entschärft: dumpsys verlangt die DUMP-Berechtigung,
die eine gewöhnliche App nicht hat. Das Fehlschlagen ist erwartbar und kein
Warnsignal; die Meldung nennt jetzt den Weg über adb statt Ratlosigkeit zu säen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 18:27:40 +02:00

239 lines
10 KiB
Bash
Executable File

#!/usr/bin/env bash
# Phase 0 · Beweis S2 — Erreicht das Mikrofon die Linux-Seite?
#
# Der stillste K.-o.-Punkt des Projekts. Ohne Mikrofon im Linux gibt es keine
# Videocalls in Element oder im Browser, keine Sprachaufnahme, keine Spracheingabe.
#
# Der Test ist bewusst dreistufig, weil "man hört nichts" drei völlig
# verschiedene Ursachen haben kann und man sie sonst nicht auseinanderhält:
#
# Stufe 1 Wiedergabe — kommt überhaupt Ton aus dem Linux heraus?
# Stufe 2 Aufnahme — liefert die Mikrofonquelle Daten?
# Stufe 3 Gegenhören — ist auf der Aufnahme wirklich Sprache?
#
# Ausführen IN TERMUX: bash spike/02-s2-mikrofon.sh
set -uo pipefail
source "$(dirname "$0")/lib.sh"
require_termux
DISTRO=debian
DAUER="${HPOS_DAUER:-6}"
WAV_NAME="s2-aufnahme.wav"
WAV="$OUT_DIR/$WAV_NAME"
log "HPOS Phase 0 · S2 — Mikrofon im Linux"
log "Protokoll: $LOG"
voraussetzungen_pruefen "$DISTRO" || {
log ""
fail "Abbruch: die Grundlage steht nicht. S2 wäre nicht aussagekräftig."
exit 1
}
# ── Berechtigung ─────────────────────────────────────────────────────────────
# Der häufigste Grund für eine formal korrekte, aber stumme Aufnahme: PulseAudio
# läuft IM Termux-Prozess und öffnet das Mikrofon über OpenSL ES von dort aus.
# Eine Berechtigung, die nur Termux:API hat, nützt ihm deshalb nichts.
section "Mikrofonberechtigung von Termux"
BERECHTIGT="unbekannt"
RECHTE=$(dumpsys package com.termux 2>/dev/null | grep -i 'RECORD_AUDIO' || true)
if [ -n "$RECHTE" ]; then
log "$(printf '%s' "$RECHTE" | sed 's/^/ /')"
case "$RECHTE" in
*granted=true*) BERECHTIGT=ja; ok "com.termux darf aufnehmen" ;;
*granted=false*) BERECHTIGT=nein; fail "com.termux darf NICHT aufnehmen" ;;
esac
else
# Erwartbar: dumpsys verlangt die DUMP-Berechtigung, die eine gewöhnliche App
# nicht hat. Kein Grund zur Sorge, aber der Status bleibt hier eben offen.
warn "Status von hier aus nicht auslesbar (dumpsys ist Apps verwehrt)"
hint "Vom PC prüfen: adb shell dumpsys package com.termux | grep RECORD_AUDIO"
hint "Setzen: adb shell pm grant com.termux android.permission.RECORD_AUDIO"
hint "Oder am Gerät: Einstellungen → Apps → Termux → Berechtigungen → Mikrofon"
fi
if [ "$BERECHTIGT" = nein ]; then
log ""
fail "Das ist mit hoher Wahrscheinlichkeit die Ursache."
hint "Auf dem Telefon: Einstellungen → Apps → Termux → Berechtigungen → Mikrofon"
hint "Oder vom PC: adb shell pm grant com.termux android.permission.RECORD_AUDIO"
hint "Danach dieses Skript erneut ausführen."
log ""
printf ' Trotzdem weitermachen? [j/N] > '
read -r WEITER </dev/tty || WEITER=n
case "${WEITER:-n}" in j|J) : ;; *) exit 1 ;; esac
fi
# Zusätzlich Termux:API antesten — das löst nebenbei den Systemdialog aus.
if have termux-microphone-record; then
try termux-microphone-record -d -l 2 -f "$OUT_DIR/s2-vortest.m4a"
sleep 3
try termux-microphone-record -q
[ -s "$OUT_DIR/s2-vortest.m4a" ] && ok "Termux:API kann aufnehmen" \
|| warn "Termux:API konnte nicht aufnehmen"
fi
# ── PulseAudio ───────────────────────────────────────────────────────────────
section "PulseAudio mit OpenSL-Quelle"
pulseaudio --kill 2>/dev/null || true
sleep 1
if pulseaudio --start --exit-idle-time=-1 \
--load="module-sles-source" \
--load="module-native-protocol-tcp auth-ip-acl=127.0.0.1 auth-anonymous=1" \
>>"$LOG" 2>&1; then
ok "PulseAudio mit module-sles-source gestartet"
else
warn "module-sles-source ließ sich nicht laden — starte ohne"
try pulseaudio --start --exit-idle-time=-1 \
--load="module-native-protocol-tcp auth-ip-acl=127.0.0.1 auth-anonymous=1"
fi
sleep 2
section "Quellen und Standardgeräte"
if have pactl; then
pactl info 2>&1 | grep -Ei 'Default (Source|Sink)' | tee -a "$LOG" | sed 's/^/ /'
log " Quellen:"
pactl list sources short 2>&1 | tee -a "$LOG" | sed 's/^/ /'
# Die Standardquelle ist bei zwei Einträgen gern der Sink-Monitor. Der nimmt
# auf, was ABGESPIELT wird, nicht was am Mikrofon anliegt — eine Aufnahme
# daraus sieht technisch einwandfrei aus und enthält doch nie eine Stimme.
STANDARD=$(pactl info 2>/dev/null | awk -F': ' '/Default Source/{print $2}')
case "$STANDARD" in
*monitor*) warn "Standardquelle ist ein Monitor ($STANDARD) — deshalb wird gleich"
hint "ausdrücklich OpenSL_ES_source gewählt statt der Vorgabe." ;;
esac
fi
# ── Stufe 1: Wiedergabe ──────────────────────────────────────────────────────
section "Stufe 1 — kommt Ton heraus?"
log " Gleich sollte ein kurzer Ton zu hören sein. Lautstärke aufdrehen."
sleep 1
proot-distro login "$DISTRO" --shared-tmp --bind "$REPO_ROOT:/hpos" -- bash -lc '
export PULSE_SERVER=127.0.0.1
command -v sox >/dev/null 2>&1 || apt-get install -y --no-install-recommends sox >/dev/null 2>&1
if command -v sox >/dev/null 2>&1; then
sox -n -r 44100 -c 1 /tmp/ton.wav synth 1.5 sine 440 vol 0.4 2>/dev/null
paplay /tmp/ton.wav && echo " Ton abgespielt."
else
echo " ! sox nicht verfügbar, Wiedergabetest übersprungen"
fi
' 2>&1 | tee -a "$LOG"
printf '\n War der Ton zu hören? [j/N] > '
read -r TON_GEHOERT </dev/tty || TON_GEHOERT=n
case "${TON_GEHOERT:-n}" in
j|J) ok "Wiedergabe funktioniert — ein späteres Schweigen liegt dann an der Aufnahme." ;;
*) warn "Keine Wiedergabe. Dann sagt der Gegenhör-Test unten nichts aus:"
hint "eine gute Aufnahme wäre trotzdem nicht zu hören." ;;
esac
# ── Stufe 2: Aufnahme ────────────────────────────────────────────────────────
section "Stufe 2 — Aufnahme"
log " ${DAUER} Sekunden — bitte jetzt laut und deutlich sprechen."
sleep 1
HPOS_DAUER="$DAUER" HPOS_WAV="/hpos/spike/out/$WAV_NAME" \
proot-distro login "$DISTRO" --shared-tmp --bind "$REPO_ROOT:/hpos" -- bash -lc '
set -uo pipefail
export PULSE_SERVER=127.0.0.1
# Ausdrücklich die Mikrofonquelle wählen. Ohne -d nimmt parecord die
# Standardquelle, und das ist bei geladenem Sink-Modul häufig dessen Monitor.
QUELLE=$(pactl list sources short 2>/dev/null | awk "/OpenSL_ES_source/ {print \$2; exit}")
if [ -z "$QUELLE" ]; then
QUELLE=$(pactl list sources short 2>/dev/null | awk "!/monitor/ {print \$2; exit}")
fi
if [ -n "$QUELLE" ]; then
echo " Aufnahmequelle: $QUELLE"
ARG="-d $QUELLE"
else
echo " ! Keine Nicht-Monitor-Quelle gefunden — nehme die Vorgabe."
ARG=""
fi
timeout $((HPOS_DAUER + 4)) parecord $ARG --channels=1 --rate=16000 \
--file-format=wav "$HPOS_WAV" &
REC=$!
sleep "$HPOS_DAUER"
kill $REC 2>/dev/null
wait $REC 2>/dev/null
echo " Aufnahme beendet."
' 2>&1 | tee -a "$LOG"
# ── Auswertung ───────────────────────────────────────────────────────────────
section "Auswertung"
if [ ! -s "$WAV" ]; then
fail "Keine Aufnahmedatei entstanden → S2 gescheitert"
else
ok "Datei: $WAV ($(stat -c%s "$WAV" 2>/dev/null || echo 0) Bytes)"
if have python; then
python - "$WAV" <<'PY' 2>&1 | tee -a "$LOG"
import array, math, sys, wave
# Bewusst ohne audioop: das Modul ist in Python 3.13 entfernt worden.
try:
with wave.open(sys.argv[1]) as w:
breite = w.getsampwidth()
rate = w.getframerate()
roh = w.readframes(w.getnframes())
if breite == 1:
werte = array.array("b", bytes(b - 128 for b in roh))
elif breite == 2:
werte = array.array("h", roh[: len(roh) // 2 * 2])
else:
raise ValueError(f"{breite} Byte je Abtastwert werden hier nicht ausgewertet")
if not werte:
raise ValueError("keine Abtastwerte enthalten")
pegel = int(math.sqrt(sum(v * v for v in werte) / len(werte)))
spitze = max(abs(v) for v in werte)
print(f" Effektivpegel: {pegel} Spitze: {spitze}")
# Sprache schwankt stark: laute Silben, Pausen dazwischen. Ein konstanter
# Pegel deutet auf Brummen, Rauschen oder ein Störsignal hin — technisch ein
# Signal, aber keine Aufnahme. Deshalb wird die Schwankung mitgemessen.
fenster = max(1, rate // 20)
bloecke = [werte[i:i + fenster] for i in range(0, len(werte), fenster)]
pegel_je_block = [
int(math.sqrt(sum(v * v for v in b) / len(b))) for b in bloecke if b
]
if len(pegel_je_block) > 4:
leise = min(pegel_je_block)
laut = max(pegel_je_block)
print(f" Leiseste Stelle: {leise} Lauteste: {laut}")
if spitze < 40:
print(" ✗ Praktisch Stille — die Quelle liefert keine Daten.")
elif laut > 4 * max(leise, 1):
print(" ✓ Deutliche Schwankung — sieht nach Sprache aus.")
else:
print(" ! Signal ohne Schwankung — eher Rauschen oder Brummen als Sprache.")
print(" Möglicherweise wurde ein Monitor statt des Mikrofons aufgenommen.")
elif spitze < 40:
print(" ✗ Praktisch Stille — die Quelle liefert keine Daten.")
else:
print(" ✓ Signal vorhanden.")
except Exception as e:
print(f" ! Datei nicht auswertbar: {e}")
PY
fi
# ── Stufe 3: Gegenhören ─────────────────────────────────────────────────
# Der einzige Test, der wirklich zählt. Kein Messwert ersetzt das Ohr.
section "Stufe 3 — Gegenhören"
log " Die Aufnahme wird jetzt abgespielt."
sleep 1
HPOS_WAV="/hpos/spike/out/$WAV_NAME" \
proot-distro login "$DISTRO" --shared-tmp --bind "$REPO_ROOT:/hpos" -- bash -lc '
export PULSE_SERVER=127.0.0.1
paplay "$HPOS_WAV" && echo " Wiedergabe beendet."
' 2>&1 | tee -a "$LOG"
fi
log ""
log " Berechtigung com.termux: $BERECHTIGT"
log " Ton vorher gehört: ${TON_GEHOERT:-n}"
verdict "S2" "War auf der Wiedergabe deine Stimme zu hören?
Nur das zählt. Ein Messwert ohne hörbare Sprache ist ein Fehlschlag."