Захотел написать простейшую программу, которая будет считывать звук с микрофона. Как-то очень сложно получилось, это тупо абсолютный минимум, чтобы просто печатать сэмплы в stdout:
#include <pipewire/pipewire.h>
#include <spa/param/audio/format-utils.h>
#include <stdio.h>
struct stream_data {
struct pw_stream *stream;
enum spa_audio_format audio_format;
};
static void main_loop_signal(void *data, int signal_number);
static void stream_param_changed(void *data, uint32_t id, const struct spa_pod *param);
static void stream_param_format_changed(void *data, const struct spa_pod *param);
static void stream_process(void *data);
int main(int argc, char *argv[]) {
pw_init(&argc, &argv);
struct pw_main_loop *main_loop = pw_main_loop_new(NULL);
struct pw_loop *loop = pw_main_loop_get_loop(main_loop);
pw_loop_add_signal(loop, SIGINT, main_loop_signal, main_loop);
pw_loop_add_signal(loop, SIGTERM, main_loop_signal, main_loop);
struct pw_properties *stream_properties = pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY,
"Capture", PW_KEY_MEDIA_ROLE, "Accessibility", NULL);
struct pw_stream_events stream_events = {
.version = PW_VERSION_STREAM_EVENTS,
.param_changed = stream_param_changed,
.process = stream_process,
};
struct stream_data stream_data;
struct pw_stream *stream = pw_stream_new_simple(loop, "tuktuk", stream_properties, &stream_events, &stream_data);
stream_data.stream = stream;
const struct spa_pod *stream_connect_params[1];
uint8_t spa_pod_builder_buffer[1024];
struct spa_pod_builder spa_pod_builder = SPA_POD_BUILDER_INIT(spa_pod_builder_buffer, sizeof(spa_pod_builder_buffer));
struct spa_audio_info_raw spa_audio_info_raw = SPA_AUDIO_INFO_RAW_INIT(.format = SPA_AUDIO_FORMAT_UNKNOWN);
stream_connect_params[0] = spa_format_audio_raw_build(&spa_pod_builder, SPA_PARAM_EnumFormat, &spa_audio_info_raw);
pw_stream_connect(stream, PW_DIRECTION_INPUT, PW_ID_ANY, PW_STREAM_FLAG_AUTOCONNECT, stream_connect_params,
sizeof(stream_connect_params) / sizeof(stream_connect_params[0]));
pw_main_loop_run(main_loop);
pw_main_loop_destroy(main_loop);
pw_deinit();
}
static void main_loop_signal(void *data, int signal_number) {
struct pw_main_loop *main_loop = data;
pw_main_loop_quit(main_loop);
}
static void stream_param_changed(void *data, uint32_t id, const struct spa_pod *param) {
switch (id) {
case SPA_PARAM_Format:
stream_param_format_changed(data, param);
break;
}
}
static void stream_param_format_changed(void *data, const struct spa_pod *param) {
uint32_t media_type;
uint32_t media_subtype;
if (spa_format_parse(param, &media_type, &media_subtype) < 0) {
fprintf(stderr, "Failed to parse format");
return;
}
if (media_type != SPA_MEDIA_TYPE_audio || media_subtype != SPA_MEDIA_SUBTYPE_raw) {
fprintf(stderr, "stream_param_format_changed media_type=%u media_subtype=%u\n", media_type, media_subtype);
return;
}
struct spa_audio_info_raw audio_info_raw;
spa_format_audio_raw_parse(param, &audio_info_raw);
enum spa_audio_format audio_format = audio_info_raw.format;
if (audio_format != SPA_AUDIO_FORMAT_DSP_F32) {
fprintf(stderr, "stream_param_format_changed audio_format=%d\n", audio_format);
}
}
static void stream_process(void *data) {
struct stream_data *stream_data = data;
struct pw_stream *stream = stream_data->stream;
struct pw_buffer *pw_buffer = pw_stream_dequeue_buffer(stream);
if (pw_buffer == NULL) {
pw_log_warn("out of buffers: %m");
return;
}
struct spa_buffer *spa_buffer = pw_buffer->buffer;
struct spa_data *spa_data = &spa_buffer->datas[0];
struct spa_chunk *spa_chunk = spa_data->chunk;
float *samples = spa_data->data;
uint32_t sample_count = spa_chunk->size / sizeof(float);
for (uint32_t i = 0; i < sample_count; i++) {
printf("%f\n", samples[i]);
}
pw_stream_queue_buffer(stream, pw_buffer);
}
Почему всё так сложно? Должно же быть проще. Зачем аудио-библиотеке выдумывать какие-то main loop-ы, это же не её дело. Почему такие хитрые API. Почему конфигурация через строки. Сложно, очень сложно, надо проще. И все хвалят этот pipewire. А я вот не хвалю, мне не понравилось.
Ещё добавлю, что документации там вообще около нуля. Вот наглядный пример: pw_main_loop_new
Что делает функция pw_main_loop_new? «Create a new main loop.». Да что вы говорите. Что она возвращает? «a newly allocated Main Loop». Да что вы говорите. А бывают ли ошибки? Ой, не написано. А что передаётся в параметре props? Ой, не написано. И это ещё хорошо документированная функция, в куче других и того нет.