Allows multiple instance of the same clip to be played simultaneously.
This might fix some issues with some sounds not being played sometimes. Also might require more memory since more clips are created.
This commit is contained in:
@@ -79,7 +79,7 @@ public enum HitSound implements SoundController.SoundComponent {
|
|||||||
private String filename;
|
private String filename;
|
||||||
|
|
||||||
/** The Clip associated with the hit sound. */
|
/** The Clip associated with the hit sound. */
|
||||||
private HashMap<SampleSet, Clip> clips;
|
private HashMap<SampleSet, MultiClip> clips;
|
||||||
|
|
||||||
/** Total number of hit sounds. */
|
/** Total number of hit sounds. */
|
||||||
public static final int SIZE = values().length;
|
public static final int SIZE = values().length;
|
||||||
@@ -90,7 +90,7 @@ public enum HitSound implements SoundController.SoundComponent {
|
|||||||
*/
|
*/
|
||||||
HitSound(String filename) {
|
HitSound(String filename) {
|
||||||
this.filename = filename;
|
this.filename = filename;
|
||||||
this.clips = new HashMap<SampleSet, Clip>();
|
this.clips = new HashMap<SampleSet, MultiClip>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,7 +100,7 @@ public enum HitSound implements SoundController.SoundComponent {
|
|||||||
public String getFileName() { return filename; }
|
public String getFileName() { return filename; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Clip getClip() {
|
public MultiClip getClip() {
|
||||||
return (currentSampleSet != null) ? clips.get(currentSampleSet) : null;
|
return (currentSampleSet != null) ? clips.get(currentSampleSet) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ public enum HitSound implements SoundController.SoundComponent {
|
|||||||
* @param s the sample set
|
* @param s the sample set
|
||||||
* @param clip the Clip
|
* @param clip the Clip
|
||||||
*/
|
*/
|
||||||
public void setClip(SampleSet s, Clip clip) {
|
public void setClip(SampleSet s, MultiClip clip) {
|
||||||
clips.put(s, clip);
|
clips.put(s, clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
95
src/itdelatrisu/opsu/audio/MultiClip.java
Normal file
95
src/itdelatrisu/opsu/audio/MultiClip.java
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package itdelatrisu.opsu.audio;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.ErrorHandler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import javax.sound.sampled.AudioFormat;
|
||||||
|
import javax.sound.sampled.AudioInputStream;
|
||||||
|
import javax.sound.sampled.AudioSystem;
|
||||||
|
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 */
|
||||||
|
byte[] buffer;
|
||||||
|
|
||||||
|
/** The name given to this clip */
|
||||||
|
String name;
|
||||||
|
|
||||||
|
/** Constructor
|
||||||
|
* @param name
|
||||||
|
* @throws LineUnavailableException */
|
||||||
|
public MultiClip(String name, AudioInputStream audioIn) throws IOException, LineUnavailableException {
|
||||||
|
this.name = name;
|
||||||
|
if(audioIn != null){
|
||||||
|
buffer = new byte[audioIn.available()];
|
||||||
|
int readed= 0;
|
||||||
|
while(readed < buffer.length) {
|
||||||
|
int read = audioIn.read(buffer, readed, buffer.length-readed);
|
||||||
|
if(read < 0 )
|
||||||
|
break;
|
||||||
|
readed += read;
|
||||||
|
}
|
||||||
|
format = audioIn.getFormat();
|
||||||
|
} else {
|
||||||
|
System.out.println("Null multiclip");
|
||||||
|
}
|
||||||
|
getClip();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
|
||||||
|
// 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
|
||||||
|
* if one is not available a new one is created
|
||||||
|
* @return the Clip
|
||||||
|
*/
|
||||||
|
private Clip getClip() throws LineUnavailableException, IOException{
|
||||||
|
for(Clip c : clips){
|
||||||
|
if(!c.isRunning()){
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Clip t = AudioSystem.getClip();
|
||||||
|
if (format != null)
|
||||||
|
t.open(format, buffer, 0, buffer.length);
|
||||||
|
clips.add(t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,6 @@ import javax.sound.sampled.AudioInputStream;
|
|||||||
import javax.sound.sampled.AudioSystem;
|
import javax.sound.sampled.AudioSystem;
|
||||||
import javax.sound.sampled.Clip;
|
import javax.sound.sampled.Clip;
|
||||||
import javax.sound.sampled.DataLine;
|
import javax.sound.sampled.DataLine;
|
||||||
import javax.sound.sampled.FloatControl;
|
|
||||||
import javax.sound.sampled.LineUnavailableException;
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
|
|
||||||
@@ -50,7 +49,7 @@ public class SoundController {
|
|||||||
* Returns the Clip associated with the sound component.
|
* Returns the Clip associated with the sound component.
|
||||||
* @return the Clip
|
* @return the Clip
|
||||||
*/
|
*/
|
||||||
public Clip getClip();
|
public MultiClip getClip();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sample volume multiplier, from timing points [0, 1]. */
|
/** Sample volume multiplier, from timing points [0, 1]. */
|
||||||
@@ -71,7 +70,7 @@ public class SoundController {
|
|||||||
* @param isMP3 true if MP3, false if WAV
|
* @param isMP3 true if MP3, false if WAV
|
||||||
* @return the loaded and opened clip
|
* @return the loaded and opened clip
|
||||||
*/
|
*/
|
||||||
private static Clip loadClip(String ref, boolean isMP3) {
|
private static MultiClip loadClip(String ref, boolean isMP3) {
|
||||||
try {
|
try {
|
||||||
URL url = ResourceLoader.getResource(ref);
|
URL url = ResourceLoader.getResource(ref);
|
||||||
|
|
||||||
@@ -79,7 +78,7 @@ public class SoundController {
|
|||||||
InputStream in = url.openStream();
|
InputStream in = url.openStream();
|
||||||
if (in.available() == 0) {
|
if (in.available() == 0) {
|
||||||
in.close();
|
in.close();
|
||||||
return AudioSystem.getClip();
|
return new MultiClip(ref, null);
|
||||||
}
|
}
|
||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
@@ -88,6 +87,9 @@ public class SoundController {
|
|||||||
// GNU/Linux workaround
|
// GNU/Linux workaround
|
||||||
// Clip clip = AudioSystem.getClip();
|
// Clip clip = AudioSystem.getClip();
|
||||||
AudioFormat format = audioIn.getFormat();
|
AudioFormat format = audioIn.getFormat();
|
||||||
|
|
||||||
|
//TODO Is this really needed? since the code below will find out the format isn't supported
|
||||||
|
// and will pretty much do the same thing I think. -fluddokt
|
||||||
if (isMP3) {
|
if (isMP3) {
|
||||||
AudioFormat decodedFormat = new AudioFormat(
|
AudioFormat decodedFormat = new AudioFormat(
|
||||||
AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), 16,
|
AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate(), 16,
|
||||||
@@ -98,11 +100,9 @@ public class SoundController {
|
|||||||
}
|
}
|
||||||
DataLine.Info info = new DataLine.Info(Clip.class, format);
|
DataLine.Info info = new DataLine.Info(Clip.class, format);
|
||||||
if(AudioSystem.isLineSupported(info)){
|
if(AudioSystem.isLineSupported(info)){
|
||||||
Clip clip = (Clip) AudioSystem.getLine(info);
|
return new MultiClip(ref, audioIn);
|
||||||
clip.open(audioIn);
|
|
||||||
return clip;
|
|
||||||
}else{
|
}else{
|
||||||
// try to find closest matching line
|
//Try to find closest matching line
|
||||||
Clip clip = AudioSystem.getClip();
|
Clip clip = AudioSystem.getClip();
|
||||||
AudioFormat[] formats = ((DataLine.Info) clip.getLineInfo()).getFormats();
|
AudioFormat[] formats = ((DataLine.Info) clip.getLineInfo()).getFormats();
|
||||||
int bestIndex = -1;
|
int bestIndex = -1;
|
||||||
@@ -149,11 +149,10 @@ public class SoundController {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (bestIndex >= 0) {
|
if (bestIndex >= 0) {
|
||||||
clip.open(AudioSystem.getAudioInputStream(formats[bestIndex], audioIn));
|
return new MultiClip(ref, AudioSystem.getAudioInputStream(formats[bestIndex], audioIn));
|
||||||
} else
|
} else
|
||||||
// still couldn't find anything, try the default clip format
|
// still couldn't find anything, try the default clip format
|
||||||
clip.open(AudioSystem.getAudioInputStream(clip.getFormat(), audioIn));
|
return new MultiClip(ref, AudioSystem.getAudioInputStream(clip.getFormat(), audioIn));
|
||||||
return clip;
|
|
||||||
}
|
}
|
||||||
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException | RuntimeException e) {
|
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException | RuntimeException e) {
|
||||||
ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e, true);
|
ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e, true);
|
||||||
@@ -231,28 +230,16 @@ public class SoundController {
|
|||||||
* @param clip the Clip to play
|
* @param clip the Clip to play
|
||||||
* @param volume the volume [0, 1]
|
* @param volume the volume [0, 1]
|
||||||
*/
|
*/
|
||||||
private static void playClip(Clip clip, float volume) {
|
private static void playClip(MultiClip clip, float volume) {
|
||||||
if (clip == null) // clip failed to load properly
|
if (clip == null) // clip failed to load properly
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (volume > 0f) {
|
if (volume > 0f) {
|
||||||
// stop clip if running
|
try {
|
||||||
if (clip.isRunning()) {
|
clip.start(volume);
|
||||||
clip.stop();
|
} catch (LineUnavailableException | IOException e) {
|
||||||
clip.flush();
|
ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// play clip
|
|
||||||
clip.setFramePosition(0);
|
|
||||||
clip.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public enum SoundEffect implements SoundController.SoundComponent {
|
|||||||
private String filename;
|
private String filename;
|
||||||
|
|
||||||
/** The Clip associated with the sound effect. */
|
/** The Clip associated with the sound effect. */
|
||||||
private Clip clip;
|
private MultiClip clip;
|
||||||
|
|
||||||
/** Total number of sound effects. */
|
/** Total number of sound effects. */
|
||||||
public static final int SIZE = values().length;
|
public static final int SIZE = values().length;
|
||||||
@@ -67,11 +67,11 @@ public enum SoundEffect implements SoundController.SoundComponent {
|
|||||||
public String getFileName() { return filename; }
|
public String getFileName() { return filename; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Clip getClip() { return clip; }
|
public MultiClip getClip() { return clip; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the Clip for the sound.
|
* Sets the Clip for the sound.
|
||||||
* @param clip the clip
|
* @param clip the clip
|
||||||
*/
|
*/
|
||||||
public void setClip(Clip clip) { this.clip = clip; }
|
public void setClip(MultiClip clip) { this.clip = clip; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user