Звуковая библиотека BASS
Существует множество библиотек для обработки звука. Если вас, как и меня интересует использование подобных вещей и вы не знаете — с чего начать, добро пожаловать. А в качетве объекта для экспериментов выберем аудиобиблиотеку BASS.
О библиотеке
Сталкиваясь со всяким самодельным (условно) ПО для учета устных ответов учащихся, заметил присутствие некой bass.dll в папке приложения. Причем приложений было много, а библиотека одна.
Погуглив инфу по BASSу обнаружил следующий ресурс. На главной странице есть раздел Licensing, в котором написано BASS is free for non-commercial use. Тоесть басс бесплатен для некоммерческого использования. Вот и попользуем его некоммерчески.
Первая компиляция
Сразу оговорюсь, что проект будем рассматривать для Linux. Все манипуляции можно провернуть в ОС AV Linux.
На главной странице сайта Un4seen Developments щелкнем по кнопке download напротив картинки с пингвином. Полученный архив можно распаковать в любую удобную для вас папку. Что мы видим внутри? Здесь есть справка в формате .chm, что важно, а также куча папок с примерами и разделяемые библиотеки с make-файлами.
Так как начинать нужно с простого, откроем папку contest. Внутри один исходник и один make-файл. Всё, что нужно сделать — это открыть теминал в этой папке и выполнить команду make. Повторюсь, что в AV Linux компиляция проходит без проблем.

Теперь в этом же окне откроем полученный бинарник командой ./contest
Вывод будет следующий:
BASS simple console player
usage: contest [-l] [-d #] <file> -l = list devices -d = device number
Из вывода ясно, что программа поддерживает две команды: «-l» для вывода списка аудиоустройств и «-d» для выбора устройства из этого списка с указанием звукового файла.
Заглянув в исходник можно заметить такой фрагмент:
// try streaming the file/url
if ((chan = BASS_StreamCreateFile(FALSE, argv[filep], 0, 0, BASS_SAMPLE_LOOP)) || (chan = BASS_StreamCreateURL(argv[filep], 0, BASS_SAMPLE_LOOP, 0, 0)))
Таким образом, из написанного видно, что программа открывает файл для воспроизведения в первую очоредь. Если загрузка не удалась, то имя файла интерпретируется как URL и откравется поток по данному URL. Воспользуемся этим свойством. Воспроизведем, например что-нибудь с данного ресурса.
В первую очередь узнаем, какими устройствами воспроизведения мы располагаем. Выполним команду «./contest -l«, найдем устройство Default и начнем воспроизведение. Результат этого процесса на рисунке ниже.

Изучаем исходник и выделяем нужное
Как правило, фирменные исходники даже самых простых программ написаны очень качественно и содержат массу украшательств и прочих интересных фишек. Лично мне эти украшательства создают препятствия для понимания сути происходящих процессов.
Среди папок с примерами создадим свою и скопируем в нее makefile из contest и добавим файл mytest.c. В файле makefile заменим значение параметра TARGET на mytest.
О функциях, необходимых для простейшего запуска
BASS_GetVersion(). Так как версия разделяемой библиотеки и заголовочных файлов может разниться, следует проверить сходство этих версий.
BASS_SetConfig(BASS_CONFIG_NET_PLAYLIST, 1). Вот и настало время заглянуть в справку. В документации сказано, что мы таким образом устанавливаем возможность обрабатывать плейлисты, состоящие из URL. Тоесть можно в качестве входного файла подать PLS или M3U файл. Существуют так называемые «загрязненные» плейлисты (среди списка песен может быть адрес другого плейлиста). Глубина воспроизведения этого добра регулирется флагом BASS_CONFIG_NET_PLAYLIST_DEPTH.
BASS_Init(device, 44100, 0, 0, NULL). Как видно из названия — инициализация устройства. Если device равно -1, то выбирается устройство по умолчанию, если 0 — устройство no sound, если 1 — первое реальное устройство и т.д. Далее идет частота дискретизации, набор флагов, идентификатор окна в Windows (для DirectSound), идентификатор класса для DirectSound.
BASS_StreamCreateFile(FALSE, argv[filep], 0, 0, BASS_SAMPLE_LOOP). FALSE в данном случае говорит о чтении файла, а не фрагмента из памяти (TRUE). argv[filep] — имя файла для чтения. Далее идут смещение (для случая с файлом) и длина воспроизводимого фрагмента. Здесь 0 означает, что файл будет воспроизведен полностью. Последний флаг говорит нам о циклическом воспроизведении. Если процедура загрузки выполнена успешно, функция вернет идентификатор потока.
BASS_ChannelGetLength(chan, BASS_POS_BYTE). Получаем длину записи в байтах.
BASS_ChannelBytes2Seconds(chan, pos). Пересчет длины записи в байтах в секунды.
BASS_ChannelPlay(chan, FALSE). Воспроизведение потока (канала). Второй параметр отвечает за перезапуск фрагмента (это если не вдаваться в подробности).
BASS_ChannelIsActive(chan). Фактически предыдущая функция возвращает управление основной программе, доверяя воспроизведение отдельному процессу. В следствии чего, неплохо было бы знать состояние нашего канала (воспроизводится, остановлен, подгружается и т.д.).
BASS_ChannelGetLevel(chan). В ходе воспроизведения получаем текущий уровень звуковой волны. Причем возвращаемое двойное слово (DWORD) содержит информацию об уровнях правого и левого каналов. Если мы хотим получить уровень левого канала, берем младшее слово LOWORD, если правого — HIWORD.
BASS_ChannelGetPosition(chan, BASS_POS_BYTE). Возвращает текущую позицию воспроизведения в байтах (которые, опять же, можно перевести в секунды).
BASS_Free(). Освобождает всю память, связанную с нашими аудиоресурсами.
Шаг 1. Каркасс
#include <stdlib.h>
#include <stdio.h>
#include "bass.h"
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
void main(int argc, char* argv[]) {
return;
}
Шаг 2. Работа с аргументами
Для начала будем считать, что имя файла передается в качестве первого аргумента командной строки. Перед return допишем следующие строки:
if(argc < 2) {
printf("Syntax: mytest <audio file>\n");
return;
};
Шаг 3. Проверка версий
Соответствует ли библиотека? Добавим следующий фрагмент.
if(HIWORD(BASS_GetVersion()) != BASSVERSION) {
printf("An incorrect version of BASS was loaded");
return;
};
Шаг 4. Инициализация
Инициализируем устройство по умолчанию:
int device = 1;
if(!BASS_Init(device, 44100, 0, 0, NULL)) {
printf("Can't init device!");
BASS_Free();
return;
};
Шаг 5. Загрузка файла и получение его параметров
Объявим переменные для канала, его состояния, текущего времени, уровня сигнала, длины трека и текущей позиции воспроизведения. Если файл не удастся открыть, выведем соответствующее сообщение. В противном случае продемонстрируем длину аудиоданных в байтах и в секундах.
DWORD ch, a, time, level;
QWORD len, pos;
if((ch = BASS_StreamCreateFile(FALSE, argv[1], 0, 0, BASS_SAMPLE_LOOP))) {
len = BASS_ChannelGetLength(ch, BASS_POS_BYTE);
if(len != -1) {
printf("Length in bytes: %u\n", len);
printf("Length in seconds: %u\n", BASS_ChannelBytes2Seconds(ch, len));
};
} else {
printf("Can't open file \"%s\"\n", argv[1]);
return;
};
Шаг 6. Воспроизведение
Теперь можно начать процесс воспроизведения.
BASS_ChannelPlay(ch, FALSE);
while(a = BASS_ChannelIsActive(ch)) {
level = BASS_ChannelGetLevel(ch);
pos = BASS_ChannelGetPosition(ch, BASS_POS_BYTE);
time = BASS_ChannelBytes2Seconds(ch, pos);
printf(" - %u:%02u - L ", time / 60, time % 60);
if(a == BASS_ACTIVE_STALLED) {
printf("- buffering: %3u%% -", (DWORD)BASS_StreamGetFilePosition(ch, BASS_FILEPOS_BUFFERING));
} else {
printf("%05u : %05u R", LOWORD(level), HIWORD(level));
};
printf("\r");
fflush(stdout);
usleep(50000);
};
printf("\r \r");
BASS_Free();
Пока канал активен (файл воспроизводится), получаем текущий уровень сигнала, текущую позицию, переводим позицию в секунды, выводим время на экран. Если файл подгружается, выводим состояние процесса, в противном случае отображаем уровни левого и правого каналов в виде числовых значений. Затем очищаем буфер для stdout и делаем паузу в 50000 микросекунд.
Особенности linux
Вот собственно и вся программа. Но в ней есть одна деталь — трек вы будете слушать циклически без возможности его прервать. К тому же, если выполнить команду Ctrl+C в терминале, работа BASS будет завершена некорректно. Так как в стандартных библиотеках для linux нет аналога функции _kbhit() для проверки нажатия клавиши без прерывания программы, авторы contest.c реализовали эту функцию.
int _kbhit()
{
int r;
fd_set rfds;
struct timeval tv = { 0 };
struct termios term, oterm;
tcgetattr(0, &oterm);
memcpy(&term, &oterm, sizeof(term));
cfmakeraw(&term);
tcsetattr(0, TCSANOW, &term);
FD_ZERO(&rfds);
FD_SET(0, &rfds);
r = select(1, &rfds, NULL, NULL, &tv);
tcsetattr(0, TCSANOW, &oterm);
return r;
}
Чтобы можно было прервать воспроизведение нажатием клавиши, добавим данный фрагмент и заголовочный файл termios.h в наш исходник. А текст while(a = BASS_ChannelIsActive(ch)) заменим на while(!_kbhit() && (a = BASS_ChannelIsActive(ch)))
Результат всех перечисленных действий на рисунке ниже.

Полезные ссылки
http://www.un4seen.com/doc/#bass/bass.html — документация
http://www.un4seen.com/bass.html — главная
Добавить комментарий