253 lines
7.9 KiB
C++
253 lines
7.9 KiB
C++
// custom build and feature flags
|
||
#ifdef DEBUG
|
||
#define OPENGL_DEBUG 0
|
||
#define FULLSCREEN 0
|
||
#define DESPERATE 0
|
||
#define BREAK_COMPATIBILITY 0
|
||
#else
|
||
#define OPENGL_DEBUG 0
|
||
#define FULLSCREEN 0
|
||
#define DESPERATE 0
|
||
#define BREAK_COMPATIBILITY 0
|
||
#endif
|
||
|
||
#define POST_PASS 0
|
||
#define USE_MIPMAPS 1
|
||
#define USE_AUDIO 1
|
||
#define NO_UNIFORMS 0
|
||
|
||
#include "definitions.h"
|
||
#if OPENGL_DEBUG
|
||
#include "debug.h"
|
||
#endif
|
||
|
||
#include "glext.h"
|
||
#pragma data_seg(".shader")
|
||
#include "shaders/fragment.inl"
|
||
#if POST_PASS
|
||
#pragma data_seg(".shader")
|
||
#include "shaders/post.inl"
|
||
#endif
|
||
|
||
#include "fft.h"
|
||
|
||
#pragma data_seg(".pids")
|
||
// static allocation saves a few bytes
|
||
static int pidMain;
|
||
static int pidPost;
|
||
// static HDC hDC;
|
||
|
||
#ifndef EDITOR_CONTROLS
|
||
#pragma code_seg(".main")
|
||
void entrypoint(void)
|
||
#else
|
||
#include "editor.h"
|
||
|
||
int __cdecl main(int argc, char* argv[])
|
||
#endif
|
||
|
||
{
|
||
// initialize window
|
||
#if FULLSCREEN
|
||
ChangeDisplaySettings(&screenSettings, CDS_FULLSCREEN);
|
||
ShowCursor(0);
|
||
HWND window = CreateWindow((LPCSTR)0xC018, 0, WS_POPUP | WS_VISIBLE | WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0);
|
||
const HDC hDC = GetDC(window);
|
||
#else
|
||
#ifdef EDITOR_CONTROLS
|
||
HWND window = CreateWindow("static", 0, WS_POPUP | WS_VISIBLE, 0, 0, XRES, YRES, 0, 0, 0, 0);
|
||
HDC hDC = GetDC(window);
|
||
#else
|
||
// you can create a pseudo fullscreen window by similarly enabling the WS_MAXIMIZE flag as above
|
||
// in which case you can replace the resolution parameters with 0s and save a couple bytes
|
||
// this only works if the resolution is set to the display device's native resolution
|
||
HWND window = CreateWindow((LPCSTR)0xC018, 0, WS_POPUP | WS_VISIBLE, 0, 0, XRES, YRES, 0, 0, 0, 0);
|
||
HDC hDC = GetDC(window);
|
||
#endif
|
||
#endif
|
||
|
||
// initalize opengl context
|
||
SetPixelFormat(hDC, ChoosePixelFormat(hDC, &pfd), &pfd);
|
||
wglMakeCurrent(hDC, wglCreateContext(hDC));
|
||
|
||
// create and compile shader programs
|
||
pidMain = ((PFNGLCREATESHADERPROGRAMVPROC)wglGetProcAddress("glCreateShaderProgramv"))(GL_FRAGMENT_SHADER, 1, &fragment_frag);
|
||
#if POST_PASS
|
||
pidPost = ((PFNGLCREATESHADERPROGRAMVPROC)wglGetProcAddress("glCreateShaderProgramv"))(GL_FRAGMENT_SHADER, 1, &post_frag);
|
||
#endif
|
||
|
||
#ifdef SU_LOAD_GMDLS
|
||
su_load_gmdls();
|
||
#endif // SU_LOAD_GMDLS
|
||
// initialize sound
|
||
#ifndef EDITOR_CONTROLS
|
||
#if USE_AUDIO
|
||
LPDIRECTSOUND direct_sound;
|
||
LPDIRECTSOUNDBUFFER direct_sound_buffer;
|
||
DirectSoundCreate(0, &direct_sound, 0);
|
||
|
||
IDirectSound_SetCooperativeLevel(direct_sound, window, DSSCL_PRIORITY);
|
||
IDirectSound_CreateSoundBuffer(direct_sound, &buffer_description, &direct_sound_buffer, NULL);
|
||
|
||
LPVOID p1;
|
||
DWORD l1;
|
||
IDirectSoundBuffer_Lock(direct_sound_buffer, 0, SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT * SU_SAMPLE_SIZE, &p1, &l1, NULL, NULL, 0);
|
||
|
||
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)su_render_song, p1, 0, 0);
|
||
|
||
#endif
|
||
|
||
#else
|
||
Leviathan::Editor editor = Leviathan::Editor();
|
||
editor.updateShaders(&pidMain, &pidPost, true);
|
||
|
||
// absolute path always works here
|
||
// relative path works only when not ran from visual studio directly
|
||
Leviathan::Song track(L"audio.wav");
|
||
track.play();
|
||
double position = 0.0;
|
||
#endif
|
||
|
||
long playCursor = 0;
|
||
long lastPlayCursor = -1;
|
||
volatile float maximum = 0.0; // Helper variable to calculate maximum fft output for normalization
|
||
|
||
// Unlock buffer for next use
|
||
IDirectSoundBuffer_Unlock(direct_sound_buffer, p1, SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT * SU_SAMPLE_SIZE, NULL, NULL);
|
||
|
||
// Play sound
|
||
direct_sound_buffer->Play(0, 0, 0);
|
||
|
||
static float syncs[1 + SU_NUMSYNCS];
|
||
|
||
// Init FFT
|
||
init_hamming_window();
|
||
// FFT buffers
|
||
static float fft_input[FFT_SIZE];
|
||
static float fft_output[FFT_SIZE / 2]; // Magnitudes
|
||
static float fft_uniform[FFT_SIZE / 4];
|
||
|
||
do
|
||
{
|
||
direct_sound_buffer->GetCurrentPosition((DWORD*)&playCursor, NULL);
|
||
|
||
#if !(DESPERATE)
|
||
// do minimal message handling so windows doesn't kill your application
|
||
// not always strictly necessary but increases compatibility and reliability a lot
|
||
// normally you'd pass an msg struct as the first argument but it's just an
|
||
// output parameter and the implementation presumably does a NULL check
|
||
PeekMessage(0, 0, 0, 0, PM_REMOVE);
|
||
#endif
|
||
|
||
// render with the primary shader
|
||
((PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"))(pidMain);
|
||
#ifndef EDITOR_CONTROLS
|
||
// if you don't have an audio system figure some other way to pass time to your shader
|
||
#if USE_AUDIO
|
||
|
||
// it is possible to upload your vars as vertex color attribute (gl_Color) to save one function import
|
||
#if NO_UNIFORMS
|
||
glColor3ui(MMTime.u.sample, 0, 0);
|
||
#else
|
||
// remember to divide your shader time variable with the SAMPLE_RATE (44100 with 4klang)
|
||
//((PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i"))(0, MMTime.u.sample);
|
||
#endif
|
||
#endif
|
||
#else
|
||
position = track.getTime();
|
||
((PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i"))(0, (static_cast<int>(position*44100.0)));
|
||
|
||
#endif
|
||
|
||
/******************
|
||
* FFT
|
||
*******************/
|
||
LPVOID audio_ptr = NULL;
|
||
DWORD audio_size = 0;
|
||
|
||
// Read audio
|
||
HRESULT hr = IDirectSoundBuffer_Lock(direct_sound_buffer, 0, FFT_SIZE * sizeof(SUsample), &audio_ptr, &audio_size, NULL, NULL, DSBLOCK_FROMWRITECURSOR);
|
||
|
||
if (SUCCEEDED(hr) && audio_ptr) {
|
||
if (playCursor < ((SU_LENGTH_IN_SAMPLES * SU_CHANNEL_COUNT * SU_SAMPLE_SIZE) - (FFT_SIZE* SU_CHANNEL_COUNT * SU_SAMPLE_SIZE)))
|
||
{
|
||
SUsample* samples = (SUsample*)audio_ptr;
|
||
for (int i = 0; i < FFT_SIZE; ++i) {
|
||
fft_input[i] = (float)samples[i];
|
||
}
|
||
}
|
||
|
||
IDirectSoundBuffer_Unlock(direct_sound_buffer, audio_ptr, audio_size, NULL, 0);
|
||
}
|
||
|
||
// Calculate FFT
|
||
compute_fft(fft_input, fft_output);
|
||
|
||
// Normalize output
|
||
for (int i = 0; i < (FFT_SIZE / 2); i++)
|
||
{
|
||
//maximum = max(fft_output[i], maximum);
|
||
//fft_output[i] = fft_output[i] / maximum;
|
||
if (i < (FFT_SIZE / 4)) // Limit uniform fft size. High frequencys contain nothing interesing.
|
||
{
|
||
float gain = 50.0f;
|
||
float alpha = 0.15f; // "Hidastaa" FFT:n piikkej<65>
|
||
float threshhold = 0.05f; // Alin arvo mik<69> p<><70>stet<65><74>n shaderille (v<>hent<6E><74> "noisea")
|
||
float x_t = fft_output[i] * gain;
|
||
// Exponential smoothing kaava
|
||
// s(t) = alpha*x(t)+(1-alpha)*s(t-1)
|
||
fft_uniform[i] = (x_t < threshhold) ? 0.f : alpha*(x_t) + (1-alpha)*fft_uniform[i];
|
||
}
|
||
}
|
||
|
||
syncs[0] = (float)playCursor / (SU_SAMPLE_RATE * SU_CHANNEL_COUNT * SU_SAMPLE_SIZE); // Aika sekunteina.
|
||
|
||
for (int i = 0; i < SU_NUMSYNCS; ++i)
|
||
{
|
||
syncs[i + 1] = syncBuf[(playCursor / (2 * sizeof(SUsample)) >> 8) * SU_NUMSYNCS + i];
|
||
}
|
||
|
||
PFNGLUNIFORM1FVPROC glUniform1fvProc = ((PFNGLUNIFORM1FVPROC)wglGetProcAddress("glUniform1fv"));
|
||
glUniform1fvProc(0, SU_NUMSYNCS + 1, syncs);
|
||
glUniform1fvProc(8, FFT_SIZE / 4, fft_uniform);
|
||
|
||
glRects(-1, -1, 1, 1);
|
||
|
||
//syncs[0] = -syncs[0];
|
||
//glUniform1fvProc(0, SU_NUMSYNCS + 1, syncs);
|
||
|
||
// render "post process" using the opengl backbuffer
|
||
#if POST_PASS
|
||
glBindTexture(GL_TEXTURE_2D, 1);
|
||
#if USE_MIPMAPS
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, XRES, YRES, 0);
|
||
((PFNGLGENERATEMIPMAPPROC)wglGetProcAddress("glGenerateMipmap"))(GL_TEXTURE_2D);
|
||
#else
|
||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, XRES, YRES, 0);
|
||
#endif
|
||
((PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture"))(GL_TEXTURE0);
|
||
((PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram"))(pidPost);
|
||
((PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i"))(0, 0);
|
||
glRects(-1, -1, 1, 1);
|
||
#endif
|
||
|
||
SwapBuffers(hDC);
|
||
|
||
// handle functionality of the editor
|
||
#ifdef EDITOR_CONTROLS
|
||
editor.endFrame(timeGetTime());
|
||
position = editor.handleEvents(&track, position);
|
||
editor.printFrameStatistics();
|
||
editor.updateShaders(&pidMain, &pidPost);
|
||
#endif
|
||
if (playCursor < lastPlayCursor) {
|
||
break;
|
||
}
|
||
lastPlayCursor = playCursor;
|
||
} while(!GetAsyncKeyState(VK_ESCAPE));
|
||
|
||
ExitProcess(0);
|
||
}
|