2019-03-02 03:35:09 (edited by magurp244 2024-01-29 09:44:33)

Considered a learning experience, I've scraped the openal bindings from Pyglet and combined them with much of my own custom handler classes into a single wrapper script. This should now make setting up and using OpenAL a snap with Python, just include the openal.pyc or openal.py script with OpenAL32.dll in your working directory and your good to go, and import it like so:

from openal import *

The archive also includes are the previous HRTF tables and adjusted examples on how to use it, along with links to relevant documentation.

You can download it [here].

edit: For the latest up to date version, check out the github here. It doesn't come with OpenAL Soft, being a wrapper, but the main page has information on where to download it and set it up for the examples.

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

2019-03-02 07:52:04

Hi, wow! i will take a look at it as soon as i get the chance.
thanks a lot for this

best regards
never give up on what ever you are doing.

2019-03-02 08:28:12

ImportError: bad magic number in 'openal': b'\x03\xf3\r\n'                                                               this was the error i got when running one of the examples.
the 3d audio

best regards
never give up on what ever you are doing.

2019-03-02 08:47:23

This might be caused by using a python 2.x pyc file in Python 3. Try deleting the openal.pyc file and using the openal.py source file in its place.

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

2019-03-02 10:20:43 (edited by ashleygrobler04 2019-03-02 10:21:54)

Hi, thanks for that, it helped a little. after fixing the print statements that did not work, and fixing the required indentations, i got the following:
stri[dev] data[a]
TypeError: must be str, not bytes
to be exact, i think it is line 2154

best regards
never give up on what ever you are doing.

2019-03-02 10:49:29

Hm, that's irritating.. Probably python 3 enforcing types again, try swapping line 2154 with this:

stri[dev] += str(data[a])

The purpose of that function is to convert a ctypes byte array to a plain string to read what capture devices are available. Thanks for the feedback on this btw.

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

2019-03-02 10:56:26

wow, this works. thanks for this wrapper, i might think of using it in one of my projects.

best regards
never give up on what ever you are doing.

2019-03-02 10:59:53

Hi, just a question regarding the wrapper: can you play stationary sounds with this?

best regards
never give up on what ever you are doing.

2019-03-02 22:33:53

Hi there. I tried to code a function that sets the orientation of the listener but I get this error:
ctypes.ArgumentError: argument 2: <class 'TypeError'>: expected LP_c_float instance instead of float
what should I do? Heres the code for the function:
    def _set_orientation(self, x, y, z, ux, uy, uz):
        al.alListenerfv(al.AL_ORIENTATION, x, y, z, ux, uy, uz)

Oh and to set the player as relative, put this function in thep layer class:
    def set_source_relative(self, value):
        al.alSourcei(self.source, al.AL_SOURCE_RELATIVE,value)
thanks to pyopenAL that i copied this function from

---
Co-founder of Sonorous Arts.
Check out Sonorous Arts on github: https://github.com/sonorous-arts/
my Discord: kianoosh.shakeri2#2988

2019-03-03 02:35:33

Could there be made an option to read ogg encoded files both from disk and from memory (with f.x. a io:BytesIO object).
NicklasMCHD

If you like what I do, Feel free to check me out on GitHub, or follow me on Twitter

2019-03-03 04:49:28 (edited by magurp244 2019-03-03 07:32:37)

@8
Yes, absolutely. You can position a player at the same location as the listener, which would play it without rolloff or panning applied.

@9
Hm, digging into the bindings of the wrapper it seems that the listeners position is set by alListener3f, which expects 3 values, but that the orientation is set with alListenerfv which expects a pointer to a ctypes float array. The C based example code in the OpenAL documentation puts it like this:

ALfloat listenerOri[]={0.0,0.0,-1.0, 0.0,1.0,0.0}; 

// Orientation ... 
alListenerfv(AL_ORIENTATION,listenerOri); 

So, the Python equivalent might look something like this:

test = [1.456,1.54,1.88, 0.4,2.23,0.1]
test = ctypes.pointer((ctypes.c_float * len(test))(*test))

al.alListenerfv(al.AL_ORIENTATION,test[0])

I haven't had the opportunity to test this out though, so grain of salt and all that.

The actual handler classes included in it don't have all the features implemented, it actually disables source relative in the player by default but didn't have the function to change that, oops. I've cleaned it up a bit and fixed some of the python 3 bugs, and put in a source relative toggle setting. I might go over it a bit later to make the handler classes more feature complete for the listener and player cone orientation settings and such. Note that the OpenAL32.dll version packed with it is the 32 bit version, if your running 64 bit python you'll need to grab the 64 bit version from OpenAL-Soft.

@10
I haven't played around much with ogg, but it should be possible to decode it with something like [PyOgg] and pass it to an OpenAL buffer for playback. I might play around more with that later and see how it goes.

EDIT:
I've messed around a bit more with PyOgg and managed to successfully load and playback an ogg file with OpenAL, its seems fairly straight forward. What you do is load the ogg file, then use a custom BufferSound() object and pass the ogg's data into it, then load it into a player like any other audio file, i'm currently using a sample sound off wikipedia [here]. Example:

from openal import *
import pyogg
import time

class Example(object):
    def __init__(self):
    #load listener
        self.listener = Listener()
    #load ogg file
        tmp = pyogg.VorbisFile("Example.ogg")

        self.buffer = BufferSound()
    #set channels, bitrate, and samplerate
        self.buffer.configure(tmp.channels, self.buffer.bitrate, tmp.frequency)
    #load audio data
        self.buffer.load(tmp.buffer)

    #load sound player
        self.player = Player()
    #load sound into player
        self.player.add(self.buffer)
    #set rolloff factor
        self.player.rolloff = 0.01
    #play sound
        self.player.play()
    #give time for the sound to play
        time.sleep(6)
    #stop player
        self.player.stop()

    #clean up resources
        self.player.delete()
        self.buffer.delete()
        self.listener.delete()

Example()

In this case, the bitrate doesn't seem to be in the vorbis data, so I just pass the channels and sample rate, use the buffers existing bitrate, then load the data and play it.

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

2019-03-03 15:07:59

Hi
@11, what about if you have the file already in memory, like in a io.BytesIO (bytes object wrapped in a file like object).
How would you pass that right to pyogg / openal?

If you like what I do, Feel free to check me out on GitHub, or follow me on Twitter

2019-03-03 22:37:50 (edited by magurp244 2019-03-03 23:37:12)

Thats seems like a bit more of a complex question. PyOgg is able to stream files, but it seems to expect the file itself rather than a BytesIO stream, and OpenAL streaming is one of those things i've generally avoided because of the required buffer management. There was a rather interesting stackoverflow topic [here] though that successfully loads and plays an mp3 file from a BytesIO stream, it also works with Vorbis though is limited to only one stream at a time. Its likely possible to do it with OpenAL, but i'd have to dig a lot more into BytesIO and Vorbis decoding, along with OpenAL streaming. Here's an example with pygame though:

import pygame
from pygame import mixer
import sys
import io

def Example():
    with open("Example.ogg",'rb') as in_file:
        buffer = io.BytesIO(in_file.read())

#initialize pygame
    pygame.init()
#initialize sound mixer
    mixer.init()
#create display
    window = pygame.display.set_mode([640,480])

#load sound, can only stream one at a time
    pygame.mixer.music.load(buffer)

#main update loop
    while True:
        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
            #if space is pressed, play sound
                if event.key == pygame.K_SPACE:
                    pygame.mixer.music.play()
            #if escape is pressed, quit
                if event.key == pygame.K_ESCAPE:
                    pygame.quit()
                    sys.exit(0)

    #update window
        pygame.display.update()

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

2019-03-07 02:08:58 (edited by amerikranian 2019-03-07 02:09:40)

Right. magurp244, get ready for quite a lot of posts of me being stupid with the open al. I wana get it to work, and you seem to know a lot about the damn thing. So, with out further chatter, let's start the show.
1: What is a rolloff factor?
2: This may sound extremely dumb, but what is a listener, as in, I update the listener's coords to make sound change positions on the field, right?
3: What are open al players? Please tell me about the limitations, amount of sounds being stored, etc.
4: Is there a way to make the volume and the pan sharper or gentler depending on what you want? I.e, if the sound is in the center of the field, what if I just want it to go strait to the right after I press left arrow instead of gently panning to the right side?
5: You said that open al has 31 player limitation. Can you please ellaberate on that? What happens if I do more than 31 players? Will it not work? will it crash? Etc.
6: Is there a way for the open al to open mp3 or ogg files? wav files can be quite large...

2019-03-07 03:14:15

@14
Heh, nothing wrong with good questions! So...

1: The rolloff factor is how much the volume drops relative to the distance a player is from the listener. If you have a high rolloff factor, then if a player moves, say, 10 units away from the listener the volume will drop dramatically between that distance. Whereas if you have a very low rolloff factor, you might have to move the player 100 units or more away from the listener to get the same amount of volume drop.

2: When a sound plays, there are always two things, a player for creating the sound, and a listener for hearing it. If there is no listener, nothing can hear the sound, and if there is no player, there is nothing to hear. So, if you move the listener, the adjacent sounds appear to move, but only because the object that hears them is moving, not the sounds themselves. Now if the listener stands still and the sounds appear to move, that would be because the players playing them are moving, not the listener.

3: With OpenAL there are various objects, the Listener, Sources, Players, Effects, Filters, and EFX Slots. Sources are the sounds themselves that are loaded into memory, like loading a file, there is no limit other than your systems resources on the number of Sources you can load and have in memory. Players are the objects used to play them, generally speaking there's a limit of about 32 players that can play simultaneously, usually 31 to help prevent playback errors when starting or stopping them. There can be only one Listener at any given time, a particular limitation of OpenAL. There is no limitation on the number of Effects, Filters or EFX Slots you can load, but there is a limit on the number you can assign to a given player, though what that is exactly may be system specific.

4: You can do this by moving the desired player object, or the listener, more in the given direction. So instead of moving them by 10 units, move them by 100, and you should get a more dramatic panning effect.

5: Being more specific, different platforms have different limits on the number of sounds they can play simultaneously, Windows Phone for example has a limit of 16 sounds. Generally 32 is considered a safe number for most platforms, if you try playing more than that it may throw an error, not play, or potentially crash if you don't catch it.

6: Directly? No. OpenAL can play sounds loaded into OpenAL buffers, but it has no encoding or decoding of its own, the WAV loader I currently have built in uses Pythons native wave function to load a file into an OpenAL buffer, for example. This isn't to say that other libraries aren't available that can't help with that however, such as PyOgg as i've previously mentioned. Another one i've come across recently is [PySoundFile], which can handle a variety of formats, including mp3 and ogg. Note that PyOgg and PySoundFile can't play sounds on their own, their encoder/decoders, but OpenAL can't load sounds on its own either, so their best used together.

You can find additional information on a number of OpenAL functions and descriptions in the developer and Effects Extenions guides.


@12
As I've mentioned, I've been digging a bit more into a few libraries and come across PySoundFile, a wrapper for libsndfile. It also seems capable of decoding a BytesIO ogg file into a format that can be passed to an OpenAL buffer, much like how pygame does:

from io import BytesIO
from pysoundfile import SoundFile
fObj = BytesIO(open('filename.flac', 'rb').read())
flac = SoundFile(fObj, virtual_io=True)

So instead of using PyOgg you could use this to load data that can then be fed into a custom BufferSound() OpenAL object for playback, although this may not totally address things like streaming as yet. There's also potentially [librosa] for decoding, which is used for audio analysis.

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

2019-03-07 05:05:13

@15, so what you’re saying is that I can’t have more than 32 sounds playing at the same time?  Wouldn’t that greatly limit you in the long run?
Just to clarify, I can have as many sound files loaded in a player  but only one sound  playing  from it at any given time. Correct?

2019-03-07 08:54:54 (edited by magurp244 2019-03-07 09:05:20)

@16
Actually, the sound limit is something I'd wondered myself some time ago, but I had a really hard time finding a straight answer between how many sources you can have, players, or stuff playing simultaneously. The 32 limit I picked up from [here], the reasoning being that different hardware has different limits on the number of sounds that can be played, like the XBox 360 can play 300 simultaneous sounds, the Windows Phone 16 sounds, iOS 32 sounds, etc. So in order to be cross platform they limited it to something safe, which is 32, though they say that could be changed with a config file. But while they talk about OpenAL like this, this may only apply to Monogame's implementation of OpenAL. To add even more gasoline to the fire theres a series of emails [here] and [here] discussing OpenAL-Soft which states that there's actually a soft limit of 255 mono sources and 1 stereo source irrespective of OS, which is the same number of sources that can be allocated, and that this ratio between mono and stereo can be adjusted when creating contexts. Ultimately he says the 256 limit is more precautionary against excessive resource consumption. So, while I've said there's a limit of 32 simultaneous sounds, it could be up to 256 or more, I frankly find the issue irritating because it makes it hard to plan out deployment on different systems. Your milage may vary.

Anyway.. As far as I can tell there are no limits to the number of sound files you can have loaded in memory, you can cue multiple sounds to a single player and it will play them one after another, and that there doesn't seem to be a limit on the number of sounds you can queue on a single player.

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

2019-03-08 21:55:00

Right, @17, now that the forum is back up, let's continue this conversation.
From what I understand, the sounds them selves do not have an x, a y, and a z. So what's the problem? The problem is this: Let's say I want to create a sound class that handles the playing of sounds and stops the sounds in question after the user has went out of earshot. The way I pictured it is having sounds as objects within a list, with their x, y, and z, and checking if we're in range on each one. I kind of got that from BGT sound pool. That won't work with open al, simply because the players are used to play the sounds, and the players are the ones that have an x, a y, and a z. I can't loop through the players because I'd hit the player limit of 32 (the safe limit) rather quickly. So what am I to do? I seem to be stuck. Because the players will change their positions after I play a sound (I obviously want me to move around and hear different things), there aren't enough constants for me to create a loop that will work all the time, not from what I can see, anyways.
So what do I do? Do I rewrite the sound class and add in x y and z? Am I missing something completely obvious? Tips are greatly welcome and are needed.

2019-03-09 02:12:47

Well, how many sounds to you intend to play in a given area, and how wide is that area? Do some sounds have a louder area of effect than others? For example you could have a few very loud sounds that you can hear over a wide area, and a bunch of quieter sounds that play within a small area. Because of their smaller play area it would be harder for the user to get too many smaller sounds playing at once because of their limited proximity. But this also depends on the kind of game your trying to make.

In the case of the ill fated AudioRTS, I used a tile based system and assigned X number of players in a radius around the cursor, manually adjusting their volume based on how far away from the cursor they were to create an area of effect.

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

2019-03-09 02:57:53 (edited by amerikranian 2019-03-09 03:02:26)

I intend to play sounds with equal volume and pan for a start. The user should be able to hear something when they're, say, 25 units away from the sound at most.
As for the number of sounds, it really depends. I can't tell yet, right now I just need to figure out how not to go over the safe limit if possible.

2019-03-09 03:38:57

Therein lies part of the problem, if you don't know how many sounds you'll need, how can you plan to not go over? Figuring out one helps solve the other. Depending on the kind of sounds though, you could try layering them over time so they play intermittently. For example a door ping that plays every 2 seconds, and inbetween the times it plays could be other sounds so they don't overlap each other.

Something else to consider is maybe not to worry too much about any arbitrary limit, well other than the 256 one maybe, and deal with any problems people may have on deployment as they come, if any. Afterall, your not going to deploy this on mobile or Xbox right?

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

2019-03-09 06:43:43

The other part of the problem is me needing a single player for every sound, and I just wondered if there's an easier, simplerway to do it then assign a class to loop through players list. I just wana know if I can reuse like, 5 players and have sounds hold x, y and z coords so I can loop through them and load them into players as needed.

2019-03-10 05:40:27

You could do it that way yes, though storing the positional data in the sounds may be less efficient than storing it in the relevant class your trying to represent, like an NPC, player, or map class, since they would be the ones updating positional data for the game state. So you could have a sound handler with an active and idle list of players, and a queue of objects that want to play a sound. When an object wants to play a sound it registers itself in the queue, so if a player is released into the idle list, it takes the next object class in the queue and assigns a player to it, when you update those classes it loads the sound into the player and plays it, then releases it when done nack into the idle list for the next object in the queue.

I sort of did something similar in AudioRTS, where there was a set number of idle and active players that would dynamically allocate a player for a unit on the screen, then deallocate it back to the idle pool when off the screen or out of range.

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

2019-03-13 04:43:56

Hi,
How does openal represent 3D position? What does those numbers (240,320,0) etc mean?

2019-03-13 07:43:11

Those represent the X, Y, and Z planes. X would be side to side, Y is up and down, and Z is forward and backward.

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