操作在 Java 后停止工作

Actions stop working in Java

所以这是我的第一个 post 如果我的语法不好,我真的很抱歉(英语不是我的母语)。我最近在 java 开始编程,我对学习很感兴趣。所以我开始了一些小项目来帮助我了解更多基本知识并改进我的编码。

最近我阅读了有关 keyListeners、keyBindings 和所有相关内容的信息。所以我想我编写了一个非常基本的程序(几乎没有 gui),它应该像一架简单的钢琴一样工作:

import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import java.awt.*;
import java.io.File;
import javax.swing.*;

import java.awt.event.*;

public class pianl1 {


    public static void main (String[] args){
        int i= 0;

        File C7 = new File("res/39191__jobro__piano-ff-044.wav");
        File D7 = new File ("res/39194__jobro__piano-ff-046.wav");

        JLabel lab1 =new JLabel("Hallo");
        frame.add(lab1);


        AbstractAction keyBindingReactorC7 = new AbstractAction(){
            @Override
            public void actionPerformed(ActionEvent e){
                Playsound(C7);
            }
        };
        AbstractAction keyBindingReactorD7 = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Playsound(D7);
            }

        JPanel panel= new JPanel(new FlowLayout());
        frame.getContentPane().add(panel);
        frame.pack();

        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("C"),
                "cPressed");
        panel.getActionMap().put("cPressed", keyBindingReactorC7);

        panel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("D"),
                "dPressed");
        panel.getActionMap().put("dPressed", keyBindingReactorD7);


    public static void Playsound(File Sound){
        try{
            Clip clip = AudioSystem.getClip();
            clip.open(AudioSystem.getAudioInputStream(Sound));
            clip.start();

        }catch(Exception e){}

    }

}

它工作正常。如果我按下键,它会播放声音,即使我敲击键,声音也几乎没有延迟。这正是我想要的。

但是有个小错误

如果我一直按一个键,或者同时按两个键(有效,同时播放),有时会出现完全没有声音的情况。我无法做任何事情让它再次工作,我所能做的就是重新启动整个程序。这是随机发生的,我不知道为什么。我知道有什么东西阻碍了我的行动持续进行。

你的问题的答案围绕着你最终想要实现的目标,如果你只是想停止多个声音的播放,那么你需要某种你可以监控的条件,如果你想停止 "particular" 播放声音,然后您需要维护某种 List 可以检查的标志。

因此,根据您的代码,假设您正在制作钢琴,因此您希望声音重叠,在这种情况下,我们需要一些方法来确定是否播放了特定的声音。

有多种方法可以实现此目的,但让我们选择一个 Set,它允许您保持唯一的 List 个项目(无重复项)。

每次调用playSound时,我们检查这个列表并确定声音是否已经播放,如果没有,我们将声音添加到列表并播放,当声音停止,我们将其从列表中删除

核心...

我稍微修改了你的代码,稍后我会更详细地解释它,但本质上,这就是想法的 "core"...

private Set<File> playing = new HashSet<File>(25);

public void playsound(File sound) {
    try {
        // Is the been played or not?
        if (!playing.contains(sound)) {
            // And the sound to prevent it from been played again
            playing.add(sound);
            // Set up a new clip
            Clip clip = AudioSystem.getClip();
            // Monitor the clip's status, we want to know when it ends
            clip.addLineListener(new LineListener() {
                @Override
                public void update(LineEvent event) {
                    // Clip has stopped
                    if (event.getType() == LineEvent.Type.STOP) {
                        // Release the resources
                        clip.close();
                        // Remove the sound from our list
                        // so it can be played again
                        playing.remove(sound);
                    }
                }
            });
            // Play it again Sam
            clip.open(AudioSystem.getAudioInputStream(sound));
            clip.start();
        }
    } catch (Exception e) {
        // Remove the sound if something goes wrong
        playing.remove(sound);
        e.printStackTrace();
    }

}

可运行示例

好的,所以我已经 "updated" 你的代码了一点,这就是为什么......

  • Java 有一些既定的编码约定,鼓励所有开发人员遵循,它们使其他人更容易阅读您的代码,也让您更容易阅读他们的代码,请查看 Code Conventions for the Java TM Programming Language更多细节。话虽如此,我让你所有的变量都以小写字符开头
  • A 创建了自定义面板。好吧,这只是我,但它让生活变得更轻松,因为您可以更轻松地封装您试图实现的逻辑。因为您正在使用 Action API,另一个选择是创建一个 SoundManager class,其中包含管理和播放声音的逻辑并通过对每个 Action 的引用,就个人而言,我可能最终会得到两个
  • 我已经使用 EventQueue.invokeLater 来确保 UI 是在事件调度线程的上下文中创建的,从而减少了违反 Swing 单线程性质的任何可能风险,这总是一个好办法想法 ;)

你的尝试似乎有了一个良好的开端,干得好,再接再厉!

import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {

            File c7 = new File("res/res39191__jobro__piano-ff-044.wav");
            File d7 = new File("res/39194__jobro__piano-ff-046.wav");

            add(new JLabel("Hello"));

            AbstractAction keyBindingReactorC7 = new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    playsound(c7);
                }
            };
            AbstractAction keyBindingReactorD7 = new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    playsound(d7);
                }
            };
            getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("C"),
                                                                                                                             "cPressed");
            getActionMap().put("cPressed", keyBindingReactorC7);

            getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("D"),
                                                                                                                             "dPressed");
            getActionMap().put("dPressed", keyBindingReactorD7);
        }

        private Set<File> playing = new HashSet<File>(25);

        public void playsound(File sound) {
            try {
                if (!playing.contains(sound)) {
                    playing.add(sound);
                    Clip clip = AudioSystem.getClip();
                    clip.addLineListener(new LineListener() {
                        @Override
                        public void update(LineEvent event) {
                            if (event.getType() == LineEvent.Type.STOP) {
                                clip.close();
                                playing.remove(sound);
                            }
                        }
                    });
                    clip.open(AudioSystem.getAudioInputStream(sound));
                    clip.start();
                }
            } catch (Exception e) {
                playing.remove(sound);
                e.printStackTrace();
            }

        }
    }
}

好的,但是如何才能一次只能播放一种声音?

啊,好问题...

基本上,我们使用单个剪辑并检查它的状态...

private Clip clip;

public void playsound(File sound) {
    try {
        if (clip == null) {
            clip = AudioSystem.getClip();
            clip.addLineListener(new LineListener() {
                @Override
                public void update(LineEvent event) {
                    if (event.getType() == LineEvent.Type.STOP) {
                        clip.close();
                    }
                }
            });
        }
        // Is the clip active or running?
        if (!clip.isActive() && !clip.isRunning()) {
            if (clip.isOpen()) {
                clip.close();
            }
            clip.open(AudioSystem.getAudioInputStream(sound));
            clip.start();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

现在,我对 Java 的音频体验非常好 [=7​​1=],但有些事情我可能会考虑尝试...

  • 制作一个AudioManagerSoundManagerclass,它管理声音并提供一个简单的播放界面(如playCplayD) . Pre-load 每个声音的剪辑。这将需要一些额外的管理,因为当剪辑结束时,您将需要 "reset" 它回到开头。这 "should" 使播放剪辑的速度更快,但它可能会消耗更多资源,哪个权衡对您来说更重要?您还可以创建某种缓存,如果某个剪辑在一段时间内未播放,则会将其关闭并丢弃。
  • 继续播放声音直到松开一个键...对你来说是个不错的挑战 ;)

除了@MadProgrammer 的大力帮助外,我还注意到了一些其他的东西。我在 Mac 上工作,但我也拥有 Windows-machine。我最近刚切换到 Mac 并且我没有注意到(直到现在),如果你按住一个键足够长的时间,OSX 会自动显示更多 key-options(像û, c 等等)。 所以我将所有内容都切换到我的 Windows-machine 并且瞧,它起作用了。当然还有一些小错误,但最后我对结果很满意。