Dokumentation

This commit is contained in:
ngb
2022-12-10 11:04:48 +01:00
parent 3cf7871591
commit cb0ee9c842
4 changed files with 173 additions and 134 deletions

View File

@@ -1,10 +1,21 @@
package schule.ngb.zm.media; package schule.ngb.zm.media;
/** /**
* Interface für Audio-Medien. * Schnittstelle für Audio-Medien.
*
* <h4>MP3-Dateien verwenden</h4>
* Java kann nativ nur Waveform ({@code .wav}) Dateien wiedergeben. Um auch
* MP3-Dateien zu nutzen, müssen die Bibliotheken <a href="#">jlayer</a>, <a
* href="#">tritonus-share</a> und <a href="#">mp3spi</a> eingebunden werden.
* Details zur Verwendung können in der <a
* href="https://zeichenmaschine.xyz/installation/#unterstutzung-fur-mp3">Dokumentation
* der Zeichenmaschine</a> nachgelesen werden.
*/ */
public interface Audio { public interface Audio {
/**
* @return Die Quelle, aus der das Medium geladen wurde.
*/
String getSource(); String getSource();
/** /**
@@ -17,7 +28,7 @@ public interface Audio {
/** /**
* Prüft, ob das Medium gerade in einer Schleife abgespielt wird. Wenn * Prüft, ob das Medium gerade in einer Schleife abgespielt wird. Wenn
* {@code isLooping() == true}, dann muss auch immer * {@code isLooping() == true} gilt, dann muss auch immer
* {@code isPlaying() == true} gelten. * {@code isPlaying() == true} gelten.
* *
* @return {@code true}, wenn das Medium in einer Schleife abgespielt wird, * @return {@code true}, wenn das Medium in einer Schleife abgespielt wird,
@@ -30,7 +41,7 @@ public interface Audio {
* <p> * <p>
* Die Lautstärke wird auf einer linearen Skale festgelegt, wobei 0 kein Ton * Die Lautstärke wird auf einer linearen Skale festgelegt, wobei 0 kein Ton
* und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des * und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des
* Mediums. * Mediums. Negative Werte setzen die Lautstärke aud 0.
* *
* @param volume Die neue Lautstärke zwischen 0 und 1. * @param volume Die neue Lautstärke zwischen 0 und 1.
* @see <a * @see <a
@@ -39,7 +50,7 @@ public interface Audio {
void setVolume( double volume ); void setVolume( double volume );
/** /**
* Gibt die aktuelle Lautstärkeeinstellung dieses Mediums zurück. * Liefert die aktuelle Lautstärke dieses Mediums.
* <p> * <p>
* Die Lautstärke wird auf einer linearen Skale angegeben, wobei 0 kein Ton * Die Lautstärke wird auf einer linearen Skale angegeben, wobei 0 kein Ton
* und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des * und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des
@@ -50,8 +61,16 @@ public interface Audio {
double getVolume(); double getVolume();
/** /**
* Startet die Wiedergabe des Mediums und beendet die Methode. Das * Startet die Wiedergabe des Mediums. Das Audio-Medium wird einmal
* Audio-Medium wird einmal abgespielt und stoppt dann. * abgespielt und stoppt dann.
* <p>
* Die Methode beendet sofort und die Wiedergabe erfolgt im Hintergrund.
* Soll die Programmausführung erst nach Wiedergabe des Mediums fortgesetzt
* werden, sollte {@link #playAndWait()} verwendet werden.
* <p>
* Soll die Wiedergabe im Hintergrund ablaufen, aber dennoch auf das Ende
* reagiert werden, kann ein
* {@link #addAudioListener(AudioListener) AudioListener} verwendet werden.
*/ */
void play(); void play();
@@ -63,28 +82,28 @@ public interface Audio {
/** /**
* Spielt das Medium in einer kontinuierlichen Schleife ab. Die Methode * Spielt das Medium in einer kontinuierlichen Schleife ab. Die Methode
* startet die Wiedergabe und beendet dann direkt die Methode. Um die * startet die Wiedergabe im Hintergrund und beendet dann sofort. Um die
* Wiedergabe zu stoppen muss {@link #stop()} aufgerufen werden. * Wiedergabe zu stoppen, muss {@link #stop()} aufgerufen werden.
*/ */
void loop(); void loop();
/** /**
* Stoppt die Wiedergabe. Wird das Medium gerade nicht abgespielt * Stoppt die Wiedergabe. Wird das Medium gerade nicht abgespielt
* {@code isPlaying() == false}, dann passiert nichts. * ({@code isPlaying() == false}), dann passiert nichts.
*/ */
void stop(); void stop();
/** /**
* Stoppt die Wiedergabe und gibt alle Resourcen, die für das Medium * Stoppt die Wiedergabe und gibt alle Ressourcen, die für das Medium
* verwendet werden, frei. * verwendet werden, frei.
*/ */
void dispose(); void dispose();
/** /**
* Fügt dem Medium das angegbene Objekt als {@code AudioListener} hinzu, der * Fügt dem Medium das angegebene Objekt als {@code AudioListener} hinzu,
* bei Start und Stopp der Wiedergabe informiert werden soll. * der bei Start und Stopp der Wiedergabe informiert wird.
* *
* @param listener Das zu informierende Objekt. * @param listener Das Listener-Objekt.
*/ */
void addAudioListener( AudioListener listener ); void addAudioListener( AudioListener listener );

View File

@@ -14,20 +14,20 @@ import schule.ngb.zm.util.events.Listener;
public interface AudioListener extends Listener<Audio> { public interface AudioListener extends Listener<Audio> {
/** /**
* Wird aufgerufen, sobald die Wiedergabe eines Audio-Objektes startet, dem * Wird aufgerufen, sobald die Wiedergabe eines Audio-Mediums startet, dem
* dieses Objekt mittels {@link Audio#addAudioListener(AudioListener)} * dieses Objekt mittels {@link Audio#addAudioListener(AudioListener)}
* hinzugefügt wurde. * hinzugefügt wurde.
* *
* @param source Das Audio-Objekt, dessen Wiedergabe gestartet wurde. * @param source Das Audio-Medium, dessen Wiedergabe gestartet wurde.
*/ */
void playbackStarted( Audio source ); void playbackStarted( Audio source );
/** /**
* Wird aufgerufen, sobald die Wiedergabe eines Audio-Objektes stoppt, dem * Wird aufgerufen, sobald die Wiedergabe eines Audio-Mediums stoppt, dem
* dieses Objekt mittels {@link Audio#addAudioListener(AudioListener)} * dieses Objekt mittels {@link Audio#addAudioListener(AudioListener)}
* hinzugefügt wurde. * hinzugefügt wurde.
* *
* @param source Das Audio-Objekt, dessen Wiedergabe gestoppt wurde. * @param source Das Audio-Medium, dessen Wiedergabe gestoppt wurde.
*/ */
void playbackStopped( Audio source ); void playbackStopped( Audio source );

View File

@@ -1,84 +1,103 @@
package schule.ngb.zm.media; package schule.ngb.zm.media;
import schule.ngb.zm.util.events.EventDispatcher;
import schule.ngb.zm.util.tasks.TaskRunner;
import schule.ngb.zm.util.Log; import schule.ngb.zm.util.Log;
import schule.ngb.zm.util.io.ResourceStreamProvider;
import schule.ngb.zm.util.Validator; import schule.ngb.zm.util.Validator;
import schule.ngb.zm.util.events.EventDispatcher;
import schule.ngb.zm.util.io.ResourceStreamProvider;
import schule.ngb.zm.util.tasks.TaskRunner;
import javax.sound.sampled.*; import javax.sound.sampled.*;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
/** /**
* Ein Musikstück, dass im Projekt abgespielt werden soll. * Eine Musik, die abgespielt werden kann.
* <p> * <p>
* Im gegensatz zu einem {@link Sound} sind Musikstücke längere Audiodateien, * Im Gegensatz zu einem {@link Sound} wird {@code Music} für längere
* die zum Beispiel als Hintergrundmusik ablaufen sollen. Die Musik wird daher * Audiodateien benutzt, die zum Beispiel als Hintergrundmusik gespielt werden.
* nicht komplett in den Speicher geladen, sondern direkt aus der Audioquelle * Die Audiodaten werden daher nicht vollständig in den Speicher geladen,
* gestreamt und wiedergegeben. * sondern direkt aus der Quelle gestreamt und direkt wiedergegeben.
* <p>
* Daher ist es nicht möglich, die länge der Musik im Vorfeld abzufragen oder zu
* einer bestimmten Stelle im Stream zu springen.
*
* <h4>MP3-Dateien verwenden</h4>
* Java kann nativ nur Waveform ({@code .wav}) Dateien wiedergeben. Um auch
* MP3-Dateien zu nutzen, müssen die Bibliotheken <a href="#">jlayer</a>, <a
* href="#">tritonus-share</a> und <a href="#">mp3spi</a> eingebunden werden.
* Details zur Verwendung können in der <a
* href="https://zeichenmaschine.xyz/installation/#unterstutzung-fur-mp3">Dokumentation
* der Zeichenmaschine</a> nachgelesen werden.
*/ */
@SuppressWarnings("unused") // TODO: Wann sollten Listener beim Loopen informiert werden? Nach jedem Loop oder erst ganz am Ende?
@SuppressWarnings( "unused" )
public class Music implements Audio { public class Music implements Audio {
// size of the byte buffer used to read/write the audio stream /**
* Größe des verwendeten Input-Puffers für die Audiodaten.
*/
private static final int BUFFER_SIZE = 4096; private static final int BUFFER_SIZE = 4096;
/** /**
* Ob der Sound gerade abgespielt wird. * Ob der Sound aktuell abgespielt wird.
*/ */
private boolean playing = false; private boolean playing = false;
/** /**
* Ob der Sound gerade in einer Schleife abgespielt wird. * Ob der Sound aktuell in einer Schleife abgespielt wird.
*/ */
private boolean looping = false; private boolean looping = false;
/** /**
* Die Quelle des Musikstücks. * Die Quelle der Audiodaten.
*/ */
private String audioSource; private String audioSource;
/** /**
* Der AudioStream, um die AUdiosdaten zulsen, falls dieser schon geöffnet * Der {@link AudioInputStream}, um die Audiosdaten zu lesen. {@code null},
* wurde. Sonst {@code null}. * falls noch kein Stream geöffnet wurde.
*/ */
private AudioInputStream audioStream; private AudioInputStream audioStream;
/** /**
* Die Line für die Ausgabe, falls diese schon geöffnet wurde. Sonst * Die {@link SourceDataLine} für die Ausgabe. {@code null}, falls die
* {@code null}. * Audiodatei noch nicht geöffnet wurde.
*/ */
private SourceDataLine audioLine; private SourceDataLine audioLine;
/** /**
* Die Lautstärke der Musik. * Die aktuelle Lautstärke des Mediums.
*/ */
private float volume = 0.8f; private float volume = 0.8f;
/**
* Dispatcher für Audio-Events (start und stop).
*/
EventDispatcher<Audio, AudioListener> eventDispatcher; EventDispatcher<Audio, AudioListener> eventDispatcher;
public Music( String source ) { /**
Validator.requireNotNull(source); * Erstellt eine Musik aus der angegebenen Audioquelle.
this.audioSource = source; *
* @param audioSource Quelle der Audiodaten.
* @throws NullPointerException Falls die Quelle {@code null} ist.
* @see ResourceStreamProvider#getResourceURL(String)
*/
public Music( String audioSource ) {
Validator.requireNotNull(audioSource);
this.audioSource = audioSource;
} }
@Override
public String getSource() { public String getSource() {
return audioSource; return audioSource;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public boolean isPlaying() { public boolean isPlaying() {
return playing; return playing;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public boolean isLooping() { public boolean isLooping() {
if( !playing ) { if( !playing ) {
@@ -87,28 +106,21 @@ public class Music implements Audio {
return looping; return looping;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void setVolume( double volume ) { public void setVolume( double volume ) {
this.volume = (float) volume; this.volume = volume < 0 ? 0f : (float) volume;
if( audioLine != null ) { if( audioLine != null ) {
applyVolume(); applyVolume();
} }
} }
/**
* {@inheritDoc}
*/
@Override @Override
public double getVolume() { public double getVolume() {
return volume; return volume;
} }
/** /**
* Interne Methode, um die gesetzte Lautstärke vor dem Abspielen * Wendet die Lautstärke vor dem Abspielen auf den Audiostream an.
* anzuwenden.
*/ */
private void applyVolume() { private void applyVolume() {
FloatControl gainControl = FloatControl gainControl =
@@ -119,24 +131,13 @@ public class Music implements Audio {
gainControl.setValue(vol); gainControl.setValue(vol);
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void play() { public void play() {
if( openLine() ) { if( openLine() ) {
TaskRunner.run(new Runnable() { TaskRunner.run(this::stream);
@Override
public void run() {
stream();
}
});
} }
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void playAndWait() { public void playAndWait() {
if( openLine() ) { if( openLine() ) {
@@ -144,18 +145,12 @@ public class Music implements Audio {
} }
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void loop() { public void loop() {
looping = true; looping = true;
play(); play();
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void stop() { public void stop() {
playing = false; playing = false;
@@ -163,9 +158,6 @@ public class Music implements Audio {
dispose(); dispose();
} }
/**
* {@inheritDoc}
*/
@Override @Override
public synchronized void dispose() { public synchronized void dispose() {
if( audioLine != null ) { if( audioLine != null ) {
@@ -189,7 +181,17 @@ public class Music implements Audio {
audioStream = null; audioStream = null;
} }
/**
* Startet den Stream der Audiodaten und damit die Wiedergabe.
* <p>
* Die {@link #audioLine} muss vorher mit {@link #openLine()} geöffnet
* werden, ansonsten passiert nichts.
*/
private synchronized void stream() { private synchronized void stream() {
if( audioLine == null ) {
return;
}
audioLine.start(); audioLine.start();
playing = true; playing = true;
if( eventDispatcher != null ) { if( eventDispatcher != null ) {
@@ -226,6 +228,14 @@ public class Music implements Audio {
} }
} }
/**
* Öffnet eine {@link SourceDataLine} für die
* {@link #audioSource Audioquelle} und bereitet die Wiedergabe vor. Es wird
* noch nichts abgespielt.
*
* @return {@code true}, wenn die Line geöffnet werden konnte, {@code false}
* sonst.
*/
private boolean openLine() { private boolean openLine() {
if( audioLine != null ) { if( audioLine != null ) {
return true; return true;
@@ -262,6 +272,15 @@ public class Music implements Audio {
return false; return false;
} }
/**
* Wird aufgerufen, wenn die Wiedergabe beendet wurde. Entweder durch einen
* Aufruf von {@link #stop()} oder weil keine Audiodaten mehr vorhanden
* sind.
* <p>
* Nach dem Ende des Streams wird {@link #dispose()} aufgerufen und, falls
* das Musikstück in einer Schleife abgespielt wird, der Stream direkt
* wieder gestartet.
*/
private void streamingStopped() { private void streamingStopped() {
dispose(); dispose();
@@ -289,14 +308,15 @@ public class Music implements Audio {
/** /**
* Interne Methode, um den Listener-Mechanismus zu initialisieren. Wird erst * Interne Methode, um den Listener-Mechanismus zu initialisieren. Wird erst
* aufgerufen, soblad sich auch ein Listener registrieren möchte. * aufgerufen, sobald sich der erste Listener anmelden möchte.
* @return *
* @return Der {@code EventDispatcher} für dieses Objekt.
*/ */
private EventDispatcher<Audio, AudioListener> initializeEventDispatcher() { private EventDispatcher<Audio, AudioListener> initializeEventDispatcher() {
if( eventDispatcher == null ) { if( eventDispatcher == null ) {
eventDispatcher = new EventDispatcher<>(); eventDispatcher = new EventDispatcher<>();
eventDispatcher.registerEventType("start", (a,l) -> l.playbackStarted(a)); eventDispatcher.registerEventType("start", ( a, l ) -> l.playbackStarted(a));
eventDispatcher.registerEventType("stop", (a,l) -> l.playbackStopped(a)); eventDispatcher.registerEventType("stop", ( a, l ) -> l.playbackStopped(a));
} }
return eventDispatcher; return eventDispatcher;
} }

View File

@@ -10,80 +10,87 @@ import java.io.IOException;
import java.net.URL; import java.net.URL;
/** /**
* Wiedergabe kurzer Soundclips, die mehrmals wiederverwendet werden. * Ein kurzer Soundclip, der mehrmals wiederverwendet werden kann.
* <p> * <p>
* In Spielen und anderen Projekten gibt es oftmals eine Reihe kurzer Sounds, * In Spielen und anderen Projekten gibt es oftmals eine Reihe kurzer
* die zusammen mit bestimmten Aktionen wiedergegeben werden (zum Beispiel, wenn * Soundclips, die zusammen mit bestimmten Aktionen wiedergegeben werden (zum
* die Spielfigur springt, wenn zwei Objekte kollidieren, usw.). Sounds werden * Beispiel, wenn die Spielfigur springt, wenn zwei Objekte kollidieren, usw.).
* komplett in den Speicher geladen und können dadurch immer wieder, als * Sounds werden vollständig in den Speicher geladen und können immer wieder,
* Schleife oder auch nur Abschnittsweise abgespielt werden. * als Schleife oder auch nur Abschnittsweise, abgespielt werden.
* <p> * <p>
* Für längre Musikstücke (beispielsweise Hintergrundmusik) bietet sich eher die * Für längere Musikstücke (beispielsweise Hintergrundmusik) bietet sich eher
* KLasse {@link Music} an. * die Klasse {@link Music} an.
*
* <h4>MP3-Dateien verwenden</h4>
* Java kann nativ nur Waveform ({@code .wav}) Dateien wiedergeben. Um auch
* MP3-Dateien zu nutzen, müssen die Bibliotheken <a href="#">jlayer</a>, <a
* href="#">tritonus-share</a> und <a href="#">mp3spi</a> eingebunden werden.
* Details zur Verwendung können in der <a
* href="https://zeichenmaschine.xyz/installation/#unterstutzung-fur-mp3">Dokumentation
* der Zeichenmaschine</a> nachgelesen werden.
*/ */
@SuppressWarnings( "unused" ) @SuppressWarnings( "unused" )
public class Sound implements Audio { public class Sound implements Audio {
/** /**
* Ob der Sound gerade abgespielt wird. * Ob der Sound aktuell abgespielt wird.
*/ */
private boolean playing = false; private boolean playing = false;
/** /**
* Ob der Sound gerade in einer Schleife abgespielt wird. * Ob der Sound aktuell in einer Schleife abgespielt wird.
*/ */
private boolean looping = false; private boolean looping = false;
/** /**
* Die Quelle des Musikstücks. * Die Quelle der Audiodaten.
*/ */
private String audioSource; private String audioSource;
/** /**
* Der Clip, falls er schon geladen wurde, sonst {@code null}. * Der Clip, falls er schon geladen wurde. Ansonsten {@code null}.
*/ */
private Clip audioClip; private Clip audioClip;
/** /**
* Ob die Resourcen des Clips im Speicher nach dem nächsten Abspielen * Ob die Ressourcen des Clips im Speicher nach dem nächsten Abspielen
* freigegeben werden sollen. * freigegeben werden sollen.
*/ */
private boolean disposeAfterPlay = false; private boolean disposeAfterPlay = false;
/** /**
* Die Lautstärke des Clips. * Die aktuelle Lautstärke des Clips.
*/ */
private float volume = 0.8f; private float volume = 0.8f;
/**
* Dispatcher für Audio-Events (start und stop).
*/
EventDispatcher<Audio, AudioListener> eventDispatcher; EventDispatcher<Audio, AudioListener> eventDispatcher;
/** /**
* Erstellt einen Sound aus der angegebene Quelle. * Erstellt einen Sound aus der angegebene Quelle.
* *
* @param source Ein Dateipfad oder eine Webadresse. * @param source Quelle der Audiodaten.
* @throws NullPointerException Falls die Quelle {@code null} ist. * @throws NullPointerException Falls die Quelle {@code null} ist.
* @see ResourceStreamProvider#getResourceURL(String)
*/ */
public Sound( String source ) { public Sound( String source ) {
Validator.requireNotNull(source); Validator.requireNotNull(source);
this.audioSource = source; this.audioSource = source;
} }
@Override
public String getSource() { public String getSource() {
return audioSource; return audioSource;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public boolean isPlaying() { public boolean isPlaying() {
// return audioClip != null && audioClip.isRunning(); // return audioClip != null && audioClip.isRunning();
return playing; return playing;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public boolean isLooping() { public boolean isLooping() {
if( !playing ) { if( !playing ) {
@@ -92,28 +99,21 @@ public class Sound implements Audio {
return looping; return looping;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void setVolume( double volume ) { public void setVolume( double volume ) {
this.volume = (float) volume; this.volume = volume < 0 ? 0f : (float) volume;
if( audioClip != null ) { if( audioClip != null ) {
applyVolume(); applyVolume();
} }
} }
/**
* {@inheritDoc}
*/
@Override @Override
public double getVolume() { public double getVolume() {
return volume; return volume;
} }
/** /**
* Interne Methode, um die gesetzte Lautstärke vor dem Abspielen * Wendet die Lautstärke vor dem Abspielen auf den Clip an.
* anzuwenden.
*/ */
private void applyVolume() { private void applyVolume() {
FloatControl gainControl = FloatControl gainControl =
@@ -124,9 +124,6 @@ public class Sound implements Audio {
gainControl.setValue(vol); gainControl.setValue(vol);
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void stop() { public void stop() {
looping = false; looping = false;
@@ -136,9 +133,6 @@ public class Sound implements Audio {
playing = false; playing = false;
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void play() { public void play() {
if( this.openClip() ) { if( this.openClip() ) {
@@ -147,9 +141,6 @@ public class Sound implements Audio {
} }
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void playAndWait() { public void playAndWait() {
this.play(); this.play();
@@ -168,7 +159,7 @@ public class Sound implements Audio {
} }
/** /**
* Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips * Spielt den Sound einmal ab und gibt danach alle Ressourcen des Clips
* frei. * frei.
* <p> * <p>
* Der Aufruf ist effektiv gleich zu * Der Aufruf ist effektiv gleich zu
@@ -185,7 +176,7 @@ public class Sound implements Audio {
} }
/** /**
* Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips * Spielt den Sound einmal ab und gibt danach alle Ressourcen des Clips
* frei. * frei.
* <p> * <p>
* Der Aufruf entspricht * Der Aufruf entspricht
@@ -199,17 +190,18 @@ public class Sound implements Audio {
playAndWait(); playAndWait();
} }
/**
* {@inheritDoc}
*/
@Override @Override
public void loop() { public void loop() {
loop(Clip.LOOP_CONTINUOUSLY); loop(Clip.LOOP_CONTINUOUSLY);
} }
/** /**
* Wiederholt den Sound die angegebene Anzahl an Wiederholungen ab und * Wiederholt den Sound die angegebene Anzahl an Wiederholungen und stoppt
* stoppt die Wiedergabe dann. * die Wiedergabe dann.
* <p>
* Wird {@code count} auf {@link Clip#LOOP_CONTINUOUSLY} gesetzt (-1), wird
* der Clip unendlich oft wiederholt. Der Aufruf entspricht dann
* {@link #loop()}.
* *
* @param count Anzahl der Wiederholungen. * @param count Anzahl der Wiederholungen.
*/ */
@@ -231,9 +223,6 @@ public class Sound implements Audio {
} }
} }
/**
* {@inheritDoc}
*/
@Override @Override
public synchronized void dispose() { public synchronized void dispose() {
if( audioClip != null ) { if( audioClip != null ) {
@@ -245,6 +234,13 @@ public class Sound implements Audio {
} }
} }
/**
* Lädt falls nötig den {@link Clip} für die
* {@link #audioSource Audioquelle} und startet die Wiedergabe.
*
* @return {@code true}, wenn der Clip geöffnet werden konnte, {@code false}
* sonst.
*/
private synchronized boolean openClip() { private synchronized boolean openClip() {
if( audioClip != null ) { if( audioClip != null ) {
audioClip.setFramePosition(0); audioClip.setFramePosition(0);
@@ -307,9 +303,12 @@ public class Sound implements Audio {
}*/ }*/
/** /**
* Interne Methode, die aufgerufen wird, wenn die Wiedergabe gestoppt wird. * Wird aufgerufen, wenn die Wiedergabe beendet wurde. Entweder durch einen
* Entweder durch einen Aufruf von {@link #stop()} oder, weil die Wiedergabe * Aufruf von {@link #stop()} oder, weil die Wiedergabe nach
* nach {@link #playOnce()} beendet wurde. * {@link #playOnce()} beendet wurde.
* <p>
* Falls {@link #disposeAfterPlay} gesetzt ist, wird nach dem Ende der
* Wiedergabe {@link #dispose()} aufgerufen.
*/ */
private void playbackStopped() { private void playbackStopped() {
playing = false; playing = false;
@@ -332,14 +331,15 @@ public class Sound implements Audio {
/** /**
* Interne Methode, um den Listener-Mechanismus zu initialisieren. Wird erst * Interne Methode, um den Listener-Mechanismus zu initialisieren. Wird erst
* aufgerufen, soblad sich auch ein Listener registrieren möchte. * aufgerufen, sobald sich der erste Listener anmelden möchte.
* @return *
* @return Der {@code EventDispatcher} für dieses Objekt.
*/ */
private EventDispatcher<Audio, AudioListener> initializeEventDispatcher() { private EventDispatcher<Audio, AudioListener> initializeEventDispatcher() {
if( eventDispatcher == null ) { if( eventDispatcher == null ) {
eventDispatcher = new EventDispatcher<>(); eventDispatcher = new EventDispatcher<>();
eventDispatcher.registerEventType("start", (a,l) -> l.playbackStarted(a)); eventDispatcher.registerEventType("start", ( a, l ) -> l.playbackStarted(a));
eventDispatcher.registerEventType("stop", (a,l) -> l.playbackStopped(a)); eventDispatcher.registerEventType("stop", ( a, l ) -> l.playbackStopped(a));
} }
return eventDispatcher; return eventDispatcher;
} }