birgersp
birgersp

Reputation: 4956

How to start a sound that is already playing?

I'm making game extension to play some sounds. The sounds may be triggered at random times, which means that the same sound may be triggered twice with very little time apart. In this case, the sound should start playing even though it is already playing (if that makes sense).

I'm using a Clip to play the sound. This means that I have to "rewind" the clip before playing it. It seems, since it's the same clip, that it stops playing before re-starting. What I want is for it to continue playing, and play the same clip "on top" of the previos one. See this example:

import java.io.File;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

public class JavaApplication {

    public static void main(String[] args) throws Exception {
        File file = new File(JavaApplication.class.getResource("1.wav").getPath());
        AudioInputStream inputStream = AudioSystem.getAudioInputStream(file);

        Clip clip = AudioSystem.getClip();
        clip.open(inputStream);
        clip.setFramePosition(0);
        clip.start(); // The sound is 300 ms long
        Thread.sleep(150); // Let it play for 150 ms
        clip.setFramePosition(0); // Attempt to start it from the beginning, without stopping it
        clip.start();
        Thread.sleep(1000);
    }
}

Upvotes: 3

Views: 302

Answers (3)

birgersp
birgersp

Reputation: 4956

Found a solution: Read the raw bytes of the sound, and create a inputstream from the data each time I play the sound. This enables me to play the same sound file "on top of itself" without loading it from disk more than once.

package com.mysite.javaapplication;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

public class JavaApplication {

    private static void playSoundBytes(byte[] data) throws Exception {
        AudioInputStream inputStream = AudioSystem.getAudioInputStream(new ByteArrayInputStream(data));
        AudioFormat format = inputStream.getFormat();
        Clip clip = AudioSystem.getClip();
        clip.open(inputStream);
        clip.setFramePosition(0);
        clip.start();
    }

    private static byte[] getResourceAsBytes(String name, int bufferSize) throws IOException {
        InputStream stream = JavaApplication.class.getResourceAsStream(name);
        byte buffer[] = new byte[bufferSize];
        int b, i = 0;
        while ((b = stream.read()) != -1) {
            try {
                buffer[i++] = (byte) b;
            } catch (IndexOutOfBoundsException e) {
                throw new IOException("Buffer of " + bufferSize + " bytes is too small to read resource \"" + name + "\"");
            }
        }
        byte data[] = new byte[i + 1];
        while (i >= 0) {
            data[i] = buffer[i];
            i--;
        }
        return data;
    }

    public static void main(String[] args) throws Exception {

        byte[] soundData = getResourceAsBytes("/1.wav", 1000*1000);
        playSoundBytes(soundData);
        Thread.sleep(1000);
        playSoundBytes(soundData);
        Thread.sleep(2000);
    }
}

Upvotes: 0

Eugene
Eugene

Reputation: 11085

You need to create two AudioInputStream instances. No need to use multi thread exlicitly in your code. Hop it will help. Thanks.

import java.io.File;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;

public class JavaApplication {

    public static void main(String[] args) throws Exception {

        File file = new File(JavaApplication.class.getResource("1.wav").getPath());
        AudioInputStream inputStream1 = AudioSystem.getAudioInputStream(file);
        AudioInputStream inputStream2 = AudioSystem.getAudioInputStream(file);

        Clip clip = AudioSystem.getClip();
        clip.open(inputStream1);
        clip.setFramePosition(0);
        clip.start();

        // Clip is 2000 ms long. let it play for 1000 ms
        Thread.sleep(1000);

        Clip clip2 = AudioSystem.getClip();
        clip2.open(inputStream2);
        clip2.setFramePosition(0);
        clip2.start();

        Thread.sleep(2000);

    }
}

Upvotes: 0

Aaron
Aaron

Reputation: 175

Have you tried creating a duplicate object when you need it and destroy it when it's finished? A new Clip object or just copy the original and get it to play along with it.

 Clip temp = clip;
 temp.start(); // The sound is 300 ms long
 Thread.sleep(150); // Let it play for 150 ms
 temp = null;

Just a suggestion, you could also try using Clip[] arrays to handle a few clips playing at different times.

Upvotes: 0

Related Questions