Lydfilerne var op til 17 dB højere end speakeren (kategori-lyden -4 LUFS mod speakerens -21). Alle effekter er nu normaliseret til -20 LUFS og musikken til -23 LUFS. Speakeren går gennem sin egen kompressor med et løft, og mens den taler, dykker effekterne og musikken, og musikken får skåret taleområdet ned. En limiter til sidst forhindrer klipning. Claude-Session: https://claude.ai/code/session_01JmhJG6JnhXfzNiKv8f2vHu
This commit is contained in:
@@ -382,14 +382,52 @@
|
||||
g.gain.setValueAtTime(0.0001, t);
|
||||
g.gain.exponentialRampToValueAtTime(vol || 0.18, t + 0.015);
|
||||
g.gain.exponentialRampToValueAtTime(0.0001, t + dur);
|
||||
o.connect(g); g.connect(actx.destination);
|
||||
o.connect(g); g.connect(mix.build() ? mix.graph.sfxBus : actx.destination);
|
||||
o.start(t); o.stop(t + dur + 0.05);
|
||||
},
|
||||
play: function (kind) { WPP.sfx(kind); } /* gammelt navn */
|
||||
});
|
||||
/* ---------- mixeren ----------
|
||||
Alt går gennem en master-limiter. Speakeren har sin egen kæde (kompressor + løft), så den står klart;
|
||||
effekter og musik har hver sin bus, som dukker sig, mens speakeren taler (se mix.duck). */
|
||||
var mix = (WPP.mix = {
|
||||
graph: null,
|
||||
VOICE_GAIN: 1.6, /* ≈ +4 dB: speakerens replikker (ca. −21 LUFS) løftes over resten */
|
||||
SFX_DUCK: 0.35, /* effekter under tale: ≈ −9 dB */
|
||||
EQ_DUCK_DB: -9, /* musikkens taleområde (2,6 kHz) skæres ned under tale */
|
||||
build: function () {
|
||||
if (mix.graph || !actx) return mix.graph;
|
||||
var limiter = actx.createDynamicsCompressor();
|
||||
limiter.threshold.value = -3; limiter.knee.value = 0; limiter.ratio.value = 20;
|
||||
limiter.attack.value = 0.002; limiter.release.value = 0.2;
|
||||
limiter.connect(actx.destination);
|
||||
var sfxBus = actx.createGain(); sfxBus.connect(limiter);
|
||||
var musicEq = actx.createBiquadFilter();
|
||||
musicEq.type = 'peaking'; musicEq.frequency.value = 2600; musicEq.Q.value = 0.9; musicEq.gain.value = 0;
|
||||
musicEq.connect(limiter);
|
||||
var voiceComp = actx.createDynamicsCompressor();
|
||||
voiceComp.threshold.value = -28; voiceComp.knee.value = 8; voiceComp.ratio.value = 4;
|
||||
voiceComp.attack.value = 0.004; voiceComp.release.value = 0.2;
|
||||
var voiceGain = actx.createGain(); voiceGain.gain.value = mix.VOICE_GAIN;
|
||||
voiceComp.connect(voiceGain); voiceGain.connect(limiter);
|
||||
mix.graph = { limiter: limiter, sfxBus: sfxBus, musicEq: musicEq, voiceIn: voiceComp };
|
||||
return mix.graph;
|
||||
},
|
||||
/* speakeren taler: dyk effekter og musik hurtigt, slip langsomt */
|
||||
duck: function (on) {
|
||||
var g = mix.build();
|
||||
if (!g) return;
|
||||
var t = actx.currentTime, tc = on ? 0.05 : 0.35;
|
||||
[[g.sfxBus.gain, on ? mix.SFX_DUCK : 1], [g.musicEq.gain, on ? mix.EQ_DUCK_DB : 0]].forEach(function (x) {
|
||||
x[0].cancelScheduledValues(t); x[0].setValueAtTime(x[0].value, t); x[0].setTargetAtTime(x[1], t, tc);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var wasReady = false;
|
||||
function onReady() {
|
||||
if (!sound.ready()) return;
|
||||
mix.build();
|
||||
Object.keys(raw).forEach(decode);
|
||||
music._kick();
|
||||
if (!wasReady) { wasReady = true; if (sound.onchange) sound.onchange(); }
|
||||
@@ -453,12 +491,11 @@
|
||||
}
|
||||
function playBuf(b, vol, opts) {
|
||||
var delay = opts.at != null ? Math.max(0, opts.at - actx.currentTime) : (opts.delay || 0);
|
||||
if (voice.active) vol *= 0.6; /* speakeren skal kunne høres */
|
||||
var src = actx.createBufferSource(), g = actx.createGain();
|
||||
src.buffer = b;
|
||||
if (opts.rate) src.playbackRate.value = opts.rate;
|
||||
g.gain.value = vol;
|
||||
src.connect(g); g.connect(actx.destination);
|
||||
src.connect(g); g.connect(opts.direct || !mix.build() ? actx.destination : mix.graph.sfxBus);
|
||||
src.start(actx.currentTime + delay);
|
||||
return delay + b.duration / (opts.rate || 1);
|
||||
}
|
||||
@@ -536,7 +573,8 @@
|
||||
var FADE = 0.8;
|
||||
var MUSIC_GAIN = { music_reveal: 0.45 }; /* afsløringen: effekterne er i fokus */
|
||||
var music = (WPP.music = {
|
||||
name: null, volume: 0.18, duckVolume: 0.05, ducked: false, cur: null, bus: null, _fade: FADE, _gain: 1,
|
||||
/* filerne er normaliseret til −23 LUFS: 0.5 ≈ −29 LUFS, dukket 0.14 ≈ −40 LUFS (+ EQ-hak i taleområdet) */
|
||||
name: null, volume: 0.5, duckVolume: 0.14, ducked: false, cur: null, bus: null, _fade: FADE, _gain: 1,
|
||||
/* WPP.music.set('music_lobby') | WPP.music.set(null, {fade: 0.25}) – fade = udtoning af det gamle nummer (s) */
|
||||
set: function (name, opts) {
|
||||
opts = opts || {};
|
||||
@@ -554,15 +592,15 @@
|
||||
var g = music.bus.gain, t = actx.currentTime;
|
||||
g.cancelScheduledValues(t);
|
||||
g.setValueAtTime(g.value, t);
|
||||
g.setTargetAtTime(music._target(), t, music.ducked ? 0.12 : 0.35);
|
||||
g.setTargetAtTime(music._target(), t, music.ducked ? 0.05 : 0.4);
|
||||
},
|
||||
/* dukker sig straks, men slipper først efter en kort pause (ingen pumpen mellem to replikker) */
|
||||
duck: function (on) {
|
||||
on = !!on;
|
||||
clearTimeout(music._duckT);
|
||||
if (on) { if (!music.ducked) { music.ducked = true; music._apply(); } return; }
|
||||
if (on) { if (!music.ducked) { music.ducked = true; music._apply(); mix.duck(true); } return; }
|
||||
if (!music.ducked) return;
|
||||
music._duckT = setTimeout(function () { music.ducked = false; music._apply(); }, 700);
|
||||
music._duckT = setTimeout(function () { music.ducked = false; music._apply(); mix.duck(false); }, 500);
|
||||
},
|
||||
/* skift til music.name, hvis lyden er klar */
|
||||
_kick: function () {
|
||||
@@ -570,7 +608,7 @@
|
||||
if (!music.bus) {
|
||||
music.bus = actx.createGain();
|
||||
music.bus.gain.value = music._target();
|
||||
music.bus.connect(actx.destination);
|
||||
music.bus.connect(mix.build() ? mix.graph.musicEq : actx.destination);
|
||||
}
|
||||
var cur = music.cur, name = music.name, t = actx.currentTime;
|
||||
if (cur && cur.name === name) return;
|
||||
@@ -639,6 +677,10 @@
|
||||
var a = new Audio(x.url);
|
||||
voice.current = a;
|
||||
voice._active(true);
|
||||
if (sound.ready() && mix.build()) {
|
||||
/* gennem kompressor + løft; ellers (lyd ikke låst op endnu) spilles den direkte */
|
||||
try { actx.createMediaElementSource(a).connect(mix.graph.voiceIn); } catch (e) { /* */ }
|
||||
}
|
||||
a.onended = a.onerror = function () { if (voice.current === a) voice._next(); };
|
||||
var p = a.play();
|
||||
if (p && p.catch) p.catch(function (e) {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user