a417175da87595dd87447d23877d087825cfcb5f
6
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
25e25260b1 |
Home and End are the bass knob
The volume keys wanted a partner, and the bass trim could not be one as it stood: it scaled the sample data as it loaded, so by the time anyone pressed a key the audio was already sitting in OpenAL buffers and nothing short of a restart would move it. So the trim is now a per-zone gain applied in the mix instead. Each buffer's depth - how much of the low band it occupies - is still worked out once at load from its playback rate, but the trim itself is read every frame, which is what lets Home and End move it while sounds are playing. It is the better form regardless: no rewriting of sample data, and no quantisation on top of audio that has already been through one gain stage. Home raises, End lowers, in steps of 0.05, and the setting is written to bass.cfg beside the exe exactly as the volume writes volume.cfg. Together with PageUp and PageDown that is the amplifier and the crossover the cabinets had in hardware and a desktop does not. Builds clean, runs, and neither knob fires unprompted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
4e8392fcfb |
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> |
||
|
|
523f713a30 |
Volume and bass knobs, for players without an amplifier
The cabinets ran the game at unity and shaped volume and tone outside it, in an external amplifier and a 3-way crossover. That is why there is no master volume anywhere in the original code and none in AUDIO.INI - an operator turned a knob on an amp. A desktop player has no amp and no crossover, and the recovered soundbanks are a good deal livelier than what 4.12 shipped with, so the game has to offer the two controls the pod got from hardware. RP412AUDIOVOLUME, 0.0 to 4.0, is the amplifier: a listener gain, which the port had never set at all. RP412AUDIOBASS, 0.0 to 1.0, is the crossover's low band. Both default to leaving the mix exactly as the pod played it, so neither changes anything for anyone who does not go looking. The bass trim is not a filter, and the reason is worth writing down: the OpenAL we ship is Creative's, not OpenAL Soft, and it implements only AL_FILTER_LOWPASS. It rejects highpass and bandpass outright. A bandpass would have been the tidy answer, carrying the authored brightness model on GAINHF and the trim on GAINLF across the single direct filter a source gets. It is not on offer. So the trim scales sample data as it loads, which suits how this low end is actually built: the weight lives in discrete deep layer zones whose per-zone tuning bakes out to a very low playback rate - thirteen zones below 8kHz, three to five octaves under their recorded pitch, against four fifths of the set at 22kHz and up. Baked rate is a dependable proxy for band, so pulling down the low-rate zones is a real low-band trim and not a blunt cut. It eases in below 22kHz and reaches full depth at 5.5kHz. Caught while building this, and the reason for the probe: EFX_Initialize checks alGetError after configuring the scratch filter, so asking for a filter type the driver refuses leaves an error pending and takes the entire bridge down - reverb included. The bandpass attempt did precisely that and would have silently killed the reverb and brightness work. Initialize now survives losing the filter and says so. Builds clean, runs with both knobs set and with neither. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e133d4c993 |
Sounds recycle their voices instead of churning through them
Recovering the soundbanks took voice demand per sound from about one zone to about two and a half, and the audio path allocated an OpenAL source for every sound event and destroyed it again on release. Sources are a hard per-context resource - this driver grants 256 - so that churn doubled at exactly the moment it got more expensive. Sources are now generated once and recycled through a free list: measured, three sources generated across twelve thousand acquisitions. The BT tree reached the same conclusion the expensive way, from field logs full of failed acquisitions: raising the source budget is not the fix, because the ceiling also acts as a governor and more voices mixing is real CPU during exactly the busiest moments. Recycling is the fix, and it costs nothing. Two older bugs were sitting underneath, both reproduced against the driver rather than assumed: Releasing a set leaked it. alDeleteSources is atomic - one bad name in the array and nothing at all is deleted. ReleaseSourceSet handed it the whole fixed-size array and then parked the slots at -1, so any partial set, and any double release, leaked every source it held. Sources are now handed back one at a time and slots park at 0, which is never a valid name. A source set began life uninitialised. The constructor set only the count, and the acquire path decided whether a slot was already filled by asking OpenAL about uninitialised stack garbage. Garbage that happened to match a live name meant two sounds silently sharing one source. Pooling would have made that more likely, not less, since it keeps small names in circulation. Recycled sources are scrubbed before parking - stopped, buffer detached, looping, gain, pitch, relative flag, position and velocity reset, and the EFX filter and reverb send dropped. Without that last part a dry cockpit sound inherits the wet send of whatever 3D source held the name before it. Verified: a deliberately dirtied source comes back clean. Builds clean. Runs with memory and handle count flat. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
d361a0b8be |
The sound effects play at the pitch they were written at
Red Planet's original AWE32 soundbanks are back in the tree, and the game's sound effects are now generated from them instead of from an incomplete one-off extraction. AUDIO1.RES and AUDIO2.RES come from the 1996 release in the TeslaRel410 archive, hash-identical. AUDIO.INI has named them all along - they were simply never carried into the port. tools/rp_sf2extract.py reads them and regenerates both the WAV set and RP_L4/WTPresets.cpp, so the assets are reproducible from the banks rather than hand-maintained. Two things were wrong with the old set: Pitch. Every shipped WAV was flat 44100 Hz with the banks' tuning discarded, so 202 of the 219 zones played at the wrong speed - the worst by nine semitones. The EMU8000's per-zone root key and tuning are now baked into each file's declared sample rate, which is exact and needs no engine change. Layers that were meant to be deep now are: a collision sub-thud that lasted 18 milliseconds at the wrong rate is a 0.66 second one at 1228 Hz. Missing layers. 93 presets were short of zones and 176 were missing outright, 219 of 395. Nothing was lost recovering them - the 46 preset slots that disappeared were all empty placeholders. The old files were also over-read, running past the end of their sample into whatever PCM came next; WellheadDrill02a was six seconds where the bank says eight hundred milliseconds. Every one of the 395 files now matches its bank record exactly. Also baked in: per-zone layer attenuation, and the static resonant low-pass the EMU8000 applied in hardware. Measured while doing it, and worth knowing: RP's banks contain no key-splits at all - every multi-zone preset is a pure layer stack - and no preset has more than four zones, which is what the engine's own "AWE appears to only play 1st 4 voices" warning has been asserting since 1995. Still to do: loop regions and the release fades, which 349 zones ask for and which need new SAMPLEINFO fields. And voice demand per sound has gone from about one zone to about two and a half, so the per-event alGenSources and alDeleteSources churn roughly doubles - the BT tree measured pooling as the fix for that, and a CPU win besides. Builds clean. The extreme baked rates, 1228 Hz up to 88200, were checked through the real path - libsndfile, alBufferData, alSourcePlay - and all load. Not yet listened to on the pod. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ce1b0ab9c3 |
Sounds fade, dull and doppler with distance again
The OpenAL port kept the whole authored audio model and then threw most of
its output away. Every frame the engine computed a distance-attenuation
curve, a high-frequency rolloff, doppler cents, a reverb level and a
front/rear placement, and every one of those consumers had been commented
out when the two AWE32 cards were replaced. What reached the speakers was
OpenAL's own defaults instead: a straight-line fade to silence, no
filtering, doppler at the wrong constants with an inverted velocity, no
reverb, and every cockpit sound dead centre.
Restored, per AUDIO.INI, which is byte-identical to the file that shipped
in August 1995:
- the authored knee/rolloff distance curve, replacing AL_LINEAR_DISTANCE.
This also un-blinds the transient cull, the voice-steal weighting and
the mix ducking, which all key off it and were treating far sources as
full presence
- the CC7 squared volume law; writing the scale linearly ran everything
about 6 dB hot at mid-scale
- brightness and distance muffling, and the wet-exterior/dry-cockpit
reverb split, both through a new OpenAL EFX bridge
- doppler on the moving-source path only, as the original had it
- front/rear placement from the authored position enum
The larger find is that AL_PITCH was never called anywhere in the tree, so
the entire pitch chain was inert - not only doppler but pitch_mix_offset,
which our own sequences author 97 times. Doppler alone would have changed
nothing audible.
Note pitch is applied for parity with the BT engine but is identity here:
our content predates NoteAudioControlID, so every source runs at note 60.
Builds clean on VS2022 Release|Win32. Smoke-tested against vRIO on COM1 -
reaches gameplay and holds a steady frame loop. ALC_EXT_EFX is present on
the build machine with all nine entry points, so the filter and reverb work
is live rather than inert. Not yet listened to on the pod, which is the
real test: the volume law changes the level of everything.
docs/SOUND.md documents the original two-card quadraphonic design, where
the surviving original assets are, and what remains.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|