Bus effects
A generated pluck routed Voice into FX into Master, with reverb and distortion you switch on and the peak level of every bus on screen.
The editor above is running this project. Change a line and it reloads.
Godot classes used
AudioServerAudioEffectReverbAudioEffectDistortion
The code
scripts/main.gd
extends Node
const MIX_RATE: float = 22050.0
const PANEL: Vector2 = Vector2(210.0, 150.0)
const GAP: float = 120.0
const PLUCK_SECONDS: float = 0.55
const DECAY: float = 0.99975
const NOTES: Array = [261.63, 329.63, 392.00, 523.25]
var player: AudioStreamPlayer
var playback: AudioStreamGeneratorPlayback
var buses: Array[int] = []
var meters: Array[ColorRect] = []
var readouts: Array[Label] = []
var dots: Array[Polygon2D] = []
var wires: Array[Vector2] = []
var effect_labels: Array[Label] = []
var start_button: Button
var phase: float = 0.0
var level: float = 0.0
var note: int = 0
var elapsed: float = 0.0
var flow: float = 0.0
func _ready() -> void:
_add_background()
var view: Vector2 = get_viewport().get_visible_rect().size
var width: float = PANEL.x * 3.0 + GAP * 2.0
var left: float = (view.x - width) * 0.5
var top: float = view.y * 0.3
_add_label('Bus effects', Vector2(left, view.y * 0.09), 26, Color('#ffffff'))
_add_label('one generated pluck routed Voice into FX into Master, with the peak of every bus below its name', Vector2(left, view.y * 0.15), 15, Color('#999999'))
_wire_buses()
for i in 3:
_add_panel(['Voice', 'FX', 'Master'][i], Vector2(left + i * (PANEL.x + GAP), top))
for i in 2:
var from: float = left + i * (PANEL.x + GAP) + PANEL.x
wires.append(Vector2(from, from + GAP))
_add_wire(Vector2(from, top + PANEL.y * 0.5))
_add_effect_labels(Vector2(left + PANEL.x + GAP + 16.0, top + 54.0))
_add_controls(Vector2(left, top + PANEL.y + 70.0))
_add_player()
print('routing is live, press Start sound and toggle the two effects on the FX bus')
func _process(delta: float) -> void:
flow = fmod(flow + delta * 0.6, 1.0)
for i in dots.size():
var at: float = fmod(flow + i * 0.5, 1.0)
dots[i].position.x = lerpf(wires[i].x, wires[i].y, at)
dots[i].modulate.a = (1.0 if playback != null else 0.55) * maxf(sin(at * PI), 0.35)
for i in buses.size():
var db: float = AudioServer.get_bus_peak_volume_left_db(buses[i], 0)
var target: float = clampf((db + 60.0) / 60.0, 0.0, 1.0)
meters[i].size.x = maxf(target * (PANEL.x - 32.0), 3.0)
readouts[i].text = '%.0f dB' % maxf(db, -60.0)
if playback == null:
return
elapsed += delta
if elapsed >= PLUCK_SECONDS:
elapsed -= PLUCK_SECONDS
note = (note + 1) % NOTES.size()
level = 0.5
_fill_buffer()
func _fill_buffer() -> void:
var hz: float = NOTES[note]
for i in playback.get_frames_available():
phase = fmod(phase + hz / MIX_RATE, 1.0)
level *= DECAY
var sample: float = (sin(phase * TAU) + sin(phase * TAU * 3.0) * 0.25) * level
playback.push_frame(Vector2(sample, sample))
func _wire_buses() -> void:
# a bus can only send to a lower index, so the downstream bus has to be created first
var fx: int = _find_bus('FX', 'Master')
var voice: int = _find_bus('Voice', 'FX')
if AudioServer.get_bus_effect_count(fx) == 0:
var reverb: AudioEffectReverb = AudioEffectReverb.new()
reverb.room_size = 0.85
reverb.wet = 0.6
AudioServer.add_bus_effect(fx, reverb)
var distortion: AudioEffectDistortion = AudioEffectDistortion.new()
distortion.drive = 0.5
distortion.pre_gain = 8.0
distortion.post_gain = -14.0
AudioServer.add_bus_effect(fx, distortion)
AudioServer.set_bus_effect_enabled(fx, 0, false)
AudioServer.set_bus_effect_enabled(fx, 1, false)
buses.append(voice)
buses.append(fx)
buses.append(AudioServer.get_bus_index('Master'))
func _find_bus(bus: String, send: String) -> int:
var index: int = AudioServer.get_bus_index(bus)
if index != -1:
return index
index = AudioServer.get_bus_count()
AudioServer.add_bus(index)
AudioServer.set_bus_name(index, bus)
AudioServer.set_bus_send(index, send)
return index
func _toggle_effect(on: bool, slot: int) -> void:
AudioServer.set_bus_effect_enabled(buses[1], slot, on)
effect_labels[slot].add_theme_color_override('font_color', Color('#8eef97') if on else Color('#999999'))
func _toggle_sound() -> void:
if playback != null:
player.stop()
playback = null
level = 0.0
start_button.text = 'Start sound'
return
player.play()
playback = player.get_stream_playback()
level = 0.5
start_button.text = 'Stop sound'
func _add_panel(title: String, at: Vector2) -> void:
var panel: ColorRect = ColorRect.new()
panel.color = Color('#2a2f3d')
panel.position = at
panel.size = PANEL
add_child(panel)
_add_label(title, at + Vector2(16.0, 14.0), 20, Color('#ffffff'))
var track: ColorRect = ColorRect.new()
track.color = Color('#12141a')
track.position = at + Vector2(16.0, PANEL.y - 46.0)
track.size = Vector2(PANEL.x - 32.0, 16.0)
add_child(track)
var meter: ColorRect = ColorRect.new()
meter.color = Color('#8eef97')
meter.position = track.position
meter.size = Vector2(3.0, 16.0)
add_child(meter)
meters.append(meter)
readouts.append(_add_label('-60 dB', at + Vector2(16.0, PANEL.y - 26.0), 14, Color('#999999')))
func _add_wire(at: Vector2) -> void:
var wire: ColorRect = ColorRect.new()
wire.color = Color('#8da5f3', 0.3)
wire.position = at - Vector2(0.0, 2.0)
wire.size = Vector2(GAP, 4.0)
add_child(wire)
var dot: Polygon2D = Polygon2D.new()
dot.color = Color('#8da5f3')
dot.position = at
dot.polygon = PackedVector2Array([Vector2(-11, 0), Vector2(0, -11), Vector2(11, 0), Vector2(0, 11)])
add_child(dot)
dots.append(dot)
func _add_effect_labels(at: Vector2) -> void:
for i in 2:
effect_labels.append(_add_label(['reverb', 'distortion'][i], at + Vector2(0.0, i * 24.0), 16, Color('#999999')))
func _add_controls(at: Vector2) -> void:
var row: HBoxContainer = HBoxContainer.new()
row.add_theme_constant_override('separation', 22)
row.position = at
add_child(row)
for i in 2:
var toggle: CheckButton = CheckButton.new()
toggle.text = ['Reverb', 'Distortion'][i]
toggle.toggled.connect(_toggle_effect.bind(i))
row.add_child(toggle)
start_button = Button.new()
start_button.text = 'Start sound'
start_button.pressed.connect(_toggle_sound)
row.add_child(start_button)
func _add_player() -> void:
var generator: AudioStreamGenerator = AudioStreamGenerator.new()
generator.mix_rate = MIX_RATE
generator.buffer_length = 0.1
player = AudioStreamPlayer.new()
player.stream = generator
player.bus = 'Voice'
add_child(player)
func _add_background() -> void:
var background: ColorRect = ColorRect.new()
background.color = Color('#12141a')
add_child(background)
background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
func _add_label(text: String, at: Vector2, size: int, color: Color) -> Label:
var label: Label = Label.new()
label.text = text
label.position = at
label.add_theme_font_size_override('font_size', size)
label.add_theme_color_override('font_color', color)
add_child(label)
return label