RX-7 audio v2: turbo whistle + exhaust from the action-cam library
CI / quality (push) Successful in 1m4s

- Scanned b-action-cam-footage/clipchamp (99GB, 5 videos of the FD):
  extracted audio NAS-side via linuxserver/ffmpeg container (stream-copy),
  scanned 69min return drive per-second for engine tonality + whistle
  prominence (1.5-9kHz peak vs band median)
- New rev loop: 985-993s highway pull — 0.0Hz pitch drift over 8s,
  whistle ~1.7kHz at +33dB, exhaust + turbo captured together (seam 0.001)
- New whistle-only loop: same pull high-passed 1.2kHz — isolated spool
- SoundController: optional per-car Whistle layer that rides boost
  (throttle held at speed): fades in past 25% speed, pitch 0.9->1.35
- ROTARY_SOUND.md updated; three .ogg uploads needed in Studio now
This commit is contained in:
2026-07-22 02:26:45 -04:00
parent 1891f3f11d
commit dae30fd22e
4 changed files with 50 additions and 15 deletions
+35 -4
View File
@@ -34,19 +34,26 @@ local player = Players.LocalPlayer
local SoundController = {}
type EngineSounds = { Idle: string, Rev: string }
type EngineSounds = { Idle: string, Rev: string, Whistle: string? }
-- Default (generic) sounds --------------------------------------------------
local DEFAULT_SOUNDS: EngineSounds = {
Idle = "rbxassetid://80317022777602", -- Roblox Resources engine loop
Rev = "rbxassetid://80317022777602", -- same loop until real rev samples exist
Whistle = nil, -- most cars have no turbo layer
}
local SQUEAL_ID = "rbxassetid://9120399077" -- ProSoundEffects tyre squeal
-- Per-car engine sounds. Fill rx7_fc with the owner's real 13B recordings
-- once uploaded (docs/ROTARY_SOUND.md). Cars not listed use DEFAULT_SOUNDS.
-- Per-car engine sounds. The rx7_fc loops are cut from the owner's REAL FD
-- footage (docs/ROTARY_SOUND.md): idle + whistle-rich rev + an isolated
-- >1.2kHz turbo whistle layer that rides boost. Fill the IDs after the
-- Studio upload of assets/audio/*.ogg.
local CAR_SOUNDS: { [string]: EngineSounds } = {
-- rx7_fc = { Idle = "rbxassetid://<13B_IDLE>", Rev = "rbxassetid://<13B_REV>" },
-- rx7_fc = {
-- Idle = "rbxassetid://<rx7_idle_loop>",
-- Rev = "rbxassetid://<rx7_rev_loop>",
-- Whistle = "rbxassetid://<rx7_whistle_loop>",
-- },
}
-- Tunables --------------------------------------------------------------------
@@ -63,6 +70,13 @@ local SLIP_START = 14 -- studs/sec lateral slip where squeal begins
local SLIP_FULL = 34 -- lateral slip for max squeal volume
local SQUEAL_MAX_VOLUME = 0.5
local RESPONSIVENESS = 8 -- volume/pitch smoothing (1/sec)
-- Turbo whistle layer (cars with Whistle set): rides "boost" = throttle
-- held at speed. Fades in above BOOST_START speedFrac when on throttle.
local WHISTLE_MAX_VOLUME = 0.45
local BOOST_START = 0.25 -- speedFrac where boost can begin
local BOOST_FULL = 0.8 -- speedFrac of max whistle
local WHISTLE_PITCH_MIN = 0.9
local WHISTLE_PITCH_MAX = 1.35
local heartbeatConn: RBXScriptConnection? = nil
local seatedConn: RBXScriptConnection? = nil
@@ -104,9 +118,16 @@ local function startAudio(seat: VehicleSeat)
local idle = makeSound("EngineIdle", engineSet.Idle, ENGINE_BASE_VOLUME, bodyPart)
local rev = makeSound("EngineRev", engineSet.Rev, 0, bodyPart)
local squeal = makeSound("TyreSqueal", SQUEAL_ID, 0, bodyPart)
local whistle: Sound? = nil
if engineSet.Whistle then
whistle = makeSound("TurboWhistle", engineSet.Whistle, 0, bodyPart)
end
idle:Play()
rev:Play()
squeal:Play() -- all loops run constantly; we fade volumes (no start/stop pops)
if whistle then
whistle:Play()
end
heartbeatConn = RunService.Heartbeat:Connect(function(dt)
if not bodyPart.Parent then
@@ -138,6 +159,16 @@ local function startAudio(seat: VehicleSeat)
local braking = (seat.ThrottleFloat == 0 and speed > tuning.MaxSpeed * 0.5) and 0.25 or 0
local squealTarget = math.max(slipFrac, braking) * SQUEAL_MAX_VOLUME
squeal.Volume += (squealTarget - squeal.Volume) * a
-- Turbo whistle: spools with throttle held at speed ("on boost").
if whistle then
local boost = math.clamp((speedFrac - BOOST_START) / (BOOST_FULL - BOOST_START), 0, 1)
* math.abs(seat.ThrottleFloat)
local whistleVolTarget = WHISTLE_MAX_VOLUME * boost
local whistlePitchTarget = WHISTLE_PITCH_MIN + (WHISTLE_PITCH_MAX - WHISTLE_PITCH_MIN) * boost
whistle.Volume += (whistleVolTarget - whistle.Volume) * a
whistle.PlaybackSpeed += (whistlePitchTarget - whistle.PlaybackSpeed) * a
end
end)
end