Sound abspielen in Java

K. Macharia 12 Oktober 2023
  1. Sound mit Clip in Java abspielen
  2. Ton abspielen mit SourceDataLine in Java
Sound abspielen in Java

Manchmal sind Java-Anwendungen erforderlich, um Audiodateien abzuspielen. Angesichts der Tatsache, dass Ton zeitbasierte Daten sind und mit der richtigen Rate geliefert werden müssen, damit sie für die Wahrnehmung des Benutzers wiedergegeben werden können. Eine Änderung der Rate, mit der Daten geliefert werden, verzerrt den abgespielten Ton. Ziel der Java Sound API ist es, sicherzustellen, dass die Sounddaten mit der richtigen Rate und kontinuierlich über die Wiedergabe des Sounds übertragen werden. Dies wird erreicht, indem der Ton über eine Linie abgespielt wird, um sicherzustellen, dass er glatt und konsistent bleibt. Die beiden Arten von Zeilen, die Java bereitstellt, sind Clip und SourceDataLine.

Der Unterschied zwischen den beiden besteht im Ansatz der Angabe der Klangdaten. Mit Clip werden alle Sounddaten vor dem Wiedergabevorgang einmal angegeben, während in SourceDataLine während des gesamten Wiedergabevorgangs kontinuierlich Puffer geschrieben werden. Diese beiden Methoden unterstützen nur Audiodateien in den folgenden Formaten: AIFF, AIFC, WAVE, AU und SND. Es gibt viele Szenarien, in denen ein Entwickler eines der beiden verwenden kann und das gleiche Ergebnis erwartet, aber es gibt auch Szenarien, in denen eines bessere Ergebnisse liefert als das andere. Im Folgenden finden Sie eine Erläuterung zur Auswahl der effektivsten Leitung für die Anforderungen Ihrer Anwendung.

  1. Clip

Dies ist effektiver, wenn Sie eine kurze Audiodatei mehr als einmal als Clip lesen und abspielen möchten. Die volle Funktionsstärke des Clips wird am besten erreicht, wenn der Benutzer den abgespielten Sound in einer Schleife wiedergeben möchte. Mit dieser Funktion kann der Benutzer auch zufällig den Ort auswählen, an dem die Wiedergabe gestartet werden soll. Der clip ist schneller, da die Audiodatei geladen wird und daher nach dem Schleifen oder nachdem der Benutzer eine zufällige Dateiposition ausgewählt hat, keine weitere Pufferung erforderlich ist.

  1. SourceDataLine

Dies ist effektiver, wenn ein Benutzer den Speicher optimieren möchte, während er eine große Audiodatei abspielt. Dies ist auch die beste Option, wenn der Entwickler den abgespielten Sound nicht kennt. Diese Methode ist auch effektiver, wenn eine Klangumwandlung erforderlich ist, da die Klangdaten während der Wiedergabe von der Anwendung kontinuierlich aktualisiert werden müssen.

Sound mit Clip in Java abspielen

Der Clip ist im Paket javax.sound.sampled verfügbar und wurde in Java 7 eingeführt.

In diesem Beispiel werden Start, Pause, Wiederaufnahme, Stopp, Neustart und Start an einer zufälligen Position behandelt.

Nachfolgend sind die Schritte aufgeführt:

  • Der erste Schritt besteht darin, ein Objekt des Audioeingangsstroms zu erstellen. Dieser Schritt konvertiert die Audiodatei in einen Eingabestream, den die App verwenden kann.
  • Der zweite Schritt besteht darin, mit Audio System ein Objekt für die Clipreferenz zu erstellen
  • Der dritte Schritt besteht nun darin, das Clipobjekt mit Audiodaten aus dem in Schritt 1 erstellten Audioeingabestream zu laden.
  • Der nächste Schritt besteht darin, die erforderlichen Eigenschaften des Clips wie Schleife, Position und Mikrosekundenposition festzulegen
  • Sie können dann den clip starten.
import java.io.File;
import java.io.IOException;
import java.util.Scanner;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;

public class SoundPlayer {
  // define storage for start position
  Long nowFrame;
  Clip clip;

  // get the clip status
  String thestatus;

  AudioInputStream audioStream;
  static String thePath;

  // initialize both the clip and streams
  public SoundPlayer() throws UnsupportedAudioFileException, IOException, LineUnavailableException {
    // the input stream object
    audioStream = AudioSystem.getAudioInputStream(new File(thePath).getAbsoluteFile());

    // the reference to the clip
    clip = AudioSystem.getClip();

    clip.open(audioStream);

    clip.loop(Clip.LOOP_CONTINUOUSLY);
  }

  public static void main(String[] args) {
    try {
      // add the path to the audio file
      thePath = "add the path to the audio file here";

      SoundPlayer simpleSoundPlayer = new SoundPlayer();

      simpleSoundPlayer.play();
      Scanner scanned = new Scanner(System.in);

      // show the options
      while (true) {
        System.out.println("1. pause");
        System.out.println("2. resume");
        System.out.println("3. restart");
        System.out.println("4. stop");
        System.out.println("5. Jump to specific time");
        int a = scanned.nextInt();
        simpleSoundPlayer.gotoChoice(a);
        if (a == 4)
          break;
      }
      scanned.close();
    }

    catch (Exception e) {
      System.out.println("Experienced an error while playing sound.");
      e.printStackTrace();
    }
  }

  // operation is now as per the user's choice

  private void gotoChoice(int a)
      throws IOException, LineUnavailableException, UnsupportedAudioFileException {
    switch (a) {
      case 1:
        pause();
        break;
      case 2:
        resumeAudio();
        break;
      case 3:
        restart();
        break;
      case 4:
        stop();
        break;
      case 5:
        System.out.println("Selected time (" + 0 + ", " + clip.getMicrosecondLength() + ")");
        Scanner scan = new Scanner(System.in);
        long cc = scan.nextLong();
        jump(cc);
        break;
    }
  }

  // play
  public void play() {
    // start the clip
    clip.start();

    thestatus = "play";
  }

  // Pause audio
  public void pause() {
    if (thestatus.equals("paused")) {
      System.out.println("audio is already paused");
      return;
    }
    this.nowFrame = this.clip.getMicrosecondPosition();
    clip.stop();
    thestatus = "paused";
  }

  // resume audio
  public void resumeAudio()
      throws UnsupportedAudioFileException, IOException, LineUnavailableException {
    if (thestatus.equals("play")) {
      System.out.println("The audio is"
          + "being played");
      return;
    }
    clip.close();
    resetAudioStream();
    clip.setMicrosecondPosition(nowFrame);
    this.play();
  }

  // restart audio
  public void restart()
      throws IOException, LineUnavailableException, UnsupportedAudioFileException {
    clip.stop();
    clip.close();
    resetAudioStream();
    nowFrame = 0L;
    clip.setMicrosecondPosition(0);
    this.play();
  }

  // stop audio
  public void stop() throws UnsupportedAudioFileException, IOException, LineUnavailableException {
    nowFrame = 0L;
    clip.stop();
    clip.close();
  }

  // jump to a selected point
  public void jump(long a)
      throws UnsupportedAudioFileException, IOException, LineUnavailableException {
    if (a > 0 && a < clip.getMicrosecondLength()) {
      clip.stop();
      clip.close();
      resetAudioStream();
      nowFrame = a;
      clip.setMicrosecondPosition(a);
      this.play();
    }
  }

  // reset the audio stream
  public void resetAudioStream()
      throws UnsupportedAudioFileException, IOException, LineUnavailableException {
    audioStream = AudioSystem.getAudioInputStream(new File(thePath).getAbsoluteFile());
    clip.open(audioStream);
    clip.loop(Clip.LOOP_CONTINUOUSLY);
  }
}

Dieses Programm verwendet einen Audiostream, der aus den Sounddaten mit dem AudioInputStream gewonnen wird. Dies muss der erste Schritt sein, da das Programm die Daten nur als Stream erkennt, der zurückgesetzt werden muss, wenn sie wiederverwendet werden sollen.

Operations Erläuterung

Pause: Um den Player erfolgreich anzuhalten, muss der aktuelle Frame nach dem Stoppen des Players in einem Objekt gespeichert werden. Der Frame wird gespeichert, um sicherzustellen, dass der Spieler fortfahren kann, wenn die resume-Methode aufgerufen wird. Die Pause-Methode verwendet die clip.getMicrosecondPosition(), um den Pausenpunkt zu erfassen.

Resume: Wenn die Resume-Methode aufgerufen wird, verwendet sie den in einem Objekt gespeicherten Frame, um zu wissen, von wo aus fortgefahren werden soll. Resume verwendet die clip.setMicrosecondPosition(nowFrame), um den Audiostream an die Position zurückzusetzen, an der er sich befand, als die Pausenmethode aufgerufen wurde.

Stop: Die Stop-Methode schließt und stoppt den Clip. Wenn diese Methode aufgerufen wird, kann der Benutzer seine Position vorher nicht wieder aufnehmen, da der Frame nicht gespeichert wurde. Dies ist der technische Unterschied zwischen Pause und Stopp.

Es ist immer ratsam, vor dem Schließen des Programms die offenen Streams zu schließen. Bei Verwendung der oben genannten Programme werden ordnungsgemäße Überprüfungen durchgeführt, indem der Clip vor dem erneuten Abspielen angehalten wird. Dies stellt sicher, dass der Sound konsistent bleibt und die Ressourcen während der Wiedergabe effizient genutzt werden. In der Regel kann in Java ein Stream, in diesem Fall der Audio-Stream, vor dem Zurücksetzen nicht wiederverwendet werden. Wenn Sie es nicht zurücksetzen, bevor Sie es wiederverwenden, gibt das Programm einen Fehler aus.

Ton abspielen mit SourceDataLine in Java

Die SourceDataLine befindet sich in javax.sound.sampled.SourceDataLine. Führen Sie die folgenden Schritte aus, um die Soundwiedergabe SourceDataLine zu implementieren.

  • Der erste Schritt besteht darin, ein Objekt des Audioeingangsstroms zu erstellen. Dieser Schritt konvertiert die Audiodatei in einen Eingabestream, den die App verwenden kann.
  • Der zweite Schritt besteht darin, eine Zeile mit der Methode AudioSystem.getLine() zu öffnen.
  • Der dritte Schritt besteht darin, die angegebenen Teile des in Schritt 1 erstellten Audioeingangsstroms wiederholt zu lesen und an den Puffer von SourceDataLine weiterzuleiten. Dies wird bis zum Ende des Audiostreams wiederholt.
  • Nach Abschluss des Lesevorgangs und des Puffers werden die Ressourcen durch Schließen der Zeile freigegeben.
import java.io.File;
import java.io.IOException;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;

public class simpleSoundPlayer {
  // defining the byte buffer
  private static final int BUFFER_SIZE = 4096;

  void play(String filePath) {
    File soundFile = new File(filePath);
    try {
      // convering the audio file to a stream
      AudioInputStream sampleStream = AudioSystem.getAudioInputStream(soundFile);

      AudioFormat formatAudio = sampleStream.getFormat();

      DataLine.Info info = new DataLine.Info(SourceDataLine.class, formatAudio);

      SourceDataLine theAudioLine = (SourceDataLine) AudioSystem.getLine(info);

      theAudioLine.open(formatAudio);

      theAudioLine.start();

      System.out.println("Audio Player Started.");

      byte[] bufferBytes = new byte[BUFFER_SIZE];
      int readBytes = -1;

      while ((readBytes = sampleStream.read(bufferBytes)) != -1) {
        theAudioLine.write(bufferBytes, 0, readBytes);
      }

      theAudioLine.drain();
      theAudioLine.close();
      sampleStream.close();

      System.out.println("Playback has been finished.");

    } catch (UnsupportedAudioFileException e) {
      System.out.println("Unsupported file.");
      e.printStackTrace();
    } catch (LineUnavailableException e) {
      System.out.println("Line not found.");
      e.printStackTrace();
    } catch (IOException e) {
      System.out.println("Experienced an error.");
      e.printStackTrace();
    }
  }

  public static void main(String[] args) {
    String thePath = "path to your audio file here";
    simpleSoundPlayer player = new simpleSoundPlayer();
    player.play(thePath);
  }
}

Das obige Programm ist am effektivsten, wenn der Benutzer eine große Datei lesen möchte, ohne viel Speicherplatz zu verlieren. Dies kann auch angewendet werden, wenn der Benutzer Echtzeit-Sounddaten streamt und keine unnötige Verzögerungszeit haben möchte. Bei guter Implementierung kann diese Methode zum Abspielen von Sound dazu führen, dass ein Benutzer einen gleichmäßigen und konsistenten Sound mit sehr geringem Speicherbedarf erhält. Wenn die Leitung nicht geschlossen wird, kann dies jedoch zu einer Verstopfung des Speichers und letztendlich zu einer Klangverzerrung führen.

SoundDataLine ist auf folgende Weise eingeschränkt:

  1. Ein Benutzer kann nicht von einer beliebigen Position aus spielen
  2. Es ist nicht möglich, den Ton zu schleifen
  3. Es ist nicht möglich, wie bei einem Clip anzuhalten und fortzufahren
  4. Ein Benutzer kann die Dauer der ausgewählten Audiodatei nicht kennen, bevor er sie wiedergibt

Dies beschränkt die Szenarien, in denen ein Benutzer effektiv vom SoundDataLine-Programm profitieren kann, auf große Dateien oder Online-Streaming-Dienste.