Default sound pack extended.

This commit is contained in:
Storm Dragon
2025-12-29 18:01:47 -05:00
parent 1e3db9c894
commit b818b685bd
6 changed files with 102 additions and 4 deletions

BIN
sounds/default/start.wav Normal file

Binary file not shown.

BIN
sounds/default/stop.wav Normal file

Binary file not shown.

View File

@@ -219,6 +219,7 @@ from . import settings
from . import settings_manager
from . import speech
from . import sound
from . import sound_theme_manager
from . import mouse_review
from .ax_object import AXObject
from .ax_utilities import AXUtilities
@@ -871,6 +872,7 @@ def shutdown(script=None, inputEvent=None):
cthulhu_state.activeScript.presentationInterrupt()
cthulhuApp.getSignalManager().emitSignal('stop-application-completed')
sound_theme_manager.getManager().playStopSound(wait=True)
cthulhuApp.getPluginSystemManager().unloadAllPlugins(ForceAllPlugins=True)
# Deactivate the event manager first so that it clears its queue and will not
@@ -993,6 +995,7 @@ def main():
debug.printMessage(debug.LEVEL_INFO, "CTHULHU: Initialized.", True)
script = cthulhu_state.activeScript
sound_theme_manager.getManager().playStartSound(wait=True)
cthulhuApp.getSignalManager().emitSignal('start-application-completed')
if script:
window = script.utilities.activeWindow()

View File

@@ -93,6 +93,36 @@ class Player:
self._player.set_property('uri', f'file://{icon.path}')
self._player.set_state(Gst.State.PLAYING)
def _playIconAndWait(self, icon, interrupt=True, timeout_seconds=10):
"""Plays a sound icon and waits for completion."""
if interrupt:
self._player.set_state(Gst.State.NULL)
self._player.set_property('uri', f'file://{icon.path}')
self._player.set_state(Gst.State.PLAYING)
bus = self._player.get_bus()
if not bus:
return False
if timeout_seconds is None:
timeout_ns = Gst.CLOCK_TIME_NONE
else:
timeout_ns = int(timeout_seconds * Gst.SECOND)
message = bus.timed_pop_filtered(
timeout_ns,
Gst.MessageType.EOS | Gst.MessageType.ERROR
)
if message and message.type == Gst.MessageType.ERROR:
error, info = message.parse_error()
msg = f'SOUND ERROR: {error}'
debug.printMessage(debug.LEVEL_INFO, msg, True)
self._player.set_state(Gst.State.NULL)
return message is not None and message.type == Gst.MessageType.EOS
def _playTone(self, tone, interrupt=True):
"""Plays a tone, interrupting the current play first unless specified."""
@@ -152,6 +182,25 @@ class Player:
tokens = ["SOUND ERROR:", item, "is not an Icon or Tone"]
debug.printTokens(debug.LEVEL_INFO, tokens, True)
def playAndWait(self, item, interrupt=True, timeout_seconds=10):
"""Plays a sound and blocks until completion or timeout."""
if not self._player:
if _gstreamerAvailable and not self._initialized:
self.init()
if not self._player:
return False
if isinstance(item, Icon):
return self._playIconAndWait(
item,
interrupt=interrupt,
timeout_seconds=timeout_seconds
)
self.play(item, interrupt)
return False
def stop(self, element=None):
"""Stops play."""

View File

@@ -47,9 +47,11 @@ from .sound_generator import Icon
_settingsManager = settings_manager.getManager()
# Sound event constants - add new events here for easy extensibility
SOUND_FOCUS_MODE = "editbox"
SOUND_FOCUS_MODE = "focus_mode"
SOUND_BROWSE_MODE = "browse_mode"
SOUND_BUTTON = "button"
SOUND_START = "start"
SOUND_STOP = "stop"
# Special theme name for no sounds
THEME_NONE = "none"
@@ -144,17 +146,23 @@ class SoundThemeManager:
return None
def playSound(self, soundName, interrupt=True):
"""Play a sound from the current theme if enabled.
def _playThemeSound(self, soundName, interrupt=True, wait=False,
requireModeChangeSetting=False, requireSoundSetting=False):
"""Play a themed sound with optional gating and blocking.
Args:
soundName: The name of the sound file (without extension)
interrupt: Whether to interrupt currently playing sounds
wait: Whether to block until the sound finishes playing
requireModeChangeSetting: Honor enableModeChangeSound setting
requireSoundSetting: Honor enableSound setting
Returns:
True if sound was played, False otherwise
"""
if not _settingsManager.getSetting('enableModeChangeSound'):
if requireModeChangeSetting and not _settingsManager.getSetting('enableModeChangeSound'):
return False
if requireSoundSetting and not _settingsManager.getSetting('enableSound'):
return False
themeName = _settingsManager.getSetting('soundTheme')
@@ -175,6 +183,8 @@ class SoundThemeManager:
icon = Icon(os.path.dirname(soundPath), os.path.basename(soundPath))
if icon.isValid():
player = sound.getPlayer()
if wait and hasattr(player, "playAndWait"):
return player.playAndWait(icon, interrupt=interrupt)
player.play(icon, interrupt=interrupt)
return True
except Exception as e:
@@ -183,6 +193,24 @@ class SoundThemeManager:
return False
def playSound(self, soundName, interrupt=True, wait=False):
"""Play a sound from the current theme if enabled.
Args:
soundName: The name of the sound file (without extension)
interrupt: Whether to interrupt currently playing sounds
wait: Whether to block until the sound finishes playing
Returns:
True if sound was played, False otherwise
"""
return self._playThemeSound(
soundName,
interrupt=interrupt,
wait=wait,
requireModeChangeSetting=True
)
def playFocusModeSound(self):
"""Play sound for entering focus mode."""
return self.playSound(SOUND_FOCUS_MODE)
@@ -195,6 +223,24 @@ class SoundThemeManager:
"""Play sound for button focus (future use)."""
return self.playSound(SOUND_BUTTON)
def playStartSound(self, wait=False):
"""Play sound for application startup."""
return self._playThemeSound(
SOUND_START,
interrupt=True,
wait=wait,
requireSoundSetting=True
)
def playStopSound(self, wait=False):
"""Play sound for application shutdown."""
return self._playThemeSound(
SOUND_STOP,
interrupt=True,
wait=wait,
requireSoundSetting=True
)
_manager = None