📄 spectral.c
字号:
/*
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));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -