Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
output.c
Go to the documentation of this file.
1 /*
2  * output.c
3  * Copyright 2009-2012 John Lindgren
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions, and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions, and the following disclaimer in the documentation
13  * provided with the distribution.
14  *
15  * This software is provided "as is" and without any warranty, express or
16  * implied. In no event shall the authors be liable for any damages arising from
17  * the use of this software.
18  */
19 
20 #include <math.h>
21 #include <pthread.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include <libaudcore/hook.h>
27 
28 #include "debug.h"
29 #include "effect.h"
30 #include "equalizer.h"
31 #include "misc.h"
32 #include "output.h"
33 #include "plugin.h"
34 #include "plugins.h"
35 #include "vis_runner.h"
36 
37 #define SW_VOLUME_RANGE 40 /* decibels */
38 
39 static pthread_mutex_t mutex_major = PTHREAD_MUTEX_INITIALIZER;
40 static pthread_mutex_t mutex_minor = PTHREAD_MUTEX_INITIALIZER;
41 
42 #define LOCK_MAJOR pthread_mutex_lock (& mutex_major)
43 #define UNLOCK_MAJOR pthread_mutex_unlock (& mutex_major)
44 #define LOCK_MINOR pthread_mutex_lock (& mutex_minor)
45 #define UNLOCK_MINOR pthread_mutex_unlock (& mutex_minor)
46 #define LOCK_ALL do { LOCK_MAJOR; LOCK_MINOR; } while (0)
47 #define UNLOCK_ALL do { UNLOCK_MINOR; UNLOCK_MAJOR; } while (0)
48 
49 /* State variables. State changes that are allowed between LOCK_MINOR and
50  * UNLOCK_MINOR (all others must take place between LOCK_ALL and UNLOCK_ALL):
51  * s_paused -> TRUE or FALSE, s_aborted -> TRUE, s_resetting -> TRUE */
52 
53 static bool_t s_input; /* input plugin connected */
54 static bool_t s_output; /* output plugin connected */
55 static bool_t s_gain; /* replay gain info set */
56 static bool_t s_paused; /* paused */
57 static bool_t s_aborted; /* writes aborted */
58 static bool_t s_resetting; /* resetting output system */
59 
60 static OutputPlugin * cop;
61 static int seek_time;
64 static int64_t in_frames, out_frames;
66 
68 static OutputPlugin * new_op;
69 
70 static inline int FR2MS (int64_t f, int r)
71  { return (f > 0) ? (f * 1000 + r / 2) / r : (f * 1000 - r / 2) / r; }
72 
73 static inline int get_format (void)
74 {
75  switch (get_int (NULL, "output_bit_depth"))
76  {
77  case 16: return FMT_S16_NE;
78  case 24: return FMT_S24_NE;
79  case 32: return FMT_S32_NE;
80  default: return FMT_FLOAT;
81  }
82 }
83 
84 /* assumes LOCK_ALL, s_output */
85 static void cleanup_output (void)
86 {
87  if (! (s_paused || s_aborted) && PLUGIN_HAS_FUNC (cop, drain))
88  {
90  cop->drain ();
91  LOCK_MINOR;
92  }
93 
94  s_output = FALSE;
95 
96  if (PLUGIN_HAS_FUNC (cop, close_audio))
97  cop->close_audio ();
98 
99  effect_flush ();
101 }
102 
103 /* assumes LOCK_ALL, s_output */
104 static void apply_pause (void)
105 {
106  if (PLUGIN_HAS_FUNC (cop, pause))
107  cop->pause (s_paused);
108 
110 }
111 
112 /* assumes LOCK_ALL, s_input */
113 static void setup_output (void)
114 {
115  int format = get_format ();
116  int channels = in_channels;
117  int rate = in_rate;
118 
119  effect_start (& channels, & rate);
120  eq_set_format (channels, rate);
121 
122  if (s_output && format == out_format && channels == out_channels && rate ==
123  out_rate && ! PLUGIN_HAS_FUNC (cop, force_reopen))
124  return;
125 
126  if (s_output)
127  cleanup_output ();
128 
129  if (! cop || ! PLUGIN_HAS_FUNC (cop, open_audio) || ! cop->open_audio (format, rate, channels))
130  return;
131 
132  s_output = TRUE;
133 
134  out_format = format;
136  out_rate = rate;
137  out_frames = 0;
138 
139  apply_pause ();
140 }
141 
142 /* assumes LOCK_MINOR, s_output */
143 static void flush_output (void)
144 {
145  if (PLUGIN_HAS_FUNC (cop, flush))
146  {
147  cop->flush (0);
148  out_frames = 0;
149  }
150 
151  effect_flush ();
152  vis_runner_flush ();
153 }
154 
155 static void apply_replay_gain (float * data, int samples)
156 {
157  if (! get_bool (NULL, "enable_replay_gain"))
158  return;
159 
160  float factor = powf (10, get_double (NULL, "replay_gain_preamp") / 20);
161 
162  if (s_gain)
163  {
164  float peak;
165 
166  if (get_bool (NULL, "replay_gain_album"))
167  {
168  factor *= powf (10, gain_info.album_gain / 20);
169  peak = gain_info.album_peak;
170  }
171  else
172  {
173  factor *= powf (10, gain_info.track_gain / 20);
174  peak = gain_info.track_peak;
175  }
176 
177  if (get_bool (NULL, "enable_clipping_prevention") && peak * factor > 1)
178  factor = 1 / peak;
179  }
180  else
181  factor *= powf (10, get_double (NULL, "default_gain") / 20);
182 
183  if (factor < 0.99 || factor > 1.01)
184  audio_amplify (data, 1, samples, & factor);
185 }
186 
187 static void apply_software_volume (float * data, int channels, int samples)
188 {
189  if (! get_bool (NULL, "software_volume_control"))
190  return;
191 
192  int l = get_int (NULL, "sw_volume_left");
193  int r = get_int (NULL, "sw_volume_right");
194 
195  if (l == 100 && r == 100)
196  return;
197 
198  float lfactor = (l == 0) ? 0 : powf (10, (float) SW_VOLUME_RANGE * (l - 100) / 100 / 20);
199  float rfactor = (r == 0) ? 0 : powf (10, (float) SW_VOLUME_RANGE * (r - 100) / 100 / 20);
200  float factors[channels];
201 
202  if (channels == 2)
203  {
204  factors[0] = lfactor;
205  factors[1] = rfactor;
206  }
207  else
208  {
209  for (int c = 0; c < channels; c ++)
210  factors[c] = MAX (lfactor, rfactor);
211  }
212 
213  audio_amplify (data, channels, samples / channels, factors);
214 }
215 
216 /* assumes LOCK_ALL, s_output */
217 static void write_output_raw (void * data, int samples)
218 {
219  void * buffer = NULL;
220 
221  vis_runner_pass_audio (FR2MS (out_frames, out_rate), data, samples,
223  out_frames += samples / out_channels;
224 
225  eq_filter (data, samples);
226  apply_software_volume (data, out_channels, samples);
227 
228  if (get_bool (NULL, "soft_clipping"))
229  audio_soft_clip (data, samples);
230 
231  if (out_format != FMT_FLOAT)
232  {
233  buffer = malloc (FMT_SIZEOF (out_format) * samples);
234  audio_to_int (data, buffer, out_format, samples);
235  data = buffer;
236  }
237 
238  while (! (s_aborted || s_resetting))
239  {
240  bool_t blocking = ! PLUGIN_HAS_FUNC (cop, buffer_free);
241  int ready;
242 
243  if (blocking)
244  ready = out_channels * (out_rate / 50);
245  else
246  ready = cop->buffer_free () / FMT_SIZEOF (out_format);
247 
248  ready = MIN (ready, samples);
249 
250  if (PLUGIN_HAS_FUNC (cop, write_audio))
251  {
252  cop->write_audio (data, FMT_SIZEOF (out_format) * ready);
253  data = (char *) data + FMT_SIZEOF (out_format) * ready;
254  samples -= ready;
255  }
256 
257  if (samples == 0)
258  break;
259 
260  UNLOCK_MINOR;
261 
262  if (! blocking)
263  {
264  if (PLUGIN_HAS_FUNC (cop, period_wait))
265  cop->period_wait ();
266  else
267  usleep (20000);
268  }
269 
270  LOCK_MINOR;
271  }
272 
273  free (buffer);
274 }
275 
276 /* assumes LOCK_ALL, s_input, s_output */
277 static void write_output (void * data, int size)
278 {
279  void * buffer = NULL;
280 
281  int samples = size / FMT_SIZEOF (in_format);
282  in_frames += samples / in_channels;
283 
284  if (in_format != FMT_FLOAT)
285  {
286  buffer = malloc (sizeof (float) * samples);
287  audio_from_int (data, in_format, buffer, samples);
288  data = buffer;
289  }
290 
291  float * fdata = data;
292  apply_replay_gain (fdata, samples);
293  effect_process (& fdata, & samples);
294  write_output_raw (fdata, samples);
295 
296  free (buffer);
297 }
298 
299 /* assumes LOCK_ALL, s_output */
300 static void finish_effects (void)
301 {
302  float * data = NULL;
303  int samples = 0;
304 
305  effect_finish (& data, & samples);
306  write_output_raw (data, samples);
307 }
308 
310 {
311  /* prevent division by zero */
312  if (rate < 1 || channels < 1)
313  return FALSE;
314 
315  LOCK_ALL;
316 
317  if (s_output && s_paused)
318  {
319  flush_output ();
320  s_paused = FALSE;
321  apply_pause ();
322  }
323 
324  s_input = TRUE;
326  seek_time = 0;
327 
328  in_format = format;
330  in_rate = rate;
331  in_frames = 0;
332 
333  setup_output ();
334 
335  UNLOCK_ALL;
336  return TRUE;
337 }
338 
340 {
341  LOCK_ALL;
342 
343  if (s_input)
344  {
345  memcpy (& gain_info, info, sizeof (ReplayGainInfo));
346  s_gain = TRUE;
347 
348  AUDDBG ("Replay Gain info:\n");
349  AUDDBG (" album gain: %f dB\n", info->album_gain);
350  AUDDBG (" album peak: %f\n", info->album_peak);
351  AUDDBG (" track gain: %f dB\n", info->track_gain);
352  AUDDBG (" track peak: %f\n", info->track_peak);
353  }
354 
355  UNLOCK_ALL;
356 }
357 
358 void output_write_audio (void * data, int size)
359 {
360  LOCK_ALL;
361 
362  if (s_input)
363  {
364  while ((! s_output || s_resetting) && ! s_aborted)
365  {
366  UNLOCK_ALL;
367  usleep (20000);
368  LOCK_ALL;
369  }
370 
371  if (! s_aborted)
372  write_output (data, size);
373  }
374 
375  UNLOCK_ALL;
376 }
377 
379 {
380  LOCK_MINOR;
381 
382  if (s_input)
383  {
384  s_aborted = TRUE;
385 
386  if (s_output)
387  flush_output ();
388  }
389 
390  UNLOCK_MINOR;
391 }
392 
394 {
395  LOCK_MINOR;
396 
397  if (s_input)
398  {
399  s_paused = pause;
400 
401  if (s_output)
402  apply_pause ();
403  }
404 
405  UNLOCK_MINOR;
406 }
407 
409 {
410  LOCK_MINOR;
411  int time = 0;
412 
413  if (s_input)
414  time = seek_time + FR2MS (in_frames, in_rate);
415 
416  UNLOCK_MINOR;
417  return time;
418 }
419 
420 void output_set_time (int time)
421 {
422  LOCK_ALL;
423 
424  if (s_input)
425  {
426  s_aborted = FALSE;
427  seek_time = time;
428  in_frames = 0;
429  }
430 
431  UNLOCK_ALL;
432 
433  /* See comment in playback_seek(). */
434  event_queue ("playback seek", NULL);
435 }
436 
438 {
439  LOCK_MINOR;
440  bool_t is_open = s_input;
441  UNLOCK_MINOR;
442  return is_open;
443 }
444 
445 int output_get_time (void)
446 {
447  LOCK_MINOR;
448  int time = 0, delay = 0;
449 
450  if (s_input)
451  {
452  if (s_output && PLUGIN_HAS_FUNC (cop, output_time))
453  delay = FR2MS (out_frames, out_rate) - cop->output_time ();
454 
455  delay = effect_adjust_delay (delay);
456  time = FR2MS (in_frames, in_rate);
457  time = seek_time + MAX (time - delay, 0);
458  }
459 
460  UNLOCK_MINOR;
461  return time;
462 }
463 
465 {
466  LOCK_MINOR;
467  int time = 0;
468 
469  if (s_output && PLUGIN_HAS_FUNC (cop, output_time))
470  time = cop->output_time ();
471 
472  UNLOCK_MINOR;
473  return time;
474 }
475 
477 {
478  LOCK_ALL;
479 
480  if (s_input)
481  {
482  s_input = FALSE;
483 
484  if (s_output && ! (s_paused || s_aborted || s_resetting))
485  finish_effects (); /* first time for end of song */
486  }
487 
488  UNLOCK_ALL;
489 }
490 
491 void output_drain (void)
492 {
493  LOCK_ALL;
494 
495  if (! s_input && s_output)
496  {
497  finish_effects (); /* second time for end of playlist */
498  cleanup_output ();
499  }
500 
501  UNLOCK_ALL;
502 }
503 
504 void output_reset (int type)
505 {
506  LOCK_MINOR;
507 
508  s_resetting = TRUE;
509 
510  if (s_output)
511  flush_output ();
512 
513  UNLOCK_MINOR;
514  LOCK_ALL;
515 
516  if (s_output && type != OUTPUT_RESET_EFFECTS_ONLY)
517  cleanup_output ();
518 
519  if (type == OUTPUT_RESET_HARD)
520  {
521  if (cop && PLUGIN_HAS_FUNC (cop, cleanup))
522  cop->cleanup ();
523 
524  if (change_op)
525  cop = new_op;
526 
527  if (cop && PLUGIN_HAS_FUNC (cop, init) && ! cop->init ())
528  cop = NULL;
529  }
530 
531  if (s_input)
532  setup_output ();
533 
534  s_resetting = FALSE;
535 
536  UNLOCK_ALL;
537 }
538 
539 void output_get_volume (int * left, int * right)
540 {
541  LOCK_MINOR;
542 
543  * left = * right = 0;
544 
545  if (get_bool (NULL, "software_volume_control"))
546  {
547  * left = get_int (NULL, "sw_volume_left");
548  * right = get_int (NULL, "sw_volume_right");
549  }
550  else if (cop && PLUGIN_HAS_FUNC (cop, get_volume))
551  cop->get_volume (left, right);
552 
553  UNLOCK_MINOR;
554 }
555 
556 void output_set_volume (int left, int right)
557 {
558  LOCK_MINOR;
559 
560  if (get_bool (NULL, "software_volume_control"))
561  {
562  set_int (NULL, "sw_volume_left", left);
563  set_int (NULL, "sw_volume_right", right);
564  }
565  else if (cop && PLUGIN_HAS_FUNC (cop, set_volume))
566  cop->set_volume (left, right);
567 
568  UNLOCK_MINOR;
569 }
570 
572 {
573  OutputPlugin * op = plugin_get_header (p);
574 
575  if (! op || (PLUGIN_HAS_FUNC (op, init) && ! op->init ()))
576  return TRUE; /* keep searching */
577 
578  if (PLUGIN_HAS_FUNC (op, cleanup))
579  op->cleanup ();
580 
581  * pp = p;
582  return FALSE; /* stop searching */
583 }
584 
586 {
587  PluginHandle * p = NULL;
589  return p;
590 }
591 
593 {
594  return cop ? plugin_by_header (cop) : NULL;
595 }
596 
598 {
599  change_op = TRUE;
600  new_op = plugin ? plugin_get_header (plugin) : NULL;
602 
603  bool_t success = (cop == new_op);
604  change_op = FALSE;
605  new_op = NULL;
606 
607  return success;
608 }