From 37d4554440b670eb172ee2ca618fbaff9e56c4c8 Mon Sep 17 00:00:00 2001 From: arcattack Date: Thu, 16 Jul 2026 08:20:42 -0500 Subject: [PATCH] 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) --- engine/MUNGA/AUDIO.cpp | 26 ++++++++++++++++++++ engine/MUNGA_L4/L4AUDIO.cpp | 46 ++++++++++++++++++++++++++++++++++++ engine/MUNGA_L4/L4AUDRES.cpp | 2 ++ 3 files changed, 74 insertions(+) diff --git a/engine/MUNGA/AUDIO.cpp b/engine/MUNGA/AUDIO.cpp index 60232e3..9afc689 100644 --- a/engine/MUNGA/AUDIO.cpp +++ b/engine/MUNGA/AUDIO.cpp @@ -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 diff --git a/engine/MUNGA_L4/L4AUDIO.cpp b/engine/MUNGA_L4/L4AUDIO.cpp index 37ab8f1..552e50d 100644 --- a/engine/MUNGA_L4/L4AUDIO.cpp +++ b/engine/MUNGA_L4/L4AUDIO.cpp @@ -1,3 +1,4 @@ +#include #include #include "mungal4.h" #pragma hdrstop @@ -10,6 +11,18 @@ #include "..\munga\player.h" #include "..\rp\vtv.h" + +// NOTE -> PITCH (task #50): the AWE32 played every patch at the requested MIDI +// note relative to the sample root (60); sequences author notes (klaxon/ambience +// rhythms are PITCHED patterns). The port captured noteValue but never applied +// any pitch to the AL sources -- everything played at root (the "chirping" +// ambience: an authored deep tick at note 51 sounded as a high click). Apply +// pitch = 2^((note-60)/12) x the control-chain cents offset at start + per frame. +static inline float BTNotePitchFactor(int note_value) +{ + return (float)pow(2.0, ((double)note_value - 60.0) / 12.0); +} + //############################################################################# //####################### L4AudioSpatialization ######################### //############################################################################# @@ -924,6 +937,12 @@ void patch_resource->SetDistance(GetDistanceToSource()); patch_resource->SetupPatch(channelSet); + { // apply the requested note's pitch to the freshly attached sources + float note_pitch = BTNotePitchFactor((int)GetCurrentNoteValue()); + for (int _i = 0; _i < channelSet.count; ++_i) + alSourcef(channelSet.sources[_i], AL_PITCH, note_pitch); + } + // // Set the channel to default control values // @@ -1034,6 +1053,11 @@ void pitch_offset = CalculateSourcePitchOffset(); double relativePitch = pow(2.0,pitch_offset/1200.0); Clamp(relativePitch,0.5,2.0); + { // apply the combined control-chain + note pitch per frame + float _np = BTNotePitchFactor((int)GetCurrentNoteValue()); + for (int _i = 0; _i < channelSet.count; ++_i) + alSourcef(channelSet.sources[_i], AL_PITCH, (float)relativePitch * _np); + } // //-------------------------------------------------------------------------- @@ -1210,6 +1234,12 @@ void patch_resource->SetDistance(GetDistanceToSource()); patch_resource->SetupPatch(channelSet); + { // apply the requested note's pitch to the freshly attached sources + float note_pitch = BTNotePitchFactor((int)GetCurrentNoteValue()); + for (int _i = 0; _i < channelSet.count; ++_i) + alSourcef(channelSet.sources[_i], AL_PITCH, note_pitch); + } + /*patch_resource->SetDistance(GetDistanceToSource()); for (i = 0; i < AudioChannelSetSize; i++) { @@ -1411,6 +1441,11 @@ void double relativePitch = pow(2.0,pitch_offset/1200.0); Clamp(relativePitch,0.5,2.0); + { // apply the combined control-chain + note pitch per frame + float _np = BTNotePitchFactor((int)GetCurrentNoteValue()); + for (int _i = 0; _i < channelSet.count; ++_i) + alSourcef(channelSet.sources[_i], AL_PITCH, (float)relativePitch * _np); + } if (getenv("BT_AUDIO_SPATIAL")) { static int s_sp=0; if ((s_sp++ % 120) == 0) DEBUG_STREAM << "[spatial] dyn src=" << (channelSet.count>0?channelSet.sources[0]:0) @@ -1704,6 +1739,12 @@ void Check(patch_resource); patch_resource->SetDistance(GetDistanceToSource()); patch_resource->SetupPatch(channelSet); + + { // apply the requested note's pitch to the freshly attached sources + float note_pitch = BTNotePitchFactor((int)GetCurrentNoteValue()); + for (int _i = 0; _i < channelSet.count; ++_i) + alSourcef(channelSet.sources[_i], AL_PITCH, note_pitch); + } /*for (i = 0; i < AudioChannelSetSize; i++) { if ((channel = channelSet.GetNth(i)) != NULL) @@ -1931,6 +1972,11 @@ void pitch_offset = CalculateSourcePitchOffset(); double relativePitch = pow(2.0,pitch_offset/1200.0); Clamp(relativePitch,0.5,2.0); + { // apply the combined control-chain + note pitch per frame + float _np = BTNotePitchFactor((int)GetCurrentNoteValue()); + for (int _i = 0; _i < channelSet.count; ++_i) + alSourcef(channelSet.sources[_i], AL_PITCH, (float)relativePitch * _np); + } Vector3D relative_position; Vector3D relative_velocity; diff --git a/engine/MUNGA_L4/L4AUDRES.cpp b/engine/MUNGA_L4/L4AUDRES.cpp index d9b19c6..9c97646 100644 --- a/engine/MUNGA_L4/L4AUDRES.cpp +++ b/engine/MUNGA_L4/L4AUDRES.cpp @@ -21,6 +21,7 @@ ALuint *g_buffers; int g_numBuffers; +const char *g_bufferNames[512]; // bufferInd -> sample file (diag: the live-playing dump) int g_curAudioWatcherClass = 0; // @@ -671,6 +672,7 @@ void //Feed the buffer with the real PCM sample data alBufferData(g_buffers[bufferInd], format, data, size, wavRate); + if (bufferInd < 512) g_bufferNames[bufferInd] = info.file; if (getenv("BT_AUDIO_LOG") && bufferInd < 3) DEBUG_STREAM << "[audio] loaded buf" << bufferInd << " " << info.file << " ch=" << wavCh << " bits=" << wavBits << " rate=" << wavRate