Anruf: der Lautsprecher -- und warum es kein Freisprech-Knopf ist
Filipe: "die funktion lautsprecher fehlt." Er ist das Gegenstueck zum Mikrofon: "Mikro aus" heisst, die anderen hoeren MICH nicht -- "Lautsprecher aus" heisst, ICH hoere sie nicht. Beides braucht man, aus verschiedenen Gruenden: das Mikro, wenn es bei einem selbst laut ist; den Lautsprecher, wenn nebenher etwas laeuft oder jemand ins Zimmer kommt. WAS BEWUSST NICHT GEBAUT WURDE: der Umschalter zwischen Hoermuschel und Freisprechen, den ein Telefon hat. Recherchiert statt geraten -- den gibt es im Browser nicht: Auf dem iPhone entscheidet Safari selbst, wohin der Ton geht, und Android laesst einzelne Toene gar nicht auf ein anderes Geraet legen (setSinkId ist dort nicht verfuegbar, weil die Plattform es nicht hergibt). Ein Knopf, der dort nichts tut, waere schlimmer als keiner -- man drueckt ihn und sucht den Fehler dann bei sich. Wo es GEHT (Rechner mit Chrome, Edge, Safari), steht dafuer eine echte Geraeteauswahl daneben. Sie erscheint nur, wenn es mehr als ein Geraet gibt UND die Namen bekannt sind; eine Auswahl mit einem Eintrag ist keine Auswahl, sondern eine Behauptung. DIE PRUEFUNG, AUF DIE ES ANKOMMT: Kommt jemand dazu oder geht jemand, werden die Toenelemente NEU GEBAUT -- und neue Elemente wissen nichts von einem Schalter, der vorher umgelegt wurde. Genau daran scheitern solche Knoepfe sonst: Sie wirken, bis sich die Runde aendert, und dann hoert man ploetzlich wieder mit. Gegenprobe (Anwenden nach dem Neuzeichnen ausgebaut) macht genau diese eine Pruefung rot, waehrend alle anderen gruen bleiben. Geprueft: pruef-anruf 73 -> 80, im echten Dreiergespraech im Browser. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -1598,6 +1598,22 @@
|
||||
}
|
||||
.anruf__knopf--auf:hover { border-color: var(--warn, #ff9aa2); }
|
||||
|
||||
/* Wohin der Ton geht -- nur auf Rechnern, wo der Browser das wirklich
|
||||
umstellen kann. Schmal gehalten: Geraetenamen wie „Kopfhoerer
|
||||
(RODECaster Duo Main)" wuerden die Knopfreihe sonst sprengen. */
|
||||
.anruf__ausgabe {
|
||||
max-width: 11rem;
|
||||
min-height: 40px;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid var(--rand-hell);
|
||||
border-radius: 999px;
|
||||
background: color-mix(in srgb, var(--tinte) 88%, #ffffff 12%);
|
||||
color: var(--text);
|
||||
font: inherit; font-size: .8rem;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.anruf__ausgabe:focus-visible { outline: 2px solid var(--akzent); outline-offset: 2px; }
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
ES KLINGELT
|
||||
|
||||
|
||||
@@ -118,12 +118,15 @@
|
||||
<div class="anruf__knoepfe">
|
||||
<button type="button" class="anruf__knopf anruf__knopf--ton" id="anruf-ton"
|
||||
aria-pressed="true">Mikro an</button>
|
||||
<button type="button" class="anruf__knopf anruf__knopf--hoeren" id="anruf-hoeren"
|
||||
aria-pressed="true">Lautsprecher an</button>
|
||||
<button type="button" class="anruf__knopf anruf__knopf--video" id="anruf-video"
|
||||
aria-pressed="false" hidden>Kamera an</button>
|
||||
<button type="button" class="anruf__knopf anruf__knopf--auf" id="anruf-auflegen">Auflegen</button>
|
||||
</div>`;
|
||||
document.body.append(k);
|
||||
$('anruf-ton').addEventListener('click', tonSchalten);
|
||||
$('anruf-hoeren').addEventListener('click', hoerenSchalten);
|
||||
$('anruf-video').addEventListener('click', videoSchalten);
|
||||
$('anruf-auflegen').addEventListener('click', () => auflegen(true));
|
||||
return k;
|
||||
@@ -380,6 +383,11 @@
|
||||
}
|
||||
void wer;
|
||||
}
|
||||
/* Der Zustand gilt auch fuer die Elemente, die es gerade eben noch
|
||||
nicht gab -- und die Geraeteauswahl kann erst hier entstehen,
|
||||
weil die Geraetenamen die Mikrofonerlaubnis voraussetzen. */
|
||||
hoerenAnwenden();
|
||||
ausgabeAnbieten();
|
||||
}
|
||||
|
||||
let uhr = null;
|
||||
@@ -404,6 +412,106 @@
|
||||
k.setAttribute('aria-pressed', String(!an));
|
||||
}
|
||||
|
||||
/* ---------- DER LAUTSPRECHER (18.09.2026) ------------------------------
|
||||
|
||||
Filipe: „die funktion lautsprecher fehlt."
|
||||
|
||||
Er ist das Gegenstueck zum Mikrofon: „Mikro aus" heisst, die
|
||||
anderen hoeren MICH nicht -- „Lautsprecher aus" heisst, ICH hoere
|
||||
sie nicht. Beides braucht man, aus verschiedenen Gruenden: Das
|
||||
Mikro macht man aus, wenn es bei einem selbst laut ist; den
|
||||
Lautsprecher, wenn nebenher etwas anderes laeuft oder jemand
|
||||
ins Zimmer kommt.
|
||||
|
||||
WAS HIER BEWUSST NICHT STEHT: ein Umschalter zwischen Hoermuschel
|
||||
und Freisprechen, wie ihn ein Telefon hat. Den gibt es im Browser
|
||||
nicht. Auf dem iPhone entscheidet Safari selbst, wohin der Ton
|
||||
geht; auf Android laesst das Betriebssystem einzelne Toene gar
|
||||
nicht auf ein anderes Geraet legen. Ein Knopf, der dort nichts
|
||||
tut, waere schlimmer als keiner -- man drueckt ihn und sucht den
|
||||
Fehler dann bei sich.
|
||||
|
||||
Wo es GEHT (Rechner mit Chrome, Edge oder Safari), steht dafuer
|
||||
eine echte Geraeteauswahl daneben -- und nur dort. */
|
||||
|
||||
/** Hoeren wir die anderen? Steht ausserhalb von `anruf`: Wer den Ton
|
||||
* abgestellt hat, will ihn nicht beim naechsten Gespraech wieder
|
||||
* an. */
|
||||
let hoeren = true;
|
||||
|
||||
/** Auf welches Geraet der Ton geht. Leer = das des Systems. */
|
||||
let ausgabe = '';
|
||||
|
||||
/** Setzt Stummschaltung und Geraet auf ALLE Toenelemente.
|
||||
*
|
||||
* Muss nach jedem `leuteZeichnen()` laufen: Das baut die Elemente
|
||||
* neu, und neue Elemente wissen nichts von einer Einstellung, die
|
||||
* vorher getroffen wurde. Genau daran scheitern solche Schalter
|
||||
* sonst -- sie wirken, bis der Naechste dazukommt. */
|
||||
function hoerenAnwenden() {
|
||||
const ziel = $('anruf-leute');
|
||||
if (!ziel) return;
|
||||
for (const m of ziel.querySelectorAll('audio, video')) {
|
||||
/* Das eigene Bild bleibt stumm, immer. Sich selbst zu hoeren ist
|
||||
ein Echo, das jedes Gespraech unbrauchbar macht. */
|
||||
if (m.classList.contains('anruf__bild--ich')) continue;
|
||||
m.muted = !hoeren;
|
||||
if (ausgabe && typeof m.setSinkId === 'function') {
|
||||
m.setSinkId(ausgabe).catch(() => { /* Geraet weg -> Standardgeraet */ });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hoerenSchalten() {
|
||||
hoeren = !hoeren;
|
||||
const k = $('anruf-hoeren');
|
||||
if (k) {
|
||||
k.textContent = hoeren ? 'Lautsprecher an' : 'Lautsprecher aus';
|
||||
k.setAttribute('aria-pressed', String(hoeren));
|
||||
}
|
||||
hoerenAnwenden();
|
||||
}
|
||||
|
||||
/** Baut die Geraeteauswahl -- oder eben nicht.
|
||||
*
|
||||
* SIE ERSCHEINT NUR, WENN ES ETWAS ZU WAEHLEN GIBT: wenn der Browser
|
||||
* `setSinkId` kann UND mehr als ein Ausgabegeraet da ist. Eine
|
||||
* Auswahl mit einem einzigen Eintrag ist keine Auswahl, sondern eine
|
||||
* Behauptung -- und auf dem Handy waere sie eine falsche. */
|
||||
async function ausgabeAnbieten() {
|
||||
const kasten = $('anruf');
|
||||
if (!kasten || $('anruf-ausgabe')) return;
|
||||
if (typeof document.createElement('audio').setSinkId !== 'function') return;
|
||||
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) return;
|
||||
let geraete = [];
|
||||
try {
|
||||
geraete = (await navigator.mediaDevices.enumerateDevices())
|
||||
.filter((g) => g.kind === 'audiooutput');
|
||||
} catch { return; }
|
||||
/* Namen gibt es erst, seit das Mikrofon erlaubt wurde -- im Anruf
|
||||
ist das der Fall. Ohne Namen waere die Liste eine Reihe von
|
||||
Nummern, und niemand wuesste, was er waehlt. */
|
||||
if (geraete.length < 2 || !geraete.some((g) => g.label)) return;
|
||||
|
||||
const w = el('select', 'anruf__ausgabe');
|
||||
w.id = 'anruf-ausgabe';
|
||||
w.setAttribute('aria-label', 'Wohin der Ton geht');
|
||||
for (const g of geraete) {
|
||||
const o = document.createElement('option');
|
||||
o.value = g.deviceId;
|
||||
o.textContent = g.label || 'Gerät';
|
||||
w.append(o);
|
||||
}
|
||||
w.value = ausgabe || geraete[0].deviceId;
|
||||
w.addEventListener('change', () => { ausgabe = w.value; hoerenAnwenden(); });
|
||||
const reihe = kasten.querySelector('.anruf__knoepfe');
|
||||
/* VOR „Auflegen", nicht dahinter: Der Auflegen-Knopf sitzt per
|
||||
`margin-inline-start: auto` ganz rechts. Wer danach anhaengt,
|
||||
schiebt die Auswahl hinter den einzigen Knopf, der immer an
|
||||
derselben Stelle stehen soll. */
|
||||
if (reihe) reihe.insertBefore(w, reihe.querySelector('.anruf__knopf--auf'));
|
||||
}
|
||||
|
||||
function videoSchalten() {
|
||||
if (!anruf) return;
|
||||
const an = anruf.eigener.getVideoTracks().some((s) => s.enabled);
|
||||
|
||||
Reference in New Issue
Block a user