OSDN Git Service

Added argument handling for the slave PCMs.
[android-x86/external-alsa-lib.git] / src / pcm / pcm_alaw.c
1 /*
2  *  PCM - A-Law 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 <byteswap.h>
23 #include "pcm_local.h"
24 #include "pcm_plugin.h"
25
26 typedef void (*alaw_f)(const snd_pcm_channel_area_t *dst_areas,
27                        snd_pcm_uframes_t dst_offset,
28                        const snd_pcm_channel_area_t *src_areas,
29                        snd_pcm_uframes_t src_offset,
30                        unsigned int channels, snd_pcm_uframes_t frames,
31                        unsigned int getputidx);
32
33 typedef struct {
34         /* This field need to be the first */
35         snd_pcm_plugin_t plug;
36         unsigned int getput_idx;
37         alaw_f func;
38         snd_pcm_format_t sformat;
39 } snd_pcm_alaw_t;
40
41 static inline int val_seg(int val)
42 {
43         int r = 1;
44         val >>= 8;
45         if (val & 0xf0) {
46                 val >>= 4;
47                 r += 4;
48         }
49         if (val & 0x0c) {
50                 val >>= 2;
51                 r += 2;
52         }
53         if (val & 0x02)
54                 r += 1;
55         return r;
56 }
57
58 /*
59  * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
60  *
61  * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
62  *
63  *              Linear Input Code       Compressed Code
64  *      ------------------------        ---------------
65  *      0000000wxyza                    000wxyz
66  *      0000001wxyza                    001wxyz
67  *      000001wxyzab                    010wxyz
68  *      00001wxyzabc                    011wxyz
69  *      0001wxyzabcd                    100wxyz
70  *      001wxyzabcde                    101wxyz
71  *      01wxyzabcdef                    110wxyz
72  *      1wxyzabcdefg                    111wxyz
73  *
74  * For further information see John C. Bellamy's Digital Telephony, 1982,
75  * John Wiley & Sons, pps 98-111 and 472-476.
76  */
77
78 static unsigned char s16_to_alaw(int pcm_val)
79 {
80         int             mask;
81         int             seg;
82         unsigned char   aval;
83
84         if (pcm_val >= 0) {
85                 mask = 0xD5;
86         } else {
87                 mask = 0x55;
88                 pcm_val = -pcm_val;
89                 if (pcm_val > 0x7fff)
90                         pcm_val = 0x7fff;
91         }
92
93         if (pcm_val < 256)
94                 aval = pcm_val >> 4;
95         else {
96                 /* Convert the scaled magnitude to segment number. */
97                 seg = val_seg(pcm_val);
98                 aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
99         }
100         return aval ^ mask;
101 }
102
103 /*
104  * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
105  *
106  */
107 static int alaw_to_s16(unsigned char a_val)
108 {
109         int             t;
110         int             seg;
111
112         a_val ^= 0x55;
113         t = a_val & 0x7f;
114         if (t < 16)
115                 t = (t << 4) + 8;
116         else {
117                 seg = (t >> 4) & 0x07;
118                 t = ((t & 0x0f) << 4) + 0x108;
119                 t <<= seg -1;
120         }
121         return ((a_val & 0x80) ? t : -t);
122 }
123
124 void snd_pcm_alaw_decode(const snd_pcm_channel_area_t *dst_areas,
125                          snd_pcm_uframes_t dst_offset,
126                          const snd_pcm_channel_area_t *src_areas,
127                          snd_pcm_uframes_t src_offset,
128                          unsigned int channels, snd_pcm_uframes_t frames,
129                          unsigned int putidx)
130 {
131 #define PUT16_LABELS
132 #include "plugin_ops.h"
133 #undef PUT16_LABELS
134         void *put = put16_labels[putidx];
135         unsigned int channel;
136         for (channel = 0; channel < channels; ++channel) {
137                 const unsigned char *src;
138                 char *dst;
139                 int src_step, dst_step;
140                 snd_pcm_uframes_t frames1;
141                 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
142                 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
143                 src = snd_pcm_channel_area_addr(src_area, src_offset);
144                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
145                 src_step = snd_pcm_channel_area_step(src_area);
146                 dst_step = snd_pcm_channel_area_step(dst_area);
147                 frames1 = frames;
148                 while (frames1-- > 0) {
149                         int16_t sample = alaw_to_s16(*src);
150                         goto *put;
151 #define PUT16_END after
152 #include "plugin_ops.h"
153 #undef PUT16_END
154                 after:
155                         src += src_step;
156                         dst += dst_step;
157                 }
158         }
159 }
160
161 void snd_pcm_alaw_encode(const snd_pcm_channel_area_t *dst_areas,
162                          snd_pcm_uframes_t dst_offset,
163                          const snd_pcm_channel_area_t *src_areas,
164                          snd_pcm_uframes_t src_offset,
165                          unsigned int channels, snd_pcm_uframes_t frames,
166                          unsigned int getidx)
167 {
168 #define GET16_LABELS
169 #include "plugin_ops.h"
170 #undef GET16_LABELS
171         void *get = get16_labels[getidx];
172         unsigned int channel;
173         int16_t sample = 0;
174         for (channel = 0; channel < channels; ++channel) {
175                 const char *src;
176                 char *dst;
177                 int src_step, dst_step;
178                 snd_pcm_uframes_t frames1;
179                 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
180                 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
181                 src = snd_pcm_channel_area_addr(src_area, src_offset);
182                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
183                 src_step = snd_pcm_channel_area_step(src_area);
184                 dst_step = snd_pcm_channel_area_step(dst_area);
185                 frames1 = frames;
186                 while (frames1-- > 0) {
187                         goto *get;
188 #define GET16_END after
189 #include "plugin_ops.h"
190 #undef GET16_END
191                 after:
192                         *dst = s16_to_alaw(sample);
193                         src += src_step;
194                         dst += dst_step;
195                 }
196         }
197 }
198
199 static int snd_pcm_alaw_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
200 {
201         snd_pcm_alaw_t *alaw = pcm->private_data;
202         int err;
203         snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
204         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
205                                          &access_mask);
206         if (err < 0)
207                 return err;
208         if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
209                 snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_LINEAR };
210                 err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
211                                                  &format_mask);
212         } else {
213                 err = _snd_pcm_hw_params_set_format(params, 
214                                                    SND_PCM_FORMAT_A_LAW);
215         }
216         if (err < 0)
217                 return err;
218         err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
219         if (err < 0)
220                 return err;
221         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
222         return 0;
223 }
224
225 static int snd_pcm_alaw_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
226 {
227         snd_pcm_alaw_t *alaw = pcm->private_data;
228         snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
229         _snd_pcm_hw_params_any(sparams);
230         _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
231                                    &saccess_mask);
232         _snd_pcm_hw_params_set_format(sparams, alaw->sformat);
233         _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
234         return 0;
235 }
236
237 static int snd_pcm_alaw_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
238                                             snd_pcm_hw_params_t *sparams)
239 {
240         int err;
241         unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
242                               SND_PCM_HW_PARBIT_RATE |
243                               SND_PCM_HW_PARBIT_PERIOD_SIZE |
244                               SND_PCM_HW_PARBIT_BUFFER_SIZE |
245                               SND_PCM_HW_PARBIT_PERIODS |
246                               SND_PCM_HW_PARBIT_PERIOD_TIME |
247                               SND_PCM_HW_PARBIT_BUFFER_TIME |
248                               SND_PCM_HW_PARBIT_TICK_TIME);
249         err = _snd_pcm_hw_params_refine(sparams, links, params);
250         if (err < 0)
251                 return err;
252         return 0;
253 }
254         
255 static int snd_pcm_alaw_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
256                                             snd_pcm_hw_params_t *sparams)
257 {
258         int err;
259         unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
260                               SND_PCM_HW_PARBIT_RATE |
261                               SND_PCM_HW_PARBIT_PERIOD_SIZE |
262                               SND_PCM_HW_PARBIT_BUFFER_SIZE |
263                               SND_PCM_HW_PARBIT_PERIODS |
264                               SND_PCM_HW_PARBIT_PERIOD_TIME |
265                               SND_PCM_HW_PARBIT_BUFFER_TIME |
266                               SND_PCM_HW_PARBIT_TICK_TIME);
267         err = _snd_pcm_hw_params_refine(params, links, sparams);
268         if (err < 0)
269                 return err;
270         return 0;
271 }
272
273 static int snd_pcm_alaw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
274 {
275         return snd_pcm_hw_refine_slave(pcm, params,
276                                        snd_pcm_alaw_hw_refine_cprepare,
277                                        snd_pcm_alaw_hw_refine_cchange,
278                                        snd_pcm_alaw_hw_refine_sprepare,
279                                        snd_pcm_alaw_hw_refine_schange,
280                                        snd_pcm_plugin_hw_refine_slave);
281 }
282
283 static int snd_pcm_alaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
284 {
285         snd_pcm_alaw_t *alaw = pcm->private_data;
286         int err = snd_pcm_hw_params_slave(pcm, params,
287                                           snd_pcm_alaw_hw_refine_cchange,
288                                           snd_pcm_alaw_hw_refine_sprepare,
289                                           snd_pcm_alaw_hw_refine_schange,
290                                           snd_pcm_plugin_hw_params_slave);
291         if (err < 0)
292                 return err;
293
294         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
295                 if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
296                         alaw->getput_idx = snd_pcm_linear_get_index(snd_pcm_hw_params_get_format(params), SND_PCM_FORMAT_S16);
297                         alaw->func = snd_pcm_alaw_encode;
298                 } else {
299                         alaw->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, alaw->sformat);
300                         alaw->func = snd_pcm_alaw_decode;
301                 }
302         } else {
303                 if (alaw->sformat == SND_PCM_FORMAT_A_LAW) {
304                         alaw->getput_idx = snd_pcm_linear_put_index(SND_PCM_FORMAT_S16, snd_pcm_hw_params_get_format(params));
305                         alaw->func = snd_pcm_alaw_decode;
306                 } else {
307                         alaw->getput_idx = snd_pcm_linear_get_index(alaw->sformat, SND_PCM_FORMAT_S16);
308                         alaw->func = snd_pcm_alaw_encode;
309                 }
310         }
311         return 0;
312 }
313
314 static snd_pcm_uframes_t
315 snd_pcm_alaw_write_areas(snd_pcm_t *pcm,
316                          const snd_pcm_channel_area_t *areas,
317                          snd_pcm_uframes_t offset,
318                          snd_pcm_uframes_t size,
319                          const snd_pcm_channel_area_t *slave_areas,
320                          snd_pcm_uframes_t slave_offset,
321                          snd_pcm_uframes_t *slave_sizep)
322 {
323         snd_pcm_alaw_t *alaw = pcm->private_data;
324         if (size > *slave_sizep)
325                 size = *slave_sizep;
326         alaw->func(slave_areas, slave_offset,
327                    areas, offset, 
328                    pcm->channels, size,
329                    alaw->getput_idx);
330         *slave_sizep = size;
331         return size;
332 }
333
334 static snd_pcm_uframes_t
335 snd_pcm_alaw_read_areas(snd_pcm_t *pcm,
336                         const snd_pcm_channel_area_t *areas,
337                         snd_pcm_uframes_t offset,
338                         snd_pcm_uframes_t size,
339                         const snd_pcm_channel_area_t *slave_areas,
340                         snd_pcm_uframes_t slave_offset,
341                         snd_pcm_uframes_t *slave_sizep)
342 {
343         snd_pcm_alaw_t *alaw = pcm->private_data;
344         if (size > *slave_sizep)
345                 size = *slave_sizep;
346         alaw->func(areas, offset, 
347                    slave_areas, slave_offset,
348                    pcm->channels, size,
349                    alaw->getput_idx);
350         *slave_sizep = size;
351         return size;
352 }
353
354 static void snd_pcm_alaw_dump(snd_pcm_t *pcm, snd_output_t *out)
355 {
356         snd_pcm_alaw_t *alaw = pcm->private_data;
357         snd_output_printf(out, "A-Law conversion PCM (%s)\n", 
358                 snd_pcm_format_name(alaw->sformat));
359         if (pcm->setup) {
360                 snd_output_printf(out, "Its setup is:\n");
361                 snd_pcm_dump_setup(pcm, out);
362         }
363         snd_output_printf(out, "Slave: ");
364         snd_pcm_dump(alaw->plug.slave, out);
365 }
366
367 snd_pcm_ops_t snd_pcm_alaw_ops = {
368         close: snd_pcm_plugin_close,
369         info: snd_pcm_plugin_info,
370         hw_refine: snd_pcm_alaw_hw_refine,
371         hw_params: snd_pcm_alaw_hw_params,
372         hw_free: snd_pcm_plugin_hw_free,
373         sw_params: snd_pcm_plugin_sw_params,
374         channel_info: snd_pcm_plugin_channel_info,
375         dump: snd_pcm_alaw_dump,
376         nonblock: snd_pcm_plugin_nonblock,
377         async: snd_pcm_plugin_async,
378         mmap: snd_pcm_plugin_mmap,
379         munmap: snd_pcm_plugin_munmap,
380 };
381
382 int snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
383 {
384         snd_pcm_t *pcm;
385         snd_pcm_alaw_t *alaw;
386         assert(pcmp && slave);
387         if (snd_pcm_format_linear(sformat) != 1 &&
388             sformat != SND_PCM_FORMAT_A_LAW)
389                 return -EINVAL;
390         alaw = calloc(1, sizeof(snd_pcm_alaw_t));
391         if (!alaw) {
392                 return -ENOMEM;
393         }
394         alaw->sformat = sformat;
395         alaw->plug.read = snd_pcm_alaw_read_areas;
396         alaw->plug.write = snd_pcm_alaw_write_areas;
397         alaw->plug.slave = slave;
398         alaw->plug.close_slave = close_slave;
399
400         pcm = calloc(1, sizeof(snd_pcm_t));
401         if (!pcm) {
402                 free(alaw);
403                 return -ENOMEM;
404         }
405         if (name)
406                 pcm->name = strdup(name);
407         pcm->type = SND_PCM_TYPE_ALAW;
408         pcm->stream = slave->stream;
409         pcm->mode = slave->mode;
410         pcm->ops = &snd_pcm_alaw_ops;
411         pcm->op_arg = pcm;
412         pcm->fast_ops = &snd_pcm_plugin_fast_ops;
413         pcm->fast_op_arg = pcm;
414         pcm->private_data = alaw;
415         pcm->poll_fd = slave->poll_fd;
416         pcm->hw_ptr = &alaw->plug.hw_ptr;
417         pcm->appl_ptr = &alaw->plug.appl_ptr;
418         *pcmp = pcm;
419
420         return 0;
421 }
422
423 int _snd_pcm_alaw_open(snd_pcm_t **pcmp, const char *name,
424                        snd_config_t *root, snd_config_t *conf, 
425                        snd_pcm_stream_t stream, int mode)
426 {
427         snd_config_iterator_t i, next;
428         int err;
429         snd_pcm_t *spcm;
430         snd_config_t *slave = NULL, *sconf;
431         snd_pcm_format_t sformat;
432         const char *args;
433         snd_config_for_each(i, next, conf) {
434                 snd_config_t *n = snd_config_iterator_entry(i);
435                 const char *id = snd_config_get_id(n);
436                 if (snd_pcm_conf_generic_id(id))
437                         continue;
438                 if (strcmp(id, "slave") == 0) {
439                         slave = n;
440                         continue;
441                 }
442                 SNDERR("Unknown field %s", id);
443                 return -EINVAL;
444         }
445         if (!slave) {
446                 SNDERR("slave is not defined");
447                 return -EINVAL;
448         }
449         err = snd_pcm_slave_conf(root, slave, &sconf, &args, 1,
450                                  SND_PCM_HW_PARAM_FORMAT, 1, &sformat);
451         if (err < 0)
452                 return err;
453         if (snd_pcm_format_linear(sformat) != 1 &&
454             sformat != SND_PCM_FORMAT_A_LAW) {
455                 SNDERR("invalid slave format");
456                 return -EINVAL;
457         }
458         err = snd_pcm_open_slave(&spcm, root, sconf, args, stream, mode);
459         if (err < 0)
460                 return err;
461         err = snd_pcm_alaw_open(pcmp, name, sformat, spcm, 1);
462         if (err < 0)
463                 snd_pcm_close(spcm);
464         return err;
465 }
466                                 
467