1 /*****************************************************************
2  * gmerlin - a general purpose multimedia framework and applications
3  *
4  * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5  * gmerlin-general@lists.sourceforge.net
6  * http://gmerlin.sourceforge.net
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  * *****************************************************************/
21 
22 #include <stdio.h>
23 #include <string.h>
24 
25 #include <sys/ioctl.h>
26 #include <sys/soundcard.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 
34 #include <config.h>
35 #include <gmerlin/translation.h>
36 
37 #include <gmerlin/plugin.h>
38 #include <gmerlin/utils.h>
39 
40 #include <gmerlin/log.h>
41 #define LOG_DOMAIN "oa_alsa"
42 
43 #include "alsa_common.h"
44 
45 /* Playback modes */
46 
47 #define PLAYBACK_NONE       0
48 #define PLAYBACK_GENERIC    1
49 #define PLAYBACK_USER       2
50 #define PLAYBACK_SURROUND40 3
51 #define PLAYBACK_SURROUND41 4
52 #define PLAYBACK_SURROUND50 5
53 #define PLAYBACK_SURROUND51 6
54 
55 
56 static const bg_parameter_info_t global_parameters[] =
57   {
58     {
59       .name =        "surround40",
60       .long_name =   TRS("Enable 4.0 Surround"),
61       .type =        BG_PARAMETER_CHECKBUTTON,
62       .val_default = { .val_i = 1 },
63       .help_string = TRS("Use the surround 4.0 (aka quadrophonic) device")
64     },
65     {
66       .name =        "surround41",
67       .long_name =   TRS("Enable 4.1 Surround"),
68       .type =        BG_PARAMETER_CHECKBUTTON,
69       .val_default = { .val_i = 1 },
70       .help_string = TRS("Use the surround 4.1 device")
71     },
72     {
73       .name =        "surround50",
74       .long_name =   TRS("Enable 5.0 Surround"),
75       .type =        BG_PARAMETER_CHECKBUTTON,
76       .val_default = { .val_i = 1 },
77       .help_string = TRS("Use the surround 5.0 device")
78     },
79     {
80       .name =        "surround51",
81       .long_name =   TRS("Enable 5.1 Surround"),
82       .type =        BG_PARAMETER_CHECKBUTTON,
83       .val_default = { .val_i = 1 },
84       .help_string = TRS("Use the surround 5.1 device")
85     },
86     {
87       .name =        "user_device",
88       .long_name =   TRS("User device"),
89       .type =        BG_PARAMETER_STRING,
90       .help_string = TRS("Enter a custom device to use for playback. Leave empty to use the\
91  settings above"),
92     },
93     {
94       .name =        "buffer_time",
95       .long_name =   TRS("Buffer time"),
96       .type =        BG_PARAMETER_INT,
97       .val_min =     { .val_i = 10    },
98       .val_max =     { .val_i = 10000 },
99       .val_default = { .val_i = 1000  },
100       .help_string = TRS("Set the buffer time (in milliseconds). Larger values \
101 improve playback performance on slow systems under load. Smaller values \
102 decrease the latency of the volume control."),
103     },
104   };
105 
106 static const int num_global_parameters =
107   sizeof(global_parameters)/sizeof(global_parameters[0]);
108 
109 typedef struct
110   {
111   bg_parameter_info_t * parameters;
112   gavl_audio_format_t format;
113   snd_pcm_t * pcm;
114 
115   /* Configuration stuff */
116 
117   int enable_surround40;
118   int enable_surround41;
119   int enable_surround50;
120   int enable_surround51;
121 
122   //  int card_index;
123   char * card;
124 
125   char * user_device;
126   int convert_4_3;
127   uint8_t * convert_buffer;
128   int convert_buffer_alloc;
129 
130   gavl_time_t buffer_time;
131   } alsa_t;
132 
convert_4_to_3(alsa_t * priv,gavl_audio_frame_t * frame)133 static void convert_4_to_3(alsa_t * priv, gavl_audio_frame_t * frame)
134   {
135   int i, imax;
136   uint8_t * src, * dst;
137 
138   imax = frame->valid_samples * priv->format.num_channels;
139 
140   if(imax * 3 < priv->convert_buffer_alloc)
141     {
142     priv->convert_buffer_alloc = (imax+1024) * 3;
143     priv->convert_buffer = realloc(priv->convert_buffer, priv->convert_buffer_alloc);
144     }
145 
146   dst = priv->convert_buffer;
147   src = frame->samples.u_8;
148 
149   for(i = 0; i < imax; i++)
150     {
151 #ifndef WORDS_BIGENDIAN
152     dst[0] = src[1];
153     dst[1] = src[2];
154     dst[2] = src[3];
155 #else
156     dst[0] = src[0];
157     dst[1] = src[1];
158     dst[2] = src[2];
159 #endif
160     }
161   }
162 
create_alsa()163 static void * create_alsa()
164   {
165   int i;
166   alsa_t * ret = calloc(1, sizeof(*ret));
167 
168   ret->parameters = calloc(num_global_parameters+2, sizeof(*ret->parameters));
169 
170   bg_alsa_create_card_parameters(ret->parameters, 0);
171 
172   for(i = 0; i < num_global_parameters; i++)
173     {
174     bg_parameter_info_copy(&ret->parameters[i+1], &global_parameters[i]);
175     }
176 
177   return ret;
178   }
179 
start_alsa(void * data)180 static int start_alsa(void * data)
181   {
182   alsa_t * priv = data;
183 
184   if(snd_pcm_prepare(priv->pcm) < 0)
185     return 0;
186   snd_pcm_start(priv->pcm);
187   return 1;
188   }
189 
stop_alsa(void * data)190 static void stop_alsa(void * data)
191   {
192   alsa_t * priv = data;
193   snd_pcm_drop(priv->pcm);
194 
195   }
196 
open_alsa(void * data,gavl_audio_format_t * format)197 static int open_alsa(void * data, gavl_audio_format_t * format)
198   {
199   int playback_mode;
200   int num_front_channels;
201   int num_rear_channels;
202   int num_lfe_channels;
203   const char * card = NULL;
204   alsa_t * priv = data;
205 
206   //  bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Opening");
207 
208   /* Figure out the right channel setup */
209 
210   num_front_channels = gavl_front_channels(format);
211   num_rear_channels = gavl_rear_channels(format);
212   num_lfe_channels = gavl_lfe_channels(format);
213 
214   if(priv->user_device)
215     {
216     playback_mode = PLAYBACK_USER;
217     }
218   else
219     {
220     playback_mode = PLAYBACK_NONE;
221 
222     if(num_front_channels > 2)
223       {
224       if(num_lfe_channels)
225         {
226         if(priv->enable_surround51)
227           playback_mode = PLAYBACK_SURROUND51;
228         else if(priv->enable_surround50)
229           playback_mode = PLAYBACK_SURROUND50;
230         }
231       else if(priv->enable_surround50)
232         playback_mode = PLAYBACK_SURROUND50;
233       }
234 
235     else if((playback_mode == PLAYBACK_NONE) && num_rear_channels)
236       {
237       if(num_lfe_channels)
238         {
239         if(priv->enable_surround41)
240           playback_mode = PLAYBACK_SURROUND41;
241         else if(priv->enable_surround40)
242           playback_mode = PLAYBACK_SURROUND40;
243         }
244       else if(priv->enable_surround40)
245         playback_mode = PLAYBACK_SURROUND40;
246       }
247 
248     if(playback_mode == PLAYBACK_NONE)
249       playback_mode = PLAYBACK_GENERIC;
250     }
251 
252   switch(playback_mode)
253     {
254     case PLAYBACK_GENERIC:
255       if(format->num_channels > 2)
256         format->num_channels = 2;
257       format->channel_locations[0] = GAVL_CHID_NONE;
258       gavl_set_channel_setup(format);
259 
260       card = priv->card;
261 
262 
263       break;
264     case PLAYBACK_USER:
265       format->channel_locations[0] = GAVL_CHID_NONE;
266       gavl_set_channel_setup(format);
267       card = priv->user_device;
268       break;
269     case PLAYBACK_SURROUND40:
270       format->num_channels = 4;
271 
272       format->channel_locations[0] = GAVL_CHID_FRONT_LEFT;
273       format->channel_locations[1] = GAVL_CHID_FRONT_RIGHT;
274       format->channel_locations[2] = GAVL_CHID_REAR_LEFT;
275       format->channel_locations[3] = GAVL_CHID_REAR_RIGHT;
276 
277       card = bg_sprintf("surround40");
278 
279 
280       break;
281     case PLAYBACK_SURROUND41:
282       format->num_channels = 5;
283 
284       format->channel_locations[0] = GAVL_CHID_FRONT_LEFT;
285       format->channel_locations[1] = GAVL_CHID_FRONT_RIGHT;
286       format->channel_locations[2] = GAVL_CHID_REAR_LEFT;
287       format->channel_locations[3] = GAVL_CHID_REAR_RIGHT;
288       format->channel_locations[4] = GAVL_CHID_LFE;
289 
290       card = bg_sprintf("surround41");
291 
292 
293       break;
294     case PLAYBACK_SURROUND50:
295       format->num_channels = 5;
296 
297       format->channel_locations[0] = GAVL_CHID_FRONT_LEFT;
298       format->channel_locations[1] = GAVL_CHID_FRONT_RIGHT;
299       format->channel_locations[2] = GAVL_CHID_REAR_LEFT;
300       format->channel_locations[3] = GAVL_CHID_REAR_RIGHT;
301       format->channel_locations[4] = GAVL_CHID_FRONT_CENTER;
302 
303       card = bg_sprintf("surround50");
304 
305       break;
306     case PLAYBACK_SURROUND51:
307       format->num_channels = 6;
308 
309       format->channel_locations[0] = GAVL_CHID_FRONT_LEFT;
310       format->channel_locations[1] = GAVL_CHID_FRONT_RIGHT;
311       format->channel_locations[2] = GAVL_CHID_REAR_LEFT;
312       format->channel_locations[3] = GAVL_CHID_REAR_RIGHT;
313       format->channel_locations[4] = GAVL_CHID_FRONT_CENTER;
314       format->channel_locations[5] = GAVL_CHID_LFE;
315 
316       card = bg_sprintf("surround51");
317       break;
318     }
319 
320   if(!card)
321     card = "default";
322 
323   priv->pcm = bg_alsa_open_write(card, format,
324                                  priv->buffer_time, &priv->convert_4_3);
325 
326   if(!priv->pcm)
327     return 0;
328   gavl_audio_format_copy(&priv->format, format);
329   return 1;
330   }
331 
close_alsa(void * p)332 static void close_alsa(void * p)
333   {
334   alsa_t * priv = p;
335 
336   //  bg_log(BG_LOG_DEBUG, LOG_DOMAIN, "Closing");
337 
338   if(priv->pcm)
339     {
340     snd_pcm_close(priv->pcm);
341     priv->pcm = NULL;
342     }
343   }
344 
write_frame_alsa(void * p,gavl_audio_frame_t * f)345 static void write_frame_alsa(void * p, gavl_audio_frame_t * f)
346   {
347   int result = -EPIPE;
348   alsa_t * priv = p;
349 
350   if(priv->convert_4_3)
351     {
352     convert_4_to_3(priv, f);
353     while(result == -EPIPE)
354       {
355       result = snd_pcm_writei(priv->pcm,
356                               priv->convert_buffer,
357                               f->valid_samples);
358 
359       if(result == -EPIPE)
360         {
361         //    snd_pcm_drop(priv->pcm);
362         if(snd_pcm_prepare(priv->pcm) < 0)
363           return;
364         }
365       }
366     }
367   else
368     {
369     while(result == -EPIPE)
370       {
371       result = snd_pcm_writei(priv->pcm,
372                               f->samples.s_8,
373                               f->valid_samples);
374 
375       if(result == -EPIPE)
376         {
377         //    snd_pcm_drop(priv->pcm);
378         if(snd_pcm_prepare(priv->pcm) < 0)
379           return;
380         }
381       }
382     }
383 
384 
385   if(result < 0)
386     {
387     bg_log(BG_LOG_ERROR, LOG_DOMAIN, "snd_pcm_write returned %s", snd_strerror(result));
388     }
389   }
390 
destroy_alsa(void * p)391 static void destroy_alsa(void * p)
392   {
393   alsa_t * priv = p;
394   close_alsa(priv);
395 
396   if(priv->parameters)
397     bg_parameter_info_destroy_array(priv->parameters);
398   if(priv->user_device)
399     free(priv->user_device);
400   if(priv->card)
401     free(priv->card);
402   snd_config_update_free_global();
403   free(priv);
404   }
405 
406 static const bg_parameter_info_t *
get_parameters_alsa(void * p)407 get_parameters_alsa(void * p)
408   {
409   alsa_t * priv = p;
410   return priv->parameters;
411   }
412 
413 
get_delay_alsa(void * p)414 static int get_delay_alsa(void * p)
415   {
416   int result;
417   snd_pcm_sframes_t frames;
418   alsa_t * priv;
419   priv = p;
420   result = snd_pcm_delay(priv->pcm, &frames);
421   if(!result)
422     return frames;
423   return 0;
424   }
425 /* Set parameter */
426 
427 static void
set_parameter_alsa(void * p,const char * name,const bg_parameter_value_t * val)428 set_parameter_alsa(void * p, const char * name,
429                    const bg_parameter_value_t * val)
430   {
431   alsa_t * priv = p;
432   if(!name)
433     return;
434 
435   if(!strcmp(name, "surround40"))
436     {
437     priv->enable_surround40 = val->val_i;
438     }
439   else if(!strcmp(name, "surround41"))
440     {
441     priv->enable_surround41 = val->val_i;
442     }
443   else if(!strcmp(name, "surround50"))
444     {
445     priv->enable_surround50 = val->val_i;
446     }
447   else if(!strcmp(name, "surround51"))
448     {
449     priv->enable_surround51 = val->val_i;
450     }
451   else if(!strcmp(name, "user_device"))
452     {
453     priv->user_device = bg_strdup(priv->user_device, val->val_str);
454     }
455   else if(!strcmp(name, "buffer_time"))
456     {
457     priv->buffer_time = val->val_i;
458     priv->buffer_time *= (GAVL_TIME_SCALE/1000);
459     }
460   else if(!strcmp(name, "card"))
461     {
462     priv->card = bg_strdup(priv->card, val->val_str);
463     }
464   }
465 
466 const bg_oa_plugin_t the_plugin =
467   {
468     .common =
469     {
470       BG_LOCALE,
471       .name =          "oa_alsa",
472       .long_name =     TRS("Alsa"),
473       .description =   TRS("Alsa output plugin with support for channel configurations up to 5.1"),
474       .type =          BG_PLUGIN_OUTPUT_AUDIO,
475       .flags =         BG_PLUGIN_PLAYBACK | BG_PLUGIN_DEVPARAM,
476       .priority =      BG_PLUGIN_PRIORITY_MAX,
477       .create =        create_alsa,
478       .destroy =       destroy_alsa,
479 
480       .get_parameters = get_parameters_alsa,
481       .set_parameter =  set_parameter_alsa,
482     },
483 
484     .open =          open_alsa,
485     .start =         start_alsa,
486     .write_audio =   write_frame_alsa,
487     .stop =          stop_alsa,
488     .close =         close_alsa,
489     .get_delay =     get_delay_alsa,
490   };
491 
492 /* Include this into all plugin modules exactly once
493    to let the plugin loader obtain the API version */
494 BG_GET_PLUGIN_API_VERSION;
495