OSDN Git Service

Added support for different mmap areas when running or stopped. Cleanings
[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)(snd_pcm_channel_area_t *src_areas,
27                         size_t src_offset,
28                         snd_pcm_channel_area_t *dst_areas,
29                         size_t dst_offset,
30                         size_t frames, size_t channels, int getputidx);
31
32 typedef struct {
33         /* This field need to be the first */
34         snd_pcm_plugin_t plug;
35         int getput_idx;
36         alaw_f func;
37         int sformat;
38         int cformat;
39         int cxfer_mode, cmmap_shape;
40 } snd_pcm_alaw_t;
41
42 static inline int val_seg(int val)
43 {
44         int r = 1;
45         val >>= 8;
46         if (val & 0xf0) {
47                 val >>= 4;
48                 r += 4;
49         }
50         if (val & 0x0c) {
51                 val >>= 2;
52                 r += 2;
53         }
54         if (val & 0x02)
55                 r += 1;
56         return r;
57 }
58
59 /*
60  * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law
61  *
62  * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data.
63  *
64  *              Linear Input Code       Compressed Code
65  *      ------------------------        ---------------
66  *      0000000wxyza                    000wxyz
67  *      0000001wxyza                    001wxyz
68  *      000001wxyzab                    010wxyz
69  *      00001wxyzabc                    011wxyz
70  *      0001wxyzabcd                    100wxyz
71  *      001wxyzabcde                    101wxyz
72  *      01wxyzabcdef                    110wxyz
73  *      1wxyzabcdefg                    111wxyz
74  *
75  * For further information see John C. Bellamy's Digital Telephony, 1982,
76  * John Wiley & Sons, pps 98-111 and 472-476.
77  */
78
79 static unsigned char s16_to_alaw(int pcm_val)
80 {
81         int             mask;
82         int             seg;
83         unsigned char   aval;
84
85         if (pcm_val >= 0) {
86                 mask = 0xD5;
87         } else {
88                 mask = 0x55;
89                 pcm_val = -pcm_val;
90                 if (pcm_val > 0x7fff)
91                         pcm_val = 0x7fff;
92         }
93
94         if (pcm_val < 256)
95                 aval = pcm_val >> 4;
96         else {
97                 /* Convert the scaled magnitude to segment number. */
98                 seg = val_seg(pcm_val);
99                 aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f);
100         }
101         return aval ^ mask;
102 }
103
104 /*
105  * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM
106  *
107  */
108 static int alaw_to_s16(unsigned char a_val)
109 {
110         int             t;
111         int             seg;
112
113         a_val ^= 0x55;
114         t = a_val & 0x7f;
115         if (t < 16)
116                 t = (t << 4) + 8;
117         else {
118                 seg = (t >> 4) & 0x07;
119                 t = ((t & 0x0f) << 4) + 0x108;
120                 t <<= seg -1;
121         }
122         return ((a_val & 0x80) ? t : -t);
123 }
124
125 static void alaw_decode(snd_pcm_channel_area_t *src_areas,
126                         size_t src_offset,
127                         snd_pcm_channel_area_t *dst_areas,
128                         size_t dst_offset,
129                         size_t frames, size_t channels, int putidx)
130 {
131 #define PUT_S16_LABELS
132 #include "plugin_ops.h"
133 #undef PUT_S16_LABELS
134         void *put = put_s16_labels[putidx];
135         size_t channel;
136         for (channel = 0; channel < channels; ++channel) {
137                 char *src;
138                 char *dst;
139                 int src_step, dst_step;
140                 size_t frames1;
141                 snd_pcm_channel_area_t *src_area = &src_areas[channel];
142                 snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
143 #if 0
144                 if (!src_area->enabled) {
145                         if (dst_area->wanted)
146                                 snd_pcm_area_silence(&dst_areas[channel], dst_offset, frames, dst_sfmt);
147                         dst_area->enabled = 0;
148                         continue;
149                 }
150                 dst_area->enabled = 1;
151 #endif
152                 src = snd_pcm_channel_area_addr(src_area, src_offset);
153                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
154                 src_step = snd_pcm_channel_area_step(src_area);
155                 dst_step = snd_pcm_channel_area_step(dst_area);
156                 frames1 = frames;
157                 while (frames1-- > 0) {
158                         int16_t sample = alaw_to_s16(*src);
159                         goto *put;
160 #define PUT_S16_END after
161 #include "plugin_ops.h"
162 #undef PUT_S16_END
163                 after:
164                         src += src_step;
165                         dst += dst_step;
166                 }
167         }
168 }
169
170 static void alaw_encode(snd_pcm_channel_area_t *src_areas,
171                          size_t src_offset,
172                          snd_pcm_channel_area_t *dst_areas,
173                          size_t dst_offset,
174                          size_t frames, size_t channels, int getidx)
175 {
176 #define GET_S16_LABELS
177 #include "plugin_ops.h"
178 #undef GET_S16_LABELS
179         void *get = get_s16_labels[getidx];
180         size_t channel;
181         int16_t sample = 0;
182         for (channel = 0; channel < channels; ++channel) {
183                 char *src;
184                 char *dst;
185                 int src_step, dst_step;
186                 size_t frames1;
187                 snd_pcm_channel_area_t *src_area = &src_areas[channel];
188                 snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
189 #if 0
190                 if (!src_area->enabled) {
191                         if (dst_area->wanted)
192                                 snd_pcm_area_silence(&dst_area->area, 0, frames, dst_sfmt);
193                         dst_area->enabled = 0;
194                         continue;
195                 }
196                 dst_area->enabled = 1;
197 #endif
198                 src = snd_pcm_channel_area_addr(src_area, src_offset);
199                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
200                 src_step = snd_pcm_channel_area_step(src_area);
201                 dst_step = snd_pcm_channel_area_step(dst_area);
202                 frames1 = frames;
203                 while (frames1-- > 0) {
204                         goto *get;
205 #define GET_S16_END after
206 #include "plugin_ops.h"
207 #undef GET_S16_END
208                 after:
209                         *dst = s16_to_alaw(sample);
210                         src += src_step;
211                         dst += dst_step;
212                 }
213         }
214 }
215
216 static int snd_pcm_alaw_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t * info)
217 {
218         snd_pcm_alaw_t *alaw = pcm->private;
219         unsigned int req_mask = info->req_mask;
220         unsigned int sfmt = info->req.format.sfmt;
221         int err;
222         if (req_mask & SND_PCM_PARAMS_SFMT) {
223                 if (alaw->sformat == SND_PCM_SFMT_A_LAW ?
224                     !snd_pcm_format_linear(sfmt) :
225                     sfmt != SND_PCM_SFMT_A_LAW) {
226                         info->req.fail_mask = SND_PCM_PARAMS_SFMT;
227                         info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
228                         return -EINVAL;
229                 }
230         }
231         info->req_mask |= SND_PCM_PARAMS_SFMT;
232         info->req_mask &= ~(SND_PCM_PARAMS_MMAP_SHAPE | 
233                             SND_PCM_PARAMS_XFER_MODE);
234         info->req.format.sfmt = alaw->sformat;
235         err = snd_pcm_params_info(alaw->plug.slave, info);
236         info->req_mask = req_mask;
237         info->req.format.sfmt = sfmt;
238         if (err < 0)
239                 return err;
240         if (req_mask & SND_PCM_PARAMS_SFMT)
241                 info->formats = 1 << sfmt;
242         else
243                 info->formats = alaw->sformat == SND_PCM_SFMT_A_LAW ?
244                         SND_PCM_LINEAR_FORMATS : 1 << SND_PCM_SFMT_A_LAW;
245         info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
246         info->flags |= SND_PCM_INFO_INTERLEAVED | SND_PCM_INFO_NONINTERLEAVED;
247         return err;
248 }
249
250 static int snd_pcm_alaw_params(snd_pcm_t *pcm, snd_pcm_params_t * params)
251 {
252         snd_pcm_alaw_t *alaw = pcm->private;
253         snd_pcm_t *slave = alaw->plug.slave;
254         int err;
255         if (alaw->sformat == SND_PCM_SFMT_A_LAW ?
256             !snd_pcm_format_linear(params->format.sfmt) :
257             params->format.sfmt != SND_PCM_SFMT_A_LAW) {
258                 params->fail_mask = SND_PCM_PARAMS_SFMT;
259                 params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
260                 return -EINVAL;
261         }
262         if (slave->mmap_data) {
263                 err = snd_pcm_munmap_data(slave);
264                 if (err < 0)
265                         return err;
266         }
267         alaw->cformat = params->format.sfmt;
268         alaw->cxfer_mode = params->xfer_mode;
269         alaw->cmmap_shape = params->mmap_shape;
270         params->format.sfmt = alaw->sformat;
271         params->xfer_mode = SND_PCM_XFER_UNSPECIFIED;
272         params->mmap_shape = SND_PCM_MMAP_UNSPECIFIED;;
273         err = snd_pcm_params(slave, params);
274         params->format.sfmt = alaw->cformat;
275         params->xfer_mode = alaw->cxfer_mode;
276         params->mmap_shape = alaw->cmmap_shape;
277         if (slave->valid_setup) {
278                 int r = snd_pcm_mmap_data(slave, NULL);
279                 assert(r >= 0);
280         }
281         return err;
282 }
283
284 static int snd_pcm_alaw_setup(snd_pcm_t *pcm, snd_pcm_setup_t * setup)
285 {
286         snd_pcm_alaw_t *alaw = pcm->private;
287         int err = snd_pcm_setup(alaw->plug.slave, setup);
288         if (err < 0)
289                 return err;
290         assert(alaw->sformat == setup->format.sfmt);
291         if (alaw->cxfer_mode == SND_PCM_XFER_UNSPECIFIED)
292                 setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
293         else
294                 setup->xfer_mode = alaw->cxfer_mode;
295         if (alaw->cmmap_shape == SND_PCM_MMAP_UNSPECIFIED)
296                 setup->mmap_shape = SND_PCM_MMAP_NONINTERLEAVED;
297         else
298                 setup->mmap_shape = alaw->cmmap_shape;
299         setup->format.sfmt = alaw->cformat;
300         setup->mmap_bytes = 0;
301         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
302                 if (alaw->sformat == SND_PCM_SFMT_A_LAW) {
303                         alaw->getput_idx = getput_index(alaw->cformat);
304                         alaw->func = alaw_encode;
305                 } else {
306                         alaw->getput_idx = getput_index(alaw->sformat);
307                         alaw->func = alaw_decode;
308                 }
309         } else {
310                 if (alaw->sformat == SND_PCM_SFMT_A_LAW) {
311                         alaw->getput_idx = getput_index(alaw->cformat);
312                         alaw->func = alaw_decode;
313                 } else {
314                         alaw->getput_idx = getput_index(alaw->sformat);
315                         alaw->func = alaw_encode;
316                 }
317         }
318         return 0;
319 }
320
321 static ssize_t snd_pcm_alaw_write_areas(snd_pcm_t *pcm,
322                                         snd_pcm_channel_area_t *areas,
323                                         size_t offset,
324                                         size_t size,
325                                         size_t *slave_sizep)
326 {
327         snd_pcm_alaw_t *alaw = pcm->private;
328         snd_pcm_t *slave = alaw->plug.slave;
329         size_t xfer = 0;
330         ssize_t err = 0;
331         if (slave_sizep && *slave_sizep < size)
332                 size = *slave_sizep;
333         assert(size > 0);
334         while (xfer < size) {
335                 size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer);
336                 alaw->func(areas, offset, 
337                             snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
338                             frames, pcm->setup.format.channels,
339                             alaw->getput_idx);
340                 err = snd_pcm_mmap_forward(slave, frames);
341                 if (err < 0)
342                         break;
343                 assert((size_t)err == frames);
344                 offset += err;
345                 xfer += err;
346                 snd_pcm_mmap_hw_forward(pcm, err);
347         }
348         if (xfer > 0) {
349                 if (slave_sizep)
350                         *slave_sizep = xfer;
351                 return xfer;
352         }
353         return err;
354 }
355
356 static ssize_t snd_pcm_alaw_read_areas(snd_pcm_t *pcm,
357                                        snd_pcm_channel_area_t *areas,
358                                        size_t offset,
359                                        size_t size,
360                                        size_t *slave_sizep)
361 {
362         snd_pcm_alaw_t *alaw = pcm->private;
363         snd_pcm_t *slave = alaw->plug.slave;
364         size_t xfer = 0;
365         ssize_t err = 0;
366         if (slave_sizep && *slave_sizep < size)
367                 size = *slave_sizep;
368         assert(size > 0);
369         while (xfer < size) {
370                 size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer);
371                 alaw->func(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave),
372                            areas, offset, 
373                            frames, pcm->setup.format.channels,
374                            alaw->getput_idx);
375                 err = snd_pcm_mmap_forward(slave, frames);
376                 if (err < 0)
377                         break;
378                 assert((size_t)err == frames);
379                 offset += err;
380                 xfer += err;
381                 snd_pcm_mmap_hw_forward(pcm, err);
382         }
383         if (xfer > 0) {
384                 if (slave_sizep)
385                         *slave_sizep = xfer;
386                 return xfer;
387         }
388         return err;
389 }
390
391 static void snd_pcm_alaw_dump(snd_pcm_t *pcm, FILE *fp)
392 {
393         snd_pcm_alaw_t *alaw = pcm->private;
394         fprintf(fp, "A-Law conversion PCM (%s)\n", 
395                 snd_pcm_format_name(alaw->sformat));
396         if (pcm->valid_setup) {
397                 fprintf(fp, "Its setup is:\n");
398                 snd_pcm_dump_setup(pcm, fp);
399         }
400         fprintf(fp, "Slave: ");
401         snd_pcm_dump(alaw->plug.slave, fp);
402 }
403
404 struct snd_pcm_ops snd_pcm_alaw_ops = {
405         close: snd_pcm_plugin_close,
406         info: snd_pcm_plugin_info,
407         params_info: snd_pcm_alaw_params_info,
408         params: snd_pcm_alaw_params,
409         setup: snd_pcm_alaw_setup,
410         channel_info: snd_pcm_plugin_channel_info,
411         channel_params: snd_pcm_plugin_channel_params,
412         channel_setup: snd_pcm_plugin_channel_setup,
413         dump: snd_pcm_alaw_dump,
414         nonblock: snd_pcm_plugin_nonblock,
415         mmap_status: snd_pcm_plugin_mmap_status,
416         mmap_control: snd_pcm_plugin_mmap_control,
417         mmap_data: snd_pcm_plugin_mmap_data,
418         munmap_status: snd_pcm_plugin_munmap_status,
419         munmap_control: snd_pcm_plugin_munmap_control,
420         munmap_data: snd_pcm_plugin_munmap_data,
421 };
422
423 int snd_pcm_alaw_open(snd_pcm_t **handlep, int sformat, snd_pcm_t *slave, int close_slave)
424 {
425         snd_pcm_t *handle;
426         snd_pcm_alaw_t *alaw;
427         int err;
428         assert(handlep && slave);
429         if (snd_pcm_format_linear(sformat) != 1 &&
430             sformat != SND_PCM_SFMT_A_LAW)
431                 return -EINVAL;
432         alaw = calloc(1, sizeof(snd_pcm_alaw_t));
433         if (!alaw) {
434                 return -ENOMEM;
435         }
436         alaw->sformat = sformat;
437         alaw->plug.read = snd_pcm_alaw_read_areas;
438         alaw->plug.write = snd_pcm_alaw_write_areas;
439         alaw->plug.slave = slave;
440         alaw->plug.close_slave = close_slave;
441
442         handle = calloc(1, sizeof(snd_pcm_t));
443         if (!handle) {
444                 free(alaw);
445                 return -ENOMEM;
446         }
447         handle->type = SND_PCM_TYPE_ALAW;
448         handle->stream = slave->stream;
449         handle->ops = &snd_pcm_alaw_ops;
450         handle->op_arg = handle;
451         handle->fast_ops = &snd_pcm_plugin_fast_ops;
452         handle->fast_op_arg = handle;
453         handle->mode = slave->mode;
454         handle->private = alaw;
455         err = snd_pcm_init(handle);
456         if (err < 0) {
457                 snd_pcm_close(handle);
458                 return err;
459         }
460         *handlep = handle;
461
462         return 0;
463 }
464
465 int _snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name,
466                          snd_config_t *conf, 
467                          int stream, int mode)
468 {
469         snd_config_iterator_t i;
470         char *sname = NULL;
471         int err;
472         snd_pcm_t *spcm;
473         int sformat = -1;
474         snd_config_foreach(i, conf) {
475                 snd_config_t *n = snd_config_entry(i);
476                 if (strcmp(n->id, "comment") == 0)
477                         continue;
478                 if (strcmp(n->id, "type") == 0)
479                         continue;
480                 if (strcmp(n->id, "stream") == 0)
481                         continue;
482                 if (strcmp(n->id, "sname") == 0) {
483                         err = snd_config_string_get(n, &sname);
484                         if (err < 0)
485                                 return -EINVAL;
486                         continue;
487                 }
488                 if (strcmp(n->id, "sformat") == 0) {
489                         char *f;
490                         err = snd_config_string_get(n, &f);
491                         if (err < 0)
492                                 return -EINVAL;
493                         sformat = snd_pcm_format_value(f);
494                         if (sformat < 0)
495                                 return -EINVAL;
496                         if (snd_pcm_format_linear(sformat) != 1 &&
497                             sformat != SND_PCM_SFMT_A_LAW)
498                                 return -EINVAL;
499                         continue;
500                 }
501                 return -EINVAL;
502         }
503         if (!sname || !sformat)
504                 return -EINVAL;
505         /* This is needed cause snd_config_update may destroy config */
506         sname = strdup(sname);
507         if (!sname)
508                 return  -ENOMEM;
509         err = snd_pcm_open(&spcm, sname, stream, mode);
510         free(sname);
511         if (err < 0)
512                 return err;
513         err = snd_pcm_alaw_open(pcmp, sformat, spcm, 1);
514         if (err < 0)
515                 snd_pcm_close(spcm);
516         return err;
517 }
518                                 
519