/*------------------------------------------------------------------------------

    File    :	mpega_decode.c

    Author  :	Stéphane TAVENARD

    $VER:   mpega_decode.c  1.0  (23/06/1995)

    (C) Copyright 1995-1995 Stéphane TAVENARD
	All Rights Reserved

    #Rev|   Date   |			  Comment
    ----|----------|--------------------------------------------------------
    0	|27/05/1995| Initial revision					  ST
    1	|23/06/1995| First release (aminet)                               ST

    ------------------------------------------------------------------------

    MPEG Audio decode functions

------------------------------------------------------------------------------*/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include "AudioPort.h"
#include "AIFF.h"
#include "mpega_decode_asm.h"
#include "mpega_decode.h"

frame_params *MPEGA_close( frame_params *fr_ps )
{
   if( !fr_ps ) return NULL;
   if( fr_ps->audio_port ) {
      AU_close( fr_ps->audio_port );
      fr_ps->audio_port = NULL;
   }
   if( fr_ps->bs ) BSTR_close( fr_ps->bs );
   fr_ps->bs = NULL;
   if( fr_ps->header ) free( fr_ps->header );
   if( fr_ps->bit_alloc ) free( fr_ps->bit_alloc );
   if( fr_ps->scfsi ) free( fr_ps->scfsi );
   if( fr_ps->scale_index ) free( fr_ps->scale_index );
   if( fr_ps->sample ) free( fr_ps->sample );
   if( fr_ps->fraction ) free( fr_ps->fraction );
   free( fr_ps );
   return NULL;
}

frame_params *MPEGA_open( void )
{
   frame_params *fr_ps;

   fr_ps = (frame_params *)malloc( sizeof( frame_params ) );
   if( !fr_ps ) return NULL;
   memset( fr_ps, 0, sizeof( frame_params ) );
   fr_ps->header = (layer *)malloc( sizeof( layer ) );
   if( !fr_ps->header ) return MPEGA_close( fr_ps );
   memset( fr_ps->header, 0, sizeof( layer ) );
   fr_ps->bit_alloc = (int *)malloc( sizeof( int ) * 2 * SBLIMIT );
   if( !fr_ps->bit_alloc ) return MPEGA_close( fr_ps );
   fr_ps->scfsi = (int *)malloc( sizeof( int ) * 2 * SBLIMIT );
   if( !fr_ps->scfsi ) return MPEGA_close( fr_ps );
   fr_ps->scale_index = (int *)malloc( sizeof( int ) * 2 * 3 * SBLIMIT );
   if( !fr_ps->scale_index ) return MPEGA_close( fr_ps );
   fr_ps->sample = (int *)malloc( sizeof( int ) * 2 * 3 * SBLIMIT );
   if( !fr_ps->sample ) return MPEGA_close( fr_ps );
   fr_ps->fraction = (INT *)malloc( sizeof( INT ) * 2 * 3 * SBLIMIT );
   if( !fr_ps->fraction ) return MPEGA_close( fr_ps );

   /* default values */
   fr_ps->freq_div = 1;
   fr_ps->quality = 2;
   fr_ps->bitstream_buffer_size = BSTR_BUFFER_SIZE;

   return fr_ps;
}

#define OUT_BUFFER_SIZE  (13824*4)

static BOOL stereo_file = FALSE;
static BYTE *l_file_buffer = NULL;
static BYTE *r_file_buffer = NULL;
static BYTE *stereo_file_buffer = NULL;

static int init_file( frame_params *fr_ps )
{
   long buffer_byte_size;

   if( !fr_ps->out_file_name ) return 1;

   if( !fr_ps->out_file ) {
      if( strcmp( fr_ps->out_file_name, "stdout" ) == 0 ) {
	 fr_ps->out_file = stdout;
      }
      else {
	 fr_ps->out_file = fopen( fr_ps->out_file_name, "w+b" );
	 if( !fr_ps->out_file ) {
	    fprintf( stderr, "Can't create file '%s'\n", fr_ps->out_file_name );
	    return 0;
	 }
      }
   }
   if( fr_ps->out_file_type == MPEGA_FILETYPE_AIFF ) {
      if( AIFF_seek_to_sound_data( fr_ps->out_file ) == -1 ) {
	 fclose( fr_ps->out_file );
	 fr_ps->out_file = NULL;
	 return 0;
      }
   }

   fr_ps->out_buffer_size = OUT_BUFFER_SIZE;

   if( (fr_ps->stereo > 1) && !(fr_ps->mono_forced) ) stereo_file = TRUE;
   else stereo_file = FALSE;
   if( fr_ps->output_8bits ) buffer_byte_size = fr_ps->out_buffer_size;
   else buffer_byte_size = (fr_ps->out_buffer_size)*2;
   fr_ps->out_buffer[ 0 ] = fr_ps->out_buffer[ 1 ] = NULL;
   fr_ps->out_buffer[ 0 ] = (BYTE *)malloc( buffer_byte_size );
   if( !fr_ps->out_buffer[ 0 ] ) exit( 0 );
   if( stereo_file ) {
      fr_ps->out_buffer[ 1 ] = (BYTE *)malloc( buffer_byte_size );
      if( !fr_ps->out_buffer[ 1 ] ) exit( 0 );
      stereo_file_buffer = (BYTE *)malloc( buffer_byte_size * 2 );
      if( !stereo_file_buffer ) exit( 0 );
   }
   l_file_buffer = fr_ps->out_buffer[ 0 ];
   r_file_buffer = fr_ps->out_buffer[ 1 ];
   return 1;
}

static int write_file( frame_params *fr_ps )
/*
   WARNING length is not byte length, it's sample length (8 or 16 bits)
*/
{
   long buffer_byte_size;
   int write_length;
   register int i;

   if( fr_ps->out_write_length > 0 ) {
      if( fr_ps->output_8bits ) buffer_byte_size = fr_ps->out_write_length;
      else buffer_byte_size = fr_ps->out_write_length * 2;
      if( stereo_file ) {
	 if( fr_ps->output_8bits ) {
	    BYTE *l_sample, *r_sample, *buffer;

	    buffer = stereo_file_buffer;
	    l_sample = l_file_buffer;
	    r_sample = r_file_buffer;
	    for( i=0; i<fr_ps->out_write_length; i++ ) {
	       *buffer++ = *l_sample++;
	       *buffer++ = *r_sample++;
	    }
	 }
	 else {
	    WORD *l_sample, *r_sample, *buffer;

	    buffer = (WORD *)stereo_file_buffer;
	    l_sample = (WORD *)l_file_buffer;
	    r_sample = (WORD *)r_file_buffer;
	    for( i=0; i<fr_ps->out_write_length; i++ ) {
	       *buffer++ = *l_sample++;
	       *buffer++ = *r_sample++;
	    }
	 }
	 write_length = fwrite( stereo_file_buffer, buffer_byte_size*2, 1, fr_ps->out_file );
      }
      else {
	 write_length = fwrite( l_file_buffer, buffer_byte_size, 1, fr_ps->out_file );
      }
      if( !fr_ps->output_8bits ) write_length <<= 1;
   }
   else {
      write_length = 0;
      if ( fr_ps->out_file_type == MPEGA_FILETYPE_AIFF ) {
	 IFF_AIFF pcm_aiff_data;

	 pcm_aiff_data.numChannels	 = fr_ps->stereo;
	 pcm_aiff_data.numSampleFrames	 = fr_ps->out_sample_length;
	 if( fr_ps->output_8bits ) pcm_aiff_data.sampleSize = 8;
	 else pcm_aiff_data.sampleSize	 = 16;
	 pcm_aiff_data.sampleRate	 = fr_ps->out_sample_freq;
	 pcm_aiff_data.sampleType	 = IFF_ID_SSND;
	 pcm_aiff_data.blkAlgn.offset	 = 0;
	 pcm_aiff_data.blkAlgn.blockSize = 0;

	 if( AIFF_write_headers( fr_ps->out_file, &pcm_aiff_data ) == -1 ) {
	    printf( "Could not write AIFF headers to \"%s\"\n", fr_ps->out_file_name );
	 }
      }
      fclose( fr_ps->out_file );
      fr_ps->out_file = NULL;
   }
   fr_ps->out_buffer[ 0 ] = l_file_buffer;
   fr_ps->out_buffer[ 1 ] = r_file_buffer;
   fr_ps->out_write_length = 0;

   return write_length;
}

static int init_audio( frame_params *fr_ps )
{
   BYTE flags = 0;

   if( fr_ps->audio_port ) return;

   fr_ps->out_buffer_size = fr_ps->out_sample_freq * 2;    /* 2 secs buffer */
   if( fr_ps->out_buffer_size >= 0x20000L ) {
      fr_ps->out_buffer_size = 0x1FFFEL;
   }
   if( !fr_ps->output_8bits ) {
      flags |= AUF_16BITS;
   }
   if( (fr_ps->stereo > 1) && !(fr_ps->mono_forced) ) flags |= AUF_STEREO;

   fr_ps->audio_port = AU_open( flags, fr_ps->out_buffer_size );

   if( !fr_ps->audio_port ) {
      fprintf( stderr, "Can't open audio port !\n" );
      exit( 0 );
   }
   fr_ps->audio_port->frequency = fr_ps->out_sample_freq;
   fprintf( stderr, "Audio output " );
   if( flags & AUF_16BITS ) fprintf( stderr, "16-bit " );
   else fprintf( stderr, "8-bit " );
   if( flags & AUF_STEREO ) fprintf( stderr, "stereo " );
   else fprintf( stderr, "mono " );
   fprintf( stderr, "%ldHz ", fr_ps->audio_port->frequency );

   fr_ps->audio_port->l_vol = fr_ps->audio_port->r_vol = 64;
   fr_ps->audio_port->filter_on = fr_ps->audio_filter;
   fr_ps->audio_port->flags = AUF_FILTER | AUF_FREQ | AUF_VOL;
   fr_ps->audio_port->mixing_frequency = fr_ps->mixing_frequency;
   if( fr_ps->mixing_frequency > 0 ) {
      fr_ps->audio_port->flags |= AUF_MIXING;
      fprintf( stderr, "mixing at %ldHz\n", fr_ps->mixing_frequency );
   }
   else fprintf( stderr, "\n" );
   fr_ps->audio_port->command = AUC_CONTROL;
   AU_write( fr_ps->audio_port );
   fr_ps->out_buffer[ 0 ] = (BYTE *)fr_ps->audio_port->l_wave;
   fr_ps->out_buffer[ 1 ] = (BYTE *)fr_ps->audio_port->r_wave;
   return 1;
}

static int write_audio( frame_params *fr_ps )
{
   int write_length = 0;

   if( !fr_ps->audio_port ) return 0;
   if( fr_ps->out_write_length >= 0 ) {
      fr_ps->audio_port->flags &= ~(AUF_FILTER|AUF_FREQ|AUF_VOL);
      if( fr_ps->no_audio_wait ) fr_ps->audio_port->flags |= AUF_NOWAIT;
      fr_ps->audio_port->command = AUC_WRITE;
      fr_ps->audio_port->wave_length = fr_ps->out_write_length;
      AU_write( fr_ps->audio_port );
      fr_ps->out_buffer[ 0 ] = (BYTE *)fr_ps->audio_port->l_wave;
      fr_ps->out_buffer[ 1 ] = (BYTE *)fr_ps->audio_port->r_wave;
      write_length = fr_ps->out_write_length;
   }

   fr_ps->out_write_length = 0;
   return write_length;
}

static int MPEGA_init_out( frame_params *fr_ps )
{
   if( fr_ps->play ) return init_audio( fr_ps );
   else 	     return init_file( fr_ps );
}

static void MPEGA_read_CRC( frame_params *fr_ps )
{
   int old_crc;

   old_crc = BSTR_read_bits( fr_ps->bs, 16);
}

int MPEGA_seek_header( frame_params *fr_ps )
/*------------------------------------------
   Seek, read and decode MPEG audio header
*/
{
   if( BSTR_end( fr_ps->bs ) ) return 0;
   if( !BSTR_seek_sync( fr_ps->bs, MPEGA_SYNC_WORD, MPEGA_SYNC_WORD_LNGTH ) ) return 0;
   if( !ASM_read_header( fr_ps ) ) return 0;
   ASM_decode_header( fr_ps );
   if( fr_ps->header->error_protection ) MPEGA_read_CRC( fr_ps );
   return 1;
}

int MPEGA_init_decode( frame_params *fr_ps )
/*------------------------------------------
   Initialize MPEG Audio decoding (analys of first frame to setup buffers)
*/
{
   int scale_size;

   if( fr_ps->freq_div <= 0 ) fr_ps->freq_div = 1;
   fr_ps->sub_band_size = SBLIMIT / fr_ps->freq_div;
   ASM_init_decode( fr_ps->sub_band_size, fr_ps->output_8bits, fr_ps->quality );

   if( !fr_ps->bs ) {
      fr_ps->bs = BSTR_open( fr_ps->bitstream_name,
			     fr_ps->bitstream_buffer_size, BSTR_MODE_READ );
      if( !fr_ps->bs ) {
	 fprintf( stderr, "Can't open file '%s'\n", fr_ps->bitstream_name );
	 return 0;
      }
   }
   if( !MPEGA_seek_header( fr_ps ) ) return 0;
   /* Seek to start of bitstream */
   if( !BSTR_rewind( fr_ps->bs ) ) return 0;

   fr_ps->out_sample_freq = fr_ps->bitstream_freq / fr_ps->freq_div;
   if( !MPEGA_init_out( fr_ps ) ) return 0;      /* Initialize on first frame */
   if( fr_ps->header->lay == 1 ) scale_size = SCALE_BLOCK;
   else if ( fr_ps->header->lay == 2 ) scale_size = 3 * SCALE_BLOCK;
   else return 0;

   fr_ps->out_write_level = fr_ps->out_buffer_size - scale_size * fr_ps->sub_band_size;
   fr_ps->got_bits = 0;
   fr_ps->frame_count = 0;
   fr_ps->out_write_length = 0;
   fr_ps->out_sample_length = 0;

   return 1;
}



int MPEGA_write_out( frame_params *fr_ps )
/*----------------------------------------
   Write out_buffer to output media
   -> return written sample count
*/
{
   if( fr_ps->play ) return write_audio( fr_ps );
   else 	     return write_file( fr_ps );
}

int MPEGA_decode_frame( frame_params *fr_ps )
/*-------------------------------------------
   Decode one MPEG Audio frame
   -> return 0 if frame can't be decoded or bitstream empty
   -> return out sample length decoded otherwise
     !!! length is not in byte, but in sample count (8 or 16 bits wide)
*/
{
   int sample_frame_length = 0;

   if( !MPEGA_seek_header( fr_ps ) ) return 0;

   switch( fr_ps->header->lay ) {
      case 1: {
	 ASM_I_decode_bitalloc( fr_ps );
	 ASM_I_decode_scale( fr_ps );
	 if( fr_ps->mono_forced ) fr_ps->stereo = 1;
	 ASM_I_decode_frame( fr_ps );
	 sample_frame_length = fr_ps->sub_band_size * SCALE_BLOCK;
	 break;
      }

      case 2: {
	 ASM_II_decode_bitalloc( fr_ps );
	 ASM_II_decode_scale( fr_ps );
	 if( fr_ps->mono_forced ) fr_ps->stereo = 1;
	 ASM_II_decode_frame( fr_ps );
	 sample_frame_length = fr_ps->sub_band_size * 3 * SCALE_BLOCK;
	 break;
      }
   }
   fr_ps->frame_count++;
   fr_ps->got_bits = BSTR_seek_tell( fr_ps->bs );
   fr_ps->out_write_length += sample_frame_length;
   fr_ps->out_sample_length += sample_frame_length;

   return sample_frame_length;
}
