- Implemented an alternative sound system for people who have issues with sound gradually or instantly disappearing on certain Linux systems (can be switched on/off in the preferences without needing a restart, also uses the standard Java Sound API so it doesn't require an external dependency; it's non-caching and, as such, less efficient than the regular sound system, so only use it in case you have issues with the default system, otherwise leave the option unchecked).

This commit is contained in:
Agetian
2013-04-09 18:35:17 +00:00
parent 2f5a7187c5
commit 07d0df8742
8 changed files with 178 additions and 17 deletions

1
.gitattributes vendored
View File

@@ -14324,6 +14324,7 @@ src/main/java/forge/quest/io/QuestDataIO.java svneol=native#text/plain
src/main/java/forge/quest/io/ReadPriceList.java svneol=native#text/plain src/main/java/forge/quest/io/ReadPriceList.java svneol=native#text/plain
src/main/java/forge/quest/io/package-info.java svneol=native#text/plain src/main/java/forge/quest/io/package-info.java svneol=native#text/plain
src/main/java/forge/quest/package-info.java svneol=native#text/plain src/main/java/forge/quest/package-info.java svneol=native#text/plain
src/main/java/forge/sound/AsyncSoundPlayer.java -text
src/main/java/forge/sound/AudioClip.java -text src/main/java/forge/sound/AudioClip.java -text
src/main/java/forge/sound/EventVisualizer.java -text src/main/java/forge/sound/EventVisualizer.java -text
src/main/java/forge/sound/IAudioClip.java -text src/main/java/forge/sound/IAudioClip.java -text

View File

@@ -186,6 +186,14 @@ public enum CSubmenuPreferences implements ICDoc {
} }
}); });
view.getCbAltSoundSystem().addItemListener(new ItemListener() {
@Override
public void itemStateChanged(final ItemEvent arg0) {
prefs.setPref(FPref.UI_ALT_SOUND_SYSTEM, view.getCbAltSoundSystem().isSelected());
prefs.save();
}
});
view.getBtnReset().setCommand(new Command() { view.getBtnReset().setCommand(new Command() {
@Override @Override
public void execute() { public void execute() {
@@ -222,6 +230,7 @@ public enum CSubmenuPreferences implements ICDoc {
view.getCbScaleLarger().setSelected(prefs.getPrefBoolean(FPref.UI_SCALE_LARGER)); view.getCbScaleLarger().setSelected(prefs.getPrefBoolean(FPref.UI_SCALE_LARGER));
view.getCbTextMana().setSelected(prefs.getPrefBoolean(FPref.UI_CARD_OVERLAY)); view.getCbTextMana().setSelected(prefs.getPrefBoolean(FPref.UI_CARD_OVERLAY));
view.getCbEnableSounds().setSelected(prefs.getPrefBoolean(FPref.UI_ENABLE_SOUNDS)); view.getCbEnableSounds().setSelected(prefs.getPrefBoolean(FPref.UI_ENABLE_SOUNDS));
view.getCbAltSoundSystem().setSelected(prefs.getPrefBoolean(FPref.UI_ALT_SOUND_SYSTEM));
view.reloadShortcuts(); view.reloadShortcuts();
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {

View File

@@ -96,6 +96,7 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
private final JCheckBox cbRandomFoil = new OptionsCheckBox("Random Foil"); private final JCheckBox cbRandomFoil = new OptionsCheckBox("Random Foil");
private final JCheckBox cbRandomizeArt = new OptionsCheckBox("Randomize Card Art"); private final JCheckBox cbRandomizeArt = new OptionsCheckBox("Randomize Card Art");
private final JCheckBox cbEnableSounds = new OptionsCheckBox("Enable Sounds"); private final JCheckBox cbEnableSounds = new OptionsCheckBox("Enable Sounds");
private final JCheckBox cbAltSoundSystem = new OptionsCheckBox("Use Alternative Sound System");
private final Map<FPref, KeyboardShortcutField> shortcutFields = new HashMap<FPref, KeyboardShortcutField>(); private final Map<FPref, KeyboardShortcutField> shortcutFields = new HashMap<FPref, KeyboardShortcutField>();
@@ -178,6 +179,9 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
pnlPrefs.add(cbEnableSounds, regularConstraints); pnlPrefs.add(cbEnableSounds, regularConstraints);
pnlPrefs.add(new NoteLabel("Enable sound effects during the game."), regularConstraints); pnlPrefs.add(new NoteLabel("Enable sound effects during the game."), regularConstraints);
pnlPrefs.add(cbAltSoundSystem, regularConstraints);
pnlPrefs.add(new NoteLabel("Use an alternative sound system (in case your have issues with sound on Linux)"), regularConstraints);
// Keyboard shortcuts // Keyboard shortcuts
final JLabel lblShortcuts = new SectionLabel("Keyboard Shortcuts"); final JLabel lblShortcuts = new SectionLabel("Keyboard Shortcuts");
pnlPrefs.add(lblShortcuts, sectionConstraints); pnlPrefs.add(lblShortcuts, sectionConstraints);
@@ -464,6 +468,11 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
return cbEnableSounds; return cbEnableSounds;
} }
/** @return {@link javax.swing.JCheckBox} */
public JCheckBox getCbAltSoundSystem() {
return cbAltSoundSystem;
}
/** @return {@link forge.gui.toolbox.FLabel} */ /** @return {@link forge.gui.toolbox.FLabel} */
public FLabel getBtnReset() { public FLabel getBtnReset() {
return btnReset; return btnReset;

View File

@@ -50,6 +50,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
UI_PREFERRED_AVATARS_ONLY ("false"), UI_PREFERRED_AVATARS_ONLY ("false"),
UI_TARGETING_OVERLAY ("false"), UI_TARGETING_OVERLAY ("false"),
UI_ENABLE_SOUNDS ("true"), UI_ENABLE_SOUNDS ("true"),
UI_ALT_SOUND_SYSTEM ("false"),
UI_RANDOM_CARD_ART ("false"), UI_RANDOM_CARD_ART ("false"),
UI_CURRENT_AI_PROFILE (AiProfileUtil.AI_PROFILE_RANDOM_MATCH), UI_CURRENT_AI_PROFILE (AiProfileUtil.AI_PROFILE_RANDOM_MATCH),
UI_CLONE_MODE_SOURCE ("false"), /** */ UI_CLONE_MODE_SOURCE ("false"), /** */

View File

@@ -0,0 +1,121 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package forge.sound;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
*
* @author agetian
*/
class AsyncSoundRegistry {
static Map<String, Integer> soundsPlayed = new HashMap<String, Integer>();
public static void registerSound(String soundName) {
if (soundsPlayed.containsKey(soundName)) {
soundsPlayed.put(soundName, soundsPlayed.get(soundName) + 1);
} else {
soundsPlayed.put(soundName, 1);
}
}
public static void unregisterSound(String soundName) {
if (soundsPlayed.containsKey(soundName) && soundsPlayed.get(soundName) != 1) {
soundsPlayed.put(soundName, soundsPlayed.get(soundName) - 1);
} else {
soundsPlayed.remove(soundName);
}
}
public static boolean isRegistered(String soundName) {
return soundsPlayed.containsKey(soundName);
}
public static int getNumIterations(String soundName) {
return soundsPlayed.containsKey(soundName) ? soundsPlayed.get(soundName) : 0;
}
}
public class AsyncSoundPlayer extends Thread {
private String filename;
private boolean isSync;
private final int EXTERNAL_BUFFER_SIZE = 524288;
private final int MAX_SOUND_ITERATIONS = 5;
public AsyncSoundPlayer(String wavfile, boolean synced) {
filename = wavfile;
isSync = synced;
}
public void run() {
if (isSync && AsyncSoundRegistry.isRegistered(filename)) {
return;
}
if (AsyncSoundRegistry.getNumIterations(filename) >= MAX_SOUND_ITERATIONS) {
return;
}
File soundFile = new File(filename);
if (!soundFile.exists()) {
return;
}
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
return;
} catch (IOException e) {
e.printStackTrace();
return;
}
AudioFormat format = audioInputStream.getFormat();
SourceDataLine audioLine = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
audioLine = (SourceDataLine) AudioSystem.getLine(info);
audioLine.open(format);
} catch (Exception e) {
return;
}
audioLine.start();
AsyncSoundRegistry.registerSound(filename);
int nBytesRead = 0;
byte[] audioBufData = new byte[EXTERNAL_BUFFER_SIZE];
try {
while (nBytesRead != -1) {
nBytesRead = audioInputStream.read(audioBufData, 0, audioBufData.length);
if (nBytesRead >= 0)
audioLine.write(audioBufData, 0, nBytesRead);
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
audioLine.drain();
audioLine.close();
AsyncSoundRegistry.unregisterSound(filename);
}
}
}

View File

@@ -43,8 +43,6 @@ public class AudioClip implements IAudioClip {
private Clip clip; private Clip clip;
private final int SOUND_SYSTEM_DELAY = 30; private final int SOUND_SYSTEM_DELAY = 30;
private static final String PathToSound = "res/sound";
public static boolean fileExists(String fileName) { public static boolean fileExists(String fileName) {
File fSound = new File(PathToSound, fileName); File fSound = new File(PathToSound, fileName);
return fSound.exists(); return fSound.exists();

View File

@@ -1,6 +1,8 @@
package forge.sound; package forge.sound;
public interface IAudioClip { public interface IAudioClip {
public static final String PathToSound = "res/sound";
public void play(); public void play();
public boolean isDone(); public boolean isDone();
public void stop(); public void stop();

View File

@@ -22,6 +22,10 @@ public class SoundSystem {
private final EventVisualizer visualizer = new EventVisualizer(); private final EventVisualizer visualizer = new EventVisualizer();
private boolean isUsingAltSystem() {
return Singletons.getModel().getPreferences().getPrefBoolean(FPref.UI_ALT_SOUND_SYSTEM);
}
/** /**
* Fetch a resource based on the sound effect type from the SoundEffectType enumeration. * Fetch a resource based on the sound effect type from the SoundEffectType enumeration.
* *
@@ -67,39 +71,55 @@ public class SoundSystem {
* Play the sound associated with the Sounds enumeration element. * Play the sound associated with the Sounds enumeration element.
*/ */
public void play(SoundEffectType type) { public void play(SoundEffectType type) {
if (isUsingAltSystem()) {
new AsyncSoundPlayer(String.format("%s/%s", IAudioClip.PathToSound, type.getResourceFileName()), false).start();
} else {
fetchResource(type).play(); fetchResource(type).play();
} }
}
/** /**
* Play the sound associated with a specific resource file. * Play the sound associated with a specific resource file.
*/ */
public void play(String resourceFileName) { public void play(String resourceFileName) {
if (isUsingAltSystem()) {
new AsyncSoundPlayer(String.format("%s/%s", IAudioClip.PathToSound, resourceFileName), false).start();
} else {
fetchResource(resourceFileName).play(); fetchResource(resourceFileName).play();
} }
}
/** /**
* Play the sound associated with the resource specified by the file name * Play the sound associated with the resource specified by the file name
* (synchronized with other sounds of the same kind, so only one can play * (synchronized with other sounds of the same kind, so only one can play at
* at the same time). * the same time).
*/ */
public void playSync(String resourceFileName) { public void playSync(String resourceFileName) {
if (isUsingAltSystem()) {
new AsyncSoundPlayer(String.format("%s/%s", IAudioClip.PathToSound, resourceFileName), false).start();
} else {
IAudioClip snd = fetchResource(resourceFileName); IAudioClip snd = fetchResource(resourceFileName);
if (snd.isDone()) { if (snd.isDone()) {
snd.play(); snd.play();
} }
} }
}
/** /**
* Play the sound associated with the Sounds enumeration element * Play the sound associated with the Sounds enumeration element
* (synchronized with other sounds of the same kind, so only one can play * (synchronized with other sounds of the same kind, so only one can play at
* at the same time). * the same time).
*/ */
public void playSync(SoundEffectType type) { public void playSync(SoundEffectType type) {
if (isUsingAltSystem()) {
new AsyncSoundPlayer(String.format("%s/%s", IAudioClip.PathToSound, type.getResourceFileName()), false).start();
} else {
IAudioClip snd = fetchResource(type); IAudioClip snd = fetchResource(type);
if (snd.isDone()) { if (snd.isDone()) {
snd.play(); snd.play();
} }
} }
}
/** /**
* Play the sound in a looping manner until 'stop' is called. * Play the sound in a looping manner until 'stop' is called.