Compare commits

...

10 Commits

Author SHA1 Message Date
Storm Dragon
401b2a8527 Merged with latest code from master. 2024-07-27 21:41:46 -04:00
Storm Dragon
428a48678d Power bars are now visual as well as audio. 2024-07-22 22:20:15 -04:00
Storm Dragon
9a6d6374f9 Added text along with the speak command. Updated window size. 2024-07-22 16:08:42 -04:00
Storm Dragon
df386cbbd9 Improved check_for_exit function. 2024-07-16 14:22:36 -04:00
Storm Dragon
38522aee78 First pass at creating requirements files. 2024-07-15 19:52:02 -04:00
Storm Dragon
0e9c52f5e1 A few fixes for x-powerbar, more accurately exiting when exit keys are pressed, etc. 2024-07-14 03:51:35 -04:00
Storm Dragon
fabf48ff42 Powerbars added and working. 2024-07-13 03:03:32 -04:00
Storm Dragon
0c73e98876 Improved sound while walking on left/right only. 2024-07-05 17:24:23 -04:00
Storm Dragon
0ef11785ec Revert "A new try at sound panning with walking."
This reverts commit 58ab5aa854.
2024-07-05 17:04:12 -04:00
Storm Dragon
58ab5aa854 A new try at sound panning with walking. 2024-06-20 02:03:06 -04:00
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