Source for gnu.javax.sound.midi.file.MidiFileWriter

   1: /* MidiFileWriter.java -- Write MIDI files.
   2:    Copyright (C) 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath 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, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath 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 GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package gnu.javax.sound.midi.file;
  39: 
  40: import java.io.File;
  41: import java.io.FileInputStream;
  42: import java.io.FileOutputStream;
  43: import java.io.IOException;
  44: import java.io.InputStream;
  45: import java.io.OutputStream;
  46: 
  47: import javax.sound.midi.MetaMessage;
  48: import javax.sound.midi.MidiEvent;
  49: import javax.sound.midi.Sequence;
  50: import javax.sound.midi.Track;
  51: 
  52: /**
  53:  * A MIDI file writer.
  54:  * 
  55:  * This code writes MIDI file types 0 and 1.  
  56:  *
  57:  * There are many decent documents on the web describing the MIDI file
  58:  * format.  I didn't bother looking for the official document.  If it
  59:  * exists, I'm not even sure if it is freely available.  We should
  60:  * update this comment if we find out anything helpful here.
  61:  *
  62:  * @author Anthony Green (green@redhat.com)
  63:  *
  64:  */
  65: public class MidiFileWriter
  66:     extends javax.sound.midi.spi.MidiFileWriter
  67: {
  68:   /* Return an array indicating which midi file types are supported.
  69:    * @see javax.sound.midi.spi.MidiFileWriter#getMidiFileTypes()
  70:    */
  71:   public int[] getMidiFileTypes()
  72:   {
  73:     return new int[]{0, 1};
  74:   }
  75: 
  76:   /* Return an array indicating which midi file types are supported
  77:    * for a given Sequence.
  78:    * @see javax.sound.midi.spi.MidiFileWriter#getMidiFileTypes(javax.sound.midi.Sequence)
  79:    */
  80:   public int[] getMidiFileTypes(Sequence sequence)
  81:   {
  82:     if (sequence.getTracks().length == 1)
  83:       return new int[]{0};
  84:     else
  85:       return new int[]{1};
  86:   }
  87: 
  88:   /* Write a sequence to an output stream in standard midi format.
  89:    * @see javax.sound.midi.spi.MidiFileWriter#write(javax.sound.midi.Sequence, int, java.io.OutputStream)
  90:    */
  91:   public int write(Sequence in, int fileType, OutputStream out)
  92:       throws IOException
  93:   {
  94:     MidiDataOutputStream dos = new MidiDataOutputStream (out);
  95:     Track[] tracks = in.getTracks();
  96:     dos.writeInt(0x4d546864); // MThd
  97:     dos.writeInt(6);  
  98:     dos.writeShort(fileType);
  99:     dos.writeShort(tracks.length);
 100:     float divisionType = in.getDivisionType();
 101:     int resolution = in.getResolution();
 102:     // FIXME: division computation is incomplete.
 103:     int division = 0;
 104:     if (divisionType == Sequence.PPQ)
 105:       division = resolution & 0x7fff;
 106:     dos.writeShort(division); 
 107:     int length = 14;
 108:     for (int i = 0; i < tracks.length; i++)
 109:       length += writeTrack(tracks[i], dos);
 110:     return length;
 111:   }
 112: 
 113:   /**
 114:    * Compute the length of a track as it will be written to the
 115:    * output stream.
 116:    *
 117:    * @param track the track to measure
 118:    * @param dos a MidiDataOutputStream used for helper method
 119:    * @return the length of the track
 120:    */
 121:   private int computeTrackLength(Track track, MidiDataOutputStream dos)
 122:   {
 123:     int count = 0, length = 0, i = 0, eventCount = track.size();
 124:     long ptick = 0;
 125:     while (i < eventCount)
 126:       {
 127:     MidiEvent me = track.get(i);
 128:     long tick = me.getTick();
 129:     length += dos.variableLengthIntLength((int) (tick - ptick));
 130:     ptick = tick;
 131:     length += me.getMessage().getLength();
 132:     i++;
 133:       } 
 134:     return length;
 135:   }
 136: 
 137:   /**
 138:    * Write a track to an output stream.
 139:    *
 140:    * @param track the track to write
 141:    * @param dos a MidiDataOutputStream to write to
 142:    * @return the number of bytes written
 143:    */
 144:   private int writeTrack(Track track, MidiDataOutputStream dos)
 145:     throws IOException
 146:   {
 147:     int i = 0, elength = track.size(), trackLength;
 148:     MidiEvent pme = null;
 149:     dos.writeInt(0x4d54726b); // "MTrk"
 150:     trackLength = computeTrackLength(track, dos);
 151:     dos.writeInt(trackLength);
 152:     while (i < elength)
 153:       {
 154:     MidiEvent me = track.get(i);
 155:     int dtime = 0;
 156:     if (pme != null)
 157:       dtime = (int) (me.getTick() - pme.getTick());
 158:     dos.writeVariableLengthInt(dtime); 
 159:     // FIXME: use running status byte
 160:     byte msg[] = me.getMessage().getMessage();
 161:     dos.write(msg);
 162:     pme = me;
 163:     i++;
 164:       } 
 165: 
 166:     // We're done if the last event was an End of Track meta message.
 167:     if (pme != null && (pme.getMessage() instanceof MetaMessage))
 168:       {
 169:     MetaMessage mm = (MetaMessage) pme.getMessage();
 170:     if (mm.getType() == 0x2f) // End of Track message
 171:       return trackLength + 8;
 172:       }
 173: 
 174:     // Write End of Track meta message
 175:     dos.writeVariableLengthInt(0); // Delta time of 0
 176:     dos.writeByte(0xff); // Meta Message
 177:     dos.writeByte(0x2f); // End of Track message
 178:     dos.writeVariableLengthInt(0); // Length of 0
 179: 
 180:     return trackLength + 8 + 4;
 181:   }
 182: 
 183:   /* Write a Sequence to a file.
 184:    * @see javax.sound.midi.spi.MidiFileWriter#write(javax.sound.midi.Sequence, int, java.io.File)
 185:    */
 186:   public int write(Sequence in, int fileType, File out) throws IOException
 187:   {
 188:     OutputStream os = new FileOutputStream(out);
 189:     try
 190:       {
 191:     return write(in, fileType, os);
 192:       }
 193:     finally
 194:       {
 195:     os.close();
 196:       } 
 197:   }
 198: 
 199: }