2017-03-08 69 views
1

我有一個簡單的使用JavaFX的合成器程序。我正在嘗試創建一個名爲Metronome的新類來與MainController類進行交互。我需要節拍器在它自己的線程上運行,但仍然能夠運行MainController中的方法,特別是在每個節拍上。例如,打開節拍器時,需要(在自己的線程上)設置形狀的填充顏色,並通過MainController中的方法發出聲音。它會不斷改變顏色(關閉和開啓),並在延遲循環中發出聲音,直到MainController中的方法停止循環。我如何讓Metronome類與我的MainController進行通信,然後讓它在自己的線程上運行?Java:如何使線程與MainController分開的類溝通

編輯:所以我基本上需要我在節拍器類run()方法能夠在MainController類中運行方法。

public class MainController implements Initializable { 

    public AudioMain audio = new AudioMain(); 
    @ FXML public AnchorPane mainPane; 

    public boolean debugMessages = true; 
    public boolean debugMessages2 = false; 
    public boolean debugMessages3 = false; 

    //public final int numKeys = 13; 
    public int C4 = 60; //The midi pitch of C4 
    public int octave = 4; //The default octave to be assigned 

    public String synthType = "Saw"; 
    public SynthSet synth = new SynthSet(111, synthType); //Creates a new SynthSet of 13 SineWaves 

    public double bpm = 120; 
    public Metronome metronome = new Metronome(bpm); 

    ....some code later.... 

    public void toggleMetronome() { 
     metronome.toggleMet(); 
    } 

    public void lightOn() { 
     metronomeLight.setFill(lightOnColor); 
    } 

    public void lightOff() { 
     metronomeLight.setFill(lightOffColor); 
    } 

然後..

public class Metronome implements Runnable{ 

    public boolean metronomeOn = false; 
    public boolean metronomeSound = true; 
    public boolean metOutputMessages = true; 
    public boolean tick8th = false; 
    public double bpm = 20; 
    public long msPerBeat = (long) (60000/bpm); // Miliseconds per beat 
    public int tickCount = 0; 
    public long nano; 

    public Metronome() { 

    } 

    public Metronome(double beat) { 
     setTempo(beat); 
    } 

    public static void main(String args[]) { 
     Metronome met = new Metronome(); 
     met.metOn(); 
    } 

    @Override 
    public void run() { 
     System.out.println(msPerBeat); 
     while (metronomeOn) { 
      beat(); 
      delay(msPerBeat/2); 
      if (tick8th) beat8th(); 
      delay(msPerBeat/2); 
     } 
    } 

    public void metOn() { 
     if (!metronomeOn) { 
      outMessage("Starting metronome at " + bpm + " bpm"); 
      metronomeOn = true; 
      new Thread(this).start(); 
     } 
    } 

    public void metOff() { 
     if (metronomeOn) { 
      outMessage("Stopping metronome"); 
      metronomeOn = false; 
     } 
    } 
    public void toggleMet() { 
     if (metronomeOn) { 
      metOff(); 
     }else if (!metronomeOn) 
      metOn(); 
    } 

    public void beat() { 
     tickCount++; 
     outMessage("Beep " + tickCount); 
    } 
} 
+0

你通常會做這種使用條件變量或事件。就目前而言,你的問題有點太寬泛,無法在這裏回答。 –

+0

你能分享一段代碼嗎?你的問題太籠統了。這真的很難回答。 – nono

+1

您可能會考慮使用[時間軸](http://docs.oracle.com/javase/8/javafx/visual-effects-tutorial/basics.htm#BEIIDFJC)來控制節拍器,而不是單獨的線程。您可以在時間軸中使用[KeyValue](https://docs.oracle.com/javase/8/javafx/api/javafx/animation/KeyValue.html),並使用更改偵聽器在值更改時執行操作。 – jewelsea

回答

1

樣品a Timeline基於節拍器一些可視控件和節拍器的拍定時指示符。

對不起,它的一堆代碼。通過對每個概念沒有單獨的類,並且只是內聯一切,你可以使它更簡潔,但是一旦事情開始變得有點不平凡(比如在這種情況下),我發現更好地定義單獨的對象。

節拍器類通過observable屬性產生節拍,其他類使用監聽器對節拍做出反應。

be still my beating heart

import javafx.animation.*; 
import javafx.application.Application; 
import javafx.beans.binding.Bindings; 
import javafx.beans.property.*; 
import javafx.beans.value.ChangeListener; 
import javafx.geometry.*; 
import javafx.scene.Scene; 
import javafx.scene.control.*; 
import javafx.scene.effect.*; 
import javafx.scene.layout.*; 
import javafx.scene.media.AudioClip; 
import javafx.scene.paint.Color; 
import javafx.scene.shape.Circle; 
import javafx.stage.Stage; 
import javafx.util.Duration; 

public class MetroGnome extends Application { 
    public void start(Stage stage) { 
     Metronome metronome = new Metronome(); 
     TempoControl tempoControl = new TempoControl(metronome); 
     BeatIndicator beatIndicator = new BeatIndicator(metronome); 
     PlayControl playControl = new PlayControl(metronome); 

     HBox layout = new HBox(10, playControl, tempoControl, beatIndicator); 
     layout.setAlignment(Pos.CENTER); 
     layout.setPadding(new Insets(10)); 

     stage.setScene(new Scene(layout)); 
     stage.show(); 
    } 

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

class PlayControl extends ToggleButton { 
    public PlayControl(Metronome metronome) { 
     super("Start"); 

     setOnAction(event -> { 
      if (isSelected()) { 
       metronome.start(); 
       setText("Stop"); 
      } else { 
       metronome.stop(); 
       setText("Start"); 
      } 
     }); 
    } 
} 

class TempoControl extends VBox { 
    private static final int MIN_TEMPO = 20; 
    private static final int MAX_TEMPO = 240; 
    private static final int DEFAULT_TEMPO = 120; 

    private Slider tempoSlider = new Slider(MIN_TEMPO, MAX_TEMPO, DEFAULT_TEMPO); 
    private Label tempoLabel = new Label(tempoSlider.getValue() + ""); 

    public TempoControl(Metronome metronome) { 
     super(5); 

     tempoLabel.textProperty().bind(Bindings.format("%.0f", tempoSlider.valueProperty())); 
     setAlignment(Pos.CENTER); 
     getChildren().setAll(tempoLabel, tempoSlider); 

     metronome.setTempo(tempoSlider.getValue()); 
     metronome.tempoProperty().bind(tempoSlider.valueProperty()); 
    } 

    public DoubleProperty valueProperty() { 
     return tempoSlider.valueProperty(); 
    } 
} 

class BeatIndicator extends Circle { 
    // Ting sound from: http://soundbible.com/1628-Ting.html 
    private static final String TING_SOUND = "Ting-Popup_Pixels-349896185.wav"; 

    private static AudioClip ting = new AudioClip(
      BeatIndicator.class.getResource(TING_SOUND).toExternalForm() 
    ); 

    public BeatIndicator(Metronome metronome) { 
     super(10, Color.RED); 
     ChangeListener<Beat> beatChangeListener = (observable, oldValue, newValue) -> { 
      ting.play(); 
      setFill(newValue.getTickTock() == 0 ? Color.GREEN : Color.ORANGE); 
     }; 

     DropShadow dropShadow = new DropShadow(5, (Color) getFill()); 
     fillProperty().addListener((observable, oldValue, newValue) -> 
       dropShadow.setColor((Color) newValue) 
     ); 

     Glow beatEffect = new Glow(); 
     beatEffect.setInput(dropShadow); 

     metronome.isRunningProperty().addListener((observable, oldValue, newValue) -> { 
      if (newValue) { 
       setFill(Color.GREEN); 
       setEffect(beatEffect); 
       metronome.beatProperty().addListener(beatChangeListener); 
      } else { 
       metronome.beatProperty().removeListener(beatChangeListener); 
       setFill(Color.RED); 
       setEffect(null); 
      } 
     }); 
    } 
} 

class Metronome { 
    private final double DEFAULT_TEMPO = 60; 
    private ReadOnlyObjectWrapper<Beat> beat = new ReadOnlyObjectWrapper<>(null); 

    private Timeline timeline = new Timeline(); 

    // tempo is measured in beats per minute. 
    private DoubleProperty tempo = new SimpleDoubleProperty(DEFAULT_TEMPO); 
    private ReadOnlyBooleanWrapper isRunning = new ReadOnlyBooleanWrapper(false); 

    private int tickTock = 0; 

    public Metronome() { 
     timeline.getKeyFrames().addAll(
       new KeyFrame(Duration.seconds(0), event -> { 
        beat.set(new Beat(tickTock, timeline.getCurrentTime())); 
        tickTock = (tickTock + 1) % 2; 
       }), 
       new KeyFrame(
         Duration.seconds(1) 
       ) 
     ); 

     tempo.addListener((observable, oldValue, newValue) -> 
       timeline.setRate(newValue.doubleValue()/60.0) 
     ); 
     timeline.setRate(tempo.getValue()/60.0); 
     timeline.setCycleCount(Timeline.INDEFINITE); 
    } 

    public void start() { 
     tickTock = 0; 
     isRunning.set(true); 
     timeline.playFromStart(); 
    } 

    public void stop() { 
     timeline.stop(); 
     isRunning.set(false); 
    } 

    public double getTempo() { 
     return tempo.get(); 
    } 

    public DoubleProperty tempoProperty() { 
     return tempo; 
    } 

    public void setTempo(double tempo) { 
     this.tempo.set(tempo); 
    } 

    public ReadOnlyObjectProperty<Beat> beatProperty() { 
     return beat.getReadOnlyProperty(); 
    } 

    public ReadOnlyBooleanProperty isRunningProperty() { 
     return isRunning.getReadOnlyProperty(); 
    } 
} 

class Beat { 
    private final Duration currentTime; 
    // tickTock varies switches from one to zero on alternate generated beats. 
    private final int tickTock; 

    public Beat(int tickTock, Duration currentTime) { 
     this.currentTime = currentTime; 
     this.tickTock = tickTock; 
    } 

    public int getTickTock() { 
     return tickTock; 
    } 

    public Duration getCurrentTime() { 
     return currentTime; 
    } 
}