Added track previews to the downloads menu.
Currently uses the osu! server to load MP3 previews. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package itdelatrisu.opsu.audio;
|
||||
|
||||
import itdelatrisu.opsu.ErrorHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
@@ -9,6 +11,7 @@ import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.Clip;
|
||||
import javax.sound.sampled.FloatControl;
|
||||
import javax.sound.sampled.LineListener;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
|
||||
/**
|
||||
@@ -36,6 +39,9 @@ public class MultiClip {
|
||||
/** A list of clips used for this audio sample. */
|
||||
private LinkedList<Clip> clips = new LinkedList<Clip>();
|
||||
|
||||
/** The audio input stream. */
|
||||
private AudioInputStream audioIn;
|
||||
|
||||
/** The format of this audio sample. */
|
||||
private AudioFormat format;
|
||||
|
||||
@@ -52,6 +58,7 @@ public class MultiClip {
|
||||
*/
|
||||
public MultiClip(String name, AudioInputStream audioIn) throws IOException, LineUnavailableException {
|
||||
this.name = name;
|
||||
this.audioIn = audioIn;
|
||||
if (audioIn != null) {
|
||||
format = audioIn.getFormat();
|
||||
|
||||
@@ -97,8 +104,9 @@ public class MultiClip {
|
||||
/**
|
||||
* Plays the clip with the specified volume.
|
||||
* @param volume the volume the play at
|
||||
* @param listener the line listener
|
||||
*/
|
||||
public void start(float volume) throws LineUnavailableException {
|
||||
public void start(float volume, LineListener listener) throws LineUnavailableException {
|
||||
Clip clip = getClip();
|
||||
if (clip == null)
|
||||
return;
|
||||
@@ -111,6 +119,8 @@ public class MultiClip {
|
||||
gainControl.setValue(dB);
|
||||
}
|
||||
|
||||
if (listener != null)
|
||||
clip.addLineListener(listener);
|
||||
clip.setFramePosition(0);
|
||||
clip.start();
|
||||
}
|
||||
@@ -159,6 +169,29 @@ public class MultiClip {
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the MultiClip and releases all resources.
|
||||
*/
|
||||
public void destroy() {
|
||||
if (clips.size() > 0) {
|
||||
for (Clip c : clips) {
|
||||
c.stop();
|
||||
c.flush();
|
||||
c.close();
|
||||
}
|
||||
extraClips -= clips.size() - 1;
|
||||
clips = new LinkedList<Clip>();
|
||||
}
|
||||
audioData = null;
|
||||
if (audioIn != null) {
|
||||
try {
|
||||
audioIn.close();
|
||||
} catch (IOException e) {
|
||||
ErrorHandler.error(String.format("Could not close AudioInputStream for MultiClip %s.", name), e, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys all extra clips.
|
||||
*/
|
||||
|
||||
@@ -54,7 +54,7 @@ public class MusicController {
|
||||
private static OsuFile lastOsu;
|
||||
|
||||
/** The track duration. */
|
||||
private static int duration = -1;
|
||||
private static int duration = 0;
|
||||
|
||||
/** Thread for loading tracks. */
|
||||
private static Thread trackLoader;
|
||||
@@ -273,7 +273,7 @@ public class MusicController {
|
||||
if (!trackExists() || lastOsu == null)
|
||||
return -1;
|
||||
|
||||
if (duration == -1) {
|
||||
if (duration == 0) {
|
||||
if (lastOsu.audioFilename.getName().endsWith(".mp3")) {
|
||||
try {
|
||||
AudioFileFormat fileFormat = AudioSystem.getAudioFileFormat(lastOsu.audioFilename);
|
||||
@@ -383,7 +383,7 @@ public class MusicController {
|
||||
|
||||
// reset state
|
||||
lastOsu = null;
|
||||
duration = -1;
|
||||
duration = 0;
|
||||
trackEnded = false;
|
||||
themePlaying = false;
|
||||
pauseTime = 0f;
|
||||
|
||||
@@ -33,9 +33,11 @@ import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.Clip;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import javax.sound.sampled.LineListener;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
/**
|
||||
@@ -52,6 +54,9 @@ public class SoundController {
|
||||
public MultiClip getClip();
|
||||
}
|
||||
|
||||
/** The current track being played, if any. */
|
||||
private static MultiClip currentTrack;
|
||||
|
||||
/** Sample volume multiplier, from timing points [0, 1]. */
|
||||
private static float sampleVolumeMultiplier = 1f;
|
||||
|
||||
@@ -83,9 +88,22 @@ public class SoundController {
|
||||
in.close();
|
||||
|
||||
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
|
||||
return loadClip(ref, audioIn, isMP3);
|
||||
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException | RuntimeException e) {
|
||||
ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e, true);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// GNU/Linux workaround
|
||||
// Clip clip = AudioSystem.getClip();
|
||||
/**
|
||||
* Loads and returns a Clip from an audio input stream.
|
||||
* @param ref the resource name
|
||||
* @param audioIn the audio input stream
|
||||
* @param isMP3 true if MP3, false if WAV
|
||||
* @return the loaded and opened clip
|
||||
*/
|
||||
private static MultiClip loadClip(String ref, AudioInputStream audioIn, boolean isMP3) {
|
||||
try {
|
||||
AudioFormat format = audioIn.getFormat();
|
||||
if (isMP3) {
|
||||
AudioFormat decodedFormat = new AudioFormat(
|
||||
@@ -151,7 +169,7 @@ public class SoundController {
|
||||
// still couldn't find anything, try the default clip format
|
||||
return new MultiClip(ref, AudioSystem.getAudioInputStream(clip.getFormat(), audioIn));
|
||||
}
|
||||
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException | RuntimeException e) {
|
||||
} catch (IOException | LineUnavailableException | RuntimeException e) {
|
||||
ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e, true);
|
||||
}
|
||||
return null;
|
||||
@@ -226,14 +244,15 @@ public class SoundController {
|
||||
* Plays a sound clip.
|
||||
* @param clip the Clip to play
|
||||
* @param volume the volume [0, 1]
|
||||
* @param listener the line listener
|
||||
*/
|
||||
private static void playClip(MultiClip clip, float volume) {
|
||||
private static void playClip(MultiClip clip, float volume, LineListener listener) {
|
||||
if (clip == null) // clip failed to load properly
|
||||
return;
|
||||
|
||||
if (volume > 0f) {
|
||||
try {
|
||||
clip.start(volume);
|
||||
clip.start(volume, listener);
|
||||
} catch (LineUnavailableException e) {
|
||||
ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e, true);
|
||||
}
|
||||
@@ -245,7 +264,7 @@ public class SoundController {
|
||||
* @param s the sound effect
|
||||
*/
|
||||
public static void playSound(SoundComponent s) {
|
||||
playClip(s.getClip(), Options.getEffectVolume() * Options.getMasterVolume());
|
||||
playClip(s.getClip(), Options.getEffectVolume() * Options.getMasterVolume(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,15 +283,15 @@ public class SoundController {
|
||||
|
||||
// play all sounds
|
||||
HitSound.setSampleSet(sampleSet);
|
||||
playClip(HitSound.NORMAL.getClip(), volume);
|
||||
playClip(HitSound.NORMAL.getClip(), volume, null);
|
||||
|
||||
HitSound.setSampleSet(additionSampleSet);
|
||||
if ((hitSound & OsuHitObject.SOUND_WHISTLE) > 0)
|
||||
playClip(HitSound.WHISTLE.getClip(), volume);
|
||||
playClip(HitSound.WHISTLE.getClip(), volume, null);
|
||||
if ((hitSound & OsuHitObject.SOUND_FINISH) > 0)
|
||||
playClip(HitSound.FINISH.getClip(), volume);
|
||||
playClip(HitSound.FINISH.getClip(), volume, null);
|
||||
if ((hitSound & OsuHitObject.SOUND_CLAP) > 0)
|
||||
playClip(HitSound.CLAP.getClip(), volume);
|
||||
playClip(HitSound.CLAP.getClip(), volume, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,7 +299,7 @@ public class SoundController {
|
||||
* @param s the hit sound
|
||||
*/
|
||||
public static void playHitSound(SoundComponent s) {
|
||||
playClip(s.getClip(), Options.getHitSoundVolume() * sampleVolumeMultiplier * Options.getMasterVolume());
|
||||
playClip(s.getClip(), Options.getHitSoundVolume() * sampleVolumeMultiplier * Options.getMasterVolume(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,4 +319,35 @@ public class SoundController {
|
||||
|
||||
return currentFileIndex * 100 / (SoundEffect.SIZE + (HitSound.SIZE * SampleSet.SIZE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a track from a URL.
|
||||
* If a track is currently playing, it will be stopped.
|
||||
* @param url the resource URL
|
||||
* @param isMP3 true if MP3, false if WAV
|
||||
* @param listener the line listener
|
||||
* @return the MultiClip being played
|
||||
* @throws SlickException if any error occurred (UnsupportedAudioFileException, IOException, RuntimeException)
|
||||
*/
|
||||
public static synchronized MultiClip playTrack(URL url, boolean isMP3, LineListener listener) throws SlickException {
|
||||
stopTrack();
|
||||
try {
|
||||
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
|
||||
currentTrack = loadClip(url.getFile(), audioIn, isMP3);
|
||||
playClip(currentTrack, Options.getMusicVolume() * Options.getMasterVolume(), listener);
|
||||
return currentTrack;
|
||||
} catch (UnsupportedAudioFileException | IOException | RuntimeException e) {
|
||||
throw new SlickException(String.format("Failed to load clip '%s'.", url.getFile(), e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current track playing, if any.
|
||||
*/
|
||||
public static synchronized void stopTrack() {
|
||||
if (currentTrack != null) {
|
||||
currentTrack.destroy();
|
||||
currentTrack = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user