Project

General

Profile

output_v3.6jwt.cc

Jim Turner, March 06, 2015 20:07

 
1
/*
2
 * output.c
3
 * Copyright 2009-2013 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 "drct.h"
21
#include "output.h"
22
#include "runtime.h"
23

    
24
#include <math.h>
25
#include <pthread.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29

    
30
#include "equalizer.h"
31
#include "internal.h"
32
#include "plugin.h"
33
#include "plugins.h"
34

    
35
static pthread_mutex_t mutex_major = PTHREAD_MUTEX_INITIALIZER;
36
static pthread_mutex_t mutex_minor = PTHREAD_MUTEX_INITIALIZER;
37

    
38
#define LOCK_MAJOR pthread_mutex_lock (& mutex_major)
39
#define UNLOCK_MAJOR pthread_mutex_unlock (& mutex_major)
40
#define LOCK_MINOR pthread_mutex_lock (& mutex_minor)
41
#define UNLOCK_MINOR pthread_mutex_unlock (& mutex_minor)
42
#define LOCK_ALL do { LOCK_MAJOR; LOCK_MINOR; } while (0)
43
#define UNLOCK_ALL do { UNLOCK_MINOR; UNLOCK_MAJOR; } while (0)
44

    
45
/* State variables.  State changes that are allowed between LOCK_MINOR and
46
 * UNLOCK_MINOR (all others must take place between LOCK_ALL and UNLOCK_ALL):
47
 * s_paused -> true or false, s_flushed -> true, s_resetting -> true */
48

    
49
static bool s_input; /* input plugin connected */
50
static bool s_output; /* output plugin connected */
51
static bool s_gain; /* replay gain info set */
52
static bool s_paused; /* paused */
53
static bool s_flushed; /* flushed, writes ignored until resume */
54
static bool s_resetting; /* resetting output system */
55

    
56
/* Condition variable linked to LOCK_MINOR.
57
 * The input thread will wait if the following is true:
58
 *   ((! s_output || s_resetting) && ! s_flushed)
59
 * Hence you must signal if you cause the inverse to be true:
60
 *   ((s_output && ! s_resetting) || s_flushed) */
61

    
62
static pthread_cond_t cond_minor = PTHREAD_COND_INITIALIZER;
63

    
64
#define SIGNAL_MINOR pthread_cond_broadcast (& cond_minor)
65
#define WAIT_MINOR pthread_cond_wait (& cond_minor, & mutex_minor)
66

    
67
short iamPaused = 0;   /* JWT:  ADDED FOR ALLOWING PAUSE TO CONTINUE READING STREAMS, ONLY PAUSING OUTPUT! */
68

    
69
static OutputPlugin * cop;
70
static int seek_time;
71
static String in_filename;
72
static Tuple in_tuple;
73
static int in_format, in_channels, in_rate;
74
static int out_format, out_channels, out_rate;
75
static int out_bytes_per_sec, out_bytes_held;
76
static int64_t in_frames, out_bytes_written;
77
static ReplayGainInfo gain_info;
78

    
79
static bool change_op;
80
static OutputPlugin * new_op;
81

    
82
static Index<float> buffer1;
83
static Index<char> buffer2;
84

    
85
static inline int get_format ()
86
{
87
    switch (aud_get_int (0, "output_bit_depth"))
88
    {
89
        case 16: return FMT_S16_NE;
90
        case 24: return FMT_S24_NE;
91
        case 32: return FMT_S32_NE;
92
        default: return FMT_FLOAT;
93
    }
94
}
95

    
96
/* assumes LOCK_ALL, s_output */
97
static void cleanup_output ()
98
{
99
    if (! s_paused && ! s_flushed && ! s_resetting)
100
    {
101
        UNLOCK_MINOR;
102
        cop->drain ();
103
        LOCK_MINOR;
104
    }
105

    
106
    s_output = false;
107

    
108
    buffer1.clear ();
109
    buffer2.clear ();
110

    
111
    effect_flush (true);
112
    cop->close_audio ();
113
    vis_runner_start_stop (false, false);
114
}
115

    
116
/* assumes LOCK_ALL, s_output */
117
static void apply_pause ()
118
{
119
    /* JWT:  IF PauseMute SET, PAUSE SHOULD JUST SUSPEND OUTPUT
120
             THIS IS SO WE CAN "MUTE" COMMERCIALS FROM RADIO WHILST PLAYING 
121
             SOMETHING ELSE!  THIS WAY, WE CAN RESUME AT THE CURRENT POINT vs
122
             WHERE WE PAUSED!
123
    */
124
    if (aud_get_pausemute_mode ())
125
        iamPaused = s_paused;
126
    else
127
    {
128
        cop->pause (s_paused);
129
        vis_runner_start_stop (true, s_paused);
130
    }
131
}
132

    
133
/* assumes LOCK_ALL, s_input */
134
static void setup_output ()
135
{
136
    int format = get_format ();
137
    int channels = in_channels;
138
    int rate = in_rate;
139

    
140
    effect_start (channels, rate);
141
    eq_set_format (channels, rate);
142

    
143
    if (s_output && format == out_format && channels == out_channels && rate ==
144
     out_rate && ! cop->force_reopen)
145
    {
146
        AUDINFO ("Reusing output stream, format %d, %d channels, %d Hz.\n", format, channels, rate);
147
        return;
148
    }
149

    
150
    if (s_output)
151
        cleanup_output ();
152

    
153
    if (! cop)
154
        return;
155

    
156
    cop->set_info (in_filename, in_tuple);
157
    if (! cop->open_audio (format, rate, channels))
158
        return;
159

    
160
    AUDINFO ("Opened output stream, format %d, %d channels, %d Hz.\n", format, channels, rate);
161

    
162
    s_output = true;
163

    
164
    out_format = format;
165
    out_channels = channels;
166
    out_rate = rate;
167
    out_bytes_per_sec = FMT_SIZEOF (format) * channels * rate;
168
    out_bytes_held = 0;
169
    out_bytes_written = 0;
170

    
171
    apply_pause ();
172

    
173
    if (! s_flushed && ! s_resetting)
174
        SIGNAL_MINOR;
175
}
176

    
177
/* assumes LOCK_MINOR, s_output */
178
static bool flush_output (bool force)
179
{
180
    if (! effect_flush (force))
181
        return false;
182

    
183
    out_bytes_held = 0;
184
    out_bytes_written = 0;
185

    
186
    cop->flush ();
187
    vis_runner_flush ();
188
    return true;
189
}
190

    
191
static void apply_replay_gain (Index<float> & data)
192
{
193
    if (! aud_get_bool (0, "enable_replay_gain"))
194
        return;
195

    
196
    float factor = powf (10, aud_get_double (0, "replay_gain_preamp") / 20);
197

    
198
    if (s_gain)
199
    {
200
        float peak;
201

    
202
        if (aud_get_bool (0, "replay_gain_album"))
203
        {
204
            factor *= powf (10, gain_info.album_gain / 20);
205
            peak = gain_info.album_peak;
206
        }
207
        else
208
        {
209
            factor *= powf (10, gain_info.track_gain / 20);
210
            peak = gain_info.track_peak;
211
        }
212

    
213
        if (aud_get_bool (0, "enable_clipping_prevention") && peak * factor > 1)
214
            factor = 1 / peak;
215
    }
216
    else
217
        factor *= powf (10, aud_get_double (0, "default_gain") / 20);
218

    
219
    if (factor < 0.99 || factor > 1.01)
220
        audio_amplify (data.begin (), 1, data.len (), & factor);
221
}
222

    
223
/* assumes LOCK_ALL, s_output */
224
static void write_output_raw (Index<float> & data)
225
{
226
    if (! data.len ())
227
        return;
228

    
229
    int out_time = aud::rescale<int64_t> (out_bytes_written, out_bytes_per_sec, 1000);
230
    vis_runner_pass_audio (out_time, data, out_channels, out_rate);
231

    
232
    eq_filter (data.begin (), data.len ());
233

    
234
    if (aud_get_bool (0, "software_volume_control"))
235
    {
236
        StereoVolume v = {aud_get_int (0, "sw_volume_left"), aud_get_int (0, "sw_volume_right")};
237
        audio_amplify (data.begin (), out_channels, data.len () / out_channels, v);
238
    }
239

    
240
    if (aud_get_bool (0, "soft_clipping"))
241
        audio_soft_clip (data.begin (), data.len ());
242

    
243
    void * out_data = data.begin ();
244

    
245
    if (out_format != FMT_FLOAT)
246
    {
247
        buffer2.resize (FMT_SIZEOF (out_format) * data.len ());
248
        audio_to_int (data.begin (), buffer2.begin (), out_format, data.len ());
249
        out_data = buffer2.begin ();
250
    }
251

    
252
    out_bytes_held = FMT_SIZEOF (out_format) * data.len ();
253

    
254
    while (! s_flushed && ! s_resetting)
255
    {
256

    
257
        /* JWT: ZERO-OUT (SILENCE) STREAM GOING TO OUTPUT WHILST PAUSING */
258
        if (iamPaused)
259
            memset (out_data, '\0', out_bytes_held);
260

    
261
        int written = cop->write_audio (out_data, out_bytes_held);
262

    
263
        out_data = (char *) out_data + written;
264
        out_bytes_held -= written;
265
        out_bytes_written += written;
266

    
267
        if (! out_bytes_held)
268
            break;
269

    
270
        UNLOCK_MINOR;
271
        cop->period_wait ();
272
        LOCK_MINOR;
273
    }
274
}
275

    
276
/* assumes LOCK_ALL, s_input, s_output */
277
static bool write_output (const void * data, int size, int stop_time)
278
{
279
    int samples = size / FMT_SIZEOF (in_format);
280
    bool stopped = false;
281

    
282
    if (stop_time != -1)
283
    {
284
        int64_t frames_left = aud::rescale<int64_t> (stop_time - seek_time, 1000, in_rate) - in_frames;
285
        int64_t samples_left = in_channels * aud::max ((int64_t) 0, frames_left);
286

    
287
        if (samples >= samples_left)
288
        {
289
            samples = samples_left;
290
            stopped = true;
291
        }
292
    }
293

    
294
    in_frames += samples / in_channels;
295

    
296
    buffer1.resize (samples);
297

    
298
    if (in_format == FMT_FLOAT)
299
        memcpy (buffer1.begin (), data, sizeof (float) * samples);
300
    else
301
        audio_from_int (data, in_format, buffer1.begin (), samples);
302

    
303
    apply_replay_gain (buffer1);
304
    write_output_raw (effect_process (buffer1));
305

    
306
    return ! stopped;
307
}
308

    
309
/* assumes LOCK_ALL, s_output */
310
static void finish_effects (bool end_of_playlist)
311
{
312
    buffer1.resize (0);
313
    write_output_raw (effect_finish (buffer1, end_of_playlist));
314
}
315

    
316
bool output_open_audio (const String & filename, const Tuple & tuple,
317
 int format, int rate, int channels, int start_time)
318
{
319
    /* prevent division by zero */
320
    if (rate < 1 || channels < 1 || channels > AUD_MAX_CHANNELS)
321
        return false;
322

    
323
    LOCK_ALL;
324

    
325
    if (s_output && s_paused)
326
    {
327
        if (! s_flushed && ! s_resetting)
328
            flush_output (true);
329

    
330
        s_paused = false;
331
        apply_pause ();
332
    }
333

    
334
    s_input = true;
335
    s_gain = s_paused = s_flushed = false;
336
    seek_time = start_time;
337

    
338
    in_filename = filename;
339
    in_tuple = tuple.ref ();
340
    in_format = format;
341
    in_channels = channels;
342
    in_rate = rate;
343
    in_frames = 0;
344

    
345
    setup_output ();
346

    
347
    UNLOCK_ALL;
348
    return true;
349
}
350

    
351
void output_set_replay_gain (const ReplayGainInfo & info)
352
{
353
    LOCK_ALL;
354

    
355
    if (s_input)
356
    {
357
        gain_info = info;
358
        s_gain = true;
359

    
360
        AUDINFO ("Replay Gain info:\n");
361
        AUDINFO (" album gain: %f dB\n", info.album_gain);
362
        AUDINFO (" album peak: %f\n", info.album_peak);
363
        AUDINFO (" track gain: %f dB\n", info.track_gain);
364
        AUDINFO (" track peak: %f\n", info.track_peak);
365
    }
366

    
367
    UNLOCK_ALL;
368
}
369

    
370
/* returns false if stop_time is reached */
371
bool output_write_audio (const void * data, int size, int stop_time)
372
{
373
RETRY:
374
    LOCK_ALL;
375
    bool good = false;
376

    
377
    if (s_input && ! s_flushed)
378
    {
379
        if (! s_output || s_resetting)
380
        {
381
            UNLOCK_MAJOR;
382
            WAIT_MINOR;
383
            UNLOCK_MINOR;
384
            goto RETRY;
385
        }
386

    
387
        good = write_output (data, size, stop_time);
388
    }
389

    
390
    UNLOCK_ALL;
391
    return good;
392
}
393

    
394
void output_flush (int time, bool force)
395
{
396
    LOCK_MINOR;
397

    
398
    if (s_input && ! s_flushed)
399
    {
400
        if (s_output && ! s_resetting)
401
        {
402
            // allow effect plugins to prevent the flush, but
403
            // always flush if paused to prevent locking up
404
            s_flushed = flush_output (s_paused || force);
405
        }
406
        else
407
        {
408
            s_flushed = true;
409
            SIGNAL_MINOR;
410
        }
411
    }
412

    
413
    if (s_input)
414
    {
415
        seek_time = time;
416
        in_frames = 0;
417
    }
418

    
419
    UNLOCK_MINOR;
420
}
421

    
422
void output_resume ()
423
{
424
    LOCK_ALL;
425

    
426
    if (s_input)
427
        s_flushed = false;
428

    
429
    UNLOCK_ALL;
430
}
431

    
432
void output_pause (bool pause)
433
{
434
    LOCK_MINOR;
435

    
436
    if (s_input)
437
    {
438
        s_paused = pause;
439

    
440
        if (s_output)
441
            apply_pause ();
442
    }
443

    
444
    UNLOCK_MINOR;
445
}
446

    
447
int output_get_time ()
448
{
449
    LOCK_MINOR;
450
    int time = 0, delay = 0;
451

    
452
    if (s_input)
453
    {
454
        if (s_output)
455
        {
456
            delay = cop->get_delay ();
457
            delay += aud::rescale<int64_t> (out_bytes_held, out_bytes_per_sec, 1000);
458
        }
459

    
460
        delay = effect_adjust_delay (delay);
461
        time = aud::rescale<int64_t> (in_frames, in_rate, 1000);
462
        time = seek_time + aud::max (time - delay, 0);
463
    }
464

    
465
    UNLOCK_MINOR;
466
    return time;
467
}
468

    
469
int output_get_raw_time ()
470
{
471
    LOCK_MINOR;
472
    int time = 0;
473

    
474
    if (s_output)
475
    {
476
        time = aud::rescale<int64_t> (out_bytes_written, out_bytes_per_sec, 1000);
477
        time = aud::max (time - cop->get_delay (), 0);
478
    }
479

    
480
    UNLOCK_MINOR;
481
    return time;
482
}
483

    
484
void output_close_audio ()
485
{
486
    LOCK_ALL;
487

    
488
    if (s_input)
489
    {
490
        s_input = false;
491
        in_filename = String ();
492
        in_tuple = Tuple ();
493

    
494
        if (s_output && ! (s_paused || s_flushed || s_resetting))
495
            finish_effects (false); /* first time for end of song */
496
    }
497

    
498
    UNLOCK_ALL;
499
}
500

    
501
void output_drain ()
502
{
503
    LOCK_ALL;
504

    
505
    if (! s_input && s_output)
506
    {
507
        finish_effects (true); /* second time for end of playlist */
508
        cleanup_output ();
509
    }
510

    
511
    UNLOCK_ALL;
512
}
513

    
514
EXPORT void aud_output_reset (OutputReset type)
515
{
516
    LOCK_MINOR;
517

    
518
    s_resetting = true;
519

    
520
    if (s_output && ! s_flushed)
521
        flush_output (true);
522

    
523
    UNLOCK_MINOR;
524
    LOCK_ALL;
525

    
526
    if (s_output && type != OutputReset::EffectsOnly)
527
        cleanup_output ();
528

    
529
    if (type == OutputReset::ResetPlugin)
530
    {
531
        if (cop)
532
            cop->cleanup ();
533

    
534
        if (change_op)
535
            cop = new_op;
536

    
537
        if (cop && ! cop->init ())
538
            cop = nullptr;
539
    }
540

    
541
    if (s_input)
542
        setup_output ();
543

    
544
    s_resetting = false;
545

    
546
    if (s_output && ! s_flushed)
547
        SIGNAL_MINOR;
548

    
549
    UNLOCK_ALL;
550
}
551

    
552
EXPORT StereoVolume aud_drct_get_volume ()
553
{
554
    StereoVolume volume = {0, 0};
555
    LOCK_MINOR;
556

    
557
    if (aud_get_bool (0, "software_volume_control"))
558
        volume = {aud_get_int (0, "sw_volume_left"), aud_get_int (0, "sw_volume_right")};
559
    else if (cop)
560
        volume = cop->get_volume ();
561

    
562
    UNLOCK_MINOR;
563
    return volume;
564
}
565

    
566
EXPORT void aud_drct_set_volume (StereoVolume volume)
567
{
568
    LOCK_MINOR;
569

    
570
    volume.left = aud::clamp (volume.left, 0, 100);
571
    volume.right = aud::clamp (volume.right, 0, 100);
572

    
573
    if (aud_get_bool (0, "software_volume_control"))
574
    {
575
        aud_set_int (0, "sw_volume_left", volume.left);
576
        aud_set_int (0, "sw_volume_right", volume.right);
577
    }
578
    else if (cop)
579
        cop->set_volume (volume);
580

    
581
    UNLOCK_MINOR;
582
}
583

    
584
PluginHandle * output_plugin_get_current ()
585
{
586
    return cop ? aud_plugin_by_header (cop) : nullptr;
587
}
588

    
589
bool output_plugin_set_current (PluginHandle * plugin)
590
{
591
    change_op = true;
592
    new_op = plugin ? (OutputPlugin *) aud_plugin_get_header (plugin) : nullptr;
593
    aud_output_reset (OutputReset::ResetPlugin);
594

    
595
    bool success = (! plugin || (new_op && cop == new_op));
596
    change_op = false;
597
    new_op = nullptr;
598

    
599
    return success;
600
}