Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 7. Auflage |
<< | < | > | >> | API | Kapitel 51 - Sound |
Das Sound-API macht eine sehr grundlegende Unterscheidung zwischen gesampeltem Sound und Midi-Sound. Beim Sampling, das wir in diesem Abschnitt behandeln wollen, wird ein Audio-Signal in viele kleine Stücke zerlegt, deren Amplitude in sehr kurzen Abständen mit Hilfe eines Analog-Digital-Konverters gemessen wird:
Abbildung 51.1: Samplen eines Audio-Signals
Die Frequenz, mit der die Abtastung geschieht, bezeichnet man als Sampling Rate und sie sollte mindestens doppelt so hoch sein wie die größte aufzuzeichnende Frequenz. Bei Audio-CDs beträgt sie 44100 Hz und die Auflösung des A/D-Konverters beträgt 16 Bit. Speichert man die so entstehenden Amplitudenwerte fortlaufend ab, so kann man bei Kenntnis der Sampling-Rate daraus später das Originalsignal näherungsweise rekonstruieren. Sind Sampling-Rate und Auflösung hoch genug, kann das menschliche Ohr keinen Unterschied zwischen gesampeltem und Originalsignal feststellen:
Abbildung 51.2: Ein gesampeltes Audio-Signal
Das Sound-API macht keine Annahmen über vorhandene Hardware oder angeschlossene Geräte. Stattdessen stellt es Methoden zur Verfügung, mit denen die vorhandene Hardware zur Laufzeit ermittelt und Objekte zum Zugriff darauf beschafft werden können. Im Falle von gesampeltem Sound dient dazu die Klasse AudioSystem aus dem Paket javax.sound.sampled. Sie besitzt eine Reihe von statischen Methoden, mit denen die grundlegenden Hilfsmittel für den Umgang mit gesampeltem Sound beschafft werden können:
Die Klasse AudioSystem stellt einige Methoden zum Zugriff auf Dateien mit gesampeltem Sound zur Verfügung:
public static AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException public static AudioFileFormat getAudioFileFormat(InputStream stream) throws UnsupportedAudioFileException, IOException public static AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException public static AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException public static AudioInputStream getAudioInputStream(InputStream stream) throws UnsupportedAudioFileException, IOException public static AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException public static boolean isConversionSupported( AudioFormat targetFormat, AudioFormat sourceFormat ) public static AudioInputStream getAudioInputStream( AudioFormat targetFormat, AudioInputStream sourceStream ) |
javax.sound.sampled.AudioSystem |
Mit getAudioFileFormat kann das AudioFileFormat einer Sound-Datei ermittelt werden. Die Eingabedatei kann entweder als File, InputStream oder URL übergeben werden. Mit getAudioInputStream kann ein AudioInputStream beschafft werden, mit dem der Inhalt der Sound-Datei gelesen werden kann. Auch hier darf der Parameter wahlweise vom Typ File, InputStream oder URL sein.
Die Klasse AudioSystem unterstützt sogar Konvertierungen zwischen verschiedenen Formaten. Ob eine bestimmte Konvertierung verfügbar ist, kann mit isConversionSupported abgefragt werden. Wird getAudioInputStream mit einem Zielformat und einem anderen AudioInputStream als Parameter aufgerufen, führt die Methode die Konvertierung durch und liefert einen AudioInputStream, der das gewünschte Zielformat hat.
Die Klasse AudioSystem stellt auch Methoden zum Zugriff auf Audio-Geräte zur Verfügung:
public static Mixer.Info[] getMixerInfo() public static Mixer getMixer(Mixer.Info info) public static Line getLine(Line.Info info) throws LineUnavailableException |
javax.sound.sampled.AudioSystem |
Um herauszufinden, welche Mixer verfügbar sind, muss zunächst getMixerInfo aufgerufen werden. Der Rückgabewert ist ein Array mit Mixer.Info-Objekten, das je vorhandenem Mixer ein Element enthält. Durch Übergabe eines Mixer.Info-Objekts an getMixer kann der zugehörige Mixer beschafft werden. Man kann allerdings auch ohne explizite Verwendung eines Mixers gesampelten Sound ausgeben, wenn mit getLine direkt ein geeignetes Audio-Gerät beschafft wird. Dazu muss ein Line.Info-Objekt übergeben werden, das dessen Eigenschaften beschreibt. Wir werden in Listing 51.1 zeigen, wie das Line.Info-Objekt konstruiert werden muss, um einen Clip zu erzeugen.
Wir hatten eingangs erwähnt, dass eine Line eine Reihe von Steuerlementen zur Verfügung stellt, mit denen Parameter wie Lautstärke, Stereo-Panorama oder Hall eingestellt werden können. Auf diese kann mit folgenden Methoden zugegriffen werden:
public Control[] getControls() public boolean isControlSupported(Control.Type control) public Control getControl(Control.Type control) |
javax.sound.sampled.Line |
getControls liefert ein Array aller verfügbaren Steuerelemente, die durch Instanzen der Klasse Control repräsentiert werden. Mit isControlSupported kann durch Übergabe eines Control.Type-Objekts festgestellt werden, ob ein bestimmter Typ vorhanden ist. Mit getControl kann dieser schließlich beschafft werden.
Control ist die Basisklasse einer ganzen Gruppe von Kontrollelementen, die sich durch den Datentyp des zu verändernden Parameters unterscheiden. Es gibt die Unterklassen BooleanControl, EnumControl, FloatControl und CompoundControl. Ein FloatControl beispielsweise dient zur Veränderung eines Fließkommawerts (wie etwa der Lautstärke), während ein BooleanControl einen An-/Aus-Wert verändern kann (beispielsweise die Stummschaltung eines Elements). Die wichtigsten Methoden der Klasse FloatControl sind:
public float getMaximum() public float getMinimum() public float getValue() public void setValue(float newValue) |
javax.sound.sampled.FloatControl |
Mit getMinimum und getMaximum können der kleinste und der größte einstellbare Wert abgefragt werden, mit getValue der aktuelle. Mit setValue kann der Wert des Controls verändert werden.
Die Type-Klassen der einzelnen Steuerelemente besitzen jeweils eine Reihe von vordefinierten Konstanten, die an getControl übergeben werden können. Für FloatControl.TYPE sind das beispielsweise MASTER_GAIN zur Einstellung der Lautstärke oder PAN zur Veränderung des Stereo-Panoramas.
Ein Steuerelement des Typs MASTER_GAIN bestimmt die Gesamtverstärkung, die das Audio-Element dem Ursprungssignal hinzufügt. Ihr Wert wird in Dezibel (dB) angegeben, wobei positive Werte eine Verstärkung und negative eine Abschwächung des Eingangssignals anzeigen. Das Dezibel ist eine logarithmische Maßeinheit, die den Verstärkungsfaktor durch die Formel 10dB/20 ausdrückt. 20 dB entsprechen also einer 10-fachen Verstärkung, -40 dB einer 100-fachen Abschwächung. 0 dB bedeutet, dass die Stärke des Ausgangs- im Verhältnis zum Eingangssignal unverändert bleibt.
Ein Steuerelement des Typs PAN bestimmt die Lage des Audio-Signals im Stereo-Panorama. Es kann Werte von -1.0 (ganz links) bis +1.0 (ganz rechts) annehmen. Ein Wert von 0 legt das Signal in die Mitte.
Ein Clip ist eine besondere Form einer DataLine, der alle Audiodaten komplett im Hauptspeicher hält. Wie jede Line muss er vor der Verwendung durch Aufruf von open geöffnet werden und nach Gebrauch mit close geschlossen werden. Zum Abspielen stellt er folgende Methoden zur Verfügung:
public void start() public void stop() public boolean isRunning() |
javax.sound.sampled.Clip |
Ein Aufruf von start startet das Abspielen des Clips, mit stop wird er angehalten. Mit isRunning kann überprüft werden, ob die Wiedergabe noch läuft oder bereits beendet wurde.
Die Methode start ist nicht modal, d.h., sie wartet beim Aufruf nicht, bis der Clip vollständig abgespielt ist. Sie initiiert lediglich den Abspielvorgang und kehrt dann sofort zum Aufrufer zurück. Falls dieser auf das Ende warten will, muss er entweder durch wiederholten Aufruf von isRunning den Status des Clips abfragen oder sich bei diesem als Listener registrieren und auf das STOP-Signal warten. |
|
Nach den Erklärungen der vorangegangenen Abschnitte wollen wir nun das Beispielprogramm zum Abspielen einer Sample-Datei vorstellen:
001 /* Listing5101.java */ 002 003 import java.io.*; 004 import javax.sound.sampled.*; 005 006 public class Listing5101 007 { 008 private static void playSampleFile(String name, float pan, float gain) 009 throws Exception 010 { 011 //AudioInputStream öffnen 012 AudioInputStream ais = AudioSystem.getAudioInputStream( 013 new File(name) 014 ); 015 AudioFormat format = ais.getFormat(); 016 //ALAW/ULAW samples in PCM konvertieren 017 if ((format.getEncoding() == AudioFormat.Encoding.ULAW) || 018 (format.getEncoding() == AudioFormat.Encoding.ALAW)) 019 { 020 AudioFormat tmp = new AudioFormat( 021 AudioFormat.Encoding.PCM_SIGNED, 022 format.getSampleRate(), 023 format.getSampleSizeInBits() * 2, 024 format.getChannels(), 025 format.getFrameSize() * 2, 026 format.getFrameRate(), 027 true 028 ); 029 ais = AudioSystem.getAudioInputStream(tmp, ais); 030 format = tmp; 031 } 032 //Clip erzeugen und öffnen 033 DataLine.Info info = new DataLine.Info( 034 Clip.class, 035 format, 036 ((int) ais.getFrameLength() * format.getFrameSize()) 037 ); 038 Clip clip = (Clip)AudioSystem.getLine(info); 039 clip.open(ais); 040 //PAN einstellen 041 FloatControl panControl = (FloatControl)clip.getControl( 042 FloatControl.Type.PAN 043 ); 044 panControl.setValue(pan); 045 //MASTER_GAIN einstellen 046 FloatControl gainControl = (FloatControl)clip.getControl( 047 FloatControl.Type.MASTER_GAIN 048 ); 049 gainControl.setValue(gain); 050 //Clip abspielen 051 clip.start(); 052 while (true) { 053 try { 054 Thread.sleep(100); 055 } catch (Exception e) { 056 //nothing 057 } 058 if (!clip.isRunning()) { 059 break; 060 } 061 } 062 clip.stop(); 063 clip.close(); 064 } 065 066 public static void main(String[] args) 067 { 068 try { 069 playSampleFile( 070 args[0], 071 Float.parseFloat(args[1]), 072 Float.parseFloat(args[2]) 073 ); 074 } catch (Exception e) { 075 e.printStackTrace(); 076 System.exit(1); 077 } 078 System.exit(0); 079 } 080 } |
Listing5101.java |
Die Methode playSampleFile öffnet zunächst einen AudioInputStream und bestimmt dessen AudioFormat. Ist dessen Kodierung ULAW oder ALAW konvertiert es den Stream in das PCM-Format, denn die anderen beiden Formate werden standardmäßig nicht unterstützt. Anschließend wird ein DataLine.Info-Objekt instanziert und in Zeile 038 ein Clip damit erzeugt. Dieser wird geöffnet und MASTER_GAIN und PAN werden auf die als Methodenparameter übergebenen Werte eingestellt. In Zeile 051 wird der Clip gestartet und anschließend gewartet, bis er vollständig abgespielt ist.
Das Programm wird mit dem Namen der Datei und den Werten für PAN und MASTER_GAIN als Kommandozeilenparameter aufgerufen. Es kann zum Abspielen von wav-, aiff- und au-Dateien verwendet werden.
Titel | Inhalt | Suchen | Index | DOC | Handbuch der Java-Programmierung, 7. Auflage, Addison Wesley, Version 7.0 |
<< | < | > | >> | API | © 1998, 2011 Guido Krüger & Heiko Hansen, http://www.javabuch.de |