LINUX.ORG.RU

Статьи - Архив 2025, Январь

Что почитать C++ программистам

Статьи — Разработка

Предлагаю вашему вниманию две бесплатные электронные книги. Первая написана мной: «Вредные советы для C++ программистов» (PDF-формат). Это переработанный под печать вариант «60 антипаттернов для С++ программиста». Если вы уже читали 60 антипаттернов, то, пожалуй, читать новый вариант смысла нет. А если нет, то рекомендую — это и возможность местами улыбнуться, и польза.

Теперь про вторую книгу: «Путеводитель C++ программиста по неопределённому поведению». Здесь я был редактором, автор: Дмитрий Свиридкин. Книга опубликована частями, ссылки на которые приведены ниже. Сейчас Дмитрий перерабатывает материал под книжное издание, но это отдельная история.

  1. Часть 1: предисловие, что такое неопределённое поведение, и как оно проявляется, сужающие преобразования и неявное приведение типов.
  2. Часть 2: переполнение целых знаковых чисел, числа с плавающей точкой, integer promotion, char и знаковое расширение.
  3. Часть 3: висячие ссылки, string_view, синтаксический сахар с ложкой дёгтя (range-based for), self-reference, std::vector и инвалидация ссылок.
  4. Часть 4: списки захвата лямбда-функций, кортежи, внезапная мутабельность, неявные ссылки, use-after-move, lifetime extension.
  5. Часть 5: Most Vexing Parse, неконстантные константы, семантика перемещения, std::enable_if_t против std::void_t, забытый return.
  6. Часть 6: эллипсис и функции, operator [], iostreams (счастливой отладки!), оператор запятая, function-try-block, типы «нулевого» размера.
  7. Часть 7: NULL-терминированные строки, std::shared_ptr, (не)явное приведение типов, как передать стандартную функцию и ничего не сломать.
  8. Часть 8: бесконечные циклы и проблема остановки, рекурсия, ложный noexcept, переполнение буфера.
  9. Часть 9: (N)RVO vs RAII, разыменование нулевых указателей, static initialization order fiasco, static inline, нарушение ODR, зарезервированные имена.
  10. Часть 10: тривиальные типы и ABI, неинициализированные переменные, С++20 unbounded ranges, невиртуальные виртуальные функции, VLA.
  11. Часть 11: невалидные указатели, placement new для массивов, data race, повторный захват mutex, сигнало(не)безопасность, как сделать всё правильно и уйти в deadlock.
  12. Часть 12: std::vector::reserve и std::vector::resize, невыровненные ссылки, время жизни и смерти, статический анализ и UB, заключение.

И последнее: если пропустили, в конце 2024 года, как всегда, вышла подборка про самые интересные из найденных нами багов: Топ-10 ошибок в C и C++ проектах за 2024 год.

 ,

Andrey_Karpov_2020
()

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

Статьи — Desktop

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

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

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

Но - у нас же есть 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]
            }
        }
    }
]

 , ,

James_Holden
()

image viewer simply на bash

Статьи — Разработка
image viewer simply на bash

Хочу представить простенький просмотрщик изображений на bash. Накропал его сам и может он не имеет практического применения,но все же. Конечно там возможно много сделать лучше и проще. Как говорится как есть))

#!/bin/bash

export tmp_num="/tmp/numbegin"
[ ! -f "$tmp_num" ] && echo "1" > "$tmp_num"
export file_image="/tmp/file_image" 
[ ! -f "$file_image" ] &&  find / -xdev  -type f -name "*.png" -or -name "*.svg"  -or -name "*.jpg"  >  /tmp/file_image  
 
export col_image=$(cat $file_image | wc -l)
export max_stroke=$(grep -c $ "$file_image" | (read a; echo $(( ($a/300)*300+1 ));))
export FILE_PATH=$(realpath "$0") 
export catnum=$(cat "$tmp_num")
  function IMAGE_FILE () { 
begin="$1" 
begin="$catnum"
IFS=$'\n'
i="$begin"  
 
for file_name in $(sed -n "${begin},$((${begin} + 299))p" $file_image) 
  do
#size=$(exiv2 $file_name 2>/dev/null | awk ' NR==2 {printf("%.2f", sum ($4 / 1024)); print "Kb"}; NR==4 {print $4 "x" $6 "px"}' | tr '\n' ' ') --text=\"$size\"yad  --undecorated --image=\"$file_name\" --text=\"$file_name\" --selectable-labels  
 if (( "$i % 30" == 0 )) && [[ "$i" -le $((${begin} + 299-30)) ]] ; then num="</hbox><hbox>"; else num=""; fi
  echo "<button tooltip-text=\"$i $file_name\"><input file>\"$file_name\"</input><action>xdg-open  \"$file_name\"</action><height>30</height><width>30</width></button> $num"
  i=$((i + 1))
done
}
export -f IMAGE_FILE 
 
export MAIN_DIALOG_IMAGE='<window window-position="1" title="Галерея  bash"><vbox><hbox>'`IMAGE_FILE $begin`'</hbox><hbox space-expand="true" space-fill="true">
<button label="start">
<action>echo "1" > "$tmp_num"</action>
<action>$FILE_PATH $begin &</action></button>

<button label="'"$catnum"' prev">
<action>if [ "$catnum" -gt 1 ]; then begin=$(($catnum - 300)); echo "$begin" > "$tmp_num"; else begin=1; echo "1" > "$tmp_num"; fi</action>
<action>$FILE_PATH $begin &</action></button>

<button label="next '"$catnum"' - '"$(( $catnum + 299 ))=$col_image"'">
<action>if [[ "$catnum" -lt "$max_stroke" ]]; then begin=$(($catnum + 300)); echo "$begin" > "$tmp_num"; else begin=1; echo "1" > "$tmp_num"; fi</action>
<action>$FILE_PATH $begin &</action></button>
</hbox>
</vbox></window>'

gtkdialog --program=MAIN_DIALOG_IMAGE &
 
 PID_SUM=$(ps | grep "MAIN_DIALOG_IMAGE" | grep -v grep | awk '{print $1}' | wc -l) 
  PID=$(ps -eo pid,cmd | grep "MAIN_DIALOG_IMAGE" | grep -v grep | awk '{print $1}' | head -1)
  [ "$PID_SUM" -gt 1 ] && kill $PID
[ "$catnum" -ge "$max_stroke" ] && echo "1" > "$tmp_num" && $FILE_PATH $begin &

Хотел чтобы список был больше,но long arg list не дал это сделать. ps Не имею ни какого образования в программировании.

 , ,

nik120s
()