1 /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6 #include <inttypes.h>
7 #include <sys/param.h>
8 #include <syslog.h>
9
10 #include "cras_util.h"
11 #include "cras_dsp_module.h"
12 #include "cras_dsp_pipeline.h"
13 #include "dsp_util.h"
14
15 /* We have a static representation of the dsp graph in a "struct ini",
16 * and here we will construct a dynamic representation of it in a
17 * "struct pipeline". The difference between the static one and the
18 * dynamic one is that we will only include the subset of the dsp
19 * graph actually needed in the dynamic one (so those plugins that are
20 * disabled will not be included). Here are the mapping between the
21 * static representation and the dynamic representation:
22 *
23 * static dynamic
24 * ------------- --------------------------------------
25 * struct ini struct pipeline
26 * struct plugin struct instance
27 * strict port struct audio_port, struct control_port
28 *
29 * For example, if the ini file specifies these plugins and their
30 * connections:
31 *
32 * [A]
33 * output_0={audio}
34 * [B]
35 * input_0={audio}
36 * output_1={result}
37 * [C]
38 * input_0={result}
39 *
40 * That is, A connects to B, and B connects to C. If the plugin B is
41 * now disabled, in the pipeline we construct there will only be two
42 * instances (A and C) and the audio ports on these instances will
43 * connect to each other directly, bypassing B.
44 */
45
46 /* This represents an audio port on an instance. */
47 struct audio_port {
48 struct audio_port *peer; /* the audio port this port connects to */
49 struct plugin *plugin; /* the plugin corresponds to the instance */
50 int original_index; /* the port index in the plugin */
51 int buf_index; /* the buffer index in the pipeline */
52 };
53
54 /* This represents a control port on an instance. */
55 struct control_port {
56 struct control_port *peer; /* the control port this port connects to */
57 struct plugin *plugin; /* the plugin corresponds to the instance */
58 int original_index; /* the port index in the plugin */
59 float value; /* the value of the control port */
60 };
61
62 DECLARE_ARRAY_TYPE(struct audio_port, audio_port_array);
63 DECLARE_ARRAY_TYPE(struct control_port, control_port_array);
64
65 /* An instance is a dynamic representation of a plugin. We only create
66 * an instance when a plugin is needed (data actually flows through it
67 * and it is not disabled). An instance also contains a pointer to a
68 * struct dsp_module, which is the implementation of the plugin */
69 struct instance {
70 /* The plugin this instance corresponds to */
71 struct plugin *plugin;
72
73 /* These are the ports on this instance. The difference
74 * between this and the port array in a struct plugin is that
75 * the ports skip disabled plugins and connect to the upstream
76 * ports directly.
77 */
78 audio_port_array input_audio_ports;
79 audio_port_array output_audio_ports;
80 control_port_array input_control_ports;
81 control_port_array output_control_ports;
82
83 /* The implementation of the plugin */
84 struct dsp_module *module;
85
86 /* Whether this module's instantiate() function has been called */
87 int instantiated;
88
89 /* This caches the value returned from get_properties() of a module */
90 int properties;
91
92 /* This is the total buffering delay from source to this instance. It is
93 * in number of frames. */
94 int total_delay;
95 };
96
97 DECLARE_ARRAY_TYPE(struct instance, instance_array)
98
99 /* An pipeline is a dynamic representation of a dsp ini file. */
100 struct pipeline {
101 /* The purpose of the pipeline. "playback" or "capture" */
102 const char *purpose;
103
104 /* The ini file this pipeline comes from */
105 struct ini *ini;
106
107 /* All needed instances for this pipeline. It is sorted in an
108 * order that if instance B depends on instance A, then A will
109 * appear in front of B. */
110 instance_array instances;
111
112 /* The maximum number of audio buffers that will be used at
113 * the same time for this pipeline */
114 int peak_buf;
115
116 /* The audio data buffers */
117 float **buffers;
118
119 /* The instance where the audio data flow in */
120 struct instance *source_instance;
121
122 /* The instance where the audio data flow out */
123 struct instance *sink_instance;
124
125 /* The number of audio channels for this pipeline */
126 int input_channels;
127 int output_channels;
128
129 /* The audio sampling rate for this pipleine. It is zero if
130 * cras_dsp_pipeline_instantiate() has not been called. */
131 int sample_rate;
132
133 /* The total time it takes to run the pipeline, in nanoseconds. */
134 int64_t total_time;
135
136 /* The max/min time it takes to run the pipeline, in nanoseconds. */
137 int64_t max_time;
138 int64_t min_time;
139
140 /* The number of blocks the pipeline. */
141 int64_t total_blocks;
142
143 /* The total number of sample frames the pipeline processed */
144 int64_t total_samples;
145 };
146
find_instance_by_plugin(const instance_array * instances,const struct plugin * plugin)147 static struct instance *find_instance_by_plugin(const instance_array *instances,
148 const struct plugin *plugin)
149 {
150 int i;
151 struct instance *instance;
152
153 ARRAY_ELEMENT_FOREACH (instances, i, instance) {
154 if (instance->plugin == plugin)
155 return instance;
156 }
157
158 return NULL;
159 }
160
161 /* Finds out where the data sent to plugin:index come from. The issue
162 * we need to handle here is the previous plugin may be disabled, so
163 * we need to go upstream until we find the real origin */
find_origin_port(struct ini * ini,const instance_array * instances,const struct plugin * plugin,int index,const struct plugin ** origin,int * origin_index)164 static int find_origin_port(struct ini *ini, const instance_array *instances,
165 const struct plugin *plugin, int index,
166 const struct plugin **origin, int *origin_index)
167 {
168 enum port_type type;
169 struct port *port;
170 int flow_id;
171 struct flow *flow;
172 int i, k;
173 int found;
174
175 port = ARRAY_ELEMENT(&plugin->ports, index);
176 type = port->type;
177 flow_id = port->flow_id;
178 if (flow_id == INVALID_FLOW_ID)
179 return -1;
180 flow = ARRAY_ELEMENT(&ini->flows, flow_id);
181
182 /* move to the previous plugin */
183 plugin = flow->from;
184 index = flow->from_port;
185
186 /* if the plugin is not disabled, it will be pointed by some instance */
187 if (find_instance_by_plugin(instances, plugin)) {
188 *origin = plugin;
189 *origin_index = index;
190 return 0;
191 }
192
193 /* Now we know the previous plugin is disabled, we need to go
194 * upstream. We assume the k-th output port of the plugin
195 * corresponds to the k-th input port of the plugin (with the
196 * same type) */
197
198 k = 0;
199 found = 0;
200 ARRAY_ELEMENT_FOREACH (&plugin->ports, i, port) {
201 if (index == i) {
202 found = 1;
203 break;
204 }
205 if (port->direction == PORT_OUTPUT && port->type == type)
206 k++;
207 }
208 if (!found)
209 return -1;
210
211 found = 0;
212 ARRAY_ELEMENT_FOREACH (&plugin->ports, i, port) {
213 if (port->direction == PORT_INPUT && port->type == type) {
214 if (k-- == 0) {
215 index = i;
216 found = 1;
217 break;
218 }
219 }
220 }
221 if (!found)
222 return -1;
223
224 return find_origin_port(ini, instances, plugin, index, origin,
225 origin_index);
226 }
227
find_output_audio_port(instance_array * instances,const struct plugin * plugin,int index)228 static struct audio_port *find_output_audio_port(instance_array *instances,
229 const struct plugin *plugin,
230 int index)
231 {
232 int i;
233 struct instance *instance;
234 struct audio_port *audio_port;
235
236 instance = find_instance_by_plugin(instances, plugin);
237 if (!instance)
238 return NULL;
239
240 ARRAY_ELEMENT_FOREACH (&instance->output_audio_ports, i, audio_port) {
241 if (audio_port->original_index == index)
242 return audio_port;
243 }
244
245 return NULL;
246 }
247
find_output_control_port(instance_array * instances,const struct plugin * plugin,int index)248 static struct control_port *find_output_control_port(instance_array *instances,
249 const struct plugin *plugin,
250 int index)
251 {
252 int i;
253 struct instance *instance;
254 struct control_port *control_port;
255
256 instance = find_instance_by_plugin(instances, plugin);
257 if (!instance)
258 return NULL;
259
260 ARRAY_ELEMENT_FOREACH (&instance->output_control_ports, i,
261 control_port) {
262 if (control_port->original_index == index)
263 return control_port;
264 }
265
266 return NULL;
267 }
268
is_disabled(struct plugin * plugin,struct cras_expr_env * env)269 static char is_disabled(struct plugin *plugin, struct cras_expr_env *env)
270 {
271 char disabled;
272 return (plugin->disable_expr &&
273 cras_expr_expression_eval_boolean(plugin->disable_expr, env,
274 &disabled) == 0 &&
275 disabled == 1);
276 }
277
topological_sort(struct pipeline * pipeline,struct cras_expr_env * env,struct plugin * plugin,char * visited)278 static int topological_sort(struct pipeline *pipeline,
279 struct cras_expr_env *env, struct plugin *plugin,
280 char *visited)
281 {
282 struct port *port;
283 struct flow *flow;
284 int index;
285 int i;
286 int flow_id;
287 struct instance *instance;
288 struct ini *ini = pipeline->ini;
289
290 index = ARRAY_INDEX(&ini->plugins, plugin);
291 if (visited[index])
292 return 0;
293 visited[index] = 1;
294
295 ARRAY_ELEMENT_FOREACH (&plugin->ports, i, port) {
296 if (port->flow_id == INVALID_FLOW_ID)
297 continue;
298 flow_id = port->flow_id;
299 flow = ARRAY_ELEMENT(&ini->flows, flow_id);
300 if (!flow->from) {
301 syslog(LOG_ERR, "no plugin flows to %s:%d",
302 plugin->title, i);
303 return -1;
304 }
305 if (topological_sort(pipeline, env, flow->from, visited) < 0)
306 return -1;
307 }
308
309 /* if the plugin is disabled, we don't construct an instance for it */
310 if (is_disabled(plugin, env))
311 return 0;
312
313 instance = ARRAY_APPEND_ZERO(&pipeline->instances);
314 instance->plugin = plugin;
315
316 /* constructs audio and control ports for the instance */
317 ARRAY_ELEMENT_FOREACH (&plugin->ports, i, port) {
318 int need_connect = (port->flow_id != INVALID_FLOW_ID &&
319 port->direction == PORT_INPUT);
320 const struct plugin *origin = NULL;
321 int origin_index = 0;
322
323 if (need_connect) {
324 if (find_origin_port(ini, &pipeline->instances, plugin,
325 i, &origin, &origin_index) < 0)
326 return -1;
327 }
328
329 if (port->type == PORT_AUDIO) {
330 audio_port_array *audio_port_array =
331 (port->direction == PORT_INPUT) ?
332 &instance->input_audio_ports :
333 &instance->output_audio_ports;
334 struct audio_port *audio_port =
335 ARRAY_APPEND_ZERO(audio_port_array);
336 audio_port->plugin = plugin;
337 audio_port->original_index = i;
338 if (need_connect) {
339 struct audio_port *from;
340 from = find_output_audio_port(
341 &pipeline->instances, origin,
342 origin_index);
343 if (!from)
344 return -1;
345 from->peer = audio_port;
346 audio_port->peer = from;
347 }
348 } else if (port->type == PORT_CONTROL) {
349 control_port_array *control_port_array =
350 (port->direction == PORT_INPUT) ?
351 &instance->input_control_ports :
352 &instance->output_control_ports;
353 struct control_port *control_port =
354 ARRAY_APPEND_ZERO(control_port_array);
355 control_port->plugin = plugin;
356 control_port->original_index = i;
357 control_port->value = port->init_value;
358 if (need_connect) {
359 struct control_port *from;
360 from = find_output_control_port(
361 &pipeline->instances, origin,
362 origin_index);
363 if (!from)
364 return -1;
365 from->peer = control_port;
366 control_port->peer = from;
367 }
368 }
369 }
370
371 return 0;
372 }
373
find_enabled_builtin_plugin(struct ini * ini,const char * label,const char * purpose,struct cras_expr_env * env)374 static struct plugin *find_enabled_builtin_plugin(struct ini *ini,
375 const char *label,
376 const char *purpose,
377 struct cras_expr_env *env)
378 {
379 int i;
380 struct plugin *plugin, *found = NULL;
381
382 ARRAY_ELEMENT_FOREACH (&ini->plugins, i, plugin) {
383 if (strcmp(plugin->library, "builtin") != 0)
384 continue;
385 if (strcmp(plugin->label, label) != 0)
386 continue;
387 if (!plugin->purpose || strcmp(plugin->purpose, purpose) != 0)
388 continue;
389 if (is_disabled(plugin, env))
390 continue;
391 if (found) {
392 syslog(LOG_ERR, "two %s plugins enabled: %s and %s",
393 label, found->title, plugin->title);
394 return NULL;
395 }
396 found = plugin;
397 }
398
399 return found;
400 }
401
cras_dsp_pipeline_create(struct ini * ini,struct cras_expr_env * env,const char * purpose)402 struct pipeline *cras_dsp_pipeline_create(struct ini *ini,
403 struct cras_expr_env *env,
404 const char *purpose)
405 {
406 struct pipeline *pipeline;
407 int n;
408 char *visited;
409 int rc;
410 struct plugin *source =
411 find_enabled_builtin_plugin(ini, "source", purpose, env);
412 struct plugin *sink =
413 find_enabled_builtin_plugin(ini, "sink", purpose, env);
414
415 if (!source || !sink) {
416 syslog(LOG_DEBUG,
417 "no enabled source or sink found %p/%p for %s", source,
418 sink, purpose);
419 return NULL;
420 }
421
422 pipeline = calloc(1, sizeof(struct pipeline));
423 if (!pipeline) {
424 syslog(LOG_ERR, "no memory for pipeline");
425 return NULL;
426 }
427
428 pipeline->ini = ini;
429 pipeline->purpose = purpose;
430 /* create instances for needed plugins, in the order of dependency */
431 n = ARRAY_COUNT(&ini->plugins);
432 visited = calloc(1, n);
433 rc = topological_sort(pipeline, env, sink, visited);
434 free(visited);
435
436 if (rc < 0) {
437 syslog(LOG_ERR, "failed to construct pipeline");
438 cras_dsp_pipeline_free(pipeline);
439 return NULL;
440 }
441
442 pipeline->source_instance =
443 find_instance_by_plugin(&pipeline->instances, source);
444 pipeline->sink_instance =
445 find_instance_by_plugin(&pipeline->instances, sink);
446
447 if (!pipeline->source_instance || !pipeline->sink_instance) {
448 syslog(LOG_ERR, "source(%p) or sink(%p) missing/disabled?",
449 source, sink);
450 cras_dsp_pipeline_free(pipeline);
451 return NULL;
452 }
453
454 pipeline->input_channels =
455 ARRAY_COUNT(&pipeline->source_instance->output_audio_ports);
456 pipeline->output_channels =
457 ARRAY_COUNT(&pipeline->sink_instance->input_audio_ports);
458 if (pipeline->output_channels > pipeline->input_channels) {
459 /* Can't increase channel count, no where to put them. */
460 syslog(LOG_ERR, "DSP output more channels than input\n");
461 cras_dsp_pipeline_free(pipeline);
462 return NULL;
463 }
464
465 return pipeline;
466 }
467
load_module(struct plugin * plugin,struct instance * instance)468 static int load_module(struct plugin *plugin, struct instance *instance)
469 {
470 struct dsp_module *module;
471 module = cras_dsp_module_load_builtin(plugin);
472 if (!module)
473 module = cras_dsp_module_load_ladspa(plugin);
474 if (!module)
475 return -1;
476 instance->module = module;
477 instance->properties = module->get_properties(module);
478 return 0;
479 }
480
use_buffers(char * busy,audio_port_array * audio_ports)481 static void use_buffers(char *busy, audio_port_array *audio_ports)
482 {
483 int i, k = 0;
484 struct audio_port *audio_port;
485
486 ARRAY_ELEMENT_FOREACH (audio_ports, i, audio_port) {
487 while (busy[k])
488 k++;
489 audio_port->buf_index = k;
490 busy[k] = 1;
491 }
492 }
493
unuse_buffers(char * busy,audio_port_array * audio_ports)494 static void unuse_buffers(char *busy, audio_port_array *audio_ports)
495 {
496 int i;
497 struct audio_port *audio_port;
498
499 ARRAY_ELEMENT_FOREACH (audio_ports, i, audio_port) {
500 busy[audio_port->buf_index] = 0;
501 }
502 }
503
504 /* assign which buffer each audio port on each instance should use */
allocate_buffers(struct pipeline * pipeline)505 static int allocate_buffers(struct pipeline *pipeline)
506 {
507 int i;
508 struct instance *instance;
509 int need_buf = 0, peak_buf = 0;
510 char *busy;
511
512 /* first figure out how many buffers do we need */
513 ARRAY_ELEMENT_FOREACH (&pipeline->instances, i, instance) {
514 int in = ARRAY_COUNT(&instance->input_audio_ports);
515 int out = ARRAY_COUNT(&instance->output_audio_ports);
516
517 if (instance->properties & MODULE_INPLACE_BROKEN) {
518 /* We cannot reuse input buffer as output
519 * buffer, so we need to use extra buffers */
520 need_buf += out;
521 peak_buf = MAX(peak_buf, need_buf);
522 need_buf -= in;
523 } else {
524 need_buf += out - in;
525 peak_buf = MAX(peak_buf, need_buf);
526 }
527 }
528 /*
529 * cras_dsp_pipeline_create creates pipeline with source and sink and it
530 * makes sure all ports could be accessed from some sources, which means
531 * that there is at least one source with out > 0 and in == 0.
532 * This will give us peak_buf > 0 in the previous calculation.
533 */
534 if (peak_buf <= 0) {
535 syslog(LOG_ERR, "peak_buf = %d, which must be greater than 0.",
536 peak_buf);
537 return -1;
538 }
539
540 /* then allocate the buffers */
541 pipeline->peak_buf = peak_buf;
542 pipeline->buffers = (float **)calloc(peak_buf, sizeof(float *));
543
544 if (!pipeline->buffers) {
545 syslog(LOG_ERR, "failed to allocate buffers");
546 return -1;
547 }
548
549 for (i = 0; i < peak_buf; i++) {
550 size_t size = DSP_BUFFER_SIZE * sizeof(float);
551 float *buf = calloc(1, size);
552 if (!buf) {
553 syslog(LOG_ERR, "failed to allocate buf");
554 return -1;
555 }
556 pipeline->buffers[i] = buf;
557 }
558
559 /* Now assign buffer index for each instance's input/output ports */
560 busy = calloc(peak_buf, sizeof(*busy));
561 ARRAY_ELEMENT_FOREACH (&pipeline->instances, i, instance) {
562 int j;
563 struct audio_port *audio_port;
564
565 /* Collect input buffers from upstream */
566 ARRAY_ELEMENT_FOREACH (&instance->input_audio_ports, j,
567 audio_port) {
568 audio_port->buf_index = audio_port->peer->buf_index;
569 }
570
571 /* If the module has the MODULE_INPLACE_BROKEN flag,
572 * we cannot reuse input buffers as output buffers, so
573 * we need to use extra buffers. For example, in this graph
574 *
575 * [A]
576 * output_0={x}
577 * output_1={y}
578 * output_2={z}
579 * output_3={w}
580 * [B]
581 * input_0={x}
582 * input_1={y}
583 * input_2={z}
584 * input_3={w}
585 * output_4={u}
586 *
587 * Then peak_buf for this pipeline is 4. However if
588 * plugin B has the MODULE_INPLACE_BROKEN flag, then
589 * peak_buf is 5 because plugin B cannot output to the
590 * same buffer used for input.
591 *
592 * This means if we don't have the flag, we can free
593 * the input buffers then allocate the output buffers,
594 * but if we have the flag, we have to allocate the
595 * output buffers before freeing the input buffers.
596 */
597 if (instance->properties & MODULE_INPLACE_BROKEN) {
598 use_buffers(busy, &instance->output_audio_ports);
599 unuse_buffers(busy, &instance->input_audio_ports);
600 } else {
601 unuse_buffers(busy, &instance->input_audio_ports);
602 use_buffers(busy, &instance->output_audio_ports);
603 }
604 }
605 free(busy);
606
607 return 0;
608 }
609
cras_dsp_pipeline_load(struct pipeline * pipeline)610 int cras_dsp_pipeline_load(struct pipeline *pipeline)
611 {
612 int i;
613 struct instance *instance;
614
615 ARRAY_ELEMENT_FOREACH (&pipeline->instances, i, instance) {
616 struct plugin *plugin = instance->plugin;
617 if (load_module(plugin, instance) != 0)
618 return -1;
619 }
620
621 if (allocate_buffers(pipeline) != 0)
622 return -1;
623
624 return 0;
625 }
626
627 /* Calculates the total buffering delay of each instance from the source */
calculate_audio_delay(struct pipeline * pipeline)628 static void calculate_audio_delay(struct pipeline *pipeline)
629 {
630 int i;
631 struct instance *instance;
632
633 ARRAY_ELEMENT_FOREACH (&pipeline->instances, i, instance) {
634 struct dsp_module *module = instance->module;
635 audio_port_array *audio_in = &instance->input_audio_ports;
636 struct audio_port *audio_port;
637 int delay = 0;
638 int j;
639
640 /* Finds the max delay of all modules that provide input to this
641 * instance. */
642 ARRAY_ELEMENT_FOREACH (audio_in, j, audio_port) {
643 struct instance *upstream = find_instance_by_plugin(
644 &pipeline->instances, audio_port->peer->plugin);
645 delay = MAX(upstream->total_delay, delay);
646 }
647
648 instance->total_delay = delay + module->get_delay(module);
649 }
650 }
651
cras_dsp_pipeline_instantiate(struct pipeline * pipeline,int sample_rate)652 int cras_dsp_pipeline_instantiate(struct pipeline *pipeline, int sample_rate)
653 {
654 int i;
655 struct instance *instance;
656
657 ARRAY_ELEMENT_FOREACH (&pipeline->instances, i, instance) {
658 struct dsp_module *module = instance->module;
659 if (module->instantiate(module, sample_rate) != 0)
660 return -1;
661 instance->instantiated = 1;
662 syslog(LOG_DEBUG, "instantiate %s", instance->plugin->label);
663 }
664 pipeline->sample_rate = sample_rate;
665
666 ARRAY_ELEMENT_FOREACH (&pipeline->instances, i, instance) {
667 audio_port_array *audio_in = &instance->input_audio_ports;
668 audio_port_array *audio_out = &instance->output_audio_ports;
669 control_port_array *control_in = &instance->input_control_ports;
670 control_port_array *control_out =
671 &instance->output_control_ports;
672 int j;
673 struct audio_port *audio_port;
674 struct control_port *control_port;
675 struct dsp_module *module = instance->module;
676
677 /* connect audio ports */
678 ARRAY_ELEMENT_FOREACH (audio_in, j, audio_port) {
679 float *buf = pipeline->buffers[audio_port->buf_index];
680 module->connect_port(module, audio_port->original_index,
681 buf);
682 syslog(LOG_DEBUG, "connect audio buf %d to %s:%d (in)",
683 audio_port->buf_index, instance->plugin->title,
684 audio_port->original_index);
685 }
686 ARRAY_ELEMENT_FOREACH (audio_out, j, audio_port) {
687 float *buf = pipeline->buffers[audio_port->buf_index];
688 module->connect_port(module, audio_port->original_index,
689 buf);
690 syslog(LOG_DEBUG, "connect audio buf %d to %s:%d (out)",
691 audio_port->buf_index, instance->plugin->title,
692 audio_port->original_index);
693 }
694
695 /* connect control ports */
696 ARRAY_ELEMENT_FOREACH (control_in, j, control_port) {
697 /* Note for input control ports which has a
698 * peer, we use &control_port->peer->value, so
699 * we can get the peer port's output value
700 * directly */
701 float *value = control_port->peer ?
702 &control_port->peer->value :
703 &control_port->value;
704 module->connect_port(
705 module, control_port->original_index, value);
706 syslog(LOG_DEBUG,
707 "connect control (val=%g) to %s:%d (in)",
708 control_port->value, instance->plugin->title,
709 control_port->original_index);
710 }
711 ARRAY_ELEMENT_FOREACH (control_out, j, control_port) {
712 module->connect_port(module,
713 control_port->original_index,
714 &control_port->value);
715 syslog(LOG_DEBUG,
716 "connect control (val=%g) to %s:%d (out)",
717 control_port->value, instance->plugin->title,
718 control_port->original_index);
719 }
720 }
721
722 calculate_audio_delay(pipeline);
723 return 0;
724 }
725
cras_dsp_pipeline_deinstantiate(struct pipeline * pipeline)726 void cras_dsp_pipeline_deinstantiate(struct pipeline *pipeline)
727 {
728 int i;
729 struct instance *instance;
730
731 ARRAY_ELEMENT_FOREACH (&pipeline->instances, i, instance) {
732 struct dsp_module *module = instance->module;
733 if (instance->instantiated) {
734 module->deinstantiate(module);
735 instance->instantiated = 0;
736 }
737 }
738 pipeline->sample_rate = 0;
739 }
740
cras_dsp_pipeline_get_delay(struct pipeline * pipeline)741 int cras_dsp_pipeline_get_delay(struct pipeline *pipeline)
742 {
743 return pipeline->sink_instance->total_delay;
744 }
745
cras_dsp_pipeline_get_sample_rate(struct pipeline * pipeline)746 int cras_dsp_pipeline_get_sample_rate(struct pipeline *pipeline)
747 {
748 return pipeline->sample_rate;
749 }
750
cras_dsp_pipeline_get_num_input_channels(struct pipeline * pipeline)751 int cras_dsp_pipeline_get_num_input_channels(struct pipeline *pipeline)
752 {
753 return pipeline->input_channels;
754 }
755
cras_dsp_pipeline_get_num_output_channels(struct pipeline * pipeline)756 int cras_dsp_pipeline_get_num_output_channels(struct pipeline *pipeline)
757 {
758 return pipeline->output_channels;
759 }
760
cras_dsp_pipeline_get_peak_audio_buffers(struct pipeline * pipeline)761 int cras_dsp_pipeline_get_peak_audio_buffers(struct pipeline *pipeline)
762 {
763 return pipeline->peak_buf;
764 }
765
find_buffer(struct pipeline * pipeline,audio_port_array * audio_ports,int index)766 static float *find_buffer(struct pipeline *pipeline,
767 audio_port_array *audio_ports, int index)
768 {
769 int i;
770 struct audio_port *audio_port;
771
772 ARRAY_ELEMENT_FOREACH (audio_ports, i, audio_port) {
773 if (audio_port->original_index == index)
774 return pipeline->buffers[audio_port->buf_index];
775 }
776 return NULL;
777 }
778
cras_dsp_pipeline_get_source_buffer(struct pipeline * pipeline,int index)779 float *cras_dsp_pipeline_get_source_buffer(struct pipeline *pipeline, int index)
780 {
781 return find_buffer(pipeline,
782 &pipeline->source_instance->output_audio_ports,
783 index);
784 }
785
cras_dsp_pipeline_get_sink_buffer(struct pipeline * pipeline,int index)786 float *cras_dsp_pipeline_get_sink_buffer(struct pipeline *pipeline, int index)
787 {
788 return find_buffer(pipeline,
789 &pipeline->sink_instance->input_audio_ports, index);
790 }
791
cras_dsp_pipeline_set_sink_ext_module(struct pipeline * pipeline,struct ext_dsp_module * ext_module)792 void cras_dsp_pipeline_set_sink_ext_module(struct pipeline *pipeline,
793 struct ext_dsp_module *ext_module)
794 {
795 cras_dsp_module_set_sink_ext_module(pipeline->sink_instance->module,
796 ext_module);
797 }
798
cras_dsp_pipeline_get_ini(struct pipeline * pipeline)799 struct ini *cras_dsp_pipeline_get_ini(struct pipeline *pipeline)
800 {
801 return pipeline->ini;
802 }
803
cras_dsp_pipeline_run(struct pipeline * pipeline,int sample_count)804 void cras_dsp_pipeline_run(struct pipeline *pipeline, int sample_count)
805 {
806 int i;
807 struct instance *instance;
808
809 ARRAY_ELEMENT_FOREACH (&pipeline->instances, i, instance) {
810 struct dsp_module *module = instance->module;
811 module->run(module, sample_count);
812 }
813 }
814
cras_dsp_pipeline_add_statistic(struct pipeline * pipeline,const struct timespec * time_delta,int samples)815 void cras_dsp_pipeline_add_statistic(struct pipeline *pipeline,
816 const struct timespec *time_delta,
817 int samples)
818 {
819 int64_t t;
820 if (samples <= 0)
821 return;
822
823 t = time_delta->tv_sec * 1000000000LL + time_delta->tv_nsec;
824
825 if (pipeline->total_blocks == 0) {
826 pipeline->max_time = t;
827 pipeline->min_time = t;
828 } else {
829 pipeline->max_time = MAX(pipeline->max_time, t);
830 pipeline->min_time = MIN(pipeline->min_time, t);
831 }
832
833 pipeline->total_blocks++;
834 pipeline->total_samples += samples;
835 pipeline->total_time += t;
836 }
837
cras_dsp_pipeline_apply(struct pipeline * pipeline,uint8_t * buf,snd_pcm_format_t format,unsigned int frames)838 int cras_dsp_pipeline_apply(struct pipeline *pipeline, uint8_t *buf,
839 snd_pcm_format_t format, unsigned int frames)
840 {
841 size_t remaining;
842 size_t chunk;
843 size_t i;
844 unsigned int input_channels = pipeline->input_channels;
845 unsigned int output_channels = pipeline->output_channels;
846 float *source[input_channels];
847 float *sink[output_channels];
848 struct timespec begin, end, delta;
849 int rc;
850
851 if (!pipeline || frames == 0)
852 return 0;
853
854 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &begin);
855
856 /* get pointers to source and sink buffers */
857 for (i = 0; i < input_channels; i++)
858 source[i] = cras_dsp_pipeline_get_source_buffer(pipeline, i);
859 for (i = 0; i < output_channels; i++)
860 sink[i] = cras_dsp_pipeline_get_sink_buffer(pipeline, i);
861
862 remaining = frames;
863
864 /* process at most DSP_BUFFER_SIZE frames each loop */
865 while (remaining > 0) {
866 chunk = MIN(remaining, (size_t)DSP_BUFFER_SIZE);
867
868 /* deinterleave and convert to float */
869 rc = dsp_util_deinterleave(buf, source, input_channels, format,
870 chunk);
871 if (rc)
872 return rc;
873
874 /* Run the pipeline */
875 cras_dsp_pipeline_run(pipeline, chunk);
876
877 /* interleave and convert back to int16_t */
878 rc = dsp_util_interleave(sink, buf, output_channels, format,
879 chunk);
880 if (rc)
881 return rc;
882
883 buf += chunk * output_channels * PCM_FORMAT_WIDTH(format) / 8;
884 remaining -= chunk;
885 }
886
887 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end);
888 subtract_timespecs(&end, &begin, &delta);
889 cras_dsp_pipeline_add_statistic(pipeline, &delta, frames);
890 return 0;
891 }
892
cras_dsp_pipeline_free(struct pipeline * pipeline)893 void cras_dsp_pipeline_free(struct pipeline *pipeline)
894 {
895 int i;
896 struct instance *instance;
897
898 ARRAY_ELEMENT_FOREACH (&pipeline->instances, i, instance) {
899 struct dsp_module *module = instance->module;
900 instance->plugin = NULL;
901 ARRAY_FREE(&instance->input_audio_ports);
902 ARRAY_FREE(&instance->input_control_ports);
903 ARRAY_FREE(&instance->output_audio_ports);
904 ARRAY_FREE(&instance->output_control_ports);
905
906 if (module) {
907 if (instance->instantiated) {
908 module->deinstantiate(module);
909 instance->instantiated = 0;
910 }
911 module->free_module(module);
912 instance->module = NULL;
913 }
914 }
915
916 pipeline->ini = NULL;
917 ARRAY_FREE(&pipeline->instances);
918
919 for (i = 0; i < pipeline->peak_buf; i++)
920 free(pipeline->buffers[i]);
921 free(pipeline->buffers);
922 free(pipeline);
923 }
924
dump_audio_ports(struct dumper * d,const char * name,audio_port_array * audio_ports)925 static void dump_audio_ports(struct dumper *d, const char *name,
926 audio_port_array *audio_ports)
927 {
928 int i;
929 struct audio_port *audio_port;
930 int n = ARRAY_COUNT(audio_ports);
931
932 if (n == 0)
933 return;
934 dumpf(d, " %s (%d) =\n", name, n);
935
936 ARRAY_ELEMENT_FOREACH (audio_ports, i, audio_port) {
937 dumpf(d, " %p, peer %p, orig=%d, buf=%d\n", audio_port,
938 audio_port->peer, audio_port->original_index,
939 audio_port->buf_index);
940 }
941 }
942
dump_control_ports(struct dumper * d,const char * name,control_port_array * control_ports)943 static void dump_control_ports(struct dumper *d, const char *name,
944 control_port_array *control_ports)
945 {
946 int i;
947 struct control_port *control_port;
948 int n = ARRAY_COUNT(control_ports);
949
950 if (n == 0)
951 return;
952 dumpf(d, " %s (%d) =\n", name, ARRAY_COUNT(control_ports));
953
954 ARRAY_ELEMENT_FOREACH (control_ports, i, control_port) {
955 dumpf(d, " %p, peer %p, orig=%d, value=%g\n", control_port,
956 control_port->peer, control_port->original_index,
957 control_port->value);
958 }
959 }
960
cras_dsp_pipeline_dump(struct dumper * d,struct pipeline * pipeline)961 void cras_dsp_pipeline_dump(struct dumper *d, struct pipeline *pipeline)
962 {
963 int i;
964 struct instance *instance;
965
966 dumpf(d, "---- pipeline dump begin ----\n");
967 dumpf(d, "pipeline (%s):\n", pipeline->purpose);
968 dumpf(d, " input channels: %d\n", pipeline->input_channels);
969 dumpf(d, " output channels: %d\n", pipeline->output_channels);
970 dumpf(d, " sample_rate: %d\n", pipeline->sample_rate);
971 dumpf(d, " processed samples: %" PRId64 "\n", pipeline->total_samples);
972 dumpf(d, " processed blocks: %" PRId64 "\n", pipeline->total_blocks);
973 dumpf(d, " total processing time: %" PRId64 "ns\n",
974 pipeline->total_time);
975 if (pipeline->total_blocks) {
976 dumpf(d, " average block size: %" PRId64 "\n",
977 pipeline->total_samples / pipeline->total_blocks);
978 dumpf(d, " avg processing time per block: %" PRId64 "ns\n",
979 pipeline->total_time / pipeline->total_blocks);
980 }
981 dumpf(d, " min processing time per block: %" PRId64 "ns\n",
982 pipeline->min_time);
983 dumpf(d, " max processing time per block: %" PRId64 "ns\n",
984 pipeline->max_time);
985 if (pipeline->total_samples)
986 dumpf(d, " cpu load: %g%%\n",
987 pipeline->total_time * 1e-9 / pipeline->total_samples *
988 pipeline->sample_rate * 100);
989 dumpf(d, " instances (%d):\n", ARRAY_COUNT(&pipeline->instances));
990 ARRAY_ELEMENT_FOREACH (&pipeline->instances, i, instance) {
991 struct dsp_module *module = instance->module;
992 dumpf(d, " [%d]%s mod=%p, total delay=%d\n", i,
993 instance->plugin->title, module, instance->total_delay);
994 if (module)
995 module->dump(module, d);
996 dump_audio_ports(d, "input_audio_ports",
997 &instance->input_audio_ports);
998 dump_audio_ports(d, "output_audio_ports",
999 &instance->output_audio_ports);
1000 dump_control_ports(d, "input_control_ports",
1001 &instance->input_control_ports);
1002 dump_control_ports(d, "output_control_ports",
1003 &instance->output_control_ports);
1004 }
1005 dumpf(d, " peak_buf = %d\n", pipeline->peak_buf);
1006 dumpf(d, "---- pipeline dump end ----\n");
1007 }
1008