/*	main.cpp	  		08.01.2014 09:48	

	Main stuff for SAQrx
	
	Architecture and great portions of code by virtue of /JHB Johan H. Bodin SM6LKM
	
====================================================================================	
changes by Roland.Froehlich@t-online.de /RFR :
------------------------------------------------------------------------------------
07.01.2014  v0.94 8/16/24 bit play file, Broadcast Wave Format BWF allowed, extra chunks skipped,
            dB display range shifted by 10dB with 'v'=down or 'V'=up
19.03.2013  v0.93 display Playfile name corrected for corrupted length
21.01.2013  v0.92 Input selector L,R,L+R,L-R
14.01.2013  v0.91 SNR bar: afmax_dB - afmin_dB in audio band filter range
21.06.2012	v0.9 cw shift programmable with SHIFT+ mouse wheel, VFO/Shift step size with cursor click 
24.05.2012	New filter structure Filter_typ in coeffs.h
09.05.2012	Wavefile player added (only 16bit)
05.05.2012	v0.8 FFT window is premade, no trig functions at audio callback (faster)
24.04.2012	SPECTRUM_AF added for audio spectrum overlay
18.04.2012	v0.7 ,CPU load display smoothed
28.05.2008	2 cases of frequency axis for 44.1 or 48 kHz base
23.05.2008	variable FS for compiling 
01.05.2008	get the actual samplerate actual_Fs
05.01.2008	added simulation code for Morse send. SAQ is rarly on air ;-)

NOTE: TABs are 4 spaces for all C-Files !
=====================================================================================

Files from Project:

FileName=SAQrx.dev
FileName=main.cpp
OriginalFilename=SAQrx.exe
FileName=coeffs.c
FileName=dspmath.cpp
FileName=coeffs.h
FileName=dspmath.h
FileName=progicon.rc
FileName=pa_convert.c
FileName=portaudio.h
FileName=pa_host.h
FileName=pa_lib.c
FileName=pa_trace.h
FileName=pa_win_wmme.c
FileName=wave2disk.h
FileName=wave2disk.cpp
FileName=WaveReadClass.h
FileName=trace.cpp
FileName=saq_config.h

*/

/*
 *
 *      #####################################################################
 *
 *        Copyright (C) 2006  Johan H. Bodin SM6LKM
 *
 *        This software is provided 'as is', without warranty of any kind,
 *        express or implied. In no event shall the author be held liable
 *        for any damages arising from the use of this software.
 *
 *        Permission to use, copy, modify, and distribute this software and
 *        its documentation for non-commercial purposes is hereby granted,
 *        provided that the above copyright notice and this disclaimer appear
 *        in all copies and supporting documentation.
 *
 *        The software must NOT be sold or used as part of a any commercial
 *        or "non-free" product.
 *
 *      #####################################################################
 */

/*------------------------------------------------------------------------------
 *
 *		
 *
 *      SM6LKM's SAQ Receiver
 *
 *      My first "real" Window$ application!
 *
 *      Revision history:
 *        2006-12-04:
 *          "Serious" work begins... /JHB
 *        2006-12-05:
 *          Figured out how to do off-screen painting to avoid flicker. /JHB
 *        2006-12-06:
 *          Removed the hi-prio worker thread and moved it all into the callback.
 *          Added some mouse support including scroll wheel for tuning. /JHB
 *        2006-12-07:
 *          Added some "homebrew" buttons (LKM's non-standard UI :~)
 *          Corrected spectrum Y scaling for Hanning window.
 *          Added text to About & Help boxes. /JHB
 *        2006-12-08:
 *          My first Windows app' is ready for SAQ's X-mas transmission!
 *
 *        2006-12-11:
 *          Some finishing touches to the help text. First release (v0.3)!
 *
 *        2006-12-12:
 *          If host error on start-up, show the error code. Second release (v0.4)
 *          Fixed icon linkage, thanks Wolf!
 *
 *        2006-12-13, v0.5wip:
 *          Tuning range changed:
 *            LO range with WIDE filter: 0..19050 Hz
 *            LO range with NARROW filter: 0..20650 Hz
 *            (LO range was fixed at 0..19000 Hz in previous versions)
 *
 *        2006-12-14, v0.5 released:
 *          - Added 300Hz filter
 *          - Added CPU load indicator (toggled on/off with 'C').
 *          - Added 1Hz keyboard tuning with 'U'/'D' for up/down.
 *
 *        2006-12-14, v0.5a released:
 *          Icon buddy eyeglobes are now non-transparent ;o). /JHB
 *
 *        2006-12-19, v0.5b (NOT RELEASED):
 *          - FFT buffer size reduced to FFT_SIZE_IN (1024).
 *          - Plot buffer size reduced to NFFTPLOTPOINTS (FFT_SIZE_IN/2+1 = 513).
 *          - Unused threadID variable removed. /JHB
 *
 *        2007-02-18, v0.6 (NOT RELEASED):
 *          - Added "SAQrx alredy running" check. /JHB
 *
 *      FIXMEs:
 *        - Use some non-modal window for help text instead of messagebox()...
 */


#include	"saq_config.h"

#define MAUSTEST	0			// test screen coordinates x,y for development



#define NFFTPLOTPOINTS (FFT_SIZE_IN/2 + 1)  // number of bins in FFT output


int kHzXOffs[1+MMSTEPS];

void init_kHzXOffs( void)	// build table of x-coord. of freq. marker text in kHz
{	int i;
	for (i=0; i <= MMSTEPS; i++) kHzXOffs[i] = (int)( 0.5+ (1000*i*512)/(FS_BASE/2));	// integer rounding	
}

//---------------------------------------------------------------------- 28.05.2008


#define SAQ_Carrier_Freq 	17200.0	// Hz
#define DCF77_Carrier_Freq	77500.0	// Hz


//===================================================================================================================

int xflag = 0;			// flag -x from commandline (usefull for test )
//===================================================================================================================


#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
//#include <conio.h>
#include <mmsystem.h>	// for timeGetTime()
#include <stdlib.h>		// for _argc, _argv
/*
 * This seems like a convenient place to declare these variables, which
 * give programs using WinMain (or main for that matter) access to main-ish
 * argc and argv. environ is a pointer to a table of environment variables.
 * NOTE: Strings in _argv and environ are ANSI strings.
 */
// extern int		_argc;
// extern char**	_argv;


#include "pa_host.h"
#include "portaudio.h"
#include "dspmath.h"
#include "coeffs.h"

#include "color.c"

#include "WaveReadClass.h"

Wave_Read_Class	WRC;	// full handling for reading wave files and converting to float , mono or stereo, 16bit only!


#define SMOOTH_BINS
#define SMOOTH_K    0.333333

// Radio Frequency FFT buffers:
float gRF_fltFftBufRe[FFT_SIZE_IN];
float gRF_fltFftBufIm[FFT_SIZE_IN];
float gRF_fltPlotFftV[NFFTPLOTPOINTS];
volatile BOOL gRF_qGUIFinishedPlotting = TRUE;

 
// Audio Frequency FFT buffers:	/RFR

#define	SPECTRUM_RF	1
#define	SPECTRUM_AF	2

volatile int spectrum_channels = (SPECTRUM_RF | SPECTRUM_AF); 	// at start: both spectra displays are enabled

float Fft_Window_Permanent[FFT_SIZE_IN]; 	// window function, only 1 time computed after start
float gAF_fltFftBufRe[FFT_SIZE_IN];
float gAF_fltFftBufIm[FFT_SIZE_IN];
float gAF_fltPlotFftV[NFFTPLOTPOINTS];
volatile BOOL gAF_qGUIFinishedPlotting = TRUE;

int afpix1,afpix2 ; // fft x-range for SNR estimation, set in FilterIQ_Set() in coeffs.c
double afmin_dB, afmax_dB, afdelta_dB ;
int afmax_pix ;
// ----------------------------------------------------




#define MAXOUTPUTGAINSTEP	30

typedef struct gain_control_struct
{	const char *  text;
	float	value;		
} gain_control_type;

gain_control_type Gain_Tab[ MAXOUTPUTGAINSTEP+1] = {
{"-20 dB", 0.100 },	 // 00
{"-16 dB", 0.158 },  // 01
{"-13 dB", 0.224 },  // 02
{"-10 dB", 0.316 },  // 03
{"- 6 dB", 0.500 },  // 04
{"- 3 dB", 0.708 },  // 05
{"  0 dB", 1.000 },	 // 06 <-- init
{"+ 3 dB", 1.414 },  // 07
{"+ 6 dB", 2.000 },  // 08
{"+10 dB", 3.162 },  // 09
{"+13 dB", 4.467 },  // 10
{"+16 dB", 6.310 },  // 11
{"+20 dB", 10.00 },  // 12
{"+23 dB", 14.14 },  // 13
{"+26 dB", 20.00 },  // 14
{"+30 dB", 31.62 },  // 15
{"+33 dB", 44.67 },  // 16
{"+36 dB", 63.10 },  // 17
{"+40 dB", 100.0 },  // 18
{"+43 dB", 141.4 },  // 19
{"+46 dB", 200.0 },  // 20
{"+50 dB", 316.2 },  // 21
{"+53 dB", 446.7 },  // 22
{"+56 dB", 631.0 },  // 23
{"+60 dB", 1000.0},  // 24

{"+63 dB", 1414.0 },  // 25
{"+66 dB", 2000.0 },  // 26
{"+70 dB", 3162.0 },  // 27
{"+73 dB", 4467.0 },  // 28
{"+76 dB", 6310.0 },  // 29
{"+80 dB", 10000.0}   // 30
};  

volatile int time_format  		= 0;		// =0: UTC or =1: local time        
volatile int g_iOutputGainStep  = 6;		// start with 0 dB output gain
volatile float g_rOutputGainFactor = 1.0; 	// =Gain_Tab[g_iOutputGainStep].value ;


volatile BOOL morse_spur = true;		// audio R channel: envelope if stereo output
volatile int no_retry=0;				// if 1: no write to disk is possible

volatile int gAF_satflag= 0;

volatile int q_OverLoad = 0;
PortAudioStream *g_PAStream;	// a void pointer! we see no internals here!
int filter_case = filt_CW300;   // enum in coeffs.h
int mph_flag=0;					// minimum-phase flag 
int g_qShowCpuLoad = 0;
volatile float g_rCpuLoad;		// lowpass filtered for display
volatile float g_rCpuLoad_act;

int bothbands_RF = 0;	// stereo input trace, with 'b' enabled
int bothbands_AF = 0;	// default USB, else USB+LSB as stereo, toggled with 's'
int left_channel = 1;	// set ADC input channel left=1 or right=0, 2: L+R, 3:L-R
int	mute_flag = 0;		// 1= muted output

// Envelope_Detector variables: -------------------->>
float Uc_DCF77 = 0.0;
float tauRC_DCF77 = 0.0;
float dc_DCF77 = 0.0;

float Envelope_Detector ( float U )	// input voltage is U, output of double rectified wave with RC integration
{
	float x;

	x = fabs( U);	
				 		
	Uc_DCF77 = (1-tauRC_DCF77)*x + tauRC_DCF77 * Uc_DCF77 ;	// Lowpass RC filter as Integrator

	return 4* Uc_DCF77;		
}

//-------------------------------------------------


double actual_Fs = 0.0;

double	Get_SampleRate_IN(void) 
{ return (double) actual_Fs; }

double	Get_SampleRate_OUT(void) 
{ return (double) (AF_SCALE*(FS_BASE/4)) ; }	// independant of FS ! always 11kHz or 12kHz

#include	"wave2disk.h"

#if	MORSE_SEND
	#include	"morse.c"

	float SAQ_Center_Freq = 17200.0;	// Hz
	float SAQ_Low_Freq ; //= SAQ_Center_Freq - 2000;	// Hz, for lower sideband
 
	float SAQ_Center_delta ; //= (SAQ_Center_Freq * 2.0 * C_PI / FS);
	float SAQ_Low_delta    ; //= (SAQ_Low_Freq * 2.0 * C_PI / FS);

	float SAQ_Center_accu = 0.0;
	float SAQ_Low_accu    = 0.0;
#endif	// #if	MORSE_SEND


int		Framesize = 0;		// initially invalid

int		Get_framelength(void) { return Framesize; }	// used for modulo block construction

// Record Timer scheduler controls: ----------------------------------------------

#ifndef  u32
#define u32	unsigned long
#endif

#define RECTIMER_WAITS			1
#define RECTIMER_RECORD_ACTIV	2
#define RECTIMER_ENDS			3


volatile int RF_record_timer_status = 0;	// 0= not init
volatile u32 RF_record_timer_ms_wait = 0;	
volatile u32 RF_record_timer_ms_dur  = 0;
u32 RF_record_timer_ms_zerolatch;			// for diff 
int RF_record_timer_channels;


volatile int AF_record_timer_status = 0;	// 0= not init
volatile u32 AF_record_timer_ms_wait = 0;	
volatile u32 AF_record_timer_ms_dur  = 0;
u32 AF_record_timer_ms_zerolatch;			// for diff 
int AF_record_timer_channels;

#define RF_timer_elapsed  ((u32) (timeGetTime() - RF_record_timer_ms_zerolatch ))
#define AF_timer_elapsed  ((u32) (timeGetTime() - AF_record_timer_ms_zerolatch ))

void init_record_timer_RF (u32 wait_time, u32 dur_time, int channels )
{
	RF_record_timer_channels = channels;
	RF_record_timer_ms_wait  = 60000 * wait_time;	// counts in milli-sec
	RF_record_timer_ms_dur   = 60000 * dur_time;	

	RF_record_timer_ms_zerolatch = timeGetTime();	// counting milli-sec after Windows-OS last start
	RF_record_timer_status = RECTIMER_WAITS;
		
}

void init_record_timer_AF (u32 wait_time, u32 dur_time, int channels )
{
	AF_record_timer_channels = channels;
	AF_record_timer_ms_wait  = 60000 * wait_time;	// counts in milli-sec
	AF_record_timer_ms_dur   = 60000 * dur_time;	

	AF_record_timer_ms_zerolatch = timeGetTime();	// counting milli-sec after Windows-OS last start
	AF_record_timer_status = RECTIMER_WAITS;	
}	

//------------------------------------------------------------------------------


/*------------------------------------------------------------------------------
 *
 *      Audio stream callback function
 *
 *      All audio processing is done in this function!
 *
 *      (No need to mess with a separate worker thread since this is
 *      already taken care of by PortAudio!)
 */

double rms_val_usb = 1.0;	// audio out in 11kHz summed to rms, upper sideband, included with gain
double rms_val_lsb = 1.0;	// audio out in 11kHz summed to rms, lower sideband, included with gain
int	   sample24bits = 0;	// lower 8 bits

Ptr_Filter_typ	pDEC_FILTER;	// Pointer to Decimation    filter
Ptr_Filter_typ	pINT_FILTER;	// Pointer to Interpolation filter


Ptr_Filter_typ	pUSR_FILTER_I;	// Pointer to Quadrature Filter for selectivity, I-phase
Ptr_Filter_typ	pUSR_FILTER_Q;	// Pointer to Quadrature Filter for selectivity, Q-phase



int vco_case_unit = 2;		//0=1Hz, 1=10Hz, 2=100Hz, 3=1kHz, 4=10kHz
float vco_hz_incr   = 100.0;

int shift_case_unit = 1;	//0=1Hz, 1=10Hz, 2=100Hz, 3=1kHz, (4=10kHz)
float shift_hz_incr   = 10.0; 
float shift_hz_requested = 750.0;	// only used in CW modes
float Hz_SH_min;
float Hz_SH_max;
float Hz_TW;		// transition bandwidth of CW filters
float Hz_DecBW;		// Decimation filter bandwidth

double Hz_VFO           = 0.0;	// use finest resolution, not float here !
double Hz_VFO_PhaseIncr = 0.0;
double Hz_VFO_PhaseAccu = 0.0;

// Filter characteristics , set by FilterIQ_Set() :
float Hz_SH;		// shift offset, passband middle BW/2 to VFO : SH = f1 + BW/2 
float Hz_BW;		// width of passband BW
float Hz_f1;		// lower corner passband after shift
float Hz_f2;		// upper corner passband after shift, f1 < f2= f1+BW, for upper sideband.
					// used for USB Mode only:
float Hz_VFO_min;	// = 0.0
float Hz_VFO_max;	// =(0.5*FS - Hz_f2) // Hz_VFO + f2 <= NYQ

	
static int AudioCallback (void *inputBuffer,
                          void *outputBuffer,
                          unsigned long framesPerBuffer,
                          PaTimestamp outTime,
                          void *userData)
{
  unsigned long frameCnt;
  float *out = (float *) outputBuffer;
  float *in = (float *) inputBuffer;
  float leftInput, rightInput, mixerInput ;

  float rI44k, rQ44k;
  float rI11k, rQ11k;
  float rTmp;
  float re,im;
  int i;

	float rUSB ;	// upper sideband signal in FS/4 (11kHz)
	float rLSB ;	// lower sideband signal in FS/4 (11kHz)
	
//  static float Hz_VFO_PhaseAccu;

  static int iRF_FftBufIdxIn = 0;	// radio fft
  static int iAF_FftBufIdxIn = 0;	// audio fft

  
  static unsigned int ui44kSampleCounter = 0;	// phase 0 .. 3 of decimation factor = R = 4

  static unsigned int iDecimatorIdxIn = 0;
  static float rDecimatorQueueI[ DECI_DATALEN ];	// modulo 2^n addressing !
  static float rDecimatorQueueQ[ DECI_DATALEN ];	// modulo 2^n addressing !

  static unsigned int iMainFilterIdxIn = 0;
  static float rMainFilterQueueI[FIR_LARGEST_POW2];	// modulo 2^n addressing !
  static float rMainFilterQueueQ[FIR_LARGEST_POW2];	// modulo 2^n addressing !

  static unsigned int iInterpolatorIdxIn = 0;
  static float rInterpolatorQueueI[ INTP_DATALEN ];	// modulo 2^n addressing !
  static float rInterpolatorQueueQ[ INTP_DATALEN ];	// modulo 2^n addressing !

  // Argument inputBuffer may be NULL during start-up so...

  if (inputBuffer == NULL) return 0;

	if ( Framesize == 0)				// the first valid callback
	{
		Framesize = framesPerBuffer;	// coming from audio device
		
		WRC.Set_Wave_Read_Parameters( Framesize, actual_Fs );	// supply actual parameters for reading from wav files

		Uc_DCF77 = 0.0;		// deload RC integrator
		dc_DCF77 = 0.0;
		tauRC_DCF77 = exp( log(0.1)/( 0.010 * FS) );	// RC time constant is: 10 ms decaying from Peak to 0.1 * Peak
		morse_spur = true; 			
	}

	if ( WRC.file_activ )	// look for disk input
	{	
		if (WRC.play_stop_request==1) WRC.play_stop_request= 2;	// Ack and no further output here 
		else
		if (WRC.play_stop_request <= 0 )
		{		
			//	float * fptr = WRC.Get_Play_Block();	// stereo block from disk
	
			in = WRC.Get_Play_Block();	// pause or valid stereo diskblock, overwrite audio input here, dirty trick!

			// but we can do other things here, par example modulation etc ...
			// AM sender ?
		}					
	}


	
#if	MORSE_SEND		//	/RFR added 05.01.2008 : overwrite input
{	float * src = (float *) inputBuffer;
	int ii;
	
  	for ( ii = 0; ii < framesPerBuffer; ii++)
  	{	
	
		leftInput = 0.0;
				
		// make CW modulation :
    	SAQ_Center_accu += SAQ_Center_delta;	// USB
    	if (SAQ_Center_accu > (2.0 * C_PI)) SAQ_Center_accu -= (2.0 * C_PI);
    	  
    	SAQ_Low_accu += SAQ_Low_delta;			// LSB
    	if (SAQ_Low_accu > (2.0 * C_PI)) SAQ_Low_accu -= (2.0 * C_PI);
    	
//		add noise to input:
#if NOISE_ADD
		leftInput += (1.82*0.10) * fRAND16();	// return max = 1.0 , 10dB S/N in BW=1kHz
#endif    	
			
		keyed_amp = get_morse_modulation ( &A_morse); 
		leftInput += 0.10 *keyed_amp * cos( SAQ_Low_accu);		// LSB modulation -20dB
    	
		keyed_amp = get_morse_modulation ( &B_morse); 
		leftInput += 0.10 *keyed_amp * cos( SAQ_Center_accu);	// USB modulation
    	
		*src++ = leftInput;
		*src++ = leftInput;
	
	}	// ii..	
	
	Record_WaveStereo_RF ( (float *) inputBuffer, framesPerBuffer);	// possible write to disk here
	
}
#endif	// #if	MORSE_SEND

  // Read input buffer, process data, and fill output buffer.

#if	MORSE_SEND==0
	Record_WaveStereo_RF ( (float *) inputBuffer, framesPerBuffer);	// possible write to disk here
#endif

	double rms_sum_usb = 0.0;	// audio out in 11kHz summed to rms, upper sideband, included with gain
	double rms_sum_lsb = 0.0;	// audio out in 11kHz summed to rms, lower sideband, included with gain
	
		
  for (frameCnt = 0; frameCnt < framesPerBuffer; frameCnt++)
  {
    // Get interleaved soundcard samples from input buffer
    
    leftInput  = *in++;
    rightInput = *in++;
	    	    
	if      (left_channel==0){ mixerInput= rightInput;}
    else if (left_channel==1){ mixerInput= leftInput; }
    else if (left_channel==2){ mixerInput= leftInput + rightInput; }    // 2: sum signal
    else                     { mixerInput= leftInput - rightInput; }    // 3: difference signal

    // Check peak signal level

    rTmp = mixerInput;
    if (rTmp < 0) rTmp = -rTmp;	 // faster than calling abs()?      
    if (rTmp > 0.994260074) q_OverLoad = 5; // 0.05 dB below full-scale (1.0), -188 LSBs(16bit), 250ms display on
      

// test bitwide
#if 1
	unsigned long sampleint = (unsigned long) ( 16777216.0 * (double)mixerInput ); 	// 24 bit left shift
	sampleint = sampleint & 0x000000FFL;
//	sample24bits = sample24bits | sampleint ;
	if (( sampleint !=0L)&&(sampleint!=255L))	sample24bits++;
#endif

//

    // Run local oscillator (NCO)

    Hz_VFO_PhaseAccu += Hz_VFO_PhaseIncr;
    if (Hz_VFO_PhaseAccu > (2.0 * C_PI))
      Hz_VFO_PhaseAccu -= (2.0 * C_PI);

    // Half complex mixer

    rI44k = mixerInput * cos (Hz_VFO_PhaseAccu);
    rQ44k = mixerInput * sin (Hz_VFO_PhaseAccu);

//########################################
// FAKED SIGNAL FOR FFT Y SCALING TESTS:
//    leftInput = cos (Hz_VFO_PhaseAccu); // amplitude 1.0
//########################################

    // Decimation filter, R = 4, nTaps = 64 (hardcoded... ;-)

/*

	DECI_TAPS = 64
	DECI_FACT = 4
	DECI_DATALEN = 256	// power of 2 >= maximal DECI_TAPS



typedef struct Filter_typ_tag 
{
	int  	IP_Factor;				// interpolation factor DECI_FACT;			
    int  	All_Taps;				// number of used taps  DECI_TAPS;          
    float	FilterCoeffs[1500];		//      
} Filter_typ, * Ptr_Filter_typ ;

Ptr_Filter_typ	pDEC_FILTER;
 
*/

	iDecimatorIdxIn = (iDecimatorIdxIn-1) & (DECI_DATALEN-1); // fill buffer backwards (that convolution stuff...)
	
    rDecimatorQueueI[iDecimatorIdxIn] = rI44k;
    rDecimatorQueueQ[iDecimatorIdxIn] = rQ44k;

    // Decimate Fs and do the heavy stuff at lower speed

    if (++ui44kSampleCounter >= pDEC_FILTER->IP_Factor)  // decimation factor = R = 4
    {
      ui44kSampleCounter = 0;

      // Fs = 11025 Hz code goes here, first decimation filter:

      rI11k = rQ11k = 0.0;          // clear MAC accumulators
      
      float * filtcoeff = & (pDEC_FILTER->FilterCoeffs[0]); // the actual filter coeffs for decimation pDEC_FILTER->FilterCoeffs[0];
      int imax = pDEC_FILTER->All_Taps;
      unsigned int IdxIn = iDecimatorIdxIn;
      
      for (i = 0; i < imax; i++)
      {
        rTmp   = *filtcoeff++;
        rI11k += rDecimatorQueueI[IdxIn] * rTmp;
        rQ11k += rDecimatorQueueQ[IdxIn] * rTmp;
		IdxIn = (IdxIn+1) & (DECI_DATALEN-1);
      }

      rI11k *= 2.0;      // normalize SSB conversion loss
      rQ11k *= 2.0;

      // rI11k and rQ11k now contains the downsampled complex signal
      // ready for processing at Fs = 11025 Hz. Passband = +/-3000Hz
      // with transition bands out to +/-5512.5Hz (stopband).

      // Main selectivity filters

      {          
		iMainFilterIdxIn = ( iMainFilterIdxIn -1) & ( FIR_LARGEST_POW2 -1);
		          
        rMainFilterQueueI[iMainFilterIdxIn] = rI11k;
        rMainFilterQueueQ[iMainFilterIdxIn] = rQ11k;

        rI11k = rQ11k = 0.0;          			// clear MAC accumulators
        unsigned int IdxIn = iMainFilterIdxIn;
        
        for (i = 0; i < pUSR_FILTER_I->All_Taps ; i++)	// same tap count for _Q !!!
        {
          rI11k += rMainFilterQueueI[IdxIn] * pUSR_FILTER_I->FilterCoeffs[i];
          rQ11k += rMainFilterQueueQ[IdxIn] * pUSR_FILTER_Q->FilterCoeffs[i];         
          IdxIn = (IdxIn +1) & ( FIR_LARGEST_POW2 -1);
        }

      }
      

      // "Summing point" of the "Phasing Method Receiver"
      

	{	static float rUSB_LSB[2];

		float UL = g_rOutputGainFactor *(rI11k + rQ11k);	// USB in 11kHz on Left  Channel
		float UR = g_rOutputGainFactor *(rI11k - rQ11k);	// LSB in 11kHz on Right Channel
		
		if ( morse_spur) 
		{	UR = Envelope_Detector ( UL ); }	// Envelope only output in stereo file out

	
		rUSB_LSB[0] = UL;	// Mono or Left = USB with gain
		rUSB_LSB[1] = UR;	//         Right= LSB with gain (or DCF77 impulses if locked)
		
		Record_WaveStereo_AF ( &rUSB_LSB[0], 1);	// possible write to disk here, mono or stereo in 11kHz, or nothing
	}

     
	rUSB = rI11k + rQ11k;				// upper sideband signal in 11kHz

	
		     
	if ( bothbands_AF) rLSB = rI11k - rQ11k;	// in stereo-file-out: lower sideband signal to Audio 11kHz ??? NOT GOOD!
	else			rLSB = rUSB;			// normal only USB used for both Audio channels 

	rms_sum_usb += rUSB * rUSB;		// integrate squared sum Upper Side Band w/o gain
	rms_sum_lsb += rLSB * rLSB;		// integrate squared sum Lower Side Band w/o gain	      


//      rI11k += rQ11k;       // select USB (subtract for LSB)
//      rQ11k = rI11k;        // same signal to both ears (mono output)

//----------------------------------------------------------------------------- /RFR 24.04.2012
//if ( spectrum_channels & SPECTRUM_AF )    // AF spectrum enabled ?
if (( spectrum_channels & SPECTRUM_AF )||(g_qShowCpuLoad))  // 14.01.2013
{
    // Do FFT of  output signal ( Audio Frequencies) for the full spectrum view in [0 ... FS/2/4 ]
    iAF_FftBufIdxIn &= (FFT_SIZE_IN-1);
    
	float asat = rUSB * g_rOutputGainFactor; // audio USB
	
	if ( asat >= 1.0) { asat= 32767.0/32768; gAF_satflag= 5; }	// with saturation (16bit) for spectrum distortion show
	else if ( asat < -1.0) { asat= -1.0; gAF_satflag= 5; }	
    
    gAF_fltFftBufRe[iAF_FftBufIdxIn++] = asat;

    if (iAF_FftBufIdxIn == FFT_SIZE_IN)
    { // time to do the next FFT :

      iAF_FftBufIdxIn = 0;   // don't do it again until buffer is filled

	  dsp_multiply_fftwindow_table ( gAF_fltFftBufRe, Fft_Window_Permanent, FFT_SIZE_IN);
      //dspmath_MultiplyHanningWindow (gAF_fltFftBufRe, FFT_SIZE_IN);
      //dspmath_MultiplyHammingWindow (gAF_fltFftBufRe, FFT_SIZE_IN);
      //dspmath_MultiplyBlackmanWindow (gAF_fltFftBufRe, FFT_SIZE_IN);

      dspmath_CalcRealFft (FFT_SIZE_IN,  gAF_fltFftBufRe, gAF_fltFftBufIm);

      if (gAF_qGUIFinishedPlotting)
      {
        gAF_qGUIFinishedPlotting = FALSE;

        // Note: Less than half of all computed FFTs will be
        // displayed because of the 50ms timer tick but who cares...

        for (i = 0; i < NFFTPLOTPOINTS; ++i)
        {
          re =  gAF_fltFftBufRe[i];
          im =  gAF_fltFftBufIm[i];
          // A pure input sin wave ... Asin(wt)... will produce an fft output
          //   peak of (N*A/4)^2  where N is FFT_SIZE .
          // Notes:
          //  - Leave the time-consuming logarithm for the display thread !
          //  - This 'power'-proportional thingy depends on the FFT-size !
          //  - The above formula is only valid for HANN window, not here..

#ifdef SMOOTH_BINS
//          Run a simple first order "RC" filter on each
//          frequency bin for smoothing:
//            out(N) = out(N-1) + K*( in(N) - out(N-1) )
//          where the R*C time constant is proportional to K/FS.
//          A sliding average would have been better (probably) but this
//          is so simple to implement:

          rTmp = SMOOTH_K * (sqrt (re*re + im*im) - gAF_fltPlotFftV[i]);
          gAF_fltPlotFftV[i] += rTmp;
#else
          gAF_fltPlotFftV[i] =  sqrt (re*re + im*im);
#endif
        }
        
        
        
      } // end if( gAF_qGUIFinishedPlotting )
      
    } // end if < time to calculate another FFT >
    
}	// if ( spectrum_channels & SPECTRUM_AF )    // AF spectrum enabled ?	

//-----------------------------------------------------------------------------


    // Feed a stereo 11k sample to the interpolation filter
    // (two separate channels are used in case we want
    // stereo output in the future for binaural I/Q etc.)

//      iInterpolatorIdxIn--;   // backward input, remember that convolution integral...
//      if (iInterpolatorIdxIn < 0) iInterpolatorIdxIn = 15;
      
      iInterpolatorIdxIn = (iInterpolatorIdxIn-1)&(INTP_DATALEN-1); // backward input, remember that convolution integral...
      rInterpolatorQueueI[iInterpolatorIdxIn] = rUSB;	//rI11k;
      rInterpolatorQueueQ[iInterpolatorIdxIn] = rLSB;	//rQ11k;
    } // end of if (++ui44kSampleCounter >= 4)

    // Now we're back in Fs = 44100 Hz business again

    // Run interpolation filter at Fs = 44k1
    //
    // Note! The interpolator uses the same 64-tap FIR as the decimator but
    // since 3 out of 4 input samples are zero, only 16 MAC operations need
    // to be computed. This is the reason why the interpolator buffer size
    // is only 16. The coefficient set is split into 4 groups (mentally...)
    // and the filter computation is cycling through these groups, one
    // group per 44k1 sample:
    //
    // 44k1 sample instant 0 (ui44kSampleCounter == 0):
    //   - Put new 11k sample into the interpolator queue
    //   - Compute the filter using coefficients 0, 4, 8 ... 56, 60
    //
    // 44k1 sample instant 1 (ui44kSampleCounter == 1):
    //   - Compute the filter using coefficients 1, 5, 9 ... 57, 61
    //
    // 44k1 sample instant 2 (ui44kSampleCounter == 2):
    //   - Compute the filter using coefficients 2, 6, 10 ... 58, 62
    //
    // 44k1 sample instant 3 (ui44kSampleCounter == 3):
    //   - Compute the filter using coefficients 3, 7, 11 ... 59, 63
    //
    // 44k1 sample instant 4: (now it starts over again!)
    //   - Put new 11k sample into the interpolator queue
    //   - Compute the filter using coefficients 0, 4, 8 ... 56, 60
    //
    //  etc. etc.

    rI44k = rQ44k = 0.0;    // clear MAC accumulators
    i = ui44kSampleCounter; // 0..3, 0 just after a new value has been inserted
    
    float * fcoeffs = & (pINT_FILTER->FilterCoeffs[0]);
	unsigned int IdxIn = iInterpolatorIdxIn;
	    
    do  // loop from newest to oldest sample (they are stored "backwards")
    {
      rTmp 	 = fcoeffs[i];
      rI44k += rInterpolatorQueueI[IdxIn] * rTmp;
      rQ44k += rInterpolatorQueueQ[IdxIn] * rTmp;
      IdxIn  = (IdxIn+1) & ( INTP_DATALEN -1);
      i 	+= pINT_FILTER-> IP_Factor;    // 4;
    } while (i < pINT_FILTER->All_Taps ); // last i == 60, 61, 62 or 63 depending on coeff' group used

    rI44k *= pINT_FILTER-> IP_Factor;      // normalize 
    rQ44k *= pINT_FILTER-> IP_Factor;

    // Write interleaved samples to output buffer

	if (mute_flag)		// muting ?
	{	*out++ = 0.0;
		*out++ = 0.0;
	}
	else
	{
    	*out++ = rI44k * g_rOutputGainFactor;  // left
    	*out++ = rQ44k * g_rOutputGainFactor;  // right
	}
    // Do FFT of original input signal (Radio Frequencies) for the full spectrum view in [0 ... FS/2 ]

    iRF_FftBufIdxIn &= (FFT_SIZE_IN-1);
    gRF_fltFftBufRe[iRF_FftBufIdxIn++] = mixerInput; //rI44k leftInput

    if (iRF_FftBufIdxIn == FFT_SIZE_IN)
    { // time to do the next FFT :

      iRF_FftBufIdxIn = 0;   // don't do it again until buffer is filled

	  dsp_multiply_fftwindow_table ( gRF_fltFftBufRe, Fft_Window_Permanent, FFT_SIZE_IN);
      //dspmath_MultiplyHanningWindow (gRF_fltFftBufRe, FFT_SIZE_IN);
      //dspmath_MultiplyHammingWindow (gRF_fltFftBufRe, FFT_SIZE_IN);
      //dspmath_MultiplyBlackmanWindow (gRF_fltFftBufRe, FFT_SIZE_IN);

      dspmath_CalcRealFft (FFT_SIZE_IN,  gRF_fltFftBufRe, gRF_fltFftBufIm);

      if (gRF_qGUIFinishedPlotting)
      {
        gRF_qGUIFinishedPlotting = FALSE;

        // Note: Less than half of all computed FFTs will be
        // displayed because of the 50ms timer tick but who cares...

        for (i = 0; i < NFFTPLOTPOINTS; ++i)
        {
          re =  gRF_fltFftBufRe[i];
          im =  gRF_fltFftBufIm[i];
          // A pure input sin wave ... Asin(wt)... will produce an fft output
          //   peak of (N*A/4)^2  where N is FFT_SIZE .
          // Notes:
          //  - Leave the time-consuming logarithm for the display thread !
          //  - This 'power'-proportional thingy depends on the FFT-size !
          //  - The above formula is only valid for HANN window, not here..

#ifdef SMOOTH_BINS
//          Run a simple first order "RC" filter on each
//          frequency bin for smoothing:
//            out(N) = out(N-1) + K*( in(N) - out(N-1) )
//          where the R*C time constant is proportional to K/FS.
//          A sliding average would have been better (probably) but this
//          is so simple to implement:

          rTmp = SMOOTH_K * (sqrt (re*re + im*im) - gRF_fltPlotFftV[i]);
          gRF_fltPlotFftV[i] += rTmp;
#else
          gRF_fltPlotFftV[i] =  sqrt (re*re + im*im);
#endif
        }
        
        
        
      } // end if( gRF_qGUIFinishedPlotting )
      
    } // end if < time to calculate another FFT >
    
  } // end : for (frameCnt = 0; frameCnt < framesPerBuffer; frameCnt++)

	rTmp = (4*FS_SCALE)*g_rOutputGainFactor*g_rOutputGainFactor;	// 4 decimation factor, gain squared
	rTmp = rTmp*rms_sum_usb / framesPerBuffer;			// normed to max = 1.0 for square wave full scale
	if (rTmp > 1.0) rTmp = 1.0;							// gain can boost over 0 dB but display not		
	rTmp = SMOOTH_K * ( rTmp - rms_val_usb );
	rms_val_usb	+= rTmp;								// smoothed rms volume

	rTmp = (4*FS_SCALE)*g_rOutputGainFactor*g_rOutputGainFactor;	// 4 decimation factor, gain squared
	rTmp = rTmp*rms_sum_lsb / framesPerBuffer;			// normed to max = 1.0 for square wave full scale
	if (rTmp > 1.0) rTmp = 1.0;							// gain can boost over 0 dB but display not		
	rTmp = SMOOTH_K * ( rTmp - rms_val_lsb );
	rms_val_lsb	+= rTmp;								// smoothed rma volume		
		
	

//	Record_WaveStereo_AF ( (float *) outputBuffer, framesPerBuffer);	// possible write to disk here


  if (g_qShowCpuLoad)	// peak + slow desintegration by RFR 18.04.2012 
  { 
    g_rCpuLoad_act = Pa_GetCPULoad (g_PAStream);
    if (g_rCpuLoad_act > g_rCpuLoad) g_rCpuLoad = g_rCpuLoad_act; // peak value
    else 							 g_rCpuLoad = 0.98 * g_rCpuLoad + 0.02 * g_rCpuLoad_act;  // discharge

  }
  
  return 0; // a non-zero return will stop the stream
  
}	// AudioCallback


/*------------------------------------------------------------------------------
 *
 *      Reaction to WM_PAINT message
 */

#define MAINSCREEN_X	640				// The window's width INCLUDING BORDER FRAME!
#define MAINSCREEN_Y	(340+100)		// The window's heigth INCLUDING BORDER FRAME! for 25 lines
           
#define CURVEHEIGHT     221
#define CURVESTARTX     60
#define CURVESTARTY     (40+20+CURVEHEIGHT)		// make an extra line at top

#define yline(nn)	((nn*16)+4)		// y-coord of line for text top pixel, nn= 0 .. 24.

volatile int ibar_db_max = 0;       // dB bar range: hold the diff constant!
volatile int ibar_db_min = -110;
// same as float:
volatile float rbar_db_max = 0.0;
volatile float rbar_db_min = -110.0;





POINT maus;				// updated with left click
int xvfo, xvfo1,xvfo2;	// filter selected range to hi light in 0 ... 512  

///=============================================================
//	Definitions of Click,Framed Box, Text Areas:

  			        // { .left=x        , .top=y     , .right=x          , .bottom=y  }
const RECT rect_Tune = { CURVESTARTX+520, yline(5) -4, CURVESTARTX+570, yline(7) +2 };	// 2 lines for frame box  only 
const RECT rect_SAQ  = { CURVESTARTX+520, yline(5) -4, CURVESTARTX+570, yline(6) +2 };	// 1 lines for click area only
const RECT rect_DCF  = { CURVESTARTX+520, yline(6) -4, CURVESTARTX+570, yline(7) +2 };	// 1 lines for click area only

RECT rect_RF_Selection = { CURVESTARTX+0, CURVESTARTY-CURVEHEIGHT+1 , CURVESTARTX+513, CURVESTARTY  };	// spectrum area

    
const RECT rect_VFOBlock =  { CURVESTARTX+160, yline(0), CURVESTARTX+160+172, yline(3) };	// 3 line click only

const RECT rect_FilterShift=  { CURVESTARTX+8  , yline(2), CURVESTARTX+8+132  , yline(3)+4 };	// shift line click only
const RECT rect_FilterCW   =  { CURVESTARTX+8  , yline(0), CURVESTARTX+8+ 44  , yline(0)+15 };	// 1 line click only
const RECT rect_FilterSSB  =  { CURVESTARTX+8+45, yline(0), CURVESTARTX+8+45+44, yline(0)+15 };	// 1 line click only
const RECT rect_FilterAM   =  { CURVESTARTX+8+90, yline(0), CURVESTARTX+8+90+42, yline(0)+15 };	// 1 line click only



const RECT rect_Time  = { CURVESTARTX+384, yline(0)  , CURVESTARTX+568, yline(1)    };	// 1 lines for click area only
    
const RECT rect_LdB =  { CURVESTARTX-11-5-40, yline(2), CURVESTARTX-11-5+12, yline(3)+2 };	// 1 line, input channel L/R

const RECT rect_AFGain = { CURVESTARTX+412, yline(2)-4, CURVESTARTX+522, yline(3) +2 };	// 1 line AF Gain= nn dB text
const RECT rect_AFMinus= { CURVESTARTX+528, yline(2) , CURVESTARTX+544, yline(3)  };	// 1 lines for Minus 16 x 16 px
const RECT rect_AFPlus = { CURVESTARTX+552, yline(2) , CURVESTARTX+568, yline(3)  };	// 1 lines for Plus  16 x 16 px


const RECT rect_Help = { CURVESTARTX+520, yline(14)-4, CURVESTARTX+570, yline(15)+2 };	// 1 line
const RECT rect_About= { CURVESTARTX+520, yline(16)-4, CURVESTARTX+570, yline(17)+2 };	// 1 line

const RECT rect_RecRF= { CURVESTARTX+392, yline(21)  , CURVESTARTX+570, yline(22)   };	// 1 line RF recorder text
const RECT rect_RecAF= { CURVESTARTX+392, yline(22)  , CURVESTARTX+570, yline(23)   };	// 1 line AF recorder text
const RECT rect_Play = { CURVESTARTX+392, yline(23)  , CURVESTARTX+570, yline(24)   };	// 1 line Player text
const RECT rect_RecBox={ CURVESTARTX+392, yline(21)-4, CURVESTARTX+570, yline(24)+2 };	// 3 line RF+AF recorder frame only
   

const RECT rect_kHz_RF={ CURVESTARTX+524, yline(18)  , CURVESTARTX+524+48, yline(19) };	// 1 line kHz RF, mouse only    
const RECT rect_kHz_AF={ CURVESTARTX+524, yline(19)  , CURVESTARTX+524+48, yline(20) };	// 1 line kHz AF, mouse only
    
///=============================================================

void SetCursor( HDC hdc, int x1, int x2, int y , COLORREF farbe)	// horizontal line
{	
	HPEN hPen,hOldPen;	
	
	hPen 	= CreatePen (PS_SOLID, 1, farbe );
	hOldPen = (HPEN)SelectObject (hdc, hPen);  		
	
	MoveToEx (hdc, x1, y, NULL);
    LineTo   (hdc, x2, y );		// horizontal line
    	
	SelectObject (hdc, hOldPen);
	DeleteObject (hPen);				
}	// SetCursor()


void BoxFrame( HDC hdc, RECT rc , COLORREF farbe)
{	POINT q[5];
	HPEN hPen,hOldPen;
	
	q[0].x = rc.left ; q[0].y = rc.top; 
	q[1].x = rc.right; q[1].y = rc.top; 		
	q[2].x = rc.right; q[2].y = rc.bottom; 		
	q[3].x = rc.left ; q[3].y = rc.bottom; 		
	q[4].x = rc.left ; q[4].y = rc.top; 	// q[4] = q[0] closed box		
	
	hPen 	= CreatePen (PS_SOLID, 1, farbe );
	hOldPen = (HPEN)SelectObject (hdc, hPen);  		

	Polyline( hdc, q, 5);	// draw the frame of rectangle
	
	SelectObject (hdc, hOldPen);
	DeleteObject (hPen);				
}	// BoxFrame()


void BoxFrameZ( HDC hdc, RECT rc , COLORREF farbe, char z)	// used for - + box for gain control input
{	POINT q[5];
	HPEN hPen,hOldPen;
	
	q[0].x = rc.left ; q[0].y = rc.top; 
	q[1].x = rc.right; q[1].y = rc.top; 		
	q[2].x = rc.right; q[2].y = rc.bottom; 		
	q[3].x = rc.left ; q[3].y = rc.bottom; 		
	q[4].x = rc.left ; q[4].y = rc.top; 	// q[4] = q[0] closed box		
	
	hPen 	= CreatePen (PS_SOLID, 1, farbe );
	hOldPen = (HPEN)SelectObject (hdc, hPen);  		

	Polyline( hdc, q, 5);	// draw the frame of rectangle
	
// - char or + :
    MoveToEx (hdc, rc.left+ 4, (rc.top+rc.bottom)/2, NULL);
    LineTo   (hdc, rc.left+12, (rc.top+rc.bottom)/2 );			// 8 px minus
    
	if (z == '+')
	{	// + char
    MoveToEx (hdc, (rc.left+rc.right)/2, rc.top+4, NULL);
    LineTo   (hdc, (rc.left+rc.right)/2, rc.top+12 );			// 8 px plus    
	}

	
	SelectObject (hdc, hOldPen);
	DeleteObject (hPen);				
}	// BoxFrameZ()


LONG OnWmPaint (HWND hWnd)
{
  HDC hdcMem;
  HBITMAP hbmMem, hbmOld;
  HPEN hPen, hOldPen;
  PAINTSTRUCT ps;
  RECT rctClientArea;
  int y, iXLoop;
  float rPixelsPerdB;
  POINT plotPoints[NFFTPLOTPOINTS];
  int i;
  char sz80[84];
  int iNumVisibleChars;
	float rdB;
	static float rmsdB;
  	static float max_RF_fft_db;	// maximum in bandwide range
  	static int max_RF_fft_px;	// at this x-pixel coord.

  BeginPaint (hWnd, &ps);

  // Get the size of the program window's client area:
  //
  // NOTE! The coordinates returned by GetClientRect() are:
  //
  //   rctClientArea.left == rctClientArea.top == 0 (always)
  //
  //   rctClientArea.right = WIDTH of client area
  //   (rightmost visible X == rctClientArea.right-1
  //
  //   rctClientArea.bottom = HEIGHT of client area
  //   (bottommost visible Y == rctClientArea.bottom-1

  GetClientRect (hWnd, &rctClientArea);

  // Create a compatible device context (DC) for drawing off-screen:

  hdcMem = CreateCompatibleDC (((LPPAINTSTRUCT)(&ps))->hdc);

  // Create an off-screen bitmap to do the off-screen drawing on:

  hbmMem = CreateCompatibleBitmap (((LPPAINTSTRUCT)(&ps))->hdc,
                                   rctClientArea.right-rctClientArea.left,
                                   rctClientArea.bottom-rctClientArea.top);

  // Direct all drawing operations onto the off-screen bitmap:

  hbmOld = (HBITMAP) SelectObject (hdcMem, hbmMem);

  // We can now begin the drawing operations, first erase the background:

//  hbrBkGnd = CreateSolidBrush (GetSysColor(COLOR_WINDOW));
//  FillRect(hdcMem, &rctClientArea, hbrBkGnd);
//  DeleteObject(hbrBkGnd);
  FillRect (hdcMem, &rctClientArea, (HBRUSH)GetStockObject(BLACK_BRUSH));

//  SetBkMode(hdcMem, TRANSPARENT);       // alternatives: TRANSPARENT/OPAQUE
  SetBkColor (hdcMem, color_black);       // background for TextOut()

  // Draw status information above the spectrum area:
	
  SetTextAlign (hdcMem, TA_RIGHT);
  SetTextColor(hdcMem, color_VFO_text);
		
	if (filter_case <= filt_CWmax)
	{	// CW:
	  	iNumVisibleChars = sprintf (sz80, "Carrier =");  	
		TextOut (hdcMem, (rect_VFOBlock.left + rect_VFOBlock.right)/2, yline(1) , sz80, iNumVisibleChars); 
  		iNumVisibleChars = sprintf (sz80, "%6d Hz", (int)(Hz_SH+Hz_VFO));	// fixed format, 1 Hz resolution
		TextOut (hdcMem, rect_VFOBlock.right, yline(1) , sz80, iNumVisibleChars); 


  		SetTextColor(hdcMem, color_kHz_numbers);	// dark		
		iNumVisibleChars = sprintf (sz80, "VFO =");	// fixed format, 1 Hz resolution
		TextOut (hdcMem, (rect_VFOBlock.left + rect_VFOBlock.right)/2, yline(2) , sz80, iNumVisibleChars);		
		iNumVisibleChars = sprintf (sz80, "%6d Hz", (int)Hz_VFO);	// fixed format, 1 Hz resolution
		TextOut (hdcMem, rect_VFOBlock.right, yline(2) , sz80, iNumVisibleChars);			
  	}
	else
	{	// SSB + AM :
		iNumVisibleChars = sprintf (sz80, "VFO =");	// fixed format, 1 Hz resolution
		TextOut (hdcMem, (rect_VFOBlock.left + rect_VFOBlock.right)/2, yline(1) , sz80, iNumVisibleChars);
		iNumVisibleChars = sprintf (sz80, "%6d Hz", (int)Hz_VFO);	// fixed format, 1 Hz resolution
		TextOut (hdcMem, rect_VFOBlock.right, yline(1) , sz80, iNumVisibleChars);

	}
	
	int x1 = rect_VFOBlock.right  -30 - vco_case_unit*8 ;	// 1 unit,10,100,1000,10k
	int x2 = x1 + 8 ;
	SetCursor( hdcMem, x1, x2, yline(1)+15 , color_VFO_text);	// horizontal line under line 1 of text

#if MAUSTEST
{	// maus test:
	SetTextAlign (hdcMem, TA_LEFT);
	SetTextColor(hdcMem, color_white);  // white 
  	iNumVisibleChars = sprintf (sz80, "[x=%4i,y=%4i]", maus.x, maus.y);
	TextOut (hdcMem, CURVESTARTX+192, yline(23) , sz80, iNumVisibleChars);	
}
#endif


	
	SetTextAlign (hdcMem, TA_CENTER);
//	SetTextColor(hdcMem, color_filterBW_text ); 

	SetTextColor(hdcMem,(filter_case <= filt_CWmax)? color_filterBW_text : color_help_text);
	iNumVisibleChars = sprintf (sz80, "CW");      
	TextOut (hdcMem, (rect_FilterCW.left+rect_FilterCW.right)/2, yline(0) , sz80, iNumVisibleChars);

	SetTextColor(hdcMem,(filter_case == filt_SSB)? color_filterBW_text : color_help_text);
	iNumVisibleChars = sprintf (sz80, "SSB");      
	TextOut (hdcMem, (rect_FilterSSB.left+rect_FilterSSB.right)/2, yline(0) , sz80, iNumVisibleChars);

	SetTextColor(hdcMem,(filter_case == filt_AM)? color_filterBW_text : color_help_text);
	iNumVisibleChars = sprintf (sz80, "AM");      
	TextOut (hdcMem, (rect_FilterAM.left+rect_FilterAM.right)/2, yline(0) , sz80, iNumVisibleChars);	
	
	
	if (mph_flag)
	{
	BoxFrame (hdcMem, rect_FilterCW , 0x00FFFFFFL );	// white if minimum-phase filters	
	BoxFrame (hdcMem, rect_FilterSSB ,0x00FFFFFFL );	
	BoxFrame (hdcMem, rect_FilterAM , 0x00FFFFFFL );			
	}
	else
	{
	BoxFrame (hdcMem, rect_FilterCW ,color_help_box );	
	BoxFrame (hdcMem, rect_FilterSSB ,color_help_box );	
	BoxFrame (hdcMem, rect_FilterAM ,color_help_box );	
	}
		 
		
	SetTextAlign (hdcMem, TA_LEFT);
	SetTextColor(hdcMem, color_filterBW_text );  
	iNumVisibleChars = sprintf (sz80, "B.Width   =");	   
	TextOut (hdcMem, rect_FilterShift.left, yline(1) , sz80, iNumVisibleChars);

	SetTextAlign (hdcMem, TA_RIGHT);
	iNumVisibleChars = sprintf (sz80, "%4d Hz", (int) Hz_BW );	   
	TextOut (hdcMem, rect_FilterShift.right, yline(1) , sz80, iNumVisibleChars);
	

	if (filter_case <= filt_CWmax)	// only for CW:
	{ 	SetTextAlign (hdcMem, TA_LEFT);
		iNumVisibleChars = sprintf (sz80, "CW  Shift =");	// fixed format, 1 Hz resolution
		TextOut (hdcMem, rect_FilterShift.left, yline(2) , sz80, iNumVisibleChars);

		SetTextAlign (hdcMem, TA_RIGHT);
		iNumVisibleChars = sprintf (sz80, "%4d Hz", (int) shift_hz_requested);	   
		TextOut (hdcMem, rect_FilterShift.right, yline(2) , sz80, iNumVisibleChars);

		int x1 = rect_FilterShift.right  -30 - shift_case_unit*8 ;	// 1 unit,10,100,1000,10k
		int x2 = x1 + 8 ;
		SetCursor( hdcMem, x1, x2, yline(2)+15 , color_filterBW_text);	// horizontal line under line 2 of text
			
	}


  	SetTextAlign (hdcMem, TA_LEFT);   
  	 
	if (mute_flag)		// muting ?
	{	SetTextColor(hdcMem, color_pure_red);	// full red
		iNumVisibleChars = sprintf (sz80, "M U T E (%s)", Gain_Tab[g_iOutputGainStep].text ); 
	}
	else
	{	SetTextColor(hdcMem, color_gain_text);
  		iNumVisibleChars = sprintf (sz80, "AF Gain= %s", Gain_Tab[g_iOutputGainStep].text ); 
	}                              

	TextOut (hdcMem, rect_AFGain.left, yline(2) , sz80, iNumVisibleChars);

  // Draw [-] and [+] buttons for output gain control

	if (g_iOutputGainStep == 0)
		BoxFrameZ( hdcMem, rect_AFMinus , color_gain_inactiv, '-');
	else
		BoxFrameZ( hdcMem, rect_AFMinus , color_gain_text, '-');

  if (g_iOutputGainStep == MAXOUTPUTGAINSTEP)
		BoxFrameZ( hdcMem, rect_AFPlus , color_gain_inactiv, '+');
	else
		BoxFrameZ( hdcMem, rect_AFPlus , color_gain_text, '+');  
  
  // Draw [SAQ], [HELP] and [ABOUT] buttons

  // yes, this is messy, and there are hardcoded coordinates in the
  // WM_LBUTTONDOWN message case too... will do it in a better organized
  // way later...
  // I (=RFR) have tried this....

	SetTextAlign (hdcMem, TA_CENTER);
	SetTextColor(hdcMem, color_help_text);	// light blue
  				
	BoxFrame (hdcMem, rect_Tune ,color_help_box );	// dark blue	
	
if (FS < 176400.0)
{
	TextOut (hdcMem, (rect_Tune.left+rect_Tune.right)/2, yline(5) , "Tune", 4);  
	TextOut (hdcMem, (rect_Tune.left+rect_Tune.right)/2, yline(6) , "S A Q", 5);  
}
else
{
	TextOut (hdcMem, (rect_Tune.left+rect_Tune.right)/2, yline(5) , "S A Q", 5); 
	TextOut (hdcMem, (rect_Tune.left+rect_Tune.right)/2, yline(6) , "DCF77", 5); 
}

	BoxFrame (hdcMem, rect_Help ,color_help_box );	
	TextOut (hdcMem, (rect_Help.left+rect_Help.right)/2, yline(14) , "Help", 4);  
	
	BoxFrame (hdcMem, rect_About ,color_help_box );	
	TextOut (hdcMem, (rect_About.left+rect_About.right)/2, yline(16) , "About", 5);  


      

{
	BoxFrame (hdcMem, rect_RecBox ,color_help_box );	
	char c;
  	
	if ( Record_Status_RF())  
	{						
		if (bothbands_RF)	SetTextColor(hdcMem,color_tapetime_stereo);	// stereo activ
		else				SetTextColor(hdcMem,color_tapetime_mono);	// mono activ 		
	}	
	else   					 	
	{	
		if (no_retry )	SetTextColor(hdcMem, color_status_text );	// invalid
		else			SetTextColor(hdcMem, color_spectrum );		// idle
	}	
	
	SetTextAlign (hdcMem, TA_LEFT);
	if (bothbands_RF) c= 'b'; else c= 'i';	
	if ( Record_Status_RF())
			iNumVisibleChars = sprintf (sz80, "Stop Rec RF (i/b)%c ",c );
	else	iNumVisibleChars = sprintf (sz80, "Record  RF (i/b)%c ",c );
	TextOut (hdcMem, rect_RecRF.left+8 , yline(21) , sz80, iNumVisibleChars);  	
    SetTextAlign (hdcMem, TA_RIGHT);
    iNumVisibleChars = sprintf (sz80, "%s", Get_Record_TapeTime_RF() );
	TextOut (hdcMem, rect_RecRF.right-4, yline(21) , sz80, iNumVisibleChars);  
		
	if ( Record_Status_AF())   
			SetTextColor(hdcMem, bothbands_AF ? color_tapetime_stereo : color_tapetime_mono);			
	else   	
	{	
		if (no_retry )	SetTextColor(hdcMem, color_status_text );	// invalid
		else			SetTextColor(hdcMem, color_tapetime_idle);	// idle 
	}
	
	c = bothbands_AF ? 's' : 'o';	   
	
	SetTextAlign (hdcMem, TA_LEFT);
	if ( Record_Status_AF())
			iNumVisibleChars = sprintf (sz80, "Stop Rec AF (o/s)%c ",c );
	else	iNumVisibleChars = sprintf (sz80, "Record  AF (o/s)%c ",c );
	TextOut (hdcMem, rect_RecAF.left+8 , yline(22) , sz80, iNumVisibleChars);  	
    SetTextAlign (hdcMem, TA_RIGHT);
	iNumVisibleChars = sprintf (sz80, "%s", Get_Record_TapeTime_AF() );
	TextOut (hdcMem, rect_RecAF.right-4, yline(22) , sz80, iNumVisibleChars);   

// Player:
    SetTextColor(hdcMem, WRC.file_activ ? color_tapetime_mono : color_tapetime_idle);    
	SetTextAlign (hdcMem, TA_LEFT);
	if (WRC.file_activ) iNumVisibleChars = sprintf (sz80, "Stop File (f) " );
	else				iNumVisibleChars = sprintf (sz80, "Play File (f) " );
	TextOut (hdcMem, rect_Play.left+8 , yline(23) , sz80, iNumVisibleChars);  	
	
    SetTextAlign (hdcMem, TA_RIGHT);   
	iNumVisibleChars = sprintf (sz80, "%s", WRC.Get_Tapetime() );
	TextOut (hdcMem, rect_Play.right-4, yline(23) , sz80, iNumVisibleChars);   	

}  

  // Draw vertical dB labels and ticks:

  SetTextAlign (hdcMem, TA_RIGHT);
  SetTextColor(hdcMem, color_dB_numbers);
  hPen = CreatePen (PS_SOLID, 1, color_dB_ticks);
  hOldPen = (HPEN)SelectObject (hdcMem, hPen);
    for (i = 0; i <= 11; i++)
    {
      iNumVisibleChars = sprintf (sz80, "%d", ibar_db_max -10*i);   // 08.01.2014
      TextOut (hdcMem, CURVESTARTX-11-5, CURVESTARTY-CURVEHEIGHT+1-8+20*i, sz80, iNumVisibleChars);
      MoveToEx (hdcMem, CURVESTARTX-10-5, CURVESTARTY-CURVEHEIGHT+1+20*i, NULL);
      LineTo (hdcMem, CURVESTARTX-1-5, CURVESTARTY-CURVEHEIGHT+1+20*i);
    }
    SetTextColor(hdcMem, color_dB_unit);
    
//    if ((WRC.file_activ)&&(!WRC.stereo_flag))
//    	TextOut (hdcMem, rect_LdB.right, rect_LdB.top , "M [dB]", 6);   //08.01.2014 removed
//    else
    {
    if (left_channel==1)
    	TextOut (hdcMem, rect_LdB.right, rect_LdB.top , "L [dB]", 6);
    else if (left_channel==0)
    	TextOut (hdcMem, rect_LdB.right, rect_LdB.top , "R [dB]", 6);
    else if (left_channel==2)
    	TextOut (hdcMem, rect_LdB.right, rect_LdB.top , "L+R[dB]", 7);
    else /*if (left_channel==3) */
    	TextOut (hdcMem, rect_LdB.right, rect_LdB.top , "L-R[dB]", 7);

	}

    // Draw horizontal kHz labels and ticks:

    SetTextColor(hdcMem, color_kHz_numbers);
    SetTextAlign (hdcMem, TA_CENTER);
    for (i = 0; i <= MMSTEPS; i++)		// 28.05.2008
    {
      if ( spectrum_channels & SPECTRUM_RF )
      {	
      iNumVisibleChars = sprintf (sz80, "%d", (int)(i *FS_SCALE));
      TextOut (hdcMem, CURVESTARTX+kHzXOffs[i], yline(18) /* CURVESTARTY+11+5 */, sz80, iNumVisibleChars); // RF kHz line
	  }
	  
      MoveToEx (hdcMem, CURVESTARTX+kHzXOffs[i], CURVESTARTY+2+5, NULL); // major ticks
      LineTo (hdcMem, CURVESTARTX+kHzXOffs[i], CURVESTARTY+11+5);
      
if ( spectrum_channels & SPECTRUM_AF )    // AF spectrum enabled ?     
{	BOOL print = TRUE;
      
    SetTextColor(hdcMem, color_help_text);	// not same as for AF fft

	switch ( AF_SCALE)
	{
		case 1: if (i==0)	iNumVisibleChars = sprintf (sz80, "%d", 0);
				else		iNumVisibleChars = sprintf (sz80, "%4.1f", (float)(i*0.25));
				print = ((i & 1L)==0);	// only even
				break;
				
		case 2:	if (i==0)	iNumVisibleChars = sprintf (sz80, "%d", 0);
				else		iNumVisibleChars = sprintf (sz80, "%d", i/2);
				print = ((i & 1L)==0);	// only even		
				break;
				
		case 4:
		default:
				iNumVisibleChars = sprintf (sz80, "%d", i);		// integers as kHz
				break;		
	}
	
	if (print) TextOut (hdcMem, CURVESTARTX+kHzXOffs[i], yline(19),  sz80, iNumVisibleChars);	// AF kHz line


    SetTextColor(hdcMem, color_kHz_numbers);	// switch back to RF color
    
}	// if ( spectrum_channels & SPECTRUM_AF )    // AF spectrum enabled ?


    }	// for all major ticks

    SetTextAlign (hdcMem, TA_LEFT);         	

	if ( spectrum_channels & SPECTRUM_AF )	SetTextColor(hdcMem, color_filterBW_text);	// same as for AF fft
	else									SetTextColor(hdcMem, color_help_text);	// not same as for AF fft
    TextOut (hdcMem, rect_kHz_AF.left, yline(19) , "kHz AF", 6);    

	if ( spectrum_channels & SPECTRUM_RF )	SetTextColor(hdcMem, color_kHz_unit);    	// RF spectrum enabled ?  
	else									SetTextColor(hdcMem, color_kHz_numbers);	// dark
    TextOut (hdcMem, rect_kHz_RF.left, yline(18) , "kHz RF", 6);

    // Draw the dark yellow box around the spectrum plot area:

    MoveToEx (hdcMem, CURVESTARTX-1-5,   CURVESTARTY+1+5, NULL);
    LineTo (hdcMem, CURVESTARTX+1+512+5, CURVESTARTY+1+5);
    LineTo (hdcMem, CURVESTARTX+1+512+5, CURVESTARTY-CURVEHEIGHT+1-1-5);
    LineTo (hdcMem, CURVESTARTX-1-5,     CURVESTARTY-CURVEHEIGHT+1-1-5);
    LineTo (hdcMem, CURVESTARTX-1-5,     CURVESTARTY+1+5);
  SelectObject (hdcMem, hOldPen);
  DeleteObject (hPen);
	
	xvfo = (int)(Hz_VFO*512.0/(0.5*FS) );  // VFO x-offset in pixels

	
if ( spectrum_channels & SPECTRUM_RF )
{
  // Draw Filter passband as colored rectangle /RFR
  
	xvfo1 = (int)((Hz_VFO + Hz_f1)*512.0/(0.5*FS) );	// passband lower corner
	xvfo2 = (int)((Hz_VFO + Hz_f2)*512.0/(0.5*FS) );	// passband upper corner	
	
	if ( xvfo2 > 512) xvfo2 = 512;	
	if ( xvfo1 < 1  ) xvfo1 = 1; 
	if (xvfo >= xvfo1) xvfo1 = xvfo  +1;
	if (xvfo >= xvfo2) xvfo2 = xvfo1 +8; 
			
	if ( ( xvfo2 - xvfo1) < 8) 
	{	
		xvfo2 = xvfo1 +8;
	}

	if (xvfo1 >512 ) xvfo1 = 512; 
	
	if ( xvfo2 > 512) 
	{	xvfo2 = 512; 
		rect_RF_Selection.right= CURVESTARTX + 512+8;	// easier dragging
	}
	else
	rect_RF_Selection.right= CURVESTARTX + xvfo2 + 4;	// dragging area here	
	rect_RF_Selection.left = CURVESTARTX + xvfo  - 4 ;	// for upper sideband
			
	  
	HBRUSH hbrOld;
	HBRUSH hbr;
		
	hbr		= CreateSolidBrush(	color_tuningbox);	// color into rectangle color_tuningbox gray
	hbrOld	=(HBRUSH) SelectObject( hdcMem, hbr);			
	
	Rectangle(hdcMem, CURVESTARTX+xvfo1, CURVESTARTY-CURVEHEIGHT+1, CURVESTARTX+xvfo2, CURVESTARTY );
         
    SelectObject(hdcMem,hbrOld);	// old brush reactivate
	DeleteObject(hbr);				// forget this rectangle color
	
}	// 	if ( spectrum_channels & SPECTRUM_RF )

  // Draw dark gray dotted raster lines:

  hPen = CreatePen (PS_DOT, 1, color_rastergrid);
  hOldPen = (HPEN)SelectObject (hdcMem, hPen);
    for (i = 1; i <= 10; i++)
    {
      MoveToEx (hdcMem, CURVESTARTX, CURVESTARTY-CURVEHEIGHT+1+20*i, NULL);
      LineTo (hdcMem, CURVESTARTX+512, CURVESTARTY-CURVEHEIGHT+1+20*i);
    }

    for (i = 1; i <= MMSTEPS-1; i++)		// 28.05.2008
    {
      MoveToEx (hdcMem, CURVESTARTX+kHzXOffs[i], CURVESTARTY, NULL);
      LineTo (hdcMem, CURVESTARTX+kHzXOffs[i], CURVESTARTY-CURVEHEIGHT+1);
    }
  SelectObject (hdcMem, hOldPen);
  DeleteObject (hPen);

  // Draw outer dark gray frame:

  hPen = CreatePen (PS_SOLID, 1, color_fft_frame);
  hOldPen = (HPEN)SelectObject (hdcMem, hPen);
    MoveToEx (hdcMem, CURVESTARTX,   CURVESTARTY, NULL);
    LineTo (hdcMem, CURVESTARTX+512, CURVESTARTY);
    LineTo (hdcMem, CURVESTARTX+512, CURVESTARTY-CURVEHEIGHT+1);
    LineTo (hdcMem, CURVESTARTX,     CURVESTARTY-CURVEHEIGHT+1);
    LineTo (hdcMem, CURVESTARTX,     CURVESTARTY);
  SelectObject (hdcMem, hOldPen);
  DeleteObject (hPen);


 	rPixelsPerdB = (float)(CURVEHEIGHT) / (rbar_db_max-rbar_db_min);
 	 	
// Draw Audio RMS bar line 	
{	// Volume Bar in red , left at dB scale 	
	hPen = CreatePen (PS_SOLID, 1, mute_flag ? color_pure_red : color_RMS_bar);	// red for volume
	hOldPen = (HPEN)SelectObject (hdcMem, hPen);
 
    if (rms_val_usb <= 0) rmsdB = -199;
    else rmsdB = /* 3.01 + */ 10.0 * log10 ((double)rms_val_usb ); 	// square root is implizit, adjusted for NOT sinus-rms
  
    y = (int)(rPixelsPerdB * (rmsdB - rbar_db_min));
    y = CURVESTARTY - y;

    // Clip Y
    if (y < (CURVESTARTY-CURVEHEIGHT+1))  // shouldn't ever happen if scaled OK...             top 
      y = CURVESTARTY-CURVEHEIGHT+1;
    if (y > CURVESTARTY)  				  // ...but this *will* happen with a quiet soundcard  bottom
      y = CURVESTARTY;
   
    MoveToEx (hdcMem, CURVESTARTX -10, CURVESTARTY, NULL);
    LineTo   (hdcMem, CURVESTARTX -10, y); 
    MoveToEx (hdcMem, CURVESTARTX -11, CURVESTARTY, NULL);	// 2 pixel bar for better visibility
    LineTo   (hdcMem, CURVESTARTX -11, y);   
    MoveToEx (hdcMem, CURVESTARTX -12, CURVESTARTY, NULL);	// 3 pixel bar for better visibility
    LineTo   (hdcMem, CURVESTARTX -12, y);   
       
	SelectObject (hdcMem, hOldPen);
	DeleteObject (hPen);  
}	// RFR  


	if ( spectrum_channels & SPECTRUM_RF )  // Draw RF spectrum plot:
{
	max_RF_fft_db = -999.9;	// maximum in bandwide range
	max_RF_fft_px = 0;
	
  for (iXLoop = 0; iXLoop < NFFTPLOTPOINTS; iXLoop++)
  {
    plotPoints[iXLoop].x = CURVESTARTX + iXLoop;

    rdB = gRF_fltPlotFftV[iXLoop];
    if (rdB <= 0)   // array init'ed to zero and an FFT bin *may* be zero too...
      rdB = 1.0e-9; // ...so don't shock that log10 function :~)
    rdB = 20.0 * log10 (rdB ); // scale is made in window table !

	if ((xvfo1  <= iXLoop)&&( iXLoop <= xvfo2))
		if ( rdB > max_RF_fft_db ) { max_RF_fft_db = rdB; max_RF_fft_px = iXLoop; }
	
    y = (int)(rPixelsPerdB * (rdB - rbar_db_min));
    y = CURVESTARTY - y;

    // Clip Y
    if (y < (CURVESTARTY-CURVEHEIGHT+1))  // shouldn't ever happen if scaled OK...
      y = CURVESTARTY-CURVEHEIGHT+1;
    if (y > CURVESTARTY)  // ...but this *will* happen with a quiet soundcard
      y = CURVESTARTY;

    plotPoints[iXLoop].y = y;
  } // end for X...


  hPen = CreatePen (PS_SOLID, 1, color_spectrum);	// green middle
  hOldPen = (HPEN)SelectObject (hdcMem, hPen);
  
  Polyline (hdcMem, plotPoints, NFFTPLOTPOINTS);	// total spectrum plot 0 ... 512

  SelectObject (hdcMem, hOldPen);
  DeleteObject (hPen);

	if ( xvfo1 < xvfo2)
	{
		hPen = CreatePen (PS_SOLID, 1, color_spectrum_hi);	// green hi
		hOldPen = (HPEN)SelectObject (hdcMem, hPen);
  
		Polyline (hdcMem, &plotPoints[xvfo1], xvfo2 - xvfo1 );	// selected area hi lighting plot

		SelectObject (hdcMem, hOldPen);
		DeleteObject (hPen);				
	}
	
}	// 	if ( spectrum_channels & SPECTRUM_RF )  // Draw RF spectrum plot:
	
//--------------------------------------------------- /RFR 24.04.2012
afmin_dB = 20.0;        // 14.01.2013
afmax_dB = -999.9;
afmax_pix= 0;

if (( spectrum_channels & SPECTRUM_AF )||(g_qShowCpuLoad))
{	
  for (iXLoop = 0; iXLoop < NFFTPLOTPOINTS; iXLoop++)
  {
    plotPoints[iXLoop].x = CURVESTARTX + iXLoop;

    rdB = gAF_fltPlotFftV[iXLoop];
    if (rdB <= 0)   // array init'ed to zero and an FFT bin *may* be zero too...
      rdB = 1.0e-9; // ...so don't shock that log10 function :~)
	rdB = 20.0 * log10 (rdB ); // scale is made in window table !

    if  ( (afpix1<= iXLoop )  &&  (iXLoop <= afpix2) )      // 14.01.2013
    {
        if ( rdB < afmin_dB) { afmin_dB = rdB; }
        
        if ( rdB > afmax_dB) { afmax_dB = rdB; afmax_pix = iXLoop; }    // mark highest x-index of spectrum in band
        
    }
    
	
    y = (int)(rPixelsPerdB * (rdB - rbar_db_min));
    y = CURVESTARTY - y;

    // Clip Y
    if (y < (CURVESTARTY-CURVEHEIGHT+1))  // shouldn't ever happen if scaled OK...
      y = CURVESTARTY-CURVEHEIGHT+1;
    if (y > CURVESTARTY)  // ...but this *will* happen with a quiet soundcard
      y = CURVESTARTY;

    plotPoints[iXLoop].y = y;
  } // end for X...


  hPen = CreatePen (PS_SOLID, 1, color_spectrum_audio);	// blue middle
  hOldPen = (HPEN)SelectObject (hdcMem, hPen);

    if (spectrum_channels & SPECTRUM_AF )  
        Polyline (hdcMem, plotPoints, NFFTPLOTPOINTS);	// total spectrum plot 0 ... 512

//----------- SNR compute  14.01.2013

    afdelta_dB = afmax_dB - afmin_dB;   // positive >= 0.0, inband SNR 

    int ySNR = (int)(rPixelsPerdB * afdelta_dB ); // positive >= 0.0, inband SNR 

    int ytopSNR =  (CURVESTARTY-CURVEHEIGHT+1);
    int ybotSNR =  (CURVESTARTY-CURVEHEIGHT+1) + ySNR;
    if (ybotSNR > CURVESTARTY) ybotSNR = CURVESTARTY; // ...but this *will* happen with a quiet soundcard
      
   
    MoveToEx (hdcMem, CURVESTARTX -9, ytopSNR, NULL);
    LineTo   (hdcMem, CURVESTARTX -9, ybotSNR); 
    MoveToEx (hdcMem, CURVESTARTX -8, ytopSNR, NULL);	// 2 pixel bar for better visibility
    LineTo   (hdcMem, CURVESTARTX -8, ybotSNR);   
//    MoveToEx (hdcMem, CURVESTARTX -7, ytopSNR, NULL);	// 3 pixel bar for better visibility
//    LineTo   (hdcMem, CURVESTARTX -7, ybotSNR);   

//-----------


  SelectObject (hdcMem, hOldPen);
  DeleteObject (hPen);
	
}	// if ( spectrum_channels & SPECTRUM_AF )

    // at last : Draw VFO line ==>
if ( spectrum_channels & SPECTRUM_RF )
{
	hPen = CreatePen (PS_SOLID, 1, color_VFO_bar);	// middle yellow
	hOldPen = (HPEN)SelectObject (hdcMem, hPen);
    MoveToEx (hdcMem, CURVESTARTX+xvfo, CURVESTARTY, NULL);		// xvfo = VFO offset in pixels
    LineTo (hdcMem, CURVESTARTX+xvfo, CURVESTARTY-CURVEHEIGHT+1);
	SelectObject (hdcMem, hOldPen);
	DeleteObject (hPen);
}


//----------------------------------------------------------------------------




  // Draw overload indicator on top of the other graphics

  if (q_OverLoad)
  {
    q_OverLoad--; // will stay for at least 5 * 50ms (except when dragging VFO)
    
    if (WRC.file_activ == 0)	// ignore full scale warning from file, this is assumed correct
	{
	    SetTextColor(hdcMem, color_overdrive_text );	// red 
	    SetTextAlign (hdcMem, TA_CENTER);
	    TextOut (hdcMem, CURVESTARTX+256, CURVESTARTY-CURVEHEIGHT/4, "*** O V E R D R I V E ***", 25);      
	}      
  }
  
	if (gAF_satflag)
  	{
	    gAF_satflag--; // will stay for at least 5*50ms (except when dragging VFO)
	    SetTextColor(hdcMem, color_filterBW_text );	// blue 
	    SetTextAlign (hdcMem, TA_CENTER);
	    TextOut (hdcMem, CURVESTARTX+256, CURVESTARTY-CURVEHEIGHT/8, "*** A U D I O   C L I P P I N G   ***", 37);            
  	}
  	
  // Show CPU load

	if (g_qShowCpuLoad)
  	{	
  		int imf;
  		int idf;
    SetTextColor (hdcMem, color_status_text );	// grey
    SetTextAlign (hdcMem, TA_LEFT);
    
//	imf = (int)(0.5 + max_RF_fft_px * ((float)FS/(FFT_SIZE_IN))); // 02.01.2013
	
    double Hz_maxAF =  ( afmax_pix * AF_FS)/FFT_SIZE_IN ;   // add then VCO for absolute max in RF	14.01.2013
    imf = (int)(0.5 + Hz_VFO + Hz_maxAF );               
	
	idf = (int)(0.5 + (float)AF_FS/(2*FFT_SIZE_IN));
	
	double abspegel = afmax_dB - 20.0*log10(g_rOutputGainFactor); 
	
    iNumVisibleChars = sprintf (sz80, " CPU= %3.1f%% RMS=%9.2f dB FFTmax=%9.2f dB @ %5d +/- %3d Hz,b=%d", 100.0*g_rCpuLoad, (double)rmsdB, (double) abspegel /*max_RF_fft_db*/, imf,idf, sample24bits );
	sample24bits = 0;
    TextOut (hdcMem, 10, yline(24), sz80, iNumVisibleChars );
#if 0
    iNumVisibleChars = sprintf (&sz80[len], " xvfo= %3d, xvfo1= %3d, xvfo2= %3d",xvfo,xvfo1,xvfo2);
    TextOut (hdcMem, 10, yline(23), sz80, iNumVisibleChars );       
#endif 
  	}	// if (g_qShowCpuLoad)
  	
	else
	if (WRC.wavinfileno)
	{
		if (WRC.file_activ) SetTextColor (hdcMem, color_white);			// white
		else				SetTextColor (hdcMem, color_status_text );	// grey
			
	    SetTextAlign (hdcMem, TA_LEFT);
	    
	    int ii = sprintf (sz80,"PLAY(%d)%5.1lfkHz: ",  WRC.wavinfileno, 0.001*WRC.ori_Fs );
	    int  r = 80 - ii;
        int  k = WRC.nPlayfile_Name - r;
        if ( k < 0) k = 0;
               	    
	    iNumVisibleChars = ii + sprintf (&sz80[ii],"%s",   &WRC.Playfile_Name[k] ); // 19.03.2013 v0.93
	    TextOut (hdcMem, 10, yline(24), sz80, iNumVisibleChars);
	    	    	
	}

// Clock stamp at top right corner:	
	{	int len;
    	SetTextColor (hdcMem, color_status_clock);
    	SetTextAlign (hdcMem, TA_RIGHT);   
    	len= get_datetimeZ( sz80, time_format);		// returned char counter, time stamp here +date  
    	TextOut (hdcMem, rect_Time.right, yline(0), sz80, len);
#if 0  
    	SetTextAlign (hdcMem, TA_LEFT);    	
    	for (int ii=0; ii <= 24; ii++)	// test metric on screen
    	{ 	len= sprintf( sz80,"Line[%d] gpABC_^t?+~#.--a___q[u]", ii);
    		TextOut (hdcMem, 10, yline(ii), sz80, len);  	
    	}
#endif      
    	
	}

  
  // "Blit" the finished drawing to the physical screen:

  BitBlt(((LPPAINTSTRUCT)(&ps))->hdc,
         rctClientArea.left, rctClientArea.top,
         rctClientArea.right-rctClientArea.left,
         rctClientArea.bottom-rctClientArea.top,
         hdcMem,
         0, 0,
         SRCCOPY);

  // Done with off-screen bitmap and DC, get rid of them

  SelectObject (hdcMem, hbmOld);
  DeleteObject (hbmMem);
  DeleteDC (hdcMem);

  EndPaint(hWnd, &ps);
  
	if ( spectrum_channels & SPECTRUM_RF )
		gRF_qGUIFinishedPlotting = TRUE; 	// allow audio-thread to fill another buffer
	if ( spectrum_channels & SPECTRUM_AF ) 
		gAF_qGUIFinishedPlotting = TRUE; 	// allow audio-thread to fill another buffer
  
    // ( very crude to do that in a WM_PAINT handler, but keeps it simple )

  return 0L;          // WM_PAINT message sucessfully handled
}


/*------------------------------------------------------------------------------
 *
 *      Increase/decrease output gain
 */

void vChangeOutputGain (int iDirection)
{
  if (iDirection)
  {
    g_iOutputGainStep++;
    if (g_iOutputGainStep > MAXOUTPUTGAINSTEP)
      g_iOutputGainStep = MAXOUTPUTGAINSTEP;
  }
  else
  {
    g_iOutputGainStep--;
    if (g_iOutputGainStep < 0)
      g_iOutputGainStep = 0;
  }

	g_rOutputGainFactor = Gain_Tab[g_iOutputGainStep].value ;
	
}


/*------------------------------------------------------------------------------
 *
 *      Set LO frequency and phase increment
 */

void Set_VFO_Freq (float rFreq)
{	
	Hz_VFO = (double) rFreq;
	if (Hz_VFO > Hz_VFO_max) Hz_VFO = Hz_VFO_max;  
	if (Hz_VFO < Hz_VFO_min) Hz_VFO = Hz_VFO_min;	   
	Hz_VFO_PhaseIncr = (Hz_VFO * 2.0 * C_PI / FS);

}

void add_to_shift ( float rOffset)	// CW change shift
{
	shift_hz_requested += rOffset;
	FilterIQ_Set ( filter_case);		
}

/*------------------------------------------------------------------------------
 *
 *      Change LO frequency by Offset
 */

void vQSY (float rOffset)
{
	float f = Hz_VFO;	
	Set_VFO_Freq (Hz_VFO + rOffset);
#if (MORSE_SEND==0)
//	if ( f != Hz_VFO ) morse_spur = false;
#endif	// #if (MORSE_SEND==0)
}


/*------------------------------------------------------------------------------
 *
 *      Set default SAQ settings
 */
void vSetDCFDefaults(void)
{
	filter_case = filt_CW300;	// CW 300 Hz
	FilterIQ_Set (filter_case);
	Uc_DCF77 = 0.0;		// deload RC integrator
	dc_DCF77 = 0.0;
	tauRC_DCF77 = exp( log(0.1)/( 0.010 * FS) );	// RC time constant is: 10 ms decaying from Peak to 0.1 * Peak

	Set_VFO_Freq ( DCF77_Carrier_Freq - Hz_SH ); 
	morse_spur = true; 		
}


void vSetSaqDefults (void)
{
	filter_case = filt_CW1000;	// CW 1000Hz
	FilterIQ_Set (filter_case);
	Set_VFO_Freq ( SAQ_Carrier_Freq - Hz_SH ); 
	
#if (MORSE_SEND==0)
//	morse_spur = false;
#endif	// #if (MORSE_SEND==0)

#if (MORSE_SEND)
	Uc_DCF77 = 0.0;		// deload RC integrator
	dc_DCF77 = 0.0;
	tauRC_DCF77 = exp( log(0.1)/( 0.010 * FS) );	// RC time constant is: 10 ms decaying from Peak to 0.1 * Peak

//	Set_VFO_Freq ( SAQ_Carrier_Freq - Hz_SH ); 
	morse_spur = true; 	

#endif 	
}


/*------------------------------------------------------------------------------
 *
 *      Show About box
 */

void vShowAboutBox (HWND hwnd)
{
  MessageBox (hwnd,
              "SAQ Panoramic VLF Receiver v0.94\n"
              "___________________________________________\n"
              " Original version v0.6,compiled 2007-02-18 \n"
              " Written by Johan Bodin SM6LKM\n\n"
              " Special thanks to:\n"
              "   Wolfgang Büscher DL4YHF\n"
              "   Alberto di Bene I2PHD\n"
              "___________________________________________\n"
              " v0.94 adds from SWL Roland Fröhlich (2014) \n"
              ,             
              " About SAQrx v0.94", 0);
}


/*------------------------------------------------------------------------------
 *
 *      Show Help box
 */

void vShowHelpBox1 (HWND hwnd)
{
  MessageBox (hwnd,
	"SOUNDCARD INPUT CHANNEL:\n"
	"   Press L for left channel, R for right channel, or click on 'L[dB]' text field,sequenced: R,L,L+R,L-R.\n\n"
	
	"SPECTRUM DISPLAY:\n"
	"   Click on 'kHz RF' or 'kHz AF' text field to select/deselect RF or AF spectrum plots.\n\n"

	"CLOCK FORMAT: Click on date-time text to toggle between UTC time and local time.\n\n"
		
	"DISK RECORDER for RF (Radio Freqency 48k) with sample frequency FS and/or AF (Audio Frequency 12k) in FS/4 :\n"			  
	"   Press i for Input  RF(48k,mono L/R) or b for (48k,stereo, L+R both channels)   \n"
	"   Press o for Output AF(12k  ,mono)   or s for USB+LSB (12k,stereo, both sidebands)\n"
	"   Click in the text fields of recorder box to start/stop function.\n\n"

	"PLAY FILE:\n"
	"   Click on this text and mark the WAV-file in the opening file selection box.\n"
	"   Format must be Mono or Stereo, 8/16/24bit. Sample rate conversion is NOT implemented!\n\n"
	 
	"TUNING:\n"
	"   1. Drag the passband area with the left mouse button or\n"
	"   2. Rotate the mouse wheel (*) or\n"
	"   3. Use the arrow keys (*)\n"
	"   4. Press U/D for 1 Hz steps up/down\n"
	"   (*) Step size is selected by click on digit cursor in numeric text field.\n\n"

	"FILTER SELECTION:\n"
	"   1. Right-click anywhere in the window to cycle through all filter bandwidths or\n"
	"   2. Left-click 'CW','SSB,'AM' text field to select, CW Shift is changed with SHIFT+WHEEL.\n\n"

	"TUNE IN SAQ ON 17.2 kHz (or DCF77 on 77.5 kHz in 192k version):\n"
	"   1. Left-click the [Tune SAQ] button or\n"
	"   2. Press the Home key.\n\n"

	"   This will set the local oscillator to 16500 Hz and select the CW 1000Hz filter. "
	"This receiver uses Upper Sideband mode only.\n\n"

	"INPUT OVERLOAD INDICATOR:\n"
	"   *** O V E R D R I V E *** will flash in the middle of the screen "
	"if the input signal reaches 0.1dB below clipping level.\n\n"

	"OUTPUT AF GAIN SELECTION:\n"
	"   1. Left-click the [-] or [+] button or\n"
	"   2. Press -/+ to decrease/increase the output gain in 3/6/10 dB steps.\n"
	"   3. Click on 'AF Gain' or press M key to Mute/Demute the AF output.\n\n"
	
	"CPU LOAD INDICATOR:\n"
	"   Press C to toggle CPU load indicator on/off.\n\n"
	
    "DISPLAY LEVEL RANGE:\n"
    "   Press v to shift meter to -10dB, V to +10dB.\n\n"
	,
	"How to use the SAQ Panoramic VLF Receiver v0.94:", 0);
}


void vShowHelpBox2 (HWND hwnd)
{
  MessageBox (hwnd,
  
	"NOTES:\n"
	"   This program uses Windows DEFAULT soundcard for both input and output. "
	" If you have more than one soundcard\n"
	"   installed, please check in the Windows Control Panel which card is selected "
	"as default for sound I/O.\n\n"

	"   Use your Windows RECORDING mixer to select the appropriate input"
	" (LINE, MIC etc.) and for adjusting the input level.\n"
	"   If your RECORDING mixer has a slider called \"Output Mix\", or similar, make sure "
	"it is fully off to prevent echo/oscillation.\n"
	"   Select WAVE output and listening volume in your PLAYBACK mixer.\n\n "

	"WARNING:\n"
	"   Never connect a large antenna, such as an outdoor longwire, directly to the "
	"soundcard input without adequate protection!\n"
	"   Use at least a series resistor followed by a pair of parallel diodes, connected "
	"back-to-back, across the input.\n"
	"   Make sure that you know what you are doing. You are using this program at your own risk. "
	"You have been warned."
	,
	"How to use the SAQ Panoramic VLF Receiver v0.94:", 0);
}


void WavePlay_OpenDlg (HWND hwnd)	// called from WindowProcedure
{	
	OPENFILENAME ofn;	// file open dialog box
	int result;
	
	ZeroMemory(&ofn, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn); // SEE NOTE BELOW
	ofn.hwndOwner = hwnd;
	
	//ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
	ofn.lpstrFilter = "Wave Files (*.wav)\0*.wav\0All Files (*.*)\0*.*\0";
	
	ofn.lpstrFile = WRC.Playfile_Name;
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = "wav";
	
	if(GetOpenFileName(&ofn))	// for write use: GetSaveFileName()
	{	
	
		WRC.Open_WavePlayer ();			
	}
		

}	// WavePlay_OpenDlg



/*------------------------------------------------------------------------------
 *
 *      Windows message callback handler
 *
 *      (This function is called by the Windows function's DispatchMessage() )
 */


LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int i;
	PaError PaInitresultCode;
	float rDelta;
	int iKeyRepeatCount;
	int iFilterMinX;
	int iFilterMaxX;
	int iMouseY;
	int iMouseX;
	int iVfoX;
	char szErr80[84];
	static int qShiftIsDown = 0;
	static int qCtrlIsDown = 0;
	static int qMouseQSY = 0;
	static int iMouseClickX;
	static double rMouseClickVfoFreq;

	
  switch (message)
  {
    case WM_CREATE:

      // Init' some variables

      for (i = 0; i < (sizeof(gRF_fltFftBufRe)/sizeof(float)); ++i)
        gRF_fltFftBufRe[i] = gRF_fltFftBufIm[i] = 0.0;

      for (i = 0; i < NFFTPLOTPOINTS; ++i)
        	gRF_fltPlotFftV[i] = 0.0;

      	gRF_qGUIFinishedPlotting = TRUE; // allow audio thread to fill another buffer
      	
		
	// ---------------------  same for AF spectrum:	

      for (i = 0; i < (sizeof(gAF_fltFftBufRe)/sizeof(float)); ++i)
        gAF_fltFftBufRe[i] = gAF_fltFftBufIm[i] = 0.0;

      for (i = 0; i < NFFTPLOTPOINTS; ++i)
        	gAF_fltPlotFftV[i] = 0.0;


		gAF_qGUIFinishedPlotting = TRUE; // allow audio-thread to fill another buffer
	//------------------------		   

      // Initialize PortAudio subsystem

      PaInitresultCode = Pa_Initialize ();
      
      if (PaInitresultCode == paNoError)
      {
        PaInitresultCode = Pa_OpenDefaultStream (
            &g_PAStream,
            2, 2,       // stereo input and output
            paFloat32,  // most convenient format on a fast machine...
            FS,         // sampling frequency desired
//          4*FPB, 16,     // FPB frames per buffer, let PA determine numBuffers
          	FPB, 0,     // FPB frames per buffer, let PA determine numBuffers            
            AudioCallback, // callback function
            NULL);      // we have no user data pointer to pass to the callback for now
        
#if 1
			if (PaInitresultCode != paNoError)	// make a new try: 12.05.2012 08:56
      		{
      			Pa_Terminate();
      			
      			PaInitresultCode = Pa_Initialize ();
      		
        		PaInitresultCode = Pa_OpenDefaultStream (
        	    &g_PAStream,
        	    2, 2,       // stereo input and output
        	    paFloat32,  // most convenient format on a fast machine...
        	    FS,         // sampling frequency desired
//      	    4*FPB, 16,     // FPB frames per buffer, let PA determine numBuffers
        	  	FPB, 0,     // FPB frames per buffer, let PA determine numBuffers            
        	    AudioCallback, // callback function
        	    NULL);         
        		
        	}
#endif        
            
        if (PaInitresultCode == paNoError)
        {			
			{	//	get the actual samplerate: ------------------->
				internalPortAudioStream * is = (internalPortAudioStream *) g_PAStream;	// void pointer to structure
				
				actual_Fs = is->past_SampleRate;     /* Closest supported sample rate. */
				
				if ( actual_Fs != (double) FS )
				{
			    		
					sprintf (szErr80, "actual_Fs= %lf Hz", actual_Fs);   		
			    		
			    	MessageBox (NULL, szErr80, "SAQrx: Sample Rate changed!",
			        				MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2);	
			        						
				}
				
			}	//--------------------------------------------------------  			
			      	
			PaInitresultCode = Pa_StartStream (g_PAStream);
			
		}
		          
      }

      if (PaInitresultCode != paNoError)
      {
        Pa_Terminate();
        if (PaInitresultCode == paHostError)
        {	// problem with FS?  next try would be OK for my USB soundcard
          sprintf (szErr80, "Host error: 0x%08X\n\nS T A R T   A G A I N !\n ", Pa_GetHostError ());	
          
          MessageBox (hwnd, szErr80, "SAQrx", MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
          PostQuitMessage (1);
        }
        else
        {
          MessageBox (hwnd, Pa_GetErrorText(PaInitresultCode), "SAQrx",
                      MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
          PostQuitMessage (1);
        }
      }

      // Start a periodic timer that posts a WM_TIMER message each 50ms

      SetTimer (hwnd, 0, 50, NULL);
      break;

    case WM_DESTROY:
      KillTimer (hwnd, 0);          // what if SetTimer() was never called? *#*
      Pa_StopStream (g_PAStream);   // result ignored...
      Pa_CloseStream (g_PAStream);
      Pa_Terminate ();
      PostQuitMessage(0);
      break;

    case WM_TIMER:		// every 50 ms executed:
    
		WRC.Play_Wave_Mono();		//---- called cyclic in Timer-IRQ: --------->>
		Poll_Recorder_toDisk  ();	// slow process, periodically in GUI called

		if (no_retry == 1)
		{	no_retry++;
			MessageBox (hwnd, "Write File not possible !","       SAQrx Warning ", MB_ICONEXCLAMATION | MB_OK);	
		}

// handle timer scheduler:
		if (RF_record_timer_status)
		{
			if (RF_record_timer_status== RECTIMER_WAITS)
			{
				if ( RF_timer_elapsed >= RF_record_timer_ms_wait)
				{
					bothbands_RF = ( RF_record_timer_channels == 2);
					Toggle_Record_Switch_RF (RF_record_timer_channels);		// 1 or 2 channels 					
					RF_record_timer_status= RECTIMER_RECORD_ACTIV;
					RF_record_timer_ms_zerolatch = timeGetTime();	// now counts for duration
				}				
			}
			else
			if (RF_record_timer_status== RECTIMER_RECORD_ACTIV)
			{
				if ( RF_timer_elapsed >= RF_record_timer_ms_dur)
				{
					bothbands_RF = ( RF_record_timer_channels == 2);
					Toggle_Record_Switch_RF (RF_record_timer_channels);		// 1 or 2 channels 					
					RF_record_timer_status= RECTIMER_ENDS;
//					RF_record_timer_ms_zerolatch = timeGetTime();
				}				
			}			
		}	// if (RF_record_timer_status)
		
		if (AF_record_timer_status)
		{
			if (AF_record_timer_status== RECTIMER_WAITS)
			{
				if ( AF_timer_elapsed >= AF_record_timer_ms_wait)
				{
					bothbands_AF = ( AF_record_timer_channels == 2);
					Toggle_Record_Switch_AF (AF_record_timer_channels);		// 1 or 2 channels 					
					AF_record_timer_status= RECTIMER_RECORD_ACTIV;
					AF_record_timer_ms_zerolatch = timeGetTime();	// now counts for duration
				}				
			}
			else
			if (AF_record_timer_status== RECTIMER_RECORD_ACTIV)
			{
				if ( AF_timer_elapsed >= AF_record_timer_ms_dur)
				{
					bothbands_AF = ( AF_record_timer_channels == 2);
					Toggle_Record_Switch_AF (AF_record_timer_channels);		// 1 or 2 channels 					
					AF_record_timer_status= RECTIMER_ENDS;
//					AF_record_timer_ms_zerolatch = timeGetTime();
				}				
			}			
		}	// if (AF_record_timer_status)		
		
   
      InvalidateRect (hwnd,   // window handle
                      NULL,   // CONST RECT*, NULL => force redraw of entire client area
                      FALSE); // FALSE => do not erase when BeginPaint() is called
      return 0L;

    case WM_ERASEBKGND:       // *#* is this case really needed?
        return (LRESULT)1;    // fake "message handled"

    case WM_PAINT:
      return OnWmPaint (hwnd);

    case WM_RBUTTONDOWN:
      					if (filter_case <= 0) filter_case = filt_MAX;
      					else				  filter_case--;
      					FilterIQ_Set (filter_case);
      					vQSY (0.0);   // don't QSY unless LO is too high for this filter
      					return 0L;

    case WM_MOUSEWHEEL:
    
    if (wParam & MK_SHIFT)	
    {
    	if ( filter_case <= filt_CWmax)	// only CW modes:
    	{
			rDelta = shift_hz_incr; 
			if (GET_WHEEL_DELTA_WPARAM (wParam) < 0) rDelta = -rDelta;
      		add_to_shift (rDelta);
		}
          	
    }
    else
    {
    
		rDelta = vco_hz_incr;
      
//      if (wParam & MK_CONTROL)	rDelta = 10.0;
//      if (wParam & MK_SHIFT)	rDelta = 1000.0;   // make shift selection here !     
		if (GET_WHEEL_DELTA_WPARAM (wParam) < 0) rDelta = -rDelta;
		vQSY (rDelta);     
    }
    
      return 0L;

    case WM_CHAR:
      switch (wParam)
      {

		case 'F':	case 'f':

					if (WRC.file_activ) 
					{
						if (WRC.play_stop_request <= 0) WRC.play_stop_request=1;
						break;
					}
					else					
						WavePlay_OpenDlg (hwnd);	// call File Dialog box
					break;


		case 'L':	case 'l':
					left_channel=1;	// set input channel to left					
					break;   
					   	
		case 'R':	case 'r':
					left_channel=0;	// set input channel to right				
					break;  


        case 'I':	case 'i':	        			
        			bothbands_RF = 0;
          			Toggle_Record_Switch_RF (1);		// only 1 channel, L or R from soundcard
          			break;

        case 'B':	case 'b':	
        			bothbands_RF = 1;
          			Toggle_Record_Switch_RF (2);		// 2 channels L+R original input from soundcard
          			break;
                            		          			
        case 'O':	case 'o':	       			
        			bothbands_AF = 0;
          			Toggle_Record_Switch_AF (1);		// only 1 channel, Left from Stereo
          			break;
          			
        case 'S':	case 's':        			      	
        			bothbands_AF = 1;
          			Toggle_Record_Switch_AF (2);		//  2 channel, Left=USB, Right=LSB or Morse envelope
          			break;        

        case '-':
          vChangeOutputGain (0);
          break;

        case '+':
          vChangeOutputGain (1);
          break;

        case '?':
          vShowAboutBox (hwnd);
          break;

        case 'C':	case 'c':
          if (g_qShowCpuLoad)
            g_qShowCpuLoad = 0;
          else
            g_qShowCpuLoad = 1;
          break;

		case 'P':	case 'p': 	mph_flag = 1 - mph_flag;	// toggle minimum-phase flag	24.06.2012		
								FilterIQ_Set (filter_case);	// update the filters
								break;
		
        case 'N':	case 'n':
//          filter_case = 0; FilterIQ_Set (filter_case);
          break;

        case 'M':	case 'm':
        			mute_flag ^= 01L;		 // toggled
					break;
					
//          filter_case = 1; FilterIQ_Set (filter_case);
//          vQSY (0.0);   // don't QSY unless LO is too high for this filter
          break;

        case 'W':	case 'w':
//          filter_case = 2; FilterIQ_Set (filter_case);
//          vQSY (0.0);   // don't QSY unless LO is too high for this filter
          break;

        case 'U':	case 'u':
          vQSY (1.0);	// 1 Hz upper
          break;

        case 'D':	case 'd':
          vQSY (-1.0);	// 1 Hz lower
          break;
          
        case 'v':   // dB scale down 08.01.2014 
            ibar_db_max = ibar_db_max -10;
            if (ibar_db_max < -40) ibar_db_max = -40;
            ibar_db_min = ibar_db_max - 110;

            rbar_db_max = (float) ibar_db_max;
            rbar_db_min = (float) ibar_db_min;
            break;
            
        case 'V':   // dB scale up 08.01.2014             
            ibar_db_max = ibar_db_max + 10;
            if (ibar_db_max > 0) ibar_db_max = 0;
            ibar_db_min = ibar_db_max - 110;

            rbar_db_max = (float) ibar_db_max;
            rbar_db_min = (float) ibar_db_min;
            break;
            
            
      }
      return 0L;

    case WM_KEYUP:
      switch (wParam)
      {
        case VK_SHIFT:
          qShiftIsDown = 0;
          break;

        case VK_CONTROL:
          qCtrlIsDown = 0;
          break;
      }
      return 0L;

    case WM_KEYDOWN:
      iKeyRepeatCount = (int)(lParam & 0xFFFF);
      switch (wParam)
      {
        case VK_SHIFT:
          qShiftIsDown = 1;
          break;

        case VK_CONTROL:
          qCtrlIsDown = 1;
          break;

        case VK_LEFT:
        case VK_DOWN:
          if (qShiftIsDown)
            vQSY (-1000.0*iKeyRepeatCount);
          else
          if (qCtrlIsDown)
            vQSY (-10.0*iKeyRepeatCount);
          else
            vQSY (-100.0*iKeyRepeatCount);
          break;

        case VK_RIGHT:
        case VK_UP:
          if (qShiftIsDown)
            vQSY (1000.0*iKeyRepeatCount);
          else
          if (qCtrlIsDown)
            vQSY (10.0*iKeyRepeatCount);
          else
            vQSY (100.0*iKeyRepeatCount);
          break;

        case VK_HOME: // set default VFO & filter for SAQ reception
          vSetSaqDefults ();
          break;

        case VK_F1:
          vShowHelpBox1 (hwnd); vShowHelpBox2 (hwnd); 
      }
      return 0L;

    case WM_LBUTTONDOWN:
      // Warning! Hardcoded coordinates here that must match
      // the corresponding hardcoded stuff in OnWmPaint()!
      maus.x = iMouseX = LOWORD(lParam);
      maus.y = iMouseY = HIWORD(lParam);

      // Mouse X in [SAQ], [Help] and [About] button X range?
 
 		if (FS < 176400.0)
 		{
 			if ( PtInRect( &rect_Tune, maus) ) { vSetSaqDefults (); return 0L; } // break; 
 		}
 		else
 		{
 			if ( PtInRect( &rect_SAQ, maus) ) { vSetSaqDefults (); return 0L; } // break;
 			else
 			if ( PtInRect( &rect_DCF, maus) ) { vSetDCFDefaults (); return 0L; } // break;  
 		}
 		
      
		if ( PtInRect( &rect_Help, maus) ) { vShowHelpBox1 (hwnd); vShowHelpBox2 (hwnd); return 0L; } // break;  
		else
		if ( PtInRect( &rect_About, maus) ) { vShowAboutBox (hwnd); return 0L; } // break;  
		else
		if ( PtInRect( &rect_RecRF, maus) ) 
			{
	           	if (( Record_Status_RF() )&&( bothbands_RF == 1)) 
		           	{	Toggle_Record_Switch_RF (2);	// close stereo file 		           		
		           	}        			
	        	bothbands_RF = 0;
	          	Toggle_Record_Switch_RF (1);		// input wave trace,  1 channel, L/R from Stereo 
	          	return 0L; 				/*break;*/       	
        	}		
		else		
		if ( PtInRect( &rect_RecAF, maus) )	// output wave trace
			{
	           	if (( Record_Status_AF() )&&( bothbands_AF == 1)) 
		           	{	Toggle_Record_Switch_AF (2);	// close stereo file  		           		
		           	}        			
	        	bothbands_AF = 0;
	          	Toggle_Record_Switch_AF (1);		// output wave trace,  1 channel, Left from Stereo 
	          	return 0L; 				/*break;*/       	
        	}  
		else
		if ( PtInRect( &rect_Play, maus) )	// player
			{
	           	if ( WRC.file_activ)  
				{
						if (WRC.play_stop_request <= 0) WRC.play_stop_request=1;
						return 0L; 		/*break;*/  
				}
				else					
					WavePlay_OpenDlg (hwnd);	// call File Dialog box
				return 0L; 				/*break;*/  	      		                	
        	}  
		else
		if ( PtInRect( &rect_LdB, maus) ) 
			{		left_channel++;	// toggle left/right input channel
			        if (left_channel > 3) left_channel=0;
					return 0L; 				/*break;*/ 
			}
		else
		if ( PtInRect( &rect_kHz_AF, maus) ) 
			{		spectrum_channels ^= SPECTRUM_AF;	// toggle AF spectrum display
					if (spectrum_channels==0) spectrum_channels = SPECTRUM_RF;
					return 0L; 				/*break;*/ 
			}
		else
		if ( PtInRect( &rect_Time, maus) ) 
			{		time_format ^= 1L;	// toggle UTC or local time
					return 0L; 				/*break;*/ 
			}
		else				
		
		if ( PtInRect( &rect_kHz_RF, maus) ) 
			{		spectrum_channels ^= SPECTRUM_RF;	// toggle RF spectrum display
					if (spectrum_channels==0) spectrum_channels = SPECTRUM_AF;
					return 0L; 				/*break;*/ 
			}
		else
		if ( PtInRect( &rect_FilterCW, maus) ) // Filter box  pressed ?
			{		if (filter_case > filt_CWmax ) filter_case = filt_CWmax;
					else
					{	filter_case--;
					 	if (filter_case < 0) filter_case = filt_CWmax;
					}      				
      				FilterIQ_Set (filter_case);
      				vQSY (0.0);   // don't QSY unless LO is too high for this filter
					return 0L; 				/*break;*/ 
			}
		else
		if ( PtInRect( &rect_FilterSSB, maus) ) // Filter box  pressed ?
			{		filter_case = filt_SSB;   				
      				FilterIQ_Set (filter_case);
      				vQSY (0.0);   // don't QSY unless LO is too high for this filter
					return 0L; 				/*break;*/ 
			}		
		else
		if ( PtInRect( &rect_FilterAM , maus) ) // Filter box  pressed ?
			{		filter_case = filt_AM;   				
      				FilterIQ_Set (filter_case);
      				vQSY (0.0);   // don't QSY unless LO is too high for this filter
					return 0L; 				/*break;*/ 
			}			
		else		
		
		if ( PtInRect( &rect_AFGain, maus) ) // Mute pressed ?
			{		mute_flag ^= 01L;		 // toggled
					return 0L; 				/*break;*/ 
			}
		else
		if ( PtInRect( &rect_AFMinus, maus) ) // Output gain [-] pressed?
			{		vChangeOutputGain (0);
					return 0L; 				/*break;*/ 
			}
		else
		if ( PtInRect( &rect_AFPlus, maus) ) // Output gain [+] pressed?
			{		vChangeOutputGain (1);
					return 0L; 				/*break;*/ 
			}
		else												       
      	if (( spectrum_channels & SPECTRUM_RF )&&
		    ( PtInRect( (const RECT*)&rect_RF_Selection, maus) ))	// Mouse clicked in the current filter passband? 
		{
          qMouseQSY = 1;
          iMouseClickX 		 = iMouseX;
          rMouseClickVfoFreq = Hz_VFO;			
			
		}
		else
		if ( PtInRect( &rect_VFOBlock, maus) ) // Hz display
		{	int u,x1,x2,fact;
			fact = 1;
			for (u=0; u <=4; u++)
			{				
				x1 = rect_VFOBlock.right  -30 - 8*u;  // vco_case_unit*8 ;	// 1 unit,10,100,1000,10k
				x2 = x1 + 8 ;
				
				if ( ( x1 <= maus.x) &&( maus.x < x2))
				{
					vco_case_unit = u;
					vco_hz_incr   = (float)fact;
					break;	// for...
				}				
				fact *= 10;
				
			}					
			return 0L; 				/*break;*/ 
		}
		else
		if ( (filter_case <= filt_CWmax) && (PtInRect( &rect_FilterShift, maus)) ) // Shift Hz display
		{	int u,x1,x2,fact;
			fact = 1;
			for (u=0; u <=3; u++)
			{				
				x1 = rect_FilterShift.right  -30 - 8*u;  // 1 unit,10,100,1000, (10k)
				x2 = x1 + 8 ;
				
				if ( ( x1 <= maus.x) &&( maus.x < x2))
				{
					shift_case_unit = u;
					shift_hz_incr   = (float)fact;
					break;	// for...
				}
				
				fact *= 10;				
			}					
			return 0L; 				/*break;*/ 
		}
			
      return 0L;

    case WM_LBUTTONUP:
      qMouseQSY = 0;
      return 0L;

    case WM_MOUSEMOVE:
      if (qMouseQSY)
      {
        maus.x= iMouseX = LOWORD(lParam);
      	maus.y= iMouseY = HIWORD(lParam);
      	
		if ( spectrum_channels & SPECTRUM_RF )
		{	int x =  (int) (0.5 + rMouseClickVfoFreq + (double)(iMouseX - iMouseClickX) * ((0.5*FS) / 512.0));
			x = 10* ((x+5)/10);	// only 10 Hz units changed
		             
	        Set_VFO_Freq ( (float) x );
	        InvalidateRect (hwnd, NULL, FALSE); // update screen fast when dragging
    	}
      }
      return 0L;

    default:          /* for messages that we don't deal with */
      return DefWindowProc (hwnd, message, wParam, lParam);
  }

  return DefWindowProc (hwnd, message, wParam, lParam);
}


/*------------------------------------------------------------------------------
 *
 *      Window$ main()
 */

const char g_szClassName[] = "SaqPanRx";

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nShowHow )
{
  HWND hwnd;                // this is the handle for our window
  MSG messages;             // here messages to the application are saved
  WNDCLASSEX wc;            // data structure for the windowclass

  if (FindWindow (g_szClassName, NULL) != NULL)
  {
    if (MessageBox (NULL, "An instance of SAQrx is already running!\n"
                          "Start another SAQrx anyway?", "Oops!",
        MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2) == IDNO)
    		return 0;	// abort this program
  }
   
// extern int		_argc;
// extern char**	_argv;
	
	BOOL invalidopt = false;
	int i;
	char * any_argument ;	
	unsigned char optionchar; 	

	int	wait_time,dur_time;			// from cmd-line, counts minutes
	int iflag,bflag,oflag,sflag;	// options for recorder	
	
	wait_time = dur_time = -1; 		// invalid time set
	iflag= bflag= oflag= sflag= 0;	// options for recorder	
	


	
	for (i=1; ((i < _argc)&&(invalidopt==false)); i++)
	{	
		any_argument = _argv[i];
		if (any_argument[0]=='-')
		{  
		any_argument++; 			/* better: verify string length !!! */
	   	optionchar = any_argument[0];
	   	any_argument++;
	   	switch (optionchar)
	   	{	

		case 'T': case 't': 					  
			  		sscanf( any_argument, "%ld,%ld", & wait_time, & dur_time );	// all times in minutes here !			  				
					if ((dur_time > 0)&&(wait_time >= 0)) 
					{			
						if ( iflag | bflag) init_record_timer_RF(wait_time, dur_time, 2*bflag+iflag);	// append ?	
						if ( oflag | sflag) init_record_timer_AF(wait_time, dur_time, 2*sflag+oflag);	
					}
					iflag= bflag= oflag= sflag= 0;	// reset options for recorder
					wait_time = dur_time = -1; 		// invalid time set			  
			  		break;

		case 'I': case 'i': iflag=1; bflag=0; break;	// options for recorder	RF
		case 'B': case 'b': iflag=0; bflag=1; break;	// options for recorder	RF
		
		case 'O': case 'o': oflag=1; sflag=0; break;	// options for recorder AF			
		case 'S': case 's': oflag=0; sflag=1; break;	// options for recorder	AF	
				
#if 0


		case 'N': case 'n': sscanf( any_argument, "%lf", &normsum ); break;
			  
		case 'Q': case 'q':
			  quantbits= -1; sscanf( any_argument, "%ld", &quantbits);			  
			  break;			  
		

		case 'S': case 's': 
			  sscanf( any_argument, "%le", & input_scaling );	/* factor */
			  break;


		case 'F': case 'f': 
			  sscanf( any_argument, "%le", & samplerate  );	/* factor for x-freq scaling 27.04.2012 */
			  samplerate = 0.5 * samplerate ;				/* to Nyquist ~ 1.0 */
			  break;
			  
			  	
		case 'f': 
			  sscanf( any_argument, "%s", filtfile_name );
			  break;


		case 'a': 
			  break;
		case 'x': kk= -1; sscanf( any_argument, "%lx", & kk );
				xflag = 1;			
			  break;
			  
#endif

		default: 	invalidopt= true;
#if 1		
    				MessageBox (NULL, "SAQrx Process started with:\n"
                          	    "unknown argument", "SAQrx: Sorry!",
        						MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2);	
#endif
			 		break;
	   } /* switch (optionchar) */

		}	// if (any_argument[0]=='-')
		
	} /* for _argc.. */	
	
	
				  
			  
			  
#if 0
/// test working dir :
{
	char * pwdname;
	
	pwdname = getenv("HOMEPATH");
	
	if (pwdname)
		MessageBox (NULL, pwdname,"working DIR=\n", MB_ICONEXCLAMATION | MB_YESNO | MB_DEFBUTTON2);	
                          	  
        				
}
#endif
///


	
	init_kHzXOffs();	// 28.05.2008   

	Filters_Init ();	// 23.05.2012

 	if (FS < 176400.0)  vSetSaqDefults ();
 	else				vSetDCFDefaults();
 		


	dsp_construct_fftwindow_table ( Fft_Window_Permanent, FFT_SIZE_IN, 0);	// use Dolph Window 28.05.2012

	
#if	MORSE_SEND

#define SAQ_XMAS2007 "        CQ CQ CQ DE SAQ SAQ SAQ ="\
"THIS IS GRIMETON RADIO/SAQ IN A TRANSMISSION USING THE ALEXANDERSON 200 KW ALTERNATOR ON 17.2 KHZ."\
"WE WISH YOU ALL A MERRY CHRISTMAS AND A HAPPY NEW YEAR. ="\
"SIGNED: THE ALEXANDER-GRIMETON VETERANRADIOS VAENNER ASSOCIATION +"\
"FOR QSL INFO PLEASE SEE OUR WEBSITE: WWW.ALEXANDER.N.SE ="\
"DE SAQ SAQ SAQ @             "


#define MY_message "         cq cq cq de roland = dear user, if you like this program write an email to the co-autor roland.froehlich@t-online.de vy 73 de roland =              "        



FTRACE( "Morse send simulation!");
		
SAQ_Center_Freq = 17200.0;	// Hz
SAQ_Low_Freq = SAQ_Center_Freq - 2000;	// Hz, for lower sideband
 
SAQ_Center_delta = (SAQ_Center_Freq * 2.0 * C_PI / FS);
SAQ_Low_delta    = (SAQ_Low_Freq * 2.0 * C_PI / FS);

SAQ_Center_accu = 0.0;
SAQ_Low_accu    = 0.0;

Set_Morse_String ( &B_morse, SAQ_XMAS2007 , (int) (0.080*FS) );	// 15 Wpm USB

//Set_Morse_String ( &B_morse, "SAQ SAQ SAQ    ", (int) (0.080*FS) );	// 15 Wpm

//Set_Morse_String ( &A_morse, MY_message , (int) (0.240*FS) );	//  5 Wpm LSB
  Set_Morse_String ( &A_morse, MY_message , (int) (0.060*FS) );	// 20 Wpm LSB

#endif	// #if MORSE_SEND

  // Set up the main window

  wc.cbSize        = sizeof (WNDCLASSEX);
  wc.style         = CS_HREDRAW | CS_VREDRAW; // or 0?
  wc.lpfnWndProc   = WindowProcedure;
  wc.cbClsExtra    = 0 ;
  wc.cbWndExtra    = 0 ;
  wc.hInstance     = hThisInstance;
  wc.hIcon         = LoadIcon(hThisInstance,TEXT("PROGRAM_ICON"));
  wc.hCursor       = LoadCursor (NULL, IDC_HAND);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // without "+1"?
  wc.lpszMenuName  = NULL;          // no menu from a RESOURCE !
  wc.lpszClassName = g_szClassName;
  wc.hIconSm       = LoadIcon(hThisInstance,TEXT("PROGRAM_ICON"));

  if (!RegisterClassEx (&wc))
  {
    MessageBox (NULL, "Window Registration Failed!", "Error!",MB_ICONEXCLAMATION | MB_OK);
    return 0;
  }

  // The class is registered, let's create the program
  char tit[256]; char * TIT;
 
 if (RF_record_timer_status | AF_record_timer_status)
 				TIT = "SAQ Panoramic VLF Receiver v0.94 -T";
  
//	if (xflag)	TIT = "SAQ Panoramic VLF Receiver v0.94 -x";
	else		TIT = "SAQ Panoramic VLF Receiver v0.94";

	sprintf(tit,"%s       FS= %5.1lf kHz", TIT, 0.001*FS);
		
  hwnd = CreateWindowEx (
           0,                             // use WS_EX_CLIENTEDGE for "sunken edge" (else 0)
           g_szClassName,                 // window class name
           tit,  						  // title Text
           WS_CAPTION | WS_SYSMENU,       // fixed size window with close button [X] only
           CW_USEDEFAULT,                 // Windows decides the initial X position
           CW_USEDEFAULT,                 // Windows decides the initial Y position
           MAINSCREEN_X,                  // The window's width INCLUDING BORDER FRAME!
           MAINSCREEN_Y,                  // The window's heigth INCLUDING BORDER FRAME!
           HWND_DESKTOP,                  // The window is a child-window to desktop
           NULL,                          // No menu
           hThisInstance,                 // Program instance handle
           NULL                           // No Window creation data
           );

  if (hwnd == NULL)
  {
    MessageBox (NULL, "Window Creation Failed!", "SAQ Error!",
                MB_ICONEXCLAMATION | MB_OK);
    return 0;
  }

  // Make the window visible on the screen

  ShowWindow (hwnd, nShowHow );
//  UpdateWindow (hwnd); not needed since we use a timer tick for redraw

  // Run the message loop. It will run until GetMessage() returns 0

  while (GetMessage (&messages, NULL, 0, 0) > 0)
  {
    // Translate virtual-key messages into character messages
    TranslateMessage (&messages);
    // Send message to WindowProcedure
    DispatchMessage (&messages);
  }

  // The program return-value is 0 - The value that PostQuitMessage() gave
  return messages.wParam;
}
