Adventure Game Studio

AGS Support => Beginners' Technical Questions => Topic started by: Ghostlady on Sat 25/05/2024 04:35:48

Title: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Sat 25/05/2024 04:35:48
How can I lower the music for a couple seconds to a allow a sound to play and when the sound has ended, bring the music back to the same level?

Is there a way to have a control for the player so they can adjust the music level or turn if off if they want to?
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Rik_Vargard on Sat 25/05/2024 11:02:33
I have created a global variable for my music that is an audio channel called BGM.
Then, whenever I want to change the volume I write something like:

BGM = aMySong.Play();
BGM.Volume = 100;
//code
BGM.Volume = 50;
// code
BGM.Volume = 100;

and so on...
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: eri0o on Sat 25/05/2024 11:33:43
If you have more than one channel set to music nothing guarantees that such variable will be the same, since you are always overwriting it, so this single global variable only works if you have at most one music playing at the same time, which may be correct by game design. Otherwise you may need an array to keep track of these or use the audio channel array directly.

But yeah, the Audio clip returns a pointer to the audio channel it was assigned.

If you want, you can also vary the volume by audio type.

https://adventuregamestudio.github.io/ags-manual/Game.html#gamesetaudiotypevolume

Game.SetAudioTypeVolume(eAudioTypeMusic, 20, eVolExistingAndFuture);
Wait(10);
Game.SetAudioTypeVolume(eAudioTypeMusic, 100, eVolExistingAndFuture);
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Crimson Wizard on Sat 25/05/2024 16:49:12
Using AudioChannel's pointer for this purpose is unreliable for multiple reasons. One of them is that AudioChannel.Volume affects only a volume of currently playing sound (or music). If player changes the slider while there's nothing playing on this channel, then nothing gets changed, and next music won't be affected. With this you would have to assign Volume each time you call Play.

Using Game.SetAudioTypeVolume is a correct way to set volume for any currently playing and future music.

For the player you may add a Slider control, and call Game.SetAudioTypeVolume when this slider changes, for example. There are of course other GUI methods, like a label with up/down or left/right arrow buttons, etc; but this is all a question of a GUI design.





In regards to lowering music's volume while a sound is playing...

If that's meant only for particular sounds, then you may write a custom function where you
a) lower music's volume
b) start the sound and save its channel in a global AudioChannel* variable
Then in "repeatedly_execute_always" check if that channel is set, and if it is no longer playing then restore music's volume to a normal value and reset the channel variable to "null".
A script example:
Code (ags) Select
function PlaySoundAndLowerMusic(AudioClip *sound)
{
    SpecialSoundChannel = sound.Play();
    if (SpecialSoundChannel == null)
        return; // if failed to play for any reason, then stop here

    // make sure to also check the player's setting of a music's volume, otherwise we may end up
    // "lowering" it to a higher level instead
    int low_volume = 20;
    if (MusicVolumeSlider.Value < 20) // assuming you have a volume slider called "MusicVolumeSlider"
        low_volume = MusicVolumeSlider.Value;

    Game.SetAudioTypeVolume(eAudioTypeMusic, low_volume, eVolExistingAndFuture);
}

function repeatedly_execute_always()
{
    if (SpecialSoundChannel != null &&
        SpecialSoundChannel.IsPlaying)
    {
        // Restore the music's volume and reset the sound channel pointer
        Game.SetAudioTypeVolume(eAudioTypeMusic, MusicVolumeSlider.Value, eVolExistingAndFuture);
        SpecialSoundChannel = null;
    }
}


On another hand, if you want this to happen whenever any sound is playing, then, instead of having a "PlaySoundAndLowerMusic" function, you might check if any clip of type "Sound" is playing in "repeatedly_execute_always". Then it will look something like:
Code (ags) Select
bool WasMusicLowered;

bool IsAnySoundPlaying()
{
    // we begin checking with channel 1, because channel 0 is reserved for the speech
    for (int i = 1; i < System.AudioChannelCount; i++)
    {
        AudioClip *clip = System.AudioChannel[i].PlayingClip;
        if (clip != null && clip.Type == eAudioTypeSound)
        {
            return true;
        }
    }
    return false;
}

function repeatedly_execute_always()
{
    bool any_sound_playing = IsAnySoundPlaying();
    if (!WasMusicLowered && any_sound_playing)
    {
        // make sure to also check the player's setting of a music's volume, otherwise we may end up
        // "lowering" it to a higher level instead
        int low_volume = 20;
        if (MusicVolumeSlider.Value < 20) // assuming you have a volume slider called "MusicVolumeSlider"
            low_volume = MusicVolumeSlider.Value;

        Game.SetAudioTypeVolume(eAudioTypeMusic, low_volume, eVolExistingAndFuture);
        WasMusicLowered = true;
    }
    else if (WasMusicLowered && !any_sound_playing)
    {
        // Restore the music's volume
        Game.SetAudioTypeVolume(eAudioTypeMusic, MusicVolumeSlider.Value, eVolExistingAndFuture);
        WasMusicLowered = false;
    }
}
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Sat 25/05/2024 18:06:38
If I use this gui, which is the overall functional gui for both characters that drops down when rolling your mouse at the top of the screen, and add something like a Up and Down arrow or a music note and if they click on it do I need another gui with a slider?

(https://mysterymanor.net/Stuff/Sample3.JPG)

This would be for all sounds and music so if I use the bottom script, would I put that code in each room where I want to check for sound/music?
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Crimson Wizard on Sat 25/05/2024 18:30:56
Quote from: Ghostlady on Sat 25/05/2024 18:06:38If I use this gui, which is the overall functional gui for both characters that drops down when rolling your mouse at the top of the screen, and add something like a Up and Down arrow or a music note and if they click on it do I need another gui with a slider?

That is a purely a question of game and gui design; for this I don't have a direct answer. You may do either, or both, as you see fit. From the script's perspective, you would need to make sure that you can read current value. If you make 2 controls, then you'll have to synchronize them somehow, like, having a global variable with this volume setting.


Quote from: Ghostlady on Sat 25/05/2024 18:06:38This would be for all sounds and music so if I use the bottom script, would I put that code in each room where I want to check for sound/music?

You only need to put this code once in the GlobalScript, and it will work for the whole game.

If, for any reason, you would like to disable this behavior for particular rooms, that may also be achieved by adding another global variable acting as a "behavior switch", and checking it prior to rest of this code in repeatedly_execute_always.
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Sat 25/05/2024 19:03:11
I am not understanding this part of the script:

 int low_volume = 20;
        if (MusicVolumeSlider.Value < 20) // assuming you have a volume slider called "MusicVolumeSlider"
            low_volume = MusicVolumeSlider.Value;

Where is the volume slider?  Is this a global int?
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Crimson Wizard on Sat 25/05/2024 19:07:22
Quote from: Ghostlady on Sat 25/05/2024 19:03:11I am not understanding this part of the script:

 int low_volume = 20;
        if (MusicVolumeSlider.Value < 20) // assuming you have a volume slider called "MusicVolumeSlider"
            low_volume = MusicVolumeSlider.Value;

Where is the volume slider?  Is this a global int?


This script assumes that you have created a volume slider in one of your menus, and called it "MusicVolumeSlider".
You may call it differently in your game of course.

If you do not want to create such slider, then replace this with something else, like a variable that you set when changing a volume in your menu.
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Mon 27/05/2024 03:47:26
Ok I see what you are saying now.  I didn't realize that the slider was created within a gui.

I added the second script to the global script where you stated and I get an error message:

Error: Specified argument was out of the range of valid values.
Version: AGS 3.6.1.24

System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
   at WeifenLuo.WinFormsUI.Docking.DockContentCollection.GetVisibleContent(Int32 index)
   at WeifenLuo.WinFormsUI.Docking.DockContentCollection.get_Item(Int32 index)
   at WeifenLuo.WinFormsUI.Docking.DockPaneStripBase.TabCollection.get_Item(Int32 index)
   at WeifenLuo.WinFormsUI.Docking.VS2005DockPaneStrip.EnsureDocumentTabVisible(IDockContent content, Boolean repaint)
   at WeifenLuo.WinFormsUI.Docking.VS2005DockPaneStrip.OnPaint(PaintEventArgs e)
   at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer)
   at System.Windows.Forms.Control.WmPaint(Message& m)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at WeifenLuo.WinFormsUI.Docking.DockPaneStripBase.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

It is also telling me that a serious error occurred, and data may be corrupted.  My main game is on another computer.  I installed the latest version on a different computer so that I can play around with it and not break anything.
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Crimson Wizard on Mon 27/05/2024 04:38:47
Quote from: Ghostlady on Mon 27/05/2024 03:47:26I added the second script to the global script where you stated and I get an error message:

This is some error in the Editor, but it's likely not related to the script at all. Could be a random glitch related to the open panels.

Could you try again?
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Mon 27/05/2024 05:16:21
It's getting an error on this line in red and the error reads:  Nested functions not supported (you may have forgotten a closing brace)

bool WasMusicLowered;

bool IsAnySoundPlaying()
{
    // we begin checking with channel 1, because channel 0 is reserved for the speech
    for (int i = 1; i < System.AudioChannelCount; i++)
    {
        AudioClip *clip = System.AudioChannel.PlayingClip;
        if (clip != null && clip.Type == eAudioTypeSound)
        {
            return true;
        }
    }
    return false;
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Crimson Wizard on Mon 27/05/2024 05:32:01
Quote from: Ghostlady on Mon 27/05/2024 05:16:21Nested functions not supported (you may have forgotten a closing brace)

This means that you placed this whole code inside another function. Each function must be separate.
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Mon 27/05/2024 05:52:16
Ok I fixed that part.  Now an error saying AudioChannel is not a public member of 'System".  Are you sure you spelt it correctly (remember, capital letters are important)?

// we begin checking with channel 1, because channel 0 is reserved for the speech
    for (int i = 1; i < System.AudioChannelCount; i++)
    {
        AudioClip *clip = System.AudioChannel.PlayingClip;
        if (clip != null && clip.Type == eAudioTypeSound)
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Crimson Wizard on Mon 27/05/2024 06:00:53
It should be
Code (ags) Select
System.AudioChannels[i].PlayingClip;
I made a typo in my code above.

The related articles in the manual that may be used for double checking the code:
https://adventuregamestudio.github.io/ags-manual/System.html#systemaudiochannels
https://adventuregamestudio.github.io/ags-manual/AudioChannel.html
https://adventuregamestudio.github.io/ags-manual/AudioClip.html
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Mon 27/05/2024 06:25:29
Where does this part of the code go? I have it in the Repeatedly Execute function but still getting an error about nested functions.

bool WasMusicLowered;

bool IsAnySoundPlaying()
{
    // we begin checking with channel 1, because channel 0 is reserved for the speech
    for (int i = 1; i < System.AudioChannelCount; i++)
    {
        AudioClip *clip = System.AudioChannel.PlayingClip;
        if (clip != null && clip.Type == eAudioTypeSound)
        {
            return true;
        }
    }
    return false;
}
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Crimson Wizard on Mon 27/05/2024 06:43:40
Quote from: Ghostlady on Mon 27/05/2024 06:25:29Where does this part of the code go? I have it in the Repeatedly Execute function but still getting an error about nested functions.

Functions must be outside of other functions. You don't need to put anything inside "Repeatedly Execute". Just place the code into the Global Script, anywhere outside of other functions.

My code example has 2 functions: "IsAnySoundPlaying()" and "repeatedly_execute_always()". The latter is a standard function and may be already present in your Global Script. If you do already have it, then append my code from "repeatedly_execute_always" to the end of yours.
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Mon 27/05/2024 22:03:40
Ok makes sense.  I moved that part to the top of the script.  I am getting a parse error on the System.AudioChannels playing clip line.

Here is the top part of my script:
// Automatically converted interaction variables
int IntVar_Global_1 = 0;
export IntVar_Global_1;
int IntVar_barhaveitems = 0;
export IntVar_barhaveitems;
// main global script file
string herscore;
string hisscore;
int x;
int y;
bool WasMusicLowered;
bool IsAnySoundPlaying()
{
    // we begin checking with channel 1, because channel 0 is reserved for the speech
    for (int i = 1; i < System.AudioChannelCount; i++)
    {
        System.AudioChannels[i].PlayingClip;
        if (clip != null && clip.Type == eAudioTypeSound)
        {
            return true;
        }
    }
        return false;
        }

#sectionstart game_start  // DO NOT EDIT OR REMOVE THIS LINE
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Crimson Wizard on Mon 27/05/2024 22:45:49
It should be :
Code (ags) Select
AudioClip *clip = System.AudioChannels[i].PlayingClip;

My typo was only in "AudioChannels" word, so I posted only that fixed part last time. But the full assignment of "clip" variable is still necessary.
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Tue 28/05/2024 03:24:53
It is working perfectly now. :)  Thank you for your time and patience, it's been a while and I am rusty. I do have a question about the music slider.  I have it set up on the gui that I posted above.  If they click on the question mark, which I will change to a music symbol, it opens the slider gui up.  They can then adjust the music. But then the slider gui does not go away unless you click on it.  Is there a better way to do this?
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Snarky on Tue 28/05/2024 06:28:11
It just depends on how you want it to act.

If you want it to go away when the player clicks somewhere else, you should probably add a case to on_mouse_click.

If you want it to go away if the player moves the mouse far enough away from the slider, you probably need to check that in repeatedly_execute or repeatedly_execute_always.

If you want it to go away if the player doesn't move the mouse or do anything else for a while, the way the UI works in many video players such as YouTube, you also need to keep track of that in repeatedly_execute.
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Wed 29/05/2024 02:30:55
What code would I use to make it go away if the player moves the mouse far enough away from the slider?
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Snarky on Wed 29/05/2024 09:31:52
Something like:

Code (ags) Select
#define CLOSING_DISTANCE 10

function repeatedly_execute()
{
  if(VolumeSliderGui.Visible)
  {
    if(   mouse.x < VolumeSliderGui.X - CLOSING_DISTANCE
       || mouse.x > VolumeSliderGui.X + VolumeSliderGui.Width  + CLOSING_DISTANCE
       || mouse.y < VolumeSliderGui.Y - CLOSING_DISTANCE
       || mouse.y > VolumeSliderGui.Y + VolumeSliderGui.Height + CLOSING_DISTANCE)
      VolumeSliderGui.Visible = false;
  }
  // Other repeatedly_execute stuff...
}

This checks whether the mouse cursor is outside of a box around the GUI the volume slider sits on (change to the name you actually use, of course), with the "tolerance border" around the GUI here set to 10 pixels wide in each direction. If it's outside, simply hide the GUI.
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Wed 29/05/2024 19:43:29
I added this code to the repeatedly execute and when the slider gui pops up it immediately closes where you can't move the slider.
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Snarky on Wed 29/05/2024 20:06:16
Well, is the mouse cursor outside of the bounds as it opens? I don't know what your GUI design looks like, but if the slider doesn't appear right where the mouse cursor is, then it might of course immediately trigger the closing condition. You should work out the region you want it to remain open within and adjust the CLOSING_DISTANCE value (or values) accordingly.
Title: Re: Music Overlaying Sound and Sound Control for Player
Post by: Ghostlady on Wed 29/05/2024 22:02:08
That was exactly the issue. Thank you!