I Added a Local Audio Compressor to My Browser Extension

від

у

Я додав локальний аудіо-компресор до мого розширення для браузера

https://ift.tt/wc1BsUm

Я нещодавно додав нову можливість до мого розширення для браузера KoalaSync: локальний аудіо-компресор для браузерних відео.

Він все ще тримається експериментальним і, ймовірно, з часом змінюватиметься, але я хотів зафіксувати, що саме я побудував, чому і що виявилось більш дратівливим, ніж очікував.

KoalaSync переважно є розширенням для перегляду разом із друзями. Воно синхронізує відтворення, паузу та перемотування між людьми, які дивляться відео разом у браузері.

Але новий компресор корисний навіть коли ви дивитесь самі, бо сучасний звук у фільмах може бути абсурдним.

Мінімально діалог може бути настільки тихим, що я збільшую гучність.
Півхвилини потому вибух намагається вбити мої навушники.

Я здебільшого дивлюсь фільми з мого сервера Emby разом із друзями, і мені набридло постійно регулювати гучність. У VLC аудіо-компресія вже давно є. Деякі телевізори мають «нічний режим». Але в браузері зазвичай отримуєш лише сирий аудіо-мікс із елемента відео.

Тому я захотів подивитися, чи можу побудувати щось подібне безпосередньо всередині розширення.

Ідея в основі
Браузер уже має те, що потрібно: API Web Audio.

Більш конкретно, DynamicsCompressorNode.

Усе це виконується локально у браузері, без окремої аудіослужби.

Проста версія виглядає приблизно так:

const ctx = new AudioContext();
const src = ctx.createMediaElementSource(videoElement);
const compressor = ctx.createDynamicsCompressor();

compressor.threshold.value = -24;
compressor.ratio.value = 8;
compressor.knee.value = 15;
compressor.attack.value = 0.010;
compressor.release.value = 0.300;

src.connect(compressor);
compressor.connect(ctx.destination);

Це бере аудіо з елемента відео, пропускає його через компресор і надсилає до динаміків.

Ця частина чесно кажучи не була складною.

Неспокійна частина: увімкнення без потряскувань
Сам вузол компресора легкий.

Зробити це зручним всередині розширення було більш дратівливим.

Я не хотів, щоб аудіо стрибало під час увімкнення або вимкнення компресора. Жорстке перемикання між оригінальним сигналом і стиснутим сигналом може звучати дуже погано, особливо коли щось уже відбувається у відтворенні.

Тож я використав схему dry/wet:

– dry шлях: оригінальне аудіо
– wet шлях: стиснуте аудіо

Коли компресор увімкнено, сухий сигнал зникає, і стислий сигнал поступово з’являється. Коли вимкнено — відбувається те саме у зворотному напрямку.

Фактична реалізація обгортає це у маленький допоміжний елемент rampGain, який спочатку скасовує заплановані значення:

function rampGain(node, value, t) {
const current = node.gain.value;
node.gain.cancelScheduledValues(t);
node.gain.setValueAtTime(current, t);
node.gain.linearRampToValueAtTime(value, t + 0.04);
}

Це всього 40 мс підйому, але робить перемикач більш плавним.

Поточні аудіо-ланцюги для кожного відео
Інша справа: на сторінках може бути більше одного елемента відео.

І елементи відео можуть зникати, коли:
– ви переходите між сторінками
– завантажується новий епізод
– плеєр перерисовується
– сайт замінює один медіа-елемент на інший

Тому кожному елементу відео надається свій аудіо-ланцюг.

Я зберігаю ці ланцюги у WeakMap, щоб коли елемент відео зникає, браузер також міг очистити пов’язану ланцюг.

const audioChains = new WeakMap();

function setupAudioChain(videoEl) {
if (audioChains.has(videoEl)) return audioChains.get(videoEl);

// … створюємо source, compressor, dryGain, compGain

const chain = { compressor, dryGain, compGain, active: false };
audioChains.set(videoEl, chain);

return chain;
}

Повна реалізація setupAudioChain також створює вузли dry/wet та підключає все.

Це один із тих випадків, коли ідея проста, але браузерні сторінки настільки заплутані, що потребує додаткової обережності.

Пресети замість лише повзунків
Я не хотів, щоб користувачі відразу бачили п’ять аудіопараметрів після увімкнення функції.

Тому додав кілька пресетів:

| Пресет | Threshold | Ratio | Attack | Release | Knee |
|—|—|—|—|—|—|
| Recommended | -24 dB | 8:1 | 10 ms | 300 ms | 15 dB |
| Dynamic Range | -18 dB | 4:1 | 20 ms | 200 ms | 10 dB |
| Vocal Enhancement | -12 dB | 3:1 | 15 ms | 150 ms | 5 dB |
| Smooth | -30 dB | 1.5:1 | 30 ms | 250 ms | 20 dB |

За замовчуванням зараз використовується «Recommended».

Це не має бути досконалим. Просто намагається зробити діалог легшим для розрізнення, не знищуючи звук дій у сценах.

Також є режим з власними параметрами, де можна ручно налаштувати threshold, ratio, attack, release та knee.

Переміщення налаштувань із попапа
Це також був перший випадок, коли я додав повноцінну внутрішню сторінку розширення до KoalaSync.

Раніше більшість налаштувань зберігались у попапі розширення. Це працює добре для дрібних перемикачів, але швидко стає незручним, якщо хочеш слайдери, пресети, пояснення та можливо інші аудіоінструменти пізніше.

Тому UI обробки аудіо тепер відкривається на окремій повній вкладці за адресою audio-options.html.

Це дає мені набагато більше простору для роботи й залишає місце для майбутніх функцій, таких як еквалайзер або інші аудіо-процеси.

Попап залишається придатним для швидких дій.
Але для будь-чого, що потребує фактичного налаштування, повна сторінка набагато краще.

Це був один із тих «малих функцій» змін, які тихо перетворилися на рішення користувальницького інтерфейсу.

Де це працює
Коротко: найкраще працює тоді, коли KoalaSync може отримати доступ до елемента відео сторінки.

Це охоплює багато звичайного відтворення в браузері: самохостингові медіасервери, багато відео-сайтів та деякі великі стрімінгові платформи.

Є й крайні випадки. Деякі сайти використовують незвичні налаштування плеєра, суворе DRM, вбудоване відтворення або поведінку браузера, яка запобігає аудіо обробці.

Коли це трапляється, KoalaSync просто залишає аудіо незмінним, замість того щоб примушувати щось.

Чи закінчена ця можливість?
Не зовсім.

Вона працює, і мені вже корисна, але я все ще вважаю її експериментальною.

Що можу змінити або додати пізніше:
– кращі пресети
– поведінка per-site
– підтримка еквалайзера
– кращі пояснення в UI
– можливо простий перемикач «нічний режим» для тих, кому не потрібні налаштування компресора

Поки що основне — це вирішити неприємну проблему «тихий діалог, гучні вибухи» без потреби в іншому додатку або зовнішньому аудіо-інструменті.

Посилання
KoalaSync безкоштовний та з відкритим кодом.
– Вебсайт: https://ift.tt/nxKZd5y
– GitHub: https://ift.tt/J8xNVW1

Відповідні частини здебільшого у extension/content.js та extension/audio-options.html, якщо хочеш подивитися реалізацію.

Мені цікаво, чи хтось інший раніше будував функції аудіообробки всередині браузерних розширень. Web Audio API досить гарний, коли працює, але крайні випадки браузера/плеєра — це точно річ.

June 10, 2026 at 07:13PM


Коментарі

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *