2024-01-27 07:17:42

@150
Found it, the problem is your declaring your sound players before the listener. The listener has to be initialized first to setup a context before you can load players and such.

class SoundManager:
    def __init__(self, sounds_directory="sounds", max_players=10):
        self.sounds_directory = sounds_directory
        self.sounds = {}
        self.listener = Listener()
        self.player_pool = [Player(), Player()]
        self.active_players = []
-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2024-01-27 08:38:09

thank you. I didn't see that. I've been trying to implement that for three days, but I can't really see what's going wrong. haha!

2024-01-28 00:25:33

Boop: For future reference, i've added an error check in the player class. If the player source fails to initialize for whatever reason, such as there being no current context/listener, it will now print out "Error: Player Generation Failure".

Something that might also throw people though is having multiple contexts/listeners. Yes, you can have multiple listeners/contexts, but the trick is that when you call a player class, like: p = Player(), that player is bound to the currently active context. So if you switch to a different context, any player created with the first context won't play until you switch back to the first context, and vice versa. So, if you plan on using multiple contexts/listeners, be sure to keep track of which players belong to which context.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2024-01-29 09:34:44

is the first post always up to date? It seems like it was made awhile ago and I"m trying to use openAL, but the version in post 1 can't find the openAL library or something like that whenever I try to run the examples.

2024-01-29 09:42:53

Ah, no it is not. The link in the original post is to a copy on one of my older repo's. The latest version can be found on my github here, it doesn't come with OpenAL Soft itself, but there are links to it on its main page with instructions on how to set it up.

I'll actually update the first post with that information.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2024-01-29 14:35:59

Thank you very much. Was trying to use openAL in a project I was working on, saw your post and tried it with no success. I'll give it another shot and try again now! Thank you.

2024-02-01 06:32:46

Hello, I have a question. In my current implementation, I always recycle the players when I play sounds, but I know that this is not a good approach. I thought that instead of always recycling the players, all I have to do is, when the sound is loaded and played, it will not be deleted and will be used again when the same sound is requested to play.
The problem is, I've run out of ideas on how to implement it. What is my possible approach? In the program I'm trying to create, there are four sounds that I'm sure I'll use all the time.
So, if ever I recycle the players, I can just do it in a timely manner because I won't run out of players since the sounds that will always be played are already loaded.
Then, I'm not sure, but, I can't load those four sounds into a player and then choose what I need to play, right? or if possible to use only one player, may I know how?
Here is what I'm trying to do.

import pyogg
import pygame

from gui import Dialogue, Menu
from openal import *
from speech import speak


class SoundManager:
    def __init__(self, sounds_directory="sounds", max_players=10):
        self.sounds_directory = sounds_directory
        self.sounds = {}
        self.listener = Listener()
        self.player_pool = [Player() for _ in range(max_players)]
        self.active_players = []
        print(f"Initialized SoundManager with {max_players} players.")

    def load_sound(self, sound_name, file_name):
        try:
            file_path = f"{self.sounds_directory}/{file_name}"
            vorbis_file = pyogg.VorbisFile(file_path)
            sound_buffer = BufferSound()
            sound_buffer.channels = vorbis_file.channels
            sound_buffer.bitrate = 16
            sound_buffer.samplerate = vorbis_file.frequency
            sound_buffer.load(vorbis_file.buffer)
            self.sounds[sound_name] = sound_buffer
            print(f"Loaded sound: {sound_name} from {file_path}")

        except Exception as e:
            print(f"Error loading sound {sound_name}: {e}")

    def play_sound(self, sound_name):
        if sound_name not in self.sounds:
            print(f"Sound '{sound_name}' not found.")
            return

        if self.player_pool:
            player = self.player_pool.pop(0)
            player.add(self.sounds[sound_name])
            player.play()
            self.active_players.append(player)
            print(f"Playing sound: {sound_name} with player {player}.")
            self.recycle_players()
        else:
            print("No available players in the pool.")

    def recycle_players(self):
        for player in self.active_players[:]:
            if not player.playing():
                player.remove()
                self.player_pool.append(player)
                self.active_players.remove(player)
                print(f"Recycled player {player}.")
            else:
                print(f"Player {player} still playing, not recycled.")

        print(f"Active players: {len(self.active_players)}, Player pool size: {len(self.player_pool)}")

    def cleanup(self):
        for player in self.active_players + self.player_pool:
            player.delete()
            print(f"Deleted player {player}.")
        for sound_name, sound_buffer in self.sounds.items():
            sound_buffer.delete()
            print(f"Deleted sound buffer for sound {sound_name}.")
        self.listener.delete()
        print("Cleaned up SoundManager resources.")

    def load_game_sounds(self):
        for file_name in os.listdir(self.sounds_directory):
            if file_name.endswith(".ogg"):
                sound_name = os.path.splitext(file_name)[0]
                self.load_sound(sound_name, file_name)


class GameState:
    INITIAL_STATE = "MainMenu"

    def __init__(self):
        self.current_state = self.INITIAL_STATE


class GameManager:
    def __init__(self):
        self.game_state = GameState()
        self.dialogue_instance = Dialogue()
        self.menu_instance = None
        self.sound_manager = SoundManager()
        self.sound_manager.load_game_sounds()


    def main_menu(self):
        text = "Choose an option: "
        items = ["Learn game sounds", "Exit"]
        self.menu_instance = Menu(text, items, False)
        result = self.menu_instance.display()

        if result is False:  # Check if the menu returned False (game exit requested)
            return False
        if result == 0:
            self.game_state.current_state = "LearningSounds"
        elif result == 1:
            self.dialogue_instance.dlg("goodbye! Thanks for trying the game. Play again next time")
            return False

        return True

    def learning_sounds(self):
        speak("Press the arrow keys to find out which key generates which tone.")
        speak("Press escape to stop practicing.")
        running = True

        while running:
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        running = False
                    else:
                        tone_id = self.get_tone_id_from_key(event.key)
                        if tone_id is not None:
                            self.play_tone("tone", tone_id)

        self.game_state.current_state = "MainMenu"

    def get_tone_id_from_key(self, key):
        key_map = {pygame.K_LEFT: 0, pygame.K_DOWN: 1, pygame.K_RIGHT: 2, pygame.K_UP: 3}
        return key_map.get(key)

    def play_tone(self, sound_type, tone_id=None):
        if sound_type == "tone" and tone_id is not None:
            self.sound_manager.play_sound(f"tone{tone_id}")
        elif sound_type == "error":
            self.sound_manager.play_sound("error")
        elif sound_type == "winner":
            self.sound_manager.play_sound("winner")


    def check_state(self):
        if self.game_state.current_state == "MainMenu":
            if not self.main_menu():
                return False  # Exit game loop
        elif self.game_state.current_state == "LearningSounds":
            self.learning_sounds()

        return True  # Continue the game loop


class Game:
    def __init__(self):
        pygame.init()
        self.window_size = (800, 600)
        self.screen = pygame.display.set_mode(self.window_size)
        pygame.display.set_caption("This is a test")
        self.clock = pygame.time.Clock()
        self.fps = 60
        self.manager = GameManager()

    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False  # Indicate to stop the game

        return True

    def game_loop(self):
        continue_game = True
        while continue_game:
            if not pygame.event.peek():  # Check if there are no new events
                pygame.time.delay(10)  # Wait for 10 milliseconds

            continue_game = self.handle_events() and self.manager.check_state()
            if continue_game:
                pygame.display.flip()

            self.clock.tick(self.fps)  # Control the frame rate

        self.manager.sound_manager.cleanup()
        pygame.quit()


if __name__ == "__main__":
    game = Game()
    game.game_loop()

2024-02-01 07:47:38 (edited by magurp244 2024-02-01 07:53:29)

Hm, I think from what I can tell you have one centralized player pool with players being added and removed as needed for each sound, yes? If that's the case then it could cause some problems with say, enemies using players crowding out other game objects like the player or menu system.

In this case, its usually a good idea to budget how many sounds you'll play within your game, and setting aside dedicated players for those tasks outside of your common pool. For example, you might reserve two players for all your menu's, one for playing music and the other for moving and selecting buttons. Or reserve 3 players for the user character. For things like the environment, if you have a certain radius the user can hear things, like 5x5 tiles, then you could reserve 25 players for that, along with 5 dedicated players for navigation effects like beacons or notifications.

Keep in mind the soft limit on players for OpenAL Soft is 256, and you'd be using around 35 sounds by now, which leaves 221 left. You could dump those into a monster pool for them to be dynamically picked up and down as monsters are spawned and despawned as needed. So if you needed 3 sounds for each monster, that would give you around 73 you can spawn at once. Which brings up something interesting, if you can only view 25 tiles around you, then you can only spawn 25 enemies in your view area, so you'd actually only need 75 players for monsters, leaving you with 146 free players floating around.

So basically, by that point you'd have to balance how large your view area is relative to the number of max enemies you want visible/spawned at a given moment.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2024-02-01 09:21:37

In my case, I don't plan to do positional audio yet. I want to start with the simplest game I can make. Anyway, what I'm trying to do is, I have six sounds to use, not including the menu, and background music.
If you examine my code, mainly, each arrow keys have sounds assigned to them. what I want to understand is, how can I possibly just play those sounds on the player without having to re-assign them again to other players.
The way I implemented the playing of sounds is, I loaded all the sounds in the buffer then, when I call play_sound, it will search for the name of the sound in the buffer then, assign it to player. What I want to try to do is, if the sound I called is already assigned to a player, instead of reassigning the sound to another player, it will just use the player that has the assigned sound.
The reason why I want to do that is because I believe it's not good for the program to recycle the players all the time when playing sounds. Instead of doing that, it should use the player that already has the sound assigned. For example, if I repeatedly press the up arrow, on the first press, it will assign tone3 to a player and play the sound. However, in the next presses, instead of using the player with the assigned sound, it will just repeat and reassign tone3 to another player.
I'm trying to think of what to do, but I've run out of ideas. Thanks in advance for the help, and sorry if I have too many questions. I'm just learning Python, and I'm slowly grasping it.

2024-02-01 10:40:25 (edited by magurp244 2024-02-01 10:43:15)

I think I get what your saying, I touched on something like that in post 132 in this thread. Hmm... Its tricky because your sound manager is self contained and recycles the players within itself. But I think what you could do is have a buffer to delay the recycling of players. For example, lets say you have a list, and instead of being immediately recycled played sounds are pushed to the top of that list. If the number of items in that list are greater than 5, recycle any player greater than 5 in that list. Then you can do a check when play_sound() is called and look into the buffer first to see if a player with that sound is already loaded, and play that, possibly also pushing it to the top of the list, if not load a new player and slot it into the top of the buffer. Sounds that aren't played often enough get automatically recycled, and the last 5 or whatever sounds that are can stay resident in your buffer.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2024-02-01 10:56:41

I'm planning to remove the recycle function from play_sound so that it won't recycle the players while calling play_sound. What I'm planning to do is call the recycle function when needed or, add a check when the active players is more than five.
So, if what I do is check the buffer if the sound is already assigned to the player, is there a function I can use to find out if the said sound is already assigned to a player? Because, based on the debugging prints I added, the players are objects. So, is it possible to know the sound that's assigned to the player?
ok, I'll look at post 132 to get some ideas.

2024-02-01 11:05:48 (edited by magurp244 2024-02-01 11:07:27)

You can do a simple check in the players queue, where all loaded files are stored.

if len(self.buffer) > 0:
    for a in self.buffer:
        print(self.sounds[sound_name] in a.queue)

In this case, if the sound is found there it returns True.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2024-02-01 11:13:32

Ok, I'll try that. Thanks much.

2024-04-19 15:17:47 (edited by abdallah 2024-04-19 15:18:47)

hi, I have two questions.
First of all, I have a map that updates the coordinates by only 1, meaning if I press the right arrow the x increases by one.
Now if i play a sound and set its coordinates to be 10, 10.
I went to x 10 and then started going forward and backward. I cannot distinguish if the sound is in front of me or behind me. Is there a solution to this problem?
The second question is about hrtf.
Should I copy the files default-48000.mhr and default-44100.mhr
into the game path, then set the hrtf to 1 or 2.

battle.net tag abdallah#22878 the new one because i lost my previous account
my facebook

2024-04-19 21:33:04

Stereo panning is typically applied between the left and right channels, but this doesn't apply to forward and backward. HRTF can give some effects that can give the impression of this, and yes you'd need to include those files with your script and enable it with 1 or 2, or however many tables you happen to have, as those are just the default ones. There are instructions on the Openal PyLite github on how to get more.

There are a few tricks you might use though, like applying a low, high, or band filter or similar EFX to the sounds that are behind you, or adjusting the pitch.

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2024-04-21 13:47:25

Hello,
how can I change the output device of the playback on this library?

Regards,
Felix.

2024-04-21 21:09:20

By output device, are you referring to which speaker it outputs too? Or recording device?

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2024-04-22 09:12:16

just outputs

Regards,
Felix.

2024-04-23 01:06:49

Hm. If output device is equivalent to a speaker or other output, then there currently isn't a simple implementation of that in Pylite. The way OpenAL works is you can open devices and close devices, but not change them. Contexts are built on devices, and associated listeners, players, and audio buffers with them. So, to pick a specific device you'd have to essentially reinitialize your entire audio stack, though there isn't a way to set a particular device in the current class handlers for pylite.

I could put something in of course, but I don't really have much of a way to test whether it works or not. Which particular output devices are you trying to output through?

-BrushTone v1.3.3: Accessible Paint Tool
-AudiMesh3D v1.0.0: Accessible 3D Model Viewer

2024-04-24 23:48:04

@169
For example I need make switch headphones to line virtual audio cable or realtek audio drive which sound cames from computer.

Regards,
Felix.