【Godot4.x】AudioStreamGeneratorを使ったサウンドの生成

波形からサウンドを生成する AudioStreamGeneratorというのを使って、簡単な波形を生成するプログラムを書いてみました。

ソースコード

ソースコードGitHubにアップロードしています。

作成方法

作成方法は「AudioStreamGeneratorリソースを作る」「AudioStreamPlayer」にそれを読み込ませる、という手順です。 まずはファイルシステムを右クリックして「+新規作成 > リソース」を選びます。

そして "AudioStreamGenerator" を作成します。

あとはこれを AudioStreamPlayer[2D/3D]に読み込ませるだけですね。

サンプルコード

サンプルとして以下のコードを書いてみました。

extends Node2D

# 波形タイプ
enum eWave {
    Sine,
    Triangle,
    Square,
    Saw,
    WhiteNoise,
}
const SEMITONE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]

@onready var player: AudioStreamPlayer2D = $AudioStreamPlayer2D
# ゲイン
@onready var slider_gain = $HSliderGain
# 再生時間
@onready var slider_time = $HSliderTime
# 波形.
@onready var list_oscillator = $OscillatorList
# オクターブ
@onready var list_octave = $OctaveList
# 半音階
@onready var list_semitone = $SemitoneList
# ビットレート.
@export var mix_rate_fallback: int = 44100

var _gen: AudioStreamGenerator
var _pb: AudioStreamGeneratorPlayback
var _phase: float = 0.0
var _rand := RandomNumberGenerator.new()
var _gain: float = 0.0

func _ready() -> void:
    _gen = player.stream as AudioStreamGenerator
    assert(_gen != null)
    
    for k in eWave.keys():
        list_oscillator.add_item(k)
    for v in SEMITONE:
        list_semitone.add_item(v)
    for i in range(1, 7):
        list_octave.add_item("%d"%i)
    list_oscillator.select(3) # Saw
    list_semitone.select(9) # A
    list_octave.select(3) # 4

# 再生実行.
func _on_button_play_pressed() -> void:
    # 音量取得
    _gain = slider_gain.value
    
    if _gen.mix_rate <= 0:
        _gen.mix_rate = mix_rate_fallback
    
    # 音の長さ.
    var buffer_len_sec = slider_time.value
    _gen.buffer_length = buffer_len_sec

    # 再生開始(この時点で出音したくないなら一時停止にする)
    player.play()
    player.stream_paused = true   # ← 4.x ならこれでサイレント再生にできる

    _pb = player.get_stream_playback() as AudioStreamGeneratorPlayback
    assert(_pb != null)

    # 「空き容量」ぶんだけ一気に生成して詰める
    var mix_rate: int = int(_gen.mix_rate)
    var frames_to_fill: int = _pb.get_frames_available()
    var octave = list_octave.selected + 1 # 1始まりなので+1
    var note = list_semitone.selected
    var semitone = note + (octave - 4) * 12
    # A4 = 440Hz を基準に計算
    var freq_hz = 440.0 * pow(2.0, (semitone - 9) / 12.0)
    var step: float = TAU * freq_hz / float(mix_rate)

    for i in range(frames_to_fill):
        var s: float = _sample_at_phase(_phase) * _gain
        _phase += step
        if _phase >= TAU:
            _phase -= TAU
        _pb.push_frame(Vector2(s, s))

    # 先詰めが終わったら、必要なタイミングで再生を解除
    player.stream_paused = false

# 指定フェーズでの波形サンプルを返す
func _sample_at_phase(ph: float) -> float:
    match list_oscillator.selected:
        eWave.Sine:
            return sin(ph)
        eWave.Triangle:
            # -1..1 の三角波: 2/pi * asin(sin(theta))
            return (2.0 / PI) * asin(sin(ph))
        eWave.Square:
            # 矩形波: sinが0以上なら1、未満なら-1
            return 1.0 if sin(ph) >= 0.0 else -1.0
        eWave.Saw:
            # ノコギリ波: -1..1
            return (fmod(ph / TAU, 1.0) * 2.0) - 1.0
        eWave.WhiteNoise:
            # ホワイトノイズ: -1..1 の乱数
            return _rand.randf_range(-1.0, 1.0)
        _:
            return 0.0

機能としては以下ものがあります。

  • 波形の選択 (Sin, Triangle, Square, Saw, WhiteNoise)
  • 音程 (C〜B, 1〜6)
  • 音量
  • 再生時間