Pygame 1.9.2 for Python 3.4: Simulating 3D sound - pygame

Long story short:
I followed this tutorial here and the documentation of pygame 1.9.2. According to both it should be possible to adjust the volume for the left and right earphone in stereo mode, however, it doesn't work for me at all. Sounds remain audible on either earphone, no matter what I try.
Here my tries:
Short foreword: I use
pygame.mixer.pre_init(44100, -16, 2, 3072)
This should actually do the job for my system to enable stereo sound in pygame, but maybe I made a mistake here already which I have overlooked, so I mention it.
Try 1:
class MainProgram(object):
def __init__(self, width = 640, height = 640):
# Much Code
# Sounds
self.testsound = pygame.mixer.Sound("data\\sounds\\ping.ogg")
self.chan1 = pygame.mixer.Channel(0)
self.chan2 = pygame.mixer.Channel(1)
self.mainloop()
def mainloop(self):
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminate()
if event.type == pygame.KEYDOWN:
self.testsound.play().set_volume(1.0, 0.0)
elif event.type == pygame.KEYUP:
self.testsound.play().set_volume(0.0, 1.0)
# More code here
When I don't adjust the volume, I can hear the sounds at full volume on either side. If I adjust as seen above, then I still hear the sound on either side, just only half the volume (rough estimation by me, but clearly quieter), it's like, instead of setting one side to 0 and the other to 1, pygame just takes the average (0.5) for either side.
Try 2:
class MainProgram(object):
def __init__(self, width = 640, height = 640):
# Much Code
# Sounds
self.testsound = pygame.mixer.Sound("data\\sounds\\ping.ogg")
self.chan1 = pygame.mixer.Channel(0).set_volume(1.0, 0.0)
self.chan2 = pygame.mixer.Channel(1).set_volume(0.0, 1.0)
self.mainloop()
def mainloop(self):
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminate()
if event.type == pygame.KEYDOWN:
self.chan1.play(self.testsound)
elif event.type == pygame.KEYUP:
self.chan2.play(self.testsound)
# More code here
This has no effect whatsoever. All sounds are played at full volume.
Try 3:
class MainProgram(object):
def __init__(self, width = 640, height = 640):
# Much Code
# Sounds
self.testsound = pygame.mixer.Sound("data\\sounds\\ping.ogg")
self.chan1 = pygame.mixer.Channel(0)
self.chan2 = pygame.mixer.Channel(1)
self.mainloop()
def mainloop(self):
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminate()
if event.type == pygame.KEYDOWN:
self.testsound.play()
self.testsound.set_volume(1.0, 0.0)
elif event.type == pygame.KEYUP:
self.testsound.play()
self.testsound.set_volume(0.0, 1.0)
# More code here
As you can see I just separated try 2's approach into two lines in try 3. Interestingly there's a new issue: I get a
TypeError: function takes exactly 1 argument (2 given)
Which makes everything all the more interesting as using the very same in one line triggers no error, though it doesn't do what it's supposed to.
As I have seen, my program is in stereo mode and according to the tutorials (I've checked others beside) and the documentation either of the two first approaches should yield the result I expected - but neither does...
I'm really grateful for any hints and tips in this issue. It's really important that I can add some kind of 3D-sound. If there's really an issue with pygame itself and not my code, do you know any other module I could use to achieve this effect - best would be if compatible to Pygame, then I don't have to rewrite everything (but I'd do that if necessary...).
Thank you in advance!
Pat
//Short comment by me:
I just saw that Try 2 is doomed anyway, the doc says, that all volumes are reset when the channel is used again, so I can't just set a fixed volume once and for all...

As the documentation states: set_volume takes one parameter, the volume as a float between 0.0 and 1.0.
This means:
ad "Try 1": this is syntactically wrong. play() doesn't return anything so a chained call with set_volume() is just wrong.
ad "Try 2": This actually might work if you set up your channels like this:
self.chan1 = pygame.mixer.Channel(0)
self.chan1.set_volume(1.0, 0.0)
self.chan2 = pygame.mixer.Channel(1)
self.chan2.set_volume(0.0, 1.0)
ad "Try 3: on the Sounds set_volume only one value is allowed.

This is the final version which worked for me. I used Try 2 from my question with a few tweaks. After all, pygame seems not to be that useful when it comes to sounds... Anyway, my code is an experiment to try out a few things, so that just works this way for now - and for the final project I'll anyway write my own sound module as I have a few requirements none covers yet ;)
Here's the final code, a modified version of my Try 2 from the question:
class MainProgram(object):
def __init__(self, width = 640, height = 640):
# Much Code
# Sounds
self.sound = pygame.mixer.Sound("data\\sounds\\ping.ogg")
self.chan1 = pygame.mixer.Channel(0)
self.chan1.set_volume(1.0, 0.0)
self.chan2 = pygame.mixer.Channel(1)
self.chan2.set_volume(0.0, 1.0)
self.mainloop()
def mainloop(self):
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
terminate()
if event.type == pygame.KEYDOWN:
self.chan1.play(self.sound)
# IMPORTANT NOTE! We have to reset the volume after each time
# a sound was played by the respective channel!
self.chan1.set_volume(1.0, 0.0)
elif event.type == pygame.KEYUP:
self.chan2.play(self.sound)
self.chan2.set_volume(0.0, 1.0)
# More code here

Related

Why does the pygame window become unresponsive?

In a 'Pygame loop', I'm trying to ask for user input but when I run the program, the pygame window becomes unresponsive if I hover my mouse over it or click anywhere. Does anyone know what's going wrong?
import pygame
win = pygame.display.set_mode((600, 600))
win.fill((240, 240, 240)) #white
pygame.display.update()
#Game loop
quit = False
while quit == False:
for e in pygame.event.get():
if e.type == pygame.QUIT:
exit()
u_input = input("Enter 'q' to quit or 'n' to fill the window with navy: ")
if u_input == 'q':
quit = True
elif u_input == 'n':
win.fill((60, 55, 100)) #navy
pygame.display.update()
Unresponsive pygame window image
The IDE I'm using is Visual Studio Code
Unfortunately, this is just a nature of pygame. When you ask for an input, the program stops and waits for the user to input something, preventing the pygame.diplay.flip() from occuring.
There is two ways I can think of to fix this. Using, threads, one for powershell (terminal) and one for pygame should work, however I'm not familiar with that at all, so you would need to research for yourself.
A different solution is to listen for user input instead of using terminal prompts
#Game loop
quit = False
while quit == False:
for e in pygame.event.get():
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_q:
quit = True
if e.key == pygame.K_n:
win.fill((60, 55, 100)) #navy
pygame.display.update()

program only works half the time

for some reason this python program i saw on a youtube tutorial only works sometimes. Whenever i run the code, i get an error in the program telling me the program doesnt answer. But once in a while the code suddenly works perfectly.
import pygame, sys
from sys import exit
# crosshair class
class Crosshair(pygame.sprite.Sprite):
def __init__(self, picture_path):
super().__init__()
self.image = pygame.image.load(picture_path)
self.rect = self.image.get_rect()
def update(self):
self.rect.center = pygame.mouse.get_pos()
# general setup
pygame.init()
clock = pygame.time.Clock()
# create the screen
screen = pygame.display.set_mode((800,400))
pygame.display.set_caption('Runner')
background = pygame.image.load("sprites/graphics/bg.png")
background = pygame.transform.scale(background, (800, 400))
pygame.mouse.set_visible(False)
#crosshair
crosshair = Crosshair('sprites/graphics/crosshair.png')
crosshair_group = pygame.sprite.Group()
crosshair_group.add(crosshair)
# while loop
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit
exit()
screen.blit(background,(0,0))
crosshair_group.draw(screen)
crosshair_group.update()
clock.tick(60)
pygame.display.update()
You need to do pygame.quit() instead of pygame.quit. Missing the parentheses means that you are not actually calling the function, and the window never closes.
You are getting a not responding message when you attempt to X out the window because exit() is being called, which ends your program, including the event-handling loop. The window is left with no program controlling it or making it respond to inputs such as closing, so you get that message.
Calling the pygame.quit() function will close the window right before the program quits, so it is all taken care of.

Time delays on sprites [duplicate]

While I've been using time.wait in my code since I began learning Python and Pygame, I've been wondering if there are any other ways to do it and what are the advantages and disadvantages of each approach. For example, Pygame also has a pygame.time.wait. What's the difference between python's wait and pygame's wait functions? Which one is better? And are there other ways to wait some time besides using these two functions?
For animation / cooldowns, etc: If you want to 'wait', but still have code running you use: pygame.time.get_ticks
class Unit():
def __init__(self):
self.last = pygame.time.get_ticks()
self.cooldown = 300
def fire(self):
# fire gun, only if cooldown has been 0.3 seconds since last
now = pygame.time.get_ticks()
if now - self.last >= self.cooldown:
self.last = now
spawn_bullet()
For Python in general, you will want to look at the sleep library.
For Pygame, however, using pygame.time.delay() will pause for a given number of milliseconds based on the CPU clock for more accuracy (as opposed to pygame.time.wait).
If you just wait for some time, you can use pygame.time.wait or pygame.time.delay. However, if you want to display a message and then wait some time, you need to update the display beforehand. The display is updated only if either pygame.display.update() or pygame.display.flip()
is called. See pygame.display.flip():
This will update the contents of the entire display.
Further you've to handles the events with pygame.event.pump(), before the update of the display becomes visible in the window. See pygame.event.pump():
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
This all means that you have to call pygame.display.flip() and pygame.event.pump() before pygame.time.delay() or pygame.time.wait():
screen.blit(text, (x, y))
pygame.display.flip()
pygame.event.pump()
pygame.time.delay(delay * 1000) # 1 second == 1000 milliseconds
See also Why doesn't PyGame draw in the window before the delay or sleep?
In any case, this is not the way to wait or delay something in a typical application. The game does not respond while you wait. Use pygame.time.get_ticks() to measure the time.
For instance if you want to show a message on the display, get the current time and calculate the point in time after that the message has to disappear. Display the message as long as the current time is below the calculated time:
message_end_time = 0
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
# [...]
current_time = pygame.time.get_ticks()
if something_has_happened:
message_surf = font.render('Important message!', True, (255, 0, 0))
message_end_time = pygame.time.get_ticks() + 3000 # display for 3 seconds
window.fill(0)
# [...]
if current_time < message_end_time:
window.blit(message_surf, (x, y))
pygame.display.flip()
See also How do I stop more than 1 bullet firing at once?
Minimal example: repl.it/#Rabbid76/PyGame-MessageDelay
import pygame
pygame.init()
font = pygame.font.SysFont(None, 50)
text = font.render('press key or mouse', True, (255, 0, 0))
window = pygame.display.set_mode((500, 200))
clock = pygame.time.Clock()
message_end_time = pygame.time.get_ticks() + 3000
run = True
while run:
clock.tick(60)
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
text = font.render(pygame.key.name(event.key) + ' pressed', True, (255, 0, 0))
message_end_time = pygame.time.get_ticks() + 2000
if event.type == pygame.MOUSEBUTTONDOWN:
text = font.render('button ' + str(event.button) + ' pressed', True, (255, 0, 0))
message_end_time = pygame.time.get_ticks() + 2000
window.fill(0)
if current_time < message_end_time:
window.blit(text, text.get_rect(center = window.get_rect().center))
pygame.display.flip()
pygame.quit()
exit()

I keep getting this error: Couldn't open "File"

I'm trying to display an image using pygame, but I get this error:
Traceback (most recent call last):
File "H:/profile/desktop/untitled/venv/Scripts/AhjaiyGame.py", line 28, in
start = pygame.image.load(os.path.join(folder, "wecvguh.png"))
pygame.error: Couldn't open H:\profile\desktop\untitled\venv\Scripts\wecvguh.png
Code block:
import sys
import random
import os
import subprocess
import pygame
pygame.init()
GUI = pygame.display.set_mode((800,600))
pygame.display.set_caption("The incredible guessing game")
x = 284
y = 250
width = 68
length = 250
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run =False
if event.type == pygame.KEYDOWN:
command = "python AhjaiyCPT.py"
subprocess.call(command)
pygame.display.update()
folder = os.path.dirname(os.path.realpath(__file__))
start = pygame.image.load(os.path.join(folder, "wecvguh.png"))
def img(x,y):
gameDisplay.blit(start, (x,y))
while run:
gameDisplay.fill(white)
start(x, y)
pygame.quit()
The code has two "run" loops, so it never gets to the second loop.
The code's indentation is confused - maybe from a paste into SO?. The overwhelming majority of programmers use 4 spaces to indent. This is probably a good custom to follow.
The code also loaded the "start" image every loop iteration, this is unnecessary (unless it changes on disk, in this case use os.stat() to check for changes before re-loading it).
Re-worked main loop:
folder = os.path.dirname(os.path.realpath(__file__))
start = pygame.image.load(os.path.join(folder, "wecvguh.png"))
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
command = "python AhjaiyCPT.py"
subprocess.call(command)
gameDisplay.fill(white)
gameDisplay.blit(start, (x,y))
pygame.display.update()
pygame.quit()

Need assistance with Pygame and creating windows

For some reason, when I use the code below:
import pygame, sys
pygame.init()
def create_window():
global window, window_height, window_width, window_title
window_width, window_height = 1280, 720
window_title = "The Adventure of Nate"
pygame.display.set.caption(window_title)
window = pygame.display.set_mode((window_width, window_height, pygame.HWSURFACE|pygame.DOUBLEBUFF)
create_window()
isrunning = True
while isrunning == True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
isRunning = False
window.fill(0, 0, 0)
pygame.display.update()
pygame.quit()
sys.exit()
I get the following error:
C:\Python3.6\python.exe "C:/Users/home/PycharmProjects/Basic RPG/Base
Game.py" File "C:/Users/home/PycharmProjects/Basic RPG/Base
Game.py", line 16
create_window()
^ SyntaxError: invalid syntax
Process finished with exit code 1
If anyone is experienced in the matter, can they help me correct my code.
(P.S: This is my first time coding without assistance, so sorry if my code is all over the place XD)
You're missing a ) in the line window = pygame.display.set_mode((window_width, window_height, pygame.HWSURFACE|pygame.DOUBLEBUFF)
It should be window = pygame.display.set_mode((window_width, window_height), pygame.HWSURFACE|pygame.DOUBLEBUFF)