/****************************************************************************

	A simple SAMP reader. This program reads in a SAMP format file using
	the dissidents requester.library for file name input and error message
	handling ( this library is not mandatory for	using the samp.library, 
	but since it's so @#$! easy to use, why not? ) This demonstrates the use
	of the EXTRAVector for obtaining various parameters of a wave, such as 
	sample rate, size, loop points, etc. Other ways of obtaining this 
	information are also shown, using the SampleHeader64 structure. Once the
	fields are obtained and Transpose Table(s) is/are setup, the program 
	closes down. This guy does a lot of printf()'s, so you'll probably want
	to redirect the output to a file which you can examine at your leisure, 
	as in:

	samp_readout > outfile

	by Jeff Glatt and Jim Fiore, dissidents.

	Not-so-fancy compile/link instructions:

	( Manx 3.6 )
	cc +P samp_readout.c
	ln samp_readout.o SampInterface.o FileInterface.o -lcl32

	( Lattice 5.0 )
	lc -b0 samp_readout.c
	blink lib:c.o samp_readout.o SampInterface.o FileInterface.o to samp_readout lib lib:lcnb.lib

	Note: SampInterface.o and FileInterface.o are not the same for Lattice/Manx.
	Create them by running SampInterface.asm and FileInterface.asm through your
	system's assembler. For non-Manx, make sure that you get rid of the
	far data  far code   reference at the top of the .asm files.

*****************************************************************************/

#ifdef AZTEC_C
#define NARGS
#define NO_PRAGMAS
#endif

#include "exec/types.h"
#include "exec/memory.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"

#include "graphics/gfx.h"
#include "graphics/rastport.h"

#ifdef LATTICE
#include "proto/all.h"
#else  
#include "functions.h"
#endif

/*============ Include files for dissidents' custom libraries ===========*/

#include "FileIO.h"
#include "SAMP.h"

/*================== For dissidents requester.library =================*/

struct	RequesterBase			*RequesterBase = 0L;
struct	FileIO					*fileio_sup = 0L;
UBYTE		fileio_filename[260];

/*==================== For dissidents samp.library =====================*/

extern	LONG SAMPError;			/* This comes from SAMPInterface.asm */
struct	SAMPBase					*SAMPBase = 0L;
struct 	SAMPInfo					*libSAMPinfo = 0L;

#define playCHANS 4
UBYTE		play_map[ 128 * playCHANS ];  /* let NumOfChans = 4. You can change this if you desire */
struct	TransposeNode			*T_List = 0L;

/* Declare 32 SampleHeader64 structs for loading up to 32 waves in a SAMP */
#define	MAXSAMPLES	32L		/* We arbitrarily load up to 32 waves */
#define	NAMESIZE		16			/* This is arbitrary */

struct SampleHeader64 samp_head[ MAXSAMPLES ];

UBYTE		nameBuffers[ MAXSAMPLES ][ NAMESIZE ];	/* for the wave names */


/*====================== The C Application begins here ==================*/

#ifdef NARGS
VOID open_all(), exit_all();
BOOL ReadExtraVector();
long main();
#else
VOID open_all( void ), exit_all( void );
BOOL ReadExtraVector( ULONG, struct SampleHeader64 *, struct SAMPInfo *, struct waveHeader * );
long main( long, char ** );
#endif

main( argc, argv )
long argc;
char *argv[];
{
	ULONG	totalWaves, p, period, wave_size, loop_start, loop_end, loop_length;
	LONG  x;
	USHORT i;
	UBYTE *address;

	/* Open libs, get FileIO struct */
	open_all();

	/* choose the SAMP file to load via the requester.library */
	address = DoFileIOWindow( fileio_sup, 0L );
	if( address == fileio_filename )
	{

		/* Open the file and determine if it is a SAMP */
		if( !( libSAMPinfo = OpenSampRead( fileio_filename, 0L ) ) )
		{
			address = SAMPErrorMsg( SAMPError );
			puts( address );
			exit_all();
		}

		/* Before proceeding, set a few output fields of SAMPinfo, depending on need */
		libSAMPinfo->MaxChars = NAMESIZE;	/* NOTE: Don't cast NAMESIZE as a LONG. This field is a WORD */
		libSAMPinfo->MemType = MEMF_PUBLIC;  /* not using DMA playback */

		/* EXTRA Vector will allow us to examine the waveHeader structure, automatically */
		libSAMPinfo->EXTRA = (APTR)ReadExtraVector;

		/* Grab any needed input fields of SAMPinfo at this point */
		printf("Format = %d bits\n\n", libSAMPinfo->SampleFormat );

		/* Load up to 32 waves starting with the first wave in the SAMP (0)
			We'll load these into our samp_head and nameBuffers starting with
			the first member of each array. */

		if( !(totalWaves = ReadWaves( 0L, MAXSAMPLES, &nameBuffers[0][0], &samp_head[0] )) )
		{
			/* We didn't load ANY waves! Must be an empty SAMP. */
			CloseSamp();
			address = SAMPErrorMsg( SAMPError );
			puts( address );
			exit_all();
		}


		/* Print out the names of the waves in the file */
		for( i = 0; i < totalWaves; ++i )
	   {
			printf("Name of wave %d is %s\n", i, &nameBuffers[i][0] );
		}

		/* The following function zeros out our playMap and transfers the loaded
	  		playMap to our	passed playMap. It adjusts for difference in numOfChans
		   between our	program's playMap (4 chans) and the loaded playMap 
			(0 to 4 chans). Also, it ensures that only those wave numbers that we 
			loaded appear in our play_map. We pass an offset of 0 because we 
			loaded from the first SampleHeader64 in our samp_head array. 

			If you do not plan on playing back the wave data from the Amiga, or
			adjusting mapping, you do not need to deal with playMaps. This would
			be the case in a program which reads in the data and just draws it, 
			or perhaps only performs some analysis of it. */


		LoadPlaymap( totalWaves, 4L, 0L, 0L, &play_map[0] );

		/* Let's verify the play_map */
		for( x = 0; x < 128*playCHANS; x += playCHANS )
		{
			printf("Loaded playmap[%d] %d %d %d %d\n",x, play_map[x], play_map[x+1], play_map[x+2], play_map[x+3] );
		}

		for( i = 0; i < totalWaves; i++ )
	   {
			/* The SAMP library set the TransTable fields of each loaded
				SampleHeader64 struct to that wave's sample period in nSec. This is
				a simple convenience trick, since you need the period, and the
				TransTable field is unused until the Transpose Table is built.
				Alternately, we could get the period from the EXTRA Vector routine.

				Now we use this function to get all of the needed TransposeTables.
			   This function links any new TranposeTable into our T_List and 
				returns the address of that TransposeTable's ORIGINAL_PITCH. We'll
			   simply restore this return in the SampleHeader64's TransTable field.

			   We can grab data at this point as well, if desired. Although we
		      are capable of finding parameters such as loop start and wave size
				in the SampleHeader64 structure, this material was simply copied
				over from the waveHeader structure by the library. Use of the
				EXTRA Vector to obtain this data is preferred ( see above ).

			   Finally, note that if you don't plan on playing back a wave, you
				have no need to create a Transpose Table, and this whole section
				may be skipped. Two cases where you might not want a Transpose 
				Table are when you prefer to 'roll your own' ( for example, for
				micro-tonal scales ), or in an application which simply analizes
				wave data ( for example, a data analysis package performing FFT
				or TDS type functions ). Note the comment above, concerning 
				playMaps. In short, playMaps and Transpose Tables are musical
				performance oriented items. They are not part of the SAMP spec.
				They simply exist for the samp reader/writer library and
				contain only the information that a player program would need. */

			wave_size = samp_head[i].WaveSize;
			loop_length = (samp_head[i].LoopLength) * 2;   /* was in WORDS */
			loop_end = (ULONG)(samp_head[i].OneShotEnd);
			loop_start = (ULONG)(samp_head[i].OneShotAddr);
			loop_start = loop_end - loop_start;            /* one shot size */
			period = (ULONG)(samp_head[i].TransTable);

			printf("\nEquivalent data obtained from SampleHeader64 structure:\n");
			printf("Size %d,  Loop Start %d, Loop Len %d, Period %d (nSec)\n\n", wave_size, loop_start, loop_length, period );

			/* Make a Transpose Table that is 6 steps up and down from the root 
				pitch, 13 notes total. 6 steps is an arbitrary value for this
				example, but the routine MakeTransTable() supports only values of
				up to 12 for upper and lower ranges.
				
				It is important to note that after a load, the TransTable field
				of all SampleHeader64 structures must be converted to an
				ORIGINAL_PITCH address ( as in the MakeTransTable() call below ), 
				or set to zero. Do not leave this field as the sampling period! */

			samp_head[i].TransTable = MakeTransTable( (ULONG)(samp_head[i].TransTable), 6L, 6L, &T_List );

			/* Let's verify the Transpose Table... Remember, 0 means out of range */
			for( x = -6; x <= 6; x++ )
			{
				printf("TransTable[%d] %d\n",x, samp_head[i].TransTable[x] );
			}
		}

		/* We are all done reading the SAMP file, so close samp */

		CloseSamp();

		/* At this point, we could do whatever we needed with the wave data.
			Since this program doesn't really _do_ anything, we just deallocate
		   whatever was allocated during the read-in process, clean up, and exit */


		/* Free the sample memory (for "total" # of waves) */
		for( p=0; p<totalWaves; ++p )
	   {
			FreeMem( samp_head[p].OneShotAddr, samp_head[p].WaveSize );
		}

		FreeTTables( &T_List );

	}

	exit_all();

}   /********** end of main() ********/




/****************************  open_all() ***********************************

	General beginning routine which opens FileIO and SAMP libs, and sets up
	the fileio support structure.

*****************************************************************************/

VOID open_all()
{
   /* Open the requester library and allocate/setup a FileIO structure */
	if( !(RequesterBase=(struct RequesterBase *)OpenLibrary("requester.library", 1L)) )
	{
		puts("Can't find requester.library\n");
		exit_all();
	}

	if( !(fileio_sup = GetFileIO()) )
	{
		puts("Can't get FileIO support\n");
		exit_all();
	}

	fileio_sup->X = 6;
	fileio_sup->Y = 11;
	fileio_sup->DrawMode = JAM2;
	fileio_sup->PenA = 0;
	fileio_sup->PenB = 1;
	fileio_sup->Buffer = (UBYTE *)fileio_filename;

   /* Open the SAMP reader/writer library */
	if( !(SAMPBase=(struct SAMPBase *)OpenLibrary("samp.library", 0L)) )
	{
		puts("Can't find samp.library\n");
		exit_all();
	}
}




/****************************  exit_all() ***********************************

	General clean up routine which closes FileIO and SAMP libs.

*****************************************************************************/

VOID exit_all()
{
	if( fileio_sup )    ReleaseFileIO( fileio_sup );
	if( RequesterBase ) CloseLibrary( RequesterBase );
	if( SAMPBase )      CloseLibrary( SAMPBase );
	exit( FALSE );
}


/****************************  ReadExtraVector() ****************************

	This is the routine passed to the EXTRA field of the SAMPinfo structure,
	for reading SAMP files. This allows us to determine a variety of parameters
	describing the SAMP file and its waves. Here, we just print out a few
	of the generally desirable elements. This routine is automatically called
	by the library during ReadWaves() immediately before the sample data for
	the current wave is about to be read in. At this point, the library has
	already called all of your other vectors (i.e. ATAKvector, USERvector, etc)

*****************************************************************************/

BOOL ReadExtraVector( wave_number, sample_header, samp_info, wave_header )
ULONG wave_number;
struct SampleHeader64 *sample_header;
struct SAMPInfo *samp_info;
struct waveHeader *wave_header;
{

	/* DON'T look at any fields of SampleHeader64 in here since the data has
	yet to be copied over from the waveHeader!! This will be garbage. */

	printf("READ EXTRA Vector results for wave %d :\n", wave_number );

	printf("Wave Size = %d bytes\n", wave_header->WaveSize );
	printf("Period = %d nanoseconds\n", wave_header->Period );
	printf("Sample Rate = %d Hz\n", wave_header->Rate );
	printf("Loop Start at %d byte offset\n", wave_header->LoopStart );
	printf("Loop End at %d byte offset\n", wave_header->LoopEnd );
	printf("Loop Type = %d ( 0 = forward, 1 = backward )\n\n", wave_header->LoopType );

	return( 0 ); /* continue, all good.  A 1 would abort the load at this point. */
}

/*************************** DAT'S ALL FOLKS.... ****************************/
