
	/***************************************************************\
	*	GPlayer V 0.0   Sonix/DMCS Score ummmm...player		*
	*								*
	*				By Greg Cunningham,		*
	*				    Nomad Development		*
	*								*
	*	SMUS/DMCS/8SVX IFF form reader				*
	*	And other stuff too.					*
	*								*
	*	Notes: I have no idea what all this does!!!		*
	*	But it may contain usefull info for a music player.	*
	*	Questions:						*
	*		What's the Sonix SNX1: chunk?			*
	*		How do you figure out Sonix instrument forms?	*
	*		And how about TRAK/UBR1 notes?			*
	\***************************************************************/

#include	<Exec/Types.h>
#include	<Exec/Memory.h>
#include	<Proto/Dos.h>
#include	<Proto/Exec.h>

#define MAKE_ID(a,b,c,d)\
	(((long)(a)<<24)+((long)(b)<<16)+((long)(c)<<8)+(long)(d))

#define FORM	MAKE_ID('F','O','R','M')
#define SMUS	MAKE_ID('S','M','U','S')
#define SHDR	MAKE_ID('S','H','D','R')
#define NAME	MAKE_ID('N','A','M','E')
#define AUTH	MAKE_ID('A','U','T','H')
#define Copy	MAKE_ID('S','N','X','1')
#define SNX1	MAKE_ID('S','N','X','1')
#define ANNO	MAKE_ID('A','N','N','O')
#define INS1	MAKE_ID('I','N','S','1')
#define TRAK	MAKE_ID('T','R','A','K')
#define Copyrt	MAKE_ID('(','c',')',' ')
/*_Sonix SampledSound_________________*/
#define Samp	MAKE_ID('S','a','m','p')
/*__________8SVX______________________*/
#define BODY	MAKE_ID('B','O','D','Y')
#define ESVX	MAKE_ID('8','S','V','X')
#define VHDR	MAKE_ID('V','H','D','R')
#define ATAK	MAKE_ID('A','T','A','K')
#define RLSE	MAKE_ID('R','L','S','E')
/*__________DMCS______________________*/
#define DMCS	MAKE_ID('D','M','C','S')
#define USC1	MAKE_ID('U','S','C','1')
#define UBR1	MAKE_ID('U','B','R','1')
#define UVLT	MAKE_ID('U','V','L','T')
#define UIID	MAKE_ID('U','I','I','D')

/*_____Score_______*/
#define INS1_Name	0
#define INS1_MIDI	1

#define SID_FirstNote	0
#define SID_LastNote	127
#define SID_Rest	128
#define SID_Instrument	129
#define SID_TimeSig	130
#define SID_KeySig	131
#define SID_Dynamic	132
#define SID_MIDI_Chnl	133
#define SID_MIDI_Preset	134
#define SID_Clef	135
#define SID_Tempo	136
#define SID_Mark	255

#define noteChord	(1<<7)
#define noteTieOut	(1<<6)
#define noteNShift	4
#define noteN3		(1<<noteNShift)
#define noteN5		(2<<noteNShift)
#define noteN7		(3<<noteNShift)
#define noteNMask	noteN7
#define noteDot 	(1<<3)
#define noteDShift	0
#define noteD1		(0<<noteDShift)
#define noteD2		(1<<noteDShift)
#define noteD4		(2<<noteDShift)
#define noteD8		(3<<noteDShift)
#define noteD16 	(4<<noteDShift)
#define noteD32 	(5<<noteDShift)
#define noteD64 	(6<<noteDShift)
#define noteD128	(7<<noteDShift)
#define noteDMask	noteD128
#define noteDurMask	0x3F
#define IsChord(snote)	(((UWORD)snote)&noteChord)
#define IsTied(snote)	(((UWORD)snote)&noteTieOut)
#define NTuplet(snote)	((((UWORD)snote)&noteNMask)>>noteNShift)
#define IsDot(snote)	(((UWORD)snote)&noteDot)
#define Division(snote) ((((UWORD)snote)&noteDMask)>>noteDShift)

#define timeNMask    0xF8
#define timeNShift	3
#define timeDMask	7

#define TimeNSig(sTime) ((((UWORD)sTime)&timeNMask)>>timeNShift)
#define TimeDSig(sTime) (((UWORD)sTime)&timeDMask)

typedef struct{UWORD tempo; UBYTE volume,ctTrack;	  }SScoreHeader;
typedef struct{UBYTE iRegister,type,data1,data2,name[60]; }RefInstrument;
typedef struct{UBYTE sID,data; }SEvent;
typedef struct{
  unsigned tone  :8,
	chord	 :1,
	tieOut	 :1,
	nTuplet  :2,
	dot	 :1,
	division :3;
 }SNote;

typedef struct{
 unsigned type	 :8,
	timeNSig :5,
	timeDSig :3;
 }STimeSig;


/*_____8SVX________*/
#define Unity	 0x10000L
#define sCmpNone	0
#define sCmpFibDelta	1

typedef struct{
	ULONG oneShotHiSamples,repeatHiSamples,samplesPerHiCycle;
	UWORD samplesPerSec;
	UBYTE ctOctave,sCompression;
	LONG  volume;
 }Voice8Header;

typedef struct{
	UWORD duration;
	LONG  dest;
 }EGPoint;

/*_____IFF Chunk___*/
union typekludge{char type_str[4]; long type_long;};
struct ChunkHeader{union typekludge chunktype; long chunksize;};

struct FileHandle  *File=0;
SScoreHeader	   shdr;
RefInstrument	   Inst[20];
Voice8Header	   vhdr;
struct ChunkHeader ch;
UBYTE		   insnum=0,traknum=0,Buf[80],InstDir[30],voicenum=0,dmcs=0;
BYTE		   *Trak[4];
LONG		   TrakSize[4];

void skipchunk(size)long size; {Seek(File,size,0);}


readform(formsize)
long	 formsize;
{
 long  subtype;
 UBYTE is_8SVX=0;

 if(!getsubtype(&subtype)) return(0);
 formsize -=sizeof(subtype);
 if(subtype==DMCS){	printf("FORM: DMCS\n"); dmcs=1;}
 else if(subtype==SMUS) printf("FORM: SMUS\n");
 else if(subtype==ESVX){printf("FORM: 8SVX\n"); is_8SVX=1;}
 else return(0);
 while(formsize>0){
   if(!getchunkheader(&ch)) return(0);
   formsize -=sizeof(ch);
   switch(ch.chunktype.type_long){
	case SHDR:
		Read(File,&shdr,ch.chunksize);
		printf("SHDR: Tempo=%d Volume=%d Tracks=%d\n",
		 shdr.tempo,shdr.volume,shdr.ctTrack);
		break;
	case VHDR:
		Read(File,&vhdr,ch.chunksize);
		printf("VHDR: OneShotHiSamples=%ld RepeatHiSamples=%ld\n",
		 vhdr.oneShotHiSamples,vhdr.repeatHiSamples);
		printf("SamplesPerHiCycle=%ld SamplesPerSec=%ld Octave=%d\n",
		 vhdr.samplesPerHiCycle,vhdr.samplesPerSec,vhdr.ctOctave);
		voicenum++; break;
	case NAME:
		Read(File,Buf,ch.chunksize);
		Buf[ch.chunksize]=0;
		printf("NAME: %s\n",Buf);
		break;
 	case INS1:
		Read(File,&Inst[insnum],ch.chunksize);
		Inst[insnum].name[ch.chunksize-4]=0;
		printf("INS1: Instrument[%d]=%s\n",insnum+1,Inst[insnum].name);
		insnum++; break;
	case TRAK:/* Sonix notes */
		TrakSize[traknum]=ch.chunksize;
		Trak[traknum]=(BYTE *)AllocMem(TrakSize[traknum],MEMF_CHIP);
		Read(File,Trak[traknum],ch.chunksize);
		printf("TRAK: Track[%d] Bytes=%ld\n",traknum+1,ch.chunksize);
		traknum++; break;
	case SNX1:
		skipchunk(ch.chunksize); /* Important for Sonix */
		printf("SNX1: Bytes=%ld\n",ch.chunksize);
		break;
	case AUTH:
		Read(File,Buf,ch.chunksize);
		Buf[ch.chunksize]=0;
		printf("AUTH: %s\n",Buf);
		break;
	case ANNO:
		skipchunk(ch.chunksize);
		printf("ANNO: Beats me!\n");
		break;
	case ATAK:
		skipchunk(ch.chunksize);
		printf("ATAK: Bytes=%ld\n",ch.chunksize);
		break;
	case RLSE:
		skipchunk(ch.chunksize);
		printf("RLSE: Bytes=%ld\n",ch.chunksize);
		break;
	case BODY:
		skipchunk(ch.chunksize);
		printf("BODY: Bytes=%ld\n",ch.chunksize);
		/**
		if(vhdr[vh_num]->sCompression){
			temp=(BYTE *)AllocMem(ch.chunksize<<1,MEMF_CHIP); 
			DUnpack(buf,ch.chunksize,temp); 
			FreeMem(buf,ch.chunksize); 
			buf=temp;
		}
		**/		 
		break;

	case Copyrt:
		Read(File,Buf,ch.chunksize);
		Buf[ch.chunksize]=0;
		printf("%s\n",Buf);
		break;
	case USC1:
		skipchunk(ch.chunksize);
		printf("USC1: Bytes=%ld\n",ch.chunksize);
		break;
	case UBR1:/* DMCS notes & text */
		skipchunk(ch.chunksize);
		printf("UBR1: Bytes=%ld\n",ch.chunksize);
		break;
	case UVLT:
		skipchunk(ch.chunksize);
		printf("ULVT: Bytes=%ld\n",ch.chunksize);
		break;
	case UIID:
		Read(File,&Inst[insnum].name,ch.chunksize);
		Inst[insnum].name[ch.chunksize]=0;
		printf("UIID: Instrument[%d]=%s\n",insnum+1,Inst[insnum].name);
		insnum++; break;

	default: skipchunk(ch.chunksize); break;
   }
   formsize -=ch.chunksize;
   if(ch.chunksize&1){formsize--; Seek(File,1,0);}
 }
 return(1);
}

getchunkheader(header)	struct ChunkHeader *header;
{return(Read(File,header,sizeof(*header)));}

getsubtype(type)	long *type;
{return(Read(File,type,sizeof(*type)));}

void FreeTraks()
{
 int i;

 for(i=0;i<4;i++)
	if(Trak[i]) FreeMem(Trak[i],TrakSize[i]);
}

void LoadInst()
{
 int i;

 for(i=0;i<insnum;i++){
   if(!strcmp(Inst[i].name,"First Voice")) goto SKIP;
   if(dmcs) sprintf(Buf,"%s%s",InstDir,Inst[i].name);
   else     sprintf(Buf,"%s%s.instr",InstDir,Inst[i].name);
   printf("%-36s",Buf);
   if(!(File=Open(Buf,1005))){printf("\n\nFile not found: %s\n"); return;}
   getchunkheader(&ch);
   if(ch.chunktype.type_long==FORM) readform(ch.chunksize);
   else if(ch.chunktype.type_long==Samp){
	printf("SampledSound\n"); Close(File);
	printf("%s%s.ss",InstDir,Inst[i].name);
	if(!(File=Open(Buf,1005))){printf("\n\nFile not found: %s\n",Buf); return;}
	printf("\n");
   }else printf("Sonix Editor Instrument\n");
   if(File) Close(File);
SKIP:
 }
}

main(argc,argv)
int argc;
char **argv;
{
 printf("\n[33mGPlayer V 0.0 by Greg Cunningham.[0m\n");
 printf("SMUS Score Player.\n\n");
 if(argc<3){printf("Usage: GPlayer Score Instruments/\n"); goto Q;}

 if(!(File=Open(argv[1],1005))){
	sprintf(Buf,"%s.smus",argv[1]);
	if(!(File=Open(Buf,1005)))
		{printf("File not found: %s\n",Buf); goto Q;}
 }
 if(!getchunkheader(&ch))	  goto Q;
 if(ch.chunktype.type_long!=FORM) goto Q;
 if(!readform(ch.chunksize))	  goto Q;
 if(File) Close(File);

 strcpy(InstDir,argv[2]); LoadInst(); File=0;

 FreeTraks();
 printf("\n\n Sorry, can't get audio channels!\n"); /* Fake'em out */
Q:
 if(File) Close(File);
 exit(0); return(0);
}



/* Unpack Fibonacci-delta encoded data from n byte source
 * buffer into 2*n byte dest buffer, given initial data
 * value x.  It returns the lats data value x so you can
 * call it several times to incrementally decompress the data.
 */

BYTE codeToDelta[16]={-34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21};

BYTE D1Unpack(source,n,dest,x) BYTE source[],dest[]; LONG n; BYTE x;
{
 BYTE d;
 LONG i,lim;

 lim=n<<1;
 for(i=0;i<lim;++i){
 /* Decode a data nibble, high nibble then low nibble */
	d=source[i>>1]; 	/* get a pair of nibbles */
	if(i&1)			/* select low or high nibble */
		d&=0xf; 	/* mask to get the low nibble */
	else	d>>=4;		/* shift to get the high nibble */
	x +=codeToDelta[d];	/* add in the decoded delta */
	dest[i]=x;		/* store a 1 byte sample */
 }
 return(x);
}

/* Unpack Fibonacci-delta encoded data from n byte
 * source buffer into 2*(n-2) byte dest buffer.
 * Source buffer has a pad byte, an 8-bit initial
 * value, followed by n-2 bytes comprising 2*(n-2)
 * 4-bit encoded samples.
 */

void DUnpack(source,n,dest) BYTE source[],dest[]; LONG n;
{D1Unpack(source+2,n-2,dest,source[1]);}


