0

I've started to make a basic game in Java just as something to do.

Its all been going well, I have maps loading, drawing works fine etc.

I have it playing music for the maps and sound effects for stuff like collision.

However I feel like the music from MAP A(The one I am leaving) should fade into the music from MAP B(The map I am entering), is this possible?

The code im using to play the music is below:

    try
    {
        if(clip != null)
        {
            clip.stop();
            clip.close();
        }

        clip = AudioSystem.getClip();

        URL a = this.getClass().getResource("/resource/sound/"+Filename + ".mid");

        ais = AudioSystem.getAudioInputStream(a);

        clip.open(ais);
        clip.loop(999);
        clip.start();
    }
    catch(Exception e)
    {

    }

As you can most probably see I am not very good in Java, but my game is going well so far and I am not, and I refuse to use an 'Engine' as I want complete control.

Any ideas?

Thanks

EDIT:

Thanks for all the answers, I have modified the code you gave me, but its not working =/

I have now made the code below, which is run on a new thread(hence the Runnable) so the music will fade while playing.

The code is below:

private Boolean _SwapMusic = false;
private String _Filename;
Thread soundThread;

    Runnable r1 = new Runnable() {
      public void run() {
        while(true)
        {
            if(_SwapMusic)
            {
                _SwapMusic = false;
                try
                {
                    //Try to lower volume
                    for (int i = 0; i < 80; i ++) {
                        FloatControl gainControlA = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
                           gainControlA.setValue(i * -1f);
                        Thread.sleep(10);
                    }
                }
                catch(Exception e){ System.out.print("Lower volume error: " + e + " ");}


                try{
                    clip.stop();
                    clip.close();
                }
                catch (Exception e) { System.out.print("clip.stop/close error: " + e + " ");}
                try
                {
                    clip = AudioSystem.getClip();

                    URL a = this.getClass().getResource("/resource/sound/"+ _Filename + ".mid");

                    ais = AudioSystem.getAudioInputStream(a);

                    clip.open(ais);

                    FloatControl gainControlB = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
                    gainControlB.setValue(-80f);

                    clip.loop(999);
                    clip.start();
                }
                catch(Exception e){ System.out.print("Play error: " + e + " ");}


                try
                {
                    //Try to higher volume
                    for (int i = -80; i < 0; i ++) {
                        FloatControl gainControlC = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
                           gainControlC.setValue(i);
                        Thread.sleep(3);
                    }
                }
                catch(Exception e){ 
                System.out.print("Higher volume error: " + e + " ");
                }
            }         
        }
      }
    };

Just to make this clear what I am trying to make music from MAP A fade completely before the music for MAP B begins.

Thanks for all help so far.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Ryuk
  • 95
  • 1
  • 5
  • 16

3 Answers3

1

You can use the MASTER_GAIN control to lower the sound of the first clip while you raise the sound of the second, you may have to play around with the dB and timings but this should work:

Clip clipA = AudioSystem.getClip();
Clip clipB = AudioSystem.getClip();
clipA.open(aisA);
clipB.open(aisB);

for (int i = 0; i < 100; i ++) {
   FloatControl gainControlA = (FloatControl) clipA.getControl(FloatControl.Type.MASTER_GAIN);
   gainControlA.setValue(i * -1f);
   FloatControl gainControlB = (FloatControl) clipB.getControl(FloatControl.Type.MASTER_GAIN);
   gainControlB.setValue(-100f + i);

   Thread.sleep(100);
}
Garrett Hall
  • 29,524
  • 10
  • 61
  • 76
  • Thanks, I have edited my original post with more information. – Ryuk Jul 18 '12 at 18:41
  • I had some invalid arguments... Apparently the setValue() will only accept -80 <-> 6.02! I have edited the original post again, but the song doesn't fade... It just starts. – Ryuk Jul 18 '12 at 18:58
  • Change the `sleep` to 100-1000 milliseconds, if it's 10 milliseconds the fade will happen in less than a second. – Garrett Hall Jul 18 '12 at 19:06
1

This is not meant to be an answer so much as to add some possibly useful information that won't fit in a comment.

Yes, the -80 to 6.0 is kind of funky. It is supposed to duplicate decibels on an outboard mixer, I think. But the slope to the ear is hardly linear. If I recall correctly by the time you get down to -20 the sound may already be inaudible. Between that and the performance problems of trying to do real-time precision in volume changes I abandoned the use of control lines and started working with the PCM data directly. But I'm NOT recommending you do that in this instance.

To make the number range of the control more evenly gradual, you can map some sort of exponential or trig function. I don't know the best fit, but I've gotten useful results with the following two functions.

With PCM data, I've been playing around with volume normalized to the range 0 to 1. Here are some examples of functions I've been using.

newValue = 1 - Math.cos(NormalizedPCMValue * PI/2)

newValue = Math.pow(NormalizedPCMValue, 6) 

newValue = Math.pow(2, NormalizedPCMValue)

These all work better for me than straight linear. I'm using lookup tables but you could probably calculate the values on the fly. If you get a mapping function that works really nicely, I'd love to hear about it here.

By the way, it is good to actually take the control line value down to the bottom of the range. Abruptly cutting off the sound, even when it is quiet, or jumping large distances in a volume change can result in discontinuities that manifest as audible clicks.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Phil Freihofner
  • 7,645
  • 1
  • 20
  • 41
  • I think volume works on a logarithmic scale. This is what I use. `/** Provides the linear version of a volume in DB. */ public double getLinearVolume(double gainDB) { return Math.pow(10.0, gainDB/20.0); } /** Provides the DB version of a linear volume. */ public double getDBVolume(double gain) { return Math.log10(gain)*20 ; }` – Andrew Thompson Jul 20 '12 at 00:34
0

Ryuk, why don't you fade the clip from the level you're leaving, while starting to play the next clip, adjusting the volume up.

You have to forgive me, I'm not 100% sure on what library you're using, but surely the clip object has a .adjustVolume() method.

To adjust the volume up over time, it would look something like: (psuedo code mind you)

clip2.adjustVolume(0*time.deltaTime()+5); //Start the clip at volume 0, and increment it each frame, not second.
clip1.adjustVolume(100*time.deltaTime()-5); //Inverse, adjust volume down to fade out.
Chad
  • 872
  • 8
  • 24