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 <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 
26 #include <gmerlin/log.h>
27 #define LOG_DOMAIN "player.audio"
28 
29 #include <gmerlin/player.h>
30 #include <playerprivate.h>
31 
bg_player_audio_create(bg_player_t * p,bg_plugin_registry_t * plugin_reg)32 void bg_player_audio_create(bg_player_t * p, bg_plugin_registry_t * plugin_reg)
33   {
34   bg_player_audio_stream_t * s = &p->audio_stream;
35 
36   bg_gavl_audio_options_init(&s->options);
37 
38   s->th = bg_player_thread_create(p->thread_common);
39 
40   s->fc =
41     bg_audio_filter_chain_create(&s->options,
42                                  plugin_reg);
43 
44   s->cnv_out = gavl_audio_converter_create();
45 
46   s->volume = gavl_volume_control_create();
47   s->peak_detector = gavl_peak_detector_create();
48 
49   pthread_mutex_init(&s->volume_mutex,NULL);
50   pthread_mutex_init(&s->config_mutex,NULL);
51   pthread_mutex_init(&s->time_mutex,NULL);
52   pthread_mutex_init(&s->mute_mutex, NULL);
53   pthread_mutex_init(&s->eof_mutex,NULL);
54 
55   s->timer = gavl_timer_create();
56   }
57 
bg_player_audio_destroy(bg_player_t * p)58 void bg_player_audio_destroy(bg_player_t * p)
59   {
60   bg_player_audio_stream_t * s = &p->audio_stream;
61   gavl_audio_converter_destroy(s->cnv_out);
62   bg_gavl_audio_options_free(&s->options);
63   bg_audio_filter_chain_destroy(s->fc);
64 
65   gavl_volume_control_destroy(s->volume);
66   gavl_peak_detector_destroy(s->peak_detector);
67   pthread_mutex_destroy(&s->volume_mutex);
68   pthread_mutex_destroy(&s->eof_mutex);
69 
70   pthread_mutex_destroy(&s->time_mutex);
71   gavl_timer_destroy(s->timer);
72 
73   if(s->plugin_handle)
74     bg_plugin_unref(s->plugin_handle);
75 
76   bg_player_thread_destroy(s->th);
77 
78   }
79 
bg_player_audio_init(bg_player_t * player,int audio_stream)80 int bg_player_audio_init(bg_player_t * player, int audio_stream)
81   {
82   gavl_sample_format_t force_format;
83   gavl_audio_options_t * opt;
84   bg_player_audio_stream_t * s;
85   //  int do_filter;
86 
87   if(!DO_AUDIO(player->flags))
88     return 1;
89 
90   s = &player->audio_stream;
91   s->send_silence = 0;
92 
93   s->options.options_changed = 0;
94 
95   s->in_func   = bg_player_input_read_audio;
96   s->in_data   = player;
97   s->in_stream = player->current_audio_stream;
98 
99   bg_player_input_get_audio_format(player);
100 
101   bg_audio_filter_chain_connect_input(s->fc,
102                                       s->in_func,
103                                       s->in_data,
104                                       s->in_stream);
105   s->in_func = bg_audio_filter_chain_read;
106   s->in_data = s->fc;
107   s->in_stream = 0;
108 
109   pthread_mutex_lock(&s->config_mutex);
110   force_format = s->options.force_format;
111   bg_audio_filter_chain_init(s->fc, &s->input_format, &s->fifo_format);
112   pthread_mutex_unlock(&s->config_mutex);
113 
114 
115   gavl_audio_format_copy(&s->output_format,
116                          &s->fifo_format);
117 
118   if(!bg_player_oa_init(s))
119     return 0;
120 
121   gavl_audio_format_copy(&s->fifo_format,
122                          &s->output_format);
123 
124   if(force_format != GAVL_SAMPLE_NONE)
125     s->fifo_format.sample_format = force_format;
126 
127   bg_audio_filter_chain_set_out_format(s->fc, &s->fifo_format);
128 
129   /* Volume control */
130   gavl_volume_control_set_format(s->volume,
131                                  &s->fifo_format);
132   gavl_peak_detector_set_format(s->peak_detector,
133                                 &s->fifo_format);
134 
135   /* Output conversion */
136   opt = gavl_audio_converter_get_options(s->cnv_out);
137   gavl_audio_options_copy(opt, s->options.opt);
138 
139   s->fifo_frame = gavl_audio_frame_create(&s->output_format);
140 
141   if(!gavl_audio_converter_init(s->cnv_out,
142                                 &s->fifo_format,
143                                 &s->output_format))
144     {
145     s->do_convert_out = 0;
146     s->output_frame = s->fifo_frame;
147     }
148   else
149     {
150     s->do_convert_out = 1;
151     s->output_frame =
152       gavl_audio_frame_create(&s->output_format);
153     }
154   return 1;
155   }
156 
bg_player_audio_cleanup(bg_player_t * player)157 void bg_player_audio_cleanup(bg_player_t * player)
158   {
159   bg_player_audio_stream_t * s;
160   s = &player->audio_stream;
161 
162   if(s->fifo_frame)
163     {
164     gavl_audio_frame_destroy(s->fifo_frame);
165     s->fifo_frame = NULL;
166     }
167   if(s->output_frame && s->do_convert_out)
168     {
169     gavl_audio_frame_destroy(s->output_frame);
170     }
171   s->output_frame = NULL;
172   }
173 
174 /* Configuration stuff */
175 
176 static const bg_parameter_info_t parameters[] =
177   {
178 #if 0
179     {
180       .name =      "audio",
181       .long_name = TRS("Audio"),
182       .type =      BG_PARAMETER_SECTION,
183     },
184 #endif
185     BG_GAVL_PARAM_FORCE_SAMPLEFORMAT,
186     BG_GAVL_PARAM_CONVERSION_QUALITY,
187     BG_GAVL_PARAM_AUDIO_DITHER_MODE,
188     BG_GAVL_PARAM_SAMPLERATE,
189     BG_GAVL_PARAM_RESAMPLE_MODE,
190     BG_GAVL_PARAM_CHANNEL_SETUP,
191     { /* End of parameters */ }
192   };
193 
194 
195 
bg_player_get_audio_parameters(bg_player_t * p)196 const bg_parameter_info_t * bg_player_get_audio_parameters(bg_player_t * p)
197   {
198   return parameters;
199   }
200 
bg_player_get_audio_filter_parameters(bg_player_t * p)201 const bg_parameter_info_t * bg_player_get_audio_filter_parameters(bg_player_t * p)
202   {
203   return bg_audio_filter_chain_get_parameters(p->audio_stream.fc);
204   }
205 
206 
bg_player_set_audio_parameter(void * data,const char * name,const bg_parameter_value_t * val)207 void bg_player_set_audio_parameter(void * data, const char * name,
208                                    const bg_parameter_value_t * val)
209   {
210   bg_player_t * p = (bg_player_t*)data;
211   int need_restart = 0;
212   int is_interrupted;
213   int do_init;
214   int check_restart;
215 
216   do_init = (bg_player_get_state(p) == BG_PLAYER_STATE_INIT);
217 
218   pthread_mutex_lock(&p->audio_stream.config_mutex);
219 
220   is_interrupted = p->audio_stream.interrupted;
221 
222   bg_gavl_audio_set_parameter(&p->audio_stream.options,
223                               name, val);
224 
225   if(!do_init && !is_interrupted)
226     check_restart = 1;
227   else
228     check_restart = 0;
229 
230   if(check_restart)
231     need_restart = p->audio_stream.options.options_changed;
232 
233   pthread_mutex_unlock(&p->audio_stream.config_mutex);
234 
235   if(!need_restart && check_restart)
236     {
237     bg_audio_filter_chain_lock(p->audio_stream.fc);
238     need_restart =
239         bg_audio_filter_chain_need_restart(p->audio_stream.fc);
240     bg_audio_filter_chain_unlock(p->audio_stream.fc);
241     }
242 
243   if(need_restart)
244     {
245     bg_log(BG_LOG_INFO, LOG_DOMAIN,
246            "Restarting playback due to changed audio options");
247     bg_player_interrupt(p);
248 
249     pthread_mutex_lock(&p->audio_stream.config_mutex);
250     p->audio_stream.interrupted = 1;
251     pthread_mutex_unlock(&p->audio_stream.config_mutex);
252     }
253 
254   if(!name && is_interrupted)
255     {
256     bg_player_interrupt_resume(p);
257     pthread_mutex_lock(&p->audio_stream.config_mutex);
258     p->audio_stream.interrupted = 0;
259     pthread_mutex_unlock(&p->audio_stream.config_mutex);
260     }
261   }
262 
bg_player_set_audio_filter_parameter(void * data,const char * name,const bg_parameter_value_t * val)263 void bg_player_set_audio_filter_parameter(void * data, const char * name,
264                                           const bg_parameter_value_t * val)
265   {
266   int need_restart = 0;
267   int is_interrupted;
268   int do_init;
269   bg_player_t * p = (bg_player_t*)data;
270 
271   do_init = (bg_player_get_state(p) == BG_PLAYER_STATE_INIT);
272 
273   pthread_mutex_lock(&p->audio_stream.config_mutex);
274   is_interrupted = p->audio_stream.interrupted;
275   pthread_mutex_unlock(&p->audio_stream.config_mutex);
276 
277   bg_audio_filter_chain_lock(p->audio_stream.fc);
278   bg_audio_filter_chain_set_parameter(p->audio_stream.fc, name, val);
279 
280   need_restart =
281     bg_audio_filter_chain_need_restart(p->audio_stream.fc);
282 
283   bg_audio_filter_chain_unlock(p->audio_stream.fc);
284 
285   if(!do_init && need_restart && !is_interrupted)
286     {
287     bg_log(BG_LOG_INFO, LOG_DOMAIN,
288            "Restarting playback due to changed audio filters");
289     bg_player_interrupt(p);
290 
291     pthread_mutex_lock(&p->audio_stream.config_mutex);
292     p->audio_stream.interrupted = 1;
293     pthread_mutex_unlock(&p->audio_stream.config_mutex);
294     }
295 
296   if(!name && is_interrupted)
297     {
298     bg_player_interrupt_resume(p);
299     pthread_mutex_lock(&p->audio_stream.config_mutex);
300     p->audio_stream.interrupted = 0;
301     pthread_mutex_unlock(&p->audio_stream.config_mutex);
302     }
303 
304 
305   }
306 
307 int
bg_player_read_audio(bg_player_t * p,gavl_audio_frame_t * frame)308 bg_player_read_audio(bg_player_t * p, gavl_audio_frame_t * frame)
309   {
310   bg_player_audio_stream_t * s = &p->audio_stream;
311   return s->in_func(s->in_data, frame, s->in_stream,
312                     s->fifo_format.samples_per_frame);
313   }
314 
bg_player_audio_set_eof(bg_player_t * p)315 int bg_player_audio_set_eof(bg_player_t * p)
316   {
317   bg_msg_t * msg;
318   int ret = 1;
319 
320   bg_log(BG_LOG_INFO, LOG_DOMAIN, "Detected EOF");
321   pthread_mutex_lock(&p->video_stream.eof_mutex);
322   pthread_mutex_lock(&p->audio_stream.eof_mutex);
323 
324   p->audio_stream.eof = 1;
325 
326   if(p->video_stream.eof)
327     {
328     msg = bg_msg_queue_lock_write(p->command_queue);
329     bg_msg_set_id(msg, BG_PLAYER_CMD_SETSTATE);
330     bg_msg_set_arg_int(msg, 0, BG_PLAYER_STATE_EOF);
331     bg_msg_queue_unlock_write(p->command_queue);
332     }
333   else
334     {
335     ret = 0;
336     p->audio_stream.send_silence = 1;
337     }
338   pthread_mutex_unlock(&p->audio_stream.eof_mutex);
339   pthread_mutex_unlock(&p->video_stream.eof_mutex);
340   return ret;
341   }
342