Riproduci suono in Java

K. Macharia 12 ottobre 2023
  1. Riproduci il suono utilizzando la clip in Java
  2. Riproduci suono utilizzando SourceDataLine in Java
Riproduci suono in Java

A volte saranno necessarie applicazioni Java per riprodurre file audio. Dato che il suono è dati basati sul tempo e deve essere trasmesso alla velocità corretta affinché possa essere riprodotto per la percezione dell’utente. Un alterco della velocità di trasmissione dei dati distorce il suono riprodotto. L’obiettivo di Java Sound API è garantire che i dati audio vengano forniti alla velocità corretta e continuamente durante la riproduzione del suono. Ottiene ciò riproducendo il suono attraverso una linea per garantire che rimanga regolare e coerente. I due tipi di righe forniti da Java sono Clip e SourceDataLine.

La differenza tra i due sta nell’approccio di specificare i dati sonori. Con Clip, tutti i dati audio vengono specificati una volta prima del processo di riproduzione, mentre in SourceDataLine, c’è una scrittura continua nel buffer durante tutto il processo di riproduzione. Questi due metodi supportano solo file audio nei seguenti formati: AIFF, AIFC, WAVE, AU e SND. Esistono molti scenari in cui uno sviluppatore può utilizzare uno dei due e aspettarsi di ottenere lo stesso risultato, ma ci sono anche scenari in cui uno fornisce risultati migliori dell’altro. Di seguito una spiegazione su come scegliere la linea più efficace per le esigenze della propria applicazione.

  1. Clip

È più efficace quando si desidera leggere e riprodurre un breve file audio come clip più di una volta. La forza della funzionalità completa della clip è meglio sperimentata quando l’utente desidera riprodurre in loop il suono riprodotto. Questa funzione consente inoltre all’utente di selezionare il luogo in cui desidera avviare la riproduzione in modo casuale. La clip è più veloce perché il file audio viene caricato e quindi non richiede più buffering dopo essere stato riprodotto in loop o dopo che l’utente ha scelto di spostarsi in una posizione di file casuale.

  1. SourceDataLine

Ciò è più efficace quando un utente desidera ottimizzare la memoria durante la riproduzione di un file audio di grandi dimensioni. È anche l’opzione migliore quando lo sviluppatore non conosce il suono che verrà riprodotto. Questo metodo è anche più efficace quando è necessaria la trasformazione del suono perché richiede che i dati audio siano continuamente aggiornati dall’applicazione durante la riproduzione.

Riproduci il suono utilizzando la clip in Java

La Clip è disponibile nel pacchetto javax.sound.sampled ed è stata introdotta in Java 7.

In questo esempio, tratteremo l’avvio, la pausa, la ripresa, l’arresto, il riavvio e l’avvio da una posizione casuale.

Di seguito sono riportati i passaggi coinvolti:

  • Il primo passo è creare un oggetto del flusso di ingresso audio. Questo passaggio converte il file audio in un flusso di input che l’app può utilizzare.
  • Il secondo passaggio consiste nell’usare Audio System per creare un oggetto per riferimento alla clip
  • Il terzo passaggio consiste ora nel caricare l’oggetto clip con i dati audio dal flusso di ingresso audio creato nel passaggio 1.
  • Il passaggio successivo consiste nell’impostare le proprietà richieste della clip come loop, posizione e posizione del microsecondo
  • È quindi possibile avviare la clip.
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);
  }
}

Questo programma utilizza un flusso audio ottenuto dai dati audio utilizzando AudioInputStream. Questo deve essere il primo passo perché il programma riconosce i dati solo come un flusso che deve essere ripristinato se deve essere riutilizzato.

Spiegazione delle operazioni

Pause: Per mettere in pausa con successo il lettore, il fotogramma corrente deve essere memorizzato in un oggetto dopo che il lettore si ferma. Il frame viene memorizzato per garantire che il giocatore possa riprendere quando viene chiamato il metodo resume. Il metodo Pause utilizza clip.getMicrosecondPosition() per catturare il punto di pausa.

Resume: Quando viene chiamato il metodo Resume, utilizza il frame memorizzato in un oggetto per sapere da dove continuare. Resume usa clip.setMicrosecondPosition(nowFrame)per ripristinare il flusso audio alla posizione in cui si trovava quando è stato chiamato il metodo di pausa.

Stop: il metodo stop chiude e arresta il clip. Quando viene chiamato questo metodo, l’utente non può riprendere la propria posizione prima perché il frame non è stato memorizzato. Questa è la differenza tecnica tra pausa e stop.

È sempre consigliabile che prima di chiudere il programma, gli stream aperti vengano chiusi. Durante l’utilizzo dei programmi di cui sopra, i controlli appropriati vengono implementati interrompendo il clip prima di riprodurlo di nuovo. Ciò garantisce che il suono rimanga coerente e le risorse siano utilizzate in modo efficiente durante la riproduzione. Di norma in Java, un flusso, in questo caso, il flusso audio non può essere riutilizzato prima di essere ripristinato. Il mancato ripristino prima di riutilizzarlo porterà il programma a dare un errore.

Riproduci suono utilizzando SourceDataLine in Java

La SourceDataLine si trova in javax.sound.sampled.SourceDataLine. Per implementare la riproduzione del suono SourceDataLine, seguiamo i seguenti passaggi.

  • Il primo passo è creare un oggetto del flusso di ingresso audio. Questo passaggio converte il file audio in un flusso di input che l’app può utilizzare.
  • Il secondo passo è aprire una riga usando il metodo AudioSystem.getLine().
  • Il terzo passaggio consiste nel leggere ripetutamente i blocchi specificati del flusso di ingresso audio creato nel passaggio 1 e inoltrarlo al buffer di SourceDataLine. Questo viene ripetuto fino alla fine del flusso audio.
  • Dopo che la lettura e il buffer sono stati completati, le risorse vengono liberate chiudendo la riga.
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);
  }
}

Il programma sopra è più efficace quando l’utente vuole leggere un file di grandi dimensioni senza rinunciare a molto spazio di memoria. Questo può essere applicato anche quando l’utente sta trasmettendo dati audio in tempo reale e non vuole avere un tempo di ritardo non necessario. Se ben implementato, questo metodo di riproduzione del suono può portare l’utente a ottenere un suono fluido e coerente con un utilizzo della memoria molto basso. Tuttavia, la mancata chiusura della linea può portare all’ostruzione della memoria e, in ultima analisi, alla distorsione del suono.

SoundDataLine è limitato nei seguenti modi:

  1. Un utente non può iniziare a giocare da una posizione arbitraria
  2. Non è possibile riprodurre in loop il suono
  3. Non è possibile mettere in pausa e riprendere come faresti con un clip
  4. Un utente non può conoscere la durata del file audio selezionato prima di riprodurlo

Ciò limita gli scenari in cui un utente può trarre vantaggio dal programma SoundDataLine a file di grandi dimensioni o servizi di streaming online.