Skip to main content

Dithering (часть 3)

Это будет третья и завершающая часть из серии постов про dithering, да и тема, наверняка, уже порядком надоела ;)

Шум для dithering'а, о котором я говорил в предыдущих постах был либо белый, либо топорно приглушенный в нижних частотах. Технология noise shaping, о которой можно прочитать в статье S.P. Lipshitz, J. Vanderkooy and R.A. Wannamaker. 1991. Minimally audible noise shaping. J. Audio Eng. Soc. 39, 836-852. — это работа скальпеля, а не топора. Дело в том, что человек воспринимает разные частоты неодинаково. Существуют экспериментальные кривые зависимости мощности тона от частоты, которые построены так, что человек воспринимает эти тоны с одинаковой "громкостью". Вот пример такой кривой (спасибо, Википедия ;-)

Как видно, на частотах около 4 и 12 кГц у человека есть локальные "пики" восприимчивости, когда тон должен быть тише, чем тоны с соседними частотами, чтобы восприниматься субъективно с той же громкостью.

Схема dithering'а выглядит так (взял из упомянутой статьи):
К исходному сигналу добавляется белый шум (например, треугольно распределенный) и сигнал квантуется. Затем из полученного зашумленно-квантованного сигнала вычитается исходный (находится ошибка округления), она фильтруется, так, чтобы шум имел желаемый спектр (в некотором смысле он должен быть обратной величиной к кривой Perceived Human Hearing выше, чтобы человек воспринимал все частотные составляющие одинаково). Если я правильно понял фишку (могу и ошибаться), то при подборе фильтра учитывается ещё то, что минимизируется "взвешенная" мощность ошибки+шума $W_{w} = \int_{-\omega_{ref}/2}^{\omega_{ref}/2} W(\omega) A(\omega) d\omega$, где $W(\omega)$ — спектральная плотность мощности, $A(\omega)$ — её вес с учетом человеческого восприятия.

В частности, в программе audacity применяется фильтр ошибки, ядро которого имеет пять ненулевых коэффициентов (скопипащенных из той же статьи, копипаст — великая сила) $a_{n} = \left\{2.033, -2.165, 1.959, -1.590, 0.6149, 0, 0, \cdots \right\}$ Ошибка, вычисленная таким образом для $n$-го семпла, складывается потом с входным сигналом на $n+1$ шаге. Вот код, спертый из audacity:
(defun uniform-noise (magnitude)
  (- (random (ash 1 magnitude))
     (ash 1 (1- magnitude))))

(defun triangle-noise (magnitude)
  (+ (uniform-noise magnitude)
     (uniform-noise magnitude)))

(defun apply-dither (array magnitude noise-fn)
  (let ((new-array (make-array (length array))))
    (map-into new-array
              (lambda (x)  (funcall noise-fn magnitude x))
              array)))

(defun quantize (x m)
  (let ((rnd (ash 1 m)))
    (* rnd (round x rnd))))

(defun shaped-noise ()
  (let ((history (make-array 8 :initial-element 0))
        (flt #(2.033 -2.165 1.959 -1.590 0.6149))
        (position 0))
    (lambda (magnitude sample)
      (let* ((noise (triangle-noise magnitude))
             (xe (+ sample
                    (* (aref flt 0) (aref history (logand position 7)))
                    (* (aref flt 1) (aref history (logand (- position 1) 7)))
                    (* (aref flt 2) (aref history (logand (- position 2) 7)))
                    (* (aref flt 3) (aref history (logand (- position 3) 7)))
                    (* (aref flt 4) (aref history (logand (- position 4) 7)))))
             (result (+ xe noise)))
        (prog1 result
          (setq position (logand (1+ position) 7))
          (setf (aref history position)
                (- xe (quantize result magnitude))))))))

Таким образом, сам шум получается отфильтрованным эквивалентным фильтром с ядром $\left\{-1, 2.033, -2.165, 1.959, -1.590, 0.6149, 0, 0, \cdots \right\}$. Вот АЧХ этого фильтра (вертикальная шкала — логарифмичекая):
АЧХ эквивалентного фильтра для шума. По горизонтальной оси — частоты в долях $\omega_{ref}$
Взяв частоту дискретизации $f_{ref} = 44100 Hz$, можно подсчитать, что локальные минимумы находятся около частот $f_{1} \approx 0.1 f_{ref} \approx 4000 Hz$ и $f_{2} \approx 0.3 f_{ref} \approx 13000 Hz$. Это идеально согласуется с картинкой в начале поста — раз человек слышит 4 кГц лучше, то имеет смысл немного "приглушить" эту частоту, чтобы она не выделялась на фоне других. Также видим, что верхние частоты звучат сильнее нижних (как в фильтрованном треугольном шуме в предыдущем посте).

У этого метода есть свой недостаток, однако. Раз какая-то часть энергии шума будет сдвинута в верхние частоты за счет нижних, то и амплитуда шума будет больше, чем при белом шуме (для компенсации "приглушенности" нижнечастотного шума). Её можно оценить как $A = 2\Delta (1 + \sum_{k=1}^{5} \left|a_{k}\right| ) \approx 9 \cdot 2\Delta$. В вики audacity рекомендуется применять этот вид dithering'а не более одного раза. Также не совсем ясно, что делать, если значение сигнал+шум превысит допустимые значения. Audacity просто клиппует результирующий сигнал, но не внесет ли это в данном случае бОльшую ошибку, чем та, от которой мы избавляемся (в немного меньшей мере эта проблема относится ко всем видам шума)?

Даю послушать тот же сигнал, что и всегда, но с примененным noise shaping:
Sine wave (shaped dithering, 5 coeffs) 400Hz (44100 Hz, 7 bps)

На спектрограмме видим:
Весь шум здесь сдвинут в верхние частоты, а потому звучит тихо.

Также в статье есть пример фильтра с ядром, имеющим 3 ненулевых коэффициента: $\left\{ 1.652, -1.049, 0.1382, 0, \cdots, \right\}$. Сам фильтр имеет худшие психоакустические характеристики, но и амплитуда шума у него меньше.

Пример:
Sine wave (shaped dithering, 3 coeffs) 440 Hz (44100 Hz, 7 bps)

Спектр:


В заключение хочу напомнить, что все эти коэффициенты имеют смысл только при частоте дискретизации $f_{ref} = 44100 Hz$! При других частотах теряется весь смысл такого фильтра (локальные минимумы на АЧХ эквивалентного фильтра для шума сдвигаются), и коэффициенты надо считать заново. Audacity, однако, не учитывает этого. Поэтому, при сохранении чего-либо в audacity с частотой дискретизации, отличной от 44100 Hz, лучше использовать triangular noise. Сказанное относится к версии 2.2.2

Comments

Popular posts from this blog

Гладкая сшивка кусочно-постоянной функции.

Накануне мне понадобилось найти гладкую функцию, определенную на $\textbf{R}[a,c]$, равную единице на одном отрезке числовой оси, скажем, $[a, b-\epsilon]$ и нулю на другом, скажем $[b+\epsilon, c]$. Для того, чтобы гладко склеить эти два кусочка, я решил найти монотонно убывающий многочлен $f(x)$, равный $f(0) = 1$ и $f(1) = 0$, а потом "вставить" его в отрезок $[b-\epsilon, b+\epsilon]$. Для гладкости сшивки должны выполняться условия $f'(0) = f'(1) = 0$. Я решил пойти далее и положить $f^{(n)}(0) = f^{(n)}(1) = 0$ для всех $n = 1,2,\cdots,N$. То есть первые N производных должны быть равны нулю в т. $x = 0$ и $x = 1$. Понятно, что это легко сделать при наперед известном N, но как найти общую формулу? Я некоторое время промучился над этой задачей, пока не решил её следующим способом. Пусть искомая функция $f(x) = 1 + x^{N+1}L_{1}(x)$, где $L_{1}(x)$ — некий многочлен. Тогда у нас выполняется условие $f(0) = 1$ и $f^{(n)}(0) = 0$ (так как первая производная будет

Ресемплинг аудио.

В этом посте расскажу о ресемплинге аудио (в частности, о даунсемплинге), под чем я подразумеваю понижение частоты дискретизации, с которой был записан звук. Известно, что в компьютере непрерывный аудио сигнал представлен в виде семплов (по-русски, измерений), сделанных в равно отстоящие друг от друга промежутки времени. Например, если звук записан с частотой дискретизации 44.1 кГц, то за одну секунду было сделано 44100 семплов. Здесь я расскажу о том, как восстановить исходный непрерывный сигнал по взятым семплам и как выбирать и менять частоту семплирования (или, что то же самое, частоту дискретизации). Преобразование Фурье. Преобразованим Фурье $F(\xi)$ функции $f(x)$ называется следующая функция: $F(\xi) = \int_{-\infty}^{\infty} f(x) e^{-ix\xi}dx$ обратное преобразование определено как $f(x) = \frac{1}{2\pi}\int_{-\infty}^{\infty} F(\xi) e^{ix\xi}d\xi$ Вопрос о том, когда существует преобразование Фурье и обратимо ли оно (т.е. работает ли вторая формула), очень сложе

На что влияет размер (глубина) семпла?

В этом посте я разберу ещё одну характеристику представления аудио на компьютере — размер или глубину семпла. В теореме Шеннона, с которой мы ознакомились в первом посте, сказано, что семплы нужно брать с определенной частотой, чтобы восстановить исходный сигнал. Однако, в ней ничего не сказано о том, сколько памяти компьютера нужно выделить на один семпл. По сути  ней семплы — это обычные вещественные числа. В компьютере же семплы чаще всего представляются целыми числами в диапазоне от $-2^{n-1}$ до $2^{n-1}-1$ (целые числа со знаком), где $n$ — количество бит на один семпл (bps, bits per sample). С количеством бит на семпл связана другая величина — динамический диапазон (или отношение сигнал-шум, что в данном случае то же самое). Он измеряется в децибелах определяется так: $DR = 20 \lg (2^{n}) \approx 6 n$ Динамический диапазон показывает логарифм отношения самого сильного сигнала к самому слабому. Теперь, что будет, если мы выберем $n$ маленьким? Если взять синусоидальную волн