tizen 2.3.1 release
[external/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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
28  *
29  */
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"
36
37 #include "plugin_ops.h"
38
39 #if 0
40 #define DEBUG_REFINE
41 #endif
42
43 #ifndef PIC
44 /* entry for static linking */
45 const char *_snd_module_pcm_rate = "";
46 #endif
47
48 #ifndef DOC_HIDDEN
49
50 typedef struct _snd_pcm_rate snd_pcm_rate_t;
51
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;
75 };
76
77 #define SND_PCM_RATE_PLUGIN_VERSION_OLD 0x010001        /* old rate plugin */
78
79 #endif /* DOC_HIDDEN */
80
81 static int snd_pcm_rate_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params)
82 {
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;
103         }
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;
109         }
110         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
111         return 0;
112 }
113
114 static int snd_pcm_rate_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
115 {
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);
124         }
125         _snd_pcm_hw_param_set_minmax(sparams, SND_PCM_HW_PARAM_RATE,
126                                      rate->srate, 0, rate->srate + 1, -1);
127         return 0;
128 }
129
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)
132 {
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;
157 }
158         
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)
161 {
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);
187         /*
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
192          */
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);
203                         }
204                 }
205         }
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;
227 }
228
229 static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm, 
230                                   snd_pcm_hw_params_t *params)
231 {
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);
238 }
239
240 static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
241 {
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;
253
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;
260         }
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;
276
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;
282
283         if (CHECK_SANITY(rate->pareas)) {
284                 SNDMSG("rate plugin already in use");
285                 return -EBUSY;
286         }
287         err = rate->ops.init(rate->obj, &rate->info);
288         if (err < 0)
289                 return err;
290
291         rate->pareas = malloc(2 * channels * sizeof(*rate->pareas));
292         if (rate->pareas == NULL)
293                 goto error;
294
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;
301
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;
311         }
312
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;
322         }
323
324         return 0;
325
326  error:
327         if (rate->pareas) {
328                 free(rate->pareas[0].addr);
329                 free(rate->pareas);
330                 rate->pareas = NULL;
331         }
332         if (rate->ops.free)
333                 rate->ops.free(rate->obj);
334         return -ENOMEM;
335 }
336
337 static int snd_pcm_rate_hw_free(snd_pcm_t *pcm)
338 {
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;
345         }
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);
352 }
353
354 static void recalc(snd_pcm_t *pcm, snd_pcm_uframes_t *val)
355 {
356         snd_pcm_rate_t *rate = pcm->private_data;
357         snd_pcm_t *slave = rate->gen.slave;
358         unsigned long div;
359
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);
368         }
369 }
370
371 static int snd_pcm_rate_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
372 {
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;
378
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;
391         }
392         params->boundary = boundary1;
393         sparams->boundary = sboundary;
394
395         if (rate->ops.adjust_pitch)
396                 rate->ops.adjust_pitch(rate->obj, &rate->info);
397
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;
405         }
406         if (sparams->stop_threshold >= params->boundary) {
407                 sparams->stop_threshold = sparams->boundary;
408         } else {
409                 recalc(pcm, &sparams->stop_threshold);
410         }
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);
416         }
417         return snd_pcm_sw_params(slave, sparams);
418 }
419
420 static int snd_pcm_rate_init(snd_pcm_t *pcm)
421 {
422         snd_pcm_rate_t *rate = pcm->private_data;
423
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;
429 }
430
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)
435 {
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;
447
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);
451         }
452
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];
465                 }
466         }
467 }
468
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)
473 {
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;
485
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);
489         }
490
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];
503                 }
504         }
505 }
506
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)
513 {
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;
523                 }
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);
535         }
536 }
537
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)
544 {
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);
549 }
550
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)
557 {
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);
562 }
563
564 static inline snd_pcm_sframes_t snd_pcm_rate_move_applptr(snd_pcm_t *pcm, snd_pcm_sframes_t frames)
565 {
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;
570
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);
583         }
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);
590         }
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);
601
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);
607         }
608         if (frames < 0)
609                 diff = -diff;
610
611         rate->last_commit_ptr = rate->appl_ptr - rate->appl_ptr % pcm->period_size;
612
613         return diff;
614 }
615
616 static inline void snd_pcm_rate_sync_hwptr(snd_pcm_t *pcm)
617 {
618         snd_pcm_rate_t *rate = pcm->private_data;
619         snd_pcm_uframes_t slave_hw_ptr = *rate->gen.slave->hw.ptr;
620
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... 
625          */
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);
629 }
630
631 static int snd_pcm_rate_hwsync(snd_pcm_t *pcm)
632 {
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;
641 }
642
643 static int snd_pcm_rate_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
644 {
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;
651 }
652
653 static int snd_pcm_rate_prepare(snd_pcm_t *pcm)
654 {
655         snd_pcm_rate_t *rate = pcm->private_data;
656         int err;
657
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;
663         }
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;
671 }
672
673 static int snd_pcm_rate_reset(snd_pcm_t *pcm)
674 {
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;
682         }
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;
690 }
691
692 static snd_pcm_sframes_t snd_pcm_rate_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
693 {
694         snd_pcm_rate_t *rate = pcm->private_data;
695         snd_pcm_sframes_t n = snd_pcm_mmap_hw_avail(pcm);
696
697         if ((snd_pcm_uframes_t)n > frames)
698                 frames = n;
699         if (frames == 0)
700                 return 0;
701         
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;
706 }
707
708 static snd_pcm_sframes_t snd_pcm_rate_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
709 {
710         snd_pcm_rate_t *rate = pcm->private_data;
711         snd_pcm_sframes_t n = snd_pcm_mmap_avail(pcm);
712
713         if ((snd_pcm_uframes_t)n > frames)
714                 frames = n;
715         if (frames == 0)
716                 return 0;
717         
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;
722 }
723
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)
728 {
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;
735
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;
744                 }
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;
755                 }
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);
765
766                 snd_pcm_rate_write_areas1(pcm, rate->pareas, 0, rate->sareas, 0);
767
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;
789                 }
790                 xfer = cont;
791
792                 if (xfer == slave_size)
793                         goto commit_done;
794                 
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;
805                 }
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;
819                 }
820         }
821
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;
827         }
828         return 1;
829 }
830
831 static int snd_pcm_rate_commit_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t appl_offset)
832 {
833         snd_pcm_rate_t *rate = pcm->private_data;
834
835         return snd_pcm_rate_commit_area(pcm, rate, appl_offset, pcm->period_size,
836                                         rate->gen.slave->period_size);
837 }
838
839 static int snd_pcm_rate_grab_next_period(snd_pcm_t *pcm, snd_pcm_uframes_t hw_offset)
840 {
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;
848
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;
866                 }
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;
889                 }
890                 xfer = cont;
891
892                 if (xfer == rate->gen.slave->period_size)
893                         goto __transfer;
894
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;
905                 }
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;
919                 }
920
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);
938                 }
939         }
940         return 1;
941 }
942
943 static int snd_pcm_rate_sync_playback_area(snd_pcm_t *pcm, snd_pcm_uframes_t appl_ptr)
944 {
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;
950
951         slave_size = snd_pcm_avail_update(slave);
952         if (slave_size < 0)
953                 return slave_size;
954
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;
971         }
972         return 0;
973 }
974
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)
978 {
979         snd_pcm_rate_t *rate = pcm->private_data;
980         int err;
981
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;
988         }
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;
993 }
994
995 static snd_pcm_sframes_t snd_pcm_rate_avail_update(snd_pcm_t *pcm)
996 {
997         snd_pcm_rate_t *rate = pcm->private_data;
998         snd_pcm_t *slave = rate->gen.slave;
999         snd_pcm_uframes_t slave_size;
1000
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;
1011         
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);
1028         }
1029         return (snd_pcm_sframes_t)xfer;
1030  }
1031 }
1032
1033 static int snd_pcm_rate_htimestamp(snd_pcm_t *pcm,
1034                                    snd_pcm_uframes_t *avail,
1035                                    snd_htimestamp_t *tstamp)
1036 {
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;
1041
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;
1055         }
1056         return 0;
1057 }
1058
1059 static int snd_pcm_rate_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
1060 {
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);
1066         }
1067         return snd_pcm_poll_descriptors_revents(rate->gen.slave, pfds, nfds, revents);
1068 }
1069
1070 static int snd_pcm_rate_drain(snd_pcm_t *pcm)
1071 {
1072         snd_pcm_rate_t *rate = pcm->private_data;
1073
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;
1078
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);
1084
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;
1089
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;
1100                         }
1101                         snd_pcm_rate_commit_area(pcm, rate, ofs,
1102                                                  psize, spsize);
1103                         ofs = (ofs + psize) % pcm->buffer_size;
1104                         size -= psize;
1105                 }
1106                 sw_params.avail_min = saved_avail_min;
1107                 snd_pcm_sw_params(rate->gen.slave, &sw_params);
1108         }
1109         return snd_pcm_drain(rate->gen.slave);
1110 }
1111
1112 static snd_pcm_state_t snd_pcm_rate_state(snd_pcm_t *pcm)
1113 {
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);
1118 }
1119
1120
1121 static int snd_pcm_rate_start(snd_pcm_t *pcm)
1122 {
1123         snd_pcm_rate_t *rate = pcm->private_data;
1124         snd_pcm_uframes_t avail;
1125                 
1126         if (pcm->stream == SND_PCM_STREAM_CAPTURE)
1127                 return snd_pcm_start(rate->gen.slave);
1128
1129         if (snd_pcm_state(rate->gen.slave) != SND_PCM_STATE_PREPARED)
1130                 return -EBADFD;
1131
1132         gettimestamp(&rate->trigger_tstamp, pcm->monotonic);
1133
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;
1139         }
1140         rate->start_pending = 0;
1141         return snd_pcm_start(rate->gen.slave);
1142 }
1143
1144 static int snd_pcm_rate_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
1145 {
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;
1156         }
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;
1161         }
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);
1173         }
1174         if (!snd_atomic_read_ok(&ratom)) {
1175                 snd_atomic_read_wait(&ratom);
1176                 goto _again;
1177         }
1178         return 0;
1179 }
1180
1181 static void snd_pcm_rate_dump(snd_pcm_t *pcm, snd_output_t *out)
1182 {
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);
1197         }
1198         snd_output_printf(out, "Slave: ");
1199         snd_pcm_dump(rate->gen.slave, out);
1200 }
1201
1202 static int snd_pcm_rate_close(snd_pcm_t *pcm)
1203 {
1204         snd_pcm_rate_t *rate = pcm->private_data;
1205
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);
1211 }
1212
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,
1237 };
1238
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,
1252 };
1253
1254 /**
1255  * \brief Get a default converter string
1256  * \param root Root configuration node
1257  * \retval A const config item if found, or NULL
1258  */
1259 const snd_config_t *snd_pcm_rate_get_default_converter(snd_config_t *root)
1260 {
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;
1266 }
1267
1268 #ifdef PIC
1269 static int is_builtin_plugin(const char *type)
1270 {
1271         return strcmp(type, "linear") == 0;
1272 }
1273
1274 static const char *const default_rate_plugins[] = {
1275         "speexrate", "linear", NULL
1276 };
1277
1278 static int rate_open_func(snd_pcm_rate_t *rate, const char *type, int verbose)
1279 {
1280         char open_name[64], lib_name[128], *lib = NULL;
1281         snd_pcm_rate_open_func_t open_func;
1282         int err;
1283
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;
1289         }
1290         open_func = snd_dlobj_cache_get(lib, open_name, NULL, verbose);
1291         if (!open_func)
1292                 return -ENOENT;
1293
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;
1298
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;
1307         }
1308
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;
1316         }
1317         return err;
1318 }
1319 #endif
1320
1321 /**
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.
1334  */
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)
1339 {
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
1348
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;
1356         }
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);
1362
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;
1367         }
1368
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;
1378                         }
1379                 }
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;
1391                 }
1392         } else {
1393                 SNDERR("Invalid type for rate converter");
1394                 snd_pcm_close(pcm);
1395                 free(rate);
1396                 return -EINVAL;
1397         }
1398         if (err < 0) {
1399                 SNDERR("Cannot find rate converter");
1400                 snd_pcm_close(pcm);
1401                 free(rate);
1402                 return -ENOENT;
1403         }
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;
1412         }
1413 #endif
1414
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;
1421         }
1422
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;
1433
1434         return 0;
1435 }
1436
1437 /*! \page pcm_plugins
1438
1439 \section pcm_plugins_rate Plugin: Rate
1440
1441 This plugin converts a stream rate. The input and output formats must be linear.
1442
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
1454         }
1455         converter STR                   # optional
1456         # or
1457         converter [ STR1 STR2 ... ]     # optional
1458                                 # Converter type, default is taken from
1459                                 # defaults.pcm.rate_converter
1460 }
1461 \endcode
1462
1463 \subsection pcm_plugins_rate_funcref Function reference
1464
1465 <UL>
1466   <LI>snd_pcm_rate_open()
1467   <LI>_snd_pcm_rate_open()
1468 </UL>
1469
1470 */
1471
1472 /**
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.
1484  */
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)
1488 {
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;
1496
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;
1507                 }
1508                 if (strcmp(id, "converter") == 0) {
1509                         converter = n;
1510                         continue;
1511                 }
1512                 SNDERR("Unknown field %s", id);
1513                 return -EINVAL;
1514         }
1515         if (!slave) {
1516                 SNDERR("slave is not defined");
1517                 return -EINVAL;
1518         }
1519
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;
1530         }
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;
1540 }
1541 #ifndef DOC_HIDDEN
1542 SND_DLSYM_BUILD_VERSION(_snd_pcm_rate_open, SND_PCM_DLSYM_VERSION);
1543 #endif