OSDN Git Service

Encapsulated conf API
[android-x86/external-alsa-lib.git] / src / pcm / pcm_adpcm.c
1 /*
2  *  PCM - Ima-ADPC conversion
3  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
4  *  Copyright (c) 1999 by Uros Bizjak <uros@kss-loka.si>
5  *                        Jaroslav Kysela <perex@suse.cz>
6  *
7  *  Based on Version 1.2, 18-Dec-92 implementation of Intel/DVI ADPCM code
8  *  by Jack Jansen, CWI, Amsterdam <Jack.Jansen@cwi.nl>, Copyright 1992
9  *  by Stichting Mathematisch Centrum, Amsterdam, The Netherlands.
10  *
11  *   This library is free software; you can redistribute it and/or modify
12  *   it under the terms of the GNU Library General Public License as
13  *   published by the Free Software Foundation; either version 2 of
14  *   the License, or (at your option) any later version.
15  *
16  *   This program is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU Library General Public License for more details.
20  *
21  *   You should have received a copy of the GNU Library General Public
22  *   License along with this library; if not, write to the Free Software
23  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  *
25  */
26   
27 /*
28 These routines convert 16 bit linear PCM samples to 4 bit ADPCM code
29 and vice versa. The ADPCM code used is the Intel/DVI ADPCM code which
30 is being recommended by the IMA Digital Audio Technical Working Group.
31
32 The algorithm for this coder was taken from:
33 Proposal for Standardized Audio Interstreamge Formats,
34 IMA compatability project proceedings, Vol 2, Issue 2, May 1992.
35
36 - No, this is *not* a G.721 coder/decoder. The algorithm used by G.721
37   is very complicated, requiring oodles of floating-point ops per
38   sample (resulting in very poor performance). I have not done any
39   tests myself but various people have assured my that 721 quality is
40   actually lower than DVI quality.
41
42 - No, it probably isn't a RIFF ADPCM decoder either. Trying to decode
43   RIFF ADPCM with these routines seems to result in something
44   recognizable but very distorted.
45
46 - No, it is not a CDROM-XA coder either, as far as I know. I haven't
47   come across a good description of XA yet.
48  */
49
50 #include <byteswap.h>
51 #include "pcm_local.h"
52 #include "pcm_plugin.h"
53
54 typedef void (*adpcm_f)(const snd_pcm_channel_area_t *dst_areas,
55                         snd_pcm_uframes_t dst_offset,
56                         const snd_pcm_channel_area_t *src_areas,
57                         snd_pcm_uframes_t src_offset,
58                         unsigned int channels, snd_pcm_uframes_t frames, int getputidx,
59                         snd_pcm_adpcm_state_t *states);
60
61 typedef struct {
62         /* This field need to be the first */
63         snd_pcm_plugin_t plug;
64         int getput_idx;
65         adpcm_f func;
66         snd_pcm_format_t sformat;
67         snd_pcm_adpcm_state_t *states;
68 } snd_pcm_adpcm_t;
69
70 /* First table lookup for Ima-ADPCM quantizer */
71 static char IndexAdjust[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
72
73 /* Second table lookup for Ima-ADPCM quantizer */
74 static short StepSize[89] = {
75         7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
76         19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
77         50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
78         130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
79         337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
80         876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
81         2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
82         5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
83         15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
84 };
85
86 static char adpcm_encoder(int sl, snd_pcm_adpcm_state_t * state)
87 {
88         short diff;             /* Difference between sl and predicted sample */
89         short pred_diff;        /* Predicted difference to next sample */
90
91         unsigned char sign;     /* sign of diff */
92         short step;             /* holds previous StepSize value */
93         unsigned char adjust_idx;       /* Index to IndexAdjust lookup table */
94
95         int i;
96
97         /* Compute difference to previous predicted value */
98         diff = sl - state->pred_val;
99         sign = (diff < 0) ? 0x8 : 0x0;
100         if (sign) {
101                 diff = -diff;
102         }
103
104         /*
105          * This code *approximately* computes:
106          *    adjust_idx = diff * 4 / step;
107          *    pred_diff = (adjust_idx + 0.5) * step / 4;
108          *
109          * But in shift step bits are dropped. The net result of this is
110          * that even if you have fast mul/div hardware you cannot put it to
111          * good use since the fixup would be too expensive.
112          */
113
114         step = StepSize[state->step_idx];
115
116         /* Divide and clamp */
117         pred_diff = step >> 3;
118         for (adjust_idx = 0, i = 0x4; i; i >>= 1, step >>= 1) {
119                 if (diff >= step) {
120                         adjust_idx |= i;
121                         diff -= step;
122                         pred_diff += step;
123                 }
124         }
125
126         /* Update and clamp previous predicted value */
127         state->pred_val += sign ? -pred_diff : pred_diff;
128
129         if (state->pred_val > 32767) {
130                 state->pred_val = 32767;
131         } else if (state->pred_val < -32768) {
132                 state->pred_val = -32768;
133         }
134
135         /* Update and clamp StepSize lookup table index */
136         state->step_idx += IndexAdjust[adjust_idx];
137
138         if (state->step_idx < 0) {
139                 state->step_idx = 0;
140         } else if (state->step_idx > 88) {
141                 state->step_idx = 88;
142         }
143         return (sign | adjust_idx);
144 }
145
146
147 static int adpcm_decoder(unsigned char code, snd_pcm_adpcm_state_t * state)
148 {
149         short pred_diff;        /* Predicted difference to next sample */
150         short step;             /* holds previous StepSize value */
151         char sign;
152
153         int i;
154
155         /* Separate sign and magnitude */
156         sign = code & 0x8;
157         code &= 0x7;
158
159         /*
160          * Computes pred_diff = (code + 0.5) * step / 4,
161          * but see comment in adpcm_coder.
162          */
163
164         step = StepSize[state->step_idx];
165
166         /* Compute difference and new predicted value */
167         pred_diff = step >> 3;
168         for (i = 0x4; i; i >>= 1, step >>= 1) {
169                 if (code & i) {
170                         pred_diff += step;
171                 }
172         }
173         state->pred_val += (sign) ? -pred_diff : pred_diff;
174
175         /* Clamp output value */
176         if (state->pred_val > 32767) {
177                 state->pred_val = 32767;
178         } else if (state->pred_val < -32768) {
179                 state->pred_val = -32768;
180         }
181
182         /* Find new StepSize index value */
183         state->step_idx += IndexAdjust[code];
184
185         if (state->step_idx < 0) {
186                 state->step_idx = 0;
187         } else if (state->step_idx > 88) {
188                 state->step_idx = 88;
189         }
190         return (state->pred_val);
191 }
192
193 void snd_pcm_adpcm_decode(const snd_pcm_channel_area_t *dst_areas,
194                           snd_pcm_uframes_t dst_offset,
195                           const snd_pcm_channel_area_t *src_areas,
196                           snd_pcm_uframes_t src_offset,
197                           unsigned int channels, snd_pcm_uframes_t frames, int putidx,
198                           snd_pcm_adpcm_state_t *states)
199 {
200 #define PUT16_LABELS
201 #include "plugin_ops.h"
202 #undef PUT16_LABELS
203         void *put = put16_labels[putidx];
204         unsigned int channel;
205         for (channel = 0; channel < channels; ++channel, ++states) {
206                 char *src;
207                 int srcbit;
208                 char *dst;
209                 int src_step, srcbit_step, dst_step;
210                 snd_pcm_uframes_t frames1;
211                 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
212                 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
213 #if 0
214                 if (!src_area->enabled) {
215                         if (dst_area->wanted)
216                                 snd_pcm_area_silence(dst_area, dst_offset, frames, dst_sfmt);
217                         dst_area->enabled = 0;
218                         continue;
219                 }
220                 dst_area->enabled = 1;
221 #endif
222                 srcbit = src_area->first + src_area->step * src_offset;
223                 src = src_area->addr + srcbit / 8;
224                 srcbit %= 8;
225                 src_step = src_area->step / 8;
226                 srcbit_step = src_area->step % 8;
227                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
228                 dst_step = snd_pcm_channel_area_step(dst_area);
229                 frames1 = frames;
230                 while (frames1-- > 0) {
231                         int16_t sample;
232                         int v;
233                         if (srcbit)
234                                 v = *src & 0x0f;
235                         else
236                                 v = (*src >> 4) & 0x0f;
237                         sample = adpcm_decoder(v, states);
238                         goto *put;
239 #define PUT16_END after
240 #include "plugin_ops.h"
241 #undef PUT16_END
242                 after:
243                         src += src_step;
244                         srcbit += srcbit_step;
245                         if (srcbit == 8) {
246                                 src++;
247                                 srcbit = 0;
248                         }
249                         dst += dst_step;
250                 }
251         }
252 }
253
254 void snd_pcm_adpcm_encode(const snd_pcm_channel_area_t *dst_areas,
255                           snd_pcm_uframes_t dst_offset,
256                           const snd_pcm_channel_area_t *src_areas,
257                           snd_pcm_uframes_t src_offset,
258                           unsigned int channels, snd_pcm_uframes_t frames, int getidx,
259                           snd_pcm_adpcm_state_t *states)
260 {
261 #define GET16_LABELS
262 #include "plugin_ops.h"
263 #undef GET16_LABELS
264         void *get = get16_labels[getidx];
265         unsigned int channel;
266         int16_t sample = 0;
267         for (channel = 0; channel < channels; ++channel, ++states) {
268                 char *src;
269                 char *dst;
270                 int dstbit;
271                 int src_step, dst_step, dstbit_step;
272                 snd_pcm_uframes_t frames1;
273                 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
274                 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
275 #if 0
276                 if (!src_area->enabled) {
277                         if (dst_area->wanted)
278                                 snd_pcm_area_silence(dst_area, dst_offset, frames, dst_sfmt);
279                         dst_area->enabled = 0;
280                         continue;
281                 }
282                 dst_area->enabled = 1;
283 #endif
284                 src = snd_pcm_channel_area_addr(src_area, src_offset);
285                 src_step = snd_pcm_channel_area_step(src_area);
286                 dstbit = dst_area->first + dst_area->step * dst_offset;
287                 dst = dst_area->addr + dstbit / 8;
288                 dstbit %= 8;
289                 dst_step = dst_area->step / 8;
290                 dstbit_step = dst_area->step % 8;
291                 frames1 = frames;
292                 while (frames1-- > 0) {
293                         int v;
294                         goto *get;
295 #define GET16_END after
296 #include "plugin_ops.h"
297 #undef GET16_END
298                 after:
299                         v = adpcm_encoder(sample, states);
300                         if (dstbit)
301                                 *dst = (*dst & 0xf0) | v;
302                         else
303                                 *dst = (*dst & 0x0f) | (v << 4);
304                         src += src_step;
305                         dst += dst_step;
306                         dstbit += dstbit_step;
307                         if (dstbit == 8) {
308                                 dst++;
309                                 dstbit = 0;
310                         }
311                 }
312         }
313 }
314
315 static int snd_pcm_adpcm_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
316 {
317         snd_pcm_adpcm_t *adpcm = pcm->private;
318         int err;
319         snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_PLUGIN };
320         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
321                                          &access_mask);
322         if (err < 0)
323                 return err;
324         if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
325                 snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
326                 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
327                                                  &format_mask);
328         } else {
329                 err = _snd_pcm_hw_params_set_format(params,
330                                                    SND_PCM_FORMAT_IMA_ADPCM);
331         }
332         if (err < 0)
333                 return err;
334         err = _snd_pcm_hw_params_set_subformat(params,
335                                                SND_PCM_SUBFORMAT_STD);
336         if (err < 0)
337                 return err;
338         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
339         return 0;
340 }
341
342 static int snd_pcm_adpcm_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
343 {
344         snd_pcm_adpcm_t *adpcm = pcm->private;
345         snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
346         _snd_pcm_hw_params_any(sparams);
347         _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
348                                    &saccess_mask);
349         _snd_pcm_hw_params_set_format(sparams, adpcm->sformat);
350         _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
351         return 0;
352 }
353
354 static int snd_pcm_adpcm_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
355                                             snd_pcm_hw_params_t *sparams)
356 {
357         int err;
358         unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
359                               SND_PCM_HW_PARBIT_RATE |
360                               SND_PCM_HW_PARBIT_PERIOD_SIZE |
361                               SND_PCM_HW_PARBIT_BUFFER_SIZE |
362                               SND_PCM_HW_PARBIT_PERIODS |
363                               SND_PCM_HW_PARBIT_PERIOD_TIME |
364                               SND_PCM_HW_PARBIT_BUFFER_TIME |
365                               SND_PCM_HW_PARBIT_TICK_TIME);
366         err = _snd_pcm_hw_params_refine(sparams, links, params);
367         if (err < 0)
368                 return err;
369         return 0;
370 }
371         
372 static int snd_pcm_adpcm_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
373                                             snd_pcm_hw_params_t *sparams)
374 {
375         int err;
376         unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
377                               SND_PCM_HW_PARBIT_RATE |
378                               SND_PCM_HW_PARBIT_PERIOD_SIZE |
379                               SND_PCM_HW_PARBIT_BUFFER_SIZE |
380                               SND_PCM_HW_PARBIT_PERIODS |
381                               SND_PCM_HW_PARBIT_PERIOD_TIME |
382                               SND_PCM_HW_PARBIT_BUFFER_TIME |
383                               SND_PCM_HW_PARBIT_TICK_TIME);
384         err = _snd_pcm_hw_params_refine(params, links, sparams);
385         if (err < 0)
386                 return err;
387         return 0;
388 }
389
390 static int snd_pcm_adpcm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
391 {
392         return snd_pcm_hw_refine_slave(pcm, params,
393                                        snd_pcm_adpcm_hw_refine_cprepare,
394                                        snd_pcm_adpcm_hw_refine_cchange,
395                                        snd_pcm_adpcm_hw_refine_sprepare,
396                                        snd_pcm_adpcm_hw_refine_schange,
397                                        snd_pcm_plugin_hw_refine_slave);
398 }
399
400 static int snd_pcm_adpcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
401 {
402         snd_pcm_adpcm_t *adpcm = pcm->private;
403         int err = snd_pcm_hw_params_slave(pcm, params,
404                                           snd_pcm_adpcm_hw_refine_cchange,
405                                           snd_pcm_adpcm_hw_refine_sprepare,
406                                           snd_pcm_adpcm_hw_refine_schange,
407                                           snd_pcm_plugin_hw_params_slave);
408         if (err < 0)
409                 return err;
410
411
412         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
413                 if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
414                         adpcm->getput_idx = snd_pcm_linear_get_index(snd_pcm_hw_params_get_format(params), SND_PCM_FORMAT_S16);
415                         adpcm->func = snd_pcm_adpcm_encode;
416                 } else {
417                         adpcm->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, adpcm->sformat);
418                         adpcm->func = snd_pcm_adpcm_decode;
419                 }
420         } else {
421                 if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
422                         adpcm->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, snd_pcm_hw_params_get_format(params));
423                         adpcm->func = snd_pcm_adpcm_decode;
424                 } else {
425                         adpcm->getput_idx = snd_pcm_linear_get_index(adpcm->sformat, SND_PCM_FORMAT_S16);
426                         adpcm->func = snd_pcm_adpcm_encode;
427                 }
428         }
429         assert(!adpcm->states);
430         adpcm->states = malloc(snd_pcm_hw_param_get(params, SND_PCM_HW_PARAM_CHANNELS, 0) * sizeof(*adpcm->states));
431         return 0;
432 }
433
434 static int snd_pcm_adpcm_hw_free(snd_pcm_t *pcm)
435 {
436         snd_pcm_adpcm_t *adpcm = pcm->private;
437         if (adpcm->states) {
438                 free(adpcm->states);
439                 adpcm->states = 0;
440         }
441         return snd_pcm_hw_free(adpcm->plug.slave);
442 }
443
444 static int snd_pcm_adpcm_init(snd_pcm_t *pcm)
445 {
446         snd_pcm_adpcm_t *adpcm = pcm->private;
447         unsigned int k;
448         for (k = 0; k < pcm->channels; ++k) {
449                 adpcm->states[k].pred_val = 0;
450                 adpcm->states[k].step_idx = 0;
451         }
452         return 0;
453 }
454
455 static snd_pcm_sframes_t snd_pcm_adpcm_write_areas(snd_pcm_t *pcm,
456                                          const snd_pcm_channel_area_t *areas,
457                                          snd_pcm_uframes_t offset,
458                                          snd_pcm_uframes_t size,
459                                          snd_pcm_uframes_t *slave_sizep)
460 {
461         snd_pcm_adpcm_t *adpcm = pcm->private;
462         snd_pcm_t *slave = adpcm->plug.slave;
463         snd_pcm_uframes_t xfer = 0;
464         snd_pcm_sframes_t err = 0;
465         if (slave_sizep && *slave_sizep < size)
466                 size = *slave_sizep;
467         assert(size > 0);
468         while (xfer < size) {
469                 snd_pcm_uframes_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
470                 adpcm->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
471                             areas, offset, 
472                             pcm->channels, frames,
473                             adpcm->getput_idx, adpcm->states);
474                 err = snd_pcm_mmap_forward(slave, frames);
475                 if (err < 0)
476                         break;
477                 assert((snd_pcm_uframes_t)err == frames);
478                 offset += err;
479                 xfer += err;
480                 snd_pcm_mmap_hw_forward(pcm, err);
481         }
482         if (xfer > 0) {
483                 if (slave_sizep)
484                         *slave_sizep = xfer;
485                 return xfer;
486         }
487         return err;
488 }
489
490 static snd_pcm_sframes_t snd_pcm_adpcm_read_areas(snd_pcm_t *pcm,
491                                         const snd_pcm_channel_area_t *areas,
492                                         snd_pcm_uframes_t offset,
493                                         snd_pcm_uframes_t size,
494                                         snd_pcm_uframes_t *slave_sizep)
495 {
496         snd_pcm_adpcm_t *adpcm = pcm->private;
497         snd_pcm_t *slave = adpcm->plug.slave;
498         snd_pcm_uframes_t xfer = 0;
499         snd_pcm_sframes_t err = 0;
500         if (slave_sizep && *slave_sizep < size)
501                 size = *slave_sizep;
502         assert(size > 0);
503         while (xfer < size) {
504                 snd_pcm_uframes_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
505                 adpcm->func(areas, offset, 
506                             snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
507                             pcm->channels, frames,
508                             adpcm->getput_idx, adpcm->states);
509                 err = snd_pcm_mmap_forward(slave, frames);
510                 if (err < 0)
511                         break;
512                 assert((snd_pcm_uframes_t)err == frames);
513                 offset += err;
514                 xfer += err;
515                 snd_pcm_mmap_hw_forward(pcm, err);
516         }
517         if (xfer > 0) {
518                 if (slave_sizep)
519                         *slave_sizep = xfer;
520                 return xfer;
521         }
522         return err;
523 }
524
525 static void snd_pcm_adpcm_dump(snd_pcm_t *pcm, snd_output_t *out)
526 {
527         snd_pcm_adpcm_t *adpcm = pcm->private;
528         snd_output_printf(out, "Ima-ADPCM conversion PCM (%s)\n", 
529                 snd_pcm_format_name(adpcm->sformat));
530         if (pcm->setup) {
531                 snd_output_printf(out, "Its setup is:\n");
532                 snd_pcm_dump_setup(pcm, out);
533         }
534         snd_output_printf(out, "Slave: ");
535         snd_pcm_dump(adpcm->plug.slave, out);
536 }
537
538 snd_pcm_ops_t snd_pcm_adpcm_ops = {
539         close: snd_pcm_plugin_close,
540         info: snd_pcm_plugin_info,
541         hw_refine: snd_pcm_adpcm_hw_refine,
542         hw_params: snd_pcm_adpcm_hw_params,
543         hw_free: snd_pcm_adpcm_hw_free,
544         sw_params: snd_pcm_plugin_sw_params,
545         channel_info: snd_pcm_plugin_channel_info,
546         dump: snd_pcm_adpcm_dump,
547         nonblock: snd_pcm_plugin_nonblock,
548         async: snd_pcm_plugin_async,
549         mmap: snd_pcm_plugin_mmap,
550         munmap: snd_pcm_plugin_munmap,
551 };
552
553 int snd_pcm_adpcm_open(snd_pcm_t **pcmp, char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
554 {
555         snd_pcm_t *pcm;
556         snd_pcm_adpcm_t *adpcm;
557         assert(pcmp && slave);
558         if (snd_pcm_format_linear(sformat) != 1 &&
559             sformat != SND_PCM_FORMAT_IMA_ADPCM)
560                 return -EINVAL;
561         adpcm = calloc(1, sizeof(snd_pcm_adpcm_t));
562         if (!adpcm) {
563                 return -ENOMEM;
564         }
565         adpcm->sformat = sformat;
566         adpcm->plug.read = snd_pcm_adpcm_read_areas;
567         adpcm->plug.write = snd_pcm_adpcm_write_areas;
568         adpcm->plug.init = snd_pcm_adpcm_init;
569         adpcm->plug.slave = slave;
570         adpcm->plug.close_slave = close_slave;
571
572         pcm = calloc(1, sizeof(snd_pcm_t));
573         if (!pcm) {
574                 free(adpcm);
575                 return -ENOMEM;
576         }
577         if (name)
578                 pcm->name = strdup(name);
579         pcm->type = SND_PCM_TYPE_ADPCM;
580         pcm->stream = slave->stream;
581         pcm->mode = slave->mode;
582         pcm->ops = &snd_pcm_adpcm_ops;
583         pcm->op_arg = pcm;
584         pcm->fast_ops = &snd_pcm_plugin_fast_ops;
585         pcm->fast_op_arg = pcm;
586         pcm->private = adpcm;
587         pcm->poll_fd = slave->poll_fd;
588         pcm->hw_ptr = &adpcm->plug.hw_ptr;
589         pcm->appl_ptr = &adpcm->plug.appl_ptr;
590         *pcmp = pcm;
591
592         return 0;
593 }
594
595 int _snd_pcm_adpcm_open(snd_pcm_t **pcmp, char *name,
596                          snd_config_t *conf, 
597                          snd_pcm_stream_t stream, int mode)
598 {
599         snd_config_iterator_t i;
600         const char *sname = NULL;
601         int err;
602         snd_pcm_t *spcm;
603         snd_pcm_format_t sformat = SND_PCM_FORMAT_UNKNOWN;
604         snd_config_foreach(i, conf) {
605                 snd_config_t *n = snd_config_iterator_entry(i);
606                 const char *id = snd_config_get_id(n);
607                 if (strcmp(id, "comment") == 0)
608                         continue;
609                 if (strcmp(id, "type") == 0)
610                         continue;
611                 if (strcmp(id, "stream") == 0)
612                         continue;
613                 if (strcmp(id, "sname") == 0) {
614                         err = snd_config_get_string(n, &sname);
615                         if (err < 0) {
616                                 ERR("Invalid type for %s", id);
617                                 return -EINVAL;
618                         }
619                         continue;
620                 }
621                 if (strcmp(id, "sformat") == 0) {
622                         const char *f;
623                         err = snd_config_get_string(n, &f);
624                         if (err < 0) {
625                                 ERR("Invalid type for %s", id);
626                                 return -EINVAL;
627                         }
628                         sformat = snd_pcm_format_value(f);
629                         if (sformat == SND_PCM_FORMAT_UNKNOWN) {
630                                 ERR("Unknown sformat");
631                                 return -EINVAL;
632                         }
633                         if (snd_pcm_format_linear(sformat) != 1 &&
634                             sformat != SND_PCM_FORMAT_IMA_ADPCM) {
635                                 ERR("Invalid sformat");
636                                 return -EINVAL;
637                         }
638                         continue;
639                 }
640                 ERR("Unknown field %s", id);
641                 return -EINVAL;
642         }
643         if (!sname) {
644                 ERR("sname is not defined");
645                 return -EINVAL;
646         }
647         if (sformat == SND_PCM_FORMAT_UNKNOWN) {
648                 ERR("sformat is not defined");
649                 return -EINVAL;
650         }
651         /* This is needed cause snd_config_update may destroy config */
652         sname = strdup(sname);
653         if (!sname)
654                 return  -ENOMEM;
655         err = snd_pcm_open(&spcm, sname, stream, mode);
656         free((void *) sname);
657         if (err < 0)
658                 return err;
659         err = snd_pcm_adpcm_open(pcmp, name, sformat, spcm, 1);
660         if (err < 0)
661                 snd_pcm_close(spcm);
662         return err;
663 }
664                                 
665