【问题标题】:Java audio player cuts audio at the endJava音频播放器在最后切音频
【发布时间】:2014-04-17 03:08:03
【问题描述】:

嘿,堆栈溢出。

我正在用 Java 创建一个播放列表播放器,到目前为止一切顺利,我把所有的逻辑都搞定了,项目即将完成。我们一直在通过创建一些大型播放列表来测试播放,然后让事情从头到尾进行。播放听起来不错,但有时音频会在最后被切断。这种情况很少发生。最后 x 秒(时间不同)未播放。

我测试的文件都是 16 或 24 位采样大小的 PCM 波形文件。我使用 Java 声音引擎结合 Java zooms mp3 和 ogg spi 来支持其他类型的音频文件。

到目前为止,我已经记录了几次,我的第一个想法是文件可能已损坏,事实并非如此。我已经尝试单独播放该文件,它可以完全播放!

我试图找出问题所在,但就是找不到。我不认为我的音频播放器有什么问题,我的想法不多了。

这是我创建音频输入流的方法:

public static AudioInputStream getUnmarkableAudioInputStream(Mixer mixer, File file)
        throws UnsupportedAudioFileException
{
    if (!file.exists() || !file.canRead()) {
        return null;
    }

    AudioInputStream stream;
    try {
        stream = getAudioInputStream(file);
    } catch (IOException e) {
        logger.error("failed to retrieve stream from file", e);
        return null;

    }

    AudioFormat baseFormat = stream.getFormat();

    DataLine.Info info = new DataLine.Info(SourceDataLine.class, baseFormat);
    boolean supportedDirectly = false;
    if (mixer == null) {
        supportedDirectly = AudioSystem.isLineSupported(info);
    } else {
        supportedDirectly = mixer.isLineSupported(info);
    }

    // compare the AudioFormat with the desired one
    if (baseFormat.getEncoding() != AudioFormat.Encoding.PCM_SIGNED || !supportedDirectly) {
        AudioFormat decodedFormat = new AudioFormat(
                AudioFormat.Encoding.PCM_SIGNED,
                baseFormat.getSampleRate(), 16, baseFormat.getChannels(),
                baseFormat.getChannels() * 2, baseFormat.getSampleRate(),
                false);

        // convert the audio format to the supported one
        if (AudioSystem.isConversionSupported(decodedFormat, baseFormat)) {
            stream = AudioSystem.getAudioInputStream(decodedFormat, stream);
        } else {
            logger.debug(
                    "Audio format {} is not supported "
                            + "and can not be converted to default format",
                    baseFormat.toString());
            return null;
        }
    }
    return stream;
}

这是我的音频播放器线程:

final class PlayerThread extends Thread
{

    private byte[] buffer;

    /**
     * Initialize the buffer
     */
    public void initBuffer()
    {
        linelock.lock();
        try {
            buffer = new byte[line.getBufferSize() / 5];
        } finally {
            linelock.unlock();
        }
    }

    public void run()
    {
        initBuffer();
        while (!isInterrupted()) {
            checkState();

            // if the line is just cleared go to the start of the loop
            if (line == null || isInterrupted()) {
                continue;
            }

            write();
        }

        // clean up all resources
        close();

        // change the state
        state = Player.State.STOPPED;
    }

    private void checkState()
    {
        if (state != Player.State.PLAYING) {
            if (line != null) {
                line.flush();
            }

            try {
                synchronized (this) {
                    this.wait();
                }
            } catch (InterruptedException e) {
                // reset the interupt status
                interrupt();
            }
        }
    }

    private void write()
    {
        // how much bytes could be written on the line
        int available = line.available();

        // is the space on the line big enough to write the buffer to
        if (available >= buffer.length) {
            // fill the buffer array
            int read = 0;
            try {
                read = audioStream.read(buffer, 0, buffer.length);
            } catch (Throwable ball) {
                logger.error("Error in audio engine (read)", ball);
            }

            // if there was something to read, write it to the line
            // otherwise stop the player
            if (read >= 0) {
                try {
                    linelock.lock();
                    line.write(buffer, 0, read);
                } catch (Throwable ball) {
                    logger.error("Error in audio engine (write)", ball);
                } finally {
                    linelock.unlock();
                }
                bytesRead += read;
            } else {
                line.drain();
                MoreDefaultPlayer.this.stop();
            }
        }
    }

    private void close()
    {
        // invoke close on listeners
        invokePlayerClosedOnListeners();

        // destroy the volume chain
        vc.removeVolumeListener(MoreDefaultPlayer.this);

        // close the stream
        try {
            audioStream.close();
        } catch (IOException e) {
            logger.error("failed to close audio stream");
        }

        clearAllListeners();

        linelock.lock();
        try {
            // quit the line
            line.stop();
            line.close();
            line = null;
        } finally {
            linelock.unlock();
        }
    }
}

如您所见,我在之后排干了线路,所以我认为问题不是在播放流中的所有内容之前线路被关闭。
谁能看到这段代码可能有什么问题?

【问题讨论】:

  • 你可以在drain之后检查line.isActive(),看看是否所有的输出都完成了
  • 1) 为了尽快获得更好的帮助,请发布MCVE(最小完整且可验证的示例)。 2) 您可以尝试使用更简单的Clip 来完成此任务。
  • 嘿安德鲁,如果可以的话,我会的,但我似乎无法确定实际的错误。重现它的唯一方法是让玩家运行一整天左右。

标签: java audio javasound


【解决方案1】:

我没有看到明显的答案,但有几件事对我来说是个黄旗。通常的做法是将 line.write() 方法放在一个 while 循环中,而不是重复调用它。通常不需要测试 line.available() 或处理锁定线路。如果行上没有可用空间,则 line.write() 方法将处理必要的阻塞。我一直被警告不要不必要地锁定或阻塞音频线。

锁定逻辑是处理队列序列的一个组成部分吗?您描述的错误可能在该处理中。 (也许available()的测试与缓冲区大小相比存在交互作用?截断量大致等于缓冲区大小吗?)

我会考虑实现一个 LineListener 来宣布提示何时完成,并使该事件成为下一个提示播放的触发器。当给定文件完成时,可以发出 STOP 类型的 LineEvent,通知处理队列的任何人继续处理下一个文件。

【讨论】:

  • 感谢您对此进行调查,但我认为我遗漏了一些明显的东西。我删除了 line.available() 并稍微重组了循环,我看看它是怎么回事!
猜你喜欢
  • 2022-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多