public static bool edit( VsqFile vsq ) { ResoXAmp form = new ResoXAmp(); if ( form.ShowDialog() != DialogResult.OK ) { return false; } VsqTrack track = vsq.Track[AppManager.Selected]; VsqBPList source = track.getCurve( CurveType.reso1amp.getName() ); VsqBPList reso2amp = (VsqBPList)track.getCurve( CurveType.reso2amp.getName() ).clone(); VsqBPList reso3amp = (VsqBPList)track.getCurve( CurveType.reso3amp.getName() ).clone(); VsqBPList reso4amp = (VsqBPList)track.getCurve( CurveType.reso4amp.getName() ).clone(); Console.WriteLine( "AmplifyCoeffReso2=" + AmplifyCoeffReso2 ); Console.WriteLine( "AmplifyCoeffReso3=" + AmplifyCoeffReso3 ); Console.WriteLine( "AmplifyCoeffReso4=" + AmplifyCoeffReso4 ); amplify( source, reso2amp, AmplifyCoeffReso2 ); amplify( source, reso3amp, AmplifyCoeffReso3 ); amplify( source, reso4amp, AmplifyCoeffReso4 ); track.setCurve( CurveType.reso2amp.getName(), reso2amp ); track.setCurve( CurveType.reso3amp.getName(), reso3amp ); track.setCurve( CurveType.reso4amp.getName(), reso4amp ); Console.WriteLine( "reso2amp.getCount()=" + reso2amp.size() ); Console.WriteLine( "reso3amp.getCount()=" + reso3amp.size() ); Console.WriteLine( "reso4amp.getCount()=" + reso4amp.size() ); MessageBox.Show( "done" ); return true; }
public void writeSingerTest() { var sequence = new VsqFile("singer", 1, 4, 4, 500000); var premeasaure_clock = sequence.getPreMeasureClocks(); var temporary = Path.GetTempFileName(); var writer = new VsqxWriter(); Assert.DoesNotThrow(() => writer.write(sequence, temporary)); var text = File.ReadAllText(temporary); var document = XElement.Parse(text); XNamespace ns = "http://www.yamaha.co.jp/vocaloid/schema/vsq3/"; var singer = document .Descendants(ns + "vsTrack") .First() .Descendants(ns + "musicalPart") .First() .Descendants(ns + "singer") .FirstOrDefault(); Assert.NotNull(singer); var pos_tick = singer.Descendants(ns + "posTick").FirstOrDefault(); Assert.NotNull(pos_tick); Assert.AreEqual("0", pos_tick.Value); }
public static bool edit(VsqFile vsq) { ResoXAmp form = new ResoXAmp(); if (form.ShowDialog() != DialogResult.OK) { return(false); } VsqTrack track = vsq.Track[AppManager.Selected]; VsqBPList source = track.getCurve(CurveType.reso1amp.getName()); VsqBPList reso2amp = (VsqBPList)track.getCurve(CurveType.reso2amp.getName()).clone(); VsqBPList reso3amp = (VsqBPList)track.getCurve(CurveType.reso3amp.getName()).clone(); VsqBPList reso4amp = (VsqBPList)track.getCurve(CurveType.reso4amp.getName()).clone(); Console.WriteLine("AmplifyCoeffReso2=" + AmplifyCoeffReso2); Console.WriteLine("AmplifyCoeffReso3=" + AmplifyCoeffReso3); Console.WriteLine("AmplifyCoeffReso4=" + AmplifyCoeffReso4); amplify(source, reso2amp, AmplifyCoeffReso2); amplify(source, reso3amp, AmplifyCoeffReso3); amplify(source, reso4amp, AmplifyCoeffReso4); track.setCurve(CurveType.reso2amp.getName(), reso2amp); track.setCurve(CurveType.reso3amp.getName(), reso3amp); track.setCurve(CurveType.reso4amp.getName(), reso4amp); Console.WriteLine("reso2amp.getCount()=" + reso2amp.size()); Console.WriteLine("reso3amp.getCount()=" + reso3amp.size()); Console.WriteLine("reso4amp.getCount()=" + reso4amp.size()); MessageBox.Show("done"); return(true); }
public static bool Edit( VsqFile vsq ) { Time_Shift ts = new Time_Shift(); if ( ts.ShowDialog() != DialogResult.OK ) { return false; } return true; }
public static VsqCommand generateCommandReplace(VsqFile vsq) { VsqCommand command = new VsqCommand(); command.Args = new Object[1]; command.Type = VsqCommandType.REPLACE; command.Args[0] = (VsqFile)vsq.clone(); return(command); }
private static void hamori(VsqFile vsq, int basecode, int opt) { int[][] steps = new int[][] { // C Major C D E F G A B // 7度上 new int[] { 11, 11, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10 }, // 6度上 new int[] { 9, 9, 9, 9, 8, 9, 9, 9, 9, 8, 8, 9 }, // 5度上 new int[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6 }, // 4度上 new int[] { 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5 }, // 3度上 new int[] { 4, 4, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3 }, // 2度上 new int[] { 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 } }; int[] step; if (opt == 6) { return; } if (opt < 6) { step = steps[opt]; } else { step = steps[opt - 7]; for (int i = 0; i < 12; i++) { step[i] -= 12; } } int note, tmp; VsqTrack track = vsq.Track[AppManager.Selected]; for (Iterator <SelectedEventEntry> itr = AppManager.getSelectedEventIterator(); itr.hasNext();) { VsqEvent item = track.findEventFromID(((SelectedEventEntry)itr.next()).original.InternalID); if (item.ID.type == VsqIDType.Anote) { tmp = (item.ID.Note + 12 - basecode) % 12; note = item.ID.Note + step[tmp]; if (note < 0) { note = 0; } if (127 < note) { note = 127; } item.ID.Note = note; } } }
public static void Main( string[] args ){ int unit = int.Parse( args[0] ); System.Windows.Forms.OpenFileDialog d = new System.Windows.Forms.OpenFileDialog(); if ( d.ShowDialog() == System.Windows.Forms.DialogResult.OK ) { string f = d.FileName; string dir = System.IO.Path.GetDirectoryName( f ); string name = System.IO.Path.GetFileNameWithoutExtension( f ); Console.WriteLine( "name=" + name ); int index = name.IndexOf( "-001" ); string basename = ""; if ( index >= 0 ) { basename = name.Substring( 0, index ); } int count = 0; Dictionary<int, int> dict = new Dictionary<int, int>(); while ( true ) { count++; string open = PortUtil.combinePath( dir, basename + "-" + PortUtil.formatDecimal( "000", count ) + ".vsq" ); Console.WriteLine( "open=" + open ); if ( !PortUtil.isFileExists( open ) ) { break; } VsqFile vsq = new VsqFile( open, "Shift_JIS" ); int i = -1; int b = 3840; for ( Iterator itr = vsq.Track.get( 1 ).getNoteEventIterator(); itr.hasNext(); ) { i++; VsqEvent item = (VsqEvent)itr.next(); if ( i == 0 ){ continue; } int ideal_start = b + i * unit; int shift = item.Clock - ideal_start; if( dict.ContainsKey( shift ) ){ dict[shift] = dict[shift] + 1; }else{ dict.Add( shift, 1 ); } } } int max = 0; int min = 0; foreach( int key in dict.Keys ){ max = Math.Max( max, key ); min = Math.Min( min, key ); } using ( System.IO.StreamWriter sw = new System.IO.StreamWriter( PortUtil.combinePath( dir, basename + ".txt" ) ) ) { for( int i = min; i <= max; i++ ){ if ( dict.ContainsKey( i ) ){ sw.WriteLine( i + "\t" + dict[i] ); }else{ sw.WriteLine( i + "\t0" ); } } } } }
public static bool Edit(VsqFile vsq) { Time_Shift ts = new Time_Shift(); if (ts.ShowDialog() != DialogResult.OK) { return(false); } return(true); }
public static bool Edit( VsqFile Vsq ) { for ( int i = 1; i < Vsq.Track.size(); i++ ) { for ( int j = 0; j < Vsq.Track.get( i ).getEventCount(); j++ ) { VsqEvent item = Vsq.Track.get( i ).getEvent( j ); if ( item.ID.type == VsqIDType.Anote ) { item.ID.LyricHandle.L0.Phrase = KanaDeRomanization.Attach( item.ID.LyricHandle.L0.Phrase ); } } } return true; }
public static void Main( string[] args ){ if( args.Length < 3 ){ Console.WriteLine( "makeMids [words-list] [singer(ex. Miku)] [DSB(ex. DSB303)] [extension(ex. .vsq)] [start-index]" ); return; } string list = args[0]; string singer = args[1]; string dsb = args[2]; string ext = args[3]; int start_index = 0; if( args.Length >= 5 ){ start_index = int.Parse( args[4] ); } if( !Directory.Exists( singer ) ){ Directory.CreateDirectory( singer ); } using( StreamReader sr = new StreamReader( list ) ){ string line = ""; VsqFile src = new VsqFile( singer, 2, 4, 4, 500000 ); VsqFile vsq = (VsqFile)src.clone(); int clock = 480 * 4 * 2; int count = 0; int numVsqs = start_index; while( (line = sr.ReadLine()) != null ){ VsqEvent item = new VsqEvent(); item.Clock = clock; item.ID = new VsqID(); item.ID.type = VsqIDType.Anote; item.ID.setLength( 240 ); item.ID.Note = 64; item.ID.LyricHandle = new LyricHandle(); item.ID.LyricHandle.L0 = new Lyric( line, "u:" ); vsq.Track.get( 1 ).addEvent( item, count + 1 ); clock += 240; count++; if( count > 5000 ){ vsq.Track.get( 1 ).getCommon().Version = dsb; vsq.Track.get( 1 ).sortEvent(); vsq.write( singer + "\\" + numVsqs + ext ); numVsqs++; vsq = null; vsq = (VsqFile)src.clone(); clock = 480 * 4 * 2; count = 0; } } if( count > 0 ){ vsq.Track.get( 1 ).getCommon().Version = dsb; vsq.Track.get( 1 ).sortEvent(); vsq.write( singer + "\\" + numVsqs + ext ); } } }
//--------------------------------------------- public static bool Edit( VsqFile vsq ) { using ( TransposeEx d = new TransposeEx() ) { if ( d.ShowDialog() != DialogResult.OK ) { return false; } else { int step = (int)d.numUpDown.Value; if (step != 0) transpose( vsq, step ); return true; } } }
private List <IconHandle> collectAllSingerInfo(VsqFile sequence) { return (sequence.Track .Where((track) => track.MetaText != null) .SelectMany((track) => track.MetaText.Events.Events) .Where((vsq_event) => vsq_event.ID.type == VsqIDType.Singer) .Select((vsq_event) => vsq_event.ID.IconHandle) .Distinct() .OrderBy((handle) => handle.Language * 255 + handle.Program) .ToList()); }
/// <summary> /// Do export to MusicXML file. /// </summary> /// <param name="sequence">A sequence to be exported.</param> /// <param name="file_path">A file path.</param> public void write(VsqFile sequence, string file_path) { var score = new scorepartwise(); score.version = "2.0"; score.identification = new identification(); score.identification.encoding = new encoding(); score.identification.encoding.software.Add(this.GetType().FullName); score.partlist = new partlist(); score.partlist.scorepart = new scorepart(); score.partlist.scorepart.id = "P1"; score.partlist.scorepart.partname = new partname(); score.partlist.scorepart.partname.Value = sequence.Track[1].getName(); var partlist = new List <scorepart>(); for (int i = 2; i < sequence.Track.Count; ++i) { var track = sequence.Track[i]; var scorepart = new scorepart(); scorepart.id = "P" + i; scorepart.partname = new partname(); scorepart.partname.Value = track.getName(); partlist.Add(scorepart); } score.partlist.Items = partlist.ToArray(); var quantized_tempo_table = quantizeTempoTable(sequence.TempoTable); score.part = sequence.Track.Skip(1).Select((track) => { var result = createScorePart(track, sequence.TimesigTable, quantized_tempo_table); quantized_tempo_table.Clear(); return(result); }).ToArray(); for (int i = 0; i < score.part.Length; i++) { score.part[i].id = "P" + (i + 1); } var serializer = new System.Xml.Serialization.XmlSerializer(typeof(scorepartwise)); using (var stream = new FileStream(file_path, FileMode.Create, FileAccess.Write)) { var writer = new System.Xml.XmlTextWriter(stream, System.Text.Encoding.UTF8); writer.Formatting = System.Xml.Formatting.Indented; writer.WriteStartDocument(); writer.WriteDocType("score-partwise", "-//Recordare//DTD MusicXML 2.0 Partwise//EN", "http://www.musicxml.org/dtds/partwise.dtd", null); var ns = new System.Xml.Serialization.XmlSerializerNamespaces(); ns.Add(string.Empty, string.Empty); serializer.Serialize(writer, score, ns); } }
private static void transpose( VsqFile vsq, int step ) { int note; VsqTrack track = vsq.Track[AppManager.Selected]; for( Iterator<SelectedEventEntry> itr = AppManager.getSelectedEventIterator(); itr.hasNext(); ) { VsqEvent item = track.findEventFromID(((SelectedEventEntry)itr.next()).original.InternalID); if ( item.ID.type == VsqIDType.Anote ) { note = item.ID.Note + step; if ( note < 0 ) note = 0; if ( 127 < note ) note = 127; item.ID.Note = note; } } }
public static bool Edit(VsqFile vsq) { using (Hamori d = new Hamori()) { if (d.ShowDialog() != DialogResult.OK) { return(false); } else { hamori(vsq, d.cbbBaseCode.SelectedIndex, d.cbbSlide.SelectedIndex); return(true); } } }
public static bool Edit(VsqFile Vsq) { for (int i = 1; i < Vsq.Track.size(); i++) { for (int j = 0; j < Vsq.Track.get(i).getEventCount(); j++) { VsqEvent item = Vsq.Track.get(i).getEvent(j); if (item.ID.type == VsqIDType.Anote) { item.ID.LyricHandle.L0.Phrase = KanaDeRomanization.Attach(item.ID.LyricHandle.L0.Phrase); } } } return(true); }
public static bool Edit(VsqFile vsq) { using (Hamori d = new Hamori()) { if (d.ShowDialog() != DialogResult.OK) { return false; } else { hamori(vsq, d.cbbBaseCode.SelectedIndex, d.cbbSlide.SelectedIndex); return true; } } }
public void Redo(int index) { if (s_current.Child.Count > 0) { ICommand run = s_current.Child[index]; ICommand rev = VsqFile.executeCommand(run); rev.Parent = s_current; for (int i = 0; i < run.Child.Count; i++) { run.Child[i].Parent = rev; rev.Child.Add(run.Child[i]); } s_current.Child[index] = rev; s_current = run; } }
private XmlElement createVoiceTableNode(VsqFile sequence) { var result = doc_.CreateElement("vVoiceTable"); var all_assigned_singers = collectAllSingerInfo(sequence); all_assigned_singers.ForEach((handle) => { var node = doc_.CreateElement("vVoice"); node.AppendChild(createNode("vBS", handle.Language)); node.AppendChild(createNode("vPC", handle.Program)); node.AppendChild(createNode("compID", "AAAAAAAAAAAAAAAA")); node.AppendChild(createNode("vVoiceName", handle.Caption)); node.AppendChild(createVoiceParamNode(handle)); result.AppendChild(node); }); return(result); }
public static bool Edit(VsqFile vsq) { int track = 1; InputBox ib = new InputBox("Input target track index"); ib.setResult(track.ToString()); if (ib.ShowDialog() != DialogResult.OK) { return(false); } if (!int.TryParse(ib.getResult(), out track)) { MessageBox.Show("integer parse error"); return(false); } if (track <= 0 || vsq.Track.Count <= track) { MessageBox.Show("invalid target track"); return(false); } using (SaveFileDialog sfd = new SaveFileDialog()) { if (sfd.ShowDialog() != DialogResult.OK) { return(false); } using (StreamWriter sw = new StreamWriter(sfd.FileName, false, Encoding.GetEncoding(932))) { sw.WriteLine("vlf\t2.0"); sw.WriteLine("vlfpart\tPhrase1\t0\t0"); for (int i = 0; i < vsq.Track[track].getEventCount(); i++) { VsqEvent ve = vsq.Track[track].getEvent(i); if (ve.ID.type == VsqIDType.Anote) { string symbol = ""; for (int j = 0; j < ve.ID.LyricHandle.L0.getPhoneticSymbolList().Count; j++) { symbol += (" " + ve.ID.LyricHandle.L0.getPhoneticSymbolList()[j]); } symbol = symbol.Trim(); sw.WriteLine(ve.ID.LyricHandle.L0.Phrase + "\t" + symbol + "\t0"); } } } } return(true); }
/// <summary> /// エクスポートを行う。 /// </summary> /// <param name="path">出力先のファイルパス</param> /// <param name="sequence">出力するシーケンス</param> public void write(VsqFile sequence, string path) { doc_ = new XmlDocument(); const string xsi_url = "http://www.w3.org/2001/XMLSchema-instance"; var root = doc_.CreateElement("vsq3"); { var xmlns = doc_.CreateAttribute("xmlns"); xmlns.Value = "http://www.yamaha.co.jp/vocaloid/schema/vsq3/"; root.Attributes.Append(xmlns); } { var xsi = doc_.CreateAttribute("xmlns:xsi"); xsi.Value = xsi_url; root.Attributes.Append(xsi); } { var schema = doc_.CreateAttribute("xsi", "schemaLocation", xsi_url); schema.Value = "http://www.yamaha.co.jp/vocaloid/schema/vsq3/ vsq3.xsd"; root.Attributes.Append(schema); } root.AppendChild(createNode("vender", "Yamaha corporation")); root.AppendChild(createNode("version", "3.0.0.11")); root.AppendChild(createVoiceTableNode(sequence)); root.AppendChild(createMixerNode(sequence.Mixer)); root.AppendChild(createMasterTrackNode(sequence, sequence.Track[0])); for (int i = 1; i < sequence.Track.Count; ++i) { root.AppendChild(createTrackNode(sequence.Track[i], i - 1, sequence.getPreMeasureClocks(), sequence.TotalClocks)); } root.AppendChild(doc_.CreateElement("seTrack")); root.AppendChild(doc_.CreateElement("karaokeTrack")); doc_.AppendChild(root); using (var stream = new System.IO.FileStream(path, System.IO.FileMode.Create, System.IO.FileAccess.Write)) { var writer = new XmlTextWriter(stream, Encoding.UTF8); writer.Formatting = Formatting.Indented; doc_.Save(writer); } }
//--------------------------------------------- public static bool Edit(VsqFile vsq) { using (TransposeEx d = new TransposeEx()) { if (d.ShowDialog() != DialogResult.OK) { return(false); } else { int step = (int)d.numUpDown.Value; if (step != 0) { transpose(vsq, step); } return(true); } } }
public void Undo() { ICommand run = s_current; ICommand rev = VsqFile.executeCommand(run); for (int i = 0; i < s_current.Child.Count; i++) { s_current.Child[i].Parent = rev; rev.Child.Add(s_current.Child[i]); } rev.Parent = s_current.Parent; for (int i = 0; i < s_current.Parent.Child.Count; i++) { if (Object.ReferenceEquals(s_current, s_current.Parent.Child[i])) { s_current.Parent.Child[i] = rev; break; } } s_current = s_current.Parent; }
public void readFromVsqxMultiTrack() { VsqFile vsq = VsqxReader.readFromVsqx("./fixture/track2.vsqx"); Assert.AreEqual(3, vsq.Track.size()); // 1トラック目 var track = vsq.Track.get(1); Assert.AreEqual(3, track.getEventCount()); Assert.AreEqual(VsqIDType.Singer, track.getEvent(1).ID.type); Assert.AreEqual("VY1V3", track.getEvent(1).ID.IconHandle.IDS); Assert.AreEqual(VsqIDType.Anote, track.getEvent(2).ID.type); Assert.AreEqual("ど", track.getEvent(2).ID.LyricHandle.L0.Phrase); // 2トラック目 track = vsq.Track.get(2); Assert.AreEqual(3, track.getEventCount()); Assert.AreEqual(VsqIDType.Singer, track.getEvent(1).ID.type); Assert.AreEqual("VY1V3", track.getEvent(1).ID.IconHandle.IDS); Assert.AreEqual(VsqIDType.Anote, track.getEvent(2).ID.type); Assert.AreEqual("み", track.getEvent(2).ID.LyricHandle.L0.Phrase); }
private static void transpose(VsqFile vsq, int step) { int note; VsqTrack track = vsq.Track[AppManager.Selected]; for (Iterator <SelectedEventEntry> itr = AppManager.getSelectedEventIterator(); itr.hasNext();) { VsqEvent item = track.findEventFromID(((SelectedEventEntry)itr.next()).original.InternalID); if (item.ID.type == VsqIDType.Anote) { note = item.ID.Note + step; if (note < 0) { note = 0; } if (127 < note) { note = 127; } item.ID.Note = note; } } }
private XmlElement createMasterTrackNode(VsqFile sequence, VsqTrack master_track) { var result = doc_.CreateElement("masterTrack"); result.AppendChild(createNode("seqName", master_track.getName())); result.AppendChild(createNode("comment", "")); result.AppendChild(createNode("resolution", sequence.getTickPerQuarter())); result.AppendChild(createNode("preMeasure", sequence.getPreMeasure())); sequence.TimesigTable.ForEach((time_sig) => { var node = doc_.CreateElement("timeSig"); node.AppendChild(createNode("posMes", time_sig.BarCount)); node.AppendChild(createNode("nume", time_sig.Numerator)); node.AppendChild(createNode("denomi", time_sig.Denominator)); result.AppendChild(node); }); sequence.TempoTable.ForEach((tempo) => { var node = doc_.CreateElement("tempo"); node.AppendChild(createNode("posTick", tempo.Clock)); node.AppendChild(createNode("bpm", (int)(60e6 / tempo.Tempo * 100))); result.AppendChild(node); }); return(result); }
public static bool Edit( VsqFile vsq ){ int track = 1; InputBox ib = new InputBox( "Input target track index" ); ib.setResult( track.ToString() ); if ( ib.ShowDialog() != DialogResult.OK ) { return false; } if ( !int.TryParse( ib.getResult(), out track ) ) { MessageBox.Show( "integer parse error" ); return false; } if ( track <= 0 || vsq.Track.size() <= track ) { MessageBox.Show( "invalid target track" ); return false; } using ( SaveFileDialog sfd = new SaveFileDialog() ) { if ( sfd.ShowDialog() != DialogResult.OK ) { return false; } using ( StreamWriter sw = new StreamWriter( sfd.FileName, false, Encoding.GetEncoding( 932 ) ) ) { sw.WriteLine( "vlf\t2.0" ); sw.WriteLine( "vlfpart\tPhrase1\t0\t0" ); for ( int i = 0; i < vsq.Track.get( track ).getEventCount(); i++ ) { VsqEvent ve = vsq.Track.get( track ).getEvent( i ); if ( ve.ID.type == VsqIDType.Anote ) { string symbol = ""; for ( int j = 0; j < ve.ID.LyricHandle.L0.getPhoneticSymbolList().Count; j++ ) { symbol += (" " + ve.ID.LyricHandle.L0.getPhoneticSymbolList()[j]); } symbol = symbol.Trim(); sw.WriteLine( ve.ID.LyricHandle.L0.Phrase + "\t" + symbol + "\t0" ); } } } } return true; }
private static void hamori(VsqFile vsq, int basecode, int opt) { int[][] steps = new int[][] { // C Major C D E F G A B // 7度上 new int[] { 11,11,10,10,10,11,11,10,10,10,10,10 }, // 6度上 new int[] { 9, 9, 9, 9, 8, 9, 9, 9, 9, 8, 8, 9 }, // 5度上 new int[] { 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6 }, // 4度上 new int[] { 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5 }, // 3度上 new int[] { 4, 4, 3, 3, 3, 4, 4, 4, 4, 3, 3, 3 }, // 2度上 new int[] { 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 } }; int[] step; if (opt == 6) return; if (opt < 6) step = steps[opt]; else { step = steps[opt - 7]; for (int i = 0; i < 12; i++) step[i] -= 12; } int note, tmp; VsqTrack track = vsq.Track[AppManager.Selected]; for( Iterator<SelectedEventEntry> itr = AppManager.getSelectedEventIterator(); itr.hasNext(); ) { VsqEvent item = track.findEventFromID(((SelectedEventEntry)itr.next()).original.InternalID); if (item.ID.type == VsqIDType.Anote ) { tmp = (item.ID.Note + 12 - basecode) % 12; note = item.ID.Note + step[tmp]; if (note < 0) note = 0; if (127 < note) note = 127; item.ID.Note = note; } } }
public static bool Edit(VsqFile vsq) { return(edit(vsq)); }
public static bool Edit( VsqFile vsq ) { return edit( vsq ); }
public static bool Edit(VsqFile vsq) { using (RenderAsUtau dlg = new RenderAsUtau()) { if (dlg.ShowDialog() == DialogResult.OK) { Singer = dlg.txtSinger.Text; Resampler = dlg.txtResampler.Text; WavTool = dlg.txtWavtool.Text; string script = Path.Combine(Application.StartupPath, Path.Combine("script", "Render As UTAU.cs")); //Script.ScriptPath; string temp_dir = Path.Combine(Path.GetDirectoryName(script), Path.GetFileNameWithoutExtension(script)); #if DEBUG if (!Directory.Exists(temp_dir)) { Directory.CreateDirectory(temp_dir); } StreamWriter sw = new StreamWriter(Path.Combine(temp_dir, "log.txt")); #endif // 原音設定を読み込み Dictionary <string, OtoArgs> config = new Dictionary <string, OtoArgs>(); string singer_name = Path.GetFileName(Singer); string config_file = Path.Combine(Singer, "oto.ini"); #if DEBUG sw.WriteLine("Singer=" + Singer); sw.WriteLine("singer_name=" + singer_name); sw.WriteLine("config_file=" + config_file); #endif if (File.Exists(config_file)) { using (cp932reader sr = new cp932reader(config_file)) { string line; while (sr.Peek() >= 0) { try { line = sr.ReadLine(); String[] spl = line.Split('='); String file_name = spl[0]; // あ.wav String a2 = spl[1]; // ,0,36,64,0,0 String a1 = Path.GetFileNameWithoutExtension(file_name); spl = a2.Split(','); OtoArgs oa = new OtoArgs(); oa.Alias = spl[0]; oa.msOffset = int.Parse(spl[1]); oa.msConsonant = int.Parse(spl[2]); oa.msBlank = int.Parse(spl[3]); oa.msPreUtterance = int.Parse(spl[4]); oa.msOverwrap = int.Parse(spl[5]); config.Add(a1, oa); } catch { } } } } int track = AppManager.getSelected(); List <Phon> phons = new List <Phon>(); if (!Directory.Exists(temp_dir)) { Directory.CreateDirectory(temp_dir); } int count = -1; double sec_end = 0; double sec_end_old = 0; for (Iterator <VsqEvent> itr = vsq.Track.get(track).getNoteEventIterator(); itr.hasNext();) { VsqEvent item = itr.next(); count++; double sec_start = vsq.getSecFromClock(item.Clock); sec_end_old = sec_end; sec_end = vsq.getSecFromClock(item.Clock + item.ID.Length); float t_temp = (float)(item.ID.Length / (sec_end - sec_start) / 8.0); if ((count == 0 && sec_start > 0.0) || (sec_start > sec_end_old)) { double sec_start2 = sec_end_old; double sec_end2 = sec_start; float t_temp2 = (float)(item.Clock / (sec_end2 - sec_start2) / 8.0); phons.Add(new Phon("R", Path.Combine(Singer, "R.wav"), item.Clock, t_temp2, true)); count++; } string lyric = item.ID.LyricHandle.L0.Phrase; string note = NoteStringFromNoteNumber(item.ID.Note); #if DEBUG sw.WriteLine("note=" + note); #endif string millisec = ((int)((sec_end - sec_start) * 1000) + 50).ToString(); //4_あ_C#4_550.wav string filename = Path.Combine(temp_dir, count + "_" + item.ID.Note + "_" + millisec + ".wav"); #if DEBUG sw.WriteLine("filename=" + filename); sw.WriteLine(); #endif if (File.Exists(filename)) { PortUtil.deleteFile(filename); } phons.Add(new Phon(lyric, filename, item.ID.Length, t_temp, false)); OtoArgs oa = new OtoArgs(); if (config.ContainsKey(lyric)) { oa = config[lyric]; } int velocity = 100; int moduration = 100; string flags = "L"; int time_percent = 100; // C4 100 L 0 550 0 0 100 100 string arg = "\"" + Path.Combine(Singer, lyric + ".wav") + "\" \"" + filename + "\" \"" + note + "\" " + time_percent + " " + flags + " " + oa.msOffset + " " + millisec + " " + oa.msConsonant + " " + oa.msBlank + " " + velocity + " " + moduration; using (System.Diagnostics.Process p = new System.Diagnostics.Process()) { p.StartInfo.FileName = (InvokeWithWine ? "wine \"" : "\"") + Resampler + "\""; p.StartInfo.Arguments = arg; p.StartInfo.WorkingDirectory = temp_dir; p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; p.Start(); p.WaitForExit(); } } #if DEBUG sw.Close(); #endif string filebase = "temp.wav"; string file = Path.Combine(temp_dir, filebase); if (File.Exists(file)) { PortUtil.deleteFile(file); } string file_whd = Path.Combine(temp_dir, filebase + ".whd"); if (File.Exists(file_whd)) { PortUtil.deleteFile(file_whd); } string file_dat = Path.Combine(temp_dir, filebase + ".dat"); if (File.Exists(file_dat)) { PortUtil.deleteFile(file_dat); } // wavtoolを呼び出す for (int i = 0; i < phons.Count; i++) { OtoArgs oa = new OtoArgs(); if (config.ContainsKey(phons[i].Lyric)) { oa = config[phons[i].Lyric]; } // 次の音符の先行発声とオーバーラップを取得 OtoArgs oa_next = new OtoArgs(); if (i + 1 < phons.Count) { if (config.ContainsKey(phons[i + 1].Lyric)) { oa_next = config[phons[i + 1].Lyric]; } } int mten = oa.msPreUtterance + oa_next.msOverwrap - oa_next.msPreUtterance; string arg = filebase + " \"" + phons[i].FileName + "\" 0 " + phons[i].ClockLength + "@" + string.Format("{0:f2}", phons[i].Tempo) + mten.ToString("+#;-#;0"); if (phons[i].ModeR) { arg += " 0 0"; } else { arg += " 0 5 35 0 100 100 100 " + oa.msOverwrap; // エンベロープ } using (System.Diagnostics.Process p = new System.Diagnostics.Process()) { p.StartInfo.FileName = (InvokeWithWine ? "wine \"" : "\"") + WavTool + "\""; p.StartInfo.Arguments = arg; p.StartInfo.WorkingDirectory = temp_dir; p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; p.Start(); p.WaitForExit(); } } // 波形とヘッダを結合 using (FileStream fs = new FileStream(file, FileMode.Create)) { string[] files = new string[] { file_whd, file_dat }; int buflen = 512; byte[] buff = new byte[buflen]; for (int i = 0; i < files.Length; i++) { using (FileStream fs2 = new FileStream(files[i], FileMode.Open)) { int len = fs2.Read(buff, 0, buflen); while (len > 0) { fs.Write(buff, 0, len); len = fs2.Read(buff, 0, buflen); } } } } // 後片付け foreach (Phon ph in phons) { if (!ph.ModeR) { if (File.Exists(ph.FileName)) { PortUtil.deleteFile(ph.FileName); } } } if (File.Exists(file_whd)) { PortUtil.deleteFile(file_whd); } if (File.Exists(file_dat)) { PortUtil.deleteFile(file_dat); } if (saveFileDialog.ShowDialog() == DialogResult.OK) { if (File.Exists(saveFileDialog.FileName)) { PortUtil.deleteFile(saveFileDialog.FileName); } LastWave = saveFileDialog.FileName; PortUtil.moveFile(file, saveFileDialog.FileName); } else { PortUtil.deleteFile(file); } return(true); } else { return(false); } } }
public static void Main( string[] args ){ int count = 0; List<string> list = new List<string>(); while( true ){ count++; string dirname = count + ""; if( !Directory.Exists( dirname ) ){ break; } int i = 0; while( true ){ i++; string fname = Path.Combine( dirname, i + ".vsq" ); if( !File.Exists( fname ) ){ break; } Console.Write( "\r" + fname + " " ); VsqFile vsq = new VsqFile( fname, "Shift_JIS" ); VsqTrack vsq_track = vsq.Track.get( 1 ); string cache_phrase = ""; string cache_symbol = ""; bool all_symbol_default = true; for( Iterator<VsqEvent> itr = vsq_track.getNoteEventIterator(); itr.hasNext(); ){ VsqEvent item = itr.next(); string phrase = item.ID.LyricHandle.L0.Phrase; string symbol = item.ID.LyricHandle.L0.getPhoneticSymbol(); if( symbol != "u:" ){ all_symbol_default = false; } if( cache_phrase == "" ){ cache_phrase = phrase; cache_symbol = symbol; }else{ cache_phrase += "\t" + phrase; cache_symbol += "\t" + symbol; } if( !phrase.EndsWith( "-" ) ){ if( cache_phrase != "a" && cache_symbol != "u:" && !all_symbol_default ){ list.Add( cache_phrase + "\t\t" + cache_symbol ); } all_symbol_default = true; cache_phrase = ""; cache_symbol = ""; } } } } list.Sort(); string last = ""; int c = list.Count; using( StreamWriter sw = new StreamWriter( "extracted.txt" ) ){ for( int i = 0; i < c; i++ ){ string s = list[i]; if( s != last ){ sw.WriteLine( s ); last = s; } } } }
public static ScriptReturnStatus Edit(VsqFileEx vsq) { // 選択状態のアイテムがなければ戻る if (AppManager.itemSelection.getEventCount() <= 0) { return(ScriptReturnStatus.NOT_EDITED); } // 現在のトラック int selected = AppManager.getSelected(); VsqTrack vsq_track = vsq.Track.get(selected); vsq_track.sortEvent(); // プラグイン情報の定義ファイル(plugin.txt)があるかどうかチェック string pluginTxtPath = s_plugin_txt_path; if (pluginTxtPath == "") { AppManager.showMessageBox("pluginTxtPath=" + pluginTxtPath); return(ScriptReturnStatus.ERROR); } if (!System.IO.File.Exists(pluginTxtPath)) { AppManager.showMessageBox("'" + pluginTxtPath + "' does not exists"); return(ScriptReturnStatus.ERROR); } // plugin.txtがあれば,プラグインの実行ファイルのパスを取得する System.Text.Encoding shift_jis = System.Text.Encoding.GetEncoding("Shift_JIS"); string name = ""; string exe_path = ""; using (StreamReader sr = new StreamReader(pluginTxtPath, shift_jis)) { string line = ""; while ((line = sr.ReadLine()) != null) { string[] spl = line.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries); if (line.StartsWith("name=")) { name = spl[1]; } else if (line.StartsWith("execute=")) { exe_path = Path.Combine(Path.GetDirectoryName(pluginTxtPath), spl[1]); } } } if (exe_path == "") { return(ScriptReturnStatus.ERROR); } if (!System.IO.File.Exists(exe_path)) { AppManager.showMessageBox("'" + exe_path + "' does not exists"); return(ScriptReturnStatus.ERROR); } // 選択状態のアイテムの最初と最後がどこか調べる // clock_start, clock_endは,最終的にはPREV, NEXTを含んだ範囲を表すことになる // sel_start, sel_endはPREV, NEXTを含まない選択範囲を表す int id_start = -1; int clock_start = int.MaxValue; int id_end = -1; int clock_end = int.MinValue; int sel_start = 0; int sel_end = 0; for (Iterator <SelectedEventEntry> itr = AppManager.itemSelection.getEventIterator(); itr.hasNext();) { SelectedEventEntry item = itr.next(); if (item.original.ID.type != VsqIDType.Anote) { continue; } int clock = item.original.Clock; if (clock < clock_start) { id_start = item.original.InternalID; clock_start = clock; sel_start = clock; } clock += item.original.ID.getLength(); if (clock_end < clock) { id_end = item.original.InternalID; clock_end = clock; sel_end = clock; } } // 選択範囲の前後の音符を探す VsqEvent ve_prev = null; VsqEvent ve_next = null; VsqEvent l = null; for (Iterator <VsqEvent> itr = vsq_track.getNoteEventIterator(); itr.hasNext();) { VsqEvent item = itr.next(); if (item.InternalID == id_start) { if (l != null) { ve_prev = l; } } if (l != null) { if (l.InternalID == id_end) { ve_next = item; } } l = item; if (ve_prev != null && ve_next != null) { break; } } int next_rest_clock = -1; bool prev_is_rest = false; if (ve_prev != null) { // 直前の音符がある場合 if (ve_prev.Clock + ve_prev.ID.getLength() == clock_start) { // 接続している clock_start = ve_prev.Clock; } else { // 接続していない int new_clock_start = ve_prev.Clock + ve_prev.ID.getLength(); clock_start = new_clock_start; } } else { // 無い場合 if (vsq.getPreMeasureClocks() < clock_start) { prev_is_rest = true; } int new_clock_start = vsq.getPreMeasureClocks(); clock_start = new_clock_start; } if (ve_next != null) { // 直後の音符がある場合 if (ve_next.Clock == clock_end) { // 接続している clock_end = ve_next.Clock + ve_next.ID.getLength(); } else { // 接続していない next_rest_clock = clock_end; clock_end = ve_next.Clock; } } // 作業用のVSQに,選択範囲のアイテムを格納 VsqFileEx v = (VsqFileEx)vsq.clone();// new VsqFile( "Miku", 1, 4, 4, 500000 ); // 選択トラックだけ残して他を削る for (int i = 1; i < selected; i++) { v.Track.removeElementAt(1); } for (int i = selected + 1; i < v.Track.size(); i++) { v.Track.removeElementAt(selected + 1); } // 選択トラックの音符を全消去する VsqTrack v_track = v.Track.get(1); v_track.MetaText.getEventList().clear(); for (Iterator <VsqEvent> itr = vsq_track.getNoteEventIterator(); itr.hasNext();) { VsqEvent item = itr.next(); if (clock_start <= item.Clock && item.Clock + item.ID.getLength() <= clock_end) { v_track.addEvent((VsqEvent)item.clone(), item.InternalID); } } // 最後のRを手動で追加.これは自動化できない if (next_rest_clock != -1) { VsqEvent item = (VsqEvent)ve_next.clone(); item.ID.LyricHandle.L0.Phrase = "R"; item.Clock = next_rest_clock; item.ID.setLength(clock_end - next_rest_clock); v_track.addEvent(item); } // 0~選択範囲の開始位置までを削除する v.removePart(0, clock_start); // vsq -> ustに変換 // キーがustのIndex, 値がInternalID TreeMap <int, int> map = new TreeMap <int, int>(); UstFile u = new UstFile(v, 1, map); u.write(Path.Combine(PortUtil.getApplicationStartupPath(), "u.ust")); // PREV, NEXTのIndex値を設定する if (ve_prev != null || prev_is_rest) { u.getTrack(0).getEvent(0).Index = UstFile.PREV_INDEX; } if (ve_next != null) { u.getTrack(0).getEvent(u.getTrack(0).getEventCount() - 1).Index = UstFile.NEXT_INDEX; } // ustファイルに出力 UstFileWriteOptions option = new UstFileWriteOptions(); option.settingCacheDir = false; option.settingOutFile = false; option.settingProjectName = false; option.settingTempo = true; option.settingTool1 = true; option.settingTool2 = true; option.settingTracks = false; option.settingVoiceDir = true; option.trackEnd = false; string temp = Path.GetTempFileName(); u.write(temp, option); StringBuilder before = new StringBuilder(); using (StreamReader sr = new StreamReader(temp, System.Text.Encoding.GetEncoding("Shift_JIS"))) { string line = ""; while ((line = sr.ReadLine()) != null) { before.AppendLine(line); } } String md5_before = PortUtil.getMD5FromString(before.ToString()); // プラグインの実行ファイルを起動 Utau_Plugin_Invoker dialog = new Utau_Plugin_Invoker(exe_path, temp); dialog.ShowDialog(); StringBuilder after = new StringBuilder(); using (StreamReader sr = new StreamReader(temp, System.Text.Encoding.GetEncoding("Shift_JIS"))) { string line = ""; while ((line = sr.ReadLine()) != null) { after.AppendLine(line); } } String md5_after = PortUtil.getMD5FromString(after.ToString()); if (md5_before == md5_after) { // 編集されなかったようだ return(ScriptReturnStatus.NOT_EDITED); } // プラグインの実行結果をustオブジェクトにロード UstFile r = new UstFile(temp); if (r.getTrackCount() < 1) { return(ScriptReturnStatus.ERROR); } // 変更のなかったものについてはプラグインは記録してこないので, // 最初の値を代入するようにする UstTrack utrack_src = u.getTrack(0); UstTrack utrack_dst = r.getTrack(0); for (int i = 0; i < utrack_dst.getEventCount(); i++) { UstEvent ue_dst = utrack_dst.getEvent(i); int index = ue_dst.Index; UstEvent ue_src = utrack_src.findEventFromIndex(index); if (ue_src == null) { continue; } if (!ue_dst.isEnvelopeSpecified() && ue_src.isEnvelopeSpecified()) { ue_dst.setEnvelope(ue_src.getEnvelope()); } if (!ue_dst.isIntensitySpecified() && ue_src.isIntensitySpecified()) { ue_dst.setIntensity(ue_src.getIntensity()); } if (!ue_dst.isLengthSpecified() && ue_src.isLengthSpecified()) { ue_dst.setLength(ue_src.getLength()); } if (!ue_dst.isLyricSpecified() && ue_src.isLyricSpecified()) { ue_dst.setLyric(ue_src.getLyric()); } if (!ue_dst.isModurationSpecified() && ue_src.isModurationSpecified()) { ue_dst.setModuration(ue_src.getModuration()); } if (!ue_dst.isNoteSpecified() && ue_src.isNoteSpecified()) { ue_dst.setNote(ue_src.getNote()); } if (!ue_dst.isPBTypeSpecified() && ue_src.isPBTypeSpecified()) { ue_dst.setPBType(ue_src.getPBType()); } if (!ue_dst.isPitchesSpecified() && ue_src.isPitchesSpecified()) { ue_dst.setPitches(ue_src.getPitches()); } if (!ue_dst.isPortamentoSpecified() && ue_src.isPortamentoSpecified()) { ue_dst.setPortamento(ue_src.getPortamento()); } if (!ue_dst.isPreUtteranceSpecified() && ue_src.isPreUtteranceSpecified()) { ue_dst.setPreUtterance(ue_src.getPreUtterance()); } if (!ue_dst.isStartPointSpecified() && ue_src.isStartPointSpecified()) { ue_dst.setStartPoint(ue_src.getStartPoint()); } if (!ue_dst.isTempoSpecified() && ue_src.isTempoSpecified()) { ue_dst.setTempo(ue_src.getTempo()); } if (!ue_dst.isVibratoSpecified() && ue_src.isVibratoSpecified()) { ue_dst.setVibrato(ue_src.getVibrato()); } if (!ue_dst.isVoiceOverlapSpecified() && ue_src.isVoiceOverlapSpecified()) { ue_dst.setVoiceOverlap(ue_src.getVoiceOverlap()); } } // PREVとNEXT含めて,clock_startからclock_endまでプラグインに渡したけれど, // それが伸びて帰ってきたか縮んで帰ってきたか. int ret_length = 0; UstTrack r_track = r.getTrack(0); int size = r_track.getEventCount(); for (int i = 0; i < size; i++) { UstEvent ue = r_track.getEvent(i); // 戻りのustには,変更があったものしか記録されていない int ue_length = ue.getLength(); if (!ue.isLengthSpecified() && map.ContainsKey(ue.Index)) { int internal_id = map[ue.Index]; VsqEvent found_item = vsq_track.findEventFromID(internal_id); if (found_item != null) { ue_length = found_item.ID.getLength(); } } // PREV, ENDの場合は長さに加えない if (ue.Index != UstFile.NEXT_INDEX && ue.Index != UstFile.PREV_INDEX) { ret_length += ue_length; } } // 伸び縮みがあった場合 // 伸ばしたり縮めたりするよ int delta = ret_length - (sel_end - sel_start); if (delta > 0) { // のびた vsq.insertBlank(selected, sel_end, delta); } else if (delta < 0) { // 縮んだ vsq.removePart(selected, sel_end + delta, sel_end); } // r_trackの内容をvsq_trackに転写 size = r_track.getEventCount(); int c = clock_start; for (int i = 0; i < size; i++) { UstEvent ue = r_track.getEvent(i); if (ue.Index == UstFile.NEXT_INDEX || ue.Index == UstFile.PREV_INDEX) { // PREVとNEXTは単に無視する continue; } int ue_length = ue.getLength(); if (map.containsKey(ue.Index)) { // 既存の音符の編集 VsqEvent target = vsq_track.findEventFromID(map[ue.Index]); if (target == null) { // そんなばかな・・・ continue; } if (!ue.isLengthSpecified()) { ue_length = target.ID.getLength(); } if (target.UstEvent == null) { target.UstEvent = (UstEvent)ue.clone(); } // utau固有のパラメータを転写 // pitchは後でやるので無視していい // テンポもあとでやるので無視していい if (ue.isEnvelopeSpecified()) { target.UstEvent.setEnvelope(ue.getEnvelope()); } if (ue.isModurationSpecified()) { target.UstEvent.setModuration(ue.getModuration()); } if (ue.isPBTypeSpecified()) { target.UstEvent.setPBType(ue.getPBType()); } if (ue.isPortamentoSpecified()) { target.UstEvent.setPortamento(ue.getPortamento()); } if (ue.isPreUtteranceSpecified()) { target.UstEvent.setPreUtterance(ue.getPreUtterance()); } if (ue.isStartPointSpecified()) { target.UstEvent.setStartPoint(ue.getStartPoint()); } if (ue.isVibratoSpecified()) { target.UstEvent.setVibrato(ue.getVibrato()); } if (ue.isVoiceOverlapSpecified()) { target.UstEvent.setVoiceOverlap(ue.getVoiceOverlap()); } // vocaloid, utauで同じ意味のパラメータを転写 if (ue.isIntensitySpecified()) { target.UstEvent.setIntensity(ue.getIntensity()); target.ID.Dynamics = ue.getIntensity(); } if (ue.isLengthSpecified()) { target.UstEvent.setLength(ue.getLength()); target.ID.setLength(ue.getLength()); } if (ue.isLyricSpecified()) { target.UstEvent.setLyric(ue.getLyric()); target.ID.LyricHandle.L0.Phrase = ue.getLyric(); } if (ue.isNoteSpecified()) { target.UstEvent.setNote(ue.getNote()); target.ID.Note = ue.getNote(); } } else { // マップに入っていないので,新しい音符の追加だと思う if (ue.getLyric() == "R") { // 休符.なにもしない } else { VsqEvent newe = new VsqEvent(); newe.Clock = c; newe.UstEvent = (UstEvent)ue.clone(); newe.ID = new VsqID(); AppManager.editorConfig.applyDefaultSingerStyle(newe.ID); if (ue.isIntensitySpecified()) { newe.ID.Dynamics = ue.getIntensity(); } newe.ID.LyricHandle = new LyricHandle("あ", "a"); if (ue.isLyricSpecified()) { newe.ID.LyricHandle.L0.Phrase = ue.getLyric(); } newe.ID.Note = ue.getNote(); newe.ID.setLength(ue.getLength()); newe.ID.type = VsqIDType.Anote; // internal id はaddEventメソッドで自動で割り振られる vsq_track.addEvent(newe); } } // テンポの追加がないかチェック if (ue.isTempoSpecified()) { insertTempoInto(vsq, c, ue.getTempo()); } c += ue_length; } // ピッチを転写 // pitのデータがほしいので,PREV, NEXTを削除して,VsqFileにコンバートする UstFile uf = (UstFile)r.clone(); // prev, nextを削除 UstTrack uf_track = uf.getTrack(0); for (int i = 0; i < uf_track.getEventCount();) { UstEvent ue = uf_track.getEvent(i); if (ue.Index == UstFile.NEXT_INDEX || ue.Index == UstFile.PREV_INDEX) { uf_track.removeEventAt(i); } else { i++; } } uf.updateTempoInfo(); // VsqFileにコンバート VsqFile uf_vsq = new VsqFile(uf); // uf_vsqの最初のトラックの0からret_lengthクロックまでが, // vsq_trackのsel_startからsel_start+ret_lengthクロックまでに対応する. // まずPBSをコピーする CurveType[] type = new CurveType[] { CurveType.PBS, CurveType.PIT }; foreach (CurveType ct in type) { // コピー元を取得 VsqBPList src = uf_vsq.Track[1].getCurve(ct.getName()); if (src != null) { // コピー先を取得 VsqBPList dst = vsq_track.getCurve(ct.getName()); if (dst == null) { // コピー先がnullだった場合は作成 dst = new VsqBPList(ct.getName(), ct.getDefault(), ct.getMinimum(), ct.getMaximum()); vsq_track.setCurve(ct.getName(), dst); } // あとで復元するので,最終位置での値を保存しておく int value_at_end = dst.getValue(sel_start + ret_length); // 復元するかどうか.最終位置にそもそもデータ点があれば復帰の必要がないので. bool do_revert = (dst.findIndexFromClock(sel_start + ret_length) < 0); // [sel_start, sel_start + ret_length)の範囲の値を削除しておく size = dst.size(); for (int i = size - 1; i >= 0; i--) { int cl = dst.getKeyClock(i); if (sel_start <= cl && cl < sel_start + ret_length) { dst.removeElementAt(i); } } // コピーを実行 size = src.size(); for (int i = 0; i < size; i++) { int cl = src.getKeyClock(i); if (ret_length <= cl) { break; } int value = src.getElementA(i); dst.add(cl + sel_start, value); } // コピー後,最終位置での値が元と異なる場合,元に戻すようにする if (do_revert && dst.getValue(sel_start + ret_length) != value_at_end) { dst.add(sel_start + ret_length, value_at_end); } } } return(ScriptReturnStatus.EDITED); }
public static bool Edit( VsqFile vsq ) { using ( RenderAsUtau dlg = new RenderAsUtau() ) { if ( dlg.ShowDialog() == DialogResult.OK ) { Singer = dlg.txtSinger.Text; Resampler = dlg.txtResampler.Text; WavTool = dlg.txtWavtool.Text; string script = Path.Combine( Application.StartupPath, Path.Combine( "script", "Render As UTAU.cs" ) );//Script.ScriptPath; string temp_dir = Path.Combine( Path.GetDirectoryName( script ), Path.GetFileNameWithoutExtension( script ) ); #if DEBUG if ( !Directory.Exists( temp_dir ) ) { Directory.CreateDirectory( temp_dir ); } StreamWriter sw = new StreamWriter( Path.Combine( temp_dir, "log.txt" ) ); #endif // 原音設定を読み込み Dictionary<string, OtoArgs> config = new Dictionary<string, OtoArgs>(); string singer_name = Path.GetFileName( Singer ); string config_file = Path.Combine( Singer, "oto.ini" ); #if DEBUG sw.WriteLine( "Singer=" + Singer ); sw.WriteLine( "singer_name=" + singer_name ); sw.WriteLine( "config_file=" + config_file ); #endif if ( File.Exists( config_file ) ) { using ( cp932reader sr = new cp932reader( config_file ) ) { string line; while ( sr.Peek() >= 0 ) { try { line = sr.ReadLine(); String[] spl = line.Split( '=' ); String file_name = spl[0]; // あ.wav String a2 = spl[1]; // ,0,36,64,0,0 String a1 = Path.GetFileNameWithoutExtension( file_name ); spl = a2.Split( ',' ); OtoArgs oa = new OtoArgs(); oa.Alias = spl[0]; oa.msOffset = int.Parse( spl[1] ); oa.msConsonant = int.Parse( spl[2] ); oa.msBlank = int.Parse( spl[3] ); oa.msPreUtterance = int.Parse( spl[4] ); oa.msOverwrap = int.Parse( spl[5] ); config.Add( a1, oa ); } catch { } } } } int track = AppManager.getSelected(); List<Phon> phons = new List<Phon>(); if ( !Directory.Exists( temp_dir ) ) { Directory.CreateDirectory( temp_dir ); } int count = -1; double sec_end = 0; double sec_end_old = 0; for ( Iterator<VsqEvent> itr = vsq.Track.get( track ).getNoteEventIterator(); itr.hasNext(); ) { VsqEvent item = itr.next(); count++; double sec_start = vsq.getSecFromClock( item.Clock ); sec_end_old = sec_end; sec_end = vsq.getSecFromClock( item.Clock + item.ID.Length ); float t_temp = (float)(item.ID.Length / (sec_end - sec_start) / 8.0); if ( (count == 0 && sec_start > 0.0) || (sec_start > sec_end_old) ) { double sec_start2 = sec_end_old; double sec_end2 = sec_start; float t_temp2 = (float)(item.Clock / (sec_end2 - sec_start2) / 8.0); phons.Add( new Phon( "R", Path.Combine( Singer, "R.wav" ), item.Clock, t_temp2, true ) ); count++; } string lyric = item.ID.LyricHandle.L0.Phrase; string note = NoteStringFromNoteNumber( item.ID.Note ); #if DEBUG sw.WriteLine( "note=" + note ); #endif string millisec = ((int)((sec_end - sec_start) * 1000) + 50).ToString(); //4_あ_C#4_550.wav string filename = Path.Combine( temp_dir, count + "_" + item.ID.Note + "_" + millisec + ".wav" ); #if DEBUG sw.WriteLine( "filename=" + filename ); sw.WriteLine(); #endif if ( File.Exists( filename ) ) { PortUtil.deleteFile( filename ); } phons.Add( new Phon( lyric, filename, item.ID.Length, t_temp, false ) ); OtoArgs oa = new OtoArgs(); if ( config.ContainsKey( lyric ) ) { oa = config[lyric]; } int velocity = 100; int moduration = 100; string flags = "L"; int time_percent = 100; // C4 100 L 0 550 0 0 100 100 string arg = "\"" + Path.Combine( Singer, lyric + ".wav" ) + "\" \"" + filename + "\" \"" + note + "\" " + time_percent + " " + flags + " " + oa.msOffset + " " + millisec + " " + oa.msConsonant + " " + oa.msBlank + " " + velocity + " " + moduration; using ( System.Diagnostics.Process p = new System.Diagnostics.Process() ) { p.StartInfo.FileName = (InvokeWithWine ? "wine \"" : "\"") + Resampler + "\""; p.StartInfo.Arguments = arg; p.StartInfo.WorkingDirectory = temp_dir; p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; p.Start(); p.WaitForExit(); } } #if DEBUG sw.Close(); #endif string filebase = "temp.wav"; string file = Path.Combine( temp_dir, filebase ); if ( File.Exists( file ) ) { PortUtil.deleteFile( file ); } string file_whd = Path.Combine( temp_dir, filebase + ".whd" ); if ( File.Exists( file_whd ) ) { PortUtil.deleteFile( file_whd ); } string file_dat = Path.Combine( temp_dir, filebase + ".dat" ); if ( File.Exists( file_dat ) ) { PortUtil.deleteFile( file_dat ); } // wavtoolを呼び出す for ( int i = 0; i < phons.Count; i++ ) { OtoArgs oa = new OtoArgs(); if ( config.ContainsKey( phons[i].Lyric ) ) { oa = config[phons[i].Lyric]; } // 次の音符の先行発声とオーバーラップを取得 OtoArgs oa_next = new OtoArgs(); if ( i + 1 < phons.Count ) { if ( config.ContainsKey( phons[i + 1].Lyric ) ) { oa_next = config[phons[i + 1].Lyric]; } } int mten = oa.msPreUtterance + oa_next.msOverwrap - oa_next.msPreUtterance; string arg = filebase + " \"" + phons[i].FileName + "\" 0 " + phons[i].ClockLength + "@" + string.Format( "{0:f2}", phons[i].Tempo ) + mten.ToString( "+#;-#;0" ); if ( phons[i].ModeR ) { arg += " 0 0"; } else { arg += " 0 5 35 0 100 100 100 " + oa.msOverwrap; // エンベロープ } using ( System.Diagnostics.Process p = new System.Diagnostics.Process() ) { p.StartInfo.FileName = (InvokeWithWine ? "wine \"" : "\"") + WavTool + "\""; p.StartInfo.Arguments = arg; p.StartInfo.WorkingDirectory = temp_dir; p.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; p.Start(); p.WaitForExit(); } } // 波形とヘッダを結合 using ( FileStream fs = new FileStream( file, FileMode.Create ) ) { string[] files = new string[] { file_whd, file_dat }; int buflen = 512; byte[] buff = new byte[buflen]; for ( int i = 0; i < files.Length; i++ ) { using ( FileStream fs2 = new FileStream( files[i], FileMode.Open ) ) { int len = fs2.Read( buff, 0, buflen ); while ( len > 0 ) { fs.Write( buff, 0, len ); len = fs2.Read( buff, 0, buflen ); } } } } // 後片付け foreach ( Phon ph in phons ) { if ( !ph.ModeR ) { if ( File.Exists( ph.FileName ) ) { PortUtil.deleteFile( ph.FileName ); } } } if ( File.Exists( file_whd ) ) { PortUtil.deleteFile( file_whd ); } if ( File.Exists( file_dat ) ) { PortUtil.deleteFile( file_dat ); } if ( saveFileDialog.ShowDialog() == DialogResult.OK ) { if ( File.Exists( saveFileDialog.FileName ) ) { PortUtil.deleteFile( saveFileDialog.FileName ); } LastWave = saveFileDialog.FileName; PortUtil.moveFile( file, saveFileDialog.FileName ); } else { PortUtil.deleteFile( file ); } return true; } else { return false; } } }
public static ScriptReturnStatus Edit( VsqFileEx vsq ){ if( AppManager.getSelectedEventCount() <= 0 ){ return ScriptReturnStatus.NOT_EDITED; } string pluginTxtPath = s_plugin_txt_path; if( pluginTxtPath == "" ){ AppManager.showMessageBox( "pluginTxtPath=" + pluginTxtPath ); return ScriptReturnStatus.ERROR; } if( !System.IO.File.Exists( pluginTxtPath ) ){ AppManager.showMessageBox( "'" + pluginTxtPath + "' does not exists" ); return ScriptReturnStatus.ERROR; } System.Text.Encoding shift_jis = System.Text.Encoding.GetEncoding( "Shift_JIS" ); string name = ""; string exe_path = ""; using( StreamReader sr = new StreamReader( pluginTxtPath, shift_jis ) ){ string line = ""; while( (line = sr.ReadLine()) != null ){ string[] spl = line.Split( new char[]{ '=' }, StringSplitOptions.RemoveEmptyEntries ); if( line.StartsWith( "name=" ) ){ name = spl[1]; }else if( line.StartsWith( "execute=" ) ){ exe_path = Path.Combine( Path.GetDirectoryName( pluginTxtPath ), spl[1] ); } } } if( exe_path == "" ){ return ScriptReturnStatus.ERROR; } if( !System.IO.File.Exists( exe_path ) ){ AppManager.showMessageBox( "'" + exe_path + "' does not exists" ); return ScriptReturnStatus.ERROR; } // ustを用意 ------------------------------------------------------------------------ // 方針は,一度VsqFileに音符を格納->UstFile#.ctor( VsqFile )を使って一括変換 // メイン画面で選択されているアイテムを列挙 List<VsqEvent> items = new List<VsqEvent>(); for( Iterator itr = AppManager.getSelectedEventIterator(); itr.hasNext(); ){ SelectedEventEntry item_itr = (SelectedEventEntry)itr.next(); if( item_itr.original.ID.type == VsqIDType.Anote ){ items.Add( (VsqEvent)item_itr.original.clone() ); } } items.Sort(); // R用音符のテンプレート VsqEvent template = new VsqEvent(); template.ID.type = VsqIDType.Anote; template.ID.LyricHandle = new LyricHandle( "R", "" ); int count = items.Count; // アイテムが2個以上の場合は、間にRを挿入する必要があるかどうか判定 if( count > 1 ){ List<VsqEvent> add = new List<VsqEvent>(); // 追加するR VsqEvent last_event = items[0]; for( int i = 1; i < count; i++ ){ VsqEvent item = items[i]; if( last_event.Clock + last_event.ID.Length < item.Clock ){ VsqEvent add_temp = (VsqEvent)template.clone(); add_temp.Clock = last_event.Clock + last_event.ID.Length; add_temp.ID.Length = item.Clock - add_temp.Clock; add.Add( add_temp ); } last_event = item; } foreach( VsqEvent v in add ){ items.Add( v ); } items.Sort(); } // ヘッダ int TEMPO = 120; // 選択アイテムの直前直後に未選択アイテムがあるかどうかを判定 int clock_begin = items[0].Clock; int clock_end = items[items.Count - 1].Clock; VsqTrack vsq_track = vsq.Track.get( AppManager.getSelected() ); VsqEvent tlast_event = null; VsqEvent prev = null; VsqEvent next = null; // VsqFile -> UstFileのコンバート VsqFile conv = new VsqFile( "Miku", 2, 4, 4, (int)(6e7 / TEMPO) ); VsqTrack conv_track = conv.Track.get( 1 ); for( Iterator itr = vsq_track.getNoteEventIterator(); itr.hasNext(); ){ VsqEvent item_itr = (VsqEvent)itr.next(); if( item_itr.Clock == clock_begin ){ if( tlast_event != null ){ // アイテムあり if( tlast_event.Clock + tlast_event.ID.Length == clock_begin ){ // ゲートタイム差0で接続 prev = (VsqEvent)tlast_event.clone(); }else{ // 時間差アリで接続 prev = (VsqEvent)template.clone(); prev.Clock = tlast_event.Clock + tlast_event.ID.Length; prev.ID.Length = clock_begin - (tlast_event.Clock + tlast_event.ID.Length); } } } tlast_event = item_itr; if( item_itr.Clock == clock_end ){ if( itr.hasNext() ){ VsqEvent foo = (VsqEvent)itr.next(); int clock_end_end = clock_end + items[items.Count - 1].ID.Length; if( foo.Clock == clock_end_end ){ // ゲートタイム差0で接続 next = (VsqEvent)foo.clone(); }else{ // 時間差アリで接続 next = (VsqEvent)template.clone(); next.Clock = clock_end_end; next.ID.Length = foo.Clock - clock_end_end; } } break; } } // [#PREV]を追加 if( prev != null ){ prev.Clock -= clock_begin; conv_track.addEvent( prev ); } // ゲートタイムを計算しながら追加 count = items.Count; for( int i = 0; i < count; i++ ){ VsqEvent itemi = items[i]; itemi.Clock -= clock_begin; conv_track.addEvent( itemi ); } // [#NEXT]を追加 if( next != null ){ next.Clock -= clock_begin; conv_track.addEvent( next ); } // PIT, PBSを追加 copyCurve( vsq_track.getCurve( "pit" ), conv_track.getCurve( "pit" ), clock_begin ); copyCurve( vsq_track.getCurve( "pbs" ), conv_track.getCurve( "pbs" ), clock_begin ); string temp = Path.GetTempFileName(); UstFile tust = new UstFile( conv, 1 ); if( prev != null ){ tust.getTrack( 0 ).getEvent( 0 ).Index = int.MinValue; } if( next != null ){ tust.getTrack( 0 ).getEvent( tust.getTrack( 0 ).getEventCount() - 1 ).Index = int.MaxValue; } tust.write( temp ); // 起動 ----------------------------------------------------------------------------- StartPluginArgs arg = new StartPluginArgs(); arg.exePath = exe_path; arg.tmpFile = temp; System.Threading.Thread t = new System.Threading.Thread( new System.Threading.ParameterizedThreadStart( runPlugin ) ); s_finished = false; t.Start( arg ); while( !s_finished ){ Application.DoEvents(); } // 結果を反映 ----------------------------------------------------------------------- UstFile ust = null; try{ ust = new UstFile( temp ); }catch( Exception ex ){ AppManager.showMessageBox( "invalid ust file; ex=" + ex ); return ScriptReturnStatus.ERROR; } int track_count = ust.getTrackCount(); for( int i = 0; i < track_count; i++ ){ UstTrack track = ust.getTrack( i ); int event_count = track.getEventCount(); for( int j = 0; j < event_count; j++ ){ UstEvent itemj = track.getEvent( j ); if( itemj.Index == int.MinValue ){ AppManager.showMessageBox( "[#PREV]" ); }else if( itemj.Index == int.MaxValue ){ AppManager.showMessageBox( "[#NEXT]" ); }else{ AppManager.showMessageBox( itemj.Index + "" ); } } } try{ System.IO.File.Delete( temp ); }catch( Exception ex ){ } return ScriptReturnStatus.ERROR; }
public static ScriptReturnStatus Edit( VsqFileEx vsq ) { // 選択状態のアイテムがなければ戻る if ( AppManager.itemSelection.getEventCount() <= 0 ) { return ScriptReturnStatus.NOT_EDITED; } // 現在のトラック int selected = AppManager.getSelected(); VsqTrack vsq_track = vsq.Track.get( selected ); vsq_track.sortEvent(); // プラグイン情報の定義ファイル(plugin.txt)があるかどうかチェック string pluginTxtPath = s_plugin_txt_path; if ( pluginTxtPath == "" ) { AppManager.showMessageBox( "pluginTxtPath=" + pluginTxtPath ); return ScriptReturnStatus.ERROR; } if ( !System.IO.File.Exists( pluginTxtPath ) ) { AppManager.showMessageBox( "'" + pluginTxtPath + "' does not exists" ); return ScriptReturnStatus.ERROR; } // plugin.txtがあれば,プラグインの実行ファイルのパスを取得する System.Text.Encoding shift_jis = System.Text.Encoding.GetEncoding( "Shift_JIS" ); string name = ""; string exe_path = ""; using ( StreamReader sr = new StreamReader( pluginTxtPath, shift_jis ) ) { string line = ""; while ( (line = sr.ReadLine()) != null ) { string[] spl = line.Split( new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries ); if ( line.StartsWith( "name=" ) ) { name = spl[1]; } else if ( line.StartsWith( "execute=" ) ) { exe_path = Path.Combine( Path.GetDirectoryName( pluginTxtPath ), spl[1] ); } } } if ( exe_path == "" ) { return ScriptReturnStatus.ERROR; } if ( !System.IO.File.Exists( exe_path ) ) { AppManager.showMessageBox( "'" + exe_path + "' does not exists" ); return ScriptReturnStatus.ERROR; } // 選択状態のアイテムの最初と最後がどこか調べる // clock_start, clock_endは,最終的にはPREV, NEXTを含んだ範囲を表すことになる // sel_start, sel_endはPREV, NEXTを含まない選択範囲を表す int id_start = -1; int clock_start = int.MaxValue; int id_end = -1; int clock_end = int.MinValue; int sel_start = 0; int sel_end = 0; for ( Iterator<SelectedEventEntry> itr = AppManager.itemSelection.getEventIterator(); itr.hasNext(); ) { SelectedEventEntry item = itr.next(); if ( item.original.ID.type != VsqIDType.Anote ) { continue; } int clock = item.original.Clock; if ( clock < clock_start ) { id_start = item.original.InternalID; clock_start = clock; sel_start = clock; } clock += item.original.ID.getLength(); if ( clock_end < clock ) { id_end = item.original.InternalID; clock_end = clock; sel_end = clock; } } // 選択範囲の前後の音符を探す VsqEvent ve_prev = null; VsqEvent ve_next = null; VsqEvent l = null; for ( Iterator<VsqEvent> itr = vsq_track.getNoteEventIterator(); itr.hasNext(); ) { VsqEvent item = itr.next(); if ( item.InternalID == id_start ) { if ( l != null ) { ve_prev = l; } } if ( l != null ) { if ( l.InternalID == id_end ) { ve_next = item; } } l = item; if ( ve_prev != null && ve_next != null ) { break; } } int next_rest_clock = -1; bool prev_is_rest = false; if ( ve_prev != null ) { // 直前の音符がある場合 if ( ve_prev.Clock + ve_prev.ID.getLength() == clock_start ) { // 接続している clock_start = ve_prev.Clock; } else { // 接続していない int new_clock_start = ve_prev.Clock + ve_prev.ID.getLength(); clock_start = new_clock_start; } } else { // 無い場合 if ( vsq.getPreMeasureClocks() < clock_start ) { prev_is_rest = true; } int new_clock_start = vsq.getPreMeasureClocks(); clock_start = new_clock_start; } if ( ve_next != null ) { // 直後の音符がある場合 if ( ve_next.Clock == clock_end ) { // 接続している clock_end = ve_next.Clock + ve_next.ID.getLength(); } else { // 接続していない next_rest_clock = clock_end; clock_end = ve_next.Clock; } } // 作業用のVSQに,選択範囲のアイテムを格納 VsqFileEx v = (VsqFileEx)vsq.clone();// new VsqFile( "Miku", 1, 4, 4, 500000 ); // 選択トラックだけ残して他を削る for ( int i = 1; i < selected; i++ ) { v.Track.removeElementAt( 1 ); } for ( int i = selected + 1; i < v.Track.size(); i++ ) { v.Track.removeElementAt( selected + 1 ); } // 選択トラックの音符を全消去する VsqTrack v_track = v.Track.get( 1 ); v_track.MetaText.getEventList().clear(); for ( Iterator<VsqEvent> itr = vsq_track.getNoteEventIterator(); itr.hasNext(); ) { VsqEvent item = itr.next(); if ( clock_start <= item.Clock && item.Clock + item.ID.getLength() <= clock_end ) { v_track.addEvent( (VsqEvent)item.clone(), item.InternalID ); } } // 最後のRを手動で追加.これは自動化できない if ( next_rest_clock != -1 ) { VsqEvent item = (VsqEvent)ve_next.clone(); item.ID.LyricHandle.L0.Phrase = "R"; item.Clock = next_rest_clock; item.ID.setLength( clock_end - next_rest_clock ); v_track.addEvent( item ); } // 0~選択範囲の開始位置までを削除する v.removePart( 0, clock_start ); // vsq -> ustに変換 // キーがustのIndex, 値がInternalID TreeMap<int, int> map = new TreeMap<int, int>(); UstFile u = new UstFile( v, 1, map ); u.write( Path.Combine( PortUtil.getApplicationStartupPath(), "u.ust" ) ); // PREV, NEXTのIndex値を設定する if ( ve_prev != null || prev_is_rest ) { u.getTrack( 0 ).getEvent( 0 ).Index = UstFile.PREV_INDEX; } if ( ve_next != null ) { u.getTrack( 0 ).getEvent( u.getTrack( 0 ).getEventCount() - 1 ).Index = UstFile.NEXT_INDEX; } // ustファイルに出力 UstFileWriteOptions option = new UstFileWriteOptions(); option.settingCacheDir = false; option.settingOutFile = false; option.settingProjectName = false; option.settingTempo = true; option.settingTool1 = true; option.settingTool2 = true; option.settingTracks = false; option.settingVoiceDir = true; option.trackEnd = false; string temp = Path.GetTempFileName(); u.write( temp, option ); StringBuilder before = new StringBuilder(); using ( StreamReader sr = new StreamReader( temp, System.Text.Encoding.GetEncoding( "Shift_JIS" ) ) ) { string line = ""; while ( (line = sr.ReadLine()) != null ) { before.AppendLine( line ); } } String md5_before = PortUtil.getMD5FromString( before.ToString() ); // プラグインの実行ファイルを起動 Utau_Plugin_Invoker dialog = new Utau_Plugin_Invoker( exe_path, temp ); dialog.ShowDialog(); StringBuilder after = new StringBuilder(); using ( StreamReader sr = new StreamReader( temp, System.Text.Encoding.GetEncoding( "Shift_JIS" ) ) ) { string line = ""; while ( (line = sr.ReadLine()) != null ) { after.AppendLine( line ); } } String md5_after = PortUtil.getMD5FromString( after.ToString() ); if ( md5_before == md5_after ) { // 編集されなかったようだ return ScriptReturnStatus.NOT_EDITED; } // プラグインの実行結果をustオブジェクトにロード UstFile r = new UstFile( temp ); if ( r.getTrackCount() < 1 ) { return ScriptReturnStatus.ERROR; } // 変更のなかったものについてはプラグインは記録してこないので, // 最初の値を代入するようにする UstTrack utrack_src = u.getTrack( 0 ); UstTrack utrack_dst = r.getTrack( 0 ); for ( int i = 0; i < utrack_dst.getEventCount(); i++ ) { UstEvent ue_dst = utrack_dst.getEvent( i ); int index = ue_dst.Index; UstEvent ue_src = utrack_src.findEventFromIndex( index ); if ( ue_src == null ) { continue; } if ( !ue_dst.isEnvelopeSpecified() && ue_src.isEnvelopeSpecified() ) { ue_dst.setEnvelope( ue_src.getEnvelope() ); } if ( !ue_dst.isIntensitySpecified() && ue_src.isIntensitySpecified() ) { ue_dst.setIntensity( ue_src.getIntensity() ); } if ( !ue_dst.isLengthSpecified() && ue_src.isLengthSpecified() ) { ue_dst.setLength( ue_src.getLength() ); } if ( !ue_dst.isLyricSpecified() && ue_src.isLyricSpecified() ) { ue_dst.setLyric( ue_src.getLyric() ); } if ( !ue_dst.isModurationSpecified() && ue_src.isModurationSpecified() ) { ue_dst.setModuration( ue_src.getModuration() ); } if ( !ue_dst.isNoteSpecified() && ue_src.isNoteSpecified() ) { ue_dst.setNote( ue_src.getNote() ); } if ( !ue_dst.isPBTypeSpecified() && ue_src.isPBTypeSpecified() ) { ue_dst.setPBType( ue_src.getPBType() ); } if ( !ue_dst.isPitchesSpecified() && ue_src.isPitchesSpecified() ) { ue_dst.setPitches( ue_src.getPitches() ); } if ( !ue_dst.isPortamentoSpecified() && ue_src.isPortamentoSpecified() ) { ue_dst.setPortamento( ue_src.getPortamento() ); } if ( !ue_dst.isPreUtteranceSpecified() && ue_src.isPreUtteranceSpecified() ) { ue_dst.setPreUtterance( ue_src.getPreUtterance() ); } if ( !ue_dst.isStartPointSpecified() && ue_src.isStartPointSpecified() ) { ue_dst.setStartPoint( ue_src.getStartPoint() ); } if ( !ue_dst.isTempoSpecified() && ue_src.isTempoSpecified() ) { ue_dst.setTempo( ue_src.getTempo() ); } if ( !ue_dst.isVibratoSpecified() && ue_src.isVibratoSpecified() ) { ue_dst.setVibrato( ue_src.getVibrato() ); } if ( !ue_dst.isVoiceOverlapSpecified() && ue_src.isVoiceOverlapSpecified() ) { ue_dst.setVoiceOverlap( ue_src.getVoiceOverlap() ); } } // PREVとNEXT含めて,clock_startからclock_endまでプラグインに渡したけれど, // それが伸びて帰ってきたか縮んで帰ってきたか. int ret_length = 0; UstTrack r_track = r.getTrack( 0 ); int size = r_track.getEventCount(); for ( int i = 0; i < size; i++ ) { UstEvent ue = r_track.getEvent( i ); // 戻りのustには,変更があったものしか記録されていない int ue_length = ue.getLength(); if ( !ue.isLengthSpecified() && map.ContainsKey( ue.Index ) ) { int internal_id = map[ue.Index]; VsqEvent found_item = vsq_track.findEventFromID( internal_id ); if ( found_item != null ) { ue_length = found_item.ID.getLength(); } } // PREV, ENDの場合は長さに加えない if ( ue.Index != UstFile.NEXT_INDEX && ue.Index != UstFile.PREV_INDEX ) { ret_length += ue_length; } } // 伸び縮みがあった場合 // 伸ばしたり縮めたりするよ int delta = ret_length - (sel_end - sel_start); if ( delta > 0 ) { // のびた vsq.insertBlank( selected, sel_end, delta ); } else if ( delta < 0 ) { // 縮んだ vsq.removePart( selected, sel_end + delta, sel_end ); } // r_trackの内容をvsq_trackに転写 size = r_track.getEventCount(); int c = clock_start; for ( int i = 0; i < size; i++ ) { UstEvent ue = r_track.getEvent( i ); if ( ue.Index == UstFile.NEXT_INDEX || ue.Index == UstFile.PREV_INDEX ) { // PREVとNEXTは単に無視する continue; } int ue_length = ue.getLength(); if ( map.containsKey( ue.Index ) ) { // 既存の音符の編集 VsqEvent target = vsq_track.findEventFromID( map[ue.Index] ); if ( target == null ) { // そんなばかな・・・ continue; } if ( !ue.isLengthSpecified() ) { ue_length = target.ID.getLength(); } if ( target.UstEvent == null ) { target.UstEvent = (UstEvent)ue.clone(); } // utau固有のパラメータを転写 // pitchは後でやるので無視していい // テンポもあとでやるので無視していい if ( ue.isEnvelopeSpecified() ) { target.UstEvent.setEnvelope( ue.getEnvelope() ); } if ( ue.isModurationSpecified() ) { target.UstEvent.setModuration( ue.getModuration() ); } if ( ue.isPBTypeSpecified() ) { target.UstEvent.setPBType( ue.getPBType() ); } if ( ue.isPortamentoSpecified() ) { target.UstEvent.setPortamento( ue.getPortamento() ); } if ( ue.isPreUtteranceSpecified() ) { target.UstEvent.setPreUtterance( ue.getPreUtterance() ); } if ( ue.isStartPointSpecified() ) { target.UstEvent.setStartPoint( ue.getStartPoint() ); } if ( ue.isVibratoSpecified() ) { target.UstEvent.setVibrato( ue.getVibrato() ); } if ( ue.isVoiceOverlapSpecified() ) { target.UstEvent.setVoiceOverlap( ue.getVoiceOverlap() ); } // vocaloid, utauで同じ意味のパラメータを転写 if ( ue.isIntensitySpecified() ) { target.UstEvent.setIntensity( ue.getIntensity() ); target.ID.Dynamics = ue.getIntensity(); } if ( ue.isLengthSpecified() ) { target.UstEvent.setLength( ue.getLength() ); target.ID.setLength( ue.getLength() ); } if ( ue.isLyricSpecified() ) { target.UstEvent.setLyric( ue.getLyric() ); target.ID.LyricHandle.L0.Phrase = ue.getLyric(); } if ( ue.isNoteSpecified() ) { target.UstEvent.setNote( ue.getNote() ); target.ID.Note = ue.getNote(); } } else { // マップに入っていないので,新しい音符の追加だと思う if ( ue.getLyric() == "R" ) { // 休符.なにもしない } else { VsqEvent newe = new VsqEvent(); newe.Clock = c; newe.UstEvent = (UstEvent)ue.clone(); newe.ID = new VsqID(); AppManager.editorConfig.applyDefaultSingerStyle( newe.ID ); if ( ue.isIntensitySpecified() ) { newe.ID.Dynamics = ue.getIntensity(); } newe.ID.LyricHandle = new LyricHandle( "あ", "a" ); if ( ue.isLyricSpecified() ) { newe.ID.LyricHandle.L0.Phrase = ue.getLyric(); } newe.ID.Note = ue.getNote(); newe.ID.setLength( ue.getLength() ); newe.ID.type = VsqIDType.Anote; // internal id はaddEventメソッドで自動で割り振られる vsq_track.addEvent( newe ); } } // テンポの追加がないかチェック if ( ue.isTempoSpecified() ) { insertTempoInto( vsq, c, ue.getTempo() ); } c += ue_length; } // ピッチを転写 // pitのデータがほしいので,PREV, NEXTを削除して,VsqFileにコンバートする UstFile uf = (UstFile)r.clone(); // prev, nextを削除 UstTrack uf_track = uf.getTrack( 0 ); for ( int i = 0; i < uf_track.getEventCount(); ) { UstEvent ue = uf_track.getEvent( i ); if ( ue.Index == UstFile.NEXT_INDEX || ue.Index == UstFile.PREV_INDEX ) { uf_track.removeEventAt( i ); } else { i++; } } uf.updateTempoInfo(); // VsqFileにコンバート VsqFile uf_vsq = new VsqFile( uf ); // uf_vsqの最初のトラックの0からret_lengthクロックまでが, // vsq_trackのsel_startからsel_start+ret_lengthクロックまでに対応する. // まずPBSをコピーする CurveType[] type = new CurveType[] { CurveType.PBS, CurveType.PIT }; foreach ( CurveType ct in type ) { // コピー元を取得 VsqBPList src = uf_vsq.Track[1].getCurve( ct.getName() ); if ( src != null ) { // コピー先を取得 VsqBPList dst = vsq_track.getCurve( ct.getName() ); if ( dst == null ) { // コピー先がnullだった場合は作成 dst = new VsqBPList( ct.getName(), ct.getDefault(), ct.getMinimum(), ct.getMaximum() ); vsq_track.setCurve( ct.getName(), dst ); } // あとで復元するので,最終位置での値を保存しておく int value_at_end = dst.getValue( sel_start + ret_length ); // 復元するかどうか.最終位置にそもそもデータ点があれば復帰の必要がないので. bool do_revert = (dst.findIndexFromClock( sel_start + ret_length ) < 0); // [sel_start, sel_start + ret_length)の範囲の値を削除しておく size = dst.size(); for ( int i = size - 1; i >= 0; i-- ) { int cl = dst.getKeyClock( i ); if ( sel_start <= cl && cl < sel_start + ret_length ) { dst.removeElementAt( i ); } } // コピーを実行 size = src.size(); for ( int i = 0; i < size; i++ ) { int cl = src.getKeyClock( i ); if ( ret_length <= cl ) { break; } int value = src.getElementA( i ); dst.add( cl + sel_start, value ); } // コピー後,最終位置での値が元と異なる場合,元に戻すようにする if ( do_revert && dst.getValue( sel_start + ret_length ) != value_at_end ) { dst.add( sel_start + ret_length, value_at_end ); } } } return ScriptReturnStatus.EDITED; }
/// <summary> /// vsqxファイルを読み込み,新しいシーケンスオブジェクトを生成する /// </summary> /// <param name="filePath">ファイルパス</param> /// <exception cref="System.Exception">読み込みに失敗した時スローされる</exception> /// <returns>生成したシーケンスオブジェクト</returns> public static VsqFile readFromVsqx(string filePath) { if (filePath == null) { throw new ArgumentNullException("filePath"); } if (false == File.Exists(filePath)) { throw new Exception("file not found"); } var xml = new XmlDocument(); xml.Load(filePath); // 音源テーブルを解釈 var voiceTable = getVoiceTable(xml); // マスタートラックを解釈 XmlElement masterTrack = xml.DocumentElement["masterTrack"]; int preMeasure = int.Parse(masterTrack["preMeasure"].InnerText); VsqFile result = new VsqFile("", preMeasure, 4, 4, 500000); // テンポ変更を読み取る result.TempoTable.Clear(); foreach (XmlNode node in masterTrack.GetElementsByTagName("tempo")) { int posTick = int.Parse(node["posTick"].InnerText); int bpm = int.Parse(node["bpm"].InnerText); int tempo = (int)(6000000000L / bpm); TempoTableEntry tempoEntry = new TempoTableEntry(posTick, tempo, 0.0); result.TempoTable.Add(tempoEntry); } result.TempoTable.updateTempoInfo(); // 拍子変更を読み取る result.TimesigTable.Clear(); foreach (XmlNode node in masterTrack.GetElementsByTagName("timeSig")) { int posMes = int.Parse(node["posMes"].InnerText); int numerator = int.Parse(node["nume"].InnerText); int denominator = int.Parse(node["denomi"].InnerText); TimeSigTableEntry timesigEntry = new TimeSigTableEntry(0, numerator, denominator, posMes); result.TimesigTable.Add(timesigEntry); } result.TimesigTable.updateTimesigInfo(); // マスター以外のトラックを解釈 foreach (XmlNode node in xml.DocumentElement.GetElementsByTagName("vsTrack")) { int trackIndex = int.Parse(node["vsTrackNo"].InnerText) + 1; VsqTrack track = null; if (result.Track.Count <= trackIndex) { int amount = trackIndex + 1 - result.Track.Count; for (int i = 0; i < amount; i++) { result.Track.Add(new VsqTrack("", "")); } } track = result.Track[trackIndex]; track.setName(node["trackName"].InnerText); foreach (XmlNode child in node.ChildNodes) { if (child.Name == "musicalPart") { parseMusicalPart(voiceTable, track, child); } } } // MasterMixerをパース var mixer = xml.DocumentElement["mixer"]; var masterUnit = mixer["masterUnit"]; result.Mixer.MasterFeder = int.Parse(masterUnit["vol"].InnerText); result.Mixer.MasterMute = 0; result.Mixer.MasterPanpot = 0; // SlaveMixerをパース result.Mixer.Slave.Clear(); for (int i = 1; i < result.Track.Count; i++) { result.Mixer.Slave.Add(null); } foreach (XmlNode vsUnit in mixer.GetElementsByTagName("vsUnit")) { int vsTrackNo = int.Parse(vsUnit["vsTrackNo"].InnerText); int mute = int.Parse(vsUnit["mute"].InnerText); int solo = int.Parse(vsUnit["solo"].InnerText); int pan = int.Parse(vsUnit["pan"].InnerText); int vol = int.Parse(vsUnit["vol"].InnerText); var slave = new VsqMixerEntry(vol, pan, mute, solo); result.Mixer.Slave[vsTrackNo] = slave; } return(result); }
public UstFile(VsqFile vsq, int track_index) : this(vsq, track_index, new SortedDictionary <int, int>()) { }
/// <summary> /// vsqの指定したトラックを元に,トラックを1つだけ持つustを構築します /// </summary> /// <param name="vsq"></param> /// <param name="track_index"></param> /// <param name="id_map">UstEventのIndexフィールドと、元になったVsqEventのInternalIDを対応付けるマップ。キーがIndex、値がInternalIDを表す</param> public UstFile(VsqFile vsq, int track_index, SortedDictionary <int, int> id_map) { VsqFile work = (VsqFile)vsq.clone(); //work.removePart( 0, work.getPreMeasureClocks() ); VsqTrack vsq_track = work.Track[track_index]; // デフォルトのテンポ if (work.TempoTable.Count <= 0) { m_tempo = 120.0f; } else { m_tempo = (float)(60e6 / (double)work.TempoTable[0].Tempo); } m_tempo_table = new List <TempoTableEntry>(); m_tempo_table.Clear(); // ustには、テンポチェンジを音符の先頭にしか入れられない // あとで音符に反映させるためのテンプレートを作っておく TempoVector tempo = new TempoVector(); int last_clock = 0; int itempo = (int)(60e6 / m_tempo); foreach (var item in vsq_track.getNoteEventIterator()) { if (last_clock < item.Clock) { // 休符Rの分 tempo.Add(new TempoTableEntry(last_clock, itempo, work.getSecFromClock(last_clock))); } tempo.Add(new TempoTableEntry(item.Clock, itempo, work.getSecFromClock(item.Clock))); last_clock = item.Clock + item.ID.getLength(); } if (tempo.Count == 0) { tempo.Add(new TempoTableEntry(0, (int)(60e6 / m_tempo), 0.0)); } // tempoの中の各要素の時刻が、vsq.TempoTableから計算した時刻と合致するよう調節 #if DEBUG sout.println("UstFile#.ctor; before; list="); for (int i = 0; i < tempo.Count; i++) { TempoTableEntry item = tempo[i]; sout.println(" #" + i + "; c" + item.Clock + "; T" + item.Tempo + "; t" + (60e6 / item.Tempo) + "; sec" + item.Time); } #endif TempoTableEntry prev = tempo[0]; for (int i = 1; i < tempo.Count; i++) { TempoTableEntry item = tempo[i]; double sec = item.Time - prev.Time; int delta = item.Clock - prev.Clock; // deltaクロックでsecを表現するにはテンポをいくらにすればいいか? int draft = (int)(480.0 * sec * 1e6 / (double)delta); // 丸め誤差が入るので、Timeを更新 // ustに実際に記録されるテンポはいくらか? float act_tempo = (float)double.Parse(PortUtil.formatDecimal("0.00", 60e6 / draft)); int i_act_tempo = (int)(60e6 / act_tempo); prev.Tempo = i_act_tempo; item.Time = prev.Time + 1e-6 * delta * prev.Tempo / 480.0; prev = item; } #if DEBUG sout.println("UstFile#.ctor; after; list="); for (int i = 0; i < tempo.Count; i++) { TempoTableEntry item = tempo[i]; sout.println(" #" + i + "; c" + item.Clock + "; T" + item.Tempo + "; t" + (60e6 / item.Tempo) + "; sec" + item.Time); } sout.println("UstFile#.ctor; vsq.TempoTable="); for (int i = 0; i < work.TempoTable.Count; i++) { TempoTableEntry item = work.TempoTable[i]; sout.println(" #" + i + "; c" + item.Clock + "; T" + item.Tempo + "; t" + (60e6 / item.Tempo) + "; sec" + item.Time); } #endif // R用音符のテンプレート int PBTYPE = 5; UstEvent template = new UstEvent(); template.setLyric("R"); template.setNote(60); template.setPreUtterance(0); template.setVoiceOverlap(0); template.setIntensity(100); template.setModuration(0); // 再生秒時をとりあえず無視して,ゲートタイム基準で音符を追加 UstTrack track_add = new UstTrack(); last_clock = 0; int index = 0; foreach (var item in vsq_track.getNoteEventIterator()) { if (last_clock < item.Clock) { // ゲートタイム差あり,Rを追加 UstEvent itemust = (UstEvent)template.clone(); itemust.setLength(item.Clock - last_clock); itemust.Index = index; index++; id_map[itemust.Index] = -1; track_add.addEvent(itemust); } UstEvent item_add = (UstEvent)item.UstEvent.clone(); item_add.setLength(item.ID.getLength()); item_add.setLyric(item.ID.LyricHandle.L0.Phrase); item_add.setNote(item.ID.Note); item_add.Index = index; id_map[item_add.Index] = item.InternalID; if (item.UstEvent.getEnvelope() != null) { item_add.setEnvelope((UstEnvelope)item.UstEvent.getEnvelope().clone()); } index++; track_add.addEvent(item_add); last_clock = item.Clock + item.ID.getLength(); } // テンポを格納(イベント数はあっているはず) if (track_add.getEventCount() > 0) { int size = track_add.getEventCount(); int lasttempo = -1; // ありえない値にしておく for (int i = 0; i < size; i++) { TempoTableEntry item = tempo[i]; if (lasttempo != item.Tempo) { // テンポ値が変わっているもののみ追加 UstEvent ue = track_add.getEvent(i); ue.setTempo((float)(60e6 / item.Tempo)); lasttempo = item.Tempo; m_tempo_table.Add(item); } } } else { // tempoはどうせ破棄されるのでクローンしなくていい m_tempo_table.Add(tempo[0]); } // ピッチを反映 // まず絶対ピッチを取得 VsqBPList abs_pit = new VsqBPList("", 600000, 0, 1280000); VsqBPList cpit = vsq_track.getCurve("pit"); int clock = 0; int search_indx = 0; int pit_size = cpit.size(); foreach (var item in track_add.getNoteEventIterator()) { int c = clock; int len = item.getLength(); clock += len; if (item.getLyric() == "R") { continue; } // 音符の先頭のpitは必ず入れる abs_pit.add(c, (int)(item.getNote() * 10000 + vsq_track.getPitchAt(c) * 100)); // c~c+lenまで for (int i = search_indx; i < pit_size; i++) { int c2 = cpit.getKeyClock(i); if (c < c2 && c2 < clock) { abs_pit.add(c2, (int)(item.getNote() * 10000 + vsq_track.getPitchAt(c2) * 100)); search_indx = i; } else if (clock <= c2) { break; } } } // ピッチをピッチベンドに変換しながら反映 clock = 0; foreach (var item in track_add.getNoteEventIterator()) { double sec_at_clock = tempo.getSecFromClock(clock); double sec_pre = item.getPreUtterance() / 1000.0; double sec_stp = item.getStartPoint() / 1000.0; double sec_at_begin = sec_at_clock - sec_pre - sec_stp; int clock_begin = (int)tempo.getClockFromSec(sec_at_begin); // 音符先頭との距離がPBTYPEの倍数になるようにする clock_begin -= (clock < clock_begin) ? ((clock_begin - clock) % PBTYPE) : ((clock - clock_begin) % PBTYPE); // clock_beginがsec_at_beginより前方になるとNGなので修正する double sec_at_clock_begin = tempo.getSecFromClock(clock_begin); while (sec_at_clock_begin < sec_at_begin) { clock_begin += PBTYPE; sec_at_clock_begin = tempo.getSecFromClock(clock_begin); } int clock_end = clock + item.getLength(); List <float> pitch = new List <float>(); bool allzero = true; ByRef <int> ref_indx = new ByRef <int>(0); for (int cl = clock_begin; cl < clock_end; cl += PBTYPE) { int abs = abs_pit.getValue(cl, ref_indx); float pit = (float)(abs / 100.0) - item.getNote() * 100; if (pit != 0.0) { allzero = false; } pitch.Add(pit); } if (!allzero) { item.setPitches(PortUtil.convertFloatArray(pitch.ToArray())); item.setPBType(PBTYPE); } else { item.setPBType(-1); } clock += item.getLength(); } m_tracks.Add(track_add); }
public void readFromVsqx() { VsqFile vsq = VsqxReader.readFromVsqx("./fixture/track1.vsqx"); // トラック数 Assert.AreEqual(2, vsq.Track.size()); // プリメジャー Assert.AreEqual(4, vsq.getPreMeasure()); // イベント数 // 最初のmusicalPartには歌手変更1個と音符2個 // 2つ目のmusicalPartには歌手変更1個と音符1個が入っているはず VsqTrack track = vsq.Track.get(1); Assert.AreEqual(6, track.getEventCount()); // 歌手変更が正しく読み込まれているか // 1個目はデフォルトの歌手変更なのでスルー var singerChange = track.getEvent(1); Assert.AreEqual(7680, singerChange.Clock); Assert.Null(singerChange.ID.IconDynamicsHandle); Assert.Null(singerChange.ID.LyricHandle); Assert.Null(singerChange.ID.NoteHeadHandle); Assert.Null(singerChange.ID.VibratoHandle); Assert.AreEqual("VY1V3", singerChange.ID.IconHandle.IDS); Assert.AreEqual("$07010000", singerChange.ID.IconHandle.IconID); Assert.AreEqual(0, singerChange.ID.IconHandle.Language); Assert.AreEqual(0, singerChange.ID.IconHandle.Program); // 1つめの音符イベントが正しく読み込まれているか var firstEvent = track.getEvent(2); Assert.AreEqual(7680 + 0, firstEvent.Clock); Assert.AreEqual(48, firstEvent.ID.Note); Assert.AreEqual(480, firstEvent.ID.getLength()); Assert.AreEqual(62, firstEvent.ID.Dynamics); Assert.AreEqual(50, firstEvent.ID.DEMaccent); Assert.AreEqual(8, firstEvent.ID.PMBendDepth); Assert.AreEqual(0, firstEvent.ID.PMBendLength); Assert.AreEqual(50, firstEvent.ID.DEMdecGainRate); Assert.AreEqual(false, firstEvent.ID.isFallPortamento()); Assert.AreEqual(false, firstEvent.ID.isRisePortamento()); Assert.Null(firstEvent.ID.IconDynamicsHandle); Assert.AreEqual("わ", firstEvent.ID.LyricHandle.L0.Phrase); Assert.AreEqual("w a", firstEvent.ID.LyricHandle.L0.getPhoneticSymbol()); Assert.AreEqual(true, firstEvent.ID.LyricHandle.L0.PhoneticSymbolProtected); Assert.Null(firstEvent.ID.NoteHeadHandle); Assert.AreEqual("$04040000", firstEvent.ID.VibratoHandle.IconID); Assert.AreEqual(316, firstEvent.ID.VibratoHandle.getLength()); var depthBP = firstEvent.ID.VibratoHandle.getDepthBP(); Assert.AreEqual(1, depthBP.getCount()); Assert.AreEqual(0.0f, depthBP.getElement(0).X); Assert.AreEqual(64, depthBP.getElement(0).Y); var rateBP = firstEvent.ID.VibratoHandle.getRateBP(); Assert.AreEqual(1, rateBP.getCount()); Assert.AreEqual(0.0f, rateBP.getElement(0).X); Assert.AreEqual(50, rateBP.getElement(0).Y); Assert.AreEqual(164, firstEvent.ID.VibratoDelay); Assert.Null(firstEvent.ID.IconHandle); // 2つめの音符イベントが正しく読み込まれているか var secondEvent = track.getEvent(3); Assert.AreEqual(7680 + 480, secondEvent.Clock); Assert.AreEqual(50, secondEvent.ID.Note); Assert.AreEqual(960, secondEvent.ID.getLength()); Assert.AreEqual(63, secondEvent.ID.Dynamics); Assert.AreEqual(50, secondEvent.ID.DEMaccent); Assert.AreEqual(8, secondEvent.ID.PMBendDepth); Assert.AreEqual(0, secondEvent.ID.PMBendLength); Assert.AreEqual(50, secondEvent.ID.DEMdecGainRate); Assert.AreEqual(true, secondEvent.ID.isFallPortamento()); Assert.AreEqual(false, secondEvent.ID.isRisePortamento()); Assert.Null(secondEvent.ID.IconDynamicsHandle); Assert.AreEqual("は", secondEvent.ID.LyricHandle.L0.Phrase); Assert.AreEqual("h a", secondEvent.ID.LyricHandle.L0.getPhoneticSymbol()); Assert.AreEqual(false, secondEvent.ID.LyricHandle.L0.PhoneticSymbolProtected); Assert.Null(secondEvent.ID.NoteHeadHandle); Assert.AreEqual("$04040004", secondEvent.ID.VibratoHandle.IconID); Assert.AreEqual(624, secondEvent.ID.VibratoHandle.getLength()); depthBP = secondEvent.ID.VibratoHandle.getDepthBP(); Assert.AreEqual(1, depthBP.getCount()); Assert.AreEqual(0.0f, depthBP.getElement(0).X); Assert.AreEqual(64, depthBP.getElement(0).Y); rateBP = secondEvent.ID.VibratoHandle.getRateBP(); Assert.AreEqual(1, secondEvent.ID.VibratoHandle.getRateBP().getCount()); Assert.AreEqual(0.0f, rateBP.getElement(0).X); Assert.AreEqual(64, rateBP.getElement(0).Y); Assert.AreEqual(336, secondEvent.ID.VibratoDelay); Assert.Null(secondEvent.ID.IconHandle); // 2つ目の歌手変更 var singerChange2 = track.getEvent(4); Assert.AreEqual(10560, singerChange2.Clock); Assert.Null(singerChange2.ID.IconDynamicsHandle); Assert.Null(singerChange2.ID.LyricHandle); Assert.Null(singerChange2.ID.NoteHeadHandle); Assert.Null(singerChange2.ID.VibratoHandle); Assert.AreEqual("Miku(V2)", singerChange2.ID.IconHandle.IDS); Assert.AreEqual("$07010001", singerChange2.ID.IconHandle.IconID); Assert.AreEqual(0, singerChange2.ID.IconHandle.Language); Assert.AreEqual(1, singerChange2.ID.IconHandle.Program); // 3つめの音符イベントが正しく読み込まれているか var thirdEvent = track.getEvent(5); Assert.AreEqual(10560 + 665, thirdEvent.Clock); Assert.AreEqual(60, thirdEvent.ID.Note); Assert.AreEqual(480, thirdEvent.ID.getLength()); Assert.AreEqual(64, thirdEvent.ID.Dynamics); Assert.AreEqual(50, thirdEvent.ID.DEMaccent); Assert.AreEqual(8, thirdEvent.ID.PMBendDepth); Assert.AreEqual(0, thirdEvent.ID.PMBendLength); Assert.AreEqual(50, thirdEvent.ID.DEMdecGainRate); Assert.AreEqual(false, thirdEvent.ID.isFallPortamento()); Assert.AreEqual(false, thirdEvent.ID.isRisePortamento()); Assert.Null(thirdEvent.ID.IconDynamicsHandle); Assert.AreEqual("a", thirdEvent.ID.LyricHandle.L0.Phrase); Assert.AreEqual("a", thirdEvent.ID.LyricHandle.L0.getPhoneticSymbol()); Assert.AreEqual(false, thirdEvent.ID.LyricHandle.L0.PhoneticSymbolProtected); Assert.Null(thirdEvent.ID.NoteHeadHandle); Assert.AreEqual("$04040000", thirdEvent.ID.VibratoHandle.IconID); Assert.AreEqual(316, thirdEvent.ID.VibratoHandle.getLength()); depthBP = thirdEvent.ID.VibratoHandle.getDepthBP(); Assert.AreEqual(1, depthBP.getCount()); Assert.AreEqual(0.0f, depthBP.getElement(0).X); Assert.AreEqual(64, depthBP.getElement(0).Y); rateBP = thirdEvent.ID.VibratoHandle.getRateBP(); Assert.AreEqual(1, thirdEvent.ID.VibratoHandle.getRateBP().getCount()); Assert.AreEqual(0.0f, rateBP.getElement(0).X); Assert.AreEqual(50, rateBP.getElement(0).Y); Assert.AreEqual(164, thirdEvent.ID.VibratoDelay); Assert.Null(thirdEvent.ID.IconHandle); // トラック名 Assert.AreEqual("Track", track.getName()); // テンポ変更 Assert.AreEqual(2, vsq.TempoTable.size()); Assert.AreEqual(0, vsq.TempoTable.get(0).Clock); Assert.AreEqual(500000, vsq.TempoTable.get(0).Tempo); Assert.AreEqual(8640, vsq.TempoTable.get(1).Clock); Assert.AreEqual(1199760, vsq.TempoTable.get(1).Tempo); // 拍子変更 Assert.AreEqual(2, vsq.TimesigTable.size()); Assert.AreEqual(0, vsq.TimesigTable.get(0).Clock); Assert.AreEqual(4, vsq.TimesigTable.get(0).Numerator); Assert.AreEqual(4, vsq.TimesigTable.get(0).Denominator); Assert.AreEqual(9600, vsq.TimesigTable.get(1).Clock); Assert.AreEqual(3, vsq.TimesigTable.get(1).Numerator); Assert.AreEqual(4, vsq.TimesigTable.get(1).Denominator); // コントロールカーブ // DYN var dyn = track.getCurve("DYN"); Assert.AreEqual(1, dyn.size()); Assert.AreEqual(720, dyn.getKeyClock(0)); Assert.AreEqual(96, dyn.getElement(0)); // BRE var bre = track.getCurve("BRE"); Assert.AreEqual(1, bre.size()); Assert.AreEqual(720, bre.getKeyClock(0)); Assert.AreEqual(102, bre.getElement(0)); // BRI // CLE // OPE var ope = track.getCurve("OPE"); Assert.AreEqual(3, ope.size()); Assert.AreEqual(7680 + 0, ope.getKeyClock(0)); Assert.AreEqual(127, ope.getElement(0)); Assert.AreEqual(7680 + 480, ope.getKeyClock(1)); Assert.AreEqual(127, ope.getElement(1)); Assert.AreEqual(10560 + 665, ope.getKeyClock(2)); Assert.AreEqual(127, ope.getElement(2)); // GEN // POR // PIT // PBS // Mixerが正しく読み込まれているか Assert.AreEqual(2, vsq.Mixer.MasterFeder); Assert.AreEqual(1, vsq.Mixer.Slave.size()); Assert.AreEqual(1, vsq.Mixer.Slave.get(0).Solo); Assert.AreEqual(0, vsq.Mixer.Slave.get(0).Mute); Assert.AreEqual(64, vsq.Mixer.Slave.get(0).Panpot); Assert.AreEqual(1, vsq.Mixer.Slave.get(0).Feder); }