
/*

				Audio Monitor Display
	 
*/

#include "netfone.h"

static int nbands = 128;				// Frequency bands to plot
static int fftsize = 512;				// Size of FFT
static int logscale = TRUE;				// Show log of intensity ?
static int logfreq = FALSE;				// Plot log of frequency ?

static short *samples = NULL;			// Audio sample buffer
static unsigned char *sourceSpectrum = NULL;	// Source of samples table
static int samps_in_buffer = 0;			// Samples in buffer
static HBITMAP easel = NULL;			// Bitmap to refresh spectrum display
static HBITMAP weasel = NULL;			// Bitmap to refresh envelope display

static double cEnergy = 0.0;			// Average energy of samples in buffer
static double mEnergy = 0.0;			// Maximum energy in buffer
#ifdef _DEBUG
static int aEnergy = 0;					// Unscaled energy for debugging
#endif
static int logEnergy = FALSE;			// Show log of energy ?
static int noiseFloor = 0;				// Noise floor
static COLORREF ceColour = RGB(0, 255, 0);	// Current input source tagged colour
static BOOL spectrumChanged = TRUE;		// Spectrum display mode changed

static double *pspectrum = NULL;		// Computed power spectrum
static double pscale = 1.0;				// Maximum value in power spectrum

#define PSMOOTH		   0.001			// Smoothing factor for spectrum scale
static double escale = 0.0;				// Exponentially smoothed power spectrum scale
static int epposx = 0;					// Energy plot position

#define M_E			   2.7182818284590452354

#define WM_UPDATE_SPECTRUM	(WM_USER + 1000)

BOOL spectrumBarGraph = TRUE;			// Plot spectrum as bar chart, equaliser style ?
BOOL spectrumVoicePrint = FALSE;		// Plot spectrum as horizontal "waterfall" ?
BOOL spectrumTransmitOnly = FALSE;		// Show transmitted audio only ?
BOOL spectrumReceiveOnly = FALSE;		// Show received audio only ?
BOOL spectrumMaxEnergy = FALSE;			// Plot maximum energy ?

static BOOL paintingSpectrum = FALSE;	// Busy painting spectrum ?
static int sgposx = 0;					// Spectrogram bar position

#define SPECTRUM_INTERVAL	0			// Spectrum update interval in milliseconds
static DWORD nextSpectrumTime = 0;		// Time for next spectrum ?

//	PAINTENERGY  --  Paint the energy display in its canvas

static void paintEnergy(HWND canvas)
{
	HDC hdc = GetDC(canvas), hdca = NULL;
	RECT r;
	HPEN pen;
	HBITMAP oe;
	int width, height, dy;
	double yscale = escale;

	paintingSpectrum = TRUE;
//	InvalidateRect(canvas, NULL, TRUE);
//	UpdateWindow(canvas);

	GetClientRect(canvas, &r);
	r.left++;
	r.top++;
	r.right--;
	r.bottom--;
	width = r.right - r.left;
	height = r.bottom - r.top;

	hdca = CreateCompatibleDC(hdc);
	oe = SelectObject(hdca, weasel);

	dy = (int) ((height / 2) * (spectrumMaxEnergy ? mEnergy : cEnergy));

	pen = (HPEN) SelectObject(hdca, GetStockObject(BLACK_PEN));
	MoveToEx(hdca, r.left + epposx, r.top, NULL);
	LineTo(hdca, r.left + epposx, r.top + ((height / 2) - dy));
	MoveToEx(hdca, r.left + epposx, r.bottom - ((height / 2) - dy), NULL);
	LineTo(hdca, r.left + epposx, r.bottom);
	SelectObject(hdca, pen);

	if (dy == 0) {
		SetPixel(hdca, r.left + epposx, r.top + (height / 2), ceColour);
	} else {
		pen = CreatePen(PS_SOLID, 1, ceColour);
		pen = (HPEN) SelectObject(hdca, pen);
		MoveToEx(hdca, r.left + epposx, r.top + ((height / 2) - dy), NULL);
		LineTo(hdca, r.left + epposx, r.bottom - ((height / 2) - dy));
		DeleteObject(SelectObject(hdca, pen));
		if (mEnergy >= 1) {
/*
			SetPixel(hdca, r.left + epposx, r.top, RGB(255, 0, 0));
			SetPixel(hdca, r.left + epposx, r.top + (height - 1), RGB(255, 0, 0));
			SetPixel(hdca, r.left + epposx, r.top + 1, RGB(255, 0, 0));
			SetPixel(hdca, r.left + epposx, r.top + (height - 2), RGB(255, 0, 0));
*/
/*
			pen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
			pen = (HPEN) SelectObject(hdca, pen);
			MoveToEx(hdca, r.left + epposx, r.top + ((height / 2) - 3), NULL);
			LineTo(hdca, r.left + epposx, r.bottom - ((height / 2) - 3));
			DeleteObject(SelectObject(hdca, pen));
*/
			pen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
			pen = (HPEN) SelectObject(hdca, pen);
			MoveToEx(hdca, r.left + epposx, r.top, NULL);
			LineTo(hdca, r.left + epposx, r.top + 3);
			MoveToEx(hdca, r.left + epposx, r.bottom - 3, NULL);
			LineTo(hdca, r.left + epposx, r.bottom);
			DeleteObject(SelectObject(hdca, pen));
		}
	}

	epposx++;
	if (epposx >= width) {
		epposx = 0;
	}

	//	Paint cursor over next column

	pen = (HPEN) SelectObject(hdca, GetStockObject(WHITE_PEN));
	MoveToEx(hdca, r.left + epposx, r.top, NULL);
	LineTo(hdca, r.left + epposx, r.bottom);
	SelectObject(hdca, pen);

	SelectObject(hdca, oe);
	DeleteDC(hdca);
	ReleaseDC(canvas, hdc);
	paintingSpectrum = FALSE;
}

//	PAINTSPECTRUM  --  Paint the spectrum display in the canvas

static BOOL disableShown = FALSE;

static void paintSpectrum(HWND canvas)
{
	HDC hdc = GetDC(canvas), hdca = NULL;
	RECT r;
	HBRUSH br;
	HPEN pen;
	HBITMAP oe;
	int i, width, height;
	double yscale = escale;

	if (logscale) {
		yscale = log(M_E + escale) - 1;
	}

	paintingSpectrum = TRUE;

	GetClientRect(canvas, &r);

	r.left++;
	r.top++;
	r.right--;
	r.bottom--;

	width = r.right - r.left;
	height = r.bottom - r.top;

	hdca = CreateCompatibleDC(hdc);
	oe = SelectObject(hdca, easel);
	disableShown = FALSE;
	if (spectrumChanged) {
		br = (HBRUSH) SelectObject(hdca, GetStockObject(GRAY_BRUSH));
		pen = SelectObject(hdca, GetStockObject(NULL_PEN));
		Rectangle(hdca, r.left - 1, r.top - 1, width + 2, height + 2);
		SelectObject(hdca, pen);
		SelectObject(hdca, br);
		spectrumChanged = FALSE;
	}

	/*	Bar graph display (like multiband equaliser).  As the
		whole display changes every sample we paint off-screen
		and blast BLT to the display pane.  */

	if (spectrumBarGraph && (easel != NULL)) {
		br = (HBRUSH) SelectObject(hdca, GetStockObject(GRAY_BRUSH));
		pen = SelectObject(hdca, GetStockObject(NULL_PEN));
		Rectangle(hdca, r.left, r.top, width, height);
		SelectObject(hdca, pen);
		SelectObject(hdca, br);

		pen = (HPEN) SelectObject(hdca, GetStockObject(BLACK_PEN));

		for (i = 0; i < nbands; i++) {
			int barx0 = (i * width) / nbands,
				barx1 = ((i + 1) * width) / nbands,
				bary = (int) (((logscale ? (log(M_E + pspectrum[i]) - 1) : pspectrum[i]) * height) / yscale),
				cr, cg, cb;

			cr = (255 * ((nbands - 1) - i)) / (nbands - 1);
			cg = (((nbands / 2) - abs(i - (nbands / 2))) * 255) / (nbands / 2);
			cb = (255 * i) / (nbands - 1);
			br = CreateSolidBrush(RGB(cr, cg, cb));
			br = (HBRUSH) SelectObject(hdca, br);
			Rectangle(hdca, r.left + barx0, r.bottom - bary, r.left + barx1, r.bottom);
			DeleteObject(SelectObject(hdca, br));
			DeleteObject(SelectObject(hdca, pen));
		}
	}

	/*	Spectrogram display.  This is like the "waterfall" sonar display
		on an SSN, but scrolls horizontally rather than vertically.  */

#define BLACKBIAS	100
	else if (spectrumVoicePrint && (easel != NULL)) {
		if (logfreq) {
			int j, y0, y1;
			double lspectrum[256];
			double lscale = log(nbands + M_E) - 1;

			memset(lspectrum, 0, sizeof(lspectrum));
			for (i = 0; i < nbands; i++) {
				y0 = (int) (nbands * (log(i + M_E) - 1) / lscale);
				y1 = (int) (nbands * (log((i + 1) + M_E) - 1) / lscale);
				for (j = y0; j < y1; j++) {
					lspectrum[j] += pspectrum[i];
				}
			}
			memcpy(pspectrum, lspectrum, nbands * sizeof(double));
		}
		for (i = 0; i < nbands; i++) {
			int y0 = (i * height) / nbands,
				y1 = (i == (nbands - 1)) ? height : (((i + 1) * height) / nbands),
				cindex = (int) ((((logscale ? (log(M_E + pspectrum[i]) - 1) : pspectrum[i])) / yscale) * 255),
				cr, cg, cb;

/*
			if (logfreq) {
				y0 = (int) (height * (log(y0 + M_E) - 1) / yscale);
				y1 = (int) (height * (log(y1 + M_E) - 1) / yscale);
				if (i == (nbands - 1)) {
					y1 = height;
				}
			}
*/

			if (cindex < BLACKBIAS) {
				cr = cg = cb = 0;
			} else {
				cindex = 255 - (((cindex - BLACKBIAS) * 255) / (255 - BLACKBIAS));
#ifndef ZZZ
				cr = (255 * (255 - cindex)) / 255;
				cg = ((128 - abs(cindex - 128)) * 255) / 128;
				cb = (255 * cindex) / 255;
#else
				cr = cg = cb = cindex;
#endif
			}
			pen = CreatePen(PS_SOLID, 1, RGB(cr, cg, cb));
			pen = (HPEN) SelectObject(hdca, pen);
			MoveToEx(hdca, r.left + sgposx, (r.bottom - 1) - y0, NULL);
			LineTo(hdca, r.left + sgposx, (r.bottom - 1) - y1);
			DeleteObject(SelectObject(hdca, pen));
		}
		sgposx++;
		if (sgposx >= width) {
			sgposx = 0;
		}

		//	Paint cursor over next column

		pen = (HPEN) SelectObject(hdca, GetStockObject(WHITE_PEN));
		MoveToEx(hdca, r.left + sgposx, r.top, NULL);
		LineTo(hdca, r.left + sgposx, r.bottom);
		SelectObject(hdca, pen);
	}

	//	Spectrum display disabled

	else {
		char *ds = rstring(IDS_SPEC_DISABLED);
		LOGFONT lf;
		HFONT hf, of;

		lf.lfHeight = 32;
		lf.lfWidth = 0;
		lf.lfEscapement = 0;
		lf.lfOrientation = 0; 
		lf.lfWeight = FW_BOLD;
		lf.lfItalic = FALSE;
		lf.lfUnderline = FALSE;
		lf.lfStrikeOut = FALSE; 
		lf.lfCharSet = DEFAULT_CHARSET;
		lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
		lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; 
		lf.lfQuality = DEFAULT_QUALITY;
		lf.lfPitchAndFamily = DEFAULT_PITCH;
		strcpy(lf.lfFaceName, "Arial");
		hf = CreateFontIndirect(&lf);

		if (hf != NULL) {
			of = SelectObject(hdca, hf);
		}
		SetBkMode(hdca, TRANSPARENT);
		SetTextColor(hdca, RGB(192, 192, 192));
		SetTextAlign(hdca, TA_CENTER | TA_BASELINE | TA_NOUPDATECP);
		TextOut(hdca, (r.right - r.left) / 2, (r.bottom - r.top) / 2, ds, strlen(ds));
		if (hf != NULL) {
			SelectObject(hdca, of);
			DeleteObject(hf);
		}
		disableShown = TRUE;
	}

	SelectObject(hdca, oe);
	DeleteDC(hdca);
	ReleaseDC(canvas, hdc);
	paintingSpectrum = FALSE;
}

//	CLEARCANVAS  --  Clear a display frame to a given colour

static void clearCanvas(HWND canvas, COLORREF rgb)
{
	HDC hdc = GetDC(canvas);
	RECT r;
	HBRUSH br;
	HPEN pen;

	GetClientRect(canvas, &r);
	br = CreateSolidBrush(rgb);
	br = (HBRUSH) SelectObject(hdc, br);
	pen = CreatePen(PS_SOLID, 1, rgb);
	pen = (HPEN) SelectObject(hdc, pen);
	Rectangle(hdc, r.left, r.top, r.right, r.bottom);
	DeleteObject(SelectObject(hdc, pen));
	DeleteObject(SelectObject(hdc, br));
	ReleaseDC(canvas, hdc);
}

//	PAINTCANVAS  --  Refresh a display frame from its backing bitmap

static void paintCanvas(HWND canvas, HBITMAP image)
{
	if (image != NULL) {
		HDC hdc = GetDC(canvas), hdca;
		HBITMAP oe;
		RECT r;
		int width, height;

		GetClientRect(canvas, &r);
		width = r.right - r.left;
		height = r.bottom - r.top;
		hdca = CreateCompatibleDC(hdc);
		oe = SelectObject(hdca, image);
		BitBlt(hdc, r.left + 1, r.top + 1, width - 2, height - 2, hdca, 1, 1, SRCCOPY);
		SelectObject(hdca, oe);
		DeleteDC(hdca);
		ReleaseDC(canvas, hdc);
	}
}

//	CLEARBITMAP  --  Clear a backing bitmap to a constant colour

static void clearBitmap(HWND canvas, HBITMAP image, COLORREF rgb)
{
	HDC hdc = GetDC(canvas), hdca;
	HBITMAP oe;
	HBRUSH br;
	HPEN pen;
	RECT r;

	hdca = CreateCompatibleDC(hdc);
	GetClientRect(canvas, &r);
	br = CreateSolidBrush(rgb);
	br = SelectObject(hdca, br);
	pen = CreatePen(PS_SOLID, 1, rgb);
	pen = SelectObject(hdca, pen);
	oe = SelectObject(hdca, image);
	Rectangle(hdca, r.left, r.top, r.right, r.bottom);
	DeleteObject(SelectObject(hdca, pen));
	DeleteObject(SelectObject(hdca, br));
	SelectObject(hdca, oe);
	DeleteDC(hdca);
	ReleaseDC(canvas, hdc);
}

//	SPECTRALDLGPROC  --  Spectral display dialogue procedure

BOOL CALLBACK spectralDlgProc(HWND hwnd, UINT nMessage, WPARAM wParam, LPARAM lParam)
{
    switch (nMessage) {

    	case WM_INITDIALOG:
			hDlgSpectral = hwnd;
			samps_in_buffer = 0;
			epposx = sgposx = 0;
			easel = weasel = NULL;
			samples = (short *) malloc(fftsize * sizeof(short));
			if (samples != NULL) {
				pspectrum = (double *) malloc(nbands * sizeof(double));
				if (pspectrum == NULL) {
					free(samples);
					samples = NULL;
				} else {
					sourceSpectrum = malloc(fftsize);
					if (sourceSpectrum == NULL) {
						free(samples);
						samples = NULL;
						free(pspectrum);
						pspectrum = NULL;						
					}
				}
			}
			CheckDlgButton(hwnd, IDC_SPEC_REALTIME, spectrumBarGraph);
			CheckDlgButton(hwnd, IDC_SPEC_VOICEPRINT, spectrumVoicePrint);
			CheckDlgButton(hwnd, IDC_SPEC_DISABLE, !(spectrumBarGraph || spectrumVoicePrint));

			CheckDlgButton(hwnd, IDC_SPEC_TRANSMIT, spectrumTransmitOnly);
			CheckDlgButton(hwnd, IDC_SPEC_RECEIVE, spectrumReceiveOnly);
			CheckDlgButton(hwnd, IDC_SPEC_BOTH, !(spectrumTransmitOnly || spectrumReceiveOnly));

			CheckDlgButton(hwnd, IDC_SPEC_ENV_RMS, !spectrumMaxEnergy);
			CheckDlgButton(hwnd, IDC_SPEC_ENV_MAX, spectrumMaxEnergy);

			{
				HWND canvas = GetDlgItem(hwnd, IDC_SPEC_CHART);
				HDC hdc = GetDC(canvas);
				int width, height;
				RECT r;

				GetClientRect(canvas, &r);
				width = r.right - r.left;
				height = r.bottom - r.top;
				easel = CreateCompatibleBitmap(hdc, width, height);
				ReleaseDC(canvas, hdc);
				clearBitmap(canvas, easel, RGB(128, 128, 128));

				canvas = GetDlgItem(hwnd, IDC_SPEC_ENVELOPE);
				hdc = GetDC(canvas);
				GetClientRect(canvas, &r);
				width = r.right - r.left;
				height = r.bottom - r.top;
				weasel = CreateCompatibleBitmap(hdc, width, height);
				ReleaseDC(canvas, hdc);
				clearBitmap(canvas, weasel, RGB(0, 0, 0));
			}
			spectrumChanged = TRUE;
	    	return TRUE;
	    	
	    case WM_CLOSE:
	    	DestroyWindow(hwnd);
	    	return TRUE;
	    	
	    case WM_DESTROY:
			if (samples != NULL) {
				free(samples);
				samples = NULL;
			}
			if (sourceSpectrum != NULL) {
				free(sourceSpectrum);
				sourceSpectrum = NULL;
			}
			if (pspectrum != NULL) {
				free(pspectrum);
				pspectrum = NULL;
			}
			if (easel != NULL) {
				DeleteObject(easel);
				easel = NULL;
			}
			if (weasel != NULL) {
				DeleteObject(weasel);
				weasel = NULL;
			}
			spectrumEnd();
	    	hDlgSpectral = NULL;
	    	return 0;

		case WM_PAINT:
			InvalidateRect(GetDlgItem(hwnd, IDC_SPEC_CHART), NULL, FALSE);
			InvalidateRect(GetDlgItem(hwnd, IDC_SPEC_ENVELOPE), NULL, FALSE);
			paintCanvas(GetDlgItem(hwnd, IDC_SPEC_CHART), easel);
			paintCanvas(GetDlgItem(hwnd, IDC_SPEC_ENVELOPE), weasel);
			break;

		case WM_UPDATE_SPECTRUM:
#ifdef _DEBUG
			{	char s[20];

				sprintf(s, "P=%.2f  E=%d", escale, aEnergy);
				SetDlgItemText(hwnd, IDC_SPEC_SCALE, s);
			}
#endif
			paintEnergy(GetDlgItem(hwnd, IDC_SPEC_ENVELOPE));
			paintSpectrum(GetDlgItem(hwnd, IDC_SPEC_CHART));
			paintCanvas(GetDlgItem(hwnd, IDC_SPEC_CHART), easel);
			paintCanvas(GetDlgItem(hwnd, IDC_SPEC_ENVELOPE), weasel);
			break;

        case WM_COMMAND:
		    switch ((short) WM_COMMAND_ID(wParam)) {

		    	case IDOK:
		        	PostMessage(hwnd, WM_CLOSE, 0, 0L);
		        	break;
		        	
                case ID_HELP:
                	WinHelp(hwndMDIFrame, rstring(IDS_HELPFILE), HELP_KEY,
                				((DWORD) (Lrstring(IDS_HELP_SPECTRUM))));
                	holped = TRUE;
                	break;

				case IDC_SPEC_REALTIME:
				case IDC_SPEC_VOICEPRINT:
				case IDC_SPEC_DISABLE:
					spectrumBarGraph = IsDlgButtonChecked(hwnd, IDC_SPEC_REALTIME);
					spectrumVoicePrint = IsDlgButtonChecked(hwnd, IDC_SPEC_VOICEPRINT);
					spectrumChanged = TRUE;
					clearCanvas(GetDlgItem(hwnd, IDC_SPEC_CHART), RGB(128, 128, 128));
					sgposx = epposx;
					disableShown = FALSE;
					if (!(spectrumBarGraph || spectrumVoicePrint)) {
						paintSpectrum(GetDlgItem(hwnd, IDC_SPEC_CHART));
					}
					break;

				case IDC_SPEC_TRANSMIT:
				case IDC_SPEC_RECEIVE:
				case IDC_SPEC_BOTH:
					spectrumTransmitOnly = IsDlgButtonChecked(hwnd, IDC_SPEC_TRANSMIT);
					spectrumReceiveOnly = IsDlgButtonChecked(hwnd, IDC_SPEC_RECEIVE);
					break;

				case IDC_SPEC_ENV_RMS:
				case IDC_SPEC_ENV_MAX:
					spectrumMaxEnergy = IsDlgButtonChecked(hwnd, IDC_SPEC_ENV_MAX);
					break;
			}
    }
    return FALSE;
}

//	SPECTRALDIALOGUE  --  Spectral display dialogue

VOID spectralDialogue(HWND hwndParent)
{
	if (hDlgSpectral == NULL) {
		hDlgSpectral = CreateDialog(hInst, MAKEINTRESOURCE(IDD_SPECTRUM),
    					hwndParent, spectralDlgProc);
	}
}

/*	ENERGY  --  Calculate RMS and maximum energy of samples in this
				buffer for envelope display.  */

static void energy(short *samples, int nsamples, double *rms, double *emax)
{
	long i;
	double alevel = 0, mlevel = 0;

	for (i = 0; i < nsamples; i++) {
		int samp = samples[i];
		
		if (samp < 0) {
			if (samp == -32768) {
				samp = -32767;
			}
			samp = -samp;
		}
		alevel += ((double) samp) * samp;
		if (samp > mlevel) {
			mlevel = samp;
		}
	}
	alevel = sqrt(alevel / nsamples);

#ifdef _DEBUG
	aEnergy = (int) alevel;
#endif
	if (alevel < noiseFloor) {
		alevel = 0.0;
	} else {
		alevel -= noiseFloor;
	}
	if (logEnergy) {
		*rms = ((log(((double) alevel) + M_E) - 1) / (log(32767.0 + M_E) - 1));
		*emax = ((log(((double) mlevel) + M_E) - 1) / (log(32767.0 + M_E) - 1));
	} else {
		*rms = alevel / 32767.0;
		*emax = mlevel / 32767.0;
	}
}

/*	STORESAMPLE  --  Store next sample into buffer, updating spectrum
					 when it's full.  */

static void storeSample(short samp, BOOL isInput)
{
	sourceSpectrum[samps_in_buffer] = isInput;
	samples[samps_in_buffer++] = samp;
	if (samps_in_buffer >= fftsize) {
		DWORD tc = GetTickCount();

		//	Update average energy (0 to 1) of samples in buffer

		energy(samples, samps_in_buffer, &cEnergy, &mEnergy);
		ceColour = sourceSpectrum[samps_in_buffer -1] ? RGB(0, 0, 255) : RGB(0, 255, 0);

		//	Update power spectrum if it's being displayed

		if (spectrumBarGraph || spectrumVoicePrint) {
			pscale = spectrum(samples, samps_in_buffer, pspectrum, nbands, 0.0);
			if (pscale > escale) {
				//	New maximum power--set as top of scale
				escale = pscale;
			} else {
				/*	Otherwise exponentially smooth the scale factor
					for a smooth adaptive gain adjustment.  */
				escale = escale + PSMOOTH * (pscale - escale);
			}
		}

		samps_in_buffer = 0;
		if (!paintingSpectrum && ((nextSpectrumTime == 0) || (tc < nextSpectrumTime) ||
								   (tc > (nextSpectrumTime + SPECTRUM_INTERVAL)))) {
			nextSpectrumTime = tc;
			PostMessage(hDlgSpectral, WM_UPDATE_SPECTRUM, 0, 0);
		}
	}
}

/*	SPECTRUMUPDATE  --  Receive latest batch of audio samples and
						update the spectrum display, if necessary.
						If align == 0 and bytesec == 8000, the samples
						are assumed to already be in mu-law format.  */

void spectrumUpdate(LPSTR buffer, WORD buflen, DWORD channels,
					DWORD rate, DWORD bytesec, WORD align, BOOL isInput)
{

	//	Discard buffer if we're not monitoring this direction

	if ((isInput && spectrumTransmitOnly) ||
		((!isInput) && spectrumReceiveOnly)) {
			return;
	}

	/*	The job of the following code is to transform, if necessary,
		the received samples into 16 bit signed form and add them to
		the FFT array.  */

	if ((hDlgSpectral != NULL) && (samples != NULL)) {
		if (align == 2) {
			int i;
			
			for (i = 0; i < buflen / align; i++) {
				storeSample((short) (((WORD FAR *) buffer)[i]), isInput);
			} 
		} else {	// align == 1 or align == 0
			int i;
			
			for (i = 0; i < buflen; i++) {
				/*	This looks inefficient--it should just transform the 8 bit
					sample directly to 16 bit, but after all it's only two
					array accesses, which are trivial compared to the number
					of FFTs we're doing here.  */
				storeSample(audio_u2s((isInput || (align == 0)) ? (((BYTE FAR *) buffer)[i]) : audio_c2u((((BYTE FAR *) buffer)[i]))), isInput);
			} 
		}
	}
}											
