Skip to content

Instantly share code, notes, and snippets.

@mk-fg
Last active October 22, 2019 18:09
Show Gist options
  • Save mk-fg/7c3bc67ec8a13541182e0139f0a1a5b4 to your computer and use it in GitHub Desktop.
Save mk-fg/7c3bc67ec8a13541182e0139f0a1a5b4 to your computer and use it in GitHub Desktop.
// Compile & run with: gcc -O1 -o test -lpulse test.c && ./test
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <error.h>
#include <pulse/context.h>
#include <pulse/mainloop.h>
#include <pulse/introspect.h>
#include <pulse/proplist.h>
#include <pulse/stream.h>
#define err(n, ...) \
error_at_line(n, 0, __FILE__, __LINE__, __VA_ARGS__)
#define p(...) do { \
printf(__VA_ARGS__); \
fflush(stdout); \
} while(0)
int st_connected = 0;
void st_callback(pa_context *c, void *userdata) {
pa_context_state_t st = pa_context_get_state(c);
if (st < PA_CONTEXT_READY) return;
if (st == PA_CONTEXT_READY) st_connected = 1;
else err(37, "ctx state [%d]", st);
}
char *bin_match = "mpv";
int info_done = 0, info_found = 0;
uint32_t sink_input_idx, sink_idx, src_idx;
volatile uint32_t sink_owner; // random metadata to query
void info_callback_find_sink_input( pa_context *c,
const pa_sink_input_info *info, int eol, void *userdata ) {
if (eol) {
info_done = 1;
return; }
const char *bin = pa_proplist_gets( info->proplist,
PA_PROP_APPLICATION_PROCESS_BINARY );
if (strcmp(bin, bin_match) != 0) return;
sink_input_idx = info->index;
sink_idx = info->sink;
info_found = 1;
}
void info_callback_query_sink( pa_context *c,
const pa_sink_info *info, int eol, void *userdata ) {
if (eol) {
info_done = 1;
return; }
src_idx = info->monitor_source;
sink_owner = info->owner_module;
info_found = 1;
}
float peak = 0;
void read_callback(pa_stream *s, size_t bs, void *userdata) {
const void *data;
if (pa_stream_peek(s, &data, &bs) < 0) err(50, "stream_peek");
if (!data) {
if (bs)
if (pa_stream_drop(s)) err(51, "stream_drop");
return; }
float v = ((const float*) data)[bs / sizeof(float) -1];
if (pa_stream_drop(s)) err(52, "stream_drop");
if (v < 0) return;
if (v > peak) peak = v;
}
uint64_t ts0 = 0;
uint32_t mono_time_ms() {
struct timespec tp;
if (clock_gettime(CLOCK_MONOTONIC, &tp) < 0) err(48, "clock");
uint64_t ts = tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
if (!ts0) ts0 = ts;
return (uint32_t) (ts - ts0);
}
int main(int argc, char*argv[]) {
pa_mainloop *m = pa_mainloop_new();
pa_mainloop_api *api = pa_mainloop_get_api(m);
pa_context *c = pa_context_new(api, "stream-info-pointer-test");
pa_context_set_state_callback(c, st_callback, NULL);
// Connect
if (pa_context_connect(c, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0)
err(38, "connect");
while (!st_connected)
if (pa_mainloop_iterate(m, 1, NULL) <= 0) err(39, "connect-iter");
// Find sink input index
while (1) {
info_done = 0;
pa_operation *op =
pa_context_get_sink_input_info_list(
c, info_callback_find_sink_input, NULL );
while (!info_done)
if (pa_mainloop_iterate(m, 1, NULL) <= 0)
err(40, "sink_input_info_list");
pa_operation_unref(op);
if (info_found) break;
if (sleep(3)) err(41, "sink_input_info_list sleep");
}
// stream + get_sink_info_by_index loop
int iter_n = 0;
peaks_and_info_loop: while (1) {
// Query sink_idx for monitor_source
info_found = 0; info_done = 0;
pa_operation *op =
pa_context_get_sink_info_by_index(
c, sink_idx, info_callback_query_sink, NULL );
while (!info_done)
if (pa_mainloop_iterate(m, 1, NULL) <= 0) err(42, "sink_info_list");
pa_operation_unref(op);
if (!info_found) err(43, "sink_idx info not found");
// Init stream
pa_sample_spec ss = {
.format = PA_SAMPLE_FLOAT32LE,
.rate = 25,
.channels = 1 };
pa_proplist *proplist = pa_proplist_from_string(
"application.id=org.PulseAudio.pavucontrol" );
if (!proplist) err(44, "stream-proplist");
pa_stream *s =
pa_stream_new_with_proplist(c, "peak detect", &ss, NULL, proplist);
pa_proplist_free(proplist);
if (!s) err(45, "stream");
if (pa_stream_set_monitor_stream(s, sink_input_idx) < 0)
err(46, "set_monitor_stream");
pa_stream_set_read_callback(s, read_callback, NULL);
pa_buffer_attr attr;
memset(&attr, 0, sizeof(attr));
attr.fragsize = sizeof(float);
attr.maxlength = (uint32_t) -1;
pa_stream_flags_t flags = PA_STREAM_DONT_MOVE |
PA_STREAM_PEAK_DETECT | PA_STREAM_ADJUST_LATENCY |
PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND ;
char src_idx_str[16];
snprintf(src_idx_str, sizeof(src_idx_str), "%u", src_idx);
if (pa_stream_connect_record(s, src_idx_str, &attr, flags) < 0)
err(47, "stream_connect_record");
// Poll for peak values
peak = 0;
uint32_t ts1 = mono_time_ms() + 200; // 0.2s
while (1) {
uint32_t ts = mono_time_ms();
if (ts >= ts1) break;
if ( pa_mainloop_prepare(m, ts1 - ts) < 0 ||
pa_mainloop_poll(m) < 0 ||
pa_mainloop_dispatch(m) < 0 )
err(49, "poll"); }
if (pa_stream_disconnect(s)) err(53, "stream_disconnect");
iter_n++;
p("peak value [%03d]: %.5f\n", iter_n, peak);
} // peaks_and_info_loop
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment