winamp/Src/Winamp/SA.cpp
2024-09-24 14:54:57 +02:00

314 lines
5.8 KiB
C++

/*
Spectrum Analyzer
*/
#include "Main.h"
#include <math.h>
#include "../nu/threadname.h"
static int last_pos;
typedef struct sa_l
{
int timestamp;
unsigned char data[2*75];
char which;
} sa_l;
static int sa_fps = 76;
static sa_l *sa_bufs;
static int sa_position;
static int sa_length, sa_size;
static CRITICAL_SECTION cs;
void sa_init(int numframes)
{
EnterCriticalSection(&cs);
sa_length=0;
if (numframes < 1) numframes = 1;
if (numframes > sa_size)
{
free(sa_bufs);
sa_bufs = (sa_l *)calloc(numframes, sizeof(sa_l));
sa_size=numframes;
}
sa_position = 0;
sa_length = numframes;
last_pos = 0;
LeaveCriticalSection(&cs);
}
void sa_deinit(void)
{
EnterCriticalSection(&cs);
//if (sa_bufs)
// {
// free(sa_bufs);
// sa_bufs = 0;
sa_length = 0;
//}
LeaveCriticalSection(&cs);
}
int sa_add(char *values, int timestamp, int csa)
{
EnterCriticalSection(&cs);
if (!sa_bufs || sa_length == 0)
{
LeaveCriticalSection(&cs);
return 1;
}
if (sa_length == 1)
{
sa_position = 0;
}
if (csa == 3) csa = 1; // dont let it happen unless it has a high bit set
csa &= 0x7fffffff;
sa_bufs[sa_position].timestamp = timestamp;
sa_bufs[sa_position].which = (char)csa;
if (csa & 1)
{
memcpy(sa_bufs[sa_position].data, values, 75);
values += 75;
}
else
memset(sa_bufs[sa_position].data, 0, 75);
if (csa & 2)
memcpy(sa_bufs[sa_position].data + 75, values, 75);
else
memset(sa_bufs[sa_position].data + 75, 0, 75);
sa_position++;
if (sa_position >= sa_length) sa_position -= sa_length;
LeaveCriticalSection(&cs);
return 0;
}
char *sa_get(int timestamp, int csa, char data[75*2+8])
{
static int sa_pos;
int closest = 1000000, closest_v = -1;
EnterCriticalSection(&cs);
if (!sa_bufs || sa_length==0)
{
LeaveCriticalSection(&cs);
return 0;
}
if (sa_length == 1)
{
memcpy(data, sa_bufs[0].data, 75*2);
LeaveCriticalSection(&cs);
return (data);
}
int i = last_pos;
for (int x = 0; x < sa_length; x ++)
{
if (i >= sa_length) i = 0;
int d = timestamp - sa_bufs[i].timestamp;
if (d < 0) d = -d;
if (d < closest)
{
closest = d;
closest_v = i;
}
else if (closest <= 6) break;
i++;
}
if (closest < 400 && closest_v >= 0 && sa_bufs[closest_v].which & csa)
{
sa_pos = 0;
last_pos = closest_v;
memcpy(data, sa_bufs[closest_v].data, 75*2);
LeaveCriticalSection(&cs);
return data;
}
if (closest_v < 0 || !(sa_bufs[closest_v].which & csa) || closest > 400)
{
memset(data, 0, 75);
data[(sa_pos % 150) >= 75 ? 149 - (sa_pos % 150) : (sa_pos % 150)] = 15;
for (int x = 0; x < 75; x ++)
data[x + 75] = (char) (int) (7.0 * sin((sa_pos + x) * 0.1));
sa_pos++;
LeaveCriticalSection(&cs);
return data;
}
LeaveCriticalSection(&cs);
return 0;
}
volatile int sa_override;
void export_sa_setreq(int want)
{
EnterCriticalSection(&cs);
sa_override = want;
LeaveCriticalSection(&cs);
}
char *export_sa_get_deprecated()
{
static char data[75*2 + 8];
int now = in_getouttime();
char *p = sa_get(now, 3, data);
if (!p) memset(data, 0, 75*2);
return data;
}
char *export_sa_get(char data[75*2 + 8])
{
try
{
int now = in_getouttime();
char *p = sa_get(now, 3, data);
if (!p) memset(data, 0, 75*2);
}
catch(...) {}
return data;
}
#pragma optimize("", off) // for some reason, optimizations are breaking the case statement in bivis_thread
#define KILL_EVENT 0
#define BLANK_EVENT 1
#define ON_EVENT 2
#define NUM_EVENTS 3
#define saKillEvent saEvents[0]
#define saBlankEvent saEvents[1]
#define saOnEvent saEvents[2]
HANDLE saEvents[NUM_EVENTS] = {0};
static int SA_Wait()
{
if (WaitForSingleObject(saKillEvent, 16) == WAIT_OBJECT_0)
return KILL_EVENT;
if (WaitForSingleObject(saBlankEvent, 0) == WAIT_OBJECT_0)
return BLANK_EVENT;
return WaitForMultipleObjects(NUM_EVENTS, saEvents, FALSE, INFINITE)-WAIT_OBJECT_0;
}
static DWORD WINAPI bivis_thread(void *none)
{
int cycleCount=0;
__int8 data[75*2 + 8] = {0};
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
SetThreadName((DWORD)-1, "Classic Viz");
while (1)
{
switch(SA_Wait())
{
case KILL_EVENT:
return 0;
case BLANK_EVENT:
draw_sa(NULL, 1);
break;
case ON_EVENT:
{
int draw=0;
if (++cycleCount < config_saref)
draw=0;
else
{
cycleCount=0;
draw=1;
}
if (config_sa
&& !paused
&& playing
&& !config_minimized
&& (config_mw_open || (config_pe_open && config_pe_width >= 350 && config_pe_height != 14))
&& (!config_disvis || !vis_running()))
{
int a = in_getouttime();
int t = config_sa;
//if ((config_windowshade&&config_mw_open) && t == 1) t=4;
char *c = sa_get(a, t, data);
if (c)
{
if (t == 2) c += 75;
else memset(c + 75, 0, 4);
draw_sa((unsigned char*)c, draw);
}
}
}
break;
}
}
return 0;
}
#pragma optimize("", on)
HANDLE saThread=0;
void SpectralAnalyzer_Create()
{
DWORD threadId = 0;
InitializeCriticalSection(&cs);
saKillEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
saBlankEvent= CreateEvent(NULL, FALSE, FALSE, NULL);
saOnEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
saThread = (HANDLE) CreateThread(NULL, 256*1024, (LPTHREAD_START_ROUTINE) bivis_thread, 0, 0, &threadId);
//cut: done on thread - SetThreadPriority(saThread, THREAD_PRIORITY_HIGHEST);
sa_length=sa_size=0;
VU_Create();
}
void SpectralAnalyzer_Destroy()
{
VU_Destroy();
SetEvent(saKillEvent);
WaitForSingleObject(saThread, INFINITE);
CloseHandle(saThread);
saThread = 0;
CloseHandle(saKillEvent);
CloseHandle(saBlankEvent);
CloseHandle(saOnEvent);
DeleteCriticalSection(&cs);
free(sa_bufs);
sa_bufs=0;
sa_size=0;
}
volatile int sa_curmode;
/*
@param mode -1==shutdown 0==none, 1==spectral analyzer, 2==oscilloscope
*/
void sa_setthread(int mode)
{
if (mode == -1)
mode=0;
sa_curmode = mode;
if (mode)
{
SetEvent(saOnEvent);
}
else
{
ResetEvent(saOnEvent);
SetEvent(saBlankEvent);
}
}