Klassen Sound und Music zur Audiowiedergabe

Sound spielt kurze Clips (z.B. Soundeffekte) ab.
Music für längere Hintergrundmusik.

MP3s werden nur über die Einbindung der externen Abhängigkeiten jlayer, tritonus-share und mp3spi ermöglicht.
This commit is contained in:
ngb
2022-07-06 20:46:49 +02:00
parent 950098110f
commit b5faf202b8
2 changed files with 394 additions and 0 deletions

View File

@@ -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());
}

View File

@@ -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());
}