PageUp and PageDown are the volume knob
The cabinets had no volume control - they ran at unity and left level to an external amplifier - so a player without that hardware had nowhere to turn it down but environ.ini and a restart. PageUp and PageDown now step the master volume by 0.05 while you play, from silent to double, and whatever you leave it on is written to volume.cfg beside the exe and used from then on. The environ.ini figure decides where a machine that has never been touched starts out; the keys are the knob, and a knob stays where it was left. Page keys because they produce no typed character, so they cannot collide with the character-keyed commands the engine already answers to, nothing else in RP binds them, and they are on every keyboard including tenkeyless. They are polled rather than read off the key-message path, which is worth recording because the message path looked like the obvious home for them and was tried first. RP's keyboard pump only takes WM_KEYUP, WM_SYSKEYUP and WM_CHAR off the front of the queue, and the front end runs message loops of its own, so key messages get raced for and lost: six deliberate, well-spaced presses arrived as two. Fine for the abort chord, useless for something you tap repeatedly to find a level. Reading key state directly costs nothing and cannot be dropped. That losses figure is a pre-existing property of the input path, not something this change introduced, and is worth knowing before anything else gets bound there. Builds clean, runs, and does not fire unprompted. The step function itself is proven - it was driven end to end through the message path before the switch, stepping the right way, clamping, and persisting. What I could not test from here is the polling trigger, because Windows would not hand the game foreground and injecting keys without it would have sprayed them across whatever else was open. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+76
-2
@@ -6,6 +6,16 @@
|
||||
#include "..\munga\notation.h"
|
||||
#include "openal/alc.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//
|
||||
// Master volume limits, shared by the startup load and the PgUp/PgDn step.
|
||||
// The file sits beside the exe with the other runtime state.
|
||||
//
|
||||
static const char kAudioVolumeFile[] = "volume.cfg";
|
||||
static const float kAudioVolumeStep = 0.05f;
|
||||
static const float kAudioVolumeMax = 2.0f;
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// L4AudioRenderer
|
||||
@@ -406,14 +416,33 @@ void
|
||||
{
|
||||
float value = (float)atof(setting);
|
||||
|
||||
if (value >= 0.0f && value <= 4.0f)
|
||||
if (value >= 0.0f && value <= kAudioVolumeMax)
|
||||
{
|
||||
master_volume = value;
|
||||
Tell("Audio master volume set to " << master_volume << "\n");
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Whatever the player last set with the volume keys wins over the
|
||||
// environ.ini figure: the keys are the amplifier knob, and a knob
|
||||
// stays where it was left. environ.ini sets where it starts on a
|
||||
// machine that has never been touched.
|
||||
//
|
||||
if (FILE *cfg = fopen(kAudioVolumeFile, "rt"))
|
||||
{
|
||||
float value = -1.0f;
|
||||
|
||||
if (fscanf(cfg, "%f", &value) == 1
|
||||
&& value >= 0.0f && value <= kAudioVolumeMax)
|
||||
{
|
||||
master_volume = value;
|
||||
}
|
||||
fclose(cfg);
|
||||
}
|
||||
|
||||
gRPMasterVolume = master_volume;
|
||||
alListenerf(AL_GAIN, master_volume);
|
||||
Tell("Audio master volume " << (int)(master_volume * 100.0f + 0.5f) << "%\n");
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1299,6 +1328,51 @@ Logical
|
||||
return resources_available;
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Master volume ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// The pod ran at unity and left volume to an external amplifier, so the game
|
||||
// never had a level control. Standing in for that amplifier means the player
|
||||
// needs to reach it while playing, not only through environ.ini -- hence the
|
||||
// PgUp/PgDn binding in L4Application::KeyCommandMessageHandler.
|
||||
//
|
||||
// Page keys specifically: they produce no typed character, so they cannot
|
||||
// collide with any of the engine's character-keyed commands the way '+'/'-'
|
||||
// would, they are bound to nothing in any RP layout, and they exist on
|
||||
// tenkeyless keyboards.
|
||||
//
|
||||
float gRPMasterVolume = 1.0f;
|
||||
|
||||
void
|
||||
RPAudioMasterVolumeStep(int direction)
|
||||
{
|
||||
gRPMasterVolume += (direction > 0) ? kAudioVolumeStep : -kAudioVolumeStep;
|
||||
|
||||
if (gRPMasterVolume < 0.0f) gRPMasterVolume = 0.0f;
|
||||
if (gRPMasterVolume > kAudioVolumeMax) gRPMasterVolume = kAudioVolumeMax;
|
||||
|
||||
//
|
||||
// Snap to the step grid so repeated presses cannot drift on float error and
|
||||
// land somewhere that never reads back as a round number.
|
||||
//
|
||||
gRPMasterVolume =
|
||||
(float)((int)(gRPMasterVolume / kAudioVolumeStep + 0.5f)) * kAudioVolumeStep;
|
||||
|
||||
alListenerf(AL_GAIN, gRPMasterVolume);
|
||||
|
||||
//
|
||||
// Persist immediately. A pod operator setting the level expects it to still
|
||||
// be there after the cabinet is power-cycled, and there is no settings UI to
|
||||
// hang it off.
|
||||
//
|
||||
if (FILE *cfg = fopen(kAudioVolumeFile, "wt"))
|
||||
{
|
||||
fprintf(cfg, "%.2f\n", gRPMasterVolume);
|
||||
fclose(cfg);
|
||||
}
|
||||
|
||||
Tell("Audio master volume " << (int)(gRPMasterVolume * 100.0f + 0.5f) << "%\n");
|
||||
}
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ OpenAL source pool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//
|
||||
// Sources are expensive to create and destroy and are a HARD per-context
|
||||
|
||||
@@ -6,6 +6,14 @@
|
||||
#include "l4audres.h"
|
||||
#include "openal/al.h"
|
||||
|
||||
//
|
||||
// Master volume, standing in for the amplifier the cabinets had. Stepped by
|
||||
// PgUp/PgDn (L4APP.cpp) and persisted to volume.cfg; see L4AUDRND.cpp.
|
||||
//
|
||||
extern float gRPMasterVolume;
|
||||
|
||||
void RPAudioMasterVolumeStep(int direction);
|
||||
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ L4AudioRenderer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "l4ctrl.h"
|
||||
#include "l4keybd.h"
|
||||
#include "l4app.h"
|
||||
#include "l4audrnd.h" // RPAudioMasterVolumeStep, for the PgUp/PgDn keys
|
||||
#include "l4dinput.h"
|
||||
#include "..\munga\appmgr.h"
|
||||
#include "dxutils.h"
|
||||
@@ -1513,6 +1514,61 @@ void
|
||||
// Update the PC keyboard mapping group
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
// Master volume, PgUp louder / PgDn quieter.
|
||||
//
|
||||
// The cabinets ran the game at unity and left level to an external
|
||||
// amplifier and crossover; without that hardware the player has to be able
|
||||
// to reach the volume while playing.
|
||||
//
|
||||
// POLLED, not taken off the key message below, and that is deliberate. The
|
||||
// pump below only ever consumes WM_KEYUP / WM_SYSKEYUP / WM_CHAR from the
|
||||
// front of the queue, and the front-end runs message loops of its own, so
|
||||
// key messages are raced for and routinely lost -- measured here at roughly
|
||||
// two of every six presses arriving. That is survivable for a one-shot like
|
||||
// the abort chord; it is not survivable for a control you tap repeatedly to
|
||||
// find a level. Reading the key state directly costs nothing and cannot be
|
||||
// dropped.
|
||||
//
|
||||
// Page keys because they produce no typed character, so they cannot collide
|
||||
// with the character-keyed commands the pump feeds, nothing else in RP binds
|
||||
// them, and they exist on tenkeyless keyboards.
|
||||
//
|
||||
// The foreground check keeps an alt-tabbed game from eating the volume keys
|
||||
// of whatever the player switched to.
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
{
|
||||
static int volume_up_held = 0;
|
||||
static int volume_down_held = 0;
|
||||
|
||||
int focused = 0;
|
||||
|
||||
if (HWND foreground = GetForegroundWindow())
|
||||
{
|
||||
DWORD foreground_process = 0;
|
||||
|
||||
GetWindowThreadProcessId(foreground, &foreground_process);
|
||||
focused = (foreground_process == GetCurrentProcessId());
|
||||
}
|
||||
|
||||
const int up = focused && (GetAsyncKeyState(VK_PRIOR) & 0x8000) != 0;
|
||||
const int down = focused && (GetAsyncKeyState(VK_NEXT) & 0x8000) != 0;
|
||||
|
||||
if (up && !volume_up_held)
|
||||
{
|
||||
RPAudioMasterVolumeStep(+1);
|
||||
}
|
||||
if (down && !volume_down_held)
|
||||
{
|
||||
RPAudioMasterVolumeStep(-1);
|
||||
}
|
||||
|
||||
volume_up_held = up;
|
||||
volume_down_held = down;
|
||||
}
|
||||
|
||||
if (flags.keyboardExists)
|
||||
{
|
||||
//RB 1/20/07
|
||||
|
||||
Reference in New Issue
Block a user