SoundController refactoring.
- Added enum classes SoundEffect and HitSound. - Added a SoundComponent interface for sound effects and hit sounds, and extended playSound() and playHitSound() methods to play any SoundComponent. - Moved features related to sample sets to the HitSound class, and rewrote sample sets as an internal enum class. Signed-off-by: Jeffrey Han <itdelatrisu@gmail.com>
This commit is contained in:
159
src/itdelatrisu/opsu/audio/HitSound.java
Normal file
159
src/itdelatrisu/opsu/audio/HitSound.java
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* opsu! - an open-source osu! client
|
||||
* Copyright (C) 2014 Jeffrey Han
|
||||
*
|
||||
* opsu! is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu! is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package itdelatrisu.opsu.audio;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.sound.sampled.Clip;
|
||||
|
||||
/**
|
||||
* Hit sounds.
|
||||
*/
|
||||
public enum HitSound implements SoundController.SoundComponent {
|
||||
CLAP ("hitclap"),
|
||||
FINISH ("hitfinish"),
|
||||
NORMAL ("hitnormal"),
|
||||
WHISTLE ("hitwhistle"),
|
||||
SLIDERSLIDE ("sliderslide"),
|
||||
SLIDERTICK ("slidertick"),
|
||||
SLIDERWHISTLE ("sliderwhistle");
|
||||
|
||||
/**
|
||||
* Sound sample sets.
|
||||
*/
|
||||
public static enum SampleSet {
|
||||
NORMAL ("normal", 1),
|
||||
SOFT ("soft", 2),
|
||||
DRUM ("drum", 3);
|
||||
// TAIKO ("taiko", 4);
|
||||
|
||||
/**
|
||||
* The sample set name.
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* The sample set index.
|
||||
*/
|
||||
private int index;
|
||||
|
||||
/**
|
||||
* Total number of sample sets.
|
||||
*/
|
||||
public static final int SIZE = SampleSet.values().length;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param name the sample set name
|
||||
*/
|
||||
SampleSet(String name, int index) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sample set name.
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() { return name; }
|
||||
|
||||
/**
|
||||
* Returns the sample set index.
|
||||
* @return the index
|
||||
*/
|
||||
public int getIndex() { return index; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Current sample set.
|
||||
*/
|
||||
private static SampleSet currentSampleSet;
|
||||
|
||||
/**
|
||||
* The file name.
|
||||
*/
|
||||
private String filename;
|
||||
|
||||
/**
|
||||
* The Clip associated with the hit sound.
|
||||
*/
|
||||
private HashMap<SampleSet, Clip> clips;
|
||||
|
||||
/**
|
||||
* Total number of hit sounds.
|
||||
*/
|
||||
public static final int SIZE = HitSound.values().length;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param filename the sound file name
|
||||
*/
|
||||
HitSound(String filename) {
|
||||
this.filename = filename;
|
||||
this.clips = new HashMap<SampleSet, Clip>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file name.
|
||||
* @return the file name
|
||||
*/
|
||||
public String getFileName() { return filename; }
|
||||
|
||||
@Override
|
||||
public Clip getClip() {
|
||||
return (currentSampleSet != null) ? clips.get(currentSampleSet) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hit sound Clip for the sample type.
|
||||
* @param s the sample set
|
||||
* @param clip the Clip
|
||||
*/
|
||||
public void setClip(SampleSet s, Clip clip) {
|
||||
clips.put(s, clip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sample set to use when playing hit sounds.
|
||||
* @param sampleSet the sample set ("None", "Normal", "Soft", "Drum")
|
||||
*/
|
||||
public static void setSampleSet(String sampleSet) {
|
||||
currentSampleSet = null;
|
||||
for (SampleSet ss : SampleSet.values()) {
|
||||
if (sampleSet.equalsIgnoreCase(ss.getName())) {
|
||||
currentSampleSet = ss;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sample set to use when playing hit sounds.
|
||||
* @param sampleType the sample set (0:none, 1:normal, 2:soft, 3:drum)
|
||||
*/
|
||||
public static void setSampleSet(byte sampleType) {
|
||||
currentSampleSet = null;
|
||||
for (SampleSet ss : SampleSet.values()) {
|
||||
if (sampleType == ss.getIndex()) {
|
||||
currentSampleSet = ss;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
341
src/itdelatrisu/opsu/audio/MusicController.java
Normal file
341
src/itdelatrisu/opsu/audio/MusicController.java
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* opsu! - an open-source osu! client
|
||||
* Copyright (C) 2014 Jeffrey Han
|
||||
*
|
||||
* opsu! is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu! is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package itdelatrisu.opsu.audio;
|
||||
|
||||
import itdelatrisu.opsu.OsuFile;
|
||||
import itdelatrisu.opsu.OsuParser;
|
||||
import itdelatrisu.opsu.states.Options;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.IntBuffer;
|
||||
|
||||
import javazoom.jl.converter.Converter;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.openal.AL;
|
||||
import org.lwjgl.openal.AL10;
|
||||
import org.newdawn.slick.Music;
|
||||
import org.newdawn.slick.SlickException;
|
||||
import org.newdawn.slick.openal.Audio;
|
||||
import org.newdawn.slick.openal.SoundStore;
|
||||
import org.newdawn.slick.util.Log;
|
||||
|
||||
/**
|
||||
* Controller for all music.
|
||||
*/
|
||||
public class MusicController {
|
||||
/**
|
||||
* The current music track.
|
||||
*/
|
||||
private static Music player;
|
||||
|
||||
/**
|
||||
* The last OsuFile passed to play().
|
||||
*/
|
||||
private static OsuFile lastOsu;
|
||||
|
||||
/**
|
||||
* Temporary WAV file for file conversions (to be deleted).
|
||||
*/
|
||||
private static File wavFile;
|
||||
|
||||
/**
|
||||
* Thread for loading tracks.
|
||||
*/
|
||||
private static Thread trackLoader;
|
||||
|
||||
/**
|
||||
* Whether the theme song is currently playing.
|
||||
*/
|
||||
private static boolean themePlaying = false;
|
||||
|
||||
// This class should not be instantiated.
|
||||
private MusicController() {}
|
||||
|
||||
/**
|
||||
* Plays an audio file at the preview position.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void play(final OsuFile osu, final boolean loop) {
|
||||
boolean play = (lastOsu == null || !osu.audioFilename.equals(lastOsu.audioFilename));
|
||||
lastOsu = osu;
|
||||
if (play) {
|
||||
themePlaying = false;
|
||||
|
||||
// TODO: properly interrupt instead of using deprecated Thread.stop();
|
||||
// interrupt the conversion/track loading
|
||||
if (isTrackLoading())
|
||||
// trackLoader.interrupt();
|
||||
trackLoader.stop();
|
||||
|
||||
if (wavFile != null)
|
||||
wavFile.delete();
|
||||
|
||||
// releases all sources from previous tracks
|
||||
destroyOpenAL();
|
||||
|
||||
switch (OsuParser.getExtension(osu.audioFilename.getName())) {
|
||||
case "ogg":
|
||||
trackLoader = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
loadTrack(osu.audioFilename, osu.previewTime, loop);
|
||||
}
|
||||
};
|
||||
trackLoader.start();
|
||||
break;
|
||||
case "mp3":
|
||||
trackLoader = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
convertMp3(osu.audioFilename);
|
||||
// if (!Thread.currentThread().isInterrupted())
|
||||
loadTrack(wavFile, osu.previewTime, loop);
|
||||
}
|
||||
};
|
||||
trackLoader.start();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a track and plays it.
|
||||
*/
|
||||
private static void loadTrack(File file, int previewTime, boolean loop) {
|
||||
try { // create a new player
|
||||
player = new Music(file.getPath());
|
||||
playAt((previewTime > 0) ? previewTime : 0, loop);
|
||||
} catch (Exception e) {
|
||||
Log.error(String.format("Could not play track '%s'.", file.getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the current track at the given position.
|
||||
*/
|
||||
public static void playAt(final int position, final boolean loop) {
|
||||
if (trackExists()) {
|
||||
SoundStore.get().setMusicVolume(Options.getMusicVolume());
|
||||
player.setPosition(position / 1000f);
|
||||
if (loop)
|
||||
player.loop();
|
||||
else
|
||||
player.play();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an MP3 file to a temporary WAV file.
|
||||
*/
|
||||
private static File convertMp3(File file) {
|
||||
try {
|
||||
wavFile = File.createTempFile(".osu", ".wav", Options.TMP_DIR);
|
||||
wavFile.deleteOnExit();
|
||||
|
||||
Converter converter = new Converter();
|
||||
converter.convert(file.getPath(), wavFile.getPath());
|
||||
return wavFile;
|
||||
} catch (Exception e) {
|
||||
Log.error(String.format("Failed to play file '%s'.", file.getAbsolutePath()), e);
|
||||
}
|
||||
return wavFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a track is being loaded.
|
||||
*/
|
||||
public static boolean isTrackLoading() {
|
||||
return (trackLoader != null && trackLoader.isAlive());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a track is loaded.
|
||||
*/
|
||||
public static boolean trackExists() {
|
||||
return (player != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the OsuFile associated with the current track.
|
||||
*/
|
||||
public static OsuFile getOsuFile() { return lastOsu; }
|
||||
|
||||
/**
|
||||
* Returns the name of the current track.
|
||||
*/
|
||||
public static String getTrackName() {
|
||||
if (!trackExists() || lastOsu == null)
|
||||
return null;
|
||||
return lastOsu.getTitle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the artist of the current track.
|
||||
*/
|
||||
public static String getArtistName() {
|
||||
if (!trackExists() || lastOsu == null)
|
||||
return null;
|
||||
return lastOsu.getArtist();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current track is playing.
|
||||
*/
|
||||
public static boolean isPlaying() {
|
||||
return (trackExists() && player.playing());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the current track.
|
||||
*/
|
||||
public static void pause() {
|
||||
if (isPlaying())
|
||||
player.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the current track.
|
||||
*/
|
||||
public static void resume() {
|
||||
if (trackExists()) {
|
||||
player.resume();
|
||||
player.setVolume(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the current track.
|
||||
*/
|
||||
public static void stop() {
|
||||
if (isPlaying())
|
||||
player.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fades out the track.
|
||||
*/
|
||||
public static void fadeOut(int duration) {
|
||||
if (isPlaying())
|
||||
player.fade(duration, 0f, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the position in the current track, in ms.
|
||||
* If no track is playing, 0 will be returned.
|
||||
*/
|
||||
public static int getPosition() {
|
||||
if (isPlaying())
|
||||
return Math.max((int) (player.getPosition() * 1000 + Options.getMusicOffset()), 0);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeks to a position in the current track.
|
||||
*/
|
||||
public static boolean setPosition(int position) {
|
||||
return (trackExists() && player.setPosition(position / 1000f));
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the theme song.
|
||||
*/
|
||||
public static void playThemeSong() {
|
||||
OsuFile osu = Options.getOsuTheme();
|
||||
if (osu != null) {
|
||||
play(osu, true);
|
||||
themePlaying = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the current track, if any, is the theme song.
|
||||
*/
|
||||
public static boolean isThemePlaying() {
|
||||
return (themePlaying && trackExists());
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops and releases all sources, clears each of the specified Audio
|
||||
* buffers, destroys the OpenAL context, and resets SoundStore for future use.
|
||||
*
|
||||
* Calling SoundStore.get().init() will re-initialize the OpenAL context
|
||||
* after a call to destroyOpenAL (Note: AudioLoader.getXXX calls init for you).
|
||||
*
|
||||
* @author davedes (http://slick.ninjacave.com/forum/viewtopic.php?t=3920)
|
||||
*/
|
||||
private static void destroyOpenAL() {
|
||||
if (!trackExists())
|
||||
return;
|
||||
stop();
|
||||
|
||||
try {
|
||||
// get Music object's (private) Audio object reference
|
||||
Field sound = player.getClass().getDeclaredField("sound");
|
||||
sound.setAccessible(true);
|
||||
Audio audio = (Audio) (sound.get(player));
|
||||
|
||||
// first clear the sources allocated by SoundStore
|
||||
int max = SoundStore.get().getSourceCount();
|
||||
IntBuffer buf = BufferUtils.createIntBuffer(max);
|
||||
for (int i = 0; i < max; i++) {
|
||||
int source = SoundStore.get().getSource(i);
|
||||
buf.put(source);
|
||||
|
||||
// stop and detach any buffers at this source
|
||||
AL10.alSourceStop(source);
|
||||
AL10.alSourcei(source, AL10.AL_BUFFER, 0);
|
||||
}
|
||||
buf.flip();
|
||||
AL10.alDeleteSources(buf);
|
||||
int exc = AL10.alGetError();
|
||||
if (exc != AL10.AL_NO_ERROR) {
|
||||
throw new SlickException(
|
||||
"Could not clear SoundStore sources, err: " + exc);
|
||||
}
|
||||
|
||||
// delete any buffer data stored in memory, too...
|
||||
if (audio != null && audio.getBufferID() != 0) {
|
||||
buf = BufferUtils.createIntBuffer(1).put(audio.getBufferID());
|
||||
buf.flip();
|
||||
AL10.alDeleteBuffers(buf);
|
||||
exc = AL10.alGetError();
|
||||
if (exc != AL10.AL_NO_ERROR) {
|
||||
throw new SlickException("Could not clear buffer "
|
||||
+ audio.getBufferID()
|
||||
+ ", err: "+exc);
|
||||
}
|
||||
}
|
||||
|
||||
// clear OpenAL
|
||||
AL.destroy();
|
||||
|
||||
// reset SoundStore so that next time we create a Sound/Music, it will reinit
|
||||
SoundStore.get().clear();
|
||||
|
||||
player = null;
|
||||
} catch (Exception e) {
|
||||
Log.error("Failed to destroy OpenAL.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
225
src/itdelatrisu/opsu/audio/SoundController.java
Normal file
225
src/itdelatrisu/opsu/audio/SoundController.java
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* opsu! - an open-source osu! client
|
||||
* Copyright (C) 2014 Jeffrey Han
|
||||
*
|
||||
* opsu! is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu! is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package itdelatrisu.opsu.audio;
|
||||
|
||||
import itdelatrisu.opsu.OsuHitObject;
|
||||
import itdelatrisu.opsu.audio.HitSound.SampleSet;
|
||||
import itdelatrisu.opsu.states.Options;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import javax.sound.sampled.AudioInputStream;
|
||||
import javax.sound.sampled.AudioSystem;
|
||||
import javax.sound.sampled.Clip;
|
||||
import javax.sound.sampled.DataLine;
|
||||
import javax.sound.sampled.FloatControl;
|
||||
import javax.sound.sampled.LineUnavailableException;
|
||||
import javax.sound.sampled.UnsupportedAudioFileException;
|
||||
|
||||
import org.newdawn.slick.util.Log;
|
||||
import org.newdawn.slick.util.ResourceLoader;
|
||||
|
||||
/**
|
||||
* Controller for all (non-music) sound components.
|
||||
* Note: Uses Java Sound because OpenAL lags too much for accurate hit sounds.
|
||||
*/
|
||||
public class SoundController {
|
||||
/**
|
||||
* Interface for all (non-music) sound components.
|
||||
*/
|
||||
public interface SoundComponent {
|
||||
/**
|
||||
* Returns the Clip associated with the sound component.
|
||||
* @return the Clip
|
||||
*/
|
||||
public Clip getClip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample volume multiplier, from timing points [0, 1].
|
||||
*/
|
||||
private static float sampleVolumeMultiplier = 1f;
|
||||
|
||||
/**
|
||||
* The name of the current sound file being loaded.
|
||||
*/
|
||||
private static String currentFileName;
|
||||
|
||||
/**
|
||||
* The number of the current sound file being loaded.
|
||||
*/
|
||||
private static int currentFileIndex = -1;
|
||||
|
||||
// This class should not be instantiated.
|
||||
private SoundController() {}
|
||||
|
||||
/**
|
||||
* Loads and returns a Clip from a resource.
|
||||
* @param ref the resource name
|
||||
* @return the loaded and opened clip
|
||||
*/
|
||||
private static Clip loadClip(String ref) {
|
||||
try {
|
||||
URL url = ResourceLoader.getResource(ref);
|
||||
AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
|
||||
|
||||
// GNU/Linux workaround
|
||||
// Clip clip = AudioSystem.getClip();
|
||||
AudioFormat format = audioIn.getFormat();
|
||||
DataLine.Info info = new DataLine.Info(Clip.class, format);
|
||||
Clip clip = (Clip) AudioSystem.getLine(info);
|
||||
|
||||
clip.open(audioIn);
|
||||
return clip;
|
||||
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException | RuntimeException e) {
|
||||
Log.error(String.format("Failed to load file '%s'.", ref), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all sound files.
|
||||
*/
|
||||
public static void init() {
|
||||
if (Options.isSoundDisabled())
|
||||
return;
|
||||
|
||||
// TODO: support MP3 sounds?
|
||||
currentFileIndex = 0;
|
||||
|
||||
// menu and game sounds
|
||||
for (SoundEffect s : SoundEffect.values()) {
|
||||
currentFileName = String.format("%s.wav", s.getFileName());
|
||||
s.setClip(loadClip(currentFileName));
|
||||
currentFileIndex++;
|
||||
}
|
||||
|
||||
// hit sounds
|
||||
for (SampleSet ss : SampleSet.values()) {
|
||||
for (HitSound s : HitSound.values()) {
|
||||
currentFileName = String.format("%s-%s.wav", ss.getName(), s.getFileName());
|
||||
s.setClip(ss, loadClip(currentFileName));
|
||||
currentFileIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
currentFileName = null;
|
||||
currentFileIndex = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sample volume (modifies the global sample volume).
|
||||
* @param volume the sample volume [0, 1]
|
||||
*/
|
||||
public static void setSampleVolume(float volume) {
|
||||
if (volume >= 0f && volume <= 1f)
|
||||
sampleVolumeMultiplier = volume;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a sound clip.
|
||||
* @param clip the Clip to play
|
||||
* @param volume the volume [0, 1]
|
||||
*/
|
||||
private static void playClip(Clip clip, float volume) {
|
||||
if (clip == null) // clip failed to load properly
|
||||
return;
|
||||
|
||||
if (volume > 0f) {
|
||||
// stop clip if running
|
||||
if (clip.isRunning()) {
|
||||
clip.stop();
|
||||
clip.flush();
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a sound.
|
||||
* @param s the sound effect
|
||||
*/
|
||||
public static void playSound(SoundComponent s) {
|
||||
playClip(s.getClip(), Options.getEffectVolume());
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays hit sound(s) using an OsuHitObject bitmask.
|
||||
* @param hitSound the hit sound (bitmask)
|
||||
*/
|
||||
public static void playHitSound(byte hitSound) {
|
||||
if (hitSound < 0)
|
||||
return;
|
||||
|
||||
float volume = Options.getHitSoundVolume() * sampleVolumeMultiplier;
|
||||
if (volume == 0f)
|
||||
return;
|
||||
|
||||
// play all sounds
|
||||
if (hitSound == OsuHitObject.SOUND_NORMAL)
|
||||
playClip(HitSound.NORMAL.getClip(), volume);
|
||||
else {
|
||||
if ((hitSound & OsuHitObject.SOUND_WHISTLE) > 0)
|
||||
playClip(HitSound.WHISTLE.getClip(), volume);
|
||||
if ((hitSound & OsuHitObject.SOUND_FINISH) > 0)
|
||||
playClip(HitSound.FINISH.getClip(), volume);
|
||||
if ((hitSound & OsuHitObject.SOUND_CLAP) > 0)
|
||||
playClip(HitSound.CLAP.getClip(), volume);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a hit sound.
|
||||
* @param s the hit sound
|
||||
*/
|
||||
public static void playHitSound(SoundComponent s) {
|
||||
playClip(s.getClip(), Options.getHitSoundVolume() * sampleVolumeMultiplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the current file being loaded, or null if none.
|
||||
*/
|
||||
public static String getCurrentFileName() {
|
||||
return (currentFileName != null) ? currentFileName : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the progress of sound loading, or -1 if not loading.
|
||||
* @return the completion percent [0, 100] or -1
|
||||
*/
|
||||
public static int getLoadingProgress() {
|
||||
if (currentFileIndex == -1)
|
||||
return -1;
|
||||
|
||||
return currentFileIndex * 100 / (SoundEffect.SIZE + (HitSound.SIZE * SampleSet.SIZE));
|
||||
}
|
||||
}
|
||||
83
src/itdelatrisu/opsu/audio/SoundEffect.java
Normal file
83
src/itdelatrisu/opsu/audio/SoundEffect.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* opsu! - an open-source osu! client
|
||||
* Copyright (C) 2014 Jeffrey Han
|
||||
*
|
||||
* opsu! is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* opsu! is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with opsu!. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package itdelatrisu.opsu.audio;
|
||||
|
||||
import javax.sound.sampled.Clip;
|
||||
|
||||
/**
|
||||
* Sound effects.
|
||||
*/
|
||||
public enum SoundEffect implements SoundController.SoundComponent {
|
||||
APPLAUSE ("applause"),
|
||||
COMBOBREAK ("combobreak"),
|
||||
// COUNT ("count"), // ?
|
||||
COUNT1 ("count1s"),
|
||||
COUNT2 ("count2s"),
|
||||
COUNT3 ("count3s"),
|
||||
FAIL ("failsound"),
|
||||
GO ("gos"),
|
||||
MENUBACK ("menuback"),
|
||||
MENUCLICK ("menuclick"),
|
||||
MENUHIT ("menuhit"),
|
||||
READY ("readys"),
|
||||
SECTIONFAIL ("sectionfail"),
|
||||
SECTIONPASS ("sectionpass"),
|
||||
SHUTTER ("shutter"),
|
||||
SPINNERBONUS ("spinnerbonus"),
|
||||
SPINNEROSU ("spinner-osu"),
|
||||
SPINNERSPIN ("spinnerspin");
|
||||
|
||||
/**
|
||||
* The file name.
|
||||
*/
|
||||
private String filename;
|
||||
|
||||
/**
|
||||
* The Clip associated with the sound effect.
|
||||
*/
|
||||
private Clip clip;
|
||||
|
||||
/**
|
||||
* Total number of sound effects.
|
||||
*/
|
||||
public static final int SIZE = SoundEffect.values().length;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param filename the sound file name
|
||||
*/
|
||||
SoundEffect(String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file name.
|
||||
* @return the file name
|
||||
*/
|
||||
public String getFileName() { return filename; }
|
||||
|
||||
@Override
|
||||
public Clip getClip() { return clip; }
|
||||
|
||||
/**
|
||||
* Sets the Clip for the sound.
|
||||
* @param clip the clip
|
||||
*/
|
||||
public void setClip(Clip clip) { this.clip = clip; }
|
||||
}
|
||||
Reference in New Issue
Block a user