• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <stdlib.h>
7 #include "cras_dsp_module.h"
8 #include "drc.h"
9 #include "dsp_util.h"
10 #include "dcblock.h"
11 #include "eq.h"
12 #include "eq2.h"
13 
14 /*
15  *  empty module functions (for source and sink)
16  */
empty_instantiate(struct dsp_module * module,unsigned long sample_rate)17 static int empty_instantiate(struct dsp_module *module,
18 			     unsigned long sample_rate)
19 {
20 	return 0;
21 }
22 
empty_connect_port(struct dsp_module * module,unsigned long port,float * data_location)23 static void empty_connect_port(struct dsp_module *module, unsigned long port,
24 			       float *data_location)
25 {
26 }
27 
empty_get_delay(struct dsp_module * module)28 static int empty_get_delay(struct dsp_module *module)
29 {
30 	return 0;
31 }
32 
empty_run(struct dsp_module * module,unsigned long sample_count)33 static void empty_run(struct dsp_module *module, unsigned long sample_count)
34 {
35 }
36 
empty_deinstantiate(struct dsp_module * module)37 static void empty_deinstantiate(struct dsp_module *module)
38 {
39 }
40 
empty_free_module(struct dsp_module * module)41 static void empty_free_module(struct dsp_module *module)
42 {
43 	free(module);
44 }
45 
empty_get_properties(struct dsp_module * module)46 static int empty_get_properties(struct dsp_module *module)
47 {
48 	return 0;
49 }
50 
empty_dump(struct dsp_module * module,struct dumper * d)51 static void empty_dump(struct dsp_module *module, struct dumper *d)
52 {
53 	dumpf(d, "built-in module\n");
54 }
55 
empty_init_module(struct dsp_module * module)56 static void empty_init_module(struct dsp_module *module)
57 {
58 	module->instantiate = &empty_instantiate;
59 	module->connect_port = &empty_connect_port;
60 	module->get_delay = &empty_get_delay;
61 	module->run = &empty_run;
62 	module->deinstantiate = &empty_deinstantiate;
63 	module->free_module = &empty_free_module;
64 	module->get_properties = &empty_get_properties;
65 	module->dump = &empty_dump;
66 }
67 
68 /*
69  *  swap_lr module functions
70  */
swap_lr_instantiate(struct dsp_module * module,unsigned long sample_rate)71 static int swap_lr_instantiate(struct dsp_module *module,
72 			       unsigned long sample_rate)
73 {
74 	module->data = calloc(4, sizeof(float *));
75 	return 0;
76 }
77 
swap_lr_connect_port(struct dsp_module * module,unsigned long port,float * data_location)78 static void swap_lr_connect_port(struct dsp_module *module, unsigned long port,
79 				 float *data_location)
80 {
81 	float **ports;
82 	ports = (float **)module->data;
83 	ports[port] = data_location;
84 }
85 
swap_lr_run(struct dsp_module * module,unsigned long sample_count)86 static void swap_lr_run(struct dsp_module *module, unsigned long sample_count)
87 {
88 	size_t i;
89 	float **ports = (float **)module->data;
90 
91 	/* This module runs dsp in-place, so ports[0] == ports[2],
92 	 * ports[1] == ports[3]. Here we swap data on two channels.
93 	 */
94 	for (i = 0; i < sample_count; i++) {
95 		float temp = ports[0][i];
96 		ports[2][i] = ports[1][i];
97 		ports[3][i] = temp;
98 	}
99 }
100 
swap_lr_deinstantiate(struct dsp_module * module)101 static void swap_lr_deinstantiate(struct dsp_module *module)
102 {
103 	free(module->data);
104 }
105 
swap_lr_init_module(struct dsp_module * module)106 static void swap_lr_init_module(struct dsp_module *module)
107 {
108 	module->instantiate = &swap_lr_instantiate;
109 	module->connect_port = &swap_lr_connect_port;
110 	module->get_delay = &empty_get_delay;
111 	module->run = &swap_lr_run;
112 	module->deinstantiate = &swap_lr_deinstantiate;
113 	module->free_module = &empty_free_module;
114 	module->get_properties = &empty_get_properties;
115 }
116 
117 /*
118  *  invert_lr module functions
119  */
invert_lr_instantiate(struct dsp_module * module,unsigned long sample_rate)120 static int invert_lr_instantiate(struct dsp_module *module,
121 				 unsigned long sample_rate)
122 {
123 	module->data = calloc(4, sizeof(float *));
124 	return 0;
125 }
126 
invert_lr_connect_port(struct dsp_module * module,unsigned long port,float * data_location)127 static void invert_lr_connect_port(struct dsp_module *module,
128 				   unsigned long port, float *data_location)
129 {
130 	float **ports;
131 	ports = (float **)module->data;
132 	ports[port] = data_location;
133 }
134 
invert_lr_run(struct dsp_module * module,unsigned long sample_count)135 static void invert_lr_run(struct dsp_module *module, unsigned long sample_count)
136 {
137 	size_t i;
138 	float **ports = (float **)module->data;
139 
140 	for (i = 0; i < sample_count; i++) {
141 		ports[2][i] = -ports[0][i];
142 		ports[3][i] = ports[1][i];
143 	}
144 }
145 
invert_lr_deinstantiate(struct dsp_module * module)146 static void invert_lr_deinstantiate(struct dsp_module *module)
147 {
148 	free(module->data);
149 }
150 
invert_lr_init_module(struct dsp_module * module)151 static void invert_lr_init_module(struct dsp_module *module)
152 {
153 	module->instantiate = &invert_lr_instantiate;
154 	module->connect_port = &invert_lr_connect_port;
155 	module->get_delay = &empty_get_delay;
156 	module->run = &invert_lr_run;
157 	module->deinstantiate = &invert_lr_deinstantiate;
158 	module->free_module = &empty_free_module;
159 	module->get_properties = &empty_get_properties;
160 }
161 
162 /*
163  *  mix_stereo module functions
164  */
mix_stereo_instantiate(struct dsp_module * module,unsigned long sample_rate)165 static int mix_stereo_instantiate(struct dsp_module *module,
166 				  unsigned long sample_rate)
167 {
168 	module->data = calloc(4, sizeof(float *));
169 	return 0;
170 }
171 
mix_stereo_connect_port(struct dsp_module * module,unsigned long port,float * data_location)172 static void mix_stereo_connect_port(struct dsp_module *module,
173 				    unsigned long port, float *data_location)
174 {
175 	float **ports;
176 	ports = (float **)module->data;
177 	ports[port] = data_location;
178 }
179 
mix_stereo_run(struct dsp_module * module,unsigned long sample_count)180 static void mix_stereo_run(struct dsp_module *module,
181 			   unsigned long sample_count)
182 {
183 	size_t i;
184 	float tmp;
185 	float **ports = (float **)module->data;
186 
187 	for (i = 0; i < sample_count; i++) {
188 		tmp = ports[0][i] + ports[1][i];
189 		ports[2][i] = tmp;
190 		ports[3][i] = tmp;
191 	}
192 }
193 
mix_stereo_deinstantiate(struct dsp_module * module)194 static void mix_stereo_deinstantiate(struct dsp_module *module)
195 {
196 	free(module->data);
197 }
198 
mix_stereo_init_module(struct dsp_module * module)199 static void mix_stereo_init_module(struct dsp_module *module)
200 {
201 	module->instantiate = &mix_stereo_instantiate;
202 	module->connect_port = &mix_stereo_connect_port;
203 	module->get_delay = &empty_get_delay;
204 	module->run = &mix_stereo_run;
205 	module->deinstantiate = &mix_stereo_deinstantiate;
206 	module->free_module = &empty_free_module;
207 	module->get_properties = &empty_get_properties;
208 	module->dump = &empty_dump;
209 }
210 
211 /*
212  *  dcblock module functions
213  */
214 struct dcblock_data {
215 	struct dcblock *dcblockl;
216 	struct dcblock *dcblockr;
217 	unsigned long sample_rate;
218 
219 	/* One port for input, one for output, and 1 parameter */
220 	float *ports[5];
221 };
222 
dcblock_instantiate(struct dsp_module * module,unsigned long sample_rate)223 static int dcblock_instantiate(struct dsp_module *module,
224 			       unsigned long sample_rate)
225 {
226 	struct dcblock_data *data;
227 
228 	module->data = calloc(1, sizeof(struct dcblock_data));
229 	data = (struct dcblock_data *)module->data;
230 	data->sample_rate = sample_rate;
231 
232 	return 0;
233 }
234 
dcblock_connect_port(struct dsp_module * module,unsigned long port,float * data_location)235 static void dcblock_connect_port(struct dsp_module *module, unsigned long port,
236 				 float *data_location)
237 {
238 	struct dcblock_data *data = (struct dcblock_data *)module->data;
239 	data->ports[port] = data_location;
240 }
241 
dcblock_run(struct dsp_module * module,unsigned long sample_count)242 static void dcblock_run(struct dsp_module *module, unsigned long sample_count)
243 {
244 	struct dcblock_data *data = (struct dcblock_data *)module->data;
245 	if (!data->dcblockl)
246 		data->dcblockl =
247 			dcblock_new(*data->ports[4], data->sample_rate);
248 	if (!data->dcblockr)
249 		data->dcblockr =
250 			dcblock_new(*data->ports[4], data->sample_rate);
251 	if (data->ports[0] != data->ports[2])
252 		memcpy(data->ports[2], data->ports[0],
253 		       sizeof(float) * sample_count);
254 	if (data->ports[1] != data->ports[3])
255 		memcpy(data->ports[3], data->ports[1],
256 		       sizeof(float) * sample_count);
257 
258 	dcblock_process(data->dcblockl, data->ports[2], (int)sample_count);
259 	dcblock_process(data->dcblockr, data->ports[3], (int)sample_count);
260 }
261 
dcblock_deinstantiate(struct dsp_module * module)262 static void dcblock_deinstantiate(struct dsp_module *module)
263 {
264 	struct dcblock_data *data = (struct dcblock_data *)module->data;
265 	if (data->dcblockl)
266 		dcblock_free(data->dcblockl);
267 	if (data->dcblockr)
268 		dcblock_free(data->dcblockr);
269 	free(data);
270 }
271 
dcblock_init_module(struct dsp_module * module)272 static void dcblock_init_module(struct dsp_module *module)
273 {
274 	module->instantiate = &dcblock_instantiate;
275 	module->connect_port = &dcblock_connect_port;
276 	module->get_delay = &empty_get_delay;
277 	module->run = &dcblock_run;
278 	module->deinstantiate = &dcblock_deinstantiate;
279 	module->free_module = &empty_free_module;
280 	module->get_properties = &empty_get_properties;
281 	module->dump = &empty_dump;
282 }
283 
284 /*
285  *  eq module functions
286  */
287 struct eq_data {
288 	int sample_rate;
289 	struct eq *eq; /* Initialized in the first call of eq_run() */
290 
291 	/* One port for input, one for output, and 4 parameters per eq */
292 	float *ports[2 + MAX_BIQUADS_PER_EQ * 4];
293 };
294 
eq_instantiate(struct dsp_module * module,unsigned long sample_rate)295 static int eq_instantiate(struct dsp_module *module, unsigned long sample_rate)
296 {
297 	struct eq_data *data;
298 
299 	module->data = calloc(1, sizeof(struct eq_data));
300 	data = (struct eq_data *)module->data;
301 	data->sample_rate = (int)sample_rate;
302 	return 0;
303 }
304 
eq_connect_port(struct dsp_module * module,unsigned long port,float * data_location)305 static void eq_connect_port(struct dsp_module *module, unsigned long port,
306 			    float *data_location)
307 {
308 	struct eq_data *data = (struct eq_data *)module->data;
309 	data->ports[port] = data_location;
310 }
311 
eq_run(struct dsp_module * module,unsigned long sample_count)312 static void eq_run(struct dsp_module *module, unsigned long sample_count)
313 {
314 	struct eq_data *data = (struct eq_data *)module->data;
315 	if (!data->eq) {
316 		float nyquist = data->sample_rate / 2;
317 		int i;
318 
319 		data->eq = eq_new();
320 		for (i = 2; i < 2 + MAX_BIQUADS_PER_EQ * 4; i += 4) {
321 			if (!data->ports[i])
322 				break;
323 			int type = (int)*data->ports[i];
324 			float freq = *data->ports[i + 1];
325 			float Q = *data->ports[i + 2];
326 			float gain = *data->ports[i + 3];
327 			eq_append_biquad(data->eq, type, freq / nyquist, Q,
328 					 gain);
329 		}
330 	}
331 	if (data->ports[0] != data->ports[1])
332 		memcpy(data->ports[1], data->ports[0],
333 		       sizeof(float) * sample_count);
334 	eq_process(data->eq, data->ports[1], (int)sample_count);
335 }
336 
eq_deinstantiate(struct dsp_module * module)337 static void eq_deinstantiate(struct dsp_module *module)
338 {
339 	struct eq_data *data = (struct eq_data *)module->data;
340 	if (data->eq)
341 		eq_free(data->eq);
342 	free(data);
343 }
344 
eq_init_module(struct dsp_module * module)345 static void eq_init_module(struct dsp_module *module)
346 {
347 	module->instantiate = &eq_instantiate;
348 	module->connect_port = &eq_connect_port;
349 	module->get_delay = &empty_get_delay;
350 	module->run = &eq_run;
351 	module->deinstantiate = &eq_deinstantiate;
352 	module->free_module = &empty_free_module;
353 	module->get_properties = &empty_get_properties;
354 	module->dump = &empty_dump;
355 }
356 
357 /*
358  *  eq2 module functions
359  */
360 struct eq2_data {
361 	int sample_rate;
362 	struct eq2 *eq2; /* Initialized in the first call of eq2_run() */
363 
364 	/* Two ports for input, two for output, and 8 parameters per eq pair */
365 	float *ports[4 + MAX_BIQUADS_PER_EQ2 * 8];
366 };
367 
eq2_instantiate(struct dsp_module * module,unsigned long sample_rate)368 static int eq2_instantiate(struct dsp_module *module, unsigned long sample_rate)
369 {
370 	struct eq2_data *data;
371 
372 	module->data = calloc(1, sizeof(struct eq2_data));
373 	data = (struct eq2_data *)module->data;
374 	data->sample_rate = (int)sample_rate;
375 	return 0;
376 }
377 
eq2_connect_port(struct dsp_module * module,unsigned long port,float * data_location)378 static void eq2_connect_port(struct dsp_module *module, unsigned long port,
379 			     float *data_location)
380 {
381 	struct eq2_data *data = (struct eq2_data *)module->data;
382 	data->ports[port] = data_location;
383 }
384 
eq2_run(struct dsp_module * module,unsigned long sample_count)385 static void eq2_run(struct dsp_module *module, unsigned long sample_count)
386 {
387 	struct eq2_data *data = (struct eq2_data *)module->data;
388 	if (!data->eq2) {
389 		float nyquist = data->sample_rate / 2;
390 		int i, channel;
391 
392 		data->eq2 = eq2_new();
393 		for (i = 4; i < 4 + MAX_BIQUADS_PER_EQ2 * 8; i += 8) {
394 			if (!data->ports[i])
395 				break;
396 			for (channel = 0; channel < 2; channel++) {
397 				int k = i + channel * 4;
398 				int type = (int)*data->ports[k];
399 				float freq = *data->ports[k + 1];
400 				float Q = *data->ports[k + 2];
401 				float gain = *data->ports[k + 3];
402 				eq2_append_biquad(data->eq2, channel, type,
403 						  freq / nyquist, Q, gain);
404 			}
405 		}
406 	}
407 
408 	if (data->ports[0] != data->ports[2])
409 		memcpy(data->ports[2], data->ports[0],
410 		       sizeof(float) * sample_count);
411 	if (data->ports[3] != data->ports[1])
412 		memcpy(data->ports[3], data->ports[1],
413 		       sizeof(float) * sample_count);
414 
415 	eq2_process(data->eq2, data->ports[2], data->ports[3],
416 		    (int)sample_count);
417 }
418 
eq2_deinstantiate(struct dsp_module * module)419 static void eq2_deinstantiate(struct dsp_module *module)
420 {
421 	struct eq2_data *data = (struct eq2_data *)module->data;
422 	if (data->eq2)
423 		eq2_free(data->eq2);
424 	free(data);
425 }
426 
eq2_init_module(struct dsp_module * module)427 static void eq2_init_module(struct dsp_module *module)
428 {
429 	module->instantiate = &eq2_instantiate;
430 	module->connect_port = &eq2_connect_port;
431 	module->get_delay = &empty_get_delay;
432 	module->run = &eq2_run;
433 	module->deinstantiate = &eq2_deinstantiate;
434 	module->free_module = &empty_free_module;
435 	module->get_properties = &empty_get_properties;
436 	module->dump = &empty_dump;
437 }
438 
439 /*
440  *  drc module functions
441  */
442 struct drc_data {
443 	int sample_rate;
444 	struct drc *drc; /* Initialized in the first call of drc_run() */
445 
446 	/* Two ports for input, two for output, one for disable_emphasis,
447 	 * and 8 parameters each band */
448 	float *ports[4 + 1 + 8 * 3];
449 };
450 
drc_instantiate(struct dsp_module * module,unsigned long sample_rate)451 static int drc_instantiate(struct dsp_module *module, unsigned long sample_rate)
452 {
453 	struct drc_data *data;
454 
455 	module->data = calloc(1, sizeof(struct drc_data));
456 	data = (struct drc_data *)module->data;
457 	data->sample_rate = (int)sample_rate;
458 	return 0;
459 }
460 
drc_connect_port(struct dsp_module * module,unsigned long port,float * data_location)461 static void drc_connect_port(struct dsp_module *module, unsigned long port,
462 			     float *data_location)
463 {
464 	struct drc_data *data = (struct drc_data *)module->data;
465 	data->ports[port] = data_location;
466 }
467 
drc_get_delay(struct dsp_module * module)468 static int drc_get_delay(struct dsp_module *module)
469 {
470 	struct drc_data *data = (struct drc_data *)module->data;
471 	return DRC_DEFAULT_PRE_DELAY * data->sample_rate;
472 }
473 
drc_run(struct dsp_module * module,unsigned long sample_count)474 static void drc_run(struct dsp_module *module, unsigned long sample_count)
475 {
476 	struct drc_data *data = (struct drc_data *)module->data;
477 	if (!data->drc) {
478 		int i;
479 		float nyquist = data->sample_rate / 2;
480 		struct drc *drc = drc_new(data->sample_rate);
481 
482 		data->drc = drc;
483 		drc->emphasis_disabled = (int)*data->ports[4];
484 		for (i = 0; i < 3; i++) {
485 			int k = 5 + i * 8;
486 			float f = *data->ports[k];
487 			float enable = *data->ports[k + 1];
488 			float threshold = *data->ports[k + 2];
489 			float knee = *data->ports[k + 3];
490 			float ratio = *data->ports[k + 4];
491 			float attack = *data->ports[k + 5];
492 			float release = *data->ports[k + 6];
493 			float boost = *data->ports[k + 7];
494 			drc_set_param(drc, i, PARAM_CROSSOVER_LOWER_FREQ,
495 				      f / nyquist);
496 			drc_set_param(drc, i, PARAM_ENABLED, enable);
497 			drc_set_param(drc, i, PARAM_THRESHOLD, threshold);
498 			drc_set_param(drc, i, PARAM_KNEE, knee);
499 			drc_set_param(drc, i, PARAM_RATIO, ratio);
500 			drc_set_param(drc, i, PARAM_ATTACK, attack);
501 			drc_set_param(drc, i, PARAM_RELEASE, release);
502 			drc_set_param(drc, i, PARAM_POST_GAIN, boost);
503 		}
504 		drc_init(drc);
505 	}
506 	if (data->ports[0] != data->ports[2])
507 		memcpy(data->ports[2], data->ports[0],
508 		       sizeof(float) * sample_count);
509 	if (data->ports[1] != data->ports[3])
510 		memcpy(data->ports[3], data->ports[1],
511 		       sizeof(float) * sample_count);
512 
513 	drc_process(data->drc, &data->ports[2], (int)sample_count);
514 }
515 
drc_deinstantiate(struct dsp_module * module)516 static void drc_deinstantiate(struct dsp_module *module)
517 {
518 	struct drc_data *data = (struct drc_data *)module->data;
519 	if (data->drc)
520 		drc_free(data->drc);
521 	free(data);
522 }
523 
drc_init_module(struct dsp_module * module)524 static void drc_init_module(struct dsp_module *module)
525 {
526 	module->instantiate = &drc_instantiate;
527 	module->connect_port = &drc_connect_port;
528 	module->get_delay = &drc_get_delay;
529 	module->run = &drc_run;
530 	module->deinstantiate = &drc_deinstantiate;
531 	module->free_module = &empty_free_module;
532 	module->get_properties = &empty_get_properties;
533 	module->dump = &empty_dump;
534 }
535 
536 /*
537  * sink module functions
538  */
539 struct sink_data {
540 	struct ext_dsp_module *ext_module;
541 	float *ports[MAX_EXT_DSP_PORTS];
542 };
543 
sink_instantiate(struct dsp_module * module,unsigned long sample_rate)544 static int sink_instantiate(struct dsp_module *module,
545 			    unsigned long sample_rate)
546 {
547 	module->data = (struct sink_data *)calloc(1, sizeof(struct sink_data));
548 	return 0;
549 }
550 
sink_deinstantiate(struct dsp_module * module)551 static void sink_deinstantiate(struct dsp_module *module)
552 {
553 	struct sink_data *data = (struct sink_data *)module->data;
554 	free(data);
555 }
556 
sink_connect_port(struct dsp_module * module,unsigned long port,float * data_location)557 static void sink_connect_port(struct dsp_module *module, unsigned long port,
558 			      float *data_location)
559 {
560 	struct sink_data *data = (struct sink_data *)module->data;
561 	data->ports[port] = data_location;
562 }
563 
sink_run(struct dsp_module * module,unsigned long sample_count)564 static void sink_run(struct dsp_module *module, unsigned long sample_count)
565 {
566 	struct sink_data *data = (struct sink_data *)module->data;
567 
568 	if (!data->ext_module)
569 		return;
570 	data->ext_module->run(data->ext_module, sample_count);
571 }
572 
sink_init_module(struct dsp_module * module)573 static void sink_init_module(struct dsp_module *module)
574 {
575 	module->instantiate = &sink_instantiate;
576 	module->connect_port = &sink_connect_port;
577 	module->get_delay = &empty_get_delay;
578 	module->run = &sink_run;
579 	module->deinstantiate = &sink_deinstantiate;
580 	module->free_module = &empty_free_module;
581 	module->get_properties = &empty_get_properties;
582 	module->dump = &empty_dump;
583 }
584 
cras_dsp_module_set_sink_ext_module(struct dsp_module * module,struct ext_dsp_module * ext_module)585 void cras_dsp_module_set_sink_ext_module(struct dsp_module *module,
586 					 struct ext_dsp_module *ext_module)
587 {
588 	struct sink_data *data = (struct sink_data *)module->data;
589 	int i;
590 	data->ext_module = ext_module;
591 
592 	if (data->ext_module == NULL)
593 		return;
594 
595 	for (i = 0; i < MAX_EXT_DSP_PORTS; i++)
596 		ext_module->ports[i] = data->ports[i];
597 }
598 
599 /*
600  *  builtin module dispatcher
601  */
cras_dsp_module_load_builtin(struct plugin * plugin)602 struct dsp_module *cras_dsp_module_load_builtin(struct plugin *plugin)
603 {
604 	struct dsp_module *module;
605 	if (strcmp(plugin->library, "builtin") != 0)
606 		return NULL;
607 
608 	module = calloc(1, sizeof(struct dsp_module));
609 
610 	if (strcmp(plugin->label, "mix_stereo") == 0) {
611 		mix_stereo_init_module(module);
612 	} else if (strcmp(plugin->label, "invert_lr") == 0) {
613 		invert_lr_init_module(module);
614 	} else if (strcmp(plugin->label, "dcblock") == 0) {
615 		dcblock_init_module(module);
616 	} else if (strcmp(plugin->label, "eq") == 0) {
617 		eq_init_module(module);
618 	} else if (strcmp(plugin->label, "eq2") == 0) {
619 		eq2_init_module(module);
620 	} else if (strcmp(plugin->label, "drc") == 0) {
621 		drc_init_module(module);
622 	} else if (strcmp(plugin->label, "swap_lr") == 0) {
623 		swap_lr_init_module(module);
624 	} else if (strcmp(plugin->label, "sink") == 0) {
625 		sink_init_module(module);
626 	} else {
627 		empty_init_module(module);
628 	}
629 
630 	return module;
631 }
632