mirror of
https://github.com/jneug/zeichenmaschine.git
synced 2026-04-14 14:43:33 +02:00
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:
190
src/schule/ngb/zm/Music.java
Normal file
190
src/schule/ngb/zm/Music.java
Normal 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());
|
||||||
|
|
||||||
|
}
|
||||||
204
src/schule/ngb/zm/Sound.java
Normal file
204
src/schule/ngb/zm/Sound.java
Normal 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());
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user