DoClick не отпускает ключи, пока не закончится цикл - PullRequest
1 голос
/ 26 марта 2012

Я делаю приложение для фортепиано на Java.Это одна из функций,

public void playOnce(int time) {
    play();
    doClick(time);
    stop();
}

public void play() {
    channel[0].noteOn(note, 60);
}

public void stop() {
    channel[0].noteOff(note);
}

Я приведу минимальный рабочий пример, если необходимо, но я хотел убедиться, что это не очевидная проблема.Проблема в том, что playOnce вызывается в цикле while.playOnce находится в классе Key, и каждый Key имеет отдельную заметку.В каждой итерации цикла while playOnce вызывается для другого ключа.Как только все клавиши будут воспроизведены, он останавливается.

Метод doClick правильно нажал клавишу, но он не отпускается до тех пор, пока все клавиши не будут воспроизведены.На самом деле, во время игры на клавишах вы ничего не можете сделать, даже нажмите кнопку паузы.Для этой проблемы, я думаю, я мог бы поместить весь цикл в другой поток, но я не думаю, что этот тип решения позволит освободить ключ.

РЕДАКТИРОВАТЬ: Да, я понял, что мне нужноновый поток, чтобы заставить другие действия работать, но мне все еще нужно исправить для doClick ().Это может быть сложнее, чем я думал, вот рабочий пример:

Main.java

import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.text.DecimalFormat;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.SpinnerNumberModel;

public class Main implements ActionListener {

    final int WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT, BLACK_KEY_WIDTH,
            BLACK_KEY_HEIGHT;
    final int WIDTH;
    final JFileChooser fc;
    {
        WHITE_KEY_WIDTH = Key.WHITE_KEY_WIDTH;
        BLACK_KEY_WIDTH = Key.BLACK_KEY_WIDTH;
        WHITE_KEY_HEIGHT = Key.WHITE_KEY_HEIGHT;
        BLACK_KEY_HEIGHT = Key.BLACK_KEY_HEIGHT;
        WIDTH = 3 * (WHITE_KEY_WIDTH * 7) + WHITE_KEY_WIDTH;
        fc = new JFileChooser();
    }

    public static Key keys[] = new Key[48];
    private static int index = 0;
    private String prevText = "";

    JTextArea shabadEditor = null;
    JSpinner tempoControl;
    JFrame frame;
    File curFile;

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

    public Main() {
        frame = new JFrame();

        JPanel mainPanel = new JPanel();
        JPanel controlPanel = new JPanel();
        JLayeredPane pianoPanel = new JLayeredPane();

        mainPanel.setLayout(new GridBagLayout());

        JButton playButton = new JButton("Play");
        JButton pauseButton = new JButton("Pause");

        playButton.addActionListener(this);
        playButton.setActionCommand("play");

        pauseButton.addActionListener(this);
        pauseButton.setActionCommand("pause");

        SpinnerNumberModel model = new SpinnerNumberModel(1, 0, 2, .1);
        tempoControl = new JSpinner(model);
        JSpinner.NumberEditor editor = (JSpinner.NumberEditor) tempoControl
                .getEditor();
        DecimalFormat format = editor.getFormat();
        format.setMinimumFractionDigits(1);
        Dimension d = tempoControl.getPreferredSize();
        d.width = 40;
        tempoControl.setPreferredSize(d);

        GridBagConstraints c = new GridBagConstraints();
        // Construct each top level component
        controlPanel.add(playButton);
        controlPanel.add(pauseButton);
        controlPanel.add(tempoControl);
        shabadEditor = new JTextArea(20, 78);
        constructKeyboard(pianoPanel);

        // Add the piano panel and shabad editor to the window
        c.gridx = 0;
        c.gridy = 0;
        c.weightx = 1.0;
        c.anchor = GridBagConstraints.NORTHWEST;
        mainPanel.add(controlPanel, c);

        c.gridx = 0;
        c.gridy = 1;
        c.weightx = 1.0;
        // c.weighty = 1.0;
        c.anchor = GridBagConstraints.NORTHWEST;
        pianoPanel
                .setPreferredSize(new Dimension(WIDTH - 18, WHITE_KEY_HEIGHT));
        mainPanel.add(pianoPanel, c);

        c.gridx = 0;
        c.gridy = 2;
        c.weightx = 1.0;
        c.weighty = 1.0;
        c.anchor = GridBagConstraints.NORTHWEST;
        mainPanel.add(shabadEditor, c);
        frame.add(mainPanel);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(WIDTH, WHITE_KEY_HEIGHT * 3 + 30);
        frame.setLocation(250, 60);
        frame.setVisible(true);
    }

    void constructKeyboard(Container panel) {
        int i = 0;
        int j = 0;

        for (int k = 0; k < 3; k++) {
            addWhiteKey(panel, i++);
            addBlackKey(panel, j++);
            addWhiteKey(panel, i++);
            addBlackKey(panel, j++);
            addWhiteKey(panel, i++);
            addWhiteKey(panel, i++);
            j++;
            addBlackKey(panel, j++);
            addWhiteKey(panel, i++);
            addBlackKey(panel, j++);
            addWhiteKey(panel, i++);
            addBlackKey(panel, j++);
            j++;
            addWhiteKey(panel, i++);
        }
    }

    void addWhiteKey(Container panel, int i) {
        WhiteKey b = new WhiteKey();
        b.setLocation(i++ * WHITE_KEY_WIDTH, 0);
        panel.add(b, 0, -1);
        keys[index++] = b;
    }

    void addBlackKey(Container panel, int factor) {
        BlackKey b = new BlackKey();
        b.setLocation(WHITE_KEY_WIDTH - BLACK_KEY_WIDTH / 2 + factor
                * WHITE_KEY_WIDTH, 0);
        panel.add(b, 1, -1);
        keys[index++] = b;
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        String action = arg0.getActionCommand();

        if (action.equals("play")) {
            System.out.println("working");
            for (int i = 0; i < 10; i++) {
                keys[i].playOnce(500);
            }
        }
    }
}

Key.java

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.sound.midi.MidiChannel;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Synthesizer;
import javax.swing.JButton;

public class Key extends JButton implements MouseListener {

    private static final long serialVersionUID = 1L;

    public static final int WHITE_KEY_HEIGHT = 200;
    public static final int WHITE_KEY_WIDTH = 40;
    public static final int BLACK_KEY_WIDTH = 20;
    public static final int BLACK_KEY_HEIGHT = 120;

    private static int noteCount = 40;
    public int note;

    private static Synthesizer synth = null;

    static {
        try {
            synth = MidiSystem.getSynthesizer();
            synth.open();
        } catch (MidiUnavailableException e) {
            e.printStackTrace();
        }
    }
    MidiChannel channel[];

    public Key() {
        note = noteCount++;

        // Instrument[] instruments = synth.getAvailableInstruments();
        // for (Instrument instrument : instruments) {
        // System.out.println(instrument.getName());
        // System.out.println(instrument.getPatch().getBank());
        // System.out.println(instrument.getPatch().getProgram());
        // }

        channel = synth.getChannels();
        channel[0].programChange(20);
        addMouseListener(this);
    }

    public void playOnce(int time) {
        play();
        doClick(time);
        stop();
    }

    public void play() {
        channel[0].noteOn(note, 60);
    }

    public void stop() {
        channel[0].noteOff(note);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.println(this.note);
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mouseExited(MouseEvent e) {
        // TODO Auto-generated method stub

    }

    @Override
    public void mousePressed(MouseEvent e) {
        play();

    }

    @Override
    public void mouseReleased(MouseEvent e) {
        stop();
    }

}

BlackKey.java

import java.awt.Color;

public class BlackKey extends Key {

    private static final long serialVersionUID = 1L;

    public BlackKey() {
        super();
        setBackground(Color.BLACK);
        setSize(BLACK_KEY_WIDTH, BLACK_KEY_HEIGHT);
    }
}

WhiteKey.java

import java.awt.Color;

public class WhiteKey extends Key {

    private static final long serialVersionUID = 1L;

    public WhiteKey() {
        super();
        setBackground(Color.WHITE);
        setSize(WHITE_KEY_WIDTH, WHITE_KEY_HEIGHT);
    }

}

РЕДАКТИРОВАТЬ: После небольшой работы с потоками, это то, что у меня есть

Поместив цикл for в другой поток,Клавиши отпускаются в нужное время:

@Override
    public void actionPerformed(ActionEvent arg0) {
        String action = arg0.getActionCommand();

        if (action.equals("play")) {
            System.out.println("working");
            new Thread(new Runnable() {
                public void run() {
                    for (int i = 0; i < 20; i++) {
                        keys[i].playOnce(100);
                    }
                }
            }).start();
        }
    }
}

Теперь проблема в том, что клавиатура глючит.Клавиатура создается с использованием многоуровневой панели, и по какой-то причине, когда клавиши отпущены, слои, которые должны быть внизу, становятся верхними.Когда я наводю на них мышь, глюк исчезает.Есть идеи?

EDIT2: я исправил глюки.Мне просто нужно было добавить

     try {
        Thread.sleep(10);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

после doClick ();

1 Ответ

1 голос
/ 26 марта 2012

Проблема вашего подхода в том, что вы блокируете поток событий. Эта ветка отвечает за пользовательский ввод, рисование и обновление окон. Теперь я предполагаю, что тайм-аут doClick проверяется внутри потока событий (кажется логичным), поэтому он не будет освобожден до тех пор, пока не завершится ваш метод actionPerformed (и, таким образом, поток событий может продолжить обработку своего события).

Решением этой проблемы было бы (как вы уже упоминали) переместить ваш цикл for в другой поток и вызвать doClick, используя SwingUtilities.invokeLater .

...