Counting AudioChannels manually

Started by Monsieur OUXX, Tue 29/11/2016 12:00:44

Previous topic - Next topic

Monsieur OUXX

A thing is puzzling me : how does the number of channels vary?
- In AGS 3.4.x+ : Is System.AudioChannelCount constant? Even when clips are playing?
- In older AGS versions: what would be a good way of counting the channels?

I've tried as follows:
- All my AudioTypes have "MaxChannels"=0 (unlimited channels), except one. That one has MaxChannels=2.
- Then I run this function, using a clip that has an "unlimited channels" AudioType with MaxChannels=0.
Code: ags

//This function only makes sense before System.AudioChannelCount was introduced -- AGS 3.3.x

//MAX_AUDIOCHANNELS : any value higher than the expected result (probbaly should be 7 or 8)
#define MAX_AUDIOCHANNELS 9
void CountAudioChannels(AudioClip* testClip)
{
    AudioChannel* audioChannels[MAX_AUDIOCHANNELS];

    //We'll count the audiochannels by playing priority 1 sounds until one of them uses a previously-used channel
    //IMPORTANT: this clip's Audiotype must have a setting that allows to use an unlimited number of channels (max channel = 0)
    AudioChannel* firstChannel = testClip.Play(eAudioPriorityVeryHigh, eOnce);
    audioChannels[0]= firstChannel;
    AudioChannel* otherChannel = testClip.Play(eAudioPriorityVeryHigh, eOnce);

    nbAudioChannels = 1;
    while (otherChannel != firstChannel)
    {
        audioChannels[nbAudioChannels] = otherChannel;
        nbAudioChannels++;
        otherChannel = testClip.Play(eAudioPriorityVeryHigh, eOnce);
    }
    #endif

    Display("Counted %d AudioChannels.", nbAudioChannels);
}



the result is 5. When al my AudioTypes have MaxChannels=0, the same function returns 7. So I infere that 5 is probably the result of 7-2.
So does that mean that the AudioType that has MaxChannels=2 reserves the channels? I thought that would mean it can use 2 channels at most.
 

Snarky

Quote from: Monsieur OUXX on Tue 29/11/2016 12:00:44
So does that mean that the AudioType that has MaxChannels=2 reserves the channels?

Yes.

Monsieur OUXX

Quote from: Snarky on Tue 29/11/2016 12:07:54
Quote from: Monsieur OUXX on Tue 29/11/2016 12:00:44
So does that mean that the AudioType that has MaxChannels=2 reserves the channels?

Yes.
Damn. Thanks.
At the moment I'm trying to find tricks to reduce the waste of channels as much as possible.
 

Crimson Wizard

#3
I am not so sure about them reserving channels, I kept thinking this is the number of most used per type at any given moment. This might need clarification.

Quote
In AGS 3.4.x+ : Is System.AudioChannelCount constant? Even when clips are playing?
As I was explaining before, the number of audio channels never changes. And it currently should be 8 always, since there is no way to configure their count.
They are like slots. Or available chairs in the room where you sit people (clips). Their number does not change when people sit down or stand up.


Quote
In older AGS versions: what would be a good way of counting the channels?
Quote
//This function only makes sense before System.AudioChannelCount was introduced -- AGS 3.3.x
No, this information is incorrect, channels and respective properties exist since AGS 3.2.0.

Quote from Changes.txt:
Quote
VERSION 3.2, June 2010
- Rewrote audio system. The old number-based sound and music are gone, replaced with
   new named audio clips. Added AudioClip.*, AudioChannel.*, Game.IsAudioPlaying,
   Game.SetAudioTypeVolume, Game.StopAudio, System.AudioChannels, System.AudioChannelCount,
   System.Volume, ViewFrame.LinkedAudio script commands.

In earlier versions there were only 3 or 4 fixed channels for every sound type (speech, music, sound, and possibly separate one for ambient sound), and users had no control over them.

Snarky

#4
Quote from: Crimson Wizard on Tue 29/11/2016 12:17:18
I am not so sure about them reserving channels, I kept thinking this is the number of most used per type at any given moment. This might need clarification.

That would be better, but it's not how the system works (at least as of 3.3.3).

-Every AudioType reserves at least 1 channel (even if MaxChannels is set to 0) (Wrong)
-If MaxChannels is set to some other number, the AudioType reserves that many channels

Method of testing:

With default game, three audio types (Ambient, Music, Sound). The game tries to play first one music track, and then another at the same time, without using Ambient or Sound at all.

1. Set MaxChannels to 0 for Music and Sound, 1 for Ambient. Result: Both music tracks play, as expected.
2. Set MaxChannels to 0 for Music and Sound, 7 for Ambient. Result: No music, because the setup tries to assign 9 channels, which doesn't work. (actually because 7 for ambient + 1 for speech leaves none for any other type of audio)
3. Set MaxChannels to 0 for Music and Sound, 6 for Ambient. Result: The first music track plays. On trying to play the second, the first one stops.

The third test demonstrates that having assigned 6 channels to Ambient (and implicitly 1 for Sound one reserved for speech), there is only one audio channel left to play music.

Alan v.Drake

Heh, perhaps we should just rename MaxChannels to ReservedChannels, we always end up realizing that it reserves channels, but the next time it comes up no one remembers and everyone is in doubt again.
It's a vicious cycle. :-D

- Alan

Monsieur OUXX

#6
I've written a module that counts and lists almost everything that's possible to count (AudioTypes, AudioChannels, AudioClips, reserved channels, etc.), even in AGS 3.3.x.
Retrospectively, I agree that the channels are not so hard to understand, but they're confusing at first. Writing this module helped me a great deal to pay attention to aspects of channel management that seem straightforward if you don't pay enough attention but are actually quite subtle.

I'll publish it in the next few days. There's a graphical interface to it to visualize what's going on, and everything is named instead of using numbers. It should help beginners a lot.

 

Crimson Wizard

#7
Quote from: Snarky on Tue 29/11/2016 12:31:40
-Every AudioType reserves at least 1 channel (even if MaxChannels is set to 0)
-If MaxChannels is set to some other number, the AudioType reserves that many channels
Quote from: Alan v.Drake on Tue 29/11/2016 20:55:49
Heh, perhaps we should just rename MaxChannels to ReservedChannels, we always end up realizing that it reserves channels,

Then I guess they work in both ways: as reserving AND as limiting. That is if you put 1 channel for music, every next music will replace that one, even if you have free channels.
In other words that's rather something like "designated channels".

If channels = 0, then that audio will take any available non-designated channel.

Snarky

#8
The system makes sense on its own terms, but could be explained a lot better in the manual.

As far as I'm concerned, the big gotcha is that even if you set its MaxChannels to 0, each AudioType still reserves 1 channel for its exclusive use. (Edit: Wrong)

Crimson Wizard

Quote from: Snarky on Wed 30/11/2016 01:11:07
As far as I'm concerned, the big gotcha is that even if you set its MaxChannels to 0, each AudioType still reserves 1 channel for its exclusive use.
I this is true for custom types? I think there are 3 built-in types that reserve channels  unconditionally, which is done for backwards-compatibility (functions like PlayMusic).

Monsieur OUXX

#10
Quote from: Snarky on Wed 30/11/2016 01:11:07
even if you set its MaxChannels to 0, each AudioType still reserves 1 channel for its exclusive use.

I haven't observed that with my custom type.
I'll need to try that with built-in types while some AudioClips (that use the built-in types) play .

 

Snarky

#11
Quote from: Crimson Wizard on Wed 30/11/2016 08:45:06
I this is true for custom types? I think there are 3 built-in types that reserve channels  unconditionally, which is done for backwards-compatibility (functions like PlayMusic).

It's not true at all, I was wrong.

I was inferring the reservation of an AudioChannel per AudioType from the fact that when setting MaxChannels to 6+0+0, one of the "unlimited" types was only able to use one AudioChannel. However, upon further testing it seems like there are actually only 7 AudioChannels available for use by the AudioTypes, since one is always reserved for speech, so my logic was off. (If there is a way to free up this channel, I couldn't find it. It's off-limits even if you set Speech.VoiceMode=eSpeechTextOnly)

In fact, AudioTypes for which MaxChannels is set to 0 do not reserve any AudioChannels, at least not under any circumstances I could find.

Here's a snippet of code to examine the audio channel status (it uses a debug GUI with a label lblStatus), which goes in repeatedly_execute_always():

Code: ags
// Displays the status of the audio channels in format '[Channel#]: [AudioType] - [ClipName/SpeakingCharacter]'
   String status="";
   int i=0;
   while(i<System.AudioChannelCount)
   {
      String clipType="NONE";
      String clipName="";
      AudioChannel* channel = System.AudioChannels[i];
      if(channel != null && channel.IsPlaying)
      {
         AudioClip* clip = channel.PlayingClip;
         if(clip == null) // If the channel is playing but the clip is null, it means it's playing speech
         {
            clipType="Speech";
            // Figure out which character is speaking
            int j=0;
            while(j<Game.CharacterCount)
            {
               if(character[j].Speaking)
                  clipName=character[j].Name;
               j++;
            }
         }
         else
         {
            if(clip.Type == eAudioTypeAmbientSound)
               clipType="Ambient";
            else if(clip.Type == eAudioTypeSound)
               clipType="Sound";
            else if(clip.Type == eAudioTypeMusic)
               clipType="Music";
            else if(clip.Type == eAudioTypeCustom)  // This is a custom audio type I created for testing
               clipType="Custom";
            else clipType = "Unknown";
            
            // There's no way to get the name of a clip, so if we need to identify it specifically, we have to special-case the check
            if(clip==aLostCraftHowl) clipName="Aircraft";
            else if(clip==aElectricNoise) clipName="Electric";
            else if(clip==aRadioNoise) clipName="Radio";
            else if(clip==aTone_01) clipName="Music1";
            else if(clip==aTone_02) clipName="Music2";
            else clipName="Unknown";
         }
      }
      String line = String.Format("%d: %s - %s[",channel.ID, clipType, clipName);
      status = status.Append(line);
      i++;
   }
   lblStatus.Text=status;


Snarky

With this debug code, I was able to spot what I think must be a bug in the AGS sound system: If you try to play a clip when you've run out of AudioChannels for that AudioType, so that it has to preempt a currently playing clip, it actually stops all copies of that clip (at least sometimes). This explains some weird behavior that a number of people have noticed but not been able to fully describe in the past (including bugs with the applause system in the AGS Awards Ceremony, and possibly the problems Monsieur OUXX was having that started this whole thing in the first place).

I'm using the two-click game template default game, and I've added this code in Room 1:

Code: ags
#define MAXCHANNELS_SOUND 5    // This matches the Sound AudioType setup
int soundTracks=0;

function hTeeVee_Interact()
{
   soundTracks++;
   if(soundTracks%2==0)
      aBirthday.Play(eAudioPriorityHigh, eRepeat);    // AudioType Sound
   else
      aLostCraftHowl.Play(eAudioPriorityHigh, eRepeat); // AudioType Sound
}

function hTeeVee_Look()
{
   aLostCraftHowl.Stop();
   aBirthday.Stop();
   soundTracks=0;
}


So each time I click on the TV, it starts playing another copy of one of the sound clips. The first five times, it correctly alternates between the two clips. But then on the sixth time (i.e. once we've run out of channels), things break. Usually, it will stop all the channels playing aLostCraftHowl (so you end up with three channels playing aBirthday and two playing nothing). Sometimes it will do the right thing (stop only one of the channels), in particular after the first time: if you keep playing more clips, it won't (ever?) happen again even once you run out of the channels that were freed up by the clips that stopped playing, but if you stop all the clips and start over, it does recur.

I've tested a few variations. Most notably, the behavior differs if you switch the audio clips around, which indicates it has something to do with the clip itself. As far as I can tell, it has to do with the clip being preempted (not the one you're trying to play): in this case, preempting the clip aLostCraftHowl tends to stop all copies of the clip, while preempting aBirthday is (usually?) OK.

It doesn't matter whether the channel limit is set explicitly by MaxChannels or implicitly by how many are left over from other audio types. It doesn't matter if the clips are playing on eRepeat or eOnce.

Monsieur OUXX

- I'm relieved to read that there isn't an implicitly-reserved channel for the built-in AudioTypes that have MaxChannels=0. That would have been one more irregularty to manage :-D
- I don't think the potential bug you've observed is what made me start my investigations; I was just too stupid to spot that the "pointers" to AudioChannels are constant even if the channel starts playing something new.
- However, yes this is potentially a bug -- except if CW manages to shade some light onto this behaviour.

 

Snarky

I thought of some further testing to do, and as far as I can tell, it's actually a problem not with running out of channels as such, but with stopping/preempting channels playing certain clips. In this case, it seems like all copies of the clip aLostCraftHowl (which is a WAV file) will usually stop playing if you stop or preempt any channel that is playing the clip (even if you preempt it with itself).

I still haven't figured out the conditions under which it doesn't break.

Monsieur OUXX

#15
OK; good to know thanks

EDIT:
Quote from: Snarky on Wed 30/11/2016 16:40:39
it's actually a problem with stopping/preempting channels playing certain clips. In this case, it seems like all copies of the clip stop playing if you stop or preempt any channel that is playing the clip (even if you preempt it with itself).
Could it be that the engine internally calls its internal implementation of AudioClip::Stop, which (by design) stops all instances of the playing AudioClip?

 

SMF spam blocked by CleanTalk