- 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/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/EventVisualizer.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() {
@Override
public void execute() {
@@ -222,6 +230,7 @@ public enum CSubmenuPreferences implements ICDoc {
view.getCbScaleLarger().setSelected(prefs.getPrefBoolean(FPref.UI_SCALE_LARGER));
view.getCbTextMana().setSelected(prefs.getPrefBoolean(FPref.UI_CARD_OVERLAY));
view.getCbEnableSounds().setSelected(prefs.getPrefBoolean(FPref.UI_ENABLE_SOUNDS));
view.getCbAltSoundSystem().setSelected(prefs.getPrefBoolean(FPref.UI_ALT_SOUND_SYSTEM));
view.reloadShortcuts();
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 cbRandomizeArt = new OptionsCheckBox("Randomize Card Art");
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>();
@@ -178,6 +179,9 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
pnlPrefs.add(cbEnableSounds, 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
final JLabel lblShortcuts = new SectionLabel("Keyboard Shortcuts");
pnlPrefs.add(lblShortcuts, sectionConstraints);
@@ -464,6 +468,11 @@ public enum VSubmenuPreferences implements IVSubmenu<CSubmenuPreferences> {
return cbEnableSounds;
}
/** @return {@link javax.swing.JCheckBox} */
public JCheckBox getCbAltSoundSystem() {
return cbAltSoundSystem;
}
/** @return {@link forge.gui.toolbox.FLabel} */
public FLabel getBtnReset() {
return btnReset;

View File

@@ -50,6 +50,7 @@ public class ForgePreferences extends PreferencesStore<ForgePreferences.FPref> {
UI_PREFERRED_AVATARS_ONLY ("false"),
UI_TARGETING_OVERLAY ("false"),
UI_ENABLE_SOUNDS ("true"),
UI_ALT_SOUND_SYSTEM ("false"),
UI_RANDOM_CARD_ART ("false"),
UI_CURRENT_AI_PROFILE (AiProfileUtil.AI_PROFILE_RANDOM_MATCH),
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 final int SOUND_SYSTEM_DELAY = 30;
private static final String PathToSound = "res/sound";
public static boolean fileExists(String fileName) {
File fSound = new File(PathToSound, fileName);
return fSound.exists();

View File

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

View File

@@ -22,6 +22,10 @@ public class SoundSystem {
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.
*
@@ -67,38 +71,54 @@ public class SoundSystem {
* Play the sound associated with the Sounds enumeration element.
*/
public void play(SoundEffectType type) {
fetchResource(type).play();
if (isUsingAltSystem()) {
new AsyncSoundPlayer(String.format("%s/%s", IAudioClip.PathToSound, type.getResourceFileName()), false).start();
} else {
fetchResource(type).play();
}
}
/**
* Play the sound associated with a specific resource file.
*/
public void play(String resourceFileName) {
fetchResource(resourceFileName).play();
if (isUsingAltSystem()) {
new AsyncSoundPlayer(String.format("%s/%s", IAudioClip.PathToSound, resourceFileName), false).start();
} else {
fetchResource(resourceFileName).play();
}
}
/**
* 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
* at the same time).
* 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 at
* the same time).
*/
public void playSync(String resourceFileName) {
IAudioClip snd = fetchResource(resourceFileName);
if (snd.isDone()) {
snd.play();
}
if (isUsingAltSystem()) {
new AsyncSoundPlayer(String.format("%s/%s", IAudioClip.PathToSound, resourceFileName), false).start();
} else {
IAudioClip snd = fetchResource(resourceFileName);
if (snd.isDone()) {
snd.play();
}
}
}
/**
* Play the sound associated with the Sounds enumeration element
* (synchronized with other sounds of the same kind, so only one can play
* at the same time).
* (synchronized with other sounds of the same kind, so only one can play at
* the same time).
*/
public void playSync(SoundEffectType type) {
IAudioClip snd = fetchResource(type);
if (snd.isDone()) {
snd.play();
}
if (isUsingAltSystem()) {
new AsyncSoundPlayer(String.format("%s/%s", IAudioClip.PathToSound, type.getResourceFileName()), false).start();
} else {
IAudioClip snd = fetchResource(type);
if (snd.isDone()) {
snd.play();
}
}
}
/**