Digital Dynamic Range Compressor

  • Автор темы Автор темы belovw
  • Дата начала Дата начала
Хочется услышать обратную связь

Я щас пользоваться и оценить по назначению не могу, но я глянул в код ;)

Ну во-первых
Код:
ga=aa*ga0 + (1-aa)*ga
ga0=ga;

куда лучше с точки зрения производительности выглядит так

Код:
ga0=ga-aa*(ga-ga0)

Но это пол беды.

Проблема заключается в куче операций log10 и 10^x. На каждый семпл такое. А операции эти крайне медленные.

Тут важно определиться вот с чем.

Обычно в компрессорах в RC-фильтре болтается уже сигнал, который gout в терминологии Вашего исходника, т.е. непосредственно множитель. А не его значение в дБ (т.е логарифм), как это сделано у Вас.

Является ли именно этот момент, так сказать, mojo данного компрессора? Мое мнение - сомнительно. Дело в том, что в пределах 6дБ разница между логарифмом сигнала и собственно сигналом всего-то 4% (log2(x+1) примерно равно x).

И так как тут все обсуждают типичный GR порядка 3дБ, то явно не эти 4% играют рояль.

Да, для очень больших изменений управляющего сигнала, поведение будет заметно отличаться.

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

Если же Вы считаете, что вся соль заключена именно в RC-фильтрации значения в dB, то могу только посоветовать хотя бы попробовать сделать быстрый логарифм/экспоненту. Хотя я боюсь, что возможности скрипта Рипера не позволят, там надо иметь возможность работать с битовым представлением числа с плавающей запятой (общий смысл примерно в том, что логарифм числа с плавающей точкой равен его порядку (целая часть) плюс мантисса (без скрытой единицы, это дробная часть).
 
  • Like
Реакции: Landre, Antonio и belovw
Интересно Ваше мнение по предыдущему посту. Я ничего не понял и по мне и так хорошо. Но раз уж дискуссия по оптимизации началась, хотелось бы её продолжения. В таких обсуждениях бывает, что то неожиданное рождается.
 
куда лучше с точки зрения производительности выглядит так
С этим согласен, несомненно даст оптимизацию кода, но настолько маленькую по сравнению с общим потреблением ресурсов из логарифмических и степенных функций, что смысла особого не вижу, но можно.
log2(x+1) примерно равно x
Вот именно примерно, с той разницей, что функция примерно равно Х не имеет колена.
Обычно в компрессорах в RC-фильтре болтается уже сигнал, который gout в терминологии Вашего исходника, т.е. непосредственно множитель. А не его значение в дБ (т.е логарифм), как это сделано у Вас.
Мысль несомненно интересна, но это скорее всего будет уже другой прибор, но это не точно.
могу только посоветовать хотя бы попробовать сделать быстрый логарифм/экспоненту.
Тоже думаю попробовать.
боюсь, что возможности скрипта Рипера не позволят, там надо иметь возможность работать с битовым представлением числа с плавающей запятой (общий смысл примерно в том, что логарифм числа с плавающей точкой равен его порядку (целая часть) плюс мантисса (без скрытой единицы, это дробная часть).
Есть такое. Числа представляются только в формате плавающей запятой. Но реализовать последовательное приближение делением на порядок можно попытаться. Тем более, если учесть что ниже размера колена передаточная функция будет стремиться к единице, а выше к Х, то количество приближений будет максимум на уровне 4.
---
Как появится время, попробую.
 
  • Like
Реакции: Elijah K
Есть такое. Числа представляются только в формате плавающей запятой. Но реализовать последовательное приближение делением на порядок можно попытаться. Тем более, если учесть что ниже размера колена передаточная функция будет стремиться к единице, а выше к Х, то количество приближений будет максимум на уровне 4.

Ну справедливости ради такой способ есть.

Сначала пакуем интересующий нас float в строку при помощи str_setchar(temp_str,0,val,'f'); а потом достаем интересующее нас слово str_getchar(temp_str,2,ival,'su');

Ну а дальше все просто.

e2=(ival>>7)-127;
m2=(ival&0x7F)/128;
log2=ival+man2;

Это, конечно, log2, а не log10, но он же только множителем отличается, что не является принципиальным (ну счет в дБ будет не log(u1/u2)*20, а log2(u1/u2)*6.02)

Аналогично можно сделать и функцию 2^x, только в обратном порядке, сначала создать хитрое целое, потом запаковать его в строку, а потом из строки достать уже float.

Точность такой функции - порядка 4%. Чего за глаза хватит для сигнала управления.
 
  • Like
Реакции: Landre, Elijah K и belovw
А если предрасчитать табличку коррекции при старте (пардон, что на Си):
Код:
float ptab[128];

    for (int i = 0; i < 128; i++)
    {
        ptab[i] = log2((float)i / 128.0f + 1.0f);
    }

А потом вычислять как
Код:
e2=(ival>>7)-127;
m2=ptab[ival&0x7F];
log2=ival+man2;

То вообще не отличишь.

Версия без таблицы поправок:
218528


Версия с таблицей поправок:
218531
 
  • Like
Реакции: belovw
@belovw
Тут внезапно глаз малость фокус навел, и увидел, что в моих писюльках есть описка. К сожалению, редактировать уже не получется.

Вместо log2=ival+man2 читать log2=e2+m2 и в первом примере, и во втором.
 
Проблема заключается в куче операций log10 и 10^x. На каждый семпл такое. А операции эти крайне медленные.
Без блока питания, в самом экономном режиме, расчет логарифма в JSFX потребовал в 2.5 раза больше ресурсов чем операция сложения, умножения или степенная функция.
Обращение к таблице требовательно примерно как расчёт логарифма.
 
@Rst7, из того что ты написал, я понял процентов 20.
Понравилась идея использовать в качестве передаточной функции таблицу, предварительно подготовив её на старте. Потребует память, но сейчас с этим особо проблем нет.
 
вся соль заключена именно в RC-фильтрации значения в dB,
Если фильтровать разы, то релиз тогда получится линейным (в логарифмической шкале дБ).
Идея этого компрессора как раз таки заключается именно в нелинейном релизе, который получается именно в дБ шкале.
Как это релизовать в "разовой" шкале, я пока не представляю. Были попытки сделать это с переменными коэфицентами, но чтол-то пошло не так... (тепа про Rea2a
 
Без блока питания, в самом экономном режиме, расчет логарифма в JSFX потребовал в 2.5 раза больше ресурсов чем операция сложения, умножения или степенная функция.
Обращение к таблице требовательно примерно как расчёт логарифма.

Что-то так у Джастина какой-то совсем мрачный компилятор. Сложение, умножение - это примерно 0.5 такта процессора, а логарифм - чуть ли не 2000.
 
то релиз тогда получится линейным (в логарифмической шкале дБ).

В пределах 6дБ он мало отличается от линейного. 6дБ - это 1 на логарифмической шкале (если log2)

Если поправочку +0.04 добавить, то вот так оно будет
218537
 
В пределах 6дБ он мало отличается от линейного. 6дБ - это 1 на логарифмической шкале (если log2)
Ты меня не понял. Если делать релиз логарифмическим в линейной шкале, то в логарифмической он получится линейным.
 
Ты меня не понял.

Нет, ты меня не понял. Хоть линейное, хоть логарифм, если всего на 6дБ изменение, то оно линейное с точностью 4%.

Если примерно GR болтается в районе -3дБ (от -6 до 0, скажем), например, то пофиг, что там в RC-фильтре, сам сигнал или его логарифм.
 
@belovw, смотри еще раз на мой последний скриншот. Сигнал меняется от 2 до 4, а логарифм от 1 до 2. Апроксимация прямой всего на 0.04 отстоит в худшем случае.
 
@belovw, все, я сдаюсь, делайте что хотите :Dle46: :Dle46: :Dle46: :Dle46:
218544

Вот представление релиза на основе RC в линейной шкале (красное) и в логарифмической (синее)
Мне нужен нелинейный релиз в логарифмической шкале. Как это сделать без использования логарифмов и степеней та ещё задачка.
Как её решить, я до сих пор не понял.
Что предлагаешь ты, я то же не понял.
Не сдавайся )))
 
Вот представление релиза на основе RC в линейной шкале (красное) и в логарифмической (синее)

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

Более того, Вы в RC-фильтре имеете, скажем, значения, от -6 до 0 (это в дБ), а если бы работали с непорседственным представлением сигнала, то управление бы было наоборот, от 0.5 до 1 (потому что Вас надо сигнал умножать на это число, *0.5 соответствует -6дБ, умножение на 1 - 0дБ). И приезд из 0.5 до 1 в RC-фильтре никак не является линейным в dB.

Более того, с точностью 4% это две одинаковые кривые на выходе.

Пример:

Допустим, мы сняли входной сигнал, перед этим у нас был gain reduction в 6дБ. Значит начальное значение RC-фильтра в Вашем варианте было -6, а в моем 0.5

Съем входного сигнала значит, что на вход Вашего RC-фильтра будет поступать 0, а на вход моего - 1.

Повторюсь, Ваш вариант - это GR в дБ, мой - непосредственно в разах.

Результаты:

Ваш вариант:-
График a - поведение значения RC-фильтра, который падает от -6дБ в 0дБ.
График b - расчетное усиление в разах по графику a

Мой вариант:
График с - был gain reduction 0.5 (т.е. -6дБ) и он стал 1 (0дБ), RC-фильтр с той же скоростью приближается к 1.

Итого:
График d - разность Вашего управления и моего * 100, т.е. в процентах. Чуть больше 4%

Спрашиваю последний раз - зачем нужно в RC-фильтре держать сигнал в dB? ;)
 
  • Like
Реакции: belovw
Поведение еще можно приблизить друг к другу путем небольшой коррекции постоянной времени в моем варианте

Коэффициент коррекции подозрительно похож на sqrt(e)/2, но я не готов щас математически доказать минимум ошибки именно с таким коэффициентом.

UPD - нет, то просто так совпал коэффициент. Я бы брал 0.8, чтобы до -10дБ иметь вполне вменяемое соответсвие.
 
Последнее редактирование:
  • Like
Реакции: belovw
Ту модель ADRS которую я использовал, давала линейный релиз.

Не могла она такое давать. Потому что там обычный RC-фильтр.

Линейно в дБ происходит падение, если бы мы измеряли уровень сигнала пиковым детектором. Т.е. запомнили в фильтре какой-то пик, а потом он бы падал до 0. Вот от этого пика до 0 изменение в дБ было бы действительно линейным.

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

Но в компрессоре это не так, потому что GR - это не абсолютный уровень сигнала. См. мой пример с реальными цифрами.
 
поведение пикового детектора
После многосуточного мозгового штурма: читаю как:
"Поведение пивного детектора"
Ну что же. Похоже у нас рождается новый компрессор, причём кривые атаки и релиза несколько отличаются от форм DDR. Хорошо это или плохо пока не знаю, но навскидку она может прозвучать ближе к опто. Надо слушать.
218562


Пока ещё не избавился от одной степенной функции отвечающей за Ratio. Надо подумать как её реализовать через выше изученные приближения.
И надо решить вопрос с коленом. Пока просто хард кни
 
Последнее редактирование:
  • Like
Реакции: Antonio и Elijah K

Сейчас просматривают