lipsync/trunk/Boare.Lib.Vsq/VsqFile.cs

2460 lines
121 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* VsqFile.cs
* Copyright (c) 2008-2009 kbinani
*
* This file is part of Boare.Lib.Vsq.
*
* Boare.Lib.Vsq is free software; you can redistribute it and/or
* modify it under the terms of the BSD License.
*
* Boare.Lib.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.
*/
using System;
using System.Collections.Generic;
using System.IO;
namespace Boare.Lib.Vsq {
/// <summary>
/// VSQファイルの内容を保持するクラス
/// </summary>
[Serializable]
public class VsqFile : ICloneable {
/// <summary>
/// トラックのリスト最初のトラックはMasterTrackであり通常の音符が格納されるトラックはインデックス1以降となる
/// </summary>
protected List<VsqTrack> m_tracks;
/// <summary>
/// テンポ情報を保持したテーブル
/// </summary>
protected List<TempoTableEntry> m_tempo_table;
protected List<TimeSigTableEntry> m_timesig_table;
protected int m_tpq;
/// <summary>
/// 曲の長さを取得します。(クロック(4分音符は480クロック))
/// </summary>
public int TotalClocks = 0;
protected int m_base_tempo;
public VsqMaster Master; // VsqMaster, VsqMixerは通常最初の非Master Trackに記述されるが可搬性のため
public VsqMixer Mixer; // ここではVsqFileに直属するものとして取り扱う
protected int m_premeasure_clocks;
public object Tag;
static readonly byte[] _MTRK = new byte[] { 0x4d, 0x54, 0x72, 0x6b };
static readonly byte[] _MTHD = new byte[] { 0x4d, 0x54, 0x68, 0x64 };
static readonly byte[] _MASTER_TRACK = new byte[] { 0x4D, 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x54, 0x72, 0x61, 0x63, 0x6B, };
static readonly string[] _CURVES = new string[] { "VEL", "DYN", "BRE", "BRI", "CLE", "OPE", "GEN", "POR", "PIT", "PBS" };
/// <summary>
/// このインスタンスに編集を行うコマンドを実行します
/// </summary>
/// <param name="command">実行するコマンド</param>
/// <returns>編集結果を元に戻すためのコマンドを返します</returns>
public VsqCommand executeCommand( VsqCommand command ) {
#if DEBUG
Console.WriteLine( "VsqFile.Execute(VsqCommand)" );
Console.WriteLine( " type=" + command.Type );
#endif
VsqCommandType type = command.Type;
if ( type == VsqCommandType.ChangePreMeasure ) {
#region ChangePreMeasure
VsqCommand ret = VsqCommand.generateCommandChangePreMeasure( Master.PreMeasure );
int value = (int)command.Args[0];
Master.PreMeasure = value;
updateTimesigInfo();
return ret;
#endregion
} else if ( type == VsqCommandType.AddTrack ) {
#region AddTrack
#if DEBUG
System.Diagnostics.Debug.WriteLine( " AddTrack" );
#endif
VsqTrack track = (VsqTrack)command.Args[0];
VsqMixerEntry mixer = (VsqMixerEntry)command.Args[1];
int position = (int)command.Args[2];
VsqCommand ret = VsqCommand.generateCommandDeleteTrack( position );
if ( m_tracks.Count <= 17 ) {
m_tracks.Insert( position, (VsqTrack)track.Clone() );
Mixer.Slave.Add( (VsqMixerEntry)mixer.Clone() );
return ret;
} else {
return null;
}
#endregion
} else if ( type == VsqCommandType.DeleteTrack ) {
#region DeleteTrack
int track = (int)command.Args[0];
VsqCommand ret = VsqCommand.generateCommandAddTrack( m_tracks[track], Mixer.Slave[track - 1], track );
m_tracks.RemoveAt( track );
Mixer.Slave.RemoveAt( track - 1 );
updateTotalClocks();
return ret;
#endregion
} else if ( type == VsqCommandType.UpdateTempo ) {
#region UpdateTempo
int clock = (int)command.Args[0];
int tempo = (int)command.Args[1];
int new_clock = (int)command.Args[2];
int index = -1;
for ( int i = 0; i < getTempoList().Count; i++ ) {
if ( getTempoList()[i].Clock == clock ) {
index = i;
break;
}
}
VsqCommand ret = null;
if ( index >= 0 ) {
if ( tempo <= 0 ) {
ret = VsqCommand.generateCommandUpdateTempo( clock, clock, getTempoList()[index].Tempo );
getTempoList().RemoveAt( index );
} else {
ret = VsqCommand.generateCommandUpdateTempo( new_clock, clock, getTempoList()[index].Tempo );
getTempoList()[index].Tempo= tempo ;
getTempoList()[index].Clock= new_clock ;
}
} else {
ret = VsqCommand.generateCommandUpdateTempo( clock, clock, -1 );
getTempoList().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.Count; i++ ) {
if ( affected_clock < m_tracks[i].getEditedStart() ) {
m_tracks[i].setEditedStart( affected_clock );
}
m_tracks[i].setEditedEnd( (int)TotalClocks );
}
return ret;
#endregion
} else if ( type == VsqCommandType.UpdateTempoRange ) {
#region 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 = int.MaxValue;
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 < getTempoList().Count; j++ ) {
if ( getTempoList()[j].Clock == clocks[i] ) {
index = j;
break;
}
}
if ( index >= 0 ) {
new_tempos[i] = getTempoList()[index].Tempo;
if ( tempos[i] <= 0 ) {
getTempoList().RemoveAt( index );
} else {
getTempoList()[index].Tempo = tempos[i];
getTempoList()[index].Clock = new_clocks[i];
}
} else {
new_tempos[i] = -1;
getTempoList().Add( new TempoTableEntry( new_clocks[i], tempos[i], 0.0 ) );
}
}
updateTempoInfo();
updateTotalClocks();
for ( int i = 1; i < m_tracks.Count; i++ ) {
if ( affected_clock < m_tracks[i].getEditedStart() ) {
m_tracks[i].setEditedStart( affected_clock );
}
m_tracks[i].setEditedEnd( (int)TotalClocks );
}
return VsqCommand.generateCommandUpdateTempoRange( new_clocks, clocks, new_tempos );
#endregion
} else if ( type == VsqCommandType.UpdateTimesig ) {
#region UpdateTimesig
int barcount = (int)command.Args[0];
int numerator = (int)command.Args[1];
int denominator = (int)command.Args[2];
int new_barcount = (int)command.Args[3];
int index = -1;
for ( int i = 0; i < getTimeSigList().Count; i++ ) {
if ( barcount == getTimeSigList()[i].BarCount ) {
index = i;
break;
}
}
VsqCommand ret = null;
if ( index >= 0 ) {
if ( numerator <= 0 ) {
ret = VsqCommand.generateCommandUpdateTimesig( barcount, barcount, getTimeSigList()[index].Numerator, getTimeSigList()[index].Denominator );
getTimeSigList().RemoveAt( index );
} else {
ret = VsqCommand.generateCommandUpdateTimesig( new_barcount, barcount, getTimeSigList()[index].Numerator, getTimeSigList()[index].Denominator );
getTimeSigList()[index].BarCount = new_barcount;
getTimeSigList()[index].Numerator = numerator;
getTimeSigList()[index].Denominator = denominator;
}
} else {
ret = VsqCommand.generateCommandUpdateTimesig( new_barcount, new_barcount, -1, -1 );
getTimeSigList().Add( new TimeSigTableEntry( 0, numerator, denominator, new_barcount ) );
}
updateTimesigInfo();
updateTotalClocks();
return ret;
#endregion
} else if ( type == VsqCommandType.UpdateTimesigRange ) {
#region 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 < getTimeSigList().Count; j++ ) {
if ( getTimeSigList()[j].BarCount == barcounts[i] ) {
index = j;
break;
}
}
if ( index >= 0 ) {
new_numerators[i] = getTimeSigList()[index].Numerator;
new_denominators[i] = getTimeSigList()[index].Denominator;
if ( numerators[i] <= 0 ) {
getTimeSigList().RemoveAt( index );
} else {
getTimeSigList()[index].BarCount = new_barcounts[i];
getTimeSigList()[index].Numerator = numerators[i];
getTimeSigList()[index].Denominator = denominators[i];
}
} else {
new_numerators[i] = -1;
new_denominators[i] = -1;
getTimeSigList().Add( new TimeSigTableEntry( 0, numerators[i], denominators[i], new_barcounts[i] ) );
}
}
updateTimesigInfo();
updateTotalClocks();
return VsqCommand.generateCommandUpdateTimesigRange( new_barcounts, barcounts, new_numerators, new_denominators );
#endregion
} else if ( type == VsqCommandType.Replace ) {
#region Replace
VsqFile vsq = (VsqFile)command.Args[0];
VsqFile inv = (VsqFile)this.Clone();
m_tracks.Clear();
for ( int i = 0; i < vsq.m_tracks.Count; i++ ) {
m_tracks.Add( (VsqTrack)vsq.m_tracks[i].Clone() );
}
m_tempo_table.Clear();
for ( int i = 0; i < vsq.m_tempo_table.Count; i++ ) {
m_tempo_table.Add( (TempoTableEntry)vsq.m_tempo_table[i].Clone() );
}
m_timesig_table.Clear();
for ( int i = 0; i < vsq.m_timesig_table.Count; i++ ) {
m_timesig_table.Add( (TimeSigTableEntry)vsq.m_timesig_table[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 VsqCommand.generateCommandReplace( inv );
#endregion
} else if ( type == VsqCommandType.EventAdd ) {
#region TrackAddNote
int track = (int)command.Args[0];
VsqEvent item = (VsqEvent)command.Args[1];
//int key = this.Tracks[track].GetNextId( 0 );
//item.InternalID = key;
m_tracks[track].addEvent( item );
VsqCommand ret = VsqCommand.generateCommandEventDelete( track, item.InternalID );
//this.Tracks[track].Events.Sort();
updateTotalClocks();
if ( item.Clock < m_tracks[track].getEditedStart() ) {
m_tracks[track].setEditedStart( item.Clock );
}
if ( m_tracks[track].getEditedEnd() < item.Clock + item.ID.Length ) {
m_tracks[track].setEditedEnd( item.Clock + item.ID.Length );
}
return ret;
#endregion
} else if ( type == VsqCommandType.EventAddRange ) {
#region TrackAddNoteRange
#if DEBUG
Console.WriteLine( " TrackAddNoteRange" );
#endif
int track = (int)command.Args[0];
VsqEvent[] items = (VsqEvent[])command.Args[1];
List<int> inv_ids = new List<int>();
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 = Tracks[track].GetNextId( i );
//item.InternalID = key;
#if DEBUG
Console.Write( " i=" + i + "; item.InternalID=" + item.InternalID );
#endif
m_tracks[track].addEvent( item );
inv_ids.Add( item.InternalID );
#if DEBUG
Console.WriteLine( " => " + item.InternalID );
#endif
}
//Tracks[track].Events.Sort();
updateTotalClocks();
if ( min_clock < m_tracks[track].getEditedStart() ) {
m_tracks[track].setEditedStart( min_clock );
}
if ( m_tracks[track].getEditedEnd() < max_clock ) {
m_tracks[track].setEditedEnd( max_clock );
}
return VsqCommand.generateCommandEventDeleteRange( track, inv_ids.ToArray() );
#endregion
} else if ( type == VsqCommandType.EventDelete ) {
#region TrackDeleteNote
int internal_id = (int)command.Args[0];
int track = (int)command.Args[1];
VsqEvent[] original = new VsqEvent[1];
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent item = (VsqEvent)itr.next();
if ( item.InternalID == internal_id ) {
original[0] = (VsqEvent)item.Clone();
break;
}
}
if ( original[0].Clock < m_tracks[track].getEditedStart() ) {
m_tracks[track].setEditedStart( original[0].Clock );
}
if ( m_tracks[track].getEditedEnd() < original[0].Clock + original[0].ID.Length ) {
m_tracks[track].setEditedEnd( original[0].Clock + original[0].ID.Length );
}
VsqCommand ret = VsqCommand.generateCommandEventAddRange( track, original );
for ( int i = 0; i < this.m_tracks[track].getEventCount(); i++ ) {
if ( this.m_tracks[track].getEvent( i ).InternalID == internal_id ) {
m_tracks[track].removeEvent( i );
break;
}
}
updateTotalClocks();
return ret;
#endregion
} else if ( type == VsqCommandType.EventDeleteRange ) {
#region TrackDeleteNoteRange
int[] internal_ids = (int[])command.Args[0];
int track = (int)command.Args[1];
List<VsqEvent> inv = new List<VsqEvent>();
int min_clock = int.MaxValue;
int max_clock = int.MinValue;
for ( int j = 0; j < internal_ids.Length; j++ ) {
for ( int i = 0; i < m_tracks[track].getEventCount(); i++ ) {
if ( internal_ids[j] == m_tracks[track].getEvent( i ).InternalID ) {
inv.Add( (VsqEvent)m_tracks[track].getEvent( i ).Clone() );
min_clock = Math.Min( min_clock, m_tracks[track].getEvent( i ).Clock );
max_clock = Math.Max( max_clock, m_tracks[track].getEvent( i ).Clock + m_tracks[track].getEvent( i ).ID.Length );
m_tracks[track].removeEvent( i );
break;
}
}
}
updateTotalClocks();
m_tracks[track].setEditedStart( min_clock );
m_tracks[track].setEditedEnd( max_clock );
return VsqCommand.generateCommandEventAddRange( track, inv.ToArray() );
#endregion
} else if ( type == VsqCommandType.EventChangeClock ) {
#region TrackChangeClock
int track = (int)command.Args[0];
int internal_id = (int)command.Args[1];
int value = (int)command.Args[2];
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent item = (VsqEvent)itr.next();
if ( item.InternalID == internal_id ) {
VsqCommand ret = VsqCommand.generateCommandEventChangeClock( 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[track].setEditedStart( min );
m_tracks[track].setEditedEnd( max );
item.Clock = value;
//this.Tracks[track].Events.Sort();
updateTotalClocks();
return ret;
}
}
return null;
#endregion
} else if ( type == VsqCommandType.EventChangeLyric ) {
#region TrackChangeLyric
int track = (int)command.Args[0];
int internal_id = (int)command.Args[1];
string phrase = (string)command.Args[2];
string phonetic_symbol = (string)command.Args[3];
bool protect_symbol = (bool)command.Args[4];
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent item = (VsqEvent)itr.next();
if ( item.InternalID == internal_id ) {
if ( item.ID.type == VsqIDType.Anote ) {
VsqCommand ret = VsqCommand.generateCommandEventChangeLyric( 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[track].setEditedStart( item.Clock );
m_tracks[track].setEditedEnd( item.Clock + item.ID.Length );
updateTotalClocks();
return ret;
}
}
}
return null;
#endregion
} else if ( type == VsqCommandType.EventChangeNote ) {
#region TrackChangeNote
int track = (int)command.Args[0];
int internal_id = (int)command.Args[1];
int note = (int)command.Args[2];
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent item = (VsqEvent)itr.next();
if ( item.InternalID == internal_id ) {
VsqCommand ret = VsqCommand.generateCommandEventChangeNote( track, internal_id, item.ID.Note );
item.ID.Note = note;
updateTotalClocks();
m_tracks[track].setEditedStart( item.Clock );
m_tracks[track].setEditedEnd( item.Clock + item.ID.Length );
return ret;
}
}
return null;
#endregion
} else if ( type == VsqCommandType.EventChangeClockAndNote ) {
#region TrackChangeClockAndNote
int track = (int)command.Args[0];
int internal_id = (int)command.Args[1];
int clock = (int)command.Args[2];
int note = (int)command.Args[3];
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent item = (VsqEvent)itr.next();
if ( item.InternalID == internal_id ) {
VsqCommand ret = VsqCommand.generateCommandEventChangeClockAndNote( 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[track].setEditedStart( min );
m_tracks[track].setEditedEnd( max );
item.Clock = clock;
item.ID.Note = note;
//this.Tracks[track].Events.Sort();
updateTotalClocks();
return ret;
}
}
return null;
#endregion
} else if ( type == VsqCommandType.TrackEditCurve ) {
#region TrackEditCurve
int track = (int)command.Args[0];
string curve = (string)command.Args[1];
List<BPPair> com = (List<BPPair>)command.Args[2];
VsqCommand inv = null;
List<BPPair> edit = new List<BPPair>();
if ( com != null ) {
if ( com.Count > 0 ) {
int start_clock = com[0].Clock;
int end_clock = com[0].Clock;
foreach ( BPPair item in com ) {
start_clock = Math.Min( start_clock, item.Clock );
end_clock = Math.Max( end_clock, item.Clock );
}
m_tracks[track].setEditedStart( start_clock );
m_tracks[track].setEditedEnd( end_clock );
int start_value = m_tracks[track].getCurve( curve ).getElement( start_clock );
int end_value = m_tracks[track].getCurve( curve ).getElement( end_clock );
for ( Iterator i = m_tracks[track].getCurve( curve ).keyClockIterator(); i.hasNext(); ){
int clock = (int)i.next();
if ( start_clock <= clock && clock <= end_clock ) {
edit.Add( new BPPair( clock, m_tracks[track].getCurve( curve ).getElement( clock ) ) );
}
}
bool start_found = false;
bool end_found = false;
for ( int i = 0; i < edit.Count; i++ ) {
if ( edit[i].Clock == start_clock ) {
start_found = true;
edit[i].Value = start_value;
if ( start_found && end_found ) {
break;
}
}
if ( edit[i].Clock == end_clock ) {
end_found = true;
edit[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 ) );
}
// 並べ替え
edit.Sort();
inv = VsqCommand.generateCommandTrackEditCurve( track, curve, edit );
} else if ( com.Count == 0 ) {
inv = VsqCommand.generateCommandTrackEditCurve( track, curve, new List<BPPair>() );
}
}
updateTotalClocks();
if ( com.Count == 0 ) {
return inv;
} else if ( com.Count == 1 ) {
bool found = false;
for ( Iterator itr = m_tracks[track].getCurve( curve ).keyClockIterator(); itr.hasNext(); ){
int clock = (int)itr.next();
if ( clock == com[0].Clock ) {
found = true;
m_tracks[track].getCurve( curve ).add( clock, com[0].Value );
break;
}
}
if ( !found ) {
m_tracks[track].getCurve( curve ).add( com[0].Clock, com[0].Value );
}
} else {
int start_clock = com[0].Clock;
int end_clock = com[com.Count - 1].Clock;
bool removed = true;
while ( removed ) {
removed = false;
for ( Iterator itr = m_tracks[track].getCurve( curve ).keyClockIterator(); itr.hasNext(); ) {
int clock = (int)itr.next();
if ( start_clock <= clock && clock <= end_clock ) {
m_tracks[track].getCurve( curve ).remove( clock );
removed = true;
break;
}
}
}
foreach ( BPPair item in com ) {
m_tracks[track].getCurve( curve ).add( item.Clock, item.Value );
}
}
return inv;
#endregion
} else if ( type == VsqCommandType.TrackEditCurveRange ) {
#region TrackEditCurveRange
int track = (int)command.Args[0];
string[] curves = (string[])command.Args[1];
List<BPPair>[] coms = (List<BPPair>[])command.Args[2];
List<BPPair>[] inv_coms = new List<BPPair>[curves.Length];
VsqCommand inv = null;
for ( int k = 0; k < curves.Length; k++ ) {
string curve = curves[k];
List<BPPair> com = coms[k];
//SortedList<int, int> list = Tracks[track][curve].List;
List<BPPair> edit = new List<BPPair>();
if ( com != null ) {
if ( com.Count > 0 ) {
int start_clock = com[0].Clock;
int end_clock = com[0].Clock;
foreach ( BPPair item in com ) {
start_clock = Math.Min( start_clock, item.Clock );
end_clock = Math.Max( end_clock, item.Clock );
}
m_tracks[track].setEditedStart( start_clock );
m_tracks[track].setEditedEnd( end_clock );
int start_value = m_tracks[track].getCurve( curve ).getElement( start_clock );
int end_value = m_tracks[track].getCurve( curve ).getElement( end_clock );
for ( Iterator itr = m_tracks[track].getCurve( curve ).keyClockIterator(); itr.hasNext(); ) {
int clock = (int)itr.next();
if ( start_clock <= clock && clock <= end_clock ) {
edit.Add( new BPPair( clock, m_tracks[track].getCurve( curve ).getElement( clock ) ) );
}
}
bool start_found = false;
bool end_found = false;
for ( int i = 0; i < edit.Count; i++ ) {
if ( edit[i].Clock == start_clock ) {
start_found = true;
edit[i].Value = start_value;
if ( start_found && end_found ) {
break;
}
}
if ( edit[i].Clock == end_clock ) {
end_found = true;
edit[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 ) );
}
// 並べ替え
edit.Sort();
inv_coms[k] = edit;
//inv = generateCommandTrackEditCurve( track, curve, edit );
} else if ( com.Count == 0 ) {
//inv = generateCommandTrackEditCurve( track, curve, new List<BPPair>() );
inv_coms[k] = new List<BPPair>();
}
}
updateTotalClocks();
if ( com.Count == 0 ) {
return inv;
} else if ( com.Count == 1 ) {
bool found = false;
for ( Iterator itr = m_tracks[track].getCurve( curve ).keyClockIterator(); itr.hasNext(); ) {
int clock = (int)itr.next();
if ( clock == com[0].Clock ) {
found = true;
m_tracks[track].getCurve( curve ).add( clock, com[0].Value );
break;
}
}
if ( !found ) {
m_tracks[track].getCurve( curve ).add( com[0].Clock, com[0].Value );
}
} else {
int start_clock = com[0].Clock;
int end_clock = com[com.Count - 1].Clock;
bool removed = true;
while ( removed ) {
removed = false;
for ( Iterator itr = m_tracks[track].getCurve( curve ).keyClockIterator(); itr.hasNext(); ) {
int clock = (int)itr.next();
if ( start_clock <= clock && clock <= end_clock ) {
m_tracks[track].getCurve( curve ).remove( clock );
removed = true;
break;
}
}
}
foreach ( BPPair item in com ) {
m_tracks[track].getCurve( curve ).add( item.Clock, item.Value );
}
}
}
return VsqCommand.generateCommandTrackEditCurveRange( track, curves, inv_coms );
#endregion
} else if ( type == VsqCommandType.EventChangeVelocity ) {
#region TrackChangeVelocity
int track = (int)command.Args[0];
List<KeyValuePair<int, int>> veloc = (List<KeyValuePair<int, int>>)command.Args[1];
List<KeyValuePair<int, int>> inv = new List<KeyValuePair<int, int>>();
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent ev = (VsqEvent)itr.next();
foreach ( KeyValuePair<int, int> add in veloc ) {
if ( ev.InternalID == add.Key ) {
inv.Add( new KeyValuePair<int, int>( ev.InternalID, ev.ID.Dynamics ) );
ev.ID.Dynamics = add.Value;
m_tracks[track].setEditedStart( ev.Clock );
m_tracks[track].setEditedEnd( ev.Clock + ev.ID.Length );
break;
}
}
}
return VsqCommand.generateCommandEventChangeVelocity( track, inv );
#endregion
} else if ( type == VsqCommandType.EventChangeAccent ) {
#region TrackChangeAccent
int track = (int)command.Args[0];
List<KeyValuePair<int, int>> veloc = (List<KeyValuePair<int, int>>)command.Args[1];
List<KeyValuePair<int, int>> inv = new List<KeyValuePair<int, int>>();
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent ev = (VsqEvent)itr.next();
foreach ( KeyValuePair<int, int> add in veloc ) {
if ( ev.InternalID == add.Key ) {
inv.Add( new KeyValuePair<int, int>( ev.InternalID, ev.ID.DEMaccent ) );
ev.ID.DEMaccent = add.Value;
m_tracks[track].setEditedStart( ev.Clock );
m_tracks[track].setEditedEnd( ev.Clock + ev.ID.Length );
break;
}
}
}
return VsqCommand.generateCommandEventChangeAccent( track, inv );
#endregion
} else if ( type == VsqCommandType.EventChangeDecay ) {
#region TrackChangeDecay
int track = (int)command.Args[0];
List<KeyValuePair<int, int>> veloc = (List<KeyValuePair<int, int>>)command.Args[1];
List<KeyValuePair<int, int>> inv = new List<KeyValuePair<int, int>>();
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent ev = (VsqEvent)itr.next();
foreach ( KeyValuePair<int, int> add in veloc ) {
if ( ev.InternalID == add.Key ) {
inv.Add( new KeyValuePair<int, int>( ev.InternalID, ev.ID.DEMdecGainRate ) );
ev.ID.DEMdecGainRate = add.Value;
m_tracks[track].setEditedStart( ev.Clock );
m_tracks[track].setEditedEnd( ev.Clock + ev.ID.Length );
break;
}
}
}
return VsqCommand.generateCommandEventChangeDecay( track, inv );
#endregion
} else if ( type == VsqCommandType.EventChangeLength ) {
#region TrackChangeLength
int track = (int)command.Args[0];
int internal_id = (int)command.Args[1];
int new_length = (int)command.Args[2];
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent item = (VsqEvent)itr.next();
if ( item.InternalID == internal_id ) {
VsqCommand ret = VsqCommand.generateCommandEventChangeLength( track, internal_id, item.ID.Length );
m_tracks[track].setEditedStart( item.Clock );
int max = Math.Max( item.Clock + item.ID.Length, item.Clock + new_length );
m_tracks[track].setEditedEnd( max );
item.ID.Length = new_length;
updateTotalClocks();
return ret;
}
}
return null;
#endregion
} else if ( type == VsqCommandType.EventChangeClockAndLength ) {
#region TrackChangeClockAndLength
int track = (int)command.Args[0];
int internal_id = (int)command.Args[1];
int new_clock = (int)command.Args[2];
int new_length = (int)command.Args[3];
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent item = (VsqEvent)itr.next();
if ( item.InternalID == internal_id ) {
VsqCommand ret = VsqCommand.generateCommandEventChangeClockAndLength( 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[track].setEditedStart( min );
m_tracks[track].setEditedEnd( max );
item.ID.Length = new_length;
item.Clock = new_clock;
updateTotalClocks();
return ret;
}
}
return null;
#endregion
} else if ( type == VsqCommandType.EventChangeIDContaints ) {
#region TrackChangeIDContaints
int track = (int)command.Args[0];
int internal_id = (int)command.Args[1];
VsqID value = (VsqID)command.Args[2];
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent item = (VsqEvent)itr.next();
if ( item.InternalID == internal_id ) {
VsqCommand ret = VsqCommand.generateCommandEventChangeIDContaints( track, internal_id, item.ID );
int max_length = Math.Max( item.ID.Length, value.Length );
m_tracks[track].setEditedStart( item.Clock );
m_tracks[track].setEditedEnd( item.Clock + max_length );
item.ID = (VsqID)value.Clone();
if ( item.ID.type == VsqIDType.Singer ) {
#if DEBUG
Console.WriteLine( " EventChangeIDContaints" );
#endif
// 歌手変更の場合、次に現れる歌手変更の位置まで編集の影響が及ぶ
bool found = false;
for ( Iterator itr2 = m_tracks[track].getSingerEventIterator(); itr2.hasNext(); ) {
VsqEvent item2 = (VsqEvent)itr2.next();
if ( item.Clock < item2.Clock ) {
m_tracks[track].setEditedEnd( item2.Clock );
found = true;
break;
}
}
if ( !found ) {
// 変更対象が、該当トラック最後の歌手変更イベントだった場合
if ( m_tracks[track].getEventCount() >= 1 ) {
VsqEvent last_event = m_tracks[track].getEvent( m_tracks[track].getEventCount() - 1 );
m_tracks[track].setEditedEnd( last_event.Clock + last_event.ID.Length );
}
}
#if DEBUG
Console.WriteLine( " EditedStart,EditedEnd=" + m_tracks[track].getEditedStart() + "," + m_tracks[track].getEditedEnd() );
#endif
}
updateTotalClocks();
return ret;
}
}
return null;
#endregion
} else if ( type == VsqCommandType.EventChangeIDContaintsRange ) {
#region TrackChangeIDContaintsRange
int track = (int)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[track].getEventIterator(); 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[track].setEditedStart( item.Clock );
m_tracks[track].setEditedEnd( item.Clock + max_length );
item.ID = (VsqID)values[i].Clone();
if ( item.ID.type == VsqIDType.Singer ) {
// 歌手変更の場合、次に現れる歌手変更の位置まで編集の影響が及ぶ
bool found = false;
for ( Iterator itr2 = m_tracks[track].getSingerEventIterator(); itr2.hasNext(); ) {
VsqEvent item2 = (VsqEvent)itr2.next();
if ( item.Clock < item2.Clock ) {
m_tracks[track].setEditedEnd( item2.Clock );
found = true;
break;
}
}
if ( !found ) {
// 変更対象が、該当トラック最後の歌手変更イベントだった場合
if ( m_tracks[track].getEventCount() >= 1 ) {
VsqEvent last_event = m_tracks[track].getEvent( m_tracks[track].getEventCount() - 1 );
m_tracks[track].setEditedEnd( last_event.Clock + last_event.ID.Length );
}
}
}
break;
}
}
}
updateTotalClocks();
return VsqCommand.generateCommandEventChangeIDContaintsRange( track, internal_ids, inv_values );
#endregion
} else if ( type == VsqCommandType.EventChangeClockAndIDContaints ) {
#region TrackChangeClockAndIDContaints
int track = (int)command.Args[0];
int internal_id = (int)command.Args[1];
int new_clock = (int)command.Args[2];
VsqID value = (VsqID)command.Args[3];
for ( Iterator itr = m_tracks[track].getEventIterator(); itr.hasNext(); ) {
VsqEvent item = (VsqEvent)itr.next();
if ( item.InternalID == internal_id ) {
VsqCommand ret = VsqCommand.generateCommandEventChangeClockAndIDContaints( 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[track].setEditedStart( min );
m_tracks[track].setEditedEnd( max );
updateTotalClocks();
return ret;
}
}
return null;
#endregion
} else if ( type == VsqCommandType.EventChangeClockAndIDContaintsRange ) {
#region TrackChangeClockAndIDContaintsRange
int track = (int)command.Args[0];
int[] internal_ids = (int[])command.Args[1];
int[] clocks = (int[])command.Args[2];
VsqID[] values = (VsqID[])command.Args[3];
List<VsqID> inv_id = new List<VsqID>();
List<int> inv_clock = new List<int>();
for ( int i = 0; i < internal_ids.Length; i++ ) {
for ( Iterator itr = m_tracks[track].getEventIterator(); 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[track].setEditedStart( min );
m_tracks[track].setEditedEnd( max );
item.ID = (VsqID)values[i].Clone();
item.Clock = clocks[i];
break;
}
}
}
updateTotalClocks();
return VsqCommand.generateCommandEventChangeClockAndIDContaintsRange(
track,
internal_ids,
inv_clock.ToArray(),
inv_id.ToArray() );
#if DEBUG
Console.WriteLine( " TrackChangeClockAndIDContaintsRange" );
Console.WriteLine( " track=" + track );
for ( int i = 0; i < internal_ids.Length; i++ ) {
Console.WriteLine( " id=" + internal_ids[i] + "; clock=" + clocks[i] + "; ID=" + values[i].ToString() );
}
#endif
#endregion
} else if ( type == VsqCommandType.TrackChangeName ) {
#region TrackCangeName
int track = (int)command.Args[0];
string new_name = (string)command.Args[1];
VsqCommand ret = VsqCommand.generateCommandTrackChangeName( track, m_tracks[track].Name );
m_tracks[track].Name = new_name;
return ret;
#endregion
} else if ( type == VsqCommandType.TrackReplace ) {
#region TrackReplace
int track = (int)command.Args[0];
VsqTrack item = (VsqTrack)command.Args[1];
VsqCommand ret = VsqCommand.generateCommandTrackReplace( track, (VsqTrack)m_tracks[track].Clone() );
m_tracks[track] = item;
updateTotalClocks();
return ret;
#endregion
} else if ( type == VsqCommandType.TrackChangeRenderer ) {
#region TrackChangeRenderer
int track = (int)command.Args[0];
string renderer = (string)command.Args[1];
string old_renderer = m_tracks[track].Renderer;
m_tracks[track].Renderer = renderer;
VsqCommand ret = VsqCommand.generateCommandTrackChangeRenderer( track, old_renderer );
return ret;
#endregion
}
return null;
}
/// <summary>
/// VSQファイルの指定されたクロック範囲のイベント等を削除します
/// </summary>
/// <param name="vsq">編集対象のVsqFileインスタンス</param>
/// <param name="clock_start">削除を行う範囲の開始クロック</param>
/// <param name="clock_end">削除を行う範囲の終了クロック</param>
public static void removePart( VsqFile vsq, int clock_start, int clock_end ) {
int dclock = clock_end - clock_start;
// テンポ情報の削除、シフト
bool changed = true;
List<TempoTableEntry> buf = new List<TempoTableEntry>( vsq.getTempoList() );
int tempo_at_clock_start = vsq.getTempoAt( clock_start );
vsq.getTempoList().Clear();
bool just_on_clock_end_added = false;
for ( int i = 0; i < buf.Count; i++ ) {
if ( buf[i].Clock < clock_start ) {
vsq.getTempoList().Add( (TempoTableEntry)buf[i].Clone() );
} else if ( clock_end <= buf[i].Clock ) {
TempoTableEntry tte = (TempoTableEntry)buf[i].Clone();
tte.Clock = tte.Clock - dclock;
if ( clock_end == buf[i].Clock ) {
vsq.getTempoList().Add( tte );
just_on_clock_end_added = true;
} else {
if ( !just_on_clock_end_added ) {
vsq.getTempoList().Add( new TempoTableEntry( clock_end - dclock, tempo_at_clock_start, 0.0 ) );
just_on_clock_end_added = true;
}
vsq.getTempoList().Add( tte );
}
}
}
vsq.updateTempoInfo();
for ( int track = 1; track < vsq.m_tracks.Count; track++ ) {
// 削除する範囲に歌手変更イベントが存在するかどうかを検査。
VsqEvent t_last_singer = null;
for ( Iterator itr = vsq.m_tracks[track].getSingerEventIterator(); itr.hasNext(); ) {
VsqEvent ve = (VsqEvent)itr.next();
if ( clock_start <= ve.Clock && ve.Clock < clock_end ) {
t_last_singer = ve;
}
if ( ve.Clock == clock_end ) {
t_last_singer = null; // 後でclock_endの位置に補うが、そこにに既に歌手変更イベントがあるとまずいので。
}
}
VsqEvent last_singer = null;
if ( t_last_singer != null ) {
last_singer = (VsqEvent)t_last_singer.Clone();
last_singer.Clock = clock_end;
}
changed = true;
// イベントの削除
while ( changed ) {
changed = false;
for ( int i = 0; i < vsq.m_tracks[track].getEventCount(); i++ ) {
if ( clock_start <= vsq.m_tracks[track].getEvent( i ).Clock && vsq.m_tracks[track].getEvent( i ).Clock < clock_end ) {
vsq.m_tracks[track].removeEvent( i );
changed = true;
break;
}
}
}
// クロックのシフト
if ( last_singer != null ) {
vsq.m_tracks[track].addEvent( last_singer ); //歌手変更イベントを補う
}
for ( int i = 0; i < vsq.m_tracks[track].getEventCount(); i++ ) {
if ( clock_end <= vsq.m_tracks[track].getEvent( i ).Clock ) {
vsq.m_tracks[track].getEvent( i ).Clock -= dclock;
}
}
foreach ( string curve in _CURVES ) {
if ( curve == "VEL" ) {
continue;
}
VsqBPList buf_bplist = (VsqBPList)vsq.m_tracks[track].getCurve( curve ).Clone();
vsq.m_tracks[track].getCurve( curve ).clear();
for ( Iterator itr = buf_bplist.keyClockIterator(); itr.hasNext(); ) {
int key = (int)itr.next();
if ( key < clock_start ) {
vsq.m_tracks[track].getCurve( curve ).add( key, buf_bplist.getElement( key ) );
} else if ( clock_end <= key ) {
vsq.m_tracks[track].getCurve( curve ).add( key - dclock, buf_bplist.getElement( key ) );
}
}
}
}
}
/// <summary>
/// vsqファイル全体のイベントを指定したクロックだけ遅らせます
/// ただし曲頭のテンポ変更イベントと歌手変更イベントはクロック0から移動しません
/// この操作を行うことでTimesigTableの情報は破綻します仕様です
/// </summary>
/// <param name="delta_clock"></param>
public static void shift( VsqFile vsq, uint delta_clock ) {
if ( delta_clock == 0 ) {
return;
}
int dclock = (int)delta_clock;
for ( int i = 0; i < vsq.getTempoList().Count; i++ ) {
if ( vsq.getTempoList()[i].Clock > 0 ) {
vsq.getTempoList()[i].Clock =vsq.getTempoList()[i].Clock + dclock;
}
}
vsq.updateTempoInfo();
for ( int track = 1; track < vsq.m_tracks.Count; track++ ) {
for ( int i = 0; i < vsq.m_tracks[track].getEventCount(); i++ ) {
if ( vsq.m_tracks[track].getEvent( i ).Clock > 0 ) {
vsq.m_tracks[track].getEvent( i ).Clock += dclock;
}
}
foreach ( string curve in _CURVES ) {
if ( curve == "VEL" ) {
continue;
}
// 順番に+=dclockしていくとVsqBPList内部のSortedListの値がかぶる可能性がある
VsqBPList edit = vsq.m_tracks[track].getCurve( curve );
VsqBPList new_one = new VsqBPList( edit.getDefault(), edit.getMinimum(), edit.getMaximum() );
foreach( int key in edit.getKeys() ){
new_one.add( key + dclock, edit.getElement( key ) );
}
vsq.m_tracks[track].setCurve( curve, new_one );
}
}
vsq.updateTotalClocks();
}
/// <summary>
/// このインスタンスのコピーを作成します
/// </summary>
/// <returns>このインスタンスのコピー</returns>
public object Clone() {
VsqFile ret = new VsqFile();
ret.m_tracks = new List<VsqTrack>();
for ( int i = 0; i < m_tracks.Count; i++ ) {
ret.m_tracks.Add( (VsqTrack)m_tracks[i].Clone() );
}
ret.m_tempo_table = new List<TempoTableEntry>();
for ( int i = 0; i < m_tempo_table.Count; i++ ) {
ret.m_tempo_table.Add( (TempoTableEntry)m_tempo_table[i].Clone() );
}
ret.m_timesig_table = new List<TimeSigTableEntry>();
for ( int i = 0; i < m_timesig_table.Count; i++ ) {
ret.m_timesig_table.Add( (TimeSigTableEntry)m_timesig_table[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();
}
private class BarLineIterator : Iterator {
private List<TimeSigTableEntry> m_list;
private int m_end_clock;
private int i;
private int clock;
int local_denominator;
int local_numerator;
int clock_step;
int t_end;
int local_clock;
int bar_counter;
public BarLineIterator( List<TimeSigTableEntry> list, int end_clock ) {
m_list = list;
m_end_clock = end_clock;
i = 0;
t_end = -1;
clock = 0;
}
public Object next() {
int mod = clock_step * local_numerator;
if ( clock < t_end ) {
if ( (clock - local_clock) % mod == 0 ) {
bar_counter++;
VsqBarLineType ret = new VsqBarLineType( clock, true, local_denominator, local_numerator, bar_counter );
clock += clock_step;
return ret;
} else {
VsqBarLineType ret = new VsqBarLineType( clock, false, local_denominator, local_numerator, bar_counter );
clock += clock_step;
return ret;
}
}
if( i < m_list.Count ) {
local_denominator = m_list[i].Denominator;
local_numerator = m_list[i].Numerator;
local_clock = m_list[i].Clock;
int local_bar_count = m_list[i].BarCount;
clock_step = 480 * 4 / local_denominator;
mod = clock_step * local_numerator;
bar_counter = local_bar_count - 1;
t_end = m_end_clock;
if ( i + 1 < m_list.Count ) {
t_end = m_list[i + 1].Clock;
}
i++;
clock = local_clock;
if( clock < t_end ){
if ( (clock - local_clock) % mod == 0 ) {
bar_counter++;
VsqBarLineType ret = new VsqBarLineType( clock, true, local_denominator, local_numerator, bar_counter );
clock += clock_step;
return ret;
} else {
VsqBarLineType ret = new VsqBarLineType( clock, false, local_denominator, local_numerator, bar_counter );
clock += clock_step;
return ret;
}
}
}
return null;
}
public void remove() {
throw new NotImplementedException();
}
public Boolean hasNext() {
if ( clock < m_end_clock ) {
return true;
} else {
return false;
}
}
}
/// <summary>
/// 小節の区切りを順次返すIterator。
/// </summary>
/// <returns></returns>
public Iterator getBarLineIterator( int end_clock ) {
return new BarLineIterator( getTimeSigList(), end_clock );
}
/// <summary>
/// 基本テンポ値を取得します
/// </summary>
public int getBaseTempo() {
return m_base_tempo;
}
/// <summary>
/// プリメジャー値を取得します
/// </summary>
public int getPreMeasure() {
return Master.PreMeasure;
}
/// <summary>
/// プリメジャー部分の長さをクロックに変換した値を取得します.
/// </summary>
public int getPreMeasureClocks() {
return m_premeasure_clocks;
}
/// <summary>
/// プリメジャーの長さ(クロック)を計算します。
/// </summary>
private int calculatePreMeasureInClock() {
int pre_measure = Master.PreMeasure;
int last_bar_count = getTimeSigList()[0].BarCount;
int last_clock = getTimeSigList()[0].Clock;
int last_denominator = getTimeSigList()[0].Denominator;
int last_numerator = getTimeSigList()[0].Numerator;
for ( int i = 1; i < getTimeSigList().Count; i++ ) {
if ( getTimeSigList()[i].BarCount >= pre_measure ) {
break;
} else {
last_bar_count = getTimeSigList()[i].BarCount;
last_clock = getTimeSigList()[i].Clock;
last_denominator = getTimeSigList()[i].Denominator;
last_numerator = getTimeSigList()[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 getSecFromClock( int clock ) {
for ( int i = getTempoList().Count - 1; i >= 0; i-- ) {
if ( getTempoList()[i].Clock < clock ) {
double init = getTempoList()[i].Time;
int dclock = clock - getTempoList()[i].Clock;
double sec_per_clock1 = getTempoList()[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 double getClockFromSec( double time ) {
// timeにおけるテンポを取得
int tempo = m_base_tempo;
double base_clock = 0;
double base_time = 0f;
if ( m_tempo_table.Count == 0 ) {
tempo = m_base_tempo;
base_clock = 0;
base_time = 0f;
} else if ( m_tempo_table.Count == 1 ) {
tempo = m_tempo_table[0].Tempo;
base_clock = m_tempo_table[0].Clock;
base_time = m_tempo_table[0].Time;
} else {
for ( int i = m_tempo_table.Count - 1; i >= 0; i-- ) {
if ( m_tempo_table[i].Time < time ) {
return m_tempo_table[i].Clock + (time - m_tempo_table[i].Time) * m_tpq * 1000000.0 / m_tempo_table[i].Tempo;
}
}
}
double dt = time - base_time;
return base_clock + dt * m_tpq * 1000000.0 / (double)tempo;
}
/// <summary>
/// 指定したクロックにおける拍子を取得します
/// </summary>
/// <param name="clock"></param>
/// <param name="numerator"></param>
/// <param name="denominator"></param>
public void getTimesigAt( int clock, out int numerator, out int denominator ) {
int index = 0;
for ( int i = getTimeSigList().Count - 1; i >= 0; i-- ) {
index = i;
if ( getTimeSigList()[i].Clock <= clock ) {
break;
}
}
numerator = getTimeSigList()[index].Numerator;
denominator = getTimeSigList()[index].Denominator;
}
/// <summary>
/// 指定したクロックにおけるテンポを取得します。
/// </summary>
/// <param name="clock"></param>
/// <returns></returns>
public int getTempoAt( int clock ) {
int index = 0;
for ( int i = getTempoList().Count - 1; i >= 0; i-- ) {
index = i;
if ( getTempoList()[i].Clock <= clock ) {
break;
}
}
return getTempoList()[index].Tempo;
}
/// <summary>
/// 指定した小節の開始クロックを調べます。ここで使用する小節数は、プリメジャーを考慮しない。即ち、曲頭の小節が0である。
/// </summary>
/// <param name="bar_count"></param>
/// <returns></returns>
public int getClockFromBarCount( int bar_count ) {
int index = 0;
for ( int i = getTimeSigList().Count - 1; i >= 0; i-- ) {
index = i;
if ( getTimeSigList()[i].BarCount <= bar_count ) {
break;
}
}
int numerator = getTimeSigList()[index].Numerator;
int denominator = getTimeSigList()[index].Denominator;
int init_clock = getTimeSigList()[index].Clock;
int init_bar_count = getTimeSigList()[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 = getTimeSigList().Count - 1; i >= 0; i-- ) {
index = i;
if ( getTimeSigList()[i].Clock <= clock ) {
break;
}
}
int bar_count = 0;
if ( index >= 0 ) {
int last_clock = getTimeSigList()[index].Clock;
int t_bar_count = getTimeSigList()[index].BarCount;
int numerator = getTimeSigList()[index].Numerator;
int denominator = getTimeSigList()[index].Denominator;
int clock_per_bar = numerator * 480 * 4 / denominator;
bar_count = t_bar_count + (clock - last_clock) / clock_per_bar;
}
return bar_count;
}
/// <summary>
/// 4分の1拍子1音あたりのクロック数を取得します
/// </summary>
public int getTickPerQuarter() {
return m_tpq;
}
/// <summary>
/// テンポの変更情報を格納したテーブルを取得します
/// </summary>
public List<TempoTableEntry> getTempoList() {
return m_tempo_table;
}
/// <summary>
/// 拍子の変更情報を格納したテーブルを取得します
/// </summary>
public List<TimeSigTableEntry> getTimeSigList() {
return m_timesig_table;
}
public VsqTrack getTrack( int track ) {
return m_tracks[track];
}
public int getTrackCount() {
return m_tracks.Count;
}
/// <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 = pre_measure * 480 * 4 / denominator * numerator;
m_tpq = 480;
m_tracks = new List<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 );
#if DEBUG
Console.WriteLine( "VsqFile.ctor()" );
#endif
Mixer = new VsqMixer( 0, 0, 0, 0 );
Mixer.Slave.Add( new VsqMixerEntry( 0, 0, 0, 0 ) );
m_timesig_table = new List<TimeSigTableEntry>();
m_timesig_table.Add( new TimeSigTableEntry( 0, numerator, denominator, 0 ) );
m_tempo_table = new List<TempoTableEntry>();
m_tempo_table.Add( new TempoTableEntry( 0, tempo, 0.0 ) );
m_base_tempo = tempo;
m_premeasure_clocks = calculatePreMeasureInClock();
}
/// <summary>
/// vsqファイルからのコンストラクタ
/// </summary>
/// <param name="_fpath"></param>
public VsqFile( string _fpath ) {
VsqUtil.loadSingerConfigs();
m_tempo_table = new List<TempoTableEntry>();
m_timesig_table = new List<TimeSigTableEntry>();
m_tpq = 480;
// SMFをコンバートしたテキストファイルを作成
using ( TextMemoryStream tms = new TextMemoryStream( FileAccess.ReadWrite ) ) {
MidiFile mf = new MidiFile( _fpath );
m_tracks = new List<VsqTrack>();
int num_track = mf.getTrackCount();
for ( int i = 0; i < num_track; i++ ) {
m_tracks.Add( new VsqTrack( mf.getMidiEventList( i ) ) );
}
}
Master = (VsqMaster)m_tracks[1].getMaster().Clone();
Mixer = (VsqMixer)m_tracks[1].getMixer().Clone();
m_tracks[1].setMaster( null );
m_tracks[1].setMixer( null );
#if DEBUG
System.Diagnostics.Debug.WriteLine( "VsqFile.ctor()" );
#endif
int master_track = -1;
for ( int i = 0; i < m_tracks.Count; i++ ) {
#if DEBUG
System.Diagnostics.Debug.WriteLine( " m_tracks[i].Name=" + m_tracks[i].Name );
#endif
if ( m_tracks[i].Name == "Master Track" ) {
master_track = i;
break;
}
}
int prev_tempo;
int prev_index;
double prev_time;
if ( master_track >= 0 ) {
#region TempoListの作成
// MIDI event リストの取得
List<MidiEvent> midi_event = m_tracks[master_track].getTempoList();
// とりあえずtempo_tableに格納
prev_tempo = midi_event[0].Data[1] << 16 | midi_event[0].Data[2] << 8 | midi_event[0].Data[3];// midi_event[0].intValue[0];
m_base_tempo = prev_tempo;
prev_index = 0;
m_tempo_table.Add( new TempoTableEntry( (int)midi_event[0].Clock/* midi_event[0].index*/, prev_tempo, 0.0 ) );
double thistime;
prev_time = 0.0;
for ( int j = 1; j < midi_event.Count; j++ ) {
int current_tempo = midi_event[j].Data[1] << 16 | midi_event[j].Data[2] << 8 | midi_event[j].Data[3];
int current_index = (int)midi_event[j].Clock;
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;
}
m_tempo_table.Sort();
#endregion
#region TimeSigTableの作成
List<MidiEvent> time_sigs = m_tracks[master_track].getTimeSigList();
int dnomi = time_sigs[0].Data[1];
int numer = 1;
for ( int i = 0; i < time_sigs[0].Data[2]; i++ ) {
numer = numer * 2;
}
m_timesig_table.Add( new TimeSigTableEntry( 0, numer, dnomi, 0 ) );
for ( int j = 1; j < time_sigs.Count; j++ ) {
int numerator = m_timesig_table[j - 1].Numerator;
int denominator = m_timesig_table[j - 1].Denominator;
int clock = m_timesig_table[j - 1].Clock;
int bar_count = m_timesig_table[j - 1].BarCount;
int dif = 480 * 4 / denominator * numerator;//1小節が何クロックか
bar_count += ((int)time_sigs[j].Clock - clock) / dif;
dnomi = time_sigs[j].Data[1];
numer = 1;
for ( int i = 0; i < time_sigs[j].Data[2]; i++ ) {
numer = numer * 2;
}
m_timesig_table.Add( new TimeSigTableEntry( (int)time_sigs[j].Clock, numer, dnomi, bar_count ) );
}
#endregion
}
// 曲の長さを計算
updateTempoInfo();
updateTimesigInfo();
m_premeasure_clocks = calculatePreMeasureInClock();
updateTotalClocks();
#if DEBUG
System.Diagnostics.Debug.WriteLine( " m_total_clocks=" + TotalClocks );
#endif
}
/// <summary>
/// TimeSigTableの[*].Clockの部分を更新します
/// </summary>
public void updateTimesigInfo() {
#if DEBUG
Console.WriteLine( "VsqFile.UpdateTimesigInfo()" );
#endif
if ( m_timesig_table[0].Clock != 0 ) {
throw new ApplicationException( "initial timesig does not found" );
}
m_timesig_table[0].Clock = 0;
m_timesig_table.Sort();
for ( int j = 1; j < m_timesig_table.Count; j++ ) {
int numerator = m_timesig_table[j - 1].Numerator;
int denominator = m_timesig_table[j - 1].Denominator;
int clock = m_timesig_table[j - 1].Clock;
int bar_count = m_timesig_table[j - 1].BarCount;
int dif = 480 * 4 / denominator * numerator;//1小節が何クロックか
clock += (m_timesig_table[j].BarCount - bar_count) * dif;
m_timesig_table[j].Clock = clock;
}
m_premeasure_clocks = calculatePreMeasureInClock();
#if DEBUG
for ( int i = 0; i < m_timesig_table.Count; i++ ) {
Console.WriteLine( " clock=" + m_timesig_table[i].Clock + "; bar_count=" + m_timesig_table[i].BarCount + "; " + m_timesig_table[i].Numerator + "/" + m_timesig_table[i].Denominator );
}
#endif
}
/// <summary>
/// TempoTableの[*].Timeの部分を更新します
/// </summary>
public void updateTempoInfo() {
if ( m_tempo_table.Count == 0 ) {
m_tempo_table.Add( new TempoTableEntry( 0, getBaseTempo(), 0.0 ) );
}
m_tempo_table.Sort();
if ( m_tempo_table[0].Clock != 0 ) {
m_tempo_table[0].Time = (double)getBaseTempo() * (double)m_tempo_table[0].Clock / (getTickPerQuarter() * 1000000.0);
} else {
m_tempo_table[0].Time = 0.0;
}
double prev_time = m_tempo_table[0].Time;
int prev_clock = m_tempo_table[0].Clock;
int prev_tempo = m_tempo_table[0].Tempo;
double inv_tpq_sec = 1.0 / (getTickPerQuarter() * 1000000.0);
for ( int i = 1; i < m_tempo_table.Count; i++ ) {
m_tempo_table[i].Time = prev_time + (double)prev_tempo * (double)(m_tempo_table[i].Clock - prev_clock) * inv_tpq_sec;
prev_time = m_tempo_table[i].Time;
prev_tempo = m_tempo_table[i].Tempo;
prev_clock = m_tempo_table[i].Clock;
}
#if DEBUG
using ( StreamWriter sw = new StreamWriter( Path.Combine( System.Windows.Forms.Application.StartupPath, "vsq_tempo_info.txt" ) ) ) {
sw.WriteLine( "Clock\tTime\tTempo" );
for ( int i = 0; i < m_tempo_table.Count; i++ ) {
sw.WriteLine( m_tempo_table[i].Clock + "\t" + m_tempo_table[i].Time + "\t" + m_tempo_table[i].Tempo );
}
}
#endif
}
/// <summary>
/// VsqFile.Executeの実行直後などに、m_total_clocksの値を更新する
/// </summary>
public void updateTotalClocks() {
int max = m_premeasure_clocks;
for( int i = 1; i < m_tracks.Count; i++ ){
VsqTrack track = m_tracks[i];
for ( Iterator itr = track.getEventIterator(); itr.hasNext(); ) {
VsqEvent ve = (VsqEvent)itr.next();
max = Math.Max( max, ve.Clock + ve.ID.Length );
}
foreach ( string vct in _CURVES ) {
if ( vct == "VEL" ) {
continue;
}
if ( track.getCurve( vct ).getCount() > 0 ) {
int keys = track.getCurve( vct ).getCount();
int last_key = track.getCurve( vct ).getKeys()[keys - 1];
max = Math.Max( max, last_key );
}
}
}
TotalClocks = max;
}
/// <summary>
/// 曲の長さを取得する。(sec)
/// </summary>
public double getTotalSec() {
return getSecFromClock( (int)TotalClocks );
}
/// <summary>
/// 指定された番号のトラックに含まれる歌詞を指定されたファイルに出力します
/// </summary>
/// <param name="track"></param>
/// <param name="fpath"></param>
public void printLyricTable( int track, string fpath ) {
using ( StreamWriter sw = new StreamWriter( fpath ) ) {
for ( int i = 0; i < m_tracks[track].getEventCount(); i++ ) {
int Length;
// timesignal
int time_signal = m_tracks[track].getEvent( i ).Clock;
// イベントで指定されたIDがLyricであった場合
if ( m_tracks[track].getEvent( i ).ID.type == VsqIDType.Anote ) {
// 発音長を取得
Length = m_tracks[track].getEvent( i ).ID.Length;
// tempo_tableから、発音開始時のtempoを取得
int last = getTempoList().Count - 1;
int tempo = getTempoList()[last].Tempo;
int prev_index = getTempoList()[last].Clock;
double prev_time = getTempoList()[last].Time;
for ( int j = 1; j < getTempoList().Count; j++ ) {
if ( getTempoList()[j].Clock > time_signal ) {
tempo = getTempoList()[j - 1].Tempo;
prev_index = getTempoList()[j - 1].Clock;
prev_time = getTempoList()[j - 1].Time;
break;
}
}
int current_index = m_tracks[track].getEvent( 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);
sw.WriteLine( m_tracks[track].getEvent( i ).Clock + "," +
start_time.ToString( "0.000000" ) + "," +
end_time.ToString( "0.000000" ) + "," +
m_tracks[track].getEvent( i ).ID.LyricHandle.L0.Phrase + "," +
m_tracks[track].getEvent( i ).ID.LyricHandle.L0.getPhoneticSymbol() );
}
}
}
}
private static void printTrack( VsqFile vsq, int track, FileStream fs, int msPreSend ) {
#if DEBUG
Console.WriteLine( "PrintTrack" );
#endif
//VsqTrack item = Tracks[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.Position;
//トラック名
writeFlexibleLengthUnsignedLong( fs, 0x00 );//デルタタイム
fs.WriteByte( 0xff );//ステータスタイプ
fs.WriteByte( 0x03 );//イベントタイプSequence/Track Name
byte[] seq_name = bocoree.cp932.convert( vsq.m_tracks[track].Name );
writeFlexibleLengthUnsignedLong( fs, (ulong)seq_name.Length );//seq_nameの文字数
fs.Write( seq_name, 0, seq_name.Length );
//Meta Textを準備
using ( TextMemoryStream sr = new TextMemoryStream( FileAccess.ReadWrite ) ) {
vsq.m_tracks[track].printMetaText( sr, vsq.TotalClocks + 120, vsq.calculatePreMeasureInClock() );
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.WriteByte( 0xff );//ステータスタイプ
fs.WriteByte( 0x01 );//イベントタイプMeta Text
writeFlexibleLengthUnsignedLong( fs, (ulong)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.WriteByte( 0xff );
fs.WriteByte( 0x01 );
writeFlexibleLengthUnsignedLong( fs, (ulong)line_char.Length );
writeCharArray( fs, line_char );
line_count++;
tmp = getLinePrefix( line_count );
}
line_char = tmp.ToCharArray();
writeFlexibleLengthUnsignedLong( fs, 0x00 );
fs.WriteByte( 0xff );
fs.WriteByte( 0x01 );
writeFlexibleLengthUnsignedLong( fs, (ulong)line_char.Length );
writeCharArray( fs, line_char );
}
}
int last = 0;
VsqNrpn[] data = generateNRPN( vsq, track, msPreSend );
NrpnData[] nrpns = VsqNrpn.convert( data );
for ( int i = 0; i < nrpns.Length; i++ ) {
writeFlexibleLengthUnsignedLong( fs, (ulong)(nrpns[i].getClock() - last) );
fs.WriteByte( 0xb0 );
fs.WriteByte( nrpns[i].getParameter() );
fs.WriteByte( nrpns[i].Value );
last = nrpns[i].getClock();
}
//トラックエンド
VsqEvent last_event = vsq.m_tracks[track].getEvent( vsq.m_tracks[track].getEventCount() - 1 );
int last_clock = last_event.Clock + last_event.ID.Length;
writeFlexibleLengthUnsignedLong( fs, (ulong)last_clock );
fs.WriteByte( 0xff );
fs.WriteByte( 0x2f );
fs.WriteByte( 0x00 );
long pos = fs.Position;
fs.Seek( first_position - 4, SeekOrigin.Begin );
writeUnsignedInt( fs, (uint)(pos - first_position) );
fs.Seek( pos, SeekOrigin.Begin );
}
/// <summary>
/// 指定したクロックにおけるプリセンド・クロックを取得します
/// </summary>
/// <param name="clock"></param>
/// <param name="msPreSend"></param>
/// <returns></returns>
public int getPresendClockAt( int clock, int msPreSend ) {
double clock_msec = getSecFromClock( clock ) * 1000.0;
float draft_clock_sec = (float)(clock_msec - msPreSend) / 1000.0f;
int draft_clock = (int)Math.Floor( getClockFromSec( draft_clock_sec ) );
return clock - draft_clock;
}
/// <summary>
/// 指定したトラックから、Expression(DYN)のNRPNリストを作成します
/// </summary>
/// <param name="track"></param>
/// <param name="msPreSend"></param>
/// <returns></returns>
public static VsqNrpn[] generateExpressionNRPN( VsqFile vsq, int track, int msPreSend ) {
List<VsqNrpn> ret = new List<VsqNrpn>();
int count = vsq.m_tracks[track].getCurve( "DYN" ).getCount();
byte delay0, delay1;
getMsbAndLsb( (ushort)msPreSend, out delay0, out delay1 );
int start_clock = (int)vsq.getClockFromSec( msPreSend / 1000.0 ) + 1;
int start_value = vsq.m_tracks[track].getCurve( "DYN" ).getElement( start_clock );
VsqNrpn add0 = new VsqNrpn( start_clock - vsq.getPresendClockAt( start_clock, msPreSend ),
(ushort)NRPN.CC_E_VESION_AND_DEVICE,
0x00, 0x00 );
add0.append( NRPN.CC_E_DELAY, delay0, delay1 );
add0.append( NRPN.CC_E_EXPRESSION, (byte)start_value );
ret.Add( add0 );
int[] keys = vsq.m_tracks[track].getCurve( "DYN" ).getKeys();
for ( int i = 0; i < keys.Length; i++ ) {
if ( keys[i] > start_clock ) {
VsqNrpn add = new VsqNrpn( keys[i] - vsq.getPresendClockAt( keys[i], msPreSend ),
(ushort)NRPN.CC_E_EXPRESSION,
(byte)vsq.m_tracks[track].getCurve( "DYN" ).getElement( keys[i] ) );
ret.Add( add );
}
}
return ret.ToArray();
}
/// <summary>
/// 先頭に記録されるNRPNを作成します
/// </summary>
/// <returns></returns>
public static VsqNrpn generateHeaderNRPN() {
VsqNrpn ret = new VsqNrpn( 0, (ushort)NRPN.CC_BS_VERSION_AND_DEVICE, 0x00, 0x00 );
ret.append( NRPN.CC_BS_DELAY, 0x00, 0x00 );
ret.append( NRPN.CC_BS_LANGUAGE_TYPE, 0x00 );
return ret;
}
/// <summary>
/// 歌手変更イベントからNRPNを作成します
/// </summary>
/// <param name="vsq"></param>
/// <param name="ve"></param>
/// <param name="msPreSend"></param>
/// <returns></returns>
public static VsqNrpn[] generateSingerNRPN( VsqFile vsq, VsqEvent ve, int msPreSend ) {
int clock = ve.Clock;
double clock_msec = vsq.getSecFromClock( clock ) * 1000.0;
int ttempo = vsq.getTempoAt( clock );
double tempo = 6e7 / ttempo;
//double sStart = SecFromClock( ve.Clock );
double msEnd = vsq.getSecFromClock( ve.Clock + ve.ID.Length ) * 1000.0;
ushort duration = (ushort)Math.Ceiling( msEnd - clock_msec );
#if DEBUG
Console.WriteLine( "GenerateNoteNRPN" );
Console.WriteLine( " duration=" + duration );
#endif
byte duration0, duration1;
getMsbAndLsb( duration, out duration0, out duration1 );
byte delay0, delay1;
getMsbAndLsb( (ushort)msPreSend, out delay0, out delay1 );
List<VsqNrpn> ret = new List<VsqNrpn>();
int i = clock - vsq.getPresendClockAt( clock, msPreSend );
VsqNrpn add = new VsqNrpn( i, (ushort)NRPN.CC_BS_VERSION_AND_DEVICE, 0x00, 0x00 );
add.append( NRPN.CC_BS_DELAY, delay0, delay1 );
add.append( NRPN.CC_BS_LANGUAGE_TYPE, (byte)ve.ID.IconHandle.Language );
add.append( NRPN.PC_VERSION_AND_DEVICE, 0x00, 0x00 );
add.append( NRPN.PC_VOICE_TYPE, (byte)ve.ID.IconHandle.Program );
return new VsqNrpn[] { add };
}
/// <summary>
/// 音符イベントからNRPNを作成します
/// </summary>
/// <param name="ve"></param>
/// <param name="msPreSend"></param>
/// <param name="note_loc"></param>
/// <returns></returns>
public static VsqNrpn generateNoteNRPN( VsqFile vsq, VsqEvent ve, int msPreSend, byte note_loc ) {
int clock = ve.Clock;
double clock_msec = vsq.getSecFromClock( clock ) * 1000.0;
int ttempo = vsq.getTempoAt( clock );
double tempo = 6e7 / ttempo;
double msEnd = vsq.getSecFromClock( ve.Clock + ve.ID.Length ) * 1000.0;
ushort duration = (ushort)Math.Ceiling( msEnd - clock_msec );
byte duration0, duration1;
getMsbAndLsb( duration, out duration0, out duration1 );
byte delay0, delay1;
getMsbAndLsb( (ushort)msPreSend, out delay0, out delay1 );
VsqNrpn add = new VsqNrpn( clock - vsq.getPresendClockAt( clock, msPreSend ), (ushort)NRPN.CVM_NM_DELAY, delay0, delay1 );
add.append( NRPN.CVM_NM_NOTE_NUMBER, (byte)ve.ID.Note ); // Note number
add.append( NRPN.CVM_NM_VELOCITY, (byte)ve.ID.Dynamics ); // Velocity
add.append( NRPN.CVM_NM_NOTE_DURATION, duration0, duration1 ); // Note duration
add.append( NRPN.CVM_NM_NOTE_LOCATION, note_loc ); // Note Location
if ( ve.ID.VibratoHandle != null ) {
add.append( NRPN.CVM_NM_INDEX_OF_VIBRATO_DB, 0x00, 0x00 );
int vibrato_type = (int)VibratoTypeUtil.getVibratoTypeFromIconID( ve.ID.VibratoHandle.IconID );
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( NRPN.CVM_NM_VIBRATO_CONFIG, (byte)vibrato_type, bVibratoDuration );
add.append( NRPN.CVM_NM_VIBRATO_DELAY, 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( NRPN.CVM_NM_PHONETIC_SYMBOL_BYTES, (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( (NRPN)((0x50 << 8) | (byte)(0x13 + count)), (byte)chars[k], (byte)ve.ID.LyricHandle.L0.getConsonantAdjustment()[j] ); // Phonetic symbol j
} else {
add.append( (NRPN)((0x50 << 8) | (byte)(0x13 + count)), (byte)chars[k] ); // Phonetic symbol j
}
}
}
add.append( NRPN.CVM_NM_PHONETIC_SYMBOL_CONTINUATION, 0x7f ); // End of phonetic symbols
add.append( NRPN.CVM_NM_V1MEAN, 0x04 );// 0x50(v1mean)
add.append( NRPN.CVM_NM_D1MEAN, 0x08 );// 0x51(d1mean)
add.append( NRPN.CVM_NM_D1MEAN_FIRST_NOTE, 0x14 );// 0x52(d1meanFirstNote)
add.append( NRPN.CVM_NM_D2MEAN, 0x1c );// 0x53(d2mean)
add.append( NRPN.CVM_NM_D4MEAN, 0x18 );// 0x54(d4mean)
add.append( NRPN.CVM_NM_PMEAN_ONSET_FIRST_NOTE, 0x0a ); // 055(pMeanOnsetFirstNote)
add.append( NRPN.CVM_NM_VMEAN_NOTE_TRNSITION, 0x0c ); // 0x56(vMeanNoteTransition)
add.append( NRPN.CVM_NM_PMEAN_ENDING_NOTE, 0x0c );// 0x57(pMeanEndingNote)
add.append( NRPN.CVM_NM_ADD_PORTAMENTO, (byte)ve.ID.PMbPortamentoUse );// 0x58(AddScoopToUpInternals&AddPortamentoToDownIntervals)
add.append( NRPN.CVM_NM_CHANGE_AFTER_PEAK, 0x32 );// 0x59(changeAfterPeak)
byte accent = (byte)(0x64 * ve.ID.DEMaccent / 100.0);
add.append( NRPN.CVM_NM_ACCENT, accent );// 0x5a(Accent)
add.append( NRPN.CVM_NM_NOTE_MESSAGE_CONTINUATION, 0x7f );// 0x7f(Note message continuation)
return add;
}
/// <summary>
/// 指定したトラックのデータからNRPNを作成します
/// </summary>
/// <param name="vsq"></param>
/// <param name="track"></param>
/// <param name="msPreSend"></param>
/// <param name="clock_start"></param>
/// <param name="clock_end"></param>
/// <returns></returns>
public static VsqNrpn[] generateNRPN( VsqFile vsq, int track, int msPreSend, int clock_start, int clock_end ) {
VsqFile temp = (VsqFile)vsq.Clone();
removePart( temp, clock_end, vsq.TotalClocks );
if ( 0 < clock_start ) {
removePart( temp, 0, clock_start );
}
temp.Master.PreMeasure = 1;
temp.m_premeasure_clocks = temp.getClockFromBarCount( 1 );
VsqNrpn[] ret = generateNRPN( temp, track, msPreSend );
temp = null;
return ret;
}
/// <summary>
/// 指定したトラックのデータからNRPNを作成します
/// </summary>
/// <param name="vsq"></param>
/// <param name="track"></param>
/// <param name="msPreSend"></param>
/// <returns></returns>
public static VsqNrpn[] generateNRPN( VsqFile vsq, int track, int msPreSend ) {
#if DEBUG
Console.WriteLine( "GenerateNRPN(VsqTrack,int,int,int,int)" );
#endif
List<VsqNrpn> list = new List<VsqNrpn>();
int count = vsq.m_tracks[track].getEventCount();
int note_start = 0;
int note_end = vsq.m_tracks[track].getEventCount() - 1;
for ( int i = 0; i < vsq.m_tracks[track].getEventCount(); i++ ) {
if ( 0 <= vsq.m_tracks[track].getEvent( i ).Clock ) {
note_start = i;
break;
}
note_start = i;
}
for ( int i = vsq.m_tracks[track].getEventCount() - 1; i >= 0; i-- ) {
if ( vsq.m_tracks[track].getEvent( i ).Clock <= vsq.TotalClocks ) {
note_end = i;
break;
}
}
// 最初の歌手を決める
int singer_event = -1;
for ( int i = note_start; i >= 0; i-- ) {
if ( vsq.m_tracks[track].getEvent( i ).ID.type == VsqIDType.Singer ) {
singer_event = i;
break;
}
}
if ( singer_event >= 0 ) { //見つかった場合
list.AddRange( generateSingerNRPN( vsq, vsq.m_tracks[track].getEvent( singer_event ), 0 ) );
} else { //多分ありえないと思うが、歌手が不明の場合。
throw new ApplicationException( "first singer was not specified" );
list.Add( new VsqNrpn( 0, (ushort)NRPN.CC_BS_LANGUAGE_TYPE, 0 ) );
list.Add( new VsqNrpn( 0, (ushort)NRPN.PC_VOICE_TYPE, 0 ) );
}
#if DEBUG
Console.WriteLine( " note_start,note_end=" + note_start + "," + note_end );
#endif
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 ( vsq.m_tracks[track].getEvent( i ).ID.type == VsqIDType.Anote ) {
list.Add( generateNoteNRPN( vsq,
vsq.m_tracks[track].getEvent( i ),
msPreSend,
note_loc ) );
list.AddRange( generateVibratoNRPN( vsq,
vsq.m_tracks[track].getEvent( i ),
msPreSend ) );
} else if ( vsq.m_tracks[track].getEvent( i ).ID.type == VsqIDType.Singer ) {
if ( i > note_start && i != singer_event) {
list.AddRange( generateSingerNRPN( vsq, vsq.m_tracks[track].getEvent( i ), msPreSend ) );
}
}
}
list.AddRange( generateVoiceChangeParameterNRPN( vsq, track, msPreSend ) );
list.AddRange( generateExpressionNRPN( vsq, track, msPreSend ) );
list.AddRange( generatePitchBendSensitivityNRPN( vsq, track, msPreSend ) );
list.AddRange( generatePitchBendNRPN( vsq, track, msPreSend ) );
list.Sort();
List<VsqNrpn> merged = new List<VsqNrpn>();
for ( int i = 0; i < list.Count; i++ ) {
merged.AddRange( list[i].expand() );
}
return merged.ToArray();
}
/// <summary>
/// 指定したトラックから、PitchBendのNRPNを作成します
/// </summary>
/// <param name="vsq"></param>
/// <param name="track"></param>
/// <param name="msPreSend"></param>
/// <returns></returns>
public static VsqNrpn[] generatePitchBendNRPN( VsqFile vsq, int track, int msPreSend ) {
List<VsqNrpn> ret = new List<VsqNrpn>();
int[] keys = vsq.m_tracks[track].getCurve( "PIT" ).getKeys();
int count = keys.Length;
byte delay0, delay1;
getMsbAndLsb( (ushort)msPreSend, out delay0, out delay1 );
int start_clock = (int)vsq.getClockFromSec( msPreSend / 1000.0 ) + 1;// 0 < vsq.m_premeasure_clocks ? vsq.m_premeasure_clocks : 0;
VsqNrpn add0 = new VsqNrpn( start_clock - vsq.getPresendClockAt( start_clock, msPreSend ),
(ushort)NRPN.PB_VERSION_AND_DEVICE,
0x00,
0x00 );
add0.append( NRPN.PB_DELAY, delay0, delay1 );
ushort start_value = (ushort)(vsq.m_tracks[track].getCurve( "PIT" ).getElement( start_clock ) + 0x2000);
byte start_value0, start_value1;
getMsbAndLsb( start_value, out start_value0, out start_value1 );
add0.append( NRPN.PB_PITCH_BEND, start_value0, start_value1 );
ret.Add( add0 );
for ( int i = 0; i < keys.Length; i++ ) {
if ( keys[i] > start_clock ) {
ushort value = (ushort)(vsq.m_tracks[track].getCurve( "PIT" ).getElement( keys[i] ) + 0x2000);
byte value0, value1;
getMsbAndLsb( value, out value0, out value1 );
VsqNrpn add = new VsqNrpn( keys[i] - vsq.getPresendClockAt( keys[i], msPreSend ),
(ushort)NRPN.PB_PITCH_BEND,
value0,
value1 );
ret.Add( add );
}
}
return ret.ToArray();
}
/// <summary>
/// 指定したトラックからPitchBendSensitivityのNRPNを作成します
/// </summary>
/// <param name="vsq"></param>
/// <param name="track"></param>
/// <param name="msPreSend"></param>
/// <returns></returns>
public static VsqNrpn[] generatePitchBendSensitivityNRPN( VsqFile vsq, int track, int msPreSend ) {
List<VsqNrpn> ret = new List<VsqNrpn>();
int[] keys = vsq.m_tracks[track].getCurve( "PBS" ).getKeys();
int count = keys.Length;
byte delay0, delay1;
getMsbAndLsb( (ushort)msPreSend, out delay0, out delay1 );
int start_clock = (int)vsq.getClockFromSec( msPreSend / 1000.0 ) + 1;// 0 < vsq.m_premeasure_clocks ? vsq.m_premeasure_clocks : 0;
int start_value = vsq.m_tracks[track].getCurve( "PBS" ).getElement( start_clock );
VsqNrpn add0 = new VsqNrpn( start_clock - vsq.getPresendClockAt( start_clock, msPreSend ),
(ushort)NRPN.CC_PBS_VERSION_AND_DEVICE,
0x00,
0x00 );
add0.append( NRPN.CC_PBS_DELAY, delay0, delay1 );
add0.append( NRPN.CC_PBS_PITCH_BEND_SENSITIVITY, (byte)start_value, 0x00 );
ret.Add( add0 );
for ( int i = 0; i < keys.Length; i++ ) {
if ( keys[i] > start_clock ) {
VsqNrpn add = new VsqNrpn( keys[i] - vsq.getPresendClockAt( keys[i], msPreSend ),
(ushort)NRPN.CC_PBS_PITCH_BEND_SENSITIVITY,
(byte)vsq.m_tracks[track].getCurve( "PBS" ).getElement( keys[i] ),
0x00 );
ret.Add( add );
}
}
return ret.ToArray();
}
/// <summary>
/// 指定した音符イベントからビブラート出力用のNRPNを作成します
/// </summary>
/// <param name="vsq"></param>
/// <param name="ve"></param>
/// <param name="msPreSend"></param>
/// <returns></returns>
public static VsqNrpn[] generateVibratoNRPN( VsqFile vsq, VsqEvent ve, int msPreSend ) {
List<VsqNrpn> ret = new List<VsqNrpn>();
if ( ve.ID.VibratoHandle != null ){
int vclock = ve.Clock + ve.ID.VibratoDelay;
byte delay0, delay1;
getMsbAndLsb( (ushort)msPreSend, out delay0, out delay1 );
VsqNrpn add2 = new VsqNrpn( vclock - vsq.getPresendClockAt( vclock, msPreSend ),
(ushort)NRPN.CC_VD_VERSION_AND_DEVICE,
0x00,
0x00 );
add2.append( NRPN.CC_VD_DELAY, delay0, delay1 );
add2.append( NRPN.CC_VD_VIBRATO_DEPTH, (byte)ve.ID.VibratoHandle.StartDepth );
add2.append( NRPN.CC_VR_VIBRATO_RATE, (byte)ve.ID.VibratoHandle.StartRate );
ret.Add( add2 );
int vlength = ve.Clock + ve.ID.Length - ve.ID.VibratoDelay;
if ( ve.ID.VibratoHandle.RateBP.getCount() > 0 ) {
for ( int i = 0; i < ve.ID.VibratoHandle.RateBP.getCount(); i++ ) {
float percent = ve.ID.VibratoHandle.RateBP.getElement( i ).X;
int cl = vclock + (int)(percent * vlength);
ret.Add( new VsqNrpn( cl, (ushort)NRPN.CC_VR_VIBRATO_RATE, (byte)ve.ID.VibratoHandle.RateBP.getElement( i ).Y ) );
}
}
if ( ve.ID.VibratoHandle.DepthBP.getCount() > 0 ) {
for ( int i = 0; i < ve.ID.VibratoHandle.DepthBP.getCount(); i++ ) {
float percent = ve.ID.VibratoHandle.DepthBP.getElement( i ).X;
int cl = vclock + (int)(percent * vlength);
ret.Add( new VsqNrpn( cl, (ushort)NRPN.CC_VD_VIBRATO_DEPTH, (byte)ve.ID.VibratoHandle.DepthBP.getElement( i ).Y ) );
}
}
}
ret.Sort();
return ret.ToArray();
}
/// <summary>
/// 指定したトラックから、VoiceChangeParameterのNRPNのリストを作成します
/// </summary>
/// <param name="vsq"></param>
/// <param name="track"></param>
/// <param name="msPreSend"></param>
/// <returns></returns>
public static VsqNrpn[] generateVoiceChangeParameterNRPN( VsqFile vsq, int track, int msPreSend ) {
int premeasure_clock = vsq.m_premeasure_clocks;
byte delay0, delay1;
getMsbAndLsb( (ushort)msPreSend, out delay0, out delay1 );
List<VsqNrpn> res = new List<VsqNrpn>();
int start_clock = (0 < premeasure_clock) ? premeasure_clock : 0;
VsqNrpn ret = new VsqNrpn( start_clock - vsq.getPresendClockAt( start_clock, msPreSend ),
(ushort)NRPN.VCP_VERSION_AND_DEVICE,
0x00,
0x00 );
ret.append( NRPN.VCP_DELAY, delay0, delay1 ); // Voice Change Parameter delay
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER_ID, 0x31 ); // BRE
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER, (byte)vsq.m_tracks[track].getCurve( "BRE" ).getElement( start_clock ) );
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER_ID, 0x32 ); // BRI
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER, (byte)vsq.m_tracks[track].getCurve( "BRI" ).getElement( start_clock ) );
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER_ID, 0x33 ); // CLE
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER, (byte)vsq.m_tracks[track].getCurve( "CLE" ).getElement( start_clock ) );
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER_ID, 0x34 ); // POR
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER, (byte)vsq.m_tracks[track].getCurve( "POR" ).getElement( start_clock ) );
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER_ID, 0x35 ); // OPE
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER, (byte)vsq.m_tracks[track].getCurve( "OPE" ).getElement( start_clock ) );
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER_ID, 0x70 ); // GEN
ret.append( NRPN.VCP_VOICE_CHANGE_PARAMETER, (byte)vsq.m_tracks[track].getCurve( "GEN" ).getElement( start_clock ) );
res.Add( ret );
foreach ( string vct in _CURVES ) {
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;
}
int[] keys = vsq.m_tracks[track].getCurve( vct ).getKeys();
for ( int i = 0; i < keys.Length; i++ ) {
if ( keys[i] > start_clock ) {
VsqNrpn add = new VsqNrpn( keys[i] - vsq.getPresendClockAt( keys[i], msPreSend ),
(ushort)NRPN.VCP_VOICE_CHANGE_PARAMETER_ID,
lsb );
add.append( NRPN.VCP_VOICE_CHANGE_PARAMETER, (byte)vsq.m_tracks[track].getCurve( vct ).getElement( keys[i] ) );
res.Add( add );
}
}
}
return res.ToArray();
}
private static void getMsbAndLsb( ushort value, out byte msb, out byte lsb ) {
msb = (byte)(value >> 7);
lsb = (byte)(value - (msb << 7));
}
/// <summary>
/// このインスタンスをファイルに出力します
/// </summary>
/// <param name="file"></param>
public void write( string file ) {
write( file, 500 );
}
/// <summary>
/// このインスタンスをファイルに出力します
/// </summary>
/// <param name="file"></param>
/// <param name="msPreSend">プリセンドタイム(msec)</param>
public void write( string file, int msPreSend ) {
#if DEBUG
Console.WriteLine( "VsqFile.Write(string)" );
#endif
int last_clock = 0;
for ( int track = 1; track < m_tracks.Count; track++ ) {
if ( m_tracks[track].getEventCount() > 0 ) {
int index = m_tracks[track].getEventCount() - 1;
VsqEvent last = m_tracks[track].getEvent( index );
last_clock = Math.Max( last_clock, last.Clock + last.ID.Length );
}
}
using ( FileStream fs = new FileStream( file, FileMode.Create ) ) {
long first_position;//チャンクの先頭のファイル位置
#region
//チャンクタイプ
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, (ushort)this.m_tracks.Count );
//時間単位
fs.WriteByte( 0x01 );
fs.WriteByte( 0xe0 );
#endregion
#region Master Track
//チャンクタイプ
fs.Write( _MTRK, 0, 4 );
//データ長。とりあえず0を入れておく
fs.Write( new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0, 4 );
first_position = fs.Position;
//トラック名
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 );
List<MidiEvent> events = new List<MidiEvent>();
foreach ( TimeSigTableEntry entry in m_timesig_table ) {
events.Add( MidiEvent.generateTimeSigEvent( entry.Clock, entry.Numerator, entry.Denominator ) );
last_clock = Math.Max( last_clock, entry.Clock );
}
foreach ( TempoTableEntry entry in m_tempo_table ) {
events.Add( MidiEvent.generateTempoChangeEvent( entry.Clock, entry.Tempo ) );
last_clock = Math.Max( last_clock, entry.Clock );
}
#if DEBUG
Console.WriteLine( " events.Count=" + events.Count );
#endif
events.Sort();
long last = 0;
foreach ( MidiEvent me in events ) {
#if DEBUG
Console.WriteLine( "me.Clock=" + me.Clock );
#endif
writeFlexibleLengthUnsignedLong( fs, (ulong)(me.Clock - last) );
me.writeData( fs );
last = me.Clock;
}
//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.Position;
fs.Seek( first_position - 4, SeekOrigin.Begin );
writeUnsignedInt( fs, (uint)(pos - first_position) );
fs.Seek( pos, SeekOrigin.Begin );
#endregion
#region
VsqFile temp = (VsqFile)this.Clone();
temp.m_tracks[1].setMaster( (VsqMaster)Master.Clone() );
temp.m_tracks[1].setMixer( (VsqMixer)Mixer.Clone() );
printTrack( temp, 1, fs, msPreSend );
for ( int track = 2; track < m_tracks.Count; track++ ) {
printTrack( this, track, fs, msPreSend );
}
#endregion
}
}
/// <summary>
/// メタテキストの行番号から、各行先頭のプレフィクス文字列("DM:0123:"等)を作成します
/// </summary>
/// <param name="count"></param>
/// <returns></returns>
public static string getLinePrefix( int count ) {
int digits = getHowManyDigits( count );
int c = (digits - 1) / 4 + 1;
string format = "";
for ( int i = 0; i < c; i++ ) {
format += "0000";
}
return "DM:" + count.ToString( format ) + ":";
}
/// <summary>
/// 数numberの桁数を調べます。10進数のみ
/// </summary>
/// <param name="number"></param>
/// <returns></returns>
private static int getHowManyDigits( 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 static void writeCharArray( FileStream fs, char[] item ) {
for ( int i = 0; i < item.Length; i++ ) {
fs.WriteByte( (byte)item[i] );
}
}
/// <summary>
/// ushort値をビッグエンディアンでfsに書き込みます
/// </summary>
/// <param name="data"></param>
public void writeUnsignedShort( FileStream fs, ushort data ) {
byte[] dat = BitConverter.GetBytes( data );
if ( BitConverter.IsLittleEndian ) {
Array.Reverse( dat );
}
fs.Write( dat, 0, dat.Length );
}
/// <summary>
/// uint値をビッグエンディアンでfsに書き込みます
/// </summary>
/// <param name="data"></param>
public static void writeUnsignedInt( FileStream fs, uint data ) {
byte[] dat = BitConverter.GetBytes( data );
if ( BitConverter.IsLittleEndian ) {
Array.Reverse( dat );
}
fs.Write( dat, 0, dat.Length );
}
/// <summary>
/// SMFの可変長数値表現を使って、ulongをbyte[]に変換します
/// </summary>
/// <param name="number"></param>
public static byte[] getBytesFlexibleLengthUnsignedLong( ulong number ) {
bool[] bits = new bool[64];
ulong 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++ ) {
uint num = 0;
uint 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( Stream fs, ulong number ) {
byte[] bytes = getBytesFlexibleLengthUnsignedLong( number );
fs.Write( bytes, 0, bytes.Length );
}
}
}