Weapon visuals + aim: beams render from the guns; pick self-target fixes
Renderer (dpl3-revive/patha): - Instance visibility honored at DRAW time (stored word 4 = live dpl_SetInstanceVisibility field): laser beams (4-frame pulses) and missile models now render; cache tags w3 0/1 instances 'gated' - PSFX world bursts (dcs=0 muzzle/impact/explosion) rendered; psfx storage list-based so one-shot bursts stop collapsing onto one key - fix_degenerate applied to INSTANCE chains (was camera-only): the beam chain rides four rank-2 rig DCSs; composed rank-2 drew beams away from the guns. Offline f1635 now shows both arm beams converging on the aim point - First-person canopy cull: the always-armed cockpit model at the head drew as a vertical line down screen center (user-confirmed fixed live) Device (vpx-device/vpxlog.cpp): - CAM backchannel on the fifosock: bridge streams its validated camera (+ vehicle root DCS); raycast_pick aims along the player's real look instead of the static view-node pose, which had locked every missile/beam onto one fixed wrong world point - raycast_pick skips hidden-until-armed instances and the player's own articulation subtree: picking own beams created a target feedback loop, picking own missiles retargeted salvos mid-flight, and a picked missile's deleted DCS froze the game (arena2 crash) Sound (vpx-device/vweawe.cpp): stereo-linked peak limiter replaces the hard output clamp (the "generator out" crackle); VWE_AWE_LOG reports pre-limit peaks + duck counts, enabled in launch_pod.ps1. Missile-fire crackle persists: per-voice EMU8000 clamp suspected. Eggs/confs: TESTAR2.EGG + gauge_arena2_sound.conf (map=arena2 city variant); cavern.egg (console-era reference); plasma display on COM2 in the gauge confs; gauge_arena_net_sound.conf (slirp net stack). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,8 +22,10 @@
|
||||
* from the emulation thread: the real cards were autonomous silicon, so
|
||||
* sustained voices keep sounding even while the emulation thread stalls
|
||||
* (e.g. RIO retry storms during mission staging). Both cards are summed
|
||||
* into one stereo stream (headset); per-card host routing for the real
|
||||
* 4-speaker cockpit comes later (see SOUND-NOTES.md).
|
||||
* into one stereo stream (headset) through a peak limiter (the raw voice
|
||||
* accumulator routinely exceeds int16; hard-clamping it was the "generator
|
||||
* out" speech crackle -- the SBK samples themselves are clean); per-card
|
||||
* host routing for the real 4-speaker cockpit comes later (SOUND-NOTES.md).
|
||||
*
|
||||
* Environment (host-side, like the VPX device):
|
||||
* VWE_AWE32=1 enable the device (inert otherwise)
|
||||
@@ -217,15 +219,28 @@ static volatile LONG awe_stop = 0;
|
||||
#define AWE_SLOTS 28 /* absolute pool size */
|
||||
static WAVEHDR awe_hdr[AWE_SLOTS];
|
||||
static int16_t awe_slotbuf[AWE_SLOTS][AWE_SLOT_FRAMES * 2];
|
||||
static int16_t awe_cardbuf[2][AWE_SLOT_FRAMES * 2];
|
||||
static int32_t awe_cardbuf[2][AWE_SLOT_FRAMES * 2];
|
||||
|
||||
/* Output limiter (always on). The EMU8000 core sums its 32 voices into a raw
|
||||
* int32 accumulator; a busy mix (speech over the engine loop, weapon hits)
|
||||
* easily exceeds int16 and the old hard clamp flat-topped it -- the user's
|
||||
* "generator out" crackle (baseline WAV from the SBK proved the sample itself
|
||||
* is clean). Instead of clipping, duck: instant attack to keep the summed
|
||||
* peak under AWE_LIM_TARGET, ~46ms release back to unity. Stereo-linked so
|
||||
* the image doesn't wander. The safety clamp after it should never engage. */
|
||||
#define AWE_LIM_TARGET 32000
|
||||
static int32_t awe_lim_gain = 1 << 16; /* Q16, <= 1.0 */
|
||||
static unsigned long awe_lim_engaged = 0; /* frames ducked (diagnostic) */
|
||||
static int awe_prelim_peak[2]; /* per-card pre-limit |peak| */
|
||||
|
||||
static void awe_render_chunk(emu8k_t *card, int ci, unsigned n) {
|
||||
wavetable_pos_global = (int)n;
|
||||
emu8k_update(card);
|
||||
for (unsigned i = 0; i < n * 2; i++) {
|
||||
int32_t v = card->buffer[i] >> awe_shift;
|
||||
if (v > 32767) v = 32767; else if (v < -32768) v = -32768;
|
||||
awe_cardbuf[ci][i] = (int16_t)v;
|
||||
awe_cardbuf[ci][i] = v; /* full-range; limited at sum */
|
||||
if (v < 0) v = -v;
|
||||
if (v > awe_prelim_peak[ci]) awe_prelim_peak[ci] = v;
|
||||
}
|
||||
memset(card->buffer, 0, n * 2 * sizeof(int32_t));
|
||||
memset(card->chorus_in_buffer, 0, n * sizeof(int32_t));
|
||||
@@ -264,27 +279,55 @@ static DWORD WINAPI awe_thread_proc(LPVOID) {
|
||||
awe_last_render_qpc = now.QuadPart;
|
||||
LeaveCriticalSection(&awe_lock);
|
||||
int16_t *out = awe_slotbuf[free_slot];
|
||||
for (unsigned i = 0; i < AWE_SLOT_FRAMES * 2; i++) {
|
||||
int32_t v = (int32_t)awe_cardbuf[0][i] + awe_cardbuf[1][i];
|
||||
if (v > 32767) v = 32767; else if (v < -32768) v = -32768;
|
||||
out[i] = (int16_t)v;
|
||||
int a0 = awe_cardbuf[0][i] < 0 ? -awe_cardbuf[0][i]
|
||||
: awe_cardbuf[0][i];
|
||||
int a1 = awe_cardbuf[1][i] < 0 ? -awe_cardbuf[1][i]
|
||||
: awe_cardbuf[1][i];
|
||||
if (a0 > peak[0]) peak[0] = a0;
|
||||
if (a1 > peak[1]) peak[1] = a1;
|
||||
for (unsigned i = 0; i < AWE_SLOT_FRAMES; i++) {
|
||||
int32_t l = awe_cardbuf[0][i * 2] + awe_cardbuf[1][i * 2];
|
||||
int32_t r = awe_cardbuf[0][i * 2 + 1] + awe_cardbuf[1][i * 2 + 1];
|
||||
/* stereo-linked peak limiter: instant attack, ~46ms release */
|
||||
int32_t pk = l < 0 ? -l : l;
|
||||
int32_t pr = r < 0 ? -r : r;
|
||||
if (pr > pk) pk = pr;
|
||||
if (pk > AWE_LIM_TARGET) {
|
||||
int32_t need = (int32_t)(((int64_t)AWE_LIM_TARGET << 16) / pk);
|
||||
if (need < awe_lim_gain) awe_lim_gain = need;
|
||||
}
|
||||
if (awe_lim_gain < (1 << 16)) {
|
||||
awe_lim_engaged++;
|
||||
l = (int32_t)(((int64_t)l * awe_lim_gain) >> 16);
|
||||
r = (int32_t)(((int64_t)r * awe_lim_gain) >> 16);
|
||||
/* release: exponential toward unity, min step 1 so it
|
||||
* actually GETS there (>>11 alone stalls 2048 short) */
|
||||
int32_t step = ((1 << 16) - awe_lim_gain) >> 11;
|
||||
awe_lim_gain += step ? step : 1;
|
||||
if (awe_lim_gain > (1 << 16)) awe_lim_gain = 1 << 16;
|
||||
}
|
||||
if (l > 32767) l = 32767; else if (l < -32768) l = -32768;
|
||||
if (r > 32767) r = 32767; else if (r < -32768) r = -32768;
|
||||
out[i * 2] = (int16_t)l;
|
||||
out[i * 2 + 1] = (int16_t)r;
|
||||
}
|
||||
if (awe_prelim_peak[0] > peak[0]) peak[0] = awe_prelim_peak[0];
|
||||
if (awe_prelim_peak[1] > peak[1]) peak[1] = awe_prelim_peak[1];
|
||||
awe_prelim_peak[0] = awe_prelim_peak[1] = 0;
|
||||
WAVEHDR *h = &awe_hdr[free_slot];
|
||||
memset(h, 0, sizeof(WAVEHDR));
|
||||
h->lpData = (LPSTR)out;
|
||||
h->dwBufferLength = AWE_SLOT_FRAMES * 4;
|
||||
waveOutPrepareHeader(awe_wo, h, sizeof(WAVEHDR));
|
||||
waveOutWrite(awe_wo, h, sizeof(WAVEHDR));
|
||||
if (awe_dump_front)
|
||||
fwrite(awe_cardbuf[0], 4, AWE_SLOT_FRAMES, awe_dump_front);
|
||||
if (awe_dump_rear)
|
||||
fwrite(awe_cardbuf[1], 4, AWE_SLOT_FRAMES, awe_dump_rear);
|
||||
if (awe_dump_front || awe_dump_rear) {
|
||||
/* dumps stay s16le: clamp the raw int32 card stream per file */
|
||||
static int16_t dump16[AWE_SLOT_FRAMES * 2];
|
||||
for (int ci = 0; ci < 2; ci++) {
|
||||
FILE *df = ci ? awe_dump_rear : awe_dump_front;
|
||||
if (!df) continue;
|
||||
for (unsigned i = 0; i < AWE_SLOT_FRAMES * 2; i++) {
|
||||
int32_t v = awe_cardbuf[ci][i];
|
||||
if (v > 32767) v = 32767; else if (v < -32768) v = -32768;
|
||||
dump16[i] = (int16_t)v;
|
||||
}
|
||||
fwrite(dump16, 4, AWE_SLOT_FRAMES, df);
|
||||
}
|
||||
}
|
||||
for (int ci = 0; ci < 2; ci++) {
|
||||
frames[ci] += AWE_SLOT_FRAMES;
|
||||
if (awe_log_on > 0 && frames[ci] - last_report[ci] >= 441000) {
|
||||
@@ -294,11 +337,12 @@ static DWORD WINAPI awe_thread_proc(LPVOID) {
|
||||
for (int i = 0; i < 32; i++)
|
||||
if (card->voice[i].cvcf_curr_volume) voices++;
|
||||
fprintf(stderr, "VWE AWE32%c 10s: wr=%lu wcRd=%lu rd=%lu "
|
||||
"smldW=%lu voices=%d peak=%d\n", ci ? 'R' : 'F',
|
||||
"smldW=%lu voices=%d peak=%d limF=%lu\n", ci ? 'R' : 'F',
|
||||
awe_stats[ci].wr, awe_stats[ci].rd_wc,
|
||||
awe_stats[ci].rd, awe_stats[ci].smld_w,
|
||||
voices, peak[ci]);
|
||||
voices, peak[ci], awe_lim_engaged);
|
||||
peak[ci] = 0;
|
||||
if (ci) awe_lim_engaged = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user