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

	A simple SAMP writer. This program creates a simple square wave and then
	saves it as a SAMP file. This demonstrates the use of the EXTRAVector for
	setting various parameters of a wave, such as  sample rate, size, loop
	points, etc. 

	This file uses puts(), so make sure that you launch it from a CLI/Shell.
	Also, this code is NOT optimized. It is for educational use only ( and
	we all know what _that_ means :-)

	by Jeff Glatt and Jim Fiore, dissidents.

	General compile/link instructions below. Note: the forms shown are certainly
	not the most efficient! Also, note that SampInterface.o is different for
	the two systems. To create your version, just assemble SampInterface.asm
	with your system's assembler ( for non-Manx, make sure you take out the
	reference to 'far code  far data' at the top of the .asm file. )

	( Manx 3.6 )
	cc +P samp_writer.c
	ln samp_writer.o SampInterface.o -lml32 -lcl32

	( Lattice 5.05 )
	lc -b0 -ff samp_writer.c
	blink lib:c.o samp_writer.o SAMPInterface.o to samp_writer lib lib:lcmffp.lib lib:lc.lib

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

#ifdef AZTEC_C
#define NARGS      /* no arguments in function decs */
#define NO_PRAGMAS
#endif

#include "stdio.h"
#include "math.h"

#ifdef LATTICE
#include "stdlib.h"
#include "mffp.h"
#endif

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

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

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

#include "Samp.h"


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

extern LONG SAMPError;			/* found in SAMPInterface.asm */
struct SAMPBase               *SAMPBase = 0L;
struct SAMPInfo					*libSAMPinfo = 0L;

#define PLAY_CHANS 4
UBYTE  play_map[ 128 * PLAY_CHANS ];  /* let NumOfChans = 4 */

/* Normally we would use MakeTransTable() to make a transpose table for a
	desired sample period and with the desired # of steps up and down. We would
	then stuff the returned *(ULONG) in our SampleHeader64's TransTable field.
	We would do all this if our application wanted to playback the waveform
	using the lookup table method as described in the samp doc. MakeTransTable
	would automatically link this new table into our T_List (the handle being
	this next variable). Then when we pass this handle to WriteWaves, the samp
	library automatically retrieves the correct table and sets the wave's sample
	period and rate accordingly. Since we aren't going to playback the wave we
	create, we'll dispense with the table, and leave our SampleHeader64's
	TransTable field NULL. WriteWaves() will then expect us to set the waveHeader's
	sample rate and period via our EXTRAvector.
*/

struct TransposeNode          *T_List = 0L;

/* Declare 1 SampleHeader64 struct for saving the wave as a SAMP */
#define  MAXSAMPLES 1L
#define  NAMESIZE   16

struct SampleHeader64 samp_head[ MAXSAMPLES ];


/*================== A goofy little structure for the wave =============*/

struct WaveData {
   UBYTE *Name;
   ULONG Period;
   ULONG WaveSize;
   SHORT *WaveStart;
	ULONG LoopStart;
	ULONG LoopEnd;
   UBYTE LoopMode; };

UBYTE global_name[ NAMESIZE ] = {"My Brain Hertz"};

struct WaveData Prof_R_J_Gumby = {
 /* *Name */           global_name,
 /* Period */          0,
 /* WaveSize */        0,
 /* *WaveStart */      0,
 /* LoopStart */       0,
 /* LoopEnd */         0,
 /* LoopMode */        0 };

struct WaveData *wave = &Prof_R_J_Gumby;

/* Professor Gumby says "I hit my head on the cupboard! I can't see anything!" */




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


#ifdef NARGS
void  exit_all();
BOOL  WriteExtraVector();
BOOL	make_wave();
long  main();
#else
void  exit_all( void );
BOOL  WriteExtraVector( struct SampleHeader64 *, struct SAMPinfo *, struct waveHeader * );
BOOL	make_wave( void );
long  main( long, char ** );
#endif

main( argc, argv )
LONG argc;
BYTE *argv[];
{
	UBYTE *address;

   /* Open the SAMP reader/writer library */

	if( !( SAMPBase = (struct SAMPBase *)OpenLibrary("samp.library", 0L) ) )
	{
		puts("need samp.library");
		exit( FALSE );
	}

	if( make_wave() )
	{
		/* At this point, we can do whatever we need with the wave data. So,
			we'll save this guy back as a new SAMP wave. */

		/* Open the output file, always called "outfile" */
		if( !( libSAMPinfo = OpenSampWrite( "outfile" ) ) )
		{
			address = SAMPErrorMsg( SAMPError );
			puts( address );
			exit_all();
		}

		/* Set a few output fields of SAMPinfo, depending on need */
		libSAMPinfo->MaxChars = NAMESIZE;
		libSAMPinfo->SampleFormat = 16;  /* bits */
		libSAMPinfo->NumOfWaves = 1;
		libSAMPinfo->PlayMode = INDEPENDENT;
		libSAMPinfo->NumOfChans = 4;

		/* Alter a few elements of the playMap, just so we can verify that this
			works. */
		play_map[0] = 1;
		play_map[5] = 1;

		if( !WriteMHDR( 0L, &play_map[0] ) )
		{
			puts("Error writing MHDR");
			CloseSamp();
			exit_all();
		}

		if( !WriteNames( NAMESIZE, &global_name[0] ) )
		{
			puts("Error writing Names");
			CloseSamp();
			exit_all();
		}

		/* EXTRA Vector will allow us to set the waveHeader structure per wave */
		libSAMPinfo->EXTRA = (APTR)WriteExtraVector;

		if( !WriteWaves( 0L, 1L, &samp_head[0], &T_List ) )
		{
			puts("Error writing Waves");
			CloseSamp();
			exit_all();
		}

		CloseSamp();

		FreeTTables( &T_List );

	}

	exit_all();

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





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

	General clean up routine which closes SAMP lib and deallocs wave mem.

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

void exit_all()
{
	if( wave->WaveStart ) FreeMem( wave->WaveStart, 2 * wave->WaveSize );
	if( SAMPBase )        CloseLibrary( SAMPBase );
	exit( FALSE );
}




/******************************** make_wave() *****************************

	This routine creates a 16 bit cheesoid square wave, and assigns the 
	data required for the SAMP format conversion to a WaveData structure ( an
	arbitrary structure of our design, just for this little example ). The
	data elements ( loop points, period, etc. ) are pretty much arbitrary here.
	Note that each sample point is 16 bits, hence 2 bytes, and all sizes are
	in terms of sample points. It is just as easy to store sizes, loops, and 
	such in terms of bytes...that's up to you! :-)

	( Why is this wave so cheesey? Well for starters, it's not band-limited.
	Aliasing anyone? )

	returns FALSE if error.

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

BOOL make_wave()
{
	UBYTE  x, y;
	SHORT  *ptr;

	wave->WaveSize = 10000;   	/* make this 10K points long ( 20K bytes ) */
	wave->Period = 50000;		/* 50000 nSecs = 20K Hz sampling rate */
	wave->LoopStart = 200;		/* Loop from the 200th point to the 8000th */
	wave->LoopEnd = 8000;
	wave->LoopMode = 0;			/* 'Forward only' type loop */

	if( !(ptr = AllocMem( 2 * wave->WaveSize, MEMF_PUBLIC )) )
	{
		puts("Can't make wave");
		return( 0 );
	}

	wave->WaveStart = ptr;

	/* make 200 cycles of a cheesey 400 Hz square wave of 18K peak.
		200 cycles at 50 points each = 10K points */

	for( x = 0; x < 200; x++ )
	{
		for( y = 0; y < 25; y++ )   /* positive halfwave */
			*ptr++ = 18000;

		for( y = 0; y < 25; y++ )   /* negative halfwave */
			*ptr++ = -18000;
	}

	return( 1 );  /* all is good */

} /******* end of make_wave() **********/





/****************************  WriteExtraVector() ****************************

	This is the routine passed to the EXTRA field of the SAMPinfo structure,
	for writing SAMP files. This allows us to set a variety of parameters
	describing the SAMP file and its waves. Here, we just set a few of the 
	generally desirable elements. Because our SampleHeader64's TransTable field
	was left NULL, the library was not able to set the waveHeader's sample
	period and rate for us. We must do that here. Also, we did not setup the
	rest of the SampleHeader64 fields, (i.e. size, loop start and end), so we
	have to setup the waveHeader fields instead of the library.

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

BOOL WriteExtraVector( sample_header, samp_info, wave_header )
struct SampleHeader64 *sample_header;
struct SAMPinfo *samp_info;
struct waveHeader *wave_header;
{
	/* The waveHeader structure uses all offsets and sizes in terms of bytes.
		Since our example WaveData structure stores everything in terms of
		sample points ( 2 bytes per point ), conversion is required.

		Note: if the TransTable field of the SampleHeader64 structure is 0,
		( ie, you didn't use a TransTable - see samp_readout example )
		then you _must_ set the waveHeader's sample Rate and Period in this
		vector ( as seen below ). */

   wave_header->WaveSize = wave->WaveSize * 2;   /* words to bytes */
	wave_header->Period = wave->Period;
	wave_header->Rate = 1E9 / ( wave->Period );   /* nanosecs to Hz */
	wave_header->LoopStart = wave->LoopStart * 2; /* words to bytes */
	wave_header->LoopEnd = wave->LoopEnd * 2;     /* words to bytes */
	wave_header->LoopType = wave->LoopMode;

	sample_header->OneShotAddr = (BYTE *)(wave->WaveStart);  /* where the data is */

	return( 0 ); /* continue, all good */
}

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