Как достать соседа или Караоке на GStreamer

Продолжаем освоение фреймворка GStreamer, и сегодня, как и было обещано, рассмотрим подробнее работу с шиной и обработку сообщений различных типов. А в практическом разделе мы напишем небольшое консольное караоке. Итак, начнем! imageШинаНапомню, что приложение на GStreamer состоит из различных элементов, помещенных в конвейер и соединенных между собой. Каждый элемент в процессе своей работы может генерировать сообщения различных типов (описаны ниже). Для того, чтобы собирать эти сообщения и доставлять в порядке очередности в основной поток приложения, используется шина.Каждый конвейер по умолчанию имеет встроенную шину, т.е. ее не нужно создавать или настраивать. Шина извлекается из конвейера следующей функцией:

GstBus * gst_pipeline_get_bus (GstPipeline *pipeline) Есть два способа обработки сообщений на шине — асинхронный и синхронный. Первый способ заключается в том, что к шине подключается обработчик, который по приходу сообщения выполняет какие-то действия. Делается это с помощью функций: guint gst_bus_add_watch (GstBus *bus, GstBusFunc func, gpointer user_data) void gst_bus_add_signal_watch (GstBus *bus) Стоит отметить, что обе эти функции имеют расширенные варианты (*_full), с помощью которых возможна более тонкая настройка обработчика.Второй способ — это постоянный опрос шины на предмет сообщений. Делается это при помощи функции: GstMessage *gst_bus_poll (GstBus *bus, GstMessageType events, GstClockTime timeout) При этом есть возможность забирать из очереди сообщения только определенных типов, а также регулировать продолжительность опроса.Сообщения Каждое сообщение характеризуется источником, типом и временной меткой. Чаще всего нет необходимости реагировать на каждый тип сообщения, однако крайне желательно обрабатывать хотя бы сообщения об ошибках. Типов довольно много, вот самые распространенные: Ошибки, warning’и и информационные сообщения Сообщения о конце потока (End of Stream) Теги Изменения состояний Можно подойти к вопросу об обработке сообщений с другой стороны, а именно подключиться только к интересующим нас сигналам в следующей форме: g_signal_connect (bus, «message: error», G_CALLBACK (cb_message_error), NULL) Важно! Если вы хотите, чтобы обработчик продолжил реагировать на сообщения, он должен возвращать значение TRUE. Если же прописать возврат FALSE, то после обработки отслеживание сообщений этим обработчиком прекратится.Практика А сейчас напишем приложение, которое воспроизводит mp3-файл и подавляет в нем определенный диапазон частот (в который входит вокальный диапазон) с помощью элемента audiokaraoke. При этом на выходе мы получаем трек без вокала. Также в консоль выводится текст песни из файла (в кодировке UTF-8).Конвейер будет выглядеть следующим образом:

image

Убедитесь, что у вас установлен набор плагинов Ugly (gst-plugins-ugly), поскольку в его состав входит mp3-декодер Mad.

А вот и сама программа:

#include

#define SUPRESS_LEVEL 1.0 // от 0.0 до 1.0 #define MONO_LEVEL 1.0 // от 0.0 до 1.0 #define FILTER_BAND 220.0 // от 0.0 до 441.0 #define FILTER_WIDTH 100.0 // от 0.0 до 100.0

static GMainLoop *loop;

static gboolean my_bus_callback (GstBus *bus, GstMessage *message, gpointer data) { switch (GST_MESSAGE_TYPE (message)) { case GST_MESSAGE_ERROR: { GError *err; gchar *debug; gst_message_parse_error (message, &err, &debug); g_print («Error: %s\n», err→message); g_error_free (err); g_free (debug); g_main_loop_quit (loop); break; }

case GST_MESSAGE_EOS: { g_print (»\n\nYou’ve got 100 points!\n»); g_main_loop_quit (loop); break; }

default: break; }

/* возвращаем TRUE, поскольку мы хотим, чтобы обработчик продолжал выполнение своих обязанностей */ return TRUE; }

int main (int argc, char * argv[]) { if (argc!= 3) { g_printerr («Usage: %s audiofile.mp3 lyricsfile.txt\n», argv[0]); return -1; }

GstElement *pipeline, *audiosrc, *parser, *decoder, *converter, *karaoke, *audiosink; GstElement *textsrc, *textsink; GstStateChangeReturn ret; GstBus *bus; guint bus_watch_id;

/* Инициализация */ gst_init (NULL, NULL);

/* Создаем элементы */ pipeline = gst_element_factory_make («pipeline», «pipe»); audiosrc = gst_element_factory_make («filesrc», «audiosrc»); parser = gst_element_factory_make («mpegaudioparse», «parser»); decoder = gst_element_factory_make («mad», «decoder»); converter = gst_element_factory_make («audioconvert», «converter»); karaoke = gst_element_factory_make («audiokaraoke», «karaoke»); audiosink = gst_element_factory_make («autoaudiosink», «audiosink»); textsrc = gst_element_factory_make («filesrc», «textsrc»); textsink = gst_element_factory_make («fdsink», «textsink»);

if (! pipeline || ! audiosrc || ! parser || ! decoder || ! converter || ! karaoke || ! audiosink || ! textsrc || ! textsink) { g_printerr («Unable to create some elements\n»); return -1; }

/* Добавляем элементы в конвейер */ gst_bin_add_many (GST_BIN (pipeline), audiosrc, parser, decoder, converter, karaoke, audiosink, textsrc, textsink, NULL);

/* и связываем их */ if (gst_element_link_many (audiosrc, parser, decoder, converter, karaoke, audiosink, NULL) != TRUE) { g_printerr («Unable to link some elements\n»); gst_object_unref (pipeline); return -1; } if (gst_element_link (textsrc, textsink) != TRUE) { g_printerr («Unable to link text with textsink\n»); gst_object_unref (pipeline); return -1; }

/* Задаем элементам свойства */ g_object_set (audiosrc, «location», argv[1], NULL); g_object_set (textsrc, «location», argv[2], NULL); g_object_set (karaoke, «level», SUPRESS_LEVEL, «mono-level», MONO_LEVEL, «filter-band», FILTER_BAND, «filter-width», FILTER_WIDTH, NULL);

/* Запускаем конвейер */ ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); if (ret == GST_STATE_CHANGE_FAILURE) { g_printerr («Unable to set pipeline to the playing state\n»); gst_object_unref (pipeline); return -1; }

/* Извлекаем шину из конвейера и подключаем к ней обработчик */ bus = gst_element_get_bus (pipeline); bus_watch_id = gst_bus_add_watch (bus, my_bus_callback, NULL); gst_object_unref (bus);

/* Запускаем основной поток приложения */ loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop);

/* Освобождаем ресурсы */ gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline); g_source_remove (bus_watch_id); g_main_loop_unref (loop);

return 0; } Компилируем как обычно $ gcc -Wall -o karaoke karaoke.c $(pkg-config --libs --cflags gstreamer-1.0) и запускаем $ ./karaoke audiofile.mp3 lyricsfile.txt Результаты Я пробовал запускать приложение (с настройками фильтра по умолчанию) на следующих песнях: Песня Эффект Tool — Parabola Неплохо вырезан вокал, но подавило много инструментала (особенно флажолеты в соло) At the Drive-In — Rolodex Propaganda Инструментал сохранен хорошо, но достаточно сильно пробивается вокал (видимо, ввиду его высоты) Radiohead — Jigsaw Falling into Place Здесь получилось довольно интересно — основной вокал задавлен, а бэк остался U2 — Raised by Wolves Гитара сохранена практически идеально Понятно, что результат сильно зависит от настроек фильтра и от самой песни, поэтому можно поиграть с его параметрами, указанными через #define и посмотреть, что из этого выйдет.

© Habrahabr.ru