mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 04:38:00 +00:00
- 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:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -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
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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"), /** */
|
||||||
|
|||||||
121
src/main/java/forge/sound/AsyncSoundPlayer.java
Executable file
121
src/main/java/forge/sound/AsyncSoundPlayer.java
Executable 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
Reference in New Issue
Block a user