/* * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.media.sound; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.SequenceInputStream; import java.util.Objects; import javax.sound.midi.InvalidMidiDataException; import javax.sound.midi.MetaMessage; import javax.sound.midi.MidiEvent; import javax.sound.midi.Sequence; import javax.sound.midi.ShortMessage; import javax.sound.midi.SysexMessage; import javax.sound.midi.Track; import javax.sound.midi.spi.MidiFileWriter; /** * MIDI file writer. * * @author Kara Kytle * @author Jan Borgersen */ public final class StandardMidiFileWriter extends MidiFileWriter { private static final int MThd_MAGIC = 0x4d546864; // 'MThd' private static final int MTrk_MAGIC = 0x4d54726b; // 'MTrk' private static final int ONE_BYTE = 1; private static final int TWO_BYTE = 2; private static final int SYSEX = 3; private static final int META = 4; private static final int ERROR = 5; private static final int IGNORE = 6; private static final int MIDI_TYPE_0 = 0; private static final int MIDI_TYPE_1 = 1; private static final int bufferSize = 16384; // buffersize for write private DataOutputStream tddos; // data output stream for track writing /** * MIDI parser types. */ private static final int[] types = { MIDI_TYPE_0, MIDI_TYPE_1 }; @Override public int[] getMidiFileTypes() { int[] localArray = new int[types.length]; System.arraycopy(types, 0, localArray, 0, types.length); return localArray; } /** * Obtains the file types that this provider can write from the * sequence specified. * @param sequence the sequence for which midi file type support * is queried * @return array of file types. If no file types are supported, * returns an array of length 0. */ @Override public int[] getMidiFileTypes(Sequence sequence){ int[] typesArray; Track[] tracks = sequence.getTracks(); if( tracks.length==1 ) { typesArray = new int[2]; typesArray[0] = MIDI_TYPE_0; typesArray[1] = MIDI_TYPE_1; } else { typesArray = new int[1]; typesArray[0] = MIDI_TYPE_1; } return typesArray; } @Override public int write(Sequence in, int type, OutputStream out) throws IOException { Objects.requireNonNull(out); if (!isFileTypeSupported(type, in)) { throw new IllegalArgumentException("Could not write MIDI file"); } byte [] buffer = null; int bytesRead = 0; long bytesWritten = 0; // First get the fileStream from this sequence InputStream fileStream = getFileStream(type,in); if (fileStream == null) { throw new IllegalArgumentException("Could not write MIDI file"); } buffer = new byte[bufferSize]; while( (bytesRead = fileStream.read( buffer )) >= 0 ) { out.write( buffer, 0, bytesRead ); bytesWritten += bytesRead; } // Done....return bytesWritten return (int) bytesWritten; } @Override public int write(Sequence in, int type, File out) throws IOException { Objects.requireNonNull(in); FileOutputStream fos = new FileOutputStream(out); // throws IOException int bytesWritten = write( in, type, fos ); fos.close(); return bytesWritten; } //================================================================================= private InputStream getFileStream(int type, Sequence sequence) throws IOException { Track[] tracks = sequence.getTracks(); int bytesBuilt = 0; int headerLength = 14; int length = 0; int timeFormat; float divtype; PipedOutputStream hpos = null; DataOutputStream hdos = null; PipedInputStream headerStream = null; InputStream[] trackStreams = null; InputStream trackStream = null; InputStream fStream = null; // Determine the filetype to write if( type==MIDI_TYPE_0 ) { if (tracks.length != 1) { return null; } } else if( type==MIDI_TYPE_1 ) { if (tracks.length < 1) { // $$jb: 05.31.99: we _can_ write TYPE_1 if tracks.length==1 return null; } } else { if(tracks.length==1) { type = MIDI_TYPE_0; } else if(tracks.length>1) { type = MIDI_TYPE_1; } else { return null; } } // Now build the file one track at a time // Note that above we made sure that MIDI_TYPE_0 only happens // if tracks.length==1 trackStreams = new InputStream[tracks.length]; int trackCount = 0; for(int i=0; i 1 ){ trackStream = trackStreams[0]; for(int i=1; i 0) && ((value & (mask << shift)) == 0)) shift-=7; // then write actual values while (shift > 0) { tddos.writeByte((int) (((value & (mask << shift)) >> shift) | 0x80)); shift-=7; len++; } tddos.writeByte((int) (value & mask)); return len; } private InputStream writeTrack( Track track, int type ) throws IOException, InvalidMidiDataException { int bytesWritten = 0; int lastBytesWritten = 0; int size = track.size(); PipedOutputStream thpos = new PipedOutputStream(); DataOutputStream thdos = new DataOutputStream(thpos); PipedInputStream thpis = new PipedInputStream(thpos); ByteArrayOutputStream tdbos = new ByteArrayOutputStream(); tddos = new DataOutputStream(tdbos); ByteArrayInputStream tdbis = null; SequenceInputStream fStream = null; long currentTick = 0; long deltaTick = 0; long eventTick = 0; int runningStatus = -1; // ----------------------------- // Write each event in the track // ----------------------------- for(int i=0; i