mirror of
https://github.com/Relintai/gdfxr.git
synced 2025-04-24 05:43:23 +02:00
Compare commits
No commits in common. "master" and "1.1.1" have entirely different histories.
12
CHANGELOG.md
12
CHANGELOG.md
@ -6,16 +6,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [1.3.0] - 2023-02-23
|
|
||||||
### Added
|
|
||||||
- Bit depth and sample rate import options.
|
|
||||||
- Paste from JSXFR.
|
|
||||||
- Add default filename for Save As dialog, with trailing number incremented.
|
|
||||||
|
|
||||||
## [1.2.0] - 2022-10-13
|
|
||||||
### Added
|
|
||||||
- Implement custom slider.
|
|
||||||
|
|
||||||
## [1.1.1] - 2022-07-30
|
## [1.1.1] - 2022-07-30
|
||||||
### Fixed
|
### Fixed
|
||||||
- Slider min value for a bipolar parameter should be -1.
|
- Slider min value for a bipolar parameter should be -1.
|
||||||
@ -33,5 +23,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
[1.0.0]: https://github.com/timothyqiu/gdfxr/releases/tag/1.0
|
[1.0.0]: https://github.com/timothyqiu/gdfxr/releases/tag/1.0
|
||||||
[1.1.0]: https://github.com/timothyqiu/gdfxr/releases/tag/1.1
|
[1.1.0]: https://github.com/timothyqiu/gdfxr/releases/tag/1.1
|
||||||
[1.1.1]: https://github.com/timothyqiu/gdfxr/releases/tag/1.1.1
|
[1.1.1]: https://github.com/timothyqiu/gdfxr/releases/tag/1.1.1
|
||||||
[1.2.0]: https://github.com/timothyqiu/gdfxr/releases/tag/1.2
|
|
||||||
[1.3.0]: https://github.com/timothyqiu/gdfxr/releases/tag/1.3
|
|
||||||
|
@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
你可以在 Godot 中把 sfxr 音效文件当作普通的音频文件使用,也可以像在原始的 sfxr 中一样对音效进行编辑。
|
你可以在 Godot 中把 sfxr 音效文件当作普通的音频文件使用,也可以像在原始的 sfxr 中一样对音效进行编辑。
|
||||||
|
|
||||||
> 🚧 如果你想在 Godot 4 里使用这个插件,请移步 [godot-4](https://github.com/timothyqiu/gdfxr/tree/godot-4) 分支。
|
|
||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
这是一个普通的 Godot 插件。安装时,先下载 ZIP 包,解压后将 `addons/` 文件夹移动到你的项目文件夹中,然后在项目设置中启用本插件。
|
这是一个普通的 Godot 插件。安装时,先下载 ZIP 包,解压后将 `addons/` 文件夹移动到你的项目文件夹中,然后在项目设置中启用本插件。
|
||||||
@ -32,8 +30,6 @@
|
|||||||
|
|
||||||
如果你希望使用原始的 sfxr 保存出的文件,请确保使用 `.sfxr` 扩展名保存。你也可以在原始的 sfxr 中加载并编辑 `.sfxr` 文件。
|
如果你希望使用原始的 sfxr 保存出的文件,请确保使用 `.sfxr` 扩展名保存。你也可以在原始的 sfxr 中加载并编辑 `.sfxr` 文件。
|
||||||
|
|
||||||
`.sfxr` 文件有循环(Loop)、位深度(Bit Depth)、采样率(Sample Rate)等导入选项。可以在 Godot 编辑器的导入面板中找到。
|
|
||||||
|
|
||||||
**注意:** 由于 GDScript 的性能限制,生成较长的音效时编辑器可能会有短暂的停滞。只有编辑器会受此影响。在游戏中使用 `.sfxr` 文件是不会在运行时生成任何东西的。
|
**注意:** 由于 GDScript 的性能限制,生成较长的音效时编辑器可能会有短暂的停滞。只有编辑器会受此影响。在游戏中使用 `.sfxr` 文件是不会在运行时生成任何东西的。
|
||||||
|
|
||||||
## 更新日志
|
## 更新日志
|
||||||
|
@ -12,8 +12,6 @@ the popular program of choice to make retro sound effects for games.
|
|||||||
You can use sfxr sound files like regular audio files in Godot and edit sound files like in the
|
You can use sfxr sound files like regular audio files in Godot and edit sound files like in the
|
||||||
original sfxr.
|
original sfxr.
|
||||||
|
|
||||||
> 🚧 Checkout the [godot-4](https://github.com/timothyqiu/gdfxr/tree/godot-4) branch if you want to use this plugin in Godot 4.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
This is a regular plugin for Godot. To install, download the ZIP archive, extract it, and move the
|
This is a regular plugin for Godot. To install, download the ZIP archive, extract it, and move the
|
||||||
@ -42,9 +40,6 @@ But they can be used directly as regular `AudioStream`s.
|
|||||||
If you want to reuse an existing sound from the original sfxr, make sure to save it with an
|
If you want to reuse an existing sound from the original sfxr, make sure to save it with an
|
||||||
`.sfxr` extension. You can also load & edit the `.sfxr` file with the original sfxr.
|
`.sfxr` extension. You can also load & edit the `.sfxr` file with the original sfxr.
|
||||||
|
|
||||||
Options for changing Looping, Bit Depth, and Sample Rate are available as import options
|
|
||||||
of the `.sfxr` file. You can find these options in Godot editor's Import dock.
|
|
||||||
|
|
||||||
**Note:** Due to performance constraints with GDScript, the editor may freeze a bit when generating
|
**Note:** Due to performance constraints with GDScript, the editor may freeze a bit when generating
|
||||||
long sounds. This only happens in-editor.
|
long sounds. This only happens in-editor.
|
||||||
Using `.sfxr` files in-game won't generate anything at runtime.
|
Using `.sfxr` files in-game won't generate anything at runtime.
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
extends Object
|
|
||||||
|
|
||||||
const BASE_58_ALPHABET := "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
|
||||||
|
|
||||||
|
|
||||||
static func b58decode(v: String) -> StreamPeerBuffer:
|
|
||||||
# Base 58 is a number expressed in the base-58 numeral system.
|
|
||||||
# When encoding data, big-endian is used and leading zeros are encoded as leading `1`s.
|
|
||||||
|
|
||||||
var original_length := v.length()
|
|
||||||
v = v.lstrip(BASE_58_ALPHABET[0])
|
|
||||||
var zeros := original_length - v.length()
|
|
||||||
|
|
||||||
var buffer := PoolByteArray()
|
|
||||||
buffer.resize(v.length()) # Won't be as long as base 58 string since the buffer is 256-based.
|
|
||||||
buffer.fill(0)
|
|
||||||
|
|
||||||
var length := 0
|
|
||||||
for c in v:
|
|
||||||
var carry := BASE_58_ALPHABET.find(c)
|
|
||||||
if carry == -1:
|
|
||||||
return null
|
|
||||||
var i := 0
|
|
||||||
while carry != 0 or i < length:
|
|
||||||
var pos := buffer.size() - 1 - i
|
|
||||||
carry += 58 * buffer[pos]
|
|
||||||
buffer[pos] = carry % 256
|
|
||||||
carry /= 256
|
|
||||||
i += 1
|
|
||||||
length = i
|
|
||||||
|
|
||||||
var result := StreamPeerBuffer.new()
|
|
||||||
for _i in zeros:
|
|
||||||
result.put_8(0)
|
|
||||||
result.put_data(buffer.subarray(buffer.size() - length, -1))
|
|
||||||
result.seek(0)
|
|
||||||
|
|
||||||
return result
|
|
@ -18,8 +18,6 @@ enum Category {
|
|||||||
BLIP_SELECT,
|
BLIP_SELECT,
|
||||||
}
|
}
|
||||||
|
|
||||||
const Base58 := preload("res://addons/gdfxr/Base58.gd")
|
|
||||||
|
|
||||||
var wave_type: int = WaveType.SQUARE_WAVE
|
var wave_type: int = WaveType.SQUARE_WAVE
|
||||||
|
|
||||||
var p_env_attack := 0.0 # Attack Time
|
var p_env_attack := 0.0 # Attack Time
|
||||||
@ -467,45 +465,3 @@ func is_equal(other: Reference) -> bool: # SFXRConfig
|
|||||||
|
|
||||||
and sound_vol == other.sound_vol
|
and sound_vol == other.sound_vol
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# Load base58 string copied from jsfxr
|
|
||||||
# See https://github.com/chr15m/jsfxr/blob/a708164e6ce200008d88202e1aaf2b9171a17ec2/sfxr.js#L132-L175
|
|
||||||
func load_from_base58(v: String) -> int: # Error
|
|
||||||
var buffer := Base58.b58decode(v)
|
|
||||||
if not buffer:
|
|
||||||
return ERR_INVALID_DATA
|
|
||||||
if buffer.get_size() != 89:
|
|
||||||
return ERR_INVALID_DATA
|
|
||||||
|
|
||||||
var params_order = [
|
|
||||||
"p_env_attack",
|
|
||||||
"p_env_sustain",
|
|
||||||
"p_env_punch",
|
|
||||||
"p_env_decay",
|
|
||||||
"p_base_freq",
|
|
||||||
"p_freq_limit",
|
|
||||||
"p_freq_ramp",
|
|
||||||
"p_freq_dramp",
|
|
||||||
"p_vib_strength",
|
|
||||||
"p_vib_speed",
|
|
||||||
"p_arp_mod",
|
|
||||||
"p_arp_speed",
|
|
||||||
"p_duty",
|
|
||||||
"p_duty_ramp",
|
|
||||||
"p_repeat_speed",
|
|
||||||
"p_pha_offset",
|
|
||||||
"p_pha_ramp",
|
|
||||||
"p_lpf_freq",
|
|
||||||
"p_lpf_ramp",
|
|
||||||
"p_lpf_resonance",
|
|
||||||
"p_hpf_freq",
|
|
||||||
"p_hpf_ramp",
|
|
||||||
]
|
|
||||||
|
|
||||||
wave_type = buffer.get_8()
|
|
||||||
|
|
||||||
for param in params_order:
|
|
||||||
set(param, buffer.get_float())
|
|
||||||
|
|
||||||
return OK
|
|
||||||
|
@ -5,15 +5,6 @@ const SFXRConfig := preload("SFXRConfig.gd")
|
|||||||
|
|
||||||
const master_vol := 0.05
|
const master_vol := 0.05
|
||||||
|
|
||||||
enum WavBits {
|
|
||||||
WAV_BITS_8,
|
|
||||||
WAV_BITS_16,
|
|
||||||
}
|
|
||||||
enum WavFreq {
|
|
||||||
WAV_FREQ_44100,
|
|
||||||
WAV_FREQ_22050,
|
|
||||||
}
|
|
||||||
|
|
||||||
var _config: SFXRConfig
|
var _config: SFXRConfig
|
||||||
|
|
||||||
var rep_time: int
|
var rep_time: int
|
||||||
@ -50,42 +41,32 @@ var fltdmp: float
|
|||||||
var fltphp: float
|
var fltphp: float
|
||||||
|
|
||||||
|
|
||||||
func generate_audio_stream(
|
func generate_audio_stream(config: SFXRConfig) -> AudioStreamSample:
|
||||||
config: SFXRConfig,
|
|
||||||
wav_bits: int = WavBits.WAV_BITS_8,
|
|
||||||
wav_freq: int = WavFreq.WAV_FREQ_44100
|
|
||||||
) -> AudioStreamSample:
|
|
||||||
var stream := AudioStreamSample.new()
|
var stream := AudioStreamSample.new()
|
||||||
stream.format = AudioStreamSample.FORMAT_8_BITS if wav_bits == WavBits.WAV_BITS_8 else AudioStreamSample.FORMAT_16_BITS
|
stream.format = AudioStreamSample.FORMAT_8_BITS
|
||||||
stream.mix_rate = 44100 if wav_freq == WavFreq.WAV_FREQ_44100 else 22050
|
stream.mix_rate = 44100
|
||||||
|
|
||||||
_config = config
|
_config = config
|
||||||
stream.data = _generate_samples(wav_bits, wav_freq).data_array
|
stream.data = _generate_samples()
|
||||||
_config = null
|
_config = null
|
||||||
|
|
||||||
return stream
|
return stream
|
||||||
|
|
||||||
|
|
||||||
func generate_samples(
|
func generate_samples(config: SFXRConfig) -> PoolByteArray:
|
||||||
config: SFXRConfig,
|
|
||||||
wav_bits: int = WavBits.WAV_BITS_8,
|
|
||||||
wav_freq: int = WavFreq.WAV_FREQ_44100
|
|
||||||
) -> PoolByteArray:
|
|
||||||
_config = config
|
_config = config
|
||||||
var data := _generate_samples(wav_bits, wav_freq).data_array
|
var data := _generate_samples()
|
||||||
_config = null
|
_config = null
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
func _generate_samples(wav_bits: int, wav_freq: int) -> StreamPeerBuffer:
|
func _generate_samples() -> PoolByteArray:
|
||||||
_reset_sample(true)
|
_reset_sample(true)
|
||||||
|
|
||||||
var playing_sample := true
|
var playing_sample := true
|
||||||
var env_stage := 0
|
var env_stage := 0
|
||||||
var env_time := 0
|
var env_time := 0
|
||||||
var filesample: float = 0
|
var output := PoolByteArray([])
|
||||||
var fileacc := 0
|
|
||||||
var buffer := StreamPeerBuffer.new()
|
|
||||||
|
|
||||||
# SynthSample
|
# SynthSample
|
||||||
while playing_sample:
|
while playing_sample:
|
||||||
@ -193,21 +174,15 @@ func _generate_samples(wav_bits: int, wav_freq: int) -> StreamPeerBuffer:
|
|||||||
ssample *= 4.0 # arbitrary gain to get reasonable output volume...
|
ssample *= 4.0 # arbitrary gain to get reasonable output volume...
|
||||||
ssample = clamp(ssample, -1.0, +1.0)
|
ssample = clamp(ssample, -1.0, +1.0)
|
||||||
|
|
||||||
filesample += ssample
|
var filesample := int((1 + ssample) / 2 * 255)
|
||||||
fileacc += 1
|
|
||||||
|
|
||||||
if wav_freq == WavFreq.WAV_FREQ_44100 or fileacc == 2:
|
# This is a hack, AudioStreamSample wants a int8_t directly interpreted as uint8_t
|
||||||
filesample /= fileacc
|
filesample += 128
|
||||||
fileacc = 0
|
if filesample > 255:
|
||||||
|
filesample -= 255
|
||||||
|
|
||||||
if wav_bits == WavBits.WAV_BITS_8:
|
output.push_back(filesample)
|
||||||
buffer.put_8(filesample * 255)
|
return output
|
||||||
else:
|
|
||||||
buffer.put_16(filesample * 32000)
|
|
||||||
|
|
||||||
filesample = 0
|
|
||||||
|
|
||||||
return buffer
|
|
||||||
|
|
||||||
|
|
||||||
func _reset_sample(restart: bool) -> void:
|
func _reset_sample(restart: bool) -> void:
|
||||||
|
@ -1,203 +0,0 @@
|
|||||||
tool
|
|
||||||
extends Control
|
|
||||||
|
|
||||||
signal value_changed(value)
|
|
||||||
|
|
||||||
export var value: float = 0.0 setget set_value
|
|
||||||
export var min_value: float = 0.0
|
|
||||||
export var max_value: float = 1.0
|
|
||||||
|
|
||||||
var _line_edit: LineEdit
|
|
||||||
|
|
||||||
var _stylebox_normal: StyleBox
|
|
||||||
var _stylebox_hover: StyleBox
|
|
||||||
var _stylebox_editing: StyleBox
|
|
||||||
var _stylebox_value: StyleBox
|
|
||||||
|
|
||||||
var _line_edit_just_closed := false
|
|
||||||
var _mouse_hovering := false
|
|
||||||
var _is_editing := false
|
|
||||||
|
|
||||||
var _drag_start_position: Vector2
|
|
||||||
var _drag_cancelled := true
|
|
||||||
var _drag_dist := 0.0
|
|
||||||
var _drag_start_factor: float
|
|
||||||
|
|
||||||
|
|
||||||
func _init() -> void:
|
|
||||||
mouse_default_cursor_shape = Control.CURSOR_HSIZE
|
|
||||||
rect_clip_content = true
|
|
||||||
focus_mode = Control.FOCUS_ALL
|
|
||||||
|
|
||||||
var style := StyleBoxEmpty.new()
|
|
||||||
style.content_margin_left = 8
|
|
||||||
style.content_margin_right = 8
|
|
||||||
|
|
||||||
_line_edit = LineEdit.new()
|
|
||||||
_line_edit.set_as_toplevel(true)
|
|
||||||
_line_edit.visible = false
|
|
||||||
_line_edit.add_theme_stylebox_override("normal", style)
|
|
||||||
_line_edit.add_theme_stylebox_override("focus", StyleBoxEmpty.new())
|
|
||||||
|
|
||||||
var _ret: int
|
|
||||||
_ret = _line_edit.connect("focus_exited", self, "_on_line_edit_focus_exited")
|
|
||||||
_ret = _line_edit.connect("text_entered", self, "_on_line_edit_text_entered")
|
|
||||||
_ret = _line_edit.connect("visibility_changed", self, "_on_line_edit_visibility_changed")
|
|
||||||
|
|
||||||
add_child(_line_edit)
|
|
||||||
|
|
||||||
|
|
||||||
func _draw() -> void:
|
|
||||||
var font := get_theme_font("font", "LineEdit")
|
|
||||||
var color := get_theme_color("highlighted_font_color" if _mouse_hovering else "font_color", "Editor")
|
|
||||||
var number_string := "%.3f" % value
|
|
||||||
var number_size := font.get_string_size(number_string)
|
|
||||||
var pos := Vector2(
|
|
||||||
(rect_size.x - number_size.x) / 2,
|
|
||||||
(rect_size.y - number_size.y) / 2 + font.get_ascent()
|
|
||||||
)
|
|
||||||
|
|
||||||
var stylebox := _stylebox_editing if _is_editing else _stylebox_hover if _mouse_hovering else _stylebox_normal
|
|
||||||
|
|
||||||
if _line_edit.visible:
|
|
||||||
draw_style_box(stylebox, Rect2(Vector2.ZERO, rect_size))
|
|
||||||
else:
|
|
||||||
var value_width := rect_size.x * ((value - min_value) / (max_value - min_value))
|
|
||||||
draw_style_box(stylebox, Rect2(value_width, 0, rect_size.x - value_width, rect_size.y))
|
|
||||||
draw_style_box(_stylebox_value, Rect2(0, 0, value_width, rect_size.y))
|
|
||||||
draw_string(font, pos, number_string, color)
|
|
||||||
|
|
||||||
|
|
||||||
func _get_minimum_size() -> Vector2:
|
|
||||||
var ms := _stylebox_normal.get_minimum_size()
|
|
||||||
ms.y += get_theme_font("font", "LineEdit").get_height()
|
|
||||||
return ms
|
|
||||||
|
|
||||||
|
|
||||||
func _gui_input(event: InputEvent) -> void:
|
|
||||||
var mb := event as InputEventMouseButton
|
|
||||||
if mb and mb.button_index == BUTTON_LEFT:
|
|
||||||
if mb.pressed:
|
|
||||||
_drag_prepare(mb)
|
|
||||||
else:
|
|
||||||
_drag_done()
|
|
||||||
if _drag_cancelled:
|
|
||||||
_show_text_edit()
|
|
||||||
_drag_cancelled = true
|
|
||||||
_is_editing = mb.pressed
|
|
||||||
update()
|
|
||||||
|
|
||||||
var mm := event as InputEventMouseMotion
|
|
||||||
if mm and mm.button_mask & BUTTON_MASK_LEFT:
|
|
||||||
_drag_motion(mm)
|
|
||||||
_drag_cancelled = false
|
|
||||||
|
|
||||||
|
|
||||||
func _notification(what: int) -> void:
|
|
||||||
match what:
|
|
||||||
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
|
|
||||||
_update_stylebox()
|
|
||||||
|
|
||||||
NOTIFICATION_MOUSE_ENTER:
|
|
||||||
_mouse_hovering = true
|
|
||||||
update()
|
|
||||||
|
|
||||||
NOTIFICATION_MOUSE_EXIT:
|
|
||||||
_mouse_hovering = false
|
|
||||||
update()
|
|
||||||
|
|
||||||
NOTIFICATION_FOCUS_ENTER:
|
|
||||||
if (Input.is_action_pressed("ui_focus_next") or Input.is_action_pressed("ui_focus_prev")) and not _line_edit_just_closed:
|
|
||||||
_show_text_edit()
|
|
||||||
_line_edit_just_closed = false
|
|
||||||
|
|
||||||
|
|
||||||
func set_value(v: float) -> void:
|
|
||||||
if is_equal_approx(v, value):
|
|
||||||
return
|
|
||||||
value = v
|
|
||||||
emit_signal("value_changed", value)
|
|
||||||
update()
|
|
||||||
|
|
||||||
|
|
||||||
func _update_stylebox() -> void:
|
|
||||||
_stylebox_normal = get_theme_stylebox("normal", "LineEdit")
|
|
||||||
_stylebox_hover = StyleBoxFlat.new()
|
|
||||||
_stylebox_hover.bg_color = get_theme_color("highlight_color", "Editor")
|
|
||||||
_stylebox_editing = StyleBoxFlat.new()
|
|
||||||
_stylebox_editing.bg_color = get_theme_color("dark_color_2", "Editor")
|
|
||||||
_stylebox_value = StyleBoxFlat.new()
|
|
||||||
_stylebox_value.bg_color = get_theme_color("accent_color", "Editor") * Color(1, 1, 1, 0.4)
|
|
||||||
|
|
||||||
|
|
||||||
func _drag_prepare(mouse: InputEventMouse) -> void:
|
|
||||||
_drag_dist = 0.0
|
|
||||||
_drag_start_factor = (value - min_value) / (max_value - min_value)
|
|
||||||
_drag_start_position = mouse.global_position
|
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
||||||
|
|
||||||
|
|
||||||
func _drag_done() -> void:
|
|
||||||
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
|
||||||
|
|
||||||
if _drag_cancelled:
|
|
||||||
Input.warp_mouse_position(_drag_start_position)
|
|
||||||
else:
|
|
||||||
Input.warp_mouse_position(rect_global_position + rect_size * Vector2(
|
|
||||||
(value - min_value) / (max_value - min_value),
|
|
||||||
0.5
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
func _drag_motion(motion: InputEventMouseMotion) -> void:
|
|
||||||
_drag_dist += motion.relative.x
|
|
||||||
|
|
||||||
var factor := _drag_start_factor + _drag_dist / rect_size.x
|
|
||||||
if factor < 0 or 1 < factor:
|
|
||||||
factor = clamp(factor, 0, 1)
|
|
||||||
_drag_dist = (factor - _drag_start_factor) * rect_size.x
|
|
||||||
|
|
||||||
var v := factor * (max_value - min_value) + min_value
|
|
||||||
var snap := motion.command or motion.shift
|
|
||||||
if snap and not (is_equal_approx(v, min_value) or is_equal_approx(v, max_value)):
|
|
||||||
if motion.shift and motion.command:
|
|
||||||
v = round(v * 1000.0) * 0.001
|
|
||||||
elif motion.shift:
|
|
||||||
v = round(v * 100.0) * 0.01
|
|
||||||
else:
|
|
||||||
v = round(v * 10.0) * 0.1
|
|
||||||
|
|
||||||
set_value(clamp(v, min_value, max_value))
|
|
||||||
|
|
||||||
update()
|
|
||||||
|
|
||||||
|
|
||||||
func _show_text_edit() -> void:
|
|
||||||
var gr := get_global_rect()
|
|
||||||
_line_edit.text = str(value)
|
|
||||||
_line_edit.set_position(gr.position)
|
|
||||||
_line_edit.set_size(gr.size)
|
|
||||||
_line_edit.show_modal()
|
|
||||||
_line_edit.select_all()
|
|
||||||
_line_edit.grab_focus()
|
|
||||||
_line_edit.focus_next = find_next_valid_focus().get_path()
|
|
||||||
_line_edit.focus_previous = find_prev_valid_focus().get_path()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_line_edit_focus_exited():
|
|
||||||
if _line_edit.get_menu().visible:
|
|
||||||
return
|
|
||||||
if _line_edit.text.is_valid_float():
|
|
||||||
set_value(clamp(_line_edit.text.to_float(), min_value, max_value))
|
|
||||||
if not _line_edit_just_closed:
|
|
||||||
_line_edit.hide()
|
|
||||||
update()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_line_edit_text_entered(_text: String):
|
|
||||||
_line_edit.hide()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_line_edit_visibility_changed():
|
|
||||||
if not _line_edit.visible:
|
|
||||||
_line_edit_just_closed = true
|
|
@ -1,12 +1,10 @@
|
|||||||
tool
|
tool
|
||||||
extends Container
|
extends Container
|
||||||
|
|
||||||
enum ExtraOption { SAVE_AS, COPY, PASTE, PASTE_JSFXR, RECENT }
|
enum ExtraOption { SAVE_AS, COPY, PASTE, RECENT }
|
||||||
enum DefaultFilename { EMPTY, GUESS_FOR_SAVE }
|
|
||||||
|
|
||||||
const SFXRConfig := preload("../SFXRConfig.gd")
|
const SFXRConfig := preload("../SFXRConfig.gd")
|
||||||
const SFXRGenerator := preload("../SFXRGenerator.gd")
|
const SFXRGenerator := preload("../SFXRGenerator.gd")
|
||||||
const Base58 := preload("../Base58.gd")
|
|
||||||
const NUM_RECENTS := 4
|
const NUM_RECENTS := 4
|
||||||
|
|
||||||
class RecentEntry:
|
class RecentEntry:
|
||||||
@ -48,7 +46,6 @@ func _ready():
|
|||||||
popup.add_separator()
|
popup.add_separator()
|
||||||
popup.add_item(translator.tr("Copy"), ExtraOption.COPY)
|
popup.add_item(translator.tr("Copy"), ExtraOption.COPY)
|
||||||
popup.add_item(translator.tr("Paste"), ExtraOption.PASTE)
|
popup.add_item(translator.tr("Paste"), ExtraOption.PASTE)
|
||||||
popup.add_item(translator.tr("Paste from jsfxr"), ExtraOption.PASTE_JSFXR)
|
|
||||||
popup.add_separator(translator.tr("Recently Generated"))
|
popup.add_separator(translator.tr("Recently Generated"))
|
||||||
popup.connect("id_pressed", self, "_on_Extra_id_pressed")
|
popup.connect("id_pressed", self, "_on_Extra_id_pressed")
|
||||||
|
|
||||||
@ -78,18 +75,18 @@ func _notification(what: int):
|
|||||||
|
|
||||||
match what:
|
match what:
|
||||||
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
|
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
|
||||||
find_node("ScrollContainer").add_theme_stylebox_override("bg", get_theme_stylebox("bg", "Tree"))
|
find_node("ScrollContainer").add_stylebox_override("bg", get_stylebox("bg", "Tree"))
|
||||||
|
|
||||||
if extra_button:
|
if extra_button:
|
||||||
var popup = extra_button.get_popup()
|
var popup = extra_button.get_popup()
|
||||||
popup.set_item_icon(popup.get_item_index(ExtraOption.COPY), get_theme_icon("ActionCopy", "EditorIcons"))
|
popup.set_item_icon(popup.get_item_index(ExtraOption.COPY), get_icon("ActionCopy", "EditorIcons"))
|
||||||
popup.set_item_icon(popup.get_item_index(ExtraOption.PASTE), get_theme_icon("ActionPaste", "EditorIcons"))
|
popup.set_item_icon(popup.get_item_index(ExtraOption.PASTE), get_icon("ActionPaste", "EditorIcons"))
|
||||||
|
|
||||||
|
|
||||||
func edit(path: String) -> void:
|
func edit(path: String) -> void:
|
||||||
if _modified:
|
if _modified:
|
||||||
_popup_confirm(
|
_popup_confirm(
|
||||||
str(translator.tr("There are unsaved changes.\nOpen '%s' anyway?")) % path,
|
translator.tr("There are unsaved changes.\nOpen '%s' anyway?") % path,
|
||||||
"_set_editing_file", [path]
|
"_set_editing_file", [path]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@ -136,20 +133,11 @@ func _popup_message(content: String) -> void:
|
|||||||
dialog.popup_centered()
|
dialog.popup_centered()
|
||||||
|
|
||||||
|
|
||||||
func _popup_file_dialog(mode: int, callback: String, default_filename: int = DefaultFilename.EMPTY) -> void:
|
func _popup_file_dialog(mode: int, callback: String) -> void:
|
||||||
var dialog := EditorFileDialog.new()
|
var dialog := EditorFileDialog.new()
|
||||||
add_child(dialog)
|
add_child(dialog)
|
||||||
dialog.access = EditorFileDialog.ACCESS_RESOURCES
|
dialog.access = EditorFileDialog.ACCESS_RESOURCES
|
||||||
dialog.mode = mode
|
dialog.mode = mode
|
||||||
|
|
||||||
match default_filename:
|
|
||||||
DefaultFilename.EMPTY:
|
|
||||||
pass
|
|
||||||
|
|
||||||
DefaultFilename.GUESS_FOR_SAVE:
|
|
||||||
if _path:
|
|
||||||
dialog.current_path = _generate_serial_path(_path)
|
|
||||||
|
|
||||||
dialog.add_filter("*.sfxr; %s" % translator.tr("SFXR Audio"))
|
dialog.add_filter("*.sfxr; %s" % translator.tr("SFXR Audio"))
|
||||||
dialog.connect("popup_hide", dialog, "queue_free")
|
dialog.connect("popup_hide", dialog, "queue_free")
|
||||||
dialog.connect("file_selected", self, callback)
|
dialog.connect("file_selected", self, callback)
|
||||||
@ -176,7 +164,7 @@ func _set_editing_file(path: String) -> int: # Error
|
|||||||
else:
|
else:
|
||||||
var err := _config.load(path)
|
var err := _config.load(path)
|
||||||
if err != OK:
|
if err != OK:
|
||||||
_popup_message(str(translator.tr("'%s' is not a valid SFXR file.")) % path)
|
_popup_message(translator.tr("'%s' is not a valid SFXR file.") % path)
|
||||||
return err
|
return err
|
||||||
audio_player.stream = load(path)
|
audio_player.stream = load(path)
|
||||||
|
|
||||||
@ -189,7 +177,7 @@ func _set_modified(value: bool) -> void:
|
|||||||
_modified = value
|
_modified = value
|
||||||
|
|
||||||
var has_file := not _path.empty()
|
var has_file := not _path.empty()
|
||||||
var base = _path if has_file else str(translator.tr("Unsaved sound"))
|
var base = _path if has_file else translator.tr("Unsaved sound")
|
||||||
if _modified:
|
if _modified:
|
||||||
base += "(*)"
|
base += "(*)"
|
||||||
filename_label.text = base
|
filename_label.text = base
|
||||||
@ -207,37 +195,6 @@ func _sync_ui() -> void:
|
|||||||
_syncing_ui = false
|
_syncing_ui = false
|
||||||
|
|
||||||
|
|
||||||
func _generate_serial_path(path: String) -> String:
|
|
||||||
var directory := Directory.new()
|
|
||||||
if directory.open(path.get_base_dir()) != OK:
|
|
||||||
return path
|
|
||||||
|
|
||||||
if not directory.file_exists(path.get_file()):
|
|
||||||
return path
|
|
||||||
|
|
||||||
var basename := path.get_basename()
|
|
||||||
var extension := path.get_extension()
|
|
||||||
|
|
||||||
# Extract trailing number.
|
|
||||||
var num_string: String
|
|
||||||
for i in range(basename.length() - 1, -1, -1):
|
|
||||||
var c: String = basename[i]
|
|
||||||
if "0" <= c and c <= "9":
|
|
||||||
num_string = c + num_string
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
var number := num_string.to_int() if num_string else 0
|
|
||||||
var name_string: String = basename.substr(0, basename.length() - num_string.length())
|
|
||||||
|
|
||||||
while true:
|
|
||||||
number += 1
|
|
||||||
var attemp := "%s%d.%s" % [name_string, number, extension]
|
|
||||||
if not directory.file_exists(attemp):
|
|
||||||
return attemp
|
|
||||||
|
|
||||||
return path # Unreachable
|
|
||||||
|
|
||||||
|
|
||||||
func _on_param_changed(name, value):
|
func _on_param_changed(name, value):
|
||||||
if _syncing_ui:
|
if _syncing_ui:
|
||||||
return
|
return
|
||||||
@ -339,7 +296,6 @@ func _on_Load_pressed():
|
|||||||
func _on_Extra_about_to_show():
|
func _on_Extra_about_to_show():
|
||||||
var popup := extra_button.get_popup()
|
var popup := extra_button.get_popup()
|
||||||
popup.set_item_disabled(popup.get_item_index(ExtraOption.PASTE), _config_clipboard == null)
|
popup.set_item_disabled(popup.get_item_index(ExtraOption.PASTE), _config_clipboard == null)
|
||||||
popup.set_item_disabled(popup.get_item_index(ExtraOption.PASTE_JSFXR), not OS.has_clipboard())
|
|
||||||
|
|
||||||
# Rebuild recents menu everytime :)
|
# Rebuild recents menu everytime :)
|
||||||
var first_recent_index := popup.get_item_index(ExtraOption.RECENT)
|
var first_recent_index := popup.get_item_index(ExtraOption.RECENT)
|
||||||
@ -359,7 +315,7 @@ func _on_Extra_about_to_show():
|
|||||||
func _on_Extra_id_pressed(id: int) -> void:
|
func _on_Extra_id_pressed(id: int) -> void:
|
||||||
match id:
|
match id:
|
||||||
ExtraOption.SAVE_AS:
|
ExtraOption.SAVE_AS:
|
||||||
_popup_file_dialog(EditorFileDialog.MODE_SAVE_FILE, "_on_SaveAsDialog_confirmed", DefaultFilename.GUESS_FOR_SAVE)
|
_popup_file_dialog(EditorFileDialog.MODE_SAVE_FILE, "_on_SaveAsDialog_confirmed")
|
||||||
|
|
||||||
ExtraOption.COPY:
|
ExtraOption.COPY:
|
||||||
if not _config_clipboard:
|
if not _config_clipboard:
|
||||||
@ -369,13 +325,6 @@ func _on_Extra_id_pressed(id: int) -> void:
|
|||||||
ExtraOption.PASTE:
|
ExtraOption.PASTE:
|
||||||
_restore_from_config(_config_clipboard)
|
_restore_from_config(_config_clipboard)
|
||||||
|
|
||||||
ExtraOption.PASTE_JSFXR:
|
|
||||||
var pasted := SFXRConfig.new()
|
|
||||||
if pasted.load_from_base58(OS.clipboard) == OK:
|
|
||||||
_restore_from_config(pasted)
|
|
||||||
else:
|
|
||||||
_popup_message(translator.tr("Clipboard does not contain code copied from jsfxr."))
|
|
||||||
|
|
||||||
_:
|
_:
|
||||||
var i := id - ExtraOption.RECENT as int
|
var i := id - ExtraOption.RECENT as int
|
||||||
if i < 0 or _config_recents.size() <= i:
|
if i < 0 or _config_recents.size() <= i:
|
||||||
|
@ -13,4 +13,4 @@ func _notification(what: int):
|
|||||||
match what:
|
match what:
|
||||||
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
|
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
|
||||||
if icon_name:
|
if icon_name:
|
||||||
icon = get_theme_icon(icon_name, "EditorIcons")
|
icon = get_icon(icon_name, "EditorIcons")
|
||||||
|
@ -9,10 +9,13 @@ margin_bottom = 40.0
|
|||||||
rect_pivot_offset = Vector2( 41, -65 )
|
rect_pivot_offset = Vector2( 41, -65 )
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
script = ExtResource( 1 )
|
script = ExtResource( 1 )
|
||||||
|
__meta__ = {
|
||||||
|
"_edit_use_anchors_": false
|
||||||
|
}
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="."]
|
[node name="Label" type="Label" parent="."]
|
||||||
margin_top = 13.0
|
margin_top = 13.0
|
||||||
margin_right = 128.0
|
margin_right = 133.0
|
||||||
margin_bottom = 27.0
|
margin_bottom = 27.0
|
||||||
rect_min_size = Vector2( 100, 0 )
|
rect_min_size = Vector2( 100, 0 )
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
@ -20,11 +23,11 @@ text = "Waveform"
|
|||||||
align = 2
|
align = 2
|
||||||
|
|
||||||
[node name="OptionButton" type="OptionButton" parent="."]
|
[node name="OptionButton" type="OptionButton" parent="."]
|
||||||
margin_left = 132.0
|
margin_left = 137.0
|
||||||
margin_top = 10.0
|
margin_top = 10.0
|
||||||
margin_right = 237.0
|
margin_right = 237.0
|
||||||
margin_bottom = 30.0
|
margin_bottom = 30.0
|
||||||
rect_min_size = Vector2( 105, 0 )
|
rect_min_size = Vector2( 100, 0 )
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
clip_text = true
|
clip_text = true
|
||||||
|
|
||||||
@ -33,9 +36,7 @@ margin_left = 241.0
|
|||||||
margin_top = 9.0
|
margin_top = 9.0
|
||||||
margin_right = 253.0
|
margin_right = 253.0
|
||||||
margin_bottom = 31.0
|
margin_bottom = 31.0
|
||||||
focus_mode = 0
|
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
enabled_focus_mode = 0
|
|
||||||
script = ExtResource( 2 )
|
script = ExtResource( 2 )
|
||||||
icon_name = "ReloadSmall"
|
icon_name = "ReloadSmall"
|
||||||
|
|
||||||
|
@ -40,4 +40,3 @@ func _on_HSlider_value_changed(value: float):
|
|||||||
|
|
||||||
func _on_Reset_pressed():
|
func _on_Reset_pressed():
|
||||||
emit_signal("param_reset", parameter)
|
emit_signal("param_reset", parameter)
|
||||||
|
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
[gd_scene load_steps=4 format=2]
|
[gd_scene load_steps=3 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://addons/gdfxr/editor/ParamSlider.gd" type="Script" id=1]
|
[ext_resource path="res://addons/gdfxr/editor/ParamSlider.gd" type="Script" id=1]
|
||||||
[ext_resource path="res://addons/gdfxr/editor/EditorIconButton.gd" type="Script" id=2]
|
[ext_resource path="res://addons/gdfxr/editor/EditorIconButton.gd" type="Script" id=2]
|
||||||
[ext_resource path="res://addons/gdfxr/editor/EditSlider.gd" type="Script" id=3]
|
|
||||||
|
|
||||||
[node name="ParamSlider" type="HBoxContainer"]
|
[node name="ParamSlider" type="HBoxContainer"]
|
||||||
margin_right = 253.0
|
margin_right = 253.0
|
||||||
@ -12,28 +11,27 @@ script = ExtResource( 1 )
|
|||||||
|
|
||||||
[node name="Label" type="Label" parent="."]
|
[node name="Label" type="Label" parent="."]
|
||||||
margin_top = 13.0
|
margin_top = 13.0
|
||||||
margin_right = 128.0
|
margin_right = 143.0
|
||||||
margin_bottom = 27.0
|
margin_bottom = 27.0
|
||||||
rect_min_size = Vector2( 100, 0 )
|
rect_min_size = Vector2( 100, 0 )
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
align = 2
|
align = 2
|
||||||
|
|
||||||
[node name="HSlider" type="Control" parent="."]
|
[node name="HSlider" type="HSlider" parent="."]
|
||||||
margin_left = 132.0
|
margin_left = 147.0
|
||||||
|
margin_top = 12.0
|
||||||
margin_right = 237.0
|
margin_right = 237.0
|
||||||
margin_bottom = 40.0
|
margin_bottom = 28.0
|
||||||
rect_min_size = Vector2( 105, 0 )
|
rect_min_size = Vector2( 90, 0 )
|
||||||
rect_clip_content = true
|
size_flags_vertical = 4
|
||||||
focus_mode = 2
|
max_value = 1.0
|
||||||
mouse_default_cursor_shape = 10
|
step = 0.0
|
||||||
script = ExtResource( 3 )
|
|
||||||
|
|
||||||
[node name="Reset" type="ToolButton" parent="."]
|
[node name="Reset" type="ToolButton" parent="."]
|
||||||
margin_left = 241.0
|
margin_left = 241.0
|
||||||
margin_top = 9.0
|
margin_top = 9.0
|
||||||
margin_right = 253.0
|
margin_right = 253.0
|
||||||
margin_bottom = 31.0
|
margin_bottom = 31.0
|
||||||
focus_mode = 0
|
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
script = ExtResource( 2 )
|
script = ExtResource( 2 )
|
||||||
icon_name = "ReloadSmall"
|
icon_name = "ReloadSmall"
|
||||||
|
@ -25,10 +25,10 @@ func set_plugin(v: EditorPlugin) -> void:
|
|||||||
_translate_node(get_parent())
|
_translate_node(get_parent())
|
||||||
|
|
||||||
|
|
||||||
func tr(message: StringName) -> StringName:
|
func tr(message: String) -> String:
|
||||||
if _translation:
|
if _translation:
|
||||||
var translated := _translation.get_message(message)
|
var translated := _translation.get_message(message)
|
||||||
if translated != "":
|
if not translated.empty():
|
||||||
return translated
|
return translated
|
||||||
return message
|
return message
|
||||||
|
|
||||||
@ -49,8 +49,5 @@ func _translate_node(node: Node):
|
|||||||
if node is Label:
|
if node is Label:
|
||||||
node.text = tr(node.text)
|
node.text = tr(node.text)
|
||||||
|
|
||||||
if node is Slider:
|
|
||||||
node.hint_tooltip = tr(node.hint_tooltip)
|
|
||||||
|
|
||||||
for child in node.get_children():
|
for child in node.get_children():
|
||||||
_translate_node(child)
|
_translate_node(child)
|
||||||
|
@ -8,14 +8,14 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gdfxr 1.0\n"
|
"Project-Id-Version: gdfxr 1.0\n"
|
||||||
"Report-Msgid-Bugs-To: timothyqiu32@gmail.com\n"
|
"Report-Msgid-Bugs-To: timothyqiu32@gmail.com\n"
|
||||||
"POT-Creation-Date: 2022-12-04 13:45+0800\n"
|
"POT-Creation-Date: 2022-03-10 17:02+0800\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=utf-8\n"
|
"Content-Type: text/plain; charset=utf-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Generated-By: Babel 2.11.0\n"
|
"Generated-By: Babel 2.9.1\n"
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.gd
|
#: addons/gdfxr/editor/Editor.gd
|
||||||
msgid "Save As..."
|
msgid "Save As..."
|
||||||
@ -29,10 +29,6 @@ msgstr ""
|
|||||||
msgid "Paste"
|
msgid "Paste"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.gd
|
|
||||||
msgid "Paste from jsfxr"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.gd
|
#: addons/gdfxr/editor/Editor.gd
|
||||||
msgid "Recently Generated"
|
msgid "Recently Generated"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -72,7 +68,7 @@ msgid ""
|
|||||||
"Open '%s' anyway?"
|
"Open '%s' anyway?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.gd
|
#: addons/gdfxr/editor/Editor.gd addons/gdfxr/editor/Editor.gd
|
||||||
msgid "SFXR Editor"
|
msgid "SFXR Editor"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
@ -113,10 +109,6 @@ msgstr ""
|
|||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.gd
|
|
||||||
msgid "Clipboard does not contain code copied from jsfxr."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.tscn
|
#: addons/gdfxr/editor/Editor.tscn
|
||||||
msgid "New"
|
msgid "New"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -137,7 +129,7 @@ msgstr ""
|
|||||||
msgid "Play"
|
msgid "Play"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.tscn
|
#: addons/gdfxr/editor/Editor.tscn addons/gdfxr/editor/Editor.tscn
|
||||||
msgid "Restore"
|
msgid "Restore"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: gdfxr 1.0\n"
|
"Project-Id-Version: gdfxr 1.0\n"
|
||||||
"Report-Msgid-Bugs-To: timothyqiu32@gmail.com\n"
|
"Report-Msgid-Bugs-To: timothyqiu32@gmail.com\n"
|
||||||
"POT-Creation-Date: 2022-12-04 13:45+0800\n"
|
"POT-Creation-Date: 2022-03-10 17:02+0800\n"
|
||||||
"PO-Revision-Date: 2022-12-04 13:45+0800\n"
|
"PO-Revision-Date: 2022-03-10 17:02+0800\n"
|
||||||
"Last-Translator: Haoyu Qiu <timothyqiu32@gmail.com>\n"
|
"Last-Translator: Haoyu Qiu <timothyqiu32@gmail.com>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: zh_CN\n"
|
"Language: zh_CN\n"
|
||||||
@ -17,7 +17,7 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
"Generated-By: Babel 2.9.1\n"
|
"Generated-By: Babel 2.9.1\n"
|
||||||
"X-Generator: Poedit 3.2.1\n"
|
"X-Generator: Poedit 3.0.1\n"
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.gd
|
#: addons/gdfxr/editor/Editor.gd
|
||||||
msgid "Save As..."
|
msgid "Save As..."
|
||||||
@ -31,10 +31,6 @@ msgstr "复制"
|
|||||||
msgid "Paste"
|
msgid "Paste"
|
||||||
msgstr "粘贴"
|
msgstr "粘贴"
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.gd
|
|
||||||
msgid "Paste from jsfxr"
|
|
||||||
msgstr "从 jsfxr 粘贴"
|
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.gd
|
#: addons/gdfxr/editor/Editor.gd
|
||||||
msgid "Recently Generated"
|
msgid "Recently Generated"
|
||||||
msgstr "最近生成"
|
msgstr "最近生成"
|
||||||
@ -121,10 +117,6 @@ msgstr ""
|
|||||||
msgid "None"
|
msgid "None"
|
||||||
msgstr "无"
|
msgstr "无"
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.gd
|
|
||||||
msgid "Clipboard does not contain code copied from jsfxr."
|
|
||||||
msgstr "剪贴板中没有从 jsfxr 复制的代码。"
|
|
||||||
|
|
||||||
#: addons/gdfxr/editor/Editor.tscn
|
#: addons/gdfxr/editor/Editor.tscn
|
||||||
msgid "New"
|
msgid "New"
|
||||||
msgstr "新建"
|
msgstr "新建"
|
||||||
@ -260,6 +252,3 @@ msgstr "高通变频"
|
|||||||
#: addons/gdfxr/editor/ParamOption.tscn
|
#: addons/gdfxr/editor/ParamOption.tscn
|
||||||
msgid "Waveform"
|
msgid "Waveform"
|
||||||
msgstr "波形"
|
msgstr "波形"
|
||||||
|
|
||||||
#~ msgid "Hold Ctrl to snap to 0.01 increments."
|
|
||||||
#~ msgstr "按住 Ctrl 吸附到 0.01 增量。"
|
|
||||||
|
@ -36,20 +36,8 @@ func get_preset_name(preset):
|
|||||||
func get_import_options(preset):
|
func get_import_options(preset):
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"name":"loop",
|
name="loop",
|
||||||
"default_value":false,
|
default_value=false,
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"bit_depth",
|
|
||||||
"property_hint":PROPERTY_HINT_ENUM,
|
|
||||||
"hint_string":"8 Bits,16 Bits",
|
|
||||||
"default_value":SFXRGenerator.WavBits.WAV_BITS_8,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name":"sample_rate",
|
|
||||||
"property_hint":PROPERTY_HINT_ENUM,
|
|
||||||
"hint_string":"44100 Hz,22050 Hz",
|
|
||||||
"default_value":SFXRGenerator.WavFreq.WAV_FREQ_44100,
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -65,9 +53,7 @@ func import(source_file, save_path, options, platform_variants, gen_files):
|
|||||||
printerr("Failed to open %s: %d" % [source_file, err])
|
printerr("Failed to open %s: %d" % [source_file, err])
|
||||||
return err
|
return err
|
||||||
|
|
||||||
var stream := SFXRGenerator.new().generate_audio_stream(
|
var stream := SFXRGenerator.new().generate_audio_stream(config)
|
||||||
config, options.bit_depth, options.sample_rate
|
|
||||||
)
|
|
||||||
if options.loop:
|
if options.loop:
|
||||||
stream.loop_mode = AudioStreamSample.LOOP_FORWARD
|
stream.loop_mode = AudioStreamSample.LOOP_FORWARD
|
||||||
stream.loop_end = stream.data.size()
|
stream.loop_end = stream.data.size()
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
name="gdfxr"
|
name="gdfxr"
|
||||||
description="A Godot plugin that ports sfxr, the popular program of choice to make retro sound effects for games."
|
description="A Godot plugin that ports sfxr, the popular program of choice to make retro sound effects for games."
|
||||||
author="Haoyu Qiu"
|
author="Haoyu Qiu"
|
||||||
version="1.3"
|
version="1.1.1"
|
||||||
script="plugin.gd"
|
script="plugin.gd"
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
extends Container
|
|
||||||
|
|
||||||
# These two classes are for runtime generation.
|
|
||||||
const SFXRConfig = preload("res://addons/gdfxr/SFXRConfig.gd")
|
|
||||||
const SFXRGenerator = preload("res://addons/gdfxr/SFXRGenerator.gd")
|
|
||||||
|
|
||||||
onready var audio_player: AudioStreamPlayer = $AudioPlayer
|
|
||||||
onready var adhoc_audio_player: AudioStreamPlayer = $AdhocAudioPlayer
|
|
||||||
|
|
||||||
|
|
||||||
func _on_Play_pressed() -> void:
|
|
||||||
audio_player.play()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_PlayFile_pressed() -> void:
|
|
||||||
adhoc_audio_player.stream = preload("res://example/example.sfxr")
|
|
||||||
adhoc_audio_player.play()
|
|
||||||
|
|
||||||
|
|
||||||
func _on_Generate_pressed() -> void:
|
|
||||||
var config := SFXRConfig.new()
|
|
||||||
|
|
||||||
# Fill the fields manually
|
|
||||||
# config.p_base_freq = 0.5
|
|
||||||
|
|
||||||
# Load from .sfxr file
|
|
||||||
# config.load("res://example/example.sfxr")
|
|
||||||
|
|
||||||
# Load from jsfxr base58 string
|
|
||||||
config.load_from_base58("34T6PkmKkNTf3aUynCpV3oetaq6ecj9Grh9W7tiTbccVYK8FxNKBbfBFXJCLzk8QTy4d7fbiCfY2gXDaiengXbENjdLWt5jZBtcz8QmSCXjHCSuooDCWp4SrT")
|
|
||||||
|
|
||||||
# generate_audio_stream() might freeze a bit when generating long sounds.
|
|
||||||
# It's recommended to pre-generate the sound effects in editor.
|
|
||||||
# If you do want to generate the sound effects on the fly, you might want
|
|
||||||
# to generate and cache the sound effects at the start of your game.
|
|
||||||
var generator := SFXRGenerator.new()
|
|
||||||
adhoc_audio_player.stream = generator.generate_audio_stream(config)
|
|
||||||
adhoc_audio_player.play()
|
|
@ -1,74 +1,23 @@
|
|||||||
[gd_scene load_steps=3 format=2]
|
[gd_scene load_steps=2 format=2]
|
||||||
|
|
||||||
[ext_resource path="res://example/Example.gd" type="Script" id=1]
|
[ext_resource path="res://example/example.sfxr" type="AudioStream" id=1]
|
||||||
[ext_resource path="res://example/example.sfxr" type="AudioStream" id=2]
|
|
||||||
|
|
||||||
[node name="Example" type="GridContainer"]
|
[node name="Example" type="CenterContainer"]
|
||||||
anchor_left = 0.5
|
anchor_right = 1.0
|
||||||
anchor_top = 0.5
|
anchor_bottom = 1.0
|
||||||
anchor_right = 0.5
|
__meta__ = {
|
||||||
anchor_bottom = 0.5
|
"_edit_use_anchors_": false
|
||||||
grow_horizontal = 2
|
}
|
||||||
grow_vertical = 2
|
|
||||||
custom_constants/hseparation = 32
|
|
||||||
custom_constants/vseparation = 32
|
|
||||||
columns = 2
|
|
||||||
script = ExtResource( 1 )
|
|
||||||
|
|
||||||
[node name="AudioPlayer" type="AudioStreamPlayer" parent="."]
|
[node name="Button" type="Button" parent="."]
|
||||||
stream = ExtResource( 2 )
|
margin_left = 448.0
|
||||||
|
margin_top = 284.0
|
||||||
[node name="AdhocAudioPlayer" type="AudioStreamPlayer" parent="."]
|
margin_right = 576.0
|
||||||
|
margin_bottom = 316.0
|
||||||
[node name="Play" type="Button" parent="."]
|
|
||||||
margin_right = 141.0
|
|
||||||
margin_bottom = 32.0
|
|
||||||
rect_min_size = Vector2( 128, 32 )
|
rect_min_size = Vector2( 128, 32 )
|
||||||
size_flags_vertical = 4
|
|
||||||
text = "Play"
|
text = "Play"
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="."]
|
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
|
||||||
margin_left = 173.0
|
stream = ExtResource( 1 )
|
||||||
margin_right = 473.0
|
|
||||||
margin_bottom = 31.0
|
|
||||||
rect_min_size = Vector2( 300, 0 )
|
|
||||||
text = "A .sfxr file can be used as regular audio files like .wav, .ogg, and .mp3."
|
|
||||||
autowrap = true
|
|
||||||
|
|
||||||
[node name="PlayFile" type="Button" parent="."]
|
[connection signal="pressed" from="Button" to="AudioStreamPlayer" method="play"]
|
||||||
margin_top = 64.0
|
|
||||||
margin_right = 141.0
|
|
||||||
margin_bottom = 96.0
|
|
||||||
rect_min_size = Vector2( 128, 32 )
|
|
||||||
size_flags_vertical = 4
|
|
||||||
text = "Load .sfxr File"
|
|
||||||
|
|
||||||
[node name="Label2" type="Label" parent="."]
|
|
||||||
margin_left = 173.0
|
|
||||||
margin_top = 64.0
|
|
||||||
margin_right = 473.0
|
|
||||||
margin_bottom = 95.0
|
|
||||||
rect_min_size = Vector2( 300, 0 )
|
|
||||||
text = "A .sfxr file is a AudioStreamSample resource that can be loaded with load() or preload()."
|
|
||||||
autowrap = true
|
|
||||||
|
|
||||||
[node name="Generate" type="Button" parent="."]
|
|
||||||
margin_top = 144.0
|
|
||||||
margin_right = 141.0
|
|
||||||
margin_bottom = 176.0
|
|
||||||
rect_min_size = Vector2( 128, 32 )
|
|
||||||
size_flags_vertical = 4
|
|
||||||
text = "Runtime Generation"
|
|
||||||
|
|
||||||
[node name="Label3" type="Label" parent="."]
|
|
||||||
margin_left = 173.0
|
|
||||||
margin_top = 128.0
|
|
||||||
margin_right = 473.0
|
|
||||||
margin_bottom = 193.0
|
|
||||||
rect_min_size = Vector2( 300, 0 )
|
|
||||||
text = "You can generate the sound effect at runtime. However, due to performance constraints with GDScript, your game might freeze when generating long sounds."
|
|
||||||
autowrap = true
|
|
||||||
|
|
||||||
[connection signal="pressed" from="Play" to="." method="_on_Play_pressed"]
|
|
||||||
[connection signal="pressed" from="PlayFile" to="." method="_on_PlayFile_pressed"]
|
|
||||||
[connection signal="pressed" from="Generate" to="." method="_on_Generate_pressed"]
|
|
||||||
|
@ -12,5 +12,3 @@ dest_files=[ "res://.import/example.sfxr-e3731bcdcd8403a2391e92667a5d1076.sample
|
|||||||
[params]
|
[params]
|
||||||
|
|
||||||
loop=false
|
loop=false
|
||||||
bit_depth=0
|
|
||||||
sample_rate=0
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 18 KiB |
Binary file not shown.
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 16 KiB |
Loading…
Reference in New Issue
Block a user