dtstofloat32.c 15.9 KB
Newer Older
gbazin's avatar
 
gbazin 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).
gbazin's avatar
 
gbazin committed
5
 *****************************************************************************
6
 * Copyright (C) 2001, 2002libdca the VideoLAN team
7
 * $Id$
gbazin's avatar
 
gbazin committed
8
 *
9
 * Author: Gildas Bazin <gbazin@videolan.org>
10
 *
gbazin's avatar
 
gbazin 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
 *
gbazin's avatar
 
gbazin 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
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
gbazin's avatar
 
gbazin committed
24 25 26 27 28 29 30 31 32
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <vlc/vlc.h>

#include <string.h>                                              /* strdup() */

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

zorglub's avatar
zorglub committed
35 36
#include <vlc_aout.h>
#include <vlc_block.h>
37
#include "vlc_filter.h"
gbazin's avatar
 
gbazin committed
38 39 40 41 42 43

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  Create    ( vlc_object_t * );
static void Destroy   ( vlc_object_t * );
44
static void DoWork    ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
gbazin's avatar
 
gbazin committed
45 46
                        aout_buffer_t * );

47 48 49 50 51 52 53
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 * );

54
/* libdca channel order */
55 56 57 58 59 60 61 62
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 };

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

    int pi_chan_table[AOUT_CHAN_MAX]; /* channel reordering */
gbazin's avatar
 
gbazin committed
75 76 77 78 79 80 81 82 83 84 85 86 87 88
};

/*****************************************************************************
 * 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();
zorglub's avatar
zorglub committed
89 90
    set_category( CAT_INPUT );
    set_subcategory( SUBCAT_INPUT_ACODEC );
zorglub's avatar
zorglub committed
91
    set_shortname( "DCA" );
gbazin's avatar
 
gbazin committed
92 93 94 95
    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 );
96 97 98 99 100

    add_submodule();
    set_description( _("DTS Coherent Acoustics audio decoder") );
    set_capability( "audio filter2", 100 );
    set_callbacks( OpenFilter, CloseFilter );
gbazin's avatar
 
gbazin committed
101 102 103
vlc_module_end();

/*****************************************************************************
zorglub's avatar
zorglub committed
104
 * Create:
gbazin's avatar
 
gbazin committed
105
 *****************************************************************************/
106
static int Create( vlc_object_t *p_this )
gbazin's avatar
 
gbazin committed
107
{
108 109 110
    aout_filter_t *p_filter = (aout_filter_t *)p_this;
    filter_sys_t *p_sys;
    int i_ret;
gbazin's avatar
 
gbazin committed
111 112 113 114 115 116 117 118 119 120 121 122 123

    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 */
124 125
    p_sys = malloc( sizeof(filter_sys_t) );
    p_filter->p_sys = (struct aout_filter_sys_t *)p_sys;
gbazin's avatar
 
gbazin committed
126 127 128 129 130 131
    if( p_sys == NULL )
    {
        msg_Err( p_filter, "out of memory" );
        return -1;
    }

132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
    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;
}

/*****************************************************************************
 * Open: 
 *****************************************************************************/
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" );
gbazin's avatar
 
gbazin committed
148 149 150
    p_sys->b_dontwarn = 0;

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

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

    case AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
184
        p_sys->i_flags = DCA_3F;
gbazin's avatar
 
gbazin committed
185 186 187
        break;

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

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

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

225 226 227 228 229
    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 );

230
    return VLC_SUCCESS;
gbazin's avatar
 
gbazin committed
231 232 233 234 235
}

/*****************************************************************************
 * Interleave: helper function to interleave channels
 *****************************************************************************/
236 237
static void Interleave( float * p_out, const float * p_in, int i_nb_channels,
                        int *pi_chan_table )
gbazin's avatar
 
gbazin committed
238
{
239
    /* We do not only have to interleave, but also reorder the channels. */
gbazin's avatar
 
gbazin committed
240 241 242 243 244 245

    int i, j;
    for ( j = 0; j < i_nb_channels; j++ )
    {
        for ( i = 0; i < 256; i++ )
        {
246
            p_out[i * i_nb_channels + pi_chan_table[j]] = p_in[j * 256 + i];
gbazin's avatar
 
gbazin committed
247 248 249 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
        }
    }
}

/*****************************************************************************
 * 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 )
{
288
    filter_sys_t    *p_sys = (filter_sys_t *)p_filter->p_sys;
gbazin's avatar
 
gbazin committed
289 290 291 292 293 294 295 296 297 298 299 300 301
    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
302
    if( !dca_syncinfo( p_sys->p_libdca, p_in_buf->p_buffer, &i_flags,
gbazin's avatar
 
gbazin committed
303 304
                       &i_sample_rate, &i_bit_rate, &i_frame_length ) )
    {
305
        msg_Warn( p_aout, "libdca couldn't sync on frame" );
gbazin's avatar
 
gbazin committed
306 307 308 309 310
        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
311
    dca_frame( p_sys->p_libdca, p_in_buf->p_buffer,
gbazin's avatar
 
gbazin committed
312 313
               &i_flags, &i_sample_level, 0 );

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

        p_sys->b_dontwarn = 1;
    }

    if( 0)//!p_sys->b_dynrng )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
327
        dca_dynrng( p_sys->p_libdca, NULL, NULL );
gbazin's avatar
 
gbazin committed
328 329
    }

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

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

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
340
        p_samples = dca_samples( p_sys->p_libdca );
gbazin's avatar
 
gbazin committed
341

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
342
        if ( (p_sys->i_flags & DCA_CHANNEL_MASK) == DCA_MONO
gbazin's avatar
 
gbazin committed
343 344 345 346 347 348 349 350 351 352 353 354 355 356
              && (p_filter->output.i_physical_channels 
                   & (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
        {
357
            /* Interleave the *$£%ù samples. */
gbazin's avatar
 
gbazin committed
358
            Interleave( (float *)(p_out_buf->p_buffer + i * i_bytes_per_block),
359
                        p_samples, p_sys->i_nb_channels, p_sys->pi_chan_table);
gbazin's avatar
 
gbazin committed
360 361 362 363 364 365 366 367 368 369
        }
    }

    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
 *****************************************************************************/
370
static void Destroy( vlc_object_t *p_this )
gbazin's avatar
 
gbazin committed
371
{
372 373
    aout_filter_t *p_filter = (aout_filter_t *)p_this;
    filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
gbazin's avatar
 
gbazin committed
374

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
375
    dca_free( p_sys->p_libdca );
gbazin's avatar
 
gbazin committed
376 377
    free( p_sys );
}
378 379 380 381 382 383 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

/*****************************************************************************
 * OpenFilter: 
 *****************************************************************************/
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;
416
    p_filter->fmt_out.audio.i_rate = p_filter->fmt_in.audio.i_rate;
417 418 419 420 421 422 423 424 425 426 427 428

    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
429
    dca_free( p_sys->p_libdca );
430 431 432 433 434 435 436 437
    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;
gbazin's avatar
*...  
gbazin committed
438
    int i_out_size;
439

gbazin's avatar
*...  
gbazin committed
440 441 442 443 444 445 446
    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 *
447
      p_filter->fmt_out.audio.i_bitspersample *
448
        p_filter->fmt_out.audio.i_channels / 8;
449 450 451 452 453

    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" );
gbazin's avatar
*...  
gbazin committed
454
        p_block->pf_release( p_block );
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
        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 );

478 479 480
    p_out->i_buffer = out_buf.i_nb_bytes;
    p_out->i_samples = out_buf.i_nb_samples;

gbazin's avatar
*...  
gbazin committed
481 482
    p_block->pf_release( p_block );

483 484
    return p_out;
}