diff --git a/src/main/java/schule/ngb/zm/media/Audio.java b/src/main/java/schule/ngb/zm/media/Audio.java index 6e8640b..2b8301a 100644 --- a/src/main/java/schule/ngb/zm/media/Audio.java +++ b/src/main/java/schule/ngb/zm/media/Audio.java @@ -80,4 +80,19 @@ public interface Audio { */ void dispose(); + /** + * Fügt dem Medium das angegbene Objekt als {@code AudioListener} hinzu, der + * bei Start und Stopp der Wiedergabe informiert werden soll. + * + * @param listener Das zu informierende Objekt. + */ + void addAudioListener( AudioListener listener ); + + /** + * Entfernt den angegebenen {@code AudioListener} vom Medium. + * + * @param listener Das Listener-Objekt. + */ + void removeAudioListener( AudioListener listener ); + } diff --git a/src/main/java/schule/ngb/zm/media/AudioListener.java b/src/main/java/schule/ngb/zm/media/AudioListener.java index 1ffe45d..fd0b02b 100644 --- a/src/main/java/schule/ngb/zm/media/AudioListener.java +++ b/src/main/java/schule/ngb/zm/media/AudioListener.java @@ -2,10 +2,33 @@ package schule.ngb.zm.media; import schule.ngb.zm.util.events.Listener; +/** + * Interface für Klassen, die auf das starten und stoppen der Wiedergabe von + * {@link Audio}-Objekten reagieren möchten. + *

+ * Implementierende Klassen können sich bei einem Auido-Objekt mittels + * {@link Audio#addAudioListener(AudioListener)} anmelden und werden über die + * jeweilige Methode informiert, sobald die Wiedergabe gestartet oder gestoppt + * wird. + */ public interface AudioListener extends Listener

+ * Der Lautstärke-Faktor setzt die Lautstärke des Audio-Objektes relativ zur + * Lautstärke des Mixers. Bei einem Faktor von 1.0 wird die Lautstärke des + * Mixers übernommen. Bei einem Wert von 0.5 wird das Objekt halb so laut + * abgespielt. Auf diese Weise lässt sich die Lautstärke aller Audio-Objekte + * des Mixers gleichzeitig anpassen, während ihre relative Lautstärke + * zueinander gleich bleibt. + * + * @param pAudio Ein Audio-Objekt. + * @param pVolumeFactor Der Lautstärke-Faktor. + */ public void add( Audio pAudio, double pVolumeFactor ) { - audios.add(new AudioWrapper(pAudio, (float) pVolumeFactor)); + if( !contains(pAudio) ) { + audios.add(new AudioWrapper(pAudio, (float) pVolumeFactor)); + } else { + findWrapper(pAudio).volumeFactor = (float) pVolumeFactor; + } pAudio.setVolume(pVolumeFactor * volume); } + /** + * Entfernt die das angegebene Audio-Objekt aus dem Mixer. Ist das Objekt + * nicht Teil des Mixers, passiert nichts. + * + * @param pAudio Ein Audio-Objekt. + */ public void remove( Audio pAudio ) { + Iterator it = audios.listIterator(); + while( it.hasNext() ) { + AudioWrapper aw = it.next(); + if( aw.audio == pAudio ) { + it.remove(); + break; + } + } } public void removeAll() { @@ -178,4 +215,57 @@ public class Mixer implements Audio { }); } + @Override + public void playbackStarted( Audio source ) { + if( eventDispatcher != null ) { + eventDispatcher.dispatchEvent("start", Mixer.this); + } + } + + @Override + public void playbackStopped( Audio source ) { + if( !isPlaying() ) { + if( eventDispatcher != null ) { + eventDispatcher.dispatchEvent("stop", Mixer.this); + } + } + } + + @Override + public void addAudioListener( AudioListener listener ) { + initializeEventDispatcher().addListener(listener); + } + + @Override + public void removeAudioListener( AudioListener listener ) { + initializeEventDispatcher().removeListener(listener); + } + + /** + * Interne Methode, um den Listener-Mechanismus zu initialisieren. Wird erst + * aufgerufen, soblad sich auch ein Listener registrieren möchte. + * @return + */ + private EventDispatcher initializeEventDispatcher() { + if( eventDispatcher == null ) { + eventDispatcher = new EventDispatcher<>(); + eventDispatcher.registerEventType("start", (a,l) -> l.playbackStarted(a)); + eventDispatcher.registerEventType("stop", (a,l) -> l.playbackStopped(a)); + } + return eventDispatcher; + } + + class AudioWrapper { + + Audio audio; + + float volumeFactor; + + public AudioWrapper( Audio audio, float volumeFactor ) { + this.audio = audio; + this.volumeFactor = volumeFactor; + } + + } + } diff --git a/src/main/java/schule/ngb/zm/media/Music.java b/src/main/java/schule/ngb/zm/media/Music.java index 8805514..90e74c5 100644 --- a/src/main/java/schule/ngb/zm/media/Music.java +++ b/src/main/java/schule/ngb/zm/media/Music.java @@ -18,6 +18,7 @@ import java.net.URL; * nicht komplett in den Speicher geladen, sondern direkt aus der Audioquelle * gestreamt und wiedergegeben. */ +@SuppressWarnings("unused") public class Music implements Audio { // size of the byte buffer used to read/write the audio stream @@ -276,11 +277,13 @@ public class Music implements Audio { } } - public void addListener( AudioListener listener ) { + @Override + public void addAudioListener( AudioListener listener ) { initializeEventDispatcher().addListener(listener); } - public void removeListener( AudioListener listener ) { + @Override + public void removeAudioListener( AudioListener listener ) { initializeEventDispatcher().removeListener(listener); } @@ -292,8 +295,8 @@ public class Music implements Audio { private EventDispatcher initializeEventDispatcher() { if( eventDispatcher == null ) { eventDispatcher = new EventDispatcher<>(); - eventDispatcher.registerEventType("start", (a,l) -> l.start(a)); - eventDispatcher.registerEventType("stop", (a,l) -> l.stop(a)); + eventDispatcher.registerEventType("start", (a,l) -> l.playbackStarted(a)); + eventDispatcher.registerEventType("stop", (a,l) -> l.playbackStopped(a)); } return eventDispatcher; } diff --git a/src/main/java/schule/ngb/zm/media/Sound.java b/src/main/java/schule/ngb/zm/media/Sound.java index ec366b4..224a118 100644 --- a/src/main/java/schule/ngb/zm/media/Sound.java +++ b/src/main/java/schule/ngb/zm/media/Sound.java @@ -2,6 +2,7 @@ package schule.ngb.zm.media; import schule.ngb.zm.util.Log; import schule.ngb.zm.util.Validator; +import schule.ngb.zm.util.events.EventDispatcher; import schule.ngb.zm.util.io.ResourceStreamProvider; import javax.sound.sampled.*; @@ -54,6 +55,8 @@ public class Sound implements Audio { */ private float volume = 0.8f; + EventDispatcher eventDispatcher; + /** * Erstellt einen Sound aus der angegebene Quelle. * @@ -235,7 +238,7 @@ public class Sound implements Audio { public synchronized void dispose() { if( audioClip != null ) { if( audioClip.isRunning() ) { - audioClip.stop(); + stop(); } audioClip.close(); audioClip = null; @@ -259,8 +262,16 @@ public class Sound implements Audio { audioClip.addLineListener(new LineListener() { @Override public void update( LineEvent event ) { - if( event.getType() == LineEvent.Type.STOP ) { + if( event.getType() == LineEvent.Type.START ) { + if( eventDispatcher != null ) { + eventDispatcher.dispatchEvent("start", Sound.this); + } + } else if( event.getType() == LineEvent.Type.STOP ) { playbackStopped(); + + if( eventDispatcher != null ) { + eventDispatcher.dispatchEvent("stop", Sound.this); + } } } }); @@ -302,12 +313,37 @@ public class Sound implements Audio { */ private void playbackStopped() { playing = false; + if( disposeAfterPlay ) { this.dispose(); disposeAfterPlay = false; } } + @Override + public void addAudioListener( AudioListener listener ) { + initializeEventDispatcher().addListener(listener); + } + + @Override + public void removeAudioListener( AudioListener listener ) { + initializeEventDispatcher().removeListener(listener); + } + + /** + * Interne Methode, um den Listener-Mechanismus zu initialisieren. Wird erst + * aufgerufen, soblad sich auch ein Listener registrieren möchte. + * @return + */ + private EventDispatcher initializeEventDispatcher() { + if( eventDispatcher == null ) { + eventDispatcher = new EventDispatcher<>(); + eventDispatcher.registerEventType("start", (a,l) -> l.playbackStarted(a)); + eventDispatcher.registerEventType("stop", (a,l) -> l.playbackStopped(a)); + } + return eventDispatcher; + } + private static final Log LOG = Log.getLogger(Sound.class); }