Compare commits

..

6 Commits
2.0 ... master

Author SHA1 Message Date
d1e4b34cdc Fix everything for Pandemonium. 2023-08-12 10:02:42 +02:00
Haoyu Qiu
7b3194f500 1.3 Release 2023-02-23 21:20:46 +08:00
Haoyu Qiu
4562f4b77c Add default filename for Save As dialog
Trailing number incremented.

Co-Authored-By: Russell Matney <russell.matney@gmail.com>
2023-02-16 09:53:49 +08:00
Haoyu Qiu
576708bb4a Add import options for bit depth and sample rate 2023-01-04 17:49:34 +08:00
Haoyu Qiu
5b910afb64 Implement paste from jsfxr 2022-12-04 14:01:44 +08:00
Haoyu Qiu
9acce6be58 Add Godot 4 information in README 2022-10-16 11:48:06 +08:00
28 changed files with 648 additions and 539 deletions

View File

@ -6,8 +6,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [2.0.0] - 2023-02-23
## [1.3.0] - 2023-02-23
### Added
- First release for Godot 4. See the `master` branch for Godot 3 support.
- Bit depth and sample rate import options.
- Paste from JSXFR.
- Add default filename for Save As dialog, with trailing number incremented.
[2.0.0]: https://github.com/timothyqiu/gdfxr/releases/tag/2.0
## [1.2.0] - 2022-10-13
### Added
- Implement custom slider.
## [1.1.1] - 2022-07-30
### Fixed
- Slider min value for a bipolar parameter should be -1.
## [1.1.0] - 2022-03-10
### Added
- Extra options menu: Save As, Copy, and Paste.
- Add a copy of the LICENSE in the plugin folder.
- Keep several recently generated sounds in the menu.
## [1.0.0] - 2022-02-25
### Added
- Initial release.
[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.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

View File

@ -10,7 +10,7 @@
你可以在 Godot 中把 sfxr 音效文件当作普通的音频文件使用,也可以像在原始的 sfxr 中一样对音效进行编辑。
> 🚧 如果你想在 Godot 3 里使用这个插件,请移步 [master](https://github.com/timothyqiu/gdfxr/tree/master) 分支。
> 🚧 如果你想在 Godot 4 里使用这个插件,请移步 [godot-4](https://github.com/timothyqiu/gdfxr/tree/godot-4) 分支。
## 安装

View File

@ -12,7 +12,7 @@ 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
original sfxr.
> 🚧 Checkout the [master](https://github.com/timothyqiu/gdfxr/tree/master) branch if you want to use this plugin in Godot 3.
> 🚧 Checkout the [godot-4](https://github.com/timothyqiu/gdfxr/tree/godot-4) branch if you want to use this plugin in Godot 4.
## Installation

View File

@ -11,7 +11,7 @@ static func b58decode(v: String) -> StreamPeerBuffer:
v = v.lstrip(BASE_58_ALPHABET[0])
var zeros := original_length - v.length()
var buffer := PackedByteArray()
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)
@ -32,7 +32,7 @@ static func b58decode(v: String) -> StreamPeerBuffer:
var result := StreamPeerBuffer.new()
for _i in zeros:
result.put_8(0)
result.put_data(buffer.slice(buffer.size() - length))
result.put_data(buffer.subarray(buffer.size() - length, -1))
result.seek(0)
return result

View File

@ -55,9 +55,10 @@ var sound_vol := 0.5
func load(path: String) -> int: # Error
var f := FileAccess.open(path, FileAccess.READ)
if not f:
return FileAccess.get_open_error()
var f := File.new()
var err := f.open(path, File.READ)
if err != OK:
return err
var version := f.get_32()
if not [100, 101, 102].has(version):
@ -105,9 +106,10 @@ func load(path: String) -> int: # Error
func save(path: String) -> int: # Error
var f := FileAccess.open(path, FileAccess.WRITE)
if not f:
return FileAccess.get_open_error()
var f := File.new()
var err := f.open(path, File.WRITE)
if err != OK:
return err
f.store_32(102)
f.store_32(wave_type)
@ -152,88 +154,88 @@ func randomize_in_category(category: int) -> void:
match category:
Category.PICKUP_COIN:
p_base_freq = randf_range(0.4, 0.9)
p_base_freq = rand_range(0.4, 0.9)
p_env_attack = 0.0
p_env_sustain = randf_range(0.0, 0.1)
p_env_decay = randf_range(0.1, 0.5)
p_env_punch = randf_range(0.3, 0.6)
p_env_sustain = rand_range(0.0, 0.1)
p_env_decay = rand_range(0.1, 0.5)
p_env_punch = rand_range(0.3, 0.6)
if randi() % 2:
p_arp_speed = randf_range(0.5, 0.7)
p_arp_mod = randf_range(0.2, 0.6)
p_arp_speed = rand_range(0.5, 0.7)
p_arp_mod = rand_range(0.2, 0.6)
Category.LASER_SHOOT:
wave_type = randi() % 3
if wave_type == 2 and randi() % 2:
wave_type = randi() % 2
p_base_freq = randf_range(0.5, 1.0)
p_freq_limit = max(0.2, p_base_freq - randf_range(0.2, 0.8))
p_freq_ramp = randf_range(-0.35, -0.15)
p_base_freq = rand_range(0.5, 1.0)
p_freq_limit = max(0.2, p_base_freq - rand_range(0.2, 0.8))
p_freq_ramp = rand_range(-0.35, -0.15)
if randi() % 3 == 0:
p_base_freq = randf_range(0.3, 0.9)
p_freq_limit = randf_range(0, 0.1)
p_freq_ramp = randf_range(-0.65, -0.35)
p_base_freq = rand_range(0.3, 0.9)
p_freq_limit = rand_range(0, 0.1)
p_freq_ramp = rand_range(-0.65, -0.35)
if randi() % 2:
p_duty = randf_range(0, 0.5)
p_duty_ramp = randf_range(0, 0.2)
p_duty = rand_range(0, 0.5)
p_duty_ramp = rand_range(0, 0.2)
else:
p_duty = randf_range(0.4, 0.9)
p_duty_ramp = randf_range(-0.7, 0)
p_duty = rand_range(0.4, 0.9)
p_duty_ramp = rand_range(-0.7, 0)
p_env_attack = 0.0
p_env_sustain = randf_range(0.1, 0.3)
p_env_decay = randf_range(0.0, 0.4)
p_env_sustain = rand_range(0.1, 0.3)
p_env_decay = rand_range(0.0, 0.4)
if randi() % 2:
p_env_punch = randf_range(0, 0.3)
p_env_punch = rand_range(0, 0.3)
if randi() % 3 == 0:
p_pha_offset = randf_range(0, 0.2)
p_pha_ramp = randf_range(0, 0.2)
p_pha_offset = rand_range(0, 0.2)
p_pha_ramp = rand_range(0, 0.2)
if randi() % 2:
p_hpf_freq = randf_range(0, 0.3)
p_hpf_freq = rand_range(0, 0.3)
Category.EXPLOSION:
wave_type = WaveType.NOISE
if randi() % 2:
p_base_freq = randf_range(0.1, 0.4)
p_freq_ramp = randf_range(-0.1, 0.3)
p_base_freq = rand_range(0.1, 0.4)
p_freq_ramp = rand_range(-0.1, 0.3)
else:
p_base_freq = randf_range(0.2, 0.9)
p_freq_ramp = randf_range(-0.4, -0.2)
p_base_freq = rand_range(0.2, 0.9)
p_freq_ramp = rand_range(-0.4, -0.2)
p_base_freq *= p_base_freq
if randi() % 5 == 0:
p_freq_ramp = 0.0
if randi() % 3 == 0:
p_repeat_speed = randf_range(0.3, 0.8)
p_repeat_speed = rand_range(0.3, 0.8)
p_env_attack = 0.0
p_env_sustain = randf_range(0.1, 0.4)
p_env_decay = randf_range(0, 0.5)
p_env_sustain = rand_range(0.1, 0.4)
p_env_decay = rand_range(0, 0.5)
if randi() % 2:
p_pha_offset = randf_range(-0.3, 0.6)
p_pha_ramp = randf_range(-0.3, 0)
p_env_punch = randf_range(0.2, 0.8)
p_pha_offset = rand_range(-0.3, 0.6)
p_pha_ramp = rand_range(-0.3, 0)
p_env_punch = rand_range(0.2, 0.8)
if randi() % 2:
p_vib_strength = randf_range(0, 0.7)
p_vib_speed = randf_range(0, 0.6)
p_vib_strength = rand_range(0, 0.7)
p_vib_speed = rand_range(0, 0.6)
if randi() % 3:
p_arp_speed = randf_range(0.6, 0.9)
p_arp_mod = randf_range(-0.8, 0.8)
p_arp_speed = rand_range(0.6, 0.9)
p_arp_mod = rand_range(-0.8, 0.8)
Category.POWERUP:
if randi() % 2:
wave_type = WaveType.SAWTOOTH
else:
p_duty = randf_range(0, 0.6)
p_duty = rand_range(0, 0.6)
if randi() % 2:
p_base_freq = randf_range(0.2, 0.5)
p_freq_ramp = randf_range(0.1, 0.5)
p_repeat_speed = randf_range(0.4, 0.8)
p_base_freq = rand_range(0.2, 0.5)
p_freq_ramp = rand_range(0.1, 0.5)
p_repeat_speed = rand_range(0.4, 0.8)
else:
p_base_freq = randf_range(0.2, 0.5)
p_freq_ramp = randf_range(0.05, 0.25)
p_base_freq = rand_range(0.2, 0.5)
p_freq_ramp = rand_range(0.05, 0.25)
if randi() % 2:
p_vib_strength = randf_range(0, 0.7)
p_vib_speed = randf_range(0, 0.6)
p_vib_strength = rand_range(0, 0.7)
p_vib_speed = rand_range(0, 0.6)
p_env_attack = 0.0
p_env_sustain = randf_range(0, 0.4)
p_env_decay = randf_range(0.1, 0.5)
p_env_sustain = rand_range(0, 0.4)
p_env_decay = rand_range(0.1, 0.5)
Category.HIT_HURT:
wave_type = randi() % 3
@ -241,123 +243,123 @@ func randomize_in_category(category: int) -> void:
WaveType.SINE_WAVE:
wave_type = WaveType.NOISE
WaveType.SQUARE_WAVE:
p_duty = randf_range(0, 0.6)
p_base_freq = randf_range(0.2, 0.8)
p_freq_ramp = randf_range(-0.7, -0.3)
p_duty = rand_range(0, 0.6)
p_base_freq = rand_range(0.2, 0.8)
p_freq_ramp = rand_range(-0.7, -0.3)
p_env_attack = 0.0
p_env_sustain = randf_range(0, 0.1)
p_env_decay = randf_range(0.1, 0.3)
p_env_sustain = rand_range(0, 0.1)
p_env_decay = rand_range(0.1, 0.3)
if randi() % 2:
p_hpf_freq = randf_range(0, 0.3)
p_hpf_freq = rand_range(0, 0.3)
Category.JUMP:
wave_type = WaveType.SQUARE_WAVE
p_duty = randf_range(0, 0.6)
p_base_freq = randf_range(0.3, 0.6)
p_freq_ramp = randf_range(0.1, 0.3)
p_duty = rand_range(0, 0.6)
p_base_freq = rand_range(0.3, 0.6)
p_freq_ramp = rand_range(0.1, 0.3)
p_env_attack = 0.0
p_env_sustain = randf_range(0.1, 0.4)
p_env_decay = randf_range(0.1, 0.3)
p_env_sustain = rand_range(0.1, 0.4)
p_env_decay = rand_range(0.1, 0.3)
if randi() % 2:
p_hpf_freq = randf_range(0, 0.3)
p_hpf_freq = rand_range(0, 0.3)
if randi() % 2:
p_lpf_freq = randf_range(0.4, 1.0)
p_lpf_freq = rand_range(0.4, 1.0)
Category.BLIP_SELECT:
wave_type = randi() % 2
if wave_type == WaveType.SQUARE_WAVE:
p_duty = randf_range(0, 0.6)
p_base_freq = randf_range(0.2, 0.6)
p_duty = rand_range(0, 0.6)
p_base_freq = rand_range(0.2, 0.6)
p_env_attack = 0.0
p_env_sustain = randf_range(0.1, 0.2)
p_env_decay = randf_range(0, 0.2)
p_env_sustain = rand_range(0.1, 0.2)
p_env_decay = rand_range(0, 0.2)
p_hpf_freq = 0.1
func randomize() -> void:
p_base_freq = pow(randf_range(-1.0, +1.0), 2.0)
p_base_freq = pow(rand_range(-1.0, +1.0), 2.0)
if randi() % 2:
p_base_freq = pow(randf_range(-1.0, +1.0), 3.0) + 0.5
p_base_freq = pow(rand_range(-1.0, +1.0), 3.0) + 0.5
p_freq_limit = 0.0
p_freq_ramp = pow(randf_range(-1.0, +1.0), 5.0)
p_freq_ramp = pow(rand_range(-1.0, +1.0), 5.0)
if p_base_freq > 0.7 and p_freq_ramp > 0.2:
p_freq_ramp = -p_freq_ramp
if p_base_freq < 0.2 and p_freq_ramp < -0.05:
p_freq_ramp = -p_freq_ramp
p_freq_dramp = pow(randf_range(-1.0, +1.0), 3.0)
p_duty = randf_range(-1.0, +1.0)
p_duty_ramp = pow(randf_range(-1.0, +1.0), 3.0)
p_vib_strength = pow(randf_range(-1.0, +1.0), 3.0)
p_vib_speed = randf_range(-1.0, +1.0)
# p_vib_delay = randf_range(-1.0, +1.0)
p_env_attack = pow(randf_range(-1.0, +1.0), 3.0)
p_env_sustain = pow(randf_range(-1.0, +1.0), 2.0)
p_env_decay = randf_range(-1.0, +1.0)
p_env_punch = pow(randf_range(0, 0.8), 2.0)
p_freq_dramp = pow(rand_range(-1.0, +1.0), 3.0)
p_duty = rand_range(-1.0, +1.0)
p_duty_ramp = pow(rand_range(-1.0, +1.0), 3.0)
p_vib_strength = pow(rand_range(-1.0, +1.0), 3.0)
p_vib_speed = rand_range(-1.0, +1.0)
# p_vib_delay = rand_range(-1.0, +1.0)
p_env_attack = pow(rand_range(-1.0, +1.0), 3.0)
p_env_sustain = pow(rand_range(-1.0, +1.0), 2.0)
p_env_decay = rand_range(-1.0, +1.0)
p_env_punch = pow(rand_range(0, 0.8), 2.0)
if p_env_attack + p_env_sustain + p_env_decay < 0.2:
p_env_sustain += randf_range(0.2, 0.5)
p_env_decay += randf_range(0.2, 0.5)
p_lpf_resonance = randf_range(-1.0, +1.0)
p_env_sustain += rand_range(0.2, 0.5)
p_env_decay += rand_range(0.2, 0.5)
p_lpf_resonance = rand_range(-1.0, +1.0)
p_lpf_freq = 1.0 - pow(randf(), 3.0)
p_lpf_ramp = pow(randf_range(-1.0, +1.0), 3.0)
p_lpf_ramp = pow(rand_range(-1.0, +1.0), 3.0)
if p_lpf_freq < 0.1 and p_lpf_ramp < -0.05:
p_lpf_ramp = -p_lpf_ramp
p_hpf_freq = pow(randf(), 5.0)
p_hpf_ramp = pow(randf_range(-1.0, +1.0), 5.0)
p_pha_offset = pow(randf_range(-1.0, +1.0), 3.0)
p_pha_ramp = pow(randf_range(-1.0, +1.0), 3.0)
p_repeat_speed = randf_range(-1.0, +1.0)
p_arp_speed = randf_range(-1.0, +1.0)
p_arp_mod = randf_range(-1.0, +1.0)
p_hpf_ramp = pow(rand_range(-1.0, +1.0), 5.0)
p_pha_offset = pow(rand_range(-1.0, +1.0), 3.0)
p_pha_ramp = pow(rand_range(-1.0, +1.0), 3.0)
p_repeat_speed = rand_range(-1.0, +1.0)
p_arp_speed = rand_range(-1.0, +1.0)
p_arp_mod = rand_range(-1.0, +1.0)
func mutate() -> void:
if randi() % 2:
p_base_freq += randf_range(-0.05, +0.05)
p_base_freq += rand_range(-0.05, +0.05)
if randi() % 2:
p_freq_limit += randf_range(-0.05, +0.05)
p_freq_limit += rand_range(-0.05, +0.05)
if randi() % 2:
p_freq_ramp += randf_range(-0.05, +0.05)
p_freq_ramp += rand_range(-0.05, +0.05)
if randi() % 2:
p_freq_dramp += randf_range(-0.05, +0.05)
p_freq_dramp += rand_range(-0.05, +0.05)
if randi() % 2:
p_duty += randf_range(-0.05, +0.05)
p_duty += rand_range(-0.05, +0.05)
if randi() % 2:
p_duty_ramp += randf_range(-0.05, +0.05)
p_duty_ramp += rand_range(-0.05, +0.05)
if randi() % 2:
p_vib_strength += randf_range(-0.05, +0.05)
p_vib_strength += rand_range(-0.05, +0.05)
if randi() % 2:
p_vib_speed += randf_range(-0.05, +0.05)
p_vib_speed += rand_range(-0.05, +0.05)
# if randi() % 2:
# p_vib_delay += randf_range(-0.05, +0.05)
# p_vib_delay += rand_range(-0.05, +0.05)
if randi() % 2:
p_env_attack += randf_range(-0.05, +0.05)
p_env_attack += rand_range(-0.05, +0.05)
if randi() % 2:
p_env_sustain += randf_range(-0.05, +0.05)
p_env_sustain += rand_range(-0.05, +0.05)
if randi() % 2:
p_env_decay += randf_range(-0.05, +0.05)
p_env_decay += rand_range(-0.05, +0.05)
if randi() % 2:
p_env_punch += randf_range(-0.05, +0.05)
p_env_punch += rand_range(-0.05, +0.05)
if randi() % 2:
p_lpf_resonance += randf_range(-0.05, +0.05)
p_lpf_resonance += rand_range(-0.05, +0.05)
if randi() % 2:
p_lpf_freq += randf_range(-0.05, +0.05)
p_lpf_freq += rand_range(-0.05, +0.05)
if randi() % 2:
p_lpf_ramp += randf_range(-0.05, +0.05)
p_lpf_ramp += rand_range(-0.05, +0.05)
if randi() % 2:
p_hpf_freq += randf_range(-0.05, +0.05)
p_hpf_freq += rand_range(-0.05, +0.05)
if randi() % 2:
p_hpf_ramp += randf_range(-0.05, +0.05)
p_hpf_ramp += rand_range(-0.05, +0.05)
if randi() % 2:
p_pha_offset += randf_range(-0.05, +0.05)
p_pha_offset += rand_range(-0.05, +0.05)
if randi() % 2:
p_pha_ramp += randf_range(-0.05, +0.05)
p_pha_ramp += rand_range(-0.05, +0.05)
if randi() % 2:
p_repeat_speed += randf_range(-0.05, +0.05)
p_repeat_speed += rand_range(-0.05, +0.05)
if randi() % 2:
p_arp_speed += randf_range(-0.05, +0.05)
p_arp_speed += rand_range(-0.05, +0.05)
if randi() % 2:
p_arp_mod += randf_range(-0.05, +0.05)
p_arp_mod += rand_range(-0.05, +0.05)
func reset():
@ -395,7 +397,7 @@ func reset():
p_arp_mod = 0.0
func copy_from(other: RefCounted) -> void: # SFXRConfig
func copy_from(other: Reference) -> void: # SFXRConfig
wave_type = other.wave_type
p_env_attack = other.p_env_attack
@ -430,7 +432,7 @@ func copy_from(other: RefCounted) -> void: # SFXRConfig
sound_vol = other.sound_vol
func is_equal(other: RefCounted) -> bool: # SFXRConfig
func is_equal(other: Reference) -> bool: # SFXRConfig
return (
wave_type == other.wave_type

View File

@ -32,15 +32,15 @@ var vib_speed: float
var square_duty: float
var square_slide: float
var env_vol: float
var env_length := PackedInt32Array([0, 0, 0])
var env_length := PoolIntArray([0, 0, 0])
var phase: int
var fphase: float
var fdphase: float
var iphase: int
var flthp: float
var flthp_d: float
var noise_buffer := PackedFloat32Array([])
var phaser_buffer := PackedFloat32Array([])
var noise_buffer := PoolRealArray([])
var phaser_buffer := PoolRealArray([])
var ipp: int
var fltp: float
var fltdp: float
@ -54,9 +54,9 @@ func generate_audio_stream(
config: SFXRConfig,
wav_bits: int = WavBits.WAV_BITS_8,
wav_freq: int = WavFreq.WAV_FREQ_44100
) -> AudioStreamWAV:
var stream := AudioStreamWAV.new()
stream.format = AudioStreamWAV.FORMAT_8_BITS if wav_bits == WavBits.WAV_BITS_8 else AudioStreamWAV.FORMAT_16_BITS
) -> AudioStreamSample:
var stream := AudioStreamSample.new()
stream.format = AudioStreamSample.FORMAT_8_BITS if wav_bits == WavBits.WAV_BITS_8 else AudioStreamSample.FORMAT_16_BITS
stream.mix_rate = 44100 if wav_freq == WavFreq.WAV_FREQ_44100 else 22050
_config = config
@ -70,7 +70,7 @@ func generate_samples(
config: SFXRConfig,
wav_bits: int = WavBits.WAV_BITS_8,
wav_freq: int = WavFreq.WAV_FREQ_44100
) -> PackedByteArray:
) -> PoolByteArray:
_config = config
var data := _generate_samples(wav_bits, wav_freq).data_array
_config = null
@ -148,7 +148,7 @@ func _generate_samples(wav_bits: int, wav_freq: int) -> StreamPeerBuffer:
phase %= period
if _config.wave_type == SFXRConfig.WaveType.NOISE:
for j in 32:
noise_buffer[j] = randf_range(-1.0, +1.0)
noise_buffer[j] = rand_range(-1.0, +1.0)
# base waveform
var fp := float(phase) / period
@ -266,7 +266,7 @@ func _reset_sample(restart: bool) -> void:
noise_buffer.resize(32)
for i in noise_buffer.size():
noise_buffer[i] = randf_range(-1.0, +1.0)
noise_buffer[i] = rand_range(-1.0, +1.0)
rep_time = 0
rep_limit = int(pow(1.0 - _config.p_repeat_speed, 2.0) * 20000 + 32)

View File

@ -1,12 +1,11 @@
@tool
tool
extends Control
signal value_changed(value)
@export var value: float = 0.0 :
set = set_value
@export var min_value: float = 0.0
@export var max_value: float = 1.0
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
@ -18,7 +17,6 @@ var _stylebox_value: StyleBox
var _line_edit_just_closed := false
var _mouse_hovering := false
var _is_editing := false
var _is_cancel := false
var _drag_start_position: Vector2
var _drag_cancelled := true
@ -26,9 +24,9 @@ var _drag_dist := 0.0
var _drag_start_factor: float
func _init():
func _init() -> void:
mouse_default_cursor_shape = Control.CURSOR_HSIZE
clip_contents = true
rect_clip_content = true
focus_mode = Control.FOCUS_ALL
var style := StyleBoxEmpty.new()
@ -36,51 +34,49 @@ func _init():
style.content_margin_right = 8
_line_edit = LineEdit.new()
_line_edit.set_as_top_level(true)
_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.focus_exited.connect(self._on_line_edit_focus_exited)
_ret = _line_edit.text_submitted.connect(self._on_line_edit_text_entered)
_ret = _line_edit.visibility_changed.connect(self._on_line_edit_visibility_changed)
_ret = _line_edit.gui_input.connect(self._on_line_edit_gui_input)
_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 font_size := get_theme_font_size("font_size", "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(
(size.x - number_size.x) / 2,
(size.y - number_size.y) / 2 + font.get_ascent()
(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, size))
draw_style_box(stylebox, Rect2(Vector2.ZERO, rect_size))
else:
var value_width := size.x * ((value - min_value) / (max_value - min_value))
draw_style_box(stylebox, Rect2(value_width, 0, size.x - value_width, size.y))
draw_style_box(_stylebox_value, Rect2(0, 0, value_width, size.y))
draw_string(font, pos, number_string, HORIZONTAL_ALIGNMENT_CENTER, -1, font_size, color)
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() if _stylebox_normal else Vector2.ZERO
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 == MOUSE_BUTTON_LEFT:
if mb and mb.button_index == BUTTON_LEFT:
if mb.pressed:
_drag_prepare(mb)
else:
@ -89,10 +85,10 @@ func _gui_input(event: InputEvent) -> void:
_show_text_edit()
_drag_cancelled = true
_is_editing = mb.pressed
queue_redraw()
update()
var mm := event as InputEventMouseMotion
if mm and mm.button_mask & MOUSE_BUTTON_MASK_LEFT:
if mm and mm.button_mask & BUTTON_MASK_LEFT:
_drag_motion(mm)
_drag_cancelled = false
@ -104,11 +100,11 @@ func _notification(what: int) -> void:
NOTIFICATION_MOUSE_ENTER:
_mouse_hovering = true
queue_redraw()
update()
NOTIFICATION_MOUSE_EXIT:
_mouse_hovering = false
queue_redraw()
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:
@ -121,7 +117,7 @@ func set_value(v: float) -> void:
return
value = v
emit_signal("value_changed", value)
queue_redraw()
update()
func _update_stylebox() -> void:
@ -145,9 +141,9 @@ func _drag_done() -> void:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
if _drag_cancelled:
Input.warp_mouse(_drag_start_position)
Input.warp_mouse_position(_drag_start_position)
else:
Input.warp_mouse(global_position + size * Vector2(
Input.warp_mouse_position(rect_global_position + rect_size * Vector2(
(value - min_value) / (max_value - min_value),
0.5
))
@ -156,33 +152,32 @@ func _drag_done() -> void:
func _drag_motion(motion: InputEventMouseMotion) -> void:
_drag_dist += motion.relative.x
var factor := _drag_start_factor + _drag_dist / size.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) * size.x
_drag_dist = (factor - _drag_start_factor) * rect_size.x
var v := factor * (max_value - min_value) + min_value
var snap := motion.is_command_or_control_pressed() or motion.shift_pressed
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_pressed and motion.is_command_or_control_pressed():
if motion.shift and motion.command:
v = round(v * 1000.0) * 0.001
elif motion.shift_pressed:
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))
queue_redraw()
update()
func _show_text_edit() -> void:
var gr := get_global_rect()
_is_cancel = false
_line_edit.text = str(value)
_line_edit.set_position(gr.position)
_line_edit.set_size(gr.size)
_line_edit.show() # FIXME: no suitable solution for `show_modal` yet
_line_edit.show_modal()
_line_edit.select_all()
_line_edit.grab_focus()
_line_edit.focus_next = find_next_valid_focus().get_path()
@ -192,11 +187,11 @@ func _show_text_edit() -> void:
func _on_line_edit_focus_exited():
if _line_edit.get_menu().visible:
return
if not _is_cancel and _line_edit.text.is_valid_float():
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()
queue_redraw()
update()
func _on_line_edit_text_entered(_text: String):
@ -206,9 +201,3 @@ func _on_line_edit_text_entered(_text: String):
func _on_line_edit_visibility_changed():
if not _line_edit.visible:
_line_edit_just_closed = true
func _on_line_edit_gui_input(event: InputEvent):
if event.is_action_pressed("ui_cancel"):
_is_cancel = true
_line_edit.hide()

View File

@ -1,4 +1,4 @@
@tool
tool
extends Container
enum ExtraOption { SAVE_AS, COPY, PASTE, PASTE_JSFXR, RECENT }
@ -13,11 +13,7 @@ class RecentEntry:
var title: String
var config := SFXRConfig.new()
var plugin: EditorPlugin:
set(v):
plugin = v
for child in get_children():
_hook_plugin(child)
var plugin: EditorPlugin
var _config := SFXRConfig.new()
var _config_defaults := SFXRConfig.new()
@ -31,44 +27,47 @@ var _param_map := {}
var _syncing_ui := false # a hack since Range set_value emits value_changed
var _category_names := {}
@onready var audio_player := $AudioStreamPlayer as AudioStreamPlayer
@onready var filename_label := find_child("Filename") as Label
@onready var save_button := find_child("Save") as Button
@onready var restore_button := find_child("Restore") as Button
@onready var extra_button := find_child("Extra") as MenuButton
@onready var version_button := find_child("VersionButton")
@onready var translator := $PluginTranslator
onready var audio_player := $AudioStreamPlayer as AudioStreamPlayer
onready var filename_label := find_node("Filename") as Label
onready var save_button := find_node("Save") as Button
onready var restore_button := find_node("Restore") as Button
onready var extra_button := find_node("Extra") as MenuButton
onready var version_button := find_node("VersionButton")
onready var translator := $PluginTranslator
func _ready():
if not plugin:
return # Running in the edited scene instead of from Plugin
for child in get_children():
_hook_plugin(child)
var popup := extra_button.get_popup()
popup.add_item(translator.t("Save As..."), ExtraOption.SAVE_AS)
popup.add_item(translator.tr("Save As..."), ExtraOption.SAVE_AS)
popup.add_separator()
popup.add_icon_item(get_theme_icon("ActionCopy", "EditorIcons"), translator.t("Copy"), ExtraOption.COPY)
popup.add_icon_item(get_theme_icon("ActionPaste", "EditorIcons"), translator.t("Paste"), ExtraOption.PASTE)
popup.add_item(translator.t("Paste from jsfxr"), ExtraOption.PASTE_JSFXR)
popup.add_separator(translator.t("Recently Generated"))
popup.id_pressed.connect(_on_Extra_id_pressed)
popup.add_item(translator.tr("Copy"), ExtraOption.COPY)
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.connect("id_pressed", self, "_on_Extra_id_pressed")
_category_names = {
SFXRConfig.Category.PICKUP_COIN: translator.t("Pickup/Coin"),
SFXRConfig.Category.LASER_SHOOT: translator.t("Laser/Shoot"),
SFXRConfig.Category.EXPLOSION: translator.t("Explosion"),
SFXRConfig.Category.POWERUP: translator.t("Powerup"),
SFXRConfig.Category.HIT_HURT: translator.t("Hit/Hurt"),
SFXRConfig.Category.JUMP: translator.t("Jump"),
SFXRConfig.Category.BLIP_SELECT: translator.t("Blip/Select"),
SFXRConfig.Category.PICKUP_COIN: translator.tr("Pickup/Coin"),
SFXRConfig.Category.LASER_SHOOT: translator.tr("Laser/Shoot"),
SFXRConfig.Category.EXPLOSION: translator.tr("Explosion"),
SFXRConfig.Category.POWERUP: translator.tr("Powerup"),
SFXRConfig.Category.HIT_HURT: translator.tr("Hit/Hurt"),
SFXRConfig.Category.JUMP: translator.tr("Jump"),
SFXRConfig.Category.BLIP_SELECT: translator.tr("Blip/Select"),
}
var params := find_child("Params") as Container
var params := find_node("Params") as Container
for category in params.get_children():
for control in category.get_children():
_param_map[control.parameter] = control
control.connect("param_changed", _on_param_changed)
control.connect("param_reset", _on_param_reset)
control.connect("param_changed", self, "_on_param_changed")
control.connect("param_reset", self, "_on_param_reset")
_set_editing_file("")
@ -79,7 +78,7 @@ func _notification(what: int):
match what:
NOTIFICATION_ENTER_TREE, NOTIFICATION_THEME_CHANGED:
find_child("ScrollContainer").add_theme_stylebox_override("panel", get_theme_stylebox("panel", "Tree"))
find_node("ScrollContainer").add_theme_stylebox_override("bg", get_theme_stylebox("bg", "Tree"))
if extra_button:
var popup = extra_button.get_popup()
@ -87,18 +86,11 @@ func _notification(what: int):
popup.set_item_icon(popup.get_item_index(ExtraOption.PASTE), get_theme_icon("ActionPaste", "EditorIcons"))
func edit(object: Variant) -> void:
if not object:
if not _modified:
_set_editing_file("")
# TODO: prompt?
return
var path := object.resource_path as String
func edit(path: String) -> void:
if _modified:
_popup_confirm(
translator.t("There are unsaved changes.\nOpen '%s' anyway?") % path,
_set_editing_file.bind(path)
str(translator.tr("There are unsaved changes.\nOpen '%s' anyway?")) % path,
"_set_editing_file", [path]
)
else:
_set_editing_file(path)
@ -125,30 +117,30 @@ func _push_recent(title: String) -> void:
_config_recents.push_front(recent)
func _popup_confirm(content: String, callback: Callable) -> void:
func _popup_confirm(content: String, callback: String, binds := []) -> void:
var dialog := ConfirmationDialog.new()
add_child(dialog)
dialog.dialog_text = content
dialog.title = translator.t("SFXR Editor")
dialog.confirmed.connect(callback)
dialog.window_title = translator.tr("SFXR Editor")
dialog.connect("confirmed", self, callback, binds)
dialog.connect("popup_hide", dialog, "queue_free")
dialog.popup_centered()
dialog.visibility_changed.connect(dialog.queue_free)
func _popup_message(content: String) -> void:
var dialog := AcceptDialog.new()
add_child(dialog)
dialog.dialog_text = content
dialog.title = translator.t("SFXR Editor")
dialog.window_title = translator.tr("SFXR Editor")
dialog.connect("popup_hide", dialog, "queue_free")
dialog.popup_centered()
dialog.visibility_changed.connect(dialog.queue_free)
func _popup_file_dialog(mode: int, callback: Callable, default_filename := DefaultFilename.EMPTY) -> void:
func _popup_file_dialog(mode: int, callback: String, default_filename: int = DefaultFilename.EMPTY) -> void:
var dialog := EditorFileDialog.new()
add_child(dialog)
dialog.access = EditorFileDialog.ACCESS_RESOURCES
dialog.file_mode = mode
dialog.mode = mode
match default_filename:
DefaultFilename.EMPTY:
@ -158,10 +150,10 @@ func _popup_file_dialog(mode: int, callback: Callable, default_filename := Defau
if _path:
dialog.current_path = _generate_serial_path(_path)
dialog.add_filter("*.sfxr; %s" % translator.t("SFXR Audio"))
dialog.file_selected.connect(callback)
dialog.add_filter("*.sfxr; %s" % translator.tr("SFXR Audio"))
dialog.connect("popup_hide", dialog, "queue_free")
dialog.connect("file_selected", self, callback)
dialog.popup_centered_ratio()
dialog.visibility_changed.connect(dialog.queue_free)
func _reset_defaults() -> void:
@ -178,13 +170,13 @@ func _restore_from_config(config: SFXRConfig) -> void:
func _set_editing_file(path: String) -> int: # Error
if path.is_empty():
if path.empty():
_config.reset()
audio_player.stream = null
else:
var err := _config.load(path)
if err != OK:
_popup_message(translator.t("'%s' is not a valid SFXR file.") % path)
_popup_message(str(translator.tr("'%s' is not a valid SFXR file.")) % path)
return err
audio_player.stream = load(path)
@ -196,8 +188,8 @@ func _set_editing_file(path: String) -> int: # Error
func _set_modified(value: bool) -> void:
_modified = value
var has_file := not _path.is_empty()
var base = _path if has_file else translator.t("Unsaved sound")
var has_file := not _path.empty()
var base = _path if has_file else str(translator.tr("Unsaved sound"))
if _modified:
base += "(*)"
filename_label.text = base
@ -216,8 +208,8 @@ func _sync_ui() -> void:
func _generate_serial_path(path: String) -> String:
var directory := DirAccess.open(path.get_base_dir())
if not directory:
var directory := Directory.new()
if directory.open(path.get_base_dir()) != OK:
return path
if not directory.file_exists(path.get_file()):
@ -281,7 +273,7 @@ func _on_Play_pressed(force_regenerate := false):
func _on_Randomize_pressed(category: int):
if category == -1:
_config.randomize()
_push_recent(translator.t("Randomize"))
_push_recent(translator.tr("Randomize"))
else:
_config.randomize_in_category(category)
_push_recent(_category_names.get(category, "Unknown"))
@ -294,7 +286,7 @@ func _on_Randomize_pressed(category: int):
func _on_Mutate_pressed():
_config.mutate()
_push_recent(translator.t("Mutate"))
_push_recent(translator.tr("Mutate"))
_set_modified(true)
_sync_ui()
_on_Play_pressed(true)
@ -307,8 +299,8 @@ func _on_Restore_pressed():
func _on_New_pressed():
if _modified:
_popup_confirm(
translator.t("There are unsaved changes.\nCreate a new one anyway?"),
_on_New_confirmed
translator.tr("There are unsaved changes.\nCreate a new one anyway?"),
"_on_New_confirmed"
)
else:
_on_New_confirmed()
@ -319,8 +311,8 @@ func _on_New_confirmed() -> void:
func _on_Save_pressed():
if _path.is_empty():
_popup_file_dialog(EditorFileDialog.FILE_MODE_SAVE_FILE, _on_SaveAsDialog_confirmed)
if _path.empty():
_popup_file_dialog(EditorFileDialog.MODE_SAVE_FILE, "_on_SaveAsDialog_confirmed")
else:
_config.save(_path)
plugin.get_editor_interface().get_resource_filesystem().scan_sources()
@ -337,17 +329,17 @@ func _on_SaveAsDialog_confirmed(path: String):
func _on_Load_pressed():
if _modified:
_popup_confirm(
translator.t("There are unsaved changes.\nLoad anyway?"),
_popup_file_dialog.bind(EditorFileDialog.FILE_MODE_OPEN_FILE, "_set_editing_file")
translator.tr("There are unsaved changes.\nLoad anyway?"),
"_popup_file_dialog", [EditorFileDialog.MODE_OPEN_FILE, "_set_editing_file"]
)
else:
_popup_file_dialog(EditorFileDialog.FILE_MODE_OPEN_FILE, _set_editing_file)
_popup_file_dialog(EditorFileDialog.MODE_OPEN_FILE, "_set_editing_file")
func _on_Extra_about_to_show():
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_JSFXR), not DisplayServer.clipboard_has())
popup.set_item_disabled(popup.get_item_index(ExtraOption.PASTE_JSFXR), not OS.has_clipboard())
# Rebuild recents menu everytime :)
var first_recent_index := popup.get_item_index(ExtraOption.RECENT)
@ -356,8 +348,8 @@ func _on_Extra_about_to_show():
for i in count - first_recent_index:
popup.remove_item(count - 1 - i)
if _config_recents.is_empty():
popup.add_item(translator.t("None"), ExtraOption.RECENT)
if _config_recents.empty():
popup.add_item(translator.tr("None"), ExtraOption.RECENT)
popup.set_item_disabled(popup.get_item_index(ExtraOption.RECENT), true)
else:
for i in _config_recents.size():
@ -367,7 +359,7 @@ func _on_Extra_about_to_show():
func _on_Extra_id_pressed(id: int) -> void:
match id:
ExtraOption.SAVE_AS:
_popup_file_dialog(EditorFileDialog.FILE_MODE_SAVE_FILE, _on_SaveAsDialog_confirmed, DefaultFilename.GUESS_FOR_SAVE)
_popup_file_dialog(EditorFileDialog.MODE_SAVE_FILE, "_on_SaveAsDialog_confirmed", DefaultFilename.GUESS_FOR_SAVE)
ExtraOption.COPY:
if not _config_clipboard:
@ -379,10 +371,10 @@ func _on_Extra_id_pressed(id: int) -> void:
ExtraOption.PASTE_JSFXR:
var pasted := SFXRConfig.new()
if pasted.load_from_base58(DisplayServer.clipboard_get()) == OK:
if pasted.load_from_base58(OS.clipboard) == OK:
_restore_from_config(pasted)
else:
_popup_message(translator.t("Clipboard does not contain code copied from jsfxr."))
_popup_message(translator.tr("Clipboard does not contain code copied from jsfxr."))
_:
var i := id - ExtraOption.RECENT as int

View File

@ -1,297 +1,394 @@
[gd_scene load_steps=7 format=3 uid="uid://dv8q33r8aan5b"]
[gd_scene load_steps=7 format=2]
[ext_resource type="Script" path="res://addons/gdfxr/editor/EditorIconButton.gd" id="1"]
[ext_resource type="Script" path="res://addons/gdfxr/editor/Editor.gd" id="2"]
[ext_resource type="PackedScene" uid="uid://p27e3x3kk5e0" path="res://addons/gdfxr/editor/ParamSlider.tscn" id="3"]
[ext_resource type="PackedScene" uid="uid://b8wt6fq8w6mxc" path="res://addons/gdfxr/editor/ParamOption.tscn" id="4"]
[ext_resource type="PackedScene" uid="uid://cewvefxbttrds" path="res://addons/gdfxr/editor/PluginTranslator.tscn" id="5"]
[ext_resource type="PackedScene" path="res://addons/gdfxr/editor/VersionButton.tscn" id="6"]
[ext_resource path="res://addons/gdfxr/editor/EditorIconButton.gd" type="Script" id=1]
[ext_resource path="res://addons/gdfxr/editor/Editor.gd" type="Script" id=2]
[ext_resource path="res://addons/gdfxr/editor/ParamSlider.tscn" type="PackedScene" id=3]
[ext_resource path="res://addons/gdfxr/editor/ParamOption.tscn" type="PackedScene" id=4]
[ext_resource path="res://addons/gdfxr/editor/PluginTranslator.tscn" type="PackedScene" id=5]
[ext_resource path="res://addons/gdfxr/editor/VersionButton.tscn" type="PackedScene" id=6]
[node name="Editor" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("2")
script = ExtResource( 2 )
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
[node name="Toolbar" type="HBoxContainer" parent="."]
layout_mode = 2
margin_right = 1024.0
margin_bottom = 22.0
[node name="New" type="Button" parent="Toolbar"]
layout_mode = 2
tooltip_text = "New"
flat = true
script = ExtResource("1")
[node name="New" type="ToolButton" parent="Toolbar"]
margin_right = 12.0
margin_bottom = 22.0
hint_tooltip = "New"
script = ExtResource( 1 )
icon_name = "New"
[node name="Load" type="Button" parent="Toolbar"]
layout_mode = 2
tooltip_text = "Load"
flat = true
script = ExtResource("1")
[node name="Load" type="ToolButton" parent="Toolbar"]
margin_left = 16.0
margin_right = 28.0
margin_bottom = 22.0
hint_tooltip = "Load"
script = ExtResource( 1 )
icon_name = "Load"
[node name="Save" type="Button" parent="Toolbar"]
layout_mode = 2
tooltip_text = "Save"
flat = true
script = ExtResource("1")
[node name="Save" type="ToolButton" parent="Toolbar"]
margin_left = 32.0
margin_right = 44.0
margin_bottom = 22.0
hint_tooltip = "Save"
script = ExtResource( 1 )
icon_name = "Save"
[node name="Extra" type="MenuButton" parent="Toolbar"]
layout_mode = 2
tooltip_text = "Extra Options"
script = ExtResource("1")
margin_left = 48.0
margin_right = 60.0
margin_bottom = 22.0
hint_tooltip = "Extra Options"
script = ExtResource( 1 )
icon_name = "GuiTabMenuHl"
[node name="VSeparator" type="VSeparator" parent="Toolbar"]
layout_mode = 2
margin_left = 64.0
margin_right = 68.0
margin_bottom = 22.0
[node name="Play" type="Button" parent="Toolbar"]
layout_mode = 2
margin_left = 72.0
margin_right = 152.0
margin_bottom = 22.0
rect_min_size = Vector2( 80, 0 )
size_flags_horizontal = 0
text = "Play"
script = ExtResource("1")
script = ExtResource( 1 )
icon_name = "Play"
[node name="Restore" type="Button" parent="Toolbar"]
layout_mode = 2
tooltip_text = "Restore"
margin_left = 156.0
margin_right = 236.0
margin_bottom = 22.0
rect_min_size = Vector2( 80, 0 )
hint_tooltip = "Restore"
disabled = true
text = "Restore"
script = ExtResource("1")
script = ExtResource( 1 )
icon_name = "Reload"
[node name="VSeparator2" type="VSeparator" parent="Toolbar"]
layout_mode = 2
margin_left = 240.0
margin_right = 244.0
margin_bottom = 22.0
[node name="Filename" type="Label" parent="Toolbar"]
layout_mode = 2
margin_left = 248.0
margin_top = 4.0
margin_right = 1020.0
margin_bottom = 18.0
size_flags_horizontal = 3
text = "Unsaved sound"
clip_text = true
[node name="VersionButton" parent="Toolbar" instance=ExtResource("6")]
layout_mode = 2
[node name="VersionButton" parent="Toolbar" instance=ExtResource( 6 )]
margin_top = 4.0
margin_bottom = 18.0
website = "https://github.com/timothyqiu/gdfxr"
[node name="HSeparator" type="HSeparator" parent="."]
layout_mode = 2
margin_top = 26.0
margin_right = 1024.0
margin_bottom = 30.0
[node name="Editor" type="HBoxContainer" parent="."]
layout_mode = 2
margin_top = 34.0
margin_right = 1024.0
margin_bottom = 600.0
size_flags_vertical = 3
[node name="Generators" type="VBoxContainer" parent="Editor"]
layout_mode = 2
margin_right = 128.0
margin_bottom = 566.0
rect_min_size = Vector2( 128, 0 )
[node name="Button" type="Button" parent="Editor/Generators"]
layout_mode = 2
margin_right = 128.0
margin_bottom = 20.0
text = "Pickup/Coin"
[node name="Button2" type="Button" parent="Editor/Generators"]
layout_mode = 2
margin_top = 24.0
margin_right = 128.0
margin_bottom = 44.0
text = "Laser/Shoot"
[node name="Button3" type="Button" parent="Editor/Generators"]
layout_mode = 2
margin_top = 48.0
margin_right = 128.0
margin_bottom = 68.0
text = "Explosion"
[node name="Button4" type="Button" parent="Editor/Generators"]
layout_mode = 2
margin_top = 72.0
margin_right = 128.0
margin_bottom = 92.0
text = "Powerup"
[node name="Button5" type="Button" parent="Editor/Generators"]
layout_mode = 2
margin_top = 96.0
margin_right = 128.0
margin_bottom = 116.0
text = "Hit/Hurt"
[node name="Button6" type="Button" parent="Editor/Generators"]
layout_mode = 2
margin_top = 120.0
margin_right = 128.0
margin_bottom = 140.0
text = "Jump"
[node name="Button7" type="Button" parent="Editor/Generators"]
layout_mode = 2
margin_top = 144.0
margin_right = 128.0
margin_bottom = 164.0
text = "Blip/Select"
[node name="HSeparator" type="HSeparator" parent="Editor/Generators"]
layout_mode = 2
margin_top = 168.0
margin_right = 128.0
margin_bottom = 172.0
[node name="Button8" type="Button" parent="Editor/Generators"]
layout_mode = 2
margin_top = 176.0
margin_right = 128.0
margin_bottom = 196.0
size_flags_horizontal = 3
text = "Mutate"
[node name="Button9" type="Button" parent="Editor/Generators"]
layout_mode = 2
margin_top = 200.0
margin_right = 128.0
margin_bottom = 220.0
size_flags_horizontal = 3
text = "Randomize"
[node name="ScrollContainer" type="ScrollContainer" parent="Editor"]
layout_mode = 2
margin_left = 132.0
margin_right = 1024.0
margin_bottom = 566.0
size_flags_horizontal = 3
scroll_vertical_enabled = false
[node name="Params" type="HBoxContainer" parent="Editor/ScrollContainer"]
layout_mode = 2
margin_top = 188.0
margin_right = 895.0
margin_bottom = 366.0
size_flags_horizontal = 6
size_flags_vertical = 6
[node name="Envolope" type="VBoxContainer" parent="Editor/ScrollContainer/Params"]
layout_mode = 2
margin_right = 211.0
margin_bottom = 178.0
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource( 3 )]
margin_right = 211.0
margin_bottom = 22.0
label = "Attack Time"
parameter = "p_env_attack"
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource( 3 )]
margin_top = 26.0
margin_right = 211.0
margin_bottom = 48.0
label = "Sustain Time"
parameter = "p_env_sustain"
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource( 3 )]
margin_top = 52.0
margin_right = 211.0
margin_bottom = 74.0
label = "Sustain Punch"
parameter = "p_env_punch"
[node name="ParamSlider4" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider4" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource( 3 )]
margin_top = 78.0
margin_right = 211.0
margin_bottom = 100.0
label = "Decay Time"
parameter = "p_env_decay"
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource( 3 )]
margin_top = 104.0
margin_right = 211.0
margin_bottom = 126.0
label = "Change Amount"
parameter = "p_arp_mod"
bipolar = true
[node name="ParamSlider6" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider6" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource( 3 )]
margin_top = 130.0
margin_right = 211.0
margin_bottom = 152.0
label = "Change Speed"
parameter = "p_arp_speed"
[node name="ParamSlider7" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider7" parent="Editor/ScrollContainer/Params/Envolope" instance=ExtResource( 3 )]
margin_top = 156.0
margin_right = 211.0
margin_bottom = 178.0
label = "Volume"
parameter = "sound_vol"
[node name="Frequency" type="VBoxContainer" parent="Editor/ScrollContainer/Params"]
layout_mode = 2
margin_left = 215.0
margin_right = 425.0
margin_bottom = 178.0
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource( 3 )]
margin_right = 210.0
margin_bottom = 22.0
label = "Start Frequency"
parameter = "p_base_freq"
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource( 3 )]
margin_top = 26.0
margin_right = 210.0
margin_bottom = 48.0
label = "Min Frequency"
parameter = "p_freq_limit"
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource( 3 )]
margin_top = 52.0
margin_right = 210.0
margin_bottom = 74.0
label = "Slide"
parameter = "p_freq_ramp"
bipolar = true
[node name="ParamSlider4" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider4" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource( 3 )]
margin_top = 78.0
margin_right = 210.0
margin_bottom = 100.0
label = "Delta Slide"
parameter = "p_freq_dramp"
bipolar = true
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource( 3 )]
margin_top = 104.0
margin_right = 210.0
margin_bottom = 126.0
label = "Vibrato Depth"
parameter = "p_vib_strength"
[node name="ParamSlider6" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider6" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource( 3 )]
margin_top = 130.0
margin_right = 210.0
margin_bottom = 152.0
label = "Vibrato Speed"
parameter = "p_vib_speed"
[node name="ParamSlider7" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider7" parent="Editor/ScrollContainer/Params/Frequency" instance=ExtResource( 3 )]
margin_top = 156.0
margin_right = 210.0
margin_bottom = 178.0
label = "Repeat Speed"
parameter = "p_repeat_speed"
[node name="Waveform" type="VBoxContainer" parent="Editor/ScrollContainer/Params"]
layout_mode = 2
margin_left = 429.0
margin_right = 649.0
margin_bottom = 178.0
[node name="WaveformOption" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource("4")]
layout_mode = 2
options = ["Square", "Sawtooth", "Sine", "Noise"]
[node name="WaveformOption" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource( 4 )]
margin_right = 220.0
margin_bottom = 22.0
options = [ "Square", "Sawtooth", "Sine", "Noise" ]
parameter = "wave_type"
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource( 3 )]
margin_top = 26.0
margin_right = 220.0
margin_bottom = 48.0
label = "Square Duty"
parameter = "p_duty"
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource( 3 )]
margin_top = 52.0
margin_right = 220.0
margin_bottom = 74.0
label = "Duty Sweep"
parameter = "p_duty_ramp"
bipolar = true
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource( 3 )]
margin_top = 78.0
margin_right = 220.0
margin_bottom = 100.0
label = "Phaser Offset"
parameter = "p_pha_offset"
bipolar = true
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Waveform" instance=ExtResource( 3 )]
margin_top = 104.0
margin_right = 220.0
margin_bottom = 126.0
label = "Phaser Sweep"
parameter = "p_pha_ramp"
bipolar = true
[node name="Filter" type="VBoxContainer" parent="Editor/ScrollContainer/Params"]
layout_mode = 2
margin_left = 653.0
margin_right = 895.0
margin_bottom = 178.0
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource( 3 )]
margin_right = 242.0
margin_bottom = 22.0
label = "Low-pass Cutoff"
parameter = "p_lpf_freq"
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider2" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource( 3 )]
margin_top = 26.0
margin_right = 242.0
margin_bottom = 48.0
label = "Low-pass Sweep"
parameter = "p_lpf_ramp"
bipolar = true
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider5" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource( 3 )]
margin_top = 52.0
margin_right = 242.0
margin_bottom = 74.0
label = "Low-pass Resonance"
parameter = "p_lpf_resonance"
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider3" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource( 3 )]
margin_top = 78.0
margin_right = 242.0
margin_bottom = 100.0
label = "High-pass Cutoff"
parameter = "p_hpf_freq"
[node name="ParamSlider4" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource("3")]
layout_mode = 2
[node name="ParamSlider4" parent="Editor/ScrollContainer/Params/Filter" instance=ExtResource( 3 )]
margin_top = 104.0
margin_right = 242.0
margin_bottom = 126.0
label = "High-pass Sweep"
parameter = "p_hpf_ramp"
bipolar = true
[node name="PluginTranslator" parent="." instance=ExtResource("5")]
[node name="PluginTranslator" parent="." instance=ExtResource( 5 )]
[connection signal="pressed" from="Toolbar/New" to="." method="_on_New_pressed"]
[connection signal="pressed" from="Toolbar/Load" to="." method="_on_Load_pressed"]
[connection signal="pressed" from="Toolbar/Save" to="." method="_on_Save_pressed"]
[connection signal="about_to_popup" from="Toolbar/Extra" to="." method="_on_Extra_about_to_show"]
[connection signal="about_to_show" from="Toolbar/Extra" to="." method="_on_Extra_about_to_show"]
[connection signal="pressed" from="Toolbar/Play" to="." method="_on_Play_pressed"]
[connection signal="pressed" from="Toolbar/Restore" to="." method="_on_Restore_pressed"]
[connection signal="pressed" from="Editor/Generators/Button" to="." method="_on_Randomize_pressed" binds= [0]]
[connection signal="pressed" from="Editor/Generators/Button2" to="." method="_on_Randomize_pressed" binds= [1]]
[connection signal="pressed" from="Editor/Generators/Button3" to="." method="_on_Randomize_pressed" binds= [2]]
[connection signal="pressed" from="Editor/Generators/Button4" to="." method="_on_Randomize_pressed" binds= [3]]
[connection signal="pressed" from="Editor/Generators/Button5" to="." method="_on_Randomize_pressed" binds= [4]]
[connection signal="pressed" from="Editor/Generators/Button6" to="." method="_on_Randomize_pressed" binds= [5]]
[connection signal="pressed" from="Editor/Generators/Button7" to="." method="_on_Randomize_pressed" binds= [6]]
[connection signal="pressed" from="Editor/Generators/Button" to="." method="_on_Randomize_pressed" binds= [ 0 ]]
[connection signal="pressed" from="Editor/Generators/Button2" to="." method="_on_Randomize_pressed" binds= [ 1 ]]
[connection signal="pressed" from="Editor/Generators/Button3" to="." method="_on_Randomize_pressed" binds= [ 2 ]]
[connection signal="pressed" from="Editor/Generators/Button4" to="." method="_on_Randomize_pressed" binds= [ 3 ]]
[connection signal="pressed" from="Editor/Generators/Button5" to="." method="_on_Randomize_pressed" binds= [ 4 ]]
[connection signal="pressed" from="Editor/Generators/Button6" to="." method="_on_Randomize_pressed" binds= [ 5 ]]
[connection signal="pressed" from="Editor/Generators/Button7" to="." method="_on_Randomize_pressed" binds= [ 6 ]]
[connection signal="pressed" from="Editor/Generators/Button8" to="." method="_on_Mutate_pressed"]
[connection signal="pressed" from="Editor/Generators/Button9" to="." method="_on_Randomize_pressed" binds= [-1]]
[connection signal="pressed" from="Editor/Generators/Button9" to="." method="_on_Randomize_pressed" binds= [ -1 ]]

View File

@ -1,7 +1,7 @@
@tool
tool
extends Button
@export var icon_name: String
export var icon_name: String
var plugin: EditorPlugin # a hack to know if this is executing as plugin

View File

@ -1,14 +1,13 @@
@tool
tool
extends HBoxContainer
signal param_changed(name, value)
signal param_reset(name)
@export var options: Array :
set = set_options
@export var parameter: String # Could be PackedStringArray, but pybabel won't catch that
export var options: Array setget set_options
export var parameter: String # Could be PoolStringArray, but pybabel won't catch that
@onready var option_button := $OptionButton as OptionButton
onready var option_button := $OptionButton as OptionButton
func _ready():

View File

@ -1,34 +1,42 @@
[gd_scene load_steps=3 format=3 uid="uid://b8wt6fq8w6mxc"]
[gd_scene load_steps=3 format=2]
[ext_resource type="Script" path="res://addons/gdfxr/editor/ParamOption.gd" id="1"]
[ext_resource type="Script" path="res://addons/gdfxr/editor/EditorIconButton.gd" id="2"]
[ext_resource path="res://addons/gdfxr/editor/ParamOption.gd" type="Script" id=1]
[ext_resource path="res://addons/gdfxr/editor/EditorIconButton.gd" type="Script" id=2]
[node name="WaveformOption" type="HBoxContainer"]
offset_right = 253.0
offset_bottom = 40.0
pivot_offset = Vector2(41, -65)
margin_right = 253.0
margin_bottom = 40.0
rect_pivot_offset = Vector2( 41, -65 )
size_flags_horizontal = 3
script = ExtResource("1")
script = ExtResource( 1 )
[node name="Label" type="Label" parent="."]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
margin_top = 13.0
margin_right = 128.0
margin_bottom = 27.0
rect_min_size = Vector2( 100, 0 )
size_flags_horizontal = 3
text = "Waveform"
horizontal_alignment = 2
align = 2
[node name="OptionButton" type="OptionButton" parent="."]
custom_minimum_size = Vector2(105, 0)
layout_mode = 2
margin_left = 132.0
margin_top = 10.0
margin_right = 237.0
margin_bottom = 30.0
rect_min_size = Vector2( 105, 0 )
size_flags_vertical = 4
clip_text = true
[node name="Reset" type="Button" parent="."]
layout_mode = 2
size_flags_vertical = 4
[node name="Reset" type="ToolButton" parent="."]
margin_left = 241.0
margin_top = 9.0
margin_right = 253.0
margin_bottom = 31.0
focus_mode = 0
flat = true
script = ExtResource("2")
size_flags_vertical = 4
enabled_focus_mode = 0
script = ExtResource( 2 )
icon_name = "ReloadSmall"
[connection signal="item_selected" from="OptionButton" to="." method="_on_OptionButton_item_selected"]

View File

@ -1,33 +1,21 @@
@tool
tool
extends HBoxContainer
signal param_changed(name, value)
signal param_reset(name)
@export var label: String :
set = set_label
@export var parameter: String
@export var bipolar := false :
set = set_bipolar
func _ready():
set_label(label)
set_bipolar(bipolar)
export var label: String setget set_label
export var parameter: String
export var bipolar := false setget set_bipolar
func set_label(v: String) -> void:
label = v
if is_inside_tree():
$Label.text = v
$Label.text = v
func set_bipolar(v: bool) -> void:
bipolar = v
if not is_inside_tree():
return
if bipolar:
$HSlider.min_value = -1.0
else:

View File

@ -1,35 +1,41 @@
[gd_scene load_steps=4 format=3 uid="uid://p27e3x3kk5e0"]
[gd_scene load_steps=4 format=2]
[ext_resource type="Script" path="res://addons/gdfxr/editor/ParamSlider.gd" id="1"]
[ext_resource type="Script" path="res://addons/gdfxr/editor/EditorIconButton.gd" id="2"]
[ext_resource type="Script" path="res://addons/gdfxr/editor/EditSlider.gd" id="3"]
[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/EditSlider.gd" type="Script" id=3]
[node name="ParamSlider" type="HBoxContainer"]
offset_right = 253.0
offset_bottom = 40.0
margin_right = 253.0
margin_bottom = 40.0
size_flags_horizontal = 3
script = ExtResource("1")
script = ExtResource( 1 )
[node name="Label" type="Label" parent="."]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
margin_top = 13.0
margin_right = 128.0
margin_bottom = 27.0
rect_min_size = Vector2( 100, 0 )
size_flags_horizontal = 3
horizontal_alignment = 2
align = 2
[node name="HSlider" type="Control" parent="."]
clip_contents = true
custom_minimum_size = Vector2(105, 0)
layout_mode = 2
margin_left = 132.0
margin_right = 237.0
margin_bottom = 40.0
rect_min_size = Vector2( 105, 0 )
rect_clip_content = true
focus_mode = 2
mouse_default_cursor_shape = 10
script = ExtResource("3")
script = ExtResource( 3 )
[node name="Reset" type="Button" parent="."]
layout_mode = 2
size_flags_vertical = 4
[node name="Reset" type="ToolButton" parent="."]
margin_left = 241.0
margin_top = 9.0
margin_right = 253.0
margin_bottom = 31.0
focus_mode = 0
flat = true
script = ExtResource("2")
size_flags_vertical = 4
script = ExtResource( 2 )
icon_name = "ReloadSmall"
[connection signal="value_changed" from="HSlider" to="." method="_on_HSlider_value_changed"]

View File

@ -1,8 +1,7 @@
@tool
tool
extends Node
var plugin: EditorPlugin :
set = set_plugin
var plugin: EditorPlugin setget set_plugin
var _translation: Translation
@ -18,40 +17,40 @@ func set_plugin(v: EditorPlugin) -> void:
plugin = v
var locale: String = plugin.get_editor_interface().get_editor_settings().get('interface/editor/editor_language')
var script := get_script() as Script
var path := script.resource_path.get_base_dir().path_join("translations/%s.po" % locale)
var path := script.resource_path.get_base_dir().plus_file("translations/%s.po" % locale)
if ResourceLoader.exists(path):
_translation = ResourceLoader.load(path)
if _translation:
_translate_node.call_deferred(get_parent())
_translate_node(get_parent())
func t(message: StringName) -> String:
func tr(message: StringName) -> StringName:
if _translation:
var translated := _translation.get_message(message)
if translated:
return String(translated)
return String(message)
if translated != "":
return translated
return message
func _translate_node(node: Node):
if node is Control:
node.tooltip_text = t(node.tooltip_text)
node.hint_tooltip = tr(node.hint_tooltip)
if node is HBoxContainer and node.has_method("set_options"):
var options = []
for item in node.options:
options.append(t(item))
options.append(tr(item))
node.options = options
if node is Button and not node is OptionButton:
node.text = t(node.text)
node.text = tr(node.text)
if node is Label:
node.text = t(node.text)
node.text = tr(node.text)
if node is Slider:
node.tooltip_text = t(node.tooltip_text)
node.hint_tooltip = tr(node.hint_tooltip)
for child in node.get_children():
_translate_node(child)

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://cewvefxbttrds"]
[gd_scene load_steps=2 format=2]
[ext_resource type="Script" path="res://addons/gdfxr/editor/PluginTranslator.gd" id="1"]
[ext_resource path="res://addons/gdfxr/editor/PluginTranslator.gd" type="Script" id=1]
[node name="PluginTranslator" type="Node"]
script = ExtResource("1")
script = ExtResource( 1 )

View File

@ -1,17 +1,16 @@
@tool
tool
extends LinkButton
@export var website: String
export var website: String
var plugin: EditorPlugin :
set = set_plugin
var plugin: EditorPlugin setget set_plugin
func set_plugin(v: EditorPlugin) -> void:
plugin = v
var script := get_script() as Script
var path := script.resource_path.get_base_dir().path_join("../plugin.cfg")
var path := script.resource_path.get_base_dir().plus_file("../plugin.cfg")
var cfg := ConfigFile.new()
var err := cfg.load(path)

View File

@ -4,10 +4,10 @@
[node name="VersionButton" type="LinkButton"]
self_modulate = Color( 1, 1, 1, 0.65 )
offset_left = 1024.0
offset_top = 5.0
offset_right = 1024.0
offset_bottom = 19.0
margin_left = 1024.0
margin_top = 5.0
margin_right = 1024.0
margin_bottom = 19.0
focus_mode = 2
size_flags_vertical = 4
underline = 1

View File

@ -1,72 +1,64 @@
@tool
tool
extends EditorImportPlugin
const SFXRConfig = preload("SFXRConfig.gd")
const SFXRGenerator = preload("SFXRGenerator.gd")
func _get_importer_name():
func get_importer_name():
return "com.timothyqiu.gdfxr.importer"
func _get_import_order():
return ResourceImporter.IMPORT_ORDER_DEFAULT
func _get_priority():
return 1.0
func _get_visible_name():
func get_visible_name():
return "SFXR Audio"
func _get_recognized_extensions():
func get_recognized_extensions():
return ["sfxr"]
func _get_save_extension():
func get_save_extension():
return "sample"
func _get_resource_type():
return "AudioStreamWAV"
func get_resource_type():
return "AudioStreamSample"
func _get_preset_count():
func get_preset_count():
return 1
func _get_preset_name(preset):
func get_preset_name(preset):
return "Default"
func _get_import_options(path, preset):
func get_import_options(preset):
return [
{
name="loop",
default_value=false,
"name":"loop",
"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":"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,
"name":"sample_rate",
"property_hint":PROPERTY_HINT_ENUM,
"hint_string":"44100 Hz,22050 Hz",
"default_value":SFXRGenerator.WavFreq.WAV_FREQ_44100,
},
]
func _get_option_visibility(path, option, options):
func get_option_visibility(option, options):
return true
func _import(source_file, save_path, options, platform_variants, gen_files):
func import(source_file, save_path, options, platform_variants, gen_files):
var config := SFXRConfig.new()
var err := config.load(source_file)
if err != OK:
@ -77,8 +69,8 @@ func _import(source_file, save_path, options, platform_variants, gen_files):
config, options.bit_depth, options.sample_rate
)
if options.loop:
stream.loop_mode = AudioStreamWAV.LOOP_FORWARD
stream.loop_mode = AudioStreamSample.LOOP_FORWARD
stream.loop_end = stream.data.size()
var filename = save_path + "." + _get_save_extension()
return ResourceSaver.save(stream, filename)
var filename = save_path + "." + get_save_extension()
return ResourceSaver.save(filename, stream)

View File

@ -3,5 +3,5 @@
name="gdfxr"
description="A Godot plugin that ports sfxr, the popular program of choice to make retro sound effects for games."
author="Haoyu Qiu"
version="2.0"
version="1.3"
script="plugin.gd"

View File

@ -1,4 +1,4 @@
@tool
tool
extends EditorPlugin
@ -10,7 +10,7 @@ func _enter_tree():
import_plugin = preload("import_plugin.gd").new()
add_import_plugin(import_plugin)
sfxr_editor = preload("editor/Editor.tscn").instantiate()
sfxr_editor = preload("editor/Editor.tscn").instance()
sfxr_editor.plugin = self
add_control_to_bottom_panel(sfxr_editor, "gdfxr")
@ -24,15 +24,15 @@ func _exit_tree():
import_plugin = null
func _handles(object: Object) -> bool:
return object is AudioStreamWAV and object.resource_path.ends_with(".sfxr")
func handles(object: Object) -> bool:
return object is AudioStreamSample and object.resource_path.ends_with(".sfxr")
func _edit(object: Object):
sfxr_editor.edit(object)
func edit(object: Object):
sfxr_editor.edit(object.resource_path) # Should already passed `handles()` checks
func _make_visible(visible: bool):
func make_visible(visible: bool):
if visible:
make_bottom_panel_item_visible(sfxr_editor)
elif sfxr_editor.is_visible_in_tree():

View File

@ -4,8 +4,8 @@ extends Container
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
onready var audio_player: AudioStreamPlayer = $AudioPlayer
onready var adhoc_audio_player: AudioStreamPlayer = $AdhocAudioPlayer
func _on_Play_pressed() -> void:

View File

@ -1,58 +1,73 @@
[gd_scene load_steps=3 format=3 uid="uid://bv31mn2hs6wom"]
[gd_scene load_steps=3 format=2]
[ext_resource type="Script" path="res://example/Example.gd" id="1"]
[ext_resource type="AudioStream" uid="uid://byf7u7a25fuf4" path="res://example/example.sfxr" id="2"]
[ext_resource path="res://example/Example.gd" type="Script" id=1]
[ext_resource path="res://example/example.sfxr" type="AudioStream" id=2]
[node name="Example" type="GridContainer"]
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/h_separation = 32
theme_override_constants/v_separation = 32
custom_constants/hseparation = 32
custom_constants/vseparation = 32
columns = 2
script = ExtResource("1")
script = ExtResource( 1 )
[node name="AudioPlayer" type="AudioStreamPlayer" parent="."]
stream = ExtResource("2")
stream = ExtResource( 2 )
[node name="AdhocAudioPlayer" type="AudioStreamPlayer" parent="."]
[node name="Play" type="Button" parent="."]
layout_mode = 2
margin_right = 141.0
margin_bottom = 32.0
rect_min_size = Vector2( 128, 32 )
size_flags_vertical = 4
text = "Play"
[node name="Label" type="Label" parent="."]
custom_minimum_size = Vector2(500, 0)
layout_mode = 2
margin_left = 173.0
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_mode = 3
autowrap = true
[node name="PlayFile" type="Button" parent="."]
layout_mode = 2
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="."]
custom_minimum_size = Vector2(500, 0)
layout_mode = 2
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_mode = 3
autowrap = true
[node name="Generate" type="Button" parent="."]
layout_mode = 2
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="."]
custom_minimum_size = Vector2(500, 0)
layout_mode = 2
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_mode = 3
autowrap = true
[connection signal="pressed" from="Play" to="." method="_on_Play_pressed"]
[connection signal="pressed" from="PlayFile" to="." method="_on_PlayFile_pressed"]

View File

@ -1,14 +1,13 @@
[remap]
importer="com.timothyqiu.gdfxr.importer"
type="AudioStreamWAV"
uid="uid://byf7u7a25fuf4"
path="res://.godot/imported/example.sfxr-e3731bcdcd8403a2391e92667a5d1076.sample"
type="AudioStreamSample"
path="res://.import/example.sfxr-e3731bcdcd8403a2391e92667a5d1076.sample"
[deps]
source_file="res://example/example.sfxr"
dest_files=["res://.godot/imported/example.sfxr-e3731bcdcd8403a2391e92667a5d1076.sample"]
dest_files=[ "res://.import/example.sfxr-e3731bcdcd8403a2391e92667a5d1076.sample" ]
[params]

View File

@ -1,9 +1,8 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b16qnfl6slyy2"
path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
type="StreamTexture"
path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex"
metadata={
"vram_texture": false
}
@ -11,24 +10,26 @@ metadata={
[deps]
source_file="res://icon.png"
dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ]
[params]
compress/mode=0
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/hdr_mode=0
compress/bptc_ldr=0
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
flags/repeat=0
flags/filter=true
flags/mipmaps=false
flags/anisotropic=false
flags/srgb=2
process/fix_alpha_border=true
process/premult_alpha=false
process/HDR_as_SRGB=false
process/invert_color=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
stream=false
size_limit=0
detect_3d=true
svg/scale=1.0

View File

@ -6,15 +6,14 @@
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
config_version=4
[application]
config/name="gdfxr"
run/main_scene="res://example/Example.tscn"
config/features=PackedStringArray("4.0")
config/icon="res://icon.png"
[editor_plugins]
enabled=PackedStringArray("res://addons/gdfxr/plugin.cfg")
enabled=PoolStringArray( "res://addons/gdfxr/plugin.cfg" )

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 19 KiB