4

我在 java 中使用 Clip 播放歌曲如下:

        MR.clip= (Clip) AudioSystem.getLine(MR.info[docIdOfSelectedSong]);
        MR.clip.open(MR.sounds[docIdOfSelectedSong]);
        MR.clip.setMicrosecondPosition(5* 1000000);
        MR.clip.start();

其中 MR.sounds 是一个类型数组,AudioInputStreamMR.info 是一个类型数组DataLine.info。当我按下一个按钮时,上面的代码被调用来播放歌曲。此外,我还有另一个按钮来停止调用以下代码的歌曲

public static void stopSong(){

    MR.clip.close();

}

问题是当我第一次播放这首歌时,播放和停止按钮工作正常。但是,当我第二次尝试播放这首歌时,我听不到这首歌。关于出了什么问题的任何建议?

4

4 回答 4

8

与所有其他 InputStream 一样,AudioInputStream 只能读取一次(除非它可以是.reset())。在尝试再次播放声音之前,您可以尝试在 AudioInputStream 上调用 .reset(),但 AudioInputStream 可能不支持 .reset()。InputStreams 不需要支持重置。另请参阅markSupported()

如果 .reset() 不起作用,请考虑在每次需要开始播放时构造一个新的 AudioInputStream。


更新:我做了一个在内存中缓存声音数据并使用 Clip 播放这些声音的例子。此示例使用 AudioInputStream.reset()。那怎么能行呢?事实上,当且仅当其底层 InputStream 支持 .reset()时,AudioInputStream支持 reset ()。因此,我的示例创建了一个由 ByteArrayInputStream 支持的 AudioInputStream。因为 ByteArrayInputStream 支持重置,所以这些缓存的 AudioInputStreams 也支持 .reset(),这允许它们被重用。

请注意,如果您要同时播放任何一个缓存的声音,您可能不应该缓存AudioInputStreams,而是缓存byte[]s 并构造一个AudioInputStream每次播放。这是因为AudioInputStream它是有状态的,因此将它的单个实例传递给两个同时运行的剪辑,或在播放一个剪辑时重置流,将导致状态冲突。

public class CachedSoundClipTest
{
    static ArrayList<AudioInputStream> cachedSounds = 
        new ArrayList<AudioInputStream>();

    public static void main(String[] args) throws Exception
    {
        File[] audioFiles = new File("/audio_storage_directory").listFiles();
        for (File file : audioFiles)
        {
            AudioInputStream reusableAudioInputStream = 
                createReusableAudioInputStream(file);
            cachedSounds.add(reusableAudioInputStream);
        }

        while(true)
        {
            System.out.println("Press enter to play next clip");
            BufferedReader br = 
                new BufferedReader(new InputStreamReader(System.in));
            br.readLine();
            playCachedSound(0);
        }
    }

    private static void playCachedSound(int i) 
        throws IOException, LineUnavailableException
    {
        AudioInputStream stream = cachedSounds.get(i);
        stream.reset();
        Clip clip = AudioSystem.getClip();
        clip.open(stream);
        clip.start();
    }

    private static AudioInputStream createReusableAudioInputStream(File file) 
        throws IOException, UnsupportedAudioFileException
    {
        AudioInputStream ais = null;
        try
        {
            ais = AudioSystem.getAudioInputStream(file);
            byte[] buffer = new byte[1024 * 32];
            int read = 0;
            ByteArrayOutputStream baos = 
                new ByteArrayOutputStream(buffer.length);
            while ((read = ais.read(buffer, 0, buffer.length)) != -1)
            {
                baos.write(buffer, 0, read);
            }
            AudioInputStream reusableAis = 
                new AudioInputStream(
                        new ByteArrayInputStream(baos.toByteArray()),
                        ais.getFormat(),
                        AudioSystem.NOT_SPECIFIED);
            return reusableAis;
        }
        finally
        {
            if (ais != null)
            {
                ais.close();
            }
        }
    }
}
于 2012-04-03T19:59:14.947 回答
7

诀窍是将剪辑的当前位置重置为开始。

clip.stop();
clip.setMicrosecondPosition(0);
clip.start();

使用这种方法,不需要多次加载相同的资源。剪辑应该只加载一次并存储为实例变量或其他东西。

于 2016-02-07T21:57:42.970 回答
3

在您的代码示例中,使用 Clip,您应该使用 stop() 方法而不是 close() 方法。然后,当您重新启动时,它将从上次中断的地方继续。如果你想从头开始重新启动,那么你可以使用 setMicrosecondPosition() 或 setFramePosition() 为 0,然后使用 start()。

有关详细信息,请参阅以下教程中的“使用剪辑”!

http://docs.oracle.com/javase/tutorial/sound/playing.html

SourceDataLine 只能使用一次,不能重置。是的?

于 2012-04-04T20:16:14.503 回答
1

我在 OS X 上遇到了类似的问题,如果您在播放时尝试停止剪辑,然后从头开始重新启动,有时剪辑不会播放。它是通过立即调用来修复flush()stop()

if(clip.isActive() || clip.isRunning()) {
    clip.stop();
    clip.flush();
}

绝对不要使用close()stop()如果您希望能够再次播放您的剪辑,请使用。

于 2014-10-21T23:29:28.913 回答