diff --git a/src/schule/ngb/zm/Music.java b/src/schule/ngb/zm/Music.java new file mode 100644 index 0000000..e1955b2 --- /dev/null +++ b/src/schule/ngb/zm/Music.java @@ -0,0 +1,190 @@ +package schule.ngb.zm; + +import schule.ngb.zm.util.ResourceStreamProvider; + +import javax.sound.sampled.*; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Music { + + // size of the byte buffer used to read/write the audio stream + private static final int BUFFER_SIZE = 4096; + + + private boolean playing = false; + + private boolean looping = false; + + private String audioSource; + + private AudioInputStream audioStream; + + private SourceDataLine audioLine; + + private float volume = 1.0f; + + public Music( String source ) { + this.audioSource = source; + } + + public boolean isPlaying() { + return playing; + } + + public boolean isLooping() { + if( !playing ) { + looping = false; + } + return looping; + } + + public void setVolume( double volume ) { + this.volume = (float) volume; + if( audioLine != null ) { + applyVolume(); + } + } + + private void applyVolume() { + FloatControl gainControl = + (FloatControl) audioLine.getControl(FloatControl.Type.MASTER_GAIN); + + float vol = 20f * (float) Math.log10(volume); + // vol = (float) Constants.limit(vol, gainControl.getMinimum(), gainControl.getMaximum()); + gainControl.setValue(vol); + } + + public void playOnce() { + if( openLine() ) { + TaskRunner.run(new Runnable() { + @Override + public void run() { + stream(); + } + }); + } + } + + public void playOnceAndWait() { + if( openLine() ) { + stream(); + } + } + + public void loop() { + looping = true; + playOnce(); + } + + public void stop() { + playing = false; + looping = false; + dispose(); + } + + public void dispose() { + if( audioLine != null ) { + if( audioLine.isRunning() ) { + playing = false; + audioLine.stop(); + } + if( audioLine.isOpen() ) { + audioLine.drain(); + audioLine.close(); + + } + } + try { + if( audioStream != null ) { + audioStream.close(); + } + } catch( IOException ex ) {} + + audioLine = null; + audioStream = null; + } + + private void stream() { + audioLine.start(); + playing = true; + + byte[] bytesBuffer = new byte[BUFFER_SIZE]; + int bytesRead = -1; + + try { + while (playing && (bytesRead = audioStream.read(bytesBuffer)) != -1) { + audioLine.write(bytesBuffer, 0, bytesRead); + } + + audioLine.drain(); + audioLine.stop(); + } catch( IOException ex ) { + LOGGER.warning("Error while playing Music source <" + audioSource + ">"); + LOGGER.throwing("Music", "stream", ex); + } + + // Wait for the remaining audio to play + /*while( audioLine.isRunning() ) { + try { + Thread.sleep(10); + } catch( InterruptedException ex ) { + // Just keep waiting ... + } + }*/ + + playing = false; + streamingStopped(); + } + + private boolean openLine() { + if( audioLine != null ) { + return true; + } + + try { + InputStream in = ResourceStreamProvider.getResourceStream(audioSource); + if( in != null ) { + final AudioInputStream inStream = AudioSystem.getAudioInputStream(in); + AudioFormat format = inStream.getFormat(); + + final int ch = format.getChannels(); + final float rate = format.getSampleRate(); + AudioFormat outFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, rate, 16, ch, ch*2, rate, false); + + DataLine.Info info = new DataLine.Info(SourceDataLine.class, outFormat); + + audioLine = (SourceDataLine) AudioSystem.getLine(info); + audioLine.open(outFormat); + applyVolume(); + + audioStream = AudioSystem.getAudioInputStream(outFormat, inStream); + return true; + } else { + LOGGER.warning("Sound source <" + audioSource + "> could not be played: No audio source found."); + } + } catch( UnsupportedAudioFileException ex ) { + LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: The specified audio file is not supported.", ex); + } catch( LineUnavailableException ex ) { + LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: Audio line for playing back is unavailable.", ex); + } catch( IOException ex ) { + LOGGER.log(Level.WARNING, "Sound source <" + audioSource + "> could not be played: Error playing the audio file.", ex); + } + return false; + } + + private void streamingStopped() { + playing = false; + dispose(); + + if( looping ) { + playOnce(); + } + } + + //private static final Logger LOGGER = Logger.getLogger("schule.ngb.zm.Music"); + private static final Logger LOGGER = Logger.getLogger(Music.class.getName()); + +} diff --git a/src/schule/ngb/zm/Sound.java b/src/schule/ngb/zm/Sound.java new file mode 100644 index 0000000..8cefc3b --- /dev/null +++ b/src/schule/ngb/zm/Sound.java @@ -0,0 +1,204 @@ +package schule.ngb.zm; + +import schule.ngb.zm.util.ResourceStreamProvider; + +import javax.sound.sampled.*; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Logger; + +public class Sound { + + private boolean playing = false; + + private boolean looping = false; + + private String audioSource; + + private Clip audioClip; + + private boolean disposeAfterPlay = false; + + private float volume = 1.0f; + + public Sound( String source ) { + this.audioSource = source; + } + + public boolean isPlaying() { + // return audioClip != null && audioClip.isRunning(); + return playing; + } + + public boolean isLooping() { + if( !playing ) { + looping = false; + } + return looping; + } + + public void setVolume( double volume ) { + this.volume = (float) volume; + if( audioClip != null ) { + applyVolume(); + } + } + + private void applyVolume() { + FloatControl gainControl = + (FloatControl) audioClip.getControl(FloatControl.Type.MASTER_GAIN); + + float vol = 20f * (float) Math.log10(volume); + // vol = (float) Constants.limit(vol, gainControl.getMinimum(), gainControl.getMaximum()); + gainControl.setValue(vol); + } + + public void stop() { + looping = false; + if( audioClip.isRunning() ) { + audioClip.stop(); + } + playing = false; + } + + public void play() { + if( this.openClip() ) { + audioClip.start(); + playing = true; + } + } + + public void playOnce() { + disposeAfterPlay = true; + play(); + } + + public void playOnceAndWait() { + disposeAfterPlay = true; + playAndWait(); + } + + public void playAndWait() { + this.play(); + + long audioLen = audioClip.getMicrosecondLength(); + while( playing ) { + try { + long ms = (audioLen - audioClip.getMicrosecondPosition()) / 1000L; + Thread.sleep(ms); + } catch( InterruptedException ex ) { + // Ignore + } + } + + audioClip.close(); + } + + public void loop() { + loop(Clip.LOOP_CONTINUOUSLY); + } + + public void loop( int count ) { + int loopCount = count; + if( loopCount != Clip.LOOP_CONTINUOUSLY ) { + if( loopCount <= 0 ) { + return; + } + // Adjust Number of loops + loopCount -= 1; + } + if( openClip() ) { + looping = true; + audioClip.loop(loopCount); + playing = true; + } + } + + public void dispose() { + if( audioClip != null ) { + if( audioClip.isRunning() ) { + audioClip.stop(); + } + audioClip.close(); + audioClip = null; + } + } + + private boolean openClip() { + if( audioClip != null ) { + audioClip.setFramePosition(0); + return true; + } + + try { + InputStream in = ResourceStreamProvider.getResourceStream(audioSource); + if( in != null ) { + AudioInputStream audioStream = AudioSystem.getAudioInputStream(in); + AudioFormat format = audioStream.getFormat(); + DataLine.Info info = new DataLine.Info(Clip.class, format); + + audioClip = (Clip) AudioSystem.getLine(info); + audioClip.addLineListener(new LineListener() { + @Override + public void update( LineEvent event ) { + if( event.getType() == LineEvent.Type.STOP ) { + playbackStopped(); + } + } + }); + audioClip.open(audioStream); + applyVolume(); + return true; + } else { + LOGGER.warning("Sound source " + audioSource + " could not be played: No audio source found."); + } + } catch( UnsupportedAudioFileException ex ) { + LOGGER.warning("Sound source " + audioSource + " could not be played: The specified audio file is not supported."); + LOGGER.throwing("Sound", "openClip", ex); + } catch( LineUnavailableException ex ) { + LOGGER.warning("Sound source " + audioSource + " could not be played: Audio line for playing back is unavailable."); + LOGGER.throwing("Sound", "openClip", ex); + } catch( IOException ex ) { + LOGGER.warning("Sound source " + audioSource + " could not be played: Error playing the audio file."); + LOGGER.throwing("Sound", "openClip", ex); + } + return false; + } + + /*@Override + public void update( LineEvent event ) { + LineEvent.Type type = event.getType(); + + if( type == LineEvent.Type.START ) { + playing = true; + } else if( type == LineEvent.Type.STOP ) { + playing = false; + if( disposeAfterPlay ) { + this.dispose(); + disposeAfterPlay = false; + } + } + }*/ + + private void playbackStopped() { + playing = false; + if( disposeAfterPlay ) { + this.dispose(); + disposeAfterPlay = false; + } + } + + /* + public void addLineListener( LineListener listener ) { + if( audioClip == null ) { + openClip(); + } + if( audioClip != null ) { + audioClip.addLineListener(listener); + } + } + */ + + private static final Logger LOGGER = Logger.getLogger(Sound.class.getName()); + +}