diff --git a/README.md b/README.md index 0361780..effefc5 100644 --- a/README.md +++ b/README.md @@ -58,3 +58,6 @@ licenced under the [Apache License 2.0](https://github.com/johnrengelman/shadow/ This mod also uses [Cava-Toml](https://github.com/ConsenSys/cava/tree/master/toml) which is licenced under the [Apache License 2.0](https://github.com/ConsenSys/cava/blob/master/LICENSE). + +This mod also uses [JavaMP3](https://github.com/kevinstadler/JavaMP3) +which is licensed under the [MIT License](https://github.com/kevinstadler/JavaMP3/blob/master/LICENSE). diff --git a/build.gradle b/build.gradle index a8be03d..df6ff43 100644 --- a/build.gradle +++ b/build.gradle @@ -38,6 +38,8 @@ dependencies { // or you may define them like so.. //compile "some.group:artifact:version:classifier" //compile "some.group:artifact:version" + + // toml parser compile "net.consensys.cava:cava-toml:0.3.1" // real examples @@ -83,6 +85,8 @@ shadowJar { relocate 'net.consensys.cava', 'shadow.turnbasedmc.net.consensys.cava' relocate 'org.antlr.v4', 'shadow.turnbasedmc.org.antlr.v4' relocate 'javax.annotation', 'shadow.turnbasedmc.javax.annotation' + + relocate 'fr.delthas', 'shadow.turnbasedmc.fr.delthas' } reobf { shadowJar { mappingType = "SEARGE" } } diff --git a/libs/javamp3-1.0.3.jar b/libs/javamp3-1.0.3.jar new file mode 100644 index 0000000..1ce61ed Binary files /dev/null and b/libs/javamp3-1.0.3.jar differ diff --git a/src/main/java/com/seodisparate/TurnBasedMinecraft/client/BattleMusic.java b/src/main/java/com/seodisparate/TurnBasedMinecraft/client/BattleMusic.java index dd784f5..a021495 100644 --- a/src/main/java/com/seodisparate/TurnBasedMinecraft/client/BattleMusic.java +++ b/src/main/java/com/seodisparate/TurnBasedMinecraft/client/BattleMusic.java @@ -1,18 +1,17 @@ package com.seodisparate.TurnBasedMinecraft.client; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FilenameFilter; +import java.io.*; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.midi.MidiSystem; import javax.sound.midi.Sequencer; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.Clip; -import javax.sound.sampled.FloatControl; -import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.*; +import fr.delthas.javamp3.Sound; import org.apache.logging.log4j.Logger; import com.seodisparate.TurnBasedMinecraft.common.TurnBasedMinecraftMod; @@ -31,7 +30,9 @@ public class BattleMusic private Clip clip; private boolean playingIsSilly; private boolean isPlaying; - + private Thread mp3StreamThread; + private MP3Streamer mp3StreamRunnable; + public BattleMusic(Logger logger) { initialized = false; @@ -39,7 +40,9 @@ public class BattleMusic battleMusic = new ArrayList(); sillyMusic = new ArrayList(); isPlaying = false; - + mp3StreamThread = null; + mp3StreamRunnable = null; + try { sequencer = MidiSystem.getSequencer(); sequencer.open(); @@ -88,14 +91,15 @@ public class BattleMusic return false; } String ext = name.substring(extIndex + 1).toLowerCase(); - return ext.equals("mid") || ext.equals("wav"); + 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) @@ -106,14 +110,15 @@ public class BattleMusic return false; } String ext = name.substring(extIndex + 1).toLowerCase(); - return ext.equals("mid") || ext.equals("wav"); + return ext.equals("mid") || ext.equals("wav") || ext.equals("mp3"); } }); for(File f : sillyFiles) { sillyMusic.add(f); } - + logger.info("Got " + sillyMusic.size() + " battle music files"); + initialized = true; pickNextBattle(); @@ -154,7 +159,7 @@ public class BattleMusic { volume = 1.0f; } - play(nextBattle, volume); + play(nextBattle, volume, true); pickNextBattle(); playingIsSilly = false; isPlaying = true; @@ -170,13 +175,13 @@ public class BattleMusic { volume = 1.0f; } - play(nextSilly, volume); + play(nextSilly, volume, false); pickNextSilly(); playingIsSilly = true; isPlaying = true; } - private void play(File next, float volume) + private void play(File next, float volume, boolean isBattleType) { if(initialized && next != null) { @@ -187,9 +192,21 @@ public class BattleMusic String suffix = next.getName().substring(next.getName().length() - 3).toLowerCase(); if(suffix.equals("mid")) { - sequencer.stop(); - clip.stop(); - clip.close(); + if(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 { sequencer.setSequence(new BufferedInputStream(new FileInputStream(next))); } catch (Throwable t) @@ -197,7 +214,7 @@ public class BattleMusic logger.error("Failed to play battle music (midi)"); return; } - + sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY); sequencer.start(); } @@ -212,22 +229,67 @@ public class BattleMusic 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 load battle music (wav)"); + 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.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 + { + 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; + } + } } } @@ -236,11 +298,14 @@ public class BattleMusic sequencer.stop(); clip.stop(); clip.close(); + if(mp3StreamThread != null && mp3StreamThread.isAlive()) + { + mp3StreamRunnable.setKeepPlaying(false); + try { mp3StreamThread.join(); } catch (Throwable t) { /* ignored */ } + } if(resumeMCSounds) { - Minecraft.getMinecraft().addScheduledTask(() -> { - Minecraft.getMinecraft().getSoundHandler().resumeSounds(); - }); + Minecraft.getMinecraft().addScheduledTask(() -> Minecraft.getMinecraft().getSoundHandler().resumeSounds() ); } isPlaying = false; } @@ -264,4 +329,112 @@ public class BattleMusic { 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(); + } + } + } }