package com.burnedkirby.TurnBasedMinecraft.client; import java.io.*; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.midi.MidiSystem; import javax.sound.midi.Sequencer; import javax.sound.sampled.*; import fr.delthas.javamp3.Sound; import org.apache.logging.log4j.Logger; import com.burnedkirby.TurnBasedMinecraft.common.TurnBasedMinecraftMod; import net.minecraft.client.Minecraft; public class BattleMusic { private Logger logger; private ArrayList battleMusic; private ArrayList sillyMusic; private boolean initialized; private File nextBattle; private File nextSilly; private Sequencer sequencer; private Clip clip; private boolean playingIsSilly; private boolean isPlaying; private Thread mp3StreamThread; private MP3Streamer mp3StreamRunnable; public BattleMusic(Logger logger) { initialized = false; this.logger = logger; battleMusic = new ArrayList(); sillyMusic = new ArrayList(); isPlaying = false; mp3StreamThread = null; mp3StreamRunnable = null; try { sequencer = MidiSystem.getSequencer(); sequencer.open(); } catch (Throwable t) { logger.error("Failed to load midi sequencer"); sequencer = null; } try { clip = AudioSystem.getClip(); } catch(Throwable t) { logger.error("Failed to load clip (for wav)"); clip = null; } File battleMusicFolder = new File(TurnBasedMinecraftMod.MUSIC_BATTLE); File sillyMusicFolder = new File(TurnBasedMinecraftMod.MUSIC_SILLY); if(!battleMusicFolder.exists()) { if(!battleMusicFolder.mkdirs()) { logger.error("Failed to create " + TurnBasedMinecraftMod.MUSIC_BATTLE); return; } } if(!sillyMusicFolder.exists()) { if(!sillyMusicFolder.mkdirs()) { logger.error("Failed to create " + TurnBasedMinecraftMod.MUSIC_SILLY); return; } } File[] battleFiles = battleMusicFolder.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { int extIndex = name.lastIndexOf("."); if(extIndex == -1) { return false; } String ext = name.substring(extIndex + 1).toLowerCase(); return ext.equals("mid") || ext.equals("wav") || ext.equals("mp3"); } }); for(File f : battleFiles) { battleMusic.add(f); } logger.info("Got " + battleMusic.size() + " battle music files"); File[] sillyFiles = sillyMusicFolder.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { int extIndex = name.lastIndexOf("."); if(extIndex == -1) { return false; } String ext = name.substring(extIndex + 1).toLowerCase(); return ext.equals("mid") || ext.equals("wav") || ext.equals("mp3"); } }); for(File f : sillyFiles) { sillyMusic.add(f); } logger.info("Got " + sillyMusic.size() + " silly music files"); initialized = true; pickNextBattle(); pickNextSilly(); } private void pickNextBattle() { if(!initialized || battleMusic.isEmpty()) { nextBattle = null; } else { nextBattle = battleMusic.get((int)(Math.random() * battleMusic.size())); } } private void pickNextSilly() { if(!initialized || sillyMusic.isEmpty()) { nextSilly = null; } else { nextSilly = sillyMusic.get((int)(Math.random() * sillyMusic.size())); } } public void playBattle(float volume) { if(!initialized || volume <= 0.0f || battleMusic.isEmpty()) { return; } else if(volume > 1.0f) { volume = 1.0f; } play(nextBattle, volume, true); pickNextBattle(); playingIsSilly = false; isPlaying = true; } public void playSilly(float volume) { if(!initialized || volume <= 0.0f || sillyMusic.isEmpty()) { return; } else if(volume > 1.0f) { volume = 1.0f; } play(nextSilly, volume, false); pickNextSilly(); playingIsSilly = true; isPlaying = true; } private void play(File next, float volume, boolean isBattleType) { if(initialized && next != null) { logger.debug("play called with file " + next.getName() + " and vol " + volume); Minecraft.getInstance().getSoundHandler().pause(); String suffix = next.getName().substring(next.getName().length() - 3).toLowerCase(); if(suffix.equals("mid") && sequencer != null) { if(sequencer.isRunning()) { sequencer.stop(); } if(clip != null && clip.isActive()) { clip.stop(); clip.close(); } if(mp3StreamThread != null && mp3StreamThread.isAlive()) { mp3StreamRunnable.setKeepPlaying(false); try { mp3StreamThread.join(); } catch (Throwable t) { /* ignored */ } } try { sequencer.setSequence(new BufferedInputStream(new FileInputStream(next))); } catch (Throwable t) { logger.error("Failed to play battle music (midi)"); return; } sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY); sequencer.start(); } else if(suffix.equals("wav") && clip != null) { if(sequencer != null && sequencer.isRunning()) { sequencer.stop(); } if(clip.isActive()) { clip.stop(); clip.close(); } if(mp3StreamThread != null && mp3StreamThread.isAlive()) { mp3StreamRunnable.setKeepPlaying(false); try { mp3StreamThread.join(); } catch (Throwable t) { /* ignored */ } } try { clip.open(AudioSystem.getAudioInputStream(next)); } catch(Throwable t) { logger.error("Failed to play battle music (wav)"); return; } // set volume FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); gainControl.setValue(volume * 20.0f - 20.0f); // in decibels clip.loop(Clip.LOOP_CONTINUOUSLY); clip.start(); } else if(suffix.equals("mp3")) { if(sequencer != null && sequencer.isRunning()) { sequencer.stop(); } if(clip != null && clip.isActive()) { clip.stop(); clip.close(); } if(mp3StreamThread != null && mp3StreamThread.isAlive()) { mp3StreamRunnable.setKeepPlaying(false); try { mp3StreamThread.join(); } catch (Throwable t) { /* ignored */ } } try { if(mp3StreamRunnable == null) { mp3StreamRunnable = new MP3Streamer(next, logger, volume); } else { mp3StreamRunnable.setMp3File(next); mp3StreamRunnable.setVolume(volume); } mp3StreamThread = new Thread(mp3StreamRunnable); mp3StreamThread.start(); logger.info("Started playing mp3 " + next.getName()); } catch (Throwable t) { logger.error("Failed to play battle music (mp3)"); return; } } } } public void stopMusic(boolean resumeMCSounds) { if(sequencer != null) { sequencer.stop(); } if(clip != null) { clip.stop(); clip.close(); } if(mp3StreamThread != null && mp3StreamThread.isAlive()) { mp3StreamRunnable.setKeepPlaying(false); try { mp3StreamThread.join(); } catch (Throwable t) { /* ignored */ } } if(resumeMCSounds) { Minecraft.getInstance().getSoundHandler().resume(); } isPlaying = false; } public boolean isPlayingSilly() { return playingIsSilly; } public boolean isPlaying() { return isPlaying || (sequencer != null && sequencer.isRunning()) || (clip != null && clip.isActive()); } public boolean hasBattleMusic() { return !battleMusic.isEmpty(); } public boolean hasSillyMusic() { return !sillyMusic.isEmpty(); } private class MP3Streamer implements Runnable { private AtomicBoolean keepPlaying; private File mp3File; private Logger logger; private float volume; public MP3Streamer(File mp3File, Logger logger, float volume) { keepPlaying = new AtomicBoolean(true); this.mp3File = mp3File; this.logger = logger; this.volume = volume; if(this.volume > 1.0f) { this.volume = 1.0f; } else if(this.volume < 0.0f) { this.volume = 0.0f; } } public void setKeepPlaying(boolean playing) { keepPlaying.set(playing); } public void setMp3File(File mp3File) { this.mp3File = mp3File; } public void setVolume(float volume) { this.volume = volume; } @Override public void run() { keepPlaying.set(true); SourceDataLine sdl = null; try { Sound mp3Sound = new Sound(new FileInputStream(mp3File)); AudioFormat audioFormat = mp3Sound.getAudioFormat(); sdl = AudioSystem.getSourceDataLine(audioFormat); sdl.open(audioFormat); { FloatControl volumeControl = (FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN); volumeControl.setValue(volume * 20.0f - 20.0f); // in decibels } ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] cached = null; int cachedOffset = 0; int cachedSize = 0; byte[] buf = new byte[4096]; sdl.start(); int read = mp3Sound.read(buf, 0, 4096); while(keepPlaying.get()) { if(baos != null) { if(read != -1) { sdl.write(buf, 0, read); baos.write(buf, 0, read); read = mp3Sound.read(buf, 0, 4096); } else { mp3Sound.close(); mp3Sound = null; cached = baos.toByteArray(); baos = null; } } else { cachedSize = cached.length - cachedOffset; if(cachedSize > 4096) { cachedSize = 4096; } sdl.write(cached, cachedOffset, cachedSize); cachedOffset += cachedSize; if(cachedOffset >= cached.length) { cachedOffset = 0; } } } } catch (Throwable t) { logger.error("Stream play mp3", t); } if(sdl != null) { sdl.stop(); sdl.flush(); sdl.close(); } } } }