Audio: implement NOTE->PITCH + control pitch -- the AL_PITCH was never applied (task #50)

The whole pitch system existed but was discarded: NoteAudioHandler captured the
MIDI note, ExecuteModel computed relativePitch from the control-chain cents --
and NOTHING ever called alSourcef(AL_PITCH).  Every sample played at root.

Consequences fixed:
- The load-time 'chirping' ambience: an authored looped sequence (div=120
  tempo=160, note 51 events on bank1:83) plays a rhythmic tick pitched ~9
  semitones DOWN; at root it was a high click ('chirp').  Identified live via
  the new BT_AUDIO_DUMP playing-source tape + user ear-match at cadence.
- Engine now revs: EngineMotor pitch rides the throttle (measured 1.0 -> 1.12).
- All sequence-authored patterns (alarms/klaxons/ambience) regain their pitch.

Implementation: BTNotePitchFactor(note) = 2^((note-60)/12); applied at all 3
patch-source StartImplementations (fresh attach) and all 3 ExecuteModels
(combined with the control-chain relativePitch, clamped 0.5..2.0 as before).
Uses the existing AudioSource::GetCurrentNoteValue().

Also: [seqcfg] sequence-config dump, g_bufferNames + BT_AUDIO_DUMP live tape
(1/s: every playing AL source with sample name/gain/pitch/loop),
tools/soundboard.py updated mapping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-16 08:20:42 -05:00
co-authored by Claude Opus 4.8
parent e017f257a5
commit 37d4554440
3 changed files with 74 additions and 0 deletions
+26
View File
@@ -157,6 +157,32 @@ void
headVelocity.MultiplyByInverse(this->GetHeadEntity()->GetWorldLinearVelocity(), this->GetHeadEntity()->localToWorld);
alListener3f(AL_VELOCITY, -headVelocity.x, -headVelocity.y, -headVelocity.z);
// LIVE-PLAYING DUMP (BT_AUDIO_DUMP): once a second list every playing AL
// source with its sample name / gain / pitch / loop -- catches "mystery
// sounds" (wrong sample, wrong pitch, chopped) red-handed.
if (getenv("BT_AUDIO_DUMP")) {
static long s_dumpTick = 0;
if ((++s_dumpTick % 45) == 0) {
extern ALuint *g_buffers; extern int g_numBuffers; extern const char *g_bufferNames[512];
for (ALuint sid = 1; sid <= 40; ++sid) {
if (!alIsSource(sid)) continue;
ALint st = 0; alGetSourcei(sid, AL_SOURCE_STATE, &st);
if (st != AL_PLAYING) continue;
ALint buf = 0, looping = 0; ALfloat gain = 0, pitch = 0;
alGetSourcei(sid, AL_BUFFER, &buf);
alGetSourcei(sid, AL_LOOPING, &looping);
alGetSourcef(sid, AL_GAIN, &gain);
alGetSourcef(sid, AL_PITCH, &pitch);
const char *nm = "?";
for (int b = 0; b < g_numBuffers && b < 512; ++b)
if ((ALint)g_buffers[b] == buf) { nm = g_bufferNames[b] ? g_bufferNames[b] : "?"; break; }
DEBUG_STREAM << "[playing] src=" << sid << " " << nm
<< " gain=" << gain << " pitch=" << pitch
<< " loop=" << looping << "\n" << std::flush;
}
}
}
#if 0
//
// Get the entity