2017-02-11 4 views
0

Dies ist mein erster Beitrag und es tut mir wirklich leid, wenn meine Grammatik schlecht ist (Englisch ist nicht meine Muttersprache). Ich fing vor kurzem an, in Java zu programmieren, und ich bin am Lernen interessiert. Also habe ich ein paar kleine Projekte angefangen, um mir zu helfen, mehr von den grundlegenden Dingen zu verstehen und meine Programmierung zu verbessern.Aktionen funktionieren nicht mehr in Java

Kürzlich las ich über KeyListeners, keyBindings und all das Zeug. Also dachte ich Code, den ich ein sehr einfaches Programm (fast kein gui), die wie ein einfaches Klavier funktionieren sollte:

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){} 

    } 

} 

Und es funktioniert gut. Wenn ich die Tasten drücke, spielt es den Ton ab, und selbst wenn ich die Tasten hämmere, hat der Ton fast keine Verzögerung. Was genau ich wollte.

Aber es ist ein kleiner Fehler.

Wenn ich weiterhin eine Taste drücke, oder ich zwei Tasten gleichzeitig drücke (was funktioniert, spielt es beide gleichzeitig), kommt es manchmal vor, dass überhaupt kein Ton zu hören ist. Ich kann nichts tun, um es wieder zum Laufen zu bringen und alles, was ich tun kann, ist das ganze Programm neu zu starten. Dies geschieht zufällig und ich kann nicht herausfinden warum. Ich weiß, dass etwas meine Arbeit daran hindert, kontinuierlich zu arbeiten.

+0

Verwenden Sie clip.is active() oder clip.is running(), wenn es nichts tut! – Gacci

+0

@Gacci Da sie als lokale Variablen erstellt werden, wird es nicht funktionieren - gute Idee, wenn es stattdessen als Instanzfeld verwendet werden könnte – MadProgrammer

+3

Verwenden Sie keinen leeren catch-Block. Vielleicht wird ein Fehler generiert. – camickr

Antwort

2

Die Antwort auf Ihre Frage dreht sich um das, was Sie letztendlich erreichen wollen, wenn Sie einfach mehr als einen Sound stoppen wollen, dann brauchen Sie eine Art von Bedingung, die Sie überwachen können, wenn Sie ein "bestimmtes" stoppen wollen Sound vom Spielen, dann müssen Sie eine Art von List Flags, die Sie überprüfen können, pflegen.

Also nehmen wir an, dass Sie, basierend auf Ihrem Code, ein Piano machen, also wollen Sie, dass sich Sounds überlappen. In diesem Fall müssen wir herausfinden, ob ein bestimmter Sound gespielt wurde oder nicht.

Es gibt eine Reihe von Möglichkeiten, um dies zu erreichen, aber lassen Sie sich auf eine Set, die Sie einzigartige List von Artikeln (keine Duplikate) pflegen können.

Jedes Mal, wenn playSound aufgerufen wird, überprüfen wir diese Liste und bestimmen, ob die Sounds bereits gespielt wurden oder nicht, wenn nicht, fügen wir den Sound zur Liste hinzu und spielen ihn ab, wenn der Sound aufhört, entfernen wir es aus der Liste

im Kern ...

ich habe Ihren Code ein wenig verändert, und ich werde es später ausführlich erklären, aber im wesentlichen, das ist der „Kern“ der Idee. ..

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(); 
    } 

} 

Runnable Beispiel

Okay, ich habe so „aktualisiert“ Ihren Code ein wenig, hier ist, warum ...

  • Java hat einige etablierte Kodierungskonventionen alle Entwickler folgen werden ermutigt, sie machen es einfacher für andere Menschen zu lesen Ihr Code und für Sie, um ihre zu lesen, werfen Sie einen Blick auf Code Conventions for the Java TM Programming Language für weitere Details. Nachdem ich gesagt habe, habe ich alle Ihre Variablen mit einem Kleinbuchstaben beginnen
  • A erstellt ein benutzerdefiniertes Panel. Okay, das ist nur ich, aber es macht das Leben viel einfacher, da Sie die Logik, die Sie implementieren möchten, leichter einkapseln können.Da Sie die Action API verwenden, wäre eine andere Möglichkeit gewesen, eine SoundManager Klasse zu erstellen, die die Logik zum Verwalten und Abspielen der Sounds enthält und eine Referenz an jede Action übergibt, persönlich würde ich wahrscheinlich mit beiden enden
  • ich habe EventQueue.invokeLater verwendet, um sicherzustellen, dass die Benutzeroberfläche im Rahmen der Veranstaltung Dispatching Thema, wodurch jegliches Risiko der Verletzung die Single-Threaded Art von Swing, immer gut eine Idee geschaffen wird;)
Sie

scheinen einen guten Start zu haben, Dinge auszuprobieren, gut gemacht, weiter so!

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(); 
      } 

     } 
    } 
} 

Ok, aber wie mache ich es so, dass nur ein Ton gleichzeitig spielen kann?

Ah, gute Frage ...

Grundsätzlich verwenden wir einen einzelnen Clip und überprüfen Sie es Zustand ist ...

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(); 
    } 
} 

Nun, mein Hörerlebnis mit Java ziemlich inexistent ist, aber einige Dinge, die ich prüfen könnte versuchen ...

  • Machen sie eine AudioManager oder SoundManager-Klasse, die die Klänge verwaltet und bietet eine einfache Schnittstelle für sie zu spielen (wie playC, playD). Laden Sie die Clips für jeden Sound vor. Dies wird einige zusätzliche Verwaltung erfordern, denn wenn der Clip endet, müssen Sie es auf den Anfang "zurücksetzen". Dies "sollte" es schneller machen, die Clips abzuspielen, aber es wird wahrscheinlich mehr Ressourcen verbrauchen, was ist für Sie wichtiger? Sie können auch eine Art Cache erstellen, in dem, wenn ein Clip für eine bestimmte Zeit nicht abgespielt wird, er geschlossen und entsorgt wird.
  • Halten Sie einen Ton, bis ein Schlüssel zu spielen freigesetzt wird ... für Sie schöne Herausforderung;)
+0

(1+) Nice Beispiel. 'weil, wenn der Clip endet, müssen Sie es zurück zum Anfang" zurücksetzen ".'- Das kann erreicht werden, indem' clip.setFramePosition (0) 'vor' 'clip.start()' – camickr

+0

@camickr verwendet wird. Ja, ich dachte auch darüber nach, das Schließen schien damals einfacher: P, aber wenn du es würdest Verwenden Sie die Clips (arbeiten Sie in Richtung Geschwindigkeit), die setFramePosition würde trotzig der Weg sein! – MadProgrammer

+0

Danke! Das ist sehr hilfreich und ich weiß es zu schätzen, dass Sie sich so viel Mühe geben, mir zu helfen. –

0

Neben der großen Hilfe von @MadProgrammer, bemerkte ich auch noch etwas anderes. Ich arbeite auf dem Mac, aber ich besitze auch eine Windows-Maschine. Ich bin erst vor kurzem zu Mac gewechselt und ich habe nicht bemerkt (bis jetzt), dass, wenn Sie einen Schlüssel lang genug gedrückt halten, OSX automatisch mehr Schlüsseloptionen anzeigt (Dinge wie û, ć und all das). Also habe ich alles auf meine Windows-Maschine geschaltet und voilá, es funktioniert. Natürlich gibt es einige kleinere Fehler, aber am Ende bin ich ziemlich glücklich mit dem Ergebnis.

Verwandte Themen