From 16a1e23fcc53cb5f10419bdffd355a22b205a772 Mon Sep 17 00:00:00 2001 From: mrwnslz Date: Sun, 4 Oct 2026 00:01:03 +0200 Subject: [PATCH] =?UTF-8?q?Ambient:=20Ton=20auf=20Android=20=C3=BCber=20de?= =?UTF-8?q?n=20normalen=20Medienweg;=20dioras=20Lautst=C3=A4rke=20gilt=20f?= =?UTF-8?q?=C3=BCr=20Drift;=20sw=20v77?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drift: AudioContext mit latencyHint 'playback' – der Niedriglatenz-Weg (AAudio/MMAP) blieb auf dem Samsung des Nutzers stumm, obwohl Drift lief - Drift: cmd 'volume' (0..1) von der Seite ist die Lautstärke; eingebettet ist der eigene Regler ausgeblendet - app.js: setVolume() und das Laden des Frames schicken die Lautstärke an Drift (nicht in der Android-App: dort gelten die Lautstärketasten) Co-Authored-By: Claude Opus 5.5 --- static/ambient/index.html | 16 ++++++++++++---- static/js/app.js | 22 +++++++++++++++++++--- static/js/sw.js | 2 +- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/static/ambient/index.html b/static/ambient/index.html index 2d22510..1aa359b 100644 --- a/static/ambient/index.html +++ b/static/ambient/index.html @@ -16,6 +16,7 @@ overflow-x:hidden; overflow-y:auto; } .embed header { display:none; } .embed #share { display:none; } + .embed .m-vol { display:none; } /* eingebettet regelt die Seite die Lautstärke (cmd "volume") */ .embed .state { padding-right:58px; } /* oben rechts liegt in diora ein Knopf */ .noise .m-music, .noise .meters { display:none !important; } html:not(.noise) .m-noise { display:none; } @@ -83,7 +84,7 @@
- + @@ -174,6 +175,8 @@ const S = { ring: null, wp: 0, written: 0, grainBuf: null, }; let rafId = 0; // Zeichenschleife (siehe wake/draw) +S.hostVol = null; // Lautstärke der einbettenden Seite (cmd "volume"), sonst gilt der eigene Regler +const outVol = () => S.hostVol != null ? S.hostVol : +$("vol").value; const speed = () => 0.35 + S.drift * 2.2; // Spannungsbogen: 0 = ruhig, 1 = Höhepunkt, über mehrere Minuten @@ -216,7 +219,8 @@ function satCurve(k) { } function buildGraph() { - const ctx = S.ctx = new (window.AudioContext || window.webkitAudioContext)(); + // "playback": the normal media path (on Android the low-latency one can stay silent, e.g. on Samsung) + const ctx = S.ctx = new (window.AudioContext || window.webkitAudioContext)({ latencyHint: "playback" }); const g = S.g = {}; g.voiceBus = ctx.createGain(); @@ -1030,6 +1034,10 @@ addEventListener("message", e => { if (d.cmd === "start" && !S.running) start(); else if (d.cmd === "stop" && S.running) stop(); else if (d.cmd === "mode" && MODES[d.mode]) setMode(d.mode); + else if (d.cmd === "volume" && isFinite(d.value)) { // die einbettende Seite (diora) gibt die Lautstärke vor + S.hostVol = clamp(+d.value, 0, 1); + if (S.running) S.g.master.gain.setTargetAtTime(outVol(), S.ctx.currentTime, .1); + } }); function startMusic() { @@ -1053,7 +1061,7 @@ function start() { S.ctx.resume().then(() => { if (S.running && S.ctx.state !== "running") stop(); }, () => { if (S.running) stop(); }); const t = S.ctx.currentTime; S.g.master.gain.cancelScheduledValues(t); - S.g.master.gain.setTargetAtTime(+$("vol").value, t, 2.5); + S.g.master.gain.setTargetAtTime(outVol(), t, 2.5); S.tickId = setInterval(tick, 1000); enterMode(); $("toggle").textContent = "Stopp"; @@ -1121,7 +1129,7 @@ function updateInfo() { $("preset").innerHTML = Object.keys(PRESETS).map(n => ``).join(""); $("toggle").onclick = () => S.running ? stop() : start(); -$("vol").oninput = e => { if (S.running) S.g.master.gain.setTargetAtTime(+e.target.value, S.ctx.currentTime, .1); }; +$("vol").oninput = () => { if (S.running) S.g.master.gain.setTargetAtTime(outVol(), S.ctx.currentTime, .1); }; $("density").oninput = e => S.density = +e.target.value; $("drift").oninput = e => S.drift = +e.target.value; $("arc").oninput = e => S.arcDepth = +e.target.value; diff --git a/static/js/app.js b/static/js/app.js index 35ba6f4..c0e06b5 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -350,6 +350,7 @@ function setVolume(val) { const numInput = $('volume-num'); if (slider) slider.value = val; if (numInput) numInput.value = val; + _ambientVolume(val); } const volSliderEl = document.getElementById('volume'); @@ -1253,7 +1254,11 @@ function _ambientFrame() { function _ambientLoad() { const f = _ambientFrame(); if (!f) return; - if (!f.getAttribute('src')) f.src = f.dataset.src; + if (!f.getAttribute('src')) { + // Drift takes the page's volume before its first sound. + f.addEventListener('load', () => _ambientVolume()); + f.src = f.dataset.src; + } requestAnimationFrame(_ambientSize); } @@ -1269,9 +1274,9 @@ function _ambientSize() { window.addEventListener('resize', () => requestAnimationFrame(_ambientSize)); // A command for Drift ('start' | 'stop'): the app's notification, the sleep timer. -window.__dioraAmbientCmd = function (cmd) { +window.__dioraAmbientCmd = function (cmd, value) { const f = _ambientFrame(); - if (f && f.contentWindow && f.getAttribute('src')) f.contentWindow.postMessage({type: 'ambient', cmd}, '*'); + if (f && f.contentWindow && f.getAttribute('src')) f.contentWindow.postMessage({type: 'ambient', cmd, value}, '*'); }; // The radio or a podcast starts: Ambient stops. @@ -1280,6 +1285,17 @@ window.__dioraAmbientStop = function () { }; window.__dioraAmbientPlaying = () => _ambientPlaying; +// The page's volume (0–255, the bar's slider) is Drift's too; its own slider hides when +// embedded. Not in the Android app: there the volume keys set it, as for everything. +function _ambientVolume(val) { + if (window.DioraNative && DioraNative.ambientState) return; + if (val == null) { + const s = $('volume'); + val = s ? +s.value : +(localStorage.getItem('diora_volume') || 102); + } + window.__dioraAmbientCmd && window.__dioraAmbientCmd('volume', val / 255); +} + window.addEventListener('message', e => { const f = _ambientFrame(); const d = e.data; diff --git a/static/js/sw.js b/static/js/sw.js index 3542001..8c43d6f 100644 --- a/static/js/sw.js +++ b/static/js/sw.js @@ -2,7 +2,7 @@ * diora service worker — caches the app shell for offline use. */ -const CACHE = 'diora-v76'; +const CACHE = 'diora-v77'; const SHELL = [ '/static/css/app.css', '/static/js/app.js',