• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * \file pcm/pcm_rate.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Rate Plugin Interface
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \author Jaroslav Kysela <perex@perex.cz>
7  * \date 2000-2004
8  */
9 /*
10  *  PCM - Rate conversion
11  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
12  *                2004 by Jaroslav Kysela <perex@perex.cz>
13  *
14  *
15  *   This library is free software; you can redistribute it and/or modify
16  *   it under the terms of the GNU Lesser General Public License as
17  *   published by the Free Software Foundation; either version 2.1 of
18  *   the License, or (at your option) any later version.
19  *
20  *   This program is distributed in the hope that it will be useful,
21  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
22  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  *   GNU Lesser General Public License for more details.
24  *
25  *   You should have received a copy of the GNU Lesser General Public
26  *   License along with this library; if not, write to the Free Software
27  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
28  *
29  */
30 #include <inttypes.h>
31 #include "bswap.h"
32 #include "pcm_local.h"
33 #include "pcm_plugin.h"
34 #include "pcm_rate.h"
35 
36 #include "plugin_ops.h"
37 
38 #if 0
39 #define DEBUG_REFINE
40 #endif
41 
42 #ifndef PIC
43 /* entry for static linking */
44 const char *_snd_module_pcm_rate = "";
45 #endif
46 
47 #ifndef DOC_HIDDEN
48 
49 typedef struct _snd_pcm_rate snd_pcm_rate_t;
50 
51 struct _snd_pcm_rate {
52 	snd_pcm_generic_t gen;
53 	snd_pcm_uframes_t appl_ptr, hw_ptr, last_slave_hw_ptr;
54 	snd_pcm_uframes_t last_commit_ptr;
55 	snd_pcm_uframes_t orig_avail_min;
56 	snd_pcm_sw_params_t sw_params;
57 	snd_pcm_format_t sformat;
58 	unsigned int srate;
59 	snd_pcm_channel_area_t *pareas;	/* areas for splitted period (rate pcm) */
60 	snd_pcm_channel_area_t *sareas;	/* areas for splitted period (slave pcm) */
61 	snd_pcm_rate_info_t info;
62 	void *open_func;
63 	void *obj;
64 	snd_pcm_rate_ops_t ops;
65 	unsigned int src_conv_idx;
66 	unsigned int dst_conv_idx;
67 	snd_pcm_channel_area_t *src_buf;
68 	snd_pcm_channel_area_t *dst_buf;
69 	int start_pending; /* start is triggered but not commited to slave */
70 	snd_htimestamp_t trigger_tstamp;
71 	unsigned int plugin_version;
72 	unsigned int rate_min, rate_max;
73 	snd_pcm_format_t orig_in_format;
74 	snd_pcm_format_t orig_out_format;
75 	uint64_t in_formats;
76 	uint64_t out_formats;
77 	unsigned int format_flags;
78 };
79 
80 #define SND_PCM_RATE_PLUGIN_VERSION_OLD	0x010001	/* old rate plugin */
81 #endif /* DOC_HIDDEN */
82 
83 /* allocate a channel area and a temporary buffer for the given size */
84 static snd_pcm_channel_area_t *
rate_alloc_tmp_buf(snd_pcm_format_t format,unsigned int channels,unsigned int frames)85 rate_alloc_tmp_buf(snd_pcm_format_t format,
86 		   unsigned int channels, unsigned int frames)
87 {
88 	snd_pcm_channel_area_t *ap;
89 	int width = snd_pcm_format_physical_width(format);
90 	unsigned int i;
91 
92 	ap = malloc(sizeof(*ap) * channels);
93 	if (!ap)
94 		return NULL;
95 	ap->addr = malloc(frames * channels * width / 8);
96 	if (!ap->addr) {
97 		free(ap);
98 		return NULL;
99 	}
100 
101 	/* set up in interleaved format */
102 	for (i = 0; i < channels; i++) {
103 		ap[i].addr = ap[0].addr + (i * width) / 8;
104 		ap[i].first = 0;
105 		ap[i].step = width * channels;
106 	}
107 
108 	return ap;
109 }
110 
rate_free_tmp_buf(snd_pcm_channel_area_t ** ptr)111 static void rate_free_tmp_buf(snd_pcm_channel_area_t **ptr)
112 {
113 	snd_pcm_channel_area_t *c = *ptr;
114 
115 	if (c) {
116 		free(c->addr);
117 		free(c);
118 		*ptr = NULL;
119 	}
120 }
121 
snd_pcm_rate_hw_refine_cprepare(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_hw_params_t * params)122 static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
123 {
124 	snd_pcm_rate_t *rate = pcm->private_data;
125 	int err;
126 	snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
127 	snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
128 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
129 					 &access_mask);
130 	if (err < 0)
131 		return err;
132 	err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
133 					 &format_mask);
134 	if (err < 0)
135 		return err;
136 	err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
137 	if (err < 0)
138 		return err;
139 	if (rate->rate_min) {
140 		err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE,
141 						rate->rate_min, 0);
142 		if (err < 0)
143 			return err;
144 	}
145 	if (rate->rate_max) {
146 		err = _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_RATE,
147 						rate->rate_max, 0);
148 		if (err < 0)
149 			return err;
150 	}
151 	params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
152 	return 0;
153 }
154 
snd_pcm_rate_hw_refine_sprepare(snd_pcm_t * pcm,snd_pcm_hw_params_t * sparams)155 static int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
156 {
157 	snd_pcm_rate_t *rate = pcm->private_data;
158 	snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
159 	_snd_pcm_hw_params_any(sparams);
160 	_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
161 				   &saccess_mask);
162 	if (rate->sformat != SND_PCM_FORMAT_UNKNOWN) {
163 		_snd_pcm_hw_params_set_format(sparams, rate->sformat);
164 		_snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
165 	}
166 	_snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
167 				     rate->srate, 0, rate->srate + 1, -1);
168 	return 0;
169 }
170 
snd_pcm_rate_hw_refine_schange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)171 static int snd_pcm_rate_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
172 					  snd_pcm_hw_params_t *sparams)
173 {
174 	snd_pcm_rate_t *rate = pcm->private_data;
175 	snd_interval_t t, buffer_size;
176 	const snd_interval_t *srate, *crate;
177 	int err;
178 	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
179 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
180 			      SND_PCM_HW_PARBIT_TICK_TIME);
181 	if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
182 		links |= (SND_PCM_HW_PARBIT_FORMAT |
183 			  SND_PCM_HW_PARBIT_SUBFORMAT |
184 			  SND_PCM_HW_PARBIT_SAMPLE_BITS |
185 			  SND_PCM_HW_PARBIT_FRAME_BITS);
186 	snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
187 	snd_interval_unfloor(&buffer_size);
188 	crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
189 	srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
190 	snd_interval_muldiv(&buffer_size, srate, crate, &t);
191 	err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
192 	if (err < 0)
193 		return err;
194 	err = _snd_pcm_hw_params_refine(sparams, links, params);
195 	if (err < 0)
196 		return err;
197 	return 0;
198 }
199 
snd_pcm_rate_hw_refine_cchange(snd_pcm_t * pcm,snd_pcm_hw_params_t * params,snd_pcm_hw_params_t * sparams)200 static int snd_pcm_rate_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
201 					  snd_pcm_hw_params_t *sparams)
202 {
203 	snd_pcm_rate_t *rate = pcm->private_data;
204 	snd_interval_t t;
205 #ifdef DEBUG_REFINE
206 	snd_output_t *out;
207 #endif
208 	const snd_interval_t *sbuffer_size, *buffer_size;
209 	const snd_interval_t *srate, *crate;
210 	int err;
211 	unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
212 			      SND_PCM_HW_PARBIT_PERIOD_TIME |
213 			      SND_PCM_HW_PARBIT_TICK_TIME);
214 	if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
215 		links |= (SND_PCM_HW_PARBIT_FORMAT |
216 			  SND_PCM_HW_PARBIT_SUBFORMAT |
217 			  SND_PCM_HW_PARBIT_SAMPLE_BITS |
218 			  SND_PCM_HW_PARBIT_FRAME_BITS);
219 	sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
220 	crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
221 	srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
222 	snd_interval_muldiv(sbuffer_size, crate, srate, &t);
223 	snd_interval_floor(&t);
224 	err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
225 	if (err < 0)
226 		return err;
227 	buffer_size = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE);
228 	/*
229 	 * this condition probably needs more work:
230 	 *   in case when the buffer_size is known and we are looking
231 	 *   for best period_size, we should prefer situation when
232 	 *   (buffer_size / period_size) * period_size == buffer_size
233 	 */
234 	if (snd_interval_single(buffer_size) && buffer_size->integer) {
235 		snd_interval_t *period_size;
236 		period_size = (snd_interval_t *)snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE);
237 		if (!snd_interval_checkempty(period_size) &&
238 		    period_size->openmin && period_size->openmax &&
239 		    period_size->min + 1 == period_size->max) {
240 			if (period_size->min > 0 && (buffer_size->min / period_size->min) * period_size->min == buffer_size->min) {
241 		    		snd_interval_set_value(period_size, period_size->min);
242 		    	} else if ((buffer_size->max / period_size->max) * period_size->max == buffer_size->max) {
243 		    		snd_interval_set_value(period_size, period_size->max);
244 		    	}
245 		}
246 	}
247 #ifdef DEBUG_REFINE
248 	snd_output_stdio_attach(&out, stderr, 0);
249 	snd_output_printf(out, "REFINE (params):\n");
250 	snd_pcm_hw_params_dump(params, out);
251 	snd_output_printf(out, "REFINE (slave params):\n");
252 	snd_pcm_hw_params_dump(sparams, out);
253 	snd_output_close(out);
254 #endif
255 	err = _snd_pcm_hw_params_refine(params, links, sparams);
256 #ifdef DEBUG_REFINE
257 	snd_output_stdio_attach(&out, stderr, 0);
258 	snd_output_printf(out, "********************\n");
259 	snd_output_printf(out, "REFINE (params) (%i):\n", err);
260 	snd_pcm_hw_params_dump(params, out);
261 	snd_output_printf(out, "REFINE (slave params):\n");
262 	snd_pcm_hw_params_dump(sparams, out);
263 	snd_output_close(out);
264 #endif
265 	if (err < 0)
266 		return err;
267 	return 0;
268 }
269 
snd_pcm_rate_hw_refine(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)270 static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm,
271 				  snd_pcm_hw_params_t *params)
272 {
273 	return snd_pcm_hw_refine_slave(pcm, params,
274 				       snd_pcm_rate_hw_refine_cprepare,
275 				       snd_pcm_rate_hw_refine_cchange,
276 				       snd_pcm_rate_hw_refine_sprepare,
277 				       snd_pcm_rate_hw_refine_schange,
278 				       snd_pcm_generic_hw_refine);
279 }
280 
281 /* evaluate the best matching available format to the given format */
get_best_format(uint64_t mask,snd_pcm_format_t orig)282 static int get_best_format(uint64_t mask, snd_pcm_format_t orig)
283 {
284 	int pwidth = snd_pcm_format_physical_width(orig);
285 	int width = snd_pcm_format_width(orig);
286 	int signd = snd_pcm_format_signed(orig);
287 	int best_score = -1;
288 	int match = -1;
289 	int f, score;
290 
291 	for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) {
292 		if (!(mask & (1ULL << f)))
293 			continue;
294 		score = 0;
295 		if (snd_pcm_format_linear(f)) {
296 			if (snd_pcm_format_physical_width(f) == pwidth)
297 				score++;
298 			if (snd_pcm_format_physical_width(f) >= pwidth)
299 				score++;
300 			if (snd_pcm_format_width(f) == width)
301 				score++;
302 			if (snd_pcm_format_signed(f) == signd)
303 				score++;
304 		}
305 		if (score > best_score) {
306 			match = f;
307 			best_score = score;
308 		}
309 	}
310 
311 	return match;
312 }
313 
314 /* set up the input and output formats from the available lists */
choose_preferred_format(snd_pcm_rate_t * rate)315 static int choose_preferred_format(snd_pcm_rate_t *rate)
316 {
317 	uint64_t in_mask = rate->in_formats;
318 	uint64_t out_mask = rate->out_formats;
319 	int in, out;
320 
321 	if (!in_mask || !out_mask)
322 		return 0;
323 
324 	if (rate->orig_in_format == rate->orig_out_format)
325 		if (in_mask & out_mask & (1ULL << rate->orig_in_format))
326 			return 0; /* nothing changed */
327 
328  repeat:
329 	in = get_best_format(in_mask, rate->orig_in_format);
330 	out = get_best_format(out_mask, rate->orig_out_format);
331 	if (in < 0 || out < 0)
332 		return -ENOENT;
333 
334 	if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) &&
335 	    in != out) {
336 		if (out_mask & (1ULL << in))
337 			out = in;
338 		else if (in_mask & (1ULL << out))
339 			in = out;
340 		else {
341 			in_mask &= ~(1ULL << in);
342 			out_mask &= ~(1ULL << out);
343 			goto repeat;
344 		}
345 	}
346 
347 	rate->info.in.format = in;
348 	rate->info.out.format = out;
349 	return 0;
350 }
351 
snd_pcm_rate_hw_params(snd_pcm_t * pcm,snd_pcm_hw_params_t * params)352 static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
353 {
354 	snd_pcm_rate_t *rate = pcm->private_data;
355 	snd_pcm_t *slave = rate->gen.slave;
356 	snd_pcm_rate_side_info_t *sinfo, *cinfo;
357 	unsigned int channels, acc;
358 	int need_src_buf, need_dst_buf;
359 	int err = snd_pcm_hw_params_slave(pcm, params,
360 					  snd_pcm_rate_hw_refine_cchange,
361 					  snd_pcm_rate_hw_refine_sprepare,
362 					  snd_pcm_rate_hw_refine_schange,
363 					  snd_pcm_generic_hw_params);
364 	if (err < 0)
365 		return err;
366 
367 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
368 		cinfo = &rate->info.in;
369 		sinfo = &rate->info.out;
370 	} else {
371 		sinfo = &rate->info.in;
372 		cinfo = &rate->info.out;
373 	}
374 	err = INTERNAL(snd_pcm_hw_params_get_format)(params, &cinfo->format);
375 	if (err < 0)
376 		return err;
377 	err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &cinfo->rate, 0);
378 	if (err < 0)
379 		return err;
380 	err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &cinfo->period_size, 0);
381 	if (err < 0)
382 		return err;
383 	err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &cinfo->buffer_size);
384 	if (err < 0)
385 		return err;
386 	err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels);
387 	if (err < 0)
388 		return err;
389 	err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc);
390 	if (err < 0)
391 		return err;
392 
393 	rate->info.channels = channels;
394 	sinfo->format = slave->format;
395 	sinfo->rate = slave->rate;
396 	sinfo->buffer_size = slave->buffer_size;
397 	sinfo->period_size = slave->period_size;
398 
399 	if (CHECK_SANITY(rate->pareas)) {
400 		SNDMSG("rate plugin already in use");
401 		return -EBUSY;
402 	}
403 
404 	rate->pareas = rate_alloc_tmp_buf(cinfo->format, channels,
405 					  cinfo->period_size);
406 	rate->sareas = rate_alloc_tmp_buf(sinfo->format, channels,
407 					  sinfo->period_size);
408 	if (!rate->pareas || !rate->sareas) {
409 		err = -ENOMEM;
410 		goto error_pareas;
411 	}
412 
413 	rate->orig_in_format = rate->info.in.format;
414 	rate->orig_out_format = rate->info.out.format;
415 	if (choose_preferred_format(rate) < 0) {
416 		SNDERR("No matching format in rate plugin");
417 		err = -EINVAL;
418 		goto error_pareas;
419 	}
420 
421 	err = rate->ops.init(rate->obj, &rate->info);
422 	if (err < 0)
423 		goto error_init;
424 
425 	rate_free_tmp_buf(&rate->src_buf);
426 	rate_free_tmp_buf(&rate->dst_buf);
427 
428 	need_src_buf = need_dst_buf = 0;
429 
430 	if ((rate->format_flags & SND_PCM_RATE_FLAG_INTERLEAVED) &&
431 	    !(acc == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
432 	      acc == SND_PCM_ACCESS_RW_INTERLEAVED)) {
433 		need_src_buf = need_dst_buf = 1;
434 	} else {
435 		if (rate->orig_in_format != rate->info.in.format)
436 			need_src_buf = 1;
437 		if (rate->orig_out_format != rate->info.out.format)
438 			need_dst_buf = 1;
439 	}
440 
441 	if (need_src_buf) {
442 		rate->src_conv_idx =
443 			snd_pcm_linear_convert_index(rate->orig_in_format,
444 						     rate->info.in.format);
445 		rate->src_buf = rate_alloc_tmp_buf(rate->info.in.format,
446 						   channels, rate->info.in.period_size);
447 		if (!rate->src_buf) {
448 			err = -ENOMEM;
449 			goto error;
450 		}
451 	}
452 
453 	if (need_dst_buf) {
454 		rate->dst_conv_idx =
455 			snd_pcm_linear_convert_index(rate->info.out.format,
456 						     rate->orig_out_format);
457 		rate->dst_buf = rate_alloc_tmp_buf(rate->info.out.format,
458 						   channels, rate->info.out.period_size);
459 		if (!rate->dst_buf) {
460 			err = -ENOMEM;
461 			goto error;
462 		}
463 	}
464 
465 	return 0;
466 
467  error:
468 	rate_free_tmp_buf(&rate->src_buf);
469 	rate_free_tmp_buf(&rate->dst_buf);
470  error_init:
471 	if (rate->ops.free)
472 		rate->ops.free(rate->obj);
473  error_pareas:
474 	rate_free_tmp_buf(&rate->pareas);
475 	rate_free_tmp_buf(&rate->sareas);
476 	return err;
477 }
478 
snd_pcm_rate_hw_free(snd_pcm_t * pcm)479 static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
480 {
481 	snd_pcm_rate_t *rate = pcm->private_data;
482 
483 	rate_free_tmp_buf(&rate->pareas);
484 	rate_free_tmp_buf(&rate->sareas);
485 	if (rate->ops.free)
486 		rate->ops.free(rate->obj);
487 	rate_free_tmp_buf(&rate->src_buf);
488 	rate_free_tmp_buf(&rate->dst_buf);
489 	return snd_pcm_hw_free(rate->gen.slave);
490 }
491 
recalc(snd_pcm_t * pcm,snd_pcm_uframes_t * val)492 static void recalc(snd_pcm_t *pcm, snd_pcm_uframes_t *val)
493 {
494 	snd_pcm_rate_t *rate = pcm->private_data;
495 	snd_pcm_t *slave = rate->gen.slave;
496 	unsigned long div;
497 
498 	if (*val == pcm->buffer_size) {
499 		*val = slave->buffer_size;
500 	} else {
501 		div = *val / pcm->period_size;
502 		if (div * pcm->period_size == *val)
503 			*val = div * slave->period_size;
504 		else
505 			*val = muldiv_near(*val, slave->period_size, pcm->period_size);
506 	}
507 }
508 
snd_pcm_rate_sw_params(snd_pcm_t * pcm,snd_pcm_sw_params_t * params)509 static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
510 {
511 	snd_pcm_rate_t *rate = pcm->private_data;
512 	snd_pcm_t *slave = rate->gen.slave;
513 	snd_pcm_sw_params_t *sparams;
514 	snd_pcm_uframes_t boundary1, boundary2, sboundary;
515 	int err;
516 
517 	sparams = &rate->sw_params;
518 	err = snd_pcm_sw_params_current(slave, sparams);
519 	if (err < 0)
520 		return err;
521 	sboundary = sparams->boundary;
522 	*sparams = *params;
523 	boundary1 = pcm->buffer_size;
524 	boundary2 = slave->buffer_size;
525 	while (boundary1 * 2 <= LONG_MAX - pcm->buffer_size &&
526 	       boundary2 * 2 <= LONG_MAX - slave->buffer_size) {
527 		boundary1 *= 2;
528 		boundary2 *= 2;
529 	}
530 	params->boundary = boundary1;
531 	sparams->boundary = sboundary;
532 
533 	if (rate->ops.adjust_pitch)
534 		rate->ops.adjust_pitch(rate->obj, &rate->info);
535 
536 	recalc(pcm, &sparams->avail_min);
537 	rate->orig_avail_min = sparams->avail_min;
538 	recalc(pcm, &sparams->start_threshold);
539 	if (sparams->avail_min < 1) sparams->avail_min = 1;
540 	if (sparams->start_threshold <= slave->buffer_size) {
541 		if (sparams->start_threshold > (slave->buffer_size / sparams->avail_min) * sparams->avail_min)
542 			sparams->start_threshold = (slave->buffer_size / sparams->avail_min) * sparams->avail_min;
543 	}
544 	if (sparams->stop_threshold >= params->boundary) {
545 		sparams->stop_threshold = sparams->boundary;
546 	} else {
547 		recalc(pcm, &sparams->stop_threshold);
548 	}
549 	recalc(pcm, &sparams->silence_threshold);
550 	if (sparams->silence_size >= params->boundary) {
551 		sparams->silence_size = sparams->boundary;
552 	} else {
553 		recalc(pcm, &sparams->silence_size);
554 	}
555 	return snd_pcm_sw_params(slave, sparams);
556 }
557 
snd_pcm_rate_init(snd_pcm_t * pcm)558 static int snd_pcm_rate_init(snd_pcm_t *pcm)
559 {
560 	snd_pcm_rate_t *rate = pcm->private_data;
561 
562 	if (rate->ops.reset)
563 		rate->ops.reset(rate->obj);
564 	rate->last_commit_ptr = 0;
565 	rate->start_pending = 0;
566 	return 0;
567 }
568 
do_convert(const snd_pcm_channel_area_t * dst_areas,snd_pcm_uframes_t dst_offset,unsigned int dst_frames,const snd_pcm_channel_area_t * src_areas,snd_pcm_uframes_t src_offset,unsigned int src_frames,unsigned int channels,snd_pcm_rate_t * rate)569 static void do_convert(const snd_pcm_channel_area_t *dst_areas,
570 		       snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
571 		       const snd_pcm_channel_area_t *src_areas,
572 		       snd_pcm_uframes_t src_offset, unsigned int src_frames,
573 		       unsigned int channels,
574 		       snd_pcm_rate_t *rate)
575 {
576 	const snd_pcm_channel_area_t *out_areas;
577 	snd_pcm_uframes_t out_offset;
578 
579 	if (rate->dst_buf) {
580 		out_areas = rate->dst_buf;
581 		out_offset = 0;
582 	} else {
583 		out_areas = dst_areas;
584 		out_offset = dst_offset;
585 	}
586 
587 	if (rate->src_buf) {
588 		snd_pcm_linear_convert(rate->src_buf, 0,
589 				       src_areas, src_offset,
590 				       channels, src_frames,
591 				       rate->src_conv_idx);
592 		src_areas = rate->src_buf;
593 		src_offset = 0;
594 	}
595 
596 	if (rate->ops.convert)
597 		rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames,
598 				   src_areas, src_offset, src_frames);
599 	else
600 		rate->ops.convert_s16(rate->obj,
601 				      snd_pcm_channel_area_addr(out_areas, out_offset),
602 				      dst_frames,
603 				      snd_pcm_channel_area_addr(src_areas, src_offset),
604 				      src_frames);
605 	if (rate->dst_buf)
606 		snd_pcm_linear_convert(dst_areas, dst_offset,
607 				       rate->dst_buf, 0,
608 				       channels, dst_frames,
609 				       rate->dst_conv_idx);
610 }
611 
612 static inline void
snd_pcm_rate_write_areas1(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset)613 snd_pcm_rate_write_areas1(snd_pcm_t *pcm,
614 			 const snd_pcm_channel_area_t *areas,
615 			 snd_pcm_uframes_t offset,
616 			 const snd_pcm_channel_area_t *slave_areas,
617 			 snd_pcm_uframes_t slave_offset)
618 {
619 	snd_pcm_rate_t *rate = pcm->private_data;
620 	do_convert(slave_areas, slave_offset, rate->gen.slave->period_size,
621 		   areas, offset, pcm->period_size,
622 		   pcm->channels, rate);
623 }
624 
625 static inline void
snd_pcm_rate_read_areas1(snd_pcm_t * pcm,const snd_pcm_channel_area_t * areas,snd_pcm_uframes_t offset,const snd_pcm_channel_area_t * slave_areas,snd_pcm_uframes_t slave_offset)626 snd_pcm_rate_read_areas1(snd_pcm_t *pcm,
627 			 const snd_pcm_channel_area_t *areas,
628 			 snd_pcm_uframes_t offset,
629 			 const snd_pcm_channel_area_t *slave_areas,
630 			 snd_pcm_uframes_t slave_offset)
631 {
632 	snd_pcm_rate_t *rate = pcm->private_data;
633 	do_convert(areas, offset, pcm->period_size,
634 		   slave_areas, slave_offset, rate->gen.slave->period_size,
635 		   pcm->channels, rate);
636 }
637 
snd_pcm_rate_sync_hwptr0(snd_pcm_t * pcm,snd_pcm_uframes_t slave_hw_ptr)638 static inline void snd_pcm_rate_sync_hwptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)
639 {
640 	snd_pcm_rate_t *rate;
641 	snd_pcm_sframes_t slave_hw_ptr_diff;
642 	snd_pcm_sframes_t last_slave_hw_ptr_frac;
643 
644 	if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
645 		return;
646 
647 	rate = pcm->private_data;
648 	slave_hw_ptr_diff = pcm_frame_diff(slave_hw_ptr, rate->last_slave_hw_ptr, rate->gen.slave->boundary);
649 	if (slave_hw_ptr_diff == 0)
650 		return;
651 	last_slave_hw_ptr_frac = rate->last_slave_hw_ptr % rate->gen.slave->period_size;
652 	/* While handling fraction part fo slave period, rounded value will be
653 	 * introduced by input_frames().
654 	 * To eliminate rounding issue on rate->hw_ptr, subtract last rounded
655 	 * value from rate->hw_ptr and add new rounded value of present
656 	 * slave_hw_ptr fraction part to rate->hw_ptr. Hence,
657 	 * rate->hw_ptr += [ (no. of updated slave periods * pcm rate period size) -
658 	 * 	fractional part of last_slave_hw_ptr rounded value +
659 	 * 	fractional part of updated slave hw ptr's rounded value ]
660 	 */
661 	rate->hw_ptr += (
662 			(((last_slave_hw_ptr_frac + slave_hw_ptr_diff) / rate->gen.slave->period_size) * pcm->period_size) -
663 			rate->ops.input_frames(rate->obj, last_slave_hw_ptr_frac) +
664 			rate->ops.input_frames(rate->obj, (last_slave_hw_ptr_frac + slave_hw_ptr_diff) % rate->gen.slave->period_size));
665 	rate->last_slave_hw_ptr = slave_hw_ptr;
666 
667 	rate->hw_ptr %= pcm->boundary;
668 }
669 
snd_pcm_rate_sync_hwptr(snd_pcm_t * pcm)670 static inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm)
671 {
672 	snd_pcm_rate_t *rate = pcm->private_data;
673 	snd_pcm_rate_sync_hwptr0(pcm, *rate->gen.slave->hw.ptr);
674 }
675 
snd_pcm_rate_hwsync(snd_pcm_t * pcm)676 static int snd_pcm_rate_hwsync(snd_pcm_t *pcm)
677 {
678 	snd_pcm_rate_t *rate = pcm->private_data;
679 	int err = snd_pcm_hwsync(rate->gen.slave);
680 	if (err < 0)
681 		return err;
682 	snd_pcm_rate_sync_hwptr(pcm);
683 	return 0;
684 }
685 
snd_pcm_rate_playback_internal_delay(snd_pcm_t * pcm)686 static snd_pcm_uframes_t snd_pcm_rate_playback_internal_delay(snd_pcm_t *pcm)
687 {
688 	snd_pcm_rate_t *rate = pcm->private_data;
689 
690 	return pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
691 }
692 
snd_pcm_rate_delay(snd_pcm_t * pcm,snd_pcm_sframes_t * delayp)693 static int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
694 {
695 	snd_pcm_rate_t *rate = pcm->private_data;
696 	snd_pcm_sframes_t slave_delay;
697 	int err;
698 
699 	snd_pcm_rate_hwsync(pcm);
700 
701 	err = snd_pcm_delay(rate->gen.slave, &slave_delay);
702 	if (err < 0) {
703 		return err;
704 	}
705 
706 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
707 		*delayp = rate->ops.input_frames(rate->obj, slave_delay)
708 				+ snd_pcm_rate_playback_internal_delay(pcm);
709 	} else {
710 		*delayp = rate->ops.output_frames(rate->obj, slave_delay)
711 				+ snd_pcm_mmap_capture_delay(pcm);
712 	}
713 	return 0;
714 }
715 
snd_pcm_rate_prepare(snd_pcm_t * pcm)716 static int snd_pcm_rate_prepare(snd_pcm_t *pcm)
717 {
718 	snd_pcm_rate_t *rate = pcm->private_data;
719 	int err;
720 
721 	err = snd_pcm_prepare(rate->gen.slave);
722 	if (err < 0)
723 		return err;
724 	*pcm->hw.ptr = 0;
725 	*pcm->appl.ptr = 0;
726 	rate->last_slave_hw_ptr = 0;
727 	err = snd_pcm_rate_init(pcm);
728 	if (err < 0)
729 		return err;
730 	return 0;
731 }
732 
snd_pcm_rate_reset(snd_pcm_t * pcm)733 static int snd_pcm_rate_reset(snd_pcm_t *pcm)
734 {
735 	snd_pcm_rate_t *rate = pcm->private_data;
736 	int err;
737 	err = snd_pcm_reset(rate->gen.slave);
738 	if (err < 0)
739 		return err;
740 	*pcm->hw.ptr = 0;
741 	*pcm->appl.ptr = 0;
742 	rate->last_slave_hw_ptr = 0;
743 	err = snd_pcm_rate_init(pcm);
744 	if (err < 0)
745 		return err;
746 	return 0;
747 }
748 
snd_pcm_rate_rewindable(snd_pcm_t * pcm ATTRIBUTE_UNUSED)749 static snd_pcm_sframes_t snd_pcm_rate_rewindable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
750 {
751 	return 0;
752 }
753 
snd_pcm_rate_forwardable(snd_pcm_t * pcm ATTRIBUTE_UNUSED)754 static snd_pcm_sframes_t snd_pcm_rate_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
755 {
756 	return 0;
757 }
758 
snd_pcm_rate_rewind(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)759 static snd_pcm_sframes_t snd_pcm_rate_rewind(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
760                                              snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
761 {
762         return 0;
763 }
764 
snd_pcm_rate_forward(snd_pcm_t * pcm ATTRIBUTE_UNUSED,snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)765 static snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
766                                               snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
767 {
768         return 0;
769 }
770 
snd_pcm_rate_commit_area(snd_pcm_t * pcm,snd_pcm_rate_t * rate,snd_pcm_uframes_t appl_offset,snd_pcm_uframes_t size,snd_pcm_uframes_t slave_size)771 static int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate,
772 				    snd_pcm_uframes_t appl_offset,
773 				    snd_pcm_uframes_t size,
774 				    snd_pcm_uframes_t slave_size)
775 {
776 	snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
777 	const snd_pcm_channel_area_t *areas;
778 	const snd_pcm_channel_area_t *slave_areas;
779 	snd_pcm_uframes_t slave_offset, xfer;
780 	snd_pcm_uframes_t slave_frames = ULONG_MAX;
781 	snd_pcm_sframes_t result;
782 
783 	areas = snd_pcm_mmap_areas(pcm);
784 	if (cont >= size) {
785 		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
786 		if (result < 0)
787 			return result;
788 		if (slave_frames < slave_size) {
789 			snd_pcm_rate_write_areas1(pcm, areas, appl_offset, rate->sareas, 0);
790 			goto __partial;
791 		}
792 		snd_pcm_rate_write_areas1(pcm, areas, appl_offset,
793 					  slave_areas, slave_offset);
794 		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, slave_size);
795 		if (result < (snd_pcm_sframes_t)slave_size) {
796 			if (result < 0)
797 				return result;
798 			result = snd_pcm_rewind(rate->gen.slave, result);
799 			if (result < 0)
800 				return result;
801 			return 0;
802 		}
803 	} else {
804 		snd_pcm_areas_copy(rate->pareas, 0,
805 				   areas, appl_offset,
806 				   pcm->channels, cont,
807 				   pcm->format);
808 		snd_pcm_areas_copy(rate->pareas, cont,
809 				   areas, 0,
810 				   pcm->channels, size - cont,
811 				   pcm->format);
812 
813 		snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0);
814 
815 		/* ok, commit first fragment */
816 		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
817 		if (result < 0)
818 			return result;
819 	      __partial:
820 		cont = slave_frames;
821 		if (cont > slave_size)
822 			cont = slave_size;
823 		snd_pcm_areas_copy(slave_areas, slave_offset,
824 				   rate->sareas, 0,
825 				   pcm->channels, cont,
826 				   rate->gen.slave->format);
827 		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
828 		if (result < (snd_pcm_sframes_t)cont) {
829 			if (result < 0)
830 				return result;
831 			result = snd_pcm_rewind(rate->gen.slave, result);
832 			if (result < 0)
833 				return result;
834 			return 0;
835 		}
836 		xfer = cont;
837 
838 		if (xfer == slave_size)
839 			goto commit_done;
840 
841 		/* commit second fragment */
842 		cont = slave_size - cont;
843 		slave_frames = cont;
844 		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
845 		if (result < 0)
846 			return result;
847 #if 0
848 		if (slave_offset) {
849 			SNDERR("non-zero slave_offset %ld", slave_offset);
850 			return -EIO;
851 		}
852 #endif
853 		snd_pcm_areas_copy(slave_areas, slave_offset,
854 				   rate->sareas, xfer,
855 				   pcm->channels, cont,
856 				   rate->gen.slave->format);
857 		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
858 		if (result < (snd_pcm_sframes_t)cont) {
859 			if (result < 0)
860 				return result;
861 			result = snd_pcm_rewind(rate->gen.slave, result + xfer);
862 			if (result < 0)
863 				return result;
864 			return 0;
865 		}
866 	}
867 
868  commit_done:
869 	if (rate->start_pending) {
870 		/* we have pending start-trigger.  let's issue it now */
871 		snd_pcm_start(rate->gen.slave);
872 		rate->start_pending = 0;
873 	}
874 	return 1;
875 }
876 
snd_pcm_rate_commit_next_period(snd_pcm_t * pcm,snd_pcm_uframes_t appl_offset)877 static int snd_pcm_rate_commit_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t appl_offset)
878 {
879 	snd_pcm_rate_t *rate = pcm->private_data;
880 
881 	return snd_pcm_rate_commit_area(pcm, rate, appl_offset, pcm->period_size,
882 					rate->gen.slave->period_size);
883 }
884 
snd_pcm_rate_grab_next_period(snd_pcm_t * pcm,snd_pcm_uframes_t hw_offset)885 static int snd_pcm_rate_grab_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t hw_offset)
886 {
887 	snd_pcm_rate_t *rate = pcm->private_data;
888 	snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
889 	const snd_pcm_channel_area_t *areas;
890 	const snd_pcm_channel_area_t *slave_areas;
891 	snd_pcm_uframes_t slave_offset, xfer;
892 	snd_pcm_uframes_t slave_frames = ULONG_MAX;
893 	snd_pcm_sframes_t result;
894 
895 	areas = snd_pcm_mmap_areas(pcm);
896 	if (cont >= pcm->period_size) {
897 		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
898 		if (result < 0)
899 			return result;
900 		if (slave_frames < rate->gen.slave->period_size)
901 			goto __partial;
902 		snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
903 					 slave_areas, slave_offset);
904 		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, rate->gen.slave->period_size);
905 		if (result < (snd_pcm_sframes_t)rate->gen.slave->period_size) {
906 			if (result < 0)
907 				return result;
908 			result = snd_pcm_rewind(rate->gen.slave, result);
909 			if (result < 0)
910 				return result;
911 			return 0;
912 		}
913 	} else {
914 		/* ok, grab first fragment */
915 		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
916 		if (result < 0)
917 			return result;
918 	      __partial:
919 		cont = slave_frames;
920 		if (cont > rate->gen.slave->period_size)
921 			cont = rate->gen.slave->period_size;
922 		snd_pcm_areas_copy(rate->sareas, 0,
923 				   slave_areas, slave_offset,
924 				   pcm->channels, cont,
925 				   rate->gen.slave->format);
926 		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
927 		if (result < (snd_pcm_sframes_t)cont) {
928 			if (result < 0)
929 				return result;
930 			result = snd_pcm_rewind(rate->gen.slave, result);
931 			if (result < 0)
932 				return result;
933 			return 0;
934 		}
935 		xfer = cont;
936 
937 		if (xfer == rate->gen.slave->period_size)
938 			goto __transfer;
939 
940 		/* grab second fragment */
941 		cont = rate->gen.slave->period_size - cont;
942 		slave_frames = cont;
943 		result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
944 		if (result < 0)
945 			return result;
946 #if 0
947 		if (slave_offset) {
948 			SNDERR("non-zero slave_offset %ld", slave_offset);
949 			return -EIO;
950 		}
951 #endif
952 		snd_pcm_areas_copy(rate->sareas, xfer,
953 		                   slave_areas, slave_offset,
954 				   pcm->channels, cont,
955 				   rate->gen.slave->format);
956 		result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
957 		if (result < (snd_pcm_sframes_t)cont) {
958 			if (result < 0)
959 				return result;
960 			result = snd_pcm_rewind(rate->gen.slave, result + xfer);
961 			if (result < 0)
962 				return result;
963 			return 0;
964 		}
965 
966 	      __transfer:
967 		cont = pcm->buffer_size - hw_offset;
968 		if (cont >= pcm->period_size) {
969 			snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
970 						 rate->sareas, 0);
971 		} else {
972 			snd_pcm_rate_read_areas1(pcm,
973 						 rate->pareas, 0,
974 						 rate->sareas, 0);
975 			snd_pcm_areas_copy(areas, hw_offset,
976 					   rate->pareas, 0,
977 					   pcm->channels, cont,
978 					   pcm->format);
979 			snd_pcm_areas_copy(areas, 0,
980 					   rate->pareas, cont,
981 					   pcm->channels, pcm->period_size - cont,
982 					   pcm->format);
983 		}
984 	}
985 	return 1;
986 }
987 
snd_pcm_rate_sync_playback_area(snd_pcm_t * pcm,snd_pcm_uframes_t appl_ptr)988 static int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t appl_ptr)
989 {
990 	snd_pcm_rate_t *rate = pcm->private_data;
991 	snd_pcm_t *slave = rate->gen.slave;
992 	snd_pcm_uframes_t xfer;
993 	snd_pcm_sframes_t slave_size;
994 	int err;
995 
996 	slave_size = snd_pcm_avail_update(slave);
997 	if (slave_size < 0)
998 		return slave_size;
999 
1000 	xfer = pcm_frame_diff(appl_ptr, rate->last_commit_ptr, pcm->boundary);
1001 	while (xfer >= pcm->period_size &&
1002 	       (snd_pcm_uframes_t)slave_size >= rate->gen.slave->period_size) {
1003 		err = snd_pcm_rate_commit_next_period(pcm, rate->last_commit_ptr % pcm->buffer_size);
1004 		if (err == 0)
1005 			break;
1006 		if (err < 0)
1007 			return err;
1008 		xfer -= pcm->period_size;
1009 		slave_size -= rate->gen.slave->period_size;
1010 		rate->last_commit_ptr += pcm->period_size;
1011 		if (rate->last_commit_ptr >= pcm->boundary)
1012 			rate->last_commit_ptr = 0;
1013 	}
1014 	return 0;
1015 }
1016 
snd_pcm_rate_mmap_commit(snd_pcm_t * pcm,snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,snd_pcm_uframes_t size)1017 static snd_pcm_sframes_t snd_pcm_rate_mmap_commit(snd_pcm_t *pcm,
1018 						  snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
1019 						  snd_pcm_uframes_t size)
1020 {
1021 	snd_pcm_rate_t *rate = pcm->private_data;
1022 	int err;
1023 
1024 	if (size == 0)
1025 		return 0;
1026 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1027 		err = snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr + size);
1028 		if (err < 0)
1029 			return err;
1030 	}
1031 	snd_pcm_mmap_appl_forward(pcm, size);
1032 	return size;
1033 }
1034 
snd_pcm_rate_avail_update_capture(snd_pcm_t * pcm,snd_pcm_sframes_t slave_size)1035 static snd_pcm_sframes_t snd_pcm_rate_avail_update_capture(snd_pcm_t *pcm,
1036 							   snd_pcm_sframes_t slave_size)
1037 {
1038 	snd_pcm_rate_t *rate = pcm->private_data;
1039 	snd_pcm_t *slave = rate->gen.slave;
1040 	snd_pcm_uframes_t xfer, hw_offset, size;
1041 
1042 	xfer = snd_pcm_mmap_capture_avail(pcm);
1043 	size = pcm->buffer_size - xfer;
1044 	hw_offset = snd_pcm_mmap_hw_offset(pcm);
1045 	while (size >= pcm->period_size &&
1046 	       (snd_pcm_uframes_t)slave_size >= slave->period_size) {
1047 		int err = snd_pcm_rate_grab_next_period(pcm, hw_offset);
1048 		if (err < 0)
1049 			return err;
1050 		if (err == 0)
1051 			return (snd_pcm_sframes_t)xfer;
1052 		xfer += pcm->period_size;
1053 		size -= pcm->period_size;
1054 		slave_size -= slave->period_size;
1055 		hw_offset += pcm->period_size;
1056 		hw_offset %= pcm->buffer_size;
1057 		snd_pcm_mmap_hw_forward(pcm, pcm->period_size);
1058 	}
1059 	return (snd_pcm_sframes_t)xfer;
1060 }
1061 
snd_pcm_rate_avail_update(snd_pcm_t * pcm)1062 static snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm)
1063 {
1064 	snd_pcm_rate_t *rate = pcm->private_data;
1065 	snd_pcm_sframes_t slave_size;
1066 
1067 	slave_size = snd_pcm_avail_update(rate->gen.slave);
1068 	if (slave_size < 0)
1069 		return slave_size;
1070 
1071 	if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1072 		return snd_pcm_rate_avail_update_capture(pcm, slave_size);
1073 
1074 	snd_pcm_rate_sync_hwptr(pcm);
1075 	snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
1076 	return snd_pcm_mmap_avail(pcm);
1077 }
1078 
snd_pcm_rate_htimestamp(snd_pcm_t * pcm,snd_pcm_uframes_t * avail,snd_htimestamp_t * tstamp)1079 static int snd_pcm_rate_htimestamp(snd_pcm_t *pcm,
1080 				   snd_pcm_uframes_t *avail,
1081 				   snd_htimestamp_t *tstamp)
1082 {
1083 	snd_pcm_rate_t *rate = pcm->private_data;
1084 	snd_pcm_sframes_t avail1;
1085 	snd_pcm_uframes_t tmp;
1086 	int ok = 0, err;
1087 
1088 	while (1) {
1089 		/* the position is from this plugin itself */
1090 		avail1 = snd_pcm_avail_update(pcm);
1091 		if (avail1 < 0)
1092 			return avail1;
1093 		if (ok && (snd_pcm_uframes_t)avail1 == *avail)
1094 			break;
1095 		*avail = avail1;
1096 		/* timestamp is taken from the slave PCM */
1097 		err = snd_pcm_htimestamp(rate->gen.slave, &tmp, tstamp);
1098 		if (err < 0)
1099 			return err;
1100 		ok = 1;
1101 	}
1102 	return 0;
1103 }
1104 
snd_pcm_rate_poll_revents(snd_pcm_t * pcm,struct pollfd * pfds,unsigned int nfds,unsigned short * revents)1105 static int snd_pcm_rate_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
1106 {
1107 	snd_pcm_rate_t *rate = pcm->private_data;
1108 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1109 		/* Try to sync as much as possible */
1110 		snd_pcm_rate_hwsync(pcm);
1111 		snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
1112 	}
1113 	return snd_pcm_poll_descriptors_revents(rate->gen.slave, pfds, nfds, revents);
1114 }
1115 
1116 /* locking */
snd_pcm_rate_drain(snd_pcm_t * pcm)1117 static int snd_pcm_rate_drain(snd_pcm_t *pcm)
1118 {
1119 	snd_pcm_rate_t *rate = pcm->private_data;
1120 
1121 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1122 		/* commit the remaining fraction (if any) */
1123 		snd_pcm_uframes_t size, ofs, saved_avail_min;
1124 		snd_pcm_sw_params_t sw_params;
1125 		int commit_err = 0;
1126 
1127 		__snd_pcm_lock(pcm);
1128 		/* temporarily set avail_min to one */
1129 		sw_params = rate->sw_params;
1130 		saved_avail_min = sw_params.avail_min;
1131 		sw_params.avail_min = 1;
1132 		snd_pcm_sw_params(rate->gen.slave, &sw_params);
1133 
1134 		size = pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
1135 		ofs = rate->last_commit_ptr % pcm->buffer_size;
1136 		while (size > 0) {
1137 			snd_pcm_uframes_t psize, spsize;
1138 			int err;
1139 
1140 			err = __snd_pcm_wait_in_lock(rate->gen.slave, -1);
1141 			if (err < 0)
1142 				break;
1143 			if (size > pcm->period_size) {
1144 				psize = pcm->period_size;
1145 				spsize = rate->gen.slave->period_size;
1146 			} else {
1147 				psize = size;
1148 				spsize = rate->ops.output_frames(rate->obj, size);
1149 				if (! spsize)
1150 					break;
1151 			}
1152 			commit_err = snd_pcm_rate_commit_area(pcm, rate, ofs,
1153 						 psize, spsize);
1154 			if (commit_err == 1) {
1155 				rate->last_commit_ptr += psize;
1156 				if (rate->last_commit_ptr >= pcm->boundary)
1157 					rate->last_commit_ptr = 0;
1158 			} else if (commit_err == 0) {
1159 				if (pcm->mode & SND_PCM_NONBLOCK) {
1160 					commit_err = -EAGAIN;
1161 					break;
1162 				}
1163 				continue;
1164 			} else
1165 				break;
1166 
1167 			ofs = (ofs + psize) % pcm->buffer_size;
1168 			size -= psize;
1169 		}
1170 		sw_params.avail_min = saved_avail_min;
1171 		snd_pcm_sw_params(rate->gen.slave, &sw_params);
1172 		__snd_pcm_unlock(pcm);
1173 		if (commit_err < 0)
1174 			return commit_err;
1175 	}
1176 	return snd_pcm_drain(rate->gen.slave);
1177 }
1178 
snd_pcm_rate_state(snd_pcm_t * pcm)1179 static snd_pcm_state_t snd_pcm_rate_state(snd_pcm_t *pcm)
1180 {
1181 	snd_pcm_rate_t *rate = pcm->private_data;
1182 	if (rate->start_pending) /* pseudo-state */
1183 		return SND_PCM_STATE_RUNNING;
1184 	return snd_pcm_state(rate->gen.slave);
1185 }
1186 
1187 
snd_pcm_rate_start(snd_pcm_t * pcm)1188 static int snd_pcm_rate_start(snd_pcm_t *pcm)
1189 {
1190 	snd_pcm_rate_t *rate = pcm->private_data;
1191 	snd_pcm_sframes_t avail;
1192 
1193 	if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1194 		return snd_pcm_start(rate->gen.slave);
1195 
1196 	if (snd_pcm_state(rate->gen.slave) != SND_PCM_STATE_PREPARED)
1197 		return -EBADFD;
1198 
1199 	gettimestamp(&rate->trigger_tstamp, pcm->tstamp_type);
1200 
1201 	avail = snd_pcm_mmap_playback_hw_avail(rate->gen.slave);
1202 	if (avail < 0) /* can't happen on healthy drivers */
1203 		return -EBADFD;
1204 
1205 	if (avail == 0) {
1206 		/* postpone the trigger since we have no data committed yet */
1207 		rate->start_pending = 1;
1208 		return 0;
1209 	}
1210 	rate->start_pending = 0;
1211 	return snd_pcm_start(rate->gen.slave);
1212 }
1213 
snd_pcm_rate_status(snd_pcm_t * pcm,snd_pcm_status_t * status)1214 static int snd_pcm_rate_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
1215 {
1216 	snd_pcm_rate_t *rate = pcm->private_data;
1217 	snd_pcm_sframes_t err;
1218 
1219 	err = snd_pcm_status(rate->gen.slave, status);
1220 	if (err < 0)
1221 		return err;
1222 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1223 		if (rate->start_pending)
1224 			status->state = SND_PCM_STATE_RUNNING;
1225 		status->trigger_tstamp = rate->trigger_tstamp;
1226 	}
1227 	snd_pcm_rate_sync_hwptr0(pcm, status->hw_ptr);
1228 	status->appl_ptr = *pcm->appl.ptr;
1229 	status->hw_ptr = *pcm->hw.ptr;
1230 	if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1231 		status->delay = rate->ops.input_frames(rate->obj, status->delay)
1232 					+ snd_pcm_rate_playback_internal_delay(pcm);
1233 		status->avail = snd_pcm_mmap_playback_avail(pcm);
1234 		status->avail_max = rate->ops.input_frames(rate->obj, status->avail_max);
1235 	} else {
1236 		status->delay = rate->ops.output_frames(rate->obj, status->delay)
1237 					+ snd_pcm_mmap_capture_delay(pcm);
1238 		status->avail = snd_pcm_mmap_capture_avail(pcm);
1239 		status->avail_max = rate->ops.output_frames(rate->obj, status->avail_max);
1240 	}
1241 	return 0;
1242 }
1243 
snd_pcm_rate_dump(snd_pcm_t * pcm,snd_output_t * out)1244 static void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
1245 {
1246 	snd_pcm_rate_t *rate = pcm->private_data;
1247 	if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
1248 		snd_output_printf(out, "Rate conversion PCM (%d)\n",
1249 			rate->srate);
1250 	else
1251 		snd_output_printf(out, "Rate conversion PCM (%d, sformat=%s)\n",
1252 			rate->srate,
1253 			snd_pcm_format_name(rate->sformat));
1254 	if (rate->ops.dump)
1255 		rate->ops.dump(rate->obj, out);
1256 	snd_output_printf(out, "Protocol version: %x\n", rate->plugin_version);
1257 	if (pcm->setup) {
1258 		snd_output_printf(out, "Its setup is:\n");
1259 		snd_pcm_dump_setup(pcm, out);
1260 	}
1261 	snd_output_printf(out, "Slave: ");
1262 	snd_pcm_dump(rate->gen.slave, out);
1263 }
1264 
snd_pcm_rate_close(snd_pcm_t * pcm)1265 static int snd_pcm_rate_close(snd_pcm_t *pcm)
1266 {
1267 	snd_pcm_rate_t *rate = pcm->private_data;
1268 
1269 	if (rate->ops.close)
1270 		rate->ops.close(rate->obj);
1271 	if (rate->open_func)
1272 		snd_dlobj_cache_put(rate->open_func);
1273 	return snd_pcm_generic_close(pcm);
1274 }
1275 
1276 static const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = {
1277 	.status = snd_pcm_rate_status,
1278 	.state = snd_pcm_rate_state,
1279 	.hwsync = snd_pcm_rate_hwsync,
1280 	.delay = snd_pcm_rate_delay,
1281 	.prepare = snd_pcm_rate_prepare,
1282 	.reset = snd_pcm_rate_reset,
1283 	.start = snd_pcm_rate_start,
1284 	.drop = snd_pcm_generic_drop,
1285 	.drain = snd_pcm_rate_drain,
1286 	.pause = snd_pcm_generic_pause,
1287 	.rewindable = snd_pcm_rate_rewindable,
1288 	.rewind = snd_pcm_rate_rewind,
1289 	.forwardable = snd_pcm_rate_forwardable,
1290 	.forward = snd_pcm_rate_forward,
1291 	.resume = snd_pcm_generic_resume,
1292 	.writei = snd_pcm_mmap_writei,
1293 	.writen = snd_pcm_mmap_writen,
1294 	.readi = snd_pcm_mmap_readi,
1295 	.readn = snd_pcm_mmap_readn,
1296 	.avail_update = snd_pcm_rate_avail_update,
1297 	.mmap_commit = snd_pcm_rate_mmap_commit,
1298 	.htimestamp = snd_pcm_rate_htimestamp,
1299 	.poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
1300 	.poll_descriptors = snd_pcm_generic_poll_descriptors,
1301 	.poll_revents = snd_pcm_rate_poll_revents,
1302 	.may_wait_for_avail_min = snd_pcm_plugin_may_wait_for_avail_min,
1303 };
1304 
1305 static const snd_pcm_ops_t snd_pcm_rate_ops = {
1306 	.close = snd_pcm_rate_close,
1307 	.info = snd_pcm_generic_info,
1308 	.hw_refine = snd_pcm_rate_hw_refine,
1309 	.hw_params = snd_pcm_rate_hw_params,
1310 	.hw_free = snd_pcm_rate_hw_free,
1311 	.sw_params = snd_pcm_rate_sw_params,
1312 	.channel_info = snd_pcm_generic_channel_info,
1313 	.dump = snd_pcm_rate_dump,
1314 	.nonblock = snd_pcm_generic_nonblock,
1315 	.async = snd_pcm_generic_async,
1316 	.mmap = snd_pcm_generic_mmap,
1317 	.munmap = snd_pcm_generic_munmap,
1318 	.query_chmaps = snd_pcm_generic_query_chmaps,
1319 	.get_chmap = snd_pcm_generic_get_chmap,
1320 	.set_chmap = snd_pcm_generic_set_chmap,
1321 };
1322 
1323 /**
1324  * \brief Get a default converter string
1325  * \param root Root configuration node
1326  * \retval A const config item if found, or NULL
1327  */
snd_pcm_rate_get_default_converter(snd_config_t * root)1328 const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root)
1329 {
1330 	snd_config_t *n;
1331 	/* look for default definition */
1332 	if (snd_config_search(root, "defaults.pcm.rate_converter", &n) >= 0)
1333 		return n;
1334 	return NULL;
1335 }
1336 
rate_initial_setup(snd_pcm_rate_t * rate)1337 static void rate_initial_setup(snd_pcm_rate_t *rate)
1338 {
1339 	if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION)
1340 		rate->plugin_version = rate->ops.version;
1341 
1342 	if (rate->plugin_version >= 0x010002 &&
1343 	    rate->ops.get_supported_rates)
1344 		rate->ops.get_supported_rates(rate->obj,
1345 					      &rate->rate_min,
1346 					      &rate->rate_max);
1347 
1348 	if (rate->plugin_version >= 0x010003 &&
1349 	    rate->ops.get_supported_formats) {
1350 		rate->ops.get_supported_formats(rate->obj,
1351 						&rate->in_formats,
1352 						&rate->out_formats,
1353 						&rate->format_flags);
1354 	} else if (!rate->ops.convert && rate->ops.convert_s16) {
1355 		rate->in_formats = rate->out_formats =
1356 			1ULL << SND_PCM_FORMAT_S16;
1357 		rate->format_flags = SND_PCM_RATE_FLAG_INTERLEAVED;
1358 	}
1359 }
1360 
1361 #ifdef PIC
is_builtin_plugin(const char * type)1362 static int is_builtin_plugin(const char *type)
1363 {
1364 	return strcmp(type, "linear") == 0;
1365 }
1366 
1367 static const char *const default_rate_plugins[] = {
1368 	"speexrate", "linear", NULL
1369 };
1370 
rate_open_func(snd_pcm_rate_t * rate,const char * type,const snd_config_t * converter_conf,int verbose)1371 static int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_config_t *converter_conf, int verbose)
1372 {
1373 	char open_name[64], open_conf_name[64], lib_name[64], *lib = NULL;
1374 	snd_pcm_rate_open_func_t open_func;
1375 	snd_pcm_rate_open_conf_func_t open_conf_func;
1376 	int err;
1377 
1378 	snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
1379 	snprintf(open_conf_name, sizeof(open_conf_name), "_snd_pcm_rate_%s_open_conf", type);
1380 	if (!is_builtin_plugin(type)) {
1381 		snprintf(lib_name, sizeof(lib_name),
1382 				 "libasound_module_rate_%s.so", type);
1383 		lib = lib_name;
1384 	}
1385 
1386 	open_conf_func = snd_dlobj_cache_get(lib, open_conf_name, NULL, verbose && converter_conf != NULL);
1387 	if (open_conf_func) {
1388 		err = open_conf_func(SND_PCM_RATE_PLUGIN_VERSION,
1389 				     &rate->obj, &rate->ops, converter_conf);
1390 		if (!err) {
1391 			rate->open_func = open_conf_func;
1392 			return 0;
1393 		} else {
1394 			snd_dlobj_cache_put(open_conf_func);
1395 			return err;
1396 		}
1397 	}
1398 
1399 	open_func = snd_dlobj_cache_get(lib, open_name, NULL, verbose);
1400 	if (!open_func)
1401 		return -ENOENT;
1402 
1403 	rate->open_func = open_func;
1404 
1405 	err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1406 	if (!err)
1407 		return 0;
1408 
1409 	/* try to open with the old protocol version */
1410 	rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD;
1411 	err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD,
1412 			&rate->obj, &rate->ops);
1413 	if (!err)
1414 		return 0;
1415 
1416 	snd_dlobj_cache_put(open_func);
1417 	rate->open_func = NULL;
1418 	return err;
1419 }
1420 #endif
1421 
1422 /*
1423  * If the conf is an array of alternatives then the id of
1424  * the first element will be "0" (or maybe NULL). Otherwise assume it is
1425  * a structure.
1426  */
is_string_array(const snd_config_t * conf)1427 static int is_string_array(const snd_config_t *conf)
1428 {
1429 	snd_config_iterator_t i;
1430 
1431 	if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
1432 		return 0;
1433 
1434 	i = snd_config_iterator_first(conf);
1435 	if (i && i != snd_config_iterator_end(conf)) {
1436 		snd_config_t *n = snd_config_iterator_entry(i);
1437 		const char *id;
1438 		if (snd_config_get_id(n, &id) < 0)
1439 			return 0;
1440 		if (id && strcmp(id, "0") != 0)
1441 			return 0;
1442 	}
1443 
1444 	return 1;
1445 }
1446 
1447 /**
1448  * \brief Creates a new rate PCM
1449  * \param pcmp Returns created PCM handle
1450  * \param name Name of PCM
1451  * \param sformat Slave format
1452  * \param srate Slave rate
1453  * \param converter SRC type string node
1454  * \param slave Slave PCM handle
1455  * \param close_slave When set, the slave PCM handle is closed with copy PCM
1456  * \retval zero on success otherwise a negative error code
1457  * \warning Using of this function might be dangerous in the sense
1458  *          of compatibility reasons. The prototype might be freely
1459  *          changed in future.
1460  */
snd_pcm_rate_open(snd_pcm_t ** pcmp,const char * name,snd_pcm_format_t sformat,unsigned int srate,const snd_config_t * converter,snd_pcm_t * slave,int close_slave)1461 int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
1462 		      snd_pcm_format_t sformat, unsigned int srate,
1463 		      const snd_config_t *converter,
1464 		      snd_pcm_t *slave, int close_slave)
1465 {
1466 	snd_pcm_t *pcm;
1467 	snd_pcm_rate_t *rate;
1468 	const char *type = NULL;
1469 	int err;
1470 #ifndef PIC
1471 	snd_pcm_rate_open_func_t open_func;
1472 	extern int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops);
1473 #endif
1474 
1475 	assert(pcmp && slave);
1476 	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1477 	    snd_pcm_format_linear(sformat) != 1)
1478 		return -EINVAL;
1479 	rate = calloc(1, sizeof(snd_pcm_rate_t));
1480 	if (!rate) {
1481 		return -ENOMEM;
1482 	}
1483 	rate->gen.slave = slave;
1484 	rate->gen.close_slave = close_slave;
1485 	rate->srate = srate;
1486 	rate->sformat = sformat;
1487 
1488 	rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
1489 	rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
1490 	rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
1491 
1492 	err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
1493 	if (err < 0) {
1494 		free(rate);
1495 		return err;
1496 	}
1497 
1498 #ifdef PIC
1499 	err = -ENOENT;
1500 	if (!converter) {
1501 		const char *const *types;
1502 		for (types = default_rate_plugins; *types; types++) {
1503 			err = rate_open_func(rate, *types, NULL, 0);
1504 			if (!err) {
1505 				type = *types;
1506 				break;
1507 			}
1508 		}
1509 	} else if (!snd_config_get_string(converter, &type))
1510 		err = rate_open_func(rate, type, NULL, 1);
1511 	else if (is_string_array(converter)) {
1512 		snd_config_iterator_t i, next;
1513 		snd_config_for_each(i, next, converter) {
1514 			snd_config_t *n = snd_config_iterator_entry(i);
1515 			if (snd_config_get_string(n, &type) < 0)
1516 				break;
1517 			err = rate_open_func(rate, type, NULL, 0);
1518 			if (!err)
1519 				break;
1520 		}
1521 	} else if (snd_config_get_type(converter) == SND_CONFIG_TYPE_COMPOUND) {
1522 		snd_config_iterator_t i, next;
1523 		snd_config_for_each(i, next, converter) {
1524 			snd_config_t *n = snd_config_iterator_entry(i);
1525 			const char *id;
1526 			if (snd_config_get_id(n, &id) < 0)
1527 				continue;
1528 			if (strcmp(id, "name") != 0)
1529 				continue;
1530 			snd_config_get_string(n, &type);
1531 			break;
1532 		}
1533 		if (!type) {
1534 			SNDERR("No name given for rate converter");
1535 			snd_pcm_free(pcm);
1536 			free(rate);
1537 			return -EINVAL;
1538 		}
1539 		err = rate_open_func(rate, type, converter, 1);
1540 	} else {
1541 		SNDERR("Invalid type for rate converter");
1542 		snd_pcm_free(pcm);
1543 		free(rate);
1544 		return -EINVAL;
1545 	}
1546 	if (err < 0) {
1547 		SNDERR("Cannot find rate converter");
1548 		snd_pcm_free(pcm);
1549 		free(rate);
1550 		return -ENOENT;
1551 	}
1552 #else
1553 	type = "linear";
1554 	open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear);
1555 	err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1556 	if (err < 0) {
1557 		snd_pcm_free(pcm);
1558 		free(rate);
1559 		return err;
1560 	}
1561 #endif
1562 
1563 	if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16) ||
1564 	    ! rate->ops.input_frames || ! rate->ops.output_frames) {
1565 		SNDERR("Inproper rate plugin %s initialization", type);
1566 		snd_pcm_free(pcm);
1567 		free(rate);
1568 		return err;
1569 	}
1570 
1571 	rate_initial_setup(rate);
1572 
1573 	pcm->ops = &snd_pcm_rate_ops;
1574 	pcm->fast_ops = &snd_pcm_rate_fast_ops;
1575 	pcm->private_data = rate;
1576 	pcm->poll_fd = slave->poll_fd;
1577 	pcm->poll_events = slave->poll_events;
1578 	pcm->mmap_rw = 1;
1579 	pcm->tstamp_type = slave->tstamp_type;
1580 	snd_pcm_set_hw_ptr(pcm, &rate->hw_ptr, -1, 0);
1581 	snd_pcm_set_appl_ptr(pcm, &rate->appl_ptr, -1, 0);
1582 	*pcmp = pcm;
1583 
1584 	return 0;
1585 }
1586 
1587 /*! \page pcm_plugins
1588 
1589 \section pcm_plugins_rate Plugin: Rate
1590 
1591 This plugin converts a stream rate. The input and output formats must be linear.
1592 
1593 \code
1594 pcm.name {
1595 	type rate               # Rate PCM
1596         slave STR               # Slave name
1597         # or
1598         slave {                 # Slave definition
1599                 pcm STR         # Slave PCM name
1600                 # or
1601                 pcm { }         # Slave PCM definition
1602                 rate INT        # Slave rate
1603                 [format STR]    # Slave format
1604         }
1605 	converter STR			# optional
1606 	# or
1607 	converter [ STR1 STR2 ... ]	# optional
1608 				# Converter type, default is taken from
1609 				# defaults.pcm.rate_converter
1610 	# or
1611 	converter {		# optional
1612 		name STR	# Convertor type
1613 		xxx yyy		# optional convertor-specific configuration
1614 	}
1615 }
1616 \endcode
1617 
1618 \subsection pcm_plugins_rate_funcref Function reference
1619 
1620 <UL>
1621   <LI>snd_pcm_rate_open()
1622   <LI>_snd_pcm_rate_open()
1623 </UL>
1624 
1625 */
1626 
1627 /**
1628  * \brief Creates a new rate PCM
1629  * \param pcmp Returns created PCM handle
1630  * \param name Name of PCM
1631  * \param root Root configuration node
1632  * \param conf Configuration node with rate PCM description
1633  * \param stream Stream type
1634  * \param mode Stream mode
1635  * \retval zero on success otherwise a negative error code
1636  * \warning Using of this function might be dangerous in the sense
1637  *          of compatibility reasons. The prototype might be freely
1638  *          changed in future.
1639  */
_snd_pcm_rate_open(snd_pcm_t ** pcmp,const char * name,snd_config_t * root,snd_config_t * conf,snd_pcm_stream_t stream,int mode)1640 int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
1641 		       snd_config_t *root, snd_config_t *conf,
1642 		       snd_pcm_stream_t stream, int mode)
1643 {
1644 	snd_config_iterator_t i, next;
1645 	int err;
1646 	snd_pcm_t *spcm;
1647 	snd_config_t *slave = NULL, *sconf;
1648 	snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1649 	int srate = -1;
1650 	const snd_config_t *converter = NULL;
1651 
1652 	snd_config_for_each(i, next, conf) {
1653 		snd_config_t *n = snd_config_iterator_entry(i);
1654 		const char *id;
1655 		if (snd_config_get_id(n, &id) < 0)
1656 			continue;
1657 		if (snd_pcm_conf_generic_id(id))
1658 			continue;
1659 		if (strcmp(id, "slave") == 0) {
1660 			slave = n;
1661 			continue;
1662 		}
1663 		if (strcmp(id, "converter") == 0) {
1664 			converter = n;
1665 			continue;
1666 		}
1667 		SNDERR("Unknown field %s", id);
1668 		return -EINVAL;
1669 	}
1670 	if (!slave) {
1671 		SNDERR("slave is not defined");
1672 		return -EINVAL;
1673 	}
1674 
1675 	err = snd_pcm_slave_conf(root, slave, &sconf, 2,
1676 				 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
1677 				 SND_PCM_HW_PARAM_RATE, SCONF_MANDATORY, &srate);
1678 	if (err < 0)
1679 		return err;
1680 	if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1681 	    snd_pcm_format_linear(sformat) != 1) {
1682 	    	snd_config_delete(sconf);
1683 		SNDERR("slave format is not linear");
1684 		return -EINVAL;
1685 	}
1686 	err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1687 	snd_config_delete(sconf);
1688 	if (err < 0)
1689 		return err;
1690 	err = snd_pcm_rate_open(pcmp, name, sformat, (unsigned int) srate,
1691 				converter, spcm, 1);
1692 	if (err < 0)
1693 		snd_pcm_close(spcm);
1694 	return err;
1695 }
1696 #ifndef DOC_HIDDEN
1697 SND_DLSYM_BUILD_VERSION(_snd_pcm_rate_open, SND_PCM_DLSYM_VERSION);
1698 #endif
1699