enum of pointers

Started by nightmarer, Tue 23/02/2021 22:27:02

Previous topic - Next topic

nightmarer

Good evening, community.

I'm trying to create an enum for a script that manages audio chanels.
In the header(ash) this is the enum created.

Code: ags
enum SetBGM_Channel {
  Channel1 = *bgm, 
  Channel2 = *bgm2, 
};


This enum is used in a struct created below.
These pointers are refered in the asc page as

Code: ags
AudioChannel *bgm; //Channel 1
AudioChannel *bgm2; //Channel 2


I'm getting the following error: enum list must be set to literal value
Any idea about how I can do it?

Thanks and regards.


Crimson Wizard

#1
You cannot have enum made out of pointers, because enum is an integer type.

It is not clear to me how are you planning to use these. Depending on your particular case you could perhaps instead make enum of channel IDs, which is an integer. Or put your channel pointers in array and make enum out of array indexes (0-based). Or make a function that converts enum to AudioChannel (accepts enum parameter and returns corresponding AudioChannel*).

Also, unfortunately, I noticed that users often misunderstand how audio channels work in AGS and may have wrong expectations of AudioChannel* pointers. I think it would be best if you explain what you are doing with them, then it will be easier to give any advice.

nightmarer

#2
Hello.

What I'm trying to do is creating a script with some functions to manage what is played in two dedicated channels for music out of the room's thread.
Why I want to do this? Because I'm trying to do some post production arangements in order to make the video game sounding like a real movie.

For example:
We have the character in one room, and that room is the entrance of the disco, so the music sounds as if it was being in next room, so when the door is opened (there's an animation for it),
the sound of the next room enters gradually.
So I have different music tracks:
-Next room with the door closed (Channel 1)
-Next room with the door opened (Channel 2)
Both songs are played as the character enters in the room, but Channel 2 Volume is set as 0.
So while the door is being opened it Channel 1 gradually changes to 0, and Channel 2 gradually changes to 100.
So it should sound as a real crossfade in audio.
When the character enters in this room it will sound a new track, but for this is only necessary one channel, but I need to send the position of this track in order to make it sound as if it was the same song being played. Do I make myself clear?

This is one of the functions that this script has. When I have this script finished I will share it with you.

I think that the array solution would work. How can I do it? Because I guess this should be the way to define it.
Code: ags
AudioChannel *myChannels[2];

But I'm not able to give them value.
Code: ags
myChannels[0] = bgm;
myChannels[1] = bgm2;

I'm always getting Parse eror: unexpected 'myChannels'
I'm getting the same error if I use it in this way:
Code: ags
myChannels[0] = *bgm;
myChannels[1] = *bgm2;


eri0o

There's a small (and incomplete) description of the system here that may be useful to you.

https://adventuregamestudio.github.io/ags-manual/MusicAndSound.html

If you want to manually do crossover, the Tween module has some functions that may be useful for you.

AGS does have crossover, if you have a single music channel, you can set it to do crossover everytime a new music is played - it's buried somewhere in sound clip properties. It will give at least three different durations for the crossover to happen.

nightmarer

#4
Thank you, but the Tween module has different functionalities that I'm trying to create.
Also I understand the methods of AudioChannel, I make them work them all.
What I'm trying to do now it to optimize the code I had already written.
I think that the solution comes from what Crimson Wizard mentioned.

Crimson Wizard

#5
Quote from: nightmarer on Wed 24/02/2021 08:19:28
I think that the array solution would work. How can I do it? Because I guess this should be the way to define it.
Code: ags
AudioChannel *myChannels[2];

But I'm not able to give them value.
Code: ags
myChannels[0] = bgm;
myChannels[1] = bgm2;

I'm always getting Parse eror: unexpected 'myChannels'


This would be normally a proper code, but where exactly do you put it? For example, if you put this outside of a function that would create error.

Also, do you assign bgm and bgm2 to anything before that? If you just declare AudioChannel* pointer that does not create a channel.

AGS has a fixed number of audio channels that are always created and present. They may be accessed with System.AudioChannels[] array.
Here's an explanation of AGS audio channels that I once wrote, I am linking it to every such discussion just in case:
https://www.adventuregamestudio.co.uk/forums/index.php?topic=54062.msg636546051#msg636546051

Khris

In general, the main use case for enums is to have convenient words instead of hard-to-memorize numbers in your code.
A basic example is
Code: ags
  player.FaceDirection(eDirectionLeft);  // instead of passing a number like 1


With regard to your own arrays, using enum constants instead of numerical array indices is of course possible, but since you can just name your audio channel pointers instead, it seems a bit pointless.

In a module you'd have something like this in the header:
Code: ags
enum ChannelIndex {
  bgmA,
  bgmB
};

import AudioChannel* myChannels[10];


And in the main script:
Code: ags
AudioChannel* myChannels[10];
export myChannels;


Now you can do this:
Code: ags
// inside game_start or some room function
  myChannels[bgmA] = someMusic.Play();


But again, this is pretty pointless, since you can just use a named pointer instead of the array.
In the context of a module you would probably want to provide functions that allow the user to conveniently manage background music without worrying about channels at all. The first step to design something like that is to write the code you want to be able to use, like this:
Code: ags
  Room.AddBGM(discoTrack1);

Next you write the functions that provide these commands, while implementing the necessary data structure.

nightmarer

Thank you very much, Khris.

Quote from: Khris on Wed 24/02/2021 12:53:31
But again, this is pretty pointless, since you can just use a named pointer instead of the array.
So if I use them as pointers then I don't know how and where can I define them as AudioChannel objects, because I'm not able to define pointers in the enum.

Quote from: Khris on Wed 24/02/2021 12:53:31
In the context of a module you would probably want to provide functions that allow the user to conveniently manage background music without worrying about channels at all. The first step to design something like that is to write the code you want to be able to use, like this:
Code: ags
  Room.AddBGM(discoTrack1);

Next you write the functions that provide these commands, while implementing the necessary data structure.

The problem is that some songs should be sounding in different rooms, so that is why I want to manage the Channel itself.

nightmarer

Quote from: Crimson Wizard on Wed 24/02/2021 12:24:35
This would be normally a proper code, but where exactly do you put it? For example, if you put this outside of a function that would create error.

The pointers were declared outside of any function in the firsts rows of the script.
Code: ags
AudioChannel *bgm;
AudioChannel *bgm2;


If it is an array then where I'm supposed to enter them?

Quote from: Crimson Wizard on Wed 24/02/2021 12:24:35
Also, do you assign bgm and bgm2 to anything before that? If you just declare AudioChannel* pointer that does not create a channel.

I'm  not assigning bgm or bgm2 before that.

Quote from: Crimson Wizard on Wed 24/02/2021 12:24:35
AGS has a fixed number of audio channels that are always created and present. They may be accessed with System.AudioChannels[] array.
Here's an explanation of AGS audio channels that I once wrote, I am linking it to every such discussion just in case:
https://www.adventuregamestudio.co.uk/forums/index.php?topic=54062.msg636546051#msg636546051

Thank you very much!

Crimson Wizard

#9
Quote from: nightmarer on Wed 24/02/2021 13:49:31
If it is an array then where I'm supposed to enter them?

In some function, for example you may use game_start for that.

Quote from: nightmarer on Wed 24/02/2021 13:49:31
Quote from: Crimson Wizard on Wed 24/02/2021 12:24:35
Also, do you assign bgm and bgm2 to anything before that? If you just declare AudioChannel* pointer that does not create a channel.

I'm  not assigning bgm or bgm2 before that.

If that's the case, then there's no point in assigning bgm and bgm2 to array at all, as that will just copy "null" values to array.


Quote from: nightmarer on Wed 24/02/2021 13:37:20
So if I use them as pointers then I don't know how and where can I define them as AudioChannel objects, because I'm not able to define pointers in the enum.

Note that you cannot create audio channel objects yourself. Only engine creates them. These objects are found in System.AudioChannel[] array. System.AudioChannelCount property tells how many are there.
Also, when you call audioclip.Play() that function returns audio channel pointer on which audio clip ended up.

So, when you are using AudioChannel* pointers either you assign your pointers to one of the System.AudioChannel elements, or you assign them to result of Play() function. Depending on what exactly you are trying to do.





EDIT: Ah, sorry, I missed your long explanation from above. I will have to re-read it all to better understand the problem.

Laura Hunt

Quote from: eri0o on Wed 24/02/2021 11:21:32
AGS does have crossover, if you have a single music channel, you can set it to do crossover everytime a new music is played - it's buried somewhere in sound clip properties.

Just wanted to point out that this is not correct; you actually need two channels (since a crossfade requires both tracks to be playing at the same time). If you only have one music channel, you will not be able to perform a crossfade and what will happen instead is that the first track will fade out and then the next track will fade in.

nightmarer

#11
Quote from: Crimson Wizard on Wed 24/02/2021 13:59:52
In some function, for example you may use game_start for that.
Ok, I'll try if it works.

Quote from: Crimson Wizard on Wed 24/02/2021 13:59:52
If that's the case, then there's no point in assigning bgm and bgm2 to array at all, as that will just copy "null" values to array.
Mmm, ok. So the array is not a solution.

Quote from: Crimson Wizard on Wed 24/02/2021 13:59:52
Note that you cannot create audio channel objects yourself. Only engine creates them. These objects are found in System.AudioChannel[] array. System.AudioChannelCount property tells how many are there.
Also, when you call audioclip.Play() that function returns audio channel pointer on which audio clip ended up.

So, when you are using AudioChannel* pointers either you assign your pointers to one of the System.AudioChannel elements, or you assign them to result of Play() function. Depending on what exactly you are trying to do.
Ok. What I'm trying to do eith this script is to manage two music channels. So I want in my functions to be able to asign a chanel.
Let me share with you part of the struct, so you will understand it better.

Code: ags
struct SetBGM {
  import static void Play(AudioClip *music, SetBGM_Channel = 1, int initialvol = 100, SetBGM_FadeIn fadeinmusic = Direct, int time = 0);
  import static void SetPosition(SetBGM_Channel = 1,  int time);
  import static int GetPosition(SetBGM_Channel = 1);
  import static void CrossFade(SetBGM_FromTo,  int time);
}


So this SetBGM_Channel parameter is the one that says the function which Channel is going to be used for that.
That is the reason I want to set this as an enum, to be more easy to write.

Regards.

eri0o

#12
Quote from: Laura Hunt on Wed 24/02/2021 14:38:42
Quote from: eri0o on Wed 24/02/2021 11:21:32
AGS does have crossover, if you have a single music channel, you can set it to do crossover everytime a new music is played - it's buried somewhere in sound clip properties.

Just wanted to point out that this is not correct; you actually need two channels (since a crossfade requires both tracks to be playing at the same time). If you only have one music channel, you will not be able to perform a crossfade and what will happen instead is that the first track will fade out and then the next track will fade in.

I meant when using the crossfade through set opt, not a manually implemented one. Maybe I am wrong but I meant when using the option below it uses only 1 music channel (it does magically spawns and kills a music channel behind the scenes, or reuses one of them, don't remember):

Code: ags
SetGameOption (OPT_CROSSFADEMUSIC, x); // where x= 0 (no), 1 (slow), 2 (slowish), 3 (medium) and 4 (fast)


Here's me in 2017: https://www.adventuregamestudio.co.uk/forums/index.php?topic=55456.0

nightmarer

Quote from: Laura Hunt on Wed 24/02/2021 14:38:42
Quote from: eri0o on Wed 24/02/2021 11:21:32
AGS does have crossover, if you have a single music channel, you can set it to do crossover everytime a new music is played - it's buried somewhere in sound clip properties.

Just wanted to point out that this is not correct; you actually need two channels (since a crossfade requires both tracks to be playing at the same time). If you only have one music channel, you will not be able to perform a crossfade and what will happen instead is that the first track will fade out and then the next track will fade in.

Yes, that's the point. I don't want to crossover or merge one audio to another, I want to crossfade two audios with the same song and different acoustic sound in order to create more realistic ambiance.

Crimson Wizard

#14
Quote from: nightmarer on Wed 24/02/2021 14:43:36
Ok. What I'm trying to do eith this script is to manage two music channels. So I want in my functions to be able to asign a chanel.

Code: ags
struct SetBGM {
  import static void Play(AudioClip *music, SetBGM_Channel = 1, int initialvol = 100, SetBGM_FadeIn fadeinmusic = Direct, int time = 0);
}


So this SetBGM_Channel parameter is the one that sais the function which Channel is going to be used for that.
That is the reason I want to set this as an enum, to be more easy to write.

I was suspecting you may be planning something like this, this is a common mistake. In AGS currently you cannot tell which channel to play clip on, AGS chooses one automatically and then Play returns which one was chosen.
If you reserve only 1 channel for music this is where it will play all the time, if you have more reserved it will take free one or replace a clip if new one has higher priority.

What you can do though is to assign resulting channel to your variables, this way you will at least know where the clips are playing and still may control their volume etc.
EDIT: Or maybe I misunderstood and this is actually what you are doing? ...

Laura Hunt

Quote from: eri0o on Wed 24/02/2021 14:50:34
Quote from: Laura Hunt on Wed 24/02/2021 14:38:42
Just wanted to point out that this is not correct; you actually need two channels (since a crossfade requires both tracks to be playing at the same time). If you only have one music channel, you will not be able to perform a crossfade and what will happen instead is that the first track will fade out and then the next track will fade in.

I meant when using the crossfade through set opt, not a manually implemented one. Maybe I am wrong but I meant when using the option below it uses only 1 music channel (it does magically spawns and kills a music channel behind the scenes, or reuses one of them, don't remember):

Code: ags
SetGameOption (OPT_CROSSFADEMUSIC, x); // where x= 0 (no), 1 (slow), 2 (slowish), 3 (medium) and 4 (fast)



No, it doesn't magically create a new channel; if you only have one music channel, the behaviour is as I described: first it will fade out the currently playing clip, and then, on the same channel, it will fade in the new one, rather than crossfading. I know because I recently tried doing this with a single music channel and that's what happened (I even verified it visually with a audio debug GUI I built).

Quote from: eri0o on Wed 24/02/2021 14:50:34
Here's me in 2017: https://www.adventuregamestudio.co.uk/forums/index.php?topic=55456.0

But... your last post in that thread actually confirms that you need two channels to perform a crossfade. Even if it's the built-in one.

nightmarer

Quote from: Crimson Wizard on Wed 24/02/2021 14:55:49
I was suspecting you may be planning something like this, this is a common mistake. In AGS currently you cannot tell which channel to play clip on, AGS chooses one automatically and then Play returns which one was chosen.
If you reserve only 1 channel for music this is where it will play all the time, if you have more reserved it will take free one or replace a clip if new one has higher priority.

What you can do though is to assign resulting channel to your variables, this way you will at least know where the clips are playing and still may control their volume etc.
EDIT: Or maybe I misunderstood and this is actually what you are doing? ...

If I'm not able to do asign music to a channel, then what is this code doing?
Because it worked so far.

Code: ags
AudioChannel *bgm;
void SetBGM.Play(AudioClip *music) { //Canal secundario de música
  if (bgm == null || bgm.PlayingClip != music) {
   bgm = music.Play();
  }
  else if(music == null) {
    music.Stop();
  }
}

Crimson Wizard

#17
Quote from: nightmarer on Wed 24/02/2021 15:09:03
If I'm not able to do asign music to a channel, then what is this code doing?
Because it worked so far.

Code: ags
AudioChannel *bgm;
void SetBGM.Play(AudioClip *music) { //Canal secundario de música
  if (bgm == null || bgm.PlayingClip != music) {
   bgm = music.Play();
  }
  else if(music == null) {
    music.Stop();
  }
}


This code takes the channel that engine chose, and stores its pointer in your variable. It's engine that puts music to *some* channel which it decided to, and then tells you what it is.

If this is what you expect to have, then it's working properly of course.

eri0o

@Laura Hunt

Ah, it's possible then! In the end I didn't use that option (I could not understand it) and made my own crossfade and stuff module that I have been using since - which requires indeed two audio channels! My use case is having different versions of the same music with exact same length and the need to do the crossfade.

Unfortunately the code is kinda kinda tricky (depends on a bunch of modules) so I never bothered packaging for others.

nightmarer

Quote from: Crimson Wizard on Wed 24/02/2021 15:10:53

This code takes the channel that engine chose, and stores its pointer in your variable. It's engine that puts music to *some* channel which it decided to, and then tells you what it is.

If this is what you expect to have, then it's working properly of course.

Ok, so we are back to the the first point. So AGS is doing it, ok.
Now, I need to tell AGS to assign a second channel for music.

Code: ags

AudioChannel *bgm;
AudioChannel *bgm2;
}

Then I guess that with this how it is done, right?

Now, how I would be able to enum these information in order to set them in a struct?

Laura Hunt

(eri0o: putting this under spoiler tags because I don't want to interfere with the thread any further)

Spoiler

Quote from: eri0o on Wed 24/02/2021 15:12:11
@Laura Hunt

Ah, it's possible then! In the end I didn't use that option (I could not understand it) and made my own crossfade and stuff module that I have been using since - which requires indeed two audio channels! My use case is having different versions of the same music with exact same length and the need to do the crossfade.

Unfortunately the code is kinda kinda tricky (depends on a bunch of modules) so I never bothered packaging for others.

Hey, I just did another test right now and it turns out I was wrong! Apparently AGS does somehow create a "ghost" channel or manages to mix both clips in the same music channel somehow in order for the crossfade to take place, but even with the help of my GUI I wasn't able to see this because even the "slow" fade is really fast, and if the volume of your tracks is lower than 100 the whole thing happens too quickly to even notice the fade and it feels and looks like one clip is playing right after the other. Apologies for the confusion! * embarrased face *

[close]

Crimson Wizard

#21
Well, speaking formally, if you'd like to tell a function to assign certain pointer from your collection, the most common way is make an array of these pointers and pass an index of the element.

So, instead of
Code: ags

AudioChannel *bgm;
AudioChannel *bgm2;


you would have
Code: ags

AudioChannel *bgm[2];


Then in the function:
Code: ags

void SetBGM.Play(AudioClip *music, int index) {
  if (bgm[index] == null || bgm[index].PlayingClip != music) {
   bgm[index] = music.Play();
  }
  else if(music == null) {
    music.Stop();
  }
}


If you don't like passing mere number, then of course you may create a enum, and fill it with values that correspond to array indexes, for example:
Code: ags

enum ChannelIndex {
  bgmA = 0,
  bgmB = 1
};



PS I think this is similar to what Khris proposed some time ago.

nightmarer

That totally works!
Thank you very much all for your help!
:) :) :) :) :) :) :)

SMF spam blocked by CleanTalk