Звуковий сервер jack. Пишемо найпростіший клієнт: практика

Відео: Програмування на Java для початківців # 16 (Client-Server)

Звуковий сервер JACK. Пишемо найпростіший клієнт: практика
Отже, ми створюємо простий клієнт, який вміє:
- Завантажувати хвильової файл (наприклад, WAV або FLAC).
- Підключатися до вхідних портів JACK.
- В петлі (тобто, зацикленість) відтворювати долучення на вхідний порт JACK.

Даний код в недалекому минулому навіть входив у набір програм для мого мікшерного пульта, оформленого з використанням техніки stickerbomb (https://parazitakusok.ru/). Так що це цілком самостійні продукт, який цілком зможе стати справжнім старт-апом для музиканта!

Завантаження файлів покладемо на плечі бібліотеки libsndfile - все її використовують, не гріх і нам! Ось файл Qmake-проекту для програми - назвемо її test01:
---------------testOl.pro --------------;
TARGET = testOl CONFIG + = console
link _ pkgconfig
CONFIG- = app _ bundle
TEMPLATE = app
SOURCES + = main.cpp
PKGCONFIG + = sndfile jack

У цьому файлі ми додали в змінну CONFIG ключ link_pkgconfig, який дозволить підключати бібліотеки через механізм pkgconfig. Далі все парою рядків підключаємо до нашої програми бібліотеки JACK і libsndfile:
PKGCONFIG + = sndfile
jack

Тепер перейдемо до коду самої програми. Це буде консольний додаток, тому обійдемося тільки одним файлом -main.cpp. Мається на увазі, що ви і так вмієте компілювати Qt-програми, проте про всяк випадок - зібрати додаток можна буде, давши дві команди:
qmake
make

Тепер - код самого додатка. Спочатку наведу його повністю, з другорядними коментарями, а найважливіші речі ми розберемо потім окремо. І хай вибачить мене читач за поганий стиль програмування - безліч глобальних змінних тощо. Зараз нам важлива простота викладу.
-----------main.cpp ----------;
#include
#include
// Підключаємо sndfile і JACK
#include
#include
#include
// Задаємо шлях до файлу, який будемо грати
#define fname _ test "/ повний _ шлях _ к / test.wav"
// Клієнтські JACK-порти під лівий і правий канали
jack _ port _t * out_left;
jack _ port _ t * out _ right;
// Буфер для хвильових даних, сюди ми прочитаємо
WAV float * buffer;
// А сюди - відомості про нього
SF _INFO info;
// Лічильник поточного місця в буфері int offset- // Розмір буфера int buffer _ len;
// Функція, яка читає весь WAV і повертає дані з нього // в вигляді буфера, а відомості про фото - в структуру типу SF _INFO float * load _ whole _ sound (const char * fname, SF _ INFO sf) {
// Відкриємо файл
SNDFILE * file = sf_open (fname, SFM _ READ, sf);
// Виділимо пам`ять під буфер в розмірі, що дорівнює кількості каналів,
// помноженому на кількість кадрів
float * buffer = new float [sf.channels * sf.frames];



// Прочитаємо з файлу в буфер
sf _ readf _float (file, buffer, sf.frames);
// Закриємо файл
sf _ close (file);
// І повернемо буфер
return buffer;
}
// Ця функція - серце програми, його ми ще обговоримо int process (jack _nframes _t nframes, void * arg) {
float * outl = (float *) jack _ port _ get _ buffer
(Out _ left, nframes);
float * outr = (float *) jack _ port _ get _ buffer
(Out _ right, nframes);
for (jack _ nframes _ t i = O- i buffer _len) offset = O;
outl [i] = buffer [offset] - offset ++;
outr [i] = buffer [offset] - offset ++;
}
return O;
}
// Ще одна callback-функція, JACK викличе її в разі помилки
void error (const char * desc)
{



qDebug () lt; lt; "JACK error:" lt; lt; desc;
}
int main (int argc, char * argv []) {
QCoreApplication a (argc, argv) - // Читаємо файл в буфер
buffer = load _ whole _ sound (fname _ test, info);
// Обчислюємо довжину буфера
buffer _ len = info.frames * info.channels;
// Скидаємо в нуль лічильник зсуву в буфері offset = O;
// Встановлюємо оброблювач помилок jack _ set _ error _ function (error);
// Намагаємося створити новий JACK-клієнт
jack _client _t * client = jack _client _new ( "testOl");
// Якщо не вийшло - повідомляємо і виходимо if (! Client) {
qDebug () lt; lt; "Jack server is not running" - return l;
}
// Встановлюємо callback, яка дає JACKV порції сигналу
// з буфера
jack _ set _ process _ callback (client, process, O);
// Реєструємо клієнтські порти, які ми підключимо
// до вхідних порту сервера
out _left = jack _ port _register (client, "left",
JACK _ DEFAULT _ AUDIO _ TYPE, JackPortIsOutput, 0);
out _ right = jack _ port _ register (client, "right",
JACK _DEFAULT _AUDIO _TYPE, JackPortIsOutput, O);
// Намагаємося запустити клієнт при цьому клієнт запускаємо
// ДО підключення портів
if (jack _activate (client)! = O) {
qDebug () lt; lt; "Can not activate client" - return l;
}
// Масив для списку портів const char ** ports;
// Отримуємо спісок- запитуємо у JACK тільки фізичні
//(т.е. порти звукової карти) і тільки вхідні - з нашого боку
// вони послужать для виведення звуку
ports = jack _ get _ ports (client,
NULL, NULL,
JackPortIsPhysical | JackPortIsInput);
// Якщо JACK не повернув порти - повідомляємо про помилку і виходимо if (! Ports) {
qDebug () lt; lt; "Can not find physical playback ports" - exit (1);
}
// З`єднуємо наші клієнтські порти з першими двома «серверними»
// портами зі списку і повідомляємо про помилки в разі невдачі
if (jack _connect (client, jack _port _name
(Out _left), ports [O])! = O)
qDebug () lt; lt; "Can not connect output port O" - if (jack _connect (client, jack _port _name
(Out _ right), ports [l])! = O)
qDebug () lt; lt; "Can not connect output port l";
// Звільняємо пам`ять від списку серверних портів free (ports);
// Входимо в цикл з 13 ітерацій під час цього циклу сервер JACK
// викликатиме нашу callback-функцію process
for (int i = 0 i buffer _len) offset = 0-

Мінлива offset зберігає в собі поточну позицію в буфері. Якщо позиція більше розміру буфера, ми скидаємо її в нуль. Таким чином, відтворення буфера почнеться з нульового семпли - це і є «петля». Тепер - такі рядки циклу. Розміщуємо семпл з звукового буфера в буфер лівого порту:
outl [i] = buffer [offset];
Зміщуємо offset на 1, тому що семпл правого каналу йде в наступній осередку буфера:
offset ++;
Беремо семпл правого каналу, кладемо в буфер правого каналу:
outr [i] = buffer [offset];
Знову збільшуємо лічильник, щоб в наступній ітерації знову взяти семпл з лівого каналу:
offset ++ -

Усе! А якби у нас був моно сигнал, то треба в ітерації один і той же семпл копіювати в обидва вихідних буфера: в лівий і правий, не збільшуючи лічильник між цим копіюванням. Ось так:
outl [i] = buffer [offset];
outr [i] = buffer [offset] - offset ++ -

Розглянутий у статті приклад - відправна точка для подальшого програмування з використанням JACK і, треба думати, libsndfile: якщо вашій програмі потрібно читання / запис звукових файлів, то кращої бібліотеки годі й шукати. Тим більше, що вона є практично у всіх дистрибутивах Linux, а також для Windows, OpenBSD, Solaris, QNX, Mac OS X і навіть Irix. Очевидними зручностями libsndfile є простота API і можливість читання відразу в формат з плаваючою точкою - в буфер типу float *. Писати з нуля свою бібліотеку вводу-виводу звукових файлів - заняття захоплююче і розвиваюче, але корисно знати і про вже готовому рішенні.

Звуковий сервер JACK. Пишемо найпростіший клієнт: практика

Наостанок, деякі зауваження. Якщо у вашому JACK-клієнті потрібно мікшування декількох звукових сигналів, то робити це слід саме в функції process. Мікшування полягають в додаванні семплів і впливу на гучність результату - адже чим більше семплів одночасно складається, тим гучнішим буде підсумковий семпл. Для роботи з гучністю є окремі алгоритми, хоча на розум перш за все приходить найпростіше: розділити значення семпли на кількість складених каналів. Цей спосіб хоч і дієвий, проте зіпсує звучання самим мерзенним чином. Про «окремих алгоритмах» докладно розповідати не буду - лише нагадаю, що для зменшення рівня сигналу треба множити його на дробове число, а для збільшення - ділити на дробове.

Також слід звернути увагу на частоту оцифровки ваших звукових даних і частоту, в якій працює сервер. Взагалі по ідеї все звуки ви повинні на ходу переоціфровивать в якусь загальну вихідну частоту. JACK замість вас не буде цього робити. Благо, є libsamplerate (https://mega-nerd.com/SRC) від того ж Еріка де Кастро Лопо (Erik de Castro Lopo), автора libsndfile. Успіхів!

ІНШЕ

Пол девіс: не тільки jack фото

Пол девіс: не тільки jack

Відео: Sean Spicer Press Conference (Melissa McCarthy) - SNL Однак досить про JACK. Інший внесок Пола Девіса у вільний…

Sdl: збірка за допомогою make фото

Sdl: збірка за допомогою make

Відео: C. Урок 12. Компіляція і система збирання (gcc, make)Етап 4. Складання за допомогою make Наведу мінімальний…

Неясності з phonon фото

Неясності з phonon

Від труднощів збірки перейдемо до неясним місцях у використанні Phonon. Phonon підтримує ті мультимедійні формати, які…

Jack в linux: настройка фото

Jack в linux: настройка

Відео: Tutorial: JACK Audio Server: Basic Explanation, Install, and Setup Головне вікно qJackCtl Пора приступити до…

Бесіди про qt: qsettings і xml фото

Бесіди про qt: qsettings і xml

Клас QSettings служить Qt-програмістам вірою і правдою, коли потрібно зберегти або завантажити будь-які налаштування…

» » Звуковий сервер jack. Пишемо найпростіший клієнт: практика