Jouer du son en Java

K. Macharia 12 octobre 2023
  1. Lire le son à l’aide d’un clip en Java
  2. Jouer du son à l’aide de SourceDataLine en Java
Jouer du son en Java

Des applications Java seront parfois nécessaires pour lire les fichiers audio. Étant donné que le son est une donnée temporelle et doit être délivré au rythme correct pour qu’il soit rendu à la perception de l’utilisateur. Une altercation de la vitesse à laquelle les données sont fournies déformera le son en cours de lecture. L’objectif de Java Sound API est de garantir que les données audio sont livrées au rythme correct et en continu pendant la lecture du son. Il y parvient en jouant le son à travers une ligne pour s’assurer qu’il reste lisse et cohérent. Les deux types de lignes fournis par Java sont Clip et SourceDataLine.

La différence entre les deux réside dans l’approche consistant à spécifier les données sonores. Avec Clip, toutes les données audio sont spécifiées une fois avant le processus de lecture, tandis que dans SourceDataLine, il y a une écriture tampon continue tout au long du processus de lecture. Ces deux méthodes ne prennent en charge que les fichiers audio aux formats suivants: AIFF, AIFC, WAVE, AU et SND. Il existe de nombreux scénarios dans lesquels un développeur peut utiliser l’un des deux et s’attendre à obtenir le même résultat, mais il existe également des scénarios où l’un donne de meilleurs résultats que l’autre. Vous trouverez ci-dessous une explication sur la façon de choisir la ligne la plus efficace pour les besoins de votre application.

  1. Clip

Il est plus efficace lorsque vous souhaitez lire et lire plusieurs fois un court fichier son sous forme de clip. La puissance de toutes les fonctionnalités du clip est mieux ressentie lorsque l’utilisateur souhaite mettre en boucle le son en cours de lecture. Cette fonction permet également à l’utilisateur de sélectionner l’endroit où il souhaite démarrer la lecture de manière aléatoire. Le clip est plus rapide car le fichier son est chargé et ne nécessite donc pas plus de mise en mémoire tampon après avoir été bouclé ou après que l’utilisateur a choisi de se déplacer vers une position de fichier aléatoire.

  1. SourceDataLine

Ceci est plus efficace lorsqu’un utilisateur souhaite optimiser la mémoire tout en lisant un fichier son volumineux. C’est également la meilleure option lorsque le développeur ne connaît pas le son qui sera joué. Cette méthode est également plus efficace lorsqu’il y a un besoin de transformation du son car elle nécessite que les données sonores soient continuellement mises à jour par l’application pendant la lecture.

Lire le son à l’aide d’un clip en Java

Le Clip est disponible dans le package javax.sound.sampled et a été introduit dans Java 7.

Dans cet exemple, nous couvrirons le démarrage, la pause, la reprise, l’arrêt, le redémarrage et le démarrage à une position aléatoire.

Voici les étapes impliquées:

  • La première étape consiste à créer un objet du flux d’entrée audio. Cette étape convertit le fichier audio en un flux d’entrée que l’application peut utiliser.
  • La deuxième étape consiste à utiliser Audio System pour créer un objet de référence de clip
  • La troisième étape consiste maintenant à charger l’objet clip avec les données audio du flux d’entrée audio créé à l’étape 1.
  • L’étape suivante consiste à définir les propriétés requises du clip telles que la boucle, la position et la position en microseconde
  • Vous pouvez alors démarrer le 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);
  }
}

Ce programme utilise un flux audio obtenu à partir des données sonores en utilisant AudioInputStream. Cela doit être la première étape car le programme reconnaît uniquement les données comme un flux qui doit être réinitialisé s’il doit être réutilisé.

Explication des opérations

Pause: Pour mettre le lecteur en pause avec succès, l’image actuelle doit être stockée dans un objet après l’arrêt du lecteur. Le cadre est stocké pour garantir que le joueur peut reprendre lorsque la méthode resume est appelée. La méthode Pause utilise le clip.getMicrosecondPosition() pour capturer le point de pause.

Resume: Lorsque la méthode Resume est appelée, elle utilise le cadre stocké dans un objet pour savoir d’où continuer. Resume utilise clip.setMicrosecondPosition(nowFrame)pour réinitialiser le flux audio à la position où il se trouvait lorsque la méthode de pause a été appelée.

Stop: La méthode d’arrêt ferme et arrête le clip. Lorsque cette méthode est appelée, l’utilisateur ne peut pas reprendre sa position précédente car la trame n’a pas été stockée. C’est la différence technique entre pause et stop.

Il est toujours conseillé qu’avant de fermer le programme, les flux ouverts soient fermés. Lors de l’utilisation des programmes ci-dessus, des vérifications appropriées sont mises en œuvre en arrêtant le clip avant de le lire à nouveau. Cela garantit que le son reste cohérent et que les ressources sont utilisées efficacement pendant la lecture. En règle générale en Java, un flux, dans ce cas, le flux audio ne peut pas être réutilisé avant d’être réinitialisé. Si vous ne le réinitialisez pas avant de le réutiliser, le programme générera une erreur.

Jouer du son à l’aide de SourceDataLine en Java

Le SourceDataLine se trouve dans javax.sound.sampled.SourceDataLine. Pour implémenter la lecture audio SourceDataLine, nous suivons les étapes suivantes.

  • La première étape consiste à créer un objet du flux d’entrée audio. Cette étape convertit le fichier audio en un flux d’entrée que l’application peut utiliser.
  • La deuxième étape consiste à ouvrir une ligne en utilisant la méthode AudioSystem.getLine().
  • La troisième étape consiste à lire à plusieurs reprises les morceaux spécifiés du flux d’entrée audio créé à l’étape 1 et à les transmettre au tampon de SourceDataLine. Ceci est répété jusqu’à la fin du flux audio.
  • Une fois la lecture et le tampon terminés, les ressources sont libérées en fermant la ligne.
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);
  }
}

Le programme ci-dessus est plus efficace lorsque l’utilisateur souhaite lire un fichier volumineux sans perdre beaucoup d’espace mémoire. Cela peut également être appliqué lorsque l’utilisateur diffuse des données audio en temps réel et ne souhaite pas avoir de temps de latence inutile. Lorsqu’elle est bien mise en œuvre, cette méthode de lecture du son peut permettre à l’utilisateur d’obtenir un son fluide et cohérent avec une très faible utilisation de la mémoire. Cependant, le fait de ne pas fermer la ligne peut entraîner un colmatage de la mémoire et, finalement, une distorsion du son.

SoundDataLine est limité des manières suivantes:

  1. Un utilisateur ne peut pas commencer à jouer à partir d’une position arbitraire
  2. Il n’est pas possible de boucler le son
  3. Il n’est pas possible de faire une pause et de reprendre comme vous le feriez avec un clip
  4. Un utilisateur ne peut pas connaître la durée du fichier audio sélectionné avant de le lire

Cela limite les scénarios dans lesquels un utilisateur peut bénéficier efficacement du programme SoundDataLine aux fichiers volumineux ou aux services de streaming en ligne.