linear.c 12.3 KB
Newer Older
1
/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
2
 * linear.c : linear interpolation resampler
3
 *****************************************************************************
4
 * Copyright (C) 2002, 2006 the VideoLAN team
5
 * $Id$
6
 *
Gildas Bazin's avatar
 
Gildas Bazin committed
7
 * Authors: Gildas Bazin <gbazin@netcourrier.com>
8
 *          Sigmund Augdal Helberg <dnumgis@videolan.org>
9 10 11 12 13
 *
 * 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.
14
 *
15 16 17 18 19 20 21
 * 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
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27 28 29
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/

#include <vlc/vlc.h>
Clément Stenac's avatar
Clément Stenac committed
30 31 32
#include <vlc_aout.h>
#include <vlc_filter.h>
#include <vlc_block.h>
33 34 35 36 37

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  Create    ( vlc_object_t * );
Gildas Bazin's avatar
 
Gildas Bazin committed
38
static void Close     ( vlc_object_t * );
39 40 41
static void DoWork    ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
                        aout_buffer_t * );

42 43 44 45
static int  OpenFilter ( vlc_object_t * );
static void CloseFilter( vlc_object_t * );
static block_t *Resample( filter_t *, block_t * );

Gildas Bazin's avatar
 
Gildas Bazin committed
46 47 48
/*****************************************************************************
 * Local structures
 *****************************************************************************/
49
struct filter_sys_t
Gildas Bazin's avatar
 
Gildas Bazin committed
50 51 52
{
    int32_t *p_prev_sample;       /* this filter introduces a 1 sample delay */

Gildas Bazin's avatar
 
Gildas Bazin committed
53
    unsigned int i_remainder;                /* remainder of previous sample */
Gildas Bazin's avatar
 
Gildas Bazin committed
54 55 56 57

    audio_date_t end_date;
};

58 59 60 61
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
vlc_module_begin();
62
    set_description( _("Audio filter for linear interpolation resampling") );
Clément Stenac's avatar
Clément Stenac committed
63 64
    set_category( CAT_AUDIO );
    set_subcategory( SUBCAT_AUDIO_MISC );
65
    set_capability( "audio filter", 5 );
Gildas Bazin's avatar
 
Gildas Bazin committed
66
    set_callbacks( Create, Close );
67 68

    add_submodule();
69
    set_description( _("Audio filter for linear interpolation resampling") );
70 71
    set_capability( "audio filter2", 5 );
    set_callbacks( OpenFilter, CloseFilter );
72 73 74
vlc_module_end();

/*****************************************************************************
Gildas Bazin's avatar
 
Gildas Bazin committed
75
 * Create: allocate linear resampler
76 77 78 79
 *****************************************************************************/
static int Create( vlc_object_t *p_this )
{
    aout_filter_t * p_filter = (aout_filter_t *)p_this;
80
    struct filter_sys_t * p_sys;
Benjamin Pracht's avatar
Benjamin Pracht committed
81
    
82 83
    if ( p_filter->input.i_rate == p_filter->output.i_rate
          || p_filter->input.i_format != p_filter->output.i_format
Gildas Bazin's avatar
 
Gildas Bazin committed
84 85 86 87
          || p_filter->input.i_physical_channels
              != p_filter->output.i_physical_channels
          || p_filter->input.i_original_channels
              != p_filter->output.i_original_channels
88 89 90 91 92
          || p_filter->input.i_format != VLC_FOURCC('f','l','3','2') )
    {
        return VLC_EGENERIC;
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
93
    /* Allocate the memory needed to store the module's structure */
94 95 96
    p_sys = malloc( sizeof(filter_sys_t) );
    p_filter->p_sys = (struct aout_filter_sys_t *)p_sys;
    if( p_sys == NULL )
Gildas Bazin's avatar
 
Gildas Bazin committed
97 98 99 100
    {
        msg_Err( p_filter, "out of memory" );
        return VLC_ENOMEM;
    }
101
    p_sys->p_prev_sample = malloc(
Benjamin Pracht's avatar
 
Benjamin Pracht committed
102
        p_filter->input.i_channels * sizeof(int32_t) );
103
    if( p_sys->p_prev_sample == NULL )
Gildas Bazin's avatar
 
Gildas Bazin committed
104 105 106 107 108
    {
        msg_Err( p_filter, "out of memory" );
        return VLC_ENOMEM;
    }

109
    p_filter->pf_do_work = DoWork;
Gildas Bazin's avatar
 
Gildas Bazin committed
110 111 112 113

    /* We don't want a new buffer to be created because we're not sure we'll
     * actually need to resample anything. */
    p_filter->b_in_place = VLC_TRUE;
114 115 116 117

    return VLC_SUCCESS;
}

Gildas Bazin's avatar
 
Gildas Bazin committed
118 119 120 121 122 123
/*****************************************************************************
 * Close: free our resources
 *****************************************************************************/
static void Close( vlc_object_t * p_this )
{
    aout_filter_t * p_filter = (aout_filter_t *)p_this;
124
    filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
Benjamin Pracht's avatar
Benjamin Pracht committed
125
    
126 127
    free( p_sys->p_prev_sample );
    free( p_sys );
Gildas Bazin's avatar
 
Gildas Bazin committed
128 129
}

130 131 132 133 134 135
/*****************************************************************************
 * DoWork: convert a buffer
 *****************************************************************************/
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 )
{
136
    filter_sys_t *p_sys = (filter_sys_t *)p_filter->p_sys;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
137 138 139 140
#ifndef HAVE_ALLOCA
    float *p_in_orig;
#endif
    float *p_in, *p_out = (float *)p_out_buf->p_buffer;
141
    float *p_prev_sample = (float *)p_sys->p_prev_sample;
142

Benjamin Pracht's avatar
 
Benjamin Pracht committed
143
    int i_nb_channels = p_filter->input.i_channels;
144
    int i_in_nb = p_in_buf->i_nb_samples;
Gildas Bazin's avatar
 
Gildas Bazin committed
145 146
    int i_chan, i_in, i_out = 0;

Gildas Bazin's avatar
 
Gildas Bazin committed
147
    /* Check if we really need to run the resampler */
Benjamin Pracht's avatar
Benjamin Pracht committed
148
    if( p_aout->mixer.mixer.i_rate == p_filter->input.i_rate )
Gildas Bazin's avatar
 
Gildas Bazin committed
149 150
    {
        if( p_filter->b_continuity &&
151 152 153 154 155 156 157 158 159
            p_in_buf->i_size >=
              p_in_buf->i_nb_bytes + sizeof(float) * i_nb_channels )
        {
            /* output the whole thing with the last sample from last time */
            memmove( ((float *)(p_in_buf->p_buffer)) + i_nb_channels,
                     p_in_buf->p_buffer, p_in_buf->i_nb_bytes );
            memcpy( p_in_buf->p_buffer, p_prev_sample,
                    i_nb_channels * sizeof(float) );
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
160 161 162 163 164 165 166
        p_filter->b_continuity = VLC_FALSE;
        return;
    }

#ifdef HAVE_ALLOCA
    p_in = (float *)alloca( p_in_buf->i_nb_bytes );
#else
167
    p_in_orig = p_in = (float *)malloc( p_in_buf->i_nb_bytes );
Gildas Bazin's avatar
 
Gildas Bazin committed
168 169 170 171 172 173
#endif
    if( p_in == NULL )
    {
        return;
    }

174
    p_aout->p_libvlc->pf_memcpy( p_in, p_in_buf->p_buffer, p_in_buf->i_nb_bytes );
Gildas Bazin's avatar
 
Gildas Bazin committed
175

Gildas Bazin's avatar
 
Gildas Bazin committed
176
    /* Take care of the previous input sample (if any) */
Gildas Bazin's avatar
 
Gildas Bazin committed
177
    if( !p_filter->b_continuity )
178
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
179
        p_filter->b_continuity = VLC_TRUE;
180 181
        p_sys->i_remainder = 0;
        aout_DateInit( &p_sys->end_date, p_filter->output.i_rate );
Gildas Bazin's avatar
 
Gildas Bazin committed
182 183 184
    }
    else
    {
185
        while( p_sys->i_remainder < p_filter->output.i_rate )
186 187 188 189
        {
            for( i_chan = i_nb_channels ; i_chan ; )
            {
                i_chan--;
Gildas Bazin's avatar
 
Gildas Bazin committed
190
                p_out[i_chan] = p_prev_sample[i_chan];
191
                p_out[i_chan] += ( ( p_in[i_chan] - p_prev_sample[i_chan] )
192
                                   * p_sys->i_remainder
193
                                   / p_filter->output.i_rate );
194 195
            }
            p_out += i_nb_channels;
196
              i_out++;
Gildas Bazin's avatar
 
Gildas Bazin committed
197

198
            p_sys->i_remainder += p_filter->input.i_rate;
199
        }
200
        p_sys->i_remainder -= p_filter->output.i_rate;
Gildas Bazin's avatar
 
Gildas Bazin committed
201 202 203 204 205
    }

    /* Take care of the current input samples (minus last one) */
    for( i_in = 0; i_in < i_in_nb - 1; i_in++ )
    {
206
        while( p_sys->i_remainder < p_filter->output.i_rate )
Gildas Bazin's avatar
 
Gildas Bazin committed
207 208 209 210 211
        {
            for( i_chan = i_nb_channels ; i_chan ; )
            {
                i_chan--;
                p_out[i_chan] = p_in[i_chan];
212 213
                p_out[i_chan] += ( ( p_in[i_chan + i_nb_channels]
                    - p_in[i_chan] )
214
                    * p_sys->i_remainder / p_filter->output.i_rate );
Gildas Bazin's avatar
 
Gildas Bazin committed
215 216
            }
            p_out += i_nb_channels;
217
              i_out++;
Gildas Bazin's avatar
 
Gildas Bazin committed
218

219
            p_sys->i_remainder += p_filter->input.i_rate;
Gildas Bazin's avatar
 
Gildas Bazin committed
220 221
        }

222
        p_in += i_nb_channels;
223
        p_sys->i_remainder -= p_filter->output.i_rate;
224
    }
Gildas Bazin's avatar
 
Gildas Bazin committed
225 226 227

    /* Backup the last input sample for next time */
    for( i_chan = i_nb_channels ; i_chan ; )
228
    {
Gildas Bazin's avatar
 
Gildas Bazin committed
229 230
        i_chan--;
        p_prev_sample[i_chan] = p_in[i_chan];
231 232
    }

Gildas Bazin's avatar
 
Gildas Bazin committed
233 234 235 236
    p_out_buf->i_nb_samples = i_out;
    p_out_buf->start_date = p_in_buf->start_date;

    if( p_in_buf->start_date !=
237
        aout_DateGet( &p_sys->end_date ) )
Gildas Bazin's avatar
 
Gildas Bazin committed
238
    {
239
        aout_DateSet( &p_sys->end_date, p_in_buf->start_date );
Gildas Bazin's avatar
 
Gildas Bazin committed
240 241
    }

242
    p_out_buf->end_date = aout_DateIncrement( &p_sys->end_date,
Gildas Bazin's avatar
 
Gildas Bazin committed
243 244 245 246 247
                                              p_out_buf->i_nb_samples );

    p_out_buf->i_nb_bytes = p_out_buf->i_nb_samples *
        i_nb_channels * sizeof(int32_t);

248 249 250 251
#ifndef HAVE_ALLOCA
    free( p_in_orig );
#endif

Gildas Bazin's avatar
 
Gildas Bazin committed
252
}
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267

/*****************************************************************************
 * OpenFilter:
 *****************************************************************************/
static int OpenFilter( vlc_object_t *p_this )
{
    filter_t *p_filter = (filter_t *)p_this;
    filter_sys_t *p_sys;
    int i_out_rate  = p_filter->fmt_out.audio.i_rate;

    if( p_filter->fmt_in.audio.i_rate == p_filter->fmt_out.audio.i_rate ||
        p_filter->fmt_in.i_codec != VLC_FOURCC('f','l','3','2') )
    {
        return VLC_EGENERIC;
    }
Benjamin Pracht's avatar
Benjamin Pracht committed
268
    
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
    /* Allocate the memory needed to store the module's structure */
    p_filter->p_sys = p_sys = malloc( sizeof(struct filter_sys_t) );
    if( p_sys == NULL )
    {
        msg_Err( p_filter, "out of memory" );
        return VLC_ENOMEM;
    }

    p_sys->p_prev_sample = malloc(
        p_filter->fmt_in.audio.i_channels * sizeof(int32_t) );
    if( p_sys->p_prev_sample == NULL )
    {
        msg_Err( p_filter, "out of memory" );
        free( p_sys );
        return VLC_ENOMEM;
    }

    p_filter->pf_audio_filter = Resample;

    msg_Dbg( p_this, "%4.4s/%iKHz/%i->%4.4s/%iKHz/%i",
             (char *)&p_filter->fmt_in.i_codec,
             p_filter->fmt_in.audio.i_rate,
             p_filter->fmt_in.audio.i_channels,
             (char *)&p_filter->fmt_out.i_codec,
             p_filter->fmt_out.audio.i_rate,
             p_filter->fmt_out.audio.i_channels);

    p_filter->fmt_out = p_filter->fmt_in;
    p_filter->fmt_out.audio.i_rate = i_out_rate;

    return 0;
}

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

/*****************************************************************************
 * Resample
 *****************************************************************************/
static block_t *Resample( filter_t *p_filter, block_t *p_block )
{
    aout_filter_t aout_filter;
    aout_buffer_t in_buf, out_buf;
    block_t *p_out;
    int i_out_size;
    int i_bytes_per_frame;

    if( !p_block || !p_block->i_samples )
    {
        if( p_block ) p_block->pf_release( p_block );
        return NULL;
    }
Benjamin Pracht's avatar
Benjamin Pracht committed
328
    
329 330
    i_bytes_per_frame = p_filter->fmt_out.audio.i_channels *
                  p_filter->fmt_out.audio.i_bitspersample / 8;
Benjamin Pracht's avatar
Benjamin Pracht committed
331
    
332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
    i_out_size = i_bytes_per_frame * ( 1 + (p_block->i_samples * 
        p_filter->fmt_out.audio.i_rate / p_filter->fmt_in.audio.i_rate));

    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" );
        p_block->pf_release( p_block );
        return NULL;
    }

    p_out->i_samples = i_out_size / i_bytes_per_frame;
    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.output = p_filter->fmt_out.audio;
    aout_filter.b_continuity = VLC_FALSE;

    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;

Benjamin Pracht's avatar
Benjamin Pracht committed
360
    DoWork( (aout_instance_t *)p_filter, &aout_filter, &in_buf, &out_buf );
361 362

    p_block->pf_release( p_block );
Benjamin Pracht's avatar
Benjamin Pracht committed
363
    
364 365 366 367 368
    p_out->i_buffer = out_buf.i_nb_bytes;
    p_out->i_samples = out_buf.i_nb_samples;

    return p_out;
}