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

    File    :	AIFF.c

    Author  :	Stéphane TAVENARD

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

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

    #Rev|   Date   |			  Comment
    ----|----------|--------------------------------------------------------
    0	|05/06/1995| Initial revision from file common.c of
	|	   | MPEG/audio software simulation group		  ST
    1	|23/06/1995| Aminet release					  ST

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

    AIFF utilities

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

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "AIFF.h"

#define 	BITS_IN_A_BYTE		8


/****************************************************************************
*
*  Routines to convert between the Apple SANE extended floating point format
*  and the IEEE double precision floating point format.  These routines are
*  called from within the Audio Interchange File Format (AIFF) routines.
*
*****************************************************************************/

/*
*** Apple's 80-bit SANE extended has the following format:

 1	 15	 1	      63
+-+-------------+-+-----------------------------+
|s|	  e	|i|	       f		|
+-+-------------+-+-----------------------------+
  msb	     lsb   msb			     lsb

The value v of the number is determined by these fields as follows:
If 0 <= e < 32767,		then v = (-1)^s * 2^(e-16383) * (i.f).
If e == 32767 and f == 0,	then v = (-1)^s * (infinity), regardless of i.
If e == 32767 and f != 0,	then v is a NaN, regardless of i.

*** IEEE Draft Standard 754 Double Precision has the following format:

MSB
+-+---------+-----------------------------+
|1| 11 Bits |		52 Bits 	  |
+-+---------+-----------------------------+
 ^     ^		^
 |     |		|
 Sign  Exponent 	Mantissa
*/

/*****************************************************************************
*
*  double_to_extended()
*
*  Purpose:	Convert from IEEE double precision format to SANE extended
*		format.
*
*  Passed:	Pointer to the double precision number and a pointer to what
*		will hold the Apple SANE extended format value.
*
*  Outputs:	The SANE extended format pointer will be filled with the
*		converted value.
*
*  Returned:	Nothing.
*
*****************************************************************************/

static void double_to_extended( double *pd, char ps[ 10 ] )
{
   register unsigned long  top2bits;

   register unsigned short *ps2;
   register IEEE_DBL	   *p_dbl;
   register SANE_EXT	   *p_ext;

   p_dbl = (IEEE_DBL *) pd;
   p_ext = (SANE_EXT *) ps;
   top2bits = p_dbl->hi & 0xc0000000;
   p_ext->l1 = ((p_dbl->hi >> 4) & 0x3ff0000) | top2bits;
   p_ext->l1 |= ((p_dbl->hi >> 5) & 0x7fff) | 0x8000;
   p_ext->l2 = (p_dbl->hi << 27) & 0xf8000000;
   p_ext->l2 |= ((p_dbl->lo >> 5) & 0x07ffffff);
   ps2 = (unsigned short *) & (p_dbl->lo);
   ps2++;
   p_ext->s1 = (*ps2 << 11) & 0xf800;
}

/*****************************************************************************
*
*  extended_to_double()
*
*  Purpose:	Convert from SANE extended format to IEEE double precision
*		format.
*
*  Passed:	Pointer to the Apple SANE extended format value and a pointer
*		to what will hold the the IEEE double precision number.
*
*  Outputs:	The IEEE double precision format pointer will be filled with
*		the converted value.
*
*  Returned:	Nothing.
*
*****************************************************************************/

static void extended_to_double( char ps[ 10 ], double *pd )
{
   register unsigned long  top2bits;

   register IEEE_DBL	   *p_dbl;
   register SANE_EXT	   *p_ext;

   p_dbl = (IEEE_DBL *) pd;
   p_ext = (SANE_EXT *) ps;
   top2bits = p_ext->l1 & 0xc0000000;
   p_dbl->hi = ((p_ext->l1 << 4) & 0x3ff00000) | top2bits;
   p_dbl->hi |= (p_ext->l1 << 5) & 0xffff0;
   p_dbl->hi |= (p_ext->l2 >> 27) & 0x1f;
   p_dbl->lo = (p_ext->l2 << 5) & 0xffffffe0;
   p_dbl->lo |= (unsigned long) ((p_ext->s1 >> 11) & 0x1f);
}

/*****************************************************************************
*
*  Read Audio Interchange File Format (AIFF) headers.
*
*****************************************************************************/

int AIFF_read_headers( FILE *file_ptr, IFF_AIFF *aiff_ptr )
{
   register long   seek_offset;
   register long   sound_position;

   ChunkHeader	   Header;
   Chunk	   FormChunk;
   CommonChunk	   CommChunk;
   SoundDataChunk  SndDChunk;

   if( fseek( file_ptr, 0, SEEK_SET ) != 0 ) return -1;

   if( fread( &FormChunk, sizeof(Chunk), 1, file_ptr ) != 1 ) return -1;

   if( (FormChunk.ckID != IFF_ID_FORM) ||
       (FormChunk.formType != IFF_ID_AIFF) ) return -1;

   /*
    * chunks need not be in any particular order
    */

   while( fread( &Header, sizeof(ChunkHeader), 1, file_ptr ) == 1 ) {

      if( Header.ckID == IFF_ID_COMM ) {
	 /*
	  * read comm chunk
	  */
	 if( fread( &CommChunk.numChannels,
		    sizeof(CommonChunk) - sizeof(ChunkHeader),
		    1, file_ptr ) != 1 )
	    return -1;

	 extended_to_double( CommChunk.sampleRate, &aiff_ptr->sampleRate);
	 aiff_ptr->numChannels = CommChunk.numChannels;
	 aiff_ptr->numSampleFrames = CommChunk.numSampleFrames;
	 aiff_ptr->sampleSize = CommChunk.sampleSize;

      }
      else if( Header.ckID == IFF_ID_SSND ) {
	 /*
	  * read ssnd chunk
	  */
	 if( fread( &SndDChunk.offset,
		    sizeof(SoundDataChunk) - sizeof(ChunkHeader),
		    1, file_ptr ) != 1 )
	    return -1;

	 aiff_ptr->blkAlgn.offset = SndDChunk.offset;
	 aiff_ptr->blkAlgn.blockSize = SndDChunk.blockSize;
	 aiff_ptr->sampleType = *(unsigned long *)Header.ckID;

	 /*
	  * record position of sound data
	  */
	 sound_position = ftell(file_ptr);

	 /*
	  * skip over sound data to look at remaining chunks
	  */
	 seek_offset = Header.ckSize - sizeof(SoundDataChunk) + sizeof(ChunkHeader);

	 if( fseek( file_ptr, seek_offset, SEEK_CUR ) != 0 ) return -1;

      } else {
	 /*
	  * skip unknown chunk
	  */
	 seek_offset = Header.ckSize;

	 if( fseek( file_ptr, seek_offset, SEEK_CUR ) != 0 ) return -1;
      }
   }

   return sound_position ;
}

/*****************************************************************************
*
*  Seek past some Audio Interchange File Format (AIFF) headers to sound data.
*
*****************************************************************************/

int AIFF_seek_to_sound_data( FILE *file_ptr )
{
   if( fwrite( NULL,
	       sizeof(Chunk) + sizeof(CommonChunk) + sizeof(SoundDataChunk),
	       1, file_ptr ) != 1) return -1;
   return 0;
}

/*******************************************************************************
*
*  Write Audio Interchange File Format (AIFF) headers.
*
*******************************************************************************/

int AIFF_write_headers( FILE *file_ptr, IFF_AIFF *aiff_ptr )
{
   Chunk	   FormChunk;
   CommonChunk	   CommChunk;
   SoundDataChunk  SndDChunk;

   FormChunk.ckID     = IFF_ID_FORM;
   FormChunk.formType = IFF_ID_AIFF;
   CommChunk.ckID     = IFF_ID_COMM;

   double_to_extended( &aiff_ptr->sampleRate, CommChunk.sampleRate );

   CommChunk.numChannels      = aiff_ptr->numChannels;
   CommChunk.numSampleFrames  = aiff_ptr->numSampleFrames;
   CommChunk.sampleSize       = aiff_ptr->sampleSize;
   SndDChunk.offset	      = aiff_ptr->blkAlgn.offset;
   SndDChunk.blockSize	      = aiff_ptr->blkAlgn.blockSize;
   SndDChunk.ckID	      = aiff_ptr->sampleType;

   CommChunk.ckSize = sizeof(CommonChunk) - sizeof(ChunkHeader);

   SndDChunk.ckSize = sizeof(SoundDataChunk) - sizeof(ChunkHeader) +
      (CommChunk.sampleSize + BITS_IN_A_BYTE - 1) / BITS_IN_A_BYTE *
      CommChunk.numChannels * CommChunk.numSampleFrames;

   FormChunk.ckSize = sizeof(Chunk) + SndDChunk.ckSize + sizeof(ChunkHeader) +
		      CommChunk.ckSize;

   if( fseek( file_ptr, 0, SEEK_SET ) != 0 ) return -1;

   if( fwrite( &FormChunk, sizeof(Chunk), 1, file_ptr ) != 1 ) return -1;

   if( fwrite( &CommChunk, sizeof(CommChunk), 1, file_ptr) != 1 ) return -1;

   if( fwrite( &SndDChunk, sizeof(SoundDataChunk), 1, file_ptr) != 1 ) return -1;

   return 0;
}


