/*	SCCS Id: @(#)amisnd.c	3.2	95/7/23	*/
/* 	Copyright (c) 1992, 1993, 1995 by Gregg Wonderly */
/* NetHack may be freely redistributed.  See license for details. */

/*
 * This file contains music playing code.
 *
 * If we were REALLY determined, we would make the sound play
 * asynchronously, but I'll save that one for a rainy day...
 */

#include "hack.h"
#include "dlb.h"

#undef red
#undef blue
#undef green
#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/audio.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <graphics/gfxbase.h>

#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>

#include <stdio.h>
#include <stdlib.h>

#define	AMII_AVERAGE_VOLUME	60

int amii_volume = AMII_AVERAGE_VOLUME;

typedef struct VHDR
{
	char name[4];
	long len;
	unsigned long oneshot, repeat, samples;
	UWORD freq;
	UBYTE n_octaves, compress;
	LONG volume;
} VHDR;

typedef struct IFFHEAD
{
	char FORM[4];
	long flen;
	char _8SVX[4];
	VHDR vhdr;
	char NAME[4];
	long namelen;
} IFFHEAD;

extern struct GfxBase *GfxBase;

UBYTE whichannel[] = { 1, 2, 4, 8 };
void makesound( char *, char *, int vol);
void amii_speaker( struct obj *instr, char *melody, int vol );

/* A major scale in indexs to freqtab... */
int notetab[] = { 0, 2, 4, 5, 7, 9, 11, 12 };

/* Frequencies for a scale starting at one octave below 'middle C' */
long freqtab[] = {
	220,	/*A */
	233,	/*Bb*/
	246,	/*B */
	261,	/*C */
	277,	/*Db*/
	293,	/*D */
	311,	/*Eb*/
	329,	/*E */
	349,	/*F */
	370,	/*Gb*/
	392,	/*G */
	415,	/*Ab*/
	440,	/*A */
};

#ifdef	TESTING
main( argc, argv )
	int argc;
	char **argv;
{
	makesound( "wooden_flute", "AwBwCwDwEwFwGwawbwcwdwewfwgw", 60 );
	makesound( "wooden_flute", "AhBhChDhEhFhGhahbhchdhehfhgh", 60 );
	makesound( "wooden_flute", "AqBqCqDqEqFqGqaqbqcqdqeqfqgq", 60 );
	makesound( "wooden_flute", "AeBeCeDeEeFeGeaebecedeeefege", 60 );
	makesound( "wooden_flute", "AxBxCxDxExFxGxaxbxcxdxexfxgx", 60 );
	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
	makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 );
}
#else
void
amii_speaker( struct obj *instr, char *melody, int vol )
{
	int typ = instr->otyp;
	char * actualn = (char *)OBJ_NAME( objects[typ] ) ;

	/* Make volume be relative to users volume level, with 60 being the
	 * average level that will be passed to us.
	 */
	vol = vol * amii_volume / AMII_AVERAGE_VOLUME;

	makesound( actualn, melody, vol );
}
#endif

void
makesound ( char *actualn , char * melody, int vol )
{
	char *t;
	int c, cycles, dot, dlay;
	dlb *stream = 0;
	IFFHEAD iffhead;
	struct IOAudio *AudioIO = 0;
	struct MsgPort *AudioMP = 0;
	struct Message *AudioMSG = 0;
	ULONG device = -1;
	BYTE *waveptr = 0;
	LONG frequency=440, duration=1, clock, samp, samples, samcyc=1;
	unsigned char name [ 100 ] ;

	if ( flags.silent )
		return;

	if( GfxBase->DisplayFlags & PAL )
		clock = 3546895;
	else
		clock = 3579545;

	/*
	 * Convert type to file name - if there's nothing to play we
	 * shouldn't be here in the first place.
	 */
	strncpy(name, actualn,sizeof(name) ) ;
	for( t = strchr( name, ' ' ); t; t = strchr( name, ' ' ) )
		*t = '_';
	if( (stream = dlb_fopen( name, "r" )) == NULL )
	{
	    perror( name );
	    return;
	}

	AudioIO = (struct IOAudio *)
		AllocMem( sizeof( struct IOAudio ), MEMF_PUBLIC|MEMF_CLEAR );
	if( AudioIO == 0 )
		goto killaudio;

	AudioMP = CreatePort( NULL, 0 );
	if( AudioMP == 0 )
		goto killaudio;

	AudioIO->ioa_Request.io_Message.mn_ReplyPort = AudioMP;
	AudioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
	AudioIO->ioa_Request.io_Command = ADCMD_ALLOCATE;
	AudioIO->ioa_Request.io_Flags = ADIOF_NOWAIT;
	AudioIO->ioa_AllocKey = 0;
	AudioIO->ioa_Data = whichannel;
	AudioIO->ioa_Length = sizeof( whichannel );

	device = OpenDevice( AUDIONAME, 0L, (struct IORequest *)AudioIO, 0L );
	if( device != 0 )
		goto killaudio;

	if( dlb_fread( (genericptr_t)&iffhead, sizeof( iffhead ), 1, stream ) != 1 )
		goto killaudio;

	/* This is an even number of bytes long */
	if( dlb_fread( name, (iffhead.namelen+1) & ~1, 1, stream ) != 1 )
		goto killaudio;

	if( dlb_fread( (genericptr_t)&samples, 4, 1, stream ) != 1 )
		goto killaudio;

	if( dlb_fread( (genericptr_t)&samples, 4, 1, stream ) != 1 )
		goto killaudio;

	waveptr = AllocMem( samples, MEMF_CHIP|MEMF_PUBLIC );
	if( !waveptr )
		goto killaudio;

	if( dlb_fread( waveptr, samples, 1, stream ) != 1 )
		goto killaudio;

	while( melody[0] && melody[1] )
	{
		c = *melody++;
		duration = *melody++;
		dot = 0;
		if( *melody == '.' )
		{
			dot = 1;
			++melody;
		}
		switch( duration )
		{
			case 'w': dlay = 3; duration = 1; cycles = 1; break;
			case 'h': dlay = 3; duration = 2; cycles = 1; break;
			case 'q': dlay = 2; duration = 4; cycles = 1; break;
			case 'e': dlay = 1; duration = 8; cycles = 1; break;
			case 'x': dlay = 0; duration = 16; cycles = 1; break;
			case 't': dlay = 0; duration = 32; cycles = 1; break;
			default: goto killaudio;  /* unrecognized duration */
		}

		/* Lower case characters are one octave above upper case */
		switch( c )
		{
			case 'a': case 'b': case 'c':
			case 'd': case 'e': case 'f': case 'g':
				c -= 'a' - 7;
				break;

			case 'A': case 'B': case 'C':
			case 'D': case 'E': case 'F': case 'G':
				c -= 'A';
				break;

			default:
				continue;
		}

		samcyc = samples;

		/* lowercase start at middle 'C', upper case are one octave below */
		frequency = c > 7 ? freqtab[notetab[c%7]]*2 : freqtab[notetab[c]];

		/* We can't actually do a dotted whole note unless we add code for a real
		 * 8SVX sample which includes sustain sample information to tell us how
		 * to hold the note steady...  So when duration == 1, ignore 'dot'...
		 */
		if( dot && duration > 1 )
			samp = ((samples / duration) * 3) / 2;
		else
			samp = samples / duration;

		/* Only use some of the samples based on frequency */
		samp = frequency * samp / 880;

		/* The 22khz samples are middle C samples, so adjust the play
		 * back frequency accordingly
		 */
		frequency = (frequency * (iffhead.vhdr.freq*2)/3) / 440L;

		AudioIO->ioa_Request.io_Message.mn_ReplyPort = AudioMP;
		AudioIO->ioa_Request.io_Command = CMD_WRITE;
		AudioIO->ioa_Request.io_Flags = ADIOF_PERVOL;
		AudioIO->ioa_Data = (BYTE *)waveptr;
		AudioIO->ioa_Length = samp;

		/* The clock rate represents the unity rate, so dividing by
		 * the frequency gives us a period ratio...
		 */
/*printf( "clock: %ld, freq: %ld, div: %ld\n", clock, frequency, clock/frequency );*/
		AudioIO->ioa_Period = clock/frequency;
		AudioIO->ioa_Volume = vol;
		AudioIO->ioa_Cycles = cycles;

		BeginIO( (struct IORequest *)AudioIO );
		WaitPort( AudioMP );
		AudioMSG = GetMsg( AudioMP );
		if( dlay )
			Delay( dlay );
	}

	killaudio:
	if( stream ) dlb_fclose( stream );
	if( waveptr ) FreeMem( waveptr, samples );
	if( device == 0 ) CloseDevice( (struct IORequest *)AudioIO );
	if( AudioMP ) DeletePort( AudioMP );
	if( AudioIO ) FreeMem( AudioIO, sizeof( *AudioIO ) );
}
