OSDN Git Service

Added name support
[android-x86/external-alsa-lib.git] / src / pcm / pcm_rate.c
1 /*
2  *  PCM - Rate conversion
3  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
4  *
5  *
6  *   This library is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU Library General Public License as
8  *   published by the Free Software Foundation; either version 2 of
9  *   the License, or (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU Library General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Library General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21   
22 #include <limits.h>
23 #include <byteswap.h>
24 #include "pcm_local.h"
25 #include "pcm_plugin.h"
26
27 #define DIV (1<<16)
28
29 typedef struct {
30         int16_t sample;
31         int sum;
32         unsigned int pos;
33 } rate_state_t;
34  
35 typedef size_t (*rate_f)(snd_pcm_channel_area_t *src_areas,
36                          size_t src_offset, size_t src_frames,
37                          snd_pcm_channel_area_t *dst_areas,
38                          size_t dst_offset, size_t *dst_framesp,
39                          size_t channels,
40                          int getidx, int putidx,
41                          unsigned int arg,
42                          rate_state_t *states);
43
44 typedef struct {
45         /* This field need to be the first */
46         snd_pcm_plugin_t plug;
47         int get_idx;
48         int put_idx;
49         unsigned int pitch;
50         rate_f func;
51         int req_sformat;
52         int req_srate;
53         int sformat;
54         int cformat;
55         int srate;
56         int crate;
57         int cxfer_mode, cmmap_shape;
58         rate_state_t *states;
59 } snd_pcm_rate_t;
60
61 static size_t resample_expand(snd_pcm_channel_area_t *src_areas,
62                               size_t src_offset, size_t src_frames,
63                               snd_pcm_channel_area_t *dst_areas,
64                               size_t dst_offset, size_t *dst_framesp,
65                               size_t channels,
66                               int getidx, int putidx,
67                               unsigned int get_threshold,
68                               rate_state_t *states)
69 {
70 #define GET_S16_LABELS
71 #define PUT_S16_LABELS
72 #include "plugin_ops.h"
73 #undef GET_S16_LABELS
74 #undef PUT_S16_LABELS
75         void *get = get_s16_labels[getidx];
76         void *put = put_s16_labels[putidx];
77         unsigned int channel;
78         size_t src_frames1 = 0;
79         size_t dst_frames1 = 0;
80         size_t dst_frames = *dst_framesp;
81         int16_t sample = 0;
82         
83         if (src_frames == 0 ||
84             dst_frames == 0)
85                 return 0;
86         for (channel = 0; channel < channels; ++channel) {
87                 snd_pcm_channel_area_t *src_area = &src_areas[channel];
88                 snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
89                 char *src, *dst;
90                 int src_step, dst_step;
91                 int16_t old_sample = states->sample;
92                 unsigned int pos = states->pos;
93 #if 0
94                 if (!src_area->enabled) {
95                         if (dst_area->wanted)
96                                 snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format.sfmt);
97                         dst_area->enabled = 0;
98                         continue;
99                 }
100                 dst_area->enabled = 1;
101 #endif
102                 src = snd_pcm_channel_area_addr(src_area, src_offset);
103                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
104                 src_step = snd_pcm_channel_area_step(src_area);
105                 dst_step = snd_pcm_channel_area_step(dst_area);
106                 src_frames1 = 0;
107                 dst_frames1 = 0;
108                 while (dst_frames1 < dst_frames) {
109                         if (pos >= get_threshold) {
110                                 int16_t new_sample;
111                                 if (src_frames1 == src_frames)
112                                         break;
113                                 pos -= get_threshold;
114                                 goto *get;
115 #define GET_S16_END after_get
116 #include "plugin_ops.h"
117 #undef GET_S16_END
118                         after_get:
119                                 src += src_step;
120                                 src_frames1++;
121                                 new_sample = sample;
122                                 sample = (old_sample * (DIV - pos) + new_sample * pos) / DIV;
123                                 old_sample = new_sample;
124                         } else
125                                 sample = old_sample;
126                         goto *put;
127 #define PUT_S16_END after_put
128 #include "plugin_ops.h"
129 #undef PUT_S16_END
130                 after_put:
131                         dst += dst_step;
132                         dst_frames1++;
133                         pos += DIV;
134                 }
135                 states->sample = old_sample;
136                 states->pos = pos;
137                 states++;
138         }
139         *dst_framesp = dst_frames1;
140         return src_frames1;
141 }
142
143 static size_t resample_shrink(snd_pcm_channel_area_t *src_areas,
144                               size_t src_offset, size_t src_frames,
145                               snd_pcm_channel_area_t *dst_areas,
146                               size_t dst_offset, size_t *dst_framesp,
147                               size_t channels,
148                               int getidx, int putidx,
149                               unsigned int get_increment,
150                               rate_state_t *states)
151 {
152 #define GET_S16_LABELS
153 #define PUT_S16_LABELS
154 #include "plugin_ops.h"
155 #undef GET_S16_LABELS
156 #undef PUT_S16_LABELS
157         void *get = get_s16_labels[getidx];
158         void *put = put_s16_labels[putidx];
159         unsigned int channel;
160         size_t src_frames1 = 0;
161         size_t dst_frames1 = 0;
162         size_t dst_frames = *dst_framesp;
163         int16_t sample = 0;
164
165         if (src_frames == 0 ||
166             dst_frames == 0)
167                 return 0;
168         for (channel = 0; channel < channels; ++channel) {
169                 snd_pcm_channel_area_t *src_area = &src_areas[channel];
170                 snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
171                 unsigned int pos;
172                 int sum;
173                 char *src, *dst;
174                 int src_step, dst_step;
175                 sum = states->sum;
176                 pos = states->pos;
177 #if 0
178                 if (!src_area->enabled) {
179                         if (dst_area->wanted)
180                                 snd_pcm_area_silence(&dst_area->area, 0, dst_frames, plugin->dst_format.sfmt);
181                         dst_area->enabled = 0;
182                         continue;
183                 }
184                 dst_area->enabled = 1;
185 #endif
186                 src = snd_pcm_channel_area_addr(src_area, src_offset);
187                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
188                 src_step = snd_pcm_channel_area_step(src_area);
189                 dst_step = snd_pcm_channel_area_step(dst_area);
190                 src_frames1 = 0;
191                 dst_frames1 = 0;
192                 while (src_frames1 < src_frames) {
193                         
194                         goto *get;
195 #define GET_S16_END after_get
196 #include "plugin_ops.h"
197 #undef GET_S16_END
198                 after_get:
199                         src += src_step;
200                         src_frames1++;
201                         pos += get_increment;
202                         if (pos >= DIV) {
203                                 int s = sample;
204                                 pos -= DIV;
205                                 sum += s * (get_increment - pos);
206                                 sum /= DIV;
207                                 sample = sum;
208                                 goto *put;
209 #define PUT_S16_END after_put
210 #include "plugin_ops.h"
211 #undef PUT_S16_END
212                         after_put:
213                                 dst += dst_step;
214                                 sum = s * pos;
215                                 dst_frames1++;
216                                 if (dst_frames1 == dst_frames)
217                                         break;
218                         } else
219                                 sum += sample * get_increment;
220                 }
221                 states->sum = sum;
222                 states->pos = pos;
223                 states++;
224         }
225         *dst_framesp = dst_frames1;
226         return src_frames1;
227 }
228
229 static int snd_pcm_rate_close(snd_pcm_t *pcm)
230 {
231         snd_pcm_rate_t *rate = pcm->private;
232         int err = 0;
233         if (rate->plug.close_slave)
234                 err = snd_pcm_close(rate->plug.slave);
235         if (rate->states)
236                 free(rate->states);
237         free(rate);
238         return 0;
239 }
240
241 static int snd_pcm_rate_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
242 {
243         snd_pcm_rate_t *rate = pcm->private;
244         unsigned int req_mask = info->req_mask;
245         unsigned int sfmt = info->req.format.sfmt;
246         unsigned int crate = info->req.format.rate;
247         unsigned int srate;
248         int err;
249         if (req_mask & SND_PCM_PARAMS_SFMT &&
250             !snd_pcm_format_linear(sfmt)) {
251                 info->req.fail_mask = SND_PCM_PARAMS_SFMT;
252                 info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
253                 return -EINVAL;
254         }
255         if (rate->req_sformat >= 0) {
256                 info->req_mask |= SND_PCM_PARAMS_SFMT;
257                 info->req.format.sfmt = rate->req_sformat;
258         }
259         info->req_mask |= SND_PCM_PARAMS_RATE;
260         info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | 
261                             SND_PCM_PARAMS_XFER_MODE);
262         info->req.format.rate = rate->req_srate;
263         err = snd_pcm_params_info(rate->plug.slave, info);
264         info->req_mask = req_mask;
265         info->req.format.sfmt = sfmt;
266         info->req.format.rate = crate;
267         if (err < 0)
268                 return err;
269         if (req_mask & SND_PCM_PARAMS_SFMT)
270                 info->formats = 1 << sfmt;
271         else
272                 info->formats = SND_PCM_LINEAR_FORMATS;
273         if (!(req_mask & SND_PCM_PARAMS_RATE)) {
274                 info->min_rate = 4000;
275                 info->max_rate = 192000;
276                 return 0;
277         }
278         if (rate->req_srate - info->min_rate < info->max_rate - rate->req_srate)
279                 srate = info->min_rate;
280         else
281                 srate = info->max_rate;
282         info->min_rate = crate;
283         info->max_rate = crate;
284         if (info->buffer_size)
285                 info->buffer_size = muldiv64(info->buffer_size, crate, srate);
286         if (info->min_fragment_size)
287                 info->min_fragment_size = muldiv64(info->min_fragment_size, crate, srate);
288         if (info->max_fragment_size)
289                 info->max_fragment_size = muldiv64(info->max_fragment_size, crate, srate);
290         if (info->fragment_align)
291                 info->fragment_align = muldiv64(info->fragment_align, crate, srate);
292         info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
293         info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
294         return 0;
295 }
296
297 static int snd_pcm_rate_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
298 {
299         snd_pcm_rate_t *rate = pcm->private;
300         snd_pcm_t *slave = rate->plug.slave;
301         snd_pcm_params_t slave_params;
302         snd_pcm_params_info_t slave_info;
303         int srate, crate;
304         int err;
305         if (!snd_pcm_format_linear(params->format.sfmt)) {
306                 params->fail_mask = SND_PCM_PARAMS_SFMT;
307                 params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
308                 return -EINVAL;
309         }
310         slave_params = *params;
311         rate->cformat = params->format.sfmt;
312         rate->crate = crate = params->format.rate;
313         rate->cxfer_mode = params->xfer_mode;
314         rate->cmmap_shape = params->mmap_shape;
315
316         memset(&slave_info, 0, sizeof(slave_info));
317         slave_info.req = *params;
318         if (rate->req_sformat >= 0)
319                 slave_info.req.format.sfmt = rate->req_sformat;
320         slave_info.req.format.rate = rate->req_srate;
321         slave_info.req_mask = ~0;
322         err = snd_pcm_params_info(slave, &slave_info);
323         if (err < 0) {
324                 params->fail_mask = slave_info.req.fail_mask;
325                 params->fail_reason = slave_info.req.fail_reason;
326                 return err;
327         }
328
329         if (slave->mmap_data) {
330                 err = snd_pcm_munmap_data(slave);
331                 if (err < 0)
332                         return err;
333         }
334
335         if (rate->req_srate - slave_info.min_rate < slave_info.max_rate - rate->req_srate)
336                 srate = slave_info.min_rate;
337         else
338                 srate = slave_info.max_rate;
339
340         slave_params.format.rate = srate;
341         slave_params.avail_min = muldiv64(params->avail_min, srate, crate);
342         slave_params.xfer_min = muldiv64(params->xfer_min, srate, crate);
343         slave_params.buffer_size = muldiv64(params->buffer_size, srate, crate);
344         slave_params.frag_size = muldiv64(params->frag_size, srate, crate);
345         slave_params.xfer_align = muldiv64(params->xfer_align, srate, crate);
346         /* FIXME: boundary? */
347         slave_params.xfer_mode = SND_PCM_XFER_UNSPECIFIED;
348         slave_params.mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
349         err = snd_pcm_params(slave, &slave_params);
350         params->fail_mask = slave_params.fail_mask;
351         params->fail_reason = slave_params.fail_reason;
352         if (slave->valid_setup) {
353                 int r = snd_pcm_mmap_data(slave, NULL);
354                 assert(r >= 0);
355         }
356         return err;
357 }
358
359 static int snd_pcm_rate_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
360 {
361         snd_pcm_rate_t *rate = pcm->private;
362         int src_format, dst_format;
363         int src_rate, dst_rate;
364         int mul, div;
365         int err = snd_pcm_setup(rate->plug.slave, setup);
366         if (err < 0)
367                 return err;
368         if (rate->req_sformat >= 0)
369                 assert(rate->req_sformat == setup->format.sfmt);
370         rate->sformat = setup->format.sfmt;
371         rate->srate = setup->format.rate;
372         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
373                 src_format = rate->cformat;
374                 dst_format = rate->sformat;
375                 src_rate = rate->crate;
376                 dst_rate = rate->srate;
377         } else {
378                 src_format = rate->sformat;
379                 dst_format = rate->cformat;
380                 src_rate = rate->srate;
381                 dst_rate = rate->crate;
382         }
383         rate->get_idx = getput_index(src_format);
384         rate->put_idx = getput_index(dst_format);
385         if (src_rate < dst_rate) {
386                 rate->func = resample_expand;
387                 /* pitch is get_threshold */
388         } else {
389                 rate->func = resample_shrink;
390                 /* pitch is get_increment */
391         }
392         rate->pitch = (((u_int64_t)dst_rate * DIV) + src_rate / 2) / src_rate;
393         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
394                 mul = DIV;
395                 div = rate->pitch;
396         } else {
397                 mul = rate->pitch;
398                 div = DIV;
399         }
400         rate->crate = muldiv64(rate->srate, mul, div);
401         if (rate->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
402                 setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
403         else
404                 setup->xfer_mode = rate->cxfer_mode;
405         if (rate->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
406                 setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
407         else
408                 setup->mmap_shape = rate->cmmap_shape;
409         setup->format.sfmt = rate->cformat;
410         setup->format.rate = rate->crate;
411         /* FIXME */
412         setup->rate_master = rate->crate;
413         setup->rate_divisor = 1;
414         setup->mmap_bytes = 0;
415         setup->avail_min = muldiv64(setup->avail_min, mul, div);
416         setup->xfer_min = muldiv64(setup->xfer_min, mul, div);
417
418         /* FIXME: the three above are not a lot sensible */
419         setup->buffer_size = muldiv64(setup->buffer_size, mul, div);
420         setup->frag_size = muldiv64(setup->frag_size, mul, div);
421         setup->xfer_align = muldiv64(setup->xfer_align, mul, div);
422
423         /* FIXME */
424         setup->boundary = LONG_MAX - LONG_MAX % setup->buffer_size;
425
426         if (rate->states)
427                 free(rate->states);
428         rate->states = malloc(setup->format.channels * sizeof(*rate->states));
429         return 0;
430 }
431
432 static int snd_pcm_rate_init(snd_pcm_t *pcm)
433 {
434         snd_pcm_rate_t *rate = pcm->private;
435         unsigned int k;
436         for (k = 0; k < pcm->setup.format.channels; ++k) {
437                 rate->states[k].sum = 0;
438                 rate->states[k].sample = 0;
439                 if (rate->func == resample_expand) {
440                         /* Get a sample on entry */
441                         rate->states[k].pos = rate->pitch + DIV;
442                 } else {
443                         rate->states[k].pos = 0;
444                 }
445         }
446         return 0;
447 }
448
449 static ssize_t snd_pcm_rate_write_areas(snd_pcm_t *pcm,
450                                         snd_pcm_channel_area_t *areas,
451                                         size_t client_offset,
452                                         size_t client_size,
453                                         size_t *slave_sizep)
454 {
455         snd_pcm_rate_t *rate = pcm->private;
456         snd_pcm_t *slave = rate->plug.slave;
457         size_t client_xfer = 0;
458         size_t slave_xfer = 0;
459         ssize_t err = 0;
460         size_t slave_size;
461         if (slave_sizep)
462                 slave_size = *slave_sizep;
463         else
464                 slave_size = INT_MAX;
465         assert(client_size > 0 && slave_size > 0);
466         while (client_xfer < client_size &&
467                slave_xfer < slave_size) {
468                 size_t src_frames, dst_frames;
469                 src_frames = client_size - client_xfer;
470                 dst_frames = snd_pcm_mmap_playback_xfer(slave, slave_size - slave_xfer);
471                 src_frames = rate->func(areas, client_offset, src_frames,
472                                         snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
473                                         &dst_frames, 
474                                         pcm->setup.format.channels,
475                                         rate->get_idx, rate->put_idx,
476                                         rate->pitch, rate->states);
477                 err = snd_pcm_mmap_forward(slave, dst_frames);
478                 if (err < 0)
479                         break;
480                 assert((size_t)err == dst_frames);
481                 client_offset += src_frames;
482                 client_xfer += src_frames;
483                 snd_pcm_mmap_hw_forward(pcm, src_frames);
484                 slave_xfer += dst_frames;
485         }
486         if (client_xfer > 0 || slave_xfer > 0) {
487                 if (slave_sizep)
488                         *slave_sizep = slave_xfer;
489                 return client_xfer;
490         }
491         return err;
492 }
493
494 static ssize_t snd_pcm_rate_read_areas(snd_pcm_t *pcm,
495                                        snd_pcm_channel_area_t *areas,
496                                        size_t client_offset,
497                                        size_t client_size,
498                                        size_t *slave_sizep)
499
500 {
501         snd_pcm_rate_t *rate = pcm->private;
502         snd_pcm_t *slave = rate->plug.slave;
503         size_t client_xfer = 0;
504         size_t slave_xfer = 0;
505         ssize_t err = 0;
506         size_t slave_size;
507         if (slave_sizep)
508                 slave_size = *slave_sizep;
509         else
510                 slave_size = INT_MAX;
511         assert(client_size > 0 && slave_size > 0);
512         while (client_xfer < client_size &&
513                slave_xfer < slave_size) {
514                 size_t src_frames, dst_frames;
515                 dst_frames = client_size - client_xfer;
516                 src_frames = snd_pcm_mmap_capture_xfer(slave, slave_size - slave_xfer);
517                 src_frames = rate->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
518                                         src_frames,
519                                         areas, client_offset, &dst_frames,
520                                         pcm->setup.format.channels,
521                                         rate->get_idx, rate->put_idx,
522                                         rate->pitch, rate->states);
523                 err = snd_pcm_mmap_forward(slave, src_frames);
524                 if (err < 0)
525                         break;
526                 assert((size_t)err == src_frames);
527                 client_offset += dst_frames;
528                 client_xfer += dst_frames;
529                 snd_pcm_mmap_hw_forward(pcm, dst_frames);
530                 slave_xfer += src_frames;
531         }
532         if (client_xfer > 0 || slave_xfer > 0) {
533                 if (slave_sizep)
534                         *slave_sizep = slave_xfer;
535                 return client_xfer;
536         }
537         return err;
538 }
539
540 size_t snd_pcm_rate_client_frames(snd_pcm_t *pcm, size_t frames)
541 {
542         snd_pcm_rate_t *rate = pcm->private;
543         /* Round toward zero */
544         if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
545                 return (int64_t)frames * DIV / rate->pitch;
546         else
547                 return (int64_t)frames * rate->pitch / DIV;
548 }
549
550 static void snd_pcm_rate_dump(snd_pcm_t *pcm, FILE *fp)
551 {
552         snd_pcm_rate_t *rate = pcm->private;
553         if (rate->req_sformat < 0)
554                 fprintf(fp, "Rate conversion PCM (%d)\n", 
555                         rate->req_srate);
556         else
557                 fprintf(fp, "Rate conversion PCM (%d, sformat=%s)\n", 
558                         rate->req_srate,
559                         snd_pcm_format_name(rate->req_sformat));
560         if (pcm->valid_setup) {
561                 fprintf(fp, "Its setup is:\n");
562                 snd_pcm_dump_setup(pcm, fp);
563         }
564         fprintf(fp, "Slave: ");
565         snd_pcm_dump(rate->plug.slave, fp);
566 }
567
568 struct snd_pcm_ops snd_pcm_rate_ops = {
569         close: snd_pcm_rate_close,
570         info: snd_pcm_plugin_info,
571         params_info: snd_pcm_rate_params_info,
572         params: snd_pcm_rate_params,
573         setup: snd_pcm_rate_setup,
574         channel_info: snd_pcm_plugin_channel_info,
575         channel_params: snd_pcm_plugin_channel_params,
576         channel_setup: snd_pcm_plugin_channel_setup,
577         dump: snd_pcm_rate_dump,
578         nonblock: snd_pcm_plugin_nonblock,
579         mmap_status: snd_pcm_plugin_mmap_status,
580         mmap_control: snd_pcm_plugin_mmap_control,
581         mmap_data: snd_pcm_plugin_mmap_data,
582         munmap_status: snd_pcm_plugin_munmap_status,
583         munmap_control: snd_pcm_plugin_munmap_control,
584         munmap_data: snd_pcm_plugin_munmap_data,
585 };
586
587 int snd_pcm_rate_open(snd_pcm_t **handlep, char *name, int sformat, int srate, snd_pcm_t *slave, int close_slave)
588 {
589         snd_pcm_t *handle;
590         snd_pcm_rate_t *rate;
591         int err;
592         assert(handlep && slave);
593         if (sformat >= 0 && snd_pcm_format_linear(sformat) != 1)
594                 return -EINVAL;
595         rate = calloc(1, sizeof(snd_pcm_rate_t));
596         if (!rate) {
597                 return -ENOMEM;
598         }
599         rate->req_srate = srate;
600         rate->req_sformat = sformat;
601         rate->plug.read = snd_pcm_rate_read_areas;
602         rate->plug.write = snd_pcm_rate_write_areas;
603         rate->plug.client_frames = snd_pcm_rate_client_frames;
604         rate->plug.init = snd_pcm_rate_init;
605         rate->plug.slave = slave;
606         rate->plug.close_slave = close_slave;
607
608         handle = calloc(1, sizeof(snd_pcm_t));
609         if (!handle) {
610                 free(rate);
611                 return -ENOMEM;
612         }
613         if (name)
614                 handle->name = strdup(name);
615         handle->type = SND_PCM_TYPE_RATE;
616         handle->stream = slave->stream;
617         handle->ops = &snd_pcm_rate_ops;
618         handle->op_arg = handle;
619         handle->fast_ops = &snd_pcm_plugin_fast_ops;
620         handle->fast_op_arg = handle;
621         handle->mode = slave->mode;
622         handle->private = rate;
623         err = snd_pcm_init(handle);
624         if (err < 0) {
625                 snd_pcm_close(handle);
626                 return err;
627         }
628         *handlep = handle;
629
630         return 0;
631 }
632
633 int _snd_pcm_rate_open(snd_pcm_t **pcmp, char *name,
634                          snd_config_t *conf, 
635                          int stream, int mode)
636 {
637         snd_config_iterator_t i;
638         char *sname = NULL;
639         int err;
640         snd_pcm_t *spcm;
641         int sformat = -1;
642         long srate = -1;
643         snd_config_foreach(i, conf) {
644                 snd_config_t *n = snd_config_entry(i);
645                 if (strcmp(n->id, "comment") == 0)
646                         continue;
647                 if (strcmp(n->id, "type") == 0)
648                         continue;
649                 if (strcmp(n->id, "stream") == 0)
650                         continue;
651                 if (strcmp(n->id, "sname") == 0) {
652                         err = snd_config_string_get(n, &sname);
653                         if (err < 0)
654                                 return -EINVAL;
655                         continue;
656                 }
657                 if (strcmp(n->id, "sformat") == 0) {
658                         char *f;
659                         err = snd_config_string_get(n, &f);
660                         if (err < 0)
661                                 return -EINVAL;
662                         sformat = snd_pcm_format_value(f);
663                         if (sformat < 0)
664                                 return -EINVAL;
665                         if (snd_pcm_format_linear(sformat) != 1)
666                                 return -EINVAL;
667                         continue;
668                 }
669                 if (strcmp(n->id, "srate") == 0) {
670                         err = snd_config_integer_get(n, &srate);
671                         if (err < 0)
672                                 return -EINVAL;
673                         continue;
674                 }
675                 return -EINVAL;
676         }
677         if (!sname || !srate)
678                 return -EINVAL;
679         /* This is needed cause snd_config_update may destroy config */
680         sname = strdup(sname);
681         if (!sname)
682                 return  -ENOMEM;
683         err = snd_pcm_open(&spcm, sname, stream, mode);
684         free(sname);
685         if (err < 0)
686                 return err;
687         err = snd_pcm_rate_open(pcmp, name, sformat, srate, spcm, 1);
688         if (err < 0)
689                 snd_pcm_close(spcm);
690         return err;
691 }
692                                 
693