Questions regarding looping sounds using ZGE Sound

All topics about ZGameEditor goes here.

Moderator: Moderators

Post Reply
User avatar
Ats
Posts: 865
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Questions regarding looping sounds using ZGE Sound

Post by Ats »

Since the very beginning of my project, I set up some kind of noise as a motor sound and then triggered it repeatedly using something like:

Code: Select all

int Sound_Channel = 0;
void ChangeSoundChannel()
{
  Sound_Channel++;
  if (Sound_Channel > 3) Sound_Channel = 0;
}

void PlaySound(Sound snd, vec3 pos, float vmax, float note)
{
  snd.Pan = clamp(0.5 + (pos.X - CameraGame.Position.X) * 0.04, 0.0, 1.0);
  snd.Volume = clamp(vmax - abs(pos.Z  - CameraGame.Position.Z) / 600, 0.0, vmax);

  ChangeSoundChannel();
  if (note) @PlaySound(Sound:snd, NoteNr: note + (pos.Z - CameraGame.Position.Z) / 3, Channel: Sound_Channel);
  else      @PlaySound(Sound:snd, Channel: Sound_Channel);
}
It worked fine until I realized it didn’t sound the same at all on different devices, and I had to admit how bad I am with the ZGE Sound component.

First off, do I really need to switch channels every time I play a sound?
Now that I’m using Sunvox for music and SFXR for sound effects, I’m not sure if that’s still relevant.

As for the motor sound (the classic ZGE Sound), I guess it plays whenever it can: on each frame, at whatever framerate the device is running, and only if a sound channel is free. So how can I make sure it sounds consistent across all devices?

Should I use some kind of App.DeltaTime timer to control when the sound is played? And then tweak its duration so it loops smoothly, without overlapping or blowing out the speakers on small Android devices?
User avatar
Kjell
Posts: 1950
Joined: Sat Feb 23, 2008 11:15 pm

Re: Questions regarding looping sounds using ZGE Sound

Post by Kjell »

Hi Ats,

The easiest way to have a sound loop forever ( until you tell it to stop ) is to use PlaySound.ByReference and set the length to INF. Here's a simple example ( hold "S" key ).

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnUpdate>
    <KeyPress Keys="S">
      <OnKeyUp>
        <ZExpression>
          <Expression>
<![CDATA[// Stop sound by setting length to 0.

NoiseSound.Length = 0;]]>
          </Expression>
        </ZExpression>
      </OnKeyUp>
      <OnKeyDown>
        <ZExpression>
          <Expression>
<![CDATA[// Since ZGE doesn't have a INF constant, cast the bits for INF instead.

NoiseSound.Length = reinterpret_cast<float>(0x7F800000);]]>
          </Expression>
        </ZExpression>
        <PlaySound Sound="NoiseSound" NoteNr="40" ByReference="255"/>
      </OnKeyDown>
    </KeyPress>
  </OnUpdate>
  <Content>
    <Sound Name="NoiseSound" Volume="1" Osc1Waveform="2"/>
  </Content>
</ZApplication>
K
User avatar
Ats
Posts: 865
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Questions regarding looping sounds using ZGE Sound

Post by Ats »

Thanks, Kjell!

After a lot of tries, I finally managed to get something working without it sounding like a Geiger counter.
I had to modify the BaseNoteNr, because once the sound is launched with a NoteNr, it can’t be changed afterward.

So here’s my little sound engine toy, based on your example:

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZExpression>
      <Expression>
<![CDATA[PlayerSpeed = 0;
PlayerSpeedMin = 55;
PlayerSpeedMax = 200;]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[if (Accelerate) PlayerSpeed += (PlayerSpeedMax - PlayerSpeed) * App.DeltaTime;
else PlayerSpeed += (PlayerSpeedMin - PlayerSpeed) * 4 * App.DeltaTime;
//trace(intToStr(PlayerSpeed));

Sound_Acceleration.BaseNoteNr = clamp(PlayerSpeed, 0, 100) * 4;
NoiseSound.BaseNoteNr = clamp(PlayerSpeed * 0.4, 0, 80);

Accelerate = 0;]]>
      </Expression>
    </ZExpression>
    <KeyPress Comment="S" Keys="S">
      <OnPressed>
        <ZExpression Expression="Accelerate = 1;"/>
      </OnPressed>
      <OnKeyUp>
        <ZExpression>
          <Expression>
<![CDATA[// Stop sound by setting length to 0.

NoiseSound.Length = 0;]]>
          </Expression>
        </ZExpression>
      </OnKeyUp>
      <OnKeyDown>
        <ZExpression>
          <Expression>
<![CDATA[// Since ZGE doesn't have a INF constant, cast the bits for INF instead.

NoiseSound.Length = reinterpret_cast<float>(0x7F800000);


@PlaySound(
  Sound: NoiseSound,
  NoteNr: 0,
  ByReference: 1
);]]>
          </Expression>
        </ZExpression>
      </OnKeyDown>
    </KeyPress>
    <KeyPress Comment="D" Keys="D">
      <OnPressed>
        <ZExpression>
          <Expression>
<![CDATA[Accelerate = 1;

if (App.Time > timer_acceleration) {
  // To avoid repeting the sound like crazy on high framerate devices
  Timer_acceleration = App.Time + 0.01;

  // Max volume is low because sounds overlaps and cumulate volume
  Sound_Acceleration.Volume = clamp(PlayerSpeed * 0.01, 0, 0.3);

  @PlaySound(
    Sound: Sound_Acceleration,
    NoteNr: 0//clamp(PlayerSpeed, 0, 100) * 4
  );
}]]>
          </Expression>
        </ZExpression>
      </OnPressed>
    </KeyPress>
  </OnUpdate>
  <Content>
    <Sound Name="NoiseSound" Length="0" Volume="1" BaseNoteNr="22" Osc1Waveform="2"/>
    <Sound Name="Sound_Acceleration" Length="0.5" Volume="0.3" BaseNoteNr="220.0001" Osc1Waveform="2" UseFilter="1" FilterCutoff="0.1598" FilterQ="0.7992" Mod0Active="1" Mod0Destination="11" Mod0Amount="1" Mod1Active="1" Mod1Destination="2" Mod1Amount="0.15" Mod2Active="1" Mod2Source="1" Mod2Destination="1" Mod2Amount="1" Env0Active="1" Env0AttackTime="2.12" Env0DecayTime="5" Env0ReleaseTime="5"/>
    <Variable Name="timer_acceleration"/>
    <Variable Name="PlayerSpeed"/>
    <Variable Name="PlayerSpeedMin"/>
    <Variable Name="PlayerSpeedMax"/>
    <Variable Name="Accelerate" Type="4"/>
  </Content>
</ZApplication>
Press D to play the sound the way I used it for years in Omeganaut. I added a timer so that the pitch and volume no longer vary depending on the hardware.
Press S to play your noise sound, with a note effect tied to the acceleration. I’m still unsure how to handle deceleration though.
Should I set the length to 0.5 upon release and add an envelope?
Or should I keep playing the sound constantly, regardless of whether the player is accelerating?

Also, both sounds can play at the same time without issues. So I really wonder why I implemented all those Sound_Channel switches in the first place… I don’t think Sound_Channel does what I originally thought it did :?


Edit:
By the way, there is a bug in preview mode where, if we run, play the D sound, stop, wait a bit, and run again, the D sound isn't working anymore. It's not always the case. And the only way I managed to recover from that is to close and reopen ZGameEditor...


Edit 2:
I just realized I could do the timer thing simply by setting ReplayDelay:

Code: Select all

  @PlaySound(
    Sound: Sound_Acceleration,
    ReplayDelay: 0.01,
    NoteNr: 0
  );
User avatar
Kjell
Posts: 1950
Joined: Sat Feb 23, 2008 11:15 pm

Re: Questions regarding looping sounds using ZGE Sound

Post by Kjell »

Hi Ats,
Ats wrote: Wed Oct 22, 2025 8:31 amI’m still unsure how to handle deceleration though.
Depends a little on how you want it to work / sound. One approach is to do something like this ( hold "S" key ) ..

Code: Select all

<?xml version="1.0" encoding="iso-8859-1" ?>
<ZApplication Name="App" Caption="ZGameEditor application" FileVersion="2">
  <OnLoaded>
    <ZExpression>
      <Expression>
<![CDATA[//

EngineSound.Length = 0;]]>
      </Expression>
    </ZExpression>
  </OnLoaded>
  <OnUpdate>
    <ZExpression>
      <Expression>
<![CDATA[//

if(EngineSound.Length)
{
  EngineSound.BaseNoteNr += (Accelerate ? (64 - EngineSound.BaseNoteNr) : -16) * App.DeltaTime;

  // Stop sound when note is below 0

  if(EngineSound.BaseNoteNr < 0)
  {
    EngineSound.BaseNoteNr = 0;
    EngineSound.Length = 0;
  }
}]]>
      </Expression>
    </ZExpression>
    <KeyPress Keys="S">
      <OnKeyUp>
        <ZExpression>
          <Expression>
<![CDATA[//

Accelerate = 0;]]>
          </Expression>
        </ZExpression>
      </OnKeyUp>
      <OnKeyDown>
        <ZExpression>
          <Expression>
<![CDATA[//

Accelerate = 1;

// Start sound if needed

if(!EngineSound.Length)
{
  EngineSound.Length = reinterpret_cast<float>(0x7F800000);
  @PlaySound(Sound: EngineSound, ByReference: 1);
}]]>
          </Expression>
        </ZExpression>
      </OnKeyDown>
    </KeyPress>
  </OnUpdate>
  <Content>
    <Variable Name="Accelerate" Type="4"/>
    <Sound Name="EngineSound" Length="0"/>
  </Content>
</ZApplication>
Ats wrote: Wed Oct 22, 2025 8:31 amAlso, both sounds can play at the same time without issues. So I really wonder why I implemented all those Sound_Channel switches in the first place… I don’t think Sound_Channel does what I originally thought it did :?
Channels are primarily for when you're using the AudioMixer component. There's no limit to the amount of sounds playing on the same channel simultaneously.

K
User avatar
Ats
Posts: 865
Joined: Fri Sep 28, 2012 10:05 am
Contact:

Re: Questions regarding looping sounds using ZGE Sound

Post by Ats »

Nice way to handle the infinite length and volume.
Kjell wrote:There's no limit to the amount of sounds playing on the same channel simultaneously.
That will simplify a lot of things in my game. I don't know why, I really thought a channel could only play one sound at a time :lol:
Post Reply