Musikplayer mit Java Sound API

Veröffentlicht: 17. April 2011 in Multimedia
Schlagwörter:,

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 PlayerImplimplementiert 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.

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.

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

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:

	@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

Programmer’s Guide zur Sound API

Advertisements
Kommentare
  1. Manuel sagt:

    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^^

  2. birgitt sagt:

    schön … finde diesen musikplayer toll:
    http://www.pinkin.ch/
    🙂

  3. Nico sagt:

    Schöne Sache, da konnte ich mir direkt ein paar Anregungen für meinen Player holen. 🙂

    • Marlene Knoche sagt:

      Das freut mich, wenn ich dich so inspirieren konnte! 🙂

      • Bernhard sagt:

        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

  4. Marlene Knoche sagt:

    Hallo Bernhard, habe den Link gefixt, danke für den Hinweis. 😉

  5. Arduno sagt:

    Wäre interessiert an deiner Arbeit, bloss anscheind gibt es die Dateien nicht mehr.

    Grüße Arduno

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s