add awawa face and scream sounds

This commit is contained in:
2026-05-05 22:12:49 +02:00
parent 74b56773d9
commit 8185fc3032
16 changed files with 226 additions and 19 deletions
Binary file not shown.
Binary file not shown.
+15
View File
@@ -140,6 +140,21 @@
<Image Include="..\..\..\..\Downloads\hyrax.ico" />
<Image Include="..\..\..\..\Downloads\hyrax.jpeg" />
<Image Include="hyrax.bmp" />
<Image Include="hyrax_scream.jpeg" />
</ItemGroup>
<ItemGroup>
<Media Include="hyrax1.wav" />
<Media Include="hyrax2.wav" />
<Media Include="hyrax3.wav" />
<Media Include="hyrax4.wav" />
<Media Include="hyrax5.wav" />
<Media Include="hyrax6.wav" />
<Media Include="hyrax7.wav" />
<Media Include="hyrax8.wav" />
<Media Include="hyrax9.wav" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\..\downloads\wave3.bin" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
+37
View File
@@ -39,5 +39,42 @@
<Image Include="..\..\..\..\Downloads\hyrax.ico">
<Filter>Resource Files</Filter>
</Image>
<Image Include="hyrax_scream.jpeg">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<Media Include="hyrax1.wav">
<Filter>Resource Files</Filter>
</Media>
<Media Include="hyrax2.wav">
<Filter>Resource Files</Filter>
</Media>
<Media Include="hyrax3.wav">
<Filter>Resource Files</Filter>
</Media>
<Media Include="hyrax4.wav">
<Filter>Resource Files</Filter>
</Media>
<Media Include="hyrax5.wav">
<Filter>Resource Files</Filter>
</Media>
<Media Include="hyrax6.wav">
<Filter>Resource Files</Filter>
</Media>
<Media Include="hyrax7.wav">
<Filter>Resource Files</Filter>
</Media>
<Media Include="hyrax8.wav">
<Filter>Resource Files</Filter>
</Media>
<Media Include="hyrax9.wav">
<Filter>Resource Files</Filter>
</Media>
</ItemGroup>
<ItemGroup>
<None Include="..\..\..\..\downloads\wave3.bin">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
</Project>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

+163 -18
View File
@@ -18,10 +18,16 @@ rattatwinko - 04/05/26
#include <windowsx.h>
#include <gdiplus.h>
#include <objidl.h>
#include <mmsystem.h>
#include <math.h>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cstdint>
#include "resource.h"
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "winmm.lib")
using namespace Gdiplus;
@@ -33,13 +39,19 @@ static constexpr double GROUND_FRICTION = 0.8;
static constexpr double REST_THRESHOLD = 5.0;
static constexpr UINT TIMER_MS = 10;
static constexpr int IMAGE_RESOURCE = 111;
static constexpr int RESIZE_BORDER = 12; // make wider so easier grab
static constexpr int RESIZE_BORDER = 12; // make wider so easier grab
static constexpr int DRAG_THRESHOLD = 5; // pixels before its a drag not a click
static constexpr int WAV_COUNT = 9;
static constexpr int MAX_PEAKS = 64; // max screams per sound
static constexpr double PEAK_HOLD = 0.10; // seconds which we hold the awawa face
static constexpr double PEAK_THRESH = 0.65; // fraction of max amplitude to count as a peak
// globals
static ULONG_PTR g_gdiplusToken = 0;
static Image* g_image = nullptr;
static int g_winW = 640;
static int g_winH = 480;
static ULONG_PTR g_gdiplusToken = 0;
static Image* g_image = nullptr; // normal hyrax
static Image* g_imageScream = nullptr; // awawa hyrax
static int g_winW = 640;
static int g_winH = 480;
static LARGE_INTEGER g_freq = {};
// physics states
@@ -47,14 +59,24 @@ static double g_x = 200.0, g_y = 50.0;
static double g_vx = 0.0, g_vy = 0.0;
static double g_lastTime = 0.0;
// resize status
// resize / drag status
static bool g_resizing = false;
static bool g_dragging = false;
static bool g_didDrag = false;
static int g_dragOffsetX = 0;
static int g_dragOffsetY = 0;
static POINT g_lastMouse = {};
static POINT g_clickOrigin = {};
static double g_lastMouseTime = 0.0;
// scream face stuff
static bool g_screaming = false;
static double g_screamUntil = 0.0; // timestamp to stop screaming
static double g_soundStart = 0.0; // when playback began
static double g_peakTimes[MAX_PEAKS] = {};
static int g_peakCount = 0;
static int g_peakIdx = 0; // next peak we havent fired yet
// helper definitions
static double Now() {
LARGE_INTEGER t;
@@ -62,8 +84,9 @@ static double Now() {
return static_cast<double>(t.QuadPart) / static_cast<double>(g_freq.QuadPart);
}
static Image* LoadImageFromResource(HINSTANCE hInst, int resId) {
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(resId), RT_RCDATA);
// load any image from any rc resource type
static Image* LoadImageFromResource(HINSTANCE hInst, int resId, LPCWSTR resType) {
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(resId), resType);
if (!hRes) return nullptr;
DWORD size = SizeofResource(hInst, hRes);
@@ -101,6 +124,93 @@ static Image* LoadImageFromResource(HINSTANCE hInst, int resId) {
return img;
}
// walk RAW fucking PCM data and check for peaks
static void FindWavPeaks(const BYTE* buf, DWORD size) {
g_peakCount = 0;
g_peakIdx = 0;
if (size < 44) return;
if (memcmp(buf, "RIFF", 4) != 0 || memcmp(buf + 8, "WAVE", 4) != 0) return;
WORD channels = 0;
DWORD sampleRate = 0;
WORD bitsPerSample = 0;
const BYTE* pcm = nullptr;
DWORD pcmSize = 0;
// walk the chunks
DWORD pos = 12;
while (pos + 8 <= size) {
DWORD chunkSize = *reinterpret_cast<const DWORD*>(buf + pos + 4);
if (memcmp(buf + pos, "fmt ", 4) == 0 && chunkSize >= 16) {
channels = *reinterpret_cast<const WORD*> (buf + pos + 10);
sampleRate = *reinterpret_cast<const DWORD*>(buf + pos + 12);
bitsPerSample = *reinterpret_cast<const WORD*> (buf + pos + 22);
}
else if (memcmp(buf + pos, "data", 4) == 0) {
pcm = buf + pos + 8;
pcmSize = chunkSize;
break;
}
pos += 8 + chunkSize;
if (chunkSize & 1) pos++; // align word cause microsoft likes to fuck my ass
}
// only 16bit PCM cause everything else is just stoopid
if (!pcm || !sampleRate || bitsPerSample != 16) return;
DWORD totalSamples = pcmSize / (channels * sizeof(int16_t));
// global maximum
int maxAmp = 1;
for (DWORD i = 0; i < pcmSize / sizeof(int16_t); i++) {
int s = abs((int)*reinterpret_cast<const int16_t*>(pcm + i * sizeof(int16_t)));
if (s > maxAmp) maxAmp = s;
}
int threshold = static_cast<int>(maxAmp * PEAK_THRESH);
DWORD windowSamples = sampleRate / 50; // 20ms windows
DWORD debounce = sampleRate / 8; // 125ms min gap so we dont spam
DWORD lastPeak = 0xFFFFFFFF;
// stupid fucking logic. scan for loud moments
for (DWORD s = 0; s + windowSamples <= totalSamples; s += windowSamples / 2) {
int winMax = 0;
for (DWORD j = 0; j < windowSamples * channels; j++) {
DWORD byteOff = (s * channels + j) * sizeof(int16_t);
if (byteOff + sizeof(int16_t) > pcmSize) break;
int amp = abs((int)*reinterpret_cast<const int16_t*>(pcm + byteOff));
if (amp > winMax) winMax = amp;
}
bool gapOk = (lastPeak == 0xFFFFFFFF) || (s - lastPeak >= debounce);
if (winMax >= threshold && gapOk && g_peakCount < MAX_PEAKS) {
g_peakTimes[g_peakCount++] = static_cast<double>(s) / sampleRate;
lastPeak = s;
}
}
}
// play a random hyrax sound and prep the scream timeline
static void PlayRandomSound(HINSTANCE hInst) {
int id = IDR_WAVE1 + (rand() % WAV_COUNT);
// grab raw wave data and look for peaks ; possible cause of .rc WAVE type
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(id), L"WAVE");
HGLOBAL hData = hRes ? LoadResource(hInst, hRes) : nullptr;
BYTE* pData = hData ? static_cast<BYTE*>(LockResource(hData)) : nullptr;
DWORD sz = hRes ? SizeofResource(hInst, hRes) : 0;
if (pData && sz)
FindWavPeaks(pData, sz);
g_soundStart = Now();
PlaySoundW(MAKEINTRESOURCEW(id), hInst, SND_RESOURCE | SND_ASYNC | SND_NODEFAULT);
}
// physics "engine"
static void StepPhysics(double dt) {
const int screenW = GetSystemMetrics(SM_CXSCREEN);
@@ -113,7 +223,7 @@ static void StepPhysics(double dt) {
g_x += g_vx * dt;
g_y += g_vy * dt;
// left/ right monitor borders
// left / right monitor borders
if (g_x < 0.0) {
g_x = 0.0;
g_vx = -g_vx * RESTITUTION;
@@ -143,15 +253,18 @@ static void StepPhysics(double dt) {
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
// init the window
// init the window
case WM_CREATE: {
QueryPerformanceFrequency(&g_freq);
srand(static_cast<unsigned>(time(nullptr)));
GdiplusStartupInput si;
GdiplusStartup(&g_gdiplusToken, &si, nullptr);
HINSTANCE hInst = reinterpret_cast<CREATESTRUCT*>(lParam)->hInstance;
g_image = LoadImageFromResource(hInst, IMAGE_RESOURCE);
g_image = LoadImageFromResource(hInst, IDR_RCDATA1, RT_RCDATA);
g_imageScream = LoadImageFromResource(hInst, IDB_BITMAP1, L"JPG");
if (g_image) {
g_winW = static_cast<int>(g_image->GetWidth());
@@ -170,11 +283,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
// drag start
case WM_LBUTTONDOWN: {
g_dragging = true;
g_didDrag = false;
g_dragOffsetX = GET_X_LPARAM(lParam);
g_dragOffsetY = GET_Y_LPARAM(lParam);
g_vx = g_vy = 0.0;
GetCursorPos(&g_lastMouse);
g_clickOrigin = g_lastMouse;
g_lastMouseTime = Now();
SetCapture(hwnd);
@@ -188,6 +303,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
POINT p;
GetCursorPos(&p);
if (abs(p.x - g_clickOrigin.x) > DRAG_THRESHOLD ||
abs(p.y - g_clickOrigin.y) > DRAG_THRESHOLD)
g_didDrag = true;
const double now = Now();
const double dt = now - g_lastMouseTime;
@@ -207,12 +326,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
break;
}
// drag end
case WM_LBUTTONUP:
// drag end or click
case WM_LBUTTONUP: {
if (!g_didDrag) {
HINSTANCE hInst = reinterpret_cast<HINSTANCE>(GetWindowLongPtrW(hwnd, GWLP_HINSTANCE));
PlayRandomSound(hInst);
}
g_dragging = false;
ReleaseCapture();
break;
}
// while dragging nutz accross fohead disable OS from doing shit
case WM_ENTERSIZEMOVE:
g_resizing = true;
@@ -269,7 +393,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
// phyisks tick
// phyisks tick and scream face check
case WM_TIMER: {
const double now = Now();
const double dt = now - g_lastTime;
@@ -280,6 +404,23 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
SetWindowPos(hwnd, nullptr, static_cast<int>(g_x), static_cast<int>(g_y),
0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
// extend scream hold
if (g_peakCount > 0) {
double elapsed = now - g_soundStart;
while (g_peakIdx < g_peakCount && elapsed >= g_peakTimes[g_peakIdx]) {
g_screamUntil = now + PEAK_HOLD;
g_peakIdx++;
}
}
// flip face state
bool shouldScream = (g_imageScream && now < g_screamUntil);
if (shouldScream != g_screaming) {
g_screaming = shouldScream;
InvalidateRect(hwnd, nullptr, FALSE);
}
break;
}
@@ -296,8 +437,10 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
Graphics g(memDC);
g.SetInterpolationMode(InterpolationModeHighQualityBicubic);
if (g_image)
g.DrawImage(g_image, 0, 0, g_winW, g_winH);
// swap to scream face on peaks
Image* frame = (g_screaming && g_imageScream) ? g_imageScream : g_image;
if (frame)
g.DrawImage(frame, 0, 0, g_winW, g_winH);
BitBlt(hdc, 0, 0, g_winW, g_winH, memDC, 0, 0, SRCCOPY);
@@ -317,11 +460,13 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
DestroyWindow(hwnd);
break;
// consuela
case WM_DESTROY:
KillTimer(hwnd, 1);
delete g_image;
g_image = nullptr;
delete g_imageScream;
g_image = g_imageScream = nullptr;
GdiplusShutdown(g_gdiplusToken);
PostQuitMessage(0);
break;
+11 -1
View File
@@ -4,12 +4,22 @@
//
#define IDR_RCDATA1 111
#define IDI_ICON1 114
#define IDR_WAVE1 115
#define IDR_WAVE2 116
#define IDR_WAVE3 118
#define IDR_WAVE4 119
#define IDR_WAVE5 120
#define IDR_WAVE6 121
#define IDR_WAVE7 122
#define IDR_WAVE8 123
#define IDR_WAVE9 124
#define IDB_BITMAP1 126
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 115
#define _APS_NEXT_RESOURCE_VALUE 127
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101