Последние записи
- Преобразовать массив байт в вещественное число (single)
- TChromium (CEF3), сохранение изображений
- Как в Delphi XE обнулить таймер?
- Изменить цвет шрифта TextBox на форме
- Ресайз PNG без потери прозрачности
- Вывод на печать графического файла
- Взаимодействие через командную строку
- Перенести программу из Delphi в Lazarus
- Определить текущую ОС
- Автоматическая смена языка (раскладки клавиатуры)
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
27th
Фев
Немного о DSP. Часть 1
Posted by Chas under Журнал, Статьи
Благодаря своему универу, пришлось иметь дело с такой вещью, как Digital Signal Processing (DSP). DSP* мне всегда казалось жутко интересным занятием, когда имеешь дело с большим количеством численных данных, применяешь какие-то умные математические приемы и в итоге случается чудо – все работает, и работает как надо. В этой статье попробую начать тему, написать простенький пример. С чего начать? Методы DSP используются для обработки всевозможных данных: звука, изображений, показаний различных датчиков и т.д. Здесь я рассмотрю только звук. Я попробую написать простейшую программу, которая открывает звуковой файл и производит некоторые действия над ним. Вот и плавно переходим к первой части.
Мират Каденов
by mirat http://mathdev.org/mirat
Представление данных
Что такое звук? На самом деле, любой звук состоит из множества волн различной частоты. Самое замечательно свойство волн – это то, что волны различной частоты «никак не влияют» друг на друга. Например, общее давление в какой-то точке среды, по которой проходят несколько волн, будет просто суммой звукового давления каждой из волн (принцип суперпозиции). Независимость волн разных частот друг от друга позволяет разложить звук на составляющие колебания и анализировать их отдельно. Именно так поступает наш мозг, поэтому мы можем разделять два и более источника звука и воспринимать звук от каждого в отдельности.
Программисты со значениями давления звуковой волны не имеют дела. Микрофон http://ru.wikipedia.org/wiki/Микрофон преобразует колебания воздуха в колебания тока в его цепи, затем это все проходит через различные усилители, фильтры и, в конечном итоге, поступает в так называемый аналого-цифровой преобразователь (АЦП http://ru.wikipedia.org/wiki/АЦП).
Напряжение на входе АЦП должно находиться в определенном диапазоне, к примеру, от 0 до 1.5 вольта (это характеристика конкретного АЦП). Еще у АЦП есть такой параметр как разрядность – это количество битов на выходе. Если уж совсем просто, то АЦП замеряет напряжение на входе и выдает на выходе число. Естественно, что даже на отрезке от 0 до 1.5 вольт напряжение, как любая уважающая себя непрерывная физическая величина, может принимать бесконечно много значений. Количество возможных значений на выходе АЦП ограничено его разрядностью. Следовательно, АЦП производит квантование сигнала http://ru.wikipedia.org/wiki/Квантование_(обработка_сигналов). На выходе этого устройства уже имеем двоичный код — числа (их называют сэмплами или отсчетами) с которыми цифровой компьютер уже умеет работать.
Естественно, что если исходный звук был суммой какого-то количества простейших колебаний, то оцифрованный звук будет представлять собой сумму этих самых колебаний, как если бы их оцифровали отдельно и затем результат сложили посэмпельно (слово-то, какое). На этом и основываются все методы DSP.
Цифровой звук может быть представлен в различных форматах. Величинами, характеризующими конкретный формат звука являются:
- частота дискретизации (количество сэмплов в секунду);
разрядность (разрядность одного сэмпла. Самые распространенные – это 8 и 16 бит на сэмпл, то есть один сэмпл представляет собой 8битное или 16-битное число, соответственно);
количество каналов – моно (1 канал), стерео (2 канала) и больше (многоканальный звук).
Если количество каналов больше 1, то сэмплы в потоке идут чередуясь: сэмпл первого канала, затем второго, третьего и т.д., затем снова для первого, для второго…
Частота дискретизации для цифрового звука обычно берется где-то около значения 44100 Гц (используется в AudioCD). Почему? Считается, что человек не воспринимает звуковые колебания с частотой выше 20-ти килогерц. А по теореме Котельникова*** (есть такая, но об этом позже) для представления и затем однозначного восстановления сигнала с ограниченным спектром, то есть сигнал не содержит колебаний с частотой выше некоторого F, достаточно частоты дискретизации 2F.
Писать обработчик WAV-файлов неинтересно. Поэтому я решил поэкспериментировать с захватом звука. Да к тому же с преобразованием звука на лету. Для записи и воспроизведения звука будем использовать OpenAL. Это позволит нам не сильно напрягаться, но в то же время обеспечит полный доступ на низком уровне к звуковым данным.
Итак, начнем…
Так, надо запастись библиотекой OpenAL. В репозитарии Ubuntu 9.04 шел OpenAL версии 1.4.272, в котором замечен страшный глюк со сбросом состояния источника. Так что советую качать новый OpenAL (я использовал openal-soft-1.10.622). Затем надо создать проект в любой среде разработки, подключить OpenAL и вот первый мегалистинг (если кому лень выделять и копировать, то внизу статьи есть ссылка на файл <dsp1.cpp>):
#include <al.h>
#include <alc.h>
const int BUF_COUNT = 5;
// Главный класс, который и отвечает за всю работу со звуком
class SoundManager
{
// Как говорится, "OpenAL related stuff"
ALCdevice * in_device;
ALCdevice * out_device;
ALCcontext * context;
ALuint al_source;
ALuint al_buffers[BUF_COUNT];
// Здесь уже наше, промежуточный буфер
ALshort *play_buf;
int read_buf_size;
int play_buf_size;
public:
SoundManager()
{
// Здесь значения надо подбирать экспериментально
read_buf_size = 44100;
play_buf_size = 1024;
play_buf = new ALshort [play_buf_size];
// Инициализируем OpenAL и создаем всякие штучки
out_device = alcOpenDevice (0);
context = alcCreateContext(out_device, 0);
alcMakeContextCurrent(context);
in_device = alcCaptureOpenDevice (0, 44100, AL_FORMAT_MONO16, read_buf_size);
alGenSources(1, &al_source);
alGenBuffers(BUF_COUNT, al_buffers);
// Заполняем первую очередь
for (int i = 0; i < play_buf_size; ++i ) play_buf = 0;
for (int i = 0; i < BUF_COUNT; ++i)
{
alBufferData(al_buffers, AL_FORMAT_MONO16, play_buf, play_buf_size * sizeof(ALshort), 44100);
}
// Здесь начинаем запись и воспроизведение
alSourceQueueBuffers(al_source, BUF_COUNT, al_buffers);
alSourcePlay(al_source);
alcCaptureStart(in_device);
}
void update()
{
ALint samples, val, state;
// Если источник вдруг перестал играть, то пнем его
alGetSourcei (al_source, AL_SOURCE_STATE, &state);
if (state != AL_PLAYING) alSourcePlay(al_source);
alcGetIntegerv(in_device, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
alGetSourcei (al_source, AL_BUFFERS_PROCESSED , &val);
// Пока есть свободные буферы воспроизведения и пока есть готовые
// данные, которые надо воспроизвести, считываем из захвата и
// пишем на выход
while ( ( (val--) > 0) && (samples > play_buf_size) )
{
ALuint buffer;
alcCaptureSamples(in_device, play_buf, play_buf_size);
// ... пишем, да только не сразу, а после обработки ;)
process();
// Вытаскиваем последний буфер из очереди, наполняем
// данными и обратно в очередь
alSourceUnqueueBuffers(al_source, 1, &buffer);
alBufferData(buffer, AL_FORMAT_MONO16, play_buf, play_buf_size * sizeof(ALshort), 44100);
alSourceQueueBuffers(al_source, 1, &buffer);
alcGetIntegerv(in_device, ALC_CAPTURE_SAMPLES, sizeof(samples), &samples);
}
}
void process()
{
// "all fun is here"
}
~SoundManager()
{
alcCaptureStop(in_device);
alcCaptureCloseDevice(in_device);
}
};
int main()
{
SoundManager s;
while (1)
{
s.update();
}
}
</alc.h></al.h>
Данная программа просто считывает порциями звук с устройства записи по-умолчанию и пишет в устройство воспроизведения. Здесь в программе реализована работа с потоковыми данными. Как видиим, программа работает только с форматом AL_FORMAT_MONO16, это означает только один канал и 16-битные сэмплы.
Заключение
Но главное здесь это то, что в момент вызова пока что пустого process() кусок звукового потока лежит в play_buf и доступен нам для изменения. Но лезть в сырые звуковые данные не стоит. Единственное, что можно сделать – это, например, домножить все числа на константу. Если константа по модулю будет больше единицы, будет увеличение громкости, если меньше, соответственно, уменьшение. В следующей статье я рассмотрю преобразование Фурье.
Исходники тестового проекта приложены в виде ресурсов непосредственно в архиве с журналом.
Ресурсы
- Исходники проекта http://mathdev.org/sites/default/files/dsp1.cpp_.zip
- Про потоковый звук в OpenAL http://kcat.strangesoft.net/openal-tutorial.html
- Персональный блог автора http://mathdev.org/mirat
Статья из одиннадцатого выпуска журнала «ПРОграммист».
Обсудить на форуме — Немного о DSP. Часть 1
Похожие статьи
Купить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту
пеллетные котлы
Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)