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

   1: /* MidiFileWriter.java -- Write MIDI files.
   2:    Copyright (C) 2006, 2012 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.FileOutputStream;
  42: import java.io.IOException;
  43: import java.io.OutputStream;
  44: 
  45: import javax.sound.midi.MetaMessage;
  46: import javax.sound.midi.MidiEvent;
  47: import javax.sound.midi.Sequence;
  48: import javax.sound.midi.Track;
  49: 
  50: /**
  51:  * A MIDI file writer.
  52:  *
  53:  * This code writes MIDI file types 0 and 1.
  54:  *
  55:  * There are many decent documents on the web describing the MIDI file
  56:  * format.  I didn't bother looking for the official document.  If it
  57:  * exists, I'm not even sure if it is freely available.  We should
  58:  * update this comment if we find out anything helpful here.
  59:  *
  60:  * @author Anthony Green (green@redhat.com)
  61:  *
  62:  */
  63: public class MidiFileWriter
  64:     extends javax.sound.midi.spi.MidiFileWriter
  65: {
  66:   /* Return an array indicating which midi file types are supported.
  67:    * @see javax.sound.midi.spi.MidiFileWriter#getMidiFileTypes()
  68:    */
  69:   public int[] getMidiFileTypes()
  70:   {
  71:     return new int[]{0, 1};
  72:   }
  73: 
  74:   /* Return an array indicating which midi file types are supported
  75:    * for a given Sequence.
  76:    * @see javax.sound.midi.spi.MidiFileWriter#getMidiFileTypes(javax.sound.midi.Sequence)
  77:    */
  78:   public int[] getMidiFileTypes(Sequence sequence)
  79:   {
  80:     if (sequence.getTracks().length == 1)
  81:       return new int[]{0};
  82:     else
  83:       return new int[]{1};
  84:   }
  85: 
  86:   /* Write a sequence to an output stream in standard midi format.
  87:    * @see javax.sound.midi.spi.MidiFileWriter#write(javax.sound.midi.Sequence, int, java.io.OutputStream)
  88:    */
  89:   public int write(Sequence in, int fileType, OutputStream out)
  90:       throws IOException
  91:   {
  92:     MidiDataOutputStream dos = new MidiDataOutputStream (out);
  93:     Track[] tracks = in.getTracks();
  94:     dos.writeInt(0x4d546864); // MThd
  95:     dos.writeInt(6);
  96:     dos.writeShort(fileType);
  97:     dos.writeShort(tracks.length);
  98:     float divisionType = in.getDivisionType();
  99:     int resolution = in.getResolution();
 100:     // FIXME: division computation is incomplete.
 101:     int division = 0;
 102:     if (divisionType == Sequence.PPQ)
 103:       division = resolution & 0x7fff;
 104:     dos.writeShort(division);
 105:     int length = 14;
 106:     for (int i = 0; i < tracks.length; i++)
 107:       length += writeTrack(tracks[i], dos);
 108:     return length;
 109:   }
 110: 
 111:   /**
 112:    * Compute the length of a track as it will be written to the
 113:    * output stream.
 114:    *
 115:    * @param track the track to measure
 116:    * @param dos a MidiDataOutputStream used for helper method
 117:    * @return the length of the track
 118:    */
 119:   private int computeTrackLength(Track track, MidiDataOutputStream dos)
 120:   {
 121:     int length = 0, i = 0, eventCount = track.size();
 122:     long ptick = 0;
 123:     while (i < eventCount)
 124:       {
 125:         MidiEvent me = track.get(i);
 126:         long tick = me.getTick();
 127:         length += dos.variableLengthIntLength((int) (tick - ptick));
 128:         ptick = tick;
 129:         length += me.getMessage().getLength();
 130:         i++;
 131:       }
 132:     return length;
 133:   }
 134: 
 135:   /**
 136:    * Write a track to an output stream.
 137:    *
 138:    * @param track the track to write
 139:    * @param dos a MidiDataOutputStream to write to
 140:    * @return the number of bytes written
 141:    */
 142:   private int writeTrack(Track track, MidiDataOutputStream dos)
 143:     throws IOException
 144:   {
 145:     int i = 0, elength = track.size(), trackLength;
 146:     MidiEvent pme = null;
 147:     dos.writeInt(0x4d54726b); // "MTrk"
 148:     trackLength = computeTrackLength(track, dos);
 149:     dos.writeInt(trackLength);
 150:     while (i < elength)
 151:       {
 152:         MidiEvent me = track.get(i);
 153:         int dtime = 0;
 154:         if (pme != null)
 155:           dtime = (int) (me.getTick() - pme.getTick());
 156:         dos.writeVariableLengthInt(dtime);
 157:         // FIXME: use running status byte
 158:         byte msg[] = me.getMessage().getMessage();
 159:         dos.write(msg);
 160:         pme = me;
 161:         i++;
 162:       }
 163: 
 164:     // We're done if the last event was an End of Track meta message.
 165:     if (pme != null && (pme.getMessage() instanceof MetaMessage))
 166:       {
 167:         MetaMessage mm = (MetaMessage) pme.getMessage();
 168:         if (mm.getType() == 0x2f) // End of Track message
 169:           return trackLength + 8;
 170:       }
 171: 
 172:     // Write End of Track meta message
 173:     dos.writeVariableLengthInt(0); // Delta time of 0
 174:     dos.writeByte(0xff); // Meta Message
 175:     dos.writeByte(0x2f); // End of Track message
 176:     dos.writeVariableLengthInt(0); // Length of 0
 177: 
 178:     return trackLength + 8 + 4;
 179:   }
 180: 
 181:   /* Write a Sequence to a file.
 182:    * @see javax.sound.midi.spi.MidiFileWriter#write(javax.sound.midi.Sequence, int, java.io.File)
 183:    */
 184:   public int write(Sequence in, int fileType, File out) throws IOException
 185:   {
 186:     OutputStream os = new FileOutputStream(out);
 187:     try
 188:       {
 189:         return write(in, fileType, os);
 190:       }
 191:     finally
 192:       {
 193:         os.close();
 194:       }
 195:   }
 196: 
 197: }