- BrainTools - https://www.braintools.ru -
Тезисно
getUserMedia({audio:true}) включает подавление шума, настроенное на речь; работающий двигатель для него тот шум, который надо вычесть(sic!). Выключил три флага — доля тишины в записях упала с 36% до 2%.
На iOS та же правка сработала наоборот: выключение обработки роняет весь тракт на 18 дБ — сигнал, пик и уровень шума одинаково.
Дело не в цене телефона и не в версии Android, а в модели устройства: разброс между 170 моделями от 0% до 100% тишины на одном и том же коде.
Я делаю пет-проект автоухо.рф [1] : человек записывает мотор на телефон, модель называет вероятную зону неисправности. В прошлый раз [2] я разбирался, почему модель ставит «поломка» в 71% случаев, и выяснил неприятное: она реагирует не на неисправность, а на параметры записи. Тихие записи с большой долей тишины почти всегда получали «поломка».
Кто именно делает записи тихими, тогда осталось непонятным. Гипотезу про дешёвые телефоны я проверил и отверг: внутри одного квартиля громкости вендоры не различались. Эта статья про то, как искал виновника ещё две недели и трижды менял мнение.
Был сделан анализ корпуса записей не по вендорам, а по контейнеру файла. Контейнер выдаёт тракт записи: webm пишет MediaRecorder в Chrome на Android, m4a приходит с айфонов, wav получается, когда человек загружает готовый файл и браузер декодирует его сам.
|
Контейнер |
Откуда |
n |
Громкость (медиана) |
Тишина |
Вердикт «поломка» |
|---|---|---|---|---|---|
|
webm/Opus |
Android, MediaRecorder |
539 |
−38,6 дБ |
70% |
84% |
|
m4a |
айфоны |
116 |
−24,8 дБ |
0% |
57% |
|
wav |
загрузка файлом |
209 |
−22,3 дБ |
1% |
47% |
Пятнадцать децибел разницы в медианной громкости и семьдесят процентных пунктов разницы в доле тишины. Десятый перцентиль громкости у webm это минус шестьдесят четыре с половиной децибела, получается каждая десятая андроидная запись практически неслышна.
Первая мысль была, что файлы битые. Проверил, но нет. Opus в такой записи кодирует 122 кбит/с, столько на тишину не тратят. volumedetect на записи с Tecno даёт средний уровень −64,9 дБ при пике −30,2, у соседнего айфона в том же корпусе −26,7 и −12,6. Звук есть, он просто в сто раз жиже по амплитуде.
Гипотезу про обработку легко проверить: Chromium умеет подставлять вместо микрофона файл.
chromium
--use-fake-device-for-media-stream
--use-file-for-fake-audio-capture=engine.wav
Первое, что подтвердилось сразу: track.getSettings() на потоке из {audio:true} возвращает echoCancellation: true, noiseSuppression: true, autoGainControl: true.
|
Эталон на входе |
С обработкой |
Без обработки |
|---|---|---|
|
стук, −21,8 дБ |
−27,4 дБ, пик 0 dBFS, тишина 1% |
−21,8 дБ, пик −2,7 дБ, тишина 0% |
|
прогар выпуска, −31,5 дБ |
−40,6 дБ, тишина 10% |
−31,4 дБ, тишина 0% |
|
стук, ослабленный до −46,8 дБ |
−43,3 дБ, тишина 30% |
−46,8 дБ, тишина 23% |
Без обработки запись совпадает с эталоном до десятой доли децибела — все три раза. С обработкой не совпадает ни разу. Причём портит по-разному: громкий сигнал загоняется в потолок с клиппингом, тихий душится на девять децибел и получает десять процентных пунктов тишины, которой в исходнике не было.
Правка — три поля в объекте ограничений:
const MIC = {echoCancellation: false, noiseSuppression: false, autoGainControl: false};
let stream;
try {
stream = await navigator.mediaDevices.getUserMedia({audio: MIC});
} catch (e1) {
// Спецификация разрешает браузеру не удовлетворить ограничения. Без второй
// попытки такой отказ выглядел бы как отказ в доступе к микрофону.
try {
stream = await navigator.mediaDevices.getUserMedia({audio: true});
} catch (e2) {
showMicDenied();
return;
}
}
Через пять дней, 175 новых записей против 102 до правки: громкость с −35,9 до −26,3 дБ, доля тишины с 36% до 2%, вердикт «поломка» с 65% до 39%, «норма» с 14% до 25%. Модель не трогали.
Здесь статья бы и кончилась, если бы не следующий раздел.
После той же правки теперь записи с айфонов стали приходить тихими, в один удачный день – семь из восьми. Разборки заняли три дня и три версии.
Кэш Safari. Отпала: добавил телеметрию, которая пишет getSettings() с живого трека вместе с загрузкой, и она показала, что новый код доехал.
Версия Safari. Отпала: тихие записи есть и в 26-й, и в 18-й, и в 17-й.
Версия iOS, состав аудитории, смешение записей с загрузками. Отпали все три.
Ответ дал отчёт с реального айфона. Для этого пришлось сделать страницу самодиагностики: она делает два захвата подряд, с обработкой по умолчанию и с выключенной, считает по обоим одни и те же метрики и умеет отправить отчёт на сервер.
|
Захват |
Громкость |
Пик |
Уровень шума |
|---|---|---|---|
|
по умолчанию |
−43,2 дБ |
−29,3 дБ |
−44,9 дБ |
|
обработка выключена |
−61,0 дБ |
−48,4 дБ |
−62,5 дБ |
Выключение обработки на iOS роняет весь тракт на восемнадцать децибел разом. Сигнал, пик и уровень шума одинаково. Это не вычитание фона, при котором полезное остаётся, а снижение усиления целиком. Удивительное рядом. А я еще удивлялся, почему же так поперек спецификаций выходит.
А вот, Сафари, новый IE Safari не сообщает
noiseSuppressionиautoGainControlвовсе — их просто нет в ответеgetSettings(). АechoCancellationработает, только так, что крутит все усиление вниз.
Теперь надо все починить.
Возвращаемся к исходному вопросу. Android действительно писал хуже, таки да, но объяснение оказалось не тем, которое напрашивалось – бюджетники пишут плохо, потому что бюджетники.
Что проверил :
бюджетность , но нет до правки самыми громкими были бюджетные Infinix (−32,5 дБ) и Tecno (−35,3), а хуже всех Huawei (−51,3), Realme (−48,0) и Xiaomi (−45,7); связи с ценовым сегментом нет, а если натягивать сову, то только в обратную сторону;
версию Android — с десятой по шестнадцатую везде 68–85% тишины, без тренда;
браузер — Chrome 70%, Яндекс.Браузер 76%: оба на Chromium, оба обращаются к одному системному механизму. Будь дело в браузере, разные сборки вели бы себя по-разному.
Разгадка нашлась на уровне модели устройства. Внутри одного бренда аппараты ведут себя противоположно:
|
Модель |
Тишина |
|---|---|
|
TECNO LJ8 |
11% |
|
TECNO LH7n |
85% |
|
Redmi M2004J19C |
0% |
|
Redmi 21061119DG |
69% |
|
SM-A137F |
70% |
|
SM-S931B |
96% |
Разброс между ста семьюдесятью моделями — от нуля до ста процентов. Усреднение по бренду это прятало.
Механизм как я понимаю такой. Chrome через WebRTC просит систему включить подавление шума, а исполняет запрос реализация производителя: в Android есть механизм AudioEffect, куда вендор подставляет обработку аппаратно, на DSP звукового чипа, и профили захвата в HAL. Ни то, ни другое не обновляется вместе с браузером и привязано к платформе, а не к версии системы. Один аппарат выходит под разными марками, под одной маркой бывают разные чипы.
На модель у меня приходится по четыре-восемнадцать записей, а это, скорее всего, один-три человека. Модель и пользователь тут неразличимы: может, владелец конкретного Tecno просто держал телефон иначе.
Понять это можно одним способом — посмотреть, как ведёт себя та же модель до и после правки. Поведение [3] человека от нашего флага не меняется, а обработка меняется.
|
Модель |
До |
После |
|---|---|---|
|
Realme RMX3938 |
−52,0 дБ / 98% тишины |
−26,3 дБ / 2% |
|
Xiaomi 2412DPC0AG |
−32,4 дБ / 28% |
−26,0 дБ / 0% |
|
Infinix X6831 |
−32,9 дБ / 0% |
−12,7 дБ / 0% |
Тот же аппарат, те же владельцы, изменился только флаг.
Третья строка интереснее первых двух: на Infinix X6831 тишины не было и до правки. На этом аппарате шумодав либо не включался, либо широкополосный шум не трогал, и правка дала ему только громкость.
Важно. Моделей с данными по обе стороны правки всего три. Это иллюстрация механизма, а не выборка. Сама правка доказана на полутора тысячах записей, помодельная таблица пока остаётся наблюдением.
Порог тишины стоял на −50 dBFS и смешивал «ничего не записалось» с «записалось тихо». Из-за него я решил, что откат правки для iOS не помог, хотя он помог: запись на −43 дБ формально считалась пустой, потому что попадала под порог.
Теперь тишина считается ещё и относительно собственного уровень шума записи : доля в пределах 6 дБ от него. Абсолютную метрику оставил ради сравнимости с историей.
Общий урок про метрики: если порог абсолютный, а уровень сигнала меняется, метрика измеряет не то, что вы думаете. Я потратил на это три дня, благо это пет проект, а не жесткий прод.
Микрофон через js может жить как хочет, т.к.становится важен запрос к незнакомому железу. Но не для всех случаев : голос ± будет писать хорошо.
Минимальная диагностика занимает минуту – getSettings() и посмотреть, что вернул браузер. Потом записать одно и то же дважды подряд на одном аппарате, с обработкой и без, и сравнить громкость, пик и уровень шума. Если все три меняются одинаково не подавление шума, а дешовые трюки.
Касается это не только диагностики механизмов: запись музыки и репетиций, полевые записи природы, шумомеры, телемедицинская аускультация. В общем всё, где предмет записи и есть стационарный широкополосный шум.
Важно. Речевая обработка в WebRTC это хорошо. Она отлично делает свою работу: убирает фон, чтобы собеседника было слышно. Проблема начинается там, где фон и есть предмет записи.
P.S. Страницу самодиагностики я выложил : автоухо.рф/mic-check [4]. Она считает всё в браузере, ничего не отправляет без нажатия кнопки и показывает, что ваш телефон делает со звуком. Мне нужна статистика, если честно, так что если не очень в лом – зайдите, запишите шум, это займет буквально 10-15 секунд и потмо нажать кнопку – “Отправить отчет”. Она внизу страницы, спасибо.
Автор: Shadow_ru
Источник [5]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/34588
URLs in this post:
[1] автоухо.рф: https://%D0%B0%D0%B2%D1%82%D0%BE%D1%83%D1%85%D0%BE.%D1%80%D1%84/?utm_source=habr&utm_medium=article&utm_campaign=mic-android-ios&utm_content=intro
[2] В прошлый раз: https://habr.com/ru/articles/1068376/
[3] Поведение: http://www.braintools.ru/article/9372
[4] автоухо.рф/mic-check: https://xn--80ae0basho.xn--p1ai/mic-check?utm_source=habr&utm_medium=article&utm_campaign=mic-android-ios
[5] Источник: https://habr.com/ru/articles/1071866/?utm_campaign=1071866&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.