BMSTU/02-dspmd-03-lab-report.tex

264 lines
17 KiB
TeX
Raw Normal View History

2023-01-27 22:32:16 +03:00
\documentclass[a4paper,fontsize=14bp]{article}
\input{../common-preamble}
\input{../bmstu-preamble}
\input{../fancy-listings-preamble}
\numerationTop
\begin{document}
\thispagestyle{empty}
\makeBMSTUHeader
% ... работе, номер, тема, предмет, ?а, кто
\makeReportTitle{лабораторной}{3}{Звуковой эффект}{Микропроцессорные устройства обработки сигналов}{}{А.И. Германчук}
\newpage
\thispagestyle{empty}
\tableofcontents
\newpage
\pagestyle{fancy}
\section{Цель}
Целью работы является разработка и исследование программы обработки сигналов на языке программирования Си, реализующей звуковой эффект, определенный в индивидуальном задании (контроллер динамического диапазона, ограничитель).
\section{Описание звукового эффекта}
Контроллер динамического диапазона - это адаптивная регулировка динамического диапазона сигнала. Динамический диапазон - это соотношение между наибольшими и наименьшими значениями, которые может принять определенная величина. Существует несколько типов контроллеров. В данной лабораторной работе будет рассматриваться ограничитель. Назначение ограничителя состоит в том, чтобы обеспечить контроль над самыми высокими пиками сигнала, но при этом, как можно меньше изменять динамику сигнала. Это достигается за счёт использования характеристической кривой с бесконечным отношением \cite[с. 109]{dsp:dafx}. Ограничитель динамического диапазона подавляет громкость звуков, пересекающих заданный порог (рисунок \hrf{pic:limiter}).
\begin{figure}[H]
\centering
\def\svgwidth{100mm}
\input{pics/02-dsp-03lab-limiter.pdf_tex}
\caption{Иллюстрация работы ограничителя сигнала}
\label{pic:limiter}
\end{figure}
Для достижения плавности кривой применяемого ослабления громкости используются такие параметры, как время атаки и высвобождения. Время атаки и время высвобождения соответствуют времени, за которое сигнал увеличивается или уменьшается до конечного значения (рисунок \hrf{pic:limiter-explain}). На рисунке видно, что при отсутствии времени атаки и освобождения усиление применяется сразу, создавая неплавные «обрезанные» кривые (жёсткое урезание).
\begin{figure}[H]
\centering
\def\svgwidth{170mm}
\input{pics/02-dsp-03lab-limiter-explain.pdf_tex}
\caption{Иллюстрация влияния времени атаки и освобождения}
\label{pic:limiter-explain}
\end{figure}
Ограничитель обычно используется для того, чтобы «поймать» самые громкие моменты источника, уменьшив их таким образом, чтобы защитить от нежелательных искажений и сохранить целостность общего баланса звука.
\section{Математическое описание ограничителя}
\label{sect:description}
Динамическая обработка выполняется усилительными устройствами, где коэффициент усиления автоматически регулируется уровнем входного сигнала. Динамическая обработка проходит в несколько этапов.
\begin{enumerate}
\item Сначала используется схема определения амплитуды/уровня по алгоритму PEAK \cite[с. 107]{dsp:dafx}.
В начале алгоритма значение переменной $X_{peak}$ равно нулю. Далее сравнивается модуль поступающего сигнала $|x(n)|$ с предыдущим значением $X_{peak}$. В случае если значение модуля поступающего сигнала больше, то значение $X_{peak}$ для текущего элемента вычисляется с использованием коэффициента $AT$, которое используется, как сокращение для времени атаки (attack time). В ином случае вычисление значения $X_{peak}$ для текущего элемента производится с помощью коэффициента $RT$, то есть времени освобождения (release time). Математическая формула описанного выше алгоритма
\begin{equation}
X_{p}(n) = \begin{cases}
(1 - AT) \times X_{p}(n - 1) + AT \times |X(n)|, |X(n)| > X_{p}(n - 1)\\
(1 - RT) \times X_{p}(n - 1), |X(n)| \leq X_{p}(n - 1),
\end{cases}
\end{equation}
где
\begin{itemize}
\item [] $X_{p}$ - пиковое значение $X_{peak}$;
\item [] $AT$ время атаки;
\item [] $RT$ время освобождения;
\item [] $n$ номер текущего элемента.
\end{itemize}
\item После определения значения $X_{peak}$ выполняется функция для получения коэффициента усиления $f$ по формуле
\begin{equation}
f(n) = min \bigg( 1; \frac{lt}{X_{peak}n} \bigg),
\end{equation}
где
\begin{itemize}
\item [] $n$ номер текущего элемента;
\item [] $lt$ пороговое значение.
\end{itemize}
\item После вычисления коэффициента усиления используется сглаживающий фильтр для предотвращения слишком резких изменений усиления. Для его реализации используется формула
\begin{equation}
cfc = \begin{cases}
AT, f(n) < f(n-1)\\
RT, f(n) \geq f(n-1),
\end{cases}
\end{equation}
где $n$ номер текущего элемента.
\item Далее вычисляется множитель для регулирования входного сигнала по формуле
\begin{equation}
g(n) = (1 - cfc) \times g(n - 1) + cfc \times f(n)
\end{equation}
\item После получения искомого множителя входного сигнала, его значение умножается на задержанный в буфере задержки входной сигнал. Сигнал задерживается для того, чтобы компенсировалась любая задержка при вычислении множителя \cite[с. 106]{dsp:dafx}.
\end{enumerate}
Результирующая комбинированная система изображена на рисунке \hrf{pic:limiter-scheme} \cite[с. 109]{dsp:dafx}.
\begin{figure}[H]
\centering
\def\svgwidth{165mm}
\input{pics/02-dsp-03lab-limiter-scheme.pdf_tex}
\caption{Принципиальная схема алгоритма работы ограничителя}
\label{pic:limiter-scheme}
\end{figure}
\section{Алгоритм обработки сигналов}
Алгоритм реализован на основе раздела \hrf{sect:description}. В реализации алгоритма учитывается средняя частота дискретизации современных аудиофайлов 44,1 КГц. Алгоритм буферизует отсчёты на 6 миллисекунд, то есть если 44 отсчета - это одна милисекунда, то величина буфера должна быть $44 * 6 = 264$ отсчётов.
На вход алгоритма, вычисляющего значение коэффициента, принимается единичный отсчёт. Пороговое значение задаётся константой \code{LT} в теле функции.
\subsection{Основная программа \code{main}}
Функция \code{main} вызывается при запуске программы и реализует следующие шаги:
\begin{enumerate}
\item инициализация кодека;
\item чтение из кодека текущего отсчета сигнала по прерыванию от интерфейса I2S \code{x}, \code{x = I2S2_W0_MSW_R};
\item вычисление и корректировка коэффициента g и сохранение текущего отсчёта в буфер;
\item получение выходного отсчета сигнала \code{y}, путём умножения буферизованного отсчёта на вычисленный на текущем шаге коэффициент \code{_smpy(g, leftBuffer[qRead++]};
\item смещение флагов чтения и записи данных в буфер, ожидание готовности контроллера прерываний;
\item запись значений обоих каналов обратно в контроллер I2S;
\item переход на шаг 2.
\end{enumerate}
\subsection{Функция ограничителя}
Фактически, функция ограничителя разделена на две части:
\begin{enumerate}
\item приём отсчёта, буферизация и вычисление коэффициента ограничителя;
\item чтение из очереди и умножение буферизованного отсчёта на коэффициент.
\end{enumerate}
Функция расчёта коэффициента реализует следующие шаги алгоритма:
\begin{enumerate}
\item получение входного значения \code{x};
\item получение модуля отсчёта;
\item определение коэффициента \code{cfc} для вычисления уровня;
\item вычисление уровня \code{xpeak}, \code{xpeak = (1 - cfc) * xpeak + coeff * x};
\item определение коэффициента усиления \code{f}, \code{f = min(1, LT / xpeak)};
\item определение коэффициента \code{cfc} для вычисления управляющего коэффициента \code{g = (1 - cfc) * g + cfc * f};
\item возврат коэффициента \code{g} из функции.
\end{enumerate}
Код, вызывающий функцию, кроме буферизации прочитанного значения (\code{leftBuffer[qWrite++] = left;}) осуществляет умножение ранее буферизованного отсчёта на текущее скорректированное значение коэффициента фильтра \code{out_left = _smpy(g, leftBuffer[qRead++])} и запись выходного отсчета на шину контроллера I2S \code{I2S2_W0_MSW_W = out_left;}
\section{Выводы}
В результате выполнения работы была разработана программа обработки сигнала на языке программирования Си, реализующая звуковой эффект - контроллер динамического диапазона, а именно, ограничитель. В ходе работы были получены графики входных и выходных отсчетов и по ним исследован результат применения звукового эффекта на входной сигнал.
\nocite{gost:texts}
\newpage
\printbibliography[heading=bibintoc, title={Список литературы}, resetnumbers=1]
\newpage
\appendix
\section*{Приложения}
\addcontentsline{toc}{section}{Приложения}
\renewcommand{\thesubsection}{\Alph{subsection}}
\subsection{Полные листинги программ}
\label{appendix:fulls}
\begin{lstlisting}[language=C,style=CCodeStyle,caption={Функция вычисления коэффициента ограничителя}, label=code:prog-coeff]
Int16 oneCountCoefficient(Int16 x) {
const Int16 LT = 28576;
Int16 at_cfc = 9830;
Int16 rt_cfc = 328;
Int16 a = abs(x); // <@\lh{dkgreen}{модуль значения текущего отсчёта}@>
Int16 cmp;
xpeak = (a > xpeak) // <@\lh{dkgreen}{коэффициент x пиковое}@>
? (_smpy((32767 - at_cfc), xpeak) + _smpy(at_cfc, a))
: _smpy((32767 - rt_cfc), xpeak);
if (abs(LT) > abs(xpeak)) // <@\lh{dkgreen}{коэффициент cmp}@>
cmp = (Int16)(((Int32) LT << 15) / ((Int32) xpeak));
Int16 f = (cmp < 32767) ? cmp : 32767;
Int16 cfc = (f < g) ? at_cfc : rt_cfc; // <@\lh{dkgreen}{коэффициент cfc}@>
// <@\lh{dkgreen}{возврат вычисленного коэффициента g}@>
return _smpy((32767 - cfc), g) + _smpy(cfc, f);
}
\end{lstlisting}
\begin{lstlisting}[language=C,style=CCodeStyle,caption={Полный листинг программы на языке С}, label=code:prog-c]
// DSP, board libraries
#include "C5515.h"
#include "gpio.h"
#include "i2c.h"
#include "i2s.h"
#include "stdio.h"
// <@\lh{dkgreen}{заголовок с функцией oneCountCoefficient}@>
#include "mylimiter.h"
#define AIC3204_I2C_ADDR 0x18
#define Rcv 0x08
#define Xmit 0x20
extern Int16 aic3204_stereo_in1();
volatile Int16 xpeak = 1;
volatile Int16 g = 1;
Int16 oneCountCoefficient(Int16 x);
void main(void) {
// <@\lh{dkgreen}{инициализация}@>
c5515_init();
// <@\lh{dkgreen}{конфигурирование параллельного порта}@>
SYS_EXBUSSEL &= ~0x7000;
SYS_EXBUSSEL |= 0x1000;
// <@\lh{dkgreen}{конфигурирование последовательного порта}@>
SYS_EXBUSSEL &= ~0x0C00;
SYS_EXBUSSEL |= 0x0400;
c5515_GPIO_init();
c5515_GPIO_setDirection(GPIO10, GPIO_OUT);
c5515_GPIO_setOutput(GPIO10, 1); // <@\lh{dkgreen}{вывод AIC3201 из reset}@>
I2C_init(); // <@\lh{dkgreen}{инициализация I2C}@>
// <@\lh{dkgreen}{I2S настройки}@>
I2S2_SRGR = 0x0015;
I2S2_ICMR = 0x0028;
I2S2_CR = 0x8012;
// Режим стерео входа
aic3204_stereo_in1();
#define BUF_LENGTH 264
Int16 left, right; // <@\lh{dkgreen}{отсчеты до примененого эффекта}@>
Int16 out_left = 0; // <@\lh{dkgreen}{отсчеты после примененого эффекта}@>
Int16 leftBuffer[BUF_LENGTH]; // <@\lh{dkgreen}{буфер}@>
Int16 qWrite = 0;
Int16 qRead = -255;
while(1){
// <@\lh{dkgreen}{Ожидание прерывания по получению отсчёта}@>
while ((Rcv & I2S2_IR) == 0);
left = I2S2_W0_MSW_R; // <@\lh{dkgreen}{16-битное значение левого канала}@>
right = I2S2_W1_MSW_R; // <@\lh{dkgreen}{16-битное значение правого канала}@>
g = oneCountCoefficient(left); // <@\lh{dkgreen}{текущий коэффициент}@>
leftBuffer[qWrite++] = left; // <@\lh{dkgreen}{буферизация отсчёта}@>
if (qWrite == BUF_LENGTH)
qWrite = 0; // <@\lh{dkgreen}{не дать флагу записи переполниться}@>
if (qRead >= 0) {
// <@\lh{dkgreen}{прочитать из буфера и умножить на коэффициент}@>
out_left = _smpy(g, leftBuffer[qRead++]);
if (qRead == BUF_LENGTH)
qRead = 0; // <@\lh{dkgreen}{не дать флагу чтения переполниться}@>
} else {
++qRead;
}
while ((Xmit & I2S2_IR) == 0); // <@\lh{dkgreen}{Ждём контроллер прерывания}@>
// <@\lh{dkgreen}{записать изменённое}@>
I2S2_W0_MSW_W = out_left;
// <@\lh{dkgreen}{записать не изменённое}@>
I2S2_W1_MSW_W = right;
}
}
\end{lstlisting}
\end{document}