/*
 * VsqEventList.java
 * Copyright (c) 2008 kbinani
 *
 * This file is part of jp.sourceforge.lipsync.vsq.
 *
 * jp.sourceforge.lipsync.vsq is free software; you can redistribute it and/or
 * modify it under the terms of the BSD License.
 *
 * jp.sourceforge.lipsync.vsq 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.
 */
package jp.sourceforge.lipsync.vsq;

import java.util.*;
import java.io.*;
import java.text.*;
import java.nio.*;

public class VsqFile implements Cloneable {
    Vector<VsqTrack> m_tracks;
    Vector<TempoTableEntry> m_tempo_table;
    Vector<TimeSigTableEntry> m_timesig_table;
    int m_tpq;
    /**
     * 曲の長さを取得します。(クロック(4分音符は480クロック))<pre>
     * public property long TotalClocks {
     *     get {
     *         return TotalClocks;
     *     }
     *     set {
     *         TotalClocks = value;
     *     }
     * };</pre>
     */
    public long TotalClocks = 0;
    int m_base_tempo;
    /**
     * <pre>public VsqMaster Master {
     *     get {
     *         return m_master;
     *     }
     *     set {
     *         m_master = value;
     *     }
     * };</pre>
     */
    public VsqMaster Master;  // VsqMaster, VsqMixerは通常，最初の非Master Trackに記述されるが，可搬性のため，

    /**
     * 
     *<pre>public VsqMixer Mixer {
     *   get {
     *       return m_mixer;
     *   }
     *   set {
     *       m_mixer = value;
     *   }
     *};</pre>
     */
    public VsqMixer Mixer;    // ここではVsqFileに直属するものとして取り扱う．

    int m_premeasure_clocks;
    static byte[] _MTRK = new byte[] {0x4d, 0x54, 0x72, 0x6b};
    static byte[] _MTHD = new byte[] {0x4d, 0x54, 0x68, 0x64};
    static byte[] _MASTER_TRACK = new byte[] {0x4D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x54, 0x72, 0x61, 0x63, 0x6B,};

    public VsqCommand Execute( VsqCommand command ) {
        VsqCommandType type = command.Type;
        if ( type == VsqCommandType.ChangePreMeasure ) {
            VsqCommand ret = GCommandChangePreMeasure( Master.PreMeasure );
            int value = (Integer)command.Args[0];
            Master.PreMeasure = value;
            updateTimesigInfo();
            return ret;
        } else if ( type == VsqCommandType.AddTrack ) {
            VsqTrack track = (VsqTrack)command.Args[0];
            VsqMixerEntry mixer = (VsqMixerEntry)command.Args[1];
            int position = (Integer)command.Args[2];
            VsqCommand ret = GCommandDeleteTrack( position );
            if ( m_tracks.size() <= 17 ) {
                m_tracks.insertElementAt( (VsqTrack)track.clone(),position );
                Mixer.Slave.add( (VsqMixerEntry)mixer.clone() );
                //Mixer.m_tracks = Mixer.Slave.Count;
                return ret;
            } else {
                return null;
            }
        } else if ( type == VsqCommandType.DeleteTrack ) {
            int track = (Integer)command.Args[0];
            VsqCommand ret = GCommandAddTrack( m_tracks.get( track), Mixer.Slave.get(track - 1), track );
            m_tracks.removeElementAt( track );
            Mixer.Slave.removeElementAt( track - 1 );
            updateTotalClocks();
            return ret;
        } else if ( type == VsqCommandType.UpdateTempo ) {
            int clock = (Integer)command.Args[0];
            int tempo = (Integer)command.Args[1];
            int new_clock = (Integer)command.Args[2];

            int index = -1;
            for ( int i = 0; i < m_tempo_table.size(); i++ ) {
                if ( m_tempo_table.get( i ).Clock == clock ) {
                    index = i;
                    break;
                }
            }
            VsqCommand ret = null;
            if ( index >= 0 ) {
                if ( tempo <= 0 ) {
                    ret = GCommandUpdateTempo( clock, clock, m_tempo_table.get( index ).Tempo );
                    m_tempo_table.removeElementAt( index );
                } else {
                    ret = GCommandUpdateTempo( new_clock, clock, m_tempo_table.get( index ).Tempo );
                    m_tempo_table.get( index ).Tempo = tempo;
                    m_tempo_table.get( index ).Clock = new_clock;
                }
            } else {
                ret = GCommandUpdateTempo( clock, clock, -1 );
                m_tempo_table.add( new TempoTableEntry( new_clock, tempo, 0.0 ) );
            }
            updateTempoInfo();
            updateTotalClocks();

            // 編集領域を更新
            int affected_clock = Math.min( clock, new_clock );
            for ( int i = 1; i < m_tracks.size(); i++ ) {
                if ( affected_clock < m_tracks.get( i ).getEditedStart() ) {
                    m_tracks.get( i ).setEditedStart( affected_clock );
                }
                m_tracks.get( i ).setEditedEnd((int)TotalClocks );
            }
            return ret;
        } else if ( type == VsqCommandType.UpdateTempoRange ) {
            int[] clocks = (int[])command.Args[0];
            int[] tempos = (int[])command.Args[1];
            int[] new_clocks = (int[])command.Args[2];
            int[] new_tempos = new int[tempos.length];
            int affected_clock = Integer.MAX_VALUE;
            for ( int i = 0; i < clocks.length; i++ ) {
                int index = -1;
                affected_clock = Math.min( affected_clock, clocks[i] );
                affected_clock = Math.min( affected_clock, new_clocks[i] );
                for ( int j = 0; j < m_tempo_table.size(); j++ ) {
                    if ( m_tempo_table.get( j ).Clock == clocks[i] ) {
                        index = j;
                        break;
                    }
                }
                if ( index >= 0 ) {
                    new_tempos[i] = m_tempo_table.get( index ).Tempo;
                    if ( tempos[i] <= 0 ) {
                        m_tempo_table.removeElementAt( index );
                    } else {
                        m_tempo_table.get( index ).Tempo = tempos[i];
                        m_tempo_table.get( index ).Clock = new_clocks[i];
                    }
                } else {
                    new_tempos[i] = -1;
                    m_tempo_table.add( new TempoTableEntry( new_clocks[i], tempos[i], 0.0 ) );
                }
            }
            updateTempoInfo();
            updateTotalClocks();
            for ( int i = 1; i < m_tracks.size(); i++ ) {
                if ( affected_clock < m_tracks.get( i ).getEditedStart() ) {
                    m_tracks.get( i ).setEditedStart( affected_clock );
                }
                m_tracks.get( i ).setEditedEnd( (int)TotalClocks );
            }
            return GCommandUpdateTempoRange( new_clocks, clocks, new_tempos );
        } else if ( type == VsqCommandType.UpdateTimesig ) {
            int barcount = (Integer)command.Args[0];
            int numerator = (Integer)command.Args[1];
            int denominator = (Integer)command.Args[2];
            int new_barcount = (Integer)command.Args[3];
            int index = -1;
            for ( int i = 0; i < m_timesig_table.size(); i++ ) {
                if ( barcount == m_timesig_table.get( i ).BarCount ) {
                    index = i;
                    break;
                }
            }
            VsqCommand ret = null;
            if ( index >= 0 ) {
                if ( numerator <= 0 ) {
                    ret = GCommandUpdateTimesig( barcount, barcount, m_timesig_table.get( index ).Numerator, m_timesig_table.get( index ).Denominator );
                    m_timesig_table.removeElementAt( index );
                } else {
                    ret = GCommandUpdateTimesig( new_barcount, barcount, m_timesig_table.get( index ).Numerator, m_timesig_table.get( index ).Denominator );
                    m_timesig_table.get( index ).BarCount = new_barcount;
                    m_timesig_table.get( index ).Numerator = numerator;
                    m_timesig_table.get( index ).Denominator = denominator;
                }
            } else {
                ret = GCommandUpdateTimesig( new_barcount, new_barcount, -1, -1 );
                m_timesig_table.add( new TimeSigTableEntry( 0, numerator, denominator, new_barcount ) );
            }
            updateTimesigInfo();
            updateTotalClocks();
            return ret;
        } else if ( type == VsqCommandType.UpdateTimesigRange ) {
            int[] barcounts = (int[])command.Args[0];
            int[] numerators = (int[])command.Args[1];
            int[] denominators = (int[])command.Args[2];
            int[] new_barcounts = (int[])command.Args[3];
            int[] new_numerators = new int[numerators.length];
            int[] new_denominators = new int[denominators.length];
            for ( int i = 0; i < barcounts.length; i++ ) {
                int index = -1;
                for ( int j = 0; j < m_timesig_table.size(); j++ ) {
                    if ( m_timesig_table.get(j).BarCount == barcounts[i] ) {
                        index = j;
                        break;
                    }
                }
                if ( index >= 0 ) {
                    new_numerators[i] = m_timesig_table.get( index ).Numerator;
                    new_denominators[i] = m_timesig_table.get( index ).Denominator;
                    if ( numerators[i] <= 0 ) {
                        m_timesig_table.removeElementAt( index );
                    } else {
                        m_timesig_table.get( index ).BarCount = new_barcounts[i];
                        m_timesig_table.get( index ).Numerator = numerators[i];
                        m_timesig_table.get( index ).Denominator = denominators[i];
                    }
                } else {
                    new_numerators[i] = -1;
                    new_denominators[i] = -1;
                    m_timesig_table.add( new TimeSigTableEntry( 0, numerators[i], denominators[i], new_barcounts[i] ) );
                }
            }
            updateTimesigInfo();
            updateTotalClocks();
            return GCommandUpdateTimesigRange( new_barcounts, barcounts, new_numerators, new_denominators );
        } else if ( type == VsqCommandType.Replace ) {
            VsqFile vsq = (VsqFile)command.Args[0];
            VsqFile inv = (VsqFile)this.clone();
            m_tracks.clear();
            for ( int i = 0; i < vsq.m_tracks.size(); i++ ) {
                m_tracks.add( (VsqTrack)vsq.m_tracks.get( i ).clone() );
            }
            m_tempo_table.clear();
            for ( int i = 0; i < vsq.m_tempo_table.size(); i++ ) {
                m_tempo_table.add( (TempoTableEntry)vsq.m_tempo_table.get( i ).clone() );
            }
            m_timesig_table.clear();
            for ( int i = 0; i < vsq.m_timesig_table.size(); i++ ) {
                m_timesig_table.add( (TimeSigTableEntry)vsq.m_timesig_table.get( i ).clone() );
            }
            m_tpq = vsq.m_tpq;
            TotalClocks = vsq.TotalClocks;
            m_base_tempo = vsq.m_base_tempo;
            Master = (VsqMaster)vsq.Master.clone();
            Mixer = (VsqMixer)vsq.Mixer.clone();
            m_premeasure_clocks = vsq.m_premeasure_clocks;
            updateTotalClocks();
            return GCommandReplace( inv );
        } else if ( type == VsqCommandType.EventAdd ) {
            int track = (Integer)command.Args[0];
            VsqEvent item = (VsqEvent)command.Args[1];
            //int key = this.m_tracks.get( track ).GetNextId( 0 );
            //item.InternalID = key;
            m_tracks.get( track ).getEvents().add( item );
            VsqCommand ret = GCommandEventDelete( track, item.InternalID );
            //this.m_tracks.get( track ).Events.Sort();
            updateTotalClocks();
            if ( item.Clock < m_tracks.get(track).getEditedStart() ) {
                m_tracks.get( track).setEditedStart( item.Clock);
            }
            if ( m_tracks.get(track).getEditedEnd() < item.Clock + item.ID.Length ) {
                m_tracks.get( track ).setEditedEnd( item.Clock + item.ID.Length );
            }
            return ret;
        } else if ( type == VsqCommandType.EventAddRange ) {
            int track = (Integer)command.Args[0];
            VsqEvent[] items = (VsqEvent[])command.Args[1];
            Vector<Integer> inv_ids = new Vector<Integer>();
            int min_clock = (int)TotalClocks;
            int max_clock = 0;
            for ( int i = 0; i < items.length; i++ ) {
                VsqEvent item = (VsqEvent)items[i].clone();
                min_clock = Math.min( min_clock, item.Clock );
                max_clock = Math.max( max_clock, item.Clock + item.ID.Length );
                //int key = m_tracks.get( track ).GetNextId( i );
                //item.InternalID = key;
                m_tracks.get( track ).getEvents().add( item );
                inv_ids.add( item.InternalID );
            }
            //m_tracks.get( track ).Events.Sort();
            updateTotalClocks();
            if ( min_clock < m_tracks.get( track ).getEditedStart() ) {
                m_tracks.get( track ).setEditedStart( min_clock );
            }
            if ( m_tracks.get( track ).getEditedEnd() < max_clock ) {
                m_tracks.get( track ).setEditedEnd( max_clock );
            }
            int count = inv_ids.size();
            int[] inv_ids_arr = new int[count];
            for( int i = 0; i < count; i++ ){
                inv_ids_arr[i] = inv_ids.get( i );
            }
            return GCommandEventDeleteRange( track, inv_ids_arr );
        } else if ( type == VsqCommandType.EventDelete ) {
            int internal_id = (Integer)command.Args[0];
            int track = (Integer)command.Args[1];
            VsqEvent[] original = new VsqEvent[1];
            for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                VsqEvent item = (VsqEvent)itr.next();
                if ( item.InternalID == internal_id ) {
                    original[0] = (VsqEvent)item.clone();
                    break;
                }
            }
            if ( original[0].Clock < m_tracks.get( track ).getEditedStart() ) {
                m_tracks.get( track ).setEditedStart( original[0].Clock );
            }
            if ( m_tracks.get( track ).getEditedEnd() < original[0].Clock + original[0].ID.Length ) {
                m_tracks.get( track ).setEditedEnd(original[0].Clock + original[0].ID.Length);
            }
            VsqCommand ret = GCommandEventAddRange( track, original );
            for ( int i = 0; i < this.m_tracks.get( track ).getEvents().size(); i++ ) {
                if ( this.m_tracks.get( track ).getEvents().get( i ).InternalID == internal_id ) {
                    m_tracks.get( track ).getEvents().removeAt( i );
                    break;
                }
            }
            updateTotalClocks();
            return ret;
        } else if ( type == VsqCommandType.EventDeleteRange ) {
            int[] internal_ids = (int[])command.Args[0];
            int track = (Integer)command.Args[1];
            Vector<VsqEvent> inv = new Vector<VsqEvent>();
            int min_clock = Integer.MAX_VALUE;
            int max_clock = Integer.MIN_VALUE;
            for ( int j = 0; j < internal_ids.length; j++ ) {
                for ( int i = 0; i < m_tracks.get( track ).getEvents().size(); i++ ) {
                    if ( internal_ids[j] == m_tracks.get( track ).getEvents().get( i ).InternalID ) {
                        inv.add( (VsqEvent)m_tracks.get( track ).getEvents().get( i ).clone() );
                        min_clock = Math.min( min_clock, m_tracks.get( track ).getEvents().get( i ).Clock );
                        max_clock = Math.max( max_clock, m_tracks.get( track ).getEvents().get( i ).Clock + m_tracks.get( track ).getEvents().get( i ).ID.Length );
                        m_tracks.get( track ).getEvents().removeAt( i );
                        break;
                    }
                }
            }
            updateTotalClocks();
            m_tracks.get( track ).setEditedStart(min_clock);
            m_tracks.get( track ).setEditedEnd(max_clock );
            VsqEvent[] inv_arr = new VsqEvent[inv.size()];
            for( int i = 0; i < inv.size(); i++ ){
                inv_arr[i] = inv.get(i );
            }
            return GCommandEventAddRange( track, inv_arr );
        } else if ( type == VsqCommandType.EventChangeClock ) {
            int track = (Integer)command.Args[0];
            int internal_id = (Integer)command.Args[1];
            int value = (Integer)command.Args[2];
            for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                VsqEvent item = (VsqEvent)itr.next();
                if ( item.InternalID == internal_id ) {
                    VsqCommand ret = GCommandEventChangeClock( track, internal_id, item.Clock );
                    int min = Math.min( item.Clock, value );
                    int max = Math.max( item.Clock + item.ID.Length, value + item.ID.Length );
                    m_tracks.get( track ).setEditedStart(min);
                    m_tracks.get( track ).setEditedEnd(max);
                    item.Clock = value;
                    updateTotalClocks();
                    return ret;
                }
            }
            return null;
        } else if ( type == VsqCommandType.EventChangeLyric ) {
            int track = (Integer)command.Args[0];
            int internal_id = (Integer)command.Args[1];
            String phrase = (String)command.Args[2];
            String phonetic_symbol = (String)command.Args[3];
            boolean protect_symbol = (Boolean)command.Args[4];
            for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                VsqEvent item = (VsqEvent)itr.next();
                if ( item.InternalID == internal_id ) {
                    if ( item.ID.type == VsqIDType.Anote ) {
                        VsqCommand ret = GCommandEventChangeLyric( track, internal_id, item.ID.LyricHandle.L0.Phrase, item.ID.LyricHandle.L0.getPhoneticSymbol(), item.ID.LyricHandle.L0.PhoneticSymbolProtected );
                        item.ID.LyricHandle.L0.Phrase = phrase;
                        item.ID.LyricHandle.L0.setPhoneticSymbol( phonetic_symbol );
                        item.ID.LyricHandle.L0.PhoneticSymbolProtected = protect_symbol;
                        m_tracks.get( track ).setEditedStart(item.Clock);
                        m_tracks.get( track ).setEditedEnd(item.Clock + item.ID.Length);
                        updateTotalClocks();
                        return ret;
                    }
                }
            }
            return null;
        } else if ( type == VsqCommandType.EventChangeNote ) {
            int track = (Integer)command.Args[0];
            int internal_id = (Integer)command.Args[1];
            int note = (Integer)command.Args[2];
            for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                VsqEvent item = (VsqEvent)itr.next();
                if ( item.InternalID == internal_id ) {
                    VsqCommand ret = GCommandEventChangeNote( track, internal_id, item.ID.Note );
                    item.ID.Note = note;
                    updateTotalClocks();
                    m_tracks.get( track ).setEditedStart(item.Clock);
                    m_tracks.get( track ).setEditedEnd(item.Clock + item.ID.Length);
                    return ret;
                }
            }
            return null;
        } else if ( type == VsqCommandType.EventChangeClockAndNote ) {
            int track = (Integer)command.Args[0];
            int internal_id = (Integer)command.Args[1];
            int clock = (Integer)command.Args[2];
            int note = (Integer)command.Args[3];
            for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                VsqEvent item = (VsqEvent)itr.next();
                if ( item.InternalID == internal_id ) {
                    VsqCommand ret = GCommandEventChangeClockAndNote( track, internal_id, item.Clock, item.ID.Note );
                    int min = Math.min( item.Clock, clock );
                    int max = Math.max( item.Clock + item.ID.Length, clock + item.ID.Length );
                    m_tracks.get( track ).setEditedStart( min );
                    m_tracks.get( track ).setEditedEnd( max );
                    item.Clock = clock;
                    item.ID.Note = note;
                    //this.m_tracks.get( track ).Events.Sort();
                    updateTotalClocks();
                    return ret;
                }
            }
            return null;
        } else if ( type == VsqCommandType.TrackEditCurve ) {
            int track = (Integer)command.Args[0];
            VsqCurveType curve = (VsqCurveType)command.Args[1];
            Vector<BPPair> com = (Vector<BPPair>)command.Args[2];
            //SortedList<int, int> list = m_tracks.get( track )[curve].Vector;

            VsqCommand inv = null;
            Vector<BPPair> edit = new Vector<BPPair>();
            if ( com != null ) {
                if ( com.size() > 0 ) {
                    int start_clock = com.get(0).Clock;
                    int end_clock = com.get(0).Clock;
                    for ( Iterator itr = com.iterator(); itr.hasNext();) {
                        BPPair item = (BPPair)itr.next();
                        start_clock = Math.min( start_clock, item.Clock );
                        end_clock = Math.max( end_clock, item.Clock );
                    }
                    m_tracks.get( track ).setEditedStart( start_clock );
                    m_tracks.get( track ).setEditedEnd( end_clock );
                    int start_value = m_tracks.get( track ).getVsqBPList(curve).get( start_clock);
                    int end_value = m_tracks.get( track ).getVsqBPList(curve).get(end_clock);
                    for ( Iterator itr = m_tracks.get( track ).getVsqBPList( curve ).keyClockIterator(); itr.hasNext();) {
                        int clock = (Integer)itr.next();
                        if ( start_clock <= clock && clock <= end_clock ) {
                            edit.add( new BPPair( clock, m_tracks.get( track ).getVsqBPList(curve).get(clock) ) );
                        }
                    }
                    boolean start_found = false;
                    boolean end_found = false;
                    for ( int i = 0; i < edit.size(); i++ ) {
                        if ( edit.get( i ).Clock == start_clock ) {
                            start_found = true;
                            edit.get( i ).Value = start_value;
                            if ( start_found && end_found ) {
                                break;
                            }
                        }
                        if ( edit.get( i ).Clock == end_clock ) {
                            end_found = true;
                            edit.get( i ).Value = end_value;
                            if ( start_found && end_found ) {
                                break;
                            }
                        }
                    }
                    if ( !start_found ) {
                        edit.add( new BPPair( start_clock, start_value ) );
                    }
                    if ( !end_found ) {
                        edit.add( new BPPair( end_clock, end_value ) );
                    }

                    // 並べ替え
                    Collections.sort( edit );
                    inv = GCommandTrackEditCurve( track, curve, edit );
                } else if ( com.size() == 0 ) {
                    inv = GCommandTrackEditCurve( track, curve, new Vector<BPPair>() );
                }
            }

            updateTotalClocks();
            if ( com.size() == 0 ) {
                return inv;
            } else if ( com.size() == 1 ) {
                boolean found = false;
                for ( Iterator itr = m_tracks.get( track ).getVsqBPList( curve ).keyClockIterator(); itr.hasNext();) {
                    int clock = (Integer)itr.next();
                    if ( clock == com.get(0).Clock ) {
                        found = true;
                        m_tracks.get( track ).getVsqBPList(curve).add( clock, com.get(0).Value );
                        break;
                    }
                }
                if ( !found ) {
                    m_tracks.get( track ).getVsqBPList(curve).add( com.get(0).Clock, com.get(0).Value );
                }
            } else {
                int start_clock = com.get(0).Clock;
                int end_clock = com.get(com.size() - 1).Clock;
                boolean removed = true;
                while ( removed ) {
                    removed = false;
                    for ( Iterator itr = m_tracks.get( track ).getVsqBPList( curve ).keyClockIterator(); itr.hasNext();) {
                        int clock = (Integer)itr.next();
                        if ( start_clock <= clock && clock <= end_clock ) {
                            m_tracks.get( track ).getVsqBPList(curve).remove( clock );
                            removed = true;
                            break;
                        }
                    }
                }
                for ( Iterator itr = com.iterator(); itr.hasNext();) {
                    BPPair item = (BPPair)itr.next();
                    m_tracks.get( track ).getVsqBPList(curve).add( item.Clock, item.Value );
                }
            }
            return inv;
        } else if ( type == VsqCommandType.TrackEditCurveRange ) {
            int track = (Integer)command.Args[0];
            VsqCurveType[] curves = (VsqCurveType[])command.Args[1];
            Vector<BPPair>[] coms = (Vector<BPPair>[])command.Args[2];
            Vector<Vector<BPPair>> inv_coms = new Vector<Vector<BPPair>>( curves.length );
            VsqCommand inv = null;

            for ( int k = 0; k < curves.length; k++ ) {
                VsqCurveType curve = curves[k];
                Vector<BPPair> com = coms[k];
                //SortedList<int, int> list = m_tracks.get( track )[curve].Vector;
                Vector<BPPair> edit = new Vector<BPPair>();
                if ( com != null ) {
                    if ( com.size() > 0 ) {
                        int start_clock = com.get(0).Clock;
                        int end_clock = com.get(0).Clock;
                        for ( Iterator itr = com.iterator(); itr.hasNext();) {
                            BPPair item = (BPPair)itr.next();
                            start_clock = Math.min( start_clock, item.Clock );
                            end_clock = Math.max( end_clock, item.Clock );
                        }
                        m_tracks.get( track ).setEditedStart( start_clock);
                        m_tracks.get( track ).setEditedEnd( end_clock);
                        int start_value = m_tracks.get( track ).getVsqBPList(curve).get(start_clock);
                        int end_value = m_tracks.get( track ).getVsqBPList(curve).get(end_clock);
                        for ( Iterator itr = m_tracks.get( track ).getVsqBPList( curve ).keyClockIterator(); itr.hasNext();) {
                            int clock = (Integer)itr.next();
                            if ( start_clock <= clock && clock <= end_clock ) {
                                edit.add( new BPPair( clock, m_tracks.get( track ).getVsqBPList(curve).get(clock) ) );
                            }
                        }
                        boolean start_found = false;
                        boolean end_found = false;
                        for ( int i = 0; i < edit.size(); i++ ) {
                            if ( edit.get( i ).Clock == start_clock ) {
                                start_found = true;
                                edit.get( i ).Value = start_value;
                                if ( start_found && end_found ) {
                                    break;
                                }
                            }
                            if ( edit.get( i ).Clock == end_clock ) {
                                end_found = true;
                                edit.get( i ).Value = end_value;
                                if ( start_found && end_found ) {
                                    break;
                                }
                            }
                        }
                        if ( !start_found ) {
                            edit.add( new BPPair( start_clock, start_value ) );
                        }
                        if ( !end_found ) {
                            edit.add( new BPPair( end_clock, end_value ) );
                        }

                        // 並べ替え
                        Collections.sort( edit);
                        inv_coms.set( k, edit );
                    //inv = GCommandTrackEditCurve( track, curve, edit );
                    } else if ( com.size() == 0 ) {
                        //inv = GCommandTrackEditCurve( track, curve, new Vector<BPPair>() );
                        inv_coms.set( k, new Vector<BPPair>() );
                    }
                }

                updateTotalClocks();
                if ( com.size() == 0 ) {
                    return inv;
                } else if ( com.size() == 1 ) {
                    boolean found = false;
                    for ( Iterator itr = m_tracks.get( track ).getVsqBPList( curve ).keyClockIterator(); itr.hasNext();) {
                        int clock = (Integer)itr.next();
                        if ( clock == com.get(0).Clock ) {
                            found = true;
                            m_tracks.get( track ).getVsqBPList(curve).add( clock, com.get(0).Value );
                            break;
                        }
                    }
                    if ( !found ) {
                        m_tracks.get( track ).getVsqBPList(curve).add( com.get(0).Clock, com.get(0).Value );
                    }
                } else {
                    int start_clock = com.get(0).Clock;
                    int end_clock = com.get(com.size() - 1).Clock;
                    boolean removed = true;
                    while ( removed ) {
                        removed = false;
                        for ( Iterator itr = m_tracks.get( track ).getVsqBPList( curve ).keyClockIterator(); itr.hasNext();) {
                            int clock = (Integer)itr.next();
                            if ( start_clock <= clock && clock <= end_clock ) {
                                m_tracks.get( track ).getVsqBPList(curve).remove( clock );
                                removed = true;
                                break;
                            }
                        }
                    }
                    for ( Iterator itr = com.iterator(); itr.hasNext();) {
                        BPPair item = (BPPair)itr.next();
                        m_tracks.get( track ).getVsqBPList(curve).add( item.Clock, item.Value );
                    }
                }
            }
            return GCommandTrackEditCurveRange( track, curves, inv_coms );
        } else if ( type == VsqCommandType.EventChangeVelocity ) {
            int track = (Integer)command.Args[0];
            Vector<KeyValuePair<Integer, Integer>> veloc = (Vector<KeyValuePair<Integer, Integer>>)command.Args[1];
            Vector<KeyValuePair<Integer, Integer>> inv = new Vector<KeyValuePair<Integer, Integer>>();
            for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                VsqEvent ev = (VsqEvent)itr.next();
                for ( Iterator itr2 = veloc.iterator(); itr2.hasNext();) {
                    KeyValuePair<Integer, Integer> add = (KeyValuePair<Integer, Integer>)itr2.next();
                    if ( ev.InternalID == add.Key ) {
                        inv.add( new KeyValuePair<Integer, Integer>( ev.InternalID, ev.ID.Dynamics ) );
                        ev.ID.Dynamics = add.Value;
                        m_tracks.get( track ).setEditedStart( ev.Clock );
                        m_tracks.get( track ).setEditedEnd( ev.Clock + ev.ID.Length );
                        break;
                    }
                }
            }
            return GCommandEventChangeVelocity( track, inv );
        } else if ( type == VsqCommandType.EventChangeLength ) {
            int track = (Integer)command.Args[0];
            int internal_id = (Integer)command.Args[1];
            int new_length = (Integer)command.Args[2];
            for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                VsqEvent item = (VsqEvent)itr.next();
                if ( item.InternalID == internal_id ) {
                    VsqCommand ret = GCommandEventChangeLength( track, internal_id, item.ID.Length );
                    m_tracks.get( track ).setEditedStart( item.Clock );
                    int max = Math.max( item.Clock + item.ID.Length, item.Clock + new_length );
                    m_tracks.get( track ).setEditedEnd( max );
                    item.ID.Length = new_length;
                    updateTotalClocks();
                    return ret;
                }
            }
            return null;
        } else if ( type == VsqCommandType.EventChangeClockAndLength ) {
            int track = (Integer)command.Args[0];
            int internal_id = (Integer)command.Args[1];
            int new_clock = (Integer)command.Args[2];
            int new_length = (Integer)command.Args[3];
            for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                VsqEvent item = (VsqEvent)itr.next();
                if ( item.InternalID == internal_id ) {
                    VsqCommand ret = GCommandEventChangeClockAndLength( track, internal_id, item.Clock, item.ID.Length );
                    int min = Math.min( item.Clock, new_clock );
                    int max_length = Math.max( item.ID.Length, new_length );
                    int max = Math.max( item.Clock + max_length, new_clock + max_length );
                    m_tracks.get( track ).setEditedStart( min );
                    m_tracks.get( track ).setEditedEnd( max );
                    item.ID.Length = new_length;
                    item.Clock = new_clock;
                    updateTotalClocks();
                    return ret;
                }
            }
            return null;
        } else if ( type == VsqCommandType.EventChangeIDContaints ) {
            int track = (Integer)command.Args[0];
            int internal_id = (Integer)command.Args[1];
            VsqID value = (VsqID)command.Args[2];
            for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                VsqEvent item = (VsqEvent)itr.next();
                if ( item.InternalID == internal_id ) {
                    VsqCommand ret = GCommandEventChangeIDContaints( track, internal_id, item.ID );
                    int max_length = Math.max( item.ID.Length, value.Length );
                    m_tracks.get( track ).setEditedStart( item.Clock );
                    m_tracks.get( track ).setEditedEnd( item.Clock + max_length );
                    item.ID = (VsqID)value.clone();
                    updateTotalClocks();
                    return ret;
                }
            }
            return null;
        } else if ( type == VsqCommandType.EventChangeIDContaintsRange ) {
            int track = (Integer)command.Args[0];
            int[] internal_ids = (int[])command.Args[1];
            VsqID[] values = (VsqID[])command.Args[2];
            VsqID[] inv_values = new VsqID[values.length];
            for ( int i = 0; i < internal_ids.length; i++ ) {
                for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                    VsqEvent item = (VsqEvent)itr.next();
                    if ( item.InternalID == internal_ids[i] ) {
                        inv_values[i] = (VsqID)item.ID.clone();
                        int max_length = Math.max( item.ID.Length, values[i].Length );
                        m_tracks.get( track ).setEditedStart( item.Clock );
                        m_tracks.get( track ).setEditedEnd( item.Clock + max_length );
                        item.ID = (VsqID)values[i].clone();
                        break;
                    }
                }
            }
            updateTotalClocks();
            return GCommandEventChangeIDContaintsRange( track, internal_ids, inv_values );
        } else if ( type == VsqCommandType.EventChangeClockAndIDContaints ) {
            int track = (Integer)command.Args[0];
            int internal_id = (Integer)command.Args[1];
            int new_clock = (Integer)command.Args[2];
            VsqID value = (VsqID)command.Args[3];
            for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                VsqEvent item = (VsqEvent)itr.next();
                if ( item.InternalID == internal_id ) {
                    VsqCommand ret = GCommandEventChangeClockAndIDContaints( track, internal_id, item.Clock, item.ID );
                    int max_length = Math.max( item.ID.Length, value.Length );
                    int min = Math.min( item.Clock, new_clock );
                    int max = Math.max( item.Clock + max_length, new_clock + max_length );
                    item.ID = (VsqID)value.clone();
                    item.Clock = new_clock;
                    m_tracks.get( track ).setEditedStart( min );
                    m_tracks.get( track ).setEditedEnd( max );
                    updateTotalClocks();
                    return ret;
                }
            }
            return null;
        } else if ( type == VsqCommandType.EventChangeClockAndIDContaintsRange ) {
            int track = (Integer)command.Args[0];
            int[] internal_ids = (int[])command.Args[1];
            int[] clocks = (int[])command.Args[2];
            VsqID[] values = (VsqID[])command.Args[3];
            Vector<VsqID> inv_id = new Vector<VsqID>();
            Vector<Integer> inv_clock = new Vector<Integer>();
            for ( int i = 0; i < internal_ids.length; i++ ) {
                for ( Iterator itr = m_tracks.get( track ).getEvents().iterator(); itr.hasNext();) {
                    VsqEvent item = (VsqEvent)itr.next();
                    if ( item.InternalID == internal_ids[i] ) {
                        inv_id.add( (VsqID)item.ID.clone() );
                        inv_clock.add( item.Clock );
                        int max_length = Math.max( item.ID.Length, values[i].Length );
                        int min = Math.min( item.Clock, clocks[i] );
                        int max = Math.max( item.Clock + max_length, clocks[i] + max_length );
                        m_tracks.get( track ).setEditedStart( min );
                        m_tracks.get( track ).setEditedEnd( max );
                        item.ID = (VsqID)values[i].clone();
                        item.Clock = clocks[i];
                        break;
                    }
                }
            }
            updateTotalClocks();
            int[] inv_clock_arr = new int[inv_clock.size()];
            for( int i = 0; i < inv_clock.size(); i++ ){
                inv_clock_arr[i] = inv_clock.get( i);
            }
            VsqID[] inv_id_arr = new VsqID[inv_id.size()];
            for( int i = 0; i < inv_id.size(); i++ ){
                inv_id_arr[i] = inv_id.get(i);
            }
            return GCommandEventChangeClockAndIDContaintsRange(
                    track,
                    internal_ids,
                    inv_clock_arr,
                    inv_id_arr );
        } else if ( type == VsqCommandType.TrackChangeName ) {
            int track = (Integer)command.Args[0];
            String new_name = (String)command.Args[1];
            VsqCommand ret = GCommandTrackChangeName( track, m_tracks.get( track ).getName() );
            m_tracks.get( track ).setName( new_name );
            return ret;
        } else if ( type == VsqCommandType.TrackReplace ) {
            int track = (Integer)command.Args[0];
            VsqTrack item = (VsqTrack)command.Args[1];
            VsqCommand ret = GCommandTrackReplace( track, (VsqTrack)m_tracks.get( track ).clone() );
            m_tracks.set( track, item );
            updateTotalClocks();
            return ret;
        }

        return null;
    }

    public static VsqCommand GCommandRoot() {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.Root;
        command.Args = null;
        return command;
    }

    public static VsqCommand GCommandReplace( VsqFile vsq ) {
        VsqCommand command = new VsqCommand();
        command.Args = new Object[1];
        command.Type = VsqCommandType.Replace;
        command.Args[0] = (VsqFile)vsq.clone();
        return command;
    }

    public static VsqCommand GCommandTrackReplace( int track, VsqTrack item ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.TrackReplace;
        command.Args = new Object[2];
        command.Args[0] = track;
        command.Args[1] = (VsqTrack)item.clone();
        return command;
    }

    public static VsqCommand GCommandUpdateTimesig( int bar_count, int new_barcount, int numerator, int denominator ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.UpdateTimesig;
        command.Args = new Object[4];
        command.Args[0] = bar_count;
        command.Args[1] = numerator;
        command.Args[2] = denominator;
        command.Args[3] = new_barcount;
        return command;
    }

    public static VsqCommand GCommandUpdateTimesigRange( int[] bar_counts, int[] new_barcounts, int[] numerators, int[] denominators ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.UpdateTimesigRange;
        command.Args = new Object[4];
        command.Args[0] = (int[])bar_counts.clone();
        command.Args[1] = (int[])numerators.clone();
        command.Args[2] = (int[])denominators.clone();
        command.Args[3] = (int[])new_barcounts.clone();
        return command;
    }

    public static VsqCommand GCommandUpdateTempoRange( int[] clocks, int[] new_clocks, int[] tempos ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.UpdateTempoRange;
        command.Args = new Object[3];
        command.Args[0] = (int[])clocks.clone();
        command.Args[1] = (int[])tempos.clone();
        command.Args[2] = (int[])new_clocks.clone();
        return command;
    }

    public static VsqCommand GCommandUpdateTempo( int clock, int new_clock, int tempo ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.UpdateTempo;
        command.Args = new Object[3];
        command.Args[0] = clock;
        command.Args[1] = tempo;
        command.Args[2] = new_clock;
        return command;
    }

    public static VsqCommand GCommandChangePreMeasure( int pre_measure ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.ChangePreMeasure;
        command.Args = new Object[1];
        command.Args[0] = pre_measure;
        return command;
    }

    public static VsqCommand GCommandDeleteTrack( int track ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.DeleteTrack;
        command.Args = new Object[1];
        command.Args[0] = track;
        return command;
    }


    /// <summary>
    /// トラックを追加するコマンドを発行します．trackはClone()して渡さなくてもよい
    /// </summary>
    /// <param name="track"></param>
    /// <returns></returns>
    public static VsqCommand GCommandAddTrack( VsqTrack track, VsqMixerEntry mixer, int position ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.AddTrack;
        command.Args = new Object[5];
        command.Args[0] = track;
        command.Args[1] = mixer;
        command.Args[2] = position;
        return command;
    }


    /// <summary>
    /// トラック名を変更するコマンドを作成します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="new_name"></param>
    /// <returns></returns>
    public static VsqCommand GCommandTrackChangeName( int track, String new_name ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.TrackChangeName;
        command.Args = new Object[2];
        command.Args[0] = track;
        command.Args[1] = new_name;
        return command;
    }


    /// <summary>
    /// VsqIDとClockを同時に変更するコマンドを発行します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="internal_ids"></param>
    /// <param name="clocks"></param>
    /// <param name="values"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeClockAndIDContaintsRange( int track, int[] internal_ids, int[] clocks, VsqID[] values ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeClockAndIDContaintsRange;
        int count = internal_ids.length;
        command.Args = new Object[4];
        command.Args[0] = track;
        command.Args[1] = (int[])internal_ids.clone();
        command.Args[2] = (int[])clocks.clone();
        command.Args[3] = (VsqID[])values.clone();
        return command;
    }


    /// <summary>
    /// VsqIDとClockを同時に変更するコマンドを発行します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="internal_id"></param>
    /// <param name="clock"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeClockAndIDContaints( int track, int internal_id, int clock, VsqID value ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeClockAndIDContaints;
        command.Args = new Object[4];
        command.Args[0] = track;
        command.Args[1] = internal_id;
        command.Args[2] = clock;
        command.Args[3] = (VsqID)value.clone();
        return command;
    }


    /// <summary>
    /// VsqIDの内容を変更するコマンドを発行します。
    /// </summary>
    /// <param name="track"></param>
    /// <param name="internal_ids"></param>
    /// <param name="values"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeIDContaintsRange( int track, int[] internal_ids, VsqID[] values ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeIDContaintsRange;
        command.Args = new Object[3];
        command.Args[0] = track;
        command.Args[1] = (int[])internal_ids.clone();
        VsqID[] list = new VsqID[values.length];
        for ( int i = 0; i < values.length; i++ ) {
            list[i] = (VsqID)values[i].clone();
        }
        command.Args[2] = list;
        return command;
    }


    /// <summary>
    /// VsqIDの内容を変更するコマンドを発行します。
    /// </summary>
    /// <param name="track"></param>
    /// <param name="internal_id"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeIDContaints( int track, int internal_id, VsqID value ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeIDContaints;
        command.Args = new Object[3];
        command.Args[0] = track;
        command.Args[1] = internal_id;
        command.Args[2] = (VsqID)value.clone();
        return command;
    }


    /// <summary>
    /// ノートの長さを変更するコマンドを発行します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="internal_id"></param>
    /// <param name="new_clock"></param>
    /// <param name="new_length"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeClockAndLength( int track, int internal_id, int new_clock, int new_length ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeClockAndLength;
        command.Args = new Object[4];
        command.Args[0] = track;
        command.Args[1] = internal_id;
        command.Args[2] = new_clock;
        command.Args[3] = new_length;
        return command;
    }


    /// <summary>
    /// ノートの長さを変更するコマンドを発行します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="internal_id"></param>
    /// <param name="new_length"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeLength( int track, int internal_id, int new_length ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeLength;
        command.Args = new Object[3];
        command.Args[0] = track;
        command.Args[1] = internal_id;
        command.Args[2] = new_length;
        return command;
    }


    /// <summary>
    /// 指定したトラックの，音符のベロシティ(VEL)を変更するコマンドを発行します．
    /// リストvelocityには，音符を指定するInteralIDと，変更したいベロシティの値のペアを登録します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="velocity"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeVelocity( int track, Vector<KeyValuePair<Integer, Integer>> velocity ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeVelocity;
        command.Args = new Object[2];
        command.Args[0] = track;
        Vector<KeyValuePair<Integer, Integer>> list = new Vector<KeyValuePair<Integer, Integer>>();
        for ( Iterator itr = velocity.iterator(); itr.hasNext();) {
            KeyValuePair<Integer, Integer> item = (KeyValuePair<Integer, Integer>)itr.next();
            list.add( new KeyValuePair<Integer, Integer>( item.Key, item.Value ) );
        }
        command.Args[1] = list;
        return command;
    }


    /// <summary>
    /// vsqファイルのカーブを編集するコマンドを発行します．
    /// </summary>
    /// <param name="track"></param>
    /// <param name="target"></param>
    /// <param name="edit"></param>
    /// <returns></returns>
    public static VsqCommand GCommandTrackEditCurve( int track, VsqCurveType target, Vector<BPPair> edit ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.TrackEditCurve;
        command.Args = new Object[5];
        command.Args[0] = track;
        command.Args[1] = target;
        Vector<BPPair> copied = new Vector<BPPair>();
        for ( Iterator itr = edit.iterator(); itr.hasNext();) {
            BPPair item = (BPPair)itr.next();
            copied.add( item );
        }
        command.Args[2] = copied;
        return command;
    }

    public static VsqCommand GCommandTrackEditCurveRange( int track, VsqCurveType[] targets, Vector<Vector<BPPair>> edits ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.TrackEditCurveRange;
        command.Args = new Object[3];
        command.Args[0] = track;
        command.Args[1] = (VsqCurveType[])targets.clone();
        Vector<Vector<BPPair>> cpy = new Vector<Vector<BPPair>>(targets.length);
        for ( int i = 0; i < edits.size(); i++ ) {
            Vector<BPPair> copied = new Vector<BPPair>();
            for ( Iterator itr = edits.get(i).iterator(); itr.hasNext();) {
                BPPair item = (BPPair)itr.next();
                copied.add( new BPPair( item.Clock, item.Value ) );
            }
            cpy.set( i, copied );
        }
        command.Args[2] = cpy;
        return command;
    }

    /// <summary>
    /// 特定位置のイベントの歌詞と発音記号を変更するコマンドを発行します。
    /// </summary>
    /// <param name="track"></param>
    /// <param name="internal_id"></param>
    /// <param name="phrase"></param>
    /// <param name="phonetic_symbol"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeLyric( int track, int internal_id, String phrase, String phonetic_symbol, boolean protect_symbol ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeLyric;
        command.Args = new Object[5];
        command.Args[0] = track;
        command.Args[1] = internal_id;
        command.Args[2] = phrase;
        command.Args[3] = phonetic_symbol;
        command.Args[4] = protect_symbol;
        return command;
    }


    /// <summary>
    /// ノートのクロック位置を変更するコマンドを発行します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="internal_id"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeClock( int track, int internal_id, int value ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeClock;
        command.Args = new Object[3];
        command.Args[0] = track;
        command.Args[1] = internal_id;
        command.Args[2] = value;
        return command;
    }

    public static VsqCommand GCommandEventDeleteRange( int track, int[] internal_ids ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventDeleteRange;
        command.Args = new Object[2];
        command.Args[0] = (int[])internal_ids.clone();
        command.Args[1] = track;
        return command;
    }


    /// <summary>
    /// ノートを削除するコマンドを発行します
    /// </summary>
    /// <param name="clock"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventDelete( int track, int internal_id ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventDelete;
        command.Args = new Object[2];
        command.Args[1] = track;
        command.Args[0] = internal_id;
        return command;
    }

    public static VsqCommand GCommandEventAddRange( int track, VsqEvent[] items ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventAddRange;
        command.Args = new Object[2];
        command.Args[0] = track;
        command.Args[1] = (VsqEvent[])items.clone();
        return command;
    }


    /// <summary>
    /// ノートを追加するコマンドを発行します。
    /// </summary>
    /// <param name="track"></param>
    /// <param name="item"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventAdd( int track, VsqEvent item ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventAdd;
        command.Args = new Object[2];
        command.Args[0] = track;
        command.Args[1] = (VsqEvent)item.clone();
        return command;
    }


    /// <summary>
    /// ノートの音程を変更するコマンドを発行します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="internal_id"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeNote( int track, int internal_id, int note ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeNote;
        command.Args = new Object[3];
        command.Args[0] = track;
        command.Args[1] = internal_id;
        command.Args[2] = note;
        return command;
    }


    /// <summary>
    /// ノートの音程とクロックを変更するコマンドを発行します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="internal_id"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static VsqCommand GCommandEventChangeClockAndNote( int track, int internal_id, int clock, int note ) {
        VsqCommand command = new VsqCommand();
        command.Type = VsqCommandType.EventChangeClockAndNote;
        command.Args = new Object[4];
        command.Args[0] = track;
        command.Args[1] = internal_id;
        command.Args[2] = clock;
        command.Args[3] = note;
        return command;
    }

    public Object clone() {
        VsqFile ret = new VsqFile();
        ret.m_tracks = new Vector<VsqTrack>();
        for ( int i = 0; i < m_tracks.size(); i++ ) {
            ret.m_tracks.add( (VsqTrack)m_tracks.get( i ).clone() );
        }
        ret.m_tempo_table = new Vector<TempoTableEntry>();
        for ( int i = 0; i < m_tempo_table.size(); i++ ) {
            ret.m_tempo_table.add( (TempoTableEntry)m_tempo_table.get( i ).clone() );
        }
        ret.m_timesig_table = new Vector<TimeSigTableEntry>();
        for ( int i = 0; i < m_timesig_table.size(); i++ ) {
            ret.m_timesig_table.add( (TimeSigTableEntry)m_timesig_table.get( i ).clone() );
        }
        ret.m_tpq = m_tpq;
        ret.TotalClocks = TotalClocks;
        ret.m_base_tempo = m_base_tempo;
        ret.Master = (VsqMaster)Master.clone();
        ret.Mixer = (VsqMixer)Mixer.clone();
        ret.m_premeasure_clocks = m_premeasure_clocks;
        return ret;
    }

    private VsqFile() {
        VsqUtil.LoadSingerConfigs();
    }


    /// <summary>
    /// 小節の区切りを順次返すEnumerator。
    /// </summary>
    /// <returns></returns>
    public Iterator<VsqBarLineType> getVsqBarLineIterator( int end_clock ) {
    }


    /// <summary>
    /// 基本テンポ値を取得します
    /// </summary>
    public int getBaseTempo() {
        return m_base_tempo;
    }


    /// <summary>
    /// プリメジャー値を取得または設定します。
    /// </summary>
    public int getPreMeasure() {
        //return m_tracks[1].MetaText.master.PreMeasure;
        return Master.PreMeasure;
    }

    public int getPreMeasureClocks() {
        return m_premeasure_clocks;
    }


    /// <summary>
    /// プリメジャーの長さ(クロック)を計算します。
    /// </summary>
    private int GetPreMeasureInClock() {
        int pre_measure = Master.PreMeasure;
        int last_bar_count = m_timesig_table.get(0).BarCount;
        int last_clock = m_timesig_table.get( 0).Clock;
        int last_denominator = m_timesig_table.get(0).Denominator;
        int last_numerator = m_timesig_table.get(0).Numerator;
        for ( int i = 1; i < m_timesig_table.size(); i++ ) {
            if ( m_timesig_table.get( i ).BarCount >= pre_measure ) {
                break;
            } else {
                last_bar_count = m_timesig_table.get( i ).BarCount;
                last_clock = m_timesig_table.get( i ).Clock;
                last_denominator = m_timesig_table.get( i ).Denominator;
                last_numerator = m_timesig_table.get( i ).Numerator;
            }
        }

        int remained = pre_measure - last_bar_count;//プリメジャーの終わりまでの残り小節数

        return last_clock + remained * last_numerator * 480 * 4 / last_denominator;
    }


    /// <summary>
    /// 指定したクロックにおける、clock=0からの演奏経過時間(sec)
    /// </summary>
    /// <param name="clock"></param>
    /// <returns></returns>
    public double SecFromClock( int clock ) {
        for ( int i = m_tempo_table.size() - 1; i >= 0; i-- ) {
            if ( m_tempo_table.get( i ).Clock < clock ) {
                double init = m_tempo_table.get( i ).Time;
                int dclock = clock - m_tempo_table.get( i ).Clock;
                double sec_per_clock1 = m_tempo_table.get( i ).Tempo * 1e-6 / 480.0;
                return init + dclock * sec_per_clock1;
            }
        }

        double sec_per_clock = m_base_tempo * 1e-6 / 480.0;
        return clock * sec_per_clock;
    }


    /// <summary>
    /// 指定した時刻における、クロックを取得します
    /// </summary>
    /// <param name="time"></param>
    /// <returns></returns>
    public float ClockFromSec( float time ) {
        // timeにおけるテンポを取得
        int tempo = m_base_tempo;
        float base_clock = 0;
        float base_time = 0f;
        if ( m_tempo_table.size() == 0 ) {
            tempo = m_base_tempo;
            base_clock = 0;
            base_time = 0f;
        } else if ( m_tempo_table.size() == 1 ) {
            tempo = m_tempo_table.get(0).Tempo;
            base_clock = m_tempo_table.get(0).Clock;
            base_time = (float)m_tempo_table.get(0).Time;
        } else {
            for ( int i = m_tempo_table.size() - 1; i >= 0; i-- ) {
                if ( m_tempo_table.get( i ).Time < time ) {
                    return m_tempo_table.get( i ).Clock + (time - (float)m_tempo_table.get( i ).Time) * m_tpq * 1000000.0f / (float)m_tempo_table.get( i ).Tempo;
                }
            }
        }
        float dt = time - base_time;
        return base_clock + dt * m_tpq * 1000000.0f / (float)tempo;
    }


    /// <summary>
    /// 指定したクロックにおける拍子を取得します
    /// </summary>
    /// <param name="clock"></param>
    /// <param name="numerator"></param>
    /// <param name="denominator"></param>
    public void getTimesigAt( int clock, Integer numerator, Integer denominator ) {
        int index = 0;
        for ( int i = m_timesig_table.size() - 1; i >= 0; i-- ) {
            index = i;
            if ( m_timesig_table.get( i ).Clock <= clock ) {
                break;
            }
        }
        numerator = m_timesig_table.get( index ).Numerator;
        denominator = m_timesig_table.get( index ).Denominator;
    }


    /// <summary>
    /// 指定したクロックにおけるテンポを取得します。
    /// </summary>
    /// <param name="clock"></param>
    /// <returns></returns>
    public int getTempoAt( int clock ) {
        int index = 0;
        for ( int i = m_tempo_table.size() - 1; i >= 0; i-- ) {
            index = i;
            if ( m_tempo_table.get( i ).Clock <= clock ) {
                break;
            }
        }
        return m_tempo_table.get( index ).Tempo;
    }


    /// <summary>
    /// 指定した小節の開始クロックを調べます。ここで使用する小節数は、プリメジャーを考慮しない。即ち、曲頭の小節が0である。
    /// </summary>
    /// <param name="bar_count"></param>
    /// <returns></returns>
    public int getClockFromBarCount( int bar_count ) {
        int index = 0;
        for ( int i = m_timesig_table.size() - 1; i >= 0; i-- ) {
            index = i;
            if ( m_timesig_table.get( i ).BarCount <= bar_count ) {
                break;
            }
        }
        int numerator = m_timesig_table.get( index ).Numerator;
        int denominator = m_timesig_table.get( index ).Denominator;
        int init_clock = m_timesig_table.get( index ).Clock;
        int init_bar_count = m_timesig_table.get( index ).BarCount;
        int clock_per_bar = numerator * 480 * 4 / denominator;
        return init_clock + (bar_count - init_bar_count) * clock_per_bar;
    }


    /// <summary>
    /// 指定したクロックが、曲頭から何小節目に属しているかを調べます。ここで使用する小節数は、プリメジャーを考慮しない。即ち、曲頭の小節が0である。
    /// </summary>
    /// <param name="clock"></param>
    /// <returns></returns>
    public int getBarCountFromClock( int clock ) {
        int index = 0;
        for ( int i = m_timesig_table.size() - 1; i >= 0; i-- ) {
            index = i;
            if ( m_timesig_table.get( i ).Clock <= clock ) {
                break;
            }
        }
        int bar_count = 0;
        if ( index >= 0 ) {
            int last_clock = m_timesig_table.get( index ).Clock;
            int t_bar_count = m_timesig_table.get( index ).BarCount;
            int numerator = m_timesig_table.get( index ).Numerator;
            int denominator = m_timesig_table.get( index ).Denominator;
            int clock_per_bar = numerator * 480 * 4 / denominator;
            bar_count = t_bar_count + (clock - last_clock) / clock_per_bar;
        }
        return bar_count;
    }

    public int getTickPerQuarter() {
        return m_tpq;
    }

    public Vector<TempoTableEntry> getTempoTable() {
        return m_tempo_table;
    }

    public Vector<TimeSigTableEntry> getm_timesig_table() {
        return m_timesig_table;
    }

    public VsqTrack getTracks( int track ) {
        return m_tracks.get( track );
    }

    public void dispose() {
        if ( m_tracks != null ) {
            m_tracks.clear();
        }
        if ( m_tempo_table != null ) {
            m_tempo_table.clear();
        }
        m_tracks = null;
        m_tempo_table = null;
    }


    /// <summary>
    /// 空のvsqファイルを構築します
    /// </summary>
    /// <param name="pre_measure"></param>
    /// <param name="numerator"></param>
    /// <param name="denominator"></param>
    /// <param name="tempo"></param>
    public VsqFile( int default_singer_program_change, int pre_measure, int numerator, int denominator, int tempo ) {
        VsqUtil.LoadSingerConfigs();
        TotalClocks = (long)(pre_measure * 480 * 4 / denominator * numerator);
        m_tpq = 480;

        m_tracks = new Vector<VsqTrack>();
        m_tracks.add( new VsqTrack( tempo, numerator, denominator ) );
        String singer = "Miku";
        SingerConfig sc = VsqUtil.GetSingerInfo( default_singer_program_change );
        if ( sc != null ) {
            singer = sc.VOICENAME;
        }
        m_tracks.add( new VsqTrack( "Voice1", singer ) );
        Master = new VsqMaster( pre_measure );

        Mixer = new VsqMixer( 0, 0, 0, 0 );
        Mixer.Slave.add( new VsqMixerEntry( 0, 0, 0, 0 ) );
//            m_mixer.Slave.Add( new VsqMixerEntry( 0, 0, 0, 0 ) );
        //m_mixer.m_tracks = 1;
        m_timesig_table = new Vector<TimeSigTableEntry>();
        m_timesig_table.add( new TimeSigTableEntry( 0, numerator, denominator, 0 ) );
        m_tempo_table = new Vector<TempoTableEntry>();
        m_tempo_table.add( new TempoTableEntry( 0, tempo, 0.0 ) );
        m_base_tempo = tempo;
        m_premeasure_clocks = GetPreMeasureInClock();
    }


    /// <summary>
    /// vsqファイルからのコンストラクタ
    /// </summary>
    /// <param name="_fpath"></param>
    public VsqFile( String _fpath ) {
        VsqUtil.LoadSingerConfigs();
        m_tempo_table = new Vector<TempoTableEntry>();
        m_timesig_table = new Vector<TimeSigTableEntry>();
        m_tpq = 480;
        //tpq_sec = 480.0 * 1000000.0;

        // SMFをコンバートしたテキストファイルを作成
        TextMemoryStream tms = new TextMemoryStream();
        SMFReader reader = new SMFReader( _fpath );
        String[] lines0 = reader.Lines;
        for ( int i = 0; i < lines0.length; i++ ) {
            String line = lines0[i];
            tms.writeLine( line );
        }

        // テキストファイルから、トラックごとに切り分け
        tms.rewind();
        m_tracks = new Vector<VsqTrack>();

        // ヘッダからトラック数、TPQを取得
        String[] spl = tms.readLine().split( " " );
        int trackNum = Integer.parseInt( spl[2] );
        m_tpq = Integer.parseInt( spl[3] );
        //tpq_sec = TickPerQuarter * 1000000.0;
        for ( int i = 0; i < trackNum; i++ ) {
            Vector<String> lines = new Vector<String>();
            String line;
            while ( (line = tms.readLine()) != null ) {
                if ( line.startsWith( "TrkEnd" ) ) {
                    break;
                }
                if ( line != "MTrk" ) {
                    lines.add( line );
                }
            }
            m_tracks.add( new VsqTrack( lines ) );
        }

        Master = (VsqMaster)m_tracks.get(1).getMaster().clone();
        Mixer = (VsqMixer)m_tracks.get(1).getMixer().clone();
        m_tracks.get(1).setMaster(null);
        m_tracks.get(1).setMixer(null);

        int master_track = -1;
        for ( int i = 0; i < m_tracks.size(); i++ ) {
            if ( m_tracks.get( i ).getName().equals("Master Track") ) {
                master_track = i;
                break;
            }
        }

        int prev_tempo;
        int prev_index;
        double prev_time;
        if ( master_track >= 0 ) {
            //TempoListの作成 
            Vector<MidiEvent> midi_event = m_tracks.get( master_track ).getTempoList();
            // とりあえずtempo_tableに格納
            prev_tempo = midi_event.get(0).intValue[0];
            m_base_tempo = prev_tempo;
            prev_index = 0;
            m_tempo_table.add( new TempoTableEntry( midi_event.get(0).index, midi_event.get(0).intValue[0], 0.0 ) );
            double thistime;
            prev_time = 0.0;
            for ( int j = 1; j < midi_event.size(); j++ ) {
                int current_tempo = midi_event.get(j).intValue[0];
                int current_index = midi_event.get(j).index;
                thistime = prev_time + (double)(prev_tempo) * (double)(current_index - prev_index) / (m_tpq * 1000000.0);
                m_tempo_table.add( new TempoTableEntry( current_index, current_tempo, thistime ) );
                prev_tempo = current_tempo;
                prev_index = current_index;
                prev_time = thistime;
            }
            Collections.sort( m_tempo_table );

            // m_timesig_tableの作成
            Vector<MidiEvent> time_sigs = m_tracks.get(master_track).getTimeSigList();
            m_timesig_table.add( new TimeSigTableEntry( 0, time_sigs.get(0).intValue[0], time_sigs.get(0).intValue[1], 0 ) );
            for ( int j = 1; j < time_sigs.size(); j++ ) {
                int numerator = m_timesig_table.get(j - 1).Numerator;
                int denominator = m_timesig_table.get(j - 1).Denominator;
                int clock = m_timesig_table.get(j - 1).Clock;
                int bar_count = m_timesig_table.get(j - 1).BarCount;

                int dif = 480 * 4 / denominator * numerator;//1小節が何クロックか？

                bar_count += (time_sigs.get(j).index - clock) / dif;
                m_timesig_table.add( new TimeSigTableEntry( time_sigs.get(j).index, time_sigs.get(j).intValue[0], time_sigs.get(j).intValue[1], bar_count ) );
            }



            // 曲の長さを計算
            updateTempoInfo();
            updateTimesigInfo();
            m_premeasure_clocks = GetPreMeasureInClock();
            updateTotalClocks();
        }
    }


    /// <summary>
    /// m_timesig_tableの[*].Clockの部分を更新します
    /// </summary>
    public void updateTimesigInfo() {
        if ( m_timesig_table.get(0).Clock != 0 ) {
            throw new Exception( "initial timesig does not found" );
        }
        m_timesig_table.get(0).Clock = 0;
        Collections.sort(m_timesig_table);
        for ( int j = 1; j < m_timesig_table.size(); j++ ) {
            int numerator = m_timesig_table.get(j - 1).Numerator;
            int denominator = m_timesig_table.get(j - 1).Denominator;
            int clock = m_timesig_table.get(j - 1).Clock;
            int bar_count = m_timesig_table.get(j - 1).BarCount;
            int dif = 480 * 4 / denominator * numerator;//1小節が何クロックか？

            clock += (m_timesig_table.get(j).BarCount - bar_count) * dif;
            m_timesig_table.get(j).Clock = clock;
        }
        m_premeasure_clocks = GetPreMeasureInClock();

    }


    /// <summary>
    /// TempoTableの[*].Timeの部分を更新します
    /// </summary>
    public void updateTempoInfo() {
        if ( m_tempo_table.size() == 0 ) {
            m_tempo_table.add( new TempoTableEntry( 0, getBaseTempo(), 0.0 ) );
        }
        Collections.sort(     m_tempo_table);
        if ( m_tempo_table.get(0).Clock != 0 ) {
            m_tempo_table.get(0).Time = (double)getBaseTempo() * (double)m_tempo_table.get(0).Clock / (getTickPerQuarter() * 1000000.0);
        } else {
            m_tempo_table.get(0).Time = 0.0;
        }
        double prev_time = m_tempo_table.get(0).Time;
        int prev_clock = m_tempo_table.get(0).Clock;
        int prev_tempo = m_tempo_table.get(0).Tempo;
        double inv_tpq_sec = 1.0 / (getTickPerQuarter() * 1000000.0);
        for ( int i = 1; i < m_tempo_table.size(); i++ ) {
            m_tempo_table.get( i ).Time = prev_time + (double)prev_tempo * (double)(m_tempo_table.get( i ).Clock - prev_clock) * inv_tpq_sec;
            prev_time = m_tempo_table.get( i ).Time;
            prev_tempo = m_tempo_table.get( i ).Tempo;
            prev_clock = m_tempo_table.get( i ).Clock;
        }
    }


    /// <summary>
    /// VsqFile.Executeの実行直後などに、m_total_clocksの値を更新する
    /// </summary>
    public void updateTotalClocks() {
        long max = m_premeasure_clocks;
        for ( int i = 1; i < m_tracks.size(); i++ ) {
            VsqTrack track = m_tracks.get( i );
            for ( Iterator itr = track.getEvents().iterator(); itr.hasNext();) {
                VsqEvent ve = (VsqEvent)itr.next();
                max = Math.max( max, (long)(ve.Clock + ve.ID.Length) );
            }
            VsqCurveType[] values = VsqCurveType.values();
            for ( int j = 0; j < values.length; j++ ) {
                VsqCurveType vct = values[j];
                if ( vct == VsqCurveType.VEL ) {
                    continue;
                }
                if ( track.getVsqBPList( vct).size() > 0 ) {
                    Set<Integer> keys_list = track.getVsqBPList( vct ).keyClockSet();
                    long last_key = 0;
                    for( Iterator itr = keys_list.iterator(); itr.hasNext(); ){
                        last_key = Math.max( last_key, (Integer)itr.next() );
                    }
                    max = Math.max( max, last_key );
                }
            }
        }
        TotalClocks = max;
    }


    /// <summary>
    /// 曲の長さを取得する。(sec)
    /// </summary>
    public double getTotalSec() {
        return SecFromClock( (int)TotalClocks );
    }


    /// <summary>
    /// 指定された番号のトラックに含まれる歌詞を指定されたファイルに出力します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="fpath"></param>
    public void printLyricTable( int track, String fpath ) {
        BufferedWriter sw = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( fpath) ) );
                for ( int i = 0; i < m_tracks.get( track ).getEvents().size(); i++ ) {
                int Length;
                // timesignal
                int time_signal = m_tracks.get( track ).getEvents().get( i ).Clock;
                // イベントで指定されたIDがLyricであった場合
                if ( m_tracks.get( track ).getEvents().get( i ).ID.type == VsqIDType.Anote ) {
                    // 発音長を取得
                    Length = m_tracks.get( track ).getEvents().get( i ).ID.Length;

                    // tempo_tableから、発音開始時のtempoを取得
                    int last = m_tempo_table.size() - 1;
                    int tempo = m_tempo_table.get(last).Tempo;
                    int prev_index = m_tempo_table.get(last).Clock;
                    double prev_time = m_tempo_table.get(last).Time;
                    for ( int j = 1; j < m_tempo_table.size(); j++ ) {
                        if ( m_tempo_table.get(j).Clock > time_signal ) {
                            tempo = m_tempo_table.get(j - 1).Tempo;
                            prev_index = m_tempo_table.get(j - 1).Clock;
                            prev_time = m_tempo_table.get(j - 1).Time;
                            break;
                        }
                    }
                    int current_index = m_tracks.get( track ).getEvents().get( i ).Clock;
                    double start_time = prev_time + (double)(current_index - prev_index) * (double)tempo / (m_tpq * 1000000.0);
                    // TODO: 単純に + Lengthしただけではまずいはず。要検討
                    double end_time = start_time + ((double)Length) * ((double)tempo) / (m_tpq * 1000000.0);
                    DecimalFormat df = new DecimalFormat("0.000000" );
                    sw.write(  m_tracks.get( track ).getEvents().get( i ).Clock + "," +
                            df.format( start_time) + "," +
                            df.format( end_time) + "," +
                            m_tracks.get( track ).getEvents().get( i ).ID.LyricHandle.L0.Phrase + "," +
                            m_tracks.get( track ).getEvents().get( i ).ID.LyricHandle.L0.getPhoneticSymbol() + "\n" );
                }

            }
        
    }

    private void printTrack( VsqTrack item, RandomAccessFile fs, int msPreSend ) {
        //VsqTrack item = m_tracks.get( track );
        String _NL = "" + (char)0x0a;
        //ヘッダ
        fs.write( _MTRK, 0, 4 );
        //データ長。とりあえず0
        fs.write( new byte[] {0x00, 0x00, 0x00, 0x00}, 0, 4 );
        long first_position = fs.getFilePointer();
        //トラック名
        writeFlexibleLengthUnsignedLong( fs, 0x00 );//デルタタイム

        fs.write( 0xff );//ステータスタイプ

        fs.write( 0x03 );//イベントタイプSequence/Track Name

        char[] seq_name = Lyric.encode( item.getName() );
        writeFlexibleLengthUnsignedLong( fs, (long)seq_name.length );//seq_nameの文字数

        writeCharArray( fs, seq_name );
        //Meta Textを準備
        TextMemoryStream sr = new TextMemoryStream();
        item.printMetaText( sr, false, TotalClocks + 120, GetPreMeasureInClock() );
        sr.rewind();
        int line_count = -1;
        String tmp = "";
        if ( sr.peek() >= 0 ) {
            tmp = sr.readLine();
            char[] line_char;
            String line = "";
            while ( sr.peek() >= 0 ) {
                line = sr.readLine();
                tmp += _NL + line;
                while ( (tmp + getLinePrefix( line_count )).length() >= 127 ) {
                    line_count++;
                    tmp = getLinePrefix( line_count ) + tmp;
                    String work = tmp.substring( 0, 127 );
                    tmp = tmp.substring( 127 );
                    line_char = work.toCharArray();
                    writeFlexibleLengthUnsignedLong( fs, 0x00 );
                    fs.write( 0xff );//ステータスタイプ

                    fs.write( 0x01 );//イベントタイプMeta Text

                    writeFlexibleLengthUnsignedLong( fs, (long)line_char.length );//データ長

                    writeCharArray( fs, line_char );//メタテキスト本体

                }
            }
            // 残りを出力
            line_count++;
            tmp = getLinePrefix( line_count ) + tmp + _NL;
            while ( tmp.length() > 127 ) {
                String work = tmp.substring( 0, 127 );
                tmp = tmp.substring( 127 );
                line_char = work.toCharArray();
                writeFlexibleLengthUnsignedLong( fs, 0x00 );
                fs.write( 0xff );
                fs.write( 0x01 );
                writeFlexibleLengthUnsignedLong( fs, line_char.length );
                writeCharArray( fs, line_char );
                line_count++;
                tmp = getLinePrefix( line_count );
            }
            line_char = tmp.toCharArray();
            writeFlexibleLengthUnsignedLong( fs, 0x00 );
            fs.write( 0xff );
            fs.write( 0x01 );
            writeFlexibleLengthUnsignedLong( fs, (long)line_char.length );
            writeCharArray( fs, line_char );
        }


        int last = 0;
        VsqNrpn[] data = generateNRPN( item, msPreSend );

        NrpnData[] nrpns = VsqNrpn.convert( data );
        for ( int i = 0; i < nrpns.length; i++ ) {
            writeFlexibleLengthUnsignedLong( fs, (long)(nrpns[i].Clock - last) );
            fs.write( 0xb0 );
            fs.write( nrpns[i].Parameter );
            fs.write( nrpns[i].Value );
            last = nrpns[i].Clock;
        }

        //トラックエンド
        VsqEvent last_event = item.getEvents().get(item.getEvents().size() - 1);
        int last_clock = last_event.Clock + last_event.ID.Length;
        writeFlexibleLengthUnsignedLong( fs, (long)last_clock );
        fs.write( 0xff );
        fs.write( 0xf2 );
        fs.write( 0x00 );
        long pos = fs.getFilePointer();
        fs.seek( first_position - 4 );
        writeUnsignedInt( fs, pos - first_position );
        fs.seek( pos  );
    }


    /// <summary>
    /// 指定したクロックにおけるプリセンド・クロックを取得します
    /// </summary>
    /// <param name="clock"></param>
    /// <param name="msPreSend"></param>
    /// <returns></returns>
    public int getPresendClockAt( int clock, int msPreSend ) {
        double clock_msec = SecFromClock( clock ) * 1000.0;
        float draft_clock_sec = (float)(clock_msec - msPreSend) / 1000.0f;
        int draft_clock = (int)Math.floor( ClockFromSec( draft_clock_sec ) );
        return clock - draft_clock;
    }


    /// <summary>
    /// 指定したトラックから、Expression(DYN)のNRPNリストを作成します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="msPreSend"></param>
    /// <returns></returns>
    public VsqNrpn[] generateExpressionNRPN( VsqTrack track, int msPreSend ) {
        return generateExpressionNRPN( track, msPreSend, 0, (int)TotalClocks, 0 );
    }

    public VsqNrpn[] generateExpressionNRPN( VsqTrack track, int msPreSend, int clStart, int clEnd, int t_temp_premeasure ) {
        Vector<VsqNrpn> ret = new Vector<VsqNrpn>();
        //SortedList<int, int> list = track[VsqCurveType.DYN].Vector;
        int count = track.getVsqBPList( VsqCurveType.DYN ).size();
        Byte delay0, delay1;
        getMsbAndLsb( msPreSend, delay0, delay1 );
        int start_clock = clStart < m_premeasure_clocks ? m_premeasure_clocks : clStart;
        int start_value = track.getVsqBPList(VsqCurveType.DYN).get(start_clock);
        VsqNrpn add0 = new VsqNrpn( start_clock - getPresendClockAt( start_clock, msPreSend ) - clStart + t_temp_premeasure, 0x6300, (byte)0x00, (byte)0x00 );
        add0.append( 0x6301, delay0, delay1 );
        add0.append( 0x6302, (byte)start_value );
        ret.add( add0 );
        Integer[] keys = track.getVsqBPList(VsqCurveType.DYN).keyClockSet().toArray( new Integer[0] );
        for ( int i = 1; i < keys.length; i++ ) {
            if ( keys[i] > clEnd ) {
                break;
            }
            if ( keys[i] > start_clock ) {
                VsqNrpn add = new VsqNrpn( keys[i] - getPresendClockAt( keys[i], msPreSend ) - clStart + t_temp_premeasure, 0x6302, (byte)track.getVsqBPList(VsqCurveType.DYN).get(keys[i]) );
                ret.add( add );
            }
        }
        return ret.toArray( new VsqNrpn[0] );
    }


    /// <summary>
    /// 先頭に記録されるNRPNを作成します
    /// </summary>
    /// <returns></returns>
    public static VsqNrpn generateHeaderNRPN() {
        VsqNrpn ret = new VsqNrpn( 0, 0x6000, (byte)0x00, (byte)0x00 ); // 0x00       (NRPN 0x6000 BNK device number = 0x00)

        ret.append( 0x6001, (byte)0x00, (byte)0x00 ); // 0x00       (NRPN 0x6001 BNK DELAY delay = 0x0000)
        //ret.append( 0x6002, 0x00 ); // 0x00       (NRPN 0x6002 LANGUAGE TYPE language type = 0x00)
        //ret.append( 0x5302, 0x00 ); // 0x00       (NRPN 0x5302 VOICE TYPE voice type = 0x00)

        return ret;
    }

    public VsqNrpn[] generateSingerNRPN( VsqEvent ve, int msPreSend, int clShift ) {
        int clock = ve.Clock;

        double clock_msec = SecFromClock( clock ) * 1000.0;

        int ttempo = getTempoAt( clock );
        double tempo = 6e7 / ttempo;
        //double sStart = SecFromClock( ve.Clock );
        double msEnd = SecFromClock( ve.Clock + ve.ID.Length ) * 1000.0;
        int duration = (int)Math.ceil( msEnd - clock_msec );
        Byte duration0, duration1;
        getMsbAndLsb( duration, duration0, duration1 );
        Byte delay0, delay1;
        getMsbAndLsb( (int)msPreSend, delay0, delay1 );
        Vector<VsqNrpn> ret = new Vector<VsqNrpn>();

        VsqNrpn add = new VsqNrpn( clock - getPresendClockAt( clock, msPreSend ) - clShift, 0x6002, (byte)ve.ID.IconHandle.Language );
        add.append( 0x5302, (byte)ve.ID.IconHandle.Program );
        return new VsqNrpn[] {add};
    }


    /// <summary>
    /// 指定したVsqEventを表現するNRPNを作成します
    /// </summary>
    /// <param name="ve"></param>
    /// <param name="msPreSend"></param>
    /// <param name="note_loc"></param>
    /// <returns></returns>
    public VsqNrpn[] generateNoteNRPN( VsqEvent ve, int msPreSend, byte note_loc ) {
        return generateNoteNRPN( ve, msPreSend, note_loc, 0 );
    }

    public VsqNrpn[] generateNoteNRPN( VsqEvent ve, int msPreSend, byte note_loc, int clShift ) {
        int clock = ve.Clock;

        double clock_msec = SecFromClock( clock ) * 1000.0;

        int ttempo = getTempoAt( clock );
        double tempo = 6e7 / ttempo;
        //double sStart = SecFromClock( ve.Clock );
        double msEnd = SecFromClock( ve.Clock + ve.ID.Length ) * 1000.0;
        int duration = (int)Math.ceil( msEnd - clock_msec );

        Byte duration0, duration1;
        getMsbAndLsb( duration, duration0, duration1 );
        Byte delay0, delay1;
        getMsbAndLsb( msPreSend, delay0, delay1 );
        Vector<VsqNrpn> ret = new Vector<VsqNrpn>();

        VsqNrpn add = new VsqNrpn( clock - getPresendClockAt( clock, msPreSend ) - clShift, 0x5001, delay0, delay1 );
        add.append( 0x5002, (byte)ve.ID.Note ); // Note number

        add.append( 0x5003, (byte)ve.ID.Dynamics ); // Velocity

        add.append( 0x5004, duration0, duration1 ); // Note duration(LSB)

        add.append( 0x5005, note_loc ); // Note Location

        if ( ve.ID.VibratoHandle != null ) {
            add.append( 0x500c, (byte)0x00, (byte)0x00 );
            int vibrato_type = VibratoTypeUtil.FromIconID( ve.ID.VibratoHandle.IconID ).ordinal();
            int note_length = ve.ID.Length;
            int vibrato_delay = ve.ID.VibratoDelay;
            byte bVibratoDuration = (byte)((float)(note_length - vibrato_delay) / (float)note_length * 127);
            byte bVibratoDelay = (byte)(0x7f - bVibratoDuration);
            add.append( 0x500d, (byte)vibrato_type, bVibratoDuration );
            add.append( 0x500e, bVibratoDelay );
        }

        String[] spl = ve.ID.LyricHandle.L0.getPhoneticSymbolList();
        String s = "";
        for ( int j = 0; j < spl.length; j++ ) {
            s += spl[j];
        }
        char[] symbols = s.toCharArray();
        add.append( 0x5012, (byte)symbols.length );// 0x12(Number of phonetic symbols in bytes)

        int count = -1;
        for ( int j = 0; j < spl.length; j++ ) {
            char[] chars = spl[j].toCharArray();
            for ( int k = 0; k < chars.length; k++ ) {
                count++;
                if ( k == 0 ) {
                    add.append( ((0x50 << 8) | (byte)(0x13 + count)), (byte)chars[k], (byte)ve.ID.LyricHandle.L0.getConsonantAdjustment()[j] ); // Phonetic symbol j

                } else {
                    add.append( (0x50 << 8) | (byte)(0x13 + count), (byte)chars[k] ); // Phonetic symbol j

                }
            }
        }
        add.append( 0x504f, (byte)0x7f ); // End of phonetic symbols

        add.append( 0x5050, (byte)0x04 );// 0x50(v1mean)

        add.append( 0x5051, (byte)0x08 );// 0x51(d1mean)

        add.append( 0x5052, (byte)0x14 );// 0x52(d1meanFirstNote)

        add.append( 0x5053, (byte)0x1c );// 0x53(d2mean)

        add.append( 0x5054, (byte)0x18 );// 0x54(d4mean)

        add.append( 0x5055, (byte)0x0a ); // 055(pMeanOnsetFirstNote)

        add.append( 0x5056, (byte)0x0c ); // 0x56(vMeanNoteTransition)

        add.append( 0x5057, (byte)0x0c );// 0x57(pMeanEndingNote)

        add.append( 0x5058, (byte)ve.ID.PMbPortamentoUse );// 0x58(AddScoopToUpInternals&AddPortamentoToDownIntervals)

        add.append( 0x5059, (byte)0x32 );// 0x59(changeAfterPeak)

        add.append( 0x505a, (byte)0x32 );// 0x5a(Accent)

        add.append( 0x507f, (byte)0x7f );// 0x7f(Note message continuation)

        ret.add( add );
        return ret.toArray(new VsqNrpn[0]);
    }


    /// <summary>
    /// 指定したトラックのデータから、NRPNのリストを作成します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="msPreSend"></param>
    /// <returns></returns>
    public VsqNrpn[] generateNRPN( VsqTrack track, int msPreSend ) {
        return generateNRPN( track, msPreSend, 0, (int)TotalClocks, 0 );
    }

    public VsqNrpn[] generateNRPN( VsqTrack track, int msPreSend, int clStart, int clEnd, int t_temp_premeasure ) {
        Vector<VsqNrpn> list = new Vector<VsqNrpn>();
        list.add( generateHeaderNRPN() );

        int count = track.getEvents().size();
        int note_start = 0;
        int note_end = track.getEvents().size() - 1;
        for ( int i = 0; i < track.getEvents().size(); i++ ) {
            if ( clStart <= track.getEvents().get( i ).Clock ) {
                note_start = i;
                break;
            }
            note_start = i;
        }
        for ( int i = track.getEvents().size() - 1; i >= 0; i-- ) {
            if ( track.getEvents().get( i ).Clock <= clEnd ) {
                note_end = i;
                break;
            }
        }

        // 最初の歌手を決める
        int singer_event = -1;
        for ( int i = note_start; i >= 0; i-- ) {
            if ( track.getEvents().get( i ).ID.type == VsqIDType.Singer ) {
                singer_event = i;
                break;
            }
        }
        if ( singer_event >= 0 ) { //見つかった場合
            Collections.addAll( list, generateSingerNRPN( track.getEvents().get(singer_event), 0, 0 ) );
        } else {                   //多分ありえないと思うが、歌手が不明の場合。

            list.add( new VsqNrpn( 0, 0x6002, (byte)0 ) );
            list.add( new VsqNrpn( 0, 0x5302, (byte)0 ) );
        }

        for ( int i = note_start; i <= note_end; i++ ) {
            byte note_loc = 0x00;
            if ( i == note_start ) {
                if ( i == note_end ) {
                    note_loc = 0x03;
                } else {
                    note_loc = 0x01;
                }
            } else if ( i == note_end ) {
                note_loc = 0x02;
            }

            if ( track.getEvents().get( i ).ID.type == VsqIDType.Anote ) {
                Collections.addAll( list, generateNoteNRPN(
                        track.getEvents().get( i ),
                        msPreSend,
                        note_loc,
                        clStart - t_temp_premeasure ) );
                Collections.addAll( list, generateVibratoNRPN(
                        track.getEvents().get( i ),
                        msPreSend,
                        clStart - t_temp_premeasure ) );
            } else if ( track.getEvents().get( i ).ID.type == VsqIDType.Singer ) {
                if ( i > note_start ) {
                    Collections.addAll( list, generateSingerNRPN( track.getEvents().get( i ), msPreSend, clStart - t_temp_premeasure ) );
                }
            }
        }
        Collections.addAll( list, generateVoiceChangeParameterNRPN( track, msPreSend, clStart, clEnd, t_temp_premeasure ) );
        Collections.addAll( list, generateExpressionNRPN( track, msPreSend, clStart, clEnd, t_temp_premeasure ) );
        Collections.addAll( list, generatePitchBendNRPN( track, msPreSend, clStart, clEnd, t_temp_premeasure ) );
        Collections.addAll( list, generatePitchBendSensitivityNRPN( track, msPreSend, clStart, clEnd, t_temp_premeasure ) );

        Collections.sort( list );
        Vector<VsqNrpn> merged = new Vector<VsqNrpn>();
        for ( int i = 0; i < list.size(); i++ ) {
            Collections.addAll( merged, list.get( i ).expand() );
        }
        return merged.toArray( new VsqNrpn[0]);
    }


    /// <summary>
    /// 指定したトラックから、PitchBendのNRPNを作成します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="msPreSend"></param>
    /// <returns></returns>
    public VsqNrpn[] generatePitchBendNRPN( VsqTrack track, int msPreSend ) {
        return generatePitchBendNRPN( track, msPreSend, 0, (int)TotalClocks, 0 );
    }

    public VsqNrpn[] generatePitchBendNRPN( VsqTrack track, int msPreSend, int clStart, int clEnd, int t_pre_measure ) {
        Vector<VsqNrpn> ret = new Vector<VsqNrpn>();
        //SortedList<int, int> list = track[VsqCurveType.PIT].Vector;
        Integer[] keys = track.getVsqBPList(VsqCurveType.PIT).keyClockSet().toArray( new Integer[0]);
        int count = keys.length;
        byte delay0, delay1;
        getMsbAndLsb( msPreSend, delay0, delay1 );
        int start_clock = clStart < m_premeasure_clocks ? m_premeasure_clocks : clStart;
        VsqNrpn add0 = new VsqNrpn( start_clock - getPresendClockAt( start_clock, msPreSend ) - clStart + t_pre_measure, 0x5400, (byte)0x00, (byte)0x00 );
        add0.append( 0x5401, delay0, delay1 );

        int start_value = track.getVsqBPList(VsqCurveType.PIT).get(start_clock) + 0x2000;
        byte start_value0, start_value1;
        getMsbAndLsb( start_value, start_value0, start_value1 );
        add0.append( 0x5402, start_value0, start_value1 );
        ret.add( add0 );

        for ( int i = 1; i < keys.length; i++ ) {
            if ( keys[i] > clEnd ) {
                break;
            }
            if ( keys[i] > start_clock ) {
                int value = track.getVsqBPList(VsqCurveType.PIT).get(keys[i])+ 0x2000;
                byte value0, value1;
                getMsbAndLsb( value, value0, value1 );
                VsqNrpn add = new VsqNrpn( keys[i] - getPresendClockAt( keys[i], msPreSend ) - clStart + t_pre_measure, 0x5402, value0, value1 );
                ret.add( add );
            }
        }
        return ret.toArray(new VsqNrpn[0]);
    }


    /// <summary>
    /// 指定したトラックからPitchBendSensitivityのNRPNを作成します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="msPreSend"></param>
    /// <returns></returns>
    public VsqNrpn[] generatePitchBendSensitivityNRPN( VsqTrack track, int msPreSend ) {
        return generatePitchBendSensitivityNRPN( track, msPreSend, 0, (int)TotalClocks, 0 );
    }

    public VsqNrpn[] generatePitchBendSensitivityNRPN( VsqTrack track, int msPreSend, int clStart, int clEnd, int t_pre_measure ) {
        Vector<VsqNrpn> ret = new Vector<VsqNrpn>();
        //SortedList<int, int> list = track[VsqCurveType.PBS].Vector;
        Integer[] keys = track.getVsqBPList(VsqCurveType.PBS).keyClockSet().toArray(new Integer[0]);
        int count = keys.length;
        Byte delay0, delay1;
        getMsbAndLsb( msPreSend, delay0, delay1               )     ;
            int start_clock = clStart < m_premeasure_clocks ? m_premeasure_clocks : clStart;
            int start_value = track.getVsqBPList(VsqCurveType.PBS).get( start_clock);
            VsqNrpn add0 = new VsqNrpn( start_clock - getPresendClockAt( start_clock, msPreSend ) - clStart + t_pre_measure, 0x6700, (byte)0x00, (byte)0x00 );
        add0.append( 0x6701, delay0, delay1 );
        add0.append( 0x6702, (byte)start_value, (byte)0x00 );
        ret.add( add0 );
        for ( int i = 1; i < keys.length; i++ ) {
            if ( keys[i] > clEnd ) {
                break;
            }
            if ( keys[i] > start_clock ) {
                VsqNrpn add = new VsqNrpn( keys[i]- getPresendClockAt( keys[i], msPreSend ) - clStart + t_pre_measure, 0x6702, (byte)track.getVsqBPList(VsqCurveType.PBS).get(keys[i]), (byte)0x00 );
                ret.add( add );
            }
        }
        return ret.toArray( new VsqNrpn[0]);
    }

    public VsqNrpn[] generateVibratoNRPN( VsqEvent ve, int msPreSend ) {
        return generateVibratoNRPN( ve, msPreSend, 0 );
    }

    public VsqNrpn[] generateVibratoNRPN( VsqEvent ve, int msPreSend, int clShift ) {
        Vector<VsqNrpn> ret = new Vector<VsqNrpn>();
        if ( ve.ID.VibratoHandle != null ) {
            int vclock = ve.Clock + ve.ID.VibratoDelay;
            Byte delay0, delay1;
            getMsbAndLsb( msPreSend,       delay0,  delay1                                         )   ;
                VsqNrpn add2 = new VsqNrpn( vclock - getPresendClockAt( vclock, msPreSend ) - clShift, 0x6500, (byte)0x00, (byte)0x00 );
            add2.append( 0x6501, delay0, delay1 );
            add2.append( 0x6502, (byte)ve.ID.VibratoHandle.StartDepth );
            add2.append( 0x6402, (byte)ve.ID.VibratoHandle.StartRate );
            ret.add( add2 );
            int vlength = ve.Clock + ve.ID.Length - ve.ID.VibratoDelay;
            if ( ve.ID.VibratoHandle.RateBPNum > 0 ) {
                for ( int i = 0; i < ve.ID.VibratoHandle.RateBPX.length; i++ ) {
                    float percent = ve.ID.VibratoHandle.RateBPX[i];
                    int cl = vclock + (int)(percent * vlength);
                    ret.add( new VsqNrpn( cl, 0x6402, (byte)ve.ID.VibratoHandle.RateBPY[i] ) );
                }
            }
            if ( ve.ID.VibratoHandle.DepthBPNum > 0 ) {
                for ( int i = 0; i < ve.ID.VibratoHandle.DepthBPX.length; i++ ) {
                    float percent = ve.ID.VibratoHandle.DepthBPX[i];
                    int cl = vclock + (int)(percent * vlength);
                    ret.add( new VsqNrpn( cl, 0x6502, (byte)ve.ID.VibratoHandle.DepthBPY[i] ) );
                }
            }
        }
        Collections.sort( ret );
        return ret.toArray(new VsqNrpn[0]);
    }


    /// <summary>
    /// 指定したトラックから、VoiceChangeParameterのNRPNのリストを作成します
    /// </summary>
    /// <param name="track"></param>
    /// <param name="msPreSend"></param>
    /// <returns></returns>
    public VsqNrpn[] generateVoiceChangeParameterNRPN( VsqTrack track, int msPreSend ) {
        return generateVoiceChangeParameterNRPN( track, msPreSend, 0, (int)TotalClocks, 0 );
    }

    public VsqNrpn[] generateVoiceChangeParameterNRPN( VsqTrack track, int msPreSend, int clStart, int clEnd, int t_pre_measure ) {
        int premeasure_clock = m_premeasure_clocks;
        byte delay0, delay1;
        getMsbAndLsb( msPreSend,      delay0, delay1                 );
         Vector   < VsqNrpn    
              > res    =    new Vector<VsqNrpn>();
            int start_clock = (clStart < premeasure_clock) ? premeasure_clock : clStart;
            VsqNrpn ret = new VsqNrpn( start_clock - getPresendClockAt( start_clock, msPreSend ) - clStart + t_pre_measure, 0x5500, (byte)0x00, (byte)0x00 );

        ret.append( 0x5501, delay0, delay1 );  // Voice Change Parameter delay

        ret.append( 0x5502, (byte)0x31 );   // BRE

        ret.append( 0x5503, (byte)track.getVsqBPList(VsqCurveType.BRE).get(start_clock) );

        ret.append( 0x5502, (byte)0x32 );   // BRI

        ret.append( 0x5503, (byte)track.getVsqBPList(VsqCurveType.BRI).get(start_clock) );

        ret.append( 0x5502, (byte)0x33 );   // CLE

        ret.append( 0x5503, (byte)track.getVsqBPList(VsqCurveType.CLE).get(start_clock) );

        ret.append( 0x5502, (byte)0x34 );   // POR

        ret.append( 0x5503, (byte)track.getVsqBPList(VsqCurveType.POR).get(start_clock) );

        ret.append( 0x5502, (byte)0x35 );   // OPE

        ret.append( 0x5503, (byte)track.getVsqBPList(VsqCurveType.OPE).get(start_clock) );

        ret.append( 0x5502, (byte)0x70 );   // GEN

        ret.append( 0x5503, (byte)track.getVsqBPList(VsqCurveType.GEN).get(start_clock) );
        res.add( ret );

        VsqCurveType[] values = VsqCurveType.values();
        for( int j = 0; j < values.length; j++ ){
            VsqCurveType vct = values[j];
                byte lsb = 0x31;
            switch ( vct ) {
                case DYN:
                case PBS:
                case PIT:
                case VEL:
                    continue;
                case BRE:
                    lsb = 0x31;
                    break;
                case BRI:
                    lsb = 0x32;
                    break;
                case CLE:
                    lsb = 0x33;
                    break;
                case POR:
                    lsb = 0x34;
                    break;
                case OPE:
                    lsb = 0x35;
                    break;
                case GEN:
                    lsb = 0x70;
                    break;
            }
            //SortedList<int, int> list = track[vct].Vector;
            Integer[] keys = track.getVsqBPList(vct).keyClockSet().toArray( new Integer[0]);
            for ( int i = 0; i < keys.length; i++ ) {
                if ( keys[i] > clEnd ) {
                    break;
                }
                if ( keys[i] > start_clock ) {
                    VsqNrpn add = new VsqNrpn( keys[i] - getPresendClockAt( keys[i], msPreSend ) - clStart + t_pre_measure, 0x5502, lsb );
                    add.append( 0x5503, (byte)track.getVsqBPList(vct).get(keys[i]) );
                    res.add( add );
                }
            }
        }

        return res.toArray( new VsqNrpn[0]);
    }

    private static void getMsbAndLsb( int value, Byte msb,Byte lsb   )  {
            msb = (byte)(value >> 7);
        lsb = (byte)(value - (msb << 7));
    }


        /// <summary>
        /// ファイルにこのVsqFileインスタンスの中身を出力します。
        /// </summary>
        /// <param name="file"></param>
        /// <param name="msPreSend">プリセンドタイム(msec)</param>
        public void write( String file, int msPreSend ) {
            int last_clock = 0;
            for ( int track = 1; track < m_tracks.size(); track++ ) {
                if ( m_tracks.get( track ).getEvents().size() > 0 ) {
                    int index = m_tracks.get( track ).getEvents().size() - 1;
                    VsqEvent last = m_tracks.get( track ).getEvents().get( index );
                    last_clock = Math.max( last_clock, last.Clock + last.ID.Length );
                }
            }

            //int delay = DelayFromPreSend( pre_send );
            RandomAccessFile fs = new RandomAccessFile( file, "rw" );
                long first_position;//チャンクの先頭のファイル位置

                //  ヘッダ
                //チャンクタイプ
                fs.write( _MTHD, 0, 4 );
                //データ長
                fs.writeByte( 0x00 );
                fs.writeByte( 0x00 );
                fs.writeByte( 0x00 );
                fs.writeByte( 0x06 );
                //フォーマット
                fs.writeByte( 0x00 );
                fs.writeByte( 0x01 );
                //トラック数
                writeUnsignedShort( fs, this.m_tracks.size() );
                //時間単位
                fs.writeByte( 0x01 );
                fs.writeByte( 0xe0 );

                // Master Track
                //チャンクタイプ
                fs.write( _MTRK, 0, 4 );
                //データ長。とりあえず0を入れておく
                fs.write( new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0, 4 );
                first_position = fs.getFilePointer();
                //トラック名
                writeFlexibleLengthUnsignedLong( fs, 0 );//デルタタイム
                fs.writeByte( 0xff );//ステータスタイプ
                fs.writeByte( 0x03 );//イベントタイプSequence/Track Name
                fs.writeByte( (byte)_MASTER_TRACK.length );//トラック名の文字数。これは固定
                fs.write( _MASTER_TRACK, 0, _MASTER_TRACK.length );

                Vector<MidiEvent> events = new Vector<MidiEvent>();
                for (int i = 0; i < this.m_timesig_table.size(); i++ ){
                    TimeSigTableEntry entry = m_timesig_table.get( i );
                    events.add( MidiEvent.TimeSig( entry.Clock, entry.Numerator, entry.Denominator ) );
                    last_clock = Math.max( last_clock, entry.Clock );
                }
                for( int i = 0; i < m_tempo_table.size(); i++ ){
                    TempoTableEntry entry = m_tempo_table.get( i );
                    events.add( MidiEvent.TempoChange( entry.Clock, entry.Tempo ) );
                    last_clock = Math.max( last_clock, entry.Clock );
                }
                Collections.sort(       events);
                int last = 0;
                for( int i = 0; i < events.size(); i++ ){
                    MidiEvent item = events.get( i );
                    switch ( item.type ) {
                        case tempo:
                            writeFlexibleLengthUnsignedLong( fs, item.index - last) ;
                            last = item.index;
                            fs.writeByte( 0xff );
                            fs.writeByte( 0x51 );
                            fs.writeByte( 0x03 );
                            writeTempo( fs, item.intValue[0] );
                            break;
                        case time_signal:
                            int num = item.intValue[0];
                            int den = item.intValue[1];
                            writeFlexibleLengthUnsignedLong( fs, item.index - last );
                            last = item.index;
                            fs.writeByte( 0xff );
                            fs.writeByte( 0x58 );//イベントタイプTime Signature
                            fs.writeByte( 0x04 );//データ長
                            fs.writeByte( (byte)num );//分子
                            fs.writeByte( (byte)(Math.log( den ) / Math.log(  2.0 ) ) );//分母の2の負のべき乗数
                            fs.writeByte( 0x18 );
                            fs.writeByte( 0x08 );
                            break;
                    }
                }

                //WriteFlexibleLengthUnsignedLong( fs, (ulong)(last_clock + 120 - last) );
                writeFlexibleLengthUnsignedLong( fs, 0 );
                fs.writeByte( 0xff );
                fs.writeByte( 0x2f );//イベントタイプEnd of Track
                fs.writeByte( 0x00 );
                long pos = fs.getFilePointer();
                fs.seek( first_position - 4);
                writeUnsignedInt( fs, pos - first_position);
                fs.seek( pos);

                // トラック
                VsqTrack t_track = (VsqTrack)m_tracks.get( 1).clone();
                t_track.setMaster( (VsqMaster)Master.clone() );
                t_track.setMixer( (VsqMixer)Mixer.clone() );
                printTrack( t_track, fs, msPreSend );
                for ( int track = 2; track < m_tracks.size(); track++ ) {
                    printTrack( m_tracks.get( track ), fs, msPreSend );
                }
        }

    public void write( String file ) {
        write( file, 500 );
    }


    /// <summary>
    /// メタテキストの行番号から、各行先頭のプレフィクス文字列("DM:0123:"等)を作成します
    /// </summary>
    /// <param name="count"></param>
    /// <returns></returns>
    public static String getLinePrefix( int count ) {
        int digits = howManyDigits( count );
        int c = (digits - 1) / 4 + 1;
        String format = "";
        for ( int i = 0; i < c; i++ ) {
            format += "0000";
        }
        DecimalFormat df = new DecimalFormat( format );
        return "DM:" + df.format( count ) + ":";
    }


    /// <summary>
    /// 数numberの桁数を調べます。（10進数のみ）
    /// </summary>
    /// <param name="number"></param>
    /// <returns></returns>
    private static int howManyDigits( int number ) {
        int val;
        if ( number > 0 ) {
            val = number;
        } else {
            val = -number;
        }
        int i = 1;
        int digits = 1;
        while ( true ) {
            i++;
            digits *= 10;
            if ( val < digits ) {
                return i - 1;
            }
        }
    }


    /// <summary>
    /// char[]を書き込む。
    /// </summary>
    /// <param name="fs"></param>
    /// <param name="item"></param>
    public void writeCharArray( RandomAccessFile fs, char[] item ) {
        for ( int i = 0; i < item.length; i++ ) {
            fs.writeByte( (byte)item[i] );
        }
    }


    /// <summary>
    /// テンポ値をビッグエンディアンで書き込みます。
    /// テンポは3バイトで記入されるので、別関数を用意した。
    /// </summary>
    /// <param name="fs"></param>
    /// <param name="tempo"></param>
    public void writeTempo( RandomAccessFile fs, long tempo ) {
        byte[] dat = new byte[3];
        ByteBuffer.wrap( dat ).order( ByteOrder.nativeOrder() ).putLong( tempo );
        fs.write( dat );
    }


    /// <summary>
    /// ushort値をビッグエンディアンでfsに書き込みます
    /// </summary>
    /// <param name="data"></param>
    public void writeUnsignedShort( RandomAccessFile fs, int data ) {
        byte[] dat = new byte[2];
        ByteBuffer.wrap( dat ).order( ByteOrder.nativeOrder() ).putInt( data );
        fs.write( dat );
    }


    /// <summary>
    /// uint値をビッグエンディアンでfsに書き込みます
    /// </summary>
    /// <param name="data"></param>
    public void writeUnsignedInt( RandomAccessFile fs, long data ) {
        byte[] dat = new byte[4];
        ByteBuffer.wrap( dat ).order( ByteOrder.nativeOrder() ).putLong( data );
        fs.write( dat );
    }


    /// <summary>
    /// SMFの可変長数値表現を使って、ulongをbyte[]に変換します
    /// </summary>
    /// <param name="fs"></param>
    /// <param name="number"></param>
    public static byte[] getBytesFlexibleLengthUnsignedLong( long number ) {
        boolean[] bits = new boolean[64];
        long val = 0x1;
        bits[0] = (number & val) == val;
        for ( int i = 1; i < 64; i++ ) {
            val = val << 1;
            bits[i] = (number & val) == val;
        }
        int first = 0;
        for ( int i = 63; i >= 0; i-- ) {
            if ( bits[i] ) {
                first = i;
                break;
            }
        }
        // 何バイト必要か？
        int bytes = first / 7 + 1;
        byte[] ret = new byte[bytes];
        for ( int i = 1; i <= bytes; i++ ) {
            int num = 0;
            int count = 0x80;
            for ( int j = (bytes - i + 1) * 7 - 1; j >= (bytes - i + 1) * 7 - 6 - 1; j-- ) {
                count = count >> 1;
                if ( bits[j] ) {
                    num += count;
                }
            }
            if ( i != bytes ) {
                num += 0x80;
            }
            ret[i - 1] = (byte)num;
        }
        return ret;
    }


    /// <summary>
    /// 整数を書き込む。フォーマットはSMFの可変長数値表現。
    /// </summary>
    /// <param name="fs"></param>
    /// <param name="number"></param>
    public static void writeFlexibleLengthUnsignedLong( RandomAccessFile fs, long number ) {
        byte[] bytes = getBytesFlexibleLengthUnsignedLong( number );
        fs.write( bytes, 0, bytes.length );
    }

}
