62eb398e223ab7c535e9f2832bc0f797501837e9
[platform/upstream/alsa-lib.git] / src / pcm / pcm_lfloat.c
1 /**
2  * \file pcm/pcm_lfloat.c
3  * \ingroup PCM_Plugins
4  * \brief PCM Linear<->Float Conversion Plugin Interface
5  * \author Jaroslav Kysela <perex@perex.cz>
6  * \date 2001
7  */
8 /*
9  *  PCM - Linear Integer <-> Linear Float conversion
10  *  Copyright (c) 2001 by Jaroslav Kysela <perex@perex.cz>
11  *
12  *
13  *   This library is free software; you can redistribute it and/or modify
14  *   it under the terms of the GNU Lesser General Public License as
15  *   published by the Free Software Foundation; either version 2.1 of
16  *   the License, or (at your option) any later version.
17  *
18  *   This program is distributed in the hope that it will be useful,
19  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
20  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  *   GNU Lesser General Public License for more details.
22  *
23  *   You should have received a copy of the GNU Lesser General Public
24  *   License along with this library; if not, write to the Free Software
25  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
26  *
27  */
28   
29 #include <byteswap.h>
30 #include "pcm_local.h"
31 #include "pcm_plugin.h"
32
33 #include "plugin_ops.h"
34
35 #ifndef DOC_HIDDEN
36
37 typedef float float_t;
38 typedef double double_t;
39
40 #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ <= 91)
41 #define BUGGY_GCC
42 #endif
43
44 #ifndef PIC
45 /* entry for static linking */
46 const char *_snd_module_pcm_lfloat = "";
47 #endif
48
49 typedef struct {
50         /* This field need to be the first */
51         snd_pcm_plugin_t plug;
52         unsigned int int32_idx;
53         unsigned int float32_idx;
54         snd_pcm_format_t sformat;
55         void (*func)(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
56                      const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
57                      unsigned int channels, snd_pcm_uframes_t frames,
58                      unsigned int get32idx, unsigned int put32floatidx);
59 } snd_pcm_lfloat_t;
60
61 int snd_pcm_lfloat_get_s32_index(snd_pcm_format_t format)
62 {
63         int width, endian;
64
65         switch (format) {
66         case SND_PCM_FORMAT_FLOAT_LE:
67         case SND_PCM_FORMAT_FLOAT_BE:
68                 width = 32;
69                 break;
70         case SND_PCM_FORMAT_FLOAT64_LE:
71         case SND_PCM_FORMAT_FLOAT64_BE:
72                 width = 64;
73                 break;
74         default:
75                 return -EINVAL;
76         }
77 #ifdef SND_LITTLE_ENDIAN
78         endian = snd_pcm_format_big_endian(format);
79 #else
80         endian = snd_pcm_format_little_endian(format);
81 #endif
82         return ((width / 32)-1) * 2 + endian;
83 }
84
85 int snd_pcm_lfloat_put_s32_index(snd_pcm_format_t format)
86 {
87         return snd_pcm_lfloat_get_s32_index(format);
88 }
89
90 #endif /* DOC_HIDDEN */
91
92 #ifndef BUGGY_GCC
93
94 #ifndef DOC_HIDDEN
95
96 void snd_pcm_lfloat_convert_integer_float(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
97                                           const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
98                                           unsigned int channels, snd_pcm_uframes_t frames,
99                                           unsigned int get32idx, unsigned int put32floatidx)
100 {
101 #define GET32_LABELS
102 #define PUT32F_LABELS
103 #include "plugin_ops.h"
104 #undef PUT32F_LABELS
105 #undef GET32_LABELS
106         void *get32 = get32_labels[get32idx];
107         void *put32float = put32float_labels[put32floatidx];
108         unsigned int channel;
109         for (channel = 0; channel < channels; ++channel) {
110                 const char *src;
111                 char *dst;
112                 int src_step, dst_step;
113                 snd_pcm_uframes_t frames1;
114                 int32_t sample = 0;
115                 snd_tmp_float_t tmp_float;
116                 snd_tmp_double_t tmp_double;
117                 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
118                 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
119                 src = snd_pcm_channel_area_addr(src_area, src_offset);
120                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
121                 src_step = snd_pcm_channel_area_step(src_area);
122                 dst_step = snd_pcm_channel_area_step(dst_area);
123                 frames1 = frames;
124                 while (frames1-- > 0) {
125                         goto *get32;
126 #define GET32_END sample_loaded
127 #include "plugin_ops.h"
128 #undef GET32_END
129                 sample_loaded:
130                         goto *put32float;
131 #define PUT32F_END sample_put
132 #include "plugin_ops.h"
133 #undef PUT32F_END
134                 sample_put:
135                         src += src_step;
136                         dst += dst_step;
137                 }
138         }
139 }
140
141 void snd_pcm_lfloat_convert_float_integer(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
142                                           const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
143                                           unsigned int channels, snd_pcm_uframes_t frames,
144                                           unsigned int put32idx, unsigned int get32floatidx)
145 {
146 #define PUT32_LABELS
147 #define GET32F_LABELS
148 #include "plugin_ops.h"
149 #undef GET32F_LABELS
150 #undef PUT32_LABELS
151         void *put32 = put32_labels[put32idx];
152         void *get32float = get32float_labels[get32floatidx];
153         unsigned int channel;
154         for (channel = 0; channel < channels; ++channel) {
155                 const char *src;
156                 char *dst;
157                 int src_step, dst_step;
158                 snd_pcm_uframes_t frames1;
159                 int32_t sample = 0;
160                 snd_tmp_float_t tmp_float;
161                 snd_tmp_double_t tmp_double;
162                 const snd_pcm_channel_area_t *src_area = &src_areas[channel];
163                 const snd_pcm_channel_area_t *dst_area = &dst_areas[channel];
164                 src = snd_pcm_channel_area_addr(src_area, src_offset);
165                 dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
166                 src_step = snd_pcm_channel_area_step(src_area);
167                 dst_step = snd_pcm_channel_area_step(dst_area);
168                 frames1 = frames;
169                 while (frames1-- > 0) {
170                         goto *get32float;
171 #define GET32F_END sample_loaded
172 #include "plugin_ops.h"
173 #undef GET32F_END
174                 sample_loaded:
175                         goto *put32;
176 #define PUT32_END sample_put
177 #include "plugin_ops.h"
178 #undef PUT32_END
179                 sample_put:
180                         src += src_step;
181                         dst += dst_step;
182                 }
183         }
184 }
185
186 #endif /* DOC_HIDDEN */
187
188 static int snd_pcm_lfloat_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
189 {
190         snd_pcm_lfloat_t *lfloat = pcm->private_data;
191         int err;
192         snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM };
193         snd_pcm_format_mask_t lformat_mask = { SND_PCM_FMTBIT_LINEAR };
194         snd_pcm_format_mask_t fformat_mask = { SND_PCM_FMTBIT_FLOAT };
195         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
196                                          &access_mask);
197         if (err < 0)
198                 return err;
199         err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT,
200                                          snd_pcm_format_linear(lfloat->sformat) ?
201                                          &fformat_mask : &lformat_mask);
202         if (err < 0)
203                 return err;
204         err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD);
205         if (err < 0)
206                 return err;
207         params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
208         return 0;
209 }
210
211 static int snd_pcm_lfloat_hw_refine_sprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *sparams)
212 {
213         snd_pcm_lfloat_t *lfloat = pcm->private_data;
214         snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
215         _snd_pcm_hw_params_any(sparams);
216         _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
217                                    &saccess_mask);
218         _snd_pcm_hw_params_set_format(sparams, lfloat->sformat);
219         _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD);
220         return 0;
221 }
222
223 static int snd_pcm_lfloat_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
224                                             snd_pcm_hw_params_t *sparams)
225 {
226         int err;
227         unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
228                               SND_PCM_HW_PARBIT_RATE |
229                               SND_PCM_HW_PARBIT_PERIOD_SIZE |
230                               SND_PCM_HW_PARBIT_BUFFER_SIZE |
231                               SND_PCM_HW_PARBIT_PERIODS |
232                               SND_PCM_HW_PARBIT_PERIOD_TIME |
233                               SND_PCM_HW_PARBIT_BUFFER_TIME |
234                               SND_PCM_HW_PARBIT_TICK_TIME);
235         err = _snd_pcm_hw_params_refine(sparams, links, params);
236         if (err < 0)
237                 return err;
238         return 0;
239 }
240         
241 static int snd_pcm_lfloat_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params,
242                                             snd_pcm_hw_params_t *sparams)
243 {
244         int err;
245         unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS |
246                               SND_PCM_HW_PARBIT_RATE |
247                               SND_PCM_HW_PARBIT_PERIOD_SIZE |
248                               SND_PCM_HW_PARBIT_BUFFER_SIZE |
249                               SND_PCM_HW_PARBIT_PERIODS |
250                               SND_PCM_HW_PARBIT_PERIOD_TIME |
251                               SND_PCM_HW_PARBIT_BUFFER_TIME |
252                               SND_PCM_HW_PARBIT_TICK_TIME);
253         err = _snd_pcm_hw_params_refine(params, links, sparams);
254         if (err < 0)
255                 return err;
256         return 0;
257 }
258
259 static int snd_pcm_lfloat_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
260 {
261         return snd_pcm_hw_refine_slave(pcm, params,
262                                        snd_pcm_lfloat_hw_refine_cprepare,
263                                        snd_pcm_lfloat_hw_refine_cchange,
264                                        snd_pcm_lfloat_hw_refine_sprepare,
265                                        snd_pcm_lfloat_hw_refine_schange,
266                                        snd_pcm_generic_hw_refine);
267 }
268
269 static int snd_pcm_lfloat_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
270 {
271         snd_pcm_lfloat_t *lfloat = pcm->private_data;
272         snd_pcm_t *slave = lfloat->plug.gen.slave;
273         snd_pcm_format_t src_format, dst_format;
274         int err = snd_pcm_hw_params_slave(pcm, params,
275                                           snd_pcm_lfloat_hw_refine_cchange,
276                                           snd_pcm_lfloat_hw_refine_sprepare,
277                                           snd_pcm_lfloat_hw_refine_schange,
278                                           snd_pcm_generic_hw_params);
279         if (err < 0)
280                 return err;
281         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
282                 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &src_format);
283                 dst_format = slave->format;
284         } else {
285                 src_format = slave->format;
286                 err = INTERNAL(snd_pcm_hw_params_get_format)(params, &dst_format);
287         }
288         if (snd_pcm_format_linear(src_format)) {
289                 lfloat->int32_idx = snd_pcm_linear_get32_index(src_format, SND_PCM_FORMAT_S32);
290                 lfloat->float32_idx = snd_pcm_lfloat_put_s32_index(dst_format);
291                 lfloat->func = snd_pcm_lfloat_convert_integer_float;
292         } else {
293                 lfloat->int32_idx = snd_pcm_linear_put32_index(SND_PCM_FORMAT_S32, dst_format);
294                 lfloat->float32_idx = snd_pcm_lfloat_get_s32_index(src_format);
295                 lfloat->func = snd_pcm_lfloat_convert_float_integer;
296         }
297         return 0;
298 }
299
300 static snd_pcm_uframes_t
301 snd_pcm_lfloat_write_areas(snd_pcm_t *pcm,
302                            const snd_pcm_channel_area_t *areas,
303                            snd_pcm_uframes_t offset,
304                            snd_pcm_uframes_t size,
305                            const snd_pcm_channel_area_t *slave_areas,
306                            snd_pcm_uframes_t slave_offset,
307                            snd_pcm_uframes_t *slave_sizep)
308 {
309         snd_pcm_lfloat_t *lfloat = pcm->private_data;
310         if (size > *slave_sizep)
311                 size = *slave_sizep;
312         lfloat->func(slave_areas, slave_offset,
313                      areas, offset, 
314                      pcm->channels, size,
315                      lfloat->int32_idx, lfloat->float32_idx);
316         *slave_sizep = size;
317         return size;
318 }
319
320 static snd_pcm_uframes_t
321 snd_pcm_lfloat_read_areas(snd_pcm_t *pcm,
322                           const snd_pcm_channel_area_t *areas,
323                           snd_pcm_uframes_t offset,
324                           snd_pcm_uframes_t size,
325                           const snd_pcm_channel_area_t *slave_areas,
326                           snd_pcm_uframes_t slave_offset,
327                           snd_pcm_uframes_t *slave_sizep)
328 {
329         snd_pcm_lfloat_t *lfloat = pcm->private_data;
330         if (size > *slave_sizep)
331                 size = *slave_sizep;
332         lfloat->func(areas, offset, 
333                      slave_areas, slave_offset,
334                      pcm->channels, size,
335                      lfloat->int32_idx, lfloat->float32_idx);
336         *slave_sizep = size;
337         return size;
338 }
339
340 static void snd_pcm_lfloat_dump(snd_pcm_t *pcm, snd_output_t *out)
341 {
342         snd_pcm_lfloat_t *lfloat = pcm->private_data;
343         snd_output_printf(out, "Linear Integer <-> Linear Float conversion PCM (%s)\n", 
344                 snd_pcm_format_name(lfloat->sformat));
345         if (pcm->setup) {
346                 snd_output_printf(out, "Its setup is:\n");
347                 snd_pcm_dump_setup(pcm, out);
348         }
349         snd_output_printf(out, "Slave: ");
350         snd_pcm_dump(lfloat->plug.gen.slave, out);
351 }
352
353 static const snd_pcm_ops_t snd_pcm_lfloat_ops = {
354         .close = snd_pcm_generic_close,
355         .info = snd_pcm_generic_info,
356         .hw_refine = snd_pcm_lfloat_hw_refine,
357         .hw_params = snd_pcm_lfloat_hw_params,
358         .hw_free = snd_pcm_generic_hw_free,
359         .sw_params = snd_pcm_generic_sw_params,
360         .channel_info = snd_pcm_generic_channel_info,
361         .dump = snd_pcm_lfloat_dump,
362         .nonblock = snd_pcm_generic_nonblock,
363         .async = snd_pcm_generic_async,
364         .mmap = snd_pcm_generic_mmap,
365         .munmap = snd_pcm_generic_munmap,
366 };
367
368 /**
369  * \brief Creates a new linear conversion PCM
370  * \param pcmp Returns created PCM handle
371  * \param name Name of PCM
372  * \param sformat Slave (destination) format
373  * \param slave Slave PCM handle
374  * \param close_slave When set, the slave PCM handle is closed with copy PCM
375  * \retval zero on success otherwise a negative error code
376  * \warning Using of this function might be dangerous in the sense
377  *          of compatibility reasons. The prototype might be freely
378  *          changed in future.
379  */
380 int snd_pcm_lfloat_open(snd_pcm_t **pcmp, const char *name, snd_pcm_format_t sformat, snd_pcm_t *slave, int close_slave)
381 {
382         snd_pcm_t *pcm;
383         snd_pcm_lfloat_t *lfloat;
384         int err;
385         assert(pcmp && slave);
386         if (snd_pcm_format_linear(sformat) != 1 &&
387             snd_pcm_format_float(sformat) != 1)
388                 return -EINVAL;
389         lfloat = calloc(1, sizeof(snd_pcm_lfloat_t));
390         if (!lfloat) {
391                 return -ENOMEM;
392         }
393         snd_pcm_plugin_init(&lfloat->plug);
394         lfloat->sformat = sformat;
395         lfloat->plug.read = snd_pcm_lfloat_read_areas;
396         lfloat->plug.write = snd_pcm_lfloat_write_areas;
397         lfloat->plug.undo_read = snd_pcm_plugin_undo_read_generic;
398         lfloat->plug.undo_write = snd_pcm_plugin_undo_write_generic;
399         lfloat->plug.gen.slave = slave;
400         lfloat->plug.gen.close_slave = close_slave;
401
402         err = snd_pcm_new(&pcm, SND_PCM_TYPE_LINEAR_FLOAT, name, slave->stream, slave->mode);
403         if (err < 0) {
404                 free(lfloat);
405                 return err;
406         }
407         pcm->ops = &snd_pcm_lfloat_ops;
408         pcm->fast_ops = &snd_pcm_plugin_fast_ops;
409         pcm->private_data = lfloat;
410         pcm->poll_fd = slave->poll_fd;
411         pcm->poll_events = slave->poll_events;
412         pcm->monotonic = slave->monotonic;
413         snd_pcm_set_hw_ptr(pcm, &lfloat->plug.hw_ptr, -1, 0);
414         snd_pcm_set_appl_ptr(pcm, &lfloat->plug.appl_ptr, -1, 0);
415         *pcmp = pcm;
416         
417         return 0;
418 }
419
420 /*! \page pcm_plugins
421
422 \section pcm_plugins_lfloat Plugin: linear<->float
423
424 This plugin converts linear to float samples and float to linear samples from master
425 linear<->float conversion PCM to given slave PCM. The channel count, format and rate must
426 match for both of them.
427
428 \code
429 pcm.name {
430         type lfloat             # Linear<->Float conversion PCM
431         slave STR               # Slave name
432         # or
433         slave {                 # Slave definition
434                 pcm STR         # Slave PCM name
435                 # or
436                 pcm { }         # Slave PCM definition
437                 format STR      # Slave format
438         }
439 }
440 \endcode
441
442 \subsection pcm_plugins_lfloat_funcref Function reference
443
444 <UL>
445   <LI>snd_pcm_lfloat_open()
446   <LI>_snd_pcm_lfloat_open()
447 </UL>
448
449 */
450
451 /**
452  * \brief Creates a new linear<->float conversion PCM
453  * \param pcmp Returns created PCM handle
454  * \param name Name of PCM
455  * \param root Root configuration node
456  * \param conf Configuration node with copy PCM description
457  * \param stream Stream type
458  * \param mode Stream mode
459  * \retval zero on success otherwise a negative error code
460  * \warning Using of this function might be dangerous in the sense
461  *          of compatibility reasons. The prototype might be freely
462  *          changed in future.
463  */
464 int _snd_pcm_lfloat_open(snd_pcm_t **pcmp, const char *name,
465                          snd_config_t *root, snd_config_t *conf, 
466                          snd_pcm_stream_t stream, int mode)
467 {
468         snd_config_iterator_t i, next;
469         int err;
470         snd_pcm_t *spcm;
471         snd_config_t *slave = NULL, *sconf;
472         snd_pcm_format_t sformat;
473         snd_config_for_each(i, next, conf) {
474                 snd_config_t *n = snd_config_iterator_entry(i);
475                 const char *id;
476                 if (snd_config_get_id(n, &id) < 0)
477                         continue;
478                 if (snd_pcm_conf_generic_id(id))
479                         continue;
480                 if (strcmp(id, "slave") == 0) {
481                         slave = n;
482                         continue;
483                 }
484                 SNDERR("Unknown field %s", id);
485                 return -EINVAL;
486         }
487         if (!slave) {
488                 SNDERR("slave is not defined");
489                 return -EINVAL;
490         }
491         err = snd_pcm_slave_conf(root, slave, &sconf, 1,
492                                  SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, &sformat);
493         if (err < 0)
494                 return err;
495         if (snd_pcm_format_linear(sformat) != 1 &&
496             snd_pcm_format_float(sformat) != 1) {
497                 snd_config_delete(sconf);
498                 SNDERR("slave format is not linear integer or linear float");
499                 return -EINVAL;
500         }
501         err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf);
502         snd_config_delete(sconf);
503         if (err < 0)
504                 return err;
505         err = snd_pcm_lfloat_open(pcmp, name, sformat, spcm, 1);
506         if (err < 0)
507                 snd_pcm_close(spcm);
508         return err;
509 }
510 #ifndef DOC_HIDDEN
511 SND_DLSYM_BUILD_VERSION(_snd_pcm_lfloat_open, SND_PCM_DLSYM_VERSION);
512 #endif
513
514 #else /* BUGGY_GCC */
515
516 int snd_pcm_lfloat_open(snd_pcm_t **pcmp ATTRIBUTE_UNUSED,
517                         const char *name ATTRIBUTE_UNUSED,
518                         snd_pcm_format_t sformat ATTRIBUTE_UNUSED,
519                         snd_pcm_t *slave ATTRIBUTE_UNUSED,
520                         int close_slave ATTRIBUTE_UNUSED)
521 {
522         SNDERR("please, upgrade your GCC to use lfloat plugin");
523         return -EINVAL;
524 }
525
526 int _snd_pcm_lfloat_open(snd_pcm_t **pcmp ATTRIBUTE_UNUSED,
527                          const char *name ATTRIBUTE_UNUSED,
528                          snd_config_t *root ATTRIBUTE_UNUSED,
529                          snd_config_t *conf ATTRIBUTE_UNUSED, 
530                          snd_pcm_stream_t stream ATTRIBUTE_UNUSED,
531                          int mode ATTRIBUTE_UNUSED)
532 {
533         SNDERR("please, upgrade your GCC to use lfloat plugin");
534         return -EINVAL;
535 }
536
537 #endif /* BUGGY_GCC */