LowPassFilter not working properly

Hi monkeys!

So, I am following “The Book”, and I’m stuck with the LowPassFilter exercise.

The exercise consists of applying the LowPassFilter to my wavesAudio node when the the camera goes up and down, to change the sound underwater. The code works. If I go underwater, the sound lowers. If i emerge, the sound comes back to normal. However, if I go underwater again, the sound doesn’t change.

So, I debugged the app and I found this when printing my two LowPassFilter’s:

  • When I go underwater and go up the first time, it prints this (the first one is underwaterFilter, the second is aboveWaterFilter):

      NativeLowPassFilter -1
      NativeLowPassFilter -1
    
  • When I repeat that, it starts printing this:

      NativeLowPassFilter 68
      NativeLowPassFilter 69
    

I think this is a weird bug. My code is exactly the same of the book example.

Source:

  • The part where I change filters

      @Override
      public void simpleUpdate(float tpf) {
          listener.setLocation(cam.getLocation());
          listener.setRotation(cam.getRotation());
          if(agua.isUnderWater() && !debaixoDeAgua) {
              aguaAudio.stop();
              debaixoDeAgua = true;
              aguaAudio.setDryFilter(debaixoDeAguaFiltro);
              System.out.println(debaixoDeAguaFiltro);
              aguaAudio.play();
          } else if(!agua.isUnderWater() && debaixoDeAgua) {
              aguaAudio.stop();
              debaixoDeAgua = false;
              aguaAudio.setDryFilter(acimaDeAguaFiltro);
              System.out.println(acimaDeAguaFiltro);
              aguaAudio.play();
          }
      }
    
  • The whole code

      package mygame;
    
      import com.jme3.app.SimpleApplication;
      import com.jme3.audio.AudioNode;
      import com.jme3.audio.Filter;
      import com.jme3.audio.LowPassFilter;
      import com.jme3.light.AmbientLight;
      import com.jme3.light.DirectionalLight;
      import com.jme3.material.Material;
      import com.jme3.math.ColorRGBA;
      import com.jme3.math.Vector3f;
      import com.jme3.post.FilterPostProcessor;
      import com.jme3.renderer.RenderManager;
      import com.jme3.scene.Geometry;
      import com.jme3.scene.Node;
      import com.jme3.scene.shape.Box;
      import com.jme3.util.SkyFactory;
      import com.jme3.water.WaterFilter;
    
      public class SonsDebaixoDeAgua extends SimpleApplication {
      
      private Node cenaRefletida;
      
      private AudioNode aguaAudio;
      private LowPassFilter debaixoDeAguaFiltro = new LowPassFilter(.5f, .1f);
      private LowPassFilter acimaDeAguaFiltro = new LowPassFilter(1, 1);
      private boolean debaixoDeAgua = false;
      
      private WaterFilter agua;
    
      public static void main(String[] args) {
          SonsDebaixoDeAgua app = new SonsDebaixoDeAgua();
          app.start();
      }
    
      @Override
      public void simpleInitApp() {
          DirectionalLight sol = new DirectionalLight();
          //sol.setDirection(new Vector3f(.3f, -.5f, -.5f));
          sol.setDirection(new Vector3f(-.39f, -.32f, -.74f));
          sol.setColor(ColorRGBA.White);
          rootNode.addLight(sol);
          
          cenaRefletida = new Node("Cena");
          rootNode.attachChild(cenaRefletida);
          
          cenaRefletida.attachChild(SkyFactory.createSky(assetManager, "Textures/Sky/Bright/BrightSky.dds", false));
          Material caixaMat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
          caixaMat.setTexture("DiffuseMap", assetManager.loadTexture("Interface/Monkey.png"));
          Box caixaMesh = new Box(2, 2, 2);
          Geometry caixaGeo = new Geometry("Caixinha", caixaMesh);
          caixaGeo.setMaterial(caixaMat);
          cenaRefletida.attachChild(caixaGeo);
          
          FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
          viewPort.addProcessor(fpp);
          agua = new WaterFilter(cenaRefletida, sol.getDirection());
          fpp.addFilter(agua);
          
          AmbientLight ambiente = new AmbientLight();
          ambiente.setColor(ColorRGBA.White);
          rootNode.addLight(ambiente);
          flyCam.setMoveSpeed(15);
          
          aguaAudio = new AudioNode(assetManager, "Sounds/Environment/Ocean Waves.ogg");
          aguaAudio.setLooping(true);
          aguaAudio.setPositional(false);
          aguaAudio.play();
          rootNode.attachChild(aguaAudio);
      }
    
      @Override
      public void simpleUpdate(float tpf) {
          listener.setLocation(cam.getLocation());
          listener.setRotation(cam.getRotation());
          if(agua.isUnderWater() && !debaixoDeAgua) {
              aguaAudio.stop();
              debaixoDeAgua = true;
              aguaAudio.setDryFilter(debaixoDeAguaFiltro);
              System.out.println(debaixoDeAguaFiltro);
              aguaAudio.play();
          } else if(!agua.isUnderWater() && debaixoDeAgua) {
              aguaAudio.stop();
              debaixoDeAgua = false;
              aguaAudio.setDryFilter(acimaDeAguaFiltro);
              System.out.println(acimaDeAguaFiltro);
              aguaAudio.play();
          }
      }
    
      @Override
      public void simpleRender(RenderManager rm) {
          //TODO: add render code
      }
      }
    

Btw @nehon, when I paste my code, I need to ident my code 8 spaces, instead of the supposed 4 spaces. I try to use the </> key on the editor, but it doesn’t work. Am I doing anything wrong?

Thanks,
Ev1lbl0w

The indent thing totally sucks. Better just to use the three back ticks ` before and after.

Well, I sincerely prefer:

  • This

      public class Game {
    
  • Than this…

    pulic class Game {

Add a carriage return after each triple back tick and you won’t have to care about indentation or this white crappy code highlighting.

Like this

public class Game{

Ok, thanks @nehon! Now, can someone help with my problem please?

I really need help guys…

You might be getting hit with a threading issue. When you stop() the audio, it actually stops on a separate thread. So when you set the filter to the new filter it’s setting it to the wrong channel, I guess. It’s kind of a bug in the AudioNode that it doesn’t transfer state when the new channel comes but you can work around it by not stopping/playing again. Changing the filter shouldn’t require restarting the playback, I guess.

I already tried without stopping the node. The same problem happens.

One thing that I can do is:

if(agua.isUnderWater() && !debaixoDeAgua) {
        aguaAudio.stop();
        debaixoDeAgua = true;
        aguaAudio.setDryFilter(new LowPassFilter(.5f, .1f));
        aguaAudio.play();
    } else if(!agua.isUnderWater() && debaixoDeAgua) {
        aguaAudio.stop();
        debaixoDeAgua = false;
        aguaAudio.setDryFilter(new LowPassFilter(1f, 1f));
        aguaAudio.play();
    }

And it works. However, this solution looks a little dirty since it’s not reusing the same LowPassFilter when I do this multiple times…

As a test, before setting the filter (in your original code) can you try calling setUpdateNeeded() on that filter?

So, I added that line to update the filters just before they are added to the AudioNode. It works! But isn’t that supposed to be “automatic”?

I sincerely think that audio in jmonkey is not finished. For example, how about a nice method about setRefDistance() and setMaxDistance()? Because even if i set ref to 50 and max to 1000, the sound can be heard from a long distance. I don’t get it.

Sure… it’s a bug. But now we know what the bug is.

That’s because you didn’t read the javadoc for those methods, I guess. setMaxDistance() is not doing what you think it is doing… but the javadoc explains it (hopefully) well.

Well, I know what the ref and max values does. But can you give a code to create two methods?

  • One that sets the distance when the sound is at 50% volume
  • The other sets the distance when the sound can’t be heard

Note: this is not what max distance is.

The way the math works, what you ask for is hard to calculate.

Leave max distance set to infinity and tweak ref distance until the sounds stops being heard from the distance you don’t want it heard. (Though recognize if the volume is up loud enough you will still hear it… see, that pesky math problem comes back again.)

If you want clipped sound then that is something you have to implement yourself as it implies a bunch of other things going on as well.

Ok, thanks for your help @pspeed!