gpt4 book ai didi

java - 从 JAR 应用程序播放音频文件在 Linux 上不起作用

转载 作者:行者123 更新时间:2023-12-04 18:38:53 26 4
gpt4 key购买 nike

我的 Java 应用程序需要一些帮助。它的目的是阅读某个网站,所以我需要连续播放很多音频文件。 JAR 是使用 Java 8 编译的。我使用 Windows 11 和 Java 16.0.1 测试了我的应用程序,一切正常。然后我使用了最新的 Ubuntu Linux 和 Java 11.0.13 以及 Java 8:它播放一些音频,但不是每个文件。
我写了一个测试类,结果是——无论我以什么顺序播放音频——只播放第一个(完全正确!)62个文件。每个下一个文件(即使是最初成功播放的文件)都会产生我的代码在此位置抛出的异常:

if (mixerSelected != null) {
audioClip0 = AudioSystem.getClip(mixerSelected);
} else {
throw new IllegalArgumentException("File is not compatible: '" + audioFilePath + "'.");
}
我确保每个音频文件都是 .WAV
  • 8k 采样率,
  • 平均每秒 16k 字节,
  • 16 位和
  • pcm_s16le 编解码器。

  • 我的应用程序构建为 JAR 文件,包括资源目录中的音频文件。
    这是我的代码:
    public class PlayAudio {

    /**
    * plays an audio file
    *
    * @param audioFilePath String: path to the audio file
    * @param speed double: speed applied to the audios
    */
    public boolean singleFile(String audioFilePath, double speed) {

    //audioFilePath = "audio" + File.separator + audioFilePath;
    audioFilePath = "audio" + "/" + audioFilePath;

    AudioInputStream audioStream0;

    //create new file using path to the audio
    try {
    //load files from resources folder as stream
    ClassLoader classLoader = getClass().getClassLoader();
    InputStream inputStream = classLoader.getResourceAsStream(audioFilePath);
    InputStream bufferedInputStream = new BufferedInputStream(inputStream);

    if (bufferedInputStream == null) {
    throw new IllegalArgumentException("File not found: '" + audioFilePath + "'.");
    } else {
    //create new AudioStream
    audioStream0 = AudioSystem.getAudioInputStream(bufferedInputStream);
    }
    } catch (IllegalArgumentException e) {
    //handle
    return false;
    } catch (IOException e) {
    //handle
    return false;
    } catch (UnsupportedAudioFileException e) {
    //handle
    return false;
    }

    try {
    //create new AudioFormat
    AudioFormat audioFormat0 = audioStream0.getFormat();

    //create new Info
    DataLine.Info info0 = new DataLine.Info(Clip.class, audioFormat0);

    //initialize new Mixer
    Mixer.Info mixerSelected = null;

    for (Mixer.Info mixerInfo : AudioSystem.getMixerInfo()) {
    Mixer mixer = AudioSystem.getMixer(mixerInfo);

    if (mixer.isLineSupported(info0)) {
    mixerSelected = mixerInfo;
    break;
    }
    }

    //create new Clip
    Clip audioClip0;

    if (mixerSelected != null) {
    audioClip0 = AudioSystem.getClip(mixerSelected);
    } else {
    //THIS EXCEPTION GETS THROWN!!!
    throw new IllegalArgumentException("File is not compatible: '" + audioFilePath + "'.");
    }

    //open created Clips via created AudioStream
    audioClip0.open(audioStream0);

    //start the play of audio file
    audioClip0.start();

    //wait until play completed
    double waitTime = (double)((((double)audioClip0.getMicrosecondLength()/1000.0)/speed + 50.0) * 0.8);
    Thread.sleep((long)waitTime);

    return true;

    //handle exceptions
    } catch (LineUnavailableException e) {
    //handle
    return false;
    } catch (IOException e) {
    //handle
    return false;
    } catch (InterruptedException e) {
    //handle
    return false;
    } catch (IllegalArgumentException e) {
    //THIS EXCEPTION GETS THROWN!!!
    //handle invalid audio clips
    System.out.println(e);
    e.printStackTrace();
    return false;
    }
    }



    /**
    * plays multiple audio files in the order they are stored in an ArrayList
    *
    * @param fileNames ArrayList<String>: list with filenames of audio files to play
    * @param speaker String: speaker to use for playing the audios (can be 'm' or 'w')
    * @param speed double: speed applied to the audios
    * @return boolean: true if playing audios completed successfully, otherwise false
    */
    public static boolean multiFiles(ArrayList<String> fileNames, String speaker, double speed) {

    PlayAudio player = new PlayAudio();

    //play every audio file in the array of file names
    for (int i = 0; (i < fileNames.toArray().length); i ++) {
    //generate file names
    String fullFileName = speaker + "_" + fileNames.toArray()[i];

    //play audio
    player.singleFile(fullFileName, speed);
    }

    return true;
    }
    }
    我已经尝试了什么?
  • 我也在另一台运行 Ubuntu Linux 的计算机上进行了尝试。
  • 我创建了 PlayAudio() 的新实例每次播放新的音频。
  • 我用了audioClip0.stop();在每个音频之后。
  • 我将每个音频后的 sleep 毫秒数增加到音频长度加 1 秒。
  • 我重建了项目......近 1k 次。

  • 如何重现错误?
    我只需要在 Linux Ubuntu 下播放超过 62 个运行我的 JAR 文件的音频文件。
    你怎么能帮助我?
    我不知道如何处理这个问题。播放有什么问题 .WAV -Linux 的文件?
    有没有一种通用的方法来解决这个问题?
    (我不允许使用除 OracleJDK 和 OpenJDK 之外的任何库。)

    最佳答案

    #1 的建议是由 Mark Rotteveel 提出的。 AudioInputStream 类需要关闭。这通常会让人们感到惊讶,因为 Java 以管理垃圾收集而闻名。但是对于 AudioInputStream 有需要释放的资源。 API 没有充分指出这一点,恕我直言,但可以从 AudioInputStream.close() 方法的描述中推断出需要处理:

    Closes this audio input stream and releases any system resourcesassociated with the stream.


    #2 来自 Andrew Thompson 和 Hendrik 的建议可能比直接解决方案更有帮助,但这仍然是一个非常好的主意,而且在我看来,所有额外的、不需要的基础设施的低效率 ( ClassLoader, InputStreamBufferedInputStream )可能是导致问题的原因。但我对底层代码的理解真的不够好,无法知道它的相关性。
    但是,我认为你可以做得更好。不要使用 Clip 。您当前对 Clip 的使用违背了其设计理念。 Clips 适用于要保存在内存中并多次播放的短时声音,而不是在每次播放之前重复重新加载的文件。这种使用(加载和播放)的正确类是 SourceDataLine
    可以在 javasound wiki 中找到使用 SourceDataLine 进行回放的示例。此示例还说明了使用 URL 来获取必要的 AudioInputStream 。我将在这里逐字引用。
    public class ExampleSourceDataLine {

    public static void main(String[] args) throws Exception {

    // Name string uses relative addressing, assumes the resource is
    // located in "audio" child folder of folder holding this class.
    URL url = ExampleSourceDataLine.class.getResource("audio/371535__robinhood76__06934-distant-ship-horn.wav");
    // The wav file named above was obtained from https://freesound.org/people/Robinhood76/sounds/371535/
    // and matches the audioFormat.
    AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url);

    AudioFormat audioFormat = new AudioFormat(
    Encoding.PCM_SIGNED, 44100, 16, 2, 4, 44100, false);
    Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
    SourceDataLine sourceDataLine = (SourceDataLine)AudioSystem.getLine(info);
    sourceDataLine.open(audioFormat);

    int bytesRead = 0;
    byte[] buffer = new byte[1024];
    sourceDataLine.start();
    while((bytesRead = audioInputStream.read(buffer)) != -1)
    {
    // It is possible at this point manipulate the data in buffer[].
    // The write operation blocks while the system plays the sound.
    sourceDataLine.write(buffer, 0, bytesRead);
    }
    sourceDataLine.drain();
    // release resources
    sourceDataLine.close();
    audioInputStream.close();
    }
    }
    您必须进行一些编辑,因为该示例设置为通过 main 方法运行。此外,您将使用您的音频格式,并且音频文件的名称及其文件夹位置必须与您在 getResource() 方法中使用的参数中指定的相对或绝对位置相匹配。此外, buffer 数组的更大尺寸可能是首选。 (我经常用8192)。
    但最重要的是,请注意,在此示例中,我们关闭了 SourceDataLineAudioInputStream 。使用 try-with-resources 的替代建议是一个很好的建议,并且也会释放资源。
    如果在更改上述内容以适应您的程序时遇到困难,我相信如果您向我们展示您的尝试,我们可以帮助使其工作。

    关于java - 从 JAR 应用程序播放音频文件在 Linux 上不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70684725/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com