/*
 *  Copyright (c) by Jaroslav Kysela (Perex soft)
 */

#ifndef __STDIO_H
#include <stdio.h>
#endif
#include <sys/gus.h>
#include "effects.h"

#define VERSION	"1.11h"

/*
   New types
*/

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned long dword;

extern byte *convPos;

/*
   Defines
*/

#define MAX_INSTRUMENTS		128
#define MAX_SAMPLES		16
#define MAX_ORDER_VALUE		256
#define MAX_PATTERN_VALUE	256
#define GUS_CHANNELS		32
#define SIZE_OF_SEQBUF		( 8 * 1024 )

#define MODULE_NONE	0
#define MODULE_XM	1
#define MODULE_S3M	2
#define MODULE_MTM	3
#define MODULE_669	4
#define MODULE_MOD	5

#define MAX_SONG_LEN	( 192L * 1024L )

#define SROW_TERMINATE	0xffff

#define MIN_PERIOD	1
#define MAX_PERIOD	0x7fff

#define MIN_PERIOD_AMIGA (28-2)
#define MAX_PERIOD_AMIGA (3424+2)

#define PAL_FREQ	8287
#define NTSC_FREQ	8363

#define MAX_OCTAVE	8

#define CALC_BPM( arg ) ( ( 5000000L + arg ) / ( 2L * arg ) )

#define WIN_NOTE_START	0x0001
#define WIN_FREQ	0x0002
#define WIN_VOL		0x0004
#define WIN_OFFSET	0x0008
#define WIN_PAN		0x0010
#define WIN_KEYOFF	0x0020

#define FTYPE_NOTHING	0x0000
#define FTYPE_LINEAR	0x0001
#define FTYPE_S3M	0x0002
#define FTYPE_AMIGA	0x0003

#define START_NOTE( channel ) voices[ channel ].what_is_new |= WIN_NOTE_START;
#define STOP_NOTE( channel ) { voices[ channel ].volume = 0; SET_VOLUME( channel ); }
extern void SET_VOLUME( byte channel );
extern void SET_INSTR( byte channel, short instr );
extern void SET_NOTE( byte channel, short note );
extern void SET_PERIOD( byte channel, int period );
#define SET_BPERIOD1( channel ) SET_BPERIOD( channel, voices[ channel ].basePeriod )
extern int GET_NOTE_PERIOD( byte channel, short note );

#define LO_BYTE( w ) ( w & 0xff )
#define HI_BYTE( w ) ( ( w >> 8 ) & 0xff )

#define LFLG_NONE	0
#define LFLG_WHOLE	1
#define LFLG_JUMP	2

#ifdef ENABLE_JUMP
#define DEFAULT_LOOP_FLAGS	LFLG_JUMP
#else
#define DEFAULT_LOOP_FLAGS	LFLG_NONE
#endif

/*
   Structs
*/

#define SMP_TYPE_UNSIGNED	0x0001
#define SMP_TYPE_16BIT		0x0002
#define SMP_TYPE_DELTA		0x0004
#define SMP_TYPE_PINGPONG_LOOP	0x0008
#define SMP_TYPE_PAN_USED	0x0010

struct SAMPLE {
  short used;
  char name[ 31 ];
  dword length;
  dword lstart;
  dword lend;
  byte volume;
  byte pan;
  signed char finetune;
  signed char relativeNote;
  dword freqC4;		/* frequency of C2 (C4) note */
  short ident;		/* identifier of sample for gus driver */
  word type;		/* see SMP_TYPE_XXXX constants */
};

#define IS_TYPE_ON	0x01
#define IS_TYPE_SUSTAIN	0x02
#define IS_TYPE_LOOP	0x04

struct envelopePoints {
  word pos;
  word val;
};

struct INSTR {
  short used;		/* 0 - instrument isn't used */
  char name[ 31 ];
  byte sampleNumber[ MAX_OCTAVE * 12 ];
  struct envelopePoints volEnvelope[ 12 ];
  struct envelopePoints panEnvelope[ 12 ];
  byte volPoints;
  byte panPoints;
  byte volSustain;
  byte volLoopStart;
  byte volLoopEnd;
  byte panSustain;
  byte panLoopStart;
  byte panLoopEnd;
  byte volType;		/* see IS_TYPE_XXXX */
  byte panType;		/* see IS_TYPE_XXXX */
  byte vibratoType;
  byte vibratoSweep;
  byte vibratoDepth;
  byte vibratoRate;
  word fadeout;
  struct SAMPLE *samples[ MAX_SAMPLES ];
};

struct VOICE {
  short instr;		/* 0 - MAX_INSTRUMENTS */
  short sample;		/* 0 - MAX_SAMPLES */
  short note;
  int basePeriod;	/* ST3 base period */
  short volume;		/* 0-64 */
  short pan;		/* 0-255 */

  int cmd[ 2 ];
  short arg[ 2 ];
  
  byte glissando;
  short slideToNote;
  int slideToPeriod;
  word slideToNoteArg;

  byte slideUpFirst;
  byte slideDownFirst;
  
  byte vibratoWave;
  byte vibratoArg;
  byte fineVibratoArg;
  byte vibratoPek;
  byte vibratoFirst;
  
  byte tremoloWave;
  byte tremoloArg;
  byte tremoloPek;
  byte tremoloFirst;

  short tremor;
  byte tremorArg;
  byte tremorFirst;
  
  byte arpegArg;
  byte arpegPos2;
  short arpegOldNote;
  
  byte retrigArg;
  byte retrigFirst;
  
  byte cutNoteFirst;
  byte delayNoteFirst;

  word slideUpArg;
  word slideDownArg;
  word extraSlideUpArg;
  word extraSlideDownArg;
  byte volSlideArg;
  byte gvolSlideArg;
  byte panSlideArg;

  word eVolPos;		/* volume envelope position - computed from start of sample */
  word ePanPos;		/* pan envelope position - computed form start of sample */
  byte eVol;		/* current envelope volume (0-64) */
  byte ePan;		/* current envelope pan position (0-64) */
  dword eFadeout;	/* current fadeout (cumulative) (0-65535) 65536=stop voice */
  short eKeyoffVol;	/* 0=nothing,1=keyoff request,2=keyoff in progress */
  short eKeyoffPan;
  short eVibPos;	/* 0x7fff=maximum depth */
  word eVibPos1;
  int eVib;

  word what_is_new;	/* flags */
  
  long finalPeriod;
  dword gusOffset;	/* sample offset */
};

struct sTimeSynchro {
  long tick;
  word order;
  word row;
  struct sTimeSynchro *next;
};

#define SNDO_VIBRATO		0x00000001

#define MAX_RANGE_SIZE		80

struct sPlayList {
  short volume;
  short palFreq;
  int loopFlags;
  dword options;
  short freqType;
  char patterns[ MAX_RANGE_SIZE + 1 ];
  char sinstruments[ MAX_RANGE_SIZE + 1 ];
  int sleepForSecs;
  short rampAuto;
  short smoothPan;
  char *filename;
  struct sPlayList *prev;
  struct sPlayList *next;
};

/*
   Variables
*/

extern byte *_seq_buf;
extern byte seqBuf[];
extern int seqfd;

extern int moduleType;

extern short songError;

extern byte *song;
extern byte songName[];
extern byte songType[];
extern byte songChannels;
extern byte songInstr;
extern struct INSTR *songInstrs[];
extern byte songPlaySpeed;
extern byte songTempo;
extern short songGlobalVolume;
extern signed char finetuneTable[];
extern short freqType;
extern short currentPalFreq;
extern short palFreq;
extern short mainVolume;
extern short songVolume;
extern short smoothValue;
extern short rampAutoValue;
extern int gusMemorySize;
extern int ticksPerDivision;
extern short delayPattern;
extern short sineOscTbl[][ 64 ];
extern struct VOICE voices[];
extern int songLoopFlags;
extern int warnings;
extern int background;
extern short songMaxOrder;
extern short songOrder[];
extern short songRestart;
extern short songMaxPattern;
extern byte *songPatternPos[];
extern struct sPlayList *playListFirst;
extern struct sPlayList *playListLast;
extern struct sPlayList *playListCurrent;
extern int orderDelta;
extern int quit;
extern int next;
extern int prev;
extern int loop;
extern long absTick;
extern struct sTimeSynchro *timeSynchroFirst;
extern struct sTimeSynchro *timeSynchroLast;
extern dword disableChannels;
extern dword disabledChannels;
extern dword songOptions;
extern char *songPatterns;
extern char *shortInstrs;
extern int ncursesFlag;

/*
   Prototypes
*/


/* Misc */

void fatal( char *fmt, ... );
void dprintf( char *fmt, ... );
void wprintf( char *fmt, ... );
void pprintf( char *fmt, ... );
void info();

/* Loaders */

struct INSTR *newInstrument( short instrument );
struct SAMPLE *newSample( short instrument, short sample );

void convNewSong();
void convReallocSong();
void convClearSong();

void convInitPattern();
void convDonePattern();
void convInitRow();
void convDoneRow();
void convAddNote( byte channel, byte instr, byte note, byte volume, 
                  byte effect1, byte arg1, 
                  byte effect2, byte arg2 );

void translateModEffect( byte *eff, byte *arg );

void detectModuleType( FILE *file );

void loadMOD( FILE *file, char *filename );
void loadXM ( FILE *file, char *filename );
void loadS3M( FILE *file, char *filename );
void loadMTM( FILE *file, char *filename );
void load669( FILE *file, char *filename );

/* GUS routines */

void gusInit();
void gusDone();
int gusMemFree();
void gusStopAllChannels();
void gusDownload( FILE *in, int instrument, int sample ); 
void gusSetupVolume();
void gusSetupChannels();
void doEnvelopeThings();
void doNewThings();

/* Effects */

void initVoices();
void doEffects( short what );
void playEffectOnly( byte what, byte channel, byte instr, byte effect, byte arg );
void playEffect( byte what, byte channel, byte instr, byte volume, byte effect, byte arg );
void hiddenPlayEffect( byte what, byte channel, byte effect, byte arg );
int doPreEffect( byte what, byte channel, byte instr, byte note, byte volume, byte effect, byte arg );
void keyoff( byte channel );
void clearEffects();
void stopNewNoteEffects( byte channel );

/* Timer routines */

void timeSynchroAdd( word order, word row );
struct sTimeSynchro *timeSynchroGet( long tick );
void timeSynchroFree();

void queueFlush();
void timerSetup();
void timerNext();

/* Play routines */

void playShowPosition( word order, word row );
void play();

/* Signals */

void initSignalHandlers();
void doneSignalHandlers();

/* Play list */

struct sPlayList *playListAdd( char *songFilename );
void playListFree();
int rangeCheck( char *range );
int rangeFirst( char *range, int min );
int rangeLast( char *range, int max );
int rangeNext( char *range, int prev, int max );
int rangePrev( char *range, int next, int min );
int rangeIn( char *range, int value );

/* some inline functions */

extern inline void SET_BPERIOD( byte channel, int period )
{
  SET_PERIOD( channel, period );
  voices[ channel ].basePeriod = period;
}

extern inline void SET_NOTE_PERIOD( byte channel )
{
  voices[ channel ].basePeriod =
    GET_NOTE_PERIOD( channel, voices[ channel ].note );
  SET_BPERIOD1( channel );
}  
