Logo Search packages:      
Sourcecode: mas version File versions

mas_codec_mp1a_mad_device.c

/*
 * mad - MPEG audio decoder
 * Copyright (C) 2000-2001 Robert Leslie
 *
 * 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.
 *
 * 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
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id$
 */

/*
 * Copyright (c) 2002-2003 Shiman Associates Inc. All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * This file contains source code that has been released under the GNU
 * General Public License (GPL).  While some portions of the file may
 * be considered MIT-license clean, any portion derived from the
 * original MAD code and any portion which invokes the MAD library
 * interface must be considered to be licensed under the terms of the
 * GPL.
 */

/* 2 OCT 2002 - rocko - verified reentrant
 * 2 OCT 2002 - rocko - NOT timestamp clean
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#ifndef WIN32
# include <sys/mman.h>
#endif /* WIN32 */
#include "mas/mas_dpi.h"
#include "mad.h"
#include "profile.h"

#define BUFFER_SIZE     32768
#define BUFFER_FILLTO   MAD_BUFFER_MDLEN

/************************************************************************
 * codec_mp1a_state
 *
 * State memory structure for this device instance.
 *
 ************************************************************************/
struct codec_mp1a_state
{
    int32 reaction;
    int32 sink;
    int32 source;
    int   source_configured;
    int   sink_configured;
    int   outres;

    struct mas_data* dq; /* queue of data structs from input - use
                          * these for output */
    
    struct mad_decoder decoder;
    struct mad_stream *stream;
    struct mad_frame *frame;
    struct mad_synth *synth;

    unsigned char buffer[BUFFER_SIZE];
    int length;
    int first_frame;
    uint32 sequence;
};

static enum mad_flow input(void *data, struct mad_stream *stream);
static signed int scale(mad_fixed_t sample, int resolution);
static enum mad_flow output(void *data, struct mad_header const *header, struct mad_pcm *pcm);
static enum mad_flow error(void *data, struct mad_stream *stream, struct mad_frame *frame);

/* standard actions ****************************************************/
int32 mas_dev_init_instance( int32 , void* );
int32 mas_dev_show_state( int32 device_instance, void* predicate );

/* device specific actions *********************************************/
int32 mas_codec_mp1a_convert( int32 , void* );

int32
mas_dev_init_instance( int32 device_instance, void* predicate )
{
    struct codec_mp1a_state*  state;
    
    /* Allocate state holder and cast it so we can work on it */
    state       = MAS_NEW(state);
    if ( state == 0 )
      return mas_error(MERR_MEMORY);

    masd_set_state(device_instance, state); /* set device state */

    masd_get_port_by_name( device_instance, "sink", &state->sink );
    masd_get_port_by_name( device_instance, "source", &state->source );
    masd_get_port_by_name( device_instance, "reaction", &state->reaction );

    state->first_frame = TRUE; /* MAD needs a one frame buffer */
    
    mad_decoder_init(&state->decoder, state, input, 0 /* header */, 0 /* filter */, output, error, 0 /* message */);

    state->decoder.mode = MAD_DECODER_MODE_SYNC;
    state->decoder.sync = masc_rtalloc(sizeof(*(state->decoder.sync)));
    if (state->decoder.sync == 0)
        return mas_error(MERR_MEMORY);

    state->stream = &state->decoder.sync->stream;
    state->frame  = &state->decoder.sync->frame;
    state->synth  = &state->decoder.sync->synth;
    
    state->stream->buffer = masc_rtalloc( BUFFER_SIZE );

    state->dq = MAS_NEW( state->dq );

    mad_stream_init(state->stream);
    mad_frame_init(state->frame);
    mad_synth_init(state->synth);

    mad_stream_options(state->stream, state->decoder.options);
    
    state->sequence = 0;
    
    return 0;
}

int32
mas_dev_exit_instance( int32 device_instance, void* predicate )
{
    struct codec_mp1a_state*  state;

    MASD_GET_STATE(device_instance, state);

    mad_synth_finish(state->synth);
    mad_frame_finish(state->frame);
    mad_stream_finish(state->stream);
    
    masc_rtfree(state->decoder.sync);
    state->decoder.sync = 0;

    /* release the decoder */
    mad_decoder_finish(&state->decoder);

    masc_rtfree( state );
    
    return 0;
}

int32
mas_dev_configure_port( int32 device_instance, void* predicate )
{
    struct codec_mp1a_state*  state;
    int32  portnum = *(int32*)predicate;
    int32* dataflow_port_dependency;
    struct mas_data_characteristic* dc;
    uint8  format, resolution, channels, endian;
    uint32 srate;
    int32  err;

    MASD_GET_STATE(device_instance, state);

    if ( portnum == state->sink )
    {
        state->sink_configured = TRUE;
    }
    else if ( portnum  == state->source )
    {
        state->source_configured = TRUE;

        /* retrieve the output sample resolution */
        err = masd_get_data_characteristic( portnum, &dc );
        if ( err < 0 )
            return mas_error(MERR_INVALID);
        err = masc_scan_audio_basic_dc( dc, &format, &srate, &resolution, &channels, &endian );
        masc_entering_log_level("codec_mp1a_mad: mas_dev_configure_port");
        masc_log_message( MAS_VERBLVL_DEBUG, "codec_mp1a_mad: generating %d-bit samples", resolution );
        masc_exiting_log_level();
        
        if ( err < 0 )
            return mas_error(MERR_INVALID);
        state->outres = resolution;
    }
    else
    {
        return mas_error( MERR_NOTDEF );
    }

    /* set up the dataflow dependency if both ports are configured */
    if ( state->source_configured && state->sink_configured )
    {
        /* schedule our dataflow dependency on data_sink */
        dataflow_port_dependency = masc_rtalloc( sizeof (int32) );
        *dataflow_port_dependency = state->sink;
        err = masd_reaction_queue_action(state->reaction, device_instance, 
                                         "mas_codec_mp1a_convert", 0, 0, 0, 0, 0,
                                         MAS_PRIORITY_DATAFLOW, 1, 1, 
                                         dataflow_port_dependency);
    }
    
    return 0;
}

int32
mas_dev_disconnect_port( int32 device_instance, void* predicate )
{
    struct codec_mp1a_state*  state;
    int32  portnum = *(int32*)predicate;

    MASD_GET_STATE(device_instance, state);

    if ( portnum == state->sink )
    {
        state->sink_configured = FALSE;
    }
    else if ( portnum  == state->source )
    {
        state->source_configured = FALSE;
        state->outres = 0;
    }
    else
    {
        return mas_error( MERR_NOTDEF );
    }
    
    return 0;
}

int32
mas_dev_terminate( int32 device_instance, void* predicate )
{
    /* no op */
    return 0;
}

int32
mas_dev_show_state( int32 device_instance, void* predicate )
{
    return mas_error(MERR_NOTIMP);
}


/*
 * This is the function called by main() above to perform all the
 * decoding. It instantiates a decoder object and configures it with the
 * input, output, and error callback functions above. A single call to
 * mad_decoder_run() continues until a callback function returns
 * MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
 * signal an error).
 */

int32
mas_codec_mp1a_convert( int32 device_instance, void* predicate )
{
    struct codec_mp1a_state* state;
    enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *);
    void *error_data;
    int bad_last_frame = 0;

    masd_get_state(device_instance, (void**)&state);

    error_func = state->decoder.error_func;
    error_data = state->decoder.cb_data;

    state->decoder.input_func(state->decoder.cb_data, state->stream);

    if (mad_frame_decode(state->frame, state->stream) == -1)
    {
        if ( state->first_frame )
        {
            return 0;
        }
        else
        {
            error_func(error_data, state->stream, state->frame);
            if (!MAD_RECOVERABLE(state->stream->error))
                return mas_error(MERR_IO);
        }
    }
    else
        bad_last_frame = 0;

    state->first_frame = FALSE;
    
    mad_synth_frame(state->synth, state->frame);
  
    state->decoder.output_func(state->decoder.cb_data, &state->frame->header, &state->synth->pcm);
    
    return 0;
}

int32
mas_source_flush( int32 device_instance, void* predicate )
{
    struct codec_mp1a_state* state;
    enum mad_flow (*error_func)(void *, struct mad_stream *, struct mad_frame *);
    void *error_data;
    int bad_last_frame = 0;
    struct mad_stream* stream;
    
    masd_get_state(device_instance, (void**)&state);

    stream = state->stream;
    error_func = state->decoder.error_func;
    error_data = state->decoder.cb_data;

    if (stream->next_frame)
    {
        if ( state->length * 2 > BUFFER_SIZE )
            return mas_error(MERR_MEMORY);
    
        memmove(state->buffer, stream->next_frame, state->length = &(state->buffer[state->length]) - stream->next_frame);
        memcpy(state->buffer + state->length, state->buffer, state->length);
    }
    else
    {
        return 0;
    }
        
    memset( state->buffer + state->length + 4, 0, state->length - 4 );
    state->length *= 2;
    stream->error = 0;
    
    mad_stream_buffer(stream, state->buffer, state->length);
    
    if (mad_frame_decode(state->frame, state->stream) == -1)
    {
        if ( state->first_frame )
        {
            return 0;
        }
        else
        {
            error_func(error_data, state->stream, state->frame);
            if (!MAD_RECOVERABLE(state->stream->error))
                return mas_error(MERR_IO);
        }
    }
    else
        bad_last_frame = 0;

    mad_synth_frame(state->synth, state->frame);
  
    return 0;
}

/*** LOCAL FUNCTIONS ************************************************/

void
append_data( struct mas_data* head, struct mas_data* data )
{
    struct mas_data* n;

    if ( head == 0 ) return;

    n = head;
    while( n->next ) n = n->next;
    n->next = data;
}

static enum mad_flow
input(void *stuff, struct mad_stream *stream)
{
  struct codec_mp1a_state* state = stuff;
  struct mas_data* data;
  int32 err;
  
  err = masd_get_data( state->sink, &data );
  if ( err < 0 || data->segment == NULL )
      return -1;

  if (stream->next_frame)
  {
      memmove(state->buffer, stream->next_frame, state->length = &(state->buffer[state->length]) - stream->next_frame);
  }

  if ( state->length + data->length > BUFFER_SIZE )
      return mas_error(MERR_MEMORY);

  memcpy( state->buffer + state->length, data->segment, data->length );
  state->length += data->length;

  append_data( state->dq, data );
  masc_rtfree( data->segment );
  data->segment = 0;
  
  stream->error = 0;
  mad_stream_buffer(stream, state->buffer, state->length);

  return MAD_FLOW_CONTINUE;
}

/*
 * The following utility routine performs simple rounding, clipping,
 * and scaling of MAD's high-resolution samples down to "resolution"
 * bits. It does not perform any dithering or noise shaping, which
 * would be recommended to obtain any exceptional audio quality. It is
 * therefore not recommended to use this routine if high-quality
 * output is desired.
 */

static signed int
scale(mad_fixed_t sample, int resolution)
{
  /* round */
  sample += (1L << (MAD_F_FRACBITS - resolution));

  /* clip */
  if (sample >= MAD_F_ONE)
    sample = MAD_F_ONE - 1;
  else if (sample < -MAD_F_ONE)
    sample = -MAD_F_ONE;

  /* quantize */
  return sample >> (MAD_F_FRACBITS + 1 - resolution);
}

/*
 * This is the output callback function. It is called after each frame of
 * MPEG audio data has been completely decoded. The purpose of this callback
 * is to output (or play) the decoded PCM audio.
 */

static enum mad_flow
output(void *data, struct mad_header const *header, struct mad_pcm *pcm)
{
    struct codec_mp1a_state* state = data;
    unsigned int nchannels, nstc;
    mad_fixed_t const *left_ch, *right_ch;
    struct mas_data* d;
    int i;
    int16* ss;
    int32* sl;
  
    /* pcm->samplerate contains the sampling frequency */

    nchannels = pcm->channels;
    nstc = pcm->length * pcm->channels; /* number of samples times
                                         * channels */
    left_ch   = pcm->samples[0];
    right_ch  = pcm->samples[1];

    d = state->dq->next; /* preserve header from prior data */
    state->dq->next = state->dq->next->next; /* peel off data struct */
    d->next = 0;

    /* reconstruct sample clock */

    /* ok there was a bug here; sequence number is uint16 in jrtp
       wrapper; therefore, when data goes through net, we'll wrap at
       65536. */
       /*     d->header.media_timestamp = (MAD_NSBSAMPLES(header) << 5) * d->header.sequence; */
    /* now I'm assuning the packets are in sequence; let's see how this works */
    d->header.media_timestamp = (MAD_NSBSAMPLES(header) << 5) * (state->sequence++);
    
    if ( state->outres == 16 )
    {
        d->allocated_length = nchannels * pcm->length * 2;
        d->length = d->allocated_length;
        d->segment = masc_rtalloc( d->allocated_length );
        ss = (int16*) d->segment;
        
        for ( i=0; i < nstc; )
        {
            ss[i++] = scale(*left_ch++, state->outres);
            if ( nchannels == 2 )
            {
                ss[i++] = scale(*right_ch++, state->outres);
            }
        }
    }
    else /* 20 or 24 bit */
    {
        /* pack into 32-bit ints */
        d->allocated_length = nchannels * pcm->length * 4;
        d->length = d->allocated_length;
        d->segment = masc_rtalloc( d->allocated_length );
        sl = (int32*) d->segment;
        
        for ( i=0; i < nstc; )
        {
            sl[i++] = scale(*left_ch++, state->outres);
            if ( nchannels == 2 )
            {
                sl[i++] = scale(*right_ch++, state->outres);
            }
            
        }
    }
    
    masd_post_data( state->source, d );

    return MAD_FLOW_CONTINUE;
}

/*
 * This is the error callback function. It is called whenever a decoding
 * error occurs. The error is indicated by stream->error; the list of
 * possible MAD_ERROR_* errors can be found in the mad.h (or
 * libmad/stream.h) header file.
 */

static enum mad_flow
error(void *data, struct mad_stream *stream, struct mad_frame *frame)
{
    masc_log_message( MAS_VERBLVL_ERROR, "codec_mp1a_mad: decoding error 0x%04x (%s) at byte offset %u\n", stream->error, mad_stream_errorstr(stream), stream->this_frame );
    
    return MAD_FLOW_BREAK;
}


Generated by  Doxygen 1.6.0   Back to index