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 */
27 struct 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
37 static struct dumper *syslog_dumper;
38 static const char *ini_filename;
39 static struct ini *ini;
40 static struct cras_dsp_context *context_list;
41
initialize_environment(struct cras_expr_env * env)42 static 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
destroy_pipeline(struct pipeline * pipeline)51 static void destroy_pipeline(struct pipeline *pipeline)
52 {
53 struct ini *private_ini;
54
55 private_ini = cras_dsp_pipeline_get_ini(pipeline);
56 cras_dsp_pipeline_free(pipeline);
57
58 /*
59 * If pipeline is using an dsp ini other than the global one, free
60 * this ini so its life cycle is aligned with the associated dsp
61 * pipeline.
62 */
63 if (private_ini && (private_ini != ini))
64 cras_dsp_ini_free(private_ini);
65 }
66
prepare_pipeline(struct cras_dsp_context * ctx,struct ini * target_ini)67 static struct pipeline *prepare_pipeline(struct cras_dsp_context *ctx,
68 struct ini *target_ini)
69 {
70 struct pipeline *pipeline;
71 const char *purpose = ctx->purpose;
72
73 pipeline = cras_dsp_pipeline_create(target_ini, &ctx->env, purpose);
74
75 if (pipeline) {
76 syslog(LOG_DEBUG, "pipeline created");
77 } else {
78 syslog(LOG_DEBUG, "cannot create pipeline");
79 goto bail;
80 }
81
82 if (cras_dsp_pipeline_load(pipeline) != 0) {
83 syslog(LOG_ERR, "cannot load pipeline");
84 goto bail;
85 }
86
87 if (cras_dsp_pipeline_instantiate(pipeline, ctx->sample_rate) != 0) {
88 syslog(LOG_ERR, "cannot instantiate pipeline");
89 goto bail;
90 }
91
92 if (cras_dsp_pipeline_get_sample_rate(pipeline) != ctx->sample_rate) {
93 syslog(LOG_ERR, "pipeline sample rate mismatch (%d vs %d)",
94 cras_dsp_pipeline_get_sample_rate(pipeline),
95 ctx->sample_rate);
96 goto bail;
97 }
98
99 return pipeline;
100
101 bail:
102 if (pipeline)
103 destroy_pipeline(pipeline);
104 return NULL;
105 }
106
cmd_load_pipeline(struct cras_dsp_context * ctx,struct ini * target_ini)107 static void cmd_load_pipeline(struct cras_dsp_context *ctx,
108 struct ini *target_ini)
109 {
110 struct pipeline *pipeline, *old_pipeline;
111
112 pipeline = target_ini ? prepare_pipeline(ctx, target_ini) : NULL;
113
114 /* This locking is short to avoild blocking audio thread. */
115 pthread_mutex_lock(&ctx->mutex);
116 old_pipeline = ctx->pipeline;
117 ctx->pipeline = pipeline;
118 pthread_mutex_unlock(&ctx->mutex);
119
120 if (old_pipeline)
121 destroy_pipeline(old_pipeline);
122 }
123
cmd_reload_ini()124 static void cmd_reload_ini()
125 {
126 struct ini *old_ini = ini;
127 struct cras_dsp_context *ctx;
128
129 ini = cras_dsp_ini_create(ini_filename);
130 if (!ini) {
131 syslog(LOG_DEBUG, "cannot create dsp ini");
132 return;
133 }
134
135 DL_FOREACH (context_list, ctx) {
136 cmd_load_pipeline(ctx, ini);
137 }
138
139 if (old_ini)
140 cras_dsp_ini_free(old_ini);
141 }
142
143 /* Exported functions */
144
cras_dsp_init(const char * filename)145 void cras_dsp_init(const char *filename)
146 {
147 dsp_enable_flush_denormal_to_zero();
148 ini_filename = strdup(filename);
149 syslog_dumper = syslog_dumper_create(LOG_ERR);
150 cmd_reload_ini();
151 }
152
cras_dsp_stop()153 void cras_dsp_stop()
154 {
155 syslog_dumper_free(syslog_dumper);
156 free((char *)ini_filename);
157 if (ini) {
158 cras_dsp_ini_free(ini);
159 ini = NULL;
160 }
161 }
162
cras_dsp_context_new(int sample_rate,const char * purpose)163 struct cras_dsp_context *cras_dsp_context_new(int sample_rate,
164 const char *purpose)
165 {
166 struct cras_dsp_context *ctx = calloc(1, sizeof(*ctx));
167
168 pthread_mutex_init(&ctx->mutex, NULL);
169 initialize_environment(&ctx->env);
170 ctx->sample_rate = sample_rate;
171 ctx->purpose = strdup(purpose);
172
173 DL_APPEND(context_list, ctx);
174 return ctx;
175 }
176
cras_dsp_context_free(struct cras_dsp_context * ctx)177 void cras_dsp_context_free(struct cras_dsp_context *ctx)
178 {
179 DL_DELETE(context_list, ctx);
180
181 pthread_mutex_destroy(&ctx->mutex);
182 if (ctx->pipeline) {
183 destroy_pipeline(ctx->pipeline);
184 ctx->pipeline = NULL;
185 }
186 cras_expr_env_free(&ctx->env);
187 free((char *)ctx->purpose);
188 free(ctx);
189 }
190
cras_dsp_set_variable_string(struct cras_dsp_context * ctx,const char * key,const char * value)191 void cras_dsp_set_variable_string(struct cras_dsp_context *ctx, const char *key,
192 const char *value)
193 {
194 cras_expr_env_set_variable_string(&ctx->env, key, value);
195 }
196
cras_dsp_set_variable_boolean(struct cras_dsp_context * ctx,const char * key,char value)197 void cras_dsp_set_variable_boolean(struct cras_dsp_context *ctx,
198 const char *key, char value)
199 {
200 cras_expr_env_set_variable_boolean(&ctx->env, key, value);
201 }
202
cras_dsp_load_pipeline(struct cras_dsp_context * ctx)203 void cras_dsp_load_pipeline(struct cras_dsp_context *ctx)
204 {
205 cmd_load_pipeline(ctx, ini);
206 }
207
cras_dsp_load_dummy_pipeline(struct cras_dsp_context * ctx,unsigned int num_channels)208 void cras_dsp_load_dummy_pipeline(struct cras_dsp_context *ctx,
209 unsigned int num_channels)
210 {
211 struct ini *dummy_ini;
212 dummy_ini = create_dummy_ini(ctx->purpose, num_channels);
213 if (dummy_ini == NULL)
214 syslog(LOG_ERR, "Failed to create dummy ini");
215 else
216 cmd_load_pipeline(ctx, dummy_ini);
217 }
218
cras_dsp_get_pipeline(struct cras_dsp_context * ctx)219 struct pipeline *cras_dsp_get_pipeline(struct cras_dsp_context *ctx)
220 {
221 pthread_mutex_lock(&ctx->mutex);
222 if (!ctx->pipeline) {
223 pthread_mutex_unlock(&ctx->mutex);
224 return NULL;
225 }
226 return ctx->pipeline;
227 }
228
cras_dsp_put_pipeline(struct cras_dsp_context * ctx)229 void cras_dsp_put_pipeline(struct cras_dsp_context *ctx)
230 {
231 pthread_mutex_unlock(&ctx->mutex);
232 }
233
cras_dsp_reload_ini()234 void cras_dsp_reload_ini()
235 {
236 cmd_reload_ini();
237 }
238
cras_dsp_dump_info()239 void cras_dsp_dump_info()
240 {
241 struct pipeline *pipeline;
242 struct cras_dsp_context *ctx;
243
244 if (ini)
245 cras_dsp_ini_dump(syslog_dumper, ini);
246 DL_FOREACH (context_list, ctx) {
247 cras_expr_env_dump(syslog_dumper, &ctx->env);
248 pipeline = ctx->pipeline;
249 if (pipeline)
250 cras_dsp_pipeline_dump(syslog_dumper, pipeline);
251 }
252 }
253
cras_dsp_num_output_channels(const struct cras_dsp_context * ctx)254 unsigned int cras_dsp_num_output_channels(const struct cras_dsp_context *ctx)
255 {
256 return cras_dsp_pipeline_get_num_output_channels(ctx->pipeline);
257 }
258
cras_dsp_num_input_channels(const struct cras_dsp_context * ctx)259 unsigned int cras_dsp_num_input_channels(const struct cras_dsp_context *ctx)
260 {
261 return cras_dsp_pipeline_get_num_input_channels(ctx->pipeline);
262 }
263