Audio Interface ud Mixer Klasse

This commit is contained in:
ngb
2022-07-07 08:08:28 +02:00
parent 5213dbb7e9
commit 4e147586e4
4 changed files with 211 additions and 4 deletions

View File

@@ -0,0 +1,70 @@
package schule.ngb.zm.media;
/**
* Interface für Audio-Medien.
*/
public interface Audio {
/**
* Prüft, ob das Medium gerade abgespielt wird.
*
* @return {@code true}, wenn das Medium abgespielt wird, {@code false}
* sonst.
*/
boolean isPlaying();
/**
* Prüft, ob das Medium gerade in einer Schleife abgespielt wird. Wenn
* {@code isLooping() == true}, dann muss auch immer
* {@code isPlaying() == true} gelten.
*
* @return @return {@code true}, wenn das Medium in einer Schleife
* abgespielt wird, {@code false} sonst.
*/
boolean isLooping();
/**
* Legt die Lautstärke des Mediums beim Abspielen fest.
* <p>
* 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
* Mediums.
*
* @param volume Die neue Lautstärke zwischen 0 und 1.
* @see <a
* href="https://stackoverflow.com/a/40698149">https://stackoverflow.com/a/40698149</a>
*/
void setVolume( double volume );
/**
* Startet die Wiedergabe des Mediums und beendet die Methode. Das
* Audio-Medium wird einmal abgespielt und stoppt dann.
*/
void playOnce();
/**
* Startet die Wiedergabe des Mediums und blockiert das Programm, bis die
* Wiedergabe beendet ist.
*/
void playOnceAndWait();
/**
* Spielt das Medium in einer kontinuierlichen Schleife ab. Die Methode
* startet die Wiedergabe und beendet dann direkt die Methode. Um die
* Wiedergabe zu stoppen muss {@link #stop()} aufgerufen werden.
*/
void loop();
/**
* Stoppt die Wiedergabe. Wird das Medium gerade nicht abgespielt
* {@code isPlaying() == false}, dann passiert nichts.
*/
void stop();
/**
* Stoppt die Wiedergabe und gibt alle Resourcen, die für das Medium
* verwendet werden, frei.
*/
void dispose();
}

View File

@@ -0,0 +1,129 @@
package schule.ngb.zm.media;
import schule.ngb.zm.Constants;
import schule.ngb.zm.tasks.TaskRunner;
import java.util.ArrayList;
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.
*/
public class Mixer implements Audio {
private List<AudioWrapper> audios;
private float volume = 0.8f;
class AudioWrapper {
Audio audio;
float volumeFactor;
public AudioWrapper( Audio audio, float volumeFactor ) {
this.audio = audio;
this.volumeFactor = volumeFactor;
}
}
public Mixer() {
this.audios = new ArrayList<>(4);
}
public void add( Audio pAudio ) {
add(pAudio, 1f);
}
public void add( Audio pAudio, double pVolumeFactor ) {
audios.add(new AudioWrapper(pAudio, (float) pVolumeFactor));
pAudio.setVolume(pVolumeFactor * volume);
}
@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 pVolume ) {
volume = (float) pVolume;
audios.stream().forEach(aw -> aw.audio.setVolume(aw.volumeFactor * pVolume));
}
@Override
public void playOnce() {
audios.stream().forEach(aw -> aw.audio.playOnce());
}
@Override
public void playOnceAndWait() {
audios.stream().forEach(aw -> aw.audio.playOnce());
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());
}
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;
if( !isPlaying() ) {
playOnce();
}
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);
}
});
}
}

View File

@@ -9,7 +9,7 @@ import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Music {
public class Music implements Audio {
// size of the byte buffer used to read/write the audio stream
private static final int BUFFER_SIZE = 4096;
@@ -25,16 +25,18 @@ public class Music {
private SourceDataLine audioLine;
private float volume = 1.0f;
private float volume = 0.8f;
public Music( String source ) {
this.audioSource = source;
}
@Override
public boolean isPlaying() {
return playing;
}
@Override
public boolean isLooping() {
if( !playing ) {
looping = false;
@@ -42,6 +44,7 @@ public class Music {
return looping;
}
@Override
public void setVolume( double volume ) {
this.volume = (float) volume;
if( audioLine != null ) {
@@ -58,6 +61,7 @@ public class Music {
gainControl.setValue(vol);
}
@Override
public void playOnce() {
if( openLine() ) {
TaskRunner.run(new Runnable() {
@@ -69,23 +73,27 @@ public class Music {
}
}
@Override
public void playOnceAndWait() {
if( openLine() ) {
stream();
}
}
@Override
public void loop() {
looping = true;
playOnce();
}
@Override
public void stop() {
playing = false;
looping = false;
dispose();
}
@Override
public void dispose() {
if( audioLine != null ) {
if( audioLine.isRunning() ) {

View File

@@ -7,7 +7,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
public class Sound {
public class Sound implements Audio {
private boolean playing = false;
@@ -19,7 +19,7 @@ public class Sound {
private boolean disposeAfterPlay = false;
private float volume = 1.0f;
private float volume = 0.8f;
public Sound( String source ) {
this.audioSource = source;