Reproduzir som em Java

K. Macharia 12 outubro 2023
  1. Reproduzir som usando Clip em Java
  2. Tocar som usando SourceDataLine em Java
Reproduzir som em Java

Às vezes, os aplicativos Java são necessários para reproduzir arquivos de áudio. Dado que o som é um dado baseado no tempo e deve ser entregue na taxa correta para que seja renderizado para a percepção do usuário. Uma altercação da taxa em que os dados são entregues distorcerá o som que está sendo reproduzido. O objetivo da API Java Sound é garantir que os dados de som sejam entregues na taxa correta e continuamente durante a reprodução do som. Ele consegue isso reproduzindo o som através de uma linha para garantir que ele permaneça suave e consistente. Os dois tipos de linhas que o Java fornece são Clip e SourceDataLine.

A diferença entre os dois está na abordagem de especificar os dados de som. Com Clip, todos os dados de som são especificados uma vez antes do processo de reprodução, enquanto em SourceDataLine, há gravação de buffer contínua durante todo o processo de reprodução. Estes dois métodos suportam apenas arquivos de áudio nos seguintes formatos: AIFF, AIFC, WAVE, AU e SND. Existem muitos cenários em que um desenvolvedor pode usar qualquer um dos dois e esperar obter o mesmo resultado, mas também existem cenários em que um oferece melhores resultados do que o outro. Abaixo está uma explicação sobre como escolher a linha mais eficaz para as necessidades de seu aplicativo.

  1. Clip

É mais eficaz quando você deseja ler e reproduzir um arquivo de som curto como um clipe mais de uma vez. A força total da funcionalidade do Clip é melhor experimentada quando o usuário deseja repetir o som que está sendo reproduzido. Este recurso também permite que o usuário selecione o local onde deseja iniciar a reprodução aleatoriamente. O clip é mais rápido porque o arquivo de som é carregado e, portanto, não requer mais armazenamento em buffer após ser repetido ou depois que o usuário seleciona mover para uma posição de arquivo aleatória.

  1. SourceDataLine

Isso é mais eficaz quando um usuário deseja otimizar a memória enquanto reproduz um grande arquivo de som. Também é a melhor opção quando o desenvolvedor não conhece o som que será reproduzido. Este método também é mais eficaz quando há necessidade de transformação de som, pois requer que os dados de som sejam atualizados continuamente pelo aplicativo durante a reprodução.

Reproduzir som usando Clip em Java

O Clip está disponível no pacote javax.sound.sampled e foi introduzido no Java 7.

Neste exemplo, abordaremos iniciar, pausar, retomar, parar, reiniciar e iniciar em uma posição aleatória.

Abaixo estão as etapas envolvidas:

  • A primeira etapa é criar um objeto do fluxo de entrada de áudio. Esta etapa converte o arquivo de áudio em um fluxo de entrada que o aplicativo pode usar.
  • A segunda etapa é usar o sistema de áudio para criar um objeto para referência de clipe
  • A terceira etapa agora é carregar o objeto de clipe com dados de áudio do fluxo de entrada de áudio que foi criado na etapa 1.
  • A próxima etapa é definir as propriedades necessárias do clipe, como loop, posição e posição de microssegundo
  • Você pode então iniciar o 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);
  }
}

Este programa usa um fluxo de áudio obtido a partir dos dados de som usando o AudioInputStream. Este deve ser o primeiro passo porque o programa só reconhece os dados como um fluxo que deve ser redefinido se for reutilizado.

Explicação de operações

Pause: Para pausar o player com sucesso, o quadro atual deve ser armazenado em um objeto após o player parar. O quadro é armazenado para garantir que o player possa retomar quando o método resume for chamado. O método de pausa usa clip.getMicrosecondPosition() para capturar o ponto de pausa.

Resume: quando o método Resume é chamado, ele usa o quadro armazenado em um objeto para saber de onde continuar. Resume usa clip.setMicrosecondPosition(nowFrame)para redefinir o fluxo de áudio para a posição em que estava quando o método de pausa foi chamado.

Stop: o método stop fecha e pára o clipe. Quando este método é chamado, o usuário não pode retomar sua posição anterior porque o quadro não foi armazenado. Esta é a diferença técnica entre pausar e parar.

É sempre aconselhável que antes de fechar o programa, os fluxos abertos sejam fechados. Ao usar os programas acima, verificações adequadas são implementadas interrompendo o clipe antes de reproduzi-lo novamente. Isso garante que o som permaneça consistente e os recursos sejam utilizados de forma eficiente durante a reprodução. Como regra em Java, um fluxo, neste caso, o fluxo de áudio não pode ser reutilizado antes de ser reiniciado. Se não reiniciá-lo antes de reutilizá-lo, o programa apresentará um erro.

Tocar som usando SourceDataLine em Java

O SourceDataLine é encontrado em javax.sound.sampled.SourceDataLine. Para implementar a reprodução de som SourceDataLine, seguimos os seguintes passos.

  • A primeira etapa é criar um objeto do fluxo de entrada de áudio. Esta etapa converte o arquivo de áudio em um fluxo de entrada que o aplicativo pode usar.
  • A segunda etapa é abrir uma linha usando o método AudioSystem.getLine().
  • A terceira etapa é ler repetidamente os pedaços especificados do fluxo de entrada de áudio criado na etapa 1 e encaminhá-los para o buffer de SourceDataLine. Isso é repetido até o final do fluxo de áudio.
  • Depois que a leitura e o buffer foram concluídos, os recursos são liberados fechando a linha.
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);
  }
}

O programa acima é mais eficaz quando o usuário deseja ler um arquivo grande sem abrir mão de muito espaço na memória. Isso também pode ser aplicado quando o usuário está transmitindo dados de som em tempo real e não deseja ter um tempo de retardo desnecessário. Quando bem implementado, esse método de reprodução de som pode fazer com que o usuário obtenha um som suave e consistente com uso de memória muito baixo. No entanto, o não fechamento da linha pode causar obstrução da memória e, em última análise, distorção do som.

SoundDataLine é limitado das seguintes maneiras:

  1. Um usuário não pode começar a jogar de uma posição arbitrária
  2. Não é possível repetir o som
  3. Não é possível pausar e retomar como faria com um clipe
  4. Um usuário não pode saber a duração do arquivo de áudio selecionado antes de reproduzi-lo

Isso limita os cenários em que um usuário pode se beneficiar efetivamente do programa SoundDataLine para arquivos grandes ou serviços de streaming online.