4 * \brief PCM Rate Plugin Interface
5 * \author Abramo Bagnara <abramo@alsa-project.org>
6 * \author Jaroslav Kysela <perex@perex.cz>
10 * PCM - Rate conversion
11 * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
12 * 2004 by Jaroslav Kysela <perex@perex.cz>
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.
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.
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
30 #include "pcm_local.h"
31 #include "pcm_plugin.h"
33 #include "plugin_ops.h"
42 /* entry for static linking */
43 const char *_snd_module_pcm_rate = "";
48 typedef struct _snd_pcm_rate snd_pcm_rate_t;
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;
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;
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;
76 unsigned int format_flags;
79 #define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001 /* old rate plugin */
80 #endif /* DOC_HIDDEN */
82 /* allocate a channel area and a temporary buffer for the given size */
83 static snd_pcm_channel_area_t *
84 rate_alloc_tmp_buf(snd_pcm_format_t format,
85 unsigned int channels, unsigned int frames)
87 snd_pcm_channel_area_t *ap;
88 int width = snd_pcm_format_physical_width(format);
91 if (width < 0 || width > 128)
95 if (frames > 10*1024*1024)
97 ap = malloc(sizeof(*ap) * channels);
100 ap->addr = malloc(frames * channels * width / 8);
106 /* set up in interleaved format */
107 for (i = 0; i < channels; i++) {
108 ap[i].addr = ap[0].addr + (i * width) / 8;
110 ap[i].step = width * channels;
116 static void rate_free_tmp_buf(snd_pcm_channel_area_t **ptr)
118 snd_pcm_channel_area_t *c = *ptr;
127 static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
129 snd_pcm_rate_t *rate = pcm->private_data;
131 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
132 snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
133 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
137 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
141 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
144 if (rate->rate_min) {
145 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE,
150 if (rate->rate_max) {
151 err = _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_RATE,
156 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
160 static int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
162 snd_pcm_rate_t *rate = pcm->private_data;
163 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
164 _snd_pcm_hw_params_any(sparams);
165 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
167 if (rate->sformat != SND_PCM_FORMAT_UNKNOWN) {
168 _snd_pcm_hw_params_set_format(sparams, rate->sformat);
169 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
171 _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
172 rate->srate, 0, rate->srate + 1, -1);
176 static int snd_pcm_rate_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
177 snd_pcm_hw_params_t *sparams)
179 snd_pcm_rate_t *rate = pcm->private_data;
180 snd_interval_t t, buffer_size;
181 const snd_interval_t *srate, *crate;
183 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
184 SND_PCM_HW_PARBIT_PERIOD_TIME |
185 SND_PCM_HW_PARBIT_TICK_TIME);
186 if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
187 links |= (SND_PCM_HW_PARBIT_FORMAT |
188 SND_PCM_HW_PARBIT_SUBFORMAT |
189 SND_PCM_HW_PARBIT_SAMPLE_BITS |
190 SND_PCM_HW_PARBIT_FRAME_BITS);
191 snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
192 snd_interval_unfloor(&buffer_size);
193 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
194 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
195 snd_interval_muldiv(&buffer_size, srate, crate, &t);
196 err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
199 err = _snd_pcm_hw_params_refine(sparams, links, params);
205 static int snd_pcm_rate_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
206 snd_pcm_hw_params_t *sparams)
208 snd_pcm_rate_t *rate = pcm->private_data;
213 const snd_interval_t *sbuffer_size, *buffer_size;
214 const snd_interval_t *srate, *crate;
216 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
217 SND_PCM_HW_PARBIT_PERIOD_TIME |
218 SND_PCM_HW_PARBIT_TICK_TIME);
219 if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
220 links |= (SND_PCM_HW_PARBIT_FORMAT |
221 SND_PCM_HW_PARBIT_SUBFORMAT |
222 SND_PCM_HW_PARBIT_SAMPLE_BITS |
223 SND_PCM_HW_PARBIT_FRAME_BITS);
224 sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
225 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
226 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
227 snd_interval_muldiv(sbuffer_size, crate, srate, &t);
228 snd_interval_floor(&t);
229 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
232 buffer_size = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE);
234 * this condition probably needs more work:
235 * in case when the buffer_size is known and we are looking
236 * for best period_size, we should prefer situation when
237 * (buffer_size / period_size) * period_size == buffer_size
239 if (snd_interval_single(buffer_size) && buffer_size->integer) {
240 snd_interval_t *period_size;
241 period_size = (snd_interval_t *)snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE);
242 if (!snd_interval_checkempty(period_size) &&
243 period_size->openmin && period_size->openmax &&
244 period_size->min + 1 == period_size->max) {
245 if (period_size->min > 0 && (buffer_size->min / period_size->min) * period_size->min == buffer_size->min) {
246 snd_interval_set_value(period_size, period_size->min);
247 } else if ((buffer_size->max / period_size->max) * period_size->max == buffer_size->max) {
248 snd_interval_set_value(period_size, period_size->max);
253 snd_output_stdio_attach(&out, stderr, 0);
254 snd_output_printf(out, "REFINE (params):\n");
255 snd_pcm_hw_params_dump(params, out);
256 snd_output_printf(out, "REFINE (slave params):\n");
257 snd_pcm_hw_params_dump(sparams, out);
258 snd_output_close(out);
260 err = _snd_pcm_hw_params_refine(params, links, sparams);
262 snd_output_stdio_attach(&out, stderr, 0);
263 snd_output_printf(out, "********************\n");
264 snd_output_printf(out, "REFINE (params) (%i):\n", err);
265 snd_pcm_hw_params_dump(params, out);
266 snd_output_printf(out, "REFINE (slave params):\n");
267 snd_pcm_hw_params_dump(sparams, out);
268 snd_output_close(out);
275 static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm,
276 snd_pcm_hw_params_t *params)
278 return snd_pcm_hw_refine_slave(pcm, params,
279 snd_pcm_rate_hw_refine_cprepare,
280 snd_pcm_rate_hw_refine_cchange,
281 snd_pcm_rate_hw_refine_sprepare,
282 snd_pcm_rate_hw_refine_schange,
283 snd_pcm_generic_hw_refine);
286 /* evaluate the best matching available format to the given format */
287 static int get_best_format(uint64_t mask, snd_pcm_format_t orig)
289 int pwidth = snd_pcm_format_physical_width(orig);
290 int width = snd_pcm_format_width(orig);
291 int signd = snd_pcm_format_signed(orig);
296 for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) {
297 if (!(mask & (1ULL << f)))
300 if (snd_pcm_format_linear(f)) {
301 if (snd_pcm_format_physical_width(f) == pwidth)
303 if (snd_pcm_format_physical_width(f) >= pwidth)
305 if (snd_pcm_format_width(f) == width)
307 if (snd_pcm_format_signed(f) == signd)
310 if (score > best_score) {
319 /* set up the input and output formats from the available lists */
320 static int choose_preferred_format(snd_pcm_rate_t *rate)
322 uint64_t in_mask = rate->in_formats;
323 uint64_t out_mask = rate->out_formats;
326 if (!in_mask || !out_mask)
329 if (rate->orig_in_format < 0 || rate->orig_in_format > 63)
332 if (rate->orig_in_format == rate->orig_out_format)
333 if (in_mask & out_mask & (1ULL << rate->orig_in_format))
334 return 0; /* nothing changed */
337 in = get_best_format(in_mask, rate->orig_in_format);
338 out = get_best_format(out_mask, rate->orig_out_format);
339 if (in < 0 || out < 0)
342 if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) &&
344 if (out_mask & (1ULL << in))
346 else if (in_mask & (1ULL << out))
349 in_mask &= ~(1ULL << in);
350 out_mask &= ~(1ULL << out);
355 rate->info.in.format = in;
356 rate->info.out.format = out;
360 static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
362 snd_pcm_rate_t *rate = pcm->private_data;
363 snd_pcm_t *slave = rate->gen.slave;
364 snd_pcm_rate_side_info_t *sinfo, *cinfo;
365 unsigned int channels, acc;
366 int need_src_buf, need_dst_buf;
367 int err = snd_pcm_hw_params_slave(pcm, params,
368 snd_pcm_rate_hw_refine_cchange,
369 snd_pcm_rate_hw_refine_sprepare,
370 snd_pcm_rate_hw_refine_schange,
371 snd_pcm_generic_hw_params);
375 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
376 cinfo = &rate->info.in;
377 sinfo = &rate->info.out;
379 sinfo = &rate->info.in;
380 cinfo = &rate->info.out;
382 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &cinfo->format);
385 err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &cinfo->rate, 0);
388 err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &cinfo->period_size, 0);
391 err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &cinfo->buffer_size);
394 err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels);
397 err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc);
401 rate->info.channels = channels;
402 sinfo->format = slave->format;
403 sinfo->rate = slave->rate;
404 sinfo->buffer_size = slave->buffer_size;
405 sinfo->period_size = slave->period_size;
407 if (CHECK_SANITY(rate->pareas)) {
408 snd_check(PCM, "rate plugin already in use");
412 rate->pareas = rate_alloc_tmp_buf(cinfo->format, channels,
414 rate->sareas = rate_alloc_tmp_buf(sinfo->format, channels,
416 if (!rate->pareas || !rate->sareas) {
421 rate->orig_in_format = rate->info.in.format;
422 rate->orig_out_format = rate->info.out.format;
423 if (choose_preferred_format(rate) < 0) {
424 snd_error(PCM, "No matching format in rate plugin");
429 err = rate->ops.init(rate->obj, &rate->info);
433 rate_free_tmp_buf(&rate->src_buf);
434 rate_free_tmp_buf(&rate->dst_buf);
436 need_src_buf = need_dst_buf = 0;
438 if ((rate->format_flags & SND_PCM_RATE_FLAG_INTERLEAVED) &&
439 !(acc == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
440 acc == SND_PCM_ACCESS_RW_INTERLEAVED)) {
441 need_src_buf = need_dst_buf = 1;
443 if (rate->orig_in_format != rate->info.in.format)
445 if (rate->orig_out_format != rate->info.out.format)
451 snd_pcm_linear_convert_index(rate->orig_in_format,
452 rate->info.in.format);
453 rate->src_buf = rate_alloc_tmp_buf(rate->info.in.format,
454 channels, rate->info.in.period_size);
455 if (!rate->src_buf) {
463 snd_pcm_linear_convert_index(rate->info.out.format,
464 rate->orig_out_format);
465 rate->dst_buf = rate_alloc_tmp_buf(rate->info.out.format,
466 channels, rate->info.out.period_size);
467 if (!rate->dst_buf) {
476 rate_free_tmp_buf(&rate->src_buf);
477 rate_free_tmp_buf(&rate->dst_buf);
480 rate->ops.free(rate->obj);
482 rate_free_tmp_buf(&rate->pareas);
483 rate_free_tmp_buf(&rate->sareas);
487 static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
489 snd_pcm_rate_t *rate = pcm->private_data;
491 rate_free_tmp_buf(&rate->pareas);
492 rate_free_tmp_buf(&rate->sareas);
494 rate->ops.free(rate->obj);
495 rate_free_tmp_buf(&rate->src_buf);
496 rate_free_tmp_buf(&rate->dst_buf);
497 return snd_pcm_hw_free(rate->gen.slave);
500 static void recalc(snd_pcm_t *pcm, snd_pcm_uframes_t *val)
502 snd_pcm_rate_t *rate = pcm->private_data;
503 snd_pcm_t *slave = rate->gen.slave;
506 if (*val == pcm->buffer_size) {
507 *val = slave->buffer_size;
509 div = *val / pcm->period_size;
510 if (div * pcm->period_size == *val)
511 *val = div * slave->period_size;
513 *val = muldiv_near(*val, slave->period_size, pcm->period_size);
517 static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
519 snd_pcm_rate_t *rate = pcm->private_data;
520 snd_pcm_t *slave = rate->gen.slave;
521 snd_pcm_sw_params_t *sparams;
522 snd_pcm_uframes_t boundary1, boundary2, sboundary;
525 sparams = &rate->sw_params;
526 err = snd_pcm_sw_params_current(slave, sparams);
529 sboundary = sparams->boundary;
531 boundary1 = pcm->buffer_size;
532 boundary2 = slave->buffer_size;
533 while (boundary1 * 2 <= LONG_MAX - pcm->buffer_size &&
534 boundary2 * 2 <= LONG_MAX - slave->buffer_size) {
538 params->boundary = boundary1;
539 sparams->boundary = sboundary;
541 if (rate->ops.adjust_pitch)
542 rate->ops.adjust_pitch(rate->obj, &rate->info);
544 recalc(pcm, &sparams->avail_min);
545 rate->orig_avail_min = sparams->avail_min;
546 recalc(pcm, &sparams->start_threshold);
547 if (sparams->avail_min < 1) sparams->avail_min = 1;
548 if (sparams->start_threshold <= slave->buffer_size) {
549 if (sparams->start_threshold > (slave->buffer_size / sparams->avail_min) * sparams->avail_min)
550 sparams->start_threshold = (slave->buffer_size / sparams->avail_min) * sparams->avail_min;
552 if (sparams->stop_threshold >= params->boundary) {
553 sparams->stop_threshold = sparams->boundary;
555 recalc(pcm, &sparams->stop_threshold);
557 recalc(pcm, &sparams->silence_threshold);
558 if (sparams->silence_size >= params->boundary) {
559 sparams->silence_size = sparams->boundary;
561 recalc(pcm, &sparams->silence_size);
563 return snd_pcm_sw_params(slave, sparams);
566 static int snd_pcm_rate_init(snd_pcm_t *pcm)
568 snd_pcm_rate_t *rate = pcm->private_data;
571 rate->ops.reset(rate->obj);
572 rate->last_commit_ptr = 0;
573 rate->start_pending = 0;
577 static void do_convert(const snd_pcm_channel_area_t *dst_areas,
578 snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
579 const snd_pcm_channel_area_t *src_areas,
580 snd_pcm_uframes_t src_offset, unsigned int src_frames,
581 unsigned int channels,
582 snd_pcm_rate_t *rate)
584 const snd_pcm_channel_area_t *out_areas;
585 snd_pcm_uframes_t out_offset;
588 out_areas = rate->dst_buf;
591 out_areas = dst_areas;
592 out_offset = dst_offset;
596 snd_pcm_linear_convert(rate->src_buf, 0,
597 src_areas, src_offset,
598 channels, src_frames,
600 src_areas = rate->src_buf;
604 if (rate->ops.convert)
605 rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames,
606 src_areas, src_offset, src_frames);
608 rate->ops.convert_s16(rate->obj,
609 snd_pcm_channel_area_addr(out_areas, out_offset),
611 snd_pcm_channel_area_addr(src_areas, src_offset),
614 snd_pcm_linear_convert(dst_areas, dst_offset,
616 channels, dst_frames,
621 snd_pcm_rate_write_areas1(snd_pcm_t *pcm,
622 const snd_pcm_channel_area_t *areas,
623 snd_pcm_uframes_t offset,
624 const snd_pcm_channel_area_t *slave_areas,
625 snd_pcm_uframes_t slave_offset)
627 snd_pcm_rate_t *rate = pcm->private_data;
628 do_convert(slave_areas, slave_offset, rate->gen.slave->period_size,
629 areas, offset, pcm->period_size,
630 pcm->channels, rate);
634 snd_pcm_rate_read_areas1(snd_pcm_t *pcm,
635 const snd_pcm_channel_area_t *areas,
636 snd_pcm_uframes_t offset,
637 const snd_pcm_channel_area_t *slave_areas,
638 snd_pcm_uframes_t slave_offset)
640 snd_pcm_rate_t *rate = pcm->private_data;
641 do_convert(areas, offset, pcm->period_size,
642 slave_areas, slave_offset, rate->gen.slave->period_size,
643 pcm->channels, rate);
646 static inline void snd_pcm_rate_sync_hwptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)
648 snd_pcm_rate_t *rate;
649 snd_pcm_sframes_t slave_hw_ptr_diff;
650 snd_pcm_sframes_t last_slave_hw_ptr_frac;
652 if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
655 rate = pcm->private_data;
656 slave_hw_ptr_diff = pcm_frame_diff(slave_hw_ptr, rate->last_slave_hw_ptr, rate->gen.slave->boundary);
657 if (slave_hw_ptr_diff == 0)
659 last_slave_hw_ptr_frac = rate->last_slave_hw_ptr % rate->gen.slave->period_size;
660 /* While handling fraction part fo slave period, rounded value will be
661 * introduced by input_frames().
662 * To eliminate rounding issue on rate->hw_ptr, subtract last rounded
663 * value from rate->hw_ptr and add new rounded value of present
664 * slave_hw_ptr fraction part to rate->hw_ptr. Hence,
665 * rate->hw_ptr += [ (no. of updated slave periods * pcm rate period size) -
666 * fractional part of last_slave_hw_ptr rounded value +
667 * fractional part of updated slave hw ptr's rounded value ]
670 (((last_slave_hw_ptr_frac + slave_hw_ptr_diff) / rate->gen.slave->period_size) * pcm->period_size) -
671 rate->ops.input_frames(rate->obj, last_slave_hw_ptr_frac) +
672 rate->ops.input_frames(rate->obj, (last_slave_hw_ptr_frac + slave_hw_ptr_diff) % rate->gen.slave->period_size));
673 rate->last_slave_hw_ptr = slave_hw_ptr;
675 rate->hw_ptr %= pcm->boundary;
678 static inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm)
680 snd_pcm_rate_t *rate = pcm->private_data;
681 snd_pcm_rate_sync_hwptr0(pcm, *rate->gen.slave->hw.ptr);
684 static int snd_pcm_rate_hwsync(snd_pcm_t *pcm)
686 snd_pcm_rate_t *rate = pcm->private_data;
687 int err = snd_pcm_hwsync(rate->gen.slave);
690 snd_pcm_rate_sync_hwptr(pcm);
694 static snd_pcm_uframes_t snd_pcm_rate_playback_internal_delay(snd_pcm_t *pcm)
696 snd_pcm_rate_t *rate = pcm->private_data;
698 return pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
701 static int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
703 snd_pcm_rate_t *rate = pcm->private_data;
704 snd_pcm_sframes_t slave_delay;
707 snd_pcm_rate_hwsync(pcm);
709 err = snd_pcm_delay(rate->gen.slave, &slave_delay);
714 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
715 *delayp = rate->ops.input_frames(rate->obj, slave_delay)
716 + snd_pcm_rate_playback_internal_delay(pcm);
718 *delayp = rate->ops.output_frames(rate->obj, slave_delay)
719 + snd_pcm_mmap_capture_delay(pcm);
724 static int snd_pcm_rate_prepare(snd_pcm_t *pcm)
726 snd_pcm_rate_t *rate = pcm->private_data;
729 err = snd_pcm_prepare(rate->gen.slave);
734 rate->last_slave_hw_ptr = 0;
735 err = snd_pcm_rate_init(pcm);
741 static int snd_pcm_rate_reset(snd_pcm_t *pcm)
743 snd_pcm_rate_t *rate = pcm->private_data;
745 err = snd_pcm_reset(rate->gen.slave);
750 rate->last_slave_hw_ptr = 0;
751 err = snd_pcm_rate_init(pcm);
757 static snd_pcm_sframes_t snd_pcm_rate_rewindable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
762 static snd_pcm_sframes_t snd_pcm_rate_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
767 static snd_pcm_sframes_t snd_pcm_rate_rewind(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
768 snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
773 static snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
774 snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
779 static int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate,
780 snd_pcm_uframes_t appl_offset,
781 snd_pcm_uframes_t size ATTRIBUTE_UNUSED,
782 snd_pcm_uframes_t slave_size)
784 snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
785 const snd_pcm_channel_area_t *areas;
786 const snd_pcm_channel_area_t *slave_areas;
787 snd_pcm_uframes_t slave_offset, xfer;
788 snd_pcm_uframes_t slave_frames = ULONG_MAX;
789 snd_pcm_sframes_t result;
791 areas = snd_pcm_mmap_areas(pcm);
793 * Because snd_pcm_rate_write_areas1() below will convert a full source period
794 * then there had better be a full period available in the current buffer.
796 if (cont >= pcm->period_size) {
797 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
801 * Because snd_pcm_rate_write_areas1() below will convert to a full slave period
802 * then there had better be a full slave period available in the slave buffer.
804 if (slave_frames < rate->gen.slave->period_size) {
805 snd_pcm_rate_write_areas1(pcm, areas, appl_offset, rate->sareas, 0);
808 snd_pcm_rate_write_areas1(pcm, areas, appl_offset,
809 slave_areas, slave_offset);
810 /* Only commit the requested slave_size, even if more was actually converted */
811 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, slave_size);
812 if (result < (snd_pcm_sframes_t)slave_size) {
815 result = snd_pcm_rewind(rate->gen.slave, result);
821 snd_pcm_areas_copy(rate->pareas, 0,
825 snd_pcm_areas_copy(rate->pareas, cont,
827 pcm->channels, pcm->period_size - cont,
830 snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0);
832 /* ok, commit first fragment */
833 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
838 if (cont > slave_size)
840 snd_pcm_areas_copy(slave_areas, slave_offset,
843 rate->gen.slave->format);
844 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
845 if (result < (snd_pcm_sframes_t)cont) {
848 result = snd_pcm_rewind(rate->gen.slave, result);
855 if (xfer == slave_size)
858 /* commit second fragment */
859 cont = slave_size - cont;
861 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
866 snd_error(PCM, "non-zero slave_offset %ld", slave_offset);
870 snd_pcm_areas_copy(slave_areas, slave_offset,
873 rate->gen.slave->format);
874 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
875 if (result < (snd_pcm_sframes_t)cont) {
878 result = snd_pcm_rewind(rate->gen.slave, result + xfer);
886 if (rate->start_pending) {
887 /* we have pending start-trigger. let's issue it now */
888 snd_pcm_start(rate->gen.slave);
889 rate->start_pending = 0;
894 static int snd_pcm_rate_commit_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t appl_offset)
896 snd_pcm_rate_t *rate = pcm->private_data;
898 return snd_pcm_rate_commit_area(pcm, rate, appl_offset, pcm->period_size,
899 rate->gen.slave->period_size);
902 static int snd_pcm_rate_grab_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t hw_offset)
904 snd_pcm_rate_t *rate = pcm->private_data;
905 snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
906 const snd_pcm_channel_area_t *areas;
907 const snd_pcm_channel_area_t *slave_areas;
908 snd_pcm_uframes_t slave_offset, xfer;
909 snd_pcm_uframes_t slave_frames = ULONG_MAX;
910 snd_pcm_sframes_t result;
912 areas = snd_pcm_mmap_areas(pcm);
913 if (cont >= pcm->period_size) {
914 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
917 if (slave_frames < rate->gen.slave->period_size)
919 snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
920 slave_areas, slave_offset);
921 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, rate->gen.slave->period_size);
922 if (result < (snd_pcm_sframes_t)rate->gen.slave->period_size) {
925 result = snd_pcm_rewind(rate->gen.slave, result);
931 /* ok, grab first fragment */
932 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
937 if (cont > rate->gen.slave->period_size)
938 cont = rate->gen.slave->period_size;
939 snd_pcm_areas_copy(rate->sareas, 0,
940 slave_areas, slave_offset,
942 rate->gen.slave->format);
943 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
944 if (result < (snd_pcm_sframes_t)cont) {
947 result = snd_pcm_rewind(rate->gen.slave, result);
954 if (xfer == rate->gen.slave->period_size)
957 /* grab second fragment */
958 cont = rate->gen.slave->period_size - cont;
960 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
965 snd_error(PCM, "non-zero slave_offset %ld", slave_offset);
969 snd_pcm_areas_copy(rate->sareas, xfer,
970 slave_areas, slave_offset,
972 rate->gen.slave->format);
973 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
974 if (result < (snd_pcm_sframes_t)cont) {
977 result = snd_pcm_rewind(rate->gen.slave, result + xfer);
984 cont = pcm->buffer_size - hw_offset;
985 if (cont >= pcm->period_size) {
986 snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
989 snd_pcm_rate_read_areas1(pcm,
992 snd_pcm_areas_copy(areas, hw_offset,
996 snd_pcm_areas_copy(areas, 0,
998 pcm->channels, pcm->period_size - cont,
1005 static int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t appl_ptr)
1007 snd_pcm_rate_t *rate = pcm->private_data;
1008 snd_pcm_t *slave = rate->gen.slave;
1009 snd_pcm_uframes_t xfer;
1010 snd_pcm_sframes_t slave_size;
1013 slave_size = snd_pcm_avail_update(slave);
1017 xfer = pcm_frame_diff(appl_ptr, rate->last_commit_ptr, pcm->boundary);
1018 while (xfer >= pcm->period_size &&
1019 (snd_pcm_uframes_t)slave_size >= rate->gen.slave->period_size) {
1020 err = snd_pcm_rate_commit_next_period(pcm, rate->last_commit_ptr % pcm->buffer_size);
1025 xfer -= pcm->period_size;
1026 slave_size -= rate->gen.slave->period_size;
1027 rate->last_commit_ptr += pcm->period_size;
1028 if (rate->last_commit_ptr >= pcm->boundary)
1029 rate->last_commit_ptr -= pcm->boundary;
1034 static snd_pcm_sframes_t snd_pcm_rate_mmap_commit(snd_pcm_t *pcm,
1035 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
1036 snd_pcm_uframes_t size)
1038 snd_pcm_rate_t *rate = pcm->private_data;
1043 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1044 err = snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr + size);
1048 snd_pcm_mmap_appl_forward(pcm, size);
1052 static snd_pcm_sframes_t snd_pcm_rate_avail_update_capture(snd_pcm_t *pcm,
1053 snd_pcm_sframes_t slave_size)
1055 snd_pcm_rate_t *rate = pcm->private_data;
1056 snd_pcm_t *slave = rate->gen.slave;
1057 snd_pcm_uframes_t xfer, hw_offset, size;
1059 xfer = snd_pcm_mmap_capture_avail(pcm);
1060 size = pcm->buffer_size - xfer;
1061 hw_offset = snd_pcm_mmap_hw_offset(pcm);
1062 while (size >= pcm->period_size &&
1063 (snd_pcm_uframes_t)slave_size >= slave->period_size) {
1064 int err = snd_pcm_rate_grab_next_period(pcm, hw_offset);
1068 return (snd_pcm_sframes_t)xfer;
1069 xfer += pcm->period_size;
1070 size -= pcm->period_size;
1071 slave_size -= slave->period_size;
1072 hw_offset += pcm->period_size;
1073 hw_offset %= pcm->buffer_size;
1074 snd_pcm_mmap_hw_forward(pcm, pcm->period_size);
1076 return (snd_pcm_sframes_t)xfer;
1079 static snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm)
1081 snd_pcm_rate_t *rate = pcm->private_data;
1082 snd_pcm_sframes_t slave_size;
1084 slave_size = snd_pcm_avail_update(rate->gen.slave);
1088 if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1089 return snd_pcm_rate_avail_update_capture(pcm, slave_size);
1091 snd_pcm_rate_sync_hwptr(pcm);
1092 snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
1093 return snd_pcm_mmap_avail(pcm);
1096 static int snd_pcm_rate_htimestamp(snd_pcm_t *pcm,
1097 snd_pcm_uframes_t *avail,
1098 snd_htimestamp_t *tstamp)
1100 snd_pcm_rate_t *rate = pcm->private_data;
1101 snd_pcm_sframes_t avail1;
1102 snd_pcm_uframes_t tmp;
1106 /* the position is from this plugin itself */
1107 avail1 = snd_pcm_avail_update(pcm);
1110 if (ok && (snd_pcm_uframes_t)avail1 == *avail)
1113 /* timestamp is taken from the slave PCM */
1114 err = snd_pcm_htimestamp(rate->gen.slave, &tmp, tstamp);
1122 static int snd_pcm_rate_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
1124 snd_pcm_rate_t *rate = pcm->private_data;
1125 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1126 /* Try to sync as much as possible */
1127 snd_pcm_rate_hwsync(pcm);
1128 snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
1130 return snd_pcm_poll_descriptors_revents(rate->gen.slave, pfds, nfds, revents);
1134 static int snd_pcm_rate_drain(snd_pcm_t *pcm)
1136 snd_pcm_rate_t *rate = pcm->private_data;
1138 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1139 /* commit the remaining fraction (if any) */
1140 snd_pcm_uframes_t size, ofs, saved_avail_min;
1141 snd_pcm_sw_params_t sw_params;
1144 __snd_pcm_lock(pcm);
1145 /* temporarily set avail_min to one */
1146 sw_params = rate->sw_params;
1147 saved_avail_min = sw_params.avail_min;
1148 sw_params.avail_min = 1;
1149 snd_pcm_sw_params(rate->gen.slave, &sw_params);
1151 size = pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
1152 ofs = rate->last_commit_ptr % pcm->buffer_size;
1154 snd_pcm_uframes_t psize, spsize;
1157 err = __snd_pcm_wait_in_lock(rate->gen.slave, SND_PCM_WAIT_DRAIN);
1160 if (size > pcm->period_size) {
1161 psize = pcm->period_size;
1162 spsize = rate->gen.slave->period_size;
1165 spsize = rate->ops.output_frames(rate->obj, size);
1169 commit_err = snd_pcm_rate_commit_area(pcm, rate, ofs,
1171 if (commit_err == 1) {
1172 rate->last_commit_ptr += psize;
1173 if (rate->last_commit_ptr >= pcm->boundary)
1174 rate->last_commit_ptr -= pcm->boundary;
1175 } else if (commit_err == 0) {
1176 if (pcm->mode & SND_PCM_NONBLOCK) {
1177 commit_err = -EAGAIN;
1184 ofs = (ofs + psize) % pcm->buffer_size;
1187 sw_params.avail_min = saved_avail_min;
1188 snd_pcm_sw_params(rate->gen.slave, &sw_params);
1189 __snd_pcm_unlock(pcm);
1193 return snd_pcm_drain(rate->gen.slave);
1196 static snd_pcm_state_t snd_pcm_rate_state(snd_pcm_t *pcm)
1198 snd_pcm_rate_t *rate = pcm->private_data;
1199 if (rate->start_pending) /* pseudo-state */
1200 return SND_PCM_STATE_RUNNING;
1201 return snd_pcm_state(rate->gen.slave);
1205 static int snd_pcm_rate_start(snd_pcm_t *pcm)
1207 snd_pcm_rate_t *rate = pcm->private_data;
1208 snd_pcm_sframes_t avail;
1210 if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1211 return snd_pcm_start(rate->gen.slave);
1213 if (snd_pcm_state(rate->gen.slave) != SND_PCM_STATE_PREPARED)
1216 gettimestamp(&rate->trigger_tstamp, pcm->tstamp_type);
1218 avail = snd_pcm_mmap_playback_hw_avail(rate->gen.slave);
1219 if (avail < 0) /* can't happen on healthy drivers */
1223 /* postpone the trigger since we have no data committed yet */
1224 rate->start_pending = 1;
1227 rate->start_pending = 0;
1228 return snd_pcm_start(rate->gen.slave);
1231 static int snd_pcm_rate_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
1233 snd_pcm_rate_t *rate = pcm->private_data;
1234 snd_pcm_sframes_t err;
1236 err = snd_pcm_status(rate->gen.slave, status);
1239 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1240 if (rate->start_pending)
1241 status->state = SND_PCM_STATE_RUNNING;
1242 status->trigger_tstamp = rate->trigger_tstamp;
1244 snd_pcm_rate_sync_hwptr0(pcm, status->hw_ptr);
1245 status->appl_ptr = *pcm->appl.ptr;
1246 status->hw_ptr = *pcm->hw.ptr;
1247 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1248 status->delay = rate->ops.input_frames(rate->obj, status->delay)
1249 + snd_pcm_rate_playback_internal_delay(pcm);
1250 status->avail = snd_pcm_mmap_playback_avail(pcm);
1251 status->avail_max = rate->ops.input_frames(rate->obj, status->avail_max);
1253 status->delay = rate->ops.output_frames(rate->obj, status->delay)
1254 + snd_pcm_mmap_capture_delay(pcm);
1255 status->avail = snd_pcm_mmap_capture_avail(pcm);
1256 status->avail_max = rate->ops.output_frames(rate->obj, status->avail_max);
1261 static void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
1263 snd_pcm_rate_t *rate = pcm->private_data;
1264 if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
1265 snd_output_printf(out, "Rate conversion PCM (%d)\n",
1268 snd_output_printf(out, "Rate conversion PCM (%d, sformat=%s)\n",
1270 snd_pcm_format_name(rate->sformat));
1272 rate->ops.dump(rate->obj, out);
1273 snd_output_printf(out, "Protocol version: %x\n", rate->plugin_version);
1275 snd_output_printf(out, "Its setup is:\n");
1276 snd_pcm_dump_setup(pcm, out);
1278 snd_output_printf(out, "Slave: ");
1279 snd_pcm_dump(rate->gen.slave, out);
1282 static int snd_pcm_rate_close(snd_pcm_t *pcm)
1284 snd_pcm_rate_t *rate = pcm->private_data;
1286 if (rate->ops.close)
1287 rate->ops.close(rate->obj);
1288 if (rate->open_func)
1289 snd_dlobj_cache_put(rate->open_func);
1290 return snd_pcm_generic_close(pcm);
1294 * \brief Convert rate pcm frames to corresponding rate slave pcm frames
1295 * \param pcm PCM handle
1296 * \param frames Frames to be converted to slave frames
1297 * \retval Corresponding slave frames
1299 static snd_pcm_uframes_t snd_pcm_rate_slave_frames(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
1301 snd_pcm_uframes_t sframes;
1302 snd_pcm_rate_t *rate = pcm->private_data;
1304 if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
1305 sframes = rate->ops.output_frames(rate->obj, frames);
1307 sframes = rate->ops.input_frames(rate->obj, frames);
1312 static int snd_pcm_rate_may_wait_for_avail_min(snd_pcm_t *pcm,
1313 snd_pcm_uframes_t avail)
1315 return snd_pcm_plugin_may_wait_for_avail_min_conv(pcm, avail,
1316 snd_pcm_rate_slave_frames);
1319 static const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = {
1320 .status = snd_pcm_rate_status,
1321 .state = snd_pcm_rate_state,
1322 .hwsync = snd_pcm_rate_hwsync,
1323 .delay = snd_pcm_rate_delay,
1324 .prepare = snd_pcm_rate_prepare,
1325 .reset = snd_pcm_rate_reset,
1326 .start = snd_pcm_rate_start,
1327 .drop = snd_pcm_generic_drop,
1328 .drain = snd_pcm_rate_drain,
1329 .pause = snd_pcm_generic_pause,
1330 .rewindable = snd_pcm_rate_rewindable,
1331 .rewind = snd_pcm_rate_rewind,
1332 .forwardable = snd_pcm_rate_forwardable,
1333 .forward = snd_pcm_rate_forward,
1334 .resume = snd_pcm_generic_resume,
1335 .writei = snd_pcm_mmap_writei,
1336 .writen = snd_pcm_mmap_writen,
1337 .readi = snd_pcm_mmap_readi,
1338 .readn = snd_pcm_mmap_readn,
1339 .avail_update = snd_pcm_rate_avail_update,
1340 .mmap_commit = snd_pcm_rate_mmap_commit,
1341 .htimestamp = snd_pcm_rate_htimestamp,
1342 .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
1343 .poll_descriptors = snd_pcm_generic_poll_descriptors,
1344 .poll_revents = snd_pcm_rate_poll_revents,
1345 .may_wait_for_avail_min = snd_pcm_rate_may_wait_for_avail_min,
1348 static const snd_pcm_ops_t snd_pcm_rate_ops = {
1349 .close = snd_pcm_rate_close,
1350 .info = snd_pcm_generic_info,
1351 .hw_refine = snd_pcm_rate_hw_refine,
1352 .hw_params = snd_pcm_rate_hw_params,
1353 .hw_free = snd_pcm_rate_hw_free,
1354 .sw_params = snd_pcm_rate_sw_params,
1355 .channel_info = snd_pcm_generic_channel_info,
1356 .dump = snd_pcm_rate_dump,
1357 .nonblock = snd_pcm_generic_nonblock,
1358 .async = snd_pcm_generic_async,
1359 .mmap = snd_pcm_generic_mmap,
1360 .munmap = snd_pcm_generic_munmap,
1361 .query_chmaps = snd_pcm_generic_query_chmaps,
1362 .get_chmap = snd_pcm_generic_get_chmap,
1363 .set_chmap = snd_pcm_generic_set_chmap,
1367 * \brief Get a default converter string
1368 * \param root Root configuration node
1369 * \retval A const config item if found, or NULL
1371 const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root)
1374 /* look for default definition */
1375 if (snd_config_search(root, "defaults.pcm.rate_converter", &n) >= 0)
1380 static void rate_initial_setup(snd_pcm_rate_t *rate)
1382 if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION)
1383 rate->plugin_version = rate->ops.version;
1385 if (rate->plugin_version >= 0x010002 &&
1386 rate->ops.get_supported_rates)
1387 rate->ops.get_supported_rates(rate->obj,
1391 if (rate->plugin_version >= 0x010003 &&
1392 rate->ops.get_supported_formats) {
1393 rate->ops.get_supported_formats(rate->obj,
1396 &rate->format_flags);
1397 } else if (!rate->ops.convert && rate->ops.convert_s16) {
1398 rate->in_formats = rate->out_formats =
1399 1ULL << SND_PCM_FORMAT_S16;
1400 rate->format_flags = SND_PCM_RATE_FLAG_INTERLEAVED;
1405 static int is_builtin_plugin(const char *type)
1407 return strcmp(type, "linear") == 0;
1410 static const char *const default_rate_plugins[] = {
1411 "speexrate", "linear", NULL
1414 static int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_config_t *converter_conf, int verbose)
1416 char open_name[64], open_conf_name[64], lib_name[64], *lib = NULL;
1417 snd_pcm_rate_open_func_t open_func;
1418 snd_pcm_rate_open_conf_func_t open_conf_func;
1421 snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
1422 snprintf(open_conf_name, sizeof(open_conf_name), "_snd_pcm_rate_%s_open_conf", type);
1423 if (!is_builtin_plugin(type)) {
1424 snprintf(lib_name, sizeof(lib_name),
1425 "libasound_module_rate_%s.so", type);
1429 open_conf_func = snd_dlobj_cache_get(lib, open_conf_name, NULL, verbose && converter_conf != NULL);
1430 if (open_conf_func) {
1431 err = open_conf_func(SND_PCM_RATE_PLUGIN_VERSION,
1432 &rate->obj, &rate->ops, converter_conf);
1434 rate->open_func = open_conf_func;
1437 snd_dlobj_cache_put(open_conf_func);
1442 open_func = snd_dlobj_cache_get(lib, open_name, NULL, verbose);
1446 rate->open_func = open_func;
1448 err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1452 /* try to open with the old protocol version */
1453 rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD;
1454 err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD,
1455 &rate->obj, &rate->ops);
1459 snd_dlobj_cache_put(open_func);
1460 rate->open_func = NULL;
1466 * If the conf is an array of alternatives then the id of
1467 * the first element will be "0" (or maybe NULL). Otherwise assume it is
1470 static int is_string_array(const snd_config_t *conf)
1472 snd_config_iterator_t i;
1474 if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
1477 i = snd_config_iterator_first(conf);
1478 if (i && i != snd_config_iterator_end(conf)) {
1479 snd_config_t *n = snd_config_iterator_entry(i);
1481 if (snd_config_get_id(n, &id) < 0)
1483 if (id && strcmp(id, "0") != 0)
1491 * \brief Creates a new rate PCM
1492 * \param pcmp Returns created PCM handle
1493 * \param name Name of PCM
1494 * \param sformat Slave format
1495 * \param srate Slave rate
1496 * \param converter SRC type string node
1497 * \param slave Slave PCM handle
1498 * \param close_slave When set, the slave PCM handle is closed with copy PCM
1499 * \retval zero on success otherwise a negative error code
1500 * \warning Using of this function might be dangerous in the sense
1501 * of compatibility reasons. The prototype might be freely
1502 * changed in future.
1504 int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
1505 snd_pcm_format_t sformat, unsigned int srate,
1506 const snd_config_t *converter,
1507 snd_pcm_t *slave, int close_slave)
1510 snd_pcm_rate_t *rate;
1511 const char *type = NULL;
1514 snd_pcm_rate_open_func_t open_func;
1515 extern int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops);
1518 assert(pcmp && slave);
1519 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1520 snd_pcm_format_linear(sformat) != 1)
1522 rate = calloc(1, sizeof(snd_pcm_rate_t));
1526 rate->gen.slave = slave;
1527 rate->gen.close_slave = close_slave;
1528 rate->srate = srate;
1529 rate->sformat = sformat;
1531 rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
1532 rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
1533 rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
1535 err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
1544 const char *const *types;
1545 for (types = default_rate_plugins; *types; types++) {
1546 err = rate_open_func(rate, *types, NULL, 0);
1552 } else if (!snd_config_get_string(converter, &type))
1553 err = rate_open_func(rate, type, NULL, 1);
1554 else if (is_string_array(converter)) {
1555 snd_config_iterator_t i, next;
1556 snd_config_for_each(i, next, converter) {
1557 snd_config_t *n = snd_config_iterator_entry(i);
1558 if (snd_config_get_string(n, &type) < 0)
1560 err = rate_open_func(rate, type, NULL, 0);
1564 } else if (snd_config_get_type(converter) == SND_CONFIG_TYPE_COMPOUND) {
1565 snd_config_iterator_t i, next;
1566 snd_config_for_each(i, next, converter) {
1567 snd_config_t *n = snd_config_iterator_entry(i);
1569 if (snd_config_get_id(n, &id) < 0)
1571 if (strcmp(id, "name") != 0)
1573 err = snd_config_get_string(n, &type);
1582 snd_error(PCM, "No name given for rate converter");
1587 err = rate_open_func(rate, type, converter, 1);
1589 snd_error(PCM, "Invalid type for rate converter");
1595 snd_error(PCM, "Cannot find rate converter");
1602 open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear);
1603 err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1611 if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16) ||
1612 ! rate->ops.input_frames || ! rate->ops.output_frames) {
1613 snd_error(PCM, "Inproper rate plugin %s initialization", type);
1619 rate_initial_setup(rate);
1621 pcm->ops = &snd_pcm_rate_ops;
1622 pcm->fast_ops = &snd_pcm_rate_fast_ops;
1623 pcm->private_data = rate;
1624 pcm->poll_fd = slave->poll_fd;
1625 pcm->poll_events = slave->poll_events;
1627 pcm->tstamp_type = slave->tstamp_type;
1628 snd_pcm_set_hw_ptr(pcm, &rate->hw_ptr, -1, 0);
1629 snd_pcm_set_appl_ptr(pcm, &rate->appl_ptr, -1, 0);
1635 /*! \page pcm_plugins
1637 \section pcm_plugins_rate Plugin: Rate
1639 This plugin converts a stream rate. The input and output formats must be linear.
1643 type rate # Rate PCM
1644 slave STR # Slave name
1646 slave { # Slave definition
1647 pcm STR # Slave PCM name
1649 pcm { } # Slave PCM definition
1650 rate INT # Slave rate
1651 [format STR] # Slave format
1653 converter STR # optional
1655 converter [ STR1 STR2 ... ] # optional
1656 # Converter type, default is taken from
1657 # defaults.pcm.rate_converter
1659 converter { # optional
1660 name STR # Convertor type
1661 xxx yyy # optional convertor-specific configuration
1666 \subsection pcm_plugins_rate_funcref Function reference
1669 <LI>snd_pcm_rate_open()
1670 <LI>_snd_pcm_rate_open()
1676 * \brief Creates a new rate PCM
1677 * \param pcmp Returns created PCM handle
1678 * \param name Name of PCM
1679 * \param root Root configuration node
1680 * \param conf Configuration node with rate PCM description
1681 * \param stream Stream type
1682 * \param mode Stream mode
1683 * \retval zero on success otherwise a negative error code
1684 * \warning Using of this function might be dangerous in the sense
1685 * of compatibility reasons. The prototype might be freely
1686 * changed in future.
1688 int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
1689 snd_config_t *root, snd_config_t *conf,
1690 snd_pcm_stream_t stream, int mode)
1692 snd_config_iterator_t i, next;
1695 snd_config_t *slave = NULL, *sconf;
1696 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1698 const snd_config_t *converter = NULL;
1700 snd_config_for_each(i, next, conf) {
1701 snd_config_t *n = snd_config_iterator_entry(i);
1703 if (snd_config_get_id(n, &id) < 0)
1705 if (snd_pcm_conf_generic_id(id))
1707 if (strcmp(id, "slave") == 0) {
1711 if (strcmp(id, "converter") == 0) {
1715 snd_error(PCM, "Unknown field %s", id);
1719 snd_error(PCM, "slave is not defined");
1723 err = snd_pcm_slave_conf(root, slave, &sconf, 2,
1724 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
1725 SND_PCM_HW_PARAM_RATE, SCONF_MANDATORY, &srate);
1728 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1729 snd_pcm_format_linear(sformat) != 1) {
1730 snd_config_delete(sconf);
1731 snd_error(PCM, "slave format is not linear");
1734 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1735 snd_config_delete(sconf);
1738 err = snd_pcm_rate_open(pcmp, name, sformat, (unsigned int) srate,
1739 converter, spcm, 1);
1741 snd_pcm_close(spcm);
1745 SND_DLSYM_BUILD_VERSION(_snd_pcm_rate_open, SND_PCM_DLSYM_VERSION);