/*		WaveReadClass.h			05.01.2014 18:17
		R.Froehlich

-------------------------------------------------
05.01.2014  Broadcast Wave allowed, chunk skipped
-------------------------------------------------		
*/

#ifndef	_WaveReadClass_h
#define	_WaveReadClass_h


#include	"saq_config.h"


#include	<windows.h>
#include	<stdio.h>
#include	<math.h>

#ifndef	DWORD
#define	DWORD unsigned long
#endif


#define	u8	unsigned char
#define u16 unsigned short
#define u32 unsigned long


static inline 	unsigned long swap16(unsigned long a) 
{  
	unsigned long	x= a & 0xff ; 
	unsigned long	y= (a>>8) & 0xff ; 
	return (x<<8)|y; 
}

static inline 	unsigned long swap32(unsigned long a) 
{ 	unsigned long x = a & 0xffff;
	unsigned long y = (a>>16) & 0xffff;
	return (swap16(x)<<16) | swap16(y);
}

typedef struct wave_header_struct 
{
	int  	riffname;			// 00
    int  	dateilen;           // 04
    int  	wavname;            // 08
    
    int  	formname;           // 12
    int   	subchlen;           // 16
    
    short 	format;             // 20
    short 	nChannels;          // 22 1/2
    int  	sample_freq;        // 24
    int		bytes_per_sec;      // 28
    short 	bytes_per_sample;   // 32 : 8bit: 1/2 or 16bit: 2/4 or 24bit: 3/6
    short 	bits_per_sample;    // 34 only if PCM: 8/16/24
    
    int  	dataname;           // 36
    int  	datalen_bytes;      // 40
// ... wave data				   44 ff.    
    
} wave_header_typ;
	
///// DiskPlayer :


class Wave_Read_Class
{


private:

	FILE  * INWAV;
	long	framesize;	// stereo samples (one for a L/R float pair) per block for audio callback
	double	fs;			// host sample frequ in Hz, typ. 44100.0 Hz, unused here
	long	rd_blockno,wr_blockno,mod_block;	// linear gliding block index read+write, actual access modulo mod_block, empty is wr_blockno-rd_blockno=0, full is wr_blockno-rd_blockno = mod_block.

	
	float * zerobuff;	// stereo buffer, used for pause
	float * blockstore;	// huge array for ca. 1 sec. stereo samples from disk to audio callback application
	short * inbuf; 		// here is a single buffer for file input in stereo, for mono only half is read		

public:	
	volatile int file_activ;	
	volatile long rest_samplecount;	// counts mono-sample or stereo-pair	
	volatile int play_stop_request;	// -1,0,1,2.
	volatile int pause;
	volatile int stereo_flag;		// 0= mono
    volatile int case_24bit;         
    volatile int case_16bit;   
    volatile int case_8bit;   
        	
	wave_header_typ inheader;

	double ori_Fs;		// from wav-file read, unused here
	int wavinfileno;	
	int warnbox;
	
	int  nPlayfile_Name;    // length of name	
	char Playfile_Name[MAX_PATH];
	
	int		update_hms;						// flag for GUI
	int		hours,minutes,seconds;			// the stored run time for display by GUI
	int		frames_per_sec, written_frames;

	char	timestring[40];
	
	char	* Get_Tapetime(void)
	{	update_hms = 0;
		sprintf( timestring,"%3d:%02d:%02d", hours,minutes,seconds);
		return &timestring[0];	
	}
		
	
	void	INC_h()	{ hours++; }
	
	void	INC_m()
	{	minutes++;
		if (minutes >= 60) 
		{	minutes = 0;
			INC_h();
		}	
	}
		
	void	INC_s()
	{	seconds++;
		if (seconds >= 60) 
		{	seconds = 0;
			INC_m();	
		}
		// after every second: trigger message to GUI:
		
		update_hms = 1;	
	}	
		  
public:
	
Wave_Read_Class()		// Constructor :
{ 	
	INWAV = NULL;
	framesize = 0;
	fs = 0.0;
	rd_blockno = wr_blockno = 0;
	zerobuff = NULL;
	blockstore = NULL;
	inbuf = NULL;
	wavinfileno = 0;
	warnbox = 0;
	
	pause = 0;
	rest_samplecount=0;
	play_stop_request=0;
	file_activ = 0;
	stereo_flag = 0;
	case_24bit = case_16bit = case_8bit= 0;
	
	hours=minutes=seconds = 0;
	update_hms = 0;
	frames_per_sec = 44100;	// can be overwritten
	written_frames = 0;
							
}	// Constructor 


~Wave_Read_Class()	// Destructor:
{
	if (zerobuff) delete zerobuff;
	if (blockstore) delete blockstore;
	if (inbuf) delete inbuf;
	if (INWAV) fclose(INWAV);
					
}	// Destructor:
		
//******************************************************************************
#if 1
unsigned long Get_Filesize (FILE * fp)	// Return value: File size in bytes, or 0 if file is not open.
{
	unsigned long curr_pos, size;

	if (fp == (FILE *) NULL)	//   File open? 
    	return 0L;
  
	if (feof (fp))	curr_pos = 0xffffffffL;	// my end marker
	else			curr_pos = ftell (fp);

	fseek (fp, 0, SEEK_END);
	size = ftell (fp);
	fseek (fp, curr_pos, SEEK_SET);

	return size;

}
#endif
//******************************************************************************
	
int Open_WavePlayer ( void )		// return 0 if OK, else error
{	int i, mcount ;
	unsigned long fsize;

    INWAV = fopen( Playfile_Name,"rb");	// read binary for WAV file
 	if ( INWAV == NULL) return -1;

    nPlayfile_Name = strlen(Playfile_Name);
    
	fsize = Get_Filesize ( INWAV );

	if ( fsize < sizeof (inheader))  { FTRACE("Header too short, NO read!"); goto L10; } 
		  
// i = FileSize( INWAV);	// Borland function
// FTRACE("input file size = %ld", i);

mcount = fread( &inheader,  1, 12 ,INWAV ); // 3 long read
if (mcount!= 12)  { FTRACE("Bad Header read!"); goto L10; }
if    ( (inheader.riffname != swap32('RIFF'))||
        (inheader.wavname  != swap32('WAVE'))
      ) { FTRACE("Bad Header read!"); goto L10; }

skip_chunk:
mcount = fread( &inheader.formname,  1, 8 ,INWAV ); // 2 long read, incl. subchlen
if (mcount!= 8)  { FTRACE("No data read!"); goto L10; }
if (inheader.formname != swap32('fmt '))
    {
      mcount = fseek( INWAV, inheader.subchlen, SEEK_CUR);  // skip all chunks not related to us
      if (mcount !=0)  { FTRACE("No data read!"); goto L10; }
      goto skip_chunk;  
    }

if (inheader.subchlen != 16) { FTRACE("Bad format chunk!"); goto L10; } 
   
mcount = fread( &inheader.format,  1, 16 ,INWAV ); // format chunk read
if (mcount!= 16)  { FTRACE("No format read!"); goto L10; }

skip_chunk1:
mcount = fread( &inheader.dataname,  1, 8 ,INWAV ); // 2 long read, incl. subchlen
if (mcount!= 8)  { FTRACE("No data read!"); goto L10; }
if (inheader.dataname != swap32('data'))
    {
      mcount = fseek( INWAV, inheader.datalen_bytes, SEEK_CUR);  // skip all chunks not related to us
      if (mcount !=0)  { FTRACE("No data read!"); goto L10; }
      goto skip_chunk1;  
    }


// if (inheader.dataname != swap32('data')) { FTRACE("No data chunk!"); goto L10; }

#if 0
if ( fsize < ( inheader.datalen_bytes + sizeof (inheader)))
{
	MessageBox (NULL, "Bad data length, truncated !","       SAQrx Warning ", MB_ICONEXCLAMATION | MB_OK);	
	
	inheader.datalen_bytes = fsize - sizeof (inheader); // correct length	
	if (inheader.datalen_bytes <= 4) goto L9;
	
}
#endif

if (  inheader.format != 1) { FTRACE("NOT PCM format, aborted!"); goto L9; }

stereo_flag = (inheader.nChannels == 2);

if (inheader.nChannels > 2 ) { FTRACE("Too many channels, aborted!"); goto L9; }

if ( inheader.bytes_per_sample==0) { FTRACE("Bad format, aborted!"); goto L9; }

case_8bit = (inheader.bits_per_sample == 8);
case_16bit= (inheader.bits_per_sample ==16);
case_24bit= (inheader.bits_per_sample ==24);

if ((inheader.bits_per_sample!=8)&&(inheader.bits_per_sample!=16)&&(inheader.bits_per_sample!=24))  
    { FTRACE("bits_per_sample  = %d", inheader.bits_per_sample ); goto L9; }

ori_Fs = (double) inheader.sample_freq;	/* int --> float  */

if ((warnbox==0)&&(ori_Fs != fs))
{	char sz[200];
	double r = ori_Fs/fs;
	double rr;
	if (r <= 0.0) rr=9999; else rr= 1/r;
	
	sprintf(sz,"Frequency/Time scaled with %6.3lf / %6.3lf\n\n! SAMPLERATE IS NOT CONVERTED !\n\nThis warning is given only one time !", rr,r);
	
	MessageBox (NULL, sz,"       SAQrx Warning ", MB_ICONEXCLAMATION | MB_OK);	
	warnbox++;
}


FTRACE("ori_Fs= %f Hz", ori_Fs);

rest_samplecount = inheader.datalen_bytes/ inheader.bytes_per_sample;   

FTRACE("File opened for replay, samples = %d", rest_samplecount );

if ( rest_samplecount == 0)
{
	MessageBox (NULL, "No data to play !","       SAQrx Warning ", MB_ICONEXCLAMATION | MB_OK);	
	goto L10;
}	
	
wavinfileno++;	
pause = 0;
play_stop_request = 0;
written_frames = 0;
hours=minutes=seconds = 0;
file_activ = 1;
return 0;	// 0 = OK.

L9:
MessageBox (NULL, "Not allowed file format !","    SAQrx Warning ", MB_ICONEXCLAMATION | MB_OK);	
L10:
fclose( INWAV );
INWAV = NULL;

return -1;

}	/* Open_WavePlayer */

		



	void Set_Wave_Read_Parameters (long Framesize, double Fs )	// written from main/ASIO at first audio callback
	{
		if (framesize == 0)			// only onetime !
		{
			framesize = Framesize;
			fs		  = Fs;
			
	
			zerobuff = new float[ 2*framesize ];				// stereo buffer for pause
			memset( zerobuff, 0, 2*framesize * sizeof(float) );

			// entire block number modulo range is 0 .. mod_block-1.
			mod_block = (long) (2.2+(10.2*(2*fs)/framesize));	// ca. 10.2 sec storage 	
			blockstore = new float[ 2*framesize*mod_block ];	// stereo blocks ! each has (2*framesize) float samples

			inbuf     = new short [  4*framesize ];	// 2*: stereo buffer from disk 16bit, if mono: only half length is read !
                                                    // 4*: for 24/32bit storage 06.01.2014
			frames_per_sec = (int) ( fs );												
		}
		
		rd_blockno = wr_blockno = 0;	// block index cleared
		
	}
	

// if data fifo is not full, return pointer is valid and buffer is accepted, else NULL	
	float * Get_Free_BlockPointer(void)
	{
		if ( framesize == 0) return NULL;	// not initialized!		
		long diff = wr_blockno - rd_blockno;	// count number of blocks used , linear access!		
		if ( diff >= (mod_block-1) ) return NULL;	// almost full		
		long wrblock = wr_blockno % mod_block;	// index is modulo for cyclic wave buffer		

		// wr_blockno++;	// callee must do this after filling of buffer !						

		wrblock = wrblock * (2*framesize);		// absolute index		
		return & blockstore [ wrblock ];
	}	// Get_Free_BlockPointer()
	
// if data fifo is not empty, return pointer is valid disk data, else return pause=zero buffer :

	float * Get_Play_Block(void)	// called from audio callback		
	{
//		if ( framesize == 0) return NULL;	// not initialized! but audio is not performed here
		
		long diff = wr_blockno - rd_blockno;// count number of blocks used , linear access!
		
		if (( diff == 0 )&&(play_stop_request == -1))	// regular file end
			{ play_stop_request = 1; return zerobuff; }
			
		if (( diff == 0 )||(pause)) return zerobuff;	// empty

	
		written_frames += framesize;
		if ( written_frames >= frames_per_sec )
		{
			written_frames -= frames_per_sec;
			INC_s();
		}
				
		long rdblock = rd_blockno % mod_block;	// index is modulo for cyclic wave buffer
		
		rd_blockno++;						// increment read block counter 
		
		rdblock = rdblock * (2*framesize);	// absolute index for stereo block
		
		return & blockstore[ rdblock ];
	}	// Get_Play_Block()


void Play_Wave_Mono(void)	//---- called cyclic in Timer-IRQ: --------->>
{	int len,i;
	short * sptr;
	float * dest;	
	float const scale16 = 1.0/32768;
	float const scale32 = 1.0/(256*256)/(32768);
	float const scale8  = 1.0/128;
	float res;

	if ((file_activ==0)||(play_stop_request==-1)) return;

	if ( play_stop_request==2)			// ack from audio
	{	rest_samplecount = 0;
		file_activ = 0;
		fclose( INWAV );				// file ready.
		INWAV = NULL;
		play_stop_request = 0;
		pause = 0;
		return;		
	}
					
	if ( rest_samplecount <= 0) 
	{	if ( play_stop_request==0)
		{	play_stop_request=1;		// wait for ack from audio
			return;
		}
		
		return;	// nothing to play
	}
	

	
next:		
	len = rest_samplecount;
	if ( len > framesize) len = framesize;	
	
	dest = Get_Free_BlockPointer();		// get a free block
	if (dest == NULL) return;			// wait if all buffers busy

	sptr = &inbuf[0];
    
    int structsize = inheader.bytes_per_sample;

    if ( len < framesize) memset ( sptr, 0, sizeof(inbuf) ); // clear inbuf for rest of file
        	
	i = fread( sptr ,  structsize , len ,INWAV );	// read data into disk transfer block
	
	if (i == len)
	{
		rest_samplecount -= len;
			
		// sptr = &inbuf[0]; // unchanged
		
		if (case_24bit)
		{   // 24bit:
		    int is24;   // signed int
		    char * cp = (char *) sptr;
		    int  * ip = (int *) cp;

            if (stereo_flag)
    		{
    			for ( i = 0; i < framesize; i++)	// conversion 24bit to float:
     			{	
     			    is24 = *ip;   // read 32 bit
     			    is24 = is24 << 8; // hi byte discard
     			    *dest++ = scale32 * (float) is24;   // L channel
     			    cp = cp +3; 
     			    ip = (int *) cp;
     			    
                    is24 = *ip;   // read 32 bit
     			    is24 = is24 << 8; // hi byte discard
     			    *dest++ = scale32 * (float) is24;   // R channel
     			    cp = cp +3; 
     			    ip = (int *) cp;
     			         			     		
    			}			
    		}		    
		    else
    		{
    			for ( i = 0; i < framesize; i++)	// conversion 24bit to float:
     			{	
     			    is24 = *ip;   // read 32 bit
     			    is24 = is24 << 8; // hi byte discard
     			    res = scale32 * (float) is24;
     			    *dest++ = res;   // L channel
     			    *dest++ = res;   // R channel
     			    cp = cp +3; 
     			    ip = (int *) cp;     			                        		
    			}			
    		}		    
		    		    
		}
		else if (case_16bit)
		{   // 16bit:
		if (stereo_flag)
		{
			for ( i = 0; i < framesize; i++)	// conversion short to float:
  			{		
				*dest++ = scale16 * (float) *sptr++;	// L channel
				*dest++ = scale16 * (float) *sptr++;	// R channel			
			}			
		}
		else
		{
			for ( i = 0; i < framesize; i++)	// conversion short to float:
	  		{		
				res = scale16 * (float) *sptr++;	// mono propagate to both channels
				*dest++ = res;	// L channel
				*dest++ = res;	// R channel			
			}
		}
		}
    else if (case_8bit)
    {   // 8 bit:
		    signed int is8; 
		    signed char * cp = (signed char *) sptr;
		   

            if (stereo_flag)
    		{
    			for ( i = 0; i < framesize; i++)	// conversion 8bit to float:
     			{	
     			    is8 = (signed int) (*cp++);         // read 8 bit, sign expand
     			    is8 = is8 - 128;                    // remove offset
     			    *dest++ = scale8 * (float) is8;     // L channel
     			    
     			    is8 = (signed int) (*cp++);         // read 8 bit, sign expand
     			    is8 = is8 - 128;                    // remove offset
     			    *dest++ = scale8 * (float) is8;     // R channel
     			         			         			     		
    			}			
    		}		    
		    else
    		{
    			for ( i = 0; i < framesize; i++)	// conversion 8bit to float:
     			{	
     			    is8 = (signed int) (*cp++);         // read 8 bit, sign expand
     			    is8 = is8 - 128;                    // remove offset
     			    res = scale8 * (float) is8;    			         			    
     			    *dest++ = res;                      // L channel
     			    *dest++ = res;                      // R channel
     			    			                        		
    			}			
    		}		    
		    		    
		}   // 8 bit
				
		// after conversion, the block is now ready for accept:
		wr_blockno++;
		
		if (rest_samplecount > 0) goto next;	// try again

		if ( play_stop_request==0)
		{	play_stop_request= -1;	}	// wait for ack from audio		

//		fclose( INWAV );	// file ready.
//		INWAV = NULL;
//		Button_Selectfile->Caption = "Select File";
//		Memo1->Lines->Append("WAVE terminated."); 
	}
	else // error:
	{	
		if ( play_stop_request==0)
		{	play_stop_request=1;	}	// wait for ack from audio	
				
		rest_samplecount = 0;
		
//		fclose( INWAV );	// file ready.
//		INWAV = NULL;
//		play_stop_request = 0;
//		Button_Selectfile->Caption = "Select File";
//		Memo1->Lines->Append("WAVE read error!"); 		
	}
		


}	// Play_Wave_Mono()


  	
};	// Wave_Read_Class
	

#endif	// _WaveReadClass_h

