2015-02-27 05:37:30 +01:00
|
|
|
package itdelatrisu.opsu.audio;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
2015-03-01 00:28:58 +01:00
|
|
|
import java.util.Iterator;
|
2015-02-27 05:37:30 +01:00
|
|
|
import java.util.LinkedList;
|
|
|
|
|
|
|
|
import javax.sound.sampled.AudioFormat;
|
|
|
|
import javax.sound.sampled.AudioInputStream;
|
|
|
|
import javax.sound.sampled.Clip;
|
|
|
|
import javax.sound.sampled.FloatControl;
|
|
|
|
import javax.sound.sampled.LineUnavailableException;
|
|
|
|
|
|
|
|
//http://stackoverflow.com/questions/1854616/in-java-how-can-i-play-the-same-audio-clip-multiple-times-simultaneously
|
|
|
|
public class MultiClip {
|
|
|
|
/** A list of clips used for this audio sample */
|
|
|
|
LinkedList<Clip> clips = new LinkedList<Clip>();
|
|
|
|
|
|
|
|
/** The format of this audio sample */
|
|
|
|
AudioFormat format;
|
|
|
|
|
|
|
|
/** The data for this audio sample */
|
2015-02-28 01:22:29 +01:00
|
|
|
byte[] audioData;
|
2015-02-27 05:37:30 +01:00
|
|
|
|
|
|
|
/** The name given to this clip */
|
|
|
|
String name;
|
|
|
|
|
2015-02-28 01:22:29 +01:00
|
|
|
/** Size of a single buffer */
|
|
|
|
final int BUFFER_SIZE = 0x1000;
|
|
|
|
|
2015-03-01 00:28:58 +01:00
|
|
|
static LinkedList<MultiClip> allMultiClips = new LinkedList<MultiClip>();
|
|
|
|
|
2015-02-28 01:22:29 +01:00
|
|
|
/** Constructor */
|
2015-02-27 05:37:30 +01:00
|
|
|
public MultiClip(String name, AudioInputStream audioIn) throws IOException, LineUnavailableException {
|
|
|
|
this.name = name;
|
|
|
|
if(audioIn != null){
|
|
|
|
format = audioIn.getFormat();
|
2015-02-28 01:22:29 +01:00
|
|
|
|
|
|
|
LinkedList<byte[]> allBufs = new LinkedList<byte[]>();
|
|
|
|
|
|
|
|
int readed = 0;
|
|
|
|
boolean hasData = true;
|
|
|
|
while (hasData) {
|
|
|
|
readed = 0;
|
|
|
|
byte[] tbuf = new byte[BUFFER_SIZE];
|
|
|
|
while (readed < tbuf.length) {
|
|
|
|
int read = audioIn.read(tbuf, readed, tbuf.length - readed);
|
|
|
|
if (read < 0) {
|
|
|
|
hasData = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
readed += read;
|
|
|
|
}
|
|
|
|
allBufs.add(tbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
audioData = new byte[(allBufs.size() - 1) * BUFFER_SIZE + readed];
|
|
|
|
|
|
|
|
int cnt = 0;
|
|
|
|
for (byte[] tbuf : allBufs) {
|
|
|
|
int size = BUFFER_SIZE;
|
|
|
|
if (cnt == allBufs.size() - 1) {
|
|
|
|
size = readed;
|
|
|
|
}
|
|
|
|
System.arraycopy(tbuf, 0, audioData, BUFFER_SIZE * cnt, size);
|
|
|
|
cnt++;
|
|
|
|
}
|
2015-02-27 05:37:30 +01:00
|
|
|
}
|
|
|
|
getClip();
|
2015-03-01 00:28:58 +01:00
|
|
|
allMultiClips.add(this);
|
2015-02-27 05:37:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the name of the clip
|
|
|
|
* @return the name
|
|
|
|
*/
|
|
|
|
public String getName() {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Plays the clip with the specified volume.
|
|
|
|
* @param volume the volume the play at
|
|
|
|
* @throws IOException
|
|
|
|
* @throws LineUnavailableException
|
|
|
|
*/
|
|
|
|
public void start(float volume) throws LineUnavailableException, IOException {
|
|
|
|
Clip clip = getClip();
|
|
|
|
|
2015-03-01 00:28:58 +01:00
|
|
|
if(clip == null)
|
|
|
|
return;
|
|
|
|
|
2015-02-27 05:37:30 +01:00
|
|
|
// PulseAudio does not support Master Gain
|
|
|
|
if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
|
|
|
|
// set volume
|
|
|
|
FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
|
|
|
|
float dB = (float) (Math.log(volume) / Math.log(10.0) * 20.0);
|
|
|
|
gainControl.setValue(dB);
|
|
|
|
}
|
|
|
|
|
|
|
|
clip.setFramePosition(0);
|
|
|
|
clip.start();
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Returns a Clip that is not playing from the list
|
2015-03-01 00:28:58 +01:00
|
|
|
* if one is not available a new one is created if able
|
2015-02-27 05:37:30 +01:00
|
|
|
* @return the Clip
|
|
|
|
*/
|
|
|
|
private Clip getClip() throws LineUnavailableException, IOException{
|
2015-03-01 00:28:58 +01:00
|
|
|
for(Iterator<Clip> ita = clips.listIterator(); ita.hasNext(); ) {
|
|
|
|
Clip c = ita.next();
|
2015-02-27 05:37:30 +01:00
|
|
|
if(!c.isRunning()){
|
2015-03-01 00:28:58 +01:00
|
|
|
ita.remove();
|
|
|
|
clips.add(c);
|
2015-02-27 05:37:30 +01:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
}
|
2015-03-01 00:28:58 +01:00
|
|
|
|
|
|
|
Clip t = SoundController.newClip();
|
|
|
|
if(t == null){
|
|
|
|
if(clips.isEmpty()){
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
t = clips.removeFirst();
|
|
|
|
t.stop();
|
|
|
|
clips.add(t);
|
|
|
|
} else {
|
|
|
|
if (format != null)
|
|
|
|
t.open(format, audioData, 0, audioData.length);
|
|
|
|
clips.add(t);
|
|
|
|
}
|
2015-02-27 05:37:30 +01:00
|
|
|
return t;
|
|
|
|
}
|
2015-03-01 00:28:58 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroys all but one clip
|
|
|
|
*/
|
|
|
|
protected void destroyAllButOne(){
|
|
|
|
for(Iterator<Clip> ita = clips.listIterator(); ita.hasNext(); ) {
|
|
|
|
Clip c = ita.next();
|
|
|
|
if(clips.size()>1){
|
|
|
|
ita.remove();
|
|
|
|
SoundController.destroyClip(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroys all but one clip for all MultiClips
|
|
|
|
*/
|
|
|
|
protected static void destroyExtraClips() {
|
|
|
|
for(MultiClip mc : MultiClip.allMultiClips){
|
|
|
|
mc.destroyAllButOne();
|
|
|
|
}
|
|
|
|
}
|
2015-02-27 05:37:30 +01:00
|
|
|
}
|