Compare commits

..

17 Commits

12 changed files with 78 additions and 130 deletions

View File

@@ -99,36 +99,6 @@ jobs:
runs-on: self-hosted
if: github.ref == 'refs/heads/master' && github.event_name == 'push' && success()
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Generate Release Info
id: release_info
run: |
# Get current date and time
$date = Get-Date -Format "yyyy.MM.dd"
$time = Get-Date -Format "HHmm"
# Get short commit hash (first 7 characters)
$shortSha = "${{ github.sha }}".Substring(0, 7)
# Get commit count since last tag (for build number)
$buildNumber = & git rev-list --count HEAD
# Create version-like tag and release name
$tag = "v$date-build$buildNumber"
$releaseName = "PNGTuber+ v$date Build $buildNumber"
Write-Host "Generated tag: $tag"
Write-Host "Generated release name: $releaseName"
# Set outputs for use in next steps
echo "tag=$tag" >> $env:GITHUB_OUTPUT
echo "release_name=$releaseName" >> $env:GITHUB_OUTPUT
echo "short_sha=$shortSha" >> $env:GITHUB_OUTPUT
echo "build_number=$buildNumber" >> $env:GITHUB_OUTPUT
- name: Download Windows Build
uses: actions/download-artifact@v4
with:
@@ -141,19 +111,21 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.release_info.outputs.tag }}
release_name: ${{ steps.release_info.outputs.release_name }}
tag_name: build-${{ github.sha }}
release_name: PNGTuber Plus Build ${{ github.sha }}
body: |
Automated build of PNGTuber Plus
## Recent Updates by Litruv
- **StreamDeck Integration**: Add buttons to enable the streamdeck plugin that was built in
- **Rotation Wobble**: New sprite rotation effects with frequency and amplitude controls
- **Enhanced Modifier Keys**: Support for complex key combinations (Ctrl+Shift+Alt+Cmd)
- **Godot 4.4.1 Support**: Updated for latest Godot stable release
## Installation
1. Download PNGTuber-Plus.exe from the assets below
2. Run the executable - no installation required!
3. For StreamDeck integration, download the plugin from [here](https://github.com/BoyneGames/streamdeck-godot-plugin/releases/tag/1.0.0)
## Credits
- Original project by [kaiakairos](https://github.com/kaiakairos/PNGTuber-Plus)
- Microphone improvements by [k0ffinz](https://github.com/k0ffinz/PNGTuber-Plus)
draft: false
prerelease: false

View File

@@ -8,10 +8,9 @@
- **Rotation Wobble**: New sprite rotation effects with frequency and amplitude controls
- **Enhanced Modifier Keys**: Support for complex key combinations (Ctrl+Shift+Alt+Cmd)
- **Godot 4.4.1 Support**: Updated for latest Godot stable release
- **Microphone Improvements**: Audio delay fix & more *from [k0ffinz/PNGTuber-Plus](https://github.com/k0ffinz/PNGTuber-Plus)*
## 🙏 Credits
- **Original Project**: [PNGTuber Plus by kaiakairos](https://github.com/kaiakairos/PNGTuber-Plus)
- **Original Project**: PNGTuber Plus base application
- **StreamDeck Plugin**: Based on [BoyneGames StreamDeck Godot Plugin](https://github.com/BoyneGames/streamdeck-godot-plugin)
- **Microphone Improvements**: k0ffinz - Experimental mic loudness detection, improved audio initialization, and audio delay fixes from [k0ffinz/PNGTuber-Plus](https://github.com/k0ffinz/PNGTuber-Plus)
- **Enhanced Features**: Max Litruv Boonzaayer

View File

@@ -29,13 +29,13 @@ var blink = false
var blinkTick = 0
#Audio Listener
var playa:AudioStreamPlayer
var currentMicrophone = null
var speaking = false
var spectrum
var volume = 0
var volumeSensitivity = 0.0
var experimentalMicLoudness = false
var volumeLimit = 0.0
var senseLimit = 0.0
@@ -44,6 +44,8 @@ var senseLimit = 0.0
signal startSpeaking
signal stopSpeaking
var micResetTime = 180
var updatePusherNode = null
var rand = RandomNumberGenerator.new()
@@ -54,23 +56,28 @@ func _ready():
if !Saving.settings.has("useStreamDeck"):
Saving.settings["useStreamDeck"] = false
if Saving.settings.has("experimentalMicLoudness"):
Global.experimentalMicLoudness = Saving.settings["experimentalMicLoudness"]
if Saving.settings.has("secondsToMicReset"):
Global.micResetTime = Saving.settings["secondsToMicReset"]
else:
Saving.settings["experimentalMicLoudness"] = false
Saving.settings["secondsToMicReset"] = 180
createMicrophone()
func createMicrophone():
if playa != null:
remove_child(playa)
playa.free()
playa = AudioStreamPlayer.new()
var playa = AudioStreamPlayer.new()
var mic = AudioStreamMicrophone.new()
playa.stream = mic
playa.autoplay = true
playa.bus = "MIC"
playa.stream = AudioStreamMicrophone.new()
add_child(playa)
await get_tree().create_timer(0.25).timeout # apparently it works to fix WASAPI "Initialize failed with error 0xffffffff88890002" and "init_input_device" errors
playa.play()
currentMicrophone = playa
await get_tree().create_timer(micResetTime).timeout
if currentMicrophone != playa:
return
deleteAllMics()
currentMicrophone = null
await get_tree().create_timer(0.25).timeout
createMicrophone()
func deleteAllMics():
for child in get_children():
@@ -80,9 +87,8 @@ func deleteAllMics():
func _process(delta):
animationTick += 1
volume = (AudioServer.get_bus_peak_volume_left_db(1, 0) + AudioServer.get_bus_peak_volume_right_db(1, 0)) / 1600.0 + 0.25 if experimentalMicLoudness else spectrum.get_magnitude_for_frequency_range(20, 20000).length()
if playa != null:
volume = spectrum.get_magnitude_for_frequency_range(20, 20000).length()
if currentMicrophone != null:
volumeSensitivity = lerp(volumeSensitivity,0.0,delta*2)
if volume>volumeLimit:

View File

@@ -54,13 +54,13 @@ var settings = {
"bounce":250,
"gravity":1000,
"maxFPS":60,
"secondsToMicReset":180,
"backgroundColor":var_to_str(Color(0.0,0.0,0.0,0.0)),
"filtering":false,
"costumeKeys":["1","2","3","4","5","6","7","8","9","0"],
"blinkSpeed":1.0,
"blinkChance":200,
"bounceOnCostumeChange":false,
"experimentalMicLoudness":false,
}
var settingsPath = "user://settings.pngtp"

View File

@@ -8,7 +8,7 @@ dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter="addons/godot-git-plugin/*"
exclude_filter=""
export_path="build/windows/PNGTuber-Plus.exe"
patches=PackedStringArray()
encryption_include_filters=""
@@ -23,7 +23,7 @@ script_export_mode=2
custom_template/debug=""
custom_template/release=""
debug/export_console_wrapper=2
binary_format/embed_pck=true
binary_format/embed_pck=false
texture_format/s3tc_bptc=true
texture_format/etc2_astc=false
binary_format/architecture="x86_64"

View File

@@ -445,9 +445,9 @@ func _on_kofi_pressed():
Global.pushUpdate("Support me on ko-fi!")
func _on_bluesky_pressed():
OS.shell_open("https://bsky.app/profile/kaiakairos.net")
Global.pushUpdate("Follow me on bluesky!")
func _on_twitter_pressed():
OS.shell_open("https://twitter.com/kaiakairos")
Global.pushUpdate("Follow me on twitter!")
func _on_replace_button_pressed():
@@ -627,46 +627,28 @@ func _on_background_input_capture_bg_key_pressed(node, keys_pressed):
if i == KEY_CTRL or i == KEY_SHIFT or i == KEY_ALT or i == KEY_META:
continue
# Skip mouse button keycodes (common source of phantom inputs)
if i == MOUSE_BUTTON_LEFT or i == MOUSE_BUTTON_RIGHT or i == MOUSE_BUTTON_MIDDLE:
continue
# Skip phantom keypad inputs if no actual keypad connected
var key_name = OS.get_keycode_string(i)
if key_name.begins_with("kp ") or key_name.begins_with("Kp "):
# Skip keypad inputs unless they're from actual key presses
continue
if !key_name.strip_edges().is_empty():
keyStrings.append(modifier_prefix + key_name)
else:
keyStrings.append(modifier_prefix + "Keycode" + str(i))
var key_name = OS.get_keycode_string(i) if !OS.get_keycode_string(i).strip_edges().is_empty() else "Keycode" + str(i)
keyStrings.append(modifier_prefix + key_name)
if fileSystemOpen:
return
if keyStrings.size() <= 0:
emit_signal("emptiedCapture")
emit_signal("fatfuckingballs")
return
if settingsMenu.awaitingCostumeInput >= 0:
# Cancel key assignment if user clicks outside the settings menu
if keyStrings[0] == "Keycode1" and !settingsMenu.hasMouse:
# Don't assign the key, just complete the flow
emit_signal("pressedKey")
return
if keyStrings[0] == "Keycode1":
if !settingsMenu.hasMouse:
emit_signal("pressedKey")
return
var currentButton = costumeKeys[settingsMenu.awaitingCostumeInput]
costumeKeys[settingsMenu.awaitingCostumeInput] = keyStrings[0]
Saving.settings["costumeKeys"] = costumeKeys
Global.pushUpdate("Changed costume " + str(settingsMenu.awaitingCostumeInput+1) + " hotkey from \"" + currentButton + "\" to \"" + keyStrings[0] + "\"")
emit_signal("pressedKey")
return
# Handle sprite visibility toggles if not waiting for costume input
spriteVisToggles.emit(keyStrings)
for key in keyStrings:
var i = costumeKeys.find(key)
@@ -674,3 +656,18 @@ func _on_background_input_capture_bg_key_pressed(node, keys_pressed):
changeCostume(i+1)
func bgInputSprite(node, keys_pressed):
if fileSystemOpen:
return
var keyStrings = []
for i in keys_pressed:
if keys_pressed[i]:
keyStrings.append(OS.get_keycode_string(i) if !OS.get_keycode_string(i).strip_edges().is_empty() else "Keycode" + str(i))
if keyStrings.size() <= 0:
emit_signal("fatfuckingballs")
return
spriteVisToggles.emit(keyStrings)

View File

@@ -14341,7 +14341,7 @@ offset_bottom = 27.0
theme = SubResource("Theme_rv56a")
flat = true
[node name="bluesky" type="Button" parent="ControlPanel/Links"]
[node name="twitter" type="Button" parent="ControlPanel/Links"]
offset_left = -5.0
offset_top = -29.0
offset_right = 61.0
@@ -14730,7 +14730,7 @@ z_index = -4096
[connection signal="pressed" from="ControlPanel/SettingsButton/settingsButtons" to="." method="_on_settings_buttons_pressed"]
[connection signal="pressed" from="ControlPanel/Edit/Button" to="." method="swapMode"]
[connection signal="pressed" from="ControlPanel/Links/kofi" to="." method="_on_kofi_pressed"]
[connection signal="pressed" from="ControlPanel/Links/bluesky" to="." method="_on_bluesky_pressed"]
[connection signal="pressed" from="ControlPanel/Links/twitter" to="." method="_on_twitter_pressed"]
[connection signal="pressed" from="EditControls/Add/addButton" to="." method="_on_add_button_pressed"]
[connection signal="pressed" from="EditControls/Link/linkButton" to="." method="_on_link_button_pressed"]
[connection signal="pressed" from="EditControls/Exit/Button2" to="." method="swapMode"]
@@ -14744,4 +14744,5 @@ z_index = -4096
[connection signal="visibility_changed" from="ReplaceDialog" to="." method="_on_replace_dialog_visibility_changed"]
[connection signal="file_selected" from="SaveDialog" to="." method="_on_save_dialog_file_selected"]
[connection signal="file_selected" from="LoadDialog" to="." method="_on_load_dialog_file_selected"]
[connection signal="bg_key_pressed" from="BackgroundInputCapture" to="." method="bgInputSprite"]
[connection signal="bg_key_pressed" from="BackgroundInputCapture" to="." method="_on_background_input_capture_bg_key_pressed"]

View File

@@ -23,7 +23,6 @@ config/icon="res://icon.png"
[audio]
driver/enable_input=true
buses/channel_disable_time=0.0
[autoload]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -7,11 +7,17 @@ func _ready():
func _on_button_pressed():
if !get_parent().get_parent().get_parent().visible:
return
AudioServer.input_device = micName
Global.createMicrophone()
Global.deleteAllMics()
Global.currentMicrophone = null
get_parent().get_parent().get_parent().visible = false
await get_tree().create_timer(1.0).timeout
Global.createMicrophone()

View File

@@ -46,8 +46,6 @@ func setvalues():
label.text = "costume " + str(tag) + " key: \"" + Global.main.costumeKeys[tag-1] + "\""
tag += 1
$experimentalMicLoudness/checkmark.button_pressed = Global.experimentalMicLoudness
func _on_color_picker_button_color_changed(color):
get_viewport().transparent_bg = false
RenderingServer.set_default_clear_color(color)
@@ -138,10 +136,13 @@ func _on_bounce_gravity_value_changed(value):
func costumeButtonsPressed(label,id):
label.text = "AWAITING INPUT"
await Global.main.emptiedCapture
awaitingCostumeInput = id - 1
await Global.main.pressedKey
label.text = "costume " + str(id) + " key: \"" + Global.main.costumeKeys[id - 1] + "\""
await Global.main.emptiedCapture
awaitingCostumeInput = -1
func _on_costume_button_1_pressed():
@@ -205,13 +206,6 @@ func _on_stream_deck_check_toggled(button_pressed):
ElgatoStreamDeck.refresh_connection()
Global.pushUpdate("StreamDeck integration " + ("enabled" if button_pressed else "disabled") + ".")
## Handle experimental microphone loudness toggle
## @param checked: bool - Whether experimental mic loudness should be enabled
func _on_experimental_mic_loudness_toggle(checked):
Global.experimentalMicLoudness = checked
Saving.settings["experimentalMicLoudness"] = checked
print("Experimental mic loudness: ", checked)
func _process(delta):
var g = to_local(get_global_mouse_position())

View File

@@ -14289,67 +14289,46 @@ offset_bottom = 28.0
text = "confirm"
[node name="StreamDeck" type="Node2D" parent="."]
position = Vector2(16, 337)
position = Vector2(16, 373)
[node name="Label" type="Label" parent="StreamDeck"]
offset_right = 138.0
offset_bottom = 34.0
text = "use streamdeck"
metadata/_edit_use_anchors_ = true
[node name="streamDeckCheck" type="CheckBox" parent="StreamDeck"]
offset_left = 130.0
offset_top = 1.0
offset_right = 154.0
offset_bottom = 25.0
metadata/_edit_use_anchors_ = true
[node name="experimentalMicLoudness" type="Node2D" parent="."]
position = Vector2(16, 369)
[node name="Label" type="Label" parent="experimentalMicLoudness"]
offset_right = 200.0
offset_bottom = 34.0
text = "mic loudness"
[node name="checkmark" type="CheckBox" parent="experimentalMicLoudness"]
offset_left = 130.0
offset_top = 1.0
offset_right = 154.0
offset_bottom = 25.0
metadata/_edit_use_anchors_ = true
[node name="AntiAliasing" type="Node2D" parent="."]
position = Vector2(16, 401)
position = Vector2(16, 405)
[node name="Label" type="Label" parent="AntiAliasing"]
offset_right = 138.0
offset_bottom = 34.0
text = "texture filtering"
metadata/_edit_use_anchors_ = true
[node name="CheckBox" type="CheckBox" parent="AntiAliasing"]
offset_left = 130.0
offset_top = 1.0
offset_right = 154.0
offset_bottom = 25.0
metadata/_edit_use_anchors_ = true
[node name="bounceOnCostume" type="Node2D" parent="."]
position = Vector2(17, 305)
position = Vector2(17, 341)
[node name="Label" type="Label" parent="bounceOnCostume"]
offset_right = 138.0
offset_bottom = 34.0
text = "costume bounce"
metadata/_edit_use_anchors_ = true
[node name="costumeCheck" type="CheckBox" parent="bounceOnCostume"]
offset_left = 130.0
offset_top = 1.0
offset_right = 154.0
offset_bottom = 25.0
metadata/_edit_use_anchors_ = true
[node name="BounceForce" type="Node2D" parent="."]
position = Vector2(22, 115)
@@ -14853,7 +14832,7 @@ offset_bottom = -75.0
text = "costume hotkeys"
[node name="BlinkSpeed" type="Node2D" parent="."]
position = Vector2(17, 253)
position = Vector2(17, 283)
[node name="Label" type="Label" parent="BlinkSpeed"]
offset_left = -10.0
@@ -14862,7 +14841,6 @@ offset_right = 158.0
offset_bottom = 27.0
text = "blink speed: 1"
horizontal_alignment = 1
metadata/_edit_use_anchors_ = true
[node name="blinkSpeed" type="HSlider" parent="BlinkSpeed"]
offset_left = -9.0
@@ -14873,10 +14851,9 @@ theme = SubResource("Theme_e6tc4")
max_value = 20.0
value = 1.0
scrollable = false
metadata/_edit_use_anchors_ = true
[node name="BlinkChance" type="Node2D" parent="."]
position = Vector2(17, 205)
position = Vector2(17, 235)
[node name="Label" type="Label" parent="BlinkChance"]
offset_left = -10.0
@@ -14885,7 +14862,6 @@ offset_right = 160.0
offset_bottom = 27.0
text = "blink chance: 1 in 200"
horizontal_alignment = 1
metadata/_edit_use_anchors_ = true
[node name="blinkChance" type="HSlider" parent="BlinkChance"]
offset_left = -8.0
@@ -14897,7 +14873,6 @@ min_value = 1.0
max_value = 300.0
value = 200.0
scrollable = false
metadata/_edit_use_anchors_ = true
[connection signal="pressed" from="Background/Transparent/Button" to="." method="_on_button_pressed"]
[connection signal="pressed" from="Background/Green/greenButton" to="." method="_on_green_button_pressed"]
@@ -14908,7 +14883,6 @@ metadata/_edit_use_anchors_ = true
[connection signal="value_changed" from="MaxFPS/fpsDrag" to="." method="_on_fps_drag_value_changed"]
[connection signal="pressed" from="MaxFPS/confirm" to="." method="_on_confirm_pressed"]
[connection signal="toggled" from="StreamDeck/streamDeckCheck" to="." method="_on_stream_deck_check_toggled"]
[connection signal="toggled" from="experimentalMicLoudness/checkmark" to="." method="_on_experimental_mic_loudness_toggle"]
[connection signal="toggled" from="AntiAliasing/CheckBox" to="." method="_on_check_box_toggled"]
[connection signal="toggled" from="bounceOnCostume/costumeCheck" to="." method="_on_costume_check_toggled"]
[connection signal="value_changed" from="BounceForce/bounceForce" to="." method="_on_bounce_force_value_changed"]