/*
 * Copyright (c) 2001-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.
 *
 */
/*
 * $Id: mas_squant_device.c,v 1.2 2003/03/17 19:07:33 rocko Exp $
 *
 * Copyright (c) 2000, 2001 by Shiman Associates Inc. and Sun
 * Microsystems, 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.
 *
 * Except as contained in this notice, the names of the authors or
 * copyright holders shall not be used in advertising or otherwise to
 * promote the sale, use or other dealings in this Software without
 * prior written authorization from the authors or copyright holders,
 * as applicable.
 *
 * All trademarks and registered trademarks mentioned herein are the
 * property of their respective owners. No right, title or interest in
 * or to any trademark, service mark, logo or trade name of the
 * authors or copyright holders or their licensors is granted.
 *
 */

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

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h> 
#include "mas/mas_dpi.h"
#include "profile.h"

/* define a right shift (>>) operator that fills up 1's from the left
   if the sign bit is on.  Kernighan & Ritchie say that this may not
   be automatically the case on all machines (though on Linux and
   Solaris it seems to be). I can get rid of the conditional, by
   putting in a multiplication. But could we do without either?
   integer is an unsigned int of any word length (that's why it's a
   macro), shift is the shift amount (i.e. the '2' in 'x>>2'), and ws1
   is the 'intended' word length length minus 1 (i.e., 15 for a 16-bit
   int, or 19 for a 20-bit int stuck in an int32)
*/

#define ARITHMETIC_RIGHTSHIFT(integer, shift, ws1) \
( integer>>ws1 ) ? (integer >> shift) | ( (~0)<<(ws1-shift) ) : \
(integer >> shift)


/************************************************************************
 * squant_state
 *
 * State memory structure for this device instance.
 *
 ************************************************************************/
struct squant_state
{
    int32   squant_sink;
    int32   squant_source;
    int32   reaction;
    uint32  sequence;
    uint32  media_ts;
    int8    source_configured, sink_configured;
    int     inres, outres;
    int     insgnd, outsgnd;
    void    (*requantize)(char*, char**, uint16*);
  
};


/**** local prototypes ****************************************************/
void choose_algorithm(struct squant_state*);



/*************************************************************************
 * ACTIONS
 *************************************************************************/


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

/* device specific actions *********************************************/


/***************************************************************************
 * mas_dev_init_instance - standard device action
 *
 *  predicate: unused
 *
 * Initializes state structure.
 *  
 * returns: error
 *
 ***************************************************************************/
int32 mas_dev_init_instance( int32 device_instance, void* predicate )
{
    struct squant_state*  state;

    /* Allocate state holder and cast it so we can work on it */
    state = masc_rtalloc(sizeof(struct squant_state));
    if ( state == 0 )
	return mas_error(MERR_MEMORY);

    state->source_configured = state->sink_configured = 0;

    state->sequence = 0;
    /* in case you wonder... the other state struct members are
       initialized in mas_dev_configure_port()                  */

    
    masd_set_state(device_instance, state); /* set device state */
    
    masd_get_port_by_name( device_instance, "sink",
			   &state->squant_sink );

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

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

    return 0;
}

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

int32 mas_dev_exit_instance( int32 device_instance, void* predicate )
{
    struct squant_state*  state;
    
    masd_get_state( device_instance, (void**)&state );
    masc_rtfree( state );
    
    return 0;
}


/***************************************************************************
 * mas_dev_show_state - standard debugging action
 *
 *  predicate: unused
 *
 * Sends state information to stdout.
 *  
 * returns: error
 *
 ***************************************************************************/
int32 mas_dev_show_state( int32 device_instance, void* predicate )
{
    struct squant_state* s;
    masd_get_state(device_instance, (void**)&s);

    printf("*-- squant state ---------------------------------------------\n");

    printf(" input resolution:");
    if (s->sink_configured) printf(" %d\n", s->inres);
    else printf(" not yet configured\n");
    
    printf("output resolution:");
    if (s->source_configured) printf(" %d\n", s->outres);
    else printf(" not yet configured\n");
    
    if ( (s->sink_configured)&&(s->source_configured) ) { }
    else printf("Both ports need to be configured for operation.\n");
    
    return 0;
}


/***************************************************************************
 * mas_dev_configure_port
 *
 *  predicate: int32 portnum
 *  
 * returns: 0 on success
 *
 ***************************************************************************/
int32 mas_dev_configure_port( int32 device_instance, void* predicate )
{
    struct squant_state*             state;
    struct mas_data_characteristic* dc;
    int32*                          dataflow_port_dependency;
    int                             sres, sformat; 
    int32                           err;
    char                            string[100];
    
    
    err = masd_get_state(device_instance, (void**)&state);
    err = masd_get_data_characteristic( *(int32*)predicate, &dc );
    if ( err < 0 ) return err;
    
    
    sres = masc_get_index_of_key(dc, "resolution");
    if ( sres < 0 ) return mas_error(MERR_INVALID);
    
    sformat = masc_get_index_of_key(dc, "format");
    if ( sformat < 0 ) return mas_error(MERR_INVALID);
    
    

    /* set in- or output resolution in state */

    if (*(int32*)predicate == state->squant_sink){
        state->inres = atoi(dc->values[sres]);
        if ( !strcmp(dc->values[sformat], "linear") ) {
            state->insgnd = 1;
        }
        else {
            if ( !strcmp(dc->values[sformat], "ulinear") ) {
                state->insgnd = 0;
            }
            else {
                return mas_error(MERR_INVALID);
            }
        }
        state->sink_configured = 1;
        sprintf(string, "squant: configuring sink for %d bit %s",
               state->inres,dc->values[sformat]);
        masc_log_message(0, string);
        
    }
    

    if (*(int32*)predicate == state->squant_source){
        state->outres = atoi(dc->values[sres]);
        if ( !strcmp(dc->values[sformat], "linear") ) {
            state->outsgnd = 1;
        }
        else {
            if ( !strcmp(dc->values[sformat], "ulinear") ) {
                state->outsgnd = 0;
            }
            else {
                return mas_error(MERR_INVALID);
            }
        }
        state->source_configured = 1;
        sprintf(string, "squant: configuring source for %d bit %s",
               state->outres, dc->values[sformat]);
        masc_log_message(0, string);
    }
    
    
    if ( state->source_configured && state->sink_configured )
    {
	/* this call will set the function pointer state->requantize
	   to the correct requantization algorithm
	   (there are 8^2=64: from/to signed/unsigned 8/16/20/24 bits/sample)
	*/
	choose_algorithm( state );
	
	state->media_ts = 0;
	
	dataflow_port_dependency = masc_rtalloc( sizeof (int32) );
	*dataflow_port_dependency = state->squant_sink;
	
	err = masd_reaction_queue_action(state->reaction, device_instance, 
					 "mas_squant_requantize",
					 0, 0, 0, 0, 0,
					 MAS_PRIORITY_DATAFLOW, 1, 1, 
					 dataflow_port_dependency);   
    }
    
    return 0;
}


/***************************************************************************
 * mas_dev_disconnect_port
 *
 *  predicate: int32 portnum
 *
 * returns: error
 *
 ***************************************************************************/
int32 mas_dev_disconnect_port( int32 device_instance, void* predicate )
{
    return 0;
}


/***************************************************************************
 * mas_squant_requantize
 *
 *  predicate: unused
 *
 * 
 ***************************************************************************/
int32 mas_squant_requantize( int32 device_instance, void* predicate )
{
    struct squant_state* state;
    struct mas_data     *data;
    char *out;
    int32               err;
  
    out = 0;   /* to get rid of 'uninitialized' compiler warning */

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

    err = masd_get_data(state->squant_sink, &data);
    if (data->length == 0) return mas_error(MERR_INVALID);
  
    /* data->length will be manipulated in this call!  But we are
     * going to throw the data struct away anyway.
     * If you don't then don't. */
    state->requantize( data->segment, &out, &data->length );

    /* trash old segment */
    masc_rtfree( data->segment );

    /* set segment to quantizer output */
    data->segment          = out;
    data->allocated_length = data->length; /* new segment is exact */

    err = masd_post_data(state->squant_source, data);
    if ( err < 0 ) return err;
  
    return 0;
}



/* Everything from here on is generated by the gen_algs.pl Perl script.
 * Preferably, changes should be made there and the result pasted here.
 * perhaps run like
 * gen_algs.pl |perl -ne 's/ << 0//; print' >somefile
 * to get rid of '<< 0's. Also note the presence of dumb functions
 * like the first one - this device isn't even needed in those
 * cases. But, perhaps someone will use it anyway so I left them in.
 * */

void s8tos8( char *in, char **out, uint16 *len )
{
    int8  *a;
    int8 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int8*)in;
    *out = masc_rtalloc( length );
    b = (int8*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (int8)a[i];
    }
}

void u8tos8( char *in, char **out, uint16 *len )
{
    uint8 *a;
    int8 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint8*)in;
    *out = masc_rtalloc( length );
    b = (int8*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (int8)(a[i]-128);
    }
}

void s8tou8( char *in, char **out, uint16 *len )
{
    int8   *a;
    uint8  *b;
    uint8 c;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int8*)in;
    *out = masc_rtalloc( length );
    b = (uint8*) *out;
    for ( i=0; i<(length); i++ ) {
        c = a[i] + 128;
        b[i] = (uint8)c;
    }
}

void u8tou8( char *in, char **out, uint16 *len )
{
    uint8 *a;
    uint8 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint8*)in;
    *out = masc_rtalloc( length );
    b = (uint8*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (uint8)a[i];
    }
}

void s8tos16( char *in, char **out, uint16 *len )
{
    int8   *a;
    int16  *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int8*)in;
    *out = masc_rtalloc( 2 * length );
    b = (int16*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (int16)a[i] << 8;
    }
    *len = 2 * length;
}

void u8tos16( char *in, char **out, uint16 *len )
{
    uint8 *a;
    int16 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint8*)in;
    *out = masc_rtalloc( 2 * length );
    b = (int16*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (int16)(a[i]-128) << 8;
    }
    *len = 2 * length;
}

void s8tou16( char *in, char **out, uint16 *len )
{
    int8   *a;
    uint16  *b;
    uint8 c;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int8*)in;
    *out = masc_rtalloc( 2 * length );
    b = (uint16*) *out;
    for ( i=0; i<(length); i++ ) {
        c = a[i] + 128;
        b[i] = (uint16)c << 8;
    }
    *len = 2 * length;
}

void u8tou16( char *in, char **out, uint16 *len )
{
    uint8 *a;
    uint16 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint8*)in;
    *out = masc_rtalloc( 2 * length );
    b = (uint16*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (uint16)a[i] << 8;
    }
    *len = 2 * length;
}

void s8tos20( char *in, char **out, uint16 *len )
{
    int8   *a;
    int32  *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int8*)in;
    *out = masc_rtalloc( 4 * length );
    b = (int32*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (int32)a[i] << 12;
    }
    *len = 4 * length;
}

void u8tos20( char *in, char **out, uint16 *len )
{
    uint8 *a;
    int32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint8*)in;
    *out = masc_rtalloc( 4 * length );
    b = (int32*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (int32)(a[i]-128) << 12;
    }
    *len = 4 * length;
}

void s8tou20( char *in, char **out, uint16 *len )
{
    int8   *a;
    uint32  *b;
    uint8 c;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int8*)in;
    *out = masc_rtalloc( 4 * length );
    b = (uint32*) *out;
    for ( i=0; i<(length); i++ ) {
        c = a[i] + 128;
        b[i] = (uint32)c << 12;
    }
    *len = 4 * length;
}

void u8tou20( char *in, char **out, uint16 *len )
{
    uint8 *a;
    uint32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint8*)in;
    *out = masc_rtalloc( 4 * length );
    b = (uint32*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (uint32)a[i] << 12;
    }
    *len = 4 * length;
}

void s8tos24( char *in, char **out, uint16 *len )
{
    int8   *a;
    int32  *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int8*)in;
    *out = masc_rtalloc( 4 * length );
    b = (int32*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (int32)a[i] << 16;
    }
    *len = 4 * length;
}

void u8tos24( char *in, char **out, uint16 *len )
{
    uint8 *a;
    int32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint8*)in;
    *out = masc_rtalloc( 4 * length );
    b = (int32*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (int32)(a[i]-128) << 16;
    }
    *len = 4 * length;
}

void s8tou24( char *in, char **out, uint16 *len )
{
    int8   *a;
    uint32  *b;
    uint8 c;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int8*)in;
    *out = masc_rtalloc( 4 * length );
    b = (uint32*) *out;
    for ( i=0; i<(length); i++ ) {
        c = a[i] + 128;
        b[i] = (uint32)c << 16;
    }
    *len = 4 * length;
}

void u8tou24( char *in, char **out, uint16 *len )
{
    uint8 *a;
    uint32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint8*)in;
    *out = masc_rtalloc( 4 * length );
    b = (uint32*) *out;
    for ( i=0; i<(length); i++ ) {
        b[i] = (uint32)a[i] << 16;
    }
    *len = 4 * length;
}

void s16tos8( char *in, char **out, uint16 *len )
{
    int16 *a;
    int8 *b;
    uint16 i;
    int16 tmp;
    uint16 utmp;
    uint16 length;
    
    length = *len;
    a = (int16*)in;
    *out = masc_rtalloc( length / 2 );
    b = (int8*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        tmp = a[i] + (1<<8)*(0.5-(double)rand()/(double)RAND_MAX);        
        utmp = tmp;
        tmp = ARITHMETIC_RIGHTSHIFT(utmp, 8, 15);
        b[i] = tmp;
    }
    *len = length / 2;
}

void s16tou8( char *in, char **out, uint16 *len )
{
    int16 *a;
    uint8 *b;
    uint16 i;
    int16 tmp;
    uint16 utmp;
    uint16 length;

    length = *len;
    a = (int16*)in;
    *out = masc_rtalloc( length / 2 );
    b = (uint8*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        tmp = a[i] + (1<<8)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp + 32768;
        utmp = utmp >> 8;
        b[i] = utmp;
    }
    *len = length / 2;
}

void u16tos8( char *in, char **out, uint16 *len )
{
    uint16 *a;
    int8 *b;
    uint16 i;
    uint16 utmp;
    uint16 length;

    length = *len;
    a = (uint16*)in;
    *out = masc_rtalloc( length / 2 );
    b = (int8*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        utmp = a[i] + (1<<8)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = ARITHMETIC_RIGHTSHIFT(utmp, 8, 15);
        b[i] = utmp - 128;
    }
    *len = length / 2;
}

void u16tou8( char *in, char **out, uint16 *len )
{
    uint16 *a;
    uint8 *b;
    uint16 i;
    uint16 utmp;
    uint16 length;

    length = *len;
    a = (uint16*)in;
    *out = masc_rtalloc( length / 2 );
    b = (uint8*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        utmp = a[i] + (1<<8)*(0.5-(double)rand()/(double)RAND_MAX);
        b[i] = utmp >> 8;
    }
    *len = length / 2;
}

void s16tos16( char *in, char **out, uint16 *len )
{
    int16   *a;
    int16  *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int16*)in;
    *out = masc_rtalloc( length );
    b = (int16*) *out;
    for ( i=0; i<(length/2); i++ ) {
        b[i] = (int16)a[i];
    }
}

void u16tos16( char *in, char **out, uint16 *len )
{
    uint16 *a;
    int16 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint16*)in;
    *out = masc_rtalloc( length );
    b = (int16*) *out;
    for ( i=0; i<(length/2); i++ ) {
        b[i] = (int16)(a[i]-32768);
    }
}

void s16tou16( char *in, char **out, uint16 *len )
{
    int16   *a;
    uint16  *b;
    uint16 c;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int16*)in;
    *out = masc_rtalloc( length );
    b = (uint16*) *out;
    for ( i=0; i<(length/2); i++ ) {
        c = a[i] + 32768;
        b[i] = (uint16)c;
    }
}

void u16tou16( char *in, char **out, uint16 *len )
{
    uint16 *a;
    uint16 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint16*)in;
    *out = masc_rtalloc( length );
    b = (uint16*) *out;
    for ( i=0; i<(length/2); i++ ) {
        b[i] = (uint16)a[i];
    }
}

void s16tos20( char *in, char **out, uint16 *len )
{
    int16   *a;
    int32  *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int16*)in;
    *out = masc_rtalloc( 2 * length );
    b = (int32*) *out;
    for ( i=0; i<(length/2); i++ ) {
        b[i] = (int32)a[i] << 4;
    }
    *len = 2 * length;
}

void u16tos20( char *in, char **out, uint16 *len )
{
    uint16 *a;
    int32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint16*)in;
    *out = masc_rtalloc( 2 * length );
    b = (int32*) *out;
    for ( i=0; i<(length/2); i++ ) {
        b[i] = (int32)(a[i]-32768) << 4;
    }
    *len = 2 * length;
}

void s16tou20( char *in, char **out, uint16 *len )
{
    int16   *a;
    uint32  *b;
    uint16 c;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int16*)in;
    *out = masc_rtalloc( 2 * length );
    b = (uint32*) *out;
    for ( i=0; i<(length/2); i++ ) {
        c = a[i] + 32768;
        b[i] = (uint32)c << 4;
    }
    *len = 2 * length;
}

void u16tou20( char *in, char **out, uint16 *len )
{
    uint16 *a;
    uint32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint16*)in;
    *out = masc_rtalloc( 2 * length );
    b = (uint32*) *out;
    for ( i=0; i<(length/2); i++ ) {
        b[i] = (uint32)a[i] << 4;
    }
    *len = 2 * length;
}

void s16tos24( char *in, char **out, uint16 *len )
{
    int16   *a;
    int32  *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int16*)in;
    *out = masc_rtalloc( 2 * length );
    b = (int32*) *out;
    for ( i=0; i<(length/2); i++ ) {
        b[i] = (int32)a[i] << 8;
    }
    *len = 2 * length;
}

void u16tos24( char *in, char **out, uint16 *len )
{
    uint16 *a;
    int32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint16*)in;
    *out = masc_rtalloc( 2 * length );
    b = (int32*) *out;
    for ( i=0; i<(length/2); i++ ) {
        b[i] = (int32)(a[i]-32768) << 8;
    }
    *len = 2 * length;
}

void s16tou24( char *in, char **out, uint16 *len )
{
    int16   *a;
    uint32  *b;
    uint16 c;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int16*)in;
    *out = masc_rtalloc( 2 * length );
    b = (uint32*) *out;
    for ( i=0; i<(length/2); i++ ) {
        c = a[i] + 32768;
        b[i] = (uint32)c << 8;
    }
    *len = 2 * length;
}

void u16tou24( char *in, char **out, uint16 *len )
{
    uint16 *a;
    uint32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint16*)in;
    *out = masc_rtalloc( 2 * length );
    b = (uint32*) *out;
    for ( i=0; i<(length/2); i++ ) {
        b[i] = (uint32)a[i] << 8;
    }
    *len = 2 * length;
}

void s20tos8( char *in, char **out, uint16 *len )
{
    int32 *a;
    int8 *b;
    uint16 i;
    int32 tmp;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length / 4 );
    b = (int8*) *out;
    for ( i=0; i<(length / 4); i++ ) {
        tmp = a[i] + (1<<12)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp;
        tmp = ARITHMETIC_RIGHTSHIFT(utmp, 12, 19);
        b[i] = tmp;
    }
    *len = length / 4;
}

void s20tou8( char *in, char **out, uint16 *len )
{
    int32 *a;
    uint8 *b;
    uint16 i;
    int32 tmp;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length / 4 );
    b = (uint8*) *out;
    for ( i=0; i<(length / 4); i++ ) {
        tmp = a[i] + (1<<12)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp + 524288;
        utmp = utmp >> 12;
        b[i] = utmp;
    }
    *len = length / 4;
}

void u20tos8( char *in, char **out, uint16 *len )
{
    uint32 *a;
    int8 *b;
    uint16 i;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length / 4 );
    b = (int8*) *out;
    for ( i=0; i<(length / 4); i++ ) {
        utmp = a[i] + (1<<12)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = ARITHMETIC_RIGHTSHIFT(utmp, 12, 19);
        b[i] = utmp - 128;
    }
    *len = length / 4;
}

void u20tou8( char *in, char **out, uint16 *len )
{
    uint32 *a;
    uint8 *b;
    uint16 i;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length / 4 );
    b = (uint8*) *out;
    for ( i=0; i<(length / 4); i++ ) {
        utmp = a[i] + (1<<12)*(0.5-(double)rand()/(double)RAND_MAX);
        b[i] = utmp >> 12;
    }
    *len = length / 4;
}

void s20tos16( char *in, char **out, uint16 *len )
{
    int32 *a;
    int16 *b;
    uint16 i;
    int32 tmp;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length / 2 );
    b = (int16*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        tmp = a[i] + (1<<4)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp;
        tmp = ARITHMETIC_RIGHTSHIFT(utmp, 4, 19);
        b[i] = tmp;
    }
    *len = length / 2;
}

void s20tou16( char *in, char **out, uint16 *len )
{
    int32 *a;
    uint16 *b;
    uint16 i;
    int32 tmp;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length / 2 );
    b = (uint16*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        tmp = a[i] + (1<<4)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp + 524288;
        utmp = utmp >> 4;
        b[i] = utmp;
    }
    *len = length / 2;
}

void u20tos16( char *in, char **out, uint16 *len )
{
    uint32 *a;
    int16 *b;
    uint16 i;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length / 2 );
    b = (int16*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        utmp = a[i] + (1<<4)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = ARITHMETIC_RIGHTSHIFT(utmp, 4, 19);
        b[i] = utmp - 32768;
    }
    *len = length / 2;
}

void u20tou16( char *in, char **out, uint16 *len )
{
    uint32 *a;
    uint16 *b;
    uint16 i;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length / 2 );
    b = (uint16*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        utmp = a[i] + (1<<4)*(0.5-(double)rand()/(double)RAND_MAX);
        b[i] = utmp >> 4;
    }
    *len = length / 2;
}

void s20tos20( char *in, char **out, uint16 *len )
{
    int32   *a;
    int32  *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length );
    b = (int32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        b[i] = (int32)a[i];
    }
}

void u20tos20( char *in, char **out, uint16 *len )
{
    uint32 *a;
    int32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length );
    b = (int32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        b[i] = (int32)(a[i]-524288);
    }
}

void s20tou20( char *in, char **out, uint16 *len )
{
    int32   *a;
    uint32  *b;
    uint32 c;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length );
    b = (uint32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        c = a[i] + 524288;
        b[i] = (uint32)c;
    }
}

void u20tou20( char *in, char **out, uint16 *len )
{
    uint32 *a;
    uint32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length );
    b = (uint32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        b[i] = (uint32)a[i];
    }
}

void s20tos24( char *in, char **out, uint16 *len )
{
    int32   *a;
    int32  *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length );
    b = (int32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        b[i] = (int32)a[i] << 4;
    }
}

void u20tos24( char *in, char **out, uint16 *len )
{
    uint32 *a;
    int32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length );
    b = (int32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        b[i] = (int32)(a[i]-524288) << 4;
    }
}

void s20tou24( char *in, char **out, uint16 *len )
{
    int32   *a;
    uint32  *b;
    uint32 c;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length );
    b = (uint32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        c = a[i] + 524288;
        b[i] = (uint32)c << 4;
    }
}

void u20tou24( char *in, char **out, uint16 *len )
{
    uint32 *a;
    uint32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length );
    b = (uint32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        b[i] = (uint32)a[i] << 4;
    }
}

void s24tos8( char *in, char **out, uint16 *len )
{
    int32 *a;
    int8 *b;
    uint16 i;
    int32 tmp;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length / 4 );
    b = (int8*) *out;
    for ( i=0; i<(length / 4); i++ ) {
        tmp = a[i] + (1<<16)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp;
        tmp = ARITHMETIC_RIGHTSHIFT(utmp, 16, 23);
        b[i] = tmp;
    }
    *len = length / 4;
}

void s24tou8( char *in, char **out, uint16 *len )
{
    int32 *a;
    uint8 *b;
    uint16 i;
    int32 tmp;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length / 4 );
    b = (uint8*) *out;
    for ( i=0; i<(length / 4); i++ ) {
        tmp = a[i] + (1<<16)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp + 8388608;
        utmp = utmp >> 16;
        b[i] = utmp;
    }
    *len = length / 4;
}

void u24tos8( char *in, char **out, uint16 *len )
{
    uint32 *a;
    int8 *b;
    uint16 i;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length / 4 );
    b = (int8*) *out;
    for ( i=0; i<(length / 4); i++ ) {
        utmp = a[i] + (1<<16)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = ARITHMETIC_RIGHTSHIFT(utmp, 16, 23);
        b[i] = utmp - 128;
    }
    *len = length / 4;
}

void u24tou8( char *in, char **out, uint16 *len )
{
    uint32 *a;
    uint8 *b;
    uint16 i;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length / 4 );
    b = (uint8*) *out;
    for ( i=0; i<(length / 4); i++ ) {
        utmp = a[i] + (1<<16)*(0.5-(double)rand()/(double)RAND_MAX);
        b[i] = utmp >> 16;
    }
    *len = length / 4;
}

void s24tos16( char *in, char **out, uint16 *len )
{
    int32 *a;
    int16 *b;
    uint16 i;
    int32 tmp;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length / 2 );
    b = (int16*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        tmp = a[i] + (1<<8)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp;
        tmp = ARITHMETIC_RIGHTSHIFT(utmp, 8, 23);
        b[i] = tmp;
    }
    *len = length / 2;
}

void s24tou16( char *in, char **out, uint16 *len )
{
    int32 *a;
    uint16 *b;
    uint16 i;
    int32 tmp;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length / 2 );
    b = (uint16*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        tmp = a[i] + (1<<8)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp + 8388608;
        utmp = utmp >> 8;
        b[i] = utmp;
    }
    *len = length / 2;
}

void u24tos16( char *in, char **out, uint16 *len )
{
    uint32 *a;
    int16 *b;
    uint16 i;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length / 2 );
    b = (int16*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        utmp = a[i] + (1<<8)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = ARITHMETIC_RIGHTSHIFT(utmp, 8, 23);
        b[i] = utmp - 32768;
    }
    *len = length / 2;
}

void u24tou16( char *in, char **out, uint16 *len )
{
    uint32 *a;
    uint16 *b;
    uint16 i;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length / 2 );
    b = (uint16*) *out;
    for ( i=0; i<(length / 2); i++ ) {
        utmp = a[i] + (1<<8)*(0.5-(double)rand()/(double)RAND_MAX);
        b[i] = utmp >> 8;
    }
    *len = length / 2;
}

void s24tos20( char *in, char **out, uint16 *len )
{
    int32 *a;
    int32 *b;
    uint16 i;
    int32 tmp;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length );
    b = (int32*) *out;
    for ( i=0; i<(length); i++ ) {
        tmp = a[i] + (1<<4)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp;
        tmp = ARITHMETIC_RIGHTSHIFT(utmp, 4, 23);
        b[i] = tmp;
    }
}

void s24tou20( char *in, char **out, uint16 *len )
{
    int32 *a;
    uint32 *b;
    uint16 i;
    int32 tmp;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length );
    b = (uint32*) *out;
    for ( i=0; i<(length); i++ ) {
        tmp = a[i] + (1<<4)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = tmp + 8388608;
        utmp = utmp >> 4;
        b[i] = utmp;
    }
}

void u24tos20( char *in, char **out, uint16 *len )
{
    uint32 *a;
    int32 *b;
    uint16 i;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length );
    b = (int32*) *out;
    for ( i=0; i<(length); i++ ) {
        utmp = a[i] + (1<<4)*(0.5-(double)rand()/(double)RAND_MAX);
        utmp = ARITHMETIC_RIGHTSHIFT(utmp, 4, 23);
        b[i] = utmp - 524288;
    }
}

void u24tou20( char *in, char **out, uint16 *len )
{
    uint32 *a;
    uint32 *b;
    uint16 i;
    uint32 utmp;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length );
    b = (uint32*) *out;
    for ( i=0; i<(length); i++ ) {
        utmp = a[i] + (1<<4)*(0.5-(double)rand()/(double)RAND_MAX);
        b[i] = utmp >> 4;
    }
}

void s24tos24( char *in, char **out, uint16 *len )
{
    int32   *a;
    int32  *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length );
    b = (int32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        b[i] = (int32)a[i];
    }
}

void u24tos24( char *in, char **out, uint16 *len )
{
    uint32 *a;
    int32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length );
    b = (int32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        b[i] = (int32)(a[i]-8388608);
    }
}

void s24tou24( char *in, char **out, uint16 *len )
{
    int32   *a;
    uint32  *b;
    uint32 c;
    uint16 i;
    uint16 length;

    length = *len;
    a = (int32*)in;
    *out = masc_rtalloc( length );
    b = (uint32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        c = a[i] + 8388608;
        b[i] = (uint32)c;
    }
}

void u24tou24( char *in, char **out, uint16 *len )
{
    uint32 *a;
    uint32 *b;
    uint16 i;
    uint16 length;

    length = *len;
    a = (uint32*)in;
    *out = masc_rtalloc( length );
    b = (uint32*) *out;
    for ( i=0; i<(length/4); i++ ) {
        b[i] = (uint32)a[i];
    }
}



/* Here comes the if statement for choosing the right function pointer:
 */
void choose_algorithm( struct squant_state *state )
{
    if (state->insgnd == 1)
    {
        if (state->outsgnd == 1)
        {
            if (state->inres == 8)
            {
                if (state->outres == 8)
                {
                    state->requantize = &s8tos8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &s8tos16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &s8tos20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &s8tos24;
                    return;
                }
            }
            if (state->inres == 16)
            {
                if (state->outres == 8)
                {
                    state->requantize = &s16tos8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &s16tos16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &s16tos20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &s16tos24;
                    return;
                }
            }
            if (state->inres == 20)
            {
                if (state->outres == 8)
                {
                    state->requantize = &s20tos8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &s20tos16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &s20tos20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &s20tos24;
                    return;
                }
            }
            if (state->inres == 24)
            {
                if (state->outres == 8)
                {
                    state->requantize = &s24tos8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &s24tos16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &s24tos20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &s24tos24;
                    return;
                }
            }
        }
        if (state->outsgnd == 0)
        {
            if (state->inres == 8)
            {
                if (state->outres == 8)
                {
                    state->requantize = &s8tou8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &s8tou16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &s8tou20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &s8tou24;
                    return;
                }
            }
            if (state->inres == 16)
            {
                if (state->outres == 8)
                {
                    state->requantize = &s16tou8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &s16tou16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &s16tou20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &s16tou24;
                    return;
                }
            }
            if (state->inres == 20)
            {
                if (state->outres == 8)
                {
                    state->requantize = &s20tou8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &s20tou16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &s20tou20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &s20tou24;
                    return;
                }
            }
            if (state->inres == 24)
            {
                if (state->outres == 8)
                {
                    state->requantize = &s24tou8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &s24tou16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &s24tou20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &s24tou24;
                    return;
                }
            }
        }
    }
    if (state->insgnd == 0)
    {
        if (state->outsgnd == 1)
        {
            if (state->inres == 8)
            {
                if (state->outres == 8)
                {
                    state->requantize = &u8tos8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &u8tos16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &u8tos20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &u8tos24;
                    return;
                }
            }
            if (state->inres == 16)
            {
                if (state->outres == 8)
                {
                    state->requantize = &u16tos8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &u16tos16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &u16tos20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &u16tos24;
                    return;
                }
            }
            if (state->inres == 20)
            {
                if (state->outres == 8)
                {
                    state->requantize = &u20tos8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &u20tos16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &u20tos20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &u20tos24;
                    return;
                }
            }
            if (state->inres == 24)
            {
                if (state->outres == 8)
                {
                    state->requantize = &u24tos8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &u24tos16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &u24tos20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &u24tos24;
                    return;
                }
            }
        }
        if (state->outsgnd == 0)
        {
            if (state->inres == 8)
            {
                if (state->outres == 8)
                {
                    state->requantize = &u8tou8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &u8tou16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &u8tou20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &u8tou24;
                    return;
                }
            }
            if (state->inres == 16)
            {
                if (state->outres == 8)
                {
                    state->requantize = &u16tou8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &u16tou16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &u16tou20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &u16tou24;
                    return;
                }
            }
            if (state->inres == 20)
            {
                if (state->outres == 8)
                {
                    state->requantize = &u20tou8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &u20tou16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &u20tou20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &u20tou24;
                    return;
                }
            }
            if (state->inres == 24)
            {
                if (state->outres == 8)
                {
                    state->requantize = &u24tou8;
                    return;
                }
                if (state->outres == 16)
                {
                    state->requantize = &u24tou16;
                    return;
                }
                if (state->outres == 20)
                {
                    state->requantize = &u24tou20;
                    return;
                }
                if (state->outres == 24)
                {
                    state->requantize = &u24tou24;
                    return;
                }
            }
        }
    }
    return;
}


/*   triangular noise: */
/*  if (rand()>r2){ */
/* 	    noise =  (1.0-sqrt((double)rand()/RAND_MAX)) * (double)(1<<bitdiff); */
/* 	  } */
/* 	  else { */
/* 	    noise =  (sqrt((double)rand()/RAND_MAX)-1.0) * (double)(1<<bitdiff); */
/* 	  } */
	 
   
  
