]> git.alsa-project.org Git - alsa-lib.git/blob - src/pcm/pcm_rate.c
d54be3c2250ee59ea2743707b96bfc0f06398bc0
[alsa-lib.git] / src / pcm / pcm_rate.c
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 *
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         if (width < 0 || width > 128)
92                 return NULL;
93         if (channels > 1024)
94                 return NULL;
95         if (frames > 10*1024*1024)
96                 return NULL;
97         ap = malloc(sizeof(*ap) * channels);
98         if (!ap)
99                 return NULL;
100         ap->addr = malloc(frames * channels * width / 8);
101         if (!ap->addr) {
102                 free(ap);
103                 return NULL;
104         }
105
106         /* set up in interleaved format */
107         for (i = 0; i < channels; i++) {
108                 ap[i].addr = ap[0].addr + (i * width) / 8;
109                 ap[i].first = 0;
110                 ap[i].step = width * channels;
111         }
112
113         return ap;
114 }
115
116 static void rate_free_tmp_buf(snd_pcm_channel_area_t **ptr)
117 {
118         snd_pcm_channel_area_t *c = *ptr;
119
120         if (c) {
121                 free(c->addr);
122                 free(c);
123                 *ptr = NULL;
124         }
125 }
126
127 static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
128 {
129         snd_pcm_rate_t *rate = pcm->private_data;
130         int err;
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,
134                                          &access_mask);
135         if (err < 0)
136                 return err;
137         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
138                                          &format_mask);
139         if (err < 0)
140                 return err;
141         err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
142         if (err < 0)
143                 return err;
144         if (rate->rate_min) {
145                 err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_RATE,
146                                                 rate->rate_min, 0);
147                 if (err < 0)
148                         return err;
149         }
150         if (rate->rate_max) {
151                 err = _snd_pcm_hw_param_set_max(params, SND_PCM_HW_PARAM_RATE,
152                                                 rate->rate_max, 0);
153                 if (err < 0)
154                         return err;
155         }
156         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
157         return 0;
158 }
159
160 static int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
161 {
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,
166                                    &saccess_mask);
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);
170         }
171         _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
172                                      rate->srate, 0, rate->srate + 1, -1);
173         return 0;
174 }
175
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)
178 {
179         snd_pcm_rate_t *rate = pcm->private_data;
180         snd_interval_t t, buffer_size;
181         const snd_interval_t *srate, *crate;
182         int err;
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);
197         if (err < 0)
198                 return err;
199         err = _snd_pcm_hw_params_refine(sparams, links, params);
200         if (err < 0)
201                 return err;
202         return 0;
203 }
204
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)
207 {
208         snd_pcm_rate_t *rate = pcm->private_data;
209         snd_interval_t t;
210 #ifdef DEBUG_REFINE
211         snd_output_t *out;
212 #endif
213         const snd_interval_t *sbuffer_size, *buffer_size;
214         const snd_interval_t *srate, *crate;
215         int err;
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);
230         if (err < 0)
231                 return err;
232         buffer_size = snd_pcm_hw_param_get_interval(params, SND_PCM_HW_PARAM_BUFFER_SIZE);
233         /*
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
238          */
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);
249                         }
250                 }
251         }
252 #ifdef DEBUG_REFINE
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);
259 #endif
260         err = _snd_pcm_hw_params_refine(params, links, sparams);
261 #ifdef DEBUG_REFINE
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);
269 #endif
270         if (err < 0)
271                 return err;
272         return 0;
273 }
274
275 static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm,
276                                   snd_pcm_hw_params_t *params)
277 {
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);
284 }
285
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)
288 {
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);
292         int best_score = -1;
293         int match = -1;
294         int f, score;
295
296         for (f = 0; f <= SND_PCM_FORMAT_LAST; f++) {
297                 if (!(mask & (1ULL << f)))
298                         continue;
299                 score = 0;
300                 if (snd_pcm_format_linear(f)) {
301                         if (snd_pcm_format_physical_width(f) == pwidth)
302                                 score++;
303                         if (snd_pcm_format_physical_width(f) >= pwidth)
304                                 score++;
305                         if (snd_pcm_format_width(f) == width)
306                                 score++;
307                         if (snd_pcm_format_signed(f) == signd)
308                                 score++;
309                 }
310                 if (score > best_score) {
311                         match = f;
312                         best_score = score;
313                 }
314         }
315
316         return match;
317 }
318
319 /* set up the input and output formats from the available lists */
320 static int choose_preferred_format(snd_pcm_rate_t *rate)
321 {
322         uint64_t in_mask = rate->in_formats;
323         uint64_t out_mask = rate->out_formats;
324         int in, out;
325
326         if (!in_mask || !out_mask)
327                 return 0;
328
329         if (rate->orig_in_format < 0 || rate->orig_in_format > 63)
330                 return -EINVAL;
331
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 */
335
336  repeat:
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)
340                 return -ENOENT;
341
342         if ((rate->format_flags & SND_PCM_RATE_FLAG_SYNC_FORMATS) &&
343             in != out) {
344                 if (out_mask & (1ULL << in))
345                         out = in;
346                 else if (in_mask & (1ULL << out))
347                         in = out;
348                 else {
349                         in_mask &= ~(1ULL << in);
350                         out_mask &= ~(1ULL << out);
351                         goto repeat;
352                 }
353         }
354
355         rate->info.in.format = in;
356         rate->info.out.format = out;
357         return 0;
358 }
359
360 static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
361 {
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);
372         if (err < 0)
373                 return err;
374
375         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
376                 cinfo = &rate->info.in;
377                 sinfo = &rate->info.out;
378         } else {
379                 sinfo = &rate->info.in;
380                 cinfo = &rate->info.out;
381         }
382         err = INTERNAL(snd_pcm_hw_params_get_format)(params, &cinfo->format);
383         if (err < 0)
384                 return err;
385         err = INTERNAL(snd_pcm_hw_params_get_rate)(params, &cinfo->rate, 0);
386         if (err < 0)
387                 return err;
388         err = INTERNAL(snd_pcm_hw_params_get_period_size)(params, &cinfo->period_size, 0);
389         if (err < 0)
390                 return err;
391         err = INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &cinfo->buffer_size);
392         if (err < 0)
393                 return err;
394         err = INTERNAL(snd_pcm_hw_params_get_channels)(params, &channels);
395         if (err < 0)
396                 return err;
397         err = INTERNAL(snd_pcm_hw_params_get_access)(params, &acc);
398         if (err < 0)
399                 return err;
400
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;
406
407         if (CHECK_SANITY(rate->pareas)) {
408                 snd_check(PCM, "rate plugin already in use");
409                 return -EBUSY;
410         }
411
412         rate->pareas = rate_alloc_tmp_buf(cinfo->format, channels,
413                                           cinfo->period_size);
414         rate->sareas = rate_alloc_tmp_buf(sinfo->format, channels,
415                                           sinfo->period_size);
416         if (!rate->pareas || !rate->sareas) {
417                 err = -ENOMEM;
418                 goto error_pareas;
419         }
420
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");
425                 err = -EINVAL;
426                 goto error_pareas;
427         }
428
429         err = rate->ops.init(rate->obj, &rate->info);
430         if (err < 0)
431                 goto error_init;
432
433         rate_free_tmp_buf(&rate->src_buf);
434         rate_free_tmp_buf(&rate->dst_buf);
435
436         need_src_buf = need_dst_buf = 0;
437
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;
442         } else {
443                 if (rate->orig_in_format != rate->info.in.format)
444                         need_src_buf = 1;
445                 if (rate->orig_out_format != rate->info.out.format)
446                         need_dst_buf = 1;
447         }
448
449         if (need_src_buf) {
450                 rate->src_conv_idx =
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) {
456                         err = -ENOMEM;
457                         goto error;
458                 }
459         }
460
461         if (need_dst_buf) {
462                 rate->dst_conv_idx =
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) {
468                         err = -ENOMEM;
469                         goto error;
470                 }
471         }
472
473         return 0;
474
475  error:
476         rate_free_tmp_buf(&rate->src_buf);
477         rate_free_tmp_buf(&rate->dst_buf);
478  error_init:
479         if (rate->ops.free)
480                 rate->ops.free(rate->obj);
481  error_pareas:
482         rate_free_tmp_buf(&rate->pareas);
483         rate_free_tmp_buf(&rate->sareas);
484         return err;
485 }
486
487 static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
488 {
489         snd_pcm_rate_t *rate = pcm->private_data;
490
491         rate_free_tmp_buf(&rate->pareas);
492         rate_free_tmp_buf(&rate->sareas);
493         if (rate->ops.free)
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);
498 }
499
500 static void recalc(snd_pcm_t *pcm, snd_pcm_uframes_t *val)
501 {
502         snd_pcm_rate_t *rate = pcm->private_data;
503         snd_pcm_t *slave = rate->gen.slave;
504         unsigned long div;
505
506         if (*val == pcm->buffer_size) {
507                 *val = slave->buffer_size;
508         } else {
509                 div = *val / pcm->period_size;
510                 if (div * pcm->period_size == *val)
511                         *val = div * slave->period_size;
512                 else
513                         *val = muldiv_near(*val, slave->period_size, pcm->period_size);
514         }
515 }
516
517 static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
518 {
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;
523         int err;
524
525         sparams = &rate->sw_params;
526         err = snd_pcm_sw_params_current(slave, sparams);
527         if (err < 0)
528                 return err;
529         sboundary = sparams->boundary;
530         *sparams = *params;
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) {
535                 boundary1 *= 2;
536                 boundary2 *= 2;
537         }
538         params->boundary = boundary1;
539         sparams->boundary = sboundary;
540
541         if (rate->ops.adjust_pitch)
542                 rate->ops.adjust_pitch(rate->obj, &rate->info);
543
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;
551         }
552         if (sparams->stop_threshold >= params->boundary) {
553                 sparams->stop_threshold = sparams->boundary;
554         } else {
555                 recalc(pcm, &sparams->stop_threshold);
556         }
557         recalc(pcm, &sparams->silence_threshold);
558         if (sparams->silence_size >= params->boundary) {
559                 sparams->silence_size = sparams->boundary;
560         } else {
561                 recalc(pcm, &sparams->silence_size);
562         }
563         return snd_pcm_sw_params(slave, sparams);
564 }
565
566 static int snd_pcm_rate_init(snd_pcm_t *pcm)
567 {
568         snd_pcm_rate_t *rate = pcm->private_data;
569
570         if (rate->ops.reset)
571                 rate->ops.reset(rate->obj);
572         rate->last_commit_ptr = 0;
573         rate->start_pending = 0;
574         return 0;
575 }
576
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)
583 {
584         const snd_pcm_channel_area_t *out_areas;
585         snd_pcm_uframes_t out_offset;
586
587         if (rate->dst_buf) {
588                 out_areas = rate->dst_buf;
589                 out_offset = 0;
590         } else {
591                 out_areas = dst_areas;
592                 out_offset = dst_offset;
593         }
594
595         if (rate->src_buf) {
596                 snd_pcm_linear_convert(rate->src_buf, 0,
597                                        src_areas, src_offset,
598                                        channels, src_frames,
599                                        rate->src_conv_idx);
600                 src_areas = rate->src_buf;
601                 src_offset = 0;
602         }
603
604         if (rate->ops.convert)
605                 rate->ops.convert(rate->obj, out_areas, out_offset, dst_frames,
606                                    src_areas, src_offset, src_frames);
607         else
608                 rate->ops.convert_s16(rate->obj,
609                                       snd_pcm_channel_area_addr(out_areas, out_offset),
610                                       dst_frames,
611                                       snd_pcm_channel_area_addr(src_areas, src_offset),
612                                       src_frames);
613         if (rate->dst_buf)
614                 snd_pcm_linear_convert(dst_areas, dst_offset,
615                                        rate->dst_buf, 0,
616                                        channels, dst_frames,
617                                        rate->dst_conv_idx);
618 }
619
620 static inline void
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)
626 {
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);
631 }
632
633 static inline void
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)
639 {
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);
644 }
645
646 static inline void snd_pcm_rate_sync_hwptr0(snd_pcm_t *pcm, snd_pcm_uframes_t slave_hw_ptr)
647 {
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;
651
652         if (pcm->stream != SND_PCM_STREAM_PLAYBACK)
653                 return;
654
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)
658                 return;
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 ]
668          */
669         rate->hw_ptr += (
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;
674
675         rate->hw_ptr %= pcm->boundary;
676 }
677
678 static inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm)
679 {
680         snd_pcm_rate_t *rate = pcm->private_data;
681         snd_pcm_rate_sync_hwptr0(pcm, *rate->gen.slave->hw.ptr);
682 }
683
684 static int snd_pcm_rate_hwsync(snd_pcm_t *pcm)
685 {
686         snd_pcm_rate_t *rate = pcm->private_data;
687         int err = snd_pcm_hwsync(rate->gen.slave);
688         if (err < 0)
689                 return err;
690         snd_pcm_rate_sync_hwptr(pcm);
691         return 0;
692 }
693
694 static snd_pcm_uframes_t snd_pcm_rate_playback_internal_delay(snd_pcm_t *pcm)
695 {
696         snd_pcm_rate_t *rate = pcm->private_data;
697
698         return pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
699 }
700
701 static int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
702 {
703         snd_pcm_rate_t *rate = pcm->private_data;
704         snd_pcm_sframes_t slave_delay;
705         int err;
706
707         snd_pcm_rate_hwsync(pcm);
708
709         err = snd_pcm_delay(rate->gen.slave, &slave_delay);
710         if (err < 0) {
711                 return err;
712         }
713
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);
717         } else {
718                 *delayp = rate->ops.output_frames(rate->obj, slave_delay)
719                                 + snd_pcm_mmap_capture_delay(pcm);
720         }
721         return 0;
722 }
723
724 static int snd_pcm_rate_prepare(snd_pcm_t *pcm)
725 {
726         snd_pcm_rate_t *rate = pcm->private_data;
727         int err;
728
729         err = snd_pcm_prepare(rate->gen.slave);
730         if (err < 0)
731                 return err;
732         *pcm->hw.ptr = 0;
733         *pcm->appl.ptr = 0;
734         rate->last_slave_hw_ptr = 0;
735         err = snd_pcm_rate_init(pcm);
736         if (err < 0)
737                 return err;
738         return 0;
739 }
740
741 static int snd_pcm_rate_reset(snd_pcm_t *pcm)
742 {
743         snd_pcm_rate_t *rate = pcm->private_data;
744         int err;
745         err = snd_pcm_reset(rate->gen.slave);
746         if (err < 0)
747                 return err;
748         *pcm->hw.ptr = 0;
749         *pcm->appl.ptr = 0;
750         rate->last_slave_hw_ptr = 0;
751         err = snd_pcm_rate_init(pcm);
752         if (err < 0)
753                 return err;
754         return 0;
755 }
756
757 static snd_pcm_sframes_t snd_pcm_rate_rewindable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
758 {
759         return 0;
760 }
761
762 static snd_pcm_sframes_t snd_pcm_rate_forwardable(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
763 {
764         return 0;
765 }
766
767 static snd_pcm_sframes_t snd_pcm_rate_rewind(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
768                                              snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
769 {
770         return 0;
771 }
772
773 static snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
774                                               snd_pcm_uframes_t frames ATTRIBUTE_UNUSED)
775 {
776         return 0;
777 }
778
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)
783 {
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;
790
791         areas = snd_pcm_mmap_areas(pcm);
792         /*
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.
795          */
796         if (cont >= pcm->period_size) {
797                 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
798                 if (result < 0)
799                         return result;
800                 /*
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.
803                  */
804                 if (slave_frames < rate->gen.slave->period_size) {
805                         snd_pcm_rate_write_areas1(pcm, areas, appl_offset, rate->sareas, 0);
806                         goto __partial;
807                 }
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) {
813                         if (result < 0)
814                                 return result;
815                         result = snd_pcm_rewind(rate->gen.slave, result);
816                         if (result < 0)
817                                 return result;
818                         return 0;
819                 }
820         } else {
821                 snd_pcm_areas_copy(rate->pareas, 0,
822                                    areas, appl_offset,
823                                    pcm->channels, cont,
824                                    pcm->format);
825                 snd_pcm_areas_copy(rate->pareas, cont,
826                                    areas, 0,
827                                    pcm->channels, pcm->period_size - cont,
828                                    pcm->format);
829
830                 snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0);
831
832                 /* ok, commit first fragment */
833                 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
834                 if (result < 0)
835                         return result;
836               __partial:
837                 cont = slave_frames;
838                 if (cont > slave_size)
839                         cont = slave_size;
840                 snd_pcm_areas_copy(slave_areas, slave_offset,
841                                    rate->sareas, 0,
842                                    pcm->channels, cont,
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) {
846                         if (result < 0)
847                                 return result;
848                         result = snd_pcm_rewind(rate->gen.slave, result);
849                         if (result < 0)
850                                 return result;
851                         return 0;
852                 }
853                 xfer = cont;
854
855                 if (xfer == slave_size)
856                         goto commit_done;
857
858                 /* commit second fragment */
859                 cont = slave_size - cont;
860                 slave_frames = cont;
861                 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
862                 if (result < 0)
863                         return result;
864 #if 0
865                 if (slave_offset) {
866                         snd_error(PCM, "non-zero slave_offset %ld", slave_offset);
867                         return -EIO;
868                 }
869 #endif
870                 snd_pcm_areas_copy(slave_areas, slave_offset,
871                                    rate->sareas, xfer,
872                                    pcm->channels, cont,
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) {
876                         if (result < 0)
877                                 return result;
878                         result = snd_pcm_rewind(rate->gen.slave, result + xfer);
879                         if (result < 0)
880                                 return result;
881                         return 0;
882                 }
883         }
884
885  commit_done:
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;
890         }
891         return 1;
892 }
893
894 static int snd_pcm_rate_commit_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t appl_offset)
895 {
896         snd_pcm_rate_t *rate = pcm->private_data;
897
898         return snd_pcm_rate_commit_area(pcm, rate, appl_offset, pcm->period_size,
899                                         rate->gen.slave->period_size);
900 }
901
902 static int snd_pcm_rate_grab_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t hw_offset)
903 {
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;
911
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);
915                 if (result < 0)
916                         return result;
917                 if (slave_frames < rate->gen.slave->period_size)
918                         goto __partial;
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) {
923                         if (result < 0)
924                                 return result;
925                         result = snd_pcm_rewind(rate->gen.slave, result);
926                         if (result < 0)
927                                 return result;
928                         return 0;
929                 }
930         } else {
931                 /* ok, grab first fragment */
932                 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
933                 if (result < 0)
934                         return result;
935               __partial:
936                 cont = 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,
941                                    pcm->channels, cont,
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) {
945                         if (result < 0)
946                                 return result;
947                         result = snd_pcm_rewind(rate->gen.slave, result);
948                         if (result < 0)
949                                 return result;
950                         return 0;
951                 }
952                 xfer = cont;
953
954                 if (xfer == rate->gen.slave->period_size)
955                         goto __transfer;
956
957                 /* grab second fragment */
958                 cont = rate->gen.slave->period_size - cont;
959                 slave_frames = cont;
960                 result = snd_pcm_mmap_begin(rate->gen.slave, &slave_areas, &slave_offset, &slave_frames);
961                 if (result < 0)
962                         return result;
963 #if 0
964                 if (slave_offset) {
965                         snd_error(PCM, "non-zero slave_offset %ld", slave_offset);
966                         return -EIO;
967                 }
968 #endif
969                 snd_pcm_areas_copy(rate->sareas, xfer,
970                                    slave_areas, slave_offset,
971                                    pcm->channels, cont,
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) {
975                         if (result < 0)
976                                 return result;
977                         result = snd_pcm_rewind(rate->gen.slave, result + xfer);
978                         if (result < 0)
979                                 return result;
980                         return 0;
981                 }
982
983               __transfer:
984                 cont = pcm->buffer_size - hw_offset;
985                 if (cont >= pcm->period_size) {
986                         snd_pcm_rate_read_areas1(pcm, areas, hw_offset,
987                                                  rate->sareas, 0);
988                 } else {
989                         snd_pcm_rate_read_areas1(pcm,
990                                                  rate->pareas, 0,
991                                                  rate->sareas, 0);
992                         snd_pcm_areas_copy(areas, hw_offset,
993                                            rate->pareas, 0,
994                                            pcm->channels, cont,
995                                            pcm->format);
996                         snd_pcm_areas_copy(areas, 0,
997                                            rate->pareas, cont,
998                                            pcm->channels, pcm->period_size - cont,
999                                            pcm->format);
1000                 }
1001         }
1002         return 1;
1003 }
1004
1005 static int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t appl_ptr)
1006 {
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;
1011         int err;
1012
1013         slave_size = snd_pcm_avail_update(slave);
1014         if (slave_size < 0)
1015                 return slave_size;
1016
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);
1021                 if (err == 0)
1022                         break;
1023                 if (err < 0)
1024                         return err;
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;
1030         }
1031         return 0;
1032 }
1033
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)
1037 {
1038         snd_pcm_rate_t *rate = pcm->private_data;
1039         int err;
1040
1041         if (size == 0)
1042                 return 0;
1043         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
1044                 err = snd_pcm_rate_sync_playback_area(pcm, rate->appl_ptr + size);
1045                 if (err < 0)
1046                         return err;
1047         }
1048         snd_pcm_mmap_appl_forward(pcm, size);
1049         return size;
1050 }
1051
1052 static snd_pcm_sframes_t snd_pcm_rate_avail_update_capture(snd_pcm_t *pcm,
1053                                                            snd_pcm_sframes_t slave_size)
1054 {
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;
1058
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);
1065                 if (err < 0)
1066                         return err;
1067                 if (err == 0)
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);
1075         }
1076         return (snd_pcm_sframes_t)xfer;
1077 }
1078
1079 static snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm)
1080 {
1081         snd_pcm_rate_t *rate = pcm->private_data;
1082         snd_pcm_sframes_t slave_size;
1083
1084         slave_size = snd_pcm_avail_update(rate->gen.slave);
1085         if (slave_size < 0)
1086                 return slave_size;
1087
1088         if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1089                 return snd_pcm_rate_avail_update_capture(pcm, slave_size);
1090
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);
1094 }
1095
1096 static int snd_pcm_rate_htimestamp(snd_pcm_t *pcm,
1097                                    snd_pcm_uframes_t *avail,
1098                                    snd_htimestamp_t *tstamp)
1099 {
1100         snd_pcm_rate_t *rate = pcm->private_data;
1101         snd_pcm_sframes_t avail1;
1102         snd_pcm_uframes_t tmp;
1103         int ok = 0, err;
1104
1105         while (1) {
1106                 /* the position is from this plugin itself */
1107                 avail1 = snd_pcm_avail_update(pcm);
1108                 if (avail1 < 0)
1109                         return avail1;
1110                 if (ok && (snd_pcm_uframes_t)avail1 == *avail)
1111                         break;
1112                 *avail = avail1;
1113                 /* timestamp is taken from the slave PCM */
1114                 err = snd_pcm_htimestamp(rate->gen.slave, &tmp, tstamp);
1115                 if (err < 0)
1116                         return err;
1117                 ok = 1;
1118         }
1119         return 0;
1120 }
1121
1122 static int snd_pcm_rate_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
1123 {
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);
1129         }
1130         return snd_pcm_poll_descriptors_revents(rate->gen.slave, pfds, nfds, revents);
1131 }
1132
1133 /* locking */
1134 static int snd_pcm_rate_drain(snd_pcm_t *pcm)
1135 {
1136         snd_pcm_rate_t *rate = pcm->private_data;
1137
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;
1142                 int commit_err = 0;
1143
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);
1150
1151                 size = pcm_frame_diff(rate->appl_ptr, rate->last_commit_ptr, pcm->boundary);
1152                 ofs = rate->last_commit_ptr % pcm->buffer_size;
1153                 while (size > 0) {
1154                         snd_pcm_uframes_t psize, spsize;
1155                         int err;
1156
1157                         err = __snd_pcm_wait_in_lock(rate->gen.slave, SND_PCM_WAIT_DRAIN);
1158                         if (err < 0)
1159                                 break;
1160                         if (size > pcm->period_size) {
1161                                 psize = pcm->period_size;
1162                                 spsize = rate->gen.slave->period_size;
1163                         } else {
1164                                 psize = size;
1165                                 spsize = rate->ops.output_frames(rate->obj, size);
1166                                 if (! spsize)
1167                                         break;
1168                         }
1169                         commit_err = snd_pcm_rate_commit_area(pcm, rate, ofs,
1170                                                  psize, spsize);
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;
1178                                         break;
1179                                 }
1180                                 continue;
1181                         } else
1182                                 break;
1183
1184                         ofs = (ofs + psize) % pcm->buffer_size;
1185                         size -= psize;
1186                 }
1187                 sw_params.avail_min = saved_avail_min;
1188                 snd_pcm_sw_params(rate->gen.slave, &sw_params);
1189                 __snd_pcm_unlock(pcm);
1190                 if (commit_err < 0)
1191                         return commit_err;
1192         }
1193         return snd_pcm_drain(rate->gen.slave);
1194 }
1195
1196 static snd_pcm_state_t snd_pcm_rate_state(snd_pcm_t *pcm)
1197 {
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);
1202 }
1203
1204
1205 static int snd_pcm_rate_start(snd_pcm_t *pcm)
1206 {
1207         snd_pcm_rate_t *rate = pcm->private_data;
1208         snd_pcm_sframes_t avail;
1209
1210         if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1211                 return snd_pcm_start(rate->gen.slave);
1212
1213         if (snd_pcm_state(rate->gen.slave) != SND_PCM_STATE_PREPARED)
1214                 return -EBADFD;
1215
1216         gettimestamp(&rate->trigger_tstamp, pcm->tstamp_type);
1217
1218         avail = snd_pcm_mmap_playback_hw_avail(rate->gen.slave);
1219         if (avail < 0) /* can't happen on healthy drivers */
1220                 return -EBADFD;
1221
1222         if (avail == 0) {
1223                 /* postpone the trigger since we have no data committed yet */
1224                 rate->start_pending = 1;
1225                 return 0;
1226         }
1227         rate->start_pending = 0;
1228         return snd_pcm_start(rate->gen.slave);
1229 }
1230
1231 static int snd_pcm_rate_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
1232 {
1233         snd_pcm_rate_t *rate = pcm->private_data;
1234         snd_pcm_sframes_t err;
1235
1236         err = snd_pcm_status(rate->gen.slave, status);
1237         if (err < 0)
1238                 return err;
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;
1243         }
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);
1252         } else {
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);
1257         }
1258         return 0;
1259 }
1260
1261 static void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
1262 {
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",
1266                         rate->srate);
1267         else
1268                 snd_output_printf(out, "Rate conversion PCM (%d, sformat=%s)\n",
1269                         rate->srate,
1270                         snd_pcm_format_name(rate->sformat));
1271         if (rate->ops.dump)
1272                 rate->ops.dump(rate->obj, out);
1273         snd_output_printf(out, "Protocol version: %x\n", rate->plugin_version);
1274         if (pcm->setup) {
1275                 snd_output_printf(out, "Its setup is:\n");
1276                 snd_pcm_dump_setup(pcm, out);
1277         }
1278         snd_output_printf(out, "Slave: ");
1279         snd_pcm_dump(rate->gen.slave, out);
1280 }
1281
1282 static int snd_pcm_rate_close(snd_pcm_t *pcm)
1283 {
1284         snd_pcm_rate_t *rate = pcm->private_data;
1285
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);
1291 }
1292
1293 /**
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
1298 */
1299 static snd_pcm_uframes_t snd_pcm_rate_slave_frames(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
1300 {
1301         snd_pcm_uframes_t sframes;
1302         snd_pcm_rate_t *rate = pcm->private_data;
1303
1304         if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
1305                 sframes = rate->ops.output_frames(rate->obj, frames);
1306         else
1307                 sframes = rate->ops.input_frames(rate->obj, frames);
1308
1309         return sframes;
1310 }
1311
1312 static int snd_pcm_rate_may_wait_for_avail_min(snd_pcm_t *pcm,
1313                                                snd_pcm_uframes_t avail)
1314 {
1315         return snd_pcm_plugin_may_wait_for_avail_min_conv(pcm, avail,
1316                                                           snd_pcm_rate_slave_frames);
1317 }
1318
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,
1346 };
1347
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,
1364 };
1365
1366 /**
1367  * \brief Get a default converter string
1368  * \param root Root configuration node
1369  * \retval A const config item if found, or NULL
1370  */
1371 const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root)
1372 {
1373         snd_config_t *n;
1374         /* look for default definition */
1375         if (snd_config_search(root, "defaults.pcm.rate_converter", &n) >= 0)
1376                 return n;
1377         return NULL;
1378 }
1379
1380 static void rate_initial_setup(snd_pcm_rate_t *rate)
1381 {
1382         if (rate->plugin_version == SND_PCM_RATE_PLUGIN_VERSION)
1383                 rate->plugin_version = rate->ops.version;
1384
1385         if (rate->plugin_version >= 0x010002 &&
1386             rate->ops.get_supported_rates)
1387                 rate->ops.get_supported_rates(rate->obj,
1388                                               &rate->rate_min,
1389                                               &rate->rate_max);
1390
1391         if (rate->plugin_version >= 0x010003 &&
1392             rate->ops.get_supported_formats) {
1393                 rate->ops.get_supported_formats(rate->obj,
1394                                                 &rate->in_formats,
1395                                                 &rate->out_formats,
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;
1401         }
1402 }
1403
1404 #ifdef PIC
1405 static int is_builtin_plugin(const char *type)
1406 {
1407         return strcmp(type, "linear") == 0;
1408 }
1409
1410 static const char *const default_rate_plugins[] = {
1411         "speexrate", "linear", NULL
1412 };
1413
1414 static int rate_open_func(snd_pcm_rate_t *rate, const char *type, const snd_config_t *converter_conf, int verbose)
1415 {
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;
1419         int err;
1420
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);
1426                 lib = lib_name;
1427         }
1428
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);
1433                 if (!err) {
1434                         rate->open_func = open_conf_func;
1435                         return 0;
1436                 } else {
1437                         snd_dlobj_cache_put(open_conf_func);
1438                         return err;
1439                 }
1440         }
1441
1442         open_func = snd_dlobj_cache_get(lib, open_name, NULL, verbose);
1443         if (!open_func)
1444                 return -ENOENT;
1445
1446         rate->open_func = open_func;
1447
1448         err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1449         if (!err)
1450                 return 0;
1451
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);
1456         if (!err)
1457                 return 0;
1458
1459         snd_dlobj_cache_put(open_func);
1460         rate->open_func = NULL;
1461         return err;
1462 }
1463 #endif
1464
1465 /*
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
1468  * a structure.
1469  */
1470 static int is_string_array(const snd_config_t *conf)
1471 {
1472         snd_config_iterator_t i;
1473
1474         if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND)
1475                 return 0;
1476
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);
1480                 const char *id;
1481                 if (snd_config_get_id(n, &id) < 0)
1482                         return 0;
1483                 if (id && strcmp(id, "0") != 0)
1484                         return 0;
1485         }
1486
1487         return 1;
1488 }
1489
1490 /**
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.
1503  */
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)
1508 {
1509         snd_pcm_t *pcm;
1510         snd_pcm_rate_t *rate;
1511         const char *type = NULL;
1512         int err;
1513 #ifndef PIC
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);
1516 #endif
1517
1518         assert(pcmp && slave);
1519         if (sformat != SND_PCM_FORMAT_UNKNOWN &&
1520             snd_pcm_format_linear(sformat) != 1)
1521                 return -EINVAL;
1522         rate = calloc(1, sizeof(snd_pcm_rate_t));
1523         if (!rate) {
1524                 return -ENOMEM;
1525         }
1526         rate->gen.slave = slave;
1527         rate->gen.close_slave = close_slave;
1528         rate->srate = srate;
1529         rate->sformat = sformat;
1530
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;
1534
1535         err = snd_pcm_new(&pcm, SND_PCM_TYPE_RATE, name, slave->stream, slave->mode);
1536         if (err < 0) {
1537                 free(rate);
1538                 return err;
1539         }
1540
1541 #ifdef PIC
1542         err = -ENOENT;
1543         if (!converter) {
1544                 const char *const *types;
1545                 for (types = default_rate_plugins; *types; types++) {
1546                         err = rate_open_func(rate, *types, NULL, 0);
1547                         if (!err) {
1548                                 type = *types;
1549                                 break;
1550                         }
1551                 }
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)
1559                                 break;
1560                         err = rate_open_func(rate, type, NULL, 0);
1561                         if (!err)
1562                                 break;
1563                 }
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);
1568                         const char *id;
1569                         if (snd_config_get_id(n, &id) < 0)
1570                                 continue;
1571                         if (strcmp(id, "name") != 0)
1572                                 continue;
1573                         err = snd_config_get_string(n, &type);
1574                         if (err < 0) {
1575                                 snd_pcm_free(pcm);
1576                                 free(rate);
1577                                 return err;
1578                         }
1579                         break;
1580                 }
1581                 if (!type) {
1582                         snd_error(PCM, "No name given for rate converter");
1583                         snd_pcm_free(pcm);
1584                         free(rate);
1585                         return -EINVAL;
1586                 }
1587                 err = rate_open_func(rate, type, converter, 1);
1588         } else {
1589                 snd_error(PCM, "Invalid type for rate converter");
1590                 snd_pcm_free(pcm);
1591                 free(rate);
1592                 return -EINVAL;
1593         }
1594         if (err < 0) {
1595                 snd_error(PCM, "Cannot find rate converter");
1596                 snd_pcm_free(pcm);
1597                 free(rate);
1598                 return -ENOENT;
1599         }
1600 #else
1601         type = "linear";
1602         open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear);
1603         err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops);
1604         if (err < 0) {
1605                 snd_pcm_free(pcm);
1606                 free(rate);
1607                 return err;
1608         }
1609 #endif
1610
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);
1614                 snd_pcm_free(pcm);
1615                 free(rate);
1616                 return err;
1617         }
1618
1619         rate_initial_setup(rate);
1620
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;
1626         pcm->mmap_rw = 1;
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);
1630         *pcmp = pcm;
1631
1632         return 0;
1633 }
1634
1635 /*! \page pcm_plugins
1636
1637 \section pcm_plugins_rate Plugin: Rate
1638
1639 This plugin converts a stream rate. The input and output formats must be linear.
1640
1641 \code
1642 pcm.name {
1643         type rate               # Rate PCM
1644         slave STR               # Slave name
1645         # or
1646         slave {                 # Slave definition
1647                 pcm STR         # Slave PCM name
1648                 # or
1649                 pcm { }         # Slave PCM definition
1650                 rate INT        # Slave rate
1651                 [format STR]    # Slave format
1652         }
1653         converter STR                   # optional
1654         # or
1655         converter [ STR1 STR2 ... ]     # optional
1656                                 # Converter type, default is taken from
1657                                 # defaults.pcm.rate_converter
1658         # or
1659         converter {             # optional
1660                 name STR        # Convertor type
1661                 xxx yyy         # optional convertor-specific configuration
1662         }
1663 }
1664 \endcode
1665
1666 \subsection pcm_plugins_rate_funcref Function reference
1667
1668 <UL>
1669   <LI>snd_pcm_rate_open()
1670   <LI>_snd_pcm_rate_open()
1671 </UL>
1672
1673 */
1674
1675 /**
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.
1687  */
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)
1691 {
1692         snd_config_iterator_t i, next;
1693         int err;
1694         snd_pcm_t *spcm;
1695         snd_config_t *slave = NULL, *sconf;
1696         snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
1697         int srate = -1;
1698         const snd_config_t *converter = NULL;
1699
1700         snd_config_for_each(i, next, conf) {
1701                 snd_config_t *n = snd_config_iterator_entry(i);
1702                 const char *id;
1703                 if (snd_config_get_id(n, &id) < 0)
1704                         continue;
1705                 if (snd_pcm_conf_generic_id(id))
1706                         continue;
1707                 if (strcmp(id, "slave") == 0) {
1708                         slave = n;
1709                         continue;
1710                 }
1711                 if (strcmp(id, "converter") == 0) {
1712                         converter = n;
1713                         continue;
1714                 }
1715                 snd_error(PCM, "Unknown field %s", id);
1716                 return -EINVAL;
1717         }
1718         if (!slave) {
1719                 snd_error(PCM, "slave is not defined");
1720                 return -EINVAL;
1721         }
1722
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);
1726         if (err < 0)
1727                 return err;
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");
1732                 return -EINVAL;
1733         }
1734         err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
1735         snd_config_delete(sconf);
1736         if (err < 0)
1737                 return err;
1738         err = snd_pcm_rate_open(pcmp, name, sformat, (unsigned int) srate,
1739                                 converter, spcm, 1);
1740         if (err < 0)
1741                 snd_pcm_close(spcm);
1742         return err;
1743 }
1744 #ifndef DOC_HIDDEN
1745 SND_DLSYM_BUILD_VERSION(_snd_pcm_rate_open, SND_PCM_DLSYM_VERSION);
1746 #endif