Null pointer error (audio channel/sound clip)

Started by Honza, Tue 27/12/2022 09:17:12

Previous topic - Next topic

Honza

I'm getting this error message on this line of code:



I'm referencing a null pointer when specifically checking for null pointers?!

Music is an AudioClip, music1 is an AudioChannel, they're both defined in Global variables. Sorry if I'm not providing enough context, not sure what is relevant here. Thanks to anyone who can help! Trying to handle sound in AGS has been giving me a headache.

Snarky

You would get this error if music1 is null, which you do not check for.

Honza

I arrived at the same thing by trial and error in the meantime, but I guess I still don't understand how it works :). Now I'm having trouble with this line of code for instance:

Code: ags
else if (Music != null && music1 != null) {
    if (Music.ID != music1.PlayingClip.ID) music1 = Music.Play (eAudioPriorityNormal, eRepeat);
    }

Again, I would get a null pointer error on the second line occasionally. But if music1 isn't null, wouldn't it mean that it must be playing some instance of a clip, so music1.PlayingClip.ID can't be null either? Or what is the null pointer I'm referencing this time? How many potential null pointers do I have to check every time I want to play new music? :)

Snarky

#3
No, AudioChannels are persistent throughout the game, so they can be non-null without any playing clips.

The analogy I use is that AudioChannels are like individual cassette players, and AudioClips are like tapes that you load and play on them.

As for your null checks, it depends on your logic. If you set things up properly, I would think that you could ensure neither Music nor music1 could be null (as long as audio is available at all; AGS does not deal well if the system does not support sounds).

Khris

Yes, the error here means that music1.PlayingClip is null.

In general, if you need to read a.b.c.d, you usually need to do if (a != null && a.b != null && a.b.c != null) first.

However in this specific case you should be able to get away with testing if Music != music.PlayingClip, which should work just like the ID comparison but makes checking for music.PlayingClip not being null first unnecessary.

eri0o

I suggest using names like ach_mus and aMusic for audio channel and the audio clip respectively to improve readability a bit.

Honza

So how can a channel become null? I assumed it was when it hasn't been assigned a clip or has stopped playing one, but if that's not the case, then I'm not sure I understand what a channel being null means :-[.

Thank you all for the patience and helpful tips otherwise :).

eri0o

Any problems in opening the audio device can cause the audio channel to be null. Say you are in a desktop computer, with no Speakers or Headphones connected to it, and using Windows, Windows will tell that no audio devices are available, and then AGS will fail to open the audio device. So if you try to play something, there will be no channel to assign the clip as it fails to play.

I don't remember correctly, but I think if the game uses external vox packages but they are missing for some reason, it returns null too.

Honza

I think I got it (maybe? :)). I conflated a channel being null with a channel not playing anything. But my channel was probably null because I hadn't played anything on it yet, so it hadn't been assigned one of the 16 permanent game channels. Would that make sense?

eri0o

Ah, good question, I don't remember if the PlayingClip property is set in the same frame or only in the next one.

Crimson Wizard

#10
Quote from: Honza on Tue 27/12/2022 12:14:20I think I got it (maybe? :)). I conflated a channel being null with a channel not playing anything. But my channel was probably null because I hadn't played anything on it yet, so it hadn't been assigned one of the 16 permanent game channels. Would that make sense?

Indeed, if you have not assigned anything to a pointer, it would be null. Every pointer starts as null.

Like Khris mentioned above, if you have a "chained" structure like Channel.PlayingClip.ID, then you have to test for every nested pointer there that theoretically may be null: Channel != null, then Channel.PlayingClip != null, and so on.

Quote from: eri0o on Tue 27/12/2022 12:19:23Ah, good question, I don't remember if the PlayingClip property is set in the same frame or only in the next one.

I don't think this is what Honza was asking about.
But to give an answer, PlayingClip should have a valid return value immediately after Play() is called.

Honza

Yep, I imagined music1 != null and music1.PlayingClip != null was essentially testing for the same thing and would be redundant to do both, now I get the difference between them. This is the new code and it seems to be working:

Code: ags
  if (music1 == null) {
    if (Music != null) music1 = Music.Play (eAudioPriorityNormal, eRepeat);
    }
    
  else {
    if (Music != null && music1.PlayingClip != null) {
      if (Music != music1.PlayingClip) music1 = Music.Play (eAudioPriorityNormal, eRepeat);
      }
    else if (Music != null && music1.PlayingClip == null) music1 = Music.Play (eAudioPriorityNormal, eRepeat);
    else if (Music == null && music1.PlayingClip != null) music_fadeout = true;
    }

Khris

Yes, music1 is an AudioChannel* pointer. If it's not pointing at null but an actual AudioChannel, you can now read (or set) the properties of this channel by referring to music1.

So if music1 isn't pointing at null, music1.PlayingClip can safely be accessed. It in turn is an AudioClip* pointer. It can also point to null though, if the AudioChannel isn't playing anything currently. So now we need to check for music1.PlayingClip not being a null pointer.

If music1.PlayingClip isn't pointing at null, we can now safely access music1.PlayingClip.ID.

You could do the check the long way like this:

Code: ags
  if (music1 != null) {
    AudioClip* clip = music1.PlayingClip;
    if (clip != null) {
      int id = clip.ID;
      // compare id, etc.
    }
  }

Honza


eri0o

music1 is a bad name for a variable that represents the audio channel, this makes the code really confusing.  :-\

Honza

#15
Quote from: eri0o on Tue 27/12/2022 19:37:14music1 is a bad name for a variable that represents the audio channel, this makes the code really confusing.  :-\

Okay, I'll consider sticking to the "ach" prefix for channels you suggested before :).

SMF spam blocked by CleanTalk