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

Quick Search    Search Deep

Source code: jmqt/QTCycle.java


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