1

Hi, so I am making my way through the BGT tutorials, and I have been able to create a couple of my own modules, for handling speech, and menus, but I still don't have a good grasp on how BGT is handling sounds. I have looked at a couple examples of how the sound_pool class is used, and the documentation, but it still just doesn't feel right to me.

Perhaps different words and more examples will help.

Here is a couple questions that I have:
1. What is Preloading sounds  mean? I have seen this on the forms a couple times, as well as in the documentation, but I am not sure if I fully get the purpose, and the application for this.
2. Is there a good way to load sounds without using global variables? I am a computer science major, and in all of my classes thus far have encourage avoiding using global variables because it is bad design?
3. What is the listener and source? In the play methods, the documentation talks about listener and source  positions, but it doesn't exactly explain anywhere I saw what these mean. I am guessing listener means the person playing the game, but I don't know about source.

TJ Breitenfeldt

Thumbs up

2

1. The underlying sound engine will clone sounds that are already in memory, to save on load time during game-play. Preloading is loading the sounds for which this matters at the beginning.
2. Avoid global variables the same way you would in any other programming language: classes and properties. However, I'm not sure if the reason for avoiding global variables really applies in BGT, since it is not multithreaded. Still, I'd avoid global variables on the off chance you want to port to something mainstream later.3. The source is the point from which the sound plays. The source is basically the sound you're playing. So, if an enemy is making a sound, source_x and source_y are enemy_x and enemy_y respectively. You'll probably want classes which make sound to use a method to save you from typing all of this more than a couple times, since it's the most common usage.

Some of my games
Keep up to date by following @Jeqofire on twitter!
Ear Ninja?

Thumbs up

3

TJ.Breitenfeldt wrote:

1. What is Preloading sounds  mean? I have seen this on the forms a couple times, as well as in the documentation, but I am not sure if I fully get the purpose, and the application for this.

Creating a sound class doesn't need alot of time, since this just creates a class in memory, but calling the load() method of a sound object accesses the hard drive, if not even needs to decrypt an encrypted sound file, and that is, compared to other operations, extremely time consuming.
Playing a FPS and having to load sounds within the game might, if the amount of sounds is rather high and the device the game is running on is rather slow, result in getting killed, since your game will stuck while the sounds slip into their place, without you being able to react onto your enemy's movements. That's why people usually preload the sounds, meaning they create the classes and load all files into memory before the actual game starts (e.g. right at the startup of the game). In this case you can use existing objects when in game without the need of loading them from hard drive when playing actively.

TJ.Breitenfeldt wrote:

2. Is there a good way to load sounds without using global variables? I am a computer science major, and in all of my classes thus far have encourage avoiding using global variables because it is bad design?

That's a question you should be able to answer yourself, since it's one of the most common global variable avoidance strategies. Just create a class which does all the sound storing for you, create an object of that class in a function you like, and handle all this stuff there. Or create a singleton class which contains a handle to itself and return it by a simple function/method call or such stuff.
You're right, global variables are bad code nowadays, not just in multithreaded environments. That's why you are right, you should avoid them where possible, but it's not forbidden to use them, so if you think it's okay to use them, just do so. But it's better to use as less globals as possible, that means having one object as global variable is nicer than having 200 sounds with each sound as another global variable.

TJ.Breitenfeldt wrote:

3. What is the listener and source? In the play methods, the documentation talks about listener and source  positions, but it doesn't exactly explain anywhere I saw what these mean. I am guessing listener means the person playing the game, but I don't know about source.

That's rather simple. The listener usually is the player, while the sounds are positioned somewhere around the listener. Just imagine that to be in your room. The listener is you, while you might have a door (a source) to your far left, and a window (another source) to your far right. You define some coordinates (x y) for those two sources. When you move, you now just need to move yourself (the listener), since the door and the window are statics (they don't move), and the sounds will change anyway. If you move e.g. right, the door left will get more quiet (it's now very far left), while the window is quite loud and is now directly right from you. That simplifies sound positioning, because the engine will do all the sound positioning for you and the only thing you have to do is move the listener (yourself) and not all the possible tens or hunderets of sources around you to achieve a more or less realistic audio environment.

Best Regards.
Hijacker

Thumbs up

4

Thank you for the replies, I want to ask a couple follow up questions. primarily about the preloading.

At Hijacker:

I think part of my problem is that I don't know much about sounds to begin with, it makes sense to load all of my sounds before the game loads in a single object, but how do you access them? It has to be hard to keep track of which index is associated with which sound assuming we store them as an array. If a dictionary, what would you use as a key, because if you gave every sound a unique name, it would be very tedious to go through and load all the sounds by hand, it is much easier to give generic names, that are numbered, so you can load them using a loop, but I would imagine that it is hard to work with those sounds.
So as I start creating my classes for the player and, monsters, and other game objects, I want to create a handle for the sound as a class level variable, so that I can use a constructor to point the handle at the given sound object, right? I do not want to load the sound in each object such as in the constructor?
And I think this is where my inexperience about using sounds is going to really show, what do you mean about encrypting and decrypting sounds? Why do you need to deal with encryption at all when working with sounds?
What do you mean by:
"In this case you can use existing objects when in game without the need of loading them from hard drive when playing actively."

I only asked about the global variables because all of the game based tutorials I have seen, mostly python based,  including BGT, seem to use and often encourage using global variables. So I started wondering if global variables are used more in game programming than else where? it kind of makes sense because if you have to access a central object where all of your sounds are stored, it seems like it would not be very efficient to pass that object through to every class and function that needs to access a sound. Also, for timers, and other game states, it also seems to make sense to set these as global. Is this what most people are doing, or is there another method of handling these game properties.

That makes a lot of sense about the listener and source now. So just to be clear, if I wanted to give the player a sound when they are moving, I would set the listener and the source to the coordinates of the player's position?

TJ Breitenfeldt

Thumbs up

5 (edited by Hijacker 2017-07-01 20:30:52)

That's actually alot of stuff you ask here, hopefully I don't miss anything out.

TJ.Breitenfeldt wrote:

I think part of my problem is that I don't know much about sounds to begin with, it makes sense to load all of my sounds before the game loads in a single object, but how do you access them? It has to be hard to keep track of which index is associated with which sound assuming we store them as an array. If a dictionary, what would you use as a key, because if you gave every sound a unique name, it would be very tedious to go through and load all the sounds by hand, it is much easier to give generic names, that are numbered, so you can load them using a loop, but I would imagine that it is hard to work with those sounds.

There are multiple questions in here. Some of those are basic programming questions which don't relate to sounds explicitely. How do you handle a collection of different data objects (might it be sounds, strings, whatever) and how do you address them properly, without wasting too much time?
That's totally up to the programmer. Imagine it like you want. Create a class which loads the sounds if you tell it which sound you want and stores it the way you like.
Try yourself if it's more efficient to go through an array with strings or some sort of whatever you like or searching a dictionary for the stuff you want. That depends actually on the amount of sounds your game will have.
An array might be the most efficient way until it reaches a certain size. If you notice that this size will only be reached as soon as you played several hours along it might be acceptable to use an array anyway. Dictionaries are comparably slow in comparison to arrays, but their processing time is really constant, which makes them a good solution if you know that you're using alot of sounds and that the array processing time will quickly exceed the dictionary processing time.
You know why I don't want to answer such questions with a detailed solution, because there might be another best fitting solution for your case here, and because there are so many solutions.
Just imagine two arrays, one containing the string of the sound name and one, the same size and the same indices, the handles to the sound objects. That way you could search array 1 for the sound string and return array 2 at the same index if the sounds could be found. If it's not found, you could just load that sound and add the sound to array 2 and the string to array 1 and you'll find it the next time you want to load it within your class so you don't need to load it again from hard drive.
That's one way i'm using in one of my projects (not even bgt, because those concepts apply to any other language too).
I load sounds always using a class implemented by me. This class gets a name. The class looks if it already knows the sound. If not, it loads it from hard drive and if yes, it returns that already loaded sound. If you then load all sounds one time on startup the game will know all sounds and the loading is done forever.
If you like and know that your game will be very large, because you included over one gb of sounds in your game, you can even garbage collect the sounds you don't need anymore.
Don't forget here that all sounds loaded will ly in the game's RAM until you unload the sounds manually or close the game. That's why the game's RAM usage might skyrocket if you decide to include several gigs of audio and load all of that on game start.
Back to topic: the index you use to find your sound is up to you. But why not simply use the sound's name? What's the problem here?
Of course you could also access the hard drive using directory level access functions and load all sounds you find automatically (no need to do that by hand) and there is so much more you could do if you want... the possibilities are endless.

TJ.Breitenfeldt wrote:

So as I start creating my classes for the player and, monsters, and other game objects, I want to create a handle for the sound as a class level variable, so that I can use a constructor to point the handle at the given sound object, right? I do not want to load the sound in each object such as in the constructor?

Depends on your implementation. If you aren't storing the sounds somewhere in memory, then yes. It would be absolutely inefficient to load them all new again if an object is instanciated. But here again, that depends on your chosen implementation.

TJ.Breitenfeldt wrote:

And I think this is where my inexperience about using sounds is going to really show, what do you mean about encrypting and decrypting sounds? Why do you need to deal with encryption at all when working with sounds?

Again not a question about sounds themselves.
The problem related to assets in general apply here, which many people here don't understand at all. Any assets found in games, programs and all that kind of stuff are licensed (except open source stuff). That means people payed for it, if not the user, but the developer. Sound libraries aren't free, at least not the most of them.
Music producers want to be paid too, by the developer. The developer then paid several hundreds, if not thousand dollars for his sound effects and music to use in his program. Of course he doesn't want any user who plays his game to enter his game folder, find the sounds he wants, copy that stuff and insert it into his own game, all for free, and then wants to be paid for his game too. All without paying a single dollar for that. Best example might be the quite popular Crazy Party game in the forums here. Most of the music found there was stolen from Pokémon games like Pokémon Rangers, Pokémon Black & White and so on. Not that the Crazy Party developers didn't pay for it in any way, the also get in conflict with the law. If GameFreak or some other developer who licensed the music they're actually using noticed that, they could get in real trouble, paying hundrets and thousands of dollars, no matter if their game is for free or paid. You're not allowed to use stuff you don't own. That's why developers encrypt the assets they're using as best as possible, making it almost impossible for users who just play the game to decrypt the sounds and use them for their own project. But encrypting this stuff also means that the game itself must be able to decrypt the assets on runtime and in RAM, because if you store them decrypted on hard drive somewhere, the user might again be able to copy the decrypted sounds and all work would be worth nothing.
That's why developers (Crazy Party too) encrypt their sounds.

Regarding your question to my last post, I just answered that above. If you hold the sounds in memory, you don't need to load them again from hard drive.

TJ.Breitenfeldt wrote:

I only asked about the global variables because all of the game based tutorials I have seen, mostly python based,  including BGT, seem to use and often encourage using global variables. So I started wondering if global variables are used more in game programming than else where? it kind of makes sense because if you have to access a central object where all of your sounds are stored, it seems like it would not be very efficient to pass that object through to every class and function that needs to access a sound. Also, for timers, and other game states, it also seems to make sense to set these as global. Is this what most people are doing, or is there another method of handling these game properties.

That's usually bad practice. But again, some stuff can be saved as global variable, but I'd prefer to use either static variables or singleton classes, meaning variables with exist in all objects of a class or classes which may only exist once per program. Those are basic programming concepts, if you don't know about those things, try googling it yourself (my response is rather long even without explaining that stuff).

TJ.Breitenfeldt wrote:

That makes a lot of sense about the listener and source now. So just to be clear, if I wanted to give the player a sound when they are moving, I would set the listener and the source to the coordinates of the player's position?

That's absolutely it. If you move the listener, the source will stay absolute and therefore move more left or right from you, just as you move the listener. If you want to have this source always in the middle, unrelated to the listener, you usually can tell the sound to be non-relative (usually some sort of relative = false initialization or playback parameter), the sound will always stay in the middle.

Best Regards.
Hijacker

Thumbs up

6

Decryption is also relevant for compressed formats, such az ogg.
In BGT, you don't actually need to do anything with preloaded sounds, other than keep them around. When BGT loads sounds, it checks to see if that sound is already somewhere in its memory. If it is, BGT will refer the new sound object to that data, rather than load it again from disk. If you have a ton of compressed or encrypted sounds, this can save the game lots of in-game lag from loading sounds.
However, I'd still use the sound_pool for most things. The one exception is background music, which I preload to a dictionary just as Hijacker described.
Singleton classes are mostly global variables disguised as something else, so the reasons to avoid global variables isn't much gainsaid. I'd use global variables for things which it would be a waste to pass around all the time, such as the sound_pool.
Anything whose scope can be sufficiently limited, though, can be a property/member variable of the relevant class. For example, you might have a class for enemies, a class for levels, and a class for the game itself. All 3 will want to play sounds, but there's no reason for an enemy to know what the player's score or name is, or what BGM is playing, so those can be left up to the game class. Likewise for timers: use a master timer in the game class, be sure that the same amount of time passes each frame, then update the enemies by that amount.
Strictly speaking, the sound_pool remembers the last listener position, so you don't necessarily need enemies to know the player's position. In practice, I don't like typing pool.last_listener_x when I can just say camera.x, so I often use a global vector for the camera position, even though that's a textbook case of something which is not a great idea to make global. If BGT had the concept of static protected static variables, I might make the camera a protected static class property, but in that case I'd probably just make a global function or four that use pool.last_listener_x so I only have to type it once.
The sound_pool is useful for the following:

  • Managing large numbers of sounds, especially if you have no reason to reference them after they start playing.

  • Moving the listener. You don't need to adjust each sound individually, when you can just call pool.update_listener_2d.

  • Pausing, unpausing, and stopping many sounds simultaneously. For example, pausing/unpausing the game, or reaching the end of a level.

  • Easier positioning of moving sounds. The sound_pool returns the index where the sound is playing, so you can have moving objects like enemies keep track of their sounds, and call pool.update_sound_2d(index, x, y); and be done.

  • For operations which affect most or all playing sounds, you can also iterate the sound_pool's internal array. This is a bit cumbersome, but is much easier than trying to keep track of individual sound objects.

  • Major changes to how sound is presented. For example, if you want to adjust how much pan corresponds to how much x distance, or how volume corresponds to distance, or how far away a sound can be from the listener and still play.

Some of my games
Keep up to date by following @Jeqofire on twitter!
Ear Ninja?

Thumbs up

7

CAE_Jones wrote:

Decryption is also relevant for compressed formats, such az ogg.

That's decoding and a totally different thing. Try not to mess this around with decryption. Not just that this works totally different internally, those are words which describe different algorithms which are used in totally different cases.

CAE_Jones wrote:

Singleton classes are mostly global variables disguised as something else, so the reasons to avoid global variables isn't much gainsaid.

Singleton classes aren't global variables at all, that's actually the reason they exist. But they work more or less identically. If you want to, you could say that singleton classes are the global variables in an completely OOP based language. These are just classes which may only exist once. You can avoid using globalvariables here if your language allows you to use e.g. static variables or allows you to use singleton classes per definition. If both of those aren't available, you'd have to use global variables, in which case you're absolutely right. This would mean that you could use global variables directly.
That's why I said it's usually bad practice today, but some language simply don't allow to skip using globals without much overhead passing around several handles which would be unnecessary in languages which would allow those features.

Best Regards.
Hijacker

Thumbs up

8 (edited by TJ.Breitenfeldt 2017-07-04 01:43:46)

Thank you for the very detailed responses. So, I just want to make sure I understand this correctly, if I preload my sounds, when I pass the file name to the sound_pool methods, such as play_1d, it will first check if the sound is already loaded in memory, if so, it proceeds to play the sound, if not, it will load the sound, and then play it. preloading allows me to load all the sounds before the game starts, or at the beginning of each level, so that the sound_pool does not have to load the sounds later when it tries to play a specific sound. If this is true, does sound_pool just some how know that if that sound has already been instantiated, even though the sound_pool isn't given a handle to the preloaded sounds?

TJ Breitenfeldt

Thumbs up

9

Yes. It's more the sound object than the pool itself, but this is how it works under the hood, so to speak.

Some of my games
Keep up to date by following @Jeqofire on twitter!
Ear Ninja?

Thumbs up

10 (edited by Hijacker 2017-07-04 12:17:00)

Yep, that's it and that's how it works.
BGT tells you the following about this:

The engine uses a technique to try and ensure that the least possible amount of memory is used when the same sound is loaded into more than one object, which is often the case with the previously mentioned sounds. For instance, if you have 50 enemies for which you have two footsteps each, 100 footstep sounds will not be loaded into memory even though you get 100 completely independent sound objects.

How this is done internally inside of BGT - no idea, ask Philip for it. But what matters is that it's done and it works quite well.

Even if it isn't said that there won't come the time where the program needs to reload the sounds you're using, because you didn't hold a handle to some sound object. That's because the garbage collector might hit any moment and trash all unused objects to reduce memory consumption.
That's why you should at least always keep one handle to a sound safe in some place. That secures that the GC won't trash the sound completely. That's the moment where classes like the one I explained earlier come in handy.

Best Regards.
Hijacker

Thumbs up

11

Okay, that makes a lot of sense now, thank you very much for all of the responses.

TJ Breitenfeldt

Thumbs up

12

So, I am trying to take the my new understanding of sounds and apply them to my menu class I created, just using two sounds, one for every time you arrow to the next option, and another sound for when you select the choice by pressing enter. I am using a global variable for my preloads, and sound_pool. since this is just a menu, it seemed unnecessary to use the sound_pool methods, so I just grabbed the handle from the preloads, and told it to play.

It is working for the most part, but if you arrow up and down to fast, the sounds can't keep up, and there end up being options that speech is read for, but no sound is played. Anyone have any suggestions of how to fix this?

I just wrote a main function to test this class, and a loadSounds function to load the sounds for the menu into the preloads object. In this case the lodeSounds function is overkill, I just wanted to get an idea of what it would look like if I had more sounds.

#include "speach/SpeechManager.bgt"
#include "gameUtils.bgt"
#include "soundManager/Preloads.bgt"

#include "sound_pool.bgt"



string soundType = ".ogg";
sound_pool soundPool;
[email protected] preloads = loadSounds();

void main() {
    string title = "Main Menu";
    string[] options = {"Start Game", "Help", "exit"};
    Menu menu(title, options);
    SpeechManager reader;
    int choice = -1;
    
    reader.stopSpeaking();
    show_game_window("Testing Menu");
    wait(1000);
    install_keyhook();
    
    do {
        choice = menu.run();
        
        if (choice == 0) {
            reader.sayInterrupt("Starting game");
            wait(1000);
        } else if (choice == 1) {
            reader.sayInterrupt("starting help");
            wait(1000);
        }  //end else if 
    
    } while (choice != 2 && choice != -1);
    
}  //end main()

[email protected] loadSounds() {
    Preloads temp;
    string soundName = "";
    
    for (uint counter = 1; counter < 3; counter++) {
        soundName = "sounds/menu" + counter + soundType;
        
        if ( !temp.addSound(soundName) ) {
            alert("Sound Loading Error", soundName + " could not be loaded for some reason");
        }  //end if 
    }  //end for loop 
    
    return temp;
}  //end method loadSounds()

class Menu {
    
    int menuIndex;
    uint numberOfOptions;
    string[] options;
    string currentItem;
    string title;
    bool closeMenu;
    SpeechManager reader;
    [email protected] optionSound;
    [email protected] selectionSound;
    
    //EVC 
    Menu(string title, string[] options) {
        this.menuIndex = 0;
        this.options = options;
        this.numberOfOptions = this.options.length();
        this.closeMenu = false;
        this.currentItem = "";
        
        string optionSound = "sounds/menu1.ogg";
        string selectionSound = "sounds/menu2.ogg";
        
        @this.optionSound = preloads.getSound(optionSound);
        @this.selectionSound = preloads.getSound(selectionSound);
    }  //end EVC
    
    /*
     * run method
     * This method is used to speak the first item, start the menu loop, check for the exitKeys, check for screen reader specific keys,
     * and check and update the menu state accordingly
     *
     * return: int, returns the index of the option that the user selected 
    */
    int run() {
        int choice = -1;
        
        this.currentItem = this.options[this.menuIndex];
        this.reader.say("," + this.currentItem);
        
        while ( !this.closeMenu) {
            this.closeMenu = exitKeys();
            this.reader.speechControls(this.currentItem);
            choice = this.getMenuOption();
            wait(5);
        }  //end while loop 
        
        this.cleanup();
        return choice;
    }  //end method run()
    
    /*
     * getMenuOption method
     * This method is used to check for menu specific keys, such as up and down errow, and change the menu state accordingly
     *
     * return: int, this returns the index of the option selected by the user, passing the value back to run()
    */
    int getMenuOption() {
        if (key_pressed(KEY_RETURN) || key_pressed(KEY_NUMPADENTER)) {
            this.selectionSound.play_wait();
            this.closeMenu = true;
            return this.menuIndex;
        } else if (key_pressed(KEY_DOWN)) {
            this.optionSound.play();
            
            if ( ++this.menuIndex >= this.numberOfOptions) {
                this.menuIndex = 0;
            }  //end if 
            
            this.currentItem = this.options[this.menuIndex];
            this.reader.sayInterrupt(this.currentItem);
        } else if (key_pressed(KEY_UP)) {
            this.optionSound.play();
            
            if (--this.menuIndex < 0) {
                this.menuIndex = this.numberOfOptions - 1;
            }  //end if 
            
            this.currentItem = this.options[this.menuIndex];
            this.reader.sayInterrupt(this.currentItem);
        }  //end if 
        
        return -1;
    }  //end method getMenuOption()
    
    /*
     * cleanup method 
     * This resets the menu bak to default, changing variables back to their starting values
    */
    void cleanup() {
        this.closeMenu = false;
        this.menuIndex = 0;
    }  //end method cleanup()
    
}  //end class Menu

TJ Breitenfeldt

Thumbs up

13

Yes, there is one absolutely simple way to prevent this, and I guess you could have considered that yourself. Just do a combination of .stop() and .play() calls each time you retrieve an arrow key pressure.
First stop(), then play(). That should do the trick.
Also, it's fine to create an own menu class if you want to implement some additional functionality, but for any other purposes, take a look at the BGT help. There you can find a dynamic_menu class which should do almost everything your class does too and you can enhance it with callbacks quite easily.
Best Regards.
Hijacker

Thumbs up

14

Sorry about that, I should have thought of that.

I knew about the dynamic_menu class, I just wanted to practice using the language, and building a menu seemed like a simple place to start and give me the basics I will need for actually working on the game itself.

What exactly do you mean by
"enhance it with callbacks quite easily"?

Thanks,

TJ Breitenfeldt

Thumbs up

15

Just what I said. Since this menu class is closed, you can't just catch addiional keystrokes inside the run() loop as you do in your menu. Therefore you can add a callback into the dynamic_menu object, which can do this stuff for you.
If you don't know callbacks, then again, take the time to google it yourself and read yourself through. It should also be enough to read through the BGT tutorials, they explain the concept good enough to understand it at least in the BGT environment.
Best Regards.
Hijacker

Thumbs up