Refactor audio manager, add line pool.

This commit is contained in:
rkfg 2016-03-07 22:33:30 +03:00
parent cd4d2a12a3
commit d8ff28c7f7
3 changed files with 221 additions and 134 deletions

View file

@ -55,8 +55,8 @@ public class AudioManager {
/**
* AudioManager singleton.
*/
private static final AudioManager audioManager = new AudioManager();;
private static final AudioManager audioManager = new AudioManager();
private final LinePool linePool = new LinePool();
public static AudioManager getManager() {
return audioManager;
@ -64,88 +64,77 @@ public class AudioManager {
public static void playNextPage() {
if (audioManager.nextPageClip == null) {
audioManager.nextPageClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnPrevPage.wav"),
AudioGroup.OtherSounds);
audioManager.nextPageClip = new MageClip(Constants.BASE_SOUND_PATH + "OnPrevPage.wav", AudioGroup.OtherSounds);
}
checkAndPlayClip(getManager().nextPageClip);
}
public static void playPrevPage() {
if (audioManager.prevPageClip == null) {
audioManager.prevPageClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnPrevPage.wav"),
AudioGroup.OtherSounds);
audioManager.prevPageClip = new MageClip(Constants.BASE_SOUND_PATH + "OnPrevPage.wav", AudioGroup.OtherSounds);
}
checkAndPlayClip(getManager().prevPageClip);
}
public static void playAnotherTab() {
if (audioManager.anotherTabClip == null) {
audioManager.anotherTabClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnNextPage.wav"),
AudioGroup.OtherSounds);
audioManager.anotherTabClip = new MageClip(Constants.BASE_SOUND_PATH + "OnNextPage.wav", AudioGroup.OtherSounds);
}
checkAndPlayClip(getManager().anotherTabClip);
}
public static void playNextPhase() {
if (audioManager.nextPhaseClip == null) {
audioManager.nextPhaseClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnNextPhase.wav"),
AudioGroup.GameSounds);
audioManager.nextPhaseClip = new MageClip(Constants.BASE_SOUND_PATH + "OnNextPhase.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().nextPhaseClip);
}
public static void playEndTurn() {
if (audioManager.endTurnClip == null) {
audioManager.endTurnClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnEndTurn.wav"),
AudioGroup.GameSounds);
audioManager.endTurnClip = new MageClip(Constants.BASE_SOUND_PATH + "OnEndTurn.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().endTurnClip);
}
public static void playTapPermanent() {
if (audioManager.tapPermanentClip == null) {
audioManager.tapPermanentClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnTapPermanent.wav"),
AudioGroup.GameSounds);
audioManager.tapPermanentClip = new MageClip(Constants.BASE_SOUND_PATH + "OnTapPermanent.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().tapPermanentClip);
}
public static void playSummon() {
if (audioManager.summonClip == null) {
audioManager.summonClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnSummon.wav"),
AudioGroup.GameSounds);
audioManager.summonClip = new MageClip(Constants.BASE_SOUND_PATH + "OnSummon.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().summonClip);
}
public static void playDiedCreature() {
if (audioManager.diedCreatureClip == null) {
audioManager.diedCreatureClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnSummon-.wav"),
AudioGroup.GameSounds);
audioManager.diedCreatureClip = new MageClip(Constants.BASE_SOUND_PATH + "OnSummon-.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().diedCreatureClip);
}
public static void playDraw() {
if (audioManager.drawClip == null) {
audioManager.drawClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnDraw.wav"),
AudioGroup.GameSounds);
audioManager.drawClip = new MageClip(Constants.BASE_SOUND_PATH + "OnDraw.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().drawClip);
}
public static void playButtonOk() {
if (audioManager.buttonOkClip == null) {
audioManager.buttonOkClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnButtonOk.wav"),
AudioGroup.GameSounds);
audioManager.buttonOkClip = new MageClip(Constants.BASE_SOUND_PATH + "OnButtonOk.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().buttonOkClip);
}
public static void playButtonCancel() {
if (audioManager.buttonCancelClip == null) {
audioManager.buttonCancelClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnButtonCancel.wav"),
AudioGroup.SkipSounds);
audioManager.buttonCancelClip = new MageClip(Constants.BASE_SOUND_PATH + "OnButtonCancel.wav", AudioGroup.SkipSounds);
}
checkAndPlayClip(getManager().buttonCancelClip);
@ -153,117 +142,105 @@ public class AudioManager {
public static void playAttack() {
if (audioManager.attackClip == null) {
audioManager.attackClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnAttack.wav"),
AudioGroup.GameSounds);
audioManager.attackClip = new MageClip(Constants.BASE_SOUND_PATH + "OnAttack.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().attackClip);
}
public static void playBlock() {
if (audioManager.blockClip == null) {
audioManager.blockClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnBlock.wav"),
AudioGroup.GameSounds);
audioManager.blockClip = new MageClip(Constants.BASE_SOUND_PATH + "OnBlock.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().blockClip);
}
public static void playAddPermanent() {
if (audioManager.addPermanentClip == null) {
audioManager.addPermanentClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnAddPermanent.wav"),
AudioGroup.GameSounds);
audioManager.addPermanentClip = new MageClip(Constants.BASE_SOUND_PATH + "OnAddPermanent.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().addPermanentClip);
}
public static void playAddArtifact() {
if (audioManager.addArtifactClip == null) {
audioManager.addArtifactClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnAddArtifact.wav"),
AudioGroup.GameSounds);
audioManager.addArtifactClip = new MageClip(Constants.BASE_SOUND_PATH + "OnAddArtifact.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().addArtifactClip);
}
public static void playStackNew() {
if (audioManager.updateStackClip == null) {
audioManager.updateStackClip = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnStackNew.wav"),
AudioGroup.GameSounds);
audioManager.updateStackClip = new MageClip(Constants.BASE_SOUND_PATH + "OnStackNew.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().updateStackClip);
}
public static void playOnHover() {
if (audioManager.onHover == null) {
audioManager.onHover = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnHover.wav"),
AudioGroup.GameSounds);
audioManager.onHover = new MageClip(Constants.BASE_SOUND_PATH + "OnHover.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().onHover);
}
public static void playOnCountdown1() {
if (audioManager.onCountdown1 == null) {
audioManager.onCountdown1 = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnCountdown1.wav"),
AudioGroup.DraftSounds);
audioManager.onCountdown1 = new MageClip(Constants.BASE_SOUND_PATH + "OnCountdown1.wav", AudioGroup.DraftSounds);
}
checkAndPlayClip(getManager().onCountdown1);
}
public static void playOnDraftSelect() {
if (audioManager.onDraftSelect == null) {
audioManager.onDraftSelect = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnDraftSelect.wav"),
AudioGroup.DraftSounds);
audioManager.onDraftSelect = new MageClip(Constants.BASE_SOUND_PATH + "OnDraftSelect.wav", AudioGroup.DraftSounds);
}
checkAndPlayClip(getManager().onDraftSelect);
}
public static void playOnSkipButton() {
if (audioManager.onSkipButton == null) {
audioManager.onSkipButton = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnSkipButton.wav"),
AudioGroup.SkipSounds);
audioManager.onSkipButton = new MageClip(Constants.BASE_SOUND_PATH + "OnSkipButton.wav", AudioGroup.SkipSounds);
}
checkAndPlayClip(getManager().onSkipButton);
}
public static void playOnSkipButtonCancel() {
if (audioManager.onSkipButtonCancel == null) {
audioManager.onSkipButtonCancel = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnSkipButtonCancel.wav"),
AudioGroup.SkipSounds);
audioManager.onSkipButtonCancel = new MageClip(Constants.BASE_SOUND_PATH + "OnSkipButtonCancel.wav", AudioGroup.SkipSounds);
}
checkAndPlayClip(getManager().onSkipButtonCancel);
}
public static void playPlayerJoinedTable() {
if (audioManager.playerJoinedTable == null) {
audioManager.playerJoinedTable = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnPlayerJoined.wav"),
AudioGroup.OtherSounds);
audioManager.playerJoinedTable = new MageClip(Constants.BASE_SOUND_PATH + "OnPlayerJoined.wav", AudioGroup.OtherSounds);
}
checkAndPlayClip(getManager().playerJoinedTable);
}
public static void playYourGameStarted() {
if (audioManager.yourGameStarted == null) {
audioManager.yourGameStarted = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnGameStart.wav"),
AudioGroup.OtherSounds);
audioManager.yourGameStarted = new MageClip(Constants.BASE_SOUND_PATH + "OnGameStart.wav", AudioGroup.OtherSounds);
}
checkAndPlayClip(getManager().yourGameStarted);
}
public static void playTournamentStarted() {
if (audioManager.tournamentStarted == null) {
audioManager.tournamentStarted = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnTournamentStart.wav"),
AudioGroup.OtherSounds);
audioManager.tournamentStarted = new MageClip(Constants.BASE_SOUND_PATH + "OnTournamentStart.wav", AudioGroup.OtherSounds);
}
checkAndPlayClip(getManager().tournamentStarted);
}
public static void playPlayerWhispered() {
if (audioManager.playerWhispered == null) {
audioManager.playerWhispered = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnPlayerWhispered.wav"),
AudioGroup.OtherSounds);
audioManager.playerWhispered = new MageClip(Constants.BASE_SOUND_PATH + "OnPlayerWhispered.wav", AudioGroup.OtherSounds);
}
checkAndPlayClip(getManager().playerWhispered);
}
public static void playPlayerSubmittedDeck() {
if (audioManager.playerSubmittedDeck == null) {
audioManager.playerSubmittedDeck = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnPlayerSubmittedDeck.wav"),
audioManager.playerSubmittedDeck = new MageClip(Constants.BASE_SOUND_PATH + "OnPlayerSubmittedDeck.wav",
AudioGroup.OtherSounds);
}
checkAndPlayClip(getManager().playerSubmittedDeck);
@ -271,15 +248,14 @@ public class AudioManager {
public static void playPlayerLeft() {
if (audioManager.playerLeft == null) {
audioManager.playerLeft = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnPlayerLeft.wav"),
AudioGroup.OtherSounds);
audioManager.playerLeft = new MageClip(Constants.BASE_SOUND_PATH + "OnPlayerLeft.wav", AudioGroup.OtherSounds);
}
checkAndPlayClip(getManager().playerLeft);
}
public static void playPlayerQuitTournament() {
if (audioManager.playerQuitTournament == null) {
audioManager.playerQuitTournament = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnPlayerQuitTournament.wav"),
audioManager.playerQuitTournament = new MageClip(Constants.BASE_SOUND_PATH + "OnPlayerQuitTournament.wav",
AudioGroup.OtherSounds);
}
checkAndPlayClip(getManager().playerQuitTournament);
@ -287,23 +263,21 @@ public class AudioManager {
public static void playPlayerLost() {
if (audioManager.playerLost == null) {
audioManager.playerLost = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnPlayerLost.wav"),
AudioGroup.GameSounds);
audioManager.playerLost = new MageClip(Constants.BASE_SOUND_PATH + "OnPlayerLost.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().playerLost);
}
public static void playPlayerWon() {
if (audioManager.playerWon == null) {
audioManager.playerWon = new MageClip(audioManager.loadClip(Constants.BASE_SOUND_PATH + "OnPlayerWon.wav"),
AudioGroup.GameSounds);
audioManager.playerWon = new MageClip(Constants.BASE_SOUND_PATH + "OnPlayerWon.wav", AudioGroup.GameSounds);
}
checkAndPlayClip(getManager().playerWon);
}
private static void checkAndPlayClip(MageClip mageClip) {
try {
if (mageClip != null && mageClip.getClip() != null) {
if (mageClip != null) {
boolean playSound = false;
switch (mageClip.getAudioGroup()) {
case GameSounds:
@ -320,7 +294,7 @@ public class AudioManager {
}
if (playSound) {
audioManager.play(mageClip.getClip());
audioManager.play(mageClip);
}
}
} catch (Exception e) {
@ -328,36 +302,8 @@ public class AudioManager {
}
}
public void play(final Clip clip) {
new Thread(new Runnable() {
@Override
public void run() {
clip.setFramePosition(0);
clip.start();
}
}).start();
public void play(final MageClip mageClip) {
linePool.playSound(mageClip);
}
private Clip loadClip(String filename) {
try {
File soundFile = new File(filename);
AudioInputStream soundIn = AudioSystem
.getAudioInputStream(soundFile);
AudioFormat format = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED, AudioSystem.NOT_SPECIFIED,
16, 2, 4, AudioSystem.NOT_SPECIFIED, true);
DataLine.Info info = new DataLine.Info(Clip.class, format);
Clip clip = (Clip) AudioSystem.getLine(info);
clip.open(soundIn);
return clip;
} catch (Exception e) {
log.error("Couldn't load sound: " + filename + ".", e);
}
return null;
}
}

View file

@ -0,0 +1,118 @@
package mage.client.util.audio;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.Mixer.Info;
import javax.sound.sampled.SourceDataLine;
import mage.utils.ThreadUtils;
public class LinePool {
private static final int LINE_CLEANUP_INTERVAL = 30000;
AudioFormat format;
Set<SourceDataLine> freeLines = new HashSet<>();
Set<SourceDataLine> activeLines = new HashSet<>();
Set<SourceDataLine> busyLines = new HashSet<>();
LinkedList<MageClip> queue = new LinkedList<>();
/*
* Initially all the lines are in the freeLines pool. When a sound plays, one line is being selected randomly from
* the activeLines and then, if it's empty, from freeLines pool and used to play the sound. The line is moved to
* busyLines. When a sound stops, the line is moved to activeLines if it contains <= elements than alwaysActive
* parameter, else it's moved to the freeLines pool.
*/
private Mixer mixer;
private int alwaysActive;
public LinePool() {
this(new AudioFormat(22050, 16, 1, true, false), 4, 1);
}
public LinePool(AudioFormat audioFormat, int size, int alwaysActive) {
format = audioFormat;
this.alwaysActive = alwaysActive;
Info[] mixerInfos = AudioSystem.getMixerInfo();
Mixer.Info mInfo = null;
if (mixerInfos.length > 0) {
mInfo = mixerInfos[0];
}
mixer = AudioSystem.getMixer(mInfo);
DataLine.Info lineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
for (int i = 0; i < size; i++) {
try {
final SourceDataLine line = (SourceDataLine) mixer.getLine(lineInfo);
freeLines.add(line);
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
new Timer("Line cleanup", true).scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
synchronized (LinePool.this) {
for (SourceDataLine sourceDataLine : freeLines) {
sourceDataLine.close();
}
}
}
}, LINE_CLEANUP_INTERVAL, LINE_CLEANUP_INTERVAL);
}
public void playSound(final MageClip mageClip) {
final SourceDataLine line;
synchronized (LinePool.this) {
if (activeLines.size() > 0) {
line = activeLines.iterator().next();
} else if (freeLines.size() > 0) {
line = freeLines.iterator().next();
} else {
// no lines available, queue sound to play it when a line is available
queue.add(mageClip);
return;
}
freeLines.remove(line);
activeLines.remove(line);
busyLines.add(line);
}
ThreadUtils.threadPool.submit(new Runnable() {
@Override
public void run() {
try {
if (!line.isOpen()) {
line.open();
}
line.start();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
byte[] buffer = mageClip.getBuffer();
line.write(buffer, 0, buffer.length);
synchronized (LinePool.this) {
boolean hasQueue = queue.size() > 0;
busyLines.remove(line);
if (activeLines.size() < LinePool.this.alwaysActive) {
activeLines.add(line);
} else {
freeLines.add(line);
}
if (hasQueue) {
playSound(queue.poll());
}
}
}
});
}
}

View file

@ -28,7 +28,12 @@
package mage.client.util.audio;
import javax.sound.sampled.Clip;
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
/**
*
@ -36,20 +41,38 @@ import javax.sound.sampled.Clip;
*/
public class MageClip {
private final Clip clip;
private final AudioGroup audioGroup;
private String filename;
private byte buf[];
public MageClip(Clip clip, AudioGroup audioGroup) {
this.clip = clip;
public MageClip(String filename, AudioGroup audioGroup) {
this.filename = filename;
this.audioGroup = audioGroup;
loadStream();
}
public Clip getClip() {
return clip;
private void loadStream() {
File file = new File(filename);
try {
AudioInputStream soundIn = AudioSystem.getAudioInputStream(file);
byte tmp[] = new byte[(int) file.length()];
int read = 0;
read = soundIn.read(tmp, 0, tmp.length);
buf = new byte[read];
System.arraycopy(tmp, 0, buf, 0, read); // truncate the buffer to the actual audio size
} catch (UnsupportedAudioFileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public AudioGroup getAudioGroup() {
return audioGroup;
}
public byte[] getBuffer(){
return buf;
}
}