Open bilhox opened 4 weeks ago
for the record, a full pythonic implementation would require the update method but the implementation i added in #3057 uses a bit of C to remove the need for it.
Ok, so it's got all these features, cool, but what does this actually do for a game? What's the use case?
Maybe it shouldn't be called a Queue, because this is not a generic data structure, this is a very specific object. And it seems weird to have remove() and indexing on a queue.
But I don't understand the use case, so names are hard to come up with. Album
?
Also this would need to support file like objects, so things shouldn't be called filenames
and be exclusively lists of strings.
I ended up writing something very similar for my game. Specifically because I wanted the functionality of Sound objects but for music. I wanted the ability to preload each track and set per track volume levels or make "groups" of tracks for certain playback properties. There were a couple of other reasons I ended up writing it but I do agree that the current mixer.Music is very basic and could use some gussying up. I simply called mine a music manager but a 'playlist' or 'tracklist' would work for names too.
what does this actually do for a game? What's the use case?
It allows to handle and manage multiple musics at once, and most precisely the possibility to play musics like a queue. It can be used in a game to play a list of music.
But I don't understand the use case, so names are hard to come up with. Album ?
I do agree with this name, this feature requires discussion about the actual features it comes up with. This is why every feedback is appreciated.
Thanks for the feedback ❤️ @Starbuck5 .
I had several potential use cases for my games, and ended up programming something similar myself on several occasions:
pygame.mixer.music
is busy every frame.As for naming, I always looped the music functionality in the same object as the one that handled sound effects and called the whole thing a SoundManager
.
No matter what features you add or don't add, I would highly recommend making it subclassable and extensible like pygame.sprite.Group
. That way I can add more niche things like per-track fading myself without having to reinvent the wheel completely. That's kinda how I see this feature - a springboard for further advancement that's just a bit more useful and intuitive than bare pygame.mixer.music
.
At least that's my two cents. I've been making games with pygame for about 5 years now and as a user that's what I would like to see, but all you pygame developers have other things like maintainability to worry about and I have little experience there.
EDIT: Wait mixer.music streams audio from the file. That invalidates a significant portion of point 2. It would still be nice to have an API for loading my music filenames when I load my other assets.
Also WASM generally wants you to load everything at the beginning, so a unified interface for that might be something that pygbag can take advantage of. Maybe talk to pmp-p about that...?
@pmp-p This is something you might want to answer.
A lot of looping soundtracks start with a small fanfare at the beginning before looping. Some games that come to mind which did this include Super Mario Bros (and sequels), Sonic the Hedgehog (and sequels), Legend of Zelda (and sequels), and likely others (I liked those games so much I didn't ever play much else). This Queue would allow you to play the fanfare, with the looping portion queued up to play automatically. This would save me a lot of work having to wait for an event to trigger the next track, or checking if pygame.mixer.music is busy every frame.
This is possible already with pygame.mixer.music.queue
. play()
the fanfare and queue
loops of the looped portion.
I could also see potential performance gains from preloading all of the soundtracks at the beginning. Generally I see games do that with most other assets, so having a way to do that for music, especially big chunky wav files, would be consistent and helpful. This especially holds true when the music track needs to be changed quickly in real time. Cutscenes (especially if someone is trying to button mash through it really fast like I do half the time) or boss fights with multiple stages come to mind here. Also WASM generally wants you to load everything at the beginning, so a unified interface for that might be something that pygbag can take advantage of. Maybe talk to pmp-p about that...?
The difference between music and sounds in pygame-ce is that music is streamed, so I don't see any performance gains from preloading here. And regardless, the current implementation doesn't stage the musics as Mix_Music anyways.
EDIT: I now see your edit about music being streamed,
It would still be nice to have an API for loading my music filenames when I load my other assets
What do you mean?
I ended up writing something very similar for my game. Specifically because I wanted the functionality of Sound objects but for music. I wanted the ability to preload each track and set per track volume levels or make "groups" of tracks for certain playback properties. There were a couple of other reasons I ended up writing it but I do agree that the current mixer.Music is very basic and could use some gussying up. I simply called mine a music manager but a 'playlist' or 'tracklist' would work for names too.
Volume and other playback properties aren't supported by this proposed API though. The only playback property supported seems to be loops. And the proposed API doesn't preload in any meaningful sense besides storing the filenames.
Talked to bilhox on discord about this, here's what I think would be an interesting use case for a feature like this:
"My game has multiple worlds / stages / levels whatever, and I want to compile some music for each to be ambient music for the player. I want an object that I can say "go" and it plays my music in a random order continuously, with configurable fade-in/fade-out and silent periods in between as not to jar players with sudden changes. Each time I switch levels I will transition to a new one of these objects."
I think finding use cases here is a meaningful exercise, because the proposed API does not work for my given use case.
This is possible already with pygame.mixer.music.queue. play() the fanfare and queue loops of the looped portion.
🤦 I have been up and down the docs so many times and I never saw that. Thanks!
What do you mean?
This is just a code architecture thing. Generally surfaces, sounds, text files, and any other assets are all preloaded at the beginning of game, with a nice splash screen and stuff. In my head music is an asset too, and it would make sense to process them at the beginning. For streamed music currently I can't do any of that because I generally have to load stuff right before I play it, so I generally just ignore them in my asset loading code. My OCD would really like an object to make and play with upon game load, instead of messing around with filenames later in the code, so I generally make one, just to store the filenames, so that all path processing is in one place. I've refactored my assets handling enough times (pyinstaller and pygbag, I'm looking at you!) where I'm a bit paranoid about this.
Also I still tout cutscenes and boss fights where the music needs to change seamlessly and sometimes in rapid succession. Just having a function that makes the music one level more of scary, or adds another level of atmosphere, when the next stage starts would make developing that sort of stuff that much nicer.
I could also see alternate soundtracks for stuff being an interesting use case. What if I have two variants on a level sountrack, just to keep things interesting? Picking a random index out of a Queue would be very useful here.
@bilhox pygame.mixer.music is not decently useable on WASM(browser) because that part of SDL2 is hard realtime. Wasi runtimes don't have audio yet.
As a result in pyodide there's poor support for audio or none at all (worker).
In Pygbag pygame.mixer.music works fine but under the hood is re-implemented in javascript : it is already multichannel/multitracks because using browser audio threads and a track cache.
So i'm +1 for handling multitrack music since feature is already available.
Preloading is always better since it make smooth transitions and avoid I/O error at runtime.
sidenote : i would discourage people from using .mp3 explicitely for that new feature and promote .ogg usage. pygame-ce on pyodide is not assisted to convert mp3 at pack/preload.
Personally, I'm 50/50 on whether pygame should have this.
Any game with multiple tracks will probably need to implement a music manager or system. But implementing it is a bit annoying with the basic music
API: set_endevent()
and having to check the event queue, or checking get_busy()
. For me, this would be the most important reason for having a class for this.
A built-in, highly extensible class can make using pygame easier, like the sprite
module. Its implementation may also leverage pygame internals to improve its ease of use.
However, it may be best to leave it to the user so they can write it to their needs, similar to implementing animations or the event loop. (Though, using a built-in class would be optional.) Pygame is like this for a lot of things, providing basics and not abstractions, which helps to avoid bloat.
I advocate for the class name Playlist
or Tracklist
because playlists are the closest thing to this class. I think Album
is a little imprecise. Also, the term for individual pieces of music should probably be either "song" or "track", in the implementation.
I'm going to propose another design/API for this class to address problems in extensibility and ease of use.
Let me know if there are any issues or possible improvements with this design.
Hello,
Following what we said on discord, I analysed more precisely the situation and I think personally it's not the thing we're actually in need. This is why I came across a better idea, what's actually stopping us from having a pygame.Music
object. This would be a more pythonic implementation of how you handle musics, and it would allow you in your game if you want to play several musics with different settings (like playing offset, fadein, fadeout). I'm just giving the main ideas of this feature, I'll open a different issue when I'll properly shape the feature.
That said, pygame.mixer.music.Queue
could be turned to an example that makes use of pygame.Music
.
Hello, I want to introduce a new feature called
pygame.mixer_music.Queue
, which is, as its name says about it, an object for handling several queued music at once compared to his friendpygame.mixer_music.queue
. What features it can propose for a pygame user ? :Queue.play_next()
andQueue.play_previous()
, or even play a music at certain index.Below a code example of how it can work :
stubs preview by @damusss :
While this can be just a python implementation,
pygame.Channel
has already a similar system withpygame.Sound
, so I highly believe this can be pygame feature. @damusss tried to test and try to make an implementation in python, but keeping track of the music playing would involve having an update method, which doesn't follow how pygame.mixer works.I would like to hear the opinion of the steering council about this.