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