dtstofloat32.c 15.8 KB
Newer Older
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).
5
 *****************************************************************************
6
 * Copyright (C) 2001, 2002libdca the VideoLAN team
7
 * $Id$
8
 *
9
 * Author: Gildas Bazin <gbazin@videolan.org>
10
 *
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
 *
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.
24 25 26 27 28
 *****************************************************************************/

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

33 34 35
#include <vlc/vlc.h>


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

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

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  Create    ( vlc_object_t * );
static void Destroy   ( vlc_object_t * );
47
static void DoWork    ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
48 49
                        aout_buffer_t * );

50 51 52 53 54 55 56
static int  Open      ( vlc_object_t *, filter_sys_t *,
                        audio_format_t, audio_format_t );

static int  OpenFilter ( vlc_object_t * );
static void CloseFilter( vlc_object_t * );
static block_t *Convert( filter_t *, block_t * );

57
/* libdca channel order */
58 59 60 61 62 63 64 65
static const uint32_t pi_channels_in[] =
{ AOUT_CHAN_CENTER, AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
  AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_LFE, 0 };
/* our internal channel order (WG-4 order) */
static const uint32_t pi_channels_out[] =
{ AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
  AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };

66 67 68
/*****************************************************************************
 * Local structures
 *****************************************************************************/
69
struct filter_sys_t
70
{
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
71
    dca_state_t * p_libdca; /* libdca internal structure */
72
    vlc_bool_t b_dynrng; /* see below */
73
    int i_flags; /* libdca flags, see dtsdec/doc/libdts.txt */
74 75
    vlc_bool_t b_dontwarn;
    int i_nb_channels; /* number of float32 per sample */
76 77

    int pi_chan_table[AOUT_CHAN_MAX]; /* channel reordering */
78 79 80 81 82 83 84 85 86 87 88 89 90 91
};

/*****************************************************************************
 * 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.")

vlc_module_begin();
Clément Stenac's avatar
Clément Stenac committed
92 93
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_ACODEC );
Clément Stenac's avatar
Clément Stenac committed
94
    set_shortname( "DCA" );
95 96 97 98
    set_description( _("DTS Coherent Acoustics audio decoder") );
    add_bool( "dts-dynrng", 1, NULL, DYNRNG_TEXT, DYNRNG_LONGTEXT, VLC_FALSE );
    set_capability( "audio filter", 100 );
    set_callbacks( Create, Destroy );
99 100 101 102 103

    add_submodule();
    set_description( _("DTS Coherent Acoustics audio decoder") );
    set_capability( "audio filter2", 100 );
    set_callbacks( OpenFilter, CloseFilter );
104 105 106
vlc_module_end();

/*****************************************************************************
Clément Stenac's avatar
Clément Stenac committed
107
 * Create:
108
 *****************************************************************************/
109
static int Create( vlc_object_t *p_this )
110
{
111 112 113
    aout_filter_t *p_filter = (aout_filter_t *)p_this;
    filter_sys_t *p_sys;
    int i_ret;
114 115 116 117 118 119 120 121 122 123 124 125 126

    if ( p_filter->input.i_format != VLC_FOURCC('d','t','s',' ')
          || p_filter->output.i_format != VLC_FOURCC('f','l','3','2') )
    {
        return -1;
    }

    if ( p_filter->input.i_rate != p_filter->output.i_rate )
    {
        return -1;
    }

    /* Allocate the memory needed to store the module's structure */
127 128
    p_sys = malloc( sizeof(filter_sys_t) );
    p_filter->p_sys = (struct aout_filter_sys_t *)p_sys;
129 130 131 132 133 134
    if( p_sys == NULL )
    {
        msg_Err( p_filter, "out of memory" );
        return -1;
    }

135 136 137 138 139 140 141 142 143 144
    i_ret = Open( VLC_OBJECT(p_filter), p_sys,
                  p_filter->input, p_filter->output );

    p_filter->pf_do_work = DoWork;
    p_filter->b_in_place = 0;

    return i_ret;
}

/*****************************************************************************
145
 * Open:
146 147 148 149 150
 *****************************************************************************/
static int Open( vlc_object_t *p_this, filter_sys_t *p_sys,
                 audio_format_t input, audio_format_t output )
{
    p_sys->b_dynrng = config_GetInt( p_this, "dts-dynrng" );
151 152 153
    p_sys->b_dontwarn = 0;

    /* We'll do our own downmixing, thanks. */
154 155
    p_sys->i_nb_channels = aout_FormatNbChannels( &output );
    switch ( (output.i_physical_channels & AOUT_CHAN_PHYSMASK)
156 157 158
              & ~AOUT_CHAN_LFE )
    {
    case AOUT_CHAN_CENTER:
159 160
        if ( (output.i_original_channels & AOUT_CHAN_CENTER)
              || (output.i_original_channels
161 162
                   & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) )
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
163
            p_sys->i_flags = DCA_MONO;
164 165 166 167
        }
        break;

    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT:
168
        if ( output.i_original_channels & AOUT_CHAN_DOLBYSTEREO )
169
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
170
            p_sys->i_flags = DCA_DOLBY;
171
        }
172
        else if ( input.i_original_channels == AOUT_CHAN_CENTER )
173
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
174
            p_sys->i_flags = DCA_MONO;
175
        }
176
        else if ( input.i_original_channels & AOUT_CHAN_DUALMONO )
177
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
178
            p_sys->i_flags = DCA_CHANNEL;
179 180 181
        }
        else
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
182
            p_sys->i_flags = DCA_STEREO;
183 184 185 186
        }
        break;

    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
187
        p_sys->i_flags = DCA_3F;
188 189 190
        break;

    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARCENTER:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
191
        p_sys->i_flags = DCA_2F1R;
192 193 194 195
        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
196
        p_sys->i_flags = DCA_3F1R;
197 198 199 200
        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
201
        p_sys->i_flags = DCA_2F2R;
202 203 204 205
        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
206
        p_sys->i_flags = DCA_3F2R;
207 208 209
        break;

    default:
210
        msg_Warn( p_this, "unknown sample format!" );
211 212 213
        free( p_sys );
        return -1;
    }
214
    if ( output.i_physical_channels & AOUT_CHAN_LFE )
215
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
216
        p_sys->i_flags |= DCA_LFE;
217
    }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
218
    //p_sys->i_flags |= DCA_ADJUST_LEVEL;
219

220
    /* Initialize libdca */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
221 222
    p_sys->p_libdca = dca_init( 0 );
    if( p_sys->p_libdca == NULL )
223
    {
224
        msg_Err( p_this, "unable to initialize libdca" );
225
        return VLC_EGENERIC;
226 227
    }

228 229 230 231 232
    aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
                              output.i_physical_channels & AOUT_CHAN_PHYSMASK,
                              p_sys->i_nb_channels,
                              p_sys->pi_chan_table );

233
    return VLC_SUCCESS;
234 235 236 237 238
}

/*****************************************************************************
 * Interleave: helper function to interleave channels
 *****************************************************************************/
239 240
static void Interleave( float * p_out, const float * p_in, int i_nb_channels,
                        int *pi_chan_table )
241
{
242
    /* We do not only have to interleave, but also reorder the channels. */
243 244 245 246 247 248

    int i, j;
    for ( j = 0; j < i_nb_channels; j++ )
    {
        for ( i = 0; i < 256; i++ )
        {
249
            p_out[i * i_nb_channels + pi_chan_table[j]] = p_in[j * 256 + i];
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
        }
    }
}

/*****************************************************************************
 * 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.
 *****************************************************************************/
static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
                    aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
{
291
    filter_sys_t    *p_sys = (filter_sys_t *)p_filter->p_sys;
292 293 294 295 296 297 298 299 300 301 302 303 304
    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
305
    if( !dca_syncinfo( p_sys->p_libdca, p_in_buf->p_buffer, &i_flags,
306 307
                       &i_sample_rate, &i_bit_rate, &i_frame_length ) )
    {
308
        msg_Warn( p_aout, "libdca couldn't sync on frame" );
309 310 311 312 313
        p_out_buf->i_nb_samples = p_out_buf->i_nb_bytes = 0;
        return;
    }

    i_flags = p_sys->i_flags;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
314
    dca_frame( p_sys->p_libdca, p_in_buf->p_buffer,
315 316
               &i_flags, &i_sample_level, 0 );

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
317
    if ( (i_flags & DCA_CHANNEL_MASK) != (p_sys->i_flags & DCA_CHANNEL_MASK)
318 319
          && !p_sys->b_dontwarn )
    {
320
        msg_Warn( p_aout,
321
                  "libdca couldn't do the requested downmix 0x%x->0x%x",
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
322 323
                  p_sys->i_flags  & DCA_CHANNEL_MASK,
                  i_flags & DCA_CHANNEL_MASK );
324 325 326 327 328 329

        p_sys->b_dontwarn = 1;
    }

    if( 0)//!p_sys->b_dynrng )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
330
        dca_dynrng( p_sys->p_libdca, NULL, NULL );
331 332
    }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
333
    for ( i = 0; i < dca_blocks_num(p_sys->p_libdca); i++ )
334 335 336
    {
        sample_t * p_samples;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
337
        if( dca_block( p_sys->p_libdca ) )
338
        {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
339
            msg_Warn( p_aout, "dca_block failed for block %d", i );
340 341 342
            break;
        }

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
343
        p_samples = dca_samples( p_sys->p_libdca );
344

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
345
        if ( (p_sys->i_flags & DCA_CHANNEL_MASK) == DCA_MONO
346
              && (p_filter->output.i_physical_channels
347 348 349 350 351 352 353 354 355 356 357 358 359
                   & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) )
        {
            Duplicate( (float *)(p_out_buf->p_buffer + i * i_bytes_per_block),
                       p_samples );
        }
        else if ( p_filter->output.i_original_channels
                    & AOUT_CHAN_REVERSESTEREO )
        {
            Exchange( (float *)(p_out_buf->p_buffer + i * i_bytes_per_block),
                      p_samples );
        }
        else
        {
360
            /* Interleave the *$£%ù samples. */
361
            Interleave( (float *)(p_out_buf->p_buffer + i * i_bytes_per_block),
362
                        p_samples, p_sys->i_nb_channels, p_sys->pi_chan_table);
363 364 365 366 367 368 369 370 371 372
        }
    }

    p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
    p_out_buf->i_nb_bytes = i_bytes_per_block * i;
}

/*****************************************************************************
 * Destroy : deallocate data structures
 *****************************************************************************/
373
static void Destroy( vlc_object_t *p_this )
374
{
375 376
    aout_filter_t *p_filter = (aout_filter_t *)p_this;
    filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
377

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
378
    dca_free( p_sys->p_libdca );
379 380
    free( p_sys );
}
381 382

/*****************************************************************************
383
 * OpenFilter:
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
 *****************************************************************************/
static int OpenFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys;
    int i_ret;

    if( p_filter->fmt_in.i_codec != VLC_FOURCC('d','t','s',' ')  )
    {
        return VLC_EGENERIC;
    }

    p_filter->fmt_out.audio.i_format =
        p_filter->fmt_out.i_codec = VLC_FOURCC('f','l','3','2');

    /* 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 )
    {
        msg_Err( p_filter, "out of memory" );
        return VLC_EGENERIC;
    }

    /* Allocate the memory needed to store the module's structure */
    p_filter->p_sys = p_sys = malloc( sizeof(filter_sys_t) );
    if( p_sys == NULL )
    {
        msg_Err( p_filter, "out of memory" );
        return VLC_EGENERIC;
    }

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

    p_filter->pf_audio_filter = Convert;
419
    p_filter->fmt_out.audio.i_rate = p_filter->fmt_in.audio.i_rate;
420 421 422 423 424 425 426 427 428 429 430 431

    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
432
    dca_free( p_sys->p_libdca );
433 434 435 436 437 438 439 440
    free( p_sys );
}

static block_t *Convert( filter_t *p_filter, block_t *p_block )
{
    aout_filter_t aout_filter;
    aout_buffer_t in_buf, out_buf;
    block_t *p_out;
Gildas Bazin's avatar
*...  
Gildas Bazin committed
441
    int i_out_size;
442

Gildas Bazin's avatar
*...  
Gildas Bazin committed
443 444 445 446 447 448 449
    if( !p_block || !p_block->i_samples )
    {
        if( p_block ) p_block->pf_release( p_block );
        return NULL;
    }

    i_out_size = p_block->i_samples *
450
      p_filter->fmt_out.audio.i_bitspersample *
451
        p_filter->fmt_out.audio.i_channels / 8;
452 453 454 455 456

    p_out = p_filter->pf_audio_buffer_new( p_filter, i_out_size );
    if( !p_out )
    {
        msg_Warn( p_filter, "can't get output buffer" );
Gildas Bazin's avatar
*...  
Gildas Bazin committed
457
        p_block->pf_release( p_block );
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
        return NULL;
    }

    p_out->i_samples = p_block->i_samples;
    p_out->i_dts = p_block->i_dts;
    p_out->i_pts = p_block->i_pts;
    p_out->i_length = p_block->i_length;

    aout_filter.p_sys = (struct aout_filter_sys_t *)p_filter->p_sys;
    aout_filter.input = p_filter->fmt_in.audio;
    aout_filter.input.i_format = p_filter->fmt_in.i_codec;
    aout_filter.output = p_filter->fmt_out.audio;
    aout_filter.output.i_format = p_filter->fmt_out.i_codec;

    in_buf.p_buffer = p_block->p_buffer;
    in_buf.i_nb_bytes = p_block->i_buffer;
    in_buf.i_nb_samples = p_block->i_samples;
    out_buf.p_buffer = p_out->p_buffer;
    out_buf.i_nb_bytes = p_out->i_buffer;
    out_buf.i_nb_samples = p_out->i_samples;

    DoWork( (aout_instance_t *)p_filter, &aout_filter, &in_buf, &out_buf );

481 482 483
    p_out->i_buffer = out_buf.i_nb_bytes;
    p_out->i_samples = out_buf.i_nb_samples;

Gildas Bazin's avatar
*...  
Gildas Bazin committed
484 485
    p_block->pf_release( p_block );

486 487
    return p_out;
}