001    /* Sequence.java -- A sequence of MIDI events
002       Copyright (C) 2005 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.sound.midi;
040    
041    import java.util.Iterator;
042    import java.util.Vector;
043    
044    /**
045     * Objects of this type represent sequences of MIDI messages that can be 
046     * played back by a Sequencer.
047     * 
048     * @author Anthony Green (green@redhat.com)
049     * @since 1.3
050     *
051     */
052    public class Sequence
053    {
054      /**
055       * The timing division type for this sequence (PPQ or SMPTE*) 
056       */
057      protected float divisionType;
058      
059      /**
060       * The timing resolution in ticks/beat or ticks/frame, depending on the 
061       * division type.  
062       */
063      protected int resolution;
064      
065      /**
066       * The MIDI tracks used by this sequence. 
067       */
068      protected Vector<Track> tracks;
069      
070      /**
071       * Tempo-based timing.  Resolution is specified in ticks per beat.
072       */
073      public static final float PPQ = 0.0f;
074      
075      /**
076       * 24 frames/second timing.  Resolution is specific in ticks per frame.
077       */
078      public static final float SMPTE_24 = 24.0f;
079      
080      /**
081       * 25 frames/second timing.  Resolution is specific in ticks per frame.
082       */
083      public static final float SMPTE_25 = 25.0f;
084      
085      /**
086       * 30 frames/second timing.  Resolution is specific in ticks per frame.
087       */
088      public static final float SMPTE_30 = 30.0f;
089      
090      /**
091       * 29.97 frames/second timing.  Resolution is specific in ticks per frame.
092       */
093      public static final float SMPTE_30DROP = 29.97f;
094      
095      // Private helper class
096      private void init(float divisionType, int resolution, int numTracks)
097        throws InvalidMidiDataException
098      {
099        if (divisionType != PPQ
100            && divisionType != SMPTE_24 
101            && divisionType != SMPTE_25
102            && divisionType != SMPTE_30
103            && divisionType != SMPTE_30DROP)
104          throw new InvalidMidiDataException("Invalid division type (" 
105                                             + divisionType + ")");
106    
107        this.divisionType = divisionType;
108        this.resolution = resolution;
109        
110        tracks = new Vector<Track>(numTracks);
111        while (numTracks > 0)
112          tracks.set(--numTracks, new Track());
113      }
114      
115      /**
116       * Create a MIDI sequence object with no initial tracks.
117       * 
118       * @param divisionType the division type (must be one of PPQ or SMPTE_*)
119       * @param resolution the timing resolution
120       * @throws InvalidMidiDataException if the division type is invalid
121       */
122      public Sequence(float divisionType, int resolution)
123        throws InvalidMidiDataException
124      {
125        init(divisionType, resolution, 0);
126      }
127    
128      /**
129       * Create a MIDI seqence object.
130       * 
131       * @param divisionType the division type (must be one of PPQ or SMPTE_*)
132       * @param resolution the timing resolution
133       * @param numTracks the number of initial tracks
134       * @throws InvalidMidiDataException if the division type is invalid
135       */
136      public Sequence(float divisionType, int resolution, int numTracks)
137        throws InvalidMidiDataException
138      {
139        init(divisionType, resolution, 0);
140      }
141      
142      /**
143       * The division type of this sequence.
144       * 
145       * @return division type of this sequence
146       */
147      public float getDivisionType()
148      {
149        return divisionType;
150      }
151      
152      /**
153       * The timing resolution for this sequence, relative to the division type.
154       * 
155       * @return the timing resolution for this sequence
156       */
157      public int getResolution()
158      {
159        return resolution;
160      }
161      
162      /**
163       * Create a new empty MIDI track and add it to this sequence.
164       * 
165       * @return the newly create MIDI track
166       */
167      public Track createTrack()
168      {
169        Track track = new Track();
170        tracks.add(track);
171        return track;
172      }
173      
174      /**
175       * Remove the specified MIDI track from this sequence.
176       * 
177       * @param track the track to remove
178       * @return true if track was removed and false othewise
179       */
180      public boolean deleteTrack(Track track)
181      {
182        return tracks.remove(track);
183      }
184      
185      /**
186       * Get an array of MIDI tracks used in this sequence.
187       * 
188       * @return a possibly empty array of tracks
189       */
190      public Track[] getTracks()
191      {
192        return tracks.toArray(new Track[tracks.size()]);
193      }
194      
195      /**
196       * The length of this sequence in microseconds.
197       * 
198       * @return the length of this sequence in microseconds
199       */
200      public long getMicrosecondLength()
201      {
202        long tickLength = getTickLength();
203        
204        if (divisionType == PPQ)
205        {
206          // FIXME
207          // How can this possible be computed?  PPQ is pulses per quarter-note,
208          // which is dependent on the tempo of the Sequencer.
209          throw new 
210              UnsupportedOperationException("Can't compute PPQ based lengths yet");
211        }
212        else
213        {
214          // This is a fixed tick per frame computation
215          return (long) ((tickLength * 1000000) / (divisionType * resolution));
216        }
217      }
218      
219      /**
220       * The length of this sequence in MIDI ticks.
221       * 
222       * @return the length of this sequence in MIDI ticks
223       */
224      public long getTickLength()
225      {
226        long length = 0;
227        Iterator<Track> itr = tracks.iterator();
228        while (itr.hasNext())
229        {
230          Track track = itr.next();
231          long trackTicks = track.ticks();
232          if (trackTicks > length)
233            length = trackTicks;
234        }
235        return length;
236      }
237      
238      /**
239       * Get an array of patches used in this sequence.
240       * 
241       * @return an array of patches used in this sequence
242       */
243      public Patch[] getPatchList()
244      {
245        // FIXE: not quite sure how to do this yet. 
246        throw new UnsupportedOperationException("Can't get patch list yet");
247      }
248    }