LINUX.ORG.RU

EasyEffects не нужно, или PipeWire для продвинутых: часть 4

 , ,


0

2

Спатиалайзер для наушников

Вы когда-нибудь задумывались, чем прослушивание музыки через наушники, отличается от прослушивания музыки через колонки? Основное отличие - когда мы в наушниках, то левое ухо слышит только звук левого наушника (левый стерео канал), и совсем не слышит звук правого. И наоборот. Когда мы слушаем колонки, то оба уха слышат обе колонки, но по-разному. Из-за этого простого факта, восприятие стерео-сцены в наушниках и через колонки, радикально отличается.

И возникает существенная проблема - а на что должна быть расчитана запись, на прослушивание в наушниках, или в колонках? Индустрия выбирает колонки, как основной источник звука, и все делается в расчете на них. А в наушниках мы будем слышать неправильное стерео, сильно искаженное.

Но - у нас же есть pipewire, поэтому не беда, сейчас мы это исправим! В этой и следующей статье, мы заставим звучать наушники как колонки, а колонки как наушники (ну почти)!

ТЕОРИЯ

Поможет нам в этом эффект, который называется спатиалайзер. С наушниками его реализация даже проще, потому что конструктивно мы левым ухом слышим левый стерео канал без примеси правого, правым ухом слышим правый без примеси левого. Теперь, нам нужно добавить эти примеси, чтобы имитировать ситуацию с колонками, когда есть crosstalk - то есть левый стерео канал проникает в правое ухо, и наоборот.

В простейшем случае, можно просто подмешать левый сигнал в правый с пониженной громкостью, и наоборот. Есть готовые устройства для наушников (кроссфидер) которые это делают. В наушниках станет возможно слушать первые альбомы Beatles)) но радикально ничего не изменится - звуки все равно будут внутри головы, а не вокруг, как с настоящими колонками.

Для того, чтобы имитировать звучание колонок на более серьезном уровне, давайте разберемся, а как вообще человек определяет, с какой стороны идет звук - слева, справа, спереди, сзади? Как работает локализация источника звука?

Форма наших ушей и форма нашей головы такова, что она оказывает влияние на звук в зависимости от того, с какой стороны идет звук! Проще говоря, воспринимаемый ухом баланс низких, средних, высоких частот будет меняться, если колонку перемещать вокруг вашей головы! Ушная раковина работает как эквалайзер, который меняет звук в зависимости от положения источника звука.

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

Третий фактор - разница в громкости между левым и правым ухом.

Вот эти три фактора определяют для нас ощущение в пространстве места источника звука. Теперь, если мы хотим сделать спатиалайзер, то есть имитатор источника звука, расположенного где-то вокруг нас с заданными координатами, нам нужна математическая функция, которая на вход получает необработанный, исходный звук, имеет параметры - направление (азимут) и расстояние до источника звука, и на выход выдает обработанный звук, измененный так же, как его бы изменила наша ушная раковина и форма черепа, если бы он шел из заданной точки! Причем, на выходе мы должны получать два канала - для левого наушника, и для правого, потому что все источники звука мы слышим двумя ушами.

Такая функция называется HRTF - Head Related Transfer Function, или ее подвариант DTF - Directional Transfer Function (содержит только разницу между ушами, по сравнению с HRTF, это не сильно важные пока тонкости реализации). Откуда ее взять, как получить?

Берем вас. Вставляем вам в ушные каналы микрофоны. Теперь через ваши уши будете слышать не вы, а шайтан-машина! Сажаем вас в заглушенную лабораторию, где вокруг вас расставлено огромное количество колонок, по кругу. И начинаем воспроизводить тестовый сигнал с каждой колонки по очереди и записывать звук с микрофонов. Для каждой записи, помечаем под каким азимутом стояла колонка. Так мы получаем набор импульсных откликов уха-головы (HRIR, Head Related Impulse Response), для каждого направления на колонку, когда она перед вами, влево под 5 градусов, влево под 10 градусов и так далее. Это запись того, что слышат ваши уши.

Дальше эти HRIR записи обрабатываются, пересчитываются и получается HRTF или DTF. Это все сохраняется в файл по стандарту SOFA.

Теперь мы можем сделать обратный процесс, если у нас есть этот SOFA файл! Мы можем взять музыку, левый стерео канал, задать координаты виртуальной левой колонки, пропустить звук через HRTF, и получить измененный, окрашенный звук для левого и правого наушника, который обманывает уши, заставляет их думать, что источник звука - вот там вот! Имитируем правую колонку так же, можно задние для 5.1 системы, боковые для 7.1, что угодно! И вы услышите окружающий звук через простые наушники!

Фантастика! Но есть ложка дегтя. Стоить такой обмер вашей головы будет столько, что вы не захотите это делать)) Поэтому, максимум что мы можем иметь - чужая HRTF другого человека, или какая-то обобщенная HRTF «для всех». Которую заморочились и сняли. Вы будете слышать через чужие уши! А уши это такая зараза, что у каждого человека они разные. Поэтому, идеальной точной локализации вы не получите, но эффект все равно достаточно сильный и крутой.

ПРАКТИКА

Теперь мы можем реализовать спатиалайзер - он должен загружать HRTF (или DTF) из SOFA файла, пропускать исходный стерео звук через HRTF, задаем для него координаты виртуальных стерео колонок перед нами, и получаем звук в наушниках, как из колонок (в какой-то мере). И намного более правильную стерео сцену.

Такой спатиалайзер уже есть во всех основных ОС, в андроид смартфонах это называется 3D Effects, в Windows не помню как, но начиная с Windows 10 это доступно в системном микшере просто. В Linux - хм хм, вы видели? Нет? Что-то не находите? Как всегда…

Хорошая новость - на самом деле теперь и в Linux есть все средства для того чтобы сделать спатиалайзер, ведь его встроили в pipewire! Плагин filter-chain, которому посвящены эти статьи, содержит эффект SOFA, который загружает SOFA файлы и делает нужную нам обработку. В параметрах эффекта, можно задать азимут направления на виртуальный источник, и расстояние до него.

Плохая новость - никто не включил в pipewire сами SOFA файлы! Их просто нет в комплекте, ищи где хочешь. Благо, найти их не сложно. Есть ресурс https://www.sofaconventions.org/ и там море ссылок на свободно доступные HRTF и DTF, снятые учеными в лабораториях для нас!

Я использую вот этот файл https://sofacoustics.org/data/database/ari/dtf%20b_nh2.sofa (прямая ссылка). Там рядом их еще море, отличаются тем, что их сняли с ушей разных людей. Выбор файла - поле для экспериментов.

Теперь напишем конфиг файл, который создает две виртуальные колонки перед нами. Сначала разберем важные куски из этого файла, полный листинг файла и куда его помещать, будет в конце статьи.

Азимут у них будет 30 градусов от направления «вперед», это теоретически идеальная расстановка стерео колонок по принципу равностороннего треугольника. Еще создадим две задние колонки, для ощущения окружающего звука и глубины, на них подадим тот же стерео звук, только тише.

{
  type = sofa
  label = spatializer
  name = spFL
  config = {
    filename = "/home/curufinwe/dtf_b_nh2.sofa"
  }
  control = {
    "Azimuth"    = 30.0
    "Elevation"  = 0.0
    "Radius"     = 1.0
  }
}

В filename указываем путь, где лежит sofa файл. Азимут - направление на виртуальный источник, прямо вперед это 0 градусов, влево это 90 градусов, сзади 180, справа 270. Elevation - угол подъема, не имеет значения, в нашем sofa файле нет информации о вертикальной плоскости. Radius - расстояние до источника, тоже не имеет значения, этот файл не содержит информации о расстоянии. Работает только азимут.

В начале цепочки нам понадобятся эффекты copy, потому что у нас будет по два sofa эффекта для каждого канала - для передних виртуальных колонок, и для задних. После sofa эффектов мы получаем по 4 сигнала на каждый канал наушников, смешиваем их при помощи mixer, задаем громкости для передних «колонок» 0.5, для задних «колонок» 0.3.

Если громкость задних сделать 0, то будем слышать чистую имитацию стерео колонок впереди вас.

Практика прослушивания показала, что конечно, ощущение колонок далеко не полностью реалистичное, звук только немного выходит вперед и в стороны из головы, но при сравнении с реализациями в других ОС - оно работает не хуже, точно так же! Идеала пока нигде нет, к нему можно приблизиться, если снять HRTF с вашей личной головы.

Полный конфиг-файл, содержимое этого листинга надо поместить в файл по адресу ~/.config/pipewire/pipewire.conf.d/spatializer.conf и перезапустить pipewire.

context.modules = [
    { name = libpipewire-module-filter-chain
        args = {
            node.description = "Spatializer"
            media.name       = "Spatializer"
            filter.graph = {
                nodes = [
                    {
                        type = builtin
                        label = copy
                        name = copyL
                    }
                    {
                        type = builtin
                        label = copy
                        name = copyR
                    }
                    {
                        type = sofa
                        label = spatializer
                        name = spFL
                        config = {
                            filename = "/home/curufinwe/dtf_b_nh2.sofa"
                        }
                        control = {
                            "Azimuth"    = 30.0
                            "Elevation"  = 0.0
                            "Radius"     = 1.0
                        }
                    }
                    {
                        type = sofa
                        label = spatializer
                        name = spFR
                        config = {
                            filename = "/home/curufinwe/dtf_b_nh2.sofa"
                        }
                        control = {
                            "Azimuth"    = 330.0
                            "Elevation"  = 0.0
                            "Radius"     = 1.0
                        }
                    }
                    {
                        type = sofa
                        label = spatializer
                        name = spRL
                        config = {
                            filename = "/home/curufinwe/dtf_b_nh2.sofa"
                        }
                        control = {
                            "Azimuth"    = 150.0
                            "Elevation"  = 0.0
                            "Radius"     = 1.0
                        }
                    }
                    {
                        type = sofa
                        label = spatializer
                        name = spRR
                        config = {
                            filename = "/home/curufinwe/dtf_b_nh2.sofa"
                        }
                        control = {
                            "Azimuth"    = 210.0
                            "Elevation"  = 0.0
                            "Radius"     = 1.0
                        }
                    }
                    {
                      type = builtin
                      label = mixer
                      name = mixL
                      control = {
                        "Gain 1" = 0.5
                        "Gain 2" = 0.5
                        "Gain 3" = 0.3
                        "Gain 4" = 0.3
                      }
                    }
                    {
                      type = builtin
                      label = mixer
                      name = mixR
                      control = {
                        "Gain 1" = 0.5
                        "Gain 2" = 0.5
                        "Gain 3" = 0.3
                        "Gain 4" = 0.3
                      }
                    }
                ]
                links = [
                    { output = "copyL:Out"  input="spFL:In" }
                    { output = "copyR:Out"  input="spFR:In" }
                    { output = "copyL:Out"  input="spRL:In" }
                    { output = "copyR:Out"  input="spRR:In" }
                    { output = "spFL:Out L"  input="mixL:In 1" }
                    { output = "spFL:Out R"  input="mixR:In 1" }
                    { output = "spFR:Out L"  input="mixL:In 2" }
                    { output = "spFR:Out R"  input="mixR:In 2" }
                    { output = "spRL:Out L"  input="mixL:In 3" }
                    { output = "spRL:Out R"  input="mixR:In 3" }
                    { output = "spRR:Out L"  input="mixL:In 4" }
                    { output = "spRR:Out R"  input="mixR:In 4" }
                ]
                inputs = [ "copyL:In" "copyR:In" ]
                outputs = [ "mixL:Out" "mixR:Out" ]
            }
            capture.props = {
                node.name   = "effect_input.spatializer"
                media.class = Audio/Sink
                audio.channels = 2
                audio.position=[FL FR]
            }
            playback.props = {
                node.name   = "effect_output.spatializer"
                node.passive = true
                audio.channels = 2
                audio.position=[FL FR]
            }
        }
    }
]

★★★★

Проверено: maxcom ()
Последнее исправление: James_Holden (всего исправлений: 2)
Ответ на: комментарий от James_Holden

Честно говоря, я так глубоко никогда в это не копал. Играет и играет. Дискретизацию выставил и всё. Потому что был один источник звука. Никаких постобработок не делал никогда

По твоему тексту согласен полностью. И всё же на базовом уровне алса + dmix работало и работало лучше, чем появившаяся пульса в то время. И всё же вопрос остается открытым, зачем надо раз в 10 лет менять звуковой сервер. Может стоило пульсу переписать, чтобы все аплеки это поддерживали и всё бесшовно протекало, а не как переход алса –> пульса, когда 10 лет пшшш было

serg002 ★★★
()
Ответ на: комментарий от serg002

И всё же на базовом уровне алса + dmix работало и работало лучше, чем появившаяся пульса в то время.

Образовавшийся от крикливого меньшинства миф, которых устраивали текущие ограничения и кто смог вывести звук через какую-нибудь sb live.

dmix никогда нормально не работал. Но в то время были распространены карточки, в которые можно было выводить несколько потоков. После избавления от всяких arts и esd можно было получить систему, на которой таки при запуске приложения в четырех случаях из пяти звук выводился из коробки.

Почему не в 5 из 5? В выводе звука напрямую через алсу не было никаких ограничений или какого-то стандарта. Вместо вывода в указанный в конфиге дефолт приложение могло само выбрать другую звуковушку или захватить монопольно основную.

Для большинства же пользователей звук в линуксе из коробки просто не работал, причина по которой я знаю о монопольном доступе, частоте дискретизации, звуковых серверах, микшировании и т.д. и т.п. - необходимость во всём этом разбираться для базовой настройки звука в линуксе на разнообразном железе. И со всем этим багажом можно было собрать систему с возможностями по звуку уровня win98, без регулирования звука отдельных приложений, блютусов и динамического переключения звука на usb/блютус наушники или hdmi.

Из коробки были ситуации, когда звук есть то в одной трети приложений (звук захватил кдешный артс), или в другой (захватил гномовский есд). А в трети его просто не было. На фоне этого проблемы пульсы были просто незначительными.

altwazar ★★★★★
()
Ответ на: комментарий от serg002

Одно из двух - либо в дебиане версия pipewire, в которой еще нет sofa, либо в дебиане оно ставится отдельным пакетом, там любят распилить. Но вообще это встроенный плагин, так что странновато, сейчас поищу инфу

James_Holden ★★★★
() автор топика
Ответ на: комментарий от serg002

Хм, пока инфа неутешительная - даже в Ubuntu pipewire собран без sofa. С deb based постоянно какие-то загоны, ну штош такое

James_Holden ★★★★
() автор топика
Ответ на: комментарий от serg002

Короче! Насколько я вижу по списку файлов пакетов, в Debian Bookworm нету sofa в основной репе, но она есть в backports!

Пакет libpipewire-0.3-modules из bookworm-backports содержит sofa плагин.

James_Holden ★★★★
() автор топика
Ответ на: комментарий от serg002

И всё же на базовом уровне алса + dmix работало и работало лучше, чем появившаяся пульса в то время.

Да, я тоже ее выпиливал первым делом. Но - это не проблема концепции звукового сервера как такового, это проблема конкретной реализации нашим любимым программистом всея линукса.

Может стоило пульсу переписать, чтобы все аплеки это поддерживали и всё бесшовно протекало

Поэтому - pulseaudio пытались лет 10 переписать после поттера, но там все равно куча проблем, потому что изначально неудачная архитектура.

И куда уж бесшовнее, сделали поддержку API pulseaudio и jack, все приложения даже не замечают что что-то изменилось. Какие швы то?

10 лет пшшш с пульсой было, потому что поттер не осилил шедулинг своей шайтан-машины сделать нормально. А pipewire даже с ранних версий работает лучше чем допиливавшийся 10 лет pulseaudio, с bluetooth - заметно лучше.

James_Holden ★★★★
() автор топика
Ответ на: комментарий от James_Holden

10 лет пшшш с пульсой было

У меня на сусекедах (там пульса почему-то дефолтом) она еще этим летом трещала при движении ползунка громкости. То есть, не десять лет, а двадцать. ДВАДЦАТЬ.

thesis ★★★★★
()
Ответ на: комментарий от thesis

Вот там ее реально, серьезно пытались чинить. Но этот треск, там by design какие-то проблемы, не смогли устранить.

James_Holden ★★★★
() автор топика
Ответ на: комментарий от James_Holden

С JACK таки не бесшовно, регулярно читал ченджлоги и там чинили и джек и альсу и до сих пор JUCE приложения не очень дружат с PW. Юзаю ALSA, JACK и PA по отдельности на десктопе, на ноуте юзаю PW так как там блютус работает как надо.

kott ★★★★★
()
Ответ на: комментарий от kott

У меня на сюзекедах PA не трещит, да и не трещала в общем-то.

Трещала при софтовом регулировании звука с разными причинами. У кого-то алгоритм передискретизации стоял «качественный», у кого-то софт для синтеза звука для инвалидов глюки вызывал (и не только со звуком), у кого-то вывод был через эквалайзер или изиэффектс.

altwazar ★★★★★
()
Ответ на: комментарий от altwazar

Да запросто, верю. Но поскольку я с ней не делал ничего сложнее, чем вывести звук с браузера/плеера - то не сталкивался с треском.

Приходилось танцевать с подключением микрофона, и в целом соорудить какой-нибудь роутинг, чтоб отправить звук со своей стороны, в этом плане она меня бесила.

kott ★★★★★
()
Ответ на: комментарий от James_Holden

т.е. с алсой криво работало? я получал треск только когда пытался выводить напрямую в устройство (не через API) при запущенной PA

kott ★★★★★
()
Ответ на: комментарий от kott

Нет, именно pulseaudio криво работает, но это каким-то образом зависит от конкретного железа. На некоторых машинах у меня пшшшшш было, на некоторых ни единого разрыва, одновременно по времени. На разных дистрибутивах.

У меня трещало само PA, при этом вывод в ALSA не трещал тут же.

James_Holden ★★★★
() автор топика
Последнее исправление: James_Holden (всего исправлений: 1)
Ответ на: комментарий от James_Holden

я имел в виду, что PA криво работало с алсой, раз железо влияло каким-то образом

kott ★★★★★
()
Ответ на: комментарий от James_Holden

Нет, именно pulseaudio криво работает, но это каким-то образом зависит от конкретного железа

Вот тут Лёня пишет о своей glitch-free модели в PA. Как я понял от знающих людей (возможно я понял неправильно), что практически все major complications, которые он там написал, оказались трудно или практически не решаемыми в рамках PA. Т.е. алгоритм компенсации отклонений в таймерах 100% гарантию не даёт (и как я понял, легендарное пшшш в большей степени отсюда), как и «угадать» необходимый правильный размер буфера - тоже задача далеко нетривиальная.

SkyMaverick ★★★★★
()
Ответ на: комментарий от kott

Ну я думаю, если бы она трещала прям с порога абсолютно у всех, ее бы дефолтом и не пихали. Хотя кто его знает, линукс этот.

В общем, я не разбирался в причинах и не тестировал на звуковухах, кроме основной. Тупо сменил на пайпварь и все стало хорошо.

thesis ★★★★★
()
Ответ на: комментарий от James_Holden

Ясно. Спасибо. Видишь, всякие приколюхи в основной репе отсутствуют, это надо подключать бекпорты

Попробовал поюзать pipewire сутки. После standby в телеге отваливается звук. Это нужно смотреть, чинить. Пока что переключусь на пульсу, а в новом релизе debian попробую покрутить. Функционально для меня что пульса, что сабж - одинаковы. Смысла менять то, что работает - в текущий момент времени нет

serg002 ★★★
()
Ответ на: комментарий от James_Holden

Хм, пока инфа неутешительная - даже в Ubuntu pipewire собран без sofa. С deb based постоянно какие-то загоны, ну штош такое

Видимо потому что это должны протестировать на себе юзеры федоры, арча и генты а потом, когда оно стабилизируется, - должно появиться в дебиан и убунте :)

serg002 ★★★
()
Ответ на: комментарий от serg002

Так вот как раз в арче, все уже починено и работает стабильно. И приколюхи в основной репе. А пользователи дебиана починенные версии получат через год, или когда там.

James_Holden ★★★★
() автор топика
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.