Last updated on 26. November 2013
Die Java Sound API bietet die Möglichkeit Audio-Files in Java abzuspielen. Die dabei unterstützten Dateiformate sind .wav, .aiff, .au und .mid, wobei letzteres außerdem den Funktoinsumfang mit sich bringt, Soundspuren aufzunehmen. Für die Multimedia-Vorlesung sind nun zwei Programme entstanden, welche die Sound API nutzen.
Sound Applet
Um sich mit der Sound API vertraut zu machen, diente zunächst ein Applet, welches mittels eines AudioClip
einen Stream von einer URL ausliest und diese dann abspielt. Dabei schränkt sich der Funktionsumfang auf einen Play- und einen Stop-Button ein.
Musikplayer
Nach dem Schnupperkurs mit dem Sound Applet galt es nun einen Musikplayer zu implementieren, der gepuffert eine Musik-Datei abspielen können sollte. Durch die Freiheit ein Applet oder eine Desktop-Anwendung zu implementieren, habe ich mich für eine Desktop-Anwendung entschieden. Die Implementierung unterstützt .wav, .au und .aiff – Dateien.
Mockup
Mit der Erstellung des Mockups war der Grundstein für den Musikplayer gelegt. Aus dem Mockup erkennbar gehen auch die Use Cases hervor, die für den Musikplayer relevant sind:
- Laden von Soundfiles von Festplatte und/oder Internet
- Play
- Pause
- Stop
- Anzeige des aktuellen Titels
- Regulierung der Lautstärke
- Visualisierung der Position im Song
Klassendiagramm
Das hier abgebildete Klassendiagramm möchte ich im folgenden Abschnitt nun etwas näher erläutern.
Implementierung
Die Implementierung des Musikplayers ist deutlich aufwändiger als die des Sound Applets. Dadurch ist es auch nicht verwunderlich, dass es einen enormen Zuwachs an Klassen gibt. Eine weitere wesentliche Änderung zum Sound Applet ist, dass als Audio-Spur nun nicht mehr ein AudioClip
sondern eine SourceDataLine
verwendet wird. Diese ermöglicht ein gepuffertes abspielen der Audiodateien.
GUI
Die genauere Erklärung möchte ich bei der GUI beginnen. Diese befindet sich in der Klasse PlayerGui
, wie auch schon aus dessen Name ersichtlich wird. Die Klasse besitzt, wie im Mockup oben ersichtlich diverse Buttons, Labels und einen Slider. Diese sind mit diversen Listenern versehen und ermöglichen somit die Interaktion mit dem zwischen Nutzer und Player.
Weiterhin besitzt die Klasse eine Referenz auf das Interface Player
und einen Setter für das Player-Objekt.
Interface Player
Das eben angesprochene Interface stellt eine Schnittstelle für alle Funktionen des Musikplayers zur Verfügung. Dabei sind die folgenden Methoden enthalten: play() , pause(), stop(), setVolume(), mute(), setFile(), getFile(), getStatus()
und addTimeListener()
. Was die einzelnen Methoden machen, sollte aus dem Methodennamen hervorgehen, daher werde ich nur auf vier dieser Methoden etwas näher eingehen.
setFile()
– Diese Methode teilt dem Player mit, welches Lied er abspielen soll. Das Gegenstück dazu ist getFile()
, welche das aktuelle Fileobjekt des Players zurück liefert. Das wird benötigt, um den aktuellen Titel in der GUI anzeigen zu können.
Mit getStatus()
wird der aktuelle Status des Players, welcher durch ein Enum Status
repräsentiert wird, zurückgegeben. Dieses Enum kann die Werte stopped, playing
oder paused
annehmen. Die Methode addTimeListener()
fügt einen neuen TimeListener einer Liste von TimeListenern hinzu.
Interface TimeListener
Was es mit dem TimeListener auf sich hat, möchte ich noch etwas genauer ausführen. TimeListener
ist ebenfalls ein Interface, welches die Methode setTime()
bereitstellt. Diese Methode aktualisiert die Zeitanzeige in der GUI. Dabei ist die Implementierung hier über das Observerpattern gelöst, wobei die GUI mittels addTimeListener()
einen Listener beim Player anmeldet.
Die Klasse PlayerImpl
Die Klasse PlayerImpl
implementiert das Interface Player
und hat somit auch alle Methoden dieses Interfaces. Des Weiteren besitzt diese Klasse einen Status
, ein File
, eine SourceDataLine
sowie einen Boolean isMute
. Zudem enthält sie eine Liste von TimeListenern, welche über addTimeListener()
, wie oben beschrieben hinzugefügt werden.
PlayerImpl
hat noch eine Methode updateTimeListener()
welche auf alle TimeListener setTime()
ausführt, wodurch eine Zeitübergabe und damit die Aktualisierung der Zeit in der GUI ausgeführt werden kann.
Innere Klasse PlayerThread
Da das Abspielen der Musik in einem separaten Thread erfolgen muss und der Thread aber auch auf die Attribute von PlayerImpl
zugreifen muss, war es sinnvoll, diesen Thread als innere Klasse zu realisieren.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class PlayerImpl implements Player { Status status = Status.stopped; File file; SourceDataLine sdl; boolean isMute = false; List timeListeners = new ArrayList(); private class PlayerThread extends Thread { public void run() { // Implementierung } } } |
Nachdem die SourceDataLine
geöffnet wurde, wird in der While-Schleife, die sich in der run()
-Methode befindet, der Status des Players überprüft. Da diese Schleife unendlich lang läuft bleibt der Player also auch so lange pausiert, bis man ihm einen anderen Status zuweist.
1 2 3 4 5 6 7 8 9 10 |
while ( true ) { i f ( s t a t u s == S t a t u s . stopped ) { break ; } i f ( s t a t u s == S t a t u s . paused ) { Thread . s l e e p ( 5 0 0 ) ; } else { / / abspielen } } |
Die Grundstruktur des Players ist damit erklärt, nun möchte ich noch auf die enthaltenen Funktionen etwas näher eingehen.
Files auswählen
Um einen File von der Festplatte an den Player zu übergeben, habe ich mich des JFileChooser
’s bedient, welcher einen Dialog zur Auswahl einer Datei von der Festplatte bereit stellt.
Abspielen des Streams
Zum Abspielen eines Streams sind einige Schritte der Vorbereitung notwendig. Man muss zunächst ein File Objekt anlegen, wonach man einen AudioInputStream anfordern muss. Ist dies geschehen erfolgt eine Überprüfung des Audioformates und es wird eine Line zum Audioformat angefordert. Jetzt kann die Line geöffnet werden und die Wiedergabe kann starten. Wenn die Wiedergabe gestoppt ist, muss die Line wieder geschlossen werden, weil sonst das AudioDevice nicht verfügbar wäre, über welches die Musik abgespielt wird. Dieser Zustand würde sich erst nach einem Neustart des Computers beheben lassen.
Position des Titels
1 2 3 |
long timeInSeconds = sdl.getMicrosecondPosition(); updateTimeListeners(timeInSeconds); |
Über diesen Aufruf kann man die aktuelle Position des Liedes bestimmen. Sie befindet sich im PlayerThread und wird durch die updateTimeListeners()
– Methode ständig aktuell gehalten.
Lautstärkeregelung
Die Lautstärkeregelung kann zum einen über den Mute-Button vorgenommen werden, welcher den Player beim drücken auf stumm schaltet und bei erneutem drücken wieder auf die Ausgangslautstärke zurücksetzt.
Zum anderen kann man zur Lautstärkeregelung aber auch den Slider benutzen, welcher mit einem ChangeListener versehen ist und somit den aktuellen Wert für die Lautstärke auch augenblicklich verändern kann. Dies geschieht mit Hilfe von einer kleinen Berechnung, die den Wert in Dezibel wiedergibt:
1 2 3 4 5 6 7 8 9 10 11 12 |
@Override public void setVolume(float control) { double gain = control / 100; float dB = (float) (Math.log(gain) / Math.log(10.0) * 20.0); FloatControl floatControl = (FloatControl) sdl.getControl(FloatControl.Type.MASTER_GAIN); floatControl.setValue(dB); } |
Dabei ist FloatControl
eine Methode die die Sound API zur Verfügung stellt, um die Lautstärkeregelung zu beeinflussen. Als Anmerkung sei gesagt, dass es sich bei Dezibel um eine logarithmische Größe handelt, weshalb in der Methode auch mit der Logarithmusfunktion gearbeitet wurde.
Downloads und weiterführende Links
Code des SoundApplets
Ausführbare Jar Datei des Players
Code des Musikplayers
Cool cool. Der Player fetzt schon so. Da hast du es tatsächlich eher geschafft, nen Funktionierenden Player zu basteln als ich. Meiner braucht noch ne weile^^
schön … finde diesen musikplayer toll:
http://www.pinkin.ch/
🙂
Schöne Sache, da konnte ich mir direkt ein paar Anregungen für meinen Player holen. 🙂
Das freut mich, wenn ich dich so inspirieren konnte! 🙂
Hallo, sehr netter Blog bzw. Beitrag gerade zufällig drauf gestoßen und wollte mir den Code zu Gemüte führen, leider funktioniert der Link nicht mehr…wäre super wenn du den Link erneuern könntest
lg
Hallo Bernhard, habe den Link gefixt, danke für den Hinweis. 😉
Die Links funktionieren nicht …..
Bitte fixen
DANKE 🙂
Fixe ich die Tage mal – hab momentan leider zu viel um die Ohren. Melde mich wieder, sobald das geschehen ist. 😉