【发布时间】:2021-12-19 22:14:20
【问题描述】:
我正在尝试使用 godot 创建一个音乐游戏,它类似于弹钢琴,玩家按下琴键,每个琴键都分配一个声音,然后播放音乐。我想要的是在游戏中拥有一种录音机,以便能够保存创建的旋律并将其导出到诸如 mp3 或 wav 之类的文件中。我正在使用 gdScript,我一直在研究这个问题,但找不到任何东西。
【问题讨论】:
标签: audio mp3 wav godot gdscript
我正在尝试使用 godot 创建一个音乐游戏,它类似于弹钢琴,玩家按下琴键,每个琴键都分配一个声音,然后播放音乐。我想要的是在游戏中拥有一种录音机,以便能够保存创建的旋律并将其导出到诸如 mp3 或 wav 之类的文件中。我正在使用 gdScript,我一直在研究这个问题,但找不到任何东西。
【问题讨论】:
标签: audio mp3 wav godot gdscript
这个问题有两个部分,首先,我们需要实际再现音频,其次我们需要将其录制并保存为文件。我们有两个播放音频的选项......让我们从那里开始。
播放音频样本
您可能知道您可以使用AudioStreamPlayer(2D/3D) 来播放音频。如果你有一个乐器样本,你可以设置多个,每个音符一个。然后致电play单独播放。
好的,但是如果您没有每个音符的样本怎么办?只有一个。
好吧,您可以在 AudioStreamPlayer(2D/3D) 上使用 pitch_scale,但这也会缩短注释。而是……
看看Godot编辑器的底部,有一个叫Audio的面板。您可以在那里配置音频总线。添加一个新总线,将PitchShift 效果添加到它。如果您选择效果,您可以在 Inspector 面板上对其进行配置。现在,在您的AudioStreamPlayer(2D/3D) 中,您可以选择具有效果的总线,然后就可以了。
除了,您必须设置很多这些。所以,让我们用代码来做吧。
首先,添加一个AudioStreamPlayer(2D/3D),就是添加一个节点:
var player := AudioStreamPlayer.new()
add_child(player)
我们要设置样本,所以还需要加载样本:
var sample := preload("res://sample.ogg")
var player := AudioStreamPlayer.new()
player.stream = sample # <--
add_child(player)
顺便说一下,仔细检查您的示例的导入设置(在“文件系统”面板中选择文件后,转到“导入”面板)是否启用了loop。
要设置音频总线,您可以这样做:
var sample := preload("res://sample.ogg")
var player := AudioStreamPlayer.new()
player.stream = sample
player.bus = "Bus Name" # <--
add_child(player)
这让我开始添加音频总线:
AudioServer.add_bus()
等等,嗯……好吧,让我们明确地将总线放在最后,这样我们就知道了索引:
var bus_index := AudioServer.get_bus_count()
AudioServer.add_bus(bus_index)
并给它一个名字:
var bus_index := AudioServer.get_bus_count()
AudioServer.add_bus(bus_index)
AudioServer.set_bus_name(bus_index, "Bus Name") # <--
让我们将它路由到 Master:
var bus_index := AudioServer.get_bus_count()
AudioServer.add_bus(bus_index)
AudioServer.set_bus_name(bus_index, "Bus Name")
AudioServer.set_bus_send(bus_index, "Master") # <--
是的,默认设置为 Master。但你可能需要改变它。
现在我们可以添加效果了:
var pitch_shift_effect := AudioEffectPitchShift.new()
pitch_shift_effect.pitch_scale = 0.1
AudioServer.add_bus_effect(bus_index, pitch_shift_effect, 0)
最后那个0是效果的索引。由于这是新创建的音频总线中的唯一效果,因此它位于索引 0 中。
而且,您可以创建一个循环并添加播放器及其相应的音频总线及其音高转换效果。
生成音频
好的,也许您根本没有样品。相反,您想产生波浪。我们也可以这样做。
这次我们将有一个AudioStreamPlayer(2D/3D),并给它一个AudioStreamGenerator(您可以在检查面板的stream属性的下拉菜单中找到它)。 编辑资源以设置您要使用的混合速率,默认为44100。但我们将尽可能接近实时地生成此音频,因此可能需要较低的速率。
现在,在脚本中,我们将从AudioStreamPlayer(2D/3D) 中获取播放对象:
onready var _playback := $Player.get_stream_playback()
或者如果您将脚本放置在播放器中,为什么不这样做:
onready var _playback := get_stream_playback()
我们可以将音频帧推送到播放对象。对于playback.push_frame,它需要一个Vector2,其中每个组件都是立体声通道之一(x = 左,y = 右)。
我们将调用get_frames_available 来确定我们需要推送多少,并且我们将在每个图形帧(即_process)中推送它们。
以下脚本将生成频率为440 (A) 的正弦波:
extends AudioStreamPlayer
onready var _playback := get_stream_playback()
onready var _sample_hz:float = stream.mix_rate
var _pulse_hz := 440.0
var _phase := 0.0
func _ready():
_fill_buffer()
func _process(_delta):
_fill_buffer()
func _fill_buffer():
var increment := _pulse_hz / _sample_hz
for frame_index in int(_playback.get_frames_available()):
_playback.push_frame(Vector2.ONE * sin(_phase * TAU))
_phase = fmod(_phase + increment, 1.0)
您可以将AudioStreamPlayer 设置为自动播放。
此代码改编自official Audio Generator demo。
当然,我们可能想要同时演奏多个这样的音符。
所以,我创建了一个如下所示的Note 类(只需在文件系统面板中添加一个新脚本):
class_name Note extends Object
var increment:float
var pulse_hz:float
var phase:float
func _init(hz:float, sample_hz):
pulse_hz = hz
phase = 0.0
increment = pulse_hz / sample_hz
func frame() -> float:
var result := sin(phase * TAU)
phase = fmod(phase + increment, 1.0)
return result
现在我们可以这样玩了:
extends AudioStreamPlayer
onready var _playback := get_stream_playback()
onready var _sample_hz:float = stream.mix_rate
onready var notes := [
Note.new(440, _sample_hz),
Note.new(554.37, _sample_hz),
Note.new(622.25, _sample_hz)
]
func _ready():
_fill_buffer()
func _process(_delta):
_fill_buffer()
func _fill_buffer():
var note_count := notes.size()
for frame_index in int(_playback.get_frames_available()):
var frame := 0.0
for note in notes:
frame += note.frame()
_playback.push_frame(Vector2.ONE * frame / note_count)
要录制,我们将向音频总线添加“录制”(AudioEffectRecord) 效果。假设您将它添加到 Master(第一条总线),它是那里的第一个效果,我们可以从这样的代码中得到 if:
var record_bus_index := 0 # Master
var record_effect_index := 0 # First Effect
var record_effect = AudioServer.get_bus_effect(record_bus_index, record_effect_index) as AudioEffectRecord
然后我们需要开始录制,像这样:
record_effect.set_recording_active(true)
当我们完成录制后,我们可以获取录制的内容(AudioStreamSample)并停止录制:
var recording := record_effect.get_recording()
record_effect.set_recording_active(false)
最后我们可以将它保存到 WAV 文件中:
recording.save_to_wav("user://recording.wav")
另请参阅official Audio Mic Record demo project。
不,引擎中没有保存 MP3 的解决方案。
【讨论】: