1 /* Spa
2 *
3 * Copyright © 2018 Wim Taymans
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 /*
26 [title]
27 [title]
28 */
29
30 #include "config.h"
31
32 #include <math.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <dlfcn.h>
38 #include <errno.h>
39 #include <pthread.h>
40 #include <poll.h>
41
42 #include <spa/support/plugin.h>
43 #include <spa/support/log-impl.h>
44 #include <spa/support/loop.h>
45 #include <spa/node/node.h>
46 #include <spa/node/io.h>
47 #include <spa/node/utils.h>
48 #include <spa/param/param.h>
49 #include <spa/param/props.h>
50 #include <spa/param/audio/format-utils.h>
51 #include <spa/utils/names.h>
52 #include <spa/utils/result.h>
53 #include <spa/utils/string.h>
54
55 #define M_PI_M2 ( M_PI + M_PI )
56
57 static SPA_LOG_IMPL(default_log);
58
59 #define spa_debug(f,...) spa_log_trace(&default_log.log, f, __VA_ARGS__)
60
61 #include <spa/graph/graph.h>
62
63 #include <spa/debug/pod.h>
64
65 struct buffer {
66 struct spa_buffer buffer;
67 struct spa_meta metas[1];
68 struct spa_meta_header header;
69 struct spa_data datas[1];
70 struct spa_chunk chunks[1];
71 };
72
73 struct data {
74 const char *plugin_dir;
75 struct spa_log *log;
76 struct spa_system *system;
77 struct spa_loop *loop;
78 struct spa_loop_control *control;
79 struct spa_support support[5];
80 uint32_t n_support;
81
82 struct spa_graph graph;
83 struct spa_graph_state graph_state;
84 struct spa_graph_node source_node;
85 struct spa_graph_state source_state;
86 struct spa_graph_port source_out;
87 struct spa_graph_port sink_in;
88 struct spa_graph_node sink_node;
89 struct spa_graph_state sink_state;
90
91 struct spa_node *sink;
92
93 struct spa_node *source;
94 struct spa_io_buffers source_sink_io[1];
95 struct spa_buffer *source_buffers[1];
96 struct buffer source_buffer[1];
97
98 uint8_t ctrl[1024];
99 double freq_accum;
100 double volume_accum;
101
102 bool running;
103 pthread_t thread;
104 };
105
106 #define MIN_LATENCY 1024
107
108 #define BUFFER_SIZE 4096
109
110 static void
init_buffer(struct data * data,struct spa_buffer ** bufs,struct buffer * ba,int n_buffers,size_t size)111 init_buffer(struct data *data, struct spa_buffer **bufs, struct buffer *ba, int n_buffers,
112 size_t size)
113 {
114 int i;
115
116 for (i = 0; i < n_buffers; i++) {
117 struct buffer *b = &ba[i];
118 bufs[i] = &b->buffer;
119
120 b->buffer.metas = b->metas;
121 b->buffer.n_metas = 1;
122 b->buffer.datas = b->datas;
123 b->buffer.n_datas = 1;
124
125 b->header.flags = 0;
126 b->header.seq = 0;
127 b->header.pts = 0;
128 b->header.dts_offset = 0;
129 b->metas[0].type = SPA_META_Header;
130 b->metas[0].data = &b->header;
131 b->metas[0].size = sizeof(b->header);
132
133 b->datas[0].type = SPA_DATA_MemPtr;
134 b->datas[0].flags = 0;
135 b->datas[0].fd = -1;
136 b->datas[0].mapoffset = 0;
137 b->datas[0].maxsize = size;
138 b->datas[0].data = malloc(size);
139 b->datas[0].chunk = &b->chunks[0];
140 b->datas[0].chunk->offset = 0;
141 b->datas[0].chunk->size = 0;
142 b->datas[0].chunk->stride = 0;
143 }
144 }
145
make_node(struct data * data,struct spa_node ** node,const char * lib,const char * name)146 static int make_node(struct data *data, struct spa_node **node, const char *lib, const char *name)
147 {
148 struct spa_handle *handle;
149 int res;
150 void *hnd;
151 spa_handle_factory_enum_func_t enum_func;
152 uint32_t i;
153 char *path;
154
155 if ((path = spa_aprintf("%s/%s", data->plugin_dir, lib)) == NULL) {
156 return -ENOMEM;
157 }
158 if ((hnd = dlopen(path, RTLD_NOW)) == NULL) {
159 printf("can't load %s: %s\n", lib, dlerror());
160 free(path);
161 return -ENOENT;
162 }
163 free(path);
164 if ((enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) {
165 printf("can't find enum function\n");
166 return -ENOENT;
167 }
168
169 for (i = 0;;) {
170 const struct spa_handle_factory *factory;
171 void *iface;
172
173 if ((res = enum_func(&factory, &i)) <= 0) {
174 if (res != 0)
175 printf("can't enumerate factories: %s\n", spa_strerror(res));
176 break;
177 }
178 if (factory->version < 1)
179 continue;
180 if (!spa_streq(factory->name, name))
181 continue;
182
183 handle = calloc(1, spa_handle_factory_get_size(factory, NULL));
184 if ((res =
185 spa_handle_factory_init(factory, handle, NULL, data->support,
186 data->n_support)) < 0) {
187 printf("can't make factory instance: %d\n", res);
188 return res;
189 }
190 if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Node, &iface)) < 0) {
191 printf("can't get interface %d\n", res);
192 return res;
193 }
194 *node = iface;
195 return 0;
196 }
197 return -EBADF;
198 }
199
update_props(struct data * data)200 static void update_props(struct data *data)
201 {
202 struct spa_pod_builder b;
203 struct spa_pod *pod;
204 struct spa_pod_frame f[2];
205
206 spa_pod_builder_init(&b, data->ctrl, sizeof(data->ctrl));
207
208 #if 0
209 spa_pod_builder_push_sequence(&b, &f[0], 0);
210 spa_pod_builder_control(&b, 0, SPA_CONTROL_Properties);
211 spa_pod_builder_push_object(&b, &f[1], SPA_TYPE_OBJECT_Props, 0);
212 spa_pod_builder_prop(&b, SPA_PROP_frequency, 0);
213 spa_pod_builder_float(&b, ((sin(data->freq_accum) + 1.0) * 200.0) + 440.0);
214 spa_pod_builder_prop(&b, SPA_PROP_volume, 0);
215 spa_pod_builder_float(&b, (sin(data->volume_accum) / 2.0) + 0.5);
216 spa_pod_builder_pop(&b, &f[1]);
217 pod = spa_pod_builder_pop(&b, &f[0]);
218 #else
219 spa_pod_builder_push_sequence(&b, &f[0], 0);
220 spa_pod_builder_control(&b, 0, SPA_CONTROL_Properties);
221 spa_pod_builder_add_object(&b,
222 SPA_TYPE_OBJECT_Props, 0,
223 SPA_PROP_frequency, SPA_POD_Float(((sin(data->freq_accum) + 1.0) * 200.0) + 440.0),
224 SPA_PROP_volume, SPA_POD_Float((sin(data->volume_accum) / 2.0) + 0.5));
225 pod = spa_pod_builder_pop(&b, &f[0]);
226 #endif
227
228 spa_debug_pod(0, NULL, pod);
229
230 data->freq_accum += M_PI_M2 / 880.0;
231 if (data->freq_accum >= M_PI_M2)
232 data->freq_accum -= M_PI_M2;
233
234 data->volume_accum += M_PI_M2 / 2000.0;
235 if (data->volume_accum >= M_PI_M2)
236 data->volume_accum -= M_PI_M2;
237 }
238
on_sink_ready(void * _data,int status)239 static int on_sink_ready(void *_data, int status)
240 {
241 struct data *data = _data;
242
243 update_props(data);
244
245 spa_graph_node_process(&data->source_node);
246 spa_graph_node_process(&data->sink_node);
247 return 0;
248 }
249
250 static int
on_sink_reuse_buffer(void * _data,uint32_t port_id,uint32_t buffer_id)251 on_sink_reuse_buffer(void *_data, uint32_t port_id, uint32_t buffer_id)
252 {
253 struct data *data = _data;
254
255 data->source_sink_io[0].buffer_id = buffer_id;
256 return 0;
257 }
258
259 static const struct spa_node_callbacks sink_callbacks = {
260 SPA_VERSION_NODE_CALLBACKS,
261 .ready = on_sink_ready,
262 .reuse_buffer = on_sink_reuse_buffer
263 };
264
make_nodes(struct data * data,const char * device)265 static int make_nodes(struct data *data, const char *device)
266 {
267 int res;
268 struct spa_pod *props;
269 struct spa_pod_builder b = { 0 };
270 uint8_t buffer[512];
271 //uint32_t idx;
272
273 if ((res = make_node(data, &data->sink,
274 "alsa/libspa-alsa.so",
275 SPA_NAME_API_ALSA_PCM_SINK)) < 0) {
276 printf("can't create alsa-sink: %d\n", res);
277 return res;
278 }
279 spa_node_set_callbacks(data->sink, &sink_callbacks, data);
280
281 spa_pod_builder_init(&b, buffer, sizeof(buffer));
282 props = spa_pod_builder_add_object(&b,
283 SPA_TYPE_OBJECT_Props, 0,
284 SPA_PROP_device, SPA_POD_String(device ? device : "hw:0"),
285 SPA_PROP_minLatency, SPA_POD_Int(MIN_LATENCY));
286
287 spa_debug_pod(0, NULL, props);
288
289 if ((res = spa_node_set_param(data->sink, SPA_PARAM_Props, 0, props)) < 0)
290 printf("got set_props error %d\n", res);
291
292 if ((res = make_node(data, &data->source,
293 "audiotestsrc/libspa-audiotestsrc.so",
294 "audiotestsrc")) < 0) {
295 printf("can't create audiotestsrc: %d\n", res);
296 return res;
297 }
298
299 spa_pod_builder_init(&b, buffer, sizeof(buffer));
300 props = spa_pod_builder_add_object(&b,
301 SPA_TYPE_OBJECT_Props, 0,
302 SPA_PROP_frequency, SPA_POD_Float(600.0),
303 SPA_PROP_volume, SPA_POD_Float(0.5),
304 SPA_PROP_live, SPA_POD_Bool(false));
305
306 if ((res = spa_node_set_param(data->source, SPA_PARAM_Props, 0, props)) < 0)
307 printf("got set_props error %d\n", res);
308
309 if ((res = spa_node_port_set_io(data->source,
310 SPA_DIRECTION_OUTPUT, 0,
311 SPA_IO_Control,
312 &data->ctrl, sizeof(data->ctrl))) < 0) {
313 printf("can't set_io freq: %d\n", res);
314 return res;
315 }
316
317 data->source_sink_io[0] = SPA_IO_BUFFERS_INIT;
318
319 spa_node_port_set_io(data->source,
320 SPA_DIRECTION_OUTPUT, 0,
321 SPA_IO_Buffers,
322 &data->source_sink_io[0], sizeof(data->source_sink_io[0]));
323 spa_node_port_set_io(data->sink,
324 SPA_DIRECTION_INPUT, 0,
325 SPA_IO_Buffers,
326 &data->source_sink_io[0], sizeof(data->source_sink_io[0]));
327
328 spa_graph_node_init(&data->source_node, &data->source_state);
329 spa_graph_node_set_callbacks(&data->source_node, &spa_graph_node_impl_default, data->source);
330 spa_graph_node_add(&data->graph, &data->source_node);
331 spa_graph_port_init(&data->source_out, SPA_DIRECTION_OUTPUT, 0, 0);
332 spa_graph_port_add(&data->source_node, &data->source_out);
333
334 spa_graph_node_init(&data->sink_node, &data->sink_state);
335 spa_graph_node_set_callbacks(&data->sink_node, &spa_graph_node_impl_default, data->sink);
336 spa_graph_node_add(&data->graph, &data->sink_node);
337 spa_graph_port_init(&data->sink_in, SPA_DIRECTION_INPUT, 0, 0);
338 spa_graph_port_add(&data->sink_node, &data->sink_in);
339
340 spa_graph_port_link(&data->source_out, &data->sink_in);
341
342 return res;
343 }
344
negotiate_formats(struct data * data)345 static int negotiate_formats(struct data *data)
346 {
347 int res;
348 struct spa_pod *format;
349 struct spa_pod_builder b = { 0 };
350 uint8_t buffer[4096];
351
352 spa_pod_builder_init(&b, buffer, sizeof(buffer));
353 format = spa_format_audio_raw_build(&b, 0,
354 &SPA_AUDIO_INFO_RAW_INIT(
355 .format = SPA_AUDIO_FORMAT_S16,
356 .rate = 48000,
357 .channels = 2 ));
358
359 if ((res = spa_node_port_set_param(data->sink,
360 SPA_DIRECTION_INPUT, 0,
361 SPA_PARAM_Format, 0, format)) < 0)
362 return res;
363
364 if ((res = spa_node_port_set_param(data->source,
365 SPA_DIRECTION_OUTPUT, 0,
366 SPA_PARAM_Format, 0, format)) < 0)
367 return res;
368
369 init_buffer(data, data->source_buffers, data->source_buffer, 1, BUFFER_SIZE);
370 if ((res =
371 spa_node_port_use_buffers(data->sink,
372 SPA_DIRECTION_INPUT, 0, 0,
373 data->source_buffers, 1)) < 0)
374 return res;
375 if ((res =
376 spa_node_port_use_buffers(data->source,
377 SPA_DIRECTION_OUTPUT, 0, 0,
378 data->source_buffers, 1)) < 0)
379 return res;
380
381 return 0;
382 }
383
loop(void * user_data)384 static void *loop(void *user_data)
385 {
386 struct data *data = user_data;
387
388 printf("enter thread\n");
389 spa_loop_control_enter(data->control);
390
391 while (data->running) {
392 spa_loop_control_iterate(data->control, -1);
393 }
394
395 printf("leave thread\n");
396 spa_loop_control_leave(data->control);
397
398 return NULL;
399 }
400
run_async_sink(struct data * data)401 static void run_async_sink(struct data *data)
402 {
403 int res, err;
404 struct spa_command cmd;
405
406 cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Start);
407 if ((res = spa_node_send_command(data->sink, &cmd)) < 0)
408 printf("got error %d\n", res);
409
410 spa_loop_control_leave(data->control);
411
412 data->running = true;
413 if ((err = pthread_create(&data->thread, NULL, loop, data)) != 0) {
414 printf("can't create thread: %d %s", err, strerror(err));
415 data->running = false;
416 }
417
418 printf("sleeping for 1000 seconds\n");
419 sleep(1000);
420
421 if (data->running) {
422 data->running = false;
423 pthread_join(data->thread, NULL);
424 }
425
426 spa_loop_control_enter(data->control);
427
428 cmd = SPA_NODE_COMMAND_INIT(SPA_NODE_COMMAND_Pause);
429 if ((res = spa_node_send_command(data->sink, &cmd)) < 0)
430 printf("got error %d\n", res);
431 }
432
load_handle(struct data * data,struct spa_handle ** handle,const char * lib,const char * name)433 static int load_handle(struct data *data, struct spa_handle **handle, const char *lib, const char *name)
434 {
435 int res;
436 void *hnd;
437 spa_handle_factory_enum_func_t enum_func;
438 uint32_t i;
439 char *path;
440
441 if ((path = spa_aprintf("%s/%s", data->plugin_dir, lib)) == NULL) {
442 return -ENOMEM;
443 }
444 if ((hnd = dlopen(path, RTLD_NOW)) == NULL) {
445 printf("can't load %s: %s\n", lib, dlerror());
446 free(path);
447 return -ENOENT;
448 }
449 free(path);
450 if ((enum_func = dlsym(hnd, SPA_HANDLE_FACTORY_ENUM_FUNC_NAME)) == NULL) {
451 printf("can't find enum function\n");
452 return -ENOENT;
453 }
454
455 for (i = 0;;) {
456 const struct spa_handle_factory *factory;
457
458 if ((res = enum_func(&factory, &i)) <= 0) {
459 if (res != 0)
460 printf("can't enumerate factories: %s\n", spa_strerror(res));
461 break;
462 }
463 if (factory->version < 1)
464 continue;
465 if (!spa_streq(factory->name, name))
466 continue;
467
468 *handle = calloc(1, spa_handle_factory_get_size(factory, NULL));
469 if ((res = spa_handle_factory_init(factory, *handle,
470 NULL, data->support,
471 data->n_support)) < 0) {
472 printf("can't make factory instance: %d\n", res);
473 return res;
474 }
475 return 0;
476 }
477 return -EBADF;
478 }
479
init_data(struct data * data)480 int init_data(struct data *data)
481 {
482 int res;
483 const char *str;
484 struct spa_handle *handle = NULL;
485 void *iface;
486
487 if ((str = getenv("SPA_PLUGIN_DIR")) == NULL)
488 str = PLUGINDIR;
489 data->plugin_dir = str;
490
491 /* init the graph */
492 spa_graph_init(&data->graph, &data->graph_state);
493
494 /* set the default log */
495 data->log = &default_log.log;
496 data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Log, data->log);
497
498 /* load and set support system */
499 if ((res = load_handle(data, &handle,
500 "support/libspa-support.so",
501 SPA_NAME_SUPPORT_SYSTEM)) < 0)
502 return res;
503 if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_System, &iface)) < 0) {
504 printf("can't get System interface %d\n", res);
505 return res;
506 }
507 data->system = iface;
508 data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_System, data->system);
509 data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataSystem, data->system);
510
511 /* load and set support loop and loop control */
512 if ((res = load_handle(data, &handle,
513 "support/libspa-support.so",
514 SPA_NAME_SUPPORT_LOOP)) < 0)
515 return res;
516
517 if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_Loop, &iface)) < 0) {
518 printf("can't get interface %d\n", res);
519 return res;
520 }
521 data->loop = iface;
522 data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Loop, data->loop);
523 data->support[data->n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataLoop, data->loop);
524 if ((res = spa_handle_get_interface(handle, SPA_TYPE_INTERFACE_LoopControl, &iface)) < 0) {
525 printf("can't get interface %d\n", res);
526 return res;
527 }
528 data->control = iface;
529
530 if ((str = getenv("SPA_DEBUG")))
531 data->log->level = atoi(str);
532
533 return 0;
534 }
535
main(int argc,char * argv[])536 int main(int argc, char *argv[])
537 {
538 struct data data = { NULL };
539 int res;
540
541 if ((res = init_data(&data)) < 0) {
542 printf("can't init data: %d (%s)\n", res, spa_strerror(res));
543 return -1;
544 }
545
546 if ((res = make_nodes(&data, argc > 1 ? argv[1] : NULL)) < 0) {
547 printf("can't make nodes: %d (%s)\n", res, spa_strerror(res));
548 return -1;
549 }
550 if ((res = negotiate_formats(&data)) < 0) {
551 printf("can't negotiate nodes: %d (%s)\n", res, spa_strerror(res));
552 return -1;
553 }
554
555 spa_loop_control_enter(data.control);
556 run_async_sink(&data);
557 spa_loop_control_leave(data.control);
558
559 return 0;
560 }
561