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