dtstofloat32.c 12.7 KB
Newer Older
Gildas Bazin's avatar
 
Gildas Bazin committed
1 2
/*****************************************************************************
 * dtstofloat32.c: DTS Coherent Acoustics decoder plugin for VLC.
3 4
 *   This plugin makes use of libdca to do the actual decoding
 *   (http://developers.videolan.org/libdca.html).
Gildas Bazin's avatar
 
Gildas Bazin committed
5
 *****************************************************************************
6
 * Copyright (C) 2001, 2002libdca the VideoLAN team
7
 * $Id$
Gildas Bazin's avatar
 
Gildas Bazin committed
8
 *
9
 * Author: Gildas Bazin <gbazin@videolan.org>
10
 *
Gildas Bazin's avatar
 
Gildas Bazin committed
11 12 13 14
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
15
 *
Gildas Bazin's avatar
 
Gildas Bazin committed
16 17 18 19 20 21 22
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
Gildas Bazin's avatar
 
Gildas Bazin committed
24 25 26 27 28
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
29 30 31 32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

33
#include <vlc_common.h>
34
#include <vlc_plugin.h>
Gildas Bazin's avatar
 
Gildas Bazin committed
35 36


Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
37
#include <dca.h>                                       /* libdca header file */
Gildas Bazin's avatar
 
Gildas Bazin committed
38

Clément Stenac's avatar
Clément Stenac committed
39 40
#include <vlc_aout.h>
#include <vlc_block.h>
41
#include <vlc_filter.h>
Gildas Bazin's avatar
 
Gildas Bazin committed
42 43 44 45

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
46 47 48 49
static int  OpenFilter ( vlc_object_t * );
static void CloseFilter( vlc_object_t * );
static block_t *Convert( filter_t *, block_t * );

50
/* libdca channel order
51
 * libdca currently only decodes 5.1, even if you have a DTS-ES source. */
52
static const uint32_t pi_channels_in[] =
53 54 55
{ AOUT_CHAN_CENTER, AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
  AOUT_CHAN_REARCENTER, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
  AOUT_CHAN_LFE,
56
  0 };
57

Gildas Bazin's avatar
 
Gildas Bazin committed
58 59 60
/*****************************************************************************
 * Local structures
 *****************************************************************************/
61
struct filter_sys_t
Gildas Bazin's avatar
 
Gildas Bazin committed
62
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
63
    dca_state_t * p_libdca; /* libdca internal structure */
64
    bool b_dynrng; /* see below */
65
    int i_flags; /* libdca flags, see dtsdec/doc/libdts.txt */
66
    bool b_dontwarn;
Gildas Bazin's avatar
 
Gildas Bazin committed
67
    int i_nb_channels; /* number of float32 per sample */
68 69

    int pi_chan_table[AOUT_CHAN_MAX]; /* channel reordering */
Gildas Bazin's avatar
 
Gildas Bazin committed
70 71 72 73 74 75 76 77 78 79 80 81 82
};

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
#define DYNRNG_TEXT N_("DTS dynamic range compression")
#define DYNRNG_LONGTEXT N_( \
    "Dynamic range compression makes the loud sounds softer, and the soft " \
    "sounds louder, so you can more easily listen to the stream in a noisy " \
    "environment without disturbing anyone. If you disable the dynamic range "\
    "compression the playback will be more adapted to a movie theater or a " \
    "listening room.")

83 84 85 86 87
vlc_module_begin ()
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_ACODEC )
    set_shortname( "DCA" )
    set_description( N_("DTS Coherent Acoustics audio decoder") )
88
    add_bool( "dts-dynrng", true, DYNRNG_TEXT, DYNRNG_LONGTEXT, false )
89
    set_capability( "audio filter", 100 )
90 91
    set_callbacks( OpenFilter, CloseFilter )
vlc_module_end ()
Gildas Bazin's avatar
 
Gildas Bazin committed
92

93
/*****************************************************************************
94
 * Open:
95 96
 *****************************************************************************/
static int Open( vlc_object_t *p_this, filter_sys_t *p_sys,
97 98
                 const audio_format_t *restrict input,
                 const audio_format_t *restrict output )
99
{
100
    p_sys->b_dynrng = var_InheritBool( p_this, "dts-dynrng" );
Gildas Bazin's avatar
 
Gildas Bazin committed
101 102 103
    p_sys->b_dontwarn = 0;

    /* We'll do our own downmixing, thanks. */
104 105
    p_sys->i_nb_channels = aout_FormatNbChannels( output );
    switch ( (output->i_physical_channels & AOUT_CHAN_PHYSMASK)
Gildas Bazin's avatar
 
Gildas Bazin committed
106 107 108
              & ~AOUT_CHAN_LFE )
    {
    case AOUT_CHAN_CENTER:
109 110
        if ( (output->i_original_channels & AOUT_CHAN_CENTER)
              || (output->i_original_channels
Gildas Bazin's avatar
 
Gildas Bazin committed
111 112
                   & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) )
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
113
            p_sys->i_flags = DCA_MONO;
Gildas Bazin's avatar
 
Gildas Bazin committed
114 115 116 117
        }
        break;

    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT:
118
        if ( output->i_original_channels & AOUT_CHAN_DOLBYSTEREO )
Gildas Bazin's avatar
 
Gildas Bazin committed
119
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
120
            p_sys->i_flags = DCA_DOLBY;
Gildas Bazin's avatar
 
Gildas Bazin committed
121
        }
122
        else if ( input->i_original_channels == AOUT_CHAN_CENTER )
Gildas Bazin's avatar
 
Gildas Bazin committed
123
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
124
            p_sys->i_flags = DCA_MONO;
Gildas Bazin's avatar
 
Gildas Bazin committed
125
        }
126
        else if ( input->i_original_channels & AOUT_CHAN_DUALMONO )
Gildas Bazin's avatar
 
Gildas Bazin committed
127
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
128
            p_sys->i_flags = DCA_CHANNEL;
Gildas Bazin's avatar
 
Gildas Bazin committed
129 130 131
        }
        else
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
132
            p_sys->i_flags = DCA_STEREO;
Gildas Bazin's avatar
 
Gildas Bazin committed
133 134 135 136
        }
        break;

    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
137
        p_sys->i_flags = DCA_3F;
Gildas Bazin's avatar
 
Gildas Bazin committed
138 139 140
        break;

    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
141
        p_sys->i_flags = DCA_2F1R;
Gildas Bazin's avatar
 
Gildas Bazin committed
142 143 144 145
        break;

    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
          | AOUT_CHAN_REARCENTER:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
146
        p_sys->i_flags = DCA_3F1R;
Gildas Bazin's avatar
 
Gildas Bazin committed
147 148 149 150
        break;

    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
151
        p_sys->i_flags = DCA_2F2R;
Gildas Bazin's avatar
 
Gildas Bazin committed
152 153 154 155
        break;

    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
          | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
156
        p_sys->i_flags = DCA_3F2R;
Gildas Bazin's avatar
 
Gildas Bazin committed
157 158 159
        break;

    default:
160
        msg_Warn( p_this, "unknown sample format!" );
Gildas Bazin's avatar
 
Gildas Bazin committed
161 162 163
        free( p_sys );
        return -1;
    }
164
    if ( output->i_physical_channels & AOUT_CHAN_LFE )
Gildas Bazin's avatar
 
Gildas Bazin committed
165
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
166
        p_sys->i_flags |= DCA_LFE;
Gildas Bazin's avatar
 
Gildas Bazin committed
167
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
168
    //p_sys->i_flags |= DCA_ADJUST_LEVEL;
Gildas Bazin's avatar
 
Gildas Bazin committed
169

170
    /* Initialize libdca */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
171 172
    p_sys->p_libdca = dca_init( 0 );
    if( p_sys->p_libdca == NULL )
Gildas Bazin's avatar
 
Gildas Bazin committed
173
    {
174
        msg_Err( p_this, "unable to initialize libdca" );
175
        return VLC_EGENERIC;
Gildas Bazin's avatar
 
Gildas Bazin committed
176 177
    }

178
    aout_CheckChannelReorder( pi_channels_in, NULL,
179
                              output->i_physical_channels & AOUT_CHAN_PHYSMASK,
180 181 182
                              p_sys->i_nb_channels,
                              p_sys->pi_chan_table );

183
    return VLC_SUCCESS;
Gildas Bazin's avatar
 
Gildas Bazin committed
184 185 186 187 188
}

/*****************************************************************************
 * Interleave: helper function to interleave channels
 *****************************************************************************/
189 190
static void Interleave( float * p_out, const float * p_in, int i_nb_channels,
                        int *pi_chan_table )
Gildas Bazin's avatar
 
Gildas Bazin committed
191
{
192
    /* We do not only have to interleave, but also reorder the channels. */
Gildas Bazin's avatar
 
Gildas Bazin committed
193 194 195 196 197 198

    int i, j;
    for ( j = 0; j < i_nb_channels; j++ )
    {
        for ( i = 0; i < 256; i++ )
        {
199
            p_out[i * i_nb_channels + pi_chan_table[j]] = p_in[j * 256 + i];
Gildas Bazin's avatar
 
Gildas Bazin committed
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
        }
    }
}

/*****************************************************************************
 * Duplicate: helper function to duplicate a unique channel
 *****************************************************************************/
static void Duplicate( float * p_out, const float * p_in )
{
    int i;

    for ( i = 256; i--; )
    {
        *p_out++ = *p_in;
        *p_out++ = *p_in;
        p_in++;
    }
}

/*****************************************************************************
 * Exchange: helper function to exchange left & right channels
 *****************************************************************************/
static void Exchange( float * p_out, const float * p_in )
{
    int i;
    const float * p_first = p_in + 256;
    const float * p_second = p_in;

    for ( i = 0; i < 256; i++ )
    {
        *p_out++ = *p_first++;
        *p_out++ = *p_second++;
    }
}

/*****************************************************************************
 * DoWork: decode a DTS frame.
 *****************************************************************************/
238
static void DoWork( filter_t * p_filter,
Gildas Bazin's avatar
 
Gildas Bazin committed
239 240
                    aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
{
241
    filter_sys_t    *p_sys = p_filter->p_sys;
Gildas Bazin's avatar
 
Gildas Bazin committed
242 243 244 245 246 247 248 249 250 251 252 253 254
    sample_t        i_sample_level = 1;
    int             i_flags = p_sys->i_flags;
    int             i_bytes_per_block = 256 * p_sys->i_nb_channels
                      * sizeof(float);
    int             i;

    /*
     * Do the actual decoding now.
     */

    /* Needs to be called so the decoder knows which type of bitstream it is
     * dealing with. */
    int i_sample_rate, i_bit_rate, i_frame_length;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
255
    if( !dca_syncinfo( p_sys->p_libdca, p_in_buf->p_buffer, &i_flags,
Gildas Bazin's avatar
 
Gildas Bazin committed
256 257
                       &i_sample_rate, &i_bit_rate, &i_frame_length ) )
    {
258
        msg_Warn( p_filter, "libdca couldn't sync on frame" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
259
        p_out_buf->i_nb_samples = p_out_buf->i_buffer = 0;
Gildas Bazin's avatar
 
Gildas Bazin committed
260 261 262 263
        return;
    }

    i_flags = p_sys->i_flags;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
264
    dca_frame( p_sys->p_libdca, p_in_buf->p_buffer,
Gildas Bazin's avatar
 
Gildas Bazin committed
265 266
               &i_flags, &i_sample_level, 0 );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
267
    if ( (i_flags & DCA_CHANNEL_MASK) != (p_sys->i_flags & DCA_CHANNEL_MASK)
Gildas Bazin's avatar
 
Gildas Bazin committed
268 269
          && !p_sys->b_dontwarn )
    {
270
        msg_Warn( p_filter,
271
                  "libdca couldn't do the requested downmix 0x%x->0x%x",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
272 273
                  p_sys->i_flags  & DCA_CHANNEL_MASK,
                  i_flags & DCA_CHANNEL_MASK );
Gildas Bazin's avatar
 
Gildas Bazin committed
274 275 276 277 278 279

        p_sys->b_dontwarn = 1;
    }

    if( 0)//!p_sys->b_dynrng )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
280
        dca_dynrng( p_sys->p_libdca, NULL, NULL );
Gildas Bazin's avatar
 
Gildas Bazin committed
281 282
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
283
    for ( i = 0; i < dca_blocks_num(p_sys->p_libdca); i++ )
Gildas Bazin's avatar
 
Gildas Bazin committed
284 285 286
    {
        sample_t * p_samples;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
287
        if( dca_block( p_sys->p_libdca ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
288
        {
289
            msg_Warn( p_filter, "dca_block failed for block %d", i );
Gildas Bazin's avatar
 
Gildas Bazin committed
290 291 292
            break;
        }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
293
        p_samples = dca_samples( p_sys->p_libdca );
Gildas Bazin's avatar
 
Gildas Bazin committed
294

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
295
        if ( (p_sys->i_flags & DCA_CHANNEL_MASK) == DCA_MONO
296
              && (p_filter->fmt_out.audio.i_physical_channels
Gildas Bazin's avatar
 
Gildas Bazin committed
297 298 299 300 301
                   & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) )
        {
            Duplicate( (float *)(p_out_buf->p_buffer + i * i_bytes_per_block),
                       p_samples );
        }
302
        else if ( p_filter->fmt_out.audio.i_original_channels
Gildas Bazin's avatar
 
Gildas Bazin committed
303 304 305 306 307 308 309
                    & AOUT_CHAN_REVERSESTEREO )
        {
            Exchange( (float *)(p_out_buf->p_buffer + i * i_bytes_per_block),
                      p_samples );
        }
        else
        {
310
            /* Interleave the *$£%ù samples. */
Gildas Bazin's avatar
 
Gildas Bazin committed
311
            Interleave( (float *)(p_out_buf->p_buffer + i * i_bytes_per_block),
312
                        p_samples, p_sys->i_nb_channels, p_sys->pi_chan_table);
Gildas Bazin's avatar
 
Gildas Bazin committed
313 314 315 316
        }
    }

    p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
317
    p_out_buf->i_buffer = i_bytes_per_block * i;
Gildas Bazin's avatar
 
Gildas Bazin committed
318 319
}

320
/*****************************************************************************
321
 * OpenFilter:
322 323 324 325 326 327 328
 *****************************************************************************/
static int OpenFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys;
    int i_ret;

329 330
    if( p_filter->fmt_in.i_codec != VLC_CODEC_DTS
     || p_filter->fmt_out.audio.i_format != VLC_CODEC_FL32 )
331 332 333 334 335
        return VLC_EGENERIC;

    /* Allocate the memory needed to store the module's structure */
    p_sys = p_filter->p_sys = malloc( sizeof(filter_sys_t) );
    if( p_sys == NULL )
336
        return VLC_ENOMEM;
337 338

    i_ret = Open( VLC_OBJECT(p_filter), p_sys,
339
                  &p_filter->fmt_in.audio, &p_filter->fmt_out.audio );
340 341

    p_filter->pf_audio_filter = Convert;
342
    p_filter->fmt_out.audio.i_rate = p_filter->fmt_in.audio.i_rate;
343 344 345 346 347 348 349 350 351 352 353 354

    return i_ret;
}

/*****************************************************************************
 * CloseFilter : deallocate data structures
 *****************************************************************************/
static void CloseFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys = p_filter->p_sys;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
355
    dca_free( p_sys->p_libdca );
356 357 358 359 360
    free( p_sys );
}

static block_t *Convert( filter_t *p_filter, block_t *p_block )
{
361
    if( !p_block || !p_block->i_nb_samples )
Gildas Bazin's avatar
*...  
Gildas Bazin committed
362
    {
363 364
        if( p_block )
            block_Release( p_block );
Gildas Bazin's avatar
*...  
Gildas Bazin committed
365 366 367
        return NULL;
    }

368
    size_t i_out_size = p_block->i_nb_samples *
369
      p_filter->fmt_out.audio.i_bitspersample *
370
        p_filter->fmt_out.audio.i_channels / 8;
371

372
    block_t *p_out = block_Alloc( i_out_size );
373 374 375
    if( !p_out )
    {
        msg_Warn( p_filter, "can't get output buffer" );
376
        block_Release( p_block );
377 378 379
        return NULL;
    }

380
    p_out->i_nb_samples = p_block->i_nb_samples;
381 382 383 384
    p_out->i_dts = p_block->i_dts;
    p_out->i_pts = p_block->i_pts;
    p_out->i_length = p_block->i_length;

385
    DoWork( p_filter, p_block, p_out );
386

387
    block_Release( p_block );
Gildas Bazin's avatar
*...  
Gildas Bazin committed
388

389 390
    return p_out;
}