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