Java でサウンドを再生する

K. Macharia 2023年10月12日
  1. Java で Clip を使用してサウンドを再生する
  2. Java で SourceDataLine を使用してサウンドを再生する
Java でサウンドを再生する

オーディオファイルを再生するには、Java アプリケーションが必要になる場合があります。サウンドは時間ベースのデータであり、ユーザーの知覚に合わせてレンダリングするには、正しいレートで配信する必要があることを前提としています。データの配信速度を変更すると、再生中のサウンドが歪んでしまいます。Java Sound API の目的は、サウンドデータが正しいレートで配信され、サウンドの再生中に継続的に配信されるようにすることです。ラインを介してサウンドを再生し、スムーズで一貫性を保つことでこれを実現します。Java が提供する 2 種類の行は、Clip と SourceDataLine です。

両者の違いは、サウンドデータを指定するアプローチにあります。Clip では、すべてのサウンドデータは再生プロセスの前に一度指定されますが、SourceDataLine では、再生プロセス全体で継続的なバッファ書き込みが行われます。これらの 2つの方法は、AIFFAIFCWAVEAU、および SND の形式のオーディオファイルのみをサポートします。開発者が 2つのいずれかを使用して同じ結果を期待できるシナリオはたくさんありますが、一方が他方よりも良い結果をもたらすシナリオもあります。以下は、アプリケーションのニーズに最も効果的なラインを選択する方法の説明です。

  1. Clip

短いサウンドファイルをクリップとして複数回読んで再生したい場合に効果的です。クリップの全機能の強さは、ユーザーが再生中のサウンドをループさせたいときに最もよく体験されます。この機能により、ユーザーはランダムに再生を開始する場所を選択することもできます。サウンドファイルがロードされ、ループ後またはユーザーがランダムなファイル位置に移動することを選択した後、より多くのバッファリングを必要としないため、クリップはより高速です。

  1. SourceDataLine

これは、ユーザーが大きなサウンドファイルを再生しながらメモリを最適化したい場合に効果的です。また、開発者が再生されるサウンドがわからない場合にも最適なオプションです。この方法は、再生中にアプリケーションによってサウンドデータを継続的に更新する必要があるため、サウンド変換が必要な場合にも効果的です。

Java で Clip を使用してサウンドを再生する

Clipjavax.sound.sampled パッケージで利用可能であり、Java7 で導入されました。

この例では、ランダムな位置での開始、一時停止、再開、停止、再開、および開始について説明します。

以下は、関連する手順です。

  • 最初のステップは、オーディオ入力ストリームのオブジェクトを作成することです。この手順では、オーディオファイルをアプリが使用できる入力ストリームに変換します。
  • 2 番目のステップは、オーディオシステムを使用して、クリップ参照用のオブジェクトを作成することです。
  • 3 番目のステップは、ステップ 1 で作成されたオーディオ入力ストリームからのオーディオデータをクリップオブジェクトにロードすることです。
  • 次のステップは、ループ、位置、マイクロ秒の位置など、クリップに必要なプロパティを設定することです。
  • その後、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);
  }
}

このプログラムは、AudioInputStream を使用してサウンドデータから取得したオーディオストリームを使用します。プログラムはデータをストリームとしてのみ認識するため、これは最初のステップである必要があります。ストリームは、再利用する場合はリセットする必要があります。

操作説明

Pause:プレーヤーを正常に一時停止するには、プレーヤーが停止した後、現在のフレームをオブジェクトに保存する必要があります。フレームは、resume メソッドが呼び出されたときにプレーヤーが再開できるように保存されます。Pause メソッドは、clip.getMicrosecondPosition() を使用して一時停止ポイントをキャプチャします。

ResumeResume メソッドが呼び出されると、オブジェクトに格納されているフレームを使用して、どこから続行するかを認識します。Resumeclip.setMicrosecondPosition(nowFrame) を使用して、pause メソッドが呼び出されたときの位置にオーディオストリームをリセットします。

StopStop メソッドは、クリップを閉じて停止します。このメソッドが呼び出されると、フレームが保存されていないため、ユーザーは以前に位置を再開できません。これは、一時停止と停止の技術的な違いです。

プログラムを閉じる前に、開いているストリームを閉じることを常にお勧めします。上記のプログラムを使用している間、クリップを再度再生する前に停止することにより、適切なチェックが実装されます。これにより、サウンドの一貫性が維持され、再生の進行中にリソースが効率的に利用されます。原則として、Java の場合、ストリーム、この場合、オーディオストリームはリセットされる前に再利用できません。再利用する前にリセットしないと、プログラムでエラーが発生します。

Java で SourceDataLine を使用してサウンドを再生する

SourceDataLinejavax.sound.sampled.SourceDataLine にあります。SourceDataLine サウンド再生を実装するには、次の手順に従います。

  • 最初のステップは、オーディオ入力ストリームのオブジェクトを作成することです。この手順では、オーディオファイルをアプリが使用できる入力ストリームに変換します。
  • 2 番目のステップは、AudioSystem.getLine() メソッドを使用して行を開くことです。
  • 3 番目のステップは、ステップ 1 で作成されたオーディオ入力ストリームの指定されたチャンクを繰り返し読み取り、それを SourceDataLine のバッファーに転送することです。これは、オーディオストリームが終了するまで繰り返されます。
  • 読み取りとバッファが完了した後、行を閉じることでリソースが解放されます。
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);
  }
}

上記のプログラムは、ユーザーが多くのメモリスペースを放棄せずに大きなファイルを読み取りたい場合に最も効果的です。これは、ユーザーがリアルタイムのサウンドデータをストリーミングしていて、不要なラグタイムを発生させたくない場合にも適用できます。適切に実装されている場合、このサウンドの再生方法により、ユーザーはメモリ使用量が非常に少なく、スムーズで一貫性のあるサウンドを得ることができます。ただし、回線を閉じないと、メモリが詰まり、最終的には音が歪む可能性があります。

SoundDataLine は、次の方法で制限されます。

  1. ユーザーが任意の位置からプレイを開始することはできません
  2. 音をループさせることはできません
  3. クリップの場合のように一時停止および再開することはできません
  4. ユーザーは、選択したオーディオファイルを再生する前にその長さを知ることができません

これにより、ユーザーが SoundDataLine プログラムの恩恵を効果的に享受できるシナリオが、大きなファイルやオンラインストリーミングサービスに限定されます。