godot-audio-systems by thedivergentai/gd-agentic-skills
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-audio-systems关于 Godot 音频引擎与混音架构的专家级指导。
set_bus_volume_db() 是对数函数。为滑块使用 linear_to_db(),否则在最后 5% 之前所有声音都会听起来太响。Inverse Distance,地图另一端的耳语声也会是全局音量。0.1 个单位。DOPPLER_TRACKING_PHYSICS_STEP,会感觉平淡且静止。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
Limiter (audio_voice_limiter_manager.gd)。queue_free() 会导致帧时间尖峰。使用池。Tween 来衔接音轨。强制要求:在实现相应模式前,请阅读对应的脚本。
具备基于优先级的“语音抢占”逻辑的专家级高性能语音池管理器。
专业的基于射线投射的音频遮挡系统,用于实现墙后动态声音衰减。
BPM 同步的水平重排序器,用于实现无缝的音乐过渡。
专家级 FFT 频谱分析组件,用于驱动逻辑到数据的可视化。
专业的侧链式总线闪避系统(对话优先于音乐)。
专家级实时合成器,用于生成程序化的嗡嗡声、引擎声和信号声。
通过 Area3D 触发区域进行动态混响/总线效果管理。
并发管理器,通过限制相同音效实例的数量来防止“耳朵出血”。
专家级辅助工具,用于实现平滑、符合音乐性的用户界面音量滑块映射。
基于物理的表面检测和脚步声声音库选择器。
# 无空间定位,各处音量相同
# 适用于:音乐、用户界面音效、旁白
@onready var music := AudioStreamPlayer.new()
func _ready() -> void:
music.stream = load("res://audio/music_main.ogg")
music.volume_db = -10 # 更安静
music.autoplay = false
music.bus = "Music" # 路由到音乐总线
add_child(music)
music.play()
# 基于与摄像机距离的 2D 声像定位
# 适用于:2D 游戏、俯视角音频提示
extends Area2D
@onready var footstep := AudioStreamPlayer2D.new()
func _ready() -> void:
footstep.stream = load("res://audio/footstep.ogg")
footstep.max_distance = 500 # 可听范围(像素)
footstep.attenuation = 2.0 # 衰减曲线(值越高,淡出越快)
add_child(footstep)
func play_footstep() -> void:
if not footstep.playing:
footstep.play()
# 具备多普勒效应、混响发送的 3D 空间音频
# 适用于:3D 游戏、逼真的声音定位
extends Node3D
@onready var explosion := AudioStreamPlayer3D.new()
func _ready() -> void:
explosion.stream = load("res://audio/explosion.ogg")
explosion.unit_size = 10.0 # 声源大小
explosion.max_distance = 100.0 # 范围
explosion.attenuation_model = AudioStreamPlayer3D.ATTENUATION_INVERSE_DISTANCE
explosion.doppler_tracking = AudioStreamPlayer3D.DOPPLER_TRACKING_PHYSICS_STEP
add_child(explosion)
explosion.play()
Master (始终存在)
├─ Music
│ └─ Effects: Compressor, EQ
├─ SFX
│ └─ Effects: Reverb (for environment)
└─ Ambient
└─ Effects: LowPassFilter (muffled ambience)
# ❌ 错误做法:线性音量(无效)
AudioServer.set_bus_volume_db(music_bus_idx, 0.5) # 错误!
# ✅ 正确做法:使用分贝
var music_bus := AudioServer.get_bus_index("Music")
AudioServer.set_bus_volume_db(music_bus, -10) # -10 分贝(更安静)
# 将线性值(0.0-1.0)转换为分贝:
var linear_volume := 0.5 # 50%
var db := linear_to_db(linear_volume) # 约 -6 分贝
AudioServer.set_bus_volume_db(music_bus, db)
# 将分贝转换为线性值:
var current_db := AudioServer.get_bus_volume_db(music_bus)
var linear := db_to_linear(current_db)
print("当前音量:%d%%" % int(linear * 100))
func toggle_mute(bus_name: String) -> void:
var bus_idx := AudioServer.get_bus_index(bus_name)
var is_muted := AudioServer.is_bus_mute(bus_idx)
AudioServer.set_bus_mute(bus_idx, not is_muted)
# ❌ 错误做法:在 60 FPS 下每秒创建 60 个新节点
func play_footstep() -> void:
var player := AudioStreamPlayer.new()
add_child(player)
player.stream = load("res://audio/footstep.ogg")
player.finished.connect(player.queue_free)
player.play()
# 结果:1 分钟内创建了 3600 个节点!
# audio_pool.gd (自动加载)
extends Node
const POOL_SIZE = 10
var pool: Array[AudioStreamPlayer] = []
var pool_index := 0
func _ready() -> void:
# 预创建播放器
for i in range(POOL_SIZE):
var player := AudioStreamPlayer.new()
player.bus = "SFX"
add_child(player)
pool.append(player)
func play_sound(stream: AudioStream, volume_db := 0.0) -> void:
var player := pool[pool_index]
pool_index = (pool_index + 1) % POOL_SIZE # 循环轮转
# 如果之前的音效仍在播放,则停止它
if player.playing:
player.stop()
player.stream = stream
player.volume_db = volume_db
player.play()
# 用法:
AudioPool.play_sound(load("res://audio/coin.ogg"), -5.0)
# music_manager.gd (自动加载)
extends Node
@onready var track_a := AudioStreamPlayer.new()
@onready var track_b := AudioStreamPlayer.new()
var current_track: AudioStreamPlayer
var fade_duration := 2.0
func _ready() -> void:
track_a.bus = "Music"
track_b.bus = "Music"
add_child(track_a)
add_child(track_b)
current_track = track_a
func crossfade_to(new_stream: AudioStream) -> void:
var next_track := track_b if current_track == track_a else track_a
# 以 0 分贝开始新音轨
next_track.stream = new_stream
next_track.volume_db = -80 # 静音
next_track.play()
# 淡出当前音轨,淡入下一个音轨
var tween := create_tween().set_parallel(true)
tween.tween_property(current_track, "volume_db", -80, fade_duration)
tween.tween_property(next_track, "volume_db", 0, fade_duration)
await tween.finished
# 停止旧音轨
current_track.stop()
current_track = next_track
# 在节拍边界进行过渡
var bpm := 120.0 # 每分钟节拍数
var beat_duration := 60.0 / bpm # 每拍 0.5 秒
func queue_transition_on_beat(new_stream: AudioStream) -> void:
# 等待下一个节拍
var current_time := current_track.get_playback_position()
var time_to_next_beat := beat_duration - fmod(current_time, beat_duration)
await get_tree().create_timer(time_to_next_beat).timeout
crossfade_to(new_stream)
# 为音效总线添加混响
var sfx_bus := AudioServer.get_bus_index("SFX")
var reverb := AudioEffectReverb.new()
reverb.room_size = 0.8 # 大房间
reverb.damping = 0.5
reverb.wet = 0.3 # 30% 效果,70% 干声
AudioServer.add_bus_effect(sfx_bus, reverb)
func set_underwater(enabled: bool) -> void:
var sfx_bus := AudioServer.get_bus_index("SFX")
if enabled:
# 添加低通滤波器(声音模糊)
var lowpass := AudioEffectLowPassFilter.new()
lowpass.cutoff_hz = 500 # 切除 500 Hz 以上的频率
AudioServer.add_bus_effect(sfx_bus, lowpass)
else:
# 移除所有效果
for i in range(AudioServer.get_bus_effect_count(sfx_bus)):
AudioServer.remove_bus_effect(sfx_bus, 0)
# 生成简单的正弦波
func create_beep(frequency: float, duration: float) -> AudioStreamGenerator:
var stream := AudioStreamGenerator.new()
stream.mix_rate = 44100 # 采样率
var playback := stream.instantiate_playback()
var increment := frequency / stream.mix_rate
var phase := 0.0
for i in range(int(stream.mix_rate * duration)):
var sample := sin(phase * TAU)
playback.push_frame(Vector2(sample, sample)) # 立体声
phase += increment
phase = fmod(phase, 1.0)
return stream
# 用法:
var beep_stream := create_beep(440.0, 0.1) # 440 赫兹 (A4),0.1 秒
$AudioStreamPlayer.stream = beep_stream
$AudioStreamPlayer.play()
# auto_duck.gd (位于对话 AudioStreamPlayer 上)
extends AudioStreamPlayer
func _ready() -> void:
playing.connect(_on_playing)
finished.connect(_on_finished)
func _on_playing() -> void:
# 将音乐闪避到 -15 分贝
var music_bus := AudioServer.get_bus_index("Music")
var tween := create_tween()
tween.tween_method(set_music_volume, 0.0, -15.0, 0.5)
func _on_finished() -> void:
# 将音乐恢复到 0 分贝
var tween := create_tween()
tween.tween_method(set_music_volume, -15.0, 0.0, 0.5)
func set_music_volume(db: float) -> void:
var music_bus := AudioServer.get_bus_index("Music")
AudioServer.set_bus_volume_db(music_bus, db)
# 防止声音完全相同(脚步声、枪声)
func play_varied_sound(stream: AudioStream) -> void:
$AudioStreamPlayer.stream = stream
$AudioStreamPlayer.pitch_scale = randf_range(0.9, 1.1) # ±10% 音高变化
$AudioStreamPlayer.play()
# 基于强度的音乐分层(从安静开始,随着强度增加添加层)
# 示例:和平探索 → 战斗
@onready var layer_drums := $Music/Drums
@onready var layer_bass := $Music/Bass
@onready var layer_melody := $Music/Melody
var intensity := 0.0 # 0.0 = 平静,1.0 = 激烈
func _ready() -> void:
# 同步开始所有层
layer_drums.play()
layer_bass.play()
layer_melody.play()
# 静音高强度层
layer_bass.volume_db = -80
layer_melody.volume_db = -80
func set_music_intensity(new_intensity: float) -> void:
intensity = clamp(new_intensity, 0.0, 1.0)
# 根据强度淡入各层
var tween := create_tween().set_parallel(true)
# 第 1 层(鼓):始终可听
tween.tween_property(layer_drums, "volume_db", 0, 1.0)
# 第 2 层(贝斯):在 33% 强度时淡入
var bass_db := -80 if intensity < 0.33 else lerp(-80.0, 0.0, (intensity - 0.33) / 0.67)
tween.tween_property(layer_bass, "volume_db", bass_db, 1.0)
# 第 3 层(旋律):在 66% 强度时淡入
var melody_db := -80 if intensity < 0.66 else lerp(-80.0, 0.0, (intensity - 0.66) / 0.34)
tween.tween_property(layer_melody, "volume_db", melody_db, 1.0)
# 用法(战斗系统):
func _on_enemy_spotted() -> void:
MusicManager.set_music_intensity(1.0) # 全强度
func _on_all_enemies_defeated() -> void:
MusicManager.set_music_intensity(0.0) # 恢复平静
# 不播放玩家听不到的声音
extends AudioStreamPlayer3D
func _process(delta: float) -> void:
var listener := get_viewport().get_camera_3d()
if not listener:
return
var distance := global_position.distance_to(listener.global_position)
if distance > max_distance * 1.5: # 1.5 倍最大范围
if playing:
stop()
# 检查:
# 1. 是否分配了音频流?
if not $AudioStreamPlayer.stream:
push_error("未分配音频流!")
# 2. 总线是否静音?
var bus_idx := AudioServer.get_bus_index($AudioStreamPlayer.bus)
if AudioServer.is_bus_mute(bus_idx):
print("总线已静音!")
# 3. 音量是否太低?
if $AudioStreamPlayer.volume_db < -60:
print("音量太安静(< -60 分贝)")
| 功能 | AudioStreamPlayer | AudioStreamPlayer2D | AudioStreamPlayer3D |
|---|---|---|---|
| 空间性 | ❌ 全局 | ✅ 2D 声像定位 | ✅ 3D 定位 |
| 多普勒效应 | ❌ | ❌ | ✅ |
| 衰减 | ❌ | ✅ 基于距离 | ✅ 3D 衰减 |
| 混响发送 | ❌ | ❌ | ✅ |
| 适用于 | 音乐、用户界面 | 2D 游戏 | 3D 游戏 |
| 性能 | 最快 | 中等 | 最慢 |
每周安装次数
94
代码仓库
GitHub 星标数
59
首次出现
2026 年 2 月 10 日
安全审计
已安装于
gemini-cli92
opencode92
codex92
kimi-cli90
amp90
github-copilot90
Expert guidance for Godot's audio engine and mixing architecture.
set_bus_volume_db() is logarithmic. Use linear_to_db() for sliders OR everything will sound too loud until the last 5%.Inverse Distance, a whisper on the other side of the map will be global volume.0.1 units.DOPPLER_TRACKING_PHYSICS_STEP feels flat and static.Limiter (audio_voice_limiter_manager.gd).queue_free()ing causes frame-time spikes. Use a Pool.Tween to bridge tracks.MANDATORY : Read the appropriate script before implementing the corresponding pattern.
Expert high-performance voice pooler with priority-based 'voice stealing' logic.
Professional Raycast-based audio occlusion for dynamic muffling behind walls.
BPM-synced horizontal re-sequencer for seamless musical transitions.
Expert FFT spectrum analysis component for driving logic-to-data visuals.
Professional sidechain-style bus ducking (Dialogue-over-Music).
Expert real-time synthesizer for procedural hums, engines, and signals.
Dynamic reverb/bus effect management via Area3D trigger zones.
Concurrency manager that prevents 'Ear Bleed' by capping identical SFX instances.
Expert helper for smooth, musically-accurate UI volume slider mapping.
Physics-driven surface detection and sound-bank selector for footsteps.
# No spatial positioning, same volume everywhere
# Use for: Music, UI sounds, voiceovers
@onready var music := AudioStreamPlayer.new()
func _ready() -> void:
music.stream = load("res://audio/music_main.ogg")
music.volume_db = -10 # Quieter
music.autoplay = false
music.bus = "Music" # Route to Music bus
add_child(music)
music.play()
# 2D panning based on distance from camera
# Use for: 2D games, top-down audio cues
extends Area2D
@onready var footstep := AudioStreamPlayer2D.new()
func _ready() -> void:
footstep.stream = load("res://audio/footstep.ogg")
footstep.max_distance = 500 # Audible range (pixels)
footstep.attenuation = 2.0 # Falloff curve (higher = faster fadeout)
add_child(footstep)
func play_footstep() -> void:
if not footstep.playing:
footstep.play()
# 3D spatial audio with doppler, reverb send
# Use for: 3D games, realistic sound positioning
extends Node3D
@onready var explosion := AudioStreamPlayer3D.new()
func _ready() -> void:
explosion.stream = load("res://audio/explosion.ogg")
explosion.unit_size = 10.0 # Size of sound source
explosion.max_distance = 100.0 # Range
explosion.attenuation_model = AudioStreamPlayer3D.ATTENUATION_INVERSE_DISTANCE
explosion.doppler_tracking = AudioStreamPlayer3D.DOPPLER_TRACKING_PHYSICS_STEP
add_child(explosion)
explosion.play()
Master (always exists)
├─ Music
│ └─ Effects: Compressor, EQ
├─ SFX
│ └─ Effects: Reverb (for environment)
└─ Ambient
└─ Effects: LowPassFilter (muffled ambience)
# ❌ BAD: Linear volume (doesn't work)
AudioServer.set_bus_volume_db(music_bus_idx, 0.5) # WRONG!
# ✅ GOOD: Use decibels
var music_bus := AudioServer.get_bus_index("Music")
AudioServer.set_bus_volume_db(music_bus, -10) # -10 dB (quieter)
# Convert linear (0.0-1.0) to dB:
var linear_volume := 0.5 # 50%
var db := linear_to_db(linear_volume) # ~-6 dB
AudioServer.set_bus_volume_db(music_bus, db)
# Convert dB to linear:
var current_db := AudioServer.get_bus_volume_db(music_bus)
var linear := db_to_linear(current_db)
print("Current volume: %d%%" % int(linear * 100))
func toggle_mute(bus_name: String) -> void:
var bus_idx := AudioServer.get_bus_index(bus_name)
var is_muted := AudioServer.is_bus_mute(bus_idx)
AudioServer.set_bus_mute(bus_idx, not is_muted)
# ❌ BAD: Creates 60 new nodes/second at 60 FPS
func play_footstep() -> void:
var player := AudioStreamPlayer.new()
add_child(player)
player.stream = load("res://audio/footstep.ogg")
player.finished.connect(player.queue_free)
player.play()
# Result: 3600 nodes created in 1 minute!
# audio_pool.gd (AutoLoad)
extends Node
const POOL_SIZE = 10
var pool: Array[AudioStreamPlayer] = []
var pool_index := 0
func _ready() -> void:
# Pre-create players
for i in range(POOL_SIZE):
var player := AudioStreamPlayer.new()
player.bus = "SFX"
add_child(player)
pool.append(player)
func play_sound(stream: AudioStream, volume_db := 0.0) -> void:
var player := pool[pool_index]
pool_index = (pool_index + 1) % POOL_SIZE # Round-robin
# Stop previous sound if still playing
if player.playing:
player.stop()
player.stream = stream
player.volume_db = volume_db
player.play()
# Usage:
AudioPool.play_sound(load("res://audio/coin.ogg"), -5.0)
# music_manager.gd (AutoLoad)
extends Node
@onready var track_a := AudioStreamPlayer.new()
@onready var track_b := AudioStreamPlayer.new()
var current_track: AudioStreamPlayer
var fade_duration := 2.0
func _ready() -> void:
track_a.bus = "Music"
track_b.bus = "Music"
add_child(track_a)
add_child(track_b)
current_track = track_a
func crossfade_to(new_stream: AudioStream) -> void:
var next_track := track_b if current_track == track_a else track_a
# Start new track at 0 dB
next_track.stream = new_stream
next_track.volume_db = -80 # Silent
next_track.play()
# Fade out current, fade in next
var tween := create_tween().set_parallel(true)
tween.tween_property(current_track, "volume_db", -80, fade_duration)
tween.tween_property(next_track, "volume_db", 0, fade_duration)
await tween.finished
# Stop old track
current_track.stop()
current_track = next_track
# Transition on beat boundary
var bpm := 120.0 # Beats per minute
var beat_duration := 60.0 / bpm # 0.5s per beat
func queue_transition_on_beat(new_stream: AudioStream) -> void:
# Wait for next beat
var current_time := current_track.get_playback_position()
var time_to_next_beat := beat_duration - fmod(current_time, beat_duration)
await get_tree().create_timer(time_to_next_beat).timeout
crossfade_to(new_stream)
# Add reverb to SFX bus
var sfx_bus := AudioServer.get_bus_index("SFX")
var reverb := AudioEffectReverb.new()
reverb.room_size = 0.8 # Large room
reverb.damping = 0.5
reverb.wet = 0.3 # 30% effect, 70% dry
AudioServer.add_bus_effect(sfx_bus, reverb)
func set_underwater(enabled: bool) -> void:
var sfx_bus := AudioServer.get_bus_index("SFX")
if enabled:
# Add low-pass filter (muffled sound)
var lowpass := AudioEffectLowPassFilter.new()
lowpass.cutoff_hz = 500 # Cut frequencies above 500 Hz
AudioServer.add_bus_effect(sfx_bus, lowpass)
else:
# Remove all effects
for i in range(AudioServer.get_bus_effect_count(sfx_bus)):
AudioServer.remove_bus_effect(sfx_bus, 0)
# Generate simple sine wave
func create_beep(frequency: float, duration: float) -> AudioStreamGenerator:
var stream := AudioStreamGenerator.new()
stream.mix_rate = 44100 # Sample rate
var playback := stream.instantiate_playback()
var increment := frequency / stream.mix_rate
var phase := 0.0
for i in range(int(stream.mix_rate * duration)):
var sample := sin(phase * TAU)
playback.push_frame(Vector2(sample, sample)) # Stereo
phase += increment
phase = fmod(phase, 1.0)
return stream
# Usage:
var beep_stream := create_beep(440.0, 0.1) # 440 Hz (A4), 0.1s
$AudioStreamPlayer.stream = beep_stream
$AudioStreamPlayer.play()
# auto_duck.gd (on Dialogue AudioStreamPlayer)
extends AudioStreamPlayer
func _ready() -> void:
playing.connect(_on_playing)
finished.connect(_on_finished)
func _on_playing() -> void:
# Duck music to -15 dB
var music_bus := AudioServer.get_bus_index("Music")
var tween := create_tween()
tween.tween_method(set_music_volume, 0.0, -15.0, 0.5)
func _on_finished() -> void:
# Restore music to 0 dB
var tween := create_tween()
tween.tween_method(set_music_volume, -15.0, 0.0, 0.5)
func set_music_volume(db: float) -> void:
var music_bus := AudioServer.get_bus_index("Music")
AudioServer.set_bus_volume_db(music_bus, db)
# Prevent identical sounds (footsteps, gunshots)
func play_varied_sound(stream: AudioStream) -> void:
$AudioStreamPlayer.stream = stream
$AudioStreamPlayer.pitch_scale = randf_range(0.9, 1.1) # ±10% pitch
$AudioStreamPlayer.play()
# Intensity-based music layers (start quiet, add layers as intensity increases)
# Example: Peaceful exploration → Combat
@onready var layer_drums := $Music/Drums
@onready var layer_bass := $Music/Bass
@onready var layer_melody := $Music/Melody
var intensity := 0.0 # 0.0 = calm, 1.0 = intense
func _ready() -> void:
# Start all layers in sync
layer_drums.play()
layer_bass.play()
layer_melody.play()
# Mute high-intensity layers
layer_bass.volume_db = -80
layer_melody.volume_db = -80
func set_music_intensity(new_intensity: float) -> void:
intensity = clamp(new_intensity, 0.0, 1.0)
# Fade in layers based on intensity
var tween := create_tween().set_parallel(true)
# Layer 1 (drums): always audible
tween.tween_property(layer_drums, "volume_db", 0, 1.0)
# Layer 2 (bass): fade in at 33% intensity
var bass_db := -80 if intensity < 0.33 else lerp(-80.0, 0.0, (intensity - 0.33) / 0.67)
tween.tween_property(layer_bass, "volume_db", bass_db, 1.0)
# Layer 3 (melody): fade in at 66% intensity
var melody_db := -80 if intensity < 0.66 else lerp(-80.0, 0.0, (intensity - 0.66) / 0.34)
tween.tween_property(layer_melody, "volume_db", melody_db, 1.0)
# Usage (combat system):
func _on_enemy_spotted() -> void:
MusicManager.set_music_intensity(1.0) # Full intensity
func _on_all_enemies_defeated() -> void:
MusicManager.set_music_intensity(0.0) # Back to calm
# Don't play sounds the player can't hear
extends AudioStreamPlayer3D
func _process(delta: float) -> void:
var listener := get_viewport().get_camera_3d()
if not listener:
return
var distance := global_position.distance_to(listener.global_position)
if distance > max_distance * 1.5: # 1.5x max range
if playing:
stop()
# Check:
# 1. Is stream assigned?
if not $AudioStreamPlayer.stream:
push_error("No audio stream assigned!")
# 2. Is bus muted?
var bus_idx := AudioServer.get_bus_index($AudioStreamPlayer.bus)
if AudioServer.is_bus_mute(bus_idx):
print("Bus is muted!")
# 3. Is volume too low?
if $AudioStreamPlayer.volume_db < -60:
print("Volume too quiet (< -60 dB)")
| Feature | AudioStreamPlayer | AudioStreamPlayer2D | AudioStreamPlayer3D |
|---|---|---|---|
| Spatial | ❌ Global | ✅ 2D panning | ✅ 3D positioning |
| Doppler | ❌ | ❌ | ✅ |
| Attenuation | ❌ | ✅ Distance-based | ✅ 3D falloff |
| Reverb send | ❌ | ❌ | ✅ |
| Use for | Music, UI | 2D games | 3D games |
| Performance | Fastest | Medium | Slowest |
Weekly Installs
94
Repository
GitHub Stars
59
First Seen
Feb 10, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli92
opencode92
codex92
kimi-cli90
amp90
github-copilot90
AI播客创作工具 - 使用inference.sh CLI快速生成AI驱动播客和音频内容
7,800 周安装