private static void appendParameterEvents(EventQueueSequence list, VsqBPList cle, int parameter_index, int clock_start, int clock_end) { int max = cle.getMaximum(); int min = cle.getMinimum(); float order = 1.0f / (float)(max - min); if (cle != null) { int keycount = cle.size(); for (int i = 0; i < keycount; i++) { int clock = cle.getKeyClock(i); if (clock_start <= clock && clock <= clock_end) { int value = cle.getElementA(i); MidiEventQueue queue = list.get(clock); ParameterEvent pe = new ParameterEvent(); pe.index = parameter_index; pe.value = (value - min) * order; queue.param.add(pe); } else if (clock_end < clock) { break; } } } }
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 testReflectVibratoPitch() { var tempoTable = new TempoVector(); var pitchBend = new VsqBPList( CurveType.PIT.getName(), CurveType.PIT.getDefault(), CurveType.PIT.getMinimum(), CurveType.PIT.getMaximum() ); var pitchBendSensitivity = new VsqBPList( CurveType.PIT.getName(), CurveType.PIT.getDefault(), CurveType.PIT.getMinimum(), CurveType.PIT.getMaximum() ); pitchBend.add( 0, 8191 ); pitchBendSensitivity.add( 0, 2 ); var generator = new AquesTone2WaveGeneratorStub(); var item = new VsqEvent( 0, new VsqID() ); item.ID.type = VsqIDType.Anote; item.ID.VibratoHandle = new VibratoHandle(); item.ID.VibratoHandle.StartRate = 0x40; item.ID.VibratoHandle.RateBP.clear(); item.ID.VibratoHandle.StartDepth = 0x40; item.ID.VibratoHandle.DepthBP.clear(); item.ID.setLength( 480 ); item.ID.VibratoDelay = 430; item.ID.VibratoHandle.setLength( 50 ); generator.reflectVibratoPitch( item, pitchBend, pitchBendSensitivity, tempoTable ); { var expectedPit = new Dictionary<int, int> { { 0, 8191 }, { 430, 5461 }, { 431, 5467 }, { 432, 5484 }, { 433, 5514 }, { 434, 5555 }, { 435, 5608 }, { 436, 5672 }, { 437, 5748 }, { 438, 5835 }, { 439, 5932 }, { 440, 6041 }, { 441, 6096 }, { 442, 6151 }, { 443, 6205 }, { 444, 6257 }, { 445, 6309 }, { 446, 6360 }, { 447, 6410 }, { 448, 6459 }, { 449, 6507 }, { 450, 6553 }, { 451, 6598 }, { 452, 6642 }, { 453, 6684 }, { 454, 6725 }, { 455, 6764 }, { 456, 6802 }, { 457, 6838 }, { 458, 6873 }, { 459, 6906 }, { 460, 6937 }, { 461, 6967 }, { 462, 6994 }, { 463, 7020 }, { 464, 7044 }, { 465, 7066 }, { 466, 7087 }, { 467, 7105 }, { 468, 7121 }, { 469, 7136 }, { 470, 7148 }, { 471, 6989 }, { 472, 6826 }, { 473, 6660 }, { 474, 6491 }, { 475, 6320 }, { 476, 6149 }, { 477, 5976 }, { 478, 5804 }, { 479, 5632 }, { 480, 8191 } }; Assert.AreEqual( expectedPit.Count, pitchBend.size() ); int i = 0; foreach ( var pitInfo in expectedPit ) { Assert.AreEqual( pitInfo.Key, pitchBend.getKeyClock( i ) ); Assert.AreEqual( pitInfo.Value, pitchBend.getElementA( i ) ); ++i; } } { var expectedPbs = new Dictionary<int, int> { { 0, 2 }, { 430, 3 }, { 480, 2 } }; Assert.AreEqual( expectedPbs.Count, pitchBendSensitivity.size() ); int i = 0; foreach ( var pbsInfo in expectedPbs ) { Assert.AreEqual( pbsInfo.Key, pitchBendSensitivity.getKeyClock( i ) ); Assert.AreEqual( pbsInfo.Value, pitchBendSensitivity.getElementA( i ) ); ++i; } } }
private static void amplify( VsqBPList source, VsqBPList target, double amplify ) { target.clear(); int count = source.size(); int min = target.getMinimum(); int max = target.getMaximum(); for ( int i = 0; i < count; i++ ) { int clock = source.getKeyClock( i ); int value = (int)(source.getElement( i ) * amplify); if ( value < min ) { value = min; } if ( max < value ) { value = max; } target.add( clock, value ); } }
private static void amplify(VsqBPList source, VsqBPList target, double amplify) { target.clear(); int count = source.size(); int min = target.getMinimum(); int max = target.getMaximum(); for (int i = 0; i < count; i++) { int clock = source.getKeyClock(i); int value = (int)(source.getElement(i) * amplify); if (value < min) { value = min; } if (max < value) { value = max; } target.add(clock, value); } }
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 VsqBPListComparisonContext(VsqBPList list1, VsqBPList list2) { // インデクス0の位置に,デフォルト値を持つダミーがあるように振舞わせる this.list1 = list1; this.list2 = list2; }
public void reflectVibratoPitch( VsqEvent item, VsqBPList pitchBend, VsqBPList pitchBendSensitivity, TempoVector tempoTable ) { base.reflectNoteEventPitch( item, pitchBend, pitchBendSensitivity, tempoTable ); }
/// <summary> /// 音符に付随するピッチベンドの情報を、PIT・PBS カーブに反映する /// </summary> /// <param name="item">音符</param> /// <param name="pitchBend">PIT カーブ</param> /// <param name="pitchBendSensitivity">PBS カーブ</param> /// <param name="tempoTable">テンポ情報</param> protected void reflectNoteEventPitch( VsqEvent item, VsqBPList pitchBend, VsqBPList pitchBendSensitivity, TempoVector tempoTable ) { if ( item.ID.type != VsqIDType.Anote ) return; // AquesTone2 では、note on と同 clock にピッチベンドイベントを送らないと音程が反映されないので、必ずピッチイベントが送られるようにする pitchBend.add( item.Clock, pitchBend.getValue( item.Clock ) ); if ( item.ID.VibratoHandle == null ) { return; } int startClock = item.Clock + item.ID.VibratoDelay; int vibratoLength = item.ID.Length - item.ID.VibratoDelay; var iterator = new VibratoPointIteratorByClock( tempoTable, item.ID.VibratoHandle.RateBP, item.ID.VibratoHandle.StartRate, item.ID.VibratoHandle.DepthBP, item.ID.VibratoHandle.StartDepth, startClock, vibratoLength ); var pitContext = new ByRef<int>( 0 ); var pbsContext = new ByRef<int>( 0 ); int pitAtEnd = pitchBend.getValue( startClock + vibratoLength ); int pbsAtEnd = pitchBendSensitivity.getValue( startClock + vibratoLength ); var pitBackup = (VsqBPList)pitchBend.Clone(); var pbsBackup = (VsqBPList)pitchBendSensitivity.Clone(); bool resetPBS = false; double maxNetPitchBendInCent = 0.0; for ( int clock = startClock; clock < startClock + vibratoLength && iterator.hasNext(); ++clock ) { double vibratoPitchBendInCent = iterator.next() * 100.0; int pit = pitchBend.getValue( clock, pitContext ); int pbs = pitchBendSensitivity.getValue( clock, pbsContext ); const double pow2_13 = 8192; double netPitchBendInCent = (pbs * pit / pow2_13) * 100.0 + vibratoPitchBendInCent; maxNetPitchBendInCent = Math.Max( maxNetPitchBendInCent, Math.Abs( netPitchBendInCent ) ); int draftPitchBend = (int)Math.Round( (netPitchBendInCent / 100.0) * pow2_13 / pbs ); if ( draftPitchBend < pitchBend.Minimum || pitchBend.Maximum < draftPitchBend ) { // pbs を変更せずにビブラートによるピッチベンドを反映しようとすると、 // pit が範囲を超えてしまう。 resetPBS = true; } else { if ( draftPitchBend != pit ) { pitchBend.add( clock, draftPitchBend ); } } } if ( !resetPBS ) { return; } pitchBend.Data = pitBackup.Data; // ピッチベンドの最大値を実現するのに必要なPBS int requiredPitchbendSensitivity = (int)Math.Ceiling( maxNetPitchBendInCent / 100.0 ); int pseudoMaxPitchbendSensitivity = 12; // AquesTone2 は最大 12 半音までベンドできる。 if ( requiredPitchbendSensitivity < pitchBendSensitivity.Minimum ) requiredPitchbendSensitivity = pitchBendSensitivity.Minimum; if ( pseudoMaxPitchbendSensitivity < requiredPitchbendSensitivity ) requiredPitchbendSensitivity = pseudoMaxPitchbendSensitivity; { int i = 0; while ( i < pitchBend.size() ) { var clock = pitchBend.getKeyClock( i ); if ( startClock <= clock && clock < startClock + vibratoLength ) { pitchBend.removeElementAt( i ); } else { ++i; } } } { int i = 0; while ( i < pitchBendSensitivity.size() ) { var clock = pitchBendSensitivity.getKeyClock( i ); if ( startClock <= clock && clock < startClock + vibratoLength ) { pitchBendSensitivity.removeElementAt( i ); } else { ++i; } } } if ( pitchBendSensitivity.getValue( startClock ) != requiredPitchbendSensitivity ) { pitchBendSensitivity.add( startClock, requiredPitchbendSensitivity ); } pitchBend.add( startClock + vibratoLength, pitAtEnd ); pitchBendSensitivity.add( startClock + vibratoLength, pbsAtEnd ); iterator.rewind(); pitContext.value = 0; pbsContext.value = 0; int lastPitchBend = pitchBend.getValue( startClock ); for ( int clock = startClock; clock < startClock + vibratoLength && iterator.hasNext(); ++clock ) { double vibratoPitchBendInCent = iterator.next() * 100.0; int pit = pitBackup.getValue( clock, pitContext ); int pbs = pbsBackup.getValue( clock, pbsContext ); const double pow2_13 = 8192; double netPitchBendInCent = (pbs * pit / pow2_13) * 100.0 + vibratoPitchBendInCent; maxNetPitchBendInCent = Math.Max( maxNetPitchBendInCent, Math.Abs( netPitchBendInCent ) ); int draftPitchBend = (int)Math.Round((netPitchBendInCent / 100.0) * pow2_13 / requiredPitchbendSensitivity); if ( draftPitchBend < pitchBend.Minimum ) draftPitchBend = pitchBend.Minimum; if ( pitchBend.Maximum < draftPitchBend ) draftPitchBend = pitchBend.Maximum; if ( draftPitchBend != lastPitchBend ) { pitchBend.add( clock, draftPitchBend ); lastPitchBend = draftPitchBend; } } }
private static void appendParameterEvents( TreeMap<Integer, MidiEventQueue> list, VsqBPList cle, int parameter_index, int clock_start, int clock_end ) { int max = cle.getMaximum(); int min = cle.getMinimum(); float order = 1.0f / (float)(max - min); if ( cle != null ) { int keycount = cle.size(); for ( int i = 0; i < keycount; i++ ) { int clock = cle.getKeyClock( i ); if ( clock_start <= clock && clock <= clock_end ) { int value = cle.getElementA( i ); MidiEventQueue queue = null; if ( list.containsKey( clock ) ) { queue = list.get( clock ); } else { queue = new MidiEventQueue(); } if ( queue.param == null ) { queue.param = new Vector<ParameterEvent>(); } ParameterEvent pe = new ParameterEvent(); pe.index = parameter_index; pe.value = (value - min) * order; queue.param.add( pe ); list.put( clock, queue ); } else if ( clock_end < clock ) { break; } } } }
private void applyValue(bool mode_clock) { if (!m_changed) { return; } int value = m_curve.getDefault(); try { value = int.Parse(txtDataPointValue.Text); } catch (Exception ex) { Logger.write(typeof(FormCurvePointEdit) + ".applyValue; ex=" + ex + "\n"); return; } if (value < m_curve.getMinimum()) { value = m_curve.getMinimum(); } else if (m_curve.getMaximum() < value) { value = m_curve.getMaximum(); } int clock = 0; try { clock = int.Parse(txtDataPointClock.Text); } catch (Exception ex) { Logger.write(typeof(FormCurvePointEdit) + ".applyValue; ex=" + ex + "\n"); return; } int selected = AppManager.getSelected(); VsqTrack vsq_track = AppManager.getVsqFile().Track[selected]; VsqBPList src = vsq_track.getCurve(m_curve.getName()); VsqBPList list = (VsqBPList)src.clone(); VsqBPPairSearchContext context = list.findElement(m_editing_id); list.move(context.clock, clock, value); CadenciiCommand run = new CadenciiCommand(VsqCommand.generateCommandTrackCurveReplace(selected, m_curve.getName(), list)); EditedZone zone = new EditedZone(); Utility.compareList(zone, new VsqBPListComparisonContext(list, src)); List <EditedZoneUnit> zoneUnits = new List <EditedZoneUnit>(); foreach (var item in zone.iterator()) { zoneUnits.Add(item); } AppManager.editHistory.register(AppManager.getVsqFile().executeCommand(run)); txtDataPointClock.Text = clock + ""; txtDataPointValue.Text = value + ""; if (mMainWindow != null) { mMainWindow.setEdited(true); mMainWindow.ensureVisible(clock); mMainWindow.refreshScreen(); } if (mode_clock) { txtDataPointClock.SelectAll(); } else { txtDataPointValue.SelectAll(); } btnUndo.Enabled = AppManager.editHistory.hasUndoHistory(); btnRedo.Enabled = AppManager.editHistory.hasRedoHistory(); m_changed = false; }
public void testReflectVibratoPitch() { var tempoTable = new TempoVector(); var pitchBend = new VsqBPList(CurveType.PIT.getName(), CurveType.PIT.getDefault(), CurveType.PIT.getMinimum(), CurveType.PIT.getMaximum()); var pitchBendSensitivity = new VsqBPList(CurveType.PIT.getName(), CurveType.PIT.getDefault(), CurveType.PIT.getMinimum(), CurveType.PIT.getMaximum()); pitchBend.add(0, 8191); pitchBendSensitivity.add(0, 2); var generator = new AquesTone2WaveGeneratorStub(); var item = new VsqEvent(0, new VsqID()); item.ID.type = VsqIDType.Anote; item.ID.VibratoHandle = new VibratoHandle(); item.ID.VibratoHandle.StartRate = 0x40; item.ID.VibratoHandle.RateBP.clear(); item.ID.VibratoHandle.StartDepth = 0x40; item.ID.VibratoHandle.DepthBP.clear(); item.ID.setLength(480); item.ID.VibratoDelay = 430; item.ID.VibratoHandle.setLength(50); generator.reflectVibratoPitch(item, pitchBend, pitchBendSensitivity, tempoTable); { var expectedPit = new Dictionary <int, int> { { 0, 8191 }, { 430, 5461 }, { 431, 5467 }, { 432, 5484 }, { 433, 5514 }, { 434, 5555 }, { 435, 5608 }, { 436, 5672 }, { 437, 5748 }, { 438, 5835 }, { 439, 5932 }, { 440, 6041 }, { 441, 6096 }, { 442, 6151 }, { 443, 6205 }, { 444, 6257 }, { 445, 6309 }, { 446, 6360 }, { 447, 6410 }, { 448, 6459 }, { 449, 6507 }, { 450, 6553 }, { 451, 6598 }, { 452, 6642 }, { 453, 6684 }, { 454, 6725 }, { 455, 6764 }, { 456, 6802 }, { 457, 6838 }, { 458, 6873 }, { 459, 6906 }, { 460, 6937 }, { 461, 6967 }, { 462, 6994 }, { 463, 7020 }, { 464, 7044 }, { 465, 7066 }, { 466, 7087 }, { 467, 7105 }, { 468, 7121 }, { 469, 7136 }, { 470, 7148 }, { 471, 6989 }, { 472, 6826 }, { 473, 6660 }, { 474, 6491 }, { 475, 6320 }, { 476, 6149 }, { 477, 5976 }, { 478, 5804 }, { 479, 5632 }, { 480, 8191 } }; Assert.AreEqual(expectedPit.Count, pitchBend.size()); int i = 0; foreach (var pitInfo in expectedPit) { Assert.AreEqual(pitInfo.Key, pitchBend.getKeyClock(i)); Assert.AreEqual(pitInfo.Value, pitchBend.getElementA(i)); ++i; } } { var expectedPbs = new Dictionary <int, int> { { 0, 2 }, { 430, 3 }, { 480, 2 } }; Assert.AreEqual(expectedPbs.Count, pitchBendSensitivity.size()); int i = 0; foreach (var pbsInfo in expectedPbs) { Assert.AreEqual(pbsInfo.Key, pitchBendSensitivity.getKeyClock(i)); Assert.AreEqual(pbsInfo.Value, pitchBendSensitivity.getElementA(i)); ++i; } } }
public void reflectVibratoPitch(VsqEvent item, VsqBPList pitchBend, VsqBPList pitchBendSensitivity, TempoVector tempoTable) { base.reflectNoteEventPitch(item, pitchBend, pitchBendSensitivity, tempoTable); }
/// <summary> /// /// </summary> /// <param name="vsq"></param> /// <param name="track"></param> /// <param name="clock_start"></param> /// <param name="clock_end"></param> /// <returns></returns> protected override EventQueueSequence generateMidiEvent(VsqFileEx vsq, int track, int clock_start, int clock_end) { EventQueueSequence list = new EventQueueSequence(); VsqTrack t = vsq.Track.get(track); addSingerEvents(list, t, clock_start, clock_end); // ノートon, off Vector <Point> pit_send = new Vector <Point>(); // PITが追加されたゲートタイム。音符先頭の分を重複して送信するのを回避するために必要。 VsqBPList pit = t.getCurve("pit"); VsqBPList pbs = t.getCurve("pbs"); VsqBPList dyn = t.getCurve("dyn"); VsqBPList bre = t.getCurve("bre"); VsqBPList cle = t.getCurve("cle"); VsqBPList por = t.getCurve("por"); for (Iterator <VsqEvent> itr = t.getNoteEventIterator(); itr.hasNext();) { VsqEvent item = itr.next(); int endclock = item.Clock + item.ID.getLength(); boolean contains_start = clock_start <= item.Clock && item.Clock <= clock_end; boolean contains_end = clock_start <= endclock && endclock <= clock_end; if (contains_start || contains_end) { if (contains_start) { #region contains_start // noteonのゲートタイムが,範囲に入っている // noteon MIDIイベントを作成 MidiEvent[] noteOnEvents = mDriver.createNoteOnEvent(item.ID.Note, item.ID.Dynamics, item.ID.LyricHandle.L0.Phrase); if (noteOnEvents.Length > 0) { MidiEventQueue queue = list.get(item.Clock); Vector <MidiEvent> add = Arrays.asList(noteOnEvents); queue.noteon.addAll(add); pit_send.add(new Point(item.Clock, item.Clock)); } /* 音符頭で設定するパラメータ */ // Release MidiEventQueue q = list.get(item.Clock); String strRelease = VsqFileEx.getEventTag(item, VsqFileEx.TAG_VSQEVENT_AQUESTONE_RELEASE); int release = 64; try { release = int.Parse(strRelease); } catch (Exception ex) { Logger.write(typeof(AquesToneWaveGenerator) + ".generateMidiEvent; ex=" + ex + "\n"); release = 64; } ParameterEvent pe = new ParameterEvent(); pe.index = mDriver.releaseParameterIndex; pe.value = release / 127.0f; q.param.add(pe); // dyn int dynAtStart = dyn.getValue(item.Clock); ParameterEvent peDyn = new ParameterEvent(); peDyn.index = mDriver.volumeParameterIndex; peDyn.value = (float)(dynAtStart - dyn.getMinimum()) / (float)(dyn.getMaximum() - dyn.getMinimum()); q.param.add(peDyn); // bre int breAtStart = bre.getValue(item.Clock); ParameterEvent peBre = new ParameterEvent(); peBre.index = mDriver.haskyParameterIndex; peBre.value = (float)(breAtStart - bre.getMinimum()) / (float)(bre.getMaximum() - bre.getMinimum()); q.param.add(peBre); // cle int cleAtStart = cle.getValue(item.Clock); ParameterEvent peCle = new ParameterEvent(); peCle.index = mDriver.resonancParameterIndex; peCle.value = (float)(cleAtStart - cle.getMinimum()) / (float)(cle.getMaximum() - cle.getMinimum()); q.param.add(peCle); // por int porAtStart = por.getValue(item.Clock); ParameterEvent pePor = new ParameterEvent(); pePor.index = mDriver.portaTimeParameterIndex; pePor.value = (float)(porAtStart - por.getMinimum()) / (float)(por.getMaximum() - por.getMinimum()); q.param.add(pePor); #endregion } // ビブラート // ビブラートが存在する場合、PBSは勝手に変更する。 if (item.ID.VibratoHandle == null) { if (contains_start) { // 音符頭のPIT, PBSを強制的に指定 int notehead_pit = pit.getValue(item.Clock); MidiEvent pit0 = getPitMidiEvent(notehead_pit); MidiEventQueue queue = list.get(item.Clock); queue.pit.clear(); queue.pit.add(pit0); int notehead_pbs = pbs.getValue(item.Clock); ParameterEvent pe = new ParameterEvent(); pe.index = mDriver.bendLblParameterIndex; pe.value = notehead_pbs / 13.0f; queue.param.add(pe); } } else { int delta_clock = 5; //ピッチを取得するクロック間隔 int tempo = 120; double sec_start_act = vsq.getSecFromClock(item.Clock); double sec_end_act = vsq.getSecFromClock(item.Clock + item.ID.getLength()); double delta_sec = delta_clock / (8.0 * tempo); //ピッチを取得する時間間隔 float pitmax = 0.0f; int st = item.Clock; if (st < clock_start) { st = clock_start; } int end = item.Clock + item.ID.getLength(); if (clock_end < end) { end = clock_end; } pit_send.add(new Point(st, end)); // ビブラートが始まるまでのピッチを取得 double sec_vibstart = vsq.getSecFromClock(item.Clock + item.ID.VibratoDelay); int pit_count = (int)((sec_vibstart - sec_start_act) / delta_sec); TreeMap <Integer, Float> pit_change = new TreeMap <Integer, Float>(); for (int i = 0; i < pit_count; i++) { double gtime = sec_start_act + delta_sec * i; int clock = (int)vsq.getClockFromSec(gtime); float pvalue = (float)t.getPitchAt(clock); pitmax = Math.Max(pitmax, Math.Abs(pvalue)); pit_change.put(clock, pvalue); } // ビブラート部分のピッチを取得 Vector <PointD> ret = new Vector <PointD>(); Iterator <PointD> itr2 = new VibratoPointIteratorBySec( vsq, item.ID.VibratoHandle.getRateBP(), item.ID.VibratoHandle.getStartRate(), item.ID.VibratoHandle.getDepthBP(), item.ID.VibratoHandle.getStartDepth(), item.Clock + item.ID.VibratoDelay, item.ID.getLength() - item.ID.VibratoDelay, (float)delta_sec); for ( ; itr2.hasNext();) { PointD p = itr2.next(); float gtime = (float)p.getX(); int clock = (int)vsq.getClockFromSec(gtime); float pvalue = (float)(t.getPitchAt(clock) + p.getY() * 100.0); pitmax = Math.Max(pitmax, Math.Abs(pvalue)); pit_change.put(clock, pvalue); } // ピッチベンドの最大値を実現するのに必要なPBS int required_pbs = (int)Math.Ceiling(pitmax / 100.0); #if DEBUG sout.println("AquesToneRenderingRunner#generateMidiEvent; required_pbs=" + required_pbs); #endif if (required_pbs > 13) { required_pbs = 13; } MidiEventQueue queue = list.get(item.Clock); ParameterEvent pe = new ParameterEvent(); pe.index = mDriver.bendLblParameterIndex; pe.value = required_pbs / 13.0f; queue.param.add(pe); // PITを順次追加 for (Iterator <Integer> itr3 = pit_change.keySet().iterator(); itr3.hasNext();) { Integer clock = itr3.next(); if (clock_start <= clock && clock <= clock_end) { float pvalue = pit_change.get(clock); int pit_value = (int)(8192.0 / (double)required_pbs * pvalue / 100.0); MidiEventQueue q = list.get(clock); MidiEvent me = getPitMidiEvent(pit_value); q.pit.clear(); q.pit.add(me); } else if (clock_end < clock) { break; } } } //pit_send.add( pit_send_p ); // noteoff MIDIイベントを作成 if (contains_end) { MidiEvent noteoff = new MidiEvent(); noteoff.firstByte = 0x80; noteoff.data = new int[] { item.ID.Note, 0x40 }; // ここのvel Vector <MidiEvent> a_noteoff = Arrays.asList(new MidiEvent[] { noteoff }); MidiEventQueue q = list.get(endclock); q.noteoff.addAll(a_noteoff); pit_send.add(new Point(endclock, endclock)); // PITの送信を抑制するために必要 } } if (clock_end < item.Clock) { break; } } // pitch bend sensitivity // RPNで送信するのが上手くいかないので、parameterを直接いぢる if (pbs != null) { int keycount = pbs.size(); for (int i = 0; i < keycount; i++) { int clock = pbs.getKeyClock(i); if (clock_start <= clock && clock <= clock_end) { int value = pbs.getElementA(i); ParameterEvent pbse = new ParameterEvent(); pbse.index = mDriver.bendLblParameterIndex; pbse.value = value / 13.0f; MidiEventQueue queue = list.get(clock); queue.param.add(pbse); } else if (clock_end < clock) { break; } } } // pitch bend if (pit != null) { int keycount = pit.size(); for (int i = 0; i < keycount; i++) { int clock = pit.getKeyClock(i); if (clock_start <= clock && clock <= clock_end) { boolean contains = false; for (Iterator <Point> itr = pit_send.iterator(); itr.hasNext();) { Point p = itr.next(); if (p.x <= clock && clock <= p.y) { contains = true; break; } } if (contains) { continue; } int value = pit.getElementA(i); MidiEvent pbs0 = getPitMidiEvent(value); MidiEventQueue queue = list.get(clock); queue.pit.clear(); queue.pit.add(pbs0); } else if (clock_end < clock) { break; } } } appendParameterEvents(list, dyn, mDriver.volumeParameterIndex, clock_start, clock_end); appendParameterEvents(list, bre, mDriver.haskyParameterIndex, clock_start, clock_end); appendParameterEvents(list, cle, mDriver.resonancParameterIndex, clock_start, clock_end); appendParameterEvents(list, por, mDriver.portaTimeParameterIndex, clock_start, clock_end); return(list); }
/// <summary> /// 音符に付随するピッチベンドの情報を、PIT・PBS カーブに反映する /// </summary> /// <param name="item">音符</param> /// <param name="pitchBend">PIT カーブ</param> /// <param name="pitchBendSensitivity">PBS カーブ</param> /// <param name="tempoTable">テンポ情報</param> protected void reflectNoteEventPitch(VsqEvent item, VsqBPList pitchBend, VsqBPList pitchBendSensitivity, TempoVector tempoTable) { if (item.ID.type != VsqIDType.Anote) { return; } // AquesTone2 では、note on と同 clock にピッチベンドイベントを送らないと音程が反映されないので、必ずピッチイベントが送られるようにする pitchBend.add(item.Clock, pitchBend.getValue(item.Clock)); if (item.ID.VibratoHandle == null) { return; } int startClock = item.Clock + item.ID.VibratoDelay; int vibratoLength = item.ID.Length - item.ID.VibratoDelay; var iterator = new VibratoPointIteratorByClock(tempoTable, item.ID.VibratoHandle.RateBP, item.ID.VibratoHandle.StartRate, item.ID.VibratoHandle.DepthBP, item.ID.VibratoHandle.StartDepth, startClock, vibratoLength); var pitContext = new ByRef <int>(0); var pbsContext = new ByRef <int>(0); int pitAtEnd = pitchBend.getValue(startClock + vibratoLength); int pbsAtEnd = pitchBendSensitivity.getValue(startClock + vibratoLength); var pitBackup = (VsqBPList)pitchBend.Clone(); var pbsBackup = (VsqBPList)pitchBendSensitivity.Clone(); bool resetPBS = false; double maxNetPitchBendInCent = 0.0; for (int clock = startClock; clock < startClock + vibratoLength && iterator.hasNext(); ++clock) { double vibratoPitchBendInCent = iterator.next() * 100.0; int pit = pitchBend.getValue(clock, pitContext); int pbs = pitchBendSensitivity.getValue(clock, pbsContext); const double pow2_13 = 8192; double netPitchBendInCent = (pbs * pit / pow2_13) * 100.0 + vibratoPitchBendInCent; maxNetPitchBendInCent = Math.Max(maxNetPitchBendInCent, Math.Abs(netPitchBendInCent)); int draftPitchBend = (int)Math.Round((netPitchBendInCent / 100.0) * pow2_13 / pbs); if (draftPitchBend < pitchBend.Minimum || pitchBend.Maximum < draftPitchBend) { // pbs を変更せずにビブラートによるピッチベンドを反映しようとすると、 // pit が範囲を超えてしまう。 resetPBS = true; } else { if (draftPitchBend != pit) { pitchBend.add(clock, draftPitchBend); } } } if (!resetPBS) { return; } pitchBend.Data = pitBackup.Data; // ピッチベンドの最大値を実現するのに必要なPBS int requiredPitchbendSensitivity = (int)Math.Ceiling(maxNetPitchBendInCent / 100.0); int pseudoMaxPitchbendSensitivity = 12; // AquesTone2 は最大 12 半音までベンドできる。 if (requiredPitchbendSensitivity < pitchBendSensitivity.Minimum) { requiredPitchbendSensitivity = pitchBendSensitivity.Minimum; } if (pseudoMaxPitchbendSensitivity < requiredPitchbendSensitivity) { requiredPitchbendSensitivity = pseudoMaxPitchbendSensitivity; } { int i = 0; while (i < pitchBend.size()) { var clock = pitchBend.getKeyClock(i); if (startClock <= clock && clock < startClock + vibratoLength) { pitchBend.removeElementAt(i); } else { ++i; } } } { int i = 0; while (i < pitchBendSensitivity.size()) { var clock = pitchBendSensitivity.getKeyClock(i); if (startClock <= clock && clock < startClock + vibratoLength) { pitchBendSensitivity.removeElementAt(i); } else { ++i; } } } if (pitchBendSensitivity.getValue(startClock) != requiredPitchbendSensitivity) { pitchBendSensitivity.add(startClock, requiredPitchbendSensitivity); } pitchBend.add(startClock + vibratoLength, pitAtEnd); pitchBendSensitivity.add(startClock + vibratoLength, pbsAtEnd); iterator.rewind(); pitContext.value = 0; pbsContext.value = 0; int lastPitchBend = pitchBend.getValue(startClock); for (int clock = startClock; clock < startClock + vibratoLength && iterator.hasNext(); ++clock) { double vibratoPitchBendInCent = iterator.next() * 100.0; int pit = pitBackup.getValue(clock, pitContext); int pbs = pbsBackup.getValue(clock, pbsContext); const double pow2_13 = 8192; double netPitchBendInCent = (pbs * pit / pow2_13) * 100.0 + vibratoPitchBendInCent; maxNetPitchBendInCent = Math.Max(maxNetPitchBendInCent, Math.Abs(netPitchBendInCent)); int draftPitchBend = (int)Math.Round((netPitchBendInCent / 100.0) * pow2_13 / requiredPitchbendSensitivity); if (draftPitchBend < pitchBend.Minimum) { draftPitchBend = pitchBend.Minimum; } if (pitchBend.Maximum < draftPitchBend) { draftPitchBend = pitchBend.Maximum; } if (draftPitchBend != lastPitchBend) { pitchBend.add(clock, draftPitchBend); lastPitchBend = draftPitchBend; } } }
private static void copyCurve( VsqBPList src, VsqBPList dest, int clock_shift ){ int last_value = src.getDefault(); int count = src.size(); bool first_over_zero = true; for( int i = 0; i < count; i++ ){ int cl = src.getKeyClock( i ) - clock_shift; int value = src.getElementA( i ); if( cl < 0 ){ last_value = value; }else{ if( first_over_zero ){ first_over_zero = false; if( last_value != src.getDefault() ){ dest.add( 0, last_value ); } } dest.add( cl, value ); } } }
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 void commonButton_Click(Object sender, EventArgs e) { VsqBPList list = AppManager.getVsqFile().Track[AppManager.getSelected()].getCurve(m_curve.getName()); VsqBPPairSearchContext search = list.findElement(m_editing_id); int index = search.index; if (sender == btnForward) { index++; } else if (sender == btnBackward) { index--; } else if (sender == btnBackward2) { index -= 5; } else if (sender == btnForward2) { index += 5; } else if (sender == btnForward3) { index += 10; } else if (sender == btnBackward3) { index -= 10; } if (index < 0) { index = 0; } if (list.size() <= index) { index = list.size() - 1; } VsqBPPair bp = list.getElementB(index); m_editing_id = bp.id; int clock = list.getKeyClock(index); txtDataPointClock.TextChanged -= commonTextBox_TextChanged; txtDataPointValue.TextChanged -= commonTextBox_TextChanged; txtDataPointClock.Text = clock + ""; txtDataPointValue.Text = bp.value + ""; txtDataPointClock.TextChanged += commonTextBox_TextChanged; txtDataPointValue.TextChanged += commonTextBox_TextChanged; txtDataPointValue.Focus(); txtDataPointValue.SelectAll(); AppManager.itemSelection.clearPoint(); AppManager.itemSelection.addPoint(m_curve, bp.id); if (mMainWindow != null) { mMainWindow.ensureVisible(clock); mMainWindow.refreshScreen(); } }