diff --git a/bounce_cpp/bounce_cpp.vcxproj b/bounce_cpp/bounce_cpp.vcxproj
index 5b3b94c..fb7c52f 100644
--- a/bounce_cpp/bounce_cpp.vcxproj
+++ b/bounce_cpp/bounce_cpp.vcxproj
@@ -42,7 +42,7 @@
Application
true
- v145
+ v143
Unicode
@@ -129,9 +129,13 @@
+
+
+
+
diff --git a/bounce_cpp/main.cpp b/bounce_cpp/main.cpp
index 2a338a4..20b13e6 100644
--- a/bounce_cpp/main.cpp
+++ b/bounce_cpp/main.cpp
@@ -8,6 +8,7 @@ Quick description of the app:
- A hyrax picture which bounces around your desktop, like bonzi buddy but not destructive, more of a fun thing which you can open
when you get bored.
- Run the exe youll find out.
+ - you can click and it will awawa
This is not malware or a virus, its purely for fun and entertainment purpouses.
@@ -24,7 +25,10 @@ rattatwinko - 04/05/26
#include
#include
#include
+#include
#include "resource.h"
+#include "sound_handler.h"
+#include "physics.h"
#pragma comment(lib, "gdiplus.lib")
#pragma comment(lib, "winmm.lib")
@@ -32,19 +36,10 @@ rattatwinko - 04/05/26
using namespace Gdiplus;
// consts
-static constexpr double GRAVITY = 1200.0;
-static constexpr double RESTITUTION = 0.6;
-static constexpr double AIR_DRAG = 0.999;
-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 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;
@@ -54,10 +49,8 @@ static int g_winW = 640;
static int g_winH = 480;
static LARGE_INTEGER g_freq = {};
-// physics states
-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;
+// physics object (initial position 200,50 with zero velocity)
+static physics g_physics(200.0, 50.0, 0.0, 0.0);
// resize / drag status
static bool g_resizing = false;
@@ -68,14 +61,10 @@ static int g_dragOffsetY = 0;
static POINT g_lastMouse = {};
static POINT g_clickOrigin = {};
static double g_lastMouseTime = 0.0;
+static double g_lastTime = 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
+static std::unique_ptr g_sound;
// helper definitions
static double Now() {
@@ -124,131 +113,6 @@ static Image* LoadImageFromResource(HINSTANCE hInst, int resId, LPCWSTR resType)
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(buf + pos + 4);
-
- if (memcmp(buf + pos, "fmt ", 4) == 0 && chunkSize >= 16) {
- channels = *reinterpret_cast (buf + pos + 10);
- sampleRate = *reinterpret_cast(buf + pos + 12);
- bitsPerSample = *reinterpret_cast (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(pcm + i * sizeof(int16_t)));
- if (s > maxAmp) maxAmp = s;
- }
-
- int threshold = static_cast(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(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(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(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);
- const int screenH = GetSystemMetrics(SM_CYSCREEN);
-
- g_vy += GRAVITY * dt;
- g_vx *= AIR_DRAG;
- g_vy *= AIR_DRAG;
-
- g_x += g_vx * dt;
- g_y += g_vy * dt;
-
- // left / right monitor borders
- if (g_x < 0.0) {
- g_x = 0.0;
- g_vx = -g_vx * RESTITUTION;
- }
- else if (g_x > screenW - g_winW) {
- g_x = static_cast(screenW - g_winW);
- g_vx = -g_vx * RESTITUTION;
- }
-
- // top
- if (g_y < 0.0) {
- g_y = 0.0;
- g_vy = -g_vy * RESTITUTION;
- }
-
- // bottom
- if (g_y > screenH - g_winH) {
- g_y = static_cast(screenH - g_winH);
- g_vy = -g_vy * RESTITUTION;
- g_vx *= GROUND_FRICTION;
- if (fabs(g_vy) < REST_THRESHOLD)
- g_vy = 0.0;
- }
-}
-
// win api window proc
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
@@ -267,12 +131,26 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
g_imageScream = LoadImageFromResource(hInst, IDB_BITMAP1, L"JPG");
if (g_image) {
+ /*
g_winW = static_cast(g_image->GetWidth());
g_winH = static_cast(g_image->GetHeight());
+ */
+ g_winW = 480;
+ g_winH = 480;
SetWindowPos(hwnd, nullptr, 0, 0, g_winW, g_winH,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
+ // sound handler
+ g_sound = std::make_unique(
+ hInst,
+ std::initializer_list{
+ IDR_WAVE1, IDR_WAVE2, IDR_WAVE3,
+ IDR_WAVE4, IDR_WAVE5, IDR_WAVE6,
+ IDR_WAVE7, IDR_WAVE8, IDR_WAVE9
+ }
+ );
+
g_lastTime = Now();
g_lastMouseTime = g_lastTime;
@@ -286,7 +164,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
g_didDrag = false;
g_dragOffsetX = GET_X_LPARAM(lParam);
g_dragOffsetY = GET_Y_LPARAM(lParam);
- g_vx = g_vy = 0.0;
+ g_physics.resetVelocity(); // stop all motion
GetCursorPos(&g_lastMouse);
g_clickOrigin = g_lastMouse;
@@ -311,17 +189,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
const double dt = now - g_lastMouseTime;
if (dt > 0.0) {
- g_vx = (p.x - g_lastMouse.x) / dt;
- g_vy = (p.y - g_lastMouse.y) / dt;
+ double vx = (p.x - g_lastMouse.x) / dt;
+ double vy = (p.y - g_lastMouse.y) / dt;
+ g_physics.setVelocity(vx, vy);
}
g_lastMouse = p;
g_lastMouseTime = now;
- g_x = p.x - g_dragOffsetX;
- g_y = p.y - g_dragOffsetY;
+ g_physics.setPos(p.x - g_dragOffsetX, p.y - g_dragOffsetY);
- SetWindowPos(hwnd, nullptr, static_cast(g_x), static_cast(g_y),
+ SetWindowPos(hwnd, nullptr, static_cast(g_physics.x()), static_cast(g_physics.y()),
0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
}
@@ -329,8 +207,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
// drag end or click
case WM_LBUTTONUP: {
if (!g_didDrag) {
- HINSTANCE hInst = reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_HINSTANCE));
- PlayRandomSound(hInst);
+ // play random sound
+ if (g_sound)
+ g_sound->PlayRandom();
}
g_dragging = false;
ReleaseCapture();
@@ -344,13 +223,12 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
case WM_EXITSIZEMOVE: {
g_resizing = false;
- g_vx = g_vy = 0.0;
+ g_physics.resetVelocity();
// sync gx/y with resized cords
RECT r;
GetWindowRect(hwnd, &r);
- g_x = static_cast(r.left);
- g_y = static_cast(r.top);
+ g_physics.setPos(static_cast(r.left), static_cast(r.top));
g_lastTime = Now(); // after everything is clean then redraw
break;
@@ -400,27 +278,23 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
g_lastTime = now;
if (!g_dragging && !g_resizing) {
- StepPhysics(dt);
- SetWindowPos(hwnd, nullptr, static_cast(g_x), static_cast(g_y),
+ int screenW = GetSystemMetrics(SM_CXSCREEN);
+ int screenH = GetSystemMetrics(SM_CYSCREEN);
+ g_physics.update(dt, screenW, screenH, g_winW, g_winH);
+ SetWindowPos(hwnd, nullptr, static_cast(g_physics.x()), static_cast(g_physics.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++;
+ // use sound handler instead of doing this in main
+ if (g_sound) {
+ g_sound->Update(now);
+ bool shouldScream = (g_imageScream && g_sound->IsAwAwA(now));
+ if (shouldScream != g_screaming) {
+ g_screaming = shouldScream;
+ InvalidateRect(hwnd, nullptr, FALSE);
}
}
- // flip face state
- bool shouldScream = (g_imageScream && now < g_screamUntil);
- if (shouldScream != g_screaming) {
- g_screaming = shouldScream;
- InvalidateRect(hwnd, nullptr, FALSE);
- }
-
break;
}
@@ -460,10 +334,11 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
DestroyWindow(hwnd);
break;
-
+
// consuela
case WM_DESTROY:
KillTimer(hwnd, 1);
+ g_sound.reset(); // clean up sound handler
delete g_image;
delete g_imageScream;
g_image = g_imageScream = nullptr;
@@ -491,7 +366,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow) {
WS_EX_TOPMOST,
L"Hyrax", L"",
WS_POPUP,
- static_cast(g_x), static_cast(g_y),
+ static_cast(g_physics.x()), static_cast(g_physics.y()),
g_winW, g_winH,
nullptr, nullptr, hInstance, nullptr
);
diff --git a/bounce_cpp/physics.cpp b/bounce_cpp/physics.cpp
new file mode 100644
index 0000000..e39f6e9
--- /dev/null
+++ b/bounce_cpp/physics.cpp
@@ -0,0 +1,74 @@
+/*
+Preamble:
+ These exact physics calculations are documented in a TeX Document soon to be in the source tree of
+ the project. (as of 06/05/26 not in the tree)
+
+ The physics calculations are done using a Semi-Implicit Euler integrator. (big words)
+
+ | Parameter | Value | Description | Mapped code identifier |
+ |--------------------|--------|------------------------------------------------|------------------------------|
+ | `GRAVITY` | 1200.0 | Downward acceleration (px s⁻²) | `GRAVITY` (constant) |
+ | `RESTITUTION` | 0.6 | Energy retention after bounce | `RESTITUTION` (constant) |
+ | `AIR_DRAG` | 0.999 | Velocity multiplier per frame | `AIR_DRAG` (constant) |
+ | `GROUND_FRICTION` | 0.8 | Horizontal speed reduction on ground | `GROUND_FRICTION` (constant) |
+ | `REST_THRESHOLD` | 5.0 | Minimum `\|v_y\|` to consider at rest | `REST_THRESHOLD` (constant) |
+
+
+ 06/05/26 - rattatwinko ; when cleaning the source code
+*/
+
+#include "physics.h"
+#include
+
+physics::physics() : physics(0.0, 0.0) {}
+
+physics::physics(double x, double y, double vx, double vy)
+ : m_x(x), m_y(y), m_vx(vx), m_vy(vy) {
+}
+
+void physics::update(double dt, int screenWidth, int screenHeight, int objWidth, int objHeight) {
+ m_vy += GRAVITY * dt;
+ m_vx *= AIR_DRAG;
+ m_vy *= AIR_DRAG;
+
+ m_x += m_vx * dt;
+ m_y += m_vy * dt;
+
+ const double rightBound = static_cast(screenWidth - objWidth);
+ const double bottomBound = static_cast(screenHeight - objHeight);
+
+ if (m_x < 0.0) {
+ m_x = 0.0;
+ m_vx = -m_vx * RESTITUTION;
+ }
+ else if (m_x > rightBound) {
+ m_x = rightBound;
+ m_vx = -m_vx * RESTITUTION;
+ }
+
+ if (m_y < 0.0) {
+ m_y = 0.0;
+ m_vy = -m_vy * RESTITUTION;
+ }
+ else if (m_y > bottomBound) {
+ m_y = bottomBound;
+ m_vy = -m_vy * RESTITUTION;
+ m_vx *= GROUND_FRICTION;
+ if (std::fabs(m_vy) < REST_THRESHOLD)
+ m_vy = 0.0;
+ }
+}
+
+void physics::setPos(double x, double y) {
+ m_x = x;
+ m_y = y;
+}
+
+void physics::setVelocity(double vx, double vy) {
+ m_vx = vx;
+ m_vy = vy;
+}
+
+void physics::resetVelocity() {
+ m_vx = m_vy = 0.0;
+}
\ No newline at end of file
diff --git a/bounce_cpp/physics.h b/bounce_cpp/physics.h
new file mode 100644
index 0000000..3e8187b
--- /dev/null
+++ b/bounce_cpp/physics.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#define scd static constexpr double
+
+class physics
+{
+public:
+ scd GRAVITY = 1200.0;
+ scd RESTITUTION = 0.6;
+ scd AIR_DRAG = 0.999;
+ scd GROUND_FRICTION = 0.8;
+ scd REST_THRESHOLD = 5.0;
+
+ physics();
+ physics(double x, double y, double vx = 0.0, double vy = 0.0);
+ void update(double dt, int screenW, int screenH, int objW, int objH);
+
+ // getters / setters
+ double x() const { return m_x; }
+ double y() const { return m_y; }
+ double vx() const { return m_vx; }
+ double vy() const { return m_vy; }
+ void setPos(double x, double y);
+ void setVelocity(double vx, double vy);
+ void resetVelocity();
+
+private:
+ double m_x, m_y;
+ double m_vx, m_vy;
+};
+
diff --git a/bounce_cpp/sound_handler.cpp b/bounce_cpp/sound_handler.cpp
new file mode 100644
index 0000000..ec636a2
--- /dev/null
+++ b/bounce_cpp/sound_handler.cpp
@@ -0,0 +1,135 @@
+#include "sound_handler.h"
+#include
+#include
+
+sound_handler::sound_handler(HINSTANCE hInst, std::initializer_list waveIds)
+ : m_hInst(hInst), m_wavIds(waveIds)
+{
+ for (int id : m_wavIds)
+ m_peaks.push_back(AnalyzeWavResource(id));
+}
+
+void sound_handler::PlayRandom()
+{
+ if (m_wavIds.empty()) return;
+
+ int idx = rand() % static_cast(m_wavIds.size());
+ m_currentWave = idx;
+ m_soundStart = Now();
+ m_nextPeak = 0;
+
+ int id = m_wavIds[idx];
+ PlaySoundW(MAKEINTRESOURCEW(id), m_hInst,
+ SND_RESOURCE | SND_ASYNC | SND_NODEFAULT);
+}
+
+void sound_handler::Update(double now)
+{
+ if (m_currentWave < 0 || m_currentWave >= static_cast(m_peaks.size()))
+ return;
+
+ const auto& peaks = m_peaks[m_currentWave].times;
+ double elapsed = now - m_soundStart;
+
+ while (m_nextPeak < static_cast(peaks.size()) && elapsed >= peaks[m_nextPeak]) {
+ m_screamUntil = now + p_hold;
+ ++m_nextPeak;
+ }
+}
+
+bool sound_handler::IsAwAwA(double now) const
+{
+ return now < m_screamUntil;
+}
+
+sound_handler::PeakData sound_handler::AnalyzeWavResource(int resId)
+{
+ PeakData data;
+
+ HRSRC hRes = FindResource(m_hInst, MAKEINTRESOURCE(resId), L"WAVE");
+ if (!hRes) return data;
+
+ HGLOBAL hData = LoadResource(m_hInst, hRes);
+ if (!hData) return data;
+
+ DWORD size = SizeofResource(m_hInst, hRes);
+ const BYTE* buf = static_cast(LockResource(hData));
+ if (!buf) return data;
+
+ if (size < 44) return data;
+ if (memcmp(buf, "RIFF", 4) != 0 || memcmp(buf + 8, "WAVE", 4) != 0)
+ return data;
+
+ WORD channels = 0;
+ DWORD sampleRate = 0;
+ WORD bitsPerSample = 0;
+ const BYTE* pcm = nullptr;
+ DWORD pcmSize = 0;
+
+ DWORD pos = 12;
+ while (pos + 8 <= size) {
+ DWORD chunkSize = *reinterpret_cast(buf + pos + 4);
+
+ if (memcmp(buf + pos, "fmt ", 4) == 0 && chunkSize >= 16) {
+ channels = *reinterpret_cast(buf + pos + 10);
+ sampleRate = *reinterpret_cast(buf + pos + 12);
+ bitsPerSample = *reinterpret_cast(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++;
+ }
+
+ if (!pcm || bitsPerSample != 16) return data;
+
+ DWORD totalSamples = pcmSize / (channels * sizeof(int16_t));
+
+ int maxAmp = 1;
+ for (DWORD i = 0; i < pcmSize / sizeof(int16_t); i++) {
+ int s = abs(static_cast(
+ *reinterpret_cast(pcm + i * sizeof(int16_t))));
+ if (s > maxAmp) maxAmp = s;
+ }
+
+ int threshold = static_cast(maxAmp * p_thresh);
+ DWORD windowSamples = sampleRate / 50; // 20 ms windows
+ DWORD debounce = sampleRate / 8; // 125 ms minimum gap
+ DWORD lastPeak = 0xFFFFFFFF;
+
+ 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(static_cast(
+ *reinterpret_cast(pcm + byteOff)));
+ if (amp > winMax) winMax = amp;
+ }
+
+ bool gapOk = (lastPeak == 0xFFFFFFFF) || (s - lastPeak >= debounce);
+ if (winMax >= threshold && gapOk && data.times.size() < p_max) {
+ data.times.push_back(static_cast(s) / sampleRate);
+ lastPeak = s;
+ if (data.times.size() >= p_max) break;
+ }
+ }
+
+ return data;
+}
+
+double sound_handler::Now()
+{
+ static LARGE_INTEGER freq = [] {
+ LARGE_INTEGER f;
+ QueryPerformanceFrequency(&f);
+ return f;
+ }();
+ LARGE_INTEGER t;
+ QueryPerformanceCounter(&t);
+ return static_cast(t.QuadPart) / static_cast(freq.QuadPart);
+}
\ No newline at end of file
diff --git a/bounce_cpp/sound_handler.h b/bounce_cpp/sound_handler.h
new file mode 100644
index 0000000..0b59675
--- /dev/null
+++ b/bounce_cpp/sound_handler.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+
+using namespace std;
+
+class sound_handler
+{
+public:
+ struct PeakData {
+ vector times;
+ };
+
+private:
+ HINSTANCE m_hInst;
+ vector m_wavIds;
+ vector m_peaks;
+
+ int m_currentWave = -1;
+ double m_soundStart = 0.0; // time of playsound
+ int m_nextPeak = 0;
+ double m_screamUntil = 0.0;
+
+ static constexpr double p_hold = 0.10;
+ static constexpr double p_thresh = 0.65;
+ static constexpr int p_max = 64;
+
+public:
+ sound_handler(HINSTANCE hInst, initializer_list waveIds);
+ void PlayRandom();
+ void Update(double now);
+ bool IsAwAwA(double now) const;
+
+private:
+ PeakData AnalyzeWavResource(int resId);
+ static double Now();
+};
\ No newline at end of file