Merged with latest code from master.

This commit is contained in:
Storm Dragon 2024-07-27 21:41:46 -04:00
commit 401b2a8527
3 changed files with 192 additions and 52 deletions

View File

@ -10,11 +10,12 @@ from os.path import isfile, join
from inspect import isfunction from inspect import isfunction
from xdg import BaseDirectory from xdg import BaseDirectory
from setproctitle import setproctitle from setproctitle import setproctitle
import pyglet import pygame
import pyperclip import pyperclip
import random import random
import re import re
import requests import requests
import textwrap
import webbrowser import webbrowser
# Global variable for speech provider # Global variable for speech provider
try: try:
@ -29,6 +30,7 @@ except ImportError:
print("No other speech providers found.") print("No other speech providers found.")
exit() exit()
import math import math
import numpy as np
import time import time
localConfig = configparser.ConfigParser() localConfig = configparser.ConfigParser()
@ -114,19 +116,45 @@ def read_config(readGlobal = False):
except: except:
pass pass
def speak(text, interupt = True): def speak(text, interupt=True):
if speechProvider == "speechd": if speechProvider == "speechd":
if interupt == True: spd.cancel() if interupt: spd.cancel()
spd.say(text) spd.say(text)
else: else:
if speechProvider == "accessible_output2": if speechProvider == "accessible_output2":
s.speak(text, interrupt=True) s.speak(text, interrupt=True)
# Display the text on screen
screen = pygame.display.get_surface()
font = pygame.font.Font(None, 36)
# Wrap the text
maxWidth = screen.get_width() - 40 # Leave a 20-pixel margin on each side
wrappedText = textwrap.wrap(text, width=maxWidth // font.size('A')[0])
# Render each line
textSurfaces = [font.render(line, True, (255, 255, 255)) for line in wrappedText]
screen.fill((0, 0, 0)) # Clear screen with black
# Calculate total height of text block
totalHeight = sum(surface.get_height() for surface in textSurfaces)
# Start y-position (centered vertically)
currentY = (screen.get_height() - totalHeight) // 2
# Blit each line of text
for surface in textSurfaces:
textRect = surface.get_rect(center=(screen.get_width() // 2, currentY + surface.get_height() // 2))
screen.blit(surface, textRect)
currentY += surface.get_height()
pygame.display.flip()
def check_for_exit():
for event in pygame.event.get():
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
return True
return False
pygame.event.pump()
def exit_game(): def exit_game():
if speechProvider == "speechd": spd.close() if speechProvider == "speechd": spd.close()
# Close the pyglet window pygame.mixer.music.stop()
pyglet.app.exit() pygame.quit()
exit()
def initialize_gui(gameTitle): def initialize_gui(gameTitle):
# Check for, and possibly create, storm-games path # Check for, and possibly create, storm-games path
@ -141,18 +169,101 @@ def initialize_gui(gameTitle):
global gameName global gameName
gameName = gameTitle gameName = gameTitle
setproctitle(str.lower(str.replace(gameTitle, " ", ""))) setproctitle(str.lower(str.replace(gameTitle, " ", "")))
# init pyglet window # start pygame
window = pyglet.window.Window(500, 300, gameTitle) pygame.init()
# start the display (required by the event loop)
pygame.display.set_mode((800, 600))
pygame.display.set_caption(gameTitle)
# Set 32 channels for sound by default
pygame.mixer.pre_init(44100, -16, 2, 1024)
pygame.mixer.init()
pygame.mixer.set_num_channels(32)
# Reserve the cut scene channel
pygame.mixer.set_reserved(0)
# Load sounds from the sound directory and creates a list like {'bottle': 'bottle.ogg'} # Load sounds from the sound directory and creates a list like {'bottle': 'bottle.ogg'}
try:
soundFiles = [f for f in listdir("sounds/") if isfile(join("sounds/", f)) and (f.split('.')[1].lower() in ["ogg","wav"])] soundFiles = [f for f in listdir("sounds/") if isfile(join("sounds/", f)) and (f.split('.')[1].lower() in ["ogg","wav"])]
# make a dict with pyglet media {'bottle':<soundobject>} except Exception as e:
print("No sounds found.")
speak("No sounds found.", False)
#lets make a dict with pygame.mixer.Sound() objects {'bottle':<soundobject>}
soundData = {} soundData = {}
for f in soundFiles: for f in soundFiles:
soundData[f.split('.')[0]] = pyglet.media.load("sounds/" + f, streaming = False) soundData[f.split('.')[0]] = pygame.mixer.Sound("sounds/" + f)
soundData['game-intro'].play() soundData['game-intro'].play()
time.sleep(soundData['game-intro'].duration) time.sleep(soundData['game-intro'].get_length())
return soundData return soundData
def generate_tone(frequency, duration=0.1, sample_rate=44100, volume = 0.2):
t = np.linspace(0, duration, int(sample_rate * duration), False)
tone = np.sin(2 * np.pi * frequency * t)
stereo_tone = np.vstack((tone, tone)).T # Create a 2D array for stereo
stereo_tone = (stereo_tone * 32767).astype(np.int16)
stereo_tone = (stereo_tone * 32767 * volume).astype(np.int16) # Apply volume
stereo_tone = np.ascontiguousarray(stereo_tone) # Ensure C-contiguous array
return pygame.sndarray.make_sound(stereo_tone)
def x_powerbar():
clock = pygame.time.Clock()
screen = pygame.display.get_surface()
position = -50 # Start from the leftmost position
direction = 1 # Move right initially
barHeight = 20
while True:
frequency = 440 # A4 note
leftVolume = (50 - position) / 100
rightVolume = (position + 50) / 100
tone = generate_tone(frequency)
channel = tone.play()
channel.set_volume(leftVolume, rightVolume)
# Visual representation
screen.fill((0, 0, 0))
barWidth = screen.get_width() - 40 # Leave 20px margin on each side
pygame.draw.rect(screen, (100, 100, 100), (20, screen.get_height() // 2 - barHeight // 2, barWidth, barHeight))
markerPos = int(20 + (position + 50) / 100 * barWidth)
pygame.draw.rect(screen, (255, 0, 0), (markerPos - 5, screen.get_height() // 2 - barHeight, 10, barHeight * 2))
pygame.display.flip()
for event in pygame.event.get():
check_for_exit()
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
channel.stop()
return position # This will return a value between -50 and 50
position += direction
if position > 50:
position = 50
direction = -1
elif position < -50:
position = -50
direction = 1
clock.tick(40) # Speed of bar
def y_powerbar():
clock = pygame.time.Clock()
screen = pygame.display.get_surface()
power = 0
direction = 1 # 1 for increasing, -1 for decreasing
barWidth = 20
while True:
frequency = 220 + (power * 5) # Adjust these values to change the pitch range
tone = generate_tone(frequency)
channel = tone.play()
# Visual representation
screen.fill((0, 0, 0))
barHeight = screen.get_height() - 40 # Leave 20px margin on top and bottom
pygame.draw.rect(screen, (100, 100, 100), (screen.get_width() // 2 - barWidth // 2, 20, barWidth, barHeight))
markerPos = int(20 + (100 - power) / 100 * barHeight)
pygame.draw.rect(screen, (255, 0, 0), (screen.get_width() // 2 - barWidth, markerPos - 5, barWidth * 2, 10))
pygame.display.flip()
for event in pygame.event.get():
check_for_exit()
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
channel.stop()
return power
power += direction
if power >= 100 or power <= 0:
direction *= -1 # Reverse direction at limits
clock.tick(40)
def cut_scene(sounds, soundName): def cut_scene(sounds, soundName):
pygame.event.clear() pygame.event.clear()
pygame.mixer.stop() pygame.mixer.stop()
@ -164,45 +275,46 @@ def cut_scene(sounds, soundName):
pygame.mixer.stop() pygame.mixer.stop()
pygame.event.pump() pygame.event.pump()
def obj_play(sounds, soundName, playerPos, objPos):
distance = playerPos - objPos
if distance > 9 or distance < -9:
# The item is out of range, so play it at 0
left = 0
right = 0
elif distance == 0:
left = 0.9
right = 0.9
else:
angle = math.radians(distance * 5)
left = math.sqrt(2)/2.0 * (math.cos(angle) + math.sin(angle))
right = math.sqrt(2)/2.0 * (math.cos(angle) - math.sin(angle))
if left < 0: left *= -1
if right < 0: right *= -1
# x is the channel for the sound
x = sounds[soundName].play(-1)
# Apply the position information to the channel
x.set_volume(left, right)
# return the channel so that it can be used in the update and stop functions.
return x
def obj_update(x, playerPos, objPos): def calculate_volume_and_pan(player_pos, obj_pos):
distance = playerPos - objPos distance = abs(player_pos - obj_pos)
if distance > 9 or distance < -9: max_distance = 12 # Maximum audible distance
left = 0 if distance > max_distance:
right = 0 return 0, 0, 0 # No sound if out of range
elif distance == 0: # Calculate volume (non-linear scaling for more noticeable changes)
left = 0.9 volume = ((max_distance - distance) / max_distance) ** 1.5
right = 0.9 # Determine left/right based on relative position
if player_pos < obj_pos:
# Object is to the right
left = max(0, 1 - (obj_pos - player_pos) / max_distance)
right = 1
elif player_pos > obj_pos:
# Object is to the left
left = 1
right = max(0, 1 - (player_pos - obj_pos) / max_distance)
else: else:
angle = math.radians(distance * 5) # Player is on the object
left = math.sqrt(2)/2.0 * (math.cos(angle) + math.sin(angle)) left = right = 1
right = math.sqrt(2)/2.0 * (math.cos(angle) - math.sin(angle)) return volume, left, right
if left < 0: left *= -1
if right < 0: right *= -1 def obj_play(sounds, soundName, player_pos, obj_pos):
# Apply the position information to the channel volume, left, right = calculate_volume_and_pan(player_pos, obj_pos)
x.set_volume(left, right) if volume == 0:
# return the channel return None # Don't play if out of range
# Play the sound on a new channel
x = sounds[soundName].play(-1)
# Apply the volume and pan
x.set_volume(volume * left, volume * right)
return x
def obj_update(x, player_pos, obj_pos):
if x is None:
return None
volume, left, right = calculate_volume_and_pan(player_pos, obj_pos)
if volume == 0:
x.stop()
return None
# Apply the volume and pan
x.set_volume(volume * left, volume * right)
return x return x
def obj_stop(x): def obj_stop(x):
@ -213,6 +325,22 @@ def obj_stop(x):
except: except:
return x return x
def play_ambiance(sounds, soundNames, probability, randomLocation = False):
# Check if any of the sounds in the list is already playing
for soundName in soundNames:
if pygame.mixer.find_channel(True) and pygame.mixer.find_channel(True).get_busy():
return
if random.randint(1, 100) > probability:
return
# Choose a random sound from the list
ambianceSound = random.choice(soundNames)
channel = sounds[ambianceSound].play()
if randomLocation and channel:
left_volume = random.random()
right_volume = random.random()
channel.set_volume(left_volume, right_volume)
return channel # Return the channel object for potential further manipulation
def play_random(sounds, soundName, pause = False, interrupt = False): def play_random(sounds, soundName, pause = False, interrupt = False):
key = [] key = []
for i in sounds.keys(): for i in sounds.keys():
@ -282,7 +410,7 @@ def learn_sounds(sounds):
loop = True loop = True
pygame.mixer.music.pause() pygame.mixer.music.pause()
i = 0 i = 0
soundFiles = [f for f in listdir("sounds/") if isfile(join("sounds/", f)) and (f.split('.')[1].lower() in ["ogg","wav"]) and (f.split('.')[0].lower() not in ["game-intro", "music_menu"])] soundFiles = [f for f in listdir("sounds/") if isfile(join("sounds/", f)) and (f.split('.')[1].lower() in ["ogg","wav"]) and (f.split('.')[0].lower() not in ["game-intro", "music_menu"]) and (not f.lower().startswith("_"))]
# j keeps track of last spoken index so it isn't voiced on key up. # j keeps track of last spoken index so it isn't voiced on key up.
j = -1 j = -1
while loop == True: while loop == True:
@ -317,9 +445,12 @@ def game_menu(sounds, *options):
if pygame.mixer.music.get_busy(): if pygame.mixer.music.get_busy():
pygame.mixer.music.unpause() pygame.mixer.music.unpause()
else: else:
try:
pygame.mixer.music.load("sounds/music_menu.ogg") pygame.mixer.music.load("sounds/music_menu.ogg")
pygame.mixer.music.set_volume(0.75) pygame.mixer.music.set_volume(0.75)
pygame.mixer.music.play(-1) pygame.mixer.music.play(-1)
except:
pass
i = 0 i = 0
# j keeps track of last spoken index so it isn't voiced on key up. # j keeps track of last spoken index so it isn't voiced on key up.
j = -1 j = -1

7
requirements.txt Normal file
View File

@ -0,0 +1,7 @@
pygame>=2.0.0
pyperclip>=1.8.0
requests>=2.25.0
pyxdg>=0.27
setproctitle>=1.2.0
numpy>=1.19.0
accessible-output2>=0.14

2
requirements_linux.txt Normal file
View File

@ -0,0 +1,2 @@
-r requirements.txt
python-speechd>=0.11.1