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:
parent
7d5899ba7e
commit
ec042159a8
|
@ -1,5 +1,7 @@
|
||||||
package itdelatrisu.opsu.audio;
|
package itdelatrisu.opsu.audio;
|
||||||
|
|
||||||
|
import itdelatrisu.opsu.ErrorHandler;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -9,6 +11,7 @@ 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.FloatControl;
|
import javax.sound.sampled.FloatControl;
|
||||||
|
import javax.sound.sampled.LineListener;
|
||||||
import javax.sound.sampled.LineUnavailableException;
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,6 +39,9 @@ public class MultiClip {
|
||||||
/** A list of clips used for this audio sample. */
|
/** A list of clips used for this audio sample. */
|
||||||
private LinkedList<Clip> clips = new LinkedList<Clip>();
|
private LinkedList<Clip> clips = new LinkedList<Clip>();
|
||||||
|
|
||||||
|
/** The audio input stream. */
|
||||||
|
private AudioInputStream audioIn;
|
||||||
|
|
||||||
/** The format of this audio sample. */
|
/** The format of this audio sample. */
|
||||||
private AudioFormat format;
|
private AudioFormat format;
|
||||||
|
|
||||||
|
@ -52,6 +58,7 @@ public class MultiClip {
|
||||||
*/
|
*/
|
||||||
public MultiClip(String name, AudioInputStream audioIn) throws IOException, LineUnavailableException {
|
public MultiClip(String name, AudioInputStream audioIn) throws IOException, LineUnavailableException {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.audioIn = audioIn;
|
||||||
if (audioIn != null) {
|
if (audioIn != null) {
|
||||||
format = audioIn.getFormat();
|
format = audioIn.getFormat();
|
||||||
|
|
||||||
|
@ -97,8 +104,9 @@ public class MultiClip {
|
||||||
/**
|
/**
|
||||||
* Plays the clip with the specified volume.
|
* Plays the clip with the specified volume.
|
||||||
* @param volume the volume the play at
|
* @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();
|
Clip clip = getClip();
|
||||||
if (clip == null)
|
if (clip == null)
|
||||||
return;
|
return;
|
||||||
|
@ -111,6 +119,8 @@ public class MultiClip {
|
||||||
gainControl.setValue(dB);
|
gainControl.setValue(dB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (listener != null)
|
||||||
|
clip.addLineListener(listener);
|
||||||
clip.setFramePosition(0);
|
clip.setFramePosition(0);
|
||||||
clip.start();
|
clip.start();
|
||||||
}
|
}
|
||||||
|
@ -159,6 +169,29 @@ public class MultiClip {
|
||||||
return c;
|
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.
|
* Destroys all extra clips.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -54,7 +54,7 @@ public class MusicController {
|
||||||
private static OsuFile lastOsu;
|
private static OsuFile lastOsu;
|
||||||
|
|
||||||
/** The track duration. */
|
/** The track duration. */
|
||||||
private static int duration = -1;
|
private static int duration = 0;
|
||||||
|
|
||||||
/** Thread for loading tracks. */
|
/** Thread for loading tracks. */
|
||||||
private static Thread trackLoader;
|
private static Thread trackLoader;
|
||||||
|
@ -273,7 +273,7 @@ public class MusicController {
|
||||||
if (!trackExists() || lastOsu == null)
|
if (!trackExists() || lastOsu == null)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (duration == -1) {
|
if (duration == 0) {
|
||||||
if (lastOsu.audioFilename.getName().endsWith(".mp3")) {
|
if (lastOsu.audioFilename.getName().endsWith(".mp3")) {
|
||||||
try {
|
try {
|
||||||
AudioFileFormat fileFormat = AudioSystem.getAudioFileFormat(lastOsu.audioFilename);
|
AudioFileFormat fileFormat = AudioSystem.getAudioFileFormat(lastOsu.audioFilename);
|
||||||
|
@ -383,7 +383,7 @@ public class MusicController {
|
||||||
|
|
||||||
// reset state
|
// reset state
|
||||||
lastOsu = null;
|
lastOsu = null;
|
||||||
duration = -1;
|
duration = 0;
|
||||||
trackEnded = false;
|
trackEnded = false;
|
||||||
themePlaying = false;
|
themePlaying = false;
|
||||||
pauseTime = 0f;
|
pauseTime = 0f;
|
||||||
|
|
|
@ -33,9 +33,11 @@ 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.LineListener;
|
||||||
import javax.sound.sampled.LineUnavailableException;
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||||
|
|
||||||
|
import org.newdawn.slick.SlickException;
|
||||||
import org.newdawn.slick.util.ResourceLoader;
|
import org.newdawn.slick.util.ResourceLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,6 +54,9 @@ public class SoundController {
|
||||||
public MultiClip getClip();
|
public MultiClip getClip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The current track being played, if any. */
|
||||||
|
private static MultiClip currentTrack;
|
||||||
|
|
||||||
/** Sample volume multiplier, from timing points [0, 1]. */
|
/** Sample volume multiplier, from timing points [0, 1]. */
|
||||||
private static float sampleVolumeMultiplier = 1f;
|
private static float sampleVolumeMultiplier = 1f;
|
||||||
|
|
||||||
|
@ -83,9 +88,22 @@ public class SoundController {
|
||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
|
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();
|
AudioFormat format = audioIn.getFormat();
|
||||||
if (isMP3) {
|
if (isMP3) {
|
||||||
AudioFormat decodedFormat = new AudioFormat(
|
AudioFormat decodedFormat = new AudioFormat(
|
||||||
|
@ -151,7 +169,7 @@ public class SoundController {
|
||||||
// still couldn't find anything, try the default clip format
|
// still couldn't find anything, try the default clip format
|
||||||
return new MultiClip(ref, AudioSystem.getAudioInputStream(clip.getFormat(), audioIn));
|
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);
|
ErrorHandler.error(String.format("Failed to load file '%s'.", ref), e, true);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -226,14 +244,15 @@ public class SoundController {
|
||||||
* Plays a sound clip.
|
* Plays a sound clip.
|
||||||
* @param clip the Clip to play
|
* @param clip the Clip to play
|
||||||
* @param volume the volume [0, 1]
|
* @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
|
if (clip == null) // clip failed to load properly
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (volume > 0f) {
|
if (volume > 0f) {
|
||||||
try {
|
try {
|
||||||
clip.start(volume);
|
clip.start(volume, listener);
|
||||||
} catch (LineUnavailableException e) {
|
} catch (LineUnavailableException e) {
|
||||||
ErrorHandler.error(String.format("Could not start a clip '%s'.", clip.getName()), e, true);
|
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
|
* @param s the sound effect
|
||||||
*/
|
*/
|
||||||
public static void playSound(SoundComponent s) {
|
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
|
// play all sounds
|
||||||
HitSound.setSampleSet(sampleSet);
|
HitSound.setSampleSet(sampleSet);
|
||||||
playClip(HitSound.NORMAL.getClip(), volume);
|
playClip(HitSound.NORMAL.getClip(), volume, null);
|
||||||
|
|
||||||
HitSound.setSampleSet(additionSampleSet);
|
HitSound.setSampleSet(additionSampleSet);
|
||||||
if ((hitSound & OsuHitObject.SOUND_WHISTLE) > 0)
|
if ((hitSound & OsuHitObject.SOUND_WHISTLE) > 0)
|
||||||
playClip(HitSound.WHISTLE.getClip(), volume);
|
playClip(HitSound.WHISTLE.getClip(), volume, null);
|
||||||
if ((hitSound & OsuHitObject.SOUND_FINISH) > 0)
|
if ((hitSound & OsuHitObject.SOUND_FINISH) > 0)
|
||||||
playClip(HitSound.FINISH.getClip(), volume);
|
playClip(HitSound.FINISH.getClip(), volume, null);
|
||||||
if ((hitSound & OsuHitObject.SOUND_CLAP) > 0)
|
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
|
* @param s the hit sound
|
||||||
*/
|
*/
|
||||||
public static void playHitSound(SoundComponent s) {
|
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));
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ import org.json.JSONObject;
|
||||||
/**
|
/**
|
||||||
* Download server: http://bloodcat.com/osu/
|
* Download server: http://bloodcat.com/osu/
|
||||||
*/
|
*/
|
||||||
public class BloodcatServer implements DownloadServer {
|
public class BloodcatServer extends DownloadServer {
|
||||||
/** Formatted download URL: {@code beatmapSetID} */
|
/** Formatted download URL: {@code beatmapSetID} */
|
||||||
private static final String DOWNLOAD_URL = "http://bloodcat.com/osu/s/%d";
|
private static final String DOWNLOAD_URL = "http://bloodcat.com/osu/s/%d";
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,21 @@ public class DownloadNode {
|
||||||
(cy > y && cy < y + buttonHeight));
|
(cy > y && cy < y + buttonHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the coordinates are within the bounds of the
|
||||||
|
* download result action icon at the given index.
|
||||||
|
* @param cx the x coordinate
|
||||||
|
* @param cy the y coordinate
|
||||||
|
* @param index the index (to offset the button from the topmost button)
|
||||||
|
*/
|
||||||
|
public static boolean resultIconContains(float cx, float cy, int index) {
|
||||||
|
int iconWidth = GameImage.MUSIC_PLAY.getImage().getWidth();
|
||||||
|
float x = buttonBaseX + buttonWidth * 0.001f;
|
||||||
|
float y = buttonBaseY + (index * buttonOffset) + buttonHeight / 2f;
|
||||||
|
return ((cx > x && cx < x + iconWidth) &&
|
||||||
|
(cy > y - iconWidth / 2 && cy < y + iconWidth / 2));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the coordinates are within the bounds of the
|
* Returns true if the coordinates are within the bounds of the
|
||||||
* download result button area.
|
* download result button area.
|
||||||
|
@ -283,9 +298,10 @@ public class DownloadNode {
|
||||||
* @param index the index (to offset the button from the topmost button)
|
* @param index the index (to offset the button from the topmost button)
|
||||||
* @param hover true if the mouse is hovering over this button
|
* @param hover true if the mouse is hovering over this button
|
||||||
* @param focus true if the button is focused
|
* @param focus true if the button is focused
|
||||||
|
* @param previewing true if the beatmap is currently being previewed
|
||||||
*/
|
*/
|
||||||
public void drawResult(Graphics g, int index, boolean hover, boolean focus) {
|
public void drawResult(Graphics g, int index, boolean hover, boolean focus, boolean previewing) {
|
||||||
float textX = buttonBaseX + buttonWidth * 0.02f;
|
float textX = buttonBaseX + buttonWidth * 0.001f;
|
||||||
float edgeX = buttonBaseX + buttonWidth * 0.985f;
|
float edgeX = buttonBaseX + buttonWidth * 0.985f;
|
||||||
float y = buttonBaseY + index * buttonOffset;
|
float y = buttonBaseY + index * buttonOffset;
|
||||||
float marginY = buttonHeight * 0.04f;
|
float marginY = buttonHeight * 0.04f;
|
||||||
|
@ -310,6 +326,11 @@ public class DownloadNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// preview button
|
||||||
|
Image img = (previewing) ? GameImage.MUSIC_PAUSE.getImage() : GameImage.MUSIC_PLAY.getImage();
|
||||||
|
img.drawCentered(textX + img.getWidth() / 2, y + buttonHeight / 2f);
|
||||||
|
textX += img.getWidth() + buttonWidth * 0.001f;
|
||||||
|
|
||||||
// text
|
// text
|
||||||
Utils.FONT_BOLD.drawString(
|
Utils.FONT_BOLD.drawString(
|
||||||
textX, y + marginY,
|
textX, y + marginY,
|
||||||
|
|
|
@ -21,15 +21,18 @@ package itdelatrisu.opsu.downloads;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for beatmap download servers.
|
* Abstract class for beatmap download servers.
|
||||||
*/
|
*/
|
||||||
public interface DownloadServer {
|
public abstract class DownloadServer {
|
||||||
|
/** Track preview URL. */
|
||||||
|
private static final String PREVIEW_URL = "http://b.ppy.sh/preview/%d.mp3";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a web address to download the given beatmap.
|
* Returns a web address to download the given beatmap.
|
||||||
* @param beatmapSetID the beatmap set ID
|
* @param beatmapSetID the beatmap set ID
|
||||||
* @return the URL string
|
* @return the URL string
|
||||||
*/
|
*/
|
||||||
public String getURL(int beatmapSetID);
|
public abstract String getURL(int beatmapSetID);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of results for a given search query, or null if the
|
* Returns a list of results for a given search query, or null if the
|
||||||
|
@ -40,7 +43,7 @@ public interface DownloadServer {
|
||||||
* @return the result array
|
* @return the result array
|
||||||
* @throws IOException if any connection problem occurs
|
* @throws IOException if any connection problem occurs
|
||||||
*/
|
*/
|
||||||
public DownloadNode[] resultList(String query, int page, boolean rankedOnly) throws IOException;
|
public abstract DownloadNode[] resultList(String query, int page, boolean rankedOnly) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the total number of results for the last search query.
|
* Returns the total number of results for the last search query.
|
||||||
|
@ -48,5 +51,14 @@ public interface DownloadServer {
|
||||||
* {@link #resultList(String, int, boolean)} if multiple pages exist.
|
* {@link #resultList(String, int, boolean)} if multiple pages exist.
|
||||||
* @return the result count, or -1 if no query
|
* @return the result count, or -1 if no query
|
||||||
*/
|
*/
|
||||||
public int totalResults();
|
public abstract int totalResults();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a web address to preview the given beatmap.
|
||||||
|
* @param beatmapSetID the beatmap set ID
|
||||||
|
* @return the URL string
|
||||||
|
*/
|
||||||
|
public String getPreviewURL(int beatmapSetID) {
|
||||||
|
return String.format(PREVIEW_URL, beatmapSetID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,11 @@ import itdelatrisu.opsu.downloads.DownloadServer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import javax.sound.sampled.LineEvent;
|
||||||
|
import javax.sound.sampled.LineListener;
|
||||||
|
|
||||||
import org.newdawn.slick.Color;
|
import org.newdawn.slick.Color;
|
||||||
import org.newdawn.slick.GameContainer;
|
import org.newdawn.slick.GameContainer;
|
||||||
|
@ -51,6 +56,7 @@ import org.newdawn.slick.state.BasicGameState;
|
||||||
import org.newdawn.slick.state.StateBasedGame;
|
import org.newdawn.slick.state.StateBasedGame;
|
||||||
import org.newdawn.slick.state.transition.FadeInTransition;
|
import org.newdawn.slick.state.transition.FadeInTransition;
|
||||||
import org.newdawn.slick.state.transition.FadeOutTransition;
|
import org.newdawn.slick.state.transition.FadeOutTransition;
|
||||||
|
import org.newdawn.slick.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Downloads menu.
|
* Downloads menu.
|
||||||
|
@ -137,6 +143,9 @@ public class DownloadsMenu extends BasicGameState {
|
||||||
/** Beatmap importing thread. */
|
/** Beatmap importing thread. */
|
||||||
private Thread importThread;
|
private Thread importThread;
|
||||||
|
|
||||||
|
/** Beatmap set ID of the current beatmap being previewed, or -1 if none. */
|
||||||
|
private int previewID = -1;
|
||||||
|
|
||||||
// game-related variables
|
// game-related variables
|
||||||
private GameContainer container;
|
private GameContainer container;
|
||||||
private StateBasedGame game;
|
private StateBasedGame game;
|
||||||
|
@ -248,7 +257,8 @@ public class DownloadsMenu extends BasicGameState {
|
||||||
int index = startResult + i;
|
int index = startResult + i;
|
||||||
if (index >= nodes.length)
|
if (index >= nodes.length)
|
||||||
break;
|
break;
|
||||||
nodes[index].drawResult(g, i, DownloadNode.resultContains(mouseX, mouseY, i), (index == focusResult));
|
nodes[index].drawResult(g, i, DownloadNode.resultContains(mouseX, mouseY, i),
|
||||||
|
(index == focusResult), (previewID == nodes[index].getID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// scroll bar
|
// scroll bar
|
||||||
|
@ -441,10 +451,61 @@ public class DownloadsMenu extends BasicGameState {
|
||||||
if (index >= nodes.length)
|
if (index >= nodes.length)
|
||||||
break;
|
break;
|
||||||
if (DownloadNode.resultContains(x, y, i)) {
|
if (DownloadNode.resultContains(x, y, i)) {
|
||||||
DownloadNode node = nodes[index];
|
final DownloadNode node = nodes[index];
|
||||||
|
|
||||||
// check if map is already loaded
|
// check if map is already loaded
|
||||||
if (OsuGroupList.get().containsBeatmapSetID(node.getID()))
|
boolean isLoaded = OsuGroupList.get().containsBeatmapSetID(node.getID());
|
||||||
|
|
||||||
|
// track preview
|
||||||
|
if (DownloadNode.resultIconContains(x, y, i)) {
|
||||||
|
// set focus
|
||||||
|
if (!isLoaded) {
|
||||||
|
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||||
|
focusResult = index;
|
||||||
|
focusTimer = FOCUS_DELAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previewID == node.getID()) {
|
||||||
|
// stop preview
|
||||||
|
previewID = -1;
|
||||||
|
SoundController.stopTrack();
|
||||||
|
} else {
|
||||||
|
// play preview
|
||||||
|
try {
|
||||||
|
final URL url = new URL(server.getPreviewURL(node.getID()));
|
||||||
|
MusicController.pause();
|
||||||
|
new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
previewID = -1;
|
||||||
|
SoundController.playTrack(url, true, new LineListener() {
|
||||||
|
@Override
|
||||||
|
public void update(LineEvent event) {
|
||||||
|
if (event.getType() == LineEvent.Type.STOP) {
|
||||||
|
if (previewID != -1) {
|
||||||
|
SoundController.stopTrack();
|
||||||
|
previewID = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
previewID = node.getID();
|
||||||
|
} catch (SlickException e) {
|
||||||
|
UI.sendBarNotification("Failed to load track preview.");
|
||||||
|
Log.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
UI.sendBarNotification("Could not load track preview (bad URL).");
|
||||||
|
Log.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SoundController.playSound(SoundEffect.MENUCLICK);
|
SoundController.playSound(SoundEffect.MENUCLICK);
|
||||||
|
@ -677,12 +738,15 @@ public class DownloadsMenu extends BasicGameState {
|
||||||
startResult = 0;
|
startResult = 0;
|
||||||
startDownloadIndex = 0;
|
startDownloadIndex = 0;
|
||||||
pageDir = Page.RESET;
|
pageDir = Page.RESET;
|
||||||
|
previewID = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void leave(GameContainer container, StateBasedGame game)
|
public void leave(GameContainer container, StateBasedGame game)
|
||||||
throws SlickException {
|
throws SlickException {
|
||||||
search.setFocus(false);
|
search.setFocus(false);
|
||||||
|
SoundController.stopTrack();
|
||||||
|
MusicController.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1099,10 +1099,9 @@ public class Game extends BasicGameState {
|
||||||
UI.hideCursor();
|
UI.hideCursor();
|
||||||
|
|
||||||
// replays
|
// replays
|
||||||
if (isReplay) {
|
if (isReplay)
|
||||||
GameMod.loadModState(previousMods);
|
GameMod.loadModState(previousMods);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws hit objects, hit results, and follow points.
|
* Draws hit objects, hit results, and follow points.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user