Ambient: Ton auf Android über den normalen Medienweg; dioras Lautstärke gilt für Drift; sw v77
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
3ff998f9cf
commit
16a1e23fcc
3 changed files with 32 additions and 8 deletions
|
|
@ -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 @@
|
|||
<label class="m-music">Tuning
|
||||
<select id="tuning"><option value="just">Rein</option><option value="equal">Gleichstufig</option></select></label>
|
||||
<div class="sep"></div>
|
||||
<label>Lautstärke <input type="range" id="vol" min="0" max="1" step="0.01" value="0.7"></label>
|
||||
<label class="m-vol">Lautstärke <input type="range" id="vol" min="0" max="1" step="0.01" value="0.7"></label>
|
||||
<label class="m-noise"><span id="lla">A</span> <input type="range" id="la" min="0" max="1" step="0.01" value="0.5"></label>
|
||||
<label class="m-noise"><span id="llb">B</span> <input type="range" id="lb" min="0" max="1" step="0.01" value="0.5"></label>
|
||||
<label class="m-music">Dichte <input type="range" id="density" min="0" max="1" step="0.01" value="0.5"></label>
|
||||
|
|
@ -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 => `<option>${n}</option>`).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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue