• Home
  • History
  • Annotate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/ap/gpl/iserver/alsa-lib-1.0.26/src/pcm/

Lines Matching defs:*

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
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 #include <inttypes.h>
31 #include <byteswap.h>
32 #include "pcm_local.h"
33 #include "pcm_plugin.h"
34 #include "pcm_rate.h"
35 #include "iatomic.h"
37 #include "plugin_ops.h"
39 #if 0
40 #define DEBUG_REFINE
41 #endif
43 #ifndef PIC
44 /* entry for static linking */
45 const char *_snd_module_pcm_rate = "";
46 #endif
48 #ifndef DOC_HIDDEN
50 typedef struct _snd_pcm_rate snd_pcm_rate_t;
52 struct _snd_pcm_rate {
53 snd_pcm_generic_t gen;
54 snd_atomic_write_t watom;
55 snd_pcm_uframes_t appl_ptr, hw_ptr;
56 snd_pcm_uframes_t last_commit_ptr;
57 snd_pcm_uframes_t orig_avail_min;
58 snd_pcm_sw_params_t sw_params;
59 snd_pcm_format_t sformat;
60 unsigned int srate;
61 snd_pcm_channel_area_t *pareas; /* areas for splitted period (rate pcm) */
62 snd_pcm_channel_area_t *sareas; /* areas for splitted period (slave pcm) */
63 snd_pcm_rate_info_t info;
64 void *open_func;
65 void *obj;
66 snd_pcm_rate_ops_t ops;
67 unsigned int get_idx;
68 unsigned int put_idx;
69 int16_t *src_buf;
70 int16_t *dst_buf;
71 int start_pending; /* start is triggered but not commited to slave */
72 snd_htimestamp_t trigger_tstamp;
73 unsigned int plugin_version;
74 unsigned int rate_min, rate_max;
77 #define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001 /* old rate plugin */
79 #endif /* DOC_HIDDEN */
81 static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
83 snd_pcm_rate_t *rate = pcm->private_data;
84 int err;
85 snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
86 snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
87 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
88 &access_mask);
89 if (err < 0)
90 return err;
91 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
92 &format_mask);
93 if (err < 0)
94 return err;
95 err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
96 if (err < 0)
97 return err;
98 if (rate->rate_min) {
99 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE,
100 rate->rate_min, 0);
101 if (err < 0)
102 return err;
104 if (rate->rate_max) {
105 err = _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_RATE,
106 rate->rate_max, 0);
107 if (err < 0)
108 return err;
110 params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
111 return 0;
114 static int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
116 snd_pcm_rate_t *rate = pcm->private_data;
117 snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
118 _snd_pcm_hw_params_any(sparams);
119 _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
120 &saccess_mask);
121 if (rate->sformat != SND_PCM_FORMAT_UNKNOWN) {
122 _snd_pcm_hw_params_set_format(sparams, rate->sformat);
123 _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
125 _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
126 rate->srate, 0, rate->srate + 1, -1);
127 return 0;
130 static int snd_pcm_rate_hw_refine_schange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
131 snd_pcm_hw_params_t *sparams)
133 snd_pcm_rate_t *rate = pcm->private_data;
134 snd_interval_t t, buffer_size;
135 const snd_interval_t *srate, *crate;
136 int err;
137 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
138 SND_PCM_HW_PARBIT_PERIOD_TIME |
139 SND_PCM_HW_PARBIT_TICK_TIME);
140 if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
141 links |= (SND_PCM_HW_PARBIT_FORMAT |
142 SND_PCM_HW_PARBIT_SUBFORMAT |
143 SND_PCM_HW_PARBIT_SAMPLE_BITS |
144 SND_PCM_HW_PARBIT_FRAME_BITS);
145 snd_interval_copy(&buffer_size, snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE));
146 snd_interval_unfloor(&buffer_size);
147 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
148 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
149 snd_interval_muldiv(&buffer_size, srate, crate, &t);
150 err = _snd_pcm_hw_param_set_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
151 if (err < 0)
152 return err;
153 err = _snd_pcm_hw_params_refine(sparams, links, params);
154 if (err < 0)
155 return err;
156 return 0;
159 static int snd_pcm_rate_hw_refine_cchange(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
160 snd_pcm_hw_params_t *sparams)
162 snd_pcm_rate_t *rate = pcm->private_data;
163 snd_interval_t t;
164 #ifdef DEBUG_REFINE
165 snd_output_t *out;
166 #endif
167 const snd_interval_t *sbuffer_size, *buffer_size;
168 const snd_interval_t *srate, *crate;
169 int err;
170 unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
171 SND_PCM_HW_PARBIT_PERIOD_TIME |
172 SND_PCM_HW_PARBIT_TICK_TIME);
173 if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
174 links |= (SND_PCM_HW_PARBIT_FORMAT |
175 SND_PCM_HW_PARBIT_SUBFORMAT |
176 SND_PCM_HW_PARBIT_SAMPLE_BITS |
177 SND_PCM_HW_PARBIT_FRAME_BITS);
178 sbuffer_size = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_BUFFER_SIZE);
179 crate = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_RATE);
180 srate = snd_pcm_hw_param_get_interval(sparams, SND_PCM_HW_PARAM_RATE);
181 snd_interval_muldiv(sbuffer_size, crate, srate, &t);
182 snd_interval_floor(&t);
183 err = _snd_pcm_hw_param_set_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &t);
184 if (err < 0)
185 return err;
186 buffer_size = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE);
188 * this condition probably needs more work:
189 * in case when the buffer_size is known and we are looking
190 * for best period_size, we should prefer situation when
191 * (buffer_size / period_size) * period_size == buffer_size
193 if (snd_interval_single(buffer_size) && buffer_size->integer) {
194 snd_interval_t *period_size;
195 period_size = (snd_interval_t *)snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_PERIOD_SIZE);
196 if (!snd_interval_checkempty(period_size) &&
197 period_size->openmin && period_size->openmax &&
198 period_size->min + 1 == period_size->max) {
199 if (period_size->min > 0 && (buffer_size->min / period_size->min) * period_size->min == buffer_size->min) {
200 snd_interval_set_value(period_size, period_size->min);
201 } else if ((buffer_size->max / period_size->max) * period_size->max == buffer_size->max) {
202 snd_interval_set_value(period_size, period_size->max);
206 #ifdef DEBUG_REFINE
207 snd_output_stdio_attach(&out, stderr, 0);
208 snd_output_printf(out, "REFINE (params):\n");
209 snd_pcm_hw_params_dump(params, out);
210 snd_output_printf(out, "REFINE (slave params):\n");
211 snd_pcm_hw_params_dump(sparams, out);
212 snd_output_close(out);
213 #endif
214 err = _snd_pcm_hw_params_refine(params, links, sparams);
215 #ifdef DEBUG_REFINE
216 snd_output_stdio_attach(&out, stderr, 0);
217 snd_output_printf(out, "********************\n");
218 snd_output_printf(out, "REFINE (params) (%i):\n", err);
219 snd_pcm_hw_params_dump(params, out);
220 snd_output_printf(out, "REFINE (slave params):\n");
221 snd_pcm_hw_params_dump(sparams, out);
222 snd_output_close(out);
223 #endif
224 if (err < 0)
225 return err;
226 return 0;
229 static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm,
230 snd_pcm_hw_params_t *params)
232 return snd_pcm_hw_refine_slave(pcm, params,
233 snd_pcm_rate_hw_refine_cprepare,
234 snd_pcm_rate_hw_refine_cchange,
235 snd_pcm_rate_hw_refine_sprepare,
236 snd_pcm_rate_hw_refine_schange,
237 snd_pcm_generic_hw_refine);
240 static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
242 snd_pcm_rate_t *rate = pcm->private_data;
243 snd_pcm_t *slave = rate->gen.slave;
244 snd_pcm_rate_side_info_t *sinfo, *cinfo;
245 unsigned int channels, cwidth, swidth, chn;
246 int err = snd_pcm_hw_params_slave(pcm, params,
247 snd_pcm_rate_hw_refine_cchange,
248 snd_pcm_rate_hw_refine_sprepare,
249 snd_pcm_rate_hw_refine_schange,
250 snd_pcm_generic_hw_params);
251 if (err < 0)
252 return err;
254 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
255 cinfo = &rate->info.in;
256 sinfo = &rate->info.out;
257 } else {
258 sinfo = &rate->info.in;
259 cinfo = &rate->info.out;
261 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &cinfo->format);
262 if (err < 0)
263 return err;
264 err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &cinfo->rate, 0);
265 if (err < 0)
266 return err;
267 err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &cinfo->period_size, 0);
268 if (err < 0)
269 return err;
270 err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &cinfo->buffer_size);
271 if (err < 0)
272 return err;
273 err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels);
274 if (err < 0)
275 return err;
277 rate->info.channels = channels;
278 sinfo->format = slave->format;
279 sinfo->rate = slave->rate;
280 sinfo->buffer_size = slave->buffer_size;
281 sinfo->period_size = slave->period_size;
283 if (CHECK_SANITY(rate->pareas)) {
284 SNDMSG("rate plugin already in use");
285 return -EBUSY;
287 err = rate->ops.init(rate->obj, &rate->info);
288 if (err < 0)
289 return err;
291 rate->pareas = malloc(2 * channels * sizeof(*rate->pareas));
292 if (rate->pareas == NULL)
293 goto error;
295 cwidth = snd_pcm_format_physical_width(cinfo->format);
296 swidth = snd_pcm_format_physical_width(sinfo->format);
297 rate->pareas[0].addr = malloc(((cwidth * channels * cinfo->period_size) / 8) +
298 ((swidth * channels * sinfo->period_size) / 8));
299 if (rate->pareas[0].addr == NULL)
300 goto error;
302 rate->sareas = rate->pareas + channels;
303 rate->sareas[0].addr = (char *)rate->pareas[0].addr + ((cwidth * channels * cinfo->period_size) / 8);
304 for (chn = 0; chn < channels; chn++) {
305 rate->pareas[chn].addr = rate->pareas[0].addr + (cwidth * chn * cinfo->period_size) / 8;
306 rate->pareas[chn].first = 0;
307 rate->pareas[chn].step = cwidth;
308 rate->sareas[chn].addr = rate->sareas[0].addr + (swidth * chn * sinfo->period_size) / 8;
309 rate->sareas[chn].first = 0;
310 rate->sareas[chn].step = swidth;
313 if (rate->ops.convert_s16) {
314 rate->get_idx = snd_pcm_linear_get_index(rate->info.in.format, SND_PCM_FORMAT_S16);
315 rate->put_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, rate->info.out.format);
316 free(rate->src_buf);
317 rate->src_buf = malloc(channels * rate->info.in.period_size * 2);
318 free(rate->dst_buf);
319 rate->dst_buf = malloc(channels * rate->info.out.period_size * 2);
320 if (! rate->src_buf || ! rate->dst_buf)
321 goto error;
324 return 0;
326 error:
327 if (rate->pareas) {
328 free(rate->pareas[0].addr);
329 free(rate->pareas);
330 rate->pareas = NULL;
332 if (rate->ops.free)
333 rate->ops.free(rate->obj);
334 return -ENOMEM;
337 static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
339 snd_pcm_rate_t *rate = pcm->private_data;
340 if (rate->pareas) {
341 free(rate->pareas[0].addr);
342 free(rate->pareas);
343 rate->pareas = NULL;
344 rate->sareas = NULL;
346 if (rate->ops.free)
347 rate->ops.free(rate->obj);
348 free(rate->src_buf);
349 free(rate->dst_buf);
350 rate->src_buf = rate->dst_buf = NULL;
351 return snd_pcm_hw_free(rate->gen.slave);
354 static void recalc(snd_pcm_t *pcm, snd_pcm_uframes_t *val)
356 snd_pcm_rate_t *rate = pcm->private_data;
357 snd_pcm_t *slave = rate->gen.slave;
358 unsigned long div;
360 if (*val == pcm->buffer_size) {
361 *val = slave->buffer_size;
362 } else {
363 div = *val / pcm->period_size;
364 if (div * pcm->period_size == *val)
365 *val = div * slave->period_size;
366 else
367 *val = muldiv_near(*val, slave->period_size, pcm->period_size);
371 static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
373 snd_pcm_rate_t *rate = pcm->private_data;
374 snd_pcm_t *slave = rate->gen.slave;
375 snd_pcm_sw_params_t *sparams;
376 snd_pcm_uframes_t boundary1, boundary2, sboundary;
377 int err;
379 sparams = &rate->sw_params;
380 err = snd_pcm_sw_params_current(slave, sparams);
381 if (err < 0)
382 return err;
383 sboundary = sparams->boundary;
384 *sparams = *params;
385 boundary1 = pcm->buffer_size;
386 boundary2 = slave->buffer_size;
387 while (boundary1 * 2 <= LONG_MAX - pcm->buffer_size &&
388 boundary2 * 2 <= LONG_MAX - slave->buffer_size) {
389 boundary1 *= 2;
390 boundary2 *= 2;
392 params->boundary = boundary1;
393 sparams->boundary = sboundary;
395 if (rate->ops.adjust_pitch)
396 rate->ops.adjust_pitch(rate->obj, &rate->info);
398 recalc(pcm, &sparams->avail_min);
399 rate->orig_avail_min = sparams->avail_min;
400 recalc(pcm, &sparams->start_threshold);
401 if (sparams->avail_min < 1) sparams->avail_min = 1;
402 if (sparams->start_threshold <= slave->buffer_size) {
403 if (sparams->start_threshold > (slave->buffer_size / sparams->avail_min) * sparams->avail_min)
404 sparams->start_threshold = (slave->buffer_size / sparams->avail_min) * sparams->avail_min;
406 if (sparams->stop_threshold >= params->boundary) {
407 sparams->stop_threshold = sparams->boundary;
408 } else {
409 recalc(pcm, &sparams->stop_threshold);
411 recalc(pcm, &sparams->silence_threshold);
412 if (sparams->silence_size >= params->boundary) {
413 sparams->silence_size = sparams->boundary;
414 } else {
415 recalc(pcm, &sparams->silence_size);
417 return snd_pcm_sw_params(slave, sparams);
420 static int snd_pcm_rate_init(snd_pcm_t *pcm)
422 snd_pcm_rate_t *rate = pcm->private_data;
424 if (rate->ops.reset)
425 rate->ops.reset(rate->obj);
426 rate->last_commit_ptr = 0;
427 rate->start_pending = 0;
428 return 0;
431 static void convert_to_s16(snd_pcm_rate_t *rate, int16_t *buf,
432 const snd_pcm_channel_area_t *areas,
433 snd_pcm_uframes_t offset, unsigned int frames,
434 unsigned int channels)
436 #ifndef DOC_HIDDEN
437 #define GET16_LABELS
438 #include "plugin_ops.h"
439 #undef GET16_LABELS
440 #endif /* DOC_HIDDEN */
441 void *get = get16_labels[rate->get_idx];
442 const char *src;
443 int16_t sample;
444 const char *srcs[channels];
445 int src_step[channels];
446 unsigned int c;
448 for (c = 0; c < channels; c++) {
449 srcs[c] = snd_pcm_channel_area_addr(areas + c, offset);
450 src_step[c] = snd_pcm_channel_area_step(areas + c);
453 while (frames--) {
454 for (c = 0; c < channels; c++) {
455 src = srcs[c];
456 goto *get;
457 #ifndef DOC_HIDDEN
458 #define GET16_END after_get
459 #include "plugin_ops.h"
460 #undef GET16_END
461 #endif /* DOC_HIDDEN */
462 after_get:
463 *buf++ = sample;
464 srcs[c] += src_step[c];
469 static void convert_from_s16(snd_pcm_rate_t *rate, const int16_t *buf,
470 const snd_pcm_channel_area_t *areas,
471 snd_pcm_uframes_t offset, unsigned int frames,
472 unsigned int channels)
474 #ifndef DOC_HIDDEN
475 #define PUT16_LABELS
476 #include "plugin_ops.h"
477 #undef PUT16_LABELS
478 #endif /* DOC_HIDDEN */
479 void *put = put16_labels[rate->put_idx];
480 char *dst;
481 int16_t sample;
482 char *dsts[channels];
483 int dst_step[channels];
484 unsigned int c;
486 for (c = 0; c < channels; c++) {
487 dsts[c] = snd_pcm_channel_area_addr(areas + c, offset);
488 dst_step[c] = snd_pcm_channel_area_step(areas + c);
491 while (frames--) {
492 for (c = 0; c < channels; c++) {
493 dst = dsts[c];
494 sample = *buf++;
495 goto *put;
496 #ifndef DOC_HIDDEN
497 #define PUT16_END after_put
498 #include "plugin_ops.h"
499 #undef PUT16_END
500 #endif /* DOC_HIDDEN */
501 after_put:
502 dsts[c] += dst_step[c];
507 static void do_convert(const snd_pcm_channel_area_t *dst_areas,
508 snd_pcm_uframes_t dst_offset, unsigned int dst_frames,
509 const snd_pcm_channel_area_t *src_areas,
510 snd_pcm_uframes_t src_offset, unsigned int src_frames,
511 unsigned int channels,
512 snd_pcm_rate_t *rate)
514 if (rate->ops.convert_s16) {
515 const int16_t *src;
516 int16_t *dst;
517 if (! rate->src_buf)
518 src = src_areas->addr + src_offset * 2 * channels;
519 else {
520 convert_to_s16(rate, rate->src_buf, src_areas, src_offset,
521 src_frames, channels);
522 src = rate->src_buf;
524 if (! rate->dst_buf)
525 dst = dst_areas->addr + dst_offset * 2 * channels;
526 else
527 dst = rate->dst_buf;
528 rate->ops.convert_s16(rate->obj, dst, dst_frames, src, src_frames);
529 if (dst == rate->dst_buf)
530 convert_from_s16(rate, rate->dst_buf, dst_areas, dst_offset,
531 dst_frames, channels);
532 } else {
533 rate->ops.convert(rate->obj, dst_areas, dst_offset, dst_frames,
534 src_areas, src_offset, src_frames);
538 static inline void
539 snd_pcm_rate_write_areas1(snd_pcm_t *pcm,
540 const snd_pcm_channel_area_t *areas,
541 snd_pcm_uframes_t offset,
542 const snd_pcm_channel_area_t *slave_areas,
543 snd_pcm_uframes_t slave_offset)
545 snd_pcm_rate_t *rate = pcm->private_data;
546 do_convert(slave_areas, slave_offset, rate->gen.slave->period_size,
547 areas, offset, pcm->period_size,
548 pcm->channels, rate);
551 static inline void
552 snd_pcm_rate_read_areas1(snd_pcm_t *pcm,
553 const snd_pcm_channel_area_t *areas,
554 snd_pcm_uframes_t offset,
555 const snd_pcm_channel_area_t *slave_areas,
556 snd_pcm_uframes_t slave_offset)
558 snd_pcm_rate_t *rate = pcm->private_data;
559 do_convert(areas, offset, pcm->period_size,
560 slave_areas, slave_offset, rate->gen.slave->period_size,
561 pcm->channels, rate);
564 static inline snd_pcm_sframes_t snd_pcm_rate_move_applptr(snd_pcm_t *pcm, snd_pcm_sframes_t frames)
566 snd_pcm_rate_t *rate = pcm->private_data;
567 snd_pcm_uframes_t orig_appl_ptr, appl_ptr = rate->appl_ptr, slave_appl_ptr;
568 snd_pcm_sframes_t diff, ndiff;
569 snd_pcm_t *slave = rate->gen.slave;
571 orig_appl_ptr = rate->appl_ptr;
572 if (frames > 0)
573 snd_pcm_mmap_appl_forward(pcm, frames);
574 else
575 snd_pcm_mmap_appl_backward(pcm, -frames);
576 slave_appl_ptr =
577 (appl_ptr / pcm->period_size) * rate->gen.slave->period_size;
578 diff = slave_appl_ptr - *slave->appl.ptr;
579 if (diff < -(snd_pcm_sframes_t)(slave->boundary / 2)) {
580 diff = (slave->boundary - *slave->appl.ptr) + slave_appl_ptr;
581 } else if (diff > (snd_pcm_sframes_t)(slave->boundary / 2)) {
582 diff = -((slave->boundary - slave_appl_ptr) + *slave->appl.ptr);
584 if (diff == 0)
585 return frames;
586 if (diff > 0) {
587 ndiff = snd_pcm_forward(rate->gen.slave, diff);
588 } else {
589 ndiff = snd_pcm_rewind(rate->gen.slave, diff);
591 if (ndiff < 0)
592 return diff;
593 slave_appl_ptr = *slave->appl.ptr;
594 rate->appl_ptr =
595 (slave_appl_ptr / rate->gen.slave->period_size) * pcm->period_size +
596 orig_appl_ptr % pcm->period_size;
597 if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
598 rate->appl_ptr += rate->ops.input_frames(rate->obj, slave_appl_ptr % rate->gen.slave->period_size);
599 else
600 rate->appl_ptr += rate->ops.output_frames(rate->obj, slave_appl_ptr % rate->gen.slave->period_size);
602 diff = orig_appl_ptr - rate->appl_ptr;
603 if (diff < -(snd_pcm_sframes_t)(slave->boundary / 2)) {
604 diff = (slave->boundary - rate->appl_ptr) + orig_appl_ptr;
605 } else if (diff > (snd_pcm_sframes_t)(slave->boundary / 2)) {
606 diff = -((slave->boundary - orig_appl_ptr) + rate->appl_ptr);
608 if (frames < 0)
609 diff = -diff;
611 rate->last_commit_ptr = rate->appl_ptr - rate->appl_ptr % pcm->period_size;
613 return diff;
616 static inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm)
618 snd_pcm_rate_t *rate = pcm->private_data;
619 snd_pcm_uframes_t slave_hw_ptr = *rate->gen.slave->hw.ptr;
621 if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
622 return;
623 /* FIXME: boundary overlap of slave hw_ptr isn't evaluated here!
624 * e.g. if slave rate is small...
626 rate->hw_ptr =
627 (slave_hw_ptr / rate->gen.slave->period_size) * pcm->period_size +
628 rate->ops.input_frames(rate->obj, slave_hw_ptr % rate->gen.slave->period_size);
631 static int snd_pcm_rate_hwsync(snd_pcm_t *pcm)
633 snd_pcm_rate_t *rate = pcm->private_data;
634 int err = snd_pcm_hwsync(rate->gen.slave);
635 if (err < 0)
636 return err;
637 snd_atomic_write_begin(&rate->watom);
638 snd_pcm_rate_sync_hwptr(pcm);
639 snd_atomic_write_end(&rate->watom);
640 return 0;
643 static int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
645 snd_pcm_rate_hwsync(pcm);
646 if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
647 *delayp = snd_pcm_mmap_playback_hw_avail(pcm);
648 else
649 *delayp = snd_pcm_mmap_capture_hw_avail(pcm);
650 return 0;
653 static int snd_pcm_rate_prepare(snd_pcm_t *pcm)
655 snd_pcm_rate_t *rate = pcm->private_data;
656 int err;
658 snd_atomic_write_begin(&rate->watom);
659 err = snd_pcm_prepare(rate->gen.slave);
660 if (err < 0) {
661 snd_atomic_write_end(&rate->watom);
662 return err;
664 *pcm->hw.ptr = 0;
665 *pcm->appl.ptr = 0;
666 snd_atomic_write_end(&rate->watom);
667 err = snd_pcm_rate_init(pcm);
668 if (err < 0)
669 return err;
670 return 0;
673 static int snd_pcm_rate_reset(snd_pcm_t *pcm)
675 snd_pcm_rate_t *rate = pcm->private_data;
676 int err;
677 snd_atomic_write_begin(&rate->watom);
678 err = snd_pcm_reset(rate->gen.slave);
679 if (err < 0) {
680 snd_atomic_write_end(&rate->watom);
681 return err;
683 *pcm->hw.ptr = 0;
684 *pcm->appl.ptr = 0;
685 snd_atomic_write_end(&rate->watom);
686 err = snd_pcm_rate_init(pcm);
687 if (err < 0)
688 return err;
689 return 0;
692 static snd_pcm_sframes_t snd_pcm_rate_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
694 snd_pcm_rate_t *rate = pcm->private_data;
695 snd_pcm_sframes_t n = snd_pcm_mmap_hw_avail(pcm);
697 if ((snd_pcm_uframes_t)n > frames)
698 frames = n;
699 if (frames == 0)
700 return 0;
702 snd_atomic_write_begin(&rate->watom);
703 n = snd_pcm_rate_move_applptr(pcm, -frames);
704 snd_atomic_write_end(&rate->watom);
705 return n;
708 static snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
710 snd_pcm_rate_t *rate = pcm->private_data;
711 snd_pcm_sframes_t n = snd_pcm_mmap_avail(pcm);
713 if ((snd_pcm_uframes_t)n > frames)
714 frames = n;
715 if (frames == 0)
716 return 0;
718 snd_atomic_write_begin(&rate->watom);
719 n = snd_pcm_rate_move_applptr(pcm, frames);
720 snd_atomic_write_end(&rate->watom);
721 return n;
724 static int snd_pcm_rate_commit_area(snd_pcm_t *pcm, snd_pcm_rate_t *rate,
725 snd_pcm_uframes_t appl_offset,
726 snd_pcm_uframes_t size,
727 snd_pcm_uframes_t slave_size)
729 snd_pcm_uframes_t cont = pcm->buffer_size - appl_offset;
730 const snd_pcm_channel_area_t *areas;
731 const snd_pcm_channel_area_t *slave_areas;
732 snd_pcm_uframes_t slave_offset, xfer;
733 snd_pcm_uframes_t slave_frames = ULONG_MAX;
734 snd_pcm_sframes_t result;
736 areas = snd_pcm_mmap_areas(pcm);
737 if (cont >= size) {
738 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
739 if (result < 0)
740 return result;
741 if (slave_frames < slave_size) {
742 snd_pcm_rate_write_areas1(pcm, areas, appl_offset, rate->sareas, 0);
743 goto __partial;
745 snd_pcm_rate_write_areas1(pcm, areas, appl_offset,
746 slave_areas, slave_offset);
747 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, slave_size);
748 if (result < (snd_pcm_sframes_t)slave_size) {
749 if (result < 0)
750 return result;
751 result = snd_pcm_rewind(rate->gen.slave, result);
752 if (result < 0)
753 return result;
754 return 0;
756 } else {
757 snd_pcm_areas_copy(rate->pareas, 0,
758 areas, appl_offset,
759 pcm->channels, cont,
760 pcm->format);
761 snd_pcm_areas_copy(rate->pareas, cont,
762 areas, 0,
763 pcm->channels, size - cont,
764 pcm->format);
766 snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0);
768 /* ok, commit first fragment */
769 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
770 if (result < 0)
771 return result;
772 __partial:
773 xfer = 0;
774 cont = slave_frames;
775 if (cont > slave_size)
776 cont = slave_size;
777 snd_pcm_areas_copy(slave_areas, slave_offset,
778 rate->sareas, 0,
779 pcm->channels, cont,
780 rate->gen.slave->format);
781 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
782 if (result < (snd_pcm_sframes_t)cont) {
783 if (result < 0)
784 return result;
785 result = snd_pcm_rewind(rate->gen.slave, result);
786 if (result < 0)
787 return result;
788 return 0;
790 xfer = cont;
792 if (xfer == slave_size)
793 goto commit_done;
795 /* commit second fragment */
796 cont = slave_size - cont;
797 slave_frames = cont;
798 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
799 if (result < 0)
800 return result;
801 #if 0
802 if (slave_offset) {
803 SNDERR("non-zero slave_offset %ld", slave_offset);
804 return -EIO;
806 #endif
807 snd_pcm_areas_copy(slave_areas, slave_offset,
808 rate->sareas, xfer,
809 pcm->channels, cont,
810 rate->gen.slave->format);
811 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
812 if (result < (snd_pcm_sframes_t)cont) {
813 if (result < 0)
814 return result;
815 result = snd_pcm_rewind(rate->gen.slave, result + xfer);
816 if (result < 0)
817 return result;
818 return 0;
822 commit_done:
823 if (rate->start_pending) {
824 /* we have pending start-trigger. let's issue it now */
825 snd_pcm_start(rate->gen.slave);
826 rate->start_pending = 0;
828 return 1;
831 static int snd_pcm_rate_commit_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t appl_offset)
833 snd_pcm_rate_t *rate = pcm->private_data;
835 return snd_pcm_rate_commit_area(pcm, rate, appl_offset, pcm->period_size,
836 rate->gen.slave->period_size);
839 static int snd_pcm_rate_grab_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t hw_offset)
841 snd_pcm_rate_t *rate = pcm->private_data;
842 snd_pcm_uframes_t cont = pcm->buffer_size - hw_offset;
843 const snd_pcm_channel_area_t *areas;
844 const snd_pcm_channel_area_t *slave_areas;
845 snd_pcm_uframes_t slave_offset, xfer;
846 snd_pcm_uframes_t slave_frames = ULONG_MAX;
847 snd_pcm_sframes_t result;
849 areas = snd_pcm_mmap_areas(pcm);
850 if (cont >= pcm->period_size) {
851 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
852 if (result < 0)
853 return result;
854 if (slave_frames < rate->gen.slave->period_size)
855 goto __partial;
856 snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
857 slave_areas, slave_offset);
858 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, rate->gen.slave->period_size);
859 if (result < (snd_pcm_sframes_t)rate->gen.slave->period_size) {
860 if (result < 0)
861 return result;
862 result = snd_pcm_rewind(rate->gen.slave, result);
863 if (result < 0)
864 return result;
865 return 0;
867 } else {
868 /* ok, grab first fragment */
869 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
870 if (result < 0)
871 return result;
872 __partial:
873 xfer = 0;
874 cont = slave_frames;
875 if (cont > rate->gen.slave->period_size)
876 cont = rate->gen.slave->period_size;
877 snd_pcm_areas_copy(rate->sareas, 0,
878 slave_areas, slave_offset,
879 pcm->channels, cont,
880 rate->gen.slave->format);
881 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
882 if (result < (snd_pcm_sframes_t)cont) {
883 if (result < 0)
884 return result;
885 result = snd_pcm_rewind(rate->gen.slave, result);
886 if (result < 0)
887 return result;
888 return 0;
890 xfer = cont;
892 if (xfer == rate->gen.slave->period_size)
893 goto __transfer;
895 /* grab second fragment */
896 cont = rate->gen.slave->period_size - cont;
897 slave_frames = cont;
898 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
899 if (result < 0)
900 return result;
901 #if 0
902 if (slave_offset) {
903 SNDERR("non-zero slave_offset %ld", slave_offset);
904 return -EIO;
906 #endif
907 snd_pcm_areas_copy(rate->sareas, xfer,
908 slave_areas, slave_offset,
909 pcm->channels, cont,
910 rate->gen.slave->format);
911 result = snd_pcm_mmap_commit(rate->gen.slave, slave_offset, cont);
912 if (result < (snd_pcm_sframes_t)cont) {
913 if (result < 0)
914 return result;
915 result = snd_pcm_rewind(rate->gen.slave, result + xfer);
916 if (result < 0)
917 return result;
918 return 0;
921 __transfer:
922 cont = pcm->buffer_size - hw_offset;
923 if (cont >= pcm->period_size) {
924 snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
925 rate->sareas, 0);
926 } else {
927 snd_pcm_rate_read_areas1(pcm,
928 rate->pareas, 0,
929 rate->sareas, 0);
930 snd_pcm_areas_copy(areas, hw_offset,
931 rate->pareas, 0,
932 pcm->channels, cont,
933 pcm->format);
934 snd_pcm_areas_copy(areas, 0,
935 rate->pareas, cont,
936 pcm->channels, pcm->period_size - cont,
937 pcm->format);
940 return 1;
943 static int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t appl_ptr)
945 snd_pcm_rate_t *rate = pcm->private_data;
946 snd_pcm_t *slave = rate->gen.slave;
947 snd_pcm_uframes_t xfer;
948 snd_pcm_sframes_t slave_size;
949 int err;
951 slave_size = snd_pcm_avail_update(slave);
952 if (slave_size < 0)
953 return slave_size;
955 if (appl_ptr < rate->last_commit_ptr)
956 xfer = appl_ptr - rate->last_commit_ptr + pcm->boundary;
957 else
958 xfer = appl_ptr - rate->last_commit_ptr;
959 while (xfer >= pcm->period_size &&
960 (snd_pcm_uframes_t)slave_size >= rate->gen.slave->period_size) {
961 err = snd_pcm_rate_commit_next_period(pcm, rate->last_commit_ptr % pcm->buffer_size);
962 if (err == 0)
963 break;
964 if (err < 0)
965 return err;
966 xfer -= pcm->period_size;
967 slave_size -= rate->gen.slave->period_size;
968 rate->last_commit_ptr += pcm->period_size;
969 if (rate->last_commit_ptr >= pcm->boundary)
970 rate->last_commit_ptr = 0;
972 return 0;
975 static snd_pcm_sframes_t snd_pcm_rate_mmap_commit(snd_pcm_t *pcm,
976 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
977 snd_pcm_uframes_t size)
979 snd_pcm_rate_t *rate = pcm->private_data;
980 int err;
982 if (size == 0)
983 return 0;
984 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
985 err = snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr + size);
986 if (err < 0)
987 return err;
989 snd_atomic_write_begin(&rate->watom);
990 snd_pcm_mmap_appl_forward(pcm, size);
991 snd_atomic_write_end(&rate->watom);
992 return size;
995 static snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm)
997 snd_pcm_rate_t *rate = pcm->private_data;
998 snd_pcm_t *slave = rate->gen.slave;
999 snd_pcm_uframes_t slave_size;
1001 slave_size = snd_pcm_avail_update(slave);
1002 if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1003 goto _capture;
1004 snd_atomic_write_begin(&rate->watom);
1005 snd_pcm_rate_sync_hwptr(pcm);
1006 snd_atomic_write_end(&rate->watom);
1007 snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
1008 return snd_pcm_mmap_avail(pcm);
1009 _capture: {
1010 snd_pcm_uframes_t xfer, hw_offset, size;
1012 xfer = snd_pcm_mmap_capture_avail(pcm);
1013 size = pcm->buffer_size - xfer;
1014 hw_offset = snd_pcm_mmap_hw_offset(pcm);
1015 while (size >= pcm->period_size &&
1016 slave_size >= rate->gen.slave->period_size) {
1017 int err = snd_pcm_rate_grab_next_period(pcm, hw_offset);
1018 if (err < 0)
1019 return err;
1020 if (err == 0)
1021 return (snd_pcm_sframes_t)xfer;
1022 xfer += pcm->period_size;
1023 size -= pcm->period_size;
1024 slave_size -= rate->gen.slave->period_size;
1025 hw_offset += pcm->period_size;
1026 hw_offset %= pcm->buffer_size;
1027 snd_pcm_mmap_hw_forward(pcm, pcm->period_size);
1029 return (snd_pcm_sframes_t)xfer;
1033 static int snd_pcm_rate_htimestamp(snd_pcm_t *pcm,
1034 snd_pcm_uframes_t *avail,
1035 snd_htimestamp_t *tstamp)
1037 snd_pcm_rate_t *rate = pcm->private_data;
1038 snd_pcm_sframes_t avail1;
1039 snd_pcm_uframes_t tmp;
1040 int ok = 0, err;
1042 while (1) {
1043 /* the position is from this plugin itself */
1044 avail1 = snd_pcm_avail_update(pcm);
1045 if (avail1 < 0)
1046 return avail1;
1047 if (ok && (snd_pcm_uframes_t)avail1 == *avail)
1048 break;
1049 *avail = avail1;
1050 /* timestamp is taken from the slave PCM */
1051 err = snd_pcm_htimestamp(rate->gen.slave, &tmp, tstamp);
1052 if (err < 0)
1053 return err;
1054 ok = 1;
1056 return 0;
1059 static int snd_pcm_rate_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
1061 snd_pcm_rate_t *rate = pcm->private_data;
1062 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1063 /* Try to sync as much as possible */
1064 snd_pcm_rate_hwsync(pcm);
1065 snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr);
1067 return snd_pcm_poll_descriptors_revents(rate->gen.slave, pfds, nfds, revents);
1070 static int snd_pcm_rate_drain(snd_pcm_t *pcm)
1072 snd_pcm_rate_t *rate = pcm->private_data;
1074 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1075 /* commit the remaining fraction (if any) */
1076 snd_pcm_uframes_t size, ofs, saved_avail_min;
1077 snd_pcm_sw_params_t sw_params;
1079 /* temporarily set avail_min to one */
1080 sw_params = rate->sw_params;
1081 saved_avail_min = sw_params.avail_min;
1082 sw_params.avail_min = 1;
1083 snd_pcm_sw_params(rate->gen.slave, &sw_params);
1085 size = rate->appl_ptr - rate->last_commit_ptr;
1086 ofs = rate->last_commit_ptr % pcm->buffer_size;
1087 while (size > 0) {
1088 snd_pcm_uframes_t psize, spsize;
1090 if (snd_pcm_wait(rate->gen.slave, -1) < 0)
1091 break;
1092 if (size > pcm->period_size) {
1093 psize = pcm->period_size;
1094 spsize = rate->gen.slave->period_size;
1095 } else {
1096 psize = size;
1097 spsize = rate->ops.output_frames(rate->obj, size);
1098 if (! spsize)
1099 break;
1101 snd_pcm_rate_commit_area(pcm, rate, ofs,
1102 psize, spsize);
1103 ofs = (ofs + psize) % pcm->buffer_size;
1104 size -= psize;
1106 sw_params.avail_min = saved_avail_min;
1107 snd_pcm_sw_params(rate->gen.slave, &sw_params);
1109 return snd_pcm_drain(rate->gen.slave);
1112 static snd_pcm_state_t snd_pcm_rate_state(snd_pcm_t *pcm)
1114 snd_pcm_rate_t *rate = pcm->private_data;
1115 if (rate->start_pending) /* pseudo-state */
1116 return SND_PCM_STATE_RUNNING;
1117 return snd_pcm_state(rate->gen.slave);
1121 static int snd_pcm_rate_start(snd_pcm_t *pcm)
1123 snd_pcm_rate_t *rate = pcm->private_data;
1124 snd_pcm_uframes_t avail;
1126 if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1127 return snd_pcm_start(rate->gen.slave);
1129 if (snd_pcm_state(rate->gen.slave) != SND_PCM_STATE_PREPARED)
1130 return -EBADFD;
1132 gettimestamp(&rate->trigger_tstamp, pcm->monotonic);
1134 avail = snd_pcm_mmap_playback_hw_avail(rate->gen.slave);
1135 if (avail == 0) {
1136 /* postpone the trigger since we have no data committed yet */
1137 rate->start_pending = 1;
1138 return 0;
1140 rate->start_pending = 0;
1141 return snd_pcm_start(rate->gen.slave);
1144 static int snd_pcm_rate_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
1146 snd_pcm_rate_t *rate = pcm->private_data;
1147 snd_pcm_sframes_t err;
1148 snd_atomic_read_t ratom;
1149 snd_atomic_read_init(&ratom, &rate->watom);
1150 _again:
1151 snd_atomic_read_begin(&ratom);
1152 err = snd_pcm_status(rate->gen.slave, status);
1153 if (err < 0) {
1154 snd_atomic_read_ok(&ratom);
1155 return err;
1157 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1158 if (rate->start_pending)
1159 status->state = SND_PCM_STATE_RUNNING;
1160 status->trigger_tstamp = rate->trigger_tstamp;
1162 snd_pcm_rate_sync_hwptr(pcm);
1163 status->appl_ptr = *pcm->appl.ptr;
1164 status->hw_ptr = *pcm->hw.ptr;
1165 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1166 status->delay = snd_pcm_mmap_playback_hw_avail(pcm);
1167 status->avail = snd_pcm_mmap_playback_avail(pcm);
1168 status->avail_max = rate->ops.input_frames(rate->obj, status->avail_max);
1169 } else {
1170 status->delay = snd_pcm_mmap_capture_hw_avail(pcm);
1171 status->avail = snd_pcm_mmap_capture_avail(pcm);
1172 status->avail_max = rate->ops.output_frames(rate->obj, status->avail_max);
1174 if (!snd_atomic_read_ok(&ratom)) {
1175 snd_atomic_read_wait(&ratom);
1176 goto _again;
1178 return 0;
1181 static void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
1183 snd_pcm_rate_t *rate = pcm->private_data;
1184 if (rate->sformat == SND_PCM_FORMAT_UNKNOWN)
1185 snd_output_printf(out, "Rate conversion PCM (%d)\n",
1186 rate->srate);
1187 else
1188 snd_output_printf(out, "Rate conversion PCM (%d, sformat=%s)\n",
1189 rate->srate,
1190 snd_pcm_format_name(rate->sformat));
1191 if (rate->ops.dump)
1192 rate->ops.dump(rate->obj, out);
1193 snd_output_printf(out, "Protocol version: %x\n", rate->plugin_version);
1194 if (pcm->setup) {
1195 snd_output_printf(out, "Its setup is:\n");
1196 snd_pcm_dump_setup(pcm, out);
1198 snd_output_printf(out, "Slave: ");
1199 snd_pcm_dump(rate->gen.slave, out);
1202 static int snd_pcm_rate_close(snd_pcm_t *pcm)
1204 snd_pcm_rate_t *rate = pcm->private_data;
1206 if (rate->ops.close)
1207 rate->ops.close(rate->obj);
1208 if (rate->open_func)
1209 snd_dlobj_cache_put(rate->open_func);
1210 return snd_pcm_generic_close(pcm);
1213 static const snd_pcm_fast_ops_t snd_pcm_rate_fast_ops = {
1214 .status = snd_pcm_rate_status,
1215 .state = snd_pcm_rate_state,
1216 .hwsync = snd_pcm_rate_hwsync,
1217 .delay = snd_pcm_rate_delay,
1218 .prepare = snd_pcm_rate_prepare,
1219 .reset = snd_pcm_rate_reset,
1220 .start = snd_pcm_rate_start,
1221 .drop = snd_pcm_generic_drop,
1222 .drain = snd_pcm_rate_drain,
1223 .pause = snd_pcm_generic_pause,
1224 .rewind = snd_pcm_rate_rewind,
1225 .forward = snd_pcm_rate_forward,
1226 .resume = snd_pcm_generic_resume,
1227 .writei = snd_pcm_mmap_writei,
1228 .writen = snd_pcm_mmap_writen,
1229 .readi = snd_pcm_mmap_readi,
1230 .readn = snd_pcm_mmap_readn,
1231 .avail_update = snd_pcm_rate_avail_update,
1232 .mmap_commit = snd_pcm_rate_mmap_commit,
1233 .htimestamp = snd_pcm_rate_htimestamp,
1234 .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count,
1235 .poll_descriptors = snd_pcm_generic_poll_descriptors,
1236 .poll_revents = snd_pcm_rate_poll_revents,
1239 static const snd_pcm_ops_t snd_pcm_rate_ops = {
1240 .close = snd_pcm_rate_close,
1241 .info = snd_pcm_generic_info,
1242 .hw_refine = snd_pcm_rate_hw_refine,
1243 .hw_params = snd_pcm_rate_hw_params,
1244 .hw_free = snd_pcm_rate_hw_free,
1245 .sw_params = snd_pcm_rate_sw_params,
1246 .channel_info = snd_pcm_generic_channel_info,
1247 .dump = snd_pcm_rate_dump,
1248 .nonblock = snd_pcm_generic_nonblock,
1249 .async = snd_pcm_generic_async,
1250 .mmap = snd_pcm_generic_mmap,
1251 .munmap = snd_pcm_generic_munmap,
1255 * \brief Get a default converter string
1256 * \param root Root configuration node
1257 * \retval A const config item if found, or NULL
1259 const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root)
1261 snd_config_t *n;
1262 /* look for default definition */
1263 if (snd_config_search(root, "defaults.pcm.rate_converter", &n) >= 0)
1264 return n;
1265 return NULL;
1268 #ifdef PIC
1269 static int is_builtin_plugin(const char *type)
1271 return strcmp(type, "linear") == 0;
1274 static const char *const default_rate_plugins[] = {
1275 "speexrate", "linear", NULL
1278 static int rate_open_func(snd_pcm_rate_t *rate, const char *type, int verbose)
1280 char open_name[64], lib_name[128], *lib = NULL;
1281 snd_pcm_rate_open_func_t open_func;
1282 int err;
1284 snprintf(open_name, sizeof(open_name), "_snd_pcm_rate_%s_open", type);
1285 if (!is_builtin_plugin(type)) {
1286 snprintf(lib_name, sizeof(lib_name),
1287 "%s/libasound_module_rate_%s.so", ALSA_PLUGIN_DIR, type);
1288 lib = lib_name;
1290 open_func = snd_dlobj_cache_get(lib, open_name, NULL, verbose);
1291 if (!open_func)
1292 return -ENOENT;
1294 rate->open_func = open_func;
1295 rate->rate_min = SND_PCM_PLUGIN_RATE_MIN;
1296 rate->rate_max = SND_PCM_PLUGIN_RATE_MAX;
1297 rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION;
1299 err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1300 if (!err) {
1301 rate->plugin_version = rate->ops.version;
1302 if (rate->ops.get_supported_rates)
1303 rate->ops.get_supported_rates(rate->obj,
1304 &rate->rate_min,
1305 &rate->rate_max);
1306 return 0;
1309 /* try to open with the old protocol version */
1310 rate->plugin_version = SND_PCM_RATE_PLUGIN_VERSION_OLD;
1311 err = open_func(SND_PCM_RATE_PLUGIN_VERSION_OLD,
1312 &rate->obj, &rate->ops);
1313 if (err) {
1314 snd_dlobj_cache_put(open_func);
1315 rate->open_func = NULL;
1317 return err;
1319 #endif
1322 * \brief Creates a new rate PCM
1323 * \param pcmp Returns created PCM handle
1324 * \param name Name of PCM
1325 * \param sformat Slave format
1326 * \param srate Slave rate
1327 * \param converter SRC type string node
1328 * \param slave Slave PCM handle
1329 * \param close_slave When set, the slave PCM handle is closed with copy PCM
1330 * \retval zero on success otherwise a negative error code
1331 * \warning Using of this function might be dangerous in the sense
1332 * of compatibility reasons. The prototype might be freely
1333 * changed in future.
1335 int snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
1336 snd_pcm_format_t sformat, unsigned int srate,
1337 const snd_config_t *converter,
1338 snd_pcm_t *slave, int close_slave)
1340 snd_pcm_t *pcm;
1341 snd_pcm_rate_t *rate;
1342 const char *type = NULL;
1343 int err;
1344 #ifndef PIC
1345 snd_pcm_rate_open_func_t open_func;
1346 extern int SND_PCM_RATE_PLUGIN_ENTRY(linear) (unsigned int version, void **objp, snd_pcm_rate_ops_t *ops);
1347 #endif
1349 assert(pcmp && slave);
1350 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1351 snd_pcm_format_linear(sformat) != 1)
1352 return -EINVAL;
1353 rate = calloc(1, sizeof(snd_pcm_rate_t));
1354 if (!rate) {
1355 return -ENOMEM;
1357 rate->gen.slave = slave;
1358 rate->gen.close_slave = close_slave;
1359 rate->srate = srate;
1360 rate->sformat = sformat;
1361 snd_atomic_write_init(&rate->watom);
1363 err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
1364 if (err < 0) {
1365 free(rate);
1366 return err;
1369 #ifdef PIC
1370 err = -ENOENT;
1371 if (!converter) {
1372 const char *const *types;
1373 for (types = default_rate_plugins; *types; types++) {
1374 err = rate_open_func(rate, *types, 0);
1375 if (!err) {
1376 type = *types;
1377 break;
1380 } else if (!snd_config_get_string(converter, &type))
1381 err = rate_open_func(rate, type, 1);
1382 else if (snd_config_get_type(converter) == SND_CONFIG_TYPE_COMPOUND) {
1383 snd_config_iterator_t i, next;
1384 snd_config_for_each(i, next, converter) {
1385 snd_config_t *n = snd_config_iterator_entry(i);
1386 if (snd_config_get_string(n, &type) < 0)
1387 break;
1388 err = rate_open_func(rate, type, 0);
1389 if (!err)
1390 break;
1392 } else {
1393 SNDERR("Invalid type for rate converter");
1394 snd_pcm_close(pcm);
1395 free(rate);
1396 return -EINVAL;
1398 if (err < 0) {
1399 SNDERR("Cannot find rate converter");
1400 snd_pcm_close(pcm);
1401 free(rate);
1402 return -ENOENT;
1404 #else
1405 type = "linear";
1406 open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear);
1407 err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1408 if (err < 0) {
1409 snd_pcm_close(pcm);
1410 free(rate);
1411 return err;
1413 #endif
1415 if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16) ||
1416 ! rate->ops.input_frames || ! rate->ops.output_frames) {
1417 SNDERR("Inproper rate plugin %s initialization", type);
1418 snd_pcm_close(pcm);
1419 free(rate);
1420 return err;
1423 pcm->ops = &snd_pcm_rate_ops;
1424 pcm->fast_ops = &snd_pcm_rate_fast_ops;
1425 pcm->private_data = rate;
1426 pcm->poll_fd = slave->poll_fd;
1427 pcm->poll_events = slave->poll_events;
1428 pcm->mmap_rw = 1;
1429 pcm->monotonic = slave->monotonic;
1430 snd_pcm_set_hw_ptr(pcm, &rate->hw_ptr, -1, 0);
1431 snd_pcm_set_appl_ptr(pcm, &rate->appl_ptr, -1, 0);
1432 *pcmp = pcm;
1434 return 0;
1437 /*! \page pcm_plugins
1439 \section pcm_plugins_rate Plugin: Rate
1441 This plugin converts a stream rate. The input and output formats must be linear.
1443 \code
1444 pcm.name {
1445 type rate # Rate PCM
1446 slave STR # Slave name
1447 # or
1448 slave { # Slave definition
1449 pcm STR # Slave PCM name
1450 # or
1451 pcm { } # Slave PCM definition
1452 rate INT # Slave rate
1453 [format STR] # Slave format
1455 converter STR # optional
1456 # or
1457 converter [ STR1 STR2 ... ] # optional
1458 # Converter type, default is taken from
1459 # defaults.pcm.rate_converter
1461 \endcode
1463 \subsection pcm_plugins_rate_funcref Function reference
1465 <UL>
1466 <LI>snd_pcm_rate_open()
1467 <LI>_snd_pcm_rate_open()
1468 </UL>
1473 * \brief Creates a new rate PCM
1474 * \param pcmp Returns created PCM handle
1475 * \param name Name of PCM
1476 * \param root Root configuration node
1477 * \param conf Configuration node with rate PCM description
1478 * \param stream Stream type
1479 * \param mode Stream mode
1480 * \retval zero on success otherwise a negative error code
1481 * \warning Using of this function might be dangerous in the sense
1482 * of compatibility reasons. The prototype might be freely
1483 * changed in future.
1485 int _snd_pcm_rate_open(snd_pcm_t **pcmp, const char *name,
1486 snd_config_t *root, snd_config_t *conf,
1487 snd_pcm_stream_t stream, int mode)
1489 snd_config_iterator_t i, next;
1490 int err;
1491 snd_pcm_t *spcm;
1492 snd_config_t *slave = NULL, *sconf;
1493 snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1494 int srate = -1;
1495 const snd_config_t *converter = NULL;
1497 snd_config_for_each(i, next, conf) {
1498 snd_config_t *n = snd_config_iterator_entry(i);
1499 const char *id;
1500 if (snd_config_get_id(n, &id) < 0)
1501 continue;
1502 if (snd_pcm_conf_generic_id(id))
1503 continue;
1504 if (strcmp(id, "slave") == 0) {
1505 slave = n;
1506 continue;
1508 if (strcmp(id, "converter") == 0) {
1509 converter = n;
1510 continue;
1512 SNDERR("Unknown field %s", id);
1513 return -EINVAL;
1515 if (!slave) {
1516 SNDERR("slave is not defined");
1517 return -EINVAL;
1520 err = snd_pcm_slave_conf(root, slave, &sconf, 2,
1521 SND_PCM_HW_PARAM_FORMAT, 0, &sformat,
1522 SND_PCM_HW_PARAM_RATE, SCONF_MANDATORY, &srate);
1523 if (err < 0)
1524 return err;
1525 if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1526 snd_pcm_format_linear(sformat) != 1) {
1527 snd_config_delete(sconf);
1528 SNDERR("slave format is not linear");
1529 return -EINVAL;
1531 err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1532 snd_config_delete(sconf);
1533 if (err < 0)
1534 return err;
1535 err = snd_pcm_rate_open(pcmp, name, sformat, (unsigned int) srate,
1536 converter, spcm, 1);
1537 if (err < 0)
1538 snd_pcm_close(spcm);
1539 return err;
1541 #ifndef DOC_HIDDEN
1542 SND_DLSYM_BUILD_VERSION(_snd_pcm_rate_open, SND_PCM_DLSYM_VERSION);
1543 #endif