Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: qt/QTCycle.java


1   /*
2   
3   < This Java Class is part of the jMusic API Version 2001.01>
4   
5   Copyright (C) 2000 Andrew Sorensen & Andrew Brown
6   
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or any
10  later version.
11  
12  This program is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16  
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  
21  */ 
22  package qt;
23  
24  import jm.JMC;
25  import jm.music.data.*;
26  import qt.QTUtil;
27  import jm.util.View;
28  import jm.music.tools.Mod;
29  import javax.swing.*;
30  import javax.swing.event.*;
31  import java.awt.event.*;
32  
33  /**
34  * QuickTime Real Time playback for jMusic.
35  * Score 1 is used first then score 2 and repeat infinitly.
36  * While one score is playing the other score is updating.
37  * Any class calling QTRT must implement two methods,
38  * updateScore1() and updateScore2()
39  * It is the responsibility of the calling app to
40  * update the scores as required. Syncronisation of
41  * this can be assisted by polling the value of the
42  * score1PlayingFlag boolean variable.
43  * A swing GUI is provided to adjust the scheduling 
44  * parameters as required.
45  * The scoreLength argument is critical as it determines the
46  * size of the scores created when updateScore methods
47  * are called, which (of course) also changes the delay 
48  * between update calls. It can be changed using the
49  * setScoreLength() method.
50  * Remember to call the startPlayback() method (only once) or else
51  * nothing will happen ;) Use suspendPlayback() and
52  * resumePlayback() to interupt the playback once started.
53  *
54  * @param Score the first score object
55  * @param Score the first score object
56  * @param Object the class the calls this class
57  *
58  * @author Andrew Brown
59  */
60  
61  public final class QTCycle implements JMC, ChangeListener, ActionListener{
62    private Thread myThread;
63    private QTUtil qtu1; 
64    private Object obj;
65    private Score score = new Score();
66    private double tempo = 120.0; // bpm
67    private double nextTempo = 0.0; // bpm, 0.0 == off
68    private double scoreLength = CROTCHET;
69    // non accessable attributes
70    private double time;
71    private double waitTime;
72    private double sleepTime = 480.0;
73    private double theGap = 10.0;
74    private JSlider tempoSlider, sleepSlider, gapSlider;
75    private JLabel tempoNumber, sleepNumber, gapNumber;
76    private JButton startButton, stopButton;
77    private boolean newScoreFlag = true;
78    private int cycleCount = 0;
79    
80    public QTCycle(Score score) {
81          this.obj = obj;
82          this.score = score;
83          this.scoreLength = score.getEndTime();
84      qtu1 = new QTUtil(this.score); 
85          
86          // Thread Stuff - inner class
87          myThread = new Thread(new Runnable() {
88              public void run() { 
89                  while (true) {
90                      playCycle();
91                      try { Thread.sleep((int)(sleepTime * scoreLength * 60.0/tempo)); 
92                      }
93                    catch( InterruptedException e) {}
94                    // sleep a bit to give the GC and other threads a chance to act
95                      if (!newScoreFlag) System.gc(); // if (tuneFlag) if tempi < 130 /
96                }
97            }
98          } );
99          
100         myThread.setPriority(Thread.MAX_PRIORITY);   
101   }
102   
103   /**
104   * Begin the cycling playback of the scores.
105   */
106   public void startPlayback() {
107       myThread.start();
108       time = System.currentTimeMillis();
109   }
110   
111   /**
112   * Shedule score playback na d score updating when required.
113   */
114   private void playCycle() {
115       waitTime = time + (1000.0 * scoreLength * 60.0/tempo) - theGap;
116       
117       if (newScoreFlag) {
118           //double ts = System.currentTimeMillis();
119           qtu1.setScore(score);
120           newScoreFlag = false;
121           //double te = System.currentTimeMillis();
122           //System.out.println("Time taken to set score was " + (te - ts) + " milliseconds");
123       }
124       while (System.currentTimeMillis() < waitTime) {};
125       //double t1 = System.currentTimeMillis();
126       updateTempo();
127       qtu1.setSpeed(tempo/60.0);
128       qtu1.replay();
129       time = System.currentTimeMillis();
130       cycleCount ++;
131       //System.out.println("Time taken by QTU was " + (time - t1) + " milliseconds");
132   }
133   
134   /**
135   * Specifies the length of a score to be played.
136   * Changes the interval between score update calls.
137   */
138   public void setScoreLength(double newLength) {
139       if (newLength > 0.0) scoreLength = newLength;
140   }
141   
142   /**
143   * Reports the current score length setting.
144   */
145   public double getScoreLength() {
146       return scoreLength;
147   }
148   
149   /**
150   * Specifies the  score to be played.
151   */
152   public void setScore(Score s) {
153       this.score = s;
154       scoreLength = s.getEndTime();
155       newScoreFlag = true;
156   }
157   
158   /**
159   * Specifies the speed in beats per minute.
160   */
161   public void setTempo(double newTempo) {
162       if (newTempo > 0.0) tempo = newTempo;
163   }
164   
165   /**
166   * Reports the current speed in beats per minute.
167   */
168   public double getTempo() {
169       return tempo;
170   }
171   
172   /**
173   * Specifies the speed in beats per minute.
174   */
175   public void setNextTempo(double newTempo) {
176       if (newTempo > 0.0) nextTempo = newTempo;
177   }
178   /**
179   * Updates the current speed in beats per minute
180   * based on a waiting tempo change.
181   * This prevents gaps or overlaps in the playback
182   * which can occur from the use of setTempo()
183   * which updates immediatly.
184   */
185   public void updateTempo() {
186       if (nextTempo != 0.0) {
187           tempo = nextTempo;
188           nextTempo = 0.0;
189       }
190   }
191   
192   /**
193   * Specify the number ot count cycles from.
194   */
195   public void setCycleCount(int newCount) {
196       this.cycleCount = newCount;
197   }
198   
199   /**
200   * Report the number of times the cycle method has been run.
201   * Useful for counting beats , bars, phrases or 
202   * whatever the chunk passed to the QTRT.
203   */
204   public int getCycleCount() {
205       return this.cycleCount;
206   }
207   
208   /**
209   * If paused, restart playback.
210   */
211   public void resumePlayback() {
212       myThread.resume();
213   }
214   
215   /**
216   * Pause playback.
217   */
218   public void suspendPlayback() {
219       myThread.suspend();
220       qtu1.stopPlayback();
221   }
222   
223   /**
224   * Update the sleep time value..
225   */
226   public void setSleepTime( double newSleepTime) {
227       this.sleepTime = newSleepTime;
228   }
229   
230   
231   /**
232   * Report the current loop gap time.
233   */
234   public double getSleepTime() {
235       return this.sleepTime;
236   }
237   
238   /**
239   * Update the sleep time value..
240   */
241   public void setTheGap( double newGap) {
242       this.theGap = newGap;
243   }
244   
245   
246   /**
247   * Report the current loop gap time.
248   */
249   public double getTheGap() {
250       return this.theGap;
251   }
252   
253   /**
254   * Open a GUI with sliders to adjust the scheduler loading.
255   * Used to optimise the timing and efficiecy of the real time
256   * QuickTime playback.
257   */
258   public void settings() {
259     JFrame f = new JFrame("Thread GUI");
260     f.setSize(400, 200);
261     Box masterBox = new Box(0);
262     // tempo
263     Box b1 = new Box(1);
264     JLabel tempoTitle = new JLabel("Tempo");
265     b1.add(tempoTitle);
266     tempoSlider = new JSlider(1, 20, 200, 120);
267     tempoSlider.addChangeListener(this);
268     b1.add(tempoSlider);
269     tempoNumber = new JLabel("120");
270     b1.add(tempoNumber);
271     masterBox.add(b1);
272     
273     // sleep time
274     Box b2 = new Box(1);
275     JLabel sleepTitle = new JLabel("Sleep");
276     b2.add(sleepTitle);
277     sleepSlider = new JSlider(1, 0, 1200, (int)(sleepTime)); // 570 G4, 300 G3
278     sleepSlider.addChangeListener(this);
279     b2.add(sleepSlider);
280     sleepNumber = new JLabel(Double.toString(sleepTime));
281     b2.add(sleepNumber);
282     masterBox.add(b2);
283     
284     // gap time
285     Box b3 = new Box(1);
286     JLabel gapTitle = new JLabel("Gap");
287     b3.add(gapTitle);
288     gapSlider = new JSlider(1, 0, 100, (int)(theGap));
289     gapSlider.addChangeListener(this);
290     b3.add(gapSlider);
291     gapNumber = new JLabel(Double.toString(theGap));
292     b3.add(gapNumber);
293     masterBox.add(b3);
294     
295     // buttons
296     Box b4 = new Box(1);
297     startButton = new JButton("Start");
298     startButton.addActionListener(this);
299     b4.add(startButton);
300     stopButton = new JButton("Stop");
301     stopButton.addActionListener(this);
302     b4.add(stopButton);
303     masterBox.add(b4);
304     
305     f.getContentPane().add(masterBox);
306     f.setVisible(true);
307   }
308   
309   public void stateChanged(ChangeEvent e) {
310     if(e.getSource() == tempoSlider) changeTempo();
311     if(e.getSource() == sleepSlider) changeSleep();
312     if(e.getSource() == gapSlider) changeGap();
313   }
314   
315   private void changeTempo() {
316     tempo = (double)(tempoSlider.getValue());
317     tempoNumber.setText(Integer.toString(tempoSlider.getValue()));
318   }
319   
320   private void changeSleep() {
321     sleepTime = (double)(sleepSlider.getValue());
322     sleepNumber.setText(Integer.toString(sleepSlider.getValue()));
323   }
324   
325   private void changeGap() {
326     theGap = (double)(gapSlider.getValue());
327     gapNumber.setText(Integer.toString(gapSlider.getValue()));
328   }
329   
330   public void actionPerformed(ActionEvent ae) {
331       if(ae.getSource() == startButton) myThread.resume();
332       if(ae.getSource() == stopButton) myThread.suspend();
333   }
334 }