mirror of
https://github.com/jneug/zeichenmaschine.git
synced 2026-10-12 01:45:18 +02:00
Javadoc Kommentare, die nur {@inheritDoc} enthalten, sind redundant und wurden entfernt.
246 lines
6.1 KiB
Java
246 lines
6.1 KiB
Java
package schule.ngb.zm.media;
|
|
|
|
import schule.ngb.zm.Constants;
|
|
import schule.ngb.zm.util.events.EventDispatcher;
|
|
import schule.ngb.zm.util.tasks.TaskRunner;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Ein Mixer ist eine Sammlung mehrerer {@link Audio Audio-Medien}, die
|
|
* gemeinsam kontrolliert werden können.
|
|
* <p>
|
|
* Im einfachsten Fall kann die Audio-Gruppe gemeinsam gestartet und gestoppt
|
|
* werden. Ein Mixer kann die Lautstärke der Medien in Relation zueinander
|
|
* setzen. Dazu wird jedem Medium ein Faktor mitgegeben. Ein Medium mit dem
|
|
* Faktor 0.5 ist dann halb so laut wie eines, mit dem Faktor 1.0.
|
|
* <p>
|
|
* Darüber hinaus kann ein Mixer Effekte wie einen
|
|
* {@link #fade(double, int) fadeIn} auf die Medien anwenden.
|
|
*/
|
|
@SuppressWarnings( "unused" )
|
|
public class Mixer implements Audio, AudioListener {
|
|
|
|
private List<AudioWrapper> audios;
|
|
|
|
private float volume = 0.8f;
|
|
|
|
EventDispatcher<Audio, AudioListener> eventDispatcher;
|
|
|
|
public Mixer() {
|
|
this.audios = new ArrayList<>(4);
|
|
}
|
|
|
|
public String getSource() {
|
|
return "";
|
|
}
|
|
|
|
private AudioWrapper findWrapper( Audio pAudio ) {
|
|
for( AudioWrapper aw: audios ) {
|
|
if( aw.audio == pAudio ) {
|
|
return aw;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public boolean contains( Audio pAudio ) {
|
|
return findWrapper(pAudio) != null;
|
|
}
|
|
|
|
public void add( Audio pAudio ) {
|
|
add(pAudio, 1);
|
|
}
|
|
|
|
/**
|
|
* Fügt ein Audio-Objekt dem Mixer mit dem angegebenen Lautstärke-Faktor
|
|
* hinzu.
|
|
* <p>
|
|
* 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 ) {
|
|
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<AudioWrapper> it = audios.listIterator();
|
|
while( it.hasNext() ) {
|
|
AudioWrapper aw = it.next();
|
|
if( aw.audio == pAudio ) {
|
|
it.remove();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void removeAll() {
|
|
audios.clear();
|
|
}
|
|
|
|
@Override
|
|
public boolean isPlaying() {
|
|
return audios.stream().anyMatch(aw -> aw.audio.isPlaying());
|
|
}
|
|
|
|
@Override
|
|
public boolean isLooping() {
|
|
return audios.stream().anyMatch(aw -> aw.audio.isLooping());
|
|
}
|
|
|
|
@Override
|
|
public void setVolume( double volume ) {
|
|
this.volume = volume < 0 ? 0f : (float) volume;
|
|
audios.stream().forEach(aw -> aw.audio.setVolume(aw.volumeFactor * volume));
|
|
}
|
|
|
|
@Override
|
|
public double getVolume() {
|
|
return volume;
|
|
}
|
|
|
|
@Override
|
|
public void play() {
|
|
audios.stream().forEach(aw -> aw.audio.play());
|
|
}
|
|
|
|
@Override
|
|
public void playAndWait() {
|
|
audios.stream().forEach(aw -> aw.audio.play());
|
|
while( audios.stream().anyMatch(aw -> aw.audio.isPlaying()) ) {
|
|
try {
|
|
Thread.sleep(10);
|
|
} catch( InterruptedException e ) {
|
|
// Just keep waiting
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void loop() {
|
|
audios.stream().forEach(aw -> aw.audio.loop());
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
audios.stream().forEach(aw -> aw.audio.stop());
|
|
}
|
|
|
|
@Override
|
|
public void dispose() {
|
|
if( isPlaying() ) {
|
|
stop();
|
|
}
|
|
audios.stream().forEach(aw -> aw.audio.dispose());
|
|
}
|
|
|
|
/**
|
|
* Ändert die Lautstärke aller hinzugefügten Audiomedien in der angegebenen
|
|
* Zeit schrittweise, bis die angegebene Lautstärke erreicht ist.
|
|
* <p>
|
|
* Zu beachten ist, dass die Lautstärke des Mixers angepasst wird. Das
|
|
* bedeutet, dass die Lautstärke der hinzugefügten Medien mit ihrem
|
|
* Lautstärkefaktor multipliziert werden. Die Medien haben am Ende also
|
|
* nicht unbedingt die Lautstärke {@code to}.
|
|
*
|
|
* @param to Der Zielwert für die Lautstärke.
|
|
* @param time Die Zeit, nach der die Änderung abgeschlossen sein soll.
|
|
*/
|
|
public void fade( final double to, final int time ) {
|
|
TaskRunner.run(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
final long start = System.currentTimeMillis();
|
|
double t = 0.0;
|
|
double from = volume;
|
|
do {
|
|
setVolume(Constants.interpolate(from, to, t));
|
|
t = (double) (System.currentTimeMillis() - start) / (double) time;
|
|
|
|
try {
|
|
Thread.sleep(1000 / Constants.framesPerSecond);
|
|
} catch( InterruptedException e ) {
|
|
// Keep waiting
|
|
}
|
|
} while( t < 1.0 );
|
|
setVolume(to);
|
|
}
|
|
});
|
|
}
|
|
|
|
@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, sobald sich auch ein Listener registrieren möchte.
|
|
*
|
|
* @return
|
|
*/
|
|
private EventDispatcher<Audio, AudioListener> 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;
|
|
}
|
|
|
|
}
|
|
|
|
}
|