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 <pthread.h>
7#include <syslog.h>
8#include "dumper.h"
9#include "cras_expr.h"
10#include "cras_dsp_ini.h"
11#include "cras_dsp_pipeline.h"
12#include "dsp_util.h"
13#include "utlist.h"
14
15/* We have a dsp_context for each pipeline. The context records the
16 * parameters used to create a pipeline, so the pipeline can be
17 * (re-)loaded later. The pipeline is (re-)loaded in the following
18 * cases:
19 *
20 * (1) The client asks to (re-)load it with cras_load_pipeline().
21 * (2) The client asks to reload the ini with cras_reload_ini().
22 *
23 * The pipeline is (re-)loaded asynchronously in an internal thread,
24 * so the client needs to use cras_dsp_get_pipeline() and
25 * cras_dsp_put_pipeline() to safely access the pipeline.
26 */
27struct cras_dsp_context {
28	pthread_mutex_t mutex;
29	struct pipeline *pipeline;
30
31	struct cras_expr_env env;
32	int sample_rate;
33	const char *purpose;
34	struct cras_dsp_context *prev, *next;
35};
36
37static struct dumper *syslog_dumper;
38static const char *ini_filename;
39static struct ini *ini;
40static struct cras_dsp_context *context_list;
41
42static void initialize_environment(struct cras_expr_env *env)
43{
44	cras_expr_env_install_builtins(env);
45	cras_expr_env_set_variable_boolean(env, "disable_eq", 0);
46	cras_expr_env_set_variable_boolean(env, "disable_drc", 0);
47	cras_expr_env_set_variable_string(env, "dsp_name", "");
48	cras_expr_env_set_variable_boolean(env, "swap_lr_disabled", 1);
49}
50
51static struct pipeline *prepare_pipeline(struct cras_dsp_context *ctx)
52{
53	struct pipeline *pipeline;
54	const char *purpose = ctx->purpose;
55
56	if (!ini)
57		return NULL;
58
59	pipeline = cras_dsp_pipeline_create(ini, &ctx->env, purpose);
60
61	if (pipeline) {
62		syslog(LOG_DEBUG, "pipeline created");
63	} else {
64		syslog(LOG_DEBUG, "cannot create pipeline");
65		goto bail;
66	}
67
68	if (cras_dsp_pipeline_load(pipeline) != 0) {
69		syslog(LOG_ERR, "cannot load pipeline");
70		goto bail;
71	}
72
73	if (cras_dsp_pipeline_instantiate(pipeline, ctx->sample_rate) != 0) {
74		syslog(LOG_ERR, "cannot instantiate pipeline");
75		goto bail;
76	}
77
78	if (cras_dsp_pipeline_get_sample_rate(pipeline) != ctx->sample_rate) {
79		syslog(LOG_ERR, "pipeline sample rate mismatch (%d vs %d)",
80		       cras_dsp_pipeline_get_sample_rate(pipeline),
81		       ctx->sample_rate);
82		goto bail;
83	}
84
85	return pipeline;
86
87bail:
88	if (pipeline)
89		cras_dsp_pipeline_free(pipeline);
90	return NULL;
91}
92
93static void cmd_load_pipeline(struct cras_dsp_context *ctx)
94{
95	struct pipeline *pipeline, *old_pipeline;
96
97	pipeline = prepare_pipeline(ctx);
98
99	/* This locking is short to avoild blocking audio thread. */
100	pthread_mutex_lock(&ctx->mutex);
101	old_pipeline = ctx->pipeline;
102	ctx->pipeline = pipeline;
103	pthread_mutex_unlock(&ctx->mutex);
104
105	if (old_pipeline)
106		cras_dsp_pipeline_free(old_pipeline);
107}
108
109static void cmd_reload_ini()
110{
111	struct ini *old_ini = ini;
112	struct cras_dsp_context *ctx;
113
114	ini = cras_dsp_ini_create(ini_filename);
115	if (!ini)
116		syslog(LOG_ERR, "cannot create dsp ini");
117
118	DL_FOREACH(context_list, ctx) {
119		cmd_load_pipeline(ctx);
120	}
121
122	if (old_ini)
123		cras_dsp_ini_free(old_ini);
124}
125
126/* Exported functions */
127
128void cras_dsp_init(const char *filename)
129{
130	dsp_enable_flush_denormal_to_zero();
131	ini_filename = strdup(filename);
132	syslog_dumper = syslog_dumper_create(LOG_ERR);
133	cmd_reload_ini();
134}
135
136void cras_dsp_stop()
137{
138	syslog_dumper_free(syslog_dumper);
139	free((char *)ini_filename);
140	if (ini) {
141		cras_dsp_ini_free(ini);
142		ini = NULL;
143	}
144}
145
146struct cras_dsp_context *cras_dsp_context_new(int sample_rate,
147					      const char *purpose)
148{
149	struct cras_dsp_context *ctx = calloc(1, sizeof(*ctx));
150
151	pthread_mutex_init(&ctx->mutex, NULL);
152	initialize_environment(&ctx->env);
153	ctx->sample_rate = sample_rate;
154	ctx->purpose = strdup(purpose);
155
156	DL_APPEND(context_list, ctx);
157	return ctx;
158}
159
160void cras_dsp_context_free(struct cras_dsp_context *ctx)
161{
162	DL_DELETE(context_list, ctx);
163
164	pthread_mutex_destroy(&ctx->mutex);
165	if (ctx->pipeline) {
166		cras_dsp_pipeline_free(ctx->pipeline);
167		ctx->pipeline = NULL;
168	}
169	cras_expr_env_free(&ctx->env);
170	free((char *)ctx->purpose);
171	free(ctx);
172}
173
174void cras_dsp_set_variable_string(struct cras_dsp_context *ctx, const char *key,
175			   const char *value)
176{
177	cras_expr_env_set_variable_string(&ctx->env, key, value);
178}
179
180void cras_dsp_set_variable_boolean(struct cras_dsp_context *ctx,
181				   const char *key,
182				   char value)
183{
184	cras_expr_env_set_variable_boolean(&ctx->env, key, value);
185}
186
187void cras_dsp_load_pipeline(struct cras_dsp_context *ctx)
188{
189	cmd_load_pipeline(ctx);
190}
191
192struct pipeline *cras_dsp_get_pipeline(struct cras_dsp_context *ctx)
193{
194	pthread_mutex_lock(&ctx->mutex);
195	if (!ctx->pipeline) {
196		pthread_mutex_unlock(&ctx->mutex);
197		return NULL;
198	}
199	return ctx->pipeline;
200}
201
202void cras_dsp_put_pipeline(struct cras_dsp_context *ctx)
203{
204	pthread_mutex_unlock(&ctx->mutex);
205}
206
207void cras_dsp_reload_ini()
208{
209	cmd_reload_ini();
210}
211
212void cras_dsp_dump_info()
213{
214	struct pipeline *pipeline;
215	struct cras_dsp_context *ctx;
216
217	if (ini)
218		cras_dsp_ini_dump(syslog_dumper, ini);
219	DL_FOREACH(context_list, ctx) {
220		cras_expr_env_dump(syslog_dumper, &ctx->env);
221		pipeline = ctx->pipeline;
222		if (pipeline)
223			cras_dsp_pipeline_dump(syslog_dumper, pipeline);
224	}
225}
226
227unsigned int cras_dsp_num_output_channels(const struct cras_dsp_context *ctx)
228{
229	return cras_dsp_pipeline_get_num_output_channels(ctx->pipeline);
230}
231
232unsigned int cras_dsp_num_input_channels(const struct cras_dsp_context *ctx)
233{
234	return cras_dsp_pipeline_get_num_input_channels(ctx->pipeline);
235}
236